@formkit/drag-and-drop
Version:
Drag and drop package.
1,481 lines (1,476 loc) • 94.1 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
addClass: () => addClass,
addEvents: () => addEvents,
addNodeClass: () => addNodeClass,
addParentClass: () => addParentClass,
animations: () => animations,
copyNodeStyle: () => copyNodeStyle,
createEmitter: () => createEmitter,
dragAndDrop: () => dragAndDrop,
dragStateProps: () => dragStateProps,
dragValues: () => dragValues,
dragstartClasses: () => dragstartClasses,
dropOrSwap: () => dropOrSwap,
emit: () => emit,
eventCoordinates: () => eventCoordinates,
getElFromPoint: () => getElFromPoint,
getRealCoords: () => getRealCoords2,
handleClickNode: () => handleClickNode,
handleClickParent: () => handleClickParent,
handleDragend: () => handleDragend,
handleDragstart: () => handleDragstart,
handleEnd: () => handleEnd3,
handleLongPress: () => handleLongPress,
handleNodeDragover: () => handleNodeDragover3,
handleNodeDrop: () => handleNodeDrop,
handleNodeKeydown: () => handleNodeKeydown,
handleNodePointerdown: () => handleNodePointerdown,
handleNodePointermove: () => handleNodePointermove,
handleNodePointerover: () => handleNodePointerover2,
handleNodePointerup: () => handleNodePointerup,
handleParentBlur: () => handleParentBlur,
handleParentDragover: () => handleParentDragover3,
handleParentDrop: () => handleParentDrop,
handleParentFocus: () => handleParentFocus,
handleParentKeydown: () => handleParentKeydown,
handleParentPointerover: () => handleParentPointerover2,
handlePointercancel: () => handlePointercancel,
initDrag: () => initDrag,
insert: () => insert,
isBrowser: () => isBrowser,
isDragState: () => isDragState,
isNode: () => isNode,
isSynthDragState: () => isSynthDragState,
noDefault: () => noDefault,
nodeEventData: () => nodeEventData,
nodes: () => nodes,
on: () => on,
parentEventData: () => parentEventData,
parentValues: () => parentValues,
parents: () => parents,
performSort: () => performSort,
performTransfer: () => performTransfer,
preventSortOnScroll: () => preventSortOnScroll,
remapFinished: () => remapFinished,
remapNodes: () => remapNodes,
removeClass: () => removeClass,
resetState: () => resetState,
setAttrs: () => setAttrs,
setDragState: () => setDragState,
setParentValues: () => setParentValues,
setupNode: () => setupNode,
setupNodeRemap: () => setupNodeRemap,
sort: () => sort,
state: () => state,
synthMove: () => synthMove,
tearDown: () => tearDown,
tearDownNode: () => tearDownNode,
tearDownNodeRemap: () => tearDownNodeRemap,
throttle: () => throttle,
transfer: () => transfer,
treeAncestors: () => treeAncestors,
updateConfig: () => updateConfig,
validateDragHandle: () => validateDragHandle,
validateDragstart: () => validateDragstart,
validateSort: () => validateSort,
validateTransfer: () => validateTransfer
});
module.exports = __toCommonJS(src_exports);
// src/plugins/animations/index.ts
function animations(animationsConfig = {}) {
const slideUp = [
{
transform: `translateY(${animationsConfig.yScale || 50}%)`
},
{
transform: `translateY(${animationsConfig.yScale || 0}%)`
}
];
const slideDown = [
{
transform: `translateY(-${animationsConfig.yScale || 50}%)`
},
{
transform: `translateY(${animationsConfig.yScale || 0}%)`
}
];
const slideLeft = [
{
transform: `translateX(${animationsConfig.xScale || 50}%)`
},
{
transform: `translateX(${animationsConfig.xScale || 0}%)`
}
];
const slideRight = [
{
transform: `translateX(-${animationsConfig.xScale || 50}%)`
},
{
transform: `translateX(${animationsConfig.xScale || 0}%)`
}
];
return (parent) => {
const parentData = parents.get(parent);
if (!parentData) return;
return {
setup() {
if (document.head.querySelector("[data-drag-and-drop]")) return;
},
setupNodeRemap(data) {
if (!isDragState(state)) return;
const duration = animationsConfig.duration || 150;
if (data.node.data.value === state.draggedNode.data.value) {
switch (state.incomingDirection) {
case "below":
animate(data.node.el, slideUp, duration);
break;
case "above":
animate(data.node.el, slideDown, duration);
break;
case "left":
animate(data.node.el, slideRight, duration);
break;
case "right":
animate(data.node.el, slideLeft, duration);
break;
}
return;
}
if (!state.affectedNodes.map((x) => x.data.value).includes(data.node.data.value))
return;
const nodeRect = data.node.el.getBoundingClientRect();
const nodeIndex = state.affectedNodes.findIndex(
(x) => x.data.value === data.node.data.value
);
const draggedNodeIndex = state.draggedNode.data.index;
const ascendingDirection = draggedNodeIndex >= state.targetIndex;
let adjacentNode;
if (ascendingDirection) {
adjacentNode = state.affectedNodes[nodeIndex + 1] ? state.affectedNodes[nodeIndex + 1] : state.affectedNodes[nodeIndex - 1];
} else {
adjacentNode = state.affectedNodes[nodeIndex - 1] ? state.affectedNodes[nodeIndex - 1] : state.affectedNodes[nodeIndex + 1];
}
if (adjacentNode) {
const xDiff = Math.abs(
nodeRect.x - adjacentNode.el.getBoundingClientRect().x
);
const yDiff = Math.abs(
nodeRect.y - adjacentNode.el.getBoundingClientRect().y
);
if (xDiff > yDiff && ascendingDirection) {
animate(data.node.el, slideRight, duration);
} else if (xDiff > yDiff && !ascendingDirection) {
animate(data.node.el, slideLeft, duration);
}
} else {
switch (state.incomingDirection) {
case "below":
animate(data.node.el, slideDown, duration);
break;
case "above":
animate(data.node.el, slideUp, duration);
break;
case "left":
animate(data.node.el, slideLeft, duration);
break;
case "right":
animate(data.node.el, slideRight, duration);
break;
}
}
}
};
};
}
function animate(node, animation, duration) {
if (!state) return;
state.preventEnter = true;
node.animate(animation, {
duration,
easing: "ease-in-out"
});
setTimeout(() => {
if (!state) return;
state.preventEnter = false;
}, duration);
}
// src/plugins/insert/index.ts
var insertState = {
draggedOverNodes: [],
draggedOverParent: null,
targetIndex: 0,
ascending: false,
coordinates: { x: 0, y: 0 },
insertPoint: null,
dragging: false
};
var documentController;
function insert(insertConfig) {
return (parent) => {
const parentData = parents.get(parent);
if (!parentData) return;
const insertParentConfig = {
...parentData.config,
insertConfig
};
return {
teardown() {
if (parentData.abortControllers.root) {
parentData.abortControllers.root.abort();
}
},
setup() {
insertParentConfig.handleNodeDragover = insertConfig.handleNodeDragover || handleNodeDragover;
insertParentConfig.handleParentPointerover = insertConfig.handleParentPointerover || handleParentPointerover;
insertParentConfig.handleNodePointerover = insertConfig.handleNodePointerover || handleParentPointerover;
insertParentConfig.handleParentDragover = insertConfig.handleParentDragover || handleParentDragover;
const originalHandleend = insertParentConfig.handleEnd;
insertParentConfig.handleEnd = (state2) => {
handleEnd(state2);
originalHandleend(state2);
};
parentData.on("dragStarted", () => {
documentController = addEvents(document, {
dragover: checkPosition,
pointermove: checkPosition
});
});
parentData.on("dragEnded", () => {
documentController?.abort();
});
parentData.config = insertParentConfig;
state.on("dragStarted", () => {
const insertPoint = parentData.config.insertConfig?.insertPoint({
el: parent,
data: parentData
});
if (!insertPoint) return;
if (!document.body.contains(insertPoint))
document.body.appendChild(insertPoint);
Object.assign(insertPoint, {
position: "absolute",
display: "none"
});
insertState.insertPoint = insertPoint;
});
window.addEventListener("scroll", defineRanges.bind(null, parent));
window.addEventListener("resize", defineRanges.bind(null, parent));
},
remapFinished() {
defineRanges(parent);
}
};
};
}
function checkPosition(e) {
if (!isDragState(state)) return;
const el = document.elementFromPoint(e.clientX, e.clientY);
if (!(el instanceof HTMLElement)) return;
if (!parents.has(el)) {
const insertPoint = insertState.insertPoint;
if (insertPoint && insertPoint === el) return;
if (insertPoint) insertPoint.style.display = "none";
if (insertState.draggedOverParent) {
removeClass(
[insertState.draggedOverParent.el],
insertState.draggedOverParent.data.config.dropZoneClass
);
}
insertState.draggedOverNodes = [];
insertState.draggedOverParent = null;
state.currentParent = state.initialParent;
}
}
function ascendingVertical(nodeCoords, nextNodeCoords) {
const center = nodeCoords.top + nodeCoords.height / 2;
if (!nextNodeCoords) {
return {
y: [center, center + nodeCoords.height / 2 + 10],
x: [nodeCoords.left, nodeCoords.right],
vertical: true
};
}
return {
y: [
center,
nodeCoords.bottom + Math.abs(nodeCoords.bottom - nextNodeCoords.top) / 2
],
x: [nodeCoords.left, nodeCoords.right],
vertical: true
};
}
function descendingVertical(nodeCoords, prevNodeCoords) {
const center = nodeCoords.top + nodeCoords.height / 2;
if (!prevNodeCoords) {
return {
y: [center - nodeCoords.height / 2 - 10, center],
x: [nodeCoords.left, nodeCoords.right],
vertical: true
};
}
return {
y: [
prevNodeCoords.bottom + Math.abs(prevNodeCoords.bottom - nodeCoords.top) / 2,
center
],
x: [nodeCoords.left, nodeCoords.right],
vertical: true
};
}
function ascendingHorizontal(nodeCoords, nextNodeCoords, lastInRow = false) {
const center = nodeCoords.left + nodeCoords.width / 2;
if (!nextNodeCoords) {
return {
x: [center, center + nodeCoords.width],
y: [nodeCoords.top, nodeCoords.bottom],
vertical: false
};
}
if (lastInRow) {
return {
x: [center, nodeCoords.right + 10],
y: [nodeCoords.top, nodeCoords.bottom],
vertical: false
};
} else {
const nextNodeCenter = nextNodeCoords.left + nextNodeCoords.width / 2;
return {
x: [center, center + Math.abs(center - nextNodeCenter) / 2],
y: [nodeCoords.top, nodeCoords.bottom],
vertical: false
};
}
}
function descendingHorizontal(nodeCoords, prevNodeCoords) {
const center = nodeCoords.left + nodeCoords.width / 2;
if (!prevNodeCoords) {
return {
x: [nodeCoords.left - 10, center],
y: [nodeCoords.top, nodeCoords.bottom],
vertical: false
};
}
return {
x: [
prevNodeCoords.right + Math.abs(prevNodeCoords.right - nodeCoords.left) / 2,
center
],
y: [nodeCoords.top, nodeCoords.bottom],
vertical: false
};
}
function getRealCoords(el) {
const { top, bottom, left, right, height, width } = el.getBoundingClientRect();
const scrollLeft = window.scrollX || document.documentElement.scrollLeft;
const scrollTop = window.scrollY || document.documentElement.scrollTop;
const adjustedTop = top + scrollTop;
const adjustedBottom = bottom + scrollTop;
const adjustedLeft = left + scrollLeft;
const adjustedRight = right + scrollLeft;
return {
top: adjustedTop,
bottom: adjustedBottom,
left: adjustedLeft,
right: adjustedRight,
height,
width
};
}
function defineRanges(parent) {
const parentData = parents.get(parent);
if (!parentData) return;
const enabledNodes = parentData.enabledNodes;
enabledNodes.forEach((node, index) => {
node.data.range = {};
let aboveOrBelowPrevious = false;
let aboveOrBelowAfter = false;
let prevNodeCoords = void 0;
let nextNodeCoords = void 0;
if (enabledNodes[index - 1])
prevNodeCoords = getRealCoords(enabledNodes[index - 1].el);
if (enabledNodes[index + 1])
nextNodeCoords = getRealCoords(enabledNodes[index + 1].el);
const nodeCoords = getRealCoords(node.el);
if (prevNodeCoords) {
aboveOrBelowPrevious = nodeCoords.top > prevNodeCoords.bottom || nodeCoords.bottom < prevNodeCoords.top;
}
if (nextNodeCoords) {
aboveOrBelowAfter = nodeCoords.top > nextNodeCoords.bottom || nodeCoords.bottom < nextNodeCoords.top;
}
const fullishWidth = parent.getBoundingClientRect().width * 0.8 < nodeCoords.width;
if (fullishWidth) {
node.data.range.ascending = ascendingVertical(nodeCoords, nextNodeCoords);
node.data.range.descending = descendingVertical(
nodeCoords,
prevNodeCoords
);
} else if (aboveOrBelowAfter && !aboveOrBelowPrevious) {
node.data.range.ascending = ascendingHorizontal(
nodeCoords,
nextNodeCoords,
true
);
node.data.range.descending = descendingHorizontal(
nodeCoords,
prevNodeCoords
);
} else if (!aboveOrBelowPrevious && !aboveOrBelowAfter) {
node.data.range.ascending = ascendingHorizontal(
nodeCoords,
nextNodeCoords
);
node.data.range.descending = descendingHorizontal(
nodeCoords,
prevNodeCoords
);
} else if (aboveOrBelowPrevious && !nextNodeCoords) {
node.data.range.ascending = ascendingHorizontal(nodeCoords);
} else if (aboveOrBelowPrevious && !aboveOrBelowAfter) {
node.data.range.ascending = ascendingHorizontal(
nodeCoords,
nextNodeCoords
);
node.data.range.descending = descendingHorizontal(nodeCoords);
}
});
}
function handleNodeDragover(data) {
data.e.preventDefault();
}
function handleParentDragover(data, state2) {
if (!state2 || !insertState) return;
data.e.stopPropagation();
data.e.preventDefault();
const { x, y } = eventCoordinates(data.e);
const clientX = x;
const clientY = y;
const scrollLeft = window.scrollX || document.documentElement.scrollLeft;
const scrollTop = window.scrollY || document.documentElement.scrollTop;
insertState.coordinates.x = clientX + scrollLeft;
insertState.coordinates.y = clientY + scrollTop;
const nestedParent = data.targetData.parent.data.nestedParent;
let realTargetParent = data.targetData.parent;
if (nestedParent) {
const rect = nestedParent.el.getBoundingClientRect();
if (insertState.coordinates.y > rect.top && insertState.coordinates.y < rect.bottom)
realTargetParent = nestedParent;
}
realTargetParent.el === state2.currentParent?.el ? moveBetween(realTargetParent) : moveOutside(realTargetParent, state2);
state2.currentParent = realTargetParent;
}
function moveBetween(data) {
if (data.data.config.sortable === false) return;
if (data.el === insertState.draggedOverParent?.el && insertState.draggedOverParent.data.getValues(data.el).length === 0) {
return;
} else if (insertState.draggedOverParent?.el) {
removeClass(
[insertState.draggedOverParent.el],
insertState.draggedOverParent.data.config.dropZoneClass
);
insertState.draggedOverParent = null;
}
const foundRange = findClosest(data.data.enabledNodes);
if (!foundRange) return;
const key = foundRange[1];
if (foundRange) {
const position = foundRange[0].data.range ? foundRange[0].data.range[key] : void 0;
if (position)
positioninsertPoint(
position,
foundRange[1] === "ascending",
foundRange[0]
);
}
}
function moveOutside(data, state2) {
if (data.el === state2.currentParent.el) return false;
const targetConfig = data.data.config;
if (targetConfig.treeGroup && state2.draggedNode.el.contains(data.el))
return false;
if (targetConfig.dropZone === false) return false;
const initialParentConfig = state2.initialParent.data.config;
if (targetConfig.accepts) {
return targetConfig.accepts(
data,
state2.initialParent,
state2.currentParent,
state2
);
} else if (!targetConfig.group || targetConfig.group !== initialParentConfig.group) {
return false;
}
const values = data.data.getValues(data.el);
if (!values.length) {
addParentClass([data.el], targetConfig.dropZoneClass);
insertState.draggedOverParent = data;
const insertPoint = insertState.insertPoint;
if (insertPoint) insertPoint.style.display = "none";
} else {
removeClass([state2.currentParent.el], targetConfig.dropZoneClass);
const enabledNodes = data.data.enabledNodes;
const foundRange = findClosest(enabledNodes);
if (!foundRange) return;
const key = foundRange[1];
if (foundRange) {
const position = foundRange[0].data.range ? foundRange[0].data.range[key] : void 0;
if (position)
positioninsertPoint(
position,
foundRange[1] === "ascending",
foundRange[0]
);
}
}
}
function findClosest(enabledNodes) {
let foundRange = null;
for (let x = 0; x < enabledNodes.length; x++) {
if (!state || !enabledNodes[x].data.range) continue;
if (enabledNodes[x].data.range.ascending) {
if (insertState.coordinates.y > enabledNodes[x].data.range.ascending.y[0] && insertState.coordinates.y < enabledNodes[x].data.range.ascending.y[1] && insertState.coordinates.x > enabledNodes[x].data.range.ascending.x[0] && insertState.coordinates.x < enabledNodes[x].data.range.ascending.x[1]) {
foundRange = [enabledNodes[x], "ascending"];
return foundRange;
}
}
if (enabledNodes[x].data.range.descending) {
if (insertState.coordinates.y > enabledNodes[x].data.range.descending.y[0] && insertState.coordinates.y < enabledNodes[x].data.range.descending.y[1] && insertState.coordinates.x > enabledNodes[x].data.range.descending.x[0] && insertState.coordinates.x < enabledNodes[x].data.range.descending.x[1]) {
foundRange = [enabledNodes[x], "descending"];
return foundRange;
}
}
}
}
function handleParentPointerover(data, state2) {
data.detail.e.stopPropagation();
const { x, y } = eventCoordinates(data.detail.e);
state2.coordinates.y = y;
state2.coordinates.x = x;
const nestedParent = data.detail.targetData.parent.data.nestedParent;
let realTargetParent = data.detail.targetData.parent;
if (nestedParent) {
const rect = nestedParent.el.getBoundingClientRect();
if (state2.coordinates.y > rect.top && state2.coordinates.y < rect.bottom)
realTargetParent = nestedParent;
}
const enabledNodes = realTargetParent.data.enabledNodes;
const foundRange = findClosest(enabledNodes);
if (!foundRange) return;
const key = foundRange[1];
if (foundRange) {
const position = foundRange[0].data.range ? foundRange[0].data.range[key] : void 0;
if (position)
positioninsertPoint(
position,
foundRange[1] === "ascending",
foundRange[0]
);
}
data.detail.targetData.parent.el === state2.currentParent.el ? moveBetween(realTargetParent) : moveOutside(realTargetParent, state2);
}
function positioninsertPoint(position, ascending, node) {
if (!state) return;
insertState.draggedOverNodes = [node];
if (!insertState.insertPoint) return;
if (position.vertical) {
const topPosition = position.y[ascending ? 1 : 0] - insertState.insertPoint.getBoundingClientRect().height / 2;
insertState.insertPoint.style.top = `${topPosition}px`;
const leftCoordinate = position.x[0];
const rightCoordinate = position.x[1];
insertState.insertPoint.style.left = `${leftCoordinate}px`;
insertState.insertPoint.style.right = `${rightCoordinate}px`;
insertState.insertPoint.style.height = "4px";
insertState.insertPoint.style.width = rightCoordinate - leftCoordinate + "px";
} else {
const leftPosition = position.x[ascending ? 1 : 0] - insertState.insertPoint.getBoundingClientRect().width / 2;
insertState.insertPoint.style.left = `${leftPosition}px`;
const topCoordinate = position.y[0];
const bottomCoordinate = position.y[1];
insertState.insertPoint.style.top = `${topCoordinate}px`;
insertState.insertPoint.style.bottom = `${bottomCoordinate}px`;
insertState.insertPoint.style.width = "4px";
insertState.insertPoint.style.height = bottomCoordinate - topCoordinate + "px";
}
insertState.targetIndex = node.data.index;
insertState.ascending = ascending;
insertState.insertPoint.style.display = "block";
}
function handleEnd(state2) {
const insertPoint = insertState.insertPoint;
if (!insertState.draggedOverParent) {
const draggedParentValues = parentValues(
state2.initialParent.el,
state2.initialParent.data
);
const transferred = state2.initialParent.el !== state2.currentParent.el;
const draggedValues = state2.draggedNodes.map((node) => node.data.value);
if (!transferred && insertState.draggedOverNodes[0] && insertState.draggedOverNodes[0].el !== state2.draggedNodes[0].el) {
const newParentValues = [
...draggedParentValues.filter((x) => !draggedValues.includes(x))
];
let index = insertState.draggedOverNodes[0].data.index;
if (insertState.targetIndex > state2.draggedNodes[0].data.index && !insertState.ascending) {
index--;
} else if (insertState.targetIndex < state2.draggedNodes[0].data.index && insertState.ascending) {
index++;
}
newParentValues.splice(index, 0, ...draggedValues);
setParentValues(state2.initialParent.el, state2.initialParent.data, [
...newParentValues
]);
if (state2.initialParent.data.config.onSort) {
}
} else if (transferred && insertState.draggedOverNodes.length) {
const targetParentValues = parentValues(
state2.currentParent.el,
state2.currentParent.data
);
const draggedParentValues2 = parentValues(
state2.initialParent.el,
state2.initialParent.data
);
let index = insertState.draggedOverNodes[0].data.index || 0;
if (insertState.ascending) index++;
const insertValues = state2.dynamicValues.length ? state2.dynamicValues : draggedValues;
targetParentValues.splice(index, 0, ...insertValues);
setParentValues(state2.currentParent.el, state2.currentParent.data, [
...targetParentValues
]);
draggedParentValues2.splice(state2.initialIndex, draggedValues.length);
setParentValues(state2.initialParent.el, state2.initialParent.data, [
...draggedParentValues2
]);
const data = {
sourceParent: state2.initialParent,
targetParent: state2.currentParent,
draggedNodes: state2.draggedNodes,
targetNodes: insertState.draggedOverNodes,
state: state2
};
if (state2.initialParent.data.config.insertConfig?.insertEvent)
state2.initialParent.data.config.insertConfig.insertEvent(data);
if (state2.currentParent.data.config.insertConfig?.insertEvent)
state2.currentParent.data.config.insertConfig.insertEvent(data);
}
} else if (insertState.draggedOverParent) {
const draggedValues = state2.draggedNodes.map((node) => node.data.value);
const draggedParentValues = parentValues(
state2.initialParent.el,
state2.initialParent.data
);
const newParentValues = [
...draggedParentValues.filter((x) => !draggedValues.includes(x))
];
const draggedOverParentValues = parentValues(
insertState.draggedOverParent.el,
insertState.draggedOverParent.data
);
const insertValues = state2.dynamicValues.length ? state2.dynamicValues : draggedValues;
draggedOverParentValues.push(...insertValues);
setParentValues(
insertState.draggedOverParent.el,
insertState.draggedOverParent.data,
[...draggedOverParentValues]
);
setParentValues(state2.initialParent.el, state2.initialParent.data, [
...newParentValues
]);
const data = {
sourceParent: state2.initialParent,
targetParent: state2.currentParent,
draggedNodes: state2.draggedNodes,
targetNodes: insertState.draggedOverNodes,
state: state2
};
if (state2.initialParent.data.config.insertConfig?.insertEvent)
state2.initialParent.data.config.insertConfig.insertEvent(data);
if (state2.currentParent.data.config.insertConfig?.insertEvent)
state2.currentParent.data.config.insertConfig.insertEvent(data);
removeClass(
[insertState.draggedOverParent.el],
insertState.draggedOverParent.data.config.dropZoneClass
);
}
if (insertPoint) insertPoint.style.display = "none";
const dropZoneClass = isSynthDragState(state2) ? state2.initialParent.data.config.synthDropZoneClass : state2.initialParent.data.config.dropZoneClass;
removeClass(
insertState.draggedOverNodes.map((node) => node.el),
dropZoneClass
);
const dragPlaceholderClass = state2.initialParent.data.config.dragPlaceholderClass;
removeClass(
state2.draggedNodes.map((node) => node.el),
dragPlaceholderClass
);
insertState.draggedOverNodes = [];
insertState.draggedOverParent = null;
}
// src/plugins/drop-or-swap/index.ts
var dropSwapState = {
draggedOverNodes: Array(),
initialDraggedIndex: void 0,
transferred: false,
dragging: false
};
var documentController2;
function dropOrSwap(dropSwapConfig = {}) {
return (parent) => {
const parentData = parents.get(parent);
if (!parentData) return;
const dropSwapParentConfig = {
...parentData.config,
dropSwapConfig
};
return {
setup() {
dropSwapParentConfig.handleNodeDragover = dropSwapConfig.handleNodeDragover || handleNodeDragover2;
dropSwapParentConfig.handleParentDragover = dropSwapConfig.handleParentDragover || handleParentDragover2;
dropSwapParentConfig.handleNodePointerover = dropSwapConfig.handleNodePointerover || handleNodePointerover;
dropSwapParentConfig.handleParentPointerover = dropSwapConfig.handleParentPointerover || handeParentPointerover;
const originalHandleend = dropSwapParentConfig.handleEnd;
dropSwapParentConfig.handleEnd = (state2) => {
handleEnd2(state2);
originalHandleend(state2);
};
parentData.on("dragStarted", () => {
documentController2 = addEvents(document, {
dragover: rootDragover,
handleRootPointerover: rootPointerover
});
});
parentData.on("dragEnded", () => {
documentController2?.abort();
});
parentData.config = dropSwapParentConfig;
}
};
};
}
function rootDragover(_e) {
if (!isDragState(state)) return;
removeClass(
[state.currentParent.el],
state.currentParent.data.config.dropZoneParentClass
);
state.currentParent = state.initialParent;
}
function rootPointerover(_e) {
if (!isSynthDragState(state)) return;
removeClass(
[state.currentParent.el],
state.currentParent.data.config.synthDropZoneParentClass
);
state.currentParent = state.initialParent;
}
function updateDraggedOverNodes(data, state2) {
const targetData = "detail" in data ? data.detail.targetData : data.targetData;
const config = targetData.parent.data.config;
const dropZoneClass = isSynthDragState(state2) ? config.synthDropZoneClass : config.dropZoneClass;
removeClass(
dropSwapState.draggedOverNodes.map((node) => node.el),
dropZoneClass
);
const enabledNodes = targetData.parent.data.enabledNodes;
if (!enabledNodes) return;
dropSwapState.draggedOverNodes = enabledNodes.slice(
targetData.node.data.index,
targetData.node.data.index + state2.draggedNodes.length
);
addNodeClass(
dropSwapState.draggedOverNodes.map((node) => node.el),
dropZoneClass,
true
);
state2.currentTargetValue = targetData.node.data.value;
state2.currentParent = targetData.parent;
addClass(
state2.currentParent.el,
isSynthDragState(state2) ? config.synthDropZoneParentClass : config.dropZoneParentClass,
state2.currentParent.data,
true
);
}
function handleNodeDragover2(data, state2) {
data.e.preventDefault();
data.e.stopPropagation();
updateDraggedOverNodes(data, state2);
}
function handleParentDragover2(data, state2) {
data.e.preventDefault();
data.e.stopPropagation();
const currentConfig = state2.currentParent.data.config;
removeClass(
dropSwapState.draggedOverNodes.map((node) => node.el),
currentConfig.dropZoneClass
);
removeClass([state2.currentParent.el], currentConfig.dropZoneParentClass);
const config = data.targetData.parent.data.config;
addClass(
data.targetData.parent.el,
config.dropZoneParentClass,
data.targetData.parent.data,
true
);
dropSwapState.draggedOverNodes = [];
state2.currentParent = data.targetData.parent;
}
function handeParentPointerover(data) {
const currentConfig = data.detail.state.currentParent.data.config;
removeClass(
dropSwapState.draggedOverNodes.map((node) => node.el),
currentConfig.synthDropZoneClass
);
removeClass(
[data.detail.state.currentParent.el],
currentConfig.synthDropZoneParentClass
);
const config = data.detail.targetData.parent.data.config;
addClass(
data.detail.targetData.parent.el,
config.synthDropZoneParentClass,
data.detail.targetData.parent.data,
true
);
dropSwapState.draggedOverNodes = [];
data.detail.state.currentParent = data.detail.targetData.parent;
}
function handleNodePointerover(data) {
if (!isSynthDragState(data.detail.state)) return;
updateDraggedOverNodes(data, data.detail.state);
}
function swapElements(arr1, arr2, index1, index2) {
const indices1 = Array.isArray(index1) ? index1 : [index1];
if (arr2 === null) {
const elementsFromArr1 = indices1.map((i) => arr1[i]);
const elementFromArr2 = arr1[index2];
arr1.splice(index2, 1, ...elementsFromArr1);
indices1.forEach((i, idx) => {
arr1[i] = idx === 0 ? elementFromArr2 : void 0;
});
return arr1.filter((el) => el !== void 0);
} else {
const elementsFromArr1 = indices1.map((i) => arr1[i]);
const elementFromArr2 = arr2[index2];
arr2.splice(index2, 1, ...elementsFromArr1);
indices1.forEach((i, idx) => {
arr1[i] = idx === 0 ? elementFromArr2 : void 0;
});
return [arr1.filter((el) => el !== void 0), arr2];
}
}
function handleEnd2(state2) {
const isSynth = isSynthDragState(state2);
removeClass(
[state2.currentParent.el],
isSynth ? state2.currentParent.data.config.synthDropZoneParentClass : state2.currentParent.data.config.dropZoneParentClass
);
removeClass(
dropSwapState.draggedOverNodes.map((node) => node.el),
isSynth ? state2.currentParent.data.config.synthDropZoneClass : state2.currentParent.data.config.dropZoneClass
);
const values = parentValues(state2.currentParent.el, state2.currentParent.data);
const draggedValues = state2.draggedNodes.map((node) => node.data.value);
const newValues = values.filter((x) => !draggedValues.includes(x));
const targetIndex = dropSwapState.draggedOverNodes[0]?.data.index;
const draggedIndex = state2.draggedNodes[0].data.index;
const initialParentValues = parentValues(
state2.initialParent.el,
state2.initialParent.data
);
if (targetIndex === void 0) {
if (state2.initialParent.el === state2.currentParent.el) return;
const newInitialValues = initialParentValues.filter(
(x) => !draggedValues.includes(x)
);
setParentValues(
state2.initialParent.el,
state2.initialParent.data,
newInitialValues
);
setParentValues(
state2.currentParent.el,
state2.currentParent.data,
values.concat(draggedValues)
);
return;
}
let swap = false;
const shouldSwap = state2.initialParent.data.config.dropSwapConfig?.shouldSwap;
if (shouldSwap)
swap = shouldSwap({
sourceParent: state2.initialParent,
targetParent: state2.currentParent,
draggedNodes: state2.draggedNodes,
targetNodes: dropSwapState.draggedOverNodes,
state: state2
});
if (state2.initialParent.el === state2.currentParent.el) {
newValues.splice(targetIndex, 0, ...draggedValues);
setParentValues(
state2.currentParent.el,
state2.currentParent.data,
swap ? swapElements(values, null, draggedIndex, targetIndex) : newValues
);
} else {
if (swap) {
const res = swapElements(
initialParentValues,
newValues,
state2.initialIndex,
targetIndex
);
setParentValues(
state2.initialParent.el,
state2.initialParent.data,
res[0]
);
setParentValues(
state2.currentParent.el,
state2.currentParent.data,
res[1]
);
} else {
const newInitialValues = initialParentValues.filter(
(x) => !draggedValues.includes(x)
);
setParentValues(
state2.initialParent.el,
state2.initialParent.data,
newInitialValues
);
newValues.splice(targetIndex, 0, ...draggedValues);
setParentValues(
state2.currentParent.el,
state2.currentParent.data,
newValues
);
}
}
}
// src/index.ts
function checkTouchSupport() {
if (!isBrowser) return false;
return "ontouchstart" in window || navigator.maxTouchPoints > 0;
}
var isBrowser = typeof window !== "undefined";
var dropped = false;
var documentController3;
var windowController;
var touchDevice = false;
var nodes = /* @__PURE__ */ new WeakMap();
var parents = /* @__PURE__ */ new WeakMap();
var treeAncestors = {};
var synthNodePointerDown = false;
function createEmitter() {
const callbacks = /* @__PURE__ */ new Map();
const emit2 = function(eventName, data) {
if (!callbacks.get(eventName)) return;
callbacks.get(eventName).forEach((cb) => {
cb(data);
});
};
const on2 = function(eventName, callback) {
const cbs = callbacks.get(eventName) ?? [];
cbs.push(callback);
callbacks.set(eventName, cbs);
};
return [emit2, on2];
}
var [emit, on] = createEmitter();
var baseDragState = {
activeDescendant: void 0,
affectedNodes: [],
currentTargetValue: void 0,
on,
emit,
newActiveDescendant: void 0,
originalZIndex: void 0,
pointerSelection: false,
preventEnter: false,
longPress: false,
longPressTimeout: 0,
remapJustFinished: false,
selectednodes: [],
selectedParent: void 0,
preventSynthDrag: false
};
var state = baseDragState;
function resetState() {
const baseDragState2 = {
activeDescendant: void 0,
affectedNodes: [],
on,
emit,
currentTargetValue: void 0,
originalZIndex: void 0,
pointerId: void 0,
preventEnter: false,
remapJustFinished: false,
selectednodes: [],
preventSynthDrag: false,
selectedParent: void 0,
pointerSelection: false,
synthScrollDirection: void 0,
draggedNodeDisplay: void 0,
synthDragScrolling: false,
longPress: false,
longPressTimeout: 0
};
state = { ...baseDragState2 };
}
function setDragState(dragStateProps2) {
Object.assign(state, dragStateProps2);
dragStateProps2.initialParent.data.emit("dragStarted", state);
dropped = false;
state.emit("dragStarted", state);
return state;
}
function handleRootPointerdown(_e) {
if (state.activeState) setActive(state.activeState.parent, void 0, state);
if (state.selectedState)
deselect(state.selectedState.nodes, state.selectedState.parent, state);
state.selectedState = state.activeState = void 0;
}
function handleRootPointerup(_e) {
if (!isSynthDragState(state)) return;
const config = state.currentParent.data.config;
if (isSynthDragState(state)) config.handleEnd(state);
}
function handleRootKeydown(e) {
if (e.key === "Escape") {
if (state.selectedState)
deselect(state.selectedState.nodes, state.selectedState.parent, state);
if (state.activeState)
setActive(state.activeState.parent, void 0, state);
state.selectedState = state.activeState = void 0;
}
}
function handleRootDrop(_e) {
}
function handleRootDragover(e) {
if (!isDragState(state)) return;
e.preventDefault();
}
function dragAndDrop({
parent,
getValues,
setValues,
config = {}
}) {
if (!isBrowser) return;
touchDevice = checkTouchSupport();
if (!documentController3)
documentController3 = addEvents(document, {
dragover: handleRootDragover,
pointerdown: handleRootPointerdown,
pointerup: handleRootPointerup,
keydown: handleRootKeydown,
drop: handleRootDrop
});
if (!windowController)
windowController = addEvents(window, {
resize: () => {
touchDevice = checkTouchSupport();
}
});
tearDown(parent);
const [emit2, on2] = createEmitter();
const parentData = {
getValues,
setValues,
config: {
dragDropEffect: config.dragDropEffect ?? "move",
dragEffectAllowed: config.dragEffectAllowed ?? "move",
draggedNodes,
dragstartClasses,
deepCopyStyles: config.deepCopyStyles ?? false,
handleNodeKeydown,
handleParentKeydown,
handleDragstart,
handleNodeDragover: handleNodeDragover3,
handleParentDragover: handleParentDragover3,
handleNodeDrop,
handlePointercancel,
handleEnd: handleEnd3,
handleDragend,
handleParentBlur,
handleParentFocus,
handleNodePointerup,
handleNodePointerover: handleNodePointerover2,
handleParentPointerover: handleParentPointerover2,
handleParentScroll,
handleNodePointerdown,
handleNodePointermove,
handleNodeDragenter,
handleNodeDragleave,
handleParentDrop,
multiDrag: config.multiDrag ?? false,
nativeDrag: config.nativeDrag ?? true,
performSort,
performTransfer,
root: config.root ?? document,
setupNode,
setupNodeRemap,
reapplyDragClasses,
tearDownNode,
tearDownNodeRemap,
remapFinished,
scrollBehavior: {
x: 0.95,
y: 0.95
},
threshold: {
horizontal: 0,
vertical: 0
},
...config
},
enabledNodes: [],
abortControllers: {},
privateClasses: [],
on: on2,
emit: emit2
};
const nodesObserver = new MutationObserver(nodesMutated);
nodesObserver.observe(parent, { childList: true });
parents.set(parent, parentData);
if (config.treeAncestor && config.treeGroup)
treeAncestors[config.treeGroup] = parent;
config.plugins?.forEach((plugin) => {
plugin(parent)?.tearDown?.();
});
config.plugins?.forEach((plugin) => {
plugin(parent)?.tearDown?.();
});
config.plugins?.forEach((plugin) => {
plugin(parent)?.setup?.();
});
setup(parent, parentData);
remapNodes(parent, true);
}
function dragStateProps(data, draggedNodes2) {
const { x, y } = eventCoordinates(data.e);
const rect = data.targetData.node.el.getBoundingClientRect();
return {
affectedNodes: [],
ascendingDirection: false,
clonedDraggedEls: [],
dynamicValues: [],
coordinates: {
x,
y
},
draggedNode: {
el: data.targetData.node.el,
data: data.targetData.node.data
},
draggedNodes: draggedNodes2,
incomingDirection: void 0,
initialIndex: data.targetData.node.data.index,
initialParent: {
el: data.targetData.parent.el,
data: data.targetData.parent.data
},
currentParent: {
el: data.targetData.parent.el,
data: data.targetData.parent.data
},
longPress: data.targetData.parent.data.config.longPress ?? false,
longPressTimeout: 0,
currentTargetValue: data.targetData.node.data.value,
scrollEls: [],
startLeft: x - rect.left,
startTop: y - rect.top,
targetIndex: data.targetData.node.data.index,
transferred: false
};
}
function performSort({
parent,
draggedNodes: draggedNodes2,
targetNode
}) {
const draggedValues = draggedNodes2.map((x) => x.data.value);
const targetParentValues = parentValues(parent.el, parent.data);
const originalIndex = draggedNodes2[0].data.index;
const enabledNodes = [...parent.data.enabledNodes];
const newParentValues = [
...targetParentValues.filter((x) => !draggedValues.includes(x))
];
newParentValues.splice(targetNode.data.index, 0, ...draggedValues);
if ("draggedNode" in state) state.currentTargetValue = targetNode.data.value;
setParentValues(parent.el, parent.data, [...newParentValues]);
if (parent.data.config.onSort)
parent.data.config.onSort({
parent: {
el: parent.el,
data: parent.data
},
previousValues: [...targetParentValues],
previousNodes: [...enabledNodes],
nodes: [...parent.data.enabledNodes],
values: [...newParentValues],
draggedNode: draggedNodes2[0],
previousPosition: originalIndex,
position: targetNode.data.index
});
}
function setActive(parent, newActiveNode, state2) {
const activeDescendantClass = parent.data.config.activeDescendantClass;
if (state2.activeState) {
{
removeClass([state2.activeState.node.el], activeDescendantClass);
if (state2.activeState.parent.el !== parent.el)
state2.activeState.parent.el.setAttribute("aria-activedescendant", "");
}
}
if (!newActiveNode) {
state2.activeState?.parent.el.setAttribute("aria-activedescendant", "");
state2.activeState = void 0;
return;
}
state2.activeState = {
node: newActiveNode,
parent
};
addNodeClass([newActiveNode.el], activeDescendantClass);
state2.activeState.parent.el.setAttribute(
"aria-activedescendant",
state2.activeState.node.el.id
);
}
function deselect(nodes2, parent, state2) {
const selectedClass = parent.data.config.selectedClass;
if (!state2.selectedState) return;
const iterativeNodes = Array.from(nodes2);
removeClass(
nodes2.map((x) => x.el),
selectedClass
);
for (const node of iterativeNodes) {
node.el.setAttribute("aria-selected", "false");
const index = state2.selectedState.nodes.findIndex((x) => x.el === node.el);
if (index === -1) continue;
state2.selectedState.nodes.splice(index, 1);
}
clearLiveRegion(parent);
}
function setSelected(parent, selectedNodes, newActiveNode, state2, pointerdown = false) {
state2.pointerSelection = pointerdown;
for (const node of selectedNodes) {
node.el.setAttribute("aria-selected", "true");
addNodeClass([node.el], parent.data.config.selectedClass, true);
}
state2.selectedState = {
nodes: selectedNodes,
parent
};
const selectedItems = selectedNodes.map(
(x) => x.el.getAttribute("aria-label")
);
if (selectedItems.length === 0) {
state2.selectedState = void 0;
clearLiveRegion(parent);
return;
}
setActive(parent, newActiveNode, state2);
updateLiveRegion(
parent,
`${selectedItems.join(
", "
)} ready for dragging. Use arrow keys to navigate. Press enter to drop ${selectedItems.join(
", "
)}.`
);
}
function updateLiveRegion(parent, message) {
const parentId = parent.el.id;
const liveRegion = document.getElementById(parentId + "-live-region");
if (!liveRegion) return;
liveRegion.textContent = message;
}
function clearLiveRegion(parent) {
const liveRegion = document.getElementById(parent.el.id + "-live-region");
if (!liveRegion) return;
liveRegion.textContent = "";
}
function handleParentBlur(_data, _state) {
}
function handleParentFocus(data, state2) {
const firstEnabledNode = data.targetData.parent.data.enabledNodes[0];
if (!firstEnabledNode) return;
if (state2.selectedState && state2.selectedState.parent.el !== data.targetData.parent.el) {
setActive(data.targetData.parent, firstEnabledNode, state2);
} else if (!state2.selectedState) {
setActive(data.targetData.parent, firstEnabledNode, state2);
}
}
function performTransfer({
currentParent,
targetParent,
initialParent,
draggedNodes: draggedNodes2,
initialIndex,
targetNode,
state: state2
}) {
const draggedValues = draggedNodes2.map((x) => x.data.value);
const currentParentValues = parentValues(
currentParent.el,
currentParent.data
).filter((x) => !draggedValues.includes(x));
const targetParentValues = parentValues(targetParent.el, targetParent.data);
const reset = initialParent.el === targetParent.el && targetParent.data.config.sortable === false;
let targetIndex;
if (targetNode) {
if (reset) {
targetIndex = initialIndex;
} else if (targetParent.data.config.sortable === false) {
targetIndex = targetParent.data.enabledNodes.length;
} else {
targetIndex = targetNode.data.index;
}
targetParentValues.splice(targetIndex, 0, ...draggedValues);
} else {
targetIndex = reset ? initialIndex : targetParent.data.enabledNodes.length;
targetParentValues.splice(targetIndex, 0, ...draggedValues);
}
setParentValues(currentParent.el, currentParent.data, currentParentValues);
setParentValues(targetParent.el, targetParent.data, targetParentValues);
if (targetParent.data.config.onTransfer) {
targetParent.data.config.onTransfer({
sourceParent: currentParent,
targetParent,
initialParent,
draggedNodes: draggedNodes2,
targetIndex,
state: state2
});
}
if (currentParent.data.config.onTransfer) {
currentParent.data.config.onTransfer({
sourceParent: currentParent,
targetParent,
initialParent,
draggedNodes: draggedNodes2,
targetIndex,
state: state2
});
}
}
function parentValues(parent, parentData) {
return [...parentData.getValues(parent)];
}
function findArrayCoordinates(obj, targetArray, path = []) {
let result = [];
if (obj === targetArray) result.push(path);
if (Array.isArray(obj)) {
const index = obj.findIndex((el) => el === targetArray);
if (index !== -1) {
result.push([...path, index]);
} else {
for (let i = 0; i < obj.length; i++) {
result = result.concat(
findArrayCoordinates(obj[i], targetArray, [...path, i])
);
}
}
} else if (typeof obj === "object" && obj !== null) {
for (const key in obj) {
result = result.concat(
findArrayCoordinates(obj[key], targetArray, [...path, key])
);
}
}
return result;
}
function setValueAtCoordinatesUsingFindIndex(obj, targetArray, newArray) {
const coordinates = findArrayCoordinates(obj, targetArray);
let newValues;
coordinates.forEach((coords) => {
let current = obj;
for (let i = 0; i < coords.length - 1; i++) {
const index = coords[i];
current = current[index];
}
const lastIndex = coords[coords.length - 1];
current[lastIndex] = newArray;
newValues = current[lastIndex];
});
return newValues;
}
function setParentValues(parent, parentData, values) {
const treeGroup = parentData.config.treeGroup;
if (treeGroup) {
const ancestorEl = treeAncestors[treeGroup];
const ancestorData = parents.get(ancestorEl);
if (!ancestorData) return;
const ancestorValues = ancestorData.getValues(ancestorEl);
const initialParentValues = parentData.getValues(parent);
const updatedValues = setValueAtCoordinatesUsingFindIndex(
ancestorValues,
initialParentValues,
values
);
if (!updatedValues) {
console.warn("No updated value found");
return;
}
parentData.setValues(updatedValues, parent);
return;
}
parentData.setValues(values, parent);
}
function dragValues(state2) {
return [...state2.draggedNodes.map((x) => x.data.value)];
}
function updateConfig(parent, config) {
const parentData = parents.get(parent);
if (!parentData) return;
parents.set(parent, {
...parentData,
config: { ...parentData.config, ...config }
});
dragAndDrop({
parent,
getValues: parentData.getValues,
setValues: parentData.setValues,
config
});
}
function handleParentDrop(data, state2) {
data.e.stopPropagation();
dropped = true;
const config = data.targetData.parent.data.config;
config.handleEnd(state2);
}
function tearDown(parent) {
const parentData = parents.get(parent);
if (!parentData) return;
if (parentData.abortControllers.mainParent)
parentData.abortControllers.mainParent.abort();
}
function isDragState(state2) {
return "draggedNode" in state2 && !!state2.draggedNode;
}
function isSyn