@evinvest/uikit 0.26.0 → 0.29.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +148 -17
- package/dist/components/account-menu.js +70 -0
- package/dist/components/account-menu.js.map +1 -0
- package/dist/components/alert-dialog.js +4 -3
- package/dist/components/alert-dialog.js.map +1 -1
- package/dist/components/app-shell.js +11 -0
- package/dist/components/app-shell.js.map +1 -1
- package/dist/components/auth-wall.js +42 -0
- package/dist/components/auth-wall.js.map +1 -0
- package/dist/components/checkbox.js +2 -0
- package/dist/components/checkbox.js.map +1 -1
- package/dist/components/command.js +180 -25
- package/dist/components/command.js.map +1 -1
- package/dist/components/context-menu.js +8 -8
- package/dist/components/context-menu.js.map +1 -1
- package/dist/components/dialog.js +4 -3
- package/dist/components/dialog.js.map +1 -1
- package/dist/components/drawer.js +9 -5
- package/dist/components/drawer.js.map +1 -1
- package/dist/components/dropdown-menu.js +13 -10
- package/dist/components/dropdown-menu.js.map +1 -1
- package/dist/components/field.js +3 -8
- package/dist/components/field.js.map +1 -1
- package/dist/components/info-tip.js +2 -2
- package/dist/components/info-tip.js.map +1 -1
- package/dist/components/input.js +2 -0
- package/dist/components/input.js.map +1 -1
- package/dist/components/menubar.js +18 -5
- package/dist/components/menubar.js.map +1 -1
- package/dist/components/native-select.js +2 -0
- package/dist/components/native-select.js.map +1 -1
- package/dist/components/navigation-menu.js +2 -2
- package/dist/components/navigation-menu.js.map +1 -1
- package/dist/components/popover.js +2 -2
- package/dist/components/popover.js.map +1 -1
- package/dist/components/select.js +4 -2
- package/dist/components/select.js.map +1 -1
- package/dist/components/sheet.js +4 -3
- package/dist/components/sheet.js.map +1 -1
- package/dist/components/sonner.js +1 -0
- package/dist/components/sonner.js.map +1 -1
- package/dist/components/switch.js +2 -0
- package/dist/components/switch.js.map +1 -1
- package/dist/components/table.js +24 -7
- package/dist/components/table.js.map +1 -1
- package/dist/components/textarea.js +2 -0
- package/dist/components/textarea.js.map +1 -1
- package/dist/components/toggle-group.js +1 -1
- package/dist/components/toggle-group.js.map +1 -1
- package/dist/components/top-bar.js +21 -0
- package/dist/components/top-bar.js.map +1 -0
- package/dist/generated/command.js +2 -2
- package/dist/generated/command.js.map +1 -1
- package/dist/generated/context-menu.js +1 -1
- package/dist/generated/context-menu.js.map +1 -1
- package/dist/generated/dropdown-menu.js +1 -1
- package/dist/generated/dropdown-menu.js.map +1 -1
- package/dist/generated/menubar.js +1 -1
- package/dist/generated/menubar.js.map +1 -1
- package/dist/generated/table.js +20 -3
- package/dist/generated/table.js.map +1 -1
- package/dist/generated/toggle.js +2 -2
- package/dist/generated/toggle.js.map +1 -1
- package/dist/index.d.ts +179 -76
- package/dist/index.js +6 -4
- package/dist/primitives/dismissable-layer.js +80 -15
- package/dist/primitives/dismissable-layer.js.map +1 -1
- package/dist/primitives/labelable.js +24 -0
- package/dist/primitives/labelable.js.map +1 -0
- package/dist/primitives/use-roving-focus.js +13 -7
- package/dist/primitives/use-roving-focus.js.map +1 -1
- package/package.json +1 -1
- package/styles/ev.css +4 -2
- package/styles/theme.css +7 -0
- package/styles/tokens.css +15 -3
- package/dist/components/chart.js +0 -151
- package/dist/components/chart.js.map +0 -1
- package/dist/components/resizable.js +0 -193
- package/dist/components/resizable.js.map +0 -1
- package/dist/generated/chart.js +0 -9
- package/dist/generated/chart.js.map +0 -1
- package/dist/generated/resizable.js +0 -11
- package/dist/generated/resizable.js.map +0 -1
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`
|
|
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,93 @@ 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 and ranks its items on the client, as fzf does:
|
|
544
|
+
each space-separated term of the query must appear in the item's `value` as a
|
|
545
|
+
case-insensitive subsequence, and runs and word starts rank above scattered
|
|
546
|
+
matches. Ties keep your order. Ranking is CSS `order` within `CommandList` /
|
|
547
|
+
`CommandGroup`, which the arrows follow. When the
|
|
548
|
+
rows are already the answer to the query — a search endpoint, a ranking of your
|
|
549
|
+
own — pass `shouldFilter={false}` and own the query state:
|
|
550
|
+
|
|
551
|
+
```tsx
|
|
552
|
+
const [query, setQuery] = useState("");
|
|
553
|
+
const { rows, loading } = useInvestorSearch(query); // your fetch, debounced or not
|
|
554
|
+
|
|
555
|
+
<Command shouldFilter={false} search={query} onSearchChange={setQuery}>
|
|
556
|
+
<CommandInput placeholder="Search investors…" />
|
|
557
|
+
<CommandList>
|
|
558
|
+
{loading ? <Spinner /> : <CommandEmpty>No investors found.</CommandEmpty>}
|
|
559
|
+
{rows.map((r) => (
|
|
560
|
+
<CommandItem key={r.id} value={r.id} onSelect={pick}>
|
|
561
|
+
{r.name}
|
|
562
|
+
</CommandItem>
|
|
563
|
+
))}
|
|
564
|
+
</CommandList>
|
|
565
|
+
</Command>
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
Then nothing is hidden or reordered: every mounted item renders in your order,
|
|
569
|
+
and `CommandEmpty` shows once a query is typed and no item is mounted. `value`
|
|
570
|
+
is then just what `onSelect` receives.
|
|
571
|
+
|
|
572
|
+
Keyboard works the same in both modes, as an ARIA combobox: focus stays in
|
|
573
|
+
`CommandInput`, which points at the highlighted row via
|
|
574
|
+
`aria-activedescendant`; ArrowUp / ArrowDown move the highlight (clamped,
|
|
575
|
+
disabled rows skipped), Enter fires the row's `onSelect`, and hover moves it
|
|
576
|
+
too. Home / End stay with the caret, and keys from any other focusable inside
|
|
577
|
+
Command (a button in `CommandEmpty`) are left alone. Rows stay out of the Tab order on purpose. The highlight sits on
|
|
578
|
+
the first row until the user moves it, so results that arrive after the
|
|
579
|
+
keystroke put Enter on the top hit. Without a `CommandInput` there is nothing to
|
|
580
|
+
hold focus, so a bare list is pointer-only. The live demo is `CommandDemo` in
|
|
581
|
+
`example/`.
|
|
582
|
+
|
|
471
583
|
## Rust ↔ TS parity
|
|
472
584
|
|
|
473
585
|
The Rust crate is the source of truth; this package preserves its _semantics_
|
|
@@ -484,7 +596,7 @@ element across the two ports.
|
|
|
484
596
|
| overlay placement | inline `position:fixed` + `data-side` | `Portal` + `useFloating` |
|
|
485
597
|
| dismiss / focus trap | full-screen backdrop / native order | `useDismissableLayer` / `useFocusScope` |
|
|
486
598
|
|
|
487
|
-
### Component inventory (all
|
|
599
|
+
### Component inventory (all 75 bricks)
|
|
488
600
|
|
|
489
601
|
- **Tier A — static (23):** badge, button, button-group, card, input, textarea,
|
|
490
602
|
label, field, separator, skeleton, spinner, kbd, table, container, alert,
|
|
@@ -496,15 +608,14 @@ element across the two ports.
|
|
|
496
608
|
- **Tier C — overlay (13):** tooltip, popover, hover-card, dropdown-menu,
|
|
497
609
|
context-menu, menubar, navigation-menu, dialog, alert-dialog, sheet, drawer,
|
|
498
610
|
select, command.
|
|
499
|
-
- **Tier D — engines (
|
|
500
|
-
form, resizable.
|
|
611
|
+
- **Tier D — engines (4):** calendar, date-time-picker, sonner (toaster), form.
|
|
501
612
|
- **Site chrome (7):** header (marketing / compact density, plus `hideNav`),
|
|
502
613
|
footer, logo, and the shared status pages — `StatusScreen` with the `NotFound`
|
|
503
614
|
/ `Forbidden` / `ServerError` presets (404 / 403 / 500). The kit ships no
|
|
504
615
|
artwork: `Logo` masks whatever `--brand-mark` / `--brand-aspect` the consumer
|
|
505
616
|
declares beside the palette, and the status pages use it for their mark.
|
|
506
|
-
- **App shell — TS-only (
|
|
507
|
-
nav-badge (`NavBadge` / `NavDot`), mobile-app-bar, page-frame (`PageFrame` /
|
|
617
|
+
- **App shell — TS-only (12):** app-shell, shell-nav, bottom-tab-bar,
|
|
618
|
+
nav-badge (`NavBadge` / `NavDot`), top-bar, account-menu, mobile-app-bar, page-frame (`PageFrame` /
|
|
508
619
|
`PageHeading` / `SectionLabel`), section-nav, resource-error, system-banner,
|
|
509
620
|
settled — see [App shell](#app-shell).
|
|
510
621
|
- **Features — composed screens (1):** terminal — a trading terminal over an
|
|
@@ -536,10 +647,31 @@ measuring needs host-only `web-sys`). Known gaps:
|
|
|
536
647
|
no viewport-measured floating, native focus order (no trap). TS overlays use a
|
|
537
648
|
real `Portal`, single-flip `useFloating` (absolute in the document, so it
|
|
538
649
|
scrolls with the page natively), `useDismissableLayer`, and `useFocusScope`.
|
|
539
|
-
- **
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
650
|
+
- **overlay layers (TS):** every TS overlay is on one dismissable-layer stack.
|
|
651
|
+
A layer rendered inside another sits above it, even when both open in the
|
|
652
|
+
same commit; unrelated layers stack in the order they opened. Escape closes
|
|
653
|
+
the layer that was on top when it was pressed — a menu, `Select` or
|
|
654
|
+
`Popover` inside a `Dialog`, `Sheet` or `Drawer` closes first, the modal on
|
|
655
|
+
the next Escape — and a press inside a layer above is not "outside" the
|
|
656
|
+
ones below. A modal (`Dialog`, `AlertDialog`, `Sheet`, `Drawer`,
|
|
657
|
+
`CommandDialog`) bars the layers below from pointer-downs while it is open:
|
|
658
|
+
a click on its backdrop closes it alone. `Drawer` and `CommandDialog` close
|
|
659
|
+
on a click on their own scrim, not on any outside pointer-down. Toasts are
|
|
660
|
+
outside every layer: they never take Escape, and a click on one closes
|
|
661
|
+
nothing. The stack is shared through `globalThis`, so a micro-frontend
|
|
662
|
+
bundling its own uikit copy sees the host's layers; parent links are a
|
|
663
|
+
React context, so across two copies the nested layer's place falls back to
|
|
664
|
+
opening order. A non-uikit overlay joins through `useDismissableLayer`
|
|
665
|
+
(`modal`, `pointerOutside`) and `DismissableLayerScope`; mark a floating
|
|
666
|
+
region that should dismiss nothing with `LAYER_IGNORE_ATTR`.
|
|
667
|
+
- **overlay layers (Rust):** `Select`, `Popover`, `Dialog` and `DropdownMenu`
|
|
668
|
+
share one dismissable-layer stack per `VirtualDom`
|
|
669
|
+
(`primitives::use_dismissable_layer`): Escape and a click on a backdrop act
|
|
670
|
+
on the top layer only, so a `Select` inside a `Dialog` closes first. There is
|
|
671
|
+
no document listener — an Escape counts only once it reaches an open layer's
|
|
672
|
+
`onkeydown` — and `AlertDialog`, `Sheet`, `Drawer`, `ContextMenu`, `Menubar`,
|
|
673
|
+
`NavigationMenu`, `HoverCard`, `Tooltip`, `InfoTip`, `DateTimePicker`,
|
|
674
|
+
`CommandDialog` and `DropdownMenuSub` are not on the stack yet.
|
|
543
675
|
- **terminal:** no plotting engine and no state. `TerminalChart` is a sized
|
|
544
676
|
`relative` host that hands its element out (`ref` in TS, `id` in Rust) for a
|
|
545
677
|
consumer-chosen charting library to mount into; `OrderForm` only prevents the
|
|
@@ -585,9 +717,8 @@ measuring needs host-only `web-sys`). Known gaps:
|
|
|
585
717
|
onto an arbitrary child (no `Slot`), so it publishes them as a
|
|
586
718
|
`FormControlContext` that `Input`/`Textarea` consume; wrap a bare element and
|
|
587
719
|
you wire them yourself.
|
|
588
|
-
- **
|
|
589
|
-
|
|
590
|
-
reproduced.
|
|
720
|
+
- **carousel:** pointer-drag physics are TS-only (prev/next + keyboard in
|
|
721
|
+
Rust). Embla momentum is not reproduced.
|
|
591
722
|
- **drawer:** the Vaul-style enter/exit motion is shared by both ports via
|
|
592
723
|
`motion.css` (inlined into `tokens.css`), keyed on `data-slot` +
|
|
593
724
|
`data-vaul-drawer-direction` + `data-state`; the panel stays mounted on close
|
|
@@ -605,8 +736,8 @@ measuring needs host-only `web-sys`). Known gaps:
|
|
|
605
736
|
The lock-up is composed from `mark` + `brand` (display family) + `tagline`,
|
|
606
737
|
or passed whole as `lockup`. Brand-coloured text reads `primary-ink`.
|
|
607
738
|
- **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` /
|
|
739
|
+
`SelectValue` labels, `ListRows` / `ListRow` (a table's phone form), and the whole [app shell](#app-shell) (`AppShell`,
|
|
740
|
+
`ShellNav`, `BottomTabBar`, `NavBadge` / `NavDot`, `TopBar`, `AccountMenu`, `MobileAppBar`, `PageFrame` /
|
|
610
741
|
`PageHeading` / `SectionLabel`, `SectionNav`, `ResourceError`, `SystemBanner`,
|
|
611
742
|
`Settled`) — no Dioxus twin. Its CSS (the entrances, the markers, the motion
|
|
612
743
|
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";
|
|
@@ -70,7 +70,8 @@ function AlertDialogContent({ className, children, ...props }) {
|
|
|
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, { returnFocusTo: triggerRef });\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}
|
|
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":"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","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
|
|
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":[]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import {
|
|
4
|
+
Empty,
|
|
5
|
+
EmptyContent,
|
|
6
|
+
EmptyDescription,
|
|
7
|
+
EmptyHeader,
|
|
8
|
+
EmptyMedia,
|
|
9
|
+
EmptyTitle
|
|
10
|
+
} from "./empty.js";
|
|
11
|
+
function AuthWall({ title, description, children, ...props }) {
|
|
12
|
+
return /* @__PURE__ */ jsxs(Empty, { ...props, children: [
|
|
13
|
+
/* @__PURE__ */ jsxs(EmptyHeader, { children: [
|
|
14
|
+
/* @__PURE__ */ jsx(EmptyMedia, { variant: "icon", children: /* @__PURE__ */ jsx(LockIcon, {}) }),
|
|
15
|
+
/* @__PURE__ */ jsx(EmptyTitle, { children: title }),
|
|
16
|
+
/* @__PURE__ */ jsx(EmptyDescription, { children: description })
|
|
17
|
+
] }),
|
|
18
|
+
/* @__PURE__ */ jsx(EmptyContent, { children })
|
|
19
|
+
] });
|
|
20
|
+
}
|
|
21
|
+
function LockIcon() {
|
|
22
|
+
return /* @__PURE__ */ jsxs(
|
|
23
|
+
"svg",
|
|
24
|
+
{
|
|
25
|
+
viewBox: "0 0 24 24",
|
|
26
|
+
fill: "none",
|
|
27
|
+
stroke: "currentColor",
|
|
28
|
+
strokeWidth: 2,
|
|
29
|
+
strokeLinecap: "round",
|
|
30
|
+
strokeLinejoin: "round",
|
|
31
|
+
"aria-hidden": true,
|
|
32
|
+
children: [
|
|
33
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "11", width: "18", height: "11", rx: "2", ry: "2" }),
|
|
34
|
+
/* @__PURE__ */ jsx("path", { d: "M7 11V7a5 5 0 0 1 10 0v4" })
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
export {
|
|
40
|
+
AuthWall
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=auth-wall.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/auth-wall.tsx"],"sourcesContent":["import * as React from \"react\";\nimport {\n Empty,\n EmptyContent,\n EmptyDescription,\n EmptyHeader,\n EmptyMedia,\n EmptyTitle,\n} from \"./empty\";\n\n/**\n * What a section shows a caller it is not for: an `Empty` with a lock, a title, why,\n * and an action slot. A guest gets the sign-in button there; a signed-in caller\n * without the permission gets no action, or the host's own (request access).\n */\nexport interface AuthWallProps extends Omit<React.ComponentProps<\"div\">, \"title\"> {\n title: React.ReactNode;\n description: React.ReactNode;\n}\n\nexport function AuthWall({ title, description, children, ...props }: AuthWallProps) {\n return (\n <Empty {...props}>\n <EmptyHeader>\n <EmptyMedia variant=\"icon\">\n <LockIcon />\n </EmptyMedia>\n <EmptyTitle>{title}</EmptyTitle>\n <EmptyDescription>{description}</EmptyDescription>\n </EmptyHeader>\n <EmptyContent>{children}</EmptyContent>\n </Empty>\n );\n}\n\n// lucide `lock`, inlined so the kit keeps its zero-icon-dep footprint.\nfunction LockIcon() {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden\n >\n <rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\" />\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\" />\n </svg>\n );\n}\n"],"mappings":"AAuBM,SAEI,KAFJ;AAvBN,YAAY,WAAW;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAYA,SAAS,SAAS,EAAE,OAAO,aAAa,UAAU,GAAG,MAAM,GAAkB;AAClF,SACE,qBAAC,SAAO,GAAG,OACT;AAAA,yBAAC,eACC;AAAA,0BAAC,cAAW,SAAQ,QAClB,8BAAC,YAAS,GACZ;AAAA,MACA,oBAAC,cAAY,iBAAM;AAAA,MACnB,oBAAC,oBAAkB,uBAAY;AAAA,OACjC;AAAA,IACA,oBAAC,gBAAc,UAAS;AAAA,KAC1B;AAEJ;AAGA,SAAS,WAAW;AAClB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,QAAO;AAAA,MACP,aAAa;AAAA,MACb,eAAc;AAAA,MACd,gBAAe;AAAA,MACf,eAAW;AAAA,MAEX;AAAA,4BAAC,UAAK,GAAE,KAAI,GAAE,MAAK,OAAM,MAAK,QAAO,MAAK,IAAG,KAAI,IAAG,KAAI;AAAA,QACxD,oBAAC,UAAK,GAAE,4BAA2B;AAAA;AAAA;AAAA,EACrC;AAEJ;","names":[]}
|
|
@@ -4,6 +4,7 @@ import * as React from "react";
|
|
|
4
4
|
import { cn } from "../lib/cn.js";
|
|
5
5
|
import { CHECKBOX_BASE, CHECKBOX_INDICATOR } from "../generated/checkbox.js";
|
|
6
6
|
import { useControllableState } from "../primitives/use-controllable-state.js";
|
|
7
|
+
import { markLabelable } from "../primitives/labelable.js";
|
|
7
8
|
import { useFieldControlId } from "./field-context.js";
|
|
8
9
|
function Checkbox({
|
|
9
10
|
className,
|
|
@@ -62,6 +63,7 @@ function Checkbox({
|
|
|
62
63
|
}
|
|
63
64
|
);
|
|
64
65
|
}
|
|
66
|
+
markLabelable(Checkbox);
|
|
65
67
|
export {
|
|
66
68
|
Checkbox
|
|
67
69
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/checkbox.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { CHECKBOX_BASE, CHECKBOX_INDICATOR } from \"../generated/checkbox\";\nimport { useControllableState } from \"../primitives/use-controllable-state\";\nimport { useFieldControlId } from \"./field-context\";\n\nexport interface CheckboxProps\n extends Omit<React.ComponentProps<\"button\">, \"onChange\"> {\n checked?: boolean;\n defaultChecked?: boolean;\n onCheckedChange?: (checked: boolean) => void;\n}\n\nexport function Checkbox({\n className,\n checked,\n defaultChecked = false,\n onCheckedChange,\n onClick,\n disabled,\n id,\n ...props\n}: CheckboxProps) {\n const [on, setOn] = useControllableState<boolean>({\n ...(checked !== undefined ? { value: checked } : {}),\n defaultValue: defaultChecked,\n ...(onCheckedChange ? { onChange: onCheckedChange } : {}),\n });\n const state = on ? \"checked\" : \"unchecked\";\n return (\n <button\n type=\"button\"\n role=\"checkbox\"\n data-slot=\"checkbox\"\n data-state={state}\n aria-checked={on}\n disabled={disabled}\n id={useFieldControlId(id)}\n className={cn(CHECKBOX_BASE, className)}\n onClick={(e) => {\n onClick?.(e);\n setOn(!on);\n }}\n {...props}\n >\n {on && (\n <span\n data-slot=\"checkbox-indicator\"\n className={CHECKBOX_INDICATOR}\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-3.5\"\n aria-hidden=\"true\"\n >\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n </span>\n )}\n </button>\n );\n}\n"],"mappings":";
|
|
1
|
+
{"version":3,"sources":["../../src/components/checkbox.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { CHECKBOX_BASE, CHECKBOX_INDICATOR } from \"../generated/checkbox\";\nimport { useControllableState } from \"../primitives/use-controllable-state\";\nimport { markLabelable } from \"../primitives/labelable\";\nimport { useFieldControlId } from \"./field-context\";\n\nexport interface CheckboxProps\n extends Omit<React.ComponentProps<\"button\">, \"onChange\"> {\n checked?: boolean;\n defaultChecked?: boolean;\n onCheckedChange?: (checked: boolean) => void;\n}\n\nexport function Checkbox({\n className,\n checked,\n defaultChecked = false,\n onCheckedChange,\n onClick,\n disabled,\n id,\n ...props\n}: CheckboxProps) {\n const [on, setOn] = useControllableState<boolean>({\n ...(checked !== undefined ? { value: checked } : {}),\n defaultValue: defaultChecked,\n ...(onCheckedChange ? { onChange: onCheckedChange } : {}),\n });\n const state = on ? \"checked\" : \"unchecked\";\n return (\n <button\n type=\"button\"\n role=\"checkbox\"\n data-slot=\"checkbox\"\n data-state={state}\n aria-checked={on}\n disabled={disabled}\n id={useFieldControlId(id)}\n className={cn(CHECKBOX_BASE, className)}\n onClick={(e) => {\n onClick?.(e);\n setOn(!on);\n }}\n {...props}\n >\n {on && (\n <span\n data-slot=\"checkbox-indicator\"\n className={CHECKBOX_INDICATOR}\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-3.5\"\n aria-hidden=\"true\"\n >\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n </span>\n )}\n </button>\n );\n}\n\nmarkLabelable(Checkbox);\n"],"mappings":";AAgEY;AA9DZ,YAAY,WAAW;AACvB,SAAS,UAAU;AACnB,SAAS,eAAe,0BAA0B;AAClD,SAAS,4BAA4B;AACrC,SAAS,qBAAqB;AAC9B,SAAS,yBAAyB;AAS3B,SAAS,SAAS;AAAA,EACvB;AAAA,EACA;AAAA,EACA,iBAAiB;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAkB;AAChB,QAAM,CAAC,IAAI,KAAK,IAAI,qBAA8B;AAAA,IAChD,GAAI,YAAY,SAAY,EAAE,OAAO,QAAQ,IAAI,CAAC;AAAA,IAClD,cAAc;AAAA,IACd,GAAI,kBAAkB,EAAE,UAAU,gBAAgB,IAAI,CAAC;AAAA,EACzD,CAAC;AACD,QAAM,QAAQ,KAAK,YAAY;AAC/B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,MAAK;AAAA,MACL,aAAU;AAAA,MACV,cAAY;AAAA,MACZ,gBAAc;AAAA,MACd;AAAA,MACA,IAAI,kBAAkB,EAAE;AAAA,MACxB,WAAW,GAAG,eAAe,SAAS;AAAA,MACtC,SAAS,CAAC,MAAM;AACd,kBAAU,CAAC;AACX,cAAM,CAAC,EAAE;AAAA,MACX;AAAA,MACC,GAAG;AAAA,MAEH,gBACC;AAAA,QAAC;AAAA;AAAA,UACC,aAAU;AAAA,UACV,WAAW;AAAA,UAEX;AAAA,YAAC;AAAA;AAAA,cACC,OAAM;AAAA,cACN,SAAQ;AAAA,cACR,MAAK;AAAA,cACL,QAAO;AAAA,cACP,aAAY;AAAA,cACZ,eAAc;AAAA,cACd,gBAAe;AAAA,cACf,WAAU;AAAA,cACV,eAAY;AAAA,cAEZ,8BAAC,UAAK,GAAE,mBAAkB;AAAA;AAAA,UAC5B;AAAA;AAAA,MACF;AAAA;AAAA,EAEJ;AAEJ;AAEA,cAAc,QAAQ;","names":[]}
|