@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.
- package/CHANGELOG.md +8 -1
- package/lib/cjs/core/create-tree.js +13 -4
- package/lib/cjs/features/async-data-loader/feature.js +73 -48
- package/lib/cjs/features/async-data-loader/types.d.ts +17 -14
- package/lib/cjs/features/drag-and-drop/feature.js +98 -93
- package/lib/cjs/features/drag-and-drop/types.d.ts +17 -29
- package/lib/cjs/features/drag-and-drop/types.js +7 -7
- package/lib/cjs/features/drag-and-drop/utils.d.ts +18 -3
- package/lib/cjs/features/drag-and-drop/utils.js +50 -51
- package/lib/cjs/features/expand-all/feature.js +26 -3
- package/lib/cjs/features/expand-all/types.d.ts +3 -1
- package/lib/cjs/features/hotkeys-core/feature.js +7 -3
- package/lib/cjs/features/hotkeys-core/types.d.ts +4 -5
- package/lib/cjs/features/keyboard-drag-and-drop/feature.d.ts +2 -0
- package/lib/cjs/features/keyboard-drag-and-drop/feature.js +207 -0
- package/lib/cjs/features/keyboard-drag-and-drop/types.d.ts +27 -0
- package/lib/cjs/features/keyboard-drag-and-drop/types.js +11 -0
- package/lib/cjs/features/prop-memoization/feature.js +2 -2
- package/lib/cjs/features/prop-memoization/types.d.ts +2 -2
- package/lib/cjs/features/renaming/feature.js +1 -1
- package/lib/cjs/features/search/feature.js +2 -0
- package/lib/cjs/features/search/types.d.ts +2 -2
- package/lib/cjs/features/selection/feature.js +4 -4
- package/lib/cjs/features/selection/types.d.ts +1 -1
- package/lib/cjs/features/sync-data-loader/feature.js +31 -5
- package/lib/cjs/features/sync-data-loader/types.d.ts +5 -5
- package/lib/cjs/features/tree/feature.js +4 -7
- package/lib/cjs/features/tree/types.d.ts +7 -5
- package/lib/cjs/index.d.ts +2 -0
- package/lib/cjs/index.js +2 -0
- package/lib/cjs/mddocs-entry.d.ts +10 -0
- package/lib/cjs/test-utils/test-tree-do.d.ts +2 -2
- package/lib/cjs/test-utils/test-tree-do.js +19 -6
- package/lib/cjs/test-utils/test-tree-expect.d.ts +5 -3
- package/lib/cjs/test-utils/test-tree-expect.js +3 -0
- package/lib/cjs/test-utils/test-tree.d.ts +2 -1
- package/lib/cjs/test-utils/test-tree.js +24 -21
- package/lib/cjs/types/core.d.ts +2 -1
- package/lib/cjs/utilities/create-on-drop-handler.d.ts +2 -2
- package/lib/cjs/utilities/create-on-drop-handler.js +13 -4
- package/lib/cjs/utilities/insert-items-at-target.d.ts +2 -2
- package/lib/cjs/utilities/insert-items-at-target.js +21 -12
- package/lib/cjs/utilities/remove-items-from-parents.d.ts +1 -1
- package/lib/cjs/utilities/remove-items-from-parents.js +12 -3
- package/lib/esm/core/create-tree.js +13 -4
- package/lib/esm/features/async-data-loader/feature.js +73 -48
- package/lib/esm/features/async-data-loader/types.d.ts +17 -14
- package/lib/esm/features/drag-and-drop/feature.js +99 -94
- package/lib/esm/features/drag-and-drop/types.d.ts +17 -29
- package/lib/esm/features/drag-and-drop/types.js +6 -6
- package/lib/esm/features/drag-and-drop/utils.d.ts +18 -3
- package/lib/esm/features/drag-and-drop/utils.js +45 -49
- package/lib/esm/features/expand-all/feature.js +26 -3
- package/lib/esm/features/expand-all/types.d.ts +3 -1
- package/lib/esm/features/hotkeys-core/feature.js +7 -3
- package/lib/esm/features/hotkeys-core/types.d.ts +4 -5
- package/lib/esm/features/keyboard-drag-and-drop/feature.d.ts +2 -0
- package/lib/esm/features/keyboard-drag-and-drop/feature.js +204 -0
- package/lib/esm/features/keyboard-drag-and-drop/types.d.ts +27 -0
- package/lib/esm/features/keyboard-drag-and-drop/types.js +8 -0
- package/lib/esm/features/prop-memoization/feature.js +2 -2
- package/lib/esm/features/prop-memoization/types.d.ts +2 -2
- package/lib/esm/features/renaming/feature.js +1 -1
- package/lib/esm/features/search/feature.js +2 -0
- package/lib/esm/features/search/types.d.ts +2 -2
- package/lib/esm/features/selection/feature.js +4 -4
- package/lib/esm/features/selection/types.d.ts +1 -1
- package/lib/esm/features/sync-data-loader/feature.js +31 -5
- package/lib/esm/features/sync-data-loader/types.d.ts +5 -5
- package/lib/esm/features/tree/feature.js +4 -7
- package/lib/esm/features/tree/types.d.ts +7 -5
- package/lib/esm/index.d.ts +2 -0
- package/lib/esm/index.js +2 -0
- package/lib/esm/mddocs-entry.d.ts +10 -0
- package/lib/esm/test-utils/test-tree-do.d.ts +2 -2
- package/lib/esm/test-utils/test-tree-do.js +19 -6
- package/lib/esm/test-utils/test-tree-expect.d.ts +5 -3
- package/lib/esm/test-utils/test-tree-expect.js +3 -0
- package/lib/esm/test-utils/test-tree.d.ts +2 -1
- package/lib/esm/test-utils/test-tree.js +24 -21
- package/lib/esm/types/core.d.ts +2 -1
- package/lib/esm/utilities/create-on-drop-handler.d.ts +2 -2
- package/lib/esm/utilities/create-on-drop-handler.js +13 -4
- package/lib/esm/utilities/insert-items-at-target.d.ts +2 -2
- package/lib/esm/utilities/insert-items-at-target.js +21 -12
- package/lib/esm/utilities/remove-items-from-parents.d.ts +1 -1
- package/lib/esm/utilities/remove-items-from-parents.js +12 -3
- package/package.json +2 -2
- package/src/core/core.spec.ts +31 -0
- package/src/core/create-tree.ts +15 -5
- package/src/features/async-data-loader/async-data-loader.spec.ts +10 -6
- package/src/features/async-data-loader/feature.ts +76 -48
- package/src/features/async-data-loader/types.ts +18 -11
- package/src/features/drag-and-drop/drag-and-drop.spec.ts +75 -89
- package/src/features/drag-and-drop/feature.ts +21 -22
- package/src/features/drag-and-drop/types.ts +23 -35
- package/src/features/drag-and-drop/utils.ts +69 -57
- package/src/features/expand-all/feature.ts +29 -5
- package/src/features/expand-all/types.ts +3 -1
- package/src/features/hotkeys-core/feature.ts +4 -0
- package/src/features/hotkeys-core/types.ts +4 -13
- package/src/features/keyboard-drag-and-drop/feature.ts +255 -0
- package/src/features/keyboard-drag-and-drop/keyboard-drag-and-drop.spec.ts +401 -0
- package/src/features/keyboard-drag-and-drop/types.ts +30 -0
- package/src/features/prop-memoization/feature.ts +2 -2
- package/src/features/prop-memoization/prop-memoization.spec.ts +2 -2
- package/src/features/prop-memoization/types.ts +2 -2
- package/src/features/renaming/feature.ts +8 -2
- package/src/features/search/feature.ts +2 -0
- package/src/features/search/types.ts +2 -2
- package/src/features/selection/feature.ts +4 -4
- package/src/features/selection/types.ts +1 -1
- package/src/features/sync-data-loader/feature.ts +26 -7
- package/src/features/sync-data-loader/types.ts +5 -5
- package/src/features/tree/feature.ts +8 -11
- package/src/features/tree/types.ts +7 -5
- package/src/index.ts +2 -0
- package/src/mddocs-entry.ts +16 -0
- package/src/test-utils/test-tree-do.ts +3 -3
- package/src/test-utils/test-tree-expect.ts +7 -2
- package/src/test-utils/test-tree.ts +26 -22
- package/src/types/core.ts +2 -0
- package/src/utilities/create-on-drop-handler.ts +4 -4
- package/src/utilities/insert-items-at-target.ts +18 -14
- 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
|
|
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
|
-
|
|
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() ===
|
|
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.
|
|
53
|
-
const leftPixels = bb ? e.
|
|
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,28 @@ const getNthParent = (item, n) => {
|
|
|
100
111
|
}
|
|
101
112
|
return getNthParent(item.getParent(), n);
|
|
102
113
|
};
|
|
103
|
-
|
|
104
|
-
|
|
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
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
117
|
+
const reparentedTarget = getNthParent(item, reparentLevel - 1);
|
|
118
|
+
const targetItemAbove = getNthParent(item, reparentLevel); // .getItemBelow()!;
|
|
119
|
+
const targetIndex = targetItemAbove.getIndexInParent() + 1;
|
|
120
|
+
// TODO possibly count items dragged out above the new target
|
|
121
|
+
return {
|
|
122
|
+
item: reparentedTarget,
|
|
123
|
+
childIndex: targetIndex,
|
|
124
|
+
insertionIndex: getInsertionIndex(reparentedTarget.getChildren(), targetIndex, draggedItems),
|
|
125
|
+
dragLineIndex: itemMeta.index + 1,
|
|
126
|
+
dragLineLevel: reparentLevel,
|
|
114
127
|
};
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
: null;
|
|
128
|
+
};
|
|
129
|
+
export const getDragTarget = (e, item, tree, canReorder = tree.getConfig().canReorder) => {
|
|
130
|
+
var _a;
|
|
131
|
+
const draggedItems = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggedItems;
|
|
132
|
+
const itemMeta = item.getItemMeta();
|
|
133
|
+
const parent = item.getParent();
|
|
134
|
+
const itemTarget = { item };
|
|
135
|
+
const parentTarget = parent ? { item: parent } : null;
|
|
124
136
|
const canBecomeSibling = parentTarget && canDrop(e.dataTransfer, parentTarget, tree);
|
|
125
137
|
const canMakeChild = canDrop(e.dataTransfer, itemTarget, tree);
|
|
126
138
|
const placement = getTargetPlacement(e, item, tree, canMakeChild);
|
|
@@ -131,8 +143,8 @@ export const getDropTarget = (e, item, tree, canReorder = tree.getConfig().canRe
|
|
|
131
143
|
return parentTarget;
|
|
132
144
|
}
|
|
133
145
|
if (!canReorder && parent && !canBecomeSibling) {
|
|
134
|
-
// TODO! this breaks in story DND/Can Drop. Maybe move this logic into a composable
|
|
135
|
-
return
|
|
146
|
+
// TODO! this breaks in story DND/Can Drop. Maybe move this logic into a composable DragTargetStrategy[] ?
|
|
147
|
+
return getDragTarget(e, parent, tree, false);
|
|
136
148
|
}
|
|
137
149
|
if (!parent) {
|
|
138
150
|
// Shouldn't happen, but if dropped "next" to root item, just drop it inside
|
|
@@ -142,35 +154,19 @@ export const getDropTarget = (e, item, tree, canReorder = tree.getConfig().canRe
|
|
|
142
154
|
return itemTarget;
|
|
143
155
|
}
|
|
144
156
|
if (!canBecomeSibling) {
|
|
145
|
-
return
|
|
157
|
+
return getDragTarget(e, parent, tree, false);
|
|
146
158
|
}
|
|
147
159
|
if (placement.type === PlacementType.Reparent) {
|
|
148
|
-
|
|
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
|
-
};
|
|
160
|
+
return getReparentTarget(item, placement.reparentLevel, draggedItems);
|
|
159
161
|
}
|
|
160
162
|
const maybeAddOneForBelow = placement.type === PlacementType.ReorderAbove ? 0 : 1;
|
|
161
163
|
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
164
|
return {
|
|
169
165
|
item: parent,
|
|
170
166
|
dragLineIndex: itemMeta.index + maybeAddOneForBelow,
|
|
171
167
|
dragLineLevel: itemMeta.level,
|
|
172
168
|
childIndex,
|
|
173
169
|
// TODO performance could be improved by computing this only when dragcode changed
|
|
174
|
-
insertionIndex: childIndex
|
|
170
|
+
insertionIndex: getInsertionIndex(parent.getChildren(), childIndex, draggedItems),
|
|
175
171
|
};
|
|
176
172
|
};
|
|
@@ -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
|
|
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
|
|
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:
|
|
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
|
|
25
|
-
(_a = (
|
|
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,
|
|
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
|
|
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,
|
|
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,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
|
},
|
|
@@ -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
|
|
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
|
-
|
|
82
|
-
hotkey: "
|
|
83
|
-
|
|
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: "
|
|
20
|
+
hotkeys: "toggleSelectedItem" | "selectUpwards" | "selectDownwards" | "selectAll";
|
|
21
21
|
};
|