@snack-uikit/tree 0.4.0 → 0.5.0
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 +11 -0
- package/README.md +1 -1
- package/dist/components/Tree/Tree.d.ts +1 -1
- package/dist/components/Tree/Tree.js +4 -8
- package/dist/components/Tree/utils.d.ts +2 -0
- package/dist/components/Tree/utils.js +20 -0
- package/dist/contexts/TreeContext.d.ts +1 -0
- package/dist/contexts/TreeContext.js +5 -2
- package/dist/helperComponents/TreeNode/TreeNode.js +2 -2
- package/dist/types.d.ts +7 -1
- package/package.json +2 -2
- package/src/components/Tree/Tree.tsx +3 -12
- package/src/components/Tree/utils.ts +26 -0
- package/src/contexts/TreeContext.tsx +6 -2
- package/src/helperComponents/TreeNode/TreeNode.tsx +2 -1
- package/src/types.ts +8 -1
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.5.0 (2024-01-18)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **FF-4126:** selectionMode is optional, and tree may be unselectable ([1540e26](https://github.com/cloud-ru-tech/snack-uikit/commit/1540e266b9351a92f1619c399a904fd6bd25518f))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
6
17
|
# 0.4.0 (2024-01-11)
|
|
7
18
|
|
|
8
19
|
|
package/README.md
CHANGED
|
@@ -43,7 +43,6 @@ const [selectedNodes, setSelected] = useState<TreeNodeId[]>([]);
|
|
|
43
43
|
| name | type | default value | description |
|
|
44
44
|
|------|------|---------------|-------------|
|
|
45
45
|
| data* | `TreeNodeProps[]` | - | Данные для отрисовки |
|
|
46
|
-
| selectionMode | "single" \| "multi" | - | Режим выбора элементов: <br> - `Single` - одиночный выбор <br> - `Multi` - множественный выбор |
|
|
47
46
|
| onNodeClick | `OnNodeClick` | - | Обработчик клика по элементу дерева |
|
|
48
47
|
| expandedNodes | `string[]` | - | Состояние для раскрытых элементов |
|
|
49
48
|
| onExpand | `(expandedKeys: string[], nodeId: string) => void` | - | Колбэк при раскрытии/закрытии элементов |
|
|
@@ -53,6 +52,7 @@ const [selectedNodes, setSelected] = useState<TreeNodeId[]>([]);
|
|
|
53
52
|
| showIcons | `boolean` | true | Флаг отвечающий за отображение иконок у элементов дерева |
|
|
54
53
|
| showLines | `boolean` | true | Флаг отвечающий за отображение линий вложенности |
|
|
55
54
|
| className | `string` | - | CSS-класс |
|
|
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
58
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { WithSupportProps } from '@snack-uikit/utils';
|
|
2
2
|
import { TreeBaseProps } from '../../types';
|
|
3
3
|
export type TreeProps = WithSupportProps<TreeBaseProps>;
|
|
4
|
-
export declare function Tree({ data,
|
|
4
|
+
export declare function Tree({ data, onNodeClick, onExpand, expandedNodes, nodeActions, parentActions, onDataLoad, showLines, showIcons, className, ...rest }: TreeProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -15,13 +15,10 @@ import { extractSupportProps } from '@snack-uikit/utils';
|
|
|
15
15
|
import { TreeContextProvider } from '../../contexts/TreeContext';
|
|
16
16
|
import { TreeItem } from '../../helperComponents';
|
|
17
17
|
import styles from './styles.module.css';
|
|
18
|
+
import { extractSelectableProps } from './utils';
|
|
18
19
|
export function Tree(_a) {
|
|
19
|
-
var { data,
|
|
20
|
-
return (_jsx("div", Object.assign({ className: cn(styles.tree, className), role: 'tree' }, extractSupportProps(rest), { children: _jsx(TreeContextProvider, { value: {
|
|
21
|
-
data,
|
|
22
|
-
selected: selected,
|
|
23
|
-
selectionMode: selectionMode,
|
|
24
|
-
onSelect: onSelect,
|
|
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,
|
|
25
22
|
expandedNodes,
|
|
26
23
|
onNodeClick,
|
|
27
24
|
onExpand,
|
|
@@ -29,6 +26,5 @@ export function Tree(_a) {
|
|
|
29
26
|
parentActions,
|
|
30
27
|
onDataLoad,
|
|
31
28
|
showLines,
|
|
32
|
-
showIcons,
|
|
33
|
-
}, children: data.map((node, index) => (_jsx(TreeItem, { node: node, tabIndexAvailable: index === 0 || index === data.length - 1 }, node.id))) }) })));
|
|
29
|
+
showIcons }, extractSelectableProps(rest)), children: data.map((node, index) => (_jsx(TreeItem, { node: node, tabIndexAvailable: index === 0 || index === data.length - 1 }, node.id))) }) })));
|
|
34
30
|
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { TreeBaseProps, TreeMultiSelect, TreeSingleSelect, TreeView } from '../../types';
|
|
2
|
+
export declare function extractSelectableProps({ selectionMode, selected, onSelect, }: Pick<TreeBaseProps, 'selectionMode' | 'selected' | 'onSelect'>): Pick<TreeSingleSelect, "selected" | "selectionMode" | "onSelect"> | Pick<TreeMultiSelect, "selected" | "selectionMode" | "onSelect"> | Pick<TreeView, "selected" | "selectionMode" | "onSelect">;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SELECTION_MODE } from '../../constants';
|
|
2
|
+
export function extractSelectableProps({ selectionMode, selected, onSelect, }) {
|
|
3
|
+
switch (selectionMode) {
|
|
4
|
+
case SELECTION_MODE.Single:
|
|
5
|
+
return {
|
|
6
|
+
selectionMode,
|
|
7
|
+
selected,
|
|
8
|
+
onSelect,
|
|
9
|
+
};
|
|
10
|
+
case SELECTION_MODE.Multi:
|
|
11
|
+
return {
|
|
12
|
+
selectionMode,
|
|
13
|
+
selected,
|
|
14
|
+
onSelect,
|
|
15
|
+
};
|
|
16
|
+
case undefined:
|
|
17
|
+
default:
|
|
18
|
+
return {};
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -7,6 +7,7 @@ type TreeContextProps = Omit<TreeBaseProps, 'onSelect' | 'onExpand' | 'onNodeCli
|
|
|
7
7
|
onNodeClick: OnNodeClick;
|
|
8
8
|
isMultiSelect: boolean;
|
|
9
9
|
isSingleSelect: boolean;
|
|
10
|
+
isSelectable: boolean;
|
|
10
11
|
focusedNodeId?: TreeNodeId;
|
|
11
12
|
setFocusPosition(id: TreeNodeId): void;
|
|
12
13
|
resetFocusPosition(): void;
|
|
@@ -29,11 +29,13 @@ export const TreeContext = createContext({
|
|
|
29
29
|
focusableNodeIds: [],
|
|
30
30
|
isMultiSelect: false,
|
|
31
31
|
isSingleSelect: false,
|
|
32
|
+
isSelectable: false,
|
|
32
33
|
});
|
|
33
34
|
export function TreeContextProvider({ children, value }) {
|
|
34
35
|
const { onNodeClick: onNodeClickProp, onExpand: onExpandProp, onSelect: onSelectProp, selectionMode, data } = value, props = __rest(value, ["onNodeClick", "onExpand", "onSelect", "selectionMode", "data"]);
|
|
35
36
|
const isMultiSelect = selectionMode === SELECTION_MODE.Multi;
|
|
36
37
|
const isSingleSelect = selectionMode === SELECTION_MODE.Single;
|
|
38
|
+
const isSelectable = Boolean(selectionMode);
|
|
37
39
|
const [expandedNodes, onExpandHandler] = useUncontrolledProp(value.expandedNodes, value.expandedNodes || [], onExpandProp);
|
|
38
40
|
const onExpand = useCallback(node => {
|
|
39
41
|
const isExpanded = expandedNodes.includes(node.id);
|
|
@@ -42,7 +44,7 @@ export function TreeContextProvider({ children, value }) {
|
|
|
42
44
|
}, [expandedNodes, onExpandHandler]);
|
|
43
45
|
const [selectedNodes, onSelectHandler] = useUncontrolledProp(value.selected, [], onSelectProp);
|
|
44
46
|
const onSelect = useCallback((node, parentNode) => {
|
|
45
|
-
if (node.disabled)
|
|
47
|
+
if (!isSelectable || node.disabled)
|
|
46
48
|
return;
|
|
47
49
|
if (isSingleSelect) {
|
|
48
50
|
onSelectHandler(node.id, node);
|
|
@@ -52,7 +54,7 @@ export function TreeContextProvider({ children, value }) {
|
|
|
52
54
|
const updatedSelectedNodes = lookupTreeForSelectedNodes({ node, parentNode, selectedNodes });
|
|
53
55
|
onSelectHandler(updatedSelectedNodes, node);
|
|
54
56
|
}
|
|
55
|
-
}, [isSingleSelect, onSelectHandler, selectedNodes]);
|
|
57
|
+
}, [isSingleSelect, onSelectHandler, selectedNodes, isSelectable]);
|
|
56
58
|
const focusableNodeIds = useMemo(() => {
|
|
57
59
|
if (!(expandedNodes === null || expandedNodes === void 0 ? void 0 : expandedNodes.length)) {
|
|
58
60
|
return data.map(node => node.id);
|
|
@@ -86,6 +88,7 @@ export function TreeContextProvider({ children, value }) {
|
|
|
86
88
|
}, [isSingleSelect, onSelect, onNodeClickProp]);
|
|
87
89
|
return (_jsx(TreeContext.Provider, { value: Object.assign(Object.assign({}, props), { data, selected: selectedNodes, isSingleSelect,
|
|
88
90
|
isMultiSelect,
|
|
91
|
+
isSelectable,
|
|
89
92
|
expandedNodes,
|
|
90
93
|
onExpand,
|
|
91
94
|
onSelect,
|
|
@@ -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, 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, showLines, showIcons, } = useTreeContext();
|
|
31
31
|
const [isDroplistOpen, setDroplistOpen] = useState(false);
|
|
32
32
|
const [isDroplistTriggerFocused, setFocusDroplistTrigger] = useState(false);
|
|
33
33
|
const ref = useRef(null);
|
|
@@ -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, {}), 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": isSelected, "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 && (_jsx("div", { className: styles.treeNodeCheckboxWrap, children: _jsx(Checkbox, { size: 's', disabled: disabled, checked: !disabled && isSelected, indeterminate: !disabled && !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: _jsx(TruncateString, { text: title, "data-test-id": TEST_IDS.title }) }), 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, {}), 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 && (_jsx("div", { className: styles.treeNodeCheckboxWrap, children: _jsx(Checkbox, { size: 's', disabled: disabled, checked: !disabled && isSelected, indeterminate: !disabled && !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: _jsx(TruncateString, { text: title, "data-test-id": TEST_IDS.title }) }), getNodeActions && (_jsx(TreeNodeActions, { getNodeActions: getNodeActions, node: {
|
|
143
143
|
id,
|
|
144
144
|
title,
|
|
145
145
|
disabled,
|
package/dist/types.d.ts
CHANGED
|
@@ -43,6 +43,7 @@ export type TreeCommonProps = {
|
|
|
43
43
|
* Режим выбора элементов:
|
|
44
44
|
* <br> - `Single` - одиночный выбор
|
|
45
45
|
* <br> - `Multi` - множественный выбор
|
|
46
|
+
* <br> - `undefined` - без выбора
|
|
46
47
|
*/
|
|
47
48
|
selectionMode?: SelectionMode;
|
|
48
49
|
/** Обработчик клика по элементу дерева */
|
|
@@ -68,6 +69,11 @@ export type TreeCommonProps = {
|
|
|
68
69
|
showLines?: boolean;
|
|
69
70
|
className?: string;
|
|
70
71
|
};
|
|
72
|
+
export type TreeView = Omit<TreeCommonProps, 'selected' | 'selectionMode'> & {
|
|
73
|
+
selectionMode?: never;
|
|
74
|
+
selected?: never;
|
|
75
|
+
onSelect?: never;
|
|
76
|
+
};
|
|
71
77
|
export type TreeSingleSelect = Omit<TreeCommonProps, 'selected'> & {
|
|
72
78
|
selectionMode: 'single';
|
|
73
79
|
/** Состояние для выбраного элемента */
|
|
@@ -89,4 +95,4 @@ export type TreeMultiSelect = Omit<TreeCommonProps, 'selected'> & {
|
|
|
89
95
|
*/
|
|
90
96
|
onSelect?(selectedKeys: TreeNodeId[], node: TreeNodeProps): void;
|
|
91
97
|
};
|
|
92
|
-
export type TreeBaseProps = TreeMultiSelect | TreeSingleSelect;
|
|
98
|
+
export type TreeBaseProps = TreeView | TreeMultiSelect | TreeSingleSelect;
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
6
|
"title": "Tree",
|
|
7
|
-
"version": "0.
|
|
7
|
+
"version": "0.5.0",
|
|
8
8
|
"sideEffects": [
|
|
9
9
|
"*.css",
|
|
10
10
|
"*.woff",
|
|
@@ -43,5 +43,5 @@
|
|
|
43
43
|
"react-transition-state": "2.1.1",
|
|
44
44
|
"uncontrollable": "8.0.4"
|
|
45
45
|
},
|
|
46
|
-
"gitHead": "
|
|
46
|
+
"gitHead": "4b0eba6c0cbe808bf637fc77c2b25666e15668e3"
|
|
47
47
|
}
|
|
@@ -2,19 +2,16 @@ import cn from 'classnames';
|
|
|
2
2
|
|
|
3
3
|
import { extractSupportProps, WithSupportProps } from '@snack-uikit/utils';
|
|
4
4
|
|
|
5
|
-
import { SELECTION_MODE } from '../../constants';
|
|
6
5
|
import { TreeContextProvider } from '../../contexts/TreeContext';
|
|
7
6
|
import { TreeItem } from '../../helperComponents';
|
|
8
|
-
import {
|
|
7
|
+
import { TreeBaseProps } from '../../types';
|
|
9
8
|
import styles from './styles.module.scss';
|
|
9
|
+
import { extractSelectableProps } from './utils';
|
|
10
10
|
|
|
11
11
|
export type TreeProps = WithSupportProps<TreeBaseProps>;
|
|
12
12
|
|
|
13
13
|
export function Tree({
|
|
14
14
|
data,
|
|
15
|
-
selected,
|
|
16
|
-
selectionMode,
|
|
17
|
-
onSelect,
|
|
18
15
|
onNodeClick,
|
|
19
16
|
onExpand,
|
|
20
17
|
expandedNodes,
|
|
@@ -31,13 +28,6 @@ export function Tree({
|
|
|
31
28
|
<TreeContextProvider
|
|
32
29
|
value={{
|
|
33
30
|
data,
|
|
34
|
-
selected: selected as SelectionMode extends typeof SELECTION_MODE.Single ? TreeNodeId : TreeNodeId[],
|
|
35
|
-
selectionMode: selectionMode as typeof selected extends string
|
|
36
|
-
? typeof SELECTION_MODE.Single
|
|
37
|
-
: typeof SELECTION_MODE.Multi,
|
|
38
|
-
onSelect: onSelect as SelectionMode extends typeof SELECTION_MODE.Single
|
|
39
|
-
? TreeSingleSelect['onSelect']
|
|
40
|
-
: TreeMultiSelect['onSelect'],
|
|
41
31
|
expandedNodes,
|
|
42
32
|
onNodeClick,
|
|
43
33
|
onExpand,
|
|
@@ -46,6 +36,7 @@ export function Tree({
|
|
|
46
36
|
onDataLoad,
|
|
47
37
|
showLines,
|
|
48
38
|
showIcons,
|
|
39
|
+
...extractSelectableProps(rest),
|
|
49
40
|
}}
|
|
50
41
|
>
|
|
51
42
|
{data.map((node, index) => (
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { SELECTION_MODE } from '../../constants';
|
|
2
|
+
import { TreeBaseProps, TreeMultiSelect, TreeSingleSelect, TreeView } from '../../types';
|
|
3
|
+
|
|
4
|
+
export function extractSelectableProps({
|
|
5
|
+
selectionMode,
|
|
6
|
+
selected,
|
|
7
|
+
onSelect,
|
|
8
|
+
}: Pick<TreeBaseProps, 'selectionMode' | 'selected' | 'onSelect'>) {
|
|
9
|
+
switch (selectionMode) {
|
|
10
|
+
case SELECTION_MODE.Single:
|
|
11
|
+
return {
|
|
12
|
+
selectionMode,
|
|
13
|
+
selected,
|
|
14
|
+
onSelect,
|
|
15
|
+
} as Pick<TreeSingleSelect, 'selectionMode' | 'selected' | 'onSelect'>;
|
|
16
|
+
case SELECTION_MODE.Multi:
|
|
17
|
+
return {
|
|
18
|
+
selectionMode,
|
|
19
|
+
selected,
|
|
20
|
+
onSelect,
|
|
21
|
+
} as Pick<TreeMultiSelect, 'selectionMode' | 'selected' | 'onSelect'>;
|
|
22
|
+
case undefined:
|
|
23
|
+
default:
|
|
24
|
+
return {} as Pick<TreeView, 'selectionMode' | 'selected' | 'onSelect'>;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -12,6 +12,7 @@ type TreeContextProps = Omit<TreeBaseProps, 'onSelect' | 'onExpand' | 'onNodeCli
|
|
|
12
12
|
onNodeClick: OnNodeClick;
|
|
13
13
|
isMultiSelect: boolean;
|
|
14
14
|
isSingleSelect: boolean;
|
|
15
|
+
isSelectable: boolean;
|
|
15
16
|
focusedNodeId?: TreeNodeId;
|
|
16
17
|
setFocusPosition(id: TreeNodeId): void;
|
|
17
18
|
resetFocusPosition(): void;
|
|
@@ -39,6 +40,7 @@ export const TreeContext = createContext<TreeContextProps>({
|
|
|
39
40
|
focusableNodeIds: [],
|
|
40
41
|
isMultiSelect: false,
|
|
41
42
|
isSingleSelect: false,
|
|
43
|
+
isSelectable: false,
|
|
42
44
|
});
|
|
43
45
|
|
|
44
46
|
export function TreeContextProvider({ children, value }: TreeContextProviderProps) {
|
|
@@ -53,6 +55,7 @@ export function TreeContextProvider({ children, value }: TreeContextProviderProp
|
|
|
53
55
|
|
|
54
56
|
const isMultiSelect = selectionMode === SELECTION_MODE.Multi;
|
|
55
57
|
const isSingleSelect = selectionMode === SELECTION_MODE.Single;
|
|
58
|
+
const isSelectable = Boolean(selectionMode);
|
|
56
59
|
|
|
57
60
|
const [expandedNodes, onExpandHandler] = useUncontrolledProp<TreeNodeId[]>(
|
|
58
61
|
value.expandedNodes,
|
|
@@ -75,7 +78,7 @@ export function TreeContextProvider({ children, value }: TreeContextProviderProp
|
|
|
75
78
|
|
|
76
79
|
const onSelect = useCallback<TreeContextProps['onSelect']>(
|
|
77
80
|
(node, parentNode) => {
|
|
78
|
-
if (node.disabled) return;
|
|
81
|
+
if (!isSelectable || node.disabled) return;
|
|
79
82
|
|
|
80
83
|
if (isSingleSelect) {
|
|
81
84
|
onSelectHandler(node.id, node);
|
|
@@ -88,7 +91,7 @@ export function TreeContextProvider({ children, value }: TreeContextProviderProp
|
|
|
88
91
|
onSelectHandler(updatedSelectedNodes, node);
|
|
89
92
|
}
|
|
90
93
|
},
|
|
91
|
-
[isSingleSelect, onSelectHandler, selectedNodes],
|
|
94
|
+
[isSingleSelect, onSelectHandler, selectedNodes, isSelectable],
|
|
92
95
|
);
|
|
93
96
|
|
|
94
97
|
const focusableNodeIds = useMemo(() => {
|
|
@@ -146,6 +149,7 @@ export function TreeContextProvider({ children, value }: TreeContextProviderProp
|
|
|
146
149
|
selected: selectedNodes,
|
|
147
150
|
isSingleSelect,
|
|
148
151
|
isMultiSelect,
|
|
152
|
+
isSelectable,
|
|
149
153
|
expandedNodes,
|
|
150
154
|
onExpand,
|
|
151
155
|
onSelect,
|
|
@@ -46,6 +46,7 @@ export function TreeNode({
|
|
|
46
46
|
}: TreeNodeComponentProps) {
|
|
47
47
|
const {
|
|
48
48
|
isMultiSelect,
|
|
49
|
+
isSelectable,
|
|
49
50
|
onNodeClick,
|
|
50
51
|
selected,
|
|
51
52
|
expandedNodes,
|
|
@@ -233,7 +234,7 @@ export function TreeNode({
|
|
|
233
234
|
<div
|
|
234
235
|
role='treeitem'
|
|
235
236
|
aria-expanded={isExpanded}
|
|
236
|
-
aria-selected={isSelected}
|
|
237
|
+
aria-selected={isSelectable ? isSelected : undefined}
|
|
237
238
|
aria-disabled={disabled}
|
|
238
239
|
data-multiselect={isMultiSelect || undefined}
|
|
239
240
|
data-droplist-active={isDroplistOpen || isDroplistTriggerFocused || undefined}
|
package/src/types.ts
CHANGED
|
@@ -52,6 +52,7 @@ export type TreeCommonProps = {
|
|
|
52
52
|
* Режим выбора элементов:
|
|
53
53
|
* <br> - `Single` - одиночный выбор
|
|
54
54
|
* <br> - `Multi` - множественный выбор
|
|
55
|
+
* <br> - `undefined` - без выбора
|
|
55
56
|
*/
|
|
56
57
|
selectionMode?: SelectionMode;
|
|
57
58
|
/** Обработчик клика по элементу дерева */
|
|
@@ -78,6 +79,12 @@ export type TreeCommonProps = {
|
|
|
78
79
|
className?: string;
|
|
79
80
|
};
|
|
80
81
|
|
|
82
|
+
export type TreeView = Omit<TreeCommonProps, 'selected' | 'selectionMode'> & {
|
|
83
|
+
selectionMode?: never;
|
|
84
|
+
selected?: never;
|
|
85
|
+
onSelect?: never;
|
|
86
|
+
};
|
|
87
|
+
|
|
81
88
|
export type TreeSingleSelect = Omit<TreeCommonProps, 'selected'> & {
|
|
82
89
|
selectionMode: 'single';
|
|
83
90
|
/** Состояние для выбраного элемента */
|
|
@@ -101,4 +108,4 @@ export type TreeMultiSelect = Omit<TreeCommonProps, 'selected'> & {
|
|
|
101
108
|
onSelect?(selectedKeys: TreeNodeId[], node: TreeNodeProps): void;
|
|
102
109
|
};
|
|
103
110
|
|
|
104
|
-
export type TreeBaseProps = TreeMultiSelect | TreeSingleSelect;
|
|
111
|
+
export type TreeBaseProps = TreeView | TreeMultiSelect | TreeSingleSelect;
|