2018-03-05 14:30:28 +01:00
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* Wysiwyg
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import React from 'react';
|
2018-03-06 18:20:05 +01:00
|
|
|
import { ContentState, convertFromHTML, Editor, EditorState, getDefaultKeyBinding, RichUtils, convertToRaw } from 'draft-js';
|
2018-03-05 14:30:28 +01:00
|
|
|
import PropTypes from 'prop-types';
|
|
|
|
|
import { FormattedMessage } from 'react-intl';
|
2018-03-05 19:23:06 +01:00
|
|
|
import { isEmpty } from 'lodash';
|
2018-03-05 14:30:28 +01:00
|
|
|
import cn from 'classnames';
|
|
|
|
|
|
2018-03-06 18:20:05 +01:00
|
|
|
import Controls from 'components/WysiwygInlineControls';
|
2018-03-05 14:30:28 +01:00
|
|
|
import styles from './styles.scss';
|
|
|
|
|
|
2018-03-06 18:20:05 +01:00
|
|
|
const styleMap = {
|
|
|
|
|
CODE: {
|
|
|
|
|
backgroundColor: 'rgba(0, 0, 0, 0.05)',
|
|
|
|
|
fontFamily: '"Inconsolata", "Menlo", "Consolas", monospace',
|
|
|
|
|
fontSize: 16,
|
|
|
|
|
padding: 2,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
function getBlockStyle(block) {
|
|
|
|
|
switch (block.getType()) {
|
|
|
|
|
case 'blockquote': return 'RichEditor-blockquote';
|
|
|
|
|
default: return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-05 14:30:28 +01:00
|
|
|
class Wysiwyg extends React.Component {
|
2018-03-06 18:20:05 +01:00
|
|
|
constructor(props) {
|
|
|
|
|
super(props);
|
|
|
|
|
this.state = { editorState: EditorState.createEmpty(), isFocused: false };
|
|
|
|
|
this.focus = () => {
|
|
|
|
|
this.setState({ isFocused: true });
|
|
|
|
|
return this.refs.editor.focus();
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-03-05 14:30:28 +01:00
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
|
if (this.props.autoFocus) {
|
|
|
|
|
this.focus();
|
|
|
|
|
}
|
2018-03-05 19:23:06 +01:00
|
|
|
|
|
|
|
|
if (!isEmpty(this.props.value)) {
|
|
|
|
|
this.setInitialValue(this.props);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
componentWillReceiveProps(nextProps) {
|
|
|
|
|
if (nextProps.value !== this.props.value && !this.state.hasInitialValue) {
|
|
|
|
|
this.setInitialValue(nextProps);
|
|
|
|
|
}
|
2018-03-05 14:30:28 +01:00
|
|
|
}
|
|
|
|
|
|
2018-03-06 18:20:05 +01:00
|
|
|
handleKeyCommand(command, editorState) {
|
|
|
|
|
const newState = RichUtils.handleKeyCommand(editorState, command);
|
|
|
|
|
if (newState) {
|
|
|
|
|
this.onChange(newState);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
2018-03-05 14:30:28 +01:00
|
|
|
}
|
|
|
|
|
|
2018-03-05 19:23:06 +01:00
|
|
|
onChange = (editorState) => {
|
2018-03-06 18:20:05 +01:00
|
|
|
this.setState({ editorState });
|
|
|
|
|
this.props.onChange({ target: {
|
2018-03-05 19:23:06 +01:00
|
|
|
value: editorState.getCurrentContent().getPlainText(),
|
2018-03-06 18:20:05 +01:00
|
|
|
name: this.props.name,
|
|
|
|
|
type: 'textarea',
|
|
|
|
|
}});
|
|
|
|
|
}
|
2018-03-05 19:23:06 +01:00
|
|
|
|
2018-03-06 18:20:05 +01:00
|
|
|
mapKeyToEditorCommand = (e) => {
|
|
|
|
|
if (e.keyCode === 9 /* TAB */) {
|
|
|
|
|
const newEditorState = RichUtils.onTab(
|
|
|
|
|
e,
|
|
|
|
|
this.state.editorState,
|
|
|
|
|
4, /* maxDepth */
|
|
|
|
|
);
|
|
|
|
|
if (newEditorState !== this.state.editorState) {
|
|
|
|
|
this.onChange(newEditorState);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
return getDefaultKeyBinding(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
toggleBlockType = (blockType) => {
|
|
|
|
|
this.onChange(
|
|
|
|
|
RichUtils.toggleBlockType(
|
|
|
|
|
this.state.editorState,
|
|
|
|
|
blockType
|
|
|
|
|
)
|
|
|
|
|
);
|
2018-03-05 19:23:06 +01:00
|
|
|
}
|
2018-03-05 14:30:28 +01:00
|
|
|
|
2018-03-06 18:20:05 +01:00
|
|
|
toggleInlineStyle = (inlineStyle) => {
|
|
|
|
|
this.onChange(
|
|
|
|
|
RichUtils.toggleInlineStyle(
|
|
|
|
|
this.state.editorState,
|
|
|
|
|
inlineStyle
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
2018-03-05 14:30:28 +01:00
|
|
|
|
2018-03-05 19:23:06 +01:00
|
|
|
setInitialValue = (props) => {
|
2018-03-06 18:20:05 +01:00
|
|
|
const contentState = ContentState.createFromText(props.value);
|
|
|
|
|
let editorState = EditorState.createWithContent(contentState);
|
2018-03-05 19:23:06 +01:00
|
|
|
|
|
|
|
|
// Get the cursor at the end
|
|
|
|
|
editorState = EditorState.moveFocusToEnd(editorState);
|
|
|
|
|
|
|
|
|
|
this.setState({ editorState, hasInitialValue: true });
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-06 18:20:05 +01:00
|
|
|
previewHTML = () => {
|
|
|
|
|
const blocksFromHTML = convertFromHTML(this.props.value);
|
|
|
|
|
const contentState = ContentState.createFromBlockArray(blocksFromHTML);
|
|
|
|
|
const editorState = EditorState.createWithContent(contentState);
|
|
|
|
|
this.setState({ editorState });
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-05 14:30:28 +01:00
|
|
|
render() {
|
|
|
|
|
const { editorState } = this.state;
|
|
|
|
|
|
|
|
|
|
return (
|
2018-03-06 18:20:05 +01:00
|
|
|
<div className={cn(styles.editorWrapper, this.state.isFocused && styles.editorFocus)}>
|
|
|
|
|
<Controls
|
|
|
|
|
editorState={editorState}
|
|
|
|
|
onToggle={this.toggleInlineStyle}
|
|
|
|
|
onToggleBlock={this.toggleBlockType}
|
|
|
|
|
previewHTML={this.previewHTML}
|
2018-03-05 19:23:06 +01:00
|
|
|
/>
|
2018-03-06 18:20:05 +01:00
|
|
|
<div className={styles.editor} onClick={this.focus}>
|
|
|
|
|
<Editor
|
|
|
|
|
blockStyleFn={getBlockStyle}
|
|
|
|
|
customStyleMap={styleMap}
|
|
|
|
|
editorState={editorState}
|
|
|
|
|
handleKeyCommand={this.handleKeyCommand}
|
|
|
|
|
keyBindingFn={this.mapKeyToEditorCommand}
|
|
|
|
|
onBlur={() => this.setState({ isFocused: false })}
|
|
|
|
|
onChange={this.onChange}
|
|
|
|
|
placeholder={this.props.placeholder}
|
|
|
|
|
ref="editor"
|
|
|
|
|
spellCheck={true}
|
|
|
|
|
/>
|
|
|
|
|
<input className={styles.editorInput} value="" tabIndex="-1" />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2018-03-05 14:30:28 +01:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Wysiwyg.defaultProps = {
|
2018-03-05 19:23:06 +01:00
|
|
|
autoFocus: false,
|
2018-03-06 18:20:05 +01:00
|
|
|
placeholder: '',
|
2018-03-05 14:30:28 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Wysiwyg.propTypes = {
|
|
|
|
|
autoFocus: PropTypes.bool,
|
|
|
|
|
placeholder: PropTypes.string,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default Wysiwyg;
|