diff --git a/packages/strapi-helper-plugin/lib/src/components/Wysiwyg/index.js b/packages/strapi-helper-plugin/lib/src/components/Wysiwyg/index.js index f1d54a6dce..78349eab81 100644 --- a/packages/strapi-helper-plugin/lib/src/components/Wysiwyg/index.js +++ b/packages/strapi-helper-plugin/lib/src/components/Wysiwyg/index.js @@ -5,16 +5,39 @@ */ import React from 'react'; -import { ContentState, convertFromHTML, Editor, EditorState } from 'draft-js'; +import { ContentState, convertFromHTML, Editor, EditorState, getDefaultKeyBinding, RichUtils, convertToRaw } from 'draft-js'; import PropTypes from 'prop-types'; import { FormattedMessage } from 'react-intl'; import { isEmpty } from 'lodash'; import cn from 'classnames'; +import Controls from 'components/WysiwygInlineControls'; import styles from './styles.scss'; +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; + } +} + class Wysiwyg extends React.Component { - state = { editorState: EditorState.createEmpty(), isFocused: false, hasInitialValue: false }; + constructor(props) { + super(props); + this.state = { editorState: EditorState.createEmpty(), isFocused: false }; + this.focus = () => { + this.setState({ isFocused: true }); + return this.refs.editor.focus(); + } + } componentDidMount() { if (this.props.autoFocus) { @@ -32,40 +55,60 @@ class Wysiwyg extends React.Component { } } - focus = () => { - this.setState({ isFocused: true }); - this.domEditor.focus(); + handleKeyCommand(command, editorState) { + const newState = RichUtils.handleKeyCommand(editorState, command); + if (newState) { + this.onChange(newState); + return true; + } + return false; } - handleBlur = (editorState) => { - this.setState({ isFocused: false }); - this.domEditor.blur(); - } - - /** - * Determine if the state is empty - * @return {Boolean} - */ - isContentStateEmpty = () => !this.state.editorState.getCurrentContent().hasText(); - onChange = (editorState) => { - const target = { - name: this.props.name, - type: 'text', + this.setState({ editorState }); + this.props.onChange({ target: { value: editorState.getCurrentContent().getPlainText(), - }; - - this.props.onChange({ target }); - this.setState({editorState}); + name: this.props.name, + type: 'textarea', + }}); } - setDomEditorRef = ref => this.domEditor = ref; + 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 + ) + ); + } + + toggleInlineStyle = (inlineStyle) => { + this.onChange( + RichUtils.toggleInlineStyle( + this.state.editorState, + inlineStyle + ) + ); + } setInitialValue = (props) => { - let editorState; - const blocksFromHTML = convertFromHTML(props.value); - const contentState = ContentState.createFromBlockArray(blocksFromHTML); - editorState = EditorState.createWithContent(contentState); + const contentState = ContentState.createFromText(props.value); + let editorState = EditorState.createWithContent(contentState); // Get the cursor at the end editorState = EditorState.moveFocusToEnd(editorState); @@ -73,40 +116,47 @@ class Wysiwyg extends React.Component { this.setState({ editorState, hasInitialValue: true }); } + previewHTML = () => { + const blocksFromHTML = convertFromHTML(this.props.value); + const contentState = ContentState.createFromBlockArray(blocksFromHTML); + const editorState = EditorState.createWithContent(contentState); + this.setState({ editorState }); + } + render() { - const { - placeholder, - } = this.props; const { editorState } = this.state; return ( - -
- - {(message) => ( - - )} - -
- + -
+
+ this.setState({ isFocused: false })} + onChange={this.onChange} + placeholder={this.props.placeholder} + ref="editor" + spellCheck={true} + /> + +
+ ); } } Wysiwyg.defaultProps = { autoFocus: false, - placeholder: 'app.utils.placeholder.defaultMessage', + placeholder: '', }; Wysiwyg.propTypes = { diff --git a/packages/strapi-helper-plugin/lib/src/components/Wysiwyg/styles.scss b/packages/strapi-helper-plugin/lib/src/components/Wysiwyg/styles.scss index 2baa7bed87..b3681f4d59 100644 --- a/packages/strapi-helper-plugin/lib/src/components/Wysiwyg/styles.scss +++ b/packages/strapi-helper-plugin/lib/src/components/Wysiwyg/styles.scss @@ -1,7 +1,6 @@ .editorWrapper { min-height: 350px; margin-top: .9rem; - padding: 8px 10px 0 10px; border: 1px solid #E3E9F3; border-radius: 0.25rem; line-height: 18px; @@ -9,8 +8,16 @@ box-shadow: 0px 1px 1px rgba(104, 118, 142, 0.05); } +.editor { + min-height: 300px; + padding: 8px 10px 0 10px; + font-size: 16px; + margin-top: 10px; + cursor: text; +} + .editorFocus { - border-color: #78caff; + border-color: #78caff !important; } .editorInput { diff --git a/packages/strapi-helper-plugin/lib/src/components/WysiwygInlineControls/index.js b/packages/strapi-helper-plugin/lib/src/components/WysiwygInlineControls/index.js new file mode 100644 index 0000000000..6a5ce8fb69 --- /dev/null +++ b/packages/strapi-helper-plugin/lib/src/components/WysiwygInlineControls/index.js @@ -0,0 +1,102 @@ +/** + * + * WysiwygInlineControls + * + */ + +import React from 'react'; +import PropTypes from 'prop-types'; +import cn from 'classnames'; + +import styles from './styles.scss'; + +const CONTROLS = [ + {label: 'B', style: 'BOLD'}, + {label: 'I', style: 'ITALIC', className: 'styleButtonItalic'}, + {label: 'U', style: 'UNDERLINE'}, + {label: 'UL', style: 'unordered-list-item'}, + {label: 'OL', style: 'ordered-list-item'}, +]; + +class StyleButton extends React.Component { + handleClick = (e) => { + e.preventDefault(); + + if (['UL', 'OL'].includes(this.props.label)) { + return this.props.onToggleBlock(this.props.style); + } + return this.props.onToggle(this.props.style); + } + + render() { + return ( +
+ {this.props.label} +
+ ); + } +} + +const WysiwygInlineControls = ({ editorState, onToggle, onToggleBlock, previewHTML }) => { + const selection = editorState.getSelection(); + const blockType = editorState + .getCurrentContent() + .getBlockForKey(selection.getStartKey()) + .getType(); + + const currentStyle = editorState.getCurrentInlineStyle(); + + return ( +
+ {CONTROLS.map(type => ( + + ))} + +
+ ); +} + +StyleButton.defaultProps = { + active: false, + className: '', + label: '', + onToggle: () => {}, + style: '', +}; + +StyleButton.propTypes = { + active: PropTypes.bool, + className: PropTypes.string, + label: PropTypes.string, + onToggle: PropTypes.func, + style: PropTypes.string +}; + +WysiwygInlineControls.defaultProps = { + onToggle: () => {}, +}; + +WysiwygInlineControls.propTypes = { + editorState: PropTypes.object.isRequired, + onToggle: PropTypes.func, +}; + +export default WysiwygInlineControls; diff --git a/packages/strapi-helper-plugin/lib/src/components/WysiwygInlineControls/styles.scss b/packages/strapi-helper-plugin/lib/src/components/WysiwygInlineControls/styles.scss new file mode 100644 index 0000000000..98dfe2bf01 --- /dev/null +++ b/packages/strapi-helper-plugin/lib/src/components/WysiwygInlineControls/styles.scss @@ -0,0 +1,52 @@ +.controlsWrapper { + display: flex; + user-select: none; + > div:nth-child(even) { + border-left: 0; + border-right: 0; + } +} + +.styleButton { + height: 31px; + min-width: 31px; + background-color: #FFFFFF; + border: 1px solid rgba(16,22,34,0.10); + font-size: 13px; + font-weight: 500; + line-height: 31px; + text-align: center; + // color: #999; + cursor: pointer; +} + +.styleButtonActive { + border: 0; + background: rgba(16,22,34,0.00); + box-shadow: inset 0 -1px 0 0 rgba(16,22,34,0.04), inset 0 1px 0 0 rgba(16,22,34,0.04); +} + +.styleButtonItalic { + font-style: italic; +} + +.wysiwygInlineControls { + height: 49px; + width: 100%; + display: flex; + user-select: none; + > div:nth-child(even) { + border-left: 0; + border-right: 0; + } + padding: 8px 10px 0 10px; + background-color: #F3F4F4; +} + +.wysiwygInlineControlsFocus { + border-color: #78caff; +} + +.active { + background-color: red; +}