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