UNPKG

@wix/design-system

Version:

@wix/design-system

113 lines 5.84 kB
import React from 'react'; import { EditorState, Editor, CompositeDecorator } from 'draft-js'; import { convertFromHTML } from 'draft-convert'; import { st, classes, vars } from './RichTextInputArea.st.css.js'; import RichTextToolbar from './Toolbar/RichTextToolbar'; import EditorUtilities from './EditorUtilities'; import { RichTextInputAreaContext } from './RichTextInputAreaContext'; import { defaultTexts } from './RichTextInputAreaTexts'; import StatusIndicator from '../StatusIndicator'; import { StatusContext, getStatusFromContext, } from '../FormField/StatusContext'; const decorator = new CompositeDecorator([ { strategy: EditorUtilities.findLinkEntities, component: ({ contentState, entityKey, children }) => { const { url } = contentState.getEntity(entityKey).getData(); return (React.createElement("a", { "data-hook": "richtextarea-link", href: url, className: classes.link, target: "_blank", // Avoids a potentially serious vulnerability for '_blank' links rel: "noopener noreferrer" }, children)); }, }, ]); class RichTextInputArea extends React.PureComponent { constructor(props) { super(props); this._setEditorState = (newEditorState, onStateChanged = () => { }) => { this.setState({ editorState: newEditorState }, () => { const { onChange = () => { } } = this.props; const htmlText = EditorUtilities.convertToHtml(newEditorState); const plainText = newEditorState.getCurrentContent().getPlainText(); onChange(htmlText, { plainText }); onStateChanged(); }); }; this._updateContentByValue = value => { const content = convertFromHTML({ htmlToEntity: (nodeName, node, createEntity) => { if (nodeName === 'a') { return createEntity('LINK', 'MUTABLE', { url: node.href }); } }, })(value); const updatedEditorState = EditorState.push(this.state.editorState, content); this.setState({ editorState: updatedEditorState }); }; /** Set value to display in the editor */ this.setValue = value => { this._updateContentByValue(value); }; /** Sets focus on rich text input area element */ this.focus = () => { this.editorRef.current?.focus(); }; /** Removes focus from rich text input area element */ this.blur = () => { this.editorRef.current?.blur(); }; const { texts: consumerTexts } = props; this.state = { editorState: EditorState.createEmpty(decorator), texts: { toolbarButtons: { ...defaultTexts.toolbarButtons, ...consumerTexts.toolbarButtons, }, insertionForm: { ...defaultTexts.insertionForm, ...consumerTexts.insertionForm, }, }, }; } componentDidMount() { const { initialValue } = this.props; // TODO: currently it treats the value as an initial value this._updateContentByValue(initialValue); this.editorRef = React.createRef(); } render() { const { dataHook, className, placeholder, disabled, minHeight, maxHeight, status, statusMessage, spellCheck, customButtons, } = this.props; const isEditorEmpty = EditorUtilities.isEditorEmpty(this.state.editorState); const finalStatus = getStatusFromContext(this.context, status); return (React.createElement("div", { "data-hook": dataHook, className: st(classes.root, { hidePlaceholder: !isEditorEmpty, disabled, hasError: !disabled && finalStatus === 'error', hasWarning: !disabled && finalStatus === 'warning', }, className), // Using CSS variable instead of applying minHeight & maxHeight on each child, down to the editor's content style: { [vars.minHeight]: minHeight, [vars.maxHeight]: maxHeight, } }, React.createElement(RichTextInputAreaContext.Provider, { value: { texts: this.state.texts, } }, React.createElement(RichTextToolbar, { dataHook: "richtextarea-toolbar", className: classes.toolbar, isDisabled: disabled, editorState: this.state.editorState, onBold: this._setEditorState, onItalic: this._setEditorState, onUnderline: this._setEditorState, onLink: newEditorState => { this._setEditorState(newEditorState, () => this.focus()); }, onBulletedList: this._setEditorState, onNumberedList: this._setEditorState, customButtons: customButtons })), React.createElement("div", { className: classes.editorWrapper, onClick: () => this.focus() }, React.createElement(Editor, { ref: this.editorRef, editorState: this.state.editorState, onChange: this._setEditorState, placeholder: placeholder, readOnly: disabled, spellCheck: spellCheck }), !disabled && (status || finalStatus === 'loading') && (React.createElement("span", { className: classes.statusIndicator }, React.createElement(StatusIndicator, { dataHook: "richtextarea-status-indicator", status: finalStatus, message: statusMessage })))))); } } RichTextInputArea.contextType = StatusContext; RichTextInputArea.displayName = 'RichTextInputArea'; RichTextInputArea.defaultProps = { initialValue: '<p/>', texts: {}, disabled: false, }; export default RichTextInputArea; //# sourceMappingURL=RichTextInputArea.js.map