@headless-tree/core 0.0.0-20250915162508 → 0.0.0-20250918201417
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 +6 -1
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +24 -5
- package/dist/index.mjs +24 -5
- package/package.json +1 -1
- package/src/core/create-tree.ts +24 -6
- package/src/features/async-data-loader/async-data-loader.spec.ts +1 -0
- package/src/features/async-data-loader/feature.ts +2 -2
- package/src/features/main/types.ts +7 -0
- package/src/test-utils/test-tree-do.ts +6 -0
- package/src/test-utils/test-tree.ts +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
# @headless-tree/core
|
|
2
2
|
|
|
3
|
-
## 0.0.0-
|
|
3
|
+
## 0.0.0-20250918201417
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- cbeaba6: all state updates (like setSelectedItems) will not propagate while the component is unmounted. This happened before for `tree.setState()` calls directly, but not individual state atoms like `setSelectedItems`. When calling `createTree()` directly (instead of `useTree()`), `tree.setMounted(true)` needs to be called once after mount. No changes are necessary when using the React-based `useTree()` integration. (#158)
|
|
4
8
|
|
|
5
9
|
### Patch Changes
|
|
6
10
|
|
|
7
11
|
- 72e714b: all NPM deployments will now publish with provenance
|
|
12
|
+
- 6693986: fixed an issue where async data loaders cause calling `item.getItemData()` outside of the component calling `useTree()` to cause a React warning log (#158)
|
|
8
13
|
- 7a7424f: fixed incorrect exports definition in package.json for require/cjs imports (#161)
|
|
9
14
|
- 215ab4b: add a new symbol that can be used in hotkey configurations "metaorcontrol" that will trigger if either any windows control key or mac meta key is pressed (#141)
|
|
10
15
|
- 51b0dea: Added `isUnorderedDragTarget` as alternative to `isDragTarget` for easier detection of drag type
|
package/dist/index.d.mts
CHANGED
|
@@ -184,6 +184,8 @@ type MainFeatureDef<T = any> = {
|
|
|
184
184
|
rebuildTree: () => void;
|
|
185
185
|
/** @deprecated Experimental feature, might get removed or changed in the future. */
|
|
186
186
|
scheduleRebuildTree: () => void;
|
|
187
|
+
/** @internal */
|
|
188
|
+
setMounted: (isMounted: boolean) => void;
|
|
187
189
|
};
|
|
188
190
|
itemInstance: {
|
|
189
191
|
registerElement: (element: HTMLElement | null) => void;
|
package/dist/index.d.ts
CHANGED
|
@@ -184,6 +184,8 @@ type MainFeatureDef<T = any> = {
|
|
|
184
184
|
rebuildTree: () => void;
|
|
185
185
|
/** @deprecated Experimental feature, might get removed or changed in the future. */
|
|
186
186
|
scheduleRebuildTree: () => void;
|
|
187
|
+
/** @internal */
|
|
188
|
+
setMounted: (isMounted: boolean) => void;
|
|
187
189
|
};
|
|
188
190
|
itemInstance: {
|
|
189
191
|
registerElement: (element: HTMLElement | null) => void;
|
package/dist/index.js
CHANGED
|
@@ -544,10 +544,29 @@ var createTree = (initialConfig) => {
|
|
|
544
544
|
var _a2;
|
|
545
545
|
(_a2 = config.setState) == null ? void 0 : _a2.call(config, state);
|
|
546
546
|
},
|
|
547
|
+
setMounted: ({}, isMounted) => {
|
|
548
|
+
var _a2;
|
|
549
|
+
const ref = treeDataRef;
|
|
550
|
+
treeDataRef.current.isMounted = isMounted;
|
|
551
|
+
if (isMounted) {
|
|
552
|
+
(_a2 = ref.waitingForMount) == null ? void 0 : _a2.forEach((cb) => cb());
|
|
553
|
+
ref.waitingForMount = [];
|
|
554
|
+
}
|
|
555
|
+
},
|
|
547
556
|
applySubStateUpdate: ({}, stateName, updater) => {
|
|
548
|
-
|
|
549
|
-
const
|
|
550
|
-
|
|
557
|
+
var _a2;
|
|
558
|
+
const apply = () => {
|
|
559
|
+
state[stateName] = typeof updater === "function" ? updater(state[stateName]) : updater;
|
|
560
|
+
const externalStateSetter = config[stateHandlerNames[stateName]];
|
|
561
|
+
externalStateSetter == null ? void 0 : externalStateSetter(state[stateName]);
|
|
562
|
+
};
|
|
563
|
+
const ref = treeDataRef.current;
|
|
564
|
+
if (ref.isMounted) {
|
|
565
|
+
apply();
|
|
566
|
+
} else {
|
|
567
|
+
(_a2 = ref.waitingForMount) != null ? _a2 : ref.waitingForMount = [];
|
|
568
|
+
ref.waitingForMount.push(apply);
|
|
569
|
+
}
|
|
551
570
|
},
|
|
552
571
|
// TODO rebuildSubTree: (itemId: string) => void;
|
|
553
572
|
rebuildTree: () => {
|
|
@@ -1169,7 +1188,7 @@ var asyncDataLoaderFeature = {
|
|
|
1169
1188
|
return dataRef.current.itemData[itemId];
|
|
1170
1189
|
}
|
|
1171
1190
|
if (!tree.getState().loadingItemData.includes(itemId) && !skipFetch) {
|
|
1172
|
-
loadItemData(tree, itemId);
|
|
1191
|
+
setTimeout(() => loadItemData(tree, itemId));
|
|
1173
1192
|
}
|
|
1174
1193
|
return (_b = (_a = config.createLoadingItemData) == null ? void 0 : _a.call(config)) != null ? _b : null;
|
|
1175
1194
|
},
|
|
@@ -1181,7 +1200,7 @@ var asyncDataLoaderFeature = {
|
|
|
1181
1200
|
if (tree.getState().loadingItemChildrens.includes(itemId) || skipFetch) {
|
|
1182
1201
|
return [];
|
|
1183
1202
|
}
|
|
1184
|
-
loadChildrenIds(tree, itemId);
|
|
1203
|
+
setTimeout(() => loadChildrenIds(tree, itemId));
|
|
1185
1204
|
return [];
|
|
1186
1205
|
}
|
|
1187
1206
|
},
|
package/dist/index.mjs
CHANGED
|
@@ -500,10 +500,29 @@ var createTree = (initialConfig) => {
|
|
|
500
500
|
var _a2;
|
|
501
501
|
(_a2 = config.setState) == null ? void 0 : _a2.call(config, state);
|
|
502
502
|
},
|
|
503
|
+
setMounted: ({}, isMounted) => {
|
|
504
|
+
var _a2;
|
|
505
|
+
const ref = treeDataRef;
|
|
506
|
+
treeDataRef.current.isMounted = isMounted;
|
|
507
|
+
if (isMounted) {
|
|
508
|
+
(_a2 = ref.waitingForMount) == null ? void 0 : _a2.forEach((cb) => cb());
|
|
509
|
+
ref.waitingForMount = [];
|
|
510
|
+
}
|
|
511
|
+
},
|
|
503
512
|
applySubStateUpdate: ({}, stateName, updater) => {
|
|
504
|
-
|
|
505
|
-
const
|
|
506
|
-
|
|
513
|
+
var _a2;
|
|
514
|
+
const apply = () => {
|
|
515
|
+
state[stateName] = typeof updater === "function" ? updater(state[stateName]) : updater;
|
|
516
|
+
const externalStateSetter = config[stateHandlerNames[stateName]];
|
|
517
|
+
externalStateSetter == null ? void 0 : externalStateSetter(state[stateName]);
|
|
518
|
+
};
|
|
519
|
+
const ref = treeDataRef.current;
|
|
520
|
+
if (ref.isMounted) {
|
|
521
|
+
apply();
|
|
522
|
+
} else {
|
|
523
|
+
(_a2 = ref.waitingForMount) != null ? _a2 : ref.waitingForMount = [];
|
|
524
|
+
ref.waitingForMount.push(apply);
|
|
525
|
+
}
|
|
507
526
|
},
|
|
508
527
|
// TODO rebuildSubTree: (itemId: string) => void;
|
|
509
528
|
rebuildTree: () => {
|
|
@@ -1125,7 +1144,7 @@ var asyncDataLoaderFeature = {
|
|
|
1125
1144
|
return dataRef.current.itemData[itemId];
|
|
1126
1145
|
}
|
|
1127
1146
|
if (!tree.getState().loadingItemData.includes(itemId) && !skipFetch) {
|
|
1128
|
-
loadItemData(tree, itemId);
|
|
1147
|
+
setTimeout(() => loadItemData(tree, itemId));
|
|
1129
1148
|
}
|
|
1130
1149
|
return (_b = (_a = config.createLoadingItemData) == null ? void 0 : _a.call(config)) != null ? _b : null;
|
|
1131
1150
|
},
|
|
@@ -1137,7 +1156,7 @@ var asyncDataLoaderFeature = {
|
|
|
1137
1156
|
if (tree.getState().loadingItemChildrens.includes(itemId) || skipFetch) {
|
|
1138
1157
|
return [];
|
|
1139
1158
|
}
|
|
1140
|
-
loadChildrenIds(tree, itemId);
|
|
1159
|
+
setTimeout(() => loadChildrenIds(tree, itemId));
|
|
1141
1160
|
return [];
|
|
1142
1161
|
}
|
|
1143
1162
|
},
|
package/package.json
CHANGED
package/src/core/create-tree.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { treeFeature } from "../features/tree/feature";
|
|
|
11
11
|
import { ItemMeta } from "../features/tree/types";
|
|
12
12
|
import { buildStaticInstance } from "./build-static-instance";
|
|
13
13
|
import { throwError } from "../utilities/errors";
|
|
14
|
+
import type { TreeDataRef } from "../features/main/types";
|
|
14
15
|
|
|
15
16
|
const verifyFeatures = (features: FeatureImplementation[] | undefined) => {
|
|
16
17
|
const loadedFeatures = features?.map((feature) => feature.key);
|
|
@@ -161,17 +162,34 @@ export const createTree = <T>(
|
|
|
161
162
|
config.setState?.(state); // TODO this cant be right... This doesnt allow external state updates
|
|
162
163
|
// TODO this is never used, remove
|
|
163
164
|
},
|
|
165
|
+
setMounted: ({}, isMounted) => {
|
|
166
|
+
const ref = treeDataRef as TreeDataRef;
|
|
167
|
+
treeDataRef.current.isMounted = isMounted;
|
|
168
|
+
if (isMounted) {
|
|
169
|
+
ref.waitingForMount?.forEach((cb) => cb());
|
|
170
|
+
ref.waitingForMount = [];
|
|
171
|
+
}
|
|
172
|
+
},
|
|
164
173
|
applySubStateUpdate: <K extends keyof TreeState<any>>(
|
|
165
174
|
{},
|
|
166
175
|
stateName: K,
|
|
167
176
|
updater: Updater<TreeState<T>[K]>,
|
|
168
177
|
) => {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
178
|
+
const apply = () => {
|
|
179
|
+
state[stateName] =
|
|
180
|
+
typeof updater === "function" ? updater(state[stateName]) : updater;
|
|
181
|
+
const externalStateSetter = config[
|
|
182
|
+
stateHandlerNames[stateName]
|
|
183
|
+
] as Function;
|
|
184
|
+
externalStateSetter?.(state[stateName]);
|
|
185
|
+
};
|
|
186
|
+
const ref = treeDataRef.current as TreeDataRef;
|
|
187
|
+
if (ref.isMounted) {
|
|
188
|
+
apply();
|
|
189
|
+
} else {
|
|
190
|
+
ref.waitingForMount ??= [];
|
|
191
|
+
ref.waitingForMount.push(apply);
|
|
192
|
+
}
|
|
175
193
|
},
|
|
176
194
|
// TODO rebuildSubTree: (itemId: string) => void;
|
|
177
195
|
rebuildTree: () => {
|
|
@@ -46,6 +46,7 @@ describe("core-feature/selections", () => {
|
|
|
46
46
|
);
|
|
47
47
|
const setLoadingItemData = tree.mockedHandler("setLoadingItemData");
|
|
48
48
|
tree.do.selectItem("x12");
|
|
49
|
+
await tree.do.awaitNextTick();
|
|
49
50
|
expect(setLoadingItemChildrens).toHaveBeenCalledWith(["x12"]);
|
|
50
51
|
expect(setLoadingItemData).not.toHaveBeenCalled();
|
|
51
52
|
await tree.resolveAsyncVisibleItems();
|
|
@@ -118,7 +118,7 @@ export const asyncDataLoaderFeature: FeatureImplementation = {
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
if (!tree.getState().loadingItemData.includes(itemId) && !skipFetch) {
|
|
121
|
-
loadItemData(tree, itemId);
|
|
121
|
+
setTimeout(() => loadItemData(tree, itemId));
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
return config.createLoadingItemData?.() ?? null;
|
|
@@ -134,7 +134,7 @@ export const asyncDataLoaderFeature: FeatureImplementation = {
|
|
|
134
134
|
return [];
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
loadChildrenIds(tree, itemId);
|
|
137
|
+
setTimeout(() => loadChildrenIds(tree, itemId));
|
|
138
138
|
|
|
139
139
|
return [];
|
|
140
140
|
},
|
|
@@ -10,6 +10,11 @@ import {
|
|
|
10
10
|
} from "../../types/core";
|
|
11
11
|
import { ItemMeta } from "../tree/types";
|
|
12
12
|
|
|
13
|
+
export interface TreeDataRef {
|
|
14
|
+
isMounted?: boolean;
|
|
15
|
+
waitingForMount?: (() => void)[];
|
|
16
|
+
}
|
|
17
|
+
|
|
13
18
|
export type InstanceTypeMap = {
|
|
14
19
|
itemInstance: ItemInstance<any>;
|
|
15
20
|
treeInstance: TreeInstance<any>;
|
|
@@ -51,6 +56,8 @@ export type MainFeatureDef<T = any> = {
|
|
|
51
56
|
rebuildTree: () => void;
|
|
52
57
|
/** @deprecated Experimental feature, might get removed or changed in the future. */
|
|
53
58
|
scheduleRebuildTree: () => void;
|
|
59
|
+
/** @internal */
|
|
60
|
+
setMounted: (isMounted: boolean) => void;
|
|
54
61
|
};
|
|
55
62
|
itemInstance: {
|
|
56
63
|
registerElement: (element: HTMLElement | null) => void;
|
|
@@ -78,6 +78,7 @@ export class TestTree<T = string> {
|
|
|
78
78
|
get instance() {
|
|
79
79
|
if (!this.treeInstance) {
|
|
80
80
|
this.treeInstance = createTree(this.config);
|
|
81
|
+
this.treeInstance.setMounted(true);
|
|
81
82
|
this.treeInstance.rebuildTree();
|
|
82
83
|
}
|
|
83
84
|
return this.treeInstance;
|
|
@@ -87,10 +88,9 @@ export class TestTree<T = string> {
|
|
|
87
88
|
|
|
88
89
|
static async resolveAsyncLoaders() {
|
|
89
90
|
do {
|
|
91
|
+
await vi.advanceTimersToNextTimerAsync();
|
|
90
92
|
TestTree.asyncLoaderResolvers.shift()?.();
|
|
91
|
-
await
|
|
92
|
-
setTimeout(r);
|
|
93
|
-
});
|
|
93
|
+
await vi.advanceTimersToNextTimerAsync();
|
|
94
94
|
} while (TestTree.asyncLoaderResolvers.length);
|
|
95
95
|
}
|
|
96
96
|
|