@headless-tree/core 0.0.11 → 0.0.13

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 +12 -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
@@ -17,7 +17,6 @@ export const treeFeature = {
17
17
  focusedItem: "setFocusedItem",
18
18
  },
19
19
  treeInstance: {
20
- isItemExpanded: ({ tree }, itemId) => tree.getState().expandedItems.includes(itemId),
21
20
  getItemsMeta: ({ tree }) => {
22
21
  const { rootItemId } = tree.getConfig();
23
22
  const { expandedItems } = tree.getState();
@@ -49,44 +48,22 @@ export const treeFeature = {
49
48
  }
50
49
  return flatItems;
51
50
  },
52
- expandItem: ({ tree }, itemId) => {
53
- var _a;
54
- if (!tree.getItemInstance(itemId).isFolder()) {
55
- return;
56
- }
57
- if ((_a = tree.getState().loadingItems) === null || _a === void 0 ? void 0 : _a.includes(itemId)) {
58
- return;
59
- }
60
- tree.applySubStateUpdate("expandedItems", (expandedItems) => [
61
- ...expandedItems,
62
- itemId,
63
- ]);
64
- tree.rebuildTree();
65
- },
66
- collapseItem: ({ tree }, itemId) => {
67
- if (!tree.getItemInstance(itemId).isFolder()) {
68
- return;
69
- }
70
- tree.applySubStateUpdate("expandedItems", (expandedItems) => expandedItems.filter((id) => id !== itemId));
71
- tree.rebuildTree();
72
- },
73
51
  // TODO memo
74
52
  getFocusedItem: ({ tree }) => {
75
53
  var _a, _b;
76
54
  return ((_b = tree.getItemInstance((_a = tree.getState().focusedItem) !== null && _a !== void 0 ? _a : "")) !== null && _b !== void 0 ? _b : tree.getItems()[0]);
77
55
  },
78
- focusItem: ({ tree }, itemId) => {
79
- tree.applySubStateUpdate("focusedItem", itemId);
80
- },
81
56
  focusNextItem: ({ tree }) => {
57
+ var _a;
82
58
  const { index } = tree.getFocusedItem().getItemMeta();
83
59
  const nextIndex = Math.min(index + 1, tree.getItems().length - 1);
84
- tree.focusItem(tree.getItems()[nextIndex].getId());
60
+ (_a = tree.getItems()[nextIndex]) === null || _a === void 0 ? void 0 : _a.setFocused();
85
61
  },
86
62
  focusPreviousItem: ({ tree }) => {
63
+ var _a;
87
64
  const { index } = tree.getFocusedItem().getItemMeta();
88
65
  const nextIndex = Math.max(index - 1, 0);
89
- tree.focusItem(tree.getItems()[nextIndex].getId());
66
+ (_a = tree.getItems()[nextIndex]) === null || _a === void 0 ? void 0 : _a.setFocused();
90
67
  },
91
68
  updateDomFocus: ({ tree }) => {
92
69
  // Required because if the state is managed outside in react, the state only updated during next render
@@ -101,7 +78,8 @@ export const treeFeature = {
101
78
  focusedElement.focus();
102
79
  }));
103
80
  },
104
- getContainerProps: ({ prev }) => (Object.assign(Object.assign({}, prev === null || prev === void 0 ? void 0 : prev()), { role: "tree", ariaLabel: "", ariaActivedescendant: "" })),
81
+ // TODO add label parameter
82
+ getContainerProps: ({ prev, tree }) => (Object.assign(Object.assign({}, prev === null || prev === void 0 ? void 0 : prev()), { role: "tree", "aria-label": "", ref: tree.registerElement })),
105
83
  // relevant for hotkeys of this feature
106
84
  isSearchOpen: () => false,
107
85
  },
@@ -112,10 +90,10 @@ export const treeFeature = {
112
90
  yield poll(() => item.getElement() !== null, 20);
113
91
  (_d = item.getElement()) === null || _d === void 0 ? void 0 : _d.scrollIntoView(scrollIntoViewArg);
114
92
  }),
115
- getId: ({ item }) => item.getItemMeta().itemId,
93
+ getId: ({ itemId }) => itemId,
116
94
  getProps: ({ item, prev }) => {
117
95
  const itemMeta = item.getItemMeta();
118
- 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) => {
96
+ 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) => {
119
97
  item.setFocused();
120
98
  item.primaryAction();
121
99
  if (e.ctrlKey || e.shiftKey || e.metaKey) {
@@ -130,18 +108,37 @@ export const treeFeature = {
130
108
  else {
131
109
  item.expand();
132
110
  }
133
- }) });
111
+ } });
112
+ },
113
+ expand: ({ tree, item, itemId }) => {
114
+ var _a;
115
+ if (!item.isFolder()) {
116
+ return;
117
+ }
118
+ if ((_a = tree.getState().loadingItems) === null || _a === void 0 ? void 0 : _a.includes(itemId)) {
119
+ return;
120
+ }
121
+ tree.applySubStateUpdate("expandedItems", (expandedItems) => [
122
+ ...expandedItems,
123
+ itemId,
124
+ ]);
125
+ tree.rebuildTree();
134
126
  },
135
- expand: ({ tree, item }) => tree.expandItem(item.getItemMeta().itemId),
136
- collapse: ({ tree, item }) => tree.collapseItem(item.getItemMeta().itemId),
137
- getItemData: ({ tree, item }) => tree.retrieveItemData(item.getItemMeta().itemId),
127
+ collapse: ({ tree, item, itemId }) => {
128
+ if (!item.isFolder()) {
129
+ return;
130
+ }
131
+ tree.applySubStateUpdate("expandedItems", (expandedItems) => expandedItems.filter((id) => id !== itemId));
132
+ tree.rebuildTree();
133
+ },
134
+ getItemData: ({ tree, itemId }) => tree.retrieveItemData(itemId),
138
135
  equals: ({ item }, other) => item.getId() === (other === null || other === void 0 ? void 0 : other.getId()),
139
- isExpanded: ({ tree, item }) => tree.getState().expandedItems.includes(item.getItemMeta().itemId),
136
+ isExpanded: ({ tree, itemId }) => tree.getState().expandedItems.includes(itemId),
140
137
  isDescendentOf: ({ item }, parentId) => {
141
138
  const parent = item.getParent();
142
139
  return Boolean((parent === null || parent === void 0 ? void 0 : parent.getId()) === parentId || (parent === null || parent === void 0 ? void 0 : parent.isDescendentOf(parentId)));
143
140
  },
144
- isFocused: ({ tree, item }) => tree.getState().focusedItem === item.getItemMeta().itemId ||
141
+ isFocused: ({ tree, item, itemId }) => tree.getState().focusedItem === itemId ||
145
142
  (tree.getState().focusedItem === null && item.getItemMeta().index === 0),
146
143
  isFolder: ({ tree, item }) => item.getItemMeta().level === -1 ||
147
144
  tree.getConfig().isItemFolder(item),
@@ -149,12 +146,13 @@ export const treeFeature = {
149
146
  const config = tree.getConfig();
150
147
  return config.getItemName(item);
151
148
  },
152
- setFocused: ({ tree, item }) => tree.focusItem(item.getItemMeta().itemId),
149
+ setFocused: ({ tree, itemId }) => {
150
+ tree.applySubStateUpdate("focusedItem", itemId);
151
+ },
153
152
  primaryAction: ({ tree, item }) => { var _a, _b; return (_b = (_a = tree.getConfig()).onPrimaryAction) === null || _b === void 0 ? void 0 : _b.call(_a, item); },
154
153
  getParent: ({ tree, item }) => item.getItemMeta().parentId
155
154
  ? tree.getItemInstance(item.getItemMeta().parentId)
156
155
  : undefined,
157
- // TODO remove
158
156
  getIndexInParent: ({ item }) => item.getItemMeta().posInSet,
159
157
  getChildren: ({ tree, item }) => tree
160
158
  .retrieveChildrenIds(item.getItemMeta().itemId)
@@ -162,23 +160,6 @@ export const treeFeature = {
162
160
  getTree: ({ tree }) => tree,
163
161
  getItemAbove: ({ tree, item }) => tree.getItems()[item.getItemMeta().index - 1],
164
162
  getItemBelow: ({ tree, item }) => tree.getItems()[item.getItemMeta().index + 1],
165
- getMemoizedProp: ({ item }, name, create, deps) => {
166
- var _a, _b, _c, _d, _e;
167
- var _f, _g;
168
- const data = item.getDataRef();
169
- const memoizedValue = (_a = data.current.memoizedValues) === null || _a === void 0 ? void 0 : _a[name];
170
- if (memoizedValue &&
171
- (!deps ||
172
- ((_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])))) {
173
- return memoizedValue;
174
- }
175
- (_d = (_f = data.current).memoizedDeps) !== null && _d !== void 0 ? _d : (_f.memoizedDeps = {});
176
- (_e = (_g = data.current).memoizedValues) !== null && _e !== void 0 ? _e : (_g.memoizedValues = {});
177
- const value = create();
178
- data.current.memoizedDeps[name] = deps;
179
- data.current.memoizedValues[name] = value;
180
- return value;
181
- },
182
163
  },
183
164
  hotkeys: {
184
165
  focusNextItem: {
@@ -234,14 +215,16 @@ export const treeFeature = {
234
215
  focusFirstItem: {
235
216
  hotkey: "Home",
236
217
  handler: (e, tree) => {
237
- tree.focusItem(tree.getItems()[0].getId());
218
+ var _a;
219
+ (_a = tree.getItems()[0]) === null || _a === void 0 ? void 0 : _a.setFocused();
238
220
  tree.updateDomFocus();
239
221
  },
240
222
  },
241
223
  focusLastItem: {
242
224
  hotkey: "End",
243
225
  handler: (e, tree) => {
244
- tree.focusItem(tree.getItems()[tree.getItems().length - 1].getId());
226
+ var _a;
227
+ (_a = tree.getItems()[tree.getItems().length - 1]) === null || _a === void 0 ? void 0 : _a.setFocused();
245
228
  tree.updateDomFocus();
246
229
  },
247
230
  },
@@ -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/esm/index.js CHANGED
@@ -9,6 +9,7 @@ export * from "./features/hotkeys-core/types";
9
9
  export * from "./features/search/types";
10
10
  export * from "./features/renaming/types";
11
11
  export * from "./features/expand-all/types";
12
+ export * from "./features/prop-memoization/types";
12
13
  export * from "./features/selection/feature";
13
14
  export * from "./features/hotkeys-core/feature";
14
15
  export * from "./features/async-data-loader/feature";
@@ -17,6 +18,7 @@ export * from "./features/drag-and-drop/feature";
17
18
  export * from "./features/search/feature";
18
19
  export * from "./features/renaming/feature";
19
20
  export * from "./features/expand-all/feature";
21
+ export * from "./features/prop-memoization/feature";
20
22
  export * from "./utilities/create-on-drop-handler";
21
23
  export * from "./utilities/insert-items-at-target";
22
24
  export * from "./utilities/remove-items-from-parents";
@@ -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;
@@ -45,7 +45,7 @@ export class TestTreeExpect {
45
45
  expect(this.tree.instance.getDragLineData()).toEqual({
46
46
  indent,
47
47
  left: indent * 20,
48
- right: 100,
48
+ width: 100 - indent * 20,
49
49
  top: 0,
50
50
  });
51
51
  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;
@@ -160,10 +160,18 @@ export class TestTree {
160
160
  }
161
161
  setElementBoundingBox(itemId, bb = {
162
162
  left: 0,
163
- right: 100,
163
+ width: 100,
164
164
  top: 0,
165
165
  height: 20,
166
166
  }) {
167
+ this.instance.registerElement({
168
+ getBoundingClientRect: () => ({
169
+ left: 0,
170
+ width: 100,
171
+ top: 0,
172
+ height: 10000,
173
+ }),
174
+ });
167
175
  this.instance.getItemInstance(itemId).registerElement({
168
176
  getBoundingClientRect: () => bb,
169
177
  });
@@ -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 {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@headless-tree/core",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "type": "module",
5
5
  "main": "lib/cjs/index.js",
6
6
  "module": "lib/esm/index.js",
@@ -12,7 +12,7 @@ const findPrevInstanceMethod = (
12
12
  ) => {
13
13
  for (let i = featureSearchIndex; i >= 0; i--) {
14
14
  const feature = features[i];
15
- const itemInstanceMethod = feature[instanceType]?.[methodKey];
15
+ const itemInstanceMethod = (feature[instanceType] as any)?.[methodKey];
16
16
  if (itemInstanceMethod) {
17
17
  return i;
18
18
  }
@@ -34,13 +34,15 @@ const invokeInstanceMethod = (
34
34
  methodKey,
35
35
  featureIndex - 1,
36
36
  );
37
- const itemInstanceMethod = features[featureIndex][instanceType]?.[methodKey]!;
37
+ const itemInstanceMethod = (features[featureIndex][instanceType] as any)?.[
38
+ methodKey
39
+ ]!;
38
40
  return itemInstanceMethod(
39
41
  {
40
42
  ...opts,
41
43
  prev:
42
44
  prevIndex !== null
43
- ? (...newArgs) =>
45
+ ? (...newArgs: any[]) =>
44
46
  invokeInstanceMethod(
45
47
  features,
46
48
  instanceType,
@@ -11,8 +11,7 @@ export const buildStaticInstance: InstanceBuilder = (
11
11
  const finalize = () => {
12
12
  const opts = buildOpts(instance);
13
13
  featureLoop: for (let i = 0; i < features.length; i++) {
14
- // Loop goes in forward order, because later features overwrite previous ones
15
- // TODO loop order correct? I think so...
14
+ // Loop goes in forward order, each features overwrite previous ones and wraps those in a prev() fn
16
15
  const definition = features[i][instanceType];
17
16
  if (!definition) continue featureLoop;
18
17
  methodLoop: for (const [key, method] of Object.entries(definition)) {