diff --git a/frontend/components/Editor/Editor.js b/frontend/components/Editor/Editor.js index dc0e0e6da..a7c3201a0 100644 --- a/frontend/components/Editor/Editor.js +++ b/frontend/components/Editor/Editor.js @@ -67,6 +67,16 @@ type KeyData = { } } + componentDidMount() { + if (!this.props.readOnly) { + if (this.props.text) { + this.focusAtEnd(); + } else { + this.focusAtStart(); + } + } + } + componentDidUpdate(prevProps: Props) { if (prevProps.readOnly && !this.props.readOnly) { this.focusAtEnd(); @@ -121,6 +131,8 @@ type KeyData = { // Handling of keyboard shortcuts outside of editor focus @keydown('meta+s') onSave(ev: SyntheticKeyboardEvent) { + if (this.props.readOnly) return; + ev.preventDefault(); ev.stopPropagation(); this.props.onSave(); @@ -128,6 +140,8 @@ type KeyData = { @keydown('meta+enter') onSaveAndExit(ev: SyntheticKeyboardEvent) { + if (this.props.readOnly) return; + ev.preventDefault(); ev.stopPropagation(); this.props.onSave({ redirect: false }); @@ -135,6 +149,7 @@ type KeyData = { @keydown('esc') onCancel() { + if (this.props.readOnly) return; this.props.onCancel(); } @@ -193,7 +208,8 @@ type KeyData = { (this.editor = ref)} - placeholder="Start with a title..." + placeholder="Start with a title…" + bodyPlaceholder="Insert witty platitude here" className={cx(styles.editor, { readOnly: this.props.readOnly })} schema={this.schema} plugins={this.plugins} diff --git a/frontend/components/Editor/Editor.scss b/frontend/components/Editor/Editor.scss index 808cd0f01..fc26f9493 100644 --- a/frontend/components/Editor/Editor.scss +++ b/frontend/components/Editor/Editor.scss @@ -30,6 +30,18 @@ } } + h1:first-of-type { + .placeholder { + visibility: visible; + } + } + + p:first-of-type { + .placeholder { + visibility: visible; + } + } + ul, ol { margin: 1em .1em; @@ -41,6 +53,10 @@ } } + p { + position: relative; + } + li p { display: inline; margin: 0; @@ -113,8 +129,10 @@ .placeholder { position: absolute; top: 0; + visibility: hidden; pointer-events: none; - color: #ddd; + user-select: none; + color: #B1BECC; } @media all and (max-width: 2000px) and (min-width: 960px) { diff --git a/frontend/components/Editor/components/Heading.js b/frontend/components/Editor/components/Heading.js index 49149a015..a219135a1 100644 --- a/frontend/components/Editor/components/Heading.js +++ b/frontend/components/Editor/components/Heading.js @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import styled from 'styled-components'; +import { Document } from 'slate'; import _ from 'lodash'; import slug from 'slug'; import StarIcon from 'components/Icon/StarIcon'; @@ -41,8 +42,8 @@ const StyledStar = styled(StarIcon)` } `; -function Heading( - { +function Heading(props: Props, { starred }: Context) { + const { parent, placeholder, node, @@ -52,10 +53,9 @@ function Heading( readOnly, children, component = 'h1', - }: Props, - { starred }: Context -) { - const firstHeading = parent.nodes.first() === node; + } = props; + const parentIsDocument = parent instanceof Document; + const firstHeading = parentIsDocument && parent.nodes.first() === node; const showPlaceholder = placeholder && firstHeading && !node.text; const slugish = _.escape(`${component}-${slug(node.text)}`); const showStar = readOnly && !!onStar; @@ -66,7 +66,7 @@ function Heading( {children} {showPlaceholder && - + {editor.props.placeholder} } {showHash && diff --git a/frontend/components/Editor/components/Paragraph.js b/frontend/components/Editor/components/Paragraph.js new file mode 100644 index 000000000..a3d43ba7b --- /dev/null +++ b/frontend/components/Editor/components/Paragraph.js @@ -0,0 +1,29 @@ +// @flow +import React from 'react'; +import { Document } from 'slate'; +import type { Props } from '../types'; +import styles from '../Editor.scss'; + +export default function Link({ + attributes, + editor, + node, + parent, + children, +}: Props) { + const parentIsDocument = parent instanceof Document; + const firstParagraph = parent && parent.nodes.get(1) === node; + const lastParagraph = parent && parent.nodes.last() === node; + const showPlaceholder = + parentIsDocument && firstParagraph && lastParagraph && !node.text; + + return ( +

+ {children} + {showPlaceholder && + + {editor.props.bodyPlaceholder} + } +

+ ); +} diff --git a/frontend/components/Editor/plugins/MarkdownShortcuts.js b/frontend/components/Editor/plugins/MarkdownShortcuts.js index 745bea0d3..1d4533c65 100644 --- a/frontend/components/Editor/plugins/MarkdownShortcuts.js +++ b/frontend/components/Editor/plugins/MarkdownShortcuts.js @@ -20,6 +20,8 @@ export default function MarkdownShortcuts() { return this.onDash(ev, state); case '`': return this.onBacktick(ev, state); + case 'tab': + return this.onTab(ev, state); case 'space': return this.onSpace(ev, state); case 'backspace': @@ -184,6 +186,17 @@ export default function MarkdownShortcuts() { } }, + /** + * On tab, if at the end of the heading jump to the main body content + * as if it is another input field (act the same as enter). + */ + onTab(ev: SyntheticEvent, state: Object) { + if (state.startBlock.type === 'heading1') { + ev.preventDefault(); + return state.transform().splitBlock().setBlock('paragraph').apply(); + } + }, + /** * On return, if at the end of a node type that should not be extended, * create a new paragraph below it. diff --git a/frontend/components/Editor/schema.js b/frontend/components/Editor/schema.js index 3d96a8e08..0ae017e6c 100644 --- a/frontend/components/Editor/schema.js +++ b/frontend/components/Editor/schema.js @@ -5,6 +5,7 @@ import Image from './components/Image'; import Link from './components/Link'; import ListItem from './components/ListItem'; import Heading from './components/Heading'; +import Paragraph from './components/Paragraph'; import type { Props, Node, Transform } from './types'; import styles from './Editor.scss'; @@ -25,7 +26,7 @@ const createSchema = ({ onStar, onUnstar }: Options) => { }, nodes: { - paragraph: (props: Props) =>

{props.children}

, + paragraph: (props: Props) => , 'block-quote': (props: Props) => (
{props.children}
), diff --git a/frontend/scenes/Document/Document.js b/frontend/scenes/Document/Document.js index 46f353328..7a2f09807 100644 --- a/frontend/scenes/Document/Document.js +++ b/frontend/scenes/Document/Document.js @@ -167,7 +167,7 @@ type Props = { const isNew = this.props.newDocument; const isEditing = !!this.props.match.params.edit || isNew; const isFetching = !this.document; - const titleText = get(this.document, 'title', 'Loading'); + const titleText = get(this.document, 'title', ''); const document = this.document; return (