@react-fabric/data 1.0.0-beta.54 → 1.0.0-beta.56

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 +7 -1
  4. package/dist/cjs/table/useVirtualTable.js +18 -0
  5. package/dist/cjs/tree/TreeNode.js +3 -2
  6. package/dist/cjs/tree/TreePanel.js +18 -4
  7. package/dist/cjs/tree/useTree.js +107 -39
  8. package/dist/cjs/tree/utils.js +5 -4
  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 +7 -1
  14. package/dist/esm/table/useVirtualTable.js +19 -1
  15. package/dist/esm/tree/TreeNode.js +3 -2
  16. package/dist/esm/tree/TreePanel.js +19 -5
  17. package/dist/esm/tree/useTree.js +109 -41
  18. package/dist/esm/tree/utils.js +5 -4
  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 +12 -13
  28. package/dist/types/tree/useTree.d.ts +3 -0
  29. package/dist/types/tree/utils.d.ts +2 -2
  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 +5 -0
  34. package/src/table/types.ts +4 -0
  35. package/src/table/useVirtualTable.ts +24 -1
  36. package/src/tree/TreeNode.tsx +4 -5
  37. package/src/tree/TreePanel.tsx +12 -4
  38. package/src/tree/types.ts +12 -13
  39. package/src/tree/useTree.ts +105 -35
  40. package/src/tree/utils.ts +11 -5
  41. package/src/virtual/Gallery.tsx +1 -1
  42. package/src/virtual/List.tsx +1 -1
@@ -22,7 +22,7 @@
22
22
  */
23
23
 
24
24
  import { useDebounce } from "@react-fabric/core";
25
- import { EMPTY_ARRAY } from "@react-fabric/utilities";
25
+ import { EMPTY_ARRAY, isEmpty } from "@react-fabric/utilities";
26
26
  import memoizeOne from "memoize-one";
27
27
  import { useCallback, useEffect, useReducer, useRef } from "react";
28
28
  import {
@@ -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
  }
@@ -105,7 +110,7 @@ export const useTree = <T extends KeyValue>({
105
110
  onExpandToggle,
106
111
  }: Partial<TreePanelProps<T>>) => {
107
112
  const componentEvents = useRef({ onSelect, onChecked });
108
- const intialExpand = useRef(defaultExpanded ?? EMPTY_ARRAY);
113
+ const intialExpand = useRef([...(defaultExpanded ?? EMPTY_ARRAY)]);
109
114
  const itemList = createItemList(items);
110
115
 
111
116
  const fireExpandToggle = useCallback(
@@ -122,23 +127,26 @@ export const useTree = <T extends KeyValue>({
122
127
  (node: InternalNode) => {
123
128
  if (node.open && !node.leaf && !node.children?.length) {
124
129
  node.loading = true;
125
- void Promise.resolve(onLoad?.(node.id))
126
- .then((resp = EMPTY_ARRAY) => {
127
- node.children = refactorTree(
128
- resp,
129
- { sorter, defaultExpanded },
130
- { level: node.level + 1, parent: node.id },
131
- );
132
- })
133
- .finally(() => {
134
- node.loading = false;
135
- dispatch({ type: "loaded" });
136
- intialExpand.current?.length > 0 &&
137
- dispatch({
138
- type: "expand",
139
- id: intialExpand.current.shift() as unknown as string,
140
- });
141
- });
130
+ // delay onload callback to avoid state issues
131
+ setTimeout(() => {
132
+ void Promise.resolve(onLoad?.(node.id))
133
+ .then((resp = EMPTY_ARRAY) => {
134
+ node.children = refactorTree(
135
+ resp,
136
+ { sorter, defaultExpanded },
137
+ { level: node.level + 1, parent: node.id },
138
+ );
139
+ })
140
+ .finally(() => {
141
+ node.loading = false;
142
+ dispatch({ type: "loaded" });
143
+ intialExpand.current?.length > 0 &&
144
+ dispatch({
145
+ type: "expand",
146
+ id: intialExpand.current.shift() as unknown as string,
147
+ });
148
+ });
149
+ }, 100);
142
150
  } else {
143
151
  dispatch({ type: "loaded" });
144
152
  intialExpand.current?.length > 0 &&
@@ -151,12 +159,36 @@ export const useTree = <T extends KeyValue>({
151
159
  [onLoad],
152
160
  );
153
161
 
162
+ const fireSelected = useDebounce(
163
+ (nodeId: string, data: T) => {
164
+ componentEvents.current.onSelect?.(nodeId, data);
165
+ },
166
+ [onSelect],
167
+ );
168
+
169
+ const fireChecked = useDebounce(
170
+ (leafs: string[], nodes: string[], partials: string[]) => {
171
+ componentEvents.current.onChecked?.(leafs, nodes, partials);
172
+ },
173
+ [onChecked],
174
+ );
175
+
176
+ const fireQuery = useDebounce(
177
+ (query: string) => {
178
+ onQuery?.(query);
179
+ },
180
+ [onQuery],
181
+ );
182
+
154
183
  const [state, dispatch] = useReducer(
155
184
  (state: TreeState, action: TreeActions) => {
156
185
  if (action.type === "load") {
157
186
  state.items = refactorTree(action.items, { sorter, defaultExpanded });
158
187
  state.itemMap = makeTreeMap(state.items);
159
188
  state.tree = flattenTree(state.items);
189
+ state.selected = undefined;
190
+ state.checked = undefined;
191
+ state.query = "";
160
192
  return { ...state };
161
193
  }
162
194
  if (action.type === "toggleExpand") {
@@ -171,7 +203,8 @@ export const useTree = <T extends KeyValue>({
171
203
  state.itemMap = makeTreeMap(state.items);
172
204
  fireExpandToggle(Array.from(state.itemMap.values()));
173
205
  state.tree = flattenTree(state.items);
174
- state.selected && updateSelection(state.itemMap, state.selected, true);
206
+ state.selected = updateSelection(state.itemMap, selected, true);
207
+ state.checked?.forEach((id) => updateChecked(state.itemMap, id, 1));
175
208
  return { ...state };
176
209
  }
177
210
  if (action.type === "expand") {
@@ -206,36 +239,60 @@ export const useTree = <T extends KeyValue>({
206
239
  }
207
240
  if (action.type === "filter") {
208
241
  if (onQuery) {
209
- onQuery(action.query ?? "");
242
+ // TODO: onQuery should provide new tree listing
243
+ // save current tree state as preQueryState
244
+ // on clearing filter restore preQueryState
245
+ if (!isEmpty(action.query)) {
246
+ // state.prevItems = state.items;
247
+ fireQuery(action.query);
248
+ } else {
249
+ // state.items = state.prevItems;
250
+ // build tree map
251
+ }
210
252
  } else {
211
253
  filterTree(state.itemMap, action.query, matcher);
212
254
  state.tree = flattenTree(state.items);
213
- return { ...state };
214
255
  }
256
+ return { ...state, query: action.query };
257
+ }
258
+ if (action.type === "toggleSelect") {
259
+ if (!onSelect) {
260
+ if (state.selected) {
261
+ updateSelection(state.itemMap, state.selected);
262
+ }
263
+ state.selected = updateSelection(state.itemMap, action.id, true);
264
+ state.tree = flattenTree(state.items);
265
+ } else {
266
+ fireSelected(action.id, state.itemMap.get(action.id)?.data);
267
+ }
268
+ return { ...state };
215
269
  }
216
270
  if (action.type === "select") {
217
271
  if (state.selected) {
218
272
  updateSelection(state.itemMap, state.selected);
219
273
  }
220
- updateSelection(state.itemMap, action.id, true);
274
+ state.selected = updateSelection(state.itemMap, action.id, true);
221
275
  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 };
276
+ return { ...state };
227
277
  }
228
278
  if (action.type === "toggleCheck") {
229
279
  updateChecked(state.itemMap, action.id);
230
- const checkList: string[][] = [[], []];
280
+ const checkList: string[][] = [[], [], []];
231
281
  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 };
282
+ .filter((node) => node.checked !== 0)
283
+ .forEach((node) =>
284
+ checkList[node.leaf ? 0 : node.checked].push(node.id),
285
+ );
286
+ fireChecked(
287
+ checkList[0], // leafs
288
+ checkList[1], // nodes
289
+ checkList[2], // partials
290
+ );
291
+ return { ...state, checked: checkList[0] };
236
292
  }
237
293
  if (action.type === "checked") {
238
- action.id.forEach((id) => updateChecked(state.itemMap, id));
294
+ state.itemMap.forEach((item) => (item.checked = 0));
295
+ action.id.forEach((id) => updateChecked(state.itemMap, id, 1));
239
296
  return { ...state, checked: action.id };
240
297
  }
241
298
  return state;
@@ -245,6 +302,7 @@ export const useTree = <T extends KeyValue>({
245
302
  items: [],
246
303
  tree: [],
247
304
  checked: [],
305
+ query: "",
248
306
  },
249
307
  );
250
308
 
@@ -260,6 +318,8 @@ export const useTree = <T extends KeyValue>({
260
318
  type: "expand",
261
319
  id: intialExpand.current.shift() as unknown as string,
262
320
  });
321
+ selected && dispatch({ type: "select", id: selected });
322
+ checked && dispatch({ type: "checked", id: checked });
263
323
  }, [itemList]);
264
324
 
265
325
  useEffect(() => {
@@ -279,13 +339,20 @@ export const useTree = <T extends KeyValue>({
279
339
  }, []);
280
340
 
281
341
  const select = useCallback((id: string) => {
282
- dispatch({ type: "select", id });
342
+ dispatch({ type: "toggleSelect", id });
283
343
  }, []);
284
344
 
285
345
  const expand = useCallback((id: string) => {
286
346
  dispatch({ type: "expand", id });
287
347
  }, []);
288
348
 
349
+ const expandAndLoad = useCallback((expandList: string[]) => {
350
+ if (expandList.length > 0) {
351
+ intialExpand.current = [...expandList];
352
+ dispatch({ type: "expand", id: intialExpand.current.shift() as string });
353
+ }
354
+ }, []);
355
+
289
356
  const expandAll = useCallback(() => {
290
357
  dispatch({ type: "expandAll" });
291
358
  }, []);
@@ -304,10 +371,13 @@ export const useTree = <T extends KeyValue>({
304
371
 
305
372
  return {
306
373
  tree: state.tree,
374
+ query: state.query,
375
+ selected: state.selected,
307
376
  toggleExpand,
308
377
  expandAll,
309
378
  collapseAll,
310
379
  toggleCheck,
380
+ expandAndLoad,
311
381
  select,
312
382
  expand,
313
383
  onFilter,
package/src/tree/utils.ts CHANGED
@@ -127,10 +127,10 @@ export const flattenTree = (
127
127
 
128
128
  export const updateSelection = (
129
129
  nodes: Map<string, InternalNode>,
130
- id: string,
130
+ id?: string,
131
131
  selected?: true,
132
132
  ) => {
133
- const node = nodes.get(id);
133
+ const node = nodes.get(id ?? "");
134
134
  if (node) {
135
135
  // change selection
136
136
  node.selected = selected;
@@ -139,16 +139,22 @@ 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
+ return id;
145
146
  }
146
147
  };
147
148
 
148
- export const updateChecked = (list: Map<string, AnyObject>, id: string) => {
149
+ export const updateChecked = (
150
+ list: Map<string, AnyObject>,
151
+ id: string,
152
+ checked?: 0 | 1 | 2,
153
+ ) => {
149
154
  const node = list.get(id);
150
155
  if (node) {
151
- node.checked = node.checked === 1 ? 0 : 1;
156
+ node.checked =
157
+ checked === undefined ? (node.checked === 0 ? 1 : 0) : checked;
152
158
  // check all children
153
159
  node.children &&
154
160
  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] &&