@12-apps/ui 6.5.0 → 6.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/dist/{chunk-3KWD5Y5O.js → chunk-RXE5CUUE.js} +8 -4
  2. package/dist/chunk-RXE5CUUE.js.map +1 -0
  3. package/dist/data-display/Alert.js +1 -1
  4. package/dist/data-display/AsyncStateContainer.js +2 -1
  5. package/dist/data-display/AsyncStateContainer.js.map +1 -1
  6. package/dist/data-display/Banner.js +4 -2
  7. package/dist/data-display/Banner.js.map +1 -1
  8. package/dist/data-display/CardKit.js +3 -1
  9. package/dist/data-display/CardKit.js.map +1 -1
  10. package/dist/data-display/Carousel.js +5 -2
  11. package/dist/data-display/Carousel.js.map +1 -1
  12. package/dist/data-display/DataViews.js +8 -7
  13. package/dist/data-display/DataViews.js.map +1 -1
  14. package/dist/data-display/Lightbox.js +147 -117
  15. package/dist/data-display/Lightbox.js.map +1 -1
  16. package/dist/data-display/MapPreview.js +14 -10
  17. package/dist/data-display/MapPreview.js.map +1 -1
  18. package/dist/pt-BR.js +46 -3
  19. package/dist/pt-BR.js.map +1 -1
  20. package/dist/types/components/data-display/Alert/Alert.d.ts +9 -2
  21. package/dist/types/components/data-display/Alert/Alert.d.ts.map +1 -1
  22. package/dist/types/components/data-display/Alert/Alert.types.d.ts +25 -9
  23. package/dist/types/components/data-display/Alert/Alert.types.d.ts.map +1 -1
  24. package/dist/types/components/data-display/AsyncStateContainer/AsyncStateContainer.d.ts.map +1 -1
  25. package/dist/types/components/data-display/AsyncStateContainer/AsyncStateContainer.types.d.ts +6 -0
  26. package/dist/types/components/data-display/AsyncStateContainer/AsyncStateContainer.types.d.ts.map +1 -1
  27. package/dist/types/components/data-display/Banner/Banner.d.ts.map +1 -1
  28. package/dist/types/components/data-display/Banner/Banner.types.d.ts +2 -0
  29. package/dist/types/components/data-display/Banner/Banner.types.d.ts.map +1 -1
  30. package/dist/types/components/data-display/CardKit/card-actions-context.d.ts +3 -1
  31. package/dist/types/components/data-display/CardKit/card-actions-context.d.ts.map +1 -1
  32. package/dist/types/components/data-display/Carousel/Carousel.chrome.d.ts +3 -0
  33. package/dist/types/components/data-display/Carousel/Carousel.chrome.d.ts.map +1 -1
  34. package/dist/types/components/data-display/Carousel/Carousel.parts.d.ts.map +1 -1
  35. package/dist/types/components/data-display/Carousel/Carousel.types.d.ts +7 -0
  36. package/dist/types/components/data-display/Carousel/Carousel.types.d.ts.map +1 -1
  37. package/dist/types/components/data-display/DataViews/data-views-copy.d.ts +25 -0
  38. package/dist/types/components/data-display/DataViews/data-views-copy.d.ts.map +1 -1
  39. package/dist/types/components/data-display/DataViews/data-views-layout-context.d.ts.map +1 -1
  40. package/dist/types/components/data-display/DataViews/data-views-table-parts.d.ts.map +1 -1
  41. package/dist/types/components/data-display/Lightbox/Lightbox.d.ts.map +1 -1
  42. package/dist/types/components/data-display/Lightbox/Lightbox.types.d.ts +9 -0
  43. package/dist/types/components/data-display/Lightbox/Lightbox.types.d.ts.map +1 -1
  44. package/dist/types/components/data-display/Lightbox/LightboxControls.d.ts.map +1 -1
  45. package/dist/types/components/data-display/Lightbox/lightbox-copy-context.d.ts +17 -0
  46. package/dist/types/components/data-display/Lightbox/lightbox-copy-context.d.ts.map +1 -0
  47. package/dist/types/components/data-display/MapPreview/MapControlBar.d.ts +7 -0
  48. package/dist/types/components/data-display/MapPreview/MapControlBar.d.ts.map +1 -1
  49. package/dist/types/components/data-display/MapPreview/MapLayers.d.ts.map +1 -1
  50. package/dist/types/components/data-display/MapPreview/MapPreview.types.d.ts +9 -0
  51. package/dist/types/components/data-display/MapPreview/MapPreview.types.d.ts.map +1 -1
  52. package/dist/types/components/data-display/MapPreview/MapSearchBar.d.ts +2 -0
  53. package/dist/types/components/data-display/MapPreview/MapSearchBar.d.ts.map +1 -1
  54. package/dist/types/copy.d.ts +53 -0
  55. package/dist/types/copy.d.ts.map +1 -1
  56. package/dist/types/pt-BR.d.ts +5 -1
  57. package/dist/types/pt-BR.d.ts.map +1 -1
  58. package/package.json +1 -1
  59. package/src/components/data-display/Alert/Alert.tsx +22 -5
  60. package/src/components/data-display/Alert/Alert.types.ts +20 -10
  61. package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.tsx +2 -1
  62. package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.types.ts +6 -0
  63. package/src/components/data-display/Banner/Banner.tsx +5 -2
  64. package/src/components/data-display/Banner/Banner.types.ts +2 -0
  65. package/src/components/data-display/CardKit/card-actions-context.tsx +4 -0
  66. package/src/components/data-display/Carousel/Carousel.chrome.tsx +5 -0
  67. package/src/components/data-display/Carousel/Carousel.parts.tsx +4 -3
  68. package/src/components/data-display/Carousel/Carousel.types.ts +7 -0
  69. package/src/components/data-display/DataViews/data-views-copy.ts +25 -0
  70. package/src/components/data-display/DataViews/data-views-display-tabs.tsx +2 -2
  71. package/src/components/data-display/DataViews/data-views-export.tsx +1 -1
  72. package/src/components/data-display/DataViews/data-views-layout-context.tsx +3 -1
  73. package/src/components/data-display/DataViews/data-views-more-filters.tsx +2 -2
  74. package/src/components/data-display/DataViews/data-views-range-pill.tsx +1 -1
  75. package/src/components/data-display/DataViews/data-views-table-parts.tsx +1 -0
  76. package/src/components/data-display/Lightbox/Lightbox.tsx +3 -0
  77. package/src/components/data-display/Lightbox/Lightbox.types.ts +9 -0
  78. package/src/components/data-display/Lightbox/LightboxControls.tsx +28 -15
  79. package/src/components/data-display/Lightbox/lightbox-copy-context.tsx +42 -0
  80. package/src/components/data-display/MapPreview/MapControlBar.tsx +17 -9
  81. package/src/components/data-display/MapPreview/MapLayers.tsx +2 -0
  82. package/src/components/data-display/MapPreview/MapPreview.types.ts +10 -0
  83. package/src/components/data-display/MapPreview/MapSearchBar.tsx +4 -1
  84. package/src/copy.ts +57 -0
  85. package/src/pt-BR.ts +48 -1
  86. package/dist/chunk-3KWD5Y5O.js.map +0 -1
@@ -351,12 +351,13 @@ var AlertContent = ({ title, description, dataTestId, children }) => /* @__PURE_
351
351
  ] });
352
352
  var AlertCloseButton = ({
353
353
  dataTestId,
354
+ label,
354
355
  onClose
355
356
  }) => /* @__PURE__ */ jsx2(
356
357
  IconButton,
357
358
  {
358
359
  "data-testid": testIdFor(dataTestId, "close"),
359
- "aria-label": "close alert",
360
+ "aria-label": label,
360
361
  color: "inherit",
361
362
  size: "small",
362
363
  onClick: onClose,
@@ -377,6 +378,10 @@ var AlertCloseButton = ({
377
378
  children: /* @__PURE__ */ jsx2(Close, { fontSize: "inherit" })
378
379
  }
379
380
  );
381
+ function dismissButton(props, dataTestId, onClose) {
382
+ if (!props.closable) return null;
383
+ return /* @__PURE__ */ jsx2(AlertCloseButton, { dataTestId, label: props.closeLabel, onClose });
384
+ }
380
385
  var Alert = React.forwardRef(
381
386
  (alertProps, ref) => {
382
387
  const {
@@ -386,7 +391,6 @@ var Alert = React.forwardRef(
386
391
  pulse,
387
392
  icon,
388
393
  showIcon,
389
- closable,
390
394
  onClose,
391
395
  title,
392
396
  description,
@@ -426,7 +430,7 @@ var Alert = React.forwardRef(
426
430
  "aria-live": ariaLive,
427
431
  "aria-atomic": ariaAtomic,
428
432
  tabIndex: 0,
429
- action: closable && /* @__PURE__ */ jsx2(AlertCloseButton, { dataTestId, onClose: handleClose }),
433
+ action: dismissButton(alertProps, dataTestId, handleClose),
430
434
  ...props,
431
435
  children: content
432
436
  }
@@ -438,4 +442,4 @@ Alert.displayName = "Alert";
438
442
  export {
439
443
  Alert
440
444
  };
441
- //# sourceMappingURL=chunk-3KWD5Y5O.js.map
445
+ //# sourceMappingURL=chunk-RXE5CUUE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/data-display/Alert/Alert.tsx","../src/components/data-display/Alert/Alert.styles.tsx"],"sourcesContent":["import Close from '@mui/icons-material/Close';\nimport {\n Alert as MuiAlert,\n AlertTitle,\n alpha,\n Box,\n Collapse,\n IconButton } from '@mui/material';\nimport { styled } from '@mui/material';\nimport React from 'react';\n\nimport {\n alertEmphasisStyles,\n alertLayoutStyles,\n alertVariantStyles,\n fadeInScale,\n getColorFromTheme,\n getVariantIcon } from './Alert.styles';\nimport type { AlertColor } from '@mui/material';\nimport type { AlertProps } from './Alert.types';\n\n// Define animations\nconst StyledAlert = styled(MuiAlert, {\n shouldForwardProp: (prop) =>\n !['customVariant', 'customColor', 'glow', 'pulse', 'animate'].includes(prop as string) })<{\n customVariant?: string;\n customColor?: string;\n glow?: boolean;\n pulse?: boolean;\n animate?: boolean;\n}>(({ theme, customVariant, customColor, glow, pulse, animate }) => {\n const colorPalette = getColorFromTheme(theme, customColor || customVariant || 'info');\n\n return {\n borderRadius: theme.spacing(1.5),\n transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',\n position: 'relative',\n overflow: 'hidden',\n animation: animate ? `${fadeInScale} 0.3s ease-out` : 'none',\n willChange: 'transform, opacity',\n\n ...alertLayoutStyles(theme, colorPalette, animate),\n\n // Hover effects\n '&:hover': {\n transform: 'translateY(-3px) scale(1.01)',\n boxShadow: `0 8px 20px ${alpha(colorPalette.main, 0.2)}`,\n transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',\n\n '.MuiAlert-icon': {\n transform: 'scale(1.15) rotate(10deg)' },\n\n '&::before': {\n opacity: 1 } },\n\n // Active state\n '&:active': {\n transform: 'translateY(-1px) scale(0.99)',\n transition: 'transform 0.1s ease' },\n\n // Focus styles for accessibility\n '&:focus-within': {\n outline: `3px solid ${alpha(colorPalette.main, 0.5)}`,\n outlineOffset: '3px',\n boxShadow: `0 0 0 6px ${alpha(colorPalette.main, 0.1)}`,\n transition: 'all 0.2s ease' },\n\n ...alertVariantStyles(theme, customVariant, colorPalette),\n ...alertEmphasisStyles(colorPalette, Boolean(glow), Boolean(pulse)) };\n});\n\nconst ALERT_DEFAULTS = {\n variant: 'info',\n glow: false,\n pulse: false,\n showIcon: true,\n closable: false,\n animate: true,\n role: 'alert',\n 'aria-atomic': 'true',\n 'data-testid': 'alert' } satisfies Partial<AlertProps>;\n\n// Strips explicitly-undefined props before the merge so `prop={undefined}` still\n// falls back to the default, the way a destructuring default would.\nconst definedProps = (props: AlertProps): Partial<AlertProps> =>\n Object.fromEntries(\n Object.entries(props).filter(([, value]) => value !== undefined),\n ) as Partial<AlertProps>;\n\nconst testIdFor = (base: string | undefined, suffix: string) =>\n base ? `${base}-${suffix}` : `alert-${suffix}`;\n\n// glass and gradient are our own looks with no MUI severity of their own; both\n// borrow info's. danger is MUI's error.\nconst MUI_SEVERITY: Record<string, AlertColor> = {\n danger: 'error',\n glass: 'info',\n gradient: 'info' };\n\nconst toMuiSeverity = (variant: string): AlertColor =>\n MUI_SEVERITY[variant] ?? (variant as AlertColor);\n\nconst AlertContent: React.FC<{\n title?: AlertProps['title'];\n description?: AlertProps['description'];\n dataTestId?: string;\n children?: React.ReactNode;\n}> = ({ title, description, dataTestId, children }) => (\n <>\n {title && (\n <AlertTitle\n data-testid={testIdFor(dataTestId, 'title')}\n sx={{\n fontWeight: 600,\n fontSize: '1.05rem',\n marginBottom: description ? 0.5 : 0,\n wordWrap: 'break-word',\n overflowWrap: 'break-word',\n wordBreak: 'break-word' }}\n >\n {title}\n </AlertTitle>\n )}\n {description && (\n <Box\n component=\"div\"\n data-testid={testIdFor(dataTestId, 'message')}\n sx={{\n opacity: 0.9,\n fontSize: '0.925rem',\n wordWrap: 'break-word',\n overflowWrap: 'break-word',\n wordBreak: 'break-word' }}\n >\n {description}\n </Box>\n )}\n {children}\n </>\n);\n\nconst AlertCloseButton: React.FC<{ dataTestId?: string; label: string; onClose: () => void }> = ({\n dataTestId,\n label,\n onClose }) => (\n <IconButton\n data-testid={testIdFor(dataTestId, 'close')}\n aria-label={label}\n color=\"inherit\"\n size=\"small\"\n onClick={onClose}\n sx={(theme) => ({\n transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',\n opacity: 0.7,\n '&:hover': {\n transform: 'rotate(90deg) scale(1.1)',\n opacity: 1,\n backgroundColor: alpha(theme.palette.action.hover, 0.1) },\n '&:focus': {\n opacity: 1,\n outline: 'none',\n backgroundColor: alpha(theme.palette.action.focus, 0.1) } })}\n >\n <Close fontSize=\"inherit\" />\n </IconButton>\n);\n\n/**\n * The dismiss button, or nothing.\n *\n * Reads `closeLabel` straight off the props rather than through the\n * `definedProps` merge, which would widen it to `string | undefined` and hand\n * the button an empty name. The UNION in `AlertProps` guarantees the label\n * wherever `closable` is true — that narrowing is gone inside the component,\n * which is why the fallback below exists and why it is unreachable.\n */\nfunction dismissButton(\n props: AlertProps,\n dataTestId: string | undefined,\n onClose: () => void,\n): React.JSX.Element | null {\n if (!props.closable) return null;\n return <AlertCloseButton dataTestId={dataTestId} label={props.closeLabel} onClose={onClose} />;\n}\n\nexport const Alert = React.forwardRef<HTMLDivElement, AlertProps>(\n (\n alertProps,\n ref,\n ) => {\n const {\n variant,\n color,\n glow,\n pulse,\n icon,\n showIcon,\n onClose,\n title,\n description,\n children,\n animate,\n role,\n 'aria-atomic': ariaAtomic,\n 'data-testid': dataTestId,\n ...props\n } = { ...ALERT_DEFAULTS, ...definedProps(alertProps) };\n\n // Depends on `variant`, so it cannot live in the static defaults above.\n const ariaLive = alertProps['aria-live'] ?? (variant === 'danger' ? 'assertive' : 'polite');\n const [open, setOpen] = React.useState(true);\n const [isClosing, setIsClosing] = React.useState(false);\n\n const handleClose = () => {\n setIsClosing(true);\n window.setTimeout(() => {\n setOpen(false);\n onClose?.();\n }, 200);\n };\n\n const severity = toMuiSeverity(variant);\n\n const displayIcon = showIcon ? (\n <Box component=\"span\" data-testid={testIdFor(dataTestId, 'icon')}>\n {icon || getVariantIcon(variant)}\n </Box>\n ) : false;\n\n const content = (\n <AlertContent title={title} description={description} dataTestId={dataTestId}>\n {children}\n </AlertContent>\n );\n\n return (\n <Collapse in={open && !isClosing} timeout={300}>\n <StyledAlert\n ref={ref}\n data-testid={dataTestId || 'alert'}\n severity={severity}\n customVariant={variant}\n customColor={color}\n glow={glow}\n pulse={pulse}\n animate={animate}\n icon={displayIcon}\n role={role}\n aria-live={ariaLive}\n aria-atomic={ariaAtomic}\n tabIndex={0}\n action={dismissButton(alertProps, dataTestId, handleClose)}\n {...props}\n >\n {content}\n </StyledAlert>\n </Collapse>\n );\n },\n);\n\nAlert.displayName = 'Alert';\n","import Error from '@mui/icons-material/Error';\nimport Info from '@mui/icons-material/Info';\nimport CheckCircle from '@mui/icons-material/CheckCircle';\nimport Warning from '@mui/icons-material/Warning';\nimport { alpha, darken, keyframes, lighten } from '@mui/material';\nimport type { CSSObject, Theme } from '@mui/material';\nimport React from 'react';\n\nexport const pulseAnimation = keyframes`\n 0% {\n box-shadow: 0 0 0 0 currentColor;\n opacity: 1;\n }\n 70% {\n box-shadow: 0 0 0 10px currentColor;\n opacity: 0;\n }\n 100% {\n box-shadow: 0 0 0 0 currentColor;\n opacity: 0;\n }\n`;\n\n// Additional animations can be enabled as needed\n// const slideInAnimation = keyframes`\n// from {\n// transform: translateX(-100%);\n// opacity: 0;\n// }\n// to {\n// transform: translateX(0);\n// opacity: 1;\n// }\n// `;\n\n// const bounceIn = keyframes`\n// 0% {\n// transform: scale(0.3);\n// opacity: 0;\n// }\n// 50% {\n// transform: scale(1.05);\n// }\n// 70% {\n// transform: scale(0.9);\n// }\n// 100% {\n// transform: scale(1);\n// opacity: 1;\n// }\n// `;\n\n// Removed unused slideInAnimation - can be re-added if needed for future features\n\nexport const shimmerAnimation = keyframes`\n 0% {\n background-position: -1000px 0;\n }\n 100% {\n background-position: 1000px 0;\n }\n`;\n\nexport const fadeInScale = keyframes`\n from {\n opacity: 0;\n transform: scale(0.95) translateY(-10px);\n }\n to {\n opacity: 1;\n transform: scale(1) translateY(0);\n }\n`;\n\nexport const iconRotate = keyframes`\n 0% {\n transform: rotate(0deg) scale(0.8);\n }\n 50% {\n transform: rotate(180deg) scale(1.1);\n }\n 100% {\n transform: rotate(360deg) scale(1);\n }\n`;\n\nexport const getColorFromTheme = (theme: Theme, variant: string) => {\n const colorMap: Record<string, { main: string; light?: string; dark?: string }> = {\n info: theme.palette.info,\n success: theme.palette.success,\n warning: theme.palette.warning,\n danger: theme.palette.error,\n primary: theme.palette.primary,\n secondary: theme.palette.secondary,\n neutral: {\n main: theme.palette.grey[500] || '#9E9E9E',\n light: theme.palette.grey[300] || '#E0E0E0',\n dark: theme.palette.grey[700] || '#616161',\n },\n };\n\n return colorMap[variant] || theme.palette.info;\n};\n\nexport const getVariantIcon = (variant: string) => {\n const iconMap: Record<string, React.ReactNode> = {\n info: <Info />,\n success: <CheckCircle />,\n warning: <Warning />,\n danger: <Error />,\n };\n\n return iconMap[variant];\n};\n\n// The palette shape getColorFromTheme returns — a main plus optional light/dark,\n// which is narrower than MUI's PaletteColor.\nexport type AlertPalette = { main: string; light?: string; dark?: string };\n\n/** The four semantic variants, which differ only in which palette they read. */\nconst SEMANTIC_VARIANTS = new Set(['info', 'success', 'warning', 'danger']);\n\n/**\n * The room the message is given — padding, the stack's rhythm, and where the\n * icon and the close button sit relative to it.\n *\n * Extracted for the same reason {@link alertVariantStyles} and\n * {@link alertEmphasisStyles} were: `styled()`'s callback is one function, and\n * every block written inline is counted against its length by the complexity\n * gate. Keeping the layout here means the callback assembles four named pieces\n * instead of carrying all of them.\n */\nexport const alertLayoutStyles = (\n theme: Theme,\n colorPalette: AlertPalette,\n animate: boolean | undefined,\n): CSSObject => ({\n // ROOM TO READ.\n //\n // MUI's `6px 16px` is sized for one line of text and nothing else. This Alert\n // routinely carries three things stacked — a sentence, a smaller explanation,\n // then a control — and at 6px they touched top and bottom and ran together\n // vertically, so the whole card read as one dense block instead of as a\n // message with parts. The generosity is what makes the structure legible; it\n // is not decoration.\n //\n // Padding stays overridable by an `sx` (a full-bleed strip legitimately wants\n // it tight), because this is a default rather than a rule.\n padding: theme.spacing(1.75, 2),\n\n '.MuiAlert-message': {\n display: 'flex',\n flexDirection: 'column',\n // 0.5 put a title, its explanation and a button 4px apart, which reads as a\n // spacing bug rather than as a group. 1 separates the lines; the extra\n // margin below goes to whatever CONTROL sits at the end, because the gap\n // between prose and a thing you press has to be bigger than the gap between\n // two lines of prose or the button looks like part of the text.\n gap: theme.spacing(1),\n fontSize: '0.95rem',\n lineHeight: 1.5,\n // No padding of its own — the root's is now doing that job, and MUI's\n // default `8px 0` on top of it would double the vertical space.\n padding: 0,\n // A CONTROL ON A TINTED PANEL NEEDS AN EDGE.\n //\n // `ghost` and `text` paint no background and no border — bare labels, which\n // works on white where the surrounding page is obviously not clickable.\n // Inside a coloured Alert it stops working: the label is one more coloured\n // phrase among several, and a \"Cancelar\" beside two lines of prose reads as\n // part of the prose.\n //\n // Only the BORDER is set, never the background or the colour, and that is\n // what makes it safe to apply to every button rather than just the bare\n // ones: a `solid` keeps its fill and its light label and gains a hairline\n // in the same hue, while a `ghost` gains the whole affordance.\n '.MuiButton-root': {\n marginTop: theme.spacing(0.5),\n border: `1px solid ${alpha(colorPalette.main, 0.45)}` },\n },\n\n '.MuiAlert-icon': {\n transition: 'transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)',\n alignItems: 'center',\n // Clear of the words rather than nearly touching them, and aligned to the\n // FIRST line instead of centred on the whole block — an icon floating\n // halfway down a three-line message points at nothing.\n alignSelf: 'flex-start',\n marginRight: theme.spacing(1.75),\n paddingTop: theme.spacing(0.25),\n animation: animate ? `${iconRotate} 0.6s ease-out` : 'none' },\n\n // The close button, kept off the text it sits beside.\n '.MuiAlert-action': {\n alignItems: 'flex-start',\n paddingLeft: theme.spacing(2) },\n});\n\n/**\n * A semantic Alert's surface and ink.\n *\n * ## Why this is not `alpha(main, 0.1)`\n *\n * It used to be, four times over, with `color: main` for the text. Both halves\n * were wrong, and independently so.\n *\n * The fill was TRANSLUCENT. A 10% wash only reads as a banner when something\n * opaque is behind it, so the component silently depended on where a consumer\n * put it. Placed over content — a floating notice, an invite anchored above a\n * catalogue — the page came straight through and the sentence interleaved with\n * whatever it was over. That is not a faint banner; it is an unreadable one,\n * and nothing in the component said so.\n *\n * The ink was the SEVERITY HUE at body size. `#0288d1`, this library's info\n * blue, is 3.03:1 on white — under the 4.5:1 WCAG AA floor before any wash is\n * involved. So the text failed on a plain white page too, where the fill was\n * behaving exactly as intended.\n *\n * ## Why `lighten`/`darken` rather than a hand-picked pair\n *\n * These are the same transforms MUI's own `standard` Alert uses, at the same\n * ratios — the recipe this component overrode. Reusing it means an OPAQUE tint\n * with a dark ink of the same hue, contrast that holds on any surface because\n * it no longer depends on one, and a pairing already proven across MUI's\n * palette rather than eyeballed here. Both modes are handled: on a dark theme\n * the tint darkens and the ink lightens, which an alpha wash could never do\n * because it carries the seed's own lightness through unchanged.\n *\n * The border keeps an alpha — it is a hairline over the tint it belongs to, so\n * translucency there costs nothing and lets the edge follow the fill.\n */\nconst semanticSurface = (theme: Theme, colorPalette: AlertPalette): CSSObject => {\n const dark = theme.palette.mode === 'dark';\n return {\n backgroundColor: dark ? darken(colorPalette.main, 0.8) : lighten(colorPalette.main, 0.9),\n color: dark ? lighten(colorPalette.main, 0.6) : darken(colorPalette.main, 0.6),\n border: `1px solid ${alpha(colorPalette.main, 0.35)}`,\n '.MuiAlert-icon': {\n color: colorPalette.main,\n },\n };\n};\n\n// The six visual variants. Each block is spread in only when it matches, which\n// is how the original inline version read; keeping that shape here means the\n// styled() callback carries one branch instead of six. The four semantics were\n// four IDENTICAL copies of the same block — they now share one, so the next\n// change to a banner's surface cannot land on three of them.\nexport const alertVariantStyles = (\n theme: Theme,\n customVariant: string | undefined,\n colorPalette: AlertPalette,\n): CSSObject => ({\n...(customVariant !== undefined &&\n SEMANTIC_VARIANTS.has(customVariant) &&\n semanticSurface(theme, colorPalette)),\n\n...(customVariant === 'glass' && {\n // 85%, not 10%. This is the variant that is MEANT to be see-through, and it\n // was the same mistake in a costume: at 0.1 the blur had almost nothing to\n // sit on, so `glass` was not frosted, it was a window. A frosted pane has to\n // be mostly pane — the backdrop should be legible AS texture behind the\n // text, never as competition with it.\n backgroundColor: alpha(theme.palette.background.paper, 0.85),\n backdropFilter: 'blur(20px) saturate(180%)',\n WebkitBackdropFilter: 'blur(20px) saturate(180%)',\n border: `1px solid ${alpha(theme.palette.divider, 0.4)}`,\n color: theme.palette.text.primary,\n '.MuiAlert-icon': {\n color: theme.palette.primary.main,\n },\n // Safari before 18 and Firefox with the filter disabled paint the declared\n // alpha with nothing blurred behind it — the see-through banner this is\n // fixing. There is no degraded frost to fall back to, so it falls back to an\n // opaque pane: less pretty, still readable.\n '@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)))': {\n backgroundColor: theme.palette.background.paper,\n },\n}),\n\n...(customVariant === 'gradient' && {\n background: `linear-gradient(135deg, ${alpha(colorPalette.light || colorPalette.main, 0.9)}, ${alpha(colorPalette.dark || colorPalette.main, 0.9)})`,\n color: theme.palette.getContrastText(colorPalette.main),\n border: 'none',\n position: 'relative',\n overflow: 'hidden',\n '.MuiAlert-icon': {\n color: theme.palette.getContrastText(colorPalette.main),\n },\n '&::before': {\n content: '\"\"',\n position: 'absolute',\n top: 0,\n left: '-1000px',\n width: '100%',\n height: '100%',\n background: `linear-gradient(90deg, transparent, ${alpha(theme.palette.common.white, 0.2)}, transparent)`,\n animation: `${shimmerAnimation} 3s infinite`,\n },\n '&:hover': {\n filter: 'brightness(1.1)',\n transform: 'translateY(-2px) scale(1.01)',\n },\n}),\n});\n\n// glow and pulse combine into three distinct looks, so the combinations are\n// spelled out rather than layered — layering them would let the glow-only shadow\n// leak into the glow+pulse case.\nexport const alertEmphasisStyles = (\n colorPalette: AlertPalette,\n glow: boolean,\n pulse: boolean,\n): CSSObject => ({\n...(glow &&\n !pulse && {\n boxShadow: `0 0 20px 5px ${alpha(colorPalette.main, 0.3)} !important`,\n filter: 'brightness(1.05)',\n }),\n\n// Pulse animation\n...(pulse &&\n !glow && {\n position: 'relative',\n '&::after': {\n content: '\"\"',\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n borderRadius: 'inherit',\n backgroundColor: colorPalette.main,\n opacity: 0.2,\n animation: `${pulseAnimation} 2s infinite`,\n pointerEvents: 'none',\n zIndex: -1,\n },\n }),\n\n// Both glow and pulse\n...(glow &&\n pulse && {\n position: 'relative',\n boxShadow: `0 0 20px 5px ${alpha(colorPalette.main, 0.3)} !important`,\n filter: 'brightness(1.05)',\n '&::after': {\n content: '\"\"',\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n borderRadius: 'inherit',\n backgroundColor: colorPalette.main,\n opacity: 0.2,\n animation: `${pulseAnimation} 2s infinite`,\n pointerEvents: 'none',\n zIndex: -1,\n },\n }),\n});\n"],"mappings":"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","names":["alpha","jsx","alpha"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Alert
3
- } from "../chunk-3KWD5Y5O.js";
3
+ } from "../chunk-RXE5CUUE.js";
4
4
  import "../chunk-PZ5AY32C.js";
5
5
  export {
6
6
  Alert
@@ -41,6 +41,7 @@ var AsyncStateContainer = React.memo(
41
41
  errorComponent,
42
42
  renderError,
43
43
  emptyComponent,
44
+ emptyTitle,
44
45
  renderEmpty,
45
46
  className,
46
47
  dataTestId
@@ -54,7 +55,7 @@ var AsyncStateContainer = React.memo(
54
55
  return /* @__PURE__ */ jsx(StateBox, { state: "error", ...box, children: errorComponent || renderError?.(message) || /* @__PURE__ */ jsx(ErrorState, { message }) });
55
56
  }
56
57
  if (isEmpty) {
57
- return /* @__PURE__ */ jsx(StateBox, { state: "empty", ...box, children: pickNode(emptyComponent, renderEmpty, /* @__PURE__ */ jsx(EmptyState, { title: "No data available" })) });
58
+ return /* @__PURE__ */ jsx(StateBox, { state: "empty", ...box, children: pickNode(emptyComponent, renderEmpty, /* @__PURE__ */ jsx(EmptyState, { title: emptyTitle })) });
58
59
  }
59
60
  return /* @__PURE__ */ jsx(StateBox, { state: "success", ...box, children });
60
61
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/data-display/AsyncStateContainer/AsyncStateContainer.tsx"],"sourcesContent":["import { Box } from '@mui/material';\nimport React from 'react';\n\nimport { EmptyState } from '../EmptyState/EmptyState';\nimport { ErrorState } from '../ErrorState/ErrorState';\nimport { LoadingState } from '../LoadingState/LoadingState';\n\nimport type { AsyncStateContainerProps } from './AsyncStateContainer.types';\n\n/**\n * AsyncStateContainer - A composition-based wrapper for handling async data states\n *\n * Priority order: loading > error > empty > children\n *\n * @example\n * // With component props (composition pattern)\n * <AsyncStateContainer\n * isLoading={isLoading}\n * error={error}\n * isEmpty={data.length === 0}\n * loadingComponent={<LoadingState variant=\"skeleton\" />}\n * errorComponent={<ErrorState message={error} onRetry={refetch} />}\n * emptyComponent={<EmptyState title=\"No items\" onCreate={handleCreate} />}\n * >\n * <DataGrid data={data} />\n * </AsyncStateContainer>\n *\n * @example\n * // With render props for more control\n * <AsyncStateContainer\n * isLoading={isLoading}\n * error={error}\n * isEmpty={data.length === 0}\n * renderLoading={() => <CustomLoader />}\n * renderError={(error) => <CustomError error={error} />}\n * renderEmpty={() => <CustomEmpty />}\n * >\n * <DataGrid data={data} />\n * </AsyncStateContainer>\n */\n// The four states render the same wrapper, differing only in data-state and\n// what goes inside it.\nconst StateBox: React.FC<{\n state: 'loading' | 'error' | 'empty' | 'success';\n className?: string;\n dataTestId?: string;\n children: React.ReactNode;\n}> = ({ state, className, dataTestId, children }) => (\n <Box\n className={className}\n data-testid={dataTestId || 'async-state-container'}\n data-state={state}\n >\n {children}\n </Box>\n);\n\nconst errorMessageOf = (error: unknown): string => {\n if (!error) return '';\n if (typeof error === 'string') return error;\n if (error instanceof Error) return error.message;\n return 'An unexpected error occurred';\n};\n\n// Each state takes a ready-made node, else a render function, else the default.\nconst pickNode = (\n node: React.ReactNode,\n render: (() => React.ReactNode) | undefined,\n fallback: React.ReactNode,\n): React.ReactNode => node || render?.() || fallback;\n\nexport const AsyncStateContainer: React.FC<AsyncStateContainerProps> = React.memo(\n ({\n isLoading = false,\n error,\n isEmpty = false,\n children,\n loadingComponent,\n renderLoading,\n errorComponent,\n renderError,\n emptyComponent,\n renderEmpty,\n className,\n dataTestId,\n }) => {\n const box = { className, dataTestId };\n\n // Priority: loading > error > empty > children\n if (isLoading) {\n return (\n <StateBox state=\"loading\" {...box}>\n {pickNode(loadingComponent, renderLoading, <LoadingState />)}\n </StateBox>\n );\n }\n\n if (error) {\n const message = errorMessageOf(error);\n return (\n <StateBox state=\"error\" {...box}>\n {errorComponent || renderError?.(message) || <ErrorState message={message} />}\n </StateBox>\n );\n }\n\n if (isEmpty) {\n return (\n <StateBox state=\"empty\" {...box}>\n {pickNode(emptyComponent, renderEmpty, <EmptyState title=\"No data available\" />)}\n </StateBox>\n );\n }\n\n return (\n <StateBox state=\"success\" {...box}>\n {children}\n </StateBox>\n );\n },\n);\n\nAsyncStateContainer.displayName = 'AsyncStateContainer';\n\nexport default AsyncStateContainer;\n"],"mappings":";;;;;;;;;;;;;AAAA,SAAS,WAAW;AACpB,OAAO,WAAW;AA+ChB;AANF,IAAM,WAKD,CAAC,EAAE,OAAO,WAAW,YAAY,SAAS,MAC7C;AAAA,EAAC;AAAA;AAAA,IACC;AAAA,IACA,eAAa,cAAc;AAAA,IAC3B,cAAY;AAAA,IAEX;AAAA;AACH;AAGF,IAAM,iBAAiB,CAAC,UAA2B;AACjD,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,iBAAiB,MAAO,QAAO,MAAM;AACzC,SAAO;AACT;AAGA,IAAM,WAAW,CACf,MACA,QACA,aACoB,QAAQ,SAAS,KAAK;AAErC,IAAM,sBAA0D,MAAM;AAAA,EAC3E,CAAC;AAAA,IACC,YAAY;AAAA,IACZ;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,MAAM;AACJ,UAAM,MAAM,EAAE,WAAW,WAAW;AAGpC,QAAI,WAAW;AACb,aACE,oBAAC,YAAS,OAAM,WAAW,GAAG,KAC3B,mBAAS,kBAAkB,eAAe,oBAAC,gBAAa,CAAE,GAC7D;AAAA,IAEJ;AAEA,QAAI,OAAO;AACT,YAAM,UAAU,eAAe,KAAK;AACpC,aACE,oBAAC,YAAS,OAAM,SAAS,GAAG,KACzB,4BAAkB,cAAc,OAAO,KAAK,oBAAC,cAAW,SAAkB,GAC7E;AAAA,IAEJ;AAEA,QAAI,SAAS;AACX,aACE,oBAAC,YAAS,OAAM,SAAS,GAAG,KACzB,mBAAS,gBAAgB,aAAa,oBAAC,cAAW,OAAM,qBAAoB,CAAE,GACjF;AAAA,IAEJ;AAEA,WACE,oBAAC,YAAS,OAAM,WAAW,GAAG,KAC3B,UACH;AAAA,EAEJ;AACF;AAEA,oBAAoB,cAAc;","names":[]}
1
+ {"version":3,"sources":["../../src/components/data-display/AsyncStateContainer/AsyncStateContainer.tsx"],"sourcesContent":["import { Box } from '@mui/material';\nimport React from 'react';\n\nimport { EmptyState } from '../EmptyState/EmptyState';\nimport { ErrorState } from '../ErrorState/ErrorState';\nimport { LoadingState } from '../LoadingState/LoadingState';\n\nimport type { AsyncStateContainerProps } from './AsyncStateContainer.types';\n\n/**\n * AsyncStateContainer - A composition-based wrapper for handling async data states\n *\n * Priority order: loading > error > empty > children\n *\n * @example\n * // With component props (composition pattern)\n * <AsyncStateContainer\n * isLoading={isLoading}\n * error={error}\n * isEmpty={data.length === 0}\n * loadingComponent={<LoadingState variant=\"skeleton\" />}\n * errorComponent={<ErrorState message={error} onRetry={refetch} />}\n * emptyComponent={<EmptyState title=\"No items\" onCreate={handleCreate} />}\n * >\n * <DataGrid data={data} />\n * </AsyncStateContainer>\n *\n * @example\n * // With render props for more control\n * <AsyncStateContainer\n * isLoading={isLoading}\n * error={error}\n * isEmpty={data.length === 0}\n * renderLoading={() => <CustomLoader />}\n * renderError={(error) => <CustomError error={error} />}\n * renderEmpty={() => <CustomEmpty />}\n * >\n * <DataGrid data={data} />\n * </AsyncStateContainer>\n */\n// The four states render the same wrapper, differing only in data-state and\n// what goes inside it.\nconst StateBox: React.FC<{\n state: 'loading' | 'error' | 'empty' | 'success';\n className?: string;\n dataTestId?: string;\n children: React.ReactNode;\n}> = ({ state, className, dataTestId, children }) => (\n <Box\n className={className}\n data-testid={dataTestId || 'async-state-container'}\n data-state={state}\n >\n {children}\n </Box>\n);\n\nconst errorMessageOf = (error: unknown): string => {\n if (!error) return '';\n if (typeof error === 'string') return error;\n if (error instanceof Error) return error.message;\n return 'An unexpected error occurred';\n};\n\n// Each state takes a ready-made node, else a render function, else the default.\nconst pickNode = (\n node: React.ReactNode,\n render: (() => React.ReactNode) | undefined,\n fallback: React.ReactNode,\n): React.ReactNode => node || render?.() || fallback;\n\nexport const AsyncStateContainer: React.FC<AsyncStateContainerProps> = React.memo(\n ({\n isLoading = false,\n error,\n isEmpty = false,\n children,\n loadingComponent,\n renderLoading,\n errorComponent,\n renderError,\n emptyComponent,\n emptyTitle,\n renderEmpty,\n className,\n dataTestId,\n }) => {\n const box = { className, dataTestId };\n\n // Priority: loading > error > empty > children\n if (isLoading) {\n return (\n <StateBox state=\"loading\" {...box}>\n {pickNode(loadingComponent, renderLoading, <LoadingState />)}\n </StateBox>\n );\n }\n\n if (error) {\n const message = errorMessageOf(error);\n return (\n <StateBox state=\"error\" {...box}>\n {errorComponent || renderError?.(message) || <ErrorState message={message} />}\n </StateBox>\n );\n }\n\n if (isEmpty) {\n return (\n <StateBox state=\"empty\" {...box}>\n {pickNode(emptyComponent, renderEmpty, <EmptyState title={emptyTitle} />)}\n </StateBox>\n );\n }\n\n return (\n <StateBox state=\"success\" {...box}>\n {children}\n </StateBox>\n );\n },\n);\n\nAsyncStateContainer.displayName = 'AsyncStateContainer';\n\nexport default AsyncStateContainer;\n"],"mappings":";;;;;;;;;;;;;AAAA,SAAS,WAAW;AACpB,OAAO,WAAW;AA+ChB;AANF,IAAM,WAKD,CAAC,EAAE,OAAO,WAAW,YAAY,SAAS,MAC7C;AAAA,EAAC;AAAA;AAAA,IACC;AAAA,IACA,eAAa,cAAc;AAAA,IAC3B,cAAY;AAAA,IAEX;AAAA;AACH;AAGF,IAAM,iBAAiB,CAAC,UAA2B;AACjD,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,iBAAiB,MAAO,QAAO,MAAM;AACzC,SAAO;AACT;AAGA,IAAM,WAAW,CACf,MACA,QACA,aACoB,QAAQ,SAAS,KAAK;AAErC,IAAM,sBAA0D,MAAM;AAAA,EAC3E,CAAC;AAAA,IACC,YAAY;AAAA,IACZ;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,MAAM;AACJ,UAAM,MAAM,EAAE,WAAW,WAAW;AAGpC,QAAI,WAAW;AACb,aACE,oBAAC,YAAS,OAAM,WAAW,GAAG,KAC3B,mBAAS,kBAAkB,eAAe,oBAAC,gBAAa,CAAE,GAC7D;AAAA,IAEJ;AAEA,QAAI,OAAO;AACT,YAAM,UAAU,eAAe,KAAK;AACpC,aACE,oBAAC,YAAS,OAAM,SAAS,GAAG,KACzB,4BAAkB,cAAc,OAAO,KAAK,oBAAC,cAAW,SAAkB,GAC7E;AAAA,IAEJ;AAEA,QAAI,SAAS;AACX,aACE,oBAAC,YAAS,OAAM,SAAS,GAAG,KACzB,mBAAS,gBAAgB,aAAa,oBAAC,cAAW,OAAO,YAAY,CAAE,GAC1E;AAAA,IAEJ;AAEA,WACE,oBAAC,YAAS,OAAM,WAAW,GAAG,KAC3B,UACH;AAAA,EAEJ;AACF;AAEA,oBAAoB,cAAc;","names":[]}
@@ -251,7 +251,7 @@ var BannerBody = ({ displayIcon, title, description, dataTestId, children }) =>
251
251
  children
252
252
  ] })
253
253
  ] });
254
- var BannerTrailing = ({ actions, variant, dismissible, onDismissClick, dataTestId }) => /* @__PURE__ */ jsxs(Box, { display: "flex", alignItems: "center", gap: 1, children: [
254
+ var BannerTrailing = ({ actions, variant, dismissible, onDismissClick, dismissLabel, dataTestId }) => /* @__PURE__ */ jsxs(Box, { display: "flex", alignItems: "center", gap: 1, children: [
255
255
  actions && actions.length > 0 && /* @__PURE__ */ jsx(Box, { className: "banner-actions", children: actions.map((action, index) => /* @__PURE__ */ jsx(
256
256
  BannerActionButton,
257
257
  {
@@ -267,7 +267,7 @@ var BannerTrailing = ({ actions, variant, dismissible, onDismissClick, dataTestI
267
267
  className: "banner-dismiss",
268
268
  size: "small",
269
269
  onClick: onDismissClick,
270
- "aria-label": "Dismiss banner",
270
+ "aria-label": dismissLabel,
271
271
  "data-testid": testIdFor(dataTestId, "close"),
272
272
  sx: { transition: "all 0.2s ease" },
273
273
  children: /* @__PURE__ */ jsx(Close, { fontSize: "small" })
@@ -281,6 +281,7 @@ var Banner = React.forwardRef(
281
281
  description,
282
282
  icon,
283
283
  dismissible = false,
284
+ dismissLabel,
284
285
  onDismiss,
285
286
  actions,
286
287
  sticky = false,
@@ -332,6 +333,7 @@ var Banner = React.forwardRef(
332
333
  variant,
333
334
  dismissible,
334
335
  onDismissClick: handleDismiss,
336
+ dismissLabel,
335
337
  dataTestId
336
338
  }
337
339
  )
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/data-display/Banner/Banner.tsx","../../src/components/data-display/Banner/Banner.styles.ts"],"sourcesContent":["import CheckCircle from '@mui/icons-material/CheckCircle';\nimport Close from '@mui/icons-material/Close';\nimport Error from '@mui/icons-material/Error';\nimport Info from '@mui/icons-material/Info';\nimport Warning from '@mui/icons-material/Warning';\nimport { alpha, Box, Button, IconButton, Typography } from '@mui/material';\nimport type { Theme } from '@mui/material';\nimport { styled } from '@mui/material';\nimport React from 'react';\n\nimport { bannerPartStyles, fadeInSlide, getVariantColor } from './Banner.styles';\nimport type { BannerProps, BannerVariant } from './Banner.types';\n\nconst StyledBanner = styled(Box, {\n shouldForwardProp: (prop) => !['variant', 'sticky', 'fullWidth'].includes(prop as string),\n})<{\n variant: BannerVariant;\n sticky: boolean;\n fullWidth: boolean;\n}>(({ theme, variant, sticky, fullWidth }) => {\n\n const colorPalette = getVariantColor(theme, variant);\n\n return {\n display: 'flex',\n alignItems: 'flex-start',\n gap: theme.spacing(1.5),\n padding: theme.spacing(1.5, 2),\n borderRadius: theme.spacing(1),\n backgroundColor: alpha(colorPalette.main, 0.1),\n border: `1px solid ${alpha(colorPalette.main, 0.2)}`,\n color: colorPalette.main,\n position: sticky ? 'sticky' : 'relative',\n top: sticky ? 0 : 'auto',\n zIndex: sticky ? theme.zIndex.appBar : 'auto',\n width: fullWidth ? '100vw' : '100%',\n marginLeft: fullWidth ? '50%' : 0,\n transform: fullWidth ? 'translateX(-50%)' : 'none',\n animation: `${fadeInSlide} 0.3s ease-out`,\n transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',\n\n // Enhanced accessibility and focus styles\n '&:focus-within': {\n outline: `3px solid ${alpha(colorPalette.main, 0.5)}`,\n outlineOffset: '2px',\n },\n\n ...bannerPartStyles(theme, colorPalette),\n\n // Responsive layout\n [theme.breakpoints.down('sm')]: {\n flexDirection: 'column',\n alignItems: 'stretch',\n gap: theme.spacing(1),\n \n '.banner-content': {\n order: 1,\n },\n\n '.banner-actions': {\n order: 2,\n justifyContent: 'flex-start',\n marginTop: theme.spacing(1),\n marginLeft: 0,\n },\n },\n\n // Glass effect variant (optional enhancement)\n '&.banner-glass': {\n backgroundColor: alpha(theme.palette.background.paper, 0.1),\n backdropFilter: 'blur(20px)',\n border: `1px solid ${alpha(theme.palette.divider, 0.2)}`,\n color: theme.palette.text.primary,\n \n '.banner-icon': {\n color: colorPalette.main,\n },\n },\n };\n});\n\nconst getVariantIcon = (variant: BannerVariant) => {\n const iconMap: Record<BannerVariant, React.ReactNode> = {\n info: <Info />,\n success: <CheckCircle />,\n warning: <Warning />,\n critical: <Error />,\n };\n return iconMap[variant];\n};\n\ntype BannerAction = NonNullable<BannerProps['actions']>[number];\n\n// Warnings and criticals interrupt the screen reader; the rest are announced\n// politely when it next pauses.\nconst resolveAriaAnnouncement = (variant: BannerVariant) => ({\n role: variant === 'warning' || variant === 'critical' ? 'alert' : 'status',\n live: (variant === 'critical' ? 'assertive' : 'polite') as 'assertive' | 'polite',\n});\n\nconst testIdFor = (base: string | undefined, suffix: string) =>\n base ? `${base}-${suffix}` : `banner-${suffix}`;\n\n// Resolves the banner's palette once, rather than per property as the inline\n// version did — getVariantColor was being called nine times to build one button.\nconst buildActionSx = (\n theme: Theme,\n variant: BannerVariant,\n actionVariant: BannerAction['variant'],\n) => {\n const palette = getVariantColor(theme, variant);\n\n return {\n minWidth: 'auto',\n px: 1.5,\n py: 0.5,\n fontSize: '0.8125rem',\n fontWeight: 500,\n borderRadius: 1,\n\n ...(actionVariant === 'primary' && {\n backgroundColor: palette.main,\n color: theme.palette.getContrastText(palette.main),\n\n '&:hover': {\n backgroundColor: palette.dark || palette.main,\n transform: 'translateY(-1px)',\n },\n }),\n\n ...(actionVariant === 'secondary' && {\n borderColor: palette.main,\n color: palette.main,\n\n '&:hover': {\n backgroundColor: alpha(palette.main, 0.1),\n borderColor: palette.main,\n transform: 'translateY(-1px)',\n },\n }),\n };\n};\n\nconst BannerActionButton: React.FC<{\n action: BannerAction;\n variant: BannerVariant;\n dataTestId?: string;\n}> = ({ action, variant, dataTestId }) => (\n <Button\n size=\"small\"\n variant={action.variant === 'primary' ? 'contained' : 'outlined'}\n onClick={action.onClick}\n data-testid={testIdFor(dataTestId, 'action')}\n sx={(theme) => buildActionSx(theme, variant, action.variant)}\n >\n {action.label}\n </Button>\n);\n\nconst BannerBody: React.FC<{\n displayIcon: React.ReactNode;\n title?: BannerProps['title'];\n description?: BannerProps['description'];\n dataTestId?: string;\n children?: React.ReactNode;\n}> = ({ displayIcon, title, description, dataTestId, children }) => (\n <>\n {displayIcon && (\n <Box className=\"banner-icon\" aria-hidden=\"true\" data-testid={testIdFor(dataTestId, 'icon')}>\n {displayIcon}\n </Box>\n )}\n\n <Box className=\"banner-content\">\n {title && (\n <Typography\n className=\"banner-title\"\n variant=\"subtitle2\"\n component=\"div\"\n data-testid={testIdFor(dataTestId, 'title')}\n >\n {title}\n </Typography>\n )}\n {description && (\n <Typography\n className=\"banner-description\"\n variant=\"body2\"\n component=\"div\"\n data-testid={testIdFor(dataTestId, 'message')}\n >\n {description}\n </Typography>\n )}\n {children}\n </Box>\n </>\n);\n\nconst BannerTrailing: React.FC<{\n actions?: BannerProps['actions'];\n variant: BannerVariant;\n dismissible: boolean;\n onDismissClick: () => void;\n dataTestId?: string;\n}> = ({ actions, variant, dismissible, onDismissClick, dataTestId }) => (\n <Box display=\"flex\" alignItems=\"center\" gap={1}>\n {actions && actions.length > 0 && (\n <Box className=\"banner-actions\">\n {actions.map((action, index) => (\n <BannerActionButton\n key={index}\n action={action}\n variant={variant}\n dataTestId={dataTestId}\n />\n ))}\n </Box>\n )}\n\n {dismissible && (\n <IconButton\n className=\"banner-dismiss\"\n size=\"small\"\n onClick={onDismissClick}\n aria-label=\"Dismiss banner\"\n data-testid={testIdFor(dataTestId, 'close')}\n sx={{ transition: 'all 0.2s ease' }}\n >\n <Close fontSize=\"small\" />\n </IconButton>\n )}\n </Box>\n);\n\nexport const Banner = React.forwardRef<HTMLDivElement, BannerProps>(\n (\n {\n variant = 'info',\n title,\n description,\n icon,\n dismissible = false,\n onDismiss,\n actions,\n sticky = false,\n fullWidth = false,\n className,\n children,\n 'data-testid': dataTestId = 'banner',\n ...props\n },\n ref,\n ) => {\n const [visible, setVisible] = React.useState(true);\n\n const handleDismiss = React.useCallback(() => {\n setVisible(false);\n onDismiss?.();\n }, [onDismiss]);\n\n const displayIcon = icon || getVariantIcon(variant);\n\n const { role: ariaRole, live: ariaLive } = resolveAriaAnnouncement(variant);\n\n if (!visible) {\n return null;\n }\n\n return (\n <StyledBanner\n ref={ref}\n variant={variant}\n sticky={sticky}\n fullWidth={fullWidth}\n className={className}\n role={ariaRole}\n aria-live={ariaLive}\n aria-atomic=\"true\"\n tabIndex={0}\n data-testid={dataTestId}\n {...props}\n >\n <BannerBody\n displayIcon={displayIcon}\n title={title}\n description={description}\n dataTestId={dataTestId}\n >\n {children}\n </BannerBody>\n\n <BannerTrailing\n actions={actions}\n variant={variant}\n dismissible={dismissible}\n onDismissClick={handleDismiss}\n dataTestId={dataTestId}\n />\n </StyledBanner>\n );\n },\n);\n\nBanner.displayName = 'Banner';","import { alpha, keyframes } from '@mui/material';\nimport type { CSSObject, PaletteColor, Theme } from '@mui/material';\n\nimport type { BannerVariant } from './Banner.types';\n\n// Animations\nexport const fadeInSlide = keyframes`\n from {\n opacity: 0;\n transform: translateY(-10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n`;\n\nconst pulseAnimation = keyframes`\n 0% {\n box-shadow: 0 0 0 0 currentColor;\n opacity: 0.7;\n }\n 70% {\n box-shadow: 0 0 0 8px currentColor;\n opacity: 0;\n }\n 100% {\n box-shadow: 0 0 0 0 currentColor;\n opacity: 0;\n }\n`;\n\nexport const getVariantColor = (theme: Theme, variant: BannerVariant) => {\n const colorMap = {\n info: theme.palette.info,\n success: theme.palette.success,\n warning: theme.palette.warning,\n critical: theme.palette.error,\n };\n return colorMap[variant];\n};\n\nexport const bannerPartStyles = (theme: Theme, colorPalette: PaletteColor): CSSObject => ({\n // Icon container\n '.banner-icon': {\n flexShrink: 0,\n marginTop: theme.spacing(0.25),\n color: colorPalette.main,\n fontSize: '1.25rem',\n display: 'flex',\n alignItems: 'center',\n position: 'relative',\n\n '&::before': {\n content: '\"\"',\n position: 'absolute',\n inset: -4,\n borderRadius: '50%',\n background: alpha(colorPalette.main, 0.1),\n animation: `${pulseAnimation} 3s infinite`,\n },\n },\n\n // Content area\n '.banner-content': {\n flex: 1,\n minWidth: 0, // Prevents flex item overflow\n },\n\n // Title styles\n '.banner-title': {\n fontWeight: 600,\n fontSize: '1rem',\n lineHeight: 1.4,\n marginBottom: theme.spacing(0.5),\n color: colorPalette.dark || colorPalette.main,\n },\n\n // Description styles \n '.banner-description': {\n fontSize: '0.875rem',\n lineHeight: 1.5,\n opacity: 0.9,\n color: 'inherit',\n },\n\n // Actions area\n '.banner-actions': {\n display: 'flex',\n flexShrink: 0,\n gap: theme.spacing(1),\n alignItems: 'center',\n marginTop: theme.spacing(1),\n\n [theme.breakpoints.up('sm')]: {\n marginTop: 0,\n marginLeft: theme.spacing(2),\n },\n },\n\n // Dismiss button\n '.banner-dismiss': {\n color: 'inherit',\n opacity: 0.7,\n marginLeft: theme.spacing(1),\n flexShrink: 0,\n\n '&:hover': {\n opacity: 1,\n backgroundColor: alpha(colorPalette.main, 0.1),\n transform: 'rotate(90deg)',\n },\n\n '&:focus': {\n opacity: 1,\n outline: `2px solid ${colorPalette.main}`,\n outlineOffset: '2px',\n },\n },\n});\n"],"mappings":";;;AAAA,OAAO,iBAAiB;AACxB,OAAO,WAAW;AAClB,OAAO,WAAW;AAClB,OAAO,UAAU;AACjB,OAAO,aAAa;AACpB,SAAS,SAAAA,QAAO,KAAK,QAAQ,YAAY,kBAAkB;AAE3D,SAAS,cAAc;AACvB,OAAO,WAAW;;;ACRlB,SAAS,OAAO,iBAAiB;AAM1B,IAAM,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAW3B,IAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAehB,IAAM,kBAAkB,CAAC,OAAc,YAA2B;AACvE,QAAM,WAAW;AAAA,IACf,MAAM,MAAM,QAAQ;AAAA,IACpB,SAAS,MAAM,QAAQ;AAAA,IACvB,SAAS,MAAM,QAAQ;AAAA,IACvB,UAAU,MAAM,QAAQ;AAAA,EAC1B;AACA,SAAO,SAAS,OAAO;AACzB;AAEO,IAAM,mBAAmB,CAAC,OAAc,kBAA2C;AAAA;AAAA,EAExF,gBAAgB;AAAA,IACd,YAAY;AAAA,IACZ,WAAW,MAAM,QAAQ,IAAI;AAAA,IAC7B,OAAO,aAAa;AAAA,IACpB,UAAU;AAAA,IACV,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,UAAU;AAAA,IAEV,aAAa;AAAA,MACX,SAAS;AAAA,MACT,UAAU;AAAA,MACV,OAAO;AAAA,MACP,cAAc;AAAA,MACd,YAAY,MAAM,aAAa,MAAM,GAAG;AAAA,MACxC,WAAW,GAAG,cAAc;AAAA,IAC9B;AAAA,EACF;AAAA;AAAA,EAGA,mBAAmB;AAAA,IACjB,MAAM;AAAA,IACN,UAAU;AAAA;AAAA,EACZ;AAAA;AAAA,EAGA,iBAAiB;AAAA,IACf,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,cAAc,MAAM,QAAQ,GAAG;AAAA,IAC/B,OAAO,aAAa,QAAQ,aAAa;AAAA,EAC3C;AAAA;AAAA,EAGA,uBAAuB;AAAA,IACrB,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,OAAO;AAAA,EACT;AAAA;AAAA,EAGA,mBAAmB;AAAA,IACjB,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,KAAK,MAAM,QAAQ,CAAC;AAAA,IACpB,YAAY;AAAA,IACZ,WAAW,MAAM,QAAQ,CAAC;AAAA,IAE1B,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG;AAAA,MAC5B,WAAW;AAAA,MACX,YAAY,MAAM,QAAQ,CAAC;AAAA,IAC7B;AAAA,EACF;AAAA;AAAA,EAGA,mBAAmB;AAAA,IACjB,OAAO;AAAA,IACP,SAAS;AAAA,IACT,YAAY,MAAM,QAAQ,CAAC;AAAA,IAC3B,YAAY;AAAA,IAEZ,WAAW;AAAA,MACT,SAAS;AAAA,MACT,iBAAiB,MAAM,aAAa,MAAM,GAAG;AAAA,MAC7C,WAAW;AAAA,IACb;AAAA,IAEA,WAAW;AAAA,MACT,SAAS;AAAA,MACT,SAAS,aAAa,aAAa,IAAI;AAAA,MACvC,eAAe;AAAA,IACjB;AAAA,EACF;AACF;;;ADpCU,SAmFR,UAnFQ,KA0FN,YA1FM;AAtEV,IAAM,eAAe,OAAO,KAAK;AAAA,EAC/B,mBAAmB,CAAC,SAAS,CAAC,CAAC,WAAW,UAAU,WAAW,EAAE,SAAS,IAAc;AAC1F,CAAC,EAIE,CAAC,EAAE,OAAO,SAAS,QAAQ,UAAU,MAAM;AAE5C,QAAM,eAAe,gBAAgB,OAAO,OAAO;AAEnD,SAAO;AAAA,IACL,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,KAAK,MAAM,QAAQ,GAAG;AAAA,IACtB,SAAS,MAAM,QAAQ,KAAK,CAAC;AAAA,IAC7B,cAAc,MAAM,QAAQ,CAAC;AAAA,IAC7B,iBAAiBC,OAAM,aAAa,MAAM,GAAG;AAAA,IAC7C,QAAQ,aAAaA,OAAM,aAAa,MAAM,GAAG,CAAC;AAAA,IAClD,OAAO,aAAa;AAAA,IACpB,UAAU,SAAS,WAAW;AAAA,IAC9B,KAAK,SAAS,IAAI;AAAA,IAClB,QAAQ,SAAS,MAAM,OAAO,SAAS;AAAA,IACvC,OAAO,YAAY,UAAU;AAAA,IAC7B,YAAY,YAAY,QAAQ;AAAA,IAChC,WAAW,YAAY,qBAAqB;AAAA,IAC5C,WAAW,GAAG,WAAW;AAAA,IACzB,YAAY;AAAA;AAAA,IAGZ,kBAAkB;AAAA,MAChB,SAAS,aAAaA,OAAM,aAAa,MAAM,GAAG,CAAC;AAAA,MACnD,eAAe;AAAA,IACjB;AAAA,IAEA,GAAG,iBAAiB,OAAO,YAAY;AAAA;AAAA,IAGvC,CAAC,MAAM,YAAY,KAAK,IAAI,CAAC,GAAG;AAAA,MAC9B,eAAe;AAAA,MACf,YAAY;AAAA,MACZ,KAAK,MAAM,QAAQ,CAAC;AAAA,MAEpB,mBAAmB;AAAA,QACjB,OAAO;AAAA,MACT;AAAA,MAEA,mBAAmB;AAAA,QACjB,OAAO;AAAA,QACP,gBAAgB;AAAA,QAChB,WAAW,MAAM,QAAQ,CAAC;AAAA,QAC1B,YAAY;AAAA,MACd;AAAA,IACF;AAAA;AAAA,IAGA,kBAAkB;AAAA,MAChB,iBAAiBA,OAAM,MAAM,QAAQ,WAAW,OAAO,GAAG;AAAA,MAC1D,gBAAgB;AAAA,MAChB,QAAQ,aAAaA,OAAM,MAAM,QAAQ,SAAS,GAAG,CAAC;AAAA,MACtD,OAAO,MAAM,QAAQ,KAAK;AAAA,MAE1B,gBAAgB;AAAA,QACd,OAAO,aAAa;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AACF,CAAC;AAED,IAAM,iBAAiB,CAAC,YAA2B;AACjD,QAAM,UAAkD;AAAA,IACtD,MAAM,oBAAC,QAAK;AAAA,IACZ,SAAS,oBAAC,eAAY;AAAA,IACtB,SAAS,oBAAC,WAAQ;AAAA,IAClB,UAAU,oBAAC,SAAM;AAAA,EACnB;AACA,SAAO,QAAQ,OAAO;AACxB;AAMA,IAAM,0BAA0B,CAAC,aAA4B;AAAA,EAC3D,MAAM,YAAY,aAAa,YAAY,aAAa,UAAU;AAAA,EAClE,MAAO,YAAY,aAAa,cAAc;AAChD;AAEA,IAAM,YAAY,CAAC,MAA0B,WAC3C,OAAO,GAAG,IAAI,IAAI,MAAM,KAAK,UAAU,MAAM;AAI/C,IAAM,gBAAgB,CACpB,OACA,SACA,kBACG;AACH,QAAM,UAAU,gBAAgB,OAAO,OAAO;AAE9C,SAAO;AAAA,IACL,UAAU;AAAA,IACV,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,cAAc;AAAA,IAEd,GAAI,kBAAkB,aAAa;AAAA,MACjC,iBAAiB,QAAQ;AAAA,MACzB,OAAO,MAAM,QAAQ,gBAAgB,QAAQ,IAAI;AAAA,MAEjD,WAAW;AAAA,QACT,iBAAiB,QAAQ,QAAQ,QAAQ;AAAA,QACzC,WAAW;AAAA,MACb;AAAA,IACF;AAAA,IAEA,GAAI,kBAAkB,eAAe;AAAA,MACnC,aAAa,QAAQ;AAAA,MACrB,OAAO,QAAQ;AAAA,MAEf,WAAW;AAAA,QACT,iBAAiBA,OAAM,QAAQ,MAAM,GAAG;AAAA,QACxC,aAAa,QAAQ;AAAA,QACrB,WAAW;AAAA,MACb;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAM,qBAID,CAAC,EAAE,QAAQ,SAAS,WAAW,MAClC;AAAA,EAAC;AAAA;AAAA,IACC,MAAK;AAAA,IACL,SAAS,OAAO,YAAY,YAAY,cAAc;AAAA,IACtD,SAAS,OAAO;AAAA,IAChB,eAAa,UAAU,YAAY,QAAQ;AAAA,IAC3C,IAAI,CAAC,UAAU,cAAc,OAAO,SAAS,OAAO,OAAO;AAAA,IAE1D,iBAAO;AAAA;AACV;AAGF,IAAM,aAMD,CAAC,EAAE,aAAa,OAAO,aAAa,YAAY,SAAS,MAC5D,iCACG;AAAA,iBACC,oBAAC,OAAI,WAAU,eAAc,eAAY,QAAO,eAAa,UAAU,YAAY,MAAM,GACtF,uBACH;AAAA,EAGF,qBAAC,OAAI,WAAU,kBACZ;AAAA,aACC;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,SAAQ;AAAA,QACR,WAAU;AAAA,QACV,eAAa,UAAU,YAAY,OAAO;AAAA,QAEzC;AAAA;AAAA,IACH;AAAA,IAED,eACC;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,SAAQ;AAAA,QACR,WAAU;AAAA,QACV,eAAa,UAAU,YAAY,SAAS;AAAA,QAE3C;AAAA;AAAA,IACH;AAAA,IAED;AAAA,KACH;AAAA,GACF;AAGF,IAAM,iBAMD,CAAC,EAAE,SAAS,SAAS,aAAa,gBAAgB,WAAW,MAChE,qBAAC,OAAI,SAAQ,QAAO,YAAW,UAAS,KAAK,GAC1C;AAAA,aAAW,QAAQ,SAAS,KAC3B,oBAAC,OAAI,WAAU,kBACZ,kBAAQ,IAAI,CAAC,QAAQ,UACpB;AAAA,IAAC;AAAA;AAAA,MAEC;AAAA,MACA;AAAA,MACA;AAAA;AAAA,IAHK;AAAA,EAIP,CACD,GACH;AAAA,EAGD,eACC;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,MAAK;AAAA,MACL,SAAS;AAAA,MACT,cAAW;AAAA,MACX,eAAa,UAAU,YAAY,OAAO;AAAA,MAC1C,IAAI,EAAE,YAAY,gBAAgB;AAAA,MAElC,8BAAC,SAAM,UAAS,SAAQ;AAAA;AAAA,EAC1B;AAAA,GAEJ;AAGK,IAAM,SAAS,MAAM;AAAA,EAC1B,CACE;AAAA,IACE,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd;AAAA,IACA;AAAA,IACA,SAAS;AAAA,IACT,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA,eAAe,aAAa;AAAA,IAC5B,GAAG;AAAA,EACL,GACA,QACG;AACH,UAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,IAAI;AAEjD,UAAM,gBAAgB,MAAM,YAAY,MAAM;AAC5C,iBAAW,KAAK;AAChB,kBAAY;AAAA,IACd,GAAG,CAAC,SAAS,CAAC;AAEd,UAAM,cAAc,QAAQ,eAAe,OAAO;AAElD,UAAM,EAAE,MAAM,UAAU,MAAM,SAAS,IAAI,wBAAwB,OAAO;AAE1E,QAAI,CAAC,SAAS;AACZ,aAAO;AAAA,IACT;AAEA,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,MAAM;AAAA,QACN,aAAW;AAAA,QACX,eAAY;AAAA,QACZ,UAAU;AAAA,QACV,eAAa;AAAA,QACZ,GAAG;AAAA,QAEJ;AAAA;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cAEC;AAAA;AAAA,UACH;AAAA,UAEA;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cACA;AAAA,cACA;AAAA,cACA,gBAAgB;AAAA,cAChB;AAAA;AAAA,UACF;AAAA;AAAA;AAAA,IACF;AAAA,EAEJ;AACF;AAEA,OAAO,cAAc;","names":["alpha","alpha"]}
1
+ {"version":3,"sources":["../../src/components/data-display/Banner/Banner.tsx","../../src/components/data-display/Banner/Banner.styles.ts"],"sourcesContent":["import CheckCircle from '@mui/icons-material/CheckCircle';\nimport Close from '@mui/icons-material/Close';\nimport Error from '@mui/icons-material/Error';\nimport Info from '@mui/icons-material/Info';\nimport Warning from '@mui/icons-material/Warning';\nimport { alpha, Box, Button, IconButton, Typography } from '@mui/material';\nimport type { Theme } from '@mui/material';\nimport { styled } from '@mui/material';\nimport React from 'react';\n\nimport { bannerPartStyles, fadeInSlide, getVariantColor } from './Banner.styles';\nimport type { BannerProps, BannerVariant } from './Banner.types';\n\nconst StyledBanner = styled(Box, {\n shouldForwardProp: (prop) => !['variant', 'sticky', 'fullWidth'].includes(prop as string),\n})<{\n variant: BannerVariant;\n sticky: boolean;\n fullWidth: boolean;\n}>(({ theme, variant, sticky, fullWidth }) => {\n\n const colorPalette = getVariantColor(theme, variant);\n\n return {\n display: 'flex',\n alignItems: 'flex-start',\n gap: theme.spacing(1.5),\n padding: theme.spacing(1.5, 2),\n borderRadius: theme.spacing(1),\n backgroundColor: alpha(colorPalette.main, 0.1),\n border: `1px solid ${alpha(colorPalette.main, 0.2)}`,\n color: colorPalette.main,\n position: sticky ? 'sticky' : 'relative',\n top: sticky ? 0 : 'auto',\n zIndex: sticky ? theme.zIndex.appBar : 'auto',\n width: fullWidth ? '100vw' : '100%',\n marginLeft: fullWidth ? '50%' : 0,\n transform: fullWidth ? 'translateX(-50%)' : 'none',\n animation: `${fadeInSlide} 0.3s ease-out`,\n transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',\n\n // Enhanced accessibility and focus styles\n '&:focus-within': {\n outline: `3px solid ${alpha(colorPalette.main, 0.5)}`,\n outlineOffset: '2px',\n },\n\n ...bannerPartStyles(theme, colorPalette),\n\n // Responsive layout\n [theme.breakpoints.down('sm')]: {\n flexDirection: 'column',\n alignItems: 'stretch',\n gap: theme.spacing(1),\n \n '.banner-content': {\n order: 1,\n },\n\n '.banner-actions': {\n order: 2,\n justifyContent: 'flex-start',\n marginTop: theme.spacing(1),\n marginLeft: 0,\n },\n },\n\n // Glass effect variant (optional enhancement)\n '&.banner-glass': {\n backgroundColor: alpha(theme.palette.background.paper, 0.1),\n backdropFilter: 'blur(20px)',\n border: `1px solid ${alpha(theme.palette.divider, 0.2)}`,\n color: theme.palette.text.primary,\n \n '.banner-icon': {\n color: colorPalette.main,\n },\n },\n };\n});\n\nconst getVariantIcon = (variant: BannerVariant) => {\n const iconMap: Record<BannerVariant, React.ReactNode> = {\n info: <Info />,\n success: <CheckCircle />,\n warning: <Warning />,\n critical: <Error />,\n };\n return iconMap[variant];\n};\n\ntype BannerAction = NonNullable<BannerProps['actions']>[number];\n\n// Warnings and criticals interrupt the screen reader; the rest are announced\n// politely when it next pauses.\nconst resolveAriaAnnouncement = (variant: BannerVariant) => ({\n role: variant === 'warning' || variant === 'critical' ? 'alert' : 'status',\n live: (variant === 'critical' ? 'assertive' : 'polite') as 'assertive' | 'polite',\n});\n\nconst testIdFor = (base: string | undefined, suffix: string) =>\n base ? `${base}-${suffix}` : `banner-${suffix}`;\n\n// Resolves the banner's palette once, rather than per property as the inline\n// version did — getVariantColor was being called nine times to build one button.\nconst buildActionSx = (\n theme: Theme,\n variant: BannerVariant,\n actionVariant: BannerAction['variant'],\n) => {\n const palette = getVariantColor(theme, variant);\n\n return {\n minWidth: 'auto',\n px: 1.5,\n py: 0.5,\n fontSize: '0.8125rem',\n fontWeight: 500,\n borderRadius: 1,\n\n ...(actionVariant === 'primary' && {\n backgroundColor: palette.main,\n color: theme.palette.getContrastText(palette.main),\n\n '&:hover': {\n backgroundColor: palette.dark || palette.main,\n transform: 'translateY(-1px)',\n },\n }),\n\n ...(actionVariant === 'secondary' && {\n borderColor: palette.main,\n color: palette.main,\n\n '&:hover': {\n backgroundColor: alpha(palette.main, 0.1),\n borderColor: palette.main,\n transform: 'translateY(-1px)',\n },\n }),\n };\n};\n\nconst BannerActionButton: React.FC<{\n action: BannerAction;\n variant: BannerVariant;\n dataTestId?: string;\n}> = ({ action, variant, dataTestId }) => (\n <Button\n size=\"small\"\n variant={action.variant === 'primary' ? 'contained' : 'outlined'}\n onClick={action.onClick}\n data-testid={testIdFor(dataTestId, 'action')}\n sx={(theme) => buildActionSx(theme, variant, action.variant)}\n >\n {action.label}\n </Button>\n);\n\nconst BannerBody: React.FC<{\n displayIcon: React.ReactNode;\n title?: BannerProps['title'];\n description?: BannerProps['description'];\n dataTestId?: string;\n children?: React.ReactNode;\n}> = ({ displayIcon, title, description, dataTestId, children }) => (\n <>\n {displayIcon && (\n <Box className=\"banner-icon\" aria-hidden=\"true\" data-testid={testIdFor(dataTestId, 'icon')}>\n {displayIcon}\n </Box>\n )}\n\n <Box className=\"banner-content\">\n {title && (\n <Typography\n className=\"banner-title\"\n variant=\"subtitle2\"\n component=\"div\"\n data-testid={testIdFor(dataTestId, 'title')}\n >\n {title}\n </Typography>\n )}\n {description && (\n <Typography\n className=\"banner-description\"\n variant=\"body2\"\n component=\"div\"\n data-testid={testIdFor(dataTestId, 'message')}\n >\n {description}\n </Typography>\n )}\n {children}\n </Box>\n </>\n);\n\nconst BannerTrailing: React.FC<{\n actions?: BannerProps['actions'];\n variant: BannerVariant;\n dismissible: boolean;\n onDismissClick: () => void;\n dismissLabel: string;\n dataTestId?: string;\n}> = ({ actions, variant, dismissible, onDismissClick, dismissLabel, dataTestId }) => (\n <Box display=\"flex\" alignItems=\"center\" gap={1}>\n {actions && actions.length > 0 && (\n <Box className=\"banner-actions\">\n {actions.map((action, index) => (\n <BannerActionButton\n key={index}\n action={action}\n variant={variant}\n dataTestId={dataTestId}\n />\n ))}\n </Box>\n )}\n\n {dismissible && (\n <IconButton\n className=\"banner-dismiss\"\n size=\"small\"\n onClick={onDismissClick}\n aria-label={dismissLabel}\n data-testid={testIdFor(dataTestId, 'close')}\n sx={{ transition: 'all 0.2s ease' }}\n >\n <Close fontSize=\"small\" />\n </IconButton>\n )}\n </Box>\n);\n\nexport const Banner = React.forwardRef<HTMLDivElement, BannerProps>(\n (\n {\n variant = 'info',\n title,\n description,\n icon,\n dismissible = false,\n dismissLabel,\n onDismiss,\n actions,\n sticky = false,\n fullWidth = false,\n className,\n children,\n 'data-testid': dataTestId = 'banner',\n ...props\n },\n ref,\n ) => {\n const [visible, setVisible] = React.useState(true);\n\n const handleDismiss = React.useCallback(() => {\n setVisible(false);\n onDismiss?.();\n }, [onDismiss]);\n\n const displayIcon = icon || getVariantIcon(variant);\n\n const { role: ariaRole, live: ariaLive } = resolveAriaAnnouncement(variant);\n\n if (!visible) {\n return null;\n }\n\n return (\n <StyledBanner\n ref={ref}\n variant={variant}\n sticky={sticky}\n fullWidth={fullWidth}\n className={className}\n role={ariaRole}\n aria-live={ariaLive}\n aria-atomic=\"true\"\n tabIndex={0}\n data-testid={dataTestId}\n {...props}\n >\n <BannerBody\n displayIcon={displayIcon}\n title={title}\n description={description}\n dataTestId={dataTestId}\n >\n {children}\n </BannerBody>\n\n <BannerTrailing\n actions={actions}\n variant={variant}\n dismissible={dismissible}\n onDismissClick={handleDismiss}\n dismissLabel={dismissLabel}\n dataTestId={dataTestId}\n />\n </StyledBanner>\n );\n },\n);\n\nBanner.displayName = 'Banner';","import { alpha, keyframes } from '@mui/material';\nimport type { CSSObject, PaletteColor, Theme } from '@mui/material';\n\nimport type { BannerVariant } from './Banner.types';\n\n// Animations\nexport const fadeInSlide = keyframes`\n from {\n opacity: 0;\n transform: translateY(-10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n`;\n\nconst pulseAnimation = keyframes`\n 0% {\n box-shadow: 0 0 0 0 currentColor;\n opacity: 0.7;\n }\n 70% {\n box-shadow: 0 0 0 8px currentColor;\n opacity: 0;\n }\n 100% {\n box-shadow: 0 0 0 0 currentColor;\n opacity: 0;\n }\n`;\n\nexport const getVariantColor = (theme: Theme, variant: BannerVariant) => {\n const colorMap = {\n info: theme.palette.info,\n success: theme.palette.success,\n warning: theme.palette.warning,\n critical: theme.palette.error,\n };\n return colorMap[variant];\n};\n\nexport const bannerPartStyles = (theme: Theme, colorPalette: PaletteColor): CSSObject => ({\n // Icon container\n '.banner-icon': {\n flexShrink: 0,\n marginTop: theme.spacing(0.25),\n color: colorPalette.main,\n fontSize: '1.25rem',\n display: 'flex',\n alignItems: 'center',\n position: 'relative',\n\n '&::before': {\n content: '\"\"',\n position: 'absolute',\n inset: -4,\n borderRadius: '50%',\n background: alpha(colorPalette.main, 0.1),\n animation: `${pulseAnimation} 3s infinite`,\n },\n },\n\n // Content area\n '.banner-content': {\n flex: 1,\n minWidth: 0, // Prevents flex item overflow\n },\n\n // Title styles\n '.banner-title': {\n fontWeight: 600,\n fontSize: '1rem',\n lineHeight: 1.4,\n marginBottom: theme.spacing(0.5),\n color: colorPalette.dark || colorPalette.main,\n },\n\n // Description styles \n '.banner-description': {\n fontSize: '0.875rem',\n lineHeight: 1.5,\n opacity: 0.9,\n color: 'inherit',\n },\n\n // Actions area\n '.banner-actions': {\n display: 'flex',\n flexShrink: 0,\n gap: theme.spacing(1),\n alignItems: 'center',\n marginTop: theme.spacing(1),\n\n [theme.breakpoints.up('sm')]: {\n marginTop: 0,\n marginLeft: theme.spacing(2),\n },\n },\n\n // Dismiss button\n '.banner-dismiss': {\n color: 'inherit',\n opacity: 0.7,\n marginLeft: theme.spacing(1),\n flexShrink: 0,\n\n '&:hover': {\n opacity: 1,\n backgroundColor: alpha(colorPalette.main, 0.1),\n transform: 'rotate(90deg)',\n },\n\n '&:focus': {\n opacity: 1,\n outline: `2px solid ${colorPalette.main}`,\n outlineOffset: '2px',\n },\n },\n});\n"],"mappings":"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","names":["alpha","alpha"]}
@@ -7,7 +7,7 @@ import {
7
7
  } from "../chunk-5TCPZ6GR.js";
8
8
  import {
9
9
  Alert
10
- } from "../chunk-3KWD5Y5O.js";
10
+ } from "../chunk-RXE5CUUE.js";
11
11
  import "../chunk-ARNQIGA4.js";
12
12
  import {
13
13
  Box
@@ -44,6 +44,7 @@ function CardActionsProvider({
44
44
  tenantSlug,
45
45
  onRefresh,
46
46
  errorTitle,
47
+ errorDismissLabel,
47
48
  autoHideMs = 6e3,
48
49
  children
49
50
  }) {
@@ -65,6 +66,7 @@ function CardActionsProvider({
65
66
  title: errorTitle,
66
67
  description: error ?? "",
67
68
  closable: true,
69
+ closeLabel: errorDismissLabel,
68
70
  onClose: () => setError(null),
69
71
  "data-testid": "card-action-error"
70
72
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/data-display/CardKit/CardKebab.tsx","../../src/components/data-display/CardKit/card-actions-context.tsx","../../src/components/data-display/CardKit/row-actions-to-menu.ts","../../src/components/data-display/CardKit/list-card-parts.tsx","../../src/components/data-display/CardKit/use-row-confirm.tsx","../../src/components/data-display/CardKit/use-remove-confirm.tsx"],"sourcesContent":["'use client';\n\nimport type { JSX } from 'react';\n\nimport { Button } from '../../form/Button';\nimport { DropdownMenu, type DropdownMenuItem } from '../../navigation/DropdownMenu';\n\n/**\n * The \"⋮\" trigger every row and card menu opens from.\n *\n * One component rather than a `DropdownMenu` per entity, so the kebab looks and\n * behaves identically in the card, in the list row and in the table row. The\n * entity's menu owns its own popups; this is the trigger and the item list.\n */\nexport function CardKebab({\n items,\n menuLabel,\n dataTestId,\n}: {\n items: DropdownMenuItem[];\n /**\n * The trigger's accessible name — required, and with no default.\n *\n * It is the only text this component renders, and a screen reader announces\n * it verbatim. A default here would be a word in the origin's language that\n * reads as finished to the next adopter right up until somebody using a\n * screen reader meets it.\n */\n menuLabel: string;\n dataTestId?: string;\n}): JSX.Element {\n return (\n <DropdownMenu\n size=\"sm\"\n items={items}\n trigger={\n <Button variant=\"text\" size=\"sm\" aria-label={menuLabel} dataTestId={dataTestId}>\n ⋮\n </Button>\n }\n />\n );\n}\n","'use client';\n\nimport { createContext, useCallback, useContext, useState, type JSX, type ReactNode } from 'react';\n\nimport { Snackbar } from '@mui/material';\n\nimport { Alert } from '../Alert';\n\n/**\n * The ambient wiring a self-contained row/card menu needs, and nothing else.\n *\n * A menu that owns its own popups still needs three things it cannot know: the\n * tenant it is acting inside, what to do after a write lands, and somewhere to\n * put a failure. Passing those down through the grid, the layout and the card\n * is four props of drilling per entity; holding them in context is one.\n *\n * Kept deliberately SMALL — it is not an edit-dialog host. A menu that read its\n * dialogs from here would stop being portable, because the set of dialogs is\n * per-entity. What it may read is the three facts below.\n *\n * The provider renders ONE shared error snackbar, which is the whole reason the\n * error channel is here rather than per-menu: before it, every page carried its\n * own error alert and every menu had to find its page's.\n */\ninterface CardActionsValue {\n /** The tenant every action in this subtree acts inside. */\n tenantSlug: string;\n /** Re-read the list after a write. */\n onRefresh: () => void;\n /** Surface a failed action's message (the shared snackbar). */\n notifyError: (message: string) => void;\n}\n\nconst CardActionsContext = createContext<CardActionsValue | null>(null);\n\nexport function CardActionsProvider({\n tenantSlug,\n onRefresh,\n errorTitle,\n autoHideMs = 6_000,\n children,\n}: {\n tenantSlug: string;\n onRefresh: () => void;\n /**\n * The snackbar's heading — required, no default.\n *\n * It is the one sentence this provider renders on its own, and a default\n * would be the origin's language shipped as a finished-looking silence. The\n * MESSAGE beside it comes from whatever failed, which is already the host's.\n */\n errorTitle: string;\n /** How long the snackbar stays up. */\n autoHideMs?: number;\n children: ReactNode;\n}): JSX.Element {\n const [error, setError] = useState<string | null>(null);\n const notifyError = useCallback((message: string) => setError(message), []);\n\n return (\n <CardActionsContext.Provider value={{ tenantSlug, onRefresh, notifyError }}>\n {children}\n <Snackbar\n open={error !== null}\n autoHideDuration={autoHideMs}\n onClose={() => setError(null)}\n anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}\n >\n <span>\n <Alert\n variant=\"danger\"\n title={errorTitle}\n description={error ?? ''}\n closable\n onClose={() => setError(null)}\n data-testid=\"card-action-error\"\n />\n </span>\n </Snackbar>\n </CardActionsContext.Provider>\n );\n}\n\n/**\n * Read the tenant + refresh + error wiring.\n *\n * THROWS outside a provider rather than answering a null object, deliberately:\n * a menu whose `onRefresh` silently did nothing would leave the operator\n * looking at a stale row after a delete that worked, which reads as the delete\n * having failed. Failing at mount says where the provider is missing.\n */\nexport function useCardActions(): CardActionsValue {\n const value = useContext(CardActionsContext);\n if (!value) {\n throw new Error('useCardActions must be used within a CardActionsProvider');\n }\n return value;\n}\n","import type { DropdownMenuItem } from '../../navigation/DropdownMenu';\nimport type { RowAction } from '../DataViews';\n\n/**\n * Adapt a list of {@link RowAction}s into single-row kebab items.\n *\n * Applies each action's `isVisible` guard, resolves its per-row `rowLabel`\n * override, carries its colour, and fires `onSelect([row])` on click.\n *\n * The point is that ONE definition drives both surfaces. An entity that already\n * declares its per-row actions as `RowAction[]` — because the table's multi-\n * select menu needs them — gets the card's kebab from the same array through\n * `renderRowMenu` / `renderCard`, with no second list to keep in step. Two\n * lists is how a \"Duplicar\" that appears in the table and not on the card\n * happens, and nothing reports it.\n */\nexport function rowActionsToMenuItems<T extends Record<string, unknown>>(\n actions: RowAction<T>[],\n row: T,\n): DropdownMenuItem[] {\n return actions\n .filter((action) => action.isVisible?.(row) ?? true)\n .map((action) => ({\n id: action.id,\n label: action.rowLabel?.(row) ?? action.label,\n color: action.color,\n onClick: () => void action.onSelect([row]),\n }));\n}\n","'use client';\n\nimport type { JSX, ReactNode } from 'react';\n\nimport { Box } from '../../../mui/Box';\n\nimport type { CardFactProps, CardLedgerLine } from './CardKit.types';\n\n/**\n * THE SHARED FURNITURE OF AN EXPANDED LIST-CARD BODY.\n *\n * `BaseListCard` says nothing about what goes inside a row's expanded area — it\n * spans the rails and stops, because the design belongs to the consumer. Right\n * for the card; wrong as the place where every entity in an admin app invents\n * its own two-column layout, its own label/value pair and its own ledger.\n *\n * So the four shapes that actually repeat live here, once. Nothing here knows a\n * domain: they take `ReactNode` and lay it out.\n */\n\n/** The expanded body's two-column split: detail on the left, a ledger right. */\nexport function DetailColumns({ left, right }: { left: ReactNode; right: ReactNode }): JSX.Element {\n return (\n <Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', md: '1fr 1fr' }, gap: 3 }}>\n <Box sx={{ minWidth: 0 }}>{left}</Box>\n <Box sx={{ minWidth: 0 }}>{right}</Box>\n </Box>\n );\n}\n\n/**\n * One labelled fact inside an expanded body.\n *\n * Horizontal, not stacked: the body is already a wide area with room for a\n * label beside its value, and stacking them there would ape the summary rails\n * above without their alignment.\n */\nexport function Fact({ label, value }: CardFactProps): JSX.Element {\n return (\n <Box sx={{ display: 'flex', justifyContent: 'space-between', gap: 2, fontSize: 13, py: 0.25 }}>\n <Box component=\"span\" sx={{ color: 'text.secondary', flexShrink: 0 }}>\n {label}\n </Box>\n <Box component=\"span\" sx={{ minWidth: 0, textAlign: 'right', overflowWrap: 'anywhere' }}>\n {value}\n </Box>\n </Box>\n );\n}\n\n/**\n * A money ledger: rows of label/amount, then a ruled total.\n *\n * The total is separated by a RULE rather than by weight alone, because a\n * subtotal and a total in one column with only bold between them is the classic\n * misread — the eye lands on whichever number is larger.\n *\n * The figures arrive pre-formatted. This is a layout, and a component that\n * formatted money would be choosing a locale on the consumer's behalf.\n */\nexport function Ledger({\n lines,\n total,\n}: {\n lines: CardLedgerLine[];\n total: { label: string; value: ReactNode };\n}): JSX.Element {\n return (\n <Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.25 }}>\n {lines.map((line) => (\n <Box\n key={line.label}\n sx={{\n display: 'flex',\n justifyContent: 'space-between',\n fontSize: 13,\n color: line.tone === 'info' ? 'info.main' : 'text.secondary',\n }}\n >\n <Box component=\"span\">{line.label}</Box>\n <Box component=\"span\" sx={{ fontVariantNumeric: 'tabular-nums' }}>\n {line.value}\n </Box>\n </Box>\n ))}\n <Box\n sx={{\n display: 'flex',\n justifyContent: 'space-between',\n fontWeight: 700,\n borderTop: 1,\n borderColor: 'divider',\n pt: 0.5,\n mt: 0.5,\n }}\n >\n <Box component=\"span\">{total.label}</Box>\n <Box component=\"span\" sx={{ fontVariantNumeric: 'tabular-nums' }}>\n {total.value}\n </Box>\n </Box>\n </Box>\n );\n}\n\n/** A heading inside the expanded body, for a section that needs naming. */\nexport function BodyHeading({ children }: { children: ReactNode }): JSX.Element {\n return (\n <Box\n sx={{\n fontSize: 11,\n fontWeight: 700,\n letterSpacing: 0.4,\n textTransform: 'uppercase',\n color: 'text.disabled',\n mb: 0.5,\n }}\n >\n {children}\n </Box>\n );\n}\n\n/**\n * A wrapping run of small items — permission names, role names, target ids.\n *\n * The whole reason several entities earn a list card at all: a `string[]` has no\n * honest table cell. Truncated to \"3 items\" it says nothing; printed in full it\n * destroys the column. Here it simply wraps.\n */\nexport function TagList({\n items,\n empty,\n}: {\n items: readonly string[];\n /** What to show for an empty run — the consumer's sentence, no default. */\n empty: string;\n}): JSX.Element {\n if (items.length === 0) {\n return <Box sx={{ fontSize: 13, color: 'text.disabled' }}>{empty}</Box>;\n }\n return (\n <Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>\n {items.map((item) => (\n <Box\n key={item}\n component=\"span\"\n sx={{\n fontSize: 12,\n px: 0.75,\n py: 0.25,\n borderRadius: 1,\n bgcolor: 'action.hover',\n color: 'text.secondary',\n fontFamily: 'monospace',\n }}\n >\n {item}\n </Box>\n ))}\n </Box>\n );\n}\n","'use client';\n\nimport { useState, type JSX } from 'react';\n\nimport type { ConfirmActionCopy } from '../../../copy';\nimport { ConfirmActionDialog, useConfirmAction } from '../../feedback/ConfirmAction';\n\n/**\n * How the popup should read for the rows about to be acted on.\n *\n * Built from the SELECTION rather than from a single row, because the same\n * guard covers a bulk action: \"Delete 4 categories?\" and \"Delete the category\n * Drinks?\" are the same question asked of a different-sized selection — and an\n * operator who multi-selected by mistake is exactly who this popup is for.\n */\nexport interface RowConfirmCopy {\n title: string;\n description: string;\n /** Echoed into the popup; omit for a multi-row selection the title covers. */\n entityName?: string;\n /** The VERB. Required, never defaulted — see {@link useRowConfirm}. */\n confirmText: string;\n /** Override where the word \"cancel\" would mean the ACTION itself. */\n cancelText?: string;\n /** Require typing this before the confirm enables — for the unrecoverable few. */\n typeToConfirm?: string;\n}\n\ninterface RowConfirmOptions<T> {\n /**\n * The destructive write, over the whole selection. Called ONLY after the\n * operator confirms; throw (or reject) to keep the popup open carrying the\n * message.\n */\n write: (rows: T[]) => Promise<unknown>;\n /** The wording, given the selection. */\n describe: (rows: T[]) => RowConfirmCopy;\n dataTestId: string;\n /** Shown when `write` fails without a message of its own. */\n errorText: string;\n /** The dialog's own words. REQUIRED — this package ships no default copy. */\n copy: ConfirmActionCopy;\n}\n\nexport interface RowConfirm<T> {\n /** Hand this a grid action's `rows`: it opens the popup and writes nothing. */\n request: (rows: T[]) => void;\n /** Render next to the grid. */\n dialog: JSX.Element | null;\n}\n\n/**\n * Confirmation for a grid's `RowAction` — the per-row kebab entry and the\n * multi-select actions menu both, since they run the same handler.\n *\n * The SELECTION is held while the popup is open, so the write sees exactly the\n * rows the operator was looking at when they were asked. Re-reading the grid's\n * live selection at confirm time is the bug this shape avoids: the list can\n * refresh underneath an open dialog.\n *\n * `confirmText` is required and comes from `describe`, with no default. A verb\n * defaulted in the origin's language reads as finished to the next adopter\n * right up until an operator sees it — and `ConfirmOptions` already refuses to\n * default one for the same reason.\n */\nexport function useRowConfirm<T>({\n write,\n describe,\n dataTestId,\n errorText,\n copy,\n}: RowConfirmOptions<T>): RowConfirm<T> {\n const [target, setTarget] = useState<T[] | null>(null);\n\n const state = useConfirmAction(async () => {\n if (!target) return;\n await write(target);\n setTarget(null);\n }, errorText);\n\n const request = (rows: T[]): void => {\n // An empty selection has nothing to confirm — asking would be a popup about\n // nothing, and confirming it would write nothing.\n if (rows.length === 0) return;\n setTarget(rows);\n state.request();\n };\n\n return {\n request,\n dialog: target ? (\n <ConfirmActionDialog state={state}\n copy={copy} {...describe(target)} dataTestId={dataTestId} />\n ) : null,\n };\n}\n","'use client';\n\nimport type { ConfirmActionCopy } from \"../../../copy\";\nimport type { JSX } from 'react';\n\nimport { ConfirmActionDialog, useConfirmAction } from '../../feedback/ConfirmAction';\n\nimport { useCardActions } from './card-actions-context';\n\n/** The `{ ok, error }` envelope a write action answers with. */\nexport interface CardWriteResult {\n ok: boolean;\n error?: string;\n}\n\ninterface RemoveConfirmOptions {\n /** The destructive write. Called ONLY after the operator confirms. */\n write: () => Promise<CardWriteResult>;\n /** The popup's question. */\n title: string;\n /** The row being removed, echoed into the popup so it names a thing. */\n entityName: string;\n /**\n * What happens, and whether it can be walked back. Say which — the difference\n * between a soft delete a recycle bin recovers and one nothing recovers is\n * the whole content of the decision being asked for.\n */\n description: string;\n /** The confirm button's VERB. Required, never defaulted. */\n confirmText: string;\n /** Used when the write fails without a message of its own. */\n fallbackError: string;\n /** The dialog's own words. REQUIRED — this package ships no default copy. */\n copy: ConfirmActionCopy;\n dataTestId: string;\n}\n\ninterface RemoveConfirm {\n /** Hand this to the menu item's `onClick`: it opens the popup, nothing more. */\n request: () => void;\n /** Render alongside the trigger. */\n dialog: JSX.Element;\n}\n\n/**\n * The confirm-before-removing wiring every card/row menu repeats, in one place.\n *\n * It closes the three gaps a hand-rolled version keeps re-opening: the write\n * leaves ONLY on confirm; a refusal is surfaced twice — inside the popup, where\n * the operator is still deciding, and in the shared snackbar behind it — and a\n * failed attempt leaves the popup OPEN to be retried instead of vanishing over\n * a delete that never happened.\n *\n * The difference from {@link useRowConfirm} is which surface it serves: this one\n * is for a SELF-CONTAINED menu that already sits inside a\n * `CardActionsProvider`, so it reads the refresh and the error channel from\n * context and answers the `{ ok, error }` envelope those actions use.\n * `useRowConfirm` is for a grid whose actions are declared as `RowAction[]` and\n * act on a selection.\n *\n * ```tsx\n * const remove = useRemoveConfirm({\n * write: () => deleteCategory({ tenantSlug, id: row.id }),\n * title: copy.deleteTitle,\n * entityName: row.name,\n * description: copy.deleteDescription,\n * confirmText: copy.delete,\n * fallbackError: copy.deleteFailed,\n * dataTestId: 'category-delete-confirm',\n * });\n * ```\n */\nexport function useRemoveConfirm({\n write,\n title,\n entityName,\n description,\n confirmText,\n fallbackError,\n dataTestId,\n copy,\n}: RemoveConfirmOptions): RemoveConfirm {\n const { onRefresh, notifyError } = useCardActions();\n\n const state = useConfirmAction(async () => {\n const result = await write();\n if (!result.ok) {\n const message = result.error ?? fallbackError;\n notifyError(message);\n // Rejecting is what keeps the popup open carrying `message`; the snackbar\n // above is what remains once the operator dismisses it.\n throw new Error(message);\n }\n onRefresh();\n }, fallbackError);\n\n return {\n request: state.request,\n dialog: (\n <ConfirmActionDialog\n state={state}\n copy={copy}\n title={title}\n entityName={entityName}\n description={description}\n confirmText={confirmText}\n dataTestId={dataTestId}\n />\n ),\n };\n}\n"],"mappings":"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","names":["jsx","jsx","jsxs","useState","jsx","useState","jsx"]}
1
+ {"version":3,"sources":["../../src/components/data-display/CardKit/CardKebab.tsx","../../src/components/data-display/CardKit/card-actions-context.tsx","../../src/components/data-display/CardKit/row-actions-to-menu.ts","../../src/components/data-display/CardKit/list-card-parts.tsx","../../src/components/data-display/CardKit/use-row-confirm.tsx","../../src/components/data-display/CardKit/use-remove-confirm.tsx"],"sourcesContent":["'use client';\n\nimport type { JSX } from 'react';\n\nimport { Button } from '../../form/Button';\nimport { DropdownMenu, type DropdownMenuItem } from '../../navigation/DropdownMenu';\n\n/**\n * The \"⋮\" trigger every row and card menu opens from.\n *\n * One component rather than a `DropdownMenu` per entity, so the kebab looks and\n * behaves identically in the card, in the list row and in the table row. The\n * entity's menu owns its own popups; this is the trigger and the item list.\n */\nexport function CardKebab({\n items,\n menuLabel,\n dataTestId,\n}: {\n items: DropdownMenuItem[];\n /**\n * The trigger's accessible name — required, and with no default.\n *\n * It is the only text this component renders, and a screen reader announces\n * it verbatim. A default here would be a word in the origin's language that\n * reads as finished to the next adopter right up until somebody using a\n * screen reader meets it.\n */\n menuLabel: string;\n dataTestId?: string;\n}): JSX.Element {\n return (\n <DropdownMenu\n size=\"sm\"\n items={items}\n trigger={\n <Button variant=\"text\" size=\"sm\" aria-label={menuLabel} dataTestId={dataTestId}>\n ⋮\n </Button>\n }\n />\n );\n}\n","'use client';\n\nimport { createContext, useCallback, useContext, useState, type JSX, type ReactNode } from 'react';\n\nimport { Snackbar } from '@mui/material';\n\nimport { Alert } from '../Alert';\n\n/**\n * The ambient wiring a self-contained row/card menu needs, and nothing else.\n *\n * A menu that owns its own popups still needs three things it cannot know: the\n * tenant it is acting inside, what to do after a write lands, and somewhere to\n * put a failure. Passing those down through the grid, the layout and the card\n * is four props of drilling per entity; holding them in context is one.\n *\n * Kept deliberately SMALL — it is not an edit-dialog host. A menu that read its\n * dialogs from here would stop being portable, because the set of dialogs is\n * per-entity. What it may read is the three facts below.\n *\n * The provider renders ONE shared error snackbar, which is the whole reason the\n * error channel is here rather than per-menu: before it, every page carried its\n * own error alert and every menu had to find its page's.\n */\ninterface CardActionsValue {\n /** The tenant every action in this subtree acts inside. */\n tenantSlug: string;\n /** Re-read the list after a write. */\n onRefresh: () => void;\n /** Surface a failed action's message (the shared snackbar). */\n notifyError: (message: string) => void;\n}\n\nconst CardActionsContext = createContext<CardActionsValue | null>(null);\n\nexport function CardActionsProvider({\n tenantSlug,\n onRefresh,\n errorTitle,\n errorDismissLabel,\n autoHideMs = 6_000,\n children,\n}: {\n tenantSlug: string;\n onRefresh: () => void;\n /**\n * The snackbar's heading — required, no default.\n *\n * It is the one sentence this provider renders on its own, and a default\n * would be the origin's language shipped as a finished-looking silence. The\n * MESSAGE beside it comes from whatever failed, which is already the host's.\n */\n errorTitle: string;\n /** The dismiss on that alert, which carries a glyph only. REQUIRED. */\n errorDismissLabel: string;\n /** How long the snackbar stays up. */\n autoHideMs?: number;\n children: ReactNode;\n}): JSX.Element {\n const [error, setError] = useState<string | null>(null);\n const notifyError = useCallback((message: string) => setError(message), []);\n\n return (\n <CardActionsContext.Provider value={{ tenantSlug, onRefresh, notifyError }}>\n {children}\n <Snackbar\n open={error !== null}\n autoHideDuration={autoHideMs}\n onClose={() => setError(null)}\n anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}\n >\n <span>\n <Alert\n variant=\"danger\"\n title={errorTitle}\n description={error ?? ''}\n closable\n closeLabel={errorDismissLabel}\n onClose={() => setError(null)}\n data-testid=\"card-action-error\"\n />\n </span>\n </Snackbar>\n </CardActionsContext.Provider>\n );\n}\n\n/**\n * Read the tenant + refresh + error wiring.\n *\n * THROWS outside a provider rather than answering a null object, deliberately:\n * a menu whose `onRefresh` silently did nothing would leave the operator\n * looking at a stale row after a delete that worked, which reads as the delete\n * having failed. Failing at mount says where the provider is missing.\n */\nexport function useCardActions(): CardActionsValue {\n const value = useContext(CardActionsContext);\n if (!value) {\n throw new Error('useCardActions must be used within a CardActionsProvider');\n }\n return value;\n}\n","import type { DropdownMenuItem } from '../../navigation/DropdownMenu';\nimport type { RowAction } from '../DataViews';\n\n/**\n * Adapt a list of {@link RowAction}s into single-row kebab items.\n *\n * Applies each action's `isVisible` guard, resolves its per-row `rowLabel`\n * override, carries its colour, and fires `onSelect([row])` on click.\n *\n * The point is that ONE definition drives both surfaces. An entity that already\n * declares its per-row actions as `RowAction[]` — because the table's multi-\n * select menu needs them — gets the card's kebab from the same array through\n * `renderRowMenu` / `renderCard`, with no second list to keep in step. Two\n * lists is how a \"Duplicar\" that appears in the table and not on the card\n * happens, and nothing reports it.\n */\nexport function rowActionsToMenuItems<T extends Record<string, unknown>>(\n actions: RowAction<T>[],\n row: T,\n): DropdownMenuItem[] {\n return actions\n .filter((action) => action.isVisible?.(row) ?? true)\n .map((action) => ({\n id: action.id,\n label: action.rowLabel?.(row) ?? action.label,\n color: action.color,\n onClick: () => void action.onSelect([row]),\n }));\n}\n","'use client';\n\nimport type { JSX, ReactNode } from 'react';\n\nimport { Box } from '../../../mui/Box';\n\nimport type { CardFactProps, CardLedgerLine } from './CardKit.types';\n\n/**\n * THE SHARED FURNITURE OF AN EXPANDED LIST-CARD BODY.\n *\n * `BaseListCard` says nothing about what goes inside a row's expanded area — it\n * spans the rails and stops, because the design belongs to the consumer. Right\n * for the card; wrong as the place where every entity in an admin app invents\n * its own two-column layout, its own label/value pair and its own ledger.\n *\n * So the four shapes that actually repeat live here, once. Nothing here knows a\n * domain: they take `ReactNode` and lay it out.\n */\n\n/** The expanded body's two-column split: detail on the left, a ledger right. */\nexport function DetailColumns({ left, right }: { left: ReactNode; right: ReactNode }): JSX.Element {\n return (\n <Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', md: '1fr 1fr' }, gap: 3 }}>\n <Box sx={{ minWidth: 0 }}>{left}</Box>\n <Box sx={{ minWidth: 0 }}>{right}</Box>\n </Box>\n );\n}\n\n/**\n * One labelled fact inside an expanded body.\n *\n * Horizontal, not stacked: the body is already a wide area with room for a\n * label beside its value, and stacking them there would ape the summary rails\n * above without their alignment.\n */\nexport function Fact({ label, value }: CardFactProps): JSX.Element {\n return (\n <Box sx={{ display: 'flex', justifyContent: 'space-between', gap: 2, fontSize: 13, py: 0.25 }}>\n <Box component=\"span\" sx={{ color: 'text.secondary', flexShrink: 0 }}>\n {label}\n </Box>\n <Box component=\"span\" sx={{ minWidth: 0, textAlign: 'right', overflowWrap: 'anywhere' }}>\n {value}\n </Box>\n </Box>\n );\n}\n\n/**\n * A money ledger: rows of label/amount, then a ruled total.\n *\n * The total is separated by a RULE rather than by weight alone, because a\n * subtotal and a total in one column with only bold between them is the classic\n * misread — the eye lands on whichever number is larger.\n *\n * The figures arrive pre-formatted. This is a layout, and a component that\n * formatted money would be choosing a locale on the consumer's behalf.\n */\nexport function Ledger({\n lines,\n total,\n}: {\n lines: CardLedgerLine[];\n total: { label: string; value: ReactNode };\n}): JSX.Element {\n return (\n <Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.25 }}>\n {lines.map((line) => (\n <Box\n key={line.label}\n sx={{\n display: 'flex',\n justifyContent: 'space-between',\n fontSize: 13,\n color: line.tone === 'info' ? 'info.main' : 'text.secondary',\n }}\n >\n <Box component=\"span\">{line.label}</Box>\n <Box component=\"span\" sx={{ fontVariantNumeric: 'tabular-nums' }}>\n {line.value}\n </Box>\n </Box>\n ))}\n <Box\n sx={{\n display: 'flex',\n justifyContent: 'space-between',\n fontWeight: 700,\n borderTop: 1,\n borderColor: 'divider',\n pt: 0.5,\n mt: 0.5,\n }}\n >\n <Box component=\"span\">{total.label}</Box>\n <Box component=\"span\" sx={{ fontVariantNumeric: 'tabular-nums' }}>\n {total.value}\n </Box>\n </Box>\n </Box>\n );\n}\n\n/** A heading inside the expanded body, for a section that needs naming. */\nexport function BodyHeading({ children }: { children: ReactNode }): JSX.Element {\n return (\n <Box\n sx={{\n fontSize: 11,\n fontWeight: 700,\n letterSpacing: 0.4,\n textTransform: 'uppercase',\n color: 'text.disabled',\n mb: 0.5,\n }}\n >\n {children}\n </Box>\n );\n}\n\n/**\n * A wrapping run of small items — permission names, role names, target ids.\n *\n * The whole reason several entities earn a list card at all: a `string[]` has no\n * honest table cell. Truncated to \"3 items\" it says nothing; printed in full it\n * destroys the column. Here it simply wraps.\n */\nexport function TagList({\n items,\n empty,\n}: {\n items: readonly string[];\n /** What to show for an empty run — the consumer's sentence, no default. */\n empty: string;\n}): JSX.Element {\n if (items.length === 0) {\n return <Box sx={{ fontSize: 13, color: 'text.disabled' }}>{empty}</Box>;\n }\n return (\n <Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>\n {items.map((item) => (\n <Box\n key={item}\n component=\"span\"\n sx={{\n fontSize: 12,\n px: 0.75,\n py: 0.25,\n borderRadius: 1,\n bgcolor: 'action.hover',\n color: 'text.secondary',\n fontFamily: 'monospace',\n }}\n >\n {item}\n </Box>\n ))}\n </Box>\n );\n}\n","'use client';\n\nimport { useState, type JSX } from 'react';\n\nimport type { ConfirmActionCopy } from '../../../copy';\nimport { ConfirmActionDialog, useConfirmAction } from '../../feedback/ConfirmAction';\n\n/**\n * How the popup should read for the rows about to be acted on.\n *\n * Built from the SELECTION rather than from a single row, because the same\n * guard covers a bulk action: \"Delete 4 categories?\" and \"Delete the category\n * Drinks?\" are the same question asked of a different-sized selection — and an\n * operator who multi-selected by mistake is exactly who this popup is for.\n */\nexport interface RowConfirmCopy {\n title: string;\n description: string;\n /** Echoed into the popup; omit for a multi-row selection the title covers. */\n entityName?: string;\n /** The VERB. Required, never defaulted — see {@link useRowConfirm}. */\n confirmText: string;\n /** Override where the word \"cancel\" would mean the ACTION itself. */\n cancelText?: string;\n /** Require typing this before the confirm enables — for the unrecoverable few. */\n typeToConfirm?: string;\n}\n\ninterface RowConfirmOptions<T> {\n /**\n * The destructive write, over the whole selection. Called ONLY after the\n * operator confirms; throw (or reject) to keep the popup open carrying the\n * message.\n */\n write: (rows: T[]) => Promise<unknown>;\n /** The wording, given the selection. */\n describe: (rows: T[]) => RowConfirmCopy;\n dataTestId: string;\n /** Shown when `write` fails without a message of its own. */\n errorText: string;\n /** The dialog's own words. REQUIRED — this package ships no default copy. */\n copy: ConfirmActionCopy;\n}\n\nexport interface RowConfirm<T> {\n /** Hand this a grid action's `rows`: it opens the popup and writes nothing. */\n request: (rows: T[]) => void;\n /** Render next to the grid. */\n dialog: JSX.Element | null;\n}\n\n/**\n * Confirmation for a grid's `RowAction` — the per-row kebab entry and the\n * multi-select actions menu both, since they run the same handler.\n *\n * The SELECTION is held while the popup is open, so the write sees exactly the\n * rows the operator was looking at when they were asked. Re-reading the grid's\n * live selection at confirm time is the bug this shape avoids: the list can\n * refresh underneath an open dialog.\n *\n * `confirmText` is required and comes from `describe`, with no default. A verb\n * defaulted in the origin's language reads as finished to the next adopter\n * right up until an operator sees it — and `ConfirmOptions` already refuses to\n * default one for the same reason.\n */\nexport function useRowConfirm<T>({\n write,\n describe,\n dataTestId,\n errorText,\n copy,\n}: RowConfirmOptions<T>): RowConfirm<T> {\n const [target, setTarget] = useState<T[] | null>(null);\n\n const state = useConfirmAction(async () => {\n if (!target) return;\n await write(target);\n setTarget(null);\n }, errorText);\n\n const request = (rows: T[]): void => {\n // An empty selection has nothing to confirm — asking would be a popup about\n // nothing, and confirming it would write nothing.\n if (rows.length === 0) return;\n setTarget(rows);\n state.request();\n };\n\n return {\n request,\n dialog: target ? (\n <ConfirmActionDialog state={state}\n copy={copy} {...describe(target)} dataTestId={dataTestId} />\n ) : null,\n };\n}\n","'use client';\n\nimport type { ConfirmActionCopy } from \"../../../copy\";\nimport type { JSX } from 'react';\n\nimport { ConfirmActionDialog, useConfirmAction } from '../../feedback/ConfirmAction';\n\nimport { useCardActions } from './card-actions-context';\n\n/** The `{ ok, error }` envelope a write action answers with. */\nexport interface CardWriteResult {\n ok: boolean;\n error?: string;\n}\n\ninterface RemoveConfirmOptions {\n /** The destructive write. Called ONLY after the operator confirms. */\n write: () => Promise<CardWriteResult>;\n /** The popup's question. */\n title: string;\n /** The row being removed, echoed into the popup so it names a thing. */\n entityName: string;\n /**\n * What happens, and whether it can be walked back. Say which — the difference\n * between a soft delete a recycle bin recovers and one nothing recovers is\n * the whole content of the decision being asked for.\n */\n description: string;\n /** The confirm button's VERB. Required, never defaulted. */\n confirmText: string;\n /** Used when the write fails without a message of its own. */\n fallbackError: string;\n /** The dialog's own words. REQUIRED — this package ships no default copy. */\n copy: ConfirmActionCopy;\n dataTestId: string;\n}\n\ninterface RemoveConfirm {\n /** Hand this to the menu item's `onClick`: it opens the popup, nothing more. */\n request: () => void;\n /** Render alongside the trigger. */\n dialog: JSX.Element;\n}\n\n/**\n * The confirm-before-removing wiring every card/row menu repeats, in one place.\n *\n * It closes the three gaps a hand-rolled version keeps re-opening: the write\n * leaves ONLY on confirm; a refusal is surfaced twice — inside the popup, where\n * the operator is still deciding, and in the shared snackbar behind it — and a\n * failed attempt leaves the popup OPEN to be retried instead of vanishing over\n * a delete that never happened.\n *\n * The difference from {@link useRowConfirm} is which surface it serves: this one\n * is for a SELF-CONTAINED menu that already sits inside a\n * `CardActionsProvider`, so it reads the refresh and the error channel from\n * context and answers the `{ ok, error }` envelope those actions use.\n * `useRowConfirm` is for a grid whose actions are declared as `RowAction[]` and\n * act on a selection.\n *\n * ```tsx\n * const remove = useRemoveConfirm({\n * write: () => deleteCategory({ tenantSlug, id: row.id }),\n * title: copy.deleteTitle,\n * entityName: row.name,\n * description: copy.deleteDescription,\n * confirmText: copy.delete,\n * fallbackError: copy.deleteFailed,\n * dataTestId: 'category-delete-confirm',\n * });\n * ```\n */\nexport function useRemoveConfirm({\n write,\n title,\n entityName,\n description,\n confirmText,\n fallbackError,\n dataTestId,\n copy,\n}: RemoveConfirmOptions): RemoveConfirm {\n const { onRefresh, notifyError } = useCardActions();\n\n const state = useConfirmAction(async () => {\n const result = await write();\n if (!result.ok) {\n const message = result.error ?? fallbackError;\n notifyError(message);\n // Rejecting is what keeps the popup open carrying `message`; the snackbar\n // above is what remains once the operator dismisses it.\n throw new Error(message);\n }\n onRefresh();\n }, fallbackError);\n\n return {\n request: state.request,\n dialog: (\n <ConfirmActionDialog\n state={state}\n copy={copy}\n title={title}\n entityName={entityName}\n description={description}\n confirmText={confirmText}\n dataTestId={dataTestId}\n />\n ),\n };\n}\n"],"mappings":"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","names":["jsx","jsx","jsxs","useState","jsx","useState","jsx"]}
@@ -236,6 +236,7 @@ var arrowSx = (theme, color, isOverlay) => ({
236
236
  });
237
237
  var CarouselArrows = (props) => {
238
238
  const {
239
+ copy,
239
240
  onPrev,
240
241
  onNext,
241
242
  position,
@@ -258,7 +259,7 @@ var CarouselArrows = (props) => {
258
259
  disabled: disabled || disablePrev,
259
260
  size: muiSize(size),
260
261
  className,
261
- "aria-label": "Previous slide",
262
+ "aria-label": copy.previous,
262
263
  "data-testid": "carousel-prev-button",
263
264
  sx: { ...sx, left: offset, ...style },
264
265
  children: /* @__PURE__ */ jsx(ArrowBackIosIcon, {})
@@ -269,7 +270,7 @@ var CarouselArrows = (props) => {
269
270
  {
270
271
  onClick: onNext,
271
272
  disabled: disabled || disableNext,
272
- "aria-label": "Next slide",
273
+ "aria-label": copy.next,
273
274
  size: muiSize(size),
274
275
  className,
275
276
  "data-testid": "carousel-next-button",
@@ -374,6 +375,7 @@ var CarouselThumbnails = ({
374
375
  // src/components/data-display/Carousel/Carousel.chrome.tsx
375
376
  import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
376
377
  var CarouselChrome = ({
378
+ copy,
377
379
  items,
378
380
  activeIndex,
379
381
  loop,
@@ -392,6 +394,7 @@ var CarouselChrome = ({
392
394
  showArrows && !disabled && /* @__PURE__ */ jsx2(
393
395
  CarouselArrows,
394
396
  {
397
+ copy,
395
398
  onPrev,
396
399
  onNext,
397
400
  position: arrowPosition,