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

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.
@@ -2,6 +2,7 @@
2
2
 
3
3
  var _defineProperty = require('@babel/runtime/helpers/defineProperty');
4
4
  var core = require('@react-fabric/core');
5
+ var utilities = require('@react-fabric/utilities');
5
6
  var classNames = require('classnames');
6
7
  var react = require('react');
7
8
  var jsxRuntime = require('react/jsx-runtime');
@@ -17,16 +18,19 @@ var TreeNode = function TreeNode(_ref) {
17
18
  defaultLeafIcon = _ref.defaultLeafIcon,
18
19
  defaultNodeIcon = _ref.defaultNodeIcon,
19
20
  onToggle = _ref.onToggle,
21
+ _onClick = _ref.onClick,
20
22
  onSelect = _ref.onSelect,
21
23
  onChecked = _ref.onChecked,
22
- children = _ref.children;
24
+ children = _ref.children,
25
+ leafClassName = _ref.leafClassName,
26
+ nodeClassName = _ref.nodeClassName;
23
27
  var badgeProps = react.useMemo(function () {
24
28
  return core.getBadgeProps(node.badge);
25
29
  }, [node.badge]);
26
30
  var filler = react.useMemo(function () {
27
- return node.lines.slice(noLines ? 1 : 0).map(function (line, idx) {
31
+ return node.lines.slice(1).map(function (line, idx) {
28
32
  return /*#__PURE__*/jsxRuntime.jsx("div", {
29
- className: classNames("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]", !noLines && line !== 2 && "before:h-full"),
33
+ className: classNames("flex-initial self-stretch flex items-center w-6 relative", !noLines && line > 0 && "before:absolute before:top-0 before:start-[7px] before:bg-tint-200 before:min-h-[50%] before:w-[2px] before:scale-x-[.625]", !noLines && line !== 2 && "before:h-full"),
30
34
  children: /*#__PURE__*/jsxRuntime.jsxs("svg", {
31
35
  viewBox: "0 0 24 24",
32
36
  className: "fill-tint-200",
@@ -56,7 +60,7 @@ var TreeNode = function TreeNode(_ref) {
56
60
  return /*#__PURE__*/jsxRuntime.jsxs("div", {
57
61
  role: "treeitem",
58
62
  "aria-selected": node.selected,
59
- className: "flex flex-nowrap items-center",
63
+ className: classNames("flex flex-nowrap items-center", node.leaf ? leafClassName : nodeClassName),
60
64
  "data-testid": node["data-testid"],
61
65
  "data-test-value": node["data-test-value"],
62
66
  children: [filler, !node.leaf && !node.childFiltered && /*#__PURE__*/jsxRuntime.jsx("div", {
@@ -80,19 +84,20 @@ var TreeNode = function TreeNode(_ref) {
80
84
  })
81
85
  }), /*#__PURE__*/jsxRuntime.jsxs("div", {
82
86
  role: "none",
83
- className: classNames("group data-[selected]:bg-primary-100 flex flex-nowrap flex-1 overflow-hidden select-none", node.childSelected && "font-medium", canSelect && "hover:bg-primary-50 cursor-pointer"),
87
+ className: classNames("group data-[selected]:bg-primary-100 flex flex-nowrap flex-1 overflow-hidden select-none", node.childSelected && "font-medium", (canSelect || _onClick) && "hover:bg-primary-50 cursor-pointer"),
84
88
  "data-selected": node.selected,
85
89
  onClick: function onClick() {
86
- return canSelect ? onSelect(node.id) : canCheck ? onChecked(node.id) : onToggle(node.id);
90
+ _onClick === null || _onClick === void 0 || _onClick(node.id, node.data);
91
+ canSelect ? onSelect(node.id) : canCheck ? onChecked(node.id) : onToggle(node.id);
87
92
  },
88
- children: [nodeIcon && /*#__PURE__*/jsxRuntime.jsx("div", {
89
- className: "flex-content w-6",
90
- children: /*#__PURE__*/jsxRuntime.jsx(core.Icon, {
93
+ children: [nodeIcon && /*#__PURE__*/jsxRuntime.jsxs("div", {
94
+ className: "flex-content w-6 self-center leading-none",
95
+ children: [/*#__PURE__*/react.isValidElement(nodeIcon) && nodeIcon, utilities.isString(nodeIcon) && /*#__PURE__*/jsxRuntime.jsx(core.Icon, {
91
96
  icon: nodeIcon,
92
97
  bg: node.iconBg,
93
98
  color: node.iconColor,
94
99
  rtlFlip: node.rtlFlip
95
- })
100
+ })]
96
101
  }), node.childSelected && !node.open && /*#__PURE__*/jsxRuntime.jsx("span", {
97
102
  className: " flex-content text-primary-500",
98
103
  children: "\u2022"
@@ -100,7 +105,7 @@ var TreeNode = function TreeNode(_ref) {
100
105
  role: "none",
101
106
  className: "flex-1 truncate",
102
107
  children: children
103
- }), node.badge && /*#__PURE__*/jsxRuntime.jsx(core.Badge, _objectSpread(_objectSpread({}, badgeProps), {}, {
108
+ }), !utilities.isEmpty(node.badge) && /*#__PURE__*/jsxRuntime.jsx(core.Badge, _objectSpread(_objectSpread({}, badgeProps), {}, {
104
109
  className: classNames("me-1 self-center flex-content bg-opacity-50", badgeProps.className)
105
110
  }))]
106
111
  })]
@@ -11,7 +11,7 @@ var types = require('./types.js');
11
11
  var useTree = require('./useTree.js');
12
12
  var jsxRuntime = require('react/jsx-runtime');
13
13
 
14
- var _excluded = ["ref", "items", "selected", "checked", "checkable", "noLines", "renderer", "searchable", "selectable", "matcher", "filterPlaceholder", "defaultLeafIcon", "defaultNodeIcon", "expander", "onChecked", "onLoad", "onSelect"];
14
+ var _excluded = ["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"];
15
15
  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; }
16
16
  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; }
17
17
  var TreePanel = function TreePanel(_ref) {
@@ -34,21 +34,32 @@ var TreePanel = function TreePanel(_ref) {
34
34
  onChecked = _ref.onChecked;
35
35
  _ref.onLoad;
36
36
  var onSelect = _ref.onSelect,
37
+ onClick = _ref.onClick,
38
+ sorter = _ref.sorter,
39
+ makeLabel = _ref.makeLabel,
40
+ makeIcon = _ref.makeIcon,
41
+ defaultExpanded = _ref.defaultExpanded,
42
+ onExpandToggle = _ref.onExpandToggle,
43
+ leafClassName = _ref.leafClassName,
44
+ nodeClassName = _ref.nodeClassName,
37
45
  aria = _objectWithoutProperties(_ref, _excluded);
38
46
  var _useTree = useTree.useTree({
39
47
  items: items,
40
48
  checked: checked,
41
49
  selected: selected,
42
50
  matcher: matcher,
51
+ sorter: sorter,
43
52
  onSelect: onSelect,
44
- onChecked: onChecked
53
+ onChecked: onChecked,
54
+ defaultExpanded: defaultExpanded,
55
+ onExpandToggle: onExpandToggle
45
56
  }),
46
57
  tree = _useTree.tree,
47
58
  toggleExpand = _useTree.toggleExpand,
48
59
  toggleCheck = _useTree.toggleCheck,
49
60
  expandAll = _useTree.expandAll,
50
61
  collapseAll = _useTree.collapseAll,
51
- select = _useTree.select,
62
+ _select = _useTree.select,
52
63
  expand = _useTree.expand,
53
64
  onFilter = _useTree.onFilter;
54
65
  var scrollerRef = react.useRef(null);
@@ -74,7 +85,7 @@ var TreePanel = function TreePanel(_ref) {
74
85
  return expand(id);
75
86
  },
76
87
  select: function select(id) {
77
- return expand(id);
88
+ return _select(id);
78
89
  }
79
90
  };
80
91
  }, []);
@@ -120,6 +131,7 @@ var TreePanel = function TreePanel(_ref) {
120
131
  height: (_virtualItems$ = virtualItems[0]) === null || _virtualItems$ === void 0 ? void 0 : _virtualItems$.start
121
132
  }
122
133
  }), virtualItems.map(function (_ref2) {
134
+ var _makeIcon, _ref3, _ref4;
123
135
  var index = _ref2.index,
124
136
  key = _ref2.key;
125
137
  return /*#__PURE__*/jsxRuntime.jsx("div", {
@@ -132,11 +144,14 @@ var TreePanel = function TreePanel(_ref) {
132
144
  checkable: checkable,
133
145
  selectable: selectable,
134
146
  defaultLeafIcon: defaultLeafIcon,
135
- defaultNodeIcon: defaultNodeIcon,
147
+ defaultNodeIcon: (_makeIcon = makeIcon === null || makeIcon === void 0 ? void 0 : makeIcon(tree[index].data)) !== null && _makeIcon !== void 0 ? _makeIcon : defaultNodeIcon,
136
148
  onToggle: toggleExpand,
137
149
  onChecked: toggleCheck,
138
- onSelect: select,
139
- children: tree[index].data && (renderer === null || renderer === void 0 ? void 0 : renderer(tree[index].data)) || tree[index].label
150
+ onSelect: _select,
151
+ onClick: onClick,
152
+ leafClassName: leafClassName,
153
+ nodeClassName: nodeClassName,
154
+ children: (_ref3 = (_ref4 = tree[index].data && (renderer === null || renderer === void 0 ? void 0 : renderer(tree[index].data))) !== null && _ref4 !== void 0 ? _ref4 : makeLabel === null || makeLabel === void 0 ? void 0 : makeLabel(tree[index].data)) !== null && _ref3 !== void 0 ? _ref3 : tree[index].label
140
155
  })
141
156
  }, key);
142
157
  })]
@@ -19,17 +19,31 @@ var useTree = function useTree(_ref) {
19
19
  _ref$checked = _ref.checked,
20
20
  checked = _ref$checked === void 0 ? utilities.EMPTY_ARRAY : _ref$checked,
21
21
  selected = _ref.selected,
22
+ sorter = _ref.sorter,
22
23
  matcher = _ref.matcher,
23
24
  onSelect = _ref.onSelect,
24
- onChecked = _ref.onChecked;
25
+ onChecked = _ref.onChecked,
26
+ defaultExpanded = _ref.defaultExpanded,
27
+ onExpandToggle = _ref.onExpandToggle;
25
28
  var componentEvents = react.useRef({
26
29
  onSelect: onSelect,
27
30
  onChecked: onChecked
28
31
  });
29
32
  var itemList = createItemList(items);
33
+ var fireExpandToggle = react.useCallback(function (itemMap) {
34
+ var expandedList = itemMap.filter(function (node) {
35
+ return node.open;
36
+ }).map(function (node) {
37
+ return node.id;
38
+ });
39
+ onExpandToggle === null || onExpandToggle === void 0 || onExpandToggle(expandedList);
40
+ }, [onExpandToggle]);
30
41
  var _useReducer = react.useReducer(function (state, action) {
31
42
  if (action.type === "load") {
32
- state.items = utils.refactorTree(action.items);
43
+ state.items = utils.refactorTree(action.items, {
44
+ sorter: sorter,
45
+ defaultExpanded: defaultExpanded
46
+ });
33
47
  state.itemMap = utils.makeTreeMap(state.items);
34
48
  state.tree = utils.flattenTree(state.items);
35
49
  return _objectSpread({}, state);
@@ -37,6 +51,7 @@ var useTree = function useTree(_ref) {
37
51
  if (action.type === "toggleExpand") {
38
52
  var node = state.itemMap.get(action.id);
39
53
  node && (node.open = !node.open);
54
+ fireExpandToggle(Array.from(state.itemMap.values()));
40
55
  state.tree = utils.flattenTree(state.items);
41
56
  return _objectSpread({}, state);
42
57
  }
@@ -44,6 +59,7 @@ var useTree = function useTree(_ref) {
44
59
  // TODO: check if children available, if not call onLoad to fetch node children
45
60
  var _node = state.itemMap.get(action.id);
46
61
  _node && (_node.open = true);
62
+ fireExpandToggle(Array.from(state.itemMap.values()));
47
63
  state.tree = utils.flattenTree(state.items);
48
64
  return _objectSpread({}, state);
49
65
  }
@@ -52,6 +68,7 @@ var useTree = function useTree(_ref) {
52
68
  state.itemMap.forEach(function (node) {
53
69
  !node.leaf && (node.open = true);
54
70
  });
71
+ fireExpandToggle(Array.from(state.itemMap.values()));
55
72
  state.tree = utils.flattenTree(state.items);
56
73
  return _objectSpread({}, state);
57
74
  }
@@ -59,6 +76,7 @@ var useTree = function useTree(_ref) {
59
76
  state.itemMap.forEach(function (node) {
60
77
  !node.leaf && (node.open = false);
61
78
  });
79
+ fireExpandToggle([]);
62
80
  state.tree = utils.flattenTree(state.items);
63
81
  return _objectSpread({}, state);
64
82
  }
@@ -68,13 +86,13 @@ var useTree = function useTree(_ref) {
68
86
  return _objectSpread({}, state);
69
87
  }
70
88
  if (action.type === "select") {
71
- var _componentEvents$curr, _componentEvents$curr2;
89
+ var _componentEvents$curr, _componentEvents$curr2, _state$itemMap$get;
72
90
  if (state.selected) {
73
91
  utils.updateSelection(state.itemMap, state.selected);
74
92
  }
75
93
  utils.updateSelection(state.itemMap, action.id, true);
76
94
  state.tree = utils.flattenTree(state.items);
77
- (_componentEvents$curr = (_componentEvents$curr2 = componentEvents.current).onSelect) === null || _componentEvents$curr === void 0 || _componentEvents$curr.call(_componentEvents$curr2, action.id);
95
+ (_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
96
  return _objectSpread(_objectSpread({}, state), {}, {
79
97
  selected: action.id
80
98
  });
@@ -6,7 +6,7 @@ var utilities = require('@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 utilities.compareValues()(a.label, b.label);
12
12
  };
@@ -28,15 +28,20 @@ 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
- open: false,
44
+ open: defaultExpanded.includes(node.id),
40
45
  level: options.level,
41
46
  parent: options.parent,
42
47
  checked: 0,
@@ -47,6 +52,9 @@ var refactorTree = function refactorTree(nodes) {
47
52
  }, node), {}, {
48
53
  leaf: !!node.leaf,
49
54
  children: node.children && refactorTree(node.children, {
55
+ sorter: sorter,
56
+ defaultExpanded: defaultExpanded
57
+ }, {
50
58
  level: options.level + 1,
51
59
  parent: node.id
52
60
  })
@@ -2,8 +2,9 @@
2
2
 
3
3
  import _defineProperty from '@babel/runtime/helpers/defineProperty';
4
4
  import { getBadgeProps, Icon, CoreIcons, Badge } from '@react-fabric/core';
5
+ import { isString, isEmpty } from '@react-fabric/utilities';
5
6
  import classNames from 'classnames';
6
- import { useMemo } from 'react';
7
+ import { useMemo, isValidElement } from 'react';
7
8
  import { jsx, jsxs } from 'react/jsx-runtime';
8
9
 
9
10
  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; }
@@ -17,16 +18,19 @@ var TreeNode = function TreeNode(_ref) {
17
18
  defaultLeafIcon = _ref.defaultLeafIcon,
18
19
  defaultNodeIcon = _ref.defaultNodeIcon,
19
20
  onToggle = _ref.onToggle,
21
+ _onClick = _ref.onClick,
20
22
  onSelect = _ref.onSelect,
21
23
  onChecked = _ref.onChecked,
22
- children = _ref.children;
24
+ children = _ref.children,
25
+ leafClassName = _ref.leafClassName,
26
+ nodeClassName = _ref.nodeClassName;
23
27
  var badgeProps = useMemo(function () {
24
28
  return getBadgeProps(node.badge);
25
29
  }, [node.badge]);
26
30
  var filler = useMemo(function () {
27
- return node.lines.slice(noLines ? 1 : 0).map(function (line, idx) {
31
+ return node.lines.slice(1).map(function (line, idx) {
28
32
  return /*#__PURE__*/jsx("div", {
29
- className: classNames("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]", !noLines && line !== 2 && "before:h-full"),
33
+ className: classNames("flex-initial self-stretch flex items-center w-6 relative", !noLines && line > 0 && "before:absolute before:top-0 before:start-[7px] before:bg-tint-200 before:min-h-[50%] before:w-[2px] before:scale-x-[.625]", !noLines && line !== 2 && "before:h-full"),
30
34
  children: /*#__PURE__*/jsxs("svg", {
31
35
  viewBox: "0 0 24 24",
32
36
  className: "fill-tint-200",
@@ -56,7 +60,7 @@ var TreeNode = function TreeNode(_ref) {
56
60
  return /*#__PURE__*/jsxs("div", {
57
61
  role: "treeitem",
58
62
  "aria-selected": node.selected,
59
- className: "flex flex-nowrap items-center",
63
+ className: classNames("flex flex-nowrap items-center", node.leaf ? leafClassName : nodeClassName),
60
64
  "data-testid": node["data-testid"],
61
65
  "data-test-value": node["data-test-value"],
62
66
  children: [filler, !node.leaf && !node.childFiltered && /*#__PURE__*/jsx("div", {
@@ -80,19 +84,20 @@ var TreeNode = function TreeNode(_ref) {
80
84
  })
81
85
  }), /*#__PURE__*/jsxs("div", {
82
86
  role: "none",
83
- className: classNames("group data-[selected]:bg-primary-100 flex flex-nowrap flex-1 overflow-hidden select-none", node.childSelected && "font-medium", canSelect && "hover:bg-primary-50 cursor-pointer"),
87
+ className: classNames("group data-[selected]:bg-primary-100 flex flex-nowrap flex-1 overflow-hidden select-none", node.childSelected && "font-medium", (canSelect || _onClick) && "hover:bg-primary-50 cursor-pointer"),
84
88
  "data-selected": node.selected,
85
89
  onClick: function onClick() {
86
- return canSelect ? onSelect(node.id) : canCheck ? onChecked(node.id) : onToggle(node.id);
90
+ _onClick === null || _onClick === void 0 || _onClick(node.id, node.data);
91
+ canSelect ? onSelect(node.id) : canCheck ? onChecked(node.id) : onToggle(node.id);
87
92
  },
88
- children: [nodeIcon && /*#__PURE__*/jsx("div", {
89
- className: "flex-content w-6",
90
- children: /*#__PURE__*/jsx(Icon, {
93
+ children: [nodeIcon && /*#__PURE__*/jsxs("div", {
94
+ className: "flex-content w-6 self-center leading-none",
95
+ children: [/*#__PURE__*/isValidElement(nodeIcon) && nodeIcon, isString(nodeIcon) && /*#__PURE__*/jsx(Icon, {
91
96
  icon: nodeIcon,
92
97
  bg: node.iconBg,
93
98
  color: node.iconColor,
94
99
  rtlFlip: node.rtlFlip
95
- })
100
+ })]
96
101
  }), node.childSelected && !node.open && /*#__PURE__*/jsx("span", {
97
102
  className: " flex-content text-primary-500",
98
103
  children: "\u2022"
@@ -100,7 +105,7 @@ var TreeNode = function TreeNode(_ref) {
100
105
  role: "none",
101
106
  className: "flex-1 truncate",
102
107
  children: children
103
- }), node.badge && /*#__PURE__*/jsx(Badge, _objectSpread(_objectSpread({}, badgeProps), {}, {
108
+ }), !isEmpty(node.badge) && /*#__PURE__*/jsx(Badge, _objectSpread(_objectSpread({}, badgeProps), {}, {
104
109
  className: classNames("me-1 self-center flex-content bg-opacity-50", badgeProps.className)
105
110
  }))]
106
111
  })]
@@ -11,7 +11,7 @@ import { iconExpandAll, iconCollapseAll } from './types.js';
11
11
  import { useTree } from './useTree.js';
12
12
  import { jsxs, jsx } from 'react/jsx-runtime';
13
13
 
14
- var _excluded = ["ref", "items", "selected", "checked", "checkable", "noLines", "renderer", "searchable", "selectable", "matcher", "filterPlaceholder", "defaultLeafIcon", "defaultNodeIcon", "expander", "onChecked", "onLoad", "onSelect"];
14
+ var _excluded = ["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"];
15
15
  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; }
16
16
  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; }
17
17
  var TreePanel = function TreePanel(_ref) {
@@ -34,21 +34,32 @@ var TreePanel = function TreePanel(_ref) {
34
34
  onChecked = _ref.onChecked;
35
35
  _ref.onLoad;
36
36
  var onSelect = _ref.onSelect,
37
+ onClick = _ref.onClick,
38
+ sorter = _ref.sorter,
39
+ makeLabel = _ref.makeLabel,
40
+ makeIcon = _ref.makeIcon,
41
+ defaultExpanded = _ref.defaultExpanded,
42
+ onExpandToggle = _ref.onExpandToggle,
43
+ leafClassName = _ref.leafClassName,
44
+ nodeClassName = _ref.nodeClassName,
37
45
  aria = _objectWithoutProperties(_ref, _excluded);
38
46
  var _useTree = useTree({
39
47
  items: items,
40
48
  checked: checked,
41
49
  selected: selected,
42
50
  matcher: matcher,
51
+ sorter: sorter,
43
52
  onSelect: onSelect,
44
- onChecked: onChecked
53
+ onChecked: onChecked,
54
+ defaultExpanded: defaultExpanded,
55
+ onExpandToggle: onExpandToggle
45
56
  }),
46
57
  tree = _useTree.tree,
47
58
  toggleExpand = _useTree.toggleExpand,
48
59
  toggleCheck = _useTree.toggleCheck,
49
60
  expandAll = _useTree.expandAll,
50
61
  collapseAll = _useTree.collapseAll,
51
- select = _useTree.select,
62
+ _select = _useTree.select,
52
63
  expand = _useTree.expand,
53
64
  onFilter = _useTree.onFilter;
54
65
  var scrollerRef = useRef(null);
@@ -74,7 +85,7 @@ var TreePanel = function TreePanel(_ref) {
74
85
  return expand(id);
75
86
  },
76
87
  select: function select(id) {
77
- return expand(id);
88
+ return _select(id);
78
89
  }
79
90
  };
80
91
  }, []);
@@ -120,6 +131,7 @@ var TreePanel = function TreePanel(_ref) {
120
131
  height: (_virtualItems$ = virtualItems[0]) === null || _virtualItems$ === void 0 ? void 0 : _virtualItems$.start
121
132
  }
122
133
  }), virtualItems.map(function (_ref2) {
134
+ var _makeIcon, _ref3, _ref4;
123
135
  var index = _ref2.index,
124
136
  key = _ref2.key;
125
137
  return /*#__PURE__*/jsx("div", {
@@ -132,11 +144,14 @@ var TreePanel = function TreePanel(_ref) {
132
144
  checkable: checkable,
133
145
  selectable: selectable,
134
146
  defaultLeafIcon: defaultLeafIcon,
135
- defaultNodeIcon: defaultNodeIcon,
147
+ defaultNodeIcon: (_makeIcon = makeIcon === null || makeIcon === void 0 ? void 0 : makeIcon(tree[index].data)) !== null && _makeIcon !== void 0 ? _makeIcon : defaultNodeIcon,
136
148
  onToggle: toggleExpand,
137
149
  onChecked: toggleCheck,
138
- onSelect: select,
139
- children: tree[index].data && (renderer === null || renderer === void 0 ? void 0 : renderer(tree[index].data)) || tree[index].label
150
+ onSelect: _select,
151
+ onClick: onClick,
152
+ leafClassName: leafClassName,
153
+ nodeClassName: nodeClassName,
154
+ children: (_ref3 = (_ref4 = tree[index].data && (renderer === null || renderer === void 0 ? void 0 : renderer(tree[index].data))) !== null && _ref4 !== void 0 ? _ref4 : makeLabel === null || makeLabel === void 0 ? void 0 : makeLabel(tree[index].data)) !== null && _ref3 !== void 0 ? _ref3 : tree[index].label
140
155
  })
141
156
  }, key);
142
157
  })]
@@ -5,7 +5,7 @@ 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';
8
+ import { useRef, useCallback, useReducer, useEffect } from 'react';
9
9
  import { refactorTree, makeTreeMap, flattenTree, filterTree, updateSelection, 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; }
@@ -19,17 +19,31 @@ 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,
23
24
  onSelect = _ref.onSelect,
24
- onChecked = _ref.onChecked;
25
+ onChecked = _ref.onChecked,
26
+ defaultExpanded = _ref.defaultExpanded,
27
+ onExpandToggle = _ref.onExpandToggle;
25
28
  var componentEvents = useRef({
26
29
  onSelect: onSelect,
27
30
  onChecked: onChecked
28
31
  });
29
32
  var itemList = createItemList(items);
33
+ var fireExpandToggle = useCallback(function (itemMap) {
34
+ var expandedList = itemMap.filter(function (node) {
35
+ return node.open;
36
+ }).map(function (node) {
37
+ return node.id;
38
+ });
39
+ onExpandToggle === null || onExpandToggle === void 0 || onExpandToggle(expandedList);
40
+ }, [onExpandToggle]);
30
41
  var _useReducer = useReducer(function (state, action) {
31
42
  if (action.type === "load") {
32
- state.items = refactorTree(action.items);
43
+ state.items = refactorTree(action.items, {
44
+ sorter: sorter,
45
+ defaultExpanded: defaultExpanded
46
+ });
33
47
  state.itemMap = makeTreeMap(state.items);
34
48
  state.tree = flattenTree(state.items);
35
49
  return _objectSpread({}, state);
@@ -37,6 +51,7 @@ var useTree = function useTree(_ref) {
37
51
  if (action.type === "toggleExpand") {
38
52
  var node = state.itemMap.get(action.id);
39
53
  node && (node.open = !node.open);
54
+ fireExpandToggle(Array.from(state.itemMap.values()));
40
55
  state.tree = flattenTree(state.items);
41
56
  return _objectSpread({}, state);
42
57
  }
@@ -44,6 +59,7 @@ var useTree = function useTree(_ref) {
44
59
  // TODO: check if children available, if not call onLoad to fetch node children
45
60
  var _node = state.itemMap.get(action.id);
46
61
  _node && (_node.open = true);
62
+ fireExpandToggle(Array.from(state.itemMap.values()));
47
63
  state.tree = flattenTree(state.items);
48
64
  return _objectSpread({}, state);
49
65
  }
@@ -52,6 +68,7 @@ var useTree = function useTree(_ref) {
52
68
  state.itemMap.forEach(function (node) {
53
69
  !node.leaf && (node.open = true);
54
70
  });
71
+ fireExpandToggle(Array.from(state.itemMap.values()));
55
72
  state.tree = flattenTree(state.items);
56
73
  return _objectSpread({}, state);
57
74
  }
@@ -59,6 +76,7 @@ var useTree = function useTree(_ref) {
59
76
  state.itemMap.forEach(function (node) {
60
77
  !node.leaf && (node.open = false);
61
78
  });
79
+ fireExpandToggle([]);
62
80
  state.tree = flattenTree(state.items);
63
81
  return _objectSpread({}, state);
64
82
  }
@@ -68,13 +86,13 @@ var useTree = function useTree(_ref) {
68
86
  return _objectSpread({}, state);
69
87
  }
70
88
  if (action.type === "select") {
71
- var _componentEvents$curr, _componentEvents$curr2;
89
+ var _componentEvents$curr, _componentEvents$curr2, _state$itemMap$get;
72
90
  if (state.selected) {
73
91
  updateSelection(state.itemMap, state.selected);
74
92
  }
75
93
  updateSelection(state.itemMap, action.id, true);
76
94
  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);
95
+ (_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
96
  return _objectSpread(_objectSpread({}, state), {}, {
79
97
  selected: action.id
80
98
  });
@@ -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,20 @@ 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
- open: false,
44
+ open: defaultExpanded.includes(node.id),
40
45
  level: options.level,
41
46
  parent: options.parent,
42
47
  checked: 0,
@@ -47,6 +52,9 @@ var refactorTree = function refactorTree(nodes) {
47
52
  }, node), {}, {
48
53
  leaf: !!node.leaf,
49
54
  children: node.children && refactorTree(node.children, {
55
+ sorter: sorter,
56
+ defaultExpanded: defaultExpanded
57
+ }, {
50
58
  level: options.level + 1,
51
59
  parent: node.id
52
60
  })
@@ -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, 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.26",
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.26",
37
+ "@react-fabric/date": "^1.0.0-beta.26",
38
+ "@react-fabric/form": "^1.0.0-beta.26",
39
+ "@react-fabric/utilities": "^1.0.0-beta.26",
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": "452f821dc8b22c5765b3858cc53ad9d7e2f9bcf1"
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,7 +95,10 @@ 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
  >
@@ -127,25 +137,29 @@ export const TreeNode = ({
127
137
  className={classNames(
128
138
  "group data-[selected]:bg-primary-100 flex flex-nowrap flex-1 overflow-hidden select-none",
129
139
  node.childSelected && "font-medium",
130
- canSelect && "hover:bg-primary-50 cursor-pointer",
140
+ (canSelect || onClick) && "hover:bg-primary-50 cursor-pointer",
131
141
  )}
132
142
  data-selected={node.selected}
133
- onClick={() =>
143
+ onClick={() => {
144
+ onClick?.(node.id, node.data);
134
145
  canSelect
135
146
  ? onSelect(node.id)
136
147
  : canCheck
137
148
  ? onChecked(node.id)
138
- : onToggle(node.id)
139
- }
149
+ : onToggle(node.id);
150
+ }}
140
151
  >
141
152
  {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
- />
153
+ <div className="flex-content w-6 self-center leading-none">
154
+ {isValidElement(nodeIcon) && nodeIcon}
155
+ {isString(nodeIcon) && (
156
+ <Icon
157
+ icon={nodeIcon}
158
+ bg={node.iconBg}
159
+ color={node.iconColor}
160
+ rtlFlip={node.rtlFlip}
161
+ />
162
+ )}
149
163
  </div>
150
164
  )}
151
165
  {node.childSelected && !node.open && (
@@ -154,7 +168,7 @@ export const TreeNode = ({
154
168
  <div role="none" className="flex-1 truncate">
155
169
  {children}
156
170
  </div>
157
- {node.badge && (
171
+ {!isEmpty(node.badge) && (
158
172
  <Badge
159
173
  {...badgeProps}
160
174
  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,11 @@ export const TreePanel = <T extends KeyValue>({
72
80
  checked,
73
81
  selected,
74
82
  matcher,
83
+ sorter,
75
84
  onSelect,
76
85
  onChecked,
86
+ defaultExpanded,
87
+ onExpandToggle,
77
88
  });
78
89
 
79
90
  const scrollerRef = useRef<HTMLDivElement>(null);
@@ -99,7 +110,7 @@ export const TreePanel = <T extends KeyValue>({
99
110
  ref,
100
111
  () => ({
101
112
  open: (id: string) => expand(id),
102
- select: (id: string) => expand(id),
113
+ select: (id: string) => select(id),
103
114
  }),
104
115
  [],
105
116
  );
@@ -151,12 +162,18 @@ export const TreePanel = <T extends KeyValue>({
151
162
  checkable={checkable}
152
163
  selectable={selectable}
153
164
  defaultLeafIcon={defaultLeafIcon}
154
- defaultNodeIcon={defaultNodeIcon}
165
+ defaultNodeIcon={
166
+ makeIcon?.(tree[index].data) ?? defaultNodeIcon
167
+ }
155
168
  onToggle={toggleExpand}
156
169
  onChecked={toggleCheck}
157
170
  onSelect={select}
171
+ onClick={onClick}
172
+ leafClassName={leafClassName}
173
+ nodeClassName={nodeClassName}
158
174
  >
159
- {(tree[index].data && renderer?.(tree[index].data)) ||
175
+ {(tree[index].data && renderer?.(tree[index].data)) ??
176
+ makeLabel?.(tree[index].data) ??
160
177
  tree[index].label}
161
178
  </TreeNode>
162
179
  </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 =
@@ -25,7 +25,11 @@ import { useDebounce } from "@react-fabric/core";
25
25
  import { EMPTY_ARRAY } from "@react-fabric/utilities";
26
26
  import memoizeOne from "memoize-one";
27
27
  import { useCallback, useEffect, useReducer, useRef } from "react";
28
- import { type InternalNode, type TreeNodeType } from "./types";
28
+ import {
29
+ type InternalNode,
30
+ type TreeNodeType,
31
+ type TreePanelProps,
32
+ } from "./types";
29
33
  import {
30
34
  filterTree,
31
35
  flattenTree,
@@ -87,25 +91,31 @@ export const useTree = <T extends KeyValue>({
87
91
  items = EMPTY_ARRAY,
88
92
  checked = EMPTY_ARRAY,
89
93
  selected,
94
+ sorter,
90
95
  matcher,
91
96
  onSelect,
92
97
  onChecked,
93
- }: {
94
- items?: Array<TreeNodeType<T>>;
95
- selected?: string;
96
- checked?: string[];
97
- onSelect?: (id: string) => void;
98
- onChecked?: (leafs: string[], nodes: string[]) => void;
99
- matcher?: (data: T, query: string) => boolean;
100
- }) => {
98
+ defaultExpanded,
99
+ onExpandToggle,
100
+ }: Partial<TreePanelProps<T>>) => {
101
101
  const componentEvents = useRef({ onSelect, onChecked });
102
102
 
103
103
  const itemList = createItemList(items);
104
104
 
105
+ const fireExpandToggle = useCallback(
106
+ (itemMap: InternalNode[]) => {
107
+ const expandedList = itemMap
108
+ .filter((node) => node.open)
109
+ .map((node) => node.id);
110
+ onExpandToggle?.(expandedList);
111
+ },
112
+ [onExpandToggle],
113
+ );
114
+
105
115
  const [state, dispatch] = useReducer(
106
116
  (state: TreeState, action: TreeActions) => {
107
117
  if (action.type === "load") {
108
- state.items = refactorTree(action.items);
118
+ state.items = refactorTree(action.items, { sorter, defaultExpanded });
109
119
  state.itemMap = makeTreeMap(state.items);
110
120
  state.tree = flattenTree(state.items);
111
121
  return { ...state };
@@ -113,6 +123,7 @@ export const useTree = <T extends KeyValue>({
113
123
  if (action.type === "toggleExpand") {
114
124
  const node = state.itemMap.get(action.id);
115
125
  node && (node.open = !node.open);
126
+ fireExpandToggle(Array.from(state.itemMap.values()));
116
127
  state.tree = flattenTree(state.items);
117
128
  return { ...state };
118
129
  }
@@ -120,6 +131,7 @@ export const useTree = <T extends KeyValue>({
120
131
  // TODO: check if children available, if not call onLoad to fetch node children
121
132
  const node = state.itemMap.get(action.id);
122
133
  node && (node.open = true);
134
+ fireExpandToggle(Array.from(state.itemMap.values()));
123
135
  state.tree = flattenTree(state.items);
124
136
  return { ...state };
125
137
  }
@@ -128,6 +140,7 @@ export const useTree = <T extends KeyValue>({
128
140
  state.itemMap.forEach((node) => {
129
141
  !node.leaf && (node.open = true);
130
142
  });
143
+ fireExpandToggle(Array.from(state.itemMap.values()));
131
144
  state.tree = flattenTree(state.items);
132
145
  return { ...state };
133
146
  }
@@ -135,6 +148,7 @@ export const useTree = <T extends KeyValue>({
135
148
  state.itemMap.forEach((node) => {
136
149
  !node.leaf && (node.open = false);
137
150
  });
151
+ fireExpandToggle([]);
138
152
  state.tree = flattenTree(state.items);
139
153
  return { ...state };
140
154
  }
@@ -149,7 +163,10 @@ export const useTree = <T extends KeyValue>({
149
163
  }
150
164
  updateSelection(state.itemMap, action.id, true);
151
165
  state.tree = flattenTree(state.items);
152
- componentEvents.current.onSelect?.(action.id);
166
+ componentEvents.current.onSelect?.(
167
+ action.id,
168
+ state.itemMap.get(action.id)?.data,
169
+ );
153
170
  return { ...state, selected: action.id };
154
171
  }
155
172
  if (action.type === "toggleCheck") {
package/src/tree/utils.ts CHANGED
@@ -24,7 +24,7 @@
24
24
  import { compareValues, isString, matchString } from "@react-fabric/utilities";
25
25
  import { type InternalNode, type TreeNodeType } from "./types";
26
26
 
27
- const sorter = (a: TreeNodeType, b: TreeNodeType) => {
27
+ const defaultSorter = (a: TreeNodeType, b: TreeNodeType) => {
28
28
  if (!!a.leaf !== !!b.leaf) return a.leaf ? 1 : -1;
29
29
  return compareValues()(a.label, b.label);
30
30
  };
@@ -48,13 +48,17 @@ const getIntermediate = (list: AnyObject[] = []) => {
48
48
  // convert node item to internal node used by the tree
49
49
  export const refactorTree = (
50
50
  nodes: TreeNodeType[],
51
+ {
52
+ sorter = defaultSorter,
53
+ defaultExpanded = [],
54
+ }: { sorter?: any; defaultExpanded?: string[] },
51
55
  options: { level: number; parent?: string } = { level: 0, parent: undefined },
52
56
  ) => {
53
57
  const list: InternalNode[] = [];
54
-
55
- nodes.sort(sorter).forEach((node) => {
58
+ if (sorter !== false) nodes = nodes.sort(sorter);
59
+ nodes.forEach((node) => {
56
60
  list.push({
57
- open: false,
61
+ open: defaultExpanded.includes(node.id),
58
62
  level: options.level,
59
63
  parent: options.parent,
60
64
  checked: 0,
@@ -66,10 +70,14 @@ export const refactorTree = (
66
70
  leaf: !!node.leaf,
67
71
  children:
68
72
  node.children &&
69
- refactorTree(node.children, {
70
- level: options.level + 1,
71
- parent: node.id,
72
- }),
73
+ refactorTree(
74
+ node.children,
75
+ { sorter, defaultExpanded },
76
+ {
77
+ level: options.level + 1,
78
+ parent: node.id,
79
+ },
80
+ ),
73
81
  });
74
82
  });
75
83