@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.
- package/css/carbon.css +4 -0
- package/css/carbon.css.map +1 -1
- package/css/index-full-carbon.css +99 -33
- 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 +95 -33
- 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 +95 -33
- 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 +95 -33
- 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/AddSelect/AddSelectBody.js +1 -1
- package/es/components/Coachmark/next/Coachmark/CoachmarkBeacon/CoachmarkBeacon.js +6 -6
- package/es/components/Datagrid/Datagrid/addons/CustomizeColumns/CustomizeColumnsTearsheet.js +1 -1
- package/es/components/PageHeader/PageHeader.js +4 -12
- package/es/components/PageHeader/next/PageHeader.js +29 -12
- package/es/components/PageHeader/next/context.d.ts +3 -0
- package/es/components/PageHeader/next/utils.js +8 -0
- package/es/components/SidePanel/SidePanel.js +11 -3
- package/es/components/Tearsheet/TearsheetPresence.d.ts +37 -0
- package/es/components/Tearsheet/TearsheetPresence.js +56 -0
- package/es/components/Tearsheet/TearsheetShell.js +76 -17
- package/es/components/Tearsheet/index.d.ts +2 -0
- package/es/components/Tearsheet/usePresence.d.ts +17 -0
- package/es/components/Tearsheet/usePresence.js +69 -0
- package/es/components/Tearsheet/usePresenceContext.d.ts +25 -0
- package/es/components/Tearsheet/usePresenceContext.js +50 -0
- package/es/global/js/hooks/useMergedRefs.d.ts +1 -0
- package/es/global/js/hooks/useMergedRefs.js +32 -0
- package/es/global/js/hooks/useOverflowString.js +1 -16
- package/es/index.js +1 -0
- package/lib/components/AddSelect/AddSelectBody.js +1 -1
- package/lib/components/Coachmark/next/Coachmark/CoachmarkBeacon/CoachmarkBeacon.js +6 -6
- package/lib/components/Datagrid/Datagrid/addons/CustomizeColumns/CustomizeColumnsTearsheet.js +1 -1
- package/lib/components/PageHeader/PageHeader.js +3 -11
- package/lib/components/PageHeader/next/PageHeader.js +29 -12
- package/lib/components/PageHeader/next/context.d.ts +3 -0
- package/lib/components/PageHeader/next/utils.js +8 -0
- package/lib/components/SidePanel/SidePanel.js +10 -2
- package/lib/components/Tearsheet/TearsheetPresence.d.ts +37 -0
- package/lib/components/Tearsheet/TearsheetPresence.js +61 -0
- package/lib/components/Tearsheet/TearsheetShell.js +74 -15
- package/lib/components/Tearsheet/index.d.ts +2 -0
- package/lib/components/Tearsheet/usePresence.d.ts +17 -0
- package/lib/components/Tearsheet/usePresence.js +71 -0
- package/lib/components/Tearsheet/usePresenceContext.d.ts +25 -0
- package/lib/components/Tearsheet/usePresenceContext.js +52 -0
- package/lib/global/js/hooks/useMergedRefs.d.ts +1 -0
- package/lib/global/js/hooks/useMergedRefs.js +34 -0
- package/lib/global/js/hooks/useOverflowString.js +0 -16
- package/lib/index.js +3 -0
- package/package.json +22 -21
- package/scss/components/APIKeyModal/_api-key-modal.scss +6 -4
- package/scss/components/AboutModal/_about-modal.scss +5 -5
- package/scss/components/ActionBar/_action-bar.scss +2 -0
- package/scss/components/ActionSet/_action-set.scss +12 -11
- package/scss/components/AddSelect/_add-select.scss +28 -29
- package/scss/components/BreadcrumbWithOverflow/_breadcrumb-with-overflow.scss +10 -8
- package/scss/components/ButtonMenu/_button-menu.scss +11 -9
- package/scss/components/Card/_card.scss +12 -10
- package/scss/components/Checklist/_checklist.scss +8 -6
- package/scss/components/Coachmark/_coachmark-overlay.scss +11 -9
- package/scss/components/Coachmark/_coachmark.scss +1 -1
- package/scss/components/CoachmarkStack/_coachmark-stack.scss +6 -4
- package/scss/components/ComboButton/_combo-button.scss +11 -9
- package/scss/components/CreateFullPage/_create-full-page.scss +9 -9
- package/scss/components/CreateModal/_create-modal.scss +9 -7
- package/scss/components/CreateSidePanel/_create-side-panel.scss +6 -4
- package/scss/components/CreateTearsheet/_create-tearsheet.scss +9 -9
- package/scss/components/CreateTearsheetNarrow/_create-tearsheet-narrow.scss +5 -3
- package/scss/components/Datagrid/_datagrid.scss +9 -7
- package/scss/components/Datagrid/styles/_datagrid.scss +86 -86
- package/scss/components/Datagrid/styles/_useExpandedRow.scss +11 -9
- package/scss/components/Datagrid/styles/_useInlineEdit.scss +48 -46
- package/scss/components/Datagrid/styles/_useNestedRows.scss +16 -16
- package/scss/components/Datagrid/styles/_useNestedTable.scss +5 -3
- package/scss/components/Datagrid/styles/_useSelectAllToggle.scss +4 -2
- package/scss/components/Datagrid/styles/_useSortableColumns.scss +21 -19
- package/scss/components/Datagrid/styles/addons/_CustomizeColumnsTearsheet.scss +5 -4
- package/scss/components/Datagrid/styles/addons/_FilterFlyout.scss +5 -5
- package/scss/components/Datagrid/styles/addons/_FilterPanel.scss +11 -8
- package/scss/components/Datagrid/styles/addons/_RowSizeDropdown.scss +18 -16
- package/scss/components/Datagrid/styles/addons/_animations.scss +4 -4
- package/scss/components/DescriptionList/_description-list.scss +6 -4
- package/scss/components/EditInPlace/_edit-in-place.scss +5 -9
- package/scss/components/EditSidePanel/_edit-side-panel.scss +6 -4
- package/scss/components/EditTearsheet/_edit-tearsheet.scss +8 -9
- package/scss/components/ExportModal/_export-modal.scss +7 -5
- package/scss/components/FilterPanel/_filter-panel-accordion-item.scss +6 -5
- package/scss/components/FilterPanel/_filter-panel-checkbox-with-overflow.scss +6 -5
- package/scss/components/FilterPanel/_filter-panel-checkbox.scss +6 -5
- package/scss/components/FilterPanel/_filter-panel.scss +6 -5
- package/scss/components/FilterSummary/_filter-summary.scss +5 -9
- package/scss/components/Guidebanner/_guidebanner.scss +5 -3
- package/scss/components/ImportModal/_import-modal.scss +16 -16
- package/scss/components/InterstitialScreen/_interstitial-screen.scss +6 -4
- package/scss/components/NotificationsPanel/_notifications-panel.scss +13 -8
- package/scss/components/OptionsTile/_options-tile.scss +8 -6
- package/scss/components/PageHeader/_page-header.scss +25 -21
- package/scss/components/RemoveModal/_remove-modal.scss +5 -4
- package/scss/components/Saving/_saving.scss +5 -3
- package/scss/components/SearchBar/_search-bar.scss +5 -4
- package/scss/components/SidePanel/_animations.scss +4 -4
- package/scss/components/SidePanel/_side-panel.scss +31 -12
- package/scss/components/SimpleHeader/_simple-header.scss +5 -4
- package/scss/components/StatusIcon/_status-icon.scss +5 -3
- package/scss/components/StatusIndicator/_status-indicator.scss +3 -2
- package/scss/components/StringFormatter/_string-formatter.scss +5 -4
- package/scss/components/TagOverflow/_tag-overflow.scss +7 -6
- package/scss/components/TagSet/_tag-set.scss +20 -18
- package/scss/components/Tearsheet/_tearsheet.scss +121 -30
- package/scss/components/Toolbar/_toolbar.scss +4 -2
- package/scss/components/TruncatedList/_truncated-list.scss +4 -3
- package/scss/components/TruncatedText/_truncated-text.scss +2 -2
- package/scss/components/UserAvatar/_user-avatar.scss +5 -4
- package/scss/components/UserProfileImage/_user-profile-image.scss +11 -7
- package/scss/components/WebTerminal/_web-terminal.scss +4 -2
- package/telemetry.yml +3 -0
|
@@ -12,6 +12,7 @@ import cx from 'classnames';
|
|
|
12
12
|
import { Tag } from '@carbon/react';
|
|
13
13
|
import { Tearsheet } from '../Tearsheet/Tearsheet.js';
|
|
14
14
|
import { TearsheetNarrow } from '../Tearsheet/TearsheetNarrow.js';
|
|
15
|
+
import { pkg } from '../../settings.js';
|
|
15
16
|
import '../EmptyStates/EmptyState.js';
|
|
16
17
|
import '../EmptyStates/EmptyStateV2.deprecated.js';
|
|
17
18
|
import '../EmptyStates/ErrorEmptyState/ErrorEmptyState.js';
|
|
@@ -30,7 +31,6 @@ import { getFilteredItems, sortItems } from './add-select-utils.js';
|
|
|
30
31
|
import { useItemSort } from './hooks/useItemSort.js';
|
|
31
32
|
import useParentSelect from './hooks/useParentSelect.js';
|
|
32
33
|
import usePath from './hooks/usePath.js';
|
|
33
|
-
import { pkg } from '../../settings.js';
|
|
34
34
|
|
|
35
35
|
const blockClass = `${pkg.prefix}--add-select`;
|
|
36
36
|
const componentName = 'AddSelectBody';
|
|
@@ -31,18 +31,18 @@ const CoachmarkBeacon = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
31
31
|
} = props;
|
|
32
32
|
return /*#__PURE__*/React__default.createElement("div", _extends({
|
|
33
33
|
className: cx(blockClass, `${blockClass}-${kind}`, className)
|
|
34
|
-
}, getDevtoolsProps(componentName), {
|
|
35
|
-
role: "tooltip"
|
|
36
|
-
}, rest, {
|
|
34
|
+
}, getDevtoolsProps(componentName), rest, {
|
|
37
35
|
ref: ref
|
|
38
36
|
}), /*#__PURE__*/React__default.createElement("button", _extends({
|
|
39
37
|
type: "button"
|
|
40
38
|
}, buttonProps, {
|
|
41
|
-
className: `${blockClass}__target
|
|
39
|
+
className: `${blockClass}__target`,
|
|
40
|
+
"aria-label": label
|
|
42
41
|
}), /*#__PURE__*/React__default.createElement("svg", {
|
|
43
42
|
className: `${blockClass}__center`,
|
|
44
|
-
"aria-
|
|
45
|
-
|
|
43
|
+
"aria-hidden": "true",
|
|
44
|
+
focusable: "false"
|
|
45
|
+
}, _circle || (_circle = /*#__PURE__*/React__default.createElement("circle", {
|
|
46
46
|
r: 1,
|
|
47
47
|
cx: 38,
|
|
48
48
|
cy: 38
|
package/es/components/Datagrid/Datagrid/addons/CustomizeColumns/CustomizeColumnsTearsheet.js
CHANGED
|
@@ -9,9 +9,9 @@ import React__default, { useState, useRef, useCallback, useEffect } from 'react'
|
|
|
9
9
|
import PropTypes from '../../../../../_virtual/index.js';
|
|
10
10
|
import '../../../../Tearsheet/Tearsheet.js';
|
|
11
11
|
import { TearsheetNarrow } from '../../../../Tearsheet/TearsheetNarrow.js';
|
|
12
|
+
import { pkg } from '../../../../../settings.js';
|
|
12
13
|
import Columns from './Columns.js';
|
|
13
14
|
import Actions from './Actions.js';
|
|
14
|
-
import { pkg } from '../../../../../settings.js';
|
|
15
15
|
|
|
16
16
|
const blockClass = `${pkg.prefix}--datagrid`;
|
|
17
17
|
const CustomizeColumnsTearsheet = _ref => {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js';
|
|
9
|
-
import { Tooltip, Tag, Button, usePrefix, FlexGrid, Row, Column
|
|
9
|
+
import { Tooltip, Tag, Button, usePrefix, FlexGrid, Row, Column } from '@carbon/react';
|
|
10
10
|
import React__default, { useState, useRef, useEffect } from 'react';
|
|
11
11
|
import { TagSet } from '../TagSet/TagSet.js';
|
|
12
12
|
import { spacing, baseFontSize, breakpoints } from '@carbon/layout';
|
|
@@ -19,7 +19,6 @@ import cx from 'classnames';
|
|
|
19
19
|
import { getDevtoolsProps } from '../../global/js/utils/devtools.js';
|
|
20
20
|
import { pkg } from '../../settings.js';
|
|
21
21
|
import { useResizeObserver } from '../../global/js/hooks/useResizeObserver.js';
|
|
22
|
-
import { useOverflowStringHeight } from '../../global/js/hooks/useOverflowString.js';
|
|
23
22
|
import { useNearestScroll } from '../../global/js/hooks/useWindowScroll.js';
|
|
24
23
|
import { useWindowResize } from '../../global/js/hooks/useWindowResize.js';
|
|
25
24
|
import { useIsomorphicEffect } from '../../global/js/hooks/useIsomorphicEffect.js';
|
|
@@ -355,12 +354,6 @@ const PageHeader = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
|
|
|
355
354
|
}
|
|
356
355
|
});
|
|
357
356
|
}, [headerRef, pageHeaderStyles]);
|
|
358
|
-
const subtitleRef = useRef(null);
|
|
359
|
-
const isOverflowing = useOverflowStringHeight(subtitleRef);
|
|
360
|
-
const subtitleContent = /*#__PURE__*/React__default.createElement("span", {
|
|
361
|
-
ref: subtitleRef,
|
|
362
|
-
className: `${blockClass}__subtitle-text`
|
|
363
|
-
}, subtitle);
|
|
364
357
|
return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("div", {
|
|
365
358
|
className: `${blockClass}--offset-top-measuring-element`,
|
|
366
359
|
ref: offsetTopMeasuringRef
|
|
@@ -437,10 +430,9 @@ const PageHeader = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
|
|
|
437
430
|
className: `${blockClass}__subtitle-row`
|
|
438
431
|
}, /*#__PURE__*/React__default.createElement(Column, {
|
|
439
432
|
className: `${blockClass}__subtitle`
|
|
440
|
-
},
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
}, subtitleContent) : subtitleContent)), children ? /*#__PURE__*/React__default.createElement(Row, {
|
|
433
|
+
}, /*#__PURE__*/React__default.createElement("span", {
|
|
434
|
+
className: `${blockClass}__subtitle-text`
|
|
435
|
+
}, subtitle))), children ? /*#__PURE__*/React__default.createElement(Row, {
|
|
444
436
|
className: `${blockClass}__available-row`
|
|
445
437
|
}, /*#__PURE__*/React__default.createElement(Column, {
|
|
446
438
|
className: `${blockClass}__available-column`
|
|
@@ -57,6 +57,7 @@ const PageHeader = /*#__PURE__*/React__default.forwardRef(function PageHeader(_r
|
|
|
57
57
|
const [fullyCollapsed, setFullyCollapsed] = useState(false);
|
|
58
58
|
const [titleClipped, setTitleClipped] = useState(false);
|
|
59
59
|
const [contentActionsClipped, setContentActionsClipped] = useState(false);
|
|
60
|
+
const [breadcrumbActionsClipped, setBreadcrumbActionsClipped] = useState(false);
|
|
60
61
|
|
|
61
62
|
// Intersection Observer setup, tracks if the PageHeaderContent is visible on page.
|
|
62
63
|
// If it is not visible, we should set fully collapsed to true so that the
|
|
@@ -101,6 +102,7 @@ const PageHeader = /*#__PURE__*/React__default.forwardRef(function PageHeader(_r
|
|
|
101
102
|
entries.forEach(entry => {
|
|
102
103
|
if (entry.target === refs?.contentActions.current) {
|
|
103
104
|
setContentActionsClipped(!entry.isIntersecting);
|
|
105
|
+
setBreadcrumbActionsClipped(entry.isIntersecting);
|
|
104
106
|
}
|
|
105
107
|
});
|
|
106
108
|
}, {
|
|
@@ -140,7 +142,8 @@ const PageHeader = /*#__PURE__*/React__default.forwardRef(function PageHeader(_r
|
|
|
140
142
|
pageActionsInstance,
|
|
141
143
|
setPageActionsInstance,
|
|
142
144
|
titleClipped,
|
|
143
|
-
contentActionsClipped
|
|
145
|
+
contentActionsClipped,
|
|
146
|
+
breadcrumbActionsClipped
|
|
144
147
|
}
|
|
145
148
|
}, /*#__PURE__*/React__default.createElement("div", _extends({
|
|
146
149
|
className: classNames,
|
|
@@ -167,10 +170,11 @@ const PageHeaderBreadcrumbBar = /*#__PURE__*/React__default.forwardRef(function
|
|
|
167
170
|
pageActionsFlush,
|
|
168
171
|
...other
|
|
169
172
|
} = _ref2;
|
|
173
|
+
const context = usePageHeader();
|
|
170
174
|
const {
|
|
171
175
|
pageActionsInstance: globalActions,
|
|
172
176
|
contentActionsClipped
|
|
173
|
-
} =
|
|
177
|
+
} = context;
|
|
174
178
|
const classNames = cx({
|
|
175
179
|
[`${blockClass}__breadcrumb-bar`]: true,
|
|
176
180
|
[`${blockClass}__breadcrumb-bar-border`]: border,
|
|
@@ -181,7 +185,12 @@ const PageHeaderBreadcrumbBar = /*#__PURE__*/React__default.forwardRef(function
|
|
|
181
185
|
[`${blockClass}__breadcrumb__content-actions-with-global-actions`]: !!globalActions,
|
|
182
186
|
[`${blockClass}__breadcrumb__content-actions-with-global-actions--show`]: contentActionsClipped
|
|
183
187
|
});
|
|
184
|
-
return /*#__PURE__*/React__default.createElement(
|
|
188
|
+
return /*#__PURE__*/React__default.createElement(PageHeaderContext.Provider, {
|
|
189
|
+
value: {
|
|
190
|
+
...context,
|
|
191
|
+
isContentActionsInBreadcrumbBar: true
|
|
192
|
+
}
|
|
193
|
+
}, /*#__PURE__*/React__default.createElement("div", _extends({
|
|
185
194
|
className: classNames,
|
|
186
195
|
ref: ref
|
|
187
196
|
}, other), /*#__PURE__*/React__default.createElement(Grid, null, /*#__PURE__*/React__default.createElement(Column, {
|
|
@@ -198,7 +207,7 @@ const PageHeaderBreadcrumbBar = /*#__PURE__*/React__default.forwardRef(function
|
|
|
198
207
|
className: `${blockClass}__breadcrumb__actions`
|
|
199
208
|
}, /*#__PURE__*/React__default.createElement("div", {
|
|
200
209
|
className: contentActionsClasses
|
|
201
|
-
}, contentActions), pageActions)))));
|
|
210
|
+
}, contentActions), pageActions))))));
|
|
202
211
|
});
|
|
203
212
|
PageHeaderBreadcrumbBar.displayName = 'PageHeaderBreadcrumbBar';
|
|
204
213
|
|
|
@@ -330,12 +339,14 @@ const PageHeaderContentPageActions = _ref4 => {
|
|
|
330
339
|
} = _ref4;
|
|
331
340
|
const {
|
|
332
341
|
setRefs,
|
|
333
|
-
contentActionsClipped
|
|
342
|
+
contentActionsClipped,
|
|
343
|
+
breadcrumbActionsClipped,
|
|
344
|
+
isContentActionsInBreadcrumbBar: isInBreadcrumbBar
|
|
334
345
|
} = usePageHeader();
|
|
335
346
|
const classNames = cx(`${blockClass}__content__page-actions`, {
|
|
336
347
|
// Revisit this:
|
|
337
348
|
// May want to only add this class if there are content actions in the breadcrumb bar as well
|
|
338
|
-
[`${blockClass}__content__page-actions--clipped`]: contentActionsClipped
|
|
349
|
+
[`${blockClass}__content__page-actions--clipped`]: isInBreadcrumbBar ? breadcrumbActionsClipped : contentActionsClipped
|
|
339
350
|
}, className);
|
|
340
351
|
const containerRef = useRef(null);
|
|
341
352
|
const offsetRef = useRef(null);
|
|
@@ -351,12 +362,18 @@ const PageHeaderContentPageActions = _ref4 => {
|
|
|
351
362
|
}
|
|
352
363
|
}, [menuButtonVisibility]);
|
|
353
364
|
useEffect(() => {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
365
|
+
if (isInBreadcrumbBar) {
|
|
366
|
+
setRefs(prev => ({
|
|
367
|
+
...prev,
|
|
368
|
+
breadcrumbActions: containerRef
|
|
369
|
+
}));
|
|
370
|
+
} else {
|
|
371
|
+
setRefs(prev => ({
|
|
372
|
+
...prev,
|
|
373
|
+
contentActions: containerRef
|
|
374
|
+
}));
|
|
375
|
+
}
|
|
376
|
+
}, [isInBreadcrumbBar, setRefs]);
|
|
360
377
|
useEffect(() => {
|
|
361
378
|
if (!containerRef.current || !Array.isArray(actions)) {
|
|
362
379
|
return;
|
|
@@ -14,6 +14,7 @@ export type PageHeaderRefs = {
|
|
|
14
14
|
contentRef?: RefObject<HTMLDivElement | null>;
|
|
15
15
|
titleRef?: RefObject<HTMLHeadingElement | null>;
|
|
16
16
|
contentActions?: RefObject<HTMLDivElement | null>;
|
|
17
|
+
breadcrumbActions?: RefObject<HTMLDivElement | null>;
|
|
17
18
|
};
|
|
18
19
|
type PageHeaderContextType = {
|
|
19
20
|
refs?: PageHeaderRefs;
|
|
@@ -23,6 +24,8 @@ type PageHeaderContextType = {
|
|
|
23
24
|
fullyCollapsed?: boolean;
|
|
24
25
|
titleClipped?: boolean;
|
|
25
26
|
contentActionsClipped?: boolean;
|
|
27
|
+
breadcrumbActionsClipped?: boolean;
|
|
28
|
+
isContentActionsInBreadcrumbBar?: boolean;
|
|
26
29
|
};
|
|
27
30
|
export declare const PageHeaderContext: import("react").Context<PageHeaderContextType | undefined>;
|
|
28
31
|
export declare function usePageHeader(): PageHeaderContextType;
|
|
@@ -39,6 +39,14 @@ const windowExists = typeof window !== `undefined`;
|
|
|
39
39
|
*/
|
|
40
40
|
const scrollable = target => {
|
|
41
41
|
const style = window.getComputedStyle(target);
|
|
42
|
+
const tagName = target.tagName.toLowerCase();
|
|
43
|
+
|
|
44
|
+
// Exclude body/html from hidden check (modals set overflow:hidden on body)
|
|
45
|
+
if (tagName === 'body' || tagName === 'html') {
|
|
46
|
+
return /(auto|scroll)/.test(style.overflow);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// For other elements, include hidden as it may be intentional scroll container
|
|
42
50
|
return /(auto|scroll|hidden)/.test(style.overflow);
|
|
43
51
|
};
|
|
44
52
|
|
|
@@ -9,7 +9,7 @@ import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js
|
|
|
9
9
|
import { ArrowLeft, Close } from '@carbon/react/icons';
|
|
10
10
|
import { Button, Section, IconButton, Layer, Heading } from '@carbon/react';
|
|
11
11
|
import { useFeatureFlag } from '../FeatureFlags/index.js';
|
|
12
|
-
import React__default, { useState, useRef,
|
|
12
|
+
import React__default, { useState, useRef, useCallback, useEffect } from 'react';
|
|
13
13
|
import { ActionSet } from '../ActionSet/ActionSet.js';
|
|
14
14
|
import { Resizer } from '@carbon-labs/react-resizer';
|
|
15
15
|
import PropTypes from '../../_virtual/index.js';
|
|
@@ -113,11 +113,19 @@ const SidePanel = /*#__PURE__*/React__default.forwardRef((props, ref) => {
|
|
|
113
113
|
|
|
114
114
|
// Title animation on scroll related state
|
|
115
115
|
const [labelTextHeight, setLabelTextHeight] = useState(0);
|
|
116
|
-
const handleEscapeKey = event => {
|
|
116
|
+
const handleEscapeKey = useCallback(event => {
|
|
117
117
|
if (event.key === 'Escape' && open) {
|
|
118
118
|
onRequestClose?.();
|
|
119
119
|
}
|
|
120
|
-
};
|
|
120
|
+
}, [onRequestClose, open]);
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
if (open && !slideIn) {
|
|
123
|
+
window.addEventListener('keydown', handleEscapeKey);
|
|
124
|
+
return () => {
|
|
125
|
+
window.removeEventListener('keydown', handleEscapeKey);
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}, [handleEscapeKey, open, slideIn]);
|
|
121
129
|
useEffect(() => {
|
|
122
130
|
if (!enableResizer) {
|
|
123
131
|
return;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2016, 2025
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
import React, { type ComponentType, type FC, type PropsWithChildren } from 'react';
|
|
8
|
+
import { type PresenceContext } from './usePresenceContext';
|
|
9
|
+
export interface TearsheetPresenceProps {
|
|
10
|
+
/**
|
|
11
|
+
* Specify whether the Modal is currently open
|
|
12
|
+
*/
|
|
13
|
+
open: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Internal property for backwards compatibility. Specify whether the Modal should opt in to presence mode.
|
|
16
|
+
*/
|
|
17
|
+
_autoEnablePresence?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Internal property to predefine the presence context's id for exclusivity.
|
|
20
|
+
*/
|
|
21
|
+
_presenceId?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare const TearsheetPresence: ({ open, _presenceId: presenceId, _autoEnablePresence: autoEnablePresence, children, }: PropsWithChildren<TearsheetPresenceProps>) => React.JSX.Element | null;
|
|
24
|
+
interface ModalPresenceContextProps extends PresenceContext {
|
|
25
|
+
autoEnablePresence: boolean;
|
|
26
|
+
}
|
|
27
|
+
export declare const TearsheetPresenceContext: React.Context<ModalPresenceContextProps | undefined>;
|
|
28
|
+
/**
|
|
29
|
+
* Handles occurrences where only a single modal must consume a context.
|
|
30
|
+
*/
|
|
31
|
+
export declare const useExclusiveTearsheetPresenceContext: (id: string) => ModalPresenceContextProps | undefined;
|
|
32
|
+
type WithModalPresenceProps = Pick<TearsheetPresenceProps, 'open'>;
|
|
33
|
+
/**
|
|
34
|
+
* Higher-order function that wraps a component with ModalPresence
|
|
35
|
+
*/
|
|
36
|
+
export declare const withTearsheetPresence: <TProps extends object>(Component: ComponentType<TProps>) => FC<TProps & WithModalPresenceProps>;
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
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, { createContext, useContext, useMemo } from 'react';
|
|
9
|
+
import { usePresenceContext } from './usePresenceContext.js';
|
|
10
|
+
|
|
11
|
+
const TearsheetPresence = _ref => {
|
|
12
|
+
let {
|
|
13
|
+
open,
|
|
14
|
+
_presenceId: presenceId,
|
|
15
|
+
_autoEnablePresence: autoEnablePresence = true,
|
|
16
|
+
children
|
|
17
|
+
} = _ref;
|
|
18
|
+
const [isPresent, context] = usePresenceContext(open, presenceId);
|
|
19
|
+
const contextValue = useMemo(() => ({
|
|
20
|
+
autoEnablePresence,
|
|
21
|
+
...context
|
|
22
|
+
}), [autoEnablePresence, context]);
|
|
23
|
+
if (!isPresent) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
return /*#__PURE__*/React__default.createElement(TearsheetPresenceContext.Provider, {
|
|
27
|
+
value: contextValue
|
|
28
|
+
}, children);
|
|
29
|
+
};
|
|
30
|
+
const TearsheetPresenceContext = /*#__PURE__*/createContext(undefined);
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Handles occurrences where only a single modal must consume a context.
|
|
34
|
+
*/
|
|
35
|
+
const useExclusiveTearsheetPresenceContext = id => {
|
|
36
|
+
const ctx = useContext(TearsheetPresenceContext);
|
|
37
|
+
return ctx?.isPresenceExclusive(id) ? ctx : undefined;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Higher-order function that wraps a component with ModalPresence
|
|
41
|
+
*/
|
|
42
|
+
const withTearsheetPresence = Component => {
|
|
43
|
+
const WithModalPresence = props => {
|
|
44
|
+
const {
|
|
45
|
+
open,
|
|
46
|
+
...componentProps
|
|
47
|
+
} = props;
|
|
48
|
+
return /*#__PURE__*/React__default.createElement(TearsheetPresence, {
|
|
49
|
+
open: open
|
|
50
|
+
}, /*#__PURE__*/React__default.createElement(Component, componentProps));
|
|
51
|
+
};
|
|
52
|
+
WithModalPresence.displayName = `withModalPresence(${Component.displayName || Component.name || 'Component'})`;
|
|
53
|
+
return WithModalPresence;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export { TearsheetPresence, TearsheetPresenceContext, useExclusiveTearsheetPresenceContext, withTearsheetPresence };
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js';
|
|
9
|
-
import React__default, { useRef, useState, useEffect } from 'react';
|
|
9
|
+
import React__default, { useContext, useRef, useState, useEffect } from 'react';
|
|
10
10
|
import { useResizeObserver } from '../../global/js/hooks/useResizeObserver.js';
|
|
11
11
|
import PropTypes from '../../_virtual/index.js';
|
|
12
12
|
import cx from 'classnames';
|
|
@@ -14,11 +14,14 @@ import { pkg } from '../../settings.js';
|
|
|
14
14
|
import pconsole from '../../global/js/utils/pconsole.js';
|
|
15
15
|
import { getNodeTextContent } from '../../global/js/utils/getNodeTextContent.js';
|
|
16
16
|
import { deprecateProp } from '../../global/js/utils/props-helper.js';
|
|
17
|
-
import { Button, usePrefix, unstable_FeatureFlags, ComposedModal, ModalHeader,
|
|
17
|
+
import { Button, useFeatureFlag, usePrefix, unstable_FeatureFlags, ComposedModal, ModalHeader, Layer, Section } from '@carbon/react';
|
|
18
18
|
import { ActionSet } from '../ActionSet/ActionSet.js';
|
|
19
19
|
import { Wrap } from '../../global/js/utils/Wrap.js';
|
|
20
20
|
import { usePortalTarget } from '../../global/js/hooks/usePortalTarget.js';
|
|
21
21
|
import { useFocus } from '../../global/js/hooks/useFocus.js';
|
|
22
|
+
import { useMergedRefs } from '../../global/js/hooks/useMergedRefs.js';
|
|
23
|
+
import { useId } from '../../global/js/utils/useId.js';
|
|
24
|
+
import { TearsheetPresenceContext, useExclusiveTearsheetPresenceContext, TearsheetPresence } from './TearsheetPresence.js';
|
|
22
25
|
import { usePreviousValue } from '../../global/js/hooks/usePreviousValue.js';
|
|
23
26
|
import { useIsomorphicEffect } from '../../global/js/hooks/useIsomorphicEffect.js';
|
|
24
27
|
|
|
@@ -72,6 +75,35 @@ const SectionLevel3 = _ref => {
|
|
|
72
75
|
* See the canvas tab for the component API details.
|
|
73
76
|
* */
|
|
74
77
|
const TearsheetShell = /*#__PURE__*/React__default.forwardRef((_ref2, ref) => {
|
|
78
|
+
let {
|
|
79
|
+
open,
|
|
80
|
+
...props
|
|
81
|
+
} = _ref2;
|
|
82
|
+
const id = useId();
|
|
83
|
+
const enablePresence = useFeatureFlag('enable-presence');
|
|
84
|
+
const hasPresenceContext = Boolean(useContext(TearsheetPresenceContext));
|
|
85
|
+
const hasPresenceOptIn = enablePresence || hasPresenceContext;
|
|
86
|
+
const exclusivePresenceContext = useExclusiveTearsheetPresenceContext(id);
|
|
87
|
+
|
|
88
|
+
// if opt in and not exclusive to a presence context, wrap with presence
|
|
89
|
+
if (hasPresenceOptIn && !exclusivePresenceContext) {
|
|
90
|
+
return /*#__PURE__*/React__default.createElement(TearsheetPresence, {
|
|
91
|
+
open: open ?? false,
|
|
92
|
+
_presenceId: id
|
|
93
|
+
// do not auto enable styles for opt-in by feature flag
|
|
94
|
+
,
|
|
95
|
+
_autoEnablePresence: hasPresenceContext
|
|
96
|
+
}, /*#__PURE__*/React__default.createElement(TearsheetShellDialog, _extends({
|
|
97
|
+
open: true,
|
|
98
|
+
ref: ref
|
|
99
|
+
}, props)));
|
|
100
|
+
}
|
|
101
|
+
return /*#__PURE__*/React__default.createElement(TearsheetShellDialog, _extends({
|
|
102
|
+
ref: ref,
|
|
103
|
+
open: open
|
|
104
|
+
}, props));
|
|
105
|
+
});
|
|
106
|
+
const TearsheetShellDialog = /*#__PURE__*/React__default.forwardRef((_ref3, ref) => {
|
|
75
107
|
let {
|
|
76
108
|
// The component props, in alphabetical order (for consistency).
|
|
77
109
|
actions,
|
|
@@ -91,7 +123,7 @@ const TearsheetShell = /*#__PURE__*/React__default.forwardRef((_ref2, ref) => {
|
|
|
91
123
|
label,
|
|
92
124
|
navigation,
|
|
93
125
|
onClose,
|
|
94
|
-
open,
|
|
126
|
+
open: externalOpen,
|
|
95
127
|
portalTarget: portalTargetIn,
|
|
96
128
|
selectorPrimaryFocus,
|
|
97
129
|
selectorsFloatingMenus = [],
|
|
@@ -102,7 +134,7 @@ const TearsheetShell = /*#__PURE__*/React__default.forwardRef((_ref2, ref) => {
|
|
|
102
134
|
launcherButtonRef,
|
|
103
135
|
// Collect any other property values passed in.
|
|
104
136
|
...rest
|
|
105
|
-
} =
|
|
137
|
+
} = _ref3;
|
|
106
138
|
const carbonPrefix = usePrefix();
|
|
107
139
|
const bcModalHeader = `${carbonPrefix}--modal-header`;
|
|
108
140
|
const renderPortalUse = usePortalTarget(portalTargetIn);
|
|
@@ -113,13 +145,19 @@ const TearsheetShell = /*#__PURE__*/React__default.forwardRef((_ref2, ref) => {
|
|
|
113
145
|
const {
|
|
114
146
|
width
|
|
115
147
|
} = useResizeObserver(resizer);
|
|
116
|
-
const prevOpen = usePreviousValue(open);
|
|
117
148
|
const {
|
|
118
149
|
keyDownListener,
|
|
119
150
|
claimFocus
|
|
120
151
|
} = useFocus(modalRef, selectorPrimaryFocus);
|
|
121
152
|
modalRef.current;
|
|
122
153
|
const wide = size === 'wide';
|
|
154
|
+
const presenceContext = useContext(TearsheetPresenceContext);
|
|
155
|
+
const mergedRefs = useMergedRefs([modalRef, presenceContext?.presenceRef]);
|
|
156
|
+
const enablePresence = useFeatureFlag('enable-presence') || presenceContext?.autoEnablePresence;
|
|
157
|
+
|
|
158
|
+
// always mark as open when mounted with presence
|
|
159
|
+
const open = externalOpen || enablePresence;
|
|
160
|
+
const prevOpen = usePreviousValue(open);
|
|
123
161
|
|
|
124
162
|
// Keep track of the stack depth and our position in it (1-based, 0=closed)
|
|
125
163
|
const [depth, setDepth] = useState(0);
|
|
@@ -146,13 +184,28 @@ const TearsheetShell = /*#__PURE__*/React__default.forwardRef((_ref2, ref) => {
|
|
|
146
184
|
claimFocus();
|
|
147
185
|
}
|
|
148
186
|
}, [open, currentStep, effectiveHasCloseIcon, claimFocus]);
|
|
187
|
+
|
|
188
|
+
// Focus launcher button on open change for non presence tearsheet
|
|
149
189
|
useEffect(() => {
|
|
150
|
-
if (prevOpen && !open && launcherButtonRef?.current) {
|
|
190
|
+
if (!enablePresence && prevOpen && !open && launcherButtonRef?.current) {
|
|
151
191
|
setTimeout(() => {
|
|
152
192
|
launcherButtonRef?.current?.focus();
|
|
153
193
|
}, 10);
|
|
154
194
|
}
|
|
155
|
-
}, [open, prevOpen, launcherButtonRef]);
|
|
195
|
+
}, [enablePresence, open, prevOpen, launcherButtonRef]);
|
|
196
|
+
|
|
197
|
+
// Focus launcher button on unmount for presence tearsheet
|
|
198
|
+
useEffect(() => {
|
|
199
|
+
const launcherButton = launcherButtonRef?.current;
|
|
200
|
+
if (!enablePresence || !launcherButton) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
return () => {
|
|
204
|
+
setTimeout(() => {
|
|
205
|
+
launcherButton.focus();
|
|
206
|
+
}, 10);
|
|
207
|
+
};
|
|
208
|
+
}, [enablePresence, launcherButtonRef]);
|
|
156
209
|
useEffect(() => {
|
|
157
210
|
requestAnimationFrame(() => {
|
|
158
211
|
if (open && depth === position && !modalRef?.current?.contains(document.activeElement)) {
|
|
@@ -167,6 +220,8 @@ const TearsheetShell = /*#__PURE__*/React__default.forwardRef((_ref2, ref) => {
|
|
|
167
220
|
}
|
|
168
221
|
}, [claimFocus, hasError, modalRef]);
|
|
169
222
|
useEffect(() => {
|
|
223
|
+
// Other tearsheets should already be notified if this tearsheet is exiting
|
|
224
|
+
const isPresent = open && !presenceContext?.isExiting;
|
|
170
225
|
const notify = () => stack.all.forEach(handler => {
|
|
171
226
|
handler(Math.min(stack.open.length, maxDepth), stack.open.indexOf(handler) + 1);
|
|
172
227
|
});
|
|
@@ -179,7 +234,7 @@ const TearsheetShell = /*#__PURE__*/React__default.forwardRef((_ref2, ref) => {
|
|
|
179
234
|
// false to true to open it then append its notification callback to
|
|
180
235
|
// the end of the stack array (as its ID), and call all the callbacks
|
|
181
236
|
// to notify all open tearsheets that the stacking has changed.
|
|
182
|
-
if (
|
|
237
|
+
if (isPresent) {
|
|
183
238
|
stack.open.push(handleStackChange);
|
|
184
239
|
notify();
|
|
185
240
|
}
|
|
@@ -201,7 +256,7 @@ const TearsheetShell = /*#__PURE__*/React__default.forwardRef((_ref2, ref) => {
|
|
|
201
256
|
notify();
|
|
202
257
|
}
|
|
203
258
|
};
|
|
204
|
-
}, [open, size]);
|
|
259
|
+
}, [open, presenceContext?.isExiting, size]);
|
|
205
260
|
const areAllSameSizeVariant = () => new Set(stack.sizes).size === 1;
|
|
206
261
|
useIsomorphicEffect(() => {
|
|
207
262
|
const setScaleValues = () => {
|
|
@@ -217,8 +272,8 @@ const TearsheetShell = /*#__PURE__*/React__default.forwardRef((_ref2, ref) => {
|
|
|
217
272
|
};
|
|
218
273
|
};
|
|
219
274
|
if (modalRef.current) {
|
|
220
|
-
Object.entries(setScaleValues()).map(
|
|
221
|
-
let [key, value] =
|
|
275
|
+
Object.entries(setScaleValues()).map(_ref4 => {
|
|
276
|
+
let [key, value] = _ref4;
|
|
222
277
|
modalRef.current.style.setProperty(key, String(value));
|
|
223
278
|
});
|
|
224
279
|
}
|
|
@@ -245,7 +300,9 @@ const TearsheetShell = /*#__PURE__*/React__default.forwardRef((_ref2, ref) => {
|
|
|
245
300
|
[`${bc}--has-slug`]: deprecated_slug,
|
|
246
301
|
[`${bc}--has-ai-label`]: !!decorator && decorator['type']?.displayName === 'AILabel',
|
|
247
302
|
[`${bc}--has-decorator`]: !!decorator && decorator['type']?.displayName !== 'AILabel',
|
|
248
|
-
[`${bc}--has-close`]: effectiveHasCloseIcon
|
|
303
|
+
[`${bc}--has-close`]: effectiveHasCloseIcon,
|
|
304
|
+
['is-visible']: enablePresence,
|
|
305
|
+
[`${bc}--tearsheet-enable-presence`]: presenceContext?.autoEnablePresence
|
|
249
306
|
}),
|
|
250
307
|
decorator: decorator || deprecated_slug,
|
|
251
308
|
containerClassName: cx(`${bc}__container`, {
|
|
@@ -257,9 +314,10 @@ const TearsheetShell = /*#__PURE__*/React__default.forwardRef((_ref2, ref) => {
|
|
|
257
314
|
selectorPrimaryFocus,
|
|
258
315
|
onKeyDown: keyDownListener,
|
|
259
316
|
preventCloseOnClickOutside: !isPassive,
|
|
260
|
-
ref:
|
|
317
|
+
ref: mergedRefs,
|
|
261
318
|
selectorsFloatingMenus: [`.${carbonPrefix}--overflow-menu-options`, `.${carbonPrefix}--tooltip`, '.flatpickr-calendar', `.${bc}__container`, `.${carbonPrefix}--menu`, ...selectorsFloatingMenus],
|
|
262
|
-
size: "sm"
|
|
319
|
+
size: "sm",
|
|
320
|
+
"data-tearsheet-exiting": presenceContext?.isExiting || undefined
|
|
263
321
|
}), includeHeader && /*#__PURE__*/React__default.createElement(ModalHeader, {
|
|
264
322
|
className: cx(`${bc}__header`, {
|
|
265
323
|
[`${bc}__header--with-close-icon`]: effectiveHasCloseIcon,
|
|
@@ -270,16 +328,17 @@ const TearsheetShell = /*#__PURE__*/React__default.forwardRef((_ref2, ref) => {
|
|
|
270
328
|
}),
|
|
271
329
|
closeModal: onClose,
|
|
272
330
|
iconDescription: effectiveHasCloseIcon ? closeIconDescription : undefined
|
|
273
|
-
}, /*#__PURE__*/React__default.createElement(
|
|
331
|
+
}, /*#__PURE__*/React__default.createElement(Wrap, {
|
|
274
332
|
className: `${bc}__header-content`,
|
|
275
333
|
element: wide ? Layer : undefined
|
|
276
334
|
}, /*#__PURE__*/React__default.createElement(Wrap, {
|
|
277
335
|
className: `${bc}__header-fields`
|
|
278
336
|
}, /*#__PURE__*/React__default.createElement(Wrap, {
|
|
279
337
|
className: `${bcModalHeader}__label`
|
|
280
|
-
}, label), /*#__PURE__*/React__default.createElement(
|
|
338
|
+
}, label), /*#__PURE__*/React__default.createElement(Wrap, {
|
|
339
|
+
element: "h3",
|
|
281
340
|
className: cx(`${bcModalHeader}__heading`, `${bc}__heading`)
|
|
282
|
-
},
|
|
341
|
+
}, title), /*#__PURE__*/React__default.createElement(Wrap, {
|
|
283
342
|
className: `${bc}__header-description`
|
|
284
343
|
}, description)), /*#__PURE__*/React__default.createElement(Wrap, {
|
|
285
344
|
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,69 @@
|
|
|
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 { useState, useCallback, useLayoutEffect } from 'react';
|
|
9
|
+
import { pkg } from '../../settings.js';
|
|
10
|
+
|
|
11
|
+
const usePresence = (ref, isOpen) => {
|
|
12
|
+
const [exitState, setExitState] = useState(isOpen ? 'idle' : 'finished');
|
|
13
|
+
const isExiting = exitState === 'active';
|
|
14
|
+
|
|
15
|
+
// element is exiting
|
|
16
|
+
if (!isOpen && exitState === 'idle') {
|
|
17
|
+
setExitState('active');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// element exit was interrupted
|
|
21
|
+
if (isOpen && exitState !== 'idle') {
|
|
22
|
+
setExitState('idle');
|
|
23
|
+
}
|
|
24
|
+
const handleAnimationEnd = useCallback(() => {
|
|
25
|
+
setExitState('finished');
|
|
26
|
+
}, []);
|
|
27
|
+
useLayoutEffect(() => {
|
|
28
|
+
if (!ref.current || !isExiting) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// resolve for JSDOM
|
|
33
|
+
if (!('getAnimations' in ref.current)) {
|
|
34
|
+
handleAnimationEnd();
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// cover all animations that start with the presence prefix
|
|
39
|
+
const animations = ref.current.getAnimations({
|
|
40
|
+
subtree: true
|
|
41
|
+
}).filter(animation => animation instanceof CSSAnimation && animation.animationName.startsWith(`${pkg.prefix}`));
|
|
42
|
+
if (!animations.length) {
|
|
43
|
+
handleAnimationEnd();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
let cancelled = false;
|
|
47
|
+
Promise.all(animations.map(animation => animation.finished)).finally(() => {
|
|
48
|
+
if (cancelled) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
handleAnimationEnd();
|
|
52
|
+
});
|
|
53
|
+
return () => {
|
|
54
|
+
cancelled = true;
|
|
55
|
+
};
|
|
56
|
+
}, [ref, isExiting, handleAnimationEnd]);
|
|
57
|
+
return {
|
|
58
|
+
/**
|
|
59
|
+
* Indicates whether the ref object is supposed to be mounted
|
|
60
|
+
*/
|
|
61
|
+
isPresent: isOpen || exitState !== 'finished',
|
|
62
|
+
/**
|
|
63
|
+
* Indicates whether the ref object is currently exiting
|
|
64
|
+
*/
|
|
65
|
+
isExiting
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export { usePresence };
|