@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 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
- const overlay = new DragOverlay();
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) return;
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
- if (overlayRenderer) {
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
- callbacks.onBlockMove({
451
- block: tree.getBlock(tree.getBlocks()[0]?.id),
452
- from: { parentId: null, index: 0 },
453
- to: { parentId: null, index: 0 },
454
- blocks: result.blocks,
455
- movedIds: []
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
- overlayRenderer = render;
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
- el.style.height = `${height}px`;
784
- el.style.minHeight = `${height}px`;
785
- el.style.transition = "background-color 150ms ease";
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, animateExpand } = options;
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.innerHTML = "";
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
- const overlay = new DragOverlay();
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) return;
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
- if (overlayRenderer) {
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
- callbacks.onBlockMove({
450
- block: tree.getBlock(tree.getBlocks()[0]?.id),
451
- from: { parentId: null, index: 0 },
452
- to: { parentId: null, index: 0 },
453
- blocks: result.blocks,
454
- movedIds: []
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
- overlayRenderer = render;
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
- el.style.height = `${height}px`;
783
- el.style.minHeight = `${height}px`;
784
- el.style.transition = "background-color 150ms ease";
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, animateExpand } = options;
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.innerHTML = "";
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.0",
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
  }