@react-fabric/data 1.0.0-beta.53 → 1.0.0-beta.55

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 (42) hide show
  1. package/dist/cjs/hooks/useFilteredList.js +68 -10
  2. package/dist/cjs/table/HeaderCell.js +3 -2
  3. package/dist/cjs/table/Table.js +9 -3
  4. package/dist/cjs/table/useVirtualTable.js +18 -0
  5. package/dist/cjs/tree/TreeNode.js +2 -2
  6. package/dist/cjs/tree/TreePanel.js +5 -4
  7. package/dist/cjs/tree/useTree.js +62 -15
  8. package/dist/cjs/tree/utils.js +3 -3
  9. package/dist/cjs/virtual/Gallery.js +2 -2
  10. package/dist/cjs/virtual/List.js +2 -2
  11. package/dist/esm/hooks/useFilteredList.js +69 -11
  12. package/dist/esm/table/HeaderCell.js +3 -2
  13. package/dist/esm/table/Table.js +9 -3
  14. package/dist/esm/table/useVirtualTable.js +19 -1
  15. package/dist/esm/tree/TreeNode.js +2 -2
  16. package/dist/esm/tree/TreePanel.js +5 -4
  17. package/dist/esm/tree/useTree.js +63 -16
  18. package/dist/esm/tree/utils.js +3 -3
  19. package/dist/esm/virtual/Gallery.js +2 -2
  20. package/dist/esm/virtual/List.js +2 -2
  21. package/dist/types/hooks/useFilteredList.d.ts +49 -6
  22. package/dist/types/table/HeaderCell.d.ts +1 -1
  23. package/dist/types/table/Table.d.ts +1 -1
  24. package/dist/types/table/types.d.ts +3 -0
  25. package/dist/types/table/useVirtualTable.d.ts +3 -0
  26. package/dist/types/tree/TreePanel.d.ts +1 -1
  27. package/dist/types/tree/types.d.ts +11 -13
  28. package/dist/types/tree/useTree.d.ts +1 -0
  29. package/dist/types/tree/utils.d.ts +1 -1
  30. package/package.json +5 -5
  31. package/src/hooks/useFilteredList.tsx +90 -9
  32. package/src/table/HeaderCell.tsx +3 -2
  33. package/src/table/Table.tsx +7 -2
  34. package/src/table/types.ts +4 -0
  35. package/src/table/useVirtualTable.ts +24 -1
  36. package/src/tree/TreeNode.tsx +3 -5
  37. package/src/tree/TreePanel.tsx +3 -3
  38. package/src/tree/types.ts +12 -12
  39. package/src/tree/useTree.ts +57 -13
  40. package/src/tree/utils.ts +8 -3
  41. package/src/virtual/Gallery.tsx +1 -1
  42. package/src/virtual/List.tsx +1 -1
@@ -21,7 +21,7 @@
21
21
  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
  */
23
23
 
24
- import { isString, matchString } from "@react-fabric/utilities";
24
+ import { isArray, isString, matchString } from "@react-fabric/utilities";
25
25
  import {
26
26
  useCallback,
27
27
  useDeferredValue,
@@ -33,6 +33,7 @@ import {
33
33
  const filterList = (
34
34
  items: AnyObject[],
35
35
  query: AnyObject = "",
36
+ matchAll: boolean = true,
36
37
  matcher?: AnyObject,
37
38
  ) => {
38
39
  const newList: AnyObject[] = [];
@@ -40,7 +41,20 @@ const filterList = (
40
41
  if (matcher) {
41
42
  return matcher?.(item, query) && newList.push(item);
42
43
  }
43
- if (query && isString(query)) {
44
+ if (query && isArray(query) && query.length > 0) {
45
+ if (
46
+ matchAll &&
47
+ query.every((q) => matchString(item.label ?? item.toString(), q))
48
+ ) {
49
+ return newList.push(item);
50
+ }
51
+ if (
52
+ !matchAll &&
53
+ query.some((q) => matchString(item.label ?? item.toString(), q))
54
+ ) {
55
+ return newList.push(item);
56
+ }
57
+ } else if (query && isString(query)) {
44
58
  if (matchString(item.label ?? item.toString(), query)) {
45
59
  return newList.push(item);
46
60
  }
@@ -51,6 +65,61 @@ const filterList = (
51
65
  return newList;
52
66
  };
53
67
 
68
+ interface RetType<T, Q> {
69
+ onSearch: (query?: Q) => void;
70
+ query: Q;
71
+ filteredList: T[];
72
+ isSearching: boolean;
73
+ }
74
+
75
+ /**
76
+ * Custom hook to filter a list of items based on a query.
77
+ * It provides a way to search through the items and returns the filtered list.
78
+ * The hook uses a deferred value to optimize performance during transitions.
79
+ * It also allows for a custom matcher function to define how items should be matched against the query.
80
+ *
81
+ * @param items - The list of items to filter.
82
+ * @param matchAll - Match all phrases in the query list against the items.
83
+ * @returns An object containing the search function, current query, filtered list, and a boolean indicating if a search is in progress.
84
+ *
85
+ * @example
86
+ * ```jsx
87
+ * useFilteredList(items, (item, query) => {
88
+ * return item.name.includes(query);
89
+ * });
90
+ * ```
91
+ *
92
+ * @see {@link https://adarshpastakia.github.io/react-fabric/?path=/docs/data-usefilteredlist--docs}
93
+ */
94
+ export function useFilteredList<
95
+ T extends AnyObject = KeyValue,
96
+ Q extends AnyObject = string,
97
+ >(items: T[], matchAll?: boolean): RetType<T, Q>;
98
+
99
+ /**
100
+ * Custom hook to filter a list of items based on a query.
101
+ * It provides a way to search through the items and returns the filtered list.
102
+ * The hook uses a deferred value to optimize performance during transitions.
103
+ * It also allows for a custom matcher function to define how items should be matched against the query.
104
+ *
105
+ * @param items - The list of items to filter.
106
+ * @param matcher - An optional function to match items against the query.
107
+ * @returns An object containing the search function, current query, filtered list, and a boolean indicating if a search is in progress.
108
+ *
109
+ * @example
110
+ * ```jsx
111
+ * useFilteredList(items, (item, query) => {
112
+ * return item.name.includes(query);
113
+ * });
114
+ * ```
115
+ *
116
+ * @see {@link https://adarshpastakia.github.io/react-fabric/?path=/docs/data-usefilteredlist--docs}
117
+ */
118
+ export function useFilteredList<
119
+ T extends AnyObject = KeyValue,
120
+ Q extends AnyObject = string,
121
+ >(items: T[], matcher?: (item: T, query: Q) => boolean): RetType<T, Q>;
122
+
54
123
  /**
55
124
  * Custom hook to filter a list of items based on a query.
56
125
  * It provides a way to search through the items and returns the filtered list.
@@ -58,6 +127,7 @@ const filterList = (
58
127
  * It also allows for a custom matcher function to define how items should be matched against the query.
59
128
  *
60
129
  * @param items - The list of items to filter.
130
+ * @param matchAll - Match all phrases in the query list against the items.
61
131
  * @param matcher - An optional function to match items against the query.
62
132
  * @returns An object containing the search function, current query, filtered list, and a boolean indicating if a search is in progress.
63
133
  *
@@ -70,28 +140,39 @@ const filterList = (
70
140
  *
71
141
  * @see {@link https://adarshpastakia.github.io/react-fabric/?path=/docs/data-usefilteredlist--docs}
72
142
  */
73
- export const useFilteredList = <
143
+ export function useFilteredList<
74
144
  T extends AnyObject = KeyValue,
75
145
  Q extends AnyObject = string,
76
146
  >(
77
147
  items: T[],
148
+ matchAll?: boolean,
78
149
  matcher?: (item: T, query: Q) => boolean,
79
- ) => {
150
+ ): RetType<T, Q>;
151
+
152
+ export function useFilteredList<
153
+ T extends AnyObject = KeyValue,
154
+ Q extends AnyObject = string,
155
+ >(items: T[], matchAll?: AnyObject, matcher?: (item: T, query: Q) => boolean) {
80
156
  const [query, setQuery] = useState<Q>();
81
157
  const _items = useDeferredValue(items ?? []);
82
158
  const [filteredList, setFilteredList] = useState<T[]>([]);
83
159
  const [isSearching, startTransition] = useTransition();
84
160
 
161
+ const matchingAll = typeof matchAll === "boolean" ? matchAll : true;
162
+ const matcherFn = typeof matchAll === "function" ? matchAll : matcher;
163
+
85
164
  const filterItems = useCallback(
86
- (items: T[], query?: Q) =>
87
- startTransition(() => setFilteredList(filterList(items, query, matcher))),
165
+ (items: T[], query?: Q, matchingAll?: boolean) =>
166
+ startTransition(() =>
167
+ setFilteredList(filterList(items, query, matchingAll, matcherFn)),
168
+ ),
88
169
  [],
89
170
  );
90
171
 
91
172
  useEffect(() => {
92
- if (query) filterItems(_items, query);
173
+ if (query) filterItems(_items, query, matchingAll);
93
174
  else setFilteredList(items);
94
- }, [query, _items]);
175
+ }, [query, matchingAll, _items]);
95
176
 
96
177
  return { onSearch: setQuery, query, filteredList, isSearching };
97
- };
178
+ }
@@ -45,6 +45,7 @@ import {
45
45
 
46
46
  export const HeaderCell = ({
47
47
  id,
48
+ field,
48
49
  dataType,
49
50
  label,
50
51
  icon,
@@ -99,7 +100,7 @@ export const HeaderCell = ({
99
100
  type={dataType}
100
101
  list={filterOptions}
101
102
  filter={filter}
102
- onFilter={(value) => onFilter?.(id, value)}
103
+ onFilter={(value) => onFilter?.(field ?? id, value)}
103
104
  />
104
105
  </Menu>,
105
106
  );
@@ -131,7 +132,7 @@ export const HeaderCell = ({
131
132
  type={dataType}
132
133
  list={filterOptions}
133
134
  filter={filter}
134
- onFilter={(value) => onFilter?.(id, value)}
135
+ onFilter={(value) => onFilter?.(field ?? id, value)}
135
136
  />,
136
137
  ];
137
138
  }
@@ -82,6 +82,8 @@ export const Table = <T extends KeyValue = KeyValue>({
82
82
  onScroll,
83
83
  onRowClick,
84
84
  onCheckedChanged,
85
+ total = 0,
86
+ onLoadMore,
85
87
  }: TableProps<T>) => {
86
88
  const { t } = useTranslation("data");
87
89
  const fireCheckChanged = useDebounce(
@@ -109,6 +111,9 @@ export const Table = <T extends KeyValue = KeyValue>({
109
111
  checked,
110
112
  keyProperty,
111
113
  groupProperty,
114
+ total,
115
+ loading,
116
+ onLoadMore,
112
117
  onCheckChanged: fireCheckChanged,
113
118
  });
114
119
  const refBody = useRef<HTMLDivElement>(null);
@@ -179,7 +184,7 @@ export const Table = <T extends KeyValue = KeyValue>({
179
184
  >
180
185
  <div
181
186
  data-ref="tableHeader"
182
- className="bg-base font-medium sticky top-0 h-8 area-head border-b select-none flex flex-nowrap z-2"
187
+ className="bg-base font-medium sticky top-0 h-8 area-head border-b select-none flex flex-nowrap z-3"
183
188
  >
184
189
  <div className={wrapperStart}>
185
190
  {canExpand && <div className="w-6" />}
@@ -247,7 +252,7 @@ export const Table = <T extends KeyValue = KeyValue>({
247
252
  ref={measureElement}
248
253
  data-index={index}
249
254
  className={classNames(
250
- "px-2 py-1 text-md bg-base border-y border-tint-100 cursor-pointer flex items-center gap-1 z-1",
255
+ "px-2 py-1 text-md bg-base border-y border-tint-100 cursor-pointer flex items-center gap-1 z-2",
251
256
  isActiveSticky(index) && "sticky top-8",
252
257
  )}
253
258
  role="none"
@@ -67,6 +67,7 @@ export interface TableColumn<T = KeyValue>
67
67
  IconProps,
68
68
  TestProps {
69
69
  id: keyof T | string;
70
+ field?: string;
70
71
  label?: string;
71
72
  tooltip?: string;
72
73
  actions?: ReactElement[];
@@ -134,4 +135,7 @@ export interface TableProps<T = KeyValue> extends RefProp<TableRef> {
134
135
  children?: (record: T) => Elements<AnyObject>;
135
136
 
136
137
  onFilter?: (id: keyof T | string, value?: AnyObject) => void;
138
+
139
+ total?: number;
140
+ onLoadMore?: (lastIndex?: number) => void;
137
141
  }
@@ -21,7 +21,7 @@
21
21
  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
  */
23
23
 
24
- import { compareValues, groupBy } from "@react-fabric/utilities";
24
+ import { compareValues, debounce, groupBy } from "@react-fabric/utilities";
25
25
  import {
26
26
  defaultRangeExtractor,
27
27
  type Range,
@@ -60,6 +60,9 @@ export const useVirtualTable = <T extends KeyValue = KeyValue>(
60
60
  checked?: AnyObject[];
61
61
  keyProperty: keyof T;
62
62
  groupProperty?: keyof T;
63
+ loading?: boolean;
64
+ total: number;
65
+ onLoadMore?: (startIndex: number) => void;
63
66
  onCheckChanged?: (checked: AnyObject[]) => void;
64
67
  },
65
68
  ) => {
@@ -202,6 +205,26 @@ export const useVirtualTable = <T extends KeyValue = KeyValue>(
202
205
  dispatch({ type: "updateState", checked: options.checked ?? [] });
203
206
  }, [options.checked]);
204
207
 
208
+ useEffect(() => {
209
+ if (options.onLoadMore) {
210
+ const firstItem = virtualItems[0]?.index;
211
+ const lastItem = virtualItems?.slice(-1).pop()?.index;
212
+ const canLoadPrevPage =
213
+ firstItem !== undefined && state.items[firstItem] === null;
214
+ const canLoadNextPage =
215
+ lastItem !== undefined &&
216
+ lastItem === state.items.length - 1 &&
217
+ state.items.length < options.total;
218
+ const db = debounce(options.onLoadMore);
219
+ if (canLoadPrevPage) {
220
+ db(firstItem);
221
+ } else if (!options.loading && canLoadNextPage) {
222
+ db(lastItem + 1);
223
+ }
224
+ return () => db.cancel();
225
+ }
226
+ }, [options.onLoadMore, virtualItems]);
227
+
205
228
  return {
206
229
  scrollerRef,
207
230
  items: virtualItems,
@@ -97,11 +97,7 @@ export const TreeNode = ({
97
97
  <div
98
98
  role="treeitem"
99
99
  aria-selected={node.selected}
100
- className={classNames(
101
- "flex flex-nowrap items-center",
102
- node.className,
103
- node.leaf ? leafClassName : nodeClassName,
104
- )}
100
+ className={classNames("flex flex-nowrap items-center")}
105
101
  data-testid={node["data-testid"]}
106
102
  data-test-value={node["data-test-value"]}
107
103
  >
@@ -147,6 +143,8 @@ export const TreeNode = ({
147
143
  "group/tool data-[selected]:bg-primary-100 flex flex-nowrap flex-1 overflow-hidden select-none",
148
144
  node.childSelected && "font-medium",
149
145
  node.disabled && "text-muted opacity-65",
146
+ node.className,
147
+ node.leaf ? leafClassName : nodeClassName,
150
148
  !node.disabled &&
151
149
  (canSelect || onClick) &&
152
150
  "hover:bg-primary-50 cursor-pointer",
@@ -86,7 +86,6 @@ export const TreePanel = <T extends KeyValue>({
86
86
  checked,
87
87
  checkable,
88
88
  noLines,
89
- renderer,
90
89
  searchable,
91
90
  selectable,
92
91
  matcher,
@@ -110,6 +109,7 @@ export const TreePanel = <T extends KeyValue>({
110
109
  }: TreePanelProps<T>) => {
111
110
  const {
112
111
  tree,
112
+ query,
113
113
  toggleExpand,
114
114
  toggleCheck,
115
115
  expandAll,
@@ -167,6 +167,7 @@ export const TreePanel = <T extends KeyValue>({
167
167
  >
168
168
  {searchable && (
169
169
  <Search
170
+ value={query}
170
171
  onSearch={onFilter}
171
172
  allowClear
172
173
  placeholder={filterPlaceholder}
@@ -216,8 +217,7 @@ export const TreePanel = <T extends KeyValue>({
216
217
  leafClassName={leafClassName}
217
218
  nodeClassName={nodeClassName}
218
219
  >
219
- {(tree[index].data && renderer?.(tree[index].data)) ??
220
- makeLabel?.(tree[index].data) ??
220
+ {(tree[index].data && makeLabel?.(tree[index].data)) ??
221
221
  tree[index].label}
222
222
  </TreeNode>
223
223
  </div>
package/src/tree/types.ts CHANGED
@@ -21,6 +21,7 @@
21
21
  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
  */
23
23
 
24
+ import { AvatarProps } from "@react-fabric/core/dist/types/components/avatar/Avatar";
24
25
  import {
25
26
  type BadgeType,
26
27
  type ChildProp,
@@ -56,7 +57,7 @@ interface TreeBaseNode<T> extends Omit<IconProps, "icon">, CssProp, TestProps {
56
57
  disabled?: boolean;
57
58
  data?: T;
58
59
 
59
- icon?: ElementType<IconProps> | string;
60
+ icon?: Elements<JSX.Element>;
60
61
  }
61
62
 
62
63
  export type TreeNodeType<T extends KeyValue = KeyValue> = TreeBaseNode<T> &
@@ -90,7 +91,7 @@ export interface InternalNode
90
91
  loaded: boolean;
91
92
  parent?: string;
92
93
  empty?: boolean;
93
- icon?: ElementType<IconProps> | string;
94
+ icon?: Elements<JSX.Element>;
94
95
  errored?: string;
95
96
  selected?: true;
96
97
  childSelected?: true;
@@ -103,8 +104,8 @@ export interface InternalNode
103
104
  export interface TreeNodeProps extends ChildProp {
104
105
  node: InternalNode;
105
106
  expanders: [string, string];
106
- defaultNodeIcon?: string;
107
- defaultLeafIcon?: string;
107
+ defaultNodeIcon?: Elements<JSX.Element>;
108
+ defaultLeafIcon?: Elements<JSX.Element>;
108
109
  noLines?: boolean;
109
110
  selectable?: true | "leafOnly";
110
111
  checkable?: true | "leafOnly";
@@ -185,23 +186,22 @@ export interface TreePanelProps<T extends KeyValue = KeyValue>
185
186
  /**
186
187
  * callback on selection of tree node
187
188
  */
188
- onSelect?: (id: string, data: T) => void;
189
+ onSelect?: (id: string, data: T) => boolean | undefined;
189
190
  /**
190
191
  * callback on change of checked list
192
+ * @param leafs - list of checked leaf nodes
193
+ * @param nodes - list of checked nodes
194
+ * @param partials - list of partially checked nodes
191
195
  */
192
- onChecked?: (leafs: string[], nodes: string[]) => void;
193
- /**
194
- * item label renderer
195
- */
196
- renderer?: (data: T) => JSX.Element;
196
+ onChecked?: (leafs: string[], nodes: string[], partials: string[]) => void;
197
197
  /**
198
198
  * item filter matcher
199
199
  */
200
200
  matcher?: (data: T, query: string) => boolean;
201
201
 
202
202
  sorter?: false | ((a: T, b: T) => number);
203
- makeLabel?: (node: T) => string;
204
- makeIcon?: (node: T) => string;
203
+ makeLabel?: (node: T) => Elements<JSX.Element>;
204
+ makeIcon?: (node: T) => Elements<JSX.Element>;
205
205
 
206
206
  defaultExpanded?: string[];
207
207
  onExpandToggle?: (id: string[]) => void;
@@ -56,6 +56,10 @@ type TreeActions =
56
56
  * update item check state
57
57
  */
58
58
  | { type: "checked"; id: string[] }
59
+ /**
60
+ * toggle item select, used as action handler
61
+ */
62
+ | { type: "toggleSelect"; id: string }
59
63
  /**
60
64
  * toggle item check, used as action handler
61
65
  */
@@ -85,6 +89,7 @@ interface TreeState {
85
89
  items: InternalNode[];
86
90
  itemMap: Map<string, InternalNode>;
87
91
  tree: InternalNode[];
92
+ query?: string;
88
93
  selected?: string;
89
94
  checked?: string[];
90
95
  }
@@ -151,12 +156,29 @@ export const useTree = <T extends KeyValue>({
151
156
  [onLoad],
152
157
  );
153
158
 
159
+ const fireSelected = useDebounce(
160
+ (nodeId: string, data: T) => {
161
+ componentEvents.current.onSelect?.(nodeId, data);
162
+ },
163
+ [onSelect],
164
+ );
165
+
166
+ const fireChecked = useDebounce(
167
+ (leafs: string[], nodes: string[], partials: string[]) => {
168
+ componentEvents.current.onChecked?.(leafs, nodes, partials);
169
+ },
170
+ [onChecked],
171
+ );
172
+
154
173
  const [state, dispatch] = useReducer(
155
174
  (state: TreeState, action: TreeActions) => {
156
175
  if (action.type === "load") {
157
176
  state.items = refactorTree(action.items, { sorter, defaultExpanded });
158
177
  state.itemMap = makeTreeMap(state.items);
159
178
  state.tree = flattenTree(state.items);
179
+ state.selected = undefined;
180
+ state.checked = undefined;
181
+ state.query = "";
160
182
  return { ...state };
161
183
  }
162
184
  if (action.type === "toggleExpand") {
@@ -172,6 +194,7 @@ export const useTree = <T extends KeyValue>({
172
194
  fireExpandToggle(Array.from(state.itemMap.values()));
173
195
  state.tree = flattenTree(state.items);
174
196
  state.selected && updateSelection(state.itemMap, state.selected, true);
197
+ state.checked?.forEach((id) => updateChecked(state.itemMap, id, 1));
175
198
  return { ...state };
176
199
  }
177
200
  if (action.type === "expand") {
@@ -210,32 +233,49 @@ export const useTree = <T extends KeyValue>({
210
233
  } else {
211
234
  filterTree(state.itemMap, action.query, matcher);
212
235
  state.tree = flattenTree(state.items);
213
- return { ...state };
214
236
  }
237
+ return { ...state, query: action.query };
238
+ }
239
+ if (action.type === "toggleSelect") {
240
+ if (state.selected) {
241
+ updateSelection(state.itemMap, state.selected);
242
+ }
243
+ if (!onSelect) {
244
+ updateSelection(state.itemMap, action.id, true);
245
+ state.selected = action.id;
246
+ state.tree = flattenTree(state.items);
247
+ } else {
248
+ fireSelected(action.id, state.itemMap.get(action.id)?.data);
249
+ }
250
+ return { ...state };
215
251
  }
216
252
  if (action.type === "select") {
217
253
  if (state.selected) {
218
254
  updateSelection(state.itemMap, state.selected);
219
255
  }
220
256
  updateSelection(state.itemMap, action.id, true);
257
+ state.selected = action.id;
221
258
  state.tree = flattenTree(state.items);
222
- componentEvents.current.onSelect?.(
223
- action.id,
224
- state.itemMap.get(action.id)?.data,
225
- );
226
- return { ...state, selected: action.id };
259
+ return { ...state };
227
260
  }
228
261
  if (action.type === "toggleCheck") {
229
262
  updateChecked(state.itemMap, action.id);
230
- const checkList: string[][] = [[], []];
263
+ const checkList: string[][] = [[], [], []];
231
264
  Array.from(state.itemMap.values())
232
- .filter((node) => node.checked === 1)
233
- .forEach((node) => checkList[node.leaf ? 0 : 1].push(node.id));
234
- componentEvents.current.onChecked?.(checkList[0], checkList[1]);
235
- return { ...state };
265
+ .filter((node) => node.checked !== 0)
266
+ .forEach((node) =>
267
+ checkList[node.leaf ? 0 : node.checked].push(node.id),
268
+ );
269
+ fireChecked(
270
+ checkList[0], // leafs
271
+ checkList[1], // nodes
272
+ checkList[2], // partials
273
+ );
274
+ return { ...state, checked: checkList[0] };
236
275
  }
237
276
  if (action.type === "checked") {
238
- action.id.forEach((id) => updateChecked(state.itemMap, id));
277
+ state.itemMap.forEach((item) => (item.checked = 0));
278
+ action.id.forEach((id) => updateChecked(state.itemMap, id, 1));
239
279
  return { ...state, checked: action.id };
240
280
  }
241
281
  return state;
@@ -245,6 +285,7 @@ export const useTree = <T extends KeyValue>({
245
285
  items: [],
246
286
  tree: [],
247
287
  checked: [],
288
+ query: "",
248
289
  },
249
290
  );
250
291
 
@@ -260,6 +301,8 @@ export const useTree = <T extends KeyValue>({
260
301
  type: "expand",
261
302
  id: intialExpand.current.shift() as unknown as string,
262
303
  });
304
+ selected && dispatch({ type: "select", id: selected });
305
+ checked && dispatch({ type: "checked", id: checked });
263
306
  }, [itemList]);
264
307
 
265
308
  useEffect(() => {
@@ -279,7 +322,7 @@ export const useTree = <T extends KeyValue>({
279
322
  }, []);
280
323
 
281
324
  const select = useCallback((id: string) => {
282
- dispatch({ type: "select", id });
325
+ dispatch({ type: "toggleSelect", id });
283
326
  }, []);
284
327
 
285
328
  const expand = useCallback((id: string) => {
@@ -304,6 +347,7 @@ export const useTree = <T extends KeyValue>({
304
347
 
305
348
  return {
306
349
  tree: state.tree,
350
+ query: state.query,
307
351
  toggleExpand,
308
352
  expandAll,
309
353
  collapseAll,
package/src/tree/utils.ts CHANGED
@@ -139,16 +139,21 @@ export const updateSelection = (
139
139
  while (parent) {
140
140
  const parentNode = nodes.get(parent);
141
141
  parentNode && (parentNode.childSelected = selected);
142
- selected && parentNode && (parentNode.open = true);
142
+ // selected && parentNode && (parentNode.open = true);
143
143
  parent = parentNode?.parent;
144
144
  }
145
145
  }
146
146
  };
147
147
 
148
- export const updateChecked = (list: Map<string, AnyObject>, id: string) => {
148
+ export const updateChecked = (
149
+ list: Map<string, AnyObject>,
150
+ id: string,
151
+ checked?: 0 | 1 | 2,
152
+ ) => {
149
153
  const node = list.get(id);
150
154
  if (node) {
151
- node.checked = node.checked === 1 ? 0 : 1;
155
+ node.checked =
156
+ checked === undefined ? (node.checked === 0 ? 1 : 0) : checked;
152
157
  // check all children
153
158
  node.children &&
154
159
  getChildren(node.children).forEach(
@@ -189,7 +189,7 @@ const _VirtualGallery = <T extends AnyObject>({
189
189
 
190
190
  const currentIndex = useRef(0);
191
191
  useEffect(() => {
192
- currentIndex.current = virtualItems[1]?.index ?? 0;
192
+ currentIndex.current = virtualItems[2]?.index ?? 0;
193
193
  if (
194
194
  scrollerRef.current &&
195
195
  virtualItems[0] &&
@@ -159,7 +159,7 @@ const _VirtualList = <T extends AnyObject>({
159
159
 
160
160
  const currentIndex = useRef(0);
161
161
  useEffect(() => {
162
- currentIndex.current = virtualItems[1]?.index ?? 0;
162
+ currentIndex.current = virtualItems[2]?.index ?? 0;
163
163
  if (
164
164
  scrollerRef.current &&
165
165
  virtualItems[0] &&