UNPKG

@wordpress/block-editor

Version:
205 lines (197 loc) 8.38 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.InsertionPointOpenRef = void 0; var _clsx = _interopRequireDefault(require("clsx")); var _data = require("@wordpress/data"); var _element = require("@wordpress/element"); var _components = require("@wordpress/components"); var _i18n = require("@wordpress/i18n"); var _store = require("../../store"); var _useBlockRefs = require("../block-list/use-block-props/use-block-refs"); var _usePopoverScroll = _interopRequireDefault(require("./use-popover-scroll")); var _jsxRuntime = require("react/jsx-runtime"); /** * External dependencies */ /** * WordPress dependencies */ /** * Internal dependencies */ const MAX_POPOVER_RECOMPUTE_COUNTER = Number.MAX_SAFE_INTEGER; const InsertionPointOpenRef = exports.InsertionPointOpenRef = (0, _element.createContext)(); function BlockPopoverInbetween({ previousClientId, nextClientId, children, __unstablePopoverSlot, __unstableContentRef, operation = 'insert', nearestSide = 'right', ...props }) { // This is a temporary hack to get the inbetween inserter to recompute properly. const [popoverRecomputeCounter, forcePopoverRecompute] = (0, _element.useReducer)( // Module is there to make sure that the counter doesn't overflow. s => (s + 1) % MAX_POPOVER_RECOMPUTE_COUNTER, 0); const { orientation, rootClientId, isVisible } = (0, _data.useSelect)(select => { const { getBlockListSettings, getBlockRootClientId, isBlockVisible } = select(_store.store); const _rootClientId = getBlockRootClientId(previousClientId !== null && previousClientId !== void 0 ? previousClientId : nextClientId); return { orientation: getBlockListSettings(_rootClientId)?.orientation || 'vertical', rootClientId: _rootClientId, isVisible: isBlockVisible(previousClientId) && isBlockVisible(nextClientId) }; }, [previousClientId, nextClientId]); const previousElement = (0, _useBlockRefs.useBlockElement)(previousClientId); const nextElement = (0, _useBlockRefs.useBlockElement)(nextClientId); const isVertical = orientation === 'vertical'; const popoverAnchor = (0, _element.useMemo)(() => { if ( // popoverRecomputeCounter is by definition always equal or greater than 0. // This check is only there to satisfy the correctness of the // exhaustive-deps rule for the `useMemo` hook. popoverRecomputeCounter < 0 || !previousElement && !nextElement || !isVisible) { return undefined; } const contextElement = operation === 'group' ? nextElement || previousElement : previousElement || nextElement; return { contextElement, getBoundingClientRect() { const previousRect = previousElement ? previousElement.getBoundingClientRect() : null; const nextRect = nextElement ? nextElement.getBoundingClientRect() : null; let left = 0; let top = 0; let width = 0; let height = 0; if (operation === 'group') { const targetRect = nextRect || previousRect; top = targetRect.top; // No spacing is likely around blocks in this operation. // So width of the inserter containing rect is set to 0. width = 0; height = targetRect.bottom - targetRect.top; // Popover calculates its distance from mid-block so some // adjustments are needed to make it appear in the right place. left = nearestSide === 'left' ? targetRect.left - 2 : targetRect.right - 2; } else if (isVertical) { // vertical top = previousRect ? previousRect.bottom : nextRect.top; width = previousRect ? previousRect.width : nextRect.width; height = nextRect && previousRect ? nextRect.top - previousRect.bottom : 0; left = previousRect ? previousRect.left : nextRect.left; } else { top = previousRect ? previousRect.top : nextRect.top; height = previousRect ? previousRect.height : nextRect.height; if ((0, _i18n.isRTL)()) { // non vertical, rtl left = nextRect ? nextRect.right : previousRect.left; width = previousRect && nextRect ? previousRect.left - nextRect.right : 0; } else { // non vertical, ltr left = previousRect ? previousRect.right : nextRect.left; width = previousRect && nextRect ? nextRect.left - previousRect.right : 0; } // Avoid a negative width which happens when the next rect // is on the next line. width = Math.max(width, 0); } return new window.DOMRect(left, top, width, height); } }; }, [previousElement, nextElement, popoverRecomputeCounter, isVertical, isVisible, operation, nearestSide]); const popoverScrollRef = (0, _usePopoverScroll.default)(__unstableContentRef); // This is only needed for a smooth transition when moving blocks. // When blocks are moved up/down, their position can be set by // updating the `transform` property manually (i.e. without using CSS // transitions or animations). The animation, which can also scroll the block // editor, can sometimes cause the position of the Popover to get out of sync. // A MutationObserver is therefore used to make sure that changes to the // selectedElement's attribute (i.e. `transform`) can be tracked and used to // trigger the Popover to rerender. (0, _element.useLayoutEffect)(() => { if (!previousElement) { return; } const observer = new window.MutationObserver(forcePopoverRecompute); observer.observe(previousElement, { attributes: true }); return () => { observer.disconnect(); }; }, [previousElement]); (0, _element.useLayoutEffect)(() => { if (!nextElement) { return; } const observer = new window.MutationObserver(forcePopoverRecompute); observer.observe(nextElement, { attributes: true }); return () => { observer.disconnect(); }; }, [nextElement]); (0, _element.useLayoutEffect)(() => { if (!previousElement) { return; } previousElement.ownerDocument.defaultView.addEventListener('resize', forcePopoverRecompute); return () => { previousElement.ownerDocument.defaultView?.removeEventListener('resize', forcePopoverRecompute); }; }, [previousElement]); // If there's either a previous or a next element, show the inbetween popover. // Note that drag and drop uses the inbetween popover to show the drop indicator // before the first block and after the last block. if (!previousElement && !nextElement || !isVisible) { return null; } /* eslint-disable jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */ // 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 return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Popover, { ref: popoverScrollRef, animate: false, anchor: popoverAnchor, focusOnMount: false // Render in the old slot if needed for backward compatibility, // otherwise render in place (not in the default popover slot). , __unstableSlotName: __unstablePopoverSlot, inline: !__unstablePopoverSlot // Forces a remount of the popover when its position changes // This makes sure the popover doesn't animate from its previous position. , ...props, className: (0, _clsx.default)('block-editor-block-popover', 'block-editor-block-popover__inbetween', props.className), resize: false, flip: false, placement: "overlay", variant: "unstyled", children: /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { className: "block-editor-block-popover__inbetween-container", children: children }) }, nextClientId + '--' + rootClientId); /* eslint-enable jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */ } var _default = exports.default = BlockPopoverInbetween; //# sourceMappingURL=inbetween.js.map