UNPKG

@wordpress/block-editor

Version:
192 lines (180 loc) 6.36 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = useBlockDropZone; exports.getNearestBlockIndex = getNearestBlockIndex; var _reactNativeReanimated = require("react-native-reanimated"); var _data = require("@wordpress/data"); var _element = require("@wordpress/element"); var _store = require("../../store"); var _blockListContext = require("../block-list/block-list-context"); var _math = require("../../utils/math"); var _useOnBlockDrop = _interopRequireDefault(require("../use-on-block-drop")); /** * External dependencies */ /** * WordPress dependencies */ /** * Internal dependencies */ const UPDATE_TARGET_BLOCK_INDEX_THRESHOLD = 20; // In pixels /** @typedef {import('../../utils/math').WPPoint} WPPoint */ /** * The orientation of a block list. * * @typedef {'horizontal'|'vertical'|undefined} WPBlockListOrientation */ /** * Given a list of blocks layouts finds the index that a block should be dropped at. * * @param {Object} blocksLayouts Blocks layouts object. * @param {WPPoint} position The position of the item being dragged. * @param {WPBlockListOrientation} orientation The orientation of a block list. * @param {boolean} isRTL Check if current locale is RTL. * * @return {number|undefined} The block index that's closest to the drag position. */ function getNearestBlockIndex(blocksLayouts, position, orientation, isRTL) { const allowedEdges = orientation === 'horizontal' ? ['left', 'right'] : ['top', 'bottom']; const isRightToLeft = isRTL; let candidateIndex; let candidateDistance; // Only enabled for root level blocks. blocksLayouts.forEach((element, index) => { const { x, y, width, height } = element; const rect = { x: element.x, y: element.y, top: y, right: x + width, bottom: y + height, left: x, width, height }; 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} WPBlockDropZoneConfig * @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 {WPBlockDropZoneConfig} dropZoneConfig configuration data for the drop zone. * * @return {Object} An object that contains `targetBlockIndex` and the event * handlers `onBlockDragOver`, `onBlockDragEnd` and `onBlockDrop`. */ function useBlockDropZone({ // 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 = '' } = {}) { const targetBlockIndex = (0, _reactNativeReanimated.useSharedValue)(null); const dragPosition = { x: (0, _reactNativeReanimated.useSharedValue)(0), y: (0, _reactNativeReanimated.useSharedValue)(0) }; const prevDragPosition = { x: (0, _reactNativeReanimated.useSharedValue)(0), y: (0, _reactNativeReanimated.useSharedValue)(0) }; const { getBlockListSettings, getSettings } = (0, _data.useSelect)(_store.store); const { blocksLayouts, getBlockLayoutsOrderedByYCoord } = (0, _blockListContext.useBlockListContext)(); const getSortedBlocksLayouts = (0, _element.useCallback)(() => { return getBlockLayoutsOrderedByYCoord(blocksLayouts.current); // We use the value of `blocksLayouts` as the dependency. }, [blocksLayouts.current]); const isRTL = getSettings().isRTL; const onBlockDrop = (0, _useOnBlockDrop.default)(); const updateTargetBlockIndex = (0, _element.useCallback)(event => { const sortedBlockLayouts = getSortedBlocksLayouts(); const targetIndex = getNearestBlockIndex(sortedBlockLayouts, { x: event.x, y: event.y }, getBlockListSettings(targetRootClientId)?.orientation, isRTL); if (targetIndex !== null) { targetBlockIndex.value = targetIndex !== null && targetIndex !== void 0 ? targetIndex : 0; } }, [getSortedBlocksLayouts, getBlockListSettings, targetRootClientId, isRTL, targetBlockIndex]); (0, _reactNativeReanimated.useDerivedValue)(() => { const x = dragPosition.x.value; const y = dragPosition.y.value; const prevX = prevDragPosition.x.value; const prevY = prevDragPosition.y.value; // `updateTargetBlockIndex` performs expensive calculations, so we throttle // the call using a offset threshold based on the dragging position. if (Math.abs(x - prevX) >= UPDATE_TARGET_BLOCK_INDEX_THRESHOLD || Math.abs(y - prevY) >= UPDATE_TARGET_BLOCK_INDEX_THRESHOLD) { (0, _reactNativeReanimated.runOnJS)(updateTargetBlockIndex)({ x, y }); prevDragPosition.x.value = x; prevDragPosition.y.value = y; return true; } return false; }); return { onBlockDragOver({ x, y }) { dragPosition.x.value = x; dragPosition.y.value = y; }, onBlockDragOverWorklet({ x, y }) { 'worklet'; dragPosition.x.value = x; dragPosition.y.value = y; }, onBlockDragEnd() { targetBlockIndex.value = null; }, onBlockDrop: event => { if (targetBlockIndex.value !== null) { onBlockDrop({ ...event, targetRootClientId, targetBlockIndex: targetBlockIndex.value }); } }, targetBlockIndex }; } //# sourceMappingURL=index.native.js.map