@carbon/ibm-products 2.83.0 → 2.84.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.
Files changed (125) hide show
  1. package/css/carbon.css +4 -0
  2. package/css/carbon.css.map +1 -1
  3. package/css/index-full-carbon.css +99 -33
  4. package/css/index-full-carbon.css.map +1 -1
  5. package/css/index-full-carbon.min.css +1 -1
  6. package/css/index-full-carbon.min.css.map +1 -1
  7. package/css/index-without-carbon-released-only.css +95 -33
  8. package/css/index-without-carbon-released-only.css.map +1 -1
  9. package/css/index-without-carbon-released-only.min.css +1 -1
  10. package/css/index-without-carbon-released-only.min.css.map +1 -1
  11. package/css/index-without-carbon.css +95 -33
  12. package/css/index-without-carbon.css.map +1 -1
  13. package/css/index-without-carbon.min.css +1 -1
  14. package/css/index-without-carbon.min.css.map +1 -1
  15. package/css/index.css +95 -33
  16. package/css/index.css.map +1 -1
  17. package/css/index.min.css +1 -1
  18. package/css/index.min.css.map +1 -1
  19. package/es/components/AddSelect/AddSelectBody.js +1 -1
  20. package/es/components/Coachmark/next/Coachmark/CoachmarkBeacon/CoachmarkBeacon.js +6 -6
  21. package/es/components/Datagrid/Datagrid/addons/CustomizeColumns/CustomizeColumnsTearsheet.js +1 -1
  22. package/es/components/PageHeader/PageHeader.js +4 -12
  23. package/es/components/PageHeader/next/PageHeader.js +29 -12
  24. package/es/components/PageHeader/next/context.d.ts +3 -0
  25. package/es/components/PageHeader/next/utils.js +8 -0
  26. package/es/components/SidePanel/SidePanel.js +11 -3
  27. package/es/components/Tearsheet/TearsheetPresence.d.ts +37 -0
  28. package/es/components/Tearsheet/TearsheetPresence.js +56 -0
  29. package/es/components/Tearsheet/TearsheetShell.js +76 -17
  30. package/es/components/Tearsheet/index.d.ts +2 -0
  31. package/es/components/Tearsheet/usePresence.d.ts +17 -0
  32. package/es/components/Tearsheet/usePresence.js +69 -0
  33. package/es/components/Tearsheet/usePresenceContext.d.ts +25 -0
  34. package/es/components/Tearsheet/usePresenceContext.js +50 -0
  35. package/es/global/js/hooks/useMergedRefs.d.ts +1 -0
  36. package/es/global/js/hooks/useMergedRefs.js +32 -0
  37. package/es/global/js/hooks/useOverflowString.js +1 -16
  38. package/es/index.js +1 -0
  39. package/lib/components/AddSelect/AddSelectBody.js +1 -1
  40. package/lib/components/Coachmark/next/Coachmark/CoachmarkBeacon/CoachmarkBeacon.js +6 -6
  41. package/lib/components/Datagrid/Datagrid/addons/CustomizeColumns/CustomizeColumnsTearsheet.js +1 -1
  42. package/lib/components/PageHeader/PageHeader.js +3 -11
  43. package/lib/components/PageHeader/next/PageHeader.js +29 -12
  44. package/lib/components/PageHeader/next/context.d.ts +3 -0
  45. package/lib/components/PageHeader/next/utils.js +8 -0
  46. package/lib/components/SidePanel/SidePanel.js +10 -2
  47. package/lib/components/Tearsheet/TearsheetPresence.d.ts +37 -0
  48. package/lib/components/Tearsheet/TearsheetPresence.js +61 -0
  49. package/lib/components/Tearsheet/TearsheetShell.js +74 -15
  50. package/lib/components/Tearsheet/index.d.ts +2 -0
  51. package/lib/components/Tearsheet/usePresence.d.ts +17 -0
  52. package/lib/components/Tearsheet/usePresence.js +71 -0
  53. package/lib/components/Tearsheet/usePresenceContext.d.ts +25 -0
  54. package/lib/components/Tearsheet/usePresenceContext.js +52 -0
  55. package/lib/global/js/hooks/useMergedRefs.d.ts +1 -0
  56. package/lib/global/js/hooks/useMergedRefs.js +34 -0
  57. package/lib/global/js/hooks/useOverflowString.js +0 -16
  58. package/lib/index.js +3 -0
  59. package/package.json +22 -21
  60. package/scss/components/APIKeyModal/_api-key-modal.scss +6 -4
  61. package/scss/components/AboutModal/_about-modal.scss +5 -5
  62. package/scss/components/ActionBar/_action-bar.scss +2 -0
  63. package/scss/components/ActionSet/_action-set.scss +12 -11
  64. package/scss/components/AddSelect/_add-select.scss +28 -29
  65. package/scss/components/BreadcrumbWithOverflow/_breadcrumb-with-overflow.scss +10 -8
  66. package/scss/components/ButtonMenu/_button-menu.scss +11 -9
  67. package/scss/components/Card/_card.scss +12 -10
  68. package/scss/components/Checklist/_checklist.scss +8 -6
  69. package/scss/components/Coachmark/_coachmark-overlay.scss +11 -9
  70. package/scss/components/Coachmark/_coachmark.scss +1 -1
  71. package/scss/components/CoachmarkStack/_coachmark-stack.scss +6 -4
  72. package/scss/components/ComboButton/_combo-button.scss +11 -9
  73. package/scss/components/CreateFullPage/_create-full-page.scss +9 -9
  74. package/scss/components/CreateModal/_create-modal.scss +9 -7
  75. package/scss/components/CreateSidePanel/_create-side-panel.scss +6 -4
  76. package/scss/components/CreateTearsheet/_create-tearsheet.scss +9 -9
  77. package/scss/components/CreateTearsheetNarrow/_create-tearsheet-narrow.scss +5 -3
  78. package/scss/components/Datagrid/_datagrid.scss +9 -7
  79. package/scss/components/Datagrid/styles/_datagrid.scss +86 -86
  80. package/scss/components/Datagrid/styles/_useExpandedRow.scss +11 -9
  81. package/scss/components/Datagrid/styles/_useInlineEdit.scss +48 -46
  82. package/scss/components/Datagrid/styles/_useNestedRows.scss +16 -16
  83. package/scss/components/Datagrid/styles/_useNestedTable.scss +5 -3
  84. package/scss/components/Datagrid/styles/_useSelectAllToggle.scss +4 -2
  85. package/scss/components/Datagrid/styles/_useSortableColumns.scss +21 -19
  86. package/scss/components/Datagrid/styles/addons/_CustomizeColumnsTearsheet.scss +5 -4
  87. package/scss/components/Datagrid/styles/addons/_FilterFlyout.scss +5 -5
  88. package/scss/components/Datagrid/styles/addons/_FilterPanel.scss +11 -8
  89. package/scss/components/Datagrid/styles/addons/_RowSizeDropdown.scss +18 -16
  90. package/scss/components/Datagrid/styles/addons/_animations.scss +4 -4
  91. package/scss/components/DescriptionList/_description-list.scss +6 -4
  92. package/scss/components/EditInPlace/_edit-in-place.scss +5 -9
  93. package/scss/components/EditSidePanel/_edit-side-panel.scss +6 -4
  94. package/scss/components/EditTearsheet/_edit-tearsheet.scss +8 -9
  95. package/scss/components/ExportModal/_export-modal.scss +7 -5
  96. package/scss/components/FilterPanel/_filter-panel-accordion-item.scss +6 -5
  97. package/scss/components/FilterPanel/_filter-panel-checkbox-with-overflow.scss +6 -5
  98. package/scss/components/FilterPanel/_filter-panel-checkbox.scss +6 -5
  99. package/scss/components/FilterPanel/_filter-panel.scss +6 -5
  100. package/scss/components/FilterSummary/_filter-summary.scss +5 -9
  101. package/scss/components/Guidebanner/_guidebanner.scss +5 -3
  102. package/scss/components/ImportModal/_import-modal.scss +16 -16
  103. package/scss/components/InterstitialScreen/_interstitial-screen.scss +6 -4
  104. package/scss/components/NotificationsPanel/_notifications-panel.scss +13 -8
  105. package/scss/components/OptionsTile/_options-tile.scss +8 -6
  106. package/scss/components/PageHeader/_page-header.scss +25 -21
  107. package/scss/components/RemoveModal/_remove-modal.scss +5 -4
  108. package/scss/components/Saving/_saving.scss +5 -3
  109. package/scss/components/SearchBar/_search-bar.scss +5 -4
  110. package/scss/components/SidePanel/_animations.scss +4 -4
  111. package/scss/components/SidePanel/_side-panel.scss +31 -12
  112. package/scss/components/SimpleHeader/_simple-header.scss +5 -4
  113. package/scss/components/StatusIcon/_status-icon.scss +5 -3
  114. package/scss/components/StatusIndicator/_status-indicator.scss +3 -2
  115. package/scss/components/StringFormatter/_string-formatter.scss +5 -4
  116. package/scss/components/TagOverflow/_tag-overflow.scss +7 -6
  117. package/scss/components/TagSet/_tag-set.scss +20 -18
  118. package/scss/components/Tearsheet/_tearsheet.scss +121 -30
  119. package/scss/components/Toolbar/_toolbar.scss +4 -2
  120. package/scss/components/TruncatedList/_truncated-list.scss +4 -3
  121. package/scss/components/TruncatedText/_truncated-text.scss +2 -2
  122. package/scss/components/UserAvatar/_user-avatar.scss +5 -4
  123. package/scss/components/UserProfileImage/_user-profile-image.scss +11 -7
  124. package/scss/components/WebTerminal/_web-terminal.scss +4 -2
  125. package/telemetry.yml +3 -0
@@ -0,0 +1,25 @@
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 { type RefObject } from 'react';
8
+ export interface PresenceContext {
9
+ /**
10
+ * The ref object the presence mode is mounted with
11
+ */
12
+ presenceRef: RefObject<HTMLDivElement | null>;
13
+ /**
14
+ * Indicates whether the ref object is currently exiting
15
+ */
16
+ isExiting: boolean;
17
+ /**
18
+ * Returns if the caller is exclusive to this presence context
19
+ */
20
+ isPresenceExclusive: (id: string) => boolean;
21
+ }
22
+ /**
23
+ * Returns if the presence node is present and the context value to be used by a presence context, e.g. ModalPresence.
24
+ */
25
+ export declare const usePresenceContext: (open: boolean, initialPresenceId?: string) => readonly [boolean, PresenceContext];
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Copyright IBM Corp. 2020, 2026
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 { useRef, useCallback, useMemo } from 'react';
9
+ import { usePresence } from './usePresence.js';
10
+
11
+ /**
12
+ * Returns if the presence node is present and the context value to be used by a presence context, e.g. ModalPresence.
13
+ */
14
+ const usePresenceContext = (open, initialPresenceId) => {
15
+ const presenceIdRef = useRef(initialPresenceId);
16
+ const presenceRef = useRef(null);
17
+ const prevPresenceRef = useRef(null);
18
+
19
+ // clean up the presence id, if not predefined and if the presence node was unmounted
20
+ if (!initialPresenceId && prevPresenceRef.current && !presenceRef.current) {
21
+ presenceIdRef.current = null;
22
+ }
23
+ prevPresenceRef.current = presenceRef.current;
24
+ const {
25
+ isPresent,
26
+ isExiting
27
+ } = usePresence(presenceRef, open);
28
+ const isPresenceExclusive = useCallback(id => {
29
+ if (!id) {
30
+ return false;
31
+ }
32
+
33
+ // return false if the presence context is occupied
34
+ if (presenceIdRef.current && presenceIdRef.current !== id) {
35
+ return false;
36
+ }
37
+
38
+ // otherwise occupy presence context and return true
39
+ presenceIdRef.current = id;
40
+ return true;
41
+ }, []);
42
+ const contextValue = useMemo(() => ({
43
+ presenceRef,
44
+ isPresenceExclusive,
45
+ isExiting
46
+ }), [presenceRef, isPresenceExclusive, isExiting]);
47
+ return [isPresent, contextValue];
48
+ };
49
+
50
+ export { usePresenceContext };
@@ -0,0 +1 @@
1
+ export function useMergedRefs(refs: any): (node: any) => void;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Copyright IBM Corp. 2020, 2026
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 { useMemo, useCallback } from 'react';
9
+
10
+ /**
11
+ * Merges multiple refs into a single callback ref.
12
+ *
13
+ * This hook is useful when you need to attach multiple refs (for example, a ref
14
+ * passed from `forwardRef` and a local ref from `useRef`) to the same node. It
15
+ * accepts an array of refs and returns a callback ref that, when attached to a
16
+ * node, assigns that node to every ref in the array.
17
+ */
18
+ const useMergedRefs = refs => {
19
+ // eslint-disable-next-line react-hooks/exhaustive-deps
20
+ const memoizedRefs = useMemo(() => refs, refs);
21
+ return useCallback(node => {
22
+ memoizedRefs.forEach(ref => {
23
+ if (typeof ref === 'function') {
24
+ ref(node);
25
+ } else if (ref) {
26
+ ref.current = node;
27
+ }
28
+ });
29
+ }, [memoizedRefs]);
30
+ };
31
+
32
+ export { useMergedRefs };
@@ -22,20 +22,5 @@ function useOverflowStringWidth(elementRef) {
22
22
  }, [checkWidthOverflow, elementRef, innerText]);
23
23
  return isOverflowing;
24
24
  }
25
- const useOverflowStringHeight = elementRef => {
26
- const innerText = elementRef?.current?.innerText;
27
- const [isOverflowing, setIsOverflowing] = useState();
28
- const checkHeightOverflow = useCallback(() => {
29
- const offsetHeight = elementRef?.current?.offsetHeight;
30
- const scrollHeight = elementRef?.current?.scrollHeight;
31
- if (offsetHeight && scrollHeight) {
32
- setIsOverflowing(offsetHeight < scrollHeight);
33
- }
34
- }, [elementRef]);
35
- useEffect(() => {
36
- checkHeightOverflow();
37
- }, [checkHeightOverflow, elementRef, innerText]);
38
- return isOverflowing;
39
- };
40
25
 
41
- export { useOverflowStringHeight, useOverflowStringWidth };
26
+ export { useOverflowStringWidth };
package/es/index.js CHANGED
@@ -49,6 +49,7 @@ export { TagOverflow } from './components/TagOverflow/TagOverflow.js';
49
49
  export { TagSet } from './components/TagSet/TagSet.js';
50
50
  export { Tearsheet } from './components/Tearsheet/Tearsheet.js';
51
51
  export { TearsheetNarrow } from './components/Tearsheet/TearsheetNarrow.js';
52
+ export { TearsheetPresence, withTearsheetPresence } from './components/Tearsheet/TearsheetPresence.js';
52
53
  export { WebTerminal } from './components/WebTerminal/WebTerminal.js';
53
54
  export { WebTerminalContentWrapper } from './components/WebTerminal/WebTerminalContentWrapper.js';
54
55
  export { WebTerminalProvider, useWebTerminal } from './components/WebTerminal/hooks/index.js';
@@ -14,6 +14,7 @@ var cx = require('classnames');
14
14
  var react = require('@carbon/react');
15
15
  var Tearsheet = require('../Tearsheet/Tearsheet.js');
16
16
  var TearsheetNarrow = require('../Tearsheet/TearsheetNarrow.js');
17
+ var settings = require('../../settings.js');
17
18
  require('../EmptyStates/EmptyState.js');
18
19
  require('../EmptyStates/EmptyStateV2.deprecated.js');
19
20
  require('../EmptyStates/ErrorEmptyState/ErrorEmptyState.js');
@@ -32,7 +33,6 @@ var addSelectUtils = require('./add-select-utils.js');
32
33
  var useItemSort = require('./hooks/useItemSort.js');
33
34
  var useParentSelect = require('./hooks/useParentSelect.js');
34
35
  var usePath = require('./hooks/usePath.js');
35
- var settings = require('../../settings.js');
36
36
 
37
37
  const blockClass = `${settings.pkg.prefix}--add-select`;
38
38
  const componentName = 'AddSelectBody';
@@ -33,18 +33,18 @@ const CoachmarkBeacon = /*#__PURE__*/React.forwardRef((props, ref) => {
33
33
  } = props;
34
34
  return /*#__PURE__*/React.createElement("div", _rollupPluginBabelHelpers.extends({
35
35
  className: cx(blockClass, `${blockClass}-${kind}`, className)
36
- }, devtools.getDevtoolsProps(componentName), {
37
- role: "tooltip"
38
- }, rest, {
36
+ }, devtools.getDevtoolsProps(componentName), rest, {
39
37
  ref: ref
40
38
  }), /*#__PURE__*/React.createElement("button", _rollupPluginBabelHelpers.extends({
41
39
  type: "button"
42
40
  }, buttonProps, {
43
- className: `${blockClass}__target`
41
+ className: `${blockClass}__target`,
42
+ "aria-label": label
44
43
  }), /*#__PURE__*/React.createElement("svg", {
45
44
  className: `${blockClass}__center`,
46
- "aria-label": label
47
- }, /*#__PURE__*/React.createElement("title", null, label), _circle || (_circle = /*#__PURE__*/React.createElement("circle", {
45
+ "aria-hidden": "true",
46
+ focusable: "false"
47
+ }, _circle || (_circle = /*#__PURE__*/React.createElement("circle", {
48
48
  r: 1,
49
49
  cx: 38,
50
50
  cy: 38
@@ -13,9 +13,9 @@ var React = require('react');
13
13
  var index = require('../../../../../_virtual/index.js');
14
14
  require('../../../../Tearsheet/Tearsheet.js');
15
15
  var TearsheetNarrow = require('../../../../Tearsheet/TearsheetNarrow.js');
16
+ var settings = require('../../../../../settings.js');
16
17
  var Columns = require('./Columns.js');
17
18
  var Actions = require('./Actions.js');
18
- var settings = require('../../../../../settings.js');
19
19
 
20
20
  const blockClass = `${settings.pkg.prefix}--datagrid`;
21
21
  const CustomizeColumnsTearsheet = _ref => {
@@ -21,7 +21,6 @@ var cx = require('classnames');
21
21
  var devtools = require('../../global/js/utils/devtools.js');
22
22
  var settings = require('../../settings.js');
23
23
  var useResizeObserver = require('../../global/js/hooks/useResizeObserver.js');
24
- var useOverflowString = require('../../global/js/hooks/useOverflowString.js');
25
24
  var useWindowScroll = require('../../global/js/hooks/useWindowScroll.js');
26
25
  var useWindowResize = require('../../global/js/hooks/useWindowResize.js');
27
26
  var useIsomorphicEffect = require('../../global/js/hooks/useIsomorphicEffect.js');
@@ -357,12 +356,6 @@ const PageHeader = /*#__PURE__*/React.forwardRef((_ref, ref) => {
357
356
  }
358
357
  });
359
358
  }, [headerRef, pageHeaderStyles]);
360
- const subtitleRef = React.useRef(null);
361
- const isOverflowing = useOverflowString.useOverflowStringHeight(subtitleRef);
362
- const subtitleContent = /*#__PURE__*/React.createElement("span", {
363
- ref: subtitleRef,
364
- className: `${PageHeaderUtils.blockClass}__subtitle-text`
365
- }, subtitle);
366
359
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
367
360
  className: `${PageHeaderUtils.blockClass}--offset-top-measuring-element`,
368
361
  ref: offsetTopMeasuringRef
@@ -439,10 +432,9 @@ const PageHeader = /*#__PURE__*/React.forwardRef((_ref, ref) => {
439
432
  className: `${PageHeaderUtils.blockClass}__subtitle-row`
440
433
  }, /*#__PURE__*/React.createElement(react.Column, {
441
434
  className: `${PageHeaderUtils.blockClass}__subtitle`
442
- }, isOverflowing ? /*#__PURE__*/React.createElement(react.DefinitionTooltip, {
443
- definition: subtitle,
444
- className: `${PageHeaderUtils.blockClass}__subtitle-tooltip`
445
- }, subtitleContent) : subtitleContent)), children ? /*#__PURE__*/React.createElement(react.Row, {
435
+ }, /*#__PURE__*/React.createElement("span", {
436
+ className: `${PageHeaderUtils.blockClass}__subtitle-text`
437
+ }, subtitle))), children ? /*#__PURE__*/React.createElement(react.Row, {
446
438
  className: `${PageHeaderUtils.blockClass}__available-row`
447
439
  }, /*#__PURE__*/React.createElement(react.Column, {
448
440
  className: `${PageHeaderUtils.blockClass}__available-column`
@@ -59,6 +59,7 @@ const PageHeader = /*#__PURE__*/React.forwardRef(function PageHeader(_ref, ref)
59
59
  const [fullyCollapsed, setFullyCollapsed] = React.useState(false);
60
60
  const [titleClipped, setTitleClipped] = React.useState(false);
61
61
  const [contentActionsClipped, setContentActionsClipped] = React.useState(false);
62
+ const [breadcrumbActionsClipped, setBreadcrumbActionsClipped] = React.useState(false);
62
63
 
63
64
  // Intersection Observer setup, tracks if the PageHeaderContent is visible on page.
64
65
  // If it is not visible, we should set fully collapsed to true so that the
@@ -103,6 +104,7 @@ const PageHeader = /*#__PURE__*/React.forwardRef(function PageHeader(_ref, ref)
103
104
  entries.forEach(entry => {
104
105
  if (entry.target === refs?.contentActions.current) {
105
106
  setContentActionsClipped(!entry.isIntersecting);
107
+ setBreadcrumbActionsClipped(entry.isIntersecting);
106
108
  }
107
109
  });
108
110
  }, {
@@ -142,7 +144,8 @@ const PageHeader = /*#__PURE__*/React.forwardRef(function PageHeader(_ref, ref)
142
144
  pageActionsInstance,
143
145
  setPageActionsInstance,
144
146
  titleClipped,
145
- contentActionsClipped
147
+ contentActionsClipped,
148
+ breadcrumbActionsClipped
146
149
  }
147
150
  }, /*#__PURE__*/React.createElement("div", _rollupPluginBabelHelpers.extends({
148
151
  className: classNames,
@@ -169,10 +172,11 @@ const PageHeaderBreadcrumbBar = /*#__PURE__*/React.forwardRef(function PageHeade
169
172
  pageActionsFlush,
170
173
  ...other
171
174
  } = _ref2;
175
+ const context$1 = context.usePageHeader();
172
176
  const {
173
177
  pageActionsInstance: globalActions,
174
178
  contentActionsClipped
175
- } = context.usePageHeader();
179
+ } = context$1;
176
180
  const classNames = cx({
177
181
  [`${PageHeaderUtils.blockClass}__breadcrumb-bar`]: true,
178
182
  [`${PageHeaderUtils.blockClass}__breadcrumb-bar-border`]: border,
@@ -183,7 +187,12 @@ const PageHeaderBreadcrumbBar = /*#__PURE__*/React.forwardRef(function PageHeade
183
187
  [`${PageHeaderUtils.blockClass}__breadcrumb__content-actions-with-global-actions`]: !!globalActions,
184
188
  [`${PageHeaderUtils.blockClass}__breadcrumb__content-actions-with-global-actions--show`]: contentActionsClipped
185
189
  });
186
- return /*#__PURE__*/React.createElement("div", _rollupPluginBabelHelpers.extends({
190
+ return /*#__PURE__*/React.createElement(context.PageHeaderContext.Provider, {
191
+ value: {
192
+ ...context$1,
193
+ isContentActionsInBreadcrumbBar: true
194
+ }
195
+ }, /*#__PURE__*/React.createElement("div", _rollupPluginBabelHelpers.extends({
187
196
  className: classNames,
188
197
  ref: ref
189
198
  }, other), /*#__PURE__*/React.createElement(react.Grid, null, /*#__PURE__*/React.createElement(react.Column, {
@@ -200,7 +209,7 @@ const PageHeaderBreadcrumbBar = /*#__PURE__*/React.forwardRef(function PageHeade
200
209
  className: `${PageHeaderUtils.blockClass}__breadcrumb__actions`
201
210
  }, /*#__PURE__*/React.createElement("div", {
202
211
  className: contentActionsClasses
203
- }, contentActions), pageActions)))));
212
+ }, contentActions), pageActions))))));
204
213
  });
205
214
  PageHeaderBreadcrumbBar.displayName = 'PageHeaderBreadcrumbBar';
206
215
 
@@ -332,12 +341,14 @@ const PageHeaderContentPageActions = _ref4 => {
332
341
  } = _ref4;
333
342
  const {
334
343
  setRefs,
335
- contentActionsClipped
344
+ contentActionsClipped,
345
+ breadcrumbActionsClipped,
346
+ isContentActionsInBreadcrumbBar: isInBreadcrumbBar
336
347
  } = context.usePageHeader();
337
348
  const classNames = cx(`${PageHeaderUtils.blockClass}__content__page-actions`, {
338
349
  // Revisit this:
339
350
  // May want to only add this class if there are content actions in the breadcrumb bar as well
340
- [`${PageHeaderUtils.blockClass}__content__page-actions--clipped`]: contentActionsClipped
351
+ [`${PageHeaderUtils.blockClass}__content__page-actions--clipped`]: isInBreadcrumbBar ? breadcrumbActionsClipped : contentActionsClipped
341
352
  }, className);
342
353
  const containerRef = React.useRef(null);
343
354
  const offsetRef = React.useRef(null);
@@ -353,12 +364,18 @@ const PageHeaderContentPageActions = _ref4 => {
353
364
  }
354
365
  }, [menuButtonVisibility]);
355
366
  React.useEffect(() => {
356
- setRefs(prev => ({
357
- ...prev,
358
- contentActions: containerRef
359
- }));
360
- // eslint-disable-next-line react-hooks/exhaustive-deps
361
- }, []);
367
+ if (isInBreadcrumbBar) {
368
+ setRefs(prev => ({
369
+ ...prev,
370
+ breadcrumbActions: containerRef
371
+ }));
372
+ } else {
373
+ setRefs(prev => ({
374
+ ...prev,
375
+ contentActions: containerRef
376
+ }));
377
+ }
378
+ }, [isInBreadcrumbBar, setRefs]);
362
379
  React.useEffect(() => {
363
380
  if (!containerRef.current || !Array.isArray(actions)) {
364
381
  return;
@@ -14,6 +14,7 @@ export type PageHeaderRefs = {
14
14
  contentRef?: RefObject<HTMLDivElement | null>;
15
15
  titleRef?: RefObject<HTMLHeadingElement | null>;
16
16
  contentActions?: RefObject<HTMLDivElement | null>;
17
+ breadcrumbActions?: RefObject<HTMLDivElement | null>;
17
18
  };
18
19
  type PageHeaderContextType = {
19
20
  refs?: PageHeaderRefs;
@@ -23,6 +24,8 @@ type PageHeaderContextType = {
23
24
  fullyCollapsed?: boolean;
24
25
  titleClipped?: boolean;
25
26
  contentActionsClipped?: boolean;
27
+ breadcrumbActionsClipped?: boolean;
28
+ isContentActionsInBreadcrumbBar?: boolean;
26
29
  };
27
30
  export declare const PageHeaderContext: import("react").Context<PageHeaderContextType | undefined>;
28
31
  export declare function usePageHeader(): PageHeaderContextType;
@@ -41,6 +41,14 @@ const windowExists = typeof window !== `undefined`;
41
41
  */
42
42
  const scrollable = target => {
43
43
  const style = window.getComputedStyle(target);
44
+ const tagName = target.tagName.toLowerCase();
45
+
46
+ // Exclude body/html from hidden check (modals set overflow:hidden on body)
47
+ if (tagName === 'body' || tagName === 'html') {
48
+ return /(auto|scroll)/.test(style.overflow);
49
+ }
50
+
51
+ // For other elements, include hidden as it may be intentional scroll container
44
52
  return /(auto|scroll|hidden)/.test(style.overflow);
45
53
  };
46
54
 
@@ -115,11 +115,19 @@ const SidePanel = /*#__PURE__*/React.forwardRef((props, ref) => {
115
115
 
116
116
  // Title animation on scroll related state
117
117
  const [labelTextHeight, setLabelTextHeight] = React.useState(0);
118
- const handleEscapeKey = event => {
118
+ const handleEscapeKey = React.useCallback(event => {
119
119
  if (event.key === 'Escape' && open) {
120
120
  onRequestClose?.();
121
121
  }
122
- };
122
+ }, [onRequestClose, open]);
123
+ React.useEffect(() => {
124
+ if (open && !slideIn) {
125
+ window.addEventListener('keydown', handleEscapeKey);
126
+ return () => {
127
+ window.removeEventListener('keydown', handleEscapeKey);
128
+ };
129
+ }
130
+ }, [handleEscapeKey, open, slideIn]);
123
131
  React.useEffect(() => {
124
132
  if (!enableResizer) {
125
133
  return;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Copyright IBM Corp. 2016, 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, { type ComponentType, type FC, type PropsWithChildren } from 'react';
8
+ import { type PresenceContext } from './usePresenceContext';
9
+ export interface TearsheetPresenceProps {
10
+ /**
11
+ * Specify whether the Modal is currently open
12
+ */
13
+ open: boolean;
14
+ /**
15
+ * Internal property for backwards compatibility. Specify whether the Modal should opt in to presence mode.
16
+ */
17
+ _autoEnablePresence?: boolean;
18
+ /**
19
+ * Internal property to predefine the presence context's id for exclusivity.
20
+ */
21
+ _presenceId?: string;
22
+ }
23
+ export declare const TearsheetPresence: ({ open, _presenceId: presenceId, _autoEnablePresence: autoEnablePresence, children, }: PropsWithChildren<TearsheetPresenceProps>) => React.JSX.Element | null;
24
+ interface ModalPresenceContextProps extends PresenceContext {
25
+ autoEnablePresence: boolean;
26
+ }
27
+ export declare const TearsheetPresenceContext: React.Context<ModalPresenceContextProps | undefined>;
28
+ /**
29
+ * Handles occurrences where only a single modal must consume a context.
30
+ */
31
+ export declare const useExclusiveTearsheetPresenceContext: (id: string) => ModalPresenceContextProps | undefined;
32
+ type WithModalPresenceProps = Pick<TearsheetPresenceProps, 'open'>;
33
+ /**
34
+ * Higher-order function that wraps a component with ModalPresence
35
+ */
36
+ export declare const withTearsheetPresence: <TProps extends object>(Component: ComponentType<TProps>) => FC<TProps & WithModalPresenceProps>;
37
+ export {};
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Copyright IBM Corp. 2020, 2026
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 usePresenceContext = require('./usePresenceContext.js');
12
+
13
+ const TearsheetPresence = _ref => {
14
+ let {
15
+ open,
16
+ _presenceId: presenceId,
17
+ _autoEnablePresence: autoEnablePresence = true,
18
+ children
19
+ } = _ref;
20
+ const [isPresent, context] = usePresenceContext.usePresenceContext(open, presenceId);
21
+ const contextValue = React.useMemo(() => ({
22
+ autoEnablePresence,
23
+ ...context
24
+ }), [autoEnablePresence, context]);
25
+ if (!isPresent) {
26
+ return null;
27
+ }
28
+ return /*#__PURE__*/React.createElement(TearsheetPresenceContext.Provider, {
29
+ value: contextValue
30
+ }, children);
31
+ };
32
+ const TearsheetPresenceContext = /*#__PURE__*/React.createContext(undefined);
33
+
34
+ /**
35
+ * Handles occurrences where only a single modal must consume a context.
36
+ */
37
+ const useExclusiveTearsheetPresenceContext = id => {
38
+ const ctx = React.useContext(TearsheetPresenceContext);
39
+ return ctx?.isPresenceExclusive(id) ? ctx : undefined;
40
+ };
41
+ /**
42
+ * Higher-order function that wraps a component with ModalPresence
43
+ */
44
+ const withTearsheetPresence = Component => {
45
+ const WithModalPresence = props => {
46
+ const {
47
+ open,
48
+ ...componentProps
49
+ } = props;
50
+ return /*#__PURE__*/React.createElement(TearsheetPresence, {
51
+ open: open
52
+ }, /*#__PURE__*/React.createElement(Component, componentProps));
53
+ };
54
+ WithModalPresence.displayName = `withModalPresence(${Component.displayName || Component.name || 'Component'})`;
55
+ return WithModalPresence;
56
+ };
57
+
58
+ exports.TearsheetPresence = TearsheetPresence;
59
+ exports.TearsheetPresenceContext = TearsheetPresenceContext;
60
+ exports.useExclusiveTearsheetPresenceContext = useExclusiveTearsheetPresenceContext;
61
+ exports.withTearsheetPresence = withTearsheetPresence;