Upgrade Editor – Slate 0.44 / RME 8.0.0 (#863)

* Upgrade Slate

* Normalization

* Remove dupe dep

* Fixes placeholders

* Image uploading fixed

* Verbose but solid placeholder handling

* RME 8.0.0

* Fixes: Ability to delete embeds
This commit is contained in:
Tom Moor
2019-01-18 22:44:31 -08:00
committed by GitHub
parent 0be5aef1c6
commit d21dd710bb
11 changed files with 185 additions and 306 deletions

View File

@@ -7,8 +7,6 @@ import Embed from './Embed';
import embeds from '../../embeds';
type Props = {
titlePlaceholder?: string,
bodyPlaceholder?: string,
defaultValue?: string,
readOnly?: boolean,
disableEmbeds?: boolean,

View File

@@ -26,7 +26,7 @@ export default class Embed extends React.Component<*> {
const result = this.matches;
if (!result) return null;
const { attributes, isSelected } = this.props;
const { attributes, isSelected, children } = this.props;
const { component, matches } = result;
const EmbedComponent = component;
@@ -37,6 +37,7 @@ export default class Embed extends React.Component<*> {
{...attributes}
>
<EmbedComponent matches={matches} url={this.url} />
{children}
</Container>
);
}

View File

@@ -49,7 +49,7 @@ You have unsaved changes.
Are you sure you want to discard them?
`;
const UPLOADING_WARNING = `
Image are still uploading.
Images are still uploading.
Are you sure you want to discard them?
`;
@@ -240,7 +240,7 @@ class DocumentScene extends React.Component<Props> {
const document = this.document;
this.isDirty =
document && this.getEditorText().trim() !== document.text.trim();
!!document && this.getEditorText().trim() !== document.text.trim();
}, IS_DIRTY_DELAY);
onImageUploadStart = () => {
@@ -340,11 +340,11 @@ class DocumentScene extends React.Component<Props> {
{this.isEditing && (
<React.Fragment>
<Prompt
when={this.isDirty || false}
when={this.isDirty && !this.isUploading}
message={DISCARD_CHANGES}
/>
<Prompt
when={this.isUploading || false}
when={this.isUploading && !this.isDirty}
message={UPLOADING_WARNING}
/>
</React.Fragment>
@@ -365,8 +365,6 @@ class DocumentScene extends React.Component<Props> {
<MaxWidth column auto>
<Editor
key={embedsDisabled ? 'embeds-disabled' : 'embeds-enabled'}
titlePlaceholder="Start with a title…"
bodyPlaceholder="…the rest is your canvas"
defaultValue={revision ? revision.text : document.text}
pretitle={document.emoji}
disableEmbeds={embedsDisabled}

View File

@@ -1,14 +1,12 @@
// @flow
import * as React from 'react';
import styled from 'styled-components';
import { Text } from 'slate';
import { Placeholder } from 'rich-markdown-editor';
import Editor from 'components/Editor';
import Placeholder from 'rich-markdown-editor/lib/components/Placeholder';
import ClickablePadding from 'components/ClickablePadding';
import plugins from './plugins';
type Props = {
titlePlaceholder?: string,
bodyPlaceholder?: string,
defaultValue?: string,
readOnly?: boolean,
};
@@ -30,23 +28,6 @@ class DocumentEditor extends React.Component<Props> {
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 <Placeholder>{editor.props.readOnly ? '' : text}</Placeholder>;
};
render() {
const { readOnly } = this.props;
@@ -54,7 +35,7 @@ class DocumentEditor extends React.Component<Props> {
<React.Fragment>
<StyledEditor
ref={ref => (this.editor = ref)}
renderPlaceholder={this.renderPlaceholder}
plugins={plugins}
{...this.props}
/>
<ClickablePadding
@@ -66,21 +47,15 @@ class DocumentEditor extends React.Component<Props> {
}
}
// additional styles account for placeholder nodes not always re-rendering
const StyledEditor = styled(Editor)`
display: flex;
flex: 0;
${Placeholder} {
visibility: hidden;
}
h1:first-of-type {
p {
${Placeholder} {
visibility: visible;
visibility: hidden;
}
}
p:nth-child(2):last-child {
${Placeholder} {
visibility: visible;

View File

@@ -0,0 +1,27 @@
// @flow
import { Node, Editor } from 'slate';
import Placeholder from 'rich-markdown-editor/lib/plugins/Placeholder';
export default [
Placeholder({
placeholder: 'Start with a title…',
when: (editor: Editor, node: Node) => {
if (editor.readOnly) return false;
if (node.object !== 'block') return false;
if (node.type !== 'heading1') return false;
if (node.text !== '') return false;
if (editor.value.document.nodes.first() !== node) return false;
return true;
},
}),
Placeholder({
placeholder: '…the rest is your canvas',
when: (editor: Editor, node: Node) => {
if (editor.readOnly) return false;
if (node.object !== 'block') return false;
if (node.type !== 'paragraph') return false;
if (node.text !== '') return false;
return true;
},
}),
];

View File

@@ -1,32 +1,39 @@
// @flow
import { cloneDeep } from 'lodash';
import { Block, Change, Node, Mark } from 'slate';
import { Block, SlateError, Editor } from 'slate';
import { schema as originalSchema } from 'rich-markdown-editor';
const schema = cloneDeep(originalSchema);
// 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'
schema.document.nodes.unshift({ match: { type: 'heading1' }, min: 1, max: 1 });
schema.document.normalize = (editor: Editor, error: SlateError) => {
switch (error.code) {
case 'child_max_invalid': {
return editor.setNodeByKey(
error.child.key,
error.index === 0 ? 'heading1' : 'paragraph'
);
}
case 'child_required': {
const block = Block.create(index === 0 ? 'heading1' : 'paragraph');
return change.insertNodeByKey(node.key, index, block);
case 'child_min_invalid': {
const missingTitle = error.index === 0;
const firstNode = editor.value.document.nodes.get(0);
if (!firstNode) {
editor.insertNodeByKey(error.node.key, 0, Block.create('heading1'));
} else {
editor.setNodeByKey(firstNode.key, { type: 'heading1' });
}
const secondNode = editor.value.document.nodes.get(1);
if (!secondNode) {
editor.insertNodeByKey(error.node.key, 1, Block.create('paragraph'));
} else {
editor.setNodeByKey(secondNode.key, { type: 'paragraph' });
}
if (missingTitle) setImmediate(() => editor.moveFocusToStartOfDocument());
return editor;
}
default:
}