@carbon-labs/react-ui-shell 0.5.0 → 0.7.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.
@@ -5,26 +5,39 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
  import React, { type ComponentProps, type FocusEvent, type KeyboardEvent, type MouseEventHandler } from 'react';
8
- export interface SideNavProps extends ComponentProps<'nav'> {
9
- expanded?: boolean | undefined;
10
- defaultExpanded?: boolean | undefined;
11
- isChildOfHeader?: boolean | undefined;
12
- onToggle?: (event: FocusEvent<HTMLElement> | KeyboardEvent<HTMLElement> | boolean, value: boolean) => void | undefined;
13
- href?: string | undefined;
14
- isFixedNav?: boolean | undefined;
15
- isRail?: boolean | undefined;
16
- isPersistent?: boolean | undefined;
17
- addFocusListeners?: boolean | undefined;
18
- addMouseListeners?: boolean | undefined;
19
- onOverlayClick?: MouseEventHandler<HTMLDivElement> | undefined;
20
- onSideNavBlur?: () => void | undefined;
8
+ import { TranslateWithId } from '@carbon/react/lib/types/common';
9
+ export declare enum SIDE_NAV_TYPE {
10
+ DEFAULT = "default",
11
+ RAIL = "rail",
12
+ PANEL = "panel"
13
+ }
14
+ export type TranslationKey = keyof typeof translationIds;
15
+ export declare const translationIds: {
16
+ readonly 'collapse.sidenav': "collapse.sidenav";
17
+ readonly 'expand.sidenav': "expand.sidenav";
18
+ };
19
+ export interface SideNavProps extends ComponentProps<'nav'>, TranslateWithId<TranslationKey> {
20
+ expanded?: boolean;
21
+ defaultExpanded?: boolean;
22
+ isChildOfHeader?: boolean;
23
+ onToggle?: (event: FocusEvent<HTMLElement> | KeyboardEvent<HTMLElement> | boolean, value: boolean) => void;
24
+ href?: string;
25
+ isFixedNav?: boolean;
26
+ isRail?: boolean;
27
+ isPersistent?: boolean;
28
+ addFocusListeners?: boolean;
29
+ addMouseListeners?: boolean;
30
+ onOverlayClick?: MouseEventHandler<HTMLDivElement>;
31
+ onSideNavBlur?: () => void;
21
32
  enterDelayMs?: number;
22
33
  inert?: boolean;
23
- isCollapsible: boolean | undefined;
24
- hideOverlay: boolean | undefined;
34
+ isCollapsible: boolean;
35
+ hideOverlay: boolean;
36
+ navType: SIDE_NAV_TYPE;
25
37
  }
26
38
  interface SideNavContextData {
27
- isRail?: boolean | undefined;
39
+ isRail?: boolean;
40
+ navType?: SIDE_NAV_TYPE;
28
41
  }
29
42
  export declare const SideNavContext: React.Context<SideNavContextData>;
30
43
  export declare const SideNav: React.ForwardRefExoticComponent<Omit<SideNavProps, "ref"> & React.RefAttributes<HTMLElement>>;
@@ -12,17 +12,31 @@ 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
- import { keys } from '@carbon/react/lib/internal/keyboard/keys';
15
+ import * as keys from '@carbon/react/lib/internal/keyboard/keys';
16
16
  import { match } 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';
20
20
  import { breakpoints } from '../node_modules/@carbon/layout/es/index.js';
21
21
  import { useMatchMedia } from '@carbon/react/lib/internal/useMatchMedia';
22
+ import { SideNavToggle } from './SideNavToggle.js';
23
+ import { SidePanelClose, SidePanelOpen } from '../node_modules/@carbon/icons-react/es/generated/bucket-15.js';
22
24
 
23
- // TO-DO: comment back in when footer is added for rails
24
- // import SideNavFooter from './SideNavFooter';
25
-
25
+ let SIDE_NAV_TYPE = /*#__PURE__*/function (SIDE_NAV_TYPE) {
26
+ SIDE_NAV_TYPE["DEFAULT"] = "default";
27
+ SIDE_NAV_TYPE["RAIL"] = "rail";
28
+ SIDE_NAV_TYPE["PANEL"] = "panel";
29
+ return SIDE_NAV_TYPE;
30
+ }({});
31
+ const translationIds = {
32
+ 'collapse.sidenav': 'collapse.sidenav',
33
+ 'expand.sidenav': 'expand.sidenav'
34
+ };
35
+ const defaultTranslations = {
36
+ [translationIds['collapse.sidenav']]: 'Collapse',
37
+ [translationIds['expand.sidenav']]: 'Expand'
38
+ };
39
+ const defaultTranslateWithId = id => defaultTranslations[id];
26
40
  const SideNavContext = /*#__PURE__*/createContext({});
27
41
  function SideNavRenderFunction(_ref, ref) {
28
42
  let {
@@ -38,6 +52,7 @@ function SideNavRenderFunction(_ref, ref) {
38
52
  isFixedNav = false,
39
53
  isRail,
40
54
  isPersistent = true,
55
+ navType = SIDE_NAV_TYPE.DEFAULT,
41
56
  addFocusListeners = true,
42
57
  addMouseListeners = true,
43
58
  onOverlayClick,
@@ -45,6 +60,7 @@ function SideNavRenderFunction(_ref, ref) {
45
60
  enterDelayMs = 100,
46
61
  isCollapsible = false,
47
62
  hideOverlay = false,
63
+ translateWithId: t = defaultTranslateWithId,
48
64
  ...other
49
65
  } = _ref;
50
66
  const prefix = usePrefix();
@@ -56,6 +72,7 @@ function SideNavRenderFunction(_ref, ref) {
56
72
  const expanded = controlled ? expandedProp : expandedState;
57
73
  const sideNavRef = useRef(null);
58
74
  const navRef = useMergedRefs([sideNavRef, ref]);
75
+ const sideNavToggleText = expandedState ? t('collapse.sidenav') : t('expand.sidenav');
59
76
  const handleToggle = function (event) {
60
77
  let value = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : !expanded;
61
78
  if (!controlled) {
@@ -77,6 +94,7 @@ function SideNavRenderFunction(_ref, ref) {
77
94
  [`${prefix}--side-nav--expanded`]: expanded || expandedViaHoverState,
78
95
  [`${prefix}--side-nav--collapsed`]: !expanded && isFixedNav,
79
96
  [`${prefix}--side-nav--rail`]: isRail,
97
+ [`${prefix}--side-nav--panel`]: navType === SIDE_NAV_TYPE.PANEL,
80
98
  [`${prefix}--side-nav--ux`]: isChildOfHeader,
81
99
  [`${prefix}--side-nav--hidden`]: !isPersistent,
82
100
  [`${prefix}--side-nav--collapsible`]: isCollapsible
@@ -110,6 +128,9 @@ function SideNavRenderFunction(_ref, ref) {
110
128
  }
111
129
  };
112
130
  eventHandlers.onBlur = event => {
131
+ if (navType === SIDE_NAV_TYPE.PANEL) {
132
+ return;
133
+ }
113
134
  if (!event.currentTarget.contains(event.relatedTarget)) {
114
135
  handleToggle(event, false);
115
136
  }
@@ -167,7 +188,10 @@ function SideNavRenderFunction(_ref, ref) {
167
188
  ref: navRef,
168
189
  className: `${prefix}--side-nav__navigation ${className}`,
169
190
  inert: !isRail ? expanded || isLg ? undefined : -1 : undefined
170
- }, accessibilityLabel, eventHandlers, other), childrenToRender));
191
+ }, accessibilityLabel, eventHandlers, other), childrenToRender, navType === SIDE_NAV_TYPE.PANEL && /*#__PURE__*/React.createElement(SideNavToggle, {
192
+ renderIcon: expandedState ? SidePanelClose : SidePanelOpen,
193
+ onClick: () => setExpandedState(!expandedState)
194
+ }, sideNavToggleText)));
171
195
  }
172
196
  const SideNav = /*#__PURE__*/React.forwardRef(SideNavRenderFunction);
173
197
  SideNav.displayName = 'SideNav';
@@ -259,4 +283,4 @@ SideNav.propTypes = {
259
283
  // translateById: PropTypes.func,
260
284
  };
261
285
 
262
- export { SideNav, SideNavContext };
286
+ export { SIDE_NAV_TYPE, SideNav, SideNavContext, translationIds };
@@ -0,0 +1,28 @@
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, { ReactNode } from 'react';
8
+ interface SideNavToggleProps {
9
+ /**
10
+ * Specify the text content for the link
11
+ */
12
+ children: ReactNode;
13
+ /**
14
+ * Provide an optional function to be called when the item is clicked.
15
+ */
16
+ onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
17
+ /**
18
+ * A custom icon to render next to the SideNavToggle title. This can be a function returning JSX or JSX itself.
19
+ */
20
+ renderIcon?: React.ComponentType;
21
+ /**
22
+ * The tabIndex for the button element.
23
+ * If not specified, the default validation will be applied.
24
+ */
25
+ tabIndex?: number;
26
+ }
27
+ export declare const SideNavToggle: React.ForwardRefExoticComponent<SideNavToggleProps & React.RefAttributes<HTMLElement>>;
28
+ export default SideNavToggle;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Copyright IBM Corp. 2024
3
+ *
4
+ * This source code is licensed under the Apache-2.0 license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import { extends as _extends } from '../_virtual/_rollupPluginBabelHelpers.js';
9
+ import PropTypes from 'prop-types';
10
+ import React from 'react';
11
+ import { SideNavIcon } from '@carbon/react';
12
+ import { usePrefix } from '@carbon/react/lib/internal/usePrefix';
13
+
14
+ const SideNavToggle = /*#__PURE__*/React.forwardRef(function SideNavToggle(_ref, ref) {
15
+ let {
16
+ renderIcon: IconElement,
17
+ tabIndex,
18
+ children,
19
+ ...rest
20
+ } = _ref;
21
+ const prefix = usePrefix();
22
+ return /*#__PURE__*/React.createElement("button", _extends({
23
+ className: `${prefix}--side-nav__toggle`,
24
+ ref: ref,
25
+ type: "button",
26
+ tabIndex: tabIndex ?? 0
27
+ }, rest), IconElement && /*#__PURE__*/React.createElement(SideNavIcon, null, /*#__PURE__*/React.createElement(IconElement, null)), /*#__PURE__*/React.createElement("span", {
28
+ className: `${prefix}--side-nav__toggle-text`
29
+ }, children));
30
+ });
31
+ SideNavToggle.displayName = 'SideNavToggle';
32
+ SideNavToggle.propTypes = {
33
+ /**
34
+ * Specify the text content for the toggle
35
+ */
36
+ children: PropTypes.element,
37
+ /**
38
+ * Provide an optional function to be called when clicked
39
+ */
40
+ onClick: PropTypes.func,
41
+ /**
42
+ * Pass in a custom icon to render next to the `SideNavToggle` title
43
+ */
44
+ // @ts-expect-error - PropTypes are unable to cover this case.
45
+ renderIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
46
+ /**
47
+ * Optional prop to specify the tabIndex of the button. If undefined, it will be applied default validation
48
+ */
49
+ tabIndex: PropTypes.number
50
+ };
51
+
52
+ export { SideNavToggle, SideNavToggle as default };
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Copyright IBM Corp. 2024
3
+ *
4
+ * This source code is licensed under the Apache-2.0 license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ function _defineProperty(e, r, t) {
9
+ return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
10
+ value: t,
11
+ enumerable: !0,
12
+ configurable: !0,
13
+ writable: !0
14
+ }) : e[r] = t, e;
15
+ }
16
+ function ownKeys(e, r) {
17
+ var t = Object.keys(e);
18
+ if (Object.getOwnPropertySymbols) {
19
+ var o = Object.getOwnPropertySymbols(e);
20
+ r && (o = o.filter(function (r) {
21
+ return Object.getOwnPropertyDescriptor(e, r).enumerable;
22
+ })), t.push.apply(t, o);
23
+ }
24
+ return t;
25
+ }
26
+ function _objectSpread2(e) {
27
+ for (var r = 1; r < arguments.length; r++) {
28
+ var t = null != arguments[r] ? arguments[r] : {};
29
+ r % 2 ? ownKeys(Object(t), !0).forEach(function (r) {
30
+ _defineProperty(e, r, t[r]);
31
+ }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) {
32
+ Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
33
+ });
34
+ }
35
+ return e;
36
+ }
37
+ function _objectWithoutProperties(e, t) {
38
+ if (null == e) return {};
39
+ var o,
40
+ r,
41
+ i = _objectWithoutPropertiesLoose(e, t);
42
+ if (Object.getOwnPropertySymbols) {
43
+ var s = Object.getOwnPropertySymbols(e);
44
+ for (r = 0; r < s.length; r++) o = s[r], t.includes(o) || {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]);
45
+ }
46
+ return i;
47
+ }
48
+ function _objectWithoutPropertiesLoose(r, e) {
49
+ if (null == r) return {};
50
+ var t = {};
51
+ for (var n in r) if ({}.hasOwnProperty.call(r, n)) {
52
+ if (e.includes(n)) continue;
53
+ t[n] = r[n];
54
+ }
55
+ return t;
56
+ }
57
+ function _toPrimitive(t, r) {
58
+ if ("object" != typeof t || !t) return t;
59
+ var e = t[Symbol.toPrimitive];
60
+ if (void 0 !== e) {
61
+ var i = e.call(t, r || "default");
62
+ if ("object" != typeof i) return i;
63
+ throw new TypeError("@@toPrimitive must return a primitive value.");
64
+ }
65
+ return ("string" === r ? String : Number)(t);
66
+ }
67
+ function _toPropertyKey(t) {
68
+ var i = _toPrimitive(t, "string");
69
+ return "symbol" == typeof i ? i : i + "";
70
+ }
71
+ var _excluded = ["width", "height", "viewBox"],
72
+ _excluded2 = ["tabindex"];
73
+ /**
74
+ * Copyright IBM Corp. 2018, 2024
75
+ *
76
+ * This source code is licensed under the Apache-2.0 license found in the
77
+ * LICENSE file in the root directory of this source tree.
78
+ */
79
+
80
+ var defaultAttributes = {
81
+ // Reference:
82
+ // https://github.com/IBM/carbon-components-react/issues/1392
83
+ // https://github.com/PolymerElements/iron-iconset-svg/pull/47
84
+ // `focusable` is a string attribute which is why we do not use a boolean here
85
+ focusable: 'false',
86
+ preserveAspectRatio: 'xMidYMid meet'
87
+ };
88
+
89
+ /**
90
+ * Get supplementary HTML attributes for a given <svg> element based on existing
91
+ * attributes.
92
+ */
93
+ function getAttributes() {
94
+ var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
95
+ width = _ref.width,
96
+ height = _ref.height,
97
+ _ref$viewBox = _ref.viewBox,
98
+ viewBox = _ref$viewBox === void 0 ? "0 0 ".concat(width, " ").concat(height) : _ref$viewBox,
99
+ attributes = _objectWithoutProperties(_ref, _excluded);
100
+ var tabindex = attributes.tabindex,
101
+ rest = _objectWithoutProperties(attributes, _excluded2);
102
+ var iconAttributes = _objectSpread2(_objectSpread2(_objectSpread2({}, defaultAttributes), rest), {}, {
103
+ width: width,
104
+ height: height,
105
+ viewBox: viewBox
106
+ });
107
+
108
+ // TODO: attributes.title assumes that the consumer will implement <title> and
109
+ // correctly set `aria-labelledby`.
110
+ if (iconAttributes['aria-label'] || iconAttributes['aria-labelledby'] || iconAttributes.title) {
111
+ iconAttributes.role = 'img';
112
+
113
+ // Reference:
114
+ // https://allyjs.io/tutorials/focusing-in-svg.html
115
+ if (tabindex !== undefined && tabindex !== null) {
116
+ iconAttributes.focusable = 'true';
117
+ iconAttributes.tabindex = tabindex;
118
+ }
119
+ } else {
120
+ iconAttributes['aria-hidden'] = true;
121
+ }
122
+ return iconAttributes;
123
+ }
124
+
125
+ export { defaultAttributes, getAttributes };
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Copyright IBM Corp. 2024
3
+ *
4
+ * This source code is licensed under the Apache-2.0 license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import { getAttributes } from '../../icon-helpers/es/index.js';
9
+ import PropTypes from 'prop-types';
10
+ import React from 'react';
11
+
12
+ /**
13
+ * Copyright IBM Corp. 2019, 2023
14
+ *
15
+ * This source code is licensed under the Apache-2.0 license found in the
16
+ * LICENSE file in the root directory of this source tree.
17
+ */
18
+ const Icon = /*#__PURE__*/React.forwardRef(function Icon(_ref, ref) {
19
+ let {
20
+ className,
21
+ children,
22
+ tabIndex,
23
+ xmlns = 'http://www.w3.org/2000/svg',
24
+ preserveAspectRatio = 'xMidYMid meet',
25
+ ...rest
26
+ } = _ref;
27
+ const {
28
+ tabindex,
29
+ ...attrs
30
+ } = getAttributes({
31
+ ...rest,
32
+ tabindex: tabIndex
33
+ });
34
+ const props = attrs;
35
+ if (className) {
36
+ props.className = className;
37
+ }
38
+ if (tabindex !== undefined && tabindex !== null) {
39
+ if (typeof tabindex === 'number') {
40
+ props.tabIndex = tabindex;
41
+ } else {
42
+ props.tabIndex = Number(tabIndex);
43
+ }
44
+ }
45
+ if (ref) {
46
+ props.ref = ref;
47
+ }
48
+ if (xmlns) {
49
+ props.xmlns = xmlns;
50
+ }
51
+ if (preserveAspectRatio) {
52
+ props.preserveAspectRatio = preserveAspectRatio;
53
+ }
54
+ return /*#__PURE__*/React.createElement('svg', props, children);
55
+ });
56
+ Icon.displayName = 'Icon';
57
+ Icon.propTypes = {
58
+ 'aria-hidden': PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(['true', 'false'])]),
59
+ 'aria-label': PropTypes.string,
60
+ 'aria-labelledby': PropTypes.string,
61
+ children: PropTypes.node,
62
+ className: PropTypes.string,
63
+ height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
64
+ preserveAspectRatio: PropTypes.string,
65
+ tabIndex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
66
+ viewBox: PropTypes.string,
67
+ width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
68
+ xmlns: PropTypes.string
69
+ };
70
+
71
+ export { Icon as default };