@carbon-labs/react-ui-shell 0.41.0 → 0.43.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/SideNav.d.ts +3 -0
- package/es/components/SideNav.js +39 -10
- package/es/components/SideNavMenu.d.ts +19 -0
- package/es/components/SideNavMenu.js +127 -20
- package/es/node_modules/@carbon/icons-react/es/generated/bucket-0.js +2956 -0
- package/lib/components/SideNav.d.ts +3 -0
- package/lib/components/SideNav.js +39 -10
- package/lib/components/SideNavMenu.d.ts +19 -0
- package/lib/components/SideNavMenu.js +123 -16
- package/lib/node_modules/@carbon/icons-react/es/generated/bucket-0.js +3082 -0
- package/package.json +2 -2
- package/scss/styles/_side-nav.scss +89 -0
|
@@ -21,6 +21,7 @@ export interface SideNavProps extends ComponentProps<'nav'>, TranslateWithId<Tra
|
|
|
21
21
|
defaultExpanded?: boolean;
|
|
22
22
|
isChildOfHeader?: boolean;
|
|
23
23
|
onToggle?: (event: FocusEvent<HTMLElement> | KeyboardEvent<HTMLElement> | boolean, value: boolean) => void;
|
|
24
|
+
hideRailBreakpointDown?: 'sm' | 'md' | 'lg' | 'xlg' | 'max';
|
|
24
25
|
href?: string;
|
|
25
26
|
isFixedNav?: boolean;
|
|
26
27
|
isRail?: boolean;
|
|
@@ -42,6 +43,8 @@ interface SideNavContextData {
|
|
|
42
43
|
navType?: SIDE_NAV_TYPE;
|
|
43
44
|
isTreeview?: boolean;
|
|
44
45
|
setIsTreeview?: (value: boolean) => void;
|
|
46
|
+
currentPrimaryMenu?: string;
|
|
47
|
+
setCurrentPrimaryMenu?: (value: string) => void;
|
|
45
48
|
}
|
|
46
49
|
export declare const SideNavContext: React.Context<SideNavContextData>;
|
|
47
50
|
export declare const SideNav: React.ForwardRefExoticComponent<Omit<SideNavProps, "ref"> & React.RefAttributes<HTMLElement>>;
|
package/es/components/SideNav.js
CHANGED
|
@@ -48,6 +48,7 @@ function SideNavRenderFunction(_ref, ref) {
|
|
|
48
48
|
children,
|
|
49
49
|
onToggle,
|
|
50
50
|
className: customClassName,
|
|
51
|
+
hideRailBreakpointDown,
|
|
51
52
|
href,
|
|
52
53
|
isFixedNav = false,
|
|
53
54
|
isRail,
|
|
@@ -74,6 +75,7 @@ function SideNavRenderFunction(_ref, ref) {
|
|
|
74
75
|
const expanded = controlled ? expandedProp : expandedState;
|
|
75
76
|
const sideNavRef = useRef(null);
|
|
76
77
|
const navRef = useMergedRefs([sideNavRef, ref]);
|
|
78
|
+
const [currentPrimaryMenu, setCurrentPrimaryMenu] = useState();
|
|
77
79
|
const sideNavToggleText = expandedState ? t('collapse.sidenav') : t('expand.sidenav');
|
|
78
80
|
const handleToggle = function (event) {
|
|
79
81
|
let value = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : !expanded;
|
|
@@ -95,6 +97,7 @@ function SideNavRenderFunction(_ref, ref) {
|
|
|
95
97
|
[`${prefix}--side-nav`]: true,
|
|
96
98
|
[`${prefix}--side-nav--expanded`]: expanded || expandedViaHoverState,
|
|
97
99
|
[`${prefix}--side-nav--collapsed`]: !expanded && isFixedNav,
|
|
100
|
+
[`${prefix}--side-nav--hide-rail-breakpoint-down-${hideRailBreakpointDown}`]: hideRailBreakpointDown,
|
|
98
101
|
[`${prefix}--side-nav--rail`]: isRail,
|
|
99
102
|
[`${prefix}--side-nav--panel`]: navType === SIDE_NAV_TYPE.PANEL,
|
|
100
103
|
[`${prefix}--side-nav--ux`]: isChildOfHeader,
|
|
@@ -147,11 +150,22 @@ function SideNavRenderFunction(_ref, ref) {
|
|
|
147
150
|
resetNodeTabIndices();
|
|
148
151
|
}
|
|
149
152
|
}, [prefix, internalIsTreeview]);
|
|
153
|
+
const smMediaQuery = `(min-width: ${breakpoints.sm.width})`;
|
|
154
|
+
const isSm = useMatchMedia(smMediaQuery);
|
|
150
155
|
useEffect(() => {
|
|
151
156
|
if (sideNavRef.current) {
|
|
157
|
+
const backButton = sideNavRef?.current.querySelector(`.${prefix}--side-nav__back-button`);
|
|
152
158
|
const firstElement = sideNavRef?.current?.querySelector('a, button');
|
|
153
|
-
if (
|
|
154
|
-
|
|
159
|
+
if (navType == SIDE_NAV_TYPE.PANEL || expanded) {
|
|
160
|
+
if (isSm && backButton) {
|
|
161
|
+
backButton.tabIndex = 0;
|
|
162
|
+
const firstElementAfterBack = backButton.nextElementSibling?.querySelector('a, button');
|
|
163
|
+
if (firstElementAfterBack) {
|
|
164
|
+
firstElementAfterBack.tabIndex = 0;
|
|
165
|
+
}
|
|
166
|
+
} else if (firstElement) {
|
|
167
|
+
firstElement.tabIndex = 0;
|
|
168
|
+
}
|
|
155
169
|
}
|
|
156
170
|
}
|
|
157
171
|
}, [expanded]);
|
|
@@ -219,15 +233,17 @@ function SideNavRenderFunction(_ref, ref) {
|
|
|
219
233
|
let nextFocusNode = null;
|
|
220
234
|
if (match(event, ArrowUp)) {
|
|
221
235
|
const parentNode = parentSideNavMenu(treeWalker.currentNode);
|
|
222
|
-
let previousSideNavMenu =
|
|
223
|
-
|
|
236
|
+
let previousSideNavMenu = treeWalker.currentNode?.previousSibling;
|
|
224
237
|
// skip the divider
|
|
225
238
|
if (previousSideNavMenu?.classList.contains(`${prefix}--side-nav__divider`)) {
|
|
226
239
|
previousSideNavMenu = previousSideNavMenu?.previousElementSibling;
|
|
227
240
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
if (
|
|
241
|
+
if (previousSideNavMenu?.classList.contains(`${prefix}--side-nav__item--primary`)) {
|
|
242
|
+
nextFocusNode = previousSideNavMenu;
|
|
243
|
+
} else if (treeWalker.currentNode.classList.contains(`${prefix}--side-nav__item--primary`)) {
|
|
244
|
+
nextFocusNode = treeWalker.currentNode.previousSibling;
|
|
245
|
+
} // when previous sibling is open, go to its last item
|
|
246
|
+
else if (previousSideNavMenu?.getAttribute('aria-expanded') == 'true') {
|
|
231
247
|
const allItems = previousSideNavMenu.querySelectorAll(`.${prefix}--side-nav__item`);
|
|
232
248
|
const lastMenu = allItems[allItems.length - 1];
|
|
233
249
|
if (lastMenu && lastMenu.getAttribute('aria-expanded') == 'false') {
|
|
@@ -239,7 +255,7 @@ function SideNavRenderFunction(_ref, ref) {
|
|
|
239
255
|
nextFocusNode = treeWalker.previousSibling();
|
|
240
256
|
|
|
241
257
|
// first item in the menu, go back up to SideNavMenu button
|
|
242
|
-
if (nextFocusNode == null) {
|
|
258
|
+
if (nextFocusNode == null && !parentNode.classList.contains(`${prefix}--side-nav__item--primary`)) {
|
|
243
259
|
nextFocusNode = parentNode;
|
|
244
260
|
}
|
|
245
261
|
}
|
|
@@ -251,6 +267,11 @@ function SideNavRenderFunction(_ref, ref) {
|
|
|
251
267
|
const parent = parentSideNavMenu(treeWalker.currentNode);
|
|
252
268
|
nextFocusNode = parent?.nextElementSibling;
|
|
253
269
|
}
|
|
270
|
+
} else if (treeWalker.currentNode.classList.contains(`${prefix}--side-nav__item--primary`)) {
|
|
271
|
+
nextFocusNode = treeWalker.currentNode.nextSibling;
|
|
272
|
+
if (nextFocusNode?.classList.contains(`${prefix}--side-nav__divider`)) {
|
|
273
|
+
nextFocusNode = nextFocusNode.nextSibling;
|
|
274
|
+
}
|
|
254
275
|
} else {
|
|
255
276
|
nextFocusNode = treeWalker.nextNode();
|
|
256
277
|
}
|
|
@@ -325,7 +346,7 @@ function SideNavRenderFunction(_ref, ref) {
|
|
|
325
346
|
function resetNodeTabIndices() {
|
|
326
347
|
const items = sideNavRef?.current?.querySelectorAll('[tabIndex="0"]') ?? [];
|
|
327
348
|
items.forEach(item => {
|
|
328
|
-
if (item.classList.contains(`${prefix}--side-nav__toggle`)) {
|
|
349
|
+
if (item.classList.contains(`${prefix}--side-nav__toggle`) || item.classList.contains(`${prefix}--side-nav__back-button`)) {
|
|
329
350
|
return;
|
|
330
351
|
}
|
|
331
352
|
item.tabIndex = -1;
|
|
@@ -356,7 +377,9 @@ function SideNavRenderFunction(_ref, ref) {
|
|
|
356
377
|
isRail,
|
|
357
378
|
navType,
|
|
358
379
|
isTreeview: internalIsTreeview,
|
|
359
|
-
setIsTreeview
|
|
380
|
+
setIsTreeview,
|
|
381
|
+
currentPrimaryMenu,
|
|
382
|
+
setCurrentPrimaryMenu
|
|
360
383
|
}
|
|
361
384
|
}, isFixedNav || hideOverlay ? null :
|
|
362
385
|
/*#__PURE__*/
|
|
@@ -410,6 +433,12 @@ SideNav.propTypes = {
|
|
|
410
433
|
* If `true`, the overlay will be hidden. Defaults to `false`.
|
|
411
434
|
*/
|
|
412
435
|
hideOverlay: PropTypes.bool,
|
|
436
|
+
/**
|
|
437
|
+
* Specify the breakpoint at which the SideNav will be hidden.
|
|
438
|
+
* Can be one of `sm`, `md`, `lg`, `xlg`, or `max`.
|
|
439
|
+
* Only applies when `isRail` is `true`.
|
|
440
|
+
*/
|
|
441
|
+
hideRailBreakpointDown: PropTypes.oneOf(['sm', 'md', 'lg', 'xlg', 'max']),
|
|
413
442
|
/**
|
|
414
443
|
* Provide the `href` to the id of the element on your package that is the
|
|
415
444
|
* main content.
|
|
@@ -6,6 +6,15 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import React from 'react';
|
|
8
8
|
export interface SideNavMenuProps {
|
|
9
|
+
/**
|
|
10
|
+
* Title for back button in sm screen
|
|
11
|
+
*/
|
|
12
|
+
backButtonTitle?: string;
|
|
13
|
+
/**
|
|
14
|
+
* A custom icon to render on the back button in sm screen
|
|
15
|
+
* default is ArrowLeft
|
|
16
|
+
*/
|
|
17
|
+
backButtonRenderIcon?: React.ComponentType;
|
|
9
18
|
/**
|
|
10
19
|
* An optional CSS class to apply to the component.
|
|
11
20
|
*/
|
|
@@ -23,6 +32,10 @@ export interface SideNavMenuProps {
|
|
|
23
32
|
* SideNavMenu depth to determine spacing
|
|
24
33
|
*/
|
|
25
34
|
depth?: number;
|
|
35
|
+
/**
|
|
36
|
+
* Provide a unique id
|
|
37
|
+
*/
|
|
38
|
+
id?: string;
|
|
26
39
|
/**
|
|
27
40
|
* Indicates whether the SideNavMenu is active.
|
|
28
41
|
*/
|
|
@@ -44,6 +57,12 @@ export interface SideNavMenuProps {
|
|
|
44
57
|
* Indicates if the side navigation container is expanded or collapsed.
|
|
45
58
|
*/
|
|
46
59
|
isSideNavExpanded?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Specifies if this is the primary SideNav.
|
|
62
|
+
* If true, child components will open to the right,
|
|
63
|
+
* creating the double-wide navigation layout
|
|
64
|
+
*/
|
|
65
|
+
primary?: boolean;
|
|
47
66
|
/**
|
|
48
67
|
* The boolean to show the flyout menu has been selected.
|
|
49
68
|
*/
|
|
@@ -5,33 +5,45 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { breakpoints } from '../node_modules/@carbon/layout/es/index.js';
|
|
8
9
|
import cx from '../_virtual/index.js';
|
|
9
10
|
import PropTypes from 'prop-types';
|
|
10
11
|
import React, { useContext, useState, useRef, useEffect } from 'react';
|
|
11
12
|
import { CARBON_SIDENAV_ITEMS } from './_utils.js';
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
13
|
+
import { useId } from '../internal/useId.js';
|
|
14
|
+
import { SideNavIcon, Button } from '@carbon/react';
|
|
15
|
+
import { Escape, ArrowLeft as ArrowLeft$1, ArrowRight } from '../internal/keyboard/keys.js';
|
|
14
16
|
import { match } from '../internal/keyboard/match.js';
|
|
15
17
|
import { usePrefix } from '../internal/usePrefix.js';
|
|
16
18
|
import { SideNavContext, SIDE_NAV_TYPE } from './SideNav.js';
|
|
17
19
|
import { useMergedRefs } from '../internal/useMergedRefs.js';
|
|
18
20
|
import { SharkFinIcon } from './SharkFinIcon.js';
|
|
19
21
|
import { SideNavFlyoutMenu } from './SideNavFlyoutMenu.js';
|
|
20
|
-
import {
|
|
22
|
+
import { SideNavItems } from './SideNavItems.js';
|
|
23
|
+
import { useMatchMedia } from '../internal/useMatchMedia.js';
|
|
24
|
+
import { ArrowLeft } from '../node_modules/@carbon/icons-react/es/generated/bucket-0.js';
|
|
25
|
+
import { ChevronRight, ChevronDown } from '../node_modules/@carbon/icons-react/es/generated/bucket-3.js';
|
|
21
26
|
|
|
22
|
-
var _SharkFinIcon, _ChevronDown;
|
|
27
|
+
var _ArrowLeft, _SharkFinIcon, _ChevronRight, _ChevronDown;
|
|
28
|
+
const smMediaQuery = `(max-width: ${breakpoints.md.width})`;
|
|
23
29
|
const SideNavMenu = /*#__PURE__*/React.forwardRef(function SideNavMenu(_ref, ref) {
|
|
24
30
|
let {
|
|
31
|
+
backButtonRenderIcon = () => _ArrowLeft || (_ArrowLeft = /*#__PURE__*/React.createElement(ArrowLeft, {
|
|
32
|
+
size: 16
|
|
33
|
+
})),
|
|
34
|
+
backButtonTitle = 'My products',
|
|
25
35
|
className: customClassName,
|
|
26
36
|
children,
|
|
27
37
|
defaultExpanded = false,
|
|
28
38
|
depth: propDepth,
|
|
39
|
+
id,
|
|
29
40
|
isActive = false,
|
|
30
41
|
large = false,
|
|
31
42
|
renderIcon: IconElement,
|
|
32
43
|
isSideNavExpanded,
|
|
33
44
|
title,
|
|
34
|
-
onMenuToggle
|
|
45
|
+
onMenuToggle,
|
|
46
|
+
primary
|
|
35
47
|
} = _ref;
|
|
36
48
|
const depth = propDepth;
|
|
37
49
|
const {
|
|
@@ -46,10 +58,19 @@ const SideNavMenu = /*#__PURE__*/React.forwardRef(function SideNavMenu(_ref, ref
|
|
|
46
58
|
const [isExpanded, setIsExpanded] = useState(defaultExpanded);
|
|
47
59
|
const [active, setActive] = useState(isActive);
|
|
48
60
|
const firstLink = useRef(null);
|
|
61
|
+
const backButtonRef = useRef(null);
|
|
62
|
+
const uid = useId('side-nav-menu');
|
|
63
|
+
const uniqueId = id || uid;
|
|
49
64
|
const [prevExpanded, setPrevExpanded] = useState(defaultExpanded);
|
|
65
|
+
const [isSecondaryOpen, setSecondaryOpen] = useState(defaultExpanded);
|
|
66
|
+
const {
|
|
67
|
+
currentPrimaryMenu,
|
|
68
|
+
setCurrentPrimaryMenu
|
|
69
|
+
} = useContext(SideNavContext);
|
|
50
70
|
const className = cx({
|
|
51
71
|
[`${prefix}--side-nav__item`]: true,
|
|
52
|
-
[`${prefix}--side-nav__item--
|
|
72
|
+
[`${prefix}--side-nav__item--primary`]: primary,
|
|
73
|
+
[`${prefix}--side-nav__item--active`]: !primary && (active || hasActiveDescendant(children) && !isExpanded),
|
|
53
74
|
[`${prefix}--side-nav__item--icon`]: IconElement,
|
|
54
75
|
[`${prefix}--side-nav__item--large`]: large,
|
|
55
76
|
[customClassName]: !!customClassName
|
|
@@ -58,6 +79,10 @@ const SideNavMenu = /*#__PURE__*/React.forwardRef(function SideNavMenu(_ref, ref
|
|
|
58
79
|
[`${prefix}--side-nav__submenu`]: true,
|
|
59
80
|
[`${prefix}--side-nav__submenu--active`]: active || hasActiveDescendant(children) && isExpanded
|
|
60
81
|
});
|
|
82
|
+
const primaryClassNames = cx({
|
|
83
|
+
[`${prefix}--side-nav__menu-secondary-wrapper`]: true,
|
|
84
|
+
[`${prefix}--side-nav__menu-secondary-wrapper-expanded`]: isSideNavExpanded && isSecondaryOpen && currentPrimaryMenu === uniqueId
|
|
85
|
+
});
|
|
61
86
|
const buttonRef = useRef(null);
|
|
62
87
|
const listRef = useRef(null);
|
|
63
88
|
const menuRef = useMergedRefs([buttonRef, ref]);
|
|
@@ -159,7 +184,7 @@ const SideNavMenu = /*#__PURE__*/React.forwardRef(function SideNavMenu(_ref, ref
|
|
|
159
184
|
const isMenu = node.hasAttribute('aria-expanded');
|
|
160
185
|
const isExpanded = node.getAttribute('aria-expanded');
|
|
161
186
|
const parent = parentSideNavMenu(node);
|
|
162
|
-
if (match(event, ArrowLeft)) {
|
|
187
|
+
if (match(event, ArrowLeft$1)) {
|
|
163
188
|
event.stopPropagation();
|
|
164
189
|
|
|
165
190
|
// collapse menu
|
|
@@ -168,10 +193,11 @@ const SideNavMenu = /*#__PURE__*/React.forwardRef(function SideNavMenu(_ref, ref
|
|
|
168
193
|
if (onMenuToggle) {
|
|
169
194
|
onMenuToggle();
|
|
170
195
|
}
|
|
171
|
-
|
|
172
|
-
|
|
196
|
+
if (!primary && isExpanded) {
|
|
197
|
+
setIsExpanded(false);
|
|
198
|
+
}
|
|
173
199
|
// go to previous level's side nav menu button
|
|
174
|
-
} else {
|
|
200
|
+
} else if (!isSm) {
|
|
175
201
|
// since we're in a menu, it finds its own <li>, we go up one more
|
|
176
202
|
const previousMenu = parentSideNavMenu(parent);
|
|
177
203
|
const button = previousMenu.querySelector('button');
|
|
@@ -181,33 +207,74 @@ const SideNavMenu = /*#__PURE__*/React.forwardRef(function SideNavMenu(_ref, ref
|
|
|
181
207
|
|
|
182
208
|
// go to side nav menu button
|
|
183
209
|
} else if (parent) {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
210
|
+
if (parent.hasAttribute('aria-expanded')) {
|
|
211
|
+
const button = parent.querySelector('button');
|
|
212
|
+
if (button) {
|
|
213
|
+
button.tabIndex = 0;
|
|
214
|
+
button.focus();
|
|
215
|
+
}
|
|
216
|
+
} else if (!isSm) {
|
|
217
|
+
const previousMenu = parentSideNavMenu(parent);
|
|
218
|
+
const button = previousMenu.querySelector('button');
|
|
219
|
+
button.tabIndex = 0;
|
|
220
|
+
button?.focus();
|
|
221
|
+
}
|
|
187
222
|
}
|
|
188
223
|
}
|
|
189
224
|
if (match(event, ArrowRight)) {
|
|
225
|
+
setIsExpanded(true);
|
|
226
|
+
if (primary && node.hasAttribute('aria-expanded')) {
|
|
227
|
+
event.preventDefault();
|
|
228
|
+
}
|
|
190
229
|
event.stopPropagation();
|
|
191
230
|
|
|
192
231
|
// expand menu when sidenav is expanded
|
|
193
232
|
if (isMenu && sideNavExpanded) {
|
|
194
|
-
setIsExpanded(true);
|
|
195
233
|
if (onMenuToggle) {
|
|
196
234
|
onMenuToggle();
|
|
197
235
|
}
|
|
198
236
|
|
|
199
237
|
// if already expanded, focus on first element
|
|
200
|
-
if (isExpanded == 'true') {
|
|
201
|
-
|
|
238
|
+
if (isExpanded == 'true' || isSm) {
|
|
239
|
+
const nextNode = node.nextElementSibling?.querySelector('a, button');
|
|
202
240
|
if (nextNode) {
|
|
203
241
|
nextNode.tabIndex = 0;
|
|
204
242
|
nextNode.focus();
|
|
205
243
|
}
|
|
244
|
+
if (isSm) {
|
|
245
|
+
const nextNodeAfterBackButton = nextNode.nextElementSibling?.querySelector('a, button');
|
|
246
|
+
if (nextNodeAfterBackButton) {
|
|
247
|
+
nextNodeAfterBackButton.tabIndex = 0;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
206
250
|
}
|
|
207
251
|
}
|
|
208
252
|
}
|
|
209
253
|
}
|
|
210
254
|
}
|
|
255
|
+
function handleOnBackButtonClick(event) {
|
|
256
|
+
const node = event.target;
|
|
257
|
+
const parent = parentSideNavMenu(node);
|
|
258
|
+
const button = parent.querySelector('button');
|
|
259
|
+
if (button) {
|
|
260
|
+
button.tabIndex = 0;
|
|
261
|
+
button.focus();
|
|
262
|
+
}
|
|
263
|
+
setIsExpanded(false);
|
|
264
|
+
}
|
|
265
|
+
useEffect(() => {
|
|
266
|
+
if (isExpanded && primary && setCurrentPrimaryMenu) {
|
|
267
|
+
setCurrentPrimaryMenu(uniqueId);
|
|
268
|
+
}
|
|
269
|
+
setSecondaryOpen(isExpanded);
|
|
270
|
+
}, [isExpanded]);
|
|
271
|
+
useEffect(() => {
|
|
272
|
+
if (currentPrimaryMenu !== uniqueId) {
|
|
273
|
+
setIsExpanded(false);
|
|
274
|
+
} else {
|
|
275
|
+
setIsExpanded(true);
|
|
276
|
+
}
|
|
277
|
+
}, [currentPrimaryMenu]);
|
|
211
278
|
|
|
212
279
|
// save expanded state before SideNav collapse
|
|
213
280
|
const [lastExpandedState, setLastExpandedState] = useState(isExpanded);
|
|
@@ -222,6 +289,14 @@ const SideNavMenu = /*#__PURE__*/React.forwardRef(function SideNavMenu(_ref, ref
|
|
|
222
289
|
}
|
|
223
290
|
}, [sideNavExpanded]);
|
|
224
291
|
const [openPopover, setOpenPopover] = React.useState(false);
|
|
292
|
+
const isSm = useMatchMedia(smMediaQuery);
|
|
293
|
+
|
|
294
|
+
// keeps the secondary open when moving from small to large breakpoints
|
|
295
|
+
useEffect(() => {
|
|
296
|
+
if (!isSm && uniqueId === currentPrimaryMenu) {
|
|
297
|
+
setIsExpanded(true);
|
|
298
|
+
}
|
|
299
|
+
}, [isSm]);
|
|
225
300
|
const content =
|
|
226
301
|
/*#__PURE__*/
|
|
227
302
|
// eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
|
|
@@ -230,7 +305,8 @@ const SideNavMenu = /*#__PURE__*/React.forwardRef(function SideNavMenu(_ref, ref
|
|
|
230
305
|
"aria-expanded": isExpanded,
|
|
231
306
|
className: className,
|
|
232
307
|
ref: listRef,
|
|
233
|
-
onKeyDown: handleKeyDown
|
|
308
|
+
onKeyDown: handleKeyDown,
|
|
309
|
+
id: uniqueId
|
|
234
310
|
}, /*#__PURE__*/React.createElement("button", {
|
|
235
311
|
"aria-expanded": isExpanded,
|
|
236
312
|
className: buttonClassName,
|
|
@@ -243,10 +319,13 @@ const SideNavMenu = /*#__PURE__*/React.forwardRef(function SideNavMenu(_ref, ref
|
|
|
243
319
|
if (navType == SIDE_NAV_TYPE.PANEL && !isExpanded && firstLink.current && !sideNavExpanded) {
|
|
244
320
|
setOpenPopover(!openPopover);
|
|
245
321
|
// window.location.href = firstLink.current;
|
|
246
|
-
} else {
|
|
322
|
+
} else if (isSm || !primary || currentPrimaryMenu !== uniqueId) {
|
|
247
323
|
setIsExpanded(!isExpanded);
|
|
248
324
|
setLastExpandedState(!isExpanded);
|
|
249
325
|
}
|
|
326
|
+
if (isSm && backButtonRef.current) {
|
|
327
|
+
backButtonRef.current.focus();
|
|
328
|
+
}
|
|
250
329
|
},
|
|
251
330
|
ref: menuRef,
|
|
252
331
|
type: "button",
|
|
@@ -260,9 +339,24 @@ const SideNavMenu = /*#__PURE__*/React.forwardRef(function SideNavMenu(_ref, ref
|
|
|
260
339
|
}, title), /*#__PURE__*/React.createElement(SideNavIcon, {
|
|
261
340
|
className: `${prefix}--side-nav__submenu-chevron`,
|
|
262
341
|
small: true
|
|
263
|
-
},
|
|
342
|
+
}, primary ? _ChevronRight || (_ChevronRight = /*#__PURE__*/React.createElement(ChevronRight, {
|
|
264
343
|
size: 20
|
|
265
|
-
}))
|
|
344
|
+
})) : _ChevronDown || (_ChevronDown = /*#__PURE__*/React.createElement(ChevronDown, {
|
|
345
|
+
size: 20
|
|
346
|
+
})))), primary && /*#__PURE__*/React.createElement("div", {
|
|
347
|
+
className: primaryClassNames
|
|
348
|
+
}, /*#__PURE__*/React.createElement(SideNavItems, {
|
|
349
|
+
accessibilityLabel: {
|
|
350
|
+
'aria-label': `${title} submenu`
|
|
351
|
+
}
|
|
352
|
+
}, isSm && /*#__PURE__*/React.createElement(Button, {
|
|
353
|
+
ref: backButtonRef,
|
|
354
|
+
kind: "ghost",
|
|
355
|
+
size: "md",
|
|
356
|
+
onClick: handleOnBackButtonClick,
|
|
357
|
+
className: `${prefix}--side-nav__back-button`,
|
|
358
|
+
renderIcon: backButtonRenderIcon
|
|
359
|
+
}, backButtonTitle), childrenToRender)), /*#__PURE__*/React.createElement("ul", {
|
|
266
360
|
className: `${prefix}--side-nav__menu`,
|
|
267
361
|
role: "group"
|
|
268
362
|
}, childrenToRender));
|
|
@@ -275,6 +369,15 @@ const SideNavMenu = /*#__PURE__*/React.forwardRef(function SideNavMenu(_ref, ref
|
|
|
275
369
|
});
|
|
276
370
|
SideNavMenu.displayName = 'SideNavMenu';
|
|
277
371
|
SideNavMenu.propTypes = {
|
|
372
|
+
/**
|
|
373
|
+
* A custom icon to render on the back button in sm screen
|
|
374
|
+
*/
|
|
375
|
+
// @ts-expect-error - PropTypes are unable to cover this case.
|
|
376
|
+
backButtonRenderIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
|
|
377
|
+
/**
|
|
378
|
+
* Title for back button in sm screen
|
|
379
|
+
*/
|
|
380
|
+
backButtonTitle: PropTypes.string,
|
|
278
381
|
/**
|
|
279
382
|
* Provide <SideNavMenuItem>'s inside of the `SideNavMenu`
|
|
280
383
|
*/
|
|
@@ -293,6 +396,10 @@ SideNavMenu.propTypes = {
|
|
|
293
396
|
* SideNavMenu depth to determine spacing
|
|
294
397
|
*/
|
|
295
398
|
depth: PropTypes.number,
|
|
399
|
+
/**
|
|
400
|
+
* Provide a unique id
|
|
401
|
+
*/
|
|
402
|
+
id: PropTypes.string,
|
|
296
403
|
/**
|
|
297
404
|
* Specify whether the `SideNavMenu` is "active". `SideNavMenu` should be
|
|
298
405
|
* considered active if one of its menu items are a link for the current
|