@snack-uikit/tree 0.8.19 → 0.8.20

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.
Files changed (31) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/cjs/helperComponents/ExpandableTreeNode/ExpandableTreeNode.d.ts +1 -1
  3. package/dist/cjs/helperComponents/ExpandableTreeNode/ExpandableTreeNode.js +28 -11
  4. package/dist/cjs/helperComponents/ExpandableTreeNode/styles.module.css +4 -0
  5. package/dist/cjs/helperComponents/TreeItem/TreeItem.d.ts +1 -1
  6. package/dist/cjs/helperComponents/TreeItem/TreeItem.js +8 -5
  7. package/dist/cjs/helperComponents/TreeLine/TreeLine.d.ts +4 -3
  8. package/dist/cjs/helperComponents/TreeLine/TreeLine.js +10 -4
  9. package/dist/cjs/helperComponents/TreeLine/styles.module.css +3 -3
  10. package/dist/cjs/helperComponents/TreeNode/TreeNode.d.ts +1 -1
  11. package/dist/cjs/helperComponents/TreeNode/TreeNode.js +27 -20
  12. package/dist/cjs/helperComponents/TreeNode/styles.module.css +20 -6
  13. package/dist/esm/helperComponents/ExpandableTreeNode/ExpandableTreeNode.d.ts +1 -1
  14. package/dist/esm/helperComponents/ExpandableTreeNode/ExpandableTreeNode.js +22 -11
  15. package/dist/esm/helperComponents/ExpandableTreeNode/styles.module.css +4 -0
  16. package/dist/esm/helperComponents/TreeItem/TreeItem.d.ts +1 -1
  17. package/dist/esm/helperComponents/TreeItem/TreeItem.js +5 -4
  18. package/dist/esm/helperComponents/TreeLine/TreeLine.d.ts +4 -3
  19. package/dist/esm/helperComponents/TreeLine/TreeLine.js +3 -2
  20. package/dist/esm/helperComponents/TreeLine/styles.module.css +3 -3
  21. package/dist/esm/helperComponents/TreeNode/TreeNode.d.ts +1 -1
  22. package/dist/esm/helperComponents/TreeNode/TreeNode.js +13 -11
  23. package/dist/esm/helperComponents/TreeNode/styles.module.css +20 -6
  24. package/package.json +2 -2
  25. package/src/helperComponents/ExpandableTreeNode/ExpandableTreeNode.tsx +120 -97
  26. package/src/helperComponents/ExpandableTreeNode/styles.module.scss +6 -0
  27. package/src/helperComponents/TreeItem/TreeItem.tsx +8 -4
  28. package/src/helperComponents/TreeLine/TreeLine.tsx +8 -5
  29. package/src/helperComponents/TreeLine/styles.module.scss +9 -13
  30. package/src/helperComponents/TreeNode/TreeNode.tsx +266 -245
  31. package/src/helperComponents/TreeNode/styles.module.scss +36 -19
package/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## 0.8.20 (2025-01-31)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **PDS-1461:** fix height of tree node with custom content ([c53e1cc](https://github.com/cloud-ru-tech/snack-uikit/commit/c53e1cc2a90634bee4619c2b8f1d4690ef49d461))
12
+
13
+
14
+
15
+
16
+
6
17
  ## 0.8.19 (2025-01-23)
7
18
 
8
19
  ### Only dependencies have been changed
@@ -7,5 +7,5 @@ type ExpandableTreeNodeProps = {
7
7
  };
8
8
  tabIndexAvailable?: boolean;
9
9
  };
10
- export declare function ExpandableTreeNode({ node: { 'data-test-id': dataTestId, ...node }, parentNode, tabIndexAvailable, }: ExpandableTreeNodeProps): import("react/jsx-runtime").JSX.Element;
10
+ export declare const ExpandableTreeNode: import("react").ForwardRefExoticComponent<ExpandableTreeNodeProps & import("react").RefAttributes<HTMLDivElement>>;
11
11
  export {};
@@ -43,7 +43,7 @@ var __importDefault = void 0 && (void 0).__importDefault || function (mod) {
43
43
  Object.defineProperty(exports, "__esModule", {
44
44
  value: true
45
45
  });
46
- exports.ExpandableTreeNode = ExpandableTreeNode;
46
+ exports.ExpandableTreeNode = void 0;
47
47
  const jsx_runtime_1 = require("react/jsx-runtime");
48
48
  const react_1 = require("react");
49
49
  const react_transition_state_1 = __importDefault(require("react-transition-state"));
@@ -53,7 +53,7 @@ const TreeItem_1 = require("../TreeItem");
53
53
  const TreeLine_1 = require("../TreeLine");
54
54
  const TreeNode_1 = require("../TreeNode");
55
55
  const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
56
- function ExpandableTreeNode(_a) {
56
+ exports.ExpandableTreeNode = (0, react_1.forwardRef)((_a, ref) => {
57
57
  var _b = _a.node,
58
58
  {
59
59
  'data-test-id': dataTestId
@@ -78,18 +78,32 @@ function ExpandableTreeNode(_a) {
78
78
  enter: isExpanded
79
79
  });
80
80
  const showContent = node.nested && node.nested.length > 0 && state.status !== 'exited';
81
- const isLineHalfHeight = (0, react_1.useMemo)(() => {
81
+ const childrenRefs = (0, react_1.useRef)([]);
82
+ const [lastItemHeight, setLastItemHeight] = (0, react_1.useState)(0);
83
+ (0, react_1.useEffect)(() => {
82
84
  var _a, _b;
83
- if (!((_a = node.nested) === null || _a === void 0 ? void 0 : _a.length)) return false;
84
- return !((_b = node.nested.at(-1)) === null || _b === void 0 ? void 0 : _b.nested);
85
- }, [node.nested]);
85
+ const lastItem = (_a = childrenRefs.current) === null || _a === void 0 ? void 0 : _a.at(-1);
86
+ if (lastItem) {
87
+ const observer = new ResizeObserver(entities => entities.forEach(entity => {
88
+ if (entity.target === lastItem) {
89
+ const [{
90
+ blockSize: newHeight
91
+ }] = entity.contentBoxSize;
92
+ setLastItemHeight(newHeight);
93
+ }
94
+ }));
95
+ observer.observe(lastItem);
96
+ setLastItemHeight((_b = lastItem.scrollHeight) !== null && _b !== void 0 ? _b : 0);
97
+ return () => observer.disconnect();
98
+ }
99
+ }, [state.status]);
86
100
  (0, react_1.useEffect)(() => {
87
101
  var _a;
88
102
  if (((_a = node.nested) === null || _a === void 0 ? void 0 : _a.length) && isExpanded && state.status === 'exited') {
89
103
  toggle(true);
90
104
  }
91
105
  }, [isExpanded, node.nested, state.status]);
92
- const toggleExpand = () => __awaiter(this, void 0, void 0, function* () {
106
+ const toggleExpand = () => __awaiter(void 0, void 0, void 0, function* () {
93
107
  if (node.nested && !node.nested.length && !isExpanded && onDataLoad) {
94
108
  setLoading(true);
95
109
  yield onDataLoad(node).finally(() => {
@@ -125,6 +139,7 @@ function ExpandableTreeNode(_a) {
125
139
  "data-test-id": dataTestId,
126
140
  "data-expandable": Boolean(node.nested) || undefined,
127
141
  "data-disabled": node.disabled || undefined,
142
+ ref: ref,
128
143
  children: [(0, jsx_runtime_1.jsx)(TreeNode_1.TreeNode, Object.assign({}, node, {
129
144
  isLoading: isLoading,
130
145
  parentNode: parentNode,
@@ -140,17 +155,19 @@ function ExpandableTreeNode(_a) {
140
155
  "data-test-id": constants_1.TEST_IDS.expandableContent,
141
156
  children: [(0, jsx_runtime_1.jsx)(TreeLine_1.TreeLine, {
142
157
  visible: showLines,
143
- halfHeight: isLineHalfHeight,
144
- "data-test-id": constants_1.TEST_IDS.line
158
+ "data-test-id": constants_1.TEST_IDS.line,
159
+ height: `calc(100% - ${lastItemHeight}px)`,
160
+ className: styles_module_scss_1.default.treeLine
145
161
  }), (0, jsx_runtime_1.jsx)("div", {
146
162
  className: styles_module_scss_1.default.expandableNested,
147
- children: node.nested.map(nestedNode => {
163
+ children: node.nested.map((nestedNode, index) => {
148
164
  const parent = {
149
165
  id: node.id,
150
166
  nested: node.nested,
151
167
  parentNode
152
168
  };
153
169
  return (0, jsx_runtime_1.jsx)(TreeItem_1.TreeItem, {
170
+ ref: el => childrenRefs.current[index] = el,
154
171
  node: Object.assign(Object.assign({}, nestedNode), {
155
172
  disabled: node.disabled || nestedNode.disabled
156
173
  }),
@@ -161,4 +178,4 @@ function ExpandableTreeNode(_a) {
161
178
  })
162
179
  })]
163
180
  });
164
- }
181
+ });
@@ -31,4 +31,8 @@
31
31
  .expandableNested{
32
32
  width:100%;
33
33
  min-width:0;
34
+ }
35
+
36
+ .treeLine{
37
+ padding-bottom:calc(var(--size-tree-item-line-height, 32px) / 2);
34
38
  }
@@ -4,5 +4,5 @@ type TreeItemProps = {
4
4
  parentNode?: ParentNode;
5
5
  tabIndexAvailable?: boolean;
6
6
  };
7
- export declare function TreeItem({ node, tabIndexAvailable, parentNode }: TreeItemProps): import("react/jsx-runtime").JSX.Element;
7
+ export declare const TreeItem: import("react").ForwardRefExoticComponent<TreeItemProps & import("react").RefAttributes<HTMLDivElement>>;
8
8
  export {};
@@ -3,12 +3,13 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.TreeItem = TreeItem;
6
+ exports.TreeItem = void 0;
7
7
  const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const react_1 = require("react");
8
9
  const constants_1 = require("../../constants");
9
10
  const ExpandableTreeNode_1 = require("../ExpandableTreeNode");
10
11
  const TreeNode_1 = require("../TreeNode");
11
- function TreeItem(_ref) {
12
+ exports.TreeItem = (0, react_1.forwardRef)((_ref, ref) => {
12
13
  let {
13
14
  node,
14
15
  tabIndexAvailable,
@@ -22,11 +23,13 @@ function TreeItem(_ref) {
22
23
  return (0, jsx_runtime_1.jsx)(ExpandableTreeNode_1.ExpandableTreeNode, {
23
24
  node: nodeProps,
24
25
  tabIndexAvailable: tabIndexAvailable,
25
- parentNode: parentNode
26
+ parentNode: parentNode,
27
+ ref: ref
26
28
  });
27
29
  }
28
30
  return (0, jsx_runtime_1.jsx)(TreeNode_1.TreeNode, Object.assign({}, nodeProps, {
29
31
  tabIndexAvailable: tabIndexAvailable,
30
- parentNode: parentNode
32
+ parentNode: parentNode,
33
+ ref: ref
31
34
  }));
32
- }
35
+ });
@@ -1,7 +1,8 @@
1
+ import { CSSProperties } from 'react';
1
2
  export type TreeLineProps = {
3
+ className?: string;
2
4
  halfWidth?: boolean;
3
- halfHeight?: boolean;
4
5
  visible?: boolean;
5
6
  horizontal?: boolean;
6
- };
7
- export declare function TreeLine({ halfWidth, halfHeight, horizontal, visible }: TreeLineProps): import("react/jsx-runtime").JSX.Element;
7
+ } & Pick<CSSProperties, 'height' | 'width'>;
8
+ export declare function TreeLine({ className, halfWidth, horizontal, visible, height, width }: TreeLineProps): import("react/jsx-runtime").JSX.Element;
@@ -10,20 +10,26 @@ Object.defineProperty(exports, "__esModule", {
10
10
  });
11
11
  exports.TreeLine = TreeLine;
12
12
  const jsx_runtime_1 = require("react/jsx-runtime");
13
+ const classnames_1 = __importDefault(require("classnames"));
13
14
  const constants_1 = require("../../constants");
14
15
  const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
15
16
  function TreeLine(_ref) {
16
17
  let {
18
+ className,
17
19
  halfWidth,
18
- halfHeight,
19
20
  horizontal,
20
- visible
21
+ visible,
22
+ height,
23
+ width
21
24
  } = _ref;
22
25
  return (0, jsx_runtime_1.jsx)("div", {
23
- className: styles_module_scss_1.default.treeLine,
26
+ className: (0, classnames_1.default)(styles_module_scss_1.default.treeLine, className),
27
+ style: {
28
+ height,
29
+ width
30
+ },
24
31
  "data-horizontal": horizontal || undefined,
25
32
  "data-half-width": halfWidth || undefined,
26
- "data-half-height": halfHeight || undefined,
27
33
  "data-visible": visible || undefined,
28
34
  "data-test-id": constants_1.TEST_IDS.line
29
35
  });
@@ -15,11 +15,11 @@
15
15
  left:50%;
16
16
  transform:translateX(-50%);
17
17
  width:1px;
18
- height:calc(100% - var(--size-tree-item-height, 32px) / 2);
18
+ height:100%;
19
19
  background:var(--sys-neutral-decor-default, #dde0ea);
20
20
  }
21
- .treeLine[data-half-height]::after{
22
- height:calc(100% - var(--size-tree-item-height, 32px) / 2);
21
+ .treeLine[data-horizontal]{
22
+ min-height:var(--size-tree-item-height, 32px);
23
23
  }
24
24
  .treeLine[data-horizontal]::after{
25
25
  top:50%;
@@ -8,4 +8,4 @@ type TreeNodeComponentProps = TreeNodeProps & {
8
8
  parentNode?: ParentNode;
9
9
  tabIndexAvailable?: boolean;
10
10
  };
11
- export declare function TreeNode({ id, title, icon, expandedIcon, collapsedIcon, disabled, onClick, nested, className, onChevronClick, onKeyDown, isLoading, parentNode, tabIndexAvailable, ...rest }: TreeNodeComponentProps): import("react/jsx-runtime").JSX.Element;
11
+ export declare const TreeNode: import("react").ForwardRefExoticComponent<TreeNodeComponentProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -16,7 +16,7 @@ var __importDefault = void 0 && (void 0).__importDefault || function (mod) {
16
16
  Object.defineProperty(exports, "__esModule", {
17
17
  value: true
18
18
  });
19
- exports.TreeNode = TreeNode;
19
+ exports.TreeNode = void 0;
20
20
  const jsx_runtime_1 = require("react/jsx-runtime");
21
21
  const classnames_1 = __importDefault(require("classnames"));
22
22
  const react_1 = require("react");
@@ -33,7 +33,7 @@ const TreeLine_1 = require("../TreeLine");
33
33
  const components_1 = require("./components");
34
34
  const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
35
35
  const utils_2 = require("./utils");
36
- function TreeNode(_a) {
36
+ exports.TreeNode = (0, react_1.forwardRef)((_a, ref) => {
37
37
  var {
38
38
  id,
39
39
  title,
@@ -77,7 +77,7 @@ function TreeNode(_a) {
77
77
  } = (0, TreeContext_1.useTreeContext)();
78
78
  const [isDroplistOpen, setDroplistOpen] = (0, react_1.useState)(false);
79
79
  const [isDroplistTriggerFocused, setFocusDroplistTrigger] = (0, react_1.useState)(false);
80
- const ref = (0, react_1.useRef)(null);
80
+ const contentRef = (0, react_1.useRef)(null);
81
81
  const isExpandable = Array.isArray(nested);
82
82
  const isExpanded = isExpandable ? expandedNodes === null || expandedNodes === void 0 ? void 0 : expandedNodes.includes(id) : undefined;
83
83
  const nestedNodesSelection = (0, react_1.useMemo)(() => {
@@ -87,8 +87,8 @@ function TreeNode(_a) {
87
87
  const isSelected = (Array.isArray(selected) ? selected.includes(id) || (nestedNodesSelection === null || nestedNodesSelection === void 0 ? void 0 : nestedNodesSelection.allSelected) : selected === id) || false;
88
88
  const isFocused = focusedNodeId === id;
89
89
  (0, react_1.useEffect)(() => {
90
- if (ref.current && isFocused) {
91
- ref.current.focus();
90
+ if (contentRef.current && isFocused) {
91
+ contentRef.current.focus();
92
92
  }
93
93
  }, [isFocused]);
94
94
  const treeNodeIcon = (0, react_1.useMemo)(() => {
@@ -119,14 +119,14 @@ function TreeNode(_a) {
119
119
  setFocusPosition(id);
120
120
  if (!e) {
121
121
  setFocusDroplistTrigger(false);
122
- (_a = ref.current) === null || _a === void 0 ? void 0 : _a.focus();
122
+ (_a = contentRef.current) === null || _a === void 0 ? void 0 : _a.focus();
123
123
  }
124
124
  };
125
125
  const handleBlurActions = () => {
126
126
  var _a;
127
127
  if (isDroplistTriggerFocused && !isDroplistOpen) {
128
128
  setFocusDroplistTrigger(false);
129
- (_a = ref.current) === null || _a === void 0 ? void 0 : _a.blur();
129
+ (_a = contentRef.current) === null || _a === void 0 ? void 0 : _a.blur();
130
130
  }
131
131
  };
132
132
  const handleKeyDown = e => {
@@ -171,13 +171,13 @@ function TreeNode(_a) {
171
171
  e.preventDefault();
172
172
  if (isDroplistTriggerFocused) {
173
173
  setFocusDroplistTrigger(false);
174
- (_a = ref.current) === null || _a === void 0 ? void 0 : _a.focus();
174
+ (_a = contentRef.current) === null || _a === void 0 ? void 0 : _a.focus();
175
175
  }
176
176
  return;
177
177
  }
178
178
  case 'Escape':
179
179
  {
180
- (_b = ref.current) === null || _b === void 0 ? void 0 : _b.blur();
180
+ (_b = contentRef.current) === null || _b === void 0 ? void 0 : _b.blur();
181
181
  return;
182
182
  }
183
183
  case ' ':
@@ -197,6 +197,7 @@ function TreeNode(_a) {
197
197
  className: (0, classnames_1.default)(styles_module_scss_1.default.treeNode, className)
198
198
  }, (0, utils_1.extractSupportProps)(rest), {
199
199
  "data-node-id": id,
200
+ ref: ref,
200
201
  children: [parentNode && (0, jsx_runtime_1.jsx)(TreeLine_1.TreeLine, {
201
202
  halfWidth: Boolean(nested),
202
203
  horizontal: true,
@@ -204,15 +205,21 @@ function TreeNode(_a) {
204
205
  "data-test-id": constants_1.TEST_IDS.line
205
206
  }), !parentNode && !nested && (0, jsx_runtime_1.jsx)(TreeLine_1.TreeLine, {
206
207
  visible: false
207
- }), isExpandable && (0, jsx_runtime_1.jsx)(button_1.ButtonFunction, {
208
- size: 'xs',
209
- icon: (0, jsx_runtime_1.jsx)(icons_1.ChevronRightSVG, {}),
210
- loading: isLoading,
211
- onClick: onChevronClick,
212
- "data-expanded": isExpanded || undefined,
213
- className: styles_module_scss_1.default.treeNodeExpandButton,
214
- tabIndex: -1,
215
- "data-test-id": constants_1.TEST_IDS.chevron
208
+ }), isExpandable && (0, jsx_runtime_1.jsxs)("div", {
209
+ className: styles_module_scss_1.default.treeNodeExpandButtonWrapper,
210
+ children: [(0, jsx_runtime_1.jsx)(button_1.ButtonFunction, {
211
+ size: 'xs',
212
+ icon: (0, jsx_runtime_1.jsx)(icons_1.ChevronRightSVG, {}),
213
+ loading: isLoading,
214
+ onClick: onChevronClick,
215
+ "data-expanded": isExpanded || undefined,
216
+ className: styles_module_scss_1.default.treeNodeExpandButton,
217
+ tabIndex: -1,
218
+ "data-test-id": constants_1.TEST_IDS.chevron
219
+ }), (0, jsx_runtime_1.jsx)(TreeLine_1.TreeLine, {
220
+ visible: isExpanded && showLines,
221
+ height: '100%'
222
+ })]
216
223
  }), (0, jsx_runtime_1.jsxs)("div", {
217
224
  role: 'treeitem',
218
225
  "aria-expanded": isExpanded,
@@ -227,7 +234,7 @@ function TreeNode(_a) {
227
234
  tabIndex: tabIndexAvailable ? 0 : -1,
228
235
  className: styles_module_scss_1.default.treeNodeContent,
229
236
  "data-test-id": constants_1.TEST_IDS.item,
230
- ref: ref,
237
+ ref: contentRef,
231
238
  children: [(isMultiSelect || showToggle) && (0, jsx_runtime_1.jsxs)("div", {
232
239
  className: styles_module_scss_1.default.treeNodeCheckboxWrap,
233
240
  children: [isMultiSelect && (0, jsx_runtime_1.jsx)(toggles_1.Checkbox, {
@@ -277,4 +284,4 @@ function TreeNode(_a) {
277
284
  })]
278
285
  })]
279
286
  }));
280
- }
287
+ });
@@ -1,6 +1,6 @@
1
1
  .treeNode{
2
2
  display:flex;
3
- align-items:center;
3
+ align-items:flex-start;
4
4
  box-sizing:border-box;
5
5
  width:100%;
6
6
  }
@@ -19,9 +19,12 @@
19
19
  }
20
20
 
21
21
  .treeNodeTitle{
22
+ min-height:var(--size-tree-item-wrap, 24px);
23
+ display:flex;
22
24
  flex-grow:1;
25
+ align-items:center;
23
26
  box-sizing:border-box;
24
- min-width:var(--size-tree-item-checkbox-wrap, 24px);
27
+ min-width:var(--size-tree-item-wrap, 24px);
25
28
  color:var(--sys-neutral-text-main, #41424e);
26
29
  }
27
30
 
@@ -34,15 +37,17 @@
34
37
  }
35
38
 
36
39
  .treeNodeContent{
37
- height:var(--size-tree-item-height, 32px);
38
40
  gap:var(--space-tree-item-content-layout-gap, 4px);
41
+ padding-top:var(--space-tree-item-content-layout-padding, 4px);
39
42
  padding-right:var(--space-tree-item-content-layout-padding, 4px);
43
+ padding-bottom:var(--space-tree-item-content-layout-padding, 4px);
40
44
  padding-left:var(--space-tree-item-content-layout-padding, 4px);
41
45
  border-radius:var(--radius-tree-item-content-layout, 8px);
46
+ min-height:var(--size-tree-item-height, 32px);
42
47
  cursor:pointer;
43
48
  position:relative;
44
49
  display:flex;
45
- align-items:center;
50
+ align-items:flex-start;
46
51
  box-sizing:border-box;
47
52
  width:100%;
48
53
  }
@@ -93,8 +98,8 @@
93
98
  }
94
99
 
95
100
  .treeNodeCheckboxWrap{
96
- width:var(--size-tree-item-checkbox-wrap, 24px);
97
- height:var(--size-tree-item-checkbox-wrap, 24px);
101
+ width:var(--size-tree-item-wrap, 24px);
102
+ height:var(--size-tree-item-wrap, 24px);
98
103
  display:flex;
99
104
  flex-shrink:0;
100
105
  align-items:center;
@@ -103,6 +108,7 @@
103
108
  }
104
109
 
105
110
  .treeNodeExpandButton{
111
+ min-height:var(--size-tree-item-height, 32px);
106
112
  box-sizing:border-box;
107
113
  }
108
114
  .treeNodeExpandButton svg{
@@ -110,4 +116,12 @@
110
116
  }
111
117
  .treeNodeExpandButton[data-expanded] svg{
112
118
  transform:rotate(90deg);
119
+ }
120
+
121
+ .treeNodeExpandButtonWrapper{
122
+ overflow:hidden;
123
+ display:flex;
124
+ flex-direction:column;
125
+ align-self:stretch;
126
+ min-width:var(--size-button-xs, 24px);
113
127
  }
@@ -7,5 +7,5 @@ type ExpandableTreeNodeProps = {
7
7
  };
8
8
  tabIndexAvailable?: boolean;
9
9
  };
10
- export declare function ExpandableTreeNode({ node: { 'data-test-id': dataTestId, ...node }, parentNode, tabIndexAvailable, }: ExpandableTreeNodeProps): import("react/jsx-runtime").JSX.Element;
10
+ export declare const ExpandableTreeNode: import("react").ForwardRefExoticComponent<ExpandableTreeNodeProps & import("react").RefAttributes<HTMLDivElement>>;
11
11
  export {};
@@ -19,7 +19,7 @@ var __rest = (this && this.__rest) || function (s, e) {
19
19
  return t;
20
20
  };
21
21
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
22
- import { useEffect, useMemo, useState } from 'react';
22
+ import { forwardRef, useEffect, useRef, useState } from 'react';
23
23
  import useTransition from 'react-transition-state';
24
24
  import { TEST_IDS, TRANSITION_TIMING } from '../../constants';
25
25
  import { useTreeContext } from '../../contexts/TreeContext';
@@ -27,7 +27,7 @@ import { TreeItem } from '../TreeItem';
27
27
  import { TreeLine } from '../TreeLine';
28
28
  import { TreeNode } from '../TreeNode';
29
29
  import styles from './styles.module.css';
30
- export function ExpandableTreeNode(_a) {
30
+ export const ExpandableTreeNode = forwardRef((_a, ref) => {
31
31
  var _b = _a.node, { 'data-test-id': dataTestId } = _b, node = __rest(_b, ['data-test-id']), { parentNode, tabIndexAvailable } = _a;
32
32
  const { expandedNodes, onExpand, onDataLoad, showLines } = useTreeContext();
33
33
  const isExpandable = Boolean(node.nested);
@@ -39,19 +39,30 @@ export function ExpandableTreeNode(_a) {
39
39
  enter: isExpanded,
40
40
  });
41
41
  const showContent = node.nested && node.nested.length > 0 && state.status !== 'exited';
42
- const isLineHalfHeight = useMemo(() => {
42
+ const childrenRefs = useRef([]);
43
+ const [lastItemHeight, setLastItemHeight] = useState(0);
44
+ useEffect(() => {
43
45
  var _a, _b;
44
- if (!((_a = node.nested) === null || _a === void 0 ? void 0 : _a.length))
45
- return false;
46
- return !((_b = node.nested.at(-1)) === null || _b === void 0 ? void 0 : _b.nested);
47
- }, [node.nested]);
46
+ const lastItem = (_a = childrenRefs.current) === null || _a === void 0 ? void 0 : _a.at(-1);
47
+ if (lastItem) {
48
+ const observer = new ResizeObserver(entities => entities.forEach(entity => {
49
+ if (entity.target === lastItem) {
50
+ const [{ blockSize: newHeight }] = entity.contentBoxSize;
51
+ setLastItemHeight(newHeight);
52
+ }
53
+ }));
54
+ observer.observe(lastItem);
55
+ setLastItemHeight((_b = lastItem.scrollHeight) !== null && _b !== void 0 ? _b : 0);
56
+ return () => observer.disconnect();
57
+ }
58
+ }, [state.status]);
48
59
  useEffect(() => {
49
60
  var _a;
50
61
  if (((_a = node.nested) === null || _a === void 0 ? void 0 : _a.length) && isExpanded && state.status === 'exited') {
51
62
  toggle(true);
52
63
  }
53
64
  }, [isExpanded, node.nested, state.status]);
54
- const toggleExpand = () => __awaiter(this, void 0, void 0, function* () {
65
+ const toggleExpand = () => __awaiter(void 0, void 0, void 0, function* () {
55
66
  if (node.nested && !node.nested.length && !isExpanded && onDataLoad) {
56
67
  setLoading(true);
57
68
  yield onDataLoad(node).finally(() => {
@@ -81,8 +92,8 @@ export function ExpandableTreeNode(_a) {
81
92
  return;
82
93
  }
83
94
  };
84
- return (_jsxs("div", { className: styles.expandableTreeNode, "data-test-id": dataTestId, "data-expandable": Boolean(node.nested) || undefined, "data-disabled": node.disabled || undefined, children: [_jsx(TreeNode, Object.assign({}, node, { isLoading: isLoading, parentNode: parentNode, onChevronClick: toggleExpand, onKeyDown: handleKeyDown, tabIndexAvailable: tabIndexAvailable })), node.nested && (_jsx("div", { className: styles.expandableWrap, "data-expanded": isExpanded || undefined, "data-test-id": TEST_IDS.expandable, children: showContent && (_jsxs("div", { className: styles.expandableContent, "data-test-id": TEST_IDS.expandableContent, children: [_jsx(TreeLine, { visible: showLines, halfHeight: isLineHalfHeight, "data-test-id": TEST_IDS.line }), _jsx("div", { className: styles.expandableNested, children: node.nested.map(nestedNode => {
95
+ return (_jsxs("div", { className: styles.expandableTreeNode, "data-test-id": dataTestId, "data-expandable": Boolean(node.nested) || undefined, "data-disabled": node.disabled || undefined, ref: ref, children: [_jsx(TreeNode, Object.assign({}, node, { isLoading: isLoading, parentNode: parentNode, onChevronClick: toggleExpand, onKeyDown: handleKeyDown, tabIndexAvailable: tabIndexAvailable })), node.nested && (_jsx("div", { className: styles.expandableWrap, "data-expanded": isExpanded || undefined, "data-test-id": TEST_IDS.expandable, children: showContent && (_jsxs("div", { className: styles.expandableContent, "data-test-id": TEST_IDS.expandableContent, children: [_jsx(TreeLine, { visible: showLines, "data-test-id": TEST_IDS.line, height: `calc(100% - ${lastItemHeight}px)`, className: styles.treeLine }), _jsx("div", { className: styles.expandableNested, children: node.nested.map((nestedNode, index) => {
85
96
  const parent = { id: node.id, nested: node.nested, parentNode };
86
- return (_jsx(TreeItem, { node: Object.assign(Object.assign({}, nestedNode), { disabled: node.disabled || nestedNode.disabled }), parentNode: parent }, nestedNode.id));
97
+ return (_jsx(TreeItem, { ref: el => (childrenRefs.current[index] = el), node: Object.assign(Object.assign({}, nestedNode), { disabled: node.disabled || nestedNode.disabled }), parentNode: parent }, nestedNode.id));
87
98
  }) })] })) }))] }));
88
- }
99
+ });
@@ -31,4 +31,8 @@
31
31
  .expandableNested{
32
32
  width:100%;
33
33
  min-width:0;
34
+ }
35
+
36
+ .treeLine{
37
+ padding-bottom:calc(var(--size-tree-item-line-height, 32px) / 2);
34
38
  }
@@ -4,5 +4,5 @@ type TreeItemProps = {
4
4
  parentNode?: ParentNode;
5
5
  tabIndexAvailable?: boolean;
6
6
  };
7
- export declare function TreeItem({ node, tabIndexAvailable, parentNode }: TreeItemProps): import("react/jsx-runtime").JSX.Element;
7
+ export declare const TreeItem: import("react").ForwardRefExoticComponent<TreeItemProps & import("react").RefAttributes<HTMLDivElement>>;
8
8
  export {};
@@ -1,12 +1,13 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef } from 'react';
2
3
  import { TEST_IDS } from '../../constants';
3
4
  import { ExpandableTreeNode } from '../ExpandableTreeNode';
4
5
  import { TreeNode } from '../TreeNode';
5
- export function TreeItem({ node, tabIndexAvailable, parentNode }) {
6
+ export const TreeItem = forwardRef(({ node, tabIndexAvailable, parentNode }, ref) => {
6
7
  const dataTestId = node['data-test-id'] || TEST_IDS.node;
7
8
  const nodeProps = Object.assign(Object.assign({}, node), { 'data-test-id': dataTestId });
8
9
  if (node.nested) {
9
- return _jsx(ExpandableTreeNode, { node: nodeProps, tabIndexAvailable: tabIndexAvailable, parentNode: parentNode });
10
+ return (_jsx(ExpandableTreeNode, { node: nodeProps, tabIndexAvailable: tabIndexAvailable, parentNode: parentNode, ref: ref }));
10
11
  }
11
- return _jsx(TreeNode, Object.assign({}, nodeProps, { tabIndexAvailable: tabIndexAvailable, parentNode: parentNode }));
12
- }
12
+ return _jsx(TreeNode, Object.assign({}, nodeProps, { tabIndexAvailable: tabIndexAvailable, parentNode: parentNode, ref: ref }));
13
+ });
@@ -1,7 +1,8 @@
1
+ import { CSSProperties } from 'react';
1
2
  export type TreeLineProps = {
3
+ className?: string;
2
4
  halfWidth?: boolean;
3
- halfHeight?: boolean;
4
5
  visible?: boolean;
5
6
  horizontal?: boolean;
6
- };
7
- export declare function TreeLine({ halfWidth, halfHeight, horizontal, visible }: TreeLineProps): import("react/jsx-runtime").JSX.Element;
7
+ } & Pick<CSSProperties, 'height' | 'width'>;
8
+ export declare function TreeLine({ className, halfWidth, horizontal, visible, height, width }: TreeLineProps): import("react/jsx-runtime").JSX.Element;
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ import cn from 'classnames';
2
3
  import { TEST_IDS } from '../../constants';
3
4
  import styles from './styles.module.css';
4
- export function TreeLine({ halfWidth, halfHeight, horizontal, visible }) {
5
- return (_jsx("div", { className: styles.treeLine, "data-horizontal": horizontal || undefined, "data-half-width": halfWidth || undefined, "data-half-height": halfHeight || undefined, "data-visible": visible || undefined, "data-test-id": TEST_IDS.line }));
5
+ export function TreeLine({ className, halfWidth, horizontal, visible, height, width }) {
6
+ return (_jsx("div", { className: cn(styles.treeLine, className), style: { height, width }, "data-horizontal": horizontal || undefined, "data-half-width": halfWidth || undefined, "data-visible": visible || undefined, "data-test-id": TEST_IDS.line }));
6
7
  }
@@ -15,11 +15,11 @@
15
15
  left:50%;
16
16
  transform:translateX(-50%);
17
17
  width:1px;
18
- height:calc(100% - var(--size-tree-item-height, 32px) / 2);
18
+ height:100%;
19
19
  background:var(--sys-neutral-decor-default, #dde0ea);
20
20
  }
21
- .treeLine[data-half-height]::after{
22
- height:calc(100% - var(--size-tree-item-height, 32px) / 2);
21
+ .treeLine[data-horizontal]{
22
+ min-height:var(--size-tree-item-height, 32px);
23
23
  }
24
24
  .treeLine[data-horizontal]::after{
25
25
  top:50%;
@@ -8,4 +8,4 @@ type TreeNodeComponentProps = TreeNodeProps & {
8
8
  parentNode?: ParentNode;
9
9
  tabIndexAvailable?: boolean;
10
10
  };
11
- export declare function TreeNode({ id, title, icon, expandedIcon, collapsedIcon, disabled, onClick, nested, className, onChevronClick, onKeyDown, isLoading, parentNode, tabIndexAvailable, ...rest }: TreeNodeComponentProps): import("react/jsx-runtime").JSX.Element;
11
+ export declare const TreeNode: import("react").ForwardRefExoticComponent<TreeNodeComponentProps & import("react").RefAttributes<HTMLDivElement>>;