@headless-tree/core 0.0.13 → 0.0.15

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