@react-fabric/data 1.0.0-beta.59 → 1.0.0-beta.60

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.
@@ -89,7 +89,7 @@ var Histogram = function Histogram(_ref) {
89
89
  return [_onClick === null || _onClick === void 0 ? void 0 : _onClick(item.id), e.stopPropagation()];
90
90
  },
91
91
  children: [/*#__PURE__*/jsxRuntime.jsx("div", {
92
- className: classNames("absolute z-0 inset-0 opacity-50 min-w-2 ltr:bg-gradient-to-r rtl:bg-gradient-to-l", "fabric-histoBar"),
92
+ className: classNames("absolute z-0 inset-0 opacity-50 min-w-2 ltr:bg-linear-to-r rtl:bg-linear-to-l", "fabric-histoBar"),
93
93
  style: {
94
94
  width: "".concat(Math.min(item.count / maxTotal, 1) * 100, "%")
95
95
  }
@@ -55,7 +55,7 @@ var BodyRow = function BodyRow(_ref) {
55
55
  children: [/*#__PURE__*/jsxRuntime.jsxs("div", {
56
56
  role: "none",
57
57
  "data-row": index,
58
- className: classNames("flex flex-nowrap datatable-row data-[hilight]:outline-1 -outline-offset-1 outline-primary-500 group/table-row", onRowClick && "hover:bg-primary-50 active:bg-primary-100", index % 2 ? "bg-even" : "bg-odd"),
58
+ className: classNames("flex flex-nowrap datatable-row data-hilight:outline-1 -outline-offset-1 outline-primary-500 group/table-row", onRowClick && "hover:bg-primary-50 active:bg-primary-100", index % 2 ? "bg-even" : "bg-odd"),
59
59
  onClick: function onClick() {
60
60
  return checkableRows && keyProperty && !onRowClick ? toggleChecked(data[keyProperty]) : onRowClick === null || onRowClick === void 0 ? void 0 : onRowClick(data);
61
61
  },
@@ -33,7 +33,7 @@ var TreeNode = function TreeNode(_ref) {
33
33
  var filler = react.useMemo(function () {
34
34
  return node.lines.slice(1).map(function (line, idx) {
35
35
  return /*#__PURE__*/jsxRuntime.jsx("div", {
36
- 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"),
36
+ 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-0.5 before:scale-x-[.625]", !noLines && line !== 2 && "before:h-full"),
37
37
  children: /*#__PURE__*/jsxRuntime.jsxs("svg", {
38
38
  viewBox: "0 0 24 24",
39
39
  className: "fill-tint-200 rtl:-scale-x-100",
@@ -96,11 +96,11 @@ var TreeNode = function TreeNode(_ref) {
96
96
  })
97
97
  }), !node.empty && /*#__PURE__*/jsxRuntime.jsxs("div", {
98
98
  role: "none",
99
- className: classNames("group/tool data-[selected]:bg-primary-100 flex flex-nowrap flex-1 overflow-hidden select-none", node.childSelected && "font-medium", node.disabled && "text-muted opacity-65", node.className, node.leaf ? leafClassName : nodeClassName, !node.disabled && (canSelect || onClick) && "hover:bg-primary-50 cursor-pointer"),
99
+ className: classNames("group/tool data-selected:bg-primary-100 flex flex-nowrap flex-1 overflow-hidden select-none", node.childSelected && "font-medium", node.disabled && "text-muted opacity-65", node.className, node.leaf ? leafClassName : nodeClassName, !node.disabled && (canSelect || onClick) && "hover:bg-primary-50 cursor-pointer"),
100
100
  "data-selected": node.selected,
101
- onClick: node.disabled ? undefined : function () {
101
+ onClick: node.disabled ? undefined : function (e) {
102
102
  onClick === null || onClick === void 0 || onClick(node.id, node.data);
103
- canSelect ? onSelect(node.id) : canCheck ? onChecked(node.id) : onToggle(node.id);
103
+ canSelect ? onSelect(node.id, e.shiftKey) : canCheck ? onChecked(node.id) : onToggle(node.id);
104
104
  },
105
105
  children: [nodeIcon && /*#__PURE__*/jsxRuntime.jsxs("div", {
106
106
  className: "flex-content max-w-6 me-1 self-center leading-none",
@@ -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", "searchable", "selectable", "matcher", "filterPlaceholder", "defaultLeafIcon", "defaultNodeIcon", "expander", "onChecked", "onLoad", "onQuery", "onSelect", "onClick", "sorter", "makeLabel", "makeIcon", "defaultExpanded", "onExpandToggle", "leafClassName", "nodeClassName"];
14
+ var _excluded = ["ref", "items", "selected", "checked", "checkable", "noLines", "searchable", "selectable", "multiple", "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) {
@@ -24,6 +24,7 @@ var TreePanel = function TreePanel(_ref) {
24
24
  noLines = _ref.noLines,
25
25
  searchable = _ref.searchable,
26
26
  selectable = _ref.selectable,
27
+ multiple = _ref.multiple,
27
28
  matcher = _ref.matcher,
28
29
  filterPlaceholder = _ref.filterPlaceholder,
29
30
  defaultLeafIcon = _ref.defaultLeafIcon,
@@ -47,6 +48,7 @@ var TreePanel = function TreePanel(_ref) {
47
48
  items: items,
48
49
  checked: checked,
49
50
  selected: selected,
51
+ multiple: multiple,
50
52
  matcher: matcher,
51
53
  sorter: sorter,
52
54
  onLoad: onLoad,
@@ -20,6 +20,7 @@ var useTree = function useTree(_ref) {
20
20
  _ref$checked = _ref.checked,
21
21
  checked = _ref$checked === void 0 ? utilities.EMPTY_ARRAY : _ref$checked,
22
22
  selected = _ref.selected,
23
+ multiple = _ref.multiple,
23
24
  sorter = _ref.sorter,
24
25
  matcher = _ref.matcher,
25
26
  onLoad = _ref.onLoad,
@@ -28,9 +29,11 @@ var useTree = function useTree(_ref) {
28
29
  onChecked = _ref.onChecked,
29
30
  defaultExpanded = _ref.defaultExpanded,
30
31
  onExpandToggle = _ref.onExpandToggle;
31
- var componentEvents = react.useRef({
32
- onSelect: onSelect,
33
- onChecked: onChecked
32
+ var eventSelect = react.useEffectEvent(onSelect !== null && onSelect !== void 0 ? onSelect : function () {
33
+ return undefined;
34
+ });
35
+ var eventChecked = react.useEffectEvent(onChecked !== null && onChecked !== void 0 ? onChecked : function () {
36
+ return undefined;
34
37
  });
35
38
  var intialExpand = react.useRef(_toConsumableArray(defaultExpanded !== null && defaultExpanded !== void 0 ? defaultExpanded : utilities.EMPTY_ARRAY));
36
39
  var itemList = createItemList(items);
@@ -83,13 +86,11 @@ var useTree = function useTree(_ref) {
83
86
  }
84
87
  }, [onLoad]);
85
88
  var fireSelected = core.useDebounce(function (nodeId, data) {
86
- var _componentEvents$curr, _componentEvents$curr2;
87
- (_componentEvents$curr = (_componentEvents$curr2 = componentEvents.current).onSelect) === null || _componentEvents$curr === void 0 || _componentEvents$curr.call(_componentEvents$curr2, nodeId, data);
88
- }, [onSelect]);
89
+ eventSelect(nodeId, data);
90
+ }, []);
89
91
  var fireChecked = core.useDebounce(function (leafs, nodes, partials) {
90
- var _componentEvents$curr3, _componentEvents$curr4;
91
- (_componentEvents$curr3 = (_componentEvents$curr4 = componentEvents.current).onChecked) === null || _componentEvents$curr3 === void 0 || _componentEvents$curr3.call(_componentEvents$curr4, leafs, nodes, partials);
92
- }, [onChecked]);
92
+ eventChecked(leafs, nodes, partials);
93
+ }, []);
93
94
  var fireQuery = core.useDebounce(function (query) {
94
95
  onQuery === null || onQuery === void 0 || onQuery(query);
95
96
  }, [onQuery]);
@@ -119,7 +120,12 @@ var useTree = function useTree(_ref) {
119
120
  state.itemMap = utils.makeTreeMap(state.items);
120
121
  fireExpandToggle(Array.from(state.itemMap.values()));
121
122
  state.tree = utils.flattenTree(state.items);
122
- state.selected = utils.updateSelection(state.itemMap, selected, true);
123
+ state.selected = selected;
124
+ utils.updateSelection({
125
+ nodes: state.itemMap,
126
+ multiple: multiple,
127
+ selected: selected
128
+ });
123
129
  (_state$checked = state.checked) === null || _state$checked === void 0 || _state$checked.forEach(function (id) {
124
130
  return utils.updateChecked(state.itemMap, id, 1);
125
131
  });
@@ -174,23 +180,32 @@ var useTree = function useTree(_ref) {
174
180
  });
175
181
  }
176
182
  if (action.type === "toggleSelect") {
177
- if (!onSelect) {
178
- if (state.selected) {
179
- utils.updateSelection(state.itemMap, state.selected);
180
- }
181
- state.selected = utils.updateSelection(state.itemMap, action.id, true);
182
- state.tree = utils.flattenTree(state.items);
183
+ var _state$itemMap$get;
184
+ if (multiple) {
185
+ var _state$selected, _state$selected$filte, _state$selected2, _state$selected3;
186
+ var isSelected = (_state$selected = state.selected) === null || _state$selected === void 0 ? void 0 : _state$selected.includes(action.id);
187
+ if (action.shiftKey && isSelected) state.selected = (_state$selected$filte = (_state$selected2 = state.selected) === null || _state$selected2 === void 0 ? void 0 : _state$selected2.filter(function (id) {
188
+ return id !== action.id;
189
+ })) !== null && _state$selected$filte !== void 0 ? _state$selected$filte : [];else if (action.shiftKey && !isSelected) state.selected = [].concat(_toConsumableArray((_state$selected3 = state.selected) !== null && _state$selected3 !== void 0 ? _state$selected3 : []), [action.id]);else state.selected = [action.id];
183
190
  } else {
184
- var _state$itemMap$get;
185
- fireSelected(action.id, (_state$itemMap$get = state.itemMap.get(action.id)) === null || _state$itemMap$get === void 0 ? void 0 : _state$itemMap$get.data);
191
+ state.selected = action.id;
186
192
  }
193
+ utils.updateSelection({
194
+ nodes: state.itemMap,
195
+ selected: state.selected,
196
+ multiple: multiple
197
+ });
198
+ state.tree = utils.flattenTree(state.items);
199
+ fireSelected(state.selected, (_state$itemMap$get = state.itemMap.get(action.id)) === null || _state$itemMap$get === void 0 ? void 0 : _state$itemMap$get.data);
187
200
  return _objectSpread({}, state);
188
201
  }
189
202
  if (action.type === "select") {
190
- if (state.selected) {
191
- utils.updateSelection(state.itemMap, state.selected);
192
- }
193
- state.selected = utils.updateSelection(state.itemMap, action.id, true);
203
+ state.selected = action.id;
204
+ utils.updateSelection({
205
+ nodes: state.itemMap,
206
+ multiple: multiple,
207
+ selected: action.id
208
+ });
194
209
  state.tree = utils.flattenTree(state.items);
195
210
  return _objectSpread({}, state);
196
211
  }
@@ -234,12 +249,6 @@ var useTree = function useTree(_ref) {
234
249
  _useReducer2 = _slicedToArray(_useReducer, 2),
235
250
  state = _useReducer2[0],
236
251
  dispatch = _useReducer2[1];
237
- react.useEffect(function () {
238
- componentEvents.current = {
239
- onSelect: onSelect,
240
- onChecked: onChecked
241
- };
242
- }, [onSelect, onChecked]);
243
252
  react.useEffect(function () {
244
253
  var _intialExpand$current3;
245
254
  dispatch({
@@ -284,9 +293,11 @@ var useTree = function useTree(_ref) {
284
293
  });
285
294
  }, []);
286
295
  var select = react.useCallback(function (id) {
296
+ var shiftKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
287
297
  dispatch({
288
298
  type: "toggleSelect",
289
- id: id
299
+ id: id,
300
+ shiftKey: shiftKey
290
301
  });
291
302
  }, []);
292
303
  var expand = react.useCallback(function (id) {
@@ -95,26 +95,37 @@ var flattenTree = function flattenTree(nodes) {
95
95
  });
96
96
  return list;
97
97
  };
98
- var updateSelection = function updateSelection(nodes, id, selected) {
98
+ var selectNode = function selectNode(nodes, id) {
99
99
  var node = nodes.get(id !== null && id !== void 0 ? id : "");
100
100
  if (node) {
101
101
  // change selection
102
- node.selected = selected;
102
+ node.selected = true;
103
103
  // update all parent childSelected flag
104
104
  var parent = node.parent;
105
105
  while (parent) {
106
106
  var parentNode = nodes.get(parent);
107
- parentNode && (parentNode.childSelected = selected);
107
+ parentNode && (parentNode.childSelected = true);
108
108
  // selected && parentNode && (parentNode.open = true);
109
109
  parent = parentNode === null || parentNode === void 0 ? void 0 : parentNode.parent;
110
110
  }
111
- return id;
112
111
  }
113
112
  };
113
+ var updateSelection = function updateSelection(_ref2) {
114
+ var nodes = _ref2.nodes,
115
+ multiple = _ref2.multiple,
116
+ selected = _ref2.selected;
117
+ nodes.forEach(function (node) {
118
+ node.selected = undefined;
119
+ node.childSelected = undefined;
120
+ });
121
+ multiple && utilities.isArray(selected) ? selected === null || selected === void 0 ? void 0 : selected.forEach(function (id) {
122
+ return selectNode(nodes, id);
123
+ }) : selectNode(nodes, selected);
124
+ };
114
125
  var updateChecked = function updateChecked(list, id, checked) {
115
126
  var node = list.get(id);
116
127
  if (node) {
117
- node.checked = checked === undefined ? node.checked === 0 ? 1 : 0 : checked;
128
+ node.checked = (checked !== null && checked !== void 0 ? checked : node.checked === 0) ? 1 : 0;
118
129
  // check all children
119
130
  node.children && getChildren(node.children).forEach(function (child) {
120
131
  return child.checked = node.checked;
@@ -208,7 +208,7 @@ var _VirtualGallery = function _VirtualGallery(_ref) {
208
208
  "data-index": index,
209
209
  ref: virtualizer.measureElement,
210
210
  children: columnMap.map(function (col) {
211
- var _itemList;
211
+ var _itemList, _itemList2;
212
212
  return /*#__PURE__*/jsxRuntime.jsx("div", {
213
213
  "data-index": index * columnCount + col,
214
214
  className: classNames("grid rounded-capped"),
@@ -216,7 +216,7 @@ var _VirtualGallery = function _VirtualGallery(_ref) {
216
216
  minWidth: width,
217
217
  minHeight: height
218
218
  },
219
- children: (_itemList = itemList[index * columnCount + col]) !== null && _itemList !== void 0 && _itemList.item ? children(itemList[index * columnCount + col]) : /*#__PURE__*/jsxRuntime.jsx(core.Skeleton, {
219
+ children: (_itemList = itemList[index * columnCount + col]) !== null && _itemList !== void 0 && _itemList.item ? children(itemList[index * columnCount + col]) : ((_itemList2 = itemList[index * columnCount + col]) === null || _itemList2 === void 0 ? void 0 : _itemList2.item) === null && /*#__PURE__*/jsxRuntime.jsx(core.Skeleton, {
220
220
  className: "p-2"
221
221
  })
222
222
  }, "".concat(key, ":").concat(col));
@@ -193,7 +193,7 @@ var _VirtualList = function _VirtualList(_ref) {
193
193
  display: "inline-block"
194
194
  }
195
195
  }), virtualItems.map(function (_ref3) {
196
- var _itemList$index;
196
+ var _itemList$index, _itemList$index2;
197
197
  var key = _ref3.key,
198
198
  index = _ref3.index;
199
199
  return /*#__PURE__*/jsxRuntime.jsx("div", {
@@ -204,7 +204,7 @@ var _VirtualList = function _VirtualList(_ref) {
204
204
  minWidth: width,
205
205
  minHeight: height
206
206
  },
207
- children: (_itemList$index = itemList[index]) !== null && _itemList$index !== void 0 && _itemList$index.item ? children(itemList[index]) : /*#__PURE__*/jsxRuntime.jsx(core.Skeleton, {
207
+ children: (_itemList$index = itemList[index]) !== null && _itemList$index !== void 0 && _itemList$index.item ? children(itemList[index]) : ((_itemList$index2 = itemList[index]) === null || _itemList$index2 === void 0 ? void 0 : _itemList$index2.item) === null && /*#__PURE__*/jsxRuntime.jsx(core.Skeleton, {
208
208
  className: "p-2"
209
209
  })
210
210
  }, key);
@@ -89,7 +89,7 @@ var Histogram = function Histogram(_ref) {
89
89
  return [_onClick === null || _onClick === void 0 ? void 0 : _onClick(item.id), e.stopPropagation()];
90
90
  },
91
91
  children: [/*#__PURE__*/jsx("div", {
92
- className: classNames("absolute z-0 inset-0 opacity-50 min-w-2 ltr:bg-gradient-to-r rtl:bg-gradient-to-l", "fabric-histoBar"),
92
+ className: classNames("absolute z-0 inset-0 opacity-50 min-w-2 ltr:bg-linear-to-r rtl:bg-linear-to-l", "fabric-histoBar"),
93
93
  style: {
94
94
  width: "".concat(Math.min(item.count / maxTotal, 1) * 100, "%")
95
95
  }
@@ -55,7 +55,7 @@ var BodyRow = function BodyRow(_ref) {
55
55
  children: [/*#__PURE__*/jsxs("div", {
56
56
  role: "none",
57
57
  "data-row": index,
58
- className: classNames("flex flex-nowrap datatable-row data-[hilight]:outline-1 -outline-offset-1 outline-primary-500 group/table-row", onRowClick && "hover:bg-primary-50 active:bg-primary-100", index % 2 ? "bg-even" : "bg-odd"),
58
+ className: classNames("flex flex-nowrap datatable-row data-hilight:outline-1 -outline-offset-1 outline-primary-500 group/table-row", onRowClick && "hover:bg-primary-50 active:bg-primary-100", index % 2 ? "bg-even" : "bg-odd"),
59
59
  onClick: function onClick() {
60
60
  return checkableRows && keyProperty && !onRowClick ? toggleChecked(data[keyProperty]) : onRowClick === null || onRowClick === void 0 ? void 0 : onRowClick(data);
61
61
  },
@@ -33,7 +33,7 @@ var TreeNode = function TreeNode(_ref) {
33
33
  var filler = useMemo(function () {
34
34
  return node.lines.slice(1).map(function (line, idx) {
35
35
  return /*#__PURE__*/jsx("div", {
36
- 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"),
36
+ 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-0.5 before:scale-x-[.625]", !noLines && line !== 2 && "before:h-full"),
37
37
  children: /*#__PURE__*/jsxs("svg", {
38
38
  viewBox: "0 0 24 24",
39
39
  className: "fill-tint-200 rtl:-scale-x-100",
@@ -96,11 +96,11 @@ var TreeNode = function TreeNode(_ref) {
96
96
  })
97
97
  }), !node.empty && /*#__PURE__*/jsxs("div", {
98
98
  role: "none",
99
- className: classNames("group/tool data-[selected]:bg-primary-100 flex flex-nowrap flex-1 overflow-hidden select-none", node.childSelected && "font-medium", node.disabled && "text-muted opacity-65", node.className, node.leaf ? leafClassName : nodeClassName, !node.disabled && (canSelect || onClick) && "hover:bg-primary-50 cursor-pointer"),
99
+ className: classNames("group/tool data-selected:bg-primary-100 flex flex-nowrap flex-1 overflow-hidden select-none", node.childSelected && "font-medium", node.disabled && "text-muted opacity-65", node.className, node.leaf ? leafClassName : nodeClassName, !node.disabled && (canSelect || onClick) && "hover:bg-primary-50 cursor-pointer"),
100
100
  "data-selected": node.selected,
101
- onClick: node.disabled ? undefined : function () {
101
+ onClick: node.disabled ? undefined : function (e) {
102
102
  onClick === null || onClick === void 0 || onClick(node.id, node.data);
103
- canSelect ? onSelect(node.id) : canCheck ? onChecked(node.id) : onToggle(node.id);
103
+ canSelect ? onSelect(node.id, e.shiftKey) : canCheck ? onChecked(node.id) : onToggle(node.id);
104
104
  },
105
105
  children: [nodeIcon && /*#__PURE__*/jsxs("div", {
106
106
  className: "flex-content max-w-6 me-1 self-center leading-none",
@@ -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", "searchable", "selectable", "matcher", "filterPlaceholder", "defaultLeafIcon", "defaultNodeIcon", "expander", "onChecked", "onLoad", "onQuery", "onSelect", "onClick", "sorter", "makeLabel", "makeIcon", "defaultExpanded", "onExpandToggle", "leafClassName", "nodeClassName"];
14
+ var _excluded = ["ref", "items", "selected", "checked", "checkable", "noLines", "searchable", "selectable", "multiple", "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) {
@@ -24,6 +24,7 @@ var TreePanel = function TreePanel(_ref) {
24
24
  noLines = _ref.noLines,
25
25
  searchable = _ref.searchable,
26
26
  selectable = _ref.selectable,
27
+ multiple = _ref.multiple,
27
28
  matcher = _ref.matcher,
28
29
  filterPlaceholder = _ref.filterPlaceholder,
29
30
  defaultLeafIcon = _ref.defaultLeafIcon,
@@ -47,6 +48,7 @@ var TreePanel = function TreePanel(_ref) {
47
48
  items: items,
48
49
  checked: checked,
49
50
  selected: selected,
51
+ multiple: multiple,
50
52
  matcher: matcher,
51
53
  sorter: sorter,
52
54
  onLoad: onLoad,
@@ -6,7 +6,7 @@ import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
6
6
  import { useDebounce } from '@react-fabric/core';
7
7
  import { EMPTY_ARRAY, isEmpty } from '@react-fabric/utilities';
8
8
  import memoizeOne from 'memoize-one';
9
- import { useRef, useCallback, useReducer, useEffect } from 'react';
9
+ import { useEffectEvent, useRef, useCallback, useReducer, useEffect } from 'react';
10
10
  import { refactorTree, makeTreeMap, flattenTree, updateSelection, updateChecked, filterTree } from './utils.js';
11
11
 
12
12
  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; }
@@ -20,6 +20,7 @@ var useTree = function useTree(_ref) {
20
20
  _ref$checked = _ref.checked,
21
21
  checked = _ref$checked === void 0 ? EMPTY_ARRAY : _ref$checked,
22
22
  selected = _ref.selected,
23
+ multiple = _ref.multiple,
23
24
  sorter = _ref.sorter,
24
25
  matcher = _ref.matcher,
25
26
  onLoad = _ref.onLoad,
@@ -28,9 +29,11 @@ var useTree = function useTree(_ref) {
28
29
  onChecked = _ref.onChecked,
29
30
  defaultExpanded = _ref.defaultExpanded,
30
31
  onExpandToggle = _ref.onExpandToggle;
31
- var componentEvents = useRef({
32
- onSelect: onSelect,
33
- onChecked: onChecked
32
+ var eventSelect = useEffectEvent(onSelect !== null && onSelect !== void 0 ? onSelect : function () {
33
+ return undefined;
34
+ });
35
+ var eventChecked = useEffectEvent(onChecked !== null && onChecked !== void 0 ? onChecked : function () {
36
+ return undefined;
34
37
  });
35
38
  var intialExpand = useRef(_toConsumableArray(defaultExpanded !== null && defaultExpanded !== void 0 ? defaultExpanded : EMPTY_ARRAY));
36
39
  var itemList = createItemList(items);
@@ -83,13 +86,11 @@ var useTree = function useTree(_ref) {
83
86
  }
84
87
  }, [onLoad]);
85
88
  var fireSelected = useDebounce(function (nodeId, data) {
86
- var _componentEvents$curr, _componentEvents$curr2;
87
- (_componentEvents$curr = (_componentEvents$curr2 = componentEvents.current).onSelect) === null || _componentEvents$curr === void 0 || _componentEvents$curr.call(_componentEvents$curr2, nodeId, data);
88
- }, [onSelect]);
89
+ eventSelect(nodeId, data);
90
+ }, []);
89
91
  var fireChecked = useDebounce(function (leafs, nodes, partials) {
90
- var _componentEvents$curr3, _componentEvents$curr4;
91
- (_componentEvents$curr3 = (_componentEvents$curr4 = componentEvents.current).onChecked) === null || _componentEvents$curr3 === void 0 || _componentEvents$curr3.call(_componentEvents$curr4, leafs, nodes, partials);
92
- }, [onChecked]);
92
+ eventChecked(leafs, nodes, partials);
93
+ }, []);
93
94
  var fireQuery = useDebounce(function (query) {
94
95
  onQuery === null || onQuery === void 0 || onQuery(query);
95
96
  }, [onQuery]);
@@ -119,7 +120,12 @@ var useTree = function useTree(_ref) {
119
120
  state.itemMap = makeTreeMap(state.items);
120
121
  fireExpandToggle(Array.from(state.itemMap.values()));
121
122
  state.tree = flattenTree(state.items);
122
- state.selected = updateSelection(state.itemMap, selected, true);
123
+ state.selected = selected;
124
+ updateSelection({
125
+ nodes: state.itemMap,
126
+ multiple: multiple,
127
+ selected: selected
128
+ });
123
129
  (_state$checked = state.checked) === null || _state$checked === void 0 || _state$checked.forEach(function (id) {
124
130
  return updateChecked(state.itemMap, id, 1);
125
131
  });
@@ -174,23 +180,32 @@ var useTree = function useTree(_ref) {
174
180
  });
175
181
  }
176
182
  if (action.type === "toggleSelect") {
177
- if (!onSelect) {
178
- if (state.selected) {
179
- updateSelection(state.itemMap, state.selected);
180
- }
181
- state.selected = updateSelection(state.itemMap, action.id, true);
182
- state.tree = flattenTree(state.items);
183
+ var _state$itemMap$get;
184
+ if (multiple) {
185
+ var _state$selected, _state$selected$filte, _state$selected2, _state$selected3;
186
+ var isSelected = (_state$selected = state.selected) === null || _state$selected === void 0 ? void 0 : _state$selected.includes(action.id);
187
+ if (action.shiftKey && isSelected) state.selected = (_state$selected$filte = (_state$selected2 = state.selected) === null || _state$selected2 === void 0 ? void 0 : _state$selected2.filter(function (id) {
188
+ return id !== action.id;
189
+ })) !== null && _state$selected$filte !== void 0 ? _state$selected$filte : [];else if (action.shiftKey && !isSelected) state.selected = [].concat(_toConsumableArray((_state$selected3 = state.selected) !== null && _state$selected3 !== void 0 ? _state$selected3 : []), [action.id]);else state.selected = [action.id];
183
190
  } else {
184
- var _state$itemMap$get;
185
- fireSelected(action.id, (_state$itemMap$get = state.itemMap.get(action.id)) === null || _state$itemMap$get === void 0 ? void 0 : _state$itemMap$get.data);
191
+ state.selected = action.id;
186
192
  }
193
+ updateSelection({
194
+ nodes: state.itemMap,
195
+ selected: state.selected,
196
+ multiple: multiple
197
+ });
198
+ state.tree = flattenTree(state.items);
199
+ fireSelected(state.selected, (_state$itemMap$get = state.itemMap.get(action.id)) === null || _state$itemMap$get === void 0 ? void 0 : _state$itemMap$get.data);
187
200
  return _objectSpread({}, state);
188
201
  }
189
202
  if (action.type === "select") {
190
- if (state.selected) {
191
- updateSelection(state.itemMap, state.selected);
192
- }
193
- state.selected = updateSelection(state.itemMap, action.id, true);
203
+ state.selected = action.id;
204
+ updateSelection({
205
+ nodes: state.itemMap,
206
+ multiple: multiple,
207
+ selected: action.id
208
+ });
194
209
  state.tree = flattenTree(state.items);
195
210
  return _objectSpread({}, state);
196
211
  }
@@ -234,12 +249,6 @@ var useTree = function useTree(_ref) {
234
249
  _useReducer2 = _slicedToArray(_useReducer, 2),
235
250
  state = _useReducer2[0],
236
251
  dispatch = _useReducer2[1];
237
- useEffect(function () {
238
- componentEvents.current = {
239
- onSelect: onSelect,
240
- onChecked: onChecked
241
- };
242
- }, [onSelect, onChecked]);
243
252
  useEffect(function () {
244
253
  var _intialExpand$current3;
245
254
  dispatch({
@@ -284,9 +293,11 @@ var useTree = function useTree(_ref) {
284
293
  });
285
294
  }, []);
286
295
  var select = useCallback(function (id) {
296
+ var shiftKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
287
297
  dispatch({
288
298
  type: "toggleSelect",
289
- id: id
299
+ id: id,
300
+ shiftKey: shiftKey
290
301
  });
291
302
  }, []);
292
303
  var expand = useCallback(function (id) {
@@ -2,7 +2,7 @@
2
2
 
3
3
  import _defineProperty from '@babel/runtime/helpers/defineProperty';
4
4
  import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
5
- import { isString, matchString, compareValues } from '@react-fabric/utilities';
5
+ import { isArray, 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; }
@@ -95,26 +95,37 @@ var flattenTree = function flattenTree(nodes) {
95
95
  });
96
96
  return list;
97
97
  };
98
- var updateSelection = function updateSelection(nodes, id, selected) {
98
+ var selectNode = function selectNode(nodes, id) {
99
99
  var node = nodes.get(id !== null && id !== void 0 ? id : "");
100
100
  if (node) {
101
101
  // change selection
102
- node.selected = selected;
102
+ node.selected = true;
103
103
  // update all parent childSelected flag
104
104
  var parent = node.parent;
105
105
  while (parent) {
106
106
  var parentNode = nodes.get(parent);
107
- parentNode && (parentNode.childSelected = selected);
107
+ parentNode && (parentNode.childSelected = true);
108
108
  // selected && parentNode && (parentNode.open = true);
109
109
  parent = parentNode === null || parentNode === void 0 ? void 0 : parentNode.parent;
110
110
  }
111
- return id;
112
111
  }
113
112
  };
113
+ var updateSelection = function updateSelection(_ref2) {
114
+ var nodes = _ref2.nodes,
115
+ multiple = _ref2.multiple,
116
+ selected = _ref2.selected;
117
+ nodes.forEach(function (node) {
118
+ node.selected = undefined;
119
+ node.childSelected = undefined;
120
+ });
121
+ multiple && isArray(selected) ? selected === null || selected === void 0 ? void 0 : selected.forEach(function (id) {
122
+ return selectNode(nodes, id);
123
+ }) : selectNode(nodes, selected);
124
+ };
114
125
  var updateChecked = function updateChecked(list, id, checked) {
115
126
  var node = list.get(id);
116
127
  if (node) {
117
- node.checked = checked === undefined ? node.checked === 0 ? 1 : 0 : checked;
128
+ node.checked = (checked !== null && checked !== void 0 ? checked : node.checked === 0) ? 1 : 0;
118
129
  // check all children
119
130
  node.children && getChildren(node.children).forEach(function (child) {
120
131
  return child.checked = node.checked;
@@ -208,7 +208,7 @@ var _VirtualGallery = function _VirtualGallery(_ref) {
208
208
  "data-index": index,
209
209
  ref: virtualizer.measureElement,
210
210
  children: columnMap.map(function (col) {
211
- var _itemList;
211
+ var _itemList, _itemList2;
212
212
  return /*#__PURE__*/jsx("div", {
213
213
  "data-index": index * columnCount + col,
214
214
  className: classNames("grid rounded-capped"),
@@ -216,7 +216,7 @@ var _VirtualGallery = function _VirtualGallery(_ref) {
216
216
  minWidth: width,
217
217
  minHeight: height
218
218
  },
219
- children: (_itemList = itemList[index * columnCount + col]) !== null && _itemList !== void 0 && _itemList.item ? children(itemList[index * columnCount + col]) : /*#__PURE__*/jsx(Skeleton, {
219
+ children: (_itemList = itemList[index * columnCount + col]) !== null && _itemList !== void 0 && _itemList.item ? children(itemList[index * columnCount + col]) : ((_itemList2 = itemList[index * columnCount + col]) === null || _itemList2 === void 0 ? void 0 : _itemList2.item) === null && /*#__PURE__*/jsx(Skeleton, {
220
220
  className: "p-2"
221
221
  })
222
222
  }, "".concat(key, ":").concat(col));
@@ -193,7 +193,7 @@ var _VirtualList = function _VirtualList(_ref) {
193
193
  display: "inline-block"
194
194
  }
195
195
  }), virtualItems.map(function (_ref3) {
196
- var _itemList$index;
196
+ var _itemList$index, _itemList$index2;
197
197
  var key = _ref3.key,
198
198
  index = _ref3.index;
199
199
  return /*#__PURE__*/jsx("div", {
@@ -204,7 +204,7 @@ var _VirtualList = function _VirtualList(_ref) {
204
204
  minWidth: width,
205
205
  minHeight: height
206
206
  },
207
- children: (_itemList$index = itemList[index]) !== null && _itemList$index !== void 0 && _itemList$index.item ? children(itemList[index]) : /*#__PURE__*/jsx(Skeleton, {
207
+ children: (_itemList$index = itemList[index]) !== null && _itemList$index !== void 0 && _itemList$index.item ? children(itemList[index]) : ((_itemList$index2 = itemList[index]) === null || _itemList$index2 === void 0 ? void 0 : _itemList$index2.item) === null && /*#__PURE__*/jsx(Skeleton, {
208
208
  className: "p-2"
209
209
  })
210
210
  }, key);
@@ -1,4 +1,4 @@
1
- import { IconProps } from "@react-fabric/core/dist/types/components/icon/Icon";
1
+ import { type IconProps } from "@react-fabric/core/dist/types/components/icon/Icon";
2
2
  import { type CssProp, type Elements, type RefProp, type TestProps } from "@react-fabric/core/dist/types/types";
3
3
  import { type ReactElement } from "react";
4
4
  export declare const COL_MIN_WIDTH = "3rem";
@@ -40,4 +40,4 @@ import { type TreePanelProps } from "./types";
40
40
  *
41
41
  * @see {@link https://adarshpastakia.github.io/react-fabric/?path=/docs/data-treepanel--docs}
42
42
  */
43
- export declare const TreePanel: <T extends KeyValue>({ ref, items, selected, checked, checkable, noLines, searchable, selectable, matcher, filterPlaceholder, defaultLeafIcon, defaultNodeIcon, expander, onChecked, onLoad, onQuery, onSelect, onClick, sorter, makeLabel, makeIcon, defaultExpanded, onExpandToggle, leafClassName, nodeClassName, ...aria }: TreePanelProps<T>) => import("react/jsx-runtime").JSX.Element;
43
+ export declare const TreePanel: <T extends KeyValue>({ ref, items, selected, checked, checkable, noLines, searchable, selectable, multiple, matcher, filterPlaceholder, defaultLeafIcon, defaultNodeIcon, expander, onChecked, onLoad, onQuery, onSelect, onClick, sorter, makeLabel, makeIcon, defaultExpanded, onExpandToggle, leafClassName, nodeClassName, ...aria }: TreePanelProps<T>) => import("react/jsx-runtime").JSX.Element;
@@ -1,4 +1,4 @@
1
- import { IconProps } from "@react-fabric/core/dist/types/components/icon/Icon";
1
+ import { type IconProps } from "@react-fabric/core/dist/types/components/icon/Icon";
2
2
  import { type BadgeType, type ChildProp, type CssProp, type Elements, type RefProp, type TestProps } from "@react-fabric/core/dist/types/types";
3
3
  interface TreeBaseNode<T> extends CssProp, TestProps {
4
4
  id: string;
@@ -74,7 +74,7 @@ export interface TreeNodeProps extends ChildProp {
74
74
  leafClassName?: string;
75
75
  nodeClassName?: string;
76
76
  onToggle: (id: string) => void;
77
- onSelect: (id: string) => void;
77
+ onSelect: (id: string, shiftKey?: boolean) => void;
78
78
  onChecked: (id: string) => void;
79
79
  onClick?: (id: string, node: any) => void;
80
80
  }
@@ -83,7 +83,34 @@ export interface TreeRef {
83
83
  open: (id: string) => void;
84
84
  openAndLoad: (list: string[]) => void;
85
85
  }
86
- export interface TreePanelProps<T extends KeyValue = KeyValue> extends TestProps, RefProp<TreeRef> {
86
+ export type TreePanelProps<T extends KeyValue = KeyValue> = BaseTreePanelProps<T> & ({
87
+ /**
88
+ * enable multi select
89
+ */
90
+ multiple?: never | false;
91
+ /**
92
+ * selected item id
93
+ */
94
+ selected?: string;
95
+ /**
96
+ * callback on selection of tree node
97
+ */
98
+ onSelect?: (id: string, data: T) => void;
99
+ } | {
100
+ /**
101
+ * enable multi select
102
+ */
103
+ multiple: true;
104
+ /**
105
+ * selected item id
106
+ */
107
+ selected?: string[];
108
+ /**
109
+ * callback on selection of tree node
110
+ */
111
+ onSelect?: (id: string[], data: T) => void;
112
+ });
113
+ interface BaseTreePanelProps<T extends KeyValue = KeyValue> extends TestProps, RefProp<TreeRef> {
87
114
  items?: Array<TreeNodeType<T>>;
88
115
  /**
89
116
  * make tree searchable
@@ -98,13 +125,13 @@ export interface TreePanelProps<T extends KeyValue = KeyValue> extends TestProps
98
125
  */
99
126
  selectable?: true | "leafOnly";
100
127
  /**
101
- * enable checkable
128
+ * enable multi select
102
129
  */
103
- checkable?: true | "leafOnly";
130
+ multiple?: boolean;
104
131
  /**
105
- * selected item id
132
+ * enable checkable
106
133
  */
107
- selected?: string;
134
+ checkable?: true | "leafOnly";
108
135
  /**
109
136
  * checked items
110
137
  */
@@ -137,10 +164,6 @@ export interface TreePanelProps<T extends KeyValue = KeyValue> extends TestProps
137
164
  * callback on click of tree node
138
165
  */
139
166
  onClick?: (id: string, data: T) => void;
140
- /**
141
- * callback on selection of tree node
142
- */
143
- onSelect?: (id: string, data: T) => boolean | undefined;
144
167
  /**
145
168
  * callback on change of checked list
146
169
  * @param leafs - list of checked leaf nodes
@@ -1,14 +1,17 @@
1
1
  import { type InternalNode, type TreePanelProps } from "./types";
2
- export declare const useTree: <T extends KeyValue>({ items, checked, selected, sorter, matcher, onLoad, onQuery, onSelect, onChecked, defaultExpanded, onExpandToggle, }: Partial<TreePanelProps<T>>) => {
2
+ export declare const useTree: <T extends KeyValue>({ items, checked, selected, multiple, sorter, matcher, onLoad, onQuery, onSelect, onChecked, defaultExpanded, onExpandToggle, }: Partial<TreePanelProps<T>> & {
3
+ selected?: AnyObject;
4
+ multiple?: boolean;
5
+ }) => {
3
6
  tree: InternalNode[];
4
7
  query: string | undefined;
5
- selected: string | undefined;
8
+ selected: any;
6
9
  toggleExpand: (id: string) => void;
7
10
  expandAll: () => void;
8
11
  collapseAll: () => void;
9
12
  toggleCheck: (id: string) => void;
10
13
  expandAndLoad: (expandList: string[]) => void;
11
- select: (id: string) => void;
14
+ select: (id: string, shiftKey?: boolean) => void;
12
15
  expand: (id: string) => void;
13
16
  onFilter: {
14
17
  (...args: AnyObject): void;
@@ -8,6 +8,10 @@ export declare const refactorTree: (nodes: TreeNodeType[], { sorter, defaultExpa
8
8
  }) => InternalNode[];
9
9
  export declare const makeTreeMap: (nodes: InternalNode[], map?: Map<any, any>) => Map<any, any>;
10
10
  export declare const flattenTree: (nodes: InternalNode[], lines?: Array<0 | 1 | 2 | 3>) => InternalNode[];
11
- export declare const updateSelection: (nodes: Map<string, InternalNode>, id?: string, selected?: true) => string | undefined;
11
+ export declare const updateSelection: ({ nodes, multiple, selected, }: {
12
+ nodes: Map<string, InternalNode>;
13
+ multiple?: boolean;
14
+ selected?: string | string[];
15
+ }) => void;
12
16
  export declare const updateChecked: (list: Map<string, AnyObject>, id: string, checked?: 0 | 1 | 2) => void;
13
17
  export declare const filterTree: (nodes: Map<string, InternalNode>, query?: string, matcher?: AnyObject) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-fabric/data",
3
- "version": "1.0.0-beta.59",
3
+ "version": "1.0.0-beta.60",
4
4
  "description": "RUF: Data package",
5
5
  "author": "Adarsh Pastakia <adarshpastakia@outlook.com>",
6
6
  "license": "MIT",
@@ -35,10 +35,10 @@
35
35
  "watch": "npm-run-all -p watch:rollup watch:dts"
36
36
  },
37
37
  "dependencies": {
38
- "@react-fabric/core": "^1.0.0-beta.59",
39
- "@react-fabric/date": "^1.0.0-beta.59",
40
- "@react-fabric/form": "^1.0.0-beta.59",
41
- "@react-fabric/utilities": "^1.0.0-beta.59",
38
+ "@react-fabric/core": "^1.0.0-beta.60",
39
+ "@react-fabric/date": "^1.0.0-beta.60",
40
+ "@react-fabric/form": "^1.0.0-beta.60",
41
+ "@react-fabric/utilities": "^1.0.0-beta.60",
42
42
  "@tanstack/react-virtual": "~3.13.12",
43
43
  "classnames": "~2.5.1",
44
44
  "color": "~5.0.2",
@@ -54,5 +54,5 @@
54
54
  "node": ">=18.12.0",
55
55
  "npm": ">=8.19.0"
56
56
  },
57
- "gitHead": "74efd06c9c5ac629b6ef5cdc6d53ecb1c356f2eb"
57
+ "gitHead": "a79f053ee7e13422c06d7d9c35d4f0c2610cdf5d"
58
58
  }
@@ -163,7 +163,7 @@ export const Histogram = ({
163
163
  >
164
164
  <div
165
165
  className={classNames(
166
- "absolute z-0 inset-0 opacity-50 min-w-2 ltr:bg-gradient-to-r rtl:bg-gradient-to-l",
166
+ "absolute z-0 inset-0 opacity-50 min-w-2 ltr:bg-linear-to-r rtl:bg-linear-to-l",
167
167
  "fabric-histoBar",
168
168
  )}
169
169
  style={{
@@ -74,7 +74,7 @@ export const BodyRow = ({
74
74
  role="none"
75
75
  data-row={index}
76
76
  className={classNames(
77
- "flex flex-nowrap datatable-row data-[hilight]:outline-1 -outline-offset-1 outline-primary-500 group/table-row",
77
+ "flex flex-nowrap datatable-row data-hilight:outline-1 -outline-offset-1 outline-primary-500 group/table-row",
78
78
  onRowClick && "hover:bg-primary-50 active:bg-primary-100",
79
79
  index % 2 ? "bg-even" : "bg-odd",
80
80
  )}
@@ -21,7 +21,7 @@
21
21
  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
  */
23
23
 
24
- import { IconProps } from "@react-fabric/core/dist/types/components/icon/Icon";
24
+ import { type IconProps } from "@react-fabric/core/dist/types/components/icon/Icon";
25
25
  import {
26
26
  type CssProp,
27
27
  type Elements,
@@ -63,7 +63,7 @@ export const TreeNode = ({
63
63
  "flex-initial self-stretch flex items-center w-6 relative",
64
64
  !noLines &&
65
65
  line > 0 &&
66
- "before:absolute before:top-0 before:start-[7px] before:bg-tint-200 before:min-h-[50%] before:w-[2px] before:scale-x-[.625]",
66
+ "before:absolute before:top-0 before:start-[7px] before:bg-tint-200 before:min-h-[50%] before:w-0.5 before:scale-x-[.625]",
67
67
  !noLines && line !== 2 && "before:h-full",
68
68
  )}
69
69
  >
@@ -147,7 +147,7 @@ export const TreeNode = ({
147
147
  <div
148
148
  role="none"
149
149
  className={classNames(
150
- "group/tool data-[selected]:bg-primary-100 flex flex-nowrap flex-1 overflow-hidden select-none",
150
+ "group/tool data-selected:bg-primary-100 flex flex-nowrap flex-1 overflow-hidden select-none",
151
151
  node.childSelected && "font-medium",
152
152
  node.disabled && "text-muted opacity-65",
153
153
  node.className,
@@ -160,10 +160,10 @@ export const TreeNode = ({
160
160
  onClick={
161
161
  node.disabled
162
162
  ? undefined
163
- : () => {
163
+ : (e) => {
164
164
  onClick?.(node.id, node.data);
165
165
  canSelect
166
- ? onSelect(node.id)
166
+ ? onSelect(node.id, e.shiftKey)
167
167
  : canCheck
168
168
  ? onChecked(node.id)
169
169
  : onToggle(node.id);
@@ -88,6 +88,7 @@ export const TreePanel = <T extends KeyValue>({
88
88
  noLines,
89
89
  searchable,
90
90
  selectable,
91
+ multiple,
91
92
  matcher,
92
93
  filterPlaceholder,
93
94
  defaultLeafIcon,
@@ -123,6 +124,7 @@ export const TreePanel = <T extends KeyValue>({
123
124
  items,
124
125
  checked,
125
126
  selected,
127
+ multiple,
126
128
  matcher,
127
129
  sorter,
128
130
  onLoad,
@@ -131,7 +133,7 @@ export const TreePanel = <T extends KeyValue>({
131
133
  onChecked,
132
134
  defaultExpanded,
133
135
  onExpandToggle,
134
- });
136
+ } as any);
135
137
 
136
138
  const scrollerRef = useRef<HTMLDivElement>(null);
137
139
  const virtualizer = useVirtualizer({
package/src/tree/types.ts CHANGED
@@ -21,7 +21,7 @@
21
21
  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
  */
23
23
 
24
- import { IconProps } from "@react-fabric/core/dist/types/components/icon/Icon";
24
+ import { type IconProps } from "@react-fabric/core/dist/types/components/icon/Icon";
25
25
  import {
26
26
  type BadgeType,
27
27
  type ChildProp,
@@ -109,7 +109,7 @@ export interface TreeNodeProps extends ChildProp {
109
109
  leafClassName?: string;
110
110
  nodeClassName?: string;
111
111
  onToggle: (id: string) => void;
112
- onSelect: (id: string) => void;
112
+ onSelect: (id: string, shiftKey?: boolean) => void;
113
113
  onChecked: (id: string) => void;
114
114
  onClick?: (id: string, node: any) => void;
115
115
  }
@@ -120,7 +120,40 @@ export interface TreeRef {
120
120
  openAndLoad: (list: string[]) => void;
121
121
  }
122
122
 
123
- export interface TreePanelProps<T extends KeyValue = KeyValue>
123
+ export type TreePanelProps<T extends KeyValue = KeyValue> =
124
+ BaseTreePanelProps<T> &
125
+ (
126
+ | {
127
+ /**
128
+ * enable multi select
129
+ */
130
+ multiple?: never | false;
131
+ /**
132
+ * selected item id
133
+ */
134
+ selected?: string;
135
+ /**
136
+ * callback on selection of tree node
137
+ */
138
+ onSelect?: (id: string, data: T) => void;
139
+ }
140
+ | {
141
+ /**
142
+ * enable multi select
143
+ */
144
+ multiple: true;
145
+ /**
146
+ * selected item id
147
+ */
148
+ selected?: string[];
149
+ /**
150
+ * callback on selection of tree node
151
+ */
152
+ onSelect?: (id: string[], data: T) => void;
153
+ }
154
+ );
155
+
156
+ interface BaseTreePanelProps<T extends KeyValue = KeyValue>
124
157
  extends TestProps,
125
158
  RefProp<TreeRef> {
126
159
  items?: Array<TreeNodeType<T>>;
@@ -137,13 +170,13 @@ export interface TreePanelProps<T extends KeyValue = KeyValue>
137
170
  */
138
171
  selectable?: true | "leafOnly";
139
172
  /**
140
- * enable checkable
173
+ * enable multi select
141
174
  */
142
- checkable?: true | "leafOnly";
175
+ multiple?: boolean;
143
176
  /**
144
- * selected item id
177
+ * enable checkable
145
178
  */
146
- selected?: string;
179
+ checkable?: true | "leafOnly";
147
180
  /**
148
181
  * checked items
149
182
  */
@@ -181,10 +214,6 @@ export interface TreePanelProps<T extends KeyValue = KeyValue>
181
214
  * callback on click of tree node
182
215
  */
183
216
  onClick?: (id: string, data: T) => void;
184
- /**
185
- * callback on selection of tree node
186
- */
187
- onSelect?: (id: string, data: T) => boolean | undefined;
188
217
  /**
189
218
  * callback on change of checked list
190
219
  * @param leafs - list of checked leaf nodes
@@ -24,7 +24,13 @@
24
24
  import { useDebounce } from "@react-fabric/core";
25
25
  import { EMPTY_ARRAY, isEmpty } from "@react-fabric/utilities";
26
26
  import memoizeOne from "memoize-one";
27
- import { useCallback, useEffect, useReducer, useRef } from "react";
27
+ import {
28
+ useCallback,
29
+ useEffect,
30
+ useReducer,
31
+ useRef,
32
+ useEffectEvent,
33
+ } from "react";
28
34
  import {
29
35
  type InternalNode,
30
36
  type TreeNodeType,
@@ -59,7 +65,7 @@ type TreeActions =
59
65
  /**
60
66
  * toggle item select, used as action handler
61
67
  */
62
- | { type: "toggleSelect"; id: string }
68
+ | { type: "toggleSelect"; id: string; shiftKey?: boolean }
63
69
  /**
64
70
  * toggle item check, used as action handler
65
71
  */
@@ -90,7 +96,7 @@ interface TreeState {
90
96
  itemMap: Map<string, InternalNode>;
91
97
  tree: InternalNode[];
92
98
  query?: string;
93
- selected?: string;
99
+ selected?: AnyObject;
94
100
  checked?: string[];
95
101
  }
96
102
 
@@ -100,6 +106,7 @@ export const useTree = <T extends KeyValue>({
100
106
  items = EMPTY_ARRAY,
101
107
  checked = EMPTY_ARRAY,
102
108
  selected,
109
+ multiple,
103
110
  sorter,
104
111
  matcher,
105
112
  onLoad,
@@ -108,8 +115,12 @@ export const useTree = <T extends KeyValue>({
108
115
  onChecked,
109
116
  defaultExpanded,
110
117
  onExpandToggle,
111
- }: Partial<TreePanelProps<T>>) => {
112
- const componentEvents = useRef({ onSelect, onChecked });
118
+ }: Partial<TreePanelProps<T>> & {
119
+ selected?: AnyObject;
120
+ multiple?: boolean;
121
+ }) => {
122
+ const eventSelect = useEffectEvent(onSelect ?? (() => undefined));
123
+ const eventChecked = useEffectEvent(onChecked ?? (() => undefined));
113
124
  const intialExpand = useRef([...(defaultExpanded ?? EMPTY_ARRAY)]);
114
125
  const itemList = createItemList(items);
115
126
 
@@ -159,18 +170,15 @@ export const useTree = <T extends KeyValue>({
159
170
  [onLoad],
160
171
  );
161
172
 
162
- const fireSelected = useDebounce(
163
- (nodeId: string, data: T) => {
164
- componentEvents.current.onSelect?.(nodeId, data);
165
- },
166
- [onSelect],
167
- );
173
+ const fireSelected = useDebounce((nodeId: any, data: T) => {
174
+ eventSelect(nodeId, data);
175
+ }, []);
168
176
 
169
177
  const fireChecked = useDebounce(
170
178
  (leafs: string[], nodes: string[], partials: string[]) => {
171
- componentEvents.current.onChecked?.(leafs, nodes, partials);
179
+ eventChecked(leafs, nodes, partials);
172
180
  },
173
- [onChecked],
181
+ [],
174
182
  );
175
183
 
176
184
  const fireQuery = useDebounce(
@@ -203,7 +211,12 @@ export const useTree = <T extends KeyValue>({
203
211
  state.itemMap = makeTreeMap(state.items);
204
212
  fireExpandToggle(Array.from(state.itemMap.values()));
205
213
  state.tree = flattenTree(state.items);
206
- state.selected = updateSelection(state.itemMap, selected, true);
214
+ state.selected = selected;
215
+ updateSelection({
216
+ nodes: state.itemMap,
217
+ multiple,
218
+ selected,
219
+ });
207
220
  state.checked?.forEach((id) => updateChecked(state.itemMap, id, 1));
208
221
  return { ...state };
209
222
  }
@@ -256,22 +269,33 @@ export const useTree = <T extends KeyValue>({
256
269
  return { ...state, query: action.query };
257
270
  }
258
271
  if (action.type === "toggleSelect") {
259
- if (!onSelect) {
260
- if (state.selected) {
261
- updateSelection(state.itemMap, state.selected);
262
- }
263
- state.selected = updateSelection(state.itemMap, action.id, true);
264
- state.tree = flattenTree(state.items);
272
+ if (multiple) {
273
+ const isSelected = state.selected?.includes(action.id);
274
+ if (action.shiftKey && isSelected)
275
+ state.selected =
276
+ state.selected?.filter((id: string) => id !== action.id) ?? [];
277
+ else if (action.shiftKey && !isSelected)
278
+ state.selected = [...(state.selected ?? []), action.id];
279
+ else state.selected = [action.id];
265
280
  } else {
266
- fireSelected(action.id, state.itemMap.get(action.id)?.data);
281
+ state.selected = action.id;
267
282
  }
283
+ updateSelection({
284
+ nodes: state.itemMap,
285
+ selected: state.selected,
286
+ multiple,
287
+ });
288
+ state.tree = flattenTree(state.items);
289
+ fireSelected(state.selected, state.itemMap.get(action.id)?.data);
268
290
  return { ...state };
269
291
  }
270
292
  if (action.type === "select") {
271
- if (state.selected) {
272
- updateSelection(state.itemMap, state.selected);
273
- }
274
- state.selected = updateSelection(state.itemMap, action.id, true);
293
+ state.selected = action.id;
294
+ updateSelection({
295
+ nodes: state.itemMap,
296
+ multiple,
297
+ selected: action.id,
298
+ });
275
299
  state.tree = flattenTree(state.items);
276
300
  return { ...state };
277
301
  }
@@ -306,10 +330,6 @@ export const useTree = <T extends KeyValue>({
306
330
  },
307
331
  );
308
332
 
309
- useEffect(() => {
310
- componentEvents.current = { onSelect, onChecked };
311
- }, [onSelect, onChecked]);
312
-
313
333
  useEffect(() => {
314
334
  dispatch({ type: "load", items: itemList });
315
335
  itemList.length &&
@@ -338,8 +358,8 @@ export const useTree = <T extends KeyValue>({
338
358
  dispatch({ type: "toggleCheck", id });
339
359
  }, []);
340
360
 
341
- const select = useCallback((id: string) => {
342
- dispatch({ type: "toggleSelect", id });
361
+ const select = useCallback((id: string, shiftKey = false) => {
362
+ dispatch({ type: "toggleSelect", id, shiftKey });
343
363
  }, []);
344
364
 
345
365
  const expand = useCallback((id: string) => {
@@ -349,7 +369,10 @@ export const useTree = <T extends KeyValue>({
349
369
  const expandAndLoad = useCallback((expandList: string[]) => {
350
370
  if (expandList.length > 0) {
351
371
  intialExpand.current = [...expandList];
352
- dispatch({ type: "expand", id: intialExpand.current.shift() as string });
372
+ dispatch({
373
+ type: "expand",
374
+ id: intialExpand.current.shift() as unknown as string,
375
+ });
353
376
  }
354
377
  }, []);
355
378
 
package/src/tree/utils.ts CHANGED
@@ -21,7 +21,12 @@
21
21
  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
  */
23
23
 
24
- import { compareValues, isString, matchString } from "@react-fabric/utilities";
24
+ import {
25
+ compareValues,
26
+ isArray,
27
+ isString,
28
+ matchString,
29
+ } from "@react-fabric/utilities";
25
30
  import { type InternalNode, type TreeNodeType } from "./types";
26
31
 
27
32
  const defaultSorter = (a: TreeNodeType, b: TreeNodeType) => {
@@ -125,27 +130,40 @@ export const flattenTree = (
125
130
  return list;
126
131
  };
127
132
 
128
- export const updateSelection = (
129
- nodes: Map<string, InternalNode>,
130
- id?: string,
131
- selected?: true,
132
- ) => {
133
+ const selectNode = (nodes: Map<string, InternalNode>, id?: string) => {
133
134
  const node = nodes.get(id ?? "");
134
135
  if (node) {
135
136
  // change selection
136
- node.selected = selected;
137
+ node.selected = true;
137
138
  // update all parent childSelected flag
138
139
  let parent = node.parent;
139
140
  while (parent) {
140
141
  const parentNode = nodes.get(parent);
141
- parentNode && (parentNode.childSelected = selected);
142
+ parentNode && (parentNode.childSelected = true);
142
143
  // selected && parentNode && (parentNode.open = true);
143
144
  parent = parentNode?.parent;
144
145
  }
145
- return id;
146
146
  }
147
147
  };
148
148
 
149
+ export const updateSelection = ({
150
+ nodes,
151
+ multiple,
152
+ selected,
153
+ }: {
154
+ nodes: Map<string, InternalNode>;
155
+ multiple?: boolean;
156
+ selected?: string | string[];
157
+ }) => {
158
+ nodes.forEach((node) => {
159
+ node.selected = undefined;
160
+ node.childSelected = undefined;
161
+ });
162
+ multiple && isArray(selected)
163
+ ? selected?.forEach((id) => selectNode(nodes, id))
164
+ : selectNode(nodes, selected as string);
165
+ };
166
+
149
167
  export const updateChecked = (
150
168
  list: Map<string, AnyObject>,
151
169
  id: string,
@@ -153,8 +171,7 @@ export const updateChecked = (
153
171
  ) => {
154
172
  const node = list.get(id);
155
173
  if (node) {
156
- node.checked =
157
- checked === undefined ? (node.checked === 0 ? 1 : 0) : checked;
174
+ node.checked = checked ?? node.checked === 0 ? 1 : 0;
158
175
  // check all children
159
176
  node.children &&
160
177
  getChildren(node.children).forEach(
@@ -337,11 +337,13 @@ const _VirtualGallery = <T extends AnyObject>({
337
337
  minHeight: height,
338
338
  }}
339
339
  >
340
- {itemList[index * columnCount + col]?.item ? (
341
- children(itemList[index * columnCount + col] as AnyObject)
342
- ) : (
343
- <Skeleton className="p-2" />
344
- )}
340
+ {itemList[index * columnCount + col]?.item
341
+ ? children(
342
+ itemList[index * columnCount + col] as AnyObject,
343
+ )
344
+ : itemList[index * columnCount + col]?.item === null && (
345
+ <Skeleton className="p-2" />
346
+ )}
345
347
  </div>
346
348
  ))}
347
349
  </div>
@@ -322,11 +322,11 @@ const _VirtualList = <T extends AnyObject>({
322
322
  minHeight: height,
323
323
  }}
324
324
  >
325
- {itemList[index]?.item ? (
326
- children(itemList[index] as AnyObject)
327
- ) : (
328
- <Skeleton className="p-2" />
329
- )}
325
+ {itemList[index]?.item
326
+ ? children(itemList[index] as AnyObject)
327
+ : itemList[index]?.item === null && (
328
+ <Skeleton className="p-2" />
329
+ )}
330
330
  </div>
331
331
  ))}
332
332
  </div>