@snack-uikit/tree 0.6.26 → 0.6.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +1 -0
- package/dist/components/Tree/Tree.js +1 -1
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +1 -0
- package/dist/contexts/TreeContext.d.ts +5 -2
- package/dist/contexts/TreeContext.js +1 -1
- package/dist/helperComponents/ExpandableTreeNode/ExpandableTreeNode.js +1 -3
- package/dist/helperComponents/TreeNode/TreeNode.js +5 -5
- package/dist/types.d.ts +3 -1
- package/package.json +3 -3
- package/src/components/Tree/Tree.tsx +1 -0
- package/src/constants.ts +1 -0
- package/src/contexts/TreeContext.tsx +10 -3
- package/src/helperComponents/ExpandableTreeNode/ExpandableTreeNode.tsx +7 -3
- package/src/helperComponents/TreeNode/TreeNode.tsx +28 -16
- package/src/types.ts +4 -1
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.28 (2024-10-16)
|
|
7
|
+
|
|
8
|
+
### Only dependencies have been changed
|
|
9
|
+
* [@snack-uikit/list@0.17.2](https://github.com/cloud-ru-tech/snack-uikit/blob/master/packages/list/CHANGELOG.md)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
## 0.6.27 (2024-10-15)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* **PDS-556:** add render fn for tree component ([6b2608e](https://github.com/cloud-ru-tech/snack-uikit/commit/6b2608ee282abe8ac039d98ca71c4365c84774f1))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
6
26
|
## 0.6.26 (2024-10-14)
|
|
7
27
|
|
|
8
28
|
### Only dependencies have been changed
|
package/README.md
CHANGED
|
@@ -55,6 +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
|
+
| showToggle | `boolean` | - | |
|
|
58
59
|
|
|
59
60
|
|
|
60
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({ 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,
|
package/dist/constants.d.ts
CHANGED
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,6 +1,9 @@
|
|
|
1
1
|
import { Dispatch, ReactNode, SetStateAction } from 'react';
|
|
2
2
|
import { OnNodeClick, ParentNode, TreeBaseProps, TreeNodeId, TreeNodeProps } from '../types';
|
|
3
|
-
type
|
|
3
|
+
type TreeContextBaseProps = TreeBaseProps & {
|
|
4
|
+
showToggle?: boolean;
|
|
5
|
+
};
|
|
6
|
+
type TreeContextProps = Omit<TreeContextBaseProps, 'onSelect' | 'onExpand' | 'onNodeClick' | 'selected' | 'selectionMode'> & {
|
|
4
7
|
onExpand(node: TreeNodeProps): void;
|
|
5
8
|
onSelect(node: Pick<TreeNodeProps, 'id' | 'nested' | 'disabled'>, parentNode?: ParentNode): void;
|
|
6
9
|
selected?: TreeNodeId[] | TreeNodeId;
|
|
@@ -16,7 +19,7 @@ type TreeContextProps = Omit<TreeBaseProps, 'onSelect' | 'onExpand' | 'onNodeCli
|
|
|
16
19
|
};
|
|
17
20
|
type TreeContextProviderProps = {
|
|
18
21
|
children: ReactNode;
|
|
19
|
-
value:
|
|
22
|
+
value: TreeContextBaseProps;
|
|
20
23
|
};
|
|
21
24
|
export declare const TreeContext: import("react").Context<TreeContextProps>;
|
|
22
25
|
export declare function TreeContextProvider({ children, value }: TreeContextProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -47,7 +47,7 @@ export function TreeContextProvider({ children, value }) {
|
|
|
47
47
|
if (!isSelectable || node.disabled)
|
|
48
48
|
return;
|
|
49
49
|
if (isSingleSelect) {
|
|
50
|
-
onSelectHandler(node.id, node);
|
|
50
|
+
onSelectHandler(node.id === selectedNodes ? undefined : node.id, node);
|
|
51
51
|
return;
|
|
52
52
|
}
|
|
53
53
|
if (Array.isArray(selectedNodes)) {
|
|
@@ -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,16 +27,16 @@ 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, 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);
|
|
34
34
|
const isExpandable = Array.isArray(nested);
|
|
35
35
|
const isExpanded = isExpandable ? expandedNodes === null || expandedNodes === void 0 ? void 0 : expandedNodes.includes(id) : undefined;
|
|
36
36
|
const nestedNodesSelection = useMemo(() => {
|
|
37
|
-
if (!nested || !
|
|
37
|
+
if (!nested || !selected)
|
|
38
38
|
return undefined;
|
|
39
|
-
return checkNestedNodesSelection(nested, selected);
|
|
39
|
+
return checkNestedNodesSelection(nested, Array.isArray(selected) ? selected : [selected]);
|
|
40
40
|
}, [nested, selected]);
|
|
41
41
|
const isSelected = (Array.isArray(selected) ? selected.includes(id) || (nestedNodesSelection === null || nestedNodesSelection === void 0 ? void 0 : nestedNodesSelection.allSelected) : selected === id) || false;
|
|
42
42
|
const isFocused = focusedNodeId === id;
|
|
@@ -139,7 +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, {}),
|
|
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: {
|
|
143
143
|
id,
|
|
144
144
|
title,
|
|
145
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;
|
|
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,6 +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;
|
|
85
|
+
showToggle?: boolean;
|
|
84
86
|
};
|
|
85
87
|
export type TreeMultiSelect = Omit<TreeCommonProps, 'selected'> & {
|
|
86
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.
|
|
7
|
+
"version": "0.6.28",
|
|
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.
|
|
37
|
+
"@snack-uikit/list": "0.17.2",
|
|
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": "
|
|
46
|
+
"gitHead": "995adf216ab5c2a02a2722d6e7ed6c928d4e0c63"
|
|
47
47
|
}
|
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',
|
|
@@ -5,7 +5,14 @@ import { SELECTION_MODE } from '../constants';
|
|
|
5
5
|
import { findAllExpandedChildNodeIds, lookupTreeForSelectedNodes } from '../helpers';
|
|
6
6
|
import { OnNodeClick, ParentNode, TreeBaseProps, TreeNodeId, TreeNodeProps } from '../types';
|
|
7
7
|
|
|
8
|
-
type
|
|
8
|
+
type TreeContextBaseProps = TreeBaseProps & {
|
|
9
|
+
showToggle?: boolean;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type TreeContextProps = Omit<
|
|
13
|
+
TreeContextBaseProps,
|
|
14
|
+
'onSelect' | 'onExpand' | 'onNodeClick' | 'selected' | 'selectionMode'
|
|
15
|
+
> & {
|
|
9
16
|
onExpand(node: TreeNodeProps): void;
|
|
10
17
|
onSelect(node: Pick<TreeNodeProps, 'id' | 'nested' | 'disabled'>, parentNode?: ParentNode): void;
|
|
11
18
|
selected?: TreeNodeId[] | TreeNodeId;
|
|
@@ -22,7 +29,7 @@ type TreeContextProps = Omit<TreeBaseProps, 'onSelect' | 'onExpand' | 'onNodeCli
|
|
|
22
29
|
|
|
23
30
|
type TreeContextProviderProps = {
|
|
24
31
|
children: ReactNode;
|
|
25
|
-
value:
|
|
32
|
+
value: TreeContextBaseProps;
|
|
26
33
|
};
|
|
27
34
|
|
|
28
35
|
export const TreeContext = createContext<TreeContextProps>({
|
|
@@ -81,7 +88,7 @@ export function TreeContextProvider({ children, value }: TreeContextProviderProp
|
|
|
81
88
|
if (!isSelectable || node.disabled) return;
|
|
82
89
|
|
|
83
90
|
if (isSingleSelect) {
|
|
84
|
-
onSelectHandler(node.id, node);
|
|
91
|
+
onSelectHandler(node.id === selectedNodes ? undefined : node.id, node);
|
|
85
92
|
return;
|
|
86
93
|
}
|
|
87
94
|
|
|
@@ -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
|
|
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,6 +58,7 @@ export function TreeNode({
|
|
|
58
58
|
focusedNodeId,
|
|
59
59
|
setFocusIndex,
|
|
60
60
|
focusableNodeIds,
|
|
61
|
+
showToggle,
|
|
61
62
|
showLines,
|
|
62
63
|
showIcons,
|
|
63
64
|
} = useTreeContext();
|
|
@@ -71,9 +72,9 @@ export function TreeNode({
|
|
|
71
72
|
const isExpanded = isExpandable ? expandedNodes?.includes(id) : undefined;
|
|
72
73
|
|
|
73
74
|
const nestedNodesSelection = useMemo(() => {
|
|
74
|
-
if (!nested || !
|
|
75
|
+
if (!nested || !selected) return undefined;
|
|
75
76
|
|
|
76
|
-
return checkNestedNodesSelection(nested, selected);
|
|
77
|
+
return checkNestedNodesSelection(nested, Array.isArray(selected) ? selected : [selected]);
|
|
77
78
|
}, [nested, selected]);
|
|
78
79
|
|
|
79
80
|
const isSelected =
|
|
@@ -221,7 +222,6 @@ export function TreeNode({
|
|
|
221
222
|
<ButtonFunction
|
|
222
223
|
size='xs'
|
|
223
224
|
icon={<ChevronRightSVG />}
|
|
224
|
-
disabled={disabled}
|
|
225
225
|
loading={isLoading}
|
|
226
226
|
onClick={onChevronClick}
|
|
227
227
|
data-expanded={isExpanded || undefined}
|
|
@@ -247,18 +247,29 @@ export function TreeNode({
|
|
|
247
247
|
data-test-id={TEST_IDS.item}
|
|
248
248
|
ref={ref}
|
|
249
249
|
>
|
|
250
|
-
{isMultiSelect && (
|
|
250
|
+
{(isMultiSelect || showToggle) && (
|
|
251
251
|
<div className={styles.treeNodeCheckboxWrap}>
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
252
|
+
{isMultiSelect && (
|
|
253
|
+
<Checkbox
|
|
254
|
+
size='s'
|
|
255
|
+
disabled={disabled}
|
|
256
|
+
checked={isSelected}
|
|
257
|
+
indeterminate={!isSelected && nestedNodesSelection?.someSelected}
|
|
258
|
+
onChange={handleSelect}
|
|
259
|
+
onClick={stopPropagationClick}
|
|
260
|
+
data-test-id={TEST_IDS.checkbox}
|
|
261
|
+
tabIndex={-1}
|
|
262
|
+
/>
|
|
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
|
+
)}
|
|
262
273
|
</div>
|
|
263
274
|
)}
|
|
264
275
|
|
|
@@ -269,7 +280,8 @@ export function TreeNode({
|
|
|
269
280
|
)}
|
|
270
281
|
|
|
271
282
|
<Typography.SansBodyM tag='div' className={styles.treeNodeTitle}>
|
|
272
|
-
<TruncateString text={title} data-test-id={TEST_IDS.title} />
|
|
283
|
+
{typeof title === 'string' && <TruncateString text={title} data-test-id={TEST_IDS.title} />}
|
|
284
|
+
{typeof title !== 'string' && title({ id, disabled, nested } as TreeNodeProps)}
|
|
273
285
|
</Typography.SansBodyM>
|
|
274
286
|
|
|
275
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;
|
|
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,6 +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;
|
|
97
|
+
showToggle?: boolean;
|
|
95
98
|
};
|
|
96
99
|
|
|
97
100
|
export type TreeMultiSelect = Omit<TreeCommonProps, 'selected'> & {
|