@headless-tree/core 0.0.5 → 0.0.7

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 (93) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/lib/cjs/core/create-tree.js +34 -12
  3. package/lib/cjs/features/async-data-loader/feature.js +30 -22
  4. package/lib/cjs/features/drag-and-drop/feature.js +58 -18
  5. package/lib/cjs/features/drag-and-drop/types.d.ts +21 -1
  6. package/lib/cjs/features/drag-and-drop/utils.d.ts +1 -1
  7. package/lib/cjs/features/drag-and-drop/utils.js +34 -17
  8. package/lib/cjs/features/expand-all/feature.js +12 -10
  9. package/lib/cjs/features/expand-all/types.d.ts +2 -2
  10. package/lib/cjs/features/hotkeys-core/feature.js +0 -1
  11. package/lib/cjs/features/main/types.d.ts +3 -1
  12. package/lib/cjs/features/renaming/feature.js +10 -11
  13. package/lib/cjs/features/search/feature.js +21 -8
  14. package/lib/cjs/features/search/types.d.ts +1 -1
  15. package/lib/cjs/features/selection/feature.js +6 -5
  16. package/lib/cjs/features/sync-data-loader/feature.js +6 -1
  17. package/lib/cjs/features/sync-data-loader/types.d.ts +1 -1
  18. package/lib/cjs/features/tree/feature.js +23 -24
  19. package/lib/cjs/features/tree/types.d.ts +2 -2
  20. package/lib/cjs/index.d.ts +3 -1
  21. package/lib/cjs/index.js +3 -1
  22. package/lib/cjs/types/core.d.ts +3 -4
  23. package/lib/cjs/utilities/create-on-drop-handler.d.ts +3 -0
  24. package/lib/cjs/utilities/create-on-drop-handler.js +11 -0
  25. package/lib/cjs/utilities/insert-items-at-target.d.ts +3 -0
  26. package/lib/cjs/utilities/insert-items-at-target.js +24 -0
  27. package/lib/cjs/utilities/remove-items-from-parents.d.ts +2 -0
  28. package/lib/cjs/utilities/remove-items-from-parents.js +17 -0
  29. package/lib/cjs/utils.d.ts +1 -4
  30. package/lib/cjs/utils.js +1 -53
  31. package/lib/esm/core/create-tree.js +34 -12
  32. package/lib/esm/features/async-data-loader/feature.js +30 -22
  33. package/lib/esm/features/drag-and-drop/feature.js +58 -18
  34. package/lib/esm/features/drag-and-drop/types.d.ts +21 -1
  35. package/lib/esm/features/drag-and-drop/utils.d.ts +1 -1
  36. package/lib/esm/features/drag-and-drop/utils.js +35 -18
  37. package/lib/esm/features/expand-all/feature.js +12 -10
  38. package/lib/esm/features/expand-all/types.d.ts +2 -2
  39. package/lib/esm/features/hotkeys-core/feature.js +0 -1
  40. package/lib/esm/features/main/types.d.ts +3 -1
  41. package/lib/esm/features/renaming/feature.js +10 -11
  42. package/lib/esm/features/search/feature.js +21 -8
  43. package/lib/esm/features/search/types.d.ts +1 -1
  44. package/lib/esm/features/selection/feature.js +6 -5
  45. package/lib/esm/features/sync-data-loader/feature.js +6 -1
  46. package/lib/esm/features/sync-data-loader/types.d.ts +1 -1
  47. package/lib/esm/features/tree/feature.js +23 -24
  48. package/lib/esm/features/tree/types.d.ts +2 -2
  49. package/lib/esm/index.d.ts +3 -1
  50. package/lib/esm/index.js +3 -1
  51. package/lib/esm/types/core.d.ts +3 -4
  52. package/lib/esm/utilities/create-on-drop-handler.d.ts +3 -0
  53. package/lib/esm/utilities/create-on-drop-handler.js +7 -0
  54. package/lib/esm/utilities/insert-items-at-target.d.ts +3 -0
  55. package/lib/esm/utilities/insert-items-at-target.js +20 -0
  56. package/lib/esm/utilities/remove-items-from-parents.d.ts +2 -0
  57. package/lib/esm/utilities/remove-items-from-parents.js +13 -0
  58. package/lib/esm/utils.d.ts +1 -4
  59. package/lib/esm/utils.js +0 -50
  60. package/package.json +1 -1
  61. package/src/core/create-tree.ts +42 -9
  62. package/src/features/async-data-loader/feature.ts +15 -6
  63. package/src/features/drag-and-drop/feature.ts +75 -14
  64. package/src/features/drag-and-drop/types.ts +30 -5
  65. package/src/features/drag-and-drop/utils.ts +53 -24
  66. package/src/features/expand-all/feature.ts +10 -9
  67. package/src/features/expand-all/types.ts +2 -2
  68. package/src/features/hotkeys-core/feature.ts +0 -1
  69. package/src/features/main/types.ts +6 -0
  70. package/src/features/renaming/feature.ts +10 -7
  71. package/src/features/search/feature.ts +22 -6
  72. package/src/features/search/types.ts +1 -0
  73. package/src/features/selection/feature.ts +7 -3
  74. package/src/features/sync-data-loader/feature.ts +17 -3
  75. package/src/features/sync-data-loader/types.ts +1 -1
  76. package/src/features/tree/feature.ts +23 -22
  77. package/src/features/tree/types.ts +4 -2
  78. package/src/index.ts +4 -1
  79. package/src/types/core.ts +6 -5
  80. package/src/utilities/create-on-drop-handler.ts +14 -0
  81. package/src/utilities/insert-items-at-target.ts +30 -0
  82. package/src/utilities/remove-items-from-parents.ts +21 -0
  83. package/src/utils.ts +1 -69
  84. package/lib/cjs/data-adapters/nested-data-adapter.d.ts +0 -9
  85. package/lib/cjs/data-adapters/nested-data-adapter.js +0 -32
  86. package/lib/cjs/data-adapters/types.d.ts +0 -7
  87. package/lib/cjs/data-adapters/types.js +0 -2
  88. package/lib/esm/data-adapters/nested-data-adapter.d.ts +0 -9
  89. package/lib/esm/data-adapters/nested-data-adapter.js +0 -28
  90. package/lib/esm/data-adapters/types.d.ts +0 -7
  91. package/lib/esm/data-adapters/types.js +0 -1
  92. package/src/data-adapters/nested-data-adapter.ts +0 -48
  93. package/src/data-adapters/types.ts +0 -9
@@ -1,46 +1,54 @@
1
1
  import { makeStateUpdater } from "../../utils";
2
2
  export const asyncDataLoaderFeature = {
3
3
  key: "async-data-loader",
4
- dependingFeatures: ["main"],
5
4
  getInitialState: (initialState) => (Object.assign({ loadingItems: [] }, initialState)),
6
5
  getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setLoadingItems: makeStateUpdater("loadingItems", tree) }, defaultConfig)),
6
+ stateHandlerNames: {
7
+ loadingItems: "setLoadingItems",
8
+ },
7
9
  createTreeInstance: (prev, instance) => (Object.assign(Object.assign({}, prev), { retrieveItemData: (itemId) => {
8
- var _a, _b, _c, _d, _e, _f;
9
- var _g, _h;
10
+ var _a, _b, _c, _d, _e;
11
+ var _f, _g;
10
12
  const config = instance.getConfig();
11
13
  const dataRef = instance.getDataRef();
12
- (_a = (_g = dataRef.current).itemData) !== null && _a !== void 0 ? _a : (_g.itemData = {});
13
- (_b = (_h = dataRef.current).childrenIds) !== null && _b !== void 0 ? _b : (_h.childrenIds = {});
14
+ (_a = (_f = dataRef.current).itemData) !== null && _a !== void 0 ? _a : (_f.itemData = {});
15
+ (_b = (_g = dataRef.current).childrenIds) !== null && _b !== void 0 ? _b : (_g.childrenIds = {});
14
16
  if (dataRef.current.itemData[itemId]) {
15
17
  return dataRef.current.itemData[itemId];
16
18
  }
17
19
  if (!instance.getState().loadingItems.includes(itemId)) {
18
- (_c = config.setLoadingItems) === null || _c === void 0 ? void 0 : _c.call(config, (loadingItems) => [...loadingItems, itemId]);
19
- (_d = config.asyncDataLoader) === null || _d === void 0 ? void 0 : _d.getItem(itemId).then((item) => {
20
- var _a, _b;
20
+ instance.applySubStateUpdate("loadingItems", (loadingItems) => [
21
+ ...loadingItems,
22
+ itemId,
23
+ ]);
24
+ (_c = config.asyncDataLoader) === null || _c === void 0 ? void 0 : _c.getItem(itemId).then((item) => {
25
+ var _a;
21
26
  dataRef.current.itemData[itemId] = item;
22
27
  (_a = config.onLoadedItem) === null || _a === void 0 ? void 0 : _a.call(config, itemId, item);
23
- (_b = config.setLoadingItems) === null || _b === void 0 ? void 0 : _b.call(config, (loadingItems) => loadingItems.filter((id) => id !== itemId));
28
+ instance.applySubStateUpdate("loadingItems", (loadingItems) => loadingItems.filter((id) => id !== itemId));
24
29
  });
25
30
  }
26
- return (_f = (_e = config.createLoadingItemData) === null || _e === void 0 ? void 0 : _e.call(config)) !== null && _f !== void 0 ? _f : null;
31
+ return (_e = (_d = config.createLoadingItemData) === null || _d === void 0 ? void 0 : _d.call(config)) !== null && _e !== void 0 ? _e : null;
27
32
  }, retrieveChildrenIds: (itemId) => {
28
- var _a, _b, _c, _d, _e, _f;
29
- var _g, _h;
33
+ var _a, _b, _c, _d, _e;
34
+ var _f, _g;
30
35
  const config = instance.getConfig();
31
36
  const dataRef = instance.getDataRef();
32
- (_a = (_g = dataRef.current).itemData) !== null && _a !== void 0 ? _a : (_g.itemData = {});
33
- (_b = (_h = dataRef.current).childrenIds) !== null && _b !== void 0 ? _b : (_h.childrenIds = {});
37
+ (_a = (_f = dataRef.current).itemData) !== null && _a !== void 0 ? _a : (_f.itemData = {});
38
+ (_b = (_g = dataRef.current).childrenIds) !== null && _b !== void 0 ? _b : (_g.childrenIds = {});
34
39
  if (dataRef.current.childrenIds[itemId]) {
35
40
  return dataRef.current.childrenIds[itemId];
36
41
  }
37
42
  if (instance.getState().loadingItems.includes(itemId)) {
38
43
  return [];
39
44
  }
40
- (_c = config.setLoadingItems) === null || _c === void 0 ? void 0 : _c.call(config, (loadingItems) => [...loadingItems, itemId]);
41
- if ((_d = config.asyncDataLoader) === null || _d === void 0 ? void 0 : _d.getChildrenWithData) {
42
- (_e = config.asyncDataLoader) === null || _e === void 0 ? void 0 : _e.getChildrenWithData(itemId).then((children) => {
43
- var _a, _b, _c;
45
+ instance.applySubStateUpdate("loadingItems", (loadingItems) => [
46
+ ...loadingItems,
47
+ itemId,
48
+ ]);
49
+ if ((_c = config.asyncDataLoader) === null || _c === void 0 ? void 0 : _c.getChildrenWithData) {
50
+ (_d = config.asyncDataLoader) === null || _d === void 0 ? void 0 : _d.getChildrenWithData(itemId).then((children) => {
51
+ var _a, _b;
44
52
  for (const { id, data } of children) {
45
53
  dataRef.current.itemData[id] = data;
46
54
  (_a = config.onLoadedItem) === null || _a === void 0 ? void 0 : _a.call(config, id, data);
@@ -48,16 +56,16 @@ export const asyncDataLoaderFeature = {
48
56
  const childrenIds = children.map(({ id }) => id);
49
57
  dataRef.current.childrenIds[itemId] = childrenIds;
50
58
  (_b = config.onLoadedChildren) === null || _b === void 0 ? void 0 : _b.call(config, itemId, childrenIds);
51
- (_c = config.setLoadingItems) === null || _c === void 0 ? void 0 : _c.call(config, (loadingItems) => loadingItems.filter((id) => id !== itemId));
59
+ instance.applySubStateUpdate("loadingItems", (loadingItems) => loadingItems.filter((id) => id !== itemId));
52
60
  instance.rebuildTree();
53
61
  });
54
62
  }
55
63
  else {
56
- (_f = config.asyncDataLoader) === null || _f === void 0 ? void 0 : _f.getChildren(itemId).then((childrenIds) => {
57
- var _a, _b;
64
+ (_e = config.asyncDataLoader) === null || _e === void 0 ? void 0 : _e.getChildren(itemId).then((childrenIds) => {
65
+ var _a;
58
66
  dataRef.current.childrenIds[itemId] = childrenIds;
59
67
  (_a = config.onLoadedChildren) === null || _a === void 0 ? void 0 : _a.call(config, itemId, childrenIds);
60
- (_b = config.setLoadingItems) === null || _b === void 0 ? void 0 : _b.call(config, (loadingItems) => loadingItems.filter((id) => id !== itemId));
68
+ instance.applySubStateUpdate("loadingItems", (loadingItems) => loadingItems.filter((id) => id !== itemId));
61
69
  instance.rebuildTree();
62
70
  });
63
71
  }
@@ -2,16 +2,47 @@ import { canDrop, getDragCode, getDropTarget } from "./utils";
2
2
  import { makeStateUpdater } from "../../utils";
3
3
  export const dragAndDropFeature = {
4
4
  key: "dragAndDrop",
5
- dependingFeatures: ["main", "tree", "selection"],
6
- getDefaultConfig: (defaultConfig, tree) => (Object.assign({ canDrop: (_, target) => target.item.isFolder(), setDndState: makeStateUpdater("dnd", tree) }, defaultConfig)),
5
+ deps: ["selection"],
6
+ getDefaultConfig: (defaultConfig, tree) => (Object.assign({ canDrop: (_, target) => target.item.isFolder(), canDropForeignDragObject: () => false, setDndState: makeStateUpdater("dnd", tree) }, defaultConfig)),
7
+ stateHandlerNames: {
8
+ dnd: "setDndState",
9
+ },
7
10
  createTreeInstance: (prev, tree) => (Object.assign(Object.assign({}, prev), { getDropTarget: () => {
8
11
  var _a, _b;
9
12
  return (_b = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.dragTarget) !== null && _b !== void 0 ? _b : null;
13
+ }, getDragLineData: () => {
14
+ var _a, _b, _c, _d, _e;
15
+ const target = tree.getDropTarget();
16
+ const intend = ((_a = target === null || target === void 0 ? void 0 : target.item.getItemMeta().level) !== null && _a !== void 0 ? _a : 0) + 1;
17
+ if (!target || target.childIndex === null)
18
+ return null;
19
+ const children = target.item.getChildren();
20
+ if (target.childIndex === children.length) {
21
+ const bb = (_c = (_b = children[target.childIndex - 1]) === null || _b === void 0 ? void 0 : _b.getElement()) === null || _c === void 0 ? void 0 : _c.getBoundingClientRect();
22
+ if (bb) {
23
+ return {
24
+ intend,
25
+ top: bb.bottom,
26
+ left: bb.left,
27
+ right: bb.right,
28
+ };
29
+ }
30
+ }
31
+ const bb = (_e = (_d = children[target.childIndex]) === null || _d === void 0 ? void 0 : _d.getElement()) === null || _e === void 0 ? void 0 : _e.getBoundingClientRect();
32
+ if (bb) {
33
+ return {
34
+ intend,
35
+ top: bb.top,
36
+ left: bb.left,
37
+ right: bb.right,
38
+ };
39
+ }
40
+ return null;
10
41
  } })),
11
42
  createItemInstance: (prev, item, tree) => (Object.assign(Object.assign({}, prev), { getProps: () => {
12
43
  var _a, _b, _c;
13
44
  return (Object.assign(Object.assign({}, prev.getProps()), { draggable: (_c = (_b = (_a = tree.getConfig()).isItemDraggable) === null || _b === void 0 ? void 0 : _b.call(_a, item)) !== null && _c !== void 0 ? _c : true, onDragStart: item.getMemoizedProp("dnd/onDragStart", () => (e) => {
14
- var _a, _b, _c, _d, _e;
45
+ var _a, _b, _c;
15
46
  const selectedItems = tree.getSelectedItems();
16
47
  const items = selectedItems.includes(item) ? selectedItems : [item];
17
48
  const config = tree.getConfig();
@@ -26,12 +57,12 @@ export const dragAndDropFeature = {
26
57
  const { format, data } = config.createForeignDragObject(items);
27
58
  (_c = e.dataTransfer) === null || _c === void 0 ? void 0 : _c.setData(format, data);
28
59
  }
29
- (_e = (_d = tree.getConfig()).setDndState) === null || _e === void 0 ? void 0 : _e.call(_d, {
60
+ tree.applySubStateUpdate("dnd", {
30
61
  draggedItems: items,
31
62
  draggingOverItem: tree.getFocusedItem(),
32
63
  });
33
64
  }), onDragOver: item.getMemoizedProp("dnd/onDragOver", () => (e) => {
34
- var _a, _b, _c, _d, _e;
65
+ var _a, _b, _c;
35
66
  const target = getDropTarget(e, item, tree);
36
67
  const dataRef = tree.getDataRef();
37
68
  if (!((_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggedItems) &&
@@ -47,14 +78,21 @@ export const dragAndDropFeature = {
47
78
  return;
48
79
  }
49
80
  dataRef.current.lastDragCode = nextDragCode;
50
- (_e = (_d = tree.getConfig()).setDndState) === null || _e === void 0 ? void 0 : _e.call(_d, (state) => (Object.assign(Object.assign({}, state), { dragTarget: target, draggingOverItem: item })));
81
+ tree.applySubStateUpdate("dnd", (state) => (Object.assign(Object.assign({}, state), { dragTarget: target, draggingOverItem: item })));
51
82
  }), onDragLeave: item.getMemoizedProp("dnd/onDragLeave", () => () => {
52
- var _a, _b;
53
83
  const dataRef = tree.getDataRef();
54
84
  dataRef.current.lastDragCode = "no-drag";
55
- (_b = (_a = tree.getConfig()).setDndState) === null || _b === void 0 ? void 0 : _b.call(_a, (state) => (Object.assign(Object.assign({}, state), { draggingOverItem: undefined, dragTarget: undefined })));
85
+ tree.applySubStateUpdate("dnd", (state) => (Object.assign(Object.assign({}, state), { draggingOverItem: undefined, dragTarget: undefined })));
86
+ }), onDragEnd: item.getMemoizedProp("dnd/onDragEnd", () => (e) => {
87
+ var _a, _b, _c;
88
+ const draggedItems = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggedItems;
89
+ tree.applySubStateUpdate("dnd", null);
90
+ if (e.dataTransfer.dropEffect === "none" || !draggedItems) {
91
+ return;
92
+ }
93
+ (_c = (_b = tree.getConfig()).onCompleteForeignDrop) === null || _c === void 0 ? void 0 : _c.call(_b, draggedItems);
56
94
  }), onDrop: item.getMemoizedProp("dnd/onDrop", () => (e) => {
57
- var _a, _b, _c, _d, _e;
95
+ var _a, _b, _c;
58
96
  const dataRef = tree.getDataRef();
59
97
  const target = getDropTarget(e, item, tree);
60
98
  if (!canDrop(e.dataTransfer, target, tree)) {
@@ -64,12 +102,12 @@ export const dragAndDropFeature = {
64
102
  const config = tree.getConfig();
65
103
  const draggedItems = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggedItems;
66
104
  dataRef.current.lastDragCode = undefined;
67
- (_c = (_b = tree.getConfig()).setDndState) === null || _c === void 0 ? void 0 : _c.call(_b, null);
105
+ tree.applySubStateUpdate("dnd", null);
68
106
  if (draggedItems) {
69
- (_d = config.onDrop) === null || _d === void 0 ? void 0 : _d.call(config, draggedItems, target);
107
+ (_b = config.onDrop) === null || _b === void 0 ? void 0 : _b.call(config, draggedItems, target);
70
108
  }
71
109
  else {
72
- (_e = config.onDropForeignDragObject) === null || _e === void 0 ? void 0 : _e.call(config, e.dataTransfer, target);
110
+ (_c = config.onDropForeignDragObject) === null || _c === void 0 ? void 0 : _c.call(config, e.dataTransfer, target);
73
111
  }
74
112
  // TODO rebuild tree?
75
113
  }) }));
@@ -78,16 +116,18 @@ export const dragAndDropFeature = {
78
116
  return target ? target.item.getId() === item.getId() : false;
79
117
  }, isDropTargetAbove: () => {
80
118
  const target = tree.getDropTarget();
81
- if (!target || target.childIndex === null)
119
+ if (!target ||
120
+ target.childIndex === null ||
121
+ target.item !== item.getParent())
82
122
  return false;
83
- const targetIndex = target.item.getItemMeta().index;
84
- return targetIndex + target.childIndex + 1 === item.getItemMeta().index;
123
+ return target.childIndex === item.getItemMeta().posInSet;
85
124
  }, isDropTargetBelow: () => {
86
125
  const target = tree.getDropTarget();
87
- if (!target || target.childIndex === null)
126
+ if (!target ||
127
+ target.childIndex === null ||
128
+ target.item !== item.getParent())
88
129
  return false;
89
- const targetIndex = target.item.getItemMeta().index;
90
- return targetIndex + target.childIndex === item.getItemMeta().index;
130
+ return target.childIndex - 1 === item.getItemMeta().posInSet;
91
131
  }, isDraggingOver: () => {
92
132
  var _a, _b;
93
133
  return ((_b = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggingOverItem) === null || _b === void 0 ? void 0 : _b.getId()) === item.getId();
@@ -7,9 +7,20 @@ export type DndState<T> = {
7
7
  draggingOverItem?: ItemInstance<T>;
8
8
  dragTarget?: DropTarget<T>;
9
9
  };
10
+ export type DragLineData = {
11
+ intend: number;
12
+ top: number;
13
+ left: number;
14
+ right: number;
15
+ };
10
16
  export type DropTarget<T> = {
11
17
  item: ItemInstance<T>;
12
- childIndex: number | null;
18
+ childIndex: number;
19
+ insertionIndex: number;
20
+ } | {
21
+ item: ItemInstance<T>;
22
+ childIndex: null;
23
+ insertionIndex: null;
13
24
  };
14
25
  export declare enum DropTargetPosition {
15
26
  Top = "top",
@@ -35,9 +46,18 @@ export type DragAndDropFeatureDef<T> = {
35
46
  canDropForeignDragObject?: (dataTransfer: DataTransfer, target: DropTarget<T>) => boolean;
36
47
  onDrop?: (items: ItemInstance<T>[], target: DropTarget<T>) => void;
37
48
  onDropForeignDragObject?: (dataTransfer: DataTransfer, target: DropTarget<T>) => void;
49
+ /** Runs in the onDragEnd event, if `ev.dataTransfer.dropEffect` is not `none`, i.e. the drop
50
+ * was not aborted. No target is provided as parameter since the target may be a foreign drop target.
51
+ * This is useful to seperate out the logic to move dragged items out of their previous parents.
52
+ * Use `onDrop` to handle drop-related logic.
53
+ *
54
+ * This ignores the `canDrop` handler, since the drop target is unknown in this handler.
55
+ */
56
+ onCompleteForeignDrop?: (items: ItemInstance<T>[]) => void;
38
57
  };
39
58
  treeInstance: {
40
59
  getDropTarget: () => DropTarget<T> | null;
60
+ getDragLineData: () => DragLineData | null;
41
61
  };
42
62
  itemInstance: {
43
63
  isDropTarget: () => boolean;
@@ -3,4 +3,4 @@ import { DropTarget } from "./types";
3
3
  export declare const getDragCode: ({ item, childIndex }: DropTarget<any>) => string;
4
4
  export declare const getDropOffset: (e: any, item: ItemInstance<any>) => number;
5
5
  export declare const canDrop: (dataTransfer: DataTransfer | null, target: DropTarget<any>, tree: TreeInstance<any>) => boolean;
6
- export declare const getDropTarget: (e: any, item: ItemInstance<any>, tree: TreeInstance<any>) => DropTarget<any>;
6
+ export declare const getDropTarget: (e: any, item: ItemInstance<any>, tree: TreeInstance<any>, canDropInbetween?: boolean | undefined) => DropTarget<any>;
@@ -1,4 +1,4 @@
1
- import { DropTargetPosition } from "./types";
1
+ import { DropTargetPosition, } from "./types";
2
2
  export const getDragCode = ({ item, childIndex }) => `${item.getId()}__${childIndex !== null && childIndex !== void 0 ? childIndex : "none"}`;
3
3
  export const getDropOffset = (e, item) => {
4
4
  var _a;
@@ -28,28 +28,45 @@ const getDropTargetPosition = (offset, topLinePercentage, bottomLinePercentage)
28
28
  }
29
29
  return DropTargetPosition.Item;
30
30
  };
31
- export const getDropTarget = (e, item, tree) => {
32
- var _a, _b;
31
+ export const getDropTarget = (e, item, tree, canDropInbetween = tree.getConfig().canDropInbetween) => {
32
+ var _a, _b, _c, _d;
33
33
  const config = tree.getConfig();
34
- const offset = getDropOffset(e, item);
35
- const dropOnItemTarget = { item, childIndex: null };
36
- const pos = getDropTargetPosition(offset, (_a = config.topLinePercentage) !== null && _a !== void 0 ? _a : 0.3, (_b = config.bottomLinePercentage) !== null && _b !== void 0 ? _b : 0.7);
37
- const inbetweenPos = getDropTargetPosition(offset, 0.5, 0.5);
38
- if (!config.canDropInbetween) {
39
- return dropOnItemTarget;
40
- }
41
- if (!canDrop(e.dataTransfer, dropOnItemTarget, tree)) {
42
- return {
43
- item: item.getParent(),
44
- childIndex: item.getIndexInParent() +
45
- (inbetweenPos === DropTargetPosition.Top ? 0 : 1),
46
- };
34
+ const draggedItems = (_b = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggedItems) !== null && _b !== void 0 ? _b : [];
35
+ const itemTarget = { item, childIndex: null, insertionIndex: null };
36
+ const parentTarget = {
37
+ item: item.getParent(),
38
+ childIndex: null,
39
+ insertionIndex: null,
40
+ };
41
+ if (!canDropInbetween) {
42
+ if (!canDrop(e.dataTransfer, parentTarget, tree)) {
43
+ return getDropTarget(e, item.getParent(), tree, false);
44
+ }
45
+ return itemTarget;
47
46
  }
47
+ const canDropInside = canDrop(e.dataTransfer, itemTarget, tree);
48
+ const offset = getDropOffset(e, item);
49
+ const pos = canDropInside
50
+ ? getDropTargetPosition(offset, (_c = config.topLinePercentage) !== null && _c !== void 0 ? _c : 0.3, (_d = config.bottomLinePercentage) !== null && _d !== void 0 ? _d : 0.7)
51
+ : getDropTargetPosition(offset, 0.5, 0.5);
48
52
  if (pos === DropTargetPosition.Item) {
49
- return dropOnItemTarget;
53
+ return itemTarget;
54
+ }
55
+ if (!canDrop(e.dataTransfer, parentTarget, tree)) {
56
+ return getDropTarget(e, item.getParent(), tree, false);
50
57
  }
58
+ const childIndex = item.getIndexInParent() + (pos === DropTargetPosition.Top ? 0 : 1);
59
+ const numberOfDragItemsBeforeTarget = item
60
+ .getParent()
61
+ .getChildren()
62
+ .slice(0, childIndex)
63
+ .reduce((counter, child) => child && (draggedItems === null || draggedItems === void 0 ? void 0 : draggedItems.some((i) => i.getId() === child.getId()))
64
+ ? ++counter
65
+ : counter, 0);
51
66
  return {
52
67
  item: item.getParent(),
53
- childIndex: item.getIndexInParent() + (pos === DropTargetPosition.Top ? 0 : 1),
68
+ childIndex,
69
+ // TODO performance could be improved by computing this only when dragcode changed
70
+ insertionIndex: childIndex - numberOfDragItemsBeforeTarget,
54
71
  };
55
72
  };
@@ -10,27 +10,29 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import { poll } from "../../utils";
11
11
  export const expandAllFeature = {
12
12
  key: "expand-all",
13
- dependingFeatures: ["main", "tree"],
14
13
  createTreeInstance: (prev, tree) => (Object.assign(Object.assign({}, prev), { expandAll: (cancelToken) => __awaiter(void 0, void 0, void 0, function* () {
15
14
  yield Promise.all(tree.getItems().map((item) => item.expandAll(cancelToken)));
16
- }), collapseAll: () => __awaiter(void 0, void 0, void 0, function* () {
17
- var _a, _b;
18
- (_b = (_a = tree.getConfig()).setExpandedItems) === null || _b === void 0 ? void 0 : _b.call(_a, []);
19
- }) })),
15
+ }), collapseAll: () => {
16
+ tree.applySubStateUpdate("expandedItems", []);
17
+ tree.rebuildTree();
18
+ } })),
20
19
  createItemInstance: (prev, item, tree) => (Object.assign(Object.assign({}, prev), { expandAll: (cancelToken) => __awaiter(void 0, void 0, void 0, function* () {
21
20
  if (cancelToken === null || cancelToken === void 0 ? void 0 : cancelToken.current) {
22
21
  return;
23
22
  }
23
+ if (!item.isFolder()) {
24
+ return;
25
+ }
24
26
  item.expand();
25
27
  yield poll(() => !tree.getState().loadingItems.includes(item.getId()));
26
28
  yield Promise.all(item.getChildren().map((child) => __awaiter(void 0, void 0, void 0, function* () {
27
29
  yield poll(() => !tree.getState().loadingItems.includes(child.getId()));
28
30
  yield (child === null || child === void 0 ? void 0 : child.expandAll(cancelToken));
29
31
  })));
30
- }), collapseAll: () => __awaiter(void 0, void 0, void 0, function* () {
31
- yield Promise.all(item.getChildren().map((child) => __awaiter(void 0, void 0, void 0, function* () {
32
- yield (child === null || child === void 0 ? void 0 : child.collapseAll());
33
- })));
32
+ }), collapseAll: () => {
33
+ for (const child of item.getChildren()) {
34
+ child === null || child === void 0 ? void 0 : child.collapseAll();
35
+ }
34
36
  item.collapse();
35
- }) })),
37
+ } })),
36
38
  };
@@ -5,13 +5,13 @@ export type ExpandAllFeatureDef = {
5
5
  expandAll: (cancelToken?: {
6
6
  current: boolean;
7
7
  }) => Promise<void>;
8
- collapseAll: () => Promise<void>;
8
+ collapseAll: () => void;
9
9
  };
10
10
  itemInstance: {
11
11
  expandAll: (cancelToken?: {
12
12
  current: boolean;
13
13
  }) => Promise<void>;
14
- collapseAll: () => Promise<void>;
14
+ collapseAll: () => void;
15
15
  };
16
16
  hotkeys: never;
17
17
  };
@@ -17,7 +17,6 @@ const findHotkeyMatch = (pressedKeys, tree, config1, config2) => {
17
17
  };
18
18
  export const hotkeysCoreFeature = {
19
19
  key: "hotkeys-core",
20
- dependingFeatures: ["main", "tree"],
21
20
  onTreeMount: (tree, element) => {
22
21
  const data = tree.getDataRef();
23
22
  const keydown = (e) => {
@@ -1,4 +1,4 @@
1
- import { FeatureImplementation, HotkeysConfig, ItemInstance, SetStateFn, TreeConfig, TreeState } from "../../types/core";
1
+ import { FeatureImplementation, HotkeysConfig, ItemInstance, SetStateFn, TreeConfig, TreeState, Updater } from "../../types/core";
2
2
  import { ItemMeta } from "../tree/types";
3
3
  export type MainFeatureDef<T = any> = {
4
4
  state: {};
@@ -9,6 +9,8 @@ export type MainFeatureDef<T = any> = {
9
9
  setState?: SetStateFn<TreeState<T>>;
10
10
  };
11
11
  treeInstance: {
12
+ /** @internal */
13
+ applySubStateUpdate: <K extends keyof TreeState<any>>(stateName: K, updater: Updater<TreeState<T>[K]>) => void;
12
14
  setState: SetStateFn<TreeState<T>>;
13
15
  getState: () => TreeState<T>;
14
16
  setConfig: SetStateFn<TreeConfig<T>>;
@@ -1,38 +1,37 @@
1
1
  import { makeStateUpdater } from "../../utils";
2
2
  export const renamingFeature = {
3
3
  key: "renaming",
4
- dependingFeatures: ["main", "tree"],
5
4
  getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setRenamingItem: makeStateUpdater("renamingItem", tree), setRenamingValue: makeStateUpdater("renamingValue", tree), canRename: () => true }, defaultConfig)),
5
+ stateHandlerNames: {
6
+ renamingItem: "setRenamingItem",
7
+ renamingValue: "setRenamingValue",
8
+ },
6
9
  createTreeInstance: (prev, instance) => (Object.assign(Object.assign({}, prev), { startRenamingItem: (itemId) => {
7
- var _a, _b;
8
- const config = instance.getConfig();
9
10
  const item = instance.getItemInstance(itemId);
10
11
  if (!item.canRename()) {
11
12
  return;
12
13
  }
13
- (_a = config.setRenamingItem) === null || _a === void 0 ? void 0 : _a.call(config, itemId);
14
- (_b = config.setRenamingValue) === null || _b === void 0 ? void 0 : _b.call(config, item.getItemName());
14
+ instance.applySubStateUpdate("renamingItem", itemId);
15
+ instance.applySubStateUpdate("renamingValue", item.getItemName());
15
16
  }, getRenamingItem: () => {
16
17
  const itemId = instance.getState().renamingItem;
17
18
  return itemId ? instance.getItemInstance(itemId) : null;
18
19
  }, getRenamingValue: () => instance.getState().renamingValue || "", abortRenaming: () => {
19
- var _a, _b;
20
- (_b = (_a = instance.getConfig()).setRenamingItem) === null || _b === void 0 ? void 0 : _b.call(_a, null);
20
+ instance.applySubStateUpdate("renamingItem", null);
21
21
  }, completeRenaming: () => {
22
- var _a, _b, _c;
22
+ var _a;
23
23
  const config = instance.getConfig();
24
24
  const item = instance.getRenamingItem();
25
25
  if (item) {
26
26
  (_a = config.onRename) === null || _a === void 0 ? void 0 : _a.call(config, item, instance.getState().renamingValue || "");
27
27
  }
28
- (_c = (_b = instance.getConfig()).setRenamingItem) === null || _c === void 0 ? void 0 : _c.call(_b, null);
28
+ instance.applySubStateUpdate("renamingItem", null);
29
29
  }, isRenamingItem: () => !!instance.getState().renamingItem })),
30
30
  createItemInstance: (prev, instance, tree) => (Object.assign(Object.assign({}, prev), { getRenameInputProps: () => ({
31
31
  onBlur: () => tree.abortRenaming(),
32
32
  value: tree.getRenamingValue(),
33
33
  onChange: (e) => {
34
- var _a, _b;
35
- (_b = (_a = tree.getConfig()).setRenamingValue) === null || _b === void 0 ? void 0 : _b.call(_a, e.target.value);
34
+ tree.applySubStateUpdate("renamingValue", e.target.value);
36
35
  },
37
36
  }), canRename: () => { var _a, _b, _c; return (_c = (_b = (_a = tree.getConfig()).canRename) === null || _b === void 0 ? void 0 : _b.call(_a, instance)) !== null && _c !== void 0 ? _c : true; }, isRenaming: () => instance.getId() === tree.getState().renamingItem })),
38
37
  hotkeys: {
@@ -1,20 +1,22 @@
1
1
  import { makeStateUpdater, memo } from "../../utils";
2
2
  export const searchFeature = {
3
3
  key: "search",
4
- dependingFeatures: ["main", "tree"],
5
4
  getInitialState: (initialState) => (Object.assign({ search: null }, initialState)),
6
5
  getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setSearch: makeStateUpdater("search", tree), isSearchMatchingItem: (search, item) => search.length > 0 &&
7
6
  item.getItemName().toLowerCase().includes(search.toLowerCase()) }, defaultConfig)),
7
+ stateHandlerNames: {
8
+ search: "setSearch",
9
+ },
8
10
  createTreeInstance: (prev, instance) => (Object.assign(Object.assign({}, prev), { setSearch: (search) => {
9
- var _a, _b, _c;
10
- (_b = (_a = instance.getConfig()).setSearch) === null || _b === void 0 ? void 0 : _b.call(_a, search);
11
- (_c = instance
11
+ var _a;
12
+ instance.applySubStateUpdate("search", search);
13
+ (_a = instance
12
14
  .getItems()
13
15
  .find((item) => {
14
16
  var _a, _b;
15
17
  return (_b = (_a = instance
16
18
  .getConfig()).isSearchMatchingItem) === null || _b === void 0 ? void 0 : _b.call(_a, instance.getSearchValue(), item);
17
- })) === null || _c === void 0 ? void 0 : _c.setFocused();
19
+ })) === null || _a === void 0 ? void 0 : _a.setFocused();
18
20
  }, openSearch: (initialValue = "") => {
19
21
  instance.setSearch(initialValue);
20
22
  setTimeout(() => {
@@ -36,7 +38,7 @@ export const searchFeature = {
36
38
  value: instance.getSearchValue(),
37
39
  onChange: (e) => instance.setSearch(e.target.value),
38
40
  onBlur: () => instance.closeSearch(),
39
- }), getSearchMatchingItems: memo((search, items) => items.filter((item) => { var _a, _b; return (_b = (_a = instance.getConfig()).isSearchMatchingItem) === null || _b === void 0 ? void 0 : _b.call(_a, search, item); }), () => [instance.getSearchValue(), instance.getItems()]) })),
41
+ }), getSearchMatchingItems: memo((search, items) => items.filter((item) => { var _a, _b; return search && ((_b = (_a = instance.getConfig()).isSearchMatchingItem) === null || _b === void 0 ? void 0 : _b.call(_a, search, item)); }), () => [instance.getSearchValue(), instance.getItems()]) })),
40
42
  createItemInstance: (prev, item, tree) => (Object.assign(Object.assign({}, prev), { isMatchingSearch: () => tree.getSearchMatchingItems().some((i) => i.getId() === item.getId()) })),
41
43
  hotkeys: {
42
44
  openSearch: {
@@ -57,30 +59,41 @@ export const searchFeature = {
57
59
  tree.closeSearch();
58
60
  },
59
61
  },
62
+ submitSearch: {
63
+ hotkey: "Enter",
64
+ allowWhenInputFocused: true,
65
+ isEnabled: (tree) => tree.isSearchOpen(),
66
+ handler: (e, tree) => {
67
+ tree.closeSearch();
68
+ tree.setSelectedItems([tree.getFocusedItem().getId()]);
69
+ },
70
+ },
60
71
  nextSearchItem: {
61
72
  hotkey: "ArrowDown",
62
73
  allowWhenInputFocused: true,
74
+ canRepeat: true,
63
75
  isEnabled: (tree) => tree.isSearchOpen(),
64
76
  handler: (e, tree) => {
65
- // TODO scroll into view
66
77
  const focusItem = tree
67
78
  .getSearchMatchingItems()
68
79
  .find((item) => item.getItemMeta().index >
69
80
  tree.getFocusedItem().getItemMeta().index);
70
81
  focusItem === null || focusItem === void 0 ? void 0 : focusItem.setFocused();
82
+ focusItem === null || focusItem === void 0 ? void 0 : focusItem.scrollTo({ block: "nearest", inline: "nearest" });
71
83
  },
72
84
  },
73
85
  previousSearchItem: {
74
86
  hotkey: "ArrowUp",
75
87
  allowWhenInputFocused: true,
88
+ canRepeat: true,
76
89
  isEnabled: (tree) => tree.isSearchOpen(),
77
90
  handler: (e, tree) => {
78
- // TODO scroll into view
79
91
  const focusItem = [...tree.getSearchMatchingItems()]
80
92
  .reverse()
81
93
  .find((item) => item.getItemMeta().index <
82
94
  tree.getFocusedItem().getItemMeta().index);
83
95
  focusItem === null || focusItem === void 0 ? void 0 : focusItem.setFocused();
96
+ focusItem === null || focusItem === void 0 ? void 0 : focusItem.scrollTo({ block: "nearest", inline: "nearest" });
84
97
  },
85
98
  },
86
99
  },
@@ -29,5 +29,5 @@ export type SearchFeatureDef<T> = {
29
29
  itemInstance: {
30
30
  isMatchingSearch: () => boolean;
31
31
  };
32
- hotkeys: "openSearch" | "closeSearch" | "nextSearchItem" | "previousSearchItem";
32
+ hotkeys: "openSearch" | "closeSearch" | "submitSearch" | "nextSearchItem" | "previousSearchItem";
33
33
  };
@@ -1,12 +1,13 @@
1
1
  import { makeStateUpdater } from "../../utils";
2
2
  export const selectionFeature = {
3
3
  key: "selection",
4
- dependingFeatures: ["main", "tree"],
5
4
  getInitialState: (initialState) => (Object.assign({ selectedItems: [] }, initialState)),
6
5
  getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setSelectedItems: makeStateUpdater("selectedItems", tree) }, defaultConfig)),
6
+ stateHandlerNames: {
7
+ selectedItems: "setSelectedItems",
8
+ },
7
9
  createTreeInstance: (prev, instance) => (Object.assign(Object.assign({}, prev), { setSelectedItems: (selectedItems) => {
8
- var _a, _b;
9
- (_b = (_a = instance.getConfig()).setSelectedItems) === null || _b === void 0 ? void 0 : _b.call(_a, selectedItems);
10
+ instance.applySubStateUpdate("selectedItems", selectedItems);
10
11
  },
11
12
  // TODO memo
12
13
  getSelectedItems: () => {
@@ -31,7 +32,7 @@ export const selectionFeature = {
31
32
  const newSelectedItems = tree
32
33
  .getItems()
33
34
  .slice(a, b + 1)
34
- .map((item) => item.getItemMeta().itemId);
35
+ .map((treeItem) => treeItem.getItemMeta().itemId);
35
36
  if (!ctrl) {
36
37
  tree.setSelectedItems(newSelectedItems);
37
38
  return;
@@ -48,7 +49,7 @@ export const selectionFeature = {
48
49
  else {
49
50
  item.select();
50
51
  }
51
- }, getProps: () => (Object.assign(Object.assign({}, prev.getProps()), { onClick: item.getMemoizedProp("selection/onClick", () => (e) => {
52
+ }, getProps: () => (Object.assign(Object.assign({}, prev.getProps()), { "aria-selected": item.isSelected() ? "true" : "false", onClick: item.getMemoizedProp("selection/onClick", () => (e) => {
52
53
  var _a, _b;
53
54
  if (e.shiftKey) {
54
55
  item.selectUpTo(e.ctrlKey || e.metaKey);
@@ -1,6 +1,11 @@
1
+ import { makeStateUpdater } from "../../utils";
1
2
  export const syncDataLoaderFeature = {
2
3
  key: "sync-data-loader",
3
- dependingFeatures: ["main"],
4
+ getInitialState: (initialState) => (Object.assign({ loadingItems: [] }, initialState)),
5
+ getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setLoadingItems: makeStateUpdater("loadingItems", tree) }, defaultConfig)),
6
+ stateHandlerNames: {
7
+ loadingItems: "setLoadingItems",
8
+ },
4
9
  createTreeInstance: (prev, instance) => (Object.assign(Object.assign({}, prev), { retrieveItemData: (itemId) => instance.getConfig().dataLoader.getItem(itemId), retrieveChildrenIds: (itemId) => instance.getConfig().dataLoader.getChildren(itemId) })),
5
10
  createItemInstance: (prev) => (Object.assign(Object.assign({}, prev), { isLoading: () => false })),
6
11
  };
@@ -6,7 +6,7 @@ export type SyncDataLoaderFeatureDef<T> = {
6
6
  state: {};
7
7
  config: {
8
8
  rootItemId: string;
9
- dataLoader: SyncTreeDataLoader<T>;
9
+ dataLoader?: SyncTreeDataLoader<T>;
10
10
  };
11
11
  treeInstance: {
12
12
  retrieveItemData: (itemId: string) => T;