diff --git a/app/components/ClickablePadding.js b/app/components/ClickablePadding.js new file mode 100644 index 00000000..14572c4d --- /dev/null +++ b/app/components/ClickablePadding.js @@ -0,0 +1,21 @@ +// @flow +import React from 'react'; +import styled from 'styled-components'; + +type Props = { + onClick?: ?Function, + grow?: boolean, +}; + +const ClickablePadding = (props: Props) => { + return ; +}; + +const Container = styled.div` + min-height: 50vh; + padding-top: 50px; + cursor: ${({ onClick }) => (onClick ? 'text' : 'default')}; + ${({ grow }) => grow && `flex-grow: 1;`}; +`; + +export default ClickablePadding; diff --git a/app/scenes/Document/Document.js b/app/scenes/Document/Document.js index f2c0cd82..ee79cb7a 100644 --- a/app/scenes/Document/Document.js +++ b/app/scenes/Document/Document.js @@ -140,7 +140,7 @@ class DocumentScene extends React.Component { }; loadEditor = async () => { - const EditorImport = await import('rich-markdown-editor'); + const EditorImport = await import('./components/Editor'); this.editorComponent = EditorImport.default; }; diff --git a/app/scenes/Document/components/Editor.js b/app/scenes/Document/components/Editor.js new file mode 100644 index 00000000..44c3a5de --- /dev/null +++ b/app/scenes/Document/components/Editor.js @@ -0,0 +1,112 @@ +// @flow +import * as React from 'react'; +import styled from 'styled-components'; +import { Block, Change, Node, Mark, Text } from 'slate'; +import RichMarkdownEditor, { Placeholder, schema } from 'rich-markdown-editor'; +import ClickablePadding from 'components/ClickablePadding'; + +type Props = { + titlePlaceholder: string, + bodyPlaceholder: string, + readOnly: boolean, +}; + +// add rules to the schema to ensure the first node is a heading +schema.document.nodes.unshift({ types: ['heading1'], min: 1, max: 1 }); +schema.document.normalize = ( + change: Change, + reason: string, + { + node, + child, + mark, + index, + }: { node: Node, mark?: Mark, child: Node, index: number } +) => { + switch (reason) { + case 'child_type_invalid': { + return change.setNodeByKey( + child.key, + index === 0 ? 'heading1' : 'paragraph' + ); + } + case 'child_required': { + const block = Block.create(index === 0 ? 'heading1' : 'paragraph'); + return change.insertNodeByKey(node.key, index, block); + } + default: + } +}; + +class Editor extends React.Component { + editor: *; + + setEditorRef = (ref: RichMarkdownEditor) => { + this.editor = ref; + }; + + focusAtEnd = () => { + console.log(this.editor); + if (this.editor) this.editor.focusAtEnd(); + }; + + renderPlaceholder = (props: *) => { + const { editor, node } = props; + + if (editor.state.isComposing) return; + if (node.object !== 'block') return; + if (!Text.isTextList(node.nodes)) return; + if (node.text !== '') return; + + const index = editor.value.document.getBlocks().indexOf(node); + if (index > 1) return; + + const text = + index === 0 ? this.props.titlePlaceholder : this.props.bodyPlaceholder; + + return {editor.props.readOnly ? '' : text}; + }; + + render() { + const { readOnly } = this.props; + + return ( + + + + + ); + } +} + +// additional styles account for placeholder nodes not always re-rendering +const StyledEditor = styled(RichMarkdownEditor)` + display: flex; + flex: 0; + + ${Placeholder} { + visibility: hidden; + } + + h1:first-of-type { + ${Placeholder} { + visibility: visible; + } + } + + p:nth-child(2):last-child { + ${Placeholder} { + visibility: visible; + } + } +`; + +export default Editor; diff --git a/package.json b/package.json index fe75f7f4..89f27d6c 100644 --- a/package.json +++ b/package.json @@ -157,7 +157,7 @@ "react-waypoint": "^7.3.1", "redis": "^2.6.2", "redis-lock": "^0.1.0", - "rich-markdown-editor": "1.4.3-0", + "rich-markdown-editor": "2.0.4", "safestart": "1.1.0", "sequelize": "4.28.6", "sequelize-cli": "^2.7.0", diff --git a/yarn.lock b/yarn.lock index fb4b6a11..3a68d66e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -762,7 +762,7 @@ babel-plugin-syntax-trailing-function-commas@^6.22.0: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3" -babel-plugin-transform-async-to-generator@^6.22.0: +babel-plugin-transform-async-to-generator@^6.22.0, babel-plugin-transform-async-to-generator@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz#6536e378aff6cb1d5517ac0e40eb3e9fc8d08761" dependencies: @@ -1011,6 +1011,12 @@ babel-plugin-transform-regenerator@^6.22.0, babel-plugin-transform-regenerator@^ dependencies: regenerator-transform "^0.10.0" +babel-plugin-transform-runtime@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.23.0.tgz#88490d446502ea9b8e7efb0fe09ec4d99479b1ee" + dependencies: + babel-runtime "^6.22.0" + babel-plugin-transform-strict-mode@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758" @@ -8848,11 +8854,13 @@ retry-axios@0.3.2, retry-axios@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/retry-axios/-/retry-axios-0.3.2.tgz#5757c80f585b4cc4c4986aa2ffd47a60c6d35e13" -rich-markdown-editor@1.4.3-0: - version "1.4.3-0" - resolved "https://registry.yarnpkg.com/rich-markdown-editor/-/rich-markdown-editor-1.4.3-0.tgz#2b90e185b35821463c138ad36f2eb209c990c05a" +rich-markdown-editor@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/rich-markdown-editor/-/rich-markdown-editor-2.0.4.tgz#6cd317e213a0b574a2ed905ea710eba2d27aab21" dependencies: "@tommoor/slate-drop-or-paste-images" "^0.8.1" + babel-plugin-transform-async-to-generator "^6.24.1" + babel-plugin-transform-runtime "^6.23.0" boundless-arrow-key-navigation "^1.1.0" copy-to-clipboard "^3.0.8" eslint-plugin-flowtype "^2.46.1" @@ -8874,7 +8882,7 @@ rich-markdown-editor@1.4.3-0: slate-prism "^0.5.0" slate-react "^0.12.3" slate-trailing-block "^0.5.0" - slug "^0.9.1" + slugify "^1.3.0" styled-components "^3.2.3" right-align@^0.1.1: @@ -9320,12 +9328,16 @@ slide@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" -slug@0.9.1, slug@^0.9.1: +slug@0.9.1: version "0.9.1" resolved "https://registry.yarnpkg.com/slug/-/slug-0.9.1.tgz#af08f608a7c11516b61778aa800dce84c518cfda" dependencies: unicode ">= 0.3.1" +slugify@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.3.0.tgz#787919259d28c825fbcae6da2e01c77a109793f6" + snake-case@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f" @@ -10152,8 +10164,8 @@ unherit@^1.0.4: xtend "^4.0.1" "unicode@>= 0.3.1": - version "10.0.0" - resolved "https://registry.yarnpkg.com/unicode/-/unicode-10.0.0.tgz#e5d51c1db93b6c71a0b879e0b0c4af7e6fdf688e" + version "11.0.1" + resolved "https://registry.yarnpkg.com/unicode/-/unicode-11.0.1.tgz#735bd422ec75cf28d396eb224d535d168d5f1db6" unified@^6.1.5: version "6.1.6"