@headless-tree/core 0.0.0-20250726131830 → 0.0.0-20250730213235

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.
package/CHANGELOG.md CHANGED
@@ -1,9 +1,10 @@
1
1
  # @headless-tree/core
2
2
 
3
- ## 0.0.0-20250726131830
3
+ ## 0.0.0-20250730213235
4
4
 
5
5
  ### Patch Changes
6
6
 
7
+ - e8ddbb0: Added `item.updateCachedData(data)` in async tree feature, that works similar to the existing `item.updateCachedChildrenIds(childrenIds)` feature
7
8
  - 662e2a8: Added stories and documentation on how to use nested DOM rendering for tree structures instead of flat lists,
8
9
  which can be used for animating expand/collapse behavior
9
10
  - b41e1d2: fixed a bug where ending drag without successful drop doesn't properly reset drag line (#132)
package/dist/index.d.mts CHANGED
@@ -150,8 +150,6 @@ type MainFeatureDef<T = any> = {
150
150
  treeInstance: {
151
151
  /** @internal */
152
152
  applySubStateUpdate: <K extends keyof TreeState<any>>(stateName: K, updater: Updater<TreeState<T>[K]>) => void;
153
- /** @internal */
154
- buildItemInstance: (itemId: string) => ItemInstance<T>;
155
153
  setState: SetStateFn<TreeState<T>>;
156
154
  getState: () => TreeState<T>;
157
155
  setConfig: SetStateFn<TreeConfig<T>>;
@@ -296,6 +294,7 @@ type AsyncDataLoaderFeatureDef<T> = {
296
294
  * @param optimistic If true, the item will not trigger a state update on `loadingItemChildrens`, and
297
295
  * the tree will continue to display the old data until the new data has loaded. */
298
296
  invalidateChildrenIds: (optimistic?: boolean) => Promise<void>;
297
+ updateCachedData: (data: T) => void;
299
298
  updateCachedChildrenIds: (childrenIds: string[]) => void;
300
299
  isLoading: () => boolean;
301
300
  };
package/dist/index.d.ts CHANGED
@@ -150,8 +150,6 @@ type MainFeatureDef<T = any> = {
150
150
  treeInstance: {
151
151
  /** @internal */
152
152
  applySubStateUpdate: <K extends keyof TreeState<any>>(stateName: K, updater: Updater<TreeState<T>[K]>) => void;
153
- /** @internal */
154
- buildItemInstance: (itemId: string) => ItemInstance<T>;
155
153
  setState: SetStateFn<TreeState<T>>;
156
154
  getState: () => TreeState<T>;
157
155
  setConfig: SetStateFn<TreeConfig<T>>;
@@ -296,6 +294,7 @@ type AsyncDataLoaderFeatureDef<T> = {
296
294
  * @param optimistic If true, the item will not trigger a state update on `loadingItemChildrens`, and
297
295
  * the tree will continue to display the old data until the new data has loaded. */
298
296
  invalidateChildrenIds: (optimistic?: boolean) => Promise<void>;
297
+ updateCachedData: (data: T) => void;
299
298
  updateCachedChildrenIds: (childrenIds: string[]) => void;
300
299
  isLoading: () => boolean;
301
300
  };
package/dist/index.js CHANGED
@@ -547,19 +547,6 @@ var createTree = (initialConfig) => {
547
547
  const externalStateSetter = config[stateHandlerNames[stateName]];
548
548
  externalStateSetter == null ? void 0 : externalStateSetter(state[stateName]);
549
549
  },
550
- buildItemInstance: ({}, itemId) => {
551
- const [instance, finalizeInstance] = buildInstance(
552
- features,
553
- "itemInstance",
554
- (instance2) => ({
555
- item: instance2,
556
- tree: treeInstance,
557
- itemId
558
- })
559
- );
560
- finalizeInstance();
561
- return instance;
562
- },
563
550
  // TODO rebuildSubTree: (itemId: string) => void;
564
551
  rebuildTree: () => {
565
552
  var _a2;
@@ -580,7 +567,23 @@ var createTree = (initialConfig) => {
580
567
  (_c2 = config.setState) == null ? void 0 : _c2.call(config, state);
581
568
  }
582
569
  },
583
- getItemInstance: ({}, itemId) => itemInstancesMap[itemId],
570
+ getItemInstance: ({}, itemId) => {
571
+ const existingInstance = itemInstancesMap[itemId];
572
+ if (!existingInstance) {
573
+ const [instance, finalizeInstance] = buildInstance(
574
+ features,
575
+ "itemInstance",
576
+ (instance2) => ({
577
+ item: instance2,
578
+ tree: treeInstance,
579
+ itemId
580
+ })
581
+ );
582
+ finalizeInstance();
583
+ return instance;
584
+ }
585
+ return existingInstance;
586
+ },
584
587
  getItems: () => itemInstances,
585
588
  registerElement: ({}, element) => {
586
589
  if (treeElement === element) {
@@ -636,7 +639,17 @@ var createTree = (initialConfig) => {
636
639
  var _a2;
637
640
  return (_a2 = itemDataRefs[itemId]) != null ? _a2 : itemDataRefs[itemId] = { current: {} };
638
641
  },
639
- getItemMeta: ({ itemId }) => itemMetaMap[itemId]
642
+ getItemMeta: ({ itemId }) => {
643
+ var _a2;
644
+ return (_a2 = itemMetaMap[itemId]) != null ? _a2 : {
645
+ itemId,
646
+ parentId: null,
647
+ level: -1,
648
+ index: -1,
649
+ posInSet: 0,
650
+ setSize: 1
651
+ };
652
+ }
640
653
  }
641
654
  };
642
655
  features.unshift(mainFeature);
@@ -801,7 +814,7 @@ var selectionFeature = {
801
814
 
802
815
  // src/features/checkboxes/feature.ts
803
816
  var getAllLoadedDescendants = (tree, itemId, includeFolders = false) => {
804
- if (!tree.getConfig().isItemFolder(tree.buildItemInstance(itemId))) {
817
+ if (!tree.getConfig().isItemFolder(tree.getItemInstance(itemId))) {
805
818
  return [itemId];
806
819
  }
807
820
  const descendants = tree.retrieveChildrenIds(itemId).map((child) => getAllLoadedDescendants(tree, child, includeFolders)).flat();
@@ -1147,6 +1160,11 @@ var asyncDataLoaderFeature = {
1147
1160
  const dataRef = tree.getDataRef();
1148
1161
  dataRef.current.childrenIds[itemId] = childrenIds;
1149
1162
  tree.rebuildTree();
1163
+ },
1164
+ updateCachedData: ({ tree, itemId }, data) => {
1165
+ const dataRef = tree.getDataRef();
1166
+ dataRef.current.itemData[itemId] = data;
1167
+ tree.rebuildTree();
1150
1168
  }
1151
1169
  }
1152
1170
  };
package/dist/index.mjs CHANGED
@@ -503,19 +503,6 @@ var createTree = (initialConfig) => {
503
503
  const externalStateSetter = config[stateHandlerNames[stateName]];
504
504
  externalStateSetter == null ? void 0 : externalStateSetter(state[stateName]);
505
505
  },
506
- buildItemInstance: ({}, itemId) => {
507
- const [instance, finalizeInstance] = buildInstance(
508
- features,
509
- "itemInstance",
510
- (instance2) => ({
511
- item: instance2,
512
- tree: treeInstance,
513
- itemId
514
- })
515
- );
516
- finalizeInstance();
517
- return instance;
518
- },
519
506
  // TODO rebuildSubTree: (itemId: string) => void;
520
507
  rebuildTree: () => {
521
508
  var _a2;
@@ -536,7 +523,23 @@ var createTree = (initialConfig) => {
536
523
  (_c2 = config.setState) == null ? void 0 : _c2.call(config, state);
537
524
  }
538
525
  },
539
- getItemInstance: ({}, itemId) => itemInstancesMap[itemId],
526
+ getItemInstance: ({}, itemId) => {
527
+ const existingInstance = itemInstancesMap[itemId];
528
+ if (!existingInstance) {
529
+ const [instance, finalizeInstance] = buildInstance(
530
+ features,
531
+ "itemInstance",
532
+ (instance2) => ({
533
+ item: instance2,
534
+ tree: treeInstance,
535
+ itemId
536
+ })
537
+ );
538
+ finalizeInstance();
539
+ return instance;
540
+ }
541
+ return existingInstance;
542
+ },
540
543
  getItems: () => itemInstances,
541
544
  registerElement: ({}, element) => {
542
545
  if (treeElement === element) {
@@ -592,7 +595,17 @@ var createTree = (initialConfig) => {
592
595
  var _a2;
593
596
  return (_a2 = itemDataRefs[itemId]) != null ? _a2 : itemDataRefs[itemId] = { current: {} };
594
597
  },
595
- getItemMeta: ({ itemId }) => itemMetaMap[itemId]
598
+ getItemMeta: ({ itemId }) => {
599
+ var _a2;
600
+ return (_a2 = itemMetaMap[itemId]) != null ? _a2 : {
601
+ itemId,
602
+ parentId: null,
603
+ level: -1,
604
+ index: -1,
605
+ posInSet: 0,
606
+ setSize: 1
607
+ };
608
+ }
596
609
  }
597
610
  };
598
611
  features.unshift(mainFeature);
@@ -757,7 +770,7 @@ var selectionFeature = {
757
770
 
758
771
  // src/features/checkboxes/feature.ts
759
772
  var getAllLoadedDescendants = (tree, itemId, includeFolders = false) => {
760
- if (!tree.getConfig().isItemFolder(tree.buildItemInstance(itemId))) {
773
+ if (!tree.getConfig().isItemFolder(tree.getItemInstance(itemId))) {
761
774
  return [itemId];
762
775
  }
763
776
  const descendants = tree.retrieveChildrenIds(itemId).map((child) => getAllLoadedDescendants(tree, child, includeFolders)).flat();
@@ -1103,6 +1116,11 @@ var asyncDataLoaderFeature = {
1103
1116
  const dataRef = tree.getDataRef();
1104
1117
  dataRef.current.childrenIds[itemId] = childrenIds;
1105
1118
  tree.rebuildTree();
1119
+ },
1120
+ updateCachedData: ({ tree, itemId }, data) => {
1121
+ const dataRef = tree.getDataRef();
1122
+ dataRef.current.itemData[itemId] = data;
1123
+ tree.rebuildTree();
1106
1124
  }
1107
1125
  }
1108
1126
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@headless-tree/core",
3
- "version": "0.0.0-20250726131830",
3
+ "version": "0.0.0-20250730213235",
4
4
  "main": "dist/index.d.ts",
5
5
  "module": "dist/index.mjs",
6
6
  "types": "dist/index.d.mts",
@@ -170,19 +170,6 @@ export const createTree = <T>(
170
170
  ] as Function;
171
171
  externalStateSetter?.(state[stateName]);
172
172
  },
173
- buildItemInstance: ({}, itemId) => {
174
- const [instance, finalizeInstance] = buildInstance(
175
- features,
176
- "itemInstance",
177
- (instance) => ({
178
- item: instance,
179
- tree: treeInstance,
180
- itemId,
181
- }),
182
- );
183
- finalizeInstance();
184
- return instance;
185
- },
186
173
  // TODO rebuildSubTree: (itemId: string) => void;
187
174
  rebuildTree: () => {
188
175
  rebuildItemMeta();
@@ -206,7 +193,23 @@ export const createTree = <T>(
206
193
  config.setState?.(state);
207
194
  }
208
195
  },
209
- getItemInstance: ({}, itemId) => itemInstancesMap[itemId],
196
+ getItemInstance: ({}, itemId) => {
197
+ const existingInstance = itemInstancesMap[itemId];
198
+ if (!existingInstance) {
199
+ const [instance, finalizeInstance] = buildInstance(
200
+ features,
201
+ "itemInstance",
202
+ (instance) => ({
203
+ item: instance,
204
+ tree: treeInstance,
205
+ itemId,
206
+ }),
207
+ );
208
+ finalizeInstance();
209
+ return instance;
210
+ }
211
+ return existingInstance;
212
+ },
210
213
  getItems: () => itemInstances,
211
214
  registerElement: ({}, element) => {
212
215
  if (treeElement === element) {
@@ -249,7 +252,15 @@ export const createTree = <T>(
249
252
  getElement: ({ itemId }) => itemElementsMap[itemId],
250
253
  // eslint-disable-next-line no-return-assign
251
254
  getDataRef: ({ itemId }) => (itemDataRefs[itemId] ??= { current: {} }),
252
- getItemMeta: ({ itemId }) => itemMetaMap[itemId],
255
+ getItemMeta: ({ itemId }) =>
256
+ itemMetaMap[itemId] ?? {
257
+ itemId,
258
+ parentId: null,
259
+ level: -1,
260
+ index: -1,
261
+ posInSet: 0,
262
+ setSize: 1,
263
+ },
253
264
  },
254
265
  };
255
266
 
@@ -165,5 +165,10 @@ export const asyncDataLoaderFeature: FeatureImplementation = {
165
165
  dataRef.current.childrenIds[itemId] = childrenIds;
166
166
  tree.rebuildTree();
167
167
  },
168
+ updateCachedData: ({ tree, itemId }, data) => {
169
+ const dataRef = tree.getDataRef<AsyncDataLoaderDataRef>();
170
+ dataRef.current.itemData[itemId] = data;
171
+ tree.rebuildTree();
172
+ },
168
173
  },
169
174
  };
@@ -34,6 +34,7 @@ export type AsyncDataLoaderFeatureDef<T> = {
34
34
  waitForItemChildrenLoaded: (itemId: string) => Promise<void>;
35
35
  loadItemData: (itemId: string) => Promise<T>;
36
36
  loadChildrenIds: (itemId: string) => Promise<string[]>;
37
+ /* idea: recursiveLoadItems: (itemId: string, cancelToken?: { current: boolean }, onLoad: (itemIds: string[]) => void) => Promise<T[]> */
37
38
  };
38
39
  itemInstance: SyncDataLoaderFeatureDef<T>["itemInstance"] & {
39
40
  /** Invalidate fetched data for item, and triggers a refetch and subsequent rerender if the item is visible
@@ -46,6 +47,7 @@ export type AsyncDataLoaderFeatureDef<T> = {
46
47
  * the tree will continue to display the old data until the new data has loaded. */
47
48
  invalidateChildrenIds: (optimistic?: boolean) => Promise<void>;
48
49
 
50
+ updateCachedData: (data: T) => void;
49
51
  updateCachedChildrenIds: (childrenIds: string[]) => void;
50
52
  isLoading: () => boolean;
51
53
  };
@@ -8,7 +8,7 @@ const getAllLoadedDescendants = <T>(
8
8
  itemId: string,
9
9
  includeFolders = false,
10
10
  ): string[] => {
11
- if (!tree.getConfig().isItemFolder(tree.buildItemInstance(itemId))) {
11
+ if (!tree.getConfig().isItemFolder(tree.getItemInstance(itemId))) {
12
12
  return [itemId];
13
13
  }
14
14
  const descendants = tree
@@ -36,8 +36,6 @@ export type MainFeatureDef<T = any> = {
36
36
  stateName: K,
37
37
  updater: Updater<TreeState<T>[K]>,
38
38
  ) => void;
39
- /** @internal */
40
- buildItemInstance: (itemId: string) => ItemInstance<T>;
41
39
  setState: SetStateFn<TreeState<T>>;
42
40
  getState: () => TreeState<T>;
43
41
  setConfig: SetStateFn<TreeConfig<T>>;