@headless-tree/core 0.0.0-20250322153940 → 0.0.0-20250330232313
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 +2 -1
- package/lib/cjs/features/drag-and-drop/feature.js +11 -11
- package/lib/cjs/features/drag-and-drop/types.d.ts +11 -11
- 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 +42 -30
- package/lib/cjs/features/hotkeys-core/feature.js +1 -0
- 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/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-expect.d.ts +5 -3
- package/lib/cjs/test-utils/test-tree-expect.js +3 -0
- 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/insert-items-at-target.d.ts +2 -2
- package/lib/esm/features/drag-and-drop/feature.js +12 -12
- package/lib/esm/features/drag-and-drop/types.d.ts +11 -11
- 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 +37 -28
- package/lib/esm/features/hotkeys-core/feature.js +1 -0
- 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/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-expect.d.ts +5 -3
- package/lib/esm/test-utils/test-tree-expect.js +3 -0
- 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/insert-items-at-target.d.ts +2 -2
- package/package.json +1 -1
- package/src/features/drag-and-drop/drag-and-drop.spec.ts +6 -6
- package/src/features/drag-and-drop/feature.ts +12 -12
- package/src/features/drag-and-drop/types.ts +11 -11
- package/src/features/drag-and-drop/utils.ts +64 -39
- package/src/features/hotkeys-core/feature.ts +1 -0
- 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/index.ts +2 -0
- package/src/mddocs-entry.ts +16 -0
- package/src/test-utils/test-tree-expect.ts +7 -2
- package/src/types/core.ts +2 -0
- package/src/utilities/create-on-drop-handler.ts +2 -2
- package/src/utilities/insert-items-at-target.ts +2 -2
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { DragEvent } from "react";
|
|
2
2
|
import { TestTree } from "./test-tree";
|
|
3
|
-
import {
|
|
3
|
+
import { DragTarget } from "../features/drag-and-drop/types";
|
|
4
|
+
import { TreeState } from "../types/core";
|
|
4
5
|
export declare class TestTreeExpect<T> {
|
|
5
6
|
private tree;
|
|
6
|
-
protected itemInstance(itemId: string): import("
|
|
7
|
+
protected itemInstance(itemId: string): import("../types/core").ItemInstance<T>;
|
|
7
8
|
protected itemProps(itemId: string): Record<string, any>;
|
|
8
9
|
constructor(tree: TestTree<T>);
|
|
9
10
|
foldersExpanded(...itemIds: string[]): void;
|
|
10
11
|
foldersCollapsed(...itemIds: string[]): void;
|
|
11
12
|
hasChildren(itemId: string, children: string[]): void;
|
|
12
|
-
|
|
13
|
+
substate<K extends keyof TreeState<T>>(key: K, value: TreeState<T>[K]): void;
|
|
14
|
+
dropped(draggedItems: string[], target: DragTarget<any>): void;
|
|
13
15
|
dragOverNotAllowed(itemId: string, event?: DragEvent): DragEvent<Element>;
|
|
14
16
|
defaultDragLineProps(indent?: number): void;
|
|
15
17
|
}
|
|
@@ -29,6 +29,9 @@ class TestTreeExpect {
|
|
|
29
29
|
const itemChildren = item.getChildren().map((child) => child.getId());
|
|
30
30
|
(0, vitest_1.expect)(itemChildren).toEqual(children);
|
|
31
31
|
}
|
|
32
|
+
substate(key, value) {
|
|
33
|
+
(0, vitest_1.expect)(this.tree.instance.getState()[key]).toEqual(value);
|
|
34
|
+
}
|
|
32
35
|
dropped(draggedItems, target) {
|
|
33
36
|
(0, vitest_1.expect)(this.tree.instance.getConfig().onDrop).toBeCalledWith(draggedItems.map((id) => this.tree.item(id)), target);
|
|
34
37
|
}
|
package/lib/cjs/types/core.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { SearchFeatureDef } from "../features/search/types";
|
|
|
9
9
|
import { RenamingFeatureDef } from "../features/renaming/types";
|
|
10
10
|
import { ExpandAllFeatureDef } from "../features/expand-all/types";
|
|
11
11
|
import { PropMemoizationFeatureDef } from "../features/prop-memoization/types";
|
|
12
|
+
import { KeyboardDragAndDropFeatureDef } from "../features/keyboard-drag-and-drop/types";
|
|
12
13
|
export type Updater<T> = T | ((old: T) => T);
|
|
13
14
|
export type SetStateFn<T> = (updaterOrValue: Updater<T>) => void;
|
|
14
15
|
export type FeatureDef = {
|
|
@@ -33,7 +34,7 @@ type MergedFeatures<F extends FeatureDef> = {
|
|
|
33
34
|
itemInstance: UnionToIntersection<F["itemInstance"]>;
|
|
34
35
|
hotkeys: F["hotkeys"];
|
|
35
36
|
};
|
|
36
|
-
export type RegisteredFeatures<T> = MainFeatureDef<T> | TreeFeatureDef<T> | SelectionFeatureDef<T> | DragAndDropFeatureDef<T> | HotkeysCoreFeatureDef<T> | SyncDataLoaderFeatureDef<T> | AsyncDataLoaderFeatureDef<T> | SearchFeatureDef<T> | RenamingFeatureDef<T> | ExpandAllFeatureDef | PropMemoizationFeatureDef;
|
|
37
|
+
export type RegisteredFeatures<T> = MainFeatureDef<T> | TreeFeatureDef<T> | SelectionFeatureDef<T> | DragAndDropFeatureDef<T> | KeyboardDragAndDropFeatureDef<T> | HotkeysCoreFeatureDef<T> | SyncDataLoaderFeatureDef<T> | AsyncDataLoaderFeatureDef<T> | SearchFeatureDef<T> | RenamingFeatureDef<T> | ExpandAllFeatureDef | PropMemoizationFeatureDef;
|
|
37
38
|
type TreeStateType<T> = MergedFeatures<RegisteredFeatures<T>>["state"];
|
|
38
39
|
export interface TreeState<T> extends TreeStateType<T> {
|
|
39
40
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { ItemInstance } from "../types/core";
|
|
2
|
-
import {
|
|
3
|
-
export declare const createOnDropHandler: <T>(onChangeChildren: (item: ItemInstance<T>, newChildren: string[]) => void) => (items: ItemInstance<T>[], target:
|
|
2
|
+
import { DragTarget } from "../features/drag-and-drop/types";
|
|
3
|
+
export declare const createOnDropHandler: <T>(onChangeChildren: (item: ItemInstance<T>, newChildren: string[]) => void) => (items: ItemInstance<T>[], target: DragTarget<T>) => Promise<void>;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { ItemInstance } from "../types/core";
|
|
2
|
-
import {
|
|
3
|
-
export declare const insertItemsAtTarget: <T>(itemIds: string[], target:
|
|
2
|
+
import { DragTarget } from "../features/drag-and-drop/types";
|
|
3
|
+
export declare const insertItemsAtTarget: <T>(itemIds: string[], target: DragTarget<T>, onChangeChildren: (item: ItemInstance<T>, newChildrenIds: string[]) => Promise<void> | void) => Promise<void>;
|
|
@@ -7,7 +7,7 @@ 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 { canDrop, getDragCode,
|
|
10
|
+
import { canDrop, getDragCode, getDragTarget } from "./utils";
|
|
11
11
|
import { makeStateUpdater } from "../../utils";
|
|
12
12
|
export const dragAndDropFeature = {
|
|
13
13
|
key: "drag-and-drop",
|
|
@@ -17,13 +17,13 @@ export const dragAndDropFeature = {
|
|
|
17
17
|
dnd: "setDndState",
|
|
18
18
|
},
|
|
19
19
|
treeInstance: {
|
|
20
|
-
|
|
20
|
+
getDragTarget: ({ tree }) => {
|
|
21
21
|
var _a, _b;
|
|
22
22
|
return (_b = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.dragTarget) !== null && _b !== void 0 ? _b : null;
|
|
23
23
|
},
|
|
24
24
|
getDragLineData: ({ tree }) => {
|
|
25
25
|
var _a, _b, _c, _d, _e, _f;
|
|
26
|
-
const target = tree.
|
|
26
|
+
const target = tree.getDragTarget();
|
|
27
27
|
const indent = ((_a = target === null || target === void 0 ? void 0 : target.item.getItemMeta().level) !== null && _a !== void 0 ? _a : 0) + 1;
|
|
28
28
|
const treeBb = (_b = tree.getElement()) === null || _b === void 0 ? void 0 : _b.getBoundingClientRect();
|
|
29
29
|
if (!target || !treeBb || !("childIndex" in target))
|
|
@@ -36,7 +36,7 @@ export const dragAndDropFeature = {
|
|
|
36
36
|
if (bb) {
|
|
37
37
|
return {
|
|
38
38
|
indent,
|
|
39
|
-
top: bb.bottom - treeBb.
|
|
39
|
+
top: bb.bottom - treeBb.top,
|
|
40
40
|
left: bb.left + leftOffset - treeBb.left,
|
|
41
41
|
width: bb.width - leftOffset,
|
|
42
42
|
};
|
|
@@ -101,7 +101,7 @@ export const dragAndDropFeature = {
|
|
|
101
101
|
return;
|
|
102
102
|
}
|
|
103
103
|
dataRef.current.lastDragCode = nextDragCode;
|
|
104
|
-
const target =
|
|
104
|
+
const target = getDragTarget(e, item, tree);
|
|
105
105
|
if (!((_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggedItems) &&
|
|
106
106
|
(!e.dataTransfer ||
|
|
107
107
|
!((_c = (_b = tree
|
|
@@ -131,7 +131,7 @@ export const dragAndDropFeature = {
|
|
|
131
131
|
}, onDrop: (e) => __awaiter(void 0, void 0, void 0, function* () {
|
|
132
132
|
var _a, _b, _c;
|
|
133
133
|
const dataRef = tree.getDataRef();
|
|
134
|
-
const target =
|
|
134
|
+
const target = getDragTarget(e, item, tree);
|
|
135
135
|
if (!canDrop(e.dataTransfer, target, tree)) {
|
|
136
136
|
return;
|
|
137
137
|
}
|
|
@@ -147,20 +147,20 @@ export const dragAndDropFeature = {
|
|
|
147
147
|
yield ((_c = config.onDropForeignDragObject) === null || _c === void 0 ? void 0 : _c.call(config, e.dataTransfer, target));
|
|
148
148
|
}
|
|
149
149
|
}) })),
|
|
150
|
-
|
|
151
|
-
const target = tree.
|
|
150
|
+
isDragTarget: ({ tree, item }) => {
|
|
151
|
+
const target = tree.getDragTarget();
|
|
152
152
|
return target ? target.item.getId() === item.getId() : false;
|
|
153
153
|
},
|
|
154
|
-
|
|
155
|
-
const target = tree.
|
|
154
|
+
isDragTargetAbove: ({ tree, item }) => {
|
|
155
|
+
const target = tree.getDragTarget();
|
|
156
156
|
if (!target ||
|
|
157
157
|
!("childIndex" in target) ||
|
|
158
158
|
target.item !== item.getParent())
|
|
159
159
|
return false;
|
|
160
160
|
return target.childIndex === item.getItemMeta().posInSet;
|
|
161
161
|
},
|
|
162
|
-
|
|
163
|
-
const target = tree.
|
|
162
|
+
isDragTargetBelow: ({ tree, item }) => {
|
|
163
|
+
const target = tree.getDragTarget();
|
|
164
164
|
if (!target ||
|
|
165
165
|
!("childIndex" in target) ||
|
|
166
166
|
target.item !== item.getParent())
|
|
@@ -6,7 +6,7 @@ export interface DndDataRef {
|
|
|
6
6
|
export interface DndState<T> {
|
|
7
7
|
draggedItems?: ItemInstance<T>[];
|
|
8
8
|
draggingOverItem?: ItemInstance<T>;
|
|
9
|
-
dragTarget?:
|
|
9
|
+
dragTarget?: DragTarget<T>;
|
|
10
10
|
}
|
|
11
11
|
export interface DragLineData {
|
|
12
12
|
indent: number;
|
|
@@ -14,7 +14,7 @@ export interface DragLineData {
|
|
|
14
14
|
left: number;
|
|
15
15
|
width: number;
|
|
16
16
|
}
|
|
17
|
-
export type
|
|
17
|
+
export type DragTarget<T> = {
|
|
18
18
|
item: ItemInstance<T>;
|
|
19
19
|
childIndex: number;
|
|
20
20
|
insertionIndex: number;
|
|
@@ -23,7 +23,7 @@ export type DropTarget<T> = {
|
|
|
23
23
|
} | {
|
|
24
24
|
item: ItemInstance<T>;
|
|
25
25
|
};
|
|
26
|
-
export declare enum
|
|
26
|
+
export declare enum DragTargetPosition {
|
|
27
27
|
Top = "top",
|
|
28
28
|
Bottom = "bottom",
|
|
29
29
|
Item = "item"
|
|
@@ -40,26 +40,26 @@ export type DragAndDropFeatureDef<T> = {
|
|
|
40
40
|
reorderAreaPercentage?: number;
|
|
41
41
|
canReorder?: boolean;
|
|
42
42
|
canDrag?: (items: ItemInstance<T>[]) => boolean;
|
|
43
|
-
canDrop?: (items: ItemInstance<T>[], target:
|
|
43
|
+
canDrop?: (items: ItemInstance<T>[], target: DragTarget<T>) => boolean;
|
|
44
44
|
indent?: number;
|
|
45
45
|
createForeignDragObject?: (items: ItemInstance<T>[]) => {
|
|
46
46
|
format: string;
|
|
47
47
|
data: any;
|
|
48
48
|
};
|
|
49
|
-
canDropForeignDragObject?: (dataTransfer: DataTransfer, target:
|
|
50
|
-
onDrop?: (items: ItemInstance<T>[], target:
|
|
51
|
-
onDropForeignDragObject?: (dataTransfer: DataTransfer, target:
|
|
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>;
|
|
52
52
|
onCompleteForeignDrop?: (items: ItemInstance<T>[]) => void;
|
|
53
53
|
};
|
|
54
54
|
treeInstance: {
|
|
55
|
-
|
|
55
|
+
getDragTarget: () => DragTarget<T> | null;
|
|
56
56
|
getDragLineData: () => DragLineData | null;
|
|
57
57
|
getDragLineStyle: (topOffset?: number, leftOffset?: number) => Record<string, any>;
|
|
58
58
|
};
|
|
59
59
|
itemInstance: {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
isDragTarget: () => boolean;
|
|
61
|
+
isDragTargetAbove: () => boolean;
|
|
62
|
+
isDragTargetBelow: () => boolean;
|
|
63
63
|
isDraggingOver: () => boolean;
|
|
64
64
|
};
|
|
65
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,20 @@
|
|
|
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 canDrop: (dataTransfer: DataTransfer | null, target: DragTarget<any>, tree: TreeInstance<any>) => boolean;
|
|
9
|
+
export declare const getItemDropCategory: (item: ItemInstance<any>) => ItemDropCategory;
|
|
10
|
+
export declare const getInsertionIndex: <T>(children: ItemInstance<T>[], childIndex: number, draggedItems: ItemInstance<T>[] | undefined) => number;
|
|
4
11
|
export declare const getDragCode: (e: any, item: ItemInstance<any>, tree: TreeInstance<any>) => string;
|
|
5
|
-
|
|
12
|
+
/** @param item refers to the bottom-most item of the container, at which bottom is being reparented on (e.g. root-1-2-6) */
|
|
13
|
+
export declare const getReparentTarget: <T>(item: ItemInstance<T>, reparentLevel: number, draggedItems: ItemInstance<T>[] | undefined) => {
|
|
14
|
+
item: ItemInstance<any>;
|
|
15
|
+
childIndex: number;
|
|
16
|
+
insertionIndex: number;
|
|
17
|
+
dragLineIndex: number;
|
|
18
|
+
dragLineLevel: number;
|
|
19
|
+
};
|
|
20
|
+
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";
|
|
@@ -12,7 +12,7 @@ var PlacementType;
|
|
|
12
12
|
PlacementType[PlacementType["Reparent"] = 3] = "Reparent";
|
|
13
13
|
})(PlacementType || (PlacementType = {}));
|
|
14
14
|
export const canDrop = (dataTransfer, target, tree) => {
|
|
15
|
-
var _a, _b, _c
|
|
15
|
+
var _a, _b, _c;
|
|
16
16
|
const draggedItems = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggedItems;
|
|
17
17
|
const config = tree.getConfig();
|
|
18
18
|
if (draggedItems && !((_c = (_b = config.canDrop) === null || _b === void 0 ? void 0 : _b.call(config, draggedItems, target)) !== null && _c !== void 0 ? _c : true)) {
|
|
@@ -25,12 +25,13 @@ export const canDrop = (dataTransfer, target, tree) => {
|
|
|
25
25
|
}
|
|
26
26
|
if (!draggedItems &&
|
|
27
27
|
dataTransfer &&
|
|
28
|
-
|
|
28
|
+
config.canDropForeignDragObject &&
|
|
29
|
+
!config.canDropForeignDragObject(dataTransfer, target)) {
|
|
29
30
|
return false;
|
|
30
31
|
}
|
|
31
32
|
return true;
|
|
32
33
|
};
|
|
33
|
-
const getItemDropCategory = (item) => {
|
|
34
|
+
export const getItemDropCategory = (item) => {
|
|
34
35
|
if (item.isExpanded()) {
|
|
35
36
|
return ItemDropCategory.ExpandedFolder;
|
|
36
37
|
}
|
|
@@ -40,6 +41,15 @@ const getItemDropCategory = (item) => {
|
|
|
40
41
|
}
|
|
41
42
|
return ItemDropCategory.Item;
|
|
42
43
|
};
|
|
44
|
+
export const getInsertionIndex = (children, childIndex, draggedItems) => {
|
|
45
|
+
var _a;
|
|
46
|
+
const numberOfDragItemsBeforeTarget = (_a = children
|
|
47
|
+
.slice(0, childIndex)
|
|
48
|
+
.reduce((counter, child) => child && (draggedItems === null || draggedItems === void 0 ? void 0 : draggedItems.some((i) => i.getId() === child.getId()))
|
|
49
|
+
? ++counter
|
|
50
|
+
: counter, 0)) !== null && _a !== void 0 ? _a : 0;
|
|
51
|
+
return childIndex - numberOfDragItemsBeforeTarget;
|
|
52
|
+
};
|
|
43
53
|
const getTargetPlacement = (e, item, tree, canMakeChild) => {
|
|
44
54
|
var _a, _b, _c, _d, _e;
|
|
45
55
|
const config = tree.getConfig();
|
|
@@ -101,9 +111,24 @@ const getNthParent = (item, n) => {
|
|
|
101
111
|
}
|
|
102
112
|
return getNthParent(item.getParent(), n);
|
|
103
113
|
};
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const
|
|
114
|
+
/** @param item refers to the bottom-most item of the container, at which bottom is being reparented on (e.g. root-1-2-6) */
|
|
115
|
+
export const getReparentTarget = (item, reparentLevel, draggedItems) => {
|
|
116
|
+
const itemMeta = item.getItemMeta();
|
|
117
|
+
const reparentedTarget = getNthParent(item, reparentLevel - 1);
|
|
118
|
+
const targetItemAbove = getNthParent(item, reparentLevel); // .getItemBelow()!;
|
|
119
|
+
const targetIndex = targetItemAbove.getIndexInParent() + 1;
|
|
120
|
+
// TODO possibly count items dragged out above the new target
|
|
121
|
+
return {
|
|
122
|
+
item: reparentedTarget,
|
|
123
|
+
childIndex: targetIndex,
|
|
124
|
+
insertionIndex: getInsertionIndex(reparentedTarget.getChildren(), targetIndex, draggedItems),
|
|
125
|
+
dragLineIndex: itemMeta.index + 1,
|
|
126
|
+
dragLineLevel: reparentLevel,
|
|
127
|
+
};
|
|
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;
|
|
107
132
|
const itemMeta = item.getItemMeta();
|
|
108
133
|
const parent = item.getParent();
|
|
109
134
|
const itemTarget = { item };
|
|
@@ -118,8 +143,8 @@ export const getDropTarget = (e, item, tree, canReorder = tree.getConfig().canRe
|
|
|
118
143
|
return parentTarget;
|
|
119
144
|
}
|
|
120
145
|
if (!canReorder && parent && !canBecomeSibling) {
|
|
121
|
-
// TODO! this breaks in story DND/Can Drop. Maybe move this logic into a composable
|
|
122
|
-
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);
|
|
123
148
|
}
|
|
124
149
|
if (!parent) {
|
|
125
150
|
// Shouldn't happen, but if dropped "next" to root item, just drop it inside
|
|
@@ -129,35 +154,19 @@ export const getDropTarget = (e, item, tree, canReorder = tree.getConfig().canRe
|
|
|
129
154
|
return itemTarget;
|
|
130
155
|
}
|
|
131
156
|
if (!canBecomeSibling) {
|
|
132
|
-
return
|
|
157
|
+
return getDragTarget(e, parent, tree, false);
|
|
133
158
|
}
|
|
134
159
|
if (placement.type === PlacementType.Reparent) {
|
|
135
|
-
|
|
136
|
-
const targetItemAbove = getNthParent(item, placement.reparentLevel); // .getItemBelow()!;
|
|
137
|
-
const targetIndex = targetItemAbove.getIndexInParent() + 1;
|
|
138
|
-
// TODO possibly count items dragged out above the new target
|
|
139
|
-
return {
|
|
140
|
-
item: reparentedTarget,
|
|
141
|
-
childIndex: targetIndex,
|
|
142
|
-
insertionIndex: targetIndex,
|
|
143
|
-
dragLineIndex: itemMeta.index + 1,
|
|
144
|
-
dragLineLevel: placement.reparentLevel,
|
|
145
|
-
};
|
|
160
|
+
return getReparentTarget(item, placement.reparentLevel, draggedItems);
|
|
146
161
|
}
|
|
147
162
|
const maybeAddOneForBelow = placement.type === PlacementType.ReorderAbove ? 0 : 1;
|
|
148
163
|
const childIndex = item.getIndexInParent() + maybeAddOneForBelow;
|
|
149
|
-
const numberOfDragItemsBeforeTarget = (_c = parent
|
|
150
|
-
.getChildren()
|
|
151
|
-
.slice(0, childIndex)
|
|
152
|
-
.reduce((counter, child) => child && (draggedItems === null || draggedItems === void 0 ? void 0 : draggedItems.some((i) => i.getId() === child.getId()))
|
|
153
|
-
? ++counter
|
|
154
|
-
: counter, 0)) !== null && _c !== void 0 ? _c : 0;
|
|
155
164
|
return {
|
|
156
165
|
item: parent,
|
|
157
166
|
dragLineIndex: itemMeta.index + maybeAddOneForBelow,
|
|
158
167
|
dragLineLevel: itemMeta.level,
|
|
159
168
|
childIndex,
|
|
160
169
|
// TODO performance could be improved by computing this only when dragcode changed
|
|
161
|
-
insertionIndex: childIndex
|
|
170
|
+
insertionIndex: getInsertionIndex(parent.getChildren(), childIndex, draggedItems),
|
|
162
171
|
};
|
|
163
172
|
};
|
|
@@ -27,6 +27,7 @@ export const hotkeysCoreFeature = {
|
|
|
27
27
|
(_a = (_e = data.current).pressedKeys) !== null && _a !== void 0 ? _a : (_e.pressedKeys = new Set());
|
|
28
28
|
const newMatch = !data.current.pressedKeys.has(e.key);
|
|
29
29
|
data.current.pressedKeys.add(e.key);
|
|
30
|
+
console.log("HOTKEYS", data.current.pressedKeys);
|
|
30
31
|
const hotkeyName = findHotkeyMatch(data.current.pressedKeys, tree, tree.getHotkeyPresets(), tree.getConfig().hotkeys);
|
|
31
32
|
if (!hotkeyName)
|
|
32
33
|
return;
|
|
@@ -0,0 +1,204 @@
|
|
|
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, } 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 ("childIndex" in dragTarget) {
|
|
19
|
+
// TODO move check in reusable function
|
|
20
|
+
const parent = dragTarget.item.getParent();
|
|
21
|
+
const targetedItem = tree.getItems()[dragTarget.dragLineIndex - 1]; // item above dragline
|
|
22
|
+
const targetCategory = targetedItem
|
|
23
|
+
? getItemDropCategory(targetedItem)
|
|
24
|
+
: ItemDropCategory.Item;
|
|
25
|
+
const maxLevel = (_b = targetedItem === null || targetedItem === void 0 ? void 0 : targetedItem.getItemMeta().level) !== null && _b !== void 0 ? _b : 0;
|
|
26
|
+
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;
|
|
27
|
+
// reparenting
|
|
28
|
+
if (targetCategory === ItemDropCategory.LastInGroup) {
|
|
29
|
+
if (isUp && dragTarget.dragLineLevel < maxLevel) {
|
|
30
|
+
return getReparentTarget(targetedItem, dragTarget.dragLineLevel + 1, draggedItems);
|
|
31
|
+
}
|
|
32
|
+
if (!isUp && dragTarget.dragLineLevel > minLevel && parent) {
|
|
33
|
+
return getReparentTarget(targetedItem, dragTarget.dragLineLevel - 1, draggedItems);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const newIndex = dragTarget.dragLineIndex - 1 + direction;
|
|
37
|
+
const item = tree.getItems()[newIndex];
|
|
38
|
+
return item ? { item } : undefined;
|
|
39
|
+
}
|
|
40
|
+
// moving upwards outside of an open folder
|
|
41
|
+
const targetingExpandedFolder = getItemDropCategory(dragTarget.item) === ItemDropCategory.ExpandedFolder;
|
|
42
|
+
if (targetingExpandedFolder && !isUp) {
|
|
43
|
+
return {
|
|
44
|
+
item: dragTarget.item,
|
|
45
|
+
childIndex: 0,
|
|
46
|
+
insertionIndex: getInsertionIndex(dragTarget.item.getChildren(), 0, draggedItems),
|
|
47
|
+
dragLineIndex: dragTarget.item.getItemMeta().index + direction,
|
|
48
|
+
dragLineLevel: dragTarget.item.getItemMeta().level + 1,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
// currently hovering over item
|
|
52
|
+
const childIndex = dragTarget.item.getIndexInParent() + direction;
|
|
53
|
+
return {
|
|
54
|
+
item: dragTarget.item.getParent(),
|
|
55
|
+
childIndex,
|
|
56
|
+
insertionIndex: getInsertionIndex(dragTarget.item.getParent().getChildren(), childIndex, draggedItems),
|
|
57
|
+
dragLineIndex: dragTarget.item.getItemMeta().index + direction,
|
|
58
|
+
dragLineLevel: dragTarget.item.getItemMeta().level,
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
const getNextValidDragTarget = (tree, isUp, previousTarget) => {
|
|
62
|
+
var _a, _b;
|
|
63
|
+
if (previousTarget === void 0) { previousTarget = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.dragTarget; }
|
|
64
|
+
if (!previousTarget)
|
|
65
|
+
return undefined;
|
|
66
|
+
const nextTarget = getNextDragTarget(tree, isUp, previousTarget);
|
|
67
|
+
const dataTransfer = (_b = tree.getDataRef().current.kDndDataTransfer) !== null && _b !== void 0 ? _b : null;
|
|
68
|
+
if (!nextTarget)
|
|
69
|
+
return undefined;
|
|
70
|
+
if (canDrop(dataTransfer, nextTarget, tree)) {
|
|
71
|
+
return nextTarget;
|
|
72
|
+
}
|
|
73
|
+
return getNextValidDragTarget(tree, isUp, nextTarget);
|
|
74
|
+
};
|
|
75
|
+
const updateScroll = (tree) => {
|
|
76
|
+
const state = tree.getState().dnd;
|
|
77
|
+
if (!(state === null || state === void 0 ? void 0 : state.dragTarget) || "childIndex" in state.dragTarget)
|
|
78
|
+
return;
|
|
79
|
+
state.dragTarget.item.scrollTo({ block: "nearest", inline: "nearest" });
|
|
80
|
+
};
|
|
81
|
+
const initiateDrag = (tree, draggedItems, dataTransfer) => {
|
|
82
|
+
var _a, _b;
|
|
83
|
+
const focusedItem = tree.getFocusedItem();
|
|
84
|
+
const { canDrag } = tree.getConfig();
|
|
85
|
+
if (draggedItems && canDrag && !canDrag(draggedItems)) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (draggedItems) {
|
|
89
|
+
tree.applySubStateUpdate("dnd", { draggedItems });
|
|
90
|
+
// getNextValidDragTarget->canDrop needs the draggedItems in state
|
|
91
|
+
(_b = (_a = tree.getConfig()).onStartKeyboardDrag) === null || _b === void 0 ? void 0 : _b.call(_a, draggedItems);
|
|
92
|
+
}
|
|
93
|
+
else if (dataTransfer) {
|
|
94
|
+
tree.getDataRef().current.kDndDataTransfer = dataTransfer;
|
|
95
|
+
}
|
|
96
|
+
const dragTarget = getNextValidDragTarget(tree, false, {
|
|
97
|
+
item: focusedItem,
|
|
98
|
+
});
|
|
99
|
+
if (!dragTarget)
|
|
100
|
+
return;
|
|
101
|
+
tree.applySubStateUpdate("dnd", {
|
|
102
|
+
draggedItems,
|
|
103
|
+
dragTarget,
|
|
104
|
+
});
|
|
105
|
+
tree.applySubStateUpdate("assistiveDndState", AssistiveDndState.Started);
|
|
106
|
+
updateScroll(tree);
|
|
107
|
+
};
|
|
108
|
+
const moveDragPosition = (tree, isUp) => {
|
|
109
|
+
var _a;
|
|
110
|
+
const dragTarget = getNextValidDragTarget(tree, isUp);
|
|
111
|
+
if (!dragTarget)
|
|
112
|
+
return;
|
|
113
|
+
tree.applySubStateUpdate("dnd", {
|
|
114
|
+
draggedItems: (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggedItems,
|
|
115
|
+
dragTarget,
|
|
116
|
+
});
|
|
117
|
+
tree.applySubStateUpdate("assistiveDndState", AssistiveDndState.Dragging);
|
|
118
|
+
if (!("childIndex" in dragTarget)) {
|
|
119
|
+
dragTarget.item.setFocused();
|
|
120
|
+
}
|
|
121
|
+
updateScroll(tree);
|
|
122
|
+
};
|
|
123
|
+
export const keyboardDragAndDropFeature = {
|
|
124
|
+
key: "keyboard-drag-and-drop",
|
|
125
|
+
deps: ["drag-and-drop"],
|
|
126
|
+
getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setAssistiveDndState: makeStateUpdater("assistiveDndState", tree) }, defaultConfig)),
|
|
127
|
+
stateHandlerNames: {
|
|
128
|
+
assistiveDndState: "setAssistiveDndState",
|
|
129
|
+
},
|
|
130
|
+
treeInstance: {
|
|
131
|
+
startKeyboardDrag: ({ tree }, draggedItems) => {
|
|
132
|
+
initiateDrag(tree, draggedItems, undefined);
|
|
133
|
+
},
|
|
134
|
+
startKeyboardDragOnForeignObject: ({ tree }, dataTransfer) => {
|
|
135
|
+
initiateDrag(tree, undefined, dataTransfer);
|
|
136
|
+
},
|
|
137
|
+
stopKeyboardDrag: ({ tree }) => {
|
|
138
|
+
tree.getDataRef().current.kDndDataTransfer = undefined;
|
|
139
|
+
tree.applySubStateUpdate("dnd", null);
|
|
140
|
+
tree.applySubStateUpdate("assistiveDndState", AssistiveDndState.None);
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
hotkeys: {
|
|
144
|
+
startDrag: {
|
|
145
|
+
hotkey: "Control+Shift+D",
|
|
146
|
+
preventDefault: true,
|
|
147
|
+
isEnabled: (tree) => !tree.getState().dnd,
|
|
148
|
+
handler: (_, tree) => {
|
|
149
|
+
tree.startKeyboardDrag(tree.getSelectedItems());
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
dragUp: {
|
|
153
|
+
hotkey: "ArrowUp",
|
|
154
|
+
preventDefault: true,
|
|
155
|
+
isEnabled: (tree) => !!tree.getState().dnd,
|
|
156
|
+
handler: (_, tree) => {
|
|
157
|
+
moveDragPosition(tree, true);
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
dragDown: {
|
|
161
|
+
hotkey: "ArrowDown",
|
|
162
|
+
preventDefault: true,
|
|
163
|
+
isEnabled: (tree) => !!tree.getState().dnd,
|
|
164
|
+
handler: (_, tree) => {
|
|
165
|
+
moveDragPosition(tree, false);
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
cancelDrag: {
|
|
169
|
+
hotkey: "Escape",
|
|
170
|
+
isEnabled: (tree) => !!tree.getState().dnd,
|
|
171
|
+
handler: (_, tree) => {
|
|
172
|
+
tree.stopKeyboardDrag();
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
completeDrag: {
|
|
176
|
+
hotkey: "Enter",
|
|
177
|
+
preventDefault: true,
|
|
178
|
+
isEnabled: (tree) => !!tree.getState().dnd,
|
|
179
|
+
handler: (e, tree) => __awaiter(void 0, void 0, void 0, function* () {
|
|
180
|
+
var _a, _b, _c, _d;
|
|
181
|
+
e.stopPropagation();
|
|
182
|
+
// TODO copied from keyboard onDrop, unify them
|
|
183
|
+
const dataRef = tree.getDataRef();
|
|
184
|
+
const target = tree.getDragTarget();
|
|
185
|
+
const dataTransfer = (_a = dataRef.current.kDndDataTransfer) !== null && _a !== void 0 ? _a : null;
|
|
186
|
+
if (!target || !canDrop(dataTransfer, target, tree)) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
const config = tree.getConfig();
|
|
190
|
+
const draggedItems = (_b = tree.getState().dnd) === null || _b === void 0 ? void 0 : _b.draggedItems;
|
|
191
|
+
dataRef.current.lastDragCode = undefined;
|
|
192
|
+
tree.applySubStateUpdate("dnd", null);
|
|
193
|
+
if (draggedItems) {
|
|
194
|
+
yield ((_c = config.onDrop) === null || _c === void 0 ? void 0 : _c.call(config, draggedItems, target));
|
|
195
|
+
tree.getItemInstance(draggedItems[0].getId()).setFocused();
|
|
196
|
+
}
|
|
197
|
+
else if (dataTransfer) {
|
|
198
|
+
yield ((_d = config.onDropForeignDragObject) === null || _d === void 0 ? void 0 : _d.call(config, dataTransfer, target));
|
|
199
|
+
}
|
|
200
|
+
tree.applySubStateUpdate("assistiveDndState", AssistiveDndState.Completed);
|
|
201
|
+
}),
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ItemInstance, SetStateFn } from "../../types/core";
|
|
2
|
+
export interface KDndDataRef {
|
|
3
|
+
kDndDataTransfer: DataTransfer | undefined;
|
|
4
|
+
}
|
|
5
|
+
export declare enum AssistiveDndState {
|
|
6
|
+
None = 0,
|
|
7
|
+
Started = 1,
|
|
8
|
+
Dragging = 2,
|
|
9
|
+
Completed = 3,
|
|
10
|
+
Aborted = 4
|
|
11
|
+
}
|
|
12
|
+
export type KeyboardDragAndDropFeatureDef<T> = {
|
|
13
|
+
state: {
|
|
14
|
+
assistiveDndState?: AssistiveDndState | null;
|
|
15
|
+
};
|
|
16
|
+
config: {
|
|
17
|
+
setAssistiveDndState?: SetStateFn<AssistiveDndState | undefined | null>;
|
|
18
|
+
onStartKeyboardDrag?: (items: ItemInstance<T>[]) => void;
|
|
19
|
+
};
|
|
20
|
+
treeInstance: {
|
|
21
|
+
startKeyboardDrag: (items: ItemInstance<T>[]) => void;
|
|
22
|
+
startKeyboardDragOnForeignObject: (dataTransfer: DataTransfer) => void;
|
|
23
|
+
stopKeyboardDrag: () => void;
|
|
24
|
+
};
|
|
25
|
+
itemInstance: {};
|
|
26
|
+
hotkeys: "startDrag" | "cancelDrag" | "completeDrag" | "dragUp" | "dragDown";
|
|
27
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export var AssistiveDndState;
|
|
2
|
+
(function (AssistiveDndState) {
|
|
3
|
+
AssistiveDndState[AssistiveDndState["None"] = 0] = "None";
|
|
4
|
+
AssistiveDndState[AssistiveDndState["Started"] = 1] = "Started";
|
|
5
|
+
AssistiveDndState[AssistiveDndState["Dragging"] = 2] = "Dragging";
|
|
6
|
+
AssistiveDndState[AssistiveDndState["Completed"] = 3] = "Completed";
|
|
7
|
+
AssistiveDndState[AssistiveDndState["Aborted"] = 4] = "Aborted";
|
|
8
|
+
})(AssistiveDndState || (AssistiveDndState = {}));
|
package/lib/esm/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export * from "./core/create-tree";
|
|
|
3
3
|
export * from "./features/tree/types";
|
|
4
4
|
export { MainFeatureDef, InstanceBuilder } from "./features/main/types";
|
|
5
5
|
export * from "./features/drag-and-drop/types";
|
|
6
|
+
export * from "./features/keyboard-drag-and-drop/types";
|
|
6
7
|
export * from "./features/selection/types";
|
|
7
8
|
export * from "./features/async-data-loader/types";
|
|
8
9
|
export * from "./features/sync-data-loader/types";
|
|
@@ -16,6 +17,7 @@ export * from "./features/hotkeys-core/feature";
|
|
|
16
17
|
export * from "./features/async-data-loader/feature";
|
|
17
18
|
export * from "./features/sync-data-loader/feature";
|
|
18
19
|
export * from "./features/drag-and-drop/feature";
|
|
20
|
+
export * from "./features/keyboard-drag-and-drop/feature";
|
|
19
21
|
export * from "./features/search/feature";
|
|
20
22
|
export * from "./features/renaming/feature";
|
|
21
23
|
export * from "./features/expand-all/feature";
|