@cloud-ru/ds-tree 1.0.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 +8 -0
- package/LICENSE +201 -0
- package/README.md +341 -0
- package/dist/cjs/components/Tree/Tree.d.ts +4 -0
- package/dist/cjs/components/Tree/Tree.js +31 -0
- package/dist/cjs/components/Tree/index.d.ts +1 -0
- package/dist/cjs/components/Tree/index.js +17 -0
- package/dist/cjs/components/Tree/styles.module.css +17 -0
- package/dist/cjs/components/Tree/utils.d.ts +4 -0
- package/dist/cjs/components/Tree/utils.js +15 -0
- package/dist/cjs/components/index.d.ts +1 -0
- package/dist/cjs/components/index.js +17 -0
- package/dist/cjs/constants.d.ts +26 -0
- package/dist/cjs/constants.js +29 -0
- package/dist/cjs/contexts/TreeContext.d.ts +30 -0
- package/dist/cjs/contexts/TreeContext.js +108 -0
- package/dist/cjs/helperComponents/ExpandableTreeNode/ExpandableTreeNode.d.ts +11 -0
- package/dist/cjs/helperComponents/ExpandableTreeNode/ExpandableTreeNode.js +87 -0
- package/dist/cjs/helperComponents/ExpandableTreeNode/index.d.ts +1 -0
- package/dist/cjs/helperComponents/ExpandableTreeNode/index.js +17 -0
- package/dist/cjs/helperComponents/ExpandableTreeNode/styles.module.css +37 -0
- package/dist/cjs/helperComponents/TreeItem/TreeItem.d.ts +8 -0
- package/dist/cjs/helperComponents/TreeItem/TreeItem.js +16 -0
- package/dist/cjs/helperComponents/TreeItem/index.d.ts +1 -0
- package/dist/cjs/helperComponents/TreeItem/index.js +17 -0
- package/dist/cjs/helperComponents/TreeLine/TreeLine.d.ts +10 -0
- package/dist/cjs/helperComponents/TreeLine/TreeLine.js +13 -0
- package/dist/cjs/helperComponents/TreeLine/index.d.ts +1 -0
- package/dist/cjs/helperComponents/TreeLine/index.js +17 -0
- package/dist/cjs/helperComponents/TreeLine/styles.module.css +37 -0
- package/dist/cjs/helperComponents/TreeNode/TreeNode.d.ts +4 -0
- package/dist/cjs/helperComponents/TreeNode/TreeNode.js +187 -0
- package/dist/cjs/helperComponents/TreeNode/components/TreeNodeActions.d.ts +15 -0
- package/dist/cjs/helperComponents/TreeNode/components/TreeNodeActions.js +76 -0
- package/dist/cjs/helperComponents/TreeNode/components/TreeNodeHref.d.ts +7 -0
- package/dist/cjs/helperComponents/TreeNode/components/TreeNodeHref.js +12 -0
- package/dist/cjs/helperComponents/TreeNode/components/index.d.ts +2 -0
- package/dist/cjs/helperComponents/TreeNode/components/index.js +18 -0
- package/dist/cjs/helperComponents/TreeNode/constants.d.ts +5 -0
- package/dist/cjs/helperComponents/TreeNode/constants.js +10 -0
- package/dist/cjs/helperComponents/TreeNode/index.d.ts +1 -0
- package/dist/cjs/helperComponents/TreeNode/index.js +17 -0
- package/dist/cjs/helperComponents/TreeNode/styles.module.css +474 -0
- package/dist/cjs/helperComponents/TreeNode/types.d.ts +9 -0
- package/dist/cjs/helperComponents/TreeNode/types.js +2 -0
- package/dist/cjs/helperComponents/TreeNode/utils.d.ts +3 -0
- package/dist/cjs/helperComponents/TreeNode/utils.js +10 -0
- package/dist/cjs/helperComponents/index.d.ts +4 -0
- package/dist/cjs/helperComponents/index.js +20 -0
- package/dist/cjs/helpers/checkNestedNodesSelection.d.ts +9 -0
- package/dist/cjs/helpers/checkNestedNodesSelection.js +15 -0
- package/dist/cjs/helpers/collectEmptyNestedNodesInExpanded.d.ts +9 -0
- package/dist/cjs/helpers/collectEmptyNestedNodesInExpanded.js +21 -0
- package/dist/cjs/helpers/collectHierarchicalAncestors.d.ts +3 -0
- package/dist/cjs/helpers/collectHierarchicalAncestors.js +18 -0
- package/dist/cjs/helpers/collectIds.d.ts +9 -0
- package/dist/cjs/helpers/collectIds.js +16 -0
- package/dist/cjs/helpers/extractTreeNodeTitle.d.ts +6 -0
- package/dist/cjs/helpers/extractTreeNodeTitle.js +9 -0
- package/dist/cjs/helpers/findAllChildNodeIds.d.ts +9 -0
- package/dist/cjs/helpers/findAllChildNodeIds.js +24 -0
- package/dist/cjs/helpers/findAllExpandedChildNodeIds.d.ts +8 -0
- package/dist/cjs/helpers/findAllExpandedChildNodeIds.js +21 -0
- package/dist/cjs/helpers/getSearchedTreeItems.d.ts +23 -0
- package/dist/cjs/helpers/getSearchedTreeItems.js +41 -0
- package/dist/cjs/helpers/getSearchedTreeNodeById.d.ts +19 -0
- package/dist/cjs/helpers/getSearchedTreeNodeById.js +35 -0
- package/dist/cjs/helpers/index.d.ts +13 -0
- package/dist/cjs/helpers/index.js +29 -0
- package/dist/cjs/helpers/lookupTreeForSelectedNodes.d.ts +15 -0
- package/dist/cjs/helpers/lookupTreeForSelectedNodes.js +21 -0
- package/dist/cjs/helpers/sortTreeItemsByTitle.d.ts +13 -0
- package/dist/cjs/helpers/sortTreeItemsByTitle.js +14 -0
- package/dist/cjs/helpers/traverse.d.ts +11 -0
- package/dist/cjs/helpers/traverse.js +25 -0
- package/dist/cjs/helpers/traverseWithTarget.d.ts +14 -0
- package/dist/cjs/helpers/traverseWithTarget.js +30 -0
- package/dist/cjs/helpers/updateTreeNode.d.ts +13 -0
- package/dist/cjs/helpers/updateTreeNode.js +34 -0
- package/dist/cjs/hooks/index.d.ts +2 -0
- package/dist/cjs/hooks/index.js +18 -0
- package/dist/cjs/hooks/internalHooks.d.ts +4 -0
- package/dist/cjs/hooks/internalHooks.js +29 -0
- package/dist/cjs/hooks/useSearchableTree.d.ts +52 -0
- package/dist/cjs/hooks/useSearchableTree.js +113 -0
- package/dist/cjs/hooks/useTreeMultiSelection.d.ts +33 -0
- package/dist/cjs/hooks/useTreeMultiSelection.js +37 -0
- package/dist/cjs/index.d.ts +17 -0
- package/dist/cjs/index.js +47 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/style.css +565 -0
- package/dist/cjs/types.d.ts +151 -0
- package/dist/cjs/types.js +2 -0
- package/dist/esm/components/Tree/Tree.d.ts +4 -0
- package/dist/esm/components/Tree/Tree.js +25 -0
- package/dist/esm/components/Tree/index.d.ts +1 -0
- package/dist/esm/components/Tree/index.js +1 -0
- package/dist/esm/components/Tree/styles.module.css +17 -0
- package/dist/esm/components/Tree/utils.d.ts +4 -0
- package/dist/esm/components/Tree/utils.js +12 -0
- package/dist/esm/components/index.d.ts +1 -0
- package/dist/esm/components/index.js +1 -0
- package/dist/esm/constants.d.ts +26 -0
- package/dist/esm/constants.js +26 -0
- package/dist/esm/contexts/TreeContext.d.ts +30 -0
- package/dist/esm/contexts/TreeContext.js +103 -0
- package/dist/esm/helperComponents/ExpandableTreeNode/ExpandableTreeNode.d.ts +11 -0
- package/dist/esm/helperComponents/ExpandableTreeNode/ExpandableTreeNode.js +81 -0
- package/dist/esm/helperComponents/ExpandableTreeNode/index.d.ts +1 -0
- package/dist/esm/helperComponents/ExpandableTreeNode/index.js +1 -0
- package/dist/esm/helperComponents/ExpandableTreeNode/styles.module.css +37 -0
- package/dist/esm/helperComponents/TreeItem/TreeItem.d.ts +8 -0
- package/dist/esm/helperComponents/TreeItem/TreeItem.js +13 -0
- package/dist/esm/helperComponents/TreeItem/index.d.ts +1 -0
- package/dist/esm/helperComponents/TreeItem/index.js +1 -0
- package/dist/esm/helperComponents/TreeLine/TreeLine.d.ts +10 -0
- package/dist/esm/helperComponents/TreeLine/TreeLine.js +7 -0
- package/dist/esm/helperComponents/TreeLine/index.d.ts +1 -0
- package/dist/esm/helperComponents/TreeLine/index.js +1 -0
- package/dist/esm/helperComponents/TreeLine/styles.module.css +37 -0
- package/dist/esm/helperComponents/TreeNode/TreeNode.d.ts +4 -0
- package/dist/esm/helperComponents/TreeNode/TreeNode.js +181 -0
- package/dist/esm/helperComponents/TreeNode/components/TreeNodeActions.d.ts +15 -0
- package/dist/esm/helperComponents/TreeNode/components/TreeNodeActions.js +70 -0
- package/dist/esm/helperComponents/TreeNode/components/TreeNodeHref.d.ts +7 -0
- package/dist/esm/helperComponents/TreeNode/components/TreeNodeHref.js +6 -0
- package/dist/esm/helperComponents/TreeNode/components/index.d.ts +2 -0
- package/dist/esm/helperComponents/TreeNode/components/index.js +2 -0
- package/dist/esm/helperComponents/TreeNode/constants.d.ts +5 -0
- package/dist/esm/helperComponents/TreeNode/constants.js +7 -0
- package/dist/esm/helperComponents/TreeNode/index.d.ts +1 -0
- package/dist/esm/helperComponents/TreeNode/index.js +1 -0
- package/dist/esm/helperComponents/TreeNode/styles.module.css +474 -0
- package/dist/esm/helperComponents/TreeNode/types.d.ts +9 -0
- package/dist/esm/helperComponents/TreeNode/types.js +1 -0
- package/dist/esm/helperComponents/TreeNode/utils.d.ts +3 -0
- package/dist/esm/helperComponents/TreeNode/utils.js +6 -0
- package/dist/esm/helperComponents/index.d.ts +4 -0
- package/dist/esm/helperComponents/index.js +4 -0
- package/dist/esm/helpers/checkNestedNodesSelection.d.ts +9 -0
- package/dist/esm/helpers/checkNestedNodesSelection.js +12 -0
- package/dist/esm/helpers/collectEmptyNestedNodesInExpanded.d.ts +9 -0
- package/dist/esm/helpers/collectEmptyNestedNodesInExpanded.js +18 -0
- package/dist/esm/helpers/collectHierarchicalAncestors.d.ts +3 -0
- package/dist/esm/helpers/collectHierarchicalAncestors.js +15 -0
- package/dist/esm/helpers/collectIds.d.ts +9 -0
- package/dist/esm/helpers/collectIds.js +13 -0
- package/dist/esm/helpers/extractTreeNodeTitle.d.ts +6 -0
- package/dist/esm/helpers/extractTreeNodeTitle.js +5 -0
- package/dist/esm/helpers/findAllChildNodeIds.d.ts +9 -0
- package/dist/esm/helpers/findAllChildNodeIds.js +21 -0
- package/dist/esm/helpers/findAllExpandedChildNodeIds.d.ts +8 -0
- package/dist/esm/helpers/findAllExpandedChildNodeIds.js +18 -0
- package/dist/esm/helpers/getSearchedTreeItems.d.ts +23 -0
- package/dist/esm/helpers/getSearchedTreeItems.js +37 -0
- package/dist/esm/helpers/getSearchedTreeNodeById.d.ts +19 -0
- package/dist/esm/helpers/getSearchedTreeNodeById.js +31 -0
- package/dist/esm/helpers/index.d.ts +13 -0
- package/dist/esm/helpers/index.js +13 -0
- package/dist/esm/helpers/lookupTreeForSelectedNodes.d.ts +15 -0
- package/dist/esm/helpers/lookupTreeForSelectedNodes.js +18 -0
- package/dist/esm/helpers/sortTreeItemsByTitle.d.ts +13 -0
- package/dist/esm/helpers/sortTreeItemsByTitle.js +10 -0
- package/dist/esm/helpers/traverse.d.ts +11 -0
- package/dist/esm/helpers/traverse.js +21 -0
- package/dist/esm/helpers/traverseWithTarget.d.ts +14 -0
- package/dist/esm/helpers/traverseWithTarget.js +26 -0
- package/dist/esm/helpers/updateTreeNode.d.ts +13 -0
- package/dist/esm/helpers/updateTreeNode.js +30 -0
- package/dist/esm/hooks/index.d.ts +2 -0
- package/dist/esm/hooks/index.js +2 -0
- package/dist/esm/hooks/internalHooks.d.ts +4 -0
- package/dist/esm/hooks/internalHooks.js +24 -0
- package/dist/esm/hooks/useSearchableTree.d.ts +52 -0
- package/dist/esm/hooks/useSearchableTree.js +110 -0
- package/dist/esm/hooks/useTreeMultiSelection.d.ts +33 -0
- package/dist/esm/hooks/useTreeMultiSelection.js +34 -0
- package/dist/esm/index.d.ts +17 -0
- package/dist/esm/index.js +17 -0
- package/dist/esm/style.css +565 -0
- package/dist/esm/types.d.ts +151 -0
- package/dist/esm/types.js +1 -0
- package/dist/tsconfig.cjs.tsbuildinfo +1 -0
- package/dist/tsconfig.esm.tsbuildinfo +1 -0
- package/package.json +51 -0
- package/src/components/Tree/Tree.tsx +53 -0
- package/src/components/Tree/index.ts +1 -0
- package/src/components/Tree/styles.module.scss +22 -0
- package/src/components/Tree/utils.ts +16 -0
- package/src/components/index.ts +1 -0
- package/src/constants.ts +29 -0
- package/src/contexts/TreeContext.tsx +189 -0
- package/src/helperComponents/ExpandableTreeNode/ExpandableTreeNode.tsx +156 -0
- package/src/helperComponents/ExpandableTreeNode/index.ts +1 -0
- package/src/helperComponents/ExpandableTreeNode/styles.module.scss +55 -0
- package/src/helperComponents/TreeItem/TreeItem.tsx +26 -0
- package/src/helperComponents/TreeItem/index.ts +1 -0
- package/src/helperComponents/TreeLine/TreeLine.tsx +27 -0
- package/src/helperComponents/TreeLine/index.ts +1 -0
- package/src/helperComponents/TreeLine/styles.module.scss +58 -0
- package/src/helperComponents/TreeNode/TreeNode.tsx +389 -0
- package/src/helperComponents/TreeNode/components/TreeNodeActions.tsx +129 -0
- package/src/helperComponents/TreeNode/components/TreeNodeHref.tsx +29 -0
- package/src/helperComponents/TreeNode/components/index.ts +2 -0
- package/src/helperComponents/TreeNode/constants.ts +7 -0
- package/src/helperComponents/TreeNode/index.ts +1 -0
- package/src/helperComponents/TreeNode/styles.module.scss +300 -0
- package/src/helperComponents/TreeNode/types.ts +11 -0
- package/src/helperComponents/TreeNode/utils.ts +9 -0
- package/src/helperComponents/index.ts +4 -0
- package/src/helpers/checkNestedNodesSelection.ts +15 -0
- package/src/helpers/collectEmptyNestedNodesInExpanded.ts +23 -0
- package/src/helpers/collectHierarchicalAncestors.ts +22 -0
- package/src/helpers/collectIds.ts +17 -0
- package/src/helpers/extractTreeNodeTitle.ts +8 -0
- package/src/helpers/findAllChildNodeIds.ts +27 -0
- package/src/helpers/findAllExpandedChildNodeIds.ts +24 -0
- package/src/helpers/getSearchedTreeItems.ts +63 -0
- package/src/helpers/getSearchedTreeNodeById.ts +54 -0
- package/src/helpers/index.ts +13 -0
- package/src/helpers/lookupTreeForSelectedNodes.ts +33 -0
- package/src/helpers/sortTreeItemsByTitle.ts +26 -0
- package/src/helpers/traverse.ts +28 -0
- package/src/helpers/traverseWithTarget.ts +41 -0
- package/src/helpers/updateTreeNode.ts +46 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/internalHooks.ts +30 -0
- package/src/hooks/useSearchableTree.ts +190 -0
- package/src/hooks/useTreeMultiSelection.ts +67 -0
- package/src/index.ts +17 -0
- package/src/types.ts +162 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.TreeNode = void 0;
|
|
7
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
+
const system_1 = require("@cloud-ru/ds-icons/interface/system");
|
|
9
|
+
const loader_1 = require("@cloud-ru/ds-loader");
|
|
10
|
+
const toggles_1 = require("@cloud-ru/ds-toggles");
|
|
11
|
+
const truncate_string_1 = require("@cloud-ru/ds-truncate-string");
|
|
12
|
+
const typography_1 = require("@cloud-ru/ds-typography");
|
|
13
|
+
const utils_1 = require("@cloud-ru/ds-utils");
|
|
14
|
+
const classnames_1 = __importDefault(require("classnames"));
|
|
15
|
+
const react_1 = require("react");
|
|
16
|
+
const constants_1 = require("../../constants");
|
|
17
|
+
const TreeContext_1 = require("../../contexts/TreeContext");
|
|
18
|
+
const helpers_1 = require("../../helpers");
|
|
19
|
+
const TreeLine_1 = require("../TreeLine");
|
|
20
|
+
const components_1 = require("./components");
|
|
21
|
+
const constants_2 = require("./constants");
|
|
22
|
+
const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
|
|
23
|
+
const utils_2 = require("./utils");
|
|
24
|
+
exports.TreeNode = (0, react_1.forwardRef)(({ id, title, icon: iconProp, expandedIcon: expandedIconProp, collapsedIcon: collapsedIconProp, disabled, onClick, nested, className, onChevronClick, onKeyDown, loading, parentNode, tabIndexAvailable, href, ...rest }, ref) => {
|
|
25
|
+
const { isMultiSelect, isSelectable, onNodeClick, selected, expandedNodes, onSelect, nodeActions, parentActions, setFocusPosition, resetFocusPosition, focusedNodeId, setFocusIndex, focusableNodeIds, showToggle, showLines, showIcons, size = 'm', titleMaxLines = 1, getSelectionState, } = (0, TreeContext_1.useTreeContext)();
|
|
26
|
+
const [isDroplistOpen, setDroplistOpen] = (0, react_1.useState)(false);
|
|
27
|
+
const [isDroplistTriggerFocused, setFocusDroplistTrigger] = (0, react_1.useState)(false);
|
|
28
|
+
const contentRef = (0, react_1.useRef)(null);
|
|
29
|
+
const anchorRef = (0, react_1.useRef)(null);
|
|
30
|
+
const isExpandable = Array.isArray(nested);
|
|
31
|
+
const isExpanded = isExpandable ? expandedNodes?.includes(id) : undefined;
|
|
32
|
+
const selectedIds = (0, react_1.useMemo)(() => {
|
|
33
|
+
if (Array.isArray(selected)) {
|
|
34
|
+
return selected;
|
|
35
|
+
}
|
|
36
|
+
if (selected) {
|
|
37
|
+
return [selected];
|
|
38
|
+
}
|
|
39
|
+
return [];
|
|
40
|
+
}, [selected]);
|
|
41
|
+
const descendantIds = (0, react_1.useMemo)(() => (nested?.length ? (0, helpers_1.findAllChildNodeIds)(nested) : []), [nested]);
|
|
42
|
+
const nestedNodesSelection = (0, react_1.useMemo)(() => {
|
|
43
|
+
if (!nested || !selected)
|
|
44
|
+
return undefined;
|
|
45
|
+
return (0, helpers_1.checkNestedNodesSelection)(nested, selectedIds);
|
|
46
|
+
}, [nested, selected, selectedIds]);
|
|
47
|
+
const multiSelectState = (0, react_1.useMemo)(() => {
|
|
48
|
+
if (!isMultiSelect) {
|
|
49
|
+
return { checked: false, indeterminate: false };
|
|
50
|
+
}
|
|
51
|
+
return getSelectionState({
|
|
52
|
+
nodeId: id,
|
|
53
|
+
childIds: descendantIds,
|
|
54
|
+
selectedIds,
|
|
55
|
+
});
|
|
56
|
+
}, [descendantIds, getSelectionState, id, isMultiSelect, selectedIds]);
|
|
57
|
+
const isSelected = isMultiSelect ? multiSelectState.checked : selected === id || false;
|
|
58
|
+
const isIndeterminate = isMultiSelect && multiSelectState.indeterminate;
|
|
59
|
+
const isFocused = focusedNodeId === id;
|
|
60
|
+
(0, react_1.useEffect)(() => {
|
|
61
|
+
if (contentRef.current && isFocused) {
|
|
62
|
+
contentRef.current.focus();
|
|
63
|
+
}
|
|
64
|
+
}, [isFocused]);
|
|
65
|
+
const treeNodeIcon = (0, react_1.useMemo)(() => {
|
|
66
|
+
if (!showIcons)
|
|
67
|
+
return undefined;
|
|
68
|
+
// Размер svg задаётся в styles.module.scss через `.treeNodeIcon svg`,
|
|
69
|
+
// зависит от data-size корня; size в JSX не прокидываем.
|
|
70
|
+
if (isExpandable) {
|
|
71
|
+
return isExpanded ? (expandedIconProp ?? (0, jsx_runtime_1.jsx)(system_1.FolderOpenSVG, {})) : (collapsedIconProp ?? (0, jsx_runtime_1.jsx)(system_1.FolderSVG, {}));
|
|
72
|
+
}
|
|
73
|
+
return iconProp ?? (0, jsx_runtime_1.jsx)(system_1.FileSVG, {});
|
|
74
|
+
}, [showIcons, isExpandable, isExpanded, iconProp, expandedIconProp, collapsedIconProp]);
|
|
75
|
+
const handleClick = e => {
|
|
76
|
+
onNodeClick({
|
|
77
|
+
id,
|
|
78
|
+
title,
|
|
79
|
+
disabled,
|
|
80
|
+
nested,
|
|
81
|
+
onClick,
|
|
82
|
+
href,
|
|
83
|
+
}, e);
|
|
84
|
+
};
|
|
85
|
+
const handleAnchorClick = (e) => {
|
|
86
|
+
e.stopPropagation();
|
|
87
|
+
if (e?.metaKey || e?.ctrlKey || e?.button === 1) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
// preventDefault только если есть кастомный onClick на ноде — иначе
|
|
91
|
+
// позволяем браузеру навигировать по href. Но handleClick зовём всегда,
|
|
92
|
+
// чтобы Tree успел прокинуть onNodeClick/onSelect.
|
|
93
|
+
if (onClick) {
|
|
94
|
+
e.preventDefault();
|
|
95
|
+
}
|
|
96
|
+
handleClick(e);
|
|
97
|
+
};
|
|
98
|
+
const handleSelect = () => {
|
|
99
|
+
onSelect({
|
|
100
|
+
id,
|
|
101
|
+
disabled,
|
|
102
|
+
nested,
|
|
103
|
+
}, parentNode);
|
|
104
|
+
};
|
|
105
|
+
const handleFocus = (e) => {
|
|
106
|
+
setFocusPosition(id);
|
|
107
|
+
if (!e) {
|
|
108
|
+
setFocusDroplistTrigger(false);
|
|
109
|
+
contentRef.current?.focus();
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
const handleBlurActions = () => {
|
|
113
|
+
if (isDroplistTriggerFocused && !isDroplistOpen) {
|
|
114
|
+
setFocusDroplistTrigger(false);
|
|
115
|
+
contentRef.current?.blur();
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
const handleKeyDown = e => {
|
|
119
|
+
onKeyDown?.(e);
|
|
120
|
+
const focusableCount = focusableNodeIds.length;
|
|
121
|
+
switch (e.key) {
|
|
122
|
+
case 'ArrowDown': {
|
|
123
|
+
e.preventDefault();
|
|
124
|
+
setFocusIndex((prev = 0) => {
|
|
125
|
+
if (prev >= focusableCount - 1) {
|
|
126
|
+
return focusableCount - 1;
|
|
127
|
+
}
|
|
128
|
+
return prev + 1;
|
|
129
|
+
});
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
case 'ArrowUp': {
|
|
133
|
+
e.preventDefault();
|
|
134
|
+
setFocusIndex((prev = 0) => {
|
|
135
|
+
if (!prev) {
|
|
136
|
+
return 0;
|
|
137
|
+
}
|
|
138
|
+
return prev - 1;
|
|
139
|
+
});
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
case 'ArrowRight': {
|
|
143
|
+
e.preventDefault();
|
|
144
|
+
// ArrowRight уводит фокус на droplist trigger row-actions — это нужно
|
|
145
|
+
// только если на строке вообще есть actions. Без них клавиша не
|
|
146
|
+
// должна давать визуального hover-эффекта (`data-droplist-active`).
|
|
147
|
+
const hasRowActions = Boolean(nested ? parentActions : nodeActions);
|
|
148
|
+
if (hasRowActions && (isExpanded || !isExpandable || disabled)) {
|
|
149
|
+
setFocusDroplistTrigger(true);
|
|
150
|
+
}
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
case 'ArrowLeft': {
|
|
154
|
+
e.preventDefault();
|
|
155
|
+
if (isDroplistTriggerFocused) {
|
|
156
|
+
setFocusDroplistTrigger(false);
|
|
157
|
+
contentRef.current?.focus();
|
|
158
|
+
}
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
case 'Escape': {
|
|
162
|
+
contentRef.current?.blur();
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
case ' ':
|
|
166
|
+
case 'Enter': {
|
|
167
|
+
e.preventDefault();
|
|
168
|
+
handleSelect();
|
|
169
|
+
if (href && anchorRef.current) {
|
|
170
|
+
anchorRef.current.click();
|
|
171
|
+
}
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
default:
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
const getNodeActions = nested ? parentActions : nodeActions;
|
|
179
|
+
return ((0, jsx_runtime_1.jsxs)("div", { role: 'presentation', className: (0, classnames_1.default)(styles_module_scss_1.default.treeNode, className), ...(0, utils_1.extractSupportProps)(rest), "data-node-id": id, "data-size": size, ref: ref, children: [parentNode && (0, jsx_runtime_1.jsx)(TreeLine_1.TreeLine, { halfWidth: true, horizontal: true, visible: showLines, "data-test-id": constants_1.TEST_IDS.line }), (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.treeNodeExpandButtonWrapper, children: isExpandable ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("button", { type: 'button', onClick: onChevronClick, "data-expanded": isExpanded || undefined, className: styles_module_scss_1.default.treeNodeExpandButton, tabIndex: -1, "data-test-id": constants_1.TEST_IDS.chevron, "aria-label": isExpanded ? 'Collapse' : 'Expand', children: [(0, jsx_runtime_1.jsx)("span", { className: styles_module_scss_1.default.treeNodeExpandButtonStateLayer, "aria-hidden": true, "data-state": 'emptyNeutralOnBackground' }), loading ? (0, jsx_runtime_1.jsx)(loader_1.Spinner, { size: constants_2.CONTROL_SIZE_BY_TREE_SIZE[size] }) : (0, jsx_runtime_1.jsx)(system_1.ChevronRightSVG, {})] }), (0, jsx_runtime_1.jsx)(TreeLine_1.TreeLine, { visible: isExpanded && showLines })] })) : ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.treeNodeExpandButtonSpacer, "aria-hidden": true })) }), (0, jsx_runtime_1.jsxs)("div", { role: 'treeitem', "aria-expanded": isExpanded, "aria-selected": isSelectable
|
|
180
|
+
? isSelected || (nestedNodesSelection?.someSelected && !isExpanded && !isMultiSelect)
|
|
181
|
+
: 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_module_scss_1.default.treeNodeContent, "data-test-id": constants_1.TEST_IDS.item, ref: contentRef, children: [(0, jsx_runtime_1.jsx)("span", { className: styles_module_scss_1.default.stateLayer, "aria-hidden": true, "data-state": isSelected && !isMultiSelect ? 'activatedOnBackground' : 'emptyNeutralOnBackground' }), (isMultiSelect || showToggle) && ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.treeNodeCheckboxWrap, children: [isMultiSelect && ((0, jsx_runtime_1.jsx)(toggles_1.Checkbox, { size: constants_2.CONTROL_SIZE_BY_TREE_SIZE[size], disabled: disabled, checked: isSelected, indeterminate: isIndeterminate, onChange: handleSelect, onClick: utils_2.stopPropagationClick, "data-test-id": constants_1.TEST_IDS.checkbox, tabIndex: -1 })), showToggle && ((0, jsx_runtime_1.jsx)(toggles_1.Radio, { size: constants_2.CONTROL_SIZE_BY_TREE_SIZE[size], checked: isSelected, disabled: disabled, "data-test-id": constants_1.TEST_IDS.radio, tabIndex: -1 }))] })), treeNodeIcon && ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.treeNodeIcon, "data-test-id": constants_1.TEST_IDS.icon, children: treeNodeIcon })), (0, jsx_runtime_1.jsx)(typography_1.Typography, { variant: 'body', size: size, as: 'div', className: styles_module_scss_1.default.treeNodeTitle, children: (0, jsx_runtime_1.jsxs)(components_1.TreeNodeHref, { href: href, onClick: handleAnchorClick, ref: anchorRef, children: [typeof title === 'string' && ((0, jsx_runtime_1.jsx)(truncate_string_1.TruncateString, { text: title, maxLines: titleMaxLines, "data-test-id": constants_1.TEST_IDS.title })), typeof title !== 'string' && title({ id, disabled, nested })] }) }), getNodeActions && ((0, jsx_runtime_1.jsx)(components_1.TreeNodeActions, { getNodeActions: getNodeActions, node: {
|
|
182
|
+
id,
|
|
183
|
+
title,
|
|
184
|
+
disabled,
|
|
185
|
+
nested,
|
|
186
|
+
}, focusNode: handleFocus, onBlurActions: handleBlurActions, isDroplistTriggerFocused: isDroplistTriggerFocused, isDroplistOpen: isDroplistOpen, setDroplistOpen: setDroplistOpen, size: size }))] })] }));
|
|
187
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ItemProps } from '@cloud-ru/ds-list';
|
|
2
|
+
import { Dispatch, SetStateAction } from 'react';
|
|
3
|
+
import { Size, TreeNodeProps } from '../../../types';
|
|
4
|
+
type TreeNodeActionsProps = {
|
|
5
|
+
isDroplistOpen: boolean;
|
|
6
|
+
setDroplistOpen: Dispatch<SetStateAction<boolean>>;
|
|
7
|
+
getNodeActions(node: Omit<TreeNodeProps, 'href'>): ItemProps[];
|
|
8
|
+
node: Omit<TreeNodeProps, 'href'>;
|
|
9
|
+
isDroplistTriggerFocused: boolean;
|
|
10
|
+
focusNode(): void;
|
|
11
|
+
onBlurActions(): void;
|
|
12
|
+
size: Size;
|
|
13
|
+
};
|
|
14
|
+
export declare function TreeNodeActions({ getNodeActions, isDroplistTriggerFocused, focusNode, isDroplistOpen, setDroplistOpen, onBlurActions, node, size, }: TreeNodeActionsProps): import("react/jsx-runtime").JSX.Element | null;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.TreeNodeActions = TreeNodeActions;
|
|
7
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
+
const button_1 = require("@cloud-ru/ds-button");
|
|
9
|
+
const system_1 = require("@cloud-ru/ds-icons/interface/system");
|
|
10
|
+
const list_1 = require("@cloud-ru/ds-list");
|
|
11
|
+
const react_1 = require("react");
|
|
12
|
+
const constants_1 = require("../../../constants");
|
|
13
|
+
const styles_module_scss_1 = __importDefault(require('../styles.module.css'));
|
|
14
|
+
const utils_1 = require("../utils");
|
|
15
|
+
function TreeNodeActions({ getNodeActions, isDroplistTriggerFocused, focusNode, isDroplistOpen, setDroplistOpen, onBlurActions, node, size, }) {
|
|
16
|
+
const droplistActions = getNodeActions(node);
|
|
17
|
+
const localRef = (0, react_1.useRef)(null);
|
|
18
|
+
(0, react_1.useEffect)(() => {
|
|
19
|
+
if (localRef.current && isDroplistTriggerFocused) {
|
|
20
|
+
localRef.current.focus();
|
|
21
|
+
}
|
|
22
|
+
}, [isDroplistTriggerFocused, localRef]);
|
|
23
|
+
const handleKeyDown = e => {
|
|
24
|
+
switch (e.key) {
|
|
25
|
+
case 'Tab': {
|
|
26
|
+
focusNode();
|
|
27
|
+
setDroplistOpen(false);
|
|
28
|
+
e.preventDefault();
|
|
29
|
+
e.stopPropagation();
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
case 'ArrowLeft': {
|
|
33
|
+
if (isDroplistTriggerFocused) {
|
|
34
|
+
focusNode();
|
|
35
|
+
setDroplistOpen(false);
|
|
36
|
+
e.stopPropagation();
|
|
37
|
+
}
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
case ' ':
|
|
41
|
+
case 'Enter': {
|
|
42
|
+
e.stopPropagation();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
case 'ArrowDown': {
|
|
46
|
+
if (isDroplistTriggerFocused) {
|
|
47
|
+
setDroplistOpen(true);
|
|
48
|
+
}
|
|
49
|
+
e.stopPropagation();
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
case 'ArrowUp': {
|
|
53
|
+
setDroplistOpen(false);
|
|
54
|
+
localRef.current?.focus();
|
|
55
|
+
e.stopPropagation();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
default:
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
if (!droplistActions.length) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
return ((0, jsx_runtime_1.jsx)("div", { role: 'presentation', className: styles_module_scss_1.default.treeNodeActions, "data-focused": isDroplistTriggerFocused || undefined, onClick: utils_1.stopPropagationClick, onKeyDown: handleKeyDown, onFocus: utils_1.stopPropagationFocus, children: (0, jsx_runtime_1.jsx)(list_1.Droplist, { open: isDroplistOpen, onOpenChange: open => {
|
|
66
|
+
setDroplistOpen(open);
|
|
67
|
+
// Закрытие дроплиста (по клику в пункт, ESC, outside-click) должно
|
|
68
|
+
// вернуть фокус на строку и снять `isDroplistTriggerFocused` —
|
|
69
|
+
// иначе kebab остаётся в visible-hover-состоянии. Tab и ArrowLeft
|
|
70
|
+
// уже делают это сами; для пути через `closeDroplistOnItemClick`
|
|
71
|
+
// ресет навешиваем здесь.
|
|
72
|
+
if (!open) {
|
|
73
|
+
focusNode();
|
|
74
|
+
}
|
|
75
|
+
}, items: droplistActions, closeDroplistOnItemClick: true, placement: 'bottom-end', size: size, children: (0, jsx_runtime_1.jsx)(button_1.Button, { view: 'elevated', appearance: 'neutral', size: size, icon: (0, jsx_runtime_1.jsx)(system_1.KebabSVG, {}), onBlur: onBlurActions, tabIndex: -1, "data-test-id": constants_1.TEST_IDS.droplistTrigger, innerRef: localRef }) }) }));
|
|
76
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { MouseEventHandler, ReactNode } from 'react';
|
|
2
|
+
export type TreeNodeHrefProps = {
|
|
3
|
+
href?: string;
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
onClick?: MouseEventHandler<HTMLAnchorElement>;
|
|
6
|
+
};
|
|
7
|
+
export declare const TreeNodeHref: import("react").ForwardRefExoticComponent<TreeNodeHrefProps & import("react").RefAttributes<HTMLAnchorElement>>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.TreeNodeHref = void 0;
|
|
7
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
+
const react_1 = require("react");
|
|
9
|
+
const constants_1 = require("../../../constants");
|
|
10
|
+
const styles_module_scss_1 = __importDefault(require('../styles.module.css'));
|
|
11
|
+
exports.TreeNodeHref = (0, react_1.forwardRef)(({ href, children, onClick }, ref) => href ? ((0, jsx_runtime_1.jsx)("a", { href: href, className: styles_module_scss_1.default.treeNodeLink, onClick: onClick, "data-test-id": constants_1.TEST_IDS.link, tabIndex: -2, ref: ref, children: children })) : (children));
|
|
12
|
+
exports.TreeNodeHref.displayName = 'TreeNodeHref';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./TreeNodeActions"), exports);
|
|
18
|
+
__exportStar(require("./TreeNodeHref"), exports);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CONTROL_SIZE_BY_TREE_SIZE = void 0;
|
|
4
|
+
// Size-ось контролов (Checkbox / Radio / Spinner) короче, чем у Tree:
|
|
5
|
+
// 's'-строка → 'xs'-контрол, 'm'/'l' → 's'.
|
|
6
|
+
exports.CONTROL_SIZE_BY_TREE_SIZE = {
|
|
7
|
+
s: 'xs',
|
|
8
|
+
m: 's',
|
|
9
|
+
l: 's',
|
|
10
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './TreeNode';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./TreeNode"), exports);
|