@evinvest/uikit 0.25.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +142 -8
  2. package/dist/components/account-menu.js +70 -0
  3. package/dist/components/account-menu.js.map +1 -0
  4. package/dist/components/alert-dialog.js +5 -4
  5. package/dist/components/alert-dialog.js.map +1 -1
  6. package/dist/components/app-shell.js +11 -0
  7. package/dist/components/app-shell.js.map +1 -1
  8. package/dist/components/command.js +129 -20
  9. package/dist/components/command.js.map +1 -1
  10. package/dist/components/context-menu.js +8 -8
  11. package/dist/components/context-menu.js.map +1 -1
  12. package/dist/components/dialog.js +6 -4
  13. package/dist/components/dialog.js.map +1 -1
  14. package/dist/components/drawer.js +15 -9
  15. package/dist/components/drawer.js.map +1 -1
  16. package/dist/components/dropdown-menu.js +13 -10
  17. package/dist/components/dropdown-menu.js.map +1 -1
  18. package/dist/components/info-tip.js +2 -2
  19. package/dist/components/info-tip.js.map +1 -1
  20. package/dist/components/list-rows.js +73 -0
  21. package/dist/components/list-rows.js.map +1 -0
  22. package/dist/components/menubar.js +18 -5
  23. package/dist/components/menubar.js.map +1 -1
  24. package/dist/components/navigation-menu.js +2 -2
  25. package/dist/components/navigation-menu.js.map +1 -1
  26. package/dist/components/popover.js +2 -2
  27. package/dist/components/popover.js.map +1 -1
  28. package/dist/components/select.js +2 -2
  29. package/dist/components/select.js.map +1 -1
  30. package/dist/components/sheet.js +12 -5
  31. package/dist/components/sheet.js.map +1 -1
  32. package/dist/components/sonner.js +1 -0
  33. package/dist/components/sonner.js.map +1 -1
  34. package/dist/components/table.js +24 -7
  35. package/dist/components/table.js.map +1 -1
  36. package/dist/components/toggle-group.js +1 -1
  37. package/dist/components/toggle-group.js.map +1 -1
  38. package/dist/components/top-bar.js +21 -0
  39. package/dist/components/top-bar.js.map +1 -0
  40. package/dist/generated/alert-dialog.js +2 -2
  41. package/dist/generated/alert-dialog.js.map +1 -1
  42. package/dist/generated/context-menu.js +1 -1
  43. package/dist/generated/context-menu.js.map +1 -1
  44. package/dist/generated/dialog.js +3 -3
  45. package/dist/generated/dialog.js.map +1 -1
  46. package/dist/generated/drawer.js +1 -1
  47. package/dist/generated/drawer.js.map +1 -1
  48. package/dist/generated/dropdown-menu.js +1 -1
  49. package/dist/generated/dropdown-menu.js.map +1 -1
  50. package/dist/generated/menubar.js +1 -1
  51. package/dist/generated/menubar.js.map +1 -1
  52. package/dist/generated/sheet.js +3 -3
  53. package/dist/generated/sheet.js.map +1 -1
  54. package/dist/generated/table.js +20 -3
  55. package/dist/generated/table.js.map +1 -1
  56. package/dist/generated/toggle.js +2 -2
  57. package/dist/generated/toggle.js.map +1 -1
  58. package/dist/index.d.ts +199 -27
  59. package/dist/index.js +5 -2
  60. package/dist/primitives/dismissable-layer.js +80 -15
  61. package/dist/primitives/dismissable-layer.js.map +1 -1
  62. package/dist/primitives/focus-scope.js +22 -6
  63. package/dist/primitives/focus-scope.js.map +1 -1
  64. package/dist/primitives/use-roving-focus.js +13 -7
  65. package/dist/primitives/use-roving-focus.js.map +1 -1
  66. package/package.json +1 -1
  67. package/styles/ev.css +4 -2
  68. package/styles/theme.css +34 -2
  69. package/styles/tokens.css +42 -5
package/README.md CHANGED
@@ -63,7 +63,7 @@ your Tailwind v4 entrypoint — this is the load-bearing part of the kit:
63
63
  | surfaces | `background` `card` `popover` `muted` `hover` |
64
64
  | ink | `ink` `ink-mid` `ink-soft` — hierarchy, loudest first |
65
65
  | lines | `border` `input` `ring` |
66
- | roles | `brand` `primary` `secondary` `positive` `accent-trace` `accent-debug` `accent-info` `accent-warn` `accent-error`, each with `on-*` where it gets filled; `primary-ink` — the primary role as ink on a surface (`#128377` fill under a white `on-primary`, `#2a9d8f` ink) |
66
+ | roles | `brand` `primary` `secondary` `positive` `accent-trace` `accent-debug` `accent-info` `accent-warn` `accent-error`, each with `on-*` where it gets filled; `primary-ink` — the primary role as ink on a surface (`#128377` fill under a white `on-primary`, `#2a9d8f` ink); `accent-error-ink` — the error role as text on its own tint (derived, a palette may pin it) |
67
67
  | scalars | `radius` `control-radius` `control-py` `display-scale` `band-py` `page-max` `page-px` `shell-rail-w` `shell-tab-bar-h` `shadow-*` `font-*` |
68
68
  | motion | `ev-ease-out` `ev-ease-in-out` `ev-dur-fast` `ev-dur-base` `ev-dur-slow` `ev-rise` `ev-stagger` `ev-stagger-section` |
69
69
  | charts | `chart-1` … `chart-5` |
@@ -106,7 +106,8 @@ It has two halves, each also shipped as a flat sheet of its own —
106
106
 
107
107
  - **the contract** — the `@theme inline` mapping, the geometry, and the tokens
108
108
  *derived* from a palette (`hover`, `ink-mid`, `ink-soft`, `border`, `input`
109
- from `ink`; `ring` from `primary-ink`). The derived ones are redeclared on
109
+ from `ink`; `ring` from `primary-ink`; `accent-error-ink` from `accent-error`
110
+ and `ink`). The derived ones are redeclared on
110
111
  `:root`, `[data-brand]`, `.light` and `.dark` at zero specificity, so a brand
111
112
  scope gets its own borders, hover and focus ring rather than inheriting the
112
113
  root's, and a palette may still pin any of them outright. The geometry
@@ -366,10 +367,11 @@ export function Nav() {
366
367
  }
367
368
  ```
368
369
 
369
- - **`AppShell`** — slots `rail`, `tabBar`, `banner`, `children`; `breakpoint`
370
+ - **`AppShell`** — slots `rail`, `topBar`, `tabBar`, `banner`, `children`; `breakpoint`
370
371
  (`"md"` | `"lg"`, default `lg`) is where the tab bar gives way to the rail.
371
372
  Fills `100dvh` less `--ev-shell-offset`; the rail sticks under that offset; the
372
373
  column reserves `--shell-tab-bar-h` plus the bottom safe area for the bar.
374
+ `topBar` sticks over the column, also under that offset, from `breakpoint` up.
373
375
  `mainProps` reaches the `<main>`. Layout only — no `"use client"`.
374
376
  - **`NavItem`** — `{ id, href, label, icon?, badge?, trailing?, external?,
375
377
  target?, match?, also?, disabled? }`, shared by the rail and the tab bar.
@@ -401,6 +403,10 @@ export function Nav() {
401
403
  - **`MobileAppBar`** `{ title, back?: { href } | { onClick }, right?,
402
404
  linkComponent?, hideFrom = "lg", labels: { back } }` — sticky under
403
405
  `--ev-shell-offset`; with `back` the title centres.
406
+ - **`TopBar`** `{ start?, end? }` — layout only, server-safe.
407
+ - **`AccountMenu`** `{ account: { name?, email }, manageHref?, switchHref,
408
+ groups?: NavGroup[], onSignOut, linkComponent?, labels: { trigger, manage,
409
+ switchAccount, signOut } }` — an initials avatar opening the menu.
404
410
  - **`PageFrame`** `{ title?, description?, eyebrow?, actions?, appBar?,
405
411
  width: "full" | "content", breakpoint }`, with **`PageHeading`** and
406
412
  **`SectionLabel`** (`tone: "muted" | "accent"`). With an `appBar` the heading
@@ -419,6 +425,25 @@ export function Nav() {
419
425
  Every string the kit renders or speaks is a `labels` entry with an English
420
426
  default.
421
427
 
428
+ ### The top bar contract
429
+
430
+ Every service under one account wears the same top bar, so a user moving
431
+ between them finds who they are and how to leave in the same place:
432
+
433
+ ```
434
+ [start: app switcher or service name] [end: service stats…] [AccountMenu]
435
+ AccountMenu: name / email · Manage account · ── service groups ── · Switch account · Sign out
436
+ ```
437
+
438
+ - The bar is utility only; product nav stays in the rail.
439
+ - The kit owns the fixed parts and their order. A service adds stats before the
440
+ `AccountMenu` and `groups` inside it — it never reorders or drops the rest.
441
+ - Data is the host's: each service reads its own session and passes `account`;
442
+ `manageHref` is the identity provider's account center, `switchHref` the
443
+ sign-in that shows the account chooser.
444
+ - A surface that cannot take the kit (the cabinet's public chip) hand-writes the
445
+ same order.
446
+
422
447
  ### Motion without a motion library
423
448
 
424
449
  The shell's motion is CSS in `tokens.css` (from `motion.css`), keyed on data
@@ -468,6 +493,90 @@ cn("p-4", "p-2"); // "p-2" — tailwind-merge resolves the conflict, rightmost w
468
493
  `cn` (clsx + tailwind-merge) is the mirror of the Rust `cn!` macro
469
494
  (`tailwind_fuse::tw_merge!`). A caller's `className`, passed last, beats the base.
470
495
 
496
+ ### Tables
497
+
498
+ ```tsx
499
+ import { Table, TableBody, TableCard, TableCell, TableHead, TableHeader, TableRow } from "@evinvest/uikit";
500
+
501
+ <TableCard>
502
+ <Table variant="card" density="compact">
503
+ <TableHeader>
504
+ <TableRow>
505
+ <TableHead>Key</TableHead>
506
+ <TableHead align="end">Amount</TableHead>
507
+ </TableRow>
508
+ </TableHeader>
509
+ <TableBody>
510
+ <TableRow>
511
+ <TableCell>sk_live_7f3a</TableCell>
512
+ <TableCell align="end">1,250.00</TableCell>
513
+ </TableRow>
514
+ </TableBody>
515
+ </Table>
516
+ </TableCard>
517
+ ```
518
+
519
+ - `variant="card"` — uppercase `text-xs tracking-wide text-ink-soft` head,
520
+ `px-5 py-3` cells, no hover on the head row. Meant to sit edge to edge in a
521
+ `TableCard`, the paddingless card surface.
522
+ - `density="compact"` — shorter head and rows; combines with either variant.
523
+ - `align="start" | "end"` on `TableHead` / `TableCell` — `end` right-aligns
524
+ with tabular numerals for figures. It replaces the obsolete HTML `align`
525
+ attribute on those two.
526
+ - `Table` sets no context: `variant` and `density` are inherited `--table-*`
527
+ custom properties the heads and cells read. `Table` stays a Server
528
+ Component, and a cell's own `className` (`px-0`, `text-center`) still wins.
529
+ - **`ListRows` / `ListRow` (TS-only)** — a table's phone form: label/value rows
530
+ split by hairlines, so the trailing columns are not scrolled off-screen.
531
+ `variant="card"` pads the rows for a `TableCard`; `ListRowLabel` /
532
+ `ListRowValue` compose a row by hand.
533
+
534
+ ```tsx
535
+ <TableCard className="sm:hidden">
536
+ <ListRows variant="card">
537
+ <ListRow label="sk_live_7f3a" description="api · aquafix" value="2026-09-30" />
538
+ </ListRows>
539
+ </TableCard>
540
+ ```
541
+ ### Command
542
+
543
+ By default `Command` filters its items on the client: a case-insensitive
544
+ substring match of the typed query against each `CommandItem`'s `value`. When the
545
+ rows are already the answer to the query — a search endpoint, a ranking of your
546
+ own — pass `shouldFilter={false}` and own the query state:
547
+
548
+ ```tsx
549
+ const [query, setQuery] = useState("");
550
+ const { rows, loading } = useInvestorSearch(query); // your fetch, debounced or not
551
+
552
+ <Command shouldFilter={false} search={query} onSearchChange={setQuery}>
553
+ <CommandInput placeholder="Search investors…" />
554
+ <CommandList>
555
+ {loading ? <Spinner /> : <CommandEmpty>No investors found.</CommandEmpty>}
556
+ {rows.map((r) => (
557
+ <CommandItem key={r.id} value={r.id} onSelect={pick}>
558
+ {r.name}
559
+ </CommandItem>
560
+ ))}
561
+ </CommandList>
562
+ </Command>
563
+ ```
564
+
565
+ Then nothing is hidden or reordered: every mounted item renders in your order,
566
+ and `CommandEmpty` shows once a query is typed and no item is mounted. `value`
567
+ is then just what `onSelect` receives.
568
+
569
+ Keyboard works the same in both modes, as an ARIA combobox: focus stays in
570
+ `CommandInput`, which points at the highlighted row via
571
+ `aria-activedescendant`; ArrowUp / ArrowDown move the highlight (clamped,
572
+ disabled rows skipped), Enter fires the row's `onSelect`, and hover moves it
573
+ too. Home / End stay with the caret, and keys from any other focusable inside
574
+ Command (a button in `CommandEmpty`) are left alone. Rows stay out of the Tab order on purpose. The highlight sits on
575
+ the first row until the user moves it, so results that arrive after the
576
+ keystroke put Enter on the top hit. Without a `CommandInput` there is nothing to
577
+ hold focus, so a bare list is pointer-only. The live demo is `CommandDemo` in
578
+ `example/`.
579
+
471
580
  ## Rust ↔ TS parity
472
581
 
473
582
  The Rust crate is the source of truth; this package preserves its _semantics_
@@ -484,7 +593,7 @@ element across the two ports.
484
593
  | overlay placement | inline `position:fixed` + `data-side` | `Portal` + `useFloating` |
485
594
  | dismiss / focus trap | full-screen backdrop / native order | `useDismissableLayer` / `useFocusScope` |
486
595
 
487
- ### Component inventory (all 73 bricks)
596
+ ### Component inventory (all 75 bricks)
488
597
 
489
598
  - **Tier A — static (23):** badge, button, button-group, card, input, textarea,
490
599
  label, field, separator, skeleton, spinner, kbd, table, container, alert,
@@ -503,8 +612,8 @@ element across the two ports.
503
612
  / `Forbidden` / `ServerError` presets (404 / 403 / 500). The kit ships no
504
613
  artwork: `Logo` masks whatever `--brand-mark` / `--brand-aspect` the consumer
505
614
  declares beside the palette, and the status pages use it for their mark.
506
- - **App shell — TS-only (10):** app-shell, shell-nav, bottom-tab-bar,
507
- nav-badge (`NavBadge` / `NavDot`), mobile-app-bar, page-frame (`PageFrame` /
615
+ - **App shell — TS-only (12):** app-shell, shell-nav, bottom-tab-bar,
616
+ nav-badge (`NavBadge` / `NavDot`), top-bar, account-menu, mobile-app-bar, page-frame (`PageFrame` /
508
617
  `PageHeading` / `SectionLabel`), section-nav, resource-error, system-banner,
509
618
  settled — see [App shell](#app-shell).
510
619
  - **Features — composed screens (1):** terminal — a trading terminal over an
@@ -536,6 +645,31 @@ measuring needs host-only `web-sys`). Known gaps:
536
645
  no viewport-measured floating, native focus order (no trap). TS overlays use a
537
646
  real `Portal`, single-flip `useFloating` (absolute in the document, so it
538
647
  scrolls with the page natively), `useDismissableLayer`, and `useFocusScope`.
648
+ - **overlay layers (TS):** every TS overlay is on one dismissable-layer stack.
649
+ A layer rendered inside another sits above it, even when both open in the
650
+ same commit; unrelated layers stack in the order they opened. Escape closes
651
+ the layer that was on top when it was pressed — a menu, `Select` or
652
+ `Popover` inside a `Dialog`, `Sheet` or `Drawer` closes first, the modal on
653
+ the next Escape — and a press inside a layer above is not "outside" the
654
+ ones below. A modal (`Dialog`, `AlertDialog`, `Sheet`, `Drawer`,
655
+ `CommandDialog`) bars the layers below from pointer-downs while it is open:
656
+ a click on its backdrop closes it alone. `Drawer` and `CommandDialog` close
657
+ on a click on their own scrim, not on any outside pointer-down. Toasts are
658
+ outside every layer: they never take Escape, and a click on one closes
659
+ nothing. The stack is shared through `globalThis`, so a micro-frontend
660
+ bundling its own uikit copy sees the host's layers; parent links are a
661
+ React context, so across two copies the nested layer's place falls back to
662
+ opening order. A non-uikit overlay joins through `useDismissableLayer`
663
+ (`modal`, `pointerOutside`) and `DismissableLayerScope`; mark a floating
664
+ region that should dismiss nothing with `LAYER_IGNORE_ATTR`.
665
+ - **overlay layers (Rust):** `Select`, `Popover`, `Dialog` and `DropdownMenu`
666
+ share one dismissable-layer stack per `VirtualDom`
667
+ (`primitives::use_dismissable_layer`): Escape and a click on a backdrop act
668
+ on the top layer only, so a `Select` inside a `Dialog` closes first. There is
669
+ no document listener — an Escape counts only once it reaches an open layer's
670
+ `onkeydown` — and `AlertDialog`, `Sheet`, `Drawer`, `ContextMenu`, `Menubar`,
671
+ `NavigationMenu`, `HoverCard`, `Tooltip`, `InfoTip`, `DateTimePicker`,
672
+ `CommandDialog` and `DropdownMenuSub` are not on the stack yet.
539
673
  - **chart:** the recharts plotting engine is not bundled. `ChartContainer` is a
540
674
  themed SVG host (emits `--color-*` from its config); `ChartTooltipContent` /
541
675
  `ChartLegendContent` are presentational and take explicit items. Draw series
@@ -605,8 +739,8 @@ measuring needs host-only `web-sys`). Known gaps:
605
739
  The lock-up is composed from `mark` + `brand` (display family) + `tagline`,
606
740
  or passed whole as `lockup`. Brand-coloured text reads `primary-ink`.
607
741
  - **TS-only for now:** `NativeSelect`, the `Field` id hand-off,
608
- `SelectValue` labels, and the whole [app shell](#app-shell) (`AppShell`,
609
- `ShellNav`, `BottomTabBar`, `NavBadge` / `NavDot`, `MobileAppBar`, `PageFrame` /
742
+ `SelectValue` labels, `ListRows` / `ListRow` (a table's phone form), and the whole [app shell](#app-shell) (`AppShell`,
743
+ `ShellNav`, `BottomTabBar`, `NavBadge` / `NavDot`, `TopBar`, `AccountMenu`, `MobileAppBar`, `PageFrame` /
610
744
  `PageHeading` / `SectionLabel`, `SectionNav`, `ResourceError`, `SystemBanner`,
611
745
  `Settled`) — no Dioxus twin. Its CSS (the entrances, the markers, the motion
612
746
  and shell tokens) ships in the shared sheet, so a Rust app can use the
@@ -0,0 +1,70 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { cn } from "../lib/cn.js";
5
+ import { Avatar, AvatarFallback } from "./avatar.js";
6
+ import {
7
+ DropdownMenu,
8
+ DropdownMenuContent,
9
+ DropdownMenuGroup,
10
+ DropdownMenuItem,
11
+ DropdownMenuLabel,
12
+ DropdownMenuSeparator,
13
+ DropdownMenuTrigger
14
+ } from "./dropdown-menu.js";
15
+ const initials = (account) => {
16
+ const words = account.name?.trim().split(/\s+/).filter(Boolean) ?? [];
17
+ const letters = words.length > 0 ? words.slice(0, 2).map((w) => Array.from(w)[0]) : [Array.from(account.email)[0]];
18
+ return letters.join("").toUpperCase();
19
+ };
20
+ function AccountMenu({
21
+ account,
22
+ manageHref,
23
+ switchHref,
24
+ groups = [],
25
+ onSignOut,
26
+ linkComponent,
27
+ labels = {},
28
+ className
29
+ }) {
30
+ const Link = linkComponent ?? "a";
31
+ return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
32
+ /* @__PURE__ */ jsx(
33
+ DropdownMenuTrigger,
34
+ {
35
+ "data-slot": "account-menu-trigger",
36
+ "aria-label": labels.trigger ?? "Account",
37
+ className: cn("rounded-full outline-none focus-visible:ring-2 focus-visible:ring-ring", className),
38
+ children: /* @__PURE__ */ jsx(Avatar, { children: /* @__PURE__ */ jsx(AvatarFallback, { className: "text-xs font-medium text-ink", children: initials(account) }) })
39
+ }
40
+ ),
41
+ /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", className: "w-64", children: [
42
+ /* @__PURE__ */ jsxs(DropdownMenuLabel, { className: "flex flex-col", children: [
43
+ account.name && /* @__PURE__ */ jsx("span", { className: "truncate text-ink", children: account.name }),
44
+ /* @__PURE__ */ jsx("span", { className: "truncate font-normal text-ink-soft", children: account.email })
45
+ ] }),
46
+ manageHref && /* @__PURE__ */ jsx(DropdownMenuItem, { asChild: true, children: /* @__PURE__ */ jsx("a", { href: manageHref, children: labels.manage ?? "Manage account" }) }),
47
+ groups.map((group) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
48
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
49
+ /* @__PURE__ */ jsxs(DropdownMenuGroup, { children: [
50
+ group.label && /* @__PURE__ */ jsx(DropdownMenuLabel, { className: "text-xs text-ink-soft", children: group.label }),
51
+ group.items.map((item) => {
52
+ const Item = item.external ? "a" : Link;
53
+ const Icon = item.icon;
54
+ return /* @__PURE__ */ jsx(DropdownMenuItem, { asChild: true, children: /* @__PURE__ */ jsxs(Item, { href: item.href, children: [
55
+ Icon && /* @__PURE__ */ jsx(Icon, { className: "size-4" }),
56
+ item.label
57
+ ] }) }, item.id);
58
+ })
59
+ ] })
60
+ ] }, group.id)),
61
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
62
+ /* @__PURE__ */ jsx(DropdownMenuItem, { asChild: true, children: /* @__PURE__ */ jsx("a", { href: switchHref, children: labels.switchAccount ?? "Switch account" }) }),
63
+ /* @__PURE__ */ jsx(DropdownMenuItem, { onClick: onSignOut, children: labels.signOut ?? "Sign out" })
64
+ ] })
65
+ ] });
66
+ }
67
+ export {
68
+ AccountMenu
69
+ };
70
+ //# sourceMappingURL=account-menu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/account-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { Avatar, AvatarFallback } from \"./avatar\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"./dropdown-menu\";\nimport type { NavGroup } from \"./nav-model\";\n\nexport interface AccountMenuLabels {\n /** Accessible name of the trigger. Default `\"Account\"`. */\n trigger?: string;\n /** Default `\"Manage account\"`. */\n manage?: string;\n /** Default `\"Switch account\"`. */\n switchAccount?: string;\n /** Default `\"Sign out\"`. */\n signOut?: string;\n}\n\nexport interface AccountMenuProps {\n account: { name?: string | null | undefined; email: string };\n /** The account center, on the identity provider's origin; no item without it. */\n manageHref?: string | undefined;\n /** A full page load to the sign-in that offers the account chooser. */\n switchHref: string;\n /** The service's own entries, between the account center and switching. Draws `label`, `icon`, `external`. */\n groups?: readonly NavGroup[];\n onSignOut: () => void;\n linkComponent?: React.ElementType;\n labels?: AccountMenuLabels;\n className?: string;\n}\n\nconst initials = (account: AccountMenuProps[\"account\"]) => {\n const words = account.name?.trim().split(/\\s+/).filter(Boolean) ?? [];\n const letters = words.length > 0 ? words.slice(0, 2).map(w => Array.from(w)[0]) : [Array.from(account.email)[0]];\n return letters.join(\"\").toUpperCase();\n};\n\n/**\n * Who is signed in, top right. The order is fixed — the account, the account\n * center, the service's groups, switching, signing out — so every service's\n * menu reads the same; a service adds groups and nothing else.\n */\nexport function AccountMenu({\n account,\n manageHref,\n switchHref,\n groups = [],\n onSignOut,\n linkComponent,\n labels = {},\n className,\n}: AccountMenuProps) {\n const Link = linkComponent ?? \"a\";\n return (\n <DropdownMenu>\n <DropdownMenuTrigger\n data-slot=\"account-menu-trigger\"\n aria-label={labels.trigger ?? \"Account\"}\n className={cn(\"rounded-full outline-none focus-visible:ring-2 focus-visible:ring-ring\", className)}\n >\n <Avatar>\n <AvatarFallback className=\"text-xs font-medium text-ink\">{initials(account)}</AvatarFallback>\n </Avatar>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-64\">\n <DropdownMenuLabel className=\"flex flex-col\">\n {account.name && <span className=\"truncate text-ink\">{account.name}</span>}\n <span className=\"truncate font-normal text-ink-soft\">{account.email}</span>\n </DropdownMenuLabel>\n {manageHref && (\n <DropdownMenuItem asChild>\n <a href={manageHref}>{labels.manage ?? \"Manage account\"}</a>\n </DropdownMenuItem>\n )}\n {groups.map(group => (\n <React.Fragment key={group.id}>\n <DropdownMenuSeparator />\n <DropdownMenuGroup>\n {group.label && <DropdownMenuLabel className=\"text-xs text-ink-soft\">{group.label}</DropdownMenuLabel>}\n {group.items.map(item => {\n const Item = item.external ? \"a\" : Link;\n const Icon = item.icon;\n return (\n <DropdownMenuItem key={item.id} asChild>\n <Item href={item.href}>\n {Icon && <Icon className=\"size-4\" />}\n {item.label}\n </Item>\n </DropdownMenuItem>\n );\n })}\n </DropdownMenuGroup>\n </React.Fragment>\n ))}\n <DropdownMenuSeparator />\n <DropdownMenuItem asChild>\n <a href={switchHref}>{labels.switchAccount ?? \"Switch account\"}</a>\n </DropdownMenuItem>\n <DropdownMenuItem onClick={onSignOut}>{labels.signOut ?? \"Sign out\"}</DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n"],"mappings":";AAuEU,cAIF,YAJE;AArEV,YAAY,WAAW;AACvB,SAAS,UAAU;AACnB,SAAS,QAAQ,sBAAsB;AACvC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AA4BP,MAAM,WAAW,CAAC,YAAyC;AACzD,QAAM,QAAQ,QAAQ,MAAM,KAAK,EAAE,MAAM,KAAK,EAAE,OAAO,OAAO,KAAK,CAAC;AACpE,QAAM,UAAU,MAAM,SAAS,IAAI,MAAM,MAAM,GAAG,CAAC,EAAE,IAAI,OAAK,MAAM,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,KAAK,QAAQ,KAAK,EAAE,CAAC,CAAC;AAC/G,SAAO,QAAQ,KAAK,EAAE,EAAE,YAAY;AACtC;AAOO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS,CAAC;AAAA,EACV;AAAA,EACA;AAAA,EACA,SAAS,CAAC;AAAA,EACV;AACF,GAAqB;AACnB,QAAM,OAAO,iBAAiB;AAC9B,SACE,qBAAC,gBACC;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,aAAU;AAAA,QACV,cAAY,OAAO,WAAW;AAAA,QAC9B,WAAW,GAAG,0EAA0E,SAAS;AAAA,QAEjG,8BAAC,UACC,8BAAC,kBAAe,WAAU,gCAAgC,mBAAS,OAAO,GAAE,GAC9E;AAAA;AAAA,IACF;AAAA,IACA,qBAAC,uBAAoB,OAAM,OAAM,WAAU,QACzC;AAAA,2BAAC,qBAAkB,WAAU,iBAC1B;AAAA,gBAAQ,QAAQ,oBAAC,UAAK,WAAU,qBAAqB,kBAAQ,MAAK;AAAA,QACnE,oBAAC,UAAK,WAAU,sCAAsC,kBAAQ,OAAM;AAAA,SACtE;AAAA,MACC,cACC,oBAAC,oBAAiB,SAAO,MACvB,8BAAC,OAAE,MAAM,YAAa,iBAAO,UAAU,kBAAiB,GAC1D;AAAA,MAED,OAAO,IAAI,WACV,qBAAC,MAAM,UAAN,EACC;AAAA,4BAAC,yBAAsB;AAAA,QACvB,qBAAC,qBACE;AAAA,gBAAM,SAAS,oBAAC,qBAAkB,WAAU,yBAAyB,gBAAM,OAAM;AAAA,UACjF,MAAM,MAAM,IAAI,UAAQ;AACvB,kBAAM,OAAO,KAAK,WAAW,MAAM;AACnC,kBAAM,OAAO,KAAK;AAClB,mBACE,oBAAC,oBAA+B,SAAO,MACrC,+BAAC,QAAK,MAAM,KAAK,MACd;AAAA,sBAAQ,oBAAC,QAAK,WAAU,UAAS;AAAA,cACjC,KAAK;AAAA,eACR,KAJqB,KAAK,EAK5B;AAAA,UAEJ,CAAC;AAAA,WACH;AAAA,WAhBmB,MAAM,EAiB3B,CACD;AAAA,MACD,oBAAC,yBAAsB;AAAA,MACvB,oBAAC,oBAAiB,SAAO,MACvB,8BAAC,OAAE,MAAM,YAAa,iBAAO,iBAAiB,kBAAiB,GACjE;AAAA,MACA,oBAAC,oBAAiB,SAAS,WAAY,iBAAO,WAAW,YAAW;AAAA,OACtE;AAAA,KACF;AAEJ;","names":[]}
@@ -4,7 +4,7 @@ import * as React from "react";
4
4
  import { cn } from "../lib/cn.js";
5
5
  import { buttonVariants } from "./button.js";
6
6
  import { useControllableState } from "../primitives/use-controllable-state.js";
7
- import { useDismissableLayer } from "../primitives/dismissable-layer.js";
7
+ import { DismissableLayerScope, useDismissableLayer } from "../primitives/dismissable-layer.js";
8
8
  import { useFocusScope } from "../primitives/focus-scope.js";
9
9
  import { usePresence } from "../primitives/presence.js";
10
10
  import { mergeRefs } from "../primitives/merge-refs.js";
@@ -66,11 +66,12 @@ function AlertDialogOverlay({ className, ...props }) {
66
66
  function AlertDialogContent({ className, children, ...props }) {
67
67
  const { open, setOpen, triggerRef } = useAlertDialog();
68
68
  const { isPresent, ref: presRef } = usePresence(open);
69
- const focusRef = useFocusScope(open);
69
+ const focusRef = useFocusScope(open, { returnFocusTo: triggerRef });
70
70
  const dismissRef = useDismissableLayer({
71
71
  enabled: open,
72
72
  onDismiss: () => setOpen(false),
73
- exclude: [triggerRef]
73
+ exclude: [triggerRef],
74
+ modal: true
74
75
  });
75
76
  if (!isPresent) return null;
76
77
  return /* @__PURE__ */ jsxs(Portal, { children: [
@@ -85,7 +86,7 @@ function AlertDialogContent({ className, children, ...props }) {
85
86
  ref: mergeRefs(focusRef, dismissRef, presRef),
86
87
  className: cn(ALERT_DIALOG_CONTENT, className),
87
88
  ...props,
88
- children
89
+ children: /* @__PURE__ */ jsx(DismissableLayerScope, { layer: dismissRef, children })
89
90
  }
90
91
  )
91
92
  ] });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/alert-dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { buttonVariants } from \"./button\";\nimport { useControllableState } from \"../primitives/use-controllable-state\";\nimport { useDismissableLayer } from \"../primitives/dismissable-layer\";\nimport { useFocusScope } from \"../primitives/focus-scope\";\nimport { usePresence } from \"../primitives/presence\";\nimport { mergeRefs } from \"../primitives/merge-refs\";\nimport { Portal } from \"../primitives/portal\";\nimport { Slot } from \"../primitives/slot\";\nimport {\n ALERT_DIALOG_CONTENT,\n ALERT_DIALOG_DESCRIPTION,\n ALERT_DIALOG_FOOTER,\n ALERT_DIALOG_HEADER,\n ALERT_DIALOG_OVERLAY,\n ALERT_DIALOG_TITLE,\n} from \"../generated/alert-dialog\";\n\ninterface AlertDialogContextValue {\n open: boolean;\n setOpen: (next: boolean) => void;\n triggerRef: React.RefObject<HTMLElement | null>;\n}\n\nconst AlertDialogContext = React.createContext<AlertDialogContextValue | null>(null);\n\nfunction useAlertDialog(): AlertDialogContextValue {\n const ctx = React.useContext(AlertDialogContext);\n if (!ctx) throw new Error(\"AlertDialog parts must be used within <AlertDialog>\");\n return ctx;\n}\n\nexport interface AlertDialogProps {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n children?: React.ReactNode;\n}\n\nexport function AlertDialog({ open, defaultOpen = false, onOpenChange, children }: AlertDialogProps) {\n const [isOpen, setOpen] = useControllableState<boolean>({\n ...(open !== undefined ? { value: open } : {}),\n defaultValue: defaultOpen,\n ...(onOpenChange ? { onChange: onOpenChange } : {}),\n });\n const triggerRef = React.useRef<HTMLElement | null>(null);\n return (\n <AlertDialogContext.Provider value={{ open: isOpen, setOpen, triggerRef }}>\n {children}\n </AlertDialogContext.Provider>\n );\n}\n\nexport interface AlertDialogTriggerProps extends React.ComponentProps<\"button\"> {\n asChild?: boolean;\n}\n\nexport function AlertDialogTrigger({ asChild = false, onClick, ...props }: AlertDialogTriggerProps) {\n const { open, setOpen, triggerRef } = useAlertDialog();\n const Comp = asChild ? Slot : \"button\";\n return (\n <Comp\n data-slot=\"alert-dialog-trigger\"\n aria-expanded={open}\n ref={triggerRef as React.Ref<HTMLButtonElement>}\n onClick={(e: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(e);\n setOpen(true);\n }}\n {...(props as Record<string, unknown>)}\n />\n );\n}\n\nexport interface AlertDialogOverlayProps extends React.ComponentProps<\"div\"> {}\n\nexport function AlertDialogOverlay({ className, ...props }: AlertDialogOverlayProps) {\n const { open, setOpen } = useAlertDialog();\n return (\n <div\n data-slot=\"alert-dialog-overlay\"\n data-state={open ? \"open\" : \"closed\"}\n onClick={() => setOpen(false)}\n className={cn(ALERT_DIALOG_OVERLAY, className)}\n {...(props as Record<string, unknown>)}\n />\n );\n}\n\nexport interface AlertDialogContentProps extends React.ComponentProps<\"div\"> {}\n\nexport function AlertDialogContent({ className, children, ...props }: AlertDialogContentProps) {\n const { open, setOpen, triggerRef } = useAlertDialog();\n const { isPresent, ref: presRef } = usePresence(open);\n const focusRef = useFocusScope(open);\n const dismissRef = useDismissableLayer({\n enabled: open,\n onDismiss: () => setOpen(false),\n exclude: [triggerRef],\n });\n if (!isPresent) return null;\n return (\n <Portal>\n <AlertDialogOverlay />\n <div\n data-slot=\"alert-dialog-content\"\n role=\"alertdialog\"\n aria-modal=\"true\"\n data-state={open ? \"open\" : \"closed\"}\n ref={mergeRefs(focusRef, dismissRef, presRef as React.Ref<HTMLDivElement>)}\n className={cn(ALERT_DIALOG_CONTENT, className)}\n {...(props as Record<string, unknown>)}\n >\n {children}\n </div>\n </Portal>\n );\n}\n\nexport function AlertDialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"alert-dialog-header\"\n className={cn(ALERT_DIALOG_HEADER, className)}\n {...props}\n />\n );\n}\n\nexport function AlertDialogFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"alert-dialog-footer\"\n className={cn(ALERT_DIALOG_FOOTER, className)}\n {...props}\n />\n );\n}\n\nexport function AlertDialogTitle({ className, ...props }: React.ComponentProps<\"h2\">) {\n return (\n <h2\n data-slot=\"alert-dialog-title\"\n className={cn(ALERT_DIALOG_TITLE, className)}\n {...props}\n />\n );\n}\n\nexport function AlertDialogDescription({ className, ...props }: React.ComponentProps<\"p\">) {\n return (\n <p\n data-slot=\"alert-dialog-description\"\n className={cn(ALERT_DIALOG_DESCRIPTION, className)}\n {...props}\n />\n );\n}\n\nexport interface AlertDialogActionProps extends React.ComponentProps<\"button\"> {}\n\nexport function AlertDialogAction({ className, onClick, ...props }: AlertDialogActionProps) {\n const { setOpen } = useAlertDialog();\n return (\n <button\n data-slot=\"alert-dialog-action\"\n className={cn(buttonVariants(), className)}\n onClick={(e: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(e);\n setOpen(false);\n }}\n {...(props as Record<string, unknown>)}\n />\n );\n}\n\nexport interface AlertDialogCancelProps extends React.ComponentProps<\"button\"> {}\n\nexport function AlertDialogCancel({ className, onClick, ...props }: AlertDialogCancelProps) {\n const { setOpen } = useAlertDialog();\n return (\n <button\n data-slot=\"alert-dialog-cancel\"\n className={cn(buttonVariants({ variant: \"outline\" }), className)}\n onClick={(e: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(e);\n setOpen(false);\n }}\n {...(props as Record<string, unknown>)}\n />\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/alert-dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { buttonVariants } from \"./button\";\nimport { useControllableState } from \"../primitives/use-controllable-state\";\nimport { DismissableLayerScope, useDismissableLayer } from \"../primitives/dismissable-layer\";\nimport { useFocusScope } from \"../primitives/focus-scope\";\nimport { usePresence } from \"../primitives/presence\";\nimport { mergeRefs } from \"../primitives/merge-refs\";\nimport { Portal } from \"../primitives/portal\";\nimport { Slot } from \"../primitives/slot\";\nimport {\n ALERT_DIALOG_CONTENT,\n ALERT_DIALOG_DESCRIPTION,\n ALERT_DIALOG_FOOTER,\n ALERT_DIALOG_HEADER,\n ALERT_DIALOG_OVERLAY,\n ALERT_DIALOG_TITLE,\n} from \"../generated/alert-dialog\";\n\ninterface AlertDialogContextValue {\n open: boolean;\n setOpen: (next: boolean) => void;\n triggerRef: React.RefObject<HTMLElement | null>;\n}\n\nconst AlertDialogContext = React.createContext<AlertDialogContextValue | null>(null);\n\nfunction useAlertDialog(): AlertDialogContextValue {\n const ctx = React.useContext(AlertDialogContext);\n if (!ctx) throw new Error(\"AlertDialog parts must be used within <AlertDialog>\");\n return ctx;\n}\n\nexport interface AlertDialogProps {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n children?: React.ReactNode;\n}\n\nexport function AlertDialog({ open, defaultOpen = false, onOpenChange, children }: AlertDialogProps) {\n const [isOpen, setOpen] = useControllableState<boolean>({\n ...(open !== undefined ? { value: open } : {}),\n defaultValue: defaultOpen,\n ...(onOpenChange ? { onChange: onOpenChange } : {}),\n });\n const triggerRef = React.useRef<HTMLElement | null>(null);\n return (\n <AlertDialogContext.Provider value={{ open: isOpen, setOpen, triggerRef }}>\n {children}\n </AlertDialogContext.Provider>\n );\n}\n\nexport interface AlertDialogTriggerProps extends React.ComponentProps<\"button\"> {\n asChild?: boolean;\n}\n\nexport function AlertDialogTrigger({ asChild = false, onClick, ...props }: AlertDialogTriggerProps) {\n const { open, setOpen, triggerRef } = useAlertDialog();\n const Comp = asChild ? Slot : \"button\";\n return (\n <Comp\n data-slot=\"alert-dialog-trigger\"\n aria-expanded={open}\n ref={triggerRef as React.Ref<HTMLButtonElement>}\n onClick={(e: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(e);\n setOpen(true);\n }}\n {...(props as Record<string, unknown>)}\n />\n );\n}\n\nexport interface AlertDialogOverlayProps extends React.ComponentProps<\"div\"> {}\n\nexport function AlertDialogOverlay({ className, ...props }: AlertDialogOverlayProps) {\n const { open, setOpen } = useAlertDialog();\n return (\n <div\n data-slot=\"alert-dialog-overlay\"\n data-state={open ? \"open\" : \"closed\"}\n onClick={() => setOpen(false)}\n className={cn(ALERT_DIALOG_OVERLAY, className)}\n {...(props as Record<string, unknown>)}\n />\n );\n}\n\nexport interface AlertDialogContentProps extends React.ComponentProps<\"div\"> {}\n\nexport function AlertDialogContent({ className, children, ...props }: AlertDialogContentProps) {\n const { open, setOpen, triggerRef } = useAlertDialog();\n const { isPresent, ref: presRef } = usePresence(open);\n const focusRef = useFocusScope(open, { returnFocusTo: triggerRef });\n const dismissRef = useDismissableLayer({\n enabled: open,\n onDismiss: () => setOpen(false),\n exclude: [triggerRef],\n modal: true,\n });\n if (!isPresent) return null;\n return (\n <Portal>\n <AlertDialogOverlay />\n <div\n data-slot=\"alert-dialog-content\"\n role=\"alertdialog\"\n aria-modal=\"true\"\n data-state={open ? \"open\" : \"closed\"}\n ref={mergeRefs(focusRef, dismissRef, presRef as React.Ref<HTMLDivElement>)}\n className={cn(ALERT_DIALOG_CONTENT, className)}\n {...(props as Record<string, unknown>)}\n >\n <DismissableLayerScope layer={dismissRef}>{children}</DismissableLayerScope>\n </div>\n </Portal>\n );\n}\n\nexport function AlertDialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"alert-dialog-header\"\n className={cn(ALERT_DIALOG_HEADER, className)}\n {...props}\n />\n );\n}\n\nexport function AlertDialogFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"alert-dialog-footer\"\n className={cn(ALERT_DIALOG_FOOTER, className)}\n {...props}\n />\n );\n}\n\nexport function AlertDialogTitle({ className, ...props }: React.ComponentProps<\"h2\">) {\n return (\n <h2\n data-slot=\"alert-dialog-title\"\n className={cn(ALERT_DIALOG_TITLE, className)}\n {...props}\n />\n );\n}\n\nexport function AlertDialogDescription({ className, ...props }: React.ComponentProps<\"p\">) {\n return (\n <p\n data-slot=\"alert-dialog-description\"\n className={cn(ALERT_DIALOG_DESCRIPTION, className)}\n {...props}\n />\n );\n}\n\nexport interface AlertDialogActionProps extends React.ComponentProps<\"button\"> {}\n\nexport function AlertDialogAction({ className, onClick, ...props }: AlertDialogActionProps) {\n const { setOpen } = useAlertDialog();\n return (\n <button\n data-slot=\"alert-dialog-action\"\n className={cn(buttonVariants(), className)}\n onClick={(e: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(e);\n setOpen(false);\n }}\n {...(props as Record<string, unknown>)}\n />\n );\n}\n\nexport interface AlertDialogCancelProps extends React.ComponentProps<\"button\"> {}\n\nexport function AlertDialogCancel({ className, onClick, ...props }: AlertDialogCancelProps) {\n const { setOpen } = useAlertDialog();\n return (\n <button\n data-slot=\"alert-dialog-cancel\"\n className={cn(buttonVariants({ variant: \"outline\" }), className)}\n onClick={(e: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(e);\n setOpen(false);\n }}\n {...(props as Record<string, unknown>)}\n />\n );\n}\n"],"mappings":";AAkDI,cAwDA,YAxDA;AAhDJ,YAAY,WAAW;AACvB,SAAS,UAAU;AACnB,SAAS,sBAAsB;AAC/B,SAAS,4BAA4B;AACrC,SAAS,uBAAuB,2BAA2B;AAC3D,SAAS,qBAAqB;AAC9B,SAAS,mBAAmB;AAC5B,SAAS,iBAAiB;AAC1B,SAAS,cAAc;AACvB,SAAS,YAAY;AACrB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAQP,MAAM,qBAAqB,MAAM,cAA8C,IAAI;AAEnF,SAAS,iBAA0C;AACjD,QAAM,MAAM,MAAM,WAAW,kBAAkB;AAC/C,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,qDAAqD;AAC/E,SAAO;AACT;AASO,SAAS,YAAY,EAAE,MAAM,cAAc,OAAO,cAAc,SAAS,GAAqB;AACnG,QAAM,CAAC,QAAQ,OAAO,IAAI,qBAA8B;AAAA,IACtD,GAAI,SAAS,SAAY,EAAE,OAAO,KAAK,IAAI,CAAC;AAAA,IAC5C,cAAc;AAAA,IACd,GAAI,eAAe,EAAE,UAAU,aAAa,IAAI,CAAC;AAAA,EACnD,CAAC;AACD,QAAM,aAAa,MAAM,OAA2B,IAAI;AACxD,SACE,oBAAC,mBAAmB,UAAnB,EAA4B,OAAO,EAAE,MAAM,QAAQ,SAAS,WAAW,GACrE,UACH;AAEJ;AAMO,SAAS,mBAAmB,EAAE,UAAU,OAAO,SAAS,GAAG,MAAM,GAA4B;AAClG,QAAM,EAAE,MAAM,SAAS,WAAW,IAAI,eAAe;AACrD,QAAM,OAAO,UAAU,OAAO;AAC9B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,iBAAe;AAAA,MACf,KAAK;AAAA,MACL,SAAS,CAAC,MAA2C;AACnD,kBAAU,CAAC;AACX,gBAAQ,IAAI;AAAA,MACd;AAAA,MACC,GAAI;AAAA;AAAA,EACP;AAEJ;AAIO,SAAS,mBAAmB,EAAE,WAAW,GAAG,MAAM,GAA4B;AACnF,QAAM,EAAE,MAAM,QAAQ,IAAI,eAAe;AACzC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,cAAY,OAAO,SAAS;AAAA,MAC5B,SAAS,MAAM,QAAQ,KAAK;AAAA,MAC5B,WAAW,GAAG,sBAAsB,SAAS;AAAA,MAC5C,GAAI;AAAA;AAAA,EACP;AAEJ;AAIO,SAAS,mBAAmB,EAAE,WAAW,UAAU,GAAG,MAAM,GAA4B;AAC7F,QAAM,EAAE,MAAM,SAAS,WAAW,IAAI,eAAe;AACrD,QAAM,EAAE,WAAW,KAAK,QAAQ,IAAI,YAAY,IAAI;AACpD,QAAM,WAAW,cAAc,MAAM,EAAE,eAAe,WAAW,CAAC;AAClE,QAAM,aAAa,oBAAoB;AAAA,IACrC,SAAS;AAAA,IACT,WAAW,MAAM,QAAQ,KAAK;AAAA,IAC9B,SAAS,CAAC,UAAU;AAAA,IACpB,OAAO;AAAA,EACT,CAAC;AACD,MAAI,CAAC,UAAW,QAAO;AACvB,SACE,qBAAC,UACC;AAAA,wBAAC,sBAAmB;AAAA,IACpB;AAAA,MAAC;AAAA;AAAA,QACC,aAAU;AAAA,QACV,MAAK;AAAA,QACL,cAAW;AAAA,QACX,cAAY,OAAO,SAAS;AAAA,QAC5B,KAAK,UAAU,UAAU,YAAY,OAAoC;AAAA,QACzE,WAAW,GAAG,sBAAsB,SAAS;AAAA,QAC5C,GAAI;AAAA,QAEL,8BAAC,yBAAsB,OAAO,YAAa,UAAS;AAAA;AAAA,IACtD;AAAA,KACF;AAEJ;AAEO,SAAS,kBAAkB,EAAE,WAAW,GAAG,MAAM,GAAgC;AACtF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,qBAAqB,SAAS;AAAA,MAC3C,GAAG;AAAA;AAAA,EACN;AAEJ;AAEO,SAAS,kBAAkB,EAAE,WAAW,GAAG,MAAM,GAAgC;AACtF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,qBAAqB,SAAS;AAAA,MAC3C,GAAG;AAAA;AAAA,EACN;AAEJ;AAEO,SAAS,iBAAiB,EAAE,WAAW,GAAG,MAAM,GAA+B;AACpF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,oBAAoB,SAAS;AAAA,MAC1C,GAAG;AAAA;AAAA,EACN;AAEJ;AAEO,SAAS,uBAAuB,EAAE,WAAW,GAAG,MAAM,GAA8B;AACzF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,0BAA0B,SAAS;AAAA,MAChD,GAAG;AAAA;AAAA,EACN;AAEJ;AAIO,SAAS,kBAAkB,EAAE,WAAW,SAAS,GAAG,MAAM,GAA2B;AAC1F,QAAM,EAAE,QAAQ,IAAI,eAAe;AACnC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,eAAe,GAAG,SAAS;AAAA,MACzC,SAAS,CAAC,MAA2C;AACnD,kBAAU,CAAC;AACX,gBAAQ,KAAK;AAAA,MACf;AAAA,MACC,GAAI;AAAA;AAAA,EACP;AAEJ;AAIO,SAAS,kBAAkB,EAAE,WAAW,SAAS,GAAG,MAAM,GAA2B;AAC1F,QAAM,EAAE,QAAQ,IAAI,eAAe;AACnC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,eAAe,EAAE,SAAS,UAAU,CAAC,GAAG,SAAS;AAAA,MAC/D,SAAS,CAAC,MAA2C;AACnD,kBAAU,CAAC;AACX,gBAAQ,KAAK;AAAA,MACf;AAAA,MACC,GAAI;AAAA;AAAA,EACP;AAEJ;","names":[]}
@@ -5,12 +5,14 @@ const AT = {
5
5
  md: {
6
6
  rail: "hidden md:flex",
7
7
  tabBar: "md:hidden",
8
+ topBar: "hidden md:block",
8
9
  reserve: "pb-[calc(var(--shell-tab-bar-h)+env(safe-area-inset-bottom,0px))] md:pb-0",
9
10
  banner: "md:px-8"
10
11
  },
11
12
  lg: {
12
13
  rail: "hidden lg:flex",
13
14
  tabBar: "lg:hidden",
15
+ topBar: "hidden lg:block",
14
16
  reserve: "pb-[calc(var(--shell-tab-bar-h)+env(safe-area-inset-bottom,0px))] lg:pb-0",
15
17
  banner: "lg:px-8"
16
18
  }
@@ -19,6 +21,7 @@ const present = (node) => node !== null && node !== void 0 && node !== false;
19
21
  function AppShell({
20
22
  rail,
21
23
  tabBar,
24
+ topBar,
22
25
  banner,
23
26
  breakpoint = "lg",
24
27
  mainProps,
@@ -55,6 +58,14 @@ function AppShell({
55
58
  "data-slot": "app-shell-content",
56
59
  className: cn("flex min-w-0 flex-1 flex-col", present(tabBar) && at.reserve),
57
60
  children: [
61
+ present(topBar) && /* @__PURE__ */ jsx(
62
+ "div",
63
+ {
64
+ "data-slot": "app-shell-top-bar",
65
+ className: cn("sticky top-[var(--ev-shell-offset,0px)] z-30", at.topBar),
66
+ children: topBar
67
+ }
68
+ ),
58
69
  present(banner) && /* @__PURE__ */ jsx(
59
70
  "div",
60
71
  {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/app-shell.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../lib/cn\";\n\n/** Where the shell swaps the bottom tab bar for the rail. */\nexport type ShellBreakpoint = \"md\" | \"lg\";\n\nexport interface AppShellProps extends React.ComponentProps<\"div\"> {\n /** The desktop rail — a `ShellNav`. Shown from `breakpoint` up. */\n rail?: React.ReactNode;\n /** The phone's tab bar — a `BottomTabBar`. Pinned to the viewport bottom below `breakpoint`. */\n tabBar?: React.ReactNode;\n /** Strips above the page (`SystemBanner`s). An empty slot takes no room. */\n banner?: React.ReactNode;\n /** Default `lg`. */\n breakpoint?: ShellBreakpoint;\n /** Props for the `<main>` the page renders into (an `id` for a skip link, say). */\n mainProps?: React.ComponentProps<\"main\">;\n}\n\n// Tailwind only sees whole class strings, so each breakpoint spells its own.\nconst AT = {\n md: {\n rail: \"hidden md:flex\",\n tabBar: \"md:hidden\",\n reserve: \"pb-[calc(var(--shell-tab-bar-h)+env(safe-area-inset-bottom,0px))] md:pb-0\",\n banner: \"md:px-8\",\n },\n lg: {\n rail: \"hidden lg:flex\",\n tabBar: \"lg:hidden\",\n reserve: \"pb-[calc(var(--shell-tab-bar-h)+env(safe-area-inset-bottom,0px))] lg:pb-0\",\n banner: \"lg:px-8\",\n },\n} as const;\n\nconst present = (node: React.ReactNode) => node !== null && node !== undefined && node !== false;\n\n/**\n * The signed-in app's frame: a rail beside a content column on a wide screen, a\n * bottom tab bar under it on a phone. Layout only — the nav, the banners and the\n * page are slots, so the shell knows nothing about routes, roles or data.\n *\n * Fills the viewport less `--ev-shell-offset` (the room a host shell's own header\n * takes); the rail sticks under that offset at full remaining height and scrolls\n * by itself when it cannot fit. The column reserves the tab bar's height plus\n * the bottom safe area, so the bar never covers content.\n */\nexport function AppShell({\n rail,\n tabBar,\n banner,\n breakpoint = \"lg\",\n mainProps,\n className,\n children,\n ...props\n}: AppShellProps) {\n const at = AT[breakpoint];\n return (\n <div\n data-slot=\"app-shell\"\n data-breakpoint={breakpoint}\n className={cn(\n \"flex min-h-[calc(100dvh-var(--ev-shell-offset,0px))] bg-background\",\n className,\n )}\n {...props}\n >\n {present(rail) && (\n <div\n data-slot=\"app-shell-rail\"\n className={cn(\n \"sticky top-[var(--ev-shell-offset,0px)] h-[calc(100dvh-var(--ev-shell-offset,0px))] shrink-0 self-start\",\n at.rail,\n )}\n >\n {rail}\n </div>\n )}\n <div\n data-slot=\"app-shell-content\"\n className={cn(\"flex min-w-0 flex-1 flex-col\", present(tabBar) && at.reserve)}\n >\n {present(banner) && (\n <div\n data-slot=\"app-shell-banner\"\n className={cn(\"flex flex-col gap-2 px-4 pt-4 empty:hidden\", at.banner)}\n >\n {banner}\n </div>\n )}\n <main {...mainProps} className={cn(\"min-w-0 flex-1\", mainProps?.className)}>\n {children}\n </main>\n </div>\n {present(tabBar) && (\n <div\n data-slot=\"app-shell-tab-bar\"\n className={cn(\"fixed inset-x-0 bottom-0 z-40\", at.tabBar)}\n >\n {tabBar}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"AAqEQ,cAUF,YAVE;AArER,YAAY,WAAW;AACvB,SAAS,UAAU;AAmBnB,MAAM,KAAK;AAAA,EACT,IAAI;AAAA,IACF,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AAAA,EACA,IAAI;AAAA,IACF,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AACF;AAEA,MAAM,UAAU,CAAC,SAA0B,SAAS,QAAQ,SAAS,UAAa,SAAS;AAYpF,SAAS,SAAS;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAkB;AAChB,QAAM,KAAK,GAAG,UAAU;AACxB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,mBAAiB;AAAA,MACjB,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA,gBAAQ,IAAI,KACX;AAAA,UAAC;AAAA;AAAA,YACC,aAAU;AAAA,YACV,WAAW;AAAA,cACT;AAAA,cACA,GAAG;AAAA,YACL;AAAA,YAEC;AAAA;AAAA,QACH;AAAA,QAEF;AAAA,UAAC;AAAA;AAAA,YACC,aAAU;AAAA,YACV,WAAW,GAAG,gCAAgC,QAAQ,MAAM,KAAK,GAAG,OAAO;AAAA,YAE1E;AAAA,sBAAQ,MAAM,KACb;AAAA,gBAAC;AAAA;AAAA,kBACC,aAAU;AAAA,kBACV,WAAW,GAAG,8CAA8C,GAAG,MAAM;AAAA,kBAEpE;AAAA;AAAA,cACH;AAAA,cAEF,oBAAC,UAAM,GAAG,WAAW,WAAW,GAAG,kBAAkB,WAAW,SAAS,GACtE,UACH;AAAA;AAAA;AAAA,QACF;AAAA,QACC,QAAQ,MAAM,KACb;AAAA,UAAC;AAAA;AAAA,YACC,aAAU;AAAA,YACV,WAAW,GAAG,iCAAiC,GAAG,MAAM;AAAA,YAEvD;AAAA;AAAA,QACH;AAAA;AAAA;AAAA,EAEJ;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../src/components/app-shell.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../lib/cn\";\n\n/** Where the shell swaps the bottom tab bar for the rail. */\nexport type ShellBreakpoint = \"md\" | \"lg\";\n\nexport interface AppShellProps extends React.ComponentProps<\"div\"> {\n /** The desktop rail — a `ShellNav`. Shown from `breakpoint` up. */\n rail?: React.ReactNode;\n /** The phone's tab bar — a `BottomTabBar`. Pinned to the viewport bottom below `breakpoint`. */\n tabBar?: React.ReactNode;\n /** A `TopBar`, pinned over the column from `breakpoint` up (a phone titles its screen with `MobileAppBar`). */\n topBar?: React.ReactNode;\n /** Strips above the page (`SystemBanner`s). An empty slot takes no room. */\n banner?: React.ReactNode;\n /** Default `lg`. */\n breakpoint?: ShellBreakpoint;\n /** Props for the `<main>` the page renders into (an `id` for a skip link, say). */\n mainProps?: React.ComponentProps<\"main\">;\n}\n\n// Tailwind only sees whole class strings, so each breakpoint spells its own.\nconst AT = {\n md: {\n rail: \"hidden md:flex\",\n tabBar: \"md:hidden\",\n topBar: \"hidden md:block\",\n reserve: \"pb-[calc(var(--shell-tab-bar-h)+env(safe-area-inset-bottom,0px))] md:pb-0\",\n banner: \"md:px-8\",\n },\n lg: {\n rail: \"hidden lg:flex\",\n tabBar: \"lg:hidden\",\n topBar: \"hidden lg:block\",\n reserve: \"pb-[calc(var(--shell-tab-bar-h)+env(safe-area-inset-bottom,0px))] lg:pb-0\",\n banner: \"lg:px-8\",\n },\n} as const;\n\nconst present = (node: React.ReactNode) => node !== null && node !== undefined && node !== false;\n\n/**\n * The signed-in app's frame: a rail beside a content column on a wide screen, a\n * bottom tab bar under it on a phone. Layout only — the nav, the top bar, the banners\n * and the page are slots, so the shell knows nothing about routes, roles or data.\n *\n * Fills the viewport less `--ev-shell-offset` (the room a host shell's own header\n * takes); the rail sticks under that offset at full remaining height and scrolls\n * by itself when it cannot fit. The column reserves the tab bar's height plus\n * the bottom safe area, so the bar never covers content.\n */\nexport function AppShell({\n rail,\n tabBar,\n topBar,\n banner,\n breakpoint = \"lg\",\n mainProps,\n className,\n children,\n ...props\n}: AppShellProps) {\n const at = AT[breakpoint];\n return (\n <div\n data-slot=\"app-shell\"\n data-breakpoint={breakpoint}\n className={cn(\n \"flex min-h-[calc(100dvh-var(--ev-shell-offset,0px))] bg-background\",\n className,\n )}\n {...props}\n >\n {present(rail) && (\n <div\n data-slot=\"app-shell-rail\"\n className={cn(\n \"sticky top-[var(--ev-shell-offset,0px)] h-[calc(100dvh-var(--ev-shell-offset,0px))] shrink-0 self-start\",\n at.rail,\n )}\n >\n {rail}\n </div>\n )}\n <div\n data-slot=\"app-shell-content\"\n className={cn(\"flex min-w-0 flex-1 flex-col\", present(tabBar) && at.reserve)}\n >\n {present(topBar) && (\n <div\n data-slot=\"app-shell-top-bar\"\n className={cn(\"sticky top-[var(--ev-shell-offset,0px)] z-30\", at.topBar)}\n >\n {topBar}\n </div>\n )}\n {present(banner) && (\n <div\n data-slot=\"app-shell-banner\"\n className={cn(\"flex flex-col gap-2 px-4 pt-4 empty:hidden\", at.banner)}\n >\n {banner}\n </div>\n )}\n <main {...mainProps} className={cn(\"min-w-0 flex-1\", mainProps?.className)}>\n {children}\n </main>\n </div>\n {present(tabBar) && (\n <div\n data-slot=\"app-shell-tab-bar\"\n className={cn(\"fixed inset-x-0 bottom-0 z-40\", at.tabBar)}\n >\n {tabBar}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"AA0EQ,cAUF,YAVE;AA1ER,YAAY,WAAW;AACvB,SAAS,UAAU;AAqBnB,MAAM,KAAK;AAAA,EACT,IAAI;AAAA,IACF,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AAAA,EACA,IAAI;AAAA,IACF,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AACF;AAEA,MAAM,UAAU,CAAC,SAA0B,SAAS,QAAQ,SAAS,UAAa,SAAS;AAYpF,SAAS,SAAS;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAkB;AAChB,QAAM,KAAK,GAAG,UAAU;AACxB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,mBAAiB;AAAA,MACjB,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA,gBAAQ,IAAI,KACX;AAAA,UAAC;AAAA;AAAA,YACC,aAAU;AAAA,YACV,WAAW;AAAA,cACT;AAAA,cACA,GAAG;AAAA,YACL;AAAA,YAEC;AAAA;AAAA,QACH;AAAA,QAEF;AAAA,UAAC;AAAA;AAAA,YACC,aAAU;AAAA,YACV,WAAW,GAAG,gCAAgC,QAAQ,MAAM,KAAK,GAAG,OAAO;AAAA,YAE1E;AAAA,sBAAQ,MAAM,KACb;AAAA,gBAAC;AAAA;AAAA,kBACC,aAAU;AAAA,kBACV,WAAW,GAAG,gDAAgD,GAAG,MAAM;AAAA,kBAEtE;AAAA;AAAA,cACH;AAAA,cAED,QAAQ,MAAM,KACb;AAAA,gBAAC;AAAA;AAAA,kBACC,aAAU;AAAA,kBACV,WAAW,GAAG,8CAA8C,GAAG,MAAM;AAAA,kBAEpE;AAAA;AAAA,cACH;AAAA,cAEF,oBAAC,UAAM,GAAG,WAAW,WAAW,GAAG,kBAAkB,WAAW,SAAS,GACtE,UACH;AAAA;AAAA;AAAA,QACF;AAAA,QACC,QAAQ,MAAM,KACb;AAAA,UAAC;AAAA;AAAA,YACC,aAAU;AAAA,YACV,WAAW,GAAG,iCAAiC,GAAG,MAAM;AAAA,YAEvD;AAAA;AAAA,QACH;AAAA;AAAA;AAAA,EAEJ;AAEJ;","names":[]}