@headless-tree/core 0.0.5 → 0.0.6
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 +6 -0
- package/lib/cjs/core/create-tree.js +7 -6
- package/lib/cjs/features/async-data-loader/feature.js +30 -21
- package/lib/cjs/features/drag-and-drop/feature.js +29 -17
- package/lib/cjs/features/drag-and-drop/types.d.ts +14 -1
- package/lib/cjs/features/drag-and-drop/utils.d.ts +1 -1
- package/lib/cjs/features/drag-and-drop/utils.js +34 -17
- package/lib/cjs/features/expand-all/feature.js +12 -9
- package/lib/cjs/features/expand-all/types.d.ts +2 -2
- package/lib/cjs/features/main/types.d.ts +3 -1
- package/lib/cjs/features/renaming/feature.js +10 -9
- package/lib/cjs/features/search/feature.js +21 -7
- package/lib/cjs/features/search/types.d.ts +1 -1
- package/lib/cjs/features/selection/feature.js +5 -3
- package/lib/cjs/features/sync-data-loader/feature.js +6 -0
- package/lib/cjs/features/sync-data-loader/types.d.ts +1 -1
- package/lib/cjs/features/tree/feature.js +23 -23
- package/lib/cjs/features/tree/types.d.ts +2 -2
- package/lib/cjs/index.d.ts +3 -1
- package/lib/cjs/index.js +3 -1
- package/lib/cjs/types/core.d.ts +1 -3
- package/lib/cjs/utilities/create-on-drop-handler.d.ts +3 -0
- package/lib/cjs/utilities/create-on-drop-handler.js +11 -0
- package/lib/cjs/utilities/insert-items-at-target.d.ts +3 -0
- package/lib/cjs/utilities/insert-items-at-target.js +24 -0
- package/lib/cjs/utilities/remove-items-from-parents.d.ts +2 -0
- package/lib/cjs/utilities/remove-items-from-parents.js +17 -0
- package/lib/cjs/utils.d.ts +1 -4
- package/lib/cjs/utils.js +1 -53
- package/lib/esm/core/create-tree.js +7 -6
- package/lib/esm/features/async-data-loader/feature.js +30 -21
- package/lib/esm/features/drag-and-drop/feature.js +29 -17
- package/lib/esm/features/drag-and-drop/types.d.ts +14 -1
- package/lib/esm/features/drag-and-drop/utils.d.ts +1 -1
- package/lib/esm/features/drag-and-drop/utils.js +35 -18
- package/lib/esm/features/expand-all/feature.js +12 -9
- package/lib/esm/features/expand-all/types.d.ts +2 -2
- package/lib/esm/features/main/types.d.ts +3 -1
- package/lib/esm/features/renaming/feature.js +10 -9
- package/lib/esm/features/search/feature.js +21 -7
- package/lib/esm/features/search/types.d.ts +1 -1
- package/lib/esm/features/selection/feature.js +5 -3
- package/lib/esm/features/sync-data-loader/feature.js +6 -0
- package/lib/esm/features/sync-data-loader/types.d.ts +1 -1
- package/lib/esm/features/tree/feature.js +23 -23
- package/lib/esm/features/tree/types.d.ts +2 -2
- package/lib/esm/index.d.ts +3 -1
- package/lib/esm/index.js +3 -1
- package/lib/esm/types/core.d.ts +1 -3
- package/lib/esm/utilities/create-on-drop-handler.d.ts +3 -0
- package/lib/esm/utilities/create-on-drop-handler.js +7 -0
- package/lib/esm/utilities/insert-items-at-target.d.ts +3 -0
- package/lib/esm/utilities/insert-items-at-target.js +20 -0
- package/lib/esm/utilities/remove-items-from-parents.d.ts +2 -0
- package/lib/esm/utilities/remove-items-from-parents.js +13 -0
- package/lib/esm/utils.d.ts +1 -4
- package/lib/esm/utils.js +0 -50
- package/package.json +1 -1
- package/src/core/create-tree.ts +11 -7
- package/src/features/async-data-loader/feature.ts +15 -5
- package/src/features/drag-and-drop/feature.ts +34 -12
- package/src/features/drag-and-drop/types.ts +23 -6
- package/src/features/drag-and-drop/utils.ts +53 -24
- package/src/features/expand-all/feature.ts +10 -8
- package/src/features/expand-all/types.ts +2 -2
- package/src/features/main/types.ts +6 -0
- package/src/features/renaming/feature.ts +10 -5
- package/src/features/search/feature.ts +22 -5
- package/src/features/search/types.ts +1 -0
- package/src/features/selection/feature.ts +6 -1
- package/src/features/sync-data-loader/feature.ts +17 -2
- package/src/features/sync-data-loader/types.ts +1 -1
- package/src/features/tree/feature.ts +23 -21
- package/src/features/tree/types.ts +4 -2
- package/src/index.ts +4 -1
- package/src/types/core.ts +4 -4
- package/src/utilities/create-on-drop-handler.ts +14 -0
- package/src/utilities/insert-items-at-target.ts +30 -0
- package/src/utilities/remove-items-from-parents.ts +21 -0
- package/src/utils.ts +1 -69
- package/lib/cjs/data-adapters/nested-data-adapter.d.ts +0 -9
- package/lib/cjs/data-adapters/nested-data-adapter.js +0 -32
- package/lib/cjs/data-adapters/types.d.ts +0 -7
- package/lib/cjs/data-adapters/types.js +0 -2
- package/lib/esm/data-adapters/nested-data-adapter.d.ts +0 -9
- package/lib/esm/data-adapters/nested-data-adapter.js +0 -28
- package/lib/esm/data-adapters/types.d.ts +0 -7
- package/lib/esm/data-adapters/types.js +0 -1
- package/src/data-adapters/nested-data-adapter.ts +0 -48
- package/src/data-adapters/types.ts +0 -9
package/src/core/create-tree.ts
CHANGED
|
@@ -44,6 +44,10 @@ export const createTree = <T>(
|
|
|
44
44
|
(acc, feature) => feature.getDefaultConfig?.(acc, treeInstance) ?? acc,
|
|
45
45
|
initialConfig
|
|
46
46
|
) as TreeConfig<T>;
|
|
47
|
+
const stateHandlerNames = additionalFeatures.reduce(
|
|
48
|
+
(acc, feature) => ({ ...acc, ...feature.stateHandlerNames }),
|
|
49
|
+
{} as Record<string, string>
|
|
50
|
+
);
|
|
47
51
|
|
|
48
52
|
let treeElement: HTMLElement | undefined | null;
|
|
49
53
|
const treeDataRef: { current: any } = { current: {} };
|
|
@@ -109,10 +113,14 @@ export const createTree = <T>(
|
|
|
109
113
|
...prev,
|
|
110
114
|
getState: () => state,
|
|
111
115
|
setState: (updater) => {
|
|
112
|
-
|
|
116
|
+
// Not necessary, since I think the subupdate below keeps the state fresh anyways?
|
|
117
|
+
// state = typeof updater === "function" ? updater(state) : updater;
|
|
113
118
|
config.setState?.(state);
|
|
114
|
-
|
|
115
|
-
|
|
119
|
+
},
|
|
120
|
+
applySubStateUpdate: (stateName, updater) => {
|
|
121
|
+
state[stateName] =
|
|
122
|
+
typeof updater === "function" ? updater(state[stateName]) : updater;
|
|
123
|
+
config[stateHandlerNames[stateName]]!(state[stateName]);
|
|
116
124
|
},
|
|
117
125
|
rebuildTree: () => {
|
|
118
126
|
rebuildItemMeta(mainFeature);
|
|
@@ -124,11 +132,7 @@ export const createTree = <T>(
|
|
|
124
132
|
|
|
125
133
|
if (config.state) {
|
|
126
134
|
state = { ...state, ...config.state };
|
|
127
|
-
eachFeature((feature) => feature.setState?.(treeInstance));
|
|
128
135
|
}
|
|
129
|
-
|
|
130
|
-
eachFeature((feature) => feature.onConfigChange?.(treeInstance));
|
|
131
|
-
eachFeature((feature) => feature.onStateOrConfigChange?.(treeInstance));
|
|
132
136
|
},
|
|
133
137
|
getItemInstance: (itemId) => itemInstancesMap[itemId],
|
|
134
138
|
getItems: () => itemInstances,
|
|
@@ -22,6 +22,10 @@ export const asyncDataLoaderFeature: FeatureImplementation<
|
|
|
22
22
|
...defaultConfig,
|
|
23
23
|
}),
|
|
24
24
|
|
|
25
|
+
stateHandlerNames: {
|
|
26
|
+
loadingItems: "setLoadingItems",
|
|
27
|
+
},
|
|
28
|
+
|
|
25
29
|
createTreeInstance: (prev, instance) => ({
|
|
26
30
|
...prev,
|
|
27
31
|
|
|
@@ -36,11 +40,14 @@ export const asyncDataLoaderFeature: FeatureImplementation<
|
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
if (!instance.getState().loadingItems.includes(itemId)) {
|
|
39
|
-
|
|
43
|
+
instance.applySubStateUpdate("loadingItems", (loadingItems) => [
|
|
44
|
+
...loadingItems,
|
|
45
|
+
itemId,
|
|
46
|
+
]);
|
|
40
47
|
config.asyncDataLoader?.getItem(itemId).then((item) => {
|
|
41
48
|
dataRef.current.itemData[itemId] = item;
|
|
42
49
|
config.onLoadedItem?.(itemId, item);
|
|
43
|
-
|
|
50
|
+
instance.applySubStateUpdate("loadingItems", (loadingItems) =>
|
|
44
51
|
loadingItems.filter((id) => id !== itemId)
|
|
45
52
|
);
|
|
46
53
|
});
|
|
@@ -62,7 +69,10 @@ export const asyncDataLoaderFeature: FeatureImplementation<
|
|
|
62
69
|
return [];
|
|
63
70
|
}
|
|
64
71
|
|
|
65
|
-
|
|
72
|
+
instance.applySubStateUpdate("loadingItems", (loadingItems) => [
|
|
73
|
+
...loadingItems,
|
|
74
|
+
itemId,
|
|
75
|
+
]);
|
|
66
76
|
|
|
67
77
|
if (config.asyncDataLoader?.getChildrenWithData) {
|
|
68
78
|
config.asyncDataLoader?.getChildrenWithData(itemId).then((children) => {
|
|
@@ -73,7 +83,7 @@ export const asyncDataLoaderFeature: FeatureImplementation<
|
|
|
73
83
|
const childrenIds = children.map(({ id }) => id);
|
|
74
84
|
dataRef.current.childrenIds[itemId] = childrenIds;
|
|
75
85
|
config.onLoadedChildren?.(itemId, childrenIds);
|
|
76
|
-
|
|
86
|
+
instance.applySubStateUpdate("loadingItems", (loadingItems) =>
|
|
77
87
|
loadingItems.filter((id) => id !== itemId)
|
|
78
88
|
);
|
|
79
89
|
instance.rebuildTree();
|
|
@@ -82,7 +92,7 @@ export const asyncDataLoaderFeature: FeatureImplementation<
|
|
|
82
92
|
config.asyncDataLoader?.getChildren(itemId).then((childrenIds) => {
|
|
83
93
|
dataRef.current.childrenIds[itemId] = childrenIds;
|
|
84
94
|
config.onLoadedChildren?.(itemId, childrenIds);
|
|
85
|
-
|
|
95
|
+
instance.applySubStateUpdate("loadingItems", (loadingItems) =>
|
|
86
96
|
loadingItems.filter((id) => id !== itemId)
|
|
87
97
|
);
|
|
88
98
|
instance.rebuildTree();
|
|
@@ -13,10 +13,15 @@ export const dragAndDropFeature: FeatureImplementation<
|
|
|
13
13
|
|
|
14
14
|
getDefaultConfig: (defaultConfig, tree) => ({
|
|
15
15
|
canDrop: (_, target) => target.item.isFolder(),
|
|
16
|
+
canDropForeignDragObject: () => false,
|
|
16
17
|
setDndState: makeStateUpdater("dnd", tree),
|
|
17
18
|
...defaultConfig,
|
|
18
19
|
}),
|
|
19
20
|
|
|
21
|
+
stateHandlerNames: {
|
|
22
|
+
dnd: "setDndState",
|
|
23
|
+
},
|
|
24
|
+
|
|
20
25
|
createTreeInstance: (prev, tree) => ({
|
|
21
26
|
...prev,
|
|
22
27
|
|
|
@@ -52,7 +57,7 @@ export const dragAndDropFeature: FeatureImplementation<
|
|
|
52
57
|
e.dataTransfer?.setData(format, data);
|
|
53
58
|
}
|
|
54
59
|
|
|
55
|
-
tree.
|
|
60
|
+
tree.applySubStateUpdate("dnd", {
|
|
56
61
|
draggedItems: items,
|
|
57
62
|
draggingOverItem: tree.getFocusedItem(),
|
|
58
63
|
});
|
|
@@ -82,7 +87,7 @@ export const dragAndDropFeature: FeatureImplementation<
|
|
|
82
87
|
|
|
83
88
|
dataRef.current.lastDragCode = nextDragCode;
|
|
84
89
|
|
|
85
|
-
tree.
|
|
90
|
+
tree.applySubStateUpdate("dnd", (state) => ({
|
|
86
91
|
...state,
|
|
87
92
|
dragTarget: target,
|
|
88
93
|
draggingOverItem: item,
|
|
@@ -92,13 +97,24 @@ export const dragAndDropFeature: FeatureImplementation<
|
|
|
92
97
|
onDragLeave: item.getMemoizedProp("dnd/onDragLeave", () => () => {
|
|
93
98
|
const dataRef = tree.getDataRef<DndDataRef>();
|
|
94
99
|
dataRef.current.lastDragCode = "no-drag";
|
|
95
|
-
tree.
|
|
100
|
+
tree.applySubStateUpdate("dnd", (state) => ({
|
|
96
101
|
...state,
|
|
97
102
|
draggingOverItem: undefined,
|
|
98
103
|
dragTarget: undefined,
|
|
99
104
|
}));
|
|
100
105
|
}),
|
|
101
106
|
|
|
107
|
+
onDragEnd: item.getMemoizedProp("dnd/onDragEnd", () => (e) => {
|
|
108
|
+
const draggedItems = tree.getState().dnd?.draggedItems;
|
|
109
|
+
tree.applySubStateUpdate("dnd", null);
|
|
110
|
+
|
|
111
|
+
if (e.dataTransfer.dropEffect === "none" || !draggedItems) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
tree.getConfig().onCompleteForeignDrop?.(draggedItems);
|
|
116
|
+
}),
|
|
117
|
+
|
|
102
118
|
onDrop: item.getMemoizedProp("dnd/onDrop", () => (e) => {
|
|
103
119
|
const dataRef = tree.getDataRef<DndDataRef>();
|
|
104
120
|
const target = getDropTarget(e, item, tree);
|
|
@@ -112,7 +128,7 @@ export const dragAndDropFeature: FeatureImplementation<
|
|
|
112
128
|
const draggedItems = tree.getState().dnd?.draggedItems;
|
|
113
129
|
|
|
114
130
|
dataRef.current.lastDragCode = undefined;
|
|
115
|
-
tree.
|
|
131
|
+
tree.applySubStateUpdate("dnd", null);
|
|
116
132
|
|
|
117
133
|
if (draggedItems) {
|
|
118
134
|
config.onDrop?.(draggedItems, target);
|
|
@@ -131,19 +147,25 @@ export const dragAndDropFeature: FeatureImplementation<
|
|
|
131
147
|
isDropTargetAbove: () => {
|
|
132
148
|
const target = tree.getDropTarget();
|
|
133
149
|
|
|
134
|
-
if (
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
150
|
+
if (
|
|
151
|
+
!target ||
|
|
152
|
+
target.childIndex === null ||
|
|
153
|
+
target.item !== item.getParent()
|
|
154
|
+
)
|
|
155
|
+
return false;
|
|
156
|
+
return target.childIndex === item.getItemMeta().posInSet;
|
|
138
157
|
},
|
|
139
158
|
|
|
140
159
|
isDropTargetBelow: () => {
|
|
141
160
|
const target = tree.getDropTarget();
|
|
142
161
|
|
|
143
|
-
if (
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
162
|
+
if (
|
|
163
|
+
!target ||
|
|
164
|
+
target.childIndex === null ||
|
|
165
|
+
target.item !== item.getParent()
|
|
166
|
+
)
|
|
167
|
+
return false;
|
|
168
|
+
return target.childIndex - 1 === item.getItemMeta().posInSet;
|
|
147
169
|
},
|
|
148
170
|
|
|
149
171
|
isDraggingOver: () => {
|
|
@@ -5,15 +5,22 @@ export type DndDataRef = {
|
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
export type DndState<T> = {
|
|
8
|
-
draggedItems?: ItemInstance<T>[];
|
|
8
|
+
draggedItems?: ItemInstance<T>[]; // TODO not used anymore?
|
|
9
9
|
draggingOverItem?: ItemInstance<T>;
|
|
10
10
|
dragTarget?: DropTarget<T>;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
-
export type DropTarget<T> =
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
export type DropTarget<T> =
|
|
14
|
+
| {
|
|
15
|
+
item: ItemInstance<T>;
|
|
16
|
+
childIndex: number;
|
|
17
|
+
insertionIndex: number;
|
|
18
|
+
}
|
|
19
|
+
| {
|
|
20
|
+
item: ItemInstance<T>;
|
|
21
|
+
childIndex: null;
|
|
22
|
+
insertionIndex: null;
|
|
23
|
+
};
|
|
17
24
|
|
|
18
25
|
export enum DropTargetPosition {
|
|
19
26
|
Top = "top",
|
|
@@ -44,12 +51,22 @@ export type DragAndDropFeatureDef<T> = {
|
|
|
44
51
|
dataTransfer: DataTransfer,
|
|
45
52
|
target: DropTarget<T>
|
|
46
53
|
) => boolean;
|
|
47
|
-
|
|
48
54
|
onDrop?: (items: ItemInstance<T>[], target: DropTarget<T>) => void;
|
|
49
55
|
onDropForeignDragObject?: (
|
|
50
56
|
dataTransfer: DataTransfer,
|
|
51
57
|
target: DropTarget<T>
|
|
52
58
|
) => void;
|
|
59
|
+
|
|
60
|
+
/** Runs in the onDragEnd event, if `ev.dataTransfer.dropEffect` is not `none`, i.e. the drop
|
|
61
|
+
* was not aborted. No target is provided as parameter since the target may be a foreign drop target.
|
|
62
|
+
* This is useful to seperate out the logic to move dragged items out of their previous parents.
|
|
63
|
+
* Use `onDrop` to handle drop-related logic.
|
|
64
|
+
*
|
|
65
|
+
* This ignores the `canDrop` handler, since the drop target is unknown in this handler.
|
|
66
|
+
*/
|
|
67
|
+
// onSuccessfulDragEnd?: (items: ItemInstance<T>[]) => void;
|
|
68
|
+
|
|
69
|
+
onCompleteForeignDrop?: (items: ItemInstance<T>[]) => void;
|
|
53
70
|
};
|
|
54
71
|
treeInstance: {
|
|
55
72
|
getDropTarget: () => DropTarget<T> | null;
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { ItemInstance, TreeInstance } from "../../types/core";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
DndState,
|
|
4
|
+
DragAndDropFeatureDef,
|
|
5
|
+
DropTarget,
|
|
6
|
+
DropTargetPosition,
|
|
7
|
+
} from "./types";
|
|
3
8
|
|
|
4
9
|
export const getDragCode = ({ item, childIndex }: DropTarget<any>) =>
|
|
5
10
|
`${item.getId()}__${childIndex ?? "none"}`;
|
|
@@ -49,40 +54,64 @@ const getDropTargetPosition = (
|
|
|
49
54
|
export const getDropTarget = (
|
|
50
55
|
e: any,
|
|
51
56
|
item: ItemInstance<any>,
|
|
52
|
-
tree: TreeInstance<any
|
|
57
|
+
tree: TreeInstance<any>,
|
|
58
|
+
canDropInbetween = tree.getConfig().canDropInbetween
|
|
53
59
|
): DropTarget<any> => {
|
|
54
60
|
const config = tree.getConfig();
|
|
55
|
-
const
|
|
61
|
+
const draggedItems = tree.getState().dnd?.draggedItems ?? [];
|
|
62
|
+
const itemTarget = { item, childIndex: null, insertionIndex: null };
|
|
63
|
+
const parentTarget = {
|
|
64
|
+
item: item.getParent(),
|
|
65
|
+
childIndex: null,
|
|
66
|
+
insertionIndex: null,
|
|
67
|
+
};
|
|
56
68
|
|
|
57
|
-
|
|
69
|
+
if (!canDropInbetween) {
|
|
70
|
+
if (!canDrop(e.dataTransfer, parentTarget, tree)) {
|
|
71
|
+
return getDropTarget(e, item.getParent(), tree, false);
|
|
72
|
+
}
|
|
73
|
+
return itemTarget;
|
|
74
|
+
}
|
|
58
75
|
|
|
59
|
-
const
|
|
60
|
-
offset,
|
|
61
|
-
config.topLinePercentage ?? 0.3,
|
|
62
|
-
config.bottomLinePercentage ?? 0.7
|
|
63
|
-
);
|
|
64
|
-
const inbetweenPos = getDropTargetPosition(offset, 0.5, 0.5);
|
|
76
|
+
const canDropInside = canDrop(e.dataTransfer, itemTarget, tree);
|
|
65
77
|
|
|
66
|
-
|
|
67
|
-
return dropOnItemTarget;
|
|
68
|
-
}
|
|
78
|
+
const offset = getDropOffset(e, item);
|
|
69
79
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
80
|
+
const pos = canDropInside
|
|
81
|
+
? getDropTargetPosition(
|
|
82
|
+
offset,
|
|
83
|
+
config.topLinePercentage ?? 0.3,
|
|
84
|
+
config.bottomLinePercentage ?? 0.7
|
|
85
|
+
)
|
|
86
|
+
: getDropTargetPosition(offset, 0.5, 0.5);
|
|
78
87
|
|
|
79
88
|
if (pos === DropTargetPosition.Item) {
|
|
80
|
-
return
|
|
89
|
+
return itemTarget;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!canDrop(e.dataTransfer, parentTarget, tree)) {
|
|
93
|
+
return getDropTarget(e, item.getParent(), tree, false);
|
|
81
94
|
}
|
|
82
95
|
|
|
96
|
+
const childIndex =
|
|
97
|
+
item.getIndexInParent() + (pos === DropTargetPosition.Top ? 0 : 1);
|
|
98
|
+
|
|
99
|
+
const numberOfDragItemsBeforeTarget = item
|
|
100
|
+
.getParent()
|
|
101
|
+
.getChildren()
|
|
102
|
+
.slice(0, childIndex)
|
|
103
|
+
.reduce(
|
|
104
|
+
(counter, child) =>
|
|
105
|
+
child && draggedItems?.some((i) => i.getId() === child.getId())
|
|
106
|
+
? ++counter
|
|
107
|
+
: counter,
|
|
108
|
+
0
|
|
109
|
+
);
|
|
110
|
+
|
|
83
111
|
return {
|
|
84
112
|
item: item.getParent(),
|
|
85
|
-
childIndex
|
|
86
|
-
|
|
113
|
+
childIndex,
|
|
114
|
+
// TODO performance could be improved by computing this only when dragcode changed
|
|
115
|
+
insertionIndex: childIndex - numberOfDragItemsBeforeTarget,
|
|
87
116
|
};
|
|
88
117
|
};
|
|
@@ -25,8 +25,9 @@ export const expandAllFeature: FeatureImplementation<
|
|
|
25
25
|
);
|
|
26
26
|
},
|
|
27
27
|
|
|
28
|
-
collapseAll:
|
|
29
|
-
tree.
|
|
28
|
+
collapseAll: () => {
|
|
29
|
+
tree.applySubStateUpdate("expandedItems", []);
|
|
30
|
+
tree.rebuildTree();
|
|
30
31
|
},
|
|
31
32
|
}),
|
|
32
33
|
|
|
@@ -37,6 +38,9 @@ export const expandAllFeature: FeatureImplementation<
|
|
|
37
38
|
if (cancelToken?.current) {
|
|
38
39
|
return;
|
|
39
40
|
}
|
|
41
|
+
if (!item.isFolder()) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
40
44
|
|
|
41
45
|
item.expand();
|
|
42
46
|
await poll(() => !tree.getState().loadingItems.includes(item.getId()));
|
|
@@ -50,12 +54,10 @@ export const expandAllFeature: FeatureImplementation<
|
|
|
50
54
|
);
|
|
51
55
|
},
|
|
52
56
|
|
|
53
|
-
collapseAll:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
})
|
|
58
|
-
);
|
|
57
|
+
collapseAll: () => {
|
|
58
|
+
for (const child of item.getChildren()) {
|
|
59
|
+
child?.collapseAll();
|
|
60
|
+
}
|
|
59
61
|
item.collapse();
|
|
60
62
|
},
|
|
61
63
|
}),
|
|
@@ -3,11 +3,11 @@ export type ExpandAllFeatureDef = {
|
|
|
3
3
|
config: {};
|
|
4
4
|
treeInstance: {
|
|
5
5
|
expandAll: (cancelToken?: { current: boolean }) => Promise<void>;
|
|
6
|
-
collapseAll: () =>
|
|
6
|
+
collapseAll: () => void;
|
|
7
7
|
};
|
|
8
8
|
itemInstance: {
|
|
9
9
|
expandAll: (cancelToken?: { current: boolean }) => Promise<void>;
|
|
10
|
-
collapseAll: () =>
|
|
10
|
+
collapseAll: () => void;
|
|
11
11
|
};
|
|
12
12
|
hotkeys: never;
|
|
13
13
|
};
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
SetStateFn,
|
|
6
6
|
TreeConfig,
|
|
7
7
|
TreeState,
|
|
8
|
+
Updater,
|
|
8
9
|
} from "../../types/core";
|
|
9
10
|
import { ItemMeta } from "../tree/types";
|
|
10
11
|
|
|
@@ -17,6 +18,11 @@ export type MainFeatureDef<T = any> = {
|
|
|
17
18
|
setState?: SetStateFn<TreeState<T>>;
|
|
18
19
|
};
|
|
19
20
|
treeInstance: {
|
|
21
|
+
/** @internal */
|
|
22
|
+
applySubStateUpdate: <K extends keyof TreeState<any>>(
|
|
23
|
+
stateName: K,
|
|
24
|
+
updater: Updater<TreeState<T>[K]>
|
|
25
|
+
) => void;
|
|
20
26
|
setState: SetStateFn<TreeState<T>>;
|
|
21
27
|
getState: () => TreeState<T>;
|
|
22
28
|
setConfig: SetStateFn<TreeConfig<T>>;
|
|
@@ -19,6 +19,11 @@ export const renamingFeature: FeatureImplementation<
|
|
|
19
19
|
...defaultConfig,
|
|
20
20
|
}),
|
|
21
21
|
|
|
22
|
+
stateHandlerNames: {
|
|
23
|
+
renamingItem: "setRenamingItem",
|
|
24
|
+
renamingValue: "setRenamingValue",
|
|
25
|
+
},
|
|
26
|
+
|
|
22
27
|
createTreeInstance: (prev, instance) => ({
|
|
23
28
|
...prev,
|
|
24
29
|
|
|
@@ -30,8 +35,8 @@ export const renamingFeature: FeatureImplementation<
|
|
|
30
35
|
return;
|
|
31
36
|
}
|
|
32
37
|
|
|
33
|
-
|
|
34
|
-
|
|
38
|
+
instance.applySubStateUpdate("renamingItem", itemId);
|
|
39
|
+
instance.applySubStateUpdate("renamingValue", item.getItemName());
|
|
35
40
|
},
|
|
36
41
|
|
|
37
42
|
getRenamingItem: () => {
|
|
@@ -42,7 +47,7 @@ export const renamingFeature: FeatureImplementation<
|
|
|
42
47
|
getRenamingValue: () => instance.getState().renamingValue || "",
|
|
43
48
|
|
|
44
49
|
abortRenaming: () => {
|
|
45
|
-
instance.
|
|
50
|
+
instance.applySubStateUpdate("renamingItem", null);
|
|
46
51
|
},
|
|
47
52
|
|
|
48
53
|
completeRenaming: () => {
|
|
@@ -51,7 +56,7 @@ export const renamingFeature: FeatureImplementation<
|
|
|
51
56
|
if (item) {
|
|
52
57
|
config.onRename?.(item, instance.getState().renamingValue || "");
|
|
53
58
|
}
|
|
54
|
-
instance.
|
|
59
|
+
instance.applySubStateUpdate("renamingItem", null);
|
|
55
60
|
},
|
|
56
61
|
|
|
57
62
|
isRenamingItem: () => !!instance.getState().renamingItem,
|
|
@@ -63,7 +68,7 @@ export const renamingFeature: FeatureImplementation<
|
|
|
63
68
|
onBlur: () => tree.abortRenaming(),
|
|
64
69
|
value: tree.getRenamingValue(),
|
|
65
70
|
onChange: (e) => {
|
|
66
|
-
tree.
|
|
71
|
+
tree.applySubStateUpdate("renamingValue", e.target.value);
|
|
67
72
|
},
|
|
68
73
|
}),
|
|
69
74
|
|
|
@@ -25,11 +25,15 @@ export const searchFeature: FeatureImplementation<
|
|
|
25
25
|
...defaultConfig,
|
|
26
26
|
}),
|
|
27
27
|
|
|
28
|
+
stateHandlerNames: {
|
|
29
|
+
search: "setSearch",
|
|
30
|
+
},
|
|
31
|
+
|
|
28
32
|
createTreeInstance: (prev, instance) => ({
|
|
29
33
|
...prev,
|
|
30
34
|
|
|
31
35
|
setSearch: (search) => {
|
|
32
|
-
instance.
|
|
36
|
+
instance.applySubStateUpdate("search", search);
|
|
33
37
|
instance
|
|
34
38
|
.getItems()
|
|
35
39
|
.find((item) =>
|
|
@@ -71,8 +75,9 @@ export const searchFeature: FeatureImplementation<
|
|
|
71
75
|
|
|
72
76
|
getSearchMatchingItems: memo(
|
|
73
77
|
(search, items) =>
|
|
74
|
-
items.filter(
|
|
75
|
-
|
|
78
|
+
items.filter(
|
|
79
|
+
(item) =>
|
|
80
|
+
search && instance.getConfig().isSearchMatchingItem?.(search, item)
|
|
76
81
|
),
|
|
77
82
|
() => [instance.getSearchValue(), instance.getItems()]
|
|
78
83
|
),
|
|
@@ -105,12 +110,22 @@ export const searchFeature: FeatureImplementation<
|
|
|
105
110
|
},
|
|
106
111
|
},
|
|
107
112
|
|
|
113
|
+
submitSearch: {
|
|
114
|
+
hotkey: "Enter",
|
|
115
|
+
allowWhenInputFocused: true,
|
|
116
|
+
isEnabled: (tree) => tree.isSearchOpen(),
|
|
117
|
+
handler: (e, tree) => {
|
|
118
|
+
tree.closeSearch();
|
|
119
|
+
tree.setSelectedItems([tree.getFocusedItem().getId()]);
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
|
|
108
123
|
nextSearchItem: {
|
|
109
124
|
hotkey: "ArrowDown",
|
|
110
125
|
allowWhenInputFocused: true,
|
|
126
|
+
canRepeat: true,
|
|
111
127
|
isEnabled: (tree) => tree.isSearchOpen(),
|
|
112
128
|
handler: (e, tree) => {
|
|
113
|
-
// TODO scroll into view
|
|
114
129
|
const focusItem = tree
|
|
115
130
|
.getSearchMatchingItems()
|
|
116
131
|
.find(
|
|
@@ -119,15 +134,16 @@ export const searchFeature: FeatureImplementation<
|
|
|
119
134
|
tree.getFocusedItem().getItemMeta().index
|
|
120
135
|
);
|
|
121
136
|
focusItem?.setFocused();
|
|
137
|
+
focusItem?.scrollTo({ block: "nearest", inline: "nearest" });
|
|
122
138
|
},
|
|
123
139
|
},
|
|
124
140
|
|
|
125
141
|
previousSearchItem: {
|
|
126
142
|
hotkey: "ArrowUp",
|
|
127
143
|
allowWhenInputFocused: true,
|
|
144
|
+
canRepeat: true,
|
|
128
145
|
isEnabled: (tree) => tree.isSearchOpen(),
|
|
129
146
|
handler: (e, tree) => {
|
|
130
|
-
// TODO scroll into view
|
|
131
147
|
const focusItem = [...tree.getSearchMatchingItems()]
|
|
132
148
|
.reverse()
|
|
133
149
|
.find(
|
|
@@ -136,6 +152,7 @@ export const searchFeature: FeatureImplementation<
|
|
|
136
152
|
tree.getFocusedItem().getItemMeta().index
|
|
137
153
|
);
|
|
138
154
|
focusItem?.setFocused();
|
|
155
|
+
focusItem?.scrollTo({ block: "nearest", inline: "nearest" });
|
|
139
156
|
},
|
|
140
157
|
},
|
|
141
158
|
},
|
|
@@ -22,11 +22,15 @@ export const selectionFeature: FeatureImplementation<
|
|
|
22
22
|
...defaultConfig,
|
|
23
23
|
}),
|
|
24
24
|
|
|
25
|
+
stateHandlerNames: {
|
|
26
|
+
selectedItems: "setSelectedItems",
|
|
27
|
+
},
|
|
28
|
+
|
|
25
29
|
createTreeInstance: (prev, instance) => ({
|
|
26
30
|
...prev,
|
|
27
31
|
|
|
28
32
|
setSelectedItems: (selectedItems) => {
|
|
29
|
-
instance.
|
|
33
|
+
instance.applySubStateUpdate("selectedItems", selectedItems);
|
|
30
34
|
},
|
|
31
35
|
|
|
32
36
|
// TODO memo
|
|
@@ -91,6 +95,7 @@ export const selectionFeature: FeatureImplementation<
|
|
|
91
95
|
|
|
92
96
|
getProps: () => ({
|
|
93
97
|
...prev.getProps(),
|
|
98
|
+
"aria-selected": item.isSelected() ? "true" : "false",
|
|
94
99
|
onClick: item.getMemoizedProp("selection/onClick", () => (e) => {
|
|
95
100
|
if (e.shiftKey) {
|
|
96
101
|
item.selectUpTo(e.ctrlKey || e.metaKey);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { FeatureImplementation } from "../../types/core";
|
|
2
2
|
import { SyncDataLoaderFeatureDef } from "./types";
|
|
3
3
|
import { MainFeatureDef } from "../main/types";
|
|
4
|
+
import { makeStateUpdater } from "../../utils";
|
|
4
5
|
|
|
5
6
|
export const syncDataLoaderFeature: FeatureImplementation<
|
|
6
7
|
any,
|
|
@@ -10,14 +11,28 @@ export const syncDataLoaderFeature: FeatureImplementation<
|
|
|
10
11
|
key: "sync-data-loader",
|
|
11
12
|
dependingFeatures: ["main"],
|
|
12
13
|
|
|
14
|
+
getInitialState: (initialState) => ({
|
|
15
|
+
loadingItems: [],
|
|
16
|
+
...initialState,
|
|
17
|
+
}),
|
|
18
|
+
|
|
19
|
+
getDefaultConfig: (defaultConfig, tree) => ({
|
|
20
|
+
setLoadingItems: makeStateUpdater("loadingItems", tree),
|
|
21
|
+
...defaultConfig,
|
|
22
|
+
}),
|
|
23
|
+
|
|
24
|
+
stateHandlerNames: {
|
|
25
|
+
loadingItems: "setLoadingItems",
|
|
26
|
+
},
|
|
27
|
+
|
|
13
28
|
createTreeInstance: (prev, instance) => ({
|
|
14
29
|
...prev,
|
|
15
30
|
|
|
16
31
|
retrieveItemData: (itemId) =>
|
|
17
|
-
instance.getConfig().dataLoader
|
|
32
|
+
instance.getConfig().dataLoader!.getItem(itemId),
|
|
18
33
|
|
|
19
34
|
retrieveChildrenIds: (itemId) =>
|
|
20
|
-
instance.getConfig().dataLoader
|
|
35
|
+
instance.getConfig().dataLoader!.getChildren(itemId),
|
|
21
36
|
}),
|
|
22
37
|
|
|
23
38
|
createItemInstance: (prev) => ({
|