@headless-tree/core 0.0.0-20250224204705 → 0.0.0-20250322153940

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 (94) hide show
  1. package/CHANGELOG.md +13 -1
  2. package/lib/cjs/core/create-tree.js +13 -4
  3. package/lib/cjs/features/async-data-loader/feature.js +73 -48
  4. package/lib/cjs/features/async-data-loader/types.d.ts +17 -14
  5. package/lib/cjs/features/drag-and-drop/feature.js +89 -84
  6. package/lib/cjs/features/drag-and-drop/types.d.ts +8 -20
  7. package/lib/cjs/features/drag-and-drop/utils.js +8 -21
  8. package/lib/cjs/features/expand-all/feature.js +26 -3
  9. package/lib/cjs/features/expand-all/types.d.ts +3 -1
  10. package/lib/cjs/features/hotkeys-core/feature.js +6 -3
  11. package/lib/cjs/features/hotkeys-core/types.d.ts +4 -5
  12. package/lib/cjs/features/prop-memoization/feature.js +2 -2
  13. package/lib/cjs/features/prop-memoization/types.d.ts +2 -2
  14. package/lib/cjs/features/renaming/feature.js +1 -1
  15. package/lib/cjs/features/search/feature.js +2 -0
  16. package/lib/cjs/features/search/types.d.ts +2 -2
  17. package/lib/cjs/features/selection/feature.js +4 -4
  18. package/lib/cjs/features/selection/types.d.ts +1 -1
  19. package/lib/cjs/features/sync-data-loader/feature.js +31 -5
  20. package/lib/cjs/features/sync-data-loader/types.d.ts +5 -5
  21. package/lib/cjs/features/tree/feature.js +4 -7
  22. package/lib/cjs/features/tree/types.d.ts +7 -5
  23. package/lib/cjs/test-utils/test-tree-do.d.ts +2 -2
  24. package/lib/cjs/test-utils/test-tree-do.js +19 -6
  25. package/lib/cjs/test-utils/test-tree.d.ts +2 -1
  26. package/lib/cjs/test-utils/test-tree.js +24 -21
  27. package/lib/cjs/utilities/create-on-drop-handler.d.ts +1 -1
  28. package/lib/cjs/utilities/create-on-drop-handler.js +13 -4
  29. package/lib/cjs/utilities/insert-items-at-target.d.ts +1 -1
  30. package/lib/cjs/utilities/insert-items-at-target.js +21 -12
  31. package/lib/cjs/utilities/remove-items-from-parents.d.ts +1 -1
  32. package/lib/cjs/utilities/remove-items-from-parents.js +12 -3
  33. package/lib/esm/core/create-tree.js +13 -4
  34. package/lib/esm/features/async-data-loader/feature.js +73 -48
  35. package/lib/esm/features/async-data-loader/types.d.ts +17 -14
  36. package/lib/esm/features/drag-and-drop/feature.js +89 -84
  37. package/lib/esm/features/drag-and-drop/types.d.ts +8 -20
  38. package/lib/esm/features/drag-and-drop/utils.js +8 -21
  39. package/lib/esm/features/expand-all/feature.js +26 -3
  40. package/lib/esm/features/expand-all/types.d.ts +3 -1
  41. package/lib/esm/features/hotkeys-core/feature.js +6 -3
  42. package/lib/esm/features/hotkeys-core/types.d.ts +4 -5
  43. package/lib/esm/features/prop-memoization/feature.js +2 -2
  44. package/lib/esm/features/prop-memoization/types.d.ts +2 -2
  45. package/lib/esm/features/renaming/feature.js +1 -1
  46. package/lib/esm/features/search/feature.js +2 -0
  47. package/lib/esm/features/search/types.d.ts +2 -2
  48. package/lib/esm/features/selection/feature.js +4 -4
  49. package/lib/esm/features/selection/types.d.ts +1 -1
  50. package/lib/esm/features/sync-data-loader/feature.js +31 -5
  51. package/lib/esm/features/sync-data-loader/types.d.ts +5 -5
  52. package/lib/esm/features/tree/feature.js +4 -7
  53. package/lib/esm/features/tree/types.d.ts +7 -5
  54. package/lib/esm/test-utils/test-tree-do.d.ts +2 -2
  55. package/lib/esm/test-utils/test-tree-do.js +19 -6
  56. package/lib/esm/test-utils/test-tree.d.ts +2 -1
  57. package/lib/esm/test-utils/test-tree.js +24 -21
  58. package/lib/esm/utilities/create-on-drop-handler.d.ts +1 -1
  59. package/lib/esm/utilities/create-on-drop-handler.js +13 -4
  60. package/lib/esm/utilities/insert-items-at-target.d.ts +1 -1
  61. package/lib/esm/utilities/insert-items-at-target.js +21 -12
  62. package/lib/esm/utilities/remove-items-from-parents.d.ts +1 -1
  63. package/lib/esm/utilities/remove-items-from-parents.js +12 -3
  64. package/package.json +2 -2
  65. package/src/core/core.spec.ts +31 -0
  66. package/src/core/create-tree.ts +15 -5
  67. package/src/features/async-data-loader/async-data-loader.spec.ts +10 -6
  68. package/src/features/async-data-loader/feature.ts +76 -48
  69. package/src/features/async-data-loader/types.ts +18 -11
  70. package/src/features/drag-and-drop/drag-and-drop.spec.ts +69 -83
  71. package/src/features/drag-and-drop/feature.ts +9 -10
  72. package/src/features/drag-and-drop/types.ts +15 -27
  73. package/src/features/drag-and-drop/utils.ts +7 -20
  74. package/src/features/expand-all/feature.ts +29 -5
  75. package/src/features/expand-all/types.ts +3 -1
  76. package/src/features/hotkeys-core/feature.ts +3 -0
  77. package/src/features/hotkeys-core/types.ts +4 -13
  78. package/src/features/prop-memoization/feature.ts +2 -2
  79. package/src/features/prop-memoization/prop-memoization.spec.ts +2 -2
  80. package/src/features/prop-memoization/types.ts +2 -2
  81. package/src/features/renaming/feature.ts +8 -2
  82. package/src/features/search/feature.ts +2 -0
  83. package/src/features/search/types.ts +2 -2
  84. package/src/features/selection/feature.ts +4 -4
  85. package/src/features/selection/types.ts +1 -1
  86. package/src/features/sync-data-loader/feature.ts +26 -7
  87. package/src/features/sync-data-loader/types.ts +5 -5
  88. package/src/features/tree/feature.ts +8 -11
  89. package/src/features/tree/types.ts +7 -5
  90. package/src/test-utils/test-tree-do.ts +3 -3
  91. package/src/test-utils/test-tree.ts +26 -22
  92. package/src/utilities/create-on-drop-handler.ts +3 -3
  93. package/src/utilities/insert-items-at-target.ts +16 -12
  94. package/src/utilities/remove-items-from-parents.ts +6 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@headless-tree/core",
3
- "version": "0.0.0-20250224204705",
3
+ "version": "0.0.0-20250322153940",
4
4
  "type": "module",
5
5
  "main": "lib/cjs/index.js",
6
6
  "module": "lib/esm/index.js",
@@ -21,7 +21,7 @@
21
21
  "license": "MIT",
22
22
  "devDependencies": {
23
23
  "jsdom": "^26.0.0",
24
- "typescript": "^5.7.3",
24
+ "typescript": "^5.7.2",
25
25
  "vitest": "^3.0.3"
26
26
  }
27
27
  }
@@ -1,5 +1,6 @@
1
1
  import { describe, expect, it, vi } from "vitest";
2
2
  import { TestTree } from "../test-utils/test-tree";
3
+ import { buildStaticInstance } from "./build-static-instance";
3
4
 
4
5
  declare module "../types/core" {
5
6
  export interface TreeInstance<T> {
@@ -17,6 +18,36 @@ const factory = TestTree.default({});
17
18
 
18
19
  describe("core-feature/prop-memoization", () => {
19
20
  factory.forSuits((tree) => {
21
+ describe("rebuilds item instance", () => {
22
+ it("rebuilds when explicitly invoked", async () => {
23
+ const instanceBuilder = vi.fn().mockImplementation(buildStaticInstance);
24
+ const testTree = await tree
25
+ .with({ instanceBuilder })
26
+ .createTestCaseTree();
27
+ expect(instanceBuilder).toBeCalled();
28
+ instanceBuilder.mockClear();
29
+ testTree.instance.rebuildTree();
30
+ // if tree structure doesnt mutate, only root tree item is rebuilt actually
31
+ expect(instanceBuilder).toBeCalled();
32
+ });
33
+
34
+ it("rebuilds when config changes with new expanded items", async () => {
35
+ const instanceBuilder = vi.fn().mockImplementation(buildStaticInstance);
36
+ const testTree = await tree
37
+ .with({ instanceBuilder })
38
+ .createTestCaseTree();
39
+ expect(instanceBuilder).toBeCalled();
40
+ instanceBuilder.mockClear();
41
+ testTree.instance.setConfig((oldCfg) => ({
42
+ ...oldCfg,
43
+ state: {
44
+ expandedItems: ["x4"],
45
+ },
46
+ }));
47
+ expect(instanceBuilder).toBeCalled();
48
+ });
49
+ });
50
+
20
51
  describe("calls prev in correct order", () => {
21
52
  it("tree instance with overwrite marks, order 1", async () => {
22
53
  const testTree = await tree
@@ -137,6 +137,7 @@ export const createTree = <T>(
137
137
  // Not necessary, since I think the subupdate below keeps the state fresh anyways?
138
138
  // state = typeof updater === "function" ? updater(state) : updater;
139
139
  config.setState?.(state); // TODO this cant be right... This doesnt allow external state updates
140
+ // TODO this is never used, remove
140
141
  },
141
142
  applySubStateUpdate: <K extends keyof TreeState<any>>(
142
143
  {},
@@ -157,10 +158,20 @@ export const createTree = <T>(
157
158
  },
158
159
  getConfig: () => config,
159
160
  setConfig: (_, updater) => {
160
- config = typeof updater === "function" ? updater(config) : updater;
161
-
162
- if (config.state) {
163
- state = { ...state, ...config.state };
161
+ const newConfig =
162
+ typeof updater === "function" ? updater(config) : updater;
163
+ const hasChangedExpandedItems =
164
+ newConfig.state?.expandedItems &&
165
+ newConfig.state?.expandedItems !== state.expandedItems;
166
+ config = newConfig;
167
+
168
+ if (newConfig.state) {
169
+ state = { ...state, ...newConfig.state };
170
+ }
171
+ if (hasChangedExpandedItems) {
172
+ // if expanded items where changed from the outside
173
+ rebuildItemMeta();
174
+ config.setState?.(state);
164
175
  }
165
176
  },
166
177
  getItemInstance: ({}, itemId) => itemInstancesMap[itemId],
@@ -186,7 +197,6 @@ export const createTree = <T>(
186
197
  getHotkeyPresets: () => hotkeyPresets,
187
198
  },
188
199
  itemInstance: {
189
- // TODO just change to a getRef method that memoizes, maybe as part of getProps
190
200
  registerElement: ({ itemId, item }, element) => {
191
201
  if (itemElementsMap[itemId] === element) {
192
202
  return;
@@ -41,11 +41,17 @@ describe("core-feature/selections", () => {
41
41
 
42
42
  describe("calls handlers", () => {
43
43
  it("updates setLoadingItems", async () => {
44
- const setLoadingItems = tree.mockedHandler("setLoadingItems");
44
+ const setLoadingItemChildrens = tree.mockedHandler(
45
+ "setLoadingItemChildrens",
46
+ );
47
+ const setLoadingItemData = tree.mockedHandler("setLoadingItemData");
45
48
  tree.do.selectItem("x12");
46
- expect(setLoadingItems).toHaveBeenCalledWith(["x12"]);
49
+ expect(setLoadingItemChildrens).toHaveBeenCalledWith(["x12"]);
50
+ expect(setLoadingItemData).not.toHaveBeenCalled();
47
51
  await tree.resolveAsyncVisibleItems();
48
- expect(setLoadingItems).toHaveBeenCalledWith([]);
52
+ expect(setLoadingItemChildrens).toHaveBeenCalledWith([]);
53
+ expect(setLoadingItemData).toHaveBeenCalledWith(["x121"]);
54
+ expect(setLoadingItemData).toHaveBeenCalledWith([]);
49
55
  });
50
56
 
51
57
  it("calls onLoadedItem", async () => {
@@ -76,7 +82,7 @@ describe("core-feature/selections", () => {
76
82
  `${id}3`,
77
83
  `${id}4`,
78
84
  ]);
79
- const suiteTree = tree.with({ asyncDataLoader: { getItem, getChildren } });
85
+ const suiteTree = tree.with({ dataLoader: { getItem, getChildren } });
80
86
  suiteTree.resetBeforeEach();
81
87
 
82
88
  it("invalidates item data on item instance", async () => {
@@ -119,6 +125,4 @@ describe("core-feature/selections", () => {
119
125
  expect(getChildren).toHaveBeenCalledTimes(1);
120
126
  });
121
127
  });
122
-
123
- describe.todo("getChildrenWithData");
124
128
  });
@@ -1,28 +1,57 @@
1
1
  import { FeatureImplementation } from "../../types/core";
2
- import { AsyncDataLoaderRef } from "./types";
2
+ import { AsyncDataLoaderDataRef } from "./types";
3
3
  import { makeStateUpdater } from "../../utils";
4
4
 
5
5
  export const asyncDataLoaderFeature: FeatureImplementation = {
6
6
  key: "async-data-loader",
7
7
 
8
8
  getInitialState: (initialState) => ({
9
- loadingItems: [],
9
+ loadingItemData: [],
10
+ loadingItemChildrens: [],
10
11
  ...initialState,
11
12
  }),
12
13
 
13
14
  getDefaultConfig: (defaultConfig, tree) => ({
14
- setLoadingItems: makeStateUpdater("loadingItems", tree),
15
+ setLoadingItemData: makeStateUpdater("loadingItemData", tree),
16
+ setLoadingItemChildrens: makeStateUpdater("loadingItemChildrens", tree),
15
17
  ...defaultConfig,
16
18
  }),
17
19
 
18
20
  stateHandlerNames: {
19
- loadingItems: "setLoadingItems",
21
+ loadingItemData: "setLoadingItemData",
22
+ loadingItemChildrens: "setLoadingItemChildrens",
20
23
  },
21
24
 
22
25
  treeInstance: {
26
+ waitForItemDataLoaded: async ({ tree }, itemId) => {
27
+ tree.retrieveItemData(itemId);
28
+ if (!tree.getState().loadingItemData.includes(itemId)) {
29
+ return;
30
+ }
31
+ await new Promise<void>((resolve) => {
32
+ const dataRef = tree.getDataRef<AsyncDataLoaderDataRef>();
33
+ dataRef.current.awaitingItemDataLoading ??= {};
34
+ dataRef.current.awaitingItemDataLoading[itemId] ??= [];
35
+ dataRef.current.awaitingItemDataLoading[itemId].push(resolve);
36
+ });
37
+ },
38
+
39
+ waitForItemChildrenLoaded: async ({ tree }, itemId) => {
40
+ tree.retrieveChildrenIds(itemId);
41
+ if (!tree.getState().loadingItemChildrens.includes(itemId)) {
42
+ return;
43
+ }
44
+ await new Promise<void>((resolve) => {
45
+ const dataRef = tree.getDataRef<AsyncDataLoaderDataRef>();
46
+ dataRef.current.awaitingItemChildrensLoading ??= {};
47
+ dataRef.current.awaitingItemChildrensLoading[itemId] ??= [];
48
+ dataRef.current.awaitingItemChildrensLoading[itemId].push(resolve);
49
+ });
50
+ },
51
+
23
52
  retrieveItemData: ({ tree }, itemId) => {
24
53
  const config = tree.getConfig();
25
- const dataRef = tree.getDataRef<AsyncDataLoaderRef>();
54
+ const dataRef = tree.getDataRef<AsyncDataLoaderDataRef>();
26
55
  dataRef.current.itemData ??= {};
27
56
  dataRef.current.childrenIds ??= {};
28
57
 
@@ -30,18 +59,25 @@ export const asyncDataLoaderFeature: FeatureImplementation = {
30
59
  return dataRef.current.itemData[itemId];
31
60
  }
32
61
 
33
- if (!tree.getState().loadingItems.includes(itemId)) {
34
- tree.applySubStateUpdate("loadingItems", (loadingItems) => [
35
- ...loadingItems,
62
+ if (!tree.getState().loadingItemData.includes(itemId)) {
63
+ tree.applySubStateUpdate("loadingItemData", (loadingItemData) => [
64
+ ...loadingItemData,
36
65
  itemId,
37
66
  ]);
38
- config.asyncDataLoader?.getItem(itemId).then((item) => {
67
+
68
+ (async () => {
69
+ const item = await config.dataLoader.getItem(itemId);
39
70
  dataRef.current.itemData[itemId] = item;
40
71
  config.onLoadedItem?.(itemId, item);
41
- tree.applySubStateUpdate("loadingItems", (loadingItems) =>
42
- loadingItems.filter((id) => id !== itemId),
72
+ tree.applySubStateUpdate("loadingItemData", (loadingItemData) =>
73
+ loadingItemData.filter((id) => id !== itemId),
43
74
  );
44
- });
75
+
76
+ dataRef.current.awaitingItemDataLoading?.[itemId].forEach((cb) =>
77
+ cb(),
78
+ );
79
+ delete dataRef.current.awaitingItemDataLoading?.[itemId];
80
+ })();
45
81
  }
46
82
 
47
83
  return config.createLoadingItemData?.() ?? null;
@@ -49,46 +85,37 @@ export const asyncDataLoaderFeature: FeatureImplementation = {
49
85
 
50
86
  retrieveChildrenIds: ({ tree }, itemId) => {
51
87
  const config = tree.getConfig();
52
- const dataRef = tree.getDataRef<AsyncDataLoaderRef>();
53
- dataRef.current.itemData ??= {};
88
+ const dataRef = tree.getDataRef<AsyncDataLoaderDataRef>();
54
89
  dataRef.current.childrenIds ??= {};
55
90
  if (dataRef.current.childrenIds[itemId]) {
56
91
  return dataRef.current.childrenIds[itemId];
57
92
  }
58
93
 
59
- if (tree.getState().loadingItems.includes(itemId)) {
94
+ if (tree.getState().loadingItemChildrens.includes(itemId)) {
60
95
  return [];
61
96
  }
62
97
 
63
- tree.applySubStateUpdate("loadingItems", (loadingItems) => [
64
- ...loadingItems,
65
- itemId,
66
- ]);
67
-
68
- if (config.asyncDataLoader?.getChildrenWithData) {
69
- config.asyncDataLoader?.getChildrenWithData(itemId).then((children) => {
70
- for (const { id, data } of children) {
71
- dataRef.current.itemData[id] = data;
72
- config.onLoadedItem?.(id, data);
73
- }
74
- const childrenIds = children.map(({ id }) => id);
75
- dataRef.current.childrenIds[itemId] = childrenIds;
76
- config.onLoadedChildren?.(itemId, childrenIds);
77
- tree.applySubStateUpdate("loadingItems", (loadingItems) =>
78
- loadingItems.filter((id) => id !== itemId),
79
- );
80
- tree.rebuildTree();
81
- });
82
- } else {
83
- config.asyncDataLoader?.getChildren(itemId).then((childrenIds) => {
84
- dataRef.current.childrenIds[itemId] = childrenIds;
85
- config.onLoadedChildren?.(itemId, childrenIds);
86
- tree.applySubStateUpdate("loadingItems", (loadingItems) =>
87
- loadingItems.filter((id) => id !== itemId),
88
- );
89
- tree.rebuildTree();
90
- });
91
- }
98
+ tree.applySubStateUpdate(
99
+ "loadingItemChildrens",
100
+ (loadingItemChildrens) => [...loadingItemChildrens, itemId],
101
+ );
102
+
103
+ (async () => {
104
+ const childrenIds = await config.dataLoader.getChildren(itemId);
105
+ dataRef.current.childrenIds[itemId] = childrenIds;
106
+ config.onLoadedChildren?.(itemId, childrenIds);
107
+ tree.applySubStateUpdate(
108
+ "loadingItemChildrens",
109
+ (loadingItemChildrens) =>
110
+ loadingItemChildrens.filter((id) => id !== itemId),
111
+ );
112
+ tree.rebuildTree();
113
+
114
+ dataRef.current.awaitingItemChildrensLoading?.[itemId]?.forEach((cb) =>
115
+ cb(),
116
+ );
117
+ delete dataRef.current.awaitingItemChildrensLoading?.[itemId];
118
+ })();
92
119
 
93
120
  return [];
94
121
  },
@@ -96,19 +123,20 @@ export const asyncDataLoaderFeature: FeatureImplementation = {
96
123
 
97
124
  itemInstance: {
98
125
  isLoading: ({ tree, item }) =>
99
- tree.getState().loadingItems.includes(item.getItemMeta().itemId),
126
+ tree.getState().loadingItemData.includes(item.getItemMeta().itemId) ||
127
+ tree.getState().loadingItemChildrens.includes(item.getItemMeta().itemId),
100
128
  invalidateItemData: ({ tree, itemId }) => {
101
- const dataRef = tree.getDataRef<AsyncDataLoaderRef>();
129
+ const dataRef = tree.getDataRef<AsyncDataLoaderDataRef>();
102
130
  delete dataRef.current.itemData?.[itemId];
103
131
  tree.retrieveItemData(itemId);
104
132
  },
105
133
  invalidateChildrenIds: ({ tree, itemId }) => {
106
- const dataRef = tree.getDataRef<AsyncDataLoaderRef>();
134
+ const dataRef = tree.getDataRef<AsyncDataLoaderDataRef>();
107
135
  delete dataRef.current.childrenIds?.[itemId];
108
136
  tree.retrieveChildrenIds(itemId);
109
137
  },
110
138
  updateCachedChildrenIds: ({ tree, itemId }, childrenIds) => {
111
- const dataRef = tree.getDataRef<AsyncDataLoaderRef>();
139
+ const dataRef = tree.getDataRef<AsyncDataLoaderDataRef>();
112
140
  dataRef.current.childrenIds[itemId] = childrenIds;
113
141
  tree.rebuildTree();
114
142
  },
@@ -1,33 +1,40 @@
1
1
  import { SetStateFn } from "../../types/core";
2
2
  import { SyncDataLoaderFeatureDef } from "../sync-data-loader/types";
3
3
 
4
- export type AsyncTreeDataLoader<T> = {
5
- getItem: (itemId: string) => Promise<T>;
6
- getChildren: (itemId: string) => Promise<string[]>;
7
- getChildrenWithData?: (itemId: string) => Promise<{ id: string; data: T }[]>;
8
- };
4
+ type AwaitingLoaderCallbacks = Record<string, (() => void)[]>;
9
5
 
10
- export type AsyncDataLoaderRef<T = any> = {
6
+ export interface AsyncDataLoaderDataRef<T = any> {
11
7
  itemData: Record<string, T>;
12
8
  childrenIds: Record<string, string[]>;
13
- };
9
+ awaitingItemDataLoading: AwaitingLoaderCallbacks;
10
+ awaitingItemChildrensLoading: AwaitingLoaderCallbacks;
11
+ }
14
12
 
15
13
  /**
16
14
  * @category Async Data Loader/General
17
15
  * */
18
16
  export type AsyncDataLoaderFeatureDef<T> = {
19
17
  state: {
20
- loadingItems: string[];
18
+ loadingItemData: string[];
19
+ loadingItemChildrens: string[];
21
20
  };
22
21
  config: {
23
22
  rootItemId: string;
23
+
24
+ /** Will be called when HT retrieves item data for an item whose item data is asynchronously being loaded.
25
+ * Can be used to create placeholder data to use for rendering the tree item while it is loaded. If not defined,
26
+ * the tree item data will be null. */
24
27
  createLoadingItemData?: () => T;
25
- setLoadingItems?: SetStateFn<string[]>;
28
+
29
+ setLoadingItemData?: SetStateFn<string[]>;
30
+ setLoadingItemChildrens?: SetStateFn<string[]>;
26
31
  onLoadedItem?: (itemId: string, item: T) => void;
27
32
  onLoadedChildren?: (itemId: string, childrenIds: string[]) => void;
28
- asyncDataLoader?: AsyncTreeDataLoader<T>;
29
33
  };
30
- treeInstance: SyncDataLoaderFeatureDef<T>["treeInstance"];
34
+ treeInstance: SyncDataLoaderFeatureDef<T>["treeInstance"] & {
35
+ waitForItemDataLoaded: (itemId: string) => Promise<void>;
36
+ waitForItemChildrenLoaded: (itemId: string) => Promise<void>;
37
+ };
31
38
  itemInstance: SyncDataLoaderFeatureDef<T>["itemInstance"] & {
32
39
  /** Invalidate fetched data for item, and triggers a refetch and subsequent rerender if the item is visible */
33
40
  invalidateItemData: () => void;