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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -63,7 +63,14 @@ var TreeNode = function TreeNode(_ref) {
63
63
  className: classNames("flex flex-nowrap items-center", node.leaf ? leafClassName : nodeClassName),
64
64
  "data-testid": node["data-testid"],
65
65
  "data-test-value": node["data-test-value"],
66
- children: [filler, !node.leaf && !node.childFiltered && /*#__PURE__*/jsxRuntime.jsx("div", {
66
+ children: [filler, node.loading && /*#__PURE__*/jsxRuntime.jsx("div", {
67
+ className: "flex-initial w-6",
68
+ children: /*#__PURE__*/jsxRuntime.jsx(core.Icon, {
69
+ rtlFlip: true,
70
+ icon: core.CoreIcons.spinner,
71
+ className: "animate-spin text-muted"
72
+ })
73
+ }), !node.leaf && !node.childFiltered && !node.loading && /*#__PURE__*/jsxRuntime.jsx("div", {
67
74
  className: "flex-initial w-6",
68
75
  children: /*#__PURE__*/jsxRuntime.jsx(core.Icon, {
69
76
  rtlFlip: true,
@@ -84,7 +91,7 @@ var TreeNode = function TreeNode(_ref) {
84
91
  })
85
92
  }), /*#__PURE__*/jsxRuntime.jsxs("div", {
86
93
  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"),
94
+ 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
95
  "data-selected": node.selected,
89
96
  onClick: function onClick() {
90
97
  _onClick === null || _onClick === void 0 || _onClick(node.id, node.data);
@@ -31,9 +31,9 @@ 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
+ onSelect = _ref.onSelect,
37
37
  onClick = _ref.onClick,
38
38
  sorter = _ref.sorter,
39
39
  makeLabel = _ref.makeLabel,
@@ -49,6 +49,7 @@ var TreePanel = function TreePanel(_ref) {
49
49
  selected: selected,
50
50
  matcher: matcher,
51
51
  sorter: sorter,
52
+ onLoad: onLoad,
52
53
  onSelect: onSelect,
53
54
  onChecked: onChecked,
54
55
  defaultExpanded: defaultExpanded,
@@ -21,6 +21,7 @@ var useTree = function useTree(_ref) {
21
21
  selected = _ref.selected,
22
22
  sorter = _ref.sorter,
23
23
  matcher = _ref.matcher,
24
+ onLoad = _ref.onLoad,
24
25
  onSelect = _ref.onSelect,
25
26
  onChecked = _ref.onChecked,
26
27
  defaultExpanded = _ref.defaultExpanded,
@@ -29,6 +30,7 @@ var useTree = function useTree(_ref) {
29
30
  onSelect: onSelect,
30
31
  onChecked: onChecked
31
32
  });
33
+ var intialExpand = react.useRef(defaultExpanded !== null && defaultExpanded !== void 0 ? defaultExpanded : utilities.EMPTY_ARRAY);
32
34
  var itemList = createItemList(items);
33
35
  var fireExpandToggle = react.useCallback(function (itemMap) {
34
36
  var expandedList = itemMap.filter(function (node) {
@@ -38,6 +40,41 @@ var useTree = function useTree(_ref) {
38
40
  });
39
41
  onExpandToggle === null || onExpandToggle === void 0 || onExpandToggle(expandedList);
40
42
  }, [onExpandToggle]);
43
+ var loadChildren = react.useCallback(function (node) {
44
+ var _node$children;
45
+ if (node.open && !node.leaf && !((_node$children = node.children) !== null && _node$children !== void 0 && _node$children.length)) {
46
+ node.loading = true;
47
+ void Promise.resolve(onLoad === null || onLoad === void 0 ? void 0 : onLoad(node.id)).then(function () {
48
+ var resp = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : utilities.EMPTY_ARRAY;
49
+ node.children = utils.refactorTree(resp, {
50
+ sorter: sorter,
51
+ defaultExpanded: defaultExpanded
52
+ }, {
53
+ level: node.level + 1,
54
+ parent: node.id
55
+ });
56
+ })["finally"](function () {
57
+ var _intialExpand$current;
58
+ node.loading = false;
59
+ dispatch({
60
+ type: "loaded"
61
+ });
62
+ ((_intialExpand$current = intialExpand.current) === null || _intialExpand$current === void 0 ? void 0 : _intialExpand$current.length) > 0 && dispatch({
63
+ type: "expand",
64
+ id: intialExpand.current.shift()
65
+ });
66
+ });
67
+ } else {
68
+ var _intialExpand$current2;
69
+ dispatch({
70
+ type: "loaded"
71
+ });
72
+ ((_intialExpand$current2 = intialExpand.current) === null || _intialExpand$current2 === void 0 ? void 0 : _intialExpand$current2.length) > 0 && dispatch({
73
+ type: "expand",
74
+ id: intialExpand.current.shift()
75
+ });
76
+ }
77
+ }, [onLoad]);
41
78
  var _useReducer = react.useReducer(function (state, action) {
42
79
  if (action.type === "load") {
43
80
  state.items = utils.refactorTree(action.items, {
@@ -50,23 +87,37 @@ var useTree = function useTree(_ref) {
50
87
  }
51
88
  if (action.type === "toggleExpand") {
52
89
  var node = state.itemMap.get(action.id);
53
- node && (node.open = !node.open);
90
+ if (node && !node.leaf) {
91
+ node && (node.open = !node.open);
92
+ loadChildren(node);
93
+ return _objectSpread({}, state);
94
+ }
95
+ }
96
+ if (action.type === "loaded") {
97
+ state.itemMap = utils.makeTreeMap(state.items);
54
98
  fireExpandToggle(Array.from(state.itemMap.values()));
55
99
  state.tree = utils.flattenTree(state.items);
100
+ state.selected && utils.updateSelection(state.itemMap, state.selected, true);
56
101
  return _objectSpread({}, state);
57
102
  }
58
103
  if (action.type === "expand") {
59
- // TODO: check if children available, if not call onLoad to fetch node children
60
104
  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);
105
+ if (_node && !_node.leaf) {
106
+ _node.open = true;
107
+ var parentId = _node.parent;
108
+ while (parentId) {
109
+ var _node2 = state.itemMap.get(parentId);
110
+ _node2 && (_node2.open = true);
111
+ parentId = _node2 === null || _node2 === void 0 ? void 0 : _node2.parent;
112
+ }
113
+ loadChildren(_node);
114
+ return _objectSpread({}, state);
115
+ }
65
116
  }
66
117
  if (action.type === "expandAll") {
67
- // TODO: prevent opening dynamic nodes that dont have children
68
118
  state.itemMap.forEach(function (node) {
69
- !node.leaf && (node.open = true);
119
+ var _node$children2;
120
+ !node.leaf && ((_node$children2 = node.children) === null || _node$children2 === void 0 ? void 0 : _node$children2.length) && (node.open = true);
70
121
  });
71
122
  fireExpandToggle(Array.from(state.itemMap.values()));
72
123
  state.tree = utils.flattenTree(state.items);
@@ -134,10 +185,15 @@ var useTree = function useTree(_ref) {
134
185
  };
135
186
  }, [onSelect, onChecked]);
136
187
  react.useEffect(function () {
188
+ var _intialExpand$current3;
137
189
  dispatch({
138
190
  type: "load",
139
191
  items: itemList
140
192
  });
193
+ ((_intialExpand$current3 = intialExpand.current) === null || _intialExpand$current3 === void 0 ? void 0 : _intialExpand$current3.length) > 0 && dispatch({
194
+ type: "expand",
195
+ id: intialExpand.current.shift()
196
+ });
141
197
  }, [itemList]);
142
198
  react.useEffect(function () {
143
199
  selected && dispatch({
@@ -41,7 +41,8 @@ var refactorTree = function refactorTree(nodes, _ref) {
41
41
  if (sorter !== false) nodes = nodes.sort(sorter);
42
42
  nodes.forEach(function (node) {
43
43
  list.push(_objectSpread(_objectSpread({
44
- open: defaultExpanded.includes(node.id),
44
+ open: false,
45
+ // defaultExpanded.includes(node.id),
45
46
  level: options.level,
46
47
  parent: options.parent,
47
48
  checked: 0,
@@ -63,7 +63,14 @@ var TreeNode = function TreeNode(_ref) {
63
63
  className: classNames("flex flex-nowrap items-center", node.leaf ? leafClassName : nodeClassName),
64
64
  "data-testid": node["data-testid"],
65
65
  "data-test-value": node["data-test-value"],
66
- children: [filler, !node.leaf && !node.childFiltered && /*#__PURE__*/jsx("div", {
66
+ children: [filler, node.loading && /*#__PURE__*/jsx("div", {
67
+ className: "flex-initial w-6",
68
+ children: /*#__PURE__*/jsx(Icon, {
69
+ rtlFlip: true,
70
+ icon: CoreIcons.spinner,
71
+ className: "animate-spin text-muted"
72
+ })
73
+ }), !node.leaf && !node.childFiltered && !node.loading && /*#__PURE__*/jsx("div", {
67
74
  className: "flex-initial w-6",
68
75
  children: /*#__PURE__*/jsx(Icon, {
69
76
  rtlFlip: true,
@@ -84,7 +91,7 @@ var TreeNode = function TreeNode(_ref) {
84
91
  })
85
92
  }), /*#__PURE__*/jsxs("div", {
86
93
  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"),
94
+ 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
95
  "data-selected": node.selected,
89
96
  onClick: function onClick() {
90
97
  _onClick === null || _onClick === void 0 || _onClick(node.id, node.data);
@@ -31,9 +31,9 @@ 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
+ onSelect = _ref.onSelect,
37
37
  onClick = _ref.onClick,
38
38
  sorter = _ref.sorter,
39
39
  makeLabel = _ref.makeLabel,
@@ -49,6 +49,7 @@ var TreePanel = function TreePanel(_ref) {
49
49
  selected: selected,
50
50
  matcher: matcher,
51
51
  sorter: sorter,
52
+ onLoad: onLoad,
52
53
  onSelect: onSelect,
53
54
  onChecked: onChecked,
54
55
  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,7 @@ var useTree = function useTree(_ref) {
21
21
  selected = _ref.selected,
22
22
  sorter = _ref.sorter,
23
23
  matcher = _ref.matcher,
24
+ onLoad = _ref.onLoad,
24
25
  onSelect = _ref.onSelect,
25
26
  onChecked = _ref.onChecked,
26
27
  defaultExpanded = _ref.defaultExpanded,
@@ -29,6 +30,7 @@ var useTree = function useTree(_ref) {
29
30
  onSelect: onSelect,
30
31
  onChecked: onChecked
31
32
  });
33
+ var intialExpand = useRef(defaultExpanded !== null && defaultExpanded !== void 0 ? defaultExpanded : EMPTY_ARRAY);
32
34
  var itemList = createItemList(items);
33
35
  var fireExpandToggle = useCallback(function (itemMap) {
34
36
  var expandedList = itemMap.filter(function (node) {
@@ -38,6 +40,41 @@ var useTree = function useTree(_ref) {
38
40
  });
39
41
  onExpandToggle === null || onExpandToggle === void 0 || onExpandToggle(expandedList);
40
42
  }, [onExpandToggle]);
43
+ var loadChildren = useCallback(function (node) {
44
+ var _node$children;
45
+ if (node.open && !node.leaf && !((_node$children = node.children) !== null && _node$children !== void 0 && _node$children.length)) {
46
+ node.loading = true;
47
+ void Promise.resolve(onLoad === null || onLoad === void 0 ? void 0 : onLoad(node.id)).then(function () {
48
+ var resp = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : EMPTY_ARRAY;
49
+ node.children = refactorTree(resp, {
50
+ sorter: sorter,
51
+ defaultExpanded: defaultExpanded
52
+ }, {
53
+ level: node.level + 1,
54
+ parent: node.id
55
+ });
56
+ })["finally"](function () {
57
+ var _intialExpand$current;
58
+ node.loading = false;
59
+ dispatch({
60
+ type: "loaded"
61
+ });
62
+ ((_intialExpand$current = intialExpand.current) === null || _intialExpand$current === void 0 ? void 0 : _intialExpand$current.length) > 0 && dispatch({
63
+ type: "expand",
64
+ id: intialExpand.current.shift()
65
+ });
66
+ });
67
+ } else {
68
+ var _intialExpand$current2;
69
+ dispatch({
70
+ type: "loaded"
71
+ });
72
+ ((_intialExpand$current2 = intialExpand.current) === null || _intialExpand$current2 === void 0 ? void 0 : _intialExpand$current2.length) > 0 && dispatch({
73
+ type: "expand",
74
+ id: intialExpand.current.shift()
75
+ });
76
+ }
77
+ }, [onLoad]);
41
78
  var _useReducer = useReducer(function (state, action) {
42
79
  if (action.type === "load") {
43
80
  state.items = refactorTree(action.items, {
@@ -50,23 +87,37 @@ var useTree = function useTree(_ref) {
50
87
  }
51
88
  if (action.type === "toggleExpand") {
52
89
  var node = state.itemMap.get(action.id);
53
- node && (node.open = !node.open);
90
+ if (node && !node.leaf) {
91
+ node && (node.open = !node.open);
92
+ loadChildren(node);
93
+ return _objectSpread({}, state);
94
+ }
95
+ }
96
+ if (action.type === "loaded") {
97
+ state.itemMap = makeTreeMap(state.items);
54
98
  fireExpandToggle(Array.from(state.itemMap.values()));
55
99
  state.tree = flattenTree(state.items);
100
+ state.selected && updateSelection(state.itemMap, state.selected, true);
56
101
  return _objectSpread({}, state);
57
102
  }
58
103
  if (action.type === "expand") {
59
- // TODO: check if children available, if not call onLoad to fetch node children
60
104
  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);
105
+ if (_node && !_node.leaf) {
106
+ _node.open = true;
107
+ var parentId = _node.parent;
108
+ while (parentId) {
109
+ var _node2 = state.itemMap.get(parentId);
110
+ _node2 && (_node2.open = true);
111
+ parentId = _node2 === null || _node2 === void 0 ? void 0 : _node2.parent;
112
+ }
113
+ loadChildren(_node);
114
+ return _objectSpread({}, state);
115
+ }
65
116
  }
66
117
  if (action.type === "expandAll") {
67
- // TODO: prevent opening dynamic nodes that dont have children
68
118
  state.itemMap.forEach(function (node) {
69
- !node.leaf && (node.open = true);
119
+ var _node$children2;
120
+ !node.leaf && ((_node$children2 = node.children) === null || _node$children2 === void 0 ? void 0 : _node$children2.length) && (node.open = true);
70
121
  });
71
122
  fireExpandToggle(Array.from(state.itemMap.values()));
72
123
  state.tree = flattenTree(state.items);
@@ -134,10 +185,15 @@ var useTree = function useTree(_ref) {
134
185
  };
135
186
  }, [onSelect, onChecked]);
136
187
  useEffect(function () {
188
+ var _intialExpand$current3;
137
189
  dispatch({
138
190
  type: "load",
139
191
  items: itemList
140
192
  });
193
+ ((_intialExpand$current3 = intialExpand.current) === null || _intialExpand$current3 === void 0 ? void 0 : _intialExpand$current3.length) > 0 && dispatch({
194
+ type: "expand",
195
+ id: intialExpand.current.shift()
196
+ });
141
197
  }, [itemList]);
142
198
  useEffect(function () {
143
199
  selected && dispatch({
@@ -41,7 +41,8 @@ var refactorTree = function refactorTree(nodes, _ref) {
41
41
  if (sorter !== false) nodes = nodes.sort(sorter);
42
42
  nodes.forEach(function (node) {
43
43
  list.push(_objectSpread(_objectSpread({
44
- open: defaultExpanded.includes(node.id),
44
+ open: false,
45
+ // defaultExpanded.includes(node.id),
45
46
  level: options.level,
46
47
  parent: options.parent,
47
48
  checked: 0,
@@ -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, 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.27",
4
4
  "description": "RUF: Data package",
5
5
  "author": "Adarsh Pastakia <adarshpastakia@outlook.com>",
6
6
  "license": "MIT",
@@ -33,10 +33,10 @@
33
33
  "watch": "npm-run-all -p watch:rollup watch:dts"
34
34
  },
35
35
  "dependencies": {
36
- "@react-fabric/core": "^1.0.0-beta.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.27",
37
+ "@react-fabric/date": "^1.0.0-beta.27",
38
+ "@react-fabric/form": "^1.0.0-beta.27",
39
+ "@react-fabric/utilities": "^1.0.0-beta.27",
40
40
  "@tanstack/react-virtual": "^3.8.6",
41
41
  "classnames": "^2.5.1",
42
42
  "color": "^4.2.3",
@@ -52,5 +52,5 @@
52
52
  "node": ">=18.12.0",
53
53
  "npm": ">=8.19.0"
54
54
  },
55
- "gitHead": "452f821dc8b22c5765b3858cc53ad9d7e2f9bcf1"
55
+ "gitHead": "02ddac152718fe86b6003f35ea5e1ef1581168f2"
56
56
  }
@@ -103,7 +103,16 @@ export const TreeNode = ({
103
103
  data-test-value={node["data-test-value"]}
104
104
  >
105
105
  {filler}
106
- {!node.leaf && !node.childFiltered && (
106
+ {node.loading && (
107
+ <div className="flex-initial w-6">
108
+ <Icon
109
+ rtlFlip
110
+ icon={CoreIcons.spinner}
111
+ className="animate-spin text-muted"
112
+ />
113
+ </div>
114
+ )}
115
+ {!node.leaf && !node.childFiltered && !node.loading && (
107
116
  <div className="flex-initial w-6">
108
117
  <Icon
109
118
  rtlFlip
@@ -135,7 +144,7 @@ export const TreeNode = ({
135
144
  <div
136
145
  role="none"
137
146
  className={classNames(
138
- "group data-[selected]:bg-primary-100 flex flex-nowrap flex-1 overflow-hidden select-none",
147
+ "group/tool data-[selected]:bg-primary-100 flex flex-nowrap flex-1 overflow-hidden select-none",
139
148
  node.childSelected && "font-medium",
140
149
  (canSelect || onClick) && "hover:bg-primary-50 cursor-pointer",
141
150
  )}
@@ -81,6 +81,7 @@ export const TreePanel = <T extends KeyValue>({
81
81
  selected,
82
82
  matcher,
83
83
  sorter,
84
+ onLoad,
84
85
  onSelect,
85
86
  onChecked,
86
87
  defaultExpanded,
@@ -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,14 @@ export const useTree = <T extends KeyValue>({
93
97
  selected,
94
98
  sorter,
95
99
  matcher,
100
+ onLoad,
96
101
  onSelect,
97
102
  onChecked,
98
103
  defaultExpanded,
99
104
  onExpandToggle,
100
105
  }: Partial<TreePanelProps<T>>) => {
101
106
  const componentEvents = useRef({ onSelect, onChecked });
102
-
107
+ const intialExpand = useRef(defaultExpanded ?? EMPTY_ARRAY);
103
108
  const itemList = createItemList(items);
104
109
 
105
110
  const fireExpandToggle = useCallback(
@@ -112,6 +117,39 @@ export const useTree = <T extends KeyValue>({
112
117
  [onExpandToggle],
113
118
  );
114
119
 
120
+ const loadChildren = useCallback(
121
+ (node: InternalNode) => {
122
+ if (node.open && !node.leaf && !node.children?.length) {
123
+ node.loading = true;
124
+ void Promise.resolve(onLoad?.(node.id))
125
+ .then((resp = EMPTY_ARRAY) => {
126
+ node.children = refactorTree(
127
+ resp,
128
+ { sorter, defaultExpanded },
129
+ { level: node.level + 1, parent: node.id },
130
+ );
131
+ })
132
+ .finally(() => {
133
+ node.loading = false;
134
+ dispatch({ type: "loaded" });
135
+ intialExpand.current?.length > 0 &&
136
+ dispatch({
137
+ type: "expand",
138
+ id: intialExpand.current.shift() as unknown as string,
139
+ });
140
+ });
141
+ } else {
142
+ dispatch({ type: "loaded" });
143
+ intialExpand.current?.length > 0 &&
144
+ dispatch({
145
+ type: "expand",
146
+ id: intialExpand.current.shift() as unknown as string,
147
+ });
148
+ }
149
+ },
150
+ [onLoad],
151
+ );
152
+
115
153
  const [state, dispatch] = useReducer(
116
154
  (state: TreeState, action: TreeActions) => {
117
155
  if (action.type === "load") {
@@ -122,23 +160,36 @@ export const useTree = <T extends KeyValue>({
122
160
  }
123
161
  if (action.type === "toggleExpand") {
124
162
  const node = state.itemMap.get(action.id);
125
- node && (node.open = !node.open);
163
+ if (node && !node.leaf) {
164
+ node && (node.open = !node.open);
165
+ loadChildren(node);
166
+ return { ...state };
167
+ }
168
+ }
169
+ if (action.type === "loaded") {
170
+ state.itemMap = makeTreeMap(state.items);
126
171
  fireExpandToggle(Array.from(state.itemMap.values()));
127
172
  state.tree = flattenTree(state.items);
173
+ state.selected && updateSelection(state.itemMap, state.selected, true);
128
174
  return { ...state };
129
175
  }
130
176
  if (action.type === "expand") {
131
- // TODO: check if children available, if not call onLoad to fetch node children
132
177
  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 };
178
+ if (node && !node.leaf) {
179
+ node.open = true;
180
+ let parentId = node.parent;
181
+ while (parentId) {
182
+ const node = state.itemMap.get(parentId);
183
+ node && (node.open = true);
184
+ parentId = node?.parent;
185
+ }
186
+ loadChildren(node);
187
+ return { ...state };
188
+ }
137
189
  }
138
190
  if (action.type === "expandAll") {
139
- // TODO: prevent opening dynamic nodes that dont have children
140
191
  state.itemMap.forEach((node) => {
141
- !node.leaf && (node.open = true);
192
+ !node.leaf && node.children?.length && (node.open = true);
142
193
  });
143
194
  fireExpandToggle(Array.from(state.itemMap.values()));
144
195
  state.tree = flattenTree(state.items);
@@ -198,6 +249,11 @@ export const useTree = <T extends KeyValue>({
198
249
 
199
250
  useEffect(() => {
200
251
  dispatch({ type: "load", items: itemList });
252
+ intialExpand.current?.length > 0 &&
253
+ dispatch({
254
+ type: "expand",
255
+ id: intialExpand.current.shift() as unknown as string,
256
+ });
201
257
  }, [itemList]);
202
258
 
203
259
  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: false, // defaultExpanded.includes(node.id),
62
62
  level: options.level,
63
63
  parent: options.parent,
64
64
  checked: 0,