@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
@@ -1,3 +1,29 @@
1
+ interface RetType<T, Q> {
2
+ onSearch: (query?: Q) => void;
3
+ query: Q;
4
+ filteredList: T[];
5
+ isSearching: boolean;
6
+ }
7
+ /**
8
+ * Custom hook to filter a list of items based on a query.
9
+ * It provides a way to search through the items and returns the filtered list.
10
+ * The hook uses a deferred value to optimize performance during transitions.
11
+ * It also allows for a custom matcher function to define how items should be matched against the query.
12
+ *
13
+ * @param items - The list of items to filter.
14
+ * @param matchAll - Match all phrases in the query list against the items.
15
+ * @returns An object containing the search function, current query, filtered list, and a boolean indicating if a search is in progress.
16
+ *
17
+ * @example
18
+ * ```jsx
19
+ * useFilteredList(items, (item, query) => {
20
+ * return item.name.includes(query);
21
+ * });
22
+ * ```
23
+ *
24
+ * @see {@link https://adarshpastakia.github.io/react-fabric/?path=/docs/data-usefilteredlist--docs}
25
+ */
26
+ export declare function useFilteredList<T extends AnyObject = KeyValue, Q extends AnyObject = string>(items: T[], matchAll?: boolean): RetType<T, Q>;
1
27
  /**
2
28
  * Custom hook to filter a list of items based on a query.
3
29
  * It provides a way to search through the items and returns the filtered list.
@@ -17,9 +43,26 @@
17
43
  *
18
44
  * @see {@link https://adarshpastakia.github.io/react-fabric/?path=/docs/data-usefilteredlist--docs}
19
45
  */
20
- export declare const useFilteredList: <T extends AnyObject = KeyValue, Q extends AnyObject = string>(items: T[], matcher?: (item: T, query: Q) => boolean) => {
21
- onSearch: import("react").Dispatch<import("react").SetStateAction<Q | undefined>>;
22
- query: Q | undefined;
23
- filteredList: T[];
24
- isSearching: boolean;
25
- };
46
+ export declare function useFilteredList<T extends AnyObject = KeyValue, Q extends AnyObject = string>(items: T[], matcher?: (item: T, query: Q) => boolean): RetType<T, Q>;
47
+ /**
48
+ * Custom hook to filter a list of items based on a query.
49
+ * It provides a way to search through the items and returns the filtered list.
50
+ * The hook uses a deferred value to optimize performance during transitions.
51
+ * It also allows for a custom matcher function to define how items should be matched against the query.
52
+ *
53
+ * @param items - The list of items to filter.
54
+ * @param matchAll - Match all phrases in the query list against the items.
55
+ * @param matcher - An optional function to match items against the query.
56
+ * @returns An object containing the search function, current query, filtered list, and a boolean indicating if a search is in progress.
57
+ *
58
+ * @example
59
+ * ```jsx
60
+ * useFilteredList(items, (item, query) => {
61
+ * return item.name.includes(query);
62
+ * });
63
+ * ```
64
+ *
65
+ * @see {@link https://adarshpastakia.github.io/react-fabric/?path=/docs/data-usefilteredlist--docs}
66
+ */
67
+ export declare function useFilteredList<T extends AnyObject = KeyValue, Q extends AnyObject = string>(items: T[], matchAll?: boolean, matcher?: (item: T, query: Q) => boolean): RetType<T, Q>;
68
+ export {};
@@ -1,2 +1,2 @@
1
1
  import { type TableColumn, type TableProps } from "./types";
2
- export declare const HeaderCell: ({ id, dataType, label, icon, iconBg, iconColor, rtlFlip, actions, locked, filter, filterable, filterOptions, tooltip, sortable, resizable, width: _width, maxWidth, minWidth, sort, onSort, onFilter, }: TableColumn & Pick<TableProps, "sort" | "onSort" | "onFilter">) => import("react/jsx-runtime").JSX.Element;
2
+ export declare const HeaderCell: ({ id, field, dataType, label, icon, iconBg, iconColor, rtlFlip, actions, locked, filter, filterable, filterOptions, tooltip, sortable, resizable, width: _width, maxWidth, minWidth, sort, onSort, onFilter, }: TableColumn & Pick<TableProps, "sort" | "onSort" | "onFilter">) => import("react/jsx-runtime").JSX.Element;
@@ -12,4 +12,4 @@ import { type TableProps } from "./types";
12
12
  * @see {@link https://adarshpastakia.github.io/react-fabric/?path=/story/data-table--table}
13
13
  * @see {@link https://tanstack.com/table/v8/docs/api/virtual}
14
14
  */
15
- export declare const Table: <T extends KeyValue = KeyValue>({ ref, data, columns, keyProperty, groupProperty, checkableRows, initialScroll, hideableColumns, sort, loading, checked, emptyDisplay, groupRenderer, canExpand, children, onSort, onFilter, onScroll, onRowClick, onCheckedChanged, }: TableProps<T>) => import("react/jsx-runtime").JSX.Element;
15
+ export declare const Table: <T extends KeyValue = KeyValue>({ ref, data, columns, keyProperty, groupProperty, checkableRows, initialScroll, hideableColumns, sort, loading, checked, emptyDisplay, groupRenderer, canExpand, children, onSort, onFilter, onScroll, onRowClick, onCheckedChanged, total, onLoadMore, }: TableProps<T>) => import("react/jsx-runtime").JSX.Element;
@@ -33,6 +33,7 @@ export declare const COL_DEFAULT_WIDTH = "12rem";
33
33
  */
34
34
  export interface TableColumn<T = KeyValue> extends CssProp, IconProps, TestProps {
35
35
  id: keyof T | string;
36
+ field?: string;
36
37
  label?: string;
37
38
  tooltip?: string;
38
39
  actions?: ReactElement[];
@@ -91,4 +92,6 @@ export interface TableProps<T = KeyValue> extends RefProp<TableRef> {
91
92
  canExpand?: (record: T) => boolean;
92
93
  children?: (record: T) => Elements<AnyObject>;
93
94
  onFilter?: (id: keyof T | string, value?: AnyObject) => void;
95
+ total?: number;
96
+ onLoadMore?: (lastIndex?: number) => void;
94
97
  }
@@ -11,6 +11,9 @@ export declare const useVirtualTable: <T extends KeyValue = KeyValue>(data: T[],
11
11
  checked?: AnyObject[];
12
12
  keyProperty: keyof T;
13
13
  groupProperty?: keyof T;
14
+ loading?: boolean;
15
+ total: number;
16
+ onLoadMore?: (startIndex: number) => void;
14
17
  onCheckChanged?: (checked: AnyObject[]) => void;
15
18
  }) => {
16
19
  scrollerRef: import("react").RefObject<HTMLDivElement | null>;
@@ -40,4 +40,4 @@ import { type TreePanelProps } from "./types";
40
40
  *
41
41
  * @see {@link https://adarshpastakia.github.io/react-fabric/?path=/docs/data-treepanel--docs}
42
42
  */
43
- export declare const TreePanel: <T extends KeyValue>({ ref, items, selected, checked, checkable, noLines, renderer, searchable, selectable, matcher, filterPlaceholder, defaultLeafIcon, defaultNodeIcon, expander, onChecked, onLoad, onQuery, onSelect, onClick, sorter, makeLabel, makeIcon, defaultExpanded, onExpandToggle, leafClassName, nodeClassName, ...aria }: TreePanelProps<T>) => import("react/jsx-runtime").JSX.Element;
43
+ export declare const TreePanel: <T extends KeyValue>({ ref, items, selected, checked, checkable, noLines, searchable, selectable, matcher, filterPlaceholder, defaultLeafIcon, defaultNodeIcon, expander, onChecked, onLoad, onQuery, onSelect, onClick, sorter, makeLabel, makeIcon, defaultExpanded, onExpandToggle, leafClassName, nodeClassName, ...aria }: TreePanelProps<T>) => import("react/jsx-runtime").JSX.Element;
@@ -1,5 +1,4 @@
1
1
  import { type BadgeType, type ChildProp, type CssProp, type Elements, type IconProps, type RefProp, type TestProps } from "@react-fabric/core/dist/types/types";
2
- import { type ElementType } from "react";
3
2
  interface TreeBaseNode<T> extends Omit<IconProps, "icon">, CssProp, TestProps {
4
3
  id: string;
5
4
  /**
@@ -23,7 +22,7 @@ interface TreeBaseNode<T> extends Omit<IconProps, "icon">, CssProp, TestProps {
23
22
  */
24
23
  disabled?: boolean;
25
24
  data?: T;
26
- icon?: ElementType<IconProps> | string;
25
+ icon?: Elements<JSX.Element>;
27
26
  }
28
27
  export type TreeNodeType<T extends KeyValue = KeyValue> = TreeBaseNode<T> & ({
29
28
  leaf: true;
@@ -51,7 +50,7 @@ export interface InternalNode extends Omit<IconProps, "icon">, CssProp, TestProp
51
50
  loaded: boolean;
52
51
  parent?: string;
53
52
  empty?: boolean;
54
- icon?: ElementType<IconProps> | string;
53
+ icon?: Elements<JSX.Element>;
55
54
  errored?: string;
56
55
  selected?: true;
57
56
  childSelected?: true;
@@ -63,8 +62,8 @@ export interface InternalNode extends Omit<IconProps, "icon">, CssProp, TestProp
63
62
  export interface TreeNodeProps extends ChildProp {
64
63
  node: InternalNode;
65
64
  expanders: [string, string];
66
- defaultNodeIcon?: string;
67
- defaultLeafIcon?: string;
65
+ defaultNodeIcon?: Elements<JSX.Element>;
66
+ defaultLeafIcon?: Elements<JSX.Element>;
68
67
  noLines?: boolean;
69
68
  selectable?: true | "leafOnly";
70
69
  checkable?: true | "leafOnly";
@@ -78,6 +77,7 @@ export interface TreeNodeProps extends ChildProp {
78
77
  export interface TreeRef {
79
78
  select: (id: string) => void;
80
79
  open: (id: string) => void;
80
+ openAndLoad: (list: string[]) => void;
81
81
  }
82
82
  export interface TreePanelProps<T extends KeyValue = KeyValue> extends TestProps, RefProp<TreeRef> {
83
83
  items?: Array<TreeNodeType<T>>;
@@ -136,22 +136,21 @@ export interface TreePanelProps<T extends KeyValue = KeyValue> extends TestProps
136
136
  /**
137
137
  * callback on selection of tree node
138
138
  */
139
- onSelect?: (id: string, data: T) => void;
139
+ onSelect?: (id: string, data: T) => boolean | undefined;
140
140
  /**
141
141
  * callback on change of checked list
142
+ * @param leafs - list of checked leaf nodes
143
+ * @param nodes - list of checked nodes
144
+ * @param partials - list of partially checked nodes
142
145
  */
143
- onChecked?: (leafs: string[], nodes: string[]) => void;
144
- /**
145
- * item label renderer
146
- */
147
- renderer?: (data: T) => JSX.Element;
146
+ onChecked?: (leafs: string[], nodes: string[], partials: string[]) => void;
148
147
  /**
149
148
  * item filter matcher
150
149
  */
151
150
  matcher?: (data: T, query: string) => boolean;
152
151
  sorter?: false | ((a: T, b: T) => number);
153
- makeLabel?: (node: T) => string;
154
- makeIcon?: (node: T) => string;
152
+ makeLabel?: (node: T) => Elements<JSX.Element>;
153
+ makeIcon?: (node: T) => Elements<JSX.Element>;
155
154
  defaultExpanded?: string[];
156
155
  onExpandToggle?: (id: string[]) => void;
157
156
  leafClassName?: string;
@@ -1,10 +1,13 @@
1
1
  import { type InternalNode, type TreePanelProps } from "./types";
2
2
  export declare const useTree: <T extends KeyValue>({ items, checked, selected, sorter, matcher, onLoad, onQuery, onSelect, onChecked, defaultExpanded, onExpandToggle, }: Partial<TreePanelProps<T>>) => {
3
3
  tree: InternalNode[];
4
+ query: string | undefined;
5
+ selected: string | undefined;
4
6
  toggleExpand: (id: string) => void;
5
7
  expandAll: () => void;
6
8
  collapseAll: () => void;
7
9
  toggleCheck: (id: string) => void;
10
+ expandAndLoad: (expandList: string[]) => void;
8
11
  select: (id: string) => void;
9
12
  expand: (id: string) => void;
10
13
  onFilter: {
@@ -8,6 +8,6 @@ export declare const refactorTree: (nodes: TreeNodeType[], { sorter, defaultExpa
8
8
  }) => InternalNode[];
9
9
  export declare const makeTreeMap: (nodes: InternalNode[], map?: Map<any, any>) => Map<any, any>;
10
10
  export declare const flattenTree: (nodes: InternalNode[], lines?: Array<0 | 1 | 2 | 3>) => InternalNode[];
11
- export declare const updateSelection: (nodes: Map<string, InternalNode>, id: string, selected?: true) => void;
12
- export declare const updateChecked: (list: Map<string, AnyObject>, id: string) => void;
11
+ export declare const updateSelection: (nodes: Map<string, InternalNode>, id?: string, selected?: true) => string | undefined;
12
+ export declare const updateChecked: (list: Map<string, AnyObject>, id: string, checked?: 0 | 1 | 2) => void;
13
13
  export declare const filterTree: (nodes: Map<string, InternalNode>, query?: string, matcher?: AnyObject) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-fabric/data",
3
- "version": "1.0.0-beta.54",
3
+ "version": "1.0.0-beta.56",
4
4
  "description": "RUF: Data package",
5
5
  "author": "Adarsh Pastakia <adarshpastakia@outlook.com>",
6
6
  "license": "MIT",
@@ -35,9 +35,9 @@
35
35
  "watch": "npm-run-all -p watch:rollup watch:dts"
36
36
  },
37
37
  "dependencies": {
38
- "@react-fabric/core": "^1.0.0-beta.54",
39
- "@react-fabric/date": "^1.0.0-beta.54",
40
- "@react-fabric/form": "^1.0.0-beta.54",
38
+ "@react-fabric/core": "^1.0.0-beta.55",
39
+ "@react-fabric/date": "^1.0.0-beta.55",
40
+ "@react-fabric/form": "^1.0.0-beta.56",
41
41
  "@react-fabric/utilities": "^1.0.0-beta.52",
42
42
  "@tanstack/react-virtual": "^3.13.9",
43
43
  "classnames": "^2.5.1",
@@ -54,5 +54,5 @@
54
54
  "node": ">=18.12.0",
55
55
  "npm": ">=8.19.0"
56
56
  },
57
- "gitHead": "242f15e3de29d365cce6f41f4ef99ab36bb6b995"
57
+ "gitHead": "fd9dd6cfd1a58efb5c4bfb6446928a78cb4f4266"
58
58
  }
@@ -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);
@@ -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,13 +97,10 @@ 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"]}
103
+ data-id={node.id}
107
104
  >
108
105
  {filler}
109
106
  {node.loading && (
@@ -147,6 +144,8 @@ export const TreeNode = ({
147
144
  "group/tool data-[selected]:bg-primary-100 flex flex-nowrap flex-1 overflow-hidden select-none",
148
145
  node.childSelected && "font-medium",
149
146
  node.disabled && "text-muted opacity-65",
147
+ node.className,
148
+ node.leaf ? leafClassName : nodeClassName,
150
149
  !node.disabled &&
151
150
  (canSelect || onClick) &&
152
151
  "hover:bg-primary-50 cursor-pointer",
@@ -31,7 +31,7 @@ import {
31
31
  } from "@react-fabric/core";
32
32
  import { Search } from "@react-fabric/form";
33
33
  import { useVirtualizer } from "@tanstack/react-virtual";
34
- import { useImperativeHandle, useMemo, useRef } from "react";
34
+ import { useEffect, useImperativeHandle, useMemo, useRef } from "react";
35
35
  import { TreeNode } from "./TreeNode";
36
36
  import { iconCollapseAll, iconExpandAll, type TreePanelProps } from "./types";
37
37
  import { useTree } from "./useTree";
@@ -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,10 +109,13 @@ export const TreePanel = <T extends KeyValue>({
110
109
  }: TreePanelProps<T>) => {
111
110
  const {
112
111
  tree,
112
+ query,
113
+ selected: currentSelected,
113
114
  toggleExpand,
114
115
  toggleCheck,
115
116
  expandAll,
116
117
  collapseAll,
118
+ expandAndLoad,
117
119
  select,
118
120
  expand,
119
121
  onFilter,
@@ -154,11 +156,17 @@ export const TreePanel = <T extends KeyValue>({
154
156
  ref,
155
157
  () => ({
156
158
  open: (id: string) => expand(id),
159
+ openAndLoad: (list: string[]) => expandAndLoad(list),
157
160
  select: (id: string) => select(id),
158
161
  }),
159
162
  [],
160
163
  );
161
164
 
165
+ useEffect(() => {
166
+ const index = tree.findIndex((n) => n.id === currentSelected);
167
+ virtualizer.scrollToIndex(index, { align: "auto" });
168
+ }, [currentSelected]);
169
+
162
170
  return (
163
171
  <Section>
164
172
  <Header
@@ -167,6 +175,7 @@ export const TreePanel = <T extends KeyValue>({
167
175
  >
168
176
  {searchable && (
169
177
  <Search
178
+ value={query}
170
179
  onSearch={onFilter}
171
180
  allowClear
172
181
  placeholder={filterPlaceholder}
@@ -216,8 +225,7 @@ export const TreePanel = <T extends KeyValue>({
216
225
  leafClassName={leafClassName}
217
226
  nodeClassName={nodeClassName}
218
227
  >
219
- {(tree[index].data && renderer?.(tree[index].data)) ??
220
- makeLabel?.(tree[index].data) ??
228
+ {(tree[index].data && makeLabel?.(tree[index].data)) ??
221
229
  tree[index].label}
222
230
  </TreeNode>
223
231
  </div>
package/src/tree/types.ts CHANGED
@@ -30,7 +30,6 @@ import {
30
30
  type RefProp,
31
31
  type TestProps,
32
32
  } from "@react-fabric/core/dist/types/types";
33
- import { type ElementType } from "react";
34
33
 
35
34
  interface TreeBaseNode<T> extends Omit<IconProps, "icon">, CssProp, TestProps {
36
35
  id: string;
@@ -56,7 +55,7 @@ interface TreeBaseNode<T> extends Omit<IconProps, "icon">, CssProp, TestProps {
56
55
  disabled?: boolean;
57
56
  data?: T;
58
57
 
59
- icon?: ElementType<IconProps> | string;
58
+ icon?: Elements<JSX.Element>;
60
59
  }
61
60
 
62
61
  export type TreeNodeType<T extends KeyValue = KeyValue> = TreeBaseNode<T> &
@@ -90,7 +89,7 @@ export interface InternalNode
90
89
  loaded: boolean;
91
90
  parent?: string;
92
91
  empty?: boolean;
93
- icon?: ElementType<IconProps> | string;
92
+ icon?: Elements<JSX.Element>;
94
93
  errored?: string;
95
94
  selected?: true;
96
95
  childSelected?: true;
@@ -103,8 +102,8 @@ export interface InternalNode
103
102
  export interface TreeNodeProps extends ChildProp {
104
103
  node: InternalNode;
105
104
  expanders: [string, string];
106
- defaultNodeIcon?: string;
107
- defaultLeafIcon?: string;
105
+ defaultNodeIcon?: Elements<JSX.Element>;
106
+ defaultLeafIcon?: Elements<JSX.Element>;
108
107
  noLines?: boolean;
109
108
  selectable?: true | "leafOnly";
110
109
  checkable?: true | "leafOnly";
@@ -119,6 +118,7 @@ export interface TreeNodeProps extends ChildProp {
119
118
  export interface TreeRef {
120
119
  select: (id: string) => void;
121
120
  open: (id: string) => void;
121
+ openAndLoad: (list: string[]) => void;
122
122
  }
123
123
 
124
124
  export interface TreePanelProps<T extends KeyValue = KeyValue>
@@ -185,23 +185,22 @@ export interface TreePanelProps<T extends KeyValue = KeyValue>
185
185
  /**
186
186
  * callback on selection of tree node
187
187
  */
188
- onSelect?: (id: string, data: T) => void;
188
+ onSelect?: (id: string, data: T) => boolean | undefined;
189
189
  /**
190
190
  * callback on change of checked list
191
+ * @param leafs - list of checked leaf nodes
192
+ * @param nodes - list of checked nodes
193
+ * @param partials - list of partially checked nodes
191
194
  */
192
- onChecked?: (leafs: string[], nodes: string[]) => void;
193
- /**
194
- * item label renderer
195
- */
196
- renderer?: (data: T) => JSX.Element;
195
+ onChecked?: (leafs: string[], nodes: string[], partials: string[]) => void;
197
196
  /**
198
197
  * item filter matcher
199
198
  */
200
199
  matcher?: (data: T, query: string) => boolean;
201
200
 
202
201
  sorter?: false | ((a: T, b: T) => number);
203
- makeLabel?: (node: T) => string;
204
- makeIcon?: (node: T) => string;
202
+ makeLabel?: (node: T) => Elements<JSX.Element>;
203
+ makeIcon?: (node: T) => Elements<JSX.Element>;
205
204
 
206
205
  defaultExpanded?: string[];
207
206
  onExpandToggle?: (id: string[]) => void;