tempest-react-sdk 0.74.0 → 0.75.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/br/BrazilMap.module.cjs.map +1 -1
- package/dist/br/BrazilMap.module.js.map +1 -1
- package/dist/br/MunicipalitySearch.cjs +1 -1
- package/dist/br/MunicipalitySearch.cjs.map +1 -1
- package/dist/br/MunicipalitySearch.js +66 -48
- package/dist/br/MunicipalitySearch.js.map +1 -1
- package/dist/br/MunicipalitySearch.module.cjs +1 -1
- package/dist/br/MunicipalitySearch.module.cjs.map +1 -1
- package/dist/br/MunicipalitySearch.module.js +4 -3
- package/dist/br/MunicipalitySearch.module.js.map +1 -1
- package/dist/br.d.ts +12 -0
- package/dist/components/AIChat/AIChat.module.cjs.map +1 -1
- package/dist/components/AIChat/AIChat.module.js.map +1 -1
- package/dist/components/AppBar/AppBar.cjs +1 -1
- package/dist/components/AppBar/AppBar.cjs.map +1 -1
- package/dist/components/AppBar/AppBar.js +1 -0
- package/dist/components/AppBar/AppBar.js.map +1 -1
- package/dist/components/AppBar/AppBar.module.cjs +1 -1
- package/dist/components/AppBar/AppBar.module.cjs.map +1 -1
- package/dist/components/AppBar/AppBar.module.js +12 -11
- package/dist/components/AppBar/AppBar.module.js.map +1 -1
- package/dist/components/Button/Button.module.cjs.map +1 -1
- package/dist/components/Button/Button.module.js.map +1 -1
- package/dist/components/Button/ButtonContent.cjs.map +1 -1
- package/dist/components/Button/ButtonContent.js.map +1 -1
- package/dist/components/Carousel/Carousel.module.cjs.map +1 -1
- package/dist/components/Carousel/Carousel.module.js.map +1 -1
- package/dist/components/Chat/Chat.module.cjs.map +1 -1
- package/dist/components/Chat/Chat.module.js.map +1 -1
- package/dist/components/Combobox/Combobox.cjs +1 -1
- package/dist/components/Combobox/Combobox.cjs.map +1 -1
- package/dist/components/Combobox/Combobox.js +18 -23
- package/dist/components/Combobox/Combobox.js.map +1 -1
- package/dist/components/Combobox/use-listbox-dismiss.cjs +2 -0
- package/dist/components/Combobox/use-listbox-dismiss.cjs.map +1 -0
- package/dist/components/Combobox/use-listbox-dismiss.js +34 -0
- package/dist/components/Combobox/use-listbox-dismiss.js.map +1 -0
- package/dist/components/Dropzone/Dropzone.module.cjs.map +1 -1
- package/dist/components/Dropzone/Dropzone.module.js.map +1 -1
- package/dist/components/FilterBar/FilterBar.module.cjs.map +1 -1
- package/dist/components/FilterBar/FilterBar.module.js.map +1 -1
- package/dist/components/FloatingActionButton/FloatingActionButton.module.cjs.map +1 -1
- package/dist/components/FloatingActionButton/FloatingActionButton.module.js.map +1 -1
- package/dist/components/ImageCropper/ImageCropper.module.cjs.map +1 -1
- package/dist/components/ImageCropper/ImageCropper.module.js.map +1 -1
- package/dist/components/ListTile/ListTile.module.cjs.map +1 -1
- package/dist/components/ListTile/ListTile.module.js.map +1 -1
- package/dist/components/Markdown/Markdown.module.cjs.map +1 -1
- package/dist/components/Markdown/Markdown.module.js.map +1 -1
- package/dist/components/Menubar/Menubar.module.cjs.map +1 -1
- package/dist/components/Menubar/Menubar.module.js.map +1 -1
- package/dist/components/MultiSelect/MultiSelect.cjs +1 -1
- package/dist/components/MultiSelect/MultiSelect.cjs.map +1 -1
- package/dist/components/MultiSelect/MultiSelect.js +55 -54
- package/dist/components/MultiSelect/MultiSelect.js.map +1 -1
- package/dist/components/Navbar/Navbar.cjs +1 -1
- package/dist/components/Navbar/Navbar.cjs.map +1 -1
- package/dist/components/Navbar/Navbar.js +1 -0
- package/dist/components/Navbar/Navbar.js.map +1 -1
- package/dist/components/Navbar/Navbar.module.cjs +1 -1
- package/dist/components/Navbar/Navbar.module.cjs.map +1 -1
- package/dist/components/Navbar/Navbar.module.js +9 -8
- package/dist/components/Navbar/Navbar.module.js.map +1 -1
- package/dist/components/NavigationMenu/NavigationMenu.module.cjs.map +1 -1
- package/dist/components/NavigationMenu/NavigationMenu.module.js.map +1 -1
- package/dist/components/NavigationRail/NavigationRail.module.cjs.map +1 -1
- package/dist/components/NavigationRail/NavigationRail.module.js.map +1 -1
- package/dist/components/NotificationCenter/NotificationCenter.module.cjs.map +1 -1
- package/dist/components/NotificationCenter/NotificationCenter.module.js.map +1 -1
- package/dist/components/PasswordInput/PasswordInput.module.cjs.map +1 -1
- package/dist/components/PasswordInput/PasswordInput.module.js.map +1 -1
- package/dist/components/PinInput/PinInput.module.cjs.map +1 -1
- package/dist/components/PinInput/PinInput.module.js.map +1 -1
- package/dist/components/SectionHeader/SectionHeader.cjs +1 -1
- package/dist/components/SectionHeader/SectionHeader.cjs.map +1 -1
- package/dist/components/SectionHeader/SectionHeader.js +33 -25
- package/dist/components/SectionHeader/SectionHeader.js.map +1 -1
- package/dist/components/Tour/Tour.module.cjs.map +1 -1
- package/dist/components/Tour/Tour.module.js.map +1 -1
- package/dist/components/Transfer/Transfer.module.cjs.map +1 -1
- package/dist/components/Transfer/Transfer.module.js.map +1 -1
- package/dist/components/VirtualTable/VirtualTable.module.cjs.map +1 -1
- package/dist/components/VirtualTable/VirtualTable.module.js.map +1 -1
- package/dist/geo/TrajectoryMap.module.cjs.map +1 -1
- package/dist/geo/TrajectoryMap.module.js.map +1 -1
- package/dist/icons/IconPicker.module.cjs.map +1 -1
- package/dist/icons/IconPicker.module.js.map +1 -1
- package/dist/styles/actions.css +2 -2
- package/dist/styles/br.css +3 -3
- package/dist/styles/chat.css +5 -5
- package/dist/styles/component/AIChat.css +3 -3
- package/dist/styles/component/AppBar.css +1 -0
- package/dist/styles/component/BrazilMap.css +2 -2
- package/dist/styles/component/Button.css +1 -1
- package/dist/styles/component/Carousel.css +3 -3
- package/dist/styles/component/Chat.css +2 -2
- package/dist/styles/component/Dropzone.css +2 -1
- package/dist/styles/component/FilterBar.css +2 -2
- package/dist/styles/component/FloatingActionButton.css +1 -1
- package/dist/styles/component/IconPicker.css +1 -1
- package/dist/styles/component/ImageCropper.css +2 -2
- package/dist/styles/component/ListTile.css +1 -1
- package/dist/styles/component/Markdown.css +1 -1
- package/dist/styles/component/Menubar.css +1 -1
- package/dist/styles/component/MunicipalitySearch.css +1 -1
- package/dist/styles/component/Navbar.css +1 -0
- package/dist/styles/component/NavigationMenu.css +1 -1
- package/dist/styles/component/NavigationRail.css +1 -1
- package/dist/styles/component/NotificationCenter.css +3 -3
- package/dist/styles/component/PasswordInput.css +1 -1
- package/dist/styles/component/PinInput.css +1 -1
- package/dist/styles/component/Tour.css +2 -2
- package/dist/styles/component/TrajectoryMap.css +1 -1
- package/dist/styles/component/Transfer.css +2 -2
- package/dist/styles/component/VirtualTable.css +2 -2
- package/dist/styles/data.css +10 -10
- package/dist/styles/feedback.css +3 -3
- package/dist/styles/forms.css +6 -5
- package/dist/styles/geo.css +1 -1
- package/dist/styles/icons.css +1 -1
- package/dist/styles/inverse.css +225 -0
- package/dist/styles/navigation.css +8 -6
- package/dist/styles.css +1 -1
- package/dist/tempest-react-sdk.d.ts +130 -17
- package/dist/theme/create-theme.cjs +1 -1
- package/dist/theme/create-theme.cjs.map +1 -1
- package/dist/theme/create-theme.js +6 -1
- package/dist/theme/create-theme.js.map +1 -1
- package/dist/theme/inverse-render.cjs +1 -1
- package/dist/theme/inverse-render.cjs.map +1 -1
- package/dist/theme/inverse-render.js +1 -1
- package/dist/theme/inverse-render.js.map +1 -1
- package/dist/theme/inverse-restore.cjs +5 -0
- package/dist/theme/inverse-restore.cjs.map +1 -0
- package/dist/theme/inverse-restore.js +30 -0
- package/dist/theme/inverse-restore.js.map +1 -0
- package/dist/theme/inverse-surface.cjs +1 -1
- package/dist/theme/inverse-surface.cjs.map +1 -1
- package/dist/theme/inverse-surface.js +69 -72
- package/dist/theme/inverse-surface.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppBar.cjs","names":[],"sources":["../../../src/components/AppBar/AppBar.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — a top bar is a slot layout: leading (leading,\n * showBack, onBack, backLabel, backIcon, brand), centre (title, centered) and\n * trailing (actions), plus the chrome it sits in (sticky, tone, bordered, safeArea).\n * Every one of those is a real decision an app makes per screen, and the alternative\n * to accepting them is the app rebuilding the bar around the parts it wanted to\n * change.\n */\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { ArrowLeft } from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport { useStickyBodyWarning } from \"./use-sticky-body-warning\";\nimport styles from \"./AppBar.module.css\";\n\nexport type AppBarTone = \"surface\" | \"primary\" | \"transparent\";\n\nexport interface AppBarProps extends Omit<HTMLAttributes<HTMLElement>, \"title\"> {\n /** Page title — string or any node. Rendered as the bar's `<h1>`. */\n title?: ReactNode;\n /**\n * Replace the whole left slot. When set, the auto back button and `brand`\n * are ignored — you own the leading content.\n */\n leading?: ReactNode;\n /** Show a back button in the left slot. Ignored when `leading` is set. */\n showBack?: boolean;\n /**\n * Back-button handler. Defaults to `window.history.back()`. With a router,\n * pass `onBack={() => navigate(-1)}`.\n */\n onBack?: () => void;\n /** Accessible label for the back button. Default `\"Go back\"`. */\n backLabel?: string;\n /** Custom back icon. Default a left arrow. */\n backIcon?: ReactNode;\n /** Brand / logo node shown in the left slot (after the back button). */\n brand?: ReactNode;\n /** Right slot — action buttons / menu. One node or many. */\n actions?: ReactNode;\n /** Center the title (three-column grid). Default `false` (left-aligned). */\n centered?: boolean;\n /**\n * Stick to the top of the scroll container. Default `true`.\n *\n * The page has to leave the body out of the scrolling: `body { overflow-x:\n * hidden }` makes the body a scroll container and the bar scrolls away with\n * the content. Use `overflow-x: clip` on `html` and `body` instead — in\n * development the bar says so in the console when it detects it.\n */\n sticky?: boolean;\n
|
|
1
|
+
{"version":3,"file":"AppBar.cjs","names":[],"sources":["../../../src/components/AppBar/AppBar.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — a top bar is a slot layout: leading (leading,\n * showBack, onBack, backLabel, backIcon, brand), centre (title, centered) and\n * trailing (actions), plus the chrome it sits in (sticky, tone, bordered, safeArea).\n * Every one of those is a real decision an app makes per screen, and the alternative\n * to accepting them is the app rebuilding the bar around the parts it wanted to\n * change.\n */\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { ArrowLeft } from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport { useStickyBodyWarning } from \"./use-sticky-body-warning\";\nimport styles from \"./AppBar.module.css\";\n\n/**\n * Visual tone of an {@link AppBar}.\n *\n * - `\"surface\"` — the page's surface and text.\n * - `\"primary\"` — the brand fill (`--tempest-primary`) with\n * `--tempest-primary-foreground` ink. Only the bar's own text and back button\n * follow the fill: components in `actions` keep the page's tokens. Measured in\n * Chromium over the SDK's `#0066ff`, a primary `Button` inside it has the same\n * color as the bar (1:1) and its focus ring reads 1.41:1.\n * - `\"inverse\"` — the inverse surface: every color token inverted inside the bar,\n * so the components in it are measured against the fill. See\n * {@link AppBarProps.tone}.\n * - `\"transparent\"` — no background; the text inherits.\n */\nexport type AppBarTone = \"surface\" | \"primary\" | \"inverse\" | \"transparent\";\n\nexport interface AppBarProps extends Omit<HTMLAttributes<HTMLElement>, \"title\"> {\n /** Page title — string or any node. Rendered as the bar's `<h1>`. */\n title?: ReactNode;\n /**\n * Replace the whole left slot. When set, the auto back button and `brand`\n * are ignored — you own the leading content.\n */\n leading?: ReactNode;\n /** Show a back button in the left slot. Ignored when `leading` is set. */\n showBack?: boolean;\n /**\n * Back-button handler. Defaults to `window.history.back()`. With a router,\n * pass `onBack={() => navigate(-1)}`.\n */\n onBack?: () => void;\n /** Accessible label for the back button. Default `\"Go back\"`. */\n backLabel?: string;\n /** Custom back icon. Default a left arrow. */\n backIcon?: ReactNode;\n /** Brand / logo node shown in the left slot (after the back button). */\n brand?: ReactNode;\n /** Right slot — action buttons / menu. One node or many. */\n actions?: ReactNode;\n /** Center the title (three-column grid). Default `false` (left-aligned). */\n centered?: boolean;\n /**\n * Stick to the top of the scroll container. Default `true`.\n *\n * The page has to leave the body out of the scrolling: `body { overflow-x:\n * hidden }` makes the body a scroll container and the bar scrolls away with\n * the content. Use `overflow-x: clip` on `html` and `body` instead — in\n * development the bar says so in the console when it detects it.\n */\n sticky?: boolean;\n /**\n * Visual tone. Default `\"surface\"`.\n *\n * `\"inverse\"` writes `data-tempest-tone=\"inverse\"` and paints the bar with the\n * inverse surface's `--tempest-bg`, so the back button, the title and the\n * components in `actions` invert with it. The tokens are opt-in:\n * `tempest-react-sdk/styles/inverse.css` for the SDK's own blue, or\n * `createTheme({ primary, inverse: createInverseSurface })` for yours. With\n * neither loaded the bar falls back to the page's `--tempest-bg` and\n * `--tempest-text` — legible, not inverted.\n *\n * A separate tone rather than a change to `\"primary\"`, because the inverse\n * fill is not always the brand's `500`: for `#0066ff` it is the `800`\n * (`#042e75`), and turning `\"primary\"` into it would repaint every bar that\n * uses it today.\n */\n tone?: AppBarTone;\n /** Thin bottom border. Default `true`. */\n bordered?: boolean;\n /** Add `env(safe-area-inset-top)` padding (iOS notch / PWA). Default `true`. */\n safeArea?: boolean;\n}\n\n/**\n * Mobile-first top app bar for PWAs — leading (back / brand) + title +\n * trailing actions, sticky with safe-area padding out of the box.\n *\n * Consumers customise via tokens (`--tempest-*`) and slots; the SDK ships the\n * layout, sticky/safe-area behaviour, and accessible back button so apps don't\n * hand-roll one per screen. For a desktop three-slot nav use [[Navbar]].\n *\n * @example\n * // Detail screen with back + a trailing action\n * <AppBar\n * title=\"Profile\"\n * showBack\n * onBack={() => navigate(-1)}\n * actions={<IconButton icon={<Settings />} onClick={openSettings} />}\n * />\n *\n * @example\n * // Home screen — brand left, centered title disabled\n * <AppBar brand={<Logo />} actions={<UserMenu />} />\n */\nexport function AppBar({\n title,\n leading,\n showBack = false,\n onBack,\n backLabel = \"Go back\",\n backIcon,\n brand,\n actions,\n centered = false,\n sticky = true,\n tone = \"surface\",\n bordered = true,\n safeArea = true,\n className,\n ...props\n}: AppBarProps) {\n useStickyBodyWarning(sticky);\n\n const handleBack = onBack ?? (() => window.history.back());\n\n const leadingContent = leading ?? (\n <>\n {showBack && (\n <button\n type=\"button\"\n className={styles.iconBtn}\n onClick={handleBack}\n aria-label={backLabel}\n >\n {backIcon ?? <ArrowLeft size={22} aria-hidden=\"true\" />}\n </button>\n )}\n {brand && <span className={styles.brand}>{brand}</span>}\n </>\n );\n\n return (\n <header\n className={cn(\n styles.bar,\n styles[tone],\n centered && styles.centered,\n sticky && styles.sticky,\n bordered && styles.bordered,\n safeArea && styles.safeArea,\n className,\n )}\n data-tempest-tone={tone === \"inverse\" ? \"inverse\" : undefined}\n {...props}\n >\n <div className={styles.leading}>{leadingContent}</div>\n {title != null && title !== \"\" && (\n <h1 className={styles.title} aria-live=\"polite\">\n {title}\n </h1>\n )}\n <div className={styles.actions}>{actions}</div>\n </header>\n );\n}\n"],"mappings":"+KA4GA,SAAgB,EAAO,CACnB,QACA,UACA,WAAW,GACX,SACA,YAAY,UACZ,WACA,QACA,UACA,WAAW,GACX,SAAS,GACT,OAAO,UACP,WAAW,GACX,WAAW,GACX,YACA,GAAG,GACS,CACZ,EAAA,qBAAqB,CAAM,EAE3B,IAAM,EAAa,QAAiB,OAAO,QAAQ,KAAK,GAElD,EAAiB,IACnB,EAAA,EAAA,KAAA,CAAA,EAAA,SAAA,CAAA,SAAA,CACK,IACG,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,QAAO,QAClB,QAAS,EACT,aAAY,EAEX,SAAA,IAAY,EAAA,EAAA,IAAA,CAAC,EAAA,UAAD,CAAW,KAAM,GAAI,cAAY,MAAQ,CAAA,CAClD,CAAA,EAEX,IAAS,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,MAAQ,SAAA,CAAY,CAAA,CACxD,CAAA,CAAA,EAGN,OACI,EAAA,EAAA,KAAA,CAAC,SAAD,CACI,UAAW,EAAA,GACP,EAAA,QAAO,IACP,EAAA,QAAO,GACP,GAAY,EAAA,QAAO,SACnB,GAAU,EAAA,QAAO,OACjB,GAAY,EAAA,QAAO,SACnB,GAAY,EAAA,QAAO,SACnB,CACJ,EACA,oBAAmB,IAAS,UAAY,UAAY,IAAA,GACpD,GAAI,EAXR,SAAA,EAaI,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,QAAU,SAAA,CAAoB,CAAA,EACpD,GAAS,MAAQ,IAAU,KACxB,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAW,EAAA,QAAO,MAAO,YAAU,SAClC,SAAA,CACD,CAAA,GAER,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,QAAU,SAAA,CAAa,CAAA,CAC1C,GAEhB"}
|
|
@@ -21,6 +21,7 @@ function s({ title: s, leading: c, showBack: l = !1, onBack: u, backLabel: d = "
|
|
|
21
21
|
})] });
|
|
22
22
|
return /* @__PURE__ */ a("header", {
|
|
23
23
|
className: e(n.bar, n[_], h && n.centered, g && n.sticky, v && n.bordered, y && n.safeArea, b),
|
|
24
|
+
"data-tempest-tone": _ === "inverse" ? "inverse" : void 0,
|
|
24
25
|
...x,
|
|
25
26
|
children: [
|
|
26
27
|
/* @__PURE__ */ i("div", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppBar.js","names":[],"sources":["../../../src/components/AppBar/AppBar.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — a top bar is a slot layout: leading (leading,\n * showBack, onBack, backLabel, backIcon, brand), centre (title, centered) and\n * trailing (actions), plus the chrome it sits in (sticky, tone, bordered, safeArea).\n * Every one of those is a real decision an app makes per screen, and the alternative\n * to accepting them is the app rebuilding the bar around the parts it wanted to\n * change.\n */\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { ArrowLeft } from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport { useStickyBodyWarning } from \"./use-sticky-body-warning\";\nimport styles from \"./AppBar.module.css\";\n\nexport type AppBarTone = \"surface\" | \"primary\" | \"transparent\";\n\nexport interface AppBarProps extends Omit<HTMLAttributes<HTMLElement>, \"title\"> {\n /** Page title — string or any node. Rendered as the bar's `<h1>`. */\n title?: ReactNode;\n /**\n * Replace the whole left slot. When set, the auto back button and `brand`\n * are ignored — you own the leading content.\n */\n leading?: ReactNode;\n /** Show a back button in the left slot. Ignored when `leading` is set. */\n showBack?: boolean;\n /**\n * Back-button handler. Defaults to `window.history.back()`. With a router,\n * pass `onBack={() => navigate(-1)}`.\n */\n onBack?: () => void;\n /** Accessible label for the back button. Default `\"Go back\"`. */\n backLabel?: string;\n /** Custom back icon. Default a left arrow. */\n backIcon?: ReactNode;\n /** Brand / logo node shown in the left slot (after the back button). */\n brand?: ReactNode;\n /** Right slot — action buttons / menu. One node or many. */\n actions?: ReactNode;\n /** Center the title (three-column grid). Default `false` (left-aligned). */\n centered?: boolean;\n /**\n * Stick to the top of the scroll container. Default `true`.\n *\n * The page has to leave the body out of the scrolling: `body { overflow-x:\n * hidden }` makes the body a scroll container and the bar scrolls away with\n * the content. Use `overflow-x: clip` on `html` and `body` instead — in\n * development the bar says so in the console when it detects it.\n */\n sticky?: boolean;\n
|
|
1
|
+
{"version":3,"file":"AppBar.js","names":[],"sources":["../../../src/components/AppBar/AppBar.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — a top bar is a slot layout: leading (leading,\n * showBack, onBack, backLabel, backIcon, brand), centre (title, centered) and\n * trailing (actions), plus the chrome it sits in (sticky, tone, bordered, safeArea).\n * Every one of those is a real decision an app makes per screen, and the alternative\n * to accepting them is the app rebuilding the bar around the parts it wanted to\n * change.\n */\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { ArrowLeft } from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport { useStickyBodyWarning } from \"./use-sticky-body-warning\";\nimport styles from \"./AppBar.module.css\";\n\n/**\n * Visual tone of an {@link AppBar}.\n *\n * - `\"surface\"` — the page's surface and text.\n * - `\"primary\"` — the brand fill (`--tempest-primary`) with\n * `--tempest-primary-foreground` ink. Only the bar's own text and back button\n * follow the fill: components in `actions` keep the page's tokens. Measured in\n * Chromium over the SDK's `#0066ff`, a primary `Button` inside it has the same\n * color as the bar (1:1) and its focus ring reads 1.41:1.\n * - `\"inverse\"` — the inverse surface: every color token inverted inside the bar,\n * so the components in it are measured against the fill. See\n * {@link AppBarProps.tone}.\n * - `\"transparent\"` — no background; the text inherits.\n */\nexport type AppBarTone = \"surface\" | \"primary\" | \"inverse\" | \"transparent\";\n\nexport interface AppBarProps extends Omit<HTMLAttributes<HTMLElement>, \"title\"> {\n /** Page title — string or any node. Rendered as the bar's `<h1>`. */\n title?: ReactNode;\n /**\n * Replace the whole left slot. When set, the auto back button and `brand`\n * are ignored — you own the leading content.\n */\n leading?: ReactNode;\n /** Show a back button in the left slot. Ignored when `leading` is set. */\n showBack?: boolean;\n /**\n * Back-button handler. Defaults to `window.history.back()`. With a router,\n * pass `onBack={() => navigate(-1)}`.\n */\n onBack?: () => void;\n /** Accessible label for the back button. Default `\"Go back\"`. */\n backLabel?: string;\n /** Custom back icon. Default a left arrow. */\n backIcon?: ReactNode;\n /** Brand / logo node shown in the left slot (after the back button). */\n brand?: ReactNode;\n /** Right slot — action buttons / menu. One node or many. */\n actions?: ReactNode;\n /** Center the title (three-column grid). Default `false` (left-aligned). */\n centered?: boolean;\n /**\n * Stick to the top of the scroll container. Default `true`.\n *\n * The page has to leave the body out of the scrolling: `body { overflow-x:\n * hidden }` makes the body a scroll container and the bar scrolls away with\n * the content. Use `overflow-x: clip` on `html` and `body` instead — in\n * development the bar says so in the console when it detects it.\n */\n sticky?: boolean;\n /**\n * Visual tone. Default `\"surface\"`.\n *\n * `\"inverse\"` writes `data-tempest-tone=\"inverse\"` and paints the bar with the\n * inverse surface's `--tempest-bg`, so the back button, the title and the\n * components in `actions` invert with it. The tokens are opt-in:\n * `tempest-react-sdk/styles/inverse.css` for the SDK's own blue, or\n * `createTheme({ primary, inverse: createInverseSurface })` for yours. With\n * neither loaded the bar falls back to the page's `--tempest-bg` and\n * `--tempest-text` — legible, not inverted.\n *\n * A separate tone rather than a change to `\"primary\"`, because the inverse\n * fill is not always the brand's `500`: for `#0066ff` it is the `800`\n * (`#042e75`), and turning `\"primary\"` into it would repaint every bar that\n * uses it today.\n */\n tone?: AppBarTone;\n /** Thin bottom border. Default `true`. */\n bordered?: boolean;\n /** Add `env(safe-area-inset-top)` padding (iOS notch / PWA). Default `true`. */\n safeArea?: boolean;\n}\n\n/**\n * Mobile-first top app bar for PWAs — leading (back / brand) + title +\n * trailing actions, sticky with safe-area padding out of the box.\n *\n * Consumers customise via tokens (`--tempest-*`) and slots; the SDK ships the\n * layout, sticky/safe-area behaviour, and accessible back button so apps don't\n * hand-roll one per screen. For a desktop three-slot nav use [[Navbar]].\n *\n * @example\n * // Detail screen with back + a trailing action\n * <AppBar\n * title=\"Profile\"\n * showBack\n * onBack={() => navigate(-1)}\n * actions={<IconButton icon={<Settings />} onClick={openSettings} />}\n * />\n *\n * @example\n * // Home screen — brand left, centered title disabled\n * <AppBar brand={<Logo />} actions={<UserMenu />} />\n */\nexport function AppBar({\n title,\n leading,\n showBack = false,\n onBack,\n backLabel = \"Go back\",\n backIcon,\n brand,\n actions,\n centered = false,\n sticky = true,\n tone = \"surface\",\n bordered = true,\n safeArea = true,\n className,\n ...props\n}: AppBarProps) {\n useStickyBodyWarning(sticky);\n\n const handleBack = onBack ?? (() => window.history.back());\n\n const leadingContent = leading ?? (\n <>\n {showBack && (\n <button\n type=\"button\"\n className={styles.iconBtn}\n onClick={handleBack}\n aria-label={backLabel}\n >\n {backIcon ?? <ArrowLeft size={22} aria-hidden=\"true\" />}\n </button>\n )}\n {brand && <span className={styles.brand}>{brand}</span>}\n </>\n );\n\n return (\n <header\n className={cn(\n styles.bar,\n styles[tone],\n centered && styles.centered,\n sticky && styles.sticky,\n bordered && styles.bordered,\n safeArea && styles.safeArea,\n className,\n )}\n data-tempest-tone={tone === \"inverse\" ? \"inverse\" : undefined}\n {...props}\n >\n <div className={styles.leading}>{leadingContent}</div>\n {title != null && title !== \"\" && (\n <h1 className={styles.title} aria-live=\"polite\">\n {title}\n </h1>\n )}\n <div className={styles.actions}>{actions}</div>\n </header>\n );\n}\n"],"mappings":";;;;;;AA4GA,SAAgB,EAAO,EACnB,UACA,YACA,cAAW,IACX,WACA,eAAY,WACZ,aACA,UACA,YACA,cAAW,IACX,YAAS,IACT,UAAO,WACP,cAAW,IACX,cAAW,IACX,cACA,GAAG,KACS;CACZ,EAAqB,CAAM;CAE3B,IAAM,IAAa,YAAiB,OAAO,QAAQ,KAAK,IAElD,IAAiB,KACnB,kBAAA,GAAA,EAAA,UAAA,CACK,KACG,kBAAC,UAAD;EACI,MAAK;EACL,WAAW,EAAO;EAClB,SAAS;EACT,cAAY;EAEX,UAAA,KAAY,kBAAC,GAAD;GAAW,MAAM;GAAI,eAAY;EAAQ,CAAA;CAClD,CAAA,GAEX,KAAS,kBAAC,QAAD;EAAM,WAAW,EAAO;EAAQ,UAAA;CAAY,CAAA,CACxD,EAAA,CAAA;CAGN,OACI,kBAAC,UAAD;EACI,WAAW,EACP,EAAO,KACP,EAAO,IACP,KAAY,EAAO,UACnB,KAAU,EAAO,QACjB,KAAY,EAAO,UACnB,KAAY,EAAO,UACnB,CACJ;EACA,qBAAmB,MAAS,YAAY,YAAY,KAAA;EACpD,GAAI;EAXR,UAAA;GAaI,kBAAC,OAAD;IAAK,WAAW,EAAO;IAAU,UAAA;GAAoB,CAAA;GACpD,KAAS,QAAQ,MAAU,MACxB,kBAAC,MAAD;IAAI,WAAW,EAAO;IAAO,aAAU;IAClC,UAAA;GACD,CAAA;GAER,kBAAC,OAAD;IAAK,WAAW,EAAO;IAAU,UAAA;GAAa,CAAA;EAC1C;;AAEhB"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
require("../../styles/component/AppBar.css");
|
|
2
|
-
var e=`tempest_bar_SHqWS`,t=`tempest_centered_S0CCw`,n=`tempest_surface_fusKb`,r=`tempest_primary_QcCyN`,i=`
|
|
2
|
+
var e=`tempest_bar_SHqWS`,t=`tempest_centered_S0CCw`,n=`tempest_surface_fusKb`,r=`tempest_primary_QcCyN`,i=`tempest_inverse_Icool`,a=`tempest_transparent_uGrsu`,o=`tempest_bordered_5M9FZ`,s=`tempest_sticky_DStUO`,c=`tempest_safeArea_Vo3Bi`,l=`tempest_leading_mXv-d`,u=`tempest_actions_x7tMx`,d=`tempest_title_qaMMQ`,f=`tempest_brand_xKt54`,p=`tempest_iconBtn_S-BTg`,m={bar:e,centered:t,surface:n,primary:r,inverse:i,transparent:a,bordered:o,sticky:s,safeArea:c,leading:l,actions:u,title:d,brand:f,iconBtn:p};exports.actions=u,exports.bar=e,exports.bordered=o,exports.brand=f,exports.centered=t,exports.default=m,exports.iconBtn=p,exports.inverse=i,exports.leading=l,exports.primary=r,exports.safeArea=c,exports.sticky=s,exports.surface=n,exports.title=d,exports.transparent=a;
|
|
3
3
|
//# sourceMappingURL=AppBar.module.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppBar.module.cjs","names":[],"sources":["../../../src/components/AppBar/AppBar.module.css"],"sourcesContent":[".bar {\n display: grid;\n grid-template-columns: auto 1fr auto;\n align-items: center;\n gap: var(--tempest-space-2);\n width: 100%;\n min-height: 56px;\n padding: var(--tempest-space-3) var(--tempest-space-4);\n font-family: var(--tempest-font-sans);\n}\n\n/* Symmetric side columns so the title is truly centered. */\n.bar.centered {\n grid-template-columns: 1fr auto 1fr;\n}\n\n.bar.surface {\n background: var(--tempest-surface);\n color: var(--tempest-text);\n}\n\n.bar.primary {\n background: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n}\n\n.bar.transparent {\n background: transparent;\n color: inherit;\n}\n\n.bar.bordered {\n border-bottom: 1px solid var(--tempest-border);\n}\n\n.bar.sticky {\n position: sticky;\n top: 0;\n z-index: 30;\n}\n\n.bar.safeArea {\n padding-top: max(var(--tempest-space-3), env(safe-area-inset-top, 0px));\n}\n\n.leading {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n min-width: 0;\n}\n\n.actions {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: var(--tempest-space-1);\n min-width: 0;\n}\n\n.title {\n margin: 0;\n min-width: 0;\n font-size: var(--tempest-text-xl);\n font-weight: var(--tempest-weight-bold);\n line-height: 1.2;\n text-align: left;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.bar.centered .title {\n text-align: center;\n}\n\n.brand {\n font-size: var(--tempest-text-lg);\n font-weight: var(--tempest-weight-bold);\n color: inherit;\n white-space: nowrap;\n}\n\n.iconBtn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: 0 0 auto;\n width: 36px;\n height: 36px;\n padding: 0;\n border: none;\n border-radius: var(--tempest-radius-full);\n background: transparent;\n color: inherit;\n cursor: pointer;\n}\n\n.iconBtn:hover {\n background: var(--tempest-surface-2);\n}\n\n.iconBtn:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n"],"mappings":";"}
|
|
1
|
+
{"version":3,"file":"AppBar.module.cjs","names":[],"sources":["../../../src/components/AppBar/AppBar.module.css"],"sourcesContent":[".bar {\n display: grid;\n grid-template-columns: auto 1fr auto;\n align-items: center;\n gap: var(--tempest-space-2);\n width: 100%;\n min-height: 56px;\n padding: var(--tempest-space-3) var(--tempest-space-4);\n font-family: var(--tempest-font-sans);\n}\n\n/* Symmetric side columns so the title is truly centered. */\n.bar.centered {\n grid-template-columns: 1fr auto 1fr;\n}\n\n.bar.surface {\n background: var(--tempest-surface);\n color: var(--tempest-text);\n}\n\n.bar.primary {\n background: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n}\n\n.bar.inverse {\n background: var(--tempest-bg);\n color: var(--tempest-text);\n}\n\n.bar.transparent {\n background: transparent;\n color: inherit;\n}\n\n.bar.bordered {\n border-bottom: 1px solid var(--tempest-border);\n}\n\n.bar.sticky {\n position: sticky;\n top: 0;\n z-index: 30;\n}\n\n.bar.safeArea {\n padding-top: max(var(--tempest-space-3), env(safe-area-inset-top, 0px));\n}\n\n.leading {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n min-width: 0;\n}\n\n.actions {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: var(--tempest-space-1);\n min-width: 0;\n}\n\n.title {\n margin: 0;\n min-width: 0;\n font-size: var(--tempest-text-xl);\n font-weight: var(--tempest-weight-bold);\n line-height: 1.2;\n text-align: left;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.bar.centered .title {\n text-align: center;\n}\n\n.brand {\n font-size: var(--tempest-text-lg);\n font-weight: var(--tempest-weight-bold);\n color: inherit;\n white-space: nowrap;\n}\n\n.iconBtn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: 0 0 auto;\n width: 36px;\n height: 36px;\n padding: 0;\n border: none;\n border-radius: var(--tempest-radius-full);\n background: transparent;\n color: inherit;\n cursor: pointer;\n}\n\n.iconBtn:hover {\n background: var(--tempest-surface-2);\n}\n\n.iconBtn:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n"],"mappings":";"}
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
import "../../styles/component/AppBar.css";
|
|
2
2
|
//#region src/components/AppBar/AppBar.module.css
|
|
3
|
-
var e = "tempest_bar_SHqWS", t = "tempest_centered_S0CCw", n = "tempest_surface_fusKb", r = "tempest_primary_QcCyN", i = "
|
|
3
|
+
var e = "tempest_bar_SHqWS", t = "tempest_centered_S0CCw", n = "tempest_surface_fusKb", r = "tempest_primary_QcCyN", i = "tempest_inverse_Icool", a = "tempest_transparent_uGrsu", o = "tempest_bordered_5M9FZ", s = "tempest_sticky_DStUO", c = "tempest_safeArea_Vo3Bi", l = "tempest_leading_mXv-d", u = "tempest_actions_x7tMx", d = "tempest_title_qaMMQ", f = "tempest_brand_xKt54", p = "tempest_iconBtn_S-BTg", m = {
|
|
4
4
|
bar: e,
|
|
5
5
|
centered: t,
|
|
6
6
|
surface: n,
|
|
7
7
|
primary: r,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
inverse: i,
|
|
9
|
+
transparent: a,
|
|
10
|
+
bordered: o,
|
|
11
|
+
sticky: s,
|
|
12
|
+
safeArea: c,
|
|
13
|
+
leading: l,
|
|
14
|
+
actions: u,
|
|
15
|
+
title: d,
|
|
16
|
+
brand: f,
|
|
17
|
+
iconBtn: p
|
|
17
18
|
};
|
|
18
19
|
//#endregion
|
|
19
|
-
export {
|
|
20
|
+
export { u as actions, e as bar, o as bordered, f as brand, t as centered, m as default, p as iconBtn, i as inverse, l as leading, r as primary, c as safeArea, s as sticky, n as surface, d as title, a as transparent };
|
|
20
21
|
|
|
21
22
|
//# sourceMappingURL=AppBar.module.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppBar.module.js","names":[],"sources":["../../../src/components/AppBar/AppBar.module.css"],"sourcesContent":[".bar {\n display: grid;\n grid-template-columns: auto 1fr auto;\n align-items: center;\n gap: var(--tempest-space-2);\n width: 100%;\n min-height: 56px;\n padding: var(--tempest-space-3) var(--tempest-space-4);\n font-family: var(--tempest-font-sans);\n}\n\n/* Symmetric side columns so the title is truly centered. */\n.bar.centered {\n grid-template-columns: 1fr auto 1fr;\n}\n\n.bar.surface {\n background: var(--tempest-surface);\n color: var(--tempest-text);\n}\n\n.bar.primary {\n background: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n}\n\n.bar.transparent {\n background: transparent;\n color: inherit;\n}\n\n.bar.bordered {\n border-bottom: 1px solid var(--tempest-border);\n}\n\n.bar.sticky {\n position: sticky;\n top: 0;\n z-index: 30;\n}\n\n.bar.safeArea {\n padding-top: max(var(--tempest-space-3), env(safe-area-inset-top, 0px));\n}\n\n.leading {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n min-width: 0;\n}\n\n.actions {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: var(--tempest-space-1);\n min-width: 0;\n}\n\n.title {\n margin: 0;\n min-width: 0;\n font-size: var(--tempest-text-xl);\n font-weight: var(--tempest-weight-bold);\n line-height: 1.2;\n text-align: left;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.bar.centered .title {\n text-align: center;\n}\n\n.brand {\n font-size: var(--tempest-text-lg);\n font-weight: var(--tempest-weight-bold);\n color: inherit;\n white-space: nowrap;\n}\n\n.iconBtn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: 0 0 auto;\n width: 36px;\n height: 36px;\n padding: 0;\n border: none;\n border-radius: var(--tempest-radius-full);\n background: transparent;\n color: inherit;\n cursor: pointer;\n}\n\n.iconBtn:hover {\n background: var(--tempest-surface-2);\n}\n\n.iconBtn:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n"],"mappings":";"}
|
|
1
|
+
{"version":3,"file":"AppBar.module.js","names":[],"sources":["../../../src/components/AppBar/AppBar.module.css"],"sourcesContent":[".bar {\n display: grid;\n grid-template-columns: auto 1fr auto;\n align-items: center;\n gap: var(--tempest-space-2);\n width: 100%;\n min-height: 56px;\n padding: var(--tempest-space-3) var(--tempest-space-4);\n font-family: var(--tempest-font-sans);\n}\n\n/* Symmetric side columns so the title is truly centered. */\n.bar.centered {\n grid-template-columns: 1fr auto 1fr;\n}\n\n.bar.surface {\n background: var(--tempest-surface);\n color: var(--tempest-text);\n}\n\n.bar.primary {\n background: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n}\n\n.bar.inverse {\n background: var(--tempest-bg);\n color: var(--tempest-text);\n}\n\n.bar.transparent {\n background: transparent;\n color: inherit;\n}\n\n.bar.bordered {\n border-bottom: 1px solid var(--tempest-border);\n}\n\n.bar.sticky {\n position: sticky;\n top: 0;\n z-index: 30;\n}\n\n.bar.safeArea {\n padding-top: max(var(--tempest-space-3), env(safe-area-inset-top, 0px));\n}\n\n.leading {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n min-width: 0;\n}\n\n.actions {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: var(--tempest-space-1);\n min-width: 0;\n}\n\n.title {\n margin: 0;\n min-width: 0;\n font-size: var(--tempest-text-xl);\n font-weight: var(--tempest-weight-bold);\n line-height: 1.2;\n text-align: left;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.bar.centered .title {\n text-align: center;\n}\n\n.brand {\n font-size: var(--tempest-text-lg);\n font-weight: var(--tempest-weight-bold);\n color: inherit;\n white-space: nowrap;\n}\n\n.iconBtn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: 0 0 auto;\n width: 36px;\n height: 36px;\n padding: 0;\n border: none;\n border-radius: var(--tempest-radius-full);\n background: transparent;\n color: inherit;\n cursor: pointer;\n}\n\n.iconBtn:hover {\n background: var(--tempest-surface-2);\n}\n\n.iconBtn:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n"],"mappings":";"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.module.cjs","names":[],"sources":["../../../src/components/Button/Button.module.css"],"sourcesContent":[".button {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-control-gap, var(--tempest-space-2));\n border: 1px solid transparent;\n border-radius: var(--tempest-control-radius, var(--tempest-radius-md));\n font-family: var(--tempest-font-sans);\n font-weight: var(--tempest-weight-semibold);\n line-height: var(--tempest-leading-none);\n letter-spacing: var(--tempest-tracking-normal);\n color: var(--_tempest-button-fg);\n text-decoration: none;\n white-space: nowrap;\n user-select: none;\n transition: var(--tempest-transition-base);\n box-shadow: var(--tempest-shadow-xs);\n}\n\n.button:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .button:hover:not(:disabled, [aria-disabled=\"true\"]) {\n box-shadow: var(--tempest-shadow-sm);\n }\n}\n\n.button:active:not(:disabled, [aria-disabled=\"true\"]) {\n transform: translateY(1px);\n box-shadow: var(--tempest-shadow-xs);\n}\n\n.button:disabled,\n.button[aria-disabled=\"true\"] {\n opacity: 0.55;\n box-shadow: none;\n}\n\n.button[aria-disabled=\"true\"] {\n cursor: not-allowed;\n}\n\n/*\n * `<Button href>` and `<ButtonSlot>` around a link render an `<a>`, and an app's own link\n * rules reach it. `.button` is one class (0,1,0); `a:hover { color }` is (0,1,1) and\n * wins — the Vite React template ships exactly that rule, so in a fresh app a\n * primary link-button turned its label `#535bf2` on hover, over the primary fill.\n *\n * Each variant therefore sets its text colour as `--_tempest-button-fg` instead of\n * `color`, and this rule restates it on every link state at (0,2,1), above any\n * single-element link rule. The `link` variant's hover underline is (0,3,0) and\n * still applies.\n */\na.button:is(:link, :visited, :hover, :active, :focus, :not([href])) {\n color: var(--_tempest-button-fg);\n text-decoration: none;\n}\n\n/* ============================================================\n * Variants — solid (default), soft, outline, ghost\n * Status colors: primary, secondary (neutral), danger, success\n * ============================================================ */\n\n/* primary — solid filled */\n.primary {\n background-color: var(--tempest-primary);\n --_tempest-button-fg: var(--tempest-primary-foreground);\n}\n.primary:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-primary-hover);\n}\n.primary:active:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-primary-active);\n}\n\n/* secondary — neutral surface */\n.secondary {\n background-color: var(--tempest-surface);\n --_tempest-button-fg: var(--tempest-text);\n border-color: var(--tempest-border);\n}\n.secondary:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-surface-2);\n border-color: var(--tempest-border-strong);\n}\n\n/* danger — solid red */\n.danger {\n background-color: var(--tempest-danger-solid);\n --_tempest-button-fg: var(--tempest-danger-on-solid);\n}\n.danger:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-danger-hover);\n}\n\n/* success — solid green */\n.success {\n background-color: var(--tempest-success-solid);\n --_tempest-button-fg: var(--tempest-success-on-solid);\n}\n.success:hover:not(:disabled, [aria-disabled=\"true\"]) {\n filter: brightness(0.92);\n}\n\n/* ghost — transparent, hover surface */\n.ghost {\n background-color: transparent;\n --_tempest-button-fg: var(--tempest-text);\n box-shadow: none;\n}\n.ghost:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-surface);\n box-shadow: none;\n}\n.ghost:active:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-surface-2);\n box-shadow: none;\n}\n\n/* soft — tinted background, primary text */\n.soft {\n background-color: var(--tempest-primary-soft);\n --_tempest-button-fg: var(--tempest-primary-on-soft);\n box-shadow: none;\n}\n.soft:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-primary-soft-hover);\n}\n\n/* outline — bordered, transparent fill */\n.outline {\n background-color: transparent;\n --_tempest-button-fg: var(--tempest-primary);\n border-color: var(--tempest-primary);\n box-shadow: none;\n}\n/*\n * The text colour has to move with the background.\n *\n * `.outline` sets its text to `var(--tempest-primary)` over a transparent background,\n * where it is fine. This rule tints the background to `--tempest-primary-soft` and,\n * without restating the colour, leaves the fill token sitting on the tint: **4.38:1 in\n * the light theme and 4.28:1 in dark**, both under the 4.5:1 floor for text — so the\n * label got harder to read at the exact moment the pointer was on it.\n *\n * `--tempest-primary-on-soft` is the token for this ground (6.18:1 / 8.72:1) and\n * `docs/styles.md` has documented the rule all along. A rule that changes only the\n * background of a coloured element is the shape of this bug; the other eight places in\n * the SDK that tint a background inherit `--tempest-text`, `--tempest-text-muted` or\n * `--tempest-primary-on-soft` and are fine.\n */\n.outline:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-primary-soft);\n --_tempest-button-fg: var(--tempest-primary-on-soft);\n}\n\n/* link — text-only, underline on hover */\n.link {\n background-color: transparent;\n --_tempest-button-fg: var(--tempest-primary);\n box-shadow: none;\n padding-left: 0;\n padding-right: 0;\n border-radius: var(--tempest-radius-sm);\n}\n.link:hover:not(:disabled, [aria-disabled=\"true\"]) {\n --_tempest-button-fg: var(--tempest-primary-hover);\n text-decoration: underline;\n background: none;\n box-shadow: none;\n}\n\n/* ============================================================\n * Sizes — token-driven, density-aware\n * ============================================================ */\n.xs {\n height: var(--tempest-control-height-xs);\n padding: 0 var(--tempest-control-padding-xs);\n font-size: var(--tempest-control-font-xs);\n}\n.sm {\n height: var(--tempest-control-height-sm);\n padding: 0 var(--tempest-control-padding-sm);\n font-size: var(--tempest-control-font-sm);\n}\n.md {\n height: var(--tempest-control-height-md);\n padding: 0 var(--tempest-control-padding-md);\n font-size: var(--tempest-control-font-md);\n}\n.lg {\n height: var(--tempest-control-height-lg);\n padding: 0 var(--tempest-control-padding-lg);\n font-size: var(--tempest-control-font-lg);\n}\n.xl {\n height: var(--tempest-control-height-xl);\n padding: 0 var(--tempest-control-padding-xl);\n font-size: var(--tempest-control-font-xl);\n}\n\n/* Square (icon-only) — width matches height */\n.iconOnly {\n position: relative;\n}\n.iconOnly.xs {\n width: var(--tempest-control-height-xs);\n padding: 0;\n}\n.iconOnly.sm {\n width: var(--tempest-control-height-sm);\n padding: 0;\n}\n.iconOnly.md {\n width: var(--tempest-control-height-md);\n padding: 0;\n}\n.iconOnly.lg {\n width: var(--tempest-control-height-lg);\n padding: 0;\n}\n.iconOnly.xl {\n width: var(--tempest-control-height-xl);\n padding: 0;\n}\n\n/* Touch hit-slop — extend tap target to a min 44×44 on coarse pointers\n * for the smallest icon-only sizes, without changing visual footprint. */\n@media (pointer: coarse) {\n .iconOnly.xs::after,\n .iconOnly.sm::after {\n content: \"\";\n position: absolute;\n inset: -8px;\n }\n}\n\n/* Pill shape */\n.pill {\n border-radius: var(--tempest-radius-full);\n}\n\n.fullWidth {\n width: 100%;\n}\n\n/*\n * Lay out leftIcon + label + rightIcon as one centered, gapped row. Without\n * this the three are inline inside a single span (no gap, icon baseline-\n * aligned instead of centered), which reads as off-center — most visible on\n * fullWidth buttons where the label has room to drift.\n */\n.content {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-control-gap, var(--tempest-space-2));\n min-width: 0;\n}\n\n.loading .hiddenText {\n
|
|
1
|
+
{"version":3,"file":"Button.module.cjs","names":[],"sources":["../../../src/components/Button/Button.module.css"],"sourcesContent":[".button {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-control-gap, var(--tempest-space-2));\n border: 1px solid transparent;\n border-radius: var(--tempest-control-radius, var(--tempest-radius-md));\n font-family: var(--tempest-font-sans);\n font-weight: var(--tempest-weight-semibold);\n line-height: var(--tempest-leading-none);\n letter-spacing: var(--tempest-tracking-normal);\n color: var(--_tempest-button-fg);\n text-decoration: none;\n white-space: nowrap;\n user-select: none;\n transition: var(--tempest-transition-base);\n box-shadow: var(--tempest-shadow-xs);\n}\n\n.button:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .button:hover:not(:disabled, [aria-disabled=\"true\"]) {\n box-shadow: var(--tempest-shadow-sm);\n }\n}\n\n.button:active:not(:disabled, [aria-disabled=\"true\"]) {\n transform: translateY(1px);\n box-shadow: var(--tempest-shadow-xs);\n}\n\n.button:disabled,\n.button[aria-disabled=\"true\"] {\n opacity: 0.55;\n box-shadow: none;\n}\n\n.button[aria-disabled=\"true\"] {\n cursor: not-allowed;\n}\n\n/*\n * `<Button href>` and `<ButtonSlot>` around a link render an `<a>`, and an app's own link\n * rules reach it. `.button` is one class (0,1,0); `a:hover { color }` is (0,1,1) and\n * wins — the Vite React template ships exactly that rule, so in a fresh app a\n * primary link-button turned its label `#535bf2` on hover, over the primary fill.\n *\n * Each variant therefore sets its text colour as `--_tempest-button-fg` instead of\n * `color`, and this rule restates it on every link state at (0,2,1), above any\n * single-element link rule. The `link` variant's hover underline is (0,3,0) and\n * still applies.\n */\na.button:is(:link, :visited, :hover, :active, :focus, :not([href])) {\n color: var(--_tempest-button-fg);\n text-decoration: none;\n}\n\n/* ============================================================\n * Variants — solid (default), soft, outline, ghost\n * Status colors: primary, secondary (neutral), danger, success\n * ============================================================ */\n\n/* primary — solid filled */\n.primary {\n background-color: var(--tempest-primary);\n --_tempest-button-fg: var(--tempest-primary-foreground);\n}\n.primary:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-primary-hover);\n}\n.primary:active:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-primary-active);\n}\n\n/* secondary — neutral surface */\n.secondary {\n background-color: var(--tempest-surface);\n --_tempest-button-fg: var(--tempest-text);\n border-color: var(--tempest-border);\n}\n.secondary:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-surface-2);\n border-color: var(--tempest-border-strong);\n}\n\n/* danger — solid red */\n.danger {\n background-color: var(--tempest-danger-solid);\n --_tempest-button-fg: var(--tempest-danger-on-solid);\n}\n.danger:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-danger-hover);\n}\n\n/* success — solid green */\n.success {\n background-color: var(--tempest-success-solid);\n --_tempest-button-fg: var(--tempest-success-on-solid);\n}\n.success:hover:not(:disabled, [aria-disabled=\"true\"]) {\n filter: brightness(0.92);\n}\n\n/* ghost — transparent, hover surface */\n.ghost {\n background-color: transparent;\n --_tempest-button-fg: var(--tempest-text);\n box-shadow: none;\n}\n.ghost:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-surface);\n box-shadow: none;\n}\n.ghost:active:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-surface-2);\n box-shadow: none;\n}\n\n/* soft — tinted background, primary text */\n.soft {\n background-color: var(--tempest-primary-soft);\n --_tempest-button-fg: var(--tempest-primary-on-soft);\n box-shadow: none;\n}\n.soft:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-primary-soft-hover);\n}\n\n/* outline — bordered, transparent fill */\n.outline {\n background-color: transparent;\n --_tempest-button-fg: var(--tempest-primary);\n border-color: var(--tempest-primary);\n box-shadow: none;\n}\n/*\n * The text colour has to move with the background.\n *\n * `.outline` sets its text to `var(--tempest-primary)` over a transparent background,\n * where it is fine. This rule tints the background to `--tempest-primary-soft` and,\n * without restating the colour, leaves the fill token sitting on the tint: **4.38:1 in\n * the light theme and 4.28:1 in dark**, both under the 4.5:1 floor for text — so the\n * label got harder to read at the exact moment the pointer was on it.\n *\n * `--tempest-primary-on-soft` is the token for this ground (6.18:1 / 8.72:1) and\n * `docs/styles.md` has documented the rule all along. A rule that changes only the\n * background of a coloured element is the shape of this bug; the other eight places in\n * the SDK that tint a background inherit `--tempest-text`, `--tempest-text-muted` or\n * `--tempest-primary-on-soft` and are fine.\n */\n.outline:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-primary-soft);\n --_tempest-button-fg: var(--tempest-primary-on-soft);\n}\n\n/* link — text-only, underline on hover */\n.link {\n background-color: transparent;\n --_tempest-button-fg: var(--tempest-primary);\n box-shadow: none;\n padding-left: 0;\n padding-right: 0;\n border-radius: var(--tempest-radius-sm);\n}\n.link:hover:not(:disabled, [aria-disabled=\"true\"]) {\n --_tempest-button-fg: var(--tempest-primary-hover);\n text-decoration: underline;\n background: none;\n box-shadow: none;\n}\n\n/* ============================================================\n * Sizes — token-driven, density-aware\n * ============================================================ */\n.xs {\n height: var(--tempest-control-height-xs);\n padding: 0 var(--tempest-control-padding-xs);\n font-size: var(--tempest-control-font-xs);\n}\n.sm {\n height: var(--tempest-control-height-sm);\n padding: 0 var(--tempest-control-padding-sm);\n font-size: var(--tempest-control-font-sm);\n}\n.md {\n height: var(--tempest-control-height-md);\n padding: 0 var(--tempest-control-padding-md);\n font-size: var(--tempest-control-font-md);\n}\n.lg {\n height: var(--tempest-control-height-lg);\n padding: 0 var(--tempest-control-padding-lg);\n font-size: var(--tempest-control-font-lg);\n}\n.xl {\n height: var(--tempest-control-height-xl);\n padding: 0 var(--tempest-control-padding-xl);\n font-size: var(--tempest-control-font-xl);\n}\n\n/* Square (icon-only) — width matches height */\n.iconOnly {\n position: relative;\n}\n.iconOnly.xs {\n width: var(--tempest-control-height-xs);\n padding: 0;\n}\n.iconOnly.sm {\n width: var(--tempest-control-height-sm);\n padding: 0;\n}\n.iconOnly.md {\n width: var(--tempest-control-height-md);\n padding: 0;\n}\n.iconOnly.lg {\n width: var(--tempest-control-height-lg);\n padding: 0;\n}\n.iconOnly.xl {\n width: var(--tempest-control-height-xl);\n padding: 0;\n}\n\n/* Touch hit-slop — extend tap target to a min 44×44 on coarse pointers\n * for the smallest icon-only sizes, without changing visual footprint. */\n@media (pointer: coarse) {\n .iconOnly.xs::after,\n .iconOnly.sm::after {\n content: \"\";\n position: absolute;\n inset: -8px;\n }\n}\n\n/* Pill shape */\n.pill {\n border-radius: var(--tempest-radius-full);\n}\n\n.fullWidth {\n width: 100%;\n}\n\n/*\n * Lay out leftIcon + label + rightIcon as one centered, gapped row. Without\n * this the three are inline inside a single span (no gap, icon baseline-\n * aligned instead of centered), which reads as off-center — most visible on\n * fullWidth buttons where the label has room to drift.\n */\n.content {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-control-gap, var(--tempest-space-2));\n min-width: 0;\n}\n\n/*\n * Hide the label by paint only. `visibility: hidden` also drops it from the\n * accessibility tree, so a loading button lost its name (#416). `opacity: 0`\n * keeps the name and the width; `pointer-events: none` keeps the invisible\n * label out of hit-testing, as it was. Forced colors leaves `opacity` alone.\n */\n.loading .hiddenText {\n opacity: 0;\n pointer-events: none;\n}\n\n.spinner {\n position: absolute;\n animation: tempest-spin 0.8s linear infinite;\n}\n\n@keyframes tempest-spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n"],"mappings":";"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.module.js","names":[],"sources":["../../../src/components/Button/Button.module.css"],"sourcesContent":[".button {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-control-gap, var(--tempest-space-2));\n border: 1px solid transparent;\n border-radius: var(--tempest-control-radius, var(--tempest-radius-md));\n font-family: var(--tempest-font-sans);\n font-weight: var(--tempest-weight-semibold);\n line-height: var(--tempest-leading-none);\n letter-spacing: var(--tempest-tracking-normal);\n color: var(--_tempest-button-fg);\n text-decoration: none;\n white-space: nowrap;\n user-select: none;\n transition: var(--tempest-transition-base);\n box-shadow: var(--tempest-shadow-xs);\n}\n\n.button:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .button:hover:not(:disabled, [aria-disabled=\"true\"]) {\n box-shadow: var(--tempest-shadow-sm);\n }\n}\n\n.button:active:not(:disabled, [aria-disabled=\"true\"]) {\n transform: translateY(1px);\n box-shadow: var(--tempest-shadow-xs);\n}\n\n.button:disabled,\n.button[aria-disabled=\"true\"] {\n opacity: 0.55;\n box-shadow: none;\n}\n\n.button[aria-disabled=\"true\"] {\n cursor: not-allowed;\n}\n\n/*\n * `<Button href>` and `<ButtonSlot>` around a link render an `<a>`, and an app's own link\n * rules reach it. `.button` is one class (0,1,0); `a:hover { color }` is (0,1,1) and\n * wins — the Vite React template ships exactly that rule, so in a fresh app a\n * primary link-button turned its label `#535bf2` on hover, over the primary fill.\n *\n * Each variant therefore sets its text colour as `--_tempest-button-fg` instead of\n * `color`, and this rule restates it on every link state at (0,2,1), above any\n * single-element link rule. The `link` variant's hover underline is (0,3,0) and\n * still applies.\n */\na.button:is(:link, :visited, :hover, :active, :focus, :not([href])) {\n color: var(--_tempest-button-fg);\n text-decoration: none;\n}\n\n/* ============================================================\n * Variants — solid (default), soft, outline, ghost\n * Status colors: primary, secondary (neutral), danger, success\n * ============================================================ */\n\n/* primary — solid filled */\n.primary {\n background-color: var(--tempest-primary);\n --_tempest-button-fg: var(--tempest-primary-foreground);\n}\n.primary:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-primary-hover);\n}\n.primary:active:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-primary-active);\n}\n\n/* secondary — neutral surface */\n.secondary {\n background-color: var(--tempest-surface);\n --_tempest-button-fg: var(--tempest-text);\n border-color: var(--tempest-border);\n}\n.secondary:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-surface-2);\n border-color: var(--tempest-border-strong);\n}\n\n/* danger — solid red */\n.danger {\n background-color: var(--tempest-danger-solid);\n --_tempest-button-fg: var(--tempest-danger-on-solid);\n}\n.danger:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-danger-hover);\n}\n\n/* success — solid green */\n.success {\n background-color: var(--tempest-success-solid);\n --_tempest-button-fg: var(--tempest-success-on-solid);\n}\n.success:hover:not(:disabled, [aria-disabled=\"true\"]) {\n filter: brightness(0.92);\n}\n\n/* ghost — transparent, hover surface */\n.ghost {\n background-color: transparent;\n --_tempest-button-fg: var(--tempest-text);\n box-shadow: none;\n}\n.ghost:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-surface);\n box-shadow: none;\n}\n.ghost:active:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-surface-2);\n box-shadow: none;\n}\n\n/* soft — tinted background, primary text */\n.soft {\n background-color: var(--tempest-primary-soft);\n --_tempest-button-fg: var(--tempest-primary-on-soft);\n box-shadow: none;\n}\n.soft:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-primary-soft-hover);\n}\n\n/* outline — bordered, transparent fill */\n.outline {\n background-color: transparent;\n --_tempest-button-fg: var(--tempest-primary);\n border-color: var(--tempest-primary);\n box-shadow: none;\n}\n/*\n * The text colour has to move with the background.\n *\n * `.outline` sets its text to `var(--tempest-primary)` over a transparent background,\n * where it is fine. This rule tints the background to `--tempest-primary-soft` and,\n * without restating the colour, leaves the fill token sitting on the tint: **4.38:1 in\n * the light theme and 4.28:1 in dark**, both under the 4.5:1 floor for text — so the\n * label got harder to read at the exact moment the pointer was on it.\n *\n * `--tempest-primary-on-soft` is the token for this ground (6.18:1 / 8.72:1) and\n * `docs/styles.md` has documented the rule all along. A rule that changes only the\n * background of a coloured element is the shape of this bug; the other eight places in\n * the SDK that tint a background inherit `--tempest-text`, `--tempest-text-muted` or\n * `--tempest-primary-on-soft` and are fine.\n */\n.outline:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-primary-soft);\n --_tempest-button-fg: var(--tempest-primary-on-soft);\n}\n\n/* link — text-only, underline on hover */\n.link {\n background-color: transparent;\n --_tempest-button-fg: var(--tempest-primary);\n box-shadow: none;\n padding-left: 0;\n padding-right: 0;\n border-radius: var(--tempest-radius-sm);\n}\n.link:hover:not(:disabled, [aria-disabled=\"true\"]) {\n --_tempest-button-fg: var(--tempest-primary-hover);\n text-decoration: underline;\n background: none;\n box-shadow: none;\n}\n\n/* ============================================================\n * Sizes — token-driven, density-aware\n * ============================================================ */\n.xs {\n height: var(--tempest-control-height-xs);\n padding: 0 var(--tempest-control-padding-xs);\n font-size: var(--tempest-control-font-xs);\n}\n.sm {\n height: var(--tempest-control-height-sm);\n padding: 0 var(--tempest-control-padding-sm);\n font-size: var(--tempest-control-font-sm);\n}\n.md {\n height: var(--tempest-control-height-md);\n padding: 0 var(--tempest-control-padding-md);\n font-size: var(--tempest-control-font-md);\n}\n.lg {\n height: var(--tempest-control-height-lg);\n padding: 0 var(--tempest-control-padding-lg);\n font-size: var(--tempest-control-font-lg);\n}\n.xl {\n height: var(--tempest-control-height-xl);\n padding: 0 var(--tempest-control-padding-xl);\n font-size: var(--tempest-control-font-xl);\n}\n\n/* Square (icon-only) — width matches height */\n.iconOnly {\n position: relative;\n}\n.iconOnly.xs {\n width: var(--tempest-control-height-xs);\n padding: 0;\n}\n.iconOnly.sm {\n width: var(--tempest-control-height-sm);\n padding: 0;\n}\n.iconOnly.md {\n width: var(--tempest-control-height-md);\n padding: 0;\n}\n.iconOnly.lg {\n width: var(--tempest-control-height-lg);\n padding: 0;\n}\n.iconOnly.xl {\n width: var(--tempest-control-height-xl);\n padding: 0;\n}\n\n/* Touch hit-slop — extend tap target to a min 44×44 on coarse pointers\n * for the smallest icon-only sizes, without changing visual footprint. */\n@media (pointer: coarse) {\n .iconOnly.xs::after,\n .iconOnly.sm::after {\n content: \"\";\n position: absolute;\n inset: -8px;\n }\n}\n\n/* Pill shape */\n.pill {\n border-radius: var(--tempest-radius-full);\n}\n\n.fullWidth {\n width: 100%;\n}\n\n/*\n * Lay out leftIcon + label + rightIcon as one centered, gapped row. Without\n * this the three are inline inside a single span (no gap, icon baseline-\n * aligned instead of centered), which reads as off-center — most visible on\n * fullWidth buttons where the label has room to drift.\n */\n.content {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-control-gap, var(--tempest-space-2));\n min-width: 0;\n}\n\n.loading .hiddenText {\n
|
|
1
|
+
{"version":3,"file":"Button.module.js","names":[],"sources":["../../../src/components/Button/Button.module.css"],"sourcesContent":[".button {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-control-gap, var(--tempest-space-2));\n border: 1px solid transparent;\n border-radius: var(--tempest-control-radius, var(--tempest-radius-md));\n font-family: var(--tempest-font-sans);\n font-weight: var(--tempest-weight-semibold);\n line-height: var(--tempest-leading-none);\n letter-spacing: var(--tempest-tracking-normal);\n color: var(--_tempest-button-fg);\n text-decoration: none;\n white-space: nowrap;\n user-select: none;\n transition: var(--tempest-transition-base);\n box-shadow: var(--tempest-shadow-xs);\n}\n\n.button:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .button:hover:not(:disabled, [aria-disabled=\"true\"]) {\n box-shadow: var(--tempest-shadow-sm);\n }\n}\n\n.button:active:not(:disabled, [aria-disabled=\"true\"]) {\n transform: translateY(1px);\n box-shadow: var(--tempest-shadow-xs);\n}\n\n.button:disabled,\n.button[aria-disabled=\"true\"] {\n opacity: 0.55;\n box-shadow: none;\n}\n\n.button[aria-disabled=\"true\"] {\n cursor: not-allowed;\n}\n\n/*\n * `<Button href>` and `<ButtonSlot>` around a link render an `<a>`, and an app's own link\n * rules reach it. `.button` is one class (0,1,0); `a:hover { color }` is (0,1,1) and\n * wins — the Vite React template ships exactly that rule, so in a fresh app a\n * primary link-button turned its label `#535bf2` on hover, over the primary fill.\n *\n * Each variant therefore sets its text colour as `--_tempest-button-fg` instead of\n * `color`, and this rule restates it on every link state at (0,2,1), above any\n * single-element link rule. The `link` variant's hover underline is (0,3,0) and\n * still applies.\n */\na.button:is(:link, :visited, :hover, :active, :focus, :not([href])) {\n color: var(--_tempest-button-fg);\n text-decoration: none;\n}\n\n/* ============================================================\n * Variants — solid (default), soft, outline, ghost\n * Status colors: primary, secondary (neutral), danger, success\n * ============================================================ */\n\n/* primary — solid filled */\n.primary {\n background-color: var(--tempest-primary);\n --_tempest-button-fg: var(--tempest-primary-foreground);\n}\n.primary:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-primary-hover);\n}\n.primary:active:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-primary-active);\n}\n\n/* secondary — neutral surface */\n.secondary {\n background-color: var(--tempest-surface);\n --_tempest-button-fg: var(--tempest-text);\n border-color: var(--tempest-border);\n}\n.secondary:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-surface-2);\n border-color: var(--tempest-border-strong);\n}\n\n/* danger — solid red */\n.danger {\n background-color: var(--tempest-danger-solid);\n --_tempest-button-fg: var(--tempest-danger-on-solid);\n}\n.danger:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-danger-hover);\n}\n\n/* success — solid green */\n.success {\n background-color: var(--tempest-success-solid);\n --_tempest-button-fg: var(--tempest-success-on-solid);\n}\n.success:hover:not(:disabled, [aria-disabled=\"true\"]) {\n filter: brightness(0.92);\n}\n\n/* ghost — transparent, hover surface */\n.ghost {\n background-color: transparent;\n --_tempest-button-fg: var(--tempest-text);\n box-shadow: none;\n}\n.ghost:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-surface);\n box-shadow: none;\n}\n.ghost:active:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-surface-2);\n box-shadow: none;\n}\n\n/* soft — tinted background, primary text */\n.soft {\n background-color: var(--tempest-primary-soft);\n --_tempest-button-fg: var(--tempest-primary-on-soft);\n box-shadow: none;\n}\n.soft:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-primary-soft-hover);\n}\n\n/* outline — bordered, transparent fill */\n.outline {\n background-color: transparent;\n --_tempest-button-fg: var(--tempest-primary);\n border-color: var(--tempest-primary);\n box-shadow: none;\n}\n/*\n * The text colour has to move with the background.\n *\n * `.outline` sets its text to `var(--tempest-primary)` over a transparent background,\n * where it is fine. This rule tints the background to `--tempest-primary-soft` and,\n * without restating the colour, leaves the fill token sitting on the tint: **4.38:1 in\n * the light theme and 4.28:1 in dark**, both under the 4.5:1 floor for text — so the\n * label got harder to read at the exact moment the pointer was on it.\n *\n * `--tempest-primary-on-soft` is the token for this ground (6.18:1 / 8.72:1) and\n * `docs/styles.md` has documented the rule all along. A rule that changes only the\n * background of a coloured element is the shape of this bug; the other eight places in\n * the SDK that tint a background inherit `--tempest-text`, `--tempest-text-muted` or\n * `--tempest-primary-on-soft` and are fine.\n */\n.outline:hover:not(:disabled, [aria-disabled=\"true\"]) {\n background-color: var(--tempest-primary-soft);\n --_tempest-button-fg: var(--tempest-primary-on-soft);\n}\n\n/* link — text-only, underline on hover */\n.link {\n background-color: transparent;\n --_tempest-button-fg: var(--tempest-primary);\n box-shadow: none;\n padding-left: 0;\n padding-right: 0;\n border-radius: var(--tempest-radius-sm);\n}\n.link:hover:not(:disabled, [aria-disabled=\"true\"]) {\n --_tempest-button-fg: var(--tempest-primary-hover);\n text-decoration: underline;\n background: none;\n box-shadow: none;\n}\n\n/* ============================================================\n * Sizes — token-driven, density-aware\n * ============================================================ */\n.xs {\n height: var(--tempest-control-height-xs);\n padding: 0 var(--tempest-control-padding-xs);\n font-size: var(--tempest-control-font-xs);\n}\n.sm {\n height: var(--tempest-control-height-sm);\n padding: 0 var(--tempest-control-padding-sm);\n font-size: var(--tempest-control-font-sm);\n}\n.md {\n height: var(--tempest-control-height-md);\n padding: 0 var(--tempest-control-padding-md);\n font-size: var(--tempest-control-font-md);\n}\n.lg {\n height: var(--tempest-control-height-lg);\n padding: 0 var(--tempest-control-padding-lg);\n font-size: var(--tempest-control-font-lg);\n}\n.xl {\n height: var(--tempest-control-height-xl);\n padding: 0 var(--tempest-control-padding-xl);\n font-size: var(--tempest-control-font-xl);\n}\n\n/* Square (icon-only) — width matches height */\n.iconOnly {\n position: relative;\n}\n.iconOnly.xs {\n width: var(--tempest-control-height-xs);\n padding: 0;\n}\n.iconOnly.sm {\n width: var(--tempest-control-height-sm);\n padding: 0;\n}\n.iconOnly.md {\n width: var(--tempest-control-height-md);\n padding: 0;\n}\n.iconOnly.lg {\n width: var(--tempest-control-height-lg);\n padding: 0;\n}\n.iconOnly.xl {\n width: var(--tempest-control-height-xl);\n padding: 0;\n}\n\n/* Touch hit-slop — extend tap target to a min 44×44 on coarse pointers\n * for the smallest icon-only sizes, without changing visual footprint. */\n@media (pointer: coarse) {\n .iconOnly.xs::after,\n .iconOnly.sm::after {\n content: \"\";\n position: absolute;\n inset: -8px;\n }\n}\n\n/* Pill shape */\n.pill {\n border-radius: var(--tempest-radius-full);\n}\n\n.fullWidth {\n width: 100%;\n}\n\n/*\n * Lay out leftIcon + label + rightIcon as one centered, gapped row. Without\n * this the three are inline inside a single span (no gap, icon baseline-\n * aligned instead of centered), which reads as off-center — most visible on\n * fullWidth buttons where the label has room to drift.\n */\n.content {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-control-gap, var(--tempest-space-2));\n min-width: 0;\n}\n\n/*\n * Hide the label by paint only. `visibility: hidden` also drops it from the\n * accessibility tree, so a loading button lost its name (#416). `opacity: 0`\n * keeps the name and the width; `pointer-events: none` keeps the invisible\n * label out of hit-testing, as it was. Forced colors leaves `opacity` alone.\n */\n.loading .hiddenText {\n opacity: 0;\n pointer-events: none;\n}\n\n.spinner {\n position: absolute;\n animation: tempest-spin 0.8s linear infinite;\n}\n\n@keyframes tempest-spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n"],"mappings":";"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ButtonContent.cjs","names":[],"sources":["../../../src/components/Button/ButtonContent.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Button.module.css\";\n\n/** Props of {@link ButtonContent}. */\nexport interface ButtonContentProps {\n loading: boolean;\n leftIcon?: ReactNode;\n rightIcon?: ReactNode;\n children?: ReactNode;\n}\n\n/**\n * The inside of every button-looking element: the absolutely-positioned spinner\n * plus the icon/label row, shared by `Button` and `ButtonSlot`.\n *\n * @param props - See {@link ButtonContentProps}.\n * @returns The content nodes.\n */\nexport function ButtonContent({ loading, leftIcon, rightIcon, children }: ButtonContentProps) {\n return (\n <>\n {loading && (\n <span className={styles.spinner} aria-hidden>\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M21 12a9 9 0 1 1-6.219-8.56\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n </span>\n )}\n <span className={cn(styles.content, loading && styles.hiddenText)}>\n {leftIcon}\n {children}\n {rightIcon}\n </span>\n </>\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"ButtonContent.cjs","names":[],"sources":["../../../src/components/Button/ButtonContent.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Button.module.css\";\n\n/** Props of {@link ButtonContent}. */\nexport interface ButtonContentProps {\n loading: boolean;\n leftIcon?: ReactNode;\n rightIcon?: ReactNode;\n children?: ReactNode;\n}\n\n/**\n * The inside of every button-looking element: the absolutely-positioned spinner\n * plus the icon/label row, shared by `Button` and `ButtonSlot`.\n *\n * While loading, the row is hidden by paint only (`opacity: 0`), never with\n * `visibility: hidden` or `display: none`: those drop the label from the\n * accessibility tree and the button is announced with no name (#416). Painted\n * out, the label still sizes the button — its width does not move — and still\n * names it. The spinner is `aria-hidden`, so the name is the label alone and the\n * busy state comes from `aria-busy` on the host element.\n *\n * @param props - See {@link ButtonContentProps}.\n * @returns The content nodes.\n */\nexport function ButtonContent({ loading, leftIcon, rightIcon, children }: ButtonContentProps) {\n return (\n <>\n {loading && (\n <span className={styles.spinner} aria-hidden>\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M21 12a9 9 0 1 1-6.219-8.56\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n </span>\n )}\n <span className={cn(styles.content, loading && styles.hiddenText)}>\n {leftIcon}\n {children}\n {rightIcon}\n </span>\n </>\n );\n}\n"],"mappings":"0GA0BA,SAAgB,EAAc,CAAE,UAAS,WAAU,YAAW,YAAgC,CAC1F,OACI,EAAA,EAAA,KAAA,CAAA,EAAA,SAAA,CAAA,SAAA,CACK,IACG,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,QAAS,cAAA,GAC7B,UAAA,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,MAAM,KACN,OAAO,KACP,QAAQ,YACR,KAAK,OACL,MAAM,6BAEN,UAAA,EAAA,EAAA,IAAA,CAAC,OAAD,CACI,EAAE,8BACF,OAAO,eACP,YAAY,IACZ,cAAc,OACjB,CAAA,CACA,CAAA,CACH,CAAA,GAEV,EAAA,EAAA,KAAA,CAAC,OAAD,CAAM,UAAW,EAAA,GAAG,EAAA,QAAO,QAAS,GAAW,EAAA,QAAO,UAAU,EAAhE,SAAA,CACK,EACA,EACA,CACC,CACR,CAAA,CAAA,CAAA,CAAA,CAEV"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ButtonContent.js","names":[],"sources":["../../../src/components/Button/ButtonContent.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Button.module.css\";\n\n/** Props of {@link ButtonContent}. */\nexport interface ButtonContentProps {\n loading: boolean;\n leftIcon?: ReactNode;\n rightIcon?: ReactNode;\n children?: ReactNode;\n}\n\n/**\n * The inside of every button-looking element: the absolutely-positioned spinner\n * plus the icon/label row, shared by `Button` and `ButtonSlot`.\n *\n * @param props - See {@link ButtonContentProps}.\n * @returns The content nodes.\n */\nexport function ButtonContent({ loading, leftIcon, rightIcon, children }: ButtonContentProps) {\n return (\n <>\n {loading && (\n <span className={styles.spinner} aria-hidden>\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M21 12a9 9 0 1 1-6.219-8.56\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n </span>\n )}\n <span className={cn(styles.content, loading && styles.hiddenText)}>\n {leftIcon}\n {children}\n {rightIcon}\n </span>\n </>\n );\n}\n"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"ButtonContent.js","names":[],"sources":["../../../src/components/Button/ButtonContent.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Button.module.css\";\n\n/** Props of {@link ButtonContent}. */\nexport interface ButtonContentProps {\n loading: boolean;\n leftIcon?: ReactNode;\n rightIcon?: ReactNode;\n children?: ReactNode;\n}\n\n/**\n * The inside of every button-looking element: the absolutely-positioned spinner\n * plus the icon/label row, shared by `Button` and `ButtonSlot`.\n *\n * While loading, the row is hidden by paint only (`opacity: 0`), never with\n * `visibility: hidden` or `display: none`: those drop the label from the\n * accessibility tree and the button is announced with no name (#416). Painted\n * out, the label still sizes the button — its width does not move — and still\n * names it. The spinner is `aria-hidden`, so the name is the label alone and the\n * busy state comes from `aria-busy` on the host element.\n *\n * @param props - See {@link ButtonContentProps}.\n * @returns The content nodes.\n */\nexport function ButtonContent({ loading, leftIcon, rightIcon, children }: ButtonContentProps) {\n return (\n <>\n {loading && (\n <span className={styles.spinner} aria-hidden>\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M21 12a9 9 0 1 1-6.219-8.56\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n </span>\n )}\n <span className={cn(styles.content, loading && styles.hiddenText)}>\n {leftIcon}\n {children}\n {rightIcon}\n </span>\n </>\n );\n}\n"],"mappings":";;;;AA0BA,SAAgB,EAAc,EAAE,YAAS,aAAU,cAAW,eAAgC;CAC1F,OACI,kBAAA,GAAA,EAAA,UAAA,CACK,KACG,kBAAC,QAAD;EAAM,WAAW,EAAO;EAAS,eAAA;EAC7B,UAAA,kBAAC,OAAD;GACI,OAAM;GACN,QAAO;GACP,SAAQ;GACR,MAAK;GACL,OAAM;GAEN,UAAA,kBAAC,QAAD;IACI,GAAE;IACF,QAAO;IACP,aAAY;IACZ,eAAc;GACjB,CAAA;EACA,CAAA;CACH,CAAA,GAEV,kBAAC,QAAD;EAAM,WAAW,EAAG,EAAO,SAAS,KAAW,EAAO,UAAU;EAAhE,UAAA;GACK;GACA;GACA;EACC;CACR,CAAA,CAAA,EAAA,CAAA;AAEV"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Carousel.module.cjs","names":[],"sources":["../../../src/components/Carousel/Carousel.module.css"],"sourcesContent":[".root {\n position: relative;\n width: 100%;\n font-family: var(--tempest-font-sans);\n}\n\n.root:focus-visible {\n outline:
|
|
1
|
+
{"version":3,"file":"Carousel.module.cjs","names":[],"sources":["../../../src/components/Carousel/Carousel.module.css"],"sourcesContent":[".root {\n position: relative;\n width: 100%;\n font-family: var(--tempest-font-sans);\n}\n\n.root:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n.viewport {\n overflow: hidden;\n border-radius: var(--tempest-radius-lg);\n}\n\n.track {\n display: flex;\n transition: transform var(--tempest-duration-base) var(--tempest-ease-out);\n}\n\n.slide {\n flex: 0 0 100%;\n min-width: 0;\n}\n\n.arrow {\n position: absolute;\n top: 50%;\n transform: translateY(-50%);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-full);\n box-shadow: var(--tempest-shadow-md);\n color: var(--tempest-text);\n font-size: var(--tempest-text-xl);\n line-height: 1;\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .arrow:hover:not(:disabled) {\n background-color: var(--tempest-surface);\n }\n}\n\n.arrow:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n.arrow:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.prev {\n left: var(--tempest-space-2);\n}\n\n.next {\n right: var(--tempest-space-2);\n}\n\n.dots {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-space-2);\n margin-top: var(--tempest-space-3);\n}\n\n.dot {\n width: 0.5rem;\n height: 0.5rem;\n padding: 0;\n background-color: var(--tempest-border);\n border: none;\n border-radius: var(--tempest-radius-full);\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n.dotActive {\n background-color: var(--tempest-primary);\n}\n\n.dot:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .track {\n transition: none;\n }\n}\n"],"mappings":";"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Carousel.module.js","names":[],"sources":["../../../src/components/Carousel/Carousel.module.css"],"sourcesContent":[".root {\n position: relative;\n width: 100%;\n font-family: var(--tempest-font-sans);\n}\n\n.root:focus-visible {\n outline:
|
|
1
|
+
{"version":3,"file":"Carousel.module.js","names":[],"sources":["../../../src/components/Carousel/Carousel.module.css"],"sourcesContent":[".root {\n position: relative;\n width: 100%;\n font-family: var(--tempest-font-sans);\n}\n\n.root:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n.viewport {\n overflow: hidden;\n border-radius: var(--tempest-radius-lg);\n}\n\n.track {\n display: flex;\n transition: transform var(--tempest-duration-base) var(--tempest-ease-out);\n}\n\n.slide {\n flex: 0 0 100%;\n min-width: 0;\n}\n\n.arrow {\n position: absolute;\n top: 50%;\n transform: translateY(-50%);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-full);\n box-shadow: var(--tempest-shadow-md);\n color: var(--tempest-text);\n font-size: var(--tempest-text-xl);\n line-height: 1;\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .arrow:hover:not(:disabled) {\n background-color: var(--tempest-surface);\n }\n}\n\n.arrow:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n.arrow:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.prev {\n left: var(--tempest-space-2);\n}\n\n.next {\n right: var(--tempest-space-2);\n}\n\n.dots {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-space-2);\n margin-top: var(--tempest-space-3);\n}\n\n.dot {\n width: 0.5rem;\n height: 0.5rem;\n padding: 0;\n background-color: var(--tempest-border);\n border: none;\n border-radius: var(--tempest-radius-full);\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n.dotActive {\n background-color: var(--tempest-primary);\n}\n\n.dot:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .track {\n transition: none;\n }\n}\n"],"mappings":";"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Chat.module.cjs","names":[],"sources":["../../../src/components/Chat/Chat.module.css"],"sourcesContent":[".panel {\n display: flex;\n flex-direction: column;\n min-width: 0;\n min-height: 0;\n height: var(--tempest-chat-height, 100%);\n background-color: var(--tempest-bg);\n color: var(--tempest-text);\n font-family: var(--tempest-font-sans);\n}\n\n.header {\n flex: 0 0 auto;\n padding: var(--tempest-space-3) var(--tempest-space-4);\n border-bottom: 1px solid var(--tempest-border);\n}\n\n.thread {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n gap: var(--tempest-space-3);\n min-height: 0;\n padding: var(--tempest-space-4);\n overflow-y: auto;\n overscroll-behavior: contain;\n}\n\n.thread:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: -2px;\n}\n\n.day {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-3);\n}\n\n.day::before,\n.day::after {\n flex: 1 1 auto;\n height: 1px;\n background-color: var(--tempest-border);\n content: \"\";\n}\n\n.dayLabel {\n color: var(--tempest-text-subtle);\n font-size: var(--tempest-text-xs);\n text-transform: capitalize;\n}\n\n.run {\n display: flex;\n gap: var(--tempest-space-3);\n align-items: flex-start;\n max-width: 100%;\n}\n\n/*\n * Own messages mirror the row instead of moving to a second column: the avatar,\n * the name and the bubbles keep the same source order, so a screen reader still\n * reads \"who, then what\" while the eye reads it from the other side.\n */\n.ownRun {\n flex-direction: row-reverse;\n}\n\n.avatar {\n flex: 0 0 auto;\n}\n\n.runBody {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n min-width: 0;\n max-width: min(78%, 42rem);\n}\n\n.ownRun .runBody {\n align-items: flex-end;\n}\n\n.author {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n font-weight: var(--tempest-weight-medium);\n}\n\n.bubbles {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.bubbleRow {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n}\n\n.ownRun .bubbleRow {\n flex-direction: row-reverse;\n}\n\n.bubble {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n min-width: 0;\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n background-color: var(--tempest-surface);\n font-size: var(--tempest-text-sm);\n line-height: var(--tempest-leading-normal);\n}\n\n.ownBubble {\n border-color: transparent;\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n}\n\n/*\n * A failed message keeps its text readable and states the failure at the border\n * and in the meta row. Tinting the whole bubble danger-red would make the message\n * itself hard to read — and re-reading it is exactly what somebody does before\n * deciding to retry.\n */\n.failedBubble {\n border-color: var(--tempest-danger);\n background-color: var(--tempest-danger-bg);\n color: var(--tempest-text);\n}\n\n.body {\n overflow-wrap: anywhere;\n white-space: pre-wrap;\n}\n\n/*\n * The timestamp uses the muted token, not the subtle one, and switches to the\n * bubble's own foreground when the bubble is tinted.\n *\n * `--tempest-text-subtle` is solved against the page and surface backgrounds. On\n * `--tempest-primary-soft` it is a different composite, and the real browser\n * measured it below the 4.5:1 text floor — the same trap the syntax tokens hit.\n * Size is what de-emphasises the meta row here; colour still has to pass.\n */\n.meta {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: var(--tempest-space-2);\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-2xs);\n font-variant-numeric: tabular-nums;\n}\n\n.ownBubble .meta {\n color: var(--tempest-primary-on-soft);\n}\n\n.status {\n display: inline-flex;\n align-items: center;\n}\n\n.statusFailed {\n color: var(--tempest-danger);\n font-weight: var(--tempest-weight-bold);\n}\n\n/*\n * A bubble row carries more than a bubble now: the actions button sits beside it\n * and the reaction chips hang under it, so the row wraps and the chips are given\n * the whole width to fall onto.\n */\n.bubbleRow {\n flex-wrap: wrap;\n}\n\n.reactions {\n display: flex;\n flex-basis: 100%;\n flex-wrap: wrap;\n gap: var(--tempest-space-1);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.ownRun .reactions {\n justify-content: flex-end;\n}\n\n.reaction {\n display: inline-flex;\n align-items: center;\n gap: var(--tempest-space-1);\n padding: 0 var(--tempest-space-2);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-full, 999px);\n background-color: var(--tempest-surface);\n color: var(--tempest-text);\n font: inherit;\n font-size: var(--tempest-text-xs);\n line-height: 1.8;\n cursor: pointer;\n}\n\n.reaction:disabled {\n cursor: default;\n}\n\n/*\n * Your own reaction is marked by a tinted chip and its own foreground, never by\n * `--tempest-text-subtle` over the tint: that token is solved against the page\n * background and fails the text floor on a tinted surface.\n */\n.reactionMine {\n border-color: transparent;\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n font-weight: var(--tempest-weight-medium);\n}\n\n/*\n * The ⋮ button is always in the DOM — on touch there is no hover to reveal it,\n * and a control that only appears on hover is a control a phone does not have.\n * A fine pointer gets it faded until the row is hovered or it is focused.\n */\n.actionsButton {\n flex: 0 0 auto;\n padding: 0 var(--tempest-space-1);\n border: none;\n background: none;\n color: var(--tempest-text-muted);\n font: inherit;\n line-height: 1;\n cursor: pointer;\n}\n\n@media (hover: hover) and (pointer: fine) {\n .actionsButton {\n opacity: 0;\n transition: var(--tempest-transition-opacity, opacity 120ms ease);\n }\n\n .bubbleRow:hover .actionsButton,\n .actionsButton:focus-visible {\n opacity: 1;\n }\n}\n\n/* The reply stub: a tinted strip with a bar on the side it is anchored to. */\n.quote {\n display: flex;\n flex-direction: column;\n gap: 2px;\n width: 100%;\n padding: var(--tempest-space-1) var(--tempest-space-2);\n border-left: 3px solid var(--tempest-primary);\n border-radius: var(--tempest-radius-sm);\n background-color: var(--tempest-surface-2);\n color: var(--tempest-text);\n font-size: var(--tempest-text-xs);\n text-align: left;\n}\n\n.quoteButton {\n border-top: none;\n border-right: none;\n border-bottom: none;\n font: inherit;\n font-size: var(--tempest-text-xs);\n cursor: pointer;\n}\n\n/*\n * The author of a quote is text, so it is coloured like text.\n *\n * `--tempest-primary` here measured **4.35:1** against `--tempest-surface-2` in a\n * real browser — below the 4.5:1 floor for a 12px label, and invisible to the\n * jsdom axe sweep, which disables `color-contrast` because nothing is painted.\n * The brand still marks the quote: it is the bar down the side, which is a\n * non-text indicator and only owes 3:1.\n */\n.quoteAuthor {\n color: var(--tempest-text);\n font-weight: var(--tempest-weight-medium);\n}\n\n.quoteText {\n overflow: hidden;\n display: -webkit-box;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n line-clamp: 2;\n overflow-wrap: anywhere;\n}\n\n/*\n * A revoked quote reads as absent, which is the point: the text of a deleted\n * message must not survive inside the reply to it.\n */\n.quoteRevoked {\n color: var(--tempest-text-muted);\n font-style: italic;\n}\n\n.tombstone {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-1);\n color: var(--tempest-text-muted);\n font-style: italic;\n}\n\n.deletedBubble {\n border-style: dashed;\n background-color: transparent;\n}\n\n.edited {\n font-style: italic;\n}\n\n/* Read by everyone is the only state that turns the ticks. */\n.statusRead {\n color: var(--tempest-info, var(--tempest-primary));\n}\n\n.attachments {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n max-width: 320px;\n}\n\n/* Holds the row's height while the attachments chunk is still loading. */\n.attachmentsFallback {\n min-height: var(--tempest-space-8, 2rem);\n}\n\n.imageAttachment {\n padding: 0;\n border: none;\n border-radius: var(--tempest-radius-md);\n background: none;\n line-height: 0;\n cursor: pointer;\n}\n\n.imageAttachment img {\n max-width: 100%;\n max-height: 260px;\n border-radius: var(--tempest-radius-md);\n object-fit: cover;\n}\n\n.videoAttachment {\n max-width: 100%;\n border-radius: var(--tempest-radius-md);\n}\n\n.voiceAttachment {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n}\n\n/*\n * The waveform is the difference between a voice note and a grey rectangle: it\n * is what tells somebody whether this is a word or a two-minute monologue.\n */\n.waveform {\n display: flex;\n align-items: flex-end;\n gap: 2px;\n height: 28px;\n}\n\n.waveform span {\n flex: 1 1 auto;\n min-width: 2px;\n border-radius: 1px;\n background-color: var(--tempest-primary);\n opacity: 0.7;\n}\n\n.fileAttachment {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: var(--tempest-space-2);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-md);\n color: var(--tempest-text);\n text-decoration: none;\n}\n\n.fileName {\n overflow-wrap: anywhere;\n font-weight: var(--tempest-weight-medium);\n}\n\n.fileMeta {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-2xs);\n font-variant-numeric: tabular-nums;\n}\n\n.retry {\n padding: 0;\n border: none;\n background: none;\n color: var(--tempest-danger);\n font: inherit;\n font-size: var(--tempest-text-xs);\n text-decoration: underline;\n cursor: pointer;\n}\n\n.typing {\n flex: 0 0 auto;\n margin: 0;\n padding: 0 var(--tempest-space-4) var(--tempest-space-2);\n color: var(--tempest-text-subtle);\n font-size: var(--tempest-text-xs);\n}\n\n.composer {\n display: flex;\n flex: 0 0 auto;\n align-items: flex-end;\n gap: var(--tempest-space-2);\n padding: var(--tempest-space-3) var(--tempest-space-4);\n border-top: 1px solid var(--tempest-border);\n}\n\n.field {\n flex: 1 1 auto;\n min-width: 0;\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-md);\n background-color: var(--tempest-bg);\n color: inherit;\n font: inherit;\n font-size: var(--tempest-text-sm);\n line-height: var(--tempest-leading-normal);\n resize: none;\n}\n\n.field:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: -1px;\n}\n\n.field:disabled {\n background-color: var(--tempest-surface-2);\n color: var(--tempest-text-subtle);\n cursor: not-allowed;\n}\n\n.composerActions {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n}\n\n.send {\n padding: var(--tempest-space-2) var(--tempest-space-4);\n border: none;\n border-radius: var(--tempest-radius-md);\n background-color: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n font: inherit;\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-medium);\n cursor: pointer;\n}\n\n.send:disabled {\n background-color: var(--tempest-surface-3);\n color: var(--tempest-text-subtle);\n cursor: not-allowed;\n}\n\n@media (max-width: 480px) {\n .runBody {\n max-width: 88%;\n }\n}\n"],"mappings":";"}
|
|
1
|
+
{"version":3,"file":"Chat.module.cjs","names":[],"sources":["../../../src/components/Chat/Chat.module.css"],"sourcesContent":[".panel {\n display: flex;\n flex-direction: column;\n min-width: 0;\n min-height: 0;\n height: var(--tempest-chat-height, 100%);\n background-color: var(--tempest-bg);\n color: var(--tempest-text);\n font-family: var(--tempest-font-sans);\n}\n\n.header {\n flex: 0 0 auto;\n padding: var(--tempest-space-3) var(--tempest-space-4);\n border-bottom: 1px solid var(--tempest-border);\n}\n\n.thread {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n gap: var(--tempest-space-3);\n min-height: 0;\n padding: var(--tempest-space-4);\n overflow-y: auto;\n overscroll-behavior: contain;\n}\n\n.thread:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: calc(var(--tempest-focus-ring-width) * -1);\n}\n\n.day {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-3);\n}\n\n.day::before,\n.day::after {\n flex: 1 1 auto;\n height: 1px;\n background-color: var(--tempest-border);\n content: \"\";\n}\n\n.dayLabel {\n color: var(--tempest-text-subtle);\n font-size: var(--tempest-text-xs);\n text-transform: capitalize;\n}\n\n.run {\n display: flex;\n gap: var(--tempest-space-3);\n align-items: flex-start;\n max-width: 100%;\n}\n\n/*\n * Own messages mirror the row instead of moving to a second column: the avatar,\n * the name and the bubbles keep the same source order, so a screen reader still\n * reads \"who, then what\" while the eye reads it from the other side.\n */\n.ownRun {\n flex-direction: row-reverse;\n}\n\n.avatar {\n flex: 0 0 auto;\n}\n\n.runBody {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n min-width: 0;\n max-width: min(78%, 42rem);\n}\n\n.ownRun .runBody {\n align-items: flex-end;\n}\n\n.author {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n font-weight: var(--tempest-weight-medium);\n}\n\n.bubbles {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.bubbleRow {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n}\n\n.ownRun .bubbleRow {\n flex-direction: row-reverse;\n}\n\n.bubble {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n min-width: 0;\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n background-color: var(--tempest-surface);\n font-size: var(--tempest-text-sm);\n line-height: var(--tempest-leading-normal);\n}\n\n.ownBubble {\n border-color: transparent;\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n}\n\n/*\n * A failed message keeps its text readable and states the failure at the border\n * and in the meta row. Tinting the whole bubble danger-red would make the message\n * itself hard to read — and re-reading it is exactly what somebody does before\n * deciding to retry.\n */\n.failedBubble {\n border-color: var(--tempest-danger);\n background-color: var(--tempest-danger-bg);\n color: var(--tempest-text);\n}\n\n.body {\n overflow-wrap: anywhere;\n white-space: pre-wrap;\n}\n\n/*\n * The timestamp uses the muted token, not the subtle one, and switches to the\n * bubble's own foreground when the bubble is tinted.\n *\n * `--tempest-text-subtle` is solved against the page and surface backgrounds. On\n * `--tempest-primary-soft` it is a different composite, and the real browser\n * measured it below the 4.5:1 text floor — the same trap the syntax tokens hit.\n * Size is what de-emphasises the meta row here; colour still has to pass.\n */\n.meta {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: var(--tempest-space-2);\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-2xs);\n font-variant-numeric: tabular-nums;\n}\n\n.ownBubble .meta {\n color: var(--tempest-primary-on-soft);\n}\n\n.status {\n display: inline-flex;\n align-items: center;\n}\n\n.statusFailed {\n color: var(--tempest-danger);\n font-weight: var(--tempest-weight-bold);\n}\n\n/*\n * A bubble row carries more than a bubble now: the actions button sits beside it\n * and the reaction chips hang under it, so the row wraps and the chips are given\n * the whole width to fall onto.\n */\n.bubbleRow {\n flex-wrap: wrap;\n}\n\n.reactions {\n display: flex;\n flex-basis: 100%;\n flex-wrap: wrap;\n gap: var(--tempest-space-1);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.ownRun .reactions {\n justify-content: flex-end;\n}\n\n.reaction {\n display: inline-flex;\n align-items: center;\n gap: var(--tempest-space-1);\n padding: 0 var(--tempest-space-2);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-full, 999px);\n background-color: var(--tempest-surface);\n color: var(--tempest-text);\n font: inherit;\n font-size: var(--tempest-text-xs);\n line-height: 1.8;\n cursor: pointer;\n}\n\n.reaction:disabled {\n cursor: default;\n}\n\n/*\n * Your own reaction is marked by a tinted chip and its own foreground, never by\n * `--tempest-text-subtle` over the tint: that token is solved against the page\n * background and fails the text floor on a tinted surface.\n */\n.reactionMine {\n border-color: transparent;\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n font-weight: var(--tempest-weight-medium);\n}\n\n/*\n * The ⋮ button is always in the DOM — on touch there is no hover to reveal it,\n * and a control that only appears on hover is a control a phone does not have.\n * A fine pointer gets it faded until the row is hovered or it is focused.\n */\n.actionsButton {\n flex: 0 0 auto;\n padding: 0 var(--tempest-space-1);\n border: none;\n background: none;\n color: var(--tempest-text-muted);\n font: inherit;\n line-height: 1;\n cursor: pointer;\n}\n\n@media (hover: hover) and (pointer: fine) {\n .actionsButton {\n opacity: 0;\n transition: var(--tempest-transition-opacity, opacity 120ms ease);\n }\n\n .bubbleRow:hover .actionsButton,\n .actionsButton:focus-visible {\n opacity: 1;\n }\n}\n\n/* The reply stub: a tinted strip with a bar on the side it is anchored to. */\n.quote {\n display: flex;\n flex-direction: column;\n gap: 2px;\n width: 100%;\n padding: var(--tempest-space-1) var(--tempest-space-2);\n border-left: 3px solid var(--tempest-primary);\n border-radius: var(--tempest-radius-sm);\n background-color: var(--tempest-surface-2);\n color: var(--tempest-text);\n font-size: var(--tempest-text-xs);\n text-align: left;\n}\n\n.quoteButton {\n border-top: none;\n border-right: none;\n border-bottom: none;\n font: inherit;\n font-size: var(--tempest-text-xs);\n cursor: pointer;\n}\n\n/*\n * The author of a quote is text, so it is coloured like text.\n *\n * `--tempest-primary` here measured **4.35:1** against `--tempest-surface-2` in a\n * real browser — below the 4.5:1 floor for a 12px label, and invisible to the\n * jsdom axe sweep, which disables `color-contrast` because nothing is painted.\n * The brand still marks the quote: it is the bar down the side, which is a\n * non-text indicator and only owes 3:1.\n */\n.quoteAuthor {\n color: var(--tempest-text);\n font-weight: var(--tempest-weight-medium);\n}\n\n.quoteText {\n overflow: hidden;\n display: -webkit-box;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n line-clamp: 2;\n overflow-wrap: anywhere;\n}\n\n/*\n * A revoked quote reads as absent, which is the point: the text of a deleted\n * message must not survive inside the reply to it.\n */\n.quoteRevoked {\n color: var(--tempest-text-muted);\n font-style: italic;\n}\n\n.tombstone {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-1);\n color: var(--tempest-text-muted);\n font-style: italic;\n}\n\n.deletedBubble {\n border-style: dashed;\n background-color: transparent;\n}\n\n.edited {\n font-style: italic;\n}\n\n/* Read by everyone is the only state that turns the ticks. */\n.statusRead {\n color: var(--tempest-info, var(--tempest-primary));\n}\n\n.attachments {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n max-width: 320px;\n}\n\n/* Holds the row's height while the attachments chunk is still loading. */\n.attachmentsFallback {\n min-height: var(--tempest-space-8, 2rem);\n}\n\n.imageAttachment {\n padding: 0;\n border: none;\n border-radius: var(--tempest-radius-md);\n background: none;\n line-height: 0;\n cursor: pointer;\n}\n\n.imageAttachment img {\n max-width: 100%;\n max-height: 260px;\n border-radius: var(--tempest-radius-md);\n object-fit: cover;\n}\n\n.videoAttachment {\n max-width: 100%;\n border-radius: var(--tempest-radius-md);\n}\n\n.voiceAttachment {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n}\n\n/*\n * The waveform is the difference between a voice note and a grey rectangle: it\n * is what tells somebody whether this is a word or a two-minute monologue.\n */\n.waveform {\n display: flex;\n align-items: flex-end;\n gap: 2px;\n height: 28px;\n}\n\n.waveform span {\n flex: 1 1 auto;\n min-width: 2px;\n border-radius: 1px;\n background-color: var(--tempest-primary);\n opacity: 0.7;\n}\n\n.fileAttachment {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: var(--tempest-space-2);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-md);\n color: var(--tempest-text);\n text-decoration: none;\n}\n\n.fileName {\n overflow-wrap: anywhere;\n font-weight: var(--tempest-weight-medium);\n}\n\n.fileMeta {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-2xs);\n font-variant-numeric: tabular-nums;\n}\n\n.retry {\n padding: 0;\n border: none;\n background: none;\n color: var(--tempest-danger);\n font: inherit;\n font-size: var(--tempest-text-xs);\n text-decoration: underline;\n cursor: pointer;\n}\n\n.typing {\n flex: 0 0 auto;\n margin: 0;\n padding: 0 var(--tempest-space-4) var(--tempest-space-2);\n color: var(--tempest-text-subtle);\n font-size: var(--tempest-text-xs);\n}\n\n.composer {\n display: flex;\n flex: 0 0 auto;\n align-items: flex-end;\n gap: var(--tempest-space-2);\n padding: var(--tempest-space-3) var(--tempest-space-4);\n border-top: 1px solid var(--tempest-border);\n}\n\n.field {\n flex: 1 1 auto;\n min-width: 0;\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-md);\n background-color: var(--tempest-bg);\n color: inherit;\n font: inherit;\n font-size: var(--tempest-text-sm);\n line-height: var(--tempest-leading-normal);\n resize: none;\n}\n\n.field:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: calc(var(--tempest-focus-ring-width) * -1);\n}\n\n.field:disabled {\n background-color: var(--tempest-surface-2);\n color: var(--tempest-text-subtle);\n cursor: not-allowed;\n}\n\n.composerActions {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n}\n\n.send {\n padding: var(--tempest-space-2) var(--tempest-space-4);\n border: none;\n border-radius: var(--tempest-radius-md);\n background-color: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n font: inherit;\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-medium);\n cursor: pointer;\n}\n\n.send:disabled {\n background-color: var(--tempest-surface-3);\n color: var(--tempest-text-subtle);\n cursor: not-allowed;\n}\n\n@media (max-width: 480px) {\n .runBody {\n max-width: 88%;\n }\n}\n"],"mappings":";"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Chat.module.js","names":[],"sources":["../../../src/components/Chat/Chat.module.css"],"sourcesContent":[".panel {\n display: flex;\n flex-direction: column;\n min-width: 0;\n min-height: 0;\n height: var(--tempest-chat-height, 100%);\n background-color: var(--tempest-bg);\n color: var(--tempest-text);\n font-family: var(--tempest-font-sans);\n}\n\n.header {\n flex: 0 0 auto;\n padding: var(--tempest-space-3) var(--tempest-space-4);\n border-bottom: 1px solid var(--tempest-border);\n}\n\n.thread {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n gap: var(--tempest-space-3);\n min-height: 0;\n padding: var(--tempest-space-4);\n overflow-y: auto;\n overscroll-behavior: contain;\n}\n\n.thread:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: -2px;\n}\n\n.day {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-3);\n}\n\n.day::before,\n.day::after {\n flex: 1 1 auto;\n height: 1px;\n background-color: var(--tempest-border);\n content: \"\";\n}\n\n.dayLabel {\n color: var(--tempest-text-subtle);\n font-size: var(--tempest-text-xs);\n text-transform: capitalize;\n}\n\n.run {\n display: flex;\n gap: var(--tempest-space-3);\n align-items: flex-start;\n max-width: 100%;\n}\n\n/*\n * Own messages mirror the row instead of moving to a second column: the avatar,\n * the name and the bubbles keep the same source order, so a screen reader still\n * reads \"who, then what\" while the eye reads it from the other side.\n */\n.ownRun {\n flex-direction: row-reverse;\n}\n\n.avatar {\n flex: 0 0 auto;\n}\n\n.runBody {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n min-width: 0;\n max-width: min(78%, 42rem);\n}\n\n.ownRun .runBody {\n align-items: flex-end;\n}\n\n.author {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n font-weight: var(--tempest-weight-medium);\n}\n\n.bubbles {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.bubbleRow {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n}\n\n.ownRun .bubbleRow {\n flex-direction: row-reverse;\n}\n\n.bubble {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n min-width: 0;\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n background-color: var(--tempest-surface);\n font-size: var(--tempest-text-sm);\n line-height: var(--tempest-leading-normal);\n}\n\n.ownBubble {\n border-color: transparent;\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n}\n\n/*\n * A failed message keeps its text readable and states the failure at the border\n * and in the meta row. Tinting the whole bubble danger-red would make the message\n * itself hard to read — and re-reading it is exactly what somebody does before\n * deciding to retry.\n */\n.failedBubble {\n border-color: var(--tempest-danger);\n background-color: var(--tempest-danger-bg);\n color: var(--tempest-text);\n}\n\n.body {\n overflow-wrap: anywhere;\n white-space: pre-wrap;\n}\n\n/*\n * The timestamp uses the muted token, not the subtle one, and switches to the\n * bubble's own foreground when the bubble is tinted.\n *\n * `--tempest-text-subtle` is solved against the page and surface backgrounds. On\n * `--tempest-primary-soft` it is a different composite, and the real browser\n * measured it below the 4.5:1 text floor — the same trap the syntax tokens hit.\n * Size is what de-emphasises the meta row here; colour still has to pass.\n */\n.meta {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: var(--tempest-space-2);\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-2xs);\n font-variant-numeric: tabular-nums;\n}\n\n.ownBubble .meta {\n color: var(--tempest-primary-on-soft);\n}\n\n.status {\n display: inline-flex;\n align-items: center;\n}\n\n.statusFailed {\n color: var(--tempest-danger);\n font-weight: var(--tempest-weight-bold);\n}\n\n/*\n * A bubble row carries more than a bubble now: the actions button sits beside it\n * and the reaction chips hang under it, so the row wraps and the chips are given\n * the whole width to fall onto.\n */\n.bubbleRow {\n flex-wrap: wrap;\n}\n\n.reactions {\n display: flex;\n flex-basis: 100%;\n flex-wrap: wrap;\n gap: var(--tempest-space-1);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.ownRun .reactions {\n justify-content: flex-end;\n}\n\n.reaction {\n display: inline-flex;\n align-items: center;\n gap: var(--tempest-space-1);\n padding: 0 var(--tempest-space-2);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-full, 999px);\n background-color: var(--tempest-surface);\n color: var(--tempest-text);\n font: inherit;\n font-size: var(--tempest-text-xs);\n line-height: 1.8;\n cursor: pointer;\n}\n\n.reaction:disabled {\n cursor: default;\n}\n\n/*\n * Your own reaction is marked by a tinted chip and its own foreground, never by\n * `--tempest-text-subtle` over the tint: that token is solved against the page\n * background and fails the text floor on a tinted surface.\n */\n.reactionMine {\n border-color: transparent;\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n font-weight: var(--tempest-weight-medium);\n}\n\n/*\n * The ⋮ button is always in the DOM — on touch there is no hover to reveal it,\n * and a control that only appears on hover is a control a phone does not have.\n * A fine pointer gets it faded until the row is hovered or it is focused.\n */\n.actionsButton {\n flex: 0 0 auto;\n padding: 0 var(--tempest-space-1);\n border: none;\n background: none;\n color: var(--tempest-text-muted);\n font: inherit;\n line-height: 1;\n cursor: pointer;\n}\n\n@media (hover: hover) and (pointer: fine) {\n .actionsButton {\n opacity: 0;\n transition: var(--tempest-transition-opacity, opacity 120ms ease);\n }\n\n .bubbleRow:hover .actionsButton,\n .actionsButton:focus-visible {\n opacity: 1;\n }\n}\n\n/* The reply stub: a tinted strip with a bar on the side it is anchored to. */\n.quote {\n display: flex;\n flex-direction: column;\n gap: 2px;\n width: 100%;\n padding: var(--tempest-space-1) var(--tempest-space-2);\n border-left: 3px solid var(--tempest-primary);\n border-radius: var(--tempest-radius-sm);\n background-color: var(--tempest-surface-2);\n color: var(--tempest-text);\n font-size: var(--tempest-text-xs);\n text-align: left;\n}\n\n.quoteButton {\n border-top: none;\n border-right: none;\n border-bottom: none;\n font: inherit;\n font-size: var(--tempest-text-xs);\n cursor: pointer;\n}\n\n/*\n * The author of a quote is text, so it is coloured like text.\n *\n * `--tempest-primary` here measured **4.35:1** against `--tempest-surface-2` in a\n * real browser — below the 4.5:1 floor for a 12px label, and invisible to the\n * jsdom axe sweep, which disables `color-contrast` because nothing is painted.\n * The brand still marks the quote: it is the bar down the side, which is a\n * non-text indicator and only owes 3:1.\n */\n.quoteAuthor {\n color: var(--tempest-text);\n font-weight: var(--tempest-weight-medium);\n}\n\n.quoteText {\n overflow: hidden;\n display: -webkit-box;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n line-clamp: 2;\n overflow-wrap: anywhere;\n}\n\n/*\n * A revoked quote reads as absent, which is the point: the text of a deleted\n * message must not survive inside the reply to it.\n */\n.quoteRevoked {\n color: var(--tempest-text-muted);\n font-style: italic;\n}\n\n.tombstone {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-1);\n color: var(--tempest-text-muted);\n font-style: italic;\n}\n\n.deletedBubble {\n border-style: dashed;\n background-color: transparent;\n}\n\n.edited {\n font-style: italic;\n}\n\n/* Read by everyone is the only state that turns the ticks. */\n.statusRead {\n color: var(--tempest-info, var(--tempest-primary));\n}\n\n.attachments {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n max-width: 320px;\n}\n\n/* Holds the row's height while the attachments chunk is still loading. */\n.attachmentsFallback {\n min-height: var(--tempest-space-8, 2rem);\n}\n\n.imageAttachment {\n padding: 0;\n border: none;\n border-radius: var(--tempest-radius-md);\n background: none;\n line-height: 0;\n cursor: pointer;\n}\n\n.imageAttachment img {\n max-width: 100%;\n max-height: 260px;\n border-radius: var(--tempest-radius-md);\n object-fit: cover;\n}\n\n.videoAttachment {\n max-width: 100%;\n border-radius: var(--tempest-radius-md);\n}\n\n.voiceAttachment {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n}\n\n/*\n * The waveform is the difference between a voice note and a grey rectangle: it\n * is what tells somebody whether this is a word or a two-minute monologue.\n */\n.waveform {\n display: flex;\n align-items: flex-end;\n gap: 2px;\n height: 28px;\n}\n\n.waveform span {\n flex: 1 1 auto;\n min-width: 2px;\n border-radius: 1px;\n background-color: var(--tempest-primary);\n opacity: 0.7;\n}\n\n.fileAttachment {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: var(--tempest-space-2);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-md);\n color: var(--tempest-text);\n text-decoration: none;\n}\n\n.fileName {\n overflow-wrap: anywhere;\n font-weight: var(--tempest-weight-medium);\n}\n\n.fileMeta {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-2xs);\n font-variant-numeric: tabular-nums;\n}\n\n.retry {\n padding: 0;\n border: none;\n background: none;\n color: var(--tempest-danger);\n font: inherit;\n font-size: var(--tempest-text-xs);\n text-decoration: underline;\n cursor: pointer;\n}\n\n.typing {\n flex: 0 0 auto;\n margin: 0;\n padding: 0 var(--tempest-space-4) var(--tempest-space-2);\n color: var(--tempest-text-subtle);\n font-size: var(--tempest-text-xs);\n}\n\n.composer {\n display: flex;\n flex: 0 0 auto;\n align-items: flex-end;\n gap: var(--tempest-space-2);\n padding: var(--tempest-space-3) var(--tempest-space-4);\n border-top: 1px solid var(--tempest-border);\n}\n\n.field {\n flex: 1 1 auto;\n min-width: 0;\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-md);\n background-color: var(--tempest-bg);\n color: inherit;\n font: inherit;\n font-size: var(--tempest-text-sm);\n line-height: var(--tempest-leading-normal);\n resize: none;\n}\n\n.field:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: -1px;\n}\n\n.field:disabled {\n background-color: var(--tempest-surface-2);\n color: var(--tempest-text-subtle);\n cursor: not-allowed;\n}\n\n.composerActions {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n}\n\n.send {\n padding: var(--tempest-space-2) var(--tempest-space-4);\n border: none;\n border-radius: var(--tempest-radius-md);\n background-color: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n font: inherit;\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-medium);\n cursor: pointer;\n}\n\n.send:disabled {\n background-color: var(--tempest-surface-3);\n color: var(--tempest-text-subtle);\n cursor: not-allowed;\n}\n\n@media (max-width: 480px) {\n .runBody {\n max-width: 88%;\n }\n}\n"],"mappings":";"}
|
|
1
|
+
{"version":3,"file":"Chat.module.js","names":[],"sources":["../../../src/components/Chat/Chat.module.css"],"sourcesContent":[".panel {\n display: flex;\n flex-direction: column;\n min-width: 0;\n min-height: 0;\n height: var(--tempest-chat-height, 100%);\n background-color: var(--tempest-bg);\n color: var(--tempest-text);\n font-family: var(--tempest-font-sans);\n}\n\n.header {\n flex: 0 0 auto;\n padding: var(--tempest-space-3) var(--tempest-space-4);\n border-bottom: 1px solid var(--tempest-border);\n}\n\n.thread {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n gap: var(--tempest-space-3);\n min-height: 0;\n padding: var(--tempest-space-4);\n overflow-y: auto;\n overscroll-behavior: contain;\n}\n\n.thread:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: calc(var(--tempest-focus-ring-width) * -1);\n}\n\n.day {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-3);\n}\n\n.day::before,\n.day::after {\n flex: 1 1 auto;\n height: 1px;\n background-color: var(--tempest-border);\n content: \"\";\n}\n\n.dayLabel {\n color: var(--tempest-text-subtle);\n font-size: var(--tempest-text-xs);\n text-transform: capitalize;\n}\n\n.run {\n display: flex;\n gap: var(--tempest-space-3);\n align-items: flex-start;\n max-width: 100%;\n}\n\n/*\n * Own messages mirror the row instead of moving to a second column: the avatar,\n * the name and the bubbles keep the same source order, so a screen reader still\n * reads \"who, then what\" while the eye reads it from the other side.\n */\n.ownRun {\n flex-direction: row-reverse;\n}\n\n.avatar {\n flex: 0 0 auto;\n}\n\n.runBody {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n min-width: 0;\n max-width: min(78%, 42rem);\n}\n\n.ownRun .runBody {\n align-items: flex-end;\n}\n\n.author {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n font-weight: var(--tempest-weight-medium);\n}\n\n.bubbles {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.bubbleRow {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n}\n\n.ownRun .bubbleRow {\n flex-direction: row-reverse;\n}\n\n.bubble {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n min-width: 0;\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n background-color: var(--tempest-surface);\n font-size: var(--tempest-text-sm);\n line-height: var(--tempest-leading-normal);\n}\n\n.ownBubble {\n border-color: transparent;\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n}\n\n/*\n * A failed message keeps its text readable and states the failure at the border\n * and in the meta row. Tinting the whole bubble danger-red would make the message\n * itself hard to read — and re-reading it is exactly what somebody does before\n * deciding to retry.\n */\n.failedBubble {\n border-color: var(--tempest-danger);\n background-color: var(--tempest-danger-bg);\n color: var(--tempest-text);\n}\n\n.body {\n overflow-wrap: anywhere;\n white-space: pre-wrap;\n}\n\n/*\n * The timestamp uses the muted token, not the subtle one, and switches to the\n * bubble's own foreground when the bubble is tinted.\n *\n * `--tempest-text-subtle` is solved against the page and surface backgrounds. On\n * `--tempest-primary-soft` it is a different composite, and the real browser\n * measured it below the 4.5:1 text floor — the same trap the syntax tokens hit.\n * Size is what de-emphasises the meta row here; colour still has to pass.\n */\n.meta {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: var(--tempest-space-2);\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-2xs);\n font-variant-numeric: tabular-nums;\n}\n\n.ownBubble .meta {\n color: var(--tempest-primary-on-soft);\n}\n\n.status {\n display: inline-flex;\n align-items: center;\n}\n\n.statusFailed {\n color: var(--tempest-danger);\n font-weight: var(--tempest-weight-bold);\n}\n\n/*\n * A bubble row carries more than a bubble now: the actions button sits beside it\n * and the reaction chips hang under it, so the row wraps and the chips are given\n * the whole width to fall onto.\n */\n.bubbleRow {\n flex-wrap: wrap;\n}\n\n.reactions {\n display: flex;\n flex-basis: 100%;\n flex-wrap: wrap;\n gap: var(--tempest-space-1);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.ownRun .reactions {\n justify-content: flex-end;\n}\n\n.reaction {\n display: inline-flex;\n align-items: center;\n gap: var(--tempest-space-1);\n padding: 0 var(--tempest-space-2);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-full, 999px);\n background-color: var(--tempest-surface);\n color: var(--tempest-text);\n font: inherit;\n font-size: var(--tempest-text-xs);\n line-height: 1.8;\n cursor: pointer;\n}\n\n.reaction:disabled {\n cursor: default;\n}\n\n/*\n * Your own reaction is marked by a tinted chip and its own foreground, never by\n * `--tempest-text-subtle` over the tint: that token is solved against the page\n * background and fails the text floor on a tinted surface.\n */\n.reactionMine {\n border-color: transparent;\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n font-weight: var(--tempest-weight-medium);\n}\n\n/*\n * The ⋮ button is always in the DOM — on touch there is no hover to reveal it,\n * and a control that only appears on hover is a control a phone does not have.\n * A fine pointer gets it faded until the row is hovered or it is focused.\n */\n.actionsButton {\n flex: 0 0 auto;\n padding: 0 var(--tempest-space-1);\n border: none;\n background: none;\n color: var(--tempest-text-muted);\n font: inherit;\n line-height: 1;\n cursor: pointer;\n}\n\n@media (hover: hover) and (pointer: fine) {\n .actionsButton {\n opacity: 0;\n transition: var(--tempest-transition-opacity, opacity 120ms ease);\n }\n\n .bubbleRow:hover .actionsButton,\n .actionsButton:focus-visible {\n opacity: 1;\n }\n}\n\n/* The reply stub: a tinted strip with a bar on the side it is anchored to. */\n.quote {\n display: flex;\n flex-direction: column;\n gap: 2px;\n width: 100%;\n padding: var(--tempest-space-1) var(--tempest-space-2);\n border-left: 3px solid var(--tempest-primary);\n border-radius: var(--tempest-radius-sm);\n background-color: var(--tempest-surface-2);\n color: var(--tempest-text);\n font-size: var(--tempest-text-xs);\n text-align: left;\n}\n\n.quoteButton {\n border-top: none;\n border-right: none;\n border-bottom: none;\n font: inherit;\n font-size: var(--tempest-text-xs);\n cursor: pointer;\n}\n\n/*\n * The author of a quote is text, so it is coloured like text.\n *\n * `--tempest-primary` here measured **4.35:1** against `--tempest-surface-2` in a\n * real browser — below the 4.5:1 floor for a 12px label, and invisible to the\n * jsdom axe sweep, which disables `color-contrast` because nothing is painted.\n * The brand still marks the quote: it is the bar down the side, which is a\n * non-text indicator and only owes 3:1.\n */\n.quoteAuthor {\n color: var(--tempest-text);\n font-weight: var(--tempest-weight-medium);\n}\n\n.quoteText {\n overflow: hidden;\n display: -webkit-box;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n line-clamp: 2;\n overflow-wrap: anywhere;\n}\n\n/*\n * A revoked quote reads as absent, which is the point: the text of a deleted\n * message must not survive inside the reply to it.\n */\n.quoteRevoked {\n color: var(--tempest-text-muted);\n font-style: italic;\n}\n\n.tombstone {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-1);\n color: var(--tempest-text-muted);\n font-style: italic;\n}\n\n.deletedBubble {\n border-style: dashed;\n background-color: transparent;\n}\n\n.edited {\n font-style: italic;\n}\n\n/* Read by everyone is the only state that turns the ticks. */\n.statusRead {\n color: var(--tempest-info, var(--tempest-primary));\n}\n\n.attachments {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n max-width: 320px;\n}\n\n/* Holds the row's height while the attachments chunk is still loading. */\n.attachmentsFallback {\n min-height: var(--tempest-space-8, 2rem);\n}\n\n.imageAttachment {\n padding: 0;\n border: none;\n border-radius: var(--tempest-radius-md);\n background: none;\n line-height: 0;\n cursor: pointer;\n}\n\n.imageAttachment img {\n max-width: 100%;\n max-height: 260px;\n border-radius: var(--tempest-radius-md);\n object-fit: cover;\n}\n\n.videoAttachment {\n max-width: 100%;\n border-radius: var(--tempest-radius-md);\n}\n\n.voiceAttachment {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n}\n\n/*\n * The waveform is the difference between a voice note and a grey rectangle: it\n * is what tells somebody whether this is a word or a two-minute monologue.\n */\n.waveform {\n display: flex;\n align-items: flex-end;\n gap: 2px;\n height: 28px;\n}\n\n.waveform span {\n flex: 1 1 auto;\n min-width: 2px;\n border-radius: 1px;\n background-color: var(--tempest-primary);\n opacity: 0.7;\n}\n\n.fileAttachment {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: var(--tempest-space-2);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-md);\n color: var(--tempest-text);\n text-decoration: none;\n}\n\n.fileName {\n overflow-wrap: anywhere;\n font-weight: var(--tempest-weight-medium);\n}\n\n.fileMeta {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-2xs);\n font-variant-numeric: tabular-nums;\n}\n\n.retry {\n padding: 0;\n border: none;\n background: none;\n color: var(--tempest-danger);\n font: inherit;\n font-size: var(--tempest-text-xs);\n text-decoration: underline;\n cursor: pointer;\n}\n\n.typing {\n flex: 0 0 auto;\n margin: 0;\n padding: 0 var(--tempest-space-4) var(--tempest-space-2);\n color: var(--tempest-text-subtle);\n font-size: var(--tempest-text-xs);\n}\n\n.composer {\n display: flex;\n flex: 0 0 auto;\n align-items: flex-end;\n gap: var(--tempest-space-2);\n padding: var(--tempest-space-3) var(--tempest-space-4);\n border-top: 1px solid var(--tempest-border);\n}\n\n.field {\n flex: 1 1 auto;\n min-width: 0;\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-md);\n background-color: var(--tempest-bg);\n color: inherit;\n font: inherit;\n font-size: var(--tempest-text-sm);\n line-height: var(--tempest-leading-normal);\n resize: none;\n}\n\n.field:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: calc(var(--tempest-focus-ring-width) * -1);\n}\n\n.field:disabled {\n background-color: var(--tempest-surface-2);\n color: var(--tempest-text-subtle);\n cursor: not-allowed;\n}\n\n.composerActions {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n}\n\n.send {\n padding: var(--tempest-space-2) var(--tempest-space-4);\n border: none;\n border-radius: var(--tempest-radius-md);\n background-color: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n font: inherit;\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-medium);\n cursor: pointer;\n}\n\n.send:disabled {\n background-color: var(--tempest-surface-3);\n color: var(--tempest-text-subtle);\n cursor: not-allowed;\n}\n\n@media (max-width: 480px) {\n .runBody {\n max-width: 88%;\n }\n}\n"],"mappings":";"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../../utils/cn.cjs"),t=require("../Portal/Portal.cjs"),n=require("../Portal/anchor-position.cjs"),r=require("../Select/CaretIcon.cjs"),i=require("./Combobox.module.cjs");let
|
|
1
|
+
const e=require("../../utils/cn.cjs"),t=require("../Portal/Portal.cjs"),n=require("../Portal/anchor-position.cjs"),r=require("../Select/CaretIcon.cjs"),i=require("./Combobox.module.cjs"),a=require("./use-listbox-dismiss.cjs");let o=require("react"),s=require("react/jsx-runtime");var c=4;function l(e,t){return!t||e.label.toLowerCase().includes(t.toLowerCase())}function u({options:u,value:d,onChange:f,label:p,placeholder:m=`Selecione`,helperText:h,error:g,disabled:_,filter:v=l,emptyMessage:y=`Nenhuma opção encontrada`,portal:b=!0,className:x}){let S=(0,o.useId)(),C=(0,o.useRef)(null),w=(0,o.useRef)(null),[T,E]=(0,o.useState)(!1),[D,O]=(0,o.useState)(``),[k,A]=(0,o.useState)(0),[j,M]=(0,o.useState)(null),[N,P]=(0,o.useState)(null),F=n.useAnchorPosition({anchor:j,floating:N,side:`bottom`,align:`start`,offset:c,enabled:b&&T,matchWidth:!0}),I=(0,o.useMemo)(()=>u.find(e=>e.value===d)?.label??``,[u,d]),L=(0,o.useMemo)(()=>u.filter(e=>v(e,D)),[u,v,D]),R=(0,o.useCallback)(()=>{E(!1),O(``),A(0)},[]),z=(0,o.useCallback)(e=>{e.disabled||(f(e.value),R())},[f,R]),{onInputBlur:B,onListMouseDown:V}=a.useListboxDismiss({open:T,rootRef:C,listNode:N,onDismiss:R}),H=e=>`${S}-option-${e}`,U=T?L[k]:void 0,W=e=>{if(e.key===`ArrowDown`)e.preventDefault(),E(!0),A(e=>Math.min(L.length-1,e+1));else if(e.key===`ArrowUp`)e.preventDefault(),A(e=>Math.max(0,e-1));else if(e.key===`Enter`){e.preventDefault();let t=L[k];t&&z(t)}else e.key===`Escape`&&(T&&e.preventDefault(),R())},G=(0,s.jsx)(`ul`,{ref:P,id:`${S}-listbox`,role:`listbox`,className:e.cn(i.default.menu,b&&i.default.portalled),style:F,onMouseDown:V,children:L.length===0?(0,s.jsx)(`li`,{className:i.default.empty,children:y}):L.map((t,n)=>(0,s.jsx)(`li`,{id:H(n),role:`option`,"aria-selected":t.value===d,className:e.cn(i.default.option,n===k&&i.default.active,t.value===d&&i.default.selected),onMouseEnter:()=>A(n),onMouseDown:()=>z(t),children:t.label},t.value))});return(0,s.jsxs)(`div`,{ref:C,className:e.cn(i.default.wrapper,g&&i.default.error,x),children:[p&&(0,s.jsx)(`label`,{htmlFor:S,className:i.default.label,children:p}),(0,s.jsxs)(`div`,{ref:M,className:i.default.field,children:[(0,s.jsx)(`input`,{id:S,ref:w,type:`text`,role:`combobox`,"aria-expanded":T,"aria-controls":`${S}-listbox`,"aria-autocomplete":`list`,"aria-activedescendant":U?H(k):void 0,className:i.default.input,placeholder:m,disabled:_,value:T?D:I,onFocus:()=>E(!0),onChange:e=>{O(e.target.value),E(!0),A(0)},onKeyDown:W,onBlur:B}),(0,s.jsx)(`span`,{className:i.default.caret,"aria-hidden":!0,children:(0,s.jsx)(r.CaretIcon,{})})]}),T&&(b?(0,s.jsx)(t.Portal,{children:G}):G),g?(0,s.jsx)(`span`,{className:i.default.errorText,children:g}):h?(0,s.jsx)(`span`,{className:i.default.helper,children:h}):null]})}exports.Combobox=u;
|
|
2
2
|
//# sourceMappingURL=Combobox.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Combobox.cjs","names":[],"sources":["../../../src/components/Combobox/Combobox.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — options/value/onChange\n * plus the shared field contract (label, placeholder, helperText, error, disabled,\n * className) and the combobox-specific knobs: filter, emptyMessage, portal. The body\n * is the listbox — open state, active descendant, type-ahead and the ARIA wiring —\n * and those cannot be separated without passing the same active index back and\n * forth.\n */\nimport { useCallback, useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport { Portal } from \"@/components/Portal\";\nimport { useAnchorPosition } from \"@/components/Portal/anchor-position\";\nimport { CaretIcon } from \"@/components/Select/CaretIcon\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Combobox.module.css\";\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport interface ComboboxProps {\n options: ComboboxOption[];\n value: string;\n onChange: (value: string) => void;\n label?: string;\n placeholder?: string;\n helperText?: string;\n error?: string;\n disabled?: boolean;\n /** Custom filter — return true to keep the option. Default is case-insensitive substring match on label. */\n filter?: (option: ComboboxOption, query: string) => boolean;\n /** Message shown when no option matches. */\n emptyMessage?: string;\n /**\n * Render the list in a portal, positioned under the field. Default `true`.\n *\n * In flow, an ancestor with `overflow` other than `visible` clips the list —\n * in the last row of a `DataTable` none of it was visible (measured in Chrome\n * at 1440 px). In a portal it escapes the clip, takes the field's width,\n * flips above the field when there is no room below, and follows it on\n * scroll, resize and when the field or the list changes size.\n */\n portal?: boolean;\n className?: string;\n}\n\n/** Gap between field and list, in px — the in-flow CSS uses the same 4 px. */\nconst LIST_OFFSET = 4;\n\nfunction defaultFilter(option: ComboboxOption, query: string): boolean {\n if (!query) return true;\n return option.label.toLowerCase().includes(query.toLowerCase());\n}\n\n/**\n * Combobox — text input with a filterable dropdown of options.\n *\n * Selecting an option fires `onChange(value)`. Typing filters the list.\n * Keyboard: ArrowUp/ArrowDown to navigate, Enter to select, Esc to close.\n */\nexport function Combobox({\n options,\n value,\n onChange,\n label,\n placeholder = \"Selecione\",\n helperText,\n error,\n disabled,\n filter = defaultFilter,\n emptyMessage = \"Nenhuma opção encontrada\",\n portal = true,\n className,\n}: ComboboxProps) {\n const id = useId();\n const rootRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState(\"\");\n const [activeIndex, setActiveIndex] = useState(0);\n const [fieldNode, setFieldNode] = useState<HTMLDivElement | null>(null);\n const [listNode, setListNode] = useState<HTMLUListElement | null>(null);\n const floatingStyle = useAnchorPosition({\n anchor: fieldNode,\n floating: listNode,\n side: \"bottom\",\n align: \"start\",\n offset: LIST_OFFSET,\n enabled: portal && open,\n matchWidth: true,\n });\n\n const selectedLabel = useMemo(\n () => options.find((o) => o.value === value)?.label ?? \"\",\n [options, value],\n );\n\n const filtered = useMemo(\n () => options.filter((o) => filter(o, query)),\n [options, filter, query],\n );\n\n const closeAndReset = useCallback((): void => {\n setOpen(false);\n setQuery(\"\");\n setActiveIndex(0);\n }, []);\n\n const handleSelect = useCallback(\n (option: ComboboxOption): void => {\n if (option.disabled) return;\n onChange(option.value);\n closeAndReset();\n },\n [onChange, closeAndReset],\n );\n\n /**\n * Close on a press outside both the field and the list.\n *\n * The list is checked on its own because in a portal it is not inside the\n * field's wrapper: a press on its scrollbar, or on the empty message, would\n * otherwise count as outside and close it.\n */\n useEffect(() => {\n if (!open) return;\n const onDown = (event: MouseEvent): void => {\n const target = event.target as Node;\n if (rootRef.current?.contains(target) || listNode?.contains(target)) return;\n closeAndReset();\n };\n window.addEventListener(\"mousedown\", onDown);\n return () => window.removeEventListener(\"mousedown\", onDown);\n }, [open, closeAndReset, listNode]);\n\n /**\n * Keyboard model of the input.\n *\n * `Escape` calls `preventDefault()` only while the list is open: that marks the\n * key as consumed, so an enclosing `Modal` or `Drawer` stays open and the next\n * `Escape` reaches it. With the list closed the key is left alone and dismisses\n * the enclosing layer, as the user expects.\n */\n const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n setOpen(true);\n setActiveIndex((current) => Math.min(filtered.length - 1, current + 1));\n } else if (event.key === \"ArrowUp\") {\n event.preventDefault();\n setActiveIndex((current) => Math.max(0, current - 1));\n } else if (event.key === \"Enter\") {\n event.preventDefault();\n const option = filtered[activeIndex];\n if (option) handleSelect(option);\n } else if (event.key === \"Escape\") {\n if (open) event.preventDefault();\n closeAndReset();\n }\n };\n\n const list = (\n <ul\n ref={setListNode}\n id={`${id}-listbox`}\n role=\"listbox\"\n className={cn(styles.menu, portal && styles.portalled)}\n style={floatingStyle}\n >\n {filtered.length === 0 ? (\n <li className={styles.empty}>{emptyMessage}</li>\n ) : (\n filtered.map((option, index) => (\n <li\n key={option.value}\n role=\"option\"\n aria-selected={option.value === value}\n className={cn(\n styles.option,\n index === activeIndex && styles.active,\n option.value === value && styles.selected,\n )}\n onMouseEnter={() => setActiveIndex(index)}\n onMouseDown={(event) => {\n event.preventDefault();\n handleSelect(option);\n }}\n >\n {option.label}\n </li>\n ))\n )}\n </ul>\n );\n\n return (\n <div ref={rootRef} className={cn(styles.wrapper, error && styles.error, className)}>\n {label && (\n <label htmlFor={id} className={styles.label}>\n {label}\n </label>\n )}\n <div ref={setFieldNode} className={styles.field}>\n <input\n id={id}\n ref={inputRef}\n type=\"text\"\n role=\"combobox\"\n aria-expanded={open}\n aria-controls={`${id}-listbox`}\n aria-autocomplete=\"list\"\n className={styles.input}\n placeholder={placeholder}\n disabled={disabled}\n value={open ? query : selectedLabel}\n onFocus={() => setOpen(true)}\n onChange={(event) => {\n setQuery(event.target.value);\n setOpen(true);\n setActiveIndex(0);\n }}\n onKeyDown={handleKeyDown}\n />\n <span className={styles.caret} aria-hidden>\n <CaretIcon />\n </span>\n </div>\n {open && (portal ? <Portal>{list}</Portal> : list)}\n {error ? (\n <span className={styles.errorText}>{error}</span>\n ) : helperText ? (\n <span className={styles.helper}>{helperText}</span>\n ) : null}\n </div>\n );\n}\n"],"mappings":"iPAgDA,IAAM,EAAc,EAEpB,SAAS,EAAc,EAAwB,EAAwB,CAEnE,MADA,CAAK,GACE,EAAO,MAAM,YAAY,CAAC,CAAC,SAAS,EAAM,YAAY,CAAC,CAClE,CAQA,SAAgB,EAAS,CACrB,UACA,QACA,WACA,QACA,cAAc,YACd,aACA,QACA,WACA,SAAS,EACT,eAAe,2BACf,SAAS,GACT,aACc,CACd,IAAM,GAAA,EAAK,EAAA,MAAA,CAAM,EACX,GAAA,EAAU,EAAA,OAAA,CAAuB,IAAI,EACrC,GAAA,EAAW,EAAA,OAAA,CAAyB,IAAI,EACxC,CAAC,EAAM,IAAA,EAAW,EAAA,SAAA,CAAS,EAAK,EAChC,CAAC,EAAO,IAAA,EAAY,EAAA,SAAA,CAAS,EAAE,EAC/B,CAAC,EAAa,IAAA,EAAkB,EAAA,SAAA,CAAS,CAAC,EAC1C,CAAC,EAAW,IAAA,EAAgB,EAAA,SAAA,CAAgC,IAAI,EAChE,CAAC,EAAU,IAAA,EAAe,EAAA,SAAA,CAAkC,IAAI,EAChE,EAAgB,EAAA,kBAAkB,CACpC,OAAQ,EACR,SAAU,EACV,KAAM,SACN,MAAO,QACP,OAAQ,EACR,QAAS,GAAU,EACnB,WAAY,EAChB,CAAC,EAEK,GAAA,EAAgB,EAAA,QAAA,KACZ,EAAQ,KAAM,GAAM,EAAE,QAAU,CAAK,CAAC,EAAE,OAAS,GACvD,CAAC,EAAS,CAAK,CACnB,EAEM,GAAA,EAAW,EAAA,QAAA,KACP,EAAQ,OAAQ,GAAM,EAAO,EAAG,CAAK,CAAC,EAC5C,CAAC,EAAS,EAAQ,CAAK,CAC3B,EAEM,GAAA,EAAgB,EAAA,YAAA,KAAwB,CAC1C,EAAQ,EAAK,EACb,EAAS,EAAE,EACX,EAAe,CAAC,CACpB,EAAG,CAAC,CAAC,EAEC,GAAA,EAAe,EAAA,YAAA,CAChB,GAAiC,CAC1B,EAAO,WACX,EAAS,EAAO,KAAK,EACrB,EAAc,EAClB,EACA,CAAC,EAAU,CAAa,CAC5B,GASA,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,EAAM,OACX,IAAM,EAAU,GAA4B,CACxC,IAAM,EAAS,EAAM,OACjB,EAAQ,SAAS,SAAS,CAAM,GAAK,GAAU,SAAS,CAAM,GAClE,EAAc,CAClB,EAEA,OADA,OAAO,iBAAiB,YAAa,CAAM,MAC9B,OAAO,oBAAoB,YAAa,CAAM,CAC/D,EAAG,CAAC,EAAM,EAAe,CAAQ,CAAC,EAUlC,IAAM,EAAiB,GAAuD,CAC1E,GAAI,EAAM,MAAQ,YACd,EAAM,eAAe,EACrB,EAAQ,EAAI,EACZ,EAAgB,GAAY,KAAK,IAAI,EAAS,OAAS,EAAG,EAAU,CAAC,CAAC,OACnE,GAAI,EAAM,MAAQ,UACrB,EAAM,eAAe,EACrB,EAAgB,GAAY,KAAK,IAAI,EAAG,EAAU,CAAC,CAAC,OACjD,GAAI,EAAM,MAAQ,QAAS,CAC9B,EAAM,eAAe,EACrB,IAAM,EAAS,EAAS,GACpB,GAAQ,EAAa,CAAM,CACnC,MAAW,EAAM,MAAQ,WACjB,GAAM,EAAM,eAAe,EAC/B,EAAc,EAEtB,EAEM,GACF,EAAA,EAAA,IAAA,CAAC,KAAD,CACI,IAAK,EACL,GAAI,GAAG,EAAG,UACV,KAAK,UACL,UAAW,EAAA,GAAG,EAAA,QAAO,KAAM,GAAU,EAAA,QAAO,SAAS,EACrD,MAAO,EAEN,SAAA,EAAS,SAAW,GACjB,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAW,EAAA,QAAO,MAAQ,SAAA,CAAiB,CAAA,EAE/C,EAAS,KAAK,EAAQ,KAClB,EAAA,EAAA,IAAA,CAAC,KAAD,CAEI,KAAK,SACL,gBAAe,EAAO,QAAU,EAChC,UAAW,EAAA,GACP,EAAA,QAAO,OACP,IAAU,GAAe,EAAA,QAAO,OAChC,EAAO,QAAU,GAAS,EAAA,QAAO,QACrC,EACA,iBAAoB,EAAe,CAAK,EACxC,YAAc,GAAU,CACpB,EAAM,eAAe,EACrB,EAAa,CAAM,CACvB,EAEC,SAAA,EAAO,KACR,EAfK,EAAO,KAeZ,CACP,CAEL,CAAA,EAGR,OACI,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,IAAK,EAAS,UAAW,EAAA,GAAG,EAAA,QAAO,QAAS,GAAS,EAAA,QAAO,MAAO,CAAS,EAAjF,SAAA,CACK,IACG,EAAA,EAAA,IAAA,CAAC,QAAD,CAAO,QAAS,EAAI,UAAW,EAAA,QAAO,MACjC,SAAA,CACE,CAAA,GAEX,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,IAAK,EAAc,UAAW,EAAA,QAAO,MAA1C,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,QAAD,CACQ,KACJ,IAAK,EACL,KAAK,OACL,KAAK,WACL,gBAAe,EACf,gBAAe,GAAG,EAAG,UACrB,oBAAkB,OAClB,UAAW,EAAA,QAAO,MACL,cACH,WACV,MAAO,EAAO,EAAQ,EACtB,YAAe,EAAQ,EAAI,EAC3B,SAAW,GAAU,CACjB,EAAS,EAAM,OAAO,KAAK,EAC3B,EAAQ,EAAI,EACZ,EAAe,CAAC,CACpB,EACA,UAAW,CACd,CAAA,GACD,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,MAAO,cAAA,GAC3B,UAAA,EAAA,EAAA,IAAA,CAAC,EAAA,UAAD,CAAY,CAAA,CACV,CAAA,CACL,IACJ,IAAS,GAAS,EAAA,EAAA,IAAA,CAAC,EAAA,OAAD,CAAA,SAAS,CAAa,CAAA,EAAI,GAC5C,GACG,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,UAAY,SAAA,CAAY,CAAA,EAChD,GACA,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,OAAS,SAAA,CAAiB,CAAA,EAClD,IACH,GAEb"}
|
|
1
|
+
{"version":3,"file":"Combobox.cjs","names":[],"sources":["../../../src/components/Combobox/Combobox.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — options/value/onChange\n * plus the shared field contract (label, placeholder, helperText, error, disabled,\n * className) and the combobox-specific knobs: filter, emptyMessage, portal. The body\n * is the listbox — open state, active descendant, type-ahead and the ARIA wiring —\n * and those cannot be separated without passing the same active index back and\n * forth.\n */\nimport { useCallback, useId, useMemo, useRef, useState } from \"react\";\nimport { Portal } from \"@/components/Portal\";\nimport { useAnchorPosition } from \"@/components/Portal/anchor-position\";\nimport { CaretIcon } from \"@/components/Select/CaretIcon\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Combobox.module.css\";\nimport { useListboxDismiss } from \"./use-listbox-dismiss\";\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport interface ComboboxProps {\n options: ComboboxOption[];\n value: string;\n onChange: (value: string) => void;\n label?: string;\n placeholder?: string;\n helperText?: string;\n error?: string;\n disabled?: boolean;\n /** Custom filter — return true to keep the option. Default is case-insensitive substring match on label. */\n filter?: (option: ComboboxOption, query: string) => boolean;\n /** Message shown when no option matches. */\n emptyMessage?: string;\n /**\n * Render the list in a portal, positioned under the field. Default `true`.\n *\n * In flow, an ancestor with `overflow` other than `visible` clips the list —\n * in the last row of a `DataTable` none of it was visible (measured in Chrome\n * at 1440 px). In a portal it escapes the clip, takes the field's width,\n * flips above the field when there is no room below, and follows it on\n * scroll, resize and when the field or the list changes size.\n */\n portal?: boolean;\n className?: string;\n}\n\n/** Gap between field and list, in px — the in-flow CSS uses the same 4 px. */\nconst LIST_OFFSET = 4;\n\nfunction defaultFilter(option: ComboboxOption, query: string): boolean {\n if (!query) return true;\n return option.label.toLowerCase().includes(query.toLowerCase());\n}\n\n/**\n * Combobox — text input with a filterable dropdown of options.\n *\n * Selecting an option fires `onChange(value)`. Typing filters the list.\n * Keyboard: ArrowUp/ArrowDown to navigate — the active option is announced\n * through `aria-activedescendant`, focus stays on the input — Enter to select,\n * Esc to close, and Tab/Shift+Tab leave the field closing the list.\n */\nexport function Combobox({\n options,\n value,\n onChange,\n label,\n placeholder = \"Selecione\",\n helperText,\n error,\n disabled,\n filter = defaultFilter,\n emptyMessage = \"Nenhuma opção encontrada\",\n portal = true,\n className,\n}: ComboboxProps) {\n const id = useId();\n const rootRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState(\"\");\n const [activeIndex, setActiveIndex] = useState(0);\n const [fieldNode, setFieldNode] = useState<HTMLDivElement | null>(null);\n const [listNode, setListNode] = useState<HTMLUListElement | null>(null);\n const floatingStyle = useAnchorPosition({\n anchor: fieldNode,\n floating: listNode,\n side: \"bottom\",\n align: \"start\",\n offset: LIST_OFFSET,\n enabled: portal && open,\n matchWidth: true,\n });\n\n const selectedLabel = useMemo(\n () => options.find((o) => o.value === value)?.label ?? \"\",\n [options, value],\n );\n\n const filtered = useMemo(\n () => options.filter((o) => filter(o, query)),\n [options, filter, query],\n );\n\n const closeAndReset = useCallback((): void => {\n setOpen(false);\n setQuery(\"\");\n setActiveIndex(0);\n }, []);\n\n const handleSelect = useCallback(\n (option: ComboboxOption): void => {\n if (option.disabled) return;\n onChange(option.value);\n closeAndReset();\n },\n [onChange, closeAndReset],\n );\n\n const { onInputBlur, onListMouseDown } = useListboxDismiss({\n open,\n rootRef,\n listNode,\n onDismiss: closeAndReset,\n });\n const optionId = (index: number): string => `${id}-option-${index}`;\n const activeOption = open ? filtered[activeIndex] : undefined;\n\n /**\n * Keyboard model of the input.\n *\n * `Escape` calls `preventDefault()` only while the list is open: that marks the\n * key as consumed, so an enclosing `Modal` or `Drawer` stays open and the next\n * `Escape` reaches it. With the list closed the key is left alone and dismisses\n * the enclosing layer, as the user expects.\n */\n const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n setOpen(true);\n setActiveIndex((current) => Math.min(filtered.length - 1, current + 1));\n } else if (event.key === \"ArrowUp\") {\n event.preventDefault();\n setActiveIndex((current) => Math.max(0, current - 1));\n } else if (event.key === \"Enter\") {\n event.preventDefault();\n const option = filtered[activeIndex];\n if (option) handleSelect(option);\n } else if (event.key === \"Escape\") {\n if (open) event.preventDefault();\n closeAndReset();\n }\n };\n\n const list = (\n <ul\n ref={setListNode}\n id={`${id}-listbox`}\n role=\"listbox\"\n className={cn(styles.menu, portal && styles.portalled)}\n style={floatingStyle}\n onMouseDown={onListMouseDown}\n >\n {filtered.length === 0 ? (\n <li className={styles.empty}>{emptyMessage}</li>\n ) : (\n filtered.map((option, index) => (\n <li\n key={option.value}\n id={optionId(index)}\n role=\"option\"\n aria-selected={option.value === value}\n className={cn(\n styles.option,\n index === activeIndex && styles.active,\n option.value === value && styles.selected,\n )}\n onMouseEnter={() => setActiveIndex(index)}\n onMouseDown={() => handleSelect(option)}\n >\n {option.label}\n </li>\n ))\n )}\n </ul>\n );\n\n return (\n <div ref={rootRef} className={cn(styles.wrapper, error && styles.error, className)}>\n {label && (\n <label htmlFor={id} className={styles.label}>\n {label}\n </label>\n )}\n <div ref={setFieldNode} className={styles.field}>\n <input\n id={id}\n ref={inputRef}\n type=\"text\"\n role=\"combobox\"\n aria-expanded={open}\n aria-controls={`${id}-listbox`}\n aria-autocomplete=\"list\"\n aria-activedescendant={activeOption ? optionId(activeIndex) : undefined}\n className={styles.input}\n placeholder={placeholder}\n disabled={disabled}\n value={open ? query : selectedLabel}\n onFocus={() => setOpen(true)}\n onChange={(event) => {\n setQuery(event.target.value);\n setOpen(true);\n setActiveIndex(0);\n }}\n onKeyDown={handleKeyDown}\n onBlur={onInputBlur}\n />\n <span className={styles.caret} aria-hidden>\n <CaretIcon />\n </span>\n </div>\n {open && (portal ? <Portal>{list}</Portal> : list)}\n {error ? (\n <span className={styles.errorText}>{error}</span>\n ) : helperText ? (\n <span className={styles.helper}>{helperText}</span>\n ) : null}\n </div>\n );\n}\n"],"mappings":"wRAiDA,IAAM,EAAc,EAEpB,SAAS,EAAc,EAAwB,EAAwB,CAEnE,MADA,CAAK,GACE,EAAO,MAAM,YAAY,CAAC,CAAC,SAAS,EAAM,YAAY,CAAC,CAClE,CAUA,SAAgB,EAAS,CACrB,UACA,QACA,WACA,QACA,cAAc,YACd,aACA,QACA,WACA,SAAS,EACT,eAAe,2BACf,SAAS,GACT,aACc,CACd,IAAM,GAAA,EAAK,EAAA,MAAA,CAAM,EACX,GAAA,EAAU,EAAA,OAAA,CAAuB,IAAI,EACrC,GAAA,EAAW,EAAA,OAAA,CAAyB,IAAI,EACxC,CAAC,EAAM,IAAA,EAAW,EAAA,SAAA,CAAS,EAAK,EAChC,CAAC,EAAO,IAAA,EAAY,EAAA,SAAA,CAAS,EAAE,EAC/B,CAAC,EAAa,IAAA,EAAkB,EAAA,SAAA,CAAS,CAAC,EAC1C,CAAC,EAAW,IAAA,EAAgB,EAAA,SAAA,CAAgC,IAAI,EAChE,CAAC,EAAU,IAAA,EAAe,EAAA,SAAA,CAAkC,IAAI,EAChE,EAAgB,EAAA,kBAAkB,CACpC,OAAQ,EACR,SAAU,EACV,KAAM,SACN,MAAO,QACP,OAAQ,EACR,QAAS,GAAU,EACnB,WAAY,EAChB,CAAC,EAEK,GAAA,EAAgB,EAAA,QAAA,KACZ,EAAQ,KAAM,GAAM,EAAE,QAAU,CAAK,CAAC,EAAE,OAAS,GACvD,CAAC,EAAS,CAAK,CACnB,EAEM,GAAA,EAAW,EAAA,QAAA,KACP,EAAQ,OAAQ,GAAM,EAAO,EAAG,CAAK,CAAC,EAC5C,CAAC,EAAS,EAAQ,CAAK,CAC3B,EAEM,GAAA,EAAgB,EAAA,YAAA,KAAwB,CAC1C,EAAQ,EAAK,EACb,EAAS,EAAE,EACX,EAAe,CAAC,CACpB,EAAG,CAAC,CAAC,EAEC,GAAA,EAAe,EAAA,YAAA,CAChB,GAAiC,CAC1B,EAAO,WACX,EAAS,EAAO,KAAK,EACrB,EAAc,EAClB,EACA,CAAC,EAAU,CAAa,CAC5B,EAEM,CAAE,cAAa,mBAAoB,EAAA,kBAAkB,CACvD,OACA,UACA,WACA,UAAW,CACf,CAAC,EACK,EAAY,GAA0B,GAAG,EAAG,UAAU,IACtD,EAAe,EAAO,EAAS,GAAe,IAAA,GAU9C,EAAiB,GAAuD,CAC1E,GAAI,EAAM,MAAQ,YACd,EAAM,eAAe,EACrB,EAAQ,EAAI,EACZ,EAAgB,GAAY,KAAK,IAAI,EAAS,OAAS,EAAG,EAAU,CAAC,CAAC,OACnE,GAAI,EAAM,MAAQ,UACrB,EAAM,eAAe,EACrB,EAAgB,GAAY,KAAK,IAAI,EAAG,EAAU,CAAC,CAAC,OACjD,GAAI,EAAM,MAAQ,QAAS,CAC9B,EAAM,eAAe,EACrB,IAAM,EAAS,EAAS,GACpB,GAAQ,EAAa,CAAM,CACnC,MAAW,EAAM,MAAQ,WACjB,GAAM,EAAM,eAAe,EAC/B,EAAc,EAEtB,EAEM,GACF,EAAA,EAAA,IAAA,CAAC,KAAD,CACI,IAAK,EACL,GAAI,GAAG,EAAG,UACV,KAAK,UACL,UAAW,EAAA,GAAG,EAAA,QAAO,KAAM,GAAU,EAAA,QAAO,SAAS,EACrD,MAAO,EACP,YAAa,EAEZ,SAAA,EAAS,SAAW,GACjB,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAW,EAAA,QAAO,MAAQ,SAAA,CAAiB,CAAA,EAE/C,EAAS,KAAK,EAAQ,KAClB,EAAA,EAAA,IAAA,CAAC,KAAD,CAEI,GAAI,EAAS,CAAK,EAClB,KAAK,SACL,gBAAe,EAAO,QAAU,EAChC,UAAW,EAAA,GACP,EAAA,QAAO,OACP,IAAU,GAAe,EAAA,QAAO,OAChC,EAAO,QAAU,GAAS,EAAA,QAAO,QACrC,EACA,iBAAoB,EAAe,CAAK,EACxC,gBAAmB,EAAa,CAAM,EAErC,SAAA,EAAO,KACR,EAbK,EAAO,KAaZ,CACP,CAEL,CAAA,EAGR,OACI,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,IAAK,EAAS,UAAW,EAAA,GAAG,EAAA,QAAO,QAAS,GAAS,EAAA,QAAO,MAAO,CAAS,EAAjF,SAAA,CACK,IACG,EAAA,EAAA,IAAA,CAAC,QAAD,CAAO,QAAS,EAAI,UAAW,EAAA,QAAO,MACjC,SAAA,CACE,CAAA,GAEX,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,IAAK,EAAc,UAAW,EAAA,QAAO,MAA1C,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,QAAD,CACQ,KACJ,IAAK,EACL,KAAK,OACL,KAAK,WACL,gBAAe,EACf,gBAAe,GAAG,EAAG,UACrB,oBAAkB,OAClB,wBAAuB,EAAe,EAAS,CAAW,EAAI,IAAA,GAC9D,UAAW,EAAA,QAAO,MACL,cACH,WACV,MAAO,EAAO,EAAQ,EACtB,YAAe,EAAQ,EAAI,EAC3B,SAAW,GAAU,CACjB,EAAS,EAAM,OAAO,KAAK,EAC3B,EAAQ,EAAI,EACZ,EAAe,CAAC,CACpB,EACA,UAAW,EACX,OAAQ,CACX,CAAA,GACD,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,MAAO,cAAA,GAC3B,UAAA,EAAA,EAAA,IAAA,CAAC,EAAA,UAAD,CAAY,CAAA,CACV,CAAA,CACL,IACJ,IAAS,GAAS,EAAA,EAAA,IAAA,CAAC,EAAA,OAAD,CAAA,SAAS,CAAa,CAAA,EAAI,GAC5C,GACG,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,UAAY,SAAA,CAAY,CAAA,EAChD,GACA,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,OAAS,SAAA,CAAiB,CAAA,EAClD,IACH,GAEb"}
|