UNPKG

@atlaskit/pragmatic-drag-and-drop-hitbox

Version:

An optional package for Pragmatic drag and drop that enables the attaching of interaction information to a drop target

137 lines (128 loc) • 3.99 kB
import { stable } from './internal/stable'; import { axisLookup, reorder, uniqueKey } from './list-item'; function reorderAndCombine({ client, borderBox, axis }) { const quarterOfSize = borderBox[axis.size] / 4; // In the top 1/4: reorder-before // On the line: reorder-before to give a slight preference to reordering if (client[axis.point] <= borderBox[axis.start] + quarterOfSize) { return 'reorder-before'; } // In the bottom 1/4: reorder-after // On the line: reorder-after to give a slight preference to reordering if (client[axis.point] >= borderBox[axis.end] - quarterOfSize) { return 'reorder-after'; } return 'combine'; } // Note: not using `memoize-one` as all we need is a cached value. // We do not need to avoid executing an expensive function. const memoizeInstruction = stable(); function isPossible(...values) { return values.every(value => value === 'available' || value === 'blocked'); } function isNotAvailable(...values) { return values.every(value => value === 'not-available'); } /** * Calculate the `Instruction` for a drag operation based on the users input * and the available operations. * * Notes: * * - `attachInstruction` can attach an `Instruction | null`. `null` will be attached if all `operations` provided are `"not-available"`. * - Use `extractInstruction` to obtain the `Instruction | null` * * @example * * ```ts * dropTargetForElements({ * element: myElement, * getData({input, element}) { * // The data I want to attach to the drop target * const myData = {type: 'card', cardId: 'A'}; * * // Add an instruction to myData * return attachInstruction(myData, { * input, * element, * operations: { * 'reorder-before': 'available', * 'reorder-after': 'available', * combine: 'available', * } * }); * } * }); * ``` */ export function attachInstruction(userData, { operations, element, input, axis: axisValue = 'vertical' }) { var _operations$combine, _operations$reorderB, _operations$reorderA; const client = { x: input.clientX, y: input.clientY }; const borderBox = element.getBoundingClientRect(); const axis = axisLookup[axisValue]; const combine = (_operations$combine = operations.combine) !== null && _operations$combine !== void 0 ? _operations$combine : 'not-available'; const reorderAbove = (_operations$reorderB = operations['reorder-before']) !== null && _operations$reorderB !== void 0 ? _operations$reorderB : 'not-available'; const reorderBelow = (_operations$reorderA = operations['reorder-after']) !== null && _operations$reorderA !== void 0 ? _operations$reorderA : 'not-available'; const operation = (() => { // Combining not possible if (!isPossible(combine)) { // can reorder above and below if (isPossible(reorderAbove, reorderBelow)) { return reorder({ client, borderBox, axis }); } // can only reorder above if (isPossible(reorderAbove)) { return 'reorder-before'; } // can only reorder below if (isPossible(reorderBelow)) { return 'reorder-after'; } // no `true` values - no Outcome available. return null; } // combining is available const result = reorderAndCombine({ client, borderBox, axis }); if (result === 'reorder-after') { return isNotAvailable(reorderBelow) ? 'combine' : result; } if (result === 'reorder-before') { return isNotAvailable(reorderAbove) ? 'combine' : result; } return result; })(); // We cannot attach an instruction - all values passed where `false` or no values passed if (!operation) { return userData; } const instruction = { operation, blocked: operations[operation] === 'blocked', axis: axisValue }; const memoized = memoizeInstruction(instruction); return { ...userData, [uniqueKey]: memoized }; }