@headless-tree/core 0.0.0-20250511185653 → 0.0.0-20250511194715

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 (49) hide show
  1. package/CHANGELOG.md +2 -1
  2. package/lib/cjs/core/create-tree.js +0 -9
  3. package/lib/cjs/features/async-data-loader/feature.js +20 -41
  4. package/lib/cjs/features/async-data-loader/types.d.ts +4 -4
  5. package/lib/cjs/features/expand-all/feature.js +2 -2
  6. package/lib/cjs/features/hotkeys-core/feature.js +27 -13
  7. package/lib/cjs/features/keyboard-drag-and-drop/feature.js +1 -1
  8. package/lib/cjs/features/main/types.d.ts +0 -2
  9. package/lib/cjs/features/selection/feature.js +1 -1
  10. package/lib/cjs/features/sync-data-loader/feature.js +2 -0
  11. package/lib/cjs/index.d.ts +0 -2
  12. package/lib/cjs/index.js +0 -2
  13. package/lib/cjs/types/core.d.ts +1 -2
  14. package/lib/esm/core/create-tree.js +0 -9
  15. package/lib/esm/features/async-data-loader/feature.js +20 -41
  16. package/lib/esm/features/async-data-loader/types.d.ts +4 -4
  17. package/lib/esm/features/expand-all/feature.js +2 -2
  18. package/lib/esm/features/hotkeys-core/feature.js +27 -13
  19. package/lib/esm/features/keyboard-drag-and-drop/feature.js +1 -1
  20. package/lib/esm/features/main/types.d.ts +0 -2
  21. package/lib/esm/features/selection/feature.js +1 -1
  22. package/lib/esm/features/sync-data-loader/feature.js +2 -0
  23. package/lib/esm/index.d.ts +0 -2
  24. package/lib/esm/index.js +0 -2
  25. package/lib/esm/types/core.d.ts +1 -2
  26. package/package.json +1 -1
  27. package/src/core/create-tree.ts +0 -13
  28. package/src/features/async-data-loader/feature.ts +24 -35
  29. package/src/features/async-data-loader/types.ts +4 -4
  30. package/src/features/expand-all/feature.ts +2 -2
  31. package/src/features/hotkeys-core/feature.ts +33 -15
  32. package/src/features/keyboard-drag-and-drop/feature.ts +1 -1
  33. package/src/features/main/types.ts +0 -2
  34. package/src/features/selection/feature.ts +1 -1
  35. package/src/features/sync-data-loader/feature.ts +3 -0
  36. package/src/features/tree/types.ts +1 -1
  37. package/src/index.ts +0 -2
  38. package/src/types/core.ts +0 -2
  39. package/lib/cjs/features/checkboxes/feature.d.ts +0 -2
  40. package/lib/cjs/features/checkboxes/feature.js +0 -94
  41. package/lib/cjs/features/checkboxes/types.d.ts +0 -26
  42. package/lib/cjs/features/checkboxes/types.js +0 -9
  43. package/lib/esm/features/checkboxes/feature.d.ts +0 -2
  44. package/lib/esm/features/checkboxes/feature.js +0 -91
  45. package/lib/esm/features/checkboxes/types.d.ts +0 -26
  46. package/lib/esm/features/checkboxes/types.js +0 -6
  47. package/src/features/checkboxes/checkboxes.spec.ts +0 -134
  48. package/src/features/checkboxes/feature.ts +0 -119
  49. package/src/features/checkboxes/types.ts +0 -28
package/CHANGELOG.md CHANGED
@@ -1,10 +1,11 @@
1
1
  # @headless-tree/core
2
2
 
3
- ## 0.0.0-20250511185653
3
+ ## 0.0.0-20250511194715
4
4
 
5
5
  ### Minor Changes
6
6
 
7
7
  - 64d8e2a: add getChildrenWithData method to data loader to support fetching all children of an item at once
8
+ - 35260e3: fixed hotkey issues where releasing modifier keys (like shift) before normal keys can cause issues with subsequent keydown events
8
9
 
9
10
  ### Patch Changes
10
11
 
@@ -100,15 +100,6 @@ 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
- },
112
103
  // TODO rebuildSubTree: (itemId: string) => void;
113
104
  rebuildTree: () => {
114
105
  var _a;
@@ -20,44 +20,41 @@ const getDataRef = (tree) => {
20
20
  return dataRef;
21
21
  };
22
22
  const loadItemData = (tree, itemId) => __awaiter(void 0, void 0, void 0, function* () {
23
- var _a, _b, _c;
23
+ var _a;
24
24
  const config = tree.getConfig();
25
25
  const dataRef = getDataRef(tree);
26
26
  const item = yield config.dataLoader.getItem(itemId);
27
27
  dataRef.current.itemData[itemId] = item;
28
28
  (_a = config.onLoadedItem) === null || _a === void 0 ? void 0 : _a.call(config, itemId, item);
29
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];
30
+ return item;
32
31
  });
33
32
  const loadChildrenIds = (tree, itemId) => __awaiter(void 0, void 0, void 0, function* () {
34
- var _a, _b, _c, _d, _e;
33
+ var _a, _b;
35
34
  const config = tree.getConfig();
36
35
  const dataRef = getDataRef(tree);
36
+ let childrenIds;
37
37
  if ("getChildrenWithData" in config.dataLoader) {
38
38
  const children = yield config.dataLoader.getChildrenWithData(itemId);
39
- const childrenIds = children.map((c) => c.id);
39
+ childrenIds = children.map((c) => c.id);
40
40
  dataRef.current.childrenIds[itemId] = childrenIds;
41
41
  children.forEach(({ id, data }) => {
42
- var _a, _b, _c;
42
+ var _a;
43
43
  dataRef.current.itemData[id] = data;
44
44
  (_a = config.onLoadedItem) === null || _a === void 0 ? void 0 : _a.call(config, id, data);
45
- (_b = dataRef.current.awaitingItemDataLoading) === null || _b === void 0 ? void 0 : _b[id].forEach((cb) => cb());
46
- (_c = dataRef.current.awaitingItemDataLoading) === null || _c === void 0 ? true : delete _c[id];
47
45
  });
48
46
  (_a = config.onLoadedChildren) === null || _a === void 0 ? void 0 : _a.call(config, itemId, childrenIds);
49
47
  tree.rebuildTree();
50
48
  tree.applySubStateUpdate("loadingItemData", (loadingItemData) => loadingItemData.filter((id) => !childrenIds.includes(id)));
51
49
  }
52
50
  else {
53
- const childrenIds = yield config.dataLoader.getChildren(itemId);
51
+ childrenIds = yield config.dataLoader.getChildren(itemId);
54
52
  dataRef.current.childrenIds[itemId] = childrenIds;
55
53
  (_b = config.onLoadedChildren) === null || _b === void 0 ? void 0 : _b.call(config, itemId, childrenIds);
56
54
  tree.rebuildTree();
57
55
  }
58
56
  tree.applySubStateUpdate("loadingItemChildrens", (loadingItemChildrens) => loadingItemChildrens.filter((id) => id !== itemId));
59
- (_d = (_c = dataRef.current.awaitingItemChildrensLoading) === null || _c === void 0 ? void 0 : _c[itemId]) === null || _d === void 0 ? void 0 : _d.forEach((cb) => cb());
60
- (_e = dataRef.current.awaitingItemChildrensLoading) === null || _e === void 0 ? true : delete _e[itemId];
57
+ return childrenIds;
61
58
  });
62
59
  exports.asyncDataLoaderFeature = {
63
60
  key: "async-data-loader",
@@ -68,42 +65,24 @@ exports.asyncDataLoaderFeature = {
68
65
  loadingItemChildrens: "setLoadingItemChildrens",
69
66
  },
70
67
  treeInstance: {
71
- waitForItemDataLoaded: (_a, itemId_1) => __awaiter(void 0, [_a, itemId_1], void 0, function* ({ tree }, itemId) {
72
- tree.retrieveItemData(itemId);
73
- if (!tree.getState().loadingItemData.includes(itemId)) {
74
- return;
75
- }
76
- yield new Promise((resolve) => {
77
- var _a, _b;
78
- var _c, _d;
79
- const dataRef = tree.getDataRef();
80
- (_a = (_c = dataRef.current).awaitingItemDataLoading) !== null && _a !== void 0 ? _a : (_c.awaitingItemDataLoading = {});
81
- (_b = (_d = dataRef.current.awaitingItemDataLoading)[itemId]) !== null && _b !== void 0 ? _b : (_d[itemId] = []);
82
- dataRef.current.awaitingItemDataLoading[itemId].push(resolve);
83
- });
68
+ waitForItemDataLoaded: ({ tree }, itemId) => tree.loadItemData(itemId),
69
+ waitForItemChildrenLoaded: ({ tree }, itemId) => tree.loadChildrenIds(itemId),
70
+ loadItemData: (_a, itemId_1) => __awaiter(void 0, [_a, itemId_1], void 0, function* ({ tree }, itemId) {
71
+ var _b;
72
+ return ((_b = getDataRef(tree).current.itemData[itemId]) !== null && _b !== void 0 ? _b : (yield loadItemData(tree, itemId)));
84
73
  }),
85
- waitForItemChildrenLoaded: (_a, itemId_1) => __awaiter(void 0, [_a, itemId_1], void 0, function* ({ tree }, itemId) {
86
- tree.retrieveChildrenIds(itemId);
87
- if (!tree.getState().loadingItemChildrens.includes(itemId)) {
88
- return;
89
- }
90
- yield new Promise((resolve) => {
91
- var _a, _b;
92
- var _c, _d;
93
- const dataRef = tree.getDataRef();
94
- (_a = (_c = dataRef.current).awaitingItemChildrensLoading) !== null && _a !== void 0 ? _a : (_c.awaitingItemChildrensLoading = {});
95
- (_b = (_d = dataRef.current.awaitingItemChildrensLoading)[itemId]) !== null && _b !== void 0 ? _b : (_d[itemId] = []);
96
- dataRef.current.awaitingItemChildrensLoading[itemId].push(resolve);
97
- });
74
+ loadChildrenIds: (_a, itemId_1) => __awaiter(void 0, [_a, itemId_1], void 0, function* ({ tree }, itemId) {
75
+ var _b;
76
+ return ((_b = getDataRef(tree).current.childrenIds[itemId]) !== null && _b !== void 0 ? _b : (yield loadChildrenIds(tree, itemId)));
98
77
  }),
99
- retrieveItemData: ({ tree }, itemId, skipFetch = false) => {
78
+ retrieveItemData: ({ tree }, itemId) => {
100
79
  var _a, _b;
101
80
  const config = tree.getConfig();
102
81
  const dataRef = getDataRef(tree);
103
82
  if (dataRef.current.itemData[itemId]) {
104
83
  return dataRef.current.itemData[itemId];
105
84
  }
106
- if (!tree.getState().loadingItemData.includes(itemId) && !skipFetch) {
85
+ if (!tree.getState().loadingItemData.includes(itemId)) {
107
86
  tree.applySubStateUpdate("loadingItemData", (loadingItemData) => [
108
87
  ...loadingItemData,
109
88
  itemId,
@@ -112,12 +91,12 @@ exports.asyncDataLoaderFeature = {
112
91
  }
113
92
  return (_b = (_a = config.createLoadingItemData) === null || _a === void 0 ? void 0 : _a.call(config)) !== null && _b !== void 0 ? _b : null;
114
93
  },
115
- retrieveChildrenIds: ({ tree }, itemId, skipFetch = false) => {
94
+ retrieveChildrenIds: ({ tree }, itemId) => {
116
95
  const dataRef = getDataRef(tree);
117
96
  if (dataRef.current.childrenIds[itemId]) {
118
97
  return dataRef.current.childrenIds[itemId];
119
98
  }
120
- if (tree.getState().loadingItemChildrens.includes(itemId) || skipFetch) {
99
+ if (tree.getState().loadingItemChildrens.includes(itemId)) {
121
100
  return [];
122
101
  }
123
102
  tree.applySubStateUpdate("loadingItemChildrens", (loadingItemChildrens) => [...loadingItemChildrens, itemId]);
@@ -1,11 +1,8 @@
1
1
  import { SetStateFn } from "../../types/core";
2
2
  import { SyncDataLoaderFeatureDef } from "../sync-data-loader/types";
3
- type AwaitingLoaderCallbacks = Record<string, (() => void)[]>;
4
3
  export interface AsyncDataLoaderDataRef<T = any> {
5
4
  itemData: Record<string, T>;
6
5
  childrenIds: Record<string, string[]>;
7
- awaitingItemDataLoading: AwaitingLoaderCallbacks;
8
- awaitingItemChildrensLoading: AwaitingLoaderCallbacks;
9
6
  }
10
7
  /**
11
8
  * @category Async Data Loader/General
@@ -27,8 +24,12 @@ export type AsyncDataLoaderFeatureDef<T> = {
27
24
  onLoadedChildren?: (itemId: string, childrenIds: string[]) => void;
28
25
  };
29
26
  treeInstance: SyncDataLoaderFeatureDef<T>["treeInstance"] & {
27
+ /** @deprecated use loadItemData instead */
30
28
  waitForItemDataLoaded: (itemId: string) => Promise<void>;
29
+ /** @deprecated use loadChildrenIds instead */
31
30
  waitForItemChildrenLoaded: (itemId: string) => Promise<void>;
31
+ loadItemData: (itemId: string) => Promise<T>;
32
+ loadChildrenIds: (itemId: string) => Promise<string[]>;
32
33
  };
33
34
  itemInstance: SyncDataLoaderFeatureDef<T>["itemInstance"] & {
34
35
  /** Invalidate fetched data for item, and triggers a refetch and subsequent rerender if the item is visible
@@ -44,4 +45,3 @@ export type AsyncDataLoaderFeatureDef<T> = {
44
45
  };
45
46
  hotkeys: SyncDataLoaderFeatureDef<T>["hotkeys"];
46
47
  };
47
- export {};
@@ -51,7 +51,7 @@ exports.expandAllFeature = {
51
51
  handler: (_, tree) => __awaiter(void 0, void 0, void 0, function* () {
52
52
  const cancelToken = { current: false };
53
53
  const cancelHandler = (e) => {
54
- if (e.key === "Escape") {
54
+ if (e.code === "Escape") {
55
55
  cancelToken.current = true;
56
56
  }
57
57
  };
@@ -61,7 +61,7 @@ exports.expandAllFeature = {
61
61
  }),
62
62
  },
63
63
  collapseSelected: {
64
- hotkey: "Control+Shift+-",
64
+ hotkey: "Control+Shift+Minus",
65
65
  handler: (_, tree) => {
66
66
  tree.getSelectedItems().forEach((item) => item.collapseAll());
67
67
  },
@@ -2,16 +2,31 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.hotkeysCoreFeature = void 0;
4
4
  const specialKeys = {
5
- Letter: /^[a-z]$/,
6
- LetterOrNumber: /^[a-z0-9]$/,
7
- Plus: /^\+$/,
8
- Space: /^ $/,
5
+ // TODO:breaking deprecate auto-lowercase
6
+ letter: /^Key[A-Z]$/,
7
+ letterornumber: /^(Key[A-Z]|Digit[0-9])$/,
8
+ plus: /^(NumpadAdd|Plus)$/,
9
+ minus: /^(NumpadSubtract|Minus)$/,
10
+ control: /^(ControlLeft|ControlRight)$/,
11
+ shift: /^(ShiftLeft|ShiftRight)$/,
9
12
  };
10
13
  const testHotkeyMatch = (pressedKeys, tree, hotkey) => {
11
- const supposedKeys = hotkey.hotkey.toLowerCase().split("+");
12
- const doKeysMatch = supposedKeys.every((key) => key in specialKeys
13
- ? [...pressedKeys].some((pressedKey) => specialKeys[key].test(pressedKey))
14
- : pressedKeys.has(key));
14
+ const supposedKeys = hotkey.hotkey.toLowerCase().split("+"); // TODO:breaking deprecate auto-lowercase
15
+ const doKeysMatch = supposedKeys.every((key) => {
16
+ if (key in specialKeys) {
17
+ return [...pressedKeys].some((pressedKey) => specialKeys[key].test(pressedKey));
18
+ }
19
+ const pressedKeysLowerCase = [...pressedKeys] // TODO:breaking deprecate auto-lowercase
20
+ .map((k) => k.toLowerCase());
21
+ if (pressedKeysLowerCase.includes(key.toLowerCase())) {
22
+ return true;
23
+ }
24
+ if (pressedKeysLowerCase.includes(`key${key.toLowerCase()}`)) {
25
+ // TODO:breaking deprecate e.key character matching
26
+ return true;
27
+ }
28
+ return false;
29
+ });
15
30
  const isEnabled = !hotkey.isEnabled || hotkey.isEnabled(tree);
16
31
  const equalCounts = pressedKeys.size === supposedKeys.length;
17
32
  return doKeysMatch && isEnabled && equalCounts;
@@ -31,15 +46,14 @@ exports.hotkeysCoreFeature = {
31
46
  if (e.target instanceof HTMLInputElement && ignoreHotkeysOnInputs) {
32
47
  return;
33
48
  }
34
- const key = e.key.toLowerCase();
35
49
  (_a = (_b = data.current).pressedKeys) !== null && _a !== void 0 ? _a : (_b.pressedKeys = new Set());
36
- const newMatch = !data.current.pressedKeys.has(key);
37
- data.current.pressedKeys.add(key);
50
+ const newMatch = !data.current.pressedKeys.has(e.code);
51
+ data.current.pressedKeys.add(e.code);
38
52
  const hotkeyName = findHotkeyMatch(data.current.pressedKeys, tree, tree.getHotkeyPresets(), hotkeys);
39
53
  if (e.target instanceof HTMLInputElement) {
40
54
  // JS respects composite keydowns while input elements are focused, and
41
55
  // doesnt send the associated keyup events with the same key name
42
- data.current.pressedKeys.delete(key);
56
+ data.current.pressedKeys.delete(e.code);
43
57
  }
44
58
  if (!hotkeyName)
45
59
  return;
@@ -60,7 +74,7 @@ exports.hotkeysCoreFeature = {
60
74
  var _a;
61
75
  var _b;
62
76
  (_a = (_b = data.current).pressedKeys) !== null && _a !== void 0 ? _a : (_b.pressedKeys = new Set());
63
- data.current.pressedKeys.delete(e.key.toLowerCase());
77
+ data.current.pressedKeys.delete(e.code);
64
78
  };
65
79
  const reset = () => {
66
80
  data.current.pressedKeys = new Set();
@@ -144,7 +144,7 @@ exports.keyboardDragAndDropFeature = {
144
144
  },
145
145
  hotkeys: {
146
146
  startDrag: {
147
- hotkey: "Control+Shift+D",
147
+ hotkey: "Control+Shift+KeyD",
148
148
  preventDefault: true,
149
149
  isEnabled: (tree) => !tree.getState().dnd,
150
150
  handler: (_, tree) => {
@@ -17,8 +17,6 @@ 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>;
22
20
  setState: SetStateFn<TreeState<T>>;
23
21
  getState: () => TreeState<T>;
24
22
  setConfig: SetStateFn<TreeConfig<T>>;
@@ -122,7 +122,7 @@ exports.selectionFeature = {
122
122
  },
123
123
  },
124
124
  selectAll: {
125
- hotkey: "Control+a",
125
+ hotkey: "Control+KeyA",
126
126
  preventDefault: true,
127
127
  handler: (e, tree) => {
128
128
  tree.setSelectedItems(tree.getItems().map((item) => item.getId()));
@@ -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,
@@ -5,7 +5,6 @@ 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";
9
8
  export * from "./features/async-data-loader/types";
10
9
  export * from "./features/sync-data-loader/types";
11
10
  export * from "./features/hotkeys-core/types";
@@ -14,7 +13,6 @@ export * from "./features/renaming/types";
14
13
  export * from "./features/expand-all/types";
15
14
  export * from "./features/prop-memoization/types";
16
15
  export * from "./features/selection/feature";
17
- export * from "./features/checkboxes/feature";
18
16
  export * from "./features/hotkeys-core/feature";
19
17
  export * from "./features/async-data-loader/feature";
20
18
  export * from "./features/sync-data-loader/feature";
package/lib/cjs/index.js CHANGED
@@ -20,7 +20,6 @@ __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);
24
23
  __exportStar(require("./features/async-data-loader/types"), exports);
25
24
  __exportStar(require("./features/sync-data-loader/types"), exports);
26
25
  __exportStar(require("./features/hotkeys-core/types"), exports);
@@ -29,7 +28,6 @@ __exportStar(require("./features/renaming/types"), exports);
29
28
  __exportStar(require("./features/expand-all/types"), exports);
30
29
  __exportStar(require("./features/prop-memoization/types"), exports);
31
30
  __exportStar(require("./features/selection/feature"), exports);
32
- __exportStar(require("./features/checkboxes/feature"), exports);
33
31
  __exportStar(require("./features/hotkeys-core/feature"), exports);
34
32
  __exportStar(require("./features/async-data-loader/feature"), exports);
35
33
  __exportStar(require("./features/sync-data-loader/feature"), exports);
@@ -10,7 +10,6 @@ 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";
14
13
  export type Updater<T> = T | ((old: T) => T);
15
14
  export type SetStateFn<T> = (updaterOrValue: Updater<T>) => void;
16
15
  export type FeatureDef = {
@@ -35,7 +34,7 @@ type MergedFeatures<F extends FeatureDef> = {
35
34
  itemInstance: UnionToIntersection<F["itemInstance"]>;
36
35
  hotkeys: F["hotkeys"];
37
36
  };
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;
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;
39
38
  type TreeStateType<T> = MergedFeatures<RegisteredFeatures<T>>["state"];
40
39
  export interface TreeState<T> extends TreeStateType<T> {
41
40
  }
@@ -97,15 +97,6 @@ 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
- },
109
100
  // TODO rebuildSubTree: (itemId: string) => void;
110
101
  rebuildTree: () => {
111
102
  var _a;
@@ -17,44 +17,41 @@ const getDataRef = (tree) => {
17
17
  return dataRef;
18
18
  };
19
19
  const loadItemData = (tree, itemId) => __awaiter(void 0, void 0, void 0, function* () {
20
- var _a, _b, _c;
20
+ var _a;
21
21
  const config = tree.getConfig();
22
22
  const dataRef = getDataRef(tree);
23
23
  const item = yield config.dataLoader.getItem(itemId);
24
24
  dataRef.current.itemData[itemId] = item;
25
25
  (_a = config.onLoadedItem) === null || _a === void 0 ? void 0 : _a.call(config, itemId, item);
26
26
  tree.applySubStateUpdate("loadingItemData", (loadingItemData) => loadingItemData.filter((id) => id !== itemId));
27
- (_b = dataRef.current.awaitingItemDataLoading) === null || _b === void 0 ? void 0 : _b[itemId].forEach((cb) => cb());
28
- (_c = dataRef.current.awaitingItemDataLoading) === null || _c === void 0 ? true : delete _c[itemId];
27
+ return item;
29
28
  });
30
29
  const loadChildrenIds = (tree, itemId) => __awaiter(void 0, void 0, void 0, function* () {
31
- var _a, _b, _c, _d, _e;
30
+ var _a, _b;
32
31
  const config = tree.getConfig();
33
32
  const dataRef = getDataRef(tree);
33
+ let childrenIds;
34
34
  if ("getChildrenWithData" in config.dataLoader) {
35
35
  const children = yield config.dataLoader.getChildrenWithData(itemId);
36
- const childrenIds = children.map((c) => c.id);
36
+ childrenIds = children.map((c) => c.id);
37
37
  dataRef.current.childrenIds[itemId] = childrenIds;
38
38
  children.forEach(({ id, data }) => {
39
- var _a, _b, _c;
39
+ var _a;
40
40
  dataRef.current.itemData[id] = data;
41
41
  (_a = config.onLoadedItem) === null || _a === void 0 ? void 0 : _a.call(config, id, data);
42
- (_b = dataRef.current.awaitingItemDataLoading) === null || _b === void 0 ? void 0 : _b[id].forEach((cb) => cb());
43
- (_c = dataRef.current.awaitingItemDataLoading) === null || _c === void 0 ? true : delete _c[id];
44
42
  });
45
43
  (_a = config.onLoadedChildren) === null || _a === void 0 ? void 0 : _a.call(config, itemId, childrenIds);
46
44
  tree.rebuildTree();
47
45
  tree.applySubStateUpdate("loadingItemData", (loadingItemData) => loadingItemData.filter((id) => !childrenIds.includes(id)));
48
46
  }
49
47
  else {
50
- const childrenIds = yield config.dataLoader.getChildren(itemId);
48
+ childrenIds = yield config.dataLoader.getChildren(itemId);
51
49
  dataRef.current.childrenIds[itemId] = childrenIds;
52
50
  (_b = config.onLoadedChildren) === null || _b === void 0 ? void 0 : _b.call(config, itemId, childrenIds);
53
51
  tree.rebuildTree();
54
52
  }
55
53
  tree.applySubStateUpdate("loadingItemChildrens", (loadingItemChildrens) => loadingItemChildrens.filter((id) => id !== itemId));
56
- (_d = (_c = dataRef.current.awaitingItemChildrensLoading) === null || _c === void 0 ? void 0 : _c[itemId]) === null || _d === void 0 ? void 0 : _d.forEach((cb) => cb());
57
- (_e = dataRef.current.awaitingItemChildrensLoading) === null || _e === void 0 ? true : delete _e[itemId];
54
+ return childrenIds;
58
55
  });
59
56
  export const asyncDataLoaderFeature = {
60
57
  key: "async-data-loader",
@@ -65,42 +62,24 @@ export const asyncDataLoaderFeature = {
65
62
  loadingItemChildrens: "setLoadingItemChildrens",
66
63
  },
67
64
  treeInstance: {
68
- waitForItemDataLoaded: (_a, itemId_1) => __awaiter(void 0, [_a, itemId_1], void 0, function* ({ tree }, itemId) {
69
- tree.retrieveItemData(itemId);
70
- if (!tree.getState().loadingItemData.includes(itemId)) {
71
- return;
72
- }
73
- yield new Promise((resolve) => {
74
- var _a, _b;
75
- var _c, _d;
76
- const dataRef = tree.getDataRef();
77
- (_a = (_c = dataRef.current).awaitingItemDataLoading) !== null && _a !== void 0 ? _a : (_c.awaitingItemDataLoading = {});
78
- (_b = (_d = dataRef.current.awaitingItemDataLoading)[itemId]) !== null && _b !== void 0 ? _b : (_d[itemId] = []);
79
- dataRef.current.awaitingItemDataLoading[itemId].push(resolve);
80
- });
65
+ waitForItemDataLoaded: ({ tree }, itemId) => tree.loadItemData(itemId),
66
+ waitForItemChildrenLoaded: ({ tree }, itemId) => tree.loadChildrenIds(itemId),
67
+ loadItemData: (_a, itemId_1) => __awaiter(void 0, [_a, itemId_1], void 0, function* ({ tree }, itemId) {
68
+ var _b;
69
+ return ((_b = getDataRef(tree).current.itemData[itemId]) !== null && _b !== void 0 ? _b : (yield loadItemData(tree, itemId)));
81
70
  }),
82
- waitForItemChildrenLoaded: (_a, itemId_1) => __awaiter(void 0, [_a, itemId_1], void 0, function* ({ tree }, itemId) {
83
- tree.retrieveChildrenIds(itemId);
84
- if (!tree.getState().loadingItemChildrens.includes(itemId)) {
85
- return;
86
- }
87
- yield new Promise((resolve) => {
88
- var _a, _b;
89
- var _c, _d;
90
- const dataRef = tree.getDataRef();
91
- (_a = (_c = dataRef.current).awaitingItemChildrensLoading) !== null && _a !== void 0 ? _a : (_c.awaitingItemChildrensLoading = {});
92
- (_b = (_d = dataRef.current.awaitingItemChildrensLoading)[itemId]) !== null && _b !== void 0 ? _b : (_d[itemId] = []);
93
- dataRef.current.awaitingItemChildrensLoading[itemId].push(resolve);
94
- });
71
+ loadChildrenIds: (_a, itemId_1) => __awaiter(void 0, [_a, itemId_1], void 0, function* ({ tree }, itemId) {
72
+ var _b;
73
+ return ((_b = getDataRef(tree).current.childrenIds[itemId]) !== null && _b !== void 0 ? _b : (yield loadChildrenIds(tree, itemId)));
95
74
  }),
96
- retrieveItemData: ({ tree }, itemId, skipFetch = false) => {
75
+ retrieveItemData: ({ tree }, itemId) => {
97
76
  var _a, _b;
98
77
  const config = tree.getConfig();
99
78
  const dataRef = getDataRef(tree);
100
79
  if (dataRef.current.itemData[itemId]) {
101
80
  return dataRef.current.itemData[itemId];
102
81
  }
103
- if (!tree.getState().loadingItemData.includes(itemId) && !skipFetch) {
82
+ if (!tree.getState().loadingItemData.includes(itemId)) {
104
83
  tree.applySubStateUpdate("loadingItemData", (loadingItemData) => [
105
84
  ...loadingItemData,
106
85
  itemId,
@@ -109,12 +88,12 @@ export const asyncDataLoaderFeature = {
109
88
  }
110
89
  return (_b = (_a = config.createLoadingItemData) === null || _a === void 0 ? void 0 : _a.call(config)) !== null && _b !== void 0 ? _b : null;
111
90
  },
112
- retrieveChildrenIds: ({ tree }, itemId, skipFetch = false) => {
91
+ retrieveChildrenIds: ({ tree }, itemId) => {
113
92
  const dataRef = getDataRef(tree);
114
93
  if (dataRef.current.childrenIds[itemId]) {
115
94
  return dataRef.current.childrenIds[itemId];
116
95
  }
117
- if (tree.getState().loadingItemChildrens.includes(itemId) || skipFetch) {
96
+ if (tree.getState().loadingItemChildrens.includes(itemId)) {
118
97
  return [];
119
98
  }
120
99
  tree.applySubStateUpdate("loadingItemChildrens", (loadingItemChildrens) => [...loadingItemChildrens, itemId]);
@@ -1,11 +1,8 @@
1
1
  import { SetStateFn } from "../../types/core";
2
2
  import { SyncDataLoaderFeatureDef } from "../sync-data-loader/types";
3
- type AwaitingLoaderCallbacks = Record<string, (() => void)[]>;
4
3
  export interface AsyncDataLoaderDataRef<T = any> {
5
4
  itemData: Record<string, T>;
6
5
  childrenIds: Record<string, string[]>;
7
- awaitingItemDataLoading: AwaitingLoaderCallbacks;
8
- awaitingItemChildrensLoading: AwaitingLoaderCallbacks;
9
6
  }
10
7
  /**
11
8
  * @category Async Data Loader/General
@@ -27,8 +24,12 @@ export type AsyncDataLoaderFeatureDef<T> = {
27
24
  onLoadedChildren?: (itemId: string, childrenIds: string[]) => void;
28
25
  };
29
26
  treeInstance: SyncDataLoaderFeatureDef<T>["treeInstance"] & {
27
+ /** @deprecated use loadItemData instead */
30
28
  waitForItemDataLoaded: (itemId: string) => Promise<void>;
29
+ /** @deprecated use loadChildrenIds instead */
31
30
  waitForItemChildrenLoaded: (itemId: string) => Promise<void>;
31
+ loadItemData: (itemId: string) => Promise<T>;
32
+ loadChildrenIds: (itemId: string) => Promise<string[]>;
32
33
  };
33
34
  itemInstance: SyncDataLoaderFeatureDef<T>["itemInstance"] & {
34
35
  /** Invalidate fetched data for item, and triggers a refetch and subsequent rerender if the item is visible
@@ -44,4 +45,3 @@ export type AsyncDataLoaderFeatureDef<T> = {
44
45
  };
45
46
  hotkeys: SyncDataLoaderFeatureDef<T>["hotkeys"];
46
47
  };
47
- export {};
@@ -48,7 +48,7 @@ export const expandAllFeature = {
48
48
  handler: (_, tree) => __awaiter(void 0, void 0, void 0, function* () {
49
49
  const cancelToken = { current: false };
50
50
  const cancelHandler = (e) => {
51
- if (e.key === "Escape") {
51
+ if (e.code === "Escape") {
52
52
  cancelToken.current = true;
53
53
  }
54
54
  };
@@ -58,7 +58,7 @@ export const expandAllFeature = {
58
58
  }),
59
59
  },
60
60
  collapseSelected: {
61
- hotkey: "Control+Shift+-",
61
+ hotkey: "Control+Shift+Minus",
62
62
  handler: (_, tree) => {
63
63
  tree.getSelectedItems().forEach((item) => item.collapseAll());
64
64
  },
@@ -1,14 +1,29 @@
1
1
  const specialKeys = {
2
- Letter: /^[a-z]$/,
3
- LetterOrNumber: /^[a-z0-9]$/,
4
- Plus: /^\+$/,
5
- Space: /^ $/,
2
+ // TODO:breaking deprecate auto-lowercase
3
+ letter: /^Key[A-Z]$/,
4
+ letterornumber: /^(Key[A-Z]|Digit[0-9])$/,
5
+ plus: /^(NumpadAdd|Plus)$/,
6
+ minus: /^(NumpadSubtract|Minus)$/,
7
+ control: /^(ControlLeft|ControlRight)$/,
8
+ shift: /^(ShiftLeft|ShiftRight)$/,
6
9
  };
7
10
  const testHotkeyMatch = (pressedKeys, tree, hotkey) => {
8
- const supposedKeys = hotkey.hotkey.toLowerCase().split("+");
9
- const doKeysMatch = supposedKeys.every((key) => key in specialKeys
10
- ? [...pressedKeys].some((pressedKey) => specialKeys[key].test(pressedKey))
11
- : pressedKeys.has(key));
11
+ const supposedKeys = hotkey.hotkey.toLowerCase().split("+"); // TODO:breaking deprecate auto-lowercase
12
+ const doKeysMatch = supposedKeys.every((key) => {
13
+ if (key in specialKeys) {
14
+ return [...pressedKeys].some((pressedKey) => specialKeys[key].test(pressedKey));
15
+ }
16
+ const pressedKeysLowerCase = [...pressedKeys] // TODO:breaking deprecate auto-lowercase
17
+ .map((k) => k.toLowerCase());
18
+ if (pressedKeysLowerCase.includes(key.toLowerCase())) {
19
+ return true;
20
+ }
21
+ if (pressedKeysLowerCase.includes(`key${key.toLowerCase()}`)) {
22
+ // TODO:breaking deprecate e.key character matching
23
+ return true;
24
+ }
25
+ return false;
26
+ });
12
27
  const isEnabled = !hotkey.isEnabled || hotkey.isEnabled(tree);
13
28
  const equalCounts = pressedKeys.size === supposedKeys.length;
14
29
  return doKeysMatch && isEnabled && equalCounts;
@@ -28,15 +43,14 @@ export const hotkeysCoreFeature = {
28
43
  if (e.target instanceof HTMLInputElement && ignoreHotkeysOnInputs) {
29
44
  return;
30
45
  }
31
- const key = e.key.toLowerCase();
32
46
  (_a = (_b = data.current).pressedKeys) !== null && _a !== void 0 ? _a : (_b.pressedKeys = new Set());
33
- const newMatch = !data.current.pressedKeys.has(key);
34
- data.current.pressedKeys.add(key);
47
+ const newMatch = !data.current.pressedKeys.has(e.code);
48
+ data.current.pressedKeys.add(e.code);
35
49
  const hotkeyName = findHotkeyMatch(data.current.pressedKeys, tree, tree.getHotkeyPresets(), hotkeys);
36
50
  if (e.target instanceof HTMLInputElement) {
37
51
  // JS respects composite keydowns while input elements are focused, and
38
52
  // doesnt send the associated keyup events with the same key name
39
- data.current.pressedKeys.delete(key);
53
+ data.current.pressedKeys.delete(e.code);
40
54
  }
41
55
  if (!hotkeyName)
42
56
  return;
@@ -57,7 +71,7 @@ export const hotkeysCoreFeature = {
57
71
  var _a;
58
72
  var _b;
59
73
  (_a = (_b = data.current).pressedKeys) !== null && _a !== void 0 ? _a : (_b.pressedKeys = new Set());
60
- data.current.pressedKeys.delete(e.key.toLowerCase());
74
+ data.current.pressedKeys.delete(e.code);
61
75
  };
62
76
  const reset = () => {
63
77
  data.current.pressedKeys = new Set();
@@ -141,7 +141,7 @@ export const keyboardDragAndDropFeature = {
141
141
  },
142
142
  hotkeys: {
143
143
  startDrag: {
144
- hotkey: "Control+Shift+D",
144
+ hotkey: "Control+Shift+KeyD",
145
145
  preventDefault: true,
146
146
  isEnabled: (tree) => !tree.getState().dnd,
147
147
  handler: (_, tree) => {
@@ -17,8 +17,6 @@ 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>;
22
20
  setState: SetStateFn<TreeState<T>>;
23
21
  getState: () => TreeState<T>;
24
22
  setConfig: SetStateFn<TreeConfig<T>>;