@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,74 +0,0 @@
1
- "use client";
2
-
3
- import { Field, Label as HeadlessLabel, Switch as HeadlessSwitch } from "@headlessui/react";
4
- import type { ReactNode } from "react";
5
- import { cn } from "../../lib/cn";
6
-
7
- export interface SwitchProps {
8
- checked: boolean;
9
- onCheckedChange: (checked: boolean) => void;
10
- disabled?: boolean | undefined;
11
- "aria-label"?: string | undefined;
12
- "aria-labelledby"?: string | undefined;
13
- className?: string | undefined;
14
- }
15
-
16
- /**
17
- * An on/off toggle that takes effect immediately.
18
- *
19
- * **Use a `Checkbox`, not this, inside a form with a Save button.** The
20
- * distinction is not cosmetic: a switch promises the change has already
21
- * happened, so pairing one with a Save button tells the operator two
22
- * contradictory things and leaves them unsure whether the toggle landed.
23
- * A switch belongs where the write fires on change and the UI can report
24
- * the result.
25
- */
26
- export function Switch({ checked, onCheckedChange, disabled, className, ...aria }: SwitchProps) {
27
- return (
28
- <HeadlessSwitch
29
- checked={checked}
30
- onChange={onCheckedChange}
31
- disabled={disabled ?? false}
32
- className={cn(
33
- "group relative inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border border-edge-strong bg-surface-3 transition-colors",
34
- "data-checked:border-primary data-checked:bg-primary",
35
- "data-focus:outline-none data-focus:ring-1 data-focus:ring-ring",
36
- "data-disabled:cursor-not-allowed data-disabled:opacity-50",
37
- className,
38
- )}
39
- {...aria}
40
- >
41
- <span
42
- aria-hidden="true"
43
- className={cn(
44
- "pointer-events-none ml-0.5 inline-block size-3.5 rounded-full bg-foreground transition-transform",
45
- "group-data-checked:translate-x-4 group-data-checked:bg-primary-content",
46
- )}
47
- />
48
- </HeadlessSwitch>
49
- );
50
- }
51
-
52
- export interface SwitchFieldProps extends Omit<SwitchProps, "aria-label" | "aria-labelledby"> {
53
- label: ReactNode;
54
- description?: ReactNode;
55
- }
56
-
57
- /** A switch with its label to the left, filling the available width — the
58
- * settings-row layout, where the labels form a readable column and the
59
- * controls line up on the right. */
60
- export function SwitchField({ label, description, className, ...props }: SwitchFieldProps) {
61
- return (
62
- <Field className={cn("flex items-center justify-between gap-4", className)}>
63
- <span className="flex flex-col gap-0.5">
64
- <HeadlessLabel className="cursor-pointer text-body text-foreground data-disabled:cursor-not-allowed data-disabled:opacity-50">
65
- {label}
66
- </HeadlessLabel>
67
- {description !== undefined && (
68
- <span className="text-caption text-muted-foreground">{description}</span>
69
- )}
70
- </span>
71
- <Switch {...props} />
72
- </Field>
73
- );
74
- }
@@ -1,157 +0,0 @@
1
- import type { HTMLAttributes, TdHTMLAttributes, ThHTMLAttributes } from "react";
2
- import { cn } from "../../lib/cn";
3
-
4
- // Table conventions per design doc §6.4: sticky header on surface-1, 1px
5
- // `border-edge-subtle` row dividers, no zebra striping (the status tints
6
- // are the signal — zebra plus tints is noise), hover on surface-3.
7
- //
8
- // D16: daisyUI's real `table`/`table-pin-rows` classes sit underneath this
9
- // file's own bespoke row/cell behaviour (constraint 7 — "DaisyUI does the
10
- // work") rather than replacing it. Every own-authored utility class below
11
- // is unchanged from before this PR; `table`/`table-pin-rows` are added on
12
- // top and reconciled against three things confirmed by reading
13
- // `daisyui/components/table.css` directly, not assumed:
14
- //
15
- // 1. `table-pin-rows` alone does nothing — every one of its selectors is
16
- // `.table :where(.table-pin-rows thead)`, i.e. it only matches when
17
- // `table-pin-rows` sits on the same element as `table`. Both classes go
18
- // on `<table>` together, never one without the other.
19
- // 2. `.table` switches `border-collapse` to `separate`. Under the separate
20
- // border model a border set on `<tr>` — this file's whole row-divider
21
- // mechanism, `TableRow`'s `border-edge-subtle border-b` — does not
22
- // render at all; the CSS spec confines borders in that model to cells,
23
- // never rows or sections. `border-collapse!` (Tailwind v4's trailing-`!`
24
- // important syntax) stays on `<table>` specifically so adding
25
- // `.table` doesn't silently delete every row divider in this app —
26
- // verified live against a real render (`just demo`, `/messages`) with
27
- // and without the override before trusting it.
28
- // 3. `.table` also applies its own `padding-block`/`padding-inline` to
29
- // every `th`/`td` via a zero-specificity-inside `:where()` selector,
30
- // and its own `font-size`. `TableHead`/`TableCell` keep their existing
31
- // `px-3`/`py-2`/`h-8` utilities unchanged, at equal selector
32
- // specificity to daisyUI's rule — confirmed live that the existing
33
- // cell padding and type scale still win, not daisyUI's defaults.
34
- export function Table({ className, ...props }: HTMLAttributes<HTMLTableElement>) {
35
- return (
36
- <div className="w-full overflow-x-auto">
37
- <table
38
- className={cn("table table-pin-rows w-full border-collapse! text-body", className)}
39
- {...props}
40
- />
41
- </div>
42
- );
43
- }
44
-
45
- export function TableHeader({ className, ...props }: HTMLAttributes<HTMLTableSectionElement>) {
46
- return (
47
- <thead
48
- className={cn(
49
- "sticky top-0 z-10 bg-surface-1 [&_tr]:border-b [&_tr]:border-edge [&_tr]:bg-surface-1",
50
- className,
51
- )}
52
- {...props}
53
- />
54
- );
55
- }
56
-
57
- export function TableBody({ className, ...props }: HTMLAttributes<HTMLTableSectionElement>) {
58
- return <tbody className={cn("[&_tr:last-child]:border-b-0", className)} {...props} />;
59
- }
60
-
61
- export interface TableRowProps extends HTMLAttributes<HTMLTableRowElement> {
62
- selected?: boolean;
63
- }
64
-
65
- export function TableRow({ className, selected = false, ...props }: TableRowProps) {
66
- return (
67
- <tr
68
- className={cn(
69
- "border-edge-subtle border-b transition-colors duration-[var(--dur-instant)] hover:bg-surface-3",
70
- selected && "bg-surface-3 shadow-[inset_2px_0_0_var(--color-ring)]",
71
- className,
72
- )}
73
- {...props}
74
- />
75
- );
76
- }
77
-
78
- /**
79
- * Responsive column visibility. `hideBelow="md"` hides the cell on
80
- * viewports narrower than `md` and shows it from `md` up.
81
- *
82
- * This exists because the class it replaces was the single
83
- * most-duplicated string in the console: `"hidden sm:table-cell"`,
84
- * `"hidden md:table-cell"` and `"hidden lg:table-cell"` appeared **90
85
- * times** as inline literals across the route-local table components.
86
- *
87
- * Worth recording how they got there, because it is a lesson about the
88
- * rule and not just about tables. R6's own text names
89
- * `const COL_ID = "hidden lg:table-cell"` in `jobs-screen.tsx` — four
90
- * hoisted consts — as the motivating example of a class const that must
91
- * not live in a view. The R6 sweep removed those four consts and, in
92
- * moving the markup into dumb components, re-expressed the same decision
93
- * as 90 inline literals. The letter of the rule was satisfied (no classes
94
- * in a *view* file); the thing the rule exists to prevent — one decision
95
- * written in many places — got 22× worse.
96
- *
97
- * A mapping table rather than an interpolated `` `hidden ${bp}:table-cell` ``
98
- * because Tailwind scans source text statically: a template literal
99
- * produces no class at all in the built CSS. The full strings must appear
100
- * verbatim somewhere Tailwind can see them, and this is that place.
101
- */
102
- const HIDE_BELOW: Record<Breakpoint, string> = {
103
- sm: "hidden sm:table-cell",
104
- md: "hidden md:table-cell",
105
- lg: "hidden lg:table-cell",
106
- xl: "hidden xl:table-cell",
107
- };
108
-
109
- export type Breakpoint = "sm" | "md" | "lg" | "xl";
110
-
111
- export interface TableHeadProps extends Omit<ThHTMLAttributes<HTMLTableCellElement>, "align"> {
112
- align?: "start" | "end";
113
- /** Hide this column below the given breakpoint. See `HIDE_BELOW`. */
114
- hideBelow?: Breakpoint | undefined;
115
- }
116
-
117
- export function TableHead({ className, align = "start", hideBelow, ...props }: TableHeadProps) {
118
- return (
119
- <th
120
- className={cn(
121
- "h-8 whitespace-nowrap px-3 font-medium text-micro text-muted-foreground tracking-[0.03em]",
122
- align === "end" ? "text-right" : "text-left",
123
- hideBelow && HIDE_BELOW[hideBelow],
124
- className,
125
- )}
126
- {...props}
127
- />
128
- );
129
- }
130
-
131
- export interface TableCellProps extends Omit<TdHTMLAttributes<HTMLTableCellElement>, "align"> {
132
- align?: "start" | "end";
133
- mono?: boolean;
134
- /** Hide this column below the given breakpoint. See `HIDE_BELOW`. */
135
- hideBelow?: Breakpoint | undefined;
136
- }
137
-
138
- export function TableCell({
139
- className,
140
- align = "start",
141
- mono = false,
142
- hideBelow,
143
- ...props
144
- }: TableCellProps) {
145
- return (
146
- <td
147
- className={cn(
148
- "px-3 py-2 text-body",
149
- align === "end" ? "text-right" : "text-left",
150
- mono && "font-mono tabular-nums",
151
- hideBelow && HIDE_BELOW[hideBelow],
152
- className,
153
- )}
154
- {...props}
155
- />
156
- );
157
- }
@@ -1,163 +0,0 @@
1
- "use client";
2
-
3
- import { Tab, TabGroup, TabList, TabPanel, TabPanels } from "@headlessui/react";
4
- import {
5
- Children,
6
- Fragment,
7
- isValidElement,
8
- type ReactElement,
9
- type ReactNode,
10
- useMemo,
11
- useState,
12
- } from "react";
13
- import { cn } from "../../lib/cn";
14
-
15
- /**
16
- * D18/D3: Radix `Tabs` → Headless UI `TabGroup`, behind a value-based
17
- * `ValueTabs` adapter. Radix's `Tabs` was value-based and controlled
18
- * (`value`/`onValueChange`/`defaultValue`); Headless UI's own `TabGroup` is
19
- * index-based (`selectedIndex`/`onChange(index)`). Four call sites
20
- * (`payload-inspector.tsx`, `users-screen.tsx`, and the gallery) already
21
- * depend on the value-based shape, and a value-based API is more resistant
22
- * to bugs when tab order changes — so this adapter translates once, here,
23
- * rather than rewriting every call site to track an index.
24
- *
25
- * Every consumer's own JSX is untouched: only the import line changes, via
26
- * aliasing (`import { ValueTabs as Tabs, ... } from "@vaam-apps/ui"`) — the one
27
- * genuinely necessary API break this bucket owns (see each updated file's
28
- * own import for the one-line diff).
29
- */
30
-
31
- function collectTriggerValues(node: ReactNode, out: string[] = []): string[] {
32
- Children.forEach(node, (child) => {
33
- if (!isValidElement(child)) return;
34
- const el = child as ReactElement<{ value?: string; children?: ReactNode }>;
35
- if (el.type === ValueTabsTrigger) {
36
- if (typeof el.props.value === "string") out.push(el.props.value);
37
- return;
38
- }
39
- if (el.props?.children != null) collectTriggerValues(el.props.children, out);
40
- });
41
- return out;
42
- }
43
-
44
- /** Separates the one `ValueTabsList` child from everything else, so
45
- * `ValueTabs` can group "everything else" (the `ValueTabsContent` panels,
46
- * declared as direct siblings of `ValueTabsList` — the exact Radix `Tabs`
47
- * shape) under one implicit `TabPanels`, which Headless UI's `TabGroup`
48
- * requires but the old Radix-shaped call sites never had to write. */
49
- function splitOutList(children: ReactNode): { list: ReactNode; rest: ReactNode[] } {
50
- let list: ReactNode = null;
51
- const rest: ReactNode[] = [];
52
- Children.forEach(children, (child) => {
53
- if (isValidElement(child) && child.type === ValueTabsList) {
54
- list = child;
55
- } else {
56
- rest.push(child);
57
- }
58
- });
59
- return { list, rest };
60
- }
61
-
62
- /**
63
- * Note the `| undefined` on the optional props below. This package
64
- * compiles under `exactOptionalPropertyTypes`, where a bare `value?:
65
- * string` means "you may omit this key" and NOT "you may pass
66
- * `undefined`" — so the ordinary controlled pattern,
67
- * `const [v, setV] = useState<string>()` followed by `value={v}`, is a
68
- * type error. Found writing this component's own story, which is the
69
- * first code outside the original application to use it.
70
- */
71
- export interface ValueTabsProps {
72
- value?: string | undefined;
73
- defaultValue?: string | undefined;
74
- onValueChange?: ((value: string) => void) | undefined;
75
- className?: string | undefined;
76
- children: ReactNode;
77
- }
78
-
79
- export function ValueTabs({
80
- value,
81
- defaultValue,
82
- onValueChange,
83
- className,
84
- children,
85
- }: ValueTabsProps) {
86
- const values = useMemo(() => collectTriggerValues(children), [children]);
87
- const { list, rest } = useMemo(() => splitOutList(children), [children]);
88
- const [internalValue, setInternalValue] = useState(defaultValue ?? values[0]);
89
- const isControlled = value !== undefined;
90
- const currentValue = isControlled ? value : internalValue;
91
- const selectedIndex = Math.max(0, values.indexOf(currentValue ?? values[0] ?? ""));
92
-
93
- function handleChange(index: number) {
94
- const next = values[index];
95
- if (next === undefined) return;
96
- if (!isControlled) setInternalValue(next);
97
- onValueChange?.(next);
98
- }
99
-
100
- return (
101
- <TabGroup className={className} selectedIndex={selectedIndex} onChange={handleChange}>
102
- {list}
103
- <TabPanels as={Fragment}>{rest}</TabPanels>
104
- </TabGroup>
105
- );
106
- }
107
-
108
- export function ValueTabsList({
109
- className,
110
- children,
111
- }: {
112
- className?: string;
113
- children: ReactNode;
114
- }) {
115
- return (
116
- <TabList className={cn("flex items-center gap-4 border-edge border-b", className)}>
117
- {children}
118
- </TabList>
119
- );
120
- }
121
-
122
- // Underline variant only (design doc §5.2: "no pill/segmented variant — pill
123
- // tabs are consumer furniture"). 2px bottom rule in --foreground on the
124
- // active tab.
125
- export function ValueTabsTrigger({
126
- value,
127
- className,
128
- children,
129
- }: {
130
- /** Consumed by `collectTriggerValues` above via prop introspection on the
131
- * element tree, not read inside this component itself. */
132
- value: string;
133
- className?: string;
134
- children: ReactNode;
135
- }) {
136
- void value;
137
- return (
138
- <Tab
139
- type="button"
140
- className={cn(
141
- "-mb-px border-b-2 border-transparent px-1 py-2 font-medium text-body text-muted-foreground outline-none",
142
- "data-selected:border-foreground data-selected:text-foreground",
143
- className,
144
- )}
145
- >
146
- {children}
147
- </Tab>
148
- );
149
- }
150
-
151
- export function ValueTabsContent({
152
- className,
153
- children,
154
- }: {
155
- /** Kept for API parity with the old value-based `TabsContent` — the real
156
- * Tab↔Panel association is positional (Headless UI's own `TabGroup`
157
- * matches `Tab`/`TabPanel` by declaration order), not looked up by value. */
158
- value: string;
159
- className?: string;
160
- children: ReactNode;
161
- }) {
162
- return <TabPanel className={cn("pt-4", className)}>{children}</TabPanel>;
163
- }
@@ -1,21 +0,0 @@
1
- import type { TextareaHTMLAttributes } from "react";
2
- import { forwardRef } from "react";
3
- import { cn } from "../../lib/cn";
4
-
5
- export type TextareaProps = TextareaHTMLAttributes<HTMLTextAreaElement>;
6
-
7
- // D8: same reasoning as `input.tsx` — daisyUI's own `.textarea` rule sets
8
- // `border-radius: var(--radius-field)` directly (confirmed by reading
9
- // `daisyui/components/textarea.css`), so no `rounded-*` class is added
10
- // here. The class string is unchanged; the token rewrite in Phase 0 is
11
- // what moved the rendered radius.
12
- export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
13
- ({ className, ...props }, ref) => (
14
- <textarea
15
- ref={ref}
16
- className={cn("textarea textarea-bordered w-full font-sans text-prose", className)}
17
- {...props}
18
- />
19
- ),
20
- );
21
- Textarea.displayName = "Textarea";
@@ -1,117 +0,0 @@
1
- "use client";
2
-
3
- import { cva } from "class-variance-authority";
4
- import { useSyncExternalStore } from "react";
5
- import { cn } from "../../lib/cn";
6
-
7
- /**
8
- * Toasts, no Radix — `dialog`/`dropdown-menu`/`select`/`tooltip`/`popover`
9
- * are the five primitives that need Radix's behaviour (design doc T6
10
- * brief); a toast is a transient, non-modal, non-focus-trapping
11
- * notification, so a small hand-rolled store is enough.
12
- *
13
- * For transient confirmations only ("copied", "saved", "replay queued") —
14
- * design doc §5.1: anything an operator must act on is inline, never a
15
- * toast, because a toast that expires while they're reading a payload is a
16
- * lost message.
17
- */
18
-
19
- export type ToastVariant = "default" | "success" | "danger";
20
-
21
- export interface ToastItem {
22
- id: string;
23
- title: string;
24
- description?: string;
25
- variant?: ToastVariant;
26
- durationMs?: number;
27
- }
28
-
29
- type Listener = () => void;
30
-
31
- let toasts: ToastItem[] = [];
32
- const listeners = new Set<Listener>();
33
-
34
- function emit() {
35
- for (const listener of listeners) listener();
36
- }
37
-
38
- function subscribe(listener: Listener): () => void {
39
- listeners.add(listener);
40
- return () => listeners.delete(listener);
41
- }
42
-
43
- function getSnapshot(): ToastItem[] {
44
- return toasts;
45
- }
46
-
47
- export function dismissToast(id: string) {
48
- toasts = toasts.filter((t) => t.id !== id);
49
- emit();
50
- }
51
-
52
- export function toast(item: Omit<ToastItem, "id">): string {
53
- const id = crypto.randomUUID();
54
- const durationMs = item.durationMs ?? 4000;
55
- toasts = [...toasts, { ...item, id }];
56
- emit();
57
- if (durationMs > 0) {
58
- setTimeout(() => dismissToast(id), durationMs);
59
- }
60
- return id;
61
- }
62
-
63
- // D11: `cva()` replaces the previous `Record<ToastVariant, string>` lookup,
64
- // same three class strings per variant, keyed identically. One deliberate
65
- // D8 diff lives in the base string below, not here: the shared "toast card"
66
- // classes move off `rounded-sm` (`--radius-field`, 12px) onto `rounded-box`
67
- // (`--radius-box`, 20px) — a toast shares `--shadow-popover` with
68
- // dialog/popover/dropdown/drawer (see `theme.css`'s own "only floating
69
- // layers... get one [shadow]" comment), i.e. it's the same family of
70
- // floating panel those get, not a field-scale control, and D14's own
71
- // drawer sketch (§6.4) already uses `rounded-t-box` for exactly that
72
- // family. `variant` classes themselves are untouched.
73
- const toastVariants = cva(
74
- "pointer-events-auto rounded-box border p-3 text-body shadow-[var(--shadow-popover)]",
75
- {
76
- variants: {
77
- variant: {
78
- default: "border-edge bg-surface-2 text-foreground",
79
- success: "border-state-success-fg/30 bg-surface-2 text-foreground",
80
- danger: "border-state-danger-border bg-state-danger-bg text-state-danger-fg",
81
- },
82
- },
83
- defaultVariants: { variant: "default" },
84
- },
85
- );
86
-
87
- /** Mount once, near the app root. Renders the live toast stack. */
88
- export function Toaster() {
89
- const items = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
90
-
91
- return (
92
- <div
93
- role="status"
94
- aria-live="polite"
95
- className="pointer-events-none fixed right-4 bottom-4 z-50 flex w-80 flex-col gap-2"
96
- >
97
- {items.map((item) => (
98
- <div key={item.id} className={cn(toastVariants({ variant: item.variant ?? "default" }))}>
99
- <div className="flex items-start justify-between gap-2">
100
- <p className="font-medium">{item.title}</p>
101
- <button
102
- type="button"
103
- onClick={() => dismissToast(item.id)}
104
- aria-label="Dismiss"
105
- className="text-subtle-foreground hover:text-foreground"
106
- >
107
- ×
108
- </button>
109
- </div>
110
- {item.description != null && (
111
- <p className="mt-1 text-caption text-muted-foreground">{item.description}</p>
112
- )}
113
- </div>
114
- ))}
115
- </div>
116
- );
117
- }
@@ -1,38 +0,0 @@
1
- import type { ReactNode } from "react";
2
- import { cn } from "../../lib/cn";
3
-
4
- /**
5
- * D5: `@radix-ui/react-tooltip` is deleted outright, replaced by DaisyUI's
6
- * native `.tooltip`/`data-tip` CSS component — no JS, no portal, hover/focus
7
- * driven by `:hover`/`:focus` in CSS alone. Headless UI ships no Tooltip
8
- * equivalent, and introducing a second behaviour library (e.g.
9
- * `@floating-ui/react`) just to replace one Radix package with another
10
- * contradicts constraints 6–7.
11
- *
12
- * **Accepted limitation (named explicitly in the design doc, D5):** `data-tip`
13
- * is a plain HTML attribute rendered via CSS `content: attr(data-tip)`, so
14
- * the label must be a string — no rich/interactive tooltip content anywhere
15
- * in this console. Nothing today needs one (`side-nav.tsx`'s own icon-rail
16
- * tooltips already use the identical `tooltip`/`data-tip` convention
17
- * directly, predating this file).
18
- *
19
- * The API collapses Radix's four-part compound
20
- * (`TooltipProvider`/`Tooltip`/`TooltipTrigger`/`TooltipContent`) into one
21
- * wrapper, since DaisyUI's mechanism needs no provider and no separate
22
- * trigger/content split — the trigger is just `children`.
23
- */
24
- export interface TooltipProps {
25
- /** Plain text only — see the module doc above. */
26
- label: string;
27
- position?: "top" | "bottom" | "left" | "right";
28
- className?: string;
29
- children: ReactNode;
30
- }
31
-
32
- export function Tooltip({ label, position = "top", className, children }: TooltipProps) {
33
- return (
34
- <div className={cn("tooltip", `tooltip-${position}`, className)} data-tip={label}>
35
- {children}
36
- </div>
37
- );
38
- }
@@ -1,70 +0,0 @@
1
- import type { HTMLAttributes } from "react";
2
- import { cn } from "../../lib/cn";
3
- import { HUE_CLASSES, type StatusHue } from "./status-tokens";
4
-
5
- /**
6
- * A compact, state-toned chip for a single word or short phrase rendered
7
- * *inline* — beside a table cell's value, after a label, inside a card.
8
- *
9
- * # Not `InlineBanner`, and not `Badge`
10
- *
11
- * `InlineBanner` is the full-width `px-3 py-2` notice that owns its own
12
- * line. Four route groups correctly refused to route these through it
13
- * during the R6 factorization: forcing a chip into a banner would mean
14
- * overriding padding and swapping `<span>` for `<div>`, which is fighting
15
- * the primitive rather than using it. That refusal was right, and this is
16
- * the component they were missing.
17
- *
18
- * `Badge` is the other near-miss: it exists, but it is built on daisyUI's
19
- * `badge-neutral`/`badge-outline` classes, a different visual system from
20
- * the `state-*` token family every status surface in this console uses.
21
- * A chip that must read as *success* or *danger* cannot express that
22
- * through `Badge` without bypassing its variants entirely.
23
- *
24
- * # Why it is shared
25
- *
26
- * Five sites across three route groups (`workers`, `routes`, `simulator`)
27
- * had byte-identical `rounded-sm border border-state-<tone>-border
28
- * bg-state-<tone>-bg px-1.5 py-0.5 text-caption text-state-<tone>-fg`
29
- * strings, differing only in tone. A sixth (`dashboard`) used the same
30
- * shape at `px-2`. They are unified here at `px-1.5`, the majority
31
- * spelling — a one-step spacing change on that single site, and the same
32
- * normalisation `DetailRow` made for the same reason: the console should
33
- * not render one concept two ways because two people typed a different
34
- * padding.
35
- *
36
- * `StatusPill` is **not** replaced by this. It maps a whole state
37
- * machine's variants onto a fixed vocabulary and carries a glyph; this is
38
- * the generic chip for everything that is state-toned but is not a state
39
- * machine — a capability flag, a derived verdict, a one-word qualifier.
40
- *
41
- * # Tone
42
- *
43
- * Tone comes from the shared [`StatusHue`] vocabulary rather than a second
44
- * private list. Before this, `StateChip` carried its own four-tone table
45
- * whose class strings were transcribed by hand from the same tokens
46
- * `HUE_CLASSES` already held — two copies of one mapping, and the chip's
47
- * copy was missing three of the hues, so a caller wanting `expired` or
48
- * `parked` had no way to ask for it and reached for `className` instead.
49
- */
50
- export type StateChipTone = StatusHue;
51
-
52
- export interface StateChipProps extends HTMLAttributes<HTMLSpanElement> {
53
- tone?: StateChipTone | undefined;
54
- }
55
-
56
- export function StateChip({ className, tone = "uncertain", ...props }: StateChipProps) {
57
- const hue = HUE_CLASSES[tone];
58
- return (
59
- <span
60
- className={cn(
61
- "rounded-sm border px-1.5 py-0.5 text-caption",
62
- hue.border,
63
- hue.bg,
64
- hue.fg,
65
- className,
66
- )}
67
- {...props}
68
- />
69
- );
70
- }