@carbon-labs/react-ui-shell 0.82.0 → 0.83.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/es/components/HeaderOverflowPanel.d.ts +24 -0
- package/es/components/HeaderOverflowPanel.js +56 -0
- package/es/components/Profile.js +1 -1
- package/es/components/SideNav.d.ts +2 -0
- package/es/components/SideNav.js +11 -4
- package/es/components/SideNavMenu.js +17 -7
- package/es/index.d.ts +1 -0
- package/es/index.js +1 -0
- package/lib/components/HeaderOverflowPanel.d.ts +24 -0
- package/lib/components/HeaderOverflowPanel.js +58 -0
- package/lib/components/Profile.js +1 -1
- package/lib/components/SideNav.d.ts +2 -0
- package/lib/components/SideNav.js +11 -4
- package/lib/components/SideNavMenu.js +17 -7
- package/lib/index.d.ts +1 -0
- package/lib/index.js +2 -0
- package/package.json +2 -2
- package/scss/styles/_header-overflow-panel.scss +105 -0
- package/scss/styles/_profile.scss +11 -3
- package/scss/styles/_side-nav.scss +1 -1
- package/scss/ui-shell.scss +1 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
*
|
|
4
|
+
* Copyright IBM Corp. 2025
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
import React from 'react';
|
|
10
|
+
export interface HeaderOverflowPanelProps {
|
|
11
|
+
/**
|
|
12
|
+
* Provide an optional class to be applied to the containing node
|
|
13
|
+
*/
|
|
14
|
+
className?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Custom children to be rendered within the popover of the Overflow panel menu
|
|
17
|
+
*/
|
|
18
|
+
children?: React.ReactNode;
|
|
19
|
+
/**
|
|
20
|
+
* Provide the Overflow panel's label
|
|
21
|
+
*/
|
|
22
|
+
label?: string;
|
|
23
|
+
}
|
|
24
|
+
export declare const HeaderOverflowPanel: React.ForwardRefExoticComponent<HeaderOverflowPanelProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,56 @@
|
|
|
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 'classnames';
|
|
10
|
+
import PropTypes from 'prop-types';
|
|
11
|
+
import React__default from 'react';
|
|
12
|
+
import { usePrefix } from '../internal/usePrefix.js';
|
|
13
|
+
import { HeaderPopover, HeaderPopoverButton, HeaderPopoverContent } from './HeaderPopover.js';
|
|
14
|
+
import { OverflowMenuVertical } from '@carbon/icons-react';
|
|
15
|
+
import { useMatchMedia } from '../internal/useMatchMedia.js';
|
|
16
|
+
import { breakpoints } from '../node_modules/@carbon/layout/es/index.js';
|
|
17
|
+
|
|
18
|
+
var _OverflowMenuVertical;
|
|
19
|
+
const mdMediaQuery = `(max-width: ${breakpoints.md.width})`;
|
|
20
|
+
const HeaderOverflowPanel = /*#__PURE__*/React__default.forwardRef(function HeaderOverflowPanel({
|
|
21
|
+
className: customClassName,
|
|
22
|
+
children,
|
|
23
|
+
label,
|
|
24
|
+
...rest
|
|
25
|
+
}, ref) {
|
|
26
|
+
const prefix = usePrefix();
|
|
27
|
+
const className = cx({
|
|
28
|
+
[`${prefix}--header-overflow-panel`]: true,
|
|
29
|
+
[customClassName]: !!customClassName
|
|
30
|
+
});
|
|
31
|
+
const isMd = useMatchMedia(mdMediaQuery);
|
|
32
|
+
return /*#__PURE__*/React__default.createElement(HeaderPopover, _extends({
|
|
33
|
+
ref: ref,
|
|
34
|
+
align: "bottom-end",
|
|
35
|
+
className: className
|
|
36
|
+
}, rest), /*#__PURE__*/React__default.createElement(HeaderPopoverButton, {
|
|
37
|
+
align: isMd ? 'bottom-end' : 'bottom',
|
|
38
|
+
label: label
|
|
39
|
+
}, _OverflowMenuVertical || (_OverflowMenuVertical = /*#__PURE__*/React__default.createElement(OverflowMenuVertical, null))), /*#__PURE__*/React__default.createElement(HeaderPopoverContent, null, /*#__PURE__*/React__default.createElement("ul", null, children)));
|
|
40
|
+
});
|
|
41
|
+
HeaderOverflowPanel.propTypes = {
|
|
42
|
+
/**
|
|
43
|
+
* Custom children to be rendered within the popover of the Profile menu
|
|
44
|
+
*/
|
|
45
|
+
children: PropTypes.any,
|
|
46
|
+
/**
|
|
47
|
+
* Provide an optional class to be applied to the containing node
|
|
48
|
+
*/
|
|
49
|
+
className: PropTypes.string,
|
|
50
|
+
/**
|
|
51
|
+
* Provide the Overflow panel's label
|
|
52
|
+
*/
|
|
53
|
+
label: PropTypes.string
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export { HeaderOverflowPanel };
|
package/es/components/Profile.js
CHANGED
|
@@ -30,7 +30,7 @@ const Profile = /*#__PURE__*/React__default.forwardRef(function Profile({
|
|
|
30
30
|
align: "bottom-end",
|
|
31
31
|
className: className
|
|
32
32
|
}, rest), /*#__PURE__*/React__default.createElement(HeaderPopoverButton, {
|
|
33
|
-
align: "bottom",
|
|
33
|
+
align: "bottom-end",
|
|
34
34
|
label: label
|
|
35
35
|
}, IconElement), /*#__PURE__*/React__default.createElement(HeaderPopoverContent, null, children));
|
|
36
36
|
});
|
|
@@ -38,6 +38,7 @@ export interface SideNavProps extends ComponentProps<'nav'>, TranslateWithId<Tra
|
|
|
38
38
|
hideOverlay?: boolean;
|
|
39
39
|
navType?: SIDE_NAV_TYPE;
|
|
40
40
|
isTreeview?: boolean;
|
|
41
|
+
headerOverflowPanel?: boolean;
|
|
41
42
|
}
|
|
42
43
|
interface SideNavContextData {
|
|
43
44
|
autoExpand?: boolean;
|
|
@@ -48,6 +49,7 @@ interface SideNavContextData {
|
|
|
48
49
|
setIsTreeview?: (value: boolean) => void;
|
|
49
50
|
currentPrimaryMenu?: string;
|
|
50
51
|
setCurrentPrimaryMenu?: (value: string) => void;
|
|
52
|
+
headerOverflowPanel?: boolean;
|
|
51
53
|
}
|
|
52
54
|
export declare const SideNavContext: React.Context<SideNavContextData>;
|
|
53
55
|
export declare const SideNav: React.ForwardRefExoticComponent<Omit<SideNavProps, "ref"> & React.RefAttributes<HTMLElement>>;
|
package/es/components/SideNav.js
CHANGED
|
@@ -68,6 +68,7 @@ function SideNavRenderFunction({
|
|
|
68
68
|
isCollapsible = false,
|
|
69
69
|
hideOverlay = false,
|
|
70
70
|
translateWithId: t = defaultTranslateWithId,
|
|
71
|
+
headerOverflowPanel,
|
|
71
72
|
...other
|
|
72
73
|
}, ref) {
|
|
73
74
|
const [internalIsTreeview, setInternalIsTreeview] = useState(isTreeviewProp ?? false);
|
|
@@ -141,7 +142,7 @@ function SideNavRenderFunction({
|
|
|
141
142
|
const resetNodeTabIndices = useCallback(() => {
|
|
142
143
|
const items = sideNavRef?.current?.querySelectorAll('[tabIndex="0"]') ?? [];
|
|
143
144
|
items.forEach(item => {
|
|
144
|
-
if (item.classList.contains(`${prefix}--side-nav__toggle`) || item.classList.contains(`${prefix}--side-nav__back-button`) || item.closest(`.${prefix}--side-nav__slot-item`) || item.classList.contains(`${prefix}--side-nav__link`) && item.closest('ul')?.getAttribute('aria-label') === ariaLabel) {
|
|
145
|
+
if (item.classList.contains(`${prefix}--side-nav__toggle`) || item.classList.contains(`${prefix}--side-nav__back-button`) || item.closest(`.${prefix}--side-nav__slot-item`) || item.classList.contains(`${prefix}--side-nav__link`) && item.closest('ul')?.getAttribute('aria-label') === ariaLabel || item.closest(`.${prefix}--header-overflow-panel-secondary-container`)) {
|
|
145
146
|
return;
|
|
146
147
|
}
|
|
147
148
|
item.tabIndex = -1;
|
|
@@ -168,7 +169,9 @@ function SideNavRenderFunction({
|
|
|
168
169
|
}
|
|
169
170
|
}, [prefix, internalIsTreeview, resetNodeTabIndices]);
|
|
170
171
|
const smMediaQuery = `(min-width: ${breakpoints.sm.width})`;
|
|
171
|
-
const
|
|
172
|
+
const lgMediaQuery = `(min-width: ${breakpoints.lg.width})`;
|
|
173
|
+
const query = !headerOverflowPanel ? smMediaQuery : lgMediaQuery;
|
|
174
|
+
const isSm = useMatchMedia(query);
|
|
172
175
|
useEffect(() => {
|
|
173
176
|
if (sideNavRef.current) {
|
|
174
177
|
const backButton = sideNavRef?.current.querySelector(`.${prefix}--side-nav__back-button`);
|
|
@@ -379,7 +382,6 @@ function SideNavRenderFunction({
|
|
|
379
382
|
sideNavRef.current.focus();
|
|
380
383
|
}
|
|
381
384
|
});
|
|
382
|
-
const lgMediaQuery = `(min-width: ${breakpoints.lg.width})`;
|
|
383
385
|
const isLg = useMatchMedia(lgMediaQuery);
|
|
384
386
|
|
|
385
387
|
// ensure that changes are in sync with internal treeview prop
|
|
@@ -417,7 +419,8 @@ function SideNavRenderFunction({
|
|
|
417
419
|
isTreeview: internalIsTreeview,
|
|
418
420
|
setIsTreeview,
|
|
419
421
|
currentPrimaryMenu,
|
|
420
|
-
setCurrentPrimaryMenu
|
|
422
|
+
setCurrentPrimaryMenu,
|
|
423
|
+
headerOverflowPanel
|
|
421
424
|
}
|
|
422
425
|
}, isFixedNav || hideOverlay || navType === SIDE_NAV_TYPE.RAIL_PANEL ? null :
|
|
423
426
|
/*#__PURE__*/
|
|
@@ -474,6 +477,10 @@ SideNav.propTypes = {
|
|
|
474
477
|
* Using this prop causes SideNav to become a controled component.
|
|
475
478
|
*/
|
|
476
479
|
expanded: PropTypes.bool,
|
|
480
|
+
/**
|
|
481
|
+
* If `true`, it means the SideNav is being used inside the HeaderOverflowPanel component for sm/md breakpoints.
|
|
482
|
+
*/
|
|
483
|
+
headerOverflowPanel: PropTypes.bool,
|
|
477
484
|
/**
|
|
478
485
|
* If `true`, the overlay will be hidden. Defaults to `false`.
|
|
479
486
|
*/
|
|
@@ -25,6 +25,7 @@ import { useMatchMedia } from '../internal/useMatchMedia.js';
|
|
|
25
25
|
|
|
26
26
|
var _ArrowLeft, _SharkFinIcon, _ChevronRight, _ChevronDown;
|
|
27
27
|
const smMediaQuery = `(max-width: ${breakpoints.md.width})`;
|
|
28
|
+
const mdMediaQuery = `(max-width: ${breakpoints.lg.width})`;
|
|
28
29
|
const SideNavMenu = /*#__PURE__*/React__default.forwardRef(function SideNavMenu({
|
|
29
30
|
backButtonRenderIcon = () => _ArrowLeft || (_ArrowLeft = /*#__PURE__*/React__default.createElement(ArrowLeft, {
|
|
30
31
|
size: 16
|
|
@@ -50,7 +51,8 @@ const SideNavMenu = /*#__PURE__*/React__default.forwardRef(function SideNavMenu(
|
|
|
50
51
|
expanded,
|
|
51
52
|
navType,
|
|
52
53
|
isRail,
|
|
53
|
-
setIsTreeview
|
|
54
|
+
setIsTreeview,
|
|
55
|
+
headerOverflowPanel
|
|
54
56
|
} = useContext(SideNavContext);
|
|
55
57
|
const sideNavExpanded = expanded;
|
|
56
58
|
const prefix = usePrefix();
|
|
@@ -97,8 +99,6 @@ const SideNavMenu = /*#__PURE__*/React__default.forwardRef(function SideNavMenu(
|
|
|
97
99
|
if (/*#__PURE__*/React__default.isValidElement(children)) {
|
|
98
100
|
const props = children.props;
|
|
99
101
|
if (props.isActive === true || props['aria-current']) {
|
|
100
|
-
// setActive(true);
|
|
101
|
-
|
|
102
102
|
return true;
|
|
103
103
|
}
|
|
104
104
|
}
|
|
@@ -138,7 +138,7 @@ const SideNavMenu = /*#__PURE__*/React__default.forwardRef(function SideNavMenu(
|
|
|
138
138
|
[`${prefix}--side-nav__submenu`]: true,
|
|
139
139
|
[`${prefix}--side-nav__submenu--active`]: active || hasActiveDescendant && isExpanded
|
|
140
140
|
});
|
|
141
|
-
const
|
|
141
|
+
const secondaryClassNames = cx({
|
|
142
142
|
[`${prefix}--side-nav__menu-secondary-wrapper`]: true,
|
|
143
143
|
[`${prefix}--side-nav__menu-secondary-wrapper-expanded`]: isSideNavExpanded && isSecondaryOpen && currentPrimaryMenu === uniqueId
|
|
144
144
|
});
|
|
@@ -301,7 +301,8 @@ const SideNavMenu = /*#__PURE__*/React__default.forwardRef(function SideNavMenu(
|
|
|
301
301
|
}
|
|
302
302
|
}, [sideNavExpanded]);
|
|
303
303
|
const [openPopover, setOpenPopover] = React__default.useState(false);
|
|
304
|
-
const
|
|
304
|
+
const query = !headerOverflowPanel ? smMediaQuery : mdMediaQuery;
|
|
305
|
+
const isSm = useMatchMedia(query);
|
|
305
306
|
|
|
306
307
|
// keeps the secondary open when moving from small to large breakpoints
|
|
307
308
|
useEffect(() => {
|
|
@@ -355,8 +356,8 @@ const SideNavMenu = /*#__PURE__*/React__default.forwardRef(function SideNavMenu(
|
|
|
355
356
|
})) : _ChevronDown || (_ChevronDown = /*#__PURE__*/React__default.createElement(ChevronDown, {
|
|
356
357
|
size: 20
|
|
357
358
|
})))), primary ? /*#__PURE__*/React__default.createElement(Layer, null, /*#__PURE__*/React__default.createElement("div", {
|
|
358
|
-
className:
|
|
359
|
-
}, /*#__PURE__*/React__default.createElement(SideNavItems, {
|
|
359
|
+
className: secondaryClassNames
|
|
360
|
+
}, !headerOverflowPanel ? /*#__PURE__*/React__default.createElement(SideNavItems, {
|
|
360
361
|
accessibilityLabel: {
|
|
361
362
|
'aria-label': `${title} submenu`
|
|
362
363
|
}
|
|
@@ -367,6 +368,15 @@ const SideNavMenu = /*#__PURE__*/React__default.forwardRef(function SideNavMenu(
|
|
|
367
368
|
onClick: handleOnBackButtonClick,
|
|
368
369
|
className: `${prefix}--side-nav__back-button`,
|
|
369
370
|
renderIcon: backButtonRenderIcon
|
|
371
|
+
}, backButtonTitle), childrenToRender) : /*#__PURE__*/React__default.createElement("div", {
|
|
372
|
+
className: `${prefix}--header-overflow-panel-secondary-container`
|
|
373
|
+
}, /*#__PURE__*/React__default.createElement(Button, {
|
|
374
|
+
ref: backButtonRef,
|
|
375
|
+
kind: "ghost",
|
|
376
|
+
size: "md",
|
|
377
|
+
onClick: handleOnBackButtonClick,
|
|
378
|
+
className: `${prefix}--side-nav__back-button`,
|
|
379
|
+
renderIcon: backButtonRenderIcon
|
|
370
380
|
}, backButtonTitle), childrenToRender))) : /*#__PURE__*/React__default.createElement("ul", {
|
|
371
381
|
className: `${prefix}--side-nav__menu`,
|
|
372
382
|
role: "group"
|
package/es/index.d.ts
CHANGED
|
@@ -18,5 +18,6 @@ export { SharkFinIcon } from './components/SharkFinIcon';
|
|
|
18
18
|
export { HeaderDivider } from './components/HeaderDivider';
|
|
19
19
|
export { TrialCountdown } from './components/TrialCountdown';
|
|
20
20
|
export * as Profile from './components/Profile';
|
|
21
|
+
export { HeaderOverflowPanel } from './components/HeaderOverflowPanel';
|
|
21
22
|
export { SideNavSlot } from './components/SideNavSlot';
|
|
22
23
|
export { SideNavTitle } from './components/SideNavTitle';
|
package/es/index.js
CHANGED
|
@@ -18,5 +18,6 @@ export { HeaderDivider } from './components/HeaderDivider.js';
|
|
|
18
18
|
export { TrialCountdown } from './components/TrialCountdown.js';
|
|
19
19
|
import * as Profile from './components/Profile.js';
|
|
20
20
|
export { Profile };
|
|
21
|
+
export { HeaderOverflowPanel } from './components/HeaderOverflowPanel.js';
|
|
21
22
|
export { SideNavSlot } from './components/SideNavSlot.js';
|
|
22
23
|
export { SideNavTitle } from './components/SideNavTitle.js';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
*
|
|
4
|
+
* Copyright IBM Corp. 2025
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
import React from 'react';
|
|
10
|
+
export interface HeaderOverflowPanelProps {
|
|
11
|
+
/**
|
|
12
|
+
* Provide an optional class to be applied to the containing node
|
|
13
|
+
*/
|
|
14
|
+
className?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Custom children to be rendered within the popover of the Overflow panel menu
|
|
17
|
+
*/
|
|
18
|
+
children?: React.ReactNode;
|
|
19
|
+
/**
|
|
20
|
+
* Provide the Overflow panel's label
|
|
21
|
+
*/
|
|
22
|
+
label?: string;
|
|
23
|
+
}
|
|
24
|
+
export declare const HeaderOverflowPanel: React.ForwardRefExoticComponent<HeaderOverflowPanelProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,58 @@
|
|
|
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
|
+
var _rollupPluginBabelHelpers = require('../_virtual/_rollupPluginBabelHelpers.js');
|
|
11
|
+
var cx = require('classnames');
|
|
12
|
+
var PropTypes = require('prop-types');
|
|
13
|
+
var React__default = require('react');
|
|
14
|
+
var usePrefix = require('../internal/usePrefix.js');
|
|
15
|
+
var HeaderPopover = require('./HeaderPopover.js');
|
|
16
|
+
var iconsReact = require('@carbon/icons-react');
|
|
17
|
+
var useMatchMedia = require('../internal/useMatchMedia.js');
|
|
18
|
+
var index = require('../node_modules/@carbon/layout/es/index.js');
|
|
19
|
+
|
|
20
|
+
var _OverflowMenuVertical;
|
|
21
|
+
const mdMediaQuery = `(max-width: ${index.breakpoints.md.width})`;
|
|
22
|
+
const HeaderOverflowPanel = /*#__PURE__*/React__default.forwardRef(function HeaderOverflowPanel({
|
|
23
|
+
className: customClassName,
|
|
24
|
+
children,
|
|
25
|
+
label,
|
|
26
|
+
...rest
|
|
27
|
+
}, ref) {
|
|
28
|
+
const prefix = usePrefix.usePrefix();
|
|
29
|
+
const className = cx({
|
|
30
|
+
[`${prefix}--header-overflow-panel`]: true,
|
|
31
|
+
[customClassName]: !!customClassName
|
|
32
|
+
});
|
|
33
|
+
const isMd = useMatchMedia.useMatchMedia(mdMediaQuery);
|
|
34
|
+
return /*#__PURE__*/React__default.createElement(HeaderPopover.HeaderPopover, _rollupPluginBabelHelpers.extends({
|
|
35
|
+
ref: ref,
|
|
36
|
+
align: "bottom-end",
|
|
37
|
+
className: className
|
|
38
|
+
}, rest), /*#__PURE__*/React__default.createElement(HeaderPopover.HeaderPopoverButton, {
|
|
39
|
+
align: isMd ? 'bottom-end' : 'bottom',
|
|
40
|
+
label: label
|
|
41
|
+
}, _OverflowMenuVertical || (_OverflowMenuVertical = /*#__PURE__*/React__default.createElement(iconsReact.OverflowMenuVertical, null))), /*#__PURE__*/React__default.createElement(HeaderPopover.HeaderPopoverContent, null, /*#__PURE__*/React__default.createElement("ul", null, children)));
|
|
42
|
+
});
|
|
43
|
+
HeaderOverflowPanel.propTypes = {
|
|
44
|
+
/**
|
|
45
|
+
* Custom children to be rendered within the popover of the Profile menu
|
|
46
|
+
*/
|
|
47
|
+
children: PropTypes.any,
|
|
48
|
+
/**
|
|
49
|
+
* Provide an optional class to be applied to the containing node
|
|
50
|
+
*/
|
|
51
|
+
className: PropTypes.string,
|
|
52
|
+
/**
|
|
53
|
+
* Provide the Overflow panel's label
|
|
54
|
+
*/
|
|
55
|
+
label: PropTypes.string
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
exports.HeaderOverflowPanel = HeaderOverflowPanel;
|
|
@@ -32,7 +32,7 @@ const Profile = /*#__PURE__*/React__default.forwardRef(function Profile({
|
|
|
32
32
|
align: "bottom-end",
|
|
33
33
|
className: className
|
|
34
34
|
}, rest), /*#__PURE__*/React__default.createElement(HeaderPopover.HeaderPopoverButton, {
|
|
35
|
-
align: "bottom",
|
|
35
|
+
align: "bottom-end",
|
|
36
36
|
label: label
|
|
37
37
|
}, IconElement), /*#__PURE__*/React__default.createElement(HeaderPopover.HeaderPopoverContent, null, children));
|
|
38
38
|
});
|
|
@@ -38,6 +38,7 @@ export interface SideNavProps extends ComponentProps<'nav'>, TranslateWithId<Tra
|
|
|
38
38
|
hideOverlay?: boolean;
|
|
39
39
|
navType?: SIDE_NAV_TYPE;
|
|
40
40
|
isTreeview?: boolean;
|
|
41
|
+
headerOverflowPanel?: boolean;
|
|
41
42
|
}
|
|
42
43
|
interface SideNavContextData {
|
|
43
44
|
autoExpand?: boolean;
|
|
@@ -48,6 +49,7 @@ interface SideNavContextData {
|
|
|
48
49
|
setIsTreeview?: (value: boolean) => void;
|
|
49
50
|
currentPrimaryMenu?: string;
|
|
50
51
|
setCurrentPrimaryMenu?: (value: string) => void;
|
|
52
|
+
headerOverflowPanel?: boolean;
|
|
51
53
|
}
|
|
52
54
|
export declare const SideNavContext: React.Context<SideNavContextData>;
|
|
53
55
|
export declare const SideNav: React.ForwardRefExoticComponent<Omit<SideNavProps, "ref"> & React.RefAttributes<HTMLElement>>;
|
|
@@ -70,6 +70,7 @@ function SideNavRenderFunction({
|
|
|
70
70
|
isCollapsible = false,
|
|
71
71
|
hideOverlay = false,
|
|
72
72
|
translateWithId: t = defaultTranslateWithId,
|
|
73
|
+
headerOverflowPanel,
|
|
73
74
|
...other
|
|
74
75
|
}, ref) {
|
|
75
76
|
const [internalIsTreeview, setInternalIsTreeview] = React__default.useState(isTreeviewProp ?? false);
|
|
@@ -143,7 +144,7 @@ function SideNavRenderFunction({
|
|
|
143
144
|
const resetNodeTabIndices = React__default.useCallback(() => {
|
|
144
145
|
const items = sideNavRef?.current?.querySelectorAll('[tabIndex="0"]') ?? [];
|
|
145
146
|
items.forEach(item => {
|
|
146
|
-
if (item.classList.contains(`${prefix}--side-nav__toggle`) || item.classList.contains(`${prefix}--side-nav__back-button`) || item.closest(`.${prefix}--side-nav__slot-item`) || item.classList.contains(`${prefix}--side-nav__link`) && item.closest('ul')?.getAttribute('aria-label') === ariaLabel) {
|
|
147
|
+
if (item.classList.contains(`${prefix}--side-nav__toggle`) || item.classList.contains(`${prefix}--side-nav__back-button`) || item.closest(`.${prefix}--side-nav__slot-item`) || item.classList.contains(`${prefix}--side-nav__link`) && item.closest('ul')?.getAttribute('aria-label') === ariaLabel || item.closest(`.${prefix}--header-overflow-panel-secondary-container`)) {
|
|
147
148
|
return;
|
|
148
149
|
}
|
|
149
150
|
item.tabIndex = -1;
|
|
@@ -170,7 +171,9 @@ function SideNavRenderFunction({
|
|
|
170
171
|
}
|
|
171
172
|
}, [prefix, internalIsTreeview, resetNodeTabIndices]);
|
|
172
173
|
const smMediaQuery = `(min-width: ${index.breakpoints.sm.width})`;
|
|
173
|
-
const
|
|
174
|
+
const lgMediaQuery = `(min-width: ${index.breakpoints.lg.width})`;
|
|
175
|
+
const query = !headerOverflowPanel ? smMediaQuery : lgMediaQuery;
|
|
176
|
+
const isSm = useMatchMedia.useMatchMedia(query);
|
|
174
177
|
React__default.useEffect(() => {
|
|
175
178
|
if (sideNavRef.current) {
|
|
176
179
|
const backButton = sideNavRef?.current.querySelector(`.${prefix}--side-nav__back-button`);
|
|
@@ -381,7 +384,6 @@ function SideNavRenderFunction({
|
|
|
381
384
|
sideNavRef.current.focus();
|
|
382
385
|
}
|
|
383
386
|
});
|
|
384
|
-
const lgMediaQuery = `(min-width: ${index.breakpoints.lg.width})`;
|
|
385
387
|
const isLg = useMatchMedia.useMatchMedia(lgMediaQuery);
|
|
386
388
|
|
|
387
389
|
// ensure that changes are in sync with internal treeview prop
|
|
@@ -419,7 +421,8 @@ function SideNavRenderFunction({
|
|
|
419
421
|
isTreeview: internalIsTreeview,
|
|
420
422
|
setIsTreeview,
|
|
421
423
|
currentPrimaryMenu,
|
|
422
|
-
setCurrentPrimaryMenu
|
|
424
|
+
setCurrentPrimaryMenu,
|
|
425
|
+
headerOverflowPanel
|
|
423
426
|
}
|
|
424
427
|
}, isFixedNav || hideOverlay || navType === SIDE_NAV_TYPE.RAIL_PANEL ? null :
|
|
425
428
|
/*#__PURE__*/
|
|
@@ -476,6 +479,10 @@ SideNav.propTypes = {
|
|
|
476
479
|
* Using this prop causes SideNav to become a controled component.
|
|
477
480
|
*/
|
|
478
481
|
expanded: PropTypes.bool,
|
|
482
|
+
/**
|
|
483
|
+
* If `true`, it means the SideNav is being used inside the HeaderOverflowPanel component for sm/md breakpoints.
|
|
484
|
+
*/
|
|
485
|
+
headerOverflowPanel: PropTypes.bool,
|
|
479
486
|
/**
|
|
480
487
|
* If `true`, the overlay will be hidden. Defaults to `false`.
|
|
481
488
|
*/
|
|
@@ -27,6 +27,7 @@ var useMatchMedia = require('../internal/useMatchMedia.js');
|
|
|
27
27
|
|
|
28
28
|
var _ArrowLeft, _SharkFinIcon, _ChevronRight, _ChevronDown;
|
|
29
29
|
const smMediaQuery = `(max-width: ${index.breakpoints.md.width})`;
|
|
30
|
+
const mdMediaQuery = `(max-width: ${index.breakpoints.lg.width})`;
|
|
30
31
|
const SideNavMenu = /*#__PURE__*/React__default.forwardRef(function SideNavMenu({
|
|
31
32
|
backButtonRenderIcon = () => _ArrowLeft || (_ArrowLeft = /*#__PURE__*/React__default.createElement(iconsReact.ArrowLeft, {
|
|
32
33
|
size: 16
|
|
@@ -52,7 +53,8 @@ const SideNavMenu = /*#__PURE__*/React__default.forwardRef(function SideNavMenu(
|
|
|
52
53
|
expanded,
|
|
53
54
|
navType,
|
|
54
55
|
isRail,
|
|
55
|
-
setIsTreeview
|
|
56
|
+
setIsTreeview,
|
|
57
|
+
headerOverflowPanel
|
|
56
58
|
} = React__default.useContext(SideNav.SideNavContext);
|
|
57
59
|
const sideNavExpanded = expanded;
|
|
58
60
|
const prefix = usePrefix.usePrefix();
|
|
@@ -99,8 +101,6 @@ const SideNavMenu = /*#__PURE__*/React__default.forwardRef(function SideNavMenu(
|
|
|
99
101
|
if (/*#__PURE__*/React__default.isValidElement(children)) {
|
|
100
102
|
const props = children.props;
|
|
101
103
|
if (props.isActive === true || props['aria-current']) {
|
|
102
|
-
// setActive(true);
|
|
103
|
-
|
|
104
104
|
return true;
|
|
105
105
|
}
|
|
106
106
|
}
|
|
@@ -140,7 +140,7 @@ const SideNavMenu = /*#__PURE__*/React__default.forwardRef(function SideNavMenu(
|
|
|
140
140
|
[`${prefix}--side-nav__submenu`]: true,
|
|
141
141
|
[`${prefix}--side-nav__submenu--active`]: active || hasActiveDescendant && isExpanded
|
|
142
142
|
});
|
|
143
|
-
const
|
|
143
|
+
const secondaryClassNames = cx({
|
|
144
144
|
[`${prefix}--side-nav__menu-secondary-wrapper`]: true,
|
|
145
145
|
[`${prefix}--side-nav__menu-secondary-wrapper-expanded`]: isSideNavExpanded && isSecondaryOpen && currentPrimaryMenu === uniqueId
|
|
146
146
|
});
|
|
@@ -303,7 +303,8 @@ const SideNavMenu = /*#__PURE__*/React__default.forwardRef(function SideNavMenu(
|
|
|
303
303
|
}
|
|
304
304
|
}, [sideNavExpanded]);
|
|
305
305
|
const [openPopover, setOpenPopover] = React__default.useState(false);
|
|
306
|
-
const
|
|
306
|
+
const query = !headerOverflowPanel ? smMediaQuery : mdMediaQuery;
|
|
307
|
+
const isSm = useMatchMedia.useMatchMedia(query);
|
|
307
308
|
|
|
308
309
|
// keeps the secondary open when moving from small to large breakpoints
|
|
309
310
|
React__default.useEffect(() => {
|
|
@@ -357,8 +358,8 @@ const SideNavMenu = /*#__PURE__*/React__default.forwardRef(function SideNavMenu(
|
|
|
357
358
|
})) : _ChevronDown || (_ChevronDown = /*#__PURE__*/React__default.createElement(iconsReact.ChevronDown, {
|
|
358
359
|
size: 20
|
|
359
360
|
})))), primary ? /*#__PURE__*/React__default.createElement(react.Layer, null, /*#__PURE__*/React__default.createElement("div", {
|
|
360
|
-
className:
|
|
361
|
-
}, /*#__PURE__*/React__default.createElement(SideNavItems.SideNavItems, {
|
|
361
|
+
className: secondaryClassNames
|
|
362
|
+
}, !headerOverflowPanel ? /*#__PURE__*/React__default.createElement(SideNavItems.SideNavItems, {
|
|
362
363
|
accessibilityLabel: {
|
|
363
364
|
'aria-label': `${title} submenu`
|
|
364
365
|
}
|
|
@@ -369,6 +370,15 @@ const SideNavMenu = /*#__PURE__*/React__default.forwardRef(function SideNavMenu(
|
|
|
369
370
|
onClick: handleOnBackButtonClick,
|
|
370
371
|
className: `${prefix}--side-nav__back-button`,
|
|
371
372
|
renderIcon: backButtonRenderIcon
|
|
373
|
+
}, backButtonTitle), childrenToRender) : /*#__PURE__*/React__default.createElement("div", {
|
|
374
|
+
className: `${prefix}--header-overflow-panel-secondary-container`
|
|
375
|
+
}, /*#__PURE__*/React__default.createElement(react.Button, {
|
|
376
|
+
ref: backButtonRef,
|
|
377
|
+
kind: "ghost",
|
|
378
|
+
size: "md",
|
|
379
|
+
onClick: handleOnBackButtonClick,
|
|
380
|
+
className: `${prefix}--side-nav__back-button`,
|
|
381
|
+
renderIcon: backButtonRenderIcon
|
|
372
382
|
}, backButtonTitle), childrenToRender))) : /*#__PURE__*/React__default.createElement("ul", {
|
|
373
383
|
className: `${prefix}--side-nav__menu`,
|
|
374
384
|
role: "group"
|
package/lib/index.d.ts
CHANGED
|
@@ -18,5 +18,6 @@ export { SharkFinIcon } from './components/SharkFinIcon';
|
|
|
18
18
|
export { HeaderDivider } from './components/HeaderDivider';
|
|
19
19
|
export { TrialCountdown } from './components/TrialCountdown';
|
|
20
20
|
export * as Profile from './components/Profile';
|
|
21
|
+
export { HeaderOverflowPanel } from './components/HeaderOverflowPanel';
|
|
21
22
|
export { SideNavSlot } from './components/SideNavSlot';
|
|
22
23
|
export { SideNavTitle } from './components/SideNavTitle';
|
package/lib/index.js
CHANGED
|
@@ -19,6 +19,7 @@ var SharkFinIcon = require('./components/SharkFinIcon.js');
|
|
|
19
19
|
var HeaderDivider = require('./components/HeaderDivider.js');
|
|
20
20
|
var TrialCountdown = require('./components/TrialCountdown.js');
|
|
21
21
|
var Profile = require('./components/Profile.js');
|
|
22
|
+
var HeaderOverflowPanel = require('./components/HeaderOverflowPanel.js');
|
|
22
23
|
var SideNavSlot = require('./components/SideNavSlot.js');
|
|
23
24
|
var SideNavTitle = require('./components/SideNavTitle.js');
|
|
24
25
|
|
|
@@ -40,5 +41,6 @@ exports.SharkFinIcon = SharkFinIcon.SharkFinIcon;
|
|
|
40
41
|
exports.HeaderDivider = HeaderDivider.HeaderDivider;
|
|
41
42
|
exports.TrialCountdown = TrialCountdown.TrialCountdown;
|
|
42
43
|
exports.Profile = Profile;
|
|
44
|
+
exports.HeaderOverflowPanel = HeaderOverflowPanel.HeaderOverflowPanel;
|
|
43
45
|
exports.SideNavSlot = SideNavSlot.SideNavSlot;
|
|
44
46
|
exports.SideNavTitle = SideNavTitle.SideNavTitle;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@carbon-labs/react-ui-shell",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.83.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public",
|
|
6
6
|
"provenance": true
|
|
@@ -42,5 +42,5 @@
|
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@ibm/telemetry-js": "^1.10.2"
|
|
44
44
|
},
|
|
45
|
-
"gitHead": "
|
|
45
|
+
"gitHead": "36230cbb98ea772697697bc648577fdd06970002"
|
|
46
46
|
}
|
|
@@ -0,0 +1,105 @@
|
|
|
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
|
+
|
|
8
|
+
@use '@carbon/styles/scss/spacing' as *;
|
|
9
|
+
@use '@carbon/styles/scss/theme' as *;
|
|
10
|
+
@use '@carbon/react/scss/utilities/convert' as convert;
|
|
11
|
+
@use '@carbon/styles/scss/breakpoint' as *;
|
|
12
|
+
@use '@carbon/styles/scss/type' as *;
|
|
13
|
+
|
|
14
|
+
$prefix: 'cds' !default;
|
|
15
|
+
|
|
16
|
+
@include breakpoint-up(lg) {
|
|
17
|
+
.#{$prefix}--header-overflow-panel {
|
|
18
|
+
display: none;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// COPIED FROM SIDE NAV BUT NOW AT LG BREAKPOINT
|
|
23
|
+
@include breakpoint-down(lg) {
|
|
24
|
+
.#{$prefix}--header-overflow-panel
|
|
25
|
+
.#{$prefix}--side-nav--collapsible.#{$prefix}--side-nav--expanded {
|
|
26
|
+
inline-size: 100%;
|
|
27
|
+
max-inline-size: 100%;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.#{$prefix}--header-overflow-panel
|
|
31
|
+
.#{$prefix}--header-overflow-panel-secondary-container {
|
|
32
|
+
padding-block-start: $spacing-05;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.#{$prefix}--header-overflow-panel
|
|
36
|
+
.#{$prefix}--side-nav__menu-secondary-wrapper-expanded {
|
|
37
|
+
z-index: 1;
|
|
38
|
+
background-color: $background;
|
|
39
|
+
inline-size: 100%;
|
|
40
|
+
inset-inline-start: 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.#{$prefix}--header-overflow-panel
|
|
44
|
+
.#{$prefix}--side-nav__back-button.#{$prefix}--btn--ghost {
|
|
45
|
+
flex-direction: row-reverse;
|
|
46
|
+
justify-content: flex-end;
|
|
47
|
+
gap: 1rem;
|
|
48
|
+
inline-size: 100%;
|
|
49
|
+
max-inline-size: 100%;
|
|
50
|
+
|
|
51
|
+
&:focus {
|
|
52
|
+
box-shadow: inset 0 0 0 1px $focus;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.#{$prefix}--header-overflow-panel
|
|
57
|
+
.#{$prefix}--side-nav__back-button.#{$prefix}--btn--ghost:not([disabled]) {
|
|
58
|
+
svg {
|
|
59
|
+
align-self: center;
|
|
60
|
+
fill: $icon-interactive;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
&:hover svg {
|
|
64
|
+
fill: $link-primary-hover;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.#{$prefix}--header-overflow-panel .#{$prefix}--side-nav__item {
|
|
69
|
+
.#{$prefix}--side-nav__link,
|
|
70
|
+
.#{$prefix}--side-nav__submenu {
|
|
71
|
+
block-size: $spacing-08;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@include breakpoint(md) {
|
|
77
|
+
.#{$prefix}--header-overflow-panel.#{$prefix}--popover--tab-tip.#{$prefix}--popover--bottom-end
|
|
78
|
+
.#{$prefix}--popover
|
|
79
|
+
> .#{$prefix}--header-action__content.#{$prefix}--popover-content {
|
|
80
|
+
inset-inline-end: -$spacing-09;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.#{$prefix}--header-overflow-panel .#{$prefix}--toggletip-content {
|
|
85
|
+
padding: 0;
|
|
86
|
+
background-color: $background;
|
|
87
|
+
block-size: 100vh;
|
|
88
|
+
border-block-end: 1px solid $border-subtle;
|
|
89
|
+
border-inline-start: 1px solid $border-subtle;
|
|
90
|
+
gap: 0;
|
|
91
|
+
inline-size: convert.to-rem(256px);
|
|
92
|
+
overflow-y: auto;
|
|
93
|
+
|
|
94
|
+
> * {
|
|
95
|
+
padding: $spacing-05;
|
|
96
|
+
outline: convert.to-rem(0.5px) solid $border-subtle;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.#{$prefix}--header-overflow-panel .#{$prefix}--side-nav__link-text {
|
|
101
|
+
display: flex;
|
|
102
|
+
align-items: center;
|
|
103
|
+
justify-content: space-between;
|
|
104
|
+
inline-size: 100%;
|
|
105
|
+
}
|
|
@@ -15,10 +15,12 @@
|
|
|
15
15
|
$prefix: 'cds' !default;
|
|
16
16
|
|
|
17
17
|
// profile
|
|
18
|
+
|
|
18
19
|
.#{$prefix}--profile.#{$prefix}--popover--open {
|
|
19
20
|
background-color: $background;
|
|
20
21
|
}
|
|
21
22
|
|
|
23
|
+
.#{$prefix}--header-overflow-panel .#{$prefix}--toggletip-content,
|
|
22
24
|
.#{$prefix}--profile .#{$prefix}--toggletip-content {
|
|
23
25
|
padding: 0;
|
|
24
26
|
background-color: $background;
|
|
@@ -28,15 +30,21 @@ $prefix: 'cds' !default;
|
|
|
28
30
|
inline-size: convert.to-rem(256px);
|
|
29
31
|
max-block-size: 100vh;
|
|
30
32
|
overflow-y: auto;
|
|
33
|
+
}
|
|
31
34
|
|
|
32
|
-
|
|
35
|
+
.#{$prefix}--header-overflow-panel,
|
|
36
|
+
.#{$prefix}--profile {
|
|
37
|
+
.#{$prefix}--profile-user-info,
|
|
38
|
+
.#{$prefix}--profile-read-only,
|
|
39
|
+
.clabs--theme-settings {
|
|
33
40
|
padding: $spacing-05;
|
|
34
|
-
|
|
41
|
+
border-block-end: convert.to-rem(0.5px) solid $border-subtle;
|
|
42
|
+
border-block-start: convert.to-rem(0.5px) solid $border-subtle;
|
|
35
43
|
}
|
|
36
44
|
}
|
|
37
45
|
|
|
38
46
|
// user-info
|
|
39
|
-
.#{$prefix}--profile
|
|
47
|
+
.#{$prefix}--profile-user-info {
|
|
40
48
|
display: flex;
|
|
41
49
|
}
|
|
42
50
|
|