84 lines
2.4 KiB
JavaScript
Raw Normal View History

import React, { PropTypes } from 'react';
2017-07-27 18:03:13 -04:00
import { Editor as Slate, Plain } from 'slate';
import { markdownToRemark, remarkToMarkdown } from '../../unified';
2017-04-18 12:30:38 -04:00
import Toolbar from '../Toolbar/Toolbar';
2017-06-23 14:42:40 -04:00
import { Sticky } from '../../../../UI/Sticky/Sticky';
2016-10-22 04:37:22 -07:00
import styles from './index.css';
export default class RawEditor extends React.Component {
constructor(props) {
super(props);
2017-07-27 18:03:13 -04:00
/**
* The value received is a Remark AST (MDAST), and must be stringified
* to plain text before Slate's Plain serializer can convert it to the
* Slate AST.
*/
const value = remarkToMarkdown(this.props.value);
this.state = {
2017-07-27 18:03:13 -04:00
editorState: Plain.deserialize(value || ''),
};
}
2017-07-27 08:46:53 -04:00
shouldComponentUpdate(nextProps, nextState) {
2017-07-27 18:03:13 -04:00
return !this.state.editorState.equals(nextState.editorState);
2017-07-27 08:46:53 -04:00
}
2017-07-08 23:23:14 -04:00
handleChange = editorState => {
this.setState({ editorState });
}
2017-07-27 18:03:13 -04:00
/**
* When the document value changes, serialize from Slate's AST back to plain
* text (which is Markdown), and then deserialize from that to a Remark MDAST,
* before passing up as the new value.
*/
2017-07-08 23:23:14 -04:00
handleDocumentChange = (doc, editorState) => {
2017-07-27 18:03:13 -04:00
const value = Plain.serialize(editorState);
const mdast = markdownToRemark(value);
this.props.onChange(mdast);
};
2017-07-27 18:03:13 -04:00
/**
* If a paste contains plain text, deserialize it to Slate's AST and insert
* to the document. Selection logic (where to insert, whether to replace) is
* handled by Slate.
*/
2017-07-10 18:26:07 -04:00
handlePaste = (e, data, state) => {
if (data.text) {
2017-07-27 18:03:13 -04:00
const fragment = Plain.deserialize(data.text).document;
2017-07-10 18:26:07 -04:00
return state.transform().insertFragment(fragment).apply();
}
};
2017-07-08 22:09:47 -04:00
handleToggleMode = () => {
this.props.onMode('visual');
};
render() {
2017-07-08 22:09:47 -04:00
return (
2017-07-27 18:03:13 -04:00
<div className={styles.rawWrapper}>
2017-07-08 22:09:47 -04:00
<Sticky
className={styles.editorControlBar}
classNameActive={styles.editorControlBarSticky}
fillContainerWidth
>
<Toolbar onToggleMode={this.handleToggleMode} disabled rawMode />
</Sticky>
2017-07-27 18:03:13 -04:00
<Slate
className={styles.rawEditor}
2017-07-08 23:23:14 -04:00
state={this.state.editorState}
2017-07-08 22:09:47 -04:00
onChange={this.handleChange}
2017-07-08 23:23:14 -04:00
onDocumentChange={this.handleDocumentChange}
2017-07-10 18:26:07 -04:00
onPaste={this.handlePaste}
/>
2017-07-08 22:09:47 -04:00
</div>
);
}
}
RawEditor.propTypes = {
onChange: PropTypes.func.isRequired,
onMode: PropTypes.func.isRequired,
value: PropTypes.object,
};