@carbon-labs/react-ui-shell 0.19.0 → 0.21.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/README.md +1 -0
- package/es/components/SideNav.js +4 -2
- package/es/components/SideNavLink.d.ts +57 -0
- package/es/components/SideNavLink.js +92 -0
- package/es/components/SideNavLinkPopover.d.ts +135 -0
- package/es/components/SideNavLinkPopover.js +90 -0
- package/es/components/SideNavMenu.js +0 -17
- package/es/components/SideNavMenuItem.d.ts +5 -6
- package/es/components/SideNavMenuItem.js +13 -24
- package/es/index.d.ts +2 -0
- package/es/index.js +2 -0
- package/lib/components/SideNav.js +4 -2
- package/lib/components/SideNavLink.d.ts +57 -0
- package/lib/components/SideNavLink.js +97 -0
- package/lib/components/SideNavLinkPopover.d.ts +135 -0
- package/lib/components/SideNavLinkPopover.js +95 -0
- package/lib/components/SideNavMenu.js +0 -17
- package/lib/components/SideNavMenuItem.d.ts +5 -6
- package/lib/components/SideNavMenuItem.js +11 -22
- package/lib/index.d.ts +2 -0
- package/lib/index.js +4 -0
- package/package.json +2 -2
- package/scss/styles/_side-nav.scss +72 -0
package/README.md
CHANGED
package/es/components/SideNav.js
CHANGED
|
@@ -158,7 +158,9 @@ function SideNavRenderFunction(_ref, ref) {
|
|
|
158
158
|
*/
|
|
159
159
|
function parentSideNavMenu(node) {
|
|
160
160
|
const parentNode = node.parentElement?.closest(`.${prefix}--side-nav__item`);
|
|
161
|
-
if (parentNode)
|
|
161
|
+
if (parentNode) {
|
|
162
|
+
return parentNode;
|
|
163
|
+
}
|
|
162
164
|
return node;
|
|
163
165
|
}
|
|
164
166
|
if (addFocusListeners) {
|
|
@@ -340,9 +342,9 @@ function SideNavRenderFunction(_ref, ref) {
|
|
|
340
342
|
};
|
|
341
343
|
return /*#__PURE__*/React.createElement(SideNavContext.Provider, {
|
|
342
344
|
value: {
|
|
345
|
+
expanded,
|
|
343
346
|
isRail,
|
|
344
347
|
navType,
|
|
345
|
-
expanded: expanded,
|
|
346
348
|
isTreeview: internalIsTreeview,
|
|
347
349
|
setIsTreeview
|
|
348
350
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2025
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
import { ComponentType, ElementType, ForwardedRef, ReactNode, WeakValidationMap } from 'react';
|
|
8
|
+
import { LinkProps } from './Link';
|
|
9
|
+
export type SideNavLinkProps<E extends ElementType> = LinkProps<E> & {
|
|
10
|
+
/**
|
|
11
|
+
* Required props for the accessibility label
|
|
12
|
+
*/
|
|
13
|
+
'aria-label'?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Required props for the accessibility label
|
|
16
|
+
*/
|
|
17
|
+
'aria-labelledby'?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Specify the text content for the link
|
|
20
|
+
*/
|
|
21
|
+
children?: ReactNode;
|
|
22
|
+
/**
|
|
23
|
+
* Provide an optional class to be applied to the containing node
|
|
24
|
+
*/
|
|
25
|
+
className?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Specify whether the link is the current page
|
|
28
|
+
*/
|
|
29
|
+
isActive?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Property to indicate if the side nav container is open (or not). Use to
|
|
32
|
+
* keep local state and styling in step with the SideNav expansion state.
|
|
33
|
+
*/
|
|
34
|
+
isSideNavExpanded?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Specify if this is a large variation of the SideNavLink
|
|
37
|
+
*/
|
|
38
|
+
large?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Provide an icon to render in the side navigation link. Should be a React class.
|
|
41
|
+
*/
|
|
42
|
+
renderIcon?: ComponentType;
|
|
43
|
+
/**
|
|
44
|
+
* Optional prop to specify the tabIndex of the button. If undefined, it will be applied default validation
|
|
45
|
+
*/
|
|
46
|
+
tabIndex?: number;
|
|
47
|
+
};
|
|
48
|
+
export interface SideNavLinkComponent {
|
|
49
|
+
<E extends ElementType = 'a'>(props: SideNavLinkProps<E> & {
|
|
50
|
+
ref?: ForwardedRef<ElementType>;
|
|
51
|
+
}): JSX.Element | null;
|
|
52
|
+
displayName?: string;
|
|
53
|
+
propTypes?: WeakValidationMap<SideNavLinkProps<any>>;
|
|
54
|
+
}
|
|
55
|
+
export declare const SideNavLink: SideNavLinkComponent;
|
|
56
|
+
export declare const createCustomSideNavLink: (element: any) => (props: any) => import("react/jsx-runtime").JSX.Element;
|
|
57
|
+
export default SideNavLink;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2024
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { extends as _extends } from '../_virtual/_rollupPluginBabelHelpers.js';
|
|
9
|
+
import cx from '../_virtual/index.js';
|
|
10
|
+
import PropTypes from 'prop-types';
|
|
11
|
+
import React, { forwardRef, useContext } from 'react';
|
|
12
|
+
import Link, { LinkPropTypes } from './Link.js';
|
|
13
|
+
import { SideNavItem, SideNavLinkText, SideNavIcon } from '@carbon/react';
|
|
14
|
+
import { usePrefix } from '../internal/usePrefix.js';
|
|
15
|
+
import { SideNavContext } from './SideNav.js';
|
|
16
|
+
import { SideNavLinkPopover } from './SideNavLinkPopover.js';
|
|
17
|
+
|
|
18
|
+
const SideNavLink = /*#__PURE__*/forwardRef(function SideNavLink(_ref, ref) {
|
|
19
|
+
let {
|
|
20
|
+
children,
|
|
21
|
+
className: customClassName,
|
|
22
|
+
renderIcon: IconElement,
|
|
23
|
+
isActive,
|
|
24
|
+
isSideNavExpanded,
|
|
25
|
+
large = false,
|
|
26
|
+
tabIndex,
|
|
27
|
+
...rest
|
|
28
|
+
} = _ref;
|
|
29
|
+
const {
|
|
30
|
+
expanded,
|
|
31
|
+
isRail,
|
|
32
|
+
navType
|
|
33
|
+
} = useContext(SideNavContext);
|
|
34
|
+
const prefix = usePrefix();
|
|
35
|
+
const className = cx({
|
|
36
|
+
[`${prefix}--side-nav__link`]: true,
|
|
37
|
+
[`${prefix}--side-nav__link--current`]: isActive,
|
|
38
|
+
[customClassName]: !!customClassName
|
|
39
|
+
});
|
|
40
|
+
const SideNavLinkIcon = IconElement && /*#__PURE__*/React.createElement(SideNavIcon, {
|
|
41
|
+
small: true
|
|
42
|
+
}, /*#__PURE__*/React.createElement(IconElement, null));
|
|
43
|
+
if (!expanded && navType === 'panel') {
|
|
44
|
+
return /*#__PURE__*/React.createElement(SideNavLinkPopover, _extends({
|
|
45
|
+
align: "right",
|
|
46
|
+
label: children
|
|
47
|
+
}, rest), SideNavLinkIcon);
|
|
48
|
+
}
|
|
49
|
+
return /*#__PURE__*/React.createElement(SideNavItem, {
|
|
50
|
+
large: large
|
|
51
|
+
}, /*#__PURE__*/React.createElement(Link, _extends({}, rest, {
|
|
52
|
+
className: className,
|
|
53
|
+
ref: ref,
|
|
54
|
+
tabIndex: tabIndex === undefined ? !isSideNavExpanded && !isRail ? -1 : 0 : tabIndex
|
|
55
|
+
}), SideNavLinkIcon, /*#__PURE__*/React.createElement(SideNavLinkText, null, children)));
|
|
56
|
+
});
|
|
57
|
+
SideNavLink.displayName = 'SideNavLink';
|
|
58
|
+
SideNavLink.propTypes = {
|
|
59
|
+
...LinkPropTypes,
|
|
60
|
+
/**
|
|
61
|
+
* Specify the text content for the link
|
|
62
|
+
*/
|
|
63
|
+
children: PropTypes.node,
|
|
64
|
+
/**
|
|
65
|
+
* Provide an optional class to be applied to the containing node
|
|
66
|
+
*/
|
|
67
|
+
className: PropTypes.string,
|
|
68
|
+
/**
|
|
69
|
+
* Specify whether the link is the current page
|
|
70
|
+
*/
|
|
71
|
+
isActive: PropTypes.bool,
|
|
72
|
+
/**
|
|
73
|
+
* Property to indicate if the side nav container is open (or not). Use to
|
|
74
|
+
* keep local state and styling in step with the SideNav expansion state.
|
|
75
|
+
*/
|
|
76
|
+
isSideNavExpanded: PropTypes.bool,
|
|
77
|
+
/**
|
|
78
|
+
* Specify if this is a large variation of the SideNavLink
|
|
79
|
+
*/
|
|
80
|
+
large: PropTypes.bool,
|
|
81
|
+
/**
|
|
82
|
+
* Provide an icon to render in the side navigation link. Should be a React class.
|
|
83
|
+
*/
|
|
84
|
+
// @ts-expect-error - PropTypes are unable to cover this case.
|
|
85
|
+
renderIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
|
|
86
|
+
/**
|
|
87
|
+
* Optional prop to specify the tabIndex of the button. If undefined, it will be applied default validation
|
|
88
|
+
*/
|
|
89
|
+
tabIndex: PropTypes.number
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export { SideNavLink, SideNavLink as default };
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2016, 2023
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
import PropTypes from 'prop-types';
|
|
8
|
+
import React, { ReactNode } from 'react';
|
|
9
|
+
import { IconButtonAlignment, ButtonSize } from '@carbon/react';
|
|
10
|
+
interface IconButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
11
|
+
/**
|
|
12
|
+
* Specify how the trigger should align with the tooltip
|
|
13
|
+
*/
|
|
14
|
+
align?: IconButtonAlignment;
|
|
15
|
+
/**
|
|
16
|
+
* **Experimental**: Will attempt to automatically align the tooltip
|
|
17
|
+
*/
|
|
18
|
+
autoAlign?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Optionally specify an href for your IconButton to become an `<a>` element
|
|
21
|
+
*/
|
|
22
|
+
href?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Provide an icon or asset to be rendered inside of the IconButton
|
|
25
|
+
*/
|
|
26
|
+
children?: ReactNode;
|
|
27
|
+
/**
|
|
28
|
+
* Specify an optional className to be added to your Button
|
|
29
|
+
*/
|
|
30
|
+
className?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Determines whether the tooltip should close when inner content is activated (click, Enter or Space)
|
|
33
|
+
*/
|
|
34
|
+
closeOnActivation?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Specify whether the tooltip should be open when it first renders
|
|
37
|
+
*/
|
|
38
|
+
defaultOpen?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Specify whether the Button should be disabled, or not
|
|
41
|
+
*/
|
|
42
|
+
disabled?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Specify the duration in milliseconds to delay before displaying the tooltip
|
|
45
|
+
*/
|
|
46
|
+
enterDelayMs?: number;
|
|
47
|
+
/**
|
|
48
|
+
* Specify whether the IconButton is currently selected
|
|
49
|
+
*/
|
|
50
|
+
isSelected?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Provide the label to be rendered inside of the Tooltip. The label will use
|
|
53
|
+
* `aria-labelledby` and will fully describe the child node that is provided.
|
|
54
|
+
* This means that if you have text in the child node it will not be
|
|
55
|
+
* announced to the screen reader.
|
|
56
|
+
*/
|
|
57
|
+
label: ReactNode;
|
|
58
|
+
/**
|
|
59
|
+
* Specify the duration in milliseconds to delay before hiding the tooltip
|
|
60
|
+
*/
|
|
61
|
+
leaveDelayMs?: number;
|
|
62
|
+
/**
|
|
63
|
+
* Specify the size of the Button. Defaults to `md`.
|
|
64
|
+
*/
|
|
65
|
+
size?: ButtonSize;
|
|
66
|
+
/**
|
|
67
|
+
* Specify an optional className to be added to your Tooltip wrapper
|
|
68
|
+
*/
|
|
69
|
+
wrapperClasses?: string;
|
|
70
|
+
}
|
|
71
|
+
export declare function SideNavLinkPopover({ className, children, ...rest }: IconButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
72
|
+
export declare namespace SideNavLinkPopover {
|
|
73
|
+
var propTypes: {
|
|
74
|
+
/**
|
|
75
|
+
* Specify how the trigger should align with the tooltip
|
|
76
|
+
*/
|
|
77
|
+
align: PropTypes.Requireable<string>;
|
|
78
|
+
/**
|
|
79
|
+
* **Experimental**: Will attempt to automatically align the tooltip
|
|
80
|
+
*/
|
|
81
|
+
autoAlign: PropTypes.Requireable<boolean>;
|
|
82
|
+
/**
|
|
83
|
+
* Provide an icon or asset to be rendered inside of the IconButton
|
|
84
|
+
*/
|
|
85
|
+
children: PropTypes.Requireable<PropTypes.ReactNodeLike>;
|
|
86
|
+
/**
|
|
87
|
+
* Specify an optional className to be added to your Button
|
|
88
|
+
*/
|
|
89
|
+
className: PropTypes.Requireable<string>;
|
|
90
|
+
/**
|
|
91
|
+
* Determines whether the tooltip should close when inner content is activated (click, Enter or Space)
|
|
92
|
+
*/
|
|
93
|
+
closeOnActivation: PropTypes.Requireable<boolean>;
|
|
94
|
+
/**
|
|
95
|
+
* Specify whether the tooltip should be open when it first renders
|
|
96
|
+
*/
|
|
97
|
+
defaultOpen: PropTypes.Requireable<boolean>;
|
|
98
|
+
/**
|
|
99
|
+
* Specify whether the Button should be disabled, or not
|
|
100
|
+
*/
|
|
101
|
+
disabled: PropTypes.Requireable<boolean>;
|
|
102
|
+
/**
|
|
103
|
+
* Specify the duration in milliseconds to delay before displaying the tooltip
|
|
104
|
+
*/
|
|
105
|
+
enterDelayMs: PropTypes.Requireable<number>;
|
|
106
|
+
/**
|
|
107
|
+
* Optionally specify an href for your IconButton to become an `<a>` element
|
|
108
|
+
*/
|
|
109
|
+
href: PropTypes.Requireable<string>;
|
|
110
|
+
/**
|
|
111
|
+
* Specify whether the IconButton is currently selected
|
|
112
|
+
*/
|
|
113
|
+
isSelected: PropTypes.Requireable<boolean>;
|
|
114
|
+
/**
|
|
115
|
+
* Provide the label to be rendered inside of the Tooltip. The label will use
|
|
116
|
+
* `aria-labelledby` and will fully describe the child node that is provided.
|
|
117
|
+
* This means that if you have text in the child node it will not be
|
|
118
|
+
* announced to the screen reader.
|
|
119
|
+
*/
|
|
120
|
+
label: PropTypes.Validator<NonNullable<PropTypes.ReactNodeLike>>;
|
|
121
|
+
/**
|
|
122
|
+
* Specify the duration in milliseconds to delay before hiding the tooltip
|
|
123
|
+
*/
|
|
124
|
+
leaveDelayMs: PropTypes.Requireable<number>;
|
|
125
|
+
/**
|
|
126
|
+
* Specify the size of the Button. Defaults to `md`.
|
|
127
|
+
*/
|
|
128
|
+
size: PropTypes.Requireable<string>;
|
|
129
|
+
/**
|
|
130
|
+
* Specify an optional className to be added to your Tooltip wrapper
|
|
131
|
+
*/
|
|
132
|
+
wrapperClasses: PropTypes.Requireable<string>;
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
export default SideNavLinkPopover;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2024
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { extends as _extends } from '../_virtual/_rollupPluginBabelHelpers.js';
|
|
9
|
+
import cx from '../_virtual/index.js';
|
|
10
|
+
import PropTypes from 'prop-types';
|
|
11
|
+
import React from 'react';
|
|
12
|
+
import { IconButton } from '@carbon/react';
|
|
13
|
+
import { usePrefix } from '../internal/usePrefix.js';
|
|
14
|
+
|
|
15
|
+
function SideNavLinkPopover(_ref) {
|
|
16
|
+
let {
|
|
17
|
+
className,
|
|
18
|
+
children,
|
|
19
|
+
...rest
|
|
20
|
+
} = _ref;
|
|
21
|
+
const prefix = usePrefix();
|
|
22
|
+
return /*#__PURE__*/React.createElement(IconButton, _extends({
|
|
23
|
+
className: cx(className, `${prefix}--side-nav-link-popover`),
|
|
24
|
+
kind: "ghost"
|
|
25
|
+
}, rest), children);
|
|
26
|
+
}
|
|
27
|
+
SideNavLinkPopover.propTypes = {
|
|
28
|
+
/**
|
|
29
|
+
* Specify how the trigger should align with the tooltip
|
|
30
|
+
*/
|
|
31
|
+
align: PropTypes.oneOf(['top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'left', 'left-end', 'left-start', 'right', 'right-end', 'right-start']),
|
|
32
|
+
/**
|
|
33
|
+
* **Experimental**: Will attempt to automatically align the tooltip
|
|
34
|
+
*/
|
|
35
|
+
autoAlign: PropTypes.bool,
|
|
36
|
+
/**
|
|
37
|
+
* Provide an icon or asset to be rendered inside of the IconButton
|
|
38
|
+
*/
|
|
39
|
+
children: PropTypes.node,
|
|
40
|
+
/**
|
|
41
|
+
* Specify an optional className to be added to your Button
|
|
42
|
+
*/
|
|
43
|
+
className: PropTypes.string,
|
|
44
|
+
/**
|
|
45
|
+
* Determines whether the tooltip should close when inner content is activated (click, Enter or Space)
|
|
46
|
+
*/
|
|
47
|
+
closeOnActivation: PropTypes.bool,
|
|
48
|
+
/**
|
|
49
|
+
* Specify whether the tooltip should be open when it first renders
|
|
50
|
+
*/
|
|
51
|
+
defaultOpen: PropTypes.bool,
|
|
52
|
+
/**
|
|
53
|
+
* Specify whether the Button should be disabled, or not
|
|
54
|
+
*/
|
|
55
|
+
disabled: PropTypes.bool,
|
|
56
|
+
/**
|
|
57
|
+
* Specify the duration in milliseconds to delay before displaying the tooltip
|
|
58
|
+
*/
|
|
59
|
+
enterDelayMs: PropTypes.number,
|
|
60
|
+
/**
|
|
61
|
+
* Optionally specify an href for your IconButton to become an `<a>` element
|
|
62
|
+
*/
|
|
63
|
+
href: PropTypes.string,
|
|
64
|
+
/**
|
|
65
|
+
* Specify whether the IconButton is currently selected
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
isSelected: PropTypes.bool,
|
|
69
|
+
/**
|
|
70
|
+
* Provide the label to be rendered inside of the Tooltip. The label will use
|
|
71
|
+
* `aria-labelledby` and will fully describe the child node that is provided.
|
|
72
|
+
* This means that if you have text in the child node it will not be
|
|
73
|
+
* announced to the screen reader.
|
|
74
|
+
*/
|
|
75
|
+
label: PropTypes.node.isRequired,
|
|
76
|
+
/**
|
|
77
|
+
* Specify the duration in milliseconds to delay before hiding the tooltip
|
|
78
|
+
*/
|
|
79
|
+
leaveDelayMs: PropTypes.number,
|
|
80
|
+
/**
|
|
81
|
+
* Specify the size of the Button. Defaults to `md`.
|
|
82
|
+
*/
|
|
83
|
+
size: PropTypes.oneOf(['sm', 'md', 'lg']),
|
|
84
|
+
/**
|
|
85
|
+
* Specify an optional className to be added to your Tooltip wrapper
|
|
86
|
+
*/
|
|
87
|
+
wrapperClasses: PropTypes.string
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export { SideNavLinkPopover, SideNavLinkPopover as default };
|
|
@@ -96,23 +96,6 @@ const SideNavMenu = /*#__PURE__*/React.forwardRef(function SideNavMenu(_ref, ref
|
|
|
96
96
|
|
|
97
97
|
// if depth is more than 0, that means its nested, thus we set treeview mode
|
|
98
98
|
setIsTreeview?.(true);
|
|
99
|
-
if (isTreeview) {
|
|
100
|
-
const calcButtonOffset = () => {
|
|
101
|
-
// menu with icon
|
|
102
|
-
if (children && IconElement) {
|
|
103
|
-
return depth + 3;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// menu without icon
|
|
107
|
-
if (children) {
|
|
108
|
-
return depth * 4;
|
|
109
|
-
}
|
|
110
|
-
return depth;
|
|
111
|
-
};
|
|
112
|
-
if (buttonRef.current) {
|
|
113
|
-
buttonRef.current.style.paddingLeft = `${calcButtonOffset()}rem`;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
99
|
}, [isTreeview]);
|
|
117
100
|
|
|
118
101
|
/**
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* This source code is licensed under the Apache-2.0 license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
|
-
import React, { ElementType, ComponentProps } from 'react';
|
|
7
|
+
import React, { ElementType, ComponentProps, ComponentType } from 'react';
|
|
8
8
|
import Link from './Link';
|
|
9
9
|
export interface SideNavMenuItemProps extends ComponentProps<typeof Link> {
|
|
10
10
|
/**
|
|
@@ -15,11 +15,6 @@ export interface SideNavMenuItemProps extends ComponentProps<typeof Link> {
|
|
|
15
15
|
* Provide an optional class to be applied to the containing node
|
|
16
16
|
*/
|
|
17
17
|
className?: string;
|
|
18
|
-
/**
|
|
19
|
-
* **Note:** this is controlled by the parent SideNavMenu component, do not set manually.
|
|
20
|
-
* SideNavMenu depth to determine spacing
|
|
21
|
-
*/
|
|
22
|
-
depth?: number;
|
|
23
18
|
/**
|
|
24
19
|
* Optionally specify whether the link is "active". An active link is one that
|
|
25
20
|
* has an href that is the same as the current page. Can also pass in
|
|
@@ -34,5 +29,9 @@ export interface SideNavMenuItemProps extends ComponentProps<typeof Link> {
|
|
|
34
29
|
* Optional component to render instead of default Link
|
|
35
30
|
*/
|
|
36
31
|
as?: ElementType;
|
|
32
|
+
/**
|
|
33
|
+
* Provide an icon to render in the side navigation link. Should be a React class.
|
|
34
|
+
*/
|
|
35
|
+
renderIcon?: ComponentType;
|
|
37
36
|
}
|
|
38
37
|
export declare const SideNavMenuItem: React.ForwardRefExoticComponent<Omit<SideNavMenuItemProps, "ref"> & React.RefAttributes<HTMLElement>>;
|
|
@@ -8,11 +8,10 @@
|
|
|
8
8
|
import { extends as _extends } from '../_virtual/_rollupPluginBabelHelpers.js';
|
|
9
9
|
import cx from '../_virtual/index.js';
|
|
10
10
|
import PropTypes from 'prop-types';
|
|
11
|
-
import React, { useContext
|
|
12
|
-
import { SideNavLinkText } from '@carbon/react';
|
|
11
|
+
import React, { useContext } from 'react';
|
|
12
|
+
import { SideNavIcon, SideNavLinkText } from '@carbon/react';
|
|
13
13
|
import Link from './Link.js';
|
|
14
14
|
import { usePrefix } from '../internal/usePrefix.js';
|
|
15
|
-
import { useMergedRefs } from '../internal/useMergedRefs.js';
|
|
16
15
|
import { SideNavContext } from './SideNav.js';
|
|
17
16
|
|
|
18
17
|
const SideNavMenuItem = /*#__PURE__*/React.forwardRef(function SideNavMenuItem(props, ref) {
|
|
@@ -20,30 +19,19 @@ const SideNavMenuItem = /*#__PURE__*/React.forwardRef(function SideNavMenuItem(p
|
|
|
20
19
|
const {
|
|
21
20
|
children,
|
|
22
21
|
className: customClassName,
|
|
23
|
-
depth: propDepth,
|
|
24
22
|
as: Component = Link,
|
|
25
23
|
isActive,
|
|
24
|
+
renderIcon: IconElement,
|
|
26
25
|
...rest
|
|
27
26
|
} = props;
|
|
28
27
|
const {
|
|
29
28
|
isTreeview
|
|
30
29
|
} = useContext(SideNavContext);
|
|
31
30
|
const className = cx(`${prefix}--side-nav__menu-item`, customClassName);
|
|
32
|
-
const depth = propDepth;
|
|
33
31
|
const linkClassName = cx({
|
|
34
32
|
[`${prefix}--side-nav__link`]: true,
|
|
35
33
|
[`${prefix}--side-nav__link--current`]: isActive
|
|
36
34
|
});
|
|
37
|
-
const linkRef = useRef(null);
|
|
38
|
-
const itemRef = useMergedRefs([linkRef, ref]);
|
|
39
|
-
useEffect(() => {
|
|
40
|
-
const calcLinkOffset = () => {
|
|
41
|
-
return 4 + Math.max(0, depth - 1) * 1;
|
|
42
|
-
};
|
|
43
|
-
if (linkRef.current) {
|
|
44
|
-
linkRef.current.style.paddingLeft = `${calcLinkOffset()}rem`;
|
|
45
|
-
}
|
|
46
|
-
}, [isTreeview]);
|
|
47
35
|
return /*#__PURE__*/React.createElement("li", {
|
|
48
36
|
className: className
|
|
49
37
|
}, /*#__PURE__*/React.createElement(Component, _extends({}, rest, {
|
|
@@ -51,11 +39,17 @@ const SideNavMenuItem = /*#__PURE__*/React.forwardRef(function SideNavMenuItem(p
|
|
|
51
39
|
role: isTreeview ? 'treeitem' : undefined,
|
|
52
40
|
className: linkClassName,
|
|
53
41
|
tabIndex: isTreeview ? -1 : 0,
|
|
54
|
-
ref:
|
|
55
|
-
}), /*#__PURE__*/React.createElement(
|
|
42
|
+
ref: ref
|
|
43
|
+
}), IconElement && /*#__PURE__*/React.createElement(SideNavIcon, {
|
|
44
|
+
small: true
|
|
45
|
+
}, /*#__PURE__*/React.createElement(IconElement, null)), /*#__PURE__*/React.createElement(SideNavLinkText, null, children)));
|
|
56
46
|
});
|
|
57
47
|
SideNavMenuItem.displayName = 'SideNavMenuItem';
|
|
58
48
|
SideNavMenuItem.propTypes = {
|
|
49
|
+
/**
|
|
50
|
+
* Optional component to render instead of default Link
|
|
51
|
+
*/
|
|
52
|
+
as: PropTypes.elementType,
|
|
59
53
|
/**
|
|
60
54
|
* Specify the children to be rendered inside of the `SideNavMenuItem`
|
|
61
55
|
*/
|
|
@@ -64,11 +58,6 @@ SideNavMenuItem.propTypes = {
|
|
|
64
58
|
* Provide an optional class to be applied to the containing node
|
|
65
59
|
*/
|
|
66
60
|
className: PropTypes.string,
|
|
67
|
-
/**
|
|
68
|
-
* **Note:** this is controlled by the parent SideNavMenu component, do not set manually.
|
|
69
|
-
* SideNavMenu depth to determine spacing
|
|
70
|
-
*/
|
|
71
|
-
depth: PropTypes.number,
|
|
72
61
|
/**
|
|
73
62
|
* Optionally provide an href for the underlying li`
|
|
74
63
|
*/
|
|
@@ -80,9 +69,9 @@ SideNavMenuItem.propTypes = {
|
|
|
80
69
|
*/
|
|
81
70
|
isActive: PropTypes.bool,
|
|
82
71
|
/**
|
|
83
|
-
*
|
|
72
|
+
* Provide an icon to render in the side navigation link. Should be a React class.
|
|
84
73
|
*/
|
|
85
|
-
|
|
74
|
+
renderIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.object])
|
|
86
75
|
};
|
|
87
76
|
|
|
88
77
|
export { SideNavMenuItem };
|
package/es/index.d.ts
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
export { SideNav, SIDE_NAV_TYPE } from './components/SideNav.js';
|
|
10
10
|
export { SideNavItems } from './components/SideNavItems.js';
|
|
11
|
+
export { SideNavLink } from './components/SideNavLink.js';
|
|
12
|
+
export { SideNavLinkPopover } from './components/SideNavLinkPopover.js';
|
|
11
13
|
export { SideNavMenu } from './components/SideNavMenu.js';
|
|
12
14
|
export { SideNavMenuItem } from './components/SideNavMenuItem.js';
|
|
13
15
|
export { HeaderPanel } from './components/HeaderPanel';
|
package/es/index.js
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
export { SIDE_NAV_TYPE, SideNav } from './components/SideNav.js';
|
|
9
9
|
export { SideNavItems } from './components/SideNavItems.js';
|
|
10
|
+
export { SideNavLink } from './components/SideNavLink.js';
|
|
11
|
+
export { SideNavLinkPopover } from './components/SideNavLinkPopover.js';
|
|
10
12
|
export { SideNavMenu } from './components/SideNavMenu.js';
|
|
11
13
|
export { SideNavMenuItem } from './components/SideNavMenuItem.js';
|
|
12
14
|
export { HeaderPanel } from './components/HeaderPanel.js';
|
|
@@ -160,7 +160,9 @@ function SideNavRenderFunction(_ref, ref) {
|
|
|
160
160
|
*/
|
|
161
161
|
function parentSideNavMenu(node) {
|
|
162
162
|
const parentNode = node.parentElement?.closest(`.${prefix}--side-nav__item`);
|
|
163
|
-
if (parentNode)
|
|
163
|
+
if (parentNode) {
|
|
164
|
+
return parentNode;
|
|
165
|
+
}
|
|
164
166
|
return node;
|
|
165
167
|
}
|
|
166
168
|
if (addFocusListeners) {
|
|
@@ -342,9 +344,9 @@ function SideNavRenderFunction(_ref, ref) {
|
|
|
342
344
|
};
|
|
343
345
|
return /*#__PURE__*/React.createElement(SideNavContext.Provider, {
|
|
344
346
|
value: {
|
|
347
|
+
expanded,
|
|
345
348
|
isRail,
|
|
346
349
|
navType,
|
|
347
|
-
expanded: expanded,
|
|
348
350
|
isTreeview: internalIsTreeview,
|
|
349
351
|
setIsTreeview
|
|
350
352
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2025
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
import { ComponentType, ElementType, ForwardedRef, ReactNode, WeakValidationMap } from 'react';
|
|
8
|
+
import { LinkProps } from './Link';
|
|
9
|
+
export type SideNavLinkProps<E extends ElementType> = LinkProps<E> & {
|
|
10
|
+
/**
|
|
11
|
+
* Required props for the accessibility label
|
|
12
|
+
*/
|
|
13
|
+
'aria-label'?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Required props for the accessibility label
|
|
16
|
+
*/
|
|
17
|
+
'aria-labelledby'?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Specify the text content for the link
|
|
20
|
+
*/
|
|
21
|
+
children?: ReactNode;
|
|
22
|
+
/**
|
|
23
|
+
* Provide an optional class to be applied to the containing node
|
|
24
|
+
*/
|
|
25
|
+
className?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Specify whether the link is the current page
|
|
28
|
+
*/
|
|
29
|
+
isActive?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Property to indicate if the side nav container is open (or not). Use to
|
|
32
|
+
* keep local state and styling in step with the SideNav expansion state.
|
|
33
|
+
*/
|
|
34
|
+
isSideNavExpanded?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Specify if this is a large variation of the SideNavLink
|
|
37
|
+
*/
|
|
38
|
+
large?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Provide an icon to render in the side navigation link. Should be a React class.
|
|
41
|
+
*/
|
|
42
|
+
renderIcon?: ComponentType;
|
|
43
|
+
/**
|
|
44
|
+
* Optional prop to specify the tabIndex of the button. If undefined, it will be applied default validation
|
|
45
|
+
*/
|
|
46
|
+
tabIndex?: number;
|
|
47
|
+
};
|
|
48
|
+
export interface SideNavLinkComponent {
|
|
49
|
+
<E extends ElementType = 'a'>(props: SideNavLinkProps<E> & {
|
|
50
|
+
ref?: ForwardedRef<ElementType>;
|
|
51
|
+
}): JSX.Element | null;
|
|
52
|
+
displayName?: string;
|
|
53
|
+
propTypes?: WeakValidationMap<SideNavLinkProps<any>>;
|
|
54
|
+
}
|
|
55
|
+
export declare const SideNavLink: SideNavLinkComponent;
|
|
56
|
+
export declare const createCustomSideNavLink: (element: any) => (props: any) => import("react/jsx-runtime").JSX.Element;
|
|
57
|
+
export default SideNavLink;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2024
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
11
|
+
|
|
12
|
+
var _rollupPluginBabelHelpers = require('../_virtual/_rollupPluginBabelHelpers.js');
|
|
13
|
+
var index = require('../_virtual/index.js');
|
|
14
|
+
var PropTypes = require('prop-types');
|
|
15
|
+
var React = require('react');
|
|
16
|
+
var Link = require('./Link.js');
|
|
17
|
+
var react = require('@carbon/react');
|
|
18
|
+
var usePrefix = require('../internal/usePrefix.js');
|
|
19
|
+
var SideNav = require('./SideNav.js');
|
|
20
|
+
var SideNavLinkPopover = require('./SideNavLinkPopover.js');
|
|
21
|
+
|
|
22
|
+
const SideNavLink = /*#__PURE__*/React.forwardRef(function SideNavLink(_ref, ref) {
|
|
23
|
+
let {
|
|
24
|
+
children,
|
|
25
|
+
className: customClassName,
|
|
26
|
+
renderIcon: IconElement,
|
|
27
|
+
isActive,
|
|
28
|
+
isSideNavExpanded,
|
|
29
|
+
large = false,
|
|
30
|
+
tabIndex,
|
|
31
|
+
...rest
|
|
32
|
+
} = _ref;
|
|
33
|
+
const {
|
|
34
|
+
expanded,
|
|
35
|
+
isRail,
|
|
36
|
+
navType
|
|
37
|
+
} = React.useContext(SideNav.SideNavContext);
|
|
38
|
+
const prefix = usePrefix.usePrefix();
|
|
39
|
+
const className = index.default({
|
|
40
|
+
[`${prefix}--side-nav__link`]: true,
|
|
41
|
+
[`${prefix}--side-nav__link--current`]: isActive,
|
|
42
|
+
[customClassName]: !!customClassName
|
|
43
|
+
});
|
|
44
|
+
const SideNavLinkIcon = IconElement && /*#__PURE__*/React.createElement(react.SideNavIcon, {
|
|
45
|
+
small: true
|
|
46
|
+
}, /*#__PURE__*/React.createElement(IconElement, null));
|
|
47
|
+
if (!expanded && navType === 'panel') {
|
|
48
|
+
return /*#__PURE__*/React.createElement(SideNavLinkPopover.SideNavLinkPopover, _rollupPluginBabelHelpers.extends({
|
|
49
|
+
align: "right",
|
|
50
|
+
label: children
|
|
51
|
+
}, rest), SideNavLinkIcon);
|
|
52
|
+
}
|
|
53
|
+
return /*#__PURE__*/React.createElement(react.SideNavItem, {
|
|
54
|
+
large: large
|
|
55
|
+
}, /*#__PURE__*/React.createElement(Link.default, _rollupPluginBabelHelpers.extends({}, rest, {
|
|
56
|
+
className: className,
|
|
57
|
+
ref: ref,
|
|
58
|
+
tabIndex: tabIndex === undefined ? !isSideNavExpanded && !isRail ? -1 : 0 : tabIndex
|
|
59
|
+
}), SideNavLinkIcon, /*#__PURE__*/React.createElement(react.SideNavLinkText, null, children)));
|
|
60
|
+
});
|
|
61
|
+
SideNavLink.displayName = 'SideNavLink';
|
|
62
|
+
SideNavLink.propTypes = {
|
|
63
|
+
...Link.LinkPropTypes,
|
|
64
|
+
/**
|
|
65
|
+
* Specify the text content for the link
|
|
66
|
+
*/
|
|
67
|
+
children: PropTypes.node,
|
|
68
|
+
/**
|
|
69
|
+
* Provide an optional class to be applied to the containing node
|
|
70
|
+
*/
|
|
71
|
+
className: PropTypes.string,
|
|
72
|
+
/**
|
|
73
|
+
* Specify whether the link is the current page
|
|
74
|
+
*/
|
|
75
|
+
isActive: PropTypes.bool,
|
|
76
|
+
/**
|
|
77
|
+
* Property to indicate if the side nav container is open (or not). Use to
|
|
78
|
+
* keep local state and styling in step with the SideNav expansion state.
|
|
79
|
+
*/
|
|
80
|
+
isSideNavExpanded: PropTypes.bool,
|
|
81
|
+
/**
|
|
82
|
+
* Specify if this is a large variation of the SideNavLink
|
|
83
|
+
*/
|
|
84
|
+
large: PropTypes.bool,
|
|
85
|
+
/**
|
|
86
|
+
* Provide an icon to render in the side navigation link. Should be a React class.
|
|
87
|
+
*/
|
|
88
|
+
// @ts-expect-error - PropTypes are unable to cover this case.
|
|
89
|
+
renderIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
|
|
90
|
+
/**
|
|
91
|
+
* Optional prop to specify the tabIndex of the button. If undefined, it will be applied default validation
|
|
92
|
+
*/
|
|
93
|
+
tabIndex: PropTypes.number
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
exports.SideNavLink = SideNavLink;
|
|
97
|
+
exports.default = SideNavLink;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2016, 2023
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
import PropTypes from 'prop-types';
|
|
8
|
+
import React, { ReactNode } from 'react';
|
|
9
|
+
import { IconButtonAlignment, ButtonSize } from '@carbon/react';
|
|
10
|
+
interface IconButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
11
|
+
/**
|
|
12
|
+
* Specify how the trigger should align with the tooltip
|
|
13
|
+
*/
|
|
14
|
+
align?: IconButtonAlignment;
|
|
15
|
+
/**
|
|
16
|
+
* **Experimental**: Will attempt to automatically align the tooltip
|
|
17
|
+
*/
|
|
18
|
+
autoAlign?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Optionally specify an href for your IconButton to become an `<a>` element
|
|
21
|
+
*/
|
|
22
|
+
href?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Provide an icon or asset to be rendered inside of the IconButton
|
|
25
|
+
*/
|
|
26
|
+
children?: ReactNode;
|
|
27
|
+
/**
|
|
28
|
+
* Specify an optional className to be added to your Button
|
|
29
|
+
*/
|
|
30
|
+
className?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Determines whether the tooltip should close when inner content is activated (click, Enter or Space)
|
|
33
|
+
*/
|
|
34
|
+
closeOnActivation?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Specify whether the tooltip should be open when it first renders
|
|
37
|
+
*/
|
|
38
|
+
defaultOpen?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Specify whether the Button should be disabled, or not
|
|
41
|
+
*/
|
|
42
|
+
disabled?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Specify the duration in milliseconds to delay before displaying the tooltip
|
|
45
|
+
*/
|
|
46
|
+
enterDelayMs?: number;
|
|
47
|
+
/**
|
|
48
|
+
* Specify whether the IconButton is currently selected
|
|
49
|
+
*/
|
|
50
|
+
isSelected?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Provide the label to be rendered inside of the Tooltip. The label will use
|
|
53
|
+
* `aria-labelledby` and will fully describe the child node that is provided.
|
|
54
|
+
* This means that if you have text in the child node it will not be
|
|
55
|
+
* announced to the screen reader.
|
|
56
|
+
*/
|
|
57
|
+
label: ReactNode;
|
|
58
|
+
/**
|
|
59
|
+
* Specify the duration in milliseconds to delay before hiding the tooltip
|
|
60
|
+
*/
|
|
61
|
+
leaveDelayMs?: number;
|
|
62
|
+
/**
|
|
63
|
+
* Specify the size of the Button. Defaults to `md`.
|
|
64
|
+
*/
|
|
65
|
+
size?: ButtonSize;
|
|
66
|
+
/**
|
|
67
|
+
* Specify an optional className to be added to your Tooltip wrapper
|
|
68
|
+
*/
|
|
69
|
+
wrapperClasses?: string;
|
|
70
|
+
}
|
|
71
|
+
export declare function SideNavLinkPopover({ className, children, ...rest }: IconButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
72
|
+
export declare namespace SideNavLinkPopover {
|
|
73
|
+
var propTypes: {
|
|
74
|
+
/**
|
|
75
|
+
* Specify how the trigger should align with the tooltip
|
|
76
|
+
*/
|
|
77
|
+
align: PropTypes.Requireable<string>;
|
|
78
|
+
/**
|
|
79
|
+
* **Experimental**: Will attempt to automatically align the tooltip
|
|
80
|
+
*/
|
|
81
|
+
autoAlign: PropTypes.Requireable<boolean>;
|
|
82
|
+
/**
|
|
83
|
+
* Provide an icon or asset to be rendered inside of the IconButton
|
|
84
|
+
*/
|
|
85
|
+
children: PropTypes.Requireable<PropTypes.ReactNodeLike>;
|
|
86
|
+
/**
|
|
87
|
+
* Specify an optional className to be added to your Button
|
|
88
|
+
*/
|
|
89
|
+
className: PropTypes.Requireable<string>;
|
|
90
|
+
/**
|
|
91
|
+
* Determines whether the tooltip should close when inner content is activated (click, Enter or Space)
|
|
92
|
+
*/
|
|
93
|
+
closeOnActivation: PropTypes.Requireable<boolean>;
|
|
94
|
+
/**
|
|
95
|
+
* Specify whether the tooltip should be open when it first renders
|
|
96
|
+
*/
|
|
97
|
+
defaultOpen: PropTypes.Requireable<boolean>;
|
|
98
|
+
/**
|
|
99
|
+
* Specify whether the Button should be disabled, or not
|
|
100
|
+
*/
|
|
101
|
+
disabled: PropTypes.Requireable<boolean>;
|
|
102
|
+
/**
|
|
103
|
+
* Specify the duration in milliseconds to delay before displaying the tooltip
|
|
104
|
+
*/
|
|
105
|
+
enterDelayMs: PropTypes.Requireable<number>;
|
|
106
|
+
/**
|
|
107
|
+
* Optionally specify an href for your IconButton to become an `<a>` element
|
|
108
|
+
*/
|
|
109
|
+
href: PropTypes.Requireable<string>;
|
|
110
|
+
/**
|
|
111
|
+
* Specify whether the IconButton is currently selected
|
|
112
|
+
*/
|
|
113
|
+
isSelected: PropTypes.Requireable<boolean>;
|
|
114
|
+
/**
|
|
115
|
+
* Provide the label to be rendered inside of the Tooltip. The label will use
|
|
116
|
+
* `aria-labelledby` and will fully describe the child node that is provided.
|
|
117
|
+
* This means that if you have text in the child node it will not be
|
|
118
|
+
* announced to the screen reader.
|
|
119
|
+
*/
|
|
120
|
+
label: PropTypes.Validator<NonNullable<PropTypes.ReactNodeLike>>;
|
|
121
|
+
/**
|
|
122
|
+
* Specify the duration in milliseconds to delay before hiding the tooltip
|
|
123
|
+
*/
|
|
124
|
+
leaveDelayMs: PropTypes.Requireable<number>;
|
|
125
|
+
/**
|
|
126
|
+
* Specify the size of the Button. Defaults to `md`.
|
|
127
|
+
*/
|
|
128
|
+
size: PropTypes.Requireable<string>;
|
|
129
|
+
/**
|
|
130
|
+
* Specify an optional className to be added to your Tooltip wrapper
|
|
131
|
+
*/
|
|
132
|
+
wrapperClasses: PropTypes.Requireable<string>;
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
export default SideNavLinkPopover;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2024
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
11
|
+
|
|
12
|
+
var _rollupPluginBabelHelpers = require('../_virtual/_rollupPluginBabelHelpers.js');
|
|
13
|
+
var index = require('../_virtual/index.js');
|
|
14
|
+
var PropTypes = require('prop-types');
|
|
15
|
+
var React = require('react');
|
|
16
|
+
var react = require('@carbon/react');
|
|
17
|
+
var usePrefix = require('../internal/usePrefix.js');
|
|
18
|
+
|
|
19
|
+
function SideNavLinkPopover(_ref) {
|
|
20
|
+
let {
|
|
21
|
+
className,
|
|
22
|
+
children,
|
|
23
|
+
...rest
|
|
24
|
+
} = _ref;
|
|
25
|
+
const prefix = usePrefix.usePrefix();
|
|
26
|
+
return /*#__PURE__*/React.createElement(react.IconButton, _rollupPluginBabelHelpers.extends({
|
|
27
|
+
className: index.default(className, `${prefix}--side-nav-link-popover`),
|
|
28
|
+
kind: "ghost"
|
|
29
|
+
}, rest), children);
|
|
30
|
+
}
|
|
31
|
+
SideNavLinkPopover.propTypes = {
|
|
32
|
+
/**
|
|
33
|
+
* Specify how the trigger should align with the tooltip
|
|
34
|
+
*/
|
|
35
|
+
align: PropTypes.oneOf(['top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'left', 'left-end', 'left-start', 'right', 'right-end', 'right-start']),
|
|
36
|
+
/**
|
|
37
|
+
* **Experimental**: Will attempt to automatically align the tooltip
|
|
38
|
+
*/
|
|
39
|
+
autoAlign: PropTypes.bool,
|
|
40
|
+
/**
|
|
41
|
+
* Provide an icon or asset to be rendered inside of the IconButton
|
|
42
|
+
*/
|
|
43
|
+
children: PropTypes.node,
|
|
44
|
+
/**
|
|
45
|
+
* Specify an optional className to be added to your Button
|
|
46
|
+
*/
|
|
47
|
+
className: PropTypes.string,
|
|
48
|
+
/**
|
|
49
|
+
* Determines whether the tooltip should close when inner content is activated (click, Enter or Space)
|
|
50
|
+
*/
|
|
51
|
+
closeOnActivation: PropTypes.bool,
|
|
52
|
+
/**
|
|
53
|
+
* Specify whether the tooltip should be open when it first renders
|
|
54
|
+
*/
|
|
55
|
+
defaultOpen: PropTypes.bool,
|
|
56
|
+
/**
|
|
57
|
+
* Specify whether the Button should be disabled, or not
|
|
58
|
+
*/
|
|
59
|
+
disabled: PropTypes.bool,
|
|
60
|
+
/**
|
|
61
|
+
* Specify the duration in milliseconds to delay before displaying the tooltip
|
|
62
|
+
*/
|
|
63
|
+
enterDelayMs: PropTypes.number,
|
|
64
|
+
/**
|
|
65
|
+
* Optionally specify an href for your IconButton to become an `<a>` element
|
|
66
|
+
*/
|
|
67
|
+
href: PropTypes.string,
|
|
68
|
+
/**
|
|
69
|
+
* Specify whether the IconButton is currently selected
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
isSelected: PropTypes.bool,
|
|
73
|
+
/**
|
|
74
|
+
* Provide the label to be rendered inside of the Tooltip. The label will use
|
|
75
|
+
* `aria-labelledby` and will fully describe the child node that is provided.
|
|
76
|
+
* This means that if you have text in the child node it will not be
|
|
77
|
+
* announced to the screen reader.
|
|
78
|
+
*/
|
|
79
|
+
label: PropTypes.node.isRequired,
|
|
80
|
+
/**
|
|
81
|
+
* Specify the duration in milliseconds to delay before hiding the tooltip
|
|
82
|
+
*/
|
|
83
|
+
leaveDelayMs: PropTypes.number,
|
|
84
|
+
/**
|
|
85
|
+
* Specify the size of the Button. Defaults to `md`.
|
|
86
|
+
*/
|
|
87
|
+
size: PropTypes.oneOf(['sm', 'md', 'lg']),
|
|
88
|
+
/**
|
|
89
|
+
* Specify an optional className to be added to your Tooltip wrapper
|
|
90
|
+
*/
|
|
91
|
+
wrapperClasses: PropTypes.string
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
exports.SideNavLinkPopover = SideNavLinkPopover;
|
|
95
|
+
exports.default = SideNavLinkPopover;
|
|
@@ -98,23 +98,6 @@ const SideNavMenu = /*#__PURE__*/React.forwardRef(function SideNavMenu(_ref, ref
|
|
|
98
98
|
|
|
99
99
|
// if depth is more than 0, that means its nested, thus we set treeview mode
|
|
100
100
|
setIsTreeview?.(true);
|
|
101
|
-
if (isTreeview) {
|
|
102
|
-
const calcButtonOffset = () => {
|
|
103
|
-
// menu with icon
|
|
104
|
-
if (children && IconElement) {
|
|
105
|
-
return depth + 3;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// menu without icon
|
|
109
|
-
if (children) {
|
|
110
|
-
return depth * 4;
|
|
111
|
-
}
|
|
112
|
-
return depth;
|
|
113
|
-
};
|
|
114
|
-
if (buttonRef.current) {
|
|
115
|
-
buttonRef.current.style.paddingLeft = `${calcButtonOffset()}rem`;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
101
|
}, [isTreeview]);
|
|
119
102
|
|
|
120
103
|
/**
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* This source code is licensed under the Apache-2.0 license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
|
-
import React, { ElementType, ComponentProps } from 'react';
|
|
7
|
+
import React, { ElementType, ComponentProps, ComponentType } from 'react';
|
|
8
8
|
import Link from './Link';
|
|
9
9
|
export interface SideNavMenuItemProps extends ComponentProps<typeof Link> {
|
|
10
10
|
/**
|
|
@@ -15,11 +15,6 @@ export interface SideNavMenuItemProps extends ComponentProps<typeof Link> {
|
|
|
15
15
|
* Provide an optional class to be applied to the containing node
|
|
16
16
|
*/
|
|
17
17
|
className?: string;
|
|
18
|
-
/**
|
|
19
|
-
* **Note:** this is controlled by the parent SideNavMenu component, do not set manually.
|
|
20
|
-
* SideNavMenu depth to determine spacing
|
|
21
|
-
*/
|
|
22
|
-
depth?: number;
|
|
23
18
|
/**
|
|
24
19
|
* Optionally specify whether the link is "active". An active link is one that
|
|
25
20
|
* has an href that is the same as the current page. Can also pass in
|
|
@@ -34,5 +29,9 @@ export interface SideNavMenuItemProps extends ComponentProps<typeof Link> {
|
|
|
34
29
|
* Optional component to render instead of default Link
|
|
35
30
|
*/
|
|
36
31
|
as?: ElementType;
|
|
32
|
+
/**
|
|
33
|
+
* Provide an icon to render in the side navigation link. Should be a React class.
|
|
34
|
+
*/
|
|
35
|
+
renderIcon?: ComponentType;
|
|
37
36
|
}
|
|
38
37
|
export declare const SideNavMenuItem: React.ForwardRefExoticComponent<Omit<SideNavMenuItemProps, "ref"> & React.RefAttributes<HTMLElement>>;
|
|
@@ -14,7 +14,6 @@ var React = require('react');
|
|
|
14
14
|
var react = require('@carbon/react');
|
|
15
15
|
var Link = require('./Link.js');
|
|
16
16
|
var usePrefix = require('../internal/usePrefix.js');
|
|
17
|
-
var useMergedRefs = require('../internal/useMergedRefs.js');
|
|
18
17
|
var SideNav = require('./SideNav.js');
|
|
19
18
|
|
|
20
19
|
const SideNavMenuItem = /*#__PURE__*/React.forwardRef(function SideNavMenuItem(props, ref) {
|
|
@@ -22,30 +21,19 @@ const SideNavMenuItem = /*#__PURE__*/React.forwardRef(function SideNavMenuItem(p
|
|
|
22
21
|
const {
|
|
23
22
|
children,
|
|
24
23
|
className: customClassName,
|
|
25
|
-
depth: propDepth,
|
|
26
24
|
as: Component = Link.default,
|
|
27
25
|
isActive,
|
|
26
|
+
renderIcon: IconElement,
|
|
28
27
|
...rest
|
|
29
28
|
} = props;
|
|
30
29
|
const {
|
|
31
30
|
isTreeview
|
|
32
31
|
} = React.useContext(SideNav.SideNavContext);
|
|
33
32
|
const className = index.default(`${prefix}--side-nav__menu-item`, customClassName);
|
|
34
|
-
const depth = propDepth;
|
|
35
33
|
const linkClassName = index.default({
|
|
36
34
|
[`${prefix}--side-nav__link`]: true,
|
|
37
35
|
[`${prefix}--side-nav__link--current`]: isActive
|
|
38
36
|
});
|
|
39
|
-
const linkRef = React.useRef(null);
|
|
40
|
-
const itemRef = useMergedRefs.useMergedRefs([linkRef, ref]);
|
|
41
|
-
React.useEffect(() => {
|
|
42
|
-
const calcLinkOffset = () => {
|
|
43
|
-
return 4 + Math.max(0, depth - 1) * 1;
|
|
44
|
-
};
|
|
45
|
-
if (linkRef.current) {
|
|
46
|
-
linkRef.current.style.paddingLeft = `${calcLinkOffset()}rem`;
|
|
47
|
-
}
|
|
48
|
-
}, [isTreeview]);
|
|
49
37
|
return /*#__PURE__*/React.createElement("li", {
|
|
50
38
|
className: className
|
|
51
39
|
}, /*#__PURE__*/React.createElement(Component, _rollupPluginBabelHelpers.extends({}, rest, {
|
|
@@ -53,11 +41,17 @@ const SideNavMenuItem = /*#__PURE__*/React.forwardRef(function SideNavMenuItem(p
|
|
|
53
41
|
role: isTreeview ? 'treeitem' : undefined,
|
|
54
42
|
className: linkClassName,
|
|
55
43
|
tabIndex: isTreeview ? -1 : 0,
|
|
56
|
-
ref:
|
|
57
|
-
}), /*#__PURE__*/React.createElement(react.
|
|
44
|
+
ref: ref
|
|
45
|
+
}), IconElement && /*#__PURE__*/React.createElement(react.SideNavIcon, {
|
|
46
|
+
small: true
|
|
47
|
+
}, /*#__PURE__*/React.createElement(IconElement, null)), /*#__PURE__*/React.createElement(react.SideNavLinkText, null, children)));
|
|
58
48
|
});
|
|
59
49
|
SideNavMenuItem.displayName = 'SideNavMenuItem';
|
|
60
50
|
SideNavMenuItem.propTypes = {
|
|
51
|
+
/**
|
|
52
|
+
* Optional component to render instead of default Link
|
|
53
|
+
*/
|
|
54
|
+
as: PropTypes.elementType,
|
|
61
55
|
/**
|
|
62
56
|
* Specify the children to be rendered inside of the `SideNavMenuItem`
|
|
63
57
|
*/
|
|
@@ -66,11 +60,6 @@ SideNavMenuItem.propTypes = {
|
|
|
66
60
|
* Provide an optional class to be applied to the containing node
|
|
67
61
|
*/
|
|
68
62
|
className: PropTypes.string,
|
|
69
|
-
/**
|
|
70
|
-
* **Note:** this is controlled by the parent SideNavMenu component, do not set manually.
|
|
71
|
-
* SideNavMenu depth to determine spacing
|
|
72
|
-
*/
|
|
73
|
-
depth: PropTypes.number,
|
|
74
63
|
/**
|
|
75
64
|
* Optionally provide an href for the underlying li`
|
|
76
65
|
*/
|
|
@@ -82,9 +71,9 @@ SideNavMenuItem.propTypes = {
|
|
|
82
71
|
*/
|
|
83
72
|
isActive: PropTypes.bool,
|
|
84
73
|
/**
|
|
85
|
-
*
|
|
74
|
+
* Provide an icon to render in the side navigation link. Should be a React class.
|
|
86
75
|
*/
|
|
87
|
-
|
|
76
|
+
renderIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.object])
|
|
88
77
|
};
|
|
89
78
|
|
|
90
79
|
exports.SideNavMenuItem = SideNavMenuItem;
|
package/lib/index.d.ts
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
export { SideNav, SIDE_NAV_TYPE } from './components/SideNav.js';
|
|
10
10
|
export { SideNavItems } from './components/SideNavItems.js';
|
|
11
|
+
export { SideNavLink } from './components/SideNavLink.js';
|
|
12
|
+
export { SideNavLinkPopover } from './components/SideNavLinkPopover.js';
|
|
11
13
|
export { SideNavMenu } from './components/SideNavMenu.js';
|
|
12
14
|
export { SideNavMenuItem } from './components/SideNavMenuItem.js';
|
|
13
15
|
export { HeaderPanel } from './components/HeaderPanel';
|
package/lib/index.js
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
|
|
10
10
|
var SideNav = require('./components/SideNav.js');
|
|
11
11
|
var SideNavItems = require('./components/SideNavItems.js');
|
|
12
|
+
var SideNavLink = require('./components/SideNavLink.js');
|
|
13
|
+
var SideNavLinkPopover = require('./components/SideNavLinkPopover.js');
|
|
12
14
|
var SideNavMenu = require('./components/SideNavMenu.js');
|
|
13
15
|
var SideNavMenuItem = require('./components/SideNavMenuItem.js');
|
|
14
16
|
var HeaderPanel = require('./components/HeaderPanel.js');
|
|
@@ -18,6 +20,8 @@ var HeaderPanel = require('./components/HeaderPanel.js');
|
|
|
18
20
|
exports.SIDE_NAV_TYPE = SideNav.SIDE_NAV_TYPE;
|
|
19
21
|
exports.SideNav = SideNav.SideNav;
|
|
20
22
|
exports.SideNavItems = SideNavItems.SideNavItems;
|
|
23
|
+
exports.SideNavLink = SideNavLink.SideNavLink;
|
|
24
|
+
exports.SideNavLinkPopover = SideNavLinkPopover.SideNavLinkPopover;
|
|
21
25
|
exports.SideNavMenu = SideNavMenu.SideNavMenu;
|
|
22
26
|
exports.SideNavMenuItem = SideNavMenuItem.SideNavMenuItem;
|
|
23
27
|
exports.HeaderPanel = HeaderPanel.HeaderPanel;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@carbon-labs/react-ui-shell",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public",
|
|
6
6
|
"provenance": true
|
|
@@ -33,5 +33,5 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@ibm/telemetry-js": "^1.9.1"
|
|
35
35
|
},
|
|
36
|
-
"gitHead": "
|
|
36
|
+
"gitHead": "30877a143d6909d98efd0ec5e6aef7fbf2b074be"
|
|
37
37
|
}
|
|
@@ -38,6 +38,7 @@ div:has(.#{$prefix}--header)
|
|
|
38
38
|
//----------------------------------------------------------------------------
|
|
39
39
|
// Treeview Side-nav
|
|
40
40
|
//----------------------------------------------------------------------------
|
|
41
|
+
|
|
41
42
|
.#{$prefix}--side-nav__icon:not(.#{$prefix}--side-nav__submenu-chevron) {
|
|
42
43
|
margin-inline-end: $spacing-05;
|
|
43
44
|
}
|
|
@@ -62,10 +63,69 @@ div:has(.#{$prefix}--header)
|
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
// SideNavMenu
|
|
67
|
+
// Level 2
|
|
68
|
+
// without icon
|
|
69
|
+
.#{$prefix}--side-nav__submenu
|
|
70
|
+
+ .#{$prefix}--side-nav__menu
|
|
71
|
+
> .#{$prefix}--side-nav__item
|
|
72
|
+
.#{$prefix}--side-nav__submenu {
|
|
73
|
+
padding-inline-start: $spacing-10;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// with icon
|
|
77
|
+
.#{$prefix}--side-nav__submenu
|
|
78
|
+
+ .#{$prefix}--side-nav__menu
|
|
79
|
+
> .#{$prefix}--side-nav__item--icon
|
|
80
|
+
.#{$prefix}--side-nav__submenu {
|
|
81
|
+
padding-inline-start: $spacing-09;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Nested SideNavMenuItem
|
|
85
|
+
// Level 2
|
|
86
|
+
// without icon
|
|
87
|
+
.#{$prefix}--side-nav__item.#{$prefix}--side-nav__item--icon
|
|
88
|
+
a.#{$prefix}--side-nav__link {
|
|
89
|
+
padding-inline-start: $spacing-10;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// with icon
|
|
93
|
+
.#{$prefix}--side-nav__item.#{$prefix}--side-nav__item--icon
|
|
94
|
+
a.#{$prefix}--side-nav__link:has(.#{$prefix}--side-nav__icon) {
|
|
95
|
+
padding-inline-start: $spacing-09;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Level 3
|
|
99
|
+
// without icon parent
|
|
100
|
+
.#{$prefix}--side-nav__menu
|
|
101
|
+
.#{$prefix}--side-nav__menu
|
|
102
|
+
a.#{$prefix}--side-nav__link {
|
|
103
|
+
padding-inline-start: $spacing-11;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// with icon parent
|
|
107
|
+
.#{$prefix}--side-nav__menu
|
|
108
|
+
.#{$prefix}--side-nav__item.#{$prefix}--side-nav__item--icon
|
|
109
|
+
a.#{$prefix}--side-nav__link {
|
|
110
|
+
padding-inline-start: $spacing-12;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
//----------------------------------------------------------------------------
|
|
65
114
|
// Side-nav Panel
|
|
66
115
|
//----------------------------------------------------------------------------
|
|
67
116
|
.#{$prefix}--side-nav--panel {
|
|
68
117
|
z-index: 7999; /* needs to be below header */
|
|
118
|
+
overflow: visible;
|
|
119
|
+
|
|
120
|
+
.#{$prefix}--side-nav__items {
|
|
121
|
+
display: flex;
|
|
122
|
+
flex-direction: column;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.#{$prefix}--side-nav__items,
|
|
126
|
+
.#{$prefix}--side-nav__items:hover {
|
|
127
|
+
overflow: visible;
|
|
128
|
+
}
|
|
69
129
|
|
|
70
130
|
.#{$prefix}--side-nav__icon:not(.#{$prefix}--side-nav__submenu-chevron) {
|
|
71
131
|
margin-inline-end: $spacing-05;
|
|
@@ -82,6 +142,7 @@ div:has(.#{$prefix}--header)
|
|
|
82
142
|
padding-inline-start: $spacing-10;
|
|
83
143
|
}
|
|
84
144
|
|
|
145
|
+
// Side Nav Toggle
|
|
85
146
|
.#{$prefix}--side-nav__toggle {
|
|
86
147
|
@include button-reset.reset($width: true);
|
|
87
148
|
@include type-style('heading-compact-01');
|
|
@@ -110,4 +171,15 @@ div:has(.#{$prefix}--header)
|
|
|
110
171
|
|
|
111
172
|
text-align: start;
|
|
112
173
|
}
|
|
174
|
+
|
|
175
|
+
// Side Nav Link Popover
|
|
176
|
+
.#{$prefix}--side-nav-link-popover.#{$prefix}--btn--icon-only {
|
|
177
|
+
padding: 0;
|
|
178
|
+
block-size: $spacing-07;
|
|
179
|
+
min-block-size: initial;
|
|
180
|
+
|
|
181
|
+
.#{$prefix}--side-nav__icon:not(.#{$prefix}--side-nav__submenu-chevron) {
|
|
182
|
+
margin-inline-end: 0;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
113
185
|
}
|