UNPKG

@wordpress/block-editor

Version:
478 lines (407 loc) 13.9 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "RichTextShortcut", { enumerable: true, get: function () { return _shortcut.RichTextShortcut; } }); Object.defineProperty(exports, "RichTextToolbarButton", { enumerable: true, get: function () { return _toolbarButton.RichTextToolbarButton; } }); Object.defineProperty(exports, "__unstableRichTextInputEvent", { enumerable: true, get: function () { return _inputEvent.__unstableRichTextInputEvent; } }); exports.keyboardShortcutContext = exports.inputEventContext = exports.default = void 0; var _element = require("@wordpress/element"); var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _classnames = _interopRequireDefault(require("classnames")); var _data = require("@wordpress/data"); var _blocks = require("@wordpress/blocks"); var _compose = require("@wordpress/compose"); var _richText = require("@wordpress/rich-text"); var _deprecated = _interopRequireDefault(require("@wordpress/deprecated")); var _components = require("@wordpress/components"); var _autocomplete = require("../autocomplete"); var _blockEdit = require("../block-edit"); var _formatToolbarContainer = _interopRequireDefault(require("./format-toolbar-container")); var _store = require("../../store"); var _useUndoAutomaticChange = require("./use-undo-automatic-change"); var _useMarkPersistent = require("./use-mark-persistent"); var _usePasteHandler = require("./use-paste-handler"); var _useBeforeInputRules = require("./use-before-input-rules"); var _useInputRules = require("./use-input-rules"); var _useDelete = require("./use-delete"); var _useEnter = require("./use-enter"); var _useFormatTypes = require("./use-format-types"); var _useRemoveBrowserShortcuts = require("./use-remove-browser-shortcuts"); var _useShortcuts = require("./use-shortcuts"); var _useInputEvents = require("./use-input-events"); var _useInsertReplacementText = require("./use-insert-replacement-text"); var _useFirefoxCompat = require("./use-firefox-compat"); var _formatEdit = _interopRequireDefault(require("./format-edit")); var _utils = require("./utils"); var _shortcut = require("./shortcut"); var _toolbarButton = require("./toolbar-button"); var _inputEvent = require("./input-event"); /** * External dependencies */ /** * WordPress dependencies */ /** * Internal dependencies */ const keyboardShortcutContext = (0, _element.createContext)(); exports.keyboardShortcutContext = keyboardShortcutContext; const inputEventContext = (0, _element.createContext)(); /** * Removes props used for the native version of RichText so that they are not * passed to the DOM element and log warnings. * * @param {Object} props Props to filter. * * @return {Object} Filtered props. */ exports.inputEventContext = inputEventContext; function removeNativeProps(props) { const { __unstableMobileNoFocusOnMount, deleteEnter, placeholderTextColor, textAlign, selectionColor, tagsToEliminate, disableEditingMenu, fontSize, fontFamily, fontWeight, fontStyle, minWidth, maxWidth, setRef, disableSuggestions, disableAutocorrection, ...restProps } = props; return restProps; } function RichTextWrapper({ children, tagName = 'div', value: originalValue = '', onChange: originalOnChange, isSelected: originalIsSelected, multiline, inlineToolbar, wrapperClassName, autocompleters, onReplace, placeholder, allowedFormats, withoutInteractiveFormatting, onRemove, onMerge, onSplit, __unstableOnSplitAtEnd: onSplitAtEnd, __unstableOnSplitMiddle: onSplitMiddle, identifier, preserveWhiteSpace, __unstablePastePlainText: pastePlainText, __unstableEmbedURLOnPaste, __unstableDisableFormats: disableFormats, disableLineBreaks, __unstableAllowPrefixTransformations, ...props }, forwardedRef) { if (multiline) { (0, _deprecated.default)('wp.blockEditor.RichText multiline prop', { since: '6.1', version: '6.3', alternative: 'nested blocks (InnerBlocks)', link: 'https://developer.wordpress.org/block-editor/how-to-guides/block-tutorial/nested-blocks-inner-blocks/' }); } const instanceId = (0, _compose.useInstanceId)(RichTextWrapper); identifier = identifier || instanceId; props = removeNativeProps(props); const anchorRef = (0, _element.useRef)(); const { clientId } = (0, _blockEdit.useBlockEditContext)(); const selector = select => { const { getSelectionStart, getSelectionEnd } = select(_store.store); const selectionStart = getSelectionStart(); const selectionEnd = getSelectionEnd(); let isSelected; if (originalIsSelected === undefined) { isSelected = selectionStart.clientId === clientId && selectionEnd.clientId === clientId && selectionStart.attributeKey === identifier; } else if (originalIsSelected) { isSelected = selectionStart.clientId === clientId; } return { selectionStart: isSelected ? selectionStart.offset : undefined, selectionEnd: isSelected ? selectionEnd.offset : undefined, isSelected }; }; // This selector must run on every render so the right selection state is // retreived from the store on merge. // To do: fix this somehow. const { selectionStart, selectionEnd, isSelected } = (0, _data.useSelect)(selector); const { getSelectionStart, getSelectionEnd, getBlockRootClientId } = (0, _data.useSelect)(_store.store); const { selectionChange } = (0, _data.useDispatch)(_store.store); const multilineTag = (0, _utils.getMultilineTag)(multiline); const adjustedAllowedFormats = (0, _utils.getAllowedFormats)({ allowedFormats, disableFormats }); const hasFormats = !adjustedAllowedFormats || adjustedAllowedFormats.length > 0; let adjustedValue = originalValue; let adjustedOnChange = originalOnChange; // Handle deprecated format. if (Array.isArray(originalValue)) { (0, _deprecated.default)('wp.blockEditor.RichText value prop as children type', { since: '6.1', version: '6.3', alternative: 'value prop as string', link: 'https://developer.wordpress.org/block-editor/how-to-guides/block-tutorial/introducing-attributes-and-editable-fields/' }); adjustedValue = _blocks.children.toHTML(originalValue); adjustedOnChange = newValue => originalOnChange(_blocks.children.fromDOM((0, _richText.__unstableCreateElement)(document, newValue).childNodes)); } const onSelectionChange = (0, _element.useCallback)((start, end) => { const selection = {}; const unset = start === undefined && end === undefined; if (typeof start === 'number' || unset) { // If we are only setting the start (or the end below), which // means a partial selection, and we're not updating a selection // with the same client ID, abort. This means the selected block // is a parent block. if (end === undefined && getBlockRootClientId(clientId) !== getBlockRootClientId(getSelectionEnd().clientId)) { return; } selection.start = { clientId, attributeKey: identifier, offset: start }; } if (typeof end === 'number' || unset) { if (start === undefined && getBlockRootClientId(clientId) !== getBlockRootClientId(getSelectionStart().clientId)) { return; } selection.end = { clientId, attributeKey: identifier, offset: end }; } selectionChange(selection); }, [clientId, identifier]); const { formatTypes, prepareHandlers, valueHandlers, changeHandlers, dependencies } = (0, _useFormatTypes.useFormatTypes)({ clientId, identifier, withoutInteractiveFormatting, allowedFormats: adjustedAllowedFormats }); function addEditorOnlyFormats(value) { return valueHandlers.reduce((accumulator, fn) => fn(accumulator, value.text), value.formats); } function removeEditorOnlyFormats(value) { formatTypes.forEach(formatType => { // Remove formats created by prepareEditableTree, because they are editor only. if (formatType.__experimentalCreatePrepareEditableTree) { value = (0, _richText.removeFormat)(value, formatType.name, 0, value.text.length); } }); return value.formats; } function addInvisibleFormats(value) { return prepareHandlers.reduce((accumulator, fn) => fn(accumulator, value.text), value.formats); } const { value, getValue, onChange, ref: richTextRef } = (0, _richText.__unstableUseRichText)({ value: adjustedValue, onChange(html, { __unstableFormats, __unstableText }) { adjustedOnChange(html); Object.values(changeHandlers).forEach(changeHandler => { changeHandler(__unstableFormats, __unstableText); }); }, selectionStart, selectionEnd, onSelectionChange, placeholder, __unstableIsSelected: isSelected, __unstableMultilineTag: multilineTag, __unstableDisableFormats: disableFormats, preserveWhiteSpace, __unstableDependencies: [...dependencies, tagName], __unstableAfterParse: addEditorOnlyFormats, __unstableBeforeSerialize: removeEditorOnlyFormats, __unstableAddInvisibleFormats: addInvisibleFormats }); const autocompleteProps = (0, _autocomplete.useBlockEditorAutocompleteProps)({ onReplace, completers: autocompleters, record: value, onChange }); (0, _useMarkPersistent.useMarkPersistent)({ html: adjustedValue, value }); const keyboardShortcuts = (0, _element.useRef)(new Set()); const inputEvents = (0, _element.useRef)(new Set()); function onFocus() { anchorRef.current?.focus(); } const TagName = tagName; return (0, _element.createElement)(_element.Fragment, null, isSelected && (0, _element.createElement)(keyboardShortcutContext.Provider, { value: keyboardShortcuts }, (0, _element.createElement)(inputEventContext.Provider, { value: inputEvents }, (0, _element.createElement)(_components.Popover.__unstableSlotNameProvider, { value: "__unstable-block-tools-after" }, children && children({ value, onChange, onFocus }), (0, _element.createElement)(_formatEdit.default, { value: value, onChange: onChange, onFocus: onFocus, formatTypes: formatTypes, forwardedRef: anchorRef })))), isSelected && hasFormats && (0, _element.createElement)(_formatToolbarContainer.default, { inline: inlineToolbar, editableContentElement: anchorRef.current, value: value }), (0, _element.createElement)(TagName // Overridable props. , (0, _extends2.default)({ role: "textbox", "aria-multiline": !disableLineBreaks, "aria-label": placeholder }, props, autocompleteProps, { ref: (0, _compose.useMergeRefs)([forwardedRef, autocompleteProps.ref, props.ref, richTextRef, (0, _useBeforeInputRules.useBeforeInputRules)({ value, onChange }), (0, _useInputRules.useInputRules)({ getValue, onChange, __unstableAllowPrefixTransformations, formatTypes, onReplace, selectionChange }), (0, _useInsertReplacementText.useInsertReplacementText)(), (0, _useRemoveBrowserShortcuts.useRemoveBrowserShortcuts)(), (0, _useShortcuts.useShortcuts)(keyboardShortcuts), (0, _useInputEvents.useInputEvents)(inputEvents), (0, _useUndoAutomaticChange.useUndoAutomaticChange)(), (0, _usePasteHandler.usePasteHandler)({ isSelected, disableFormats, onChange, value, formatTypes, tagName, onReplace, onSplit, onSplitMiddle, __unstableEmbedURLOnPaste, multilineTag, preserveWhiteSpace, pastePlainText }), (0, _useDelete.useDelete)({ value, onMerge, onRemove }), (0, _useEnter.useEnter)({ removeEditorOnlyFormats, value, onReplace, onSplit, onSplitMiddle, multilineTag, onChange, disableLineBreaks, onSplitAtEnd }), (0, _useFirefoxCompat.useFirefoxCompat)({ value, onChange }), anchorRef]), contentEditable: true, suppressContentEditableWarning: true, className: (0, _classnames.default)('block-editor-rich-text__editable', props.className, 'rich-text') }))); } const ForwardedRichTextContainer = (0, _element.forwardRef)(RichTextWrapper); ForwardedRichTextContainer.Content = ({ value, tagName: Tag, multiline, ...props }) => { // Handle deprecated `children` and `node` sources. if (Array.isArray(value)) { (0, _deprecated.default)('wp.blockEditor.RichText value prop as children type', { since: '6.1', version: '6.3', alternative: 'value prop as string', link: 'https://developer.wordpress.org/block-editor/how-to-guides/block-tutorial/introducing-attributes-and-editable-fields/' }); value = _blocks.children.toHTML(value); } const MultilineTag = (0, _utils.getMultilineTag)(multiline); if (!value && MultilineTag) { value = `<${MultilineTag}></${MultilineTag}>`; } const content = (0, _element.createElement)(_element.RawHTML, null, value); if (Tag) { const { format, ...restProps } = props; return (0, _element.createElement)(Tag, restProps, content); } return content; }; ForwardedRichTextContainer.isEmpty = value => { return !value || value.length === 0; }; /** * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/rich-text/README.md */ var _default = ForwardedRichTextContainer; exports.default = _default; //# sourceMappingURL=index.js.map