Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@formkit/drag-and-drop

Package Overview
Dependencies
Maintainers
4
Versions
50
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@formkit/drag-and-drop - npm Package Compare versions

Comparing version 0.0.9 to 0.0.10

1572

./dist/index.js

@@ -0,98 +1,185 @@

// src/predicates.ts
function isNode(el, state2) {
return el instanceof HTMLElement && el.parentNode instanceof HTMLElement || state2.lastParent instanceof HTMLElement;
}
// src/state.ts
var state = {
activeNode: void 0,
enterCount: 0,
clonedDraggedNodes: Array(),
direction: void 0,
draggedNode: void 0,
draggedNodes: Array(),
dropZones: /* @__PURE__ */ new WeakMap(),
dropped: false,
hiddenNodes: /* @__PURE__ */ new WeakMap(),
initialParent: void 0,
initialParentValues: [],
lastCoordinates: {
x: 0,
y: 0
},
lastParent: void 0,
lastValue: void 0,
leftParent: false,
longTouch: false,
longTouchTimeout: void 0,
nodeData: /* @__PURE__ */ new WeakMap(),
parentData: /* @__PURE__ */ new WeakMap(),
parentObservers: /* @__PURE__ */ new WeakMap(),
parents: /* @__PURE__ */ new WeakSet(),
preventEnter: false,
removeDraggable: void 0,
scrollParent: void 0,
scrollParentOverflow: void 0,
touchMoving: false,
selectedNodes: Array(),
selectedValues: Array(),
touchEnded: false,
touchedNode: void 0,
touchStartLeft: void 0,
touchStartTop: void 0
};
// src/utils.ts
function splitClass(className) {
return className.split(" ").filter((x) => x);
}
var isBrowser = typeof window !== "undefined";
function handleClass(els, className, remove = false, omitAppendPrivateClass = false) {
if (!className)
function cleanUp(e) {
if (!state.draggedNode || !state.initialParent || !state.lastParent)
return;
const classNames = splitClass(className);
if (!classNames.length)
return;
remove ? removeClass(els, classNames) : addClass(els, classNames, omitAppendPrivateClass);
}
function addClass(els, classNames, omitAppendPrivateClass = false) {
if (classNames.includes("longTouch"))
return;
for (const node of els) {
if (!isNode(node) || !nodes.has(node)) {
node.classList.add(...classNames);
continue;
const config = state.parentData.get(state.initialParent)?.config;
const root = config?.root || document;
if (config) {
const hasSelections = state.selectedValues.length > 0;
const isTouch = !(e.event instanceof DragEvent);
let dropZoneClass;
if (isTouch) {
dropZoneClass = hasSelections ? config.touchSelectionDropZoneClass : config.touchDropZoneClass;
} else {
dropZoneClass = hasSelections ? config.selectionDropZoneClass : config.dropZoneClass;
}
const privateClasses = [];
const nodeData = nodes.get(node);
if (!nodeData)
continue;
for (const className of classNames) {
if (!node.classList.contains(className)) {
node.classList.add(className);
} else if (node.classList.contains(className) && omitAppendPrivateClass === false) {
privateClasses.push(className);
}
handleClass(state.draggedNodes, dropZoneClass, state, true);
if (dropZoneClass) {
const elsWithDropZoneClass = root.querySelectorAll(
`.${splitClass(dropZoneClass)}`
);
handleClass(Array.from(elsWithDropZoneClass), dropZoneClass, state, true);
}
nodeData.privateClasses = privateClasses;
nodes.set(node, nodeData);
}
}
function removeClass(els, classNames) {
for (const node of els) {
if (!isNode(node)) {
node.classList.remove(...classNames);
continue;
}
const nodeData = nodes.get(node);
if (!nodeData)
continue;
for (const className of classNames) {
if (!nodeData.privateClasses.includes(className)) {
node.classList.remove(className);
}
}
handleClass(
state.draggedNodes,
state.parentData.get(state.initialParent)?.config?.longTouchClass,
state,
true
);
state.touchedNode?.remove();
state.touchedNode = void 0;
state.lastParent = void 0;
state.draggedNode = void 0;
state.draggedNodes = [];
state.selectedNodes = [];
state.selectedValues = [];
state.touchStartTop = state.touchedNode = void 0;
state.touchStartLeft = 0;
state.lastValue = void 0;
state.touchMoving = false;
state.lastCoordinates = {
x: 0,
y: 0
};
state.direction = void 0;
if (state.scrollParent) {
state.scrollParent.style.overflow = state.scrollParentOverflow || "";
state.scrollParent = void 0;
state.scrollParentOverflow = void 0;
}
}
function getScrollParent(node) {
if (node == null)
return void 0;
if (node.scrollHeight > node.clientHeight) {
return node;
} else if (node.parentNode instanceof HTMLElement) {
return getScrollParent(node.parentNode);
}
return void 0;
function dragTransfer() {
if (!state.draggedNode || !state.lastParent || !state.initialParent)
return;
const draggedNodeData = state.nodeData.get(state.draggedNode);
const draggedParentData = state.parentData.get(state.initialParent);
const lastParentData = state.parentData.get(state.lastParent);
if (!draggedNodeData || !draggedParentData || !lastParentData)
return;
else
return {
draggedNode: state.draggedNode,
draggedNodeData,
draggedParent: state.initialParent,
draggedParentData,
lastParent: state.lastParent,
lastParentData
};
}
function getElFromPoint(eventData) {
if (!(eventData.e instanceof TouchEvent))
function touchTransfer() {
if (!state.touchedNode)
return;
const newX = eventData.e.touches[0].clientX;
const newY = eventData.e.touches[0].clientY;
const els = document.elementsFromPoint(newX, newY);
const data = dragTransfer();
if (data === void 0)
return;
const transferTouchData = {
...data,
touchedNode: state.touchedNode
};
return transferTouchData;
}
function nodeDragTarget(e) {
if (!isNode(e.currentTarget, state))
return;
const targetNodeData = state.nodeData.get(e.currentTarget);
const targetParentData = state.parentData.get(
e.currentTarget.parentNode || state.lastParent
);
if (!targetNodeData || !targetParentData)
return;
const targetData = {
targetNode: e.currentTarget,
targetNodeData,
targetParent: e.currentTarget.parentNode || state.lastParent,
targetParentData
};
return targetData;
}
function dzDragTarget(e) {
if (!isNode(e.currentTarget, state))
return;
const targetParentData = state.parentData.get(e.currentTarget);
if (!targetParentData)
return;
const targetData = {
targetParent: e.currentTarget,
targetParentData
};
return targetData;
}
function getTouchTarget(e) {
const newX = e.touches[0].clientX;
const newY = e.touches[0].clientY;
const nodes = document.elementsFromPoint(newX, newY);
if (!nodes)
return;
for (const node of els) {
if (isNode(node) && nodes.has(node)) {
for (const node of nodes) {
if (isNode(node, state) && state.nodeData.has(node)) {
const targetNode = node;
const targetNodeData = nodes.get(targetNode);
const targetParentData = parents.get(targetNode.parentNode);
const targetNodeData = state.nodeData.get(targetNode);
const targetParentData = state.parentData.get(targetNode.parentNode);
if (!targetNodeData || !targetParentData)
return;
return {
node: {
el: targetNode,
data: targetNodeData
},
parent: {
el: targetNode.parentNode,
data: targetParentData
}
targetNode,
targetNodeData,
targetParent: targetNode.parentNode,
targetParentData
};
} else if (node instanceof HTMLElement) {
const parentData = parents.get(node);
if (parentData) {
return {
parent: {
el: node,
data: parentData
}
};
}
} else if (node instanceof HTMLElement && state.dropZones.has(node)) {
if (!state.parentData.has(node))
return;
const targetParentData = state.parentData.get(node);
if (!targetParentData)
return;
return {
targetParent: node,
targetParentData
};
}

@@ -102,16 +189,61 @@ }

}
function isNode(el) {
return el instanceof HTMLElement && el.parentNode instanceof HTMLElement;
function handleSelections(e, selectedValues, state2, x, y) {
state2.selectedValues = selectedValues;
for (const child of e.draggedParentData.enabledNodes) {
if (child === e.draggedNode) {
state2.selectedNodes.push(child);
continue;
}
const childValue = state2.nodeData.get(child)?.value;
if (!childValue)
continue;
if (selectedValues?.includes(childValue)) {
state2.draggedNodes.push(child);
}
}
const config = e.draggedParentData.config;
const clonedEls = state2.draggedNodes.map((x2) => {
const el = x2.cloneNode(true);
copyNodeStyle(x2, el, true);
if (e.event instanceof DragEvent)
handleClass([el], config?.selectionDraggingClass, state2);
return el;
});
setTimeout(() => {
if (e.event instanceof DragEvent && config?.selectionDropZoneClass)
for (const node of state2.draggedNodes) {
node.classList.add(config?.selectionDropZoneClass);
}
});
state2.clonedDraggedNodes = clonedEls;
return { e, clonedEls, x, y };
}
function addEvents(el, events) {
const keysToControllers = {};
for (const key in events) {
const abortController = new AbortController();
const event = events[key];
el.addEventListener(key, event, {
signal: abortController.signal
function stackNodes({
e,
clonedEls,
x,
y
}) {
const wrapper = document.createElement("div");
if (!(wrapper instanceof HTMLElement))
return;
for (const el of clonedEls)
wrapper.append(el);
const rect = e.draggedNode.getBoundingClientRect();
wrapper.style.cssText = `
display: flex;
flex-direction: column;
width: ${rect.width}px;
position: absolute;
left: -9999px
`;
document.body.append(wrapper);
if (e.event instanceof DragEvent) {
e.event.dataTransfer?.setDragImage(wrapper, x, y);
setTimeout(() => {
wrapper.remove();
});
keysToControllers[key] = abortController;
} else {
state.touchedNode = wrapper;
}
return keysToControllers;
}

@@ -140,3 +272,3 @@ function copyNodeStyle(sourceNode, targetNode, omitKeys = false) {

for (const child of Array.from(sourceNode.children)) {
if (!isNode(child))
if (!isNode(child, state))
continue;

@@ -147,155 +279,481 @@ const targetChild = targetNode.children[Array.from(sourceNode.children).indexOf(child)];

}
function eventCoordinates(data) {
return data instanceof DragEvent ? { x: data.clientX, y: data.clientY } : { x: data.touches[0].clientX, y: data.touches[0].clientY };
function handleClass(nodes, className, state2, remove = false, omitAppendPrivateClass = false) {
if (!className)
return;
const classNames = splitClass(className);
if (!classNames.length)
return;
remove ? removeClass(nodes, classNames, state2) : addClass(nodes, classNames, state2, omitAppendPrivateClass);
}
function addClass(nodes, classNames, state2, omitAppendPrivateClass = false) {
if (classNames.includes("longTouch"))
return;
for (const node of nodes) {
if (!isNode(node, state2) || !state2.nodeData.has(node)) {
node.classList.add(...classNames);
continue;
}
const privateClasses = [];
const nodeData = state2.nodeData.get(node);
if (!nodeData)
continue;
for (const className of classNames) {
if (!node.classList.contains(className)) {
node.classList.add(className);
} else if (node.classList.contains(className) && omitAppendPrivateClass === false) {
privateClasses.push(className);
}
}
nodeData.privateClasses = privateClasses;
state2.nodeData.set(node, nodeData);
}
}
function removeClass(nodes, classNames, state2) {
for (const node of nodes) {
if (!isNode(node, state2)) {
node.classList.remove(...classNames);
continue;
}
const nodeData = state2.nodeData.get(node);
if (!nodeData)
continue;
for (const className of classNames) {
if (!nodeData.privateClasses.includes(className)) {
node.classList.remove(className);
}
}
}
}
function splitClass(className) {
return className.split(" ").filter((x) => x);
}
function getScrollParent(node) {
if (node == null) {
return void 0;
}
if (node.scrollHeight > node.clientHeight) {
return node;
} else {
if (node.parentNode instanceof HTMLElement)
return getScrollParent(node.parentNode);
}
return void 0;
}
// src/index.ts
var nodes = /* @__PURE__ */ new WeakMap();
var parents = /* @__PURE__ */ new WeakMap();
var parentObservers = /* @__PURE__ */ new WeakMap();
var state = void 0;
function resetState() {
state = void 0;
// src/validators.ts
function validateSort(e) {
const dzConfig = e.targetParentData.dzConfig;
if (dzConfig && dzConfig.nodeDropZone === false)
return false;
return e.targetParentData.config?.sortable !== false;
}
function setDragState(dragStateProps2) {
state = {
direction: void 0,
enterCount: 0,
lastCoordinates: { x: 0, y: 0 },
lastValue: void 0,
clonedDraggedEls: [],
...dragStateProps2
function validateTransfer(e) {
const draggedValues = state.draggedNodes.map(
(el) => state.nodeData.get(el)?.value
);
const targetParentValues = e.targetParentData.getValues(e.targetParent);
const draggedParentValues = e.draggedParentData.getValues(e.draggedParent);
const ctData = {
name: e.targetParentData.config?.name,
group: e.targetParentData.dzConfig?.group,
values: targetParentValues
};
return state;
}
function setTouchState(dragState, touchStateProps) {
state = {
...dragState,
...touchStateProps
const deData = {
name: e.draggedParentData.config?.name,
group: e.draggedParentData.dzConfig?.group,
values: draggedParentValues
};
return state;
const accepts = e.targetParentData.dzConfig?.accepts;
if (accepts)
return accepts(ctData, deData, draggedValues);
else if (ctData.group) {
return ctData.group === deData.group;
}
return true;
}
function dragStateProps(targetData) {
return {
draggedNode: {
el: targetData.node.el,
data: targetData.node.data
},
draggedNodes: [
{
el: targetData.node.el,
data: targetData.node.data
// src/actions.ts
function dragstart(e, state2) {
if (e.event.dataTransfer) {
e.event.dataTransfer.dropEffect = "move";
e.event.dataTransfer.effectAllowed = "move";
}
const config = e.targetParentData.config;
const selectedValues = config?.setSelections && config?.setSelections(e.draggedNode.parentNode);
if (Array.isArray(selectedValues) && selectedValues.length) {
const targetRect = e.targetNode.getBoundingClientRect();
const x = e.event.clientX - targetRect.left;
const y = e.event.clientY - targetRect.top;
stackNodes(handleSelections(e, selectedValues, state2, x, y));
} else if (config) {
handleClass(state2.draggedNodes, config.draggingClass, state2);
setTimeout(() => {
handleClass(state2.draggedNodes, config.draggingClass, state2, true);
handleClass(state2.draggedNodes, config.dropZoneClass, state2);
});
}
}
function touchstart(e, state2) {
e.event.stopPropagation();
const rect = e.draggedNode.getBoundingClientRect();
state2.touchStartLeft = e.event.touches[0].clientX - rect.left;
state2.touchStartTop = e.event.touches[0].clientY - rect.top;
const config = e.draggedParentData.config;
const selectedValues = config?.setSelections && config?.setSelections(e.draggedNode.parentNode);
if (Array.isArray(selectedValues) && selectedValues.length) {
stackNodes(
handleSelections(
e,
selectedValues,
state2,
state2.touchStartLeft,
state2.touchStartTop
)
);
} else {
state2.touchedNode = e.touchedNode;
e.touchedNode.style.cssText = `
width: ${rect.width}px;
position: absolute;
pointer-events: none;
top: -9999px;
z-index: 999999;
display: none;
`;
document.body.append(e.touchedNode);
copyNodeStyle(e.draggedNode, e.touchedNode);
e.touchedNode.style.display = "none";
}
if (config?.longTouch) {
state2.longTouchTimeout = setTimeout(() => {
state2.longTouch = true;
const parentScroll = getScrollParent(e.draggedNode);
if (parentScroll) {
state2.scrollParent = parentScroll;
state2.scrollParentOverflow = parentScroll.style.overflow;
parentScroll.style.overflow = "hidden";
}
],
initialParent: {
el: targetData.parent.el,
data: targetData.parent.data
},
lastParent: {
el: targetData.parent.el,
data: targetData.parent.data
if (config.longTouchClass)
handleClass(state2.draggedNodes, config.longTouchClass, state2);
if (e.event.cancelable)
e.event.preventDefault();
document.addEventListener("contextmenu", function(e2) {
e2.preventDefault();
});
}, 200);
}
}
function touchmove(e, state2) {
if (!state2.touchedNode)
return;
const config = e.draggedParentData.config;
if (config?.longTouch && !state2.longTouch) {
clearTimeout(state2.longTouchTimeout);
return;
}
if (state2.touchMoving !== true) {
state2.touchMoving = true;
handleClass(state2.draggedNodes, config?.longTouchClass, state2, true);
const hasSelections = Array.isArray(state2.selectedValues) && state2.selectedValues.length;
if (hasSelections) {
handleClass(
state2.clonedDraggedNodes,
config?.touchSelectionDraggingClass,
state2
);
handleClass(
state2.draggedNodes,
config?.touchSelectionDropZoneClass,
state2
);
} else {
handleClass([state2.touchedNode], config?.touchDraggingClass, state2);
handleClass(state2.draggedNodes, config?.touchDropZoneClass, state2);
}
};
}
e.touchedNode.style.display = "block";
const x = e.event.touches[0].clientX + window.scrollX;
const y = e.event.touches[0].clientY + window.scrollY;
const windowHeight = window.innerHeight + window.scrollY;
if (y > windowHeight - 50) {
window.scrollBy(0, 10);
} else if (y < window.scrollY + 50) {
window.scrollBy(0, -10);
}
const touchStartLeft = state2.touchStartLeft ?? 0;
const touchStartTop = state2.touchStartTop ?? 0;
e.touchedNode.style.left = `${x - touchStartLeft}px`;
e.touchedNode.style.top = `${y - touchStartTop}px`;
if (e.event.cancelable)
e.event.preventDefault();
}
function performSort(state2, data) {
const draggedValues = dragValues(state2);
const targetParentValues = parentValues(
data.targetData.parent.el,
data.targetData.parent.data
function sort(e, state2) {
const targetParentValues = e.targetParentData.getValues(e.targetParent);
const draggedNodeValues = state2.draggedNodes.map(
(x) => state2.nodeData.get(x)?.value
);
const newParentValues = [
...targetParentValues.filter((x) => !draggedValues.includes(x))
...targetParentValues.filter((x) => !draggedNodeValues.includes(x))
];
newParentValues.splice(data.targetData.node.data.index, 0, ...draggedValues);
setParentValues(data.targetData.parent.el, data.targetData.parent.data, [
...newParentValues
]);
newParentValues.splice(e.targetNodeData.index, 0, ...draggedNodeValues);
e.targetParentData.setValues(e.targetParent, newParentValues);
}
function parentValues(parent, parentData) {
return parentData.getValues(parent);
function transferReturn(e, state2) {
if (e.draggedParent === e.lastParent)
return;
const leftParentValues = [...e.lastParentData.getValues(e.lastParent)];
const draggedValues = state2.draggedNodes.map(
(x) => state2.nodeData.get(x)?.value
);
const newLeftParentValues = [
...leftParentValues.filter((x) => !draggedValues.includes(x))
];
e.lastParentData.setValues(e.lastParent, newLeftParentValues);
state2.lastParent = e.draggedParent;
e.draggedParentData.setValues(e.draggedParent, state2.initialParentValues);
}
function setParentValues(parent, parentData, values) {
parentData.setValues(parent, values);
function dragleave(_e, _state) {
}
function dragValues(state2) {
return state2.draggedNodes.map((x) => x.data.value);
function end(e, state2) {
if (e.event instanceof DragEvent && state2.longTouch)
return;
if (!(e.event instanceof DragEvent)) {
state2.longTouch = false;
clearTimeout(state2.longTouchTimeout);
}
if (state2.draggedNode && state2.draggedNode.parentNode && state2.draggedNode.parentNode !== state2.lastParent && state2.initialParent) {
const deData = state2.nodeData.get(state2.draggedNode);
if (deData) {
const deParentData = state2.parentData.get(state2.draggedNode.parentNode);
const deParentValues = [
...deParentData?.getValues(state2.draggedNode.parentNode) || []
];
const deIndex = deData.index;
deParentValues.splice(deIndex, state2.draggedNodes.length);
deParentData?.setValues(state2.initialParent, deParentValues);
}
}
cleanUp(e);
}
function initParent({
parent,
getValues,
setValues,
config = {}
}) {
if (!isBrowser)
function drop(e, _state) {
cleanUp(e);
}
// src/plugins/dropZone.ts
function setUpDropZone(dzConfig = {}, parent, config) {
const parentData = state.parentData.get(parent);
if (!parentData)
return;
document.addEventListener("dragover", (e) => {
e.preventDefault();
});
tearDownParent(parent);
const parentData = {
getValues,
setValues,
config: {
handleDragstart,
handleDragoverNode,
handleDragoverParent,
handleDragend,
handleTouchstart,
handleTouchmove,
handleTouchOverNode,
handleTouchOverParent,
performSort,
performTransfer,
root: document,
setupNode,
reapplyDragClasses,
tearDownNode,
...config
},
enabledNodes: [],
abortControllers: {}
!config.disabled ? activateDZ(parent, dzConfig, parent) : deactivateDZ(parent);
for (const dz of dzConfig.validDropZones || []) {
activateDZ(dz, dzConfig, parent);
}
const parentConfig = parentData.config;
if (!parentConfig || !parentConfig.setDraggable)
return;
const setDraggable2 = parentConfig.setDraggable;
parentConfig.setDraggable = (node) => {
setDraggable2(node);
setNodeEvents(node);
return node;
};
setupParent(parent, parentData);
config.plugins?.forEach((plugin) => {
plugin(parent)?.tearDownParent();
});
remapNodes(parent);
config.plugins?.forEach((plugin) => {
plugin(parent)?.setupParent();
});
}
function tearDownParent(parent) {
const parentData = parents.get(parent);
function activateDZ(dz, dzConfig, parent) {
dz.addEventListener("dragover", dzDragEvent);
dz.addEventListener("drop", dzDragEvent);
const parentData = state.parentData.get(dz);
if (!parentData)
return;
for (const event of Object.keys(parentData.abortControllers["mainParent"])) {
parentData.abortControllers["mainParent"][event].abort();
if (parent) {
parentData.dzConfig = dzConfig;
state.dropZones.set(dz, parent);
state.parentData.set(dz, parentData);
} else {
parentData.dzConfig = dzConfig;
state.parentData.set(dz, parentData);
}
for (const node of Array.from(parent.children)) {
if (isNode(node)) {
nodes.delete(node);
}
function deactivateDZ(dz) {
dz.removeEventListener("dragover", dzDragEvent);
dz.removeEventListener("drop", dzDragEvent);
const parentData = state.parentData.get(dz);
if (!parentData)
return;
}
function setNodeEvents(node) {
node.addEventListener("dragover", nodeDragEvent);
node.addEventListener("touchmove", nodeTouchEvent);
}
function dzDragEvent(e) {
e.stopPropagation();
e.preventDefault();
const targetData = dzDragTarget(e);
if (targetData === void 0)
return;
const transferData = dragTransfer();
if (transferData === void 0)
return;
const event = {
event: e,
...targetData,
...transferData
};
const dzConfig = event.targetParentData.dzConfig;
switch (event.event.type) {
case "dragover":
if (state.lastParent !== event.targetParent && event.draggedParent !== event.targetParent) {
handleTransfer(event, state);
state.enterCount++;
state.leftParent = true;
}
break;
case "drop":
if (event.targetParent !== event.draggedParent) {
dzConfig && dzConfig.drop ? dzConfig.drop(event, state, transferDrop) : transferDrop(event, state);
}
}
}
function nodeDragEvent(e) {
e.stopPropagation();
e.preventDefault();
const targetData = nodeDragTarget(e);
if (!targetData)
return;
const transferData = dragTransfer();
if (transferData === void 0)
return;
const event = {
event: e,
...targetData,
...transferData
};
if (!event)
return;
if (e.type === "dragover" && state.lastParent !== event.targetParent && event.targetParent !== event.draggedParent) {
const dzConfig = event.targetParentData?.dzConfig;
if (dzConfig && dzConfig.nodeDropZone === false)
return;
if (event.targetParent !== event.draggedParent) {
handleTransfer(event, state);
state.leftParent = true;
}
}
parents.delete(parent);
const parentObserver = parentObservers.get(parent.parentNode);
if (parentObserver)
parentObserver.disconnect();
parentObservers.delete(parent);
}
function setupParent(parent, parentData) {
const nodesObserver = new MutationObserver(nodesMutated);
nodesObserver.observe(parent, { childList: true });
if (parent.parentNode) {
const parentObserver = new MutationObserver(parentMutated);
parentObserver.observe(parent.parentNode, { childList: true });
if (!(parent.parentNode instanceof HTMLElement))
function nodeTouchEvent(e) {
if (e.type !== "touchmove")
return;
const targetData = getTouchTarget(e);
if (targetData === void 0)
return;
const transferData = touchTransfer();
if (transferData === void 0)
return;
if ("targetNode" in targetData) {
const nodeEvent = {
event: e,
...targetData,
...transferData
};
if (nodeEvent.targetParent === nodeEvent.draggedParent) {
transferReturn(nodeEvent, state);
return;
parentObservers.set(parent.parentNode, parentObserver);
}
if (nodeEvent.lastParent == nodeEvent.targetParent)
return;
handleTransfer(nodeEvent, state);
state.leftParent = true;
} else {
const parentEvent = {
event: e,
...targetData,
...transferData
};
if (parentEvent.lastParent !== parentEvent.draggedParent && parentEvent.draggedParent === parentEvent.targetParent) {
transferReturn(parentEvent, state);
state.leftParent = false;
} else if (parentEvent.targetParent !== parentEvent.draggedParent && parentEvent.lastParent !== parentEvent.targetParent) {
handleTransfer(parentEvent, state);
state.leftParent = true;
}
}
parents.set(parent, { ...parentData });
const abortControllers = addEvents(parent, {
dragover: parentEventData(parentData.config.handleDragoverParent),
touchOverParent: parentData.config.handleTouchOverParent
}
function handleTransfer(e, state2) {
if (!validateTransfer(e))
return;
const dzConfig = e.targetParentData.dzConfig;
dzConfig?.transfer ? dzConfig.transfer(e, state2, transferAction) : transferAction(e, state2);
state2.lastParent = e.targetParent;
}
function transferAction(e, state2) {
let lastParentValues = [...e.lastParentData.getValues(e.lastParent)];
const draggedNodeValues = state2.draggedNodes.map(
(x) => state2.nodeData.get(x)?.value
);
lastParentValues = [
...lastParentValues.filter((x) => !draggedNodeValues.includes(x))
];
const targetParentValues = [...e.targetParentData.getValues(e.targetParent)];
if ("targetNodeData" in e) {
targetParentValues.splice(
e.targetNodeData.index,
0,
...state2.draggedNodes.map((x) => state2.nodeData.get(x)?.value)
);
} else {
targetParentValues.push(...draggedNodeValues);
}
e.lastParentData.setValues(e.lastParent, lastParentValues);
e.targetParentData.setValues(e.targetParent, targetParentValues);
}
function transferDrop(_event, state2) {
if (!state2.draggedNode)
return;
}
// src/index.ts
function dragover(e) {
e.preventDefault();
}
function initParent(parent, getValues, setValues, config) {
if (!isBrowser)
return;
document.addEventListener("dragover", dragover);
if (!state.parents.has(parent)) {
state.parents.add(parent);
const nodesObserver = new MutationObserver(nodesMutated);
nodesObserver.observe(parent, { childList: true });
if (parent.parentNode) {
parent.addEventListener("dragenter", dzDragEvent2);
parent.addEventListener("dragover", dzDragEvent2);
parent.addEventListener("dragleave", dzDragEvent2);
parent.addEventListener("drop", dzDragEvent2);
const parentObserver = new MutationObserver(parentMutated);
parentObserver.observe(parent.parentNode, { childList: true });
if (!(parent.parentNode instanceof HTMLElement))
return;
state.parentObservers.set(parent.parentNode, parentObserver);
}
}
if (!config.setDraggable) {
config.setDraggable = setDraggable;
}
if (!config.removeDraggable) {
config.removeDraggable = removeDraggable;
}
const currentPlugins = state.parentData.get(parent)?.config?.plugins;
currentPlugins?.forEach((plugin) => {
if (plugin.tearDown) {
plugin.tearDown(parent, config);
}
});
parentData.abortControllers["mainParent"] = abortControllers;
state.parentData.set(parent, {
getValues,
setValues,
config,
enabledNodes: []
});
config.plugins?.forEach((plugin) => {
plugin.setUp(parent, config);
});
remap(parent);
}

@@ -307,11 +765,23 @@ function parentMutated(mutationList) {

continue;
const parentData = parents.get(removedNode);
const parentData = state.parentData.has(removedNode);
if (!parentData)
continue;
for (const node of Array.from(removedNode.childNodes)) {
if (isNode(node) && parentData.enabledNodes.includes(node)) {
nodes.delete(node);
state.parentData.delete(removedNode);
let hasParent = false;
for (const node of Array.from(mutation.target.childNodes)) {
if (state.parents.has(node)) {
state.parents.delete(removedNode);
hasParent = true;
break;
}
}
parents.delete(removedNode);
if (!hasParent) {
const parentObserver = state.parentObservers.get(
mutation.target
);
if (parentObserver) {
parentObserver.disconnect();
state.parentObservers.delete(mutation.target);
}
}
}

@@ -324,6 +794,6 @@ }

return;
remapNodes(parentEl);
remap(parentEl);
}
function remapNodes(parent) {
const parentData = parents.get(parent);
function remap(parent) {
const parentData = state.parentData.get(parent);
if (!parentData)

@@ -333,12 +803,18 @@ return;

const config = parentData.config;
let disabled = false;
for (let x = 0; x < parent.children.length; x++) {
const node = parent.children[x];
if (!isNode(node))
const child = parent.children[x];
if (!isNode(child, state))
continue;
const nodeData = nodes.get(node);
config.tearDownNode({ node, parent, nodeData, parentData });
if (!config.draggable || config.draggable && config.draggable(node))
enabledNodes.push(node);
if (config && config.draggable && !config.draggable(child)) {
removeDraggable(child);
} else if (config?.disabled) {
disabled = true;
removeDraggable(child);
} else if (parentData.config?.setDraggable) {
parentData.config.setDraggable(child);
enabledNodes.push(child);
}
}
if (enabledNodes.length !== parentData.getValues(parent).length && !config.disabled) {
if (enabledNodes.length !== parentData.getValues(parent).length && !disabled) {
console.warn(

@@ -351,474 +827,228 @@ "The number of enabled nodes does not match the number of values."

for (let x = 0; x < enabledNodes.length; x++) {
const node = enabledNodes[x];
const nodeData = {
const child = enabledNodes[x];
state.nodeData.set(child, {
value: values[x],
index: x,
privateClasses: [],
abortControllers: {}
};
config.setupNode({ node, parent, parentData, nodeData });
privateClasses: []
});
if (!state.draggedNode || !config)
continue;
let dropZoneClass;
const hasSelections = state.selectedValues.includes(values[x]);
if (!hasSelections && state.nodeData.get(state.draggedNode)?.value !== values[x])
continue;
const isTouch = !!state.touchedNode;
if (isTouch) {
dropZoneClass = hasSelections ? config?.touchSelectionDropZoneClass : config?.touchDropZoneClass;
} else {
dropZoneClass = hasSelections ? config?.selectionDropZoneClass : config?.dropZoneClass;
}
handleClass([child], dropZoneClass, state, false, true);
}
parents.set(parent, { ...parentData, enabledNodes });
state.parentData.set(parent, { ...parentData, enabledNodes });
}
function handleDragstart(data) {
if (!(data.e instanceof DragEvent))
return;
dragstart({
e: data.e,
targetData: data.targetData
});
}
function dragstartClasses(el, draggingClass, dropZoneClass) {
handleClass([el], draggingClass);
setTimeout(() => {
handleClass([el], draggingClass, true);
handleClass([el], dropZoneClass);
});
}
function initDrag(eventData) {
const dragState = setDragState(dragStateProps(eventData.targetData));
if (eventData.e.dataTransfer) {
eventData.e.dataTransfer.dropEffect = "move";
eventData.e.dataTransfer.effectAllowed = "move";
function removeDraggable(child) {
if (child.getAttribute("draggable") === "true") {
child.removeAttribute("draggable");
}
return dragState;
child.removeEventListener("dragstart", nodeDragEvent2);
child.removeEventListener("dragenter", nodeDragEvent2);
child.removeEventListener("dragend", nodeDragEvent2);
child.removeEventListener("dragover", nodeDragEvent2);
child.removeEventListener("touchstart", nodeTouchEvent2);
child.removeEventListener("touchmove", nodeTouchEvent2);
child.removeEventListener("touchend", nodeTouchEvent2);
}
function validateDragHandle(data) {
if (!(data.e instanceof DragEvent) && !(data.e instanceof TouchEvent))
return false;
const config = data.targetData.parent.data.config;
if (!config.dragHandle)
return true;
const dragHandles = data.targetData.node.el.querySelectorAll(
config.dragHandle
);
if (!dragHandles)
return false;
const coordinates = eventCoordinates(data.e);
const elFromPoint = config.root.elementFromPoint(
coordinates.x,
coordinates.y
);
if (!elFromPoint)
return false;
for (const handle of Array.from(dragHandles)) {
if (elFromPoint === handle || handle.contains(elFromPoint))
return true;
}
return false;
function setDraggable(child) {
child.setAttribute("draggable", "true");
child.addEventListener("dragstart", nodeDragEvent2);
child.addEventListener("dragenter", nodeDragEvent2);
child.addEventListener("dragend", nodeDragEvent2);
child.addEventListener("dragover", nodeDragEvent2);
child.addEventListener("touchstart", nodeTouchEvent2, { passive: false });
child.addEventListener("touchmove", nodeTouchEvent2, { passive: false });
child.addEventListener("touchend", nodeTouchEvent2);
}
function touchstart(data) {
if (!validateDragHandle(data)) {
data.e.preventDefault();
function nodeDragEvent2(e) {
e.stopPropagation();
const targetData = nodeDragTarget(e);
if (!targetData)
return;
if (e.type === "dragstart") {
state.lastParent = targetData.targetParent;
state.draggedNode = targetData.targetNode;
state.initialParent = targetData.targetParent;
if (!state.draggedNode)
return;
state.draggedNodes = [state.draggedNode];
state.initialParentValues = targetData.targetParentData.getValues(
targetData.targetParent
);
}
const touchState = initTouch(data);
handleTouchedNode(data, touchState);
handleLongTouch(data, touchState);
}
function dragstart(data) {
if (!validateDragHandle(data)) {
data.e.preventDefault();
const transferData = dragTransfer();
if (transferData === void 0)
return;
const eventData = {
event: e,
...targetData,
...transferData
};
const draggedConfig = eventData.draggedParentData.config;
switch (eventData.event.type) {
case "dragstart":
draggedConfig?.dragstart ? draggedConfig.dragstart(eventData, state, dragstart) : dragstart(eventData, state);
break;
case "dragover":
eventData.event.preventDefault();
if (eventData.targetNode === eventData.draggedNode) {
state.lastValue = eventData.targetNodeData.value;
return;
}
if (eventData.targetParent === eventData.lastParent) {
handleSort(eventData, state);
} else {
const config = eventData.targetParentData.config;
config?.transferReturn ? config.transferReturn(eventData, state, transferReturn) : transferReturn(eventData, state);
}
break;
case "dragend":
draggedConfig?.end ? draggedConfig.end(eventData, state, end) : end(eventData, state);
break;
default:
break;
}
const config = data.targetData.parent.data.config;
const dragState = initDrag(data);
dragstartClasses(
dragState.draggedNode.el,
config.draggingClass,
config.dropZoneClass
);
}
function handleTouchOverNode(e) {
if (!state)
function nodeTouchEvent2(e) {
e.stopPropagation();
if (state.touchMoving && e.type === "touchstart")
return;
if (e.detail.targetData.parent.el === state.lastParent.el)
sort(e.detail, state);
}
function setupNode(data) {
nodes.set(data.node, data.nodeData);
const config = data.parentData.config;
data.node.setAttribute("draggable", "true");
const abortControllers = addEvents(data.node, {
dragstart: nodeEventData(config.handleDragstart),
dragover: nodeEventData(config.handleDragoverNode),
dragend: nodeEventData(config.handleDragend),
touchstart: nodeEventData(config.handleTouchstart),
touchmove: nodeEventData(config.handleTouchmove),
touchend: nodeEventData(config.handleDragend),
touchOverNode: config.handleTouchOverNode
});
data.nodeData.abortControllers["mainNode"] = abortControllers;
data.parentData.config.plugins?.forEach((plugin) => {
plugin(data.parent)?.setupNode?.(data);
});
config.reapplyDragClasses(data.node, data.parentData);
}
function reapplyDragClasses(node, parentData) {
if (!state)
if (!isNode(e.currentTarget, state))
return;
const dropZoneClass = "touchedNode" in state ? parentData.config.touchDropZoneClass : parentData.config.dropZoneClass;
const nodeValue = nodes.get(node)?.value;
if (nodeValue !== state.draggedNode.data.value)
return;
handleClass([node], dropZoneClass);
}
function tearDownNode(data) {
data.node.setAttribute("draggable", "false");
if (data.nodeData?.abortControllers?.mainNode) {
for (const event of Object.keys(data.nodeData.abortControllers.mainNode)) {
data.nodeData.abortControllers.mainNode[event].abort();
}
}
data.parentData.config.plugins?.forEach((plugin) => {
plugin(data.parent)?.tearDownNode?.(data);
});
nodes.delete(data.node);
}
function handleDragend(eventData) {
if (!state)
return;
end(eventData, state);
resetState();
}
function end(_eventData, state2) {
if ("longTouchTimeout" in state2 && state2.longTouchTimeout)
clearTimeout(state2.longTouchTimeout);
const config = parents.get(state2.initialParent.el)?.config;
const root = config?.root || document;
const isTouch = !("touchedNode" in state2);
const dropZoneClass = isTouch ? config?.touchDropZoneClass : config?.dropZoneClass;
handleClass(
state2.draggedNodes.map((x) => x.el),
dropZoneClass,
true
);
if (dropZoneClass) {
const elsWithDropZoneClass = root.querySelectorAll(
`.${splitClass(dropZoneClass)}`
);
handleClass(Array.from(elsWithDropZoneClass), dropZoneClass, true);
}
if (config?.longTouchClass) {
handleClass(
state2.draggedNodes.map((x) => x.el),
state2.initialParent.data?.config?.longTouchClass,
true
);
}
if ("touchedNode" in state2) {
state2.touchedNode?.remove();
if (state2.scrollParent) {
state2.scrollParent.style.overflow = state2.scrollParentOverflow || "";
}
}
}
function handleTouchstart(eventData) {
if (!(eventData.e instanceof TouchEvent))
return;
touchstart({
e: eventData.e,
targetData: eventData.targetData
});
}
function initTouch(data) {
data.e.stopPropagation();
const clonedNode = data.targetData.node.el.cloneNode(true);
const rect = data.targetData.node.el.getBoundingClientRect();
const touchState = setTouchState(
setDragState(dragStateProps(data.targetData)),
{
touchStartLeft: data.e.touches[0].clientX - rect.left,
touchStartTop: data.e.touches[0].clientY - rect.top,
touchedNode: clonedNode
}
);
return touchState;
}
function handleTouchedNode(data, touchState) {
const rect = data.targetData.node.el.getBoundingClientRect();
touchState.touchedNode.style.cssText = `
width: ${rect.width}px;
position: absolute;
pointer-events: none;
top: -9999px;
z-index: 999999;
display: none;
`;
document.body.append(touchState.touchedNode);
copyNodeStyle(data.targetData.node.el, touchState.touchedNode);
touchState.touchedNode.style.display = "none";
}
function handleLongTouch(data, touchState) {
const config = data.targetData.parent.data.config;
if (!config.longTouch)
return;
touchState.longTouchTimeout = setTimeout(() => {
if (!touchState)
if (e.type === "touchstart") {
state.draggedNode = e.currentTarget;
const clone = e.currentTarget.cloneNode(true);
if (!(clone instanceof HTMLElement))
return;
touchState.longTouch = true;
const parentScroll = getScrollParent(touchState.draggedNode.el);
if (parentScroll) {
touchState.scrollParent = parentScroll;
touchState.scrollParentOverflow = parentScroll.style.overflow;
parentScroll.style.overflow = "hidden";
}
if (config.longTouchClass && data.e.cancelable)
handleClass(
touchState.draggedNodes.map((x) => x.el),
config.longTouchClass
);
data.e.preventDefault();
document.addEventListener("contextmenu", function(e) {
e.preventDefault();
});
}, config.longTouchTimeout || 200);
}
function handleTouchmove(eventData) {
if (!state || !("touchedNode" in state))
return;
touchmove(eventData, state);
}
function touchmoveClasses(touchState, config) {
if (config.longTouchClass)
handleClass(
touchState.draggedNodes.map((x) => x.el),
config?.longTouchClass,
true
);
if (config.touchDraggingClass)
handleClass([touchState.touchedNode], config.touchDraggingClass);
if (config.touchDropZoneClass)
handleClass(
touchState.draggedNodes.map((x) => x.el),
config.touchDropZoneClass
);
}
function moveTouchedNode(data, touchState) {
touchState.touchedNode.style.display = "block";
const x = data.e.touches[0].clientX + window.scrollX;
const y = data.e.touches[0].clientY + window.scrollY;
const windowHeight = window.innerHeight + window.scrollY;
if (y > windowHeight - 50) {
window.scrollBy(0, 10);
} else if (y < window.scrollY + 50) {
window.scrollBy(0, -10);
const parentData = state.parentData.get(e.currentTarget.parentNode);
if (!parentData)
return;
const parentValues = parentData.getValues(e.currentTarget.parentNode);
if (!parentValues)
return;
state.touchedNode = clone;
state.draggedNodes = [state.draggedNode];
state.lastParent = e.currentTarget.parentNode;
state.initialParent = e.currentTarget.parentNode;
state.initialParentValues = parentValues;
}
const touchStartLeft = touchState.touchStartLeft ?? 0;
const touchStartTop = touchState.touchStartTop ?? 0;
touchState.touchedNode.style.left = `${x - touchStartLeft}px`;
touchState.touchedNode.style.top = `${y - touchStartTop}px`;
}
function touchmove(data, touchState) {
if (data.e.cancelable)
data.e.preventDefault();
const config = data.targetData.parent.data.config;
if (config.longTouch && !touchState.longTouch) {
clearTimeout(touchState.longTouchTimeout);
if (!state.touchedNode)
return;
}
if (touchState.touchMoving !== true) {
touchState.touchMoving = true;
touchmoveClasses(touchState, config);
}
moveTouchedNode(data, touchState);
const elFromPoint = getElFromPoint(data);
if (!elFromPoint)
const transferData = touchTransfer();
if (transferData === void 0)
return;
if ("node" in elFromPoint && elFromPoint.node.el === touchState.draggedNodes[0].el) {
touchState.lastValue = data.targetData.node.data.value;
return;
}
const touchMoveEventData = {
e: data.e,
targetData: elFromPoint
const config = transferData.draggedParentData.config;
const touchEvent = {
event: e,
...transferData
};
if ("node" in elFromPoint) {
elFromPoint.node.el.dispatchEvent(
new CustomEvent("touchOverNode", {
detail: touchMoveEventData
})
);
} else {
elFromPoint.parent.el.dispatchEvent(
new CustomEvent("touchOverParent", {
detail: touchMoveEventData
})
);
const draggedConfig = touchEvent.draggedParentData.config;
let touchTarget;
switch (e.type) {
case "touchstart":
config?.touchstart ? config.touchstart(touchEvent, state, touchstart) : touchstart(touchEvent, state);
break;
case "touchmove":
config?.touchmove ? config.touchmove(touchEvent, state, touchmove) : touchmove(touchEvent, state);
if (config?.longTouch && !state.longTouch)
return;
touchTarget = getTouchTarget(e);
if (!touchTarget)
return;
if ("targetNode" in touchTarget) {
const event = {
event: e,
...touchTarget,
...transferData
};
if (event.targetNode === event.draggedNode) {
state.lastValue = event.targetNodeData.value;
return;
}
if (event.targetParent === event.lastParent) {
handleSort(event, state);
}
}
break;
case "touchend":
state.touchMoving = false;
draggedConfig?.end ? draggedConfig.end(touchEvent, state, end) : end(touchEvent, state);
break;
}
}
function handleDragoverNode(data) {
if (!state)
function dzDragEvent2(e) {
e.stopPropagation();
e.preventDefault();
const targetData = dzDragTarget(e);
if (!targetData)
return;
dragoverNode(data, state);
}
function handleDragoverParent(eventData) {
if (!state)
const transferData = dragTransfer();
if (transferData === void 0)
return;
transfer(eventData, state);
}
function handleTouchOverParent(e) {
if (!state)
return;
transfer(e.detail, state);
}
function validateTransfer(data, state2) {
if (data.targetData.parent.el === state2.lastParent.el)
return false;
const targetConfig = data.targetData.parent.data.config;
if (targetConfig.dropZone === false)
return false;
const initialParentConfig = state2.initialParent.data.config;
if (targetConfig.accepts && !targetConfig.accepts(
data.targetData.parent,
state2.initialParent,
state2.lastParent,
state2
)) {
return false;
} else if (targetConfig.group && targetConfig.group !== initialParentConfig.group) {
return false;
const event = {
event: e,
...targetData,
...transferData
};
const dzConfig = event.targetParentData.dzConfig;
switch (event.event.type) {
case "dragover":
if (event.targetParent === event.draggedParent && state.leftParent) {
event.targetParentData.config?.transferReturn ? event.targetParentData.config.transferReturn(
event,
state,
transferReturn
) : transferReturn(event, state);
state.leftParent = false;
}
break;
case "dragleave":
event.event.preventDefault();
if (!event.targetParent.contains(
document.elementFromPoint(event.event.clientX, event.event.clientY)
)) {
event.targetParentData.config?.dragleave ? event.targetParentData.config.dragleave(event, state, dragleave) : dragleave(event, state);
state.leftParent = true;
} else if (state.lastParent !== event.targetParent && state.lastParent === event.draggedParent) {
}
break;
case "drop":
if (event.targetParent == event.draggedParent) {
dzConfig && dzConfig.drop ? dzConfig.drop(event, state, drop) : drop(event, state);
}
}
return true;
}
function dragoverNode(eventData, dragState) {
eventData.e.preventDefault();
eventData.targetData.parent.el === dragState.lastParent?.el ? sort(eventData, dragState) : transfer(eventData, dragState);
}
function validateSort(data, dragState, x, y) {
if (!dragState)
return false;
if (data.targetData.parent.el !== dragState.lastParent?.el || data.targetData.node.data.value === dragState.lastValue)
return false;
if (dragState.lastValue === data.targetData.node.data.value) {
if (dragState.direction !== 1)
return false;
if (x >= dragState.lastCoordinates.y - 20 && y >= dragState.lastCoordinates.x - data.targetData.node.el.getBoundingClientRect().width / 20)
return false;
}
return true;
}
function sort(data, state2) {
const { x, y } = eventCoordinates(data.e);
if (!validateSort(data, state2, x, y))
return;
data.targetData.parent.data.config.performSort(state2, data);
state2.lastValue = data.targetData.node.data.value;
state2.lastCoordinates = { x, y };
state2.direction = data.targetData.node.data.index > state2.draggedNode.data.index ? 1 : -1;
}
function nodeEventData(callback) {
function nodeTargetData(node) {
const nodeData = nodes.get(node);
const parent = node.parentNode || state?.lastParent?.el;
if (!nodeData)
function handleSort(event, state2) {
const clientX = "touchedNode" in event ? event.event.touches[0].clientX : event.event.clientX;
const clientY = "touchedNode" in event ? event.event.touches[0].clientY : event.event.clientY;
if (state2.lastValue === event.targetNodeData.value) {
if (state2.direction !== 1) {
return;
const parentData = parents.get(parent);
if (!parentData)
}
if (clientY >= state2.lastCoordinates.y - 20 && clientX >= state2.lastCoordinates.x - event.targetNode.getBoundingClientRect().width / 20) {
return;
return {
node: {
el: node,
data: nodeData
},
parent: {
el: parent,
data: parentData
}
};
}
}
return (e) => {
const targetData = nodeTargetData(e.currentTarget);
if (!targetData)
return;
return callback({
e,
targetData
});
};
}
function performTransfer(state2, data) {
const draggedValues = dragValues(state2);
const lastParentValues = parentValues(
state2.lastParent.el,
state2.lastParent.data
).filter((x) => !draggedValues.includes(x));
const targetParentValues = parentValues(
data.targetData.parent.el,
data.targetData.parent.data
);
"node" in data.targetData ? targetParentValues.splice(
data.targetData.node.data.index,
0,
...draggedValues
) : targetParentValues.push(...draggedValues);
setParentValues(state2.lastParent.el, state2.lastParent.data, lastParentValues);
setParentValues(
data.targetData.parent.el,
data.targetData.parent.data,
targetParentValues
);
}
function transfer(data, state2) {
if (!validateTransfer(data, state2))
if (!validateSort(event)) {
return;
data.targetData.parent.data.config.performTransfer(state2, data);
state2.lastParent = data.targetData.parent;
}
function parentEventData(callback) {
function parentTargetData(parent) {
const parentData = parents.get(parent);
if (!parentData)
return;
return {
parent: {
el: parent,
data: parentData
}
};
}
return (e) => {
const targetData = parentTargetData(e.currentTarget);
if (!targetData)
return;
return callback({
e,
targetData
});
};
state2.lastValue = event.targetNodeData.value;
state2.lastCoordinates = { x: clientX, y: clientY };
state2.direction = event.targetNodeData.index > event.draggedNodeData.index ? 1 : -1;
event.targetParentData.config?.sort ? event.targetParentData.config.sort(event, state2, sort) : sort(event, state2);
}
export {
dragStateProps,
dragValues,
dragstart,
dragstartClasses,
end,
handleLongTouch,
handleTouchOverNode,
handleTouchOverParent,
handleTouchedNode,
initDrag,
initParent,
initTouch,
nodeEventData,
nodes,
parentEventData,
parentObservers,
parentValues,
parents,
performSort,
performTransfer,
remapNodes,
resetState,
setDragState,
setParentValues,
setTouchState,
setupNode,
sort,
state,
tearDownNode,
transfer,
validateSort,
validateTransfer
initParent as default,
nodeDragEvent2 as nodeDragEvent,
setUpDropZone
};
//# sourceMappingURL=index.js.map

@@ -1,167 +0,255 @@

interface InitParent {
parent: HTMLElement;
getValues: (parent: HTMLElement) => Array<any>;
setValues: (parent: HTMLElement, values: Array<any>) => void;
config?: Partial<ParentConfig>;
interface Plugin {
setUp: (parent: HTMLElement, config: DNDConfig) => void;
tearDown?: (parent: HTMLElement, config: DNDConfig) => void;
}
type DNDAction = (data: ParentEventData | NodeEventData, dragState: DragState) => void;
type DNDNodeAction = (data: NodeEventData, dragState: DragState) => void;
type DNDParentAction = (data: ParentEventData, dragState: DragState) => void;
interface ParentConfig {
[key: string]: any;
accepts?: (targetParentData: ParentRecord, initialParentData: ParentRecord, lastParentData: ParentRecord, state: DragState | TouchState) => boolean;
interface DNDConfig {
disabled?: boolean;
dragHandles?: boolean;
draggable?: (child: HTMLElement) => boolean;
draggingClass?: string;
dropZoneClass?: string;
group?: string;
dropZone?: boolean;
handleDragend: DNDNodeAction;
handleDragstart: DNDNodeAction;
longTouchClass?: string;
name?: string;
plugins?: Array<DNDPlugin>;
root: Document | ShadowRoot;
setupNode: SetupNode;
root?: Document | ShadowRoot;
sortable?: boolean;
tearDownNode: TearDownNode;
plugins?: Array<Plugin>;
dragstart?: DragstartEvent;
sort?: SortEvent;
dragleave?: DragleaveEvent;
end?: EndEvent;
transfer?: TransferEvent;
transferReturn?: TransferReturnEvent;
touchstart?: TouchstartEvent;
touchmove?: TouchmoveEvent;
touchend?: TouchendEvent;
drop?: DropEvent;
setDraggable?: ((el: Node) => void) | undefined;
removeDraggable?: ((el: Node) => void) | undefined;
draggingClass?: string;
touchDraggingClass?: string;
dropZoneClass?: string;
touchDropZoneClass?: string;
selectionDraggingClass?: string;
touchSelectionDraggingClass?: string;
selectionDropZoneClass?: string;
touchSelectionDropZoneClass?: string;
longTouchClass?: string;
setSelections?: (el: HTMLElement) => any[];
[key: string]: any;
}
interface ParentData {
getValues: (parent: HTMLElement) => Array<any>;
setValues: (parent: HTMLElement, values: Array<any>) => void;
config: ParentConfig;
enabledNodes: Array<Node>;
abortControllers: Record<string, AbortControllers>;
type Dragstart = (e: NodeDragTargetEvent, state: DNDState, originalDragstart?: Dragstart) => void;
type Sort = (e: NodeDragTargetEvent | NodeTouchTargetEvent, state: DNDState, originalSort?: Sort) => void;
type End = (e: NodeDragTargetEvent | NodeTouchTargetEvent | NodeTouchEvent, state: DNDState, originalEnd?: End) => void;
type Dragleave = (e: DropZoneDragEvent, state: DNDState, originalDragleave?: Dragleave) => void;
type Transfer = (e: DropZoneDragTargetEvent | NodeDragTargetEvent | DropZoneTouchTargetEvent | NodeTouchTargetEvent, state: DNDState, originalTransfer?: Transfer) => void;
type Drop = (e: DropZoneDragEvent, state: DNDState, originalDrop?: Drop) => void;
type TransferReturn = (e: NodeDragTargetEvent | DropZoneDragEvent | NodeTouchTargetEvent | DropZoneTouchEvent, state: DNDState, originalTransferReturn?: TransferReturn) => void;
type Touchstart = (e: NodeTouchEvent, state: DNDState, originalTouchstart?: Touchstart) => void;
type Touchmove = (e: NodeTouchEvent, state: DNDState, originalTouchmove?: Touchmove) => void;
type Touchend = (e: NodeTouchEvent, state: DNDState, originalTouchend?: Touchend) => void;
/**
* The event for the dragstart event.
*
* @public
*/
interface DragstartEvent {
(e: NodeDragTargetEvent, state: DNDState, originalDragstart: Dragstart): void;
}
interface EventListeners {
[key: string]: Array<EventListener>;
/**
* The event for the dragstart event.
*
* @public
*/
interface SortEvent {
(e: NodeDragEvent | NodeTouchEvent, state: DNDState, originalDragsort: Sort): void;
}
interface NodeEventData {
e: Event;
targetData: NodeTargetData;
/**
* The event for the dragleave event.
*
* @public
*/
interface DragleaveEvent {
(e: DropZoneDragEvent, state: DNDState, originalDragleave: Dragleave): void;
}
interface ParentEventData {
e: Event;
targetData: ParentTargetData;
/**
* The event for the dragleave event.
*
* @public
*/
interface EndEvent {
(e: NodeDragTargetEvent | NodeTouchTargetEvent | NodeTouchEvent, state: DNDState, originalEnd: End): void;
}
interface ParentTargetData {
parent: ParentRecord;
interface DropZoneConfig {
group?: string;
accepts?: (ctData: {
name: string | undefined;
group: string | undefined;
values: Array<any>;
}, deData: {
name: string | undefined;
group: string | undefined;
values: Array<any>;
}, draggedValues: Array<any>) => boolean;
parentDropZone?: boolean;
nodeDropZone?: boolean;
validDropZones?: Array<HTMLElement>;
transfer?: TransferEvent;
drop?: DropEvent;
}
interface NodeRecord {
el: Node;
data: NodeData;
/**
* The event for the dragstart event.
*
* @public
*/
interface TransferEvent {
(e: DropZoneDragEvent | NodeDragEvent | DropZoneTouchEvent | NodeTouchEvent, state: DNDState, originalTransfer: Transfer): void;
}
interface ParentRecord {
el: HTMLElement;
data: ParentData;
/**
* The event for the dragleave event.
*
* @public
*/
interface DropEvent {
(e: DropZoneDragEvent, state: DNDState, originDrop: Drop): void;
}
type DropZoneElement = HTMLElement;
interface NodeDragEventData extends NodeEventData {
e: DragEvent;
/**
* The event for the dragstart event.
*
* @public
*/
interface DropEvent {
(e: DropZoneDragEvent, state: DNDState, originalTransfer: Transfer): void;
}
interface NodeDragEventData {
e: DragEvent;
targetData: NodeTargetData;
/**
* The event for the transfer return event.
*
* @public
*/
interface TransferReturnEvent {
(e: NodeDragEvent | DropZoneDragEvent, state: DNDState, originalTransferReturn: TransferReturn): void;
}
interface NodeTouchEventData {
e: TouchEvent;
targetData: NodeTargetData;
/**
* The event for the transfer return event.
*
* @public
*/
interface TouchstartEvent {
(e: NodeTouchEvent, state: DNDState, originalTouchstart: Touchstart): void;
}
interface NodeTouchEventData {
e: TouchEvent;
targetData: NodeTargetData;
/**
* The event for the transfer return event.
*
* @public
*/
interface TouchmoveEvent {
(e: NodeTouchEvent, state: DNDState, originalTouchmove: Touchmove): void;
}
interface NodeFromPoint {
node: NodeRecord;
parent: ParentRecord;
/**
* The event for the transfer return event.
*
* @public
*/
interface TouchendEvent {
(e: NodeTouchEvent, state: DNDState, originalTouchend: Touchend): void;
}
interface ParentFromPoint {
parent: ParentRecord;
interface MultiDragConfig {
selectedClass?: string;
selected?: Selected;
multiDragstart?: MultiDragstart;
setStyle?: setStyle;
originClass?: string;
originLeaveClass?: string;
transitClass?: string;
}
type DNDDragAction = (data: NodeEventData) => void;
type DNDTouchAction = (data: NodeTouchEventData) => void;
interface NodeData {
index: number;
value: any;
privateClasses: Array<string>;
abortControllers: Record<string, AbortControllers>;
type setStyle = (e: NodeDragEvent, state: DNDState, draggedNodes: Array<Node>, x: number, y: number) => [Node, number, number];
type MultiDragstart = (e: NodeDragTargetEvent, state: DNDState, originalMultiDragstart?: MultiDragstart) => void;
/**
* The event for the dragleave event.
*
* @public
*/
interface MultiDragstartEvent {
(e: NodeDragEvent | NodeTouchEvent, state: DNDState, originalMultiDragstart: Dragleave): void;
}
type NodeEvent = (data: NodeEventData) => void;
interface Node extends HTMLElement {
parentNode: HTMLElement;
interface NodeTarget {
targetNode: Node;
targetNodeData: NodeData;
targetParent: HTMLElement;
targetParentData: ParentData;
}
interface TouchOverNodeEvent extends Event {
detail: {
e: TouchEvent;
targetData: NodeTargetData;
};
interface DragTransfer {
draggedNode: Node;
draggedNodeData: NodeData;
draggedParent: HTMLElement;
draggedParentData: ParentData;
lastParent: HTMLElement;
lastParentData: ParentData;
}
interface TouchOverParentEvent extends Event {
detail: {
e: TouchEvent;
targetData: ParentTargetData;
};
interface NodeDragEvent {
event: DragEvent;
draggedNode: Node;
draggedNodeData: NodeData;
draggedParent: HTMLElement;
draggedParentData: ParentData;
lastParent: HTMLElement;
lastParentData: ParentData;
}
interface DNDData {
nodes: WeakMap<Node, NodeData>;
parents: WeakMap<HTMLElement, ParentData>;
parentObservers: WeakMap<HTMLElement, MutationObserver>;
[key: string]: any;
type NodeDragTargetEvent = NodeDragEvent & NodeTarget;
interface TouchTransfer extends DragTransfer {
touchedNode: HTMLElement;
}
type NodesData = WeakMap<Node, NodeData>;
type ParentsData = WeakMap<HTMLElement, ParentData>;
type ParentObservers = WeakMap<HTMLElement, MutationObserver>;
interface NodeTargetData {
node: NodeRecord;
parent: ParentRecord;
interface NodeTouchEvent {
event: TouchEvent;
draggedNode: Node;
draggedNodeData: NodeData;
draggedParent: HTMLElement;
draggedParentData: ParentData;
lastParent: HTMLElement;
lastParentData: ParentData;
touchedNode: HTMLElement;
}
interface DNDPluginData {
setupParent: () => void;
tearDownParent: () => void;
setupNode?: SetupNode;
tearDownNode?: TearDownNode;
type NodeTouchTargetEvent = NodeTouchEvent & NodeTarget;
interface DropZoneTarget {
targetParent: HTMLElement;
targetParentData: ParentData;
}
interface PluginData {
parent: HTMLElement;
type DropZoneDragTargetEvent = DropZoneDragEvent & DropZoneTarget;
type DropZoneTouchTargetEvent = DropZoneTouchEvent & DropZoneTarget;
type DropZoneEvent = DropZoneTarget & DragTransfer;
interface DropZoneDragEvent {
event: DragEvent;
draggedNode: Node;
draggedNodeData: NodeData;
draggedParent: HTMLElement;
draggedParentData: ParentData;
lastParent: HTMLElement;
lastParentData: ParentData;
}
type DNDPlugin = (parent: HTMLElement) => DNDPluginData | undefined;
interface DragAndDropData {
parent: HTMLElement;
values: Array<any>;
interface DropZoneDragEvent extends DropZoneEvent {
event: DragEvent;
}
type SetupNode = (data: SetupNodeData) => void;
type TearDownNode = (data: TearDownNodeData) => void;
interface SetupNodeData {
node: Node;
nodeData: NodeData;
parent: HTMLElement;
parentData: ParentData;
interface DropZoneTouchEvent {
event: TouchEvent;
draggedNode: Node;
draggedNodeData: NodeData;
draggedParent: HTMLElement;
draggedParentData: ParentData;
lastParent: HTMLElement;
lastParentData: ParentData;
touchedNode: HTMLElement;
}
interface TearDownNodeData {
node: Node;
nodeData?: NodeData;
parent: HTMLElement;
parentData: ParentData;
interface DropZoneTouchEvent extends DropZoneEvent {
event: TouchEvent;
targetParent: HTMLElement;
targetParentData: ParentData;
}
type EventHandlers = Record<string, (e: Event) => void>;
interface TouchState extends DragState {
touchMoving: boolean;
touchStartLeft: number;
touchStartTop: number;
touchedNode: HTMLElement;
longTouchTimeout: ReturnType<typeof setTimeout> | undefined;
scrollParent: HTMLElement | undefined;
scrollParentOverflow: string | undefined;
longTouch: boolean;
draggedNode: NodeRecord;
draggedNodes: Array<NodeRecord>;
initialParent: ParentRecord;
lastParent: ParentRecord;
}
interface DragState extends DragStateProps {
type DNDState = {
activeNode: Node | undefined;
clonedDraggedNodes: Array<HTMLElement>;
direction: number | undefined;
draggedNode: Node | undefined;
draggedNodes: CarriedNodes;
dropped: boolean;
dropZones: WeakMap<HTMLElement, HTMLElement>;
enterCount: number;
nodeData: WeakMap<Node, NodeData>;
hiddenNodes: WeakMap<HTMLElement, Array<Node>>;
initialParent: HTMLElement | undefined;
initialParentValues: Array<any>;
lastCoordinates: {

@@ -171,108 +259,62 @@ x: number;

};
lastValue: any;
draggedNode: NodeRecord;
draggedNodes: Array<NodeRecord>;
initialParent: ParentRecord;
lastParent: ParentRecord;
clonedDraggedEls: Array<Element>;
lastValue: any | undefined;
lastParent: HTMLElement | undefined;
leftParent: boolean;
longTouch: boolean;
longTouchTimeout: ReturnType<typeof setTimeout> | undefined;
parentData: WeakMap<HTMLElement, ParentData>;
parentObservers: WeakMap<HTMLElement, MutationObserver>;
parents: WeakSet<HTMLElement>;
preventEnter: boolean;
removeDraggable: ((el: Node) => void) | undefined;
scrollParent: HTMLElement | undefined;
scrollParentOverflow: string | undefined;
selectedNodes: Array<Node>;
selectedValues: Array<any>;
touchMoving: boolean;
touchedNode: HTMLElement | undefined;
touchEnded: boolean;
touchStartLeft: number | undefined;
touchStartTop: number | undefined;
};
interface ParentData {
getValues: (parent: HTMLElement) => Array<any>;
setValues: (parent: HTMLElement, values: Array<any>) => void;
config?: DNDConfig;
enabledNodes: Array<Node>;
dzConfig?: DropZoneConfig;
}
interface DragStateProps {
draggedNode: NodeRecord;
draggedNodes: Array<NodeRecord>;
initialParent: ParentRecord;
lastParent: ParentRecord;
type CarriedNodes = Array<Node>;
interface NodeData {
index: number;
value: any;
privateClasses: Array<string>;
}
interface TouchStateProps {
touchedNode: HTMLElement;
touchStartLeft: number;
touchStartTop: number;
interface Node extends HTMLElement {
parentNode: HTMLElement;
}
interface AbortControllers {
[key: string]: AbortController;
type Selected = (data: SelectedData) => void;
interface SelectedData {
el: HTMLElement;
nodeData: NodeData;
parent: HTMLElement;
parentData: ParentData;
}
declare const nodes: NodesData;
declare const parents: ParentsData;
declare const parentObservers: ParentObservers;
declare function setUpDropZone(dzConfig: DropZoneConfig | undefined, parent: HTMLElement, config: DNDConfig): void;
/**
* The state of the drag and drop. Is undefined until either dragstart or
* touchstart is called.
*/
declare let state: DragState | TouchState | undefined;
declare function resetState(): void;
/**
* @param {DragStateProps} dragStateProps - Attributes to update state with.
* Initializes the drag and drop functionality.
*
* @mutation - Updates state with node values.
*
* @returns void
*/
declare function setDragState(dragStateProps: DragStateProps): DragState;
declare function setTouchState(dragState: DragState, touchStateProps: TouchStateProps): TouchState;
declare function dragStateProps(targetData: NodeTargetData): DragStateProps;
declare function performSort(state: DragState | TouchState, data: NodeDragEventData | NodeTouchEventData): void;
declare function parentValues(parent: HTMLElement, parentData: ParentData): Array<any>;
declare function setParentValues(parent: HTMLElement, parentData: ParentData, values: Array<any>): void;
declare function dragValues(state: DragState | TouchState): Array<any> | any;
/**
* Initializes the drag and drop functionality for a given parent.
*
* @param id - The id of the parent element.
*
* @param getValues - A function that returns the current values of the parent element.
*
* @param setValues - A function that sets the values of the parent element.
*
* @param config - The config for the parent element.
*
* @returns void
*
*/
declare function initParent({ parent, getValues, setValues, config, }: InitParent): void;
/**
* Remaps the data of the parent element's children.
*
* @param parent - The parent element.
*
* @returns void
*
* @internal
*/
declare function remapNodes(parent: HTMLElement): void;
declare function dragstartClasses(el: HTMLElement | Node | Element, draggingClass: string | undefined, dropZoneClass: string | undefined): void;
declare function initDrag(eventData: NodeDragEventData): DragState;
declare function dragstart(data: NodeDragEventData): void;
declare function handleTouchOverNode(e: TouchOverNodeEvent): void;
declare function setupNode(data: SetupNodeData): void;
declare function tearDownNode(data: TearDownNodeData): void;
declare function end(_eventData: NodeEventData, state: DragState | TouchState): void;
declare function initTouch(data: NodeTouchEventData): TouchState;
declare function handleTouchedNode(data: NodeTouchEventData, touchState: TouchState): void;
declare function handleLongTouch(data: NodeEventData, touchState: TouchState): void;
declare function handleTouchOverParent(e: TouchOverParentEvent): void;
declare function validateTransfer(data: ParentEventData, state: DragState | TouchState): boolean;
declare function validateSort(data: NodeDragEventData | NodeTouchEventData, dragState: DragState | TouchState, x: number, y: number): boolean;
declare function sort(data: NodeDragEventData | NodeTouchEventData, state: DragState | TouchState): void;
/**
* Event listener used for all nodes.
*
* @param e - The event.
*
*/
declare function nodeEventData(callback: any): (e: Event) => NodeEventData | undefined;
declare function performTransfer(state: DragState | TouchState, data: NodeEventData | ParentEventData): void;
/**
* Used when the dragged element enters into a parent other than its own.
*
* @param eventData
*
* @param state
*
* @internal
*
* @returns void
*/
declare function transfer(data: NodeEventData | ParentEventData, state: DragState | TouchState): void;
declare function parentEventData(callback: any): (e: Event) => NodeEventData | undefined;
declare function initParent(parent: HTMLElement, getValues: (parent: HTMLElement) => Array<any>, setValues: (parent: HTMLElement, values: Array<any>) => void, config: DNDConfig): void;
declare function nodeDragEvent(e: DragEvent): void;
export { type AbortControllers, type DNDAction, type DNDData, type DNDDragAction, type DNDNodeAction, type DNDParentAction, type DNDPlugin, type DNDPluginData, type DNDTouchAction, type DragAndDropData, type DragState, type DragStateProps, type DropZoneElement, type EventHandlers, type EventListeners, type InitParent, type Node, type NodeData, type NodeDragEventData, type NodeEvent, type NodeEventData, type NodeFromPoint, type NodeRecord, type NodeTargetData, type NodeTouchEventData, type NodesData, type ParentConfig, type ParentData, type ParentEventData, type ParentFromPoint, type ParentObservers, type ParentRecord, type ParentTargetData, type ParentsData, type PluginData, type SetupNode, type SetupNodeData, type TearDownNode, type TearDownNodeData, type TouchOverNodeEvent, type TouchOverParentEvent, type TouchState, type TouchStateProps, dragStateProps, dragValues, dragstart, dragstartClasses, end, handleLongTouch, handleTouchOverNode, handleTouchOverParent, handleTouchedNode, initDrag, initParent, initTouch, nodeEventData, nodes, parentEventData, parentObservers, parentValues, parents, performSort, performTransfer, remapNodes, resetState, setDragState, setParentValues, setTouchState, setupNode, sort, state, tearDownNode, transfer, validateSort, validateTransfer };
export { type DNDConfig, type DNDState, type DragTransfer, type Dragleave, type DragleaveEvent, type Dragstart, type DragstartEvent, type Drop, type DropEvent, type DropZoneConfig, type DropZoneDragEvent, type DropZoneDragTargetEvent, type DropZoneEvent, type DropZoneTarget, type DropZoneTouchEvent, type DropZoneTouchTargetEvent, type End, type EndEvent, type MultiDragConfig, type MultiDragstart, type MultiDragstartEvent, type Node, type NodeData, type NodeDragEvent, type NodeDragTargetEvent, type NodeTarget, type NodeTouchEvent, type NodeTouchTargetEvent, type ParentData, type Plugin, type Sort, type SortEvent, type TouchTransfer, type Touchend, type TouchendEvent, type Touchmove, type TouchmoveEvent, type Touchstart, type TouchstartEvent, type Transfer, type TransferEvent, type TransferReturn, type TransferReturnEvent, initParent as default, nodeDragEvent, setUpDropZone };

@@ -0,98 +1,185 @@

// src/predicates.ts
function isNode(el, state2) {
return el instanceof HTMLElement && el.parentNode instanceof HTMLElement || state2.lastParent instanceof HTMLElement;
}
// src/state.ts
var state = {
activeNode: void 0,
enterCount: 0,
clonedDraggedNodes: Array(),
direction: void 0,
draggedNode: void 0,
draggedNodes: Array(),
dropZones: /* @__PURE__ */ new WeakMap(),
dropped: false,
hiddenNodes: /* @__PURE__ */ new WeakMap(),
initialParent: void 0,
initialParentValues: [],
lastCoordinates: {
x: 0,
y: 0
},
lastParent: void 0,
lastValue: void 0,
leftParent: false,
longTouch: false,
longTouchTimeout: void 0,
nodeData: /* @__PURE__ */ new WeakMap(),
parentData: /* @__PURE__ */ new WeakMap(),
parentObservers: /* @__PURE__ */ new WeakMap(),
parents: /* @__PURE__ */ new WeakSet(),
preventEnter: false,
removeDraggable: void 0,
scrollParent: void 0,
scrollParentOverflow: void 0,
touchMoving: false,
selectedNodes: Array(),
selectedValues: Array(),
touchEnded: false,
touchedNode: void 0,
touchStartLeft: void 0,
touchStartTop: void 0
};
// src/utils.ts
function splitClass(className) {
return className.split(" ").filter((x) => x);
}
var isBrowser = typeof window !== "undefined";
function handleClass(els, className, remove = false, omitAppendPrivateClass = false) {
if (!className)
function cleanUp(e) {
if (!state.draggedNode || !state.initialParent || !state.lastParent)
return;
const classNames = splitClass(className);
if (!classNames.length)
return;
remove ? removeClass(els, classNames) : addClass(els, classNames, omitAppendPrivateClass);
}
function addClass(els, classNames, omitAppendPrivateClass = false) {
if (classNames.includes("longTouch"))
return;
for (const node of els) {
if (!isNode(node) || !nodes.has(node)) {
node.classList.add(...classNames);
continue;
const config = state.parentData.get(state.initialParent)?.config;
const root = config?.root || document;
if (config) {
const hasSelections = state.selectedValues.length > 0;
const isTouch = !(e.event instanceof DragEvent);
let dropZoneClass;
if (isTouch) {
dropZoneClass = hasSelections ? config.touchSelectionDropZoneClass : config.touchDropZoneClass;
} else {
dropZoneClass = hasSelections ? config.selectionDropZoneClass : config.dropZoneClass;
}
const privateClasses = [];
const nodeData = nodes.get(node);
if (!nodeData)
continue;
for (const className of classNames) {
if (!node.classList.contains(className)) {
node.classList.add(className);
} else if (node.classList.contains(className) && omitAppendPrivateClass === false) {
privateClasses.push(className);
}
handleClass(state.draggedNodes, dropZoneClass, state, true);
if (dropZoneClass) {
const elsWithDropZoneClass = root.querySelectorAll(
`.${splitClass(dropZoneClass)}`
);
handleClass(Array.from(elsWithDropZoneClass), dropZoneClass, state, true);
}
nodeData.privateClasses = privateClasses;
nodes.set(node, nodeData);
}
}
function removeClass(els, classNames) {
for (const node of els) {
if (!isNode(node)) {
node.classList.remove(...classNames);
continue;
}
const nodeData = nodes.get(node);
if (!nodeData)
continue;
for (const className of classNames) {
if (!nodeData.privateClasses.includes(className)) {
node.classList.remove(className);
}
}
handleClass(
state.draggedNodes,
state.parentData.get(state.initialParent)?.config?.longTouchClass,
state,
true
);
state.touchedNode?.remove();
state.touchedNode = void 0;
state.lastParent = void 0;
state.draggedNode = void 0;
state.draggedNodes = [];
state.selectedNodes = [];
state.selectedValues = [];
state.touchStartTop = state.touchedNode = void 0;
state.touchStartLeft = 0;
state.lastValue = void 0;
state.touchMoving = false;
state.lastCoordinates = {
x: 0,
y: 0
};
state.direction = void 0;
if (state.scrollParent) {
state.scrollParent.style.overflow = state.scrollParentOverflow || "";
state.scrollParent = void 0;
state.scrollParentOverflow = void 0;
}
}
function getScrollParent(node) {
if (node == null)
return void 0;
if (node.scrollHeight > node.clientHeight) {
return node;
} else if (node.parentNode instanceof HTMLElement) {
return getScrollParent(node.parentNode);
}
return void 0;
function dragTransfer() {
if (!state.draggedNode || !state.lastParent || !state.initialParent)
return;
const draggedNodeData = state.nodeData.get(state.draggedNode);
const draggedParentData = state.parentData.get(state.initialParent);
const lastParentData = state.parentData.get(state.lastParent);
if (!draggedNodeData || !draggedParentData || !lastParentData)
return;
else
return {
draggedNode: state.draggedNode,
draggedNodeData,
draggedParent: state.initialParent,
draggedParentData,
lastParent: state.lastParent,
lastParentData
};
}
function getElFromPoint(eventData) {
if (!(eventData.e instanceof TouchEvent))
function touchTransfer() {
if (!state.touchedNode)
return;
const newX = eventData.e.touches[0].clientX;
const newY = eventData.e.touches[0].clientY;
const els = document.elementsFromPoint(newX, newY);
const data = dragTransfer();
if (data === void 0)
return;
const transferTouchData = {
...data,
touchedNode: state.touchedNode
};
return transferTouchData;
}
function nodeDragTarget(e) {
if (!isNode(e.currentTarget, state))
return;
const targetNodeData = state.nodeData.get(e.currentTarget);
const targetParentData = state.parentData.get(
e.currentTarget.parentNode || state.lastParent
);
if (!targetNodeData || !targetParentData)
return;
const targetData = {
targetNode: e.currentTarget,
targetNodeData,
targetParent: e.currentTarget.parentNode || state.lastParent,
targetParentData
};
return targetData;
}
function dzDragTarget(e) {
if (!isNode(e.currentTarget, state))
return;
const targetParentData = state.parentData.get(e.currentTarget);
if (!targetParentData)
return;
const targetData = {
targetParent: e.currentTarget,
targetParentData
};
return targetData;
}
function getTouchTarget(e) {
const newX = e.touches[0].clientX;
const newY = e.touches[0].clientY;
const nodes = document.elementsFromPoint(newX, newY);
if (!nodes)
return;
for (const node of els) {
if (isNode(node) && nodes.has(node)) {
for (const node of nodes) {
if (isNode(node, state) && state.nodeData.has(node)) {
const targetNode = node;
const targetNodeData = nodes.get(targetNode);
const targetParentData = parents.get(targetNode.parentNode);
const targetNodeData = state.nodeData.get(targetNode);
const targetParentData = state.parentData.get(targetNode.parentNode);
if (!targetNodeData || !targetParentData)
return;
return {
node: {
el: targetNode,
data: targetNodeData
},
parent: {
el: targetNode.parentNode,
data: targetParentData
}
targetNode,
targetNodeData,
targetParent: targetNode.parentNode,
targetParentData
};
} else if (node instanceof HTMLElement) {
const parentData = parents.get(node);
if (parentData) {
return {
parent: {
el: node,
data: parentData
}
};
}
} else if (node instanceof HTMLElement && state.dropZones.has(node)) {
if (!state.parentData.has(node))
return;
const targetParentData = state.parentData.get(node);
if (!targetParentData)
return;
return {
targetParent: node,
targetParentData
};
}

@@ -102,16 +189,61 @@ }

}
function isNode(el) {
return el instanceof HTMLElement && el.parentNode instanceof HTMLElement;
function handleSelections(e, selectedValues, state2, x, y) {
state2.selectedValues = selectedValues;
for (const child of e.draggedParentData.enabledNodes) {
if (child === e.draggedNode) {
state2.selectedNodes.push(child);
continue;
}
const childValue = state2.nodeData.get(child)?.value;
if (!childValue)
continue;
if (selectedValues?.includes(childValue)) {
state2.draggedNodes.push(child);
}
}
const config = e.draggedParentData.config;
const clonedEls = state2.draggedNodes.map((x2) => {
const el = x2.cloneNode(true);
copyNodeStyle(x2, el, true);
if (e.event instanceof DragEvent)
handleClass([el], config?.selectionDraggingClass, state2);
return el;
});
setTimeout(() => {
if (e.event instanceof DragEvent && config?.selectionDropZoneClass)
for (const node of state2.draggedNodes) {
node.classList.add(config?.selectionDropZoneClass);
}
});
state2.clonedDraggedNodes = clonedEls;
return { e, clonedEls, x, y };
}
function addEvents(el, events) {
const keysToControllers = {};
for (const key in events) {
const abortController = new AbortController();
const event = events[key];
el.addEventListener(key, event, {
signal: abortController.signal
function stackNodes({
e,
clonedEls,
x,
y
}) {
const wrapper = document.createElement("div");
if (!(wrapper instanceof HTMLElement))
return;
for (const el of clonedEls)
wrapper.append(el);
const rect = e.draggedNode.getBoundingClientRect();
wrapper.style.cssText = `
display: flex;
flex-direction: column;
width: ${rect.width}px;
position: absolute;
left: -9999px
`;
document.body.append(wrapper);
if (e.event instanceof DragEvent) {
e.event.dataTransfer?.setDragImage(wrapper, x, y);
setTimeout(() => {
wrapper.remove();
});
keysToControllers[key] = abortController;
} else {
state.touchedNode = wrapper;
}
return keysToControllers;
}

@@ -140,3 +272,3 @@ function copyNodeStyle(sourceNode, targetNode, omitKeys = false) {

for (const child of Array.from(sourceNode.children)) {
if (!isNode(child))
if (!isNode(child, state))
continue;

@@ -147,155 +279,481 @@ const targetChild = targetNode.children[Array.from(sourceNode.children).indexOf(child)];

}
function eventCoordinates(data) {
return data instanceof DragEvent ? { x: data.clientX, y: data.clientY } : { x: data.touches[0].clientX, y: data.touches[0].clientY };
function handleClass(nodes, className, state2, remove = false, omitAppendPrivateClass = false) {
if (!className)
return;
const classNames = splitClass(className);
if (!classNames.length)
return;
remove ? removeClass(nodes, classNames, state2) : addClass(nodes, classNames, state2, omitAppendPrivateClass);
}
function addClass(nodes, classNames, state2, omitAppendPrivateClass = false) {
if (classNames.includes("longTouch"))
return;
for (const node of nodes) {
if (!isNode(node, state2) || !state2.nodeData.has(node)) {
node.classList.add(...classNames);
continue;
}
const privateClasses = [];
const nodeData = state2.nodeData.get(node);
if (!nodeData)
continue;
for (const className of classNames) {
if (!node.classList.contains(className)) {
node.classList.add(className);
} else if (node.classList.contains(className) && omitAppendPrivateClass === false) {
privateClasses.push(className);
}
}
nodeData.privateClasses = privateClasses;
state2.nodeData.set(node, nodeData);
}
}
function removeClass(nodes, classNames, state2) {
for (const node of nodes) {
if (!isNode(node, state2)) {
node.classList.remove(...classNames);
continue;
}
const nodeData = state2.nodeData.get(node);
if (!nodeData)
continue;
for (const className of classNames) {
if (!nodeData.privateClasses.includes(className)) {
node.classList.remove(className);
}
}
}
}
function splitClass(className) {
return className.split(" ").filter((x) => x);
}
function getScrollParent(node) {
if (node == null) {
return void 0;
}
if (node.scrollHeight > node.clientHeight) {
return node;
} else {
if (node.parentNode instanceof HTMLElement)
return getScrollParent(node.parentNode);
}
return void 0;
}
// src/index.ts
var nodes = /* @__PURE__ */ new WeakMap();
var parents = /* @__PURE__ */ new WeakMap();
var parentObservers = /* @__PURE__ */ new WeakMap();
var state = void 0;
function resetState() {
state = void 0;
// src/validators.ts
function validateSort(e) {
const dzConfig = e.targetParentData.dzConfig;
if (dzConfig && dzConfig.nodeDropZone === false)
return false;
return e.targetParentData.config?.sortable !== false;
}
function setDragState(dragStateProps2) {
state = {
direction: void 0,
enterCount: 0,
lastCoordinates: { x: 0, y: 0 },
lastValue: void 0,
clonedDraggedEls: [],
...dragStateProps2
function validateTransfer(e) {
const draggedValues = state.draggedNodes.map(
(el) => state.nodeData.get(el)?.value
);
const targetParentValues = e.targetParentData.getValues(e.targetParent);
const draggedParentValues = e.draggedParentData.getValues(e.draggedParent);
const ctData = {
name: e.targetParentData.config?.name,
group: e.targetParentData.dzConfig?.group,
values: targetParentValues
};
return state;
}
function setTouchState(dragState, touchStateProps) {
state = {
...dragState,
...touchStateProps
const deData = {
name: e.draggedParentData.config?.name,
group: e.draggedParentData.dzConfig?.group,
values: draggedParentValues
};
return state;
const accepts = e.targetParentData.dzConfig?.accepts;
if (accepts)
return accepts(ctData, deData, draggedValues);
else if (ctData.group) {
return ctData.group === deData.group;
}
return true;
}
function dragStateProps(targetData) {
return {
draggedNode: {
el: targetData.node.el,
data: targetData.node.data
},
draggedNodes: [
{
el: targetData.node.el,
data: targetData.node.data
// src/actions.ts
function dragstart(e, state2) {
if (e.event.dataTransfer) {
e.event.dataTransfer.dropEffect = "move";
e.event.dataTransfer.effectAllowed = "move";
}
const config = e.targetParentData.config;
const selectedValues = config?.setSelections && config?.setSelections(e.draggedNode.parentNode);
if (Array.isArray(selectedValues) && selectedValues.length) {
const targetRect = e.targetNode.getBoundingClientRect();
const x = e.event.clientX - targetRect.left;
const y = e.event.clientY - targetRect.top;
stackNodes(handleSelections(e, selectedValues, state2, x, y));
} else if (config) {
handleClass(state2.draggedNodes, config.draggingClass, state2);
setTimeout(() => {
handleClass(state2.draggedNodes, config.draggingClass, state2, true);
handleClass(state2.draggedNodes, config.dropZoneClass, state2);
});
}
}
function touchstart(e, state2) {
e.event.stopPropagation();
const rect = e.draggedNode.getBoundingClientRect();
state2.touchStartLeft = e.event.touches[0].clientX - rect.left;
state2.touchStartTop = e.event.touches[0].clientY - rect.top;
const config = e.draggedParentData.config;
const selectedValues = config?.setSelections && config?.setSelections(e.draggedNode.parentNode);
if (Array.isArray(selectedValues) && selectedValues.length) {
stackNodes(
handleSelections(
e,
selectedValues,
state2,
state2.touchStartLeft,
state2.touchStartTop
)
);
} else {
state2.touchedNode = e.touchedNode;
e.touchedNode.style.cssText = `
width: ${rect.width}px;
position: absolute;
pointer-events: none;
top: -9999px;
z-index: 999999;
display: none;
`;
document.body.append(e.touchedNode);
copyNodeStyle(e.draggedNode, e.touchedNode);
e.touchedNode.style.display = "none";
}
if (config?.longTouch) {
state2.longTouchTimeout = setTimeout(() => {
state2.longTouch = true;
const parentScroll = getScrollParent(e.draggedNode);
if (parentScroll) {
state2.scrollParent = parentScroll;
state2.scrollParentOverflow = parentScroll.style.overflow;
parentScroll.style.overflow = "hidden";
}
],
initialParent: {
el: targetData.parent.el,
data: targetData.parent.data
},
lastParent: {
el: targetData.parent.el,
data: targetData.parent.data
if (config.longTouchClass)
handleClass(state2.draggedNodes, config.longTouchClass, state2);
if (e.event.cancelable)
e.event.preventDefault();
document.addEventListener("contextmenu", function(e2) {
e2.preventDefault();
});
}, 200);
}
}
function touchmove(e, state2) {
if (!state2.touchedNode)
return;
const config = e.draggedParentData.config;
if (config?.longTouch && !state2.longTouch) {
clearTimeout(state2.longTouchTimeout);
return;
}
if (state2.touchMoving !== true) {
state2.touchMoving = true;
handleClass(state2.draggedNodes, config?.longTouchClass, state2, true);
const hasSelections = Array.isArray(state2.selectedValues) && state2.selectedValues.length;
if (hasSelections) {
handleClass(
state2.clonedDraggedNodes,
config?.touchSelectionDraggingClass,
state2
);
handleClass(
state2.draggedNodes,
config?.touchSelectionDropZoneClass,
state2
);
} else {
handleClass([state2.touchedNode], config?.touchDraggingClass, state2);
handleClass(state2.draggedNodes, config?.touchDropZoneClass, state2);
}
};
}
e.touchedNode.style.display = "block";
const x = e.event.touches[0].clientX + window.scrollX;
const y = e.event.touches[0].clientY + window.scrollY;
const windowHeight = window.innerHeight + window.scrollY;
if (y > windowHeight - 50) {
window.scrollBy(0, 10);
} else if (y < window.scrollY + 50) {
window.scrollBy(0, -10);
}
const touchStartLeft = state2.touchStartLeft ?? 0;
const touchStartTop = state2.touchStartTop ?? 0;
e.touchedNode.style.left = `${x - touchStartLeft}px`;
e.touchedNode.style.top = `${y - touchStartTop}px`;
if (e.event.cancelable)
e.event.preventDefault();
}
function performSort(state2, data) {
const draggedValues = dragValues(state2);
const targetParentValues = parentValues(
data.targetData.parent.el,
data.targetData.parent.data
function sort(e, state2) {
const targetParentValues = e.targetParentData.getValues(e.targetParent);
const draggedNodeValues = state2.draggedNodes.map(
(x) => state2.nodeData.get(x)?.value
);
const newParentValues = [
...targetParentValues.filter((x) => !draggedValues.includes(x))
...targetParentValues.filter((x) => !draggedNodeValues.includes(x))
];
newParentValues.splice(data.targetData.node.data.index, 0, ...draggedValues);
setParentValues(data.targetData.parent.el, data.targetData.parent.data, [
...newParentValues
]);
newParentValues.splice(e.targetNodeData.index, 0, ...draggedNodeValues);
e.targetParentData.setValues(e.targetParent, newParentValues);
}
function parentValues(parent, parentData) {
return parentData.getValues(parent);
function transferReturn(e, state2) {
if (e.draggedParent === e.lastParent)
return;
const leftParentValues = [...e.lastParentData.getValues(e.lastParent)];
const draggedValues = state2.draggedNodes.map(
(x) => state2.nodeData.get(x)?.value
);
const newLeftParentValues = [
...leftParentValues.filter((x) => !draggedValues.includes(x))
];
e.lastParentData.setValues(e.lastParent, newLeftParentValues);
state2.lastParent = e.draggedParent;
e.draggedParentData.setValues(e.draggedParent, state2.initialParentValues);
}
function setParentValues(parent, parentData, values) {
parentData.setValues(parent, values);
function dragleave(_e, _state) {
}
function dragValues(state2) {
return state2.draggedNodes.map((x) => x.data.value);
function end(e, state2) {
if (e.event instanceof DragEvent && state2.longTouch)
return;
if (!(e.event instanceof DragEvent)) {
state2.longTouch = false;
clearTimeout(state2.longTouchTimeout);
}
if (state2.draggedNode && state2.draggedNode.parentNode && state2.draggedNode.parentNode !== state2.lastParent && state2.initialParent) {
const deData = state2.nodeData.get(state2.draggedNode);
if (deData) {
const deParentData = state2.parentData.get(state2.draggedNode.parentNode);
const deParentValues = [
...deParentData?.getValues(state2.draggedNode.parentNode) || []
];
const deIndex = deData.index;
deParentValues.splice(deIndex, state2.draggedNodes.length);
deParentData?.setValues(state2.initialParent, deParentValues);
}
}
cleanUp(e);
}
function initParent({
parent,
getValues,
setValues,
config = {}
}) {
if (!isBrowser)
function drop(e, _state) {
cleanUp(e);
}
// src/plugins/dropZone.ts
function setUpDropZone(dzConfig = {}, parent, config) {
const parentData = state.parentData.get(parent);
if (!parentData)
return;
document.addEventListener("dragover", (e) => {
e.preventDefault();
});
tearDownParent(parent);
const parentData = {
getValues,
setValues,
config: {
handleDragstart,
handleDragoverNode,
handleDragoverParent,
handleDragend,
handleTouchstart,
handleTouchmove,
handleTouchOverNode,
handleTouchOverParent,
performSort,
performTransfer,
root: document,
setupNode,
reapplyDragClasses,
tearDownNode,
...config
},
enabledNodes: [],
abortControllers: {}
!config.disabled ? activateDZ(parent, dzConfig, parent) : deactivateDZ(parent);
for (const dz of dzConfig.validDropZones || []) {
activateDZ(dz, dzConfig, parent);
}
const parentConfig = parentData.config;
if (!parentConfig || !parentConfig.setDraggable)
return;
const setDraggable2 = parentConfig.setDraggable;
parentConfig.setDraggable = (node) => {
setDraggable2(node);
setNodeEvents(node);
return node;
};
setupParent(parent, parentData);
config.plugins?.forEach((plugin) => {
plugin(parent)?.tearDownParent();
});
remapNodes(parent);
config.plugins?.forEach((plugin) => {
plugin(parent)?.setupParent();
});
}
function tearDownParent(parent) {
const parentData = parents.get(parent);
function activateDZ(dz, dzConfig, parent) {
dz.addEventListener("dragover", dzDragEvent);
dz.addEventListener("drop", dzDragEvent);
const parentData = state.parentData.get(dz);
if (!parentData)
return;
for (const event of Object.keys(parentData.abortControllers["mainParent"])) {
parentData.abortControllers["mainParent"][event].abort();
if (parent) {
parentData.dzConfig = dzConfig;
state.dropZones.set(dz, parent);
state.parentData.set(dz, parentData);
} else {
parentData.dzConfig = dzConfig;
state.parentData.set(dz, parentData);
}
for (const node of Array.from(parent.children)) {
if (isNode(node)) {
nodes.delete(node);
}
function deactivateDZ(dz) {
dz.removeEventListener("dragover", dzDragEvent);
dz.removeEventListener("drop", dzDragEvent);
const parentData = state.parentData.get(dz);
if (!parentData)
return;
}
function setNodeEvents(node) {
node.addEventListener("dragover", nodeDragEvent);
node.addEventListener("touchmove", nodeTouchEvent);
}
function dzDragEvent(e) {
e.stopPropagation();
e.preventDefault();
const targetData = dzDragTarget(e);
if (targetData === void 0)
return;
const transferData = dragTransfer();
if (transferData === void 0)
return;
const event = {
event: e,
...targetData,
...transferData
};
const dzConfig = event.targetParentData.dzConfig;
switch (event.event.type) {
case "dragover":
if (state.lastParent !== event.targetParent && event.draggedParent !== event.targetParent) {
handleTransfer(event, state);
state.enterCount++;
state.leftParent = true;
}
break;
case "drop":
if (event.targetParent !== event.draggedParent) {
dzConfig && dzConfig.drop ? dzConfig.drop(event, state, transferDrop) : transferDrop(event, state);
}
}
}
function nodeDragEvent(e) {
e.stopPropagation();
e.preventDefault();
const targetData = nodeDragTarget(e);
if (!targetData)
return;
const transferData = dragTransfer();
if (transferData === void 0)
return;
const event = {
event: e,
...targetData,
...transferData
};
if (!event)
return;
if (e.type === "dragover" && state.lastParent !== event.targetParent && event.targetParent !== event.draggedParent) {
const dzConfig = event.targetParentData?.dzConfig;
if (dzConfig && dzConfig.nodeDropZone === false)
return;
if (event.targetParent !== event.draggedParent) {
handleTransfer(event, state);
state.leftParent = true;
}
}
parents.delete(parent);
const parentObserver = parentObservers.get(parent.parentNode);
if (parentObserver)
parentObserver.disconnect();
parentObservers.delete(parent);
}
function setupParent(parent, parentData) {
const nodesObserver = new MutationObserver(nodesMutated);
nodesObserver.observe(parent, { childList: true });
if (parent.parentNode) {
const parentObserver = new MutationObserver(parentMutated);
parentObserver.observe(parent.parentNode, { childList: true });
if (!(parent.parentNode instanceof HTMLElement))
function nodeTouchEvent(e) {
if (e.type !== "touchmove")
return;
const targetData = getTouchTarget(e);
if (targetData === void 0)
return;
const transferData = touchTransfer();
if (transferData === void 0)
return;
if ("targetNode" in targetData) {
const nodeEvent = {
event: e,
...targetData,
...transferData
};
if (nodeEvent.targetParent === nodeEvent.draggedParent) {
transferReturn(nodeEvent, state);
return;
parentObservers.set(parent.parentNode, parentObserver);
}
if (nodeEvent.lastParent == nodeEvent.targetParent)
return;
handleTransfer(nodeEvent, state);
state.leftParent = true;
} else {
const parentEvent = {
event: e,
...targetData,
...transferData
};
if (parentEvent.lastParent !== parentEvent.draggedParent && parentEvent.draggedParent === parentEvent.targetParent) {
transferReturn(parentEvent, state);
state.leftParent = false;
} else if (parentEvent.targetParent !== parentEvent.draggedParent && parentEvent.lastParent !== parentEvent.targetParent) {
handleTransfer(parentEvent, state);
state.leftParent = true;
}
}
parents.set(parent, { ...parentData });
const abortControllers = addEvents(parent, {
dragover: parentEventData(parentData.config.handleDragoverParent),
touchOverParent: parentData.config.handleTouchOverParent
}
function handleTransfer(e, state2) {
if (!validateTransfer(e))
return;
const dzConfig = e.targetParentData.dzConfig;
dzConfig?.transfer ? dzConfig.transfer(e, state2, transferAction) : transferAction(e, state2);
state2.lastParent = e.targetParent;
}
function transferAction(e, state2) {
let lastParentValues = [...e.lastParentData.getValues(e.lastParent)];
const draggedNodeValues = state2.draggedNodes.map(
(x) => state2.nodeData.get(x)?.value
);
lastParentValues = [
...lastParentValues.filter((x) => !draggedNodeValues.includes(x))
];
const targetParentValues = [...e.targetParentData.getValues(e.targetParent)];
if ("targetNodeData" in e) {
targetParentValues.splice(
e.targetNodeData.index,
0,
...state2.draggedNodes.map((x) => state2.nodeData.get(x)?.value)
);
} else {
targetParentValues.push(...draggedNodeValues);
}
e.lastParentData.setValues(e.lastParent, lastParentValues);
e.targetParentData.setValues(e.targetParent, targetParentValues);
}
function transferDrop(_event, state2) {
if (!state2.draggedNode)
return;
}
// src/index.ts
function dragover(e) {
e.preventDefault();
}
function initParent(parent, getValues, setValues, config) {
if (!isBrowser)
return;
document.addEventListener("dragover", dragover);
if (!state.parents.has(parent)) {
state.parents.add(parent);
const nodesObserver = new MutationObserver(nodesMutated);
nodesObserver.observe(parent, { childList: true });
if (parent.parentNode) {
parent.addEventListener("dragenter", dzDragEvent2);
parent.addEventListener("dragover", dzDragEvent2);
parent.addEventListener("dragleave", dzDragEvent2);
parent.addEventListener("drop", dzDragEvent2);
const parentObserver = new MutationObserver(parentMutated);
parentObserver.observe(parent.parentNode, { childList: true });
if (!(parent.parentNode instanceof HTMLElement))
return;
state.parentObservers.set(parent.parentNode, parentObserver);
}
}
if (!config.setDraggable) {
config.setDraggable = setDraggable;
}
if (!config.removeDraggable) {
config.removeDraggable = removeDraggable;
}
const currentPlugins = state.parentData.get(parent)?.config?.plugins;
currentPlugins?.forEach((plugin) => {
if (plugin.tearDown) {
plugin.tearDown(parent, config);
}
});
parentData.abortControllers["mainParent"] = abortControllers;
state.parentData.set(parent, {
getValues,
setValues,
config,
enabledNodes: []
});
config.plugins?.forEach((plugin) => {
plugin.setUp(parent, config);
});
remap(parent);
}

@@ -307,11 +765,23 @@ function parentMutated(mutationList) {

continue;
const parentData = parents.get(removedNode);
const parentData = state.parentData.has(removedNode);
if (!parentData)
continue;
for (const node of Array.from(removedNode.childNodes)) {
if (isNode(node) && parentData.enabledNodes.includes(node)) {
nodes.delete(node);
state.parentData.delete(removedNode);
let hasParent = false;
for (const node of Array.from(mutation.target.childNodes)) {
if (state.parents.has(node)) {
state.parents.delete(removedNode);
hasParent = true;
break;
}
}
parents.delete(removedNode);
if (!hasParent) {
const parentObserver = state.parentObservers.get(
mutation.target
);
if (parentObserver) {
parentObserver.disconnect();
state.parentObservers.delete(mutation.target);
}
}
}

@@ -324,6 +794,6 @@ }

return;
remapNodes(parentEl);
remap(parentEl);
}
function remapNodes(parent) {
const parentData = parents.get(parent);
function remap(parent) {
const parentData = state.parentData.get(parent);
if (!parentData)

@@ -333,12 +803,18 @@ return;

const config = parentData.config;
let disabled = false;
for (let x = 0; x < parent.children.length; x++) {
const node = parent.children[x];
if (!isNode(node))
const child = parent.children[x];
if (!isNode(child, state))
continue;
const nodeData = nodes.get(node);
config.tearDownNode({ node, parent, nodeData, parentData });
if (!config.draggable || config.draggable && config.draggable(node))
enabledNodes.push(node);
if (config && config.draggable && !config.draggable(child)) {
removeDraggable(child);
} else if (config?.disabled) {
disabled = true;
removeDraggable(child);
} else if (parentData.config?.setDraggable) {
parentData.config.setDraggable(child);
enabledNodes.push(child);
}
}
if (enabledNodes.length !== parentData.getValues(parent).length && !config.disabled) {
if (enabledNodes.length !== parentData.getValues(parent).length && !disabled) {
console.warn(

@@ -351,474 +827,228 @@ "The number of enabled nodes does not match the number of values."

for (let x = 0; x < enabledNodes.length; x++) {
const node = enabledNodes[x];
const nodeData = {
const child = enabledNodes[x];
state.nodeData.set(child, {
value: values[x],
index: x,
privateClasses: [],
abortControllers: {}
};
config.setupNode({ node, parent, parentData, nodeData });
privateClasses: []
});
if (!state.draggedNode || !config)
continue;
let dropZoneClass;
const hasSelections = state.selectedValues.includes(values[x]);
if (!hasSelections && state.nodeData.get(state.draggedNode)?.value !== values[x])
continue;
const isTouch = !!state.touchedNode;
if (isTouch) {
dropZoneClass = hasSelections ? config?.touchSelectionDropZoneClass : config?.touchDropZoneClass;
} else {
dropZoneClass = hasSelections ? config?.selectionDropZoneClass : config?.dropZoneClass;
}
handleClass([child], dropZoneClass, state, false, true);
}
parents.set(parent, { ...parentData, enabledNodes });
state.parentData.set(parent, { ...parentData, enabledNodes });
}
function handleDragstart(data) {
if (!(data.e instanceof DragEvent))
return;
dragstart({
e: data.e,
targetData: data.targetData
});
}
function dragstartClasses(el, draggingClass, dropZoneClass) {
handleClass([el], draggingClass);
setTimeout(() => {
handleClass([el], draggingClass, true);
handleClass([el], dropZoneClass);
});
}
function initDrag(eventData) {
const dragState = setDragState(dragStateProps(eventData.targetData));
if (eventData.e.dataTransfer) {
eventData.e.dataTransfer.dropEffect = "move";
eventData.e.dataTransfer.effectAllowed = "move";
function removeDraggable(child) {
if (child.getAttribute("draggable") === "true") {
child.removeAttribute("draggable");
}
return dragState;
child.removeEventListener("dragstart", nodeDragEvent2);
child.removeEventListener("dragenter", nodeDragEvent2);
child.removeEventListener("dragend", nodeDragEvent2);
child.removeEventListener("dragover", nodeDragEvent2);
child.removeEventListener("touchstart", nodeTouchEvent2);
child.removeEventListener("touchmove", nodeTouchEvent2);
child.removeEventListener("touchend", nodeTouchEvent2);
}
function validateDragHandle(data) {
if (!(data.e instanceof DragEvent) && !(data.e instanceof TouchEvent))
return false;
const config = data.targetData.parent.data.config;
if (!config.dragHandle)
return true;
const dragHandles = data.targetData.node.el.querySelectorAll(
config.dragHandle
);
if (!dragHandles)
return false;
const coordinates = eventCoordinates(data.e);
const elFromPoint = config.root.elementFromPoint(
coordinates.x,
coordinates.y
);
if (!elFromPoint)
return false;
for (const handle of Array.from(dragHandles)) {
if (elFromPoint === handle || handle.contains(elFromPoint))
return true;
}
return false;
function setDraggable(child) {
child.setAttribute("draggable", "true");
child.addEventListener("dragstart", nodeDragEvent2);
child.addEventListener("dragenter", nodeDragEvent2);
child.addEventListener("dragend", nodeDragEvent2);
child.addEventListener("dragover", nodeDragEvent2);
child.addEventListener("touchstart", nodeTouchEvent2, { passive: false });
child.addEventListener("touchmove", nodeTouchEvent2, { passive: false });
child.addEventListener("touchend", nodeTouchEvent2);
}
function touchstart(data) {
if (!validateDragHandle(data)) {
data.e.preventDefault();
function nodeDragEvent2(e) {
e.stopPropagation();
const targetData = nodeDragTarget(e);
if (!targetData)
return;
if (e.type === "dragstart") {
state.lastParent = targetData.targetParent;
state.draggedNode = targetData.targetNode;
state.initialParent = targetData.targetParent;
if (!state.draggedNode)
return;
state.draggedNodes = [state.draggedNode];
state.initialParentValues = targetData.targetParentData.getValues(
targetData.targetParent
);
}
const touchState = initTouch(data);
handleTouchedNode(data, touchState);
handleLongTouch(data, touchState);
}
function dragstart(data) {
if (!validateDragHandle(data)) {
data.e.preventDefault();
const transferData = dragTransfer();
if (transferData === void 0)
return;
const eventData = {
event: e,
...targetData,
...transferData
};
const draggedConfig = eventData.draggedParentData.config;
switch (eventData.event.type) {
case "dragstart":
draggedConfig?.dragstart ? draggedConfig.dragstart(eventData, state, dragstart) : dragstart(eventData, state);
break;
case "dragover":
eventData.event.preventDefault();
if (eventData.targetNode === eventData.draggedNode) {
state.lastValue = eventData.targetNodeData.value;
return;
}
if (eventData.targetParent === eventData.lastParent) {
handleSort(eventData, state);
} else {
const config = eventData.targetParentData.config;
config?.transferReturn ? config.transferReturn(eventData, state, transferReturn) : transferReturn(eventData, state);
}
break;
case "dragend":
draggedConfig?.end ? draggedConfig.end(eventData, state, end) : end(eventData, state);
break;
default:
break;
}
const config = data.targetData.parent.data.config;
const dragState = initDrag(data);
dragstartClasses(
dragState.draggedNode.el,
config.draggingClass,
config.dropZoneClass
);
}
function handleTouchOverNode(e) {
if (!state)
function nodeTouchEvent2(e) {
e.stopPropagation();
if (state.touchMoving && e.type === "touchstart")
return;
if (e.detail.targetData.parent.el === state.lastParent.el)
sort(e.detail, state);
}
function setupNode(data) {
nodes.set(data.node, data.nodeData);
const config = data.parentData.config;
data.node.setAttribute("draggable", "true");
const abortControllers = addEvents(data.node, {
dragstart: nodeEventData(config.handleDragstart),
dragover: nodeEventData(config.handleDragoverNode),
dragend: nodeEventData(config.handleDragend),
touchstart: nodeEventData(config.handleTouchstart),
touchmove: nodeEventData(config.handleTouchmove),
touchend: nodeEventData(config.handleDragend),
touchOverNode: config.handleTouchOverNode
});
data.nodeData.abortControllers["mainNode"] = abortControllers;
data.parentData.config.plugins?.forEach((plugin) => {
plugin(data.parent)?.setupNode?.(data);
});
config.reapplyDragClasses(data.node, data.parentData);
}
function reapplyDragClasses(node, parentData) {
if (!state)
if (!isNode(e.currentTarget, state))
return;
const dropZoneClass = "touchedNode" in state ? parentData.config.touchDropZoneClass : parentData.config.dropZoneClass;
const nodeValue = nodes.get(node)?.value;
if (nodeValue !== state.draggedNode.data.value)
return;
handleClass([node], dropZoneClass);
}
function tearDownNode(data) {
data.node.setAttribute("draggable", "false");
if (data.nodeData?.abortControllers?.mainNode) {
for (const event of Object.keys(data.nodeData.abortControllers.mainNode)) {
data.nodeData.abortControllers.mainNode[event].abort();
}
}
data.parentData.config.plugins?.forEach((plugin) => {
plugin(data.parent)?.tearDownNode?.(data);
});
nodes.delete(data.node);
}
function handleDragend(eventData) {
if (!state)
return;
end(eventData, state);
resetState();
}
function end(_eventData, state2) {
if ("longTouchTimeout" in state2 && state2.longTouchTimeout)
clearTimeout(state2.longTouchTimeout);
const config = parents.get(state2.initialParent.el)?.config;
const root = config?.root || document;
const isTouch = !("touchedNode" in state2);
const dropZoneClass = isTouch ? config?.touchDropZoneClass : config?.dropZoneClass;
handleClass(
state2.draggedNodes.map((x) => x.el),
dropZoneClass,
true
);
if (dropZoneClass) {
const elsWithDropZoneClass = root.querySelectorAll(
`.${splitClass(dropZoneClass)}`
);
handleClass(Array.from(elsWithDropZoneClass), dropZoneClass, true);
}
if (config?.longTouchClass) {
handleClass(
state2.draggedNodes.map((x) => x.el),
state2.initialParent.data?.config?.longTouchClass,
true
);
}
if ("touchedNode" in state2) {
state2.touchedNode?.remove();
if (state2.scrollParent) {
state2.scrollParent.style.overflow = state2.scrollParentOverflow || "";
}
}
}
function handleTouchstart(eventData) {
if (!(eventData.e instanceof TouchEvent))
return;
touchstart({
e: eventData.e,
targetData: eventData.targetData
});
}
function initTouch(data) {
data.e.stopPropagation();
const clonedNode = data.targetData.node.el.cloneNode(true);
const rect = data.targetData.node.el.getBoundingClientRect();
const touchState = setTouchState(
setDragState(dragStateProps(data.targetData)),
{
touchStartLeft: data.e.touches[0].clientX - rect.left,
touchStartTop: data.e.touches[0].clientY - rect.top,
touchedNode: clonedNode
}
);
return touchState;
}
function handleTouchedNode(data, touchState) {
const rect = data.targetData.node.el.getBoundingClientRect();
touchState.touchedNode.style.cssText = `
width: ${rect.width}px;
position: absolute;
pointer-events: none;
top: -9999px;
z-index: 999999;
display: none;
`;
document.body.append(touchState.touchedNode);
copyNodeStyle(data.targetData.node.el, touchState.touchedNode);
touchState.touchedNode.style.display = "none";
}
function handleLongTouch(data, touchState) {
const config = data.targetData.parent.data.config;
if (!config.longTouch)
return;
touchState.longTouchTimeout = setTimeout(() => {
if (!touchState)
if (e.type === "touchstart") {
state.draggedNode = e.currentTarget;
const clone = e.currentTarget.cloneNode(true);
if (!(clone instanceof HTMLElement))
return;
touchState.longTouch = true;
const parentScroll = getScrollParent(touchState.draggedNode.el);
if (parentScroll) {
touchState.scrollParent = parentScroll;
touchState.scrollParentOverflow = parentScroll.style.overflow;
parentScroll.style.overflow = "hidden";
}
if (config.longTouchClass && data.e.cancelable)
handleClass(
touchState.draggedNodes.map((x) => x.el),
config.longTouchClass
);
data.e.preventDefault();
document.addEventListener("contextmenu", function(e) {
e.preventDefault();
});
}, config.longTouchTimeout || 200);
}
function handleTouchmove(eventData) {
if (!state || !("touchedNode" in state))
return;
touchmove(eventData, state);
}
function touchmoveClasses(touchState, config) {
if (config.longTouchClass)
handleClass(
touchState.draggedNodes.map((x) => x.el),
config?.longTouchClass,
true
);
if (config.touchDraggingClass)
handleClass([touchState.touchedNode], config.touchDraggingClass);
if (config.touchDropZoneClass)
handleClass(
touchState.draggedNodes.map((x) => x.el),
config.touchDropZoneClass
);
}
function moveTouchedNode(data, touchState) {
touchState.touchedNode.style.display = "block";
const x = data.e.touches[0].clientX + window.scrollX;
const y = data.e.touches[0].clientY + window.scrollY;
const windowHeight = window.innerHeight + window.scrollY;
if (y > windowHeight - 50) {
window.scrollBy(0, 10);
} else if (y < window.scrollY + 50) {
window.scrollBy(0, -10);
const parentData = state.parentData.get(e.currentTarget.parentNode);
if (!parentData)
return;
const parentValues = parentData.getValues(e.currentTarget.parentNode);
if (!parentValues)
return;
state.touchedNode = clone;
state.draggedNodes = [state.draggedNode];
state.lastParent = e.currentTarget.parentNode;
state.initialParent = e.currentTarget.parentNode;
state.initialParentValues = parentValues;
}
const touchStartLeft = touchState.touchStartLeft ?? 0;
const touchStartTop = touchState.touchStartTop ?? 0;
touchState.touchedNode.style.left = `${x - touchStartLeft}px`;
touchState.touchedNode.style.top = `${y - touchStartTop}px`;
}
function touchmove(data, touchState) {
if (data.e.cancelable)
data.e.preventDefault();
const config = data.targetData.parent.data.config;
if (config.longTouch && !touchState.longTouch) {
clearTimeout(touchState.longTouchTimeout);
if (!state.touchedNode)
return;
}
if (touchState.touchMoving !== true) {
touchState.touchMoving = true;
touchmoveClasses(touchState, config);
}
moveTouchedNode(data, touchState);
const elFromPoint = getElFromPoint(data);
if (!elFromPoint)
const transferData = touchTransfer();
if (transferData === void 0)
return;
if ("node" in elFromPoint && elFromPoint.node.el === touchState.draggedNodes[0].el) {
touchState.lastValue = data.targetData.node.data.value;
return;
}
const touchMoveEventData = {
e: data.e,
targetData: elFromPoint
const config = transferData.draggedParentData.config;
const touchEvent = {
event: e,
...transferData
};
if ("node" in elFromPoint) {
elFromPoint.node.el.dispatchEvent(
new CustomEvent("touchOverNode", {
detail: touchMoveEventData
})
);
} else {
elFromPoint.parent.el.dispatchEvent(
new CustomEvent("touchOverParent", {
detail: touchMoveEventData
})
);
const draggedConfig = touchEvent.draggedParentData.config;
let touchTarget;
switch (e.type) {
case "touchstart":
config?.touchstart ? config.touchstart(touchEvent, state, touchstart) : touchstart(touchEvent, state);
break;
case "touchmove":
config?.touchmove ? config.touchmove(touchEvent, state, touchmove) : touchmove(touchEvent, state);
if (config?.longTouch && !state.longTouch)
return;
touchTarget = getTouchTarget(e);
if (!touchTarget)
return;
if ("targetNode" in touchTarget) {
const event = {
event: e,
...touchTarget,
...transferData
};
if (event.targetNode === event.draggedNode) {
state.lastValue = event.targetNodeData.value;
return;
}
if (event.targetParent === event.lastParent) {
handleSort(event, state);
}
}
break;
case "touchend":
state.touchMoving = false;
draggedConfig?.end ? draggedConfig.end(touchEvent, state, end) : end(touchEvent, state);
break;
}
}
function handleDragoverNode(data) {
if (!state)
function dzDragEvent2(e) {
e.stopPropagation();
e.preventDefault();
const targetData = dzDragTarget(e);
if (!targetData)
return;
dragoverNode(data, state);
}
function handleDragoverParent(eventData) {
if (!state)
const transferData = dragTransfer();
if (transferData === void 0)
return;
transfer(eventData, state);
}
function handleTouchOverParent(e) {
if (!state)
return;
transfer(e.detail, state);
}
function validateTransfer(data, state2) {
if (data.targetData.parent.el === state2.lastParent.el)
return false;
const targetConfig = data.targetData.parent.data.config;
if (targetConfig.dropZone === false)
return false;
const initialParentConfig = state2.initialParent.data.config;
if (targetConfig.accepts && !targetConfig.accepts(
data.targetData.parent,
state2.initialParent,
state2.lastParent,
state2
)) {
return false;
} else if (targetConfig.group && targetConfig.group !== initialParentConfig.group) {
return false;
const event = {
event: e,
...targetData,
...transferData
};
const dzConfig = event.targetParentData.dzConfig;
switch (event.event.type) {
case "dragover":
if (event.targetParent === event.draggedParent && state.leftParent) {
event.targetParentData.config?.transferReturn ? event.targetParentData.config.transferReturn(
event,
state,
transferReturn
) : transferReturn(event, state);
state.leftParent = false;
}
break;
case "dragleave":
event.event.preventDefault();
if (!event.targetParent.contains(
document.elementFromPoint(event.event.clientX, event.event.clientY)
)) {
event.targetParentData.config?.dragleave ? event.targetParentData.config.dragleave(event, state, dragleave) : dragleave(event, state);
state.leftParent = true;
} else if (state.lastParent !== event.targetParent && state.lastParent === event.draggedParent) {
}
break;
case "drop":
if (event.targetParent == event.draggedParent) {
dzConfig && dzConfig.drop ? dzConfig.drop(event, state, drop) : drop(event, state);
}
}
return true;
}
function dragoverNode(eventData, dragState) {
eventData.e.preventDefault();
eventData.targetData.parent.el === dragState.lastParent?.el ? sort(eventData, dragState) : transfer(eventData, dragState);
}
function validateSort(data, dragState, x, y) {
if (!dragState)
return false;
if (data.targetData.parent.el !== dragState.lastParent?.el || data.targetData.node.data.value === dragState.lastValue)
return false;
if (dragState.lastValue === data.targetData.node.data.value) {
if (dragState.direction !== 1)
return false;
if (x >= dragState.lastCoordinates.y - 20 && y >= dragState.lastCoordinates.x - data.targetData.node.el.getBoundingClientRect().width / 20)
return false;
}
return true;
}
function sort(data, state2) {
const { x, y } = eventCoordinates(data.e);
if (!validateSort(data, state2, x, y))
return;
data.targetData.parent.data.config.performSort(state2, data);
state2.lastValue = data.targetData.node.data.value;
state2.lastCoordinates = { x, y };
state2.direction = data.targetData.node.data.index > state2.draggedNode.data.index ? 1 : -1;
}
function nodeEventData(callback) {
function nodeTargetData(node) {
const nodeData = nodes.get(node);
const parent = node.parentNode || state?.lastParent?.el;
if (!nodeData)
function handleSort(event, state2) {
const clientX = "touchedNode" in event ? event.event.touches[0].clientX : event.event.clientX;
const clientY = "touchedNode" in event ? event.event.touches[0].clientY : event.event.clientY;
if (state2.lastValue === event.targetNodeData.value) {
if (state2.direction !== 1) {
return;
const parentData = parents.get(parent);
if (!parentData)
}
if (clientY >= state2.lastCoordinates.y - 20 && clientX >= state2.lastCoordinates.x - event.targetNode.getBoundingClientRect().width / 20) {
return;
return {
node: {
el: node,
data: nodeData
},
parent: {
el: parent,
data: parentData
}
};
}
}
return (e) => {
const targetData = nodeTargetData(e.currentTarget);
if (!targetData)
return;
return callback({
e,
targetData
});
};
}
function performTransfer(state2, data) {
const draggedValues = dragValues(state2);
const lastParentValues = parentValues(
state2.lastParent.el,
state2.lastParent.data
).filter((x) => !draggedValues.includes(x));
const targetParentValues = parentValues(
data.targetData.parent.el,
data.targetData.parent.data
);
"node" in data.targetData ? targetParentValues.splice(
data.targetData.node.data.index,
0,
...draggedValues
) : targetParentValues.push(...draggedValues);
setParentValues(state2.lastParent.el, state2.lastParent.data, lastParentValues);
setParentValues(
data.targetData.parent.el,
data.targetData.parent.data,
targetParentValues
);
}
function transfer(data, state2) {
if (!validateTransfer(data, state2))
if (!validateSort(event)) {
return;
data.targetData.parent.data.config.performTransfer(state2, data);
state2.lastParent = data.targetData.parent;
}
function parentEventData(callback) {
function parentTargetData(parent) {
const parentData = parents.get(parent);
if (!parentData)
return;
return {
parent: {
el: parent,
data: parentData
}
};
}
return (e) => {
const targetData = parentTargetData(e.currentTarget);
if (!targetData)
return;
return callback({
e,
targetData
});
};
state2.lastValue = event.targetNodeData.value;
state2.lastCoordinates = { x: clientX, y: clientY };
state2.direction = event.targetNodeData.index > event.draggedNodeData.index ? 1 : -1;
event.targetParentData.config?.sort ? event.targetParentData.config.sort(event, state2, sort) : sort(event, state2);
}
export {
dragStateProps,
dragValues,
dragstart,
dragstartClasses,
end,
handleLongTouch,
handleTouchOverNode,
handleTouchOverParent,
handleTouchedNode,
initDrag,
initParent,
initTouch,
nodeEventData,
nodes,
parentEventData,
parentObservers,
parentValues,
parents,
performSort,
performTransfer,
remapNodes,
resetState,
setDragState,
setParentValues,
setTouchState,
setupNode,
sort,
state,
tearDownNode,
transfer,
validateSort,
validateTransfer
initParent as default,
nodeDragEvent2 as nodeDragEvent,
setUpDropZone
};
//# sourceMappingURL=index.js.map
{
"name": "@formkit/drag-and-drop",
"version": "0.0.9",
"version": "0.0.10",
"description": "Drag and drop package.",

@@ -5,0 +5,0 @@ "main": "./dist/index.js",

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc