@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
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { ItemInstance, SetStateFn } from "../../types/core";
|
|
2
|
-
export
|
|
2
|
+
export interface DndDataRef {
|
|
3
3
|
lastDragCode?: string;
|
|
4
4
|
lastAllowDrop?: boolean;
|
|
5
|
-
}
|
|
6
|
-
export
|
|
5
|
+
}
|
|
6
|
+
export interface DndState<T> {
|
|
7
7
|
draggedItems?: ItemInstance<T>[];
|
|
8
8
|
draggingOverItem?: ItemInstance<T>;
|
|
9
|
-
dragTarget?:
|
|
10
|
-
}
|
|
11
|
-
export
|
|
9
|
+
dragTarget?: DragTarget<T>;
|
|
10
|
+
}
|
|
11
|
+
export interface DragLineData {
|
|
12
12
|
indent: number;
|
|
13
13
|
top: number;
|
|
14
14
|
left: number;
|
|
15
15
|
width: number;
|
|
16
|
-
}
|
|
17
|
-
export type
|
|
16
|
+
}
|
|
17
|
+
export type DragTarget<T> = {
|
|
18
18
|
item: ItemInstance<T>;
|
|
19
19
|
childIndex: number;
|
|
20
20
|
insertionIndex: number;
|
|
@@ -22,12 +22,8 @@ export type DropTarget<T> = {
|
|
|
22
22
|
dragLineLevel: number;
|
|
23
23
|
} | {
|
|
24
24
|
item: ItemInstance<T>;
|
|
25
|
-
childIndex: null;
|
|
26
|
-
insertionIndex: null;
|
|
27
|
-
dragLineIndex: null;
|
|
28
|
-
dragLineLevel: null;
|
|
29
25
|
};
|
|
30
|
-
export declare enum
|
|
26
|
+
export declare enum DragTargetPosition {
|
|
31
27
|
Top = "top",
|
|
32
28
|
Bottom = "bottom",
|
|
33
29
|
Item = "item"
|
|
@@ -43,35 +39,27 @@ export type DragAndDropFeatureDef<T> = {
|
|
|
43
39
|
* If `canReorder` is `false`, this is ignored. */
|
|
44
40
|
reorderAreaPercentage?: number;
|
|
45
41
|
canReorder?: boolean;
|
|
46
|
-
isItemDraggable?: (item: ItemInstance<T>) => boolean;
|
|
47
42
|
canDrag?: (items: ItemInstance<T>[]) => boolean;
|
|
48
|
-
canDrop?: (items: ItemInstance<T>[], target:
|
|
43
|
+
canDrop?: (items: ItemInstance<T>[], target: DragTarget<T>) => boolean;
|
|
49
44
|
indent?: number;
|
|
50
45
|
createForeignDragObject?: (items: ItemInstance<T>[]) => {
|
|
51
46
|
format: string;
|
|
52
47
|
data: any;
|
|
53
48
|
};
|
|
54
|
-
canDropForeignDragObject?: (dataTransfer: DataTransfer, target:
|
|
55
|
-
onDrop?: (items: ItemInstance<T>[], target:
|
|
56
|
-
onDropForeignDragObject?: (dataTransfer: DataTransfer, target:
|
|
57
|
-
/** Runs in the onDragEnd event, if `ev.dataTransfer.dropEffect` is not `none`, i.e. the drop
|
|
58
|
-
* was not aborted. No target is provided as parameter since the target may be a foreign drop target.
|
|
59
|
-
* This is useful to seperate out the logic to move dragged items out of their previous parents.
|
|
60
|
-
* Use `onDrop` to handle drop-related logic.
|
|
61
|
-
*
|
|
62
|
-
* This ignores the `canDrop` handler, since the drop target is unknown in this handler.
|
|
63
|
-
*/
|
|
49
|
+
canDropForeignDragObject?: (dataTransfer: DataTransfer, target: DragTarget<T>) => boolean;
|
|
50
|
+
onDrop?: (items: ItemInstance<T>[], target: DragTarget<T>) => void | Promise<void>;
|
|
51
|
+
onDropForeignDragObject?: (dataTransfer: DataTransfer, target: DragTarget<T>) => void | Promise<void>;
|
|
64
52
|
onCompleteForeignDrop?: (items: ItemInstance<T>[]) => void;
|
|
65
53
|
};
|
|
66
54
|
treeInstance: {
|
|
67
|
-
|
|
55
|
+
getDragTarget: () => DragTarget<T> | null;
|
|
68
56
|
getDragLineData: () => DragLineData | null;
|
|
69
57
|
getDragLineStyle: (topOffset?: number, leftOffset?: number) => Record<string, any>;
|
|
70
58
|
};
|
|
71
59
|
itemInstance: {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
60
|
+
isDragTarget: () => boolean;
|
|
61
|
+
isDragTargetAbove: () => boolean;
|
|
62
|
+
isDragTargetBelow: () => boolean;
|
|
75
63
|
isDraggingOver: () => boolean;
|
|
76
64
|
};
|
|
77
65
|
hotkeys: never;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export var
|
|
2
|
-
(function (
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
})(
|
|
1
|
+
export var DragTargetPosition;
|
|
2
|
+
(function (DragTargetPosition) {
|
|
3
|
+
DragTargetPosition["Top"] = "top";
|
|
4
|
+
DragTargetPosition["Bottom"] = "bottom";
|
|
5
|
+
DragTargetPosition["Item"] = "item";
|
|
6
|
+
})(DragTargetPosition || (DragTargetPosition = {}));
|
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
import { ItemInstance, TreeInstance } from "../../types/core";
|
|
2
|
-
import {
|
|
3
|
-
export declare
|
|
2
|
+
import { DragTarget } from "./types";
|
|
3
|
+
export declare enum ItemDropCategory {
|
|
4
|
+
Item = 0,
|
|
5
|
+
ExpandedFolder = 1,
|
|
6
|
+
LastInGroup = 2
|
|
7
|
+
}
|
|
8
|
+
export declare const isOrderedDragTarget: <T>(dragTarget: DragTarget<T>) => dragTarget is {
|
|
9
|
+
item: ItemInstance<T>;
|
|
10
|
+
childIndex: number;
|
|
11
|
+
insertionIndex: number;
|
|
12
|
+
dragLineIndex: number;
|
|
13
|
+
dragLineLevel: number;
|
|
14
|
+
};
|
|
15
|
+
export declare const canDrop: (dataTransfer: DataTransfer | null, target: DragTarget<any>, tree: TreeInstance<any>) => boolean;
|
|
16
|
+
export declare const getItemDropCategory: (item: ItemInstance<any>) => ItemDropCategory;
|
|
17
|
+
export declare const getInsertionIndex: <T>(children: ItemInstance<T>[], childIndex: number, draggedItems: ItemInstance<T>[] | undefined) => number;
|
|
4
18
|
export declare const getDragCode: (e: any, item: ItemInstance<any>, tree: TreeInstance<any>) => string;
|
|
5
|
-
|
|
19
|
+
/** @param item refers to the bottom-most item of the container, at which bottom is being reparented on (e.g. root-1-2-6) */
|
|
20
|
+
export declare const getReparentTarget: <T>(item: ItemInstance<T>, reparentLevel: number, draggedItems: ItemInstance<T>[] | undefined) => {
|
|
21
|
+
item: ItemInstance<any>;
|
|
22
|
+
childIndex: number;
|
|
23
|
+
insertionIndex: number;
|
|
24
|
+
dragLineIndex: number;
|
|
25
|
+
dragLineLevel: number;
|
|
26
|
+
};
|
|
27
|
+
export declare const getDragTarget: (e: any, item: ItemInstance<any>, tree: TreeInstance<any>, canReorder?: boolean | undefined) => DragTarget<any>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var ItemDropCategory;
|
|
1
|
+
export var ItemDropCategory;
|
|
2
2
|
(function (ItemDropCategory) {
|
|
3
3
|
ItemDropCategory[ItemDropCategory["Item"] = 0] = "Item";
|
|
4
4
|
ItemDropCategory[ItemDropCategory["ExpandedFolder"] = 1] = "ExpandedFolder";
|
|
@@ -11,8 +11,9 @@ var PlacementType;
|
|
|
11
11
|
PlacementType[PlacementType["MakeChild"] = 2] = "MakeChild";
|
|
12
12
|
PlacementType[PlacementType["Reparent"] = 3] = "Reparent";
|
|
13
13
|
})(PlacementType || (PlacementType = {}));
|
|
14
|
+
export const isOrderedDragTarget = (dragTarget) => "childIndex" in dragTarget;
|
|
14
15
|
export const canDrop = (dataTransfer, target, tree) => {
|
|
15
|
-
var _a, _b, _c
|
|
16
|
+
var _a, _b, _c;
|
|
16
17
|
const draggedItems = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggedItems;
|
|
17
18
|
const config = tree.getConfig();
|
|
18
19
|
if (draggedItems && !((_c = (_b = config.canDrop) === null || _b === void 0 ? void 0 : _b.call(config, draggedItems, target)) !== null && _c !== void 0 ? _c : true)) {
|
|
@@ -25,23 +26,33 @@ export const canDrop = (dataTransfer, target, tree) => {
|
|
|
25
26
|
}
|
|
26
27
|
if (!draggedItems &&
|
|
27
28
|
dataTransfer &&
|
|
28
|
-
|
|
29
|
+
config.canDropForeignDragObject &&
|
|
30
|
+
!config.canDropForeignDragObject(dataTransfer, target)) {
|
|
29
31
|
return false;
|
|
30
32
|
}
|
|
31
33
|
return true;
|
|
32
34
|
};
|
|
33
|
-
const getItemDropCategory = (item) => {
|
|
35
|
+
export const getItemDropCategory = (item) => {
|
|
34
36
|
if (item.isExpanded()) {
|
|
35
37
|
return ItemDropCategory.ExpandedFolder;
|
|
36
38
|
}
|
|
37
39
|
const parent = item.getParent();
|
|
38
|
-
if (parent && item.getIndexInParent() ===
|
|
40
|
+
if (parent && item.getIndexInParent() === item.getItemMeta().setSize - 1) {
|
|
39
41
|
return ItemDropCategory.LastInGroup;
|
|
40
42
|
}
|
|
41
43
|
return ItemDropCategory.Item;
|
|
42
44
|
};
|
|
45
|
+
export const getInsertionIndex = (children, childIndex, draggedItems) => {
|
|
46
|
+
var _a;
|
|
47
|
+
const numberOfDragItemsBeforeTarget = (_a = children
|
|
48
|
+
.slice(0, childIndex)
|
|
49
|
+
.reduce((counter, child) => child && (draggedItems === null || draggedItems === void 0 ? void 0 : draggedItems.some((i) => i.getId() === child.getId()))
|
|
50
|
+
? ++counter
|
|
51
|
+
: counter, 0)) !== null && _a !== void 0 ? _a : 0;
|
|
52
|
+
return childIndex - numberOfDragItemsBeforeTarget;
|
|
53
|
+
};
|
|
43
54
|
const getTargetPlacement = (e, item, tree, canMakeChild) => {
|
|
44
|
-
var _a, _b, _c;
|
|
55
|
+
var _a, _b, _c, _d, _e;
|
|
45
56
|
const config = tree.getConfig();
|
|
46
57
|
if (!config.canReorder) {
|
|
47
58
|
return canMakeChild
|
|
@@ -49,8 +60,8 @@ const getTargetPlacement = (e, item, tree, canMakeChild) => {
|
|
|
49
60
|
: { type: PlacementType.ReorderBelow };
|
|
50
61
|
}
|
|
51
62
|
const bb = (_a = item.getElement()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
|
|
52
|
-
const topPercent = bb ? (e.
|
|
53
|
-
const leftPixels = bb ? e.
|
|
63
|
+
const topPercent = bb ? (e.clientY - bb.top) / bb.height : 0.5;
|
|
64
|
+
const leftPixels = bb ? e.clientX - bb.left : 0;
|
|
54
65
|
const targetDropCategory = getItemDropCategory(item);
|
|
55
66
|
const reorderAreaPercentage = !canMakeChild
|
|
56
67
|
? 0.5
|
|
@@ -70,9 +81,10 @@ const getTargetPlacement = (e, item, tree, canMakeChild) => {
|
|
|
70
81
|
if (topPercent < 0.5) {
|
|
71
82
|
return { type: PlacementType.ReorderAbove };
|
|
72
83
|
}
|
|
84
|
+
const minLevel = (_e = (_d = item.getItemBelow()) === null || _d === void 0 ? void 0 : _d.getItemMeta().level) !== null && _e !== void 0 ? _e : 0;
|
|
73
85
|
return {
|
|
74
86
|
type: PlacementType.Reparent,
|
|
75
|
-
reparentLevel: Math.floor(leftPixels / indent),
|
|
87
|
+
reparentLevel: Math.max(minLevel, Math.floor(leftPixels / indent)),
|
|
76
88
|
};
|
|
77
89
|
}
|
|
78
90
|
// if not at left of item area, treat as if it was a normal item
|
|
@@ -100,27 +112,27 @@ const getNthParent = (item, n) => {
|
|
|
100
112
|
}
|
|
101
113
|
return getNthParent(item.getParent(), n);
|
|
102
114
|
};
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const draggedItems = (_b = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggedItems) !== null && _b !== void 0 ? _b : [];
|
|
115
|
+
/** @param item refers to the bottom-most item of the container, at which bottom is being reparented on (e.g. root-1-2-6) */
|
|
116
|
+
export const getReparentTarget = (item, reparentLevel, draggedItems) => {
|
|
106
117
|
const itemMeta = item.getItemMeta();
|
|
107
|
-
const
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
118
|
+
const reparentedTarget = getNthParent(item, reparentLevel - 1);
|
|
119
|
+
const targetItemAbove = getNthParent(item, reparentLevel); // .getItemBelow()!;
|
|
120
|
+
const targetIndex = targetItemAbove.getIndexInParent() + 1;
|
|
121
|
+
return {
|
|
122
|
+
item: reparentedTarget,
|
|
123
|
+
childIndex: targetIndex,
|
|
124
|
+
insertionIndex: getInsertionIndex(reparentedTarget.getChildren(), targetIndex, draggedItems),
|
|
125
|
+
dragLineIndex: itemMeta.index + 1,
|
|
126
|
+
dragLineLevel: reparentLevel,
|
|
114
127
|
};
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
: null;
|
|
128
|
+
};
|
|
129
|
+
export const getDragTarget = (e, item, tree, canReorder = tree.getConfig().canReorder) => {
|
|
130
|
+
var _a;
|
|
131
|
+
const draggedItems = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggedItems;
|
|
132
|
+
const itemMeta = item.getItemMeta();
|
|
133
|
+
const parent = item.getParent();
|
|
134
|
+
const itemTarget = { item };
|
|
135
|
+
const parentTarget = parent ? { item: parent } : null;
|
|
124
136
|
const canBecomeSibling = parentTarget && canDrop(e.dataTransfer, parentTarget, tree);
|
|
125
137
|
const canMakeChild = canDrop(e.dataTransfer, itemTarget, tree);
|
|
126
138
|
const placement = getTargetPlacement(e, item, tree, canMakeChild);
|
|
@@ -131,8 +143,8 @@ export const getDropTarget = (e, item, tree, canReorder = tree.getConfig().canRe
|
|
|
131
143
|
return parentTarget;
|
|
132
144
|
}
|
|
133
145
|
if (!canReorder && parent && !canBecomeSibling) {
|
|
134
|
-
// TODO! this breaks in story DND/Can Drop. Maybe move this logic into a composable
|
|
135
|
-
return
|
|
146
|
+
// TODO! this breaks in story DND/Can Drop. Maybe move this logic into a composable DragTargetStrategy[] ?
|
|
147
|
+
return getDragTarget(e, parent, tree, false);
|
|
136
148
|
}
|
|
137
149
|
if (!parent) {
|
|
138
150
|
// Shouldn't happen, but if dropped "next" to root item, just drop it inside
|
|
@@ -142,35 +154,19 @@ export const getDropTarget = (e, item, tree, canReorder = tree.getConfig().canRe
|
|
|
142
154
|
return itemTarget;
|
|
143
155
|
}
|
|
144
156
|
if (!canBecomeSibling) {
|
|
145
|
-
return
|
|
157
|
+
return getDragTarget(e, parent, tree, false);
|
|
146
158
|
}
|
|
147
159
|
if (placement.type === PlacementType.Reparent) {
|
|
148
|
-
|
|
149
|
-
const targetItemAbove = getNthParent(item, placement.reparentLevel); // .getItemBelow()!;
|
|
150
|
-
const targetIndex = targetItemAbove.getIndexInParent() + 1;
|
|
151
|
-
// TODO possibly count items dragged out above the new target
|
|
152
|
-
return {
|
|
153
|
-
item: reparentedTarget,
|
|
154
|
-
childIndex: targetIndex,
|
|
155
|
-
insertionIndex: targetIndex,
|
|
156
|
-
dragLineIndex: itemMeta.index + 1,
|
|
157
|
-
dragLineLevel: placement.reparentLevel,
|
|
158
|
-
};
|
|
160
|
+
return getReparentTarget(item, placement.reparentLevel, draggedItems);
|
|
159
161
|
}
|
|
160
162
|
const maybeAddOneForBelow = placement.type === PlacementType.ReorderAbove ? 0 : 1;
|
|
161
163
|
const childIndex = item.getIndexInParent() + maybeAddOneForBelow;
|
|
162
|
-
const numberOfDragItemsBeforeTarget = (_c = parent
|
|
163
|
-
.getChildren()
|
|
164
|
-
.slice(0, childIndex)
|
|
165
|
-
.reduce((counter, child) => child && (draggedItems === null || draggedItems === void 0 ? void 0 : draggedItems.some((i) => i.getId() === child.getId()))
|
|
166
|
-
? ++counter
|
|
167
|
-
: counter, 0)) !== null && _c !== void 0 ? _c : 0;
|
|
168
164
|
return {
|
|
169
165
|
item: parent,
|
|
170
166
|
dragLineIndex: itemMeta.index + maybeAddOneForBelow,
|
|
171
167
|
dragLineLevel: itemMeta.level,
|
|
172
168
|
childIndex,
|
|
173
169
|
// TODO performance could be improved by computing this only when dragcode changed
|
|
174
|
-
insertionIndex: childIndex
|
|
170
|
+
insertionIndex: getInsertionIndex(parent.getChildren(), childIndex, draggedItems),
|
|
175
171
|
};
|
|
176
172
|
};
|
|
@@ -7,7 +7,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import { poll } from "../../utils";
|
|
11
10
|
export const expandAllFeature = {
|
|
12
11
|
key: "expand-all",
|
|
13
12
|
treeInstance: {
|
|
@@ -28,17 +27,41 @@ export const expandAllFeature = {
|
|
|
28
27
|
return;
|
|
29
28
|
}
|
|
30
29
|
item.expand();
|
|
31
|
-
yield
|
|
30
|
+
yield tree.waitForItemChildrenLoaded(item.getId());
|
|
32
31
|
yield Promise.all(item.getChildren().map((child) => __awaiter(void 0, void 0, void 0, function* () {
|
|
33
|
-
yield
|
|
32
|
+
yield tree.waitForItemChildrenLoaded(item.getId());
|
|
34
33
|
yield (child === null || child === void 0 ? void 0 : child.expandAll(cancelToken));
|
|
35
34
|
})));
|
|
36
35
|
}),
|
|
37
36
|
collapseAll: ({ item }) => {
|
|
37
|
+
if (!item.isExpanded())
|
|
38
|
+
return;
|
|
38
39
|
for (const child of item.getChildren()) {
|
|
39
40
|
child === null || child === void 0 ? void 0 : child.collapseAll();
|
|
40
41
|
}
|
|
41
42
|
item.collapse();
|
|
42
43
|
},
|
|
43
44
|
},
|
|
45
|
+
hotkeys: {
|
|
46
|
+
expandSelected: {
|
|
47
|
+
hotkey: "Control+Shift+Plus",
|
|
48
|
+
handler: (_, tree) => __awaiter(void 0, void 0, void 0, function* () {
|
|
49
|
+
const cancelToken = { current: false };
|
|
50
|
+
const cancelHandler = (e) => {
|
|
51
|
+
if (e.key === "Escape") {
|
|
52
|
+
cancelToken.current = true;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
document.addEventListener("keydown", cancelHandler);
|
|
56
|
+
yield Promise.all(tree.getSelectedItems().map((item) => item.expandAll(cancelToken)));
|
|
57
|
+
document.removeEventListener("keydown", cancelHandler);
|
|
58
|
+
}),
|
|
59
|
+
},
|
|
60
|
+
collapseSelected: {
|
|
61
|
+
hotkey: "Control+Shift+-",
|
|
62
|
+
handler: (_, tree) => {
|
|
63
|
+
tree.getSelectedItems().forEach((item) => item.collapseAll());
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
},
|
|
44
67
|
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export interface ExpandAllDataRef {
|
|
2
|
+
}
|
|
1
3
|
export type ExpandAllFeatureDef = {
|
|
2
4
|
state: {};
|
|
3
5
|
config: {};
|
|
@@ -13,5 +15,5 @@ export type ExpandAllFeatureDef = {
|
|
|
13
15
|
}) => Promise<void>;
|
|
14
16
|
collapseAll: () => void;
|
|
15
17
|
};
|
|
16
|
-
hotkeys:
|
|
18
|
+
hotkeys: "expandSelected" | "collapseSelected";
|
|
17
19
|
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
const specialKeys = {
|
|
2
2
|
Letter: /^[a-z]$/,
|
|
3
3
|
LetterOrNumber: /^[a-z0-9]$/,
|
|
4
|
+
Plus: /^\+$/,
|
|
5
|
+
Space: /^ $/,
|
|
4
6
|
};
|
|
5
7
|
const testHotkeyMatch = (pressedKeys, tree, hotkey) => {
|
|
6
8
|
const supposedKeys = hotkey.hotkey.split("+");
|
|
@@ -20,11 +22,12 @@ export const hotkeysCoreFeature = {
|
|
|
20
22
|
onTreeMount: (tree, element) => {
|
|
21
23
|
const data = tree.getDataRef();
|
|
22
24
|
const keydown = (e) => {
|
|
23
|
-
var _a, _b;
|
|
24
|
-
var
|
|
25
|
-
(_a = (
|
|
25
|
+
var _a, _b, _c, _d;
|
|
26
|
+
var _e;
|
|
27
|
+
(_a = (_e = data.current).pressedKeys) !== null && _a !== void 0 ? _a : (_e.pressedKeys = new Set());
|
|
26
28
|
const newMatch = !data.current.pressedKeys.has(e.key);
|
|
27
29
|
data.current.pressedKeys.add(e.key);
|
|
30
|
+
console.log("HOTKEYS", data.current.pressedKeys);
|
|
28
31
|
const hotkeyName = findHotkeyMatch(data.current.pressedKeys, tree, tree.getHotkeyPresets(), tree.getConfig().hotkeys);
|
|
29
32
|
if (!hotkeyName)
|
|
30
33
|
return;
|
|
@@ -39,6 +42,7 @@ export const hotkeysCoreFeature = {
|
|
|
39
42
|
if (hotkeyConfig.preventDefault)
|
|
40
43
|
e.preventDefault();
|
|
41
44
|
hotkeyConfig.handler(e, tree);
|
|
45
|
+
(_d = (_c = tree.getConfig()).onTreeHotkey) === null || _d === void 0 ? void 0 : _d.call(_c, hotkeyName, e);
|
|
42
46
|
};
|
|
43
47
|
const keyup = (e) => {
|
|
44
48
|
var _a;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CustomHotkeysConfig,
|
|
1
|
+
import { CustomHotkeysConfig, TreeInstance } from "../../types/core";
|
|
2
2
|
export interface HotkeyConfig<T> {
|
|
3
3
|
hotkey: string;
|
|
4
4
|
canRepeat?: boolean;
|
|
@@ -7,17 +7,16 @@ export interface HotkeyConfig<T> {
|
|
|
7
7
|
preventDefault?: boolean;
|
|
8
8
|
handler: (e: KeyboardEvent, tree: TreeInstance<T>) => void;
|
|
9
9
|
}
|
|
10
|
-
export
|
|
10
|
+
export interface HotkeysCoreDataRef {
|
|
11
11
|
keydownHandler?: (e: KeyboardEvent) => void;
|
|
12
12
|
keyupHandler?: (e: KeyboardEvent) => void;
|
|
13
13
|
pressedKeys: Set<string>;
|
|
14
|
-
}
|
|
14
|
+
}
|
|
15
15
|
export type HotkeysCoreFeatureDef<T> = {
|
|
16
16
|
state: {};
|
|
17
17
|
config: {
|
|
18
18
|
hotkeys?: CustomHotkeysConfig<T>;
|
|
19
|
-
onTreeHotkey?: (name: string,
|
|
20
|
-
onItemHotkey?: (name: string, item: ItemInstance<T>, element: HTMLElement) => void;
|
|
19
|
+
onTreeHotkey?: (name: string, e: KeyboardEvent) => void;
|
|
21
20
|
};
|
|
22
21
|
treeInstance: {};
|
|
23
22
|
itemInstance: {};
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { ItemDropCategory, canDrop, getInsertionIndex, getItemDropCategory, getReparentTarget, isOrderedDragTarget, } from "../drag-and-drop/utils";
|
|
11
|
+
import { makeStateUpdater } from "../../utils";
|
|
12
|
+
import { AssistiveDndState } from "./types";
|
|
13
|
+
const getNextDragTarget = (tree, isUp, dragTarget) => {
|
|
14
|
+
var _a, _b, _c, _d;
|
|
15
|
+
const direction = isUp ? 0 : 1;
|
|
16
|
+
const draggedItems = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggedItems;
|
|
17
|
+
// currently hovering between items
|
|
18
|
+
if (isOrderedDragTarget(dragTarget)) {
|
|
19
|
+
const parent = dragTarget.item.getParent();
|
|
20
|
+
const targetedItem = tree.getItems()[dragTarget.dragLineIndex - 1]; // item above dragline
|
|
21
|
+
const targetCategory = targetedItem
|
|
22
|
+
? getItemDropCategory(targetedItem)
|
|
23
|
+
: ItemDropCategory.Item;
|
|
24
|
+
const maxLevel = (_b = targetedItem === null || targetedItem === void 0 ? void 0 : targetedItem.getItemMeta().level) !== null && _b !== void 0 ? _b : 0;
|
|
25
|
+
const minLevel = (_d = (_c = targetedItem === null || targetedItem === void 0 ? void 0 : targetedItem.getItemBelow()) === null || _c === void 0 ? void 0 : _c.getItemMeta().level) !== null && _d !== void 0 ? _d : 0;
|
|
26
|
+
// reparenting
|
|
27
|
+
if (targetCategory === ItemDropCategory.LastInGroup) {
|
|
28
|
+
if (isUp && dragTarget.dragLineLevel < maxLevel) {
|
|
29
|
+
return getReparentTarget(targetedItem, dragTarget.dragLineLevel + 1, draggedItems);
|
|
30
|
+
}
|
|
31
|
+
if (!isUp && dragTarget.dragLineLevel > minLevel && parent) {
|
|
32
|
+
return getReparentTarget(targetedItem, dragTarget.dragLineLevel - 1, draggedItems);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const newIndex = dragTarget.dragLineIndex - 1 + direction;
|
|
36
|
+
const item = tree.getItems()[newIndex];
|
|
37
|
+
return item ? { item } : undefined;
|
|
38
|
+
}
|
|
39
|
+
// moving upwards outside of an open folder
|
|
40
|
+
const targetingExpandedFolder = getItemDropCategory(dragTarget.item) === ItemDropCategory.ExpandedFolder;
|
|
41
|
+
if (targetingExpandedFolder && !isUp) {
|
|
42
|
+
return {
|
|
43
|
+
item: dragTarget.item,
|
|
44
|
+
childIndex: 0,
|
|
45
|
+
insertionIndex: getInsertionIndex(dragTarget.item.getChildren(), 0, draggedItems),
|
|
46
|
+
dragLineIndex: dragTarget.item.getItemMeta().index + direction,
|
|
47
|
+
dragLineLevel: dragTarget.item.getItemMeta().level + 1,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// currently hovering over item
|
|
51
|
+
const childIndex = dragTarget.item.getIndexInParent() + direction;
|
|
52
|
+
return {
|
|
53
|
+
item: dragTarget.item.getParent(),
|
|
54
|
+
childIndex,
|
|
55
|
+
insertionIndex: getInsertionIndex(dragTarget.item.getParent().getChildren(), childIndex, draggedItems),
|
|
56
|
+
dragLineIndex: dragTarget.item.getItemMeta().index + direction,
|
|
57
|
+
dragLineLevel: dragTarget.item.getItemMeta().level,
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
const getNextValidDragTarget = (tree, isUp, previousTarget) => {
|
|
61
|
+
var _a, _b;
|
|
62
|
+
if (previousTarget === void 0) { previousTarget = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.dragTarget; }
|
|
63
|
+
if (!previousTarget)
|
|
64
|
+
return undefined;
|
|
65
|
+
const nextTarget = getNextDragTarget(tree, isUp, previousTarget);
|
|
66
|
+
const dataTransfer = (_b = tree.getDataRef().current.kDndDataTransfer) !== null && _b !== void 0 ? _b : null;
|
|
67
|
+
if (!nextTarget)
|
|
68
|
+
return undefined;
|
|
69
|
+
if (canDrop(dataTransfer, nextTarget, tree)) {
|
|
70
|
+
return nextTarget;
|
|
71
|
+
}
|
|
72
|
+
return getNextValidDragTarget(tree, isUp, nextTarget);
|
|
73
|
+
};
|
|
74
|
+
const updateScroll = (tree) => {
|
|
75
|
+
const state = tree.getState().dnd;
|
|
76
|
+
if (!(state === null || state === void 0 ? void 0 : state.dragTarget) || isOrderedDragTarget(state.dragTarget))
|
|
77
|
+
return;
|
|
78
|
+
state.dragTarget.item.scrollTo({ block: "nearest", inline: "nearest" });
|
|
79
|
+
};
|
|
80
|
+
const initiateDrag = (tree, draggedItems, dataTransfer) => {
|
|
81
|
+
var _a, _b;
|
|
82
|
+
const focusedItem = tree.getFocusedItem();
|
|
83
|
+
const { canDrag } = tree.getConfig();
|
|
84
|
+
if (draggedItems && canDrag && !canDrag(draggedItems)) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (draggedItems) {
|
|
88
|
+
tree.applySubStateUpdate("dnd", { draggedItems });
|
|
89
|
+
// getNextValidDragTarget->canDrop needs the draggedItems in state
|
|
90
|
+
(_b = (_a = tree.getConfig()).onStartKeyboardDrag) === null || _b === void 0 ? void 0 : _b.call(_a, draggedItems);
|
|
91
|
+
}
|
|
92
|
+
else if (dataTransfer) {
|
|
93
|
+
tree.getDataRef().current.kDndDataTransfer = dataTransfer;
|
|
94
|
+
}
|
|
95
|
+
const dragTarget = getNextValidDragTarget(tree, false, {
|
|
96
|
+
item: focusedItem,
|
|
97
|
+
});
|
|
98
|
+
if (!dragTarget)
|
|
99
|
+
return;
|
|
100
|
+
tree.applySubStateUpdate("dnd", {
|
|
101
|
+
draggedItems,
|
|
102
|
+
dragTarget,
|
|
103
|
+
});
|
|
104
|
+
tree.applySubStateUpdate("assistiveDndState", AssistiveDndState.Started);
|
|
105
|
+
updateScroll(tree);
|
|
106
|
+
};
|
|
107
|
+
const moveDragPosition = (tree, isUp) => {
|
|
108
|
+
var _a;
|
|
109
|
+
const dragTarget = getNextValidDragTarget(tree, isUp);
|
|
110
|
+
if (!dragTarget)
|
|
111
|
+
return;
|
|
112
|
+
tree.applySubStateUpdate("dnd", {
|
|
113
|
+
draggedItems: (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggedItems,
|
|
114
|
+
dragTarget,
|
|
115
|
+
});
|
|
116
|
+
tree.applySubStateUpdate("assistiveDndState", AssistiveDndState.Dragging);
|
|
117
|
+
if (!isOrderedDragTarget(dragTarget)) {
|
|
118
|
+
dragTarget.item.setFocused();
|
|
119
|
+
}
|
|
120
|
+
updateScroll(tree);
|
|
121
|
+
};
|
|
122
|
+
export const keyboardDragAndDropFeature = {
|
|
123
|
+
key: "keyboard-drag-and-drop",
|
|
124
|
+
deps: ["drag-and-drop"],
|
|
125
|
+
getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setAssistiveDndState: makeStateUpdater("assistiveDndState", tree) }, defaultConfig)),
|
|
126
|
+
stateHandlerNames: {
|
|
127
|
+
assistiveDndState: "setAssistiveDndState",
|
|
128
|
+
},
|
|
129
|
+
treeInstance: {
|
|
130
|
+
startKeyboardDrag: ({ tree }, draggedItems) => {
|
|
131
|
+
initiateDrag(tree, draggedItems, undefined);
|
|
132
|
+
},
|
|
133
|
+
startKeyboardDragOnForeignObject: ({ tree }, dataTransfer) => {
|
|
134
|
+
initiateDrag(tree, undefined, dataTransfer);
|
|
135
|
+
},
|
|
136
|
+
stopKeyboardDrag: ({ tree }) => {
|
|
137
|
+
tree.getDataRef().current.kDndDataTransfer = undefined;
|
|
138
|
+
tree.applySubStateUpdate("dnd", null);
|
|
139
|
+
tree.applySubStateUpdate("assistiveDndState", AssistiveDndState.None);
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
hotkeys: {
|
|
143
|
+
startDrag: {
|
|
144
|
+
hotkey: "Control+Shift+D",
|
|
145
|
+
preventDefault: true,
|
|
146
|
+
isEnabled: (tree) => !tree.getState().dnd,
|
|
147
|
+
handler: (_, tree) => {
|
|
148
|
+
tree.startKeyboardDrag(tree.getSelectedItems());
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
dragUp: {
|
|
152
|
+
hotkey: "ArrowUp",
|
|
153
|
+
preventDefault: true,
|
|
154
|
+
isEnabled: (tree) => !!tree.getState().dnd,
|
|
155
|
+
handler: (_, tree) => {
|
|
156
|
+
moveDragPosition(tree, true);
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
dragDown: {
|
|
160
|
+
hotkey: "ArrowDown",
|
|
161
|
+
preventDefault: true,
|
|
162
|
+
isEnabled: (tree) => !!tree.getState().dnd,
|
|
163
|
+
handler: (_, tree) => {
|
|
164
|
+
moveDragPosition(tree, false);
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
cancelDrag: {
|
|
168
|
+
hotkey: "Escape",
|
|
169
|
+
isEnabled: (tree) => !!tree.getState().dnd,
|
|
170
|
+
handler: (_, tree) => {
|
|
171
|
+
tree.stopKeyboardDrag();
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
completeDrag: {
|
|
175
|
+
hotkey: "Enter",
|
|
176
|
+
preventDefault: true,
|
|
177
|
+
isEnabled: (tree) => !!tree.getState().dnd,
|
|
178
|
+
handler: (e, tree) => __awaiter(void 0, void 0, void 0, function* () {
|
|
179
|
+
var _a, _b, _c, _d;
|
|
180
|
+
e.stopPropagation();
|
|
181
|
+
// TODO copied from keyboard onDrop, unify them
|
|
182
|
+
const dataRef = tree.getDataRef();
|
|
183
|
+
const target = tree.getDragTarget();
|
|
184
|
+
const dataTransfer = (_a = dataRef.current.kDndDataTransfer) !== null && _a !== void 0 ? _a : null;
|
|
185
|
+
if (!target || !canDrop(dataTransfer, target, tree)) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const config = tree.getConfig();
|
|
189
|
+
const draggedItems = (_b = tree.getState().dnd) === null || _b === void 0 ? void 0 : _b.draggedItems;
|
|
190
|
+
dataRef.current.lastDragCode = undefined;
|
|
191
|
+
tree.applySubStateUpdate("dnd", null);
|
|
192
|
+
if (draggedItems) {
|
|
193
|
+
yield ((_c = config.onDrop) === null || _c === void 0 ? void 0 : _c.call(config, draggedItems, target));
|
|
194
|
+
tree.getItemInstance(draggedItems[0].getId()).setFocused();
|
|
195
|
+
}
|
|
196
|
+
else if (dataTransfer) {
|
|
197
|
+
yield ((_d = config.onDropForeignDragObject) === null || _d === void 0 ? void 0 : _d.call(config, dataTransfer, target));
|
|
198
|
+
}
|
|
199
|
+
tree.applySubStateUpdate("assistiveDndState", AssistiveDndState.Completed);
|
|
200
|
+
}),
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
};
|