@gechiui/block-editor
Version:
149 lines (124 loc) • 5.11 kB
JavaScript
;
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