@12-apps/ui 6.24.2 → 6.24.4

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 (70) hide show
  1. package/NATIVE.md +1 -1
  2. package/dist/_internal/components/data-display/Alert/Alert.js +5 -3
  3. package/dist/_internal/components/data-display/Alert/Alert.metrics.js +1 -1
  4. package/dist/_internal/components/data-display/Alert/Alert.styles.js +2 -2
  5. package/dist/_internal/components/data-display/Banner/Banner.js +7 -2
  6. package/dist/_internal/components/data-display/Banner/Banner.styles.js +10 -1
  7. package/dist/_internal/components/data-display/CardKit/card-actions-context.js +4 -4
  8. package/dist/_internal/components/data-display/CardKit/use-remove-confirm.js +5 -5
  9. package/dist/_internal/components/data-display/DataViews/DataViewsTableBase.js +5 -5
  10. package/dist/_internal/components/data-display/DataViews/data-views-table-parts.js +4 -4
  11. package/dist/_internal/components/data-display/index.js +5 -5
  12. package/dist/_internal/components/index.js +6 -6
  13. package/dist/_internal/components/navigation/AppHeader/AppHeader.identity.js +1 -1
  14. package/dist/_internal/components/navigation/index.js +1 -1
  15. package/dist/{chunk-7L6LAVJ7.js → chunk-4IVDVZZ2.js} +25 -11
  16. package/dist/chunk-4IVDVZZ2.js.map +1 -0
  17. package/dist/{chunk-U2YJCNRU.js → chunk-4T7TXRGM.js} +2 -2
  18. package/dist/{chunk-E27Y7FF2.js → chunk-57Z2ND3N.js} +9 -10
  19. package/dist/{chunk-E27Y7FF2.js.map → chunk-57Z2ND3N.js.map} +1 -1
  20. package/dist/{chunk-DI7HGYPX.js → chunk-JPV45AD6.js} +2 -2
  21. package/dist/{chunk-PRUSQQDT.js → chunk-KM5O4ICK.js} +2 -2
  22. package/dist/{chunk-2YEZONLX.js → chunk-M4LFDGI6.js} +30 -5
  23. package/dist/chunk-M4LFDGI6.js.map +1 -0
  24. package/dist/{chunk-O3336DJ6.js → chunk-SYJMI2NP.js} +2 -2
  25. package/dist/{chunk-W2YNUUVJ.js → chunk-TU6RNZUQ.js} +117 -78
  26. package/dist/chunk-TU6RNZUQ.js.map +1 -0
  27. package/dist/{chunk-B2Y3BABA.js → chunk-TXKZEKJN.js} +6 -9
  28. package/dist/chunk-TXKZEKJN.js.map +1 -0
  29. package/dist/{chunk-QFVFHIHE.js → chunk-WNMYWQ7D.js} +98 -30
  30. package/dist/chunk-WNMYWQ7D.js.map +1 -0
  31. package/dist/data-display/Alert.js +3 -3
  32. package/dist/data-display/Banner.js +7 -2
  33. package/dist/data-display/CardKit.js +5 -5
  34. package/dist/data-display/DataViews.js +5 -5
  35. package/dist/native/data-display/Alert.js +2 -5
  36. package/dist/native/data-display/Alert.js.map +1 -1
  37. package/dist/navigation/AppHeader.js +1 -1
  38. package/dist/types/components/data-display/Alert/Alert.d.ts +14 -0
  39. package/dist/types/components/data-display/Alert/Alert.d.ts.map +1 -1
  40. package/dist/types/components/data-display/Alert/Alert.metrics.d.ts +66 -23
  41. package/dist/types/components/data-display/Alert/Alert.metrics.d.ts.map +1 -1
  42. package/dist/types/components/data-display/Alert/Alert.styles.d.ts.map +1 -1
  43. package/dist/types/components/data-display/Banner/Banner.d.ts.map +1 -1
  44. package/dist/types/components/data-display/Banner/Banner.styles.d.ts +3 -0
  45. package/dist/types/components/data-display/Banner/Banner.styles.d.ts.map +1 -1
  46. package/dist/types/components/navigation/AppHeader/AppHeader.identity.d.ts +12 -5
  47. package/dist/types/components/navigation/AppHeader/AppHeader.identity.d.ts.map +1 -1
  48. package/dist/types/components/navigation/AppHeader/AppHeader.types.d.ts +12 -4
  49. package/dist/types/components/navigation/AppHeader/AppHeader.types.d.ts.map +1 -1
  50. package/dist/types-native/components/data-display/Alert/Alert.metrics.d.ts +66 -23
  51. package/dist/types-native/components/data-display/Alert/Alert.metrics.d.ts.map +1 -1
  52. package/package.json +1 -1
  53. package/src/components/data-display/Alert/Alert.metrics.ts +66 -9
  54. package/src/components/data-display/Alert/Alert.styles.tsx +4 -5
  55. package/src/components/data-display/Alert/Alert.tsx +139 -21
  56. package/src/components/data-display/Alert/NATIVE-NOTES.md +1 -1
  57. package/src/components/data-display/Banner/Banner.styles.ts +40 -2
  58. package/src/components/data-display/Banner/Banner.tsx +22 -9
  59. package/src/components/navigation/AppHeader/AppHeader.identity.tsx +159 -86
  60. package/src/components/navigation/AppHeader/AppHeader.md +12 -5
  61. package/src/components/navigation/AppHeader/AppHeader.types.ts +12 -4
  62. package/dist/chunk-2YEZONLX.js.map +0 -1
  63. package/dist/chunk-7L6LAVJ7.js.map +0 -1
  64. package/dist/chunk-B2Y3BABA.js.map +0 -1
  65. package/dist/chunk-QFVFHIHE.js.map +0 -1
  66. package/dist/chunk-W2YNUUVJ.js.map +0 -1
  67. /package/dist/{chunk-U2YJCNRU.js.map → chunk-4T7TXRGM.js.map} +0 -0
  68. /package/dist/{chunk-DI7HGYPX.js.map → chunk-JPV45AD6.js.map} +0 -0
  69. /package/dist/{chunk-PRUSQQDT.js.map → chunk-KM5O4ICK.js.map} +0 -0
  70. /package/dist/{chunk-O3336DJ6.js.map → chunk-SYJMI2NP.js.map} +0 -0
@@ -1,3 +1,8 @@
1
+ import {
2
+ ACTIVE,
3
+ HOVER
4
+ } from "./chunk-TXKZEKJN.js";
5
+
1
6
  // src/components/data-display/Banner/Banner.styles.ts
2
7
  import { alpha, keyframes } from "@mui/material/styles/index.js";
3
8
  var fadeInSlide = keyframes`
@@ -91,22 +96,42 @@ var bannerPartStyles = (theme, colorPalette) => ({
91
96
  opacity: 0.7,
92
97
  marginLeft: theme.spacing(1),
93
98
  flexShrink: 0,
99
+ // Opacity and a wash only: the dismiss used to spin 90deg, which drew the
100
+ // eye to the way out rather than to the message. Same fix as `Alert`'s
101
+ // close button, which is the sibling surface (FUT-1458).
94
102
  "&:hover": {
95
103
  opacity: 1,
96
- backgroundColor: alpha(colorPalette.main, 0.1),
97
- transform: "rotate(90deg)"
104
+ backgroundColor: alpha(colorPalette.main, 0.1)
98
105
  },
99
- "&:focus": {
106
+ "&:focus-visible": {
100
107
  opacity: 1,
101
108
  outline: `2px solid ${colorPalette.main}`,
102
109
  outlineOffset: "2px"
103
110
  }
104
111
  }
105
112
  });
113
+ var tint = (theme, a) => `inset 0 0 0 100vmax ${alpha(theme.palette.mode === "light" ? "#000000" : "#ffffff", a)}`;
114
+ var bannerPointerStates = (theme) => ({
115
+ "&:hover": {
116
+ boxShadow: tint(theme, HOVER.tintAlpha)
117
+ },
118
+ // The `:not(:has(…))` names CONTROLS, not any descendant: `:active` matches
119
+ // an ancestor of whatever is pressed, so without it pressing the dismiss or
120
+ // an action dimmed the message that owns it. Excluding `:has(:active)`
121
+ // wholesale does not work — pressing the TEXT makes that text `:active` too,
122
+ // so the guard matched on every press and the state never fired.
123
+ '&:active:not(:has(button:active, a:active, [role="button"]:active))': {
124
+ boxShadow: tint(theme, ACTIVE.tintAlpha),
125
+ transition: "none"
126
+ }
127
+ });
128
+ var BANNER_TINT_S = ACTIVE.ms / 1e3;
106
129
 
107
130
  export {
108
131
  fadeInSlide,
109
132
  getVariantColor,
110
- bannerPartStyles
133
+ bannerPartStyles,
134
+ bannerPointerStates,
135
+ BANNER_TINT_S
111
136
  };
112
- //# sourceMappingURL=chunk-2YEZONLX.js.map
137
+ //# sourceMappingURL=chunk-M4LFDGI6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/data-display/Banner/Banner.styles.ts"],"sourcesContent":["import { alpha, keyframes } from '@mui/material/styles/index.js';\nimport type { CSSObject, PaletteColor, Theme } from '@mui/material/styles/index.js';\n\nimport type { BannerVariant } from './Banner.types';\nimport { ACTIVE, HOVER } from '../Alert/Alert.metrics';\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 // Opacity and a wash only: the dismiss used to spin 90deg, which drew the\n // eye to the way out rather than to the message. Same fix as `Alert`'s\n // close button, which is the sibling surface (FUT-1458).\n '&:hover': {\n opacity: 1,\n backgroundColor: alpha(colorPalette.main, 0.1),\n },\n\n '&:focus-visible': {\n opacity: 1,\n outline: `2px solid ${colorPalette.main}`,\n outlineOffset: '2px',\n },\n },\n});\n\n/**\n * The pointer states, from `Alert`'s own table.\n *\n * Banner had NO hover at all — measured ΔE76 0.00 on every variant in both\n * modes — so moving between the two surfaces gave feedback on one and silence\n * on the other. They are the same kind of thing and now answer a pointer the\n * same way.\n *\n * Neither block may declare `transition`: `:active` under a mouse always\n * implies `:hover`, so a shorthand here would outrank the root's and reset the\n * opacity fade the press is built on.\n */\n/** The same flat inset tint `Alert` paints; see `ACTIVE` for why it is a layer. */\nconst tint = (theme: Theme, a: number): string =>\n `inset 0 0 0 100vmax ${alpha(theme.palette.mode === 'light' ? '#000000' : '#ffffff', a)}`;\n\nexport const bannerPointerStates = (theme: Theme): CSSObject => ({\n '&:hover': {\n boxShadow: tint(theme, HOVER.tintAlpha),\n },\n\n // The `:not(:has(…))` names CONTROLS, not any descendant: `:active` matches\n // an ancestor of whatever is pressed, so without it pressing the dismiss or\n // an action dimmed the message that owns it. Excluding `:has(:active)`\n // wholesale does not work — pressing the TEXT makes that text `:active` too,\n // so the guard matched on every press and the state never fired.\n '&:active:not(:has(button:active, a:active, [role=\"button\"]:active))': {\n boxShadow: tint(theme, ACTIVE.tintAlpha),\n transition: 'none',\n },\n});\n\n/** The seconds the tint takes easing back, shared with `Alert`. */\nexport const BANNER_TINT_S = ACTIVE.ms / 1000;\n"],"mappings":";;;;;;AAAA,SAAS,OAAO,iBAAiB;AAO1B,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;AAAA;AAAA;AAAA,IAKZ,WAAW;AAAA,MACT,SAAS;AAAA,MACT,iBAAiB,MAAM,aAAa,MAAM,GAAG;AAAA,IAC/C;AAAA,IAEA,mBAAmB;AAAA,MACjB,SAAS;AAAA,MACT,SAAS,aAAa,aAAa,IAAI;AAAA,MACvC,eAAe;AAAA,IACjB;AAAA,EACF;AACF;AAeA,IAAM,OAAO,CAAC,OAAc,MAC1B,uBAAuB,MAAM,MAAM,QAAQ,SAAS,UAAU,YAAY,WAAW,CAAC,CAAC;AAElF,IAAM,sBAAsB,CAAC,WAA6B;AAAA,EAC/D,WAAW;AAAA,IACT,WAAW,KAAK,OAAO,MAAM,SAAS;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,uEAAuE;AAAA,IACrE,WAAW,KAAK,OAAO,OAAO,SAAS;AAAA,IACvC,YAAY;AAAA,EACd;AACF;AAGO,IAAM,gBAAgB,OAAO,KAAK;","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Alert
3
- } from "./chunk-QFVFHIHE.js";
3
+ } from "./chunk-WNMYWQ7D.js";
4
4
 
5
5
  // src/components/data-display/CardKit/card-actions-context.tsx
6
6
  import { createContext, useCallback, useContext, useState } from "react";
@@ -54,4 +54,4 @@ export {
54
54
  CardActionsProvider,
55
55
  useCardActions
56
56
  };
57
- //# sourceMappingURL=chunk-O3336DJ6.js.map
57
+ //# sourceMappingURL=chunk-SYJMI2NP.js.map
@@ -68,35 +68,95 @@ var AppHeaderStatus = ({
68
68
  );
69
69
  };
70
70
  AppHeaderStatus.displayName = "AppHeaderStatus";
71
- var IdentityText = ({ title, subtitle, status, size = "md", interactive, disclosed, dataTestId }) => /* @__PURE__ */ jsxs(Box, { sx: { minWidth: 0, display: "flex", flexDirection: "column", gap: 0.25 }, children: [
72
- /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 0.25, minWidth: 0 }, children: [
73
- /* @__PURE__ */ jsx(
74
- Typography,
75
- {
76
- component: "span",
77
- "data-testid": `${dataTestId}-title`,
78
- sx: { fontSize: TITLE_PX[size], fontWeight: 700, lineHeight: 1.2, ...ONE_LINE },
79
- children: title
71
+ var IdentityTitle = ({ title, size = "md", interactive, disclosed, dataTestId }) => /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 0.25, minWidth: 0, width: "100%" }, children: [
72
+ /* @__PURE__ */ jsx(
73
+ Typography,
74
+ {
75
+ component: "span",
76
+ "data-testid": `${dataTestId}-title`,
77
+ sx: { fontSize: TITLE_PX[size], fontWeight: 700, lineHeight: 1.2, ...ONE_LINE },
78
+ children: title
79
+ }
80
+ ),
81
+ interactive && /* @__PURE__ */ jsx(
82
+ KeyboardArrowDown,
83
+ {
84
+ "aria-hidden": true,
85
+ "data-testid": `${dataTestId}-chevron`,
86
+ sx: {
87
+ flex: "0 0 auto",
88
+ fontSize: 20,
89
+ opacity: 0.7,
90
+ transition: "transform 150ms ease",
91
+ transform: disclosed ? "rotate(180deg)" : "none"
80
92
  }
81
- ),
82
- interactive && /* @__PURE__ */ jsx(
83
- KeyboardArrowDown,
93
+ }
94
+ )
95
+ ] });
96
+ var discloseSx = {
97
+ display: "flex",
98
+ alignItems: "center",
99
+ gap: 0.25,
100
+ width: "100%",
101
+ minWidth: 0,
102
+ appearance: "none",
103
+ px: 0.5,
104
+ mx: -0.5,
105
+ py: 0.25,
106
+ my: -0.25,
107
+ border: "none",
108
+ borderRadius: 1.5,
109
+ background: "none",
110
+ color: "inherit",
111
+ font: "inherit",
112
+ textAlign: "left",
113
+ cursor: "pointer",
114
+ // Guarded: on a touch screen an unguarded `:hover` sticks after the tap that
115
+ // opened the panel, so the row stays highlighted behind it.
116
+ "@media (hover: hover)": { "&:hover": { backgroundColor: "action.hover" } },
117
+ "&:focus-visible": { outline: "2px solid", outlineColor: "primary.main", outlineOffset: 2 }
118
+ };
119
+ var IdentityText = ({
120
+ title,
121
+ subtitle,
122
+ status,
123
+ size = "md",
124
+ onDisclose,
125
+ disclosed,
126
+ discloseLabel,
127
+ className,
128
+ dataTestId
129
+ }) => {
130
+ const name = /* @__PURE__ */ jsx(
131
+ IdentityTitle,
132
+ {
133
+ title,
134
+ size,
135
+ interactive: Boolean(onDisclose),
136
+ disclosed,
137
+ dataTestId
138
+ }
139
+ );
140
+ return /* @__PURE__ */ jsxs(Box, { sx: { minWidth: 0, flex: 1, display: "flex", flexDirection: "column", gap: 0.25 }, children: [
141
+ onDisclose ? /* @__PURE__ */ jsx(
142
+ Box,
84
143
  {
85
- "aria-hidden": true,
86
- "data-testid": `${dataTestId}-chevron`,
87
- sx: {
88
- flex: "0 0 auto",
89
- fontSize: 20,
90
- opacity: 0.7,
91
- transition: "transform 150ms ease",
92
- transform: disclosed ? "rotate(180deg)" : "none"
93
- }
144
+ component: "button",
145
+ type: "button",
146
+ className,
147
+ "data-testid": dataTestId,
148
+ onClick: onDisclose,
149
+ "aria-haspopup": "dialog",
150
+ "aria-expanded": disclosed,
151
+ "aria-label": discloseLabel ?? `Detalhes de ${title}`,
152
+ sx: discloseSx,
153
+ children: name
94
154
  }
95
- )
96
- ] }),
97
- subtitle && /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", component: "span", sx: ONE_LINE, children: subtitle }),
98
- status
99
- ] });
155
+ ) : name,
156
+ subtitle && /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", component: "span", sx: ONE_LINE, children: subtitle }),
157
+ status
158
+ ] });
159
+ };
100
160
  var IdentitySkeleton = ({
101
161
  size,
102
162
  dataTestId
@@ -138,59 +198,38 @@ var AppHeaderIdentity = ({
138
198
  dataTestId = "app-header-identity"
139
199
  }) => {
140
200
  if (loading) return /* @__PURE__ */ jsx(IdentitySkeleton, { size, dataTestId });
141
- const body = /* @__PURE__ */ jsxs(Fragment, { children: [
142
- mark ?? /* @__PURE__ */ jsx(
143
- AppHeaderBrand,
144
- {
145
- name: title,
146
- logoUrl,
147
- seedColor,
148
- size,
149
- dataTestId: `${dataTestId}-brand`
150
- }
151
- ),
152
- /* @__PURE__ */ jsx(
153
- IdentityText,
154
- {
155
- title,
156
- subtitle,
157
- status,
158
- size,
159
- interactive: Boolean(onDisclose),
160
- disclosed,
161
- dataTestId
162
- }
163
- )
164
- ] });
165
- if (!onDisclose) {
166
- return /* @__PURE__ */ jsx(Box, { className, "data-testid": dataTestId, sx: rowSx, children: body });
167
- }
168
- return /* @__PURE__ */ jsx(
201
+ return /* @__PURE__ */ jsxs(
169
202
  Box,
170
203
  {
171
- component: "button",
172
- type: "button",
173
- className,
174
- "data-testid": dataTestId,
175
- onClick: onDisclose,
176
- "aria-haspopup": "dialog",
177
- "aria-expanded": disclosed,
178
- "aria-label": discloseLabel ?? `Detalhes de ${title}`,
179
- sx: {
180
- ...rowSx,
181
- appearance: "none",
182
- p: 0.5,
183
- ml: -0.5,
184
- border: "none",
185
- borderRadius: 1.5,
186
- background: "none",
187
- color: "inherit",
188
- font: "inherit",
189
- cursor: "pointer",
190
- "&:hover": { backgroundColor: "action.hover" },
191
- "&:focus-visible": { outline: "2px solid", outlineColor: "primary.main", outlineOffset: 2 }
192
- },
193
- children: body
204
+ className: onDisclose ? void 0 : className,
205
+ "data-testid": onDisclose ? void 0 : dataTestId,
206
+ sx: rowSx,
207
+ children: [
208
+ mark ?? /* @__PURE__ */ jsx(
209
+ AppHeaderBrand,
210
+ {
211
+ name: title,
212
+ logoUrl,
213
+ seedColor,
214
+ size,
215
+ dataTestId: `${dataTestId}-brand`
216
+ }
217
+ ),
218
+ /* @__PURE__ */ jsx(
219
+ IdentityText,
220
+ {
221
+ title,
222
+ subtitle,
223
+ status,
224
+ size,
225
+ onDisclose,
226
+ disclosed,
227
+ discloseLabel,
228
+ className,
229
+ dataTestId
230
+ }
231
+ )
232
+ ]
194
233
  }
195
234
  );
196
235
  };
@@ -200,4 +239,4 @@ export {
200
239
  AppHeaderStatus,
201
240
  AppHeaderIdentity
202
241
  };
203
- //# sourceMappingURL=chunk-W2YNUUVJ.js.map
242
+ //# sourceMappingURL=chunk-TU6RNZUQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/navigation/AppHeader/AppHeader.identity.tsx"],"sourcesContent":["'use client';\n\nimport KeyboardArrowDown from '@mui/icons-material/KeyboardArrowDown';\nimport Box from '@mui/material/Box/index.js';\nimport Skeleton from '@mui/material/Skeleton/index.js';\nimport Typography from '@mui/material/Typography/index.js';\nimport { useTheme } from '@mui/material/styles/index.js';\nimport React from 'react';\n\nimport { accentFor, type SizeValue } from '../../../tokens/scales';\n\nimport { AppHeaderBrand } from './AppHeader.brand';\nimport type { AppHeaderIdentityProps, AppHeaderStatusProps } from './AppHeader.types';\n\n/** Title type scale, per house size step. */\nconst TITLE_PX: Record<SizeValue, number> = { xs: 14, sm: 15, md: 17, lg: 20, xl: 24 };\n\n/** One line, ellipsised — a long store name must not push the actions off-screen. */\nconst ONE_LINE = {\n minWidth: 0,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n} as const;\n\n/**\n * The dotted state line: \"● Aberto agora · Retirada no balcão\".\n *\n * Segments are joined here rather than by the caller because the separator is a\n * presentational choice — a caller that pre-joins its own string can never be\n * restyled, and each segment loses the chance to carry its own markup.\n */\nexport const AppHeaderStatus: React.FC<AppHeaderStatusProps> = ({\n tone,\n items,\n separator = '·',\n className,\n dataTestId = 'app-header-status',\n}) => {\n const theme = useTheme();\n const visible = items.filter((item) => item !== null && item !== undefined && item !== '');\n if (visible.length === 0) return null;\n\n return (\n <Box\n className={className}\n data-testid={dataTestId}\n sx={{ display: 'flex', alignItems: 'center', gap: 0.75, ...ONE_LINE }}\n >\n {tone && (\n <Box\n aria-hidden\n data-testid={`${dataTestId}-dot`}\n sx={{\n width: 8,\n height: 8,\n flex: '0 0 auto',\n borderRadius: '50%',\n backgroundColor: accentFor(theme, tone).main,\n }}\n />\n )}\n <Typography variant=\"body2\" color=\"text.secondary\" component=\"span\" sx={ONE_LINE}>\n {visible.map((item, index) => (\n // Index keys: the segments are positional presentation, and nothing\n // here reorders or is keyed on identity elsewhere.\n <React.Fragment key={index}>\n {index > 0 && (\n <>\n {/* Real spaces, not margin. A screen reader reads `textContent`,\n and a CSS-only gap runs \"Aberto agora\" straight into\n \"Retirada no balcão\" as one word. The glyph itself is\n hidden — it is punctuation, not something to announce. */}\n {' '}\n <Box component=\"span\" aria-hidden>\n {separator}\n </Box>{' '}\n </>\n )}\n {item}\n </React.Fragment>\n ))}\n </Typography>\n </Box>\n );\n};\n\nAppHeaderStatus.displayName = 'AppHeaderStatus';\n\n/** The name and, when the block opens onto something, the chevron beside it. */\nconst IdentityTitle: React.FC<\n Pick<AppHeaderIdentityProps, 'title' | 'size'> & {\n interactive: boolean;\n disclosed: boolean;\n dataTestId: string;\n }\n> = ({ title, size = 'md', interactive, disclosed, dataTestId }) => (\n <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.25, minWidth: 0, width: '100%' }}>\n <Typography\n component=\"span\"\n data-testid={`${dataTestId}-title`}\n sx={{ fontSize: TITLE_PX[size], fontWeight: 700, lineHeight: 1.2, ...ONE_LINE }}\n >\n {title}\n </Typography>\n {interactive && (\n <KeyboardArrowDown\n aria-hidden\n data-testid={`${dataTestId}-chevron`}\n sx={{\n flex: '0 0 auto',\n fontSize: 20,\n opacity: 0.7,\n transition: 'transform 150ms ease',\n transform: disclosed ? 'rotate(180deg)' : 'none',\n }}\n />\n )}\n </Box>\n);\n\n/**\n * The disclosure, wrapping the NAME ROW and nothing else (12-63).\n *\n * The padding is cancelled by an equal negative margin, so the hover and the\n * focus ring bleed outward while the title still starts on the same pixel as\n * the lines under it. A padded button with no offset moves one of the two.\n */\nconst discloseSx = {\n display: 'flex',\n alignItems: 'center',\n gap: 0.25,\n width: '100%',\n minWidth: 0,\n appearance: 'none',\n px: 0.5,\n mx: -0.5,\n py: 0.25,\n my: -0.25,\n border: 'none',\n borderRadius: 1.5,\n background: 'none',\n color: 'inherit',\n font: 'inherit',\n textAlign: 'left',\n cursor: 'pointer',\n // Guarded: on a touch screen an unguarded `:hover` sticks after the tap that\n // opened the panel, so the row stays highlighted behind it.\n '@media (hover: hover)': { '&:hover': { backgroundColor: 'action.hover' } },\n '&:focus-visible': { outline: '2px solid', outlineColor: 'primary.main', outlineOffset: 2 },\n} as const;\n\n/**\n * The text column: the name row, then the quieter lines under it.\n *\n * ## `subtitle` and `status` are OUTSIDE the disclosure, on purpose (12-63)\n *\n * Both are `ReactNode`, and `AppHeaderStatus.items` is a `ReactNode[]` — so a\n * segment of the state line is free to be a control, and consumers put controls\n * there: a mesa chip, a service-mode picker. While the whole block was one\n * `<button>` those were `<button>`s inside a `<button>`, which is invalid HTML\n * (the content model of `button` excludes interactive content) and, more\n * visibly, means one tap fires two handlers. An adopting storefront opened two\n * popups on every tap of either control because of it, and had already\n * hand-rolled this component at one tier to dodge the nesting — a consumer\n * working around the component is the report.\n *\n * The target stays large. The button is the full-width NAME ROW, which is the\n * thing a reader aims at — what it gives up is the mark beside it, and a mark\n * that opens the panel is not worth a state line that cannot hold a control.\n */\nconst IdentityText: React.FC<\n Pick<\n AppHeaderIdentityProps,\n 'title' | 'subtitle' | 'status' | 'size' | 'discloseLabel' | 'className'\n > & {\n onDisclose: (() => void) | undefined;\n disclosed: boolean;\n dataTestId: string;\n }\n> = ({\n title,\n subtitle,\n status,\n size = 'md',\n onDisclose,\n disclosed,\n discloseLabel,\n className,\n dataTestId,\n}) => {\n const name = (\n <IdentityTitle\n title={title}\n size={size}\n interactive={Boolean(onDisclose)}\n disclosed={disclosed}\n dataTestId={dataTestId}\n />\n );\n return (\n <Box sx={{ minWidth: 0, flex: 1, display: 'flex', flexDirection: 'column', gap: 0.25 }}>\n {onDisclose ? (\n <Box\n component=\"button\"\n type=\"button\"\n className={className}\n data-testid={dataTestId}\n onClick={onDisclose}\n aria-haspopup=\"dialog\"\n aria-expanded={disclosed}\n aria-label={discloseLabel ?? `Detalhes de ${title}`}\n sx={discloseSx}\n >\n {name}\n </Box>\n ) : (\n name\n )}\n {subtitle && (\n <Typography variant=\"body2\" color=\"text.secondary\" component=\"span\" sx={ONE_LINE}>\n {subtitle}\n </Typography>\n )}\n {status}\n </Box>\n );\n};\n\n/** The placeholder held while the identity is still resolving. */\nconst IdentitySkeleton: React.FC<{ size: SizeValue; dataTestId: string }> = ({\n size,\n dataTestId,\n}) => (\n <Box\n data-testid={`${dataTestId}-loading`}\n sx={{ display: 'flex', alignItems: 'center', gap: 1.25, minWidth: 0 }}\n >\n <Skeleton variant=\"rounded\" width={40} height={40} sx={{ flex: '0 0 auto' }} />\n <Box sx={{ minWidth: 0, flex: 1 }}>\n <Skeleton variant=\"text\" width={140} height={TITLE_PX[size] * 1.4} />\n <Skeleton variant=\"text\" width={180} height={16} />\n </Box>\n </Box>\n);\n\n/**\n * The BLOCK's row: the mark, then the text column beside it.\n *\n * Always a `Box` div now — since 12-63 the disclosure is the name row inside\n * the text column ({@link discloseSx}), not this element. `width: '100%'` and\n * `minWidth: 0` stay because the column below still has to ellipsise against\n * something; the argument about a `<button>` sizing to max-content moved to\n * `discloseSx`, which is where the button now is.\n */\nconst rowSx = {\n display: 'flex',\n alignItems: 'center',\n gap: 1.25,\n width: '100%',\n minWidth: 0,\n textAlign: 'left',\n} as const;\n\n/**\n * Mark + title + state line, and — when it can be opened — a name row that is\n * the button.\n *\n * The disclosure used to wrap EVERYTHING, on the argument that the chevron\n * alone is a 20px target next to a 40px mark that does nothing. The target is\n * still not the chevron: the button is the full-width NAME ROW, which is what a\n * reader aims at. What it no longer swallows is the mark beside it and the\n * quieter lines below — because those lines take `ReactNode`s, consumers put\n * CONTROLS in them, and a control inside a button is invalid HTML whose every\n * tap fires two handlers. See {@link IdentityText} (12-63).\n *\n * `className` and `dataTestId` stay on the same elements they always named: the\n * block when it is static, the disclosure when there is one.\n */\nexport const AppHeaderIdentity: React.FC<AppHeaderIdentityProps> = ({\n title,\n subtitle,\n status,\n mark,\n logoUrl,\n seedColor,\n onDisclose,\n disclosed = false,\n discloseLabel,\n loading = false,\n size = 'md',\n className,\n dataTestId = 'app-header-identity',\n}) => {\n if (loading) return <IdentitySkeleton size={size} dataTestId={dataTestId} />;\n\n return (\n <Box\n className={onDisclose ? undefined : className}\n data-testid={onDisclose ? undefined : dataTestId}\n sx={rowSx}\n >\n {mark ?? (\n <AppHeaderBrand\n name={title}\n logoUrl={logoUrl}\n seedColor={seedColor}\n size={size}\n dataTestId={`${dataTestId}-brand`}\n />\n )}\n <IdentityText\n title={title}\n subtitle={subtitle}\n status={status}\n size={size}\n onDisclose={onDisclose}\n disclosed={disclosed}\n discloseLabel={discloseLabel}\n className={className}\n dataTestId={dataTestId}\n />\n </Box>\n );\n};\n\nAppHeaderIdentity.displayName = 'AppHeaderIdentity';\n"],"mappings":"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","names":[]}
@@ -24,7 +24,7 @@ var TITLE = { fontSize: 16.8, fontWeight: 600, marginTop: -2, marginBottomUnits:
24
24
  var DESCRIPTION = { fontSize: 14.8, opacity: 0.9 };
25
25
  var ICON_SLOT = { size: 22, opacity: 0.9, marginRightUnits: 1.75, paddingTopUnits: 0.25, paddingBottom: 7 };
26
26
  var ACTION_SLOT = { paddingLeftUnits: 2, paddingTop: 4, marginRight: -8 };
27
- var CLOSE_BUTTON = { padding: 5, iconSize: 18, opacity: 0.7, hoverRotateDeg: 90, hoverScale: 1.1, washAlpha: 0.1 };
27
+ var CLOSE_BUTTON = { padding: 5, iconSize: 18, opacity: 0.7, washAlpha: 0.1 };
28
28
  var SEMANTIC_SURFACE = { tint: { light: 0.9, dark: 0.8 }, ink: 0.6, borderAlpha: 0.35 };
29
29
  var GLASS = { backgroundAlpha: 0.85, borderAlpha: 0.4, blur: 20, saturatePercent: 180 };
30
30
  var GRADIENT = {
@@ -32,16 +32,13 @@ var GRADIENT = {
32
32
  stopAlpha: 0.9,
33
33
  shimmerAlpha: 0.2,
34
34
  shimmerMs: 3e3,
35
- shimmerTravel: 1e3,
36
- hoverBrightness: 1.1,
37
- hoverLift: 2,
38
- hoverScale: 1.01
35
+ shimmerTravel: 1e3
39
36
  };
40
37
  var GLOW = { blur: 20, spread: 5, alpha: 0.3, brightness: 1.05 };
41
38
  var PULSE = { alpha: 0.2, ms: 2e3, spread: 10 };
42
- var HOVER = { lift: 3, scale: 1.01, shadowY: 8, shadowBlur: 20, shadowAlpha: 0.2, iconScale: 1.15, iconRotateDeg: 10 };
43
- var ACTIVE = { lift: 1, scale: 0.99, ms: 100 };
44
- var FOCUS = { ringWidth: 3, ringAlpha: 0.5, offset: 3, haloSpread: 6, haloAlpha: 0.1, ms: 200 };
39
+ var HOVER = { tintAlpha: 0.06 };
40
+ var ACTIVE = { tintAlpha: 0.2, ms: 250 };
41
+ var FOCUS = { ringWidth: 2, ringAlpha: 1, offset: 2 };
45
42
  var NEUTRAL_GREY = { main: 500, light: 300, dark: 700 };
46
43
  var seconds = (ms) => `${ms / 1e3}s`;
47
44
  function alertPalette(theme, key) {
@@ -133,4 +130,4 @@ export {
133
130
  gradientSurface,
134
131
  alertSurface
135
132
  };
136
- //# sourceMappingURL=chunk-B2Y3BABA.js.map
133
+ //# sourceMappingURL=chunk-TXKZEKJN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/data-display/Alert/Alert.metrics.ts"],"sourcesContent":["import type { AlertVariant } from './Alert.base';\nimport { alpha, darken, lighten } from '../../../tokens/color';\nimport { contrastText, type UiGreyStep, type UiTheme } from '../../../tokens/theme';\n\n/**\n * THE NUMBERS BOTH `Alert` RENDERERS DRAW WITH.\n *\n * `Alert.styles.tsx` / `Alert.tsx` (MUI) and `Alert.native.tsx` (React\n * Native) read this one table. Spacing is in SPACING UNITS where the web\n * writes `theme.spacing(n)`; durations are ms (the web turns them into `s`\n * through {@link seconds} so its emitted CSS is byte-for-byte what it was);\n * everything else is px, a ratio or an alpha. The hover, active and focus\n * numbers are here too although only the DOM can honour them — a number the\n * web draws with belongs in the table whether or not native can reach it.\n */\n\n/** The root: 12px corners, 14px 16px of padding, one 300ms curve for everything. */\nexport const ALERT_RADIUS_UNITS = 1.5;\nexport const ALERT_PADDING_UNITS = { vertical: 1.75, horizontal: 2 } as const;\nexport const ALERT_TRANSITION_MS = 300;\nexport const ALERT_EASING = 'cubic-bezier(0.4, 0, 0.2, 1)';\n\n/** Mount: the web's `fadeInScale` keyframes and the icon's `iconRotate`. */\nexport const FADE_IN = { ms: 300, scale: 0.95, lift: 10 } as const;\nexport const ICON_SPIN = { ms: 600, from: 0.8, mid: 1.1 } as const;\n/** Dismiss: MUI `Collapse` runs 300ms; `onClose` fires 200ms in. */\nexport const COLLAPSE_MS = 300;\nexport const CLOSE_DELAY_MS = 200;\n\n/** The message column: 8px between title, description and children; 0.95rem body on 1.5. */\nexport const MESSAGE_GAP_UNITS = 1;\nexport const MESSAGE_FONT_SIZE = 15.2;\nexport const MESSAGE_LINE_HEIGHT = 1.5;\n/** A control inside the message gets an edge: 4px above, a hairline at 0.45 of the hue. */\nexport const MESSAGE_BUTTON = { marginTopUnits: 0.5, borderAlpha: 0.45 } as const;\n\n/** The title: 1.05rem at 600; MUI's `AlertTitle` lifts it 2px; 4px below it when a description follows. */\nexport const TITLE = { fontSize: 16.8, fontWeight: 600, marginTop: -2, marginBottomUnits: 0.5 } as const;\n/** The description: 0.925rem, slightly faded. */\nexport const DESCRIPTION = { fontSize: 14.8, opacity: 0.9 } as const;\n\n/** The icon slot: MUI's 22px glyph at 0.9, 14px from the words, 2px down (MUI's 7px stays below). */\nexport const ICON_SLOT = { size: 22, opacity: 0.9, marginRightUnits: 1.75, paddingTopUnits: 0.25, paddingBottom: 7 } as const;\n/** The action slot: MUI's `4px 0 0 16px` with the 16px ours, pushed right, 8px into the padding. */\nexport const ACTION_SLOT = { paddingLeftUnits: 2, paddingTop: 4, marginRight: -8 } as const;\n/** The close button: MUI's small `IconButton` (5px padding, 18px glyph) at 0.7. */\nexport const CLOSE_BUTTON = { padding: 5, iconSize: 18, opacity: 0.7, washAlpha: 0.1 } as const;\n\n/**\n * A semantic Alert's surface and ink: an OPAQUE tint of the hue with a dark\n * ink of the same hue — MUI's own `standard` recipe, at its ratios. See\n * `semanticSurface` in `Alert.styles.tsx` for why not `alpha(main, 0.1)`.\n */\nexport const SEMANTIC_SURFACE = { tint: { light: 0.9, dark: 0.8 }, ink: 0.6, borderAlpha: 0.35 } as const;\n/** `glass`: 85% paper under a 20px blur, a 0.4 divider hairline. */\nexport const GLASS = { backgroundAlpha: 0.85, borderAlpha: 0.4, blur: 20, saturatePercent: 180 } as const;\n/**\n * `gradient`: light→dark at 0.9 along 135°, a 0.2 white shimmer sweeping 1000px every 3s.\n *\n * It carries no hover of its own, and that is load-bearing rather than an\n * omission. `alertVariantStyles` is spread AFTER the root's `'&:hover'` in the\n * same object literal, so a second `'&:hover'` key does not merge with it — it\n * REPLACES it. While this variant had one, `gradient` was the only alert that\n * brightened on hover while every other variant darkened, and the only one to\n * lose the shimmer reveal the root's hover performs.\n */\nexport const GRADIENT = {\n angleDeg: 135,\n stopAlpha: 0.9,\n shimmerAlpha: 0.2,\n shimmerMs: 3000,\n shimmerTravel: 1000,\n} as const;\n\n/** Emphasis: the glow's shadow and brightness, the pulse's wash. */\nexport const GLOW = { blur: 20, spread: 5, alpha: 0.3, brightness: 1.05 } as const;\nexport const PULSE = { alpha: 0.2, ms: 2000, spread: 10 } as const;\n\n/**\n * Pointer states — DOM only, kept so the web derives them.\n *\n * HOVER and ACTIVE carry NO geometry, and that is the point rather than an\n * omission. They used to lift the card 3px, scale it 1.01, grow a 20px shadow\n * and spin the icon 10deg at 1.15 — four things moving at once on a surface\n * that is not a control, which made a page of alerts twitch under the pointer.\n *\n * Both are a TINT now: a flat inset layer over the surface. Two mechanisms\n * came before it and each failed on a case the next had to fix. Dropping the\n * element's `opacity` composites toward the PAGE, so it inverted the hover on\n * a light tint (L* 95.0 idle, 89.1 hovered, 91.1 pressed) and vanished on a\n * dark one. A `filter: brightness()` fixed the direction but is MULTIPLICATIVE,\n * so it moved a near-black `glass` by four counts (ΔE76 1.71, under the JND)\n * and Banner's 10%-alpha wash by ΔE76 0.44. A layer is additive: it reads the\n * same on an opaque tint, a translucent wash and a gradient, and because it\n * paints above the background and below the content the ink keeps its own\n * contrast.\n */\nexport const HOVER = { tintAlpha: 0.06 } as const;\n\n/**\n * The press: a 20% tint LAYER, eased back fast.\n *\n * The layer is the point. The first attempt dropped the whole element's\n * opacity, which composites toward the PAGE and so behaves differently on\n * every surface it is asked about. Measured: over a light page it made a light\n * tint LIGHTER (L* 95.0 idle, 89.1 hovered, 91.1 pressed), so pushing the\n * surface looked like the pointer leaving; over a dark page it landed back on\n * the idle surface (ΔE76 0.82 on `glass`); on `gradient` it washed white body\n * copy to 2.31:1, under AA; and it diluted the ink during a text-selection\n * drag. A `brightness()` step instead of it fixed the direction but not the\n * reach — a multiplier does nothing to a near-black `glass` in dark mode\n * (ΔE76 1.71) and nothing to Banner's 10%-alpha wash (ΔE76 0.44), because it\n * scales only what the element already paints.\n *\n * An inset shadow at 100vmax is a flat tint over the whole surface: ADDITIVE,\n * so it reads the same on an opaque tint, a translucent wash and a gradient;\n * painted above the background and below the content, so the ink keeps its\n * own contrast; and animatable, which `background-image` is not.\n *\n * `ms` is 250 and not the 1000 first asked for, and the reason is that ONE\n * property carries both states. CSS runs a transition from the DESTINATION\n * state's declaration, and entering hover and releasing a press share that\n * destination — so they cannot have different durations without a second\n * animatable property, and both free pseudo-elements are already spent on the\n * `gradient` shimmer and the `pulse` wash. At 1000ms the hover inherited the\n * second: crossing an alert in 300ms peaked at a ≈ 0.024, under the JND, and\n * for ~360ms the alert you had just LEFT was darker than the one under the\n * pointer. A fast press-return is the smaller loss, and it is the call the\n * product owner made when shown both.\n */\nexport const ACTIVE = { tintAlpha: 0.2, ms: 250 } as const;\n\nexport const FOCUS = { ringWidth: 2, ringAlpha: 1, offset: 2 } as const;\n\n/** `neutral` has no palette slot; the web has always drawn it from three greys. */\nexport const NEUTRAL_GREY: Record<'main' | 'light' | 'dark', UiGreyStep> = { main: 500, light: 300, dark: 700 };\n\n/** A duration as the CSS the web has always emitted: `2000` → `2s`, `300` → `0.3s`. */\nexport const seconds = (ms: number): string => `${ms / 1000}s`;\n\n/** The palette shape the web's `getColorFromTheme` returns — a main plus optional shades. */\nexport interface AlertPalette {\n main: string;\n light?: string;\n dark?: string;\n}\n\n/**\n * The renderer-free twin of the web's `getColorFromTheme`: a variant or\n * colour name to its palette, `neutral` from the greys, anything unknown\n * (`glass`, `gradient`) to `info`.\n */\nexport function alertPalette(theme: UiTheme, key: string | undefined): AlertPalette {\n switch (key) {\n case 'info':\n case 'success':\n case 'warning':\n case 'danger':\n case 'primary':\n case 'secondary':\n return theme.palette[key];\n case 'neutral':\n return {\n main: theme.palette.grey[NEUTRAL_GREY.main],\n light: theme.palette.grey[NEUTRAL_GREY.light],\n dark: theme.palette.grey[NEUTRAL_GREY.dark],\n };\n default:\n return theme.palette.info;\n }\n}\n\n/** What a variant paints: its fill, its ink, its hairline if any, and the colour of its glyph. */\nexport interface AlertSurface {\n backgroundColor: string;\n color: string;\n borderColor?: string;\n iconColor: string;\n}\n\nexport const SEMANTIC_VARIANTS: ReadonlySet<AlertVariant> = new Set(['info', 'success', 'warning', 'danger']);\n\n/** The opaque tint and same-hue ink, inverted for dark mode; the hairline keeps its alpha. */\nexport function semanticSurface(theme: UiTheme, palette: AlertPalette): AlertSurface {\n const dark = theme.mode === 'dark';\n return {\n backgroundColor: dark\n ? darken(palette.main, SEMANTIC_SURFACE.tint.dark)\n : lighten(palette.main, SEMANTIC_SURFACE.tint.light),\n color: dark ? lighten(palette.main, SEMANTIC_SURFACE.ink) : darken(palette.main, SEMANTIC_SURFACE.ink),\n borderColor: alpha(palette.main, SEMANTIC_SURFACE.borderAlpha),\n iconColor: palette.main,\n };\n}\n\nexport function glassSurface(theme: UiTheme): AlertSurface {\n return {\n backgroundColor: alpha(theme.palette.background.paper, GLASS.backgroundAlpha),\n color: theme.palette.text.primary,\n borderColor: alpha(theme.palette.divider, GLASS.borderAlpha),\n iconColor: theme.palette.primary.main,\n };\n}\n\n/** The gradient's two stops; a renderer with no gradient fill paints `from`. */\nexport function gradientStops(palette: AlertPalette): { from: string; to: string } {\n return {\n from: alpha(palette.light || palette.main, GRADIENT.stopAlpha),\n to: alpha(palette.dark || palette.main, GRADIENT.stopAlpha),\n };\n}\n\nexport function gradientSurface(palette: AlertPalette): AlertSurface {\n const ink = contrastText(palette.main);\n return { backgroundColor: gradientStops(palette).from, color: ink, iconColor: ink };\n}\n\n/** The surface for a variant, `glass` and `gradient` included. */\nexport function alertSurface(theme: UiTheme, variant: AlertVariant, palette: AlertPalette): AlertSurface {\n if (variant === 'glass') return glassSurface(theme);\n if (variant === 'gradient') return gradientSurface(palette);\n return semanticSurface(theme, palette);\n}\n"],"mappings":"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","names":[]}
@@ -5,7 +5,12 @@ import {
5
5
  fadeInScale,
6
6
  getColorFromTheme,
7
7
  getVariantIcon
8
- } from "./chunk-E27Y7FF2.js";
8
+ } from "./chunk-57Z2ND3N.js";
9
+ import {
10
+ defaultAriaLive,
11
+ resolveAlertProps,
12
+ testIdFor
13
+ } from "./chunk-N3QUEBNS.js";
9
14
  import {
10
15
  ACTIVE,
11
16
  ALERT_EASING,
@@ -17,15 +22,11 @@ import {
17
22
  DESCRIPTION,
18
23
  FADE_IN,
19
24
  FOCUS,
25
+ GLOW,
20
26
  HOVER,
21
27
  TITLE,
22
28
  seconds
23
- } from "./chunk-B2Y3BABA.js";
24
- import {
25
- defaultAriaLive,
26
- resolveAlertProps,
27
- testIdFor
28
- } from "./chunk-N3QUEBNS.js";
29
+ } from "./chunk-TXKZEKJN.js";
29
30
  import {
30
31
  resolveTestId,
31
32
  withoutTestIdProps
@@ -45,41 +46,76 @@ import { alpha, styled } from "@mui/material/styles/index.js";
45
46
  import React from "react";
46
47
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
47
48
  var transition = `all ${seconds(ALERT_TRANSITION_MS)} ${ALERT_EASING}`;
49
+ function pointerShadow(theme, colorPalette, glow, variant) {
50
+ const ink = variant === "gradient" || theme.palette.mode === "light" ? "#000000" : "#ffffff";
51
+ const glowShadow = glow ? `0 0 ${GLOW.blur}px ${GLOW.spread}px ${alpha(colorPalette.main, GLOW.alpha)}` : null;
52
+ return (alpha_) => [`inset 0 0 0 100vmax ${alpha(ink, alpha_)}`, glowShadow].filter(Boolean).join(", ");
53
+ }
48
54
  var StyledAlert = styled(MuiAlert, {
49
55
  shouldForwardProp: (prop) => !["customVariant", "customColor", "glow", "pulse", "animate"].includes(prop)
50
56
  })(({ theme, customVariant, customColor, glow, pulse, animate }) => {
51
57
  const colorPalette = getColorFromTheme(theme, customColor || customVariant || "info");
58
+ const withTint = pointerShadow(theme, colorPalette, Boolean(glow), customVariant);
52
59
  return {
53
60
  borderRadius: theme.spacing(ALERT_RADIUS_UNITS),
54
- transition,
61
+ transition: `${transition}, box-shadow ${seconds(ACTIVE.ms)} ${ALERT_EASING}`,
55
62
  position: "relative",
56
63
  overflow: "hidden",
57
64
  animation: animate ? `${fadeInScale} ${seconds(FADE_IN.ms)} ease-out` : "none",
58
65
  willChange: "transform, opacity",
59
66
  ...alertLayoutStyles(theme, colorPalette, animate),
60
- // Hover effects
67
+ // Hover: one tint step, and nothing that moves. An alert is a surface
68
+ // rather than a control, so the pointer only needs telling it is here. The
69
+ // icon keeps its own place for the same reason — it is the variant's
70
+ // meaning, not an affordance to animate.
61
71
  "&:hover": {
62
- transform: `translateY(-${HOVER.lift}px) scale(${HOVER.scale})`,
63
- boxShadow: `0 ${HOVER.shadowY}px ${HOVER.shadowBlur}px ${alpha(colorPalette.main, HOVER.shadowAlpha)}`,
64
- transition,
65
- ".MuiAlert-icon": {
66
- transform: `scale(${HOVER.iconScale}) rotate(${HOVER.iconRotateDeg}deg)`
67
- },
72
+ boxShadow: withTint(HOVER.tintAlpha),
68
73
  "&::before": {
69
74
  opacity: 1
70
75
  }
71
76
  },
72
- // Active state
73
- "&:active": {
74
- transform: `translateY(-${ACTIVE.lift}px) scale(${ACTIVE.scale})`,
75
- transition: `transform ${seconds(ACTIVE.ms)} ease`
77
+ // Press: a 20% tint, landing at once and easing back. Still no geometry.
78
+ //
79
+ // `transition: none` here is what makes the dip INSTANT: a click's
80
+ // `:active` lasts about as long as the finger is down, so easing into it
81
+ // would be cut off in its first tenth and read as a flicker. The ease is on
82
+ // the way back, governed by the root.
83
+ //
84
+ // Neither `:hover` nor `:focus-visible` may declare `transition`, and that
85
+ // is not a style preference. `:active` under a mouse ALWAYS implies
86
+ // `:hover`, so a bare shorthand in the hover block outranks the root's
87
+ // (0,2,0 beats 0,1,0) and silently resets the timing this state rests on.
88
+ //
89
+ // The `:not(:has(…))` names CONTROLS, not any descendant. `:active`
90
+ // matches an ancestor of whatever is pressed, so dismissing an alert
91
+ // dimmed the alert being dismissed and pressing a call to action dimmed
92
+ // the message explaining it. Excluding `:has(:active)` wholesale does not
93
+ // work and was the first attempt: pressing the TEXT makes that text
94
+ // `:active` too, so the guard matched on every press and the state never
95
+ // fired at all.
96
+ '&:active:not(:has(button:active, a:active, [role="button"]:active))': {
97
+ boxShadow: withTint(ACTIVE.tintAlpha),
98
+ transition: "none"
76
99
  },
77
- // Focus styles for accessibility
78
- "&:focus-within": {
100
+ // Focus: `:focus-visible`, not `:focus-within`.
101
+ //
102
+ // The alert carries `tabIndex={0}`, so a MOUSE CLICK focused it and painted
103
+ // the ring — a keyboard affordance answering a pointer, which is what made
104
+ // clicking one look like an error state. `:focus-visible` is the browser's
105
+ // own answer to "did this focus come from the keyboard": the ring stays for
106
+ // whoever tabs here and never fires on a click.
107
+ //
108
+ // One ring, not three. It was a 3px outline at 3px offset UNDER a 6px halo,
109
+ // and stacking two rings on one edge is what read as unfinished rather than
110
+ // as emphasis.
111
+ // It declares no `transition`: `outline-style` is discrete, so the ring
112
+ // arrives at full width with only its COLOUR easing — from `currentcolor`,
113
+ // the near-black alert ink, to the blue. That is a visible dark flash on
114
+ // every keyboard focus, and it would also outrank the root's opacity
115
+ // transition the way the hover block did.
116
+ "&:focus-visible": {
79
117
  outline: `${FOCUS.ringWidth}px solid ${alpha(colorPalette.main, FOCUS.ringAlpha)}`,
80
- outlineOffset: `${FOCUS.offset}px`,
81
- boxShadow: `0 0 0 ${FOCUS.haloSpread}px ${alpha(colorPalette.main, FOCUS.haloAlpha)}`,
82
- transition: `all ${seconds(FOCUS.ms)} ease`
118
+ outlineOffset: `${FOCUS.offset}px`
83
119
  },
84
120
  ...alertVariantStyles(theme, customVariant, colorPalette),
85
121
  ...alertEmphasisStyles(colorPalette, Boolean(glow), Boolean(pulse))
@@ -139,14 +175,39 @@ var AlertCloseButton = ({
139
175
  sx: (theme) => ({
140
176
  transition,
141
177
  opacity: CLOSE_BUTTON.opacity,
178
+ // Opacity and a wash only: the button used to spin 90deg and grow 1.1,
179
+ // which drew the eye to the dismiss rather than to the message.
142
180
  "&:hover": {
143
- transform: `rotate(${CLOSE_BUTTON.hoverRotateDeg}deg) scale(${CLOSE_BUTTON.hoverScale})`,
144
181
  opacity: 1,
145
182
  backgroundColor: alpha(theme.palette.action.hover, CLOSE_BUTTON.washAlpha)
146
183
  },
147
- "&:focus": {
184
+ // A REAL ring, on the keyboard-only selector.
185
+ //
186
+ // This used to be `&:focus` setting `outline: 'none'` over a
187
+ // `rgba(0,0,0,0.1)` wash — 1.25:1 against the info surface, and MUI's
188
+ // ButtonBase already sets `outline: 0`, so there was no UA fallback
189
+ // either. It went unnoticed while the ROOT still matched `:focus-within`
190
+ // and drew a ring around the whole alert; moving the root to
191
+ // `:focus-visible` took that away and left this button with no visible
192
+ // focus at all. `Banner`'s dismiss already draws its own outline, so
193
+ // this is also what keeps the two surfaces telling a keyboard user the
194
+ // same thing.
195
+ "&:focus-visible": {
148
196
  opacity: 1,
149
- outline: "none",
197
+ // `currentColor` is the alert's own ink, inherited through
198
+ // `color="inherit"`, so the ring is drawn in whatever colour was
199
+ // already chosen to be READ on this surface. Measured 11.2:1 on the
200
+ // semantic variants and 5.7:1 on `gradient`.
201
+ //
202
+ // That is a reasoned choice, not a test-backed one, and the difference
203
+ // matters: `alert-contrast.test.tsx` covers the ink of the four
204
+ // semantic variants only — its `glass` case asserts the pane's alpha
205
+ // rather than its ink, and `gradient` it does not render at all. A
206
+ // translucent surface's contrast depends on what sits behind it, which
207
+ // is why that test stops where it does and why this comment does not
208
+ // borrow a guarantee it never made.
209
+ outline: `${FOCUS.ringWidth}px solid currentColor`,
210
+ outlineOffset: `${FOCUS.offset}px`,
150
211
  backgroundColor: alpha(theme.palette.action.focus, CLOSE_BUTTON.washAlpha)
151
212
  }
152
213
  }),
@@ -175,7 +236,7 @@ var Alert = React.forwardRef((alertProps, ref) => {
175
236
  ...others
176
237
  } = resolveAlertProps(alertProps);
177
238
  const dataTestId = resolveTestId(others, "alert");
178
- const props = withoutTestIdProps(others);
239
+ const props = withoutAlertOnlyProps(withoutTestIdProps(others));
179
240
  const ariaLive = alertProps["aria-live"] ?? defaultAriaLive(variant);
180
241
  const [open, setOpen] = React.useState(true);
181
242
  const [isClosing, setIsClosing] = React.useState(false);
@@ -211,9 +272,16 @@ var Alert = React.forwardRef((alertProps, ref) => {
211
272
  }
212
273
  ) });
213
274
  });
275
+ var ALERT_ONLY_PROPS = ["closable", "closeLabel"];
276
+ function withoutAlertOnlyProps(rest) {
277
+ const html = { ...rest };
278
+ for (const key of ALERT_ONLY_PROPS) delete html[key];
279
+ return html;
280
+ }
214
281
  Alert.displayName = "Alert";
215
282
 
216
283
  export {
217
- Alert
284
+ Alert,
285
+ ALERT_ONLY_PROPS
218
286
  };
219
- //# sourceMappingURL=chunk-QFVFHIHE.js.map
287
+ //# sourceMappingURL=chunk-WNMYWQ7D.js.map