@headless-tree/core 0.0.11 → 0.0.13

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 (108) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/lib/cjs/core/build-static-instance.js +1 -2
  3. package/lib/cjs/core/create-tree.js +7 -4
  4. package/lib/cjs/features/async-data-loader/feature.d.ts +1 -4
  5. package/lib/cjs/features/async-data-loader/feature.js +5 -7
  6. package/lib/cjs/features/async-data-loader/types.d.ts +2 -5
  7. package/lib/cjs/features/drag-and-drop/feature.d.ts +2 -3
  8. package/lib/cjs/features/drag-and-drop/feature.js +27 -24
  9. package/lib/cjs/features/drag-and-drop/types.d.ts +3 -3
  10. package/lib/cjs/features/drag-and-drop/utils.d.ts +1 -1
  11. package/lib/cjs/features/drag-and-drop/utils.js +4 -4
  12. package/lib/cjs/features/expand-all/feature.d.ts +1 -5
  13. package/lib/cjs/features/hotkeys-core/feature.d.ts +1 -3
  14. package/lib/cjs/features/prop-memoization/feature.d.ts +2 -0
  15. package/lib/cjs/features/prop-memoization/feature.js +48 -0
  16. package/lib/cjs/features/prop-memoization/types.d.ts +10 -0
  17. package/lib/cjs/features/prop-memoization/types.js +2 -0
  18. package/lib/cjs/features/renaming/feature.d.ts +1 -4
  19. package/lib/cjs/features/renaming/feature.js +8 -9
  20. package/lib/cjs/features/renaming/types.d.ts +1 -1
  21. package/lib/cjs/features/search/feature.d.ts +1 -4
  22. package/lib/cjs/features/selection/feature.d.ts +1 -4
  23. package/lib/cjs/features/selection/feature.js +35 -25
  24. package/lib/cjs/features/selection/types.d.ts +1 -1
  25. package/lib/cjs/features/sync-data-loader/feature.d.ts +1 -3
  26. package/lib/cjs/features/tree/feature.d.ts +1 -6
  27. package/lib/cjs/features/tree/feature.js +40 -57
  28. package/lib/cjs/features/tree/types.d.ts +0 -5
  29. package/lib/cjs/index.d.ts +2 -0
  30. package/lib/cjs/index.js +2 -0
  31. package/lib/cjs/mddocs-entry.d.ts +10 -0
  32. package/lib/cjs/test-utils/test-tree-do.d.ts +1 -1
  33. package/lib/cjs/test-utils/test-tree-expect.d.ts +1 -1
  34. package/lib/cjs/test-utils/test-tree-expect.js +1 -1
  35. package/lib/cjs/test-utils/test-tree.d.ts +1 -1
  36. package/lib/cjs/test-utils/test-tree.js +9 -1
  37. package/lib/cjs/types/core.d.ts +29 -30
  38. package/lib/esm/core/build-static-instance.js +1 -2
  39. package/lib/esm/core/create-tree.js +7 -4
  40. package/lib/esm/features/async-data-loader/feature.d.ts +1 -4
  41. package/lib/esm/features/async-data-loader/feature.js +5 -7
  42. package/lib/esm/features/async-data-loader/types.d.ts +2 -5
  43. package/lib/esm/features/drag-and-drop/feature.d.ts +2 -3
  44. package/lib/esm/features/drag-and-drop/feature.js +27 -24
  45. package/lib/esm/features/drag-and-drop/types.d.ts +3 -3
  46. package/lib/esm/features/drag-and-drop/utils.d.ts +1 -1
  47. package/lib/esm/features/drag-and-drop/utils.js +4 -4
  48. package/lib/esm/features/expand-all/feature.d.ts +1 -5
  49. package/lib/esm/features/hotkeys-core/feature.d.ts +1 -3
  50. package/lib/esm/features/prop-memoization/feature.d.ts +2 -0
  51. package/lib/esm/features/prop-memoization/feature.js +45 -0
  52. package/lib/esm/features/prop-memoization/types.d.ts +10 -0
  53. package/lib/esm/features/prop-memoization/types.js +1 -0
  54. package/lib/esm/features/renaming/feature.d.ts +1 -4
  55. package/lib/esm/features/renaming/feature.js +8 -9
  56. package/lib/esm/features/renaming/types.d.ts +1 -1
  57. package/lib/esm/features/search/feature.d.ts +1 -4
  58. package/lib/esm/features/selection/feature.d.ts +1 -4
  59. package/lib/esm/features/selection/feature.js +35 -25
  60. package/lib/esm/features/selection/types.d.ts +1 -1
  61. package/lib/esm/features/sync-data-loader/feature.d.ts +1 -3
  62. package/lib/esm/features/tree/feature.d.ts +1 -6
  63. package/lib/esm/features/tree/feature.js +40 -57
  64. package/lib/esm/features/tree/types.d.ts +0 -5
  65. package/lib/esm/index.d.ts +2 -0
  66. package/lib/esm/index.js +2 -0
  67. package/lib/esm/mddocs-entry.d.ts +10 -0
  68. package/lib/esm/test-utils/test-tree-do.d.ts +1 -1
  69. package/lib/esm/test-utils/test-tree-expect.d.ts +1 -1
  70. package/lib/esm/test-utils/test-tree-expect.js +1 -1
  71. package/lib/esm/test-utils/test-tree.d.ts +1 -1
  72. package/lib/esm/test-utils/test-tree.js +9 -1
  73. package/lib/esm/types/core.d.ts +29 -30
  74. package/package.json +1 -1
  75. package/src/core/build-proxified-instance.ts +5 -3
  76. package/src/core/build-static-instance.ts +1 -2
  77. package/src/core/core.spec.ts +210 -0
  78. package/src/core/create-tree.ts +13 -16
  79. package/src/features/async-data-loader/async-data-loader.spec.ts +12 -31
  80. package/src/features/async-data-loader/feature.ts +8 -20
  81. package/src/features/async-data-loader/types.ts +2 -6
  82. package/src/features/drag-and-drop/drag-and-drop.spec.ts +4 -3
  83. package/src/features/drag-and-drop/feature.ts +87 -86
  84. package/src/features/drag-and-drop/types.ts +4 -4
  85. package/src/features/drag-and-drop/utils.ts +4 -4
  86. package/src/features/expand-all/expand-all.spec.ts +5 -1
  87. package/src/features/expand-all/feature.ts +1 -12
  88. package/src/features/hotkeys-core/feature.ts +4 -13
  89. package/src/features/prop-memoization/feature.ts +51 -0
  90. package/src/features/prop-memoization/prop-memoization.spec.ts +68 -0
  91. package/src/features/prop-memoization/types.ts +11 -0
  92. package/src/features/renaming/feature.ts +11 -20
  93. package/src/features/renaming/renaming.spec.ts +11 -9
  94. package/src/features/renaming/types.ts +1 -1
  95. package/src/features/search/feature.ts +2 -8
  96. package/src/features/search/search.spec.ts +3 -1
  97. package/src/features/selection/feature.ts +45 -47
  98. package/src/features/selection/selection.spec.ts +13 -14
  99. package/src/features/selection/types.ts +0 -2
  100. package/src/features/sync-data-loader/feature.ts +1 -7
  101. package/src/features/tree/feature.ts +47 -85
  102. package/src/features/tree/tree.spec.ts +24 -64
  103. package/src/features/tree/types.ts +0 -6
  104. package/src/index.ts +2 -0
  105. package/src/mddocs-entry.ts +13 -0
  106. package/src/test-utils/test-tree-expect.ts +1 -1
  107. package/src/test-utils/test-tree.ts +11 -1
  108. package/src/types/core.ts +56 -147
@@ -12,7 +12,6 @@ export type RenamingFeatureDef<T> = {
12
12
  onRename?: (item: ItemInstance<T>, value: string) => void;
13
13
  };
14
14
  treeInstance: {
15
- startRenamingItem: (itemId: string) => void;
16
15
  getRenamingItem: () => ItemInstance<T> | null;
17
16
  getRenamingValue: () => string;
18
17
  abortRenaming: () => void;
@@ -23,6 +22,7 @@ export type RenamingFeatureDef<T> = {
23
22
  getRenameInputProps: () => any;
24
23
  canRename: () => boolean;
25
24
  isRenaming: () => boolean;
25
+ startRenaming: () => void;
26
26
  };
27
27
  hotkeys: "renameItem" | "abortRenaming" | "completeRenaming";
28
28
  };
@@ -1,14 +1,8 @@
1
1
  import { FeatureImplementation } from "../../types/core";
2
- import { SearchFeatureDataRef, SearchFeatureDef } from "./types";
3
- import { MainFeatureDef } from "../main/types";
4
- import { TreeFeatureDef } from "../tree/types";
2
+ import { SearchFeatureDataRef } from "./types";
5
3
  import { makeStateUpdater, memo } from "../../utils";
6
4
 
7
- export const searchFeature: FeatureImplementation<
8
- any,
9
- SearchFeatureDef<any>,
10
- MainFeatureDef | TreeFeatureDef<any> | SearchFeatureDef<any>
11
- > = {
5
+ export const searchFeature: FeatureImplementation = {
12
6
  key: "search",
13
7
 
14
8
  getInitialState: (initialState) => ({
@@ -2,10 +2,12 @@ import { describe, expect, it } from "vitest";
2
2
  import { TestTree } from "../../test-utils/test-tree";
3
3
  import { searchFeature } from "./feature";
4
4
  import { selectionFeature } from "../selection/feature";
5
+ import { propMemoizationFeature } from "../prop-memoization/feature";
5
6
 
6
7
  const factory = TestTree.default({}).withFeatures(
7
8
  searchFeature,
8
9
  selectionFeature,
10
+ propMemoizationFeature,
9
11
  );
10
12
 
11
13
  describe("core-feature/search", () => {
@@ -85,7 +87,7 @@ describe("core-feature/search", () => {
85
87
  it("selects focused item when search is submitted", () => {
86
88
  const setSelectedItems = tree.mockedHandler("setSelectedItems");
87
89
  const onCloseSearch = tree.mockedHandler("onCloseSearch");
88
- tree.instance.focusItem("x1");
90
+ tree.item("x1").setFocused();
89
91
  tree.do.hotkey("openSearch");
90
92
  tree.do.hotkey("submitSearch");
91
93
  expect(setSelectedItems).toHaveBeenCalledWith(["x1"]);
@@ -1,14 +1,7 @@
1
1
  import { FeatureImplementation } from "../../types/core";
2
- import { SelectionFeatureDef } from "./types";
3
- import { MainFeatureDef } from "../main/types";
4
- import { TreeFeatureDef } from "../tree/types";
5
2
  import { makeStateUpdater } from "../../utils";
6
3
 
7
- export const selectionFeature: FeatureImplementation<
8
- any,
9
- SelectionFeatureDef<any>,
10
- MainFeatureDef | TreeFeatureDef<any> | SelectionFeatureDef<any>
11
- > = {
4
+ export const selectionFeature: FeatureImplementation = {
12
5
  key: "selection",
13
6
 
14
7
  getInitialState: (initialState) => ({
@@ -37,20 +30,18 @@ export const selectionFeature: FeatureImplementation<
37
30
  },
38
31
 
39
32
  itemInstance: {
40
- select: ({ tree, item }) => {
33
+ select: ({ tree, itemId }) => {
41
34
  const { selectedItems } = tree.getState();
42
35
  tree.setSelectedItems(
43
- selectedItems.includes(item.getItemMeta().itemId)
36
+ selectedItems.includes(itemId)
44
37
  ? selectedItems
45
- : [...selectedItems, item.getItemMeta().itemId],
38
+ : [...selectedItems, itemId],
46
39
  );
47
40
  },
48
41
 
49
- deselect: ({ tree, item }) => {
42
+ deselect: ({ tree, itemId }) => {
50
43
  const { selectedItems } = tree.getState();
51
- tree.setSelectedItems(
52
- selectedItems.filter((id) => id !== item.getItemMeta().itemId),
53
- );
44
+ tree.setSelectedItems(selectedItems.filter((id) => id !== itemId));
54
45
  },
55
46
 
56
47
  isSelected: ({ tree, item }) => {
@@ -91,20 +82,17 @@ export const selectionFeature: FeatureImplementation<
91
82
  getProps: ({ tree, item, prev }) => ({
92
83
  ...prev?.(),
93
84
  "aria-selected": item.isSelected() ? "true" : "false",
94
- onClick: item.getMemoizedProp(
95
- "selection/onClick",
96
- () => (e: MouseEvent) => {
97
- if (e.shiftKey) {
98
- item.selectUpTo(e.ctrlKey || e.metaKey);
99
- } else if (e.ctrlKey || e.metaKey) {
100
- item.toggleSelect();
101
- } else {
102
- tree.setSelectedItems([item.getItemMeta().itemId]);
103
- }
104
-
105
- prev?.()?.onClick?.(e);
106
- },
107
- ),
85
+ onClick: (e: MouseEvent) => {
86
+ if (e.shiftKey) {
87
+ item.selectUpTo(e.ctrlKey || e.metaKey);
88
+ } else if (e.ctrlKey || e.metaKey) {
89
+ item.toggleSelect();
90
+ } else {
91
+ tree.setSelectedItems([item.getItemMeta().itemId]);
92
+ }
93
+
94
+ prev?.()?.onClick?.(e);
95
+ },
108
96
  }),
109
97
  },
110
98
 
@@ -122,27 +110,37 @@ export const selectionFeature: FeatureImplementation<
122
110
  },
123
111
  },
124
112
  selectUpwards: {
125
- hotkey: "shift+ArrowUp",
126
- handler: () => {
127
- // TODO
113
+ hotkey: "Shift+ArrowUp",
114
+ handler: (e, tree) => {
115
+ const focused = tree.getFocusedItem();
116
+ const above = focused.getItemAbove();
117
+ if (!above) return;
118
+
119
+ if (focused.isSelected() && above.isSelected()) {
120
+ focused.deselect();
121
+ } else {
122
+ above.select();
123
+ }
124
+
125
+ above.setFocused();
126
+ tree.updateDomFocus();
128
127
  },
129
128
  },
130
129
  selectDownwards: {
131
- hotkey: "shift+ArrowDown",
132
- handler: () => {
133
- // TODO
134
- },
135
- },
136
- selectUpwardsCtrl: {
137
- hotkey: "shift+ctrl+ArrowUp",
138
- handler: () => {
139
- // TODO
140
- },
141
- },
142
- selectDownwardsCtrl: {
143
- hotkey: "shift+ctrl+ArrowUp",
144
- handler: () => {
145
- // TODO
130
+ hotkey: "Shift+ArrowDown",
131
+ handler: (e, tree) => {
132
+ const focused = tree.getFocusedItem();
133
+ const below = focused.getItemBelow();
134
+ if (!below) return;
135
+
136
+ if (focused.isSelected() && below.isSelected()) {
137
+ focused.deselect();
138
+ } else {
139
+ below.select();
140
+ }
141
+
142
+ below.setFocused();
143
+ tree.updateDomFocus();
146
144
  },
147
145
  },
148
146
  selectAll: {
@@ -1,23 +1,22 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
  import { TestTree } from "../../test-utils/test-tree";
3
3
  import { selectionFeature } from "./feature";
4
+ import { propMemoizationFeature } from "../prop-memoization/feature";
4
5
 
5
- const factory = TestTree.default({}).withFeatures(selectionFeature);
6
-
7
- // it("test", async () => {
8
- // const tree = await factory.suits.async().tree.createDebugTree();
9
- // const setSelectedItems = tree.mockedHandler("setSelectedItems");
10
- // const setFocusedItem = tree.mockedHandler("setFocusedItem");
11
- //
12
- // tree.do.selectItem("x111");
13
- // tree.do.ctrlSelectItem("x112");
14
- // tree.do.ctrlSelectItem("x113");
15
- //
16
- // expect(setSelectedItems).toHaveBeenCalledWith(["x111", "x112", "x113"]);
17
- // expect(setFocusedItem).toHaveBeenCalledWith("x113");
18
- // });
6
+ const factory = TestTree.default({}).withFeatures(
7
+ selectionFeature,
8
+ propMemoizationFeature,
9
+ );
19
10
 
20
11
  describe("core-feature/selections", () => {
12
+ it("test", async () => {
13
+ const tree = await factory.suits.proxifiedSync().tree.createTestCaseTree();
14
+ tree.do.selectItem("x111");
15
+ expect(
16
+ tree.instance.getItemInstance("x111").getProps()["aria-selected"],
17
+ ).toBe("true");
18
+ });
19
+
21
20
  factory.forSuits((tree) => {
22
21
  it("sets aria-selected to false", () => {
23
22
  expect(
@@ -22,7 +22,5 @@ export type SelectionFeatureDef<T> = {
22
22
  | "toggleSelectItem"
23
23
  | "selectUpwards"
24
24
  | "selectDownwards"
25
- | "selectUpwardsCtrl"
26
- | "selectDownwardsCtrl"
27
25
  | "selectAll";
28
26
  };
@@ -1,13 +1,7 @@
1
1
  import { FeatureImplementation } from "../../types/core";
2
- import { SyncDataLoaderFeatureDef } from "./types";
3
- import { MainFeatureDef } from "../main/types";
4
2
  import { makeStateUpdater } from "../../utils";
5
3
 
6
- export const syncDataLoaderFeature: FeatureImplementation<
7
- any,
8
- SyncDataLoaderFeatureDef<any>,
9
- MainFeatureDef | SyncDataLoaderFeatureDef<any>
10
- > = {
4
+ export const syncDataLoaderFeature: FeatureImplementation = {
11
5
  key: "sync-data-loader",
12
6
 
13
7
  getInitialState: (initialState) => ({
@@ -1,20 +1,8 @@
1
1
  import { FeatureImplementation, ItemInstance } from "../../types/core";
2
- import { ItemMeta, TreeFeatureDef, TreeItemDataRef } from "./types";
2
+ import { ItemMeta } from "./types";
3
3
  import { makeStateUpdater, poll } from "../../utils";
4
- import { MainFeatureDef } from "../main/types";
5
- import { HotkeysCoreFeatureDef } from "../hotkeys-core/types";
6
- import { SyncDataLoaderFeatureDef } from "../sync-data-loader/types";
7
- import { SearchFeatureDef } from "../search/types";
8
4
 
9
- export const treeFeature: FeatureImplementation<
10
- any,
11
- TreeFeatureDef<any>,
12
- | MainFeatureDef
13
- | TreeFeatureDef<any>
14
- | HotkeysCoreFeatureDef<any>
15
- | SyncDataLoaderFeatureDef<any>
16
- | SearchFeatureDef<any>
17
- > = {
5
+ export const treeFeature: FeatureImplementation<any> = {
18
6
  key: "tree",
19
7
 
20
8
  getInitialState: (initialState) => ({
@@ -35,9 +23,6 @@ export const treeFeature: FeatureImplementation<
35
23
  },
36
24
 
37
25
  treeInstance: {
38
- isItemExpanded: ({ tree }, itemId) =>
39
- tree.getState().expandedItems.includes(itemId),
40
-
41
26
  getItemsMeta: ({ tree }) => {
42
27
  const { rootItemId } = tree.getConfig();
43
28
  const { expandedItems } = tree.getState();
@@ -79,33 +64,6 @@ export const treeFeature: FeatureImplementation<
79
64
  return flatItems;
80
65
  },
81
66
 
82
- expandItem: ({ tree }, itemId) => {
83
- if (!tree.getItemInstance(itemId).isFolder()) {
84
- return;
85
- }
86
-
87
- if (tree.getState().loadingItems?.includes(itemId)) {
88
- return;
89
- }
90
-
91
- tree.applySubStateUpdate("expandedItems", (expandedItems) => [
92
- ...expandedItems,
93
- itemId,
94
- ]);
95
- tree.rebuildTree();
96
- },
97
-
98
- collapseItem: ({ tree }, itemId) => {
99
- if (!tree.getItemInstance(itemId).isFolder()) {
100
- return;
101
- }
102
-
103
- tree.applySubStateUpdate("expandedItems", (expandedItems) =>
104
- expandedItems.filter((id) => id !== itemId),
105
- );
106
- tree.rebuildTree();
107
- },
108
-
109
67
  // TODO memo
110
68
  getFocusedItem: ({ tree }) => {
111
69
  return (
@@ -114,20 +72,16 @@ export const treeFeature: FeatureImplementation<
114
72
  );
115
73
  },
116
74
 
117
- focusItem: ({ tree }, itemId) => {
118
- tree.applySubStateUpdate("focusedItem", itemId);
119
- },
120
-
121
75
  focusNextItem: ({ tree }) => {
122
76
  const { index } = tree.getFocusedItem().getItemMeta();
123
77
  const nextIndex = Math.min(index + 1, tree.getItems().length - 1);
124
- tree.focusItem(tree.getItems()[nextIndex].getId());
78
+ tree.getItems()[nextIndex]?.setFocused();
125
79
  },
126
80
 
127
81
  focusPreviousItem: ({ tree }) => {
128
82
  const { index } = tree.getFocusedItem().getItemMeta();
129
83
  const nextIndex = Math.max(index - 1, 0);
130
- tree.focusItem(tree.getItems()[nextIndex].getId());
84
+ tree.getItems()[nextIndex]?.setFocused();
131
85
  },
132
86
 
133
87
  updateDomFocus: ({ tree }) => {
@@ -142,11 +96,12 @@ export const treeFeature: FeatureImplementation<
142
96
  });
143
97
  },
144
98
 
145
- getContainerProps: ({ prev }) => ({
99
+ // TODO add label parameter
100
+ getContainerProps: ({ prev, tree }) => ({
146
101
  ...prev?.(),
147
102
  role: "tree",
148
- ariaLabel: "",
149
- ariaActivedescendant: "",
103
+ "aria-label": "",
104
+ ref: tree.registerElement, // TODO memo
150
105
  }),
151
106
 
152
107
  // relevant for hotkeys of this feature
@@ -159,11 +114,12 @@ export const treeFeature: FeatureImplementation<
159
114
  await poll(() => item.getElement() !== null, 20);
160
115
  item.getElement()?.scrollIntoView(scrollIntoViewArg);
161
116
  },
162
- getId: ({ item }) => item.getItemMeta().itemId,
117
+ getId: ({ itemId }) => itemId,
163
118
  getProps: ({ item, prev }) => {
164
119
  const itemMeta = item.getItemMeta();
165
120
  return {
166
121
  ...prev?.(),
122
+ ref: item.registerElement, // TODO memo
167
123
  role: "treeitem",
168
124
  "aria-setsize": itemMeta.setSize,
169
125
  "aria-posinset": itemMeta.posInSet,
@@ -171,7 +127,7 @@ export const treeFeature: FeatureImplementation<
171
127
  "aria-label": item.getItemName(),
172
128
  "aria-level": itemMeta.level,
173
129
  tabIndex: item.isFocused() ? 0 : -1,
174
- onClick: item.getMemoizedProp("tree/onClick", () => (e: MouseEvent) => {
130
+ onClick: (e: MouseEvent) => {
175
131
  item.setFocused();
176
132
  item.primaryAction();
177
133
 
@@ -188,24 +144,46 @@ export const treeFeature: FeatureImplementation<
188
144
  } else {
189
145
  item.expand();
190
146
  }
191
- }),
147
+ },
192
148
  };
193
149
  },
194
- expand: ({ tree, item }) => tree.expandItem(item.getItemMeta().itemId),
195
- collapse: ({ tree, item }) => tree.collapseItem(item.getItemMeta().itemId),
196
- getItemData: ({ tree, item }) =>
197
- tree.retrieveItemData(item.getItemMeta().itemId),
150
+ expand: ({ tree, item, itemId }) => {
151
+ if (!item.isFolder()) {
152
+ return;
153
+ }
154
+
155
+ if (tree.getState().loadingItems?.includes(itemId)) {
156
+ return;
157
+ }
158
+
159
+ tree.applySubStateUpdate("expandedItems", (expandedItems) => [
160
+ ...expandedItems,
161
+ itemId,
162
+ ]);
163
+ tree.rebuildTree();
164
+ },
165
+ collapse: ({ tree, item, itemId }) => {
166
+ if (!item.isFolder()) {
167
+ return;
168
+ }
169
+
170
+ tree.applySubStateUpdate("expandedItems", (expandedItems) =>
171
+ expandedItems.filter((id) => id !== itemId),
172
+ );
173
+ tree.rebuildTree();
174
+ },
175
+ getItemData: ({ tree, itemId }) => tree.retrieveItemData(itemId),
198
176
  equals: ({ item }, other) => item.getId() === other?.getId(),
199
- isExpanded: ({ tree, item }) =>
200
- tree.getState().expandedItems.includes(item.getItemMeta().itemId),
177
+ isExpanded: ({ tree, itemId }) =>
178
+ tree.getState().expandedItems.includes(itemId),
201
179
  isDescendentOf: ({ item }, parentId) => {
202
180
  const parent = item.getParent();
203
181
  return Boolean(
204
182
  parent?.getId() === parentId || parent?.isDescendentOf(parentId),
205
183
  );
206
184
  },
207
- isFocused: ({ tree, item }) =>
208
- tree.getState().focusedItem === item.getItemMeta().itemId ||
185
+ isFocused: ({ tree, item, itemId }) =>
186
+ tree.getState().focusedItem === itemId ||
209
187
  (tree.getState().focusedItem === null && item.getItemMeta().index === 0),
210
188
  isFolder: ({ tree, item }) =>
211
189
  item.getItemMeta().level === -1 ||
@@ -214,14 +192,15 @@ export const treeFeature: FeatureImplementation<
214
192
  const config = tree.getConfig();
215
193
  return config.getItemName(item as ItemInstance<any>);
216
194
  },
217
- setFocused: ({ tree, item }) => tree.focusItem(item.getItemMeta().itemId),
195
+ setFocused: ({ tree, itemId }) => {
196
+ tree.applySubStateUpdate("focusedItem", itemId);
197
+ },
218
198
  primaryAction: ({ tree, item }) =>
219
199
  tree.getConfig().onPrimaryAction?.(item as ItemInstance<any>),
220
200
  getParent: ({ tree, item }) =>
221
201
  item.getItemMeta().parentId
222
202
  ? tree.getItemInstance(item.getItemMeta().parentId)
223
203
  : undefined,
224
- // TODO remove
225
204
  getIndexInParent: ({ item }) => item.getItemMeta().posInSet,
226
205
  getChildren: ({ tree, item }) =>
227
206
  tree
@@ -232,23 +211,6 @@ export const treeFeature: FeatureImplementation<
232
211
  tree.getItems()[item.getItemMeta().index - 1],
233
212
  getItemBelow: ({ tree, item }) =>
234
213
  tree.getItems()[item.getItemMeta().index + 1],
235
- getMemoizedProp: ({ item }, name, create, deps) => {
236
- const data = item.getDataRef<TreeItemDataRef>();
237
- const memoizedValue = data.current.memoizedValues?.[name];
238
- if (
239
- memoizedValue &&
240
- (!deps ||
241
- data.current.memoizedDeps?.[name]?.every((d, i) => d === deps![i]))
242
- ) {
243
- return memoizedValue;
244
- }
245
- data.current.memoizedDeps ??= {};
246
- data.current.memoizedValues ??= {};
247
- const value = create();
248
- data.current.memoizedDeps[name] = deps;
249
- data.current.memoizedValues[name] = value;
250
- return value;
251
- },
252
214
  },
253
215
 
254
216
  hotkeys: {
@@ -306,14 +268,14 @@ export const treeFeature: FeatureImplementation<
306
268
  focusFirstItem: {
307
269
  hotkey: "Home",
308
270
  handler: (e, tree) => {
309
- tree.focusItem(tree.getItems()[0].getId());
271
+ tree.getItems()[0]?.setFocused();
310
272
  tree.updateDomFocus();
311
273
  },
312
274
  },
313
275
  focusLastItem: {
314
276
  hotkey: "End",
315
277
  handler: (e, tree) => {
316
- tree.focusItem(tree.getItems()[tree.getItems().length - 1].getId());
278
+ tree.getItems()[tree.getItems().length - 1]?.setFocused();
317
279
  tree.updateDomFocus();
318
280
  },
319
281
  },