@headless-tree/core 0.0.0-20250224211405 → 0.0.0-20250330164609

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.
Files changed (125) hide show
  1. package/CHANGELOG.md +8 -1
  2. package/lib/cjs/core/create-tree.js +13 -4
  3. package/lib/cjs/features/async-data-loader/feature.js +73 -48
  4. package/lib/cjs/features/async-data-loader/types.d.ts +17 -14
  5. package/lib/cjs/features/drag-and-drop/feature.js +98 -93
  6. package/lib/cjs/features/drag-and-drop/types.d.ts +17 -29
  7. package/lib/cjs/features/drag-and-drop/types.js +7 -7
  8. package/lib/cjs/features/drag-and-drop/utils.d.ts +18 -3
  9. package/lib/cjs/features/drag-and-drop/utils.js +50 -51
  10. package/lib/cjs/features/expand-all/feature.js +26 -3
  11. package/lib/cjs/features/expand-all/types.d.ts +3 -1
  12. package/lib/cjs/features/hotkeys-core/feature.js +7 -3
  13. package/lib/cjs/features/hotkeys-core/types.d.ts +4 -5
  14. package/lib/cjs/features/keyboard-drag-and-drop/feature.d.ts +2 -0
  15. package/lib/cjs/features/keyboard-drag-and-drop/feature.js +207 -0
  16. package/lib/cjs/features/keyboard-drag-and-drop/types.d.ts +27 -0
  17. package/lib/cjs/features/keyboard-drag-and-drop/types.js +11 -0
  18. package/lib/cjs/features/prop-memoization/feature.js +2 -2
  19. package/lib/cjs/features/prop-memoization/types.d.ts +2 -2
  20. package/lib/cjs/features/renaming/feature.js +1 -1
  21. package/lib/cjs/features/search/feature.js +2 -0
  22. package/lib/cjs/features/search/types.d.ts +2 -2
  23. package/lib/cjs/features/selection/feature.js +4 -4
  24. package/lib/cjs/features/selection/types.d.ts +1 -1
  25. package/lib/cjs/features/sync-data-loader/feature.js +31 -5
  26. package/lib/cjs/features/sync-data-loader/types.d.ts +5 -5
  27. package/lib/cjs/features/tree/feature.js +4 -7
  28. package/lib/cjs/features/tree/types.d.ts +7 -5
  29. package/lib/cjs/index.d.ts +2 -0
  30. package/lib/cjs/index.js +2 -0
  31. package/lib/cjs/mddocs-entry.d.ts +10 -0
  32. package/lib/cjs/test-utils/test-tree-do.d.ts +2 -2
  33. package/lib/cjs/test-utils/test-tree-do.js +19 -6
  34. package/lib/cjs/test-utils/test-tree-expect.d.ts +5 -3
  35. package/lib/cjs/test-utils/test-tree-expect.js +3 -0
  36. package/lib/cjs/test-utils/test-tree.d.ts +2 -1
  37. package/lib/cjs/test-utils/test-tree.js +24 -21
  38. package/lib/cjs/types/core.d.ts +2 -1
  39. package/lib/cjs/utilities/create-on-drop-handler.d.ts +2 -2
  40. package/lib/cjs/utilities/create-on-drop-handler.js +13 -4
  41. package/lib/cjs/utilities/insert-items-at-target.d.ts +2 -2
  42. package/lib/cjs/utilities/insert-items-at-target.js +21 -12
  43. package/lib/cjs/utilities/remove-items-from-parents.d.ts +1 -1
  44. package/lib/cjs/utilities/remove-items-from-parents.js +12 -3
  45. package/lib/esm/core/create-tree.js +13 -4
  46. package/lib/esm/features/async-data-loader/feature.js +73 -48
  47. package/lib/esm/features/async-data-loader/types.d.ts +17 -14
  48. package/lib/esm/features/drag-and-drop/feature.js +99 -94
  49. package/lib/esm/features/drag-and-drop/types.d.ts +17 -29
  50. package/lib/esm/features/drag-and-drop/types.js +6 -6
  51. package/lib/esm/features/drag-and-drop/utils.d.ts +18 -3
  52. package/lib/esm/features/drag-and-drop/utils.js +45 -49
  53. package/lib/esm/features/expand-all/feature.js +26 -3
  54. package/lib/esm/features/expand-all/types.d.ts +3 -1
  55. package/lib/esm/features/hotkeys-core/feature.js +7 -3
  56. package/lib/esm/features/hotkeys-core/types.d.ts +4 -5
  57. package/lib/esm/features/keyboard-drag-and-drop/feature.d.ts +2 -0
  58. package/lib/esm/features/keyboard-drag-and-drop/feature.js +204 -0
  59. package/lib/esm/features/keyboard-drag-and-drop/types.d.ts +27 -0
  60. package/lib/esm/features/keyboard-drag-and-drop/types.js +8 -0
  61. package/lib/esm/features/prop-memoization/feature.js +2 -2
  62. package/lib/esm/features/prop-memoization/types.d.ts +2 -2
  63. package/lib/esm/features/renaming/feature.js +1 -1
  64. package/lib/esm/features/search/feature.js +2 -0
  65. package/lib/esm/features/search/types.d.ts +2 -2
  66. package/lib/esm/features/selection/feature.js +4 -4
  67. package/lib/esm/features/selection/types.d.ts +1 -1
  68. package/lib/esm/features/sync-data-loader/feature.js +31 -5
  69. package/lib/esm/features/sync-data-loader/types.d.ts +5 -5
  70. package/lib/esm/features/tree/feature.js +4 -7
  71. package/lib/esm/features/tree/types.d.ts +7 -5
  72. package/lib/esm/index.d.ts +2 -0
  73. package/lib/esm/index.js +2 -0
  74. package/lib/esm/mddocs-entry.d.ts +10 -0
  75. package/lib/esm/test-utils/test-tree-do.d.ts +2 -2
  76. package/lib/esm/test-utils/test-tree-do.js +19 -6
  77. package/lib/esm/test-utils/test-tree-expect.d.ts +5 -3
  78. package/lib/esm/test-utils/test-tree-expect.js +3 -0
  79. package/lib/esm/test-utils/test-tree.d.ts +2 -1
  80. package/lib/esm/test-utils/test-tree.js +24 -21
  81. package/lib/esm/types/core.d.ts +2 -1
  82. package/lib/esm/utilities/create-on-drop-handler.d.ts +2 -2
  83. package/lib/esm/utilities/create-on-drop-handler.js +13 -4
  84. package/lib/esm/utilities/insert-items-at-target.d.ts +2 -2
  85. package/lib/esm/utilities/insert-items-at-target.js +21 -12
  86. package/lib/esm/utilities/remove-items-from-parents.d.ts +1 -1
  87. package/lib/esm/utilities/remove-items-from-parents.js +12 -3
  88. package/package.json +2 -2
  89. package/src/core/core.spec.ts +31 -0
  90. package/src/core/create-tree.ts +15 -5
  91. package/src/features/async-data-loader/async-data-loader.spec.ts +10 -6
  92. package/src/features/async-data-loader/feature.ts +76 -48
  93. package/src/features/async-data-loader/types.ts +18 -11
  94. package/src/features/drag-and-drop/drag-and-drop.spec.ts +75 -89
  95. package/src/features/drag-and-drop/feature.ts +21 -22
  96. package/src/features/drag-and-drop/types.ts +23 -35
  97. package/src/features/drag-and-drop/utils.ts +69 -57
  98. package/src/features/expand-all/feature.ts +29 -5
  99. package/src/features/expand-all/types.ts +3 -1
  100. package/src/features/hotkeys-core/feature.ts +4 -0
  101. package/src/features/hotkeys-core/types.ts +4 -13
  102. package/src/features/keyboard-drag-and-drop/feature.ts +255 -0
  103. package/src/features/keyboard-drag-and-drop/keyboard-drag-and-drop.spec.ts +401 -0
  104. package/src/features/keyboard-drag-and-drop/types.ts +30 -0
  105. package/src/features/prop-memoization/feature.ts +2 -2
  106. package/src/features/prop-memoization/prop-memoization.spec.ts +2 -2
  107. package/src/features/prop-memoization/types.ts +2 -2
  108. package/src/features/renaming/feature.ts +8 -2
  109. package/src/features/search/feature.ts +2 -0
  110. package/src/features/search/types.ts +2 -2
  111. package/src/features/selection/feature.ts +4 -4
  112. package/src/features/selection/types.ts +1 -1
  113. package/src/features/sync-data-loader/feature.ts +26 -7
  114. package/src/features/sync-data-loader/types.ts +5 -5
  115. package/src/features/tree/feature.ts +8 -11
  116. package/src/features/tree/types.ts +7 -5
  117. package/src/index.ts +2 -0
  118. package/src/mddocs-entry.ts +16 -0
  119. package/src/test-utils/test-tree-do.ts +3 -3
  120. package/src/test-utils/test-tree-expect.ts +7 -2
  121. package/src/test-utils/test-tree.ts +26 -22
  122. package/src/types/core.ts +2 -0
  123. package/src/utilities/create-on-drop-handler.ts +4 -4
  124. package/src/utilities/insert-items-at-target.ts +18 -14
  125. package/src/utilities/remove-items-from-parents.ts +6 -3
@@ -1,12 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getDropTarget = exports.getDragCode = exports.canDrop = void 0;
3
+ exports.getDragTarget = exports.getReparentTarget = exports.getDragCode = exports.getInsertionIndex = exports.getItemDropCategory = exports.canDrop = exports.ItemDropCategory = void 0;
4
4
  var ItemDropCategory;
5
5
  (function (ItemDropCategory) {
6
6
  ItemDropCategory[ItemDropCategory["Item"] = 0] = "Item";
7
7
  ItemDropCategory[ItemDropCategory["ExpandedFolder"] = 1] = "ExpandedFolder";
8
8
  ItemDropCategory[ItemDropCategory["LastInGroup"] = 2] = "LastInGroup";
9
- })(ItemDropCategory || (ItemDropCategory = {}));
9
+ })(ItemDropCategory || (exports.ItemDropCategory = ItemDropCategory = {}));
10
10
  var PlacementType;
11
11
  (function (PlacementType) {
12
12
  PlacementType[PlacementType["ReorderAbove"] = 0] = "ReorderAbove";
@@ -15,7 +15,7 @@ var PlacementType;
15
15
  PlacementType[PlacementType["Reparent"] = 3] = "Reparent";
16
16
  })(PlacementType || (PlacementType = {}));
17
17
  const canDrop = (dataTransfer, target, tree) => {
18
- var _a, _b, _c, _d;
18
+ var _a, _b, _c;
19
19
  const draggedItems = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggedItems;
20
20
  const config = tree.getConfig();
21
21
  if (draggedItems && !((_c = (_b = config.canDrop) === null || _b === void 0 ? void 0 : _b.call(config, draggedItems, target)) !== null && _c !== void 0 ? _c : true)) {
@@ -28,7 +28,8 @@ const canDrop = (dataTransfer, target, tree) => {
28
28
  }
29
29
  if (!draggedItems &&
30
30
  dataTransfer &&
31
- !((_d = config.canDropForeignDragObject) === null || _d === void 0 ? void 0 : _d.call(config, dataTransfer, target))) {
31
+ config.canDropForeignDragObject &&
32
+ !config.canDropForeignDragObject(dataTransfer, target)) {
32
33
  return false;
33
34
  }
34
35
  return true;
@@ -39,13 +40,24 @@ const getItemDropCategory = (item) => {
39
40
  return ItemDropCategory.ExpandedFolder;
40
41
  }
41
42
  const parent = item.getParent();
42
- if (parent && item.getIndexInParent() === parent.getItemMeta().setSize - 1) {
43
+ if (parent && item.getIndexInParent() === item.getItemMeta().setSize - 1) {
43
44
  return ItemDropCategory.LastInGroup;
44
45
  }
45
46
  return ItemDropCategory.Item;
46
47
  };
48
+ exports.getItemDropCategory = getItemDropCategory;
49
+ const getInsertionIndex = (children, childIndex, draggedItems) => {
50
+ var _a;
51
+ const numberOfDragItemsBeforeTarget = (_a = children
52
+ .slice(0, childIndex)
53
+ .reduce((counter, child) => child && (draggedItems === null || draggedItems === void 0 ? void 0 : draggedItems.some((i) => i.getId() === child.getId()))
54
+ ? ++counter
55
+ : counter, 0)) !== null && _a !== void 0 ? _a : 0;
56
+ return childIndex - numberOfDragItemsBeforeTarget;
57
+ };
58
+ exports.getInsertionIndex = getInsertionIndex;
47
59
  const getTargetPlacement = (e, item, tree, canMakeChild) => {
48
- var _a, _b, _c;
60
+ var _a, _b, _c, _d, _e;
49
61
  const config = tree.getConfig();
50
62
  if (!config.canReorder) {
51
63
  return canMakeChild
@@ -53,9 +65,9 @@ const getTargetPlacement = (e, item, tree, canMakeChild) => {
53
65
  : { type: PlacementType.ReorderBelow };
54
66
  }
55
67
  const bb = (_a = item.getElement()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
56
- const topPercent = bb ? (e.pageY - bb.top) / bb.height : 0.5;
57
- const leftPixels = bb ? e.pageX - bb.left : 0;
58
- const targetDropCategory = getItemDropCategory(item);
68
+ const topPercent = bb ? (e.clientY - bb.top) / bb.height : 0.5;
69
+ const leftPixels = bb ? e.clientX - bb.left : 0;
70
+ const targetDropCategory = (0, exports.getItemDropCategory)(item);
59
71
  const reorderAreaPercentage = !canMakeChild
60
72
  ? 0.5
61
73
  : (_b = config.reorderAreaPercentage) !== null && _b !== void 0 ? _b : 0.3;
@@ -74,9 +86,10 @@ const getTargetPlacement = (e, item, tree, canMakeChild) => {
74
86
  if (topPercent < 0.5) {
75
87
  return { type: PlacementType.ReorderAbove };
76
88
  }
89
+ const minLevel = (_e = (_d = item.getItemBelow()) === null || _d === void 0 ? void 0 : _d.getItemMeta().level) !== null && _e !== void 0 ? _e : 0;
77
90
  return {
78
91
  type: PlacementType.Reparent,
79
- reparentLevel: Math.floor(leftPixels / indent),
92
+ reparentLevel: Math.max(minLevel, Math.floor(leftPixels / indent)),
80
93
  };
81
94
  }
82
95
  // if not at left of item area, treat as if it was a normal item
@@ -105,27 +118,29 @@ const getNthParent = (item, n) => {
105
118
  }
106
119
  return getNthParent(item.getParent(), n);
107
120
  };
108
- const getDropTarget = (e, item, tree, canReorder = tree.getConfig().canReorder) => {
109
- var _a, _b, _c;
110
- const draggedItems = (_b = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggedItems) !== null && _b !== void 0 ? _b : [];
121
+ /** @param item refers to the bottom-most item of the container, at which bottom is being reparented on (e.g. root-1-2-6) */
122
+ const getReparentTarget = (item, reparentLevel, draggedItems) => {
111
123
  const itemMeta = item.getItemMeta();
112
- const parent = item.getParent();
113
- const itemTarget = {
114
- item,
115
- childIndex: null,
116
- insertionIndex: null,
117
- dragLineIndex: null,
118
- dragLineLevel: null,
124
+ const reparentedTarget = getNthParent(item, reparentLevel - 1);
125
+ const targetItemAbove = getNthParent(item, reparentLevel); // .getItemBelow()!;
126
+ const targetIndex = targetItemAbove.getIndexInParent() + 1;
127
+ // TODO possibly count items dragged out above the new target
128
+ return {
129
+ item: reparentedTarget,
130
+ childIndex: targetIndex,
131
+ insertionIndex: (0, exports.getInsertionIndex)(reparentedTarget.getChildren(), targetIndex, draggedItems),
132
+ dragLineIndex: itemMeta.index + 1,
133
+ dragLineLevel: reparentLevel,
119
134
  };
120
- const parentTarget = parent
121
- ? {
122
- item: parent,
123
- childIndex: null,
124
- insertionIndex: null,
125
- dragLineIndex: null,
126
- dragLineLevel: null,
127
- }
128
- : null;
135
+ };
136
+ exports.getReparentTarget = getReparentTarget;
137
+ const getDragTarget = (e, item, tree, canReorder = tree.getConfig().canReorder) => {
138
+ var _a;
139
+ const draggedItems = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggedItems;
140
+ const itemMeta = item.getItemMeta();
141
+ const parent = item.getParent();
142
+ const itemTarget = { item };
143
+ const parentTarget = parent ? { item: parent } : null;
129
144
  const canBecomeSibling = parentTarget && (0, exports.canDrop)(e.dataTransfer, parentTarget, tree);
130
145
  const canMakeChild = (0, exports.canDrop)(e.dataTransfer, itemTarget, tree);
131
146
  const placement = getTargetPlacement(e, item, tree, canMakeChild);
@@ -136,8 +151,8 @@ const getDropTarget = (e, item, tree, canReorder = tree.getConfig().canReorder)
136
151
  return parentTarget;
137
152
  }
138
153
  if (!canReorder && parent && !canBecomeSibling) {
139
- // TODO! this breaks in story DND/Can Drop. Maybe move this logic into a composable DropTargetStrategy[] ?
140
- return (0, exports.getDropTarget)(e, parent, tree, false);
154
+ // TODO! this breaks in story DND/Can Drop. Maybe move this logic into a composable DragTargetStrategy[] ?
155
+ return (0, exports.getDragTarget)(e, parent, tree, false);
141
156
  }
142
157
  if (!parent) {
143
158
  // Shouldn't happen, but if dropped "next" to root item, just drop it inside
@@ -147,36 +162,20 @@ const getDropTarget = (e, item, tree, canReorder = tree.getConfig().canReorder)
147
162
  return itemTarget;
148
163
  }
149
164
  if (!canBecomeSibling) {
150
- return (0, exports.getDropTarget)(e, parent, tree, false);
165
+ return (0, exports.getDragTarget)(e, parent, tree, false);
151
166
  }
152
167
  if (placement.type === PlacementType.Reparent) {
153
- const reparentedTarget = getNthParent(item, placement.reparentLevel - 1);
154
- const targetItemAbove = getNthParent(item, placement.reparentLevel); // .getItemBelow()!;
155
- const targetIndex = targetItemAbove.getIndexInParent() + 1;
156
- // TODO possibly count items dragged out above the new target
157
- return {
158
- item: reparentedTarget,
159
- childIndex: targetIndex,
160
- insertionIndex: targetIndex,
161
- dragLineIndex: itemMeta.index + 1,
162
- dragLineLevel: placement.reparentLevel,
163
- };
168
+ return (0, exports.getReparentTarget)(item, placement.reparentLevel, draggedItems);
164
169
  }
165
170
  const maybeAddOneForBelow = placement.type === PlacementType.ReorderAbove ? 0 : 1;
166
171
  const childIndex = item.getIndexInParent() + maybeAddOneForBelow;
167
- const numberOfDragItemsBeforeTarget = (_c = parent
168
- .getChildren()
169
- .slice(0, childIndex)
170
- .reduce((counter, child) => child && (draggedItems === null || draggedItems === void 0 ? void 0 : draggedItems.some((i) => i.getId() === child.getId()))
171
- ? ++counter
172
- : counter, 0)) !== null && _c !== void 0 ? _c : 0;
173
172
  return {
174
173
  item: parent,
175
174
  dragLineIndex: itemMeta.index + maybeAddOneForBelow,
176
175
  dragLineLevel: itemMeta.level,
177
176
  childIndex,
178
177
  // TODO performance could be improved by computing this only when dragcode changed
179
- insertionIndex: childIndex - numberOfDragItemsBeforeTarget,
178
+ insertionIndex: (0, exports.getInsertionIndex)(parent.getChildren(), childIndex, draggedItems),
180
179
  };
181
180
  };
182
- exports.getDropTarget = getDropTarget;
181
+ exports.getDragTarget = getDragTarget;
@@ -10,7 +10,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.expandAllFeature = void 0;
13
- const utils_1 = require("../../utils");
14
13
  exports.expandAllFeature = {
15
14
  key: "expand-all",
16
15
  treeInstance: {
@@ -31,17 +30,41 @@ exports.expandAllFeature = {
31
30
  return;
32
31
  }
33
32
  item.expand();
34
- yield (0, utils_1.poll)(() => !tree.getState().loadingItems.includes(item.getId()));
33
+ yield tree.waitForItemChildrenLoaded(item.getId());
35
34
  yield Promise.all(item.getChildren().map((child) => __awaiter(void 0, void 0, void 0, function* () {
36
- yield (0, utils_1.poll)(() => !tree.getState().loadingItems.includes(child.getId()));
35
+ yield tree.waitForItemChildrenLoaded(item.getId());
37
36
  yield (child === null || child === void 0 ? void 0 : child.expandAll(cancelToken));
38
37
  })));
39
38
  }),
40
39
  collapseAll: ({ item }) => {
40
+ if (!item.isExpanded())
41
+ return;
41
42
  for (const child of item.getChildren()) {
42
43
  child === null || child === void 0 ? void 0 : child.collapseAll();
43
44
  }
44
45
  item.collapse();
45
46
  },
46
47
  },
48
+ hotkeys: {
49
+ expandSelected: {
50
+ hotkey: "Control+Shift+Plus",
51
+ handler: (_, tree) => __awaiter(void 0, void 0, void 0, function* () {
52
+ const cancelToken = { current: false };
53
+ const cancelHandler = (e) => {
54
+ if (e.key === "Escape") {
55
+ cancelToken.current = true;
56
+ }
57
+ };
58
+ document.addEventListener("keydown", cancelHandler);
59
+ yield Promise.all(tree.getSelectedItems().map((item) => item.expandAll(cancelToken)));
60
+ document.removeEventListener("keydown", cancelHandler);
61
+ }),
62
+ },
63
+ collapseSelected: {
64
+ hotkey: "Control+Shift+-",
65
+ handler: (_, tree) => {
66
+ tree.getSelectedItems().forEach((item) => item.collapseAll());
67
+ },
68
+ },
69
+ },
47
70
  };
@@ -1,3 +1,5 @@
1
+ export interface ExpandAllDataRef {
2
+ }
1
3
  export type ExpandAllFeatureDef = {
2
4
  state: {};
3
5
  config: {};
@@ -13,5 +15,5 @@ export type ExpandAllFeatureDef = {
13
15
  }) => Promise<void>;
14
16
  collapseAll: () => void;
15
17
  };
16
- hotkeys: never;
18
+ hotkeys: "expandSelected" | "collapseSelected";
17
19
  };
@@ -4,6 +4,8 @@ exports.hotkeysCoreFeature = void 0;
4
4
  const specialKeys = {
5
5
  Letter: /^[a-z]$/,
6
6
  LetterOrNumber: /^[a-z0-9]$/,
7
+ Plus: /^\+$/,
8
+ Space: /^ $/,
7
9
  };
8
10
  const testHotkeyMatch = (pressedKeys, tree, hotkey) => {
9
11
  const supposedKeys = hotkey.hotkey.split("+");
@@ -23,11 +25,12 @@ exports.hotkeysCoreFeature = {
23
25
  onTreeMount: (tree, element) => {
24
26
  const data = tree.getDataRef();
25
27
  const keydown = (e) => {
26
- var _a, _b;
27
- var _c;
28
- (_a = (_c = data.current).pressedKeys) !== null && _a !== void 0 ? _a : (_c.pressedKeys = new Set());
28
+ var _a, _b, _c, _d;
29
+ var _e;
30
+ (_a = (_e = data.current).pressedKeys) !== null && _a !== void 0 ? _a : (_e.pressedKeys = new Set());
29
31
  const newMatch = !data.current.pressedKeys.has(e.key);
30
32
  data.current.pressedKeys.add(e.key);
33
+ console.log("HOTKEYS", data.current.pressedKeys);
31
34
  const hotkeyName = findHotkeyMatch(data.current.pressedKeys, tree, tree.getHotkeyPresets(), tree.getConfig().hotkeys);
32
35
  if (!hotkeyName)
33
36
  return;
@@ -42,6 +45,7 @@ exports.hotkeysCoreFeature = {
42
45
  if (hotkeyConfig.preventDefault)
43
46
  e.preventDefault();
44
47
  hotkeyConfig.handler(e, tree);
48
+ (_d = (_c = tree.getConfig()).onTreeHotkey) === null || _d === void 0 ? void 0 : _d.call(_c, hotkeyName, e);
45
49
  };
46
50
  const keyup = (e) => {
47
51
  var _a;
@@ -1,4 +1,4 @@
1
- import { CustomHotkeysConfig, ItemInstance, TreeInstance } from "../../types/core";
1
+ import { CustomHotkeysConfig, TreeInstance } from "../../types/core";
2
2
  export interface HotkeyConfig<T> {
3
3
  hotkey: string;
4
4
  canRepeat?: boolean;
@@ -7,17 +7,16 @@ export interface HotkeyConfig<T> {
7
7
  preventDefault?: boolean;
8
8
  handler: (e: KeyboardEvent, tree: TreeInstance<T>) => void;
9
9
  }
10
- export type HotkeysCoreDataRef = {
10
+ export interface HotkeysCoreDataRef {
11
11
  keydownHandler?: (e: KeyboardEvent) => void;
12
12
  keyupHandler?: (e: KeyboardEvent) => void;
13
13
  pressedKeys: Set<string>;
14
- };
14
+ }
15
15
  export type HotkeysCoreFeatureDef<T> = {
16
16
  state: {};
17
17
  config: {
18
18
  hotkeys?: CustomHotkeysConfig<T>;
19
- onTreeHotkey?: (name: string, element: HTMLElement) => void;
20
- onItemHotkey?: (name: string, item: ItemInstance<T>, element: HTMLElement) => void;
19
+ onTreeHotkey?: (name: string, e: KeyboardEvent) => void;
21
20
  };
22
21
  treeInstance: {};
23
22
  itemInstance: {};
@@ -0,0 +1,2 @@
1
+ import { FeatureImplementation } from "../../types/core";
2
+ export declare const keyboardDragAndDropFeature: FeatureImplementation;
@@ -0,0 +1,207 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.keyboardDragAndDropFeature = void 0;
13
+ const utils_1 = require("../drag-and-drop/utils");
14
+ const utils_2 = require("../../utils");
15
+ const types_1 = require("./types");
16
+ const getNextDragTarget = (tree, isUp, dragTarget) => {
17
+ var _a, _b, _c, _d;
18
+ const direction = isUp ? 0 : 1;
19
+ const draggedItems = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggedItems;
20
+ // currently hovering between items
21
+ if ("childIndex" in dragTarget) {
22
+ // TODO move check in reusable function
23
+ const parent = dragTarget.item.getParent();
24
+ const targetedItem = tree.getItems()[dragTarget.dragLineIndex - 1]; // item above dragline
25
+ const targetCategory = targetedItem
26
+ ? (0, utils_1.getItemDropCategory)(targetedItem)
27
+ : utils_1.ItemDropCategory.Item;
28
+ const maxLevel = (_b = targetedItem === null || targetedItem === void 0 ? void 0 : targetedItem.getItemMeta().level) !== null && _b !== void 0 ? _b : 0;
29
+ const minLevel = (_d = (_c = targetedItem === null || targetedItem === void 0 ? void 0 : targetedItem.getItemBelow()) === null || _c === void 0 ? void 0 : _c.getItemMeta().level) !== null && _d !== void 0 ? _d : 0;
30
+ // reparenting
31
+ if (targetCategory === utils_1.ItemDropCategory.LastInGroup) {
32
+ if (isUp && dragTarget.dragLineLevel < maxLevel) {
33
+ return (0, utils_1.getReparentTarget)(targetedItem, dragTarget.dragLineLevel + 1, draggedItems);
34
+ }
35
+ if (!isUp && dragTarget.dragLineLevel > minLevel && parent) {
36
+ return (0, utils_1.getReparentTarget)(targetedItem, dragTarget.dragLineLevel - 1, draggedItems);
37
+ }
38
+ }
39
+ const newIndex = dragTarget.dragLineIndex - 1 + direction;
40
+ const item = tree.getItems()[newIndex];
41
+ return item ? { item } : undefined;
42
+ }
43
+ // moving upwards outside of an open folder
44
+ const targetingExpandedFolder = (0, utils_1.getItemDropCategory)(dragTarget.item) === utils_1.ItemDropCategory.ExpandedFolder;
45
+ if (targetingExpandedFolder && !isUp) {
46
+ return {
47
+ item: dragTarget.item,
48
+ childIndex: 0,
49
+ insertionIndex: (0, utils_1.getInsertionIndex)(dragTarget.item.getChildren(), 0, draggedItems),
50
+ dragLineIndex: dragTarget.item.getItemMeta().index + direction,
51
+ dragLineLevel: dragTarget.item.getItemMeta().level + 1,
52
+ };
53
+ }
54
+ // currently hovering over item
55
+ const childIndex = dragTarget.item.getIndexInParent() + direction;
56
+ return {
57
+ item: dragTarget.item.getParent(),
58
+ childIndex,
59
+ insertionIndex: (0, utils_1.getInsertionIndex)(dragTarget.item.getParent().getChildren(), childIndex, draggedItems),
60
+ dragLineIndex: dragTarget.item.getItemMeta().index + direction,
61
+ dragLineLevel: dragTarget.item.getItemMeta().level,
62
+ };
63
+ };
64
+ const getNextValidDragTarget = (tree, isUp, previousTarget) => {
65
+ var _a, _b;
66
+ if (previousTarget === void 0) { previousTarget = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.dragTarget; }
67
+ if (!previousTarget)
68
+ return undefined;
69
+ const nextTarget = getNextDragTarget(tree, isUp, previousTarget);
70
+ const dataTransfer = (_b = tree.getDataRef().current.kDndDataTransfer) !== null && _b !== void 0 ? _b : null;
71
+ if (!nextTarget)
72
+ return undefined;
73
+ if ((0, utils_1.canDrop)(dataTransfer, nextTarget, tree)) {
74
+ return nextTarget;
75
+ }
76
+ return getNextValidDragTarget(tree, isUp, nextTarget);
77
+ };
78
+ const updateScroll = (tree) => {
79
+ const state = tree.getState().dnd;
80
+ if (!(state === null || state === void 0 ? void 0 : state.dragTarget) || "childIndex" in state.dragTarget)
81
+ return;
82
+ state.dragTarget.item.scrollTo({ block: "nearest", inline: "nearest" });
83
+ };
84
+ const initiateDrag = (tree, draggedItems, dataTransfer) => {
85
+ var _a, _b;
86
+ const focusedItem = tree.getFocusedItem();
87
+ const { canDrag } = tree.getConfig();
88
+ if (draggedItems && canDrag && !canDrag(draggedItems)) {
89
+ return;
90
+ }
91
+ if (draggedItems) {
92
+ tree.applySubStateUpdate("dnd", { draggedItems });
93
+ // getNextValidDragTarget->canDrop needs the draggedItems in state
94
+ (_b = (_a = tree.getConfig()).onStartKeyboardDrag) === null || _b === void 0 ? void 0 : _b.call(_a, draggedItems);
95
+ }
96
+ else if (dataTransfer) {
97
+ tree.getDataRef().current.kDndDataTransfer = dataTransfer;
98
+ }
99
+ const dragTarget = getNextValidDragTarget(tree, false, {
100
+ item: focusedItem,
101
+ });
102
+ if (!dragTarget)
103
+ return;
104
+ tree.applySubStateUpdate("dnd", {
105
+ draggedItems,
106
+ dragTarget,
107
+ });
108
+ tree.applySubStateUpdate("assistiveDndState", types_1.AssistiveDndState.Started);
109
+ updateScroll(tree);
110
+ };
111
+ const moveDragPosition = (tree, isUp) => {
112
+ var _a;
113
+ const dragTarget = getNextValidDragTarget(tree, isUp);
114
+ if (!dragTarget)
115
+ return;
116
+ tree.applySubStateUpdate("dnd", {
117
+ draggedItems: (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggedItems,
118
+ dragTarget,
119
+ });
120
+ tree.applySubStateUpdate("assistiveDndState", types_1.AssistiveDndState.Dragging);
121
+ if (!("childIndex" in dragTarget)) {
122
+ dragTarget.item.setFocused();
123
+ }
124
+ updateScroll(tree);
125
+ };
126
+ exports.keyboardDragAndDropFeature = {
127
+ key: "keyboard-drag-and-drop",
128
+ deps: ["drag-and-drop"],
129
+ getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setAssistiveDndState: (0, utils_2.makeStateUpdater)("assistiveDndState", tree) }, defaultConfig)),
130
+ stateHandlerNames: {
131
+ assistiveDndState: "setAssistiveDndState",
132
+ },
133
+ treeInstance: {
134
+ startKeyboardDrag: ({ tree }, draggedItems) => {
135
+ initiateDrag(tree, draggedItems, undefined);
136
+ },
137
+ startKeyboardDragOnForeignObject: ({ tree }, dataTransfer) => {
138
+ initiateDrag(tree, undefined, dataTransfer);
139
+ },
140
+ stopKeyboardDrag: ({ tree }) => {
141
+ tree.getDataRef().current.kDndDataTransfer = undefined;
142
+ tree.applySubStateUpdate("dnd", null);
143
+ tree.applySubStateUpdate("assistiveDndState", types_1.AssistiveDndState.None);
144
+ },
145
+ },
146
+ hotkeys: {
147
+ startDrag: {
148
+ hotkey: "Control+Shift+D",
149
+ preventDefault: true,
150
+ isEnabled: (tree) => !tree.getState().dnd,
151
+ handler: (_, tree) => {
152
+ tree.startKeyboardDrag(tree.getSelectedItems());
153
+ },
154
+ },
155
+ dragUp: {
156
+ hotkey: "ArrowUp",
157
+ preventDefault: true,
158
+ isEnabled: (tree) => !!tree.getState().dnd,
159
+ handler: (_, tree) => {
160
+ moveDragPosition(tree, true);
161
+ },
162
+ },
163
+ dragDown: {
164
+ hotkey: "ArrowDown",
165
+ preventDefault: true,
166
+ isEnabled: (tree) => !!tree.getState().dnd,
167
+ handler: (_, tree) => {
168
+ moveDragPosition(tree, false);
169
+ },
170
+ },
171
+ cancelDrag: {
172
+ hotkey: "Escape",
173
+ isEnabled: (tree) => !!tree.getState().dnd,
174
+ handler: (_, tree) => {
175
+ tree.stopKeyboardDrag();
176
+ },
177
+ },
178
+ completeDrag: {
179
+ hotkey: "Enter",
180
+ preventDefault: true,
181
+ isEnabled: (tree) => !!tree.getState().dnd,
182
+ handler: (e, tree) => __awaiter(void 0, void 0, void 0, function* () {
183
+ var _a, _b, _c, _d;
184
+ e.stopPropagation();
185
+ // TODO copied from keyboard onDrop, unify them
186
+ const dataRef = tree.getDataRef();
187
+ const target = tree.getDragTarget();
188
+ const dataTransfer = (_a = dataRef.current.kDndDataTransfer) !== null && _a !== void 0 ? _a : null;
189
+ if (!target || !(0, utils_1.canDrop)(dataTransfer, target, tree)) {
190
+ return;
191
+ }
192
+ const config = tree.getConfig();
193
+ const draggedItems = (_b = tree.getState().dnd) === null || _b === void 0 ? void 0 : _b.draggedItems;
194
+ dataRef.current.lastDragCode = undefined;
195
+ tree.applySubStateUpdate("dnd", null);
196
+ if (draggedItems) {
197
+ yield ((_c = config.onDrop) === null || _c === void 0 ? void 0 : _c.call(config, draggedItems, target));
198
+ tree.getItemInstance(draggedItems[0].getId()).setFocused();
199
+ }
200
+ else if (dataTransfer) {
201
+ yield ((_d = config.onDropForeignDragObject) === null || _d === void 0 ? void 0 : _d.call(config, dataTransfer, target));
202
+ }
203
+ tree.applySubStateUpdate("assistiveDndState", types_1.AssistiveDndState.Completed);
204
+ }),
205
+ },
206
+ },
207
+ };
@@ -0,0 +1,27 @@
1
+ import { ItemInstance, SetStateFn } from "../../types/core";
2
+ export interface KDndDataRef {
3
+ kDndDataTransfer: DataTransfer | undefined;
4
+ }
5
+ export declare enum AssistiveDndState {
6
+ None = 0,
7
+ Started = 1,
8
+ Dragging = 2,
9
+ Completed = 3,
10
+ Aborted = 4
11
+ }
12
+ export type KeyboardDragAndDropFeatureDef<T> = {
13
+ state: {
14
+ assistiveDndState?: AssistiveDndState | null;
15
+ };
16
+ config: {
17
+ setAssistiveDndState?: SetStateFn<AssistiveDndState | undefined | null>;
18
+ onStartKeyboardDrag?: (items: ItemInstance<T>[]) => void;
19
+ };
20
+ treeInstance: {
21
+ startKeyboardDrag: (items: ItemInstance<T>[]) => void;
22
+ startKeyboardDragOnForeignObject: (dataTransfer: DataTransfer) => void;
23
+ stopKeyboardDrag: () => void;
24
+ };
25
+ itemInstance: {};
26
+ hotkeys: "startDrag" | "cancelDrag" | "completeDrag" | "dragUp" | "dragDown";
27
+ };
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AssistiveDndState = void 0;
4
+ var AssistiveDndState;
5
+ (function (AssistiveDndState) {
6
+ AssistiveDndState[AssistiveDndState["None"] = 0] = "None";
7
+ AssistiveDndState[AssistiveDndState["Started"] = 1] = "Started";
8
+ AssistiveDndState[AssistiveDndState["Dragging"] = 2] = "Dragging";
9
+ AssistiveDndState[AssistiveDndState["Completed"] = 3] = "Completed";
10
+ AssistiveDndState[AssistiveDndState["Aborted"] = 4] = "Aborted";
11
+ })(AssistiveDndState || (exports.AssistiveDndState = AssistiveDndState = {}));
@@ -30,10 +30,10 @@ exports.propMemoizationFeature = {
30
30
  "selection",
31
31
  ],
32
32
  treeInstance: {
33
- getContainerProps: ({ tree, prev }) => {
33
+ getContainerProps: ({ tree, prev }, treeLabel) => {
34
34
  var _a;
35
35
  const dataRef = tree.getDataRef();
36
- const props = (_a = prev === null || prev === void 0 ? void 0 : prev()) !== null && _a !== void 0 ? _a : {};
36
+ const props = (_a = prev === null || prev === void 0 ? void 0 : prev(treeLabel)) !== null && _a !== void 0 ? _a : {};
37
37
  return memoize(props, dataRef.current);
38
38
  },
39
39
  },
@@ -1,6 +1,6 @@
1
- export type PropMemoizationDataRef = {
1
+ export interface PropMemoizationDataRef {
2
2
  memoizedProps?: Record<string, any>;
3
- };
3
+ }
4
4
  export type PropMemoizationFeatureDef = {
5
5
  state: {};
6
6
  config: {};
@@ -40,11 +40,11 @@ exports.renamingFeature = {
40
40
  tree.applySubStateUpdate("renamingValue", item.getItemName());
41
41
  },
42
42
  getRenameInputProps: ({ tree }) => ({
43
+ ref: (r) => r === null || r === void 0 ? void 0 : r.focus(),
43
44
  onBlur: () => tree.abortRenaming(),
44
45
  value: tree.getRenamingValue(),
45
46
  onChange: (e) => {
46
47
  var _a;
47
- // TODO custom type with e.target.value
48
48
  tree.applySubStateUpdate("renamingValue", (_a = e.target) === null || _a === void 0 ? void 0 : _a.value);
49
49
  },
50
50
  }),
@@ -43,10 +43,12 @@ exports.searchFeature = {
43
43
  }
44
44
  },
45
45
  getSearchInputElement: ({ tree }) => { var _a; return (_a = tree.getDataRef().current.searchInput) !== null && _a !== void 0 ? _a : null; },
46
+ // TODO memoize with propMemoizationFeature
46
47
  getSearchInputElementProps: ({ tree }) => ({
47
48
  value: tree.getSearchValue(),
48
49
  onChange: (e) => tree.setSearch(e.target.value),
49
50
  onBlur: () => tree.closeSearch(),
51
+ ref: tree.registerSearchInputElement,
50
52
  }),
51
53
  getSearchMatchingItems: (0, utils_1.memo)(({ tree }) => [
52
54
  tree.getSearchValue(),
@@ -1,9 +1,9 @@
1
1
  import { ItemInstance, SetStateFn } from "../../types/core";
2
2
  import { HotkeysCoreDataRef } from "../hotkeys-core/types";
3
- export type SearchFeatureDataRef<T = any> = HotkeysCoreDataRef & {
3
+ export interface SearchFeatureDataRef<T = any> extends HotkeysCoreDataRef {
4
4
  matchingItems: ItemInstance<T>[];
5
5
  searchInput: HTMLInputElement | null;
6
- };
6
+ }
7
7
  export type SearchFeatureDef<T> = {
8
8
  state: {
9
9
  search: string | null;
@@ -13,7 +13,6 @@ exports.selectionFeature = {
13
13
  setSelectedItems: ({ tree }, selectedItems) => {
14
14
  tree.applySubStateUpdate("selectedItems", selectedItems);
15
15
  },
16
- // TODO memo
17
16
  getSelectedItems: ({ tree }) => {
18
17
  return tree.getState().selectedItems.map(tree.getItemInstance);
19
18
  },
@@ -81,9 +80,10 @@ exports.selectionFeature = {
81
80
  // tree.setSelectedItems([tree.getFocusedItem().getId()]);
82
81
  // },
83
82
  // },
84
- toggleSelectItem: {
85
- hotkey: "ctrl+space",
86
- handler: (e, tree) => {
83
+ toggleSelectedItem: {
84
+ hotkey: "Control+Space",
85
+ preventDefault: true,
86
+ handler: (_, tree) => {
87
87
  tree.getFocusedItem().toggleSelect();
88
88
  },
89
89
  },
@@ -17,5 +17,5 @@ export type SelectionFeatureDef<T> = {
17
17
  isSelected: () => boolean;
18
18
  selectUpTo: (ctrl: boolean) => void;
19
19
  };
20
- hotkeys: "toggleSelectItem" | "selectUpwards" | "selectDownwards" | "selectAll";
20
+ hotkeys: "toggleSelectedItem" | "selectUpwards" | "selectDownwards" | "selectAll";
21
21
  };