UNPKG

delta-component

Version:

embeddable react component

251 lines (198 loc) 6.95 kB
import React from 'react'; // TODO: https://github.yandex-team.ru/lego/islands/pull/2304 import { decl } from 'islands/common.blocks/i-bem/i-bem.react.js'; import { Editor, findDOMNode } from 'slate-react'; import { Value } from 'slate'; import Plain from 'slate-plain-serializer'; import Prism from 'prismjs'; var diffText = require('diff-text'); //import initialValue from './value.json'; function CodeBlock(props) { const { editor, node } = props const language = node.data.get('language') function onChange(event) { editor.change(c => c.setNodeByKey(node.key, { data: { language: event.target.value } }) ) } return ( <div style={{ position: 'relative' }}> <pre> <code {...props.attributes}>{props.children}</code> </pre> </div> ); } function CodeBlockLine(props) { return <div {...props.attributes}>{props.children}</div> } export default decl({ block: 'editor', willMount() { /* Получение plain-текста от АПИ */ this.plainText = `// A simple FizzBuzz implementation.`; let slateTreeCodeLines = Plain.deserialize(this.plainText).toJSON().document.nodes; // debugger; let slateInitialTree = { 'document': { nodes: [ { object: 'block', type: 'code', data: { language: this.props.lang }, nodes: slateTreeCodeLines } ] } }; this.setState({ value: Value.fromJSON(slateInitialTree), plainText: this.plainText }); }, updateDiff(plainText) { let diff = diffText(' ' + this.state.plainText + ' ', ' ' + plainText + ' '); let diffChain = diff.map(function(diffItem) { return diffItem[0]; }).join(','); let changedLines = []; // BACKSPACE if (diffChain === '0,-1,0') { this.lastDiff = { type: 'backspace', data: [diff[1][1].length] }; changedLines.push((diff[0][1].match(/\n/g) || []).length + 1); for (let i = changedLines[0]; i < (changedLines[0] + (diff[1][1].match(/\n/g) || []).length); i++) { changedLines.push(i + 1); } this.lastDiff.changedLines = changedLines; return; } // INSERT if (diffChain === '0,1,0') { this.lastDiff = { type: 'insert', data: [diff[1][1]] }; changedLines.push((diff[0][1].match(/\n/g) || []).length + 1); for (let i = changedLines[0]; i < (changedLines[0] + (diff[1][1].match(/\n/g) || []).length); i++) { changedLines.push(i + 1); } this.lastDiff.changedLines = changedLines; return; } // REPLACE if (diffChain === '0,-1,1,0') { this.lastDiff = { type: 'replace', data: [diff[1][1].length, diff[2][1]] }; changedLines.push((diff[0][1].match(/\n/g) || []).length + 1); for (let i = changedLines[0]; i < (changedLines[0] + (diff[1][1].match(/\n/g) || []).length + (diff[2][1].match(/\n/g) || []).length); i++) { changedLines.push(i + 1); } this.lastDiff.changedLines = changedLines; return; } // кейс, когда заменяющая строка является подстрокой заменяемой if (diffChain === '0,-1,0,1,0') { this.lastDiff = { type: 'replace', data: [diff[1][1].length + diff[2][1].length + diff[3][1].length, diff[2][1]] }; changedLines.push((diff[0][1].match(/\n/g) || []).length + 1); for (let i = changedLines[0]; i < (changedLines[0] + (diff[1][1].match(/\n/g) || []).length + (diff[2][1].match(/\n/g) || []).length + (diff[3][1].match(/\n/g) || []).length); i++) { changedLines.push(i + 1); } this.lastDiff.changedLines = changedLines; return; } this.lastDiff = null; }, onChange(change) { let value = change.value; let plainText = Plain.serialize(value); this.updateDiff(plainText); this.decoratedLine = 0; this.setState({ value: value, plainText: plainText }); }, /** * Render a Slate node. * * @param {Object} props * @return {Element} */ renderNode(props) { switch (props.node.type) { case 'code': return <CodeBlock {...props} /> case 'line': return <CodeBlockLine {...props} /> } }, /** * Render a Slate mark. * * @param {Object} props * @return {Element} */ renderMark(props) { const { children, mark, attributes } = props; return <span {...attributes} className={mark.type + ' token'}>{children}</span>; }, tokenToContent(token) { if (typeof token == 'string') { return token } else if (typeof token.content == 'string') { return token.content } else { return token.content.map(this.tokenToContent).join('') } }, onPaste(e) { //debugger; }, /** * Decorate code blocks with Prism.js highlighting. * * @param {Node} node * @return {Array} */ decorateNode(node) { if (node.type != 'line') return; const language = 'javascript' const textNode = node.getTexts().get(0); const string = node.getText(); const grammar = Prism.languages[language] const tokens = Prism.tokenize(string, grammar) const decorations = []; for (const token of tokens) { const content = this.tokenToContent(token) if (typeof token != 'string') { const range = { anchorKey: textNode.key, anchorOffset: string.indexOf(token.content), focusKey: textNode.key, focusOffset: string.indexOf(token.content) + token.length, marks: [{ type: token.type }], } decorations.push(range) } } return decorations; }, content() { return <Editor placeholder="Write some code..." value={this.state.value} onChange={this.onChange.bind(this)} />; } });