@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.
Files changed (52) hide show
  1. package/CHANGELOG.md +2 -1
  2. package/lib/cjs/features/drag-and-drop/feature.js +11 -11
  3. package/lib/cjs/features/drag-and-drop/types.d.ts +11 -11
  4. package/lib/cjs/features/drag-and-drop/types.js +7 -7
  5. package/lib/cjs/features/drag-and-drop/utils.d.ts +18 -3
  6. package/lib/cjs/features/drag-and-drop/utils.js +42 -30
  7. package/lib/cjs/features/hotkeys-core/feature.js +1 -0
  8. package/lib/cjs/features/keyboard-drag-and-drop/feature.d.ts +2 -0
  9. package/lib/cjs/features/keyboard-drag-and-drop/feature.js +207 -0
  10. package/lib/cjs/features/keyboard-drag-and-drop/types.d.ts +27 -0
  11. package/lib/cjs/features/keyboard-drag-and-drop/types.js +11 -0
  12. package/lib/cjs/index.d.ts +2 -0
  13. package/lib/cjs/index.js +2 -0
  14. package/lib/cjs/mddocs-entry.d.ts +10 -0
  15. package/lib/cjs/test-utils/test-tree-expect.d.ts +5 -3
  16. package/lib/cjs/test-utils/test-tree-expect.js +3 -0
  17. package/lib/cjs/types/core.d.ts +2 -1
  18. package/lib/cjs/utilities/create-on-drop-handler.d.ts +2 -2
  19. package/lib/cjs/utilities/insert-items-at-target.d.ts +2 -2
  20. package/lib/esm/features/drag-and-drop/feature.js +12 -12
  21. package/lib/esm/features/drag-and-drop/types.d.ts +11 -11
  22. package/lib/esm/features/drag-and-drop/types.js +6 -6
  23. package/lib/esm/features/drag-and-drop/utils.d.ts +18 -3
  24. package/lib/esm/features/drag-and-drop/utils.js +37 -28
  25. package/lib/esm/features/hotkeys-core/feature.js +1 -0
  26. package/lib/esm/features/keyboard-drag-and-drop/feature.d.ts +2 -0
  27. package/lib/esm/features/keyboard-drag-and-drop/feature.js +204 -0
  28. package/lib/esm/features/keyboard-drag-and-drop/types.d.ts +27 -0
  29. package/lib/esm/features/keyboard-drag-and-drop/types.js +8 -0
  30. package/lib/esm/index.d.ts +2 -0
  31. package/lib/esm/index.js +2 -0
  32. package/lib/esm/mddocs-entry.d.ts +10 -0
  33. package/lib/esm/test-utils/test-tree-expect.d.ts +5 -3
  34. package/lib/esm/test-utils/test-tree-expect.js +3 -0
  35. package/lib/esm/types/core.d.ts +2 -1
  36. package/lib/esm/utilities/create-on-drop-handler.d.ts +2 -2
  37. package/lib/esm/utilities/insert-items-at-target.d.ts +2 -2
  38. package/package.json +1 -1
  39. package/src/features/drag-and-drop/drag-and-drop.spec.ts +6 -6
  40. package/src/features/drag-and-drop/feature.ts +12 -12
  41. package/src/features/drag-and-drop/types.ts +11 -11
  42. package/src/features/drag-and-drop/utils.ts +64 -39
  43. package/src/features/hotkeys-core/feature.ts +1 -0
  44. package/src/features/keyboard-drag-and-drop/feature.ts +255 -0
  45. package/src/features/keyboard-drag-and-drop/keyboard-drag-and-drop.spec.ts +401 -0
  46. package/src/features/keyboard-drag-and-drop/types.ts +30 -0
  47. package/src/index.ts +2 -0
  48. package/src/mddocs-entry.ts +16 -0
  49. package/src/test-utils/test-tree-expect.ts +7 -2
  50. package/src/types/core.ts +2 -0
  51. package/src/utilities/create-on-drop-handler.ts +2 -2
  52. package/src/utilities/insert-items-at-target.ts +2 -2
package/CHANGELOG.md CHANGED
@@ -1,10 +1,11 @@
1
1
  # @headless-tree/core
2
2
 
3
- ## 0.0.0-20250322153940
3
+ ## 0.0.0-20250330232313
4
4
 
5
5
  ### Patch Changes
6
6
 
7
7
  - 2af5668: Bug fix: Mutations to expanded tree items from outside will now trigger a rebuild of the tree structure (#65)
8
+ - 617faea: Support for keyboard-controlled drag-and-drop events
8
9
 
9
10
  ## 0.0.14
10
11
 
@@ -20,13 +20,13 @@ exports.dragAndDropFeature = {
20
20
  dnd: "setDndState",
21
21
  },
22
22
  treeInstance: {
23
- getDropTarget: ({ tree }) => {
23
+ getDragTarget: ({ tree }) => {
24
24
  var _a, _b;
25
25
  return (_b = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.dragTarget) !== null && _b !== void 0 ? _b : null;
26
26
  },
27
27
  getDragLineData: ({ tree }) => {
28
28
  var _a, _b, _c, _d, _e, _f;
29
- const target = tree.getDropTarget();
29
+ const target = tree.getDragTarget();
30
30
  const indent = ((_a = target === null || target === void 0 ? void 0 : target.item.getItemMeta().level) !== null && _a !== void 0 ? _a : 0) + 1;
31
31
  const treeBb = (_b = tree.getElement()) === null || _b === void 0 ? void 0 : _b.getBoundingClientRect();
32
32
  if (!target || !treeBb || !("childIndex" in target))
@@ -39,7 +39,7 @@ exports.dragAndDropFeature = {
39
39
  if (bb) {
40
40
  return {
41
41
  indent,
42
- top: bb.bottom - treeBb.bottom,
42
+ top: bb.bottom - treeBb.top,
43
43
  left: bb.left + leftOffset - treeBb.left,
44
44
  width: bb.width - leftOffset,
45
45
  };
@@ -104,7 +104,7 @@ exports.dragAndDropFeature = {
104
104
  return;
105
105
  }
106
106
  dataRef.current.lastDragCode = nextDragCode;
107
- const target = (0, utils_1.getDropTarget)(e, item, tree);
107
+ const target = (0, utils_1.getDragTarget)(e, item, tree);
108
108
  if (!((_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggedItems) &&
109
109
  (!e.dataTransfer ||
110
110
  !((_c = (_b = tree
@@ -134,7 +134,7 @@ exports.dragAndDropFeature = {
134
134
  }, onDrop: (e) => __awaiter(void 0, void 0, void 0, function* () {
135
135
  var _a, _b, _c;
136
136
  const dataRef = tree.getDataRef();
137
- const target = (0, utils_1.getDropTarget)(e, item, tree);
137
+ const target = (0, utils_1.getDragTarget)(e, item, tree);
138
138
  if (!(0, utils_1.canDrop)(e.dataTransfer, target, tree)) {
139
139
  return;
140
140
  }
@@ -150,20 +150,20 @@ exports.dragAndDropFeature = {
150
150
  yield ((_c = config.onDropForeignDragObject) === null || _c === void 0 ? void 0 : _c.call(config, e.dataTransfer, target));
151
151
  }
152
152
  }) })),
153
- isDropTarget: ({ tree, item }) => {
154
- const target = tree.getDropTarget();
153
+ isDragTarget: ({ tree, item }) => {
154
+ const target = tree.getDragTarget();
155
155
  return target ? target.item.getId() === item.getId() : false;
156
156
  },
157
- isDropTargetAbove: ({ tree, item }) => {
158
- const target = tree.getDropTarget();
157
+ isDragTargetAbove: ({ tree, item }) => {
158
+ const target = tree.getDragTarget();
159
159
  if (!target ||
160
160
  !("childIndex" in target) ||
161
161
  target.item !== item.getParent())
162
162
  return false;
163
163
  return target.childIndex === item.getItemMeta().posInSet;
164
164
  },
165
- isDropTargetBelow: ({ tree, item }) => {
166
- const target = tree.getDropTarget();
165
+ isDragTargetBelow: ({ tree, item }) => {
166
+ const target = tree.getDragTarget();
167
167
  if (!target ||
168
168
  !("childIndex" in target) ||
169
169
  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?: DropTarget<T>;
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 DropTarget<T> = {
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 DropTargetPosition {
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: DropTarget<T>) => boolean;
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: DropTarget<T>) => boolean;
50
- onDrop?: (items: ItemInstance<T>[], target: DropTarget<T>) => void | Promise<void>;
51
- onDropForeignDragObject?: (dataTransfer: DataTransfer, target: DropTarget<T>) => void | Promise<void>;
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
- getDropTarget: () => DropTarget<T> | null;
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
- isDropTarget: () => boolean;
61
- isDropTargetAbove: () => boolean;
62
- isDropTargetBelow: () => boolean;
60
+ isDragTarget: () => boolean;
61
+ isDragTargetAbove: () => boolean;
62
+ isDragTargetBelow: () => boolean;
63
63
  isDraggingOver: () => boolean;
64
64
  };
65
65
  hotkeys: never;
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DropTargetPosition = void 0;
4
- var DropTargetPosition;
5
- (function (DropTargetPosition) {
6
- DropTargetPosition["Top"] = "top";
7
- DropTargetPosition["Bottom"] = "bottom";
8
- DropTargetPosition["Item"] = "item";
9
- })(DropTargetPosition || (exports.DropTargetPosition = DropTargetPosition = {}));
3
+ exports.DragTargetPosition = void 0;
4
+ var DragTargetPosition;
5
+ (function (DragTargetPosition) {
6
+ DragTargetPosition["Top"] = "top";
7
+ DragTargetPosition["Bottom"] = "bottom";
8
+ DragTargetPosition["Item"] = "item";
9
+ })(DragTargetPosition || (exports.DragTargetPosition = DragTargetPosition = {}));
@@ -1,5 +1,20 @@
1
1
  import { ItemInstance, TreeInstance } from "../../types/core";
2
- import { DropTarget } from "./types";
3
- export declare const canDrop: (dataTransfer: DataTransfer | null, target: DropTarget<any>, tree: TreeInstance<any>) => boolean;
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
- export declare const getDropTarget: (e: any, item: ItemInstance<any>, tree: TreeInstance<any>, canReorder?: boolean | undefined) => DropTarget<any>;
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,12 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getDropTarget = exports.getDragCode = exports.canDrop = void 0;
3
+ exports.getDragTarget = exports.getReparentTarget = exports.getDragCode = exports.getInsertionIndex = exports.getItemDropCategory = exports.canDrop = exports.ItemDropCategory = void 0;
4
4
  var ItemDropCategory;
5
5
  (function (ItemDropCategory) {
6
6
  ItemDropCategory[ItemDropCategory["Item"] = 0] = "Item";
7
7
  ItemDropCategory[ItemDropCategory["ExpandedFolder"] = 1] = "ExpandedFolder";
8
8
  ItemDropCategory[ItemDropCategory["LastInGroup"] = 2] = "LastInGroup";
9
- })(ItemDropCategory || (ItemDropCategory = {}));
9
+ })(ItemDropCategory || (exports.ItemDropCategory = ItemDropCategory = {}));
10
10
  var PlacementType;
11
11
  (function (PlacementType) {
12
12
  PlacementType[PlacementType["ReorderAbove"] = 0] = "ReorderAbove";
@@ -15,7 +15,7 @@ var PlacementType;
15
15
  PlacementType[PlacementType["Reparent"] = 3] = "Reparent";
16
16
  })(PlacementType || (PlacementType = {}));
17
17
  const canDrop = (dataTransfer, target, tree) => {
18
- var _a, _b, _c, _d;
18
+ var _a, _b, _c;
19
19
  const draggedItems = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggedItems;
20
20
  const config = tree.getConfig();
21
21
  if (draggedItems && !((_c = (_b = config.canDrop) === null || _b === void 0 ? void 0 : _b.call(config, draggedItems, target)) !== null && _c !== void 0 ? _c : true)) {
@@ -28,7 +28,8 @@ const canDrop = (dataTransfer, target, tree) => {
28
28
  }
29
29
  if (!draggedItems &&
30
30
  dataTransfer &&
31
- !((_d = config.canDropForeignDragObject) === null || _d === void 0 ? void 0 : _d.call(config, dataTransfer, target))) {
31
+ config.canDropForeignDragObject &&
32
+ !config.canDropForeignDragObject(dataTransfer, target)) {
32
33
  return false;
33
34
  }
34
35
  return true;
@@ -44,6 +45,17 @@ const getItemDropCategory = (item) => {
44
45
  }
45
46
  return ItemDropCategory.Item;
46
47
  };
48
+ exports.getItemDropCategory = getItemDropCategory;
49
+ const getInsertionIndex = (children, childIndex, draggedItems) => {
50
+ var _a;
51
+ const numberOfDragItemsBeforeTarget = (_a = children
52
+ .slice(0, childIndex)
53
+ .reduce((counter, child) => child && (draggedItems === null || draggedItems === void 0 ? void 0 : draggedItems.some((i) => i.getId() === child.getId()))
54
+ ? ++counter
55
+ : counter, 0)) !== null && _a !== void 0 ? _a : 0;
56
+ return childIndex - numberOfDragItemsBeforeTarget;
57
+ };
58
+ exports.getInsertionIndex = getInsertionIndex;
47
59
  const getTargetPlacement = (e, item, tree, canMakeChild) => {
48
60
  var _a, _b, _c, _d, _e;
49
61
  const config = tree.getConfig();
@@ -55,7 +67,7 @@ const getTargetPlacement = (e, item, tree, canMakeChild) => {
55
67
  const bb = (_a = item.getElement()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
56
68
  const topPercent = bb ? (e.clientY - bb.top) / bb.height : 0.5;
57
69
  const leftPixels = bb ? e.clientX - bb.left : 0;
58
- const targetDropCategory = getItemDropCategory(item);
70
+ const targetDropCategory = (0, exports.getItemDropCategory)(item);
59
71
  const reorderAreaPercentage = !canMakeChild
60
72
  ? 0.5
61
73
  : (_b = config.reorderAreaPercentage) !== null && _b !== void 0 ? _b : 0.3;
@@ -106,9 +118,25 @@ const getNthParent = (item, n) => {
106
118
  }
107
119
  return getNthParent(item.getParent(), n);
108
120
  };
109
- const getDropTarget = (e, item, tree, canReorder = tree.getConfig().canReorder) => {
110
- var _a, _b, _c;
111
- const draggedItems = (_b = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggedItems) !== null && _b !== void 0 ? _b : [];
121
+ /** @param item refers to the bottom-most item of the container, at which bottom is being reparented on (e.g. root-1-2-6) */
122
+ const getReparentTarget = (item, reparentLevel, draggedItems) => {
123
+ const itemMeta = item.getItemMeta();
124
+ const reparentedTarget = getNthParent(item, reparentLevel - 1);
125
+ const targetItemAbove = getNthParent(item, reparentLevel); // .getItemBelow()!;
126
+ const targetIndex = targetItemAbove.getIndexInParent() + 1;
127
+ // TODO possibly count items dragged out above the new target
128
+ return {
129
+ item: reparentedTarget,
130
+ childIndex: targetIndex,
131
+ insertionIndex: (0, exports.getInsertionIndex)(reparentedTarget.getChildren(), targetIndex, draggedItems),
132
+ dragLineIndex: itemMeta.index + 1,
133
+ dragLineLevel: reparentLevel,
134
+ };
135
+ };
136
+ exports.getReparentTarget = getReparentTarget;
137
+ const getDragTarget = (e, item, tree, canReorder = tree.getConfig().canReorder) => {
138
+ var _a;
139
+ const draggedItems = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggedItems;
112
140
  const itemMeta = item.getItemMeta();
113
141
  const parent = item.getParent();
114
142
  const itemTarget = { item };
@@ -123,8 +151,8 @@ const getDropTarget = (e, item, tree, canReorder = tree.getConfig().canReorder)
123
151
  return parentTarget;
124
152
  }
125
153
  if (!canReorder && parent && !canBecomeSibling) {
126
- // TODO! this breaks in story DND/Can Drop. Maybe move this logic into a composable DropTargetStrategy[] ?
127
- return (0, exports.getDropTarget)(e, parent, tree, false);
154
+ // TODO! this breaks in story DND/Can Drop. Maybe move this logic into a composable DragTargetStrategy[] ?
155
+ return (0, exports.getDragTarget)(e, parent, tree, false);
128
156
  }
129
157
  if (!parent) {
130
158
  // Shouldn't happen, but if dropped "next" to root item, just drop it inside
@@ -134,36 +162,20 @@ const getDropTarget = (e, item, tree, canReorder = tree.getConfig().canReorder)
134
162
  return itemTarget;
135
163
  }
136
164
  if (!canBecomeSibling) {
137
- return (0, exports.getDropTarget)(e, parent, tree, false);
165
+ return (0, exports.getDragTarget)(e, parent, tree, false);
138
166
  }
139
167
  if (placement.type === PlacementType.Reparent) {
140
- const reparentedTarget = getNthParent(item, placement.reparentLevel - 1);
141
- const targetItemAbove = getNthParent(item, placement.reparentLevel); // .getItemBelow()!;
142
- const targetIndex = targetItemAbove.getIndexInParent() + 1;
143
- // TODO possibly count items dragged out above the new target
144
- return {
145
- item: reparentedTarget,
146
- childIndex: targetIndex,
147
- insertionIndex: targetIndex,
148
- dragLineIndex: itemMeta.index + 1,
149
- dragLineLevel: placement.reparentLevel,
150
- };
168
+ return (0, exports.getReparentTarget)(item, placement.reparentLevel, draggedItems);
151
169
  }
152
170
  const maybeAddOneForBelow = placement.type === PlacementType.ReorderAbove ? 0 : 1;
153
171
  const childIndex = item.getIndexInParent() + maybeAddOneForBelow;
154
- const numberOfDragItemsBeforeTarget = (_c = parent
155
- .getChildren()
156
- .slice(0, childIndex)
157
- .reduce((counter, child) => child && (draggedItems === null || draggedItems === void 0 ? void 0 : draggedItems.some((i) => i.getId() === child.getId()))
158
- ? ++counter
159
- : counter, 0)) !== null && _c !== void 0 ? _c : 0;
160
172
  return {
161
173
  item: parent,
162
174
  dragLineIndex: itemMeta.index + maybeAddOneForBelow,
163
175
  dragLineLevel: itemMeta.level,
164
176
  childIndex,
165
177
  // TODO performance could be improved by computing this only when dragcode changed
166
- insertionIndex: childIndex - numberOfDragItemsBeforeTarget,
178
+ insertionIndex: (0, exports.getInsertionIndex)(parent.getChildren(), childIndex, draggedItems),
167
179
  };
168
180
  };
169
- exports.getDropTarget = getDropTarget;
181
+ exports.getDragTarget = getDragTarget;
@@ -30,6 +30,7 @@ exports.hotkeysCoreFeature = {
30
30
  (_a = (_e = data.current).pressedKeys) !== null && _a !== void 0 ? _a : (_e.pressedKeys = new Set());
31
31
  const newMatch = !data.current.pressedKeys.has(e.key);
32
32
  data.current.pressedKeys.add(e.key);
33
+ console.log("HOTKEYS", data.current.pressedKeys);
33
34
  const hotkeyName = findHotkeyMatch(data.current.pressedKeys, tree, tree.getHotkeyPresets(), tree.getConfig().hotkeys);
34
35
  if (!hotkeyName)
35
36
  return;
@@ -0,0 +1,2 @@
1
+ import { FeatureImplementation } from "../../types/core";
2
+ export declare const keyboardDragAndDropFeature: FeatureImplementation;
@@ -0,0 +1,207 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.keyboardDragAndDropFeature = void 0;
13
+ const utils_1 = require("../drag-and-drop/utils");
14
+ const utils_2 = require("../../utils");
15
+ const types_1 = require("./types");
16
+ const getNextDragTarget = (tree, isUp, dragTarget) => {
17
+ var _a, _b, _c, _d;
18
+ const direction = isUp ? 0 : 1;
19
+ const draggedItems = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggedItems;
20
+ // currently hovering between items
21
+ if ("childIndex" in dragTarget) {
22
+ // TODO move check in reusable function
23
+ const parent = dragTarget.item.getParent();
24
+ const targetedItem = tree.getItems()[dragTarget.dragLineIndex - 1]; // item above dragline
25
+ const targetCategory = targetedItem
26
+ ? (0, utils_1.getItemDropCategory)(targetedItem)
27
+ : utils_1.ItemDropCategory.Item;
28
+ const maxLevel = (_b = targetedItem === null || targetedItem === void 0 ? void 0 : targetedItem.getItemMeta().level) !== null && _b !== void 0 ? _b : 0;
29
+ 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;
30
+ // reparenting
31
+ if (targetCategory === utils_1.ItemDropCategory.LastInGroup) {
32
+ if (isUp && dragTarget.dragLineLevel < maxLevel) {
33
+ return (0, utils_1.getReparentTarget)(targetedItem, dragTarget.dragLineLevel + 1, draggedItems);
34
+ }
35
+ if (!isUp && dragTarget.dragLineLevel > minLevel && parent) {
36
+ return (0, utils_1.getReparentTarget)(targetedItem, dragTarget.dragLineLevel - 1, draggedItems);
37
+ }
38
+ }
39
+ const newIndex = dragTarget.dragLineIndex - 1 + direction;
40
+ const item = tree.getItems()[newIndex];
41
+ return item ? { item } : undefined;
42
+ }
43
+ // moving upwards outside of an open folder
44
+ const targetingExpandedFolder = (0, utils_1.getItemDropCategory)(dragTarget.item) === utils_1.ItemDropCategory.ExpandedFolder;
45
+ if (targetingExpandedFolder && !isUp) {
46
+ return {
47
+ item: dragTarget.item,
48
+ childIndex: 0,
49
+ insertionIndex: (0, utils_1.getInsertionIndex)(dragTarget.item.getChildren(), 0, draggedItems),
50
+ dragLineIndex: dragTarget.item.getItemMeta().index + direction,
51
+ dragLineLevel: dragTarget.item.getItemMeta().level + 1,
52
+ };
53
+ }
54
+ // currently hovering over item
55
+ const childIndex = dragTarget.item.getIndexInParent() + direction;
56
+ return {
57
+ item: dragTarget.item.getParent(),
58
+ childIndex,
59
+ insertionIndex: (0, utils_1.getInsertionIndex)(dragTarget.item.getParent().getChildren(), childIndex, draggedItems),
60
+ dragLineIndex: dragTarget.item.getItemMeta().index + direction,
61
+ dragLineLevel: dragTarget.item.getItemMeta().level,
62
+ };
63
+ };
64
+ const getNextValidDragTarget = (tree, isUp, previousTarget) => {
65
+ var _a, _b;
66
+ if (previousTarget === void 0) { previousTarget = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.dragTarget; }
67
+ if (!previousTarget)
68
+ return undefined;
69
+ const nextTarget = getNextDragTarget(tree, isUp, previousTarget);
70
+ const dataTransfer = (_b = tree.getDataRef().current.kDndDataTransfer) !== null && _b !== void 0 ? _b : null;
71
+ if (!nextTarget)
72
+ return undefined;
73
+ if ((0, utils_1.canDrop)(dataTransfer, nextTarget, tree)) {
74
+ return nextTarget;
75
+ }
76
+ return getNextValidDragTarget(tree, isUp, nextTarget);
77
+ };
78
+ const updateScroll = (tree) => {
79
+ const state = tree.getState().dnd;
80
+ if (!(state === null || state === void 0 ? void 0 : state.dragTarget) || "childIndex" in state.dragTarget)
81
+ return;
82
+ state.dragTarget.item.scrollTo({ block: "nearest", inline: "nearest" });
83
+ };
84
+ const initiateDrag = (tree, draggedItems, dataTransfer) => {
85
+ var _a, _b;
86
+ const focusedItem = tree.getFocusedItem();
87
+ const { canDrag } = tree.getConfig();
88
+ if (draggedItems && canDrag && !canDrag(draggedItems)) {
89
+ return;
90
+ }
91
+ if (draggedItems) {
92
+ tree.applySubStateUpdate("dnd", { draggedItems });
93
+ // getNextValidDragTarget->canDrop needs the draggedItems in state
94
+ (_b = (_a = tree.getConfig()).onStartKeyboardDrag) === null || _b === void 0 ? void 0 : _b.call(_a, draggedItems);
95
+ }
96
+ else if (dataTransfer) {
97
+ tree.getDataRef().current.kDndDataTransfer = dataTransfer;
98
+ }
99
+ const dragTarget = getNextValidDragTarget(tree, false, {
100
+ item: focusedItem,
101
+ });
102
+ if (!dragTarget)
103
+ return;
104
+ tree.applySubStateUpdate("dnd", {
105
+ draggedItems,
106
+ dragTarget,
107
+ });
108
+ tree.applySubStateUpdate("assistiveDndState", types_1.AssistiveDndState.Started);
109
+ updateScroll(tree);
110
+ };
111
+ const moveDragPosition = (tree, isUp) => {
112
+ var _a;
113
+ const dragTarget = getNextValidDragTarget(tree, isUp);
114
+ if (!dragTarget)
115
+ return;
116
+ tree.applySubStateUpdate("dnd", {
117
+ draggedItems: (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggedItems,
118
+ dragTarget,
119
+ });
120
+ tree.applySubStateUpdate("assistiveDndState", types_1.AssistiveDndState.Dragging);
121
+ if (!("childIndex" in dragTarget)) {
122
+ dragTarget.item.setFocused();
123
+ }
124
+ updateScroll(tree);
125
+ };
126
+ exports.keyboardDragAndDropFeature = {
127
+ key: "keyboard-drag-and-drop",
128
+ deps: ["drag-and-drop"],
129
+ getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setAssistiveDndState: (0, utils_2.makeStateUpdater)("assistiveDndState", tree) }, defaultConfig)),
130
+ stateHandlerNames: {
131
+ assistiveDndState: "setAssistiveDndState",
132
+ },
133
+ treeInstance: {
134
+ startKeyboardDrag: ({ tree }, draggedItems) => {
135
+ initiateDrag(tree, draggedItems, undefined);
136
+ },
137
+ startKeyboardDragOnForeignObject: ({ tree }, dataTransfer) => {
138
+ initiateDrag(tree, undefined, dataTransfer);
139
+ },
140
+ stopKeyboardDrag: ({ tree }) => {
141
+ tree.getDataRef().current.kDndDataTransfer = undefined;
142
+ tree.applySubStateUpdate("dnd", null);
143
+ tree.applySubStateUpdate("assistiveDndState", types_1.AssistiveDndState.None);
144
+ },
145
+ },
146
+ hotkeys: {
147
+ startDrag: {
148
+ hotkey: "Control+Shift+D",
149
+ preventDefault: true,
150
+ isEnabled: (tree) => !tree.getState().dnd,
151
+ handler: (_, tree) => {
152
+ tree.startKeyboardDrag(tree.getSelectedItems());
153
+ },
154
+ },
155
+ dragUp: {
156
+ hotkey: "ArrowUp",
157
+ preventDefault: true,
158
+ isEnabled: (tree) => !!tree.getState().dnd,
159
+ handler: (_, tree) => {
160
+ moveDragPosition(tree, true);
161
+ },
162
+ },
163
+ dragDown: {
164
+ hotkey: "ArrowDown",
165
+ preventDefault: true,
166
+ isEnabled: (tree) => !!tree.getState().dnd,
167
+ handler: (_, tree) => {
168
+ moveDragPosition(tree, false);
169
+ },
170
+ },
171
+ cancelDrag: {
172
+ hotkey: "Escape",
173
+ isEnabled: (tree) => !!tree.getState().dnd,
174
+ handler: (_, tree) => {
175
+ tree.stopKeyboardDrag();
176
+ },
177
+ },
178
+ completeDrag: {
179
+ hotkey: "Enter",
180
+ preventDefault: true,
181
+ isEnabled: (tree) => !!tree.getState().dnd,
182
+ handler: (e, tree) => __awaiter(void 0, void 0, void 0, function* () {
183
+ var _a, _b, _c, _d;
184
+ e.stopPropagation();
185
+ // TODO copied from keyboard onDrop, unify them
186
+ const dataRef = tree.getDataRef();
187
+ const target = tree.getDragTarget();
188
+ const dataTransfer = (_a = dataRef.current.kDndDataTransfer) !== null && _a !== void 0 ? _a : null;
189
+ if (!target || !(0, utils_1.canDrop)(dataTransfer, target, tree)) {
190
+ return;
191
+ }
192
+ const config = tree.getConfig();
193
+ const draggedItems = (_b = tree.getState().dnd) === null || _b === void 0 ? void 0 : _b.draggedItems;
194
+ dataRef.current.lastDragCode = undefined;
195
+ tree.applySubStateUpdate("dnd", null);
196
+ if (draggedItems) {
197
+ yield ((_c = config.onDrop) === null || _c === void 0 ? void 0 : _c.call(config, draggedItems, target));
198
+ tree.getItemInstance(draggedItems[0].getId()).setFocused();
199
+ }
200
+ else if (dataTransfer) {
201
+ yield ((_d = config.onDropForeignDragObject) === null || _d === void 0 ? void 0 : _d.call(config, dataTransfer, target));
202
+ }
203
+ tree.applySubStateUpdate("assistiveDndState", types_1.AssistiveDndState.Completed);
204
+ }),
205
+ },
206
+ },
207
+ };
@@ -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,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AssistiveDndState = void 0;
4
+ var AssistiveDndState;
5
+ (function (AssistiveDndState) {
6
+ AssistiveDndState[AssistiveDndState["None"] = 0] = "None";
7
+ AssistiveDndState[AssistiveDndState["Started"] = 1] = "Started";
8
+ AssistiveDndState[AssistiveDndState["Dragging"] = 2] = "Dragging";
9
+ AssistiveDndState[AssistiveDndState["Completed"] = 3] = "Completed";
10
+ AssistiveDndState[AssistiveDndState["Aborted"] = 4] = "Aborted";
11
+ })(AssistiveDndState || (exports.AssistiveDndState = AssistiveDndState = {}));
@@ -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";
package/lib/cjs/index.js CHANGED
@@ -18,6 +18,7 @@ __exportStar(require("./types/core"), exports);
18
18
  __exportStar(require("./core/create-tree"), exports);
19
19
  __exportStar(require("./features/tree/types"), exports);
20
20
  __exportStar(require("./features/drag-and-drop/types"), exports);
21
+ __exportStar(require("./features/keyboard-drag-and-drop/types"), exports);
21
22
  __exportStar(require("./features/selection/types"), exports);
22
23
  __exportStar(require("./features/async-data-loader/types"), exports);
23
24
  __exportStar(require("./features/sync-data-loader/types"), exports);
@@ -31,6 +32,7 @@ __exportStar(require("./features/hotkeys-core/feature"), exports);
31
32
  __exportStar(require("./features/async-data-loader/feature"), exports);
32
33
  __exportStar(require("./features/sync-data-loader/feature"), exports);
33
34
  __exportStar(require("./features/drag-and-drop/feature"), exports);
35
+ __exportStar(require("./features/keyboard-drag-and-drop/feature"), exports);
34
36
  __exportStar(require("./features/search/feature"), exports);
35
37
  __exportStar(require("./features/renaming/feature"), exports);
36
38
  __exportStar(require("./features/expand-all/feature"), exports);
@@ -9,6 +9,7 @@ import { SelectionFeatureDef } from "./features/selection/types";
9
9
  import { SyncDataLoaderFeatureDef } from "./features/sync-data-loader/types";
10
10
  import { TreeFeatureDef } from "./features/tree/types";
11
11
  import { PropMemoizationFeatureDef } from "./features/prop-memoization/types";
12
+ import { KeyboardDragAndDropFeatureDef } from "./features/keyboard-drag-and-drop/types";
12
13
  export * from ".";
13
14
  /** @interface */
14
15
  export type AsyncDataLoaderFeatureConfig<T> = AsyncDataLoaderFeatureDef<T>["config"];
@@ -29,6 +30,15 @@ export type DragAndDropFeatureTreeInstance<T> = DragAndDropFeatureDef<T>["treeIn
29
30
  export type DragAndDropFeatureItemInstance<T> = DragAndDropFeatureDef<T>["itemInstance"];
30
31
  export type DragAndDropFeatureHotkeys<T> = DragAndDropFeatureDef<T>["hotkeys"];
31
32
  /** @interface */
33
+ export type KeyboardDragAndDropFeatureConfig<T> = KeyboardDragAndDropFeatureDef<T>["config"];
34
+ /** @interface */
35
+ export type KeyboardDragAndDropFeatureState<T> = KeyboardDragAndDropFeatureDef<T>["state"];
36
+ /** @interface */
37
+ export type KeyboardDragAndDropFeatureTreeInstance<T> = KeyboardDragAndDropFeatureDef<T>["treeInstance"];
38
+ /** @interface */
39
+ export type KeyboardDragAndDropFeatureItemInstance<T> = KeyboardDragAndDropFeatureDef<T>["itemInstance"];
40
+ export type KeyboardDragAndDropFeatureHotkeys<T> = KeyboardDragAndDropFeatureDef<T>["hotkeys"];
41
+ /** @interface */
32
42
  export type ExpandAllFeatureConfig = ExpandAllFeatureDef["config"];
33
43
  /** @interface */
34
44
  export type ExpandAllFeatureState = ExpandAllFeatureDef["state"];