UNPKG

@gechiui/block-editor

Version:
451 lines (376 loc) 13.3 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("@gechiui/element"); var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _classnames = _interopRequireDefault(require("classnames")); var _lodash = require("lodash"); var _data = require("@gechiui/data"); var _blocks = require("@gechiui/blocks"); var _compose = require("@gechiui/compose"); var _richText = require("@gechiui/rich-text"); var _deprecated = _interopRequireDefault(require("@gechiui/deprecated")); var _keycodes = require("@gechiui/keycodes"); var _components = require("@gechiui/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 _useCaretInFormat = require("./use-caret-in-format"); var _useMarkPersistent = require("./use-mark-persistent"); var _usePasteHandler = require("./use-paste-handler"); var _useInputRules = require("./use-input-rules"); 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 _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 */ /** * GeChiUI 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) { return (0, _lodash.omit)(props, ['__unstableMobileNoFocusOnMount', 'deleteEnter', 'placeholderTextColor', 'textAlign', 'selectionColor', 'tagsToEliminate', 'rootTagsToEliminate', 'disableEditingMenu', 'fontSize', 'fontFamily', 'fontWeight', 'fontStyle', 'minWidth', 'maxWidth', 'setRef']); } function RichTextWrapper(_ref, forwardedRef) { let { children, tagName = 'div', value: originalValue = '', onChange: originalOnChange, isSelected: originalIsSelected, multiline, inlineToolbar, wrapperClassName, autocompleters, onReplace, placeholder, allowedFormats, formattingControls, withoutInteractiveFormatting, onRemove, onMerge, onSplit, __unstableOnSplitAtEnd: onSplitAtEnd, __unstableOnSplitMiddle: onSplitMiddle, identifier, preserveWhiteSpace, __unstablePastePlainText: pastePlainText, __unstableEmbedURLOnPaste, __unstableDisableFormats: disableFormats, disableLineBreaks, unstableOnFocus, __unstableAllowPrefixTransformations, ...props } = _ref; 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, isMultiSelecting, hasMultiSelection } = select(_store.store); const selectionStart = getSelectionStart(); const selectionEnd = getSelectionEnd(); let isSelected; if (originalIsSelected === undefined) { isSelected = selectionStart.clientId === clientId && selectionStart.attributeKey === identifier; } else if (originalIsSelected) { isSelected = selectionStart.clientId === clientId; } return { selectionStart: isSelected ? selectionStart.offset : undefined, selectionEnd: isSelected ? selectionEnd.offset : undefined, isSelected, disabled: isMultiSelecting() || hasMultiSelection() }; }; // 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, disabled } = (0, _data.useSelect)(selector); const { selectionChange } = (0, _data.useDispatch)(_store.store); const multilineTag = (0, _utils.getMultilineTag)(multiline); const adjustedAllowedFormats = (0, _utils.getAllowedFormats)({ allowedFormats, formattingControls, disableFormats }); const hasFormats = !adjustedAllowedFormats || adjustedAllowedFormats.length > 0; let adjustedValue = originalValue; let adjustedOnChange = originalOnChange; // Handle deprecated format. if (Array.isArray(originalValue)) { adjustedValue = _blocks.children.toHTML(originalValue); adjustedOnChange = newValue => originalOnChange(_blocks.children.fromDOM((0, _richText.__unstableCreateElement)(document, newValue).childNodes)); } const onSelectionChange = (0, _element.useCallback)((start, end) => { selectionChange(clientId, identifier, start, end); }, [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, onChange, ref: richTextRef } = (0, _richText.__unstableUseRichText)({ value: adjustedValue, onChange(html, _ref2) { let { __unstableFormats, __unstableText } = _ref2; 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, _useCaretInFormat.useCaretInFormat)({ value }); (0, _useMarkPersistent.useMarkPersistent)({ html: adjustedValue, value }); const keyboardShortcuts = (0, _element.useRef)(new Set()); const inputEvents = (0, _element.useRef)(new Set()); function onKeyDown(event) { const { keyCode } = event; if (event.defaultPrevented) { return; } if (keyCode === _keycodes.DELETE || keyCode === _keycodes.BACKSPACE) { const { start, end, text } = value; const isReverse = keyCode === _keycodes.BACKSPACE; const hasActiveFormats = value.activeFormats && !!value.activeFormats.length; // Only process delete if the key press occurs at an uncollapsed edge. if (!(0, _richText.isCollapsed)(value) || hasActiveFormats || isReverse && start !== 0 || !isReverse && end !== text.length) { return; } if (onMerge) { onMerge(!isReverse); } // Only handle remove on Backspace. This serves dual-purpose of being // an intentional user interaction distinguishing between Backspace and // Delete to remove the empty field, but also to avoid merge & remove // causing destruction of two fields (merge, then removed merged). if (onRemove && (0, _richText.isEmpty)(value) && isReverse) { onRemove(!isReverse); } event.preventDefault(); } } function onFocus() { anchorRef.current.focus(); } const TagName = tagName; const content = (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, anchorRef: anchorRef.current }), (0, _element.createElement)(TagName // Overridable props. , (0, _extends2.default)({ role: "textbox", "aria-multiline": true, "aria-label": placeholder }, props, autocompleteProps, { ref: (0, _compose.useMergeRefs)([autocompleteProps.ref, props.ref, richTextRef, (0, _useInputRules.useInputRules)({ value, onChange, __unstableAllowPrefixTransformations, formatTypes, onReplace }), (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, _useEnter.useEnter)({ removeEditorOnlyFormats, value, onReplace, onSplit, onSplitMiddle, multilineTag, onChange, disableLineBreaks, onSplitAtEnd }), anchorRef, forwardedRef]) // Do not set the attribute if disabled. , contentEditable: disabled ? undefined : true, suppressContentEditableWarning: !disabled, className: (0, _classnames.default)('block-editor-rich-text__editable', props.className, 'rich-text'), onFocus: unstableOnFocus, onKeyDown: onKeyDown }))); if (!wrapperClassName) { return content; } (0, _deprecated.default)('gc.blockEditor.RichText wrapperClassName prop', { since: '5.4', alternative: 'className prop or create your own wrapper div' }); const className = (0, _classnames.default)('block-editor-rich-text', wrapperClassName); return (0, _element.createElement)("div", { className: className }, content); } const ForwardedRichTextContainer = (0, _element.forwardRef)(RichTextWrapper); ForwardedRichTextContainer.Content = _ref3 => { let { value, tagName: Tag, multiline, ...props } = _ref3; // Handle deprecated `children` and `node` sources. if (Array.isArray(value)) { 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) { return (0, _element.createElement)(Tag, (0, _lodash.omit)(props, ['format']), content); } return content; }; ForwardedRichTextContainer.isEmpty = value => { return !value || value.length === 0; }; /** * @see https://github.com/GeChiUI/gutenberg/blob/HEAD/packages/block-editor/src/components/rich-text/README.md */ var _default = ForwardedRichTextContainer; exports.default = _default; //# sourceMappingURL=index.js.map