@carbon-labs/react-ui-shell 0.7.0 → 0.8.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.
@@ -6,14 +6,14 @@
6
6
  */
7
7
 
8
8
  import { extends as _extends } from '../_virtual/_rollupPluginBabelHelpers.js';
9
- import React, { useRef, isValidElement, createContext } from 'react';
9
+ import React, { useRef, isValidElement, useEffect, createContext } from 'react';
10
10
  import cx from '../_virtual/index.js';
11
11
  import PropTypes from 'prop-types';
12
12
  import { AriaLabelPropType } from '@carbon/react/lib/prop-types/AriaPropTypes';
13
13
  import { CARBON_SIDENAV_ITEMS } from './_utils.js';
14
14
  import { usePrefix } from '@carbon/react/lib/internal/usePrefix';
15
15
  import * as keys from '@carbon/react/lib/internal/keyboard/keys';
16
- import { match } from '@carbon/react/lib/internal/keyboard/match';
16
+ import { match, matches } from '@carbon/react/lib/internal/keyboard/match';
17
17
  import { useMergedRefs } from '@carbon/react/lib/internal/useMergedRefs';
18
18
  import { useWindowEvent } from '@carbon/react/lib/internal/useEvent';
19
19
  import { useDelayedState } from '@carbon/react/lib/internal/useDelayedState';
@@ -121,6 +121,39 @@ function SideNavRenderFunction(_ref, ref) {
121
121
  return child;
122
122
  });
123
123
  const eventHandlers = {};
124
+ const treeWalkerRef = useRef(null);
125
+ useEffect(() => {
126
+ treeWalkerRef.current = treeWalkerRef.current ?? document.createTreeWalker(sideNavRef?.current, NodeFilter.SHOW_ELEMENT, {
127
+ acceptNode: function (node) {
128
+ if (!(node instanceof Element)) {
129
+ return NodeFilter.FILTER_SKIP;
130
+ }
131
+ if (node.classList.contains(`${prefix}--side-nav__divider`)) {
132
+ return NodeFilter.FILTER_REJECT;
133
+ }
134
+ if (node.matches(`li.${prefix}--side-nav__item`) || node.matches(`li.${prefix}--side-nav__menu-item`)) {
135
+ return NodeFilter.FILTER_ACCEPT;
136
+ }
137
+ return NodeFilter.FILTER_SKIP;
138
+ }
139
+ });
140
+ resetNodeTabIndices();
141
+ const firstElement = sideNavRef?.current?.querySelector('a, button');
142
+ if (firstElement) {
143
+ firstElement.tabIndex = 0;
144
+ }
145
+ }, [prefix]);
146
+
147
+ /**
148
+ * Returns the parent SideNavMenu, if node is actually inside one.
149
+ * @param node
150
+ * @returns parent side nav menu node
151
+ */
152
+ function parentSideNavMenu(node) {
153
+ const parentNode = node.parentElement?.closest(`.${prefix}--side-nav__item`);
154
+ if (parentNode) return parentNode;
155
+ return node;
156
+ }
124
157
  if (addFocusListeners) {
125
158
  eventHandlers.onFocus = event => {
126
159
  if (!event.currentTarget.contains(event.relatedTarget) && isRail) {
@@ -141,7 +174,94 @@ function SideNavRenderFunction(_ref, ref) {
141
174
  }
142
175
  };
143
176
  eventHandlers.onKeyDown = event => {
177
+ if (!treeWalkerRef.current) return;
178
+ const treeWalker = treeWalkerRef.current;
179
+ event.stopPropagation();
180
+
181
+ // stops page from scrolling
182
+ if (matches(event, [keys.ArrowUp, keys.ArrowDown, keys.Home, keys.End,
183
+ // @ts-ignore - `matches` doesn't like the object syntax without missing properties
184
+ {
185
+ code: 'KeyA'
186
+ }])) {
187
+ event.preventDefault();
188
+ }
189
+ treeWalker.currentNode = event.target.closest(`li`) ?? treeWalker?.currentNode;
190
+ let nextFocusNode = null;
191
+ if (match(event, keys.ArrowUp)) {
192
+ const parentNode = parentSideNavMenu(treeWalker.currentNode);
193
+ let previousSideNavMenu = parentNode?.previousElementSibling;
194
+
195
+ // skip the divider
196
+ if (previousSideNavMenu?.classList.contains(`${prefix}--side-nav__divider`)) {
197
+ previousSideNavMenu = previousSideNavMenu?.previousElementSibling;
198
+ }
199
+
200
+ // when previous sibling is open, go to its last item
201
+ if (previousSideNavMenu?.getAttribute('aria-expanded') == 'true') {
202
+ nextFocusNode = treeWalker.previousNode();
203
+ } else {
204
+ nextFocusNode = treeWalker.previousSibling();
205
+
206
+ // first item in the menu, go back up to SideNavMenu button
207
+ if (nextFocusNode == null) {
208
+ nextFocusNode = parentNode;
209
+ }
210
+ }
211
+ }
212
+ if (match(event, keys.ArrowDown)) {
213
+ if (treeWalker.currentNode.getAttribute('aria-expanded') == 'false') {
214
+ nextFocusNode = treeWalker.nextSibling();
215
+ } else {
216
+ nextFocusNode = treeWalker.nextNode();
217
+ }
218
+ }
219
+
220
+ // Home/End functionality
221
+ if (matches(event, [keys.Home, keys.End])) {
222
+ if (!sideNavRef?.current) {
223
+ return;
224
+ }
225
+ const allItems = Array.from(sideNavRef.current.querySelectorAll('a, button'));
226
+ if (match(event, keys.Home)) {
227
+ const firstElement = allItems[0];
228
+ if (firstElement) {
229
+ firstElement.tabIndex = 0;
230
+ firstElement?.focus();
231
+ }
232
+ }
233
+ if (match(event, keys.End)) {
234
+ const allItems = Array.from(sideNavRef.current.querySelectorAll('li'));
235
+ const lastVisibleItem = allItems.reverse().find(item => getComputedStyle(item).visibility !== 'hidden');
236
+ if (lastVisibleItem) {
237
+ const node = lastVisibleItem.querySelector('button') ?? lastVisibleItem.querySelector('a');
238
+ if (node) {
239
+ node.tabIndex = 0;
240
+ node?.focus();
241
+ }
242
+ }
243
+ }
244
+ }
245
+
246
+ // focus on the focusable element within the node
247
+ if (nextFocusNode && nextFocusNode !== event.target) {
248
+ resetNodeTabIndices();
249
+ if (nextFocusNode instanceof HTMLElement) {
250
+ const node = nextFocusNode.querySelector('button') ?? nextFocusNode.querySelector('a');
251
+ if (node) {
252
+ node.tabIndex = 0;
253
+ node?.focus();
254
+ }
255
+ }
256
+ }
257
+
258
+ // close menu
144
259
  if (match(event, keys.Escape)) {
260
+ if (expanded && !isFixedNav) {
261
+ if (onSideNavBlur) {
262
+ onSideNavBlur();
263
+ }
264
+ }
145
265
  handleToggle(event, false);
146
266
  if (href) {
147
267
  window.location.href = href;
@@ -167,12 +287,19 @@ function SideNavRenderFunction(_ref, ref) {
167
287
  }
168
288
  useWindowEvent('keydown', event => {
169
289
  const focusedElement = document.activeElement;
170
- if (match(event, keys.Tab) && expanded && !isFixedNav && sideNavRef.current && focusedElement?.classList.contains(`${prefix}--header__menu-toggle`) && !focusedElement.closest('nav')) {
290
+
291
+ // going from header menu to sideNav
292
+ if (match(event, keys.Tab) && expanded && !isFixedNav && sideNavRef?.current && focusedElement?.classList.contains(`${prefix}--header__menu-toggle`) && !focusedElement.closest('nav')) {
171
293
  sideNavRef.current.focus();
172
294
  }
173
295
  });
174
296
  const lgMediaQuery = `(min-width: ${breakpoints.lg.width})`;
175
297
  const isLg = useMatchMedia(lgMediaQuery);
298
+ function resetNodeTabIndices() {
299
+ Array.prototype.forEach.call(sideNavRef?.current?.querySelectorAll('[tabIndex="0"]') ?? [], item => {
300
+ item.tabIndex = -1;
301
+ });
302
+ }
176
303
  return /*#__PURE__*/React.createElement(SideNavContext.Provider, {
177
304
  value: {
178
305
  isRail
@@ -184,6 +311,7 @@ function SideNavRenderFunction(_ref, ref) {
184
311
  className: overlayClassName,
185
312
  onClick: onOverlayClick
186
313
  }), /*#__PURE__*/React.createElement("nav", _extends({
314
+ role: "tree",
187
315
  tabIndex: -1,
188
316
  ref: navRef,
189
317
  className: `${prefix}--side-nav__navigation ${className}`,
@@ -0,0 +1,24 @@
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 React from 'react';
8
+ export interface SideNavItemsProps {
9
+ /**
10
+ * Provide a single icon as the child to `SideNavIcon` to render in the
11
+ * container
12
+ */
13
+ children: React.ReactNode;
14
+ /**
15
+ * Provide an optional class to be applied to the containing node
16
+ */
17
+ className?: string;
18
+ /**
19
+ * Property to indicate if the side nav container is open (or not). Use to
20
+ * keep local state and styling in step with the SideNav expansion state.
21
+ */
22
+ isSideNavExpanded?: boolean;
23
+ }
24
+ export declare const SideNavItems: React.FC<SideNavItemsProps>;
@@ -0,0 +1,49 @@
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 React from 'react';
8
+ export interface SideNavMenuProps {
9
+ /**
10
+ * An optional CSS class to apply to the component.
11
+ */
12
+ className?: string;
13
+ /**
14
+ * The content to render within the SideNavMenu component.
15
+ */
16
+ children?: React.ReactNode;
17
+ /**
18
+ * Specifies whether the menu should be expanded by default.
19
+ */
20
+ defaultExpanded?: boolean;
21
+ /**
22
+ * **Note:** this is controlled by the parent SideNav component, do not set manually.
23
+ * SideNavMenu depth to determine spacing
24
+ */
25
+ depth?: number;
26
+ /**
27
+ * Indicates whether the SideNavMenu is active.
28
+ */
29
+ isActive?: boolean;
30
+ /**
31
+ * Specifies whether the SideNavMenu is in a large variation.
32
+ */
33
+ large?: boolean;
34
+ /**
35
+ * A custom icon to render next to the SideNavMenu title. This can be a function returning JSX or JSX itself.
36
+ */
37
+ renderIcon?: React.ComponentType;
38
+ /**
39
+ * Indicates if the side navigation container is expanded or collapsed.
40
+ */
41
+ isSideNavExpanded?: boolean;
42
+ /**
43
+ * The tabIndex for the button element.
44
+ * If not specified, the default validation will be applied.
45
+ */
46
+ tabIndex?: number;
47
+ title: string;
48
+ }
49
+ export declare const SideNavMenu: React.ForwardRefExoticComponent<SideNavMenuProps & React.RefAttributes<HTMLElement>>;
@@ -0,0 +1,38 @@
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 React, { ElementType, ComponentProps } from 'react';
8
+ import Link from '@carbon/react/lib/components/UIShell/Link';
9
+ export interface SideNavMenuItemProps extends ComponentProps<typeof Link> {
10
+ /**
11
+ * Specify the children to be rendered inside of the `SideNavMenuItem`
12
+ */
13
+ children?: React.ReactNode;
14
+ /**
15
+ * Provide an optional class to be applied to the containing node
16
+ */
17
+ className?: string;
18
+ /**
19
+ * **Note:** this is controlled by the parent SideNavMenu component, do not set manually.
20
+ * SideNavMenu depth to determine spacing
21
+ */
22
+ depth?: number;
23
+ /**
24
+ * Optionally specify whether the link is "active". An active link is one that
25
+ * has an href that is the same as the current page. Can also pass in
26
+ * `aria-current="page"`, as well.
27
+ */
28
+ isActive?: boolean;
29
+ /**
30
+ * Optionally provide an href for the underlying li`
31
+ */
32
+ href?: string;
33
+ /**
34
+ * Optional component to render instead of default Link
35
+ */
36
+ as?: ElementType;
37
+ }
38
+ export declare const SideNavMenuItem: React.ForwardRefExoticComponent<Omit<SideNavMenuItemProps, "ref"> & React.RefAttributes<HTMLElement>>;
@@ -142,6 +142,39 @@ function SideNavRenderFunction(_ref, ref) {
142
142
  return child;
143
143
  });
144
144
  const eventHandlers = {};
145
+ const treeWalkerRef = React.useRef(null);
146
+ React.useEffect(() => {
147
+ treeWalkerRef.current = treeWalkerRef.current ?? document.createTreeWalker(sideNavRef?.current, NodeFilter.SHOW_ELEMENT, {
148
+ acceptNode: function (node) {
149
+ if (!(node instanceof Element)) {
150
+ return NodeFilter.FILTER_SKIP;
151
+ }
152
+ if (node.classList.contains(`${prefix}--side-nav__divider`)) {
153
+ return NodeFilter.FILTER_REJECT;
154
+ }
155
+ if (node.matches(`li.${prefix}--side-nav__item`) || node.matches(`li.${prefix}--side-nav__menu-item`)) {
156
+ return NodeFilter.FILTER_ACCEPT;
157
+ }
158
+ return NodeFilter.FILTER_SKIP;
159
+ }
160
+ });
161
+ resetNodeTabIndices();
162
+ const firstElement = sideNavRef?.current?.querySelector('a, button');
163
+ if (firstElement) {
164
+ firstElement.tabIndex = 0;
165
+ }
166
+ }, [prefix]);
167
+
168
+ /**
169
+ * Returns the parent SideNavMenu, if node is actually inside one.
170
+ * @param node
171
+ * @returns parent side nav menu node
172
+ */
173
+ function parentSideNavMenu(node) {
174
+ const parentNode = node.parentElement?.closest(`.${prefix}--side-nav__item`);
175
+ if (parentNode) return parentNode;
176
+ return node;
177
+ }
145
178
  if (addFocusListeners) {
146
179
  eventHandlers.onFocus = event => {
147
180
  if (!event.currentTarget.contains(event.relatedTarget) && isRail) {
@@ -162,7 +195,94 @@ function SideNavRenderFunction(_ref, ref) {
162
195
  }
163
196
  };
164
197
  eventHandlers.onKeyDown = event => {
198
+ if (!treeWalkerRef.current) return;
199
+ const treeWalker = treeWalkerRef.current;
200
+ event.stopPropagation();
201
+
202
+ // stops page from scrolling
203
+ if (match.matches(event, [keys__namespace.ArrowUp, keys__namespace.ArrowDown, keys__namespace.Home, keys__namespace.End,
204
+ // @ts-ignore - `matches` doesn't like the object syntax without missing properties
205
+ {
206
+ code: 'KeyA'
207
+ }])) {
208
+ event.preventDefault();
209
+ }
210
+ treeWalker.currentNode = event.target.closest(`li`) ?? treeWalker?.currentNode;
211
+ let nextFocusNode = null;
212
+ if (match.match(event, keys__namespace.ArrowUp)) {
213
+ const parentNode = parentSideNavMenu(treeWalker.currentNode);
214
+ let previousSideNavMenu = parentNode?.previousElementSibling;
215
+
216
+ // skip the divider
217
+ if (previousSideNavMenu?.classList.contains(`${prefix}--side-nav__divider`)) {
218
+ previousSideNavMenu = previousSideNavMenu?.previousElementSibling;
219
+ }
220
+
221
+ // when previous sibling is open, go to its last item
222
+ if (previousSideNavMenu?.getAttribute('aria-expanded') == 'true') {
223
+ nextFocusNode = treeWalker.previousNode();
224
+ } else {
225
+ nextFocusNode = treeWalker.previousSibling();
226
+
227
+ // first item in the menu, go back up to SideNavMenu button
228
+ if (nextFocusNode == null) {
229
+ nextFocusNode = parentNode;
230
+ }
231
+ }
232
+ }
233
+ if (match.match(event, keys__namespace.ArrowDown)) {
234
+ if (treeWalker.currentNode.getAttribute('aria-expanded') == 'false') {
235
+ nextFocusNode = treeWalker.nextSibling();
236
+ } else {
237
+ nextFocusNode = treeWalker.nextNode();
238
+ }
239
+ }
240
+
241
+ // Home/End functionality
242
+ if (match.matches(event, [keys__namespace.Home, keys__namespace.End])) {
243
+ if (!sideNavRef?.current) {
244
+ return;
245
+ }
246
+ const allItems = Array.from(sideNavRef.current.querySelectorAll('a, button'));
247
+ if (match.match(event, keys__namespace.Home)) {
248
+ const firstElement = allItems[0];
249
+ if (firstElement) {
250
+ firstElement.tabIndex = 0;
251
+ firstElement?.focus();
252
+ }
253
+ }
254
+ if (match.match(event, keys__namespace.End)) {
255
+ const allItems = Array.from(sideNavRef.current.querySelectorAll('li'));
256
+ const lastVisibleItem = allItems.reverse().find(item => getComputedStyle(item).visibility !== 'hidden');
257
+ if (lastVisibleItem) {
258
+ const node = lastVisibleItem.querySelector('button') ?? lastVisibleItem.querySelector('a');
259
+ if (node) {
260
+ node.tabIndex = 0;
261
+ node?.focus();
262
+ }
263
+ }
264
+ }
265
+ }
266
+
267
+ // focus on the focusable element within the node
268
+ if (nextFocusNode && nextFocusNode !== event.target) {
269
+ resetNodeTabIndices();
270
+ if (nextFocusNode instanceof HTMLElement) {
271
+ const node = nextFocusNode.querySelector('button') ?? nextFocusNode.querySelector('a');
272
+ if (node) {
273
+ node.tabIndex = 0;
274
+ node?.focus();
275
+ }
276
+ }
277
+ }
278
+
279
+ // close menu
165
280
  if (match.match(event, keys__namespace.Escape)) {
281
+ if (expanded && !isFixedNav) {
282
+ if (onSideNavBlur) {
283
+ onSideNavBlur();
284
+ }
285
+ }
166
286
  handleToggle(event, false);
167
287
  if (href) {
168
288
  window.location.href = href;
@@ -188,12 +308,19 @@ function SideNavRenderFunction(_ref, ref) {
188
308
  }
189
309
  useEvent.useWindowEvent('keydown', event => {
190
310
  const focusedElement = document.activeElement;
191
- if (match.match(event, keys__namespace.Tab) && expanded && !isFixedNav && sideNavRef.current && focusedElement?.classList.contains(`${prefix}--header__menu-toggle`) && !focusedElement.closest('nav')) {
311
+
312
+ // going from header menu to sideNav
313
+ if (match.match(event, keys__namespace.Tab) && expanded && !isFixedNav && sideNavRef?.current && focusedElement?.classList.contains(`${prefix}--header__menu-toggle`) && !focusedElement.closest('nav')) {
192
314
  sideNavRef.current.focus();
193
315
  }
194
316
  });
195
317
  const lgMediaQuery = `(min-width: ${index$1.breakpoints.lg.width})`;
196
318
  const isLg = useMatchMedia.useMatchMedia(lgMediaQuery);
319
+ function resetNodeTabIndices() {
320
+ Array.prototype.forEach.call(sideNavRef?.current?.querySelectorAll('[tabIndex="0"]') ?? [], item => {
321
+ item.tabIndex = -1;
322
+ });
323
+ }
197
324
  return /*#__PURE__*/React.createElement(SideNavContext.Provider, {
198
325
  value: {
199
326
  isRail
@@ -205,6 +332,7 @@ function SideNavRenderFunction(_ref, ref) {
205
332
  className: overlayClassName,
206
333
  onClick: onOverlayClick
207
334
  }), /*#__PURE__*/React.createElement("nav", _rollupPluginBabelHelpers.extends({
335
+ role: "tree",
208
336
  tabIndex: -1,
209
337
  ref: navRef,
210
338
  className: `${prefix}--side-nav__navigation ${className}`,
@@ -0,0 +1,24 @@
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 React from 'react';
8
+ export interface SideNavItemsProps {
9
+ /**
10
+ * Provide a single icon as the child to `SideNavIcon` to render in the
11
+ * container
12
+ */
13
+ children: React.ReactNode;
14
+ /**
15
+ * Provide an optional class to be applied to the containing node
16
+ */
17
+ className?: string;
18
+ /**
19
+ * Property to indicate if the side nav container is open (or not). Use to
20
+ * keep local state and styling in step with the SideNav expansion state.
21
+ */
22
+ isSideNavExpanded?: boolean;
23
+ }
24
+ export declare const SideNavItems: React.FC<SideNavItemsProps>;
@@ -0,0 +1,49 @@
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 React from 'react';
8
+ export interface SideNavMenuProps {
9
+ /**
10
+ * An optional CSS class to apply to the component.
11
+ */
12
+ className?: string;
13
+ /**
14
+ * The content to render within the SideNavMenu component.
15
+ */
16
+ children?: React.ReactNode;
17
+ /**
18
+ * Specifies whether the menu should be expanded by default.
19
+ */
20
+ defaultExpanded?: boolean;
21
+ /**
22
+ * **Note:** this is controlled by the parent SideNav component, do not set manually.
23
+ * SideNavMenu depth to determine spacing
24
+ */
25
+ depth?: number;
26
+ /**
27
+ * Indicates whether the SideNavMenu is active.
28
+ */
29
+ isActive?: boolean;
30
+ /**
31
+ * Specifies whether the SideNavMenu is in a large variation.
32
+ */
33
+ large?: boolean;
34
+ /**
35
+ * A custom icon to render next to the SideNavMenu title. This can be a function returning JSX or JSX itself.
36
+ */
37
+ renderIcon?: React.ComponentType;
38
+ /**
39
+ * Indicates if the side navigation container is expanded or collapsed.
40
+ */
41
+ isSideNavExpanded?: boolean;
42
+ /**
43
+ * The tabIndex for the button element.
44
+ * If not specified, the default validation will be applied.
45
+ */
46
+ tabIndex?: number;
47
+ title: string;
48
+ }
49
+ export declare const SideNavMenu: React.ForwardRefExoticComponent<SideNavMenuProps & React.RefAttributes<HTMLElement>>;
@@ -0,0 +1,38 @@
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 React, { ElementType, ComponentProps } from 'react';
8
+ import Link from '@carbon/react/lib/components/UIShell/Link';
9
+ export interface SideNavMenuItemProps extends ComponentProps<typeof Link> {
10
+ /**
11
+ * Specify the children to be rendered inside of the `SideNavMenuItem`
12
+ */
13
+ children?: React.ReactNode;
14
+ /**
15
+ * Provide an optional class to be applied to the containing node
16
+ */
17
+ className?: string;
18
+ /**
19
+ * **Note:** this is controlled by the parent SideNavMenu component, do not set manually.
20
+ * SideNavMenu depth to determine spacing
21
+ */
22
+ depth?: number;
23
+ /**
24
+ * Optionally specify whether the link is "active". An active link is one that
25
+ * has an href that is the same as the current page. Can also pass in
26
+ * `aria-current="page"`, as well.
27
+ */
28
+ isActive?: boolean;
29
+ /**
30
+ * Optionally provide an href for the underlying li`
31
+ */
32
+ href?: string;
33
+ /**
34
+ * Optional component to render instead of default Link
35
+ */
36
+ as?: ElementType;
37
+ }
38
+ export declare const SideNavMenuItem: React.ForwardRefExoticComponent<Omit<SideNavMenuItemProps, "ref"> & React.RefAttributes<HTMLElement>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carbon-labs/react-ui-shell",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "provenance": true
@@ -33,5 +33,5 @@
33
33
  "dependencies": {
34
34
  "@ibm/telemetry-js": "^1.9.1"
35
35
  },
36
- "gitHead": "964c234623e4ab60c3f08e1f4598955b926ecd7b"
36
+ "gitHead": "5b474b82f13a6c1fec121c8729d208082140b9f5"
37
37
  }
@@ -28,6 +28,24 @@ $prefix: 'cds' !default;
28
28
  inline-size: convert.to-rem(256px);
29
29
  }
30
30
 
31
+ //----------------------------------------------------------------------------
32
+ // Treeview Side-nav
33
+ //----------------------------------------------------------------------------
34
+ .#{$prefix}--side-nav__icon:not(.#{$prefix}--side-nav__submenu-chevron) {
35
+ margin-inline-end: $spacing-05;
36
+ }
37
+
38
+ .#{$prefix}--side-nav__submenu.#{$prefix}--side-nav__submenu--active {
39
+ > span {
40
+ color: $text-primary;
41
+ font-weight: 600;
42
+ }
43
+
44
+ .#{$prefix}--side-nav__submenu-chevron > svg {
45
+ fill: $icon-primary;
46
+ }
47
+ }
48
+
31
49
  //----------------------------------------------------------------------------
32
50
  // Side-nav Panel
33
51
  //----------------------------------------------------------------------------