@carbon/react 1.32.0-rc.0 → 1.32.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.
@@ -104,6 +104,10 @@ export interface TabListProps extends DivAttributes {
104
104
  * Specify whether component is contained type
105
105
  */
106
106
  contained?: boolean;
107
+ /**
108
+ * Used for tabs within a grid, this makes it so tabs span the full container width and have the same width. Only available on contained tabs with <9 children
109
+ */
110
+ fullWidth?: boolean;
107
111
  /**
108
112
  * If using `IconTab`, specify the size of the icon being used.
109
113
  */
@@ -132,7 +136,7 @@ export interface TabListProps extends DivAttributes {
132
136
  */
133
137
  scrollIntoView?: boolean;
134
138
  }
135
- declare function TabList({ activation, 'aria-label': label, children, className: customClassName, contained, iconSize, leftOverflowButtonProps, light, rightOverflowButtonProps, scrollDebounceWait, scrollIntoView, ...rest }: TabListProps): JSX.Element;
139
+ declare function TabList({ activation, 'aria-label': label, children, className: customClassName, contained, fullWidth, iconSize, leftOverflowButtonProps, light, rightOverflowButtonProps, scrollDebounceWait, scrollIntoView, ...rest }: TabListProps): JSX.Element;
136
140
  declare namespace TabList {
137
141
  var propTypes: {
138
142
  /**
@@ -158,6 +162,10 @@ declare namespace TabList {
158
162
  * Specify whether component is contained type
159
163
  */
160
164
  contained: PropTypes.Requireable<boolean>;
165
+ /**
166
+ * Used for tabs within a grid, this makes it so tabs span the full container width and have the same width. Only available on contained tabs with <9 children
167
+ */
168
+ fullWidth: PropTypes.Requireable<boolean>;
161
169
  /**
162
170
  * If using `IconTab`, specify the size of the icon being used.
163
171
  */
@@ -7,6 +7,7 @@
7
7
 
8
8
  import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js';
9
9
  import { ChevronLeft, ChevronRight, Close } from '@carbon/icons-react';
10
+ import { breakpoints } from '@carbon/layout';
10
11
  import cx from 'classnames';
11
12
  import debounce from 'lodash.debounce';
12
13
  import PropTypes from 'prop-types';
@@ -24,6 +25,7 @@ import { usePrefix } from '../../internal/usePrefix.js';
24
25
  import { usePressable } from './usePressable.js';
25
26
  import deprecate from '../../prop-types/deprecate.js';
26
27
  import { useEvent } from '../../internal/useEvent.js';
28
+ import { useMatchMedia } from '../../internal/useMatchMedia.js';
27
29
  import { matches, match } from '../../internal/keyboard/match.js';
28
30
  import { ArrowRight, ArrowLeft, Home, End, Delete } from '../../internal/keyboard/keys.js';
29
31
 
@@ -47,6 +49,7 @@ const TabContext = /*#__PURE__*/React__default.createContext({
47
49
  index: 0,
48
50
  hasSecondaryLabel: false
49
51
  });
52
+ const lgMediaQuery = `(min-width: ${breakpoints.lg.width})`;
50
53
 
51
54
  // Used to keep track of position in a list of tab panels
52
55
  const TabPanelContext = /*#__PURE__*/React__default.createContext(0);
@@ -156,6 +159,7 @@ function TabList(_ref2) {
156
159
  children,
157
160
  className: customClassName,
158
161
  contained = false,
162
+ fullWidth = false,
159
163
  iconSize,
160
164
  leftOverflowButtonProps,
161
165
  light,
@@ -183,12 +187,15 @@ function TabList(_ref2) {
183
187
  return isElement(child) && !!child.props.secondaryLabel;
184
188
  });
185
189
  }
190
+ const isLg = useMatchMedia(lgMediaQuery);
191
+ const distributeWidth = fullWidth && contained && isLg && React__default.Children.toArray(children).length < 9;
186
192
  const className = cx(`${prefix}--tabs`, {
187
193
  [`${prefix}--tabs--contained`]: contained,
188
194
  [`${prefix}--tabs--light`]: light,
189
195
  [`${prefix}--tabs__icon--default`]: iconSize === 'default',
190
196
  [`${prefix}--tabs__icon--lg`]: iconSize === 'lg',
191
- [`${prefix}--tabs--tall`]: hasSecondaryLabelTabs
197
+ [`${prefix}--tabs--tall`]: hasSecondaryLabelTabs,
198
+ [`${prefix}--tabs--full-width`]: distributeWidth
192
199
  }, customClassName);
193
200
 
194
201
  // Previous Button
@@ -396,6 +403,10 @@ TabList.propTypes = {
396
403
  * Specify whether component is contained type
397
404
  */
398
405
  contained: PropTypes.bool,
406
+ /**
407
+ * Used for tabs within a grid, this makes it so tabs span the full container width and have the same width. Only available on contained tabs with <9 children
408
+ */
409
+ fullWidth: PropTypes.bool,
399
410
  /**
400
411
  * If using `IconTab`, specify the size of the icon being used.
401
412
  */
@@ -574,7 +585,8 @@ const Tab = /*#__PURE__*/forwardRef(function Tab(_ref5, forwardRef) {
574
585
  }, /*#__PURE__*/React__default.createElement(Icon, {
575
586
  size: 16
576
587
  })), /*#__PURE__*/React__default.createElement("span", {
577
- className: `${prefix}--tabs__nav-item-label`
588
+ className: `${prefix}--tabs__nav-item-label`,
589
+ title: children
578
590
  }, children), /*#__PURE__*/React__default.createElement("div", {
579
591
  className: cx(`${prefix}--tabs__nav-item--icon`, {
580
592
  [`${prefix}--visually-hidden`]: !hasIcon
@@ -582,7 +594,8 @@ const Tab = /*#__PURE__*/forwardRef(function Tab(_ref5, forwardRef) {
582
594
  }, DismissIcon, !dismissable && Icon && /*#__PURE__*/React__default.createElement(Icon, {
583
595
  size: 16
584
596
  }))), hasSecondaryLabel && /*#__PURE__*/React__default.createElement("div", {
585
- className: `${prefix}--tabs__nav-item-secondary-label`
597
+ className: `${prefix}--tabs__nav-item-secondary-label`,
598
+ title: secondaryLabel
586
599
  }, secondaryLabel));
587
600
  });
588
601
  Tab.propTypes = {
@@ -6,29 +6,75 @@
6
6
  */
7
7
 
8
8
  import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js';
9
- import React__default from 'react';
9
+ import React__default, { useRef, useState } from 'react';
10
10
  import cx from 'classnames';
11
11
  import PropTypes from 'prop-types';
12
12
  import { usePrefix } from '../../internal/usePrefix.js';
13
+ import { useWindowEvent } from '../../internal/useEvent.js';
14
+ import { useMergedRefs } from '../../internal/useMergedRefs.js';
15
+ import { match } from '../../internal/keyboard/match.js';
16
+ import { Escape } from '../../internal/keyboard/keys.js';
13
17
 
14
18
  const HeaderPanel = /*#__PURE__*/React__default.forwardRef(function HeaderPanel(_ref, ref) {
15
19
  let {
16
20
  children,
17
21
  className: customClassName,
18
22
  expanded,
23
+ addFocusListeners = true,
24
+ onHeaderPanelFocus,
25
+ href,
19
26
  ...other
20
27
  } = _ref;
21
28
  const prefix = usePrefix();
29
+ const headerPanelReference = useRef(null);
30
+ const headerPanelRef = useMergedRefs([headerPanelReference, ref]);
31
+ const controlled = useRef(expanded !== undefined).current;
32
+ const [expandedState, setExpandedState] = useState(expanded);
33
+ const expandedProp = controlled ? expanded : expandedState;
34
+ const [lastClickedElement, setLastClickedElement] = useState(null);
22
35
  const className = cx(`${prefix}--header-panel`, {
23
- [`${prefix}--header-panel--expanded`]: expanded,
36
+ [`${prefix}--header-panel--expanded`]: expandedProp,
24
37
  [customClassName]: !!customClassName
25
38
  });
39
+ const eventHandlers = {};
40
+ if (addFocusListeners) {
41
+ eventHandlers.onBlur = event => {
42
+ if (!event.currentTarget.contains(event.relatedTarget) && !lastClickedElement.classList.contains('cds--switcher__item-link')) {
43
+ setExpandedState(false);
44
+ setLastClickedElement(null);
45
+ if (expanded) {
46
+ onHeaderPanelFocus();
47
+ }
48
+ }
49
+ };
50
+ eventHandlers.onKeyDown = event => {
51
+ if (match(event, Escape)) {
52
+ setExpandedState(false);
53
+ onHeaderPanelFocus();
54
+ if (href) {
55
+ window.location.href = href;
56
+ }
57
+ }
58
+ };
59
+ }
60
+ useWindowEvent('click', () => {
61
+ const focusedElement = document.activeElement;
62
+ setLastClickedElement(focusedElement);
63
+ if (children.type.__docgenInfo.displayName === 'Switcher' && !focusedElement?.closest(`.${prefix}--header-panel--expanded`) && !focusedElement?.closest(`.${prefix}--header__action`) && !headerPanelRef?.current?.classList.contains(`${prefix}--switcher`) && expanded) {
64
+ setExpandedState(false);
65
+ onHeaderPanelFocus();
66
+ }
67
+ });
26
68
  return /*#__PURE__*/React__default.createElement("div", _extends({}, other, {
27
69
  className: className,
28
- ref: ref
29
- }), children);
70
+ ref: headerPanelRef
71
+ }, eventHandlers), children);
30
72
  });
31
73
  HeaderPanel.propTypes = {
74
+ /**
75
+ * Specify whether focus and blur listeners are added. They are by default.
76
+ */
77
+ addFocusListeners: PropTypes.bool,
32
78
  /**
33
79
  * The content that will render inside of the `HeaderPanel`
34
80
  */
@@ -40,7 +86,17 @@ HeaderPanel.propTypes = {
40
86
  /**
41
87
  * Specify whether the panel is expanded
42
88
  */
43
- expanded: PropTypes.bool
89
+ expanded: PropTypes.bool,
90
+ /**
91
+ * Provide the `href` to the id of the element on your package that could
92
+ * be target.
93
+ */
94
+ href: PropTypes.string,
95
+ /**
96
+ * An optional listener that is called a callback to collapse the HeaderPanel
97
+ */
98
+
99
+ onHeaderPanelFocus: PropTypes.func
44
100
  };
45
101
  HeaderPanel.displayName = 'HeaderPanel';
46
102
  var HeaderPanel$1 = HeaderPanel;
@@ -20,5 +20,5 @@ interface SideNavProps extends ComponentProps<'nav'> {
20
20
  onSideNavBlur?: () => void | undefined;
21
21
  enterDelayMs?: number;
22
22
  }
23
- declare const SideNav: React.ForwardRefExoticComponent<Pick<SideNavProps, "children" | "slot" | "style" | "title" | "className" | "dir" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "onToggle" | "key" | "id" | "aria-controls" | "aria-expanded" | "onClick" | "onAnimationEnd" | "onKeyDown" | "contentEditable" | "contextMenu" | "draggable" | "hidden" | "lang" | "placeholder" | "spellCheck" | "tabIndex" | "translate" | "radioGroup" | "role" | "about" | "datatype" | "inlist" | "prefix" | "property" | "resource" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "color" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colspan" | "aria-current" | "aria-describedby" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDrag" | "onDragCapture" | "onDragEnd" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStart" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerEnterCapture" | "onPointerLeave" | "onPointerLeaveCapture" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStart" | "onAnimationStartCapture" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "href" | "expanded" | "enterDelayMs" | "defaultExpanded" | "isChildOfHeader" | "isFixedNav" | "isRail" | "isPersistent" | "addFocusListeners" | "addMouseListeners" | "onOverlayClick" | "onSideNavBlur"> & React.RefAttributes<HTMLElement>>;
23
+ declare const SideNav: React.ForwardRefExoticComponent<Pick<SideNavProps, "children" | "slot" | "style" | "title" | "className" | "dir" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "onToggle" | "key" | "id" | "aria-controls" | "aria-expanded" | "onClick" | "onAnimationEnd" | "onKeyDown" | "contentEditable" | "contextMenu" | "draggable" | "hidden" | "lang" | "placeholder" | "spellCheck" | "tabIndex" | "translate" | "radioGroup" | "role" | "about" | "datatype" | "inlist" | "prefix" | "property" | "resource" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "color" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colspan" | "aria-current" | "aria-describedby" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDrag" | "onDragCapture" | "onDragEnd" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStart" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerEnterCapture" | "onPointerLeave" | "onPointerLeaveCapture" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStart" | "onAnimationStartCapture" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "href" | "expanded" | "enterDelayMs" | "defaultExpanded" | "addFocusListeners" | "isChildOfHeader" | "isFixedNav" | "isRail" | "isPersistent" | "addMouseListeners" | "onOverlayClick" | "onSideNavBlur"> & React.RefAttributes<HTMLElement>>;
24
24
  export default SideNav;
@@ -6,19 +6,23 @@
6
6
  */
7
7
 
8
8
  import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js';
9
- import React__default from 'react';
9
+ import React__default, { useRef } from 'react';
10
10
  import cx from 'classnames';
11
11
  import PropTypes from 'prop-types';
12
12
  import { AriaLabelPropType } from '../../prop-types/AriaPropTypes.js';
13
13
  import { usePrefix } from '../../internal/usePrefix.js';
14
+ import { useMergedRefs } from '../../internal/useMergedRefs.js';
14
15
 
15
- const Switcher = /*#__PURE__*/React__default.forwardRef(function Switcher(props, ref) {
16
+ const Switcher = /*#__PURE__*/React__default.forwardRef(function Switcher(props, forwardRef) {
17
+ const switcherRef = useRef(null);
18
+ const ref = useMergedRefs([switcherRef, forwardRef]);
16
19
  const prefix = usePrefix();
17
20
  const {
18
21
  'aria-label': ariaLabel,
19
22
  'aria-labelledby': ariaLabelledBy,
20
23
  className: customClassName,
21
- children
24
+ children,
25
+ expanded
22
26
  } = props;
23
27
  const accessibilityLabel = {
24
28
  'aria-label': ariaLabel,
@@ -27,10 +31,42 @@ const Switcher = /*#__PURE__*/React__default.forwardRef(function Switcher(props,
27
31
  const className = cx(`${prefix}--switcher`, {
28
32
  [customClassName]: !!customClassName
29
33
  });
34
+ const handleSwitcherItemFocus = _ref => {
35
+ let {
36
+ currentIndex,
37
+ direction
38
+ } = _ref;
39
+ const enabledIndices = React__default.Children.toArray(children).reduce((acc, curr, i) => {
40
+ if (Object.keys(curr.props).length !== 0) {
41
+ acc.push(i);
42
+ }
43
+ return acc;
44
+ }, []);
45
+ const nextValidIndex = (() => {
46
+ const nextIndex = enabledIndices.indexOf(currentIndex) + direction;
47
+ switch (enabledIndices[nextIndex]) {
48
+ case undefined:
49
+ if (direction === -1) {
50
+ return enabledIndices[enabledIndices.length - 1];
51
+ }
52
+ return 0;
53
+ default:
54
+ return enabledIndices[nextIndex];
55
+ }
56
+ })();
57
+ const switcherItem = switcherRef.current.children[nextValidIndex].children[0];
58
+ switcherItem?.focus();
59
+ };
60
+ const childrenWithProps = React__default.Children.toArray(children).map((child, index) => /*#__PURE__*/React__default.cloneElement(child, {
61
+ handleSwitcherItemFocus,
62
+ index,
63
+ key: index,
64
+ expanded
65
+ }));
30
66
  return /*#__PURE__*/React__default.createElement("ul", _extends({
31
67
  ref: ref,
32
68
  className: className
33
- }, accessibilityLabel), children);
69
+ }, accessibilityLabel), childrenWithProps);
34
70
  });
35
71
  Switcher.displayName = 'Switcher';
36
72
  Switcher.propTypes = {
@@ -45,7 +81,11 @@ Switcher.propTypes = {
45
81
  /**
46
82
  * Optionally provide a custom class to apply to the underlying `<ul>` node
47
83
  */
48
- className: PropTypes.string
84
+ className: PropTypes.string,
85
+ /**
86
+ * Specify whether the panel is expanded
87
+ */
88
+ expanded: PropTypes.bool
49
89
  };
50
90
  var Switcher$1 = Switcher;
51
91
 
@@ -12,18 +12,24 @@ import PropTypes from 'prop-types';
12
12
  import { AriaLabelPropType } from '../../prop-types/AriaPropTypes.js';
13
13
  import Link from './Link.js';
14
14
  import { usePrefix } from '../../internal/usePrefix.js';
15
+ import { match } from '../../internal/keyboard/match.js';
16
+ import { ArrowDown, ArrowUp } from '../../internal/keyboard/keys.js';
15
17
 
16
- const SwitcherItem = /*#__PURE__*/React__default.forwardRef(function SwitcherItem(props, ref) {
17
- const prefix = usePrefix();
18
- const {
18
+ const SwitcherItem = /*#__PURE__*/React__default.forwardRef(function SwitcherItem(_ref, ref) {
19
+ let {
19
20
  'aria-label': ariaLabel,
20
21
  'aria-labelledby': ariaLabelledBy,
21
22
  className: customClassName,
22
23
  children,
23
24
  isSelected,
24
- tabIndex = 0,
25
+ expanded,
26
+ tabIndex = expanded ? 0 : -1,
27
+ index,
28
+ handleSwitcherItemFocus,
29
+ onKeyDown = () => {},
25
30
  ...rest
26
- } = props;
31
+ } = _ref;
32
+ const prefix = usePrefix();
27
33
  const className = cx(`${prefix}--switcher__item`, {
28
34
  [customClassName]: !!customClassName
29
35
  });
@@ -34,9 +40,30 @@ const SwitcherItem = /*#__PURE__*/React__default.forwardRef(function SwitcherIte
34
40
  const linkClassName = cx(`${prefix}--switcher__item-link`, {
35
41
  [`${prefix}--switcher__item-link--selected`]: isSelected
36
42
  });
43
+ function setTabFocus(evt) {
44
+ if (match(evt, ArrowDown)) {
45
+ evt.preventDefault();
46
+ handleSwitcherItemFocus?.({
47
+ currentIndex: index,
48
+ direction: 1
49
+ });
50
+ }
51
+ if (match(evt, ArrowUp)) {
52
+ evt.preventDefault();
53
+ handleSwitcherItemFocus?.({
54
+ currentIndex: index,
55
+ direction: -1
56
+ });
57
+ }
58
+ }
37
59
  return /*#__PURE__*/React__default.createElement("li", {
38
60
  className: className
39
- }, /*#__PURE__*/React__default.createElement(Link, _extends({}, rest, {
61
+ }, /*#__PURE__*/React__default.createElement(Link, _extends({
62
+ onKeyDown: evt => {
63
+ setTabFocus(evt);
64
+ onKeyDown(evt);
65
+ }
66
+ }, rest, {
40
67
  ref: ref,
41
68
  className: linkClassName,
42
69
  tabIndex: tabIndex
@@ -56,6 +83,18 @@ SwitcherItem.propTypes = {
56
83
  * Optionally provide a custom class to apply to the underlying `<li>` node
57
84
  */
58
85
  className: PropTypes.string,
86
+ /**
87
+ * event handlers
88
+ */
89
+ handleSwitcherItemFocus: PropTypes.func,
90
+ /**
91
+ * Specify the index of the SwitcherItem
92
+ */
93
+ index: PropTypes.number,
94
+ /**
95
+ * event handlers
96
+ */
97
+ onKeyDown: PropTypes.func,
59
98
  /**
60
99
  * Specify the tab index of the Link
61
100
  */
@@ -0,0 +1,44 @@
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
+ import { useState, useEffect } from 'react';
9
+ import { canUseDOM } from './environment.js';
10
+
11
+ function useMatchMedia(mediaQueryString) {
12
+ const [matches, setMatches] = useState(() => {
13
+ if (canUseDOM) {
14
+ const mediaQueryList = window.matchMedia(mediaQueryString);
15
+ return mediaQueryList.matches;
16
+ }
17
+ return false;
18
+ });
19
+ useEffect(() => {
20
+ function listener(event) {
21
+ setMatches(event.matches);
22
+ }
23
+ const mediaQueryList = window.matchMedia(mediaQueryString);
24
+ // Support fallback to `addListener` for broader browser support
25
+ if (mediaQueryList.addEventListener) {
26
+ mediaQueryList.addEventListener('change', listener);
27
+ } else {
28
+ mediaQueryList.addListener(listener);
29
+ }
30
+
31
+ // Make sure the media query list is in sync with the matches state
32
+ setMatches(mediaQueryList.matches);
33
+ return () => {
34
+ if (mediaQueryList.addEventListener) {
35
+ mediaQueryList.removeEventListener('change', listener);
36
+ } else {
37
+ mediaQueryList.removeListener(listener);
38
+ }
39
+ };
40
+ }, [mediaQueryString]);
41
+ return matches;
42
+ }
43
+
44
+ export { useMatchMedia };
@@ -104,6 +104,10 @@ export interface TabListProps extends DivAttributes {
104
104
  * Specify whether component is contained type
105
105
  */
106
106
  contained?: boolean;
107
+ /**
108
+ * Used for tabs within a grid, this makes it so tabs span the full container width and have the same width. Only available on contained tabs with <9 children
109
+ */
110
+ fullWidth?: boolean;
107
111
  /**
108
112
  * If using `IconTab`, specify the size of the icon being used.
109
113
  */
@@ -132,7 +136,7 @@ export interface TabListProps extends DivAttributes {
132
136
  */
133
137
  scrollIntoView?: boolean;
134
138
  }
135
- declare function TabList({ activation, 'aria-label': label, children, className: customClassName, contained, iconSize, leftOverflowButtonProps, light, rightOverflowButtonProps, scrollDebounceWait, scrollIntoView, ...rest }: TabListProps): JSX.Element;
139
+ declare function TabList({ activation, 'aria-label': label, children, className: customClassName, contained, fullWidth, iconSize, leftOverflowButtonProps, light, rightOverflowButtonProps, scrollDebounceWait, scrollIntoView, ...rest }: TabListProps): JSX.Element;
136
140
  declare namespace TabList {
137
141
  var propTypes: {
138
142
  /**
@@ -158,6 +162,10 @@ declare namespace TabList {
158
162
  * Specify whether component is contained type
159
163
  */
160
164
  contained: PropTypes.Requireable<boolean>;
165
+ /**
166
+ * Used for tabs within a grid, this makes it so tabs span the full container width and have the same width. Only available on contained tabs with <9 children
167
+ */
168
+ fullWidth: PropTypes.Requireable<boolean>;
161
169
  /**
162
170
  * If using `IconTab`, specify the size of the icon being used.
163
171
  */
@@ -11,6 +11,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
11
11
 
12
12
  var _rollupPluginBabelHelpers = require('../../_virtual/_rollupPluginBabelHelpers.js');
13
13
  var iconsReact = require('@carbon/icons-react');
14
+ var layout = require('@carbon/layout');
14
15
  var cx = require('classnames');
15
16
  var debounce = require('lodash.debounce');
16
17
  var PropTypes = require('prop-types');
@@ -28,6 +29,7 @@ var usePrefix = require('../../internal/usePrefix.js');
28
29
  var usePressable = require('./usePressable.js');
29
30
  var deprecate = require('../../prop-types/deprecate.js');
30
31
  var useEvent = require('../../internal/useEvent.js');
32
+ var useMatchMedia = require('../../internal/useMatchMedia.js');
31
33
  var match = require('../../internal/keyboard/match.js');
32
34
  var keys = require('../../internal/keyboard/keys.js');
33
35
 
@@ -58,6 +60,7 @@ const TabContext = /*#__PURE__*/React__default["default"].createContext({
58
60
  index: 0,
59
61
  hasSecondaryLabel: false
60
62
  });
63
+ const lgMediaQuery = `(min-width: ${layout.breakpoints.lg.width})`;
61
64
 
62
65
  // Used to keep track of position in a list of tab panels
63
66
  const TabPanelContext = /*#__PURE__*/React__default["default"].createContext(0);
@@ -167,6 +170,7 @@ function TabList(_ref2) {
167
170
  children,
168
171
  className: customClassName,
169
172
  contained = false,
173
+ fullWidth = false,
170
174
  iconSize,
171
175
  leftOverflowButtonProps,
172
176
  light,
@@ -194,12 +198,15 @@ function TabList(_ref2) {
194
198
  return reactIs.isElement(child) && !!child.props.secondaryLabel;
195
199
  });
196
200
  }
201
+ const isLg = useMatchMedia.useMatchMedia(lgMediaQuery);
202
+ const distributeWidth = fullWidth && contained && isLg && React__default["default"].Children.toArray(children).length < 9;
197
203
  const className = cx__default["default"](`${prefix}--tabs`, {
198
204
  [`${prefix}--tabs--contained`]: contained,
199
205
  [`${prefix}--tabs--light`]: light,
200
206
  [`${prefix}--tabs__icon--default`]: iconSize === 'default',
201
207
  [`${prefix}--tabs__icon--lg`]: iconSize === 'lg',
202
- [`${prefix}--tabs--tall`]: hasSecondaryLabelTabs
208
+ [`${prefix}--tabs--tall`]: hasSecondaryLabelTabs,
209
+ [`${prefix}--tabs--full-width`]: distributeWidth
203
210
  }, customClassName);
204
211
 
205
212
  // Previous Button
@@ -407,6 +414,10 @@ TabList.propTypes = {
407
414
  * Specify whether component is contained type
408
415
  */
409
416
  contained: PropTypes__default["default"].bool,
417
+ /**
418
+ * Used for tabs within a grid, this makes it so tabs span the full container width and have the same width. Only available on contained tabs with <9 children
419
+ */
420
+ fullWidth: PropTypes__default["default"].bool,
410
421
  /**
411
422
  * If using `IconTab`, specify the size of the icon being used.
412
423
  */
@@ -585,7 +596,8 @@ const Tab = /*#__PURE__*/React.forwardRef(function Tab(_ref5, forwardRef) {
585
596
  }, /*#__PURE__*/React__default["default"].createElement(Icon, {
586
597
  size: 16
587
598
  })), /*#__PURE__*/React__default["default"].createElement("span", {
588
- className: `${prefix}--tabs__nav-item-label`
599
+ className: `${prefix}--tabs__nav-item-label`,
600
+ title: children
589
601
  }, children), /*#__PURE__*/React__default["default"].createElement("div", {
590
602
  className: cx__default["default"](`${prefix}--tabs__nav-item--icon`, {
591
603
  [`${prefix}--visually-hidden`]: !hasIcon
@@ -593,7 +605,8 @@ const Tab = /*#__PURE__*/React.forwardRef(function Tab(_ref5, forwardRef) {
593
605
  }, DismissIcon, !dismissable && Icon && /*#__PURE__*/React__default["default"].createElement(Icon, {
594
606
  size: 16
595
607
  }))), hasSecondaryLabel && /*#__PURE__*/React__default["default"].createElement("div", {
596
- className: `${prefix}--tabs__nav-item-secondary-label`
608
+ className: `${prefix}--tabs__nav-item-secondary-label`,
609
+ title: secondaryLabel
597
610
  }, secondaryLabel));
598
611
  });
599
612
  Tab.propTypes = {
@@ -14,6 +14,10 @@ var React = require('react');
14
14
  var cx = require('classnames');
15
15
  var PropTypes = require('prop-types');
16
16
  var usePrefix = require('../../internal/usePrefix.js');
17
+ var useEvent = require('../../internal/useEvent.js');
18
+ var useMergedRefs = require('../../internal/useMergedRefs.js');
19
+ var match = require('../../internal/keyboard/match.js');
20
+ var keys = require('../../internal/keyboard/keys.js');
17
21
 
18
22
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
19
23
 
@@ -26,19 +30,61 @@ const HeaderPanel = /*#__PURE__*/React__default["default"].forwardRef(function H
26
30
  children,
27
31
  className: customClassName,
28
32
  expanded,
33
+ addFocusListeners = true,
34
+ onHeaderPanelFocus,
35
+ href,
29
36
  ...other
30
37
  } = _ref;
31
38
  const prefix = usePrefix.usePrefix();
39
+ const headerPanelReference = React.useRef(null);
40
+ const headerPanelRef = useMergedRefs.useMergedRefs([headerPanelReference, ref]);
41
+ const controlled = React.useRef(expanded !== undefined).current;
42
+ const [expandedState, setExpandedState] = React.useState(expanded);
43
+ const expandedProp = controlled ? expanded : expandedState;
44
+ const [lastClickedElement, setLastClickedElement] = React.useState(null);
32
45
  const className = cx__default["default"](`${prefix}--header-panel`, {
33
- [`${prefix}--header-panel--expanded`]: expanded,
46
+ [`${prefix}--header-panel--expanded`]: expandedProp,
34
47
  [customClassName]: !!customClassName
35
48
  });
49
+ const eventHandlers = {};
50
+ if (addFocusListeners) {
51
+ eventHandlers.onBlur = event => {
52
+ if (!event.currentTarget.contains(event.relatedTarget) && !lastClickedElement.classList.contains('cds--switcher__item-link')) {
53
+ setExpandedState(false);
54
+ setLastClickedElement(null);
55
+ if (expanded) {
56
+ onHeaderPanelFocus();
57
+ }
58
+ }
59
+ };
60
+ eventHandlers.onKeyDown = event => {
61
+ if (match.match(event, keys.Escape)) {
62
+ setExpandedState(false);
63
+ onHeaderPanelFocus();
64
+ if (href) {
65
+ window.location.href = href;
66
+ }
67
+ }
68
+ };
69
+ }
70
+ useEvent.useWindowEvent('click', () => {
71
+ const focusedElement = document.activeElement;
72
+ setLastClickedElement(focusedElement);
73
+ if (children.type.__docgenInfo.displayName === 'Switcher' && !focusedElement?.closest(`.${prefix}--header-panel--expanded`) && !focusedElement?.closest(`.${prefix}--header__action`) && !headerPanelRef?.current?.classList.contains(`${prefix}--switcher`) && expanded) {
74
+ setExpandedState(false);
75
+ onHeaderPanelFocus();
76
+ }
77
+ });
36
78
  return /*#__PURE__*/React__default["default"].createElement("div", _rollupPluginBabelHelpers["extends"]({}, other, {
37
79
  className: className,
38
- ref: ref
39
- }), children);
80
+ ref: headerPanelRef
81
+ }, eventHandlers), children);
40
82
  });
41
83
  HeaderPanel.propTypes = {
84
+ /**
85
+ * Specify whether focus and blur listeners are added. They are by default.
86
+ */
87
+ addFocusListeners: PropTypes__default["default"].bool,
42
88
  /**
43
89
  * The content that will render inside of the `HeaderPanel`
44
90
  */
@@ -50,7 +96,17 @@ HeaderPanel.propTypes = {
50
96
  /**
51
97
  * Specify whether the panel is expanded
52
98
  */
53
- expanded: PropTypes__default["default"].bool
99
+ expanded: PropTypes__default["default"].bool,
100
+ /**
101
+ * Provide the `href` to the id of the element on your package that could
102
+ * be target.
103
+ */
104
+ href: PropTypes__default["default"].string,
105
+ /**
106
+ * An optional listener that is called a callback to collapse the HeaderPanel
107
+ */
108
+
109
+ onHeaderPanelFocus: PropTypes__default["default"].func
54
110
  };
55
111
  HeaderPanel.displayName = 'HeaderPanel';
56
112
  var HeaderPanel$1 = HeaderPanel;
@@ -20,5 +20,5 @@ interface SideNavProps extends ComponentProps<'nav'> {
20
20
  onSideNavBlur?: () => void | undefined;
21
21
  enterDelayMs?: number;
22
22
  }
23
- declare const SideNav: React.ForwardRefExoticComponent<Pick<SideNavProps, "children" | "slot" | "style" | "title" | "className" | "dir" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "onToggle" | "key" | "id" | "aria-controls" | "aria-expanded" | "onClick" | "onAnimationEnd" | "onKeyDown" | "contentEditable" | "contextMenu" | "draggable" | "hidden" | "lang" | "placeholder" | "spellCheck" | "tabIndex" | "translate" | "radioGroup" | "role" | "about" | "datatype" | "inlist" | "prefix" | "property" | "resource" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "color" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colspan" | "aria-current" | "aria-describedby" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDrag" | "onDragCapture" | "onDragEnd" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStart" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerEnterCapture" | "onPointerLeave" | "onPointerLeaveCapture" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStart" | "onAnimationStartCapture" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "href" | "expanded" | "enterDelayMs" | "defaultExpanded" | "isChildOfHeader" | "isFixedNav" | "isRail" | "isPersistent" | "addFocusListeners" | "addMouseListeners" | "onOverlayClick" | "onSideNavBlur"> & React.RefAttributes<HTMLElement>>;
23
+ declare const SideNav: React.ForwardRefExoticComponent<Pick<SideNavProps, "children" | "slot" | "style" | "title" | "className" | "dir" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "onToggle" | "key" | "id" | "aria-controls" | "aria-expanded" | "onClick" | "onAnimationEnd" | "onKeyDown" | "contentEditable" | "contextMenu" | "draggable" | "hidden" | "lang" | "placeholder" | "spellCheck" | "tabIndex" | "translate" | "radioGroup" | "role" | "about" | "datatype" | "inlist" | "prefix" | "property" | "resource" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "color" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colspan" | "aria-current" | "aria-describedby" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDrag" | "onDragCapture" | "onDragEnd" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStart" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerEnterCapture" | "onPointerLeave" | "onPointerLeaveCapture" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStart" | "onAnimationStartCapture" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "href" | "expanded" | "enterDelayMs" | "defaultExpanded" | "addFocusListeners" | "isChildOfHeader" | "isFixedNav" | "isRail" | "isPersistent" | "addMouseListeners" | "onOverlayClick" | "onSideNavBlur"> & React.RefAttributes<HTMLElement>>;
24
24
  export default SideNav;
@@ -15,6 +15,7 @@ var cx = require('classnames');
15
15
  var PropTypes = require('prop-types');
16
16
  var AriaPropTypes = require('../../prop-types/AriaPropTypes.js');
17
17
  var usePrefix = require('../../internal/usePrefix.js');
18
+ var useMergedRefs = require('../../internal/useMergedRefs.js');
18
19
 
19
20
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
20
21
 
@@ -22,13 +23,16 @@ var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
22
23
  var cx__default = /*#__PURE__*/_interopDefaultLegacy(cx);
23
24
  var PropTypes__default = /*#__PURE__*/_interopDefaultLegacy(PropTypes);
24
25
 
25
- const Switcher = /*#__PURE__*/React__default["default"].forwardRef(function Switcher(props, ref) {
26
+ const Switcher = /*#__PURE__*/React__default["default"].forwardRef(function Switcher(props, forwardRef) {
27
+ const switcherRef = React.useRef(null);
28
+ const ref = useMergedRefs.useMergedRefs([switcherRef, forwardRef]);
26
29
  const prefix = usePrefix.usePrefix();
27
30
  const {
28
31
  'aria-label': ariaLabel,
29
32
  'aria-labelledby': ariaLabelledBy,
30
33
  className: customClassName,
31
- children
34
+ children,
35
+ expanded
32
36
  } = props;
33
37
  const accessibilityLabel = {
34
38
  'aria-label': ariaLabel,
@@ -37,10 +41,42 @@ const Switcher = /*#__PURE__*/React__default["default"].forwardRef(function Swit
37
41
  const className = cx__default["default"](`${prefix}--switcher`, {
38
42
  [customClassName]: !!customClassName
39
43
  });
44
+ const handleSwitcherItemFocus = _ref => {
45
+ let {
46
+ currentIndex,
47
+ direction
48
+ } = _ref;
49
+ const enabledIndices = React__default["default"].Children.toArray(children).reduce((acc, curr, i) => {
50
+ if (Object.keys(curr.props).length !== 0) {
51
+ acc.push(i);
52
+ }
53
+ return acc;
54
+ }, []);
55
+ const nextValidIndex = (() => {
56
+ const nextIndex = enabledIndices.indexOf(currentIndex) + direction;
57
+ switch (enabledIndices[nextIndex]) {
58
+ case undefined:
59
+ if (direction === -1) {
60
+ return enabledIndices[enabledIndices.length - 1];
61
+ }
62
+ return 0;
63
+ default:
64
+ return enabledIndices[nextIndex];
65
+ }
66
+ })();
67
+ const switcherItem = switcherRef.current.children[nextValidIndex].children[0];
68
+ switcherItem?.focus();
69
+ };
70
+ const childrenWithProps = React__default["default"].Children.toArray(children).map((child, index) => /*#__PURE__*/React__default["default"].cloneElement(child, {
71
+ handleSwitcherItemFocus,
72
+ index,
73
+ key: index,
74
+ expanded
75
+ }));
40
76
  return /*#__PURE__*/React__default["default"].createElement("ul", _rollupPluginBabelHelpers["extends"]({
41
77
  ref: ref,
42
78
  className: className
43
- }, accessibilityLabel), children);
79
+ }, accessibilityLabel), childrenWithProps);
44
80
  });
45
81
  Switcher.displayName = 'Switcher';
46
82
  Switcher.propTypes = {
@@ -55,7 +91,11 @@ Switcher.propTypes = {
55
91
  /**
56
92
  * Optionally provide a custom class to apply to the underlying `<ul>` node
57
93
  */
58
- className: PropTypes__default["default"].string
94
+ className: PropTypes__default["default"].string,
95
+ /**
96
+ * Specify whether the panel is expanded
97
+ */
98
+ expanded: PropTypes__default["default"].bool
59
99
  };
60
100
  var Switcher$1 = Switcher;
61
101
 
@@ -16,6 +16,8 @@ var PropTypes = require('prop-types');
16
16
  var AriaPropTypes = require('../../prop-types/AriaPropTypes.js');
17
17
  var Link = require('./Link.js');
18
18
  var usePrefix = require('../../internal/usePrefix.js');
19
+ var match = require('../../internal/keyboard/match.js');
20
+ var keys = require('../../internal/keyboard/keys.js');
19
21
 
20
22
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
21
23
 
@@ -23,17 +25,21 @@ var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
23
25
  var cx__default = /*#__PURE__*/_interopDefaultLegacy(cx);
24
26
  var PropTypes__default = /*#__PURE__*/_interopDefaultLegacy(PropTypes);
25
27
 
26
- const SwitcherItem = /*#__PURE__*/React__default["default"].forwardRef(function SwitcherItem(props, ref) {
27
- const prefix = usePrefix.usePrefix();
28
- const {
28
+ const SwitcherItem = /*#__PURE__*/React__default["default"].forwardRef(function SwitcherItem(_ref, ref) {
29
+ let {
29
30
  'aria-label': ariaLabel,
30
31
  'aria-labelledby': ariaLabelledBy,
31
32
  className: customClassName,
32
33
  children,
33
34
  isSelected,
34
- tabIndex = 0,
35
+ expanded,
36
+ tabIndex = expanded ? 0 : -1,
37
+ index,
38
+ handleSwitcherItemFocus,
39
+ onKeyDown = () => {},
35
40
  ...rest
36
- } = props;
41
+ } = _ref;
42
+ const prefix = usePrefix.usePrefix();
37
43
  const className = cx__default["default"](`${prefix}--switcher__item`, {
38
44
  [customClassName]: !!customClassName
39
45
  });
@@ -44,9 +50,30 @@ const SwitcherItem = /*#__PURE__*/React__default["default"].forwardRef(function
44
50
  const linkClassName = cx__default["default"](`${prefix}--switcher__item-link`, {
45
51
  [`${prefix}--switcher__item-link--selected`]: isSelected
46
52
  });
53
+ function setTabFocus(evt) {
54
+ if (match.match(evt, keys.ArrowDown)) {
55
+ evt.preventDefault();
56
+ handleSwitcherItemFocus?.({
57
+ currentIndex: index,
58
+ direction: 1
59
+ });
60
+ }
61
+ if (match.match(evt, keys.ArrowUp)) {
62
+ evt.preventDefault();
63
+ handleSwitcherItemFocus?.({
64
+ currentIndex: index,
65
+ direction: -1
66
+ });
67
+ }
68
+ }
47
69
  return /*#__PURE__*/React__default["default"].createElement("li", {
48
70
  className: className
49
- }, /*#__PURE__*/React__default["default"].createElement(Link["default"], _rollupPluginBabelHelpers["extends"]({}, rest, {
71
+ }, /*#__PURE__*/React__default["default"].createElement(Link["default"], _rollupPluginBabelHelpers["extends"]({
72
+ onKeyDown: evt => {
73
+ setTabFocus(evt);
74
+ onKeyDown(evt);
75
+ }
76
+ }, rest, {
50
77
  ref: ref,
51
78
  className: linkClassName,
52
79
  tabIndex: tabIndex
@@ -66,6 +93,18 @@ SwitcherItem.propTypes = {
66
93
  * Optionally provide a custom class to apply to the underlying `<li>` node
67
94
  */
68
95
  className: PropTypes__default["default"].string,
96
+ /**
97
+ * event handlers
98
+ */
99
+ handleSwitcherItemFocus: PropTypes__default["default"].func,
100
+ /**
101
+ * Specify the index of the SwitcherItem
102
+ */
103
+ index: PropTypes__default["default"].number,
104
+ /**
105
+ * event handlers
106
+ */
107
+ onKeyDown: PropTypes__default["default"].func,
69
108
  /**
70
109
  * Specify the tab index of the Link
71
110
  */
@@ -0,0 +1,48 @@
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
+ 'use strict';
9
+
10
+ Object.defineProperty(exports, '__esModule', { value: true });
11
+
12
+ var React = require('react');
13
+ var environment = require('./environment.js');
14
+
15
+ function useMatchMedia(mediaQueryString) {
16
+ const [matches, setMatches] = React.useState(() => {
17
+ if (environment.canUseDOM) {
18
+ const mediaQueryList = window.matchMedia(mediaQueryString);
19
+ return mediaQueryList.matches;
20
+ }
21
+ return false;
22
+ });
23
+ React.useEffect(() => {
24
+ function listener(event) {
25
+ setMatches(event.matches);
26
+ }
27
+ const mediaQueryList = window.matchMedia(mediaQueryString);
28
+ // Support fallback to `addListener` for broader browser support
29
+ if (mediaQueryList.addEventListener) {
30
+ mediaQueryList.addEventListener('change', listener);
31
+ } else {
32
+ mediaQueryList.addListener(listener);
33
+ }
34
+
35
+ // Make sure the media query list is in sync with the matches state
36
+ setMatches(mediaQueryList.matches);
37
+ return () => {
38
+ if (mediaQueryList.addEventListener) {
39
+ mediaQueryList.removeEventListener('change', listener);
40
+ } else {
41
+ mediaQueryList.removeListener(listener);
42
+ }
43
+ };
44
+ }, [mediaQueryString]);
45
+ return matches;
46
+ }
47
+
48
+ exports.useMatchMedia = useMatchMedia;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@carbon/react",
3
3
  "description": "React components for the Carbon Design System",
4
- "version": "1.32.0-rc.0",
4
+ "version": "1.32.0",
5
5
  "license": "Apache-2.0",
6
6
  "main": "lib/index.js",
7
7
  "module": "es/index.js",
@@ -46,7 +46,7 @@
46
46
  "@carbon/feature-flags": "^0.15.0",
47
47
  "@carbon/icons-react": "^11.21.0",
48
48
  "@carbon/layout": "^11.15.0",
49
- "@carbon/styles": "^1.32.0-rc.0",
49
+ "@carbon/styles": "^1.32.0",
50
50
  "@carbon/telemetry": "0.1.0",
51
51
  "classnames": "2.3.2",
52
52
  "copy-to-clipboard": "^3.3.1",
@@ -134,5 +134,5 @@
134
134
  "**/*.scss",
135
135
  "**/*.css"
136
136
  ],
137
- "gitHead": "fe40fbf428231bd35f6bc8cc871ea09c7afa5051"
137
+ "gitHead": "507ddae3074272a3be1ff98b3940532d74788d3c"
138
138
  }