@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.
Files changed (125) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/lib/cjs/core/create-tree.js +13 -4
  3. package/lib/cjs/features/async-data-loader/feature.js +73 -48
  4. package/lib/cjs/features/async-data-loader/types.d.ts +17 -14
  5. package/lib/cjs/features/drag-and-drop/feature.js +98 -93
  6. package/lib/cjs/features/drag-and-drop/types.d.ts +17 -29
  7. package/lib/cjs/features/drag-and-drop/types.js +7 -7
  8. package/lib/cjs/features/drag-and-drop/utils.d.ts +25 -3
  9. package/lib/cjs/features/drag-and-drop/utils.js +51 -51
  10. package/lib/cjs/features/expand-all/feature.js +26 -3
  11. package/lib/cjs/features/expand-all/types.d.ts +3 -1
  12. package/lib/cjs/features/hotkeys-core/feature.js +7 -3
  13. package/lib/cjs/features/hotkeys-core/types.d.ts +4 -5
  14. package/lib/cjs/features/keyboard-drag-and-drop/feature.d.ts +2 -0
  15. package/lib/cjs/features/keyboard-drag-and-drop/feature.js +206 -0
  16. package/lib/cjs/features/keyboard-drag-and-drop/types.d.ts +27 -0
  17. package/lib/cjs/features/keyboard-drag-and-drop/types.js +11 -0
  18. package/lib/cjs/features/prop-memoization/feature.js +33 -11
  19. package/lib/cjs/features/prop-memoization/types.d.ts +8 -3
  20. package/lib/cjs/features/renaming/feature.js +1 -1
  21. package/lib/cjs/features/search/feature.js +2 -0
  22. package/lib/cjs/features/search/types.d.ts +2 -2
  23. package/lib/cjs/features/selection/feature.js +4 -4
  24. package/lib/cjs/features/selection/types.d.ts +1 -1
  25. package/lib/cjs/features/sync-data-loader/feature.js +31 -5
  26. package/lib/cjs/features/sync-data-loader/types.d.ts +5 -5
  27. package/lib/cjs/features/tree/feature.js +4 -9
  28. package/lib/cjs/features/tree/types.d.ts +7 -5
  29. package/lib/cjs/index.d.ts +2 -0
  30. package/lib/cjs/index.js +2 -0
  31. package/lib/cjs/mddocs-entry.d.ts +10 -0
  32. package/lib/cjs/test-utils/test-tree-do.d.ts +2 -2
  33. package/lib/cjs/test-utils/test-tree-do.js +19 -6
  34. package/lib/cjs/test-utils/test-tree-expect.d.ts +5 -3
  35. package/lib/cjs/test-utils/test-tree-expect.js +3 -0
  36. package/lib/cjs/test-utils/test-tree.d.ts +2 -1
  37. package/lib/cjs/test-utils/test-tree.js +24 -21
  38. package/lib/cjs/types/core.d.ts +2 -1
  39. package/lib/cjs/utilities/create-on-drop-handler.d.ts +2 -2
  40. package/lib/cjs/utilities/create-on-drop-handler.js +13 -4
  41. package/lib/cjs/utilities/insert-items-at-target.d.ts +2 -2
  42. package/lib/cjs/utilities/insert-items-at-target.js +21 -12
  43. package/lib/cjs/utilities/remove-items-from-parents.d.ts +1 -1
  44. package/lib/cjs/utilities/remove-items-from-parents.js +12 -3
  45. package/lib/esm/core/create-tree.js +13 -4
  46. package/lib/esm/features/async-data-loader/feature.js +73 -48
  47. package/lib/esm/features/async-data-loader/types.d.ts +17 -14
  48. package/lib/esm/features/drag-and-drop/feature.js +99 -94
  49. package/lib/esm/features/drag-and-drop/types.d.ts +17 -29
  50. package/lib/esm/features/drag-and-drop/types.js +6 -6
  51. package/lib/esm/features/drag-and-drop/utils.d.ts +25 -3
  52. package/lib/esm/features/drag-and-drop/utils.js +45 -49
  53. package/lib/esm/features/expand-all/feature.js +26 -3
  54. package/lib/esm/features/expand-all/types.d.ts +3 -1
  55. package/lib/esm/features/hotkeys-core/feature.js +7 -3
  56. package/lib/esm/features/hotkeys-core/types.d.ts +4 -5
  57. package/lib/esm/features/keyboard-drag-and-drop/feature.d.ts +2 -0
  58. package/lib/esm/features/keyboard-drag-and-drop/feature.js +203 -0
  59. package/lib/esm/features/keyboard-drag-and-drop/types.d.ts +27 -0
  60. package/lib/esm/features/keyboard-drag-and-drop/types.js +8 -0
  61. package/lib/esm/features/prop-memoization/feature.js +33 -11
  62. package/lib/esm/features/prop-memoization/types.d.ts +8 -3
  63. package/lib/esm/features/renaming/feature.js +1 -1
  64. package/lib/esm/features/search/feature.js +2 -0
  65. package/lib/esm/features/search/types.d.ts +2 -2
  66. package/lib/esm/features/selection/feature.js +4 -4
  67. package/lib/esm/features/selection/types.d.ts +1 -1
  68. package/lib/esm/features/sync-data-loader/feature.js +31 -5
  69. package/lib/esm/features/sync-data-loader/types.d.ts +5 -5
  70. package/lib/esm/features/tree/feature.js +4 -9
  71. package/lib/esm/features/tree/types.d.ts +7 -5
  72. package/lib/esm/index.d.ts +2 -0
  73. package/lib/esm/index.js +2 -0
  74. package/lib/esm/mddocs-entry.d.ts +10 -0
  75. package/lib/esm/test-utils/test-tree-do.d.ts +2 -2
  76. package/lib/esm/test-utils/test-tree-do.js +19 -6
  77. package/lib/esm/test-utils/test-tree-expect.d.ts +5 -3
  78. package/lib/esm/test-utils/test-tree-expect.js +3 -0
  79. package/lib/esm/test-utils/test-tree.d.ts +2 -1
  80. package/lib/esm/test-utils/test-tree.js +24 -21
  81. package/lib/esm/types/core.d.ts +2 -1
  82. package/lib/esm/utilities/create-on-drop-handler.d.ts +2 -2
  83. package/lib/esm/utilities/create-on-drop-handler.js +13 -4
  84. package/lib/esm/utilities/insert-items-at-target.d.ts +2 -2
  85. package/lib/esm/utilities/insert-items-at-target.js +21 -12
  86. package/lib/esm/utilities/remove-items-from-parents.d.ts +1 -1
  87. package/lib/esm/utilities/remove-items-from-parents.js +12 -3
  88. package/package.json +2 -2
  89. package/src/core/core.spec.ts +31 -0
  90. package/src/core/create-tree.ts +15 -5
  91. package/src/features/async-data-loader/async-data-loader.spec.ts +10 -6
  92. package/src/features/async-data-loader/feature.ts +76 -48
  93. package/src/features/async-data-loader/types.ts +18 -11
  94. package/src/features/drag-and-drop/drag-and-drop.spec.ts +75 -89
  95. package/src/features/drag-and-drop/feature.ts +26 -22
  96. package/src/features/drag-and-drop/types.ts +23 -35
  97. package/src/features/drag-and-drop/utils.ts +70 -57
  98. package/src/features/expand-all/feature.ts +29 -5
  99. package/src/features/expand-all/types.ts +3 -1
  100. package/src/features/hotkeys-core/feature.ts +4 -0
  101. package/src/features/hotkeys-core/types.ts +4 -13
  102. package/src/features/keyboard-drag-and-drop/feature.ts +255 -0
  103. package/src/features/keyboard-drag-and-drop/keyboard-drag-and-drop.spec.ts +402 -0
  104. package/src/features/keyboard-drag-and-drop/types.ts +30 -0
  105. package/src/features/prop-memoization/feature.ts +27 -8
  106. package/src/features/prop-memoization/prop-memoization.spec.ts +2 -2
  107. package/src/features/prop-memoization/types.ts +8 -3
  108. package/src/features/renaming/feature.ts +8 -2
  109. package/src/features/search/feature.ts +2 -0
  110. package/src/features/search/types.ts +2 -2
  111. package/src/features/selection/feature.ts +4 -4
  112. package/src/features/selection/types.ts +1 -1
  113. package/src/features/sync-data-loader/feature.ts +26 -7
  114. package/src/features/sync-data-loader/types.ts +5 -5
  115. package/src/features/tree/feature.ts +8 -13
  116. package/src/features/tree/types.ts +7 -5
  117. package/src/index.ts +2 -0
  118. package/src/mddocs-entry.ts +16 -0
  119. package/src/test-utils/test-tree-do.ts +3 -3
  120. package/src/test-utils/test-tree-expect.ts +7 -2
  121. package/src/test-utils/test-tree.ts +26 -22
  122. package/src/types/core.ts +2 -0
  123. package/src/utilities/create-on-drop-handler.ts +4 -4
  124. package/src/utilities/insert-items-at-target.ts +18 -14
  125. package/src/utilities/remove-items-from-parents.ts +6 -3
@@ -1,20 +1,20 @@
1
1
  import { ItemInstance, SetStateFn } from "../../types/core";
2
- export type DndDataRef = {
2
+ export interface DndDataRef {
3
3
  lastDragCode?: string;
4
4
  lastAllowDrop?: boolean;
5
- };
6
- export type DndState<T> = {
5
+ }
6
+ export interface DndState<T> {
7
7
  draggedItems?: ItemInstance<T>[];
8
8
  draggingOverItem?: ItemInstance<T>;
9
- dragTarget?: DropTarget<T>;
10
- };
11
- export type DragLineData = {
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 DropTarget<T> = {
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 DropTargetPosition {
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: DropTarget<T>) => boolean;
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: DropTarget<T>) => boolean;
55
- onDrop?: (items: ItemInstance<T>[], target: DropTarget<T>) => void;
56
- onDropForeignDragObject?: (dataTransfer: DataTransfer, target: DropTarget<T>) => void;
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
- getDropTarget: () => DropTarget<T> | null;
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
- isDropTarget: () => boolean;
73
- isDropTargetAbove: () => boolean;
74
- isDropTargetBelow: () => boolean;
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 DropTargetPosition;
2
- (function (DropTargetPosition) {
3
- DropTargetPosition["Top"] = "top";
4
- DropTargetPosition["Bottom"] = "bottom";
5
- DropTargetPosition["Item"] = "item";
6
- })(DropTargetPosition || (DropTargetPosition = {}));
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 { 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 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
- export declare const getDropTarget: (e: any, item: ItemInstance<any>, tree: TreeInstance<any>, canReorder?: boolean | undefined) => DropTarget<any>;
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, _d;
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
- !((_d = config.canDropForeignDragObject) === null || _d === void 0 ? void 0 : _d.call(config, dataTransfer, target))) {
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() === parent.getItemMeta().setSize - 1) {
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.pageY - bb.top) / bb.height : 0.5;
53
- const leftPixels = bb ? e.pageX - bb.left : 0;
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
- export const getDropTarget = (e, item, tree, canReorder = tree.getConfig().canReorder) => {
104
- var _a, _b, _c;
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 parent = item.getParent();
108
- const itemTarget = {
109
- item,
110
- childIndex: null,
111
- insertionIndex: null,
112
- dragLineIndex: null,
113
- dragLineLevel: null,
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
- const parentTarget = parent
116
- ? {
117
- item: parent,
118
- childIndex: null,
119
- insertionIndex: null,
120
- dragLineIndex: null,
121
- dragLineLevel: null,
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 DropTargetStrategy[] ?
135
- return getDropTarget(e, parent, tree, false);
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 getDropTarget(e, parent, tree, false);
157
+ return getDragTarget(e, parent, tree, false);
146
158
  }
147
159
  if (placement.type === PlacementType.Reparent) {
148
- const reparentedTarget = getNthParent(item, placement.reparentLevel - 1);
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 - numberOfDragItemsBeforeTarget,
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 poll(() => !tree.getState().loadingItems.includes(item.getId()));
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 poll(() => !tree.getState().loadingItems.includes(child.getId()));
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: never;
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 _c;
25
- (_a = (_c = data.current).pressedKeys) !== null && _a !== void 0 ? _a : (_c.pressedKeys = new Set());
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, ItemInstance, TreeInstance } from "../../types/core";
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 type HotkeysCoreDataRef = {
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, element: HTMLElement) => void;
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,2 @@
1
+ import { FeatureImplementation } from "../../types/core";
2
+ export declare const keyboardDragAndDropFeature: FeatureImplementation;
@@ -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
+ };