@react-fabric/data 1.0.0-beta.25 → 1.0.0-beta.27

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.
@@ -5,8 +5,8 @@ import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
5
5
  import { useDebounce } from '@react-fabric/core';
6
6
  import { EMPTY_ARRAY } from '@react-fabric/utilities';
7
7
  import memoizeOne from 'memoize-one';
8
- import { useRef, useReducer, useEffect, useCallback } from 'react';
9
- import { refactorTree, makeTreeMap, flattenTree, filterTree, updateSelection, updateChecked } from './utils.js';
8
+ import { useRef, useCallback, useReducer, useEffect } from 'react';
9
+ import { refactorTree, makeTreeMap, flattenTree, updateSelection, filterTree, updateChecked } from './utils.js';
10
10
 
11
11
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
12
12
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
@@ -19,39 +19,107 @@ var useTree = function useTree(_ref) {
19
19
  _ref$checked = _ref.checked,
20
20
  checked = _ref$checked === void 0 ? EMPTY_ARRAY : _ref$checked,
21
21
  selected = _ref.selected,
22
+ sorter = _ref.sorter,
22
23
  matcher = _ref.matcher,
24
+ onLoad = _ref.onLoad,
23
25
  onSelect = _ref.onSelect,
24
- onChecked = _ref.onChecked;
26
+ onChecked = _ref.onChecked,
27
+ defaultExpanded = _ref.defaultExpanded,
28
+ onExpandToggle = _ref.onExpandToggle;
25
29
  var componentEvents = useRef({
26
30
  onSelect: onSelect,
27
31
  onChecked: onChecked
28
32
  });
33
+ var intialExpand = useRef(defaultExpanded !== null && defaultExpanded !== void 0 ? defaultExpanded : EMPTY_ARRAY);
29
34
  var itemList = createItemList(items);
35
+ var fireExpandToggle = useCallback(function (itemMap) {
36
+ var expandedList = itemMap.filter(function (node) {
37
+ return node.open;
38
+ }).map(function (node) {
39
+ return node.id;
40
+ });
41
+ onExpandToggle === null || onExpandToggle === void 0 || onExpandToggle(expandedList);
42
+ }, [onExpandToggle]);
43
+ var loadChildren = useCallback(function (node) {
44
+ var _node$children;
45
+ if (node.open && !node.leaf && !((_node$children = node.children) !== null && _node$children !== void 0 && _node$children.length)) {
46
+ node.loading = true;
47
+ void Promise.resolve(onLoad === null || onLoad === void 0 ? void 0 : onLoad(node.id)).then(function () {
48
+ var resp = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : EMPTY_ARRAY;
49
+ node.children = refactorTree(resp, {
50
+ sorter: sorter,
51
+ defaultExpanded: defaultExpanded
52
+ }, {
53
+ level: node.level + 1,
54
+ parent: node.id
55
+ });
56
+ })["finally"](function () {
57
+ var _intialExpand$current;
58
+ node.loading = false;
59
+ dispatch({
60
+ type: "loaded"
61
+ });
62
+ ((_intialExpand$current = intialExpand.current) === null || _intialExpand$current === void 0 ? void 0 : _intialExpand$current.length) > 0 && dispatch({
63
+ type: "expand",
64
+ id: intialExpand.current.shift()
65
+ });
66
+ });
67
+ } else {
68
+ var _intialExpand$current2;
69
+ dispatch({
70
+ type: "loaded"
71
+ });
72
+ ((_intialExpand$current2 = intialExpand.current) === null || _intialExpand$current2 === void 0 ? void 0 : _intialExpand$current2.length) > 0 && dispatch({
73
+ type: "expand",
74
+ id: intialExpand.current.shift()
75
+ });
76
+ }
77
+ }, [onLoad]);
30
78
  var _useReducer = useReducer(function (state, action) {
31
79
  if (action.type === "load") {
32
- state.items = refactorTree(action.items);
80
+ state.items = refactorTree(action.items, {
81
+ sorter: sorter,
82
+ defaultExpanded: defaultExpanded
83
+ });
33
84
  state.itemMap = makeTreeMap(state.items);
34
85
  state.tree = flattenTree(state.items);
35
86
  return _objectSpread({}, state);
36
87
  }
37
88
  if (action.type === "toggleExpand") {
38
89
  var node = state.itemMap.get(action.id);
39
- node && (node.open = !node.open);
90
+ if (node && !node.leaf) {
91
+ node && (node.open = !node.open);
92
+ loadChildren(node);
93
+ return _objectSpread({}, state);
94
+ }
95
+ }
96
+ if (action.type === "loaded") {
97
+ state.itemMap = makeTreeMap(state.items);
98
+ fireExpandToggle(Array.from(state.itemMap.values()));
40
99
  state.tree = flattenTree(state.items);
100
+ state.selected && updateSelection(state.itemMap, state.selected, true);
41
101
  return _objectSpread({}, state);
42
102
  }
43
103
  if (action.type === "expand") {
44
- // TODO: check if children available, if not call onLoad to fetch node children
45
104
  var _node = state.itemMap.get(action.id);
46
- _node && (_node.open = true);
47
- state.tree = flattenTree(state.items);
48
- return _objectSpread({}, state);
105
+ if (_node && !_node.leaf) {
106
+ _node.open = true;
107
+ var parentId = _node.parent;
108
+ while (parentId) {
109
+ var _node2 = state.itemMap.get(parentId);
110
+ _node2 && (_node2.open = true);
111
+ parentId = _node2 === null || _node2 === void 0 ? void 0 : _node2.parent;
112
+ }
113
+ loadChildren(_node);
114
+ return _objectSpread({}, state);
115
+ }
49
116
  }
50
117
  if (action.type === "expandAll") {
51
- // TODO: prevent opening dynamic nodes that dont have children
52
118
  state.itemMap.forEach(function (node) {
53
- !node.leaf && (node.open = true);
119
+ var _node$children2;
120
+ !node.leaf && ((_node$children2 = node.children) === null || _node$children2 === void 0 ? void 0 : _node$children2.length) && (node.open = true);
54
121
  });
122
+ fireExpandToggle(Array.from(state.itemMap.values()));
55
123
  state.tree = flattenTree(state.items);
56
124
  return _objectSpread({}, state);
57
125
  }
@@ -59,6 +127,7 @@ var useTree = function useTree(_ref) {
59
127
  state.itemMap.forEach(function (node) {
60
128
  !node.leaf && (node.open = false);
61
129
  });
130
+ fireExpandToggle([]);
62
131
  state.tree = flattenTree(state.items);
63
132
  return _objectSpread({}, state);
64
133
  }
@@ -68,13 +137,13 @@ var useTree = function useTree(_ref) {
68
137
  return _objectSpread({}, state);
69
138
  }
70
139
  if (action.type === "select") {
71
- var _componentEvents$curr, _componentEvents$curr2;
140
+ var _componentEvents$curr, _componentEvents$curr2, _state$itemMap$get;
72
141
  if (state.selected) {
73
142
  updateSelection(state.itemMap, state.selected);
74
143
  }
75
144
  updateSelection(state.itemMap, action.id, true);
76
145
  state.tree = flattenTree(state.items);
77
- (_componentEvents$curr = (_componentEvents$curr2 = componentEvents.current).onSelect) === null || _componentEvents$curr === void 0 || _componentEvents$curr.call(_componentEvents$curr2, action.id);
146
+ (_componentEvents$curr = (_componentEvents$curr2 = componentEvents.current).onSelect) === null || _componentEvents$curr === void 0 || _componentEvents$curr.call(_componentEvents$curr2, action.id, (_state$itemMap$get = state.itemMap.get(action.id)) === null || _state$itemMap$get === void 0 ? void 0 : _state$itemMap$get.data);
78
147
  return _objectSpread(_objectSpread({}, state), {}, {
79
148
  selected: action.id
80
149
  });
@@ -116,10 +185,15 @@ var useTree = function useTree(_ref) {
116
185
  };
117
186
  }, [onSelect, onChecked]);
118
187
  useEffect(function () {
188
+ var _intialExpand$current3;
119
189
  dispatch({
120
190
  type: "load",
121
191
  items: itemList
122
192
  });
193
+ ((_intialExpand$current3 = intialExpand.current) === null || _intialExpand$current3 === void 0 ? void 0 : _intialExpand$current3.length) > 0 && dispatch({
194
+ type: "expand",
195
+ id: intialExpand.current.shift()
196
+ });
123
197
  }, [itemList]);
124
198
  useEffect(function () {
125
199
  selected && dispatch({
@@ -6,7 +6,7 @@ import { isString, matchString, compareValues } from '@react-fabric/utilities';
6
6
 
7
7
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
8
8
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
9
- var sorter = function sorter(a, b) {
9
+ var defaultSorter = function defaultSorter(a, b) {
10
10
  if (!!a.leaf !== !!b.leaf) return a.leaf ? 1 : -1;
11
11
  return compareValues()(a.label, b.label);
12
12
  };
@@ -28,15 +28,21 @@ var getIntermediate = function getIntermediate() {
28
28
  };
29
29
 
30
30
  // convert node item to internal node used by the tree
31
- var refactorTree = function refactorTree(nodes) {
32
- var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
31
+ var refactorTree = function refactorTree(nodes, _ref) {
32
+ var _ref$sorter = _ref.sorter,
33
+ sorter = _ref$sorter === void 0 ? defaultSorter : _ref$sorter,
34
+ _ref$defaultExpanded = _ref.defaultExpanded,
35
+ defaultExpanded = _ref$defaultExpanded === void 0 ? [] : _ref$defaultExpanded;
36
+ var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {
33
37
  level: 0,
34
38
  parent: undefined
35
39
  };
36
40
  var list = [];
37
- nodes.sort(sorter).forEach(function (node) {
41
+ if (sorter !== false) nodes = nodes.sort(sorter);
42
+ nodes.forEach(function (node) {
38
43
  list.push(_objectSpread(_objectSpread({
39
44
  open: false,
45
+ // defaultExpanded.includes(node.id),
40
46
  level: options.level,
41
47
  parent: options.parent,
42
48
  checked: 0,
@@ -47,6 +53,9 @@ var refactorTree = function refactorTree(nodes) {
47
53
  }, node), {}, {
48
54
  leaf: !!node.leaf,
49
55
  children: node.children && refactorTree(node.children, {
56
+ sorter: sorter,
57
+ defaultExpanded: defaultExpanded
58
+ }, {
50
59
  level: options.level + 1,
51
60
  parent: node.id
52
61
  })
@@ -1,2 +1,2 @@
1
1
  import { type TreeNodeProps } from "./types";
2
- export declare const TreeNode: ({ node, noLines, expanders, checkable, selectable, defaultLeafIcon, defaultNodeIcon, onToggle, onSelect, onChecked, children, }: TreeNodeProps) => import("react/jsx-runtime").JSX.Element;
2
+ export declare const TreeNode: ({ node, noLines, expanders, checkable, selectable, defaultLeafIcon, defaultNodeIcon, onToggle, onClick, onSelect, onChecked, children, leafClassName, nodeClassName, }: TreeNodeProps) => import("react/jsx-runtime").JSX.Element;
@@ -1,2 +1,2 @@
1
1
  import { type TreePanelProps } from "./types";
2
- export declare const TreePanel: <T extends KeyValue<any>>({ ref, items, selected, checked, checkable, noLines, renderer, searchable, selectable, matcher, filterPlaceholder, defaultLeafIcon, defaultNodeIcon, expander, onChecked, onLoad, onSelect, ...aria }: TreePanelProps<T>) => import("react/jsx-runtime").JSX.Element;
2
+ export declare const TreePanel: <T extends KeyValue<any>>({ ref, items, selected, checked, checkable, noLines, renderer, searchable, selectable, matcher, filterPlaceholder, defaultLeafIcon, defaultNodeIcon, expander, onChecked, onLoad, onSelect, onClick, sorter, makeLabel, makeIcon, defaultExpanded, onExpandToggle, leafClassName, nodeClassName, ...aria }: TreePanelProps<T>) => import("react/jsx-runtime").JSX.Element;
@@ -1,6 +1,6 @@
1
- /// <reference types="react" />
2
1
  import { type BadgeType, type ChildProp, type CssProp, type Elements, type IconProps, type RefProp, type TestProps } from "@react-fabric/core/dist/types/types";
3
- interface TreeBaseNode<T> extends IconProps, CssProp, TestProps {
2
+ import { type ElementType } from "react";
3
+ interface TreeBaseNode<T> extends Omit<IconProps, "icon">, CssProp, TestProps {
4
4
  id: string;
5
5
  /**
6
6
  * display label
@@ -23,17 +23,18 @@ interface TreeBaseNode<T> extends IconProps, CssProp, TestProps {
23
23
  */
24
24
  disabled?: boolean;
25
25
  data?: T;
26
+ icon?: ElementType<IconProps> | string;
26
27
  }
27
28
  export type TreeNodeType<T extends KeyValue = KeyValue> = TreeBaseNode<T> & ({
28
29
  leaf: true;
29
30
  children?: never;
30
31
  } | {
31
- leaf?: false;
32
+ leaf?: boolean;
32
33
  children?: Array<TreeNodeType<T>>;
33
34
  open?: boolean;
34
35
  iconOpen?: string;
35
36
  });
36
- export interface InternalNode extends IconProps, CssProp, TestProps {
37
+ export interface InternalNode extends Omit<IconProps, "icon">, CssProp, TestProps {
37
38
  id: string;
38
39
  label: Elements<JSX.Element>;
39
40
  queryable?: string;
@@ -50,6 +51,7 @@ export interface InternalNode extends IconProps, CssProp, TestProps {
50
51
  loaded: boolean;
51
52
  parent?: string;
52
53
  empty?: boolean;
54
+ icon?: ElementType<IconProps> | string;
53
55
  errored?: string;
54
56
  selected?: true;
55
57
  childSelected?: true;
@@ -66,9 +68,12 @@ export interface TreeNodeProps extends ChildProp {
66
68
  noLines?: boolean;
67
69
  selectable?: true | "leafOnly";
68
70
  checkable?: true | "leafOnly";
71
+ leafClassName?: string;
72
+ nodeClassName?: string;
69
73
  onToggle: (id: string) => void;
70
74
  onSelect: (id: string) => void;
71
75
  onChecked: (id: string) => void;
76
+ onClick?: (id: string, node: any) => void;
72
77
  }
73
78
  export interface TreeRef {
74
79
  select: (id: string) => void;
@@ -120,10 +125,14 @@ export interface TreePanelProps<T extends KeyValue = KeyValue> extends TestProps
120
125
  * callback for lazy loading tree items
121
126
  */
122
127
  onLoad?: (id: string) => Promise<Array<TreeNodeType<T>> | undefined> | Array<TreeNodeType<T>> | undefined;
128
+ /**
129
+ * callback on click of tree node
130
+ */
131
+ onClick?: (id: string, data: T) => void;
123
132
  /**
124
133
  * callback on selection of tree node
125
134
  */
126
- onSelect?: (id: string) => void;
135
+ onSelect?: (id: string, data: T) => void;
127
136
  /**
128
137
  * callback on change of checked list
129
138
  */
@@ -136,6 +145,13 @@ export interface TreePanelProps<T extends KeyValue = KeyValue> extends TestProps
136
145
  * item filter matcher
137
146
  */
138
147
  matcher?: (data: T, query: string) => boolean;
148
+ sorter?: false | ((a: T, b: T) => number);
149
+ makeLabel?: (node: T) => string;
150
+ makeIcon?: (node: T) => string;
151
+ defaultExpanded?: string[];
152
+ onExpandToggle?: (id: string[]) => void;
153
+ leafClassName?: string;
154
+ nodeClassName?: string;
139
155
  }
140
156
  export declare const iconExpandAll = "M12.984 9v6h3l-3.984 3.984-3.984-3.984h3v-6h-3l3.984-3.984 3.984 3.984h-3zM3.984 2.016h16.031v1.969h-16.031v-1.969zM3.984 20.016h16.031v1.969h-16.031v-1.969z";
141
157
  export declare const iconCollapseAll = "M3.984 11.016h16.031v1.969h-16.031v-1.969zM15.984 5.016l-3.984 3.984-3.984-3.984h3v-4.031h1.969v4.031h3zM8.016 18.984l3.984-3.984 3.984 3.984h-3v4.031h-1.969v-4.031h-3z";
@@ -1,12 +1,5 @@
1
- import { type InternalNode, type TreeNodeType } from "./types";
2
- export declare const useTree: <T extends KeyValue<any>>({ items, checked, selected, matcher, onSelect, onChecked, }: {
3
- items?: Array<TreeNodeType<T>>;
4
- selected?: string;
5
- checked?: string[];
6
- onSelect?: (id: string) => void;
7
- onChecked?: (leafs: string[], nodes: string[]) => void;
8
- matcher?: (data: T, query: string) => boolean;
9
- }) => {
1
+ import { type InternalNode, type TreePanelProps } from "./types";
2
+ export declare const useTree: <T extends KeyValue<any>>({ items, checked, selected, sorter, matcher, onLoad, onSelect, onChecked, defaultExpanded, onExpandToggle, }: Partial<TreePanelProps<T>>) => {
10
3
  tree: InternalNode[];
11
4
  toggleExpand: (id: string) => void;
12
5
  expandAll: () => void;
@@ -1,5 +1,8 @@
1
1
  import { type InternalNode, type TreeNodeType } from "./types";
2
- export declare const refactorTree: (nodes: TreeNodeType[], options?: {
2
+ export declare const refactorTree: (nodes: TreeNodeType[], { sorter, defaultExpanded, }: {
3
+ sorter?: any;
4
+ defaultExpanded?: string[];
5
+ }, options?: {
3
6
  level: number;
4
7
  parent?: string;
5
8
  }) => InternalNode[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-fabric/data",
3
- "version": "1.0.0-beta.25",
3
+ "version": "1.0.0-beta.27",
4
4
  "description": "RUF: Data package",
5
5
  "author": "Adarsh Pastakia <adarshpastakia@outlook.com>",
6
6
  "license": "MIT",
@@ -33,10 +33,10 @@
33
33
  "watch": "npm-run-all -p watch:rollup watch:dts"
34
34
  },
35
35
  "dependencies": {
36
- "@react-fabric/core": "^1.0.0-beta.25",
37
- "@react-fabric/date": "^1.0.0-beta.25",
38
- "@react-fabric/form": "^1.0.0-beta.25",
39
- "@react-fabric/utilities": "^1.0.0-beta.25",
36
+ "@react-fabric/core": "^1.0.0-beta.27",
37
+ "@react-fabric/date": "^1.0.0-beta.27",
38
+ "@react-fabric/form": "^1.0.0-beta.27",
39
+ "@react-fabric/utilities": "^1.0.0-beta.27",
40
40
  "@tanstack/react-virtual": "^3.8.6",
41
41
  "classnames": "^2.5.1",
42
42
  "color": "^4.2.3",
@@ -44,13 +44,13 @@
44
44
  },
45
45
  "peerDependencies": {
46
46
  "i18next": "^23.15.1",
47
- "react": ">=18.0.0 <20.0.0",
48
- "react-dom": ">=18.0.0 <20.0.0",
47
+ "react": "^19.0.0",
48
+ "react-dom": "^19.0.0",
49
49
  "react-i18next": "^15.0.2"
50
50
  },
51
51
  "engines": {
52
52
  "node": ">=18.12.0",
53
53
  "npm": ">=8.19.0"
54
54
  },
55
- "gitHead": "34640e2473c7823254e82aa7aed920c9c6f8f503"
55
+ "gitHead": "02ddac152718fe86b6003f35ea5e1ef1581168f2"
56
56
  }
@@ -22,8 +22,9 @@
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";
27
28
  import { type TreeNodeProps } from "./types";
28
29
 
29
30
  export const TreeNode = ({
@@ -35,20 +36,26 @@ export const TreeNode = ({
35
36
  defaultLeafIcon,
36
37
  defaultNodeIcon,
37
38
  onToggle,
39
+ onClick,
38
40
  onSelect,
39
41
  onChecked,
40
42
  children,
43
+ leafClassName,
44
+ nodeClassName,
41
45
  }: TreeNodeProps) => {
42
46
  const badgeProps = useMemo(() => {
43
47
  return getBadgeProps(node.badge);
44
48
  }, [node.badge]);
45
49
 
46
50
  const filler = useMemo(() => {
47
- return node.lines.slice(noLines ? 1 : 0).map((line, idx) => (
51
+ return node.lines.slice(1).map((line, idx) => (
48
52
  <div
49
53
  key={idx}
50
54
  className={classNames(
51
- "flex-initial self-stretch flex items-center w-6 relative before:absolute before:top-0 before:start-[7px] before:bg-tint-200 before:min-h-[50%] before:w-[2px] before:scale-x-[.625]",
55
+ "flex-initial self-stretch flex items-center w-6 relative",
56
+ !noLines &&
57
+ line > 0 &&
58
+ "before:absolute before:top-0 before:start-[7px] before:bg-tint-200 before:min-h-[50%] before:w-[2px] before:scale-x-[.625]",
52
59
  !noLines && line !== 2 && "before:h-full",
53
60
  )}
54
61
  >
@@ -88,12 +95,24 @@ export const TreeNode = ({
88
95
  <div
89
96
  role="treeitem"
90
97
  aria-selected={node.selected}
91
- className="flex flex-nowrap items-center"
98
+ className={classNames(
99
+ "flex flex-nowrap items-center",
100
+ node.leaf ? leafClassName : nodeClassName,
101
+ )}
92
102
  data-testid={node["data-testid"]}
93
103
  data-test-value={node["data-test-value"]}
94
104
  >
95
105
  {filler}
96
- {!node.leaf && !node.childFiltered && (
106
+ {node.loading && (
107
+ <div className="flex-initial w-6">
108
+ <Icon
109
+ rtlFlip
110
+ icon={CoreIcons.spinner}
111
+ className="animate-spin text-muted"
112
+ />
113
+ </div>
114
+ )}
115
+ {!node.leaf && !node.childFiltered && !node.loading && (
97
116
  <div className="flex-initial w-6">
98
117
  <Icon
99
118
  rtlFlip
@@ -125,27 +144,31 @@ export const TreeNode = ({
125
144
  <div
126
145
  role="none"
127
146
  className={classNames(
128
- "group data-[selected]:bg-primary-100 flex flex-nowrap flex-1 overflow-hidden select-none",
147
+ "group/tool data-[selected]:bg-primary-100 flex flex-nowrap flex-1 overflow-hidden select-none",
129
148
  node.childSelected && "font-medium",
130
- canSelect && "hover:bg-primary-50 cursor-pointer",
149
+ (canSelect || onClick) && "hover:bg-primary-50 cursor-pointer",
131
150
  )}
132
151
  data-selected={node.selected}
133
- onClick={() =>
152
+ onClick={() => {
153
+ onClick?.(node.id, node.data);
134
154
  canSelect
135
155
  ? onSelect(node.id)
136
156
  : canCheck
137
157
  ? onChecked(node.id)
138
- : onToggle(node.id)
139
- }
158
+ : onToggle(node.id);
159
+ }}
140
160
  >
141
161
  {nodeIcon && (
142
- <div className="flex-content w-6">
143
- <Icon
144
- icon={nodeIcon}
145
- bg={node.iconBg}
146
- color={node.iconColor}
147
- rtlFlip={node.rtlFlip}
148
- />
162
+ <div className="flex-content w-6 self-center leading-none">
163
+ {isValidElement(nodeIcon) && nodeIcon}
164
+ {isString(nodeIcon) && (
165
+ <Icon
166
+ icon={nodeIcon}
167
+ bg={node.iconBg}
168
+ color={node.iconColor}
169
+ rtlFlip={node.rtlFlip}
170
+ />
171
+ )}
149
172
  </div>
150
173
  )}
151
174
  {node.childSelected && !node.open && (
@@ -154,7 +177,7 @@ export const TreeNode = ({
154
177
  <div role="none" className="flex-1 truncate">
155
178
  {children}
156
179
  </div>
157
- {node.badge && (
180
+ {!isEmpty(node.badge) && (
158
181
  <Badge
159
182
  {...badgeProps}
160
183
  className={classNames(
@@ -56,6 +56,14 @@ export const TreePanel = <T extends KeyValue>({
56
56
  onChecked,
57
57
  onLoad,
58
58
  onSelect,
59
+ onClick,
60
+ sorter,
61
+ makeLabel,
62
+ makeIcon,
63
+ defaultExpanded,
64
+ onExpandToggle,
65
+ leafClassName,
66
+ nodeClassName,
59
67
  ...aria
60
68
  }: TreePanelProps<T>) => {
61
69
  const {
@@ -72,8 +80,12 @@ export const TreePanel = <T extends KeyValue>({
72
80
  checked,
73
81
  selected,
74
82
  matcher,
83
+ sorter,
84
+ onLoad,
75
85
  onSelect,
76
86
  onChecked,
87
+ defaultExpanded,
88
+ onExpandToggle,
77
89
  });
78
90
 
79
91
  const scrollerRef = useRef<HTMLDivElement>(null);
@@ -99,7 +111,7 @@ export const TreePanel = <T extends KeyValue>({
99
111
  ref,
100
112
  () => ({
101
113
  open: (id: string) => expand(id),
102
- select: (id: string) => expand(id),
114
+ select: (id: string) => select(id),
103
115
  }),
104
116
  [],
105
117
  );
@@ -151,12 +163,18 @@ export const TreePanel = <T extends KeyValue>({
151
163
  checkable={checkable}
152
164
  selectable={selectable}
153
165
  defaultLeafIcon={defaultLeafIcon}
154
- defaultNodeIcon={defaultNodeIcon}
166
+ defaultNodeIcon={
167
+ makeIcon?.(tree[index].data) ?? defaultNodeIcon
168
+ }
155
169
  onToggle={toggleExpand}
156
170
  onChecked={toggleCheck}
157
171
  onSelect={select}
172
+ onClick={onClick}
173
+ leafClassName={leafClassName}
174
+ nodeClassName={nodeClassName}
158
175
  >
159
- {(tree[index].data && renderer?.(tree[index].data)) ||
176
+ {(tree[index].data && renderer?.(tree[index].data)) ??
177
+ makeLabel?.(tree[index].data) ??
160
178
  tree[index].label}
161
179
  </TreeNode>
162
180
  </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,14 @@ 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 on click of tree node
179
+ */
180
+ onClick?: (id: string, data: T) => void;
167
181
  /**
168
182
  * callback on selection of tree node
169
183
  */
170
- onSelect?: (id: string) => void;
184
+ onSelect?: (id: string, data: T) => void;
171
185
  /**
172
186
  * callback on change of checked list
173
187
  */
@@ -180,6 +194,16 @@ export interface TreePanelProps<T extends KeyValue = KeyValue>
180
194
  * item filter matcher
181
195
  */
182
196
  matcher?: (data: T, query: string) => boolean;
197
+
198
+ sorter?: false | ((a: T, b: T) => number);
199
+ makeLabel?: (node: T) => string;
200
+ makeIcon?: (node: T) => string;
201
+
202
+ defaultExpanded?: string[];
203
+ onExpandToggle?: (id: string[]) => void;
204
+
205
+ leafClassName?: string;
206
+ nodeClassName?: string;
183
207
  }
184
208
 
185
209
  export const iconExpandAll =