@setsquare/ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (312) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +49 -0
  4. package/README.md +313 -0
  5. package/dist/index.d.ts +129 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +78 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/lib/cn.d.ts +4 -0
  10. package/dist/lib/cn.d.ts.map +1 -0
  11. package/dist/lib/cn.js +59 -0
  12. package/dist/lib/cn.js.map +1 -0
  13. package/dist/lib/tokens.d.ts +5 -0
  14. package/dist/lib/tokens.d.ts.map +1 -0
  15. package/dist/lib/tokens.js +22 -0
  16. package/dist/lib/tokens.js.map +1 -0
  17. package/dist/lib/use-element-width.d.ts +4 -0
  18. package/dist/lib/use-element-width.d.ts.map +1 -0
  19. package/dist/lib/use-element-width.js +17 -0
  20. package/dist/lib/use-element-width.js.map +1 -0
  21. package/dist/lint/index.d.ts +41 -0
  22. package/dist/lint/index.d.ts.map +1 -0
  23. package/dist/lint/index.js +648 -0
  24. package/dist/lint/index.js.map +1 -0
  25. package/dist/shell/app-header.d.ts +13 -0
  26. package/dist/shell/app-header.d.ts.map +1 -0
  27. package/dist/shell/app-header.js +11 -0
  28. package/dist/shell/app-header.js.map +1 -0
  29. package/dist/shell/dashboard-shell.d.ts +67 -0
  30. package/dist/shell/dashboard-shell.d.ts.map +1 -0
  31. package/dist/shell/dashboard-shell.js +168 -0
  32. package/dist/shell/dashboard-shell.js.map +1 -0
  33. package/dist/shell/page-header.d.ts +19 -0
  34. package/dist/shell/page-header.d.ts.map +1 -0
  35. package/dist/shell/page-header.js +11 -0
  36. package/dist/shell/page-header.js.map +1 -0
  37. package/dist/shell/right-panel.d.ts +25 -0
  38. package/dist/shell/right-panel.d.ts.map +1 -0
  39. package/dist/shell/right-panel.js +37 -0
  40. package/dist/shell/right-panel.js.map +1 -0
  41. package/dist/shell/sidebar.d.ts +68 -0
  42. package/dist/shell/sidebar.d.ts.map +1 -0
  43. package/dist/shell/sidebar.js +68 -0
  44. package/dist/shell/sidebar.js.map +1 -0
  45. package/dist/shell/site-footer.d.ts +28 -0
  46. package/dist/shell/site-footer.d.ts.map +1 -0
  47. package/dist/shell/site-footer.js +15 -0
  48. package/dist/shell/site-footer.js.map +1 -0
  49. package/dist/shell/site-header-padding.d.ts +7 -0
  50. package/dist/shell/site-header-padding.d.ts.map +1 -0
  51. package/dist/shell/site-header-padding.js +27 -0
  52. package/dist/shell/site-header-padding.js.map +1 -0
  53. package/dist/shell/site-header.d.ts +23 -0
  54. package/dist/shell/site-header.d.ts.map +1 -0
  55. package/dist/shell/site-header.js +13 -0
  56. package/dist/shell/site-header.js.map +1 -0
  57. package/dist/shell/site-menu.d.ts +27 -0
  58. package/dist/shell/site-menu.d.ts.map +1 -0
  59. package/dist/shell/site-menu.js +56 -0
  60. package/dist/shell/site-menu.js.map +1 -0
  61. package/dist/shell/site-nav-link.d.ts +24 -0
  62. package/dist/shell/site-nav-link.d.ts.map +1 -0
  63. package/dist/shell/site-nav-link.js +35 -0
  64. package/dist/shell/site-nav-link.js.map +1 -0
  65. package/dist/shell/site-nav.d.ts +12 -0
  66. package/dist/shell/site-nav.d.ts.map +1 -0
  67. package/dist/shell/site-nav.js +9 -0
  68. package/dist/shell/site-nav.js.map +1 -0
  69. package/dist/shell/site-path.d.ts +8 -0
  70. package/dist/shell/site-path.d.ts.map +1 -0
  71. package/dist/shell/site-path.js +11 -0
  72. package/dist/shell/site-path.js.map +1 -0
  73. package/dist/shell/site-shell.d.ts +25 -0
  74. package/dist/shell/site-shell.d.ts.map +1 -0
  75. package/dist/shell/site-shell.js +17 -0
  76. package/dist/shell/site-shell.js.map +1 -0
  77. package/dist/styles/styles.css +10 -0
  78. package/dist/styles/theme.css +579 -0
  79. package/dist/styles/tokens.css +295 -0
  80. package/dist/testing/index.d.ts +321 -0
  81. package/dist/testing/index.d.ts.map +1 -0
  82. package/dist/testing/index.js +1175 -0
  83. package/dist/testing/index.js.map +1 -0
  84. package/dist/tokens.json +982 -0
  85. package/dist/ui/alert.d.ts +35 -0
  86. package/dist/ui/alert.d.ts.map +1 -0
  87. package/dist/ui/alert.js +65 -0
  88. package/dist/ui/alert.js.map +1 -0
  89. package/dist/ui/avatar.d.ts +16 -0
  90. package/dist/ui/avatar.d.ts.map +1 -0
  91. package/dist/ui/avatar.js +13 -0
  92. package/dist/ui/avatar.js.map +1 -0
  93. package/dist/ui/badge.d.ts +13 -0
  94. package/dist/ui/badge.d.ts.map +1 -0
  95. package/dist/ui/badge.js +24 -0
  96. package/dist/ui/badge.js.map +1 -0
  97. package/dist/ui/breadcrumbs.d.ts +13 -0
  98. package/dist/ui/breadcrumbs.d.ts.map +1 -0
  99. package/dist/ui/breadcrumbs.js +12 -0
  100. package/dist/ui/breadcrumbs.js.map +1 -0
  101. package/dist/ui/button-styles.d.ts +24 -0
  102. package/dist/ui/button-styles.d.ts.map +1 -0
  103. package/dist/ui/button-styles.js +50 -0
  104. package/dist/ui/button-styles.js.map +1 -0
  105. package/dist/ui/button.d.ts +27 -0
  106. package/dist/ui/button.d.ts.map +1 -0
  107. package/dist/ui/button.js +30 -0
  108. package/dist/ui/button.js.map +1 -0
  109. package/dist/ui/calendar.d.ts +19 -0
  110. package/dist/ui/calendar.d.ts.map +1 -0
  111. package/dist/ui/calendar.js +52 -0
  112. package/dist/ui/calendar.js.map +1 -0
  113. package/dist/ui/card.d.ts +30 -0
  114. package/dist/ui/card.d.ts.map +1 -0
  115. package/dist/ui/card.js +18 -0
  116. package/dist/ui/card.js.map +1 -0
  117. package/dist/ui/checkbox.d.ts +36 -0
  118. package/dist/ui/checkbox.d.ts.map +1 -0
  119. package/dist/ui/checkbox.js +38 -0
  120. package/dist/ui/checkbox.js.map +1 -0
  121. package/dist/ui/code-block.d.ts +20 -0
  122. package/dist/ui/code-block.d.ts.map +1 -0
  123. package/dist/ui/code-block.js +22 -0
  124. package/dist/ui/code-block.js.map +1 -0
  125. package/dist/ui/combo-box.d.ts +17 -0
  126. package/dist/ui/combo-box.d.ts.map +1 -0
  127. package/dist/ui/combo-box.js +14 -0
  128. package/dist/ui/combo-box.js.map +1 -0
  129. package/dist/ui/copy-button.d.ts +14 -0
  130. package/dist/ui/copy-button.d.ts.map +1 -0
  131. package/dist/ui/copy-button.js +36 -0
  132. package/dist/ui/copy-button.js.map +1 -0
  133. package/dist/ui/date-field.d.ts +38 -0
  134. package/dist/ui/date-field.d.ts.map +1 -0
  135. package/dist/ui/date-field.js +78 -0
  136. package/dist/ui/date-field.js.map +1 -0
  137. package/dist/ui/dialog.d.ts +41 -0
  138. package/dist/ui/dialog.d.ts.map +1 -0
  139. package/dist/ui/dialog.js +69 -0
  140. package/dist/ui/dialog.js.map +1 -0
  141. package/dist/ui/disclosure.d.ts +18 -0
  142. package/dist/ui/disclosure.d.ts.map +1 -0
  143. package/dist/ui/disclosure.js +15 -0
  144. package/dist/ui/disclosure.js.map +1 -0
  145. package/dist/ui/empty-state.d.ts +17 -0
  146. package/dist/ui/empty-state.d.ts.map +1 -0
  147. package/dist/ui/empty-state.js +9 -0
  148. package/dist/ui/empty-state.js.map +1 -0
  149. package/dist/ui/error-state.d.ts +45 -0
  150. package/dist/ui/error-state.d.ts.map +1 -0
  151. package/dist/ui/error-state.js +88 -0
  152. package/dist/ui/error-state.js.map +1 -0
  153. package/dist/ui/field.d.ts +46 -0
  154. package/dist/ui/field.d.ts.map +1 -0
  155. package/dist/ui/field.js +79 -0
  156. package/dist/ui/field.js.map +1 -0
  157. package/dist/ui/file-upload.d.ts +54 -0
  158. package/dist/ui/file-upload.d.ts.map +1 -0
  159. package/dist/ui/file-upload.js +170 -0
  160. package/dist/ui/file-upload.js.map +1 -0
  161. package/dist/ui/form.d.ts +56 -0
  162. package/dist/ui/form.d.ts.map +1 -0
  163. package/dist/ui/form.js +137 -0
  164. package/dist/ui/form.js.map +1 -0
  165. package/dist/ui/grid-list.d.ts +24 -0
  166. package/dist/ui/grid-list.d.ts.map +1 -0
  167. package/dist/ui/grid-list.js +21 -0
  168. package/dist/ui/grid-list.js.map +1 -0
  169. package/dist/ui/hero.d.ts +36 -0
  170. package/dist/ui/hero.d.ts.map +1 -0
  171. package/dist/ui/hero.js +19 -0
  172. package/dist/ui/hero.js.map +1 -0
  173. package/dist/ui/icon-button.d.ts +18 -0
  174. package/dist/ui/icon-button.d.ts.map +1 -0
  175. package/dist/ui/icon-button.js +13 -0
  176. package/dist/ui/icon-button.js.map +1 -0
  177. package/dist/ui/icon.d.ts +20 -0
  178. package/dist/ui/icon.d.ts.map +1 -0
  179. package/dist/ui/icon.js +26 -0
  180. package/dist/ui/icon.js.map +1 -0
  181. package/dist/ui/kbd.d.ts +7 -0
  182. package/dist/ui/kbd.d.ts.map +1 -0
  183. package/dist/ui/kbd.js +7 -0
  184. package/dist/ui/kbd.js.map +1 -0
  185. package/dist/ui/link-card.d.ts +23 -0
  186. package/dist/ui/link-card.d.ts.map +1 -0
  187. package/dist/ui/link-card.js +19 -0
  188. package/dist/ui/link-card.js.map +1 -0
  189. package/dist/ui/list-box.d.ts +17 -0
  190. package/dist/ui/list-box.d.ts.map +1 -0
  191. package/dist/ui/list-box.js +18 -0
  192. package/dist/ui/list-box.js.map +1 -0
  193. package/dist/ui/menu.d.ts +31 -0
  194. package/dist/ui/menu.d.ts.map +1 -0
  195. package/dist/ui/menu.js +40 -0
  196. package/dist/ui/menu.js.map +1 -0
  197. package/dist/ui/number-field.d.ts +19 -0
  198. package/dist/ui/number-field.d.ts.map +1 -0
  199. package/dist/ui/number-field.js +19 -0
  200. package/dist/ui/number-field.js.map +1 -0
  201. package/dist/ui/prose.d.ts +12 -0
  202. package/dist/ui/prose.d.ts.map +1 -0
  203. package/dist/ui/prose.js +11 -0
  204. package/dist/ui/prose.js.map +1 -0
  205. package/dist/ui/radio-group.d.ts +20 -0
  206. package/dist/ui/radio-group.d.ts.map +1 -0
  207. package/dist/ui/radio-group.js +16 -0
  208. package/dist/ui/radio-group.js.map +1 -0
  209. package/dist/ui/scroll-region.d.ts +18 -0
  210. package/dist/ui/scroll-region.d.ts.map +1 -0
  211. package/dist/ui/scroll-region.js +29 -0
  212. package/dist/ui/scroll-region.js.map +1 -0
  213. package/dist/ui/search-field.d.ts +12 -0
  214. package/dist/ui/search-field.d.ts.map +1 -0
  215. package/dist/ui/search-field.js +12 -0
  216. package/dist/ui/search-field.js.map +1 -0
  217. package/dist/ui/search.d.ts +43 -0
  218. package/dist/ui/search.d.ts.map +1 -0
  219. package/dist/ui/search.js +45 -0
  220. package/dist/ui/search.js.map +1 -0
  221. package/dist/ui/section.d.ts +50 -0
  222. package/dist/ui/section.d.ts.map +1 -0
  223. package/dist/ui/section.js +25 -0
  224. package/dist/ui/section.js.map +1 -0
  225. package/dist/ui/segmented-control.d.ts +18 -0
  226. package/dist/ui/segmented-control.d.ts.map +1 -0
  227. package/dist/ui/segmented-control.js +17 -0
  228. package/dist/ui/segmented-control.js.map +1 -0
  229. package/dist/ui/select.d.ts +18 -0
  230. package/dist/ui/select.d.ts.map +1 -0
  231. package/dist/ui/select.js +13 -0
  232. package/dist/ui/select.js.map +1 -0
  233. package/dist/ui/selection-bar.d.ts +28 -0
  234. package/dist/ui/selection-bar.d.ts.map +1 -0
  235. package/dist/ui/selection-bar.js +35 -0
  236. package/dist/ui/selection-bar.js.map +1 -0
  237. package/dist/ui/sheet.d.ts +21 -0
  238. package/dist/ui/sheet.d.ts.map +1 -0
  239. package/dist/ui/sheet.js +16 -0
  240. package/dist/ui/sheet.js.map +1 -0
  241. package/dist/ui/skeleton.d.ts +31 -0
  242. package/dist/ui/skeleton.d.ts.map +1 -0
  243. package/dist/ui/skeleton.js +38 -0
  244. package/dist/ui/skeleton.js.map +1 -0
  245. package/dist/ui/slider.d.ts +31 -0
  246. package/dist/ui/slider.d.ts.map +1 -0
  247. package/dist/ui/slider.js +33 -0
  248. package/dist/ui/slider.js.map +1 -0
  249. package/dist/ui/spinner.d.ts +27 -0
  250. package/dist/ui/spinner.d.ts.map +1 -0
  251. package/dist/ui/spinner.js +21 -0
  252. package/dist/ui/spinner.js.map +1 -0
  253. package/dist/ui/split.d.ts +14 -0
  254. package/dist/ui/split.d.ts.map +1 -0
  255. package/dist/ui/split.js +11 -0
  256. package/dist/ui/split.js.map +1 -0
  257. package/dist/ui/stat-list.d.ts +25 -0
  258. package/dist/ui/stat-list.d.ts.map +1 -0
  259. package/dist/ui/stat-list.js +14 -0
  260. package/dist/ui/stat-list.js.map +1 -0
  261. package/dist/ui/steps.d.ts +23 -0
  262. package/dist/ui/steps.d.ts.map +1 -0
  263. package/dist/ui/steps.js +16 -0
  264. package/dist/ui/steps.js.map +1 -0
  265. package/dist/ui/switch.d.ts +13 -0
  266. package/dist/ui/switch.d.ts.map +1 -0
  267. package/dist/ui/switch.js +12 -0
  268. package/dist/ui/switch.js.map +1 -0
  269. package/dist/ui/table.d.ts +25 -0
  270. package/dist/ui/table.d.ts.map +1 -0
  271. package/dist/ui/table.js +29 -0
  272. package/dist/ui/table.js.map +1 -0
  273. package/dist/ui/tag.d.ts +14 -0
  274. package/dist/ui/tag.d.ts.map +1 -0
  275. package/dist/ui/tag.js +11 -0
  276. package/dist/ui/tag.js.map +1 -0
  277. package/dist/ui/text-field.d.ts +27 -0
  278. package/dist/ui/text-field.d.ts.map +1 -0
  279. package/dist/ui/text-field.js +17 -0
  280. package/dist/ui/text-field.js.map +1 -0
  281. package/dist/ui/text-link.d.ts +17 -0
  282. package/dist/ui/text-link.d.ts.map +1 -0
  283. package/dist/ui/text-link.js +15 -0
  284. package/dist/ui/text-link.js.map +1 -0
  285. package/dist/ui/theme-switcher.d.ts +26 -0
  286. package/dist/ui/theme-switcher.d.ts.map +1 -0
  287. package/dist/ui/theme-switcher.js +32 -0
  288. package/dist/ui/theme-switcher.js.map +1 -0
  289. package/dist/ui/theme.d.ts +54 -0
  290. package/dist/ui/theme.d.ts.map +1 -0
  291. package/dist/ui/theme.js +135 -0
  292. package/dist/ui/theme.js.map +1 -0
  293. package/dist/ui/toast.d.ts +34 -0
  294. package/dist/ui/toast.d.ts.map +1 -0
  295. package/dist/ui/toast.js +69 -0
  296. package/dist/ui/toast.js.map +1 -0
  297. package/dist/ui/tooltip.d.ts +10 -0
  298. package/dist/ui/tooltip.d.ts.map +1 -0
  299. package/dist/ui/tooltip.js +10 -0
  300. package/dist/ui/tooltip.js.map +1 -0
  301. package/package.json +123 -0
  302. package/templates/next/error.tsx +32 -0
  303. package/templates/next/loading.tsx +19 -0
  304. package/templates/next/not-found.tsx +18 -0
  305. package/templates/next/page-transition.tsx +33 -0
  306. package/templates/next/portal-layout.tsx +70 -0
  307. package/templates/next/prefetch-on-intent.ts +31 -0
  308. package/templates/next/root-layout.tsx +16 -0
  309. package/templates/next/site-layout.tsx +58 -0
  310. package/templates/next/site-nav.ts +16 -0
  311. package/templates/next/site-not-found.tsx +21 -0
  312. package/templates/next/site-router.tsx +17 -0
@@ -0,0 +1,35 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type IconProp } from './icon.js';
3
+ export type AlertTone = 'info' | 'success' | 'warning' | 'danger';
4
+ export interface AlertProps {
5
+ tone?: AlertTone;
6
+ title?: ReactNode;
7
+ children?: ReactNode;
8
+ /** Buttons or links: what to do about it. */
9
+ actions?: ReactNode;
10
+ /** Show a Dismiss button. Focus moves to the next control, so it is never lost. */
11
+ onDismiss?: () => void;
12
+ /**
13
+ * Only for alerts that appear after the page has loaded: 'polite' (role=status) or 'assertive' (role=alert,
14
+ * for errors that stop the task). Alerts present on load are plain content.
15
+ */
16
+ live?: 'polite' | 'assertive';
17
+ /** 'inline' sits in the content; 'banner' spans the top of the inset, edge to edge. */
18
+ layout?: 'inline' | 'banner';
19
+ icon?: IconProp;
20
+ titleAs?: 'p' | 'h2' | 'h3';
21
+ className?: string;
22
+ }
23
+ /** A message about the page or task: tone ground, icon and words — never colour alone. Gains a tone border at AAA. */
24
+ export declare function Alert({ tone, title, children, actions, onDismiss, live, layout, icon, titleAs: Title, className, }: AlertProps): import("react").JSX.Element;
25
+ /** true while the browser reports a connection. */
26
+ export declare function useOnline(): boolean;
27
+ /**
28
+ * A banner that appears when the connection drops and says so again when it returns.
29
+ * The live region is always in the page, so both changes are announced.
30
+ */
31
+ export declare function OfflineAlert({ forceOffline, className, }: {
32
+ forceOffline?: boolean;
33
+ className?: string;
34
+ }): import("react").JSX.Element;
35
+ //# sourceMappingURL=alert.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alert.d.ts","sourceRoot":"","sources":["../../src/ui/alert.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAqD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAK1F,OAAO,EAAkC,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE1E,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAiBlE,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,6CAA6C;IAC7C,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,mFAAmF;IACnF,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB;;;OAGG;IACH,IAAI,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAC;IAC9B,uFAAuF;IACvF,MAAM,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC7B,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,OAAO,CAAC,EAAE,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,sHAAsH;AACtH,wBAAgB,KAAK,CAAC,EACpB,IAAa,EACb,KAAK,EACL,QAAQ,EACR,OAAO,EACP,SAAS,EACT,IAAI,EACJ,MAAiB,EACjB,IAAI,EACJ,OAAO,EAAE,KAAW,EACpB,SAAS,GACV,EAAE,UAAU,+BAsCZ;AAUD,mDAAmD;AACnD,wBAAgB,SAAS,IAAI,OAAO,CAMnC;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,EAC3B,YAAY,EACZ,SAAS,GACV,EAAE;IACD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,+BAwBA"}
@@ -0,0 +1,65 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useRef, useState, useSyncExternalStore } from 'react';
4
+ import { CircleAlert, CircleCheck, Info, TriangleAlert, WifiOff, X } from 'lucide-react';
5
+ import { cn } from '../lib/cn.js';
6
+ import { focusAfter } from '../lib/tokens.js';
7
+ import { IconButton } from './icon-button.js';
8
+ import { renderIcon } from './icon.js';
9
+ const tones = {
10
+ info: { box: 'bg-brand-subtle aaa:border-brand', icon: 'text-brand', Icon: Info },
11
+ success: { box: 'bg-success-subtle aaa:border-success', icon: 'text-success', Icon: CircleCheck },
12
+ warning: {
13
+ box: 'bg-warning-subtle aaa:border-warning',
14
+ icon: 'text-warning',
15
+ Icon: TriangleAlert,
16
+ },
17
+ danger: {
18
+ box: 'bg-destructive-subtle aaa:border-destructive',
19
+ icon: 'text-destructive',
20
+ Icon: CircleAlert,
21
+ },
22
+ };
23
+ /** A message about the page or task: tone ground, icon and words — never colour alone. Gains a tone border at AAA. */
24
+ export function Alert({ tone = 'info', title, children, actions, onDismiss, live, layout = 'inline', icon, titleAs: Title = 'p', className, }) {
25
+ const ref = useRef(null);
26
+ const t = tones[tone];
27
+ const shown = icon ?? t.Icon;
28
+ return (_jsxs("div", { ref: ref, role: live === 'assertive' ? 'alert' : live === 'polite' ? 'status' : undefined, className: cn('flex items-start gap-3 border border-transparent text-sm leading-5 text-foreground', layout === 'banner' ? 'border-x-0 border-t-0 px-4 py-3' : 'rounded-lg p-4', t.box, className), children: [renderIcon(shown, cn('mt-px size-5 shrink-0', t.icon)), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-1", children: [title && _jsx(Title, { className: "font-semibold", children: title }), children && _jsx("div", { className: "max-w-(--measure)", children: children }), actions && (_jsx("div", { className: "mt-2 flex flex-wrap items-center gap-(--control-gap)", children: actions }))] }), onDismiss && (_jsx(IconButton, { label: "Dismiss", icon: X, visualSize: "icon-sm", tooltip: false, className: "-mt-1 -mr-1 items-start justify-end", onPress: () => {
29
+ focusAfter(ref.current);
30
+ onDismiss();
31
+ } }))] }));
32
+ }
33
+ function subscribeOnline(cb) {
34
+ window.addEventListener('online', cb);
35
+ window.addEventListener('offline', cb);
36
+ return () => {
37
+ window.removeEventListener('online', cb);
38
+ window.removeEventListener('offline', cb);
39
+ };
40
+ }
41
+ /** true while the browser reports a connection. */
42
+ export function useOnline() {
43
+ return useSyncExternalStore(subscribeOnline, () => navigator.onLine, () => true);
44
+ }
45
+ /**
46
+ * A banner that appears when the connection drops and says so again when it returns.
47
+ * The live region is always in the page, so both changes are announced.
48
+ */
49
+ export function OfflineAlert({ forceOffline, className, }) {
50
+ const online = useOnline() && !forceOffline;
51
+ const [back, setBack] = useState(false);
52
+ const wasOffline = useRef(!online);
53
+ useEffect(() => {
54
+ if (!online)
55
+ wasOffline.current = true;
56
+ else if (wasOffline.current) {
57
+ wasOffline.current = false;
58
+ setBack(true);
59
+ const t = setTimeout(() => setBack(false), 4000);
60
+ return () => clearTimeout(t);
61
+ }
62
+ }, [online]);
63
+ return (_jsxs("div", { role: "status", className: className, children: [!online && (_jsx(Alert, { tone: "warning", layout: "banner", icon: WifiOff, title: "You're offline", children: "Changes you make now won't be saved. Keep this page open and we'll try again when you're back online." })), online && back && _jsx(Alert, { tone: "success", layout: "banner", title: "You're back online" })] }));
64
+ }
65
+ //# sourceMappingURL=alert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alert.js","sourceRoot":"","sources":["../../src/ui/alert.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ export interface AvatarProps {
2
+ initials: string;
3
+ /** Give it a name only when it stands alone; beside a visible name it stays decorative. */
4
+ label?: string;
5
+ tone?: 'brand' | 'neutral';
6
+ size?: 'sm' | 'md' | 'lg';
7
+ className?: string;
8
+ }
9
+ /** Initials in a disc. */
10
+ export declare function Avatar({ initials, label, tone, size, className }: AvatarProps): import("react").JSX.Element;
11
+ /** The product's monogram tile, used until a product has a logo. */
12
+ export declare function ProductMark({ children, className }: {
13
+ children: string;
14
+ className?: string;
15
+ }): import("react").JSX.Element;
16
+ //# sourceMappingURL=avatar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"avatar.d.ts","sourceRoot":"","sources":["../../src/ui/avatar.tsx"],"names":[],"mappings":"AAEA,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,2FAA2F;IAC3F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC3B,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAKD,0BAA0B;AAC1B,wBAAgB,MAAM,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAgB,EAAE,IAAW,EAAE,SAAS,EAAE,EAAE,WAAW,+BAgBhG;AAED,oEAAoE;AACpE,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,+BAY5F"}
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from '../lib/cn.js';
3
+ const sizes = { sm: 'size-6 text-[11px]', md: 'size-8 text-[13px]', lg: 'size-10 text-sm' };
4
+ const tones = { brand: 'bg-brand-subtle text-brand', neutral: 'bg-muted text-foreground' };
5
+ /** Initials in a disc. */
6
+ export function Avatar({ initials, label, tone = 'neutral', size = 'md', className }) {
7
+ return (_jsx("span", { role: label ? 'img' : undefined, "aria-label": label, "aria-hidden": label ? undefined : true, className: cn('inline-grid shrink-0 place-items-center rounded-full leading-none font-bold', sizes[size], tones[tone], className), children: initials }));
8
+ }
9
+ /** The product's monogram tile, used until a product has a logo. */
10
+ export function ProductMark({ children, className }) {
11
+ return (_jsx("span", { "aria-hidden": "true", className: cn('inline-grid size-7 shrink-0 place-items-center rounded-md bg-sidebar-primary text-sm font-bold text-sidebar-primary-foreground', className), children: children }));
12
+ }
13
+ //# sourceMappingURL=avatar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"avatar.js","sourceRoot":"","sources":["../../src/ui/avatar.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAWlC,MAAM,KAAK,GAAG,EAAE,EAAE,EAAE,oBAAoB,EAAE,EAAE,EAAE,oBAAoB,EAAE,EAAE,EAAE,iBAAiB,EAAE,CAAC;AAC5F,MAAM,KAAK,GAAG,EAAE,KAAK,EAAE,4BAA4B,EAAE,OAAO,EAAE,0BAA0B,EAAE,CAAC;AAE3F,0BAA0B;AAC1B,MAAM,UAAU,MAAM,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,GAAG,SAAS,EAAE,IAAI,GAAG,IAAI,EAAE,SAAS,EAAe;IAC/F,OAAO,CACL,eACE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,gBACnB,KAAK,iBACJ,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,EACrC,SAAS,EAAE,EAAE,CACX,6EAA6E,EAC7E,KAAK,CAAC,IAAI,CAAC,EACX,KAAK,CAAC,IAAI,CAAC,EACX,SAAS,CACV,YAEA,QAAQ,GACJ,CACR,CAAC;AACJ,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,WAAW,CAAC,EAAE,QAAQ,EAAE,SAAS,EAA4C;IAC3F,OAAO,CACL,8BACc,MAAM,EAClB,SAAS,EAAE,EAAE,CACX,gIAAgI,EAChI,SAAS,CACV,YAEA,QAAQ,GACJ,CACR,CAAC;AACJ,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { ReactNode } from 'react';
2
+ import { type IconProp } from './icon.js';
3
+ export type StatusTone = 'success' | 'warning' | 'danger' | 'info' | 'neutral';
4
+ export interface StatusBadgeProps {
5
+ tone?: StatusTone;
6
+ /** Replace the tone's default icon. */
7
+ icon?: IconProp;
8
+ children: ReactNode;
9
+ className?: string;
10
+ }
11
+ /** A status: always an icon and a word, never colour alone. Gains a border in AAA themes. */
12
+ export declare function StatusBadge({ tone, icon, children, className }: StatusBadgeProps): import("react").JSX.Element;
13
+ //# sourceMappingURL=badge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"badge.d.ts","sourceRoot":"","sources":["../../src/ui/badge.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,OAAO,EAAkC,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE1E,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;AAkB/E,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,uCAAuC;IACvC,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,6FAA6F;AAC7F,wBAAgB,WAAW,CAAC,EAAE,IAAgB,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,gBAAgB,+BAe5F"}
@@ -0,0 +1,24 @@
1
+ import { jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Circle, CircleCheck, CircleX, Info, TriangleAlert } from 'lucide-react';
3
+ import { cn } from '../lib/cn.js';
4
+ import { renderIcon } from './icon.js';
5
+ const tones = {
6
+ success: 'bg-success-subtle text-success',
7
+ warning: 'bg-warning-subtle text-warning',
8
+ danger: 'bg-destructive-subtle text-destructive',
9
+ info: 'bg-brand-subtle text-brand',
10
+ neutral: 'bg-muted text-muted-foreground',
11
+ };
12
+ const toneIcons = {
13
+ success: CircleCheck,
14
+ warning: TriangleAlert,
15
+ danger: CircleX,
16
+ info: Info,
17
+ neutral: Circle,
18
+ };
19
+ /** A status: always an icon and a word, never colour alone. Gains a border in AAA themes. */
20
+ export function StatusBadge({ tone = 'neutral', icon, children, className }) {
21
+ const shown = icon ?? toneIcons[tone];
22
+ return (_jsxs("span", { className: cn('inline-flex h-6 items-center gap-1 rounded-full border border-transparent pr-2 pl-1.5 text-[13px] leading-5 font-semibold whitespace-nowrap aaa:border-current', '[&_svg]:size-3.5 [&_svg]:shrink-0 [&_svg]:[stroke-width:2]', tones[tone], className), children: [renderIcon(shown), children] }));
23
+ }
24
+ //# sourceMappingURL=badge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"badge.js","sourceRoot":"","sources":["../../src/ui/badge.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACjF,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,UAAU,EAAqC,MAAM,WAAW,CAAC;AAI1E,MAAM,KAAK,GAA+B;IACxC,OAAO,EAAE,gCAAgC;IACzC,OAAO,EAAE,gCAAgC;IACzC,MAAM,EAAE,wCAAwC;IAChD,IAAI,EAAE,4BAA4B;IAClC,OAAO,EAAE,gCAAgC;CAC1C,CAAC;AAEF,MAAM,SAAS,GAAsC;IACnD,OAAO,EAAE,WAAW;IACpB,OAAO,EAAE,aAAa;IACtB,MAAM,EAAE,OAAO;IACf,IAAI,EAAE,IAAI;IACV,OAAO,EAAE,MAAM;CAChB,CAAC;AAUF,6FAA6F;AAC7F,MAAM,UAAU,WAAW,CAAC,EAAE,IAAI,GAAG,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAoB;IAC3F,MAAM,KAAK,GAAG,IAAI,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC;IACtC,OAAO,CACL,gBACE,SAAS,EAAE,EAAE,CACX,gKAAgK,EAChK,4DAA4D,EAC5D,KAAK,CAAC,IAAI,CAAC,EACX,SAAS,CACV,aAEA,UAAU,CAAC,KAAK,CAAC,EACjB,QAAQ,IACJ,CACR,CAAC;AACJ,CAAC"}
@@ -0,0 +1,13 @@
1
+ export interface BreadcrumbItem {
2
+ label: string;
3
+ href?: string;
4
+ }
5
+ export interface BreadcrumbsProps {
6
+ /** Ancestors first; the last item is the current page and is not a link. */
7
+ items: BreadcrumbItem[];
8
+ label?: string;
9
+ className?: string;
10
+ }
11
+ /** Where you are (WCAG 2.4.8). Every page below the top level shows one. */
12
+ export declare function Breadcrumbs({ items, label, className }: BreadcrumbsProps): import("react").JSX.Element;
13
+ //# sourceMappingURL=breadcrumbs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"breadcrumbs.d.ts","sourceRoot":"","sources":["../../src/ui/breadcrumbs.tsx"],"names":[],"mappings":"AAMA,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,4EAA4E;IAC5E,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,KAAoB,EAAE,SAAS,EAAE,EAAE,gBAAgB,+BAiCvF"}
@@ -0,0 +1,12 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Breadcrumb, Breadcrumbs as RACBreadcrumbs, Link } from 'react-aria-components';
4
+ import { ChevronRight } from 'lucide-react';
5
+ import { cn } from '../lib/cn.js';
6
+ /** Where you are (WCAG 2.4.8). Every page below the top level shows one. */
7
+ export function Breadcrumbs({ items, label = 'Breadcrumb', className }) {
8
+ return (_jsx("nav", { "aria-label": label, className: cn('min-w-0', className), children: _jsx(RACBreadcrumbs, { className: "flex min-w-0 items-center gap-1 text-sm", children: items.map((item, i) => (_jsxs(Breadcrumb, { id: i, className: "flex min-w-0 items-center gap-1", children: [i > 0 && (_jsx(ChevronRight, { "aria-hidden": "true", className: "size-4 shrink-0 text-muted-foreground" })), _jsx(Link, { href: item.href, className: cn(
9
+ // The current page is not a link to press: React Aria disables it, and it keeps the default cursor.
10
+ 'group/crumb flex min-h-target min-w-target max-w-[20ch] cursor-pointer items-center justify-center leading-5 no-underline outline-none data-current:cursor-default'), children: _jsx("span", { className: cn('min-w-0 truncate rounded-sm text-muted-foreground underline decoration-1 underline-offset-3', 'group-data-hovered/crumb:text-foreground group-data-focus-visible/crumb:focus-ring', 'group-data-current/crumb:font-semibold group-data-current/crumb:text-foreground group-data-current/crumb:no-underline'), children: item.label }) })] }, `${i}-${item.label}`))) }) }));
11
+ }
12
+ //# sourceMappingURL=breadcrumbs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"breadcrumbs.js","sourceRoot":"","sources":["../../src/ui/breadcrumbs.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,UAAU,EAAE,WAAW,IAAI,cAAc,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AACxF,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAclC,4EAA4E;AAC5E,MAAM,UAAU,WAAW,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG,YAAY,EAAE,SAAS,EAAoB;IACtF,OAAO,CACL,4BAAiB,KAAK,EAAE,SAAS,EAAE,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC,YACzD,KAAC,cAAc,IAAC,SAAS,EAAC,yCAAyC,YAChE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CACtB,MAAC,UAAU,IAA4B,EAAE,EAAE,CAAC,EAAE,SAAS,EAAC,iCAAiC,aACtF,CAAC,GAAG,CAAC,IAAI,CACR,KAAC,YAAY,mBAAa,MAAM,EAAC,SAAS,EAAC,uCAAuC,GAAG,CACtF,EAED,KAAC,IAAI,IACH,IAAI,EAAE,IAAI,CAAC,IAAI,EACf,SAAS,EAAE,EAAE;wBACX,oGAAoG;wBACpG,oKAAoK,CACrK,YAGD,eACE,SAAS,EAAE,EAAE,CACX,6FAA6F,EAC7F,oFAAoF,EACpF,uHAAuH,CACxH,YAEA,IAAI,CAAC,KAAK,GACN,GACF,KAtBQ,GAAG,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAuBxB,CACd,CAAC,GACa,GACb,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,24 @@
1
+ export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost' | 'destructive';
2
+ /** The painted size (Layer 2). The target around it is set by the conformance level, not by this. */
3
+ export type ButtonVisualSize = 'default' | 'sm' | 'lg' | 'icon' | 'icon-sm';
4
+ /**
5
+ * Layer 1 classes, for the interactive element of anything that should behave like a button.
6
+ * Use it on a React Aria `Button`, `Link` or `ToggleButton`: the visual layer reads their data-hovered,
7
+ * data-pressed and data-focus-visible attributes.
8
+ */
9
+ export declare function buttonTargetStyles(opts?: {
10
+ fullWidth?: boolean;
11
+ className?: string;
12
+ }): string;
13
+ /**
14
+ * Layer 2 classes, for a span inside the target. On their own they are only the painted box: an element
15
+ * carrying just these is a pointer target the size of the paint, which fails 2.5.5 at AAA. Put
16
+ * `buttonTargetStyles()` on the interactive element and these on the span inside it, as `Button` does.
17
+ */
18
+ export declare function buttonVisualStyles(opts?: {
19
+ variant?: ButtonVariant;
20
+ visualSize?: ButtonVisualSize;
21
+ fullWidth?: boolean;
22
+ className?: string;
23
+ }): string;
24
+ //# sourceMappingURL=button-styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"button-styles.d.ts","sourceRoot":"","sources":["../../src/ui/button-styles.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,GAAG,aAAa,CAAC;AAC1F,qGAAqG;AACrG,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAAC;AA0C5E;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,MAAM,CAEjG;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,GAAE;IACJ,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACf,GACL,MAAM,CAGR"}
@@ -0,0 +1,50 @@
1
+ // No 'use client': these build class strings and nothing else, so server components can call them too — to
2
+ // style a framework's own link as a button, for instance.
3
+ import { cn } from '../lib/cn.js';
4
+ /*
5
+ * Three layers, the pattern Themis (@tribepad/themis) uses:
6
+ * 1. Target — the React Aria <button> or <a>. Transparent, at least --target-min in both axes, so a
7
+ * 28px icon button is still a 44px target at AAA. Takes `className`.
8
+ * 2. Visual — a span inside it: the colours, border, radius and focus ring people see, sized by
9
+ * `visualSize`. Takes `visualClassName`.
10
+ * 3. Content — the label, icons and spinner.
11
+ * The target is in the layout, not an overlay, so two neighbouring targets can never overlap.
12
+ */
13
+ const target = 'group/button relative inline-flex shrink-0 cursor-pointer items-center justify-center min-h-target min-w-target no-underline outline-none select-none ' +
14
+ 'data-disabled:cursor-not-allowed data-disabled:opacity-50 data-pending:cursor-wait';
15
+ const visual = 'relative inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md border text-sm font-semibold no-underline ' +
16
+ 'transition-colors duration-(--duration-fast) ease-(--ease-standard) group-data-focus-visible/button:focus-ring ' +
17
+ '[&_svg]:size-5 [&_svg]:shrink-0 group-data-pending/button:[&>svg:not([data-spinner])]:hidden';
18
+ const variants = {
19
+ primary: 'border-transparent bg-primary text-primary-foreground group-data-hovered/button:bg-primary-hover group-data-pressed/button:bg-primary-hover',
20
+ secondary: 'border-transparent bg-secondary text-secondary-foreground group-data-hovered/button:bg-accent group-data-pressed/button:bg-accent',
21
+ outline: 'border-input bg-background text-foreground group-data-hovered/button:bg-accent group-data-pressed/button:bg-accent',
22
+ ghost: 'border-transparent bg-transparent text-foreground group-data-hovered/button:bg-accent group-data-pressed/button:bg-accent',
23
+ destructive: 'border-transparent bg-destructive text-destructive-foreground group-data-hovered/button:bg-destructive-hover group-data-pressed/button:bg-destructive-hover',
24
+ };
25
+ const sizes = {
26
+ default: 'min-h-control px-(--control-pad-x)',
27
+ sm: 'min-h-control-sm px-3',
28
+ // A page's call to action on a public page: --control-height-lg (44px at both levels), 16px text.
29
+ lg: 'min-h-control-lg px-5 text-base',
30
+ icon: 'size-control',
31
+ 'icon-sm': 'size-control-sm',
32
+ };
33
+ /**
34
+ * Layer 1 classes, for the interactive element of anything that should behave like a button.
35
+ * Use it on a React Aria `Button`, `Link` or `ToggleButton`: the visual layer reads their data-hovered,
36
+ * data-pressed and data-focus-visible attributes.
37
+ */
38
+ export function buttonTargetStyles(opts = {}) {
39
+ return cn(target, opts.fullWidth && 'w-full', opts.className);
40
+ }
41
+ /**
42
+ * Layer 2 classes, for a span inside the target. On their own they are only the painted box: an element
43
+ * carrying just these is a pointer target the size of the paint, which fails 2.5.5 at AAA. Put
44
+ * `buttonTargetStyles()` on the interactive element and these on the span inside it, as `Button` does.
45
+ */
46
+ export function buttonVisualStyles(opts = {}) {
47
+ const { variant = 'outline', visualSize = 'default', fullWidth, className } = opts;
48
+ return cn(visual, variants[variant], sizes[visualSize], fullWidth && 'w-full', className);
49
+ }
50
+ //# sourceMappingURL=button-styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"button-styles.js","sourceRoot":"","sources":["../../src/ui/button-styles.ts"],"names":[],"mappings":"AAAA,2GAA2G;AAC3G,0DAA0D;AAC1D,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAMlC;;;;;;;;GAQG;AACH,MAAM,MAAM,GACV,wJAAwJ;IACxJ,oFAAoF,CAAC;AAEvF,MAAM,MAAM,GACV,gIAAgI;IAChI,iHAAiH;IACjH,8FAA8F,CAAC;AAEjG,MAAM,QAAQ,GAAkC;IAC9C,OAAO,EACL,6IAA6I;IAC/I,SAAS,EACP,mIAAmI;IACrI,OAAO,EACL,oHAAoH;IACtH,KAAK,EACH,2HAA2H;IAC7H,WAAW,EACT,6JAA6J;CAChK,CAAC;AAEF,MAAM,KAAK,GAAqC;IAC9C,OAAO,EAAE,oCAAoC;IAC7C,EAAE,EAAE,uBAAuB;IAC3B,kGAAkG;IAClG,EAAE,EAAE,iCAAiC;IACrC,IAAI,EAAE,cAAc;IACpB,SAAS,EAAE,iBAAiB;CAC7B,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAI,GAAgD,EAAE;IACvF,OAAO,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,IAAI,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;AAChE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAChC,IAAI,GAKA,EAAE;IAEN,MAAM,EAAE,OAAO,GAAG,SAAS,EAAE,UAAU,GAAG,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IACnF,OAAO,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,UAAU,CAAC,EAAE,SAAS,IAAI,QAAQ,EAAE,SAAS,CAAC,CAAC;AAC5F,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { composeRenderProps, type ButtonProps as RACButtonProps, type LinkProps as RACLinkProps } from 'react-aria-components';
2
+ import { type ButtonVariant, type ButtonVisualSize } from './button-styles.js';
3
+ export type { ButtonVariant, ButtonVisualSize };
4
+ interface LayerProps {
5
+ variant?: ButtonVariant;
6
+ /** Painted size (Layer 2). The target is always at least --target-min: 24px at AA, 44px at AAA. */
7
+ visualSize?: ButtonVisualSize;
8
+ /** Stretch both layers to the container's width. */
9
+ fullWidth?: boolean;
10
+ /** Layer 1, the target: layout and alignment. A min-h-* or min-w-* here would shrink the target. */
11
+ className?: string;
12
+ /** Layer 2, the painted box. */
13
+ visualClassName?: string;
14
+ }
15
+ export interface ButtonProps extends Omit<RACButtonProps, 'className'>, LayerProps {
16
+ }
17
+ /**
18
+ * React Aria Button. One `primary` per view; `outline` is the default.
19
+ * `isPending` keeps it focusable, announces the wait and swaps any leading icon for a spinner.
20
+ */
21
+ export declare const Button: import("react").ForwardRefExoticComponent<ButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
22
+ export interface LinkButtonProps extends Omit<RACLinkProps, 'className'>, LayerProps {
23
+ }
24
+ /** A link that looks like a button — for navigation, never for actions. Same three layers as Button. */
25
+ export declare const LinkButton: import("react").ForwardRefExoticComponent<LinkButtonProps & import("react").RefAttributes<HTMLAnchorElement>>;
26
+ export { composeRenderProps };
27
+ //# sourceMappingURL=button.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"button.d.ts","sourceRoot":"","sources":["../../src/ui/button.tsx"],"names":[],"mappings":"AAGA,OAAO,EAGL,kBAAkB,EAClB,KAAK,WAAW,IAAI,cAAc,EAClC,KAAK,SAAS,IAAI,YAAY,EAC/B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACtB,MAAM,oBAAoB,CAAC;AAG5B,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,CAAC;AAEhD,UAAU,UAAU;IAClB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,mGAAmG;IACnG,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,oDAAoD;IACpD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,oGAAoG;IACpG,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gCAAgC;IAChC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,cAAc,EAAE,WAAW,CAAC,EAAE,UAAU;CAAG;AAErF;;;GAGG;AACH,eAAO,MAAM,MAAM,2GAoCjB,CAAC;AAEH,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,EAAE,UAAU;CAAG;AAEvF,wGAAwG;AACxG,eAAO,MAAM,UAAU,+GA4BrB,CAAC;AAEH,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
@@ -0,0 +1,30 @@
1
+ 'use client';
2
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { forwardRef } from 'react';
4
+ import { Button as RACButton, Link as RACLink, composeRenderProps, } from 'react-aria-components';
5
+ import { buttonTargetStyles, buttonVisualStyles, } from './button-styles.js';
6
+ import { SpinnerIcon } from './spinner.js';
7
+ /**
8
+ * React Aria Button. One `primary` per view; `outline` is the default.
9
+ * `isPending` keeps it focusable, announces the wait and swaps any leading icon for a spinner.
10
+ */
11
+ export const Button = forwardRef(function Button({ variant = 'outline', visualSize = 'default', fullWidth, className, visualClassName, children, ...props }, ref) {
12
+ const iconOnly = visualSize === 'icon' || visualSize === 'icon-sm';
13
+ return (_jsx(RACButton, { ref: ref, ...props, className: buttonTargetStyles({ fullWidth, className }), children: composeRenderProps(children, (kids, { isPending }) => (_jsx("span", { className: buttonVisualStyles({
14
+ variant,
15
+ visualSize,
16
+ fullWidth,
17
+ className: visualClassName,
18
+ }), children: isPending && iconOnly ? (_jsx(SpinnerIcon, {})) : (_jsxs(_Fragment, { children: [isPending && _jsx(SpinnerIcon, {}), kids] })) }))) }));
19
+ });
20
+ /** A link that looks like a button — for navigation, never for actions. Same three layers as Button. */
21
+ export const LinkButton = forwardRef(function LinkButton({ variant = 'outline', visualSize = 'default', fullWidth, className, visualClassName, children, ...props }, ref) {
22
+ return (_jsx(RACLink, { ref: ref, ...props, className: buttonTargetStyles({ fullWidth, className }), children: composeRenderProps(children, (kids) => (_jsx("span", { className: buttonVisualStyles({
23
+ variant,
24
+ visualSize,
25
+ fullWidth,
26
+ className: visualClassName,
27
+ }), children: kids }))) }));
28
+ });
29
+ export { composeRenderProps };
30
+ //# sourceMappingURL=button.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"button.js","sourceRoot":"","sources":["../../src/ui/button.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACnC,OAAO,EACL,MAAM,IAAI,SAAS,EACnB,IAAI,IAAI,OAAO,EACf,kBAAkB,GAGnB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,kBAAkB,EAClB,kBAAkB,GAGnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAkB3C;;;GAGG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,UAAU,CAAiC,SAAS,MAAM,CAC9E,EACE,OAAO,GAAG,SAAS,EACnB,UAAU,GAAG,SAAS,EACtB,SAAS,EACT,SAAS,EACT,eAAe,EACf,QAAQ,EACR,GAAG,KAAK,EACT,EACD,GAAG;IAEH,MAAM,QAAQ,GAAG,UAAU,KAAK,MAAM,IAAI,UAAU,KAAK,SAAS,CAAC;IACnE,OAAO,CACL,KAAC,SAAS,IAAC,GAAG,EAAE,GAAG,KAAM,KAAK,EAAE,SAAS,EAAE,kBAAkB,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,YACpF,kBAAkB,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CACrD,eACE,SAAS,EAAE,kBAAkB,CAAC;gBAC5B,OAAO;gBACP,UAAU;gBACV,SAAS;gBACT,SAAS,EAAE,eAAe;aAC3B,CAAC,YAED,SAAS,IAAI,QAAQ,CAAC,CAAC,CAAC,CACvB,KAAC,WAAW,KAAG,CAChB,CAAC,CAAC,CAAC,CACF,8BACG,SAAS,IAAI,KAAC,WAAW,KAAG,EAC5B,IAAI,IACJ,CACJ,GACI,CACR,CAAC,GACQ,CACb,CAAC;AACJ,CAAC,CAAC,CAAC;AAIH,wGAAwG;AACxG,MAAM,CAAC,MAAM,UAAU,GAAG,UAAU,CAAqC,SAAS,UAAU,CAC1F,EACE,OAAO,GAAG,SAAS,EACnB,UAAU,GAAG,SAAS,EACtB,SAAS,EACT,SAAS,EACT,eAAe,EACf,QAAQ,EACR,GAAG,KAAK,EACT,EACD,GAAG;IAEH,OAAO,CACL,KAAC,OAAO,IAAC,GAAG,EAAE,GAAG,KAAM,KAAK,EAAE,SAAS,EAAE,kBAAkB,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,YAClF,kBAAkB,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CACtC,eACE,SAAS,EAAE,kBAAkB,CAAC;gBAC5B,OAAO;gBACP,UAAU;gBACV,SAAS;gBACT,SAAS,EAAE,eAAe;aAC3B,CAAC,YAED,IAAI,GACA,CACR,CAAC,GACM,CACX,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { ReactNode } from 'react';
2
+ import { type CalendarProps as RACCalendarProps, type DateValue, type RangeCalendarProps as RACRangeCalendarProps } from 'react-aria-components';
3
+ export interface CalendarProps<T extends DateValue> extends Omit<RACCalendarProps<T>, 'className' | 'children'> {
4
+ /** Shown under the grid when the calendar is invalid. */
5
+ errorMessage?: ReactNode;
6
+ className?: string;
7
+ }
8
+ /**
9
+ * A month grid for choosing one date. Arrow keys move by day, Page Up/Down by month, Home/End to the week's
10
+ * ends; Enter selects. Inside a DatePicker this is supplied for you — use it alone only for an inline picker.
11
+ */
12
+ export declare function Calendar<T extends DateValue>({ errorMessage, className, ...props }: CalendarProps<T>): import("react").JSX.Element;
13
+ export interface RangeCalendarProps<T extends DateValue> extends Omit<RACRangeCalendarProps<T>, 'className' | 'children'> {
14
+ errorMessage?: ReactNode;
15
+ className?: string;
16
+ }
17
+ /** A month grid for choosing a start and an end date: select one, then the other. */
18
+ export declare function RangeCalendar<T extends DateValue>({ errorMessage, className, ...props }: RangeCalendarProps<T>): import("react").JSX.Element;
19
+ //# sourceMappingURL=calendar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../../src/ui/calendar.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,EAUL,KAAK,aAAa,IAAI,gBAAgB,EACtC,KAAK,SAAS,EACd,KAAK,kBAAkB,IAAI,qBAAqB,EACjD,MAAM,uBAAuB,CAAC;AA8G/B,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,SAAS,CAAE,SAAQ,IAAI,CAC9D,gBAAgB,CAAC,CAAC,CAAC,EACnB,WAAW,GAAG,UAAU,CACzB;IACC,yDAAyD;IACzD,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,SAAS,EAAE,EAC5C,YAAY,EACZ,SAAS,EACT,GAAG,KAAK,EACT,EAAE,aAAa,CAAC,CAAC,CAAC,+BAYlB;AAED,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,SAAS,CAAE,SAAQ,IAAI,CACnE,qBAAqB,CAAC,CAAC,CAAC,EACxB,WAAW,GAAG,UAAU,CACzB;IACC,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qFAAqF;AACrF,wBAAgB,aAAa,CAAC,CAAC,SAAS,SAAS,EAAE,EACjD,YAAY,EACZ,SAAS,EACT,GAAG,KAAK,EACT,EAAE,kBAAkB,CAAC,CAAC,CAAC,+BAYvB"}
@@ -0,0 +1,52 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Calendar as RACCalendar, CalendarCell as RACCalendarCell, CalendarGrid, CalendarGridBody, CalendarGridHeader, CalendarHeaderCell, Heading, RangeCalendar as RACRangeCalendar, Text, } from 'react-aria-components';
4
+ import { ChevronLeft, ChevronRight } from 'lucide-react';
5
+ import { cn } from '../lib/cn.js';
6
+ import { IconButton } from './icon-button.js';
7
+ /**
8
+ * The calendar root is `relative`: React Aria renders a visually hidden heading and page buttons inside it,
9
+ * and they must be positioned against the calendar, not against whatever scrolls around it.
10
+ */
11
+ const calendarRoot = 'relative flex w-fit max-w-full flex-col gap-3 text-sm text-foreground';
12
+ function CalendarHeader() {
13
+ return (_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx(IconButton, { slot: "previous", label: "Previous month", icon: ChevronLeft, visualSize: "icon-sm", tooltip: false }), _jsx(Heading, { className: "text-sm leading-5 font-semibold" }), _jsx(IconButton, { slot: "next", label: "Next month", icon: ChevronRight, visualSize: "icon-sm", tooltip: false })] }));
14
+ }
15
+ /**
16
+ * Days in three layers. The cell is the target (Layer 1): --target-min square, or the painted day if that is
17
+ * bigger. The day people see (Layer 2) is --control-height square (36px). At AAA each 36px day sits in a 44px
18
+ * target. Both shrink only when the viewport is too narrow for seven, so the calendar still reflows at 320px:
19
+ * the target never below --target-floor (24px, the 2.5.8 minimum). Below 44px at AAA that rests on 2.5.5's
20
+ * Equivalent exception — the typed date field beside it is always full size.
21
+ */
22
+ // Written out in full each time: Tailwind reads class names from the source, so they cannot be built.
23
+ const cellTarget = cn('group/cell relative grid aspect-square w-[max(var(--target-floor),min(max(var(--target-min),var(--control-height)),calc((100vw-3rem)/7)))] cursor-pointer place-items-center text-sm tabular-nums outline-none', 'data-outside-month:invisible data-disabled:cursor-not-allowed data-disabled:opacity-60 data-unavailable:cursor-not-allowed');
24
+ const dayBase = cn('relative grid h-[min(var(--control-height),calc((100vw-3rem)/7))] place-items-center rounded-md', 'group-data-hovered/cell:bg-accent group-data-pressed/cell:bg-accent group-data-focus-visible/cell:focus-ring',
25
+ // Today: a bar under the number (React Aria also names it "Today").
26
+ "group-data-today/cell:after:absolute group-data-today/cell:after:bottom-1 group-data-today/cell:after:h-0.5 group-data-today/cell:after:w-3 group-data-today/cell:after:rounded-full group-data-today/cell:after:bg-current group-data-today/cell:after:content-['']",
27
+ // Unavailable: struck through as well as muted.
28
+ 'group-data-unavailable/cell:text-muted-foreground group-data-unavailable/cell:line-through group-data-disabled/cell:text-muted-foreground');
29
+ // Selected: filled, and weight 600 — not colour alone.
30
+ const filled = 'bg-primary font-semibold text-primary-foreground group-data-hovered/cell:bg-primary-hover group-data-pressed/cell:bg-primary-hover';
31
+ const singleDay = ({ isSelected, isInvalid }) => cn(dayBase, 'w-[min(var(--control-height),calc((100vw-3rem)/7))]', isSelected && (isInvalid ? 'bg-destructive font-semibold text-destructive-foreground' : filled));
32
+ // Between the ends: a band in brand-subtle across the whole cell, so it is continuous at every level; the two
33
+ // ends stay filled, rounded on their outer side.
34
+ const rangeDay = ({ isSelected, isSelectionStart, isSelectionEnd, }) => cn(dayBase, isSelected ? 'w-full' : 'w-[min(var(--control-height),calc((100vw-3rem)/7))]', isSelected &&
35
+ !isSelectionStart &&
36
+ !isSelectionEnd &&
37
+ 'rounded-none bg-brand-subtle text-foreground group-data-hovered/cell:bg-brand-subtle', (isSelectionStart || isSelectionEnd) && filled, isSelected && isSelectionStart && !isSelectionEnd && 'rounded-e-none', isSelected && isSelectionEnd && !isSelectionStart && 'rounded-s-none');
38
+ function Grid({ range }) {
39
+ return (_jsxs(CalendarGrid, { className: "border-separate border-spacing-y-0.5", weekdayStyle: "short", children: [_jsx(CalendarGridHeader, { children: (day) => (_jsx(CalendarHeaderCell, { className: "pb-1 text-[13px] leading-5 font-semibold text-muted-foreground", children: day })) }), _jsx(CalendarGridBody, { children: (date) => (_jsx(RACCalendarCell, { date: date, className: cellTarget, children: (state) => (_jsx("span", { className: range ? rangeDay(state) : singleDay(state), children: state.formattedDate })) })) })] }));
40
+ }
41
+ /**
42
+ * A month grid for choosing one date. Arrow keys move by day, Page Up/Down by month, Home/End to the week's
43
+ * ends; Enter selects. Inside a DatePicker this is supplied for you — use it alone only for an inline picker.
44
+ */
45
+ export function Calendar({ errorMessage, className, ...props }) {
46
+ return (_jsxs(RACCalendar, { ...props, className: cn(calendarRoot, className), children: [_jsx(CalendarHeader, {}), _jsx(Grid, {}), errorMessage && (_jsx(Text, { slot: "errorMessage", className: "text-[13px] leading-5 font-semibold text-destructive", children: errorMessage }))] }));
47
+ }
48
+ /** A month grid for choosing a start and an end date: select one, then the other. */
49
+ export function RangeCalendar({ errorMessage, className, ...props }) {
50
+ return (_jsxs(RACRangeCalendar, { ...props, className: cn(calendarRoot, className), children: [_jsx(CalendarHeader, {}), _jsx(Grid, { range: true }), errorMessage && (_jsx(Text, { slot: "errorMessage", className: "text-[13px] leading-5 font-semibold text-destructive", children: errorMessage }))] }));
51
+ }
52
+ //# sourceMappingURL=calendar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calendar.js","sourceRoot":"","sources":["../../src/ui/calendar.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type IconProp } from './icon.js';
3
+ export interface CardProps {
4
+ title?: ReactNode;
5
+ /** 3 (default) under a section's h2; 2 when the cards are the page's sections. */
6
+ headingLevel?: 2 | 3 | 4;
7
+ /** Muted text under the title. */
8
+ description?: ReactNode;
9
+ /** Shown above the title, in a brand-tinted square. */
10
+ icon?: IconProp;
11
+ /** Anything else, after the description. */
12
+ children?: ReactNode;
13
+ /** Links or buttons, kept at the bottom of the card. */
14
+ footer?: ReactNode;
15
+ className?: string;
16
+ }
17
+ /** A surface for one item: an icon, a title, a description, content and a footer. Server-safe. */
18
+ export declare function Card({ title, headingLevel, description, icon, children, footer, className, }: CardProps): import("react").JSX.Element;
19
+ export interface CardGridProps {
20
+ /** Cards (Card or LinkCard). Each is put in a list item. */
21
+ children: ReactNode;
22
+ className?: string;
23
+ }
24
+ /**
25
+ * Cards as many to a row as fit at 18rem, one to a row on a phone. A list, so a screen reader says how many:
26
+ * `role="list"` keeps it one in Safari, where Tailwind's reset takes the markers off. Each card fills its row's
27
+ * height. Server-safe.
28
+ */
29
+ export declare function CardGrid({ children, className }: CardGridProps): import("react").JSX.Element;
30
+ //# sourceMappingURL=card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"card.d.ts","sourceRoot":"","sources":["../../src/ui/card.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEjD,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAC;AAEtD,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,kFAAkF;IAClF,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACzB,kCAAkC;IAClC,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,uDAAuD;IACvD,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,wDAAwD;IACxD,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,kGAAkG;AAClG,wBAAgB,IAAI,CAAC,EACnB,KAAK,EACL,YAAgB,EAChB,WAAW,EACX,IAAI,EACJ,QAAQ,EACR,MAAM,EACN,SAAS,GACV,EAAE,SAAS,+BAeX;AAED,MAAM,WAAW,aAAa;IAC5B,4DAA4D;IAC5D,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,aAAa,+BAW9D"}
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Children } from 'react';
3
+ import { cn } from '../lib/cn.js';
4
+ import { renderIcon } from './icon.js';
5
+ /** A surface for one item: an icon, a title, a description, content and a footer. Server-safe. */
6
+ export function Card({ title, headingLevel = 3, description, icon, children, footer, className, }) {
7
+ const Heading = `h${headingLevel}`;
8
+ return (_jsxs("div", { className: cn('card-surface flex flex-col gap-2', className), children: [icon && (_jsx("span", { className: "mb-2 inline-flex size-10 items-center justify-center rounded-md bg-brand-subtle text-brand", children: renderIcon(icon, 'size-5') })), title && _jsx(Heading, { className: "text-subheading", children: title }), description && _jsx("p", { className: "text-body text-muted-foreground", children: description }), children, footer && _jsx("div", { className: "mt-auto flex flex-wrap gap-(--control-gap) pt-4", children: footer })] }));
9
+ }
10
+ /**
11
+ * Cards as many to a row as fit at 18rem, one to a row on a phone. A list, so a screen reader says how many:
12
+ * `role="list"` keeps it one in Safari, where Tailwind's reset takes the markers off. Each card fills its row's
13
+ * height. Server-safe.
14
+ */
15
+ export function CardGrid({ children, className }) {
16
+ return (_jsx("ul", { role: "list", className: cn('card-grid', className), children: Children.map(children, (child) => child == null || typeof child === 'boolean' ? null : _jsx("li", { className: "grid", children: child })) }));
17
+ }
18
+ //# sourceMappingURL=card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"card.js","sourceRoot":"","sources":["../../src/ui/card.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAkB,MAAM,OAAO,CAAC;AACjD,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,UAAU,EAAiB,MAAM,WAAW,CAAC;AAiBtD,kGAAkG;AAClG,MAAM,UAAU,IAAI,CAAC,EACnB,KAAK,EACL,YAAY,GAAG,CAAC,EAChB,WAAW,EACX,IAAI,EACJ,QAAQ,EACR,MAAM,EACN,SAAS,GACC;IACV,MAAM,OAAO,GAAG,IAAI,YAAY,EAAwB,CAAC;IACzD,OAAO,CACL,eAAK,SAAS,EAAE,EAAE,CAAC,kCAAkC,EAAE,SAAS,CAAC,aAC9D,IAAI,IAAI,CACP,eAAM,SAAS,EAAC,4FAA4F,YACzG,UAAU,CAAC,IAAI,EAAE,QAAQ,CAAC,GACtB,CACR,EACA,KAAK,IAAI,KAAC,OAAO,IAAC,SAAS,EAAC,iBAAiB,YAAE,KAAK,GAAW,EAC/D,WAAW,IAAI,YAAG,SAAS,EAAC,iCAAiC,YAAE,WAAW,GAAK,EAC/E,QAAQ,EACR,MAAM,IAAI,cAAK,SAAS,EAAC,iDAAiD,YAAE,MAAM,GAAO,IACtF,CACP,CAAC;AACJ,CAAC;AAQD;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAiB;IAC7D,OAAO,CAEL,aAAI,IAAI,EAAC,MAAM,EAAC,SAAS,EAAE,EAAE,CAAC,WAAW,EAAE,SAAS,CAAC,YAGlD,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAChC,KAAK,IAAI,IAAI,IAAI,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,aAAI,SAAS,EAAC,MAAM,YAAE,KAAK,GAAM,CACvF,GACE,CACN,CAAC;AACJ,CAAC"}
@@ -0,0 +1,36 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type CheckboxGroupProps as RACCheckboxGroupProps, type CheckboxProps as RACCheckboxProps } from 'react-aria-components';
3
+ export interface CheckboxProps extends Omit<RACCheckboxProps, 'className' | 'children' | 'validate'> {
4
+ children: ReactNode;
5
+ /** Second line under the label. */
6
+ description?: ReactNode;
7
+ /** For a single required checkbox ("I accept the terms"): what to do. */
8
+ requiredMessage?: string;
9
+ className?: string;
10
+ }
11
+ /**
12
+ * A yes/no choice submitted with the form (settings that apply at once are a Switch).
13
+ * The whole row — box and label — is the target, at least --target-min tall.
14
+ */
15
+ export declare function Checkbox({ children, description, requiredMessage, className, ...props }: CheckboxProps): import("react").JSX.Element;
16
+ export interface CheckboxGroupProps extends Omit<RACCheckboxGroupProps, 'className' | 'children' | 'validate'> {
17
+ label: string;
18
+ description?: ReactNode;
19
+ errorMessage?: string;
20
+ /** For "choose at least one". */
21
+ requiredMessage?: string;
22
+ children: ReactNode;
23
+ className?: string;
24
+ }
25
+ /** A labelled list of checkboxes, read as a group. */
26
+ export declare function CheckboxGroup({ label, description, errorMessage, requiredMessage, children, className, ...props }: CheckboxGroupProps): import("react").JSX.Element;
27
+ export interface SelectionCheckboxProps extends Omit<RACCheckboxProps, 'className' | 'children' | 'slot'> {
28
+ className?: string;
29
+ }
30
+ /**
31
+ * The box alone, for the selection column of a Table or a GridList item. React Aria wires it to the row
32
+ * (`slot="selection"`) and names it — "Select" for a row, "Select All" in the header — so it needs no label.
33
+ * The target is --target-min square and, like every choice control, the root is `relative`.
34
+ */
35
+ export declare function SelectionCheckbox({ className, ...props }: SelectionCheckboxProps): import("react").JSX.Element;
36
+ //# sourceMappingURL=checkbox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkbox.d.ts","sourceRoot":"","sources":["../../src/ui/checkbox.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC1D,OAAO,EAGL,KAAK,kBAAkB,IAAI,qBAAqB,EAChD,KAAK,aAAa,IAAI,gBAAgB,EACvC,MAAM,uBAAuB,CAAC;AAqB/B,MAAM,WAAW,aAAc,SAAQ,IAAI,CACzC,gBAAgB,EAChB,WAAW,GAAG,UAAU,GAAG,UAAU,CACtC;IACC,QAAQ,EAAE,SAAS,CAAC;IACpB,mCAAmC;IACnC,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,yEAAyE;IACzE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,EACvB,QAAQ,EACR,WAAW,EACX,eAAe,EACf,SAAS,EACT,GAAG,KAAK,EACT,EAAE,aAAa,+BAiEf;AAED,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAC9C,qBAAqB,EACrB,WAAW,GAAG,UAAU,GAAG,UAAU,CACtC;IACC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iCAAiC;IACjC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,sDAAsD;AACtD,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,WAAW,EACX,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,SAAS,EACT,GAAG,KAAK,EACT,EAAE,kBAAkB,+BAyBpB;AAED,MAAM,WAAW,sBAAuB,SAAQ,IAAI,CAClD,gBAAgB,EAChB,WAAW,GAAG,UAAU,GAAG,MAAM,CAClC;IACC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,sBAAsB,+BAiBhF"}