@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.
Files changed (283) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/components/data/code.d.ts +0 -1
  3. package/dist/components/data/code.js +0 -1
  4. package/dist/components/data/copy-button.d.ts +0 -1
  5. package/dist/components/data/copy-button.js +0 -1
  6. package/dist/components/data/detail-row.d.ts +0 -1
  7. package/dist/components/data/detail-row.js +0 -1
  8. package/dist/components/data/id-display.d.ts +0 -1
  9. package/dist/components/data/id-display.js +0 -1
  10. package/dist/components/data/masked-value.d.ts +0 -1
  11. package/dist/components/data/masked-value.js +0 -1
  12. package/dist/components/data/money.d.ts +0 -1
  13. package/dist/components/data/money.js +0 -1
  14. package/dist/components/data/phone-display.d.ts +0 -1
  15. package/dist/components/data/phone-display.js +0 -1
  16. package/dist/components/data/stat-tile.d.ts +0 -1
  17. package/dist/components/data/stat-tile.js +0 -1
  18. package/dist/components/data/timestamp-display.d.ts +0 -1
  19. package/dist/components/data/timestamp-display.js +0 -1
  20. package/dist/components/patterns/inline-banner.d.ts +0 -1
  21. package/dist/components/patterns/inline-banner.js +0 -1
  22. package/dist/components/patterns/inline-empty-state.d.ts +0 -1
  23. package/dist/components/patterns/inline-empty-state.js +0 -1
  24. package/dist/components/patterns/live-row.d.ts +0 -1
  25. package/dist/components/patterns/live-row.js +0 -1
  26. package/dist/components/patterns/payload-inspector.d.ts +0 -1
  27. package/dist/components/patterns/payload-inspector.js +0 -1
  28. package/dist/components/patterns/route-skeleton.d.ts +0 -1
  29. package/dist/components/patterns/route-skeleton.js +0 -1
  30. package/dist/components/patterns/screen-layout.d.ts +0 -1
  31. package/dist/components/patterns/screen-layout.js +0 -1
  32. package/dist/components/patterns/stale-write-banner.d.ts +0 -1
  33. package/dist/components/patterns/stale-write-banner.js +0 -1
  34. package/dist/components/patterns/state-timeline.d.ts +0 -1
  35. package/dist/components/patterns/state-timeline.js +0 -1
  36. package/dist/components/primitives/badge.d.ts +0 -1
  37. package/dist/components/primitives/badge.js +0 -1
  38. package/dist/components/primitives/button.d.ts +0 -1
  39. package/dist/components/primitives/button.js +0 -1
  40. package/dist/components/primitives/calendar.d.ts +0 -1
  41. package/dist/components/primitives/calendar.js +0 -1
  42. package/dist/components/primitives/card.d.ts +0 -1
  43. package/dist/components/primitives/card.js +0 -1
  44. package/dist/components/primitives/checkbox.d.ts +0 -1
  45. package/dist/components/primitives/checkbox.js +0 -1
  46. package/dist/components/primitives/chip-select.d.ts +0 -1
  47. package/dist/components/primitives/chip-select.js +0 -1
  48. package/dist/components/primitives/command-menu.d.ts +0 -1
  49. package/dist/components/primitives/command-menu.js +0 -1
  50. package/dist/components/primitives/confirm-dialog.d.ts +0 -1
  51. package/dist/components/primitives/confirm-dialog.js +0 -1
  52. package/dist/components/primitives/date-picker.d.ts +0 -1
  53. package/dist/components/primitives/date-picker.js +0 -1
  54. package/dist/components/primitives/dialog.d.ts +0 -1
  55. package/dist/components/primitives/dialog.js +0 -1
  56. package/dist/components/primitives/drawer.d.ts +0 -1
  57. package/dist/components/primitives/drawer.js +0 -1
  58. package/dist/components/primitives/dropdown-menu.d.ts +0 -1
  59. package/dist/components/primitives/dropdown-menu.js +0 -1
  60. package/dist/components/primitives/form-field.d.ts +0 -1
  61. package/dist/components/primitives/form-field.js +0 -1
  62. package/dist/components/primitives/inline-confirm.d.ts +0 -1
  63. package/dist/components/primitives/inline-confirm.js +0 -1
  64. package/dist/components/primitives/input.d.ts +0 -1
  65. package/dist/components/primitives/input.js +0 -1
  66. package/dist/components/primitives/label.d.ts +0 -1
  67. package/dist/components/primitives/label.js +0 -1
  68. package/dist/components/primitives/pagination.d.ts +0 -1
  69. package/dist/components/primitives/pagination.js +0 -1
  70. package/dist/components/primitives/popover.d.ts +0 -1
  71. package/dist/components/primitives/popover.js +0 -1
  72. package/dist/components/primitives/progress.d.ts +0 -1
  73. package/dist/components/primitives/progress.js +0 -1
  74. package/dist/components/primitives/radio-group.d.ts +0 -1
  75. package/dist/components/primitives/radio-group.js +0 -1
  76. package/dist/components/primitives/select.d.ts +0 -1
  77. package/dist/components/primitives/select.js +0 -1
  78. package/dist/components/primitives/separator.d.ts +0 -1
  79. package/dist/components/primitives/separator.js +0 -1
  80. package/dist/components/primitives/side-nav.d.ts +0 -1
  81. package/dist/components/primitives/side-nav.js +0 -1
  82. package/dist/components/primitives/skeleton.d.ts +0 -1
  83. package/dist/components/primitives/skeleton.js +0 -1
  84. package/dist/components/primitives/spinner.d.ts +0 -1
  85. package/dist/components/primitives/spinner.js +0 -1
  86. package/dist/components/primitives/switch.d.ts +0 -1
  87. package/dist/components/primitives/switch.js +0 -1
  88. package/dist/components/primitives/table.d.ts +0 -1
  89. package/dist/components/primitives/table.js +0 -1
  90. package/dist/components/primitives/tabs.d.ts +0 -1
  91. package/dist/components/primitives/tabs.js +0 -1
  92. package/dist/components/primitives/textarea.d.ts +0 -1
  93. package/dist/components/primitives/textarea.js +0 -1
  94. package/dist/components/primitives/toast.d.ts +0 -1
  95. package/dist/components/primitives/toast.js +0 -1
  96. package/dist/components/primitives/tooltip.d.ts +0 -1
  97. package/dist/components/primitives/tooltip.js +0 -1
  98. package/dist/components/status/state-chip.d.ts +0 -1
  99. package/dist/components/status/state-chip.js +0 -1
  100. package/dist/components/status/state-mark.d.ts +0 -1
  101. package/dist/components/status/state-mark.js +0 -1
  102. package/dist/components/status/status-pill.d.ts +0 -1
  103. package/dist/components/status/status-pill.js +0 -1
  104. package/dist/components/status/status-tokens.d.ts +0 -1
  105. package/dist/components/status/status-tokens.js +0 -1
  106. package/dist/index.d.ts +0 -1
  107. package/dist/index.js +0 -1
  108. package/dist/lib/cn.d.ts +0 -1
  109. package/dist/lib/cn.js +0 -1
  110. package/dist/lib/money.d.ts +0 -1
  111. package/dist/lib/money.js +0 -1
  112. package/dist/lib/omit-undefined.d.ts +0 -1
  113. package/dist/lib/omit-undefined.js +0 -1
  114. package/package.json +3 -7
  115. package/dist/components/data/code.d.ts.map +0 -1
  116. package/dist/components/data/code.js.map +0 -1
  117. package/dist/components/data/copy-button.d.ts.map +0 -1
  118. package/dist/components/data/copy-button.js.map +0 -1
  119. package/dist/components/data/detail-row.d.ts.map +0 -1
  120. package/dist/components/data/detail-row.js.map +0 -1
  121. package/dist/components/data/id-display.d.ts.map +0 -1
  122. package/dist/components/data/id-display.js.map +0 -1
  123. package/dist/components/data/masked-value.d.ts.map +0 -1
  124. package/dist/components/data/masked-value.js.map +0 -1
  125. package/dist/components/data/money.d.ts.map +0 -1
  126. package/dist/components/data/money.js.map +0 -1
  127. package/dist/components/data/phone-display.d.ts.map +0 -1
  128. package/dist/components/data/phone-display.js.map +0 -1
  129. package/dist/components/data/stat-tile.d.ts.map +0 -1
  130. package/dist/components/data/stat-tile.js.map +0 -1
  131. package/dist/components/data/timestamp-display.d.ts.map +0 -1
  132. package/dist/components/data/timestamp-display.js.map +0 -1
  133. package/dist/components/patterns/inline-banner.d.ts.map +0 -1
  134. package/dist/components/patterns/inline-banner.js.map +0 -1
  135. package/dist/components/patterns/inline-empty-state.d.ts.map +0 -1
  136. package/dist/components/patterns/inline-empty-state.js.map +0 -1
  137. package/dist/components/patterns/live-row.d.ts.map +0 -1
  138. package/dist/components/patterns/live-row.js.map +0 -1
  139. package/dist/components/patterns/payload-inspector.d.ts.map +0 -1
  140. package/dist/components/patterns/payload-inspector.js.map +0 -1
  141. package/dist/components/patterns/route-skeleton.d.ts.map +0 -1
  142. package/dist/components/patterns/route-skeleton.js.map +0 -1
  143. package/dist/components/patterns/screen-layout.d.ts.map +0 -1
  144. package/dist/components/patterns/screen-layout.js.map +0 -1
  145. package/dist/components/patterns/stale-write-banner.d.ts.map +0 -1
  146. package/dist/components/patterns/stale-write-banner.js.map +0 -1
  147. package/dist/components/patterns/state-timeline.d.ts.map +0 -1
  148. package/dist/components/patterns/state-timeline.js.map +0 -1
  149. package/dist/components/primitives/badge.d.ts.map +0 -1
  150. package/dist/components/primitives/badge.js.map +0 -1
  151. package/dist/components/primitives/button.d.ts.map +0 -1
  152. package/dist/components/primitives/button.js.map +0 -1
  153. package/dist/components/primitives/calendar.d.ts.map +0 -1
  154. package/dist/components/primitives/calendar.js.map +0 -1
  155. package/dist/components/primitives/card.d.ts.map +0 -1
  156. package/dist/components/primitives/card.js.map +0 -1
  157. package/dist/components/primitives/checkbox.d.ts.map +0 -1
  158. package/dist/components/primitives/checkbox.js.map +0 -1
  159. package/dist/components/primitives/chip-select.d.ts.map +0 -1
  160. package/dist/components/primitives/chip-select.js.map +0 -1
  161. package/dist/components/primitives/command-menu.d.ts.map +0 -1
  162. package/dist/components/primitives/command-menu.js.map +0 -1
  163. package/dist/components/primitives/confirm-dialog.d.ts.map +0 -1
  164. package/dist/components/primitives/confirm-dialog.js.map +0 -1
  165. package/dist/components/primitives/date-picker.d.ts.map +0 -1
  166. package/dist/components/primitives/date-picker.js.map +0 -1
  167. package/dist/components/primitives/dialog.d.ts.map +0 -1
  168. package/dist/components/primitives/dialog.js.map +0 -1
  169. package/dist/components/primitives/drawer.d.ts.map +0 -1
  170. package/dist/components/primitives/drawer.js.map +0 -1
  171. package/dist/components/primitives/dropdown-menu.d.ts.map +0 -1
  172. package/dist/components/primitives/dropdown-menu.js.map +0 -1
  173. package/dist/components/primitives/form-field.d.ts.map +0 -1
  174. package/dist/components/primitives/form-field.js.map +0 -1
  175. package/dist/components/primitives/inline-confirm.d.ts.map +0 -1
  176. package/dist/components/primitives/inline-confirm.js.map +0 -1
  177. package/dist/components/primitives/input.d.ts.map +0 -1
  178. package/dist/components/primitives/input.js.map +0 -1
  179. package/dist/components/primitives/label.d.ts.map +0 -1
  180. package/dist/components/primitives/label.js.map +0 -1
  181. package/dist/components/primitives/pagination.d.ts.map +0 -1
  182. package/dist/components/primitives/pagination.js.map +0 -1
  183. package/dist/components/primitives/popover.d.ts.map +0 -1
  184. package/dist/components/primitives/popover.js.map +0 -1
  185. package/dist/components/primitives/progress.d.ts.map +0 -1
  186. package/dist/components/primitives/progress.js.map +0 -1
  187. package/dist/components/primitives/radio-group.d.ts.map +0 -1
  188. package/dist/components/primitives/radio-group.js.map +0 -1
  189. package/dist/components/primitives/select.d.ts.map +0 -1
  190. package/dist/components/primitives/select.js.map +0 -1
  191. package/dist/components/primitives/separator.d.ts.map +0 -1
  192. package/dist/components/primitives/separator.js.map +0 -1
  193. package/dist/components/primitives/side-nav.d.ts.map +0 -1
  194. package/dist/components/primitives/side-nav.js.map +0 -1
  195. package/dist/components/primitives/skeleton.d.ts.map +0 -1
  196. package/dist/components/primitives/skeleton.js.map +0 -1
  197. package/dist/components/primitives/spinner.d.ts.map +0 -1
  198. package/dist/components/primitives/spinner.js.map +0 -1
  199. package/dist/components/primitives/switch.d.ts.map +0 -1
  200. package/dist/components/primitives/switch.js.map +0 -1
  201. package/dist/components/primitives/table.d.ts.map +0 -1
  202. package/dist/components/primitives/table.js.map +0 -1
  203. package/dist/components/primitives/tabs.d.ts.map +0 -1
  204. package/dist/components/primitives/tabs.js.map +0 -1
  205. package/dist/components/primitives/textarea.d.ts.map +0 -1
  206. package/dist/components/primitives/textarea.js.map +0 -1
  207. package/dist/components/primitives/toast.d.ts.map +0 -1
  208. package/dist/components/primitives/toast.js.map +0 -1
  209. package/dist/components/primitives/tooltip.d.ts.map +0 -1
  210. package/dist/components/primitives/tooltip.js.map +0 -1
  211. package/dist/components/status/state-chip.d.ts.map +0 -1
  212. package/dist/components/status/state-chip.js.map +0 -1
  213. package/dist/components/status/state-mark.d.ts.map +0 -1
  214. package/dist/components/status/state-mark.js.map +0 -1
  215. package/dist/components/status/status-pill.d.ts.map +0 -1
  216. package/dist/components/status/status-pill.js.map +0 -1
  217. package/dist/components/status/status-tokens.d.ts.map +0 -1
  218. package/dist/components/status/status-tokens.js.map +0 -1
  219. package/dist/index.d.ts.map +0 -1
  220. package/dist/index.js.map +0 -1
  221. package/dist/lib/cn.d.ts.map +0 -1
  222. package/dist/lib/cn.js.map +0 -1
  223. package/dist/lib/money.d.ts.map +0 -1
  224. package/dist/lib/money.js.map +0 -1
  225. package/dist/lib/omit-undefined.d.ts.map +0 -1
  226. package/dist/lib/omit-undefined.js.map +0 -1
  227. package/src/components/data/code.tsx +0 -43
  228. package/src/components/data/copy-button.tsx +0 -97
  229. package/src/components/data/detail-row.tsx +0 -113
  230. package/src/components/data/id-display.tsx +0 -49
  231. package/src/components/data/masked-value.tsx +0 -96
  232. package/src/components/data/money.tsx +0 -46
  233. package/src/components/data/phone-display.tsx +0 -60
  234. package/src/components/data/stat-tile.tsx +0 -68
  235. package/src/components/data/timestamp-display.tsx +0 -106
  236. package/src/components/patterns/inline-banner.tsx +0 -60
  237. package/src/components/patterns/inline-empty-state.tsx +0 -45
  238. package/src/components/patterns/live-row.tsx +0 -75
  239. package/src/components/patterns/payload-inspector.tsx +0 -157
  240. package/src/components/patterns/route-skeleton.tsx +0 -77
  241. package/src/components/patterns/screen-layout.tsx +0 -44
  242. package/src/components/patterns/stale-write-banner.tsx +0 -44
  243. package/src/components/patterns/state-timeline.tsx +0 -229
  244. package/src/components/primitives/badge.tsx +0 -40
  245. package/src/components/primitives/button.tsx +0 -54
  246. package/src/components/primitives/calendar.tsx +0 -136
  247. package/src/components/primitives/card.tsx +0 -70
  248. package/src/components/primitives/checkbox.tsx +0 -101
  249. package/src/components/primitives/chip-select.tsx +0 -105
  250. package/src/components/primitives/command-menu.tsx +0 -92
  251. package/src/components/primitives/confirm-dialog.tsx +0 -109
  252. package/src/components/primitives/date-picker.tsx +0 -271
  253. package/src/components/primitives/dialog.tsx +0 -195
  254. package/src/components/primitives/drawer.tsx +0 -326
  255. package/src/components/primitives/dropdown-menu.tsx +0 -128
  256. package/src/components/primitives/form-field.tsx +0 -136
  257. package/src/components/primitives/inline-confirm.tsx +0 -118
  258. package/src/components/primitives/input.tsx +0 -21
  259. package/src/components/primitives/label.tsx +0 -43
  260. package/src/components/primitives/pagination.tsx +0 -110
  261. package/src/components/primitives/popover.tsx +0 -41
  262. package/src/components/primitives/progress.tsx +0 -66
  263. package/src/components/primitives/radio-group.tsx +0 -90
  264. package/src/components/primitives/select.tsx +0 -254
  265. package/src/components/primitives/separator.tsx +0 -49
  266. package/src/components/primitives/side-nav.tsx +0 -253
  267. package/src/components/primitives/skeleton.tsx +0 -25
  268. package/src/components/primitives/spinner.tsx +0 -39
  269. package/src/components/primitives/switch.tsx +0 -74
  270. package/src/components/primitives/table.tsx +0 -157
  271. package/src/components/primitives/tabs.tsx +0 -163
  272. package/src/components/primitives/textarea.tsx +0 -21
  273. package/src/components/primitives/toast.tsx +0 -117
  274. package/src/components/primitives/tooltip.tsx +0 -38
  275. package/src/components/status/state-chip.tsx +0 -70
  276. package/src/components/status/state-mark.tsx +0 -154
  277. package/src/components/status/status-pill.tsx +0 -153
  278. package/src/components/status/status-tokens.ts +0 -173
  279. package/src/index.ts +0 -96
  280. package/src/lib/cn.ts +0 -168
  281. package/src/lib/money.ts +0 -164
  282. package/src/lib/omit-undefined.ts +0 -25
  283. /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
- }