@carbon-labs/react-ui-shell 0.24.0 → 0.26.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.
Files changed (50) hide show
  1. package/es/components/HeaderContainer.d.ts +43 -0
  2. package/es/components/HeaderContainer.js +62 -0
  3. package/es/components/SharkFinIcon.d.ts +24 -0
  4. package/es/components/SharkFinIcon.js +59 -0
  5. package/es/components/SideNavFlyoutMenu.d.ts +141 -0
  6. package/es/components/SideNavFlyoutMenu.js +365 -0
  7. package/es/components/SideNavMenu.d.ts +4 -0
  8. package/es/components/SideNavMenu.js +96 -75
  9. package/es/components/SideNavMenuItem.d.ts +4 -0
  10. package/es/components/SideNavMenuItem.js +17 -4
  11. package/es/index.d.ts +2 -0
  12. package/es/index.js +2 -0
  13. package/es/internal/keyboard/keys.js +13 -1
  14. package/es/internal/setupGetInstanceId.d.ts +11 -0
  15. package/es/internal/setupGetInstanceId.js +19 -0
  16. package/es/internal/useId.d.ts +21 -0
  17. package/es/internal/useId.js +100 -0
  18. package/es/internal/useIdPrefix.d.ts +12 -0
  19. package/es/internal/useIdPrefix.js +19 -0
  20. package/es/internal/usePrefix.d.ts +5 -0
  21. package/es/internal/usePrefix.js +6 -0
  22. package/es/node_modules/@carbon/icons-react/es/Icon.js +1 -1
  23. package/lib/components/HeaderContainer.d.ts +43 -0
  24. package/lib/components/HeaderContainer.js +67 -0
  25. package/lib/components/SharkFinIcon.d.ts +24 -0
  26. package/lib/components/SharkFinIcon.js +61 -0
  27. package/lib/components/SideNavFlyoutMenu.d.ts +141 -0
  28. package/lib/components/SideNavFlyoutMenu.js +367 -0
  29. package/lib/components/SideNavMenu.d.ts +4 -0
  30. package/lib/components/SideNavMenu.js +96 -75
  31. package/lib/components/SideNavMenuItem.d.ts +4 -0
  32. package/lib/components/SideNavMenuItem.js +17 -4
  33. package/lib/index.d.ts +2 -0
  34. package/lib/index.js +4 -0
  35. package/lib/internal/keyboard/keys.js +14 -0
  36. package/lib/internal/setupGetInstanceId.d.ts +11 -0
  37. package/lib/internal/setupGetInstanceId.js +23 -0
  38. package/lib/internal/useId.d.ts +21 -0
  39. package/lib/internal/useId.js +103 -0
  40. package/lib/internal/useIdPrefix.d.ts +12 -0
  41. package/lib/internal/useIdPrefix.js +22 -0
  42. package/lib/internal/usePrefix.d.ts +5 -0
  43. package/lib/internal/usePrefix.js +6 -0
  44. package/lib/node_modules/@carbon/icons-react/es/Icon.js +1 -1
  45. package/package.json +2 -2
  46. package/scss/styles/_shark-fin-icon.scss +14 -0
  47. package/scss/styles/_side-nav.scss +147 -0
  48. package/scss/ui-shell.scss +1 -0
  49. /package/es/node_modules/@carbon/{icons-react/node_modules/@carbon/icon-helpers → icon-helpers}/es/index.js +0 -0
  50. /package/lib/node_modules/@carbon/{icons-react/node_modules/@carbon/icon-helpers → icon-helpers}/es/index.js +0 -0
@@ -39,6 +39,10 @@ export interface SideNavMenuProps {
39
39
  * Indicates if the side navigation container is expanded or collapsed.
40
40
  */
41
41
  isSideNavExpanded?: boolean;
42
+ /**
43
+ * The boolean to show the flyout menu has been selected.
44
+ */
45
+ selected?: boolean;
42
46
  /**
43
47
  * The tabIndex for the button element.
44
48
  * If not specified, the default validation will be applied.
@@ -15,9 +15,11 @@ import { match } from '../internal/keyboard/match.js';
15
15
  import { usePrefix } from '../internal/usePrefix.js';
16
16
  import { SideNavContext, SIDE_NAV_TYPE } from './SideNav.js';
17
17
  import { useMergedRefs } from '../internal/useMergedRefs.js';
18
+ import { SharkFinIcon } from './SharkFinIcon.js';
19
+ import { SideNavFlyoutMenu } from './SideNavFlyoutMenu.js';
18
20
  import { ChevronDown } from '../node_modules/@carbon/icons-react/es/generated/bucket-3.js';
19
21
 
20
- var _ChevronDown;
22
+ var _SharkFinIcon, _ChevronDown;
21
23
  const SideNavMenu = /*#__PURE__*/React.forwardRef(function SideNavMenu(_ref, ref) {
22
24
  let {
23
25
  className: customClassName,
@@ -84,6 +86,42 @@ const SideNavMenu = /*#__PURE__*/React.forwardRef(function SideNavMenu(_ref, ref
84
86
  }
85
87
  return child;
86
88
  });
89
+
90
+ /**
91
+ Defining the children parameter with the type ReactNode | ReactNode[]. This allows for various possibilities:
92
+ a single element, an array of elements, or null or undefined.
93
+ **/
94
+ function hasActiveDescendant(children) {
95
+ if (Array.isArray(children)) {
96
+ return children.some(child => {
97
+ if (! /*#__PURE__*/React.isValidElement(child)) {
98
+ setActive(false);
99
+ return false;
100
+ }
101
+
102
+ /** Explicitly defining the expected prop types (isActive and 'aria-current) for the children to ensure type
103
+ safety when accessing their props.
104
+ **/
105
+ const props = child.props;
106
+ if (props.isActive === true || props['aria-current'] || props.children instanceof Array && hasActiveDescendant(props.children)) {
107
+ setActive(true);
108
+ return true;
109
+ }
110
+ return false;
111
+ });
112
+ }
113
+
114
+ // We use React.isValidElement(child) to check if the child is a valid React element before accessing its props
115
+
116
+ if (/*#__PURE__*/React.isValidElement(children)) {
117
+ const props = children.props;
118
+ if (props.isActive === true || props['aria-current']) {
119
+ setActive(true);
120
+ return true;
121
+ }
122
+ }
123
+ return false;
124
+ }
87
125
  useEffect(() => {
88
126
  if (navType == SIDE_NAV_TYPE.PANEL) {
89
127
  // grab first link to redirect if clicked when not expanded
@@ -119,8 +157,9 @@ const SideNavMenu = /*#__PURE__*/React.forwardRef(function SideNavMenu(_ref, ref
119
157
  const parent = parentSideNavMenu(node);
120
158
  if (match(event, ArrowLeft)) {
121
159
  event.stopPropagation();
122
- if (isMenu) {
123
- // collapse menu
160
+
161
+ // collapse menu
162
+ if (isMenu && sideNavExpanded) {
124
163
  if (isExpanded == 'true') {
125
164
  setIsExpanded(false);
126
165
 
@@ -143,8 +182,8 @@ const SideNavMenu = /*#__PURE__*/React.forwardRef(function SideNavMenu(_ref, ref
143
182
  if (match(event, ArrowRight)) {
144
183
  event.stopPropagation();
145
184
 
146
- // expand menu
147
- if (isMenu) {
185
+ // expand menu when sidenav is expanded
186
+ if (isMenu && sideNavExpanded) {
148
187
  setIsExpanded(true);
149
188
 
150
189
  // if already expanded, focus on first element
@@ -163,7 +202,7 @@ const SideNavMenu = /*#__PURE__*/React.forwardRef(function SideNavMenu(_ref, ref
163
202
  // save expanded state before SideNav collapse
164
203
  const [lastExpandedState, setLastExpandedState] = useState(isExpanded);
165
204
 
166
- // reset when SideNav is panel
205
+ // reset to opened/collapsed menu state when Panel SideNav is toggled
167
206
  useEffect(() => {
168
207
  if (navType == SIDE_NAV_TYPE.PANEL && !sideNavExpanded) {
169
208
  setLastExpandedState(isExpanded);
@@ -172,42 +211,53 @@ const SideNavMenu = /*#__PURE__*/React.forwardRef(function SideNavMenu(_ref, ref
172
211
  setIsExpanded(lastExpandedState);
173
212
  }
174
213
  }, [sideNavExpanded]);
175
- return (
176
- /*#__PURE__*/
177
- // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
178
- React.createElement("li", {
179
- role: isTreeview ? 'treeitem' : undefined,
180
- "aria-expanded": isExpanded,
181
- className: className,
182
- ref: listRef,
183
- onKeyDown: handleKeyDown
184
- }, /*#__PURE__*/React.createElement("button", {
185
- "aria-expanded": isExpanded,
186
- className: buttonClassName,
187
- onClick: () => {
188
- // only when sidenav is panel view
189
- if (navType == SIDE_NAV_TYPE.PANEL && !isExpanded && firstLink.current && !sideNavExpanded) {
190
- window.location.href = firstLink.current;
191
- } else {
192
- setIsExpanded(!isExpanded);
193
- setLastExpandedState(!isExpanded);
194
- }
195
- },
196
- ref: menuRef,
197
- type: "button",
198
- tabIndex: isTreeview ? -1 : 0
199
- }, IconElement && /*#__PURE__*/React.createElement(SideNavIcon, null, /*#__PURE__*/React.createElement(IconElement, null)), /*#__PURE__*/React.createElement("span", {
200
- className: `${prefix}--side-nav__submenu-title`
201
- }, title), /*#__PURE__*/React.createElement(SideNavIcon, {
202
- className: `${prefix}--side-nav__submenu-chevron`,
203
- small: true
204
- }, _ChevronDown || (_ChevronDown = /*#__PURE__*/React.createElement(ChevronDown, {
205
- size: 20
206
- })))), /*#__PURE__*/React.createElement("ul", {
207
- className: `${prefix}--side-nav__menu`,
208
- role: "group"
209
- }, childrenToRender))
210
- );
214
+ const [openPopover, setOpenPopover] = React.useState(false);
215
+ const content =
216
+ /*#__PURE__*/
217
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
218
+ React.createElement("li", {
219
+ role: isTreeview ? 'treeitem' : undefined,
220
+ "aria-expanded": isExpanded,
221
+ className: className,
222
+ ref: listRef,
223
+ onKeyDown: handleKeyDown
224
+ }, /*#__PURE__*/React.createElement("button", {
225
+ "aria-expanded": isExpanded,
226
+ className: buttonClassName,
227
+ onClick: () => {
228
+ // only when sidenav is panel view
229
+ if (navType == SIDE_NAV_TYPE.PANEL && !isExpanded && firstLink.current && !sideNavExpanded) {
230
+ setOpenPopover(!openPopover);
231
+ // window.location.href = firstLink.current;
232
+ } else {
233
+ setIsExpanded(!isExpanded);
234
+ setLastExpandedState(!isExpanded);
235
+ }
236
+ },
237
+ ref: menuRef,
238
+ type: "button",
239
+ tabIndex: isTreeview ? -1 : 0
240
+ }, IconElement && /*#__PURE__*/React.createElement(SideNavIcon, null, /*#__PURE__*/React.createElement(IconElement, null)), !sideNavExpanded && /*#__PURE__*/React.createElement("div", {
241
+ className: `${prefix}--side-nav--panel-submenu-caret-container`
242
+ }, /*#__PURE__*/React.createElement("div", {
243
+ className: `${prefix}--side-nav--panel-submenu-caret`
244
+ }, _SharkFinIcon || (_SharkFinIcon = /*#__PURE__*/React.createElement(SharkFinIcon, null)))), /*#__PURE__*/React.createElement("span", {
245
+ className: `${prefix}--side-nav__submenu-title`
246
+ }, title), /*#__PURE__*/React.createElement(SideNavIcon, {
247
+ className: `${prefix}--side-nav__submenu-chevron`,
248
+ small: true
249
+ }, _ChevronDown || (_ChevronDown = /*#__PURE__*/React.createElement(ChevronDown, {
250
+ size: 20
251
+ })))), /*#__PURE__*/React.createElement("ul", {
252
+ className: `${prefix}--side-nav__menu`,
253
+ role: "group"
254
+ }, childrenToRender));
255
+ return navType == SIDE_NAV_TYPE.PANEL && !sideNavExpanded ? /*#__PURE__*/React.createElement(SideNavFlyoutMenu, {
256
+ selected: active,
257
+ className: `${prefix}--side-nav-flyout-menu`,
258
+ title: title,
259
+ menuContent: children
260
+ }, content) : content;
211
261
  });
212
262
  SideNavMenu.displayName = 'SideNavMenu';
213
263
  SideNavMenu.propTypes = {
@@ -249,6 +299,10 @@ SideNavMenu.propTypes = {
249
299
  */
250
300
  // @ts-expect-error - PropTypes are unable to cover this case.
251
301
  renderIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
302
+ /**
303
+ * The boolean to show the flyout menu has been selected.
304
+ */
305
+ selected: PropTypes.bool,
252
306
  /**
253
307
  * Optional prop to specify the tabIndex of the button. If undefined, it will be applied default validation
254
308
  */
@@ -259,37 +313,4 @@ SideNavMenu.propTypes = {
259
313
  title: PropTypes.string.isRequired
260
314
  };
261
315
 
262
- /**
263
- Defining the children parameter with the type ReactNode | ReactNode[]. This allows for various possibilities:
264
- a single element, an array of elements, or null or undefined.
265
- **/
266
- function hasActiveDescendant(children) {
267
- if (Array.isArray(children)) {
268
- return children.some(child => {
269
- if (! /*#__PURE__*/React.isValidElement(child)) {
270
- return false;
271
- }
272
-
273
- /** Explicitly defining the expected prop types (isActive and 'aria-current) for the children to ensure type
274
- safety when accessing their props.
275
- **/
276
- const props = child.props;
277
- if (props.isActive === true || props['aria-current'] || props.children instanceof Array && hasActiveDescendant(props.children)) {
278
- return true;
279
- }
280
- return false;
281
- });
282
- }
283
-
284
- // We use React.isValidElement(child) to check if the child is a valid React element before accessing its props
285
-
286
- if (/*#__PURE__*/React.isValidElement(children)) {
287
- const props = children.props;
288
- if (props.isActive === true || props['aria-current']) {
289
- return true;
290
- }
291
- }
292
- return false;
293
- }
294
-
295
316
  export { SideNavMenu };
@@ -21,6 +21,10 @@ export interface SideNavMenuItemProps extends ComponentProps<typeof Link> {
21
21
  * `aria-current="page"`, as well.
22
22
  */
23
23
  isActive?: boolean;
24
+ /**
25
+ * Specify whether the link is part of a "SideNavMenu" in "flyout menu" mode.
26
+ */
27
+ isFlyoutMenuItem?: boolean;
24
28
  /**
25
29
  * Optionally provide an href for the underlying li`
26
30
  */
@@ -13,7 +13,9 @@ import { SideNavIcon, SideNavLinkText } from '@carbon/react';
13
13
  import Link from './Link.js';
14
14
  import { usePrefix } from '../internal/usePrefix.js';
15
15
  import { SideNavContext } from './SideNav.js';
16
+ import { Checkmark } from '../node_modules/@carbon/icons-react/es/generated/bucket-3.js';
16
17
 
18
+ var _SideNavIcon;
17
19
  const SideNavMenuItem = /*#__PURE__*/React.forwardRef(function SideNavMenuItem(props, ref) {
18
20
  const prefix = usePrefix();
19
21
  const {
@@ -21,16 +23,21 @@ const SideNavMenuItem = /*#__PURE__*/React.forwardRef(function SideNavMenuItem(p
21
23
  className: customClassName,
22
24
  as: Component = Link,
23
25
  isActive,
26
+ isFlyoutMenuItem,
24
27
  renderIcon: IconElement,
25
28
  ...rest
26
29
  } = props;
27
30
  const {
28
31
  isTreeview
29
32
  } = useContext(SideNavContext);
30
- const className = cx(`${prefix}--side-nav__menu-item`, customClassName);
33
+ const className = cx({
34
+ [`${prefix}--side-nav__menu-item`]: true,
35
+ [`${prefix}--side-nav__menu-item--flyout-menu-item`]: isFlyoutMenuItem
36
+ }, customClassName);
31
37
  const linkClassName = cx({
32
38
  [`${prefix}--side-nav__link`]: true,
33
- [`${prefix}--side-nav__link--current`]: isActive
39
+ [`${prefix}--side-nav__link--active`]: isActive && isFlyoutMenuItem,
40
+ [`${prefix}--side-nav__link--current`]: isActive && !isFlyoutMenuItem
34
41
  });
35
42
  return /*#__PURE__*/React.createElement("li", {
36
43
  className: className
@@ -40,9 +47,11 @@ const SideNavMenuItem = /*#__PURE__*/React.forwardRef(function SideNavMenuItem(p
40
47
  className: linkClassName,
41
48
  tabIndex: isTreeview ? -1 : 0,
42
49
  ref: ref
43
- }), IconElement && /*#__PURE__*/React.createElement(SideNavIcon, {
50
+ }), IconElement && !isFlyoutMenuItem && /*#__PURE__*/React.createElement(SideNavIcon, {
44
51
  small: true
45
- }, /*#__PURE__*/React.createElement(IconElement, null)), /*#__PURE__*/React.createElement(SideNavLinkText, null, children)));
52
+ }, /*#__PURE__*/React.createElement(IconElement, null)), isActive && isFlyoutMenuItem && (_SideNavIcon || (_SideNavIcon = /*#__PURE__*/React.createElement(SideNavIcon, {
53
+ small: true
54
+ }, /*#__PURE__*/React.createElement(Checkmark, null)))), /*#__PURE__*/React.createElement(SideNavLinkText, null, children)));
46
55
  });
47
56
  SideNavMenuItem.displayName = 'SideNavMenuItem';
48
57
  SideNavMenuItem.propTypes = {
@@ -68,6 +77,10 @@ SideNavMenuItem.propTypes = {
68
77
  * `aria-current="page"`, as well.
69
78
  */
70
79
  isActive: PropTypes.bool,
80
+ /**
81
+ * Specify whether the link is part of a "SideNavMenu" in "flyout menu" mode.
82
+ */
83
+ isFlyoutMenuItem: PropTypes.bool,
71
84
  /**
72
85
  * Provide an icon to render in the side navigation link. Should be a React class.
73
86
  */
package/es/index.d.ts CHANGED
@@ -12,4 +12,6 @@ export { SideNavLink } from './components/SideNavLink.js';
12
12
  export { SideNavLinkPopover } from './components/SideNavLinkPopover.js';
13
13
  export { SideNavMenu } from './components/SideNavMenu.js';
14
14
  export { SideNavMenuItem } from './components/SideNavMenuItem.js';
15
+ export { HeaderContainer } from './components/HeaderContainer';
15
16
  export { HeaderPanel } from './components/HeaderPanel';
17
+ export { SharkFinIcon } from './components/SharkFinIcon';
package/es/index.js CHANGED
@@ -11,4 +11,6 @@ export { SideNavLink } from './components/SideNavLink.js';
11
11
  export { SideNavLinkPopover } from './components/SideNavLinkPopover.js';
12
12
  export { SideNavMenu } from './components/SideNavMenu.js';
13
13
  export { SideNavMenuItem } from './components/SideNavMenuItem.js';
14
+ export { HeaderContainer } from './components/HeaderContainer.js';
14
15
  export { HeaderPanel } from './components/HeaderPanel.js';
16
+ export { SharkFinIcon } from './components/SharkFinIcon.js';
@@ -19,6 +19,12 @@ const Tab = {
19
19
  keyCode: 9,
20
20
  code: 'Tab'
21
21
  };
22
+ const Enter = {
23
+ key: 'Enter',
24
+ which: 13,
25
+ keyCode: 13,
26
+ code: 'Enter'
27
+ };
22
28
  const Escape = {
23
29
  key: ['Escape',
24
30
  // IE11 Escape
@@ -27,6 +33,12 @@ const Escape = {
27
33
  keyCode: 27,
28
34
  code: 'Esc'
29
35
  };
36
+ const Space = {
37
+ key: ' ',
38
+ which: 32,
39
+ keyCode: 32,
40
+ code: 'Space'
41
+ };
30
42
  const End = {
31
43
  key: 'End',
32
44
  which: 35,
@@ -64,4 +76,4 @@ const ArrowDown = {
64
76
  code: 'ArrowDown'
65
77
  };
66
78
 
67
- export { ArrowDown, ArrowLeft, ArrowRight, ArrowUp, End, Escape, Home, Tab };
79
+ export { ArrowDown, ArrowLeft, ArrowRight, ArrowUp, End, Enter, Escape, Home, Space, Tab };
@@ -0,0 +1,11 @@
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
+ /**
8
+ * Generic utility to initialize a method that will return a unique instance id
9
+ * for a component.
10
+ */
11
+ export default function setupGetInstanceId(): () => number;
@@ -0,0 +1,19 @@
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
+ /**
9
+ * Generic utility to initialize a method that will return a unique instance id
10
+ * for a component.
11
+ */
12
+ function setupGetInstanceId() {
13
+ let instanceId = 0;
14
+ return function getInstanceId() {
15
+ return ++instanceId;
16
+ };
17
+ }
18
+
19
+ export { setupGetInstanceId as default };
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Generate a unique ID for React <=17 with an optional prefix prepended to it.
3
+ * This is an internal utility, not intended for public usage.
4
+ * @param {string} [prefix] the optional prefix id
5
+ * @returns {string}
6
+ */
7
+ export function useCompatibleId(prefix?: string | undefined): string;
8
+ /**
9
+ * Generate a unique id if a given `id` is not provided
10
+ * This is an internal utility, not intended for public usage.
11
+ * @param {string|undefined} id the provided id
12
+ * @returns {string}
13
+ */
14
+ export function useFallbackId(id: string | undefined): string;
15
+ /**
16
+ * Generate a unique ID for React >=18 with an optional prefix prepended to it.
17
+ * This is an internal utility, not intended for public usage.
18
+ * @param {string} [prefix] the optional prefix id
19
+ * @returns {string}
20
+ */
21
+ export function useId(prefix?: string | undefined): string;
@@ -0,0 +1,100 @@
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 React, { useState, useEffect, useLayoutEffect } from 'react';
9
+ import setupGetInstanceId from './setupGetInstanceId.js';
10
+ import { useIdPrefix } from './useIdPrefix.js';
11
+ import { canUseDOM } from './environment.js';
12
+
13
+ // This file was heavily inspired by:
14
+ //
15
+ // 1. Reach UI and their work on their auto-id package:
16
+ // https://github.com/reach/reach-ui/blob/86a046f54d53b6420e392b3fa56dd991d9d4e458/packages/auto-id/src/index.ts
17
+ //
18
+ // 2. Floating UI and their work on react >=18 compatibility
19
+ // https://github.com/floating-ui/floating-ui/blob/%40floating-ui/utils%400.2.5/packages/react/src/hooks/useId.ts
20
+ //
21
+ // The problem that this solves is an id mismatch when auto-generating
22
+ // ids on both the server and the client. When using server-side rendering,
23
+ // there can be the chance of a mismatch between what the server renders and
24
+ // what the client renders when the id value is auto-generated.
25
+ //
26
+ // To get around this, we set the initial value of the `id` to `null` and then
27
+ // conditionally use `useLayoutEffect` on the client and `useEffect` on the
28
+ // server. On the client, `useLayoutEffect` will patch up the id to the correct
29
+ // value. On the server, `useEffect` will not run.
30
+ //
31
+ // This ensures that we won't encounter a mismatch in ids between server and
32
+ // client, at the cost of runtime patching of the id value in
33
+ // `useLayoutEffect`
34
+ //
35
+ // React 18 introduced a new hook called `useId` that takes care of hydration
36
+ // mismatches. If the user is running React 18 or higher, the native hook is
37
+ // used via the `useReactId` function. If the user is running React 17 or
38
+ // lower, `useCompatibleId` is used.
39
+
40
+
41
+ // This tricks bundlers so they can't statically analyze this and produce
42
+ // compilation warnings/errors.
43
+ // https://github.com/webpack/webpack/issues/14814
44
+ // https://github.com/mui/material-ui/issues/41190
45
+ const _React = {
46
+ ...React
47
+ };
48
+ const instanceId = setupGetInstanceId();
49
+ const useIsomorphicLayoutEffect = canUseDOM ? useLayoutEffect : useEffect;
50
+ let serverHandoffCompleted = false;
51
+ const defaultId = 'id';
52
+
53
+ /**
54
+ * Generate a unique ID for React <=17 with an optional prefix prepended to it.
55
+ * This is an internal utility, not intended for public usage.
56
+ * @param {string} [prefix] the optional prefix id
57
+ * @returns {string}
58
+ */
59
+ function useCompatibleId() {
60
+ let prefix = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultId;
61
+ const contextPrefix = useIdPrefix();
62
+ const [id, setId] = useState(() => {
63
+ if (serverHandoffCompleted) {
64
+ return `${contextPrefix ? `${contextPrefix}-` : ``}${prefix}-${instanceId()}`;
65
+ }
66
+ return null;
67
+ });
68
+ useIsomorphicLayoutEffect(() => {
69
+ if (id === null) {
70
+ setId(`${contextPrefix ? `${contextPrefix}-` : ``}${prefix}-${instanceId()}`);
71
+ }
72
+ }, [instanceId]);
73
+ useEffect(() => {
74
+ if (serverHandoffCompleted === false) {
75
+ serverHandoffCompleted = true;
76
+ }
77
+ }, []);
78
+ return id;
79
+ }
80
+
81
+ /**
82
+ * Generate a unique ID for React >=18 with an optional prefix prepended to it.
83
+ * This is an internal utility, not intended for public usage.
84
+ * @param {string} [prefix] the optional prefix id
85
+ * @returns {string}
86
+ */
87
+ function useReactId() {
88
+ let prefix = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultId;
89
+ const contextPrefix = useIdPrefix();
90
+ return `${contextPrefix ? `${contextPrefix}-` : ``}${prefix}-${_React.useId()}`;
91
+ }
92
+
93
+ /**
94
+ * Uses React 18's built-in `useId()` when available, or falls back to a
95
+ * slightly less performant (requiring a double render) implementation for
96
+ * earlier React versions.
97
+ */
98
+ const useId = _React.useId ? useReactId : useCompatibleId;
99
+
100
+ export { useCompatibleId, useId };
@@ -0,0 +1,12 @@
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 React from 'react';
8
+ export declare const IdPrefixContext: React.Context<string | null | undefined>;
9
+ /**
10
+ *
11
+ */
12
+ export declare function useIdPrefix(): string | null | undefined;
@@ -0,0 +1,19 @@
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 React from 'react';
9
+
10
+ const IdPrefixContext = /*#__PURE__*/React.createContext(null);
11
+
12
+ /**
13
+ *
14
+ */
15
+ function useIdPrefix() {
16
+ return React.useContext(IdPrefixContext);
17
+ }
18
+
19
+ export { IdPrefixContext, useIdPrefix };
@@ -6,4 +6,9 @@
6
6
  */
7
7
  import React from 'react';
8
8
  export declare const PrefixContext: React.Context<string>;
9
+ /**
10
+ * An internal function to return the prefix used in components and styles.
11
+ *
12
+ * @returns a react context including the prefix
13
+ */
9
14
  export declare function usePrefix(): string;
@@ -16,6 +16,12 @@ import React from 'react';
16
16
  */
17
17
 
18
18
  const PrefixContext = /*#__PURE__*/React.createContext('cds');
19
+
20
+ /**
21
+ * An internal function to return the prefix used in components and styles.
22
+ *
23
+ * @returns a react context including the prefix
24
+ */
19
25
  function usePrefix() {
20
26
  return React.useContext(PrefixContext);
21
27
  }
@@ -5,7 +5,7 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import { getAttributes } from '../node_modules/@carbon/icon-helpers/es/index.js';
8
+ import { getAttributes } from '../../icon-helpers/es/index.js';
9
9
  import PropTypes from 'prop-types';
10
10
  import React from 'react';
11
11
 
@@ -0,0 +1,43 @@
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 PropTypes from 'prop-types';
8
+ import React from 'react';
9
+ export interface HeaderContainerRenderProps {
10
+ isSideNavExpanded: boolean;
11
+ isSwitcherExpanded: boolean;
12
+ onClickSideNavExpand: () => void;
13
+ onClickSwitcherExpand: () => void;
14
+ }
15
+ export type HeaderContainerProps<P extends HeaderContainerRenderProps> = {
16
+ isSideNavExpanded?: boolean;
17
+ isSwitcherExpanded?: boolean;
18
+ render: React.ComponentType<P>;
19
+ } & {
20
+ [K in keyof Omit<P, keyof HeaderContainerRenderProps>]: P[K];
21
+ };
22
+ export declare function HeaderContainer<P extends HeaderContainerRenderProps>({ render: Children, isSideNavExpanded, isSwitcherExpanded, ...rest }: HeaderContainerProps<P>): import("react/jsx-runtime").JSX.Element;
23
+ export declare namespace HeaderContainer {
24
+ var propTypes: {
25
+ /**
26
+ * `true` if the side navigation is expanded.
27
+ */
28
+ isSideNavExpanded: PropTypes.Requireable<boolean>;
29
+ /**
30
+ * `true` if the switcher is expanded.
31
+ */
32
+ isSwitcherExpanded: PropTypes.Requireable<boolean>;
33
+ /**
34
+ * A function or a component that is invoked with `isSideNavExpanded` and `onClickSideNavExpand`.
35
+ * The function or component can then use those properties to within the components it
36
+ * returns, such as with the HeaderMenuButton and SideNav components. Additional props will also be passed
37
+ * into this component for convenience.
38
+ */
39
+ render: PropTypes.Validator<NonNullable<PropTypes.ReactComponentLike>>;
40
+ };
41
+ var displayName: string;
42
+ }
43
+ export default HeaderContainer;