@carbon-labs/react-ui-shell 0.81.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.
@@ -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 };
@@ -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>>;
@@ -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 isSm = useMatchMedia(smMediaQuery);
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 primaryClassNames = cx({
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 isSm = useMatchMedia(smMediaQuery);
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: primaryClassNames
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';
@@ -21,7 +21,7 @@ const componentName = 'TooltipTrigger';
21
21
  * This is an tooltip trigger as Carbon Tooltip requires an active element to work but provides
22
22
  * no blanked button.
23
23
  */
24
- let TooltipTrigger = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
24
+ const TooltipTrigger = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
25
25
  let {
26
26
  children,
27
27
  className,
@@ -40,7 +40,6 @@ let TooltipTrigger = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
40
40
  });
41
41
 
42
42
  // Return a placeholder if not released and not enabled by feature flag.
43
- TooltipTrigger = pkg.checkComponentEnabled(TooltipTrigger, componentName);
44
43
 
45
44
  // The display name of the component, used by React. Note that displayName
46
45
  // is used in preference to relying on function.name.
@@ -28,7 +28,7 @@ const componentName = 'UserAvatar';
28
28
  * TODO: A description of the component.
29
29
  */
30
30
 
31
- let UserAvatar = /*#__PURE__*/React__default.forwardRef((props, ref) => {
31
+ const UserAvatar = /*#__PURE__*/React__default.forwardRef((props, ref) => {
32
32
  const {
33
33
  backgroundColor = 'order-1-cyan',
34
34
  className,
@@ -106,7 +106,6 @@ let UserAvatar = /*#__PURE__*/React__default.forwardRef((props, ref) => {
106
106
  });
107
107
 
108
108
  // Return a placeholder if not released and not enabled by feature flag
109
- UserAvatar = pkg.checkComponentEnabled(UserAvatar, componentName);
110
109
 
111
110
  // The display name of the component, used by React. Note that displayName
112
111
  // is used in preference to relying on function.name.
@@ -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 isSm = useMatchMedia.useMatchMedia(smMediaQuery);
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 primaryClassNames = cx({
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 isSm = useMatchMedia.useMatchMedia(smMediaQuery);
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: primaryClassNames
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;
@@ -23,7 +23,7 @@ const componentName = 'TooltipTrigger';
23
23
  * This is an tooltip trigger as Carbon Tooltip requires an active element to work but provides
24
24
  * no blanked button.
25
25
  */
26
- exports.TooltipTrigger = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
26
+ const TooltipTrigger = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
27
27
  let {
28
28
  children,
29
29
  className,
@@ -42,16 +42,15 @@ exports.TooltipTrigger = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
42
42
  });
43
43
 
44
44
  // Return a placeholder if not released and not enabled by feature flag.
45
- exports.TooltipTrigger = settings.pkg.checkComponentEnabled(exports.TooltipTrigger, componentName);
46
45
 
47
46
  // The display name of the component, used by React. Note that displayName
48
47
  // is used in preference to relying on function.name.
49
- exports.TooltipTrigger.displayName = componentName;
48
+ TooltipTrigger.displayName = componentName;
50
49
 
51
50
  // The types and DocGen commentary for the component props,
52
51
  // in alphabetical order (for consistency).
53
52
  // See https://www.npmjs.com/package/prop-types#usage.
54
- exports.TooltipTrigger.propTypes = {
53
+ TooltipTrigger.propTypes = {
55
54
  /**
56
55
  * Child content of tooltip trigger
57
56
  */
@@ -61,3 +60,5 @@ exports.TooltipTrigger.propTypes = {
61
60
  */
62
61
  className: index.default.string
63
62
  };
63
+
64
+ exports.TooltipTrigger = TooltipTrigger;
@@ -30,7 +30,7 @@ const componentName = 'UserAvatar';
30
30
  * TODO: A description of the component.
31
31
  */
32
32
 
33
- exports.UserAvatar = /*#__PURE__*/React__default.forwardRef((props, ref) => {
33
+ const UserAvatar = /*#__PURE__*/React__default.forwardRef((props, ref) => {
34
34
  const {
35
35
  backgroundColor = 'order-1-cyan',
36
36
  className,
@@ -108,16 +108,15 @@ exports.UserAvatar = /*#__PURE__*/React__default.forwardRef((props, ref) => {
108
108
  });
109
109
 
110
110
  // Return a placeholder if not released and not enabled by feature flag
111
- exports.UserAvatar = settings.pkg.checkComponentEnabled(exports.UserAvatar, componentName);
112
111
 
113
112
  // The display name of the component, used by React. Note that displayName
114
113
  // is used in preference to relying on function.name.
115
- exports.UserAvatar.displayName = componentName;
114
+ UserAvatar.displayName = componentName;
116
115
 
117
116
  // The types and DocGen commentary for the component props,
118
117
  // in alphabetical order (for consistency).
119
118
  // See https://www.npmjs.com/package/prop-types#usage.
120
- exports.UserAvatar.propTypes = {
119
+ UserAvatar.propTypes = {
121
120
  /**
122
121
  * Provide the background color need to be set for UserAvatar. Background color will be set based on lighter or darker theme.
123
122
  * For example: if you select order-5-purple, it will take $purple-60 for lighter theme and $purple-50 for darker theme.
@@ -160,3 +159,5 @@ exports.UserAvatar.propTypes = {
160
159
  tooltipText: index.default.string
161
160
  /* TODO: add types and DocGen for all props. */
162
161
  };
162
+
163
+ exports.UserAvatar = UserAvatar;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carbon-labs/react-ui-shell",
3
- "version": "0.81.0",
3
+ "version": "0.83.0",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "provenance": true
@@ -37,10 +37,10 @@
37
37
  },
38
38
  "devDependencies": {
39
39
  "@carbon-labs/utilities": "canary",
40
- "@carbon/ibm-products": "^2.72.1"
40
+ "@carbon/ibm-products": "^2.76.0"
41
41
  },
42
42
  "dependencies": {
43
43
  "@ibm/telemetry-js": "^1.10.2"
44
44
  },
45
- "gitHead": "d0f49a8c73733aa2a17bfbbbd90f8c7a62f8aa5a"
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
- outline: convert.to-rem(0.5px) solid $border-subtle;
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 .#{$prefix}--profile-user-info {
47
+ .#{$prefix}--profile-user-info {
40
48
  display: flex;
41
49
  }
42
50
 
@@ -133,7 +133,7 @@ div:has(.#{$prefix}--header)
133
133
  block-size: $spacing-08;
134
134
  }
135
135
 
136
- .#{$prefix}--side-nav__icon {
136
+ .#{$prefix}--side-nav__menu-secondary-wrapper .#{$prefix}--side-nav__icon {
137
137
  margin-inline-end: convert.to-rem(14px);
138
138
  }
139
139
 
@@ -10,5 +10,6 @@
10
10
  @use 'styles/content';
11
11
  @use 'styles/shark-fin-icon.scss';
12
12
  @use 'styles/header-divider.scss';
13
+ @use 'styles/header-overflow-panel.scss';
13
14
  @use 'styles/trial-countdown.scss';
14
15
  @use 'styles/profile.scss';