@carbon/ibm-products 2.85.0 → 2.86.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 (74) hide show
  1. package/css/carbon.css +55 -2
  2. package/css/carbon.css.map +1 -1
  3. package/css/index-full-carbon.css +615 -410
  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 +3225 -3081
  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 +2958 -2806
  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 +2202 -2011
  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/ActionSet/ActionSet.d.ts +6 -0
  20. package/es/components/ActionSet/ActionSet.js +20 -10
  21. package/es/components/Coachmark/next/Coachmark/ContentBody.js +1 -1
  22. package/es/components/DataSpreadsheet/utils/moveColumnIndicatorLine.js +2 -2
  23. package/es/components/EditInPlace/EditInPlace.d.ts +2 -3
  24. package/es/components/OptionsTile/OptionsTile.js +35 -12
  25. package/es/components/PageHeader/next/context.js +1 -1
  26. package/es/components/PageHeader/next/index.js +3 -3
  27. package/es/components/StringFormatter/StringFormatter.js +1 -1
  28. package/es/components/TagSet/TagSet.js +1 -1
  29. package/es/components/Tearsheet/next/StackContext.d.ts +1 -1
  30. package/es/components/Tearsheet/next/Tearsheet.d.ts +19 -5
  31. package/es/components/Tearsheet/next/Tearsheet.js +90 -31
  32. package/es/components/Tearsheet/next/TearsheetBody.js +2 -2
  33. package/es/components/Tearsheet/next/TearsheetFooter.d.ts +31 -0
  34. package/es/components/Tearsheet/next/TearsheetFooter.js +39 -0
  35. package/es/components/Tearsheet/next/TearsheetHeader.d.ts +1 -1
  36. package/es/components/Tearsheet/next/index.d.ts +2 -1
  37. package/es/components/index.d.ts +1 -1
  38. package/es/global/js/hooks/index.d.ts +1 -0
  39. package/es/global/js/utils/devtools.js +1 -1
  40. package/es/index.js +8 -8
  41. package/es/node_modules/@carbon/icons-react/es/generated/bucket-10.js +1184 -1110
  42. package/es/node_modules/@carbon/icons-react/es/generated/bucket-3.js +1337 -1334
  43. package/es/node_modules/@floating-ui/dom/dist/floating-ui.dom.js +2 -2
  44. package/lib/components/ActionSet/ActionSet.d.ts +6 -0
  45. package/lib/components/ActionSet/ActionSet.js +20 -10
  46. package/lib/components/Coachmark/next/Coachmark/ContentBody.js +0 -3
  47. package/lib/components/DataSpreadsheet/utils/moveColumnIndicatorLine.js +2 -2
  48. package/lib/components/EditInPlace/EditInPlace.d.ts +2 -3
  49. package/lib/components/OptionsTile/OptionsTile.js +35 -12
  50. package/lib/components/PageHeader/next/index.js +6 -6
  51. package/lib/components/TagSet/TagSet.js +0 -3
  52. package/lib/components/Tearsheet/next/StackContext.d.ts +1 -1
  53. package/lib/components/Tearsheet/next/Tearsheet.d.ts +19 -5
  54. package/lib/components/Tearsheet/next/Tearsheet.js +90 -31
  55. package/lib/components/Tearsheet/next/TearsheetBody.js +2 -2
  56. package/lib/components/Tearsheet/next/TearsheetFooter.d.ts +31 -0
  57. package/lib/components/Tearsheet/next/TearsheetFooter.js +43 -0
  58. package/lib/components/Tearsheet/next/TearsheetHeader.d.ts +1 -1
  59. package/lib/components/Tearsheet/next/index.d.ts +2 -1
  60. package/lib/components/index.d.ts +1 -1
  61. package/lib/global/js/hooks/index.d.ts +1 -0
  62. package/lib/index.js +50 -50
  63. package/lib/node_modules/@carbon/icons-react/es/generated/bucket-10.js +1204 -1130
  64. package/lib/node_modules/@carbon/icons-react/es/generated/bucket-3.js +1351 -1348
  65. package/package.json +14 -18
  66. package/scss/components/NotificationsPanel/_notifications-panel.scss +3 -0
  67. package/scss/components/OptionsTile/_options-tile.scss +28 -7
  68. package/scss/components/PageHeader/_page-header.scss +14 -4
  69. package/scss/components/SidePanel/_side-panel.scss +0 -2
  70. package/scss/components/Tearsheet/_index-with-carbon.scss +2 -1
  71. package/scss/components/Tearsheet/_index.scss +1 -0
  72. package/scss/components/Tearsheet/_tearsheet.scss +0 -2
  73. package/scss/components/Tearsheet/_tearsheet_next.scss +351 -229
  74. package/telemetry.yml +4 -1
@@ -5,8 +5,8 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import { offset as offset$1, flip as flip$1, shift as shift$1, arrow as arrow$1, computePosition as computePosition$1 } from '../../core/dist/floating-ui.core.js';
9
- import { createCoords, rectToClientRect, floor, max, round, min } from '../../utils/dist/floating-ui.utils.js';
8
+ import { offset as offset$1, flip as flip$1, shift as shift$1, computePosition as computePosition$1, arrow as arrow$1 } from '../../core/dist/floating-ui.core.js';
9
+ import { createCoords, rectToClientRect, floor, round, max, min } from '../../utils/dist/floating-ui.utils.js';
10
10
  import { getOverflowAncestors, isElement, getDocumentElement, getWindow, getFrameElement, getComputedStyle, isHTMLElement, isWebKit, isTopLayer, getParentNode, isLastTraversableNode, isTableElement, isContainingBlock, getContainingBlock, getNodeName, isOverflowElement, getNodeScroll } from '../../utils/dist/floating-ui.utils.dom.js';
11
11
 
12
12
  function getCssDimensions(element) {
@@ -27,6 +27,12 @@ export interface ActionSetProps {
27
27
  * An optional class or classes to be added to the outermost element.
28
28
  */
29
29
  className?: string;
30
+ /**
31
+ * When true, prevents automatic stacking of buttons even when size would
32
+ * normally trigger stacking (e.g., 'sm' size or 'md' with 3+ actions).
33
+ * Buttons will remain in a horizontal layout.
34
+ */
35
+ disableStacking?: boolean;
30
36
  /**
31
37
  * The size of the action set. Different button arrangements are used at
32
38
  * different sizes, to make best use of the available space.
@@ -46,7 +46,7 @@ ActionSetButton.displayName = 'ActionSetButton';
46
46
  ActionSetButton.propTypes = {
47
47
  /**@ts-ignore*/
48
48
  ...react.Button.PropTypes,
49
- kind: index.default.oneOf(['ghost', 'danger--ghost', 'secondary', 'danger', 'primary']),
49
+ kind: index.default.oneOf(['ghost', 'danger--ghost', 'tertiary', 'secondary', 'danger', 'primary']),
50
50
  label: index.default.string,
51
51
  loading: index.default.bool
52
52
  };
@@ -68,6 +68,7 @@ const validateActionSetProps = _ref2 => {
68
68
  const countActions = kind => actions.filter(action => (action.kind || defaultKind) === kind).length;
69
69
  const primaryActions = countActions('primary');
70
70
  const secondaryActions = countActions('secondary');
71
+ const tertiaryActions = countActions('tertiary');
71
72
  const dangerActions = countActions('danger');
72
73
  const ghostActions = countActions('ghost') + countActions('danger--ghost');
73
74
  if (stacking && actions.length > 3) {
@@ -85,8 +86,8 @@ const validateActionSetProps = _ref2 => {
85
86
  if (stacking && actions.length > 1 && ghostActions > 0) {
86
87
  problems.push(`you cannot have a 'ghost' button in conjunction with other action types in this size of ${componentName}`);
87
88
  }
88
- if (actions.length > primaryActions + secondaryActions + dangerActions + ghostActions) {
89
- problems.push(`you can only have 'primary', 'danger', 'secondary', 'ghost' and 'danger--ghost' buttons in a ${componentName}`);
89
+ if (actions.length > primaryActions + secondaryActions + tertiaryActions + dangerActions + ghostActions) {
90
+ problems.push(`you can only have 'primary', 'danger', 'secondary', 'tertiary', 'ghost' and 'danger--ghost' buttons in a ${componentName}`);
90
91
  }
91
92
  return problems.length > 0 ? pconsole.default.error(`Invalid prop \`actions\` supplied to \`${componentName}\`: ${problems.join(', and ')}.`) : null;
92
93
  }
@@ -109,6 +110,7 @@ const ActionSet = /*#__PURE__*/React.forwardRef((props, ref) => {
109
110
  actions,
110
111
  buttonSize,
111
112
  className,
113
+ disableStacking = false,
112
114
  size = defaults.size,
113
115
  ...rest
114
116
  } = props;
@@ -119,16 +121,18 @@ const ActionSet = /*#__PURE__*/React.forwardRef((props, ref) => {
119
121
  const buttons = actions && actions.slice?.(0) || [];
120
122
 
121
123
  // We stack the buttons in a sm set, or if there are three or more in a md set.
122
- const stacking = willStack(size, buttons.length);
124
+ // Unless disableStacking is true, in which case we never stack.
125
+ const stacking = disableStacking ? false : willStack(size, buttons.length);
123
126
 
124
- // Order of button kinds: ghost first, then danger--ghost, then most other types,
125
- // then danger, and finally primary
127
+ // Order of button kinds: ghost first, then danger--ghost, then tertiary,
128
+ // then most other types, then danger, and finally primary
126
129
  const buttonOrder = kind => ({
127
130
  ghost: 1,
128
131
  'danger--ghost': 2,
129
- danger: 4,
130
- primary: 5
131
- })[kind] ?? 3;
132
+ tertiary: 3,
133
+ danger: 5,
134
+ primary: 6
135
+ })[kind] ?? 4;
132
136
 
133
137
  // order the actions with ghost/ghost-danger buttons first and primary/danger buttons last
134
138
  // (or the opposite way if we're stacking)
@@ -185,7 +189,7 @@ ActionSet.propTypes = {
185
189
  actions: propsHelper.allPropTypes([index.default.arrayOf(index.default.shape({
186
190
  /**@ts-ignore*/
187
191
  ...react.Button.propTypes,
188
- kind: index.default.oneOf(['ghost', 'danger--ghost', 'secondary', 'danger', 'primary']),
192
+ kind: index.default.oneOf(['ghost', 'danger--ghost', 'tertiary', 'secondary', 'danger', 'primary']),
189
193
  label: index.default.string,
190
194
  loading: index.default.bool,
191
195
  // we duplicate this Button prop to improve the DocGen here
@@ -204,6 +208,12 @@ ActionSet.propTypes = {
204
208
  * An optional class or classes to be added to the outermost element.
205
209
  */
206
210
  className: index.default.string,
211
+ /**
212
+ * When true, prevents automatic stacking of buttons even when size would
213
+ * normally trigger stacking (e.g., 'sm' size or 'md' with 3+ actions).
214
+ * Buttons will remain in a horizontal layout.
215
+ */
216
+ disableStacking: index.default.bool,
207
217
  /**
208
218
  * The size of the action set. Different button arrangements are used at
209
219
  * different sizes, to make best use of the available space.
@@ -7,8 +7,6 @@
7
7
 
8
8
  'use strict';
9
9
 
10
- Object.defineProperty(exports, '__esModule', { value: true });
11
-
12
10
  var React = require('react');
13
11
  var index = require('../../../../_virtual/index.js');
14
12
  var cx = require('classnames');
@@ -39,4 +37,3 @@ ContentBody.propTypes = {
39
37
  };
40
38
 
41
39
  exports.ContentBody = ContentBody;
42
- exports.default = ContentBody;
@@ -40,7 +40,7 @@ const moveColumnIndicatorLine = _ref => {
40
40
 
41
41
  // Is near left side of viewport
42
42
  if (clientX < leftEdgeThreshold) {
43
- window.scrollBy(-10, 0);
43
+ window.scrollBy(-scrollSpeed, 0);
44
44
  }
45
45
 
46
46
  // Is near right side of viewport
@@ -50,7 +50,7 @@ const moveColumnIndicatorLine = _ref => {
50
50
 
51
51
  // Is near left edge of table
52
52
  if (clientX > left && clientX < left + leftEdgeThreshold) {
53
- listContainer.scrollBy(-10, 0);
53
+ listContainer.scrollBy(-scrollSpeed, 0);
54
54
  }
55
55
 
56
56
  // Is near right edge of table
@@ -5,7 +5,6 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
  import React, { PropsWithChildren } from 'react';
8
- import PropTypes from 'prop-types';
9
8
  type Size = 'sm' | 'md' | 'lg';
10
9
  type AlignPropType = 'top' | 'top-left' | 'top-right' | 'bottom' | 'bottom-left' | 'bottom-right' | 'left' | 'right';
11
10
  type Shape = {
@@ -69,11 +68,11 @@ export interface EditInplaceProps extends PropsWithChildren {
69
68
  /**
70
69
  * determines if the input is in readOnly mode
71
70
  */
72
- readOnly: PropTypes.bool;
71
+ readOnly?: boolean;
73
72
  /**
74
73
  * label for the edit off button that displays when in read only mode
75
74
  */
76
- readOnlyLabel?: PropTypes.string;
75
+ readOnlyLabel?: string;
77
76
  /**
78
77
  * text for the toggletip that displays when in read only mode
79
78
  */
@@ -142,7 +142,19 @@ const OptionsTile = /*#__PURE__*/React.forwardRef((props, ref) => {
142
142
  setOpen(false);
143
143
  }
144
144
  };
145
- const toggle = evt => {
145
+ const handleSummaryClick = evt => {
146
+ // Check if the click originated from the toggle button
147
+ const target = evt.target;
148
+ const toggleContainer = target.closest(`.${blockClass}__toggle-container`);
149
+
150
+ // If click is on toggle button, don't handle expand/collapse
151
+ if (toggleContainer) {
152
+ evt.preventDefault();
153
+ evt.stopPropagation();
154
+ return;
155
+ }
156
+
157
+ // Prevent default details toggle behavior
146
158
  evt.preventDefault();
147
159
  if (open) {
148
160
  collapse();
@@ -195,8 +207,25 @@ const OptionsTile = /*#__PURE__*/React.forwardRef((props, ref) => {
195
207
  [`${blockClass}--closing`]: closing
196
208
  }),
197
209
  ref: ref
198
- }, devtools.getDevtoolsProps(componentName)), enabled !== undefined && /*#__PURE__*/React.createElement("div", {
199
- className: `${blockClass}__toggle-container`
210
+ }, devtools.getDevtoolsProps(componentName)), isExpandable ? /*#__PURE__*/React.createElement("details", {
211
+ className: `${blockClass}__details`,
212
+ open: open,
213
+ ref: detailsRef
214
+ }, /*#__PURE__*/React.createElement("summary", {
215
+ className: cx(`${blockClass}__header`, {
216
+ [`${blockClass}__header--has-toggle`]: enabled !== undefined
217
+ }),
218
+ onClick: handleSummaryClick,
219
+ "data-testid": "options-tile-header"
220
+ }, enabled !== undefined &&
221
+ /*#__PURE__*/
222
+ // eslint-disable-next-line jsx-a11y/no-static-element-interactions
223
+ React.createElement("div", {
224
+ className: `${blockClass}__toggle-container`,
225
+ "data-testid": "options-tile-toggle-container",
226
+ onMouseDown: evt => {
227
+ evt.preventDefault();
228
+ }
200
229
  }, /*#__PURE__*/React.createElement(react.Toggle, {
201
230
  id: `${titleId}-toggle`,
202
231
  className: `${blockClass}__toggle`,
@@ -206,14 +235,7 @@ const OptionsTile = /*#__PURE__*/React.forwardRef((props, ref) => {
206
235
  onToggle: onToggle,
207
236
  size: "sm",
208
237
  disabled: isLocked
209
- })), isExpandable ? /*#__PURE__*/React.createElement("details", {
210
- className: `${blockClass}__details`,
211
- open: open,
212
- ref: detailsRef
213
- }, /*#__PURE__*/React.createElement("summary", {
214
- className: `${blockClass}__header`,
215
- onClick: toggle
216
- }, /*#__PURE__*/React.createElement(icons.ChevronDown, {
238
+ })), /*#__PURE__*/React.createElement(icons.ChevronDown, {
217
239
  size: 16,
218
240
  className: cx(`${blockClass}__chevron`, {
219
241
  [`${blockClass}__chevron--open`]: open,
@@ -221,7 +243,8 @@ const OptionsTile = /*#__PURE__*/React.forwardRef((props, ref) => {
221
243
  })
222
244
  }), renderTitle()), /*#__PURE__*/React.createElement("div", {
223
245
  className: `${blockClass}__content`,
224
- ref: contentRef
246
+ ref: contentRef,
247
+ "data-testid": "options-tile-content"
225
248
  }, /*#__PURE__*/React.createElement(react.Layer, null, isLocked && /*#__PURE__*/React.createElement("p", {
226
249
  className: `${blockClass}__locked-text`
227
250
  }, _Locked || (_Locked = /*#__PURE__*/React.createElement(icons.Locked, {
@@ -9,15 +9,15 @@
9
9
 
10
10
  var PageHeader = require('./PageHeader.js');
11
11
  var PageHeaderBreadcrumbBar = require('./PageHeaderBreadcrumbBar.js');
12
+ var PageHeaderBreadcrumbOverflow = require('./PageHeaderBreadcrumbOverflow.js');
12
13
  var PageHeaderContent = require('./PageHeaderContent.js');
13
14
  var PageHeaderContentPageActions = require('./PageHeaderContentPageActions.js');
14
15
  var PageHeaderContentText = require('./PageHeaderContentText.js');
15
- var PageHeaderTabBar = require('./PageHeaderTabBar.js');
16
16
  var PageHeaderHeroImage = require('./PageHeaderHeroImage.js');
17
17
  var PageHeaderScrollButton = require('./PageHeaderScrollButton.js');
18
- var PageHeaderTitleBreadcrumb = require('./PageHeaderTitleBreadcrumb.js');
19
- var PageHeaderBreadcrumbOverflow = require('./PageHeaderBreadcrumbOverflow.js');
18
+ var PageHeaderTabBar = require('./PageHeaderTabBar.js');
20
19
  var PageHeaderTagOverflow = require('./PageHeaderTagOverflow.js');
20
+ var PageHeaderTitleBreadcrumb = require('./PageHeaderTitleBreadcrumb.js');
21
21
 
22
22
 
23
23
 
@@ -34,12 +34,12 @@ exports.TabBar = PageHeader.TabBar;
34
34
  exports.TagOverflow = PageHeader.TagOverflow;
35
35
  exports.TitleBreadcrumb = PageHeader.TitleBreadcrumb;
36
36
  exports.PageHeaderBreadcrumbBar = PageHeaderBreadcrumbBar.PageHeaderBreadcrumbBar;
37
+ exports.PageHeaderBreadcrumbOverflow = PageHeaderBreadcrumbOverflow.PageHeaderBreadcrumbOverflow;
37
38
  exports.PageHeaderContent = PageHeaderContent.PageHeaderContent;
38
39
  exports.PageHeaderContentPageActions = PageHeaderContentPageActions.PageHeaderContentPageActions;
39
40
  exports.PageHeaderContentText = PageHeaderContentText.PageHeaderContentText;
40
- exports.PageHeaderTabBar = PageHeaderTabBar.PageHeaderTabBar;
41
41
  exports.PageHeaderHeroImage = PageHeaderHeroImage.PageHeaderHeroImage;
42
42
  exports.PageHeaderScrollButton = PageHeaderScrollButton.PageHeaderScrollButton;
43
- exports.PageHeaderTitleBreadcrumb = PageHeaderTitleBreadcrumb.PageHeaderTitleBreadcrumb;
44
- exports.PageHeaderBreadcrumbOverflow = PageHeaderBreadcrumbOverflow.PageHeaderBreadcrumbOverflow;
43
+ exports.PageHeaderTabBar = PageHeaderTabBar.PageHeaderTabBar;
45
44
  exports.PageHeaderTagOverflow = PageHeaderTagOverflow.PageHeaderTagOverflow;
45
+ exports.PageHeaderTitleBreadcrumb = PageHeaderTitleBreadcrumb.PageHeaderTitleBreadcrumb;
@@ -7,8 +7,6 @@
7
7
 
8
8
  'use strict';
9
9
 
10
- Object.defineProperty(exports, '__esModule', { value: true });
11
-
12
10
  var _rollupPluginBabelHelpers = require('../../_virtual/_rollupPluginBabelHelpers.js');
13
11
  var React = require('react');
14
12
  var index = require('../../_virtual/index.js');
@@ -357,4 +355,3 @@ TagSet.propTypes = {
357
355
  TagSet.displayName = componentName;
358
356
 
359
357
  exports.TagSet = TagSet;
360
- exports.default = TagSet;
@@ -7,7 +7,7 @@
7
7
  import React, { ReactNode } from 'react';
8
8
  export interface StackContextType {
9
9
  stack: string[];
10
- notifyStack: (id: string, open: boolean, container: HTMLDivElement) => void;
10
+ notifyStack: (id: string, open: boolean, container: HTMLDivElement | null) => void;
11
11
  getScaleFactor: (id: string) => number | null;
12
12
  getBlockSizeChange: (id: string) => string | null;
13
13
  getDepth: (id: string) => number | null;
@@ -11,6 +11,7 @@ import { TearsheetHeaderProps, TearsheetNavigationBarProps, TearsheetScrollButto
11
11
  import { TearsheetHeaderContentProps } from './TearsheetHeaderContent';
12
12
  import { InfluencerProps, MainContentProps, SummaryContentProps, TearsheetBodyProps } from './TearsheetBody';
13
13
  import { TearsheetHeaderActionItemProps, TearsheetHeaderActionsProps } from './TearsheetHeaderActions';
14
+ import { TearsheetFooterProps } from './TearsheetFooter';
14
15
  /**
15
16
  * ----------
16
17
  * Tearsheet
@@ -75,6 +76,19 @@ export interface TearsheetProps extends ComposedModalProps {
75
76
  * The DOM element that the tearsheet should be rendered within. Defaults to document.body.
76
77
  */
77
78
  portalTarget?: HTMLElement;
79
+ /**
80
+ * Disable the portal behavior and render the tearsheet in the existing DOM structure.
81
+ * This is useful for testing, when you need to inherit React context from parent components,
82
+ * or when you don't need the z-index isolation that portals provide.
83
+ * @default false
84
+ */
85
+ disablePortal?: boolean;
86
+ /**
87
+ * If true, the tearsheet will remain mounted in the DOM when closed, using CSS to hide it.
88
+ * By default (false), the tearsheet unmounts from the DOM after the exit animation completes.
89
+ * Set to true if you need to preserve component state or avoid remounting overhead.
90
+ */
91
+ keepMounted?: boolean;
78
92
  }
79
93
  export type TearsheetComponentType = React.ForwardRefExoticComponent<TearsheetProps & React.RefAttributes<HTMLDivElement>> & {
80
94
  Header: FC<TearsheetHeaderProps>;
@@ -87,10 +101,10 @@ export type TearsheetComponentType = React.ForwardRefExoticComponent<TearsheetPr
87
101
  MainContent: FC<MainContentProps>;
88
102
  SummaryContent: FC<SummaryContentProps>;
89
103
  Body: FC<TearsheetBodyProps>;
90
- Footer: FC<FooterProps>;
104
+ Footer: FC<TearsheetFooterProps>;
91
105
  };
106
+ /**
107
+ * Wrapper component that handles presence logic and conditionally renders TearsheetInternal.
108
+ * This ensures that all component state and effects are only initialized when the tearsheet is present.
109
+ */
92
110
  export declare const Tearsheet: TearsheetComponentType;
93
- export interface FooterProps {
94
- children: ReactNode;
95
- className?: string;
96
- }
@@ -9,6 +9,7 @@
9
9
 
10
10
  var _rollupPluginBabelHelpers = require('../../../_virtual/_rollupPluginBabelHelpers.js');
11
11
  var React = require('react');
12
+ var ReactDOM = require('react-dom');
12
13
  var cx = require('classnames');
13
14
  var react = require('@carbon/react');
14
15
  var context = require('./context.js');
@@ -16,11 +17,13 @@ var TearsheetHeader = require('./TearsheetHeader.js');
16
17
  var TearsheetHeaderContent = require('./TearsheetHeaderContent.js');
17
18
  var TearsheetBody = require('./TearsheetBody.js');
18
19
  var TearsheetHeaderActions = require('./TearsheetHeaderActions.js');
20
+ var TearsheetFooter = require('./TearsheetFooter.js');
19
21
  var layout = require('@carbon/layout');
20
- var usePortalTarget = require('../../../global/js/hooks/usePortalTarget.js');
21
22
  var StackContext = require('./StackContext.js');
22
23
  var useMatchMedia = require('../../../global/js/hooks/useMatchMedia.js');
23
24
  var useId = require('../../../global/js/utils/useId.js');
25
+ var usePresence = require('../usePresence.js');
26
+ var useMergedRefs = require('../../../global/js/hooks/useMergedRefs.js');
24
27
  var useIsomorphicEffect = require('../../../global/js/hooks/useIsomorphicEffect.js');
25
28
 
26
29
  /**
@@ -29,7 +32,11 @@ var useIsomorphicEffect = require('../../../global/js/hooks/useIsomorphicEffect.
29
32
  * ----------
30
33
  */
31
34
 
32
- const Tearsheet = /*#__PURE__*/React.forwardRef((_ref, ref) => {
35
+ /**
36
+ * Internal component that handles the actual tearsheet rendering.
37
+ * This component is always "present" when mounted - the wrapper handles presence logic.
38
+ */
39
+ const TearsheetInternal = /*#__PURE__*/React.forwardRef((_ref, ref) => {
33
40
  let {
34
41
  children,
35
42
  variant = 'wide',
@@ -42,14 +49,18 @@ const Tearsheet = /*#__PURE__*/React.forwardRef((_ref, ref) => {
42
49
  selectorPrimaryFocus,
43
50
  open = false,
44
51
  portalTarget,
52
+ disablePortal = false,
45
53
  verticalGap,
46
54
  containerClassName,
55
+ keepMounted = false,
56
+ isExiting = false,
57
+ presenceRef,
47
58
  ...rest
48
59
  } = _ref;
49
60
  const carbonPrefix = react.usePrefix();
50
- const localRef = React.useRef(undefined);
61
+ const localRef = React.useRef(null);
51
62
  const bodyRef = React.useRef(null);
52
- const modalRef = ref || localRef;
63
+ const mergedRefs = useMergedRefs.useMergedRefs([ref, localRef, presenceRef]);
53
64
  const smMediaQuery = `(max-width: ${layout.breakpoints.md.width})`;
54
65
  const isSm = useMatchMedia.useMatchMedia(smMediaQuery) || variant === 'narrow';
55
66
  const [hasCloseIcon, setHasCloseIcon] = React.useState(true);
@@ -59,7 +70,7 @@ const Tearsheet = /*#__PURE__*/React.forwardRef((_ref, ref) => {
59
70
  const header = arr.find(child => child.type === TearsheetHeader.default);
60
71
  const influencer = arr.find(child => child.type === TearsheetBody.Influencer);
61
72
  const body = arr.find(child => child.type === TearsheetBody.default);
62
- const footer = arr.find(child => child.type === Footer);
73
+ const footer = arr.find(child => child.type === TearsheetFooter.default);
63
74
  const uniqueId = React.useRef(useId.useId());
64
75
  const {
65
76
  notifyStack,
@@ -69,10 +80,17 @@ const Tearsheet = /*#__PURE__*/React.forwardRef((_ref, ref) => {
69
80
  getBlockSizeChange
70
81
  } = StackContext.useStackContext();
71
82
  const [depth, setDepth] = React.useState(0);
72
- const renderPortalUse = usePortalTarget.usePortalTarget(portalTarget);
83
+ const [mountNode, setMountNode] = React.useState(null);
84
+
85
+ // Set portal mount node using useIsomorphicEffect to avoid SSR issues and double rendering
86
+ useIsomorphicEffect.useIsomorphicEffect(() => {
87
+ if (!disablePortal) {
88
+ setMountNode(portalTarget || document.body);
89
+ }
90
+ }, [portalTarget, disablePortal]);
73
91
  useIsomorphicEffect.useIsomorphicEffect(() => {
74
- const AILabelWidth = modalRef.current?.querySelector(`.${carbonPrefix}--ai-label`)?.clientWidth ?? 0;
75
- const headerActionMarginRight = AILabelWidth + 24 + (isSm ? 8 : 0); // 24 is to compeNsate for close button
92
+ const AILabelWidth = localRef.current?.querySelector(`.${carbonPrefix}--ai-label`)?.clientWidth ?? 0;
93
+ const headerActionMarginRight = AILabelWidth + 24 + (isSm ? 8 : 0); // 24 is to compensate for close button
76
94
  document.documentElement.style.setProperty('--tearsheet-header-action-offset', `${headerActionMarginRight}px`);
77
95
  if (influencerWidth) {
78
96
  document.documentElement.style.setProperty('--tearsheet-influencer-width', `${influencerWidth}`);
@@ -87,25 +105,30 @@ const Tearsheet = /*#__PURE__*/React.forwardRef((_ref, ref) => {
87
105
  // eslint-disable-next-line react-hooks/exhaustive-deps
88
106
  }, [isSm, rest.decorator, influencerWidth, summaryContentWidth, verticalGap]);
89
107
  useIsomorphicEffect.useIsomorphicEffect(() => {
90
- if (bodyRef.current) {
91
- notifyStack?.(uniqueId.current, open, bodyRef.current);
108
+ const id = uniqueId.current;
109
+ if (localRef.current && open) {
110
+ notifyStack?.(id, true, localRef.current);
92
111
  }
93
112
 
113
+ // Cleanup when component unmounts
114
+ return () => {
115
+ notifyStack?.(id, false, null);
116
+ };
94
117
  // eslint-disable-next-line react-hooks/exhaustive-deps
95
- }, [open]);
118
+ }, [localRef.current, open]);
96
119
  React.useEffect(() => {
97
- if (stack?.length > 0) {
120
+ if (stack?.length > 0 && localRef.current) {
98
121
  const stackDepth = getDepth?.(uniqueId.current),
99
122
  blockSizeChange = getBlockSizeChange?.(uniqueId.current),
100
123
  scaleFactor = getScaleFactor?.(uniqueId.current);
101
124
  setDepth(stackDepth);
102
- modalRef.current.style.setProperty('--stack-depth', stackDepth + '');
103
- modalRef.current.style.setProperty('--block-size-change', blockSizeChange);
104
- modalRef.current.style.setProperty('--scale-factor', scaleFactor + '');
125
+ localRef.current.style.setProperty('--stack-depth', stackDepth + '');
126
+ localRef.current.style.setProperty('--block-size-change', blockSizeChange);
127
+ localRef.current.style.setProperty('--scale-factor', scaleFactor + '');
105
128
  }
106
129
  // eslint-disable-next-line react-hooks/exhaustive-deps
107
130
  }, [stack]);
108
- return renderPortalUse(/*#__PURE__*/React.createElement(context.TearsheetContext.Provider, {
131
+ const content = /*#__PURE__*/React.createElement(context.TearsheetContext.Provider, {
109
132
  value: {
110
133
  hasCloseIcon,
111
134
  setHasCloseIcon,
@@ -128,29 +151,65 @@ const Tearsheet = /*#__PURE__*/React.forwardRef((_ref, ref) => {
128
151
  [`${context.blockClass}--stack-activated`]: stack.length > 1,
129
152
  [`${context.blockClass}--has-ai-label`]: !!rest.decorator && rest.decorator['type']?.displayName === 'AILabel',
130
153
  [`${context.blockClass}--has-decorator`]: !!rest.decorator && rest.decorator['type']?.displayName !== 'AILabel',
131
- [`${context.blockClass}--has-close`]: hasCloseIcon
154
+ [`${context.blockClass}--has-close`]: hasCloseIcon,
155
+ ['is-visible']: keepMounted ? open : true,
156
+ // When keepMounted, use open prop; otherwise always visible
157
+ [`${context.blockClass}--keep-mounted`]: keepMounted
132
158
  }),
133
159
  containerClassName: cx(`${context.blockClass}__container`, containerClassName),
134
160
  onClose,
135
- open,
161
+ open: keepMounted ? open : true,
162
+ // When keepMounted, use actual open; otherwise always open
136
163
  selectorPrimaryFocus,
137
- ref: modalRef,
164
+ ref: mergedRefs,
138
165
  selectorsFloatingMenus: [`.${carbonPrefix}--overflow-menu-options`, `.${carbonPrefix}--tooltip`, '.flatpickr-calendar', `.${context.blockClass}__container`, `.${carbonPrefix}--menu`, ...selectorsFloatingMenus],
139
166
  isFullWidth: true,
140
- size: variant === 'narrow' ? 'sm' : ''
167
+ size: variant === 'narrow' ? 'sm' : '',
168
+ "data-tearsheet-exiting": isExiting ? true : undefined
141
169
  }), header, /*#__PURE__*/React.createElement(react.ModalBody, {
142
- className: `${context.blockClass}__body-layout`,
170
+ className: cx(`${context.blockClass}__body-layout`, {
171
+ [`${context.blockClass}__body-layout--has-influencer`]: influencer && !isSm
172
+ }),
143
173
  ref: bodyRef
144
- }, influencer, body, footer)))));
174
+ }, influencer, body, footer))));
175
+
176
+ // If portal is disabled, return content directly
177
+ if (disablePortal) {
178
+ return content;
179
+ }
180
+
181
+ // Return portal if mountNode is set, otherwise return content directly (SSR-safe)
182
+ return mountNode ? /*#__PURE__*/ReactDOM.createPortal(content, mountNode) : content;
145
183
  });
146
- const Footer = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
147
- let {
148
- children
149
- } = _ref2;
150
- return /*#__PURE__*/React.createElement("footer", {
151
- className: `${context.blockClass}__footer`,
152
- ref: ref
153
- }, children);
184
+
185
+ /**
186
+ * Wrapper component that handles presence logic and conditionally renders TearsheetInternal.
187
+ * This ensures that all component state and effects are only initialized when the tearsheet is present.
188
+ */
189
+ const Tearsheet = /*#__PURE__*/React.forwardRef((props, ref) => {
190
+ const {
191
+ open = false,
192
+ keepMounted = false
193
+ } = props;
194
+ const presenceRef = React.useRef(null);
195
+
196
+ // Use presence hook for enter/exit animations (unless keepMounted is true)
197
+ const {
198
+ isPresent,
199
+ isExiting
200
+ } = usePresence.usePresence(presenceRef, keepMounted ? true : open);
201
+
202
+ // Don't render if not present (after exit animation completes) - unless keepMounted is true
203
+ if (!keepMounted && !isPresent) {
204
+ return null;
205
+ }
206
+
207
+ // When present, render the internal component with all props
208
+ return /*#__PURE__*/React.createElement(TearsheetInternal, _rollupPluginBabelHelpers.extends({}, props, {
209
+ ref: ref,
210
+ presenceRef: presenceRef,
211
+ isExiting: isExiting
212
+ }));
154
213
  });
155
214
  Tearsheet.Header = TearsheetHeader.default;
156
215
  Tearsheet.HeaderContent = TearsheetHeaderContent.default;
@@ -158,7 +217,7 @@ Tearsheet.Body = TearsheetBody.default;
158
217
  Tearsheet.Influencer = TearsheetBody.Influencer;
159
218
  Tearsheet.MainContent = TearsheetBody.MainContent;
160
219
  Tearsheet.SummaryContent = TearsheetBody.SummaryContent;
161
- Tearsheet.Footer = Footer;
220
+ Tearsheet.Footer = TearsheetFooter.default;
162
221
  Tearsheet.NavigationBar = TearsheetHeader.TearsheetNavigationBar;
163
222
  Tearsheet.ScrollButton = TearsheetHeader.TearsheetScrollButton;
164
223
  Tearsheet.HeaderActions = TearsheetHeaderActions.TearsheetHeaderActions;
@@ -106,7 +106,7 @@ const SummaryContent = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
106
106
  size: "sm",
107
107
  open: summaryPanelOpen,
108
108
  onRequestClose: onSummaryPanelClose,
109
- className: className
109
+ className: cx(`${context.blockClass}__side-panel`, className)
110
110
  }, children);
111
111
  });
112
112
  const Influencer = /*#__PURE__*/React.forwardRef((_ref4, ref) => {
@@ -130,7 +130,7 @@ const Influencer = /*#__PURE__*/React.forwardRef((_ref4, ref) => {
130
130
  open: influencerPanelOpen,
131
131
  onRequestClose: onInfluencerPanelClose,
132
132
  placement: "left",
133
- className: className
133
+ className: cx(`${context.blockClass}__side-panel`, className)
134
134
  }, children);
135
135
  });
136
136
 
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Copyright IBM Corp. 2025, 2025
3
+ *
4
+ * This source code is licensed under the Apache-2.0 license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ import React, { ReactNode } from 'react';
8
+ import { ButtonProps } from '@carbon/react';
9
+ import { ActionSetProps } from '../../ActionSet';
10
+ export interface TearsheetFooterProps {
11
+ /**
12
+ * Optional children to render in the footer. If provided, children are rendered first,
13
+ * followed by the ActionSet (if actions are provided).
14
+ */
15
+ children?: ReactNode;
16
+ /**
17
+ * Optional class name to add to the footer element.
18
+ */
19
+ className?: string;
20
+ /**
21
+ * Optional array of action button configurations. If provided, an ActionSet will be
22
+ * rendered after any children. Each action follows the ActionSet button specification.
23
+ */
24
+ actions?: ButtonProps<React.ElementType>[];
25
+ /**
26
+ * Optional size for the ActionSet buttons. Defaults to the ActionSet's default size.
27
+ */
28
+ buttonSize?: ActionSetProps['buttonSize'];
29
+ }
30
+ declare const TearsheetFooter: React.ForwardRefExoticComponent<TearsheetFooterProps & React.RefAttributes<HTMLDivElement>>;
31
+ export default TearsheetFooter;
@@ -0,0 +1,43 @@
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
+ Object.defineProperty(exports, '__esModule', { value: true });
11
+
12
+ var React = require('react');
13
+ var cx = require('classnames');
14
+ var ActionSet = require('../../ActionSet/ActionSet.js');
15
+ var context = require('./context.js');
16
+
17
+ const TearsheetFooter = /*#__PURE__*/React.forwardRef((_ref, ref) => {
18
+ let {
19
+ children,
20
+ className,
21
+ actions,
22
+ buttonSize
23
+ } = _ref;
24
+ const {
25
+ variant
26
+ } = React.useContext(context.TearsheetContext);
27
+ const actionCount = actions?.length || 0;
28
+ return /*#__PURE__*/React.createElement("footer", {
29
+ className: cx(`${context.blockClass}__footer`, className, {
30
+ [`${context.blockClass}__footer--three-actions`]: actionCount == 3,
31
+ [`${context.blockClass}__footer--many-actions`]: actionCount > 3
32
+ }),
33
+ ref: ref
34
+ }, children, actions && actions.length > 0 && /*#__PURE__*/React.createElement(ActionSet.ActionSet, {
35
+ actions: actions,
36
+ buttonSize: buttonSize,
37
+ disableStacking: true,
38
+ size: variant == 'wide' ? '2xl' : 'lg'
39
+ }));
40
+ });
41
+ TearsheetFooter.displayName = 'TearsheetFooter';
42
+
43
+ exports.default = TearsheetFooter;