@vaam-apps/ui 0.1.0 → 0.1.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/CHANGELOG.md +25 -0
- package/dist/components/data/code.d.ts +0 -1
- package/dist/components/data/code.js +0 -1
- package/dist/components/data/copy-button.d.ts +0 -1
- package/dist/components/data/copy-button.js +0 -1
- package/dist/components/data/detail-row.d.ts +0 -1
- package/dist/components/data/detail-row.js +0 -1
- package/dist/components/data/id-display.d.ts +0 -1
- package/dist/components/data/id-display.js +0 -1
- package/dist/components/data/masked-value.d.ts +0 -1
- package/dist/components/data/masked-value.js +0 -1
- package/dist/components/data/money.d.ts +0 -1
- package/dist/components/data/money.js +0 -1
- package/dist/components/data/phone-display.d.ts +0 -1
- package/dist/components/data/phone-display.js +0 -1
- package/dist/components/data/stat-tile.d.ts +0 -1
- package/dist/components/data/stat-tile.js +0 -1
- package/dist/components/data/timestamp-display.d.ts +0 -1
- package/dist/components/data/timestamp-display.js +0 -1
- package/dist/components/patterns/inline-banner.d.ts +0 -1
- package/dist/components/patterns/inline-banner.js +0 -1
- package/dist/components/patterns/inline-empty-state.d.ts +0 -1
- package/dist/components/patterns/inline-empty-state.js +0 -1
- package/dist/components/patterns/live-row.d.ts +0 -1
- package/dist/components/patterns/live-row.js +0 -1
- package/dist/components/patterns/payload-inspector.d.ts +0 -1
- package/dist/components/patterns/payload-inspector.js +0 -1
- package/dist/components/patterns/route-skeleton.d.ts +0 -1
- package/dist/components/patterns/route-skeleton.js +0 -1
- package/dist/components/patterns/screen-layout.d.ts +0 -1
- package/dist/components/patterns/screen-layout.js +0 -1
- package/dist/components/patterns/stale-write-banner.d.ts +0 -1
- package/dist/components/patterns/stale-write-banner.js +0 -1
- package/dist/components/patterns/state-timeline.d.ts +0 -1
- package/dist/components/patterns/state-timeline.js +0 -1
- package/dist/components/primitives/badge.d.ts +0 -1
- package/dist/components/primitives/badge.js +0 -1
- package/dist/components/primitives/button.d.ts +0 -1
- package/dist/components/primitives/button.js +0 -1
- package/dist/components/primitives/calendar.d.ts +0 -1
- package/dist/components/primitives/calendar.js +0 -1
- package/dist/components/primitives/card.d.ts +0 -1
- package/dist/components/primitives/card.js +0 -1
- package/dist/components/primitives/checkbox.d.ts +0 -1
- package/dist/components/primitives/checkbox.js +0 -1
- package/dist/components/primitives/chip-select.d.ts +0 -1
- package/dist/components/primitives/chip-select.js +0 -1
- package/dist/components/primitives/command-menu.d.ts +0 -1
- package/dist/components/primitives/command-menu.js +0 -1
- package/dist/components/primitives/confirm-dialog.d.ts +0 -1
- package/dist/components/primitives/confirm-dialog.js +0 -1
- package/dist/components/primitives/date-picker.d.ts +0 -1
- package/dist/components/primitives/date-picker.js +0 -1
- package/dist/components/primitives/dialog.d.ts +0 -1
- package/dist/components/primitives/dialog.js +0 -1
- package/dist/components/primitives/drawer.d.ts +0 -1
- package/dist/components/primitives/drawer.js +0 -1
- package/dist/components/primitives/dropdown-menu.d.ts +0 -1
- package/dist/components/primitives/dropdown-menu.js +0 -1
- package/dist/components/primitives/form-field.d.ts +0 -1
- package/dist/components/primitives/form-field.js +0 -1
- package/dist/components/primitives/inline-confirm.d.ts +0 -1
- package/dist/components/primitives/inline-confirm.js +0 -1
- package/dist/components/primitives/input.d.ts +0 -1
- package/dist/components/primitives/input.js +0 -1
- package/dist/components/primitives/label.d.ts +0 -1
- package/dist/components/primitives/label.js +0 -1
- package/dist/components/primitives/pagination.d.ts +0 -1
- package/dist/components/primitives/pagination.js +0 -1
- package/dist/components/primitives/popover.d.ts +0 -1
- package/dist/components/primitives/popover.js +0 -1
- package/dist/components/primitives/progress.d.ts +0 -1
- package/dist/components/primitives/progress.js +0 -1
- package/dist/components/primitives/radio-group.d.ts +0 -1
- package/dist/components/primitives/radio-group.js +0 -1
- package/dist/components/primitives/select.d.ts +0 -1
- package/dist/components/primitives/select.js +0 -1
- package/dist/components/primitives/separator.d.ts +0 -1
- package/dist/components/primitives/separator.js +0 -1
- package/dist/components/primitives/side-nav.d.ts +0 -1
- package/dist/components/primitives/side-nav.js +0 -1
- package/dist/components/primitives/skeleton.d.ts +0 -1
- package/dist/components/primitives/skeleton.js +0 -1
- package/dist/components/primitives/spinner.d.ts +0 -1
- package/dist/components/primitives/spinner.js +0 -1
- package/dist/components/primitives/switch.d.ts +0 -1
- package/dist/components/primitives/switch.js +0 -1
- package/dist/components/primitives/table.d.ts +0 -1
- package/dist/components/primitives/table.js +0 -1
- package/dist/components/primitives/tabs.d.ts +0 -1
- package/dist/components/primitives/tabs.js +0 -1
- package/dist/components/primitives/textarea.d.ts +0 -1
- package/dist/components/primitives/textarea.js +0 -1
- package/dist/components/primitives/toast.d.ts +0 -1
- package/dist/components/primitives/toast.js +0 -1
- package/dist/components/primitives/tooltip.d.ts +0 -1
- package/dist/components/primitives/tooltip.js +0 -1
- package/dist/components/status/state-chip.d.ts +0 -1
- package/dist/components/status/state-chip.js +0 -1
- package/dist/components/status/state-mark.d.ts +0 -1
- package/dist/components/status/state-mark.js +0 -1
- package/dist/components/status/status-pill.d.ts +0 -1
- package/dist/components/status/status-pill.js +0 -1
- package/dist/components/status/status-tokens.d.ts +0 -1
- package/dist/components/status/status-tokens.js +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/lib/cn.d.ts +0 -1
- package/dist/lib/cn.js +0 -1
- package/dist/lib/money.d.ts +0 -1
- package/dist/lib/money.js +0 -1
- package/dist/lib/omit-undefined.d.ts +0 -1
- package/dist/lib/omit-undefined.js +0 -1
- package/package.json +3 -7
- package/dist/components/data/code.d.ts.map +0 -1
- package/dist/components/data/code.js.map +0 -1
- package/dist/components/data/copy-button.d.ts.map +0 -1
- package/dist/components/data/copy-button.js.map +0 -1
- package/dist/components/data/detail-row.d.ts.map +0 -1
- package/dist/components/data/detail-row.js.map +0 -1
- package/dist/components/data/id-display.d.ts.map +0 -1
- package/dist/components/data/id-display.js.map +0 -1
- package/dist/components/data/masked-value.d.ts.map +0 -1
- package/dist/components/data/masked-value.js.map +0 -1
- package/dist/components/data/money.d.ts.map +0 -1
- package/dist/components/data/money.js.map +0 -1
- package/dist/components/data/phone-display.d.ts.map +0 -1
- package/dist/components/data/phone-display.js.map +0 -1
- package/dist/components/data/stat-tile.d.ts.map +0 -1
- package/dist/components/data/stat-tile.js.map +0 -1
- package/dist/components/data/timestamp-display.d.ts.map +0 -1
- package/dist/components/data/timestamp-display.js.map +0 -1
- package/dist/components/patterns/inline-banner.d.ts.map +0 -1
- package/dist/components/patterns/inline-banner.js.map +0 -1
- package/dist/components/patterns/inline-empty-state.d.ts.map +0 -1
- package/dist/components/patterns/inline-empty-state.js.map +0 -1
- package/dist/components/patterns/live-row.d.ts.map +0 -1
- package/dist/components/patterns/live-row.js.map +0 -1
- package/dist/components/patterns/payload-inspector.d.ts.map +0 -1
- package/dist/components/patterns/payload-inspector.js.map +0 -1
- package/dist/components/patterns/route-skeleton.d.ts.map +0 -1
- package/dist/components/patterns/route-skeleton.js.map +0 -1
- package/dist/components/patterns/screen-layout.d.ts.map +0 -1
- package/dist/components/patterns/screen-layout.js.map +0 -1
- package/dist/components/patterns/stale-write-banner.d.ts.map +0 -1
- package/dist/components/patterns/stale-write-banner.js.map +0 -1
- package/dist/components/patterns/state-timeline.d.ts.map +0 -1
- package/dist/components/patterns/state-timeline.js.map +0 -1
- package/dist/components/primitives/badge.d.ts.map +0 -1
- package/dist/components/primitives/badge.js.map +0 -1
- package/dist/components/primitives/button.d.ts.map +0 -1
- package/dist/components/primitives/button.js.map +0 -1
- package/dist/components/primitives/calendar.d.ts.map +0 -1
- package/dist/components/primitives/calendar.js.map +0 -1
- package/dist/components/primitives/card.d.ts.map +0 -1
- package/dist/components/primitives/card.js.map +0 -1
- package/dist/components/primitives/checkbox.d.ts.map +0 -1
- package/dist/components/primitives/checkbox.js.map +0 -1
- package/dist/components/primitives/chip-select.d.ts.map +0 -1
- package/dist/components/primitives/chip-select.js.map +0 -1
- package/dist/components/primitives/command-menu.d.ts.map +0 -1
- package/dist/components/primitives/command-menu.js.map +0 -1
- package/dist/components/primitives/confirm-dialog.d.ts.map +0 -1
- package/dist/components/primitives/confirm-dialog.js.map +0 -1
- package/dist/components/primitives/date-picker.d.ts.map +0 -1
- package/dist/components/primitives/date-picker.js.map +0 -1
- package/dist/components/primitives/dialog.d.ts.map +0 -1
- package/dist/components/primitives/dialog.js.map +0 -1
- package/dist/components/primitives/drawer.d.ts.map +0 -1
- package/dist/components/primitives/drawer.js.map +0 -1
- package/dist/components/primitives/dropdown-menu.d.ts.map +0 -1
- package/dist/components/primitives/dropdown-menu.js.map +0 -1
- package/dist/components/primitives/form-field.d.ts.map +0 -1
- package/dist/components/primitives/form-field.js.map +0 -1
- package/dist/components/primitives/inline-confirm.d.ts.map +0 -1
- package/dist/components/primitives/inline-confirm.js.map +0 -1
- package/dist/components/primitives/input.d.ts.map +0 -1
- package/dist/components/primitives/input.js.map +0 -1
- package/dist/components/primitives/label.d.ts.map +0 -1
- package/dist/components/primitives/label.js.map +0 -1
- package/dist/components/primitives/pagination.d.ts.map +0 -1
- package/dist/components/primitives/pagination.js.map +0 -1
- package/dist/components/primitives/popover.d.ts.map +0 -1
- package/dist/components/primitives/popover.js.map +0 -1
- package/dist/components/primitives/progress.d.ts.map +0 -1
- package/dist/components/primitives/progress.js.map +0 -1
- package/dist/components/primitives/radio-group.d.ts.map +0 -1
- package/dist/components/primitives/radio-group.js.map +0 -1
- package/dist/components/primitives/select.d.ts.map +0 -1
- package/dist/components/primitives/select.js.map +0 -1
- package/dist/components/primitives/separator.d.ts.map +0 -1
- package/dist/components/primitives/separator.js.map +0 -1
- package/dist/components/primitives/side-nav.d.ts.map +0 -1
- package/dist/components/primitives/side-nav.js.map +0 -1
- package/dist/components/primitives/skeleton.d.ts.map +0 -1
- package/dist/components/primitives/skeleton.js.map +0 -1
- package/dist/components/primitives/spinner.d.ts.map +0 -1
- package/dist/components/primitives/spinner.js.map +0 -1
- package/dist/components/primitives/switch.d.ts.map +0 -1
- package/dist/components/primitives/switch.js.map +0 -1
- package/dist/components/primitives/table.d.ts.map +0 -1
- package/dist/components/primitives/table.js.map +0 -1
- package/dist/components/primitives/tabs.d.ts.map +0 -1
- package/dist/components/primitives/tabs.js.map +0 -1
- package/dist/components/primitives/textarea.d.ts.map +0 -1
- package/dist/components/primitives/textarea.js.map +0 -1
- package/dist/components/primitives/toast.d.ts.map +0 -1
- package/dist/components/primitives/toast.js.map +0 -1
- package/dist/components/primitives/tooltip.d.ts.map +0 -1
- package/dist/components/primitives/tooltip.js.map +0 -1
- package/dist/components/status/state-chip.d.ts.map +0 -1
- package/dist/components/status/state-chip.js.map +0 -1
- package/dist/components/status/state-mark.d.ts.map +0 -1
- package/dist/components/status/state-mark.js.map +0 -1
- package/dist/components/status/status-pill.d.ts.map +0 -1
- package/dist/components/status/status-pill.js.map +0 -1
- package/dist/components/status/status-tokens.d.ts.map +0 -1
- package/dist/components/status/status-tokens.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lib/cn.d.ts.map +0 -1
- package/dist/lib/cn.js.map +0 -1
- package/dist/lib/money.d.ts.map +0 -1
- package/dist/lib/money.js.map +0 -1
- package/dist/lib/omit-undefined.d.ts.map +0 -1
- package/dist/lib/omit-undefined.js.map +0 -1
- package/src/components/data/code.tsx +0 -43
- package/src/components/data/copy-button.tsx +0 -97
- package/src/components/data/detail-row.tsx +0 -113
- package/src/components/data/id-display.tsx +0 -49
- package/src/components/data/masked-value.tsx +0 -96
- package/src/components/data/money.tsx +0 -46
- package/src/components/data/phone-display.tsx +0 -60
- package/src/components/data/stat-tile.tsx +0 -68
- package/src/components/data/timestamp-display.tsx +0 -106
- package/src/components/patterns/inline-banner.tsx +0 -60
- package/src/components/patterns/inline-empty-state.tsx +0 -45
- package/src/components/patterns/live-row.tsx +0 -75
- package/src/components/patterns/payload-inspector.tsx +0 -157
- package/src/components/patterns/route-skeleton.tsx +0 -77
- package/src/components/patterns/screen-layout.tsx +0 -44
- package/src/components/patterns/stale-write-banner.tsx +0 -44
- package/src/components/patterns/state-timeline.tsx +0 -229
- package/src/components/primitives/badge.tsx +0 -40
- package/src/components/primitives/button.tsx +0 -54
- package/src/components/primitives/calendar.tsx +0 -136
- package/src/components/primitives/card.tsx +0 -70
- package/src/components/primitives/checkbox.tsx +0 -101
- package/src/components/primitives/chip-select.tsx +0 -105
- package/src/components/primitives/command-menu.tsx +0 -92
- package/src/components/primitives/confirm-dialog.tsx +0 -109
- package/src/components/primitives/date-picker.tsx +0 -271
- package/src/components/primitives/dialog.tsx +0 -195
- package/src/components/primitives/drawer.tsx +0 -326
- package/src/components/primitives/dropdown-menu.tsx +0 -128
- package/src/components/primitives/form-field.tsx +0 -136
- package/src/components/primitives/inline-confirm.tsx +0 -118
- package/src/components/primitives/input.tsx +0 -21
- package/src/components/primitives/label.tsx +0 -43
- package/src/components/primitives/pagination.tsx +0 -110
- package/src/components/primitives/popover.tsx +0 -41
- package/src/components/primitives/progress.tsx +0 -66
- package/src/components/primitives/radio-group.tsx +0 -90
- package/src/components/primitives/select.tsx +0 -254
- package/src/components/primitives/separator.tsx +0 -49
- package/src/components/primitives/side-nav.tsx +0 -253
- package/src/components/primitives/skeleton.tsx +0 -25
- package/src/components/primitives/spinner.tsx +0 -39
- package/src/components/primitives/switch.tsx +0 -74
- package/src/components/primitives/table.tsx +0 -157
- package/src/components/primitives/tabs.tsx +0 -163
- package/src/components/primitives/textarea.tsx +0 -21
- package/src/components/primitives/toast.tsx +0 -117
- package/src/components/primitives/tooltip.tsx +0 -38
- package/src/components/status/state-chip.tsx +0 -70
- package/src/components/status/state-mark.tsx +0 -154
- package/src/components/status/status-pill.tsx +0 -153
- package/src/components/status/status-tokens.ts +0 -173
- package/src/index.ts +0 -96
- package/src/lib/cn.ts +0 -168
- package/src/lib/money.ts +0 -164
- package/src/lib/omit-undefined.ts +0 -25
- /package/{src → dist}/styles/theme.css +0 -0
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import type { ReactNode } from "react";
|
|
4
|
-
import { cn } from "../../lib/cn";
|
|
5
|
-
import { InlineBanner } from "../patterns/inline-banner";
|
|
6
|
-
import { Button } from "./button";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* A confirmation rendered **inline**, inside the caller's own DOM subtree —
|
|
10
|
-
* never a portal, never a second `Dialog`/`FocusScope`. Exists specifically
|
|
11
|
-
* to replace a centered `Dialog` nested inside an open `QuickDetailDrawer` /
|
|
12
|
-
* `MoreDetailDrawer` (`vaul`), which is permanently broken: see
|
|
13
|
-
* `frontends/apps/admin/app/gallery/page.tsx`'s own
|
|
14
|
-
* `NestedDialogInDrawerRegression` writeup for the full root-cause
|
|
15
|
-
* investigation (`vaul`'s `Content` mounts a trapped, document-level
|
|
16
|
-
* `@radix-ui/react-focus-scope` `FocusScope` regardless of `dimmed`/`modal`,
|
|
17
|
-
* and Headless UI's `Dialog` always portals to its own sibling
|
|
18
|
-
* `#headlessui-portal-root` — so the trap yanks focus straight back out of
|
|
19
|
-
* the portal the instant it tries to move in, and the confirmation never
|
|
20
|
-
* becomes visible).
|
|
21
|
-
*
|
|
22
|
-
* Covers both destructive yes/no confirmations (`title`/`description`, no
|
|
23
|
-
* `children`) and a short form embedded in a confirmation step (`children`
|
|
24
|
-
* holds the field, e.g. sender-ids-screen.tsx's provider picker) — the two
|
|
25
|
-
* shapes this console actually needs, one component. A second, form-only
|
|
26
|
-
* component was considered and rejected: both shapes are "explain the
|
|
27
|
-
* consequence, then commit or back out," and forking that into two
|
|
28
|
-
* components would just be this file's own title/description/actions
|
|
29
|
-
* scaffold copy-pasted around a different middle section.
|
|
30
|
-
*
|
|
31
|
-
* The caller owns visibility: render this instead of the drawer's normal
|
|
32
|
-
* body content (and drop the drawer's own `footer` prop, since this
|
|
33
|
-
* component supplies its own Cancel/Confirm row) rather than layering it on
|
|
34
|
-
* top — there is no overlay, no absolute positioning, and no independent
|
|
35
|
-
* open/close transition to fight the drawer's own.
|
|
36
|
-
*/
|
|
37
|
-
export interface InlineConfirmProps {
|
|
38
|
-
/** The question being asked, e.g. "Delete this route?" or "Register {value}
|
|
39
|
-
* with a provider". Rendered as the panel's own heading — the drawer's
|
|
40
|
-
* `title` prop stays whatever record is being acted on; this is the
|
|
41
|
-
* action, not the record. */
|
|
42
|
-
title: ReactNode;
|
|
43
|
-
description?: ReactNode;
|
|
44
|
-
/** A field belonging to this confirmation, e.g. a provider `Select` — omit
|
|
45
|
-
* for a plain yes/no confirm. */
|
|
46
|
-
children?: ReactNode;
|
|
47
|
-
confirmLabel: string;
|
|
48
|
-
/** Shown on the confirm button while `pending` — defaults to `confirmLabel`
|
|
49
|
-
* with an ellipsis. */
|
|
50
|
-
pendingLabel?: string | undefined;
|
|
51
|
-
cancelLabel?: string;
|
|
52
|
-
onConfirm: () => void;
|
|
53
|
-
onCancel: () => void;
|
|
54
|
-
pending?: boolean;
|
|
55
|
-
/** Disables the confirm button independent of `pending`, e.g. no provider
|
|
56
|
-
* selected yet. */
|
|
57
|
-
confirmDisabled?: boolean;
|
|
58
|
-
/** Danger-hued confirm button + accent border, matching `Dialog`'s own
|
|
59
|
-
* "destructive action in the danger hue, never the primary hue"
|
|
60
|
-
* convention (console-redesign.md §1.7). Defaults `true` — every current
|
|
61
|
-
* call site is destructive; the one exception (sender-ids-screen.tsx's
|
|
62
|
-
* "register with a provider" form) passes `false`. */
|
|
63
|
-
destructive?: boolean;
|
|
64
|
-
error?: ReactNode;
|
|
65
|
-
className?: string;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function InlineConfirm({
|
|
69
|
-
title,
|
|
70
|
-
description,
|
|
71
|
-
children,
|
|
72
|
-
confirmLabel,
|
|
73
|
-
pendingLabel,
|
|
74
|
-
cancelLabel = "Cancel",
|
|
75
|
-
onConfirm,
|
|
76
|
-
onCancel,
|
|
77
|
-
pending = false,
|
|
78
|
-
confirmDisabled = false,
|
|
79
|
-
destructive = true,
|
|
80
|
-
error,
|
|
81
|
-
className,
|
|
82
|
-
}: InlineConfirmProps) {
|
|
83
|
-
return (
|
|
84
|
-
<div
|
|
85
|
-
className={cn(
|
|
86
|
-
"flex flex-col gap-4 rounded-sm border p-4",
|
|
87
|
-
destructive
|
|
88
|
-
? "border-state-danger-border bg-state-danger-bg/30"
|
|
89
|
-
: "border-edge bg-surface-2",
|
|
90
|
-
className,
|
|
91
|
-
)}
|
|
92
|
-
>
|
|
93
|
-
<div className="flex flex-col gap-1">
|
|
94
|
-
<h3 className="font-medium text-foreground text-title-sm">{title}</h3>
|
|
95
|
-
{description != null && <p className="text-body text-muted-foreground">{description}</p>}
|
|
96
|
-
</div>
|
|
97
|
-
|
|
98
|
-
{children != null && <div className="flex flex-col gap-4">{children}</div>}
|
|
99
|
-
|
|
100
|
-
{error != null && <InlineBanner variant="danger">{error}</InlineBanner>}
|
|
101
|
-
|
|
102
|
-
<div className="flex items-center justify-end gap-2">
|
|
103
|
-
<Button type="button" variant="ghost" size="sm" disabled={pending} onClick={onCancel}>
|
|
104
|
-
{cancelLabel}
|
|
105
|
-
</Button>
|
|
106
|
-
<Button
|
|
107
|
-
type="button"
|
|
108
|
-
variant={destructive ? "destructive" : "primary"}
|
|
109
|
-
size="sm"
|
|
110
|
-
disabled={pending || confirmDisabled}
|
|
111
|
-
onClick={onConfirm}
|
|
112
|
-
>
|
|
113
|
-
{pending ? (pendingLabel ?? `${confirmLabel}…`) : confirmLabel}
|
|
114
|
-
</Button>
|
|
115
|
-
</div>
|
|
116
|
-
</div>
|
|
117
|
-
);
|
|
118
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { InputHTMLAttributes } from "react";
|
|
2
|
-
import { forwardRef } from "react";
|
|
3
|
-
import { cn } from "../../lib/cn";
|
|
4
|
-
|
|
5
|
-
export type InputProps = InputHTMLAttributes<HTMLInputElement>;
|
|
6
|
-
|
|
7
|
-
// D8: no explicit `rounded-*` class needed or added — daisyUI's own
|
|
8
|
-
// `.input` rule already sets every corner from `--radius-field`
|
|
9
|
-
// (confirmed by reading `daisyui/components/input.css` directly), and
|
|
10
|
-
// Phase 0 already rewrote that token to the new register. The class
|
|
11
|
-
// string here is unchanged from before the redesign; the rendered radius
|
|
12
|
-
// changed anyway, entirely from the token, with zero component code to
|
|
13
|
-
// touch — the intended effect of constraint 7 ("DaisyUI does the work").
|
|
14
|
-
export const Input = forwardRef<HTMLInputElement, InputProps>(({ className, ...props }, ref) => (
|
|
15
|
-
<input
|
|
16
|
-
ref={ref}
|
|
17
|
-
className={cn("input input-bordered w-full font-sans text-prose", className)}
|
|
18
|
-
{...props}
|
|
19
|
-
/>
|
|
20
|
-
));
|
|
21
|
-
Input.displayName = "Input";
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { Field, Label as HeadlessLabel } from "@headlessui/react";
|
|
4
|
-
import { Fragment, forwardRef } from "react";
|
|
5
|
-
import { cn } from "../../lib/cn";
|
|
6
|
-
import { omitUndefined } from "../../lib/omit-undefined";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* D3: ports from Radix `Label` to Headless UI's `Field`/`Label` composition.
|
|
10
|
-
*
|
|
11
|
-
* **Found live, not assumed:** Headless UI's own `Label` throws ("You used a
|
|
12
|
-
* `<Label />` component, but it is not inside a relevant parent") unless a
|
|
13
|
-
* `Field` ancestor exists to supply its context — unlike Radix's `Label`,
|
|
14
|
-
* which was a fully standalone `<label>` wrapper. None of this console's
|
|
15
|
-
* ~15 call sites wrap their `<Label htmlFor="x">…</Label>` +
|
|
16
|
-
* `<Input id="x">` pair in a `Field` (they associate the two explicitly via
|
|
17
|
-
* matching `htmlFor`/`id` instead, which needs no shared context at all),
|
|
18
|
-
* so porting to the bare Headless UI `Label` export would crash every one
|
|
19
|
-
* of them. Wrapping each call site in `<Field>` was considered and
|
|
20
|
-
* rejected — it would touch ~15 files outside this bucket's scope for no
|
|
21
|
-
* behavioural gain, since every call site already supplies its own
|
|
22
|
-
* `htmlFor`.
|
|
23
|
-
*
|
|
24
|
-
* Fixed here instead: `Label` supplies its own self-contained `Field`
|
|
25
|
-
* (`as={Fragment}`, so it adds no DOM node) around just the `Label` itself.
|
|
26
|
-
* This satisfies Headless UI's context requirement without requiring any
|
|
27
|
-
* caller to know `Field` exists, keeps the public API
|
|
28
|
-
* (`<Label htmlFor="x">…</Label>`) byte-identical to the Radix version, and
|
|
29
|
-
* still genuinely uses Headless UI's `Label` (constraint 6) rather than a
|
|
30
|
-
* hand-rolled `<label>`.
|
|
31
|
-
*/
|
|
32
|
-
export const Label = forwardRef<HTMLLabelElement, React.ComponentPropsWithoutRef<"label">>(
|
|
33
|
-
({ className, ...props }, ref) => (
|
|
34
|
-
<Field as={Fragment}>
|
|
35
|
-
<HeadlessLabel
|
|
36
|
-
ref={ref}
|
|
37
|
-
className={cn("text-body font-medium text-foreground", className)}
|
|
38
|
-
{...omitUndefined(props)}
|
|
39
|
-
/>
|
|
40
|
-
</Field>
|
|
41
|
-
),
|
|
42
|
-
);
|
|
43
|
-
Label.displayName = "Label";
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
4
|
-
import type { ReactNode } from "react";
|
|
5
|
-
import { cn } from "../../lib/cn";
|
|
6
|
-
import { Button } from "./button";
|
|
7
|
-
|
|
8
|
-
export interface PaginationProps {
|
|
9
|
-
onPrevious: (() => void) | undefined;
|
|
10
|
-
onNext: (() => void) | undefined;
|
|
11
|
-
/**
|
|
12
|
-
* What the reader is looking at. Two honest shapes, and the choice is
|
|
13
|
-
* not cosmetic:
|
|
14
|
-
*
|
|
15
|
-
* - `{ kind: "offset", offset, pageSize, total }` — only when the API
|
|
16
|
-
* genuinely returns a total. Renders `1–25 of 340`.
|
|
17
|
-
* - `{ kind: "cursor", count }` — a keyset-paged API, which most
|
|
18
|
-
* event-shaped endpoints are, because counting a large table on every
|
|
19
|
-
* request is the expensive part. Renders `25 shown` and no page
|
|
20
|
-
* numbers, because there is no honest page number to render.
|
|
21
|
-
*
|
|
22
|
-
* Inventing a page count from a cursor API is the failure this type
|
|
23
|
-
* exists to prevent: it produces a "Page 3 of ?" that is wrong the
|
|
24
|
-
* moment a row is inserted.
|
|
25
|
-
*/
|
|
26
|
-
position: PaginationPosition;
|
|
27
|
-
/** Rendered between the counts and the buttons — a page-size select, a
|
|
28
|
-
* "Newest first" note. */
|
|
29
|
-
children?: ReactNode;
|
|
30
|
-
/**
|
|
31
|
-
* Overrides the landmark's accessible name.
|
|
32
|
-
*
|
|
33
|
-
* Needed whenever a screen shows more than one pager — a table with
|
|
34
|
-
* controls above and below is the usual case. Two `<nav>` landmarks
|
|
35
|
-
* with the same name is a real navigation defect, not a lint nit: a
|
|
36
|
-
* screen-reader user listing landmarks sees "Pagination, Pagination"
|
|
37
|
-
* and cannot tell which is which.
|
|
38
|
-
*/
|
|
39
|
-
label?: string | undefined;
|
|
40
|
-
className?: string | undefined;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export type PaginationPosition =
|
|
44
|
-
| { kind: "offset"; offset: number; pageSize: number; total: number }
|
|
45
|
-
| { kind: "cursor"; count: number };
|
|
46
|
-
|
|
47
|
-
function positionLabel(position: PaginationPosition): string {
|
|
48
|
-
if (position.kind === "cursor") {
|
|
49
|
-
return position.count === 0 ? "No results" : `${position.count} shown`;
|
|
50
|
-
}
|
|
51
|
-
const { offset, pageSize, total } = position;
|
|
52
|
-
if (total === 0) return "No results";
|
|
53
|
-
const first = offset + 1;
|
|
54
|
-
const last = Math.min(offset + pageSize, total);
|
|
55
|
-
return `${first}–${last} of ${total}`;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Previous/next paging with an honest position readout.
|
|
60
|
-
*
|
|
61
|
-
* Both callbacks are required but nullable: `undefined` disables that
|
|
62
|
-
* direction. Making them required-but-nullable rather than optional
|
|
63
|
-
* forces a caller to decide what the boundary is, instead of getting a
|
|
64
|
-
* silently dead button by omission.
|
|
65
|
-
*/
|
|
66
|
-
export function Pagination({
|
|
67
|
-
onPrevious,
|
|
68
|
-
onNext,
|
|
69
|
-
position,
|
|
70
|
-
children,
|
|
71
|
-
label = "Pagination",
|
|
72
|
-
className,
|
|
73
|
-
}: PaginationProps) {
|
|
74
|
-
const atEnd =
|
|
75
|
-
position.kind === "cursor"
|
|
76
|
-
? onNext === undefined
|
|
77
|
-
: position.offset + position.pageSize >= position.total;
|
|
78
|
-
|
|
79
|
-
return (
|
|
80
|
-
<nav
|
|
81
|
-
aria-label={label}
|
|
82
|
-
className={cn("flex items-center justify-between gap-4 text-caption", className)}
|
|
83
|
-
>
|
|
84
|
-
<p className="font-mono text-subtle-foreground tabular-nums">{positionLabel(position)}</p>
|
|
85
|
-
<div className="flex items-center gap-2">
|
|
86
|
-
{children}
|
|
87
|
-
<Button
|
|
88
|
-
variant="secondary"
|
|
89
|
-
size="sm"
|
|
90
|
-
onClick={onPrevious}
|
|
91
|
-
disabled={onPrevious === undefined}
|
|
92
|
-
aria-label="Previous page"
|
|
93
|
-
>
|
|
94
|
-
<ChevronLeft size={14} strokeWidth={1.5} />
|
|
95
|
-
Previous
|
|
96
|
-
</Button>
|
|
97
|
-
<Button
|
|
98
|
-
variant="secondary"
|
|
99
|
-
size="sm"
|
|
100
|
-
onClick={onNext}
|
|
101
|
-
disabled={onNext === undefined || atEnd}
|
|
102
|
-
aria-label="Next page"
|
|
103
|
-
>
|
|
104
|
-
Next
|
|
105
|
-
<ChevronRight size={14} strokeWidth={1.5} />
|
|
106
|
-
</Button>
|
|
107
|
-
</div>
|
|
108
|
-
</nav>
|
|
109
|
-
);
|
|
110
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { Popover as HeadlessPopover, PopoverButton, PopoverPanel } from "@headlessui/react";
|
|
4
|
-
import type { ComponentPropsWithoutRef } from "react";
|
|
5
|
-
import { cn } from "../../lib/cn";
|
|
6
|
-
|
|
7
|
-
// D3: Radix `Popover` → Headless UI `Popover`/`PopoverButton`/`PopoverPanel`.
|
|
8
|
-
// `Popover`/`PopoverButton` are used as-is — Headless UI's own `Popover` is
|
|
9
|
-
// already a fully self-contained open/close state machine (no separate
|
|
10
|
-
// "Root" plumbing needed the way `Dialog` needs one here, since Popover
|
|
11
|
-
// natively supports an in-place, uncontrolled trigger).
|
|
12
|
-
export const Popover = HeadlessPopover;
|
|
13
|
-
|
|
14
|
-
// `PopoverButton` is natively polymorphic (`as`), replacing Radix's
|
|
15
|
-
// `asChild` pattern (D4's same call, applied here to a trigger rather than
|
|
16
|
-
// `Button` itself): `<PopoverTrigger as={Button} variant="secondary">…`.
|
|
17
|
-
export const PopoverTrigger = PopoverButton;
|
|
18
|
-
|
|
19
|
-
// Radix also exported a standalone `Anchor` (a positioning reference
|
|
20
|
-
// distinct from the trigger) — unconsumed anywhere in this codebase, and
|
|
21
|
-
// Headless UI's own `anchor` prop on `PopoverPanel` (below) covers the same
|
|
22
|
-
// need without a separate element, so no replacement is exported.
|
|
23
|
-
|
|
24
|
-
export function PopoverContent({
|
|
25
|
-
className,
|
|
26
|
-
anchor = "bottom start",
|
|
27
|
-
...props
|
|
28
|
-
}: ComponentPropsWithoutRef<typeof PopoverPanel>) {
|
|
29
|
-
return (
|
|
30
|
-
<PopoverPanel
|
|
31
|
-
anchor={anchor}
|
|
32
|
-
transition
|
|
33
|
-
className={cn(
|
|
34
|
-
"z-50 rounded-md border border-edge bg-surface-2 p-3 shadow-[var(--shadow-popover)] [--anchor-gap:6px] focus:outline-none",
|
|
35
|
-
"origin-top transition duration-100 ease-out data-closed:scale-95 data-closed:opacity-0",
|
|
36
|
-
className,
|
|
37
|
-
)}
|
|
38
|
-
{...props}
|
|
39
|
-
/>
|
|
40
|
-
);
|
|
41
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { cn } from "../../lib/cn";
|
|
2
|
-
import type { StatusHue } from "../status/status-tokens";
|
|
3
|
-
import { HUE_CLASSES } from "../status/status-tokens";
|
|
4
|
-
|
|
5
|
-
export interface ProgressProps {
|
|
6
|
-
/** Completed units. Clamped into `[0, max]`. */
|
|
7
|
-
value: number;
|
|
8
|
-
max?: number;
|
|
9
|
-
/** `undefined` renders an indeterminate bar. */
|
|
10
|
-
tone?: StatusHue;
|
|
11
|
-
/** Accessible name, e.g. `"Retry budget used"`. */
|
|
12
|
-
label: string;
|
|
13
|
-
/** Show `3 / 5` beside the bar. */
|
|
14
|
-
showValue?: boolean;
|
|
15
|
-
className?: string | undefined;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* A determinate progress bar for a *bounded, countable* quantity —
|
|
20
|
-
* attempts against a retry budget, rows processed, quota consumed.
|
|
21
|
-
*
|
|
22
|
-
* Not for an unbounded wait: a bar that creeps toward a value it cannot
|
|
23
|
-
* reach is a lie, and `Spinner` is the honest alternative. The `max` is
|
|
24
|
-
* required in spirit for that reason, and defaults to 100 only so a
|
|
25
|
-
* percentage — the one case where the bound is implied — needs no
|
|
26
|
-
* ceremony.
|
|
27
|
-
*/
|
|
28
|
-
export function Progress({
|
|
29
|
-
value,
|
|
30
|
-
max = 100,
|
|
31
|
-
tone = "neutral",
|
|
32
|
-
label,
|
|
33
|
-
showValue = false,
|
|
34
|
-
className,
|
|
35
|
-
}: ProgressProps) {
|
|
36
|
-
const safeMax = max <= 0 ? 1 : max;
|
|
37
|
-
const clamped = Math.min(Math.max(value, 0), safeMax);
|
|
38
|
-
const percent = (clamped / safeMax) * 100;
|
|
39
|
-
const hue = HUE_CLASSES[tone];
|
|
40
|
-
|
|
41
|
-
return (
|
|
42
|
-
<div className={cn("flex items-center gap-2", className)}>
|
|
43
|
-
<div
|
|
44
|
-
role="progressbar"
|
|
45
|
-
aria-label={label}
|
|
46
|
-
aria-valuenow={clamped}
|
|
47
|
-
aria-valuemin={0}
|
|
48
|
-
aria-valuemax={safeMax}
|
|
49
|
-
className="h-1.5 min-w-0 flex-1 overflow-hidden rounded-full bg-surface-3"
|
|
50
|
-
>
|
|
51
|
-
<div
|
|
52
|
-
// `currentColor` on a text-token class, so the fill tracks the
|
|
53
|
-
// same hue vocabulary every status surface uses rather than
|
|
54
|
-
// introducing a parallel set of bar colours.
|
|
55
|
-
className={cn("h-full rounded-full bg-current transition-[width] duration-300", hue.fg)}
|
|
56
|
-
style={{ width: `${percent}%` }}
|
|
57
|
-
/>
|
|
58
|
-
</div>
|
|
59
|
-
{showValue && (
|
|
60
|
-
<span className="shrink-0 font-mono text-caption text-subtle-foreground tabular-nums">
|
|
61
|
-
{clamped} / {safeMax}
|
|
62
|
-
</span>
|
|
63
|
-
)}
|
|
64
|
-
</div>
|
|
65
|
-
);
|
|
66
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { RadioGroup as HeadlessRadioGroup, Radio } from "@headlessui/react";
|
|
4
|
-
import type { ReactNode } from "react";
|
|
5
|
-
import { cn } from "../../lib/cn";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* A single-choice control over a small, fixed vocabulary.
|
|
9
|
-
*
|
|
10
|
-
* # Why this exists rather than reaching for `Select`
|
|
11
|
-
*
|
|
12
|
-
* Two reasons, and the second is a correctness one.
|
|
13
|
-
*
|
|
14
|
-
* The first is the one that prompted it: for a handful of options, a
|
|
15
|
-
* `Select` makes the reader click once to discover what the choices even
|
|
16
|
-
* are, and again to pick. A radio group shows the whole vocabulary and
|
|
17
|
-
* costs one click. `SenderIdRegistrationStatus` has four values and lives
|
|
18
|
-
* in a review drawer where seeing the other three *is* the decision.
|
|
19
|
-
*
|
|
20
|
-
* The second: this cannot hit the bug that made `Select` unusable inside a
|
|
21
|
-
* drawer for its entire existence (#315). Headless UI's `Listbox` portals
|
|
22
|
-
* its options to a top-level sibling of `<body>`, which lands outside
|
|
23
|
-
* vaul's Radix focus trap and stalls the enter transition —
|
|
24
|
-
* `opacity: 0`, `pointer-events: none`, measured live. `RadioGroup`
|
|
25
|
-
* renders inline with no portal and no transition, so there is no
|
|
26
|
-
* equivalent failure available to it. For a small vocabulary inside a
|
|
27
|
-
* drawer, that makes radio the *safer* control, not merely the friendlier
|
|
28
|
-
* one.
|
|
29
|
-
*
|
|
30
|
-
* Deliberately not built on `Select`'s CVA table: there are no visual
|
|
31
|
-
* variants to parameterise, and every option renders identically apart
|
|
32
|
-
* from its checked state.
|
|
33
|
-
*/
|
|
34
|
-
export interface RadioGroupOption<T extends string> {
|
|
35
|
-
value: T;
|
|
36
|
-
label: ReactNode;
|
|
37
|
-
/** Optional one-line explanation rendered under the label. */
|
|
38
|
-
description?: ReactNode;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface RadioGroupProps<T extends string> {
|
|
42
|
-
value: T | undefined;
|
|
43
|
-
onValueChange: (value: T) => void;
|
|
44
|
-
options: readonly RadioGroupOption<T>[];
|
|
45
|
-
/** Accessible name. Pair with `FormField`'s label via `aria-labelledby`
|
|
46
|
-
* where one exists; supplied directly when the group stands alone. */
|
|
47
|
-
"aria-label"?: string | undefined;
|
|
48
|
-
"aria-labelledby"?: string | undefined;
|
|
49
|
-
disabled?: boolean | undefined;
|
|
50
|
-
className?: string | undefined;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function RadioGroup<T extends string>({
|
|
54
|
-
value,
|
|
55
|
-
onValueChange,
|
|
56
|
-
options,
|
|
57
|
-
disabled,
|
|
58
|
-
className,
|
|
59
|
-
...aria
|
|
60
|
-
}: RadioGroupProps<T>) {
|
|
61
|
-
return (
|
|
62
|
-
<HeadlessRadioGroup
|
|
63
|
-
value={value ?? null}
|
|
64
|
-
onChange={(next: T | null) => {
|
|
65
|
-
if (next !== null) onValueChange(next);
|
|
66
|
-
}}
|
|
67
|
-
disabled={disabled ?? false}
|
|
68
|
-
className={cn("flex flex-wrap gap-2", className)}
|
|
69
|
-
{...aria}
|
|
70
|
-
>
|
|
71
|
-
{options.map((option) => (
|
|
72
|
-
<Radio
|
|
73
|
-
key={option.value}
|
|
74
|
-
value={option.value}
|
|
75
|
-
className={cn(
|
|
76
|
-
"flex cursor-pointer flex-col gap-0.5 rounded-sm border border-edge bg-surface-2 px-3 py-2 text-body text-muted-foreground",
|
|
77
|
-
"data-checked:border-state-success-border data-checked:bg-state-success-bg data-checked:text-state-success-fg",
|
|
78
|
-
"data-focus:outline-none data-focus:ring-1 data-focus:ring-state-success-border",
|
|
79
|
-
"data-disabled:cursor-not-allowed data-disabled:opacity-50",
|
|
80
|
-
)}
|
|
81
|
-
>
|
|
82
|
-
<span className="font-medium">{option.label}</span>
|
|
83
|
-
{option.description !== undefined && (
|
|
84
|
-
<span className="text-caption text-subtle-foreground">{option.description}</span>
|
|
85
|
-
)}
|
|
86
|
-
</Radio>
|
|
87
|
-
))}
|
|
88
|
-
</HeadlessRadioGroup>
|
|
89
|
-
);
|
|
90
|
-
}
|