@pickle-ui/react 0.0.0-stage → 1.0.0-beta.3

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 (560) hide show
  1. package/README.md +490 -2
  2. package/dist/components/Alert.d.ts +38 -0
  3. package/dist/components/Alert.d.ts.map +1 -0
  4. package/dist/components/Alert.js +54 -0
  5. package/dist/components/Alert.js.map +1 -0
  6. package/dist/components/AlertDialog.d.ts +41 -0
  7. package/dist/components/AlertDialog.d.ts.map +1 -0
  8. package/dist/components/AlertDialog.js +47 -0
  9. package/dist/components/AlertDialog.js.map +1 -0
  10. package/dist/components/Avatar.d.ts +18 -0
  11. package/dist/components/Avatar.d.ts.map +1 -0
  12. package/dist/components/Avatar.js +34 -0
  13. package/dist/components/Avatar.js.map +1 -0
  14. package/dist/components/Badge.d.ts +12 -0
  15. package/dist/components/Badge.d.ts.map +1 -0
  16. package/dist/components/Badge.js +37 -0
  17. package/dist/components/Badge.js.map +1 -0
  18. package/dist/components/Button.d.ts +19 -0
  19. package/dist/components/Button.d.ts.map +1 -0
  20. package/dist/components/Button.js +66 -0
  21. package/dist/components/Button.js.map +1 -0
  22. package/dist/components/Card.d.ts +27 -0
  23. package/dist/components/Card.d.ts.map +1 -0
  24. package/dist/components/Card.js +34 -0
  25. package/dist/components/Card.js.map +1 -0
  26. package/dist/components/Dialog.d.ts +38 -0
  27. package/dist/components/Dialog.d.ts.map +1 -0
  28. package/dist/components/Dialog.js +41 -0
  29. package/dist/components/Dialog.js.map +1 -0
  30. package/dist/components/Flex.d.ts +22 -0
  31. package/dist/components/Flex.d.ts.map +1 -0
  32. package/dist/components/Flex.js +20 -0
  33. package/dist/components/Flex.js.map +1 -0
  34. package/dist/components/Grid.d.ts +66 -0
  35. package/dist/components/Grid.d.ts.map +1 -0
  36. package/dist/components/Grid.js +63 -0
  37. package/dist/components/Grid.js.map +1 -0
  38. package/dist/components/Group.d.ts +20 -0
  39. package/dist/components/Group.d.ts.map +1 -0
  40. package/dist/components/Group.js +43 -0
  41. package/dist/components/Group.js.map +1 -0
  42. package/dist/components/Menu.d.ts +53 -0
  43. package/dist/components/Menu.d.ts.map +1 -0
  44. package/dist/components/Menu.js +78 -0
  45. package/dist/components/Menu.js.map +1 -0
  46. package/dist/components/Popover.d.ts +27 -0
  47. package/dist/components/Popover.d.ts.map +1 -0
  48. package/dist/components/Popover.js +34 -0
  49. package/dist/components/Popover.js.map +1 -0
  50. package/dist/components/Progress.d.ts +23 -0
  51. package/dist/components/Progress.d.ts.map +1 -0
  52. package/dist/components/Progress.js +26 -0
  53. package/dist/components/Progress.js.map +1 -0
  54. package/dist/components/ScrollArea.d.ts +32 -0
  55. package/dist/components/ScrollArea.d.ts.map +1 -0
  56. package/dist/components/ScrollArea.js +36 -0
  57. package/dist/components/ScrollArea.js.map +1 -0
  58. package/dist/components/Separator.d.ts +7 -0
  59. package/dist/components/Separator.d.ts.map +1 -0
  60. package/dist/components/Separator.js +8 -0
  61. package/dist/components/Separator.js.map +1 -0
  62. package/dist/components/Sidebar.d.ts +35 -0
  63. package/dist/components/Sidebar.d.ts.map +1 -0
  64. package/dist/components/Sidebar.js +45 -0
  65. package/dist/components/Sidebar.js.map +1 -0
  66. package/dist/components/Spinner.d.ts +11 -0
  67. package/dist/components/Spinner.d.ts.map +1 -0
  68. package/dist/components/Spinner.js +13 -0
  69. package/dist/components/Spinner.js.map +1 -0
  70. package/dist/components/Table.d.ts +32 -0
  71. package/dist/components/Table.d.ts.map +1 -0
  72. package/dist/components/Table.js +37 -0
  73. package/dist/components/Table.js.map +1 -0
  74. package/dist/components/Text.d.ts +14 -0
  75. package/dist/components/Text.d.ts.map +1 -0
  76. package/dist/components/Text.js +50 -0
  77. package/dist/components/Text.js.map +1 -0
  78. package/dist/components/Toast.d.ts +39 -0
  79. package/dist/components/Toast.d.ts.map +1 -0
  80. package/dist/components/Toast.js +97 -0
  81. package/dist/components/Toast.js.map +1 -0
  82. package/dist/components/Tooltip.d.ts +18 -0
  83. package/dist/components/Tooltip.d.ts.map +1 -0
  84. package/dist/components/Tooltip.js +22 -0
  85. package/dist/components/Tooltip.js.map +1 -0
  86. package/dist/components/XStack.d.ts +6 -0
  87. package/dist/components/XStack.d.ts.map +1 -0
  88. package/dist/components/XStack.js +8 -0
  89. package/dist/components/XStack.js.map +1 -0
  90. package/dist/components/YStack.d.ts +6 -0
  91. package/dist/components/YStack.d.ts.map +1 -0
  92. package/dist/components/YStack.js +8 -0
  93. package/dist/components/YStack.js.map +1 -0
  94. package/dist/components/_internal/VisuallyHiddenInput.d.ts +11 -0
  95. package/dist/components/_internal/VisuallyHiddenInput.d.ts.map +1 -0
  96. package/dist/components/_internal/VisuallyHiddenInput.js +105 -0
  97. package/dist/components/_internal/VisuallyHiddenInput.js.map +1 -0
  98. package/dist/components/_internal/dialog-styles.d.ts +15 -0
  99. package/dist/components/_internal/dialog-styles.d.ts.map +1 -0
  100. package/dist/components/_internal/dialog-styles.js +27 -0
  101. package/dist/components/_internal/dialog-styles.js.map +1 -0
  102. package/dist/components/_internal/layout.d.ts +46 -0
  103. package/dist/components/_internal/layout.d.ts.map +1 -0
  104. package/dist/components/_internal/layout.js +72 -0
  105. package/dist/components/_internal/layout.js.map +1 -0
  106. package/dist/components/form/Checkbox.d.ts +19 -0
  107. package/dist/components/form/Checkbox.d.ts.map +1 -0
  108. package/dist/components/form/Checkbox.js +27 -0
  109. package/dist/components/form/Checkbox.js.map +1 -0
  110. package/dist/components/form/ColorPicker.d.ts +25 -0
  111. package/dist/components/form/ColorPicker.d.ts.map +1 -0
  112. package/dist/components/form/ColorPicker.js +14 -0
  113. package/dist/components/form/ColorPicker.js.map +1 -0
  114. package/dist/components/form/ColorPickerPrimitive.d.ts +65 -0
  115. package/dist/components/form/ColorPickerPrimitive.d.ts.map +1 -0
  116. package/dist/components/form/ColorPickerPrimitive.js +486 -0
  117. package/dist/components/form/ColorPickerPrimitive.js.map +1 -0
  118. package/dist/components/form/Combobox.d.ts +86 -0
  119. package/dist/components/form/Combobox.d.ts.map +1 -0
  120. package/dist/components/form/Combobox.js +198 -0
  121. package/dist/components/form/Combobox.js.map +1 -0
  122. package/dist/components/form/Form.context.d.ts +28 -0
  123. package/dist/components/form/Form.context.d.ts.map +1 -0
  124. package/dist/components/form/Form.context.js +56 -0
  125. package/dist/components/form/Form.context.js.map +1 -0
  126. package/dist/components/form/Form.d.ts +68 -0
  127. package/dist/components/form/Form.d.ts.map +1 -0
  128. package/dist/components/form/Form.js +32 -0
  129. package/dist/components/form/Form.js.map +1 -0
  130. package/dist/components/form/FormCheckbox.d.ts +10 -0
  131. package/dist/components/form/FormCheckbox.d.ts.map +1 -0
  132. package/dist/components/form/FormCheckbox.js +24 -0
  133. package/dist/components/form/FormCheckbox.js.map +1 -0
  134. package/dist/components/form/FormCombobox.d.ts +28 -0
  135. package/dist/components/form/FormCombobox.d.ts.map +1 -0
  136. package/dist/components/form/FormCombobox.js +50 -0
  137. package/dist/components/form/FormCombobox.js.map +1 -0
  138. package/dist/components/form/FormDescription.d.ts +7 -0
  139. package/dist/components/form/FormDescription.d.ts.map +1 -0
  140. package/dist/components/form/FormDescription.js +9 -0
  141. package/dist/components/form/FormDescription.js.map +1 -0
  142. package/dist/components/form/FormError.d.ts +7 -0
  143. package/dist/components/form/FormError.d.ts.map +1 -0
  144. package/dist/components/form/FormError.js +9 -0
  145. package/dist/components/form/FormError.js.map +1 -0
  146. package/dist/components/form/FormField.d.ts +13 -0
  147. package/dist/components/form/FormField.d.ts.map +1 -0
  148. package/dist/components/form/FormField.js +45 -0
  149. package/dist/components/form/FormField.js.map +1 -0
  150. package/dist/components/form/FormInput.d.ts +8 -0
  151. package/dist/components/form/FormInput.d.ts.map +1 -0
  152. package/dist/components/form/FormInput.js +17 -0
  153. package/dist/components/form/FormInput.js.map +1 -0
  154. package/dist/components/form/FormInputGroup.d.ts +7 -0
  155. package/dist/components/form/FormInputGroup.d.ts.map +1 -0
  156. package/dist/components/form/FormInputGroup.js +30 -0
  157. package/dist/components/form/FormInputGroup.js.map +1 -0
  158. package/dist/components/form/FormLabel.d.ts +7 -0
  159. package/dist/components/form/FormLabel.d.ts.map +1 -0
  160. package/dist/components/form/FormLabel.js +9 -0
  161. package/dist/components/form/FormLabel.js.map +1 -0
  162. package/dist/components/form/FormRadioGroup.d.ts +14 -0
  163. package/dist/components/form/FormRadioGroup.d.ts.map +1 -0
  164. package/dist/components/form/FormRadioGroup.js +25 -0
  165. package/dist/components/form/FormRadioGroup.js.map +1 -0
  166. package/dist/components/form/FormRoot.d.ts +10 -0
  167. package/dist/components/form/FormRoot.d.ts.map +1 -0
  168. package/dist/components/form/FormRoot.js +10 -0
  169. package/dist/components/form/FormRoot.js.map +1 -0
  170. package/dist/components/form/FormSelect.d.ts +19 -0
  171. package/dist/components/form/FormSelect.d.ts.map +1 -0
  172. package/dist/components/form/FormSelect.js +34 -0
  173. package/dist/components/form/FormSelect.js.map +1 -0
  174. package/dist/components/form/FormSwitch.d.ts +10 -0
  175. package/dist/components/form/FormSwitch.d.ts.map +1 -0
  176. package/dist/components/form/FormSwitch.js +24 -0
  177. package/dist/components/form/FormSwitch.js.map +1 -0
  178. package/dist/components/form/FormTextarea.d.ts +8 -0
  179. package/dist/components/form/FormTextarea.d.ts.map +1 -0
  180. package/dist/components/form/FormTextarea.js +13 -0
  181. package/dist/components/form/FormTextarea.js.map +1 -0
  182. package/dist/components/form/Input.d.ts +14 -0
  183. package/dist/components/form/Input.d.ts.map +1 -0
  184. package/dist/components/form/Input.js +22 -0
  185. package/dist/components/form/Input.js.map +1 -0
  186. package/dist/components/form/InputGroup.d.ts +26 -0
  187. package/dist/components/form/InputGroup.d.ts.map +1 -0
  188. package/dist/components/form/InputGroup.js +58 -0
  189. package/dist/components/form/InputGroup.js.map +1 -0
  190. package/dist/components/form/RadioGroup.d.ts +23 -0
  191. package/dist/components/form/RadioGroup.d.ts.map +1 -0
  192. package/dist/components/form/RadioGroup.js +30 -0
  193. package/dist/components/form/RadioGroup.js.map +1 -0
  194. package/dist/components/form/Select.d.ts +42 -0
  195. package/dist/components/form/Select.d.ts.map +1 -0
  196. package/dist/components/form/Select.js +68 -0
  197. package/dist/components/form/Select.js.map +1 -0
  198. package/dist/components/form/Slider.d.ts +37 -0
  199. package/dist/components/form/Slider.d.ts.map +1 -0
  200. package/dist/components/form/Slider.js +35 -0
  201. package/dist/components/form/Slider.js.map +1 -0
  202. package/dist/components/form/Switch.d.ts +23 -0
  203. package/dist/components/form/Switch.d.ts.map +1 -0
  204. package/dist/components/form/Switch.js +21 -0
  205. package/dist/components/form/Switch.js.map +1 -0
  206. package/dist/components/form/Textarea.d.ts +16 -0
  207. package/dist/components/form/Textarea.d.ts.map +1 -0
  208. package/dist/components/form/Textarea.js +12 -0
  209. package/dist/components/form/Textarea.js.map +1 -0
  210. package/dist/entries/alert-dialog.d.ts +3 -0
  211. package/dist/entries/alert-dialog.d.ts.map +1 -0
  212. package/dist/entries/alert-dialog.js +2 -0
  213. package/dist/entries/alert-dialog.js.map +1 -0
  214. package/dist/entries/alert.d.ts +3 -0
  215. package/dist/entries/alert.d.ts.map +1 -0
  216. package/dist/entries/alert.js +2 -0
  217. package/dist/entries/alert.js.map +1 -0
  218. package/dist/entries/avatar.d.ts +3 -0
  219. package/dist/entries/avatar.d.ts.map +1 -0
  220. package/dist/entries/avatar.js +2 -0
  221. package/dist/entries/avatar.js.map +1 -0
  222. package/dist/entries/badge.d.ts +3 -0
  223. package/dist/entries/badge.d.ts.map +1 -0
  224. package/dist/entries/badge.js +2 -0
  225. package/dist/entries/badge.js.map +1 -0
  226. package/dist/entries/button.d.ts +3 -0
  227. package/dist/entries/button.d.ts.map +1 -0
  228. package/dist/entries/button.js +2 -0
  229. package/dist/entries/button.js.map +1 -0
  230. package/dist/entries/card.d.ts +3 -0
  231. package/dist/entries/card.d.ts.map +1 -0
  232. package/dist/entries/card.js +2 -0
  233. package/dist/entries/card.js.map +1 -0
  234. package/dist/entries/checkbox.d.ts +3 -0
  235. package/dist/entries/checkbox.d.ts.map +1 -0
  236. package/dist/entries/checkbox.js +2 -0
  237. package/dist/entries/checkbox.js.map +1 -0
  238. package/dist/entries/cn.d.ts +3 -0
  239. package/dist/entries/cn.d.ts.map +1 -0
  240. package/dist/entries/cn.js +2 -0
  241. package/dist/entries/cn.js.map +1 -0
  242. package/dist/entries/color-picker.d.ts +3 -0
  243. package/dist/entries/color-picker.d.ts.map +1 -0
  244. package/dist/entries/color-picker.js +2 -0
  245. package/dist/entries/color-picker.js.map +1 -0
  246. package/dist/entries/combobox.d.ts +3 -0
  247. package/dist/entries/combobox.d.ts.map +1 -0
  248. package/dist/entries/combobox.js +2 -0
  249. package/dist/entries/combobox.js.map +1 -0
  250. package/dist/entries/dialog.d.ts +3 -0
  251. package/dist/entries/dialog.d.ts.map +1 -0
  252. package/dist/entries/dialog.js +2 -0
  253. package/dist/entries/dialog.js.map +1 -0
  254. package/dist/entries/flex.d.ts +3 -0
  255. package/dist/entries/flex.d.ts.map +1 -0
  256. package/dist/entries/flex.js +2 -0
  257. package/dist/entries/flex.js.map +1 -0
  258. package/dist/entries/form.d.ts +3 -0
  259. package/dist/entries/form.d.ts.map +1 -0
  260. package/dist/entries/form.js +2 -0
  261. package/dist/entries/form.js.map +1 -0
  262. package/dist/entries/grid.d.ts +3 -0
  263. package/dist/entries/grid.d.ts.map +1 -0
  264. package/dist/entries/grid.js +2 -0
  265. package/dist/entries/grid.js.map +1 -0
  266. package/dist/entries/group.d.ts +3 -0
  267. package/dist/entries/group.d.ts.map +1 -0
  268. package/dist/entries/group.js +2 -0
  269. package/dist/entries/group.js.map +1 -0
  270. package/dist/entries/input-group.d.ts +3 -0
  271. package/dist/entries/input-group.d.ts.map +1 -0
  272. package/dist/entries/input-group.js +2 -0
  273. package/dist/entries/input-group.js.map +1 -0
  274. package/dist/entries/input.d.ts +3 -0
  275. package/dist/entries/input.d.ts.map +1 -0
  276. package/dist/entries/input.js +2 -0
  277. package/dist/entries/input.js.map +1 -0
  278. package/dist/entries/menu.d.ts +3 -0
  279. package/dist/entries/menu.d.ts.map +1 -0
  280. package/dist/entries/menu.js +2 -0
  281. package/dist/entries/menu.js.map +1 -0
  282. package/dist/entries/popover.d.ts +3 -0
  283. package/dist/entries/popover.d.ts.map +1 -0
  284. package/dist/entries/popover.js +2 -0
  285. package/dist/entries/popover.js.map +1 -0
  286. package/dist/entries/progress.d.ts +3 -0
  287. package/dist/entries/progress.d.ts.map +1 -0
  288. package/dist/entries/progress.js +2 -0
  289. package/dist/entries/progress.js.map +1 -0
  290. package/dist/entries/radio-group.d.ts +3 -0
  291. package/dist/entries/radio-group.d.ts.map +1 -0
  292. package/dist/entries/radio-group.js +2 -0
  293. package/dist/entries/radio-group.js.map +1 -0
  294. package/dist/entries/scroll-area.d.ts +3 -0
  295. package/dist/entries/scroll-area.d.ts.map +1 -0
  296. package/dist/entries/scroll-area.js +2 -0
  297. package/dist/entries/scroll-area.js.map +1 -0
  298. package/dist/entries/select.d.ts +3 -0
  299. package/dist/entries/select.d.ts.map +1 -0
  300. package/dist/entries/select.js +2 -0
  301. package/dist/entries/select.js.map +1 -0
  302. package/dist/entries/separator.d.ts +3 -0
  303. package/dist/entries/separator.d.ts.map +1 -0
  304. package/dist/entries/separator.js +2 -0
  305. package/dist/entries/separator.js.map +1 -0
  306. package/dist/entries/sidebar.d.ts +3 -0
  307. package/dist/entries/sidebar.d.ts.map +1 -0
  308. package/dist/entries/sidebar.js +2 -0
  309. package/dist/entries/sidebar.js.map +1 -0
  310. package/dist/entries/slider.d.ts +3 -0
  311. package/dist/entries/slider.d.ts.map +1 -0
  312. package/dist/entries/slider.js +2 -0
  313. package/dist/entries/slider.js.map +1 -0
  314. package/dist/entries/spinner.d.ts +3 -0
  315. package/dist/entries/spinner.d.ts.map +1 -0
  316. package/dist/entries/spinner.js +2 -0
  317. package/dist/entries/spinner.js.map +1 -0
  318. package/dist/entries/switch.d.ts +3 -0
  319. package/dist/entries/switch.d.ts.map +1 -0
  320. package/dist/entries/switch.js +2 -0
  321. package/dist/entries/switch.js.map +1 -0
  322. package/dist/entries/table.d.ts +3 -0
  323. package/dist/entries/table.d.ts.map +1 -0
  324. package/dist/entries/table.js +2 -0
  325. package/dist/entries/table.js.map +1 -0
  326. package/dist/entries/text.d.ts +3 -0
  327. package/dist/entries/text.d.ts.map +1 -0
  328. package/dist/entries/text.js +2 -0
  329. package/dist/entries/text.js.map +1 -0
  330. package/dist/entries/textarea.d.ts +3 -0
  331. package/dist/entries/textarea.d.ts.map +1 -0
  332. package/dist/entries/textarea.js +2 -0
  333. package/dist/entries/textarea.js.map +1 -0
  334. package/dist/entries/theme.d.ts +3 -0
  335. package/dist/entries/theme.d.ts.map +1 -0
  336. package/dist/entries/theme.js +2 -0
  337. package/dist/entries/theme.js.map +1 -0
  338. package/dist/entries/toast.d.ts +3 -0
  339. package/dist/entries/toast.d.ts.map +1 -0
  340. package/dist/entries/toast.js +2 -0
  341. package/dist/entries/toast.js.map +1 -0
  342. package/dist/entries/tooltip.d.ts +3 -0
  343. package/dist/entries/tooltip.d.ts.map +1 -0
  344. package/dist/entries/tooltip.js +2 -0
  345. package/dist/entries/tooltip.js.map +1 -0
  346. package/dist/entries/x-stack.d.ts +3 -0
  347. package/dist/entries/x-stack.d.ts.map +1 -0
  348. package/dist/entries/x-stack.js +2 -0
  349. package/dist/entries/x-stack.js.map +1 -0
  350. package/dist/entries/y-stack.d.ts +3 -0
  351. package/dist/entries/y-stack.d.ts.map +1 -0
  352. package/dist/entries/y-stack.js +2 -0
  353. package/dist/entries/y-stack.js.map +1 -0
  354. package/dist/hooks/use-as-ref.d.ts +4 -0
  355. package/dist/hooks/use-as-ref.d.ts.map +1 -0
  356. package/dist/hooks/use-as-ref.js +11 -0
  357. package/dist/hooks/use-as-ref.js.map +1 -0
  358. package/dist/hooks/use-isomorphic-layout-effect.d.ts +4 -0
  359. package/dist/hooks/use-isomorphic-layout-effect.d.ts.map +1 -0
  360. package/dist/hooks/use-isomorphic-layout-effect.js +4 -0
  361. package/dist/hooks/use-isomorphic-layout-effect.js.map +1 -0
  362. package/dist/hooks/use-lazy-ref.d.ts +4 -0
  363. package/dist/hooks/use-lazy-ref.d.ts.map +1 -0
  364. package/dist/hooks/use-lazy-ref.js +10 -0
  365. package/dist/hooks/use-lazy-ref.js.map +1 -0
  366. package/dist/index.d.ts +37 -0
  367. package/dist/index.d.ts.map +1 -0
  368. package/dist/index.js +39 -0
  369. package/dist/index.js.map +1 -0
  370. package/dist/lib/color.d.ts +42 -0
  371. package/dist/lib/color.d.ts.map +1 -0
  372. package/dist/lib/color.js +140 -0
  373. package/dist/lib/color.js.map +1 -0
  374. package/dist/lib/compose-refs.d.ts +14 -0
  375. package/dist/lib/compose-refs.d.ts.map +1 -0
  376. package/dist/lib/compose-refs.js +57 -0
  377. package/dist/lib/compose-refs.js.map +1 -0
  378. package/dist/lib/theme.d.ts +40 -0
  379. package/dist/lib/theme.d.ts.map +1 -0
  380. package/dist/lib/theme.js +82 -0
  381. package/dist/lib/theme.js.map +1 -0
  382. package/dist/lib/utils.d.ts +8 -0
  383. package/dist/lib/utils.d.ts.map +1 -0
  384. package/dist/lib/utils.js +18 -0
  385. package/dist/lib/utils.js.map +1 -0
  386. package/dist/test-setup.d.ts +2 -0
  387. package/dist/test-setup.d.ts.map +1 -0
  388. package/dist/test-setup.js +2 -0
  389. package/dist/test-setup.js.map +1 -0
  390. package/package.json +107 -4
  391. package/src/base.css +99 -0
  392. package/src/components/Alert.stories.tsx +124 -0
  393. package/src/components/Alert.test.tsx +76 -0
  394. package/src/components/Alert.tsx +133 -0
  395. package/src/components/AlertDialog.stories.tsx +184 -0
  396. package/src/components/AlertDialog.test.tsx +142 -0
  397. package/src/components/AlertDialog.tsx +164 -0
  398. package/src/components/Avatar.stories.tsx +92 -0
  399. package/src/components/Avatar.test.tsx +26 -0
  400. package/src/components/Avatar.tsx +96 -0
  401. package/src/components/Badge.stories.tsx +52 -0
  402. package/src/components/Badge.test.tsx +35 -0
  403. package/src/components/Badge.tsx +65 -0
  404. package/src/components/Button.stories.tsx +171 -0
  405. package/src/components/Button.test.tsx +112 -0
  406. package/src/components/Button.tsx +114 -0
  407. package/src/components/Card.stories.tsx +62 -0
  408. package/src/components/Card.test.tsx +23 -0
  409. package/src/components/Card.tsx +129 -0
  410. package/src/components/Dialog.stories.tsx +133 -0
  411. package/src/components/Dialog.test.tsx +95 -0
  412. package/src/components/Dialog.tsx +147 -0
  413. package/src/components/Flex.stories.tsx +70 -0
  414. package/src/components/Flex.test.tsx +53 -0
  415. package/src/components/Flex.tsx +59 -0
  416. package/src/components/Grid.stories.tsx +70 -0
  417. package/src/components/Grid.test.tsx +46 -0
  418. package/src/components/Grid.tsx +114 -0
  419. package/src/components/Group.stories.tsx +130 -0
  420. package/src/components/Group.test.tsx +57 -0
  421. package/src/components/Group.tsx +92 -0
  422. package/src/components/Menu.stories.tsx +139 -0
  423. package/src/components/Menu.test.tsx +103 -0
  424. package/src/components/Menu.tsx +290 -0
  425. package/src/components/Popover.stories.tsx +72 -0
  426. package/src/components/Popover.tsx +117 -0
  427. package/src/components/Progress.stories.tsx +105 -0
  428. package/src/components/Progress.test.tsx +28 -0
  429. package/src/components/Progress.tsx +111 -0
  430. package/src/components/ScrollArea.stories.tsx +66 -0
  431. package/src/components/ScrollArea.test.tsx +32 -0
  432. package/src/components/ScrollArea.tsx +160 -0
  433. package/src/components/Separator.stories.tsx +39 -0
  434. package/src/components/Separator.test.tsx +24 -0
  435. package/src/components/Separator.tsx +27 -0
  436. package/src/components/Sidebar.stories.tsx +87 -0
  437. package/src/components/Sidebar.tsx +166 -0
  438. package/src/components/Spinner.stories.tsx +36 -0
  439. package/src/components/Spinner.test.tsx +25 -0
  440. package/src/components/Spinner.tsx +31 -0
  441. package/src/components/Table.stories.tsx +76 -0
  442. package/src/components/Table.tsx +151 -0
  443. package/src/components/Text.stories.tsx +148 -0
  444. package/src/components/Text.test.tsx +106 -0
  445. package/src/components/Text.tsx +85 -0
  446. package/src/components/Toast.stories.tsx +166 -0
  447. package/src/components/Toast.test.tsx +97 -0
  448. package/src/components/Toast.tsx +270 -0
  449. package/src/components/Tooltip.stories.tsx +116 -0
  450. package/src/components/Tooltip.test.tsx +51 -0
  451. package/src/components/Tooltip.tsx +80 -0
  452. package/src/components/XStack.stories.tsx +64 -0
  453. package/src/components/XStack.test.tsx +33 -0
  454. package/src/components/XStack.tsx +19 -0
  455. package/src/components/YStack.stories.tsx +65 -0
  456. package/src/components/YStack.test.tsx +31 -0
  457. package/src/components/YStack.tsx +19 -0
  458. package/src/components/_internal/Story.tsx +161 -0
  459. package/src/components/_internal/VisuallyHiddenInput.tsx +164 -0
  460. package/src/components/_internal/dialog-styles.ts +57 -0
  461. package/src/components/_internal/layout.ts +103 -0
  462. package/src/components/form/Checkbox.stories.tsx +78 -0
  463. package/src/components/form/Checkbox.test.tsx +107 -0
  464. package/src/components/form/Checkbox.tsx +95 -0
  465. package/src/components/form/ColorPicker.stories.tsx +160 -0
  466. package/src/components/form/ColorPicker.test.tsx +45 -0
  467. package/src/components/form/ColorPicker.tsx +55 -0
  468. package/src/components/form/ColorPickerPrimitive.test.tsx +14 -0
  469. package/src/components/form/ColorPickerPrimitive.tsx +1283 -0
  470. package/src/components/form/Combobox.stories.tsx +378 -0
  471. package/src/components/form/Combobox.test.tsx +334 -0
  472. package/src/components/form/Combobox.tsx +667 -0
  473. package/src/components/form/Form.context.ts +114 -0
  474. package/src/components/form/Form.stories.tsx +226 -0
  475. package/src/components/form/Form.test.tsx +293 -0
  476. package/src/components/form/Form.tsx +50 -0
  477. package/src/components/form/FormCheckbox.tsx +50 -0
  478. package/src/components/form/FormCombobox.tsx +92 -0
  479. package/src/components/form/FormDescription.tsx +21 -0
  480. package/src/components/form/FormError.tsx +21 -0
  481. package/src/components/form/FormField.tsx +109 -0
  482. package/src/components/form/FormInput.tsx +41 -0
  483. package/src/components/form/FormInputGroup.tsx +47 -0
  484. package/src/components/form/FormLabel.tsx +24 -0
  485. package/src/components/form/FormRadioGroup.tsx +46 -0
  486. package/src/components/form/FormRoot.tsx +41 -0
  487. package/src/components/form/FormSelect.tsx +60 -0
  488. package/src/components/form/FormSwitch.tsx +50 -0
  489. package/src/components/form/FormTextarea.tsx +34 -0
  490. package/src/components/form/Input.stories.tsx +114 -0
  491. package/src/components/form/Input.test.tsx +62 -0
  492. package/src/components/form/Input.tsx +93 -0
  493. package/src/components/form/InputGroup.stories.tsx +162 -0
  494. package/src/components/form/InputGroup.tsx +149 -0
  495. package/src/components/form/RadioGroup.stories.tsx +68 -0
  496. package/src/components/form/RadioGroup.test.tsx +171 -0
  497. package/src/components/form/RadioGroup.tsx +127 -0
  498. package/src/components/form/Select.stories.tsx +115 -0
  499. package/src/components/form/Select.test.tsx +115 -0
  500. package/src/components/form/Select.tsx +293 -0
  501. package/src/components/form/Slider.stories.tsx +121 -0
  502. package/src/components/form/Slider.test.tsx +33 -0
  503. package/src/components/form/Slider.tsx +200 -0
  504. package/src/components/form/Switch.stories.tsx +62 -0
  505. package/src/components/form/Switch.test.tsx +29 -0
  506. package/src/components/form/Switch.tsx +81 -0
  507. package/src/components/form/Textarea.stories.tsx +71 -0
  508. package/src/components/form/Textarea.test.tsx +31 -0
  509. package/src/components/form/Textarea.tsx +46 -0
  510. package/src/entries/alert-dialog.ts +11 -0
  511. package/src/entries/alert.ts +8 -0
  512. package/src/entries/avatar.ts +7 -0
  513. package/src/entries/badge.ts +2 -0
  514. package/src/entries/button.ts +2 -0
  515. package/src/entries/card.ts +10 -0
  516. package/src/entries/checkbox.ts +5 -0
  517. package/src/entries/cn.ts +2 -0
  518. package/src/entries/color-picker.ts +13 -0
  519. package/src/entries/combobox.ts +20 -0
  520. package/src/entries/dialog.ts +11 -0
  521. package/src/entries/entries.test.ts +65 -0
  522. package/src/entries/flex.ts +2 -0
  523. package/src/entries/form.ts +16 -0
  524. package/src/entries/grid.ts +2 -0
  525. package/src/entries/group.ts +6 -0
  526. package/src/entries/input-group.ts +8 -0
  527. package/src/entries/input.ts +2 -0
  528. package/src/entries/menu.ts +17 -0
  529. package/src/entries/popover.ts +10 -0
  530. package/src/entries/progress.ts +8 -0
  531. package/src/entries/radio-group.ts +5 -0
  532. package/src/entries/scroll-area.ts +10 -0
  533. package/src/entries/select.ts +12 -0
  534. package/src/entries/separator.ts +2 -0
  535. package/src/entries/sidebar.ts +13 -0
  536. package/src/entries/slider.ts +7 -0
  537. package/src/entries/spinner.ts +2 -0
  538. package/src/entries/switch.ts +2 -0
  539. package/src/entries/table.ts +11 -0
  540. package/src/entries/text.ts +2 -0
  541. package/src/entries/textarea.ts +2 -0
  542. package/src/entries/theme.ts +8 -0
  543. package/src/entries/toast.ts +2 -0
  544. package/src/entries/tooltip.ts +7 -0
  545. package/src/entries/x-stack.ts +2 -0
  546. package/src/entries/y-stack.ts +2 -0
  547. package/src/hooks/use-as-ref.ts +15 -0
  548. package/src/hooks/use-isomorphic-layout-effect.ts +6 -0
  549. package/src/hooks/use-lazy-ref.ts +13 -0
  550. package/src/index.ts +38 -0
  551. package/src/lib/color.test.ts +148 -0
  552. package/src/lib/color.ts +193 -0
  553. package/src/lib/compose-refs.ts +63 -0
  554. package/src/lib/theme.test.ts +64 -0
  555. package/src/lib/theme.ts +143 -0
  556. package/src/lib/utils.ts +25 -0
  557. package/src/public-api.test.ts +74 -0
  558. package/src/styles.css +2 -0
  559. package/src/test-setup.ts +1 -0
  560. package/src/theme.css +195 -0
@@ -0,0 +1,1283 @@
1
+ 'use client';
2
+
3
+ import { useDirection } from '@base-ui/react/direction-provider';
4
+ import { useRender } from '@base-ui/react/use-render';
5
+ import { IconColorPicker } from '@tabler/icons-react';
6
+ import * as React from 'react';
7
+
8
+ import { Button } from '@/components/Button';
9
+ import { Input } from '@/components/form/Input';
10
+ import { InputGroup, InputGroupInput } from '@/components/form/InputGroup';
11
+ import {
12
+ Select,
13
+ SelectContent,
14
+ SelectItem,
15
+ SelectTrigger,
16
+ SelectValue,
17
+ } from '@/components/form/Select';
18
+ import { Slider } from '@/components/form/Slider';
19
+ import { Popover, PopoverContent, PopoverTrigger } from '@/components/Popover';
20
+ import { useAsRef } from '@/hooks/use-as-ref';
21
+ import { useIsomorphicLayoutEffect } from '@/hooks/use-isomorphic-layout-effect';
22
+ import { useLazyRef } from '@/hooks/use-lazy-ref';
23
+ import {
24
+ type ColorFormat,
25
+ colorFormats,
26
+ colorToString,
27
+ type ColorValue,
28
+ hexToRgb,
29
+ hslToRgb,
30
+ type HSVColorValue,
31
+ hsvToRgb,
32
+ isColorEqual,
33
+ isHsvEqual,
34
+ parseColorString,
35
+ rgbToHex,
36
+ rgbToHsl,
37
+ rgbToHsv,
38
+ } from '@/lib/color';
39
+ import { useComposedRefs } from '@/lib/compose-refs';
40
+ import { cn } from '@/lib/utils';
41
+
42
+ import { VisuallyHiddenInput } from '../_internal/VisuallyHiddenInput';
43
+
44
+ const ROOT_NAME = 'ColorPicker';
45
+ const ROOT_IMPL_NAME = 'ColorPickerImpl';
46
+ const TRIGGER_NAME = 'ColorPickerTrigger';
47
+ const CONTENT_NAME = 'ColorPickerContent';
48
+ const AREA_NAME = 'ColorPickerArea';
49
+ const HUE_SLIDER_NAME = 'ColorPickerHueSlider';
50
+ const ALPHA_SLIDER_NAME = 'ColorPickerAlphaSlider';
51
+ const SWATCH_NAME = 'ColorPickerSwatch';
52
+ const EYE_DROPPER_NAME = 'ColorPickerEyeDropper';
53
+ const FORMAT_SELECT_NAME = 'ColorPickerFormatSelect';
54
+ const INPUT_NAME = 'ColorPickerInput';
55
+
56
+ type AreaElement = React.ComponentRef<typeof ColorPickerArea>;
57
+
58
+ type DivProps = useRender.ComponentProps<'div'>;
59
+
60
+ /**
61
+ * @see https://gist.github.com/bkrmendy/f4582173f50fab209ddfef1377ab31e3
62
+ */
63
+ interface EyeDropper {
64
+ open: (options?: { signal?: AbortSignal }) => Promise<{ sRGBHex: string }>;
65
+ }
66
+
67
+ type InputElement = React.ComponentRef<typeof ColorPickerInput>;
68
+ type RootElement = React.ComponentRef<typeof ColorPicker>;
69
+
70
+ function getSliderValue(value: number | readonly number[]) {
71
+ return Array.isArray(value) ? (value[0] ?? 0) : value;
72
+ }
73
+
74
+ declare global {
75
+ interface Window {
76
+ EyeDropper?: {
77
+ new (): EyeDropper;
78
+ };
79
+ }
80
+ }
81
+
82
+ type Direction = 'ltr' | 'rtl';
83
+
84
+ interface Store {
85
+ getState: () => StoreState;
86
+ notify: () => void;
87
+ setFormat: (value: ColorFormat) => void;
88
+ setOpen: (value: boolean) => void;
89
+ setValue: (
90
+ color: ColorValue,
91
+ hsv: HSVColorValue,
92
+ options?: { emit?: boolean },
93
+ ) => void;
94
+ subscribe: (cb: () => void) => () => void;
95
+ }
96
+
97
+ interface StoreState {
98
+ color: ColorValue;
99
+ format: ColorFormat;
100
+ hsv: HSVColorValue;
101
+ open: boolean;
102
+ }
103
+
104
+ const StoreContext = React.createContext<null | Store>(null);
105
+
106
+ interface ColorPickerContextValue {
107
+ dir: Direction;
108
+ disabled?: boolean;
109
+ inline?: boolean;
110
+ readOnly?: boolean;
111
+ required?: boolean;
112
+ }
113
+
114
+ function useStore<U>(selector: (state: StoreState) => U): U {
115
+ const store = useStoreContext('useStore');
116
+
117
+ const getSnapshot = React.useCallback(
118
+ () => selector(store.getState()),
119
+ [store, selector],
120
+ );
121
+
122
+ return React.useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);
123
+ }
124
+
125
+ function useStoreContext(consumerName: string) {
126
+ const context = React.useContext(StoreContext);
127
+ if (!context) {
128
+ throw new Error(`\`${consumerName}\` must be used within \`${ROOT_NAME}\``);
129
+ }
130
+ return context;
131
+ }
132
+
133
+ const ColorPickerContext = React.createContext<ColorPickerContextValue | null>(
134
+ null,
135
+ );
136
+
137
+ type ColorPickerContentProps = Omit<
138
+ React.ComponentProps<typeof PopoverContent>,
139
+ 'className' | 'style'
140
+ > & {
141
+ className?: string;
142
+ style?: React.CSSProperties;
143
+ };
144
+
145
+ interface ColorPickerFormatSelectProps
146
+ extends
147
+ Omit<React.ComponentProps<typeof Select>, 'onValueChange' | 'value'>,
148
+ Pick<React.ComponentProps<typeof SelectTrigger>, 'className'> {}
149
+
150
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type -- a named props interface that is intentionally an alias of the Omit below, kept as an interface for naming and future extension
151
+ interface ColorPickerImplProps extends Omit<
152
+ ColorPickerProps,
153
+ | 'defaultFormat'
154
+ | 'defaultValue'
155
+ | 'format'
156
+ | 'onFormatChange'
157
+ | 'onOpenChange'
158
+ | 'onValueChange'
159
+ > {}
160
+
161
+ interface ColorPickerInputProps extends Omit<
162
+ React.ComponentProps<typeof Input>,
163
+ 'className' | 'color' | 'onChange' | 'value'
164
+ > {
165
+ className?: string;
166
+ withoutAlpha?: boolean;
167
+ }
168
+
169
+ interface ColorPickerProps
170
+ extends
171
+ Omit<DivProps, 'onValueChange'>,
172
+ Pick<
173
+ React.ComponentProps<typeof Popover>,
174
+ 'defaultOpen' | 'modal' | 'open'
175
+ > {
176
+ defaultFormat?: ColorFormat;
177
+ defaultValue?: string;
178
+ dir?: Direction;
179
+ disabled?: boolean;
180
+ format?: ColorFormat;
181
+ inline?: boolean;
182
+ name?: string;
183
+ onFormatChange?: (format: ColorFormat) => void;
184
+ onOpenChange?: (open: boolean) => void;
185
+ onValueChange?: (value: string) => void;
186
+ readOnly?: boolean;
187
+ required?: boolean;
188
+ value?: string;
189
+ }
190
+
191
+ type ColorPickerTriggerProps = React.ComponentProps<typeof Button>;
192
+
193
+ interface FormatInputProps extends ColorPickerInputProps {
194
+ color: ColorValue;
195
+ context: ColorPickerContextValue;
196
+ onColorChange: (color: ColorValue) => void;
197
+ }
198
+
199
+ interface HsbInputProps extends Omit<FormatInputProps, 'color'> {
200
+ hsv: HSVColorValue;
201
+ }
202
+
203
+ function ColorPicker(props: ColorPickerProps) {
204
+ const {
205
+ defaultFormat = 'hex',
206
+ defaultOpen,
207
+ defaultValue = '#000000',
208
+ disabled,
209
+ format: formatProp,
210
+ inline,
211
+ name,
212
+ onFormatChange,
213
+ onOpenChange,
214
+ onValueChange,
215
+ open: openProp,
216
+ readOnly,
217
+ required,
218
+ value: valueProp,
219
+ ...rootProps
220
+ } = props;
221
+
222
+ const listenersRef = useLazyRef(() => new Set<() => void>());
223
+ const stateRef = useLazyRef<StoreState>(() => {
224
+ const colorString = valueProp ?? defaultValue;
225
+ const color = parseColorString(colorString) ?? hexToRgb(colorString);
226
+
227
+ return {
228
+ color,
229
+ format: formatProp ?? defaultFormat,
230
+ hsv: rgbToHsv(color),
231
+ open: openProp ?? defaultOpen ?? false,
232
+ };
233
+ });
234
+
235
+ const propsRef = useAsRef({
236
+ onFormatChange,
237
+ onOpenChange,
238
+ onValueChange,
239
+ });
240
+
241
+ const store = React.useMemo<Store>(() => {
242
+ return {
243
+ getState: () => stateRef.current,
244
+ notify: () => {
245
+ for (const cb of listenersRef.current) {
246
+ cb();
247
+ }
248
+ },
249
+ setFormat: (value: ColorFormat) => {
250
+ if (Object.is(stateRef.current.format, value)) return;
251
+
252
+ stateRef.current.format = value;
253
+
254
+ if (propsRef.current.onFormatChange) {
255
+ propsRef.current.onFormatChange(value);
256
+ }
257
+
258
+ store.notify();
259
+ },
260
+ setOpen: (value: boolean) => {
261
+ if (Object.is(stateRef.current.open, value)) return;
262
+
263
+ stateRef.current.open = value;
264
+
265
+ if (propsRef.current.onOpenChange) {
266
+ propsRef.current.onOpenChange(value);
267
+ }
268
+
269
+ store.notify();
270
+ },
271
+ setValue: (color: ColorValue, hsv: HSVColorValue, options) => {
272
+ const prevState = { ...stateRef.current };
273
+ const colorChanged = !isColorEqual(prevState.color, color);
274
+ const hsvChanged = !isHsvEqual(prevState.hsv, hsv);
275
+
276
+ if (!colorChanged && !hsvChanged) return;
277
+
278
+ stateRef.current.color = color;
279
+ stateRef.current.hsv = hsv;
280
+
281
+ if (
282
+ options?.emit !== false &&
283
+ colorChanged &&
284
+ propsRef.current.onValueChange
285
+ ) {
286
+ const colorString = colorToString(color, prevState.format);
287
+ propsRef.current.onValueChange(colorString);
288
+ }
289
+
290
+ store.notify();
291
+ },
292
+ subscribe: (cb) => {
293
+ listenersRef.current.add(cb);
294
+ return () => listenersRef.current.delete(cb);
295
+ },
296
+ };
297
+ }, [listenersRef, stateRef, propsRef]);
298
+
299
+ return (
300
+ <StoreContext.Provider value={store}>
301
+ <ColorPickerImpl
302
+ {...rootProps}
303
+ defaultOpen={defaultOpen}
304
+ disabled={disabled}
305
+ inline={inline}
306
+ name={name}
307
+ open={openProp}
308
+ readOnly={readOnly}
309
+ required={required}
310
+ value={valueProp}
311
+ />
312
+ </StoreContext.Provider>
313
+ );
314
+ }
315
+
316
+ function ColorPickerAlphaSlider(props: React.ComponentProps<typeof Slider>) {
317
+ const { className, ...sliderProps } = props;
318
+
319
+ const context = useColorPickerContext(ALPHA_SLIDER_NAME);
320
+ const store = useStoreContext(ALPHA_SLIDER_NAME);
321
+
322
+ const color = useStore((state) => state.color);
323
+ const hsv = useStore((state) => state.hsv);
324
+
325
+ const onValueChange = React.useCallback(
326
+ (value: number | readonly number[]) => {
327
+ const alpha = getSliderValue(value) / 100;
328
+ const newColor = { ...color, a: alpha };
329
+ const newHsv = { ...hsv, a: alpha };
330
+ store.setValue(newColor, newHsv);
331
+ },
332
+ [color, hsv, store],
333
+ );
334
+
335
+ const gradientColor = `rgb(${color?.r ?? 0}, ${color?.g ?? 0}, ${color?.b ?? 0})`;
336
+
337
+ return (
338
+ <Slider
339
+ data-slot="color-picker-alpha-slider"
340
+ {...sliderProps}
341
+ className={className}
342
+ disabled={context.disabled}
343
+ indicator={false}
344
+ max={100}
345
+ onValueChange={onValueChange}
346
+ step={1}
347
+ trackStyle={{
348
+ backgroundImage: `linear-gradient(to right, transparent, ${gradientColor}), linear-gradient(45deg, #ccc 25%, transparent 25%), linear-gradient(-45deg, #ccc 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #ccc 75%), linear-gradient(-45deg, transparent 75%, #ccc 75%)`,
349
+ backgroundPosition: '0 0, 0 0, 0 4px, 4px -4px, -4px 0px',
350
+ backgroundSize: 'auto, 8px 8px, 8px 8px, 8px 8px, 8px 8px',
351
+ }}
352
+ value={[Math.round((color?.a ?? 1) * 100)]}
353
+ />
354
+ );
355
+ }
356
+
357
+ function ColorPickerArea(props: DivProps) {
358
+ const {
359
+ className,
360
+ onPointerDown: onPointerDownProp,
361
+ onPointerMove: onPointerMoveProp,
362
+ onPointerUp: onPointerUpProp,
363
+ ref,
364
+ render,
365
+ ...areaProps
366
+ } = props;
367
+
368
+ const propsRef = useAsRef({
369
+ onPointerDown: onPointerDownProp,
370
+ onPointerMove: onPointerMoveProp,
371
+ onPointerUp: onPointerUpProp,
372
+ });
373
+
374
+ const context = useColorPickerContext(AREA_NAME);
375
+ const store = useStoreContext(AREA_NAME);
376
+
377
+ const hsv = useStore((state) => state.hsv);
378
+
379
+ const isDraggingRef = React.useRef(false);
380
+ const areaRef = React.useRef<HTMLDivElement>(null);
381
+ const composedRef = useComposedRefs(ref, areaRef);
382
+
383
+ const updateColorFromPosition = React.useCallback(
384
+ (clientX: number, clientY: number) => {
385
+ if (!areaRef.current) return;
386
+
387
+ const rect = areaRef.current.getBoundingClientRect();
388
+ const x = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
389
+ const y = Math.max(
390
+ 0,
391
+ Math.min(1, 1 - (clientY - rect.top) / rect.height),
392
+ );
393
+
394
+ const newHsv: HSVColorValue = {
395
+ a: hsv?.a ?? 1,
396
+ h: hsv?.h ?? 0,
397
+ s: Math.round(x * 100),
398
+ v: Math.round(y * 100),
399
+ };
400
+
401
+ store.setValue(hsvToRgb(newHsv), newHsv);
402
+ },
403
+ [hsv, store],
404
+ );
405
+
406
+ const onPointerDown = React.useCallback(
407
+ (event: React.PointerEvent<AreaElement>) => {
408
+ if (context.disabled) return;
409
+ propsRef.current.onPointerDown?.(event);
410
+ if (event.defaultPrevented) return;
411
+
412
+ isDraggingRef.current = true;
413
+ areaRef.current?.setPointerCapture(event.pointerId);
414
+ updateColorFromPosition(event.clientX, event.clientY);
415
+ },
416
+ [context.disabled, updateColorFromPosition, propsRef],
417
+ );
418
+
419
+ const onPointerMove = React.useCallback(
420
+ (event: React.PointerEvent<AreaElement>) => {
421
+ propsRef.current.onPointerMove?.(event);
422
+ if (event.defaultPrevented) return;
423
+
424
+ if (isDraggingRef.current) {
425
+ updateColorFromPosition(event.clientX, event.clientY);
426
+ }
427
+ },
428
+ [updateColorFromPosition, propsRef],
429
+ );
430
+
431
+ const onPointerUp = React.useCallback(
432
+ (event: React.PointerEvent<AreaElement>) => {
433
+ propsRef.current.onPointerUp?.(event);
434
+ if (event.defaultPrevented) return;
435
+
436
+ isDraggingRef.current = false;
437
+ areaRef.current?.releasePointerCapture(event.pointerId);
438
+ },
439
+ [propsRef],
440
+ );
441
+
442
+ const hue = hsv?.h ?? 0;
443
+ const backgroundHue = hsvToRgb({ a: 1, h: hue, s: 100, v: 100 });
444
+
445
+ return useRender({
446
+ defaultTagName: 'div',
447
+ props: {
448
+ 'data-slot': 'color-picker-area',
449
+ ...areaProps,
450
+ children: (
451
+ <>
452
+ <div className="absolute inset-0 overflow-hidden rounded-sm">
453
+ <div
454
+ className="absolute inset-0"
455
+ style={{
456
+ backgroundColor: `rgb(${backgroundHue.r}, ${backgroundHue.g}, ${backgroundHue.b})`,
457
+ }}
458
+ />
459
+ <div
460
+ className="absolute inset-0"
461
+ style={{
462
+ background: 'linear-gradient(to right, #fff, transparent)',
463
+ }}
464
+ />
465
+ <div
466
+ className="absolute inset-0"
467
+ style={{
468
+ background: 'linear-gradient(to bottom, transparent, #000)',
469
+ }}
470
+ />
471
+ </div>
472
+ <div
473
+ className="absolute size-3 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white shadow-sm"
474
+ style={{
475
+ left: `${hsv?.s ?? 0}%`,
476
+ top: `${100 - (hsv?.v ?? 0)}%`,
477
+ }}
478
+ />
479
+ </>
480
+ ),
481
+ className: cn(
482
+ 'relative h-40 w-full cursor-crosshair touch-none rounded-sm border',
483
+ context.disabled && 'pointer-events-none opacity-50',
484
+ className,
485
+ ),
486
+ onPointerDown,
487
+ onPointerMove,
488
+ onPointerUp,
489
+ },
490
+ ref: composedRef,
491
+ render,
492
+ });
493
+ }
494
+
495
+ function ColorPickerContent(props: ColorPickerContentProps) {
496
+ const {
497
+ align,
498
+ children,
499
+ className,
500
+ render,
501
+ side,
502
+ sideOffset,
503
+ ...popoverContentProps
504
+ } = props;
505
+
506
+ const context = useColorPickerContext(CONTENT_NAME);
507
+ const inlineContent = useRender({
508
+ defaultTagName: 'div',
509
+ enabled: Boolean(context.inline),
510
+ props: {
511
+ 'data-slot': 'color-picker-content',
512
+ ...(popoverContentProps as React.ComponentProps<'div'>),
513
+ children,
514
+ className: cn('flex w-[340px] flex-col gap-4 p-4', className),
515
+ },
516
+ render: render as undefined | useRender.RenderProp,
517
+ });
518
+
519
+ if (context.inline) {
520
+ return inlineContent;
521
+ }
522
+
523
+ return (
524
+ <PopoverContent
525
+ align={align}
526
+ data-slot="color-picker-content"
527
+ render={render}
528
+ side={side}
529
+ sideOffset={sideOffset}
530
+ {...popoverContentProps}
531
+ className={cn('flex w-[340px] flex-col gap-4 p-4', className)}
532
+ >
533
+ {children}
534
+ </PopoverContent>
535
+ );
536
+ }
537
+
538
+ function ColorPickerEyeDropper(props: React.ComponentProps<typeof Button>) {
539
+ const { children, disabled, size: sizeProp, ...buttonProps } = props;
540
+
541
+ const context = useColorPickerContext(EYE_DROPPER_NAME);
542
+ const store = useStoreContext(EYE_DROPPER_NAME);
543
+
544
+ const color = useStore((state) => state.color);
545
+ const hsv = useStore((state) => state.hsv);
546
+
547
+ const isDisabled = disabled || context.disabled;
548
+
549
+ const onEyeDropper = React.useCallback(async () => {
550
+ if (!window.EyeDropper) return;
551
+
552
+ try {
553
+ const eyeDropper = new window.EyeDropper();
554
+ const result = await eyeDropper.open();
555
+
556
+ if (result.sRGBHex) {
557
+ const currentAlpha = color?.a ?? 1;
558
+ const newColor = hexToRgb(result.sRGBHex, currentAlpha);
559
+ const newHsv = rgbToHsv(newColor, hsv?.h ?? 0);
560
+ store.setValue(newColor, newHsv);
561
+ }
562
+ } catch (error) {
563
+ console.warn('EyeDropper error:', error);
564
+ }
565
+ }, [color, hsv, store]);
566
+
567
+ const hasEyeDropper = typeof window !== 'undefined' && !!window.EyeDropper;
568
+
569
+ if (!hasEyeDropper) return null;
570
+
571
+ return (
572
+ <Button
573
+ data-slot="color-picker-eye-dropper"
574
+ {...buttonProps}
575
+ disabled={isDisabled}
576
+ onClick={onEyeDropper}
577
+ size={sizeProp}
578
+ variant="outline"
579
+ >
580
+ {children ?? <IconColorPicker />}
581
+ </Button>
582
+ );
583
+ }
584
+
585
+ function ColorPickerFormatSelect(props: ColorPickerFormatSelectProps) {
586
+ const { className, disabled, ...selectProps } = props;
587
+
588
+ const context = useColorPickerContext(FORMAT_SELECT_NAME);
589
+ const store = useStoreContext(FORMAT_SELECT_NAME);
590
+ const isDisabled = disabled || context.disabled;
591
+
592
+ const format = useStore((state) => state.format);
593
+
594
+ const onFormatChange = React.useCallback(
595
+ (value: ColorFormat) => {
596
+ store.setFormat(value);
597
+ },
598
+ [store],
599
+ );
600
+
601
+ return (
602
+ <Select
603
+ data-slot="color-picker-format-select"
604
+ {...selectProps}
605
+ disabled={isDisabled}
606
+ onValueChange={(value) => onFormatChange(value as ColorFormat)}
607
+ value={format}
608
+ >
609
+ <SelectTrigger
610
+ className={cn(className)}
611
+ data-slot="color-picker-format-select-trigger"
612
+ >
613
+ <SelectValue />
614
+ </SelectTrigger>
615
+ <SelectContent>
616
+ {colorFormats.map((format) => (
617
+ <SelectItem key={format} value={format}>
618
+ {format.toUpperCase()}
619
+ </SelectItem>
620
+ ))}
621
+ </SelectContent>
622
+ </Select>
623
+ );
624
+ }
625
+
626
+ function ColorPickerHueSlider(props: React.ComponentProps<typeof Slider>) {
627
+ const { className, ...sliderProps } = props;
628
+
629
+ const context = useColorPickerContext(HUE_SLIDER_NAME);
630
+ const store = useStoreContext(HUE_SLIDER_NAME);
631
+
632
+ const hsv = useStore((state) => state.hsv);
633
+
634
+ const onValueChange = React.useCallback(
635
+ (value: number | readonly number[]) => {
636
+ const newHsv: HSVColorValue = {
637
+ a: hsv?.a ?? 1,
638
+ h: getSliderValue(value),
639
+ s: hsv?.s ?? 0,
640
+ v: hsv?.v ?? 0,
641
+ };
642
+ store.setValue(hsvToRgb(newHsv), newHsv);
643
+ },
644
+ [hsv, store],
645
+ );
646
+
647
+ return (
648
+ <Slider
649
+ data-slot="color-picker-hue-slider"
650
+ {...sliderProps}
651
+ className={className}
652
+ disabled={context.disabled}
653
+ indicator={false}
654
+ max={360}
655
+ onValueChange={onValueChange}
656
+ step={1}
657
+ trackStyle={{
658
+ background:
659
+ 'linear-gradient(to right,#ff0000 0%,#ffff00 16.66%,#00ff00 33.33%,#00ffff 50%,#0000ff 66.66%,#ff00ff 83.33%,#ff0000 100%)',
660
+ }}
661
+ value={[hsv?.h ?? 0]}
662
+ />
663
+ );
664
+ }
665
+
666
+ function ColorPickerImpl(props: ColorPickerImplProps) {
667
+ const {
668
+ defaultOpen,
669
+ dir: dirProp,
670
+ disabled,
671
+ inline,
672
+ modal,
673
+ name,
674
+ open: openProp,
675
+ readOnly,
676
+ ref,
677
+ render,
678
+ required,
679
+ value: valueProp,
680
+ ...rootProps
681
+ } = props;
682
+
683
+ const store = useStoreContext(ROOT_IMPL_NAME);
684
+
685
+ const contextDir = useDirection();
686
+ const dir = dirProp ?? contextDir;
687
+
688
+ const [formTrigger, setFormTrigger] = React.useState<null | RootElement>(
689
+ null,
690
+ );
691
+ const composedRef = useComposedRefs(ref, (node) => setFormTrigger(node));
692
+ const isFormControl = formTrigger ? !!formTrigger.closest('form') : true;
693
+
694
+ useIsomorphicLayoutEffect(() => {
695
+ if (valueProp !== undefined) {
696
+ const currentState = store.getState();
697
+ const color =
698
+ parseColorString(valueProp) ??
699
+ hexToRgb(valueProp, currentState.color.a);
700
+ const hsv = rgbToHsv(color, currentState.hsv.h);
701
+ store.setValue(color, hsv, { emit: false });
702
+ }
703
+ }, [valueProp]);
704
+
705
+ useIsomorphicLayoutEffect(() => {
706
+ if (openProp !== undefined) {
707
+ store.setOpen(openProp);
708
+ }
709
+ }, [openProp]);
710
+
711
+ const contextValue = React.useMemo<ColorPickerContextValue>(
712
+ () => ({
713
+ dir,
714
+ disabled,
715
+ inline,
716
+ readOnly,
717
+ required,
718
+ }),
719
+ [dir, disabled, inline, readOnly, required],
720
+ );
721
+
722
+ const value = useStore((state) => rgbToHex(state.color));
723
+ const open = useStore((state) => state.open);
724
+
725
+ const root = useRender({
726
+ defaultTagName: 'div',
727
+ props: rootProps,
728
+ ref: composedRef,
729
+ render,
730
+ });
731
+
732
+ if (inline) {
733
+ return (
734
+ <ColorPickerContext.Provider value={contextValue}>
735
+ {root}
736
+ {isFormControl && (
737
+ <VisuallyHiddenInput
738
+ control={formTrigger}
739
+ disabled={disabled}
740
+ name={name}
741
+ readOnly={readOnly}
742
+ required={required}
743
+ type="hidden"
744
+ value={value}
745
+ />
746
+ )}
747
+ </ColorPickerContext.Provider>
748
+ );
749
+ }
750
+
751
+ return (
752
+ <ColorPickerContext.Provider value={contextValue}>
753
+ <Popover
754
+ defaultOpen={defaultOpen}
755
+ modal={modal}
756
+ onOpenChange={store.setOpen}
757
+ open={open}
758
+ >
759
+ {root}
760
+ {isFormControl && (
761
+ <VisuallyHiddenInput
762
+ control={formTrigger}
763
+ disabled={disabled}
764
+ name={name}
765
+ readOnly={readOnly}
766
+ required={required}
767
+ type="hidden"
768
+ value={value}
769
+ />
770
+ )}
771
+ </Popover>
772
+ </ColorPickerContext.Provider>
773
+ );
774
+ }
775
+
776
+ function ColorPickerInput(props: ColorPickerInputProps) {
777
+ const store = useStoreContext(INPUT_NAME);
778
+ const context = useColorPickerContext(INPUT_NAME);
779
+
780
+ const color = useStore((state) => state.color);
781
+ const format = useStore((state) => state.format);
782
+ const hsv = useStore((state) => state.hsv);
783
+
784
+ const onColorChange = React.useCallback(
785
+ (newColor: ColorValue) => {
786
+ const newHsv = rgbToHsv(newColor, hsv?.h ?? 0);
787
+ store.setValue(newColor, newHsv);
788
+ },
789
+ [hsv, store],
790
+ );
791
+
792
+ if (format === 'hex') {
793
+ return (
794
+ <HexInput
795
+ color={color}
796
+ context={context}
797
+ onColorChange={onColorChange}
798
+ {...props}
799
+ />
800
+ );
801
+ }
802
+
803
+ if (format === 'rgb') {
804
+ return (
805
+ <RgbInput
806
+ color={color}
807
+ context={context}
808
+ onColorChange={onColorChange}
809
+ {...props}
810
+ />
811
+ );
812
+ }
813
+
814
+ if (format === 'hsl') {
815
+ return (
816
+ <HslInput
817
+ color={color}
818
+ context={context}
819
+ onColorChange={onColorChange}
820
+ {...props}
821
+ />
822
+ );
823
+ }
824
+
825
+ if (format === 'hsb') {
826
+ return (
827
+ <HsbInput
828
+ context={context}
829
+ hsv={hsv}
830
+ onColorChange={onColorChange}
831
+ {...props}
832
+ />
833
+ );
834
+ }
835
+ }
836
+
837
+ function ColorPickerSwatch(props: DivProps) {
838
+ const { className, render, ...swatchProps } = props;
839
+
840
+ const context = useColorPickerContext(SWATCH_NAME);
841
+
842
+ const color = useStore((state) => state.color);
843
+ const format = useStore((state) => state.format);
844
+
845
+ const backgroundStyle = React.useMemo(() => {
846
+ if (!color) {
847
+ return {
848
+ background:
849
+ 'linear-gradient(to bottom right, transparent calc(50% - 1px), hsl(var(--destructive)) calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px)) no-repeat',
850
+ };
851
+ }
852
+
853
+ const colorString = `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`;
854
+
855
+ if (color.a < 1) {
856
+ return {
857
+ background: `linear-gradient(${colorString}, ${colorString}), repeating-conic-gradient(#ccc 0% 25%, #fff 0% 50%) 0% 50% / 8px 8px`,
858
+ };
859
+ }
860
+
861
+ return {
862
+ backgroundColor: colorString,
863
+ };
864
+ }, [color]);
865
+
866
+ const ariaLabel = !color
867
+ ? 'No color selected'
868
+ : `Current color: ${colorToString(color, format)}`;
869
+
870
+ return useRender({
871
+ defaultTagName: 'div',
872
+ props: {
873
+ 'aria-label': ariaLabel,
874
+ 'data-slot': 'color-picker-swatch',
875
+ role: 'img',
876
+ ...swatchProps,
877
+ className: cn(
878
+ 'box-border size-8 shrink-0 rounded-sm border shadow-sm',
879
+ context.disabled && 'opacity-50',
880
+ className,
881
+ ),
882
+ style: {
883
+ ...backgroundStyle,
884
+ forcedColorAdjust: 'none',
885
+ },
886
+ },
887
+ render,
888
+ });
889
+ }
890
+
891
+ function ColorPickerTrigger(props: ColorPickerTriggerProps) {
892
+ const { disabled, render, ...triggerProps } = props;
893
+
894
+ const context = useColorPickerContext(TRIGGER_NAME);
895
+
896
+ const isDisabled = disabled || context.disabled;
897
+
898
+ return (
899
+ <PopoverTrigger
900
+ data-slot="color-picker-trigger"
901
+ disabled={isDisabled}
902
+ render={<Button render={render} />}
903
+ {...triggerProps}
904
+ />
905
+ );
906
+ }
907
+
908
+ function HexInput(props: FormatInputProps) {
909
+ const {
910
+ className,
911
+ color,
912
+ context,
913
+ onColorChange,
914
+ withoutAlpha,
915
+ ...inputProps
916
+ } = props;
917
+
918
+ const hexValue = rgbToHex(color);
919
+ const alphaValue = Math.round((color?.a ?? 1) * 100);
920
+
921
+ const onHexChange = React.useCallback(
922
+ (event: React.ChangeEvent<InputElement>) => {
923
+ const value = event.target.value;
924
+ const parsedColor = parseColorString(value);
925
+ if (parsedColor) {
926
+ onColorChange({ ...parsedColor, a: color?.a ?? 1 });
927
+ }
928
+ },
929
+ [color, onColorChange],
930
+ );
931
+
932
+ const onAlphaChange = React.useCallback(
933
+ (event: React.ChangeEvent<InputElement>) => {
934
+ const value = Number.parseInt(event.target.value, 10);
935
+ if (!Number.isNaN(value) && value >= 0 && value <= 100) {
936
+ onColorChange({ ...color, a: value / 100 });
937
+ }
938
+ },
939
+ [color, onColorChange],
940
+ );
941
+
942
+ if (withoutAlpha) {
943
+ return (
944
+ <Input
945
+ aria-label="Hex color value"
946
+ {...inputProps}
947
+ className={cn('font-mono', className)}
948
+ disabled={context.disabled}
949
+ onChange={onHexChange}
950
+ placeholder="#000000"
951
+ value={hexValue}
952
+ />
953
+ );
954
+ }
955
+
956
+ return (
957
+ <InputGroup className={className}>
958
+ <InputGroupInput
959
+ aria-label="Hex color value"
960
+ {...inputProps}
961
+ className="flex-1 font-mono"
962
+ disabled={context.disabled}
963
+ onChange={onHexChange}
964
+ placeholder="#000000"
965
+ value={hexValue}
966
+ />
967
+ <InputGroupInput
968
+ aria-label="Alpha transparency percentage"
969
+ {...inputProps}
970
+ className="w-14"
971
+ disabled={context.disabled}
972
+ inputMode="numeric"
973
+ max="100"
974
+ min="0"
975
+ onChange={onAlphaChange}
976
+ pattern="[0-9]*"
977
+ placeholder="100"
978
+ value={alphaValue}
979
+ />
980
+ </InputGroup>
981
+ );
982
+ }
983
+
984
+ function HsbInput(props: HsbInputProps) {
985
+ const {
986
+ className,
987
+ context,
988
+ hsv,
989
+ onColorChange,
990
+ withoutAlpha,
991
+ ...inputProps
992
+ } = props;
993
+
994
+ const alphaValue = Math.round((hsv?.a ?? 1) * 100);
995
+
996
+ const onHsvChannelChange = React.useCallback(
997
+ (channel: 'h' | 's' | 'v', max: number) =>
998
+ (event: React.ChangeEvent<InputElement>) => {
999
+ const value = Number.parseInt(event.target.value, 10);
1000
+ if (!Number.isNaN(value) && value >= 0 && value <= max) {
1001
+ const newHsv = { ...hsv, [channel]: value };
1002
+ const newColor = hsvToRgb(newHsv);
1003
+ onColorChange(newColor);
1004
+ }
1005
+ },
1006
+ [hsv, onColorChange],
1007
+ );
1008
+
1009
+ const onAlphaChange = React.useCallback(
1010
+ (event: React.ChangeEvent<InputElement>) => {
1011
+ const value = Number.parseInt(event.target.value, 10);
1012
+ if (!Number.isNaN(value) && value >= 0 && value <= 100) {
1013
+ const currentColor = hsvToRgb(hsv);
1014
+ onColorChange({ ...currentColor, a: value / 100 });
1015
+ }
1016
+ },
1017
+ [hsv, onColorChange],
1018
+ );
1019
+
1020
+ return (
1021
+ <InputGroup className={className}>
1022
+ <InputGroupInput
1023
+ aria-label="Hue degree (0-360)"
1024
+ {...inputProps}
1025
+ className="w-14"
1026
+ disabled={context.disabled}
1027
+ inputMode="numeric"
1028
+ max="360"
1029
+ min="0"
1030
+ onChange={onHsvChannelChange('h', 360)}
1031
+ pattern="[0-9]*"
1032
+ placeholder="0"
1033
+ value={hsv?.h ?? 0}
1034
+ />
1035
+ <InputGroupInput
1036
+ aria-label="Saturation percentage (0-100)"
1037
+ {...inputProps}
1038
+ className="w-14"
1039
+ disabled={context.disabled}
1040
+ inputMode="numeric"
1041
+ max="100"
1042
+ min="0"
1043
+ onChange={onHsvChannelChange('s', 100)}
1044
+ pattern="[0-9]*"
1045
+ placeholder="0"
1046
+ value={hsv?.s ?? 0}
1047
+ />
1048
+ <InputGroupInput
1049
+ aria-label="Brightness percentage (0-100)"
1050
+ {...inputProps}
1051
+ className="w-14"
1052
+ disabled={context.disabled}
1053
+ inputMode="numeric"
1054
+ max="100"
1055
+ min="0"
1056
+ onChange={onHsvChannelChange('v', 100)}
1057
+ pattern="[0-9]*"
1058
+ placeholder="0"
1059
+ value={hsv?.v ?? 0}
1060
+ />
1061
+ {!withoutAlpha && (
1062
+ <InputGroupInput
1063
+ aria-label="Alpha transparency percentage"
1064
+ {...inputProps}
1065
+ className="w-14"
1066
+ disabled={context.disabled}
1067
+ inputMode="numeric"
1068
+ max="100"
1069
+ min="0"
1070
+ onChange={onAlphaChange}
1071
+ pattern="[0-9]*"
1072
+ placeholder="100"
1073
+ value={alphaValue}
1074
+ />
1075
+ )}
1076
+ </InputGroup>
1077
+ );
1078
+ }
1079
+
1080
+ function HslInput(props: FormatInputProps) {
1081
+ const {
1082
+ className,
1083
+ color,
1084
+ context,
1085
+ onColorChange,
1086
+ withoutAlpha,
1087
+ ...inputProps
1088
+ } = props;
1089
+
1090
+ const hsl = React.useMemo(() => rgbToHsl(color), [color]);
1091
+ const alphaValue = Math.round((color?.a ?? 1) * 100);
1092
+
1093
+ const onHslChannelChange = React.useCallback(
1094
+ (channel: 'h' | 'l' | 's', max: number) =>
1095
+ (event: React.ChangeEvent<InputElement>) => {
1096
+ const value = Number.parseInt(event.target.value, 10);
1097
+ if (!Number.isNaN(value) && value >= 0 && value <= max) {
1098
+ const newHsl = { ...hsl, [channel]: value };
1099
+ const newColor = hslToRgb(newHsl, color?.a ?? 1);
1100
+ onColorChange(newColor);
1101
+ }
1102
+ },
1103
+ [hsl, color, onColorChange],
1104
+ );
1105
+
1106
+ const onAlphaChange = React.useCallback(
1107
+ (event: React.ChangeEvent<InputElement>) => {
1108
+ const value = Number.parseInt(event.target.value, 10);
1109
+ if (!Number.isNaN(value) && value >= 0 && value <= 100) {
1110
+ onColorChange({ ...color, a: value / 100 });
1111
+ }
1112
+ },
1113
+ [color, onColorChange],
1114
+ );
1115
+
1116
+ return (
1117
+ <InputGroup className={className}>
1118
+ <InputGroupInput
1119
+ aria-label="Hue degree (0-360)"
1120
+ {...inputProps}
1121
+ className="w-14"
1122
+ disabled={context.disabled}
1123
+ inputMode="numeric"
1124
+ max="360"
1125
+ min="0"
1126
+ onChange={onHslChannelChange('h', 360)}
1127
+ pattern="[0-9]*"
1128
+ placeholder="0"
1129
+ value={hsl.h}
1130
+ />
1131
+ <InputGroupInput
1132
+ aria-label="Saturation percentage (0-100)"
1133
+ {...inputProps}
1134
+ className="w-14"
1135
+ disabled={context.disabled}
1136
+ inputMode="numeric"
1137
+ max="100"
1138
+ min="0"
1139
+ onChange={onHslChannelChange('s', 100)}
1140
+ pattern="[0-9]*"
1141
+ placeholder="0"
1142
+ value={hsl.s}
1143
+ />
1144
+ <InputGroupInput
1145
+ aria-label="Lightness percentage (0-100)"
1146
+ {...inputProps}
1147
+ className="w-14"
1148
+ disabled={context.disabled}
1149
+ inputMode="numeric"
1150
+ max="100"
1151
+ min="0"
1152
+ onChange={onHslChannelChange('l', 100)}
1153
+ pattern="[0-9]*"
1154
+ placeholder="0"
1155
+ value={hsl.l}
1156
+ />
1157
+ {!withoutAlpha && (
1158
+ <InputGroupInput
1159
+ aria-label="Alpha transparency percentage"
1160
+ {...inputProps}
1161
+ className="w-14"
1162
+ disabled={context.disabled}
1163
+ inputMode="numeric"
1164
+ max="100"
1165
+ min="0"
1166
+ onChange={onAlphaChange}
1167
+ pattern="[0-9]*"
1168
+ placeholder="100"
1169
+ value={alphaValue}
1170
+ />
1171
+ )}
1172
+ </InputGroup>
1173
+ );
1174
+ }
1175
+
1176
+ function RgbInput(props: FormatInputProps) {
1177
+ const {
1178
+ className,
1179
+ color,
1180
+ context,
1181
+ onColorChange,
1182
+ withoutAlpha,
1183
+ ...inputProps
1184
+ } = props;
1185
+
1186
+ const rValue = Math.round(color?.r ?? 0);
1187
+ const gValue = Math.round(color?.g ?? 0);
1188
+ const bValue = Math.round(color?.b ?? 0);
1189
+ const alphaValue = Math.round((color?.a ?? 1) * 100);
1190
+
1191
+ const onChannelChange = React.useCallback(
1192
+ (channel: 'a' | 'b' | 'g' | 'r', max: number, isAlpha = false) =>
1193
+ (event: React.ChangeEvent<InputElement>) => {
1194
+ const value = Number.parseInt(event.target.value, 10);
1195
+ if (!Number.isNaN(value) && value >= 0 && value <= max) {
1196
+ const newValue = isAlpha ? value / 100 : value;
1197
+ onColorChange({ ...color, [channel]: newValue });
1198
+ }
1199
+ },
1200
+ [color, onColorChange],
1201
+ );
1202
+
1203
+ return (
1204
+ <InputGroup className={className}>
1205
+ <InputGroupInput
1206
+ aria-label="Red color component (0-255)"
1207
+ {...inputProps}
1208
+ className="w-14"
1209
+ disabled={context.disabled}
1210
+ inputMode="numeric"
1211
+ max="255"
1212
+ min="0"
1213
+ onChange={onChannelChange('r', 255)}
1214
+ pattern="[0-9]*"
1215
+ placeholder="0"
1216
+ value={rValue}
1217
+ />
1218
+ <InputGroupInput
1219
+ aria-label="Green color component (0-255)"
1220
+ {...inputProps}
1221
+ className="w-14"
1222
+ disabled={context.disabled}
1223
+ inputMode="numeric"
1224
+ max="255"
1225
+ min="0"
1226
+ onChange={onChannelChange('g', 255)}
1227
+ pattern="[0-9]*"
1228
+ placeholder="0"
1229
+ value={gValue}
1230
+ />
1231
+ <InputGroupInput
1232
+ aria-label="Blue color component (0-255)"
1233
+ {...inputProps}
1234
+ className="w-14"
1235
+ disabled={context.disabled}
1236
+ inputMode="numeric"
1237
+ max="255"
1238
+ min="0"
1239
+ onChange={onChannelChange('b', 255)}
1240
+ pattern="[0-9]*"
1241
+ placeholder="0"
1242
+ value={bValue}
1243
+ />
1244
+ {!withoutAlpha && (
1245
+ <InputGroupInput
1246
+ aria-label="Alpha transparency percentage"
1247
+ {...inputProps}
1248
+ className="w-14"
1249
+ disabled={context.disabled}
1250
+ inputMode="numeric"
1251
+ max="100"
1252
+ min="0"
1253
+ onChange={onChannelChange('a', 100, true)}
1254
+ pattern="[0-9]*"
1255
+ placeholder="100"
1256
+ value={alphaValue}
1257
+ />
1258
+ )}
1259
+ </InputGroup>
1260
+ );
1261
+ }
1262
+
1263
+ function useColorPickerContext(consumerName: string) {
1264
+ const context = React.useContext(ColorPickerContext);
1265
+ if (!context) {
1266
+ throw new Error(`\`${consumerName}\` must be used within \`${ROOT_NAME}\``);
1267
+ }
1268
+ return context;
1269
+ }
1270
+
1271
+ export {
1272
+ ColorPicker,
1273
+ ColorPickerAlphaSlider,
1274
+ ColorPickerArea,
1275
+ ColorPickerContent,
1276
+ ColorPickerEyeDropper,
1277
+ ColorPickerFormatSelect,
1278
+ ColorPickerHueSlider,
1279
+ ColorPickerInput,
1280
+ type ColorPickerProps,
1281
+ ColorPickerSwatch,
1282
+ ColorPickerTrigger,
1283
+ };