@headless-tree/core 0.0.4 → 0.0.6

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 (91) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/lib/cjs/core/create-tree.js +11 -10
  3. package/lib/cjs/features/async-data-loader/feature.js +30 -21
  4. package/lib/cjs/features/drag-and-drop/feature.js +34 -22
  5. package/lib/cjs/features/drag-and-drop/types.d.ts +14 -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 -9
  9. package/lib/cjs/features/expand-all/types.d.ts +2 -2
  10. package/lib/cjs/features/main/types.d.ts +4 -1
  11. package/lib/cjs/features/renaming/feature.js +10 -9
  12. package/lib/cjs/features/search/feature.js +21 -7
  13. package/lib/cjs/features/search/types.d.ts +1 -1
  14. package/lib/cjs/features/selection/feature.js +6 -4
  15. package/lib/cjs/features/sync-data-loader/feature.js +6 -0
  16. package/lib/cjs/features/sync-data-loader/types.d.ts +1 -1
  17. package/lib/cjs/features/tree/feature.js +41 -24
  18. package/lib/cjs/features/tree/types.d.ts +7 -2
  19. package/lib/cjs/index.d.ts +3 -1
  20. package/lib/cjs/index.js +3 -1
  21. package/lib/cjs/types/core.d.ts +1 -3
  22. package/lib/cjs/utilities/create-on-drop-handler.d.ts +3 -0
  23. package/lib/cjs/utilities/create-on-drop-handler.js +11 -0
  24. package/lib/cjs/utilities/insert-items-at-target.d.ts +3 -0
  25. package/lib/cjs/utilities/insert-items-at-target.js +24 -0
  26. package/lib/cjs/utilities/remove-items-from-parents.d.ts +2 -0
  27. package/lib/cjs/utilities/remove-items-from-parents.js +17 -0
  28. package/lib/cjs/utils.d.ts +1 -4
  29. package/lib/cjs/utils.js +1 -53
  30. package/lib/esm/core/create-tree.js +11 -10
  31. package/lib/esm/features/async-data-loader/feature.js +30 -21
  32. package/lib/esm/features/drag-and-drop/feature.js +34 -22
  33. package/lib/esm/features/drag-and-drop/types.d.ts +14 -1
  34. package/lib/esm/features/drag-and-drop/utils.d.ts +1 -1
  35. package/lib/esm/features/drag-and-drop/utils.js +35 -18
  36. package/lib/esm/features/expand-all/feature.js +12 -9
  37. package/lib/esm/features/expand-all/types.d.ts +2 -2
  38. package/lib/esm/features/main/types.d.ts +4 -1
  39. package/lib/esm/features/renaming/feature.js +10 -9
  40. package/lib/esm/features/search/feature.js +21 -7
  41. package/lib/esm/features/search/types.d.ts +1 -1
  42. package/lib/esm/features/selection/feature.js +6 -4
  43. package/lib/esm/features/sync-data-loader/feature.js +6 -0
  44. package/lib/esm/features/sync-data-loader/types.d.ts +1 -1
  45. package/lib/esm/features/tree/feature.js +41 -24
  46. package/lib/esm/features/tree/types.d.ts +7 -2
  47. package/lib/esm/index.d.ts +3 -1
  48. package/lib/esm/index.js +3 -1
  49. package/lib/esm/types/core.d.ts +1 -3
  50. package/lib/esm/utilities/create-on-drop-handler.d.ts +3 -0
  51. package/lib/esm/utilities/create-on-drop-handler.js +7 -0
  52. package/lib/esm/utilities/insert-items-at-target.d.ts +3 -0
  53. package/lib/esm/utilities/insert-items-at-target.js +20 -0
  54. package/lib/esm/utilities/remove-items-from-parents.d.ts +2 -0
  55. package/lib/esm/utilities/remove-items-from-parents.js +13 -0
  56. package/lib/esm/utils.d.ts +1 -4
  57. package/lib/esm/utils.js +0 -50
  58. package/package.json +3 -3
  59. package/src/core/create-tree.ts +12 -8
  60. package/src/features/async-data-loader/feature.ts +15 -5
  61. package/src/features/drag-and-drop/feature.ts +42 -20
  62. package/src/features/drag-and-drop/types.ts +23 -6
  63. package/src/features/drag-and-drop/utils.ts +53 -24
  64. package/src/features/expand-all/feature.ts +10 -8
  65. package/src/features/expand-all/types.ts +2 -2
  66. package/src/features/main/types.ts +7 -0
  67. package/src/features/renaming/feature.ts +10 -5
  68. package/src/features/search/feature.ts +22 -5
  69. package/src/features/search/types.ts +1 -0
  70. package/src/features/selection/feature.ts +8 -3
  71. package/src/features/sync-data-loader/feature.ts +17 -2
  72. package/src/features/sync-data-loader/types.ts +1 -1
  73. package/src/features/tree/feature.ts +43 -23
  74. package/src/features/tree/types.ts +10 -2
  75. package/src/index.ts +4 -1
  76. package/src/types/core.ts +4 -4
  77. package/src/utilities/create-on-drop-handler.ts +14 -0
  78. package/src/utilities/insert-items-at-target.ts +30 -0
  79. package/src/utilities/remove-items-from-parents.ts +21 -0
  80. package/src/utils.ts +1 -69
  81. package/tsconfig.json +1 -1
  82. package/lib/cjs/data-adapters/nested-data-adapter.d.ts +0 -9
  83. package/lib/cjs/data-adapters/nested-data-adapter.js +0 -32
  84. package/lib/cjs/data-adapters/types.d.ts +0 -7
  85. package/lib/cjs/data-adapters/types.js +0 -2
  86. package/lib/esm/data-adapters/nested-data-adapter.d.ts +0 -9
  87. package/lib/esm/data-adapters/nested-data-adapter.js +0 -28
  88. package/lib/esm/data-adapters/types.d.ts +0 -7
  89. package/lib/esm/data-adapters/types.js +0 -1
  90. package/src/data-adapters/nested-data-adapter.ts +0 -48
  91. package/src/data-adapters/types.ts +0 -9
@@ -1,9 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.syncDataLoaderFeature = void 0;
4
+ const utils_1 = require("../../utils");
4
5
  exports.syncDataLoaderFeature = {
5
6
  key: "sync-data-loader",
6
7
  dependingFeatures: ["main"],
8
+ getInitialState: (initialState) => (Object.assign({ loadingItems: [] }, initialState)),
9
+ getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setLoadingItems: (0, utils_1.makeStateUpdater)("loadingItems", tree) }, defaultConfig)),
10
+ stateHandlerNames: {
11
+ loadingItems: "setLoadingItems",
12
+ },
7
13
  createTreeInstance: (prev, instance) => (Object.assign(Object.assign({}, prev), { retrieveItemData: (itemId) => instance.getConfig().dataLoader.getItem(itemId), retrieveChildrenIds: (itemId) => instance.getConfig().dataLoader.getChildren(itemId) })),
8
14
  createItemInstance: (prev) => (Object.assign(Object.assign({}, prev), { isLoading: () => false })),
9
15
  };
@@ -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;
@@ -16,6 +16,10 @@ exports.treeFeature = {
16
16
  dependingFeatures: ["main"],
17
17
  getInitialState: (initialState) => (Object.assign({ expandedItems: [], focusedItem: null }, initialState)),
18
18
  getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setExpandedItems: (0, utils_1.makeStateUpdater)("expandedItems", tree), setFocusedItem: (0, utils_1.makeStateUpdater)("focusedItem", tree) }, defaultConfig)),
19
+ stateHandlerNames: {
20
+ expandedItems: "setExpandedItems",
21
+ focusedItem: "setFocusedItem",
22
+ },
19
23
  createTreeInstance: (prev, instance) => (Object.assign(Object.assign({}, prev), { retrieveItemData: () => {
20
24
  throw new Error("No data-loader registered");
21
25
  }, retrieveChildrenIds: () => {
@@ -23,7 +27,6 @@ exports.treeFeature = {
23
27
  }, isItemExpanded: (itemId) => instance.getState().expandedItems.includes(itemId), getItemsMeta: () => {
24
28
  const { rootItemId } = instance.getConfig();
25
29
  const { expandedItems } = instance.getState();
26
- // console.log("!", instance.getConfig());
27
30
  const flatItems = [];
28
31
  const recursiveAdd = (itemId, parentId, level, setSize, posInSet) => {
29
32
  var _a;
@@ -50,23 +53,23 @@ exports.treeFeature = {
50
53
  }
51
54
  return flatItems;
52
55
  }, expandItem: (itemId) => {
53
- var _a, _b, _c;
56
+ var _a;
54
57
  if (!instance.getItemInstance(itemId).isFolder()) {
55
58
  return;
56
59
  }
57
60
  if ((_a = instance.getState().loadingItems) === null || _a === void 0 ? void 0 : _a.includes(itemId)) {
58
61
  return;
59
62
  }
60
- (_c = (_b = instance
61
- .getConfig()).setExpandedItems) === null || _c === void 0 ? void 0 : _c.call(_b, (expandedItems) => [...expandedItems, itemId]);
63
+ instance.applySubStateUpdate("expandedItems", (expandedItems) => [
64
+ ...expandedItems,
65
+ itemId,
66
+ ]);
62
67
  instance.rebuildTree();
63
68
  }, collapseItem: (itemId) => {
64
- var _a, _b;
65
69
  if (!instance.getItemInstance(itemId).isFolder()) {
66
70
  return;
67
71
  }
68
- (_b = (_a = instance
69
- .getConfig()).setExpandedItems) === null || _b === void 0 ? void 0 : _b.call(_a, (expandedItems) => expandedItems.filter((id) => id !== itemId));
72
+ instance.applySubStateUpdate("expandedItems", (expandedItems) => expandedItems.filter((id) => id !== itemId));
70
73
  instance.rebuildTree();
71
74
  },
72
75
  // TODO memo
@@ -74,8 +77,7 @@ exports.treeFeature = {
74
77
  var _a, _b;
75
78
  return ((_b = instance.getItemInstance((_a = instance.getState().focusedItem) !== null && _a !== void 0 ? _a : "")) !== null && _b !== void 0 ? _b : instance.getItems()[0]);
76
79
  }, focusItem: (itemId) => {
77
- var _a, _b;
78
- (_b = (_a = instance.getConfig()).setFocusedItem) === null || _b === void 0 ? void 0 : _b.call(_a, itemId);
80
+ instance.applySubStateUpdate("focusedItem", itemId);
79
81
  }, focusNextItem: () => {
80
82
  const { index } = instance.getFocusedItem().getItemMeta();
81
83
  const nextIndex = Math.min(index + 1, instance.getItems().length - 1);
@@ -84,7 +86,7 @@ exports.treeFeature = {
84
86
  const { index } = instance.getFocusedItem().getItemMeta();
85
87
  const nextIndex = Math.max(index - 1, 0);
86
88
  instance.focusItem(instance.getItems()[nextIndex].getId());
87
- }, updateDomFocus: (scrollIntoView) => {
89
+ }, updateDomFocus: () => {
88
90
  // Required because if the state is managed outside in react, the state only updated during next render
89
91
  setTimeout(() => __awaiter(void 0, void 0, void 0, function* () {
90
92
  var _a, _b;
@@ -95,9 +97,6 @@ exports.treeFeature = {
95
97
  if (!focusedElement)
96
98
  return;
97
99
  focusedElement.focus();
98
- // if (scrollIntoView) {
99
- // focusedElement.scrollIntoView();
100
- // }
101
100
  }));
102
101
  }, getContainerProps: () => {
103
102
  var _a;
@@ -105,10 +104,15 @@ exports.treeFeature = {
105
104
  } })),
106
105
  createItemInstance: (prev, item, tree) => (Object.assign(Object.assign({}, prev), { isLoading: () => {
107
106
  throw new Error("No data-loader registered");
108
- }, getId: () => item.getItemMeta().itemId, getProps: () => {
107
+ }, scrollTo: (scrollIntoViewArg) => __awaiter(void 0, void 0, void 0, function* () {
108
+ var _a, _b;
109
+ (_b = (_a = tree.getConfig()).scrollToItem) === null || _b === void 0 ? void 0 : _b.call(_a, item);
110
+ yield (0, utils_1.poll)(() => item.getElement() !== null, 20);
111
+ item.getElement().scrollIntoView(scrollIntoViewArg);
112
+ }), getId: () => item.getItemMeta().itemId, getProps: () => {
109
113
  var _a;
110
114
  const itemMeta = item.getItemMeta();
111
- return Object.assign(Object.assign({}, (_a = prev.getProps) === null || _a === void 0 ? void 0 : _a.call(prev)), { role: "treeitem", "aria-setsize": itemMeta.setSize, "aria-posinset": itemMeta.posInSet, "aria-selected": false, "aria-label": item.getItemName(), "aria-level": itemMeta.level, tabIndex: item.isFocused() ? 0 : -1, onClick: (e) => {
115
+ return Object.assign(Object.assign({}, (_a = prev.getProps) === null || _a === void 0 ? void 0 : _a.call(prev)), { role: "treeitem", "aria-setsize": itemMeta.setSize, "aria-posinset": itemMeta.posInSet, "aria-selected": "false", "aria-label": item.getItemName(), "aria-level": itemMeta.level, tabIndex: item.isFocused() ? 0 : -1, onClick: item.getMemoizedProp("tree/onClick", () => (e) => {
112
116
  item.setFocused();
113
117
  item.primaryAction();
114
118
  if (e.ctrlKey || e.shiftKey || e.metaKey) {
@@ -123,7 +127,7 @@ exports.treeFeature = {
123
127
  else {
124
128
  item.expand();
125
129
  }
126
- } });
130
+ }) });
127
131
  }, expand: () => tree.expandItem(item.getItemMeta().itemId), collapse: () => tree.collapseItem(item.getItemMeta().itemId), getItemData: () => tree.retrieveItemData(item.getItemMeta().itemId), isExpanded: () => tree.getState().expandedItems.includes(item.getItemMeta().itemId), isFocused: () => tree.getState().focusedItem === item.getItemMeta().itemId ||
128
132
  (tree.getState().focusedItem === null && item.getItemMeta().index === 0), isFolder: () => item.getItemMeta().level === -1 ||
129
133
  tree.getConfig().isItemFolder(item), getItemName: () => {
@@ -137,14 +141,27 @@ exports.treeFeature = {
137
141
  }
138
142
  }
139
143
  return tree.getItemInstance(tree.getConfig().rootItemId);
140
- }, () => [item.getItemMeta()]), getIndexInParent: () => {
141
- var _a, _b;
142
- return item.getItemMeta().index -
143
- ((_b = (_a = item.getParent()) === null || _a === void 0 ? void 0 : _a.getItemMeta().index) !== null && _b !== void 0 ? _b : 0) -
144
- 1;
145
- }, getChildren: () => tree
144
+ }, () => [item.getItemMeta()]),
145
+ // TODO remove
146
+ getIndexInParent: () => item.getItemMeta().posInSet, getChildren: () => tree
146
147
  .retrieveChildrenIds(item.getItemMeta().itemId)
147
- .map((id) => tree.getItemInstance(id)), getTree: () => tree, getItemAbove: () => tree.getItems()[item.getItemMeta().index - 1], getItemBelow: () => tree.getItems()[item.getItemMeta().index + 1] })),
148
+ .map((id) => tree.getItemInstance(id)), getTree: () => tree, getItemAbove: () => tree.getItems()[item.getItemMeta().index - 1], getItemBelow: () => tree.getItems()[item.getItemMeta().index + 1], getMemoizedProp: (name, create, deps) => {
149
+ var _a, _b, _c, _d, _e;
150
+ var _f, _g;
151
+ const data = item.getDataRef();
152
+ const memoizedValue = (_a = data.current.memoizedValues) === null || _a === void 0 ? void 0 : _a[name];
153
+ if (memoizedValue &&
154
+ (!deps ||
155
+ ((_c = (_b = data.current.memoizedDeps) === null || _b === void 0 ? void 0 : _b[name]) === null || _c === void 0 ? void 0 : _c.every((d, i) => d === deps[i])))) {
156
+ return memoizedValue;
157
+ }
158
+ (_d = (_f = data.current).memoizedDeps) !== null && _d !== void 0 ? _d : (_f.memoizedDeps = {});
159
+ (_e = (_g = data.current).memoizedValues) !== null && _e !== void 0 ? _e : (_g.memoizedValues = {});
160
+ const value = create();
161
+ data.current.memoizedDeps[name] = deps;
162
+ data.current.memoizedValues[name] = value;
163
+ return value;
164
+ } })),
148
165
  hotkeys: {
149
166
  focusNextItem: {
150
167
  hotkey: "ArrowDown",
@@ -189,7 +206,7 @@ exports.treeFeature = {
189
206
  if ((!item.isExpanded() || !item.isFolder()) &&
190
207
  item.getItemMeta().level !== 0) {
191
208
  (_a = item.getParent()) === null || _a === void 0 ? void 0 : _a.setFocused();
192
- tree.updateDomFocus(true);
209
+ tree.updateDomFocus();
193
210
  }
194
211
  else {
195
212
  item.collapse();
@@ -7,6 +7,10 @@ export type ItemMeta = {
7
7
  setSize: number;
8
8
  posInSet: number;
9
9
  };
10
+ export type TreeItemDataRef = {
11
+ memoizedValues: Record<string, any>;
12
+ memoizedDeps: Record<string, any[] | undefined>;
13
+ };
10
14
  export type TreeFeatureDef<T> = {
11
15
  state: {
12
16
  expandedItems: string[];
@@ -30,8 +34,7 @@ export type TreeFeatureDef<T> = {
30
34
  getFocusedItem: () => ItemInstance<any>;
31
35
  focusNextItem: () => void;
32
36
  focusPreviousItem: () => void;
33
- scrollToItem: (item: ItemInstance<any>) => void;
34
- updateDomFocus: (scrollIntoView?: boolean) => void;
37
+ updateDomFocus: () => void;
35
38
  getContainerProps: () => Record<string, any>;
36
39
  };
37
40
  itemInstance: {
@@ -52,6 +55,8 @@ export type TreeFeatureDef<T> = {
52
55
  getTree: () => TreeInstance<T>;
53
56
  getItemAbove: () => ItemInstance<T> | null;
54
57
  getItemBelow: () => ItemInstance<T> | null;
58
+ getMemoizedProp: <X>(name: string, create: () => X, deps?: any[]) => X;
59
+ scrollTo: (scrollIntoViewArg?: boolean | ScrollIntoViewOptions) => Promise<void>;
55
60
  };
56
61
  hotkeys: "focusNextItem" | "focusPreviousItem" | "expandOrDown" | "collapseOrUp" | "focusFirstItem" | "focusLastItem";
57
62
  };
@@ -18,4 +18,6 @@ export * from "./features/drag-and-drop/feature";
18
18
  export * from "./features/search/feature";
19
19
  export * from "./features/renaming/feature";
20
20
  export * from "./features/expand-all/feature";
21
- export * from "./data-adapters/nested-data-adapter";
21
+ export * from "./utilities/create-on-drop-handler";
22
+ export * from "./utilities/insert-items-at-target";
23
+ export * from "./utilities/remove-items-from-parents";
package/lib/cjs/index.js CHANGED
@@ -34,4 +34,6 @@ __exportStar(require("./features/drag-and-drop/feature"), exports);
34
34
  __exportStar(require("./features/search/feature"), exports);
35
35
  __exportStar(require("./features/renaming/feature"), exports);
36
36
  __exportStar(require("./features/expand-all/feature"), exports);
37
- __exportStar(require("./data-adapters/nested-data-adapter"), exports);
37
+ __exportStar(require("./utilities/create-on-drop-handler"), exports);
38
+ __exportStar(require("./utilities/insert-items-at-target"), exports);
39
+ __exportStar(require("./utilities/remove-items-from-parents"), exports);
@@ -52,6 +52,7 @@ export type CustomHotkeysConfig<T, F extends FeatureDef = FeatureDefs<T>> = Part
52
52
  export type FeatureImplementation<T = any, D extends FeatureDef = any, F extends FeatureDef = EmptyFeatureDef> = {
53
53
  key?: string;
54
54
  dependingFeatures?: string[];
55
+ stateHandlerNames?: Partial<Record<keyof MergedFeatures<F>["state"], keyof MergedFeatures<F>["config"]>>;
55
56
  getInitialState?: (initialState: Partial<MergedFeatures<F>["state"]>, tree: MergedFeatures<F>["treeInstance"]) => Partial<D["state"] & MergedFeatures<F>["state"]>;
56
57
  getDefaultConfig?: (defaultConfig: Partial<MergedFeatures<F>["config"]>, tree: MergedFeatures<F>["treeInstance"]) => Partial<D["config"] & MergedFeatures<F>["config"]>;
57
58
  createTreeInstance?: (prev: MergedFeatures<F>["treeInstance"], instance: MergedFeatures<F>["treeInstance"]) => D["treeInstance"] & MergedFeatures<F>["treeInstance"];
@@ -60,9 +61,6 @@ export type FeatureImplementation<T = any, D extends FeatureDef = any, F extends
60
61
  onTreeUnmount?: (instance: MergedFeatures<F>["treeInstance"], treeElement: HTMLElement) => void;
61
62
  onItemMount?: (instance: MergedFeatures<F>["itemInstance"], itemElement: HTMLElement, tree: MergedFeatures<F>["treeInstance"]) => void;
62
63
  onItemUnmount?: (instance: MergedFeatures<F>["itemInstance"], itemElement: HTMLElement, tree: MergedFeatures<F>["treeInstance"]) => void;
63
- setState?: (instance: MergedFeatures<F>["treeInstance"]) => void;
64
- onConfigChange?: (instance: MergedFeatures<F>["treeInstance"]) => void;
65
- onStateOrConfigChange?: (instance: MergedFeatures<F>["treeInstance"]) => void;
66
64
  hotkeys?: HotkeysConfig<T, D>;
67
65
  };
68
66
  export {};
@@ -0,0 +1,3 @@
1
+ import { ItemInstance } from "../types/core";
2
+ import { DropTarget } from "../features/drag-and-drop/types";
3
+ export declare const createOnDropHandler: <T>(onChangeChildren: (item: ItemInstance<T>, newChildren: string[]) => void) => (items: ItemInstance<T>[], target: DropTarget<T>) => void;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createOnDropHandler = void 0;
4
+ const remove_items_from_parents_1 = require("./remove-items-from-parents");
5
+ const insert_items_at_target_1 = require("./insert-items-at-target");
6
+ const createOnDropHandler = (onChangeChildren) => (items, target) => {
7
+ const itemIds = items.map((item) => item.getId());
8
+ (0, remove_items_from_parents_1.removeItemsFromParents)(items, onChangeChildren);
9
+ (0, insert_items_at_target_1.insertItemsAtTarget)(itemIds, target, onChangeChildren);
10
+ };
11
+ exports.createOnDropHandler = createOnDropHandler;
@@ -0,0 +1,3 @@
1
+ import { ItemInstance } from "../types/core";
2
+ import { DropTarget } from "../features/drag-and-drop/types";
3
+ export declare const insertItemsAtTarget: <T>(itemIds: string[], target: DropTarget<T>, onChangeChildren: (item: ItemInstance<T>, newChildrenIds: string[]) => void) => void;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.insertItemsAtTarget = void 0;
4
+ const insertItemsAtTarget = (itemIds, target, onChangeChildren) => {
5
+ // add moved items to new common parent, if dropped onto parent
6
+ if (target.childIndex === null) {
7
+ onChangeChildren(target.item, [
8
+ ...target.item.getChildren().map((item) => item.getId()),
9
+ ...itemIds,
10
+ ]);
11
+ // TODO items[0].getTree().rebuildTree();
12
+ return;
13
+ }
14
+ // add moved items to new common parent, if dropped between siblings
15
+ const oldChildren = target.item.getChildren();
16
+ const newChildren = [
17
+ ...oldChildren.slice(0, target.insertionIndex).map((item) => item.getId()),
18
+ ...itemIds,
19
+ ...oldChildren.slice(target.insertionIndex).map((item) => item.getId()),
20
+ ];
21
+ onChangeChildren(target.item, newChildren);
22
+ target.item.getTree().rebuildTree();
23
+ };
24
+ exports.insertItemsAtTarget = insertItemsAtTarget;
@@ -0,0 +1,2 @@
1
+ import { ItemInstance } from "../types/core";
2
+ export declare const removeItemsFromParents: <T>(movedItems: ItemInstance<T>[], onChangeChildren: (item: ItemInstance<T>, newChildrenIds: string[]) => void) => void;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.removeItemsFromParents = void 0;
4
+ const removeItemsFromParents = (movedItems, onChangeChildren) => {
5
+ var _a;
6
+ // TODO bulk sibling changes together
7
+ for (const item of movedItems) {
8
+ const siblings = (_a = item.getParent()) === null || _a === void 0 ? void 0 : _a.getChildren();
9
+ if (siblings) {
10
+ onChangeChildren(item.getParent(), siblings
11
+ .filter((sibling) => sibling.getId() !== item.getId())
12
+ .map((i) => i.getId()));
13
+ }
14
+ }
15
+ movedItems[0].getTree().rebuildTree();
16
+ };
17
+ exports.removeItemsFromParents = removeItemsFromParents;
@@ -1,9 +1,6 @@
1
- import { ItemInstance, TreeState, Updater } from "./types/core";
2
- import { DropTarget } from "./features/drag-and-drop/types";
1
+ import { TreeState, Updater } from "./types/core";
3
2
  export type NoInfer<T> = [T][T extends any ? 0 : never];
4
3
  export declare const memo: <D extends readonly any[], R>(fn: (...args_0: D) => R, deps: () => [...D]) => () => R;
5
4
  export declare function functionalUpdate<T>(updater: Updater<T>, input: T): T;
6
5
  export declare function makeStateUpdater<K extends keyof TreeState<any>>(key: K, instance: unknown): (updater: Updater<TreeState<any>[K]>) => void;
7
- export declare const scrollIntoView: (element: Element | undefined | null) => void;
8
- export declare const performItemsMove: <T>(items: ItemInstance<T>[], target: DropTarget<T>, onChangeChildren: (item: ItemInstance<T>, newChildren: ItemInstance<T>[]) => void) => void;
9
6
  export declare const poll: (fn: () => boolean, interval?: number, timeout?: number) => Promise<void>;
package/lib/cjs/utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.poll = exports.performItemsMove = exports.scrollIntoView = exports.makeStateUpdater = exports.functionalUpdate = exports.memo = void 0;
3
+ exports.poll = exports.makeStateUpdater = exports.functionalUpdate = exports.memo = void 0;
4
4
  const memo = (fn, deps) => {
5
5
  let value;
6
6
  let oldDeps = null;
@@ -37,58 +37,6 @@ function makeStateUpdater(key, instance) {
37
37
  };
38
38
  }
39
39
  exports.makeStateUpdater = makeStateUpdater;
40
- const scrollIntoView = (element) => {
41
- if (!element) {
42
- return;
43
- }
44
- if (element.scrollIntoViewIfNeeded) {
45
- element.scrollIntoViewIfNeeded();
46
- }
47
- else {
48
- const boundingBox = element.getBoundingClientRect();
49
- const isElementInViewport = boundingBox.top >= 0 &&
50
- boundingBox.left >= 0 &&
51
- boundingBox.bottom <=
52
- (window.innerHeight || document.documentElement.clientHeight) &&
53
- boundingBox.right <=
54
- (window.innerWidth || document.documentElement.clientWidth);
55
- if (!isElementInViewport) {
56
- element.scrollIntoView();
57
- }
58
- }
59
- };
60
- exports.scrollIntoView = scrollIntoView;
61
- const performItemsMove = (items, target, onChangeChildren) => {
62
- var _a;
63
- const numberOfDragItemsBeforeTarget = !target.childIndex
64
- ? 0
65
- : target.item
66
- .getChildren()
67
- .slice(0, target.childIndex)
68
- .filter((child) => items.some((item) => item.getId() === child.getId()))
69
- .length;
70
- // TODO bulk sibling changes together
71
- for (const item of items) {
72
- const siblings = (_a = item.getParent()) === null || _a === void 0 ? void 0 : _a.getChildren();
73
- if (siblings) {
74
- onChangeChildren(item.getParent(), siblings.filter((sibling) => sibling.getId() !== item.getId()));
75
- }
76
- }
77
- if (target.childIndex === null) {
78
- onChangeChildren(target.item, [...target.item.getChildren(), ...items]);
79
- items[0].getTree().rebuildTree();
80
- return;
81
- }
82
- const oldChildren = target.item.getChildren();
83
- const newChildren = [
84
- ...oldChildren.slice(0, target.childIndex - numberOfDragItemsBeforeTarget),
85
- ...items,
86
- ...oldChildren.slice(target.childIndex - numberOfDragItemsBeforeTarget),
87
- ];
88
- onChangeChildren(target.item, newChildren);
89
- items[0].getTree().rebuildTree();
90
- };
91
- exports.performItemsMove = performItemsMove;
92
40
  const poll = (fn, interval = 100, timeout = 1000) => new Promise((resolve) => {
93
41
  let clear;
94
42
  const i = setInterval(() => {
@@ -8,11 +8,12 @@ const buildItemInstance = (features, tree, itemId) => {
8
8
  return itemInstance;
9
9
  };
10
10
  export const createTree = (initialConfig) => {
11
- var _a, _b, _c, _d, _e;
11
+ var _a, _b, _c, _d, _e, _f;
12
12
  const treeInstance = {};
13
13
  const additionalFeatures = [treeFeature, ...((_a = initialConfig.features) !== null && _a !== void 0 ? _a : [])];
14
- let state = additionalFeatures.reduce((acc, feature) => { var _a, _b; return (_b = (_a = feature.getInitialState) === null || _a === void 0 ? void 0 : _a.call(feature, acc, treeInstance)) !== null && _b !== void 0 ? _b : acc; }, (_b = initialConfig.state) !== null && _b !== void 0 ? _b : {});
14
+ let state = additionalFeatures.reduce((acc, feature) => { var _a, _b; return (_b = (_a = feature.getInitialState) === null || _a === void 0 ? void 0 : _a.call(feature, acc, treeInstance)) !== null && _b !== void 0 ? _b : acc; }, (_c = (_b = initialConfig.initialState) !== null && _b !== void 0 ? _b : initialConfig.state) !== null && _c !== void 0 ? _c : {});
15
15
  let config = additionalFeatures.reduce((acc, feature) => { var _a, _b; return (_b = (_a = feature.getDefaultConfig) === null || _a === void 0 ? void 0 : _a.call(feature, acc, treeInstance)) !== null && _b !== void 0 ? _b : acc; }, initialConfig);
16
+ const stateHandlerNames = additionalFeatures.reduce((acc, feature) => (Object.assign(Object.assign({}, acc), feature.stateHandlerNames)), {});
16
17
  let treeElement;
17
18
  const treeDataRef = { current: {} };
18
19
  const itemInstancesMap = {};
@@ -57,10 +58,13 @@ export const createTree = (initialConfig) => {
57
58
  key: "main",
58
59
  createTreeInstance: (prev) => (Object.assign(Object.assign({}, prev), { getState: () => state, setState: (updater) => {
59
60
  var _a;
60
- state = typeof updater === "function" ? updater(state) : updater;
61
+ // Not necessary, since I think the subupdate below keeps the state fresh anyways?
62
+ // state = typeof updater === "function" ? updater(state) : updater;
61
63
  (_a = config.setState) === null || _a === void 0 ? void 0 : _a.call(config, state);
62
- eachFeature((feature) => { var _a; return (_a = feature.setState) === null || _a === void 0 ? void 0 : _a.call(feature, treeInstance); });
63
- eachFeature((feature) => { var _a; return (_a = feature.onStateOrConfigChange) === null || _a === void 0 ? void 0 : _a.call(feature, treeInstance); });
64
+ }, applySubStateUpdate: (stateName, updater) => {
65
+ state[stateName] =
66
+ typeof updater === "function" ? updater(state[stateName]) : updater;
67
+ config[stateHandlerNames[stateName]](state[stateName]);
64
68
  }, rebuildTree: () => {
65
69
  var _a;
66
70
  rebuildItemMeta(mainFeature);
@@ -69,10 +73,7 @@ export const createTree = (initialConfig) => {
69
73
  config = typeof updater === "function" ? updater(config) : updater;
70
74
  if (config.state) {
71
75
  state = Object.assign(Object.assign({}, state), config.state);
72
- eachFeature((feature) => { var _a; return (_a = feature.setState) === null || _a === void 0 ? void 0 : _a.call(feature, treeInstance); });
73
76
  }
74
- eachFeature((feature) => { var _a; return (_a = feature.onConfigChange) === null || _a === void 0 ? void 0 : _a.call(feature, treeInstance); });
75
- eachFeature((feature) => { var _a; return (_a = feature.onStateOrConfigChange) === null || _a === void 0 ? void 0 : _a.call(feature, treeInstance); });
76
77
  }, getItemInstance: (itemId) => itemInstancesMap[itemId], getItems: () => itemInstances, registerElement: (element) => {
77
78
  if (treeElement === element) {
78
79
  return;
@@ -104,8 +105,8 @@ export const createTree = (initialConfig) => {
104
105
  // todo sort features
105
106
  const features = [mainFeature, ...additionalFeatures];
106
107
  for (const feature of features) {
107
- Object.assign(treeInstance, (_d = (_c = feature.createTreeInstance) === null || _c === void 0 ? void 0 : _c.call(feature, Object.assign({}, treeInstance), treeInstance)) !== null && _d !== void 0 ? _d : {});
108
- Object.assign(hotkeyPresets, (_e = feature.hotkeys) !== null && _e !== void 0 ? _e : {});
108
+ Object.assign(treeInstance, (_e = (_d = feature.createTreeInstance) === null || _d === void 0 ? void 0 : _d.call(feature, Object.assign({}, treeInstance), treeInstance)) !== null && _e !== void 0 ? _e : {});
109
+ Object.assign(hotkeyPresets, (_f = feature.hotkeys) !== null && _f !== void 0 ? _f : {});
109
110
  }
110
111
  rebuildItemMeta(mainFeature);
111
112
  return treeInstance;
@@ -4,43 +4,52 @@ export const asyncDataLoaderFeature = {
4
4
  dependingFeatures: ["main"],
5
5
  getInitialState: (initialState) => (Object.assign({ loadingItems: [] }, initialState)),
6
6
  getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setLoadingItems: makeStateUpdater("loadingItems", tree) }, defaultConfig)),
7
+ stateHandlerNames: {
8
+ loadingItems: "setLoadingItems",
9
+ },
7
10
  createTreeInstance: (prev, instance) => (Object.assign(Object.assign({}, prev), { retrieveItemData: (itemId) => {
8
- var _a, _b, _c, _d, _e, _f;
9
- var _g, _h;
11
+ var _a, _b, _c, _d, _e;
12
+ var _f, _g;
10
13
  const config = instance.getConfig();
11
14
  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 = {});
15
+ (_a = (_f = dataRef.current).itemData) !== null && _a !== void 0 ? _a : (_f.itemData = {});
16
+ (_b = (_g = dataRef.current).childrenIds) !== null && _b !== void 0 ? _b : (_g.childrenIds = {});
14
17
  if (dataRef.current.itemData[itemId]) {
15
18
  return dataRef.current.itemData[itemId];
16
19
  }
17
20
  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;
21
+ instance.applySubStateUpdate("loadingItems", (loadingItems) => [
22
+ ...loadingItems,
23
+ itemId,
24
+ ]);
25
+ (_c = config.asyncDataLoader) === null || _c === void 0 ? void 0 : _c.getItem(itemId).then((item) => {
26
+ var _a;
21
27
  dataRef.current.itemData[itemId] = item;
22
28
  (_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));
29
+ instance.applySubStateUpdate("loadingItems", (loadingItems) => loadingItems.filter((id) => id !== itemId));
24
30
  });
25
31
  }
26
- return (_f = (_e = config.createLoadingItemData) === null || _e === void 0 ? void 0 : _e.call(config)) !== null && _f !== void 0 ? _f : null;
32
+ return (_e = (_d = config.createLoadingItemData) === null || _d === void 0 ? void 0 : _d.call(config)) !== null && _e !== void 0 ? _e : null;
27
33
  }, retrieveChildrenIds: (itemId) => {
28
- var _a, _b, _c, _d, _e, _f;
29
- var _g, _h;
34
+ var _a, _b, _c, _d, _e;
35
+ var _f, _g;
30
36
  const config = instance.getConfig();
31
37
  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 = {});
38
+ (_a = (_f = dataRef.current).itemData) !== null && _a !== void 0 ? _a : (_f.itemData = {});
39
+ (_b = (_g = dataRef.current).childrenIds) !== null && _b !== void 0 ? _b : (_g.childrenIds = {});
34
40
  if (dataRef.current.childrenIds[itemId]) {
35
41
  return dataRef.current.childrenIds[itemId];
36
42
  }
37
43
  if (instance.getState().loadingItems.includes(itemId)) {
38
44
  return [];
39
45
  }
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;
46
+ instance.applySubStateUpdate("loadingItems", (loadingItems) => [
47
+ ...loadingItems,
48
+ itemId,
49
+ ]);
50
+ if ((_c = config.asyncDataLoader) === null || _c === void 0 ? void 0 : _c.getChildrenWithData) {
51
+ (_d = config.asyncDataLoader) === null || _d === void 0 ? void 0 : _d.getChildrenWithData(itemId).then((children) => {
52
+ var _a, _b;
44
53
  for (const { id, data } of children) {
45
54
  dataRef.current.itemData[id] = data;
46
55
  (_a = config.onLoadedItem) === null || _a === void 0 ? void 0 : _a.call(config, id, data);
@@ -48,16 +57,16 @@ export const asyncDataLoaderFeature = {
48
57
  const childrenIds = children.map(({ id }) => id);
49
58
  dataRef.current.childrenIds[itemId] = childrenIds;
50
59
  (_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));
60
+ instance.applySubStateUpdate("loadingItems", (loadingItems) => loadingItems.filter((id) => id !== itemId));
52
61
  instance.rebuildTree();
53
62
  });
54
63
  }
55
64
  else {
56
- (_f = config.asyncDataLoader) === null || _f === void 0 ? void 0 : _f.getChildren(itemId).then((childrenIds) => {
57
- var _a, _b;
65
+ (_e = config.asyncDataLoader) === null || _e === void 0 ? void 0 : _e.getChildren(itemId).then((childrenIds) => {
66
+ var _a;
58
67
  dataRef.current.childrenIds[itemId] = childrenIds;
59
68
  (_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));
69
+ instance.applySubStateUpdate("loadingItems", (loadingItems) => loadingItems.filter((id) => id !== itemId));
61
70
  instance.rebuildTree();
62
71
  });
63
72
  }