@headless-tree/core 0.0.11 → 0.0.12

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 (108) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/lib/cjs/core/build-static-instance.js +1 -2
  3. package/lib/cjs/core/create-tree.js +7 -4
  4. package/lib/cjs/features/async-data-loader/feature.d.ts +1 -4
  5. package/lib/cjs/features/async-data-loader/feature.js +5 -7
  6. package/lib/cjs/features/async-data-loader/types.d.ts +2 -5
  7. package/lib/cjs/features/drag-and-drop/feature.d.ts +2 -3
  8. package/lib/cjs/features/drag-and-drop/feature.js +27 -24
  9. package/lib/cjs/features/drag-and-drop/types.d.ts +3 -3
  10. package/lib/cjs/features/drag-and-drop/utils.d.ts +1 -1
  11. package/lib/cjs/features/drag-and-drop/utils.js +4 -4
  12. package/lib/cjs/features/expand-all/feature.d.ts +1 -5
  13. package/lib/cjs/features/hotkeys-core/feature.d.ts +1 -3
  14. package/lib/cjs/features/prop-memoization/feature.d.ts +2 -0
  15. package/lib/cjs/features/prop-memoization/feature.js +48 -0
  16. package/lib/cjs/features/prop-memoization/types.d.ts +10 -0
  17. package/lib/cjs/features/prop-memoization/types.js +2 -0
  18. package/lib/cjs/features/renaming/feature.d.ts +1 -4
  19. package/lib/cjs/features/renaming/feature.js +8 -9
  20. package/lib/cjs/features/renaming/types.d.ts +1 -1
  21. package/lib/cjs/features/search/feature.d.ts +1 -4
  22. package/lib/cjs/features/selection/feature.d.ts +1 -4
  23. package/lib/cjs/features/selection/feature.js +35 -25
  24. package/lib/cjs/features/selection/types.d.ts +1 -1
  25. package/lib/cjs/features/sync-data-loader/feature.d.ts +1 -3
  26. package/lib/cjs/features/tree/feature.d.ts +1 -6
  27. package/lib/cjs/features/tree/feature.js +40 -57
  28. package/lib/cjs/features/tree/types.d.ts +0 -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 +1 -1
  33. package/lib/cjs/test-utils/test-tree-expect.d.ts +1 -1
  34. package/lib/cjs/test-utils/test-tree-expect.js +1 -1
  35. package/lib/cjs/test-utils/test-tree.d.ts +1 -1
  36. package/lib/cjs/test-utils/test-tree.js +9 -1
  37. package/lib/cjs/types/core.d.ts +29 -30
  38. package/lib/esm/core/build-static-instance.js +1 -2
  39. package/lib/esm/core/create-tree.js +7 -4
  40. package/lib/esm/features/async-data-loader/feature.d.ts +1 -4
  41. package/lib/esm/features/async-data-loader/feature.js +5 -7
  42. package/lib/esm/features/async-data-loader/types.d.ts +2 -5
  43. package/lib/esm/features/drag-and-drop/feature.d.ts +2 -3
  44. package/lib/esm/features/drag-and-drop/feature.js +27 -24
  45. package/lib/esm/features/drag-and-drop/types.d.ts +3 -3
  46. package/lib/esm/features/drag-and-drop/utils.d.ts +1 -1
  47. package/lib/esm/features/drag-and-drop/utils.js +4 -4
  48. package/lib/esm/features/expand-all/feature.d.ts +1 -5
  49. package/lib/esm/features/hotkeys-core/feature.d.ts +1 -3
  50. package/lib/esm/features/prop-memoization/feature.d.ts +2 -0
  51. package/lib/esm/features/prop-memoization/feature.js +45 -0
  52. package/lib/esm/features/prop-memoization/types.d.ts +10 -0
  53. package/lib/esm/features/prop-memoization/types.js +1 -0
  54. package/lib/esm/features/renaming/feature.d.ts +1 -4
  55. package/lib/esm/features/renaming/feature.js +8 -9
  56. package/lib/esm/features/renaming/types.d.ts +1 -1
  57. package/lib/esm/features/search/feature.d.ts +1 -4
  58. package/lib/esm/features/selection/feature.d.ts +1 -4
  59. package/lib/esm/features/selection/feature.js +35 -25
  60. package/lib/esm/features/selection/types.d.ts +1 -1
  61. package/lib/esm/features/sync-data-loader/feature.d.ts +1 -3
  62. package/lib/esm/features/tree/feature.d.ts +1 -6
  63. package/lib/esm/features/tree/feature.js +40 -57
  64. package/lib/esm/features/tree/types.d.ts +0 -5
  65. package/lib/esm/index.d.ts +2 -0
  66. package/lib/esm/index.js +2 -0
  67. package/lib/esm/mddocs-entry.d.ts +10 -0
  68. package/lib/esm/test-utils/test-tree-do.d.ts +1 -1
  69. package/lib/esm/test-utils/test-tree-expect.d.ts +1 -1
  70. package/lib/esm/test-utils/test-tree-expect.js +1 -1
  71. package/lib/esm/test-utils/test-tree.d.ts +1 -1
  72. package/lib/esm/test-utils/test-tree.js +9 -1
  73. package/lib/esm/types/core.d.ts +29 -30
  74. package/package.json +1 -1
  75. package/src/core/build-proxified-instance.ts +5 -3
  76. package/src/core/build-static-instance.ts +1 -2
  77. package/src/core/core.spec.ts +210 -0
  78. package/src/core/create-tree.ts +13 -16
  79. package/src/features/async-data-loader/async-data-loader.spec.ts +12 -31
  80. package/src/features/async-data-loader/feature.ts +8 -20
  81. package/src/features/async-data-loader/types.ts +2 -6
  82. package/src/features/drag-and-drop/drag-and-drop.spec.ts +4 -3
  83. package/src/features/drag-and-drop/feature.ts +87 -86
  84. package/src/features/drag-and-drop/types.ts +4 -4
  85. package/src/features/drag-and-drop/utils.ts +4 -4
  86. package/src/features/expand-all/expand-all.spec.ts +5 -1
  87. package/src/features/expand-all/feature.ts +1 -12
  88. package/src/features/hotkeys-core/feature.ts +4 -13
  89. package/src/features/prop-memoization/feature.ts +51 -0
  90. package/src/features/prop-memoization/prop-memoization.spec.ts +68 -0
  91. package/src/features/prop-memoization/types.ts +11 -0
  92. package/src/features/renaming/feature.ts +11 -20
  93. package/src/features/renaming/renaming.spec.ts +11 -9
  94. package/src/features/renaming/types.ts +1 -1
  95. package/src/features/search/feature.ts +2 -8
  96. package/src/features/search/search.spec.ts +3 -1
  97. package/src/features/selection/feature.ts +45 -47
  98. package/src/features/selection/selection.spec.ts +13 -14
  99. package/src/features/selection/types.ts +0 -2
  100. package/src/features/sync-data-loader/feature.ts +1 -7
  101. package/src/features/tree/feature.ts +47 -85
  102. package/src/features/tree/tree.spec.ts +24 -64
  103. package/src/features/tree/types.ts +0 -6
  104. package/src/index.ts +2 -0
  105. package/src/mddocs-entry.ts +13 -0
  106. package/src/test-utils/test-tree-expect.ts +1 -1
  107. package/src/test-utils/test-tree.ts +11 -1
  108. package/src/types/core.ts +56 -147
@@ -20,7 +20,6 @@ exports.treeFeature = {
20
20
  focusedItem: "setFocusedItem",
21
21
  },
22
22
  treeInstance: {
23
- isItemExpanded: ({ tree }, itemId) => tree.getState().expandedItems.includes(itemId),
24
23
  getItemsMeta: ({ tree }) => {
25
24
  const { rootItemId } = tree.getConfig();
26
25
  const { expandedItems } = tree.getState();
@@ -52,44 +51,22 @@ exports.treeFeature = {
52
51
  }
53
52
  return flatItems;
54
53
  },
55
- expandItem: ({ tree }, itemId) => {
56
- var _a;
57
- if (!tree.getItemInstance(itemId).isFolder()) {
58
- return;
59
- }
60
- if ((_a = tree.getState().loadingItems) === null || _a === void 0 ? void 0 : _a.includes(itemId)) {
61
- return;
62
- }
63
- tree.applySubStateUpdate("expandedItems", (expandedItems) => [
64
- ...expandedItems,
65
- itemId,
66
- ]);
67
- tree.rebuildTree();
68
- },
69
- collapseItem: ({ tree }, itemId) => {
70
- if (!tree.getItemInstance(itemId).isFolder()) {
71
- return;
72
- }
73
- tree.applySubStateUpdate("expandedItems", (expandedItems) => expandedItems.filter((id) => id !== itemId));
74
- tree.rebuildTree();
75
- },
76
54
  // TODO memo
77
55
  getFocusedItem: ({ tree }) => {
78
56
  var _a, _b;
79
57
  return ((_b = tree.getItemInstance((_a = tree.getState().focusedItem) !== null && _a !== void 0 ? _a : "")) !== null && _b !== void 0 ? _b : tree.getItems()[0]);
80
58
  },
81
- focusItem: ({ tree }, itemId) => {
82
- tree.applySubStateUpdate("focusedItem", itemId);
83
- },
84
59
  focusNextItem: ({ tree }) => {
60
+ var _a;
85
61
  const { index } = tree.getFocusedItem().getItemMeta();
86
62
  const nextIndex = Math.min(index + 1, tree.getItems().length - 1);
87
- tree.focusItem(tree.getItems()[nextIndex].getId());
63
+ (_a = tree.getItems()[nextIndex]) === null || _a === void 0 ? void 0 : _a.setFocused();
88
64
  },
89
65
  focusPreviousItem: ({ tree }) => {
66
+ var _a;
90
67
  const { index } = tree.getFocusedItem().getItemMeta();
91
68
  const nextIndex = Math.max(index - 1, 0);
92
- tree.focusItem(tree.getItems()[nextIndex].getId());
69
+ (_a = tree.getItems()[nextIndex]) === null || _a === void 0 ? void 0 : _a.setFocused();
93
70
  },
94
71
  updateDomFocus: ({ tree }) => {
95
72
  // Required because if the state is managed outside in react, the state only updated during next render
@@ -104,7 +81,8 @@ exports.treeFeature = {
104
81
  focusedElement.focus();
105
82
  }));
106
83
  },
107
- getContainerProps: ({ prev }) => (Object.assign(Object.assign({}, prev === null || prev === void 0 ? void 0 : prev()), { role: "tree", ariaLabel: "", ariaActivedescendant: "" })),
84
+ // TODO add label parameter
85
+ getContainerProps: ({ prev, tree }) => (Object.assign(Object.assign({}, prev === null || prev === void 0 ? void 0 : prev()), { role: "tree", "aria-label": "", ref: tree.registerElement })),
108
86
  // relevant for hotkeys of this feature
109
87
  isSearchOpen: () => false,
110
88
  },
@@ -115,10 +93,10 @@ exports.treeFeature = {
115
93
  yield (0, utils_1.poll)(() => item.getElement() !== null, 20);
116
94
  (_d = item.getElement()) === null || _d === void 0 ? void 0 : _d.scrollIntoView(scrollIntoViewArg);
117
95
  }),
118
- getId: ({ item }) => item.getItemMeta().itemId,
96
+ getId: ({ itemId }) => itemId,
119
97
  getProps: ({ item, prev }) => {
120
98
  const itemMeta = item.getItemMeta();
121
- return Object.assign(Object.assign({}, prev === null || prev === void 0 ? void 0 : 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) => {
99
+ return Object.assign(Object.assign({}, prev === null || prev === void 0 ? void 0 : prev()), { ref: item.registerElement, 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) => {
122
100
  item.setFocused();
123
101
  item.primaryAction();
124
102
  if (e.ctrlKey || e.shiftKey || e.metaKey) {
@@ -133,18 +111,37 @@ exports.treeFeature = {
133
111
  else {
134
112
  item.expand();
135
113
  }
136
- }) });
114
+ } });
115
+ },
116
+ expand: ({ tree, item, itemId }) => {
117
+ var _a;
118
+ if (!item.isFolder()) {
119
+ return;
120
+ }
121
+ if ((_a = tree.getState().loadingItems) === null || _a === void 0 ? void 0 : _a.includes(itemId)) {
122
+ return;
123
+ }
124
+ tree.applySubStateUpdate("expandedItems", (expandedItems) => [
125
+ ...expandedItems,
126
+ itemId,
127
+ ]);
128
+ tree.rebuildTree();
137
129
  },
138
- expand: ({ tree, item }) => tree.expandItem(item.getItemMeta().itemId),
139
- collapse: ({ tree, item }) => tree.collapseItem(item.getItemMeta().itemId),
140
- getItemData: ({ tree, item }) => tree.retrieveItemData(item.getItemMeta().itemId),
130
+ collapse: ({ tree, item, itemId }) => {
131
+ if (!item.isFolder()) {
132
+ return;
133
+ }
134
+ tree.applySubStateUpdate("expandedItems", (expandedItems) => expandedItems.filter((id) => id !== itemId));
135
+ tree.rebuildTree();
136
+ },
137
+ getItemData: ({ tree, itemId }) => tree.retrieveItemData(itemId),
141
138
  equals: ({ item }, other) => item.getId() === (other === null || other === void 0 ? void 0 : other.getId()),
142
- isExpanded: ({ tree, item }) => tree.getState().expandedItems.includes(item.getItemMeta().itemId),
139
+ isExpanded: ({ tree, itemId }) => tree.getState().expandedItems.includes(itemId),
143
140
  isDescendentOf: ({ item }, parentId) => {
144
141
  const parent = item.getParent();
145
142
  return Boolean((parent === null || parent === void 0 ? void 0 : parent.getId()) === parentId || (parent === null || parent === void 0 ? void 0 : parent.isDescendentOf(parentId)));
146
143
  },
147
- isFocused: ({ tree, item }) => tree.getState().focusedItem === item.getItemMeta().itemId ||
144
+ isFocused: ({ tree, item, itemId }) => tree.getState().focusedItem === itemId ||
148
145
  (tree.getState().focusedItem === null && item.getItemMeta().index === 0),
149
146
  isFolder: ({ tree, item }) => item.getItemMeta().level === -1 ||
150
147
  tree.getConfig().isItemFolder(item),
@@ -152,12 +149,13 @@ exports.treeFeature = {
152
149
  const config = tree.getConfig();
153
150
  return config.getItemName(item);
154
151
  },
155
- setFocused: ({ tree, item }) => tree.focusItem(item.getItemMeta().itemId),
152
+ setFocused: ({ tree, itemId }) => {
153
+ tree.applySubStateUpdate("focusedItem", itemId);
154
+ },
156
155
  primaryAction: ({ tree, item }) => { var _a, _b; return (_b = (_a = tree.getConfig()).onPrimaryAction) === null || _b === void 0 ? void 0 : _b.call(_a, item); },
157
156
  getParent: ({ tree, item }) => item.getItemMeta().parentId
158
157
  ? tree.getItemInstance(item.getItemMeta().parentId)
159
158
  : undefined,
160
- // TODO remove
161
159
  getIndexInParent: ({ item }) => item.getItemMeta().posInSet,
162
160
  getChildren: ({ tree, item }) => tree
163
161
  .retrieveChildrenIds(item.getItemMeta().itemId)
@@ -165,23 +163,6 @@ exports.treeFeature = {
165
163
  getTree: ({ tree }) => tree,
166
164
  getItemAbove: ({ tree, item }) => tree.getItems()[item.getItemMeta().index - 1],
167
165
  getItemBelow: ({ tree, item }) => tree.getItems()[item.getItemMeta().index + 1],
168
- getMemoizedProp: ({ item }, name, create, deps) => {
169
- var _a, _b, _c, _d, _e;
170
- var _f, _g;
171
- const data = item.getDataRef();
172
- const memoizedValue = (_a = data.current.memoizedValues) === null || _a === void 0 ? void 0 : _a[name];
173
- if (memoizedValue &&
174
- (!deps ||
175
- ((_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])))) {
176
- return memoizedValue;
177
- }
178
- (_d = (_f = data.current).memoizedDeps) !== null && _d !== void 0 ? _d : (_f.memoizedDeps = {});
179
- (_e = (_g = data.current).memoizedValues) !== null && _e !== void 0 ? _e : (_g.memoizedValues = {});
180
- const value = create();
181
- data.current.memoizedDeps[name] = deps;
182
- data.current.memoizedValues[name] = value;
183
- return value;
184
- },
185
166
  },
186
167
  hotkeys: {
187
168
  focusNextItem: {
@@ -237,14 +218,16 @@ exports.treeFeature = {
237
218
  focusFirstItem: {
238
219
  hotkey: "Home",
239
220
  handler: (e, tree) => {
240
- tree.focusItem(tree.getItems()[0].getId());
221
+ var _a;
222
+ (_a = tree.getItems()[0]) === null || _a === void 0 ? void 0 : _a.setFocused();
241
223
  tree.updateDomFocus();
242
224
  },
243
225
  },
244
226
  focusLastItem: {
245
227
  hotkey: "End",
246
228
  handler: (e, tree) => {
247
- tree.focusItem(tree.getItems()[tree.getItems().length - 1].getId());
229
+ var _a;
230
+ (_a = tree.getItems()[tree.getItems().length - 1]) === null || _a === void 0 ? void 0 : _a.setFocused();
248
231
  tree.updateDomFocus();
249
232
  },
250
233
  },
@@ -27,10 +27,6 @@ export type TreeFeatureDef<T> = {
27
27
  treeInstance: {
28
28
  /** @internal */
29
29
  getItemsMeta: () => ItemMeta[];
30
- expandItem: (itemId: string) => void;
31
- collapseItem: (itemId: string) => void;
32
- isItemExpanded: (itemId: string) => boolean;
33
- focusItem: (itemId: string) => void;
34
30
  getFocusedItem: () => ItemInstance<any>;
35
31
  focusNextItem: () => void;
36
32
  focusPreviousItem: () => void;
@@ -57,7 +53,6 @@ export type TreeFeatureDef<T> = {
57
53
  getTree: () => TreeInstance<T>;
58
54
  getItemAbove: () => ItemInstance<T> | undefined;
59
55
  getItemBelow: () => ItemInstance<T> | undefined;
60
- getMemoizedProp: <X>(name: string, create: () => X, deps?: any[]) => X;
61
56
  scrollTo: (scrollIntoViewArg?: boolean | ScrollIntoViewOptions) => Promise<void>;
62
57
  };
63
58
  hotkeys: "focusNextItem" | "focusPreviousItem" | "expandOrDown" | "collapseOrUp" | "focusFirstItem" | "focusLastItem";
@@ -10,6 +10,7 @@ export * from "./features/hotkeys-core/types";
10
10
  export * from "./features/search/types";
11
11
  export * from "./features/renaming/types";
12
12
  export * from "./features/expand-all/types";
13
+ export * from "./features/prop-memoization/types";
13
14
  export * from "./features/selection/feature";
14
15
  export * from "./features/hotkeys-core/feature";
15
16
  export * from "./features/async-data-loader/feature";
@@ -18,6 +19,7 @@ export * from "./features/drag-and-drop/feature";
18
19
  export * from "./features/search/feature";
19
20
  export * from "./features/renaming/feature";
20
21
  export * from "./features/expand-all/feature";
22
+ export * from "./features/prop-memoization/feature";
21
23
  export * from "./utilities/create-on-drop-handler";
22
24
  export * from "./utilities/insert-items-at-target";
23
25
  export * from "./utilities/remove-items-from-parents";
package/lib/cjs/index.js CHANGED
@@ -25,6 +25,7 @@ __exportStar(require("./features/hotkeys-core/types"), exports);
25
25
  __exportStar(require("./features/search/types"), exports);
26
26
  __exportStar(require("./features/renaming/types"), exports);
27
27
  __exportStar(require("./features/expand-all/types"), exports);
28
+ __exportStar(require("./features/prop-memoization/types"), exports);
28
29
  __exportStar(require("./features/selection/feature"), exports);
29
30
  __exportStar(require("./features/hotkeys-core/feature"), exports);
30
31
  __exportStar(require("./features/async-data-loader/feature"), exports);
@@ -33,6 +34,7 @@ __exportStar(require("./features/drag-and-drop/feature"), exports);
33
34
  __exportStar(require("./features/search/feature"), exports);
34
35
  __exportStar(require("./features/renaming/feature"), exports);
35
36
  __exportStar(require("./features/expand-all/feature"), exports);
37
+ __exportStar(require("./features/prop-memoization/feature"), exports);
36
38
  __exportStar(require("./utilities/create-on-drop-handler"), exports);
37
39
  __exportStar(require("./utilities/insert-items-at-target"), exports);
38
40
  __exportStar(require("./utilities/remove-items-from-parents"), exports);
@@ -8,6 +8,7 @@ import { SearchFeatureDef } from "./features/search/types";
8
8
  import { SelectionFeatureDef } from "./features/selection/types";
9
9
  import { SyncDataLoaderFeatureDef } from "./features/sync-data-loader/types";
10
10
  import { TreeFeatureDef } from "./features/tree/types";
11
+ import { PropMemoizationFeatureDef } from "./features/prop-memoization/types";
11
12
  export * from ".";
12
13
  /** @interface */
13
14
  export type AsyncDataLoaderFeatureConfig<T> = AsyncDataLoaderFeatureDef<T>["config"];
@@ -55,6 +56,15 @@ export type MainFeatureTreeInstance = MainFeatureDef["treeInstance"];
55
56
  export type MainFeatureItemInstance = MainFeatureDef["itemInstance"];
56
57
  export type MainFeatureHotkeys = MainFeatureDef["hotkeys"];
57
58
  /** @interface */
59
+ export type PropMemoizationConfig = PropMemoizationFeatureDef["config"];
60
+ /** @interface */
61
+ export type PropMemoizationState = PropMemoizationFeatureDef["state"];
62
+ /** @interface */
63
+ export type PropMemoizationTreeInstance = PropMemoizationFeatureDef["treeInstance"];
64
+ /** @interface */
65
+ export type PropMemoizationItemInstance = PropMemoizationFeatureDef["itemInstance"];
66
+ export type PropMemoizationHotkeys = PropMemoizationFeatureDef["hotkeys"];
67
+ /** @interface */
58
68
  export type RenamingFeatureConfig<T> = RenamingFeatureDef<T>["config"];
59
69
  /** @interface */
60
70
  export type RenamingFeatureState<T> = RenamingFeatureDef<T>["state"];
@@ -3,7 +3,7 @@ import { TestTree } from "./test-tree";
3
3
  import { HotkeyName } from "../types/core";
4
4
  export declare class TestTreeDo<T> {
5
5
  protected tree: TestTree<T>;
6
- protected itemInstance(itemId: string): import("../types/core").ItemInstance<any>;
6
+ protected itemInstance(itemId: string): import("../types/core").ItemInstance<T>;
7
7
  protected itemProps(itemId: string): Record<string, any>;
8
8
  constructor(tree: TestTree<T>);
9
9
  selectItem(id: string): void;
@@ -3,7 +3,7 @@ import { TestTree } from "./test-tree";
3
3
  import { DropTarget } from "../features/drag-and-drop/types";
4
4
  export declare class TestTreeExpect<T> {
5
5
  private tree;
6
- protected itemInstance(itemId: string): import("..").ItemInstance<any>;
6
+ protected itemInstance(itemId: string): import("..").ItemInstance<T>;
7
7
  protected itemProps(itemId: string): Record<string, any>;
8
8
  constructor(tree: TestTree<T>);
9
9
  foldersExpanded(...itemIds: string[]): void;
@@ -48,7 +48,7 @@ class TestTreeExpect {
48
48
  (0, vitest_1.expect)(this.tree.instance.getDragLineData()).toEqual({
49
49
  indent,
50
50
  left: indent * 20,
51
- right: 100,
51
+ width: 100 - indent * 20,
52
52
  top: 0,
53
53
  });
54
54
  (0, vitest_1.expect)(this.tree.instance.getDragLineStyle(0, 0)).toEqual({
@@ -37,7 +37,7 @@ export declare class TestTree<T = string> {
37
37
  createTestCaseTree(): Promise<this>;
38
38
  withFeatures(...features: any): TestTree<T>;
39
39
  mockedHandler(handlerName: keyof TreeConfig<T>): import("vitest").Mock<(...args: any[]) => any>;
40
- item(itemId: string): import("../types/core").ItemInstance<any>;
40
+ item(itemId: string): import("../types/core").ItemInstance<T>;
41
41
  reset(): void;
42
42
  debug(): void;
43
43
  setElementBoundingBox(itemId: string, bb?: Partial<DOMRect>): void;
@@ -163,10 +163,18 @@ class TestTree {
163
163
  }
164
164
  setElementBoundingBox(itemId, bb = {
165
165
  left: 0,
166
- right: 100,
166
+ width: 100,
167
167
  top: 0,
168
168
  height: 20,
169
169
  }) {
170
+ this.instance.registerElement({
171
+ getBoundingClientRect: () => ({
172
+ left: 0,
173
+ width: 100,
174
+ top: 0,
175
+ height: 10000,
176
+ }),
177
+ });
170
178
  this.instance.getItemInstance(itemId).registerElement({
171
179
  getBoundingClientRect: () => bb,
172
180
  });
@@ -8,14 +8,14 @@ import { AsyncDataLoaderFeatureDef } from "../features/async-data-loader/types";
8
8
  import { SearchFeatureDef } from "../features/search/types";
9
9
  import { RenamingFeatureDef } from "../features/renaming/types";
10
10
  import { ExpandAllFeatureDef } from "../features/expand-all/types";
11
+ import { PropMemoizationFeatureDef } from "../features/prop-memoization/types";
11
12
  export type Updater<T> = T | ((old: T) => T);
12
13
  export type SetStateFn<T> = (updaterOrValue: Updater<T>) => void;
13
- type FunctionMap = Record<string, (...args: any[]) => any>;
14
14
  export type FeatureDef = {
15
15
  state: any;
16
16
  config: any;
17
- treeInstance: FunctionMap;
18
- itemInstance: FunctionMap;
17
+ treeInstance: any;
18
+ itemInstance: any;
19
19
  hotkeys: string;
20
20
  };
21
21
  export type EmptyFeatureDef = {
@@ -26,8 +26,6 @@ export type EmptyFeatureDef = {
26
26
  hotkeys: never;
27
27
  };
28
28
  type UnionToIntersection<T> = (T extends any ? (x: T) => any : never) extends (x: infer R) => any ? R : never;
29
- export type DefaultFeatures<T> = MainFeatureDef | TreeFeatureDef<T>;
30
- export type FeatureDefs<T> = MainFeatureDef | TreeFeatureDef<T> | SelectionFeatureDef<T> | DragAndDropFeatureDef<T> | HotkeysCoreFeatureDef<T> | SyncDataLoaderFeatureDef<T> | AsyncDataLoaderFeatureDef<T> | SearchFeatureDef<T> | RenamingFeatureDef<T> | ExpandAllFeatureDef;
31
29
  type MergedFeatures<F extends FeatureDef> = {
32
30
  state: UnionToIntersection<F["state"]>;
33
31
  config: UnionToIntersection<F["config"]>;
@@ -35,49 +33,50 @@ type MergedFeatures<F extends FeatureDef> = {
35
33
  itemInstance: UnionToIntersection<F["itemInstance"]>;
36
34
  hotkeys: F["hotkeys"];
37
35
  };
38
- type TreeStateType<T> = MainFeatureDef["state"] & TreeFeatureDef<T>["state"] & SelectionFeatureDef<T>["state"] & DragAndDropFeatureDef<T>["state"] & HotkeysCoreFeatureDef<T>["state"] & SyncDataLoaderFeatureDef<T>["state"] & AsyncDataLoaderFeatureDef<T>["state"] & SearchFeatureDef<T>["state"] & RenamingFeatureDef<T>["state"] & ExpandAllFeatureDef["state"];
36
+ export type RegisteredFeatures<T> = MainFeatureDef<T> | TreeFeatureDef<T> | SelectionFeatureDef<T> | DragAndDropFeatureDef<T> | HotkeysCoreFeatureDef<T> | SyncDataLoaderFeatureDef<T> | AsyncDataLoaderFeatureDef<T> | SearchFeatureDef<T> | RenamingFeatureDef<T> | ExpandAllFeatureDef | PropMemoizationFeatureDef;
37
+ type TreeStateType<T> = MergedFeatures<RegisteredFeatures<T>>["state"];
39
38
  export interface TreeState<T> extends TreeStateType<T> {
40
39
  }
41
- type TreeConfigType<T> = MainFeatureDef["config"] & TreeFeatureDef<T>["config"] & SelectionFeatureDef<T>["config"] & DragAndDropFeatureDef<T>["config"] & HotkeysCoreFeatureDef<T>["config"] & SyncDataLoaderFeatureDef<T>["config"] & AsyncDataLoaderFeatureDef<T>["config"] & SearchFeatureDef<T>["config"] & RenamingFeatureDef<T>["config"] & ExpandAllFeatureDef["config"];
40
+ type TreeConfigType<T> = MergedFeatures<RegisteredFeatures<T>>["config"];
42
41
  export interface TreeConfig<T> extends TreeConfigType<T> {
43
42
  }
44
- type TreeInstanceType<T> = MainFeatureDef["treeInstance"] & TreeFeatureDef<T>["treeInstance"] & SelectionFeatureDef<T>["treeInstance"] & DragAndDropFeatureDef<T>["treeInstance"] & HotkeysCoreFeatureDef<T>["treeInstance"] & SyncDataLoaderFeatureDef<T>["treeInstance"] & AsyncDataLoaderFeatureDef<T>["treeInstance"] & SearchFeatureDef<T>["treeInstance"] & RenamingFeatureDef<T>["treeInstance"] & ExpandAllFeatureDef["treeInstance"];
43
+ type TreeInstanceType<T> = MergedFeatures<RegisteredFeatures<T>>["treeInstance"];
45
44
  export interface TreeInstance<T> extends TreeInstanceType<T> {
46
45
  }
47
- type ItemInstanceType<T> = MainFeatureDef["itemInstance"] & TreeFeatureDef<T>["itemInstance"] & SelectionFeatureDef<T>["itemInstance"] & DragAndDropFeatureDef<T>["itemInstance"] & HotkeysCoreFeatureDef<T>["itemInstance"] & SyncDataLoaderFeatureDef<T>["itemInstance"] & AsyncDataLoaderFeatureDef<T>["itemInstance"] & SearchFeatureDef<T>["itemInstance"] & RenamingFeatureDef<T>["itemInstance"] & ExpandAllFeatureDef["itemInstance"];
46
+ type ItemInstanceType<T> = MergedFeatures<RegisteredFeatures<T>>["itemInstance"];
48
47
  export interface ItemInstance<T> extends ItemInstanceType<T> {
49
48
  }
50
- export type HotkeyName<F extends FeatureDef = FeatureDefs<any>> = MergedFeatures<F>["hotkeys"];
51
- export type HotkeysConfig<T, F extends FeatureDef = FeatureDefs<T>> = Record<HotkeyName<F>, HotkeyConfig<T>>;
52
- export type CustomHotkeysConfig<T, F extends FeatureDef = FeatureDefs<T>> = Partial<Record<HotkeyName<F> | `custom${string}`, Partial<HotkeyConfig<T>>>>;
49
+ export type HotkeyName = MergedFeatures<RegisteredFeatures<any>>["hotkeys"];
50
+ export type HotkeysConfig<T> = Record<HotkeyName, HotkeyConfig<T>>;
51
+ export type CustomHotkeysConfig<T> = Partial<Record<HotkeyName | `custom${string}`, Partial<HotkeyConfig<T>>>>;
53
52
  type MayReturnNull<T extends (...x: any[]) => any> = (...args: Parameters<T>) => ReturnType<T> | null;
54
- export type ItemInstanceOpts<ItemInstance extends FunctionMap = FunctionMap, TreeInstance extends FunctionMap = FunctionMap, Key extends keyof ItemInstance = any> = {
55
- item: ItemInstance;
56
- tree: TreeInstance;
53
+ export type ItemInstanceOpts<Key extends keyof ItemInstance<any>> = {
54
+ item: ItemInstance<any>;
55
+ tree: TreeInstance<any>;
57
56
  itemId: string;
58
- prev?: MayReturnNull<ItemInstance[Key]>;
57
+ prev?: MayReturnNull<ItemInstance<any>[Key]>;
59
58
  };
60
- export type TreeInstanceOpts<TreeInstance extends FunctionMap = FunctionMap, Key extends keyof TreeInstance = any> = {
61
- tree: TreeInstance;
62
- prev?: MayReturnNull<TreeInstance[Key]>;
59
+ export type TreeInstanceOpts<Key extends keyof TreeInstance<any>> = {
60
+ tree: TreeInstance<any>;
61
+ prev?: MayReturnNull<TreeInstance<any>[Key]>;
63
62
  };
64
- export type FeatureImplementation<T = any, SelfFeatureDef extends FeatureDef = any, DepFeaturesDef extends FeatureDef = any> = {
63
+ export type FeatureImplementation<T = any> = {
65
64
  key?: string;
66
65
  deps?: string[];
67
66
  overwrites?: string[];
68
- stateHandlerNames?: Partial<Record<keyof MergedFeatures<DepFeaturesDef>["state"], keyof MergedFeatures<DepFeaturesDef>["config"]>>;
69
- getInitialState?: (initialState: Partial<MergedFeatures<DepFeaturesDef>["state"]>, tree: MergedFeatures<DepFeaturesDef>["treeInstance"]) => Partial<SelfFeatureDef["state"] & MergedFeatures<DepFeaturesDef>["state"]>;
70
- getDefaultConfig?: (defaultConfig: Partial<MergedFeatures<DepFeaturesDef>["config"]>, tree: MergedFeatures<DepFeaturesDef>["treeInstance"]) => Partial<SelfFeatureDef["config"] & MergedFeatures<DepFeaturesDef>["config"]>;
67
+ stateHandlerNames?: Partial<Record<keyof TreeState<T>, keyof TreeConfig<T>>>;
68
+ getInitialState?: (initialState: Partial<TreeState<T>>, tree: TreeInstance<T>) => Partial<TreeState<T>>;
69
+ getDefaultConfig?: (defaultConfig: Partial<TreeConfig<T>>, tree: TreeInstance<T>) => Partial<TreeConfig<T>>;
71
70
  treeInstance?: {
72
- [key in keyof (SelfFeatureDef["treeInstance"] & MergedFeatures<DepFeaturesDef>["treeInstance"])]?: (opts: TreeInstanceOpts<SelfFeatureDef["treeInstance"] & MergedFeatures<DepFeaturesDef>["treeInstance"], key>, ...args: Parameters<(SelfFeatureDef["treeInstance"] & MergedFeatures<DepFeaturesDef>["treeInstance"])[key]>) => void;
71
+ [key in keyof TreeInstance<T>]?: (opts: TreeInstanceOpts<key>, ...args: Parameters<TreeInstance<T>[key]>) => void;
73
72
  };
74
73
  itemInstance?: {
75
- [key in keyof (SelfFeatureDef["itemInstance"] & MergedFeatures<DepFeaturesDef>["itemInstance"])]?: (opts: ItemInstanceOpts<SelfFeatureDef["itemInstance"] & MergedFeatures<DepFeaturesDef>["itemInstance"], SelfFeatureDef["treeInstance"] & MergedFeatures<DepFeaturesDef>["treeInstance"], key>, ...args: Parameters<(SelfFeatureDef["itemInstance"] & MergedFeatures<DepFeaturesDef>["itemInstance"])[key]>) => void;
74
+ [key in keyof ItemInstance<T>]?: (opts: ItemInstanceOpts<key>, ...args: Parameters<ItemInstance<T>[key]>) => void;
76
75
  };
77
- onTreeMount?: (instance: MergedFeatures<DepFeaturesDef>["treeInstance"], treeElement: HTMLElement) => void;
78
- onTreeUnmount?: (instance: MergedFeatures<DepFeaturesDef>["treeInstance"], treeElement: HTMLElement) => void;
79
- onItemMount?: (instance: MergedFeatures<DepFeaturesDef>["itemInstance"], itemElement: HTMLElement, tree: MergedFeatures<DepFeaturesDef>["treeInstance"]) => void;
80
- onItemUnmount?: (instance: MergedFeatures<DepFeaturesDef>["itemInstance"], itemElement: HTMLElement, tree: MergedFeatures<DepFeaturesDef>["treeInstance"]) => void;
81
- hotkeys?: HotkeysConfig<T, SelfFeatureDef>;
76
+ onTreeMount?: (instance: TreeInstance<T>, treeElement: HTMLElement) => void;
77
+ onTreeUnmount?: (instance: TreeInstance<T>, treeElement: HTMLElement) => void;
78
+ onItemMount?: (instance: ItemInstance<T>, itemElement: HTMLElement, tree: TreeInstance<T>) => void;
79
+ onItemUnmount?: (instance: ItemInstance<T>, itemElement: HTMLElement, tree: TreeInstance<T>) => void;
80
+ hotkeys?: Partial<HotkeysConfig<T>>;
82
81
  };
83
82
  export {};
@@ -4,8 +4,7 @@ export const buildStaticInstance = (features, instanceType, buildOpts) => {
4
4
  const finalize = () => {
5
5
  const opts = buildOpts(instance);
6
6
  featureLoop: for (let i = 0; i < features.length; i++) {
7
- // Loop goes in forward order, because later features overwrite previous ones
8
- // TODO loop order correct? I think so...
7
+ // Loop goes in forward order, each features overwrite previous ones and wraps those in a prev() fn
9
8
  const definition = features[i][instanceType];
10
9
  if (!definition)
11
10
  continue featureLoop;
@@ -11,14 +11,17 @@ const verifyFeatures = (features) => {
11
11
  }
12
12
  }
13
13
  };
14
- const compareFeatures = (feature1, feature2) => {
15
- var _a;
14
+ const compareFeatures = (originalOrder) => (feature1, feature2) => {
15
+ var _a, _b;
16
16
  if (feature2.key && ((_a = feature1.overwrites) === null || _a === void 0 ? void 0 : _a.includes(feature2.key))) {
17
17
  return 1;
18
18
  }
19
- return -1;
19
+ if (feature1.key && ((_b = feature2.overwrites) === null || _b === void 0 ? void 0 : _b.includes(feature1.key))) {
20
+ return -1;
21
+ }
22
+ return originalOrder.indexOf(feature1) - originalOrder.indexOf(feature2);
20
23
  };
21
- const sortFeatures = (features = []) => features.sort(compareFeatures);
24
+ const sortFeatures = (features = []) => features.sort(compareFeatures(features));
22
25
  export const createTree = (initialConfig) => {
23
26
  var _a, _b, _c, _d;
24
27
  const buildInstance = (_a = initialConfig.instanceBuilder) !== null && _a !== void 0 ? _a : buildStaticInstance;
@@ -1,5 +1,2 @@
1
1
  import { FeatureImplementation } from "../../types/core";
2
- import { AsyncDataLoaderFeatureDef } from "./types";
3
- import { MainFeatureDef } from "../main/types";
4
- import { TreeFeatureDef } from "../tree/types";
5
- export declare const asyncDataLoaderFeature: FeatureImplementation<any, AsyncDataLoaderFeatureDef<any>, MainFeatureDef | TreeFeatureDef<any> | AsyncDataLoaderFeatureDef<any>>;
2
+ export declare const asyncDataLoaderFeature: FeatureImplementation;
@@ -73,23 +73,21 @@ export const asyncDataLoaderFeature = {
73
73
  }
74
74
  return [];
75
75
  },
76
- invalidateItemData: ({ tree }, itemId) => {
76
+ },
77
+ itemInstance: {
78
+ isLoading: ({ tree, item }) => tree.getState().loadingItems.includes(item.getItemMeta().itemId),
79
+ invalidateItemData: ({ tree, itemId }) => {
77
80
  var _a;
78
81
  const dataRef = tree.getDataRef();
79
82
  (_a = dataRef.current.itemData) === null || _a === void 0 ? true : delete _a[itemId];
80
83
  tree.retrieveItemData(itemId);
81
84
  },
82
- invalidateChildrenIds: ({ tree }, itemId) => {
85
+ invalidateChildrenIds: ({ tree, itemId }) => {
83
86
  var _a;
84
87
  const dataRef = tree.getDataRef();
85
88
  (_a = dataRef.current.childrenIds) === null || _a === void 0 ? true : delete _a[itemId];
86
89
  tree.retrieveChildrenIds(itemId);
87
90
  },
88
- },
89
- itemInstance: {
90
- isLoading: ({ tree, item }) => tree.getState().loadingItems.includes(item.getItemMeta().itemId),
91
- invalidateItemData: ({ tree, item }) => tree.invalidateItemData(item.getItemMeta().itemId),
92
- invalidateChildrenIds: ({ tree, item }) => tree.invalidateChildrenIds(item.getItemMeta().itemId),
93
91
  updateCachedChildrenIds: ({ tree, itemId }, childrenIds) => {
94
92
  const dataRef = tree.getDataRef();
95
93
  dataRef.current.childrenIds[itemId] = childrenIds;
@@ -27,12 +27,9 @@ export type AsyncDataLoaderFeatureDef<T> = {
27
27
  onLoadedChildren?: (itemId: string, childrenIds: string[]) => void;
28
28
  asyncDataLoader?: AsyncTreeDataLoader<T>;
29
29
  };
30
- treeInstance: SyncDataLoaderFeatureDef<T>["treeInstance"] & {
31
- /** Invalidate fetched data for item, and triggers a refetch and subsequent rerender if the item is visible */
32
- invalidateItemData: (itemId: string) => void;
33
- invalidateChildrenIds: (itemId: string) => void;
34
- };
30
+ treeInstance: SyncDataLoaderFeatureDef<T>["treeInstance"];
35
31
  itemInstance: SyncDataLoaderFeatureDef<T>["itemInstance"] & {
32
+ /** Invalidate fetched data for item, and triggers a refetch and subsequent rerender if the item is visible */
36
33
  invalidateItemData: () => void;
37
34
  invalidateChildrenIds: () => void;
38
35
  updateCachedChildrenIds: (childrenIds: string[]) => void;
@@ -1,3 +1,2 @@
1
- import { FeatureDefs, FeatureImplementation } from "../../types/core";
2
- import { DragAndDropFeatureDef } from "./types";
3
- export declare const dragAndDropFeature: FeatureImplementation<any, DragAndDropFeatureDef<any>, FeatureDefs<any>>;
1
+ import { FeatureImplementation } from "../../types/core";
2
+ export declare const dragAndDropFeature: FeatureImplementation;