@headless-tree/core 0.0.0-20250508233916 → 0.0.0-20250509212452

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 (39) hide show
  1. package/CHANGELOG.md +2 -1
  2. package/lib/cjs/core/create-tree.js +9 -0
  3. package/lib/cjs/features/async-data-loader/feature.js +90 -64
  4. package/lib/cjs/features/async-data-loader/types.d.ts +12 -3
  5. package/lib/cjs/features/checkboxes/feature.d.ts +2 -0
  6. package/lib/cjs/features/checkboxes/feature.js +128 -0
  7. package/lib/cjs/features/checkboxes/types.d.ts +26 -0
  8. package/lib/cjs/features/checkboxes/types.js +9 -0
  9. package/lib/cjs/features/main/types.d.ts +2 -0
  10. package/lib/cjs/features/sync-data-loader/feature.js +2 -0
  11. package/lib/cjs/features/sync-data-loader/types.d.ts +2 -2
  12. package/lib/cjs/index.d.ts +2 -0
  13. package/lib/cjs/index.js +2 -0
  14. package/lib/cjs/types/core.d.ts +2 -1
  15. package/lib/esm/core/create-tree.js +9 -0
  16. package/lib/esm/features/async-data-loader/feature.js +90 -64
  17. package/lib/esm/features/async-data-loader/types.d.ts +12 -3
  18. package/lib/esm/features/checkboxes/feature.d.ts +2 -0
  19. package/lib/esm/features/checkboxes/feature.js +125 -0
  20. package/lib/esm/features/checkboxes/types.d.ts +26 -0
  21. package/lib/esm/features/checkboxes/types.js +6 -0
  22. package/lib/esm/features/main/types.d.ts +2 -0
  23. package/lib/esm/features/sync-data-loader/feature.js +2 -0
  24. package/lib/esm/features/sync-data-loader/types.d.ts +2 -2
  25. package/lib/esm/index.d.ts +2 -0
  26. package/lib/esm/index.js +2 -0
  27. package/lib/esm/types/core.d.ts +2 -1
  28. package/package.json +1 -1
  29. package/src/core/create-tree.ts +13 -0
  30. package/src/features/async-data-loader/async-data-loader.spec.ts +36 -0
  31. package/src/features/async-data-loader/feature.ts +103 -68
  32. package/src/features/async-data-loader/types.ts +14 -3
  33. package/src/features/checkboxes/feature.ts +150 -0
  34. package/src/features/checkboxes/types.ts +28 -0
  35. package/src/features/main/types.ts +2 -0
  36. package/src/features/sync-data-loader/feature.ts +3 -0
  37. package/src/features/sync-data-loader/types.ts +2 -2
  38. package/src/index.ts +2 -0
  39. package/src/types/core.ts +2 -0
package/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @headless-tree/core
2
2
 
3
- ## 0.0.0-20250508233916
3
+ ## 0.0.0-20250509212452
4
4
 
5
5
  ### Minor Changes
6
6
 
@@ -10,6 +10,7 @@
10
10
 
11
11
  - 29b2c64: improved key-handling behavior for hotkeys while input elements are focused (#98)
12
12
  - da1e757: fixed a bug where alt-tabbing out of browser will break hotkeys feature
13
+ - c283f52: add feature to allow async data invalidation without triggering rerenders with `invalidateItemData(optimistic: true)` (#95)
13
14
  - 29b2c64: added option to completely ignore hotkey events while input elements are focused (`ignoreHotkeysOnInput`) (#98)
14
15
 
15
16
  ## 1.0.1
@@ -100,6 +100,15 @@ const createTree = (initialConfig) => {
100
100
  const externalStateSetter = config[stateHandlerNames[stateName]];
101
101
  externalStateSetter === null || externalStateSetter === void 0 ? void 0 : externalStateSetter(state[stateName]);
102
102
  },
103
+ buildItemInstance: ({}, itemId) => {
104
+ const [instance, finalizeInstance] = buildInstance(features, "itemInstance", (instance) => ({
105
+ item: instance,
106
+ tree: treeInstance,
107
+ itemId,
108
+ }));
109
+ finalizeInstance();
110
+ return instance;
111
+ },
103
112
  // TODO rebuildSubTree: (itemId: string) => void;
104
113
  rebuildTree: () => {
105
114
  var _a;
@@ -11,6 +11,58 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.asyncDataLoaderFeature = void 0;
13
13
  const utils_1 = require("../../utils");
14
+ const getDataRef = (tree) => {
15
+ var _a, _b;
16
+ var _c, _d;
17
+ const dataRef = tree.getDataRef();
18
+ (_a = (_c = dataRef.current).itemData) !== null && _a !== void 0 ? _a : (_c.itemData = {});
19
+ (_b = (_d = dataRef.current).childrenIds) !== null && _b !== void 0 ? _b : (_d.childrenIds = {});
20
+ return dataRef;
21
+ };
22
+ const loadItemData = (tree, itemId) => __awaiter(void 0, void 0, void 0, function* () {
23
+ var _a, _b, _c;
24
+ const config = tree.getConfig();
25
+ const dataRef = getDataRef(tree);
26
+ const item = yield config.dataLoader.getItem(itemId);
27
+ dataRef.current.itemData[itemId] = item;
28
+ (_a = config.onLoadedItem) === null || _a === void 0 ? void 0 : _a.call(config, itemId, item);
29
+ tree.applySubStateUpdate("loadingItemData", (loadingItemData) => loadingItemData.filter((id) => id !== itemId));
30
+ (_b = dataRef.current.awaitingItemDataLoading) === null || _b === void 0 ? void 0 : _b[itemId].forEach((cb) => cb());
31
+ (_c = dataRef.current.awaitingItemDataLoading) === null || _c === void 0 ? true : delete _c[itemId];
32
+ return item;
33
+ });
34
+ const loadChildrenIds = (tree, itemId) => __awaiter(void 0, void 0, void 0, function* () {
35
+ var _a, _b, _c, _d, _e;
36
+ const config = tree.getConfig();
37
+ const dataRef = getDataRef(tree);
38
+ let childrenIds;
39
+ // TODO is folder check?
40
+ if ("getChildrenWithData" in config.dataLoader) {
41
+ const children = yield config.dataLoader.getChildrenWithData(itemId);
42
+ childrenIds = children.map((c) => c.id);
43
+ dataRef.current.childrenIds[itemId] = childrenIds;
44
+ children.forEach(({ id, data }) => {
45
+ var _a, _b, _c;
46
+ dataRef.current.itemData[id] = data;
47
+ (_a = config.onLoadedItem) === null || _a === void 0 ? void 0 : _a.call(config, id, data);
48
+ (_b = dataRef.current.awaitingItemDataLoading) === null || _b === void 0 ? void 0 : _b[id].forEach((cb) => cb());
49
+ (_c = dataRef.current.awaitingItemDataLoading) === null || _c === void 0 ? true : delete _c[id];
50
+ });
51
+ (_a = config.onLoadedChildren) === null || _a === void 0 ? void 0 : _a.call(config, itemId, childrenIds);
52
+ tree.rebuildTree();
53
+ tree.applySubStateUpdate("loadingItemData", (loadingItemData) => loadingItemData.filter((id) => !childrenIds.includes(id)));
54
+ }
55
+ else {
56
+ childrenIds = yield config.dataLoader.getChildren(itemId);
57
+ dataRef.current.childrenIds[itemId] = childrenIds;
58
+ (_b = config.onLoadedChildren) === null || _b === void 0 ? void 0 : _b.call(config, itemId, childrenIds);
59
+ tree.rebuildTree();
60
+ }
61
+ tree.applySubStateUpdate("loadingItemChildrens", (loadingItemChildrens) => loadingItemChildrens.filter((id) => id !== itemId));
62
+ (_d = (_c = dataRef.current.awaitingItemChildrensLoading) === null || _c === void 0 ? void 0 : _c[itemId]) === null || _d === void 0 ? void 0 : _d.forEach((cb) => cb());
63
+ (_e = dataRef.current.awaitingItemChildrensLoading) === null || _e === void 0 ? true : delete _e[itemId];
64
+ return childrenIds;
65
+ });
14
66
  exports.asyncDataLoaderFeature = {
15
67
  key: "async-data-loader",
16
68
  getInitialState: (initialState) => (Object.assign({ loadingItemData: [], loadingItemChildrens: [] }, initialState)),
@@ -35,6 +87,7 @@ exports.asyncDataLoaderFeature = {
35
87
  });
36
88
  }),
37
89
  waitForItemChildrenLoaded: (_a, itemId_1) => __awaiter(void 0, [_a, itemId_1], void 0, function* ({ tree }, itemId) {
90
+ // TODO replace inner implementation with load() fns
38
91
  tree.retrieveChildrenIds(itemId);
39
92
  if (!tree.getState().loadingItemChildrens.includes(itemId)) {
40
93
  return;
@@ -48,92 +101,65 @@ exports.asyncDataLoaderFeature = {
48
101
  dataRef.current.awaitingItemChildrensLoading[itemId].push(resolve);
49
102
  });
50
103
  }),
51
- retrieveItemData: ({ tree }, itemId) => {
52
- var _a, _b, _c, _d;
53
- var _e, _f;
104
+ loadItemData: (_a, itemId_1) => __awaiter(void 0, [_a, itemId_1], void 0, function* ({ tree }, itemId) {
105
+ var _b;
106
+ return ((_b = getDataRef(tree).current.itemData[itemId]) !== null && _b !== void 0 ? _b : (yield loadItemData(tree, itemId)));
107
+ }),
108
+ loadChildrenIds: (_a, itemId_1) => __awaiter(void 0, [_a, itemId_1], void 0, function* ({ tree }, itemId) {
109
+ var _b;
110
+ return ((_b = getDataRef(tree).current.childrenIds[itemId]) !== null && _b !== void 0 ? _b : (yield loadChildrenIds(tree, itemId)));
111
+ }),
112
+ retrieveItemData: ({ tree }, itemId, skipFetch = false) => {
113
+ var _a, _b;
54
114
  const config = tree.getConfig();
55
- const dataRef = tree.getDataRef();
56
- (_a = (_e = dataRef.current).itemData) !== null && _a !== void 0 ? _a : (_e.itemData = {});
57
- (_b = (_f = dataRef.current).childrenIds) !== null && _b !== void 0 ? _b : (_f.childrenIds = {});
115
+ const dataRef = getDataRef(tree);
58
116
  if (dataRef.current.itemData[itemId]) {
59
117
  return dataRef.current.itemData[itemId];
60
118
  }
61
- if (!tree.getState().loadingItemData.includes(itemId)) {
119
+ if (!tree.getState().loadingItemData.includes(itemId) && !skipFetch) {
62
120
  tree.applySubStateUpdate("loadingItemData", (loadingItemData) => [
63
121
  ...loadingItemData,
64
122
  itemId,
65
123
  ]);
66
- (() => __awaiter(void 0, void 0, void 0, function* () {
67
- var _a, _b, _c;
68
- const item = yield config.dataLoader.getItem(itemId);
69
- dataRef.current.itemData[itemId] = item;
70
- (_a = config.onLoadedItem) === null || _a === void 0 ? void 0 : _a.call(config, itemId, item);
71
- tree.applySubStateUpdate("loadingItemData", (loadingItemData) => loadingItemData.filter((id) => id !== itemId));
72
- (_b = dataRef.current.awaitingItemDataLoading) === null || _b === void 0 ? void 0 : _b[itemId].forEach((cb) => cb());
73
- (_c = dataRef.current.awaitingItemDataLoading) === null || _c === void 0 ? true : delete _c[itemId];
74
- }))();
124
+ loadItemData(tree, itemId);
75
125
  }
76
- return (_d = (_c = config.createLoadingItemData) === null || _c === void 0 ? void 0 : _c.call(config)) !== null && _d !== void 0 ? _d : null;
126
+ return (_b = (_a = config.createLoadingItemData) === null || _a === void 0 ? void 0 : _a.call(config)) !== null && _b !== void 0 ? _b : null;
77
127
  },
78
- retrieveChildrenIds: ({ tree }, itemId) => {
79
- var _a, _b;
80
- var _c, _d;
81
- const config = tree.getConfig();
82
- const dataRef = tree.getDataRef();
83
- (_a = (_c = dataRef.current).itemData) !== null && _a !== void 0 ? _a : (_c.itemData = {});
84
- (_b = (_d = dataRef.current).childrenIds) !== null && _b !== void 0 ? _b : (_d.childrenIds = {});
128
+ retrieveChildrenIds: ({ tree }, itemId, skipFetch = false) => {
129
+ const dataRef = getDataRef(tree);
85
130
  if (dataRef.current.childrenIds[itemId]) {
86
131
  return dataRef.current.childrenIds[itemId];
87
132
  }
88
- if (tree.getState().loadingItemChildrens.includes(itemId)) {
133
+ if (tree.getState().loadingItemChildrens.includes(itemId) || skipFetch) {
89
134
  return [];
90
135
  }
91
136
  tree.applySubStateUpdate("loadingItemChildrens", (loadingItemChildrens) => [...loadingItemChildrens, itemId]);
92
- (() => __awaiter(void 0, void 0, void 0, function* () {
93
- var _a, _b, _c, _d, _e;
94
- if ("getChildrenWithData" in config.dataLoader) {
95
- const children = yield config.dataLoader.getChildrenWithData(itemId);
96
- const childrenIds = children.map((c) => c.id);
97
- dataRef.current.childrenIds[itemId] = childrenIds;
98
- children.forEach(({ id, data }) => {
99
- var _a, _b, _c;
100
- dataRef.current.itemData[id] = data;
101
- (_a = config.onLoadedItem) === null || _a === void 0 ? void 0 : _a.call(config, id, data);
102
- (_b = dataRef.current.awaitingItemDataLoading) === null || _b === void 0 ? void 0 : _b[id].forEach((cb) => cb());
103
- (_c = dataRef.current.awaitingItemDataLoading) === null || _c === void 0 ? true : delete _c[id];
104
- });
105
- (_a = config.onLoadedChildren) === null || _a === void 0 ? void 0 : _a.call(config, itemId, childrenIds);
106
- tree.rebuildTree();
107
- tree.applySubStateUpdate("loadingItemData", (loadingItemData) => loadingItemData.filter((id) => !childrenIds.includes(id)));
108
- }
109
- else {
110
- const childrenIds = yield config.dataLoader.getChildren(itemId);
111
- dataRef.current.childrenIds[itemId] = childrenIds;
112
- (_b = config.onLoadedChildren) === null || _b === void 0 ? void 0 : _b.call(config, itemId, childrenIds);
113
- tree.rebuildTree();
114
- }
115
- tree.applySubStateUpdate("loadingItemChildrens", (loadingItemChildrens) => loadingItemChildrens.filter((id) => id !== itemId));
116
- (_d = (_c = dataRef.current.awaitingItemChildrensLoading) === null || _c === void 0 ? void 0 : _c[itemId]) === null || _d === void 0 ? void 0 : _d.forEach((cb) => cb());
117
- (_e = dataRef.current.awaitingItemChildrensLoading) === null || _e === void 0 ? true : delete _e[itemId];
118
- }))();
137
+ loadChildrenIds(tree, itemId);
119
138
  return [];
120
139
  },
121
140
  },
122
141
  itemInstance: {
123
142
  isLoading: ({ tree, item }) => tree.getState().loadingItemData.includes(item.getItemMeta().itemId) ||
124
143
  tree.getState().loadingItemChildrens.includes(item.getItemMeta().itemId),
125
- invalidateItemData: ({ tree, itemId }) => {
126
- var _a;
127
- const dataRef = tree.getDataRef();
128
- (_a = dataRef.current.itemData) === null || _a === void 0 ? true : delete _a[itemId];
129
- tree.retrieveItemData(itemId);
130
- },
131
- invalidateChildrenIds: ({ tree, itemId }) => {
132
- var _a;
133
- const dataRef = tree.getDataRef();
134
- (_a = dataRef.current.childrenIds) === null || _a === void 0 ? true : delete _a[itemId];
135
- tree.retrieveChildrenIds(itemId);
136
- },
144
+ invalidateItemData: (_a, optimistic_1) => __awaiter(void 0, [_a, optimistic_1], void 0, function* ({ tree, itemId }, optimistic) {
145
+ var _b;
146
+ if (!optimistic) {
147
+ (_b = getDataRef(tree).current.itemData) === null || _b === void 0 ? true : delete _b[itemId];
148
+ tree.applySubStateUpdate("loadingItemData", (loadingItemData) => [
149
+ ...loadingItemData,
150
+ itemId,
151
+ ]);
152
+ }
153
+ yield loadItemData(tree, itemId);
154
+ }),
155
+ invalidateChildrenIds: (_a, optimistic_1) => __awaiter(void 0, [_a, optimistic_1], void 0, function* ({ tree, itemId }, optimistic) {
156
+ var _b;
157
+ if (!optimistic) {
158
+ (_b = getDataRef(tree).current.childrenIds) === null || _b === void 0 ? true : delete _b[itemId];
159
+ tree.applySubStateUpdate("loadingItemChildrens", (loadingItemChildrens) => [...loadingItemChildrens, itemId]);
160
+ }
161
+ yield loadChildrenIds(tree, itemId);
162
+ }),
137
163
  updateCachedChildrenIds: ({ tree, itemId }, childrenIds) => {
138
164
  const dataRef = tree.getDataRef();
139
165
  dataRef.current.childrenIds[itemId] = childrenIds;
@@ -27,13 +27,22 @@ export type AsyncDataLoaderFeatureDef<T> = {
27
27
  onLoadedChildren?: (itemId: string, childrenIds: string[]) => void;
28
28
  };
29
29
  treeInstance: SyncDataLoaderFeatureDef<T>["treeInstance"] & {
30
+ /** @deprecated use loadItemData instead */
30
31
  waitForItemDataLoaded: (itemId: string) => Promise<void>;
32
+ /** @deprecated use loadChildrenIds instead */
31
33
  waitForItemChildrenLoaded: (itemId: string) => Promise<void>;
34
+ loadItemData: (itemId: string) => Promise<T>;
35
+ loadChildrenIds: (itemId: string) => Promise<string[]>;
32
36
  };
33
37
  itemInstance: SyncDataLoaderFeatureDef<T>["itemInstance"] & {
34
- /** Invalidate fetched data for item, and triggers a refetch and subsequent rerender if the item is visible */
35
- invalidateItemData: () => void;
36
- invalidateChildrenIds: () => void;
38
+ /** Invalidate fetched data for item, and triggers a refetch and subsequent rerender if the item is visible
39
+ * @param optimistic If true, the item will not trigger a state update on `loadingItemData`, and
40
+ * the tree will continue to display the old data until the new data has loaded. */
41
+ invalidateItemData: (optimistic?: boolean) => Promise<void>;
42
+ /** Invalidate fetched children ids for item, and triggers a refetch and subsequent rerender if the item is visible
43
+ * @param optimistic If true, the item will not trigger a state update on `loadingItemChildrens`, and
44
+ * the tree will continue to display the old data until the new data has loaded. */
45
+ invalidateChildrenIds: (optimistic?: boolean) => Promise<void>;
37
46
  updateCachedChildrenIds: (childrenIds: string[]) => void;
38
47
  isLoading: () => boolean;
39
48
  };
@@ -0,0 +1,2 @@
1
+ import { FeatureImplementation } from "../../types/core";
2
+ export declare const checkboxesFeature: FeatureImplementation;
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.checkboxesFeature = void 0;
13
+ const utils_1 = require("../../utils");
14
+ const types_1 = require("./types");
15
+ /*
16
+ * Cases for checking:
17
+ * - Check an unchecked item in an unchecked or indeterminate folder
18
+ * - Check an explicitly unchecked item in a checked folder
19
+ * - Check an unchecked folder in an unchecked or indeterminate folder
20
+ *
21
+ * Cases for unchecking:
22
+ * - Uncheck a checked item in an indeterminate folder
23
+ * - Uncheck an explicitly unchecked item in an checked folder
24
+ */
25
+ const fetchAllDescendants = (tree, itemId) => __awaiter(void 0, void 0, void 0, function* () {
26
+ const children = yield tree.loadChildrenIds(itemId);
27
+ return [
28
+ itemId,
29
+ ...(yield Promise.all(children.map((child) => fetchAllDescendants(tree, child)))).flat(),
30
+ ];
31
+ });
32
+ const getAllLoadedDescendants = (tree, itemId) => {
33
+ const children = tree.retrieveChildrenIds(itemId, true);
34
+ return [
35
+ itemId,
36
+ ...children.map((child) => getAllLoadedDescendants(tree, child)).flat(),
37
+ ];
38
+ };
39
+ exports.checkboxesFeature = {
40
+ key: "checkboxes",
41
+ overwrites: ["selection"],
42
+ getInitialState: (initialState) => (Object.assign({ checkedItems: [] }, initialState)),
43
+ getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setCheckedItems: (0, utils_1.makeStateUpdater)("checkedItems", tree) }, defaultConfig)),
44
+ stateHandlerNames: {
45
+ checkedItems: "setCheckedItems",
46
+ },
47
+ treeInstance: {
48
+ setCheckedItems: ({ tree }, checkedItems) => {
49
+ tree.applySubStateUpdate("checkedItems", checkedItems);
50
+ },
51
+ },
52
+ itemInstance: {
53
+ getCheckboxProps: ({ item, itemId }) => {
54
+ const checkedState = item.getCheckedState();
55
+ // console.log("prop", itemId, checkedState);
56
+ return {
57
+ onChange: item.toggleCheckedState,
58
+ checked: checkedState === types_1.CheckedState.Checked,
59
+ ref: (r) => {
60
+ if (r) {
61
+ // console.log("ref", itemId, checkedState);
62
+ r.indeterminate = checkedState === types_1.CheckedState.Indeterminate;
63
+ }
64
+ },
65
+ };
66
+ },
67
+ toggleCheckedState: (_a) => __awaiter(void 0, [_a], void 0, function* ({ item }) {
68
+ if (item.getCheckedState() === types_1.CheckedState.Checked) {
69
+ yield item.setUnchecked();
70
+ }
71
+ else {
72
+ yield item.setChecked();
73
+ }
74
+ }),
75
+ getCheckedState: ({ item, tree, itemId }) => {
76
+ // TODO checkedcache
77
+ const { checkedItems } = tree.getState();
78
+ if (checkedItems.includes(itemId)) {
79
+ return types_1.CheckedState.Checked;
80
+ }
81
+ if (item.isFolder()) {
82
+ const descendants = getAllLoadedDescendants(tree, itemId);
83
+ console.log("descendants of ", itemId, descendants);
84
+ if (descendants.every((d) => checkedItems.includes(d))) {
85
+ return types_1.CheckedState.Checked;
86
+ }
87
+ if (descendants.some((d) => checkedItems.includes(d))) {
88
+ return types_1.CheckedState.Indeterminate;
89
+ }
90
+ }
91
+ // if (
92
+ // item.isFolder() &&
93
+ // checkedItems.some((checkedItem) =>
94
+ // tree.getItemInstance(checkedItem)?.isDescendentOf(itemId),
95
+ // )
96
+ // ) {
97
+ // // TODO for every descendent, not every checked item
98
+ // return checkedItems.every((checkedItem) =>
99
+ // tree.getItemInstance(checkedItem)?.isDescendentOf(itemId),
100
+ // )
101
+ // ? CheckedState.Checked
102
+ // : CheckedState.Indeterminate;
103
+ // }
104
+ return types_1.CheckedState.Unchecked;
105
+ },
106
+ setChecked: (_a) => __awaiter(void 0, [_a], void 0, function* ({ item, tree, itemId }) {
107
+ if (!item.isFolder() || tree.getConfig().canCheckFolders) {
108
+ tree.applySubStateUpdate("checkedItems", (items) => [...items, itemId]);
109
+ }
110
+ else {
111
+ const descendants = yield fetchAllDescendants(tree, itemId);
112
+ tree.applySubStateUpdate("checkedItems", (items) => [
113
+ ...items,
114
+ ...descendants,
115
+ ]);
116
+ }
117
+ }),
118
+ setUnchecked: (_a) => __awaiter(void 0, [_a], void 0, function* ({ item, tree, itemId }) {
119
+ if (!item.isFolder() || tree.getConfig().canCheckFolders) {
120
+ tree.applySubStateUpdate("checkedItems", (items) => items.filter((id) => id !== itemId));
121
+ }
122
+ else {
123
+ yield tree.loadChildrenIds(itemId);
124
+ item.getChildren().forEach((item) => item.setUnchecked());
125
+ }
126
+ }),
127
+ },
128
+ };
@@ -0,0 +1,26 @@
1
+ import { SetStateFn } from "../../types/core";
2
+ export declare enum CheckedState {
3
+ Checked = "checked",
4
+ Unchecked = "unchecked",
5
+ Indeterminate = "indeterminate"
6
+ }
7
+ export type CheckboxesFeatureDef<T> = {
8
+ state: {
9
+ checkedItems: string[];
10
+ };
11
+ config: {
12
+ setCheckedItems?: SetStateFn<string[]>;
13
+ canCheckFolders?: boolean;
14
+ };
15
+ treeInstance: {
16
+ setCheckedItems: (checkedItems: string[]) => void;
17
+ };
18
+ itemInstance: {
19
+ setChecked: () => Promise<void>;
20
+ setUnchecked: () => Promise<void>;
21
+ toggleCheckedState: () => Promise<void>;
22
+ getCheckedState: () => CheckedState;
23
+ getCheckboxProps: () => Record<string, any>;
24
+ };
25
+ hotkeys: never;
26
+ };
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CheckedState = void 0;
4
+ var CheckedState;
5
+ (function (CheckedState) {
6
+ CheckedState["Checked"] = "checked";
7
+ CheckedState["Unchecked"] = "unchecked";
8
+ CheckedState["Indeterminate"] = "indeterminate";
9
+ })(CheckedState || (exports.CheckedState = CheckedState = {}));
@@ -17,6 +17,8 @@ export type MainFeatureDef<T = any> = {
17
17
  treeInstance: {
18
18
  /** @internal */
19
19
  applySubStateUpdate: <K extends keyof TreeState<any>>(stateName: K, updater: Updater<TreeState<T>[K]>) => void;
20
+ /** @internal */
21
+ buildItemInstance: (itemId: string) => ItemInstance<T>;
20
22
  setState: SetStateFn<TreeState<T>>;
21
23
  getState: () => TreeState<T>;
22
24
  setConfig: SetStateFn<TreeConfig<T>>;
@@ -40,6 +40,8 @@ exports.syncDataLoaderFeature = {
40
40
  }
41
41
  return unpromise(dataLoader.getChildrenWithData(itemId)).map((c) => c.data);
42
42
  },
43
+ loadItemData: ({ tree }, itemId) => tree.retrieveItemData(itemId),
44
+ loadChildrenIds: ({ tree }, itemId) => tree.retrieveChildrenIds(itemId),
43
45
  },
44
46
  itemInstance: {
45
47
  isLoading: () => false,
@@ -18,8 +18,8 @@ export type SyncDataLoaderFeatureDef<T> = {
18
18
  dataLoader: TreeDataLoader<T>;
19
19
  };
20
20
  treeInstance: {
21
- retrieveItemData: (itemId: string) => T;
22
- retrieveChildrenIds: (itemId: string) => string[];
21
+ retrieveItemData: (itemId: string, skipFetch?: boolean) => T;
22
+ retrieveChildrenIds: (itemId: string, skipFetch?: boolean) => string[];
23
23
  };
24
24
  itemInstance: {
25
25
  isLoading: () => boolean;
@@ -5,6 +5,7 @@ export { MainFeatureDef, InstanceBuilder } from "./features/main/types";
5
5
  export * from "./features/drag-and-drop/types";
6
6
  export * from "./features/keyboard-drag-and-drop/types";
7
7
  export * from "./features/selection/types";
8
+ export * from "./features/checkboxes/types";
8
9
  export * from "./features/async-data-loader/types";
9
10
  export * from "./features/sync-data-loader/types";
10
11
  export * from "./features/hotkeys-core/types";
@@ -13,6 +14,7 @@ export * from "./features/renaming/types";
13
14
  export * from "./features/expand-all/types";
14
15
  export * from "./features/prop-memoization/types";
15
16
  export * from "./features/selection/feature";
17
+ export * from "./features/checkboxes/feature";
16
18
  export * from "./features/hotkeys-core/feature";
17
19
  export * from "./features/async-data-loader/feature";
18
20
  export * from "./features/sync-data-loader/feature";
package/lib/cjs/index.js CHANGED
@@ -20,6 +20,7 @@ __exportStar(require("./features/tree/types"), exports);
20
20
  __exportStar(require("./features/drag-and-drop/types"), exports);
21
21
  __exportStar(require("./features/keyboard-drag-and-drop/types"), exports);
22
22
  __exportStar(require("./features/selection/types"), exports);
23
+ __exportStar(require("./features/checkboxes/types"), exports);
23
24
  __exportStar(require("./features/async-data-loader/types"), exports);
24
25
  __exportStar(require("./features/sync-data-loader/types"), exports);
25
26
  __exportStar(require("./features/hotkeys-core/types"), exports);
@@ -28,6 +29,7 @@ __exportStar(require("./features/renaming/types"), exports);
28
29
  __exportStar(require("./features/expand-all/types"), exports);
29
30
  __exportStar(require("./features/prop-memoization/types"), exports);
30
31
  __exportStar(require("./features/selection/feature"), exports);
32
+ __exportStar(require("./features/checkboxes/feature"), exports);
31
33
  __exportStar(require("./features/hotkeys-core/feature"), exports);
32
34
  __exportStar(require("./features/async-data-loader/feature"), exports);
33
35
  __exportStar(require("./features/sync-data-loader/feature"), exports);
@@ -10,6 +10,7 @@ import { RenamingFeatureDef } from "../features/renaming/types";
10
10
  import { ExpandAllFeatureDef } from "../features/expand-all/types";
11
11
  import { PropMemoizationFeatureDef } from "../features/prop-memoization/types";
12
12
  import { KeyboardDragAndDropFeatureDef } from "../features/keyboard-drag-and-drop/types";
13
+ import { CheckboxesFeatureDef } from "../features/checkboxes/types";
13
14
  export type Updater<T> = T | ((old: T) => T);
14
15
  export type SetStateFn<T> = (updaterOrValue: Updater<T>) => void;
15
16
  export type FeatureDef = {
@@ -34,7 +35,7 @@ type MergedFeatures<F extends FeatureDef> = {
34
35
  itemInstance: UnionToIntersection<F["itemInstance"]>;
35
36
  hotkeys: F["hotkeys"];
36
37
  };
37
- export type RegisteredFeatures<T> = MainFeatureDef<T> | TreeFeatureDef<T> | SelectionFeatureDef<T> | DragAndDropFeatureDef<T> | KeyboardDragAndDropFeatureDef<T> | HotkeysCoreFeatureDef<T> | SyncDataLoaderFeatureDef<T> | AsyncDataLoaderFeatureDef<T> | SearchFeatureDef<T> | RenamingFeatureDef<T> | ExpandAllFeatureDef | PropMemoizationFeatureDef;
38
+ export type RegisteredFeatures<T> = MainFeatureDef<T> | TreeFeatureDef<T> | SelectionFeatureDef<T> | CheckboxesFeatureDef<T> | DragAndDropFeatureDef<T> | KeyboardDragAndDropFeatureDef<T> | HotkeysCoreFeatureDef<T> | SyncDataLoaderFeatureDef<T> | AsyncDataLoaderFeatureDef<T> | SearchFeatureDef<T> | RenamingFeatureDef<T> | ExpandAllFeatureDef | PropMemoizationFeatureDef;
38
39
  type TreeStateType<T> = MergedFeatures<RegisteredFeatures<T>>["state"];
39
40
  export interface TreeState<T> extends TreeStateType<T> {
40
41
  }
@@ -97,6 +97,15 @@ export const createTree = (initialConfig) => {
97
97
  const externalStateSetter = config[stateHandlerNames[stateName]];
98
98
  externalStateSetter === null || externalStateSetter === void 0 ? void 0 : externalStateSetter(state[stateName]);
99
99
  },
100
+ buildItemInstance: ({}, itemId) => {
101
+ const [instance, finalizeInstance] = buildInstance(features, "itemInstance", (instance) => ({
102
+ item: instance,
103
+ tree: treeInstance,
104
+ itemId,
105
+ }));
106
+ finalizeInstance();
107
+ return instance;
108
+ },
100
109
  // TODO rebuildSubTree: (itemId: string) => void;
101
110
  rebuildTree: () => {
102
111
  var _a;