@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 @@
1
+ {"version":3,"file":"tag.js","sourceRoot":"","sources":["../../src/ui/tag.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,UAAU,EAAiB,MAAM,WAAW,CAAC;AAStD;;;GAGG;AACH,MAAM,UAAU,GAAG,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAY;IACzD,OAAO,CACL,gBAAM,SAAS,EAAE,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC,aAClC,IAAI,IAAI,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,EACpC,QAAQ,IACJ,CACR,CAAC;AACJ,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { ReactNode } from 'react';
2
+ import { type TextFieldProps as RACTextFieldProps } from 'react-aria-components';
3
+ import { type FieldWidth } from './field.js';
4
+ export interface TextFieldProps extends Omit<RACTextFieldProps, 'className' | 'children' | 'validate'> {
5
+ label: string;
6
+ /** Hint: what to enter and in what format. */
7
+ description?: ReactNode;
8
+ /** Overrides the validation message. Usually leave it to `validate` / `requiredMessage`. */
9
+ errorMessage?: string;
10
+ /** Default "Enter <label>". */
11
+ requiredMessage?: string;
12
+ validate?: (value: string) => string | string[] | true | null | undefined;
13
+ placeholder?: string;
14
+ width?: FieldWidth;
15
+ className?: string;
16
+ }
17
+ /**
18
+ * Single-line text input. Pass `autoComplete` for anything about the user (email, name, organization,
19
+ * tel, postal-code…) so browsers can fill it and assistive tech can identify it (WCAG 1.3.5).
20
+ */
21
+ export declare function TextField({ label, description, errorMessage, requiredMessage, validate, placeholder, width, className, ...props }: TextFieldProps): import("react").JSX.Element;
22
+ export interface TextAreaProps extends TextFieldProps {
23
+ rows?: number;
24
+ }
25
+ /** Multi-line text. Grows with its rows; never resizes narrower than its field. */
26
+ export declare function TextArea({ label, description, errorMessage, requiredMessage, validate, placeholder, width, rows, className, ...props }: TextAreaProps): import("react").JSX.Element;
27
+ //# sourceMappingURL=text-field.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-field.d.ts","sourceRoot":"","sources":["../../src/ui/text-field.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,EAIL,KAAK,cAAc,IAAI,iBAAiB,EACzC,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAWL,KAAK,UAAU,EAChB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,cAAe,SAAQ,IAAI,CAC1C,iBAAiB,EACjB,WAAW,GAAG,UAAU,GAAG,UAAU,CACtC;IACC,KAAK,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,4FAA4F;IAC5F,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+BAA+B;IAC/B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,WAAW,EACX,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,WAAW,EACX,KAAc,EACd,SAAS,EACT,GAAG,KAAK,EACT,EAAE,cAAc,+BAsBhB;AAED,MAAM,WAAW,aAAc,SAAQ,cAAc;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,mFAAmF;AACnF,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,WAAW,EACX,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,WAAW,EACX,KAAY,EACZ,IAAQ,EACR,SAAS,EACT,GAAG,KAAK,EACT,EAAE,aAAa,+BAoBf"}
@@ -0,0 +1,17 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Input, TextArea as RACTextArea, TextField as RACTextField, } from 'react-aria-components';
4
+ import { cn } from '../lib/cn.js';
5
+ import { FieldDescription, FieldError, FieldLabel, fieldStack, fieldBox, fieldInput, fieldWidths, lcFirst, textAreaStyles, withRequired, } from './field.js';
6
+ /**
7
+ * Single-line text input. Pass `autoComplete` for anything about the user (email, name, organization,
8
+ * tel, postal-code…) so browsers can fill it and assistive tech can identify it (WCAG 1.3.5).
9
+ */
10
+ export function TextField({ label, description, errorMessage, requiredMessage, validate, placeholder, width = 'full', className, ...props }) {
11
+ return (_jsxs(RACTextField, { ...props, validate: withRequired(props.isRequired, requiredMessage ?? `Enter ${lcFirst(label)}`, (v) => !v?.trim(), validate), className: cn(fieldStack, fieldWidths[width], className), children: [_jsx(FieldLabel, { isRequired: props.isRequired, children: label }), description && _jsx(FieldDescription, { children: description }), _jsx(FieldError, { requiredMessage: requiredMessage ?? `Enter ${lcFirst(label)}`, children: errorMessage }), _jsx("div", { className: fieldBox, children: _jsx(Input, { placeholder: placeholder, className: fieldInput }) })] }));
12
+ }
13
+ /** Multi-line text. Grows with its rows; never resizes narrower than its field. */
14
+ export function TextArea({ label, description, errorMessage, requiredMessage, validate, placeholder, width = 'lg', rows = 4, className, ...props }) {
15
+ return (_jsxs(RACTextField, { ...props, validate: withRequired(props.isRequired, requiredMessage ?? `Enter ${lcFirst(label)}`, (v) => !v?.trim(), validate), className: cn(fieldStack, fieldWidths[width], className), children: [_jsx(FieldLabel, { isRequired: props.isRequired, children: label }), description && _jsx(FieldDescription, { children: description }), _jsx(FieldError, { requiredMessage: requiredMessage ?? `Enter ${lcFirst(label)}`, children: errorMessage }), _jsx(RACTextArea, { rows: rows, placeholder: placeholder, className: textAreaStyles })] }));
16
+ }
17
+ //# sourceMappingURL=text-field.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-field.js","sourceRoot":"","sources":["../../src/ui/text-field.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAGb,OAAO,EACL,KAAK,EACL,QAAQ,IAAI,WAAW,EACvB,SAAS,IAAI,YAAY,GAE1B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EACL,gBAAgB,EAChB,UAAU,EACV,UAAU,EACV,UAAU,EACV,QAAQ,EACR,UAAU,EACV,WAAW,EACX,OAAO,EACP,cAAc,EACd,YAAY,GAEb,MAAM,YAAY,CAAC;AAmBpB;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,EACxB,KAAK,EACL,WAAW,EACX,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,WAAW,EACX,KAAK,GAAG,MAAM,EACd,SAAS,EACT,GAAG,KAAK,EACO;IACf,OAAO,CACL,MAAC,YAAY,OACP,KAAK,EACT,QAAQ,EAAE,YAAY,CACpB,KAAK,CAAC,UAAU,EAChB,eAAe,IAAI,SAAS,OAAO,CAAC,KAAK,CAAC,EAAE,EAC5C,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EACjB,QAAQ,CACT,EACD,SAAS,EAAE,EAAE,CAAC,UAAU,EAAE,WAAW,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,aAExD,KAAC,UAAU,IAAC,UAAU,EAAE,KAAK,CAAC,UAAU,YAAG,KAAK,GAAc,EAC7D,WAAW,IAAI,KAAC,gBAAgB,cAAE,WAAW,GAAoB,EAClE,KAAC,UAAU,IAAC,eAAe,EAAE,eAAe,IAAI,SAAS,OAAO,CAAC,KAAK,CAAC,EAAE,YACtE,YAAY,GACF,EACb,cAAK,SAAS,EAAE,QAAQ,YACtB,KAAC,KAAK,IAAC,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,GAAI,GACtD,IACO,CAChB,CAAC;AACJ,CAAC;AAMD,mFAAmF;AACnF,MAAM,UAAU,QAAQ,CAAC,EACvB,KAAK,EACL,WAAW,EACX,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,WAAW,EACX,KAAK,GAAG,IAAI,EACZ,IAAI,GAAG,CAAC,EACR,SAAS,EACT,GAAG,KAAK,EACM;IACd,OAAO,CACL,MAAC,YAAY,OACP,KAAK,EACT,QAAQ,EAAE,YAAY,CACpB,KAAK,CAAC,UAAU,EAChB,eAAe,IAAI,SAAS,OAAO,CAAC,KAAK,CAAC,EAAE,EAC5C,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EACjB,QAAQ,CACT,EACD,SAAS,EAAE,EAAE,CAAC,UAAU,EAAE,WAAW,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,aAExD,KAAC,UAAU,IAAC,UAAU,EAAE,KAAK,CAAC,UAAU,YAAG,KAAK,GAAc,EAC7D,WAAW,IAAI,KAAC,gBAAgB,cAAE,WAAW,GAAoB,EAClE,KAAC,UAAU,IAAC,eAAe,EAAE,eAAe,IAAI,SAAS,OAAO,CAAC,KAAK,CAAC,EAAE,YACtE,YAAY,GACF,EACb,KAAC,WAAW,IAAC,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,GAAI,IACnE,CAChB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { type LinkProps as RACLinkProps } from 'react-aria-components';
2
+ export interface TextLinkProps extends Omit<RACLinkProps, 'className'> {
3
+ /**
4
+ * 'brand' (default) for links in body text. 'inherit' takes the colour around it, for a link inside an
5
+ * alert, an error message or any ground brand isn't checked against — the underline still marks it.
6
+ */
7
+ tone?: 'brand' | 'inherit';
8
+ className?: string;
9
+ }
10
+ /**
11
+ * A link in running text: underlined, because colour alone can't mark a link here (brand is under 3:1 against
12
+ * body text in every theme), with a thicker underline on hover and the focus ring. It stays inline, so it
13
+ * wraps with the sentence and falls under 2.5.8's and 2.5.5's Inline exception. For navigation use the
14
+ * sidebar, menus or breadcrumbs; for a link that looks like a button, LinkButton.
15
+ */
16
+ export declare const TextLink: import("react").ForwardRefExoticComponent<TextLinkProps & import("react").RefAttributes<HTMLAnchorElement>>;
17
+ //# sourceMappingURL=text-link.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-link.d.ts","sourceRoot":"","sources":["../../src/ui/text-link.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAmB,KAAK,SAAS,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAGxF,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC;IACpE;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,6GAWnB,CAAC"}
@@ -0,0 +1,15 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { forwardRef } from 'react';
4
+ import { Link as RACLink } from 'react-aria-components';
5
+ import { cn } from '../lib/cn.js';
6
+ /**
7
+ * A link in running text: underlined, because colour alone can't mark a link here (brand is under 3:1 against
8
+ * body text in every theme), with a thicker underline on hover and the focus ring. It stays inline, so it
9
+ * wraps with the sentence and falls under 2.5.8's and 2.5.5's Inline exception. For navigation use the
10
+ * sidebar, menus or breadcrumbs; for a link that looks like a button, LinkButton.
11
+ */
12
+ export const TextLink = forwardRef(function TextLink({ tone = 'brand', className, ...props }, ref) {
13
+ return (_jsx(RACLink, { ref: ref, ...props, className: cn('text-link', tone === 'brand' ? 'text-brand' : 'text-inherit', className) }));
14
+ });
15
+ //# sourceMappingURL=text-link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-link.js","sourceRoot":"","sources":["../../src/ui/text-link.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACnC,OAAO,EAAE,IAAI,IAAI,OAAO,EAAkC,MAAM,uBAAuB,CAAC;AACxF,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAWlC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,UAAU,CAAmC,SAAS,QAAQ,CACpF,EAAE,IAAI,GAAG,OAAO,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EACvC,GAAG;IAEH,OAAO,CACL,KAAC,OAAO,IACN,GAAG,EAAE,GAAG,KACJ,KAAK,EACT,SAAS,EAAE,EAAE,CAAC,WAAW,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,cAAc,EAAE,SAAS,CAAC,GACvF,CACH,CAAC;AACJ,CAAC,CAAC,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { type ConformanceLevel, type ThemeOptions, type ThemePreference } from './theme.js';
2
+ export interface ThemeSwitcherProps extends ThemeOptions {
3
+ /** Also offer AA and AAA, as the docs site does. */
4
+ level?: boolean;
5
+ /**
6
+ * What to show before the page has read the stored choice, as the server renders it. With `storage="cookie"`,
7
+ * pass the cookie's value from the server. Default: `defaultTheme`.
8
+ */
9
+ initialTheme?: ThemePreference;
10
+ /** The level to show before the page has read it. Default `aa`. */
11
+ initialLevel?: ConformanceLevel;
12
+ /** After a change, with the choice, whether the page is now dark, and the level. */
13
+ onChange?: (state: {
14
+ theme: ThemePreference;
15
+ resolved: 'light' | 'dark';
16
+ level: ConformanceLevel;
17
+ }) => void;
18
+ className?: string;
19
+ }
20
+ /**
21
+ * Light, Dark and System, and optionally AA and AAA: segmented controls that set `<html>` and keep the choice
22
+ * where ThemeScript reads it before the next page paints. System follows the operating system as it changes.
23
+ * Use the same options as the page's ThemeScript.
24
+ */
25
+ export declare function ThemeSwitcher({ level: offerLevel, initialTheme, initialLevel, onChange, className, ...options }: ThemeSwitcherProps): import("react").JSX.Element;
26
+ //# sourceMappingURL=theme-switcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-switcher.d.ts","sourceRoot":"","sources":["../../src/ui/theme-switcher.tsx"],"names":[],"mappings":"AAKA,OAAO,EAOL,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,eAAe,EACrB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,kBAAmB,SAAQ,YAAY;IACtD,oDAAoD;IACpD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;OAGG;IACH,YAAY,CAAC,EAAE,eAAe,CAAC;IAC/B,mEAAmE;IACnE,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,oFAAoF;IACpF,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE;QACjB,KAAK,EAAE,eAAe,CAAC;QACvB,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAC;QAC3B,KAAK,EAAE,gBAAgB,CAAC;KACzB,KAAK,IAAI,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EAAE,UAAkB,EACzB,YAAY,EACZ,YAAmB,EACnB,QAAQ,EACR,SAAS,EACT,GAAG,OAAO,EACX,EAAE,kBAAkB,+BA+CpB"}
@@ -0,0 +1,32 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useCallback, useSyncExternalStore } from 'react';
4
+ import { cn } from '../lib/cn.js';
5
+ import { Segment, SegmentedControl } from './segmented-control.js';
6
+ import { applyLevel, applyTheme, readLevel, readTheme, resolveTheme, subscribeTheme, } from './theme.js';
7
+ /**
8
+ * Light, Dark and System, and optionally AA and AAA: segmented controls that set `<html>` and keep the choice
9
+ * where ThemeScript reads it before the next page paints. System follows the operating system as it changes.
10
+ * Use the same options as the page's ThemeScript.
11
+ */
12
+ export function ThemeSwitcher({ level: offerLevel = false, initialTheme, initialLevel = 'aa', onChange, className, ...options }) {
13
+ const { storage, key, levelKey, defaultTheme } = options;
14
+ const subscribe = useCallback((cb) => subscribeTheme(cb, { storage, key, levelKey, defaultTheme }), [storage, key, levelKey, defaultTheme]);
15
+ const theme = useSyncExternalStore(subscribe, () => readTheme(options), () => initialTheme ?? defaultTheme ?? 'system');
16
+ const level = useSyncExternalStore(subscribe, readLevel, () => initialLevel);
17
+ const report = (t, l) => onChange?.({ theme: t, resolved: resolveTheme(t), level: l });
18
+ return (_jsxs("div", { className: cn('flex flex-wrap items-center gap-2', className), children: [_jsxs(SegmentedControl, { "aria-label": "Theme", selectedKeys: [theme], onSelectionChange: (keys) => {
19
+ const next = [...keys][0];
20
+ if (next !== 'light' && next !== 'dark' && next !== 'system')
21
+ return;
22
+ applyTheme(next, options);
23
+ report(next, level);
24
+ }, children: [_jsx(Segment, { id: "light", children: "Light" }), _jsx(Segment, { id: "dark", children: "Dark" }), _jsx(Segment, { id: "system", children: "System" })] }), offerLevel && (_jsxs(SegmentedControl, { "aria-label": "Conformance level", selectedKeys: [level], onSelectionChange: (keys) => {
25
+ const next = [...keys][0];
26
+ if (next !== 'aa' && next !== 'aaa')
27
+ return;
28
+ applyLevel(next, options);
29
+ report(theme, next);
30
+ }, children: [_jsx(Segment, { id: "aa", children: "AA" }), _jsx(Segment, { id: "aaa", children: "AAA" })] }))] }));
31
+ }
32
+ //# sourceMappingURL=theme-switcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-switcher.js","sourceRoot":"","sources":["../../src/ui/theme-switcher.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ export type ThemePreference = 'light' | 'dark' | 'system';
2
+ export type ConformanceLevel = 'aa' | 'aaa';
3
+ export interface ThemeOptions {
4
+ /**
5
+ * Where the choice is kept. `localStorage` (default): in this browser, read by the script before the first
6
+ * paint. `cookie`: sent with every request as well, so the server can render `<html>` with it.
7
+ */
8
+ storage?: 'localStorage' | 'cookie';
9
+ /** The key, or the cookie's name. Letters, digits, `-` and `_`. Default `setsquare-ui-theme`. */
10
+ key?: string;
11
+ /** The key for the level, when the page offers AA and AAA. Default `setsquare-ui-level`. */
12
+ levelKey?: string;
13
+ /** What to show before anyone chooses. Default `system`: follow the operating system. */
14
+ defaultTheme?: ThemePreference;
15
+ }
16
+ /** Dispatched on window when this tab changes the theme or level; `storage` only fires in other tabs. */
17
+ export declare const THEME_CHANGE_EVENT = "setsquare-ui:theme-change";
18
+ /**
19
+ * The script that puts the stored theme and level on `<html>` before the first paint: `class="dark"`,
20
+ * `color-scheme`, and `data-conformance` when a level was chosen. Inline it in `<head>` (ThemeScript does),
21
+ * where it runs before anything is drawn. It reads nothing but the stored choice and the colour-scheme media
22
+ * query, and a storage the browser refuses leaves the page as the server rendered it.
23
+ */
24
+ export declare function themeScript(options?: ThemeOptions): string;
25
+ /**
26
+ * The script's hash, `sha256-…`, for a Content-Security-Policy that lists inline scripts by hash
27
+ * (`script-src 'sha256-…'`) rather than allowing them all. The same options give the same hash.
28
+ */
29
+ export declare function themeScriptHash(options?: ThemeOptions): Promise<string>;
30
+ export interface ThemeScriptProps extends ThemeOptions {
31
+ /** The request's CSP nonce, for a policy that allows inline scripts by nonce. */
32
+ nonce?: string;
33
+ }
34
+ /**
35
+ * The theme script as an element, for `<head>` in the root layout. Give `<html>` `suppressHydrationWarning`:
36
+ * the script changes its class and style before React hydrates it.
37
+ */
38
+ export declare function ThemeScript({ nonce, ...options }: ThemeScriptProps): import("react").JSX.Element;
39
+ /** The stored theme, or the default when there is none. Browser only. */
40
+ export declare function readTheme(options?: ThemeOptions): ThemePreference;
41
+ /** The level `<html>` shows. Browser only. */
42
+ export declare function readLevel(): ConformanceLevel;
43
+ /** Whether the page is dark: the theme, with `system` resolved by the colour-scheme media query. */
44
+ export declare function resolveTheme(theme: ThemePreference): 'light' | 'dark';
45
+ /** Puts a theme on `<html>` and keeps it, as the script will on the next page. Browser only. */
46
+ export declare function applyTheme(theme: ThemePreference, options?: ThemeOptions): void;
47
+ /** Puts a conformance level on `<html>` and keeps it. Browser only. */
48
+ export declare function applyLevel(level: ConformanceLevel, options?: ThemeOptions): void;
49
+ /**
50
+ * Calls back when the theme or level may have changed: this tab, another tab, or the operating system's
51
+ * colour scheme while the theme is `system`. Browser only.
52
+ */
53
+ export declare function subscribeTheme(onChange: () => void, options?: ThemeOptions): () => void;
54
+ //# sourceMappingURL=theme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/ui/theme.tsx"],"names":[],"mappings":"AAGA,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AAC1D,MAAM,MAAM,gBAAgB,GAAG,IAAI,GAAG,KAAK,CAAC;AAE5C,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,OAAO,CAAC,EAAE,cAAc,GAAG,QAAQ,CAAC;IACpC,iGAAiG;IACjG,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yFAAyF;IACzF,YAAY,CAAC,EAAE,eAAe,CAAC;CAChC;AAID,yGAAyG;AACzG,eAAO,MAAM,kBAAkB,8BAA8B,CAAC;AAe9D;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,MAAM,CAO1D;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAM7E;AAED,MAAM,WAAW,gBAAiB,SAAQ,YAAY;IACpD,iFAAiF;IACjF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,GAAG,OAAO,EAAE,EAAE,gBAAgB,+BAElE;AAwBD,yEAAyE;AACzE,wBAAgB,SAAS,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,eAAe,CAMjE;AAED,8CAA8C;AAC9C,wBAAgB,SAAS,IAAI,gBAAgB,CAE5C;AAED,oGAAoG;AACpG,wBAAgB,YAAY,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,GAAG,MAAM,CAGrE;AASD,gGAAgG;AAChG,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI,CAK/E;AAED,uEAAuE;AACvE,wBAAgB,UAAU,CAAC,KAAK,EAAE,gBAAgB,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI,CAKhF;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,MAAM,IAAI,CAmBvF"}
@@ -0,0 +1,135 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ const KEY = /^[\w-]+$/;
3
+ const PREFERENCES = ['light', 'dark', 'system'];
4
+ /** Dispatched on window when this tab changes the theme or level; `storage` only fires in other tabs. */
5
+ export const THEME_CHANGE_EVENT = 'setsquare-ui:theme-change';
6
+ function settings(options = {}) {
7
+ const storage = options.storage ?? 'localStorage';
8
+ const key = options.key ?? 'setsquare-ui-theme';
9
+ const levelKey = options.levelKey ?? 'setsquare-ui-level';
10
+ const defaultTheme = options.defaultTheme ?? 'system';
11
+ for (const k of [key, levelKey])
12
+ if (!KEY.test(k))
13
+ throw new Error(`Theme keys are letters, digits, - and _ only; "${k}" isn't one.`);
14
+ if (!PREFERENCES.includes(defaultTheme))
15
+ throw new Error(`defaultTheme is light, dark or system, not "${defaultTheme}".`);
16
+ return { storage, key, levelKey, defaultTheme };
17
+ }
18
+ /**
19
+ * The script that puts the stored theme and level on `<html>` before the first paint: `class="dark"`,
20
+ * `color-scheme`, and `data-conformance` when a level was chosen. Inline it in `<head>` (ThemeScript does),
21
+ * where it runs before anything is drawn. It reads nothing but the stored choice and the colour-scheme media
22
+ * query, and a storage the browser refuses leaves the page as the server rendered it.
23
+ */
24
+ export function themeScript(options) {
25
+ const { storage, key, levelKey, defaultTheme } = settings(options);
26
+ const read = storage === 'cookie'
27
+ ? `function g(k){var m=document.cookie.match(new RegExp('(?:^|; )'+k+'=([^;]*)'));return m?m[1]:null}`
28
+ : `function g(k){return localStorage.getItem(k)}`;
29
+ return `(function(){try{${read}var d=document.documentElement,t=g('${key}'),l=g('${levelKey}');if(t!=='light'&&t!=='dark'&&t!=='system')t='${defaultTheme}';var n=t==='dark'||(t==='system'&&matchMedia('(prefers-color-scheme: dark)').matches);d.classList.toggle('dark',n);d.style.colorScheme=n?'dark':'light';if(l==='aa'||l==='aaa')d.setAttribute('data-conformance',l)}catch(e){}})();`;
30
+ }
31
+ /**
32
+ * The script's hash, `sha256-…`, for a Content-Security-Policy that lists inline scripts by hash
33
+ * (`script-src 'sha256-…'`) rather than allowing them all. The same options give the same hash.
34
+ */
35
+ export async function themeScriptHash(options) {
36
+ const bytes = new TextEncoder().encode(themeScript(options));
37
+ const digest = new Uint8Array(await globalThis.crypto.subtle.digest('SHA-256', bytes));
38
+ let binary = '';
39
+ for (const b of digest)
40
+ binary += String.fromCharCode(b);
41
+ return `sha256-${btoa(binary)}`;
42
+ }
43
+ /**
44
+ * The theme script as an element, for `<head>` in the root layout. Give `<html>` `suppressHydrationWarning`:
45
+ * the script changes its class and style before React hydrates it.
46
+ */
47
+ export function ThemeScript({ nonce, ...options }) {
48
+ return _jsx("script", { nonce: nonce, dangerouslySetInnerHTML: { __html: themeScript(options) } });
49
+ }
50
+ function readStored(name, storage) {
51
+ try {
52
+ if (storage === 'cookie') {
53
+ const m = document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]*)`));
54
+ return m ? m[1] : null;
55
+ }
56
+ return window.localStorage.getItem(name);
57
+ }
58
+ catch {
59
+ return null;
60
+ }
61
+ }
62
+ function writeStored(name, value, storage) {
63
+ try {
64
+ if (storage === 'cookie')
65
+ document.cookie = `${name}=${value}; path=/; max-age=31536000; samesite=lax`;
66
+ else
67
+ window.localStorage.setItem(name, value);
68
+ }
69
+ catch {
70
+ // Refused: the choice holds for this page view and isn't remembered.
71
+ }
72
+ }
73
+ /** The stored theme, or the default when there is none. Browser only. */
74
+ export function readTheme(options) {
75
+ const { storage, key, defaultTheme } = settings(options);
76
+ const stored = readStored(key, storage);
77
+ return PREFERENCES.includes(stored)
78
+ ? stored
79
+ : defaultTheme;
80
+ }
81
+ /** The level `<html>` shows. Browser only. */
82
+ export function readLevel() {
83
+ return document.documentElement.getAttribute('data-conformance') === 'aaa' ? 'aaa' : 'aa';
84
+ }
85
+ /** Whether the page is dark: the theme, with `system` resolved by the colour-scheme media query. */
86
+ export function resolveTheme(theme) {
87
+ if (theme !== 'system')
88
+ return theme;
89
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
90
+ }
91
+ function paint(theme) {
92
+ const dark = resolveTheme(theme) === 'dark';
93
+ const root = document.documentElement;
94
+ root.classList.toggle('dark', dark);
95
+ root.style.colorScheme = dark ? 'dark' : 'light';
96
+ }
97
+ /** Puts a theme on `<html>` and keeps it, as the script will on the next page. Browser only. */
98
+ export function applyTheme(theme, options) {
99
+ const { storage, key } = settings(options);
100
+ paint(theme);
101
+ writeStored(key, theme, storage);
102
+ window.dispatchEvent(new Event(THEME_CHANGE_EVENT));
103
+ }
104
+ /** Puts a conformance level on `<html>` and keeps it. Browser only. */
105
+ export function applyLevel(level, options) {
106
+ const { storage, levelKey } = settings(options);
107
+ document.documentElement.setAttribute('data-conformance', level);
108
+ writeStored(levelKey, level, storage);
109
+ window.dispatchEvent(new Event(THEME_CHANGE_EVENT));
110
+ }
111
+ /**
112
+ * Calls back when the theme or level may have changed: this tab, another tab, or the operating system's
113
+ * colour scheme while the theme is `system`. Browser only.
114
+ */
115
+ export function subscribeTheme(onChange, options) {
116
+ const { storage, levelKey } = settings(options);
117
+ const query = window.matchMedia('(prefers-color-scheme: dark)');
118
+ // The operating system changed scheme, or another tab chose: show it here too.
119
+ const follow = () => {
120
+ paint(readTheme(options));
121
+ const level = readStored(levelKey, storage);
122
+ if (level === 'aa' || level === 'aaa')
123
+ document.documentElement.setAttribute('data-conformance', level);
124
+ onChange();
125
+ };
126
+ window.addEventListener(THEME_CHANGE_EVENT, onChange);
127
+ window.addEventListener('storage', follow);
128
+ query.addEventListener('change', follow);
129
+ return () => {
130
+ window.removeEventListener(THEME_CHANGE_EVENT, onChange);
131
+ window.removeEventListener('storage', follow);
132
+ query.removeEventListener('change', follow);
133
+ };
134
+ }
135
+ //# sourceMappingURL=theme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.js","sourceRoot":"","sources":["../../src/ui/theme.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import { UNSTABLE_ToastQueue as ToastQueue } from 'react-aria-components';
2
+ export type ToastTone = 'success' | 'info' | 'warning' | 'danger';
3
+ export interface ToastContent {
4
+ title: string;
5
+ description?: string;
6
+ tone?: ToastTone;
7
+ /** One action, usually Undo or View. A toast with an action never times out. */
8
+ action?: {
9
+ label: string;
10
+ onAction: () => void;
11
+ };
12
+ }
13
+ export declare const toastQueue: ToastQueue<ToastContent>;
14
+ /**
15
+ * Show a toast. Routine confirmations time out after --toast-timeout (8s at AA, never at AAA),
16
+ * paused while hovered or focused. Errors and toasts with an action stay until dismissed.
17
+ * Returns the key, for toastQueue.close(key).
18
+ */
19
+ export declare function toast(content: ToastContent, options?: {
20
+ timeout?: number | 'persistent';
21
+ }): string;
22
+ /**
23
+ * Render once, near the root. A labelled landmark ("Notifications") at the bottom right — bottom centre
24
+ * on phones; F6 moves focus into it, and each toast is announced when it arrives.
25
+ *
26
+ * Toasts that stay (errors, anything with an action) must not cover the control that has focus. The stack's
27
+ * height is published as --toast-region-height; ShellMain and the page use it as bottom scroll padding (and
28
+ * room to scroll into), so a focused control is scrolled clear of the stack rather than hidden behind it. With
29
+ * the right panel open beside the page, the stack moves left of the panel, whose footer can't scroll.
30
+ */
31
+ export declare function Toaster({ className }: {
32
+ className?: string;
33
+ }): import("react").JSX.Element;
34
+ //# sourceMappingURL=toast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toast.d.ts","sourceRoot":"","sources":["../../src/ui/toast.tsx"],"names":[],"mappings":"AAEA,OAAO,EAIL,mBAAmB,IAAI,UAAU,EAElC,MAAM,uBAAuB,CAAC;AAS/B,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;AAElE,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,gFAAgF;IAChF,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC;CAClD;AAED,eAAO,MAAM,UAAU,0BAAwD,CAAC;AAEhF;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,GAAG,YAAY,CAAA;CAAO,UAQ7F;AA8BD;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,+BA8D5D"}
@@ -0,0 +1,69 @@
1
+ // React Aria's toast is still UNSTABLE_ in react-aria-components: this file is the only place that imports it.
2
+ 'use client';
3
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
4
+ import { Text, UNSTABLE_Toast as RACToast, UNSTABLE_ToastContent as RACToastContent, UNSTABLE_ToastQueue as ToastQueue, UNSTABLE_ToastRegion as ToastRegion, } from 'react-aria-components';
5
+ import { useCallback, useRef } from 'react';
6
+ import { CircleAlert, CircleCheck, Info, TriangleAlert, X } from 'lucide-react';
7
+ import { cn } from '../lib/cn.js';
8
+ import { readMs } from '../lib/tokens.js';
9
+ import { useOptionalShell } from '../shell/dashboard-shell.js';
10
+ import { Button } from './button.js';
11
+ export const toastQueue = new ToastQueue({ maxVisibleToasts: 3 });
12
+ /**
13
+ * Show a toast. Routine confirmations time out after --toast-timeout (8s at AA, never at AAA),
14
+ * paused while hovered or focused. Errors and toasts with an action stay until dismissed.
15
+ * Returns the key, for toastQueue.close(key).
16
+ */
17
+ export function toast(content, options = {}) {
18
+ const persistent = options.timeout === 'persistent' || content.tone === 'danger' || !!content.action;
19
+ const ms = typeof options.timeout === 'number' ? options.timeout : readMs('--toast-timeout', 8000);
20
+ return toastQueue.add(content, {
21
+ timeout: persistent || ms <= 0 ? undefined : Math.max(ms, 5000),
22
+ });
23
+ }
24
+ const tones = {
25
+ success: { icon: 'text-success', Icon: CircleCheck },
26
+ info: { icon: 'text-brand', Icon: Info },
27
+ warning: { icon: 'text-warning', Icon: TriangleAlert },
28
+ danger: { icon: 'text-destructive', Icon: CircleAlert },
29
+ };
30
+ /** Publishes the toast stack's height as --toast-region-height, so scroll padding can keep focus clear of it. */
31
+ function useRegionHeight() {
32
+ const observer = useRef(null);
33
+ return useCallback((el) => {
34
+ const root = document.documentElement;
35
+ observer.current?.disconnect();
36
+ if (!el) {
37
+ root.style.setProperty('--toast-region-height', '0px');
38
+ return;
39
+ }
40
+ const set = () => root.style.setProperty('--toast-region-height', `${Math.ceil(el.getBoundingClientRect().height)}px`);
41
+ set();
42
+ observer.current = new ResizeObserver(set);
43
+ observer.current.observe(el);
44
+ }, []);
45
+ }
46
+ /**
47
+ * Render once, near the root. A labelled landmark ("Notifications") at the bottom right — bottom centre
48
+ * on phones; F6 moves focus into it, and each toast is announced when it arrives.
49
+ *
50
+ * Toasts that stay (errors, anything with an action) must not cover the control that has focus. The stack's
51
+ * height is published as --toast-region-height; ShellMain and the page use it as bottom scroll padding (and
52
+ * room to scroll into), so a focused control is scrolled clear of the stack rather than hidden behind it. With
53
+ * the right panel open beside the page, the stack moves left of the panel, whose footer can't scroll.
54
+ */
55
+ export function Toaster({ className }) {
56
+ const regionRef = useRegionHeight();
57
+ // With the right panel open beside the page, the stack sits left of it: the panel's footer doesn't scroll,
58
+ // so nothing could move its buttons clear of a toast.
59
+ const shell = useOptionalShell();
60
+ const clearOfPanel = !!shell?.panelOpen && shell.panelMode === 'inline';
61
+ return (_jsx(ToastRegion, { ref: regionRef, queue: toastQueue, className: cn('fixed right-4 bottom-4 z-(--z-popover) flex w-[min(24rem,calc(100vw-2rem))] flex-col-reverse gap-2 rounded-lg outline-none data-focus-visible:focus-ring', clearOfPanel && 'right-[calc(var(--panel-width)+1.5rem)]', className), children: ({ toast: t }) => {
62
+ const tone = tones[t.content.tone ?? 'success'];
63
+ return (_jsxs(RACToast, { toast: t, className: "flex items-start gap-3 rounded-lg border border-border bg-popover py-3 pr-2 pl-3 text-sm text-popover-foreground shadow-(--shadow-lg) outline-none data-focus-visible:focus-ring motion-safe:animate-[setsquare-toast_var(--duration-enter)_var(--ease-standard)]", children: [_jsx(tone.Icon, { "aria-hidden": "true", className: cn('mt-px size-5 shrink-0', tone.icon) }), _jsxs(RACToastContent, { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [_jsx(Text, { slot: "title", className: "font-semibold", children: t.content.title }), t.content.description && (_jsx(Text, { slot: "description", className: "text-[13px] leading-5 text-muted-foreground", children: t.content.description })), t.content.action && (_jsx("div", { className: "mt-2", children: _jsx(Button, { variant: "outline", visualSize: "sm", onPress: () => {
64
+ t.content.action.onAction();
65
+ toastQueue.close(t.key);
66
+ }, children: t.content.action.label }) }))] }), _jsx(Button, { slot: "close", "aria-label": "Dismiss", variant: "ghost", visualSize: "icon-sm", className: "-my-0.5 shrink-0", children: _jsx(X, { "aria-hidden": "true" }) })] }));
67
+ } }));
68
+ }
69
+ //# sourceMappingURL=toast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toast.js","sourceRoot":"","sources":["../../src/ui/toast.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { TooltipTrigger, type TooltipProps as RACTooltipProps } from 'react-aria-components';
2
+ import type { ReactNode } from 'react';
3
+ export interface TooltipProps extends Omit<RACTooltipProps, 'className' | 'children'> {
4
+ className?: string;
5
+ children: ReactNode;
6
+ }
7
+ /** Short label shown on hover and keyboard focus; dismissed with Escape. Never the only place information lives. */
8
+ export declare function Tooltip({ className, offset, children, ...props }: TooltipProps): import("react").JSX.Element;
9
+ export { TooltipTrigger };
10
+ //# sourceMappingURL=tooltip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tooltip.d.ts","sourceRoot":"","sources":["../../src/ui/tooltip.tsx"],"names":[],"mappings":"AAEA,OAAO,EAEL,cAAc,EACd,KAAK,YAAY,IAAI,eAAe,EACrC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,MAAM,WAAW,YAAa,SAAQ,IAAI,CAAC,eAAe,EAAE,WAAW,GAAG,UAAU,CAAC;IACnF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,oHAAoH;AACpH,wBAAgB,OAAO,CAAC,EAAE,SAAS,EAAE,MAAU,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,YAAY,+BAalF;AAED,OAAO,EAAE,cAAc,EAAE,CAAC"}
@@ -0,0 +1,10 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { Tooltip as RACTooltip, TooltipTrigger, } from 'react-aria-components';
4
+ import { cn } from '../lib/cn.js';
5
+ /** Short label shown on hover and keyboard focus; dismissed with Escape. Never the only place information lives. */
6
+ export function Tooltip({ className, offset = 6, children, ...props }) {
7
+ return (_jsx(RACTooltip, { ...props, offset: offset, className: cn('z-(--z-popover) max-w-64 rounded-md bg-foreground px-2 py-1 text-[13px] leading-5 font-medium text-background shadow-(--shadow-lg)', className), children: children }));
8
+ }
9
+ export { TooltipTrigger };
10
+ //# sourceMappingURL=tooltip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tooltip.js","sourceRoot":"","sources":["../../src/ui/tooltip.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EACL,OAAO,IAAI,UAAU,EACrB,cAAc,GAEf,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAOlC,oHAAoH;AACpH,MAAM,UAAU,OAAO,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,CAAC,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAgB;IACjF,OAAO,CACL,KAAC,UAAU,OACL,KAAK,EACT,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,EAAE,CACX,oIAAoI,EACpI,SAAS,CACV,YAEA,QAAQ,GACE,CACd,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,cAAc,EAAE,CAAC"}