@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.
- package/CHANGELOG.md +13 -0
- 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 +49 -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 +44 -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 +67 -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,27 @@ 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
|
+
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
|
135
|
-
return
|
|
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
|
|
156
|
+
return getDragTarget(e, parent, tree, false);
|
|
146
157
|
}
|
|
147
158
|
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
|
-
};
|
|
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
|
|
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
|
|
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
|
};
|