diff --git a/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/index.js b/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/index.js
index 60569408..59873ef9 100644
--- a/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/index.js
+++ b/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/index.js
@@ -3,10 +3,10 @@ import ReactDOMServer from 'react-dom/server';
import { Map, List, fromJS } from 'immutable';
import { get, reduce, mapValues } from 'lodash';
import cn from 'classnames';
-import { Editor as SlateEditor, Html as SlateHtml, Raw as SlateRaw, Text as SlateText, Block as SlateBlock, Selection as SlateSelection} from 'slate';
+import { Editor as SlateEditor, Raw as SlateRaw, Text as SlateText, Block as SlateBlock, Selection as SlateSelection} from 'slate';
import EditList from 'slate-edit-list';
import EditTable from 'slate-edit-table';
-import { markdownToRemark, remarkToMarkdown, slateToRemark, remarkToSlate, markdownToHtml, htmlToMarkdown } from '../../unified';
+import { markdownToRemark, remarkToMarkdown, slateToRemark, remarkToSlate, markdownToHtml, htmlToSlate } from '../../unified';
import registry from '../../../../../lib/registry';
import { createAssetProxy } from '../../../../../valueObjects/AssetProxy';
import Toolbar from '../Toolbar/Toolbar';
@@ -160,176 +160,6 @@ const MARK_COMPONENTS = {
code: props => {props.children}
,
};
-const RULES = [
- {
- deserialize(el, next) {
- const shortcodeId = el.attribs && el.attribs['data-ncp'];
- if (!shortcodeId) {
- return;
- }
- const plugin = registry.getEditorComponents().get(shortcodeId);
- if (!plugin) {
- return;
- }
- const shortcodeData = Map(el.attribs).reduce((acc, value, key) => {
- if (key.startsWith('data-ncp-')) {
- const dataKey = key.slice('data-ncp-'.length).toLowerCase();
- if (dataKey) {
- return acc.set(dataKey, value);
- }
- }
- return acc;
- }, Map({ shortcodeId }));
-
- const result = {
- kind: 'block',
- isVoid: true,
- type: 'shortcode',
- data: { shortcode: shortcodeData },
- };
- return result;
- },
- serialize(entity, children) {
- if (entity.type !== 'shortcode') {
- return;
- }
-
- const data = Map(entity.data.get('shortcode'));
- const shortcodeId = data.get('shortcodeId');
- const plugin = registry.getEditorComponents().get(shortcodeId);
- const dataAttrs = data.delete('shortcodeId').mapKeys(key => `data-ncp-${key}`).set('data-ncp', shortcodeId);
- const preview = plugin.toPreview(data.toJS());
- const component = typeof preview === 'string'
- ?
- : {preview}
;
- return component;
- },
- },
- {
- deserialize(el, next) {
- const block = BLOCK_TAGS[el.tagName]
- if (!block) return
- return {
- kind: 'block',
- type: block,
- nodes: next(el.children)
- }
- },
- serialize(entity, children) {
- if (['bulleted-list', 'numbered-list'].includes(entity.type)) {
- return;
- }
- const component = BLOCK_COMPONENTS[entity.type]
- if (!component) {
- return;
- }
- return component({ children });
- }
- },
- {
- deserialize(el, next) {
- const mark = MARK_TAGS[el.tagName]
- if (!mark) return
- return {
- kind: 'mark',
- type: mark,
- nodes: next(el.children)
- }
- },
- serialize(entity, children) {
- if (entity.kind !== 'mark') {
- return;
- }
- const component = MARK_COMPONENTS[entity.type]
- return component({ children });
- }
- },
- {
- // Special case for code blocks, which need to grab the nested children.
- deserialize(el, next) {
- if (el.tagName != 'pre') return
- const code = el.children[0]
- const children = code && code.tagName == 'code'
- ? code.children
- : el.children
-
- return {
- kind: 'block',
- type: 'code',
- nodes: next(children)
- }
- },
- },
- {
- deserialize(el, next) {
- if (el.tagName != 'img') return
- return {
- kind: 'block',
- type: 'image',
- isVoid: true,
- nodes: [],
- data: {
- src: el.attribs.src,
- alt: el.attribs.alt,
- title: el.attribs.title,
- }
- }
- },
- serialize(entity, children) {
- if (entity.type !== 'image') {
- return;
- }
- const data = entity.get('data');
- const props = {
- src: data.get('src'),
- alt: data.get('alt'),
- title: data.get('title'),
- };
- const result = NODE_COMPONENTS.image(props);
- return result;
- }
- },
- {
- // Special case for links, to grab their href.
- deserialize(el, next) {
- if (el.tagName != 'a') return
- return {
- kind: 'inline',
- type: 'link',
- nodes: next(el.children),
- data: {
- href: el.attribs.href,
- title: el.attribs.title,
- }
- }
- },
- serialize(entity, children) {
- if (entity.type !== 'link') {
- return;
- }
- const data = entity.get('data');
- const props = {
- href: data.get('href'),
- title: data.get('title'),
- attributes: data.get('attributes'),
- children,
- };
- return NODE_COMPONENTS.link(props);
- }
- },
- {
- serialize(entity, children) {
- if (!['bulleted-list', 'numbered-list'].includes(entity.type)) {
- return;
- }
- return NODE_COMPONENTS[entity.type]({ children });
- }
- }
-
-]
-
-const htmlSerializer = new SlateHtml({ rules: RULES });
-
const SoftBreak = (options = {}) => ({
onKeyDown(e, data, state) {
if (data.key != 'enter') return;
@@ -423,10 +253,9 @@ export default class Editor extends Component {
if (data.type !== 'html' || data.isShift) {
return;
}
- const markdown = htmlToMarkdown(data.html);
- const html = markdownToHtml(markdown);
- const fragment = serializer.deserialize(html).document;
- return state.transform().insertFragment(fragment).apply();
+ const ast = htmlToSlate(data.html);
+ const { document: doc } = SlateRaw.deserialize(ast, { terse: true });
+ return state.transform().insertFragment(doc).apply();
}
handleDocumentChange = (doc, editorState) => {
diff --git a/src/components/Widgets/Markdown/unified.js b/src/components/Widgets/Markdown/unified.js
index 8f343f03..b9a68160 100644
--- a/src/components/Widgets/Markdown/unified.js
+++ b/src/components/Widgets/Markdown/unified.js
@@ -620,35 +620,20 @@ export const markdownToHtml = markdown => {
return result;
}
-export const htmlToMarkdown = html => {
- const result = unified()
+export const htmlToSlate = html => {
+ const hast = unified()
.use(htmlToRehype, { fragment: true })
+ .parse(html);
+
+ const result = unified()
.use(rehypeRemoveEmpty)
.use(rehypeMinifyWhitespace)
.use(rehypePaperEmoji)
.use(rehypeShortcodes)
- .use(rehypeToRemark, { handlers: { div: (h, node) => {
- const dataPrefix = `data${capitalize(shortcodeAttributePrefix)}`;
- const isShortcode = node.properties[dataPrefix];
- if (isShortcode) {
- const paragraph = h(node, 'paragraph', hastToMdastHandlerAll(h, node));
- paragraph.data = paragraph.data || {};
- paragraph.data[shortcodeAttributePrefix] = true;
- return paragraph;
- }
- }}})
- .use(() => node => {
- return node;
- })
+ .use(rehypeToRemark)
.use(remarkNestedList)
- .use(remarkToMarkdownPlugin, { listItemIndent: '1', fences: true, pedantic: true, commonmark: true })
- .use(remarkPrecompileShortcodes)
- /*
- .use(() => node => {
- return node;
- })
- */
- .processSync(html)
- .contents;
+ .use(remarkToSlatePlugin)
+ .runSync(hast);
+
return result;
};