@snack-uikit/tree 0.9.28 → 0.9.30
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 +18 -0
- package/dist/cjs/constants.d.ts +2 -0
- package/dist/cjs/constants.js +5 -3
- package/dist/cjs/helperComponents/TreeNode/TreeNode.js +33 -12
- package/dist/cjs/helperComponents/TreeNode/components/TreeNodeActions.d.ts +2 -2
- package/dist/cjs/helperComponents/TreeNode/components/TreeNodeHref.d.ts +7 -0
- package/dist/cjs/helperComponents/TreeNode/components/TreeNodeHref.js +32 -0
- package/dist/cjs/helperComponents/TreeNode/components/index.d.ts +1 -0
- package/dist/cjs/helperComponents/TreeNode/components/index.js +2 -1
- package/dist/cjs/helperComponents/TreeNode/styles.module.css +7 -0
- package/dist/cjs/types.d.ts +2 -0
- package/dist/esm/constants.d.ts +2 -0
- package/dist/esm/constants.js +2 -0
- package/dist/esm/helperComponents/TreeNode/TreeNode.js +18 -3
- package/dist/esm/helperComponents/TreeNode/components/TreeNodeActions.d.ts +2 -2
- 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 +1 -0
- package/dist/esm/helperComponents/TreeNode/components/index.js +1 -0
- package/dist/esm/helperComponents/TreeNode/styles.module.css +7 -0
- package/dist/esm/types.d.ts +2 -0
- package/package.json +4 -4
- package/src/constants.ts +3 -0
- package/src/helperComponents/TreeNode/TreeNode.tsx +26 -3
- package/src/helperComponents/TreeNode/components/TreeNodeActions.tsx +2 -2
- package/src/helperComponents/TreeNode/components/TreeNodeHref.tsx +29 -0
- package/src/helperComponents/TreeNode/components/index.ts +1 -0
- package/src/helperComponents/TreeNode/styles.module.scss +7 -0
- package/src/types.ts +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,24 @@
|
|
|
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
|
+
## <small>0.9.30 (2025-11-14)</small>
|
|
7
|
+
|
|
8
|
+
### Only dependencies have been changed
|
|
9
|
+
* [@snack-uikit/list@0.32.7](https://github.com/cloud-ru-tech/snack-uikit/blob/master/packages/list/CHANGELOG.md)
|
|
10
|
+
* [@snack-uikit/truncate-string@0.7.4](https://github.com/cloud-ru-tech/snack-uikit/blob/master/packages/truncate-string/CHANGELOG.md)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## <small>0.9.29 (2025-11-13)</small>
|
|
17
|
+
|
|
18
|
+
* feat(PDS-2935): add href param to TreeNode ([c508075](https://github.com/cloud-ru-tech/snack-uikit/commit/c508075))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
6
24
|
## <small>0.9.28 (2025-10-28)</small>
|
|
7
25
|
|
|
8
26
|
### Only dependencies have been changed
|
package/dist/cjs/constants.d.ts
CHANGED
package/dist/cjs/constants.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.TEST_IDS = exports.TRANSITION_TIMING = exports.SELECTION_MODE = void 0;
|
|
6
|
+
exports.LINK_TEST_HREF = exports.TEST_IDS = exports.TRANSITION_TIMING = exports.SELECTION_MODE = void 0;
|
|
7
7
|
exports.SELECTION_MODE = {
|
|
8
8
|
Single: 'single',
|
|
9
9
|
Multi: 'multi'
|
|
@@ -23,5 +23,7 @@ exports.TEST_IDS = {
|
|
|
23
23
|
droplistTrigger: 'tree__node__droplist-trigger',
|
|
24
24
|
droplistAction: 'tree__node__droplist-action',
|
|
25
25
|
expandable: 'tree__node__expandable',
|
|
26
|
-
expandableContent: 'tree__node__expandable-content'
|
|
27
|
-
|
|
26
|
+
expandableContent: 'tree__node__expandable-content',
|
|
27
|
+
link: 'tree__node__link'
|
|
28
|
+
};
|
|
29
|
+
exports.LINK_TEST_HREF = '/snack/?path=/story/welcome--welcome';
|
|
@@ -54,9 +54,10 @@ exports.TreeNode = (0, react_1.forwardRef)((_a, ref) => {
|
|
|
54
54
|
onKeyDown,
|
|
55
55
|
isLoading,
|
|
56
56
|
parentNode,
|
|
57
|
-
tabIndexAvailable
|
|
57
|
+
tabIndexAvailable,
|
|
58
|
+
href
|
|
58
59
|
} = _a,
|
|
59
|
-
rest = __rest(_a, ["id", "title", "icon", "expandedIcon", "collapsedIcon", "disabled", "onClick", "nested", "className", "onChevronClick", "onKeyDown", "isLoading", "parentNode", "tabIndexAvailable"]);
|
|
60
|
+
rest = __rest(_a, ["id", "title", "icon", "expandedIcon", "collapsedIcon", "disabled", "onClick", "nested", "className", "onChevronClick", "onKeyDown", "isLoading", "parentNode", "tabIndexAvailable", "href"]);
|
|
60
61
|
const {
|
|
61
62
|
isMultiSelect,
|
|
62
63
|
isSelectable,
|
|
@@ -78,6 +79,7 @@ exports.TreeNode = (0, react_1.forwardRef)((_a, ref) => {
|
|
|
78
79
|
const [isDroplistOpen, setDroplistOpen] = (0, react_1.useState)(false);
|
|
79
80
|
const [isDroplistTriggerFocused, setFocusDroplistTrigger] = (0, react_1.useState)(false);
|
|
80
81
|
const contentRef = (0, react_1.useRef)(null);
|
|
82
|
+
const anchorRef = (0, react_1.useRef)(null);
|
|
81
83
|
const isExpandable = Array.isArray(nested);
|
|
82
84
|
const isExpanded = isExpandable ? expandedNodes === null || expandedNodes === void 0 ? void 0 : expandedNodes.includes(id) : undefined;
|
|
83
85
|
const nestedNodesSelection = (0, react_1.useMemo)(() => {
|
|
@@ -104,9 +106,20 @@ exports.TreeNode = (0, react_1.forwardRef)((_a, ref) => {
|
|
|
104
106
|
title,
|
|
105
107
|
disabled,
|
|
106
108
|
nested,
|
|
107
|
-
onClick
|
|
109
|
+
onClick,
|
|
110
|
+
href
|
|
108
111
|
}, e);
|
|
109
112
|
};
|
|
113
|
+
const handleAnchorClick = e => {
|
|
114
|
+
e.stopPropagation();
|
|
115
|
+
if ((e === null || e === void 0 ? void 0 : e.metaKey) || (e === null || e === void 0 ? void 0 : e.ctrlKey) || (e === null || e === void 0 ? void 0 : e.button) === 1) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (onClick) {
|
|
119
|
+
e.preventDefault();
|
|
120
|
+
handleClick(e);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
110
123
|
const handleSelect = () => {
|
|
111
124
|
onSelect({
|
|
112
125
|
id,
|
|
@@ -185,6 +198,9 @@ exports.TreeNode = (0, react_1.forwardRef)((_a, ref) => {
|
|
|
185
198
|
{
|
|
186
199
|
e.preventDefault();
|
|
187
200
|
handleSelect();
|
|
201
|
+
if (href && anchorRef.current) {
|
|
202
|
+
anchorRef.current.click();
|
|
203
|
+
}
|
|
188
204
|
return;
|
|
189
205
|
}
|
|
190
206
|
default:
|
|
@@ -257,17 +273,22 @@ exports.TreeNode = (0, react_1.forwardRef)((_a, ref) => {
|
|
|
257
273
|
className: styles_module_scss_1.default.treeNodeIcon,
|
|
258
274
|
"data-test-id": constants_1.TEST_IDS.icon,
|
|
259
275
|
children: treeNodeIcon
|
|
260
|
-
}), (0, jsx_runtime_1.
|
|
276
|
+
}), (0, jsx_runtime_1.jsx)(typography_1.Typography.SansBodyM, {
|
|
261
277
|
tag: 'div',
|
|
262
278
|
className: styles_module_scss_1.default.treeNodeTitle,
|
|
263
|
-
children:
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
279
|
+
children: (0, jsx_runtime_1.jsxs)(components_1.TreeNodeHref, {
|
|
280
|
+
href: href,
|
|
281
|
+
onClick: handleAnchorClick,
|
|
282
|
+
ref: anchorRef,
|
|
283
|
+
children: [typeof title === 'string' && (0, jsx_runtime_1.jsx)(truncate_string_1.TruncateString, {
|
|
284
|
+
text: title,
|
|
285
|
+
"data-test-id": constants_1.TEST_IDS.title
|
|
286
|
+
}), typeof title !== 'string' && title({
|
|
287
|
+
id,
|
|
288
|
+
disabled,
|
|
289
|
+
nested
|
|
290
|
+
})]
|
|
291
|
+
})
|
|
271
292
|
}), getNodeActions && (0, jsx_runtime_1.jsx)(components_1.TreeNodeActions, {
|
|
272
293
|
getNodeActions: getNodeActions,
|
|
273
294
|
node: {
|
|
@@ -4,8 +4,8 @@ import { TreeNodeProps } from '../../../types';
|
|
|
4
4
|
type TreeNodeActionsProps = {
|
|
5
5
|
isDroplistOpen: boolean;
|
|
6
6
|
setDroplistOpen: Dispatch<SetStateAction<boolean>>;
|
|
7
|
-
getNodeActions(node: TreeNodeProps): ItemProps[];
|
|
8
|
-
node: TreeNodeProps
|
|
7
|
+
getNodeActions(node: Omit<TreeNodeProps, 'href'>): ItemProps[];
|
|
8
|
+
node: Omit<TreeNodeProps, 'href'>;
|
|
9
9
|
isDroplistTriggerFocused: boolean;
|
|
10
10
|
focusNode(): void;
|
|
11
11
|
onBlurActions(): void;
|
|
@@ -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,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var __importDefault = void 0 && (void 0).__importDefault || function (mod) {
|
|
4
|
+
return mod && mod.__esModule ? mod : {
|
|
5
|
+
"default": mod
|
|
6
|
+
};
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", {
|
|
9
|
+
value: true
|
|
10
|
+
});
|
|
11
|
+
exports.TreeNodeHref = void 0;
|
|
12
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
13
|
+
const react_1 = require("react");
|
|
14
|
+
const constants_1 = require("../../../constants");
|
|
15
|
+
const styles_module_scss_1 = __importDefault(require('../styles.module.css'));
|
|
16
|
+
exports.TreeNodeHref = (0, react_1.forwardRef)((_ref, ref) => {
|
|
17
|
+
let {
|
|
18
|
+
href,
|
|
19
|
+
children,
|
|
20
|
+
onClick
|
|
21
|
+
} = _ref;
|
|
22
|
+
return href ? (0, jsx_runtime_1.jsx)("a", {
|
|
23
|
+
href: href,
|
|
24
|
+
className: styles_module_scss_1.default.treeNodeLink,
|
|
25
|
+
onClick: onClick,
|
|
26
|
+
"data-test-id": constants_1.TEST_IDS.link,
|
|
27
|
+
tabIndex: -2,
|
|
28
|
+
ref: ref,
|
|
29
|
+
children: children
|
|
30
|
+
}) : children;
|
|
31
|
+
});
|
|
32
|
+
exports.TreeNodeHref.displayName = 'TreeNodeHref';
|
|
@@ -22,4 +22,5 @@ var __exportStar = void 0 && (void 0).__exportStar || function (m, exports) {
|
|
|
22
22
|
Object.defineProperty(exports, "__esModule", {
|
|
23
23
|
value: true
|
|
24
24
|
});
|
|
25
|
-
__exportStar(require("./TreeNodeActions"), exports);
|
|
25
|
+
__exportStar(require("./TreeNodeActions"), exports);
|
|
26
|
+
__exportStar(require("./TreeNodeHref"), exports);
|
package/dist/cjs/types.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export type BaseTreeNode = WithSupportProps<{
|
|
|
14
14
|
/** Обработчик клика по элементу */
|
|
15
15
|
onClick?: MouseEventHandler;
|
|
16
16
|
className?: string;
|
|
17
|
+
/** Ссылка на элемент */
|
|
18
|
+
href?: string;
|
|
17
19
|
}>;
|
|
18
20
|
export type ChildTreeNode = BaseTreeNode & {
|
|
19
21
|
/** Иконка дочернего элемента */
|
package/dist/esm/constants.d.ts
CHANGED
package/dist/esm/constants.js
CHANGED
|
@@ -18,4 +18,6 @@ export const TEST_IDS = {
|
|
|
18
18
|
droplistAction: 'tree__node__droplist-action',
|
|
19
19
|
expandable: 'tree__node__expandable',
|
|
20
20
|
expandableContent: 'tree__node__expandable-content',
|
|
21
|
+
link: 'tree__node__link',
|
|
21
22
|
};
|
|
23
|
+
export const LINK_TEST_HREF = '/snack/?path=/story/welcome--welcome';
|
|
@@ -22,15 +22,16 @@ import { TEST_IDS } from '../../constants';
|
|
|
22
22
|
import { useTreeContext } from '../../contexts/TreeContext';
|
|
23
23
|
import { checkNestedNodesSelection } from '../../helpers';
|
|
24
24
|
import { TreeLine } from '../TreeLine';
|
|
25
|
-
import { TreeNodeActions } from './components';
|
|
25
|
+
import { TreeNodeActions, TreeNodeHref } from './components';
|
|
26
26
|
import styles from './styles.module.css';
|
|
27
27
|
import { stopPropagationClick } from './utils';
|
|
28
28
|
export const TreeNode = forwardRef((_a, ref) => {
|
|
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"]);
|
|
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, href } = _a, rest = __rest(_a, ["id", "title", "icon", "expandedIcon", "collapsedIcon", "disabled", "onClick", "nested", "className", "onChevronClick", "onKeyDown", "isLoading", "parentNode", "tabIndexAvailable", "href"]);
|
|
30
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 contentRef = useRef(null);
|
|
34
|
+
const anchorRef = useRef(null);
|
|
34
35
|
const isExpandable = Array.isArray(nested);
|
|
35
36
|
const isExpanded = isExpandable ? expandedNodes === null || expandedNodes === void 0 ? void 0 : expandedNodes.includes(id) : undefined;
|
|
36
37
|
const nestedNodesSelection = useMemo(() => {
|
|
@@ -60,8 +61,19 @@ export const TreeNode = forwardRef((_a, ref) => {
|
|
|
60
61
|
disabled,
|
|
61
62
|
nested,
|
|
62
63
|
onClick,
|
|
64
|
+
href,
|
|
63
65
|
}, e);
|
|
64
66
|
};
|
|
67
|
+
const handleAnchorClick = (e) => {
|
|
68
|
+
e.stopPropagation();
|
|
69
|
+
if ((e === null || e === void 0 ? void 0 : e.metaKey) || (e === null || e === void 0 ? void 0 : e.ctrlKey) || (e === null || e === void 0 ? void 0 : e.button) === 1) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (onClick) {
|
|
73
|
+
e.preventDefault();
|
|
74
|
+
handleClick(e);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
65
77
|
const handleSelect = () => {
|
|
66
78
|
onSelect({
|
|
67
79
|
id,
|
|
@@ -132,6 +144,9 @@ export const TreeNode = forwardRef((_a, ref) => {
|
|
|
132
144
|
case 'Enter': {
|
|
133
145
|
e.preventDefault();
|
|
134
146
|
handleSelect();
|
|
147
|
+
if (href && anchorRef.current) {
|
|
148
|
+
anchorRef.current.click();
|
|
149
|
+
}
|
|
135
150
|
return;
|
|
136
151
|
}
|
|
137
152
|
default:
|
|
@@ -141,7 +156,7 @@ export const TreeNode = forwardRef((_a, ref) => {
|
|
|
141
156
|
const getNodeActions = nested ? parentActions : nodeActions;
|
|
142
157
|
return (_jsxs("div", Object.assign({ role: 'presentation', className: cn(styles.treeNode, className) }, extractSupportProps(rest), { "data-node-id": id, ref: ref, children: [parentNode && (_jsx(TreeLine, { halfWidth: Boolean(nested), horizontal: true, visible: showLines, "data-test-id": TEST_IDS.line })), !parentNode && !nested && _jsx(TreeLine, { visible: false }), isExpandable && (_jsxs("div", { className: styles.treeNodeExpandButtonWrapper, children: [_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 }), _jsx(TreeLine, { visible: isExpanded && showLines, height: '100%' })] })), _jsxs("div", { role: 'treeitem', "aria-expanded": isExpanded, "aria-selected": isSelectable
|
|
143
158
|
? isSelected || ((nestedNodesSelection === null || nestedNodesSelection === void 0 ? void 0 : nestedNodesSelection.someSelected) && !isExpanded && !isMultiSelect)
|
|
144
|
-
: 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: contentRef, 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, "data-test-id": TEST_IDS.radio, tabIndex: -1 }))] })), treeNodeIcon && (_jsx("div", { className: styles.treeNodeIcon, "data-test-id": TEST_IDS.icon, children: treeNodeIcon })),
|
|
159
|
+
: 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: contentRef, 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, "data-test-id": TEST_IDS.radio, 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: _jsxs(TreeNodeHref, { href: href, onClick: handleAnchorClick, ref: anchorRef, 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: {
|
|
145
160
|
id,
|
|
146
161
|
title,
|
|
147
162
|
disabled,
|
|
@@ -4,8 +4,8 @@ import { TreeNodeProps } from '../../../types';
|
|
|
4
4
|
type TreeNodeActionsProps = {
|
|
5
5
|
isDroplistOpen: boolean;
|
|
6
6
|
setDroplistOpen: Dispatch<SetStateAction<boolean>>;
|
|
7
|
-
getNodeActions(node: TreeNodeProps): ItemProps[];
|
|
8
|
-
node: TreeNodeProps
|
|
7
|
+
getNodeActions(node: Omit<TreeNodeProps, 'href'>): ItemProps[];
|
|
8
|
+
node: Omit<TreeNodeProps, 'href'>;
|
|
9
9
|
isDroplistTriggerFocused: boolean;
|
|
10
10
|
focusNode(): void;
|
|
11
11
|
onBlurActions(): void;
|
|
@@ -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,6 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
import { TEST_IDS } from '../../../constants';
|
|
4
|
+
import styles from '../styles.module.css';
|
|
5
|
+
export const TreeNodeHref = forwardRef(({ href, children, onClick }, ref) => href ? (_jsx("a", { href: href, className: styles.treeNodeLink, onClick: onClick, "data-test-id": TEST_IDS.link, tabIndex: -2, ref: ref, children: children })) : (children));
|
|
6
|
+
TreeNodeHref.displayName = 'TreeNodeHref';
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export type BaseTreeNode = WithSupportProps<{
|
|
|
14
14
|
/** Обработчик клика по элементу */
|
|
15
15
|
onClick?: MouseEventHandler;
|
|
16
16
|
className?: string;
|
|
17
|
+
/** Ссылка на элемент */
|
|
18
|
+
href?: string;
|
|
17
19
|
}>;
|
|
18
20
|
export type ChildTreeNode = BaseTreeNode & {
|
|
19
21
|
/** Иконка дочернего элемента */
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
6
|
"title": "Tree",
|
|
7
|
-
"version": "0.9.
|
|
7
|
+
"version": "0.9.30",
|
|
8
8
|
"sideEffects": [
|
|
9
9
|
"*.css",
|
|
10
10
|
"*.woff",
|
|
@@ -38,14 +38,14 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@snack-uikit/button": "0.19.16",
|
|
40
40
|
"@snack-uikit/icons": "0.27.3",
|
|
41
|
-
"@snack-uikit/list": "0.32.
|
|
41
|
+
"@snack-uikit/list": "0.32.7",
|
|
42
42
|
"@snack-uikit/toggles": "0.13.22",
|
|
43
|
-
"@snack-uikit/truncate-string": "0.7.
|
|
43
|
+
"@snack-uikit/truncate-string": "0.7.4",
|
|
44
44
|
"@snack-uikit/typography": "0.8.11",
|
|
45
45
|
"@snack-uikit/utils": "4.0.0",
|
|
46
46
|
"classnames": "2.5.1",
|
|
47
47
|
"react-transition-state": "2.1.1",
|
|
48
48
|
"uncontrollable": "8.0.4"
|
|
49
49
|
},
|
|
50
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "fc6ef99551b60c7b9d2f8cacc87cfb0fd35030bf"
|
|
51
51
|
}
|
package/src/constants.ts
CHANGED
|
@@ -20,4 +20,7 @@ export const TEST_IDS = {
|
|
|
20
20
|
droplistAction: 'tree__node__droplist-action',
|
|
21
21
|
expandable: 'tree__node__expandable',
|
|
22
22
|
expandableContent: 'tree__node__expandable-content',
|
|
23
|
+
link: 'tree__node__link',
|
|
23
24
|
};
|
|
25
|
+
|
|
26
|
+
export const LINK_TEST_HREF = '/snack/?path=/story/welcome--welcome';
|
|
@@ -22,7 +22,7 @@ import { useTreeContext } from '../../contexts/TreeContext';
|
|
|
22
22
|
import { checkNestedNodesSelection } from '../../helpers';
|
|
23
23
|
import { ParentNode, TreeNodeProps } from '../../types';
|
|
24
24
|
import { TreeLine } from '../TreeLine';
|
|
25
|
-
import { TreeNodeActions } from './components';
|
|
25
|
+
import { TreeNodeActions, TreeNodeHref } from './components';
|
|
26
26
|
import styles from './styles.module.scss';
|
|
27
27
|
import { stopPropagationClick } from './utils';
|
|
28
28
|
|
|
@@ -53,6 +53,7 @@ export const TreeNode = forwardRef<HTMLDivElement, TreeNodeComponentProps>(
|
|
|
53
53
|
isLoading,
|
|
54
54
|
parentNode,
|
|
55
55
|
tabIndexAvailable,
|
|
56
|
+
href,
|
|
56
57
|
...rest
|
|
57
58
|
},
|
|
58
59
|
ref,
|
|
@@ -81,6 +82,8 @@ export const TreeNode = forwardRef<HTMLDivElement, TreeNodeComponentProps>(
|
|
|
81
82
|
|
|
82
83
|
const contentRef = useRef<HTMLDivElement | null>(null);
|
|
83
84
|
|
|
85
|
+
const anchorRef = useRef<HTMLAnchorElement | null>(null);
|
|
86
|
+
|
|
84
87
|
const isExpandable = Array.isArray(nested);
|
|
85
88
|
const isExpanded = isExpandable ? expandedNodes?.includes(id) : undefined;
|
|
86
89
|
|
|
@@ -119,11 +122,24 @@ export const TreeNode = forwardRef<HTMLDivElement, TreeNodeComponentProps>(
|
|
|
119
122
|
disabled,
|
|
120
123
|
nested,
|
|
121
124
|
onClick,
|
|
125
|
+
href,
|
|
122
126
|
},
|
|
123
127
|
e,
|
|
124
128
|
);
|
|
125
129
|
};
|
|
126
130
|
|
|
131
|
+
const handleAnchorClick = (e: React.MouseEvent<Element>) => {
|
|
132
|
+
e.stopPropagation();
|
|
133
|
+
if (e?.metaKey || e?.ctrlKey || e?.button === 1) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (onClick) {
|
|
138
|
+
e.preventDefault();
|
|
139
|
+
handleClick(e);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
127
143
|
const handleSelect = () => {
|
|
128
144
|
onSelect(
|
|
129
145
|
{
|
|
@@ -209,6 +225,11 @@ export const TreeNode = forwardRef<HTMLDivElement, TreeNodeComponentProps>(
|
|
|
209
225
|
case 'Enter': {
|
|
210
226
|
e.preventDefault();
|
|
211
227
|
handleSelect();
|
|
228
|
+
|
|
229
|
+
if (href && anchorRef.current) {
|
|
230
|
+
anchorRef.current.click();
|
|
231
|
+
}
|
|
232
|
+
|
|
212
233
|
return;
|
|
213
234
|
}
|
|
214
235
|
default:
|
|
@@ -296,8 +317,10 @@ export const TreeNode = forwardRef<HTMLDivElement, TreeNodeComponentProps>(
|
|
|
296
317
|
)}
|
|
297
318
|
|
|
298
319
|
<Typography.SansBodyM tag='div' className={styles.treeNodeTitle}>
|
|
299
|
-
|
|
300
|
-
|
|
320
|
+
<TreeNodeHref href={href} onClick={handleAnchorClick} ref={anchorRef}>
|
|
321
|
+
{typeof title === 'string' && <TruncateString text={title} data-test-id={TEST_IDS.title} />}
|
|
322
|
+
{typeof title !== 'string' && title({ id, disabled, nested } as TreeNodeProps)}
|
|
323
|
+
</TreeNodeHref>
|
|
301
324
|
</Typography.SansBodyM>
|
|
302
325
|
|
|
303
326
|
{getNodeActions && (
|
|
@@ -12,8 +12,8 @@ import { stopPropagationClick, stopPropagationFocus } from '../utils';
|
|
|
12
12
|
type TreeNodeActionsProps = {
|
|
13
13
|
isDroplistOpen: boolean;
|
|
14
14
|
setDroplistOpen: Dispatch<SetStateAction<boolean>>;
|
|
15
|
-
getNodeActions(node: TreeNodeProps): ItemProps[];
|
|
16
|
-
node: TreeNodeProps
|
|
15
|
+
getNodeActions(node: Omit<TreeNodeProps, 'href'>): ItemProps[];
|
|
16
|
+
node: Omit<TreeNodeProps, 'href'>;
|
|
17
17
|
isDroplistTriggerFocused: boolean;
|
|
18
18
|
focusNode(): void;
|
|
19
19
|
onBlurActions(): void;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { forwardRef, MouseEventHandler, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
import { TEST_IDS } from '../../../constants';
|
|
4
|
+
import styles from '../styles.module.scss';
|
|
5
|
+
|
|
6
|
+
export type TreeNodeHrefProps = {
|
|
7
|
+
href?: string;
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
onClick?: MouseEventHandler<HTMLAnchorElement>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const TreeNodeHref = forwardRef<HTMLAnchorElement, TreeNodeHrefProps>(({ href, children, onClick }, ref) =>
|
|
13
|
+
href ? (
|
|
14
|
+
<a
|
|
15
|
+
href={href}
|
|
16
|
+
className={styles.treeNodeLink}
|
|
17
|
+
onClick={onClick}
|
|
18
|
+
data-test-id={TEST_IDS.link}
|
|
19
|
+
tabIndex={-2}
|
|
20
|
+
ref={ref}
|
|
21
|
+
>
|
|
22
|
+
{children}
|
|
23
|
+
</a>
|
|
24
|
+
) : (
|
|
25
|
+
children
|
|
26
|
+
),
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
TreeNodeHref.displayName = 'TreeNodeHref';
|
package/src/types.ts
CHANGED