UNPKG

wix-style-react

Version:
155 lines 7.83 kB
import React from 'react'; import PropTypes from 'prop-types'; import { EditorState, Editor, CompositeDecorator } from 'draft-js'; import { convertFromHTML } from 'draft-convert'; import { st, classes, vars } from './RichTextInputArea.st.css'; 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'; import { WixStyleReactContext } from '../WixStyleReactProvider/context'; 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); }; 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, } = this.props; const isEditorEmpty = EditorUtilities.isEditorEmpty(this.state.editorState); const finalStatus = getStatusFromContext(this.context, status); return (React.createElement(WixStyleReactContext.Consumer, null, ({ newColorsBranding }) => (React.createElement("div", { "data-hook": dataHook, className: st(classes.root, { hidePlaceholder: !isEditorEmpty, disabled, hasError: !disabled && finalStatus === 'error', hasWarning: !disabled && finalStatus === 'warning', newColorsBranding, }, 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.editorRef.current.focus()); }, onBulletedList: this._setEditorState, onNumberedList: this._setEditorState })), React.createElement("div", { className: classes.editorWrapper, onClick: () => this.editorRef.current.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.propTypes = { /** Specifies a CSS class name to be appended to the component’s root element. */ className: PropTypes.string, /** Applies a data-hook HTML attribute that can be used in the tests. */ dataHook: PropTypes.string, /** Sets the initial value to be displayed in the editor. */ initialValue: PropTypes.string, /** Sets a placeholder message to display. */ placeholder: PropTypes.string, /** Specifies whether an editor and its toolbar should be disabled. */ disabled: PropTypes.bool, /** Specifies the status of a field. */ status: PropTypes.oneOf(['error', 'warning', 'loading']), /** Defines the message to display on status icon hover. If not given or empty there will be no tooltip. */ statusMessage: PropTypes.string, /** Defines a standard callback function for changes: `onChange(htmlText, { plainText })` */ onChange: PropTypes.func, /** Defines a minimum height for the editor (it grows by default) */ minHeight: PropTypes.string, /** Defines a maximum height for the editor (it grows by default) */ maxHeight: PropTypes.string, /** * Enables browsers spell checking. * Do not affect IE. * In Safari, autocorrects by default. */ spellCheck: PropTypes.bool, /** Defines text styles to be shown. */ texts: PropTypes.shape({ toolbarButtons: PropTypes.shape({ boldButtonLabel: PropTypes.string, italicButtonLabel: PropTypes.string, underlineButtonLabel: PropTypes.string, linkButtonLabel: PropTypes.string, bulletedListButtonLabel: PropTypes.string, numberedListButtonLabel: PropTypes.string, }), insertionForm: PropTypes.shape({ confirmButtonLabel: PropTypes.string, cancelButtonLabel: PropTypes.string, link: PropTypes.shape({ textInputPlaceholder: PropTypes.string, urlInputPlaceholder: PropTypes.string, }), }), }), }; RichTextInputArea.defaultProps = { initialValue: '<p/>', texts: {}, disabled: false, }; export default RichTextInputArea; //# sourceMappingURL=RichTextInputArea.js.map