@inui/design-system 0.0.0-stage → 1.22.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/README.md +360 -2
  2. package/dist/components/ui/accordion.d.ts +44 -0
  3. package/dist/components/ui/accordion.d.ts.map +1 -0
  4. package/dist/components/ui/accordion.js +86 -0
  5. package/dist/components/ui/accordion.js.map +1 -0
  6. package/dist/components/ui/ai-avatar.d.ts +46 -0
  7. package/dist/components/ui/ai-avatar.d.ts.map +1 -0
  8. package/dist/components/ui/ai-avatar.js +43 -0
  9. package/dist/components/ui/ai-avatar.js.map +1 -0
  10. package/dist/components/ui/ai-label.d.ts +118 -0
  11. package/dist/components/ui/ai-label.d.ts.map +1 -0
  12. package/dist/components/ui/ai-label.js +184 -0
  13. package/dist/components/ui/ai-label.js.map +1 -0
  14. package/dist/components/ui/ai-sparkle.d.ts +91 -0
  15. package/dist/components/ui/ai-sparkle.d.ts.map +1 -0
  16. package/dist/components/ui/ai-sparkle.js +200 -0
  17. package/dist/components/ui/ai-sparkle.js.map +1 -0
  18. package/dist/components/ui/alert-dialog.d.ts +79 -0
  19. package/dist/components/ui/alert-dialog.d.ts.map +1 -0
  20. package/dist/components/ui/alert-dialog.js +147 -0
  21. package/dist/components/ui/alert-dialog.js.map +1 -0
  22. package/dist/components/ui/alert.d.ts +71 -0
  23. package/dist/components/ui/alert.d.ts.map +1 -0
  24. package/dist/components/ui/alert.js +108 -0
  25. package/dist/components/ui/alert.js.map +1 -0
  26. package/dist/components/ui/aspect-ratio.d.ts +30 -0
  27. package/dist/components/ui/aspect-ratio.d.ts.map +1 -0
  28. package/dist/components/ui/aspect-ratio.js +40 -0
  29. package/dist/components/ui/aspect-ratio.js.map +1 -0
  30. package/dist/components/ui/attachment.d.ts +74 -0
  31. package/dist/components/ui/attachment.d.ts.map +1 -0
  32. package/dist/components/ui/attachment.js +142 -0
  33. package/dist/components/ui/attachment.js.map +1 -0
  34. package/dist/components/ui/avatar.d.ts +82 -0
  35. package/dist/components/ui/avatar.d.ts.map +1 -0
  36. package/dist/components/ui/avatar.js +123 -0
  37. package/dist/components/ui/avatar.js.map +1 -0
  38. package/dist/components/ui/badge.d.ts +130 -0
  39. package/dist/components/ui/badge.d.ts.map +1 -0
  40. package/dist/components/ui/badge.js +226 -0
  41. package/dist/components/ui/badge.js.map +1 -0
  42. package/dist/components/ui/breadcrumb.d.ts +43 -0
  43. package/dist/components/ui/breadcrumb.d.ts.map +1 -0
  44. package/dist/components/ui/breadcrumb.js +98 -0
  45. package/dist/components/ui/breadcrumb.js.map +1 -0
  46. package/dist/components/ui/browser-tabs.d.ts +218 -0
  47. package/dist/components/ui/browser-tabs.d.ts.map +1 -0
  48. package/dist/components/ui/browser-tabs.js +528 -0
  49. package/dist/components/ui/browser-tabs.js.map +1 -0
  50. package/dist/components/ui/bubble.d.ts +80 -0
  51. package/dist/components/ui/bubble.d.ts.map +1 -0
  52. package/dist/components/ui/bubble.js +235 -0
  53. package/dist/components/ui/bubble.js.map +1 -0
  54. package/dist/components/ui/button-group.d.ts +60 -0
  55. package/dist/components/ui/button-group.d.ts.map +1 -0
  56. package/dist/components/ui/button-group.js +89 -0
  57. package/dist/components/ui/button-group.js.map +1 -0
  58. package/dist/components/ui/button.d.ts +157 -0
  59. package/dist/components/ui/button.d.ts.map +1 -0
  60. package/dist/components/ui/button.js +144 -0
  61. package/dist/components/ui/button.js.map +1 -0
  62. package/dist/components/ui/calendar.d.ts +57 -0
  63. package/dist/components/ui/calendar.d.ts.map +1 -0
  64. package/dist/components/ui/calendar.js +163 -0
  65. package/dist/components/ui/calendar.js.map +1 -0
  66. package/dist/components/ui/card.d.ts +96 -0
  67. package/dist/components/ui/card.d.ts.map +1 -0
  68. package/dist/components/ui/card.js +131 -0
  69. package/dist/components/ui/card.js.map +1 -0
  70. package/dist/components/ui/carousel.d.ts +86 -0
  71. package/dist/components/ui/carousel.d.ts.map +1 -0
  72. package/dist/components/ui/carousel.js +207 -0
  73. package/dist/components/ui/carousel.js.map +1 -0
  74. package/dist/components/ui/checkbox.d.ts +54 -0
  75. package/dist/components/ui/checkbox.d.ts.map +1 -0
  76. package/dist/components/ui/checkbox.js +78 -0
  77. package/dist/components/ui/checkbox.js.map +1 -0
  78. package/dist/components/ui/collapsible.d.ts +45 -0
  79. package/dist/components/ui/collapsible.d.ts.map +1 -0
  80. package/dist/components/ui/collapsible.js +65 -0
  81. package/dist/components/ui/collapsible.js.map +1 -0
  82. package/dist/components/ui/combobox.d.ts +111 -0
  83. package/dist/components/ui/combobox.d.ts.map +1 -0
  84. package/dist/components/ui/combobox.js +264 -0
  85. package/dist/components/ui/combobox.js.map +1 -0
  86. package/dist/components/ui/command.d.ts +71 -0
  87. package/dist/components/ui/command.d.ts.map +1 -0
  88. package/dist/components/ui/command.js +133 -0
  89. package/dist/components/ui/command.js.map +1 -0
  90. package/dist/components/ui/context-menu.d.ts +70 -0
  91. package/dist/components/ui/context-menu.d.ts.map +1 -0
  92. package/dist/components/ui/context-menu.js +163 -0
  93. package/dist/components/ui/context-menu.js.map +1 -0
  94. package/dist/components/ui/count-badge.d.ts +40 -0
  95. package/dist/components/ui/count-badge.d.ts.map +1 -0
  96. package/dist/components/ui/count-badge.js +43 -0
  97. package/dist/components/ui/count-badge.js.map +1 -0
  98. package/dist/components/ui/data-table.d.ts +1026 -0
  99. package/dist/components/ui/data-table.d.ts.map +1 -0
  100. package/dist/components/ui/data-table.js +3423 -0
  101. package/dist/components/ui/data-table.js.map +1 -0
  102. package/dist/components/ui/dialog.d.ts +73 -0
  103. package/dist/components/ui/dialog.d.ts.map +1 -0
  104. package/dist/components/ui/dialog.js +145 -0
  105. package/dist/components/ui/dialog.js.map +1 -0
  106. package/dist/components/ui/direction.d.ts +35 -0
  107. package/dist/components/ui/direction.d.ts.map +1 -0
  108. package/dist/components/ui/direction.js +35 -0
  109. package/dist/components/ui/direction.js.map +1 -0
  110. package/dist/components/ui/drawer.d.ts +53 -0
  111. package/dist/components/ui/drawer.d.ts.map +1 -0
  112. package/dist/components/ui/drawer.js +115 -0
  113. package/dist/components/ui/drawer.js.map +1 -0
  114. package/dist/components/ui/dropdown-menu.d.ts +75 -0
  115. package/dist/components/ui/dropdown-menu.d.ts.map +1 -0
  116. package/dist/components/ui/dropdown-menu.js +173 -0
  117. package/dist/components/ui/dropdown-menu.js.map +1 -0
  118. package/dist/components/ui/dropzone.d.ts +141 -0
  119. package/dist/components/ui/dropzone.d.ts.map +1 -0
  120. package/dist/components/ui/dropzone.js +257 -0
  121. package/dist/components/ui/dropzone.js.map +1 -0
  122. package/dist/components/ui/empty.d.ts +94 -0
  123. package/dist/components/ui/empty.d.ts.map +1 -0
  124. package/dist/components/ui/empty.js +149 -0
  125. package/dist/components/ui/empty.js.map +1 -0
  126. package/dist/components/ui/field.d.ts +122 -0
  127. package/dist/components/ui/field.d.ts.map +1 -0
  128. package/dist/components/ui/field.js +254 -0
  129. package/dist/components/ui/field.js.map +1 -0
  130. package/dist/components/ui/hover-card.d.ts +51 -0
  131. package/dist/components/ui/hover-card.d.ts.map +1 -0
  132. package/dist/components/ui/hover-card.js +101 -0
  133. package/dist/components/ui/hover-card.js.map +1 -0
  134. package/dist/components/ui/input-group.d.ts +62 -0
  135. package/dist/components/ui/input-group.d.ts.map +1 -0
  136. package/dist/components/ui/input-group.js +115 -0
  137. package/dist/components/ui/input-group.js.map +1 -0
  138. package/dist/components/ui/input-otp.d.ts +52 -0
  139. package/dist/components/ui/input-otp.d.ts.map +1 -0
  140. package/dist/components/ui/input-otp.js +89 -0
  141. package/dist/components/ui/input-otp.js.map +1 -0
  142. package/dist/components/ui/input.d.ts +47 -0
  143. package/dist/components/ui/input.d.ts.map +1 -0
  144. package/dist/components/ui/input.js +47 -0
  145. package/dist/components/ui/input.js.map +1 -0
  146. package/dist/components/ui/item.d.ts +74 -0
  147. package/dist/components/ui/item.d.ts.map +1 -0
  148. package/dist/components/ui/item.js +176 -0
  149. package/dist/components/ui/item.js.map +1 -0
  150. package/dist/components/ui/kbd.d.ts +41 -0
  151. package/dist/components/ui/kbd.d.ts.map +1 -0
  152. package/dist/components/ui/kbd.js +58 -0
  153. package/dist/components/ui/kbd.js.map +1 -0
  154. package/dist/components/ui/key-value-pair.d.ts +56 -0
  155. package/dist/components/ui/key-value-pair.d.ts.map +1 -0
  156. package/dist/components/ui/key-value-pair.js +110 -0
  157. package/dist/components/ui/key-value-pair.js.map +1 -0
  158. package/dist/components/ui/label.d.ts +83 -0
  159. package/dist/components/ui/label.d.ts.map +1 -0
  160. package/dist/components/ui/label.js +85 -0
  161. package/dist/components/ui/label.js.map +1 -0
  162. package/dist/components/ui/marker.d.ts +54 -0
  163. package/dist/components/ui/marker.d.ts.map +1 -0
  164. package/dist/components/ui/marker.js +79 -0
  165. package/dist/components/ui/marker.js.map +1 -0
  166. package/dist/components/ui/menubar.d.ts +72 -0
  167. package/dist/components/ui/menubar.d.ts.map +1 -0
  168. package/dist/components/ui/menubar.js +174 -0
  169. package/dist/components/ui/menubar.js.map +1 -0
  170. package/dist/components/ui/message-scroller.d.ts +53 -0
  171. package/dist/components/ui/message-scroller.d.ts.map +1 -0
  172. package/dist/components/ui/message-scroller.js +95 -0
  173. package/dist/components/ui/message-scroller.js.map +1 -0
  174. package/dist/components/ui/message.d.ts +63 -0
  175. package/dist/components/ui/message.d.ts.map +1 -0
  176. package/dist/components/ui/message.js +92 -0
  177. package/dist/components/ui/message.js.map +1 -0
  178. package/dist/components/ui/meta-list.d.ts +55 -0
  179. package/dist/components/ui/meta-list.d.ts.map +1 -0
  180. package/dist/components/ui/meta-list.js +75 -0
  181. package/dist/components/ui/meta-list.js.map +1 -0
  182. package/dist/components/ui/native-select.d.ts +51 -0
  183. package/dist/components/ui/native-select.d.ts.map +1 -0
  184. package/dist/components/ui/native-select.js +75 -0
  185. package/dist/components/ui/native-select.js.map +1 -0
  186. package/dist/components/ui/navigation-menu.d.ts +56 -0
  187. package/dist/components/ui/navigation-menu.d.ts.map +1 -0
  188. package/dist/components/ui/navigation-menu.js +116 -0
  189. package/dist/components/ui/navigation-menu.js.map +1 -0
  190. package/dist/components/ui/page-header.d.ts +223 -0
  191. package/dist/components/ui/page-header.d.ts.map +1 -0
  192. package/dist/components/ui/page-header.js +233 -0
  193. package/dist/components/ui/page-header.js.map +1 -0
  194. package/dist/components/ui/pagination.d.ts +68 -0
  195. package/dist/components/ui/pagination.d.ts.map +1 -0
  196. package/dist/components/ui/pagination.js +145 -0
  197. package/dist/components/ui/pagination.js.map +1 -0
  198. package/dist/components/ui/popover.d.ts +61 -0
  199. package/dist/components/ui/popover.d.ts.map +1 -0
  200. package/dist/components/ui/popover.js +144 -0
  201. package/dist/components/ui/popover.js.map +1 -0
  202. package/dist/components/ui/progress.d.ts +35 -0
  203. package/dist/components/ui/progress.d.ts.map +1 -0
  204. package/dist/components/ui/progress.js +46 -0
  205. package/dist/components/ui/progress.js.map +1 -0
  206. package/dist/components/ui/questionnaire.d.ts +92 -0
  207. package/dist/components/ui/questionnaire.d.ts.map +1 -0
  208. package/dist/components/ui/questionnaire.js +277 -0
  209. package/dist/components/ui/questionnaire.js.map +1 -0
  210. package/dist/components/ui/radio-group.d.ts +50 -0
  211. package/dist/components/ui/radio-group.d.ts.map +1 -0
  212. package/dist/components/ui/radio-group.js +65 -0
  213. package/dist/components/ui/radio-group.js.map +1 -0
  214. package/dist/components/ui/resizable.d.ts +44 -0
  215. package/dist/components/ui/resizable.d.ts.map +1 -0
  216. package/dist/components/ui/resizable.js +73 -0
  217. package/dist/components/ui/resizable.js.map +1 -0
  218. package/dist/components/ui/scroll-area.d.ts +55 -0
  219. package/dist/components/ui/scroll-area.d.ts.map +1 -0
  220. package/dist/components/ui/scroll-area.js +72 -0
  221. package/dist/components/ui/scroll-area.js.map +1 -0
  222. package/dist/components/ui/select.d.ts +274 -0
  223. package/dist/components/ui/select.d.ts.map +1 -0
  224. package/dist/components/ui/select.js +609 -0
  225. package/dist/components/ui/select.js.map +1 -0
  226. package/dist/components/ui/separator.d.ts +31 -0
  227. package/dist/components/ui/separator.d.ts.map +1 -0
  228. package/dist/components/ui/separator.js +38 -0
  229. package/dist/components/ui/separator.js.map +1 -0
  230. package/dist/components/ui/sheet.d.ts +59 -0
  231. package/dist/components/ui/sheet.d.ts.map +1 -0
  232. package/dist/components/ui/sheet.js +128 -0
  233. package/dist/components/ui/sheet.js.map +1 -0
  234. package/dist/components/ui/sidebar.d.ts +166 -0
  235. package/dist/components/ui/sidebar.d.ts.map +1 -0
  236. package/dist/components/ui/sidebar.js +512 -0
  237. package/dist/components/ui/sidebar.js.map +1 -0
  238. package/dist/components/ui/skeleton.d.ts +35 -0
  239. package/dist/components/ui/skeleton.d.ts.map +1 -0
  240. package/dist/components/ui/skeleton.js +46 -0
  241. package/dist/components/ui/skeleton.js.map +1 -0
  242. package/dist/components/ui/slider.d.ts +39 -0
  243. package/dist/components/ui/slider.d.ts.map +1 -0
  244. package/dist/components/ui/slider.js +75 -0
  245. package/dist/components/ui/slider.js.map +1 -0
  246. package/dist/components/ui/sonner.d.ts +56 -0
  247. package/dist/components/ui/sonner.d.ts.map +1 -0
  248. package/dist/components/ui/sonner.js +96 -0
  249. package/dist/components/ui/sonner.js.map +1 -0
  250. package/dist/components/ui/spinner.d.ts +37 -0
  251. package/dist/components/ui/spinner.d.ts.map +1 -0
  252. package/dist/components/ui/spinner.js +51 -0
  253. package/dist/components/ui/spinner.js.map +1 -0
  254. package/dist/components/ui/stepper.d.ts +84 -0
  255. package/dist/components/ui/stepper.d.ts.map +1 -0
  256. package/dist/components/ui/stepper.js +145 -0
  257. package/dist/components/ui/stepper.js.map +1 -0
  258. package/dist/components/ui/switch.d.ts +36 -0
  259. package/dist/components/ui/switch.d.ts.map +1 -0
  260. package/dist/components/ui/switch.js +44 -0
  261. package/dist/components/ui/switch.js.map +1 -0
  262. package/dist/components/ui/table.d.ts +80 -0
  263. package/dist/components/ui/table.d.ts.map +1 -0
  264. package/dist/components/ui/table.js +130 -0
  265. package/dist/components/ui/table.js.map +1 -0
  266. package/dist/components/ui/tabs.d.ts +87 -0
  267. package/dist/components/ui/tabs.d.ts.map +1 -0
  268. package/dist/components/ui/tabs.js +128 -0
  269. package/dist/components/ui/tabs.js.map +1 -0
  270. package/dist/components/ui/textarea.d.ts +35 -0
  271. package/dist/components/ui/textarea.d.ts.map +1 -0
  272. package/dist/components/ui/textarea.js +38 -0
  273. package/dist/components/ui/textarea.js.map +1 -0
  274. package/dist/components/ui/time-picker.d.ts +58 -0
  275. package/dist/components/ui/time-picker.d.ts.map +1 -0
  276. package/dist/components/ui/time-picker.js +188 -0
  277. package/dist/components/ui/time-picker.js.map +1 -0
  278. package/dist/components/ui/toggle-group.d.ts +52 -0
  279. package/dist/components/ui/toggle-group.d.ts.map +1 -0
  280. package/dist/components/ui/toggle-group.js +94 -0
  281. package/dist/components/ui/toggle-group.js.map +1 -0
  282. package/dist/components/ui/toggle.d.ts +50 -0
  283. package/dist/components/ui/toggle.d.ts.map +1 -0
  284. package/dist/components/ui/toggle.js +70 -0
  285. package/dist/components/ui/toggle.js.map +1 -0
  286. package/dist/components/ui/tooltip.d.ts +51 -0
  287. package/dist/components/ui/tooltip.d.ts.map +1 -0
  288. package/dist/components/ui/tooltip.js +78 -0
  289. package/dist/components/ui/tooltip.js.map +1 -0
  290. package/dist/hooks/use-dark-mode.d.ts +2 -0
  291. package/dist/hooks/use-dark-mode.d.ts.map +1 -0
  292. package/dist/hooks/use-dark-mode.js +36 -0
  293. package/dist/hooks/use-dark-mode.js.map +1 -0
  294. package/dist/hooks/use-mobile.d.ts +2 -0
  295. package/dist/hooks/use-mobile.d.ts.map +1 -0
  296. package/dist/hooks/use-mobile.js +20 -0
  297. package/dist/hooks/use-mobile.js.map +1 -0
  298. package/dist/index.d.ts +88 -0
  299. package/dist/index.d.ts.map +1 -0
  300. package/dist/index.js +77 -0
  301. package/dist/lib/scope.d.ts +64 -0
  302. package/dist/lib/scope.d.ts.map +1 -0
  303. package/dist/lib/scope.js +74 -0
  304. package/dist/lib/scope.js.map +1 -0
  305. package/dist/lib/utils.d.ts +3 -0
  306. package/dist/lib/utils.d.ts.map +1 -0
  307. package/dist/lib/utils.js +11 -0
  308. package/dist/lib/utils.js.map +1 -0
  309. package/dist/styles.css +2 -0
  310. package/dist/theme-tokens.css +537 -0
  311. package/dist/theme.css +417 -0
  312. package/package.json +103 -3
@@ -0,0 +1,128 @@
1
+ "use client";
2
+ import { cn } from "../../lib/utils.js";
3
+ import "react";
4
+ import { Tabs as Tabs$1 } from "radix-ui";
5
+ import { jsx } from "react/jsx-runtime";
6
+ import { cva } from "class-variance-authority";
7
+ //#region src/components/ui/tabs.tsx
8
+ function Tabs({ className, orientation = "horizontal", ...props }) {
9
+ return /* @__PURE__ */ jsx(Tabs$1.Root, {
10
+ "data-slot": "tabs",
11
+ "data-orientation": orientation,
12
+ className: cn("group/tabs flex gap-2 data-[orientation=horizontal]:flex-col", className),
13
+ ...props
14
+ });
15
+ }
16
+ var tabsListVariants = cva("group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col data-[variant=line]:rounded-none", {
17
+ variants: {
18
+ variant: {
19
+ default: "bg-secondary",
20
+ line: "gap-1 bg-transparent"
21
+ },
22
+ size: {
23
+ default: "group-data-[orientation=horizontal]/tabs:h-8",
24
+ sm: "group-data-[orientation=horizontal]/tabs:h-7"
25
+ }
26
+ },
27
+ defaultVariants: {
28
+ variant: "default",
29
+ size: "default"
30
+ }
31
+ });
32
+ function TabsList({ className, variant = "default", size = "default", ...props }) {
33
+ return /* @__PURE__ */ jsx(Tabs$1.List, {
34
+ "data-slot": "tabs-list",
35
+ "data-variant": variant,
36
+ "data-size": size,
37
+ className: cn(tabsListVariants({
38
+ variant,
39
+ size
40
+ }), className),
41
+ ...props
42
+ });
43
+ }
44
+ function TabsTrigger({ className, ...props }) {
45
+ return /* @__PURE__ */ jsx(Tabs$1.Trigger, {
46
+ "data-slot": "tabs-trigger",
47
+ className: cn("relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium whitespace-nowrap text-muted-foreground transition-colors group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 has-data-[icon=inline-end]:pr-1 has-data-[icon=inline-start]:pl-1 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "group-data-[size=sm]/tabs-list:gap-1 group-data-[size=sm]/tabs-list:px-1 group-data-[size=sm]/tabs-list:text-xs", "group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:border-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:text-primary dark:group-data-[variant=line]/tabs-list:data-[state=active]:hover:text-primary-hover", "group-data-[variant=line]/tabs-list:hover:after:bg-muted-foreground group-data-[variant=line]/tabs-list:hover:after:opacity-100 group-data-[variant=line]/tabs-list:data-[state=active]:hover:after:bg-primary-hover dark:group-data-[variant=line]/tabs-list:data-[state=active]:hover:after:bg-primary-hover", "group-data-[variant=default]/tabs-list:hover:bg-muted group-data-[variant=default]/tabs-list:hover:text-foreground dark:group-data-[variant=default]/tabs-list:hover:text-foreground group-data-[variant=default]/tabs-list:data-[state=active]:hover:bg-primary/10 group-data-[variant=default]/tabs-list:data-[state=active]:hover:text-primary-hover", "data-[state=active]:bg-primary/10 data-[state=active]:text-primary data-[state=active]:hover:text-primary-hover dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 dark:data-[state=active]:text-foreground", "after:absolute after:bg-primary after:opacity-0 after:transition group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100", className),
48
+ ...props
49
+ });
50
+ }
51
+ function TabsContent({ className, ...props }) {
52
+ return /* @__PURE__ */ jsx(Tabs$1.Content, {
53
+ "data-slot": "tabs-content",
54
+ className: cn("flex-1 text-sm outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring", className),
55
+ ...props
56
+ });
57
+ }
58
+ /**
59
+ * WHEN TO USE
60
+ * - Switching between 2–6 named views of the same entity (e.g. a record's
61
+ * Overview / History / Notes)
62
+ * - orientation="vertical" for a side-by-side settings-style layout
63
+ *
64
+ * NOT WHEN
65
+ * - Top-level app navigation — use NavigationMenu
66
+ * - An ordered sequence where each step has its own pending/done state —
67
+ * use Stepper, which is Tabs plus a step-circle icon
68
+ * - A one-question-at-a-time form flow with its own Previous/Next/Submit —
69
+ * use Questionnaire
70
+ * - Showing hierarchical position — use Breadcrumb
71
+ *
72
+ * KEY RULES
73
+ * - TabsList variant: "default" (transparent track, default) | "line"
74
+ * (underline, also transparent) — set once on TabsList, not per trigger
75
+ * - Both variants read the active tab as primary, not neutral: "default" gets a
76
+ * subtle bg-primary/10 fill + text-primary (translucent, so it blends on any
77
+ * surface with no per-container overrides needed); "line" gets text-primary
78
+ * plus a bg-primary underline, no fill (there's no track to fill)
79
+ * - Hover, inactive tab: "default" gets a bg-muted fill + darker text; "line"
80
+ * doesn't change text at all — it reveals a bg-border underline instead,
81
+ * a plain hover affordance, not a color change
82
+ * - Hover, active tab: "default"'s fill doesn't change, only its text darkens
83
+ * to text-primary-hover; "line" darkens BOTH its text and its underline to
84
+ * text-primary-hover/bg-primary-hover — hovering the current tab still
85
+ * reads as "this one, more so," not a neutral hover state
86
+ * - A leading icon on a trigger uses the same `data-icon="inline-start"` slot
87
+ * convention as Button/Toggle/SelectTrigger — no separate icon prop; the
88
+ * trigger's own padding adjusts automatically once that attribute is present
89
+ * - Individual TabsTrigger can be `disabled`
90
+ * - orientation defaults to "horizontal"; vertical stacks TabsList in a column
91
+ * - TabsList size: "default" (h-8 track, text-sm triggers) | "sm" (h-7,
92
+ * text-xs, tighter gap/padding) — same default/sm vocabulary as Button/
93
+ * Item/Attachment/Select, not Tabs-specific naming. Set once on TabsList;
94
+ * TabsTrigger reacts to it automatically.
95
+ * - A count next to a label (e.g. "Inbox 12") is a plain `Badge` child, same
96
+ * `gap-1.5` slot the leading-icon convention above already uses — no
97
+ * dedicated count prop. Badge's own static color reads fine against both
98
+ * the active and inactive tab state as-is.
99
+ *
100
+ * USAGE
101
+ * <Tabs defaultValue="overview">
102
+ * <TabsList>
103
+ * <TabsTrigger value="overview">Overview</TabsTrigger>
104
+ * <TabsTrigger value="history">History</TabsTrigger>
105
+ * </TabsList>
106
+ * <TabsContent value="overview">...</TabsContent>
107
+ * <TabsContent value="history">...</TabsContent>
108
+ * </Tabs>
109
+ *
110
+ * <TabsList size="sm">
111
+ * <TabsTrigger value="a">A</TabsTrigger>
112
+ * <TabsTrigger value="b">B</TabsTrigger>
113
+ * </TabsList>
114
+ *
115
+ * <TabsList variant="line">
116
+ * <TabsTrigger value="a">A</TabsTrigger>
117
+ * <TabsTrigger value="b" disabled>B</TabsTrigger>
118
+ * </TabsList>
119
+ *
120
+ * <TabsTrigger value="overview">
121
+ * <LayoutGridIcon data-icon="inline-start" />
122
+ * Overview
123
+ * </TabsTrigger>
124
+ */
125
+ //#endregion
126
+ export { Tabs, TabsContent, TabsList, TabsTrigger, tabsListVariants };
127
+
128
+ //# sourceMappingURL=tabs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs.js","names":[],"sources":["../../../src/components/ui/tabs.tsx"],"sourcesContent":["\"use client\"\n\n/**\n * Tabs — switches between panels of the same content area.\n *\n * Radix Tabs primitive with two TabsList visual variants: \"default\" (pill\n * background, active tab gets a raised background) and \"line\" (transparent\n * background, active tab gets an underline).\n */\n\nimport * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { Tabs as TabsPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction Tabs({\n className,\n orientation = \"horizontal\",\n ...props\n}: React.ComponentProps<typeof TabsPrimitive.Root>) {\n return (\n <TabsPrimitive.Root\n data-slot=\"tabs\"\n data-orientation={orientation}\n className={cn(\n \"group/tabs flex gap-2 data-[orientation=horizontal]:flex-col\",\n className\n )}\n {...props}\n />\n )\n}\n\nconst tabsListVariants = cva(\n \"group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col data-[variant=line]:rounded-none\",\n {\n variants: {\n variant: {\n default: \"bg-secondary\",\n line: \"gap-1 bg-transparent\",\n },\n size: {\n default: \"group-data-[orientation=horizontal]/tabs:h-8\",\n sm: \"group-data-[orientation=horizontal]/tabs:h-7\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n)\n\nfunction TabsList({\n className,\n variant = \"default\",\n size = \"default\",\n ...props\n}: React.ComponentProps<typeof TabsPrimitive.List> &\n VariantProps<typeof tabsListVariants>) {\n return (\n <TabsPrimitive.List\n data-slot=\"tabs-list\"\n data-variant={variant}\n data-size={size}\n className={cn(tabsListVariants({ variant, size }), className)}\n {...props}\n />\n )\n}\n\nfunction TabsTrigger({\n className,\n ...props\n}: React.ComponentProps<typeof TabsPrimitive.Trigger>) {\n return (\n <TabsPrimitive.Trigger\n data-slot=\"tabs-trigger\"\n className={cn(\n \"relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium whitespace-nowrap text-muted-foreground transition-colors group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 has-data-[icon=inline-end]:pr-1 has-data-[icon=inline-start]:pl-1 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n \"group-data-[size=sm]/tabs-list:gap-1 group-data-[size=sm]/tabs-list:px-1 group-data-[size=sm]/tabs-list:text-xs\",\n \"group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:border-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:text-primary dark:group-data-[variant=line]/tabs-list:data-[state=active]:hover:text-primary-hover\",\n \"group-data-[variant=line]/tabs-list:hover:after:bg-muted-foreground group-data-[variant=line]/tabs-list:hover:after:opacity-100 group-data-[variant=line]/tabs-list:data-[state=active]:hover:after:bg-primary-hover dark:group-data-[variant=line]/tabs-list:data-[state=active]:hover:after:bg-primary-hover\",\n \"group-data-[variant=default]/tabs-list:hover:bg-muted group-data-[variant=default]/tabs-list:hover:text-foreground dark:group-data-[variant=default]/tabs-list:hover:text-foreground group-data-[variant=default]/tabs-list:data-[state=active]:hover:bg-primary/10 group-data-[variant=default]/tabs-list:data-[state=active]:hover:text-primary-hover\",\n \"data-[state=active]:bg-primary/10 data-[state=active]:text-primary data-[state=active]:hover:text-primary-hover dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 dark:data-[state=active]:text-foreground\",\n \"after:absolute after:bg-primary after:opacity-0 after:transition group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction TabsContent({\n className,\n ...props\n}: React.ComponentProps<typeof TabsPrimitive.Content>) {\n return (\n <TabsPrimitive.Content\n data-slot=\"tabs-content\"\n className={cn(\n \"flex-1 text-sm outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants }\n\n/**\n * WHEN TO USE\n * - Switching between 2–6 named views of the same entity (e.g. a record's\n * Overview / History / Notes)\n * - orientation=\"vertical\" for a side-by-side settings-style layout\n *\n * NOT WHEN\n * - Top-level app navigation — use NavigationMenu\n * - An ordered sequence where each step has its own pending/done state —\n * use Stepper, which is Tabs plus a step-circle icon\n * - A one-question-at-a-time form flow with its own Previous/Next/Submit —\n * use Questionnaire\n * - Showing hierarchical position — use Breadcrumb\n *\n * KEY RULES\n * - TabsList variant: \"default\" (transparent track, default) | \"line\"\n * (underline, also transparent) — set once on TabsList, not per trigger\n * - Both variants read the active tab as primary, not neutral: \"default\" gets a\n * subtle bg-primary/10 fill + text-primary (translucent, so it blends on any\n * surface with no per-container overrides needed); \"line\" gets text-primary\n * plus a bg-primary underline, no fill (there's no track to fill)\n * - Hover, inactive tab: \"default\" gets a bg-muted fill + darker text; \"line\"\n * doesn't change text at all — it reveals a bg-border underline instead,\n * a plain hover affordance, not a color change\n * - Hover, active tab: \"default\"'s fill doesn't change, only its text darkens\n * to text-primary-hover; \"line\" darkens BOTH its text and its underline to\n * text-primary-hover/bg-primary-hover — hovering the current tab still\n * reads as \"this one, more so,\" not a neutral hover state\n * - A leading icon on a trigger uses the same `data-icon=\"inline-start\"` slot\n * convention as Button/Toggle/SelectTrigger — no separate icon prop; the\n * trigger's own padding adjusts automatically once that attribute is present\n * - Individual TabsTrigger can be `disabled`\n * - orientation defaults to \"horizontal\"; vertical stacks TabsList in a column\n * - TabsList size: \"default\" (h-8 track, text-sm triggers) | \"sm\" (h-7,\n * text-xs, tighter gap/padding) — same default/sm vocabulary as Button/\n * Item/Attachment/Select, not Tabs-specific naming. Set once on TabsList;\n * TabsTrigger reacts to it automatically.\n * - A count next to a label (e.g. \"Inbox 12\") is a plain `Badge` child, same\n * `gap-1.5` slot the leading-icon convention above already uses — no\n * dedicated count prop. Badge's own static color reads fine against both\n * the active and inactive tab state as-is.\n *\n * USAGE\n * <Tabs defaultValue=\"overview\">\n * <TabsList>\n * <TabsTrigger value=\"overview\">Overview</TabsTrigger>\n * <TabsTrigger value=\"history\">History</TabsTrigger>\n * </TabsList>\n * <TabsContent value=\"overview\">...</TabsContent>\n * <TabsContent value=\"history\">...</TabsContent>\n * </Tabs>\n *\n * <TabsList size=\"sm\">\n * <TabsTrigger value=\"a\">A</TabsTrigger>\n * <TabsTrigger value=\"b\">B</TabsTrigger>\n * </TabsList>\n *\n * <TabsList variant=\"line\">\n * <TabsTrigger value=\"a\">A</TabsTrigger>\n * <TabsTrigger value=\"b\" disabled>B</TabsTrigger>\n * </TabsList>\n *\n * <TabsTrigger value=\"overview\">\n * <LayoutGridIcon data-icon=\"inline-start\" />\n * Overview\n * </TabsTrigger>\n */\n"],"mappings":";;;;;;;AAgBA,SAAS,KAAK,EACZ,WACA,cAAc,cACd,GAAG,SAC+C;CAClD,OACE,oBAAC,OAAc,MAAf;EACE,aAAU;EACV,oBAAkB;EAClB,WAAW,GACT,gEACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,IAAM,mBAAmB,IACvB,wOACA;CACE,UAAU;EACR,SAAS;GACP,SAAS;GACT,MAAM;EACR;EACA,MAAM;GACJ,SAAS;GACT,IAAI;EACN;CACF;CACA,iBAAiB;EACf,SAAS;EACT,MAAM;CACR;AACF,CACF;AAEA,SAAS,SAAS,EAChB,WACA,UAAU,WACV,OAAO,WACP,GAAG,SAEoC;CACvC,OACE,oBAAC,OAAc,MAAf;EACE,aAAU;EACV,gBAAc;EACd,aAAW;EACX,WAAW,GAAG,iBAAiB;GAAE;GAAS;EAAK,CAAC,GAAG,SAAS;EAC5D,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,YAAY,EACnB,WACA,GAAG,SACkD;CACrD,OACE,oBAAC,OAAc,SAAf;EACE,aAAU;EACV,WAAW,GACT,goBACA,mHACA,ybACA,kTACA,2VACA,uOACA,ydACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,YAAY,EACnB,WACA,GAAG,SACkD;CACrD,OACE,oBAAC,OAAc,SAAf;EACE,aAAU;EACV,WAAW,GACT,sIACA,SACF;EACA,GAAI;CACL,CAAA;AAEL"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Textarea — multi-line text field.
3
+ *
4
+ * A plain native `<textarea>` with MMDS border/focus/error styling.
5
+ * Height auto-grows to content (`field-sizing-content`, min-h-16) instead of
6
+ * a fixed rows count, and error state is driven off `aria-invalid` rather
7
+ * than a prop.
8
+ */
9
+ import * as React from "react";
10
+ declare function Textarea({ className, ...props }: React.ComponentProps<"textarea">): React.JSX.Element;
11
+ export { Textarea };
12
+ /**
13
+ * WHEN TO USE
14
+ * - Notes, descriptions, messages — any free text that can span multiple lines
15
+ *
16
+ * NOT WHEN
17
+ * - A short single-line value — use Input
18
+ * - Rich text or markdown editing — Textarea is plain text only
19
+ *
20
+ * KEY RULES
21
+ * - Height grows with content (`field-sizing-content`); min-height is 4rem, no fixed `rows` styling
22
+ * - Error state is driven entirely by `aria-invalid` — there is no `error` prop
23
+ * - An AI-drafted value is a plain composition, not a dedicated prop, and the same shape as
24
+ * Input's: put `AILabel` in the label row as a sibling of FieldLabel (never inside it —
25
+ * AILabelTrigger is a button). The Textarea itself stays plain: no `shadow-ai-glow` or
26
+ * gradient border — that boundary glow is reserved for card-like AI surfaces (Card, Dialog),
27
+ * never form controls, where it fights the field's own focus/invalid rings. Pass AILabel
28
+ * `edited={value !== draft}` and an `onRevert` that restores it, so once the user edits the
29
+ * marker becomes a ghost undo button (AILabel's own override/undo morph). See AI/Textarea.
30
+ *
31
+ * USAGE
32
+ * <Textarea placeholder="Add a description..." />
33
+ * <Textarea aria-invalid defaultValue="..." />
34
+ */
35
+ //# sourceMappingURL=textarea.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"textarea.d.ts","sourceRoot":"","sources":["../../../src/components/ui/textarea.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,iBAAS,QAAQ,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,qBAW1E;AAED,OAAO,EAAE,QAAQ,EAAE,CAAA;AAEnB;;;;;;;;;;;;;;;;;;;;;;GAsBG"}
@@ -0,0 +1,38 @@
1
+ import { cn } from "../../lib/utils.js";
2
+ import "react";
3
+ import { jsx } from "react/jsx-runtime";
4
+ //#region src/components/ui/textarea.tsx
5
+ function Textarea({ className, ...props }) {
6
+ return /* @__PURE__ */ jsx("textarea", {
7
+ "data-slot": "textarea",
8
+ className: cn("flex field-sizing-content min-h-16 w-full rounded-lg border border-input bg-transparent px-2.5 py-2 text-base transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40", className),
9
+ ...props
10
+ });
11
+ }
12
+ /**
13
+ * WHEN TO USE
14
+ * - Notes, descriptions, messages — any free text that can span multiple lines
15
+ *
16
+ * NOT WHEN
17
+ * - A short single-line value — use Input
18
+ * - Rich text or markdown editing — Textarea is plain text only
19
+ *
20
+ * KEY RULES
21
+ * - Height grows with content (`field-sizing-content`); min-height is 4rem, no fixed `rows` styling
22
+ * - Error state is driven entirely by `aria-invalid` — there is no `error` prop
23
+ * - An AI-drafted value is a plain composition, not a dedicated prop, and the same shape as
24
+ * Input's: put `AILabel` in the label row as a sibling of FieldLabel (never inside it —
25
+ * AILabelTrigger is a button). The Textarea itself stays plain: no `shadow-ai-glow` or
26
+ * gradient border — that boundary glow is reserved for card-like AI surfaces (Card, Dialog),
27
+ * never form controls, where it fights the field's own focus/invalid rings. Pass AILabel
28
+ * `edited={value !== draft}` and an `onRevert` that restores it, so once the user edits the
29
+ * marker becomes a ghost undo button (AILabel's own override/undo morph). See AI/Textarea.
30
+ *
31
+ * USAGE
32
+ * <Textarea placeholder="Add a description..." />
33
+ * <Textarea aria-invalid defaultValue="..." />
34
+ */
35
+ //#endregion
36
+ export { Textarea };
37
+
38
+ //# sourceMappingURL=textarea.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"textarea.js","names":[],"sources":["../../../src/components/ui/textarea.tsx"],"sourcesContent":["/**\n * Textarea — multi-line text field.\n *\n * A plain native `<textarea>` with MMDS border/focus/error styling.\n * Height auto-grows to content (`field-sizing-content`, min-h-16) instead of\n * a fixed rows count, and error state is driven off `aria-invalid` rather\n * than a prop.\n */\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction Textarea({ className, ...props }: React.ComponentProps<\"textarea\">) {\n return (\n <textarea\n data-slot=\"textarea\"\n className={cn(\n \"flex field-sizing-content min-h-16 w-full rounded-lg border border-input bg-transparent px-2.5 py-2 text-base transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport { Textarea }\n\n/**\n * WHEN TO USE\n * - Notes, descriptions, messages — any free text that can span multiple lines\n *\n * NOT WHEN\n * - A short single-line value — use Input\n * - Rich text or markdown editing — Textarea is plain text only\n *\n * KEY RULES\n * - Height grows with content (`field-sizing-content`); min-height is 4rem, no fixed `rows` styling\n * - Error state is driven entirely by `aria-invalid` — there is no `error` prop\n * - An AI-drafted value is a plain composition, not a dedicated prop, and the same shape as\n * Input's: put `AILabel` in the label row as a sibling of FieldLabel (never inside it —\n * AILabelTrigger is a button). The Textarea itself stays plain: no `shadow-ai-glow` or\n * gradient border — that boundary glow is reserved for card-like AI surfaces (Card, Dialog),\n * never form controls, where it fights the field's own focus/invalid rings. Pass AILabel\n * `edited={value !== draft}` and an `onRevert` that restores it, so once the user edits the\n * marker becomes a ghost undo button (AILabel's own override/undo morph). See AI/Textarea.\n *\n * USAGE\n * <Textarea placeholder=\"Add a description...\" />\n * <Textarea aria-invalid defaultValue=\"...\" />\n */\n"],"mappings":";;;;AAaA,SAAS,SAAS,EAAE,WAAW,GAAG,SAA2C;CAC3E,OACE,oBAAC,YAAD;EACE,aAAU;EACV,WAAW,GACT,0hBACA,SACF;EACA,GAAI;CACL,CAAA;AAEL"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * TimePicker — masked text input for typing a 12-hour time (HH:MM AM/PM).
3
+ *
4
+ * Not a native `<input type="time">` (that renders OS/browser chrome, not
5
+ * a plain masked text field) and not a dropdown/list picker (compose
6
+ * Select or Combobox for that shape instead) — this is purely a typing
7
+ * experience, driven entirely through onKeyDown (not onChange) so the
8
+ * displayed value can never drift out of sync with what was actually
9
+ * typed.
10
+ *
11
+ * The mask is always fully visible — "--:-- AM" from the very first
12
+ * render, never a placeholder that vanishes on the first keystroke. Each
13
+ * digit replaces one "-" in place; the meridiem always shows a real
14
+ * "AM"/"PM" (defaults "AM" on mount), and its "M" never changes — only the
15
+ * leading "A"/"P" toggles. Uncontrolled only: `defaultValue` seeds it,
16
+ * `onValueChange` notifies of every change, there is no `value` prop.
17
+ */
18
+ import * as React from "react";
19
+ import { Input } from "../../components/ui/input";
20
+ declare function TimePicker({ defaultValue, onValueChange, className, ...props }: Omit<React.ComponentProps<typeof Input>, "value" | "defaultValue" | "onChange" | "type"> & {
21
+ defaultValue?: string;
22
+ onValueChange?: (value: string) => void;
23
+ }): React.JSX.Element;
24
+ export { TimePicker };
25
+ /**
26
+ * WHEN TO USE
27
+ * - Typing a time directly is faster than scrolling/clicking through a list — appointment
28
+ * forms, scheduling fields, anywhere the exact time is usually already known.
29
+ *
30
+ * NOT WHEN
31
+ * - Picking from a fixed set of bookable slots (e.g. every 15 minutes) — compose Select or
32
+ * Combobox with those options instead; this component has no dropdown at all.
33
+ * - Full date + time together — pair this with Calendar inside the same Popover; see Calendar's
34
+ * doc comment for the date-picker composition pattern.
35
+ *
36
+ * KEY RULES
37
+ * - Uncontrolled only — `defaultValue` seeds it, `onValueChange` fires on every change. There is
38
+ * no `value` prop; this component owns its own text the same way a plain `<input>` would.
39
+ * - `onValueChange` reports exactly what's on screen, mask characters included (e.g. "09:-- AM"
40
+ * while the minute is still unfilled) — check for "-" to know whether entry is complete.
41
+ * - The mask never disappears: it's the value itself, not a `placeholder` (which only shows on an
42
+ * empty value — this input is never actually empty). Each keystroke replaces one "-" in place.
43
+ * - The meridiem always shows a real "AM"/"PM" (starts "AM") — "a"/"p" toggles it at any time,
44
+ * independent of how much of the hour/minute has been typed; there's nothing to erase there.
45
+ * A trailing AM/PM button mirrors this toggle for touch/virtual-keyboard users, since
46
+ * `inputMode="numeric"` hides letter keys on those keyboards.
47
+ * - Digits fill hour then minute, each clamped live (hour rejects >12 or "00", minute rejects
48
+ * >59). Backspace removes the most recently-typed digit (minute first, then hour). Pasting a
49
+ * full time string (masked or clean) is also handled.
50
+ * - Error state is driven entirely by `aria-invalid` (spreads through to Input) — there is no
51
+ * separate `error` prop, consistent with every other input in this library.
52
+ * - This wraps Input purely for the visual shell — don't reach for Input directly and rebuild
53
+ * this masking logic; that's the one thing this component exists to own.
54
+ *
55
+ * USAGE
56
+ * <TimePicker defaultValue="09:30 AM" onValueChange={(value) => console.log(value)} />
57
+ */
58
+ //# sourceMappingURL=time-picker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time-picker.d.ts","sourceRoot":"","sources":["../../../src/components/ui/time-picker.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAA;AA0D7C,iBAAS,UAAU,CAAC,EAClB,YAAY,EACZ,aAAa,EACb,SAAS,EACT,GAAG,KAAK,EACT,EAAE,IAAI,CACL,KAAK,CAAC,cAAc,CAAC,OAAO,KAAK,CAAC,EAClC,OAAO,GAAG,cAAc,GAAG,UAAU,GAAG,MAAM,CAC/C,GAAG;IACF,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACxC,qBAyGA;AAED,OAAO,EAAE,UAAU,EAAE,CAAA;AAErB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG"}
@@ -0,0 +1,188 @@
1
+ "use client";
2
+ import { cn } from "../../lib/utils.js";
3
+ import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from "./input-group.js";
4
+ import * as React from "react";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ //#region src/components/ui/time-picker.tsx
7
+ /**
8
+ * TimePicker — masked text input for typing a 12-hour time (HH:MM AM/PM).
9
+ *
10
+ * Not a native `<input type="time">` (that renders OS/browser chrome, not
11
+ * a plain masked text field) and not a dropdown/list picker (compose
12
+ * Select or Combobox for that shape instead) — this is purely a typing
13
+ * experience, driven entirely through onKeyDown (not onChange) so the
14
+ * displayed value can never drift out of sync with what was actually
15
+ * typed.
16
+ *
17
+ * The mask is always fully visible — "--:-- AM" from the very first
18
+ * render, never a placeholder that vanishes on the first keystroke. Each
19
+ * digit replaces one "-" in place; the meridiem always shows a real
20
+ * "AM"/"PM" (defaults "AM" on mount), and its "M" never changes — only the
21
+ * leading "A"/"P" toggles. Uncontrolled only: `defaultValue` seeds it,
22
+ * `onValueChange` notifies of every change, there is no `value` prop.
23
+ */
24
+ var EMPTY_SEGMENTS = {
25
+ hour: "",
26
+ minute: "",
27
+ meridiem: "AM"
28
+ };
29
+ function maskChar(digits, index) {
30
+ return digits[index] ?? "-";
31
+ }
32
+ /** The always-visible mask string — e.g. "09:-- AM" while the minute is still unfilled. */
33
+ function formatSegments({ hour, minute, meridiem }) {
34
+ return `${maskChar(hour, 0)}${maskChar(hour, 1)}:${maskChar(minute, 0)}${maskChar(minute, 1)} ${meridiem}`;
35
+ }
36
+ /** Clamps/normalizes a segment set from an external source (defaultValue, paste) — rejects an out-of-range hour/minute instead of displaying it, and zero-pads a single digit to match the two-digit look typing always produces. */
37
+ function clampSegments({ hour, minute, meridiem }) {
38
+ const validHour = hour !== "" && Number(hour) >= 1 && Number(hour) <= 12;
39
+ const validMinute = minute !== "" && Number(minute) <= 59;
40
+ return {
41
+ hour: validHour ? hour.padStart(2, "0") : "",
42
+ minute: validMinute ? minute.padStart(2, "0") : "",
43
+ meridiem
44
+ };
45
+ }
46
+ /** Accepts either the clean "9:30 AM" shape or the masked "09:-- AM" shape (dashes are just treated as unfilled). */
47
+ function parseValue(value) {
48
+ const match = value.trim().match(/^([\d-]{1,2})?:?([\d-]{1,2})?\s*(AM|PM)?$/i);
49
+ if (!match) return EMPTY_SEGMENTS;
50
+ const [, hourRaw = "", minuteRaw = "", meridiemRaw = ""] = match;
51
+ return clampSegments({
52
+ hour: hourRaw.replace(/-/g, ""),
53
+ minute: minuteRaw.replace(/-/g, ""),
54
+ meridiem: meridiemRaw.toUpperCase() || "AM"
55
+ });
56
+ }
57
+ var NAVIGATION_KEYS = /* @__PURE__ */ new Set([
58
+ "Tab",
59
+ "ArrowLeft",
60
+ "ArrowRight",
61
+ "Home",
62
+ "End",
63
+ "Shift",
64
+ "Delete"
65
+ ]);
66
+ function TimePicker({ defaultValue, onValueChange, className, ...props }) {
67
+ const [segments, setSegments] = React.useState(() => parseValue(defaultValue ?? ""));
68
+ const [announcement, setAnnouncement] = React.useState("");
69
+ const commit = (next) => {
70
+ setSegments(next);
71
+ setAnnouncement(formatSegments(next));
72
+ onValueChange?.(formatSegments(next));
73
+ };
74
+ const handleKeyDown = (e) => {
75
+ if (e.key === "Backspace") {
76
+ e.preventDefault();
77
+ if (segments.minute) return commit({
78
+ ...segments,
79
+ minute: segments.minute.slice(0, -1)
80
+ });
81
+ if (segments.hour) return commit({
82
+ ...segments,
83
+ hour: segments.hour.slice(0, -1)
84
+ });
85
+ return;
86
+ }
87
+ if (/^[ap]$/i.test(e.key)) {
88
+ e.preventDefault();
89
+ commit({
90
+ ...segments,
91
+ meridiem: e.key.toLowerCase() === "a" ? "AM" : "PM"
92
+ });
93
+ return;
94
+ }
95
+ if (/^[0-9]$/.test(e.key)) {
96
+ e.preventDefault();
97
+ if (segments.hour.length < 2) {
98
+ const nextHour = segments.hour + e.key;
99
+ if (Number(nextHour) > 12 || nextHour === "00") return;
100
+ commit({
101
+ ...segments,
102
+ hour: nextHour
103
+ });
104
+ return;
105
+ }
106
+ if (segments.minute.length < 2) {
107
+ const nextMinute = segments.minute + e.key;
108
+ if (Number(nextMinute) > 59) return;
109
+ commit({
110
+ ...segments,
111
+ minute: nextMinute
112
+ });
113
+ }
114
+ return;
115
+ }
116
+ if (!NAVIGATION_KEYS.has(e.key) && !e.metaKey && !e.ctrlKey) e.preventDefault();
117
+ };
118
+ return /* @__PURE__ */ jsxs(InputGroup, { children: [
119
+ /* @__PURE__ */ jsx(InputGroupInput, {
120
+ type: "text",
121
+ inputMode: "numeric",
122
+ autoComplete: "off",
123
+ value: formatSegments(segments),
124
+ onChange: () => {},
125
+ onKeyDown: handleKeyDown,
126
+ onPaste: (e) => {
127
+ e.preventDefault();
128
+ commit(parseValue(e.clipboardData.getData("text")));
129
+ },
130
+ className: cn(className),
131
+ ...props
132
+ }),
133
+ /* @__PURE__ */ jsx(InputGroupAddon, {
134
+ align: "inline-end",
135
+ children: /* @__PURE__ */ jsx(InputGroupButton, {
136
+ type: "button",
137
+ "aria-label": segments.meridiem === "AM" ? "Switch to PM" : "Switch to AM",
138
+ onClick: () => commit({
139
+ ...segments,
140
+ meridiem: segments.meridiem === "AM" ? "PM" : "AM"
141
+ }),
142
+ children: segments.meridiem
143
+ })
144
+ }),
145
+ /* @__PURE__ */ jsx("span", {
146
+ role: "status",
147
+ className: "sr-only",
148
+ children: announcement
149
+ })
150
+ ] });
151
+ }
152
+ /**
153
+ * WHEN TO USE
154
+ * - Typing a time directly is faster than scrolling/clicking through a list — appointment
155
+ * forms, scheduling fields, anywhere the exact time is usually already known.
156
+ *
157
+ * NOT WHEN
158
+ * - Picking from a fixed set of bookable slots (e.g. every 15 minutes) — compose Select or
159
+ * Combobox with those options instead; this component has no dropdown at all.
160
+ * - Full date + time together — pair this with Calendar inside the same Popover; see Calendar's
161
+ * doc comment for the date-picker composition pattern.
162
+ *
163
+ * KEY RULES
164
+ * - Uncontrolled only — `defaultValue` seeds it, `onValueChange` fires on every change. There is
165
+ * no `value` prop; this component owns its own text the same way a plain `<input>` would.
166
+ * - `onValueChange` reports exactly what's on screen, mask characters included (e.g. "09:-- AM"
167
+ * while the minute is still unfilled) — check for "-" to know whether entry is complete.
168
+ * - The mask never disappears: it's the value itself, not a `placeholder` (which only shows on an
169
+ * empty value — this input is never actually empty). Each keystroke replaces one "-" in place.
170
+ * - The meridiem always shows a real "AM"/"PM" (starts "AM") — "a"/"p" toggles it at any time,
171
+ * independent of how much of the hour/minute has been typed; there's nothing to erase there.
172
+ * A trailing AM/PM button mirrors this toggle for touch/virtual-keyboard users, since
173
+ * `inputMode="numeric"` hides letter keys on those keyboards.
174
+ * - Digits fill hour then minute, each clamped live (hour rejects >12 or "00", minute rejects
175
+ * >59). Backspace removes the most recently-typed digit (minute first, then hour). Pasting a
176
+ * full time string (masked or clean) is also handled.
177
+ * - Error state is driven entirely by `aria-invalid` (spreads through to Input) — there is no
178
+ * separate `error` prop, consistent with every other input in this library.
179
+ * - This wraps Input purely for the visual shell — don't reach for Input directly and rebuild
180
+ * this masking logic; that's the one thing this component exists to own.
181
+ *
182
+ * USAGE
183
+ * <TimePicker defaultValue="09:30 AM" onValueChange={(value) => console.log(value)} />
184
+ */
185
+ //#endregion
186
+ export { TimePicker };
187
+
188
+ //# sourceMappingURL=time-picker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time-picker.js","names":[],"sources":["../../../src/components/ui/time-picker.tsx"],"sourcesContent":["\"use client\"\n\n/**\n * TimePicker — masked text input for typing a 12-hour time (HH:MM AM/PM).\n *\n * Not a native `<input type=\"time\">` (that renders OS/browser chrome, not\n * a plain masked text field) and not a dropdown/list picker (compose\n * Select or Combobox for that shape instead) — this is purely a typing\n * experience, driven entirely through onKeyDown (not onChange) so the\n * displayed value can never drift out of sync with what was actually\n * typed.\n *\n * The mask is always fully visible — \"--:-- AM\" from the very first\n * render, never a placeholder that vanishes on the first keystroke. Each\n * digit replaces one \"-\" in place; the meridiem always shows a real\n * \"AM\"/\"PM\" (defaults \"AM\" on mount), and its \"M\" never changes — only the\n * leading \"A\"/\"P\" toggles. Uncontrolled only: `defaultValue` seeds it,\n * `onValueChange` notifies of every change, there is no `value` prop.\n */\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Input } from \"@/components/ui/input\"\nimport {\n InputGroup,\n InputGroupAddon,\n InputGroupButton,\n InputGroupInput,\n} from \"@/components/ui/input-group\"\n\ntype TimeSegments = {\n hour: string // 0-2 digit chars, unfilled positions render as \"-\"\n minute: string // 0-2 digit chars, unfilled positions render as \"-\"\n meridiem: \"AM\" | \"PM\" // always has a real value — never unset\n}\n\nconst EMPTY_SEGMENTS: TimeSegments = { hour: \"\", minute: \"\", meridiem: \"AM\" }\n\nfunction maskChar(digits: string, index: number): string {\n return digits[index] ?? \"-\"\n}\n\n/** The always-visible mask string — e.g. \"09:-- AM\" while the minute is still unfilled. */\nfunction formatSegments({ hour, minute, meridiem }: TimeSegments): string {\n return `${maskChar(hour, 0)}${maskChar(hour, 1)}:${maskChar(minute, 0)}${maskChar(minute, 1)} ${meridiem}`\n}\n\n/** Clamps/normalizes a segment set from an external source (defaultValue, paste) — rejects an out-of-range hour/minute instead of displaying it, and zero-pads a single digit to match the two-digit look typing always produces. */\nfunction clampSegments({ hour, minute, meridiem }: TimeSegments): TimeSegments {\n const validHour = hour !== \"\" && Number(hour) >= 1 && Number(hour) <= 12\n const validMinute = minute !== \"\" && Number(minute) <= 59\n return {\n hour: validHour ? hour.padStart(2, \"0\") : \"\",\n minute: validMinute ? minute.padStart(2, \"0\") : \"\",\n meridiem,\n }\n}\n\n/** Accepts either the clean \"9:30 AM\" shape or the masked \"09:-- AM\" shape (dashes are just treated as unfilled). */\nfunction parseValue(value: string): TimeSegments {\n const match = value.trim().match(/^([\\d-]{1,2})?:?([\\d-]{1,2})?\\s*(AM|PM)?$/i)\n if (!match) return EMPTY_SEGMENTS\n const [, hourRaw = \"\", minuteRaw = \"\", meridiemRaw = \"\"] = match\n return clampSegments({\n hour: hourRaw.replace(/-/g, \"\"),\n minute: minuteRaw.replace(/-/g, \"\"),\n meridiem: (meridiemRaw.toUpperCase() as \"AM\" | \"PM\") || \"AM\",\n })\n}\n\nconst NAVIGATION_KEYS = new Set([\n \"Tab\",\n \"ArrowLeft\",\n \"ArrowRight\",\n \"Home\",\n \"End\",\n \"Shift\",\n \"Delete\",\n])\n\nfunction TimePicker({\n defaultValue,\n onValueChange,\n className,\n ...props\n}: Omit<\n React.ComponentProps<typeof Input>,\n \"value\" | \"defaultValue\" | \"onChange\" | \"type\"\n> & {\n defaultValue?: string\n onValueChange?: (value: string) => void\n}) {\n const [segments, setSegments] = React.useState<TimeSegments>(() =>\n parseValue(defaultValue ?? \"\")\n )\n // Every accepted keystroke is intercepted in onKeyDown and only ever\n // reaches the screen through React re-rendering `value` — never through\n // native text insertion — so most screen readers' typing-echo has\n // nothing to announce. This mirrors the resulting mask into a live\n // region so each accepted digit/backspace/meridiem change is still\n // spoken.\n const [announcement, setAnnouncement] = React.useState(\"\")\n\n const commit = (next: TimeSegments) => {\n setSegments(next)\n setAnnouncement(formatSegments(next))\n onValueChange?.(formatSegments(next))\n }\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (e.key === \"Backspace\") {\n e.preventDefault()\n if (segments.minute)\n return commit({ ...segments, minute: segments.minute.slice(0, -1) })\n if (segments.hour)\n return commit({ ...segments, hour: segments.hour.slice(0, -1) })\n return\n }\n\n if (/^[ap]$/i.test(e.key)) {\n e.preventDefault()\n commit({\n ...segments,\n meridiem: e.key.toLowerCase() === \"a\" ? \"AM\" : \"PM\",\n })\n return\n }\n\n if (/^[0-9]$/.test(e.key)) {\n e.preventDefault()\n if (segments.hour.length < 2) {\n const nextHour = segments.hour + e.key\n if (Number(nextHour) > 12 || nextHour === \"00\") return\n commit({ ...segments, hour: nextHour })\n return\n }\n if (segments.minute.length < 2) {\n const nextMinute = segments.minute + e.key\n if (Number(nextMinute) > 59) return\n commit({ ...segments, minute: nextMinute })\n }\n return\n }\n\n // Block anything else from reaching the native input — we render a\n // fully-controlled value ourselves, so an uncaught keystroke would\n // otherwise desync what's displayed from what these segments hold.\n if (!NAVIGATION_KEYS.has(e.key) && !e.metaKey && !e.ctrlKey) {\n e.preventDefault()\n }\n }\n\n return (\n <InputGroup>\n <InputGroupInput\n type=\"text\"\n inputMode=\"numeric\"\n autoComplete=\"off\"\n value={formatSegments(segments)}\n onChange={() => {}}\n onKeyDown={handleKeyDown}\n onPaste={(e) => {\n e.preventDefault()\n commit(parseValue(e.clipboardData.getData(\"text\")))\n }}\n className={cn(className)}\n {...props}\n />\n <InputGroupAddon align=\"inline-end\">\n {/*\n \"a\"/\"p\" only reaches this component through a physical keyboard —\n `inputMode=\"numeric\"` above tells on-screen/virtual keyboards to\n show digits only, so touch users on those devices have no letter\n key to press at all. This button is the modality-independent\n fallback; the \"a\"/\"p\" shortcut still works for physical keyboards.\n */}\n <InputGroupButton\n type=\"button\"\n aria-label={\n segments.meridiem === \"AM\" ? \"Switch to PM\" : \"Switch to AM\"\n }\n onClick={() =>\n commit({\n ...segments,\n meridiem: segments.meridiem === \"AM\" ? \"PM\" : \"AM\",\n })\n }\n >\n {segments.meridiem}\n </InputGroupButton>\n </InputGroupAddon>\n <span role=\"status\" className=\"sr-only\">\n {announcement}\n </span>\n </InputGroup>\n )\n}\n\nexport { TimePicker }\n\n/**\n * WHEN TO USE\n * - Typing a time directly is faster than scrolling/clicking through a list — appointment\n * forms, scheduling fields, anywhere the exact time is usually already known.\n *\n * NOT WHEN\n * - Picking from a fixed set of bookable slots (e.g. every 15 minutes) — compose Select or\n * Combobox with those options instead; this component has no dropdown at all.\n * - Full date + time together — pair this with Calendar inside the same Popover; see Calendar's\n * doc comment for the date-picker composition pattern.\n *\n * KEY RULES\n * - Uncontrolled only — `defaultValue` seeds it, `onValueChange` fires on every change. There is\n * no `value` prop; this component owns its own text the same way a plain `<input>` would.\n * - `onValueChange` reports exactly what's on screen, mask characters included (e.g. \"09:-- AM\"\n * while the minute is still unfilled) — check for \"-\" to know whether entry is complete.\n * - The mask never disappears: it's the value itself, not a `placeholder` (which only shows on an\n * empty value — this input is never actually empty). Each keystroke replaces one \"-\" in place.\n * - The meridiem always shows a real \"AM\"/\"PM\" (starts \"AM\") — \"a\"/\"p\" toggles it at any time,\n * independent of how much of the hour/minute has been typed; there's nothing to erase there.\n * A trailing AM/PM button mirrors this toggle for touch/virtual-keyboard users, since\n * `inputMode=\"numeric\"` hides letter keys on those keyboards.\n * - Digits fill hour then minute, each clamped live (hour rejects >12 or \"00\", minute rejects\n * >59). Backspace removes the most recently-typed digit (minute first, then hour). Pasting a\n * full time string (masked or clean) is also handled.\n * - Error state is driven entirely by `aria-invalid` (spreads through to Input) — there is no\n * separate `error` prop, consistent with every other input in this library.\n * - This wraps Input purely for the visual shell — don't reach for Input directly and rebuild\n * this masking logic; that's the one thing this component exists to own.\n *\n * USAGE\n * <TimePicker defaultValue=\"09:30 AM\" onValueChange={(value) => console.log(value)} />\n */\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAqCA,IAAM,iBAA+B;CAAE,MAAM;CAAI,QAAQ;CAAI,UAAU;AAAK;AAE5E,SAAS,SAAS,QAAgB,OAAuB;CACvD,OAAO,OAAO,UAAU;AAC1B;;AAGA,SAAS,eAAe,EAAE,MAAM,QAAQ,YAAkC;CACxE,OAAO,GAAG,SAAS,MAAM,CAAC,IAAI,SAAS,MAAM,CAAC,EAAE,GAAG,SAAS,QAAQ,CAAC,IAAI,SAAS,QAAQ,CAAC,EAAE,GAAG;AAClG;;AAGA,SAAS,cAAc,EAAE,MAAM,QAAQ,YAAwC;CAC7E,MAAM,YAAY,SAAS,MAAM,OAAO,IAAI,KAAK,KAAK,OAAO,IAAI,KAAK;CACtE,MAAM,cAAc,WAAW,MAAM,OAAO,MAAM,KAAK;CACvD,OAAO;EACL,MAAM,YAAY,KAAK,SAAS,GAAG,GAAG,IAAI;EAC1C,QAAQ,cAAc,OAAO,SAAS,GAAG,GAAG,IAAI;EAChD;CACF;AACF;;AAGA,SAAS,WAAW,OAA6B;CAC/C,MAAM,QAAQ,MAAM,KAAK,CAAC,CAAC,MAAM,4CAA4C;CAC7E,IAAI,CAAC,OAAO,OAAO;CACnB,MAAM,GAAG,UAAU,IAAI,YAAY,IAAI,cAAc,MAAM;CAC3D,OAAO,cAAc;EACnB,MAAM,QAAQ,QAAQ,MAAM,EAAE;EAC9B,QAAQ,UAAU,QAAQ,MAAM,EAAE;EAClC,UAAW,YAAY,YAAY,KAAqB;CAC1D,CAAC;AACH;AAEA,IAAM,kCAAkB,IAAI,IAAI;CAC9B;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,SAAS,WAAW,EAClB,cACA,eACA,WACA,GAAG,SAOF;CACD,MAAM,CAAC,UAAU,eAAe,MAAM,eACpC,WAAW,gBAAgB,EAAE,CAC/B;CAOA,MAAM,CAAC,cAAc,mBAAmB,MAAM,SAAS,EAAE;CAEzD,MAAM,UAAU,SAAuB;EACrC,YAAY,IAAI;EAChB,gBAAgB,eAAe,IAAI,CAAC;EACpC,gBAAgB,eAAe,IAAI,CAAC;CACtC;CAEA,MAAM,iBAAiB,MAA6C;EAClE,IAAI,EAAE,QAAQ,aAAa;GACzB,EAAE,eAAe;GACjB,IAAI,SAAS,QACX,OAAO,OAAO;IAAE,GAAG;IAAU,QAAQ,SAAS,OAAO,MAAM,GAAG,EAAE;GAAE,CAAC;GACrE,IAAI,SAAS,MACX,OAAO,OAAO;IAAE,GAAG;IAAU,MAAM,SAAS,KAAK,MAAM,GAAG,EAAE;GAAE,CAAC;GACjE;EACF;EAEA,IAAI,UAAU,KAAK,EAAE,GAAG,GAAG;GACzB,EAAE,eAAe;GACjB,OAAO;IACL,GAAG;IACH,UAAU,EAAE,IAAI,YAAY,MAAM,MAAM,OAAO;GACjD,CAAC;GACD;EACF;EAEA,IAAI,UAAU,KAAK,EAAE,GAAG,GAAG;GACzB,EAAE,eAAe;GACjB,IAAI,SAAS,KAAK,SAAS,GAAG;IAC5B,MAAM,WAAW,SAAS,OAAO,EAAE;IACnC,IAAI,OAAO,QAAQ,IAAI,MAAM,aAAa,MAAM;IAChD,OAAO;KAAE,GAAG;KAAU,MAAM;IAAS,CAAC;IACtC;GACF;GACA,IAAI,SAAS,OAAO,SAAS,GAAG;IAC9B,MAAM,aAAa,SAAS,SAAS,EAAE;IACvC,IAAI,OAAO,UAAU,IAAI,IAAI;IAC7B,OAAO;KAAE,GAAG;KAAU,QAAQ;IAAW,CAAC;GAC5C;GACA;EACF;EAKA,IAAI,CAAC,gBAAgB,IAAI,EAAE,GAAG,KAAK,CAAC,EAAE,WAAW,CAAC,EAAE,SAClD,EAAE,eAAe;CAErB;CAEA,OACE,qBAAC,YAAD,EAAA,UAAA;EACE,oBAAC,iBAAD;GACE,MAAK;GACL,WAAU;GACV,cAAa;GACb,OAAO,eAAe,QAAQ;GAC9B,gBAAgB,CAAC;GACjB,WAAW;GACX,UAAU,MAAM;IACd,EAAE,eAAe;IACjB,OAAO,WAAW,EAAE,cAAc,QAAQ,MAAM,CAAC,CAAC;GACpD;GACA,WAAW,GAAG,SAAS;GACvB,GAAI;EACL,CAAA;EACD,oBAAC,iBAAD;GAAiB,OAAM;GAQrB,UAAA,oBAAC,kBAAD;IACE,MAAK;IACL,cACE,SAAS,aAAa,OAAO,iBAAiB;IAEhD,eACE,OAAO;KACL,GAAG;KACH,UAAU,SAAS,aAAa,OAAO,OAAO;IAChD,CAAC;IAGF,UAAA,SAAS;GACM,CAAA;EACH,CAAA;EACjB,oBAAC,QAAD;GAAM,MAAK;GAAS,WAAU;GAC3B,UAAA;EACG,CAAA;CACI,EAAA,CAAA;AAEhB"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * ToggleGroup — set of related Toggles sharing one selection state.
3
+ *
4
+ * `type` (required) is "single" for radio-like exclusive selection or
5
+ * "multiple" for any-number-pressed. variant/size set on the group cascade to
6
+ * every ToggleGroupItem via context, so items don't need to repeat them.
7
+ * Built on the Radix ToggleGroup primitive. Adds `orientation` (vertical
8
+ * stacking) and `spacing` (gap between items — spacing=0 collapses items
9
+ * into one seamless bar with shared borders, like ButtonGroup).
10
+ */
11
+ import * as React from "react";
12
+ import { type VariantProps } from "class-variance-authority";
13
+ import { ToggleGroup as ToggleGroupPrimitive } from "radix-ui";
14
+ import { toggleVariants } from "../../components/ui/toggle";
15
+ declare function ToggleGroup({ className, variant, size, spacing, orientation, children, ...props }: React.ComponentProps<typeof ToggleGroupPrimitive.Root> & VariantProps<typeof toggleVariants> & {
16
+ spacing?: number;
17
+ orientation?: "horizontal" | "vertical";
18
+ }): React.JSX.Element;
19
+ declare function ToggleGroupItem({ className, children, variant, size, ...props }: React.ComponentProps<typeof ToggleGroupPrimitive.Item> & VariantProps<typeof toggleVariants>): React.JSX.Element;
20
+ export { ToggleGroup, ToggleGroupItem };
21
+ /**
22
+ * WHEN TO USE
23
+ * - Text alignment pickers, view density switchers, multi-select formatting toolbars
24
+ * - Any set of related Toggles that should share pressed state, variant, and size
25
+ *
26
+ * NOT WHEN
27
+ * - Switching between views/pages of content — use Tabs
28
+ * - A form field that needs a single mutually-exclusive value — use RadioGroup
29
+ * - Actions that don't hold pressed state — use ButtonGroup
30
+ *
31
+ * KEY RULES
32
+ * - type is required: "single" (only one item pressed at a time, like radio) or "multiple" (any number pressed)
33
+ * - variant/size set on ToggleGroup cascade to every ToggleGroupItem and take priority over a variant/size
34
+ * passed directly on an item
35
+ * - spacing: default 2 (gap between items); spacing={0} collapses items into a seamless segmented
36
+ * control with shared borders and collapsed inner radii
37
+ * - orientation: horizontal (default) | vertical
38
+ * - Each ToggleGroupItem needs its own aria-label (or visible text) when icon-only — none is added automatically
39
+ *
40
+ * USAGE
41
+ * <ToggleGroup type="single" defaultValue="center" variant="outline">
42
+ * <ToggleGroupItem value="left" aria-label="Align left"><AlignLeftIcon /></ToggleGroupItem>
43
+ * <ToggleGroupItem value="center" aria-label="Align center"><AlignCenterIcon /></ToggleGroupItem>
44
+ * <ToggleGroupItem value="right" aria-label="Align right"><AlignRightIcon /></ToggleGroupItem>
45
+ * </ToggleGroup>
46
+ *
47
+ * <ToggleGroup type="multiple" defaultValue={["bold"]}>
48
+ * <ToggleGroupItem value="bold" aria-label="Bold"><BoldIcon /></ToggleGroupItem>
49
+ * <ToggleGroupItem value="italic" aria-label="Italic"><ItalicIcon /></ToggleGroupItem>
50
+ * </ToggleGroup>
51
+ */
52
+ //# sourceMappingURL=toggle-group.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toggle-group.d.ts","sourceRoot":"","sources":["../../../src/components/ui/toggle-group.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,EAAE,WAAW,IAAI,oBAAoB,EAAE,MAAM,UAAU,CAAA;AAG9D,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAcvD,iBAAS,WAAW,CAAC,EACnB,SAAS,EACT,OAAO,EACP,IAAI,EACJ,OAAW,EACX,WAA0B,EAC1B,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,IAAI,CAAC,GACvD,YAAY,CAAC,OAAO,cAAc,CAAC,GAAG;IACpC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAA;CACxC,qBAsBF;AAED,iBAAS,eAAe,CAAC,EACvB,SAAS,EACT,QAAQ,EACR,OAAmB,EACnB,IAAgB,EAChB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,IAAI,CAAC,GACvD,YAAY,CAAC,OAAO,cAAc,CAAC,qBAsBpC;AAED,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,CAAA;AAEvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG"}