@headless-tree/core 0.0.5 → 0.0.7
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 +12 -0
- package/lib/cjs/core/create-tree.js +34 -12
- package/lib/cjs/features/async-data-loader/feature.js +30 -22
- package/lib/cjs/features/drag-and-drop/feature.js +58 -18
- package/lib/cjs/features/drag-and-drop/types.d.ts +21 -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 -10
- package/lib/cjs/features/expand-all/types.d.ts +2 -2
- package/lib/cjs/features/hotkeys-core/feature.js +0 -1
- package/lib/cjs/features/main/types.d.ts +3 -1
- package/lib/cjs/features/renaming/feature.js +10 -11
- package/lib/cjs/features/search/feature.js +21 -8
- package/lib/cjs/features/search/types.d.ts +1 -1
- package/lib/cjs/features/selection/feature.js +6 -5
- package/lib/cjs/features/sync-data-loader/feature.js +6 -1
- package/lib/cjs/features/sync-data-loader/types.d.ts +1 -1
- package/lib/cjs/features/tree/feature.js +23 -24
- 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 +3 -4
- 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 +34 -12
- package/lib/esm/features/async-data-loader/feature.js +30 -22
- package/lib/esm/features/drag-and-drop/feature.js +58 -18
- package/lib/esm/features/drag-and-drop/types.d.ts +21 -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 -10
- package/lib/esm/features/expand-all/types.d.ts +2 -2
- package/lib/esm/features/hotkeys-core/feature.js +0 -1
- package/lib/esm/features/main/types.d.ts +3 -1
- package/lib/esm/features/renaming/feature.js +10 -11
- package/lib/esm/features/search/feature.js +21 -8
- package/lib/esm/features/search/types.d.ts +1 -1
- package/lib/esm/features/selection/feature.js +6 -5
- package/lib/esm/features/sync-data-loader/feature.js +6 -1
- package/lib/esm/features/sync-data-loader/types.d.ts +1 -1
- package/lib/esm/features/tree/feature.js +23 -24
- 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 +3 -4
- 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 +42 -9
- package/src/features/async-data-loader/feature.ts +15 -6
- package/src/features/drag-and-drop/feature.ts +75 -14
- package/src/features/drag-and-drop/types.ts +30 -5
- package/src/features/drag-and-drop/utils.ts +53 -24
- package/src/features/expand-all/feature.ts +10 -9
- package/src/features/expand-all/types.ts +2 -2
- package/src/features/hotkeys-core/feature.ts +0 -1
- package/src/features/main/types.ts +6 -0
- package/src/features/renaming/feature.ts +10 -7
- package/src/features/search/feature.ts +22 -6
- package/src/features/search/types.ts +1 -0
- package/src/features/selection/feature.ts +7 -3
- package/src/features/sync-data-loader/feature.ts +17 -3
- package/src/features/sync-data-loader/types.ts +1 -1
- package/src/features/tree/feature.ts +23 -22
- package/src/features/tree/types.ts +4 -2
- package/src/index.ts +4 -1
- package/src/types/core.ts +6 -5
- 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
|
@@ -5,16 +5,30 @@ 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
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
export type DragLineData = {
|
|
14
|
+
intend: number;
|
|
15
|
+
top: number;
|
|
16
|
+
left: number;
|
|
17
|
+
right: number;
|
|
16
18
|
};
|
|
17
19
|
|
|
20
|
+
export type DropTarget<T> =
|
|
21
|
+
| {
|
|
22
|
+
item: ItemInstance<T>;
|
|
23
|
+
childIndex: number;
|
|
24
|
+
insertionIndex: number;
|
|
25
|
+
}
|
|
26
|
+
| {
|
|
27
|
+
item: ItemInstance<T>;
|
|
28
|
+
childIndex: null;
|
|
29
|
+
insertionIndex: null;
|
|
30
|
+
};
|
|
31
|
+
|
|
18
32
|
export enum DropTargetPosition {
|
|
19
33
|
Top = "top",
|
|
20
34
|
Bottom = "bottom",
|
|
@@ -44,15 +58,26 @@ export type DragAndDropFeatureDef<T> = {
|
|
|
44
58
|
dataTransfer: DataTransfer,
|
|
45
59
|
target: DropTarget<T>
|
|
46
60
|
) => boolean;
|
|
47
|
-
|
|
48
61
|
onDrop?: (items: ItemInstance<T>[], target: DropTarget<T>) => void;
|
|
49
62
|
onDropForeignDragObject?: (
|
|
50
63
|
dataTransfer: DataTransfer,
|
|
51
64
|
target: DropTarget<T>
|
|
52
65
|
) => void;
|
|
66
|
+
|
|
67
|
+
/** Runs in the onDragEnd event, if `ev.dataTransfer.dropEffect` is not `none`, i.e. the drop
|
|
68
|
+
* was not aborted. No target is provided as parameter since the target may be a foreign drop target.
|
|
69
|
+
* This is useful to seperate out the logic to move dragged items out of their previous parents.
|
|
70
|
+
* Use `onDrop` to handle drop-related logic.
|
|
71
|
+
*
|
|
72
|
+
* This ignores the `canDrop` handler, since the drop target is unknown in this handler.
|
|
73
|
+
*/
|
|
74
|
+
// onSuccessfulDragEnd?: (items: ItemInstance<T>[]) => void;
|
|
75
|
+
|
|
76
|
+
onCompleteForeignDrop?: (items: ItemInstance<T>[]) => void;
|
|
53
77
|
};
|
|
54
78
|
treeInstance: {
|
|
55
79
|
getDropTarget: () => DropTarget<T> | null;
|
|
80
|
+
getDragLineData: () => DragLineData | null;
|
|
56
81
|
};
|
|
57
82
|
itemInstance: {
|
|
58
83
|
isDropTarget: () => boolean;
|
|
@@ -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
|
};
|
|
@@ -14,7 +14,6 @@ export const expandAllFeature: FeatureImplementation<
|
|
|
14
14
|
| ExpandAllFeatureDef
|
|
15
15
|
> = {
|
|
16
16
|
key: "expand-all",
|
|
17
|
-
dependingFeatures: ["main", "tree"],
|
|
18
17
|
|
|
19
18
|
createTreeInstance: (prev, tree) => ({
|
|
20
19
|
...prev,
|
|
@@ -25,8 +24,9 @@ export const expandAllFeature: FeatureImplementation<
|
|
|
25
24
|
);
|
|
26
25
|
},
|
|
27
26
|
|
|
28
|
-
collapseAll:
|
|
29
|
-
tree.
|
|
27
|
+
collapseAll: () => {
|
|
28
|
+
tree.applySubStateUpdate("expandedItems", []);
|
|
29
|
+
tree.rebuildTree();
|
|
30
30
|
},
|
|
31
31
|
}),
|
|
32
32
|
|
|
@@ -37,6 +37,9 @@ export const expandAllFeature: FeatureImplementation<
|
|
|
37
37
|
if (cancelToken?.current) {
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
40
|
+
if (!item.isFolder()) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
40
43
|
|
|
41
44
|
item.expand();
|
|
42
45
|
await poll(() => !tree.getState().loadingItems.includes(item.getId()));
|
|
@@ -50,12 +53,10 @@ export const expandAllFeature: FeatureImplementation<
|
|
|
50
53
|
);
|
|
51
54
|
},
|
|
52
55
|
|
|
53
|
-
collapseAll:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
})
|
|
58
|
-
);
|
|
56
|
+
collapseAll: () => {
|
|
57
|
+
for (const child of item.getChildren()) {
|
|
58
|
+
child?.collapseAll();
|
|
59
|
+
}
|
|
59
60
|
item.collapse();
|
|
60
61
|
},
|
|
61
62
|
}),
|
|
@@ -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
|
};
|
|
@@ -48,7 +48,6 @@ export const hotkeysCoreFeature: FeatureImplementation<
|
|
|
48
48
|
MainFeatureDef | HotkeysCoreFeatureDef<any>
|
|
49
49
|
> = {
|
|
50
50
|
key: "hotkeys-core",
|
|
51
|
-
dependingFeatures: ["main", "tree"],
|
|
52
51
|
|
|
53
52
|
onTreeMount: (tree, element) => {
|
|
54
53
|
const data = tree.getDataRef<HotkeysCoreDataRef>();
|
|
@@ -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>>;
|
|
@@ -10,7 +10,6 @@ export const renamingFeature: FeatureImplementation<
|
|
|
10
10
|
MainFeatureDef | TreeFeatureDef<any> | RenamingFeatureDef<any>
|
|
11
11
|
> = {
|
|
12
12
|
key: "renaming",
|
|
13
|
-
dependingFeatures: ["main", "tree"],
|
|
14
13
|
|
|
15
14
|
getDefaultConfig: (defaultConfig, tree) => ({
|
|
16
15
|
setRenamingItem: makeStateUpdater("renamingItem", tree),
|
|
@@ -19,19 +18,23 @@ export const renamingFeature: FeatureImplementation<
|
|
|
19
18
|
...defaultConfig,
|
|
20
19
|
}),
|
|
21
20
|
|
|
21
|
+
stateHandlerNames: {
|
|
22
|
+
renamingItem: "setRenamingItem",
|
|
23
|
+
renamingValue: "setRenamingValue",
|
|
24
|
+
},
|
|
25
|
+
|
|
22
26
|
createTreeInstance: (prev, instance) => ({
|
|
23
27
|
...prev,
|
|
24
28
|
|
|
25
29
|
startRenamingItem: (itemId) => {
|
|
26
|
-
const config = instance.getConfig();
|
|
27
30
|
const item = instance.getItemInstance(itemId);
|
|
28
31
|
|
|
29
32
|
if (!item.canRename()) {
|
|
30
33
|
return;
|
|
31
34
|
}
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
instance.applySubStateUpdate("renamingItem", itemId);
|
|
37
|
+
instance.applySubStateUpdate("renamingValue", item.getItemName());
|
|
35
38
|
},
|
|
36
39
|
|
|
37
40
|
getRenamingItem: () => {
|
|
@@ -42,7 +45,7 @@ export const renamingFeature: FeatureImplementation<
|
|
|
42
45
|
getRenamingValue: () => instance.getState().renamingValue || "",
|
|
43
46
|
|
|
44
47
|
abortRenaming: () => {
|
|
45
|
-
instance.
|
|
48
|
+
instance.applySubStateUpdate("renamingItem", null);
|
|
46
49
|
},
|
|
47
50
|
|
|
48
51
|
completeRenaming: () => {
|
|
@@ -51,7 +54,7 @@ export const renamingFeature: FeatureImplementation<
|
|
|
51
54
|
if (item) {
|
|
52
55
|
config.onRename?.(item, instance.getState().renamingValue || "");
|
|
53
56
|
}
|
|
54
|
-
instance.
|
|
57
|
+
instance.applySubStateUpdate("renamingItem", null);
|
|
55
58
|
},
|
|
56
59
|
|
|
57
60
|
isRenamingItem: () => !!instance.getState().renamingItem,
|
|
@@ -63,7 +66,7 @@ export const renamingFeature: FeatureImplementation<
|
|
|
63
66
|
onBlur: () => tree.abortRenaming(),
|
|
64
67
|
value: tree.getRenamingValue(),
|
|
65
68
|
onChange: (e) => {
|
|
66
|
-
tree.
|
|
69
|
+
tree.applySubStateUpdate("renamingValue", e.target.value);
|
|
67
70
|
},
|
|
68
71
|
}),
|
|
69
72
|
|
|
@@ -10,7 +10,6 @@ export const searchFeature: FeatureImplementation<
|
|
|
10
10
|
MainFeatureDef | TreeFeatureDef<any> | SearchFeatureDef<any>
|
|
11
11
|
> = {
|
|
12
12
|
key: "search",
|
|
13
|
-
dependingFeatures: ["main", "tree"],
|
|
14
13
|
|
|
15
14
|
getInitialState: (initialState) => ({
|
|
16
15
|
search: null,
|
|
@@ -25,11 +24,15 @@ export const searchFeature: FeatureImplementation<
|
|
|
25
24
|
...defaultConfig,
|
|
26
25
|
}),
|
|
27
26
|
|
|
27
|
+
stateHandlerNames: {
|
|
28
|
+
search: "setSearch",
|
|
29
|
+
},
|
|
30
|
+
|
|
28
31
|
createTreeInstance: (prev, instance) => ({
|
|
29
32
|
...prev,
|
|
30
33
|
|
|
31
34
|
setSearch: (search) => {
|
|
32
|
-
instance.
|
|
35
|
+
instance.applySubStateUpdate("search", search);
|
|
33
36
|
instance
|
|
34
37
|
.getItems()
|
|
35
38
|
.find((item) =>
|
|
@@ -71,8 +74,9 @@ export const searchFeature: FeatureImplementation<
|
|
|
71
74
|
|
|
72
75
|
getSearchMatchingItems: memo(
|
|
73
76
|
(search, items) =>
|
|
74
|
-
items.filter(
|
|
75
|
-
|
|
77
|
+
items.filter(
|
|
78
|
+
(item) =>
|
|
79
|
+
search && instance.getConfig().isSearchMatchingItem?.(search, item)
|
|
76
80
|
),
|
|
77
81
|
() => [instance.getSearchValue(), instance.getItems()]
|
|
78
82
|
),
|
|
@@ -105,12 +109,22 @@ export const searchFeature: FeatureImplementation<
|
|
|
105
109
|
},
|
|
106
110
|
},
|
|
107
111
|
|
|
112
|
+
submitSearch: {
|
|
113
|
+
hotkey: "Enter",
|
|
114
|
+
allowWhenInputFocused: true,
|
|
115
|
+
isEnabled: (tree) => tree.isSearchOpen(),
|
|
116
|
+
handler: (e, tree) => {
|
|
117
|
+
tree.closeSearch();
|
|
118
|
+
tree.setSelectedItems([tree.getFocusedItem().getId()]);
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
|
|
108
122
|
nextSearchItem: {
|
|
109
123
|
hotkey: "ArrowDown",
|
|
110
124
|
allowWhenInputFocused: true,
|
|
125
|
+
canRepeat: true,
|
|
111
126
|
isEnabled: (tree) => tree.isSearchOpen(),
|
|
112
127
|
handler: (e, tree) => {
|
|
113
|
-
// TODO scroll into view
|
|
114
128
|
const focusItem = tree
|
|
115
129
|
.getSearchMatchingItems()
|
|
116
130
|
.find(
|
|
@@ -119,15 +133,16 @@ export const searchFeature: FeatureImplementation<
|
|
|
119
133
|
tree.getFocusedItem().getItemMeta().index
|
|
120
134
|
);
|
|
121
135
|
focusItem?.setFocused();
|
|
136
|
+
focusItem?.scrollTo({ block: "nearest", inline: "nearest" });
|
|
122
137
|
},
|
|
123
138
|
},
|
|
124
139
|
|
|
125
140
|
previousSearchItem: {
|
|
126
141
|
hotkey: "ArrowUp",
|
|
127
142
|
allowWhenInputFocused: true,
|
|
143
|
+
canRepeat: true,
|
|
128
144
|
isEnabled: (tree) => tree.isSearchOpen(),
|
|
129
145
|
handler: (e, tree) => {
|
|
130
|
-
// TODO scroll into view
|
|
131
146
|
const focusItem = [...tree.getSearchMatchingItems()]
|
|
132
147
|
.reverse()
|
|
133
148
|
.find(
|
|
@@ -136,6 +151,7 @@ export const searchFeature: FeatureImplementation<
|
|
|
136
151
|
tree.getFocusedItem().getItemMeta().index
|
|
137
152
|
);
|
|
138
153
|
focusItem?.setFocused();
|
|
154
|
+
focusItem?.scrollTo({ block: "nearest", inline: "nearest" });
|
|
139
155
|
},
|
|
140
156
|
},
|
|
141
157
|
},
|
|
@@ -10,7 +10,6 @@ export const selectionFeature: FeatureImplementation<
|
|
|
10
10
|
MainFeatureDef | TreeFeatureDef<any> | SelectionFeatureDef<any>
|
|
11
11
|
> = {
|
|
12
12
|
key: "selection",
|
|
13
|
-
dependingFeatures: ["main", "tree"],
|
|
14
13
|
|
|
15
14
|
getInitialState: (initialState) => ({
|
|
16
15
|
selectedItems: [],
|
|
@@ -22,11 +21,15 @@ export const selectionFeature: FeatureImplementation<
|
|
|
22
21
|
...defaultConfig,
|
|
23
22
|
}),
|
|
24
23
|
|
|
24
|
+
stateHandlerNames: {
|
|
25
|
+
selectedItems: "setSelectedItems",
|
|
26
|
+
},
|
|
27
|
+
|
|
25
28
|
createTreeInstance: (prev, instance) => ({
|
|
26
29
|
...prev,
|
|
27
30
|
|
|
28
31
|
setSelectedItems: (selectedItems) => {
|
|
29
|
-
instance.
|
|
32
|
+
instance.applySubStateUpdate("selectedItems", selectedItems);
|
|
30
33
|
},
|
|
31
34
|
|
|
32
35
|
// TODO memo
|
|
@@ -67,7 +70,7 @@ export const selectionFeature: FeatureImplementation<
|
|
|
67
70
|
const newSelectedItems = tree
|
|
68
71
|
.getItems()
|
|
69
72
|
.slice(a, b + 1)
|
|
70
|
-
.map((
|
|
73
|
+
.map((treeItem) => treeItem.getItemMeta().itemId);
|
|
71
74
|
|
|
72
75
|
if (!ctrl) {
|
|
73
76
|
tree.setSelectedItems(newSelectedItems);
|
|
@@ -91,6 +94,7 @@ export const selectionFeature: FeatureImplementation<
|
|
|
91
94
|
|
|
92
95
|
getProps: () => ({
|
|
93
96
|
...prev.getProps(),
|
|
97
|
+
"aria-selected": item.isSelected() ? "true" : "false",
|
|
94
98
|
onClick: item.getMemoizedProp("selection/onClick", () => (e) => {
|
|
95
99
|
if (e.shiftKey) {
|
|
96
100
|
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,
|
|
@@ -8,16 +9,29 @@ export const syncDataLoaderFeature: FeatureImplementation<
|
|
|
8
9
|
MainFeatureDef | SyncDataLoaderFeatureDef<any>
|
|
9
10
|
> = {
|
|
10
11
|
key: "sync-data-loader",
|
|
11
|
-
|
|
12
|
+
|
|
13
|
+
getInitialState: (initialState) => ({
|
|
14
|
+
loadingItems: [],
|
|
15
|
+
...initialState,
|
|
16
|
+
}),
|
|
17
|
+
|
|
18
|
+
getDefaultConfig: (defaultConfig, tree) => ({
|
|
19
|
+
setLoadingItems: makeStateUpdater("loadingItems", tree),
|
|
20
|
+
...defaultConfig,
|
|
21
|
+
}),
|
|
22
|
+
|
|
23
|
+
stateHandlerNames: {
|
|
24
|
+
loadingItems: "setLoadingItems",
|
|
25
|
+
},
|
|
12
26
|
|
|
13
27
|
createTreeInstance: (prev, instance) => ({
|
|
14
28
|
...prev,
|
|
15
29
|
|
|
16
30
|
retrieveItemData: (itemId) =>
|
|
17
|
-
instance.getConfig().dataLoader
|
|
31
|
+
instance.getConfig().dataLoader!.getItem(itemId),
|
|
18
32
|
|
|
19
33
|
retrieveChildrenIds: (itemId) =>
|
|
20
|
-
instance.getConfig().dataLoader
|
|
34
|
+
instance.getConfig().dataLoader!.getChildren(itemId),
|
|
21
35
|
}),
|
|
22
36
|
|
|
23
37
|
createItemInstance: (prev) => ({
|
|
@@ -14,7 +14,6 @@ export const treeFeature: FeatureImplementation<
|
|
|
14
14
|
| SyncDataLoaderFeatureDef<any>
|
|
15
15
|
> = {
|
|
16
16
|
key: "tree",
|
|
17
|
-
dependingFeatures: ["main"],
|
|
18
17
|
|
|
19
18
|
getInitialState: (initialState) => ({
|
|
20
19
|
expandedItems: [],
|
|
@@ -28,6 +27,11 @@ export const treeFeature: FeatureImplementation<
|
|
|
28
27
|
...defaultConfig,
|
|
29
28
|
}),
|
|
30
29
|
|
|
30
|
+
stateHandlerNames: {
|
|
31
|
+
expandedItems: "setExpandedItems",
|
|
32
|
+
focusedItem: "setFocusedItem",
|
|
33
|
+
},
|
|
34
|
+
|
|
31
35
|
createTreeInstance: (prev, instance) => ({
|
|
32
36
|
...prev,
|
|
33
37
|
|
|
@@ -45,7 +49,6 @@ export const treeFeature: FeatureImplementation<
|
|
|
45
49
|
getItemsMeta: () => {
|
|
46
50
|
const { rootItemId } = instance.getConfig();
|
|
47
51
|
const { expandedItems } = instance.getState();
|
|
48
|
-
// console.log("!", instance.getConfig());
|
|
49
52
|
const flatItems: ItemMeta[] = [];
|
|
50
53
|
|
|
51
54
|
const recursiveAdd = (
|
|
@@ -91,9 +94,10 @@ export const treeFeature: FeatureImplementation<
|
|
|
91
94
|
return;
|
|
92
95
|
}
|
|
93
96
|
|
|
94
|
-
instance
|
|
95
|
-
|
|
96
|
-
|
|
97
|
+
instance.applySubStateUpdate("expandedItems", (expandedItems) => [
|
|
98
|
+
...expandedItems,
|
|
99
|
+
itemId,
|
|
100
|
+
]);
|
|
97
101
|
instance.rebuildTree();
|
|
98
102
|
},
|
|
99
103
|
|
|
@@ -102,11 +106,9 @@ export const treeFeature: FeatureImplementation<
|
|
|
102
106
|
return;
|
|
103
107
|
}
|
|
104
108
|
|
|
105
|
-
instance
|
|
106
|
-
.
|
|
107
|
-
|
|
108
|
-
expandedItems.filter((id) => id !== itemId)
|
|
109
|
-
);
|
|
109
|
+
instance.applySubStateUpdate("expandedItems", (expandedItems) =>
|
|
110
|
+
expandedItems.filter((id) => id !== itemId)
|
|
111
|
+
);
|
|
110
112
|
instance.rebuildTree();
|
|
111
113
|
},
|
|
112
114
|
|
|
@@ -119,7 +121,7 @@ export const treeFeature: FeatureImplementation<
|
|
|
119
121
|
},
|
|
120
122
|
|
|
121
123
|
focusItem: (itemId) => {
|
|
122
|
-
instance.
|
|
124
|
+
instance.applySubStateUpdate("focusedItem", itemId);
|
|
123
125
|
},
|
|
124
126
|
|
|
125
127
|
focusNextItem: () => {
|
|
@@ -134,7 +136,7 @@ export const treeFeature: FeatureImplementation<
|
|
|
134
136
|
instance.focusItem(instance.getItems()[nextIndex].getId());
|
|
135
137
|
},
|
|
136
138
|
|
|
137
|
-
updateDomFocus: (
|
|
139
|
+
updateDomFocus: () => {
|
|
138
140
|
// Required because if the state is managed outside in react, the state only updated during next render
|
|
139
141
|
setTimeout(async () => {
|
|
140
142
|
const focusedItem = instance.getFocusedItem();
|
|
@@ -143,9 +145,6 @@ export const treeFeature: FeatureImplementation<
|
|
|
143
145
|
const focusedElement = focusedItem.getElement();
|
|
144
146
|
if (!focusedElement) return;
|
|
145
147
|
focusedElement.focus();
|
|
146
|
-
// if (scrollIntoView) {
|
|
147
|
-
// focusedElement.scrollIntoView();
|
|
148
|
-
// }
|
|
149
148
|
});
|
|
150
149
|
},
|
|
151
150
|
|
|
@@ -162,6 +161,11 @@ export const treeFeature: FeatureImplementation<
|
|
|
162
161
|
isLoading: () => {
|
|
163
162
|
throw new Error("No data-loader registered");
|
|
164
163
|
},
|
|
164
|
+
scrollTo: async (scrollIntoViewArg) => {
|
|
165
|
+
tree.getConfig().scrollToItem?.(item as any);
|
|
166
|
+
await poll(() => item.getElement() !== null, 20);
|
|
167
|
+
item.getElement()!.scrollIntoView(scrollIntoViewArg);
|
|
168
|
+
},
|
|
165
169
|
getId: () => item.getItemMeta().itemId,
|
|
166
170
|
getProps: () => {
|
|
167
171
|
const itemMeta = item.getItemMeta();
|
|
@@ -170,12 +174,11 @@ export const treeFeature: FeatureImplementation<
|
|
|
170
174
|
role: "treeitem",
|
|
171
175
|
"aria-setsize": itemMeta.setSize,
|
|
172
176
|
"aria-posinset": itemMeta.posInSet,
|
|
173
|
-
"aria-selected": false,
|
|
177
|
+
"aria-selected": "false",
|
|
174
178
|
"aria-label": item.getItemName(),
|
|
175
179
|
"aria-level": itemMeta.level,
|
|
176
180
|
tabIndex: item.isFocused() ? 0 : -1,
|
|
177
181
|
onClick: item.getMemoizedProp("tree/onClick", () => (e) => {
|
|
178
|
-
console.log("onClick", item.getId());
|
|
179
182
|
item.setFocused();
|
|
180
183
|
item.primaryAction();
|
|
181
184
|
|
|
@@ -225,10 +228,8 @@ export const treeFeature: FeatureImplementation<
|
|
|
225
228
|
},
|
|
226
229
|
() => [item.getItemMeta()]
|
|
227
230
|
),
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
(item.getParent()?.getItemMeta().index ?? 0) -
|
|
231
|
-
1,
|
|
231
|
+
// TODO remove
|
|
232
|
+
getIndexInParent: () => item.getItemMeta().posInSet,
|
|
232
233
|
getChildren: () =>
|
|
233
234
|
tree
|
|
234
235
|
.retrieveChildrenIds(item.getItemMeta().itemId)
|
|
@@ -301,7 +302,7 @@ export const treeFeature: FeatureImplementation<
|
|
|
301
302
|
item.getItemMeta().level !== 0
|
|
302
303
|
) {
|
|
303
304
|
item.getParent()?.setFocused();
|
|
304
|
-
tree.updateDomFocus(
|
|
305
|
+
tree.updateDomFocus();
|
|
305
306
|
} else {
|
|
306
307
|
item.collapse();
|
|
307
308
|
}
|
|
@@ -41,8 +41,7 @@ export type TreeFeatureDef<T> = {
|
|
|
41
41
|
getFocusedItem: () => ItemInstance<any>;
|
|
42
42
|
focusNextItem: () => void;
|
|
43
43
|
focusPreviousItem: () => void;
|
|
44
|
-
|
|
45
|
-
updateDomFocus: (scrollIntoView?: boolean) => void;
|
|
44
|
+
updateDomFocus: () => void;
|
|
46
45
|
|
|
47
46
|
getContainerProps: () => Record<string, any>;
|
|
48
47
|
};
|
|
@@ -65,6 +64,9 @@ export type TreeFeatureDef<T> = {
|
|
|
65
64
|
getItemAbove: () => ItemInstance<T> | null;
|
|
66
65
|
getItemBelow: () => ItemInstance<T> | null;
|
|
67
66
|
getMemoizedProp: <X>(name: string, create: () => X, deps?: any[]) => X;
|
|
67
|
+
scrollTo: (
|
|
68
|
+
scrollIntoViewArg?: boolean | ScrollIntoViewOptions
|
|
69
|
+
) => Promise<void>;
|
|
68
70
|
};
|
|
69
71
|
hotkeys:
|
|
70
72
|
| "focusNextItem"
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from "./types/core";
|
|
2
2
|
export * from "./core/create-tree";
|
|
3
|
+
|
|
3
4
|
export * from "./features/tree/types";
|
|
4
5
|
export * from "./features/main/types";
|
|
5
6
|
export * from "./features/drag-and-drop/types";
|
|
@@ -20,4 +21,6 @@ export * from "./features/search/feature";
|
|
|
20
21
|
export * from "./features/renaming/feature";
|
|
21
22
|
export * from "./features/expand-all/feature";
|
|
22
23
|
|
|
23
|
-
export * from "./
|
|
24
|
+
export * from "./utilities/create-on-drop-handler";
|
|
25
|
+
export * from "./utilities/insert-items-at-target";
|
|
26
|
+
export * from "./utilities/remove-items-from-parents";
|