@12-apps/ui 6.62.1 → 6.63.1

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 (67) hide show
  1. package/dist/_internal/components/data-display/DataViews/DataViewsGrid.js +7 -7
  2. package/dist/_internal/components/data-display/DataViews/DataViewsTableBase.js +8 -8
  3. package/dist/_internal/components/data-display/DataViews/data-views-filter-panel.js +5 -5
  4. package/dist/_internal/components/data-display/DataViews/data-views-grid-parts.js +6 -6
  5. package/dist/_internal/components/data-display/Sheet/Sheet.body.js +2 -2
  6. package/dist/_internal/components/data-display/Sheet/Sheet.js +3 -3
  7. package/dist/_internal/components/data-display/Sheet/Sheet.parts.js +1 -1
  8. package/dist/_internal/components/data-display/index.js +3 -3
  9. package/dist/_internal/components/feedback/StackedModal/StackedModal.hooks.js +1 -1
  10. package/dist/_internal/components/feedback/StackedModal/StackedModal.js +4 -4
  11. package/dist/_internal/components/feedback/StackedModal/StackedModal.parts.js +2 -2
  12. package/dist/_internal/components/feedback/StackedModal/StackedModal.styles.js +1 -1
  13. package/dist/_internal/components/feedback/index.js +4 -4
  14. package/dist/_internal/components/index.js +8 -8
  15. package/dist/_internal/components/navigation/AppHeader/AppHeader.details.js +4 -4
  16. package/dist/_internal/components/navigation/index.js +4 -4
  17. package/dist/{chunk-BDQCCW4N.js → chunk-4P5Y5TEM.js} +4 -3
  18. package/dist/{chunk-BDQCCW4N.js.map → chunk-4P5Y5TEM.js.map} +1 -1
  19. package/dist/{chunk-6KQWNPER.js → chunk-64BEEFLJ.js} +2 -2
  20. package/dist/{chunk-ERTXRK6S.js → chunk-AOWXWNUV.js} +26 -8
  21. package/dist/chunk-AOWXWNUV.js.map +1 -0
  22. package/dist/{chunk-SKOAJPOS.js → chunk-EP674QTM.js} +3 -3
  23. package/dist/{chunk-4HHCVL5D.js → chunk-F4XQCFIH.js} +2 -2
  24. package/dist/{chunk-6DJQKK6Q.js → chunk-F5LDJ52M.js} +2 -2
  25. package/dist/{chunk-2KGYTJCN.js → chunk-GLKUN3DL.js} +4 -3
  26. package/dist/{chunk-2KGYTJCN.js.map → chunk-GLKUN3DL.js.map} +1 -1
  27. package/dist/{chunk-UDC5M3P2.js → chunk-RG3GJTSK.js} +2 -2
  28. package/dist/{chunk-OGBLNORM.js → chunk-RQ2NKSIA.js} +19 -5
  29. package/dist/chunk-RQ2NKSIA.js.map +1 -0
  30. package/dist/{chunk-G3QKH23Y.js → chunk-TSMF7K37.js} +4 -2
  31. package/dist/chunk-TSMF7K37.js.map +1 -0
  32. package/dist/{chunk-NEHCU3EZ.js → chunk-XSIVM4VO.js} +5 -2
  33. package/dist/{chunk-NEHCU3EZ.js.map → chunk-XSIVM4VO.js.map} +1 -1
  34. package/dist/{chunk-26DSSWTT.js → chunk-YLYQ7TIC.js} +2 -2
  35. package/dist/data-display/DataViews.js +8 -8
  36. package/dist/data-display/Sheet.js +3 -3
  37. package/dist/feedback/StackedModal.js +4 -4
  38. package/dist/navigation/AppHeader.js +4 -4
  39. package/dist/types/components/data-display/Sheet/Sheet.d.ts.map +1 -1
  40. package/dist/types/components/data-display/Sheet/Sheet.parts.d.ts.map +1 -1
  41. package/dist/types/components/feedback/StackedModal/StackedModal.d.ts.map +1 -1
  42. package/dist/types/components/feedback/StackedModal/StackedModal.hooks.d.ts +6 -0
  43. package/dist/types/components/feedback/StackedModal/StackedModal.hooks.d.ts.map +1 -1
  44. package/dist/types/components/feedback/StackedModal/StackedModal.parts.d.ts +2 -0
  45. package/dist/types/components/feedback/StackedModal/StackedModal.parts.d.ts.map +1 -1
  46. package/dist/types/components/feedback/StackedModal/StackedModal.styles.d.ts.map +1 -1
  47. package/dist/types/components/feedback/StackedModal/StackedModal.types.d.ts +12 -0
  48. package/dist/types/components/feedback/StackedModal/StackedModal.types.d.ts.map +1 -1
  49. package/package.json +1 -1
  50. package/src/components/data-display/Sheet/Sheet.md +1 -1
  51. package/src/components/data-display/Sheet/Sheet.parts.tsx +3 -2
  52. package/src/components/data-display/Sheet/Sheet.tsx +32 -8
  53. package/src/components/feedback/StackedModal/StackedModal.hooks.ts +11 -1
  54. package/src/components/feedback/StackedModal/StackedModal.md +28 -1
  55. package/src/components/feedback/StackedModal/StackedModal.parts.tsx +4 -1
  56. package/src/components/feedback/StackedModal/StackedModal.styles.ts +4 -1
  57. package/src/components/feedback/StackedModal/StackedModal.tsx +22 -3
  58. package/src/components/feedback/StackedModal/StackedModal.types.ts +12 -0
  59. package/dist/chunk-ERTXRK6S.js.map +0 -1
  60. package/dist/chunk-G3QKH23Y.js.map +0 -1
  61. package/dist/chunk-OGBLNORM.js.map +0 -1
  62. /package/dist/{chunk-6KQWNPER.js.map → chunk-64BEEFLJ.js.map} +0 -0
  63. /package/dist/{chunk-SKOAJPOS.js.map → chunk-EP674QTM.js.map} +0 -0
  64. /package/dist/{chunk-4HHCVL5D.js.map → chunk-F4XQCFIH.js.map} +0 -0
  65. /package/dist/{chunk-6DJQKK6Q.js.map → chunk-F5LDJ52M.js.map} +0 -0
  66. /package/dist/{chunk-UDC5M3P2.js.map → chunk-RG3GJTSK.js.map} +0 -0
  67. /package/dist/{chunk-26DSSWTT.js.map → chunk-YLYQ7TIC.js.map} +0 -0
@@ -222,8 +222,9 @@ export const SheetOverlay: React.FC<SheetOverlayProps> = (props) => {
222
222
  onClick={onClick}
223
223
  className={className}
224
224
  sx={{
225
- // One below the drawer: this backdrop replaces MUI's own (the drawer
226
- // is given a no-op BackdropComponent) and must sit under the panel.
225
+ // One below the drawer, under the panel. Inside a Sheet this veil is
226
+ // VISUAL only: the drawer's invisible backdrop above it catches the
227
+ // tap (`ClickCatcher` in Sheet.tsx). The onClick keeps it usable alone.
227
228
  zIndex: theme.zIndex.drawer - 1,
228
229
  backgroundColor: scrim(theme, blur ? 0.6 : 0.5),
229
230
  backdropFilter: blur ? `blur(${rem(theme, 8)}) saturate(180%)` : 'none',
@@ -59,17 +59,31 @@ const DEFAULTS = {
59
59
  type ResolvedSheetProps = SheetProps & Required<Pick<SheetProps, keyof typeof DEFAULTS>>;
60
60
 
61
61
  /**
62
- * The drawer supplies its own backdrop, which would sit on top of `SheetOverlay`
63
- * and swallow its click. Rendering nothing in its place leaves this component's
64
- * overlay — the one that knows about the `glass` blur — as the only one.
62
+ * The drawer's own backdrop, made invisible: it only CATCHES the tap.
63
+ *
64
+ * The visible veil is {@link SheetOverlay}, which knows about the `glass`
65
+ * blur. But the drawer's modal root covers the whole viewport above that veil,
66
+ * so with no backdrop at all a tap on the veil landed on the bare root, which
67
+ * handles nothing — and no sheet ever closed on an outside tap. This one sits
68
+ * under the panel, paints nothing, and hands the tap to the drawer's
69
+ * `onClose('backdropClick')`, which the sheet routes through its own
70
+ * `closeOnOverlayClick` / `persistent` rules.
65
71
  *
66
72
  * Defined at module scope: a component built inside a render is a new type on
67
73
  * every pass, which remounts the subtree it belongs to.
68
74
  */
69
- const NoBackdrop = React.forwardRef<HTMLDivElement, BackdropProps>(
70
- (_props, _ref) => null,
75
+ const ClickCatcher = React.forwardRef<HTMLDivElement, BackdropProps & { 'data-testid'?: string }>(
76
+ ({ onClick, 'data-testid': testId }, ref) => (
77
+ <div
78
+ ref={ref}
79
+ aria-hidden
80
+ onClick={onClick}
81
+ data-testid={testId}
82
+ style={{ position: 'fixed', inset: 0, backgroundColor: 'transparent' }}
83
+ />
84
+ ),
71
85
  ) as React.ComponentType<BackdropProps>;
72
- NoBackdrop.displayName = 'NoBackdrop';
86
+ ClickCatcher.displayName = 'SheetClickCatcher';
73
87
 
74
88
  /**
75
89
  * Which way the sheet is anchored. Four flags rather than four repeated
@@ -111,12 +125,22 @@ export const Sheet: React.FC<SheetProps> = (props) => {
111
125
  anchor: position,
112
126
  open: isOpen,
113
127
  className,
114
- onClose: handleClose,
128
+ // A tap outside obeys `closeOnOverlayClick`. A swipe-to-dismiss (no
129
+ // reason: SwipeableDrawer calls `onClose()` bare) closes. Escape is the
130
+ // hook's own listener alone, which honours `closeOnEscape` — the modal's
131
+ // is switched off below so the key never closes twice or past the flag.
132
+ onClose: (_event?: object, reason?: 'backdropClick' | 'escapeKeyDown') =>
133
+ reason === 'backdropClick' ? handleOverlayClick() : handleClose(),
115
134
  PaperProps: {
116
135
  ref: sheetRef,
117
136
  sx: panelSx({ ...resolved, ...orientation, theme, currentHeight, isDragging, isAnimating }),
118
137
  },
119
- ModalProps: { keepMounted: true, BackdropComponent: NoBackdrop },
138
+ ModalProps: {
139
+ keepMounted: true,
140
+ disableEscapeKeyDown: true,
141
+ BackdropComponent: ClickCatcher,
142
+ BackdropProps: { 'data-testid': testId ? `${testId}-click-catcher` : undefined } as Partial<BackdropProps>,
143
+ },
120
144
  };
121
145
 
122
146
  const body = (
@@ -111,6 +111,12 @@ interface PanelChrome {
111
111
  zIndex: number;
112
112
  isMobile: boolean;
113
113
  canGoBack: boolean;
114
+ /**
115
+ * This panel is the bottom of the stack. Read from ITS position, not from the
116
+ * depth: once a child opens, the depth says "can go back" for every panel,
117
+ * the receding root included.
118
+ */
119
+ isRoot: boolean;
114
120
  handleBack: () => void;
115
121
  handleClose: (event: object, reason: 'backdropClick' | 'escapeKeyDown') => void;
116
122
  }
@@ -125,7 +131,7 @@ export const usePanelChrome = (options: PanelChromeOptions): PanelChrome => {
125
131
  options;
126
132
 
127
133
  const dialogRef = useRef<HTMLDivElement>(null);
128
- const { currentDepth } = useModalStack();
134
+ const { currentDepth, stack } = useModalStack();
129
135
  const { modalRole, isAnimating } = useModalRole(Boolean(open), modalId);
130
136
  const showSkeleton = useSkeleton(Boolean(loading));
131
137
  const zIndex = usePanelZIndex(modalId, FALLBACK_Z_INDEX);
@@ -134,6 +140,9 @@ export const usePanelChrome = (options: PanelChromeOptions): PanelChrome => {
134
140
  const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
135
141
  const isPrimary = modalRole === 'primary';
136
142
  const canGoBack = currentDepth > 1;
143
+ // Not yet pushed (its first render) and something already below it: NOT the
144
+ // root — so a child never renders a frame without its back arrow.
145
+ const isRoot = (stack[0]?.id ?? modalId) === modalId;
137
146
 
138
147
  useFocusTrap(dialogRef, Boolean(open) && isPrimary, disableFocusTrap);
139
148
  useEscapeToClose(Boolean(open && closeOnEsc) && isPrimary, onClose);
@@ -154,6 +163,7 @@ export const usePanelChrome = (options: PanelChromeOptions): PanelChrome => {
154
163
  zIndex,
155
164
  isMobile,
156
165
  canGoBack,
166
+ isRoot,
157
167
  handleBack,
158
168
  handleClose,
159
169
  };
@@ -125,6 +125,8 @@ new component that portals should too.
125
125
  | `open` | `boolean` | `false` | Controls modal visibility |
126
126
  | `onClose` | `() => void` | - | Callback when modal is closed |
127
127
  | `navigationTitle` | `string` | - | Title displayed in modal header |
128
+ | `hideClose` | `boolean` | `false` | Drop the header's ✕ — the content owns the dismiss |
129
+ | `hideHeader` | `boolean` | `false` | Draw no header bar at all, for a root panel whose content brings its own header (✕ and name). Pass `aria-labelledby` naming that header's title (a dev warning fires without it). Root panel only |
128
130
  | `children` | `ReactNode` | - | Modal content |
129
131
  | `glass` | `boolean` | `false` | Enable glass morphism effect |
130
132
  | `actions` | `ReactNode` | - | Custom action buttons for header/footer |
@@ -179,6 +181,31 @@ and a dialog named by everything in the bar ("close Title Save").
179
181
  Without a `navigationTitle` the bar is still MUI's `h2` and holds the id, as
180
182
  before. Give such a panel its own `aria-labelledby` if it needs a name.
181
183
 
184
+ ### A panel that brings its own header
185
+
186
+ A root panel whose content already has a sticky header — the name and the ✕ in
187
+ one bar — would otherwise show TWO bars, the first holding only a breadcrumb.
188
+ `hideHeader` drops the panel's bar; the dialog is then named by the content's
189
+ own title through `aria-labelledby`:
190
+
191
+ ```tsx
192
+ <StackedModal
193
+ backLabel="Voltar"
194
+ open={open}
195
+ onClose={close}
196
+ hideHeader
197
+ aria-labelledby="product-edit-title"
198
+ >
199
+ <ProductEditHeader titleId="product-edit-title" … />
200
+ </StackedModal>
201
+ ```
202
+
203
+ Only the ROOT panel drops its bar, and it stays bar-less while a child panel is
204
+ open over it. A stacked panel keeps its bar whatever this says: the back arrow
205
+ lives there and is the only way out of depth ≥2.
206
+
207
+ With no bar, initial focus lands on the first focusable element of your content.
208
+
182
209
  ## Testing
183
210
 
184
211
  The StackedModal component provides comprehensive test IDs for all interactive elements through the `dataTestId` prop. When provided, it generates consistent test IDs for all child elements.
@@ -190,7 +217,7 @@ When you provide `dataTestId="my-modal"`, the following test IDs are automatical
190
217
  | Element | Test ID | Description |
191
218
  |---------|---------|-------------|
192
219
  | Modal Container | `my-modal` | The root dialog element |
193
- | Header | `my-modal-header` | The modal header bar (a `div` when there is a title) |
220
+ | Header | `my-modal-header` | The modal header bar (a `div` when there is a title; absent with `hideHeader` on a root panel) |
194
221
  | Title | `my-modal-title` | The navigation title — the panel's one `h2` |
195
222
  | Close Button | `my-modal-close-button` | The close button (when no modals stacked) |
196
223
  | Back Button | `my-modal-back-button` | The back button (when modals are stacked) |
@@ -300,6 +300,8 @@ export const ModalFooter: FC<{ dataTestId?: string; children?: ReactNode }> = ({
300
300
  );
301
301
 
302
302
  interface PanelContentsProps extends ModalHeaderProps {
303
+ /** Draw no bar — already resolved: `hideHeader` on the ROOT panel only. */
304
+ hideHeader?: boolean;
303
305
  showSkeleton: boolean;
304
306
  loadingText?: string;
305
307
  descId: string;
@@ -317,13 +319,14 @@ export const ModalPanelContents: FC<PanelContentsProps> = ({
317
319
  content,
318
320
  modalActions,
319
321
  hasMobileActions,
322
+ hideHeader,
320
323
  ...header
321
324
  }) => (
322
325
  <>
323
326
  {showSkeleton && (
324
327
  <ModalLoadingOverlay loadingText={loadingText} dataTestId={header.dataTestId} />
325
328
  )}
326
- <ModalHeader {...header} />
329
+ {!hideHeader && <ModalHeader {...header} />}
327
330
  <ModalBody
328
331
  descId={descId}
329
332
  showSkeleton={showSkeleton}
@@ -178,7 +178,10 @@ const panelStyles = (theme: Theme, props: PanelStyleProps): CSSObject => {
178
178
  /** The full rule set for the sliding panel's Dialog root. */
179
179
  export const dialogRootStyles = (theme: Theme, props: PanelStyleProps): CSSObject => ({
180
180
  direction: props.rtl ? 'rtl' : 'ltr',
181
- '& .MuiBackdrop-root': backdropStyles(theme, props.modalRole),
181
+ // The dialog's OWN backdrop only: a descendant selector also reached the
182
+ // backdrop of any sheet or drawer rendered inside the panel, and blurred the
183
+ // very panel that sheet sits on.
184
+ '& > .MuiBackdrop-root': backdropStyles(theme, props.modalRole),
182
185
  '& .MuiDialog-container': { alignItems: 'flex-start', justifyContent: 'flex-end' },
183
186
  '& .MuiDialog-paper': panelStyles(theme, props),
184
187
  ...KEYFRAMES,
@@ -3,7 +3,7 @@ import Slide from '@mui/material/Slide/index.js';
3
3
  import { styled } from '@mui/material/styles/index.js';
4
4
  import type { TransitionProps } from '@mui/material/transitions/index.js';
5
5
  import type { FC } from 'react';
6
- import React, { forwardRef, useId } from 'react';
6
+ import React, { forwardRef, useEffect, useId } from 'react';
7
7
  import { usePanelChrome } from './StackedModal.hooks';
8
8
  import { modalAriaIds, ModalPanelContents, resolveModalContent } from './StackedModal.parts';
9
9
  import type { PanelStyleProps } from './StackedModal.styles';
@@ -59,16 +59,33 @@ const withDefaults = (props: StackedModalProps): StackedModalProps => {
59
59
  /** The modal's props once defaults are applied and its stack identity is settled. */
60
60
  type ResolvedProps = StackedModalProps & { modalId: string };
61
61
 
62
+ /**
63
+ * `hideHeader` removes the bar that names the dialog; without an
64
+ * `aria-labelledby` of the caller's own, the dialog has no accessible name.
65
+ */
66
+ const useUnnamedDialogWarning = (unnamed: boolean): void => {
67
+ useEffect(() => {
68
+ const development = typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production';
69
+ if (unnamed && development) {
70
+ // eslint-disable-next-line no-console -- a config warning has no other channel.
71
+ console.warn(
72
+ 'StackedModal: `hideHeader` without `aria-labelledby` leaves the dialog unnamed. ' +
73
+ "Point `aria-labelledby` at your content header's title.",
74
+ );
75
+ }
76
+ }, [unnamed]);
77
+ };
78
+
62
79
  const StackedModalPanel: FC<ResolvedProps> = (props) => {
63
80
  const {
64
- open, onClose, glass, navigationTitle, backLabel, hideClose, children, actions, modalId,
81
+ open, onClose, glass, navigationTitle, backLabel, hideClose, hideHeader, children, actions, modalId,
65
82
  closeOnClickOutside, closeOnEsc, loading, loadingText, fullScreen, maxWidth, size, disableBackdrop,
66
83
  disableFocusTrap, keepMounted, rtl, dataTestId,
67
84
  'aria-labelledby': ariaLabelledBy, 'aria-describedby': ariaDescribedBy, ...otherProps
68
85
  } = props;
69
86
 
70
87
  const {
71
- dialogRef, modalRole, isAnimating, showSkeleton, zIndex, isMobile, canGoBack,
88
+ dialogRef, modalRole, isAnimating, showSkeleton, zIndex, isMobile, canGoBack, isRoot,
72
89
  handleBack, handleClose,
73
90
  } = usePanelChrome({
74
91
  open, modalId, loading, closeOnEsc, closeOnClickOutside, disableFocusTrap, onClose,
@@ -83,6 +100,7 @@ const StackedModalPanel: FC<ResolvedProps> = (props) => {
83
100
  isMobile,
84
101
  );
85
102
  const { titleId, descId } = modalAriaIds(modalId, ariaLabelledBy, ariaDescribedBy);
103
+ useUnnamedDialogWarning(Boolean(hideHeader) && !ariaLabelledBy);
86
104
 
87
105
  return (
88
106
  <StyledDialog
@@ -115,6 +133,7 @@ const StackedModalPanel: FC<ResolvedProps> = (props) => {
115
133
  descId={descId}
116
134
  navigationTitle={navigationTitle}
117
135
  hideClose={hideClose}
136
+ hideHeader={Boolean(hideHeader) && isRoot}
118
137
  canGoBack={canGoBack}
119
138
  onBack={handleBack}
120
139
  onClose={onClose}
@@ -32,6 +32,18 @@ export interface StackedModalProps {
32
32
  * different action, and it is the only way out of depth ≥2.
33
33
  */
34
34
  hideClose?: boolean;
35
+ /**
36
+ * Draws no navigation bar at all, for a ROOT panel whose content carries its
37
+ * own header — the ✕ and the panel's name in one sticky bar. A bar above it
38
+ * holding only a breadcrumb is a second header on a screen that already has
39
+ * one, and on a phone it costs the first 64px of the viewport.
40
+ *
41
+ * Pass `aria-labelledby` with it, naming the content header's title: the bar
42
+ * is what names the dialog otherwise (a dev warning says so). Applies to the
43
+ * ROOT panel only — a stacked panel's back arrow lives in the bar and is the
44
+ * only way out — and the root stays bar-less while a child is open over it.
45
+ */
46
+ hideHeader?: boolean;
35
47
  /** Modal content */
36
48
  children?: ReactNode;
37
49
  /** Actions to display in the modal header (desktop) or footer (mobile) */
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/data-display/Sheet/Sheet.tsx"],"sourcesContent":["import Drawer from '@mui/material/Drawer/index.js';\nimport SwipeableDrawer from '@mui/material/SwipeableDrawer/index.js';\nimport { useTheme } from '@mui/material/styles/index.js';\nimport type { BackdropProps } from '@mui/material/Backdrop/index.js';\nimport React from 'react';\n\nimport { withDefaults } from '../../../utils/withDefaults';\n\nimport {\n DEFAULT_DRAG_RESISTANCE,\n DEFAULT_VELOCITY_THRESHOLD,\n SPRING_CONFIG,\n} from './Sheet.animations';\nimport { SheetBody } from './Sheet.body';\nimport { useSheetDrag, useSheetOpen } from './Sheet.hooks';\nimport { SheetOverlay } from './Sheet.parts';\nimport { panelSx } from './Sheet.styles';\nimport type { SheetProps } from './Sheet.types';\n\nexport { SheetContent, SheetFooter, SheetHeader, SheetOverlay } from './Sheet.parts';\n\nconst DEFAULTS = {\n open: false,\n position: 'bottom',\n variant: 'default',\n size: 'md',\n color: 'primary',\n glow: false,\n pulse: false,\n glass: false,\n gradient: false,\n loading: false,\n disabled: false,\n showOverlay: true,\n closeOnOverlayClick: true,\n closeOnEscape: true,\n showCloseButton: true,\n showHandle: true,\n swipeable: true,\n snapPoints: [0.25, 0.5, 0.75, 1],\n defaultSnapPoint: 0.5,\n minSnapPoint: 0,\n maxSnapPoint: 1,\n velocityThreshold: DEFAULT_VELOCITY_THRESHOLD,\n dragResistance: DEFAULT_DRAG_RESISTANCE,\n animationConfig: SPRING_CONFIG,\n persistent: false,\n fullHeight: false,\n rounded: true,\n elevation: 16,\n dataTestId: 'sheet',\n} satisfies Partial<SheetProps>;\n\n/**\n * The props with every defaulted key now guaranteed present. Without this the\n * component would need a `?? fallback` at each use site, and each of those is a\n * branch the complexity gate counts.\n */\ntype ResolvedSheetProps = SheetProps & Required<Pick<SheetProps, keyof typeof DEFAULTS>>;\n\n/**\n * The drawer supplies its own backdrop, which would sit on top of `SheetOverlay`\n * and swallow its click. Rendering nothing in its place leaves this component's\n * overlay — the one that knows about the `glass` blur — as the only one.\n *\n * Defined at module scope: a component built inside a render is a new type on\n * every pass, which remounts the subtree it belongs to.\n */\nconst NoBackdrop = React.forwardRef<HTMLDivElement, BackdropProps>(\n (_props, _ref) => null,\n) as React.ComponentType<BackdropProps>;\nNoBackdrop.displayName = 'NoBackdrop';\n\n/**\n * Which way the sheet is anchored. Four flags rather than four repeated\n * comparisons, and the only thing the hooks and styles need to know about\n * `position` and `variant`.\n */\nconst orientationOf = ({ position, variant }: ResolvedSheetProps) => {\n const isBottomSheet = position === 'bottom';\n const isTopSheet = position === 'top';\n\n return {\n isBottomSheet,\n isTopSheet,\n isVerticalSheet: isBottomSheet || isTopSheet,\n isDraggableVariant: variant === 'draggable',\n };\n};\n\nexport const Sheet: React.FC<SheetProps> = (props) => {\n const resolved = withDefaults(props, DEFAULTS) as ResolvedSheetProps;\n const { position, variant, showOverlay, swipeable, className, dataTestId } = resolved;\n\n const theme = useTheme();\n const orientation = orientationOf(resolved);\n\n // Support both the dataTestId prop and a plain data-testid attribute.\n const testId = (resolved as { 'data-testid'?: string })['data-testid'] || dataTestId;\n\n const { sheetRef, currentHeight, isDragging, isAnimating, handleDragStart, resetToDefaultSnapPoint } =\n useSheetDrag({ ...resolved, ...orientation });\n\n const { isOpen, handleClose, handleOverlayClick, handleSwipeOpen } = useSheetOpen({\n ...resolved,\n ...orientation,\n resetToDefaultSnapPoint,\n });\n\n const drawerProps = {\n anchor: position,\n open: isOpen,\n className,\n onClose: handleClose,\n PaperProps: {\n ref: sheetRef,\n sx: panelSx({ ...resolved, ...orientation, theme, currentHeight, isDragging, isAnimating }),\n },\n ModalProps: { keepMounted: true, BackdropComponent: NoBackdrop },\n };\n\n const body = (\n <SheetBody\n {...resolved}\n {...orientation}\n testId={testId}\n isDragging={isDragging}\n onClose={handleClose}\n onDragStart={handleDragStart}\n />\n );\n\n // The draggable variant drives the panel's height itself; MUI's swipe handling\n // would be a second thing moving it.\n const isSwipeable = swipeable && !orientation.isDraggableVariant;\n\n return (\n <>\n {showOverlay && isOpen && (\n <SheetOverlay open={isOpen} onClick={handleOverlayClick} blur={variant === 'glass'} />\n )}\n\n {isSwipeable ? (\n <SwipeableDrawer\n {...drawerProps}\n onOpen={handleSwipeOpen}\n disableSwipeToOpen={false}\n swipeAreaWidth={20}\n >\n {body}\n </SwipeableDrawer>\n ) : (\n <Drawer {...drawerProps}>{body}</Drawer>\n )}\n </>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAAA,OAAO,YAAY;AACnB,OAAO,qBAAqB;AAC5B,SAAS,gBAAgB;AAEzB,OAAO,WAAW;AAsHd,SAeA,UAfA,KAeA,YAfA;AArGJ,IAAM,WAAW;AAAA,EACf,MAAM;AAAA,EACN,UAAU;AAAA,EACV,SAAS;AAAA,EACT,MAAM;AAAA,EACN,OAAO;AAAA,EACP,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO;AAAA,EACP,UAAU;AAAA,EACV,SAAS;AAAA,EACT,UAAU;AAAA,EACV,aAAa;AAAA,EACb,qBAAqB;AAAA,EACrB,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,YAAY,CAAC,MAAM,KAAK,MAAM,CAAC;AAAA,EAC/B,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,cAAc;AAAA,EACd,mBAAmB;AAAA,EACnB,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,WAAW;AAAA,EACX,YAAY;AACd;AAiBA,IAAM,aAAa,MAAM;AAAA,EACvB,CAAC,QAAQ,SAAS;AACpB;AACA,WAAW,cAAc;AAOzB,IAAM,gBAAgB,CAAC,EAAE,UAAU,QAAQ,MAA0B;AACnE,QAAM,gBAAgB,aAAa;AACnC,QAAM,aAAa,aAAa;AAEhC,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,iBAAiB,iBAAiB;AAAA,IAClC,oBAAoB,YAAY;AAAA,EAClC;AACF;AAEO,IAAM,QAA8B,CAAC,UAAU;AACpD,QAAM,WAAW,aAAa,OAAO,QAAQ;AAC7C,QAAM,EAAE,UAAU,SAAS,aAAa,WAAW,WAAW,WAAW,IAAI;AAE7E,QAAM,QAAQ,SAAS;AACvB,QAAM,cAAc,cAAc,QAAQ;AAG1C,QAAM,SAAU,SAAwC,aAAa,KAAK;AAE1E,QAAM,EAAE,UAAU,eAAe,YAAY,aAAa,iBAAiB,wBAAwB,IACjG,aAAa,EAAE,GAAG,UAAU,GAAG,YAAY,CAAC;AAE9C,QAAM,EAAE,QAAQ,aAAa,oBAAoB,gBAAgB,IAAI,aAAa;AAAA,IAChF,GAAG;AAAA,IACH,GAAG;AAAA,IACH;AAAA,EACF,CAAC;AAED,QAAM,cAAc;AAAA,IAClB,QAAQ;AAAA,IACR,MAAM;AAAA,IACN;AAAA,IACA,SAAS;AAAA,IACT,YAAY;AAAA,MACV,KAAK;AAAA,MACL,IAAI,QAAQ,EAAE,GAAG,UAAU,GAAG,aAAa,OAAO,eAAe,YAAY,YAAY,CAAC;AAAA,IAC5F;AAAA,IACA,YAAY,EAAE,aAAa,MAAM,mBAAmB,WAAW;AAAA,EACjE;AAEA,QAAM,OACJ;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACH,GAAG;AAAA,MACJ;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MACT,aAAa;AAAA;AAAA,EACf;AAKF,QAAM,cAAc,aAAa,CAAC,YAAY;AAE9C,SACE,iCACG;AAAA,mBAAe,UACd,oBAAC,gBAAa,MAAM,QAAQ,SAAS,oBAAoB,MAAM,YAAY,SAAS;AAAA,IAGrF,cACC;AAAA,MAAC;AAAA;AAAA,QACE,GAAG;AAAA,QACJ,QAAQ;AAAA,QACR,oBAAoB;AAAA,QACpB,gBAAgB;AAAA,QAEf;AAAA;AAAA,IACH,IAEA,oBAAC,UAAQ,GAAG,aAAc,gBAAK;AAAA,KAEnC;AAEJ;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/feedback/StackedModal/StackedModal.hooks.ts"],"sourcesContent":["import { useTheme } from '@mui/material/styles/index.js';\nimport useMediaQuery from '@mui/material/useMediaQuery/index.js';\nimport type { RefObject } from 'react';\nimport { useEffect, useRef, useState } from 'react';\nimport { useModalStack } from './modal-stack';\nimport type { ModalPanelRole } from './StackedModal.types';\n\nconst FOCUSABLE = 'a[href], button, textarea, input, select, [tabindex]:not([tabindex=\"-1\"])';\n/** MUI Dialog's own default, used when the modal isn't registered in the stack yet. */\nconst FALLBACK_Z_INDEX = 1300;\n/** Matches the panel's slide duration, so role swaps animate rather than jump. */\nconst ROLE_TRANSITION_MS = 300;\n\n/** Keeps Tab cycling inside the open panel instead of escaping to the page behind. */\nconst useFocusTrap = (\n ref: RefObject<HTMLElement | null>,\n isActive: boolean,\n disabled?: boolean,\n): void => {\n useEffect(() => {\n if (!isActive || disabled || !ref.current) return;\n\n const element = ref.current;\n const focusable = element.querySelectorAll(FOCUSABLE);\n const first = focusable[0] as HTMLElement | undefined;\n const last = focusable[focusable.length - 1] as HTMLElement | undefined;\n\n window.setTimeout(() => first?.focus(), 100);\n\n const handleTabKey = (e: globalThis.KeyboardEvent): void => {\n if (e.key !== 'Tab') return;\n const atEdge = e.shiftKey ? first : last;\n if (document.activeElement !== atEdge) return;\n e.preventDefault();\n (e.shiftKey ? last : first)?.focus();\n };\n\n element.addEventListener('keydown', handleTabKey);\n return () => element.removeEventListener('keydown', handleTabKey);\n }, [ref, isActive, disabled]);\n};\n\n/** Closes the panel on Escape — only while it is the top one. */\nconst useEscapeToClose = (active: boolean, onClose: () => void): void => {\n useEffect(() => {\n if (!active) return;\n\n const handleEsc = (e: globalThis.KeyboardEvent): void => {\n if (e.key !== 'Escape') return;\n e.preventDefault();\n onClose();\n };\n\n window.addEventListener('keydown', handleEsc);\n return () => window.removeEventListener('keydown', handleEsc);\n }, [active, onClose]);\n};\n\n/** Holds the skeleton a beat past `loading` so the swap to content isn't a flash. */\nconst useSkeleton = (loading: boolean): boolean => {\n const [showSkeleton, setShowSkeleton] = useState(loading);\n\n useEffect(() => {\n if (loading) {\n setShowSkeleton(true);\n return;\n }\n const timer = window.setTimeout(() => setShowSkeleton(false), 200);\n return () => window.clearTimeout(timer);\n }, [loading]);\n\n return showSkeleton;\n};\n\n/** Registers the modal in the stack while open, and unregisters it after it slides out. */\nconst useStackRegistration = (open: boolean, modalId: string): void => {\n const { pushModal, popModal, isModalInStack } = useModalStack();\n const hasRegisteredRef = useRef(false);\n\n useEffect(() => {\n if (open && !hasRegisteredRef.current) {\n pushModal(modalId);\n hasRegisteredRef.current = true;\n } else if (!open) {\n hasRegisteredRef.current = false;\n }\n }, [open, modalId, pushModal]);\n\n useEffect(() => {\n if (open || !isModalInStack(modalId)) return;\n const timer = window.setTimeout(() => popModal(modalId), ROLE_TRANSITION_MS);\n return () => window.clearTimeout(timer);\n }, [open, modalId, popModal, isModalInStack]);\n};\n\ninterface PanelChromeOptions {\n open?: boolean;\n modalId: string;\n loading?: boolean;\n closeOnEsc?: boolean;\n closeOnClickOutside?: boolean;\n disableFocusTrap?: boolean;\n onClose: () => void;\n}\n\ninterface PanelChrome {\n dialogRef: RefObject<HTMLDivElement | null>;\n modalRole: ModalPanelRole;\n isAnimating: boolean;\n showSkeleton: boolean;\n zIndex: number;\n isMobile: boolean;\n canGoBack: boolean;\n handleBack: () => void;\n handleClose: (event: object, reason: 'backdropClick' | 'escapeKeyDown') => void;\n}\n\n/**\n * Everything the panel needs to render itself: its place in the stack, the\n * viewport it is on, and the handlers that dismiss it. Bundled into one hook so\n * the component stays a description of the markup.\n */\nexport const usePanelChrome = (options: PanelChromeOptions): PanelChrome => {\n const { open, modalId, loading, closeOnEsc, closeOnClickOutside, disableFocusTrap, onClose } =\n options;\n\n const dialogRef = useRef<HTMLDivElement>(null);\n const { currentDepth } = useModalStack();\n const { modalRole, isAnimating } = useModalRole(Boolean(open), modalId);\n const showSkeleton = useSkeleton(Boolean(loading));\n const zIndex = usePanelZIndex(modalId, FALLBACK_Z_INDEX);\n\n const theme = useTheme();\n const isMobile = useMediaQuery(theme.breakpoints.down('sm'));\n const isPrimary = modalRole === 'primary';\n const canGoBack = currentDepth > 1;\n\n useFocusTrap(dialogRef, Boolean(open) && isPrimary, disableFocusTrap);\n useEscapeToClose(Boolean(open && closeOnEsc) && isPrimary, onClose);\n\n const { handleBack, handleClose } = useCloseHandlers({\n isPrimary,\n closeOnClickOutside,\n closeOnEsc,\n canGoBack,\n onClose,\n });\n\n return {\n dialogRef,\n modalRole,\n isAnimating,\n showSkeleton,\n zIndex,\n isMobile,\n canGoBack,\n handleBack,\n handleClose,\n };\n};\n\ninterface CloseHandlerOptions {\n /** Only the top panel responds to a backdrop click or Escape. */\n isPrimary: boolean;\n closeOnClickOutside?: boolean;\n closeOnEsc?: boolean;\n /** Deeper than one panel, so the leading control goes back rather than closing. */\n canGoBack: boolean;\n onClose: () => void;\n}\n\n/** The panel's dismissal handlers, honouring which gestures the caller allows. */\nconst useCloseHandlers = ({\n isPrimary,\n closeOnClickOutside,\n closeOnEsc,\n canGoBack,\n onClose,\n}: CloseHandlerOptions): {\n handleBack: () => void;\n handleClose: (event: object, reason: 'backdropClick' | 'escapeKeyDown') => void;\n} => ({\n handleBack: () => {\n if (canGoBack) onClose();\n },\n handleClose: (_event, reason) => {\n if (!isPrimary) return;\n if (reason === 'backdropClick' && !closeOnClickOutside) return;\n if (reason === 'escapeKeyDown' && !closeOnEsc) return;\n onClose();\n },\n});\n\n/** The z-index this panel was assigned when it joined the stack. */\nconst usePanelZIndex = (modalId: string, fallback: number): number => {\n const { stack } = useModalStack();\n return stack.find((m) => m.id === modalId)?.zIndex ?? fallback;\n};\n\n/**\n * This modal's role in the stack, plus whether it is mid-transition between two\n * roles (which drives the expand/contract animation).\n */\nconst useModalRole = (\n open: boolean,\n modalId: string,\n): { modalRole: ModalPanelRole; isAnimating: boolean } => {\n const { stack } = useModalStack();\n const [modalRole, setModalRole] = useState<ModalPanelRole>('primary');\n const [isAnimating, setIsAnimating] = useState(false);\n\n useStackRegistration(open, modalId);\n\n useEffect(() => {\n if (!open) return;\n\n const newRole = stack.find((m) => m.id === modalId)?.role;\n if (!newRole || newRole === modalRole) return;\n\n setIsAnimating(true);\n setModalRole(newRole);\n const timer = window.setTimeout(() => setIsAnimating(false), ROLE_TRANSITION_MS);\n return () => window.clearTimeout(timer);\n }, [stack, modalId, open, modalRole]);\n\n return { modalRole, isAnimating };\n};\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/feedback/StackedModal/StackedModal.tsx"],"sourcesContent":["import Dialog from '@mui/material/Dialog/index.js';\nimport Slide from '@mui/material/Slide/index.js';\nimport { styled } from '@mui/material/styles/index.js';\nimport type { TransitionProps } from '@mui/material/transitions/index.js';\nimport type { FC } from 'react';\nimport React, { forwardRef, useId } from 'react';\nimport { usePanelChrome } from './StackedModal.hooks';\nimport { modalAriaIds, ModalPanelContents, resolveModalContent } from './StackedModal.parts';\nimport type { PanelStyleProps } from './StackedModal.styles';\nimport { dialogRootStyles, STYLE_ONLY_PROPS } from './StackedModal.styles';\nimport type { StackedModalProps } from './StackedModal.types';\n\nconst StyledDialog = styled(Dialog, {\n shouldForwardProp: (prop) => !STYLE_ONLY_PROPS.includes(prop as string),\n})<PanelStyleProps>(({ theme, ...props }) => dialogRootStyles(theme, props));\n\n/** Panels slide in from the right on every device. */\nconst SlideTransition = forwardRef<HTMLDivElement, TransitionProps>(\n function SlideTransition(props, ref) {\n return (\n <Slide direction=\"left\" ref={ref} {...props}>\n {props.children as React.ReactElement}\n </Slide>\n );\n },\n);\n\nconst DEFAULTS: Partial<StackedModalProps> = {\n open: false,\n glass: false,\n navigationTitle: '',\n closeOnClickOutside: true,\n closeOnEsc: true,\n loading: false,\n loadingText: 'Loading...',\n maxWidth: false,\n disableBackdrop: false,\n disableFocusTrap: false,\n keepMounted: false,\n rtl: false,\n};\n\n/**\n * Fills in the defaults for props the caller left `undefined`. A table plus one\n * loop, rather than a default per destructured prop — each of those is a branch,\n * and fifteen of them put the component over the complexity bar on their own.\n */\nconst withDefaults = (props: StackedModalProps): StackedModalProps => {\n const resolved = { ...props };\n for (const [key, value] of Object.entries(DEFAULTS)) {\n const prop = key as keyof StackedModalProps;\n if (resolved[prop] === undefined) {\n Object.assign(resolved, { [prop]: value });\n }\n }\n return resolved;\n};\n\n/** The modal's props once defaults are applied and its stack identity is settled. */\ntype ResolvedProps = StackedModalProps & { modalId: string };\n\nconst StackedModalPanel: FC<ResolvedProps> = (props) => {\n const {\n open, onClose, glass, navigationTitle, backLabel, hideClose, children, actions, modalId,\n closeOnClickOutside, closeOnEsc, loading, loadingText, fullScreen, maxWidth, size, disableBackdrop,\n disableFocusTrap, keepMounted, rtl, dataTestId,\n 'aria-labelledby': ariaLabelledBy, 'aria-describedby': ariaDescribedBy, ...otherProps\n } = props;\n\n const {\n dialogRef, modalRole, isAnimating, showSkeleton, zIndex, isMobile, canGoBack,\n handleBack, handleClose,\n } = usePanelChrome({\n open, modalId, loading, closeOnEsc, closeOnClickOutside, disableFocusTrap, onClose,\n });\n\n // Don't render if in background (performance optimization)\n if (modalRole === 'background' && !keepMounted) return null;\n\n const { content, modalActions, headerActions, hasMobileActions } = resolveModalContent(\n children,\n actions,\n isMobile,\n );\n const { titleId, descId } = modalAriaIds(modalId, ariaLabelledBy, ariaDescribedBy);\n\n return (\n <StyledDialog\n ref={dialogRef}\n open={Boolean(open)}\n onClose={handleClose}\n fullScreen={fullScreen ?? isMobile}\n modalRole={modalRole}\n isAnimating={isAnimating}\n glass={glass}\n rtl={rtl}\n customMaxWidth={maxWidth}\n panelSize={size}\n TransitionComponent={SlideTransition}\n maxWidth={maxWidth}\n disableEscapeKeyDown={!closeOnEsc}\n hideBackdrop={disableBackdrop}\n keepMounted={keepMounted}\n aria-labelledby={titleId}\n aria-describedby={descId}\n data-testid={dataTestId}\n sx={{ zIndex }}\n {...otherProps}\n >\n <ModalPanelContents\n backLabel={backLabel}\n showSkeleton={showSkeleton}\n loadingText={loadingText}\n titleId={titleId}\n descId={descId}\n navigationTitle={navigationTitle}\n hideClose={hideClose}\n canGoBack={canGoBack}\n onBack={handleBack}\n onClose={onClose}\n headerActions={headerActions}\n content={content}\n modalActions={modalActions}\n hasMobileActions={hasMobileActions}\n rtl={rtl}\n dataTestId={dataTestId}\n />\n </StyledDialog>\n );\n};\n\nexport const StackedModal: FC<StackedModalProps> = (props) => {\n // A modal with no caller-supplied id still needs a STABLE one — it keys this\n // panel's entry in the stack, so a value that changed per render (as a\n // `Math.random()` default does) would re-register it on every pass.\n const fallbackId = useId();\n const resolved = withDefaults(props);\n return <StackedModalPanel {...resolved} modalId={resolved.modalId ?? fallbackId} />;\n};\n\n// Re-exported so `./StackedModal` stays the single public entry point for the\n// component and its stack primitives, as the barrel and existing imports expect.\nexport {\n ModalStackProvider,\n ModalStackProvider as StackedModalProvider,\n useModalStack,\n useModalStack as useStackedModal,\n} from './modal-stack';\nexport { StackedModalActions, StackedModalContent } from './StackedModal.parts';\n"],"mappings":"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","names":["SlideTransition"]}