@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.
- package/css/carbon.css +55 -2
- package/css/carbon.css.map +1 -1
- package/css/index-full-carbon.css +615 -410
- package/css/index-full-carbon.css.map +1 -1
- package/css/index-full-carbon.min.css +1 -1
- package/css/index-full-carbon.min.css.map +1 -1
- package/css/index-without-carbon-released-only.css +3225 -3081
- package/css/index-without-carbon-released-only.css.map +1 -1
- package/css/index-without-carbon-released-only.min.css +1 -1
- package/css/index-without-carbon-released-only.min.css.map +1 -1
- package/css/index-without-carbon.css +2958 -2806
- package/css/index-without-carbon.css.map +1 -1
- package/css/index-without-carbon.min.css +1 -1
- package/css/index-without-carbon.min.css.map +1 -1
- package/css/index.css +2202 -2011
- package/css/index.css.map +1 -1
- package/css/index.min.css +1 -1
- package/css/index.min.css.map +1 -1
- package/es/components/ActionSet/ActionSet.d.ts +6 -0
- package/es/components/ActionSet/ActionSet.js +20 -10
- package/es/components/Coachmark/next/Coachmark/ContentBody.js +1 -1
- package/es/components/DataSpreadsheet/utils/moveColumnIndicatorLine.js +2 -2
- package/es/components/EditInPlace/EditInPlace.d.ts +2 -3
- package/es/components/OptionsTile/OptionsTile.js +35 -12
- package/es/components/PageHeader/next/context.js +1 -1
- package/es/components/PageHeader/next/index.js +3 -3
- package/es/components/StringFormatter/StringFormatter.js +1 -1
- package/es/components/TagSet/TagSet.js +1 -1
- package/es/components/Tearsheet/next/StackContext.d.ts +1 -1
- package/es/components/Tearsheet/next/Tearsheet.d.ts +19 -5
- package/es/components/Tearsheet/next/Tearsheet.js +90 -31
- package/es/components/Tearsheet/next/TearsheetBody.js +2 -2
- package/es/components/Tearsheet/next/TearsheetFooter.d.ts +31 -0
- package/es/components/Tearsheet/next/TearsheetFooter.js +39 -0
- package/es/components/Tearsheet/next/TearsheetHeader.d.ts +1 -1
- package/es/components/Tearsheet/next/index.d.ts +2 -1
- package/es/components/index.d.ts +1 -1
- package/es/global/js/hooks/index.d.ts +1 -0
- package/es/global/js/utils/devtools.js +1 -1
- package/es/index.js +8 -8
- package/es/node_modules/@carbon/icons-react/es/generated/bucket-10.js +1184 -1110
- package/es/node_modules/@carbon/icons-react/es/generated/bucket-3.js +1337 -1334
- package/es/node_modules/@floating-ui/dom/dist/floating-ui.dom.js +2 -2
- package/lib/components/ActionSet/ActionSet.d.ts +6 -0
- package/lib/components/ActionSet/ActionSet.js +20 -10
- package/lib/components/Coachmark/next/Coachmark/ContentBody.js +0 -3
- package/lib/components/DataSpreadsheet/utils/moveColumnIndicatorLine.js +2 -2
- package/lib/components/EditInPlace/EditInPlace.d.ts +2 -3
- package/lib/components/OptionsTile/OptionsTile.js +35 -12
- package/lib/components/PageHeader/next/index.js +6 -6
- package/lib/components/TagSet/TagSet.js +0 -3
- package/lib/components/Tearsheet/next/StackContext.d.ts +1 -1
- package/lib/components/Tearsheet/next/Tearsheet.d.ts +19 -5
- package/lib/components/Tearsheet/next/Tearsheet.js +90 -31
- package/lib/components/Tearsheet/next/TearsheetBody.js +2 -2
- package/lib/components/Tearsheet/next/TearsheetFooter.d.ts +31 -0
- package/lib/components/Tearsheet/next/TearsheetFooter.js +43 -0
- package/lib/components/Tearsheet/next/TearsheetHeader.d.ts +1 -1
- package/lib/components/Tearsheet/next/index.d.ts +2 -1
- package/lib/components/index.d.ts +1 -1
- package/lib/global/js/hooks/index.d.ts +1 -0
- package/lib/index.js +50 -50
- package/lib/node_modules/@carbon/icons-react/es/generated/bucket-10.js +1204 -1130
- package/lib/node_modules/@carbon/icons-react/es/generated/bucket-3.js +1351 -1348
- package/package.json +14 -18
- package/scss/components/NotificationsPanel/_notifications-panel.scss +3 -0
- package/scss/components/OptionsTile/_options-tile.scss +28 -7
- package/scss/components/PageHeader/_page-header.scss +14 -4
- package/scss/components/SidePanel/_side-panel.scss +0 -2
- package/scss/components/Tearsheet/_index-with-carbon.scss +2 -1
- package/scss/components/Tearsheet/_index.scss +1 -0
- package/scss/components/Tearsheet/_tearsheet.scss +0 -2
- package/scss/components/Tearsheet/_tearsheet_next.scss +351 -229
- package/telemetry.yml +4 -1
|
@@ -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.
|
|
@@ -44,7 +44,7 @@ ActionSetButton.displayName = 'ActionSetButton';
|
|
|
44
44
|
ActionSetButton.propTypes = {
|
|
45
45
|
/**@ts-ignore*/
|
|
46
46
|
...Button.PropTypes,
|
|
47
|
-
kind: PropTypes.oneOf(['ghost', 'danger--ghost', 'secondary', 'danger', 'primary']),
|
|
47
|
+
kind: PropTypes.oneOf(['ghost', 'danger--ghost', 'tertiary', 'secondary', 'danger', 'primary']),
|
|
48
48
|
label: PropTypes.string,
|
|
49
49
|
loading: PropTypes.bool
|
|
50
50
|
};
|
|
@@ -66,6 +66,7 @@ const validateActionSetProps = _ref2 => {
|
|
|
66
66
|
const countActions = kind => actions.filter(action => (action.kind || defaultKind) === kind).length;
|
|
67
67
|
const primaryActions = countActions('primary');
|
|
68
68
|
const secondaryActions = countActions('secondary');
|
|
69
|
+
const tertiaryActions = countActions('tertiary');
|
|
69
70
|
const dangerActions = countActions('danger');
|
|
70
71
|
const ghostActions = countActions('ghost') + countActions('danger--ghost');
|
|
71
72
|
if (stacking && actions.length > 3) {
|
|
@@ -83,8 +84,8 @@ const validateActionSetProps = _ref2 => {
|
|
|
83
84
|
if (stacking && actions.length > 1 && ghostActions > 0) {
|
|
84
85
|
problems.push(`you cannot have a 'ghost' button in conjunction with other action types in this size of ${componentName}`);
|
|
85
86
|
}
|
|
86
|
-
if (actions.length > primaryActions + secondaryActions + dangerActions + ghostActions) {
|
|
87
|
-
problems.push(`you can only have 'primary', 'danger', 'secondary', 'ghost' and 'danger--ghost' buttons in a ${componentName}`);
|
|
87
|
+
if (actions.length > primaryActions + secondaryActions + tertiaryActions + dangerActions + ghostActions) {
|
|
88
|
+
problems.push(`you can only have 'primary', 'danger', 'secondary', 'tertiary', 'ghost' and 'danger--ghost' buttons in a ${componentName}`);
|
|
88
89
|
}
|
|
89
90
|
return problems.length > 0 ? pconsole.error(`Invalid prop \`actions\` supplied to \`${componentName}\`: ${problems.join(', and ')}.`) : null;
|
|
90
91
|
}
|
|
@@ -107,6 +108,7 @@ const ActionSet = /*#__PURE__*/React__default.forwardRef((props, ref) => {
|
|
|
107
108
|
actions,
|
|
108
109
|
buttonSize,
|
|
109
110
|
className,
|
|
111
|
+
disableStacking = false,
|
|
110
112
|
size = defaults.size,
|
|
111
113
|
...rest
|
|
112
114
|
} = props;
|
|
@@ -117,16 +119,18 @@ const ActionSet = /*#__PURE__*/React__default.forwardRef((props, ref) => {
|
|
|
117
119
|
const buttons = actions && actions.slice?.(0) || [];
|
|
118
120
|
|
|
119
121
|
// We stack the buttons in a sm set, or if there are three or more in a md set.
|
|
120
|
-
|
|
122
|
+
// Unless disableStacking is true, in which case we never stack.
|
|
123
|
+
const stacking = disableStacking ? false : willStack(size, buttons.length);
|
|
121
124
|
|
|
122
|
-
// Order of button kinds: ghost first, then danger--ghost, then
|
|
123
|
-
// then danger, and finally primary
|
|
125
|
+
// Order of button kinds: ghost first, then danger--ghost, then tertiary,
|
|
126
|
+
// then most other types, then danger, and finally primary
|
|
124
127
|
const buttonOrder = kind => ({
|
|
125
128
|
ghost: 1,
|
|
126
129
|
'danger--ghost': 2,
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
+
tertiary: 3,
|
|
131
|
+
danger: 5,
|
|
132
|
+
primary: 6
|
|
133
|
+
})[kind] ?? 4;
|
|
130
134
|
|
|
131
135
|
// order the actions with ghost/ghost-danger buttons first and primary/danger buttons last
|
|
132
136
|
// (or the opposite way if we're stacking)
|
|
@@ -183,7 +187,7 @@ ActionSet.propTypes = {
|
|
|
183
187
|
actions: allPropTypes([PropTypes.arrayOf(PropTypes.shape({
|
|
184
188
|
/**@ts-ignore*/
|
|
185
189
|
...Button.propTypes,
|
|
186
|
-
kind: PropTypes.oneOf(['ghost', 'danger--ghost', 'secondary', 'danger', 'primary']),
|
|
190
|
+
kind: PropTypes.oneOf(['ghost', 'danger--ghost', 'tertiary', 'secondary', 'danger', 'primary']),
|
|
187
191
|
label: PropTypes.string,
|
|
188
192
|
loading: PropTypes.bool,
|
|
189
193
|
// we duplicate this Button prop to improve the DocGen here
|
|
@@ -202,6 +206,12 @@ ActionSet.propTypes = {
|
|
|
202
206
|
* An optional class or classes to be added to the outermost element.
|
|
203
207
|
*/
|
|
204
208
|
className: PropTypes.string,
|
|
209
|
+
/**
|
|
210
|
+
* When true, prevents automatic stacking of buttons even when size would
|
|
211
|
+
* normally trigger stacking (e.g., 'sm' size or 'md' with 3+ actions).
|
|
212
|
+
* Buttons will remain in a horizontal layout.
|
|
213
|
+
*/
|
|
214
|
+
disableStacking: PropTypes.bool,
|
|
205
215
|
/**
|
|
206
216
|
* The size of the action set. Different button arrangements are used at
|
|
207
217
|
* different sizes, to make best use of the available space.
|
|
@@ -38,7 +38,7 @@ const moveColumnIndicatorLine = _ref => {
|
|
|
38
38
|
|
|
39
39
|
// Is near left side of viewport
|
|
40
40
|
if (clientX < leftEdgeThreshold) {
|
|
41
|
-
window.scrollBy(-
|
|
41
|
+
window.scrollBy(-scrollSpeed, 0);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
// Is near right side of viewport
|
|
@@ -48,7 +48,7 @@ const moveColumnIndicatorLine = _ref => {
|
|
|
48
48
|
|
|
49
49
|
// Is near left edge of table
|
|
50
50
|
if (clientX > left && clientX < left + leftEdgeThreshold) {
|
|
51
|
-
listContainer.scrollBy(-
|
|
51
|
+
listContainer.scrollBy(-scrollSpeed, 0);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
// 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
|
|
71
|
+
readOnly?: boolean;
|
|
73
72
|
/**
|
|
74
73
|
* label for the edit off button that displays when in read only mode
|
|
75
74
|
*/
|
|
76
|
-
readOnlyLabel?:
|
|
75
|
+
readOnlyLabel?: string;
|
|
77
76
|
/**
|
|
78
77
|
* text for the toggletip that displays when in read only mode
|
|
79
78
|
*/
|
|
@@ -121,7 +121,19 @@ const OptionsTile = /*#__PURE__*/React__default.forwardRef((props, ref) => {
|
|
|
121
121
|
setOpen(false);
|
|
122
122
|
}
|
|
123
123
|
};
|
|
124
|
-
const
|
|
124
|
+
const handleSummaryClick = evt => {
|
|
125
|
+
// Check if the click originated from the toggle button
|
|
126
|
+
const target = evt.target;
|
|
127
|
+
const toggleContainer = target.closest(`.${blockClass}__toggle-container`);
|
|
128
|
+
|
|
129
|
+
// If click is on toggle button, don't handle expand/collapse
|
|
130
|
+
if (toggleContainer) {
|
|
131
|
+
evt.preventDefault();
|
|
132
|
+
evt.stopPropagation();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Prevent default details toggle behavior
|
|
125
137
|
evt.preventDefault();
|
|
126
138
|
if (open) {
|
|
127
139
|
collapse();
|
|
@@ -174,8 +186,25 @@ const OptionsTile = /*#__PURE__*/React__default.forwardRef((props, ref) => {
|
|
|
174
186
|
[`${blockClass}--closing`]: closing
|
|
175
187
|
}),
|
|
176
188
|
ref: ref
|
|
177
|
-
}, getDevtoolsProps(componentName)),
|
|
178
|
-
className: `${blockClass}
|
|
189
|
+
}, getDevtoolsProps(componentName)), isExpandable ? /*#__PURE__*/React__default.createElement("details", {
|
|
190
|
+
className: `${blockClass}__details`,
|
|
191
|
+
open: open,
|
|
192
|
+
ref: detailsRef
|
|
193
|
+
}, /*#__PURE__*/React__default.createElement("summary", {
|
|
194
|
+
className: cx(`${blockClass}__header`, {
|
|
195
|
+
[`${blockClass}__header--has-toggle`]: enabled !== undefined
|
|
196
|
+
}),
|
|
197
|
+
onClick: handleSummaryClick,
|
|
198
|
+
"data-testid": "options-tile-header"
|
|
199
|
+
}, enabled !== undefined &&
|
|
200
|
+
/*#__PURE__*/
|
|
201
|
+
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
|
202
|
+
React__default.createElement("div", {
|
|
203
|
+
className: `${blockClass}__toggle-container`,
|
|
204
|
+
"data-testid": "options-tile-toggle-container",
|
|
205
|
+
onMouseDown: evt => {
|
|
206
|
+
evt.preventDefault();
|
|
207
|
+
}
|
|
179
208
|
}, /*#__PURE__*/React__default.createElement(Toggle, {
|
|
180
209
|
id: `${titleId}-toggle`,
|
|
181
210
|
className: `${blockClass}__toggle`,
|
|
@@ -185,14 +214,7 @@ const OptionsTile = /*#__PURE__*/React__default.forwardRef((props, ref) => {
|
|
|
185
214
|
onToggle: onToggle,
|
|
186
215
|
size: "sm",
|
|
187
216
|
disabled: isLocked
|
|
188
|
-
})),
|
|
189
|
-
className: `${blockClass}__details`,
|
|
190
|
-
open: open,
|
|
191
|
-
ref: detailsRef
|
|
192
|
-
}, /*#__PURE__*/React__default.createElement("summary", {
|
|
193
|
-
className: `${blockClass}__header`,
|
|
194
|
-
onClick: toggle
|
|
195
|
-
}, /*#__PURE__*/React__default.createElement(ChevronDown, {
|
|
217
|
+
})), /*#__PURE__*/React__default.createElement(ChevronDown, {
|
|
196
218
|
size: 16,
|
|
197
219
|
className: cx(`${blockClass}__chevron`, {
|
|
198
220
|
[`${blockClass}__chevron--open`]: open,
|
|
@@ -200,7 +222,8 @@ const OptionsTile = /*#__PURE__*/React__default.forwardRef((props, ref) => {
|
|
|
200
222
|
})
|
|
201
223
|
}), renderTitle()), /*#__PURE__*/React__default.createElement("div", {
|
|
202
224
|
className: `${blockClass}__content`,
|
|
203
|
-
ref: contentRef
|
|
225
|
+
ref: contentRef,
|
|
226
|
+
"data-testid": "options-tile-content"
|
|
204
227
|
}, /*#__PURE__*/React__default.createElement(Layer, null, isLocked && /*#__PURE__*/React__default.createElement("p", {
|
|
205
228
|
className: `${blockClass}__locked-text`
|
|
206
229
|
}, _Locked || (_Locked = /*#__PURE__*/React__default.createElement(Locked, {
|
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
|
|
8
8
|
export { BreadcrumbBar, BreadcrumbOverflow, Content, ContentPageActions, ContentText, HeroImage, PageHeader, Root, ScrollButton, TabBar, TagOverflow, TitleBreadcrumb } from './PageHeader.js';
|
|
9
9
|
export { PageHeaderBreadcrumbBar } from './PageHeaderBreadcrumbBar.js';
|
|
10
|
+
export { PageHeaderBreadcrumbOverflow } from './PageHeaderBreadcrumbOverflow.js';
|
|
10
11
|
export { PageHeaderContent } from './PageHeaderContent.js';
|
|
11
12
|
export { PageHeaderContentPageActions } from './PageHeaderContentPageActions.js';
|
|
12
13
|
export { PageHeaderContentText } from './PageHeaderContentText.js';
|
|
13
|
-
export { PageHeaderTabBar } from './PageHeaderTabBar.js';
|
|
14
14
|
export { PageHeaderHeroImage } from './PageHeaderHeroImage.js';
|
|
15
15
|
export { PageHeaderScrollButton } from './PageHeaderScrollButton.js';
|
|
16
|
-
export {
|
|
17
|
-
export { PageHeaderBreadcrumbOverflow } from './PageHeaderBreadcrumbOverflow.js';
|
|
16
|
+
export { PageHeaderTabBar } from './PageHeaderTabBar.js';
|
|
18
17
|
export { PageHeaderTagOverflow } from './PageHeaderTagOverflow.js';
|
|
18
|
+
export { PageHeaderTitleBreadcrumb } from './PageHeaderTitleBreadcrumb.js';
|
|
@@ -12,7 +12,7 @@ import cx from 'classnames';
|
|
|
12
12
|
import { getDevtoolsProps } from '../../global/js/utils/devtools.js';
|
|
13
13
|
import { pkg } from '../../settings.js';
|
|
14
14
|
import { DefinitionTooltip } from '@carbon/react';
|
|
15
|
-
import { StringFormatterAlignment,
|
|
15
|
+
import { StringFormatterAlignment, deprecated_StringFormatterAlignment, propMappingFunction } from './utils/enums.js';
|
|
16
16
|
import { allPropTypes } from '../../global/js/utils/props-helper.js';
|
|
17
17
|
import { useIsomorphicEffect } from '../../global/js/hooks/useIsomorphicEffect.js';
|
|
18
18
|
|
|
@@ -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<
|
|
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
|
-
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { extends as _extends } from '../../../_virtual/_rollupPluginBabelHelpers.js';
|
|
9
9
|
import React__default, { forwardRef, useRef, useState, useEffect } from 'react';
|
|
10
|
+
import { createPortal } from 'react-dom';
|
|
10
11
|
import cx from 'classnames';
|
|
11
12
|
import { usePrefix, unstable_FeatureFlags, ComposedModal, ModalBody } from '@carbon/react';
|
|
12
13
|
import { TearsheetContext, blockClass } from './context.js';
|
|
@@ -14,11 +15,13 @@ import TearsheetHeader, { TearsheetNavigationBar, TearsheetScrollButton } from '
|
|
|
14
15
|
import TearsheetHeaderContent from './TearsheetHeaderContent.js';
|
|
15
16
|
import TearsheetBody, { Influencer, MainContent, SummaryContent } from './TearsheetBody.js';
|
|
16
17
|
import { TearsheetHeaderActions, TearsheetHeaderActionItem } from './TearsheetHeaderActions.js';
|
|
18
|
+
import TearsheetFooter from './TearsheetFooter.js';
|
|
17
19
|
import { breakpoints } from '@carbon/layout';
|
|
18
|
-
import { usePortalTarget } from '../../../global/js/hooks/usePortalTarget.js';
|
|
19
20
|
import { useStackContext } from './StackContext.js';
|
|
20
21
|
import { useMatchMedia } from '../../../global/js/hooks/useMatchMedia.js';
|
|
21
22
|
import { useId } from '../../../global/js/utils/useId.js';
|
|
23
|
+
import { usePresence } from '../usePresence.js';
|
|
24
|
+
import { useMergedRefs } from '../../../global/js/hooks/useMergedRefs.js';
|
|
22
25
|
import { useIsomorphicEffect } from '../../../global/js/hooks/useIsomorphicEffect.js';
|
|
23
26
|
|
|
24
27
|
/**
|
|
@@ -27,7 +30,11 @@ import { useIsomorphicEffect } from '../../../global/js/hooks/useIsomorphicEffec
|
|
|
27
30
|
* ----------
|
|
28
31
|
*/
|
|
29
32
|
|
|
30
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Internal component that handles the actual tearsheet rendering.
|
|
35
|
+
* This component is always "present" when mounted - the wrapper handles presence logic.
|
|
36
|
+
*/
|
|
37
|
+
const TearsheetInternal = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
31
38
|
let {
|
|
32
39
|
children,
|
|
33
40
|
variant = 'wide',
|
|
@@ -40,14 +47,18 @@ const Tearsheet = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
|
40
47
|
selectorPrimaryFocus,
|
|
41
48
|
open = false,
|
|
42
49
|
portalTarget,
|
|
50
|
+
disablePortal = false,
|
|
43
51
|
verticalGap,
|
|
44
52
|
containerClassName,
|
|
53
|
+
keepMounted = false,
|
|
54
|
+
isExiting = false,
|
|
55
|
+
presenceRef,
|
|
45
56
|
...rest
|
|
46
57
|
} = _ref;
|
|
47
58
|
const carbonPrefix = usePrefix();
|
|
48
|
-
const localRef = useRef(
|
|
59
|
+
const localRef = useRef(null);
|
|
49
60
|
const bodyRef = useRef(null);
|
|
50
|
-
const
|
|
61
|
+
const mergedRefs = useMergedRefs([ref, localRef, presenceRef]);
|
|
51
62
|
const smMediaQuery = `(max-width: ${breakpoints.md.width})`;
|
|
52
63
|
const isSm = useMatchMedia(smMediaQuery) || variant === 'narrow';
|
|
53
64
|
const [hasCloseIcon, setHasCloseIcon] = useState(true);
|
|
@@ -57,7 +68,7 @@ const Tearsheet = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
|
57
68
|
const header = arr.find(child => child.type === TearsheetHeader);
|
|
58
69
|
const influencer = arr.find(child => child.type === Influencer);
|
|
59
70
|
const body = arr.find(child => child.type === TearsheetBody);
|
|
60
|
-
const footer = arr.find(child => child.type ===
|
|
71
|
+
const footer = arr.find(child => child.type === TearsheetFooter);
|
|
61
72
|
const uniqueId = useRef(useId());
|
|
62
73
|
const {
|
|
63
74
|
notifyStack,
|
|
@@ -67,10 +78,17 @@ const Tearsheet = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
|
67
78
|
getBlockSizeChange
|
|
68
79
|
} = useStackContext();
|
|
69
80
|
const [depth, setDepth] = useState(0);
|
|
70
|
-
const
|
|
81
|
+
const [mountNode, setMountNode] = useState(null);
|
|
82
|
+
|
|
83
|
+
// Set portal mount node using useIsomorphicEffect to avoid SSR issues and double rendering
|
|
84
|
+
useIsomorphicEffect(() => {
|
|
85
|
+
if (!disablePortal) {
|
|
86
|
+
setMountNode(portalTarget || document.body);
|
|
87
|
+
}
|
|
88
|
+
}, [portalTarget, disablePortal]);
|
|
71
89
|
useIsomorphicEffect(() => {
|
|
72
|
-
const AILabelWidth =
|
|
73
|
-
const headerActionMarginRight = AILabelWidth + 24 + (isSm ? 8 : 0); // 24 is to
|
|
90
|
+
const AILabelWidth = localRef.current?.querySelector(`.${carbonPrefix}--ai-label`)?.clientWidth ?? 0;
|
|
91
|
+
const headerActionMarginRight = AILabelWidth + 24 + (isSm ? 8 : 0); // 24 is to compensate for close button
|
|
74
92
|
document.documentElement.style.setProperty('--tearsheet-header-action-offset', `${headerActionMarginRight}px`);
|
|
75
93
|
if (influencerWidth) {
|
|
76
94
|
document.documentElement.style.setProperty('--tearsheet-influencer-width', `${influencerWidth}`);
|
|
@@ -85,25 +103,30 @@ const Tearsheet = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
|
85
103
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
86
104
|
}, [isSm, rest.decorator, influencerWidth, summaryContentWidth, verticalGap]);
|
|
87
105
|
useIsomorphicEffect(() => {
|
|
88
|
-
|
|
89
|
-
|
|
106
|
+
const id = uniqueId.current;
|
|
107
|
+
if (localRef.current && open) {
|
|
108
|
+
notifyStack?.(id, true, localRef.current);
|
|
90
109
|
}
|
|
91
110
|
|
|
111
|
+
// Cleanup when component unmounts
|
|
112
|
+
return () => {
|
|
113
|
+
notifyStack?.(id, false, null);
|
|
114
|
+
};
|
|
92
115
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
93
|
-
}, [open]);
|
|
116
|
+
}, [localRef.current, open]);
|
|
94
117
|
useEffect(() => {
|
|
95
|
-
if (stack?.length > 0) {
|
|
118
|
+
if (stack?.length > 0 && localRef.current) {
|
|
96
119
|
const stackDepth = getDepth?.(uniqueId.current),
|
|
97
120
|
blockSizeChange = getBlockSizeChange?.(uniqueId.current),
|
|
98
121
|
scaleFactor = getScaleFactor?.(uniqueId.current);
|
|
99
122
|
setDepth(stackDepth);
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
123
|
+
localRef.current.style.setProperty('--stack-depth', stackDepth + '');
|
|
124
|
+
localRef.current.style.setProperty('--block-size-change', blockSizeChange);
|
|
125
|
+
localRef.current.style.setProperty('--scale-factor', scaleFactor + '');
|
|
103
126
|
}
|
|
104
127
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
105
128
|
}, [stack]);
|
|
106
|
-
|
|
129
|
+
const content = /*#__PURE__*/React__default.createElement(TearsheetContext.Provider, {
|
|
107
130
|
value: {
|
|
108
131
|
hasCloseIcon,
|
|
109
132
|
setHasCloseIcon,
|
|
@@ -126,29 +149,65 @@ const Tearsheet = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
|
126
149
|
[`${blockClass}--stack-activated`]: stack.length > 1,
|
|
127
150
|
[`${blockClass}--has-ai-label`]: !!rest.decorator && rest.decorator['type']?.displayName === 'AILabel',
|
|
128
151
|
[`${blockClass}--has-decorator`]: !!rest.decorator && rest.decorator['type']?.displayName !== 'AILabel',
|
|
129
|
-
[`${blockClass}--has-close`]: hasCloseIcon
|
|
152
|
+
[`${blockClass}--has-close`]: hasCloseIcon,
|
|
153
|
+
['is-visible']: keepMounted ? open : true,
|
|
154
|
+
// When keepMounted, use open prop; otherwise always visible
|
|
155
|
+
[`${blockClass}--keep-mounted`]: keepMounted
|
|
130
156
|
}),
|
|
131
157
|
containerClassName: cx(`${blockClass}__container`, containerClassName),
|
|
132
158
|
onClose,
|
|
133
|
-
open,
|
|
159
|
+
open: keepMounted ? open : true,
|
|
160
|
+
// When keepMounted, use actual open; otherwise always open
|
|
134
161
|
selectorPrimaryFocus,
|
|
135
|
-
ref:
|
|
162
|
+
ref: mergedRefs,
|
|
136
163
|
selectorsFloatingMenus: [`.${carbonPrefix}--overflow-menu-options`, `.${carbonPrefix}--tooltip`, '.flatpickr-calendar', `.${blockClass}__container`, `.${carbonPrefix}--menu`, ...selectorsFloatingMenus],
|
|
137
164
|
isFullWidth: true,
|
|
138
|
-
size: variant === 'narrow' ? 'sm' : ''
|
|
165
|
+
size: variant === 'narrow' ? 'sm' : '',
|
|
166
|
+
"data-tearsheet-exiting": isExiting ? true : undefined
|
|
139
167
|
}), header, /*#__PURE__*/React__default.createElement(ModalBody, {
|
|
140
|
-
className: `${blockClass}__body-layout`,
|
|
168
|
+
className: cx(`${blockClass}__body-layout`, {
|
|
169
|
+
[`${blockClass}__body-layout--has-influencer`]: influencer && !isSm
|
|
170
|
+
}),
|
|
141
171
|
ref: bodyRef
|
|
142
|
-
}, influencer, body, footer))))
|
|
172
|
+
}, influencer, body, footer))));
|
|
173
|
+
|
|
174
|
+
// If portal is disabled, return content directly
|
|
175
|
+
if (disablePortal) {
|
|
176
|
+
return content;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Return portal if mountNode is set, otherwise return content directly (SSR-safe)
|
|
180
|
+
return mountNode ? /*#__PURE__*/createPortal(content, mountNode) : content;
|
|
143
181
|
});
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Wrapper component that handles presence logic and conditionally renders TearsheetInternal.
|
|
185
|
+
* This ensures that all component state and effects are only initialized when the tearsheet is present.
|
|
186
|
+
*/
|
|
187
|
+
const Tearsheet = /*#__PURE__*/forwardRef((props, ref) => {
|
|
188
|
+
const {
|
|
189
|
+
open = false,
|
|
190
|
+
keepMounted = false
|
|
191
|
+
} = props;
|
|
192
|
+
const presenceRef = useRef(null);
|
|
193
|
+
|
|
194
|
+
// Use presence hook for enter/exit animations (unless keepMounted is true)
|
|
195
|
+
const {
|
|
196
|
+
isPresent,
|
|
197
|
+
isExiting
|
|
198
|
+
} = usePresence(presenceRef, keepMounted ? true : open);
|
|
199
|
+
|
|
200
|
+
// Don't render if not present (after exit animation completes) - unless keepMounted is true
|
|
201
|
+
if (!keepMounted && !isPresent) {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// When present, render the internal component with all props
|
|
206
|
+
return /*#__PURE__*/React__default.createElement(TearsheetInternal, _extends({}, props, {
|
|
207
|
+
ref: ref,
|
|
208
|
+
presenceRef: presenceRef,
|
|
209
|
+
isExiting: isExiting
|
|
210
|
+
}));
|
|
152
211
|
});
|
|
153
212
|
Tearsheet.Header = TearsheetHeader;
|
|
154
213
|
Tearsheet.HeaderContent = TearsheetHeaderContent;
|
|
@@ -156,7 +215,7 @@ Tearsheet.Body = TearsheetBody;
|
|
|
156
215
|
Tearsheet.Influencer = Influencer;
|
|
157
216
|
Tearsheet.MainContent = MainContent;
|
|
158
217
|
Tearsheet.SummaryContent = SummaryContent;
|
|
159
|
-
Tearsheet.Footer =
|
|
218
|
+
Tearsheet.Footer = TearsheetFooter;
|
|
160
219
|
Tearsheet.NavigationBar = TearsheetNavigationBar;
|
|
161
220
|
Tearsheet.ScrollButton = TearsheetScrollButton;
|
|
162
221
|
Tearsheet.HeaderActions = TearsheetHeaderActions;
|
|
@@ -102,7 +102,7 @@ const SummaryContent = /*#__PURE__*/forwardRef((_ref3, ref) => {
|
|
|
102
102
|
size: "sm",
|
|
103
103
|
open: summaryPanelOpen,
|
|
104
104
|
onRequestClose: onSummaryPanelClose,
|
|
105
|
-
className: className
|
|
105
|
+
className: cx(`${blockClass}__side-panel`, className)
|
|
106
106
|
}, children);
|
|
107
107
|
});
|
|
108
108
|
const Influencer = /*#__PURE__*/forwardRef((_ref4, ref) => {
|
|
@@ -126,7 +126,7 @@ const Influencer = /*#__PURE__*/forwardRef((_ref4, ref) => {
|
|
|
126
126
|
open: influencerPanelOpen,
|
|
127
127
|
onRequestClose: onInfluencerPanelClose,
|
|
128
128
|
placement: "left",
|
|
129
|
-
className: className
|
|
129
|
+
className: cx(`${blockClass}__side-panel`, className)
|
|
130
130
|
}, children);
|
|
131
131
|
});
|
|
132
132
|
|
|
@@ -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,39 @@
|
|
|
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 React__default, { forwardRef, useContext } from 'react';
|
|
9
|
+
import cx from 'classnames';
|
|
10
|
+
import { ActionSet } from '../../ActionSet/ActionSet.js';
|
|
11
|
+
import { TearsheetContext, blockClass } from './context.js';
|
|
12
|
+
|
|
13
|
+
const TearsheetFooter = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
14
|
+
let {
|
|
15
|
+
children,
|
|
16
|
+
className,
|
|
17
|
+
actions,
|
|
18
|
+
buttonSize
|
|
19
|
+
} = _ref;
|
|
20
|
+
const {
|
|
21
|
+
variant
|
|
22
|
+
} = useContext(TearsheetContext);
|
|
23
|
+
const actionCount = actions?.length || 0;
|
|
24
|
+
return /*#__PURE__*/React__default.createElement("footer", {
|
|
25
|
+
className: cx(`${blockClass}__footer`, className, {
|
|
26
|
+
[`${blockClass}__footer--three-actions`]: actionCount == 3,
|
|
27
|
+
[`${blockClass}__footer--many-actions`]: actionCount > 3
|
|
28
|
+
}),
|
|
29
|
+
ref: ref
|
|
30
|
+
}, children, actions && actions.length > 0 && /*#__PURE__*/React__default.createElement(ActionSet, {
|
|
31
|
+
actions: actions,
|
|
32
|
+
buttonSize: buttonSize,
|
|
33
|
+
disableStacking: true,
|
|
34
|
+
size: variant == 'wide' ? '2xl' : 'lg'
|
|
35
|
+
}));
|
|
36
|
+
});
|
|
37
|
+
TearsheetFooter.displayName = 'TearsheetFooter';
|
|
38
|
+
|
|
39
|
+
export { TearsheetFooter as default };
|
|
@@ -35,7 +35,7 @@ export interface TearsheetHeaderProps {
|
|
|
35
35
|
hideCloseButton?: boolean;
|
|
36
36
|
className?: string;
|
|
37
37
|
/**
|
|
38
|
-
* Default header collapse/expand while scrolling the main content can
|
|
38
|
+
* Default header collapse/expand while scrolling the main content can be disabled by setting this
|
|
39
39
|
*/
|
|
40
40
|
disableHeaderCollapse?: boolean;
|
|
41
41
|
}
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
export { Tearsheet } from './Tearsheet';
|
|
8
8
|
export { StackProvider, useStackContext } from './StackContext';
|
|
9
9
|
export type { StackContextType } from './StackContext';
|
|
10
|
-
export type { TearsheetProps, TearsheetComponentType
|
|
10
|
+
export type { TearsheetProps, TearsheetComponentType } from './Tearsheet';
|
|
11
11
|
export type { MainContentProps, SummaryContentProps, TearsheetBodyProps, InfluencerProps, } from './TearsheetBody';
|
|
12
12
|
export type { TearsheetHeaderProps, TearsheetNavigationBarProps, TearsheetScrollButtonProps, } from './TearsheetHeader';
|
|
13
13
|
export type { TearsheetHeaderActionItemProps, TearsheetHeaderActionsProps, } from './TearsheetHeaderActions';
|
|
14
14
|
export type { TearsheetHeaderContentProps } from './TearsheetHeaderContent';
|
|
15
|
+
export type { TearsheetFooterProps } from './TearsheetFooter';
|
package/es/components/index.d.ts
CHANGED
|
@@ -50,7 +50,7 @@ export { TruncatedText as preview__TruncatedText, type TruncatedTextProps, } fro
|
|
|
50
50
|
export { FeatureFlags as preview__FeatureFlags, useFeatureFlag as preview__useFeatureFlag, useFeatureFlags as preview__useFeatureFlags, } from './FeatureFlags';
|
|
51
51
|
export * as preview__PageHeader from './PageHeader/next';
|
|
52
52
|
export { Tearsheet as preview__Tearsheet, StackProvider, } from './Tearsheet/next';
|
|
53
|
-
export type { TearsheetProps as preview__TearsheetProps, TearsheetComponentType,
|
|
53
|
+
export type { TearsheetProps as preview__TearsheetProps, TearsheetComponentType, TearsheetFooterProps, MainContentProps, SummaryContentProps, TearsheetBodyProps, InfluencerProps, TearsheetHeaderProps, TearsheetNavigationBarProps, TearsheetScrollButtonProps, TearsheetHeaderActionItemProps, TearsheetHeaderActionsProps, TearsheetHeaderContentProps, StackContextType, } from './Tearsheet/next';
|
|
54
54
|
export { BigNumber as previewCandidate__BigNumber, type BigNumberProps, } from './BigNumber';
|
|
55
55
|
export { Coachmark as previewCandidate__Coachmark, BEACON_KIND, COACHMARK_OVERLAY_KIND, COACHMARK_ALIGNMENT, useCoachmark, type CoachmarkProps, } from './Coachmark';
|
|
56
56
|
export { CoachmarkBeacon as previewCandidate__CoachmarkBeacon, type CoachmarkBeaconProps, } from './CoachmarkBeacon';
|