@snack-uikit/tree 0.6.26-preview-1b7c1817.0 → 0.6.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,26 @@
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.6.27 (2024-10-15)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **PDS-556:** add render fn for tree component ([6b2608e](https://github.com/cloud-ru-tech/snack-uikit/commit/6b2608ee282abe8ac039d98ca71c4365c84774f1))
12
+
13
+
14
+
15
+
16
+
17
+ ## 0.6.26 (2024-10-14)
18
+
19
+ ### Only dependencies have been changed
20
+ * [@snack-uikit/list@0.17.1](https://github.com/cloud-ru-tech/snack-uikit/blob/master/packages/list/CHANGELOG.md)
21
+
22
+
23
+
24
+
25
+
6
26
  ## 0.6.25 (2024-09-27)
7
27
 
8
28
  ### Only dependencies have been changed
package/README.md CHANGED
@@ -55,7 +55,7 @@ const [selectedNodes, setSelected] = useState<TreeNodeId[]>([]);
55
55
  | selectionMode | "single" \| "multi" | - | Режим выбора элементов: <br> - `Single` - одиночный выбор <br> - `Multi` - множественный выбор <br> - `undefined` - без выбора |
56
56
  | selected | `string \| string[]` | - | Состояние для выбраных элементов: <br> - При <strong>selectionMode</strong>=`Multi` - принимает массив строк <br> - При <strong>selectionMode</strong>=`Single` - принимает строку |
57
57
  | onSelect | `((selectedKeys: string[], node: TreeNodeProps) => void) \| ((selectedKey: string, node: TreeNodeProps) => void)` | - | Колбэк при изменении выбраных элементов: <br> - При <strong>selectionMode</strong>=`Multi` - возвращает массив строк <br> - При <strong>selectionMode</strong>=`Single` - возвращает строку |
58
- | showToggleInSingleMode | `boolean` | - | |
58
+ | showToggle | `boolean` | - | |
59
59
 
60
60
 
61
61
  [//]: DOCUMENTATION_SECTION_END
@@ -18,7 +18,7 @@ import styles from './styles.module.css';
18
18
  import { extractSelectableProps } from './utils';
19
19
  export function Tree(_a) {
20
20
  var { data, onNodeClick, onExpand, expandedNodes, nodeActions, parentActions, onDataLoad, showLines = true, showIcons = true, className } = _a, rest = __rest(_a, ["data", "onNodeClick", "onExpand", "expandedNodes", "nodeActions", "parentActions", "onDataLoad", "showLines", "showIcons", "className"]);
21
- return (_jsx("div", Object.assign({ className: cn(styles.tree, className), role: 'tree' }, extractSupportProps(rest), { children: _jsx(TreeContextProvider, { value: Object.assign({ showToggleInSingleMode: rest.selectionMode === 'single' && rest.showToggleInSingleMode, data,
21
+ return (_jsx("div", Object.assign({ className: cn(styles.tree, className), role: 'tree' }, extractSupportProps(rest), { children: _jsx(TreeContextProvider, { value: Object.assign({ showToggle: rest.selectionMode === 'single' && rest.showToggle, data,
22
22
  expandedNodes,
23
23
  onNodeClick,
24
24
  onExpand,
@@ -11,6 +11,7 @@ export declare const TEST_IDS: {
11
11
  item: string;
12
12
  chevron: string;
13
13
  checkbox: string;
14
+ radio: string;
14
15
  icon: string;
15
16
  title: string;
16
17
  droplistTrigger: string;
package/dist/constants.js CHANGED
@@ -11,6 +11,7 @@ export const TEST_IDS = {
11
11
  item: 'tree__node__item',
12
12
  chevron: 'tree__node__chevron',
13
13
  checkbox: 'tree__node__checkbox',
14
+ radio: 'tree__node__radio',
14
15
  icon: 'tree__node__icon',
15
16
  title: 'tree__node__title',
16
17
  droplistTrigger: 'tree__node__droplist-trigger',
@@ -1,7 +1,7 @@
1
1
  import { Dispatch, ReactNode, SetStateAction } from 'react';
2
2
  import { OnNodeClick, ParentNode, TreeBaseProps, TreeNodeId, TreeNodeProps } from '../types';
3
3
  type TreeContextBaseProps = TreeBaseProps & {
4
- showToggleInSingleMode?: boolean;
4
+ showToggle?: boolean;
5
5
  };
6
6
  type TreeContextProps = Omit<TreeContextBaseProps, 'onSelect' | 'onExpand' | 'onNodeClick' | 'selected' | 'selectionMode'> & {
7
7
  onExpand(node: TreeNodeProps): void;
@@ -52,8 +52,6 @@ export function ExpandableTreeNode(_a) {
52
52
  }
53
53
  }, [isExpanded, node.nested, state.status]);
54
54
  const toggleExpand = () => __awaiter(this, void 0, void 0, function* () {
55
- if (node.disabled)
56
- return;
57
55
  if (node.nested && !node.nested.length && !isExpanded && onDataLoad) {
58
56
  setLoading(true);
59
57
  yield onDataLoad(node).finally(() => {
@@ -85,6 +83,6 @@ export function ExpandableTreeNode(_a) {
85
83
  };
86
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 => {
87
85
  const parent = { id: node.id, nested: node.nested, parentNode };
88
- return _jsx(TreeItem, { node: nestedNode, parentNode: parent }, nestedNode.id);
86
+ return (_jsx(TreeItem, { node: Object.assign(Object.assign({}, nestedNode), { disabled: node.disabled || nestedNode.disabled }), parentNode: parent }, nestedNode.id));
89
87
  }) })] })) }))] }));
90
88
  }
@@ -14,7 +14,7 @@ import cn from 'classnames';
14
14
  import { useEffect, useMemo, useRef, useState } from 'react';
15
15
  import { ButtonFunction } from '@snack-uikit/button';
16
16
  import { ChevronRightSVG, FileSVG, FolderOpenSVG, FolderSVG } from '@snack-uikit/icons';
17
- import { Checkbox } from '@snack-uikit/toggles';
17
+ import { Checkbox, Radio } from '@snack-uikit/toggles';
18
18
  import { TruncateString } from '@snack-uikit/truncate-string';
19
19
  import { Typography } from '@snack-uikit/typography';
20
20
  import { extractSupportProps } from '@snack-uikit/utils';
@@ -27,7 +27,7 @@ import styles from './styles.module.css';
27
27
  import { stopPropagationClick } from './utils';
28
28
  export function TreeNode(_a) {
29
29
  var { id, title, icon = _jsx(FileSVG, { size: 24 }), expandedIcon = _jsx(FolderOpenSVG, { size: 24 }), collapsedIcon = _jsx(FolderSVG, { size: 24 }), disabled, onClick, nested, className, onChevronClick, onKeyDown, isLoading, parentNode, tabIndexAvailable } = _a, rest = __rest(_a, ["id", "title", "icon", "expandedIcon", "collapsedIcon", "disabled", "onClick", "nested", "className", "onChevronClick", "onKeyDown", "isLoading", "parentNode", "tabIndexAvailable"]);
30
- const { isMultiSelect, isSelectable, onNodeClick, selected, expandedNodes, onSelect, nodeActions, parentActions, setFocusPosition, resetFocusPosition, focusedNodeId, setFocusIndex, focusableNodeIds, showToggleInSingleMode, showLines, showIcons, } = useTreeContext();
30
+ const { isMultiSelect, isSelectable, onNodeClick, selected, expandedNodes, onSelect, nodeActions, parentActions, setFocusPosition, resetFocusPosition, focusedNodeId, setFocusIndex, focusableNodeIds, showToggle, showLines, showIcons, } = useTreeContext();
31
31
  const [isDroplistOpen, setDroplistOpen] = useState(false);
32
32
  const [isDroplistTriggerFocused, setFocusDroplistTrigger] = useState(false);
33
33
  const ref = useRef(null);
@@ -139,8 +139,7 @@ export function TreeNode(_a) {
139
139
  }
140
140
  };
141
141
  const getNodeActions = Boolean(nested) ? parentActions : nodeActions;
142
- return (_jsxs("div", Object.assign({ role: 'presentation', className: cn(styles.treeNode, className) }, extractSupportProps(rest), { "data-node-id": id, children: [parentNode && (_jsx(TreeLine, { halfWidth: Boolean(nested), horizontal: true, visible: showLines, "data-test-id": TEST_IDS.line })), !parentNode && !Boolean(nested) && _jsx(TreeLine, { visible: false }), isExpandable && (_jsx(ButtonFunction, { size: 'xs', icon: _jsx(ChevronRightSVG, {}), disabled: disabled, loading: isLoading, onClick: onChevronClick, "data-expanded": isExpanded || undefined, className: styles.treeNodeExpandButton, tabIndex: -1, "data-test-id": TEST_IDS.chevron })), _jsxs("div", { role: 'treeitem', "aria-expanded": isExpanded, "aria-selected": isSelectable ? isSelected : undefined, "aria-disabled": disabled, "data-multiselect": isMultiSelect || undefined, "data-droplist-active": isDroplistOpen || isDroplistTriggerFocused || undefined, onClick: handleClick, onKeyDown: handleKeyDown, onFocus: handleFocus, onBlur: resetFocusPosition, tabIndex: tabIndexAvailable ? 0 : -1, className: styles.treeNodeContent, "data-test-id": TEST_IDS.item, ref: ref, children: [isMultiSelect ||
143
- (showToggleInSingleMode && (_jsx("div", { className: styles.treeNodeCheckboxWrap, children: _jsx(Checkbox, { size: 's', disabled: disabled, checked: isSelected, indeterminate: !isSelected && (nestedNodesSelection === null || nestedNodesSelection === void 0 ? void 0 : nestedNodesSelection.someSelected), onChange: handleSelect, onClick: stopPropagationClick, "data-test-id": TEST_IDS.checkbox, tabIndex: -1 }) }))), treeNodeIcon && (_jsx("div", { className: styles.treeNodeIcon, "data-test-id": TEST_IDS.icon, children: treeNodeIcon })), _jsx(Typography.SansBodyM, { tag: 'div', className: styles.treeNodeTitle, children: typeof title === 'string' ? (_jsx(TruncateString, { text: title, "data-test-id": TEST_IDS.title })) : (title(Object.assign({ id, title, onClick, className, disabled }, extractSupportProps(rest)))) }), getNodeActions && (_jsx(TreeNodeActions, { getNodeActions: getNodeActions, node: {
142
+ return (_jsxs("div", Object.assign({ role: 'presentation', className: cn(styles.treeNode, className) }, extractSupportProps(rest), { "data-node-id": id, children: [parentNode && (_jsx(TreeLine, { halfWidth: Boolean(nested), horizontal: true, visible: showLines, "data-test-id": TEST_IDS.line })), !parentNode && !Boolean(nested) && _jsx(TreeLine, { visible: false }), isExpandable && (_jsx(ButtonFunction, { size: 'xs', icon: _jsx(ChevronRightSVG, {}), loading: isLoading, onClick: onChevronClick, "data-expanded": isExpanded || undefined, className: styles.treeNodeExpandButton, tabIndex: -1, "data-test-id": TEST_IDS.chevron })), _jsxs("div", { role: 'treeitem', "aria-expanded": isExpanded, "aria-selected": isSelectable ? isSelected : undefined, "aria-disabled": disabled, "data-multiselect": isMultiSelect || undefined, "data-droplist-active": isDroplistOpen || isDroplistTriggerFocused || undefined, onClick: handleClick, onKeyDown: handleKeyDown, onFocus: handleFocus, onBlur: resetFocusPosition, tabIndex: tabIndexAvailable ? 0 : -1, className: styles.treeNodeContent, "data-test-id": TEST_IDS.item, ref: ref, children: [(isMultiSelect || showToggle) && (_jsxs("div", { className: styles.treeNodeCheckboxWrap, children: [isMultiSelect && (_jsx(Checkbox, { size: 's', disabled: disabled, checked: isSelected, indeterminate: !isSelected && (nestedNodesSelection === null || nestedNodesSelection === void 0 ? void 0 : nestedNodesSelection.someSelected), onChange: handleSelect, onClick: stopPropagationClick, "data-test-id": TEST_IDS.checkbox, tabIndex: -1 })), showToggle && (_jsx(Radio, { size: 's', checked: isSelected, disabled: disabled || (!isSelected && (nestedNodesSelection === null || nestedNodesSelection === void 0 ? void 0 : nestedNodesSelection.someSelected)), "data-test-id": TEST_IDS.radio, tabIndex: -1 }))] })), treeNodeIcon && (_jsx("div", { className: styles.treeNodeIcon, "data-test-id": TEST_IDS.icon, children: treeNodeIcon })), _jsxs(Typography.SansBodyM, { tag: 'div', className: styles.treeNodeTitle, children: [typeof title === 'string' && _jsx(TruncateString, { text: title, "data-test-id": TEST_IDS.title }), typeof title !== 'string' && title({ id, disabled, nested })] }), getNodeActions && (_jsx(TreeNodeActions, { getNodeActions: getNodeActions, node: {
144
143
  id,
145
144
  title,
146
145
  disabled,
package/dist/types.d.ts CHANGED
@@ -8,7 +8,7 @@ export type BaseTreeNode = WithSupportProps<{
8
8
  /** Идентификатор элемента */
9
9
  id: TreeNodeId;
10
10
  /** Имя элемента */
11
- title: string | ((value: BaseTreeNode) => ReactNode);
11
+ title: string | ((value: Pick<BaseTreeNode, 'id' | 'disabled'>) => ReactNode);
12
12
  /** Является ли элемент деактивированным */
13
13
  disabled?: boolean;
14
14
  /** Обработчик клика по элементу */
@@ -30,6 +30,7 @@ export type ParentTreeNode = BaseTreeNode & {
30
30
  collapsedIcon?: ReactNode;
31
31
  /** Вложенные элементы дерева */
32
32
  nested: (ChildTreeNode | ParentTreeNode)[];
33
+ title: string | ((value: Pick<ParentTreeNode, 'id' | 'disabled' | 'nested'>) => ReactNode);
33
34
  };
34
35
  export type TreeNodeProps = ChildTreeNode | ParentTreeNode;
35
36
  export type ParentNode = Pick<TreeNodeProps, 'id' | 'nested'> & {
@@ -81,7 +82,7 @@ export type TreeSingleSelect = Omit<TreeCommonProps, 'selected'> & {
81
82
  selected?: TreeNodeId;
82
83
  /** <br> - При <strong>selectionMode</strong>=`Single` - возвращает строку */
83
84
  onSelect?(selectedKey: TreeNodeId, node: TreeNodeProps): void;
84
- showToggleInSingleMode?: boolean;
85
+ showToggle?: boolean;
85
86
  };
86
87
  export type TreeMultiSelect = Omit<TreeCommonProps, 'selected'> & {
87
88
  selectionMode: 'multi';
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public"
5
5
  },
6
6
  "title": "Tree",
7
- "version": "0.6.26-preview-1b7c1817.0",
7
+ "version": "0.6.27",
8
8
  "sideEffects": [
9
9
  "*.css",
10
10
  "*.woff",
@@ -34,7 +34,7 @@
34
34
  "dependencies": {
35
35
  "@snack-uikit/button": "0.17.5",
36
36
  "@snack-uikit/icons": "0.22.1",
37
- "@snack-uikit/list": "0.17.0",
37
+ "@snack-uikit/list": "0.17.1",
38
38
  "@snack-uikit/toggles": "0.10.4",
39
39
  "@snack-uikit/truncate-string": "0.4.24",
40
40
  "@snack-uikit/typography": "0.6.3",
@@ -43,5 +43,5 @@
43
43
  "react-transition-state": "2.1.1",
44
44
  "uncontrollable": "8.0.4"
45
45
  },
46
- "gitHead": "fcec27b4b06ab5c700cc50bb4c0e9520aba50cb7"
46
+ "gitHead": "8a9406a0dc468467c41fbf024fc6ac9a0c2f3452"
47
47
  }
@@ -27,7 +27,7 @@ export function Tree({
27
27
  <div className={cn(styles.tree, className)} role='tree' {...extractSupportProps(rest)}>
28
28
  <TreeContextProvider
29
29
  value={{
30
- showToggleInSingleMode: rest.selectionMode === 'single' && rest.showToggleInSingleMode,
30
+ showToggle: rest.selectionMode === 'single' && rest.showToggle,
31
31
  data,
32
32
  expandedNodes,
33
33
  onNodeClick,
package/src/constants.ts CHANGED
@@ -13,6 +13,7 @@ export const TEST_IDS = {
13
13
  item: 'tree__node__item',
14
14
  chevron: 'tree__node__chevron',
15
15
  checkbox: 'tree__node__checkbox',
16
+ radio: 'tree__node__radio',
16
17
  icon: 'tree__node__icon',
17
18
  title: 'tree__node__title',
18
19
  droplistTrigger: 'tree__node__droplist-trigger',
@@ -6,7 +6,7 @@ import { findAllExpandedChildNodeIds, lookupTreeForSelectedNodes } from '../help
6
6
  import { OnNodeClick, ParentNode, TreeBaseProps, TreeNodeId, TreeNodeProps } from '../types';
7
7
 
8
8
  type TreeContextBaseProps = TreeBaseProps & {
9
- showToggleInSingleMode?: boolean;
9
+ showToggle?: boolean;
10
10
  };
11
11
 
12
12
  type TreeContextProps = Omit<
@@ -48,8 +48,6 @@ export function ExpandableTreeNode({
48
48
  }, [isExpanded, node.nested, state.status]);
49
49
 
50
50
  const toggleExpand = async () => {
51
- if (node.disabled) return;
52
-
53
51
  if (node.nested && !node.nested.length && !isExpanded && onDataLoad) {
54
52
  setLoading(true);
55
53
 
@@ -114,7 +112,13 @@ export function ExpandableTreeNode({
114
112
  {node.nested.map(nestedNode => {
115
113
  const parent: ParentNode = { id: node.id, nested: node.nested, parentNode };
116
114
 
117
- return <TreeItem key={nestedNode.id} node={nestedNode} parentNode={parent} />;
115
+ return (
116
+ <TreeItem
117
+ key={nestedNode.id}
118
+ node={{ ...nestedNode, disabled: node.disabled || nestedNode.disabled }}
119
+ parentNode={parent}
120
+ />
121
+ );
118
122
  })}
119
123
  </div>
120
124
  </div>
@@ -3,7 +3,7 @@ import { FocusEvent, KeyboardEventHandler, MouseEventHandler, useEffect, useMemo
3
3
 
4
4
  import { ButtonFunction } from '@snack-uikit/button';
5
5
  import { ChevronRightSVG, FileSVG, FolderOpenSVG, FolderSVG } from '@snack-uikit/icons';
6
- import { Checkbox } from '@snack-uikit/toggles';
6
+ import { Checkbox, Radio } from '@snack-uikit/toggles';
7
7
  import { TruncateString } from '@snack-uikit/truncate-string';
8
8
  import { Typography } from '@snack-uikit/typography';
9
9
  import { extractSupportProps } from '@snack-uikit/utils';
@@ -58,7 +58,7 @@ export function TreeNode({
58
58
  focusedNodeId,
59
59
  setFocusIndex,
60
60
  focusableNodeIds,
61
- showToggleInSingleMode,
61
+ showToggle,
62
62
  showLines,
63
63
  showIcons,
64
64
  } = useTreeContext();
@@ -222,7 +222,6 @@ export function TreeNode({
222
222
  <ButtonFunction
223
223
  size='xs'
224
224
  icon={<ChevronRightSVG />}
225
- disabled={disabled}
226
225
  loading={isLoading}
227
226
  onClick={onChevronClick}
228
227
  data-expanded={isExpanded || undefined}
@@ -248,9 +247,9 @@ export function TreeNode({
248
247
  data-test-id={TEST_IDS.item}
249
248
  ref={ref}
250
249
  >
251
- {isMultiSelect ||
252
- (showToggleInSingleMode && (
253
- <div className={styles.treeNodeCheckboxWrap}>
250
+ {(isMultiSelect || showToggle) && (
251
+ <div className={styles.treeNodeCheckboxWrap}>
252
+ {isMultiSelect && (
254
253
  <Checkbox
255
254
  size='s'
256
255
  disabled={disabled}
@@ -261,8 +260,18 @@ export function TreeNode({
261
260
  data-test-id={TEST_IDS.checkbox}
262
261
  tabIndex={-1}
263
262
  />
264
- </div>
265
- ))}
263
+ )}
264
+ {showToggle && (
265
+ <Radio
266
+ size='s'
267
+ checked={isSelected}
268
+ disabled={disabled || (!isSelected && nestedNodesSelection?.someSelected)}
269
+ data-test-id={TEST_IDS.radio}
270
+ tabIndex={-1}
271
+ />
272
+ )}
273
+ </div>
274
+ )}
266
275
 
267
276
  {treeNodeIcon && (
268
277
  <div className={styles.treeNodeIcon} data-test-id={TEST_IDS.icon}>
@@ -271,11 +280,8 @@ export function TreeNode({
271
280
  )}
272
281
 
273
282
  <Typography.SansBodyM tag='div' className={styles.treeNodeTitle}>
274
- {typeof title === 'string' ? (
275
- <TruncateString text={title} data-test-id={TEST_IDS.title} />
276
- ) : (
277
- title({ id, title, onClick, className, disabled, ...extractSupportProps(rest) })
278
- )}
283
+ {typeof title === 'string' && <TruncateString text={title} data-test-id={TEST_IDS.title} />}
284
+ {typeof title !== 'string' && title({ id, disabled, nested } as TreeNodeProps)}
279
285
  </Typography.SansBodyM>
280
286
 
281
287
  {getNodeActions && (
package/src/types.ts CHANGED
@@ -13,7 +13,7 @@ export type BaseTreeNode = WithSupportProps<{
13
13
  /** Идентификатор элемента */
14
14
  id: TreeNodeId;
15
15
  /** Имя элемента */
16
- title: string | ((value: BaseTreeNode) => ReactNode);
16
+ title: string | ((value: Pick<BaseTreeNode, 'id' | 'disabled'>) => ReactNode);
17
17
  /** Является ли элемент деактивированным */
18
18
  disabled?: boolean;
19
19
  /** Обработчик клика по элементу */
@@ -37,6 +37,8 @@ export type ParentTreeNode = BaseTreeNode & {
37
37
  collapsedIcon?: ReactNode;
38
38
  /** Вложенные элементы дерева */
39
39
  nested: (ChildTreeNode | ParentTreeNode)[];
40
+
41
+ title: string | ((value: Pick<ParentTreeNode, 'id' | 'disabled' | 'nested'>) => ReactNode);
40
42
  };
41
43
 
42
44
  export type TreeNodeProps = ChildTreeNode | ParentTreeNode;
@@ -92,7 +94,7 @@ export type TreeSingleSelect = Omit<TreeCommonProps, 'selected'> & {
92
94
  selected?: TreeNodeId;
93
95
  /** <br> - При <strong>selectionMode</strong>=`Single` - возвращает строку */
94
96
  onSelect?(selectedKey: TreeNodeId, node: TreeNodeProps): void;
95
- showToggleInSingleMode?: boolean;
97
+ showToggle?: boolean;
96
98
  };
97
99
 
98
100
  export type TreeMultiSelect = Omit<TreeCommonProps, 'selected'> & {