dnd-block-tree 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -7
- package/dist/index.d.mts +75 -59
- package/dist/index.d.ts +75 -59
- package/dist/index.js +92 -27
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +93 -29
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useDroppable, useSensors, useSensor, PointerSensor, TouchSensor, KeyboardSensor, DragOverlay as DragOverlay$1, DndContext
|
|
1
|
+
import { useDroppable, useDraggable, useSensors, useSensor, PointerSensor, TouchSensor, KeyboardSensor, DragOverlay as DragOverlay$1, DndContext } from '@dnd-kit/core';
|
|
2
2
|
import { memo, useCallback, useEffect, Fragment, useMemo, useRef, useReducer, useState, createContext, useLayoutEffect, useContext } from 'react';
|
|
3
3
|
import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
|
|
4
4
|
|
|
@@ -13,6 +13,12 @@ function extractBlockId(zoneId) {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
// src/core/collision.ts
|
|
16
|
+
function collisionValue(d) {
|
|
17
|
+
return d.data.value;
|
|
18
|
+
}
|
|
19
|
+
function collisionLeft(d) {
|
|
20
|
+
return d.data.left;
|
|
21
|
+
}
|
|
16
22
|
function computeCollisionScores(droppableContainers, collisionRect, snapshotRects) {
|
|
17
23
|
const pointerX = collisionRect.left + collisionRect.width / 2;
|
|
18
24
|
const pointerY = collisionRect.top + collisionRect.height / 2;
|
|
@@ -41,11 +47,7 @@ function computeCollisionScores(droppableContainers, collisionRect, snapshotRect
|
|
|
41
47
|
}
|
|
42
48
|
};
|
|
43
49
|
}).filter((c) => c !== null);
|
|
44
|
-
candidates.sort((a, b) =>
|
|
45
|
-
const aValue = a.data.value;
|
|
46
|
-
const bValue = b.data.value;
|
|
47
|
-
return aValue - bValue;
|
|
48
|
-
});
|
|
50
|
+
candidates.sort((a, b) => collisionValue(a) - collisionValue(b));
|
|
49
51
|
return candidates;
|
|
50
52
|
}
|
|
51
53
|
var weightedVerticalCollision = ({
|
|
@@ -66,13 +68,13 @@ function createStickyCollision(threshold = 15, snapshotRef) {
|
|
|
66
68
|
const candidates = computeCollisionScores(droppableContainers, collisionRect, snapshotRef?.current);
|
|
67
69
|
if (candidates.length === 0) return [];
|
|
68
70
|
const bestCandidate = candidates[0];
|
|
69
|
-
const bestScore = bestCandidate
|
|
71
|
+
const bestScore = collisionValue(bestCandidate);
|
|
70
72
|
if (currentZoneId !== null) {
|
|
71
73
|
const currentCandidate = candidates.find((c) => c.id === currentZoneId);
|
|
72
74
|
if (currentCandidate) {
|
|
73
|
-
const currentScore = currentCandidate
|
|
74
|
-
const currentLeft = currentCandidate
|
|
75
|
-
const bestLeft = bestCandidate
|
|
75
|
+
const currentScore = collisionValue(currentCandidate);
|
|
76
|
+
const currentLeft = collisionLeft(currentCandidate);
|
|
77
|
+
const bestLeft = collisionLeft(bestCandidate);
|
|
76
78
|
const crossDepth = Math.abs(currentLeft - bestLeft) > 20;
|
|
77
79
|
const effectiveThreshold = crossDepth ? threshold * 0.25 : threshold;
|
|
78
80
|
if (currentScore - bestScore < effectiveThreshold) {
|
|
@@ -111,11 +113,7 @@ var closestCenterCollision = ({
|
|
|
111
113
|
}
|
|
112
114
|
};
|
|
113
115
|
}).filter((c) => c !== null);
|
|
114
|
-
candidates.sort((a, b) =>
|
|
115
|
-
const aValue = a.data.value;
|
|
116
|
-
const bValue = b.data.value;
|
|
117
|
-
return aValue - bValue;
|
|
118
|
-
});
|
|
116
|
+
candidates.sort((a, b) => collisionValue(a) - collisionValue(b));
|
|
119
117
|
return candidates.slice(0, 1);
|
|
120
118
|
};
|
|
121
119
|
var DEFAULT_ACTIVATION_DISTANCE = 8;
|
|
@@ -246,7 +244,12 @@ function DraggableBlock({
|
|
|
246
244
|
disabled,
|
|
247
245
|
focusedId,
|
|
248
246
|
isSelected,
|
|
249
|
-
onBlockClick
|
|
247
|
+
onBlockClick,
|
|
248
|
+
isContainer,
|
|
249
|
+
isExpanded,
|
|
250
|
+
depth,
|
|
251
|
+
posInSet,
|
|
252
|
+
setSize
|
|
250
253
|
}) {
|
|
251
254
|
const { attributes, listeners, setNodeRef, isDragging } = useDraggable({
|
|
252
255
|
id: block.id,
|
|
@@ -265,11 +268,16 @@ function DraggableBlock({
|
|
|
265
268
|
"data-selected": isSelected || void 0,
|
|
266
269
|
style: { touchAction: "none", minWidth: 0, outline: "none" },
|
|
267
270
|
role: "treeitem",
|
|
271
|
+
"aria-level": depth + 1,
|
|
272
|
+
"aria-posinset": posInSet,
|
|
273
|
+
"aria-setsize": setSize,
|
|
274
|
+
"aria-expanded": isContainer ? isExpanded : void 0,
|
|
275
|
+
"aria-selected": isSelected ?? void 0,
|
|
268
276
|
children: children({ isDragging })
|
|
269
277
|
}
|
|
270
278
|
);
|
|
271
279
|
}
|
|
272
|
-
function
|
|
280
|
+
function TreeRendererInner({
|
|
273
281
|
blocks,
|
|
274
282
|
blocksByParent,
|
|
275
283
|
parentId,
|
|
@@ -339,6 +347,11 @@ function TreeRenderer({
|
|
|
339
347
|
focusedId,
|
|
340
348
|
isSelected: selectedIds?.has(block.id),
|
|
341
349
|
onBlockClick,
|
|
350
|
+
isContainer,
|
|
351
|
+
isExpanded,
|
|
352
|
+
depth,
|
|
353
|
+
posInSet: originalIndex + 1,
|
|
354
|
+
setSize: items.length,
|
|
342
355
|
children: ({ isDragging }) => {
|
|
343
356
|
if (isContainer) {
|
|
344
357
|
const expandStyle = animation?.expandDuration ? {
|
|
@@ -423,6 +436,7 @@ function TreeRenderer({
|
|
|
423
436
|
)
|
|
424
437
|
] });
|
|
425
438
|
}
|
|
439
|
+
var TreeRenderer = memo(TreeRendererInner);
|
|
426
440
|
function DragOverlay({
|
|
427
441
|
activeBlock,
|
|
428
442
|
children,
|
|
@@ -655,6 +669,9 @@ function reparentBlockIndex(state, activeId, targetZone, containerTypes = [], or
|
|
|
655
669
|
if (parentDepth + subtreeDepth > maxDepth) return state;
|
|
656
670
|
}
|
|
657
671
|
if (dragged.id === zoneTargetId) return state;
|
|
672
|
+
if (newParentId !== null && getDescendantIds(state, dragged.id).has(newParentId)) {
|
|
673
|
+
return state;
|
|
674
|
+
}
|
|
658
675
|
const oldList = byParent.get(oldParentId) ?? [];
|
|
659
676
|
const currentIndexInOldParent = oldList.indexOf(dragged.id);
|
|
660
677
|
const preNewList = byParent.get(newParentId) ?? [];
|
|
@@ -707,19 +724,24 @@ function reparentBlockIndex(state, activeId, targetZone, containerTypes = [], or
|
|
|
707
724
|
function getBlockDepth(index, blockId) {
|
|
708
725
|
let depth = 0;
|
|
709
726
|
let current = blockId;
|
|
727
|
+
const visited = /* @__PURE__ */ new Set();
|
|
710
728
|
while (current !== null) {
|
|
729
|
+
if (visited.has(current)) break;
|
|
730
|
+
visited.add(current);
|
|
711
731
|
depth++;
|
|
712
732
|
const block = index.byId.get(current);
|
|
713
733
|
current = block?.parentId ?? null;
|
|
714
734
|
}
|
|
715
735
|
return depth;
|
|
716
736
|
}
|
|
717
|
-
function getSubtreeDepth(index, blockId) {
|
|
737
|
+
function getSubtreeDepth(index, blockId, visited = /* @__PURE__ */ new Set()) {
|
|
738
|
+
if (visited.has(blockId)) return 0;
|
|
739
|
+
visited.add(blockId);
|
|
718
740
|
const children = index.byParent.get(blockId) ?? [];
|
|
719
741
|
if (children.length === 0) return 1;
|
|
720
742
|
let max = 0;
|
|
721
743
|
for (const childId of children) {
|
|
722
|
-
max = Math.max(max, getSubtreeDepth(index, childId));
|
|
744
|
+
max = Math.max(max, getSubtreeDepth(index, childId, visited));
|
|
723
745
|
}
|
|
724
746
|
return 1 + max;
|
|
725
747
|
}
|
|
@@ -746,6 +768,40 @@ function reparentMultipleBlocks(state, blockIds, targetZone, containerTypes = []
|
|
|
746
768
|
}
|
|
747
769
|
return result;
|
|
748
770
|
}
|
|
771
|
+
function validateBlockTree(index) {
|
|
772
|
+
const issues = [];
|
|
773
|
+
for (const [id] of index.byId) {
|
|
774
|
+
const visited = /* @__PURE__ */ new Set();
|
|
775
|
+
let current = id;
|
|
776
|
+
while (current !== null) {
|
|
777
|
+
if (visited.has(current)) {
|
|
778
|
+
issues.push(`Cycle detected: block "${id}" has a circular parentId chain`);
|
|
779
|
+
break;
|
|
780
|
+
}
|
|
781
|
+
visited.add(current);
|
|
782
|
+
const block = index.byId.get(current);
|
|
783
|
+
current = block?.parentId ?? null;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
for (const [id, block] of index.byId) {
|
|
787
|
+
if (block.parentId !== null && !index.byId.has(block.parentId)) {
|
|
788
|
+
issues.push(`Orphan: block "${id}" references non-existent parent "${block.parentId}"`);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
for (const [parentId, childIds] of index.byParent) {
|
|
792
|
+
for (const childId of childIds) {
|
|
793
|
+
if (!index.byId.has(childId)) {
|
|
794
|
+
issues.push(`Stale ref: byParent key "${parentId}" lists non-existent block "${childId}"`);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
if (issues.length > 0) {
|
|
799
|
+
for (const issue of issues) {
|
|
800
|
+
console.warn(`[dnd-block-tree] ${issue}`);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
return { valid: issues.length === 0, issues };
|
|
804
|
+
}
|
|
749
805
|
function deleteBlockAndDescendants(state, id) {
|
|
750
806
|
const byId = cloneMap(state.byId);
|
|
751
807
|
const byParent = cloneParentMap(state.byParent);
|
|
@@ -768,7 +824,12 @@ function getBlockPosition(blocks, blockId) {
|
|
|
768
824
|
}
|
|
769
825
|
function computeInitialExpanded(blocks, containerTypes, initialExpanded) {
|
|
770
826
|
if (initialExpanded === "none") {
|
|
771
|
-
|
|
827
|
+
const expandedMap2 = {};
|
|
828
|
+
const containers2 = blocks.filter((b) => containerTypes.includes(b.type));
|
|
829
|
+
for (const container of containers2) {
|
|
830
|
+
expandedMap2[container.id] = false;
|
|
831
|
+
}
|
|
832
|
+
return expandedMap2;
|
|
772
833
|
}
|
|
773
834
|
const expandedMap = {};
|
|
774
835
|
const containers = blocks.filter((b) => containerTypes.includes(b.type));
|
|
@@ -885,14 +946,17 @@ function BlockTree({
|
|
|
885
946
|
onDragMove?.(event);
|
|
886
947
|
}, 50)
|
|
887
948
|
).current;
|
|
888
|
-
const originalIndex =
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
)
|
|
895
|
-
|
|
949
|
+
const originalIndex = useMemo(
|
|
950
|
+
() => computeNormalizedIndex(blocks, orderingStrategy),
|
|
951
|
+
[blocks, orderingStrategy]
|
|
952
|
+
);
|
|
953
|
+
const blocksByParent = useMemo(() => {
|
|
954
|
+
const map = /* @__PURE__ */ new Map();
|
|
955
|
+
for (const [parentId, ids] of originalIndex.byParent.entries()) {
|
|
956
|
+
map.set(parentId, ids.map((id) => originalIndex.byId.get(id)).filter(Boolean));
|
|
957
|
+
}
|
|
958
|
+
return map;
|
|
959
|
+
}, [originalIndex]);
|
|
896
960
|
const focusedIdRef = useRef(null);
|
|
897
961
|
const rootRef = useRef(null);
|
|
898
962
|
const lastClickedIdRef = useRef(null);
|
|
@@ -1917,6 +1981,6 @@ function nestedToFlat(nested) {
|
|
|
1917
1981
|
return result;
|
|
1918
1982
|
}
|
|
1919
1983
|
|
|
1920
|
-
export { BlockTree, BlockTreeSSR, DragOverlay, DropZone, TreeRenderer, buildOrderedBlocks, cloneMap, cloneParentMap, closestCenterCollision, compareFractionalKeys, computeNormalizedIndex, createBlockState, createStickyCollision, createTreeState, debounce, deleteBlockAndDescendants, extractBlockId, extractUUID, flatToNested, generateId, generateInitialKeys, generateKeyBetween, generateNKeysBetween, getBlockDepth, getDescendantIds, getDropZoneType, getSensorConfig, getSubtreeDepth, initFractionalOrder, nestedToFlat, reparentBlockIndex, reparentMultipleBlocks, triggerHaptic, useBlockHistory, useConfiguredSensors, useLayoutAnimation, useVirtualTree, weightedVerticalCollision };
|
|
1984
|
+
export { BlockTree, BlockTreeSSR, DragOverlay, DropZone, TreeRenderer, buildOrderedBlocks, cloneMap, cloneParentMap, closestCenterCollision, compareFractionalKeys, computeNormalizedIndex, createBlockState, createStickyCollision, createTreeState, debounce, deleteBlockAndDescendants, extractBlockId, extractUUID, flatToNested, generateId, generateInitialKeys, generateKeyBetween, generateNKeysBetween, getBlockDepth, getDescendantIds, getDropZoneType, getSensorConfig, getSubtreeDepth, initFractionalOrder, nestedToFlat, reparentBlockIndex, reparentMultipleBlocks, triggerHaptic, useBlockHistory, useConfiguredSensors, useLayoutAnimation, useVirtualTree, validateBlockTree, weightedVerticalCollision };
|
|
1921
1985
|
//# sourceMappingURL=index.mjs.map
|
|
1922
1986
|
//# sourceMappingURL=index.mjs.map
|