fix slate migration bugs
This commit is contained in:
parent
1bbaebf6d5
commit
9342c9c064
@ -10,8 +10,7 @@
|
|||||||
|
|
||||||
& input,
|
& input,
|
||||||
& textarea,
|
& textarea,
|
||||||
& select,
|
& select {
|
||||||
& div[contenteditable=true] {
|
|
||||||
@apply --input;
|
@apply --input;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.nc-rawEditor-rawEditor {
|
.nc-rawEditor-rawEditor {
|
||||||
|
@apply(--input);
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.nc-visualEditor-editor {
|
.nc-visualEditor-editor {
|
||||||
|
@apply(--input);
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
|
@ -8,36 +8,36 @@ import cn from 'classnames';
|
|||||||
* by us when we manually deserialize from Remark's MDAST to Slate's AST.
|
* by us when we manually deserialize from Remark's MDAST to Slate's AST.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const renderMark = props => {
|
/**
|
||||||
switch (props.mark.type) {
|
* Mark Components
|
||||||
case bold: return props => <strong>{props.children}</strong>;
|
*/
|
||||||
case italic: return props => <em>{props.children}</em>;
|
const Bold = props => <strong>{props.children}</strong>;
|
||||||
case strikethrough: return props => <s>{props.children}</s>;
|
const Italic = props => <em>{props.children}</em>;
|
||||||
case code: return props => <code>{props.children}</code>;
|
const Strikethrough = props => <s>{props.children}</s>;
|
||||||
}
|
const Code = props => <code>{props.children}</code>;
|
||||||
};
|
|
||||||
|
|
||||||
export const renderNode = props => {
|
/**
|
||||||
switch (props.node.type) {
|
* Node Components
|
||||||
case 'paragraph': return props => <p {...props.attributes}>{props.children}</p>;
|
*/
|
||||||
case 'list-item': return props => <li {...props.attributes}>{props.children}</li>;
|
const Paragraph = props => <p {...props.attributes}>{props.children}</p>;
|
||||||
case 'quote': return props => <blockquote {...props.attributes}>{props.children}</blockquote>;
|
const ListItem = props => <li {...props.attributes}>{props.children}</li>;
|
||||||
case 'code': return props => <pre><code {...props.attributes}>{props.children}</code></pre>;
|
const Quote = props => <blockquote {...props.attributes}>{props.children}</blockquote>;
|
||||||
case 'heading-one': return props => <h1 {...props.attributes}>{props.children}</h1>;
|
const CodeBlock = props => <pre><code {...props.attributes}>{props.children}</code></pre>;
|
||||||
case 'heading-two': return props => <h2 {...props.attributes}>{props.children}</h2>;
|
const HeadingOne = props => <h1 {...props.attributes}>{props.children}</h1>;
|
||||||
case 'heading-three': return props => <h3 {...props.attributes}>{props.children}</h3>;
|
const HeadingTwo = props => <h2 {...props.attributes}>{props.children}</h2>;
|
||||||
case 'heading-four': return props => <h4 {...props.attributes}>{props.children}</h4>;
|
const HeadingThree = props => <h3 {...props.attributes}>{props.children}</h3>;
|
||||||
case 'heading-five': return props => <h5 {...props.attributes}>{props.children}</h5>;
|
const HeadingFour = props => <h4 {...props.attributes}>{props.children}</h4>;
|
||||||
case 'heading-six': return props => <h6 {...props.attributes}>{props.children}</h6>;
|
const HeadingFive = props => <h5 {...props.attributes}>{props.children}</h5>;
|
||||||
case 'table': return props => <table><tbody {...props.attributes}>{props.children}</tbody></table>;
|
const HeadingSix = props => <h6 {...props.attributes}>{props.children}</h6>;
|
||||||
case 'table-row': return props => <tr {...props.attributes}>{props.children}</tr>;
|
const Table = props => <table><tbody {...props.attributes}>{props.children}</tbody></table>;
|
||||||
case 'table-cell': return props => <td {...props.attributes}>{props.children}</td>;
|
const TableRow = props => <tr {...props.attributes}>{props.children}</tr>;
|
||||||
case 'thematic-break': return props => <hr {...props.attributes}/>;
|
const TableCell = props => <td {...props.attributes}>{props.children}</td>;
|
||||||
case 'bulleted-list': return props => <ul {...props.attributes}>{props.children}</ul>;
|
const ThematicBreak = props => <hr {...props.attributes}/>;
|
||||||
case 'numbered-list': return props => (
|
const BulletedList = props => <ul {...props.attributes}>{props.children}</ul>;
|
||||||
|
const NumberedList = props => (
|
||||||
<ol {...props.attributes} start={props.node.data.get('start') || 1}>{props.children}</ol>
|
<ol {...props.attributes} start={props.node.data.get('start') || 1}>{props.children}</ol>
|
||||||
);
|
);
|
||||||
case 'link': return props => {
|
const Link = props => {
|
||||||
const data = props.node.get('data');
|
const data = props.node.get('data');
|
||||||
const marks = data.get('marks');
|
const marks = data.get('marks');
|
||||||
const url = data.get('url');
|
const url = data.get('url');
|
||||||
@ -49,7 +49,7 @@ export const renderNode = props => {
|
|||||||
}, link);
|
}, link);
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
case 'image': props => {
|
const Image = props => {
|
||||||
const data = props.node.get('data');
|
const data = props.node.get('data');
|
||||||
const marks = data.get('marks');
|
const marks = data.get('marks');
|
||||||
const url = data.get('url');
|
const url = data.get('url');
|
||||||
@ -62,11 +62,42 @@ export const renderNode = props => {
|
|||||||
}, image);
|
}, image);
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
case 'shortcode': props => {
|
const Shortcode = props => {
|
||||||
const { attributes, node, editor } = props;
|
const { attributes, node, editor } = props;
|
||||||
const isSelected = editor.value.selection.hasFocusIn(node);
|
const isSelected = editor.value.selection.hasFocusIn(node);
|
||||||
const className = cn('nc-visualEditor-shortcode', { ['nc-visualEditor-shortcodeSelected']: isSelected });
|
const className = cn('nc-visualEditor-shortcode', { ['nc-visualEditor-shortcodeSelected']: isSelected });
|
||||||
return <div {...attributes} className={className} draggable >{node.data.get('shortcode')}</div>;
|
return <div {...attributes} className={className} draggable >{node.data.get('shortcode')}</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const renderMark = props => {
|
||||||
|
switch (props.mark.type) {
|
||||||
|
case 'bold': return <Bold {...props}/>;
|
||||||
|
case 'italic': return <Italic {...props}/>;
|
||||||
|
case 'strikethrough': return <Strikethrough {...props}/>;
|
||||||
|
case 'code': return <Code {...props}/>;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const renderNode = props => {
|
||||||
|
switch (props.node.type) {
|
||||||
|
case 'paragraph': return <Paragraph {...props}/>;
|
||||||
|
case 'list-item': return <ListItem {...props}/>;
|
||||||
|
case 'quote': return <Quote {...props}/>;
|
||||||
|
case 'code': return <CodeBlock {...props}/>;
|
||||||
|
case 'heading-one': return <HeadingOne {...props}/>;
|
||||||
|
case 'heading-two': return <HeadingTwo {...props}/>;
|
||||||
|
case 'heading-three': return <HeadingThree {...props}/>;
|
||||||
|
case 'heading-four': return <HeadingFour {...props}/>;
|
||||||
|
case 'heading-five': return <HeadingFive {...props}/>;
|
||||||
|
case 'heading-six': return <HeadingSix {...props}/>;
|
||||||
|
case 'table': return <Table {...props}/>;
|
||||||
|
case 'table-row': return <TableRow {...props}/>;
|
||||||
|
case 'table-cell': return <TableCell {...props}/>;
|
||||||
|
case 'thematic-break': return <ThematicBreak {...props}/>;
|
||||||
|
case 'bulleted-list': return <BulletedList {...props}/>;
|
||||||
|
case 'numbered-list': return <NumberedList {...props}/>;
|
||||||
|
case 'link': return <Link {...props}/>;
|
||||||
|
case 'image': return <Image {...props}/>;
|
||||||
|
case 'shortcode': return <Shortcode {...props}/>;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -9,6 +9,7 @@ export function validateNode(node) {
|
|||||||
* Validation of the document itself.
|
* Validation of the document itself.
|
||||||
*/
|
*/
|
||||||
if (node.kind === 'document') {
|
if (node.kind === 'document') {
|
||||||
|
const doc = node;
|
||||||
/**
|
/**
|
||||||
* If the editor is ever in an empty state, insert an empty
|
* If the editor is ever in an empty state, insert an empty
|
||||||
* paragraph block.
|
* paragraph block.
|
||||||
@ -28,26 +29,45 @@ export function validateNode(node) {
|
|||||||
/**
|
/**
|
||||||
* Ensure that shortcodes are children of the root node.
|
* Ensure that shortcodes are children of the root node.
|
||||||
*/
|
*/
|
||||||
const nestedShortcode = node.findDescendant(descendant => {
|
const nestedShortcode = doc.findDescendant(descendant => {
|
||||||
const { type, key } = descendant;
|
const { type, key } = descendant;
|
||||||
return type === 'shortcode' && node.getParent(key).key !== node.key;
|
return type === 'shortcode' && doc.getParent(key).key !== doc.key;
|
||||||
});
|
});
|
||||||
if (nestedShortcode) {
|
if (nestedShortcode) {
|
||||||
return change => change.unwrapNodeByKey(node.key);
|
const unwrapShortcode = change => {
|
||||||
|
const key = nestedShortcode.key;
|
||||||
|
const newDoc = change.value.document;
|
||||||
|
const newParent = newDoc.getParent(key);
|
||||||
|
const docIsParent = newParent.key === newDoc.key;
|
||||||
|
const newParentParent = newDoc.getParent(newParent.key);
|
||||||
|
const docIsParentParent = newParentParent && newParentParent.key === newDoc.key;
|
||||||
|
if (docIsParent) {
|
||||||
|
return change;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Normalization happens by default, and causes all validation to
|
||||||
|
* restart with the result of a change upon execution. This unwrap loop
|
||||||
|
* could temporarily place a shortcode node in conflict with an outside
|
||||||
|
* plugin's schema, resulting in an infinite loop. To ensure against
|
||||||
|
* this, we turn off normalization until the last change.
|
||||||
|
*/
|
||||||
|
change.unwrapNodeByKey(nestedShortcode.key, { normalize: docIsParentParent });
|
||||||
|
};
|
||||||
|
return unwrapShortcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure that trailing shortcodes are followed by an empty paragraph.
|
* Ensure that trailing shortcodes are followed by an empty paragraph.
|
||||||
*/
|
*/
|
||||||
const trailingShortcode = node.findDescendant(descendant => {
|
const trailingShortcode = doc.findDescendant(descendant => {
|
||||||
const { type, key } = descendant;
|
const { type, key } = descendant;
|
||||||
return type === 'shortcode' && node.getBlocks().last().key === key;
|
return type === 'shortcode' && doc.getBlocks().last().key === key;
|
||||||
});
|
});
|
||||||
if (trailingShortcode) {
|
if (trailingShortcode) {
|
||||||
return change => {
|
return change => {
|
||||||
const text = Text.create('');
|
const text = Text.create('');
|
||||||
const block = Block.create({ type: 'paragraph', nodes: [ text ] });
|
const block = Block.create({ type: 'paragraph', nodes: [ text ] });
|
||||||
return change.insertNodeByKey(node.key, node.get('nodes').size, block);
|
return change.insertNodeByKey(doc.key, doc.get('nodes').size, block);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -249,7 +249,7 @@ function convertTextNode(node) {
|
|||||||
/**
|
/**
|
||||||
* Process Slate node leaves in preparation for MDAST transformation.
|
* Process Slate node leaves in preparation for MDAST transformation.
|
||||||
*/
|
*/
|
||||||
function processLeaves(leaves) {
|
function processLeaves(leaf) {
|
||||||
/**
|
/**
|
||||||
* Get an array of the mark types, converted to their MDAST equivalent
|
* Get an array of the mark types, converted to their MDAST equivalent
|
||||||
* types.
|
* types.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user