UNPKG

@kedao/editor

Version:

Rich Text Editor Based On Draft.js

182 lines 7.06 kB
import './styles.scss'; import React from 'react'; import { Map } from 'immutable'; import { EditorState, SelectionState } from 'draft-js'; import PrismDecorator from 'draft-js-prism'; import { ContentUtils } from '@kedao/utils'; import Prism from 'prismjs'; class CodeBlockWrapper extends React.Component { constructor(props) { super(props); this.codeBlockBlockKey = null; this.codeBlockBlock = null; this.setCodeBlockSyntax = (event) => { const syntax = event.currentTarget.dataset.syntax; if (!syntax) { return false; } try { const syntaxName = this.props.syntaxs.find((item) => item.syntax === syntax).name; if (!syntaxName) { return false; } const selectionState = SelectionState.createEmpty(this.codeBlockBlockKey); const editorState = EditorState.forceSelection(this.props.editorState, selectionState); this.setState({ syntax, syntaxName }, () => { this.props.editor.setValue(ContentUtils.setSelectionBlockData(editorState, { syntax })); }); } catch (error) { console.warn(error); } }; this.state = { syntax: props.syntaxs[0].syntax, syntaxName: props.syntaxs[0].name }; } componentDidMount() { this.codeBlockBlock = this.getCodeBlockBlock(this.props); this.getCodeBlockSyntax(this.props); } UNSAFE_componentWillReceiveProps(nextProps) { this.codeBlockBlock = this.getCodeBlockBlock(nextProps); this.getCodeBlockSyntax(nextProps); } getCodeBlockBlock(props) { try { const offsetKey = props['data-offset-key']; const blockKey = offsetKey.split('-')[0]; const contentState = props.editorState.getCurrentContent(); this.codeBlockBlockKey = blockKey; return contentState.getBlockForKey(blockKey); } catch (error) { console.warn(error); return null; } } getCodeBlockSyntax(props) { if (this.codeBlockBlock) { const blockData = this.codeBlockBlock.getData(); const syntax = blockData.get('syntax') || props.syntaxs[0].syntax; const syntaxName = props.syntaxs.find((item) => item.syntax === syntax).name; if (!syntaxName) { return false; } this.setState({ syntax, syntaxName }); } } render() { return (React.createElement("div", { className: "kedao-code-block-wrapper" }, React.createElement("div", { className: "kedao-code-block-header", contentEditable: false }, React.createElement("div", { className: "syntax-switcher" }, React.createElement("span", null, this.state.syntaxName), React.createElement("ul", { className: "syntax-list" }, this.props.syntaxs.map((item, index) => (React.createElement("li", { key: index, "data-syntax": item.syntax, onClick: this.setCodeBlockSyntax }, item.name)))))), React.createElement("pre", { className: `kedao-code-block${this.props.showLineNumber ? ' show-line-number' : ''}`, "data-syntax": this.state.syntax }, this.props.children))); } } const getCodeBlockBlock = (block) => { if (!block || !block.getType || block.getType() !== 'code-block') { return null; } const blockDOMNode = document.querySelector(`code[data-offset-key="${block.getKey()}-0-0"]`); if (!blockDOMNode) { return null; } if (blockDOMNode.parentNode.nodeName.toLowerCase() !== 'pre') { return null; } return blockDOMNode.parentNode.dataset.syntax; }; const getCodeBlockRenderMap = (options) => (props) => { return Map({ 'code-block': { element: 'code', wrapper: React.createElement(CodeBlockWrapper, Object.assign({}, options, props)), nestingEnabled: false } }); }; export default (options = {}) => { const { showLineNumber, showTools, includeEditors, excludeEditors } = options; const syntaxs = options.syntaxs || [ { name: 'JavaScript', syntax: 'javascript' }, { name: 'HTML', syntax: 'html' }, { name: 'CSS', syntax: 'css' } ]; return [ { type: 'block', name: 'code-block', includeEditors, excludeEditors, renderMap: getCodeBlockRenderMap({ syntaxs, showLineNumber, showTools }), importer: (nodeName, node) => { if (nodeName.toLowerCase() === 'pre') { try { const syntax = node.dataset.lang; node.innerHTML = node.innerHTML .replace(/<code(.*?)>/g, '') .replace(/<\/code>/g, ''); return syntax ? { type: 'code-block', data: { syntax } } : null; } catch (error) { return null; } } return null; }, exporter: (contentState, block) => { if (block.type.toLowerCase() !== 'code-block') { return null; } const previousBlock = contentState.getBlockBefore(block.key); const nextBlock = contentState.getBlockAfter(block.key); const previousBlockType = previousBlock === null || previousBlock === void 0 ? void 0 : previousBlock.getType(); const nextBlockType = nextBlock === null || nextBlock === void 0 ? void 0 : nextBlock.getType(); const syntax = block.data.syntax || syntaxs[0].syntax; let start = ''; let end = ''; if (previousBlockType !== 'code-block') { start = `<pre data-lang="${syntax}" class="lang-${syntax}"><code class="lang-${syntax}">`; } else { start = ''; } if (nextBlockType !== 'code-block') { end = '</code></pre>'; } else { end = '<br/>'; } return { start, end }; } }, { type: 'decorator', includeEditors, excludeEditors, decorator: new PrismDecorator({ prism: Prism, getSyntax: getCodeBlockBlock, defaultSyntax: syntaxs[0].syntax }) } ]; }; //# sourceMappingURL=index.js.map