@react-fabric/data 1.0.0-beta.5 → 1.0.0-beta.51

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 (88) hide show
  1. package/{src/histogram/Histogram.module.css → css/_histogram.css} +16 -13
  2. package/css/styles.css +24 -0
  3. package/dist/cjs/checklist/CheckList.js +25 -16
  4. package/dist/cjs/histogram/Histogram.js +20 -10
  5. package/dist/cjs/hooks/usePagination.js +7 -4
  6. package/dist/cjs/json/JsonViewer.js +40 -27
  7. package/dist/cjs/table/AddColumn.js +1 -1
  8. package/dist/cjs/table/BodyCell.js +38 -5
  9. package/dist/cjs/table/BodyRow.js +107 -0
  10. package/dist/cjs/table/CheckboxCell.js +1 -1
  11. package/dist/cjs/table/ColumnFilters.js +97 -0
  12. package/dist/cjs/table/HeaderCell.js +70 -37
  13. package/dist/cjs/table/Table.js +113 -71
  14. package/dist/cjs/table/useResizer.js +4 -4
  15. package/dist/cjs/table/useVirtualTable.js +122 -18
  16. package/dist/cjs/tree/TreeNode.js +45 -25
  17. package/dist/cjs/tree/TreePanel.js +28 -10
  18. package/dist/cjs/tree/useTree.js +97 -15
  19. package/dist/cjs/tree/utils.js +20 -5
  20. package/dist/cjs/virtual/Gallery.js +51 -35
  21. package/dist/cjs/virtual/List.js +44 -31
  22. package/dist/esm/checklist/CheckList.js +27 -18
  23. package/dist/esm/histogram/Histogram.js +21 -11
  24. package/dist/esm/hooks/usePagination.js +7 -4
  25. package/dist/esm/json/JsonViewer.js +41 -28
  26. package/dist/esm/table/AddColumn.js +1 -1
  27. package/dist/esm/table/BodyCell.js +38 -5
  28. package/dist/esm/table/BodyRow.js +107 -0
  29. package/dist/esm/table/CheckboxCell.js +1 -1
  30. package/dist/esm/table/ColumnFilters.js +97 -0
  31. package/dist/esm/table/HeaderCell.js +71 -38
  32. package/dist/esm/table/Table.js +115 -73
  33. package/dist/esm/table/useResizer.js +4 -4
  34. package/dist/esm/table/useVirtualTable.js +124 -20
  35. package/dist/esm/tree/TreeNode.js +47 -27
  36. package/dist/esm/tree/TreePanel.js +28 -10
  37. package/dist/esm/tree/useTree.js +99 -17
  38. package/dist/esm/tree/utils.js +20 -5
  39. package/dist/esm/virtual/Gallery.js +51 -35
  40. package/dist/esm/virtual/List.js +44 -31
  41. package/dist/types/checklist/CheckList.d.ts +2 -2
  42. package/dist/types/histogram/Histogram.d.ts +3 -4
  43. package/dist/types/index.d.ts +1 -1
  44. package/dist/types/json/JsonViewer.d.ts +0 -1
  45. package/dist/types/table/AddColumn.d.ts +2 -2
  46. package/dist/types/table/BodyCell.d.ts +4 -3
  47. package/dist/types/table/BodyRow.d.ts +20 -0
  48. package/dist/types/table/ColumnFilters.d.ts +9 -0
  49. package/dist/types/table/EmptyCell.d.ts +2 -2
  50. package/dist/types/table/HeaderCell.d.ts +2 -2
  51. package/dist/types/table/Table.d.ts +1 -1
  52. package/dist/types/table/types.d.ts +20 -7
  53. package/dist/types/table/useTableColumns.d.ts +6 -6
  54. package/dist/types/table/useVirtualTable.d.ts +23 -7
  55. package/dist/types/tree/TreeNode.d.ts +1 -1
  56. package/dist/types/tree/TreePanel.d.ts +1 -1
  57. package/dist/types/tree/types.d.ts +25 -5
  58. package/dist/types/tree/useTree.d.ts +2 -9
  59. package/dist/types/tree/utils.d.ts +4 -1
  60. package/dist/types/virtual/Gallery.d.ts +12 -4
  61. package/dist/types/virtual/List.d.ts +8 -4
  62. package/package.json +13 -10
  63. package/src/checklist/CheckList.tsx +81 -77
  64. package/src/histogram/Histogram.tsx +17 -11
  65. package/src/hooks/usePagination.tsx +13 -7
  66. package/src/index.ts +1 -1
  67. package/src/json/JsonViewer.tsx +45 -35
  68. package/src/table/AddColumn.tsx +3 -3
  69. package/src/table/BodyCell.tsx +34 -5
  70. package/src/table/BodyRow.tsx +143 -0
  71. package/src/table/CheckboxCell.tsx +1 -1
  72. package/src/table/ColumnFilters.tsx +105 -0
  73. package/src/table/EmptyCell.tsx +2 -2
  74. package/src/table/HeaderCell.tsx +72 -14
  75. package/src/table/Table.tsx +164 -77
  76. package/src/table/types.ts +24 -6
  77. package/src/table/useResizer.ts +5 -3
  78. package/src/table/useTableColumns.ts +7 -7
  79. package/src/table/useVirtualTable.ts +146 -24
  80. package/src/tree/TreeNode.tsx +98 -56
  81. package/src/tree/TreePanel.tsx +23 -3
  82. package/src/tree/types.ts +32 -4
  83. package/src/tree/useTree.ts +109 -26
  84. package/src/tree/utils.ts +23 -8
  85. package/src/virtual/Gallery.tsx +63 -42
  86. package/src/virtual/List.tsx +44 -32
  87. package/dist/cjs/histogram/Histogram.module.css +0 -1
  88. package/dist/esm/histogram/Histogram.module.css +0 -1
@@ -21,78 +21,200 @@
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 { useVirtualizer } from "@tanstack/react-virtual";
25
- import { useCallback, useReducer, useRef } from "react";
24
+ import { compareValues, groupBy } from "@react-fabric/utilities";
25
+ import {
26
+ defaultRangeExtractor,
27
+ type Range,
28
+ useVirtualizer,
29
+ } from "@tanstack/react-virtual";
30
+ import {
31
+ useCallback,
32
+ useEffect,
33
+ useMemo,
34
+ useReducer,
35
+ useRef,
36
+ useState,
37
+ } from "react";
26
38
 
27
- interface DataState {
28
- checked: Key[];
39
+ interface DataState<T = KeyValue> {
40
+ data: T[];
41
+ items: T[];
42
+ grouped: KeyValue<T[]>;
43
+ groups: AnyObject[];
44
+ expanded: AnyObject[];
45
+ checked: AnyObject[];
29
46
  allChecked: 0 | 1 | 2;
30
47
  }
31
48
 
32
- type DataAction = { type: "check"; key: Key } | { type: "checkall" };
49
+ type DataAction =
50
+ | { type: "updateState"; checked: AnyObject[] }
51
+ | { type: "groupExpand"; key: AnyObject }
52
+ | { type: "reset"; key: AnyObject; data: AnyObject[] }
53
+ | { type: "expand"; key: AnyObject }
54
+ | { type: "check"; key: AnyObject }
55
+ | { type: "checkall" };
33
56
 
34
- export const useVirtualTable = (
35
- data: KeyValue[],
36
- keyProperty?: Key,
37
- onCheckChanged?: (checked: Key[]) => void,
57
+ export const useVirtualTable = <T extends KeyValue = KeyValue>(
58
+ data: T[],
59
+ options: {
60
+ checked?: AnyObject[];
61
+ keyProperty: keyof T;
62
+ groupProperty?: keyof T;
63
+ onCheckChanged?: (checked: AnyObject[]) => void;
64
+ },
38
65
  ) => {
39
66
  // TODO: refactor data into item list, apply grouping if groupColumn provided
40
67
  // TODO: when grouping maintain map of group with children, {___GROUP___:true, label, items, open}
41
68
 
42
- const scrollerRef = useRef<HTMLDivElement>(null);
43
- const virtualizer = useVirtualizer({
44
- count: data.length,
45
- getScrollElement: () => scrollerRef.current,
46
- estimateSize: () => 24,
47
- });
48
-
49
69
  const [state, dispatch] = useReducer(
50
- (state: DataState, action: DataAction) => {
70
+ (state: DataState<T>, action: DataAction) => {
71
+ if (action.type === "updateState") {
72
+ state.checked = action.checked;
73
+ }
74
+ if (action.type === "expand") {
75
+ if (state.expanded.includes(action.key))
76
+ state.expanded.splice(state.expanded.indexOf(action.key), 1);
77
+ else state.expanded.push(action.key);
78
+ }
51
79
  if (action.type === "check") {
52
80
  if (state.checked.includes(action.key))
53
81
  state.checked.splice(state.checked.indexOf(action.key), 1);
54
82
  else state.checked.push(action.key);
83
+ options.onCheckChanged?.(state.checked);
55
84
  }
56
85
  if (action.type === "checkall") {
57
86
  if (state.allChecked !== 0) {
58
87
  state.checked = [];
59
- } else if (keyProperty) {
60
- state.checked = data.map((item) => item[keyProperty]);
88
+ } else if (options.keyProperty) {
89
+ state.checked = state.data.map((item) => item[options.keyProperty]);
90
+ }
91
+ options.onCheckChanged?.(state.checked);
92
+ }
93
+ const checkList = state.data.filter((hit) =>
94
+ state.checked.includes(hit[options.keyProperty]),
95
+ );
96
+ if (action.type === "groupExpand") {
97
+ const group = state.groups.find((group) => group.key === action.key);
98
+ if (group) {
99
+ group.open = !group.open;
100
+ state.groups = state.groups.slice();
101
+ state.items = state.groups
102
+ .map((group) => [
103
+ group,
104
+ group.open ? state.grouped[group.key] : undefined,
105
+ ])
106
+ .flat(2)
107
+ .filter(Boolean) as any;
108
+ }
109
+ }
110
+ if (action.type === "reset") {
111
+ state.data = action.data;
112
+ if (action.key) {
113
+ state.grouped = groupBy<T>(state.data, action.key);
114
+ state.groups = Object.keys(state.grouped)
115
+ .sort(compareValues("asc"))
116
+ .map((group) => ({
117
+ __GROUP__: true,
118
+ open: true,
119
+ key: group,
120
+ items: state.grouped[group],
121
+ itemCount: state.grouped[group].length,
122
+ }));
123
+ state.items = state.groups
124
+ .map((group) => [group, ...state.grouped[group.key]])
125
+ .flat() as any;
126
+ } else {
127
+ state.items = state.data;
61
128
  }
62
129
  }
63
- onCheckChanged?.(state.checked);
64
130
  return {
65
131
  ...state,
66
132
  allChecked:
67
- state.checked.length === data.length
133
+ checkList.length === state.data.length
68
134
  ? 1
69
135
  : state.checked.length
70
136
  ? 2
71
137
  : 0,
72
- } as DataState;
138
+ } as DataState<T>;
73
139
  },
74
140
  {
141
+ data,
142
+ items: [],
143
+ grouped: {},
144
+ groups: [],
75
145
  checked: [],
146
+ expanded: [],
76
147
  allChecked: 0,
77
148
  },
78
149
  );
79
150
 
151
+ const activeStickyIndexRef = useRef(0);
152
+ const stickyIndexes = useMemo<number[]>(
153
+ () =>
154
+ state.items
155
+ .map((hit, idx) => ("__GROUP__" in hit ? idx : undefined))
156
+ .filter(Boolean) as number[],
157
+ [state.items],
158
+ );
159
+ const isActiveSticky = (index: number) =>
160
+ activeStickyIndexRef.current === index;
161
+
162
+ const [hasActiveSticky, setHasActiveSticky] = useState(false);
163
+ const scrollerRef = useRef<HTMLDivElement>(null);
164
+ const virtualizer = useVirtualizer({
165
+ count: state.items.length,
166
+ getScrollElement: () => scrollerRef.current,
167
+ estimateSize: () => 32,
168
+ rangeExtractor: useCallback(
169
+ (range: Range) => {
170
+ activeStickyIndexRef.current =
171
+ [...stickyIndexes]
172
+ .reverse()
173
+ .find((index) => range.startIndex > index) ?? 0;
174
+
175
+ setHasActiveSticky(range.startIndex !== activeStickyIndexRef.current);
176
+
177
+ const next = new Set([
178
+ activeStickyIndexRef.current,
179
+ ...defaultRangeExtractor(range),
180
+ ]);
181
+
182
+ return [...next];
183
+ },
184
+ [stickyIndexes],
185
+ ),
186
+ });
187
+
80
188
  const virtualItems = virtualizer.getVirtualItems();
81
189
 
82
190
  const getData = useCallback(
83
191
  (index: number) => {
84
- return data[index];
192
+ return state.items[index];
85
193
  },
86
- [data],
194
+ [state.items],
87
195
  );
88
196
 
197
+ useEffect(() => {
198
+ dispatch({ type: "reset", key: options.groupProperty, data });
199
+ }, [data, options.groupProperty]);
200
+
201
+ useEffect(() => {
202
+ dispatch({ type: "updateState", checked: options.checked ?? [] });
203
+ }, [options.checked]);
204
+
89
205
  return {
90
206
  scrollerRef,
91
207
  items: virtualItems,
92
208
  checkState: state,
93
209
  getData,
210
+ virtualizer,
211
+ hasActiveSticky,
212
+ isActiveSticky,
213
+ top: virtualizer.range?.startIndex,
94
214
  totalSize: virtualizer.getTotalSize,
95
215
  measureElement: virtualizer.measureElement,
216
+ toggleGroupExpand: (key: Key) => dispatch({ type: "groupExpand", key }),
217
+ toggleExpand: (key: Key) => dispatch({ type: "expand", key }),
96
218
  toggleChecked: (key: Key) => dispatch({ type: "check", key }),
97
219
  toggleAllChecked: () => dispatch({ type: "checkall" }),
98
220
  };
@@ -22,8 +22,10 @@
22
22
  */
23
23
 
24
24
  import { Badge, CoreIcons, getBadgeProps, Icon } from "@react-fabric/core";
25
+ import { isEmpty, isString } from "@react-fabric/utilities";
25
26
  import classNames from "classnames";
26
- import { useMemo } from "react";
27
+ import { isValidElement, useMemo } from "react";
28
+ import { useTranslation } from "react-i18next";
27
29
  import { type TreeNodeProps } from "./types";
28
30
 
29
31
  export const TreeNode = ({
@@ -35,30 +37,43 @@ export const TreeNode = ({
35
37
  defaultLeafIcon,
36
38
  defaultNodeIcon,
37
39
  onToggle,
40
+ onClick,
38
41
  onSelect,
39
42
  onChecked,
40
43
  children,
44
+ leafClassName,
45
+ nodeClassName,
41
46
  }: TreeNodeProps) => {
47
+ const { t } = useTranslation("data");
42
48
  const badgeProps = useMemo(() => {
43
49
  return getBadgeProps(node.badge);
44
50
  }, [node.badge]);
45
51
 
46
52
  const filler = useMemo(() => {
47
- return node.lines.slice(noLines ? 1 : 0).map((line, idx) => (
48
- <svg
53
+ return node.lines.slice(1).map((line, idx) => (
54
+ <div
49
55
  key={idx}
50
- viewBox="0 0 24 24"
51
- className="flex-initial w-6 fill-tint-200"
52
- >
53
- {line === 0 && <path d="" />}
54
- {!noLines && line === 1 && <path d="M7.45 26.1v-28.2h1.1v28.2h-1.1z" />}
55
- {!noLines && line === 2 && (
56
- <path d="M14.989 12.389l0.161-0.389-0.161-0.389-0.389-0.161h-6.050v-13.55h-1.1v14.1l0.161 0.389 0.389 0.161h6.6z" />
57
- )}
58
- {!noLines && line === 3 && (
59
- <path d="M8.55 12.55h6.050l0.389-0.161 0.161-0.389-0.161-0.389-0.389-0.161h-6.050v-13.55h-1.1v28.2h1.1z" />
56
+ className={classNames(
57
+ "flex-initial self-stretch flex items-center w-6 relative",
58
+ !noLines &&
59
+ line > 0 &&
60
+ "before:absolute before:top-0 before:start-[7px] before:bg-tint-200 before:min-h-[50%] before:w-[2px] before:scale-x-[.625]",
61
+ !noLines && line !== 2 && "before:h-full",
60
62
  )}
61
- </svg>
63
+ >
64
+ <svg viewBox="0 0 24 24" className="fill-tint-200 rtl:-scale-x-100">
65
+ {line === 0 && <path d="" />}
66
+ {!noLines && line === 1 && (
67
+ <path d="M7.45 26.1v-28.2h1.1v28.2h-1.1z" />
68
+ )}
69
+ {!noLines && line === 2 && (
70
+ <path d="M14.989 12.389l0.161-0.389-0.161-0.389-0.389-0.161h-6.050v-13.55h-1.1v14.1l0.161 0.389 0.389 0.161h6.6z" />
71
+ )}
72
+ {!noLines && line === 3 && (
73
+ <path d="M8.55 12.55h6.050l0.389-0.161 0.161-0.389-0.161-0.389-0.389-0.161h-6.050v-13.55h-1.1v28.2h1.1z" />
74
+ )}
75
+ </svg>
76
+ </div>
62
77
  ));
63
78
  }, [noLines, node.lines]);
64
79
 
@@ -82,12 +97,21 @@ export const TreeNode = ({
82
97
  <div
83
98
  role="treeitem"
84
99
  aria-selected={node.selected}
85
- className="flex flex-nowrap"
100
+ className={classNames(
101
+ "flex flex-nowrap items-center",
102
+ node.className,
103
+ node.leaf ? leafClassName : nodeClassName,
104
+ )}
86
105
  data-testid={node["data-testid"]}
87
106
  data-test-value={node["data-test-value"]}
88
107
  >
89
108
  {filler}
90
- {!node.leaf && !node.childFiltered && (
109
+ {node.loading && (
110
+ <div className="flex-initial w-6">
111
+ <Icon icon={CoreIcons.spinner} className="animate-spin text-muted" />
112
+ </div>
113
+ )}
114
+ {!node.leaf && !node.childFiltered && !node.loading && (
91
115
  <div className="flex-initial w-6">
92
116
  <Icon
93
117
  rtlFlip
@@ -116,48 +140,66 @@ export const TreeNode = ({
116
140
  />
117
141
  </div>
118
142
  )}
119
- <div
120
- role="none"
121
- className={classNames(
122
- "group data-[selected]:bg-primary-100 flex flex-nowrap flex-1 items-center overflow-hidden select-none",
123
- node.childSelected && "font-medium",
124
- canSelect && "hover:bg-primary-50 cursor-pointer",
125
- )}
126
- data-selected={node.selected}
127
- onClick={() =>
128
- canSelect
129
- ? onSelect(node.id)
130
- : canCheck
131
- ? onChecked(node.id)
132
- : onToggle(node.id)
133
- }
134
- >
135
- {nodeIcon && (
136
- <div className="flex-content w-6">
137
- <Icon
138
- icon={nodeIcon}
139
- bg={node.iconBg}
140
- color={node.iconColor}
141
- rtlFlip={node.rtlFlip}
142
- />
143
+ {!node.empty && (
144
+ <div
145
+ role="none"
146
+ className={classNames(
147
+ "group/tool data-[selected]:bg-primary-100 flex flex-nowrap flex-1 overflow-hidden select-none",
148
+ node.childSelected && "font-medium",
149
+ node.disabled && "text-muted opacity-65",
150
+ !node.disabled &&
151
+ (canSelect || onClick) &&
152
+ "hover:bg-primary-50 cursor-pointer",
153
+ )}
154
+ data-selected={node.selected}
155
+ onClick={
156
+ node.disabled
157
+ ? undefined
158
+ : () => {
159
+ onClick?.(node.id, node.data);
160
+ canSelect
161
+ ? onSelect(node.id)
162
+ : canCheck
163
+ ? onChecked(node.id)
164
+ : onToggle(node.id);
165
+ }
166
+ }
167
+ >
168
+ {nodeIcon && (
169
+ <div className="flex-content w-6 self-center leading-none">
170
+ {isValidElement(nodeIcon) && nodeIcon}
171
+ {isString(nodeIcon) && (
172
+ <Icon
173
+ icon={nodeIcon}
174
+ bg={node.iconBg}
175
+ color={node.iconColor}
176
+ rtlFlip={node.rtlFlip}
177
+ />
178
+ )}
179
+ </div>
180
+ )}
181
+ {node.childSelected && !node.open && (
182
+ <span className=" flex-content text-primary-500">•</span>
183
+ )}
184
+ <div role="none" className="flex-1 truncate">
185
+ {children}
143
186
  </div>
144
- )}
145
- {node.childSelected && !node.open && (
146
- <span className=" flex-content text-primary-500">•</span>
147
- )}
148
- <div role="none" className="flex-1 truncate">
149
- {children}
187
+ {!isEmpty(node.badge) && (
188
+ <Badge
189
+ {...badgeProps}
190
+ className={classNames(
191
+ "me-1 self-center flex-content bg-opacity-50",
192
+ badgeProps.className,
193
+ )}
194
+ />
195
+ )}
150
196
  </div>
151
- {node.badge && (
152
- <Badge
153
- {...badgeProps}
154
- className={classNames(
155
- "me-1 self-center flex-content bg-opacity-50",
156
- badgeProps.className,
157
- )}
158
- />
159
- )}
160
- </div>
197
+ )}
198
+ {node.empty && (
199
+ <div className="text-muted text-sm select-none">
200
+ {t("tree.noItems")}
201
+ </div>
202
+ )}
161
203
  </div>
162
204
  );
163
205
  };
@@ -55,7 +55,16 @@ export const TreePanel = <T extends KeyValue>({
55
55
  expander = "box",
56
56
  onChecked,
57
57
  onLoad,
58
+ onQuery,
58
59
  onSelect,
60
+ onClick,
61
+ sorter,
62
+ makeLabel,
63
+ makeIcon,
64
+ defaultExpanded,
65
+ onExpandToggle,
66
+ leafClassName,
67
+ nodeClassName,
59
68
  ...aria
60
69
  }: TreePanelProps<T>) => {
61
70
  const {
@@ -72,8 +81,13 @@ export const TreePanel = <T extends KeyValue>({
72
81
  checked,
73
82
  selected,
74
83
  matcher,
84
+ sorter,
85
+ onLoad,
86
+ onQuery,
75
87
  onSelect,
76
88
  onChecked,
89
+ defaultExpanded,
90
+ onExpandToggle,
77
91
  });
78
92
 
79
93
  const scrollerRef = useRef<HTMLDivElement>(null);
@@ -99,7 +113,7 @@ export const TreePanel = <T extends KeyValue>({
99
113
  ref,
100
114
  () => ({
101
115
  open: (id: string) => expand(id),
102
- select: (id: string) => expand(id),
116
+ select: (id: string) => select(id),
103
117
  }),
104
118
  [],
105
119
  );
@@ -151,12 +165,18 @@ export const TreePanel = <T extends KeyValue>({
151
165
  checkable={checkable}
152
166
  selectable={selectable}
153
167
  defaultLeafIcon={defaultLeafIcon}
154
- defaultNodeIcon={defaultNodeIcon}
168
+ defaultNodeIcon={
169
+ makeIcon?.(tree[index].data) ?? defaultNodeIcon
170
+ }
155
171
  onToggle={toggleExpand}
156
172
  onChecked={toggleCheck}
157
173
  onSelect={select}
174
+ onClick={onClick}
175
+ leafClassName={leafClassName}
176
+ nodeClassName={nodeClassName}
158
177
  >
159
- {(tree[index].data && renderer?.(tree[index].data)) ||
178
+ {(tree[index].data && renderer?.(tree[index].data)) ??
179
+ makeLabel?.(tree[index].data) ??
160
180
  tree[index].label}
161
181
  </TreeNode>
162
182
  </div>
package/src/tree/types.ts CHANGED
@@ -30,8 +30,9 @@ import {
30
30
  type RefProp,
31
31
  type TestProps,
32
32
  } from "@react-fabric/core/dist/types/types";
33
+ import { type ElementType } from "react";
33
34
 
34
- interface TreeBaseNode<T> extends IconProps, CssProp, TestProps {
35
+ interface TreeBaseNode<T> extends Omit<IconProps, "icon">, CssProp, TestProps {
35
36
  id: string;
36
37
  /**
37
38
  * display label
@@ -54,20 +55,25 @@ interface TreeBaseNode<T> extends IconProps, CssProp, TestProps {
54
55
  */
55
56
  disabled?: boolean;
56
57
  data?: T;
58
+
59
+ icon?: ElementType<IconProps> | string;
57
60
  }
58
61
 
59
62
  export type TreeNodeType<T extends KeyValue = KeyValue> = TreeBaseNode<T> &
60
63
  (
61
64
  | { leaf: true; children?: never }
62
65
  | {
63
- leaf?: false;
66
+ leaf?: boolean;
64
67
  children?: Array<TreeNodeType<T>>;
65
68
  open?: boolean;
66
69
  iconOpen?: string;
67
70
  }
68
71
  );
69
72
 
70
- export interface InternalNode extends IconProps, CssProp, TestProps {
73
+ export interface InternalNode
74
+ extends Omit<IconProps, "icon">,
75
+ CssProp,
76
+ TestProps {
71
77
  id: string;
72
78
  label: Elements<JSX.Element>;
73
79
  queryable?: string;
@@ -84,6 +90,7 @@ export interface InternalNode extends IconProps, CssProp, TestProps {
84
90
  loaded: boolean;
85
91
  parent?: string;
86
92
  empty?: boolean;
93
+ icon?: ElementType<IconProps> | string;
87
94
  errored?: string;
88
95
  selected?: true;
89
96
  childSelected?: true;
@@ -101,9 +108,12 @@ export interface TreeNodeProps extends ChildProp {
101
108
  noLines?: boolean;
102
109
  selectable?: true | "leafOnly";
103
110
  checkable?: true | "leafOnly";
111
+ leafClassName?: string;
112
+ nodeClassName?: string;
104
113
  onToggle: (id: string) => void;
105
114
  onSelect: (id: string) => void;
106
115
  onChecked: (id: string) => void;
116
+ onClick?: (id: string, node: any) => void;
107
117
  }
108
118
 
109
119
  export interface TreeRef {
@@ -164,10 +174,18 @@ export interface TreePanelProps<T extends KeyValue = KeyValue>
164
174
  | Promise<Array<TreeNodeType<T>> | undefined>
165
175
  | Array<TreeNodeType<T>>
166
176
  | undefined;
177
+ /**
178
+ * callback to load tree using search query
179
+ */
180
+ onQuery?: (query: string) => void;
181
+ /**
182
+ * callback on click of tree node
183
+ */
184
+ onClick?: (id: string, data: T) => void;
167
185
  /**
168
186
  * callback on selection of tree node
169
187
  */
170
- onSelect?: (id: string) => void;
188
+ onSelect?: (id: string, data: T) => void;
171
189
  /**
172
190
  * callback on change of checked list
173
191
  */
@@ -180,6 +198,16 @@ export interface TreePanelProps<T extends KeyValue = KeyValue>
180
198
  * item filter matcher
181
199
  */
182
200
  matcher?: (data: T, query: string) => boolean;
201
+
202
+ sorter?: false | ((a: T, b: T) => number);
203
+ makeLabel?: (node: T) => string;
204
+ makeIcon?: (node: T) => string;
205
+
206
+ defaultExpanded?: string[];
207
+ onExpandToggle?: (id: string[]) => void;
208
+
209
+ leafClassName?: string;
210
+ nodeClassName?: string;
183
211
  }
184
212
 
185
213
  export const iconExpandAll =