UNPKG

@gechiui/block-editor

Version:
149 lines (124 loc) 5.11 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = useBlockDropZone; exports.getNearestBlockIndex = getNearestBlockIndex; var _data = require("@gechiui/data"); var _element = require("@gechiui/element"); var _compose = require("@gechiui/compose"); var _i18n = require("@gechiui/i18n"); var _useOnBlockDrop = _interopRequireDefault(require("../use-on-block-drop")); var _math = require("../../utils/math"); var _store = require("../../store"); /** * GeChiUI dependencies */ /** * Internal dependencies */ /** @typedef {import('../../utils/math').GCPoint} GCPoint */ /** * The orientation of a block list. * * @typedef {'horizontal'|'vertical'|undefined} GCBlockListOrientation */ /** * Given a list of block DOM elements finds the index that a block should be dropped * at. * * @param {Element[]} elements Array of DOM elements that represent each block in a block list. * @param {GCPoint} position The position of the item being dragged. * @param {GCBlockListOrientation} orientation The orientation of a block list. * * @return {number|undefined} The block index that's closest to the drag position. */ function getNearestBlockIndex(elements, position, orientation) { const allowedEdges = orientation === 'horizontal' ? ['left', 'right'] : ['top', 'bottom']; const isRightToLeft = (0, _i18n.isRTL)(); let candidateIndex; let candidateDistance; elements.forEach((element, index) => { const rect = element.getBoundingClientRect(); const [distance, edge] = (0, _math.getDistanceToNearestEdge)(position, rect, allowedEdges); if (candidateDistance === undefined || distance < candidateDistance) { // If the user is dropping to the trailing edge of the block // add 1 to the index to represent dragging after. // Take RTL languages into account where the left edge is // the trailing edge. const isTrailingEdge = edge === 'bottom' || !isRightToLeft && edge === 'right' || isRightToLeft && edge === 'left'; const offset = isTrailingEdge ? 1 : 0; // Update the currently known best candidate. candidateDistance = distance; candidateIndex = index + offset; } }); return candidateIndex; } /** * @typedef {Object} GCBlockDropZoneConfig * @property {string} rootClientId The root client id for the block list. */ /** * A React hook that can be used to make a block list handle drag and drop. * * @param {GCBlockDropZoneConfig} dropZoneConfig configuration data for the drop zone. */ function useBlockDropZone() { let { // An undefined value represents a top-level block. Default to an empty // string for this so that `targetRootClientId` can be easily compared to // values returned by the `getRootBlockClientId` selector, which also uses // an empty string to represent top-level blocks. rootClientId: targetRootClientId = '' } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; const [targetBlockIndex, setTargetBlockIndex] = (0, _element.useState)(null); const isLockedAll = (0, _data.useSelect)(select => { const { getTemplateLock } = select(_store.store); return getTemplateLock(targetRootClientId) === 'all'; }, [targetRootClientId]); const { getBlockListSettings } = (0, _data.useSelect)(_store.store); const { showInsertionPoint, hideInsertionPoint } = (0, _data.useDispatch)(_store.store); const onBlockDrop = (0, _useOnBlockDrop.default)(targetRootClientId, targetBlockIndex); const throttled = (0, _compose.useThrottle)((0, _element.useCallback)((event, currentTarget) => { var _getBlockListSettings; const blockElements = Array.from(currentTarget.children).filter( // Ensure the element is a block. It should have the `gc-block` class. element => element.classList.contains('gc-block')); const targetIndex = getNearestBlockIndex(blockElements, { x: event.clientX, y: event.clientY }, (_getBlockListSettings = getBlockListSettings(targetRootClientId)) === null || _getBlockListSettings === void 0 ? void 0 : _getBlockListSettings.orientation); setTargetBlockIndex(targetIndex === undefined ? 0 : targetIndex); if (targetIndex !== null) { showInsertionPoint(targetRootClientId, targetIndex); } }, []), 200); return (0, _compose.__experimentalUseDropZone)({ isDisabled: isLockedAll, onDrop: onBlockDrop, onDragOver(event) { // `currentTarget` is only available while the event is being // handled, so get it now and pass it to the thottled function. // https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget throttled(event, event.currentTarget); }, onDragLeave() { throttled.cancel(); hideInsertionPoint(); setTargetBlockIndex(null); }, onDragEnd() { throttled.cancel(); hideInsertionPoint(); setTargetBlockIndex(null); } }); } //# sourceMappingURL=index.js.map