@headless-tree/core 0.0.0-20260107224057 → 0.0.0-20260108010329

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/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @headless-tree/core
2
2
 
3
- ## 0.0.0-20260107224057
3
+ ## 0.0.0-20260108010329
4
4
 
5
5
  ### Patch Changes
6
6
 
package/dist/index.d.mts CHANGED
@@ -68,6 +68,8 @@ type DragAndDropFeatureDef<T> = {
68
68
  onCompleteForeignDrop?: (items: ItemInstance<T>[]) => void;
69
69
  /** When dragging for this many ms on a closed folder, the folder will automatically open. Set to zero to disable. */
70
70
  openOnDropDelay?: number;
71
+ /** If true, `item.getProps()` will not include drag event handlers. Use `item.getDragHandleProps()` on the handler element. */
72
+ seperateDragHandle?: boolean;
71
73
  };
72
74
  treeInstance: {
73
75
  getDragTarget: () => DragTarget<T> | null;
@@ -84,6 +86,9 @@ type DragAndDropFeatureDef<T> = {
84
86
  isDragTargetAbove: () => boolean;
85
87
  isDragTargetBelow: () => boolean;
86
88
  isDraggingOver: () => boolean;
89
+ /** Note that `item.getProps()` already passes in all drag event handlers by default. Set `seperateDragHandle` to true to
90
+ * disable the default behavior and use this on the handler element instead. */
91
+ getDragHandleProps: () => Record<string, any>;
87
92
  };
88
93
  hotkeys: never;
89
94
  };
@@ -422,6 +427,7 @@ interface PropMemoizationDataRef {
422
427
  memo?: {
423
428
  tree?: Record<string, any>;
424
429
  item?: Record<string, any>;
430
+ drag?: Record<string, any>;
425
431
  search?: Record<string, any>;
426
432
  rename?: Record<string, any>;
427
433
  };
package/dist/index.d.ts CHANGED
@@ -68,6 +68,8 @@ type DragAndDropFeatureDef<T> = {
68
68
  onCompleteForeignDrop?: (items: ItemInstance<T>[]) => void;
69
69
  /** When dragging for this many ms on a closed folder, the folder will automatically open. Set to zero to disable. */
70
70
  openOnDropDelay?: number;
71
+ /** If true, `item.getProps()` will not include drag event handlers. Use `item.getDragHandleProps()` on the handler element. */
72
+ seperateDragHandle?: boolean;
71
73
  };
72
74
  treeInstance: {
73
75
  getDragTarget: () => DragTarget<T> | null;
@@ -84,6 +86,9 @@ type DragAndDropFeatureDef<T> = {
84
86
  isDragTargetAbove: () => boolean;
85
87
  isDragTargetBelow: () => boolean;
86
88
  isDraggingOver: () => boolean;
89
+ /** Note that `item.getProps()` already passes in all drag event handlers by default. Set `seperateDragHandle` to true to
90
+ * disable the default behavior and use this on the handler element instead. */
91
+ getDragHandleProps: () => Record<string, any>;
87
92
  };
88
93
  hotkeys: never;
89
94
  };
@@ -422,6 +427,7 @@ interface PropMemoizationDataRef {
422
427
  memo?: {
423
428
  tree?: Record<string, any>;
424
429
  item?: Record<string, any>;
430
+ drag?: Record<string, any>;
425
431
  search?: Record<string, any>;
426
432
  rename?: Record<string, any>;
427
433
  };
package/dist/index.js CHANGED
@@ -1620,36 +1620,8 @@ var dragAndDropFeature = {
1620
1620
  }
1621
1621
  },
1622
1622
  itemInstance: {
1623
- getProps: ({ tree, item, prev }) => __spreadProps(__spreadValues({}, prev == null ? void 0 : prev()), {
1624
- draggable: true,
1623
+ getProps: ({ tree, item, prev }) => __spreadProps(__spreadValues(__spreadValues({}, prev == null ? void 0 : prev()), tree.getConfig().seperateDragHandle ? {} : item.getDragHandleProps()), {
1625
1624
  onDragEnter: (e) => e.preventDefault(),
1626
- onDragStart: (e) => {
1627
- var _a, _b, _c, _d;
1628
- const selectedItems = tree.getSelectedItems ? tree.getSelectedItems() : [tree.getFocusedItem()];
1629
- const items = selectedItems.includes(item) ? selectedItems : [item];
1630
- const config = tree.getConfig();
1631
- if (!selectedItems.includes(item)) {
1632
- (_a = tree.setSelectedItems) == null ? void 0 : _a.call(tree, [item.getItemMeta().itemId]);
1633
- }
1634
- if (!((_c = (_b = config.canDrag) == null ? void 0 : _b.call(config, items)) != null ? _c : true)) {
1635
- e.preventDefault();
1636
- return;
1637
- }
1638
- if (config.setDragImage) {
1639
- const { imgElement, xOffset, yOffset } = config.setDragImage(items);
1640
- (_d = e.dataTransfer) == null ? void 0 : _d.setDragImage(imgElement, xOffset != null ? xOffset : 0, yOffset != null ? yOffset : 0);
1641
- }
1642
- if (config.createForeignDragObject && e.dataTransfer) {
1643
- const { format, data, dropEffect, effectAllowed } = config.createForeignDragObject(items);
1644
- e.dataTransfer.setData(format, data);
1645
- if (dropEffect) e.dataTransfer.dropEffect = dropEffect;
1646
- if (effectAllowed) e.dataTransfer.effectAllowed = effectAllowed;
1647
- }
1648
- tree.applySubStateUpdate("dnd", {
1649
- draggedItems: items,
1650
- draggingOverItem: tree.getFocusedItem()
1651
- });
1652
- },
1653
1625
  onDragOver: (e) => {
1654
1626
  var _a, _b, _c;
1655
1627
  e.stopPropagation();
@@ -1693,19 +1665,6 @@ var dragAndDropFeature = {
1693
1665
  }));
1694
1666
  }, 100);
1695
1667
  },
1696
- onDragEnd: (e) => {
1697
- var _a, _b;
1698
- const { onCompleteForeignDrop, canDragForeignDragObjectOver } = tree.getConfig();
1699
- const draggedItems = (_a = tree.getState().dnd) == null ? void 0 : _a.draggedItems;
1700
- if (((_b = e.dataTransfer) == null ? void 0 : _b.dropEffect) === "none" || !draggedItems) {
1701
- return;
1702
- }
1703
- const target = getDragTarget(e, item, tree, false);
1704
- if (canDragForeignDragObjectOver && e.dataTransfer && !canDragForeignDragObjectOver(e.dataTransfer, target)) {
1705
- return;
1706
- }
1707
- onCompleteForeignDrop == null ? void 0 : onCompleteForeignDrop(draggedItems);
1708
- },
1709
1668
  onDrop: (e) => __async(null, null, function* () {
1710
1669
  var _a, _b, _c;
1711
1670
  e.stopPropagation();
@@ -1734,6 +1693,49 @@ var dragAndDropFeature = {
1734
1693
  tree.updateDomFocus();
1735
1694
  })
1736
1695
  }),
1696
+ getDragHandleProps: ({ tree, item, prev }) => __spreadProps(__spreadValues({}, prev == null ? void 0 : prev()), {
1697
+ draggable: true,
1698
+ onDragStart: (e) => {
1699
+ var _a, _b, _c, _d;
1700
+ const selectedItems = tree.getSelectedItems ? tree.getSelectedItems() : [tree.getFocusedItem()];
1701
+ const items = selectedItems.includes(item) ? selectedItems : [item];
1702
+ const config = tree.getConfig();
1703
+ if (!selectedItems.includes(item)) {
1704
+ (_a = tree.setSelectedItems) == null ? void 0 : _a.call(tree, [item.getItemMeta().itemId]);
1705
+ }
1706
+ if (!((_c = (_b = config.canDrag) == null ? void 0 : _b.call(config, items)) != null ? _c : true)) {
1707
+ e.preventDefault();
1708
+ return;
1709
+ }
1710
+ if (config.setDragImage) {
1711
+ const { imgElement, xOffset, yOffset } = config.setDragImage(items);
1712
+ (_d = e.dataTransfer) == null ? void 0 : _d.setDragImage(imgElement, xOffset != null ? xOffset : 0, yOffset != null ? yOffset : 0);
1713
+ }
1714
+ if (config.createForeignDragObject && e.dataTransfer) {
1715
+ const { format, data, dropEffect, effectAllowed } = config.createForeignDragObject(items);
1716
+ e.dataTransfer.setData(format, data);
1717
+ if (dropEffect) e.dataTransfer.dropEffect = dropEffect;
1718
+ if (effectAllowed) e.dataTransfer.effectAllowed = effectAllowed;
1719
+ }
1720
+ tree.applySubStateUpdate("dnd", {
1721
+ draggedItems: items,
1722
+ draggingOverItem: tree.getFocusedItem()
1723
+ });
1724
+ },
1725
+ onDragEnd: (e) => {
1726
+ var _a, _b;
1727
+ const { onCompleteForeignDrop, canDragForeignDragObjectOver } = tree.getConfig();
1728
+ const draggedItems = (_a = tree.getState().dnd) == null ? void 0 : _a.draggedItems;
1729
+ if (((_b = e.dataTransfer) == null ? void 0 : _b.dropEffect) === "none" || !draggedItems) {
1730
+ return;
1731
+ }
1732
+ const target = getDragTarget(e, item, tree, false);
1733
+ if (canDragForeignDragObjectOver && e.dataTransfer && !canDragForeignDragObjectOver(e.dataTransfer, target)) {
1734
+ return;
1735
+ }
1736
+ onCompleteForeignDrop == null ? void 0 : onCompleteForeignDrop(draggedItems);
1737
+ }
1738
+ }),
1737
1739
  isDragTarget: ({ tree, item }) => {
1738
1740
  const target = tree.getDragTarget();
1739
1741
  return target ? target.item.getId() === item.getId() : false;
@@ -2308,6 +2310,14 @@ var propMemoizationFeature = {
2308
2310
  (_e = (_d = dataRef.current.memo).item) != null ? _e : _d.item = {};
2309
2311
  return memoize(props, dataRef.current.memo.item);
2310
2312
  },
2313
+ getDragHandleProps: ({ item, prev }) => {
2314
+ var _a, _b, _c, _d, _e;
2315
+ const dataRef = item.getDataRef();
2316
+ const props = (_a = prev == null ? void 0 : prev()) != null ? _a : {};
2317
+ (_c = (_b = dataRef.current).memo) != null ? _c : _b.memo = {};
2318
+ (_e = (_d = dataRef.current.memo).drag) != null ? _e : _d.drag = {};
2319
+ return memoize(props, dataRef.current.memo.drag);
2320
+ },
2311
2321
  getRenameInputProps: ({ item, prev }) => {
2312
2322
  var _a, _b, _c, _d, _e;
2313
2323
  const dataRef = item.getDataRef();
package/dist/index.mjs CHANGED
@@ -1576,36 +1576,8 @@ var dragAndDropFeature = {
1576
1576
  }
1577
1577
  },
1578
1578
  itemInstance: {
1579
- getProps: ({ tree, item, prev }) => __spreadProps(__spreadValues({}, prev == null ? void 0 : prev()), {
1580
- draggable: true,
1579
+ getProps: ({ tree, item, prev }) => __spreadProps(__spreadValues(__spreadValues({}, prev == null ? void 0 : prev()), tree.getConfig().seperateDragHandle ? {} : item.getDragHandleProps()), {
1581
1580
  onDragEnter: (e) => e.preventDefault(),
1582
- onDragStart: (e) => {
1583
- var _a, _b, _c, _d;
1584
- const selectedItems = tree.getSelectedItems ? tree.getSelectedItems() : [tree.getFocusedItem()];
1585
- const items = selectedItems.includes(item) ? selectedItems : [item];
1586
- const config = tree.getConfig();
1587
- if (!selectedItems.includes(item)) {
1588
- (_a = tree.setSelectedItems) == null ? void 0 : _a.call(tree, [item.getItemMeta().itemId]);
1589
- }
1590
- if (!((_c = (_b = config.canDrag) == null ? void 0 : _b.call(config, items)) != null ? _c : true)) {
1591
- e.preventDefault();
1592
- return;
1593
- }
1594
- if (config.setDragImage) {
1595
- const { imgElement, xOffset, yOffset } = config.setDragImage(items);
1596
- (_d = e.dataTransfer) == null ? void 0 : _d.setDragImage(imgElement, xOffset != null ? xOffset : 0, yOffset != null ? yOffset : 0);
1597
- }
1598
- if (config.createForeignDragObject && e.dataTransfer) {
1599
- const { format, data, dropEffect, effectAllowed } = config.createForeignDragObject(items);
1600
- e.dataTransfer.setData(format, data);
1601
- if (dropEffect) e.dataTransfer.dropEffect = dropEffect;
1602
- if (effectAllowed) e.dataTransfer.effectAllowed = effectAllowed;
1603
- }
1604
- tree.applySubStateUpdate("dnd", {
1605
- draggedItems: items,
1606
- draggingOverItem: tree.getFocusedItem()
1607
- });
1608
- },
1609
1581
  onDragOver: (e) => {
1610
1582
  var _a, _b, _c;
1611
1583
  e.stopPropagation();
@@ -1649,19 +1621,6 @@ var dragAndDropFeature = {
1649
1621
  }));
1650
1622
  }, 100);
1651
1623
  },
1652
- onDragEnd: (e) => {
1653
- var _a, _b;
1654
- const { onCompleteForeignDrop, canDragForeignDragObjectOver } = tree.getConfig();
1655
- const draggedItems = (_a = tree.getState().dnd) == null ? void 0 : _a.draggedItems;
1656
- if (((_b = e.dataTransfer) == null ? void 0 : _b.dropEffect) === "none" || !draggedItems) {
1657
- return;
1658
- }
1659
- const target = getDragTarget(e, item, tree, false);
1660
- if (canDragForeignDragObjectOver && e.dataTransfer && !canDragForeignDragObjectOver(e.dataTransfer, target)) {
1661
- return;
1662
- }
1663
- onCompleteForeignDrop == null ? void 0 : onCompleteForeignDrop(draggedItems);
1664
- },
1665
1624
  onDrop: (e) => __async(null, null, function* () {
1666
1625
  var _a, _b, _c;
1667
1626
  e.stopPropagation();
@@ -1690,6 +1649,49 @@ var dragAndDropFeature = {
1690
1649
  tree.updateDomFocus();
1691
1650
  })
1692
1651
  }),
1652
+ getDragHandleProps: ({ tree, item, prev }) => __spreadProps(__spreadValues({}, prev == null ? void 0 : prev()), {
1653
+ draggable: true,
1654
+ onDragStart: (e) => {
1655
+ var _a, _b, _c, _d;
1656
+ const selectedItems = tree.getSelectedItems ? tree.getSelectedItems() : [tree.getFocusedItem()];
1657
+ const items = selectedItems.includes(item) ? selectedItems : [item];
1658
+ const config = tree.getConfig();
1659
+ if (!selectedItems.includes(item)) {
1660
+ (_a = tree.setSelectedItems) == null ? void 0 : _a.call(tree, [item.getItemMeta().itemId]);
1661
+ }
1662
+ if (!((_c = (_b = config.canDrag) == null ? void 0 : _b.call(config, items)) != null ? _c : true)) {
1663
+ e.preventDefault();
1664
+ return;
1665
+ }
1666
+ if (config.setDragImage) {
1667
+ const { imgElement, xOffset, yOffset } = config.setDragImage(items);
1668
+ (_d = e.dataTransfer) == null ? void 0 : _d.setDragImage(imgElement, xOffset != null ? xOffset : 0, yOffset != null ? yOffset : 0);
1669
+ }
1670
+ if (config.createForeignDragObject && e.dataTransfer) {
1671
+ const { format, data, dropEffect, effectAllowed } = config.createForeignDragObject(items);
1672
+ e.dataTransfer.setData(format, data);
1673
+ if (dropEffect) e.dataTransfer.dropEffect = dropEffect;
1674
+ if (effectAllowed) e.dataTransfer.effectAllowed = effectAllowed;
1675
+ }
1676
+ tree.applySubStateUpdate("dnd", {
1677
+ draggedItems: items,
1678
+ draggingOverItem: tree.getFocusedItem()
1679
+ });
1680
+ },
1681
+ onDragEnd: (e) => {
1682
+ var _a, _b;
1683
+ const { onCompleteForeignDrop, canDragForeignDragObjectOver } = tree.getConfig();
1684
+ const draggedItems = (_a = tree.getState().dnd) == null ? void 0 : _a.draggedItems;
1685
+ if (((_b = e.dataTransfer) == null ? void 0 : _b.dropEffect) === "none" || !draggedItems) {
1686
+ return;
1687
+ }
1688
+ const target = getDragTarget(e, item, tree, false);
1689
+ if (canDragForeignDragObjectOver && e.dataTransfer && !canDragForeignDragObjectOver(e.dataTransfer, target)) {
1690
+ return;
1691
+ }
1692
+ onCompleteForeignDrop == null ? void 0 : onCompleteForeignDrop(draggedItems);
1693
+ }
1694
+ }),
1693
1695
  isDragTarget: ({ tree, item }) => {
1694
1696
  const target = tree.getDragTarget();
1695
1697
  return target ? target.item.getId() === item.getId() : false;
@@ -2264,6 +2266,14 @@ var propMemoizationFeature = {
2264
2266
  (_e = (_d = dataRef.current.memo).item) != null ? _e : _d.item = {};
2265
2267
  return memoize(props, dataRef.current.memo.item);
2266
2268
  },
2269
+ getDragHandleProps: ({ item, prev }) => {
2270
+ var _a, _b, _c, _d, _e;
2271
+ const dataRef = item.getDataRef();
2272
+ const props = (_a = prev == null ? void 0 : prev()) != null ? _a : {};
2273
+ (_c = (_b = dataRef.current).memo) != null ? _c : _b.memo = {};
2274
+ (_e = (_d = dataRef.current.memo).drag) != null ? _e : _d.drag = {};
2275
+ return memoize(props, dataRef.current.memo.drag);
2276
+ },
2267
2277
  getRenameInputProps: ({ item, prev }) => {
2268
2278
  var _a, _b, _c, _d, _e;
2269
2279
  const dataRef = item.getDataRef();
package/package.json CHANGED
@@ -13,7 +13,7 @@
13
13
  "checkbox",
14
14
  "hook"
15
15
  ],
16
- "version": "0.0.0-20260107224057",
16
+ "version": "0.0.0-20260108010329",
17
17
  "main": "dist/index.d.ts",
18
18
  "module": "dist/index.mjs",
19
19
  "types": "dist/index.d.mts",
@@ -729,6 +729,54 @@ describe("core-feature/drag-and-drop", () => {
729
729
  });
730
730
  });
731
731
 
732
+ describe("seperateDragHandle", () => {
733
+ it("includes all drag handlers in getProps() when seperateDragHandle is false", () => {
734
+ const props = tree.instance.getItemInstance("x111").getProps();
735
+ expect(props.draggable).toBe(true);
736
+ expect(props.onDragStart).toBeDefined();
737
+ expect(props.onDragEnd).toBeDefined();
738
+ expect(props.onDragEnter).toBeDefined();
739
+ expect(props.onDragOver).toBeDefined();
740
+ expect(props.onDragLeave).toBeDefined();
741
+ expect(props.onDrop).toBeDefined();
742
+ });
743
+
744
+ it("includes drag handle handlers in getDragHandleProps() when seperateDragHandle is false", () => {
745
+ const props = tree.instance
746
+ .getItemInstance("x111")
747
+ .getDragHandleProps();
748
+ expect(props.draggable).toBe(true);
749
+ expect(props.onDragStart).toBeDefined();
750
+ expect(props.onDragEnd).toBeDefined();
751
+ });
752
+
753
+ it("excludes drag handle handlers from getProps() but includes drop handlers when seperateDragHandle is true", async () => {
754
+ const testTree = await tree
755
+ .with({ seperateDragHandle: true })
756
+ .createTestCaseTree();
757
+ const props = testTree.instance.getItemInstance("x111").getProps();
758
+ expect(props.draggable).toBeUndefined();
759
+ expect(props.onDragStart).toBeUndefined();
760
+ expect(props.onDragEnd).toBeUndefined();
761
+ expect(props.onDragEnter).toBeDefined();
762
+ expect(props.onDragOver).toBeDefined();
763
+ expect(props.onDragLeave).toBeDefined();
764
+ expect(props.onDrop).toBeDefined();
765
+ });
766
+
767
+ it("includes drag handle handlers in getDragHandleProps() when seperateDragHandle is true", async () => {
768
+ const testTree = await tree
769
+ .with({ seperateDragHandle: true })
770
+ .createTestCaseTree();
771
+ const props = testTree.instance
772
+ .getItemInstance("x111")
773
+ .getDragHandleProps();
774
+ expect(props.draggable).toBe(true);
775
+ expect(props.onDragStart).toBeDefined();
776
+ expect(props.onDragEnd).toBeDefined();
777
+ });
778
+ });
779
+
732
780
  describe("retains last drag state with dragcode", () => {
733
781
  it("uses constant number of calls to canDrop", () => {
734
782
  const canDrop = tree.mockedHandler("canDrop").mockReturnValue(true);
@@ -178,47 +178,10 @@ export const dragAndDropFeature: FeatureImplementation = {
178
178
  itemInstance: {
179
179
  getProps: ({ tree, item, prev }) => ({
180
180
  ...prev?.(),
181
-
182
- draggable: true,
181
+ ...(tree.getConfig().seperateDragHandle ? {} : item.getDragHandleProps()),
183
182
 
184
183
  onDragEnter: (e: DragEvent) => e.preventDefault(),
185
184
 
186
- onDragStart: (e: DragEvent) => {
187
- const selectedItems = tree.getSelectedItems
188
- ? tree.getSelectedItems()
189
- : [tree.getFocusedItem()];
190
- const items = selectedItems.includes(item) ? selectedItems : [item];
191
- const config = tree.getConfig();
192
-
193
- if (!selectedItems.includes(item)) {
194
- tree.setSelectedItems?.([item.getItemMeta().itemId]);
195
- }
196
-
197
- if (!(config.canDrag?.(items) ?? true)) {
198
- e.preventDefault();
199
- return;
200
- }
201
-
202
- if (config.setDragImage) {
203
- const { imgElement, xOffset, yOffset } = config.setDragImage(items);
204
- e.dataTransfer?.setDragImage(imgElement, xOffset ?? 0, yOffset ?? 0);
205
- }
206
-
207
- if (config.createForeignDragObject && e.dataTransfer) {
208
- const { format, data, dropEffect, effectAllowed } =
209
- config.createForeignDragObject(items);
210
- e.dataTransfer.setData(format, data);
211
-
212
- if (dropEffect) e.dataTransfer.dropEffect = dropEffect;
213
- if (effectAllowed) e.dataTransfer.effectAllowed = effectAllowed;
214
- }
215
-
216
- tree.applySubStateUpdate("dnd", {
217
- draggedItems: items,
218
- draggingOverItem: tree.getFocusedItem(),
219
- });
220
- },
221
-
222
185
  onDragOver: (e: DragEvent) => {
223
186
  e.stopPropagation(); // don't bubble up to container dragover
224
187
  const dataRef = tree.getDataRef<DndDataRef>();
@@ -277,27 +240,6 @@ export const dragAndDropFeature: FeatureImplementation = {
277
240
  }, 100);
278
241
  },
279
242
 
280
- onDragEnd: (e: DragEvent) => {
281
- const { onCompleteForeignDrop, canDragForeignDragObjectOver } =
282
- tree.getConfig();
283
- const draggedItems = tree.getState().dnd?.draggedItems;
284
-
285
- if (e.dataTransfer?.dropEffect === "none" || !draggedItems) {
286
- return;
287
- }
288
-
289
- const target = getDragTarget(e, item, tree, false);
290
- if (
291
- canDragForeignDragObjectOver &&
292
- e.dataTransfer &&
293
- !canDragForeignDragObjectOver(e.dataTransfer, target)
294
- ) {
295
- return;
296
- }
297
-
298
- onCompleteForeignDrop?.(draggedItems);
299
- },
300
-
301
243
  onDrop: async (e: DragEvent) => {
302
244
  e.stopPropagation();
303
245
  const dataRef = tree.getDataRef<DndDataRef>();
@@ -332,6 +274,69 @@ export const dragAndDropFeature: FeatureImplementation = {
332
274
  },
333
275
  }),
334
276
 
277
+ getDragHandleProps: ({ tree, item, prev }) => ({
278
+ ...prev?.(),
279
+
280
+ draggable: true,
281
+
282
+ onDragStart: (e: DragEvent) => {
283
+ const selectedItems = tree.getSelectedItems
284
+ ? tree.getSelectedItems()
285
+ : [tree.getFocusedItem()];
286
+ const items = selectedItems.includes(item) ? selectedItems : [item];
287
+ const config = tree.getConfig();
288
+
289
+ if (!selectedItems.includes(item)) {
290
+ tree.setSelectedItems?.([item.getItemMeta().itemId]);
291
+ }
292
+
293
+ if (!(config.canDrag?.(items) ?? true)) {
294
+ e.preventDefault();
295
+ return;
296
+ }
297
+
298
+ if (config.setDragImage) {
299
+ const { imgElement, xOffset, yOffset } = config.setDragImage(items);
300
+ e.dataTransfer?.setDragImage(imgElement, xOffset ?? 0, yOffset ?? 0);
301
+ }
302
+
303
+ if (config.createForeignDragObject && e.dataTransfer) {
304
+ const { format, data, dropEffect, effectAllowed } =
305
+ config.createForeignDragObject(items);
306
+ e.dataTransfer.setData(format, data);
307
+
308
+ if (dropEffect) e.dataTransfer.dropEffect = dropEffect;
309
+ if (effectAllowed) e.dataTransfer.effectAllowed = effectAllowed;
310
+ }
311
+
312
+ tree.applySubStateUpdate("dnd", {
313
+ draggedItems: items,
314
+ draggingOverItem: tree.getFocusedItem(),
315
+ });
316
+ },
317
+
318
+ onDragEnd: (e: DragEvent) => {
319
+ const { onCompleteForeignDrop, canDragForeignDragObjectOver } =
320
+ tree.getConfig();
321
+ const draggedItems = tree.getState().dnd?.draggedItems;
322
+
323
+ if (e.dataTransfer?.dropEffect === "none" || !draggedItems) {
324
+ return;
325
+ }
326
+
327
+ const target = getDragTarget(e, item, tree, false);
328
+ if (
329
+ canDragForeignDragObjectOver &&
330
+ e.dataTransfer &&
331
+ !canDragForeignDragObjectOver(e.dataTransfer, target)
332
+ ) {
333
+ return;
334
+ }
335
+
336
+ onCompleteForeignDrop?.(draggedItems);
337
+ },
338
+ }),
339
+
335
340
  isDragTarget: ({ tree, item }) => {
336
341
  const target = tree.getDragTarget();
337
342
  return target ? target.item.getId() === item.getId() : false;
@@ -97,6 +97,9 @@ export type DragAndDropFeatureDef<T> = {
97
97
 
98
98
  /** When dragging for this many ms on a closed folder, the folder will automatically open. Set to zero to disable. */
99
99
  openOnDropDelay?: number;
100
+
101
+ /** If true, `item.getProps()` will not include drag event handlers. Use `item.getDragHandleProps()` on the handler element. */
102
+ seperateDragHandle?: boolean;
100
103
  };
101
104
  treeInstance: {
102
105
  getDragTarget: () => DragTarget<T> | null;
@@ -118,6 +121,10 @@ export type DragAndDropFeatureDef<T> = {
118
121
  isDragTargetAbove: () => boolean;
119
122
  isDragTargetBelow: () => boolean;
120
123
  isDraggingOver: () => boolean;
124
+
125
+ /** Note that `item.getProps()` already passes in all drag event handlers by default. Set `seperateDragHandle` to true to
126
+ * disable the default behavior and use this on the handler element instead. */
127
+ getDragHandleProps: () => Record<string, any>;
121
128
  };
122
129
  hotkeys: never;
123
130
  };
@@ -59,6 +59,14 @@ export const propMemoizationFeature: FeatureImplementation = {
59
59
  return memoize(props, dataRef.current.memo.item);
60
60
  },
61
61
 
62
+ getDragHandleProps: ({ item, prev }) => {
63
+ const dataRef = item.getDataRef<PropMemoizationDataRef>();
64
+ const props = prev?.() ?? {};
65
+ dataRef.current.memo ??= {};
66
+ dataRef.current.memo.drag ??= {};
67
+ return memoize(props, dataRef.current.memo.drag);
68
+ },
69
+
62
70
  getRenameInputProps: ({ item, prev }) => {
63
71
  const dataRef = item.getDataRef<PropMemoizationDataRef>();
64
72
  const props = prev?.() ?? {};
@@ -2,6 +2,7 @@ export interface PropMemoizationDataRef {
2
2
  memo?: {
3
3
  tree?: Record<string, any>;
4
4
  item?: Record<string, any>;
5
+ drag?: Record<string, any>;
5
6
  search?: Record<string, any>;
6
7
  rename?: Record<string, any>;
7
8
  };