@react-fabric/data 1.0.0-beta.6 → 1.0.0-beta.60
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.
- package/{src/histogram/Histogram.module.css → css/_histogram.css} +16 -13
- package/css/styles.css +24 -0
- package/dist/cjs/checklist/CheckList.js +23 -0
- package/dist/cjs/histogram/Histogram.js +37 -7
- package/dist/cjs/hooks/useFilteredList.js +88 -10
- package/dist/cjs/hooks/usePagination.js +27 -4
- package/dist/cjs/hooks/useSelectableList.js +29 -0
- package/dist/cjs/i18n/ar.json.js +8 -1
- package/dist/cjs/i18n/en.json.js +8 -1
- package/dist/cjs/json/JsonViewer.js +67 -27
- package/dist/cjs/pagination/Pagination.js +10 -0
- package/dist/cjs/table/AddColumn.js +1 -1
- package/dist/cjs/table/BodyCell.js +5 -4
- package/dist/cjs/table/BodyRow.js +2 -2
- package/dist/cjs/table/CheckboxCell.js +1 -1
- package/dist/cjs/table/ColumnFilters.js +97 -0
- package/dist/cjs/table/HeaderCell.js +71 -43
- package/dist/cjs/table/Table.js +58 -12
- package/dist/cjs/table/types.js +29 -0
- package/dist/cjs/table/useResizer.js +4 -4
- package/dist/cjs/table/useVirtualTable.js +127 -19
- package/dist/cjs/tree/TreeNode.js +48 -30
- package/dist/cjs/tree/TreePanel.js +45 -11
- package/dist/cjs/tree/useTree.js +199 -38
- package/dist/cjs/tree/utils.js +39 -12
- package/dist/cjs/virtual/Gallery.js +97 -45
- package/dist/cjs/virtual/List.js +93 -39
- package/dist/cjs/virtual/Scroller.js +16 -8
- package/dist/esm/checklist/CheckList.js +23 -0
- package/dist/esm/histogram/Histogram.js +38 -8
- package/dist/esm/hooks/useFilteredList.js +89 -11
- package/dist/esm/hooks/usePagination.js +27 -4
- package/dist/esm/hooks/useSelectableList.js +29 -0
- package/dist/esm/i18n/ar.json.js +8 -2
- package/dist/esm/i18n/en.json.js +8 -2
- package/dist/esm/json/JsonViewer.js +68 -28
- package/dist/esm/pagination/Pagination.js +10 -0
- package/dist/esm/table/AddColumn.js +1 -1
- package/dist/esm/table/BodyCell.js +5 -4
- package/dist/esm/table/BodyRow.js +2 -2
- package/dist/esm/table/CheckboxCell.js +1 -1
- package/dist/esm/table/ColumnFilters.js +97 -0
- package/dist/esm/table/HeaderCell.js +72 -44
- package/dist/esm/table/Table.js +59 -13
- package/dist/esm/table/types.js +29 -0
- package/dist/esm/table/useResizer.js +4 -4
- package/dist/esm/table/useVirtualTable.js +129 -21
- package/dist/esm/tree/TreeNode.js +51 -33
- package/dist/esm/tree/TreePanel.js +46 -12
- package/dist/esm/tree/useTree.js +202 -41
- package/dist/esm/tree/utils.js +40 -13
- package/dist/esm/virtual/Gallery.js +97 -45
- package/dist/esm/virtual/List.js +93 -39
- package/dist/esm/virtual/Scroller.js +16 -8
- package/dist/types/checklist/CheckList.d.ts +22 -0
- package/dist/types/histogram/Histogram.d.ts +28 -1
- package/dist/types/hooks/useFilteredList.d.ts +66 -5
- package/dist/types/hooks/usePagination.d.ts +19 -0
- package/dist/types/hooks/useSelectableList.d.ts +29 -0
- package/dist/types/json/JsonViewer.d.ts +27 -2
- package/dist/types/pagination/Pagination.d.ts +10 -1
- package/dist/types/table/ColumnFilters.d.ts +9 -0
- package/dist/types/table/HeaderCell.d.ts +1 -1
- package/dist/types/table/Table.d.ts +14 -1
- package/dist/types/table/types.d.ts +44 -5
- package/dist/types/table/useVirtualTable.d.ts +23 -9
- package/dist/types/tree/TreeNode.d.ts +1 -1
- package/dist/types/tree/TreePanel.d.ts +42 -1
- package/dist/types/tree/types.d.ts +65 -19
- package/dist/types/tree/useTree.d.ts +9 -10
- package/dist/types/tree/utils.d.ts +10 -3
- package/dist/types/virtual/Gallery.d.ts +42 -3
- package/dist/types/virtual/List.d.ts +42 -3
- package/package.json +16 -14
- package/src/checklist/CheckList.tsx +22 -0
- package/src/histogram/Histogram.tsx +40 -10
- package/src/hooks/useFilteredList.tsx +109 -9
- package/src/hooks/usePagination.tsx +32 -7
- package/src/hooks/useSelectableList.ts +29 -0
- package/src/i18n/ar.json +5 -0
- package/src/i18n/en.json +5 -0
- package/src/json/JsonViewer.tsx +71 -35
- package/src/pagination/Pagination.tsx +10 -0
- package/src/table/AddColumn.tsx +1 -1
- package/src/table/BodyCell.tsx +6 -4
- package/src/table/BodyRow.tsx +3 -2
- package/src/table/CheckboxCell.tsx +1 -1
- package/src/table/ColumnFilters.tsx +105 -0
- package/src/table/HeaderCell.tsx +72 -18
- package/src/table/Table.tsx +99 -11
- package/src/table/types.ts +46 -8
- package/src/table/useResizer.ts +5 -3
- package/src/table/useVirtualTable.ts +158 -27
- package/src/tree/TreeNode.tsx +98 -58
- package/src/tree/TreePanel.tsx +77 -6
- package/src/tree/types.ts +73 -18
- package/src/tree/useTree.ts +220 -44
- package/src/tree/utils.ts +58 -20
- package/src/virtual/Gallery.tsx +118 -54
- package/src/virtual/List.tsx +99 -39
- package/src/virtual/Scroller.tsx +16 -8
- package/dist/cjs/histogram/Histogram.module.css +0 -1
- package/dist/esm/histogram/Histogram.module.css +0 -1
package/src/tree/useTree.ts
CHANGED
|
@@ -22,10 +22,20 @@
|
|
|
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
|
-
import {
|
|
28
|
-
|
|
27
|
+
import {
|
|
28
|
+
useCallback,
|
|
29
|
+
useEffect,
|
|
30
|
+
useReducer,
|
|
31
|
+
useRef,
|
|
32
|
+
useEffectEvent,
|
|
33
|
+
} from "react";
|
|
34
|
+
import {
|
|
35
|
+
type InternalNode,
|
|
36
|
+
type TreeNodeType,
|
|
37
|
+
type TreePanelProps,
|
|
38
|
+
} from "./types";
|
|
29
39
|
import {
|
|
30
40
|
filterTree,
|
|
31
41
|
flattenTree,
|
|
@@ -52,6 +62,10 @@ type TreeActions =
|
|
|
52
62
|
* update item check state
|
|
53
63
|
*/
|
|
54
64
|
| { type: "checked"; id: string[] }
|
|
65
|
+
/**
|
|
66
|
+
* toggle item select, used as action handler
|
|
67
|
+
*/
|
|
68
|
+
| { type: "toggleSelect"; id: string; shiftKey?: boolean }
|
|
55
69
|
/**
|
|
56
70
|
* toggle item check, used as action handler
|
|
57
71
|
*/
|
|
@@ -71,13 +85,18 @@ type TreeActions =
|
|
|
71
85
|
/**
|
|
72
86
|
* collapse all nodes
|
|
73
87
|
*/
|
|
74
|
-
| { type: "collapseAll" }
|
|
88
|
+
| { type: "collapseAll" }
|
|
89
|
+
/**
|
|
90
|
+
* loaded dynamic nodes
|
|
91
|
+
*/
|
|
92
|
+
| { type: "loaded" };
|
|
75
93
|
|
|
76
94
|
interface TreeState {
|
|
77
95
|
items: InternalNode[];
|
|
78
96
|
itemMap: Map<string, InternalNode>;
|
|
79
97
|
tree: InternalNode[];
|
|
80
|
-
|
|
98
|
+
query?: string;
|
|
99
|
+
selected?: AnyObject;
|
|
81
100
|
checked?: string[];
|
|
82
101
|
}
|
|
83
102
|
|
|
@@ -87,47 +106,139 @@ export const useTree = <T extends KeyValue>({
|
|
|
87
106
|
items = EMPTY_ARRAY,
|
|
88
107
|
checked = EMPTY_ARRAY,
|
|
89
108
|
selected,
|
|
109
|
+
multiple,
|
|
110
|
+
sorter,
|
|
90
111
|
matcher,
|
|
112
|
+
onLoad,
|
|
113
|
+
onQuery,
|
|
91
114
|
onSelect,
|
|
92
115
|
onChecked,
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
onChecked?: (leafs: string[], nodes: string[]) => void;
|
|
99
|
-
matcher?: (data: T, query: string) => boolean;
|
|
116
|
+
defaultExpanded,
|
|
117
|
+
onExpandToggle,
|
|
118
|
+
}: Partial<TreePanelProps<T>> & {
|
|
119
|
+
selected?: AnyObject;
|
|
120
|
+
multiple?: boolean;
|
|
100
121
|
}) => {
|
|
101
|
-
const
|
|
102
|
-
|
|
122
|
+
const eventSelect = useEffectEvent(onSelect ?? (() => undefined));
|
|
123
|
+
const eventChecked = useEffectEvent(onChecked ?? (() => undefined));
|
|
124
|
+
const intialExpand = useRef([...(defaultExpanded ?? EMPTY_ARRAY)]);
|
|
103
125
|
const itemList = createItemList(items);
|
|
104
126
|
|
|
127
|
+
const fireExpandToggle = useCallback(
|
|
128
|
+
(itemMap: InternalNode[]) => {
|
|
129
|
+
const expandedList = itemMap
|
|
130
|
+
.filter((node) => node.open)
|
|
131
|
+
.map((node) => node.id);
|
|
132
|
+
setTimeout(() => onExpandToggle?.(expandedList), 100);
|
|
133
|
+
},
|
|
134
|
+
[onExpandToggle],
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
const loadChildren = useCallback(
|
|
138
|
+
(node: InternalNode) => {
|
|
139
|
+
if (node.open && !node.leaf && !node.children?.length) {
|
|
140
|
+
node.loading = true;
|
|
141
|
+
// delay onload callback to avoid state issues
|
|
142
|
+
setTimeout(() => {
|
|
143
|
+
void Promise.resolve(onLoad?.(node.id))
|
|
144
|
+
.then((resp = EMPTY_ARRAY) => {
|
|
145
|
+
node.children = refactorTree(
|
|
146
|
+
resp,
|
|
147
|
+
{ sorter, defaultExpanded },
|
|
148
|
+
{ level: node.level + 1, parent: node.id },
|
|
149
|
+
);
|
|
150
|
+
})
|
|
151
|
+
.finally(() => {
|
|
152
|
+
node.loading = false;
|
|
153
|
+
dispatch({ type: "loaded" });
|
|
154
|
+
intialExpand.current?.length > 0 &&
|
|
155
|
+
dispatch({
|
|
156
|
+
type: "expand",
|
|
157
|
+
id: intialExpand.current.shift() as unknown as string,
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
}, 100);
|
|
161
|
+
} else {
|
|
162
|
+
dispatch({ type: "loaded" });
|
|
163
|
+
intialExpand.current?.length > 0 &&
|
|
164
|
+
dispatch({
|
|
165
|
+
type: "expand",
|
|
166
|
+
id: intialExpand.current.shift() as unknown as string,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
[onLoad],
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
const fireSelected = useDebounce((nodeId: any, data: T) => {
|
|
174
|
+
eventSelect(nodeId, data);
|
|
175
|
+
}, []);
|
|
176
|
+
|
|
177
|
+
const fireChecked = useDebounce(
|
|
178
|
+
(leafs: string[], nodes: string[], partials: string[]) => {
|
|
179
|
+
eventChecked(leafs, nodes, partials);
|
|
180
|
+
},
|
|
181
|
+
[],
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
const fireQuery = useDebounce(
|
|
185
|
+
(query: string) => {
|
|
186
|
+
onQuery?.(query);
|
|
187
|
+
},
|
|
188
|
+
[onQuery],
|
|
189
|
+
);
|
|
190
|
+
|
|
105
191
|
const [state, dispatch] = useReducer(
|
|
106
192
|
(state: TreeState, action: TreeActions) => {
|
|
107
193
|
if (action.type === "load") {
|
|
108
|
-
state.items = refactorTree(action.items);
|
|
194
|
+
state.items = refactorTree(action.items, { sorter, defaultExpanded });
|
|
109
195
|
state.itemMap = makeTreeMap(state.items);
|
|
110
196
|
state.tree = flattenTree(state.items);
|
|
197
|
+
state.selected = undefined;
|
|
198
|
+
state.checked = undefined;
|
|
199
|
+
state.query = "";
|
|
111
200
|
return { ...state };
|
|
112
201
|
}
|
|
113
202
|
if (action.type === "toggleExpand") {
|
|
114
203
|
const node = state.itemMap.get(action.id);
|
|
115
|
-
|
|
204
|
+
if (node && !node.leaf) {
|
|
205
|
+
node && (node.open = !node.open);
|
|
206
|
+
loadChildren(node);
|
|
207
|
+
return { ...state };
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
if (action.type === "loaded") {
|
|
211
|
+
state.itemMap = makeTreeMap(state.items);
|
|
212
|
+
fireExpandToggle(Array.from(state.itemMap.values()));
|
|
116
213
|
state.tree = flattenTree(state.items);
|
|
214
|
+
state.selected = selected;
|
|
215
|
+
updateSelection({
|
|
216
|
+
nodes: state.itemMap,
|
|
217
|
+
multiple,
|
|
218
|
+
selected,
|
|
219
|
+
});
|
|
220
|
+
state.checked?.forEach((id) => updateChecked(state.itemMap, id, 1));
|
|
117
221
|
return { ...state };
|
|
118
222
|
}
|
|
119
223
|
if (action.type === "expand") {
|
|
120
|
-
// TODO: check if children available, if not call onLoad to fetch node children
|
|
121
224
|
const node = state.itemMap.get(action.id);
|
|
122
|
-
node &&
|
|
123
|
-
|
|
124
|
-
|
|
225
|
+
if (node && !node.leaf) {
|
|
226
|
+
node.open = true;
|
|
227
|
+
let parentId = node.parent;
|
|
228
|
+
while (parentId) {
|
|
229
|
+
const node = state.itemMap.get(parentId);
|
|
230
|
+
node && (node.open = true);
|
|
231
|
+
parentId = node?.parent;
|
|
232
|
+
}
|
|
233
|
+
loadChildren(node);
|
|
234
|
+
return { ...state };
|
|
235
|
+
}
|
|
125
236
|
}
|
|
126
237
|
if (action.type === "expandAll") {
|
|
127
|
-
// TODO: prevent opening dynamic nodes that dont have children
|
|
128
238
|
state.itemMap.forEach((node) => {
|
|
129
|
-
!node.leaf && (node.open = true);
|
|
239
|
+
!node.leaf && node.children?.length && (node.open = true);
|
|
130
240
|
});
|
|
241
|
+
fireExpandToggle(Array.from(state.itemMap.values()));
|
|
131
242
|
state.tree = flattenTree(state.items);
|
|
132
243
|
return { ...state };
|
|
133
244
|
}
|
|
@@ -135,34 +246,77 @@ export const useTree = <T extends KeyValue>({
|
|
|
135
246
|
state.itemMap.forEach((node) => {
|
|
136
247
|
!node.leaf && (node.open = false);
|
|
137
248
|
});
|
|
249
|
+
fireExpandToggle([]);
|
|
138
250
|
state.tree = flattenTree(state.items);
|
|
139
251
|
return { ...state };
|
|
140
252
|
}
|
|
141
253
|
if (action.type === "filter") {
|
|
142
|
-
|
|
254
|
+
if (onQuery) {
|
|
255
|
+
// TODO: onQuery should provide new tree listing
|
|
256
|
+
// save current tree state as preQueryState
|
|
257
|
+
// on clearing filter restore preQueryState
|
|
258
|
+
if (!isEmpty(action.query)) {
|
|
259
|
+
// state.prevItems = state.items;
|
|
260
|
+
fireQuery(action.query);
|
|
261
|
+
} else {
|
|
262
|
+
// state.items = state.prevItems;
|
|
263
|
+
// build tree map
|
|
264
|
+
}
|
|
265
|
+
} else {
|
|
266
|
+
filterTree(state.itemMap, action.query, matcher);
|
|
267
|
+
state.tree = flattenTree(state.items);
|
|
268
|
+
}
|
|
269
|
+
return { ...state, query: action.query };
|
|
270
|
+
}
|
|
271
|
+
if (action.type === "toggleSelect") {
|
|
272
|
+
if (multiple) {
|
|
273
|
+
const isSelected = state.selected?.includes(action.id);
|
|
274
|
+
if (action.shiftKey && isSelected)
|
|
275
|
+
state.selected =
|
|
276
|
+
state.selected?.filter((id: string) => id !== action.id) ?? [];
|
|
277
|
+
else if (action.shiftKey && !isSelected)
|
|
278
|
+
state.selected = [...(state.selected ?? []), action.id];
|
|
279
|
+
else state.selected = [action.id];
|
|
280
|
+
} else {
|
|
281
|
+
state.selected = action.id;
|
|
282
|
+
}
|
|
283
|
+
updateSelection({
|
|
284
|
+
nodes: state.itemMap,
|
|
285
|
+
selected: state.selected,
|
|
286
|
+
multiple,
|
|
287
|
+
});
|
|
143
288
|
state.tree = flattenTree(state.items);
|
|
289
|
+
fireSelected(state.selected, state.itemMap.get(action.id)?.data);
|
|
144
290
|
return { ...state };
|
|
145
291
|
}
|
|
146
292
|
if (action.type === "select") {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
293
|
+
state.selected = action.id;
|
|
294
|
+
updateSelection({
|
|
295
|
+
nodes: state.itemMap,
|
|
296
|
+
multiple,
|
|
297
|
+
selected: action.id,
|
|
298
|
+
});
|
|
151
299
|
state.tree = flattenTree(state.items);
|
|
152
|
-
|
|
153
|
-
return { ...state, selected: action.id };
|
|
300
|
+
return { ...state };
|
|
154
301
|
}
|
|
155
302
|
if (action.type === "toggleCheck") {
|
|
156
303
|
updateChecked(state.itemMap, action.id);
|
|
157
|
-
const checkList: string[][] = [[], []];
|
|
304
|
+
const checkList: string[][] = [[], [], []];
|
|
158
305
|
Array.from(state.itemMap.values())
|
|
159
|
-
.filter((node) => node.checked
|
|
160
|
-
.forEach((node) =>
|
|
161
|
-
|
|
162
|
-
|
|
306
|
+
.filter((node) => node.checked !== 0)
|
|
307
|
+
.forEach((node) =>
|
|
308
|
+
checkList[node.leaf ? 0 : node.checked].push(node.id),
|
|
309
|
+
);
|
|
310
|
+
fireChecked(
|
|
311
|
+
checkList[0], // leafs
|
|
312
|
+
checkList[1], // nodes
|
|
313
|
+
checkList[2], // partials
|
|
314
|
+
);
|
|
315
|
+
return { ...state, checked: checkList[0] };
|
|
163
316
|
}
|
|
164
317
|
if (action.type === "checked") {
|
|
165
|
-
|
|
318
|
+
state.itemMap.forEach((item) => (item.checked = 0));
|
|
319
|
+
action.id.forEach((id) => updateChecked(state.itemMap, id, 1));
|
|
166
320
|
return { ...state, checked: action.id };
|
|
167
321
|
}
|
|
168
322
|
return state;
|
|
@@ -172,15 +326,20 @@ export const useTree = <T extends KeyValue>({
|
|
|
172
326
|
items: [],
|
|
173
327
|
tree: [],
|
|
174
328
|
checked: [],
|
|
329
|
+
query: "",
|
|
175
330
|
},
|
|
176
331
|
);
|
|
177
332
|
|
|
178
|
-
useEffect(() => {
|
|
179
|
-
componentEvents.current = { onSelect, onChecked };
|
|
180
|
-
}, [onSelect, onChecked]);
|
|
181
|
-
|
|
182
333
|
useEffect(() => {
|
|
183
334
|
dispatch({ type: "load", items: itemList });
|
|
335
|
+
itemList.length &&
|
|
336
|
+
intialExpand.current?.length > 0 &&
|
|
337
|
+
dispatch({
|
|
338
|
+
type: "expand",
|
|
339
|
+
id: intialExpand.current.shift() as unknown as string,
|
|
340
|
+
});
|
|
341
|
+
selected && dispatch({ type: "select", id: selected });
|
|
342
|
+
checked && dispatch({ type: "checked", id: checked });
|
|
184
343
|
}, [itemList]);
|
|
185
344
|
|
|
186
345
|
useEffect(() => {
|
|
@@ -199,14 +358,24 @@ export const useTree = <T extends KeyValue>({
|
|
|
199
358
|
dispatch({ type: "toggleCheck", id });
|
|
200
359
|
}, []);
|
|
201
360
|
|
|
202
|
-
const select = useCallback((id: string) => {
|
|
203
|
-
dispatch({ type: "
|
|
361
|
+
const select = useCallback((id: string, shiftKey = false) => {
|
|
362
|
+
dispatch({ type: "toggleSelect", id, shiftKey });
|
|
204
363
|
}, []);
|
|
205
364
|
|
|
206
365
|
const expand = useCallback((id: string) => {
|
|
207
366
|
dispatch({ type: "expand", id });
|
|
208
367
|
}, []);
|
|
209
368
|
|
|
369
|
+
const expandAndLoad = useCallback((expandList: string[]) => {
|
|
370
|
+
if (expandList.length > 0) {
|
|
371
|
+
intialExpand.current = [...expandList];
|
|
372
|
+
dispatch({
|
|
373
|
+
type: "expand",
|
|
374
|
+
id: intialExpand.current.shift() as unknown as string,
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
}, []);
|
|
378
|
+
|
|
210
379
|
const expandAll = useCallback(() => {
|
|
211
380
|
dispatch({ type: "expandAll" });
|
|
212
381
|
}, []);
|
|
@@ -215,16 +384,23 @@ export const useTree = <T extends KeyValue>({
|
|
|
215
384
|
dispatch({ type: "collapseAll" });
|
|
216
385
|
}, []);
|
|
217
386
|
|
|
218
|
-
const onFilter = useDebounce(
|
|
219
|
-
|
|
220
|
-
|
|
387
|
+
const onFilter = useDebounce(
|
|
388
|
+
(query: string) => {
|
|
389
|
+
dispatch({ type: "filter", query });
|
|
390
|
+
},
|
|
391
|
+
[],
|
|
392
|
+
300,
|
|
393
|
+
);
|
|
221
394
|
|
|
222
395
|
return {
|
|
223
396
|
tree: state.tree,
|
|
397
|
+
query: state.query,
|
|
398
|
+
selected: state.selected,
|
|
224
399
|
toggleExpand,
|
|
225
400
|
expandAll,
|
|
226
401
|
collapseAll,
|
|
227
402
|
toggleCheck,
|
|
403
|
+
expandAndLoad,
|
|
228
404
|
select,
|
|
229
405
|
expand,
|
|
230
406
|
onFilter,
|
package/src/tree/utils.ts
CHANGED
|
@@ -21,10 +21,15 @@
|
|
|
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 {
|
|
24
|
+
import {
|
|
25
|
+
compareValues,
|
|
26
|
+
isArray,
|
|
27
|
+
isString,
|
|
28
|
+
matchString,
|
|
29
|
+
} from "@react-fabric/utilities";
|
|
25
30
|
import { type InternalNode, type TreeNodeType } from "./types";
|
|
26
31
|
|
|
27
|
-
const
|
|
32
|
+
const defaultSorter = (a: TreeNodeType, b: TreeNodeType) => {
|
|
28
33
|
if (!!a.leaf !== !!b.leaf) return a.leaf ? 1 : -1;
|
|
29
34
|
return compareValues()(a.label, b.label);
|
|
30
35
|
};
|
|
@@ -48,13 +53,17 @@ const getIntermediate = (list: AnyObject[] = []) => {
|
|
|
48
53
|
// convert node item to internal node used by the tree
|
|
49
54
|
export const refactorTree = (
|
|
50
55
|
nodes: TreeNodeType[],
|
|
56
|
+
{
|
|
57
|
+
sorter = defaultSorter,
|
|
58
|
+
defaultExpanded = [],
|
|
59
|
+
}: { sorter?: any; defaultExpanded?: string[] },
|
|
51
60
|
options: { level: number; parent?: string } = { level: 0, parent: undefined },
|
|
52
61
|
) => {
|
|
53
62
|
const list: InternalNode[] = [];
|
|
54
|
-
|
|
55
|
-
nodes.
|
|
63
|
+
if (sorter !== false) nodes = nodes.sort(sorter);
|
|
64
|
+
nodes.forEach((node) => {
|
|
56
65
|
list.push({
|
|
57
|
-
open:
|
|
66
|
+
open: defaultExpanded.includes(node.id) && !!node.children?.length,
|
|
58
67
|
level: options.level,
|
|
59
68
|
parent: options.parent,
|
|
60
69
|
checked: 0,
|
|
@@ -66,10 +75,14 @@ export const refactorTree = (
|
|
|
66
75
|
leaf: !!node.leaf,
|
|
67
76
|
children:
|
|
68
77
|
node.children &&
|
|
69
|
-
refactorTree(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
78
|
+
refactorTree(
|
|
79
|
+
node.children,
|
|
80
|
+
{ sorter, defaultExpanded },
|
|
81
|
+
{
|
|
82
|
+
level: options.level + 1,
|
|
83
|
+
parent: node.id,
|
|
84
|
+
},
|
|
85
|
+
),
|
|
73
86
|
});
|
|
74
87
|
});
|
|
75
88
|
|
|
@@ -106,34 +119,59 @@ export const flattenTree = (
|
|
|
106
119
|
[...lines, idx === filtered.length - 1 ? 0 : 1],
|
|
107
120
|
),
|
|
108
121
|
);
|
|
122
|
+
node.open &&
|
|
123
|
+
node.children?.length === 0 &&
|
|
124
|
+
list.push({
|
|
125
|
+
empty: true,
|
|
126
|
+
leaf: true,
|
|
127
|
+
lines: [...lines, idx === filtered.length - 1 ? 0 : 1, 2],
|
|
128
|
+
} as any);
|
|
109
129
|
});
|
|
110
130
|
return list;
|
|
111
131
|
};
|
|
112
132
|
|
|
113
|
-
|
|
114
|
-
nodes
|
|
115
|
-
id: string,
|
|
116
|
-
selected?: true,
|
|
117
|
-
) => {
|
|
118
|
-
const node = nodes.get(id);
|
|
133
|
+
const selectNode = (nodes: Map<string, InternalNode>, id?: string) => {
|
|
134
|
+
const node = nodes.get(id ?? "");
|
|
119
135
|
if (node) {
|
|
120
136
|
// change selection
|
|
121
|
-
node.selected =
|
|
137
|
+
node.selected = true;
|
|
122
138
|
// update all parent childSelected flag
|
|
123
139
|
let parent = node.parent;
|
|
124
140
|
while (parent) {
|
|
125
141
|
const parentNode = nodes.get(parent);
|
|
126
|
-
parentNode && (parentNode.childSelected =
|
|
127
|
-
selected && parentNode && (parentNode.open = true);
|
|
142
|
+
parentNode && (parentNode.childSelected = true);
|
|
143
|
+
// selected && parentNode && (parentNode.open = true);
|
|
128
144
|
parent = parentNode?.parent;
|
|
129
145
|
}
|
|
130
146
|
}
|
|
131
147
|
};
|
|
132
148
|
|
|
133
|
-
export const
|
|
149
|
+
export const updateSelection = ({
|
|
150
|
+
nodes,
|
|
151
|
+
multiple,
|
|
152
|
+
selected,
|
|
153
|
+
}: {
|
|
154
|
+
nodes: Map<string, InternalNode>;
|
|
155
|
+
multiple?: boolean;
|
|
156
|
+
selected?: string | string[];
|
|
157
|
+
}) => {
|
|
158
|
+
nodes.forEach((node) => {
|
|
159
|
+
node.selected = undefined;
|
|
160
|
+
node.childSelected = undefined;
|
|
161
|
+
});
|
|
162
|
+
multiple && isArray(selected)
|
|
163
|
+
? selected?.forEach((id) => selectNode(nodes, id))
|
|
164
|
+
: selectNode(nodes, selected as string);
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
export const updateChecked = (
|
|
168
|
+
list: Map<string, AnyObject>,
|
|
169
|
+
id: string,
|
|
170
|
+
checked?: 0 | 1 | 2,
|
|
171
|
+
) => {
|
|
134
172
|
const node = list.get(id);
|
|
135
173
|
if (node) {
|
|
136
|
-
node.checked = node.checked ===
|
|
174
|
+
node.checked = checked ?? node.checked === 0 ? 1 : 0;
|
|
137
175
|
// check all children
|
|
138
176
|
node.children &&
|
|
139
177
|
getChildren(node.children).forEach(
|