@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
@@ -21,6 +21,9 @@ var ActionSet = require('../ActionSet/ActionSet.js');
21
21
  var Wrap = require('../../global/js/utils/Wrap.js');
22
22
  var usePortalTarget = require('../../global/js/hooks/usePortalTarget.js');
23
23
  var useFocus = require('../../global/js/hooks/useFocus.js');
24
+ var useMergedRefs = require('../../global/js/hooks/useMergedRefs.js');
25
+ var useId = require('../../global/js/utils/useId.js');
26
+ var TearsheetPresence = require('./TearsheetPresence.js');
24
27
  var usePreviousValue = require('../../global/js/hooks/usePreviousValue.js');
25
28
  var useIsomorphicEffect = require('../../global/js/hooks/useIsomorphicEffect.js');
26
29
 
@@ -74,6 +77,35 @@ const SectionLevel3 = _ref => {
74
77
  * See the canvas tab for the component API details.
75
78
  * */
76
79
  const TearsheetShell = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
80
+ let {
81
+ open,
82
+ ...props
83
+ } = _ref2;
84
+ const id = useId.useId();
85
+ const enablePresence = react.useFeatureFlag('enable-presence');
86
+ const hasPresenceContext = Boolean(React.useContext(TearsheetPresence.TearsheetPresenceContext));
87
+ const hasPresenceOptIn = enablePresence || hasPresenceContext;
88
+ const exclusivePresenceContext = TearsheetPresence.useExclusiveTearsheetPresenceContext(id);
89
+
90
+ // if opt in and not exclusive to a presence context, wrap with presence
91
+ if (hasPresenceOptIn && !exclusivePresenceContext) {
92
+ return /*#__PURE__*/React.createElement(TearsheetPresence.TearsheetPresence, {
93
+ open: open ?? false,
94
+ _presenceId: id
95
+ // do not auto enable styles for opt-in by feature flag
96
+ ,
97
+ _autoEnablePresence: hasPresenceContext
98
+ }, /*#__PURE__*/React.createElement(TearsheetShellDialog, _rollupPluginBabelHelpers.extends({
99
+ open: true,
100
+ ref: ref
101
+ }, props)));
102
+ }
103
+ return /*#__PURE__*/React.createElement(TearsheetShellDialog, _rollupPluginBabelHelpers.extends({
104
+ ref: ref,
105
+ open: open
106
+ }, props));
107
+ });
108
+ const TearsheetShellDialog = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
77
109
  let {
78
110
  // The component props, in alphabetical order (for consistency).
79
111
  actions,
@@ -93,7 +125,7 @@ const TearsheetShell = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
93
125
  label,
94
126
  navigation,
95
127
  onClose,
96
- open,
128
+ open: externalOpen,
97
129
  portalTarget: portalTargetIn,
98
130
  selectorPrimaryFocus,
99
131
  selectorsFloatingMenus = [],
@@ -104,7 +136,7 @@ const TearsheetShell = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
104
136
  launcherButtonRef,
105
137
  // Collect any other property values passed in.
106
138
  ...rest
107
- } = _ref2;
139
+ } = _ref3;
108
140
  const carbonPrefix = react.usePrefix();
109
141
  const bcModalHeader = `${carbonPrefix}--modal-header`;
110
142
  const renderPortalUse = usePortalTarget.usePortalTarget(portalTargetIn);
@@ -115,13 +147,19 @@ const TearsheetShell = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
115
147
  const {
116
148
  width
117
149
  } = useResizeObserver.useResizeObserver(resizer);
118
- const prevOpen = usePreviousValue.usePreviousValue(open);
119
150
  const {
120
151
  keyDownListener,
121
152
  claimFocus
122
153
  } = useFocus.useFocus(modalRef, selectorPrimaryFocus);
123
154
  modalRef.current;
124
155
  const wide = size === 'wide';
156
+ const presenceContext = React.useContext(TearsheetPresence.TearsheetPresenceContext);
157
+ const mergedRefs = useMergedRefs.useMergedRefs([modalRef, presenceContext?.presenceRef]);
158
+ const enablePresence = react.useFeatureFlag('enable-presence') || presenceContext?.autoEnablePresence;
159
+
160
+ // always mark as open when mounted with presence
161
+ const open = externalOpen || enablePresence;
162
+ const prevOpen = usePreviousValue.usePreviousValue(open);
125
163
 
126
164
  // Keep track of the stack depth and our position in it (1-based, 0=closed)
127
165
  const [depth, setDepth] = React.useState(0);
@@ -148,13 +186,28 @@ const TearsheetShell = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
148
186
  claimFocus();
149
187
  }
150
188
  }, [open, currentStep, effectiveHasCloseIcon, claimFocus]);
189
+
190
+ // Focus launcher button on open change for non presence tearsheet
151
191
  React.useEffect(() => {
152
- if (prevOpen && !open && launcherButtonRef?.current) {
192
+ if (!enablePresence && prevOpen && !open && launcherButtonRef?.current) {
153
193
  setTimeout(() => {
154
194
  launcherButtonRef?.current?.focus();
155
195
  }, 10);
156
196
  }
157
- }, [open, prevOpen, launcherButtonRef]);
197
+ }, [enablePresence, open, prevOpen, launcherButtonRef]);
198
+
199
+ // Focus launcher button on unmount for presence tearsheet
200
+ React.useEffect(() => {
201
+ const launcherButton = launcherButtonRef?.current;
202
+ if (!enablePresence || !launcherButton) {
203
+ return;
204
+ }
205
+ return () => {
206
+ setTimeout(() => {
207
+ launcherButton.focus();
208
+ }, 10);
209
+ };
210
+ }, [enablePresence, launcherButtonRef]);
158
211
  React.useEffect(() => {
159
212
  requestAnimationFrame(() => {
160
213
  if (open && depth === position && !modalRef?.current?.contains(document.activeElement)) {
@@ -169,6 +222,8 @@ const TearsheetShell = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
169
222
  }
170
223
  }, [claimFocus, hasError, modalRef]);
171
224
  React.useEffect(() => {
225
+ // Other tearsheets should already be notified if this tearsheet is exiting
226
+ const isPresent = open && !presenceContext?.isExiting;
172
227
  const notify = () => stack.all.forEach(handler => {
173
228
  handler(Math.min(stack.open.length, maxDepth), stack.open.indexOf(handler) + 1);
174
229
  });
@@ -181,7 +236,7 @@ const TearsheetShell = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
181
236
  // false to true to open it then append its notification callback to
182
237
  // the end of the stack array (as its ID), and call all the callbacks
183
238
  // to notify all open tearsheets that the stacking has changed.
184
- if (open) {
239
+ if (isPresent) {
185
240
  stack.open.push(handleStackChange);
186
241
  notify();
187
242
  }
@@ -203,7 +258,7 @@ const TearsheetShell = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
203
258
  notify();
204
259
  }
205
260
  };
206
- }, [open, size]);
261
+ }, [open, presenceContext?.isExiting, size]);
207
262
  const areAllSameSizeVariant = () => new Set(stack.sizes).size === 1;
208
263
  useIsomorphicEffect.useIsomorphicEffect(() => {
209
264
  const setScaleValues = () => {
@@ -219,8 +274,8 @@ const TearsheetShell = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
219
274
  };
220
275
  };
221
276
  if (modalRef.current) {
222
- Object.entries(setScaleValues()).map(_ref3 => {
223
- let [key, value] = _ref3;
277
+ Object.entries(setScaleValues()).map(_ref4 => {
278
+ let [key, value] = _ref4;
224
279
  modalRef.current.style.setProperty(key, String(value));
225
280
  });
226
281
  }
@@ -247,7 +302,9 @@ const TearsheetShell = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
247
302
  [`${bc}--has-slug`]: deprecated_slug,
248
303
  [`${bc}--has-ai-label`]: !!decorator && decorator['type']?.displayName === 'AILabel',
249
304
  [`${bc}--has-decorator`]: !!decorator && decorator['type']?.displayName !== 'AILabel',
250
- [`${bc}--has-close`]: effectiveHasCloseIcon
305
+ [`${bc}--has-close`]: effectiveHasCloseIcon,
306
+ ['is-visible']: enablePresence,
307
+ [`${bc}--tearsheet-enable-presence`]: presenceContext?.autoEnablePresence
251
308
  }),
252
309
  decorator: decorator || deprecated_slug,
253
310
  containerClassName: cx(`${bc}__container`, {
@@ -259,9 +316,10 @@ const TearsheetShell = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
259
316
  selectorPrimaryFocus,
260
317
  onKeyDown: keyDownListener,
261
318
  preventCloseOnClickOutside: !isPassive,
262
- ref: modalRef,
319
+ ref: mergedRefs,
263
320
  selectorsFloatingMenus: [`.${carbonPrefix}--overflow-menu-options`, `.${carbonPrefix}--tooltip`, '.flatpickr-calendar', `.${bc}__container`, `.${carbonPrefix}--menu`, ...selectorsFloatingMenus],
264
- size: "sm"
321
+ size: "sm",
322
+ "data-tearsheet-exiting": presenceContext?.isExiting || undefined
265
323
  }), includeHeader && /*#__PURE__*/React.createElement(react.ModalHeader, {
266
324
  className: cx(`${bc}__header`, {
267
325
  [`${bc}__header--with-close-icon`]: effectiveHasCloseIcon,
@@ -272,16 +330,17 @@ const TearsheetShell = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
272
330
  }),
273
331
  closeModal: onClose,
274
332
  iconDescription: effectiveHasCloseIcon ? closeIconDescription : undefined
275
- }, /*#__PURE__*/React.createElement(react.Section, {
333
+ }, /*#__PURE__*/React.createElement(Wrap.Wrap, {
276
334
  className: `${bc}__header-content`,
277
335
  element: wide ? react.Layer : undefined
278
336
  }, /*#__PURE__*/React.createElement(Wrap.Wrap, {
279
337
  className: `${bc}__header-fields`
280
338
  }, /*#__PURE__*/React.createElement(Wrap.Wrap, {
281
339
  className: `${bcModalHeader}__label`
282
- }, label), /*#__PURE__*/React.createElement(react.Section, {
340
+ }, label), /*#__PURE__*/React.createElement(Wrap.Wrap, {
341
+ element: "h3",
283
342
  className: cx(`${bcModalHeader}__heading`, `${bc}__heading`)
284
- }, /*#__PURE__*/React.createElement(react.Heading, null, title)), /*#__PURE__*/React.createElement(Wrap.Wrap, {
343
+ }, title), /*#__PURE__*/React.createElement(Wrap.Wrap, {
285
344
  className: `${bc}__header-description`
286
345
  }, description)), /*#__PURE__*/React.createElement(Wrap.Wrap, {
287
346
  className: `${bc}__header-actions`
@@ -6,5 +6,7 @@
6
6
  */
7
7
  export { Tearsheet } from './Tearsheet';
8
8
  export { TearsheetNarrow } from './TearsheetNarrow';
9
+ export { TearsheetPresence, withTearsheetPresence } from './TearsheetPresence';
9
10
  export type { TearsheetProps } from './Tearsheet';
10
11
  export type { TearsheetNarrowProps } from './TearsheetNarrow';
12
+ export type { TearsheetPresenceProps } from './TearsheetPresence';
@@ -0,0 +1,17 @@
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 declare const usePresence: (ref: RefObject<HTMLElement | null>, isOpen: boolean) => {
9
+ /**
10
+ * Indicates whether the ref object is supposed to be mounted
11
+ */
12
+ isPresent: boolean;
13
+ /**
14
+ * Indicates whether the ref object is currently exiting
15
+ */
16
+ isExiting: boolean;
17
+ };
@@ -0,0 +1,71 @@
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 settings = require('../../settings.js');
12
+
13
+ const usePresence = (ref, isOpen) => {
14
+ const [exitState, setExitState] = React.useState(isOpen ? 'idle' : 'finished');
15
+ const isExiting = exitState === 'active';
16
+
17
+ // element is exiting
18
+ if (!isOpen && exitState === 'idle') {
19
+ setExitState('active');
20
+ }
21
+
22
+ // element exit was interrupted
23
+ if (isOpen && exitState !== 'idle') {
24
+ setExitState('idle');
25
+ }
26
+ const handleAnimationEnd = React.useCallback(() => {
27
+ setExitState('finished');
28
+ }, []);
29
+ React.useLayoutEffect(() => {
30
+ if (!ref.current || !isExiting) {
31
+ return;
32
+ }
33
+
34
+ // resolve for JSDOM
35
+ if (!('getAnimations' in ref.current)) {
36
+ handleAnimationEnd();
37
+ return;
38
+ }
39
+
40
+ // cover all animations that start with the presence prefix
41
+ const animations = ref.current.getAnimations({
42
+ subtree: true
43
+ }).filter(animation => animation instanceof CSSAnimation && animation.animationName.startsWith(`${settings.pkg.prefix}`));
44
+ if (!animations.length) {
45
+ handleAnimationEnd();
46
+ return;
47
+ }
48
+ let cancelled = false;
49
+ Promise.all(animations.map(animation => animation.finished)).finally(() => {
50
+ if (cancelled) {
51
+ return;
52
+ }
53
+ handleAnimationEnd();
54
+ });
55
+ return () => {
56
+ cancelled = true;
57
+ };
58
+ }, [ref, isExiting, handleAnimationEnd]);
59
+ return {
60
+ /**
61
+ * Indicates whether the ref object is supposed to be mounted
62
+ */
63
+ isPresent: isOpen || exitState !== 'finished',
64
+ /**
65
+ * Indicates whether the ref object is currently exiting
66
+ */
67
+ isExiting
68
+ };
69
+ };
70
+
71
+ exports.usePresence = usePresence;
@@ -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,52 @@
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 usePresence = require('./usePresence.js');
12
+
13
+ /**
14
+ * Returns if the presence node is present and the context value to be used by a presence context, e.g. ModalPresence.
15
+ */
16
+ const usePresenceContext = (open, initialPresenceId) => {
17
+ const presenceIdRef = React.useRef(initialPresenceId);
18
+ const presenceRef = React.useRef(null);
19
+ const prevPresenceRef = React.useRef(null);
20
+
21
+ // clean up the presence id, if not predefined and if the presence node was unmounted
22
+ if (!initialPresenceId && prevPresenceRef.current && !presenceRef.current) {
23
+ presenceIdRef.current = null;
24
+ }
25
+ prevPresenceRef.current = presenceRef.current;
26
+ const {
27
+ isPresent,
28
+ isExiting
29
+ } = usePresence.usePresence(presenceRef, open);
30
+ const isPresenceExclusive = React.useCallback(id => {
31
+ if (!id) {
32
+ return false;
33
+ }
34
+
35
+ // return false if the presence context is occupied
36
+ if (presenceIdRef.current && presenceIdRef.current !== id) {
37
+ return false;
38
+ }
39
+
40
+ // otherwise occupy presence context and return true
41
+ presenceIdRef.current = id;
42
+ return true;
43
+ }, []);
44
+ const contextValue = React.useMemo(() => ({
45
+ presenceRef,
46
+ isPresenceExclusive,
47
+ isExiting
48
+ }), [presenceRef, isPresenceExclusive, isExiting]);
49
+ return [isPresent, contextValue];
50
+ };
51
+
52
+ exports.usePresenceContext = usePresenceContext;
@@ -0,0 +1 @@
1
+ export function useMergedRefs(refs: any): (node: any) => void;
@@ -0,0 +1,34 @@
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
+
12
+ /**
13
+ * Merges multiple refs into a single callback ref.
14
+ *
15
+ * This hook is useful when you need to attach multiple refs (for example, a ref
16
+ * passed from `forwardRef` and a local ref from `useRef`) to the same node. It
17
+ * accepts an array of refs and returns a callback ref that, when attached to a
18
+ * node, assigns that node to every ref in the array.
19
+ */
20
+ const useMergedRefs = refs => {
21
+ // eslint-disable-next-line react-hooks/exhaustive-deps
22
+ const memoizedRefs = React.useMemo(() => refs, refs);
23
+ return React.useCallback(node => {
24
+ memoizedRefs.forEach(ref => {
25
+ if (typeof ref === 'function') {
26
+ ref(node);
27
+ } else if (ref) {
28
+ ref.current = node;
29
+ }
30
+ });
31
+ }, [memoizedRefs]);
32
+ };
33
+
34
+ exports.useMergedRefs = useMergedRefs;
@@ -24,21 +24,5 @@ function useOverflowStringWidth(elementRef) {
24
24
  }, [checkWidthOverflow, elementRef, innerText]);
25
25
  return isOverflowing;
26
26
  }
27
- const useOverflowStringHeight = elementRef => {
28
- const innerText = elementRef?.current?.innerText;
29
- const [isOverflowing, setIsOverflowing] = React.useState();
30
- const checkHeightOverflow = React.useCallback(() => {
31
- const offsetHeight = elementRef?.current?.offsetHeight;
32
- const scrollHeight = elementRef?.current?.scrollHeight;
33
- if (offsetHeight && scrollHeight) {
34
- setIsOverflowing(offsetHeight < scrollHeight);
35
- }
36
- }, [elementRef]);
37
- React.useEffect(() => {
38
- checkHeightOverflow();
39
- }, [checkHeightOverflow, elementRef, innerText]);
40
- return isOverflowing;
41
- };
42
27
 
43
- exports.useOverflowStringHeight = useOverflowStringHeight;
44
28
  exports.useOverflowStringWidth = useOverflowStringWidth;
package/lib/index.js CHANGED
@@ -51,6 +51,7 @@ var TagOverflow = require('./components/TagOverflow/TagOverflow.js');
51
51
  var TagSet = require('./components/TagSet/TagSet.js');
52
52
  var Tearsheet$1 = require('./components/Tearsheet/Tearsheet.js');
53
53
  var TearsheetNarrow = require('./components/Tearsheet/TearsheetNarrow.js');
54
+ var TearsheetPresence = require('./components/Tearsheet/TearsheetPresence.js');
54
55
  var WebTerminal = require('./components/WebTerminal/WebTerminal.js');
55
56
  var WebTerminalContentWrapper = require('./components/WebTerminal/WebTerminalContentWrapper.js');
56
57
  var index$2 = require('./components/WebTerminal/hooks/index.js');
@@ -195,6 +196,8 @@ exports.TagOverflow = TagOverflow.TagOverflow;
195
196
  exports.TagSet = TagSet.TagSet;
196
197
  exports.Tearsheet = Tearsheet$1.Tearsheet;
197
198
  exports.TearsheetNarrow = TearsheetNarrow.TearsheetNarrow;
199
+ exports.TearsheetPresence = TearsheetPresence.TearsheetPresence;
200
+ exports.withTearsheetPresence = TearsheetPresence.withTearsheetPresence;
198
201
  exports.WebTerminal = WebTerminal.WebTerminal;
199
202
  exports.WebTerminalContentWrapper = WebTerminalContentWrapper.WebTerminalContentWrapper;
200
203
  exports.WebTerminalProvider = index$2.WebTerminalProvider;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@carbon/ibm-products",
3
3
  "description": "Carbon for IBM Products",
4
- "version": "2.83.0",
4
+ "version": "2.84.0",
5
5
  "license": "Apache-2.0",
6
6
  "main": "lib/index.js",
7
7
  "module": "es/index.js",
@@ -67,23 +67,24 @@
67
67
  "@babel/preset-env": "^7.26.9",
68
68
  "@babel/preset-react": "^7.26.3",
69
69
  "@babel/preset-typescript": "^7.26.0",
70
- "@carbon/styles": "^1.96.0",
70
+ "@carbon/styles": "^1.100.0",
71
71
  "@figma/code-connect": "^1.3.5",
72
72
  "@ibm/telemetry-js-config-generator": "^2.0.1",
73
73
  "@percy/cli": "^1.31.0",
74
- "@percy/storybook": "^9.0.0",
74
+ "@percy/storybook": "^10.0.0",
75
75
  "@rollup/plugin-babel": "^6.0.4",
76
76
  "@rollup/plugin-commonjs": "^29.0.0",
77
77
  "@rollup/plugin-node-resolve": "^16.0.1",
78
78
  "@rollup/plugin-typescript": "^12.1.2",
79
- "@storybook/addon-a11y": "^9.1.8",
80
- "@storybook/addon-docs": "^9.1.8",
81
- "@storybook/addon-links": "^9.1.8",
79
+ "@storybook/addon-a11y": "^9.1.15",
80
+ "@storybook/addon-docs": "^9.1.15",
81
+ "@storybook/addon-links": "^9.1.15",
82
82
  "@storybook/csf": "^0.1.13",
83
- "@storybook/react-vite": "^9.1.8",
83
+ "@storybook/react-vite": "^9.1.15",
84
84
  "@types/react-table": "^7.7.20",
85
+ "@vitejs/plugin-react": "^5.0.0",
85
86
  "babel-plugin-dev-expression": "^0.2.3",
86
- "babel-preset-ibm-cloud-cognitive": "^0.41.0",
87
+ "babel-preset-ibm-cloud-cognitive": "^0.42.0",
87
88
  "change-case": "5.4.4",
88
89
  "classnames": "^2.5.1",
89
90
  "copyfiles": "^2.4.1",
@@ -91,7 +92,7 @@
91
92
  "fs-extra": "^11.3.0",
92
93
  "glob": "^11.0.1",
93
94
  "jest": "^29.7.0",
94
- "jest-config-ibm-cloud-cognitive": "^1.42.0",
95
+ "jest-config-ibm-cloud-cognitive": "^1.43.0",
95
96
  "jest-environment-jsdom": "^29.7.0",
96
97
  "namor": "^1.1.2",
97
98
  "npm-run-all2": "^8.0.0",
@@ -102,7 +103,7 @@
102
103
  "rollup-plugin-preserve-directives": "^0.4.0",
103
104
  "rollup-plugin-strip-banner": "^3.1.0",
104
105
  "sass": "^1.93.2",
105
- "storybook": "^9.1.8",
106
+ "storybook": "^9.1.15",
106
107
  "storybook-addon-accessibility-checker": ">=9.2.0-rc.0",
107
108
  "typescript-config-carbon": "^0.9.0",
108
109
  "vite": "^7.0.0",
@@ -111,11 +112,11 @@
111
112
  "dependencies": {
112
113
  "@babel/runtime": "^7.26.10",
113
114
  "@carbon-labs/react-resizer": "^0.10.0",
114
- "@carbon/feature-flags": "^0.32.0",
115
- "@carbon/ibm-products-styles": "^2.79.0",
115
+ "@carbon/feature-flags": "^1.0.0",
116
+ "@carbon/ibm-products-styles": "^2.80.0",
116
117
  "@carbon/telemetry": "^0.1.0",
117
- "@carbon/utilities": "^0.13.0",
118
- "@carbon/utilities-react": "0.16.0",
118
+ "@carbon/utilities": "^0.16.0",
119
+ "@carbon/utilities-react": "0.19.0",
119
120
  "@dnd-kit/core": "^6.3.1",
120
121
  "@dnd-kit/modifiers": "^9.0.0",
121
122
  "@dnd-kit/sortable": "^10.0.0",
@@ -125,14 +126,14 @@
125
126
  "react-window": "^1.8.11"
126
127
  },
127
128
  "peerDependencies": {
128
- "@carbon/grid": "^11.46.0",
129
- "@carbon/layout": "^11.44.0",
130
- "@carbon/motion": "^11.38.0",
131
- "@carbon/react": "^1.97.0",
132
- "@carbon/themes": "^11.64.0",
133
- "@carbon/type": "^11.50.0",
129
+ "@carbon/grid": "^11.50.0",
130
+ "@carbon/layout": "^11.48.0",
131
+ "@carbon/motion": "^11.41.0",
132
+ "@carbon/react": "^1.101.0",
133
+ "@carbon/themes": "^11.68.0",
134
+ "@carbon/type": "^11.54.0",
134
135
  "react": "^16.8.6 || ^17.0.1 || ^18.2.0 || ^19.0.0",
135
136
  "react-dom": "^16.8.6 || ^17.0.1 || ^18.2.0 || ^19.0.0"
136
137
  },
137
- "gitHead": "30bb896c821dbc206f87874b66b97e3121efe15a"
138
+ "gitHead": "7b0c5737d123755ee2ed937568fcd568e9c76b3f"
138
139
  }
@@ -1,5 +1,5 @@
1
1
  //
2
- // Copyright IBM Corp. 2021, 2022
2
+ // Copyright IBM Corp. 2021, 2025
3
3
  //
4
4
  // This source code is licensed under the Apache-2.0 license found in the
5
5
  // LICENSE file in the root directory of this source tree.
@@ -10,19 +10,21 @@
10
10
  @use '@carbon/styles/scss/components/button/tokens' as *;
11
11
  @use '@carbon/styles/scss/spacing' as *;
12
12
  @use '@carbon/styles/scss/type';
13
+ @use '@carbon/styles/scss/config' as carbon-config;
14
+
13
15
  @use '../../global/styles/project-settings' as c4p-settings;
14
16
 
15
17
  $block-class: #{c4p-settings.$pkg-prefix}--apikey-modal;
16
18
 
17
- .#{$block-class} .#{c4p-settings.$carbon-prefix}--modal-close {
19
+ .#{$block-class} .#{carbon-config.$prefix}--modal-close {
18
20
  display: none;
19
21
  }
20
22
 
21
- .#{$block-class} .#{c4p-settings.$carbon-prefix}--inline-loading {
23
+ .#{$block-class} .#{carbon-config.$prefix}--inline-loading {
22
24
  min-block-size: 3rem; // increasing the height from 2 to 3 resolves an issue where the scroll bar bounces
23
25
  }
24
26
 
25
- .#{$block-class} .#{c4p-settings.$carbon-prefix}--modal-content {
27
+ .#{$block-class} .#{carbon-config.$prefix}--modal-content {
26
28
  padding-inline-end: $spacing-05;
27
29
  }
28
30
 
@@ -1,5 +1,5 @@
1
1
  //
2
- // Copyright IBM Corp. 2020, 2022
2
+ // Copyright IBM Corp. 2020, 2025
3
3
  //
4
4
  // This source code is licensed under the Apache-2.0 license found in the
5
5
  // LICENSE file in the root directory of this source tree.
@@ -9,6 +9,7 @@
9
9
  @use '@carbon/styles/scss/spacing' as *;
10
10
  @use '@carbon/styles/scss/type';
11
11
  @use '@carbon/type/scss/font-family' as *;
12
+ @use '@carbon/styles/scss/config' as carbon-config;
12
13
 
13
14
  // Standard imports.
14
15
  @use '../../global/styles/project-settings' as c4p-settings;
@@ -16,7 +17,7 @@
16
17
  // The block part of our conventional BEM class names (blockClass__E--M).
17
18
  $block-class: #{c4p-settings.$pkg-prefix}--about-modal;
18
19
 
19
- .#{$block-class} .#{c4p-settings.$carbon-prefix}--modal-container {
20
+ .#{$block-class} .#{carbon-config.$prefix}--modal-container {
20
21
  grid-template-rows: auto auto 1fr auto;
21
22
  }
22
23
 
@@ -47,14 +48,13 @@ $block-class: #{c4p-settings.$pkg-prefix}--about-modal;
47
48
  padding-block-start: 0;
48
49
  padding-inline: $spacing-05 20%;
49
50
 
50
- &:not(.#{c4p-settings.$carbon-prefix}--modal-scroll-content) {
51
+ &:not(.#{carbon-config.$prefix}--modal-scroll-content) {
51
52
  margin-block-end: $spacing-06;
52
53
  padding-block-end: 0;
53
54
  }
54
55
  }
55
56
 
56
- .#{$block-class}
57
- .#{c4p-settings.$carbon-prefix}--modal-content--overflow-indicator {
57
+ .#{$block-class} .#{carbon-config.$prefix}--modal-content--overflow-indicator {
58
58
  background-image: linear-gradient(to bottom, #00000000, $layer-01);
59
59
  inset-block-end: #{$spacing-06};
60
60
  }
@@ -41,11 +41,13 @@ $block-class-overflow-items: #{$_block-class}-overflow-items;
41
41
 
42
42
  .#{$block-class-overflow-items}__item-content {
43
43
  display: flex;
44
+ align-items: center;
44
45
  justify-content: space-between;
45
46
  inline-size: 100%;
46
47
  }
47
48
 
48
49
  .#{$block-class-overflow-items}__item svg {
50
+ flex-shrink: 0;
49
51
  margin: 0 $spacing-02;
50
52
  }
51
53
  }