@neynar/ui 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (364) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +195 -0
  3. package/dist/components/ui/accordion.d.ts +229 -0
  4. package/dist/components/ui/accordion.d.ts.map +1 -0
  5. package/dist/components/ui/alert-dialog.d.ts +247 -0
  6. package/dist/components/ui/alert-dialog.d.ts.map +1 -0
  7. package/dist/components/ui/alert.d.ts +187 -0
  8. package/dist/components/ui/alert.d.ts.map +1 -0
  9. package/dist/components/ui/aspect-ratio.d.ts +94 -0
  10. package/dist/components/ui/aspect-ratio.d.ts.map +1 -0
  11. package/dist/components/ui/avatar.d.ts +244 -0
  12. package/dist/components/ui/avatar.d.ts.map +1 -0
  13. package/dist/components/ui/badge.d.ts +163 -0
  14. package/dist/components/ui/badge.d.ts.map +1 -0
  15. package/dist/components/ui/breadcrumb.d.ts +281 -0
  16. package/dist/components/ui/breadcrumb.d.ts.map +1 -0
  17. package/dist/components/ui/button.d.ts +129 -0
  18. package/dist/components/ui/button.d.ts.map +1 -0
  19. package/dist/components/ui/calendar.d.ts +169 -0
  20. package/dist/components/ui/calendar.d.ts.map +1 -0
  21. package/dist/components/ui/card.d.ts +365 -0
  22. package/dist/components/ui/card.d.ts.map +1 -0
  23. package/dist/components/ui/carousel.d.ts +369 -0
  24. package/dist/components/ui/carousel.d.ts.map +1 -0
  25. package/dist/components/ui/chart.d.ts +442 -0
  26. package/dist/components/ui/chart.d.ts.map +1 -0
  27. package/dist/components/ui/checkbox.d.ts +88 -0
  28. package/dist/components/ui/checkbox.d.ts.map +1 -0
  29. package/dist/components/ui/collapsible.d.ts +182 -0
  30. package/dist/components/ui/collapsible.d.ts.map +1 -0
  31. package/dist/components/ui/combobox.d.ts +270 -0
  32. package/dist/components/ui/combobox.d.ts.map +1 -0
  33. package/dist/components/ui/command.d.ts +355 -0
  34. package/dist/components/ui/command.d.ts.map +1 -0
  35. package/dist/components/ui/container.d.ts +102 -0
  36. package/dist/components/ui/container.d.ts.map +1 -0
  37. package/dist/components/ui/context-menu.d.ts +339 -0
  38. package/dist/components/ui/context-menu.d.ts.map +1 -0
  39. package/dist/components/ui/date-picker.d.ts +145 -0
  40. package/dist/components/ui/date-picker.d.ts.map +1 -0
  41. package/dist/components/ui/dialog.d.ts +322 -0
  42. package/dist/components/ui/dialog.d.ts.map +1 -0
  43. package/dist/components/ui/drawer.d.ts +154 -0
  44. package/dist/components/ui/drawer.d.ts.map +1 -0
  45. package/dist/components/ui/dropdown-menu.d.ts +349 -0
  46. package/dist/components/ui/dropdown-menu.d.ts.map +1 -0
  47. package/dist/components/ui/empty-state.d.ts +133 -0
  48. package/dist/components/ui/empty-state.d.ts.map +1 -0
  49. package/dist/components/ui/hover-card.d.ts +109 -0
  50. package/dist/components/ui/hover-card.d.ts.map +1 -0
  51. package/dist/components/ui/input.d.ts +89 -0
  52. package/dist/components/ui/input.d.ts.map +1 -0
  53. package/dist/components/ui/label.d.ts +93 -0
  54. package/dist/components/ui/label.d.ts.map +1 -0
  55. package/dist/components/ui/menubar.d.ts +306 -0
  56. package/dist/components/ui/menubar.d.ts.map +1 -0
  57. package/dist/components/ui/navigation-menu.d.ts +318 -0
  58. package/dist/components/ui/navigation-menu.d.ts.map +1 -0
  59. package/dist/components/ui/pagination.d.ts +343 -0
  60. package/dist/components/ui/pagination.d.ts.map +1 -0
  61. package/dist/components/ui/popover.d.ts +178 -0
  62. package/dist/components/ui/popover.d.ts.map +1 -0
  63. package/dist/components/ui/progress.d.ts +64 -0
  64. package/dist/components/ui/progress.d.ts.map +1 -0
  65. package/dist/components/ui/radio-group.d.ts +144 -0
  66. package/dist/components/ui/radio-group.d.ts.map +1 -0
  67. package/dist/components/ui/resizable.d.ts +164 -0
  68. package/dist/components/ui/resizable.d.ts.map +1 -0
  69. package/dist/components/ui/scroll-area.d.ts +82 -0
  70. package/dist/components/ui/scroll-area.d.ts.map +1 -0
  71. package/dist/components/ui/select.d.ts +316 -0
  72. package/dist/components/ui/select.d.ts.map +1 -0
  73. package/dist/components/ui/separator.d.ts +80 -0
  74. package/dist/components/ui/separator.d.ts.map +1 -0
  75. package/dist/components/ui/sheet.d.ts +346 -0
  76. package/dist/components/ui/sheet.d.ts.map +1 -0
  77. package/dist/components/ui/sidebar.d.ts +1561 -0
  78. package/dist/components/ui/sidebar.d.ts.map +1 -0
  79. package/dist/components/ui/skeleton.d.ts +66 -0
  80. package/dist/components/ui/skeleton.d.ts.map +1 -0
  81. package/dist/components/ui/slider.d.ts +95 -0
  82. package/dist/components/ui/slider.d.ts.map +1 -0
  83. package/dist/components/ui/sonner.d.ts +101 -0
  84. package/dist/components/ui/sonner.d.ts.map +1 -0
  85. package/dist/components/ui/stack.d.ts +192 -0
  86. package/dist/components/ui/stack.d.ts.map +1 -0
  87. package/dist/components/ui/stories/accordion.stories.d.ts +71 -0
  88. package/dist/components/ui/stories/accordion.stories.d.ts.map +1 -0
  89. package/dist/components/ui/stories/alert-dialog.stories.d.ts +39 -0
  90. package/dist/components/ui/stories/alert-dialog.stories.d.ts.map +1 -0
  91. package/dist/components/ui/stories/alert.stories.d.ts +48 -0
  92. package/dist/components/ui/stories/alert.stories.d.ts.map +1 -0
  93. package/dist/components/ui/stories/aspect-ratio.stories.d.ts +53 -0
  94. package/dist/components/ui/stories/aspect-ratio.stories.d.ts.map +1 -0
  95. package/dist/components/ui/stories/avatar.stories.d.ts +49 -0
  96. package/dist/components/ui/stories/avatar.stories.d.ts.map +1 -0
  97. package/dist/components/ui/stories/badge.stories.d.ts +64 -0
  98. package/dist/components/ui/stories/badge.stories.d.ts.map +1 -0
  99. package/dist/components/ui/stories/breadcrumb.stories.d.ts +27 -0
  100. package/dist/components/ui/stories/breadcrumb.stories.d.ts.map +1 -0
  101. package/dist/components/ui/stories/button.stories.d.ts +92 -0
  102. package/dist/components/ui/stories/button.stories.d.ts.map +1 -0
  103. package/dist/components/ui/stories/calendar.stories.d.ts +94 -0
  104. package/dist/components/ui/stories/calendar.stories.d.ts.map +1 -0
  105. package/dist/components/ui/stories/card.stories.d.ts +29 -0
  106. package/dist/components/ui/stories/card.stories.d.ts.map +1 -0
  107. package/dist/components/ui/stories/carousel.stories.d.ts +42 -0
  108. package/dist/components/ui/stories/carousel.stories.d.ts.map +1 -0
  109. package/dist/components/ui/stories/chart.stories.d.ts +51 -0
  110. package/dist/components/ui/stories/chart.stories.d.ts.map +1 -0
  111. package/dist/components/ui/stories/checkbox.stories.d.ts +72 -0
  112. package/dist/components/ui/stories/checkbox.stories.d.ts.map +1 -0
  113. package/dist/components/ui/stories/cn.stories.d.ts +19 -0
  114. package/dist/components/ui/stories/cn.stories.d.ts.map +1 -0
  115. package/dist/components/ui/stories/collapsible.stories.d.ts +51 -0
  116. package/dist/components/ui/stories/collapsible.stories.d.ts.map +1 -0
  117. package/dist/components/ui/stories/colors.stories.d.ts +31 -0
  118. package/dist/components/ui/stories/colors.stories.d.ts.map +1 -0
  119. package/dist/components/ui/stories/combobox.stories.d.ts +89 -0
  120. package/dist/components/ui/stories/combobox.stories.d.ts.map +1 -0
  121. package/dist/components/ui/stories/command.stories.d.ts +69 -0
  122. package/dist/components/ui/stories/command.stories.d.ts.map +1 -0
  123. package/dist/components/ui/stories/container.stories.d.ts +42 -0
  124. package/dist/components/ui/stories/container.stories.d.ts.map +1 -0
  125. package/dist/components/ui/stories/context-menu.stories.d.ts +32 -0
  126. package/dist/components/ui/stories/context-menu.stories.d.ts.map +1 -0
  127. package/dist/components/ui/stories/date-picker.stories.d.ts +67 -0
  128. package/dist/components/ui/stories/date-picker.stories.d.ts.map +1 -0
  129. package/dist/components/ui/stories/dialog.stories.d.ts +48 -0
  130. package/dist/components/ui/stories/dialog.stories.d.ts.map +1 -0
  131. package/dist/components/ui/stories/drawer.stories.d.ts +33 -0
  132. package/dist/components/ui/stories/drawer.stories.d.ts.map +1 -0
  133. package/dist/components/ui/stories/dropdown-menu.stories.d.ts +31 -0
  134. package/dist/components/ui/stories/dropdown-menu.stories.d.ts.map +1 -0
  135. package/dist/components/ui/stories/empty-state.stories.d.ts +74 -0
  136. package/dist/components/ui/stories/empty-state.stories.d.ts.map +1 -0
  137. package/dist/components/ui/stories/hover-card.stories.d.ts +35 -0
  138. package/dist/components/ui/stories/hover-card.stories.d.ts.map +1 -0
  139. package/dist/components/ui/stories/input.stories.d.ts +69 -0
  140. package/dist/components/ui/stories/input.stories.d.ts.map +1 -0
  141. package/dist/components/ui/stories/label.stories.d.ts +47 -0
  142. package/dist/components/ui/stories/label.stories.d.ts.map +1 -0
  143. package/dist/components/ui/stories/menubar.stories.d.ts +39 -0
  144. package/dist/components/ui/stories/menubar.stories.d.ts.map +1 -0
  145. package/dist/components/ui/stories/navigation-menu.stories.d.ts +44 -0
  146. package/dist/components/ui/stories/navigation-menu.stories.d.ts.map +1 -0
  147. package/dist/components/ui/stories/pagination.stories.d.ts +33 -0
  148. package/dist/components/ui/stories/pagination.stories.d.ts.map +1 -0
  149. package/dist/components/ui/stories/popover.stories.d.ts +36 -0
  150. package/dist/components/ui/stories/popover.stories.d.ts.map +1 -0
  151. package/dist/components/ui/stories/progress.stories.d.ts +38 -0
  152. package/dist/components/ui/stories/progress.stories.d.ts.map +1 -0
  153. package/dist/components/ui/stories/radio-group.stories.d.ts +76 -0
  154. package/dist/components/ui/stories/radio-group.stories.d.ts.map +1 -0
  155. package/dist/components/ui/stories/resizable.stories.d.ts +49 -0
  156. package/dist/components/ui/stories/resizable.stories.d.ts.map +1 -0
  157. package/dist/components/ui/stories/scroll-area.stories.d.ts +35 -0
  158. package/dist/components/ui/stories/scroll-area.stories.d.ts.map +1 -0
  159. package/dist/components/ui/stories/select.stories.d.ts +51 -0
  160. package/dist/components/ui/stories/select.stories.d.ts.map +1 -0
  161. package/dist/components/ui/stories/separator.stories.d.ts +58 -0
  162. package/dist/components/ui/stories/separator.stories.d.ts.map +1 -0
  163. package/dist/components/ui/stories/sheet.stories.d.ts +43 -0
  164. package/dist/components/ui/stories/sheet.stories.d.ts.map +1 -0
  165. package/dist/components/ui/stories/sidebar.stories.d.ts +60 -0
  166. package/dist/components/ui/stories/sidebar.stories.d.ts.map +1 -0
  167. package/dist/components/ui/stories/skeleton.stories.d.ts +42 -0
  168. package/dist/components/ui/stories/skeleton.stories.d.ts.map +1 -0
  169. package/dist/components/ui/stories/slider.stories.d.ts +99 -0
  170. package/dist/components/ui/stories/slider.stories.d.ts.map +1 -0
  171. package/dist/components/ui/stories/sonner.stories.d.ts +9 -0
  172. package/dist/components/ui/stories/sonner.stories.d.ts.map +1 -0
  173. package/dist/components/ui/stories/stack.stories.d.ts +39 -0
  174. package/dist/components/ui/stories/stack.stories.d.ts.map +1 -0
  175. package/dist/components/ui/stories/switch.stories.d.ts +71 -0
  176. package/dist/components/ui/stories/switch.stories.d.ts.map +1 -0
  177. package/dist/components/ui/stories/table.stories.d.ts +40 -0
  178. package/dist/components/ui/stories/table.stories.d.ts.map +1 -0
  179. package/dist/components/ui/stories/tabs.stories.d.ts +62 -0
  180. package/dist/components/ui/stories/tabs.stories.d.ts.map +1 -0
  181. package/dist/components/ui/stories/text-field.stories.d.ts +78 -0
  182. package/dist/components/ui/stories/text-field.stories.d.ts.map +1 -0
  183. package/dist/components/ui/stories/textarea.stories.d.ts +57 -0
  184. package/dist/components/ui/stories/textarea.stories.d.ts.map +1 -0
  185. package/dist/components/ui/stories/theme-toggle.stories.d.ts +71 -0
  186. package/dist/components/ui/stories/theme-toggle.stories.d.ts.map +1 -0
  187. package/dist/components/ui/stories/theme.stories.d.ts +51 -0
  188. package/dist/components/ui/stories/theme.stories.d.ts.map +1 -0
  189. package/dist/components/ui/stories/toggle-group.stories.d.ts +71 -0
  190. package/dist/components/ui/stories/toggle-group.stories.d.ts.map +1 -0
  191. package/dist/components/ui/stories/toggle.stories.d.ts +78 -0
  192. package/dist/components/ui/stories/toggle.stories.d.ts.map +1 -0
  193. package/dist/components/ui/stories/tooltip.stories.d.ts +37 -0
  194. package/dist/components/ui/stories/tooltip.stories.d.ts.map +1 -0
  195. package/dist/components/ui/stories/typography.stories.d.ts +137 -0
  196. package/dist/components/ui/stories/typography.stories.d.ts.map +1 -0
  197. package/dist/components/ui/stories/use-mobile.stories.d.ts +20 -0
  198. package/dist/components/ui/stories/use-mobile.stories.d.ts.map +1 -0
  199. package/dist/components/ui/stories/use-theme.stories.d.ts +23 -0
  200. package/dist/components/ui/stories/use-theme.stories.d.ts.map +1 -0
  201. package/dist/components/ui/switch.d.ts +84 -0
  202. package/dist/components/ui/switch.d.ts.map +1 -0
  203. package/dist/components/ui/table.d.ts +321 -0
  204. package/dist/components/ui/table.d.ts.map +1 -0
  205. package/dist/components/ui/tabs.d.ts +260 -0
  206. package/dist/components/ui/tabs.d.ts.map +1 -0
  207. package/dist/components/ui/text-field.d.ts +157 -0
  208. package/dist/components/ui/text-field.d.ts.map +1 -0
  209. package/dist/components/ui/textarea.d.ts +84 -0
  210. package/dist/components/ui/textarea.d.ts.map +1 -0
  211. package/dist/components/ui/theme-toggle.d.ts +105 -0
  212. package/dist/components/ui/theme-toggle.d.ts.map +1 -0
  213. package/dist/components/ui/theme.d.ts +110 -0
  214. package/dist/components/ui/theme.d.ts.map +1 -0
  215. package/dist/components/ui/toggle-group.d.ts +133 -0
  216. package/dist/components/ui/toggle-group.d.ts.map +1 -0
  217. package/dist/components/ui/toggle.d.ts +84 -0
  218. package/dist/components/ui/toggle.d.ts.map +1 -0
  219. package/dist/components/ui/tooltip.d.ts +202 -0
  220. package/dist/components/ui/tooltip.d.ts.map +1 -0
  221. package/dist/components/ui/typography.d.ts +287 -0
  222. package/dist/components/ui/typography.d.ts.map +1 -0
  223. package/dist/hooks/use-mobile.d.ts +74 -0
  224. package/dist/hooks/use-mobile.d.ts.map +1 -0
  225. package/dist/hooks/use-theme.d.ts +142 -0
  226. package/dist/hooks/use-theme.d.ts.map +1 -0
  227. package/dist/index.d.ts +57 -0
  228. package/dist/index.d.ts.map +1 -0
  229. package/dist/index.js +27498 -0
  230. package/dist/index.js.map +1 -0
  231. package/dist/lib/utils.d.ts +43 -0
  232. package/dist/lib/utils.d.ts.map +1 -0
  233. package/dist/tsconfig.tsbuildinfo +1 -0
  234. package/docs/llm/colors.md +273 -0
  235. package/docs/llm/components/buttons.md +68 -0
  236. package/docs/llm/components/cards.md +53 -0
  237. package/docs/llm/components/display.md +134 -0
  238. package/docs/llm/components/feedback.md +96 -0
  239. package/docs/llm/components/forms.md +90 -0
  240. package/docs/llm/components/layout.md +59 -0
  241. package/docs/llm/components/menus.md +70 -0
  242. package/docs/llm/components/navigation.md +80 -0
  243. package/docs/llm/components/overlays.md +83 -0
  244. package/docs/llm/components/tables.md +73 -0
  245. package/docs/llm/components/typography.md +199 -0
  246. package/docs/llm/components/utilities.md +114 -0
  247. package/docs/llm/guide.md +165 -0
  248. package/llms.txt +122 -0
  249. package/package.json +104 -0
  250. package/src/components/ui/accordion.tsx +285 -0
  251. package/src/components/ui/alert-dialog.tsx +387 -0
  252. package/src/components/ui/alert.tsx +243 -0
  253. package/src/components/ui/aspect-ratio.tsx +99 -0
  254. package/src/components/ui/avatar.tsx +288 -0
  255. package/src/components/ui/badge.tsx +205 -0
  256. package/src/components/ui/breadcrumb.tsx +378 -0
  257. package/src/components/ui/button.tsx +195 -0
  258. package/src/components/ui/calendar.tsx +371 -0
  259. package/src/components/ui/card.tsx +447 -0
  260. package/src/components/ui/carousel.tsx +624 -0
  261. package/src/components/ui/chart.tsx +802 -0
  262. package/src/components/ui/checkbox.tsx +113 -0
  263. package/src/components/ui/collapsible.tsx +207 -0
  264. package/src/components/ui/combobox.tsx +373 -0
  265. package/src/components/ui/command.tsx +518 -0
  266. package/src/components/ui/container.tsx +114 -0
  267. package/src/components/ui/context-menu.tsx +563 -0
  268. package/src/components/ui/date-picker.tsx +213 -0
  269. package/src/components/ui/dialog.tsx +447 -0
  270. package/src/components/ui/drawer.tsx +273 -0
  271. package/src/components/ui/dropdown-menu.tsx +578 -0
  272. package/src/components/ui/empty-state.tsx +145 -0
  273. package/src/components/ui/hover-card.tsx +144 -0
  274. package/src/components/ui/input.tsx +106 -0
  275. package/src/components/ui/label.tsx +110 -0
  276. package/src/components/ui/menubar.tsx +553 -0
  277. package/src/components/ui/navigation-menu.tsx +471 -0
  278. package/src/components/ui/pagination.tsx +456 -0
  279. package/src/components/ui/popover.tsx +216 -0
  280. package/src/components/ui/progress.tsx +88 -0
  281. package/src/components/ui/radio-group.tsx +183 -0
  282. package/src/components/ui/resizable.tsx +209 -0
  283. package/src/components/ui/scroll-area.tsx +132 -0
  284. package/src/components/ui/select.tsx +485 -0
  285. package/src/components/ui/separator.tsx +101 -0
  286. package/src/components/ui/sheet.tsx +495 -0
  287. package/src/components/ui/sidebar.tsx +2211 -0
  288. package/src/components/ui/skeleton.tsx +76 -0
  289. package/src/components/ui/slider.tsx +147 -0
  290. package/src/components/ui/sonner.tsx +120 -0
  291. package/src/components/ui/stack.tsx +180 -0
  292. package/src/components/ui/stories/accordion.stories.tsx +429 -0
  293. package/src/components/ui/stories/alert-dialog.stories.tsx +519 -0
  294. package/src/components/ui/stories/alert.stories.tsx +228 -0
  295. package/src/components/ui/stories/aspect-ratio.stories.tsx +200 -0
  296. package/src/components/ui/stories/avatar.stories.tsx +317 -0
  297. package/src/components/ui/stories/badge.stories.tsx +260 -0
  298. package/src/components/ui/stories/breadcrumb.stories.tsx +482 -0
  299. package/src/components/ui/stories/button.stories.tsx +266 -0
  300. package/src/components/ui/stories/calendar.stories.tsx +375 -0
  301. package/src/components/ui/stories/card.stories.tsx +308 -0
  302. package/src/components/ui/stories/carousel.stories.tsx +328 -0
  303. package/src/components/ui/stories/chart.stories.tsx +430 -0
  304. package/src/components/ui/stories/checkbox.stories.tsx +297 -0
  305. package/src/components/ui/stories/cn.stories.tsx +433 -0
  306. package/src/components/ui/stories/collapsible.stories.tsx +256 -0
  307. package/src/components/ui/stories/colors.stories.tsx +502 -0
  308. package/src/components/ui/stories/combobox.stories.tsx +301 -0
  309. package/src/components/ui/stories/command.stories.tsx +632 -0
  310. package/src/components/ui/stories/container.stories.tsx +250 -0
  311. package/src/components/ui/stories/context-menu.stories.tsx +446 -0
  312. package/src/components/ui/stories/date-picker.stories.tsx +378 -0
  313. package/src/components/ui/stories/dialog.stories.tsx +535 -0
  314. package/src/components/ui/stories/drawer.stories.tsx +364 -0
  315. package/src/components/ui/stories/dropdown-menu.stories.tsx +374 -0
  316. package/src/components/ui/stories/empty-state.stories.tsx +244 -0
  317. package/src/components/ui/stories/hover-card.stories.tsx +355 -0
  318. package/src/components/ui/stories/input.stories.tsx +289 -0
  319. package/src/components/ui/stories/label.stories.tsx +294 -0
  320. package/src/components/ui/stories/menubar.stories.tsx +764 -0
  321. package/src/components/ui/stories/navigation-menu.stories.tsx +539 -0
  322. package/src/components/ui/stories/pagination.stories.tsx +604 -0
  323. package/src/components/ui/stories/popover.stories.tsx +392 -0
  324. package/src/components/ui/stories/progress.stories.tsx +218 -0
  325. package/src/components/ui/stories/radio-group.stories.tsx +400 -0
  326. package/src/components/ui/stories/resizable.stories.tsx +417 -0
  327. package/src/components/ui/stories/scroll-area.stories.tsx +180 -0
  328. package/src/components/ui/stories/select.stories.tsx +389 -0
  329. package/src/components/ui/stories/separator.stories.tsx +192 -0
  330. package/src/components/ui/stories/sheet.stories.tsx +468 -0
  331. package/src/components/ui/stories/sidebar.stories.tsx +731 -0
  332. package/src/components/ui/stories/skeleton.stories.tsx +216 -0
  333. package/src/components/ui/stories/slider.stories.tsx +321 -0
  334. package/src/components/ui/stories/sonner.stories.tsx +373 -0
  335. package/src/components/ui/stories/stack.stories.tsx +222 -0
  336. package/src/components/ui/stories/switch.stories.tsx +202 -0
  337. package/src/components/ui/stories/table.stories.tsx +541 -0
  338. package/src/components/ui/stories/tabs.stories.tsx +544 -0
  339. package/src/components/ui/stories/text-field.stories.tsx +280 -0
  340. package/src/components/ui/stories/textarea.stories.tsx +245 -0
  341. package/src/components/ui/stories/theme-toggle.stories.tsx +275 -0
  342. package/src/components/ui/stories/theme.stories.tsx +412 -0
  343. package/src/components/ui/stories/toggle-group.stories.tsx +337 -0
  344. package/src/components/ui/stories/toggle.stories.tsx +325 -0
  345. package/src/components/ui/stories/tooltip.stories.tsx +444 -0
  346. package/src/components/ui/stories/typography.stories.tsx +1586 -0
  347. package/src/components/ui/stories/use-mobile.stories.tsx +420 -0
  348. package/src/components/ui/stories/use-theme.stories.tsx +531 -0
  349. package/src/components/ui/switch.tsx +106 -0
  350. package/src/components/ui/table.tsx +424 -0
  351. package/src/components/ui/tabs.tsx +316 -0
  352. package/src/components/ui/text-field.tsx +206 -0
  353. package/src/components/ui/textarea.tsx +98 -0
  354. package/src/components/ui/theme-toggle.tsx +185 -0
  355. package/src/components/ui/theme.tsx +148 -0
  356. package/src/components/ui/toggle-group.tsx +196 -0
  357. package/src/components/ui/toggle.tsx +115 -0
  358. package/src/components/ui/tooltip.tsx +253 -0
  359. package/src/components/ui/typography.tsx +468 -0
  360. package/src/hooks/use-mobile.ts +91 -0
  361. package/src/hooks/use-theme.ts +319 -0
  362. package/src/index.ts +77 -0
  363. package/src/lib/utils.ts +57 -0
  364. package/src/styles/globals.css +160 -0
@@ -0,0 +1,113 @@
1
+ import * as React from "react";
2
+ import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
3
+ import { CheckIcon } from "lucide-react";
4
+
5
+ import { cn } from "@/lib/utils";
6
+
7
+ /**
8
+ * A versatile checkbox component for binary and multi-selection interfaces
9
+ *
10
+ * Built on Radix UI Checkbox primitive, this component provides a fully accessible
11
+ * checkbox with support for controlled/uncontrolled states, indeterminate state,
12
+ * and comprehensive form integration. Features consistent styling with the design
13
+ * system and proper keyboard navigation.
14
+ *
15
+ * @example Basic usage
16
+ * ```tsx
17
+ * <div className="flex items-center space-x-2">
18
+ * <Checkbox id="terms" />
19
+ * <Label htmlFor="terms">Accept terms and conditions</Label>
20
+ * </div>
21
+ * ```
22
+ *
23
+ * @example Controlled checkbox with state management
24
+ * ```tsx
25
+ * const [checked, setChecked] = useState(false);
26
+ *
27
+ * <div className="flex items-center space-x-2">
28
+ * <Checkbox
29
+ * id="notifications"
30
+ * checked={checked}
31
+ * onCheckedChange={setChecked}
32
+ * />
33
+ * <Label htmlFor="notifications">Send me notifications</Label>
34
+ * </div>
35
+ * ```
36
+ *
37
+ * @example Indeterminate state for parent-child relationships
38
+ * ```tsx
39
+ * const [parentState, setParentState] = useState("indeterminate");
40
+ *
41
+ * <Checkbox
42
+ * checked={parentState}
43
+ * onCheckedChange={(checked) => {
44
+ * setParentState(checked);
45
+ * // Update child checkboxes accordingly
46
+ * }}
47
+ * />
48
+ * ```
49
+ *
50
+ * @example Form integration with multiple selections
51
+ * ```tsx
52
+ * const [preferences, setPreferences] = useState([]);
53
+ *
54
+ * {options.map((option) => (
55
+ * <div key={option.id} className="flex items-center space-x-2">
56
+ * <Checkbox
57
+ * id={option.id}
58
+ * name="preferences"
59
+ * value={option.value}
60
+ * checked={preferences.includes(option.id)}
61
+ * onCheckedChange={(checked) => {
62
+ * setPreferences(prev =>
63
+ * checked
64
+ * ? [...prev, option.id]
65
+ * : prev.filter(id => id !== option.id)
66
+ * );
67
+ * }}
68
+ * />
69
+ * <Label htmlFor={option.id}>{option.label}</Label>
70
+ * </div>
71
+ * ))}
72
+ * ```
73
+ *
74
+ * @see {@link https://ui.shadcn.com/docs/components/checkbox} for design patterns
75
+ * @since 1.0.0
76
+ * @see {@link Label} for accessible labeling
77
+ * @see {@link Switch} for boolean toggles
78
+ * @see {@link RadioGroup} for single selection scenarios
79
+ *
80
+ * @param className - Additional CSS classes to apply to the checkbox
81
+ * @param checked - Controlled checked state (boolean | "indeterminate")
82
+ * @param defaultChecked - Default checked state for uncontrolled usage
83
+ * @param onCheckedChange - Callback when the checked state changes
84
+ * @param disabled - Whether the checkbox is disabled
85
+ * @param required - Whether the checkbox is required for form validation
86
+ * @param name - Form field name for form submission
87
+ * @param value - Form field value for form submission
88
+ * @param id - Unique identifier, used with Label htmlFor attribute
89
+ */
90
+ function Checkbox({
91
+ className,
92
+ ...props
93
+ }: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
94
+ return (
95
+ <CheckboxPrimitive.Root
96
+ data-slot="checkbox"
97
+ className={cn(
98
+ "peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
99
+ className,
100
+ )}
101
+ {...props}
102
+ >
103
+ <CheckboxPrimitive.Indicator
104
+ data-slot="checkbox-indicator"
105
+ className="flex items-center justify-center text-current transition-none"
106
+ >
107
+ <CheckIcon className="size-3.5" />
108
+ </CheckboxPrimitive.Indicator>
109
+ </CheckboxPrimitive.Root>
110
+ );
111
+ }
112
+
113
+ export { Checkbox };
@@ -0,0 +1,207 @@
1
+ import * as React from "react";
2
+ import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
3
+
4
+ /**
5
+ * An interactive component which expands/collapses a panel
6
+ *
7
+ * A flexible container component built on Radix UI primitives that allows users to
8
+ * toggle the visibility of content sections. Unlike Accordion, Collapsible provides
9
+ * more flexibility without enforcing specific visual structures. Perfect for FAQ sections,
10
+ * expandable details, settings panels, and any content that should be initially hidden.
11
+ *
12
+ * @component
13
+ * @example
14
+ * ```tsx
15
+ * // Basic controlled collapsible
16
+ * const [isOpen, setIsOpen] = React.useState(false)
17
+ *
18
+ * <Collapsible open={isOpen} onOpenChange={setIsOpen}>
19
+ * <CollapsibleTrigger asChild>
20
+ * <Button variant="outline">
21
+ * Can I use this in my project?
22
+ * <ChevronDown className="h-4 w-4" />
23
+ * </Button>
24
+ * </CollapsibleTrigger>
25
+ * <CollapsibleContent>
26
+ * <div className="p-4 border rounded-md bg-muted/50">
27
+ * Yes. Free to use for personal and commercial projects.
28
+ * No attribution required.
29
+ * </div>
30
+ * </CollapsibleContent>
31
+ * </Collapsible>
32
+ * ```
33
+ *
34
+ * @example
35
+ * ```tsx
36
+ * // Uncontrolled collapsible with custom styling
37
+ * <Collapsible className="w-full space-y-2">
38
+ * <div className="flex items-center justify-between">
39
+ * <h4 className="text-sm font-semibold">@radix-ui/primitives</h4>
40
+ * <CollapsibleTrigger asChild>
41
+ * <Button variant="ghost" size="sm">
42
+ * <ChevronDown className="h-4 w-4" />
43
+ * <span className="sr-only">Toggle</span>
44
+ * </Button>
45
+ * </CollapsibleTrigger>
46
+ * </div>
47
+ * <CollapsibleContent className="space-y-2">
48
+ * <div className="rounded-md border px-4 py-2 text-sm shadow-sm">
49
+ * @radix-ui/react-collapsible
50
+ * </div>
51
+ * </CollapsibleContent>
52
+ * </Collapsible>
53
+ * ```
54
+ *
55
+ * @example
56
+ * ```tsx
57
+ * // FAQ-style implementation
58
+ * <Collapsible>
59
+ * <CollapsibleTrigger className="flex w-full items-center justify-between py-4 text-sm font-medium transition-all hover:underline">
60
+ * How do I get started?
61
+ * <ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
62
+ * </CollapsibleTrigger>
63
+ * <CollapsibleContent className="overflow-hidden text-sm transition-all">
64
+ * <div className="pb-4 pt-0">
65
+ * Getting started is easy! Simply follow our quick setup guide...
66
+ * </div>
67
+ * </CollapsibleContent>
68
+ * </Collapsible>
69
+ * ```
70
+ *
71
+ * @accessibility
72
+ * - Full keyboard navigation support (Space/Enter to toggle)
73
+ * - ARIA expanded state automatically managed
74
+ * - Content properly hidden from screen readers when collapsed
75
+ * - Focus management and screen reader announcements
76
+ * - Built on WAI-ARIA design patterns for disclosure widgets
77
+ *
78
+ * @see {@link https://ui.shadcn.com/docs/components/collapsible} for design patterns
79
+ * @see {@link CollapsibleTrigger} - Interactive element to toggle visibility
80
+ * @see {@link CollapsibleContent} - Content container that can be hidden/shown
81
+ * @since 1.0.0
82
+ */
83
+ function Collapsible({
84
+ ...props
85
+ }: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
86
+ return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />;
87
+ }
88
+
89
+ /**
90
+ * Interactive trigger element that toggles collapsible content visibility
91
+ *
92
+ * The clickable element that controls the expanded/collapsed state of the associated
93
+ * CollapsibleContent. Supports the asChild prop for composition with custom elements
94
+ * like buttons or other interactive components.
95
+ *
96
+ * @component
97
+ * @example
98
+ * ```tsx
99
+ * // Simple text trigger
100
+ * <CollapsibleTrigger>
101
+ * Can I use this in my project?
102
+ * </CollapsibleTrigger>
103
+ * ```
104
+ *
105
+ * @example
106
+ * ```tsx
107
+ * // Composed with Button component
108
+ * <CollapsibleTrigger asChild>
109
+ * <Button variant="outline" className="w-full justify-between">
110
+ * Advanced Settings
111
+ * <ChevronDown className="h-4 w-4" />
112
+ * </Button>
113
+ * </CollapsibleTrigger>
114
+ * ```
115
+ *
116
+ * @example
117
+ * ```tsx
118
+ * // Custom styled trigger
119
+ * <CollapsibleTrigger className="flex w-full items-center justify-between py-4 text-left text-sm font-medium transition-all hover:underline">
120
+ * How does it work?
121
+ * <ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
122
+ * </CollapsibleTrigger>
123
+ * ```
124
+ *
125
+ * @accessibility
126
+ * - Keyboard activation with Space or Enter keys
127
+ * - aria-expanded attribute reflects current expanded state
128
+ * - aria-controls connects trigger to its content panel
129
+ * - Role of button for screen reader compatibility
130
+ * - Focus visible indicator for keyboard navigation
131
+ *
132
+ * @since 1.0.0
133
+ */
134
+ function CollapsibleTrigger({
135
+ ...props
136
+ }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
137
+ return (
138
+ <CollapsiblePrimitive.CollapsibleTrigger
139
+ data-slot="collapsible-trigger"
140
+ {...props}
141
+ />
142
+ );
143
+ }
144
+
145
+ /**
146
+ * Content container that can be expanded or collapsed
147
+ *
148
+ * The panel containing content that is shown or hidden based on the collapsible state.
149
+ * Automatically handles smooth animations, height transitions, and accessibility attributes
150
+ * when toggling between expanded and collapsed states.
151
+ *
152
+ * @component
153
+ * @example
154
+ * ```tsx
155
+ * // Basic content panel
156
+ * <CollapsibleContent>
157
+ * <div className="rounded-md border px-4 py-2 text-sm">
158
+ * Yes. Free to use for personal and commercial projects.
159
+ * No attribution required.
160
+ * </div>
161
+ * </CollapsibleContent>
162
+ * ```
163
+ *
164
+ * @example
165
+ * ```tsx
166
+ * // Content with custom styling and multiple elements
167
+ * <CollapsibleContent className="space-y-2">
168
+ * <div className="rounded-md border px-4 py-3 font-mono text-sm">
169
+ * @radix-ui/react-collapsible
170
+ * </div>
171
+ * <div className="rounded-md border px-4 py-3 font-mono text-sm">
172
+ * @radix-ui/react-dialog
173
+ * </div>
174
+ * </CollapsibleContent>
175
+ * ```
176
+ *
177
+ * @example
178
+ * ```tsx
179
+ * // Content with overflow handling
180
+ * <CollapsibleContent className="overflow-hidden text-sm transition-all data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down">
181
+ * <div className="pb-4 pt-0">
182
+ * Content with custom animations and overflow handling.
183
+ * </div>
184
+ * </CollapsibleContent>
185
+ * ```
186
+ *
187
+ * @accessibility
188
+ * - Automatically hidden from accessibility tree when collapsed
189
+ * - Smooth height animations with data-state attributes
190
+ * - Properly associated with trigger via aria-controls
191
+ * - Supports custom animation classes and transitions
192
+ * - Content is announced by screen readers when expanded
193
+ *
194
+ * @since 1.0.0
195
+ */
196
+ function CollapsibleContent({
197
+ ...props
198
+ }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
199
+ return (
200
+ <CollapsiblePrimitive.CollapsibleContent
201
+ data-slot="collapsible-content"
202
+ {...props}
203
+ />
204
+ );
205
+ }
206
+
207
+ export { Collapsible, CollapsibleTrigger, CollapsibleContent };
@@ -0,0 +1,373 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { Check, ChevronsUpDown } from "lucide-react";
5
+
6
+ import { cn } from "@/lib/utils";
7
+ import { Button } from "@/components/ui/button";
8
+ import {
9
+ Command,
10
+ CommandEmpty,
11
+ CommandGroup,
12
+ CommandInput,
13
+ CommandItem,
14
+ CommandList,
15
+ } from "@/components/ui/command";
16
+ import {
17
+ Popover,
18
+ PopoverContent,
19
+ PopoverTrigger,
20
+ } from "@/components/ui/popover";
21
+
22
+ /**
23
+ * Option structure for combobox items
24
+ *
25
+ * Defines the shape of each option that can be displayed and selected
26
+ * in the combobox dropdown list.
27
+ *
28
+ * @since 1.0.0
29
+ */
30
+ export type ComboboxOption = {
31
+ /**
32
+ * Unique identifier for this option
33
+ *
34
+ * Used for selection tracking and should be unique within the options array.
35
+ */
36
+ value: string;
37
+
38
+ /**
39
+ * Display text shown to users in the dropdown
40
+ *
41
+ * Should be descriptive and help users understand what this option represents.
42
+ */
43
+ label: string;
44
+
45
+ /**
46
+ * Whether this option is disabled and cannot be selected
47
+ *
48
+ * Disabled options remain visible but are not selectable and appear dimmed.
49
+ *
50
+ * @default false
51
+ */
52
+ disabled?: boolean;
53
+ };
54
+
55
+ /**
56
+ * Properties for the Combobox component
57
+ *
58
+ * Comprehensive configuration options for customizing combobox behavior,
59
+ * appearance, and interaction patterns.
60
+ *
61
+ * @since 1.0.0
62
+ */
63
+ export type ComboboxProps = {
64
+ /**
65
+ * Array of options to display in the combobox dropdown
66
+ *
67
+ * Each option should have a unique value and descriptive label.
68
+ * Options can be disabled to prevent selection while remaining visible.
69
+ */
70
+ options: ComboboxOption[];
71
+
72
+ /**
73
+ * Currently selected value from the options array
74
+ *
75
+ * Should match the value property of one of the provided options.
76
+ * Use with onValueChange for controlled component behavior.
77
+ */
78
+ value?: string;
79
+
80
+ /**
81
+ * Callback fired when the selected value changes
82
+ *
83
+ * Receives the new selected value as a string. If the same option
84
+ * is clicked again, an empty string is passed to allow deselection.
85
+ *
86
+ * @param value - The newly selected option value or empty string for deselection
87
+ */
88
+ onValueChange?: (value: string) => void;
89
+
90
+ /**
91
+ * Placeholder text displayed when no option is selected
92
+ *
93
+ * Should be descriptive and help users understand what they're selecting.
94
+ *
95
+ * @default "Select option..."
96
+ */
97
+ placeholder?: string;
98
+
99
+ /**
100
+ * Text displayed when search query returns no matching options
101
+ *
102
+ * Customize this message to match your application's tone and context.
103
+ *
104
+ * @default "No option found."
105
+ */
106
+ emptyText?: string;
107
+
108
+ /**
109
+ * Placeholder text for the search input field
110
+ *
111
+ * Should guide users on how to search effectively within your option set.
112
+ *
113
+ * @default "Search options..."
114
+ */
115
+ searchPlaceholder?: string;
116
+
117
+ /**
118
+ * Additional CSS classes for the root container element
119
+ *
120
+ * Use for custom spacing, positioning, or layout adjustments.
121
+ */
122
+ className?: string;
123
+
124
+ /**
125
+ * Whether the entire combobox is disabled
126
+ *
127
+ * When disabled, the trigger button cannot be clicked and the
128
+ * dropdown cannot be opened. Use individual option.disabled for
129
+ * granular control.
130
+ *
131
+ * @default false
132
+ */
133
+ disabled?: boolean;
134
+
135
+ /**
136
+ * Additional CSS classes for the trigger button
137
+ *
138
+ * Commonly used to control width (e.g., "w-[300px]"), styling variants,
139
+ * or responsive behavior. For best UX, consider matching popoverClassName width.
140
+ *
141
+ * @example "w-full sm:w-[280px] border-primary/50"
142
+ */
143
+ buttonClassName?: string;
144
+
145
+ /**
146
+ * Additional CSS classes for the popover dropdown content
147
+ *
148
+ * Should typically match buttonClassName width for consistent alignment.
149
+ * Use to control positioning, width, and styling of the dropdown.
150
+ *
151
+ * @example "w-full sm:w-[280px] border-primary/50"
152
+ */
153
+ popoverClassName?: string;
154
+ };
155
+
156
+ /**
157
+ * Searchable dropdown selection component with typeahead functionality
158
+ *
159
+ * A versatile combobox that combines a button trigger with a searchable dropdown list.
160
+ * Ideal for selecting from moderate to large lists of options where search functionality
161
+ * improves user experience. Built on top of shadcn/ui Command and Popover primitives.
162
+ *
163
+ * Use this component when:
164
+ * - You have 10+ options that would benefit from filtering
165
+ * - Users need to quickly find specific options
166
+ * - You want to provide a better UX than a basic select dropdown
167
+ * - You need real-time search/filtering capabilities
168
+ *
169
+ * For fewer options or simpler selection, consider using the Select component instead.
170
+ *
171
+ * @component
172
+ * @example
173
+ * Basic controlled combobox with framework selection
174
+ * ```tsx
175
+ * const [framework, setFramework] = useState("")
176
+ *
177
+ * const frameworks = [
178
+ * { value: "next", label: "Next.js" },
179
+ * { value: "remix", label: "Remix" },
180
+ * { value: "svelte", label: "SvelteKit" },
181
+ * { value: "nuxt", label: "Nuxt.js" },
182
+ * ]
183
+ *
184
+ * <Combobox
185
+ * options={frameworks}
186
+ * value={framework}
187
+ * onValueChange={setFramework}
188
+ * placeholder="Select framework..."
189
+ * searchPlaceholder="Search frameworks..."
190
+ * buttonClassName="w-[300px]"
191
+ * />
192
+ * ```
193
+ *
194
+ * @example
195
+ * Combobox with disabled options and custom styling
196
+ * ```tsx
197
+ * const statusOptions = [
198
+ * { value: "active", label: "Active" },
199
+ * { value: "pending", label: "Pending" },
200
+ * { value: "archived", label: "Archived (Read Only)", disabled: true },
201
+ * { value: "deleted", label: "Deleted (Unavailable)", disabled: true },
202
+ * ]
203
+ *
204
+ * <Combobox
205
+ * options={statusOptions}
206
+ * value={status}
207
+ * onValueChange={setStatus}
208
+ * placeholder="Select status..."
209
+ * emptyText="No status found."
210
+ * buttonClassName="w-[250px] border-primary/50"
211
+ * popoverClassName="w-[250px]"
212
+ * />
213
+ * ```
214
+ *
215
+ * @example
216
+ * Form integration with validation and error states
217
+ * ```tsx
218
+ * import { Label } from "@/components/ui/label"
219
+ *
220
+ * <form onSubmit={handleSubmit}>
221
+ * <div className="space-y-2">
222
+ * <Label htmlFor="country">Country</Label>
223
+ * <Combobox
224
+ * options={countries}
225
+ * value={formData.country}
226
+ * onValueChange={(value) =>
227
+ * setFormData(prev => ({ ...prev, country: value }))
228
+ * }
229
+ * placeholder="Choose a country..."
230
+ * searchPlaceholder="Type to search countries..."
231
+ * emptyText="Country not found."
232
+ * buttonClassName="w-full"
233
+ * disabled={isSubmitting}
234
+ * />
235
+ * {errors.country && (
236
+ * <p className="text-sm text-destructive">{errors.country}</p>
237
+ * )}
238
+ * </div>
239
+ * </form>
240
+ * ```
241
+ *
242
+ * @example
243
+ * Responsive width with mobile optimization
244
+ * ```tsx
245
+ * <Combobox
246
+ * options={userOptions}
247
+ * value={selectedUser}
248
+ * onValueChange={setSelectedUser}
249
+ * placeholder="Select team member..."
250
+ * searchPlaceholder="Search users..."
251
+ * buttonClassName="w-full sm:w-[280px]"
252
+ * popoverClassName="w-full sm:w-[280px]"
253
+ * />
254
+ * ```
255
+ *
256
+ * @accessibility
257
+ *
258
+ * **ARIA Implementation:**
259
+ * - Uses `role="combobox"` on trigger button with proper `aria-expanded` state
260
+ * - Implements `aria-controls` to reference the popup when visible
261
+ * - Uses `aria-activedescendant` for focus management within the dropdown
262
+ * - Provides `aria-haspopup="listbox"` to indicate popup type
263
+ *
264
+ * **Keyboard Navigation (W3C ARIA 1.2 compliant):**
265
+ * - **Tab**: Moves focus to/from combobox in page tab order
266
+ * - **Down Arrow**: Opens dropdown and moves to first option, or navigates to next option
267
+ * - **Up Arrow**: Moves to previous option (when dropdown is open)
268
+ * - **Enter**: Selects the focused option and closes dropdown
269
+ * - **Escape**: Closes dropdown and returns focus to trigger
270
+ * - **Home/End**: Moves to first/last option in list
271
+ * - **Type-ahead**: Real-time filtering as user types in search input
272
+ *
273
+ * **Screen Reader Support:**
274
+ * - Announces current selection state and option count
275
+ * - Provides clear labels for empty states and loading states
276
+ * - Announces selection changes and dropdown open/close states
277
+ * - Disabled options are properly announced and skipped during navigation
278
+ *
279
+ * **Focus Management:**
280
+ * - DOM focus remains on combobox trigger for screen reader compatibility
281
+ * - Visual focus moves through options using `aria-activedescendant`
282
+ * - Search input automatically receives focus when dropdown opens
283
+ * - Focus returns to trigger when dropdown closes via Escape or selection
284
+ *
285
+ * **Visual Accessibility:**
286
+ * - Maintains sufficient color contrast for all states
287
+ * - Provides hover and focus indicators for all interactive elements
288
+ * - Disabled options are visually distinct and non-interactive
289
+ * - Clear visual feedback for selected state with check icon
290
+ *
291
+ * @performance
292
+ * - Efficient option lookup with single find() operation
293
+ * - Minimal re-renders through proper state management
294
+ * - Supports large option lists with built-in virtualization via cmdk
295
+ *
296
+ * @see {@link https://ui.shadcn.com/docs/components/combobox} shadcn/ui Combobox documentation
297
+ * @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/combobox/} W3C ARIA Combobox Pattern
298
+ * @see {@link Select} For simpler dropdowns without search functionality
299
+ * @see {@link Command} The underlying command menu component
300
+ * @see {@link Popover} The popover container component
301
+ * @since 1.0.0
302
+ */
303
+ export function Combobox({
304
+ options,
305
+ value,
306
+ onValueChange,
307
+ placeholder = "Select option...",
308
+ emptyText = "No option found.",
309
+ searchPlaceholder = "Search options...",
310
+ className,
311
+ disabled = false,
312
+ buttonClassName,
313
+ popoverClassName,
314
+ }: ComboboxProps) {
315
+ const [open, setOpen] = React.useState(false);
316
+
317
+ const selectedOption = options.find((option) => option.value === value);
318
+
319
+ const handleSelect = (selectedValue: string) => {
320
+ const newValue = selectedValue === value ? "" : selectedValue;
321
+ onValueChange?.(newValue);
322
+ setOpen(false);
323
+ };
324
+
325
+ return (
326
+ <div className={className}>
327
+ <Popover open={open} onOpenChange={setOpen}>
328
+ <PopoverTrigger asChild>
329
+ <Button
330
+ variant="outline"
331
+ role="combobox"
332
+ aria-expanded={open}
333
+ disabled={disabled}
334
+ className={cn(
335
+ "w-full justify-between",
336
+ !selectedOption && "text-muted-foreground",
337
+ buttonClassName,
338
+ )}
339
+ >
340
+ {selectedOption ? selectedOption.label : placeholder}
341
+ <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
342
+ </Button>
343
+ </PopoverTrigger>
344
+ <PopoverContent className={cn("w-full p-0", popoverClassName)}>
345
+ <Command>
346
+ <CommandInput placeholder={searchPlaceholder} className="h-9" />
347
+ <CommandList>
348
+ <CommandEmpty>{emptyText}</CommandEmpty>
349
+ <CommandGroup>
350
+ {options.map((option) => (
351
+ <CommandItem
352
+ key={option.value}
353
+ value={option.value}
354
+ disabled={option.disabled}
355
+ onSelect={handleSelect}
356
+ >
357
+ <Check
358
+ className={cn(
359
+ "mr-2 h-4 w-4",
360
+ value === option.value ? "opacity-100" : "opacity-0",
361
+ )}
362
+ />
363
+ {option.label}
364
+ </CommandItem>
365
+ ))}
366
+ </CommandGroup>
367
+ </CommandList>
368
+ </Command>
369
+ </PopoverContent>
370
+ </Popover>
371
+ </div>
372
+ );
373
+ }