@headless-tree/core 0.0.14 → 1.0.0
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 +25 -3
- package/lib/cjs/features/drag-and-drop/utils.js +51 -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 +206 -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 +33 -11
- package/lib/cjs/features/prop-memoization/types.d.ts +8 -3
- 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 -9
- 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 +25 -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 +203 -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 +33 -11
- package/lib/esm/features/prop-memoization/types.d.ts +8 -3
- 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 -9
- 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 +26 -22
- package/src/features/drag-and-drop/types.ts +23 -35
- package/src/features/drag-and-drop/utils.ts +70 -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 +402 -0
- package/src/features/keyboard-drag-and-drop/types.ts +30 -0
- package/src/features/prop-memoization/feature.ts +27 -8
- package/src/features/prop-memoization/prop-memoization.spec.ts +2 -2
- package/src/features/prop-memoization/types.ts +8 -3
- 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 -13
- 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
|
@@ -27,7 +27,7 @@ export const treeFeature: FeatureImplementation<any> = {
|
|
|
27
27
|
const { rootItemId } = tree.getConfig();
|
|
28
28
|
const { expandedItems } = tree.getState();
|
|
29
29
|
const flatItems: ItemMeta[] = [];
|
|
30
|
-
const expandedItemsSet = new Set(expandedItems);
|
|
30
|
+
const expandedItemsSet = new Set(expandedItems); // TODO support setting state expandedItems as set instead of array
|
|
31
31
|
|
|
32
32
|
const recursiveAdd = (
|
|
33
33
|
itemId: string,
|
|
@@ -46,7 +46,6 @@ export const treeFeature: FeatureImplementation<any> = {
|
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
if (expandedItemsSet.has(itemId)) {
|
|
49
|
-
// TODO THIS MADE A HUGE DIFFERENCE!
|
|
50
49
|
const children = tree.retrieveChildrenIds(itemId) ?? [];
|
|
51
50
|
let i = 0;
|
|
52
51
|
for (const childId of children) {
|
|
@@ -64,7 +63,6 @@ export const treeFeature: FeatureImplementation<any> = {
|
|
|
64
63
|
return flatItems;
|
|
65
64
|
},
|
|
66
65
|
|
|
67
|
-
// TODO memo
|
|
68
66
|
getFocusedItem: ({ tree }) => {
|
|
69
67
|
return (
|
|
70
68
|
tree.getItemInstance(tree.getState().focusedItem ?? "") ??
|
|
@@ -96,12 +94,11 @@ export const treeFeature: FeatureImplementation<any> = {
|
|
|
96
94
|
});
|
|
97
95
|
},
|
|
98
96
|
|
|
99
|
-
|
|
100
|
-
getContainerProps: ({ prev, tree }) => ({
|
|
97
|
+
getContainerProps: ({ prev, tree }, treeLabel) => ({
|
|
101
98
|
...prev?.(),
|
|
102
99
|
role: "tree",
|
|
103
|
-
"aria-label": "",
|
|
104
|
-
ref: tree.registerElement,
|
|
100
|
+
"aria-label": treeLabel ?? "",
|
|
101
|
+
ref: tree.registerElement,
|
|
105
102
|
}),
|
|
106
103
|
|
|
107
104
|
// relevant for hotkeys of this feature
|
|
@@ -119,7 +116,7 @@ export const treeFeature: FeatureImplementation<any> = {
|
|
|
119
116
|
const itemMeta = item.getItemMeta();
|
|
120
117
|
return {
|
|
121
118
|
...prev?.(),
|
|
122
|
-
ref: item.registerElement,
|
|
119
|
+
ref: item.registerElement,
|
|
123
120
|
role: "treeitem",
|
|
124
121
|
"aria-setsize": itemMeta.setSize,
|
|
125
122
|
"aria-posinset": itemMeta.posInSet,
|
|
@@ -152,7 +149,7 @@ export const treeFeature: FeatureImplementation<any> = {
|
|
|
152
149
|
return;
|
|
153
150
|
}
|
|
154
151
|
|
|
155
|
-
if (tree.getState().
|
|
152
|
+
if (tree.getState().loadingItemChildrens?.includes(itemId)) {
|
|
156
153
|
return;
|
|
157
154
|
}
|
|
158
155
|
|
|
@@ -202,10 +199,8 @@ export const treeFeature: FeatureImplementation<any> = {
|
|
|
202
199
|
? tree.getItemInstance(item.getItemMeta().parentId)
|
|
203
200
|
: undefined,
|
|
204
201
|
getIndexInParent: ({ item }) => item.getItemMeta().posInSet,
|
|
205
|
-
getChildren: ({ tree,
|
|
206
|
-
tree
|
|
207
|
-
.retrieveChildrenIds(item.getItemMeta().itemId)
|
|
208
|
-
.map((id) => tree.getItemInstance(id)),
|
|
202
|
+
getChildren: ({ tree, itemId }) =>
|
|
203
|
+
tree.retrieveChildrenIds(itemId).map((id) => tree.getItemInstance(id)),
|
|
209
204
|
getTree: ({ tree }) => tree as any,
|
|
210
205
|
getItemAbove: ({ tree, item }) =>
|
|
211
206
|
tree.getItems()[item.getItemMeta().index - 1],
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { ItemInstance, SetStateFn, TreeInstance } from "../../types/core";
|
|
2
2
|
|
|
3
|
-
export
|
|
3
|
+
export interface ItemMeta {
|
|
4
4
|
itemId: string;
|
|
5
5
|
parentId: string;
|
|
6
6
|
level: number;
|
|
7
7
|
index: number;
|
|
8
8
|
setSize: number;
|
|
9
9
|
posInSet: number;
|
|
10
|
-
}
|
|
10
|
+
}
|
|
11
11
|
|
|
12
|
-
export
|
|
12
|
+
export interface TreeItemDataRef {
|
|
13
13
|
memoizedValues: Record<string, any>;
|
|
14
14
|
memoizedDeps: Record<string, any[] | undefined>;
|
|
15
|
-
}
|
|
15
|
+
}
|
|
16
16
|
|
|
17
17
|
export type TreeFeatureDef<T> = {
|
|
18
18
|
state: {
|
|
@@ -38,7 +38,9 @@ export type TreeFeatureDef<T> = {
|
|
|
38
38
|
focusPreviousItem: () => void;
|
|
39
39
|
updateDomFocus: () => void;
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
/** Pass to the container rendering the tree children. The `treeLabel` parameter
|
|
42
|
+
* will be passed as `aria-label` parameter, and is recommended to be set. */
|
|
43
|
+
getContainerProps: (treeLabel?: string) => Record<string, any>;
|
|
42
44
|
};
|
|
43
45
|
itemInstance: {
|
|
44
46
|
getId: () => string;
|
package/src/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ export * from "./core/create-tree";
|
|
|
4
4
|
export * from "./features/tree/types";
|
|
5
5
|
export { MainFeatureDef, InstanceBuilder } from "./features/main/types";
|
|
6
6
|
export * from "./features/drag-and-drop/types";
|
|
7
|
+
export * from "./features/keyboard-drag-and-drop/types";
|
|
7
8
|
export * from "./features/selection/types";
|
|
8
9
|
export * from "./features/async-data-loader/types";
|
|
9
10
|
export * from "./features/sync-data-loader/types";
|
|
@@ -18,6 +19,7 @@ export * from "./features/hotkeys-core/feature";
|
|
|
18
19
|
export * from "./features/async-data-loader/feature";
|
|
19
20
|
export * from "./features/sync-data-loader/feature";
|
|
20
21
|
export * from "./features/drag-and-drop/feature";
|
|
22
|
+
export * from "./features/keyboard-drag-and-drop/feature";
|
|
21
23
|
export * from "./features/search/feature";
|
|
22
24
|
export * from "./features/renaming/feature";
|
|
23
25
|
export * from "./features/expand-all/feature";
|
package/src/mddocs-entry.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { SelectionFeatureDef } from "./features/selection/types";
|
|
|
9
9
|
import { SyncDataLoaderFeatureDef } from "./features/sync-data-loader/types";
|
|
10
10
|
import { TreeFeatureDef } from "./features/tree/types";
|
|
11
11
|
import { PropMemoizationFeatureDef } from "./features/prop-memoization/types";
|
|
12
|
+
import { KeyboardDragAndDropFeatureDef } from "./features/keyboard-drag-and-drop/types";
|
|
12
13
|
|
|
13
14
|
export * from ".";
|
|
14
15
|
|
|
@@ -49,6 +50,21 @@ export type DragAndDropFeatureItemInstance<T> =
|
|
|
49
50
|
DragAndDropFeatureDef<T>["itemInstance"];
|
|
50
51
|
export type DragAndDropFeatureHotkeys<T> = DragAndDropFeatureDef<T>["hotkeys"];
|
|
51
52
|
|
|
53
|
+
/** @interface */
|
|
54
|
+
export type KeyboardDragAndDropFeatureConfig<T> =
|
|
55
|
+
KeyboardDragAndDropFeatureDef<T>["config"];
|
|
56
|
+
/** @interface */
|
|
57
|
+
export type KeyboardDragAndDropFeatureState<T> =
|
|
58
|
+
KeyboardDragAndDropFeatureDef<T>["state"];
|
|
59
|
+
/** @interface */
|
|
60
|
+
export type KeyboardDragAndDropFeatureTreeInstance<T> =
|
|
61
|
+
KeyboardDragAndDropFeatureDef<T>["treeInstance"];
|
|
62
|
+
/** @interface */
|
|
63
|
+
export type KeyboardDragAndDropFeatureItemInstance<T> =
|
|
64
|
+
KeyboardDragAndDropFeatureDef<T>["itemInstance"];
|
|
65
|
+
export type KeyboardDragAndDropFeatureHotkeys<T> =
|
|
66
|
+
KeyboardDragAndDropFeatureDef<T>["hotkeys"];
|
|
67
|
+
|
|
52
68
|
/** @interface */
|
|
53
69
|
export type ExpandAllFeatureConfig = ExpandAllFeatureDef["config"];
|
|
54
70
|
/** @interface */
|
|
@@ -108,13 +108,13 @@ export class TestTreeDo<T> {
|
|
|
108
108
|
return e;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
drop(itemId: string, event?: DragEvent) {
|
|
111
|
+
async drop(itemId: string, event?: DragEvent) {
|
|
112
112
|
const e = event ?? TestTree.dragEvent();
|
|
113
|
-
this.itemProps(itemId).onDrop(e);
|
|
113
|
+
await this.itemProps(itemId).onDrop(e);
|
|
114
114
|
return e;
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
dragOverAndDrop(itemId: string, event?: DragEvent) {
|
|
117
|
+
async dragOverAndDrop(itemId: string, event?: DragEvent) {
|
|
118
118
|
const e = event ?? TestTree.dragEvent();
|
|
119
119
|
this.dragOver(itemId, e);
|
|
120
120
|
return this.drop(itemId, e);
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import { Mock, expect } from "vitest";
|
|
3
3
|
import { DragEvent } from "react";
|
|
4
4
|
import { TestTree } from "./test-tree";
|
|
5
|
-
import {
|
|
5
|
+
import { DragTarget } from "../features/drag-and-drop/types";
|
|
6
|
+
import { TreeState } from "../types/core";
|
|
6
7
|
|
|
7
8
|
export class TestTreeExpect<T> {
|
|
8
9
|
protected itemInstance(itemId: string) {
|
|
@@ -39,7 +40,11 @@ export class TestTreeExpect<T> {
|
|
|
39
40
|
expect(itemChildren).toEqual(children);
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
substate<K extends keyof TreeState<T>>(key: K, value: TreeState<T>[K]) {
|
|
44
|
+
expect(this.tree.instance.getState()[key]).toEqual(value);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
dropped(draggedItems: string[], target: DragTarget<any>) {
|
|
43
48
|
expect(this.tree.instance.getConfig().onDrop).toBeCalledWith(
|
|
44
49
|
draggedItems.map((id) => this.tree.item(id)),
|
|
45
50
|
target,
|
|
@@ -8,6 +8,7 @@ import { TestTreeExpect } from "./test-tree-expect";
|
|
|
8
8
|
import { syncDataLoaderFeature } from "../features/sync-data-loader/feature";
|
|
9
9
|
import { asyncDataLoaderFeature } from "../features/async-data-loader/feature";
|
|
10
10
|
import { buildProxiedInstance } from "../core/build-proxified-instance";
|
|
11
|
+
import { TreeDataLoader } from "../features/sync-data-loader/types";
|
|
11
12
|
|
|
12
13
|
vi.useFakeTimers({ shouldAdvanceTime: true });
|
|
13
14
|
|
|
@@ -20,13 +21,32 @@ export class TestTree<T = string> {
|
|
|
20
21
|
|
|
21
22
|
private static asyncLoaderResolvers: (() => void)[] = [];
|
|
22
23
|
|
|
24
|
+
private asyncDataLoaderImp: TreeDataLoader<T> = {
|
|
25
|
+
getItem: async (id: string) => {
|
|
26
|
+
await new Promise<void>((r) => {
|
|
27
|
+
(r as any).debugName = `Loading getItem ${id}`;
|
|
28
|
+
TestTree.asyncLoaderResolvers.push(r);
|
|
29
|
+
});
|
|
30
|
+
return id as T;
|
|
31
|
+
},
|
|
32
|
+
getChildren: async (id: string) => {
|
|
33
|
+
await new Promise<void>((r) => {
|
|
34
|
+
(r as any).debugName = `Loading getChildren ${id}`;
|
|
35
|
+
TestTree.asyncLoaderResolvers.push(r);
|
|
36
|
+
});
|
|
37
|
+
return [`${id}1`, `${id}2`, `${id}3`, `${id}4`];
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
23
41
|
suits = {
|
|
24
42
|
sync: () => ({
|
|
25
43
|
tree: this.withFeatures(syncDataLoaderFeature),
|
|
26
44
|
title: "Synchronous Data Loader",
|
|
27
45
|
}),
|
|
28
46
|
async: () => ({
|
|
29
|
-
tree: this.withFeatures(asyncDataLoaderFeature)
|
|
47
|
+
tree: this.withFeatures(asyncDataLoaderFeature).with({
|
|
48
|
+
dataLoader: this.asyncDataLoaderImp,
|
|
49
|
+
}),
|
|
30
50
|
title: "Asynchronous Data Loader",
|
|
31
51
|
}),
|
|
32
52
|
proxifiedSync: () => ({
|
|
@@ -65,12 +85,12 @@ export class TestTree<T = string> {
|
|
|
65
85
|
private constructor(private config: TreeConfig<T>) {}
|
|
66
86
|
|
|
67
87
|
static async resolveAsyncLoaders() {
|
|
68
|
-
|
|
88
|
+
do {
|
|
69
89
|
TestTree.asyncLoaderResolvers.shift()?.();
|
|
70
90
|
await new Promise<void>((r) => {
|
|
71
91
|
setTimeout(r);
|
|
72
92
|
});
|
|
73
|
-
}
|
|
93
|
+
} while (TestTree.asyncLoaderResolvers.length);
|
|
74
94
|
}
|
|
75
95
|
|
|
76
96
|
async resolveAsyncVisibleItems() {
|
|
@@ -88,22 +108,6 @@ export class TestTree<T = string> {
|
|
|
88
108
|
getItem: (id) => id,
|
|
89
109
|
getChildren: (id) => [`${id}1`, `${id}2`, `${id}3`, `${id}4`],
|
|
90
110
|
},
|
|
91
|
-
asyncDataLoader: {
|
|
92
|
-
getItem: async (id) => {
|
|
93
|
-
await new Promise<void>((r) => {
|
|
94
|
-
(r as any).debugName = `Loading getItem ${id}`;
|
|
95
|
-
TestTree.asyncLoaderResolvers.push(r);
|
|
96
|
-
});
|
|
97
|
-
return id;
|
|
98
|
-
},
|
|
99
|
-
getChildren: async (id) => {
|
|
100
|
-
await new Promise<void>((r) => {
|
|
101
|
-
(r as any).debugName = `Loading getChildren ${id}`;
|
|
102
|
-
TestTree.asyncLoaderResolvers.push(r);
|
|
103
|
-
});
|
|
104
|
-
return [`${id}1`, `${id}2`, `${id}3`, `${id}4`];
|
|
105
|
-
},
|
|
106
|
-
},
|
|
107
111
|
getItemName: (item) => item.getItemData(),
|
|
108
112
|
indent: 20,
|
|
109
113
|
isItemFolder: (item) => item.getItemMeta().level < 2,
|
|
@@ -203,7 +207,7 @@ export class TestTree<T = string> {
|
|
|
203
207
|
} as HTMLElement);
|
|
204
208
|
}
|
|
205
209
|
|
|
206
|
-
static dragEvent(
|
|
210
|
+
static dragEvent(clientX = 1000, clientY = 0) {
|
|
207
211
|
return {
|
|
208
212
|
preventDefault: vi.fn(),
|
|
209
213
|
stopPropagation: vi.fn(),
|
|
@@ -212,8 +216,8 @@ export class TestTree<T = string> {
|
|
|
212
216
|
getData: vi.fn(),
|
|
213
217
|
dropEffect: "unchaged-from-test",
|
|
214
218
|
},
|
|
215
|
-
|
|
216
|
-
|
|
219
|
+
clientX,
|
|
220
|
+
clientY,
|
|
217
221
|
} as unknown as DragEvent;
|
|
218
222
|
}
|
|
219
223
|
|
package/src/types/core.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { SearchFeatureDef } from "../features/search/types";
|
|
|
12
12
|
import { RenamingFeatureDef } from "../features/renaming/types";
|
|
13
13
|
import { ExpandAllFeatureDef } from "../features/expand-all/types";
|
|
14
14
|
import { PropMemoizationFeatureDef } from "../features/prop-memoization/types";
|
|
15
|
+
import { KeyboardDragAndDropFeatureDef } from "../features/keyboard-drag-and-drop/types";
|
|
15
16
|
|
|
16
17
|
export type Updater<T> = T | ((old: T) => T);
|
|
17
18
|
export type SetStateFn<T> = (updaterOrValue: Updater<T>) => void;
|
|
@@ -53,6 +54,7 @@ export type RegisteredFeatures<T> =
|
|
|
53
54
|
| TreeFeatureDef<T>
|
|
54
55
|
| SelectionFeatureDef<T>
|
|
55
56
|
| DragAndDropFeatureDef<T>
|
|
57
|
+
| KeyboardDragAndDropFeatureDef<T>
|
|
56
58
|
| HotkeysCoreFeatureDef<T>
|
|
57
59
|
| SyncDataLoaderFeatureDef<T>
|
|
58
60
|
| AsyncDataLoaderFeatureDef<T>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ItemInstance } from "../types/core";
|
|
2
|
-
import {
|
|
2
|
+
import { DragTarget } from "../features/drag-and-drop/types";
|
|
3
3
|
import { removeItemsFromParents } from "./remove-items-from-parents";
|
|
4
4
|
import { insertItemsAtTarget } from "./insert-items-at-target";
|
|
5
5
|
|
|
@@ -7,8 +7,8 @@ export const createOnDropHandler =
|
|
|
7
7
|
<T>(
|
|
8
8
|
onChangeChildren: (item: ItemInstance<T>, newChildren: string[]) => void,
|
|
9
9
|
) =>
|
|
10
|
-
(items: ItemInstance<T>[], target:
|
|
10
|
+
async (items: ItemInstance<T>[], target: DragTarget<T>) => {
|
|
11
11
|
const itemIds = items.map((item) => item.getId());
|
|
12
|
-
removeItemsFromParents(items, onChangeChildren);
|
|
13
|
-
insertItemsAtTarget(itemIds, target, onChangeChildren);
|
|
12
|
+
await removeItemsFromParents(items, onChangeChildren);
|
|
13
|
+
await insertItemsAtTarget(itemIds, target, onChangeChildren);
|
|
14
14
|
};
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import { ItemInstance } from "../types/core";
|
|
2
|
-
import {
|
|
2
|
+
import { DragTarget } from "../features/drag-and-drop/types";
|
|
3
3
|
|
|
4
|
-
export const insertItemsAtTarget = <T>(
|
|
4
|
+
export const insertItemsAtTarget = async <T>(
|
|
5
5
|
itemIds: string[],
|
|
6
|
-
target:
|
|
7
|
-
onChangeChildren: (
|
|
6
|
+
target: DragTarget<T>,
|
|
7
|
+
onChangeChildren: (
|
|
8
|
+
item: ItemInstance<T>,
|
|
9
|
+
newChildrenIds: string[],
|
|
10
|
+
) => Promise<void> | void,
|
|
8
11
|
) => {
|
|
12
|
+
await target.item.getTree().waitForItemChildrenLoaded(target.item.getId());
|
|
13
|
+
const oldChildrenIds = target.item
|
|
14
|
+
.getTree()
|
|
15
|
+
.retrieveChildrenIds(target.item.getId());
|
|
16
|
+
|
|
9
17
|
// add moved items to new common parent, if dropped onto parent
|
|
10
|
-
if (
|
|
11
|
-
const newChildren = [
|
|
12
|
-
|
|
13
|
-
...itemIds,
|
|
14
|
-
];
|
|
15
|
-
onChangeChildren(target.item, newChildren);
|
|
18
|
+
if (!("childIndex" in target)) {
|
|
19
|
+
const newChildren = [...oldChildrenIds, ...itemIds];
|
|
20
|
+
await onChangeChildren(target.item, newChildren);
|
|
16
21
|
if (target.item && "updateCachedChildrenIds" in target.item) {
|
|
17
22
|
target.item.updateCachedChildrenIds(newChildren);
|
|
18
23
|
}
|
|
@@ -21,14 +26,13 @@ export const insertItemsAtTarget = <T>(
|
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
// add moved items to new common parent, if dropped between siblings
|
|
24
|
-
const oldChildren = target.item.getChildren();
|
|
25
29
|
const newChildren = [
|
|
26
|
-
...
|
|
30
|
+
...oldChildrenIds.slice(0, target.insertionIndex),
|
|
27
31
|
...itemIds,
|
|
28
|
-
...
|
|
32
|
+
...oldChildrenIds.slice(target.insertionIndex),
|
|
29
33
|
];
|
|
30
34
|
|
|
31
|
-
onChangeChildren(target.item, newChildren);
|
|
35
|
+
await onChangeChildren(target.item, newChildren);
|
|
32
36
|
|
|
33
37
|
if (target.item && "updateCachedChildrenIds" in target.item) {
|
|
34
38
|
target.item.updateCachedChildrenIds(newChildren);
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { ItemInstance } from "../types/core";
|
|
2
2
|
|
|
3
|
-
export const removeItemsFromParents = <T>(
|
|
3
|
+
export const removeItemsFromParents = async <T>(
|
|
4
4
|
movedItems: ItemInstance<T>[],
|
|
5
|
-
onChangeChildren: (
|
|
5
|
+
onChangeChildren: (
|
|
6
|
+
item: ItemInstance<T>,
|
|
7
|
+
newChildrenIds: string[],
|
|
8
|
+
) => void | Promise<void>,
|
|
6
9
|
) => {
|
|
7
10
|
const movedItemsIds = movedItems.map((item) => item.getId());
|
|
8
11
|
const uniqueParents = [
|
|
@@ -15,7 +18,7 @@ export const removeItemsFromParents = <T>(
|
|
|
15
18
|
const newChildren = siblings
|
|
16
19
|
.filter((sibling) => !movedItemsIds.includes(sibling.getId()))
|
|
17
20
|
.map((i) => i.getId());
|
|
18
|
-
onChangeChildren(parent, newChildren);
|
|
21
|
+
await onChangeChildren(parent, newChildren);
|
|
19
22
|
if (parent && "updateCachedChildrenIds" in parent) {
|
|
20
23
|
parent?.updateCachedChildrenIds(newChildren);
|
|
21
24
|
}
|