@dnd-block-tree/vanilla 2.3.0 → 2.4.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/dist/index.d.mts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +87 -31
- package/dist/index.mjs +87 -31
- package/package.json +6 -2
package/dist/index.d.mts
CHANGED
|
@@ -54,8 +54,13 @@ interface RenderBlockContext {
|
|
|
54
54
|
/** DefaultRenderer options */
|
|
55
55
|
interface DefaultRendererOptions<T extends BaseBlock = BaseBlock> {
|
|
56
56
|
container: HTMLElement;
|
|
57
|
+
containerTypes?: readonly string[];
|
|
57
58
|
renderBlock: (block: T, ctx: RenderBlockContext) => HTMLElement;
|
|
58
59
|
dropZoneHeight?: number;
|
|
60
|
+
dropZoneClassName?: string;
|
|
61
|
+
dropZoneActiveClassName?: string;
|
|
62
|
+
rootClassName?: string;
|
|
63
|
+
indentClassName?: string;
|
|
59
64
|
animateExpand?: boolean;
|
|
60
65
|
}
|
|
61
66
|
/** Cleanup function */
|
|
@@ -324,6 +329,9 @@ interface TreeRendererOptions<T extends BaseBlock> {
|
|
|
324
329
|
renderBlock: (block: T, ctx: RenderBlockContext) => HTMLElement;
|
|
325
330
|
containerTypes: readonly string[];
|
|
326
331
|
dropZoneHeight?: number;
|
|
332
|
+
dropZoneClassName?: string;
|
|
333
|
+
rootClassName?: string;
|
|
334
|
+
indentClassName?: string;
|
|
327
335
|
}
|
|
328
336
|
/**
|
|
329
337
|
* Recursive DOM tree builder. Creates the full tree DOM from blocks.
|
|
@@ -334,6 +342,7 @@ declare function renderTree<T extends BaseBlock>(blocks: T[], expandedMap: Recor
|
|
|
334
342
|
interface DropZoneOptions {
|
|
335
343
|
id: string;
|
|
336
344
|
height?: number;
|
|
345
|
+
className?: string;
|
|
337
346
|
}
|
|
338
347
|
declare function createDropZoneElement(options: DropZoneOptions): HTMLElement;
|
|
339
348
|
declare function setDropZoneActive(el: HTMLElement, active: boolean): void;
|
package/dist/index.d.ts
CHANGED
|
@@ -54,8 +54,13 @@ interface RenderBlockContext {
|
|
|
54
54
|
/** DefaultRenderer options */
|
|
55
55
|
interface DefaultRendererOptions<T extends BaseBlock = BaseBlock> {
|
|
56
56
|
container: HTMLElement;
|
|
57
|
+
containerTypes?: readonly string[];
|
|
57
58
|
renderBlock: (block: T, ctx: RenderBlockContext) => HTMLElement;
|
|
58
59
|
dropZoneHeight?: number;
|
|
60
|
+
dropZoneClassName?: string;
|
|
61
|
+
dropZoneActiveClassName?: string;
|
|
62
|
+
rootClassName?: string;
|
|
63
|
+
indentClassName?: string;
|
|
59
64
|
animateExpand?: boolean;
|
|
60
65
|
}
|
|
61
66
|
/** Cleanup function */
|
|
@@ -324,6 +329,9 @@ interface TreeRendererOptions<T extends BaseBlock> {
|
|
|
324
329
|
renderBlock: (block: T, ctx: RenderBlockContext) => HTMLElement;
|
|
325
330
|
containerTypes: readonly string[];
|
|
326
331
|
dropZoneHeight?: number;
|
|
332
|
+
dropZoneClassName?: string;
|
|
333
|
+
rootClassName?: string;
|
|
334
|
+
indentClassName?: string;
|
|
327
335
|
}
|
|
328
336
|
/**
|
|
329
337
|
* Recursive DOM tree builder. Creates the full tree DOM from blocks.
|
|
@@ -334,6 +342,7 @@ declare function renderTree<T extends BaseBlock>(blocks: T[], expandedMap: Recor
|
|
|
334
342
|
interface DropZoneOptions {
|
|
335
343
|
id: string;
|
|
336
344
|
height?: number;
|
|
345
|
+
className?: string;
|
|
337
346
|
}
|
|
338
347
|
declare function createDropZoneElement(options: DropZoneOptions): HTMLElement;
|
|
339
348
|
declare function setDropZoneActive(el: HTMLElement, active: boolean): void;
|
package/dist/index.js
CHANGED
|
@@ -55,6 +55,8 @@ var DragOverlay = class {
|
|
|
55
55
|
const overlay = document.createElement("div");
|
|
56
56
|
overlay.setAttribute("data-drag-overlay", "true");
|
|
57
57
|
overlay.style.position = "fixed";
|
|
58
|
+
overlay.style.left = "0";
|
|
59
|
+
overlay.style.top = "0";
|
|
58
60
|
overlay.style.zIndex = "9999";
|
|
59
61
|
overlay.style.pointerEvents = "none";
|
|
60
62
|
overlay.style.opacity = "0.7";
|
|
@@ -380,11 +382,11 @@ function createBlockTreeController(options = {}) {
|
|
|
380
382
|
const draggableElements = /* @__PURE__ */ new Map();
|
|
381
383
|
const dropZoneElements = /* @__PURE__ */ new Map();
|
|
382
384
|
let snapshotRects = null;
|
|
385
|
+
let dragFromPosition = null;
|
|
383
386
|
const selectedIds = /* @__PURE__ */ new Set();
|
|
384
387
|
let lastSelectedId = null;
|
|
385
388
|
const activeSensors = [];
|
|
386
|
-
|
|
387
|
-
let overlayRenderer = null;
|
|
389
|
+
let overlay = new DragOverlay();
|
|
388
390
|
let history = null;
|
|
389
391
|
tree.on("blocks:change", (blocks) => {
|
|
390
392
|
onChange?.(blocks);
|
|
@@ -417,18 +419,26 @@ function createBlockTreeController(options = {}) {
|
|
|
417
419
|
const sensorCallbacks = {
|
|
418
420
|
onDragStart(blockId, x, y) {
|
|
419
421
|
const draggedIds = selectedIds.has(blockId) && selectedIds.size > 1 ? [...selectedIds] : [blockId];
|
|
422
|
+
const blocks = tree.getBlocks();
|
|
423
|
+
const dragBlock = blocks.find((b) => b.id === blockId);
|
|
424
|
+
if (dragBlock) {
|
|
425
|
+
const siblings = blocks.filter((b) => b.parentId === dragBlock.parentId);
|
|
426
|
+
dragFromPosition = {
|
|
427
|
+
parentId: dragBlock.parentId,
|
|
428
|
+
index: siblings.findIndex((b) => b.id === blockId)
|
|
429
|
+
};
|
|
430
|
+
}
|
|
420
431
|
const started = tree.startDrag(blockId, draggedIds);
|
|
421
|
-
if (!started)
|
|
432
|
+
if (!started) {
|
|
433
|
+
dragFromPosition = null;
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
422
436
|
stickyCollision.reset();
|
|
423
437
|
snapshotRects = measureDropZoneRects(dropZoneElements);
|
|
424
438
|
const block = tree.getBlock(blockId);
|
|
425
439
|
const el = draggableElements.get(blockId);
|
|
426
440
|
if (block && el) {
|
|
427
|
-
|
|
428
|
-
overlay.show(block, el, x, y);
|
|
429
|
-
} else {
|
|
430
|
-
overlay.show(block, el, x, y);
|
|
431
|
-
}
|
|
441
|
+
overlay.show(block, el, x, y);
|
|
432
442
|
}
|
|
433
443
|
emitter.emit("drag:statechange", getDragState());
|
|
434
444
|
},
|
|
@@ -439,22 +449,35 @@ function createBlockTreeController(options = {}) {
|
|
|
439
449
|
if (!detector) return;
|
|
440
450
|
const targetZone = detectCollision(detector, snapshotRects, x, y);
|
|
441
451
|
if (targetZone) {
|
|
452
|
+
const prevHover = tree.getHoverZone();
|
|
442
453
|
tree.updateDrag(targetZone);
|
|
454
|
+
if (tree.getHoverZone() !== prevHover) {
|
|
455
|
+
emitter.emit("drag:statechange", getDragState());
|
|
456
|
+
}
|
|
443
457
|
}
|
|
444
458
|
},
|
|
445
459
|
onDragEnd(_x, _y) {
|
|
460
|
+
const draggedBlockIds = selectedIds.size > 1 ? [...selectedIds] : [];
|
|
461
|
+
const activeDragId = tree.getActiveId();
|
|
446
462
|
const result = tree.endDrag();
|
|
447
463
|
overlay.hide();
|
|
448
464
|
snapshotRects = null;
|
|
449
|
-
if (result && callbacks?.onBlockMove) {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
465
|
+
if (result && callbacks?.onBlockMove && activeDragId) {
|
|
466
|
+
const movedBlock = result.blocks.find((b) => b.id === activeDragId);
|
|
467
|
+
const movedIds = draggedBlockIds.length > 0 ? draggedBlockIds : activeDragId ? [activeDragId] : [];
|
|
468
|
+
if (movedBlock) {
|
|
469
|
+
const siblings = result.blocks.filter((b) => b.parentId === movedBlock.parentId);
|
|
470
|
+
const toIndex = siblings.findIndex((b) => b.id === activeDragId);
|
|
471
|
+
callbacks.onBlockMove({
|
|
472
|
+
block: movedBlock,
|
|
473
|
+
from: dragFromPosition ?? { parentId: null, index: 0 },
|
|
474
|
+
to: { parentId: movedBlock.parentId, index: toIndex },
|
|
475
|
+
blocks: result.blocks,
|
|
476
|
+
movedIds
|
|
477
|
+
});
|
|
478
|
+
}
|
|
457
479
|
}
|
|
480
|
+
dragFromPosition = null;
|
|
458
481
|
emitter.emit("drag:statechange", getDragState());
|
|
459
482
|
emitter.emit("render", tree.getBlocks(), tree.getExpandedMap());
|
|
460
483
|
},
|
|
@@ -658,7 +681,7 @@ function createBlockTreeController(options = {}) {
|
|
|
658
681
|
return emitter.on(event, handler);
|
|
659
682
|
},
|
|
660
683
|
setOverlayRenderer(render) {
|
|
661
|
-
|
|
684
|
+
overlay = new DragOverlay({ renderOverlay: render });
|
|
662
685
|
},
|
|
663
686
|
getTree: () => tree,
|
|
664
687
|
destroy() {
|
|
@@ -775,14 +798,18 @@ var VirtualScroller = class {
|
|
|
775
798
|
|
|
776
799
|
// src/renderer/drop-zone.ts
|
|
777
800
|
function createDropZoneElement(options) {
|
|
778
|
-
const { id, height = 4 } = options;
|
|
801
|
+
const { id, height = 4, className } = options;
|
|
779
802
|
const el = createElement("div");
|
|
780
803
|
setDataAttributes(el, {
|
|
781
804
|
"zone-id": id
|
|
782
805
|
});
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
806
|
+
if (className) {
|
|
807
|
+
el.className = className;
|
|
808
|
+
} else {
|
|
809
|
+
el.style.height = `${height}px`;
|
|
810
|
+
el.style.minHeight = `${height}px`;
|
|
811
|
+
el.style.transition = "background-color 150ms ease";
|
|
812
|
+
}
|
|
786
813
|
return el;
|
|
787
814
|
}
|
|
788
815
|
function setDropZoneActive(el, active) {
|
|
@@ -791,29 +818,32 @@ function setDropZoneActive(el, active) {
|
|
|
791
818
|
|
|
792
819
|
// src/renderer/tree-renderer.ts
|
|
793
820
|
function renderTree(blocks, expandedMap, controller, options, parentId = null, depth = 0) {
|
|
794
|
-
const { renderBlock, containerTypes, dropZoneHeight } = options;
|
|
821
|
+
const { renderBlock, containerTypes, dropZoneHeight, dropZoneClassName, rootClassName, indentClassName } = options;
|
|
795
822
|
const container = createElement("div", {
|
|
796
823
|
role: parentId === null ? "tree" : "group"
|
|
797
824
|
});
|
|
798
825
|
if (parentId === null) {
|
|
799
826
|
container.setAttribute("data-dnd-tree-root", "true");
|
|
827
|
+
if (rootClassName) container.className = rootClassName;
|
|
828
|
+
} else {
|
|
829
|
+
if (indentClassName) container.className = indentClassName;
|
|
800
830
|
}
|
|
801
831
|
const children = blocks.filter((b) => b.parentId === parentId);
|
|
802
832
|
const activeId = controller.getDragState().activeId;
|
|
803
833
|
const selectedIds = controller.getSelectedIds();
|
|
804
834
|
const index = controller.getTree().getBlockIndex();
|
|
805
835
|
if (parentId !== null) {
|
|
806
|
-
const startZone = createDropZoneElement({ id: `into-${parentId}`, height: dropZoneHeight });
|
|
836
|
+
const startZone = createDropZoneElement({ id: `into-${parentId}`, height: dropZoneHeight, className: dropZoneClassName });
|
|
807
837
|
controller.registerDropZone(`into-${parentId}`, startZone);
|
|
808
838
|
container.appendChild(startZone);
|
|
809
839
|
} else {
|
|
810
|
-
const rootStart = createDropZoneElement({ id: "root-start", height: dropZoneHeight });
|
|
840
|
+
const rootStart = createDropZoneElement({ id: "root-start", height: dropZoneHeight, className: dropZoneClassName });
|
|
811
841
|
controller.registerDropZone("root-start", rootStart);
|
|
812
842
|
container.appendChild(rootStart);
|
|
813
843
|
}
|
|
814
844
|
for (const block of children) {
|
|
815
845
|
if (block.id === activeId) continue;
|
|
816
|
-
const beforeZone = createDropZoneElement({ id: `before-${block.id}`, height: dropZoneHeight });
|
|
846
|
+
const beforeZone = createDropZoneElement({ id: `before-${block.id}`, height: dropZoneHeight, className: dropZoneClassName });
|
|
817
847
|
controller.registerDropZone(`before-${block.id}`, beforeZone);
|
|
818
848
|
container.appendChild(beforeZone);
|
|
819
849
|
const isContainer = containerTypes.includes(block.type);
|
|
@@ -849,11 +879,11 @@ function renderTree(blocks, expandedMap, controller, options, parentId = null, d
|
|
|
849
879
|
container.appendChild(blockEl);
|
|
850
880
|
}
|
|
851
881
|
if (parentId !== null) {
|
|
852
|
-
const endZone = createDropZoneElement({ id: `end-${parentId}`, height: dropZoneHeight });
|
|
882
|
+
const endZone = createDropZoneElement({ id: `end-${parentId}`, height: dropZoneHeight, className: dropZoneClassName });
|
|
853
883
|
controller.registerDropZone(`end-${parentId}`, endZone);
|
|
854
884
|
container.appendChild(endZone);
|
|
855
885
|
} else {
|
|
856
|
-
const rootEnd = createDropZoneElement({ id: "root-end", height: dropZoneHeight });
|
|
886
|
+
const rootEnd = createDropZoneElement({ id: "root-end", height: dropZoneHeight, className: dropZoneClassName });
|
|
857
887
|
controller.registerDropZone("root-end", rootEnd);
|
|
858
888
|
container.appendChild(rootEnd);
|
|
859
889
|
}
|
|
@@ -862,21 +892,47 @@ function renderTree(blocks, expandedMap, controller, options, parentId = null, d
|
|
|
862
892
|
|
|
863
893
|
// src/renderer/default-renderer.ts
|
|
864
894
|
function createDefaultRenderer(controller, options) {
|
|
865
|
-
const { container, renderBlock, dropZoneHeight,
|
|
866
|
-
const containerTypes = controller.getTree().containerTypes ?? [];
|
|
895
|
+
const { container, containerTypes: explicitContainerTypes, renderBlock, dropZoneHeight, dropZoneClassName, dropZoneActiveClassName } = options;
|
|
896
|
+
const containerTypes = explicitContainerTypes ?? controller.getTree().containerTypes ?? [];
|
|
897
|
+
const activeClasses = dropZoneActiveClassName?.split(/\s+/).filter(Boolean) ?? [];
|
|
867
898
|
function render(blocks, expandedMap) {
|
|
868
|
-
container.
|
|
899
|
+
while (container.firstChild) container.removeChild(container.firstChild);
|
|
869
900
|
const tree = renderTree(blocks, expandedMap, controller, {
|
|
870
901
|
renderBlock,
|
|
871
902
|
containerTypes,
|
|
872
|
-
dropZoneHeight
|
|
903
|
+
dropZoneHeight,
|
|
904
|
+
dropZoneClassName,
|
|
905
|
+
rootClassName: options.rootClassName,
|
|
906
|
+
indentClassName: options.indentClassName
|
|
873
907
|
});
|
|
874
908
|
container.appendChild(tree);
|
|
875
909
|
}
|
|
910
|
+
let activeZoneEl = null;
|
|
911
|
+
function onDragStateChange(state) {
|
|
912
|
+
if (activeZoneEl) {
|
|
913
|
+
setDropZoneActive(activeZoneEl, false);
|
|
914
|
+
if (activeClasses.length) {
|
|
915
|
+
activeZoneEl.classList.remove(...activeClasses);
|
|
916
|
+
}
|
|
917
|
+
activeZoneEl = null;
|
|
918
|
+
}
|
|
919
|
+
if (state.isDragging && state.hoverZone) {
|
|
920
|
+
const zoneEl = container.querySelector(`[data-zone-id="${state.hoverZone}"]`);
|
|
921
|
+
if (zoneEl) {
|
|
922
|
+
setDropZoneActive(zoneEl, true);
|
|
923
|
+
if (activeClasses.length) {
|
|
924
|
+
zoneEl.classList.add(...activeClasses);
|
|
925
|
+
}
|
|
926
|
+
activeZoneEl = zoneEl;
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
}
|
|
876
930
|
const unsubRender = controller.on("render", render);
|
|
931
|
+
const unsubDrag = controller.on("drag:statechange", onDragStateChange);
|
|
877
932
|
render(controller.getBlocks(), controller.getExpandedMap());
|
|
878
933
|
const renderer = () => {
|
|
879
934
|
unsubRender();
|
|
935
|
+
unsubDrag();
|
|
880
936
|
};
|
|
881
937
|
renderer.refresh = () => {
|
|
882
938
|
render(controller.getBlocks(), controller.getExpandedMap());
|
package/dist/index.mjs
CHANGED
|
@@ -54,6 +54,8 @@ var DragOverlay = class {
|
|
|
54
54
|
const overlay = document.createElement("div");
|
|
55
55
|
overlay.setAttribute("data-drag-overlay", "true");
|
|
56
56
|
overlay.style.position = "fixed";
|
|
57
|
+
overlay.style.left = "0";
|
|
58
|
+
overlay.style.top = "0";
|
|
57
59
|
overlay.style.zIndex = "9999";
|
|
58
60
|
overlay.style.pointerEvents = "none";
|
|
59
61
|
overlay.style.opacity = "0.7";
|
|
@@ -379,11 +381,11 @@ function createBlockTreeController(options = {}) {
|
|
|
379
381
|
const draggableElements = /* @__PURE__ */ new Map();
|
|
380
382
|
const dropZoneElements = /* @__PURE__ */ new Map();
|
|
381
383
|
let snapshotRects = null;
|
|
384
|
+
let dragFromPosition = null;
|
|
382
385
|
const selectedIds = /* @__PURE__ */ new Set();
|
|
383
386
|
let lastSelectedId = null;
|
|
384
387
|
const activeSensors = [];
|
|
385
|
-
|
|
386
|
-
let overlayRenderer = null;
|
|
388
|
+
let overlay = new DragOverlay();
|
|
387
389
|
let history = null;
|
|
388
390
|
tree.on("blocks:change", (blocks) => {
|
|
389
391
|
onChange?.(blocks);
|
|
@@ -416,18 +418,26 @@ function createBlockTreeController(options = {}) {
|
|
|
416
418
|
const sensorCallbacks = {
|
|
417
419
|
onDragStart(blockId, x, y) {
|
|
418
420
|
const draggedIds = selectedIds.has(blockId) && selectedIds.size > 1 ? [...selectedIds] : [blockId];
|
|
421
|
+
const blocks = tree.getBlocks();
|
|
422
|
+
const dragBlock = blocks.find((b) => b.id === blockId);
|
|
423
|
+
if (dragBlock) {
|
|
424
|
+
const siblings = blocks.filter((b) => b.parentId === dragBlock.parentId);
|
|
425
|
+
dragFromPosition = {
|
|
426
|
+
parentId: dragBlock.parentId,
|
|
427
|
+
index: siblings.findIndex((b) => b.id === blockId)
|
|
428
|
+
};
|
|
429
|
+
}
|
|
419
430
|
const started = tree.startDrag(blockId, draggedIds);
|
|
420
|
-
if (!started)
|
|
431
|
+
if (!started) {
|
|
432
|
+
dragFromPosition = null;
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
421
435
|
stickyCollision.reset();
|
|
422
436
|
snapshotRects = measureDropZoneRects(dropZoneElements);
|
|
423
437
|
const block = tree.getBlock(blockId);
|
|
424
438
|
const el = draggableElements.get(blockId);
|
|
425
439
|
if (block && el) {
|
|
426
|
-
|
|
427
|
-
overlay.show(block, el, x, y);
|
|
428
|
-
} else {
|
|
429
|
-
overlay.show(block, el, x, y);
|
|
430
|
-
}
|
|
440
|
+
overlay.show(block, el, x, y);
|
|
431
441
|
}
|
|
432
442
|
emitter.emit("drag:statechange", getDragState());
|
|
433
443
|
},
|
|
@@ -438,22 +448,35 @@ function createBlockTreeController(options = {}) {
|
|
|
438
448
|
if (!detector) return;
|
|
439
449
|
const targetZone = detectCollision(detector, snapshotRects, x, y);
|
|
440
450
|
if (targetZone) {
|
|
451
|
+
const prevHover = tree.getHoverZone();
|
|
441
452
|
tree.updateDrag(targetZone);
|
|
453
|
+
if (tree.getHoverZone() !== prevHover) {
|
|
454
|
+
emitter.emit("drag:statechange", getDragState());
|
|
455
|
+
}
|
|
442
456
|
}
|
|
443
457
|
},
|
|
444
458
|
onDragEnd(_x, _y) {
|
|
459
|
+
const draggedBlockIds = selectedIds.size > 1 ? [...selectedIds] : [];
|
|
460
|
+
const activeDragId = tree.getActiveId();
|
|
445
461
|
const result = tree.endDrag();
|
|
446
462
|
overlay.hide();
|
|
447
463
|
snapshotRects = null;
|
|
448
|
-
if (result && callbacks?.onBlockMove) {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
464
|
+
if (result && callbacks?.onBlockMove && activeDragId) {
|
|
465
|
+
const movedBlock = result.blocks.find((b) => b.id === activeDragId);
|
|
466
|
+
const movedIds = draggedBlockIds.length > 0 ? draggedBlockIds : activeDragId ? [activeDragId] : [];
|
|
467
|
+
if (movedBlock) {
|
|
468
|
+
const siblings = result.blocks.filter((b) => b.parentId === movedBlock.parentId);
|
|
469
|
+
const toIndex = siblings.findIndex((b) => b.id === activeDragId);
|
|
470
|
+
callbacks.onBlockMove({
|
|
471
|
+
block: movedBlock,
|
|
472
|
+
from: dragFromPosition ?? { parentId: null, index: 0 },
|
|
473
|
+
to: { parentId: movedBlock.parentId, index: toIndex },
|
|
474
|
+
blocks: result.blocks,
|
|
475
|
+
movedIds
|
|
476
|
+
});
|
|
477
|
+
}
|
|
456
478
|
}
|
|
479
|
+
dragFromPosition = null;
|
|
457
480
|
emitter.emit("drag:statechange", getDragState());
|
|
458
481
|
emitter.emit("render", tree.getBlocks(), tree.getExpandedMap());
|
|
459
482
|
},
|
|
@@ -657,7 +680,7 @@ function createBlockTreeController(options = {}) {
|
|
|
657
680
|
return emitter.on(event, handler);
|
|
658
681
|
},
|
|
659
682
|
setOverlayRenderer(render) {
|
|
660
|
-
|
|
683
|
+
overlay = new DragOverlay({ renderOverlay: render });
|
|
661
684
|
},
|
|
662
685
|
getTree: () => tree,
|
|
663
686
|
destroy() {
|
|
@@ -774,14 +797,18 @@ var VirtualScroller = class {
|
|
|
774
797
|
|
|
775
798
|
// src/renderer/drop-zone.ts
|
|
776
799
|
function createDropZoneElement(options) {
|
|
777
|
-
const { id, height = 4 } = options;
|
|
800
|
+
const { id, height = 4, className } = options;
|
|
778
801
|
const el = createElement("div");
|
|
779
802
|
setDataAttributes(el, {
|
|
780
803
|
"zone-id": id
|
|
781
804
|
});
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
805
|
+
if (className) {
|
|
806
|
+
el.className = className;
|
|
807
|
+
} else {
|
|
808
|
+
el.style.height = `${height}px`;
|
|
809
|
+
el.style.minHeight = `${height}px`;
|
|
810
|
+
el.style.transition = "background-color 150ms ease";
|
|
811
|
+
}
|
|
785
812
|
return el;
|
|
786
813
|
}
|
|
787
814
|
function setDropZoneActive(el, active) {
|
|
@@ -790,29 +817,32 @@ function setDropZoneActive(el, active) {
|
|
|
790
817
|
|
|
791
818
|
// src/renderer/tree-renderer.ts
|
|
792
819
|
function renderTree(blocks, expandedMap, controller, options, parentId = null, depth = 0) {
|
|
793
|
-
const { renderBlock, containerTypes, dropZoneHeight } = options;
|
|
820
|
+
const { renderBlock, containerTypes, dropZoneHeight, dropZoneClassName, rootClassName, indentClassName } = options;
|
|
794
821
|
const container = createElement("div", {
|
|
795
822
|
role: parentId === null ? "tree" : "group"
|
|
796
823
|
});
|
|
797
824
|
if (parentId === null) {
|
|
798
825
|
container.setAttribute("data-dnd-tree-root", "true");
|
|
826
|
+
if (rootClassName) container.className = rootClassName;
|
|
827
|
+
} else {
|
|
828
|
+
if (indentClassName) container.className = indentClassName;
|
|
799
829
|
}
|
|
800
830
|
const children = blocks.filter((b) => b.parentId === parentId);
|
|
801
831
|
const activeId = controller.getDragState().activeId;
|
|
802
832
|
const selectedIds = controller.getSelectedIds();
|
|
803
833
|
const index = controller.getTree().getBlockIndex();
|
|
804
834
|
if (parentId !== null) {
|
|
805
|
-
const startZone = createDropZoneElement({ id: `into-${parentId}`, height: dropZoneHeight });
|
|
835
|
+
const startZone = createDropZoneElement({ id: `into-${parentId}`, height: dropZoneHeight, className: dropZoneClassName });
|
|
806
836
|
controller.registerDropZone(`into-${parentId}`, startZone);
|
|
807
837
|
container.appendChild(startZone);
|
|
808
838
|
} else {
|
|
809
|
-
const rootStart = createDropZoneElement({ id: "root-start", height: dropZoneHeight });
|
|
839
|
+
const rootStart = createDropZoneElement({ id: "root-start", height: dropZoneHeight, className: dropZoneClassName });
|
|
810
840
|
controller.registerDropZone("root-start", rootStart);
|
|
811
841
|
container.appendChild(rootStart);
|
|
812
842
|
}
|
|
813
843
|
for (const block of children) {
|
|
814
844
|
if (block.id === activeId) continue;
|
|
815
|
-
const beforeZone = createDropZoneElement({ id: `before-${block.id}`, height: dropZoneHeight });
|
|
845
|
+
const beforeZone = createDropZoneElement({ id: `before-${block.id}`, height: dropZoneHeight, className: dropZoneClassName });
|
|
816
846
|
controller.registerDropZone(`before-${block.id}`, beforeZone);
|
|
817
847
|
container.appendChild(beforeZone);
|
|
818
848
|
const isContainer = containerTypes.includes(block.type);
|
|
@@ -848,11 +878,11 @@ function renderTree(blocks, expandedMap, controller, options, parentId = null, d
|
|
|
848
878
|
container.appendChild(blockEl);
|
|
849
879
|
}
|
|
850
880
|
if (parentId !== null) {
|
|
851
|
-
const endZone = createDropZoneElement({ id: `end-${parentId}`, height: dropZoneHeight });
|
|
881
|
+
const endZone = createDropZoneElement({ id: `end-${parentId}`, height: dropZoneHeight, className: dropZoneClassName });
|
|
852
882
|
controller.registerDropZone(`end-${parentId}`, endZone);
|
|
853
883
|
container.appendChild(endZone);
|
|
854
884
|
} else {
|
|
855
|
-
const rootEnd = createDropZoneElement({ id: "root-end", height: dropZoneHeight });
|
|
885
|
+
const rootEnd = createDropZoneElement({ id: "root-end", height: dropZoneHeight, className: dropZoneClassName });
|
|
856
886
|
controller.registerDropZone("root-end", rootEnd);
|
|
857
887
|
container.appendChild(rootEnd);
|
|
858
888
|
}
|
|
@@ -861,21 +891,47 @@ function renderTree(blocks, expandedMap, controller, options, parentId = null, d
|
|
|
861
891
|
|
|
862
892
|
// src/renderer/default-renderer.ts
|
|
863
893
|
function createDefaultRenderer(controller, options) {
|
|
864
|
-
const { container, renderBlock, dropZoneHeight,
|
|
865
|
-
const containerTypes = controller.getTree().containerTypes ?? [];
|
|
894
|
+
const { container, containerTypes: explicitContainerTypes, renderBlock, dropZoneHeight, dropZoneClassName, dropZoneActiveClassName } = options;
|
|
895
|
+
const containerTypes = explicitContainerTypes ?? controller.getTree().containerTypes ?? [];
|
|
896
|
+
const activeClasses = dropZoneActiveClassName?.split(/\s+/).filter(Boolean) ?? [];
|
|
866
897
|
function render(blocks, expandedMap) {
|
|
867
|
-
container.
|
|
898
|
+
while (container.firstChild) container.removeChild(container.firstChild);
|
|
868
899
|
const tree = renderTree(blocks, expandedMap, controller, {
|
|
869
900
|
renderBlock,
|
|
870
901
|
containerTypes,
|
|
871
|
-
dropZoneHeight
|
|
902
|
+
dropZoneHeight,
|
|
903
|
+
dropZoneClassName,
|
|
904
|
+
rootClassName: options.rootClassName,
|
|
905
|
+
indentClassName: options.indentClassName
|
|
872
906
|
});
|
|
873
907
|
container.appendChild(tree);
|
|
874
908
|
}
|
|
909
|
+
let activeZoneEl = null;
|
|
910
|
+
function onDragStateChange(state) {
|
|
911
|
+
if (activeZoneEl) {
|
|
912
|
+
setDropZoneActive(activeZoneEl, false);
|
|
913
|
+
if (activeClasses.length) {
|
|
914
|
+
activeZoneEl.classList.remove(...activeClasses);
|
|
915
|
+
}
|
|
916
|
+
activeZoneEl = null;
|
|
917
|
+
}
|
|
918
|
+
if (state.isDragging && state.hoverZone) {
|
|
919
|
+
const zoneEl = container.querySelector(`[data-zone-id="${state.hoverZone}"]`);
|
|
920
|
+
if (zoneEl) {
|
|
921
|
+
setDropZoneActive(zoneEl, true);
|
|
922
|
+
if (activeClasses.length) {
|
|
923
|
+
zoneEl.classList.add(...activeClasses);
|
|
924
|
+
}
|
|
925
|
+
activeZoneEl = zoneEl;
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
}
|
|
875
929
|
const unsubRender = controller.on("render", render);
|
|
930
|
+
const unsubDrag = controller.on("drag:statechange", onDragStateChange);
|
|
876
931
|
render(controller.getBlocks(), controller.getExpandedMap());
|
|
877
932
|
const renderer = () => {
|
|
878
933
|
unsubRender();
|
|
934
|
+
unsubDrag();
|
|
879
935
|
};
|
|
880
936
|
renderer.refresh = () => {
|
|
881
937
|
render(controller.getBlocks(), controller.getExpandedMap());
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dnd-block-tree/vanilla",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "Vanilla JS/TS adapter for dnd-block-tree — zero framework dependencies",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -50,5 +50,9 @@
|
|
|
50
50
|
"vanilla",
|
|
51
51
|
"headless",
|
|
52
52
|
"hierarchical"
|
|
53
|
-
]
|
|
53
|
+
],
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public",
|
|
56
|
+
"provenance": true
|
|
57
|
+
}
|
|
54
58
|
}
|