@gechiui/block-editor
Version:
451 lines (376 loc) • 13.3 kB
JavaScript
;
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