@kodffe/ui 0.0.0-stage → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (304) hide show
  1. package/dist/_virtual/_rolldown/runtime.js +11 -0
  2. package/dist/accordion/accordion.d.ts +44 -0
  3. package/dist/accordion/accordion.js +84 -0
  4. package/dist/accordion/accordion.js.map +1 -0
  5. package/dist/alert/alert.d.ts +17 -0
  6. package/dist/alert/alert.js +59 -0
  7. package/dist/alert/alert.js.map +1 -0
  8. package/dist/app-shell/app-footer.d.ts +29 -0
  9. package/dist/app-shell/app-footer.js +39 -0
  10. package/dist/app-shell/app-footer.js.map +1 -0
  11. package/dist/app-shell/app-shell-context.d.ts +10 -0
  12. package/dist/app-shell/app-shell-context.js +12 -0
  13. package/dist/app-shell/app-shell-context.js.map +1 -0
  14. package/dist/app-shell/app-shell.d.ts +42 -0
  15. package/dist/app-shell/app-shell.js +191 -0
  16. package/dist/app-shell/app-shell.js.map +1 -0
  17. package/dist/app-shell/floating-actions.d.ts +19 -0
  18. package/dist/app-shell/floating-actions.js +59 -0
  19. package/dist/app-shell/floating-actions.js.map +1 -0
  20. package/dist/app-shell/workspace-switcher.d.ts +25 -0
  21. package/dist/app-shell/workspace-switcher.js +82 -0
  22. package/dist/app-shell/workspace-switcher.js.map +1 -0
  23. package/dist/badge/badge.d.ts +19 -0
  24. package/dist/badge/badge.js +59 -0
  25. package/dist/badge/badge.js.map +1 -0
  26. package/dist/banner/banner.d.ts +22 -0
  27. package/dist/banner/banner.js +80 -0
  28. package/dist/banner/banner.js.map +1 -0
  29. package/dist/breadcrumbs/breadcrumbs.d.ts +18 -0
  30. package/dist/breadcrumbs/breadcrumbs.js +45 -0
  31. package/dist/breadcrumbs/breadcrumbs.js.map +1 -0
  32. package/dist/button/button.d.ts +38 -0
  33. package/dist/button/button.js +83 -0
  34. package/dist/button/button.js.map +1 -0
  35. package/dist/card/card.d.ts +159 -0
  36. package/dist/card/card.js +390 -0
  37. package/dist/card/card.js.map +1 -0
  38. package/dist/card/insight-card.d.ts +19 -0
  39. package/dist/card/insight-card.js +51 -0
  40. package/dist/card/insight-card.js.map +1 -0
  41. package/dist/card/integration-card.d.ts +19 -0
  42. package/dist/card/integration-card.js +70 -0
  43. package/dist/card/integration-card.js.map +1 -0
  44. package/dist/charts/charts.d.ts +75 -0
  45. package/dist/charts/charts.js +453 -0
  46. package/dist/charts/charts.js.map +1 -0
  47. package/dist/choice/choice.d.ts +39 -0
  48. package/dist/choice/choice.js +143 -0
  49. package/dist/choice/choice.js.map +1 -0
  50. package/dist/combobox/combobox.d.ts +46 -0
  51. package/dist/combobox/combobox.js +196 -0
  52. package/dist/combobox/combobox.js.map +1 -0
  53. package/dist/command-palette/command-palette.d.ts +35 -0
  54. package/dist/command-palette/command-palette.js +164 -0
  55. package/dist/command-palette/command-palette.js.map +1 -0
  56. package/dist/confirm-dialog/confirm-dialog.d.ts +37 -0
  57. package/dist/confirm-dialog/confirm-dialog.js +106 -0
  58. package/dist/confirm-dialog/confirm-dialog.js.map +1 -0
  59. package/dist/content/assistant.d.ts +86 -0
  60. package/dist/content/assistant.js +213 -0
  61. package/dist/content/assistant.js.map +1 -0
  62. package/dist/content/content.d.ts +45 -0
  63. package/dist/content/content.js +136 -0
  64. package/dist/content/content.js.map +1 -0
  65. package/dist/data-display/avatar-sizes.d.ts +8 -0
  66. package/dist/data-display/avatar-sizes.js +12 -0
  67. package/dist/data-display/avatar-sizes.js.map +1 -0
  68. package/dist/data-display/avatar.d.ts +21 -0
  69. package/dist/data-display/avatar.js +53 -0
  70. package/dist/data-display/avatar.js.map +1 -0
  71. package/dist/data-display/chip.d.ts +20 -0
  72. package/dist/data-display/chip.js +64 -0
  73. package/dist/data-display/chip.js.map +1 -0
  74. package/dist/data-display/code-block.d.ts +14 -0
  75. package/dist/data-display/code-block.js +50 -0
  76. package/dist/data-display/code-block.js.map +1 -0
  77. package/dist/data-display/data-display.d.ts +73 -0
  78. package/dist/data-display/data-display.js +227 -0
  79. package/dist/data-display/data-display.js.map +1 -0
  80. package/dist/data-display/insights.d.ts +120 -0
  81. package/dist/data-display/insights.js +220 -0
  82. package/dist/data-display/insights.js.map +1 -0
  83. package/dist/data-table/cells.d.ts +86 -0
  84. package/dist/data-table/cells.js +163 -0
  85. package/dist/data-table/cells.js.map +1 -0
  86. package/dist/data-table/data-table.d.ts +112 -0
  87. package/dist/data-table/data-table.js +558 -0
  88. package/dist/data-table/data-table.js.map +1 -0
  89. package/dist/date-picker/date-picker.d.ts +61 -0
  90. package/dist/date-picker/date-picker.js +309 -0
  91. package/dist/date-picker/date-picker.js.map +1 -0
  92. package/dist/drawer/drawer.d.ts +32 -0
  93. package/dist/drawer/drawer.js +72 -0
  94. package/dist/drawer/drawer.js.map +1 -0
  95. package/dist/empty-state/empty-state.d.ts +25 -0
  96. package/dist/empty-state/empty-state.js +78 -0
  97. package/dist/empty-state/empty-state.js.map +1 -0
  98. package/dist/error-state/error-state.d.ts +19 -0
  99. package/dist/error-state/error-state.js +56 -0
  100. package/dist/error-state/error-state.js.map +1 -0
  101. package/dist/event-calendar/calendar-logic.test.d.ts +1 -0
  102. package/dist/event-calendar/dates.d.ts +35 -0
  103. package/dist/event-calendar/dates.js +55 -0
  104. package/dist/event-calendar/dates.js.map +1 -0
  105. package/dist/event-calendar/event-calendar.d.ts +125 -0
  106. package/dist/event-calendar/event-calendar.js +795 -0
  107. package/dist/event-calendar/event-calendar.js.map +1 -0
  108. package/dist/event-calendar/event-details.d.ts +50 -0
  109. package/dist/event-calendar/event-details.js +124 -0
  110. package/dist/event-calendar/event-details.js.map +1 -0
  111. package/dist/event-calendar/layout.d.ts +48 -0
  112. package/dist/event-calendar/layout.js +55 -0
  113. package/dist/event-calendar/layout.js.map +1 -0
  114. package/dist/file-upload/file-upload.d.ts +70 -0
  115. package/dist/file-upload/file-upload.js +307 -0
  116. package/dist/file-upload/file-upload.js.map +1 -0
  117. package/dist/filters/filters.d.ts +48 -0
  118. package/dist/filters/filters.js +103 -0
  119. package/dist/filters/filters.js.map +1 -0
  120. package/dist/form-field/form-field.d.ts +39 -0
  121. package/dist/form-field/form-field.js +88 -0
  122. package/dist/form-field/form-field.js.map +1 -0
  123. package/dist/hover-card/hover-card.d.ts +15 -0
  124. package/dist/hover-card/hover-card.js +27 -0
  125. package/dist/hover-card/hover-card.js.map +1 -0
  126. package/dist/hover-card/person-hover-card.d.ts +26 -0
  127. package/dist/hover-card/person-hover-card.js +85 -0
  128. package/dist/hover-card/person-hover-card.js.map +1 -0
  129. package/dist/icon/icon-tile.d.ts +20 -0
  130. package/dist/icon/icon-tile.js +55 -0
  131. package/dist/icon/icon-tile.js.map +1 -0
  132. package/dist/icon/icon.d.ts +14 -0
  133. package/dist/icon/icon.js +28 -0
  134. package/dist/icon/icon.js.map +1 -0
  135. package/dist/icon/icons.generated.d.ts +358 -0
  136. package/dist/icon/icons.generated.js +362 -0
  137. package/dist/icon/icons.generated.js.map +1 -0
  138. package/dist/index.d.ts +90 -0
  139. package/dist/index.js +88 -0
  140. package/dist/input/extra-inputs.d.ts +122 -0
  141. package/dist/input/extra-inputs.js +351 -0
  142. package/dist/input/extra-inputs.js.map +1 -0
  143. package/dist/input/field-styles.d.ts +5 -0
  144. package/dist/input/field-styles.js +14 -0
  145. package/dist/input/field-styles.js.map +1 -0
  146. package/dist/input/input.d.ts +50 -0
  147. package/dist/input/input.js +107 -0
  148. package/dist/input/input.js.map +1 -0
  149. package/dist/layout/layout.d.ts +41 -0
  150. package/dist/layout/layout.js +90 -0
  151. package/dist/layout/layout.js.map +1 -0
  152. package/dist/lib/control-styles.d.ts +9 -0
  153. package/dist/lib/control-styles.js +16 -0
  154. package/dist/lib/control-styles.js.map +1 -0
  155. package/dist/lib/cx.d.ts +2 -0
  156. package/dist/lib/cx.js +8 -0
  157. package/dist/lib/cx.js.map +1 -0
  158. package/dist/lib/glyphs.d.ts +13 -0
  159. package/dist/lib/glyphs.js +67 -0
  160. package/dist/lib/glyphs.js.map +1 -0
  161. package/dist/lib/segmented-styles.d.ts +8 -0
  162. package/dist/lib/segmented-styles.js +12 -0
  163. package/dist/lib/segmented-styles.js.map +1 -0
  164. package/dist/lib/select-on-arrow.d.ts +7 -0
  165. package/dist/lib/select-on-arrow.js +21 -0
  166. package/dist/lib/select-on-arrow.js.map +1 -0
  167. package/dist/lib/skip-link.d.ts +5 -0
  168. package/dist/lib/skip-link.js +13 -0
  169. package/dist/lib/skip-link.js.map +1 -0
  170. package/dist/lib/use-media-query.d.ts +4 -0
  171. package/dist/lib/use-media-query.js +15 -0
  172. package/dist/lib/use-media-query.js.map +1 -0
  173. package/dist/lists/lists.d.ts +76 -0
  174. package/dist/lists/lists.js +133 -0
  175. package/dist/lists/lists.js.map +1 -0
  176. package/dist/locale-switch/locale-switch.d.ts +29 -0
  177. package/dist/locale-switch/locale-switch.js +62 -0
  178. package/dist/locale-switch/locale-switch.js.map +1 -0
  179. package/dist/logo/logo.d.ts +31 -0
  180. package/dist/logo/logo.js +157 -0
  181. package/dist/logo/logo.js.map +1 -0
  182. package/dist/logo/product-logo.d.ts +19 -0
  183. package/dist/logo/product-logo.js +46 -0
  184. package/dist/logo/product-logo.js.map +1 -0
  185. package/dist/marketing/marketing.d.ts +176 -0
  186. package/dist/marketing/marketing.js +451 -0
  187. package/dist/marketing/marketing.js.map +1 -0
  188. package/dist/marketing/newsletter-form.d.ts +21 -0
  189. package/dist/marketing/newsletter-form.js +100 -0
  190. package/dist/marketing/newsletter-form.js.map +1 -0
  191. package/dist/mascot/mascot.d.ts +28 -0
  192. package/dist/mascot/mascot.js +28 -0
  193. package/dist/mascot/mascot.js.map +1 -0
  194. package/dist/mascot/mascots.generated.d.ts +284 -0
  195. package/dist/mascot/mascots.generated.js +287 -0
  196. package/dist/mascot/mascots.generated.js.map +1 -0
  197. package/dist/master-detail/master-detail.d.ts +30 -0
  198. package/dist/master-detail/master-detail.js +71 -0
  199. package/dist/master-detail/master-detail.js.map +1 -0
  200. package/dist/menu/account-menu.d.ts +44 -0
  201. package/dist/menu/account-menu.js +78 -0
  202. package/dist/menu/account-menu.js.map +1 -0
  203. package/dist/menu/context-menu.d.ts +31 -0
  204. package/dist/menu/context-menu.js +111 -0
  205. package/dist/menu/context-menu.js.map +1 -0
  206. package/dist/menu/dropdown-menu.d.ts +49 -0
  207. package/dist/menu/dropdown-menu.js +152 -0
  208. package/dist/menu/dropdown-menu.js.map +1 -0
  209. package/dist/menu/menu-styles.d.ts +8 -0
  210. package/dist/menu/menu-styles.js +6 -0
  211. package/dist/menu/menu-styles.js.map +1 -0
  212. package/dist/menu/menubar.d.ts +31 -0
  213. package/dist/menu/menubar.js +125 -0
  214. package/dist/menu/menubar.js.map +1 -0
  215. package/dist/modal/modal.d.ts +34 -0
  216. package/dist/modal/modal.js +58 -0
  217. package/dist/modal/modal.js.map +1 -0
  218. package/dist/notification-center/notification-center.d.ts +51 -0
  219. package/dist/notification-center/notification-center.js +244 -0
  220. package/dist/notification-center/notification-center.js.map +1 -0
  221. package/dist/otp-input/otp-input.d.ts +23 -0
  222. package/dist/otp-input/otp-input.js +61 -0
  223. package/dist/otp-input/otp-input.js.map +1 -0
  224. package/dist/overlay/overlay.d.ts +53 -0
  225. package/dist/overlay/overlay.js +103 -0
  226. package/dist/overlay/overlay.js.map +1 -0
  227. package/dist/page-header/page-header.d.ts +55 -0
  228. package/dist/page-header/page-header.js +114 -0
  229. package/dist/page-header/page-header.js.map +1 -0
  230. package/dist/page-header/record-headers.d.ts +51 -0
  231. package/dist/page-header/record-headers.js +134 -0
  232. package/dist/page-header/record-headers.js.map +1 -0
  233. package/dist/page-header/section-header.d.ts +21 -0
  234. package/dist/page-header/section-header.js +44 -0
  235. package/dist/page-header/section-header.js.map +1 -0
  236. package/dist/page-navigation/page-navigation.d.ts +59 -0
  237. package/dist/page-navigation/page-navigation.js +136 -0
  238. package/dist/page-navigation/page-navigation.js.map +1 -0
  239. package/dist/pagination/pagination.d.ts +28 -0
  240. package/dist/pagination/pagination.js +102 -0
  241. package/dist/pagination/pagination.js.map +1 -0
  242. package/dist/phone-input/phone-input.d.ts +34 -0
  243. package/dist/phone-input/phone-input.js +171 -0
  244. package/dist/phone-input/phone-input.js.map +1 -0
  245. package/dist/popover/popover.d.ts +23 -0
  246. package/dist/popover/popover.js +40 -0
  247. package/dist/popover/popover.js.map +1 -0
  248. package/dist/progress/progress.d.ts +21 -0
  249. package/dist/progress/progress.js +49 -0
  250. package/dist/progress/progress.js.map +1 -0
  251. package/dist/segmented-control/segmented-control.d.ts +32 -0
  252. package/dist/segmented-control/segmented-control.js +29 -0
  253. package/dist/segmented-control/segmented-control.js.map +1 -0
  254. package/dist/select/select.d.ts +48 -0
  255. package/dist/select/select.js +136 -0
  256. package/dist/select/select.js.map +1 -0
  257. package/dist/settings/permission-matrix.d.ts +29 -0
  258. package/dist/settings/permission-matrix.js +101 -0
  259. package/dist/settings/permission-matrix.js.map +1 -0
  260. package/dist/settings/settings-row.d.ts +32 -0
  261. package/dist/settings/settings-row.js +63 -0
  262. package/dist/settings/settings-row.js.map +1 -0
  263. package/dist/side-nav/side-nav.d.ts +36 -0
  264. package/dist/side-nav/side-nav.js +123 -0
  265. package/dist/side-nav/side-nav.js.map +1 -0
  266. package/dist/site-header/site-header.d.ts +32 -0
  267. package/dist/site-header/site-header.js +99 -0
  268. package/dist/site-header/site-header.js.map +1 -0
  269. package/dist/skeleton/skeleton.d.ts +16 -0
  270. package/dist/skeleton/skeleton.js +19 -0
  271. package/dist/skeleton/skeleton.js.map +1 -0
  272. package/dist/slider/slider.d.ts +15 -0
  273. package/dist/slider/slider.js +48 -0
  274. package/dist/slider/slider.js.map +1 -0
  275. package/dist/spinner/spinner.d.ts +15 -0
  276. package/dist/spinner/spinner.js +42 -0
  277. package/dist/spinner/spinner.js.map +1 -0
  278. package/dist/stepper/step-dots.d.ts +16 -0
  279. package/dist/stepper/step-dots.js +26 -0
  280. package/dist/stepper/step-dots.js.map +1 -0
  281. package/dist/stepper/stepper.d.ts +26 -0
  282. package/dist/stepper/stepper.js +113 -0
  283. package/dist/stepper/stepper.js.map +1 -0
  284. package/dist/tabs/tabs.d.ts +31 -0
  285. package/dist/tabs/tabs.js +66 -0
  286. package/dist/tabs/tabs.js.map +1 -0
  287. package/dist/templates/app-templates.d.ts +126 -0
  288. package/dist/templates/app-templates.js +153 -0
  289. package/dist/templates/app-templates.js.map +1 -0
  290. package/dist/templates/site-templates.d.ts +49 -0
  291. package/dist/templates/site-templates.js +110 -0
  292. package/dist/templates/site-templates.js.map +1 -0
  293. package/dist/toast/toast-store.d.ts +54 -0
  294. package/dist/toast/toast-store.js +49 -0
  295. package/dist/toast/toast-store.js.map +1 -0
  296. package/dist/toast/toaster.d.ts +15 -0
  297. package/dist/toast/toaster.js +124 -0
  298. package/dist/toast/toaster.js.map +1 -0
  299. package/dist/tooltip/tooltip.d.ts +29 -0
  300. package/dist/tooltip/tooltip.js +54 -0
  301. package/dist/tooltip/tooltip.js.map +1 -0
  302. package/package.json +88 -3
  303. package/styles.css +521 -0
  304. package/README.md +0 -3
@@ -0,0 +1,14 @@
1
+ import { cx as e } from "../lib/cx.js";
2
+ //#region src/input/field-styles.ts
3
+ var t = {
4
+ sm: "min-h-9 text-base sm:text-sm",
5
+ md: "min-h-11 text-base sm:text-[15px]",
6
+ lg: "min-h-13 text-base sm:text-[17px]"
7
+ };
8
+ function n(n, r, i) {
9
+ return e("w-full rounded-sm border bg-bg font-sans text-fg transition-[border-color,box-shadow] duration-150", "placeholder:text-fg-muted/80", r ? "border-danger-border" : "border-border hover:border-fg-muted/50", "focus-within:border-primary focus-within:shadow-[0_0_0_3px_color-mix(in_oklab,var(--color-focus),transparent_70%)]", "has-[:disabled]:cursor-not-allowed has-[:disabled]:bg-surface-muted has-[:disabled]:opacity-70", "aria-disabled:cursor-not-allowed aria-disabled:bg-surface-muted aria-disabled:opacity-70", t[n], i);
10
+ }
11
+ //#endregion
12
+ export { t as CONTROL_SIZES, n as controlClasses };
13
+
14
+ //# sourceMappingURL=field-styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-styles.js","names":["CONTROL_SIZES","controlClasses","cx","invalid","size","extra"],"sources":["../../src/input/field-styles.ts"],"sourcesContent":["import { cx } from '../lib/cx';\n\nexport type ControlSize = 'sm' | 'md' | 'lg';\n\n/** Heights: 36 / 44 / 52px. Text is 16px below `sm` so iOS does not zoom on focus. */\nexport const CONTROL_SIZES: Record<ControlSize, string> = {\n sm: 'min-h-9 text-base sm:text-sm',\n md: 'min-h-11 text-base sm:text-[15px]',\n lg: 'min-h-13 text-base sm:text-[17px]',\n};\n\n/** Shared look for text-like controls (Input wrapper, Textarea, Select trigger, Combobox). */\nexport function controlClasses(size: ControlSize, invalid?: boolean, extra?: string) {\n return cx(\n 'w-full rounded-sm border bg-bg font-sans text-fg transition-[border-color,box-shadow] duration-150',\n 'placeholder:text-fg-muted/80',\n invalid ? 'border-danger-border' : 'border-border hover:border-fg-muted/50',\n 'focus-within:border-primary focus-within:shadow-[0_0_0_3px_color-mix(in_oklab,var(--color-focus),transparent_70%)]',\n 'has-[:disabled]:cursor-not-allowed has-[:disabled]:bg-surface-muted has-[:disabled]:opacity-70',\n 'aria-disabled:cursor-not-allowed aria-disabled:bg-surface-muted aria-disabled:opacity-70',\n CONTROL_SIZES[size],\n extra,\n );\n}\n"],"mappings":";;AAKA,IAAaA,IAA6C;CACxD,IAAI;CACJ,IAAI;CACJ,IAAI;AACN;AAGA,SAAgBC,EAAe,GAAmB,GAAmB,GAAgB;CACnF,OAAOC,EACL,sGACA,gCACAC,IAAU,yBAAyB,0CACnC,sHACA,kGACA,4FACAH,EAAcI,IACdC,CACF;AACF"}
@@ -0,0 +1,50 @@
1
+ import { type InputHTMLAttributes, type ReactNode, type TextareaHTMLAttributes } from 'react';
2
+ import { type IconName } from '../icon/icon';
3
+ import { type ControlSize } from './field-styles';
4
+ /** Props every field control accepts, merged with the surrounding FormField. */
5
+ export declare function useFieldProps(props: {
6
+ id?: string;
7
+ disabled?: boolean;
8
+ required?: boolean;
9
+ invalid?: boolean;
10
+ 'aria-describedby'?: string;
11
+ }): {
12
+ id: string | undefined;
13
+ disabled: boolean | undefined;
14
+ required: boolean | undefined;
15
+ 'aria-invalid': true | undefined;
16
+ 'aria-describedby': string | undefined;
17
+ invalid: boolean;
18
+ };
19
+ export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size' | 'prefix'> {
20
+ size?: ControlSize;
21
+ iconStart?: IconName;
22
+ iconEnd?: IconName;
23
+ /** Static text before the value ("https://", "$"). */
24
+ prefix?: ReactNode;
25
+ /** Static text after the value ("USD", "@acme.com") or a small button. */
26
+ suffix?: ReactNode;
27
+ /** Error styling without a FormField. */
28
+ invalid?: boolean;
29
+ /** Shows "12/80" when `maxLength` is set. */
30
+ showCount?: boolean;
31
+ }
32
+ /** Text input with optional icons, affixes and character count. Use inside `FormField`. */
33
+ export declare function Input({ size, iconStart, iconEnd, prefix, suffix, invalid: invalidProp, showCount, className, onChange, ...rest }: InputProps): import("react").JSX.Element;
34
+ export interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
35
+ invalid?: boolean;
36
+ showCount?: boolean;
37
+ /** Grow with the content (CSS `field-sizing`), between `rows` and `maxRows`. */
38
+ autoResize?: boolean;
39
+ maxRows?: number;
40
+ }
41
+ export declare function Textarea({ invalid: invalidProp, showCount, autoResize, maxRows, rows, className, onChange, style, ...rest }: TextareaProps): import("react").JSX.Element;
42
+ export interface DateFieldProps extends Omit<InputProps, 'type' | 'iconStart' | 'prefix'> {
43
+ /** `date`, `datetime-local`, `time` or `month`. */
44
+ type?: 'date' | 'datetime-local' | 'time' | 'month';
45
+ }
46
+ /**
47
+ * Native date/time input styled like the other fields: the OS picker on phones, the browser
48
+ * picker on desktop. For rules, ranges and presets use DatePicker / DateRangePicker.
49
+ */
50
+ export declare function DateField({ type, ...rest }: DateFieldProps): import("react").JSX.Element;
@@ -0,0 +1,107 @@
1
+ "use client";
2
+ import { cx as e } from "../lib/cx.js";
3
+ import { Icon as t } from "../icon/icon.js";
4
+ import { useFormField as n } from "../form-field/form-field.js";
5
+ import { controlClasses as r } from "./field-styles.js";
6
+ import { jsx as i, jsxs as a } from "react/jsx-runtime";
7
+ import { useState as o } from "react";
8
+ //#region src/input/input.tsx
9
+ function s(e) {
10
+ let t = n(), r = e.invalid ?? t?.invalid ?? !1;
11
+ return {
12
+ id: e.id ?? t?.id,
13
+ disabled: e.disabled ?? t?.disabled,
14
+ required: e.required ?? t?.required,
15
+ "aria-invalid": r || void 0,
16
+ "aria-describedby": [t?.describedBy, e["aria-describedby"]].filter(Boolean).join(" ") || void 0,
17
+ invalid: r
18
+ };
19
+ }
20
+ function c({ size: n = "md", iconStart: c, iconEnd: l, prefix: u, suffix: d, invalid: f, showCount: p, className: m, onChange: h, ...g }) {
21
+ let { invalid: _, ...v } = s({
22
+ ...g,
23
+ invalid: f
24
+ }), [y, b] = o(String(g.value ?? g.defaultValue ?? "").length), x = p && g.maxLength !== void 0, S = "shrink-0 text-fg-muted select-none";
25
+ return /* @__PURE__ */ a("div", {
26
+ className: e("relative flex items-center gap-2 px-3", r(n, _), m),
27
+ children: [
28
+ c && /* @__PURE__ */ i(t, {
29
+ name: c,
30
+ size: 20,
31
+ className: S
32
+ }),
33
+ u && /* @__PURE__ */ i("span", {
34
+ className: S,
35
+ children: u
36
+ }),
37
+ /* @__PURE__ */ i("input", {
38
+ ...g,
39
+ ...v,
40
+ onChange: (e) => {
41
+ b(e.target.value.length), h?.(e);
42
+ },
43
+ className: "min-w-0 flex-1 self-stretch border-0 bg-transparent p-0 font-[inherit] text-[length:inherit] text-fg outline-none placeholder:text-fg-muted/80 disabled:cursor-not-allowed"
44
+ }),
45
+ d && /* @__PURE__ */ i("span", {
46
+ className: S,
47
+ children: d
48
+ }),
49
+ l && /* @__PURE__ */ i(t, {
50
+ name: l,
51
+ size: 20,
52
+ className: S
53
+ }),
54
+ x && /* @__PURE__ */ a("span", {
55
+ className: e("shrink-0 text-xs tabular-nums", y >= g.maxLength ? "text-danger-fg" : "text-fg-muted"),
56
+ "aria-live": "polite",
57
+ children: [
58
+ y,
59
+ "/",
60
+ g.maxLength
61
+ ]
62
+ })
63
+ ]
64
+ });
65
+ }
66
+ function l({ invalid: t, showCount: n, autoResize: c, maxRows: l = 12, rows: u = 3, className: d, onChange: f, style: p, ...m }) {
67
+ let { invalid: h, ...g } = s({
68
+ ...m,
69
+ invalid: t
70
+ }), [_, v] = o(String(m.value ?? m.defaultValue ?? "").length);
71
+ return /* @__PURE__ */ a("div", {
72
+ className: "grid gap-1",
73
+ children: [/* @__PURE__ */ i("textarea", {
74
+ rows: u,
75
+ ...m,
76
+ ...g,
77
+ onChange: (e) => {
78
+ v(e.target.value.length), f?.(e);
79
+ },
80
+ style: c ? {
81
+ minHeight: `${u * 1.5 + 1.25}em`,
82
+ maxHeight: `${l * 1.5 + 1.25}em`,
83
+ ...p
84
+ } : p,
85
+ className: e(r("md", h), "block resize-y px-3 py-2.5 leading-normal outline-none focus:border-primary focus:shadow-[0_0_0_3px_color-mix(in_oklab,var(--color-focus),transparent_70%)] disabled:cursor-not-allowed", c && "field-sizing-content resize-none", d)
86
+ }), n && m.maxLength !== void 0 && /* @__PURE__ */ a("span", {
87
+ className: e("justify-self-end text-xs tabular-nums", _ >= m.maxLength ? "text-danger-fg" : "text-fg-muted"),
88
+ "aria-live": "polite",
89
+ children: [
90
+ _,
91
+ "/",
92
+ m.maxLength
93
+ ]
94
+ })]
95
+ });
96
+ }
97
+ function u({ type: e = "date", ...t }) {
98
+ return /* @__PURE__ */ i(c, {
99
+ type: e,
100
+ iconStart: e === "time" ? "time" : "c-calendar",
101
+ ...t
102
+ });
103
+ }
104
+ //#endregion
105
+ export { u as DateField, c as Input, l as Textarea, s as useFieldProps };
106
+
107
+ //# sourceMappingURL=input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input.js","names":["useFieldProps","field","useFormField","invalid","props","Input","invalidProp","rest","length","setLength","useState","count","showCount","affix","cx","controlClasses","size","className","iconStart","Icon","prefix","onChange","suffix","iconEnd","Textarea","autoResize","rows","maxRows","style","DateField","type"],"sources":["../../src/input/input.tsx"],"sourcesContent":["'use client';\n\nimport { useState, type InputHTMLAttributes, type ReactNode, type TextareaHTMLAttributes } from 'react';\nimport { cx } from '../lib/cx';\nimport { Icon, type IconName } from '../icon/icon';\nimport { useFormField } from '../form-field/form-field';\nimport { controlClasses, type ControlSize } from './field-styles';\n\n/** Props every field control accepts, merged with the surrounding FormField. */\nexport function useFieldProps(props: {\n id?: string;\n disabled?: boolean;\n required?: boolean;\n invalid?: boolean;\n 'aria-describedby'?: string;\n}) {\n const field = useFormField();\n const invalid = props.invalid ?? field?.invalid ?? false;\n return {\n id: props.id ?? field?.id,\n disabled: props.disabled ?? field?.disabled,\n required: props.required ?? field?.required,\n 'aria-invalid': invalid || undefined,\n 'aria-describedby': [field?.describedBy, props['aria-describedby']].filter(Boolean).join(' ') || undefined,\n invalid,\n };\n}\n\nexport interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size' | 'prefix'> {\n size?: ControlSize;\n iconStart?: IconName;\n iconEnd?: IconName;\n /** Static text before the value (\"https://\", \"$\"). */\n prefix?: ReactNode;\n /** Static text after the value (\"USD\", \"@acme.com\") or a small button. */\n suffix?: ReactNode;\n /** Error styling without a FormField. */\n invalid?: boolean;\n /** Shows \"12/80\" when `maxLength` is set. */\n showCount?: boolean;\n}\n\n/** Text input with optional icons, affixes and character count. Use inside `FormField`. */\nexport function Input({\n size = 'md',\n iconStart,\n iconEnd,\n prefix,\n suffix,\n invalid: invalidProp,\n showCount,\n className,\n onChange,\n ...rest\n}: InputProps) {\n const { invalid, ...field } = useFieldProps({ ...rest, invalid: invalidProp });\n const [length, setLength] = useState(String(rest.value ?? rest.defaultValue ?? '').length);\n const count = showCount && rest.maxLength !== undefined;\n const affix = 'shrink-0 text-fg-muted select-none';\n return (\n <div className={cx('relative flex items-center gap-2 px-3', controlClasses(size, invalid), className)}>\n {iconStart && <Icon name={iconStart} size={20} className={affix} />}\n {prefix && <span className={affix}>{prefix}</span>}\n <input\n {...rest}\n {...field}\n onChange={(e) => {\n setLength(e.target.value.length);\n onChange?.(e);\n }}\n className=\"min-w-0 flex-1 self-stretch border-0 bg-transparent p-0 font-[inherit] text-[length:inherit] text-fg outline-none placeholder:text-fg-muted/80 disabled:cursor-not-allowed\"\n />\n {suffix && <span className={affix}>{suffix}</span>}\n {iconEnd && <Icon name={iconEnd} size={20} className={affix} />}\n {count && (\n <span\n className={cx(\n 'shrink-0 text-xs tabular-nums',\n length >= rest.maxLength! ? 'text-danger-fg' : 'text-fg-muted',\n )}\n aria-live=\"polite\"\n >\n {length}/{rest.maxLength}\n </span>\n )}\n </div>\n );\n}\n\nexport interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {\n invalid?: boolean;\n showCount?: boolean;\n /** Grow with the content (CSS `field-sizing`), between `rows` and `maxRows`. */\n autoResize?: boolean;\n maxRows?: number;\n}\n\nexport function Textarea({\n invalid: invalidProp,\n showCount,\n autoResize,\n maxRows = 12,\n rows = 3,\n className,\n onChange,\n style,\n ...rest\n}: TextareaProps) {\n const { invalid, ...field } = useFieldProps({ ...rest, invalid: invalidProp });\n const [length, setLength] = useState(String(rest.value ?? rest.defaultValue ?? '').length);\n return (\n <div className=\"grid gap-1\">\n <textarea\n rows={rows}\n {...rest}\n {...field}\n onChange={(e) => {\n setLength(e.target.value.length);\n onChange?.(e);\n }}\n style={\n autoResize ? { minHeight: `${rows * 1.5 + 1.25}em`, maxHeight: `${maxRows * 1.5 + 1.25}em`, ...style } : style\n }\n className={cx(\n controlClasses('md', invalid),\n 'block resize-y px-3 py-2.5 leading-normal outline-none focus:border-primary focus:shadow-[0_0_0_3px_color-mix(in_oklab,var(--color-focus),transparent_70%)] disabled:cursor-not-allowed',\n autoResize && 'field-sizing-content resize-none',\n className,\n )}\n />\n {showCount && rest.maxLength !== undefined && (\n <span\n className={cx(\n 'justify-self-end text-xs tabular-nums',\n length >= rest.maxLength ? 'text-danger-fg' : 'text-fg-muted',\n )}\n aria-live=\"polite\"\n >\n {length}/{rest.maxLength}\n </span>\n )}\n </div>\n );\n}\n\nexport interface DateFieldProps extends Omit<InputProps, 'type' | 'iconStart' | 'prefix'> {\n /** `date`, `datetime-local`, `time` or `month`. */\n type?: 'date' | 'datetime-local' | 'time' | 'month';\n}\n\n/**\n * Native date/time input styled like the other fields: the OS picker on phones, the browser\n * picker on desktop. For rules, ranges and presets use DatePicker / DateRangePicker.\n */\nexport function DateField({ type = 'date', ...rest }: DateFieldProps) {\n return <Input type={type} iconStart={type === 'time' ? 'time' : 'c-calendar'} {...rest} />;\n}\n"],"mappings":";;;;;;;;AASA,SAAgBA,EAAc,GAM3B;CACD,IAAMC,IAAQC,EAAa,GACrBC,IAAUC,EAAM,WAAWH,GAAO,WAAW;CACnD,OAAO;EACL,IAAIG,EAAM,MAAMH,GAAO;EACvB,UAAUG,EAAM,YAAYH,GAAO;EACnC,UAAUG,EAAM,YAAYH,GAAO;EACnC,gBAAgBE,KAAW,KAAA;EAC3B,oBAAoB,CAACF,GAAO,aAAaG,EAAM,mBAAmB,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG,KAAK,KAAA;EACjG;CACF;AACF;AAiBA,SAAgBC,EAAM,EACpB,UAAO,MACP,cACA,YACA,WACA,WACA,SAASC,GACT,cACA,cACA,aACA,GAAGC,KACU;CACb,IAAM,EAAE,YAAS,GAAGN,MAAUD,EAAc;EAAE,GAAGO;EAAM,SAASD;CAAY,CAAC,GACvE,CAACE,GAAQC,KAAaC,EAAS,OAAOH,EAAK,SAASA,EAAK,gBAAgB,EAAE,CAAC,CAAC,MAAM,GACnFI,IAAQC,KAAaL,EAAK,cAAc,KAAA,GACxCM,IAAQ;CACd,OACE,kBAAC,OAAD;EAAK,WAAWC,EAAG,yCAAyCC,EAAeC,GAAMb,CAAO,GAAGc,CAAS;EAApG,UAAA;GACGC,KAAa,kBAACC,GAAD;IAAM,MAAMD;IAAW,MAAM;IAAI,WAAWL;GAAQ,CAAA;GACjEO,KAAU,kBAAC,QAAD;IAAM,WAAWP;IAAQ,UAAAO;GAAa,CAAA;GACjD,kBAAC,SAAD;IACE,GAAIb;IACJ,GAAIN;IACJ,WAAW,MAAM;KAEf,AADAQ,EAAU,EAAE,OAAO,MAAM,MAAM,GAC/BY,IAAW,CAAC;IACd;IACA,WAAU;GACX,CAAA;GACAC,KAAU,kBAAC,QAAD;IAAM,WAAWT;IAAQ,UAAAS;GAAa,CAAA;GAChDC,KAAW,kBAACJ,GAAD;IAAM,MAAMI;IAAS,MAAM;IAAI,WAAWV;GAAQ,CAAA;GAC7DF,KACC,kBAAC,QAAD;IACE,WAAWG,EACT,iCACAN,KAAUD,EAAK,YAAa,mBAAmB,eACjD;IACA,aAAU;IALZ,UAAA;KAOGC;KAAO;KAAED,EAAK;IACX;;EAEL;;AAET;AAUA,SAAgBiB,EAAS,EACvB,SAASlB,GACT,cACA,eACA,aAAU,IACV,UAAO,GACP,cACA,aACA,UACA,GAAGC,KACa;CAChB,IAAM,EAAE,YAAS,GAAGN,MAAUD,EAAc;EAAE,GAAGO;EAAM,SAASD;CAAY,CAAC,GACvE,CAACE,GAAQC,KAAaC,EAAS,OAAOH,EAAK,SAASA,EAAK,gBAAgB,EAAE,CAAC,CAAC,MAAM;CACzF,OACE,kBAAC,OAAD;EAAK,WAAU;EAAf,UAAA,CACE,kBAAC,YAAD;GACQ;GACN,GAAIA;GACJ,GAAIN;GACJ,WAAW,MAAM;IAEf,AADAQ,EAAU,EAAE,OAAO,MAAM,MAAM,GAC/BY,IAAW,CAAC;GACd;GACA,OACEI,IAAa;IAAE,WAAW,GAAGC,IAAO,MAAM,KAAK;IAAK,WAAW,GAAGC,IAAU,MAAM,KAAK;IAAK,GAAGC;GAAM,IAAIA;GAE3G,WAAWd,EACTC,EAAe,MAAMZ,CAAO,GAC5B,2LACAsB,KAAc,oCACdR,CACF;EACD,CAAA,GACAL,KAAaL,EAAK,cAAc,KAAA,KAC/B,kBAAC,QAAD;GACE,WAAWO,EACT,yCACAN,KAAUD,EAAK,YAAY,mBAAmB,eAChD;GACA,aAAU;GALZ,UAAA;IAOGC;IAAO;IAAED,EAAK;GACX;EAEL,CAAA,CAAA;;AAET;AAWA,SAAgBsB,EAAU,EAAE,UAAO,QAAQ,GAAGtB,KAAwB;CACpE,OAAO,kBAACF,GAAD;EAAa;EAAM,WAAWyB,MAAS,SAAS,SAAS;EAAc,GAAIvB;CAAO,CAAA;AAC3F"}
@@ -0,0 +1,41 @@
1
+ import type { ElementType, HTMLAttributes, ReactNode } from 'react';
2
+ type Space = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 8 | 10 | 12 | 16;
3
+ interface BoxProps extends HTMLAttributes<HTMLElement> {
4
+ /** Element to render (e.g. `section`, `ul`, `main`). */
5
+ as?: ElementType;
6
+ children?: ReactNode;
7
+ }
8
+ export type ContainerSize = 'sm' | 'md' | 'lg' | 'xl' | 'full';
9
+ export interface ContainerProps extends BoxProps {
10
+ /** sm 640 (reading), md 960 (forms, docs), lg 1200 (site default), xl 1440 (dashboards). */
11
+ size?: ContainerSize;
12
+ }
13
+ /** Centers content with responsive side padding (16px mobile, 24px from `sm`, 32px from `md`). */
14
+ export declare function Container({ as: Tag, size, className, ...rest }: ContainerProps): import("react").JSX.Element;
15
+ export interface StackProps extends BoxProps {
16
+ direction?: 'column' | 'row';
17
+ /** Switch from column to row at a breakpoint (`sm` 640, `md` 960). */
18
+ rowFrom?: 'sm' | 'md';
19
+ gap?: Space;
20
+ align?: 'start' | 'center' | 'end' | 'stretch' | 'baseline';
21
+ justify?: 'start' | 'center' | 'end' | 'between';
22
+ wrap?: boolean;
23
+ }
24
+ /** One-dimensional layout with consistent gaps. */
25
+ export declare function Stack({ as: Tag, direction, rowFrom, gap, align, justify, wrap, className, ...rest }: StackProps): import("react").JSX.Element;
26
+ export interface GridProps extends BoxProps {
27
+ /** Fixed columns from `md` (1 column on mobile, 2 from `sm` when cols ≥ 3). */
28
+ cols?: 1 | 2 | 3 | 4 | 6 | 12;
29
+ /** Responsive auto-fit instead: as many columns as fit, each at least this wide (e.g. `16rem`). */
30
+ minItemWidth?: string;
31
+ gap?: Space;
32
+ }
33
+ /** Two-dimensional layout. Children never overflow their track (`minmax(0, 1fr)`). */
34
+ export declare function Grid({ as: Tag, cols, minItemWidth, gap, className, style, ...rest }: GridProps): import("react").JSX.Element;
35
+ export interface DividerProps extends HTMLAttributes<HTMLDivElement> {
36
+ orientation?: 'horizontal' | 'vertical';
37
+ /** Text in the middle ("or", a date). */
38
+ label?: ReactNode;
39
+ }
40
+ export declare function Divider({ orientation, label, className, ...rest }: DividerProps): import("react").JSX.Element;
41
+ export {};
@@ -0,0 +1,90 @@
1
+ import { cx as e } from "../lib/cx.js";
2
+ import { jsx as t, jsxs as n } from "react/jsx-runtime";
3
+ //#region src/layout/layout.tsx
4
+ var r = {
5
+ 0: "gap-0",
6
+ 1: "gap-1",
7
+ 2: "gap-2",
8
+ 3: "gap-3",
9
+ 4: "gap-4",
10
+ 5: "gap-5",
11
+ 6: "gap-6",
12
+ 8: "gap-8",
13
+ 10: "gap-10",
14
+ 12: "gap-12",
15
+ 16: "gap-16"
16
+ }, i = {
17
+ sm: "max-w-[40rem]",
18
+ md: "max-w-[60rem]",
19
+ lg: "max-w-[75rem]",
20
+ xl: "max-w-[90rem]",
21
+ full: "max-w-none"
22
+ };
23
+ function a({ as: n = "div", size: r = "lg", className: a, ...o }) {
24
+ return /* @__PURE__ */ t(n, {
25
+ className: e("mx-auto w-full px-gutter", i[r], a),
26
+ ...o
27
+ });
28
+ }
29
+ var o = {
30
+ start: "items-start",
31
+ center: "items-center",
32
+ end: "items-end",
33
+ stretch: "items-stretch",
34
+ baseline: "items-baseline"
35
+ }, s = {
36
+ start: "justify-start",
37
+ center: "justify-center",
38
+ end: "justify-end",
39
+ between: "justify-between"
40
+ };
41
+ function c({ as: n = "div", direction: i = "column", rowFrom: a, gap: c = 4, align: l, justify: u, wrap: d, className: f, ...p }) {
42
+ return /* @__PURE__ */ t(n, {
43
+ className: e("flex", i === "row" ? "flex-row" : "flex-col", a === "sm" && "sm:flex-row", a === "md" && "md:flex-row", r[c], l && o[l], u && s[u], d && "flex-wrap", f),
44
+ ...p
45
+ });
46
+ }
47
+ var l = {
48
+ 1: "grid-cols-1",
49
+ 2: "grid-cols-1 sm:grid-cols-2",
50
+ 3: "grid-cols-1 sm:grid-cols-2 md:grid-cols-3",
51
+ 4: "grid-cols-1 sm:grid-cols-2 md:grid-cols-4",
52
+ 6: "grid-cols-2 sm:grid-cols-3 md:grid-cols-6",
53
+ 12: "grid-cols-4 sm:grid-cols-6 md:grid-cols-12"
54
+ };
55
+ function u({ as: n = "div", cols: i = 3, minItemWidth: a, gap: o = 6, className: s, style: c, ...u }) {
56
+ let d = a ? { gridTemplateColumns: `repeat(auto-fit, minmax(min(${a}, 100%), 1fr))` } : void 0;
57
+ return /* @__PURE__ */ t(n, {
58
+ className: e("grid", !a && l[i], r[o], s),
59
+ style: {
60
+ ...d,
61
+ ...c
62
+ },
63
+ ...u
64
+ });
65
+ }
66
+ function d({ orientation: r = "horizontal", label: i, className: a, ...o }) {
67
+ return r === "vertical" ? /* @__PURE__ */ t("div", {
68
+ role: "separator",
69
+ "aria-orientation": "vertical",
70
+ className: e("w-px self-stretch bg-border", a),
71
+ ...o
72
+ }) : i ? /* @__PURE__ */ n("div", {
73
+ role: "separator",
74
+ className: e("flex items-center gap-3 font-sans text-xs font-semibold tracking-wide text-fg-muted uppercase", a),
75
+ ...o,
76
+ children: [
77
+ /* @__PURE__ */ t("span", { className: "h-px flex-1 bg-border" }),
78
+ i,
79
+ /* @__PURE__ */ t("span", { className: "h-px flex-1 bg-border" })
80
+ ]
81
+ }) : /* @__PURE__ */ t("div", {
82
+ role: "separator",
83
+ className: e("h-px w-full bg-border", a),
84
+ ...o
85
+ });
86
+ }
87
+ //#endregion
88
+ export { a as Container, d as Divider, u as Grid, c as Stack };
89
+
90
+ //# sourceMappingURL=layout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.js","names":["GAP","CONTAINER","Container","Tag","rest","cx","size","className","ALIGN","JUSTIFY","Stack","direction","rowFrom","gap","align","justify","wrap","COLS","Grid","autoFit","minItemWidth","cols","style","Divider","orientation","label"],"sources":["../../src/layout/layout.tsx"],"sourcesContent":["import type { CSSProperties, ElementType, HTMLAttributes, ReactNode } from 'react';\nimport { cx } from '../lib/cx';\n\n/* Layout primitives on the 4px grid. Server-safe (no hooks). */\n\ntype Space = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 8 | 10 | 12 | 16;\nconst GAP: Record<Space, string> = {\n 0: 'gap-0',\n 1: 'gap-1',\n 2: 'gap-2',\n 3: 'gap-3',\n 4: 'gap-4',\n 5: 'gap-5',\n 6: 'gap-6',\n 8: 'gap-8',\n 10: 'gap-10',\n 12: 'gap-12',\n 16: 'gap-16',\n};\n\ninterface BoxProps extends HTMLAttributes<HTMLElement> {\n /** Element to render (e.g. `section`, `ul`, `main`). */\n as?: ElementType;\n children?: ReactNode;\n}\n\nexport type ContainerSize = 'sm' | 'md' | 'lg' | 'xl' | 'full';\nconst CONTAINER: Record<ContainerSize, string> = {\n sm: 'max-w-[40rem]',\n md: 'max-w-[60rem]',\n lg: 'max-w-[75rem]',\n xl: 'max-w-[90rem]',\n full: 'max-w-none',\n};\n\nexport interface ContainerProps extends BoxProps {\n /** sm 640 (reading), md 960 (forms, docs), lg 1200 (site default), xl 1440 (dashboards). */\n size?: ContainerSize;\n}\n\n/** Centers content with responsive side padding (16px mobile, 24px from `sm`, 32px from `md`). */\nexport function Container({ as: Tag = 'div', size = 'lg', className, ...rest }: ContainerProps) {\n return <Tag className={cx('mx-auto w-full px-gutter', CONTAINER[size], className)} {...rest} />;\n}\n\nexport interface StackProps extends BoxProps {\n direction?: 'column' | 'row';\n /** Switch from column to row at a breakpoint (`sm` 640, `md` 960). */\n rowFrom?: 'sm' | 'md';\n gap?: Space;\n align?: 'start' | 'center' | 'end' | 'stretch' | 'baseline';\n justify?: 'start' | 'center' | 'end' | 'between';\n wrap?: boolean;\n}\n\nconst ALIGN = {\n start: 'items-start',\n center: 'items-center',\n end: 'items-end',\n stretch: 'items-stretch',\n baseline: 'items-baseline',\n};\nconst JUSTIFY = { start: 'justify-start', center: 'justify-center', end: 'justify-end', between: 'justify-between' };\n\n/** One-dimensional layout with consistent gaps. */\nexport function Stack({\n as: Tag = 'div',\n direction = 'column',\n rowFrom,\n gap = 4,\n align,\n justify,\n wrap,\n className,\n ...rest\n}: StackProps) {\n return (\n <Tag\n className={cx(\n 'flex',\n direction === 'row' ? 'flex-row' : 'flex-col',\n rowFrom === 'sm' && 'sm:flex-row',\n rowFrom === 'md' && 'md:flex-row',\n GAP[gap],\n align && ALIGN[align],\n justify && JUSTIFY[justify],\n wrap && 'flex-wrap',\n className,\n )}\n {...rest}\n />\n );\n}\n\nexport interface GridProps extends BoxProps {\n /** Fixed columns from `md` (1 column on mobile, 2 from `sm` when cols ≥ 3). */\n cols?: 1 | 2 | 3 | 4 | 6 | 12;\n /** Responsive auto-fit instead: as many columns as fit, each at least this wide (e.g. `16rem`). */\n minItemWidth?: string;\n gap?: Space;\n}\n\nconst COLS = {\n 1: 'grid-cols-1',\n 2: 'grid-cols-1 sm:grid-cols-2',\n 3: 'grid-cols-1 sm:grid-cols-2 md:grid-cols-3',\n 4: 'grid-cols-1 sm:grid-cols-2 md:grid-cols-4',\n 6: 'grid-cols-2 sm:grid-cols-3 md:grid-cols-6',\n 12: 'grid-cols-4 sm:grid-cols-6 md:grid-cols-12',\n};\n\n/** Two-dimensional layout. Children never overflow their track (`minmax(0, 1fr)`). */\nexport function Grid({ as: Tag = 'div', cols = 3, minItemWidth, gap = 6, className, style, ...rest }: GridProps) {\n const autoFit: CSSProperties | undefined = minItemWidth\n ? { gridTemplateColumns: `repeat(auto-fit, minmax(min(${minItemWidth}, 100%), 1fr))` }\n : undefined;\n return (\n <Tag\n className={cx('grid', !minItemWidth && COLS[cols], GAP[gap], className)}\n style={{ ...autoFit, ...style }}\n {...rest}\n />\n );\n}\n\nexport interface DividerProps extends HTMLAttributes<HTMLDivElement> {\n orientation?: 'horizontal' | 'vertical';\n /** Text in the middle (\"or\", a date). */\n label?: ReactNode;\n}\n\nexport function Divider({ orientation = 'horizontal', label, className, ...rest }: DividerProps) {\n if (orientation === 'vertical') {\n return (\n <div\n role=\"separator\"\n aria-orientation=\"vertical\"\n className={cx('w-px self-stretch bg-border', className)}\n {...rest}\n />\n );\n }\n if (label) {\n return (\n <div\n role=\"separator\"\n className={cx(\n 'flex items-center gap-3 font-sans text-xs font-semibold tracking-wide text-fg-muted uppercase',\n className,\n )}\n {...rest}\n >\n <span className=\"h-px flex-1 bg-border\" />\n {label}\n <span className=\"h-px flex-1 bg-border\" />\n </div>\n );\n }\n return <div role=\"separator\" className={cx('h-px w-full bg-border', className)} {...rest} />;\n}\n"],"mappings":";;;AAMA,IAAMA,IAA6B;CACjC,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,IAAI;CACJ,IAAI;CACJ,IAAI;AACN,GASMC,IAA2C;CAC/C,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,MAAM;AACR;AAQA,SAAgBC,EAAU,EAAE,IAAIC,IAAM,OAAO,UAAO,MAAM,cAAW,GAAGC,KAAwB;CAC9F,OAAO,kBAACD,GAAD;EAAK,WAAWE,EAAG,4BAA4BJ,EAAUK,IAAOC,CAAS;EAAG,GAAIH;CAAO,CAAA;AAChG;AAYA,IAAMI,IAAQ;CACZ,OAAO;CACP,QAAQ;CACR,KAAK;CACL,SAAS;CACT,UAAU;AACZ,GACMC,IAAU;CAAE,OAAO;CAAiB,QAAQ;CAAkB,KAAK;CAAe,SAAS;AAAkB;AAGnH,SAAgBC,EAAM,EACpB,IAAIP,IAAM,OACV,eAAY,UACZ,YACA,SAAM,GACN,UACA,YACA,SACA,cACA,GAAGC,KACU;CACb,OACE,kBAACD,GAAD;EACE,WAAWE,EACT,QACAM,MAAc,QAAQ,aAAa,YACnCC,MAAY,QAAQ,eACpBA,MAAY,QAAQ,eACpBZ,EAAIa,IACJC,KAASN,EAAMM,IACfC,KAAWN,EAAQM,IACnBC,KAAQ,aACRT,CACF;EACA,GAAIH;CACL,CAAA;AAEL;AAUA,IAAMa,IAAO;CACX,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,IAAI;AACN;AAGA,SAAgBC,EAAK,EAAE,IAAIf,IAAM,OAAO,UAAO,GAAG,iBAAc,SAAM,GAAG,cAAW,UAAO,GAAGC,KAAmB;CAC/G,IAAMe,IAAqCC,IACvC,EAAE,qBAAqB,+BAA+BA,EAAa,gBAAgB,IACnF,KAAA;CACJ,OACE,kBAACjB,GAAD;EACE,WAAWE,EAAG,QAAQ,CAACe,KAAgBH,EAAKI,IAAOrB,EAAIa,IAAMN,CAAS;EACtE,OAAO;GAAE,GAAGY;GAAS,GAAGG;EAAM;EAC9B,GAAIlB;CACL,CAAA;AAEL;AAQA,SAAgBmB,EAAQ,EAAE,iBAAc,cAAc,UAAO,cAAW,GAAGnB,KAAsB;CA2B/F,OA1BIoB,MAAgB,aAEhB,kBAAC,OAAD;EACE,MAAK;EACL,oBAAiB;EACjB,WAAWnB,EAAG,+BAA+BE,CAAS;EACtD,GAAIH;CACL,CAAA,IAGDqB,IAEA,kBAAC,OAAD;EACE,MAAK;EACL,WAAWpB,EACT,iGACAE,CACF;EACA,GAAIH;EANN,UAAA;GAQE,kBAAC,QAAD,EAAM,WAAU,wBAAyB,CAAA;GACxCqB;GACD,kBAAC,QAAD,EAAM,WAAU,wBAAyB,CAAA;EACtC;C,KAGF,kBAAC,OAAD;EAAK,MAAK;EAAY,WAAWpB,EAAG,yBAAyBE,CAAS;EAAG,GAAIH;CAAO,CAAA;AAC7F"}
@@ -0,0 +1,9 @@
1
+ export declare const focusRing = "focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-focus";
2
+ /** Radix Checkbox root. `sm` (18 px, 20 px on touch) for tables; `md` (20 px) for forms. */
3
+ export declare function checkboxBox({ size, tone, }?: {
4
+ size?: 'sm' | 'md';
5
+ tone?: 'primary' | 'danger';
6
+ }): string;
7
+ /** Radix Switch root and thumb. `md` (44×24) for forms; `sm` (36×20) for table cells and dense lists. */
8
+ export declare function switchTrack(size?: 'sm' | 'md'): string;
9
+ export declare function switchThumb(size?: 'sm' | 'md'): string;
@@ -0,0 +1,16 @@
1
+ import { cx as e } from "./cx.js";
2
+ //#region src/lib/control-styles.ts
3
+ var t = "focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-focus";
4
+ function n({ size: t = "md", tone: n = "primary" } = {}) {
5
+ return e("grid shrink-0 cursor-pointer place-items-center border-2 border-fg-muted/60 bg-bg text-on-primary transition-colors", "disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-danger-border", t === "sm" ? "size-[18px] rounded-[5px] pointer-coarse:size-5" : "size-5 rounded-[6px]", n === "danger" ? "data-[state=checked]:border-danger-solid data-[state=checked]:bg-danger-solid" : "data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary");
6
+ }
7
+ function r(t = "md") {
8
+ return e("relative inline-flex shrink-0 cursor-pointer items-center rounded-full bg-fg-muted/35 transition-colors duration-150", "data-[state=checked]:bg-primary disabled:cursor-not-allowed disabled:opacity-50", t === "sm" ? "h-5 w-9" : "h-6 w-11");
9
+ }
10
+ function i(t = "md") {
11
+ return e("block translate-x-0.5 rounded-full bg-white shadow-[0_1px_3px_rgb(0_0_0/0.25)] transition-transform duration-150 motion-reduce:transition-none", t === "sm" ? "size-4 data-[state=checked]:translate-x-[18px]" : "size-5 data-[state=checked]:translate-x-[22px]");
12
+ }
13
+ //#endregion
14
+ export { n as checkboxBox, t as focusRing, i as switchThumb, r as switchTrack };
15
+
16
+ //# sourceMappingURL=control-styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control-styles.js","names":["focusRing","checkboxBox","cx","size","tone","switchTrack","switchThumb"],"sources":["../../src/lib/control-styles.ts"],"sourcesContent":["import { cx } from './cx';\n\n/*\n * Shared looks for small controls, so Checkbox, the table's row checkbox, PermissionMatrix, Switch and CellSwitch\n * stay identical. Each returns one class string; callers only add layout (margins) or focus handling.\n */\n\nexport const focusRing = 'focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-focus';\n\n/** Radix Checkbox root. `sm` (18 px, 20 px on touch) for tables; `md` (20 px) for forms. */\nexport function checkboxBox({\n size = 'md',\n tone = 'primary',\n}: { size?: 'sm' | 'md'; tone?: 'primary' | 'danger' } = {}) {\n return cx(\n 'grid shrink-0 cursor-pointer place-items-center border-2 border-fg-muted/60 bg-bg text-on-primary transition-colors',\n 'disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-danger-border',\n size === 'sm' ? 'size-[18px] rounded-[5px] pointer-coarse:size-5' : 'size-5 rounded-[6px]',\n // One tone per checkbox: two checked colors on one element would conflict.\n tone === 'danger'\n ? 'data-[state=checked]:border-danger-solid data-[state=checked]:bg-danger-solid'\n : 'data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary',\n );\n}\n\n/** Radix Switch root and thumb. `md` (44×24) for forms; `sm` (36×20) for table cells and dense lists. */\nexport function switchTrack(size: 'sm' | 'md' = 'md') {\n return cx(\n 'relative inline-flex shrink-0 cursor-pointer items-center rounded-full bg-fg-muted/35 transition-colors duration-150',\n 'data-[state=checked]:bg-primary disabled:cursor-not-allowed disabled:opacity-50',\n size === 'sm' ? 'h-5 w-9' : 'h-6 w-11',\n );\n}\n\nexport function switchThumb(size: 'sm' | 'md' = 'md') {\n return cx(\n 'block translate-x-0.5 rounded-full bg-white shadow-[0_1px_3px_rgb(0_0_0/0.25)] transition-transform duration-150 motion-reduce:transition-none',\n size === 'sm' ? 'size-4 data-[state=checked]:translate-x-[18px]' : 'size-5 data-[state=checked]:translate-x-[22px]',\n );\n}\n"],"mappings":";;AAOA,IAAaA,IAAY;AAGzB,SAAgBC,EAAY,EAC1B,UAAO,MACP,UAAO,cACgD,CAAC,GAAG;CAC3D,OAAOC,EACL,uHACA,qFACAC,MAAS,OAAO,oDAAoD,wBAEpEC,MAAS,WACL,kFACA,qJACN;AACF;AAGA,SAAgBC,EAAY,IAAoB,MAAM;CACpD,OAAOH,EACL,wHACA,mFACAC,MAAS,OAAO,YAAY,UAC9B;AACF;AAEA,SAAgBG,EAAY,IAAoB,MAAM;CACpD,OAAOJ,EACL,kJACAC,MAAS,OAAO,mDAAmD,gDACrE;AACF"}
@@ -0,0 +1,2 @@
1
+ /** Joins class names, skipping falsy values. */
2
+ export declare function cx(...classes: Array<string | false | null | undefined>): string;
package/dist/lib/cx.js ADDED
@@ -0,0 +1,8 @@
1
+ //#region src/lib/cx.ts
2
+ function e(...e) {
3
+ return e.filter(Boolean).join(" ");
4
+ }
5
+ //#endregion
6
+ export { e as cx };
7
+
8
+ //# sourceMappingURL=cx.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cx.js","names":["cx","classes"],"sources":["../../src/lib/cx.ts"],"sourcesContent":["/** Joins class names, skipping falsy values. */\nexport function cx(...classes: Array<string | false | null | undefined>): string {\n return classes.filter(Boolean).join(' ');\n}\n"],"mappings":";AACA,SAAgBA,EAAG,GAAGC,GAA2D;CAC/E,OAAOA,EAAQ,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;AACzC"}
@@ -0,0 +1,13 @@
1
+ import type { SVGProps } from 'react';
2
+ /**
3
+ * Small utility glyphs (chevrons, check, close, menu…) that the mascot icon set does not cover.
4
+ * Internal: 24×24, stroke 2.25, `currentColor`, always decorative.
5
+ */
6
+ export type GlyphName = 'chevron-down' | 'chevron-up' | 'chevron-right' | 'chevron-left' | 'check' | 'close' | 'menu' | 'dot' | 'more' | 'search' | 'sidebar' | 'arrow-up' | 'arrow-down' | 'sort' | 'minus' | 'plus' | 'columns';
7
+ export interface GlyphProps extends Omit<SVGProps<SVGSVGElement>, 'children'> {
8
+ name: GlyphName;
9
+ size?: number;
10
+ }
11
+ export declare function Glyph({ name, size, ...rest }: GlyphProps): import("react").JSX.Element;
12
+ /** Shared focus ring. */
13
+ export declare const focusRing = "focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-focus";
@@ -0,0 +1,67 @@
1
+ import { jsx as e, jsxs as t } from "react/jsx-runtime";
2
+ //#region src/lib/glyphs.tsx
3
+ var n = {
4
+ "chevron-down": "M6 9l6 6 6-6",
5
+ "chevron-up": "M6 15l6-6 6 6",
6
+ "chevron-right": "M9 6l6 6-6 6",
7
+ "chevron-left": "M15 6l-6 6 6 6",
8
+ check: "M5 12.5l4.5 4.5L19 7.5",
9
+ close: "M6 6l12 12M18 6L6 18",
10
+ menu: "M4 7h16M4 12h16M4 17h16",
11
+ dot: "",
12
+ more: "",
13
+ search: "M10.5 17a6.5 6.5 0 1 0 0-13 6.5 6.5 0 0 0 0 13zM15.5 15.5L20 20",
14
+ sidebar: "M4 5h16v14H4zM9.5 5v14",
15
+ "arrow-up": "M12 19V5M6 11l6-6 6 6",
16
+ "arrow-down": "M12 5v14M6 13l6 6 6-6",
17
+ sort: "M8 9l4-4 4 4M8 15l4 4 4-4",
18
+ minus: "M6 12h12",
19
+ plus: "M12 6v12M6 12h12",
20
+ columns: "M4 5h16v14H4zM10 5v14M15 5v14"
21
+ };
22
+ function r({ name: r, size: i = 16, ...a }) {
23
+ return /* @__PURE__ */ e("svg", {
24
+ viewBox: "0 0 24 24",
25
+ width: i,
26
+ height: i,
27
+ "aria-hidden": "true",
28
+ focusable: "false",
29
+ fill: "none",
30
+ ...a,
31
+ children: r === "dot" ? /* @__PURE__ */ e("circle", {
32
+ cx: "12",
33
+ cy: "12",
34
+ r: "4",
35
+ fill: "currentColor"
36
+ }) : r === "more" ? /* @__PURE__ */ t("g", {
37
+ fill: "currentColor",
38
+ children: [
39
+ /* @__PURE__ */ e("circle", {
40
+ cx: "5.5",
41
+ cy: "12",
42
+ r: "1.9"
43
+ }),
44
+ /* @__PURE__ */ e("circle", {
45
+ cx: "12",
46
+ cy: "12",
47
+ r: "1.9"
48
+ }),
49
+ /* @__PURE__ */ e("circle", {
50
+ cx: "18.5",
51
+ cy: "12",
52
+ r: "1.9"
53
+ })
54
+ ]
55
+ }) : /* @__PURE__ */ e("path", {
56
+ d: n[r],
57
+ stroke: "currentColor",
58
+ strokeWidth: "2.25",
59
+ strokeLinecap: "round",
60
+ strokeLinejoin: "round"
61
+ })
62
+ });
63
+ }
64
+ //#endregion
65
+ export { r as Glyph };
66
+
67
+ //# sourceMappingURL=glyphs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glyphs.js","names":["PATHS","Glyph","rest","size","name"],"sources":["../../src/lib/glyphs.tsx"],"sourcesContent":["import type { SVGProps } from 'react';\n\n/**\n * Small utility glyphs (chevrons, check, close, menu…) that the mascot icon set does not cover.\n * Internal: 24×24, stroke 2.25, `currentColor`, always decorative.\n */\nexport type GlyphName =\n | 'chevron-down'\n | 'chevron-up'\n | 'chevron-right'\n | 'chevron-left'\n | 'check'\n | 'close'\n | 'menu'\n | 'dot'\n | 'more'\n | 'search'\n | 'sidebar'\n | 'arrow-up'\n | 'arrow-down'\n | 'sort'\n | 'minus'\n | 'plus'\n | 'columns';\n\nconst PATHS: Record<GlyphName, string> = {\n 'chevron-down': 'M6 9l6 6 6-6',\n 'chevron-up': 'M6 15l6-6 6 6',\n 'chevron-right': 'M9 6l6 6-6 6',\n 'chevron-left': 'M15 6l-6 6 6 6',\n check: 'M5 12.5l4.5 4.5L19 7.5',\n close: 'M6 6l12 12M18 6L6 18',\n menu: 'M4 7h16M4 12h16M4 17h16',\n dot: '',\n more: '',\n search: 'M10.5 17a6.5 6.5 0 1 0 0-13 6.5 6.5 0 0 0 0 13zM15.5 15.5L20 20',\n sidebar: 'M4 5h16v14H4zM9.5 5v14',\n 'arrow-up': 'M12 19V5M6 11l6-6 6 6',\n 'arrow-down': 'M12 5v14M6 13l6 6 6-6',\n sort: 'M8 9l4-4 4 4M8 15l4 4 4-4',\n minus: 'M6 12h12',\n plus: 'M12 6v12M6 12h12',\n columns: 'M4 5h16v14H4zM10 5v14M15 5v14',\n};\n\nexport interface GlyphProps extends Omit<SVGProps<SVGSVGElement>, 'children'> {\n name: GlyphName;\n size?: number;\n}\n\nexport function Glyph({ name, size = 16, ...rest }: GlyphProps) {\n return (\n <svg viewBox=\"0 0 24 24\" width={size} height={size} aria-hidden=\"true\" focusable=\"false\" fill=\"none\" {...rest}>\n {name === 'dot' ? (\n <circle cx=\"12\" cy=\"12\" r=\"4\" fill=\"currentColor\" />\n ) : name === 'more' ? (\n <g fill=\"currentColor\">\n <circle cx=\"5.5\" cy=\"12\" r=\"1.9\" />\n <circle cx=\"12\" cy=\"12\" r=\"1.9\" />\n <circle cx=\"18.5\" cy=\"12\" r=\"1.9\" />\n </g>\n ) : (\n <path d={PATHS[name]} stroke=\"currentColor\" strokeWidth=\"2.25\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n )}\n </svg>\n );\n}\n\n/** Shared focus ring. */\nexport const focusRing = 'focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-focus';\n"],"mappings":";;AAyBA,IAAMA,IAAmC;CACvC,gBAAgB;CAChB,cAAc;CACd,iBAAiB;CACjB,gBAAgB;CAChB,OAAO;CACP,OAAO;CACP,MAAM;CACN,KAAK;CACL,MAAM;CACN,QAAQ;CACR,SAAS;CACT,YAAY;CACZ,cAAc;CACd,MAAM;CACN,OAAO;CACP,MAAM;CACN,SAAS;AACX;AAOA,SAAgBC,EAAM,EAAE,SAAM,UAAO,IAAI,GAAGC,KAAoB;CAC9D,OACE,kBAAC,OAAD;EAAK,SAAQ;EAAY,OAAOC;EAAM,QAAQA;EAAM,eAAY;EAAO,WAAU;EAAQ,MAAK;EAAO,GAAID;EACtG,UAAAE,MAAS,QACR,kBAAC,UAAD;GAAQ,IAAG;GAAK,IAAG;GAAK,GAAE;GAAI,MAAK;EAAgB,CAAA,IACjDA,MAAS,SACX,kBAAC,KAAD;GAAG,MAAK;GAAR,UAAA;IACE,kBAAC,UAAD;KAAQ,IAAG;KAAM,IAAG;KAAK,GAAE;IAAO,CAAA;IAClC,kBAAC,UAAD;KAAQ,IAAG;KAAK,IAAG;KAAK,GAAE;IAAO,CAAA;IACjC,kBAAC,UAAD;KAAQ,IAAG;KAAO,IAAG;KAAK,GAAE;IAAO,CAAA;GAClC;EAEH,CAAA,IAAA,kBAAC,QAAD;GAAM,GAAGJ,EAAMI;GAAO,QAAO;GAAe,aAAY;GAAO,eAAc;GAAQ,gBAAe;EAAS,CAAA;CAE5G,CAAA;AAET"}
@@ -0,0 +1,8 @@
1
+ export declare const SEGMENTED_TRACK: {
2
+ readonly default: "gap-0.5 rounded-sm border border-border bg-surface p-1";
3
+ readonly inverse: "gap-0.5 rounded-sm border border-white/25 bg-white/10 p-1";
4
+ };
5
+ export declare const SEGMENTED_ITEM: {
6
+ readonly default: string;
7
+ readonly inverse: string;
8
+ };
@@ -0,0 +1,12 @@
1
+ //#region src/lib/segmented-styles.ts
2
+ var e = {
3
+ default: "gap-0.5 rounded-sm border border-border bg-surface p-1",
4
+ inverse: "gap-0.5 rounded-sm border border-white/25 bg-white/10 p-1"
5
+ }, t = {
6
+ default: "text-fg-muted hover:text-fg focus-visible:outline-focus data-[state=active]:bg-primary data-[state=active]:text-on-primary data-[state=active]:hover:text-on-primary data-[state=checked]:bg-primary data-[state=checked]:text-on-primary data-[state=checked]:hover:text-on-primary",
7
+ inverse: "text-white hover:bg-white/15 focus-visible:outline-white data-[state=active]:bg-white data-[state=active]:text-primary data-[state=active]:hover:text-primary data-[state=checked]:bg-white data-[state=checked]:text-primary data-[state=checked]:hover:text-primary"
8
+ };
9
+ //#endregion
10
+ export { t as SEGMENTED_ITEM, e as SEGMENTED_TRACK };
11
+
12
+ //# sourceMappingURL=segmented-styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"segmented-styles.js","names":["SEGMENTED_TRACK","SEGMENTED_ITEM"],"sources":["../../src/lib/segmented-styles.ts"],"sourcesContent":["/*\n * One look for every segmented control: `Tabs` (segmented, segmented-inverse) and `SegmentedControl`. Item colors\n * cover both Radix states (`active` for tabs, `checked` for radio items), so the two components stay identical.\n */\n\nexport const SEGMENTED_TRACK = {\n default: 'gap-0.5 rounded-sm border border-border bg-surface p-1',\n inverse: 'gap-0.5 rounded-sm border border-white/25 bg-white/10 p-1',\n} as const;\n\nexport const SEGMENTED_ITEM = {\n default:\n 'text-fg-muted hover:text-fg focus-visible:outline-focus ' +\n 'data-[state=active]:bg-primary data-[state=active]:text-on-primary data-[state=active]:hover:text-on-primary ' +\n 'data-[state=checked]:bg-primary data-[state=checked]:text-on-primary data-[state=checked]:hover:text-on-primary',\n inverse:\n 'text-white hover:bg-white/15 focus-visible:outline-white ' +\n 'data-[state=active]:bg-white data-[state=active]:text-primary data-[state=active]:hover:text-primary ' +\n 'data-[state=checked]:bg-white data-[state=checked]:text-primary data-[state=checked]:hover:text-primary',\n} as const;\n"],"mappings":";AAKA,IAAaA,IAAkB;CAC7B,SAAS;CACT,SAAS;AACX,GAEaC,IAAiB;CAC5B,SACE;CAGF,SACE;AAGJ"}
@@ -0,0 +1,7 @@
1
+ import type { KeyboardEvent } from 'react';
2
+ /**
3
+ * Radio groups (ARIA pattern): arrow keys move focus *and* select. Radix RadioGroup moves focus but, in our setup,
4
+ * does not always check the newly focused item, so the group's `onKeyDown` clicks it after the focus moves (Radix
5
+ * moves focus in a `setTimeout`; ours is queued after it).
6
+ */
7
+ export declare function selectOnArrow(e: KeyboardEvent<HTMLElement>): void;
@@ -0,0 +1,21 @@
1
+ //#region src/lib/select-on-arrow.ts
2
+ var e = /* @__PURE__ */ new Set([
3
+ "ArrowUp",
4
+ "ArrowDown",
5
+ "ArrowLeft",
6
+ "ArrowRight",
7
+ "Home",
8
+ "End"
9
+ ]);
10
+ function t(t) {
11
+ if (!e.has(t.key)) return;
12
+ let n = t.currentTarget;
13
+ setTimeout(() => {
14
+ let e = document.activeElement;
15
+ e && n.contains(e) && e.getAttribute("role") === "radio" && e.getAttribute("aria-checked") !== "true" && e.click();
16
+ });
17
+ }
18
+ //#endregion
19
+ export { t as selectOnArrow };
20
+
21
+ //# sourceMappingURL=select-on-arrow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-on-arrow.js","names":["ARROWS","selectOnArrow","e","group","el"],"sources":["../../src/lib/select-on-arrow.ts"],"sourcesContent":["import type { KeyboardEvent } from 'react';\n\nconst ARROWS = new Set(['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Home', 'End']);\n\n/**\n * Radio groups (ARIA pattern): arrow keys move focus *and* select. Radix RadioGroup moves focus but, in our setup,\n * does not always check the newly focused item, so the group's `onKeyDown` clicks it after the focus moves (Radix\n * moves focus in a `setTimeout`; ours is queued after it).\n */\nexport function selectOnArrow(e: KeyboardEvent<HTMLElement>) {\n if (!ARROWS.has(e.key)) return;\n const group = e.currentTarget;\n setTimeout(() => {\n const el = document.activeElement as HTMLElement | null;\n if (el && group.contains(el) && el.getAttribute('role') === 'radio' && el.getAttribute('aria-checked') !== 'true') {\n el.click();\n }\n });\n}\n"],"mappings":";AAEA,IAAMA,oBAAS,IAAI,IAAI;CAAC;CAAW;CAAa;CAAa;CAAc;CAAQ;AAAK,CAAC;AAOzF,SAAgBC,EAAc,GAA+B;CAC3D,IAAI,CAACD,EAAO,IAAIE,EAAE,GAAG,GAAG;CACxB,IAAMC,IAAQD,EAAE;CAChB,iBAAiB;EACf,IAAME,IAAK,SAAS;EACpB,AAAIA,KAAMD,EAAM,SAASC,CAAE,KAAKA,EAAG,aAAa,MAAM,MAAM,WAAWA,EAAG,aAAa,cAAc,MAAM,UACzGA,EAAG,MAAM;CAEb,CAAC;AACH"}
@@ -0,0 +1,5 @@
1
+ /** "Skip to content": the first focusable element, visible only on keyboard focus. Targets `<main id="kf-main">`. */
2
+ export declare function SkipLink({ label, target }: {
3
+ label?: string;
4
+ target?: string;
5
+ }): import("react").JSX.Element;
@@ -0,0 +1,13 @@
1
+ import { jsx as e } from "react/jsx-runtime";
2
+ //#region src/lib/skip-link.tsx
3
+ function t({ label: t = "Skip to content", target: n = "kf-main" }) {
4
+ return /* @__PURE__ */ e("a", {
5
+ href: `#${n}`,
6
+ className: "fixed top-2 left-2 z-[80] -translate-y-20 rounded-full bg-primary px-4 py-2 font-sans text-sm font-semibold text-on-primary focus:translate-y-0 focus-visible:outline-3 focus-visible:outline-focus",
7
+ children: t
8
+ });
9
+ }
10
+ //#endregion
11
+ export { t as SkipLink };
12
+
13
+ //# sourceMappingURL=skip-link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skip-link.js","names":["SkipLink","target","label"],"sources":["../../src/lib/skip-link.tsx"],"sourcesContent":["/** \"Skip to content\": the first focusable element, visible only on keyboard focus. Targets `<main id=\"kf-main\">`. */\nexport function SkipLink({ label = 'Skip to content', target = 'kf-main' }: { label?: string; target?: string }) {\n return (\n <a\n href={`#${target}`}\n className=\"fixed top-2 left-2 z-[80] -translate-y-20 rounded-full bg-primary px-4 py-2 font-sans text-sm font-semibold text-on-primary focus:translate-y-0 focus-visible:outline-3 focus-visible:outline-focus\"\n >\n {label}\n </a>\n );\n}\n"],"mappings":";;AACA,SAAgBA,EAAS,EAAE,WAAQ,mBAAmB,YAAS,aAAkD;CAC/G,OACE,kBAAC,KAAD;EACE,MAAM,IAAIC;EACV,WAAU;EAET,UAAAC;CACA,CAAA;AAEP"}
@@ -0,0 +1,4 @@
1
+ /** Matches the `sm` breakpoint (640px): below it, overlays switch to their mobile pattern. */
2
+ export declare const DESKTOP_QUERY = "(min-width: 40rem)";
3
+ /** `true` when the query matches. Returns `initial` on the server and on the first client render. */
4
+ export declare function useMediaQuery(query: string, initial?: boolean): boolean;
@@ -0,0 +1,15 @@
1
+ "use client";
2
+ import { useEffect as e, useState as t } from "react";
3
+ //#region src/lib/use-media-query.ts
4
+ var n = "(min-width: 40rem)";
5
+ function r(n, r = !0) {
6
+ let [i, a] = t(r);
7
+ return e(() => {
8
+ let e = window.matchMedia(n), t = () => a(e.matches);
9
+ return t(), e.addEventListener("change", t), () => e.removeEventListener("change", t);
10
+ }, [n]), i;
11
+ }
12
+ //#endregion
13
+ export { n as DESKTOP_QUERY, r as useMediaQuery };
14
+
15
+ //# sourceMappingURL=use-media-query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-media-query.js","names":["DESKTOP_QUERY","useMediaQuery","matches","setMatches","useState","initial","useEffect","mq","query","update"],"sources":["../../src/lib/use-media-query.ts"],"sourcesContent":["'use client';\n\nimport { useEffect, useState } from 'react';\n\n/** Matches the `sm` breakpoint (640px): below it, overlays switch to their mobile pattern. */\nexport const DESKTOP_QUERY = '(min-width: 40rem)';\n\n/** `true` when the query matches. Returns `initial` on the server and on the first client render. */\nexport function useMediaQuery(query: string, initial = true) {\n const [matches, setMatches] = useState(initial);\n useEffect(() => {\n const mq = window.matchMedia(query);\n const update = () => setMatches(mq.matches);\n update();\n mq.addEventListener('change', update);\n return () => mq.removeEventListener('change', update);\n }, [query]);\n return matches;\n}\n"],"mappings":";;;AAKA,IAAaA,IAAgB;AAG7B,SAAgBC,EAAc,GAAe,IAAU,IAAM;CAC3D,IAAM,CAACC,GAASC,KAAcC,EAASC,CAAO;CAQ9C,OAPAC,QAAgB;EACd,IAAMC,IAAK,OAAO,WAAWC,CAAK,GAC5BC,UAAeN,EAAWI,EAAG,OAAO;EAG1C,OAFAE,EAAO,GACPF,EAAG,iBAAiB,UAAUE,CAAM,SACvBF,EAAG,oBAAoB,UAAUE,CAAM;CACtD,GAAG,CAACD,CAAK,CAAC,GACHN;AACT"}