dnd-block-tree 1.0.0 → 1.2.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 +111 -59
- package/dist/index.d.ts +111 -59
- package/dist/index.js +831 -27
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +830 -29
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -15,6 +15,12 @@ function extractBlockId(zoneId) {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
// src/core/collision.ts
|
|
18
|
+
function collisionValue(d) {
|
|
19
|
+
return d.data.value;
|
|
20
|
+
}
|
|
21
|
+
function collisionLeft(d) {
|
|
22
|
+
return d.data.left;
|
|
23
|
+
}
|
|
18
24
|
function computeCollisionScores(droppableContainers, collisionRect, snapshotRects) {
|
|
19
25
|
const pointerX = collisionRect.left + collisionRect.width / 2;
|
|
20
26
|
const pointerY = collisionRect.top + collisionRect.height / 2;
|
|
@@ -43,11 +49,7 @@ function computeCollisionScores(droppableContainers, collisionRect, snapshotRect
|
|
|
43
49
|
}
|
|
44
50
|
};
|
|
45
51
|
}).filter((c) => c !== null);
|
|
46
|
-
candidates.sort((a, b) =>
|
|
47
|
-
const aValue = a.data.value;
|
|
48
|
-
const bValue = b.data.value;
|
|
49
|
-
return aValue - bValue;
|
|
50
|
-
});
|
|
52
|
+
candidates.sort((a, b) => collisionValue(a) - collisionValue(b));
|
|
51
53
|
return candidates;
|
|
52
54
|
}
|
|
53
55
|
var weightedVerticalCollision = ({
|
|
@@ -68,13 +70,13 @@ function createStickyCollision(threshold = 15, snapshotRef) {
|
|
|
68
70
|
const candidates = computeCollisionScores(droppableContainers, collisionRect, snapshotRef?.current);
|
|
69
71
|
if (candidates.length === 0) return [];
|
|
70
72
|
const bestCandidate = candidates[0];
|
|
71
|
-
const bestScore = bestCandidate
|
|
73
|
+
const bestScore = collisionValue(bestCandidate);
|
|
72
74
|
if (currentZoneId !== null) {
|
|
73
75
|
const currentCandidate = candidates.find((c) => c.id === currentZoneId);
|
|
74
76
|
if (currentCandidate) {
|
|
75
|
-
const currentScore = currentCandidate
|
|
76
|
-
const currentLeft = currentCandidate
|
|
77
|
-
const bestLeft = bestCandidate
|
|
77
|
+
const currentScore = collisionValue(currentCandidate);
|
|
78
|
+
const currentLeft = collisionLeft(currentCandidate);
|
|
79
|
+
const bestLeft = collisionLeft(bestCandidate);
|
|
78
80
|
const crossDepth = Math.abs(currentLeft - bestLeft) > 20;
|
|
79
81
|
const effectiveThreshold = crossDepth ? threshold * 0.25 : threshold;
|
|
80
82
|
if (currentScore - bestScore < effectiveThreshold) {
|
|
@@ -113,11 +115,7 @@ var closestCenterCollision = ({
|
|
|
113
115
|
}
|
|
114
116
|
};
|
|
115
117
|
}).filter((c) => c !== null);
|
|
116
|
-
candidates.sort((a, b) =>
|
|
117
|
-
const aValue = a.data.value;
|
|
118
|
-
const bValue = b.data.value;
|
|
119
|
-
return aValue - bValue;
|
|
120
|
-
});
|
|
118
|
+
candidates.sort((a, b) => collisionValue(a) - collisionValue(b));
|
|
121
119
|
return candidates.slice(0, 1);
|
|
122
120
|
};
|
|
123
121
|
var DEFAULT_ACTIVATION_DISTANCE = 8;
|
|
@@ -248,7 +246,12 @@ function DraggableBlock({
|
|
|
248
246
|
disabled,
|
|
249
247
|
focusedId,
|
|
250
248
|
isSelected,
|
|
251
|
-
onBlockClick
|
|
249
|
+
onBlockClick,
|
|
250
|
+
isContainer,
|
|
251
|
+
isExpanded,
|
|
252
|
+
depth,
|
|
253
|
+
posInSet,
|
|
254
|
+
setSize
|
|
252
255
|
}) {
|
|
253
256
|
const { attributes, listeners, setNodeRef, isDragging } = core.useDraggable({
|
|
254
257
|
id: block.id,
|
|
@@ -267,11 +270,16 @@ function DraggableBlock({
|
|
|
267
270
|
"data-selected": isSelected || void 0,
|
|
268
271
|
style: { touchAction: "none", minWidth: 0, outline: "none" },
|
|
269
272
|
role: "treeitem",
|
|
273
|
+
"aria-level": depth + 1,
|
|
274
|
+
"aria-posinset": posInSet,
|
|
275
|
+
"aria-setsize": setSize,
|
|
276
|
+
"aria-expanded": isContainer ? isExpanded : void 0,
|
|
277
|
+
"aria-selected": isSelected ?? void 0,
|
|
270
278
|
children: children({ isDragging })
|
|
271
279
|
}
|
|
272
280
|
);
|
|
273
281
|
}
|
|
274
|
-
function
|
|
282
|
+
function TreeRendererInner({
|
|
275
283
|
blocks,
|
|
276
284
|
blocksByParent,
|
|
277
285
|
parentId,
|
|
@@ -341,6 +349,11 @@ function TreeRenderer({
|
|
|
341
349
|
focusedId,
|
|
342
350
|
isSelected: selectedIds?.has(block.id),
|
|
343
351
|
onBlockClick,
|
|
352
|
+
isContainer,
|
|
353
|
+
isExpanded,
|
|
354
|
+
depth,
|
|
355
|
+
posInSet: originalIndex + 1,
|
|
356
|
+
setSize: items.length,
|
|
344
357
|
children: ({ isDragging }) => {
|
|
345
358
|
if (isContainer) {
|
|
346
359
|
const expandStyle = animation?.expandDuration ? {
|
|
@@ -425,6 +438,7 @@ function TreeRenderer({
|
|
|
425
438
|
)
|
|
426
439
|
] });
|
|
427
440
|
}
|
|
441
|
+
var TreeRenderer = react.memo(TreeRendererInner);
|
|
428
442
|
function DragOverlay({
|
|
429
443
|
activeBlock,
|
|
430
444
|
children,
|
|
@@ -657,6 +671,9 @@ function reparentBlockIndex(state, activeId, targetZone, containerTypes = [], or
|
|
|
657
671
|
if (parentDepth + subtreeDepth > maxDepth) return state;
|
|
658
672
|
}
|
|
659
673
|
if (dragged.id === zoneTargetId) return state;
|
|
674
|
+
if (newParentId !== null && getDescendantIds(state, dragged.id).has(newParentId)) {
|
|
675
|
+
return state;
|
|
676
|
+
}
|
|
660
677
|
const oldList = byParent.get(oldParentId) ?? [];
|
|
661
678
|
const currentIndexInOldParent = oldList.indexOf(dragged.id);
|
|
662
679
|
const preNewList = byParent.get(newParentId) ?? [];
|
|
@@ -709,19 +726,24 @@ function reparentBlockIndex(state, activeId, targetZone, containerTypes = [], or
|
|
|
709
726
|
function getBlockDepth(index, blockId) {
|
|
710
727
|
let depth = 0;
|
|
711
728
|
let current = blockId;
|
|
729
|
+
const visited = /* @__PURE__ */ new Set();
|
|
712
730
|
while (current !== null) {
|
|
731
|
+
if (visited.has(current)) break;
|
|
732
|
+
visited.add(current);
|
|
713
733
|
depth++;
|
|
714
734
|
const block = index.byId.get(current);
|
|
715
735
|
current = block?.parentId ?? null;
|
|
716
736
|
}
|
|
717
737
|
return depth;
|
|
718
738
|
}
|
|
719
|
-
function getSubtreeDepth(index, blockId) {
|
|
739
|
+
function getSubtreeDepth(index, blockId, visited = /* @__PURE__ */ new Set()) {
|
|
740
|
+
if (visited.has(blockId)) return 0;
|
|
741
|
+
visited.add(blockId);
|
|
720
742
|
const children = index.byParent.get(blockId) ?? [];
|
|
721
743
|
if (children.length === 0) return 1;
|
|
722
744
|
let max = 0;
|
|
723
745
|
for (const childId of children) {
|
|
724
|
-
max = Math.max(max, getSubtreeDepth(index, childId));
|
|
746
|
+
max = Math.max(max, getSubtreeDepth(index, childId, visited));
|
|
725
747
|
}
|
|
726
748
|
return 1 + max;
|
|
727
749
|
}
|
|
@@ -748,6 +770,40 @@ function reparentMultipleBlocks(state, blockIds, targetZone, containerTypes = []
|
|
|
748
770
|
}
|
|
749
771
|
return result;
|
|
750
772
|
}
|
|
773
|
+
function validateBlockTree(index) {
|
|
774
|
+
const issues = [];
|
|
775
|
+
for (const [id] of index.byId) {
|
|
776
|
+
const visited = /* @__PURE__ */ new Set();
|
|
777
|
+
let current = id;
|
|
778
|
+
while (current !== null) {
|
|
779
|
+
if (visited.has(current)) {
|
|
780
|
+
issues.push(`Cycle detected: block "${id}" has a circular parentId chain`);
|
|
781
|
+
break;
|
|
782
|
+
}
|
|
783
|
+
visited.add(current);
|
|
784
|
+
const block = index.byId.get(current);
|
|
785
|
+
current = block?.parentId ?? null;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
for (const [id, block] of index.byId) {
|
|
789
|
+
if (block.parentId !== null && !index.byId.has(block.parentId)) {
|
|
790
|
+
issues.push(`Orphan: block "${id}" references non-existent parent "${block.parentId}"`);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
for (const [parentId, childIds] of index.byParent) {
|
|
794
|
+
for (const childId of childIds) {
|
|
795
|
+
if (!index.byId.has(childId)) {
|
|
796
|
+
issues.push(`Stale ref: byParent key "${parentId}" lists non-existent block "${childId}"`);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
if (issues.length > 0) {
|
|
801
|
+
for (const issue of issues) {
|
|
802
|
+
console.warn(`[dnd-block-tree] ${issue}`);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
return { valid: issues.length === 0, issues };
|
|
806
|
+
}
|
|
751
807
|
function deleteBlockAndDescendants(state, id) {
|
|
752
808
|
const byId = cloneMap(state.byId);
|
|
753
809
|
const byParent = cloneParentMap(state.byParent);
|
|
@@ -770,7 +826,12 @@ function getBlockPosition(blocks, blockId) {
|
|
|
770
826
|
}
|
|
771
827
|
function computeInitialExpanded(blocks, containerTypes, initialExpanded) {
|
|
772
828
|
if (initialExpanded === "none") {
|
|
773
|
-
|
|
829
|
+
const expandedMap2 = {};
|
|
830
|
+
const containers2 = blocks.filter((b) => containerTypes.includes(b.type));
|
|
831
|
+
for (const container of containers2) {
|
|
832
|
+
expandedMap2[container.id] = false;
|
|
833
|
+
}
|
|
834
|
+
return expandedMap2;
|
|
774
835
|
}
|
|
775
836
|
const expandedMap = {};
|
|
776
837
|
const containers = blocks.filter((b) => containerTypes.includes(b.type));
|
|
@@ -887,14 +948,17 @@ function BlockTree({
|
|
|
887
948
|
onDragMove?.(event);
|
|
888
949
|
}, 50)
|
|
889
950
|
).current;
|
|
890
|
-
const originalIndex =
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
)
|
|
897
|
-
|
|
951
|
+
const originalIndex = react.useMemo(
|
|
952
|
+
() => computeNormalizedIndex(blocks, orderingStrategy),
|
|
953
|
+
[blocks, orderingStrategy]
|
|
954
|
+
);
|
|
955
|
+
const blocksByParent = react.useMemo(() => {
|
|
956
|
+
const map = /* @__PURE__ */ new Map();
|
|
957
|
+
for (const [parentId, ids] of originalIndex.byParent.entries()) {
|
|
958
|
+
map.set(parentId, ids.map((id) => originalIndex.byId.get(id)).filter(Boolean));
|
|
959
|
+
}
|
|
960
|
+
return map;
|
|
961
|
+
}, [originalIndex]);
|
|
898
962
|
const focusedIdRef = react.useRef(null);
|
|
899
963
|
const rootRef = react.useRef(null);
|
|
900
964
|
const lastClickedIdRef = react.useRef(null);
|
|
@@ -1869,6 +1933,743 @@ function useVirtualTree({
|
|
|
1869
1933
|
offsetY
|
|
1870
1934
|
};
|
|
1871
1935
|
}
|
|
1936
|
+
var MAX_EVENTS = 100;
|
|
1937
|
+
function useDevToolsCallbacks() {
|
|
1938
|
+
const [events, setEvents] = react.useState([]);
|
|
1939
|
+
const nextIdRef = react.useRef(1);
|
|
1940
|
+
const addEvent = react.useCallback((type, summary) => {
|
|
1941
|
+
const entry = {
|
|
1942
|
+
id: nextIdRef.current++,
|
|
1943
|
+
timestamp: Date.now(),
|
|
1944
|
+
type,
|
|
1945
|
+
summary
|
|
1946
|
+
};
|
|
1947
|
+
setEvents((prev) => [entry, ...prev].slice(0, MAX_EVENTS));
|
|
1948
|
+
}, []);
|
|
1949
|
+
const clearEvents = react.useCallback(() => {
|
|
1950
|
+
setEvents([]);
|
|
1951
|
+
}, []);
|
|
1952
|
+
const callbacks = react.useMemo(() => ({
|
|
1953
|
+
onDragStart: (event) => {
|
|
1954
|
+
addEvent("dragStart", `Started dragging "${event.blockId}"`);
|
|
1955
|
+
},
|
|
1956
|
+
onDragEnd: (event) => {
|
|
1957
|
+
if (event.cancelled) {
|
|
1958
|
+
addEvent("dragEnd", `Cancelled drag of "${event.blockId}"`);
|
|
1959
|
+
} else {
|
|
1960
|
+
addEvent("dragEnd", `Dropped "${event.blockId}" at ${event.targetZone ?? "none"}`);
|
|
1961
|
+
}
|
|
1962
|
+
},
|
|
1963
|
+
onBlockMove: (event) => {
|
|
1964
|
+
const fromStr = `parent=${event.from.parentId ?? "root"}[${event.from.index}]`;
|
|
1965
|
+
const toStr = `parent=${event.to.parentId ?? "root"}[${event.to.index}]`;
|
|
1966
|
+
const ids = event.movedIds.length > 1 ? ` (${event.movedIds.length} blocks)` : "";
|
|
1967
|
+
addEvent("blockMove", `Moved "${event.block.id}" from ${fromStr} to ${toStr}${ids}`);
|
|
1968
|
+
},
|
|
1969
|
+
onExpandChange: (event) => {
|
|
1970
|
+
addEvent("expandChange", `${event.expanded ? "Expanded" : "Collapsed"} "${event.blockId}"`);
|
|
1971
|
+
},
|
|
1972
|
+
onHoverChange: (event) => {
|
|
1973
|
+
if (event.zoneId) {
|
|
1974
|
+
addEvent("hoverChange", `Hovering over zone "${event.zoneId}"`);
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
}), [addEvent]);
|
|
1978
|
+
return { callbacks, events, clearEvents };
|
|
1979
|
+
}
|
|
1980
|
+
function DevToolsLogo({ size = 20 }) {
|
|
1981
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1982
|
+
"svg",
|
|
1983
|
+
{
|
|
1984
|
+
width: size,
|
|
1985
|
+
height: size,
|
|
1986
|
+
viewBox: "0 0 24 24",
|
|
1987
|
+
fill: "none",
|
|
1988
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1989
|
+
children: [
|
|
1990
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "2", y: "2", width: "8", height: "5", rx: "1", fill: "#3b82f6", opacity: "0.9" }),
|
|
1991
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "8", y: "10", width: "8", height: "5", rx: "1", fill: "#10b981", opacity: "0.9" }),
|
|
1992
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "14", y: "18", width: "8", height: "5", rx: "1", fill: "#f59e0b", opacity: "0.9" }),
|
|
1993
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 7 L6 10 L8 10", stroke: "#888", strokeWidth: "1.5", fill: "none", opacity: "0.6" }),
|
|
1994
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 15 L12 18 L14 18", stroke: "#888", strokeWidth: "1.5", fill: "none", opacity: "0.6" })
|
|
1995
|
+
]
|
|
1996
|
+
}
|
|
1997
|
+
);
|
|
1998
|
+
}
|
|
1999
|
+
function computeDiffMap(prev, next) {
|
|
2000
|
+
const prevMap = new Map(prev.map((b) => [b.id, b]));
|
|
2001
|
+
const changeMap = /* @__PURE__ */ new Map();
|
|
2002
|
+
for (const block of next) {
|
|
2003
|
+
const prevBlock = prevMap.get(block.id);
|
|
2004
|
+
if (!prevBlock) {
|
|
2005
|
+
changeMap.set(block.id, "added");
|
|
2006
|
+
} else if (prevBlock.parentId !== block.parentId || prevBlock.order !== block.order) {
|
|
2007
|
+
changeMap.set(block.id, "moved");
|
|
2008
|
+
} else {
|
|
2009
|
+
changeMap.set(block.id, "unchanged");
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
return changeMap;
|
|
2013
|
+
}
|
|
2014
|
+
function buildDiffTree(blocks, changeMap) {
|
|
2015
|
+
const result = [];
|
|
2016
|
+
const byParent = /* @__PURE__ */ new Map();
|
|
2017
|
+
for (const block of blocks) {
|
|
2018
|
+
const key = block.parentId ?? null;
|
|
2019
|
+
const list = byParent.get(key) ?? [];
|
|
2020
|
+
list.push(block);
|
|
2021
|
+
byParent.set(key, list);
|
|
2022
|
+
}
|
|
2023
|
+
function walk(parentId, depth) {
|
|
2024
|
+
const children = byParent.get(parentId) ?? [];
|
|
2025
|
+
children.sort((a, b) => {
|
|
2026
|
+
const ao = a.order, bo = b.order;
|
|
2027
|
+
if (typeof ao === "number" && typeof bo === "number") return ao - bo;
|
|
2028
|
+
return String(ao) < String(bo) ? -1 : String(ao) > String(bo) ? 1 : 0;
|
|
2029
|
+
});
|
|
2030
|
+
for (const block of children) {
|
|
2031
|
+
result.push({ block, changeType: changeMap.get(block.id) ?? "unchanged", depth });
|
|
2032
|
+
walk(block.id, depth + 1);
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
walk(null, 0);
|
|
2036
|
+
return result;
|
|
2037
|
+
}
|
|
2038
|
+
var TYPE_COLORS = {
|
|
2039
|
+
dragStart: "#3b82f6",
|
|
2040
|
+
dragEnd: "#10b981",
|
|
2041
|
+
blockMove: "#f59e0b",
|
|
2042
|
+
expandChange: "#8b5cf6",
|
|
2043
|
+
hoverChange: "#6b7280"
|
|
2044
|
+
};
|
|
2045
|
+
var TYPE_LABELS = {
|
|
2046
|
+
dragStart: "DRAG",
|
|
2047
|
+
dragEnd: "DROP",
|
|
2048
|
+
blockMove: "MOVE",
|
|
2049
|
+
expandChange: "EXPAND",
|
|
2050
|
+
hoverChange: "HOVER"
|
|
2051
|
+
};
|
|
2052
|
+
var TYPE_TOOLTIPS = {
|
|
2053
|
+
dragStart: "onDragStart \u2014 a block was picked up",
|
|
2054
|
+
dragEnd: "onDragEnd \u2014 a block was dropped or drag was cancelled",
|
|
2055
|
+
blockMove: "onBlockMove \u2014 a block was reparented to a new position",
|
|
2056
|
+
expandChange: "onExpandChange \u2014 a container was expanded or collapsed",
|
|
2057
|
+
hoverChange: "onHoverChange \u2014 the pointer entered a drop zone"
|
|
2058
|
+
};
|
|
2059
|
+
var DEFAULT_WIDTH = 340;
|
|
2060
|
+
var DEFAULT_HEIGHT = 420;
|
|
2061
|
+
var DIFF_EXTRA_WIDTH = 300;
|
|
2062
|
+
var MIN_WIDTH = 280;
|
|
2063
|
+
var MIN_HEIGHT = 200;
|
|
2064
|
+
function getAnchorCSS(position) {
|
|
2065
|
+
switch (position) {
|
|
2066
|
+
case "bottom-right":
|
|
2067
|
+
return { bottom: 16, right: 16 };
|
|
2068
|
+
case "top-left":
|
|
2069
|
+
return { top: 16, left: 16 };
|
|
2070
|
+
case "top-right":
|
|
2071
|
+
return { top: 16, right: 16 };
|
|
2072
|
+
case "bottom-left":
|
|
2073
|
+
default:
|
|
2074
|
+
return { bottom: 16, left: 16 };
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
function computeCardOrigin(position, width, height) {
|
|
2078
|
+
const vw = typeof window !== "undefined" ? window.innerWidth : 1024;
|
|
2079
|
+
const vh = typeof window !== "undefined" ? window.innerHeight : 768;
|
|
2080
|
+
switch (position) {
|
|
2081
|
+
case "bottom-right":
|
|
2082
|
+
return { x: Math.max(0, vw - 16 - width), y: Math.max(0, vh - 16 - height - 48) };
|
|
2083
|
+
case "top-left":
|
|
2084
|
+
return { x: 16, y: 16 + 48 };
|
|
2085
|
+
case "top-right":
|
|
2086
|
+
return { x: Math.max(0, vw - 16 - width), y: 16 + 48 };
|
|
2087
|
+
case "bottom-left":
|
|
2088
|
+
default:
|
|
2089
|
+
return { x: 16, y: Math.max(0, vh - 16 - height - 48) };
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
function BlockTreeDevTools({
|
|
2093
|
+
blocks,
|
|
2094
|
+
containerTypes = [],
|
|
2095
|
+
events,
|
|
2096
|
+
onClearEvents,
|
|
2097
|
+
getLabel = (b) => b.type,
|
|
2098
|
+
initialOpen = false,
|
|
2099
|
+
position = "bottom-left",
|
|
2100
|
+
buttonStyle,
|
|
2101
|
+
panelStyle
|
|
2102
|
+
}) {
|
|
2103
|
+
const [isOpen, setIsOpen] = react.useState(initialOpen);
|
|
2104
|
+
const [showDiff, setShowDiff] = react.useState(false);
|
|
2105
|
+
const [cardPos, setCardPos] = react.useState(null);
|
|
2106
|
+
const [cardSize, setCardSize] = react.useState({ w: DEFAULT_WIDTH, h: DEFAULT_HEIGHT });
|
|
2107
|
+
const [showTooltip, setShowTooltip] = react.useState(false);
|
|
2108
|
+
const dragRef = react.useRef({
|
|
2109
|
+
dragging: false,
|
|
2110
|
+
startX: 0,
|
|
2111
|
+
startY: 0,
|
|
2112
|
+
origX: 0,
|
|
2113
|
+
origY: 0
|
|
2114
|
+
});
|
|
2115
|
+
const resizeRef = react.useRef({ active: false, edge: "", startX: 0, startY: 0, origX: 0, origY: 0, origW: 0, origH: 0 });
|
|
2116
|
+
const cardRef = react.useRef(null);
|
|
2117
|
+
const prevBlocksRef = react.useRef(blocks);
|
|
2118
|
+
const diffChangeMap = react.useMemo(() => computeDiffMap(prevBlocksRef.current, blocks), [blocks]);
|
|
2119
|
+
react.useEffect(() => {
|
|
2120
|
+
prevBlocksRef.current = blocks;
|
|
2121
|
+
}, [blocks]);
|
|
2122
|
+
const diffTree = react.useMemo(() => buildDiffTree(blocks, diffChangeMap), [blocks, diffChangeMap]);
|
|
2123
|
+
const diffStats = react.useMemo(() => {
|
|
2124
|
+
let added = 0, moved = 0;
|
|
2125
|
+
for (const { changeType } of diffTree) {
|
|
2126
|
+
if (changeType === "added") added++;
|
|
2127
|
+
if (changeType === "moved") moved++;
|
|
2128
|
+
}
|
|
2129
|
+
return { added, moved };
|
|
2130
|
+
}, [diffTree]);
|
|
2131
|
+
react.useEffect(() => {
|
|
2132
|
+
setCardSize((prev) => {
|
|
2133
|
+
const targetW = showDiff ? DEFAULT_WIDTH + DIFF_EXTRA_WIDTH : DEFAULT_WIDTH;
|
|
2134
|
+
const wasDefault = Math.abs(prev.w - DEFAULT_WIDTH) < 20;
|
|
2135
|
+
const wasExpanded = Math.abs(prev.w - (DEFAULT_WIDTH + DIFF_EXTRA_WIDTH)) < 20;
|
|
2136
|
+
if (showDiff && (wasDefault || prev.w < targetW)) {
|
|
2137
|
+
return { ...prev, w: targetW };
|
|
2138
|
+
}
|
|
2139
|
+
if (!showDiff && wasExpanded) {
|
|
2140
|
+
return { ...prev, w: DEFAULT_WIDTH };
|
|
2141
|
+
}
|
|
2142
|
+
return prev;
|
|
2143
|
+
});
|
|
2144
|
+
}, [showDiff]);
|
|
2145
|
+
const getDefaultCardPos = react.useCallback(() => {
|
|
2146
|
+
return computeCardOrigin(position, cardSize.w, cardSize.h);
|
|
2147
|
+
}, [position, cardSize.w, cardSize.h]);
|
|
2148
|
+
react.useEffect(() => {
|
|
2149
|
+
if (isOpen && !cardPos) {
|
|
2150
|
+
setCardPos(getDefaultCardPos());
|
|
2151
|
+
}
|
|
2152
|
+
}, [isOpen, cardPos, getDefaultCardPos]);
|
|
2153
|
+
const handleDragPointerDown = react.useCallback((e) => {
|
|
2154
|
+
e.preventDefault();
|
|
2155
|
+
dragRef.current = {
|
|
2156
|
+
dragging: true,
|
|
2157
|
+
startX: e.clientX,
|
|
2158
|
+
startY: e.clientY,
|
|
2159
|
+
origX: cardPos?.x ?? 0,
|
|
2160
|
+
origY: cardPos?.y ?? 0
|
|
2161
|
+
};
|
|
2162
|
+
e.target.setPointerCapture(e.pointerId);
|
|
2163
|
+
}, [cardPos]);
|
|
2164
|
+
const handleDragPointerMove = react.useCallback((e) => {
|
|
2165
|
+
if (!dragRef.current.dragging) return;
|
|
2166
|
+
const dx = e.clientX - dragRef.current.startX;
|
|
2167
|
+
const dy = e.clientY - dragRef.current.startY;
|
|
2168
|
+
const newX = dragRef.current.origX + dx;
|
|
2169
|
+
const newY = dragRef.current.origY + dy;
|
|
2170
|
+
const maxX = window.innerWidth - cardSize.w;
|
|
2171
|
+
const maxY = window.innerHeight - 40;
|
|
2172
|
+
setCardPos({
|
|
2173
|
+
x: Math.max(0, Math.min(newX, maxX)),
|
|
2174
|
+
y: Math.max(0, Math.min(newY, maxY))
|
|
2175
|
+
});
|
|
2176
|
+
}, [cardSize.w]);
|
|
2177
|
+
const handleDragPointerUp = react.useCallback(() => {
|
|
2178
|
+
dragRef.current.dragging = false;
|
|
2179
|
+
}, []);
|
|
2180
|
+
const handleResizePointerDown = react.useCallback((edge) => (e) => {
|
|
2181
|
+
e.preventDefault();
|
|
2182
|
+
e.stopPropagation();
|
|
2183
|
+
resizeRef.current = {
|
|
2184
|
+
active: true,
|
|
2185
|
+
edge,
|
|
2186
|
+
startX: e.clientX,
|
|
2187
|
+
startY: e.clientY,
|
|
2188
|
+
origX: cardPos?.x ?? 0,
|
|
2189
|
+
origY: cardPos?.y ?? 0,
|
|
2190
|
+
origW: cardSize.w,
|
|
2191
|
+
origH: cardSize.h
|
|
2192
|
+
};
|
|
2193
|
+
e.target.setPointerCapture(e.pointerId);
|
|
2194
|
+
}, [cardPos, cardSize]);
|
|
2195
|
+
const handleResizePointerMove = react.useCallback((e) => {
|
|
2196
|
+
const r = resizeRef.current;
|
|
2197
|
+
if (!r.active) return;
|
|
2198
|
+
const dx = e.clientX - r.startX;
|
|
2199
|
+
const dy = e.clientY - r.startY;
|
|
2200
|
+
let newW = r.origW;
|
|
2201
|
+
let newH = r.origH;
|
|
2202
|
+
let newX = r.origX;
|
|
2203
|
+
let newY = r.origY;
|
|
2204
|
+
if (r.edge.includes("e")) newW = Math.max(MIN_WIDTH, r.origW + dx);
|
|
2205
|
+
if (r.edge.includes("w")) {
|
|
2206
|
+
newW = Math.max(MIN_WIDTH, r.origW - dx);
|
|
2207
|
+
newX = r.origX + (r.origW - newW);
|
|
2208
|
+
}
|
|
2209
|
+
if (r.edge.includes("s")) newH = Math.max(MIN_HEIGHT, r.origH + dy);
|
|
2210
|
+
if (r.edge.includes("n")) {
|
|
2211
|
+
newH = Math.max(MIN_HEIGHT, r.origH - dy);
|
|
2212
|
+
newY = r.origY + (r.origH - newH);
|
|
2213
|
+
}
|
|
2214
|
+
setCardSize({ w: newW, h: newH });
|
|
2215
|
+
setCardPos({ x: newX, y: newY });
|
|
2216
|
+
}, []);
|
|
2217
|
+
const handleResizePointerUp = react.useCallback(() => {
|
|
2218
|
+
resizeRef.current.active = false;
|
|
2219
|
+
}, []);
|
|
2220
|
+
const toggle = react.useCallback(() => {
|
|
2221
|
+
setIsOpen((prev) => !prev);
|
|
2222
|
+
}, []);
|
|
2223
|
+
const toggleDiff = react.useCallback(() => {
|
|
2224
|
+
setShowDiff((prev) => !prev);
|
|
2225
|
+
}, []);
|
|
2226
|
+
const renderCountRef = react.useRef(0);
|
|
2227
|
+
const lastRenderTimeRef = react.useRef(performance.now());
|
|
2228
|
+
const prevBlockCountRef = react.useRef(blocks.length);
|
|
2229
|
+
renderCountRef.current++;
|
|
2230
|
+
const renderTime = performance.now() - lastRenderTimeRef.current;
|
|
2231
|
+
lastRenderTimeRef.current = performance.now();
|
|
2232
|
+
const blockCountDelta = blocks.length - prevBlockCountRef.current;
|
|
2233
|
+
react.useEffect(() => {
|
|
2234
|
+
prevBlockCountRef.current = blocks.length;
|
|
2235
|
+
}, [blocks.length]);
|
|
2236
|
+
const treeStats = react.useMemo(() => {
|
|
2237
|
+
const index = computeNormalizedIndex(blocks);
|
|
2238
|
+
const containers = blocks.filter((b) => containerTypes.includes(b.type));
|
|
2239
|
+
let maxDepthVal = 0;
|
|
2240
|
+
for (const block of blocks) {
|
|
2241
|
+
const d = getBlockDepth(index, block.id);
|
|
2242
|
+
if (d > maxDepthVal) maxDepthVal = d;
|
|
2243
|
+
}
|
|
2244
|
+
const validation = validateBlockTree(index);
|
|
2245
|
+
return {
|
|
2246
|
+
blockCount: blocks.length,
|
|
2247
|
+
containerCount: containers.length,
|
|
2248
|
+
maxDepth: maxDepthVal,
|
|
2249
|
+
validation
|
|
2250
|
+
};
|
|
2251
|
+
}, [blocks, containerTypes]);
|
|
2252
|
+
const formatTime = (ts) => {
|
|
2253
|
+
const d = new Date(ts);
|
|
2254
|
+
return `${d.getHours().toString().padStart(2, "0")}:${d.getMinutes().toString().padStart(2, "0")}:${d.getSeconds().toString().padStart(2, "0")}`;
|
|
2255
|
+
};
|
|
2256
|
+
const anchor = getAnchorCSS(position);
|
|
2257
|
+
const triggerBtnStyle = {
|
|
2258
|
+
position: "fixed",
|
|
2259
|
+
...anchor,
|
|
2260
|
+
zIndex: 99998,
|
|
2261
|
+
width: 40,
|
|
2262
|
+
height: 40,
|
|
2263
|
+
borderRadius: "50%",
|
|
2264
|
+
border: "none",
|
|
2265
|
+
background: isOpen ? "rgba(59, 130, 246, 0.9)" : "rgba(30, 30, 30, 0.85)",
|
|
2266
|
+
color: "#fff",
|
|
2267
|
+
cursor: "pointer",
|
|
2268
|
+
display: "flex",
|
|
2269
|
+
alignItems: "center",
|
|
2270
|
+
justifyContent: "center",
|
|
2271
|
+
boxShadow: "0 2px 8px rgba(0,0,0,0.3)",
|
|
2272
|
+
transition: "background 0.15s, transform 0.15s",
|
|
2273
|
+
...buttonStyle
|
|
2274
|
+
};
|
|
2275
|
+
const tooltipStyle = {
|
|
2276
|
+
position: "absolute",
|
|
2277
|
+
whiteSpace: "nowrap",
|
|
2278
|
+
fontSize: 11,
|
|
2279
|
+
fontWeight: 500,
|
|
2280
|
+
padding: "4px 10px",
|
|
2281
|
+
borderRadius: 6,
|
|
2282
|
+
background: "rgba(15, 15, 20, 0.95)",
|
|
2283
|
+
color: "#ccc",
|
|
2284
|
+
boxShadow: "0 2px 8px rgba(0,0,0,0.3)",
|
|
2285
|
+
pointerEvents: "none",
|
|
2286
|
+
// Position based on anchor corner
|
|
2287
|
+
...anchor.bottom !== void 0 ? { bottom: "100%", marginBottom: 8 } : { top: "100%", marginTop: 8 },
|
|
2288
|
+
...anchor.left !== void 0 ? { left: 0 } : { right: 0 }
|
|
2289
|
+
};
|
|
2290
|
+
const cardStyle = {
|
|
2291
|
+
position: "fixed",
|
|
2292
|
+
left: cardPos?.x ?? 0,
|
|
2293
|
+
top: cardPos?.y ?? 0,
|
|
2294
|
+
zIndex: 99999,
|
|
2295
|
+
width: cardSize.w,
|
|
2296
|
+
height: cardSize.h,
|
|
2297
|
+
background: "rgba(20, 20, 24, 0.95)",
|
|
2298
|
+
backdropFilter: "blur(12px)",
|
|
2299
|
+
border: "1px solid rgba(255,255,255,0.1)",
|
|
2300
|
+
borderRadius: 10,
|
|
2301
|
+
color: "#e0e0e0",
|
|
2302
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
2303
|
+
fontSize: 12,
|
|
2304
|
+
display: "flex",
|
|
2305
|
+
flexDirection: "column",
|
|
2306
|
+
boxShadow: "0 8px 32px rgba(0,0,0,0.5)",
|
|
2307
|
+
overflow: "hidden",
|
|
2308
|
+
...panelStyle
|
|
2309
|
+
};
|
|
2310
|
+
const titleBarStyle = {
|
|
2311
|
+
display: "flex",
|
|
2312
|
+
alignItems: "center",
|
|
2313
|
+
justifyContent: "space-between",
|
|
2314
|
+
padding: "8px 12px",
|
|
2315
|
+
gap: 8,
|
|
2316
|
+
background: "rgba(255,255,255,0.04)",
|
|
2317
|
+
borderBottom: "1px solid rgba(255,255,255,0.08)",
|
|
2318
|
+
cursor: "grab",
|
|
2319
|
+
userSelect: "none",
|
|
2320
|
+
flexShrink: 0,
|
|
2321
|
+
touchAction: "none"
|
|
2322
|
+
};
|
|
2323
|
+
const titleTextStyle = {
|
|
2324
|
+
fontSize: 11,
|
|
2325
|
+
fontWeight: 600,
|
|
2326
|
+
letterSpacing: "0.03em",
|
|
2327
|
+
opacity: 0.8,
|
|
2328
|
+
flexShrink: 0
|
|
2329
|
+
};
|
|
2330
|
+
const titleBtnStyle = (active) => ({
|
|
2331
|
+
height: 22,
|
|
2332
|
+
padding: "0 8px",
|
|
2333
|
+
borderRadius: 4,
|
|
2334
|
+
border: "1px solid " + (active ? "rgba(59,130,246,0.5)" : "rgba(128,128,128,0.25)"),
|
|
2335
|
+
background: active ? "rgba(59,130,246,0.15)" : "transparent",
|
|
2336
|
+
color: active ? "#93b8f7" : "#999",
|
|
2337
|
+
cursor: "pointer",
|
|
2338
|
+
fontSize: 10,
|
|
2339
|
+
fontWeight: 600,
|
|
2340
|
+
letterSpacing: "0.02em",
|
|
2341
|
+
flexShrink: 0
|
|
2342
|
+
});
|
|
2343
|
+
const closeBtnStyle = {
|
|
2344
|
+
width: 20,
|
|
2345
|
+
height: 20,
|
|
2346
|
+
borderRadius: 4,
|
|
2347
|
+
border: "none",
|
|
2348
|
+
background: "transparent",
|
|
2349
|
+
color: "#999",
|
|
2350
|
+
cursor: "pointer",
|
|
2351
|
+
display: "flex",
|
|
2352
|
+
alignItems: "center",
|
|
2353
|
+
justifyContent: "center",
|
|
2354
|
+
fontSize: 14,
|
|
2355
|
+
lineHeight: "1",
|
|
2356
|
+
padding: 0,
|
|
2357
|
+
flexShrink: 0
|
|
2358
|
+
};
|
|
2359
|
+
const bodyStyle = {
|
|
2360
|
+
flex: 1,
|
|
2361
|
+
display: "flex",
|
|
2362
|
+
flexDirection: "row",
|
|
2363
|
+
overflow: "hidden",
|
|
2364
|
+
minHeight: 0
|
|
2365
|
+
};
|
|
2366
|
+
const mainColumnStyle = {
|
|
2367
|
+
flex: showDiff ? "0 0 50%" : "1 1 100%",
|
|
2368
|
+
overflow: "auto",
|
|
2369
|
+
padding: "10px 12px",
|
|
2370
|
+
minWidth: 0
|
|
2371
|
+
};
|
|
2372
|
+
const diffColumnStyle = {
|
|
2373
|
+
flex: "0 0 50%",
|
|
2374
|
+
overflow: "auto",
|
|
2375
|
+
padding: "10px 12px",
|
|
2376
|
+
borderLeft: "1px solid rgba(255,255,255,0.08)",
|
|
2377
|
+
minWidth: 0
|
|
2378
|
+
};
|
|
2379
|
+
const sectionStyle = {
|
|
2380
|
+
marginBottom: 8
|
|
2381
|
+
};
|
|
2382
|
+
const headingStyle = {
|
|
2383
|
+
fontSize: 11,
|
|
2384
|
+
fontWeight: 600,
|
|
2385
|
+
textTransform: "uppercase",
|
|
2386
|
+
letterSpacing: "0.05em",
|
|
2387
|
+
opacity: 0.6,
|
|
2388
|
+
marginBottom: 6
|
|
2389
|
+
};
|
|
2390
|
+
const statRowStyle = {
|
|
2391
|
+
display: "flex",
|
|
2392
|
+
justifyContent: "space-between",
|
|
2393
|
+
alignItems: "center",
|
|
2394
|
+
fontSize: 12,
|
|
2395
|
+
padding: "2px 0"
|
|
2396
|
+
};
|
|
2397
|
+
const statValueStyle = {
|
|
2398
|
+
fontFamily: "monospace",
|
|
2399
|
+
fontSize: 11,
|
|
2400
|
+
opacity: 0.8
|
|
2401
|
+
};
|
|
2402
|
+
const badgeStyle = (color) => ({
|
|
2403
|
+
display: "inline-block",
|
|
2404
|
+
fontSize: 9,
|
|
2405
|
+
fontWeight: 700,
|
|
2406
|
+
fontFamily: "monospace",
|
|
2407
|
+
padding: "1px 5px",
|
|
2408
|
+
borderRadius: 3,
|
|
2409
|
+
backgroundColor: color + "22",
|
|
2410
|
+
color,
|
|
2411
|
+
marginRight: 6,
|
|
2412
|
+
flexShrink: 0
|
|
2413
|
+
});
|
|
2414
|
+
const eventListStyle = {
|
|
2415
|
+
maxHeight: 160,
|
|
2416
|
+
overflowY: "auto",
|
|
2417
|
+
fontSize: 11,
|
|
2418
|
+
lineHeight: "1.6"
|
|
2419
|
+
};
|
|
2420
|
+
const eventItemStyle = {
|
|
2421
|
+
display: "flex",
|
|
2422
|
+
alignItems: "flex-start",
|
|
2423
|
+
gap: 4,
|
|
2424
|
+
padding: "2px 0",
|
|
2425
|
+
borderBottom: "1px solid rgba(128,128,128,0.1)"
|
|
2426
|
+
};
|
|
2427
|
+
const timeStyle = {
|
|
2428
|
+
fontFamily: "monospace",
|
|
2429
|
+
fontSize: 10,
|
|
2430
|
+
opacity: 0.4,
|
|
2431
|
+
flexShrink: 0,
|
|
2432
|
+
minWidth: 52
|
|
2433
|
+
};
|
|
2434
|
+
const clearBtnStyle = {
|
|
2435
|
+
fontSize: 10,
|
|
2436
|
+
padding: "2px 8px",
|
|
2437
|
+
border: "1px solid rgba(128,128,128,0.3)",
|
|
2438
|
+
borderRadius: 4,
|
|
2439
|
+
background: "transparent",
|
|
2440
|
+
color: "#ccc",
|
|
2441
|
+
cursor: "pointer",
|
|
2442
|
+
opacity: 0.7
|
|
2443
|
+
};
|
|
2444
|
+
const EDGE_SIZE = 6;
|
|
2445
|
+
const resizeEdge = (edge) => {
|
|
2446
|
+
const base = {
|
|
2447
|
+
position: "absolute",
|
|
2448
|
+
zIndex: 1
|
|
2449
|
+
};
|
|
2450
|
+
const cursors = {
|
|
2451
|
+
n: "ns-resize",
|
|
2452
|
+
s: "ns-resize",
|
|
2453
|
+
e: "ew-resize",
|
|
2454
|
+
w: "ew-resize",
|
|
2455
|
+
ne: "nesw-resize",
|
|
2456
|
+
nw: "nwse-resize",
|
|
2457
|
+
se: "nwse-resize",
|
|
2458
|
+
sw: "nesw-resize"
|
|
2459
|
+
};
|
|
2460
|
+
const styles = {
|
|
2461
|
+
n: { top: 0, left: EDGE_SIZE, right: EDGE_SIZE, height: EDGE_SIZE },
|
|
2462
|
+
s: { bottom: 0, left: EDGE_SIZE, right: EDGE_SIZE, height: EDGE_SIZE },
|
|
2463
|
+
e: { right: 0, top: EDGE_SIZE, bottom: EDGE_SIZE, width: EDGE_SIZE },
|
|
2464
|
+
w: { left: 0, top: EDGE_SIZE, bottom: EDGE_SIZE, width: EDGE_SIZE },
|
|
2465
|
+
ne: { top: 0, right: 0, width: EDGE_SIZE * 2, height: EDGE_SIZE * 2 },
|
|
2466
|
+
nw: { top: 0, left: 0, width: EDGE_SIZE * 2, height: EDGE_SIZE * 2 },
|
|
2467
|
+
se: { bottom: 0, right: 0, width: EDGE_SIZE * 2, height: EDGE_SIZE * 2 },
|
|
2468
|
+
sw: { bottom: 0, left: 0, width: EDGE_SIZE * 2, height: EDGE_SIZE * 2 }
|
|
2469
|
+
};
|
|
2470
|
+
return { ...base, cursor: cursors[edge], ...styles[edge] };
|
|
2471
|
+
};
|
|
2472
|
+
const EDGES = ["n", "s", "e", "w", "ne", "nw", "se", "sw"];
|
|
2473
|
+
const diffRowColor = (ct) => {
|
|
2474
|
+
if (ct === "added") return { background: "rgba(16,185,129,0.1)", color: "#34d399" };
|
|
2475
|
+
if (ct === "moved") return { background: "rgba(245,158,11,0.1)", color: "#fbbf24" };
|
|
2476
|
+
return { color: "rgba(200,200,200,0.5)" };
|
|
2477
|
+
};
|
|
2478
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2479
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "fixed", ...anchor, zIndex: 99998 }, children: [
|
|
2480
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2481
|
+
"button",
|
|
2482
|
+
{
|
|
2483
|
+
onClick: toggle,
|
|
2484
|
+
onMouseEnter: () => setShowTooltip(true),
|
|
2485
|
+
onMouseLeave: () => setShowTooltip(false),
|
|
2486
|
+
style: triggerBtnStyle,
|
|
2487
|
+
"aria-label": isOpen ? "Close DevTools" : "Open DevTools",
|
|
2488
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(DevToolsLogo, { size: 20 })
|
|
2489
|
+
}
|
|
2490
|
+
),
|
|
2491
|
+
showTooltip && !isOpen && /* @__PURE__ */ jsxRuntime.jsx("div", { style: tooltipStyle, children: "dnd-block-tree DevTools" })
|
|
2492
|
+
] }),
|
|
2493
|
+
isOpen && cardPos && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2494
|
+
"div",
|
|
2495
|
+
{
|
|
2496
|
+
ref: cardRef,
|
|
2497
|
+
style: cardStyle,
|
|
2498
|
+
"data-devtools-root": "",
|
|
2499
|
+
children: [
|
|
2500
|
+
EDGES.map((edge) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2501
|
+
"div",
|
|
2502
|
+
{
|
|
2503
|
+
style: resizeEdge(edge),
|
|
2504
|
+
onPointerDown: handleResizePointerDown(edge),
|
|
2505
|
+
onPointerMove: handleResizePointerMove,
|
|
2506
|
+
onPointerUp: handleResizePointerUp
|
|
2507
|
+
},
|
|
2508
|
+
edge
|
|
2509
|
+
)),
|
|
2510
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2511
|
+
"div",
|
|
2512
|
+
{
|
|
2513
|
+
style: titleBarStyle,
|
|
2514
|
+
onPointerDown: handleDragPointerDown,
|
|
2515
|
+
onPointerMove: handleDragPointerMove,
|
|
2516
|
+
onPointerUp: handleDragPointerUp,
|
|
2517
|
+
children: [
|
|
2518
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: titleTextStyle, children: "DevTools" }),
|
|
2519
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1 } }),
|
|
2520
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2521
|
+
"button",
|
|
2522
|
+
{
|
|
2523
|
+
onClick: toggleDiff,
|
|
2524
|
+
style: titleBtnStyle(showDiff),
|
|
2525
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
2526
|
+
title: "Toggle structure diff \u2014 shows which blocks were added, moved, or unchanged",
|
|
2527
|
+
children: "Diff"
|
|
2528
|
+
}
|
|
2529
|
+
),
|
|
2530
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2531
|
+
"button",
|
|
2532
|
+
{
|
|
2533
|
+
onClick: toggle,
|
|
2534
|
+
style: closeBtnStyle,
|
|
2535
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
2536
|
+
"aria-label": "Close DevTools",
|
|
2537
|
+
children: "\xD7"
|
|
2538
|
+
}
|
|
2539
|
+
)
|
|
2540
|
+
]
|
|
2541
|
+
}
|
|
2542
|
+
),
|
|
2543
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: bodyStyle, children: [
|
|
2544
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: mainColumnStyle, children: [
|
|
2545
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: sectionStyle, "data-devtools-section": "tree-state", children: [
|
|
2546
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: headingStyle, title: "Live snapshot of the block tree structure", children: "Tree State" }),
|
|
2547
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: statRowStyle, title: "Total number of blocks in the flat array", children: [
|
|
2548
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Blocks" }),
|
|
2549
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: statValueStyle, children: treeStats.blockCount })
|
|
2550
|
+
] }),
|
|
2551
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: statRowStyle, title: "Blocks whose type is in containerTypes (can have children)", children: [
|
|
2552
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Containers" }),
|
|
2553
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: statValueStyle, children: treeStats.containerCount })
|
|
2554
|
+
] }),
|
|
2555
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: statRowStyle, title: "Deepest nesting level in the tree (root = 1)", children: [
|
|
2556
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Max Depth" }),
|
|
2557
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: statValueStyle, children: treeStats.maxDepth })
|
|
2558
|
+
] }),
|
|
2559
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: statRowStyle, title: "Checks for cycles, orphans, and stale parent refs", children: [
|
|
2560
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Validation" }),
|
|
2561
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: {
|
|
2562
|
+
...statValueStyle,
|
|
2563
|
+
color: treeStats.validation.valid ? "#10b981" : "#ef4444"
|
|
2564
|
+
}, children: treeStats.validation.valid ? "Valid" : `${treeStats.validation.issues.length} issue(s)` })
|
|
2565
|
+
] }),
|
|
2566
|
+
!treeStats.validation.valid && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 10, color: "#ef4444", marginTop: 4 }, children: treeStats.validation.issues.map((issue, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { children: issue }, i)) })
|
|
2567
|
+
] }),
|
|
2568
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: sectionStyle, "data-devtools-section": "event-log", children: [
|
|
2569
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 6 }, children: [
|
|
2570
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: headingStyle, title: "Chronological log of drag, drop, move, expand, and hover events", children: [
|
|
2571
|
+
"Event Log (",
|
|
2572
|
+
events.length,
|
|
2573
|
+
")"
|
|
2574
|
+
] }),
|
|
2575
|
+
events.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: onClearEvents, style: clearBtnStyle, children: "Clear" })
|
|
2576
|
+
] }),
|
|
2577
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: eventListStyle, children: events.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, opacity: 0.4, padding: "8px 0" }, children: "No events yet. Drag some blocks!" }) : events.map((event) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: eventItemStyle, children: [
|
|
2578
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: timeStyle, children: formatTime(event.timestamp) }),
|
|
2579
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: badgeStyle(TYPE_COLORS[event.type]), title: TYPE_TOOLTIPS[event.type], children: TYPE_LABELS[event.type] }),
|
|
2580
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { wordBreak: "break-word" }, children: event.summary })
|
|
2581
|
+
] }, event.id)) })
|
|
2582
|
+
] }),
|
|
2583
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: sectionStyle, "data-devtools-section": "performance", children: [
|
|
2584
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: headingStyle, title: "Render metrics for the DevTools component itself", children: "Performance" }),
|
|
2585
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: statRowStyle, title: "How many times this DevTools component has rendered", children: [
|
|
2586
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Render Count" }),
|
|
2587
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: statValueStyle, children: renderCountRef.current })
|
|
2588
|
+
] }),
|
|
2589
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: statRowStyle, title: "Time since the previous render of this component", children: [
|
|
2590
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Last Render" }),
|
|
2591
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: statValueStyle, children: [
|
|
2592
|
+
renderTime.toFixed(1),
|
|
2593
|
+
"ms"
|
|
2594
|
+
] })
|
|
2595
|
+
] }),
|
|
2596
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: statRowStyle, title: "Change in block count since the last render (+added / -removed)", children: /* @__PURE__ */ jsxRuntime.jsxs("span", { style: {
|
|
2597
|
+
...statValueStyle,
|
|
2598
|
+
color: blockCountDelta > 0 ? "#10b981" : blockCountDelta < 0 ? "#ef4444" : void 0
|
|
2599
|
+
}, children: [
|
|
2600
|
+
blockCountDelta > 0 ? "+" : "",
|
|
2601
|
+
blockCountDelta
|
|
2602
|
+
] }) })
|
|
2603
|
+
] })
|
|
2604
|
+
] }),
|
|
2605
|
+
showDiff && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: diffColumnStyle, children: [
|
|
2606
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 8 }, children: [
|
|
2607
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: headingStyle, title: "Tree structure diff \u2014 compares current state to the previous render", children: "Structure" }),
|
|
2608
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 10, fontSize: 11, fontWeight: 500 }, children: [
|
|
2609
|
+
diffStats.added > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { color: "#34d399", display: "flex", alignItems: "center", gap: 4 }, children: [
|
|
2610
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { width: 5, height: 5, borderRadius: "50%", background: "#34d399", display: "inline-block" } }),
|
|
2611
|
+
diffStats.added,
|
|
2612
|
+
" new"
|
|
2613
|
+
] }),
|
|
2614
|
+
diffStats.moved > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { color: "#fbbf24", display: "flex", alignItems: "center", gap: 4 }, children: [
|
|
2615
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { width: 5, height: 5, borderRadius: "50%", background: "#fbbf24", display: "inline-block" } }),
|
|
2616
|
+
diffStats.moved,
|
|
2617
|
+
" moved"
|
|
2618
|
+
] }),
|
|
2619
|
+
diffStats.added === 0 && diffStats.moved === 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { opacity: 0.4 }, children: "No changes" })
|
|
2620
|
+
] })
|
|
2621
|
+
] }),
|
|
2622
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontFamily: "monospace", fontSize: 11, lineHeight: "1.7" }, children: diffTree.map(({ block, changeType, depth }) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2623
|
+
"div",
|
|
2624
|
+
{
|
|
2625
|
+
style: {
|
|
2626
|
+
paddingLeft: depth * 14,
|
|
2627
|
+
padding: "2px 6px 2px " + (depth * 14 + 6) + "px",
|
|
2628
|
+
borderRadius: 4,
|
|
2629
|
+
display: "flex",
|
|
2630
|
+
alignItems: "center",
|
|
2631
|
+
gap: 6,
|
|
2632
|
+
...diffRowColor(changeType)
|
|
2633
|
+
},
|
|
2634
|
+
children: [
|
|
2635
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: { width: 12, textAlign: "center", fontWeight: 700, fontSize: 10 }, children: [
|
|
2636
|
+
changeType === "added" && "+",
|
|
2637
|
+
changeType === "moved" && "~"
|
|
2638
|
+
] }),
|
|
2639
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: {
|
|
2640
|
+
textTransform: "uppercase",
|
|
2641
|
+
fontSize: 9,
|
|
2642
|
+
letterSpacing: "0.05em",
|
|
2643
|
+
width: 50,
|
|
2644
|
+
flexShrink: 0,
|
|
2645
|
+
opacity: changeType === "unchanged" ? 0.5 : 1
|
|
2646
|
+
}, children: block.type }),
|
|
2647
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: {
|
|
2648
|
+
overflow: "hidden",
|
|
2649
|
+
textOverflow: "ellipsis",
|
|
2650
|
+
whiteSpace: "nowrap",
|
|
2651
|
+
flex: 1,
|
|
2652
|
+
minWidth: 0
|
|
2653
|
+
}, children: getLabel(block) }),
|
|
2654
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: {
|
|
2655
|
+
fontFamily: "monospace",
|
|
2656
|
+
fontSize: 10,
|
|
2657
|
+
flexShrink: 0,
|
|
2658
|
+
padding: "1px 5px",
|
|
2659
|
+
borderRadius: 3,
|
|
2660
|
+
...changeType === "added" ? { background: "rgba(16,185,129,0.2)", color: "#34d399" } : changeType === "moved" ? { background: "rgba(245,158,11,0.2)", color: "#fbbf24" } : { background: "rgba(128,128,128,0.15)", color: "rgba(200,200,200,0.4)" }
|
|
2661
|
+
}, children: String(block.order) })
|
|
2662
|
+
]
|
|
2663
|
+
},
|
|
2664
|
+
block.id
|
|
2665
|
+
)) })
|
|
2666
|
+
] })
|
|
2667
|
+
] })
|
|
2668
|
+
]
|
|
2669
|
+
}
|
|
2670
|
+
)
|
|
2671
|
+
] });
|
|
2672
|
+
}
|
|
1872
2673
|
|
|
1873
2674
|
// src/utils/serialization.ts
|
|
1874
2675
|
function flatToNested(blocks) {
|
|
@@ -1920,6 +2721,7 @@ function nestedToFlat(nested) {
|
|
|
1920
2721
|
}
|
|
1921
2722
|
|
|
1922
2723
|
exports.BlockTree = BlockTree;
|
|
2724
|
+
exports.BlockTreeDevTools = BlockTreeDevTools;
|
|
1923
2725
|
exports.BlockTreeSSR = BlockTreeSSR;
|
|
1924
2726
|
exports.DragOverlay = DragOverlay;
|
|
1925
2727
|
exports.DropZone = DropZone;
|
|
@@ -1954,8 +2756,10 @@ exports.reparentMultipleBlocks = reparentMultipleBlocks;
|
|
|
1954
2756
|
exports.triggerHaptic = triggerHaptic;
|
|
1955
2757
|
exports.useBlockHistory = useBlockHistory;
|
|
1956
2758
|
exports.useConfiguredSensors = useConfiguredSensors;
|
|
2759
|
+
exports.useDevToolsCallbacks = useDevToolsCallbacks;
|
|
1957
2760
|
exports.useLayoutAnimation = useLayoutAnimation;
|
|
1958
2761
|
exports.useVirtualTree = useVirtualTree;
|
|
2762
|
+
exports.validateBlockTree = validateBlockTree;
|
|
1959
2763
|
exports.weightedVerticalCollision = weightedVerticalCollision;
|
|
1960
2764
|
//# sourceMappingURL=index.js.map
|
|
1961
2765
|
//# sourceMappingURL=index.js.map
|