132 lines
4.1 KiB
JavaScript
Raw Normal View History

2016-08-18 15:13:22 -03:00
/* eslint react/prop-types: 0, react/no-multi-comp: 0 */
2016-08-18 10:51:38 -03:00
import React from 'react';
2016-08-18 15:13:22 -03:00
import { List, Map } from 'immutable';
2016-08-18 10:51:38 -03:00
import MarkupIt from 'markup-it';
import markdownSyntax from 'markup-it/syntaxes/markdown';
import htmlSyntax from 'markup-it/syntaxes/html';
2016-08-18 15:13:22 -03:00
import reInline from 'markup-it/syntaxes/markdown/re/inline';
2016-08-18 10:51:38 -03:00
import { Icon } from '../UI';
/*
* All Rich text widgets (Markdown, for example) should use Slate for text editing and
* MarkupIt to convert between structured formats (Slate JSON, Markdown, HTML, etc.).
* This module Processes and provides Slate nodes and MarkupIt syntaxes augmented with plugins
*/
let processedPlugins = List([]);
const nodes = {};
2016-08-18 15:13:22 -03:00
let augmentedMarkdownSyntax = markdownSyntax;
let augmentedHTMLSyntax = htmlSyntax;
2016-08-18 10:51:38 -03:00
function processEditorPlugins(plugins) {
// Since the plugin list is immutable, a simple comparisson is enough
// to determine whether we need to process again.
if (plugins === processedPlugins) return;
plugins.forEach((plugin) => {
2016-08-18 17:26:01 -03:00
const basicRule = MarkupIt.Rule(plugin.id).regExp(plugin.pattern, (state, match) => (
2016-09-22 10:03:02 +02:00
{ data: plugin.fromBlock(match) }
2016-08-18 17:26:01 -03:00
));
2016-08-18 10:51:38 -03:00
2016-08-18 17:26:01 -03:00
const markdownRule = basicRule.toText((state, token) => (
`${ plugin.toBlock(token.getData().toObject()) }\n\n`
2016-08-18 17:26:01 -03:00
));
const htmlRule = basicRule.toText((state, token) => (
plugin.toPreview(token.getData().toObject())
));
2016-08-18 10:51:38 -03:00
const nodeRenderer = (props) => {
const { node, state } = props;
const isFocused = state.selection.hasEdgeIn(node);
const className = isFocused ? 'plugin active' : 'plugin';
return (
<div {...props.attributes} className={className}>
<div className="plugin_icon" contentEditable={false}><Icon type={plugin.icon} /></div>
2016-08-18 15:13:22 -03:00
<div className="plugin_fields" contentEditable={false}>
{plugin.fields.map(field => `${ field.label }: “${ node.data.get(field.name) }`)}
2016-08-18 15:13:22 -03:00
</div>
2016-08-18 10:51:38 -03:00
</div>
);
};
2016-08-18 15:13:22 -03:00
augmentedMarkdownSyntax = augmentedMarkdownSyntax.addInlineRules(markdownRule);
augmentedHTMLSyntax = augmentedHTMLSyntax.addInlineRules(htmlRule);
2016-08-18 10:51:38 -03:00
nodes[plugin.id] = nodeRenderer;
});
processedPlugins = plugins;
}
2016-08-18 15:13:22 -03:00
function processMediaProxyPlugins(getMedia) {
2016-08-18 17:26:01 -03:00
const mediaProxyRule = MarkupIt.Rule('mediaproxy').regExp(reInline.link, (state, match) => {
if (match[0].charAt(0) !== '!') {
// Return if this is not an image
return;
}
const imgData = Map({
2016-09-22 10:03:02 +02:00
alt: match[1],
src: match[2],
title: match[3],
2016-08-18 17:26:01 -03:00
}).filter(Boolean);
return {
data: imgData,
2016-08-18 17:26:01 -03:00
};
});
const mediaProxyMarkdownRule = mediaProxyRule.toText((state, token) => {
const data = token.getData();
const alt = data.get('alt', '');
const src = data.get('src', '');
const title = data.get('title', '');
2016-08-18 17:26:01 -03:00
if (title) {
return `![${ alt }](${ src } "${ title }")`;
2016-08-18 17:26:01 -03:00
} else {
return `![${ alt }](${ src })`;
2016-08-18 17:26:01 -03:00
}
});
const mediaProxyHTMLRule = mediaProxyRule.toText((state, token) => {
const data = token.getData();
const alt = data.get('alt', '');
const src = data.get('src', '');
return `<img src=${ getMedia(src) } alt=${ alt } />`;
2016-08-18 17:26:01 -03:00
});
2016-08-18 15:13:22 -03:00
nodes.mediaproxy = (props) => {
2016-08-18 15:13:22 -03:00
/* eslint react/prop-types: 0 */
const { node, state } = props;
const isFocused = state.selection.hasEdgeIn(node);
const className = isFocused ? 'active' : null;
const src = node.data.get('src');
return (
<img {...props.attributes} src={getMedia(src)} className={className} />
2016-08-18 15:13:22 -03:00
);
};
2016-08-18 17:26:01 -03:00
augmentedMarkdownSyntax = augmentedMarkdownSyntax.addInlineRules(mediaProxyMarkdownRule);
augmentedHTMLSyntax = augmentedHTMLSyntax.addInlineRules(mediaProxyHTMLRule);
2016-08-18 15:13:22 -03:00
}
2016-08-18 10:51:38 -03:00
function getPlugins() {
2016-09-22 10:03:02 +02:00
return processedPlugins.map(plugin => ({
id: plugin.id,
icon: plugin.icon,
fields: plugin.fields,
2016-09-22 10:03:02 +02:00
})).toArray();
2016-08-18 10:51:38 -03:00
}
function getNodes() {
return nodes;
}
2016-08-18 15:13:22 -03:00
function getSyntaxes(getMedia) {
2016-08-18 17:26:01 -03:00
if (getMedia) {
processMediaProxyPlugins(getMedia);
}
2016-09-22 10:03:02 +02:00
return { markdown: augmentedMarkdownSyntax, html: augmentedHTMLSyntax };
2016-08-18 10:51:38 -03:00
}
export { processEditorPlugins, getNodes, getSyntaxes, getPlugins };