@headless-tree/core 1.6.1 → 1.6.3

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.mjs CHANGED
@@ -81,6 +81,7 @@ var poll = (fn, interval = 100, timeout = 1e3) => new Promise((resolve) => {
81
81
  }, interval);
82
82
  clear = setTimeout(() => {
83
83
  clearInterval(i);
84
+ resolve();
84
85
  }, timeout);
85
86
  });
86
87
 
@@ -170,12 +171,16 @@ var treeFeature = {
170
171
  },
171
172
  updateDomFocus: ({ tree }) => {
172
173
  setTimeout(() => __async(null, null, function* () {
173
- var _a, _b;
174
+ var _a, _b, _c, _d, _e;
174
175
  const focusedItem = tree.getFocusedItem();
175
176
  (_b = (_a = tree.getConfig()).scrollToItem) == null ? void 0 : _b.call(_a, focusedItem);
176
- yield poll(() => focusedItem.getElement() !== null, 20);
177
+ yield poll(() => focusedItem.getElement() !== null, 20, 500);
177
178
  const focusedElement = focusedItem.getElement();
178
- if (!focusedElement) return;
179
+ if (!focusedElement) {
180
+ (_c = tree.getItems()[0]) == null ? void 0 : _c.setFocused();
181
+ (_e = (_d = tree.getItems()[0]) == null ? void 0 : _d.getElement()) == null ? void 0 : _e.focus();
182
+ return;
183
+ }
179
184
  focusedElement.focus();
180
185
  }));
181
186
  },
@@ -976,6 +981,7 @@ var checkboxesFeature = {
976
981
  };
977
982
 
978
983
  // src/features/hotkeys-core/feature.ts
984
+ var resolveKeyCode = (e) => e.code !== "" && e.code !== "Unidentified" ? e.code : e.key;
979
985
  var specialKeys = {
980
986
  // TODO:breaking deprecate auto-lowercase
981
987
  letter: /^Key[A-Z]$/,
@@ -1024,9 +1030,10 @@ var hotkeysCoreFeature = {
1024
1030
  if (e.target instanceof HTMLInputElement && ignoreHotkeysOnInputs) {
1025
1031
  return;
1026
1032
  }
1033
+ const resolvedCode = resolveKeyCode(e);
1027
1034
  (_b = (_a = data.current).pressedKeys) != null ? _b : _a.pressedKeys = /* @__PURE__ */ new Set();
1028
- const newMatch = !data.current.pressedKeys.has(e.code);
1029
- data.current.pressedKeys.add(e.code);
1035
+ const newMatch = !data.current.pressedKeys.has(resolvedCode);
1036
+ data.current.pressedKeys.add(resolvedCode);
1030
1037
  const hotkeyName = findHotkeyMatch(
1031
1038
  data.current.pressedKeys,
1032
1039
  tree,
@@ -1034,7 +1041,7 @@ var hotkeysCoreFeature = {
1034
1041
  hotkeys
1035
1042
  );
1036
1043
  if (e.target instanceof HTMLInputElement) {
1037
- data.current.pressedKeys.delete(e.code);
1044
+ data.current.pressedKeys.delete(resolvedCode);
1038
1045
  }
1039
1046
  if (!hotkeyName) return;
1040
1047
  const hotkeyConfig = __spreadValues(__spreadValues({}, tree.getHotkeyPresets()[hotkeyName]), hotkeys == null ? void 0 : hotkeys[hotkeyName]);
@@ -1049,7 +1056,7 @@ var hotkeysCoreFeature = {
1049
1056
  const keyup = (e) => {
1050
1057
  var _a, _b;
1051
1058
  (_b = (_a = data.current).pressedKeys) != null ? _b : _a.pressedKeys = /* @__PURE__ */ new Set();
1052
- data.current.pressedKeys.delete(e.code);
1059
+ data.current.pressedKeys.delete(resolveKeyCode(e));
1053
1060
  };
1054
1061
  const reset = () => {
1055
1062
  data.current.pressedKeys = /* @__PURE__ */ new Set();
@@ -1230,15 +1237,17 @@ var asyncDataLoaderFeature = {
1230
1237
  }
1231
1238
  yield loadChildrenIds(tree, itemId);
1232
1239
  }),
1233
- updateCachedChildrenIds: ({ tree, itemId }, childrenIds) => {
1234
- const dataRef = tree.getDataRef();
1235
- dataRef.current.childrenIds[itemId] = childrenIds;
1236
- tree.rebuildTree();
1240
+ updateCachedChildrenIds: ({ tree, itemId }, childrenIds, skipUpdateTree) => {
1241
+ getDataRef(tree).current.childrenIds[itemId] = childrenIds;
1242
+ if (!skipUpdateTree) {
1243
+ tree.rebuildTree();
1244
+ }
1237
1245
  },
1238
- updateCachedData: ({ tree, itemId }, data) => {
1239
- const dataRef = tree.getDataRef();
1240
- dataRef.current.itemData[itemId] = data;
1241
- tree.rebuildTree();
1246
+ updateCachedData: ({ tree, itemId }, data, skipUpdateTree) => {
1247
+ getDataRef(tree).current.itemData[itemId] = data;
1248
+ if (!skipUpdateTree) {
1249
+ tree.rebuildTree();
1250
+ }
1242
1251
  },
1243
1252
  hasLoadedData: ({ tree, itemId }) => {
1244
1253
  const dataRef = tree.getDataRef();
@@ -1405,15 +1414,16 @@ var getReparentTarget = (item, reparentLevel, draggedItems) => {
1405
1414
  dragLineLevel: reparentLevel
1406
1415
  };
1407
1416
  };
1408
- var getDragTarget = (e, item, tree, canReorder = tree.getConfig().canReorder) => {
1417
+ var getDragTarget = (e, item, tree, hasDataTransferPayload, canReorder = tree.getConfig().canReorder) => {
1409
1418
  var _a;
1419
+ const dataTransfer = hasDataTransferPayload ? e.dataTransfer : null;
1410
1420
  const draggedItems = (_a = tree.getState().dnd) == null ? void 0 : _a.draggedItems;
1411
1421
  const itemMeta = item.getItemMeta();
1412
1422
  const parent = item.getParent();
1413
1423
  const itemTarget = { item };
1414
1424
  const parentTarget = parent ? { item: parent } : null;
1415
- const canBecomeSibling = parentTarget && canDrop(e.dataTransfer, parentTarget, tree);
1416
- const canMakeChild = canDrop(e.dataTransfer, itemTarget, tree);
1425
+ const canBecomeSibling = parentTarget && canDrop(dataTransfer, parentTarget, tree);
1426
+ const canMakeChild = canDrop(dataTransfer, itemTarget, tree);
1417
1427
  const placement = getTargetPlacement(e, item, tree, canMakeChild);
1418
1428
  if (!canReorder && parent && canBecomeSibling && placement.type !== 2 /* MakeChild */) {
1419
1429
  if (draggedItems == null ? void 0 : draggedItems.some((item2) => item2.isDescendentOf(parent.getId()))) {
@@ -1422,7 +1432,7 @@ var getDragTarget = (e, item, tree, canReorder = tree.getConfig().canReorder) =>
1422
1432
  return parentTarget;
1423
1433
  }
1424
1434
  if (!canReorder && parent && !canBecomeSibling) {
1425
- return getDragTarget(e, parent, tree, false);
1435
+ return getDragTarget(e, parent, tree, hasDataTransferPayload, false);
1426
1436
  }
1427
1437
  if (!parent) {
1428
1438
  return itemTarget;
@@ -1431,7 +1441,7 @@ var getDragTarget = (e, item, tree, canReorder = tree.getConfig().canReorder) =>
1431
1441
  return itemTarget;
1432
1442
  }
1433
1443
  if (!canBecomeSibling) {
1434
- return getDragTarget(e, parent, tree, false);
1444
+ return getDragTarget(e, parent, tree, hasDataTransferPayload, false);
1435
1445
  }
1436
1446
  if (placement.type === 3 /* Reparent */) {
1437
1447
  return getReparentTarget(item, placement.reparentLevel, draggedItems);
@@ -1475,7 +1485,8 @@ var dragAndDropFeature = {
1475
1485
  canDragForeignDragObjectOver: defaultConfig.canDropForeignDragObject !== defaultCanDropForeignDragObject ? (dataTransfer) => dataTransfer.effectAllowed !== "none" : () => false,
1476
1486
  setDndState: makeStateUpdater("dnd", tree),
1477
1487
  canReorder: true,
1478
- openOnDropDelay: 800
1488
+ openOnDropDelay: 800,
1489
+ draggedItemOverwritesSelection: true
1479
1490
  }, defaultConfig),
1480
1491
  stateHandlerNames: {
1481
1492
  dnd: "setDndState"
@@ -1568,36 +1579,8 @@ var dragAndDropFeature = {
1568
1579
  }
1569
1580
  },
1570
1581
  itemInstance: {
1571
- getProps: ({ tree, item, prev }) => __spreadProps(__spreadValues({}, prev == null ? void 0 : prev()), {
1572
- draggable: true,
1582
+ getProps: ({ tree, item, prev }) => __spreadProps(__spreadValues(__spreadValues({}, prev == null ? void 0 : prev()), tree.getConfig().seperateDragHandle ? {} : item.getDragHandleProps()), {
1573
1583
  onDragEnter: (e) => e.preventDefault(),
1574
- onDragStart: (e) => {
1575
- var _a, _b, _c, _d;
1576
- const selectedItems = tree.getSelectedItems ? tree.getSelectedItems() : [tree.getFocusedItem()];
1577
- const items = selectedItems.includes(item) ? selectedItems : [item];
1578
- const config = tree.getConfig();
1579
- if (!selectedItems.includes(item)) {
1580
- (_a = tree.setSelectedItems) == null ? void 0 : _a.call(tree, [item.getItemMeta().itemId]);
1581
- }
1582
- if (!((_c = (_b = config.canDrag) == null ? void 0 : _b.call(config, items)) != null ? _c : true)) {
1583
- e.preventDefault();
1584
- return;
1585
- }
1586
- if (config.setDragImage) {
1587
- const { imgElement, xOffset, yOffset } = config.setDragImage(items);
1588
- (_d = e.dataTransfer) == null ? void 0 : _d.setDragImage(imgElement, xOffset != null ? xOffset : 0, yOffset != null ? yOffset : 0);
1589
- }
1590
- if (config.createForeignDragObject && e.dataTransfer) {
1591
- const { format, data, dropEffect, effectAllowed } = config.createForeignDragObject(items);
1592
- e.dataTransfer.setData(format, data);
1593
- if (dropEffect) e.dataTransfer.dropEffect = dropEffect;
1594
- if (effectAllowed) e.dataTransfer.effectAllowed = effectAllowed;
1595
- }
1596
- tree.applySubStateUpdate("dnd", {
1597
- draggedItems: items,
1598
- draggingOverItem: tree.getFocusedItem()
1599
- });
1600
- },
1601
1584
  onDragOver: (e) => {
1602
1585
  var _a, _b, _c;
1603
1586
  e.stopPropagation();
@@ -1613,12 +1596,12 @@ var dragAndDropFeature = {
1613
1596
  dataRef.current.lastDragCode = nextDragCode;
1614
1597
  dataRef.current.lastDragEnter = Date.now();
1615
1598
  handleAutoOpenFolder(dataRef, tree, item, placement);
1616
- const target = getDragTarget(e, item, tree);
1599
+ const target = getDragTarget(e, item, tree, false);
1617
1600
  if (!((_a = tree.getState().dnd) == null ? void 0 : _a.draggedItems) && (!e.dataTransfer || !((_c = (_b = tree.getConfig()).canDragForeignDragObjectOver) == null ? void 0 : _c.call(_b, e.dataTransfer, target)))) {
1618
1601
  dataRef.current.lastAllowDrop = false;
1619
1602
  return;
1620
1603
  }
1621
- if (!canDrop(e.dataTransfer, target, tree)) {
1604
+ if (!canDrop(null, target, tree)) {
1622
1605
  dataRef.current.lastAllowDrop = false;
1623
1606
  return;
1624
1607
  }
@@ -1641,24 +1624,11 @@ var dragAndDropFeature = {
1641
1624
  }));
1642
1625
  }, 100);
1643
1626
  },
1644
- onDragEnd: (e) => {
1645
- var _a, _b;
1646
- const { onCompleteForeignDrop, canDragForeignDragObjectOver } = tree.getConfig();
1647
- const draggedItems = (_a = tree.getState().dnd) == null ? void 0 : _a.draggedItems;
1648
- if (((_b = e.dataTransfer) == null ? void 0 : _b.dropEffect) === "none" || !draggedItems) {
1649
- return;
1650
- }
1651
- const target = getDragTarget(e, item, tree);
1652
- if (canDragForeignDragObjectOver && e.dataTransfer && !canDragForeignDragObjectOver(e.dataTransfer, target)) {
1653
- return;
1654
- }
1655
- onCompleteForeignDrop == null ? void 0 : onCompleteForeignDrop(draggedItems);
1656
- },
1657
1627
  onDrop: (e) => __async(null, null, function* () {
1658
1628
  var _a, _b, _c;
1659
1629
  e.stopPropagation();
1660
1630
  const dataRef = tree.getDataRef();
1661
- const target = getDragTarget(e, item, tree);
1631
+ const target = getDragTarget(e, item, tree, true);
1662
1632
  const draggedItems = (_a = tree.getState().dnd) == null ? void 0 : _a.draggedItems;
1663
1633
  const isValidDrop = canDrop(e.dataTransfer, target, tree);
1664
1634
  tree.applySubStateUpdate("dnd", {
@@ -1674,11 +1644,59 @@ var dragAndDropFeature = {
1674
1644
  dataRef.current.lastDragCode = void 0;
1675
1645
  if (draggedItems) {
1676
1646
  yield (_b = config.onDrop) == null ? void 0 : _b.call(config, draggedItems, target);
1647
+ draggedItems[0].setFocused();
1677
1648
  } else if (e.dataTransfer) {
1678
1649
  yield (_c = config.onDropForeignDragObject) == null ? void 0 : _c.call(config, e.dataTransfer, target);
1679
1650
  }
1651
+ tree.applySubStateUpdate("dnd", null);
1652
+ tree.updateDomFocus();
1680
1653
  })
1681
1654
  }),
1655
+ getDragHandleProps: ({ tree, item, prev }) => __spreadProps(__spreadValues({}, prev == null ? void 0 : prev()), {
1656
+ draggable: true,
1657
+ onDragStart: (e) => {
1658
+ var _a, _b, _c, _d;
1659
+ const { draggedItemOverwritesSelection } = tree.getConfig();
1660
+ const selectedItems = tree.getSelectedItems ? tree.getSelectedItems() : [tree.getFocusedItem()];
1661
+ const overwriteSelection = !selectedItems.includes(item) && draggedItemOverwritesSelection;
1662
+ const items = overwriteSelection ? [item] : selectedItems;
1663
+ const config = tree.getConfig();
1664
+ if (overwriteSelection) {
1665
+ (_a = tree.setSelectedItems) == null ? void 0 : _a.call(tree, [item.getItemMeta().itemId]);
1666
+ }
1667
+ if (!((_c = (_b = config.canDrag) == null ? void 0 : _b.call(config, items)) != null ? _c : true)) {
1668
+ e.preventDefault();
1669
+ return;
1670
+ }
1671
+ if (config.setDragImage) {
1672
+ const { imgElement, xOffset, yOffset } = config.setDragImage(items);
1673
+ (_d = e.dataTransfer) == null ? void 0 : _d.setDragImage(imgElement, xOffset != null ? xOffset : 0, yOffset != null ? yOffset : 0);
1674
+ }
1675
+ if (config.createForeignDragObject && e.dataTransfer) {
1676
+ const { format, data, dropEffect, effectAllowed } = config.createForeignDragObject(items);
1677
+ e.dataTransfer.setData(format, data);
1678
+ if (dropEffect) e.dataTransfer.dropEffect = dropEffect;
1679
+ if (effectAllowed) e.dataTransfer.effectAllowed = effectAllowed;
1680
+ }
1681
+ tree.applySubStateUpdate("dnd", {
1682
+ draggedItems: items,
1683
+ draggingOverItem: tree.getFocusedItem()
1684
+ });
1685
+ },
1686
+ onDragEnd: (e) => {
1687
+ var _a, _b;
1688
+ const { onCompleteForeignDrop, canDragForeignDragObjectOver } = tree.getConfig();
1689
+ const draggedItems = (_a = tree.getState().dnd) == null ? void 0 : _a.draggedItems;
1690
+ if (((_b = e.dataTransfer) == null ? void 0 : _b.dropEffect) === "none" || !draggedItems) {
1691
+ return;
1692
+ }
1693
+ const target = getDragTarget(e, item, tree, false);
1694
+ if (canDragForeignDragObjectOver && e.dataTransfer && !canDragForeignDragObjectOver(e.dataTransfer, target)) {
1695
+ return;
1696
+ }
1697
+ onCompleteForeignDrop == null ? void 0 : onCompleteForeignDrop(draggedItems);
1698
+ }
1699
+ }),
1682
1700
  isDragTarget: ({ tree, item }) => {
1683
1701
  const target = tree.getDragTarget();
1684
1702
  return target ? target.item.getId() === item.getId() : false;
@@ -1902,6 +1920,7 @@ var keyboardDragAndDropFeature = {
1902
1920
  } else if (dataTransfer) {
1903
1921
  yield (_d = config.onDropForeignDragObject) == null ? void 0 : _d.call(config, dataTransfer, target);
1904
1922
  }
1923
+ tree.updateDomFocus();
1905
1924
  tree.applySubStateUpdate(
1906
1925
  "assistiveDndState",
1907
1926
  3 /* Completed */
@@ -2252,6 +2271,14 @@ var propMemoizationFeature = {
2252
2271
  (_e = (_d = dataRef.current.memo).item) != null ? _e : _d.item = {};
2253
2272
  return memoize(props, dataRef.current.memo.item);
2254
2273
  },
2274
+ getDragHandleProps: ({ item, prev }) => {
2275
+ var _a, _b, _c, _d, _e;
2276
+ const dataRef = item.getDataRef();
2277
+ const props = (_a = prev == null ? void 0 : prev()) != null ? _a : {};
2278
+ (_c = (_b = dataRef.current).memo) != null ? _c : _b.memo = {};
2279
+ (_e = (_d = dataRef.current.memo).drag) != null ? _e : _d.drag = {};
2280
+ return memoize(props, dataRef.current.memo.drag);
2281
+ },
2255
2282
  getRenameInputProps: ({ item, prev }) => {
2256
2283
  var _a, _b, _c, _d, _e;
2257
2284
  const dataRef = item.getDataRef();
package/package.json CHANGED
@@ -13,7 +13,7 @@
13
13
  "checkbox",
14
14
  "hook"
15
15
  ],
16
- "version": "1.6.1",
16
+ "version": "1.6.3",
17
17
  "main": "dist/index.d.ts",
18
18
  "module": "dist/index.mjs",
19
19
  "types": "dist/index.d.mts",
@@ -180,15 +180,21 @@ export const asyncDataLoaderFeature: FeatureImplementation = {
180
180
  }
181
181
  await loadChildrenIds(tree, itemId);
182
182
  },
183
- updateCachedChildrenIds: ({ tree, itemId }, childrenIds) => {
184
- const dataRef = tree.getDataRef<AsyncDataLoaderDataRef>();
185
- dataRef.current.childrenIds[itemId] = childrenIds;
186
- tree.rebuildTree();
183
+ updateCachedChildrenIds: (
184
+ { tree, itemId },
185
+ childrenIds,
186
+ skipUpdateTree,
187
+ ) => {
188
+ getDataRef(tree).current.childrenIds[itemId] = childrenIds;
189
+ if (!skipUpdateTree) {
190
+ tree.rebuildTree();
191
+ }
187
192
  },
188
- updateCachedData: ({ tree, itemId }, data) => {
189
- const dataRef = tree.getDataRef<AsyncDataLoaderDataRef>();
190
- dataRef.current.itemData[itemId] = data;
191
- tree.rebuildTree();
193
+ updateCachedData: ({ tree, itemId }, data, skipUpdateTree) => {
194
+ getDataRef(tree).current.itemData[itemId] = data;
195
+ if (!skipUpdateTree) {
196
+ tree.rebuildTree();
197
+ }
192
198
  },
193
199
  hasLoadedData: ({ tree, itemId }) => {
194
200
  const dataRef = tree.getDataRef<AsyncDataLoaderDataRef>();
@@ -39,6 +39,7 @@ export type AsyncDataLoaderFeatureDef<T> = {
39
39
  waitForItemChildrenLoaded: (itemId: string) => Promise<void>;
40
40
  loadItemData: (itemId: string) => Promise<T>;
41
41
  loadChildrenIds: (itemId: string) => Promise<string[]>;
42
+
42
43
  /* idea: recursiveLoadItems: (itemId: string, cancelToken?: { current: boolean }, onLoad: (itemIds: string[]) => void) => Promise<T[]> */
43
44
  };
44
45
  itemInstance: SyncDataLoaderFeatureDef<T>["itemInstance"] & {
@@ -52,9 +53,16 @@ export type AsyncDataLoaderFeatureDef<T> = {
52
53
  * the tree will continue to display the old data until the new data has loaded. */
53
54
  invalidateChildrenIds: (optimistic?: boolean) => Promise<void>;
54
55
 
55
- /** Set to undefined to clear cache without triggering automatic refetch. Use @invalidateItemData to clear and triggering refetch. */
56
- updateCachedData: (data: T | undefined) => void;
57
- updateCachedChildrenIds: (childrenIds: string[]) => void;
56
+ /** Set to undefined to clear cache without triggering automatic refetch. Use @invalidateItemData to clear and triggering refetch.
57
+ * @param skipUpdateTree If true, the tree will not rebuild the tree structure cache afterwards by calling `tree.rebuildTree()`. */
58
+ updateCachedData: (data: T | undefined, skipUpdateTree?: boolean) => void;
59
+
60
+ /** @param skipUpdateTree If true, the tree will not rebuild the tree structure cache afterwards by calling `tree.rebuildTree()`. */
61
+ updateCachedChildrenIds: (
62
+ childrenIds: string[],
63
+ skipUpdateTree?: boolean,
64
+ ) => void;
65
+
58
66
  hasLoadedData: () => boolean;
59
67
  isLoading: () => boolean;
60
68
  };
@@ -328,8 +328,7 @@ describe("core-feature/drag-and-drop", () => {
328
328
  "onDropForeignDragObject",
329
329
  );
330
330
  const event = tree.createBottomDragEvent(2);
331
- tree.setElementBoundingBox("x212");
332
- tree.setElementBoundingBox("x213");
331
+ tree.setElementBoundingBox("x112");
333
332
  tree.do.dragOver("x112", event);
334
333
  tree.do.drop("x112", event);
335
334
  expect(onDropForeignDragObject).toHaveBeenCalledWith(
@@ -357,6 +356,38 @@ describe("core-feature/drag-and-drop", () => {
357
356
  tree.do.drop("x11", event);
358
357
  expect(onDropForeignDragObject).not.toHaveBeenCalled();
359
358
  });
359
+
360
+ it("calls canDragForeignDragObjectOver in drag over events, not canDropForeignDragObject", () => {
361
+ const canDropForeignDragObject = tree
362
+ .mockedHandler("canDropForeignDragObject")
363
+ .mockReturnValue(true);
364
+ const canDragForeignDragObjectOver = tree
365
+ .mockedHandler("canDragForeignDragObjectOver")
366
+ .mockReturnValue(true);
367
+ const event = TestTree.dragEvent();
368
+ tree.do.dragOver("x11", event);
369
+ expect(canDragForeignDragObjectOver).toHaveBeenCalledWith(
370
+ event.dataTransfer,
371
+ { item: tree.item("x11") },
372
+ );
373
+ expect(canDropForeignDragObject).not.toHaveBeenCalled();
374
+ });
375
+
376
+ it("calls canDropForeignDragObject in drop events, not canDragForeignDragObjectOver", () => {
377
+ const canDropForeignDragObject = tree
378
+ .mockedHandler("canDropForeignDragObject")
379
+ .mockReturnValue(true);
380
+ const canDragForeignDragObjectOver = tree
381
+ .mockedHandler("canDragForeignDragObjectOver")
382
+ .mockReturnValue(true);
383
+ const event = TestTree.dragEvent();
384
+ tree.do.drop("x11", event);
385
+ expect(canDropForeignDragObject).toHaveBeenCalledWith(
386
+ event.dataTransfer,
387
+ { item: tree.item("x11") },
388
+ );
389
+ expect(canDragForeignDragObjectOver).not.toHaveBeenCalled();
390
+ });
360
391
  });
361
392
 
362
393
  describe("with insertion handlers", () => {
@@ -698,6 +729,54 @@ describe("core-feature/drag-and-drop", () => {
698
729
  });
699
730
  });
700
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
+
701
780
  describe("retains last drag state with dragcode", () => {
702
781
  it("uses constant number of calls to canDrop", () => {
703
782
  const canDrop = tree.mockedHandler("canDrop").mockReturnValue(true);
@@ -708,5 +787,99 @@ describe("core-feature/drag-and-drop", () => {
708
787
  expect(canDrop).toBeCalledTimes(3);
709
788
  });
710
789
  });
790
+
791
+ describe("draggedItemOverwritesSelection", () => {
792
+ it("overwrites selection when dragging unselected item with draggedItemOverwritesSelection=true", () => {
793
+ tree.do.selectItem("x111");
794
+ tree.do.ctrlSelectItem("x112");
795
+ tree.do.ctrlSelectItem("x113");
796
+
797
+ expect(tree.instance.getItemInstance("x111").isSelected()).toBe(true);
798
+ expect(tree.instance.getItemInstance("x112").isSelected()).toBe(true);
799
+ expect(tree.instance.getItemInstance("x113").isSelected()).toBe(true);
800
+ expect(tree.instance.getItemInstance("x114").isSelected()).toBe(false);
801
+
802
+ tree.do.startDrag("x114");
803
+
804
+ expect(tree.instance.getItemInstance("x111").isSelected()).toBe(false);
805
+ expect(tree.instance.getItemInstance("x112").isSelected()).toBe(false);
806
+ expect(tree.instance.getItemInstance("x113").isSelected()).toBe(false);
807
+ expect(tree.instance.getItemInstance("x114").isSelected()).toBe(true);
808
+ });
809
+
810
+ it("preserves selection when dragging unselected item with draggedItemOverwritesSelection=false", async () => {
811
+ const testTree = await tree
812
+ .with({ draggedItemOverwritesSelection: false })
813
+ .createTestCaseTree();
814
+
815
+ testTree.do.selectItem("x111");
816
+ testTree.do.ctrlSelectItem("x112");
817
+ testTree.do.ctrlSelectItem("x113");
818
+
819
+ expect(testTree.item("x111").isSelected()).toBe(true);
820
+ expect(testTree.item("x112").isSelected()).toBe(true);
821
+ expect(testTree.item("x113").isSelected()).toBe(true);
822
+ expect(testTree.item("x114").isSelected()).toBe(false);
823
+ testTree.do.startDrag("x114");
824
+
825
+ expect(testTree.item("x111").isSelected()).toBe(true);
826
+ expect(testTree.item("x112").isSelected()).toBe(true);
827
+ expect(testTree.item("x113").isSelected()).toBe(true);
828
+ expect(testTree.item("x114").isSelected()).toBe(false);
829
+ });
830
+
831
+ it("does not overwrite selection when dragging selected item with draggedItemOverwritesSelection=true", () => {
832
+ tree.do.selectItem("x111");
833
+ tree.do.ctrlSelectItem("x112");
834
+ tree.do.ctrlSelectItem("x113");
835
+
836
+ expect(tree.item("x111").isSelected()).toBe(true);
837
+ expect(tree.item("x112").isSelected()).toBe(true);
838
+ expect(tree.item("x113").isSelected()).toBe(true);
839
+
840
+ tree.do.startDrag("x111");
841
+
842
+ expect(tree.item("x111").isSelected()).toBe(true);
843
+ expect(tree.item("x112").isSelected()).toBe(true);
844
+ expect(tree.item("x113").isSelected()).toBe(true);
845
+ });
846
+
847
+ it("does not overwrite selection when dragging selected item with draggedItemOverwritesSelection=false", async () => {
848
+ const testTree = await tree
849
+ .with({ draggedItemOverwritesSelection: false })
850
+ .createTestCaseTree();
851
+
852
+ testTree.do.selectItem("x111");
853
+ testTree.do.ctrlSelectItem("x112");
854
+ testTree.do.ctrlSelectItem("x113");
855
+
856
+ expect(testTree.item("x111").isSelected()).toBe(true);
857
+ expect(testTree.item("x112").isSelected()).toBe(true);
858
+ expect(testTree.item("x113").isSelected()).toBe(true);
859
+
860
+ testTree.do.startDrag("x111");
861
+
862
+ expect(testTree.item("x111").isSelected()).toBe(true);
863
+ expect(testTree.item("x112").isSelected()).toBe(true);
864
+ expect(testTree.item("x113").isSelected()).toBe(true);
865
+ });
866
+
867
+ it("drags all selected items when draggedItemOverwritesSelection=false", async () => {
868
+ const testTree = await tree
869
+ .with({ draggedItemOverwritesSelection: false })
870
+ .createTestCaseTree();
871
+
872
+ testTree.do.selectItem("x111");
873
+ testTree.do.ctrlSelectItem("x112");
874
+ testTree.do.ctrlSelectItem("x113");
875
+
876
+ testTree.do.startDrag("x111");
877
+ testTree.do.dragOverAndDrop("x21");
878
+
879
+ testTree.expect.dropped(["x111", "x112", "x113"], {
880
+ item: testTree.item("x21"),
881
+ });
882
+ });
883
+ });
711
884
  });
712
885
  });