@headless-tree/core 0.0.14 → 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 +7 -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,6 +1,6 @@
|
|
|
1
1
|
import { FeatureImplementation } from "../../types/core";
|
|
2
2
|
import { DndDataRef, DragLineData } from "./types";
|
|
3
|
-
import { canDrop, getDragCode,
|
|
3
|
+
import { canDrop, getDragCode, getDragTarget } from "./utils";
|
|
4
4
|
import { makeStateUpdater } from "../../utils";
|
|
5
5
|
|
|
6
6
|
export const dragAndDropFeature: FeatureImplementation = {
|
|
@@ -20,17 +20,17 @@ export const dragAndDropFeature: FeatureImplementation = {
|
|
|
20
20
|
},
|
|
21
21
|
|
|
22
22
|
treeInstance: {
|
|
23
|
-
|
|
23
|
+
getDragTarget: ({ tree }) => {
|
|
24
24
|
return tree.getState().dnd?.dragTarget ?? null;
|
|
25
25
|
},
|
|
26
26
|
|
|
27
27
|
getDragLineData: ({ tree }): DragLineData | null => {
|
|
28
|
-
const target = tree.
|
|
28
|
+
const target = tree.getDragTarget();
|
|
29
29
|
const indent = (target?.item.getItemMeta().level ?? 0) + 1;
|
|
30
30
|
|
|
31
31
|
const treeBb = tree.getElement()?.getBoundingClientRect();
|
|
32
32
|
|
|
33
|
-
if (!target || !treeBb ||
|
|
33
|
+
if (!target || !treeBb || !("childIndex" in target)) return null;
|
|
34
34
|
|
|
35
35
|
const leftOffset = target.dragLineLevel * (tree.getConfig().indent ?? 1);
|
|
36
36
|
const targetItem = tree.getItems()[target.dragLineIndex];
|
|
@@ -44,7 +44,7 @@ export const dragAndDropFeature: FeatureImplementation = {
|
|
|
44
44
|
if (bb) {
|
|
45
45
|
return {
|
|
46
46
|
indent,
|
|
47
|
-
top: bb.bottom - treeBb.
|
|
47
|
+
top: bb.bottom - treeBb.top,
|
|
48
48
|
left: bb.left + leftOffset - treeBb.left,
|
|
49
49
|
width: bb.width - leftOffset,
|
|
50
50
|
};
|
|
@@ -77,8 +77,8 @@ export const dragAndDropFeature: FeatureImplementation = {
|
|
|
77
77
|
: { display: "none" };
|
|
78
78
|
},
|
|
79
79
|
|
|
80
|
-
getContainerProps: ({ prev }) => {
|
|
81
|
-
const prevProps = prev?.();
|
|
80
|
+
getContainerProps: ({ prev }, treeLabel) => {
|
|
81
|
+
const prevProps = prev?.(treeLabel);
|
|
82
82
|
return {
|
|
83
83
|
...prevProps,
|
|
84
84
|
style: {
|
|
@@ -93,7 +93,7 @@ export const dragAndDropFeature: FeatureImplementation = {
|
|
|
93
93
|
getProps: ({ tree, item, prev }) => ({
|
|
94
94
|
...prev?.(),
|
|
95
95
|
|
|
96
|
-
draggable:
|
|
96
|
+
draggable: true,
|
|
97
97
|
|
|
98
98
|
onDragStart: (e: DragEvent) => {
|
|
99
99
|
const selectedItems = tree.getSelectedItems();
|
|
@@ -131,7 +131,7 @@ export const dragAndDropFeature: FeatureImplementation = {
|
|
|
131
131
|
}
|
|
132
132
|
dataRef.current.lastDragCode = nextDragCode;
|
|
133
133
|
|
|
134
|
-
const target =
|
|
134
|
+
const target = getDragTarget(e, item, tree);
|
|
135
135
|
|
|
136
136
|
if (
|
|
137
137
|
!tree.getState().dnd?.draggedItems &&
|
|
@@ -179,9 +179,9 @@ export const dragAndDropFeature: FeatureImplementation = {
|
|
|
179
179
|
tree.getConfig().onCompleteForeignDrop?.(draggedItems);
|
|
180
180
|
},
|
|
181
181
|
|
|
182
|
-
onDrop: (e: DragEvent) => {
|
|
182
|
+
onDrop: async (e: DragEvent) => {
|
|
183
183
|
const dataRef = tree.getDataRef<DndDataRef>();
|
|
184
|
-
const target =
|
|
184
|
+
const target = getDragTarget(e, item, tree);
|
|
185
185
|
|
|
186
186
|
if (!canDrop(e.dataTransfer, target, tree)) {
|
|
187
187
|
return;
|
|
@@ -195,37 +195,36 @@ export const dragAndDropFeature: FeatureImplementation = {
|
|
|
195
195
|
tree.applySubStateUpdate("dnd", null);
|
|
196
196
|
|
|
197
197
|
if (draggedItems) {
|
|
198
|
-
config.onDrop?.(draggedItems, target);
|
|
198
|
+
await config.onDrop?.(draggedItems, target);
|
|
199
199
|
} else if (e.dataTransfer) {
|
|
200
|
-
config.onDropForeignDragObject?.(e.dataTransfer, target);
|
|
200
|
+
await config.onDropForeignDragObject?.(e.dataTransfer, target);
|
|
201
201
|
}
|
|
202
|
-
// TODO rebuild tree?
|
|
203
202
|
},
|
|
204
203
|
}),
|
|
205
204
|
|
|
206
|
-
|
|
207
|
-
const target = tree.
|
|
205
|
+
isDragTarget: ({ tree, item }) => {
|
|
206
|
+
const target = tree.getDragTarget();
|
|
208
207
|
return target ? target.item.getId() === item.getId() : false;
|
|
209
208
|
},
|
|
210
209
|
|
|
211
|
-
|
|
212
|
-
const target = tree.
|
|
210
|
+
isDragTargetAbove: ({ tree, item }) => {
|
|
211
|
+
const target = tree.getDragTarget();
|
|
213
212
|
|
|
214
213
|
if (
|
|
215
214
|
!target ||
|
|
216
|
-
|
|
215
|
+
!("childIndex" in target) ||
|
|
217
216
|
target.item !== item.getParent()
|
|
218
217
|
)
|
|
219
218
|
return false;
|
|
220
219
|
return target.childIndex === item.getItemMeta().posInSet;
|
|
221
220
|
},
|
|
222
221
|
|
|
223
|
-
|
|
224
|
-
const target = tree.
|
|
222
|
+
isDragTargetBelow: ({ tree, item }) => {
|
|
223
|
+
const target = tree.getDragTarget();
|
|
225
224
|
|
|
226
225
|
if (
|
|
227
226
|
!target ||
|
|
228
|
-
|
|
227
|
+
!("childIndex" in target) ||
|
|
229
228
|
target.item !== item.getParent()
|
|
230
229
|
)
|
|
231
230
|
return false;
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
import { ItemInstance, SetStateFn } from "../../types/core";
|
|
2
2
|
|
|
3
|
-
export
|
|
3
|
+
export interface DndDataRef {
|
|
4
4
|
lastDragCode?: string;
|
|
5
5
|
lastAllowDrop?: boolean;
|
|
6
|
-
}
|
|
6
|
+
}
|
|
7
7
|
|
|
8
|
-
export
|
|
8
|
+
export interface DndState<T> {
|
|
9
9
|
draggedItems?: ItemInstance<T>[];
|
|
10
10
|
draggingOverItem?: ItemInstance<T>;
|
|
11
|
-
dragTarget?:
|
|
12
|
-
}
|
|
11
|
+
dragTarget?: DragTarget<T>;
|
|
12
|
+
}
|
|
13
13
|
|
|
14
|
-
export
|
|
14
|
+
export interface DragLineData {
|
|
15
15
|
indent: number;
|
|
16
16
|
top: number;
|
|
17
17
|
left: number;
|
|
18
18
|
width: number;
|
|
19
|
-
}
|
|
19
|
+
}
|
|
20
20
|
|
|
21
|
-
export type
|
|
21
|
+
export type DragTarget<T> =
|
|
22
22
|
| {
|
|
23
23
|
item: ItemInstance<T>;
|
|
24
24
|
childIndex: number;
|
|
@@ -27,14 +27,10 @@ export type DropTarget<T> =
|
|
|
27
27
|
dragLineLevel: number;
|
|
28
28
|
}
|
|
29
29
|
| {
|
|
30
|
-
item: ItemInstance<T>;
|
|
31
|
-
childIndex: null;
|
|
32
|
-
insertionIndex: null;
|
|
33
|
-
dragLineIndex: null;
|
|
34
|
-
dragLineLevel: null;
|
|
30
|
+
item: ItemInstance<T>;
|
|
35
31
|
};
|
|
36
32
|
|
|
37
|
-
export enum
|
|
33
|
+
export enum DragTargetPosition {
|
|
38
34
|
Top = "top",
|
|
39
35
|
Bottom = "bottom",
|
|
40
36
|
Item = "item",
|
|
@@ -53,10 +49,8 @@ export type DragAndDropFeatureDef<T> = {
|
|
|
53
49
|
reorderAreaPercentage?: number;
|
|
54
50
|
canReorder?: boolean;
|
|
55
51
|
|
|
56
|
-
// TODO better document difference to canDrag(), or unify both
|
|
57
|
-
isItemDraggable?: (item: ItemInstance<T>) => boolean;
|
|
58
52
|
canDrag?: (items: ItemInstance<T>[]) => boolean;
|
|
59
|
-
canDrop?: (items: ItemInstance<T>[], target:
|
|
53
|
+
canDrop?: (items: ItemInstance<T>[], target: DragTarget<T>) => boolean;
|
|
60
54
|
|
|
61
55
|
indent?: number;
|
|
62
56
|
|
|
@@ -66,37 +60,31 @@ export type DragAndDropFeatureDef<T> = {
|
|
|
66
60
|
};
|
|
67
61
|
canDropForeignDragObject?: (
|
|
68
62
|
dataTransfer: DataTransfer,
|
|
69
|
-
target:
|
|
63
|
+
target: DragTarget<T>,
|
|
70
64
|
) => boolean;
|
|
71
|
-
onDrop?: (
|
|
65
|
+
onDrop?: (
|
|
66
|
+
items: ItemInstance<T>[],
|
|
67
|
+
target: DragTarget<T>,
|
|
68
|
+
) => void | Promise<void>;
|
|
72
69
|
onDropForeignDragObject?: (
|
|
73
70
|
dataTransfer: DataTransfer,
|
|
74
|
-
target:
|
|
75
|
-
) => void
|
|
76
|
-
|
|
77
|
-
/** Runs in the onDragEnd event, if `ev.dataTransfer.dropEffect` is not `none`, i.e. the drop
|
|
78
|
-
* was not aborted. No target is provided as parameter since the target may be a foreign drop target.
|
|
79
|
-
* This is useful to seperate out the logic to move dragged items out of their previous parents.
|
|
80
|
-
* Use `onDrop` to handle drop-related logic.
|
|
81
|
-
*
|
|
82
|
-
* This ignores the `canDrop` handler, since the drop target is unknown in this handler.
|
|
83
|
-
*/
|
|
84
|
-
// onSuccessfulDragEnd?: (items: ItemInstance<T>[]) => void;
|
|
85
|
-
|
|
71
|
+
target: DragTarget<T>,
|
|
72
|
+
) => void | Promise<void>;
|
|
86
73
|
onCompleteForeignDrop?: (items: ItemInstance<T>[]) => void;
|
|
87
74
|
};
|
|
88
75
|
treeInstance: {
|
|
89
|
-
|
|
76
|
+
getDragTarget: () => DragTarget<T> | null;
|
|
90
77
|
getDragLineData: () => DragLineData | null;
|
|
78
|
+
|
|
91
79
|
getDragLineStyle: (
|
|
92
80
|
topOffset?: number,
|
|
93
81
|
leftOffset?: number,
|
|
94
82
|
) => Record<string, any>;
|
|
95
83
|
};
|
|
96
84
|
itemInstance: {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
85
|
+
isDragTarget: () => boolean;
|
|
86
|
+
isDragTargetAbove: () => boolean;
|
|
87
|
+
isDragTargetBelow: () => boolean;
|
|
100
88
|
isDraggingOver: () => boolean;
|
|
101
89
|
};
|
|
102
90
|
hotkeys: never;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ItemInstance, TreeInstance } from "../../types/core";
|
|
2
|
-
import {
|
|
2
|
+
import { DragTarget } from "./types";
|
|
3
3
|
|
|
4
|
-
enum ItemDropCategory {
|
|
4
|
+
export enum ItemDropCategory {
|
|
5
5
|
Item,
|
|
6
6
|
ExpandedFolder,
|
|
7
7
|
LastInGroup,
|
|
@@ -28,7 +28,7 @@ type TargetPlacement =
|
|
|
28
28
|
|
|
29
29
|
export const canDrop = (
|
|
30
30
|
dataTransfer: DataTransfer | null,
|
|
31
|
-
target:
|
|
31
|
+
target: DragTarget<any>,
|
|
32
32
|
tree: TreeInstance<any>,
|
|
33
33
|
) => {
|
|
34
34
|
const draggedItems = tree.getState().dnd?.draggedItems;
|
|
@@ -52,7 +52,8 @@ export const canDrop = (
|
|
|
52
52
|
if (
|
|
53
53
|
!draggedItems &&
|
|
54
54
|
dataTransfer &&
|
|
55
|
-
|
|
55
|
+
config.canDropForeignDragObject &&
|
|
56
|
+
!config.canDropForeignDragObject(dataTransfer, target)
|
|
56
57
|
) {
|
|
57
58
|
return false;
|
|
58
59
|
}
|
|
@@ -60,19 +61,37 @@ export const canDrop = (
|
|
|
60
61
|
return true;
|
|
61
62
|
};
|
|
62
63
|
|
|
63
|
-
const getItemDropCategory = (item: ItemInstance<any>) => {
|
|
64
|
+
export const getItemDropCategory = (item: ItemInstance<any>) => {
|
|
64
65
|
if (item.isExpanded()) {
|
|
65
66
|
return ItemDropCategory.ExpandedFolder;
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
const parent = item.getParent();
|
|
69
|
-
if (parent && item.getIndexInParent() ===
|
|
70
|
+
if (parent && item.getIndexInParent() === item.getItemMeta().setSize - 1) {
|
|
70
71
|
return ItemDropCategory.LastInGroup;
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
return ItemDropCategory.Item;
|
|
74
75
|
};
|
|
75
76
|
|
|
77
|
+
export const getInsertionIndex = <T>(
|
|
78
|
+
children: ItemInstance<T>[],
|
|
79
|
+
childIndex: number,
|
|
80
|
+
draggedItems: ItemInstance<T>[] | undefined,
|
|
81
|
+
) => {
|
|
82
|
+
const numberOfDragItemsBeforeTarget =
|
|
83
|
+
children
|
|
84
|
+
.slice(0, childIndex)
|
|
85
|
+
.reduce(
|
|
86
|
+
(counter, child) =>
|
|
87
|
+
child && draggedItems?.some((i) => i.getId() === child.getId())
|
|
88
|
+
? ++counter
|
|
89
|
+
: counter,
|
|
90
|
+
0,
|
|
91
|
+
) ?? 0;
|
|
92
|
+
return childIndex - numberOfDragItemsBeforeTarget;
|
|
93
|
+
};
|
|
94
|
+
|
|
76
95
|
const getTargetPlacement = (
|
|
77
96
|
e: any,
|
|
78
97
|
item: ItemInstance<any>,
|
|
@@ -88,8 +107,8 @@ const getTargetPlacement = (
|
|
|
88
107
|
}
|
|
89
108
|
|
|
90
109
|
const bb = item.getElement()?.getBoundingClientRect();
|
|
91
|
-
const topPercent = bb ? (e.
|
|
92
|
-
const leftPixels = bb ? e.
|
|
110
|
+
const topPercent = bb ? (e.clientY - bb.top) / bb.height : 0.5;
|
|
111
|
+
const leftPixels = bb ? e.clientX - bb.left : 0;
|
|
93
112
|
const targetDropCategory = getItemDropCategory(item);
|
|
94
113
|
const reorderAreaPercentage = !canMakeChild
|
|
95
114
|
? 0.5
|
|
@@ -111,9 +130,10 @@ const getTargetPlacement = (
|
|
|
111
130
|
if (topPercent < 0.5) {
|
|
112
131
|
return { type: PlacementType.ReorderAbove };
|
|
113
132
|
}
|
|
133
|
+
const minLevel = item.getItemBelow()?.getItemMeta().level ?? 0;
|
|
114
134
|
return {
|
|
115
135
|
type: PlacementType.Reparent,
|
|
116
|
-
reparentLevel: Math.floor(leftPixels / indent),
|
|
136
|
+
reparentLevel: Math.max(minLevel, Math.floor(leftPixels / indent)),
|
|
117
137
|
};
|
|
118
138
|
}
|
|
119
139
|
// if not at left of item area, treat as if it was a normal item
|
|
@@ -152,31 +172,41 @@ const getNthParent = (
|
|
|
152
172
|
return getNthParent(item.getParent()!, n);
|
|
153
173
|
};
|
|
154
174
|
|
|
155
|
-
|
|
175
|
+
/** @param item refers to the bottom-most item of the container, at which bottom is being reparented on (e.g. root-1-2-6) */
|
|
176
|
+
export const getReparentTarget = <T>(
|
|
177
|
+
item: ItemInstance<T>,
|
|
178
|
+
reparentLevel: number,
|
|
179
|
+
draggedItems: ItemInstance<T>[] | undefined,
|
|
180
|
+
) => {
|
|
181
|
+
const itemMeta = item.getItemMeta();
|
|
182
|
+
const reparentedTarget = getNthParent(item, reparentLevel - 1);
|
|
183
|
+
const targetItemAbove = getNthParent(item, reparentLevel); // .getItemBelow()!;
|
|
184
|
+
const targetIndex = targetItemAbove.getIndexInParent() + 1;
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
item: reparentedTarget,
|
|
188
|
+
childIndex: targetIndex,
|
|
189
|
+
insertionIndex: getInsertionIndex(
|
|
190
|
+
reparentedTarget.getChildren(),
|
|
191
|
+
targetIndex,
|
|
192
|
+
draggedItems,
|
|
193
|
+
),
|
|
194
|
+
dragLineIndex: itemMeta.index + 1,
|
|
195
|
+
dragLineLevel: reparentLevel,
|
|
196
|
+
};
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
export const getDragTarget = (
|
|
156
200
|
e: any,
|
|
157
201
|
item: ItemInstance<any>,
|
|
158
202
|
tree: TreeInstance<any>,
|
|
159
203
|
canReorder = tree.getConfig().canReorder,
|
|
160
|
-
):
|
|
161
|
-
const draggedItems = tree.getState().dnd?.draggedItems
|
|
204
|
+
): DragTarget<any> => {
|
|
205
|
+
const draggedItems = tree.getState().dnd?.draggedItems;
|
|
162
206
|
const itemMeta = item.getItemMeta();
|
|
163
207
|
const parent = item.getParent();
|
|
164
|
-
const itemTarget:
|
|
165
|
-
|
|
166
|
-
childIndex: null,
|
|
167
|
-
insertionIndex: null,
|
|
168
|
-
dragLineIndex: null,
|
|
169
|
-
dragLineLevel: null,
|
|
170
|
-
};
|
|
171
|
-
const parentTarget: DropTarget<any> | null = parent
|
|
172
|
-
? {
|
|
173
|
-
item: parent,
|
|
174
|
-
childIndex: null,
|
|
175
|
-
insertionIndex: null,
|
|
176
|
-
dragLineIndex: null,
|
|
177
|
-
dragLineLevel: null,
|
|
178
|
-
}
|
|
179
|
-
: null;
|
|
208
|
+
const itemTarget: DragTarget<any> = { item };
|
|
209
|
+
const parentTarget: DragTarget<any> | null = parent ? { item: parent } : null;
|
|
180
210
|
const canBecomeSibling =
|
|
181
211
|
parentTarget && canDrop(e.dataTransfer, parentTarget, tree);
|
|
182
212
|
|
|
@@ -193,8 +223,8 @@ export const getDropTarget = (
|
|
|
193
223
|
}
|
|
194
224
|
|
|
195
225
|
if (!canReorder && parent && !canBecomeSibling) {
|
|
196
|
-
// TODO! this breaks in story DND/Can Drop. Maybe move this logic into a composable
|
|
197
|
-
return
|
|
226
|
+
// TODO! this breaks in story DND/Can Drop. Maybe move this logic into a composable DragTargetStrategy[] ?
|
|
227
|
+
return getDragTarget(e, parent, tree, false);
|
|
198
228
|
}
|
|
199
229
|
|
|
200
230
|
if (!parent) {
|
|
@@ -207,47 +237,27 @@ export const getDropTarget = (
|
|
|
207
237
|
}
|
|
208
238
|
|
|
209
239
|
if (!canBecomeSibling) {
|
|
210
|
-
return
|
|
240
|
+
return getDragTarget(e, parent, tree, false);
|
|
211
241
|
}
|
|
212
242
|
|
|
213
243
|
if (placement.type === PlacementType.Reparent) {
|
|
214
|
-
|
|
215
|
-
const targetItemAbove = getNthParent(item, placement.reparentLevel); // .getItemBelow()!;
|
|
216
|
-
const targetIndex = targetItemAbove.getIndexInParent() + 1;
|
|
217
|
-
|
|
218
|
-
// TODO possibly count items dragged out above the new target
|
|
219
|
-
|
|
220
|
-
return {
|
|
221
|
-
item: reparentedTarget,
|
|
222
|
-
childIndex: targetIndex,
|
|
223
|
-
insertionIndex: targetIndex,
|
|
224
|
-
dragLineIndex: itemMeta.index + 1,
|
|
225
|
-
dragLineLevel: placement.reparentLevel,
|
|
226
|
-
};
|
|
244
|
+
return getReparentTarget(item, placement.reparentLevel, draggedItems);
|
|
227
245
|
}
|
|
228
246
|
|
|
229
247
|
const maybeAddOneForBelow =
|
|
230
248
|
placement.type === PlacementType.ReorderAbove ? 0 : 1;
|
|
231
249
|
const childIndex = item.getIndexInParent() + maybeAddOneForBelow;
|
|
232
250
|
|
|
233
|
-
const numberOfDragItemsBeforeTarget =
|
|
234
|
-
parent
|
|
235
|
-
.getChildren()
|
|
236
|
-
.slice(0, childIndex)
|
|
237
|
-
.reduce(
|
|
238
|
-
(counter, child) =>
|
|
239
|
-
child && draggedItems?.some((i) => i.getId() === child.getId())
|
|
240
|
-
? ++counter
|
|
241
|
-
: counter,
|
|
242
|
-
0,
|
|
243
|
-
) ?? 0;
|
|
244
|
-
|
|
245
251
|
return {
|
|
246
252
|
item: parent,
|
|
247
253
|
dragLineIndex: itemMeta.index + maybeAddOneForBelow,
|
|
248
254
|
dragLineLevel: itemMeta.level,
|
|
249
255
|
childIndex,
|
|
250
256
|
// TODO performance could be improved by computing this only when dragcode changed
|
|
251
|
-
insertionIndex:
|
|
257
|
+
insertionIndex: getInsertionIndex(
|
|
258
|
+
parent.getChildren(),
|
|
259
|
+
childIndex,
|
|
260
|
+
draggedItems,
|
|
261
|
+
),
|
|
252
262
|
};
|
|
253
263
|
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { FeatureImplementation } from "../../types/core";
|
|
2
|
-
import { poll } from "../../utils";
|
|
3
2
|
|
|
4
3
|
export const expandAllFeature: FeatureImplementation = {
|
|
5
4
|
key: "expand-all",
|
|
@@ -27,22 +26,47 @@ export const expandAllFeature: FeatureImplementation = {
|
|
|
27
26
|
}
|
|
28
27
|
|
|
29
28
|
item.expand();
|
|
30
|
-
await
|
|
29
|
+
await tree.waitForItemChildrenLoaded(item.getId());
|
|
31
30
|
await Promise.all(
|
|
32
31
|
item.getChildren().map(async (child) => {
|
|
33
|
-
await
|
|
34
|
-
() => !tree.getState().loadingItems.includes(child.getId()),
|
|
35
|
-
);
|
|
32
|
+
await tree.waitForItemChildrenLoaded(item.getId());
|
|
36
33
|
await child?.expandAll(cancelToken);
|
|
37
34
|
}),
|
|
38
35
|
);
|
|
39
36
|
},
|
|
40
37
|
|
|
41
38
|
collapseAll: ({ item }) => {
|
|
39
|
+
if (!item.isExpanded()) return;
|
|
42
40
|
for (const child of item.getChildren()) {
|
|
43
41
|
child?.collapseAll();
|
|
44
42
|
}
|
|
45
43
|
item.collapse();
|
|
46
44
|
},
|
|
47
45
|
},
|
|
46
|
+
|
|
47
|
+
hotkeys: {
|
|
48
|
+
expandSelected: {
|
|
49
|
+
hotkey: "Control+Shift+Plus",
|
|
50
|
+
handler: async (_, tree) => {
|
|
51
|
+
const cancelToken = { current: false };
|
|
52
|
+
const cancelHandler = (e: KeyboardEvent) => {
|
|
53
|
+
if (e.key === "Escape") {
|
|
54
|
+
cancelToken.current = true;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
document.addEventListener("keydown", cancelHandler);
|
|
58
|
+
await Promise.all(
|
|
59
|
+
tree.getSelectedItems().map((item) => item.expandAll(cancelToken)),
|
|
60
|
+
);
|
|
61
|
+
document.removeEventListener("keydown", cancelHandler);
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
collapseSelected: {
|
|
66
|
+
hotkey: "Control+Shift+-",
|
|
67
|
+
handler: (_, tree) => {
|
|
68
|
+
tree.getSelectedItems().forEach((item) => item.collapseAll());
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
48
72
|
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export interface ExpandAllDataRef {}
|
|
2
|
+
|
|
1
3
|
export type ExpandAllFeatureDef = {
|
|
2
4
|
state: {};
|
|
3
5
|
config: {};
|
|
@@ -9,5 +11,5 @@ export type ExpandAllFeatureDef = {
|
|
|
9
11
|
expandAll: (cancelToken?: { current: boolean }) => Promise<void>;
|
|
10
12
|
collapseAll: () => void;
|
|
11
13
|
};
|
|
12
|
-
hotkeys:
|
|
14
|
+
hotkeys: "expandSelected" | "collapseSelected";
|
|
13
15
|
};
|
|
@@ -8,6 +8,8 @@ import { HotkeyConfig, HotkeysCoreDataRef } from "./types";
|
|
|
8
8
|
const specialKeys: Record<string, RegExp> = {
|
|
9
9
|
Letter: /^[a-z]$/,
|
|
10
10
|
LetterOrNumber: /^[a-z0-9]$/,
|
|
11
|
+
Plus: /^\+$/,
|
|
12
|
+
Space: /^ $/,
|
|
11
13
|
};
|
|
12
14
|
|
|
13
15
|
const testHotkeyMatch = (
|
|
@@ -46,6 +48,7 @@ export const hotkeysCoreFeature: FeatureImplementation = {
|
|
|
46
48
|
data.current.pressedKeys ??= new Set();
|
|
47
49
|
const newMatch = !data.current.pressedKeys.has(e.key);
|
|
48
50
|
data.current.pressedKeys.add(e.key);
|
|
51
|
+
console.log("HOTKEYS", data.current.pressedKeys);
|
|
49
52
|
|
|
50
53
|
const hotkeyName = findHotkeyMatch(
|
|
51
54
|
data.current.pressedKeys,
|
|
@@ -71,6 +74,7 @@ export const hotkeysCoreFeature: FeatureImplementation = {
|
|
|
71
74
|
if (hotkeyConfig.preventDefault) e.preventDefault();
|
|
72
75
|
|
|
73
76
|
hotkeyConfig.handler(e, tree as any);
|
|
77
|
+
tree.getConfig().onTreeHotkey?.(hotkeyName, e);
|
|
74
78
|
};
|
|
75
79
|
|
|
76
80
|
const keyup = (e: KeyboardEvent) => {
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CustomHotkeysConfig,
|
|
3
|
-
ItemInstance,
|
|
4
|
-
TreeInstance,
|
|
5
|
-
} from "../../types/core";
|
|
1
|
+
import { CustomHotkeysConfig, TreeInstance } from "../../types/core";
|
|
6
2
|
|
|
7
3
|
export interface HotkeyConfig<T> {
|
|
8
4
|
hotkey: string;
|
|
@@ -13,22 +9,17 @@ export interface HotkeyConfig<T> {
|
|
|
13
9
|
handler: (e: KeyboardEvent, tree: TreeInstance<T>) => void;
|
|
14
10
|
}
|
|
15
11
|
|
|
16
|
-
export
|
|
12
|
+
export interface HotkeysCoreDataRef {
|
|
17
13
|
keydownHandler?: (e: KeyboardEvent) => void;
|
|
18
14
|
keyupHandler?: (e: KeyboardEvent) => void;
|
|
19
15
|
pressedKeys: Set<string>;
|
|
20
|
-
}
|
|
16
|
+
}
|
|
21
17
|
|
|
22
18
|
export type HotkeysCoreFeatureDef<T> = {
|
|
23
19
|
state: {};
|
|
24
20
|
config: {
|
|
25
21
|
hotkeys?: CustomHotkeysConfig<T>;
|
|
26
|
-
onTreeHotkey?: (name: string,
|
|
27
|
-
onItemHotkey?: (
|
|
28
|
-
name: string,
|
|
29
|
-
item: ItemInstance<T>,
|
|
30
|
-
element: HTMLElement,
|
|
31
|
-
) => void;
|
|
22
|
+
onTreeHotkey?: (name: string, e: KeyboardEvent) => void;
|
|
32
23
|
};
|
|
33
24
|
treeInstance: {};
|
|
34
25
|
itemInstance: {};
|