UNPKG

@wordpress/block-editor

Version:
395 lines (393 loc) 14.4 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // packages/block-editor/src/components/use-block-drop-zone/index.js var use_block_drop_zone_exports = {}; __export(use_block_drop_zone_exports, { default: () => useBlockDropZone, getDropTargetPosition: () => getDropTargetPosition, isDropTargetValid: () => isDropTargetValid }); module.exports = __toCommonJS(use_block_drop_zone_exports); var import_data = require("@wordpress/data"); var import_element = require("@wordpress/element"); var import_compose = require("@wordpress/compose"); var import_i18n = require("@wordpress/i18n"); var import_blocks = require("@wordpress/blocks"); var import_use_on_block_drop = __toESM(require("../use-on-block-drop")); var import_math = require("../../utils/math"); var import_store = require("../../store"); var import_lock_unlock = require("../../lock-unlock"); var THRESHOLD_DISTANCE = 30; var MINIMUM_HEIGHT_FOR_THRESHOLD = 120; var MINIMUM_WIDTH_FOR_THRESHOLD = 120; function getDropTargetPosition(blocksData, position, orientation = "vertical", options = {}) { const allowedEdges = orientation === "horizontal" ? ["left", "right"] : ["top", "bottom"]; let nearestIndex = 0; let insertPosition = "before"; let minDistance = Infinity; let targetBlockIndex = null; let nearestSide = "right"; const { dropZoneElement, parentBlockOrientation, rootBlockIndex = 0 } = options; if (dropZoneElement && parentBlockOrientation !== "horizontal") { const rect = dropZoneElement.getBoundingClientRect(); const [distance, edge] = (0, import_math.getDistanceToNearestEdge)(position, rect, [ "top", "bottom" ]); if (rect.height > MINIMUM_HEIGHT_FOR_THRESHOLD && distance < THRESHOLD_DISTANCE) { if (edge === "top") { return [rootBlockIndex, "before"]; } if (edge === "bottom") { return [rootBlockIndex + 1, "after"]; } } } const isRightToLeft = (0, import_i18n.isRTL)(); if (dropZoneElement && parentBlockOrientation === "horizontal") { const rect = dropZoneElement.getBoundingClientRect(); const [distance, edge] = (0, import_math.getDistanceToNearestEdge)(position, rect, [ "left", "right" ]); if (rect.width > MINIMUM_WIDTH_FOR_THRESHOLD && distance < THRESHOLD_DISTANCE) { if (isRightToLeft && edge === "right" || !isRightToLeft && edge === "left") { return [rootBlockIndex, "before"]; } if (isRightToLeft && edge === "left" || !isRightToLeft && edge === "right") { return [rootBlockIndex + 1, "after"]; } } } blocksData.forEach( ({ isUnmodifiedDefaultBlock, getBoundingClientRect, blockIndex, blockOrientation }) => { const rect = getBoundingClientRect(); if (!rect) { return; } let [distance, edge] = (0, import_math.getDistanceToNearestEdge)( position, rect, allowedEdges ); const [sideDistance, sideEdge] = (0, import_math.getDistanceToNearestEdge)( position, rect, ["left", "right"] ); const isPointInsideRect = (0, import_math.isPointContainedByRect)(position, rect); if (isUnmodifiedDefaultBlock && isPointInsideRect) { distance = 0; } else if (orientation === "vertical" && blockOrientation !== "horizontal" && (isPointInsideRect && sideDistance < THRESHOLD_DISTANCE || !isPointInsideRect && (0, import_math.isPointWithinTopAndBottomBoundariesOfRect)( position, rect ))) { targetBlockIndex = blockIndex; nearestSide = sideEdge; } if (distance < minDistance) { insertPosition = edge === "bottom" || !isRightToLeft && edge === "right" || isRightToLeft && edge === "left" ? "after" : "before"; minDistance = distance; nearestIndex = blockIndex; } } ); const adjacentIndex = nearestIndex + (insertPosition === "after" ? 1 : -1); const isNearestBlockUnmodifiedDefaultBlock = !!blocksData[nearestIndex]?.isUnmodifiedDefaultBlock; const isAdjacentBlockUnmodifiedDefaultBlock = !!blocksData[adjacentIndex]?.isUnmodifiedDefaultBlock; if (targetBlockIndex !== null) { return [targetBlockIndex, "group", nearestSide]; } if (!isNearestBlockUnmodifiedDefaultBlock && !isAdjacentBlockUnmodifiedDefaultBlock) { const insertionIndex = insertPosition === "after" ? nearestIndex + 1 : nearestIndex; return [insertionIndex, "insert"]; } return [ isNearestBlockUnmodifiedDefaultBlock ? nearestIndex : adjacentIndex, "replace" ]; } function isDropTargetValid(getBlockType, allowedBlocks, draggedBlockNames, targetBlockName) { let areBlocksAllowed = true; if (allowedBlocks) { const allowedBlockNames = allowedBlocks?.map(({ name }) => name); areBlocksAllowed = draggedBlockNames.every( (name) => allowedBlockNames?.includes(name) ); } const draggedBlockTypes = draggedBlockNames.map( (name) => getBlockType(name) ); const targetMatchesDraggedBlockParents = draggedBlockTypes.every( (block) => { const [allowedParentName] = block?.parent || []; if (!allowedParentName) { return true; } return allowedParentName === targetBlockName; } ); return areBlocksAllowed && targetMatchesDraggedBlockParents; } function isInsertionPoint(targetToCheck, ownerDocument) { const { defaultView } = ownerDocument; return !!(defaultView && targetToCheck instanceof defaultView.HTMLElement && targetToCheck.closest("[data-is-insertion-point]")); } function useBlockDropZone({ dropZoneElement, // 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 = "", parentClientId: parentBlockClientId = "", isDisabled = false } = {}) { const registry = (0, import_data.useRegistry)(); const [dropTarget, setDropTarget] = (0, import_element.useState)({ index: null, operation: "insert" }); const { getBlockType, getBlockVariations, getGroupingBlockName } = (0, import_data.useSelect)(import_blocks.store); const { canInsertBlockType, getBlockListSettings, getBlocks, getBlockIndex, getDraggedBlockClientIds, getBlockNamesByClientId, getAllowedBlocks, isDragging, isGroupable, isZoomOut, getSectionRootClientId, getBlockParents } = (0, import_lock_unlock.unlock)((0, import_data.useSelect)(import_store.store)); const { showInsertionPoint, hideInsertionPoint, startDragging, stopDragging } = (0, import_lock_unlock.unlock)((0, import_data.useDispatch)(import_store.store)); const onBlockDrop = (0, import_use_on_block_drop.default)( dropTarget.operation === "before" || dropTarget.operation === "after" ? parentBlockClientId : targetRootClientId, dropTarget.index, { operation: dropTarget.operation, nearestSide: dropTarget.nearestSide } ); const throttled = (0, import_compose.useThrottle)( (0, import_element.useCallback)( (event, ownerDocument) => { if (!isDragging()) { startDragging(); } const draggedBlockClientIds = getDraggedBlockClientIds(); const targetParents = [ targetRootClientId, ...getBlockParents(targetRootClientId, true) ]; const isTargetWithinDraggedBlocks = draggedBlockClientIds.some( (clientId) => targetParents.includes(clientId) ); if (isTargetWithinDraggedBlocks) { return; } const allowedBlocks = getAllowedBlocks(targetRootClientId); const targetBlockName = getBlockNamesByClientId([ targetRootClientId ])[0]; const draggedBlockNames = getBlockNamesByClientId( draggedBlockClientIds ); const isBlockDroppingAllowed = isDropTargetValid( getBlockType, allowedBlocks, draggedBlockNames, targetBlockName ); if (!isBlockDroppingAllowed) { return; } const sectionRootClientId = getSectionRootClientId(); if (isZoomOut() && sectionRootClientId !== targetRootClientId) { return; } const blocks = getBlocks(targetRootClientId).filter((block) => { return !((0, import_blocks.hasBlockSupport)(block.name, "visibility", true) && block.attributes?.metadata?.blockVisibility === false); }); if (blocks.length === 0) { registry.batch(() => { setDropTarget({ index: 0, operation: "insert" }); showInsertionPoint(targetRootClientId, 0, { operation: "insert" }); }); return; } const blocksData = blocks.map((block) => { const clientId = block.clientId; return { isUnmodifiedDefaultBlock: (0, import_blocks.isUnmodifiedDefaultBlock)(block), getBoundingClientRect: () => { const blockElement = ownerDocument.getElementById( `block-${clientId}` ); return blockElement ? blockElement.getBoundingClientRect() : null; }, blockIndex: getBlockIndex(clientId), blockOrientation: getBlockListSettings(clientId)?.orientation }; }); const dropTargetPosition = getDropTargetPosition( blocksData, { x: event.clientX, y: event.clientY }, getBlockListSettings(targetRootClientId)?.orientation, { dropZoneElement, parentBlockClientId, parentBlockOrientation: parentBlockClientId ? getBlockListSettings(parentBlockClientId)?.orientation : void 0, rootBlockIndex: getBlockIndex(targetRootClientId) } ); const [targetIndex, operation, nearestSide] = dropTargetPosition; const isTargetIndexEmptyDefaultBlock = blocksData[targetIndex]?.isUnmodifiedDefaultBlock; if (isZoomOut() && !isTargetIndexEmptyDefaultBlock && operation !== "insert") { return; } if (operation === "group") { const targetBlock = blocks[targetIndex]; const areAllImages = [ targetBlock.name, ...draggedBlockNames ].every((name) => name === "core/image"); const canInsertGalleryBlock = canInsertBlockType( "core/gallery", targetRootClientId ); const areGroupableBlocks = isGroupable([ targetBlock.clientId, getDraggedBlockClientIds() ]); const groupBlockVariations = getBlockVariations( getGroupingBlockName(), "block" ); const canInsertRow = groupBlockVariations && groupBlockVariations.find( ({ name }) => name === "group-row" ); if (areAllImages && !canInsertGalleryBlock && (!areGroupableBlocks || !canInsertRow)) { return; } if (!areAllImages && (!areGroupableBlocks || !canInsertRow)) { return; } } registry.batch(() => { setDropTarget({ index: targetIndex, operation, nearestSide }); const insertionPointClientId = [ "before", "after" ].includes(operation) ? parentBlockClientId : targetRootClientId; showInsertionPoint(insertionPointClientId, targetIndex, { operation, nearestSide }); }); }, [ isDragging, getAllowedBlocks, targetRootClientId, getBlockNamesByClientId, getDraggedBlockClientIds, getBlockType, getSectionRootClientId, isZoomOut, getBlocks, getBlockListSettings, dropZoneElement, parentBlockClientId, getBlockIndex, registry, startDragging, showInsertionPoint, canInsertBlockType, isGroupable, getBlockVariations, getGroupingBlockName ] ), 200 ); return (0, import_compose.__experimentalUseDropZone)({ dropZoneElement, isDisabled, onDrop: onBlockDrop, onDragOver(event) { throttled(event, event.currentTarget.ownerDocument); }, onDragLeave(event) { const { ownerDocument } = event.currentTarget; if (isInsertionPoint(event.relatedTarget, ownerDocument) || isInsertionPoint(event.target, ownerDocument)) { return; } throttled.cancel(); hideInsertionPoint(); }, onDragEnd() { throttled.cancel(); stopDragging(); hideInsertionPoint(); } }); } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { getDropTargetPosition, isDropTargetValid }); //# sourceMappingURL=index.js.map