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

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,6 +5,7 @@ var core = require('@react-fabric/core');
5
5
  var utilities = require('@react-fabric/utilities');
6
6
  var classNames = require('classnames');
7
7
  var react = require('react');
8
+ var reactI18next = require('react-i18next');
8
9
  var jsxRuntime = require('react/jsx-runtime');
9
10
 
10
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; }
@@ -24,6 +25,8 @@ var TreeNode = function TreeNode(_ref) {
24
25
  children = _ref.children,
25
26
  leafClassName = _ref.leafClassName,
26
27
  nodeClassName = _ref.nodeClassName;
28
+ var _useTranslation = reactI18next.useTranslation("data"),
29
+ t = _useTranslation.t;
27
30
  var badgeProps = react.useMemo(function () {
28
31
  return core.getBadgeProps(node.badge);
29
32
  }, [node.badge]);
@@ -63,7 +66,14 @@ var TreeNode = function TreeNode(_ref) {
63
66
  className: classNames("flex flex-nowrap items-center", node.leaf ? leafClassName : nodeClassName),
64
67
  "data-testid": node["data-testid"],
65
68
  "data-test-value": node["data-test-value"],
66
- children: [filler, !node.leaf && !node.childFiltered && /*#__PURE__*/jsxRuntime.jsx("div", {
69
+ children: [filler, node.loading && /*#__PURE__*/jsxRuntime.jsx("div", {
70
+ className: "flex-initial w-6",
71
+ children: /*#__PURE__*/jsxRuntime.jsx(core.Icon, {
72
+ rtlFlip: true,
73
+ icon: core.CoreIcons.spinner,
74
+ className: "animate-spin text-muted"
75
+ })
76
+ }), !node.leaf && !node.childFiltered && !node.loading && /*#__PURE__*/jsxRuntime.jsx("div", {
67
77
  className: "flex-initial w-6",
68
78
  children: /*#__PURE__*/jsxRuntime.jsx(core.Icon, {
69
79
  rtlFlip: true,
@@ -82,9 +92,9 @@ var TreeNode = function TreeNode(_ref) {
82
92
  icon: node.checked === 2 ? core.CoreIcons.checkboxInt : node.checked === 1 ? core.CoreIcons.checkboxOn : core.CoreIcons.checkboxOff,
83
93
  className: classNames(node.disabled ? "text-muted" : "text-primary-500 hover:text-primary-600")
84
94
  })
85
- }), /*#__PURE__*/jsxRuntime.jsxs("div", {
95
+ }), !node.empty && /*#__PURE__*/jsxRuntime.jsxs("div", {
86
96
  role: "none",
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"),
97
+ className: classNames("group/tool 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"),
88
98
  "data-selected": node.selected,
89
99
  onClick: function onClick() {
90
100
  _onClick === null || _onClick === void 0 || _onClick(node.id, node.data);
@@ -108,6 +118,9 @@ var TreeNode = function TreeNode(_ref) {
108
118
  }), !utilities.isEmpty(node.badge) && /*#__PURE__*/jsxRuntime.jsx(core.Badge, _objectSpread(_objectSpread({}, badgeProps), {}, {
109
119
  className: classNames("me-1 self-center flex-content bg-opacity-50", badgeProps.className)
110
120
  }))]
121
+ }), node.empty && /*#__PURE__*/jsxRuntime.jsx("div", {
122
+ className: "text-muted text-sm select-none",
123
+ children: t("tree.noItems")
111
124
  })]
112
125
  });
113
126
  };
@@ -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", "onClick", "sorter", "makeLabel", "makeIcon", "defaultExpanded", "onExpandToggle", "leafClassName", "nodeClassName"];
14
+ var _excluded = ["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"];
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) {
@@ -31,9 +31,10 @@ var TreePanel = function TreePanel(_ref) {
31
31
  defaultNodeIcon = _ref.defaultNodeIcon,
32
32
  _ref$expander = _ref.expander,
33
33
  expander = _ref$expander === void 0 ? "box" : _ref$expander,
34
- onChecked = _ref.onChecked;
35
- _ref.onLoad;
36
- var onSelect = _ref.onSelect,
34
+ onChecked = _ref.onChecked,
35
+ onLoad = _ref.onLoad,
36
+ onQuery = _ref.onQuery,
37
+ onSelect = _ref.onSelect,
37
38
  onClick = _ref.onClick,
38
39
  sorter = _ref.sorter,
39
40
  makeLabel = _ref.makeLabel,
@@ -49,6 +50,8 @@ var TreePanel = function TreePanel(_ref) {
49
50
  selected: selected,
50
51
  matcher: matcher,
51
52
  sorter: sorter,
53
+ onLoad: onLoad,
54
+ onQuery: onQuery,
52
55
  onSelect: onSelect,
53
56
  onChecked: onChecked,
54
57
  defaultExpanded: defaultExpanded,
@@ -21,6 +21,8 @@ var useTree = function useTree(_ref) {
21
21
  selected = _ref.selected,
22
22
  sorter = _ref.sorter,
23
23
  matcher = _ref.matcher,
24
+ onLoad = _ref.onLoad,
25
+ onQuery = _ref.onQuery,
24
26
  onSelect = _ref.onSelect,
25
27
  onChecked = _ref.onChecked,
26
28
  defaultExpanded = _ref.defaultExpanded,
@@ -29,6 +31,7 @@ var useTree = function useTree(_ref) {
29
31
  onSelect: onSelect,
30
32
  onChecked: onChecked
31
33
  });
34
+ var intialExpand = react.useRef(defaultExpanded !== null && defaultExpanded !== void 0 ? defaultExpanded : utilities.EMPTY_ARRAY);
32
35
  var itemList = createItemList(items);
33
36
  var fireExpandToggle = react.useCallback(function (itemMap) {
34
37
  var expandedList = itemMap.filter(function (node) {
@@ -38,6 +41,41 @@ var useTree = function useTree(_ref) {
38
41
  });
39
42
  onExpandToggle === null || onExpandToggle === void 0 || onExpandToggle(expandedList);
40
43
  }, [onExpandToggle]);
44
+ var loadChildren = react.useCallback(function (node) {
45
+ var _node$children;
46
+ if (node.open && !node.leaf && !((_node$children = node.children) !== null && _node$children !== void 0 && _node$children.length)) {
47
+ node.loading = true;
48
+ void Promise.resolve(onLoad === null || onLoad === void 0 ? void 0 : onLoad(node.id)).then(function () {
49
+ var resp = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : utilities.EMPTY_ARRAY;
50
+ node.children = utils.refactorTree(resp, {
51
+ sorter: sorter,
52
+ defaultExpanded: defaultExpanded
53
+ }, {
54
+ level: node.level + 1,
55
+ parent: node.id
56
+ });
57
+ })["finally"](function () {
58
+ var _intialExpand$current;
59
+ node.loading = false;
60
+ dispatch({
61
+ type: "loaded"
62
+ });
63
+ ((_intialExpand$current = intialExpand.current) === null || _intialExpand$current === void 0 ? void 0 : _intialExpand$current.length) > 0 && dispatch({
64
+ type: "expand",
65
+ id: intialExpand.current.shift()
66
+ });
67
+ });
68
+ } else {
69
+ var _intialExpand$current2;
70
+ dispatch({
71
+ type: "loaded"
72
+ });
73
+ ((_intialExpand$current2 = intialExpand.current) === null || _intialExpand$current2 === void 0 ? void 0 : _intialExpand$current2.length) > 0 && dispatch({
74
+ type: "expand",
75
+ id: intialExpand.current.shift()
76
+ });
77
+ }
78
+ }, [onLoad]);
41
79
  var _useReducer = react.useReducer(function (state, action) {
42
80
  if (action.type === "load") {
43
81
  state.items = utils.refactorTree(action.items, {
@@ -50,23 +88,37 @@ var useTree = function useTree(_ref) {
50
88
  }
51
89
  if (action.type === "toggleExpand") {
52
90
  var node = state.itemMap.get(action.id);
53
- node && (node.open = !node.open);
91
+ if (node && !node.leaf) {
92
+ node && (node.open = !node.open);
93
+ loadChildren(node);
94
+ return _objectSpread({}, state);
95
+ }
96
+ }
97
+ if (action.type === "loaded") {
98
+ state.itemMap = utils.makeTreeMap(state.items);
54
99
  fireExpandToggle(Array.from(state.itemMap.values()));
55
100
  state.tree = utils.flattenTree(state.items);
101
+ state.selected && utils.updateSelection(state.itemMap, state.selected, true);
56
102
  return _objectSpread({}, state);
57
103
  }
58
104
  if (action.type === "expand") {
59
- // TODO: check if children available, if not call onLoad to fetch node children
60
105
  var _node = state.itemMap.get(action.id);
61
- _node && (_node.open = true);
62
- fireExpandToggle(Array.from(state.itemMap.values()));
63
- state.tree = utils.flattenTree(state.items);
64
- return _objectSpread({}, state);
106
+ if (_node && !_node.leaf) {
107
+ _node.open = true;
108
+ var parentId = _node.parent;
109
+ while (parentId) {
110
+ var _node2 = state.itemMap.get(parentId);
111
+ _node2 && (_node2.open = true);
112
+ parentId = _node2 === null || _node2 === void 0 ? void 0 : _node2.parent;
113
+ }
114
+ loadChildren(_node);
115
+ return _objectSpread({}, state);
116
+ }
65
117
  }
66
118
  if (action.type === "expandAll") {
67
- // TODO: prevent opening dynamic nodes that dont have children
68
119
  state.itemMap.forEach(function (node) {
69
- !node.leaf && (node.open = true);
120
+ var _node$children2;
121
+ !node.leaf && ((_node$children2 = node.children) === null || _node$children2 === void 0 ? void 0 : _node$children2.length) && (node.open = true);
70
122
  });
71
123
  fireExpandToggle(Array.from(state.itemMap.values()));
72
124
  state.tree = utils.flattenTree(state.items);
@@ -81,9 +133,14 @@ var useTree = function useTree(_ref) {
81
133
  return _objectSpread({}, state);
82
134
  }
83
135
  if (action.type === "filter") {
84
- utils.filterTree(state.itemMap, action.query, matcher);
85
- state.tree = utils.flattenTree(state.items);
86
- return _objectSpread({}, state);
136
+ if (onQuery) {
137
+ var _action$query;
138
+ onQuery((_action$query = action.query) !== null && _action$query !== void 0 ? _action$query : "");
139
+ } else {
140
+ utils.filterTree(state.itemMap, action.query, matcher);
141
+ state.tree = utils.flattenTree(state.items);
142
+ return _objectSpread({}, state);
143
+ }
87
144
  }
88
145
  if (action.type === "select") {
89
146
  var _componentEvents$curr, _componentEvents$curr2, _state$itemMap$get;
@@ -134,10 +191,15 @@ var useTree = function useTree(_ref) {
134
191
  };
135
192
  }, [onSelect, onChecked]);
136
193
  react.useEffect(function () {
194
+ var _intialExpand$current3;
137
195
  dispatch({
138
196
  type: "load",
139
197
  items: itemList
140
198
  });
199
+ itemList.length && ((_intialExpand$current3 = intialExpand.current) === null || _intialExpand$current3 === void 0 ? void 0 : _intialExpand$current3.length) > 0 && dispatch({
200
+ type: "expand",
201
+ id: intialExpand.current.shift()
202
+ });
141
203
  }, [itemList]);
142
204
  react.useEffect(function () {
143
205
  selected && dispatch({
@@ -40,8 +40,9 @@ var refactorTree = function refactorTree(nodes, _ref) {
40
40
  var list = [];
41
41
  if (sorter !== false) nodes = nodes.sort(sorter);
42
42
  nodes.forEach(function (node) {
43
+ var _node$children;
43
44
  list.push(_objectSpread(_objectSpread({
44
- open: defaultExpanded.includes(node.id),
45
+ open: defaultExpanded.includes(node.id) && !!((_node$children = node.children) !== null && _node$children !== void 0 && _node$children.length),
45
46
  level: options.level,
46
47
  parent: options.parent,
47
48
  checked: 0,
@@ -80,11 +81,17 @@ var flattenTree = function flattenTree(nodes) {
80
81
  return !node.filtered;
81
82
  });
82
83
  filtered.forEach(function (node, idx) {
84
+ var _node$children2;
83
85
  node.lines = [].concat(_toConsumableArray(lines), [idx === filtered.length - 1 ? 2 : 3]);
84
86
  list.push(node);
85
87
  (node.open || node.childFiltered) && node.children && list.push.apply(list, _toConsumableArray(flattenTree(node.children.filter(function (child) {
86
88
  return !child.filtered;
87
89
  }), [].concat(_toConsumableArray(lines), [idx === filtered.length - 1 ? 0 : 1]))));
90
+ node.open && ((_node$children2 = node.children) === null || _node$children2 === void 0 ? void 0 : _node$children2.length) === 0 && list.push({
91
+ empty: true,
92
+ leaf: true,
93
+ lines: [].concat(_toConsumableArray(lines), [idx === filtered.length - 1 ? 0 : 1, 2])
94
+ });
88
95
  });
89
96
  return list;
90
97
  };
@@ -5,6 +5,7 @@ import { getBadgeProps, Icon, CoreIcons, Badge } from '@react-fabric/core';
5
5
  import { isString, isEmpty } from '@react-fabric/utilities';
6
6
  import classNames from 'classnames';
7
7
  import { useMemo, isValidElement } from 'react';
8
+ import { useTranslation } from 'react-i18next';
8
9
  import { jsx, jsxs } from 'react/jsx-runtime';
9
10
 
10
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; }
@@ -24,6 +25,8 @@ var TreeNode = function TreeNode(_ref) {
24
25
  children = _ref.children,
25
26
  leafClassName = _ref.leafClassName,
26
27
  nodeClassName = _ref.nodeClassName;
28
+ var _useTranslation = useTranslation("data"),
29
+ t = _useTranslation.t;
27
30
  var badgeProps = useMemo(function () {
28
31
  return getBadgeProps(node.badge);
29
32
  }, [node.badge]);
@@ -63,7 +66,14 @@ var TreeNode = function TreeNode(_ref) {
63
66
  className: classNames("flex flex-nowrap items-center", node.leaf ? leafClassName : nodeClassName),
64
67
  "data-testid": node["data-testid"],
65
68
  "data-test-value": node["data-test-value"],
66
- children: [filler, !node.leaf && !node.childFiltered && /*#__PURE__*/jsx("div", {
69
+ children: [filler, node.loading && /*#__PURE__*/jsx("div", {
70
+ className: "flex-initial w-6",
71
+ children: /*#__PURE__*/jsx(Icon, {
72
+ rtlFlip: true,
73
+ icon: CoreIcons.spinner,
74
+ className: "animate-spin text-muted"
75
+ })
76
+ }), !node.leaf && !node.childFiltered && !node.loading && /*#__PURE__*/jsx("div", {
67
77
  className: "flex-initial w-6",
68
78
  children: /*#__PURE__*/jsx(Icon, {
69
79
  rtlFlip: true,
@@ -82,9 +92,9 @@ var TreeNode = function TreeNode(_ref) {
82
92
  icon: node.checked === 2 ? CoreIcons.checkboxInt : node.checked === 1 ? CoreIcons.checkboxOn : CoreIcons.checkboxOff,
83
93
  className: classNames(node.disabled ? "text-muted" : "text-primary-500 hover:text-primary-600")
84
94
  })
85
- }), /*#__PURE__*/jsxs("div", {
95
+ }), !node.empty && /*#__PURE__*/jsxs("div", {
86
96
  role: "none",
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"),
97
+ className: classNames("group/tool 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"),
88
98
  "data-selected": node.selected,
89
99
  onClick: function onClick() {
90
100
  _onClick === null || _onClick === void 0 || _onClick(node.id, node.data);
@@ -108,6 +118,9 @@ var TreeNode = function TreeNode(_ref) {
108
118
  }), !isEmpty(node.badge) && /*#__PURE__*/jsx(Badge, _objectSpread(_objectSpread({}, badgeProps), {}, {
109
119
  className: classNames("me-1 self-center flex-content bg-opacity-50", badgeProps.className)
110
120
  }))]
121
+ }), node.empty && /*#__PURE__*/jsx("div", {
122
+ className: "text-muted text-sm select-none",
123
+ children: t("tree.noItems")
111
124
  })]
112
125
  });
113
126
  };
@@ -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", "onClick", "sorter", "makeLabel", "makeIcon", "defaultExpanded", "onExpandToggle", "leafClassName", "nodeClassName"];
14
+ var _excluded = ["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"];
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) {
@@ -31,9 +31,10 @@ var TreePanel = function TreePanel(_ref) {
31
31
  defaultNodeIcon = _ref.defaultNodeIcon,
32
32
  _ref$expander = _ref.expander,
33
33
  expander = _ref$expander === void 0 ? "box" : _ref$expander,
34
- onChecked = _ref.onChecked;
35
- _ref.onLoad;
36
- var onSelect = _ref.onSelect,
34
+ onChecked = _ref.onChecked,
35
+ onLoad = _ref.onLoad,
36
+ onQuery = _ref.onQuery,
37
+ onSelect = _ref.onSelect,
37
38
  onClick = _ref.onClick,
38
39
  sorter = _ref.sorter,
39
40
  makeLabel = _ref.makeLabel,
@@ -49,6 +50,8 @@ var TreePanel = function TreePanel(_ref) {
49
50
  selected: selected,
50
51
  matcher: matcher,
51
52
  sorter: sorter,
53
+ onLoad: onLoad,
54
+ onQuery: onQuery,
52
55
  onSelect: onSelect,
53
56
  onChecked: onChecked,
54
57
  defaultExpanded: defaultExpanded,
@@ -6,7 +6,7 @@ import { useDebounce } from '@react-fabric/core';
6
6
  import { EMPTY_ARRAY } from '@react-fabric/utilities';
7
7
  import memoizeOne from 'memoize-one';
8
8
  import { useRef, useCallback, useReducer, useEffect } from 'react';
9
- import { refactorTree, makeTreeMap, flattenTree, filterTree, updateSelection, updateChecked } from './utils.js';
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; }
@@ -21,6 +21,8 @@ var useTree = function useTree(_ref) {
21
21
  selected = _ref.selected,
22
22
  sorter = _ref.sorter,
23
23
  matcher = _ref.matcher,
24
+ onLoad = _ref.onLoad,
25
+ onQuery = _ref.onQuery,
24
26
  onSelect = _ref.onSelect,
25
27
  onChecked = _ref.onChecked,
26
28
  defaultExpanded = _ref.defaultExpanded,
@@ -29,6 +31,7 @@ var useTree = function useTree(_ref) {
29
31
  onSelect: onSelect,
30
32
  onChecked: onChecked
31
33
  });
34
+ var intialExpand = useRef(defaultExpanded !== null && defaultExpanded !== void 0 ? defaultExpanded : EMPTY_ARRAY);
32
35
  var itemList = createItemList(items);
33
36
  var fireExpandToggle = useCallback(function (itemMap) {
34
37
  var expandedList = itemMap.filter(function (node) {
@@ -38,6 +41,41 @@ var useTree = function useTree(_ref) {
38
41
  });
39
42
  onExpandToggle === null || onExpandToggle === void 0 || onExpandToggle(expandedList);
40
43
  }, [onExpandToggle]);
44
+ var loadChildren = useCallback(function (node) {
45
+ var _node$children;
46
+ if (node.open && !node.leaf && !((_node$children = node.children) !== null && _node$children !== void 0 && _node$children.length)) {
47
+ node.loading = true;
48
+ void Promise.resolve(onLoad === null || onLoad === void 0 ? void 0 : onLoad(node.id)).then(function () {
49
+ var resp = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : EMPTY_ARRAY;
50
+ node.children = refactorTree(resp, {
51
+ sorter: sorter,
52
+ defaultExpanded: defaultExpanded
53
+ }, {
54
+ level: node.level + 1,
55
+ parent: node.id
56
+ });
57
+ })["finally"](function () {
58
+ var _intialExpand$current;
59
+ node.loading = false;
60
+ dispatch({
61
+ type: "loaded"
62
+ });
63
+ ((_intialExpand$current = intialExpand.current) === null || _intialExpand$current === void 0 ? void 0 : _intialExpand$current.length) > 0 && dispatch({
64
+ type: "expand",
65
+ id: intialExpand.current.shift()
66
+ });
67
+ });
68
+ } else {
69
+ var _intialExpand$current2;
70
+ dispatch({
71
+ type: "loaded"
72
+ });
73
+ ((_intialExpand$current2 = intialExpand.current) === null || _intialExpand$current2 === void 0 ? void 0 : _intialExpand$current2.length) > 0 && dispatch({
74
+ type: "expand",
75
+ id: intialExpand.current.shift()
76
+ });
77
+ }
78
+ }, [onLoad]);
41
79
  var _useReducer = useReducer(function (state, action) {
42
80
  if (action.type === "load") {
43
81
  state.items = refactorTree(action.items, {
@@ -50,23 +88,37 @@ var useTree = function useTree(_ref) {
50
88
  }
51
89
  if (action.type === "toggleExpand") {
52
90
  var node = state.itemMap.get(action.id);
53
- node && (node.open = !node.open);
91
+ if (node && !node.leaf) {
92
+ node && (node.open = !node.open);
93
+ loadChildren(node);
94
+ return _objectSpread({}, state);
95
+ }
96
+ }
97
+ if (action.type === "loaded") {
98
+ state.itemMap = makeTreeMap(state.items);
54
99
  fireExpandToggle(Array.from(state.itemMap.values()));
55
100
  state.tree = flattenTree(state.items);
101
+ state.selected && updateSelection(state.itemMap, state.selected, true);
56
102
  return _objectSpread({}, state);
57
103
  }
58
104
  if (action.type === "expand") {
59
- // TODO: check if children available, if not call onLoad to fetch node children
60
105
  var _node = state.itemMap.get(action.id);
61
- _node && (_node.open = true);
62
- fireExpandToggle(Array.from(state.itemMap.values()));
63
- state.tree = flattenTree(state.items);
64
- return _objectSpread({}, state);
106
+ if (_node && !_node.leaf) {
107
+ _node.open = true;
108
+ var parentId = _node.parent;
109
+ while (parentId) {
110
+ var _node2 = state.itemMap.get(parentId);
111
+ _node2 && (_node2.open = true);
112
+ parentId = _node2 === null || _node2 === void 0 ? void 0 : _node2.parent;
113
+ }
114
+ loadChildren(_node);
115
+ return _objectSpread({}, state);
116
+ }
65
117
  }
66
118
  if (action.type === "expandAll") {
67
- // TODO: prevent opening dynamic nodes that dont have children
68
119
  state.itemMap.forEach(function (node) {
69
- !node.leaf && (node.open = true);
120
+ var _node$children2;
121
+ !node.leaf && ((_node$children2 = node.children) === null || _node$children2 === void 0 ? void 0 : _node$children2.length) && (node.open = true);
70
122
  });
71
123
  fireExpandToggle(Array.from(state.itemMap.values()));
72
124
  state.tree = flattenTree(state.items);
@@ -81,9 +133,14 @@ var useTree = function useTree(_ref) {
81
133
  return _objectSpread({}, state);
82
134
  }
83
135
  if (action.type === "filter") {
84
- filterTree(state.itemMap, action.query, matcher);
85
- state.tree = flattenTree(state.items);
86
- return _objectSpread({}, state);
136
+ if (onQuery) {
137
+ var _action$query;
138
+ onQuery((_action$query = action.query) !== null && _action$query !== void 0 ? _action$query : "");
139
+ } else {
140
+ filterTree(state.itemMap, action.query, matcher);
141
+ state.tree = flattenTree(state.items);
142
+ return _objectSpread({}, state);
143
+ }
87
144
  }
88
145
  if (action.type === "select") {
89
146
  var _componentEvents$curr, _componentEvents$curr2, _state$itemMap$get;
@@ -134,10 +191,15 @@ var useTree = function useTree(_ref) {
134
191
  };
135
192
  }, [onSelect, onChecked]);
136
193
  useEffect(function () {
194
+ var _intialExpand$current3;
137
195
  dispatch({
138
196
  type: "load",
139
197
  items: itemList
140
198
  });
199
+ itemList.length && ((_intialExpand$current3 = intialExpand.current) === null || _intialExpand$current3 === void 0 ? void 0 : _intialExpand$current3.length) > 0 && dispatch({
200
+ type: "expand",
201
+ id: intialExpand.current.shift()
202
+ });
141
203
  }, [itemList]);
142
204
  useEffect(function () {
143
205
  selected && dispatch({
@@ -40,8 +40,9 @@ var refactorTree = function refactorTree(nodes, _ref) {
40
40
  var list = [];
41
41
  if (sorter !== false) nodes = nodes.sort(sorter);
42
42
  nodes.forEach(function (node) {
43
+ var _node$children;
43
44
  list.push(_objectSpread(_objectSpread({
44
- open: defaultExpanded.includes(node.id),
45
+ open: defaultExpanded.includes(node.id) && !!((_node$children = node.children) !== null && _node$children !== void 0 && _node$children.length),
45
46
  level: options.level,
46
47
  parent: options.parent,
47
48
  checked: 0,
@@ -80,11 +81,17 @@ var flattenTree = function flattenTree(nodes) {
80
81
  return !node.filtered;
81
82
  });
82
83
  filtered.forEach(function (node, idx) {
84
+ var _node$children2;
83
85
  node.lines = [].concat(_toConsumableArray(lines), [idx === filtered.length - 1 ? 2 : 3]);
84
86
  list.push(node);
85
87
  (node.open || node.childFiltered) && node.children && list.push.apply(list, _toConsumableArray(flattenTree(node.children.filter(function (child) {
86
88
  return !child.filtered;
87
89
  }), [].concat(_toConsumableArray(lines), [idx === filtered.length - 1 ? 0 : 1]))));
90
+ node.open && ((_node$children2 = node.children) === null || _node$children2 === void 0 ? void 0 : _node$children2.length) === 0 && list.push({
91
+ empty: true,
92
+ leaf: true,
93
+ lines: [].concat(_toConsumableArray(lines), [idx === filtered.length - 1 ? 0 : 1, 2])
94
+ });
88
95
  });
89
96
  return list;
90
97
  };
@@ -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, onClick, sorter, makeLabel, makeIcon, defaultExpanded, onExpandToggle, leafClassName, nodeClassName, ...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, onQuery, onSelect, onClick, sorter, makeLabel, makeIcon, defaultExpanded, onExpandToggle, leafClassName, nodeClassName, ...aria }: TreePanelProps<T>) => import("react/jsx-runtime").JSX.Element;
@@ -125,6 +125,10 @@ export interface TreePanelProps<T extends KeyValue = KeyValue> extends TestProps
125
125
  * callback for lazy loading tree items
126
126
  */
127
127
  onLoad?: (id: string) => Promise<Array<TreeNodeType<T>> | undefined> | Array<TreeNodeType<T>> | undefined;
128
+ /**
129
+ * callback to load tree using search query
130
+ */
131
+ onQuery?: (query: string) => void;
128
132
  /**
129
133
  * callback on click of tree node
130
134
  */
@@ -1,5 +1,5 @@
1
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>>) => {
2
+ export declare const useTree: <T extends KeyValue<any>>({ items, checked, selected, sorter, matcher, onLoad, onQuery, onSelect, onChecked, defaultExpanded, onExpandToggle, }: Partial<TreePanelProps<T>>) => {
3
3
  tree: InternalNode[];
4
4
  toggleExpand: (id: string) => void;
5
5
  expandAll: () => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-fabric/data",
3
- "version": "1.0.0-beta.26",
3
+ "version": "1.0.0-beta.28",
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.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",
36
+ "@react-fabric/core": "^1.0.0-beta.28",
37
+ "@react-fabric/date": "^1.0.0-beta.28",
38
+ "@react-fabric/form": "^1.0.0-beta.28",
39
+ "@react-fabric/utilities": "^1.0.0-beta.28",
40
40
  "@tanstack/react-virtual": "^3.8.6",
41
41
  "classnames": "^2.5.1",
42
42
  "color": "^4.2.3",
@@ -52,5 +52,5 @@
52
52
  "node": ">=18.12.0",
53
53
  "npm": ">=8.19.0"
54
54
  },
55
- "gitHead": "452f821dc8b22c5765b3858cc53ad9d7e2f9bcf1"
55
+ "gitHead": "80f9d8a9f96dbd0e7a4287087e23996682bf2c0a"
56
56
  }
@@ -25,6 +25,7 @@ import { Badge, CoreIcons, getBadgeProps, Icon } from "@react-fabric/core";
25
25
  import { isEmpty, isString } from "@react-fabric/utilities";
26
26
  import classNames from "classnames";
27
27
  import { isValidElement, useMemo } from "react";
28
+ import { useTranslation } from "react-i18next";
28
29
  import { type TreeNodeProps } from "./types";
29
30
 
30
31
  export const TreeNode = ({
@@ -43,6 +44,7 @@ export const TreeNode = ({
43
44
  leafClassName,
44
45
  nodeClassName,
45
46
  }: TreeNodeProps) => {
47
+ const { t } = useTranslation("data");
46
48
  const badgeProps = useMemo(() => {
47
49
  return getBadgeProps(node.badge);
48
50
  }, [node.badge]);
@@ -103,7 +105,16 @@ export const TreeNode = ({
103
105
  data-test-value={node["data-test-value"]}
104
106
  >
105
107
  {filler}
106
- {!node.leaf && !node.childFiltered && (
108
+ {node.loading && (
109
+ <div className="flex-initial w-6">
110
+ <Icon
111
+ rtlFlip
112
+ icon={CoreIcons.spinner}
113
+ className="animate-spin text-muted"
114
+ />
115
+ </div>
116
+ )}
117
+ {!node.leaf && !node.childFiltered && !node.loading && (
107
118
  <div className="flex-initial w-6">
108
119
  <Icon
109
120
  rtlFlip
@@ -132,52 +143,59 @@ export const TreeNode = ({
132
143
  />
133
144
  </div>
134
145
  )}
135
- <div
136
- role="none"
137
- className={classNames(
138
- "group data-[selected]:bg-primary-100 flex flex-nowrap flex-1 overflow-hidden select-none",
139
- node.childSelected && "font-medium",
140
- (canSelect || onClick) && "hover:bg-primary-50 cursor-pointer",
141
- )}
142
- data-selected={node.selected}
143
- onClick={() => {
144
- onClick?.(node.id, node.data);
145
- canSelect
146
- ? onSelect(node.id)
147
- : canCheck
148
- ? onChecked(node.id)
149
- : onToggle(node.id);
150
- }}
151
- >
152
- {nodeIcon && (
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
- )}
146
+ {!node.empty && (
147
+ <div
148
+ role="none"
149
+ className={classNames(
150
+ "group/tool data-[selected]:bg-primary-100 flex flex-nowrap flex-1 overflow-hidden select-none",
151
+ node.childSelected && "font-medium",
152
+ (canSelect || onClick) && "hover:bg-primary-50 cursor-pointer",
153
+ )}
154
+ data-selected={node.selected}
155
+ onClick={() => {
156
+ onClick?.(node.id, node.data);
157
+ canSelect
158
+ ? onSelect(node.id)
159
+ : canCheck
160
+ ? onChecked(node.id)
161
+ : onToggle(node.id);
162
+ }}
163
+ >
164
+ {nodeIcon && (
165
+ <div className="flex-content w-6 self-center leading-none">
166
+ {isValidElement(nodeIcon) && nodeIcon}
167
+ {isString(nodeIcon) && (
168
+ <Icon
169
+ icon={nodeIcon}
170
+ bg={node.iconBg}
171
+ color={node.iconColor}
172
+ rtlFlip={node.rtlFlip}
173
+ />
174
+ )}
175
+ </div>
176
+ )}
177
+ {node.childSelected && !node.open && (
178
+ <span className=" flex-content text-primary-500">•</span>
179
+ )}
180
+ <div role="none" className="flex-1 truncate">
181
+ {children}
163
182
  </div>
164
- )}
165
- {node.childSelected && !node.open && (
166
- <span className=" flex-content text-primary-500">•</span>
167
- )}
168
- <div role="none" className="flex-1 truncate">
169
- {children}
183
+ {!isEmpty(node.badge) && (
184
+ <Badge
185
+ {...badgeProps}
186
+ className={classNames(
187
+ "me-1 self-center flex-content bg-opacity-50",
188
+ badgeProps.className,
189
+ )}
190
+ />
191
+ )}
170
192
  </div>
171
- {!isEmpty(node.badge) && (
172
- <Badge
173
- {...badgeProps}
174
- className={classNames(
175
- "me-1 self-center flex-content bg-opacity-50",
176
- badgeProps.className,
177
- )}
178
- />
179
- )}
180
- </div>
193
+ )}
194
+ {node.empty && (
195
+ <div className="text-muted text-sm select-none">
196
+ {t("tree.noItems")}
197
+ </div>
198
+ )}
181
199
  </div>
182
200
  );
183
201
  };
@@ -55,6 +55,7 @@ export const TreePanel = <T extends KeyValue>({
55
55
  expander = "box",
56
56
  onChecked,
57
57
  onLoad,
58
+ onQuery,
58
59
  onSelect,
59
60
  onClick,
60
61
  sorter,
@@ -81,6 +82,8 @@ export const TreePanel = <T extends KeyValue>({
81
82
  selected,
82
83
  matcher,
83
84
  sorter,
85
+ onLoad,
86
+ onQuery,
84
87
  onSelect,
85
88
  onChecked,
86
89
  defaultExpanded,
package/src/tree/types.ts CHANGED
@@ -174,6 +174,10 @@ export interface TreePanelProps<T extends KeyValue = KeyValue>
174
174
  | Promise<Array<TreeNodeType<T>> | undefined>
175
175
  | Array<TreeNodeType<T>>
176
176
  | undefined;
177
+ /**
178
+ * callback to load tree using search query
179
+ */
180
+ onQuery?: (query: string) => void;
177
181
  /**
178
182
  * callback on click of tree node
179
183
  */
@@ -75,7 +75,11 @@ type TreeActions =
75
75
  /**
76
76
  * collapse all nodes
77
77
  */
78
- | { type: "collapseAll" };
78
+ | { type: "collapseAll" }
79
+ /**
80
+ * loaded dynamic nodes
81
+ */
82
+ | { type: "loaded" };
79
83
 
80
84
  interface TreeState {
81
85
  items: InternalNode[];
@@ -93,13 +97,15 @@ export const useTree = <T extends KeyValue>({
93
97
  selected,
94
98
  sorter,
95
99
  matcher,
100
+ onLoad,
101
+ onQuery,
96
102
  onSelect,
97
103
  onChecked,
98
104
  defaultExpanded,
99
105
  onExpandToggle,
100
106
  }: Partial<TreePanelProps<T>>) => {
101
107
  const componentEvents = useRef({ onSelect, onChecked });
102
-
108
+ const intialExpand = useRef(defaultExpanded ?? EMPTY_ARRAY);
103
109
  const itemList = createItemList(items);
104
110
 
105
111
  const fireExpandToggle = useCallback(
@@ -112,6 +118,39 @@ export const useTree = <T extends KeyValue>({
112
118
  [onExpandToggle],
113
119
  );
114
120
 
121
+ const loadChildren = useCallback(
122
+ (node: InternalNode) => {
123
+ if (node.open && !node.leaf && !node.children?.length) {
124
+ node.loading = true;
125
+ void Promise.resolve(onLoad?.(node.id))
126
+ .then((resp = EMPTY_ARRAY) => {
127
+ node.children = refactorTree(
128
+ resp,
129
+ { sorter, defaultExpanded },
130
+ { level: node.level + 1, parent: node.id },
131
+ );
132
+ })
133
+ .finally(() => {
134
+ node.loading = false;
135
+ dispatch({ type: "loaded" });
136
+ intialExpand.current?.length > 0 &&
137
+ dispatch({
138
+ type: "expand",
139
+ id: intialExpand.current.shift() as unknown as string,
140
+ });
141
+ });
142
+ } else {
143
+ dispatch({ type: "loaded" });
144
+ intialExpand.current?.length > 0 &&
145
+ dispatch({
146
+ type: "expand",
147
+ id: intialExpand.current.shift() as unknown as string,
148
+ });
149
+ }
150
+ },
151
+ [onLoad],
152
+ );
153
+
115
154
  const [state, dispatch] = useReducer(
116
155
  (state: TreeState, action: TreeActions) => {
117
156
  if (action.type === "load") {
@@ -122,23 +161,36 @@ export const useTree = <T extends KeyValue>({
122
161
  }
123
162
  if (action.type === "toggleExpand") {
124
163
  const node = state.itemMap.get(action.id);
125
- node && (node.open = !node.open);
164
+ if (node && !node.leaf) {
165
+ node && (node.open = !node.open);
166
+ loadChildren(node);
167
+ return { ...state };
168
+ }
169
+ }
170
+ if (action.type === "loaded") {
171
+ state.itemMap = makeTreeMap(state.items);
126
172
  fireExpandToggle(Array.from(state.itemMap.values()));
127
173
  state.tree = flattenTree(state.items);
174
+ state.selected && updateSelection(state.itemMap, state.selected, true);
128
175
  return { ...state };
129
176
  }
130
177
  if (action.type === "expand") {
131
- // TODO: check if children available, if not call onLoad to fetch node children
132
178
  const node = state.itemMap.get(action.id);
133
- node && (node.open = true);
134
- fireExpandToggle(Array.from(state.itemMap.values()));
135
- state.tree = flattenTree(state.items);
136
- return { ...state };
179
+ if (node && !node.leaf) {
180
+ node.open = true;
181
+ let parentId = node.parent;
182
+ while (parentId) {
183
+ const node = state.itemMap.get(parentId);
184
+ node && (node.open = true);
185
+ parentId = node?.parent;
186
+ }
187
+ loadChildren(node);
188
+ return { ...state };
189
+ }
137
190
  }
138
191
  if (action.type === "expandAll") {
139
- // TODO: prevent opening dynamic nodes that dont have children
140
192
  state.itemMap.forEach((node) => {
141
- !node.leaf && (node.open = true);
193
+ !node.leaf && node.children?.length && (node.open = true);
142
194
  });
143
195
  fireExpandToggle(Array.from(state.itemMap.values()));
144
196
  state.tree = flattenTree(state.items);
@@ -153,9 +205,13 @@ export const useTree = <T extends KeyValue>({
153
205
  return { ...state };
154
206
  }
155
207
  if (action.type === "filter") {
156
- filterTree(state.itemMap, action.query, matcher);
157
- state.tree = flattenTree(state.items);
158
- return { ...state };
208
+ if (onQuery) {
209
+ onQuery(action.query ?? "");
210
+ } else {
211
+ filterTree(state.itemMap, action.query, matcher);
212
+ state.tree = flattenTree(state.items);
213
+ return { ...state };
214
+ }
159
215
  }
160
216
  if (action.type === "select") {
161
217
  if (state.selected) {
@@ -198,6 +254,12 @@ export const useTree = <T extends KeyValue>({
198
254
 
199
255
  useEffect(() => {
200
256
  dispatch({ type: "load", items: itemList });
257
+ itemList.length &&
258
+ intialExpand.current?.length > 0 &&
259
+ dispatch({
260
+ type: "expand",
261
+ id: intialExpand.current.shift() as unknown as string,
262
+ });
201
263
  }, [itemList]);
202
264
 
203
265
  useEffect(() => {
package/src/tree/utils.ts CHANGED
@@ -58,7 +58,7 @@ export const refactorTree = (
58
58
  if (sorter !== false) nodes = nodes.sort(sorter);
59
59
  nodes.forEach((node) => {
60
60
  list.push({
61
- open: defaultExpanded.includes(node.id),
61
+ open: defaultExpanded.includes(node.id) && !!node.children?.length,
62
62
  level: options.level,
63
63
  parent: options.parent,
64
64
  checked: 0,
@@ -114,6 +114,13 @@ export const flattenTree = (
114
114
  [...lines, idx === filtered.length - 1 ? 0 : 1],
115
115
  ),
116
116
  );
117
+ node.open &&
118
+ node.children?.length === 0 &&
119
+ list.push({
120
+ empty: true,
121
+ leaf: true,
122
+ lines: [...lines, idx === filtered.length - 1 ? 0 : 1, 2],
123
+ } as any);
117
124
  });
118
125
  return list;
119
126
  };