UNPKG

@gechiui/block-editor

Version:
327 lines (279 loc) 10.8 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = WrappedBlockPopover; var _element = require("@gechiui/element"); var _lodash = require("lodash"); var _classnames = _interopRequireDefault(require("classnames")); var _blocks = require("@gechiui/blocks"); var _components = require("@gechiui/components"); var _data = require("@gechiui/data"); var _keyboardShortcuts = require("@gechiui/keyboard-shortcuts"); var _compose = require("@gechiui/compose"); var _dom = require("@gechiui/dom"); var _blockSelectionButton = _interopRequireDefault(require("./block-selection-button")); var _blockContextualToolbar = _interopRequireDefault(require("./block-contextual-toolbar")); var _inserter = _interopRequireDefault(require("../inserter")); var _store = require("../../store"); var _useBlockRefs = require("../block-list/use-block-props/use-block-refs"); var _usePopoverScroll = require("./use-popover-scroll"); /** * External dependencies */ /** * GeChiUI dependencies */ /** * Internal dependencies */ function selector(select) { const { isNavigationMode, isMultiSelecting, hasMultiSelection, isTyping, isCaretWithinFormattedText, getSettings, getLastMultiSelectedBlockClientId } = select(_store.store); return { isNavigationMode: isNavigationMode(), isMultiSelecting: isMultiSelecting(), isTyping: isTyping(), isCaretWithinFormattedText: isCaretWithinFormattedText(), hasMultiSelection: hasMultiSelection(), hasFixedToolbar: getSettings().hasFixedToolbar, lastClientId: getLastMultiSelectedBlockClientId() }; } function BlockPopover(_ref) { let { clientId, rootClientId, isValid, isEmptyDefaultBlock, capturingClientId, __unstablePopoverSlot, __unstableContentRef } = _ref; const { isNavigationMode, isMultiSelecting, isTyping, isCaretWithinFormattedText, hasMultiSelection, hasFixedToolbar, lastClientId } = (0, _data.useSelect)(selector, []); const isInsertionPointVisible = (0, _data.useSelect)(select => { const { isBlockInsertionPointVisible, getBlockInsertionPoint, getBlockOrder } = select(_store.store); if (!isBlockInsertionPointVisible()) { return false; } const insertionPoint = getBlockInsertionPoint(); const order = getBlockOrder(insertionPoint.rootClientId); return order[insertionPoint.index] === clientId; }, [clientId]); const isLargeViewport = (0, _compose.useViewportMatch)('medium'); const [isToolbarForced, setIsToolbarForced] = (0, _element.useState)(false); const [isInserterShown, setIsInserterShown] = (0, _element.useState)(false); const { stopTyping } = (0, _data.useDispatch)(_store.store); // Controls when the side inserter on empty lines should // be shown, including writing and selection modes. const showEmptyBlockSideInserter = !isTyping && !isNavigationMode && isEmptyDefaultBlock && isValid; const shouldShowBreadcrumb = isNavigationMode; const shouldShowContextualToolbar = !isNavigationMode && !hasFixedToolbar && isLargeViewport && !showEmptyBlockSideInserter && !isMultiSelecting && (!isTyping || isCaretWithinFormattedText); const canFocusHiddenToolbar = !isNavigationMode && !shouldShowContextualToolbar && !hasFixedToolbar && !isEmptyDefaultBlock; (0, _keyboardShortcuts.useShortcut)('core/block-editor/focus-toolbar', () => { setIsToolbarForced(true); stopTyping(true); }, { isDisabled: !canFocusHiddenToolbar }); (0, _element.useEffect)(() => { if (!shouldShowContextualToolbar) { setIsToolbarForced(false); } }, [shouldShowContextualToolbar]); // Stores the active toolbar item index so the block toolbar can return focus // to it when re-mounting. const initialToolbarItemIndexRef = (0, _element.useRef)(); const selectedElement = (0, _useBlockRefs.__unstableUseBlockElement)(clientId); const lastSelectedElement = (0, _useBlockRefs.__unstableUseBlockElement)(lastClientId); const capturingElement = (0, _useBlockRefs.__unstableUseBlockElement)(capturingClientId); const popoverScrollRef = (0, _usePopoverScroll.usePopoverScroll)(__unstableContentRef); if (!shouldShowBreadcrumb && !shouldShowContextualToolbar && !isToolbarForced && !showEmptyBlockSideInserter) { return null; } let node = selectedElement; if (!node) { return null; } if (capturingClientId) { node = capturingElement; } let anchorRef = node; if (hasMultiSelection) { // Wait to render the popover until the bottom reference is available // as well. if (!lastSelectedElement) { return null; } anchorRef = { top: node, bottom: lastSelectedElement }; } function onFocus() { setIsInserterShown(true); } function onBlur() { setIsInserterShown(false); } // Position above the anchor, pop out towards the right, and position in the // left corner. For the side inserter, pop out towards the left, and // position in the right corner. // To do: refactor `Popover` to make this prop clearer. const popoverPosition = showEmptyBlockSideInserter ? 'top left right' : 'top right left'; const { ownerDocument } = node; const stickyBoundaryElement = showEmptyBlockSideInserter ? undefined : // The sticky boundary element should be the boundary at which the // the block toolbar becomes sticky when the block scolls out of view. // In case of an iframe, this should be the iframe boundary, otherwise // the scroll container. ownerDocument.defaultView.frameElement || (0, _dom.getScrollContainer)(node) || ownerDocument.body; return (0, _element.createElement)(_components.Popover, { ref: popoverScrollRef, noArrow: true, animate: false, position: popoverPosition, focusOnMount: false, anchorRef: anchorRef, className: (0, _classnames.default)('block-editor-block-list__block-popover', { 'is-insertion-point-visible': isInsertionPointVisible }), __unstableStickyBoundaryElement: stickyBoundaryElement // Render in the old slot if needed for backward compatibility, // otherwise render in place (not in the the default popover slot). , __unstableSlotName: __unstablePopoverSlot || null, __unstableBoundaryParent: true // Observe movement for block animations (especially horizontal). , __unstableObserveElement: node, shouldAnchorIncludePadding: true // Used to safeguard sticky position behavior against cases where it would permanently // obscure specific sections of a block. , __unstableEditorCanvasWrapper: __unstableContentRef === null || __unstableContentRef === void 0 ? void 0 : __unstableContentRef.current }, (shouldShowContextualToolbar || isToolbarForced) && (0, _element.createElement)("div", { onFocus: onFocus, onBlur: onBlur // While ideally it would be enough to capture the // bubbling focus event from the Inserter, due to the // characteristics of click focusing of `button`s in // Firefox and Safari, it is not reliable. // // See: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Clicking_and_focus , tabIndex: -1, className: (0, _classnames.default)('block-editor-block-list__block-popover-inserter', { 'is-visible': isInserterShown }) }, (0, _element.createElement)(_inserter.default, { clientId: clientId, rootClientId: rootClientId, __experimentalIsQuick: true })), (shouldShowContextualToolbar || isToolbarForced) && (0, _element.createElement)(_blockContextualToolbar.default // If the toolbar is being shown because of being forced // it should focus the toolbar right after the mount. , { focusOnMount: isToolbarForced, __experimentalInitialIndex: initialToolbarItemIndexRef.current, __experimentalOnIndexChange: index => { initialToolbarItemIndexRef.current = index; } // Resets the index whenever the active block changes so // this is not persisted. See https://github.com/GeChiUI/gutenberg/pull/25760#issuecomment-717906169 , key: clientId }), shouldShowBreadcrumb && (0, _element.createElement)(_blockSelectionButton.default, { clientId: clientId, rootClientId: rootClientId, blockElement: node }), showEmptyBlockSideInserter && (0, _element.createElement)("div", { className: "block-editor-block-list__empty-block-inserter" }, (0, _element.createElement)(_inserter.default, { position: "bottom right", rootClientId: rootClientId, clientId: clientId, __experimentalIsQuick: true }))); } function wrapperSelector(select) { const { getSelectedBlockClientId, getFirstMultiSelectedBlockClientId, getBlockRootClientId, getBlock, getBlockParents, __experimentalGetBlockListSettingsForBlocks } = select(_store.store); const clientId = getSelectedBlockClientId() || getFirstMultiSelectedBlockClientId(); if (!clientId) { return; } const { name, attributes = {}, isValid } = getBlock(clientId) || {}; const blockParentsClientIds = getBlockParents(clientId); // Get Block List Settings for all ancestors of the current Block clientId const parentBlockListSettings = __experimentalGetBlockListSettingsForBlocks(blockParentsClientIds); // Get the clientId of the topmost parent with the capture toolbars setting. const capturingClientId = (0, _lodash.find)(blockParentsClientIds, parentClientId => { var _parentBlockListSetti; return (_parentBlockListSetti = parentBlockListSettings[parentClientId]) === null || _parentBlockListSetti === void 0 ? void 0 : _parentBlockListSetti.__experimentalCaptureToolbars; }); return { clientId, rootClientId: getBlockRootClientId(clientId), name, isValid, isEmptyDefaultBlock: name && (0, _blocks.isUnmodifiedDefaultBlock)({ name, attributes }), capturingClientId }; } function WrappedBlockPopover(_ref2) { let { __unstablePopoverSlot, __unstableContentRef } = _ref2; const selected = (0, _data.useSelect)(wrapperSelector, []); if (!selected) { return null; } const { clientId, rootClientId, name, isValid, isEmptyDefaultBlock, capturingClientId } = selected; if (!name) { return null; } return (0, _element.createElement)(BlockPopover, { clientId: clientId, rootClientId: rootClientId, isValid: isValid, isEmptyDefaultBlock: isEmptyDefaultBlock, capturingClientId: capturingClientId, __unstablePopoverSlot: __unstablePopoverSlot, __unstableContentRef: __unstableContentRef }); } //# sourceMappingURL=block-popover.js.map