@misoto22/folio 0.0.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (686) hide show
  1. package/AGENTS.md +88 -0
  2. package/CLAUDE.md +1 -0
  3. package/LICENSE +21 -0
  4. package/README.md +208 -2
  5. package/bin/folio-design.mjs +277 -0
  6. package/bin/migrate-legacy.mjs +81 -0
  7. package/dist/agent/Accordion.md +65 -0
  8. package/dist/agent/Actions.md +49 -0
  9. package/dist/agent/Alert.md +65 -0
  10. package/dist/agent/AppShell.md +69 -0
  11. package/dist/agent/ArchitectureFigure.md +72 -0
  12. package/dist/agent/AreaChart.md +94 -0
  13. package/dist/agent/Article.md +65 -0
  14. package/dist/agent/AspectRatio.md +58 -0
  15. package/dist/agent/Avatar.md +53 -0
  16. package/dist/agent/Badge.md +62 -0
  17. package/dist/agent/BarChart.md +93 -0
  18. package/dist/agent/BarList.md +67 -0
  19. package/dist/agent/BigNumber.md +70 -0
  20. package/dist/agent/BoxPlot.md +82 -0
  21. package/dist/agent/Breadcrumb.md +55 -0
  22. package/dist/agent/BulletChart.md +71 -0
  23. package/dist/agent/Button.md +81 -0
  24. package/dist/agent/Calendar.md +77 -0
  25. package/dist/agent/CalendarHeatmap.md +75 -0
  26. package/dist/agent/Card.md +72 -0
  27. package/dist/agent/Checkbox.md +57 -0
  28. package/dist/agent/Code.md +51 -0
  29. package/dist/agent/CodeBlock.md +77 -0
  30. package/dist/agent/Collapsible.md +68 -0
  31. package/dist/agent/Collection.md +169 -0
  32. package/dist/agent/ColorPicker.md +76 -0
  33. package/dist/agent/Combobox.md +77 -0
  34. package/dist/agent/Command.md +122 -0
  35. package/dist/agent/ComposedChart.md +82 -0
  36. package/dist/agent/Contact.md +90 -0
  37. package/dist/agent/Content.md +70 -0
  38. package/dist/agent/ContextMenu.md +93 -0
  39. package/dist/agent/Conversation.md +247 -0
  40. package/dist/agent/DataflowFigure.md +49 -0
  41. package/dist/agent/DatePicker.md +104 -0
  42. package/dist/agent/DescriptionList.md +69 -0
  43. package/dist/agent/Diagram.md +71 -0
  44. package/dist/agent/DiagramCanvas.md +71 -0
  45. package/dist/agent/DiagramExportMenu.md +65 -0
  46. package/dist/agent/DiagramInspector.md +73 -0
  47. package/dist/agent/DiagramLegend.md +56 -0
  48. package/dist/agent/DiagramMinimap.md +69 -0
  49. package/dist/agent/DiagramToolbar.md +70 -0
  50. package/dist/agent/Dialog.md +76 -0
  51. package/dist/agent/DropdownMenu.md +110 -0
  52. package/dist/agent/EmptyState.md +63 -0
  53. package/dist/agent/ErrorState.md +67 -0
  54. package/dist/agent/Evidence.md +165 -0
  55. package/dist/agent/Facet.md +107 -0
  56. package/dist/agent/Field.md +79 -0
  57. package/dist/agent/FigureBand.md +49 -0
  58. package/dist/agent/FloatingIconButton.md +61 -0
  59. package/dist/agent/FunnelChart.md +75 -0
  60. package/dist/agent/Heading.md +65 -0
  61. package/dist/agent/Heatmap.md +78 -0
  62. package/dist/agent/Histogram.md +88 -0
  63. package/dist/agent/Input.md +52 -0
  64. package/dist/agent/Kbd.md +48 -0
  65. package/dist/agent/LifecycleFigure.md +50 -0
  66. package/dist/agent/LineChart.md +85 -0
  67. package/dist/agent/LinkArrow.md +49 -0
  68. package/dist/agent/Markdown.md +69 -0
  69. package/dist/agent/Media.md +261 -0
  70. package/dist/agent/Metric.md +70 -0
  71. package/dist/agent/Metrics.md +114 -0
  72. package/dist/agent/Music.md +86 -0
  73. package/dist/agent/NativeSelect.md +62 -0
  74. package/dist/agent/NavItem.md +56 -0
  75. package/dist/agent/NumberField.md +73 -0
  76. package/dist/agent/PageHeader.md +69 -0
  77. package/dist/agent/Pagination.md +70 -0
  78. package/dist/agent/PieChart.md +80 -0
  79. package/dist/agent/Popover.md +80 -0
  80. package/dist/agent/Portfolio.md +93 -0
  81. package/dist/agent/PortfolioIndex.md +34 -0
  82. package/dist/agent/Progress.md +58 -0
  83. package/dist/agent/RadarChart.md +79 -0
  84. package/dist/agent/RadialChart.md +81 -0
  85. package/dist/agent/RadioGroup.md +66 -0
  86. package/dist/agent/Reading.md +233 -0
  87. package/dist/agent/Recovery.md +72 -0
  88. package/dist/agent/SankeyChart.md +85 -0
  89. package/dist/agent/ScatterChart.md +80 -0
  90. package/dist/agent/ScrollArea.md +62 -0
  91. package/dist/agent/SearchPalette.md +51 -0
  92. package/dist/agent/SearchableMenu.md +69 -0
  93. package/dist/agent/Select.md +102 -0
  94. package/dist/agent/Separator.md +60 -0
  95. package/dist/agent/SequenceFigure.md +51 -0
  96. package/dist/agent/Sheet.md +82 -0
  97. package/dist/agent/Sidebar.md +194 -0
  98. package/dist/agent/SiteNavigation.md +62 -0
  99. package/dist/agent/SiteShell.md +67 -0
  100. package/dist/agent/Skeleton.md +96 -0
  101. package/dist/agent/Slider.md +67 -0
  102. package/dist/agent/Sparkline.md +68 -0
  103. package/dist/agent/Spinner.md +62 -0
  104. package/dist/agent/StatusDot.md +56 -0
  105. package/dist/agent/StatusPill.md +55 -0
  106. package/dist/agent/Steps.md +68 -0
  107. package/dist/agent/Switch.md +50 -0
  108. package/dist/agent/Table.md +111 -0
  109. package/dist/agent/Tabs.md +71 -0
  110. package/dist/agent/Tag.md +67 -0
  111. package/dist/agent/Text.md +66 -0
  112. package/dist/agent/Textarea.md +46 -0
  113. package/dist/agent/Timeline.md +114 -0
  114. package/dist/agent/Timestamp.md +65 -0
  115. package/dist/agent/Toast.md +51 -0
  116. package/dist/agent/ToggleGroup.md +67 -0
  117. package/dist/agent/Toolbar.md +72 -0
  118. package/dist/agent/Tooltip.md +74 -0
  119. package/dist/agent/TreemapChart.md +76 -0
  120. package/dist/agent/WaterfallChart.md +86 -0
  121. package/dist/agent/WorkflowFigure.md +51 -0
  122. package/dist/agent/catalog.json +8006 -0
  123. package/dist/agent/index.md +182 -0
  124. package/dist/article.css +407 -0
  125. package/dist/charts/AreaChart/AreaChart.d.ts +198 -0
  126. package/dist/charts/AreaChart/AreaChart.js +479 -0
  127. package/dist/charts/AreaChart/AreaChart.js.map +1 -0
  128. package/dist/charts/BarChart/BarChart.d.ts +190 -0
  129. package/dist/charts/BarChart/BarChart.js +599 -0
  130. package/dist/charts/BarChart/BarChart.js.map +1 -0
  131. package/dist/charts/BarList/BarList.d.ts +65 -0
  132. package/dist/charts/BarList/BarList.js +79 -0
  133. package/dist/charts/BarList/BarList.js.map +1 -0
  134. package/dist/charts/BigNumber/BigNumber.d.ts +72 -0
  135. package/dist/charts/BigNumber/BigNumber.js +51 -0
  136. package/dist/charts/BigNumber/BigNumber.js.map +1 -0
  137. package/dist/charts/BoxPlot/BoxPlot.d.ts +177 -0
  138. package/dist/charts/BoxPlot/BoxPlot.js +497 -0
  139. package/dist/charts/BoxPlot/BoxPlot.js.map +1 -0
  140. package/dist/charts/BulletChart/BulletChart.d.ts +100 -0
  141. package/dist/charts/BulletChart/BulletChart.js +197 -0
  142. package/dist/charts/BulletChart/BulletChart.js.map +1 -0
  143. package/dist/charts/CalendarHeatmap/CalendarHeatmap.d.ts +106 -0
  144. package/dist/charts/CalendarHeatmap/CalendarHeatmap.js +103 -0
  145. package/dist/charts/CalendarHeatmap/CalendarHeatmap.js.map +1 -0
  146. package/dist/charts/ComposedChart/ComposedChart.d.ts +178 -0
  147. package/dist/charts/ComposedChart/ComposedChart.js +667 -0
  148. package/dist/charts/ComposedChart/ComposedChart.js.map +1 -0
  149. package/dist/charts/Facet/Facet.d.ts +240 -0
  150. package/dist/charts/Facet/Facet.js +133 -0
  151. package/dist/charts/Facet/Facet.js.map +1 -0
  152. package/dist/charts/Facet/panels.d.ts +187 -0
  153. package/dist/charts/Facet/panels.js +203 -0
  154. package/dist/charts/Facet/panels.js.map +1 -0
  155. package/dist/charts/FunnelChart/FunnelChart.d.ts +106 -0
  156. package/dist/charts/FunnelChart/FunnelChart.js +137 -0
  157. package/dist/charts/FunnelChart/FunnelChart.js.map +1 -0
  158. package/dist/charts/Heatmap/Heatmap.d.ts +101 -0
  159. package/dist/charts/Heatmap/Heatmap.js +125 -0
  160. package/dist/charts/Heatmap/Heatmap.js.map +1 -0
  161. package/dist/charts/Histogram/Histogram.d.ts +188 -0
  162. package/dist/charts/Histogram/Histogram.js +382 -0
  163. package/dist/charts/Histogram/Histogram.js.map +1 -0
  164. package/dist/charts/LineChart/LineChart.d.ts +174 -0
  165. package/dist/charts/LineChart/LineChart.js +485 -0
  166. package/dist/charts/LineChart/LineChart.js.map +1 -0
  167. package/dist/charts/PieChart/PieChart.d.ts +140 -0
  168. package/dist/charts/PieChart/PieChart.js +317 -0
  169. package/dist/charts/PieChart/PieChart.js.map +1 -0
  170. package/dist/charts/RadarChart/RadarChart.d.ts +135 -0
  171. package/dist/charts/RadarChart/RadarChart.js +303 -0
  172. package/dist/charts/RadarChart/RadarChart.js.map +1 -0
  173. package/dist/charts/RadialChart/RadialChart.d.ts +142 -0
  174. package/dist/charts/RadialChart/RadialChart.js +279 -0
  175. package/dist/charts/RadialChart/RadialChart.js.map +1 -0
  176. package/dist/charts/SankeyChart/SankeyChart.d.ts +149 -0
  177. package/dist/charts/SankeyChart/SankeyChart.js +509 -0
  178. package/dist/charts/SankeyChart/SankeyChart.js.map +1 -0
  179. package/dist/charts/ScatterChart/ScatterChart.d.ts +149 -0
  180. package/dist/charts/ScatterChart/ScatterChart.js +275 -0
  181. package/dist/charts/ScatterChart/ScatterChart.js.map +1 -0
  182. package/dist/charts/Sparkline/Sparkline.d.ts +53 -0
  183. package/dist/charts/Sparkline/Sparkline.js +95 -0
  184. package/dist/charts/Sparkline/Sparkline.js.map +1 -0
  185. package/dist/charts/TreemapChart/TreemapChart.d.ts +98 -0
  186. package/dist/charts/TreemapChart/TreemapChart.js +127 -0
  187. package/dist/charts/TreemapChart/TreemapChart.js.map +1 -0
  188. package/dist/charts/WaterfallChart/WaterfallChart.d.ts +164 -0
  189. package/dist/charts/WaterfallChart/WaterfallChart.js +321 -0
  190. package/dist/charts/WaterfallChart/WaterfallChart.js.map +1 -0
  191. package/dist/charts/index.d.ts +43 -0
  192. package/dist/charts/index.js +130 -0
  193. package/dist/charts/index.js.map +1 -0
  194. package/dist/charts/lib/annotations.d.ts +103 -0
  195. package/dist/charts/lib/annotations.js +99 -0
  196. package/dist/charts/lib/annotations.js.map +1 -0
  197. package/dist/charts/lib/axis.d.ts +18 -0
  198. package/dist/charts/lib/axis.js +21 -0
  199. package/dist/charts/lib/axis.js.map +1 -0
  200. package/dist/charts/lib/background.d.ts +33 -0
  201. package/dist/charts/lib/background.js +173 -0
  202. package/dist/charts/lib/background.js.map +1 -0
  203. package/dist/charts/lib/brush.d.ts +94 -0
  204. package/dist/charts/lib/brush.js +535 -0
  205. package/dist/charts/lib/brush.js.map +1 -0
  206. package/dist/charts/lib/chart.d.ts +94 -0
  207. package/dist/charts/lib/chart.js +149 -0
  208. package/dist/charts/lib/chart.js.map +1 -0
  209. package/dist/charts/lib/dot.d.ts +29 -0
  210. package/dist/charts/lib/dot.js +78 -0
  211. package/dist/charts/lib/dot.js.map +1 -0
  212. package/dist/charts/lib/empty.d.ts +31 -0
  213. package/dist/charts/lib/empty.js +24 -0
  214. package/dist/charts/lib/empty.js.map +1 -0
  215. package/dist/charts/lib/export.d.ts +94 -0
  216. package/dist/charts/lib/export.js +62 -0
  217. package/dist/charts/lib/export.js.map +1 -0
  218. package/dist/charts/lib/figure.d.ts +81 -0
  219. package/dist/charts/lib/figure.js +62 -0
  220. package/dist/charts/lib/figure.js.map +1 -0
  221. package/dist/charts/lib/format.d.ts +46 -0
  222. package/dist/charts/lib/format.js +56 -0
  223. package/dist/charts/lib/format.js.map +1 -0
  224. package/dist/charts/lib/legend.d.ts +45 -0
  225. package/dist/charts/lib/legend.js +117 -0
  226. package/dist/charts/lib/legend.js.map +1 -0
  227. package/dist/charts/lib/loading.d.ts +48 -0
  228. package/dist/charts/lib/loading.js +105 -0
  229. package/dist/charts/lib/loading.js.map +1 -0
  230. package/dist/charts/lib/paint.d.ts +154 -0
  231. package/dist/charts/lib/paint.js +551 -0
  232. package/dist/charts/lib/paint.js.map +1 -0
  233. package/dist/charts/lib/scale.d.ts +23 -0
  234. package/dist/charts/lib/scale.js +13 -0
  235. package/dist/charts/lib/scale.js.map +1 -0
  236. package/dist/charts/lib/selection.d.ts +20 -0
  237. package/dist/charts/lib/selection.js +18 -0
  238. package/dist/charts/lib/selection.js.map +1 -0
  239. package/dist/charts/lib/sonify-control.d.ts +163 -0
  240. package/dist/charts/lib/sonify-control.js +172 -0
  241. package/dist/charts/lib/sonify-control.js.map +1 -0
  242. package/dist/charts/lib/sonify.d.ts +248 -0
  243. package/dist/charts/lib/sonify.js +262 -0
  244. package/dist/charts/lib/sonify.js.map +1 -0
  245. package/dist/charts/lib/toolbar.d.ts +144 -0
  246. package/dist/charts/lib/toolbar.js +156 -0
  247. package/dist/charts/lib/toolbar.js.map +1 -0
  248. package/dist/charts/lib/tooltip.d.ts +59 -0
  249. package/dist/charts/lib/tooltip.js +135 -0
  250. package/dist/charts/lib/tooltip.js.map +1 -0
  251. package/dist/charts/lib/values.d.ts +56 -0
  252. package/dist/charts/lib/values.js +71 -0
  253. package/dist/charts/lib/values.js.map +1 -0
  254. package/dist/charts/lib/zoom.d.ts +139 -0
  255. package/dist/charts/lib/zoom.js +314 -0
  256. package/dist/charts/lib/zoom.js.map +1 -0
  257. package/dist/components/Accordion/Accordion.d.ts +34 -0
  258. package/dist/components/Accordion/Accordion.js +43 -0
  259. package/dist/components/Accordion/Accordion.js.map +1 -0
  260. package/dist/components/Alert/Alert.d.ts +32 -0
  261. package/dist/components/Alert/Alert.js +49 -0
  262. package/dist/components/Alert/Alert.js.map +1 -0
  263. package/dist/components/AppShell/AppShell.d.ts +58 -0
  264. package/dist/components/AppShell/AppShell.js +126 -0
  265. package/dist/components/AppShell/AppShell.js.map +1 -0
  266. package/dist/components/Article/Article.d.ts +58 -0
  267. package/dist/components/Article/Article.js +34 -0
  268. package/dist/components/Article/Article.js.map +1 -0
  269. package/dist/components/AspectRatio/AspectRatio.d.ts +46 -0
  270. package/dist/components/AspectRatio/AspectRatio.js +33 -0
  271. package/dist/components/AspectRatio/AspectRatio.js.map +1 -0
  272. package/dist/components/Avatar/Avatar.d.ts +38 -0
  273. package/dist/components/Avatar/Avatar.js +44 -0
  274. package/dist/components/Avatar/Avatar.js.map +1 -0
  275. package/dist/components/Badge/Badge.d.ts +30 -0
  276. package/dist/components/Badge/Badge.js +29 -0
  277. package/dist/components/Badge/Badge.js.map +1 -0
  278. package/dist/components/Breadcrumb/Breadcrumb.d.ts +37 -0
  279. package/dist/components/Breadcrumb/Breadcrumb.js +46 -0
  280. package/dist/components/Breadcrumb/Breadcrumb.js.map +1 -0
  281. package/dist/components/Button/Button.d.ts +70 -0
  282. package/dist/components/Button/Button.js +95 -0
  283. package/dist/components/Button/Button.js.map +1 -0
  284. package/dist/components/Calendar/Calendar.d.ts +54 -0
  285. package/dist/components/Calendar/Calendar.js +369 -0
  286. package/dist/components/Calendar/Calendar.js.map +1 -0
  287. package/dist/components/Card/Card.d.ts +55 -0
  288. package/dist/components/Card/Card.js +64 -0
  289. package/dist/components/Card/Card.js.map +1 -0
  290. package/dist/components/Checkbox/Checkbox.d.ts +26 -0
  291. package/dist/components/Checkbox/Checkbox.js +34 -0
  292. package/dist/components/Checkbox/Checkbox.js.map +1 -0
  293. package/dist/components/Code/Code.d.ts +30 -0
  294. package/dist/components/Code/Code.js +21 -0
  295. package/dist/components/Code/Code.js.map +1 -0
  296. package/dist/components/CodeBlock/CodeBlock.d.ts +145 -0
  297. package/dist/components/CodeBlock/CodeBlock.js +114 -0
  298. package/dist/components/CodeBlock/CodeBlock.js.map +1 -0
  299. package/dist/components/Collapsible/Collapsible.d.ts +45 -0
  300. package/dist/components/Collapsible/Collapsible.js +60 -0
  301. package/dist/components/Collapsible/Collapsible.js.map +1 -0
  302. package/dist/components/ColorPicker/ColorPicker.d.ts +67 -0
  303. package/dist/components/ColorPicker/ColorPicker.js +334 -0
  304. package/dist/components/ColorPicker/ColorPicker.js.map +1 -0
  305. package/dist/components/Combobox/Combobox.d.ts +77 -0
  306. package/dist/components/Combobox/Combobox.js +170 -0
  307. package/dist/components/Combobox/Combobox.js.map +1 -0
  308. package/dist/components/Command/Command.d.ts +125 -0
  309. package/dist/components/Command/Command.js +204 -0
  310. package/dist/components/Command/Command.js.map +1 -0
  311. package/dist/components/ContextMenu/ContextMenu.d.ts +63 -0
  312. package/dist/components/ContextMenu/ContextMenu.js +109 -0
  313. package/dist/components/ContextMenu/ContextMenu.js.map +1 -0
  314. package/dist/components/DatePicker/DatePicker.d.ts +130 -0
  315. package/dist/components/DatePicker/DatePicker.js +257 -0
  316. package/dist/components/DatePicker/DatePicker.js.map +1 -0
  317. package/dist/components/DescriptionList/DescriptionList.d.ts +73 -0
  318. package/dist/components/DescriptionList/DescriptionList.js +35 -0
  319. package/dist/components/DescriptionList/DescriptionList.js.map +1 -0
  320. package/dist/components/Diagram/Diagram.d.ts +86 -0
  321. package/dist/components/Diagram/Diagram.js +211 -0
  322. package/dist/components/Diagram/Diagram.js.map +1 -0
  323. package/dist/components/Dialog/Dialog.d.ts +63 -0
  324. package/dist/components/Dialog/Dialog.js +90 -0
  325. package/dist/components/Dialog/Dialog.js.map +1 -0
  326. package/dist/components/DropdownMenu/DropdownMenu.d.ts +87 -0
  327. package/dist/components/DropdownMenu/DropdownMenu.js +120 -0
  328. package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -0
  329. package/dist/components/EmptyState/EmptyState.d.ts +55 -0
  330. package/dist/components/EmptyState/EmptyState.js +32 -0
  331. package/dist/components/EmptyState/EmptyState.js.map +1 -0
  332. package/dist/components/ErrorState/ErrorState.d.ts +55 -0
  333. package/dist/components/ErrorState/ErrorState.js +41 -0
  334. package/dist/components/ErrorState/ErrorState.js.map +1 -0
  335. package/dist/components/Field/Field.d.ts +111 -0
  336. package/dist/components/Field/Field.js +95 -0
  337. package/dist/components/Field/Field.js.map +1 -0
  338. package/dist/components/Field/field-control.d.ts +31 -0
  339. package/dist/components/Field/field-control.js +12 -0
  340. package/dist/components/Field/field-control.js.map +1 -0
  341. package/dist/components/FigureBand/FigureBand.d.ts +31 -0
  342. package/dist/components/FigureBand/FigureBand.js +51 -0
  343. package/dist/components/FigureBand/FigureBand.js.map +1 -0
  344. package/dist/components/FloatingIconButton/FloatingIconButton.d.ts +29 -0
  345. package/dist/components/FloatingIconButton/FloatingIconButton.js +34 -0
  346. package/dist/components/FloatingIconButton/FloatingIconButton.js.map +1 -0
  347. package/dist/components/Heading/Heading.d.ts +62 -0
  348. package/dist/components/Heading/Heading.js +44 -0
  349. package/dist/components/Heading/Heading.js.map +1 -0
  350. package/dist/components/Input/Input.d.ts +22 -0
  351. package/dist/components/Input/Input.js +21 -0
  352. package/dist/components/Input/Input.js.map +1 -0
  353. package/dist/components/Kbd/Kbd.d.ts +27 -0
  354. package/dist/components/Kbd/Kbd.js +21 -0
  355. package/dist/components/Kbd/Kbd.js.map +1 -0
  356. package/dist/components/LinkArrow/LinkArrow.d.ts +24 -0
  357. package/dist/components/LinkArrow/LinkArrow.js +24 -0
  358. package/dist/components/LinkArrow/LinkArrow.js.map +1 -0
  359. package/dist/components/Markdown/Markdown.d.ts +139 -0
  360. package/dist/components/Markdown/Markdown.js +135 -0
  361. package/dist/components/Markdown/Markdown.js.map +1 -0
  362. package/dist/components/Markdown/inline.d.ts +29 -0
  363. package/dist/components/Markdown/inline.js +74 -0
  364. package/dist/components/Markdown/inline.js.map +1 -0
  365. package/dist/components/Markdown/nodes.d.ts +65 -0
  366. package/dist/components/Markdown/nodes.js +1 -0
  367. package/dist/components/Markdown/nodes.js.map +1 -0
  368. package/dist/components/Markdown/parse.d.ts +24 -0
  369. package/dist/components/Markdown/parse.js +110 -0
  370. package/dist/components/Markdown/parse.js.map +1 -0
  371. package/dist/components/Markdown/slug.d.ts +15 -0
  372. package/dist/components/Markdown/slug.js +7 -0
  373. package/dist/components/Markdown/slug.js.map +1 -0
  374. package/dist/components/Metric/Metric.d.ts +61 -0
  375. package/dist/components/Metric/Metric.js +51 -0
  376. package/dist/components/Metric/Metric.js.map +1 -0
  377. package/dist/components/NativeSelect/NativeSelect.d.ts +33 -0
  378. package/dist/components/NativeSelect/NativeSelect.js +44 -0
  379. package/dist/components/NativeSelect/NativeSelect.js.map +1 -0
  380. package/dist/components/NavItem/NavItem.d.ts +39 -0
  381. package/dist/components/NavItem/NavItem.js +31 -0
  382. package/dist/components/NavItem/NavItem.js.map +1 -0
  383. package/dist/components/NumberField/NumberField.d.ts +69 -0
  384. package/dist/components/NumberField/NumberField.js +162 -0
  385. package/dist/components/NumberField/NumberField.js.map +1 -0
  386. package/dist/components/PageHeader/PageHeader.d.ts +92 -0
  387. package/dist/components/PageHeader/PageHeader.js +37 -0
  388. package/dist/components/PageHeader/PageHeader.js.map +1 -0
  389. package/dist/components/Pagination/Pagination.d.ts +67 -0
  390. package/dist/components/Pagination/Pagination.js +112 -0
  391. package/dist/components/Pagination/Pagination.js.map +1 -0
  392. package/dist/components/Popover/Popover.d.ts +39 -0
  393. package/dist/components/Popover/Popover.js +57 -0
  394. package/dist/components/Popover/Popover.js.map +1 -0
  395. package/dist/components/Progress/Progress.d.ts +43 -0
  396. package/dist/components/Progress/Progress.js +57 -0
  397. package/dist/components/Progress/Progress.js.map +1 -0
  398. package/dist/components/RadioGroup/RadioGroup.d.ts +49 -0
  399. package/dist/components/RadioGroup/RadioGroup.js +53 -0
  400. package/dist/components/RadioGroup/RadioGroup.js.map +1 -0
  401. package/dist/components/ScrollArea/ScrollArea.d.ts +46 -0
  402. package/dist/components/ScrollArea/ScrollArea.js +54 -0
  403. package/dist/components/ScrollArea/ScrollArea.js.map +1 -0
  404. package/dist/components/SearchableMenu/SearchableMenu.d.ts +68 -0
  405. package/dist/components/SearchableMenu/SearchableMenu.js +101 -0
  406. package/dist/components/SearchableMenu/SearchableMenu.js.map +1 -0
  407. package/dist/components/Select/Select.d.ts +87 -0
  408. package/dist/components/Select/Select.js +130 -0
  409. package/dist/components/Select/Select.js.map +1 -0
  410. package/dist/components/Separator/Separator.d.ts +48 -0
  411. package/dist/components/Separator/Separator.js +43 -0
  412. package/dist/components/Separator/Separator.js.map +1 -0
  413. package/dist/components/Sheet/Sheet.d.ts +43 -0
  414. package/dist/components/Sheet/Sheet.js +79 -0
  415. package/dist/components/Sheet/Sheet.js.map +1 -0
  416. package/dist/components/Sidebar/Sidebar.d.ts +357 -0
  417. package/dist/components/Sidebar/Sidebar.js +447 -0
  418. package/dist/components/Sidebar/Sidebar.js.map +1 -0
  419. package/dist/components/Skeleton/Skeleton.d.ts +70 -0
  420. package/dist/components/Skeleton/Skeleton.js +44 -0
  421. package/dist/components/Skeleton/Skeleton.js.map +1 -0
  422. package/dist/components/Slider/Slider.d.ts +74 -0
  423. package/dist/components/Slider/Slider.js +143 -0
  424. package/dist/components/Slider/Slider.js.map +1 -0
  425. package/dist/components/Spinner/Spinner.d.ts +58 -0
  426. package/dist/components/Spinner/Spinner.js +49 -0
  427. package/dist/components/Spinner/Spinner.js.map +1 -0
  428. package/dist/components/StatusDot/StatusDot.d.ts +29 -0
  429. package/dist/components/StatusDot/StatusDot.js +44 -0
  430. package/dist/components/StatusDot/StatusDot.js.map +1 -0
  431. package/dist/components/StatusPill/StatusPill.d.ts +30 -0
  432. package/dist/components/StatusPill/StatusPill.js +37 -0
  433. package/dist/components/StatusPill/StatusPill.js.map +1 -0
  434. package/dist/components/Steps/Steps.d.ts +65 -0
  435. package/dist/components/Steps/Steps.js +25 -0
  436. package/dist/components/Steps/Steps.js.map +1 -0
  437. package/dist/components/Switch/Switch.d.ts +23 -0
  438. package/dist/components/Switch/Switch.js +23 -0
  439. package/dist/components/Switch/Switch.js.map +1 -0
  440. package/dist/components/Table/Table.d.ts +116 -0
  441. package/dist/components/Table/Table.js +137 -0
  442. package/dist/components/Table/Table.js.map +1 -0
  443. package/dist/components/Tabs/Tabs.d.ts +37 -0
  444. package/dist/components/Tabs/Tabs.js +205 -0
  445. package/dist/components/Tabs/Tabs.js.map +1 -0
  446. package/dist/components/Tag/Tag.d.ts +87 -0
  447. package/dist/components/Tag/Tag.js +62 -0
  448. package/dist/components/Tag/Tag.js.map +1 -0
  449. package/dist/components/Text/Text.d.ts +62 -0
  450. package/dist/components/Text/Text.js +30 -0
  451. package/dist/components/Text/Text.js.map +1 -0
  452. package/dist/components/Textarea/Textarea.d.ts +20 -0
  453. package/dist/components/Textarea/Textarea.js +32 -0
  454. package/dist/components/Textarea/Textarea.js.map +1 -0
  455. package/dist/components/Timestamp/Timestamp.d.ts +59 -0
  456. package/dist/components/Timestamp/Timestamp.js +56 -0
  457. package/dist/components/Timestamp/Timestamp.js.map +1 -0
  458. package/dist/components/Toast/Toast.d.ts +32 -0
  459. package/dist/components/Toast/Toast.js +64 -0
  460. package/dist/components/Toast/Toast.js.map +1 -0
  461. package/dist/components/ToggleGroup/ToggleGroup.d.ts +46 -0
  462. package/dist/components/ToggleGroup/ToggleGroup.js +88 -0
  463. package/dist/components/ToggleGroup/ToggleGroup.js.map +1 -0
  464. package/dist/components/Toolbar/Toolbar.d.ts +55 -0
  465. package/dist/components/Toolbar/Toolbar.js +43 -0
  466. package/dist/components/Toolbar/Toolbar.js.map +1 -0
  467. package/dist/components/Tooltip/Tooltip.d.ts +43 -0
  468. package/dist/components/Tooltip/Tooltip.js +40 -0
  469. package/dist/components/Tooltip/Tooltip.js.map +1 -0
  470. package/dist/diagrams/ArchitectureFigure/ArchitectureFigure.d.ts +55 -0
  471. package/dist/diagrams/ArchitectureFigure/ArchitectureFigure.js +175 -0
  472. package/dist/diagrams/ArchitectureFigure/ArchitectureFigure.js.map +1 -0
  473. package/dist/diagrams/DataflowFigure/DataflowFigure.d.ts +28 -0
  474. package/dist/diagrams/DataflowFigure/DataflowFigure.js +148 -0
  475. package/dist/diagrams/DataflowFigure/DataflowFigure.js.map +1 -0
  476. package/dist/diagrams/DiagramCanvas/DiagramCanvas.d.ts +78 -0
  477. package/dist/diagrams/DiagramCanvas/DiagramCanvas.js +229 -0
  478. package/dist/diagrams/DiagramCanvas/DiagramCanvas.js.map +1 -0
  479. package/dist/diagrams/DiagramExportMenu/DiagramExportMenu.d.ts +106 -0
  480. package/dist/diagrams/DiagramExportMenu/DiagramExportMenu.js +123 -0
  481. package/dist/diagrams/DiagramExportMenu/DiagramExportMenu.js.map +1 -0
  482. package/dist/diagrams/DiagramInspector/DiagramInspector.d.ts +70 -0
  483. package/dist/diagrams/DiagramInspector/DiagramInspector.js +94 -0
  484. package/dist/diagrams/DiagramInspector/DiagramInspector.js.map +1 -0
  485. package/dist/diagrams/DiagramLegend/DiagramLegend.d.ts +42 -0
  486. package/dist/diagrams/DiagramLegend/DiagramLegend.js +35 -0
  487. package/dist/diagrams/DiagramLegend/DiagramLegend.js.map +1 -0
  488. package/dist/diagrams/DiagramMinimap/DiagramMinimap.d.ts +77 -0
  489. package/dist/diagrams/DiagramMinimap/DiagramMinimap.js +106 -0
  490. package/dist/diagrams/DiagramMinimap/DiagramMinimap.js.map +1 -0
  491. package/dist/diagrams/DiagramToolbar/DiagramToolbar.d.ts +48 -0
  492. package/dist/diagrams/DiagramToolbar/DiagramToolbar.js +53 -0
  493. package/dist/diagrams/DiagramToolbar/DiagramToolbar.js.map +1 -0
  494. package/dist/diagrams/LifecycleFigure/LifecycleFigure.d.ts +31 -0
  495. package/dist/diagrams/LifecycleFigure/LifecycleFigure.js +234 -0
  496. package/dist/diagrams/LifecycleFigure/LifecycleFigure.js.map +1 -0
  497. package/dist/diagrams/SequenceFigure/SequenceFigure.d.ts +38 -0
  498. package/dist/diagrams/SequenceFigure/SequenceFigure.js +261 -0
  499. package/dist/diagrams/SequenceFigure/SequenceFigure.js.map +1 -0
  500. package/dist/diagrams/WorkflowFigure/WorkflowFigure.d.ts +36 -0
  501. package/dist/diagrams/WorkflowFigure/WorkflowFigure.js +259 -0
  502. package/dist/diagrams/WorkflowFigure/WorkflowFigure.js.map +1 -0
  503. package/dist/diagrams/index.d.ts +17 -0
  504. package/dist/diagrams/index.js +29 -0
  505. package/dist/diagrams/index.js.map +1 -0
  506. package/dist/diagrams/lib/dev.d.ts +61 -0
  507. package/dist/diagrams/lib/dev.js +96 -0
  508. package/dist/diagrams/lib/dev.js.map +1 -0
  509. package/dist/diagrams/lib/frame.d.ts +138 -0
  510. package/dist/diagrams/lib/frame.js +131 -0
  511. package/dist/diagrams/lib/frame.js.map +1 -0
  512. package/dist/diagrams/lib/geometry.d.ts +114 -0
  513. package/dist/diagrams/lib/geometry.js +113 -0
  514. package/dist/diagrams/lib/geometry.js.map +1 -0
  515. package/dist/diagrams/lib/legend.d.ts +32 -0
  516. package/dist/diagrams/lib/legend.js +84 -0
  517. package/dist/diagrams/lib/legend.js.map +1 -0
  518. package/dist/diagrams/lib/marks.d.ts +207 -0
  519. package/dist/diagrams/lib/marks.js +480 -0
  520. package/dist/diagrams/lib/marks.js.map +1 -0
  521. package/dist/diagrams/lib/route.d.ts +106 -0
  522. package/dist/diagrams/lib/route.js +201 -0
  523. package/dist/diagrams/lib/route.js.map +1 -0
  524. package/dist/diagrams/lib/wires.d.ts +59 -0
  525. package/dist/diagrams/lib/wires.js +94 -0
  526. package/dist/diagrams/lib/wires.js.map +1 -0
  527. package/dist/diagrams/spec.d.ts +405 -0
  528. package/dist/diagrams/spec.js +1 -0
  529. package/dist/diagrams/spec.js.map +1 -0
  530. package/dist/fonts/hanken-grotesk-300.woff2 +0 -0
  531. package/dist/fonts/hanken-grotesk-400.woff2 +0 -0
  532. package/dist/fonts/hanken-grotesk-500.woff2 +0 -0
  533. package/dist/fonts/hanken-grotesk-600.woff2 +0 -0
  534. package/dist/fonts/hanken-grotesk-700.woff2 +0 -0
  535. package/dist/fonts/ibm-plex-mono-400.woff2 +0 -0
  536. package/dist/fonts/ibm-plex-mono-500.woff2 +0 -0
  537. package/dist/fonts/ibm-plex-mono-600.woff2 +0 -0
  538. package/dist/fonts/newsreader-300.woff2 +0 -0
  539. package/dist/fonts/newsreader-400.woff2 +0 -0
  540. package/dist/fonts/newsreader-500.woff2 +0 -0
  541. package/dist/fonts/newsreader-600.woff2 +0 -0
  542. package/dist/fonts.css +96 -0
  543. package/dist/index.d.ts +97 -0
  544. package/dist/index.js +80 -0
  545. package/dist/index.js.map +1 -0
  546. package/dist/keyframes.css +300 -0
  547. package/dist/lib/cn.d.ts +21 -0
  548. package/dist/lib/cn.js +9 -0
  549. package/dist/lib/cn.js.map +1 -0
  550. package/dist/lib/color.d.ts +80 -0
  551. package/dist/lib/color.js +189 -0
  552. package/dist/lib/color.js.map +1 -0
  553. package/dist/lib/control.d.ts +32 -0
  554. package/dist/lib/control.js +14 -0
  555. package/dist/lib/control.js.map +1 -0
  556. package/dist/lib/index.d.ts +4 -0
  557. package/dist/lib/index.js +11 -0
  558. package/dist/lib/index.js.map +1 -0
  559. package/dist/lib/numeric.d.ts +35 -0
  560. package/dist/lib/numeric.js +29 -0
  561. package/dist/lib/numeric.js.map +1 -0
  562. package/dist/lib/overlay-container.d.ts +48 -0
  563. package/dist/lib/overlay-container.js +15 -0
  564. package/dist/lib/overlay-container.js.map +1 -0
  565. package/dist/lib/slot-content.d.ts +8 -0
  566. package/dist/lib/slot-content.js +17 -0
  567. package/dist/lib/slot-content.js.map +1 -0
  568. package/dist/lib/step-rail.d.ts +45 -0
  569. package/dist/lib/step-rail.js +47 -0
  570. package/dist/lib/step-rail.js.map +1 -0
  571. package/dist/lib/svg-export.d.ts +138 -0
  572. package/dist/lib/svg-export.js +198 -0
  573. package/dist/lib/svg-export.js.map +1 -0
  574. package/dist/lib/useClipboardCopy.d.ts +30 -0
  575. package/dist/lib/useClipboardCopy.js +37 -0
  576. package/dist/lib/useClipboardCopy.js.map +1 -0
  577. package/dist/lib/useSelectionIndicator.d.ts +29 -0
  578. package/dist/lib/useSelectionIndicator.js +49 -0
  579. package/dist/lib/useSelectionIndicator.js.map +1 -0
  580. package/dist/lib/warn.d.ts +73 -0
  581. package/dist/lib/warn.js +38 -0
  582. package/dist/lib/warn.js.map +1 -0
  583. package/dist/semantic.css +193 -0
  584. package/dist/styles.css +98 -0
  585. package/dist/themes.css +514 -0
  586. package/dist/tokens/brand.d.ts +47 -0
  587. package/dist/tokens/brand.js +38 -0
  588. package/dist/tokens/brand.js.map +1 -0
  589. package/dist/tokens.css +652 -0
  590. package/dist/tokens.d.ts +198 -0
  591. package/dist/tokens.js +1020 -0
  592. package/dist/tokens.json +1019 -0
  593. package/dist/website/Actions/Actions.d.ts +34 -0
  594. package/dist/website/Actions/Actions.js +29 -0
  595. package/dist/website/Actions/Actions.js.map +1 -0
  596. package/dist/website/Collection/Collection.d.ts +128 -0
  597. package/dist/website/Collection/Collection.js +162 -0
  598. package/dist/website/Collection/Collection.js.map +1 -0
  599. package/dist/website/Contact/Contact.d.ts +87 -0
  600. package/dist/website/Contact/Contact.js +94 -0
  601. package/dist/website/Contact/Contact.js.map +1 -0
  602. package/dist/website/Content/CodePanel.d.ts +27 -0
  603. package/dist/website/Content/CodePanel.js +30 -0
  604. package/dist/website/Content/CodePanel.js.map +1 -0
  605. package/dist/website/Content/Content.d.ts +29 -0
  606. package/dist/website/Content/Content.js +38 -0
  607. package/dist/website/Content/Content.js.map +1 -0
  608. package/dist/website/Conversation/Conversation.d.ts +193 -0
  609. package/dist/website/Conversation/Conversation.js +252 -0
  610. package/dist/website/Conversation/Conversation.js.map +1 -0
  611. package/dist/website/Evidence/Evidence.d.ts +116 -0
  612. package/dist/website/Evidence/Evidence.js +142 -0
  613. package/dist/website/Evidence/Evidence.js.map +1 -0
  614. package/dist/website/Media/Media.d.ts +173 -0
  615. package/dist/website/Media/Media.js +225 -0
  616. package/dist/website/Media/Media.js.map +1 -0
  617. package/dist/website/Media/MediaLightbox.d.ts +18 -0
  618. package/dist/website/Media/MediaLightbox.js +38 -0
  619. package/dist/website/Media/MediaLightbox.js.map +1 -0
  620. package/dist/website/Metrics/Metrics.d.ts +74 -0
  621. package/dist/website/Metrics/Metrics.js +96 -0
  622. package/dist/website/Metrics/Metrics.js.map +1 -0
  623. package/dist/website/Music/Music.d.ts +60 -0
  624. package/dist/website/Music/Music.js +80 -0
  625. package/dist/website/Music/Music.js.map +1 -0
  626. package/dist/website/Portfolio/FrameLayout.d.ts +9 -0
  627. package/dist/website/Portfolio/FrameLayout.js +42 -0
  628. package/dist/website/Portfolio/FrameLayout.js.map +1 -0
  629. package/dist/website/Portfolio/Portfolio.d.ts +49 -0
  630. package/dist/website/Portfolio/Portfolio.js +66 -0
  631. package/dist/website/Portfolio/Portfolio.js.map +1 -0
  632. package/dist/website/Portfolio/QuestionCard.d.ts +22 -0
  633. package/dist/website/Portfolio/QuestionCard.js +33 -0
  634. package/dist/website/Portfolio/QuestionCard.js.map +1 -0
  635. package/dist/website/PortfolioIndex/PortfolioIndex.d.ts +21 -0
  636. package/dist/website/PortfolioIndex/PortfolioIndex.js +31 -0
  637. package/dist/website/PortfolioIndex/PortfolioIndex.js.map +1 -0
  638. package/dist/website/Reading/Reading.d.ts +203 -0
  639. package/dist/website/Reading/Reading.js +357 -0
  640. package/dist/website/Reading/Reading.js.map +1 -0
  641. package/dist/website/Recovery/Recovery.d.ts +39 -0
  642. package/dist/website/Recovery/Recovery.js +96 -0
  643. package/dist/website/Recovery/Recovery.js.map +1 -0
  644. package/dist/website/SearchPalette/SearchPalette.d.ts +59 -0
  645. package/dist/website/SearchPalette/SearchPalette.js +120 -0
  646. package/dist/website/SearchPalette/SearchPalette.js.map +1 -0
  647. package/dist/website/SiteNavigation/SiteNavigation.d.ts +46 -0
  648. package/dist/website/SiteNavigation/SiteNavigation.js +101 -0
  649. package/dist/website/SiteNavigation/SiteNavigation.js.map +1 -0
  650. package/dist/website/SiteShell/SiteShell.d.ts +45 -0
  651. package/dist/website/SiteShell/SiteShell.js +46 -0
  652. package/dist/website/SiteShell/SiteShell.js.map +1 -0
  653. package/dist/website/Timeline/Timeline.d.ts +80 -0
  654. package/dist/website/Timeline/Timeline.js +99 -0
  655. package/dist/website/Timeline/Timeline.js.map +1 -0
  656. package/dist/website/index.d.ts +27 -0
  657. package/dist/website/index.js +21 -0
  658. package/dist/website/index.js.map +1 -0
  659. package/dist/website-actions.css +6 -0
  660. package/dist/website-base.css +46 -0
  661. package/dist/website-collection.css +89 -0
  662. package/dist/website-contact.css +29 -0
  663. package/dist/website-content.css +17 -0
  664. package/dist/website-controls.css +91 -0
  665. package/dist/website-conversation.css +115 -0
  666. package/dist/website-evidence.css +56 -0
  667. package/dist/website-media.css +227 -0
  668. package/dist/website-metrics.css +15 -0
  669. package/dist/website-music.css +27 -0
  670. package/dist/website-portfolio.css +94 -0
  671. package/dist/website-reading.css +159 -0
  672. package/dist/website-recovery.css +28 -0
  673. package/dist/website-search.css +26 -0
  674. package/dist/website-shell.css +89 -0
  675. package/dist/website-timeline.css +43 -0
  676. package/dist/website.css +25 -0
  677. package/llms.txt +35 -0
  678. package/package.json +163 -5
  679. package/skills/folio-design/SKILL.md +228 -0
  680. package/skills/folio-design/evals/claims.json +95 -0
  681. package/skills/folio-design/evals/evals.json +84 -0
  682. package/skills/folio-design/rules/a11y.md +167 -0
  683. package/skills/folio-design/rules/composition.md +108 -0
  684. package/skills/folio-design/rules/forms.md +135 -0
  685. package/skills/folio-design/rules/naming.md +155 -0
  686. package/skills/folio-design/rules/tokens.md +133 -0
@@ -0,0 +1,334 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useId, useRef, useState } from "react";
4
+ import { cn } from "../../lib/cn.js";
5
+ import {
6
+ clamp,
7
+ colorFormat,
8
+ colorToRgb,
9
+ fitGamut,
10
+ formatColor,
11
+ maxChroma,
12
+ parseColor
13
+ } from "../../lib/color.js";
14
+ import { CONTROL_BASE, CONTROL_BORDER, isInvalid } from "../../lib/control.js";
15
+ import { warnBlankName } from "../../lib/warn.js";
16
+ import { useFieldControl } from "../Field/field-control.js";
17
+ import { Input } from "../Input/Input.js";
18
+ import { Popover, PopoverContent, PopoverTrigger } from "../Popover/Popover.js";
19
+ import { ToggleGroup, ToggleGroupItem } from "../ToggleGroup/ToggleGroup.js";
20
+ const PLANE_W = 256;
21
+ const PLANE_H = 160;
22
+ const HUE_STOPS = 73;
23
+ const BLACK = { l: 0, c: 0, h: 0, a: 1 };
24
+ const FORMAT_LABELS = {
25
+ hex: "Hex",
26
+ oklch: "OKLCH",
27
+ // Not "Display P3": the three segments share the strip evenly, and the long
28
+ // name wraps to two lines inside its third and makes the whole row twice as
29
+ // tall as the two beside it.
30
+ p3: "P3"
31
+ };
32
+ const checkerboard = (square) => `repeating-conic-gradient(var(--stone) 0% 25%, var(--paper) 0% 50%) 0 0 / ${square} ${square}`;
33
+ function ColorPicker({
34
+ label,
35
+ value: controlled,
36
+ defaultValue = "#000000",
37
+ onValueChange,
38
+ disabled,
39
+ invalid,
40
+ className,
41
+ id,
42
+ "aria-describedby": describedBy,
43
+ "aria-invalid": ariaInvalid,
44
+ "aria-required": ariaRequired
45
+ }) {
46
+ warnBlankName("ColorPicker", "label", label, "the trigger is announced with no name");
47
+ const bad = isInvalid(invalid, ariaInvalid);
48
+ const field = useFieldControl();
49
+ const generated = useId();
50
+ const triggerId = id ?? generated;
51
+ const valueId = `${triggerId}-value`;
52
+ const nameId = field?.labelId ?? `${triggerId}-name`;
53
+ const [uncontrolled, setUncontrolled] = useState(defaultValue);
54
+ const value = controlled ?? uncontrolled;
55
+ const [color, setColor] = useState(() => parseColor(defaultValue) ?? BLACK);
56
+ const [format, setFormat] = useState(() => colorFormat(defaultValue));
57
+ const emitted = useRef(null);
58
+ const saturationRef = useRef(0);
59
+ useEffect(() => {
60
+ if (value === emitted.current) return;
61
+ const parsed = parseColor(value);
62
+ if (!parsed) return;
63
+ setColor((prev) => ({ ...parsed, h: parsed.c < 1e-7 ? prev.h : parsed.h }));
64
+ setFormat(colorFormat(value));
65
+ }, [value]);
66
+ const space = format === "hex" ? "srgb" : "p3";
67
+ const ceiling = maxChroma(color.l, color.h, space);
68
+ const saturation = ceiling > 0 ? clamp(color.c / ceiling) : saturationRef.current;
69
+ useEffect(() => {
70
+ if (ceiling > 0) saturationRef.current = saturation;
71
+ }, [ceiling, saturation]);
72
+ const commit = (next, nextFormat = format) => {
73
+ const fitted = nextFormat === "oklch" ? next : fitGamut(next, nextFormat === "p3" ? "p3" : "srgb");
74
+ setColor(fitted);
75
+ setFormat(nextFormat);
76
+ const text = formatColor(fitted, nextFormat);
77
+ emitted.current = text;
78
+ if (controlled === void 0) setUncontrolled(text);
79
+ onValueChange?.(text);
80
+ };
81
+ const setPlane = (nextSaturation, lightness) => {
82
+ saturationRef.current = nextSaturation;
83
+ commit({ ...color, l: lightness, c: nextSaturation * maxChroma(lightness, color.h, space) });
84
+ };
85
+ const planeRef = useRef(null);
86
+ const canvasRef = useRef(null);
87
+ const painted = useRef("");
88
+ const trackPointer = (event) => {
89
+ const rect = planeRef.current?.getBoundingClientRect();
90
+ if (!rect || rect.width === 0 || rect.height === 0) return;
91
+ setPlane(
92
+ clamp((event.clientX - rect.left) / rect.width),
93
+ clamp(1 - (event.clientY - rect.top) / rect.height)
94
+ );
95
+ };
96
+ const paint = (canvas) => {
97
+ if (!canvas) return;
98
+ const key = `${color.h.toFixed(3)}:${space}`;
99
+ if (painted.current === key) return;
100
+ const context = canvas.getContext("2d");
101
+ if (!context || typeof context.createImageData !== "function") return;
102
+ painted.current = key;
103
+ const pixels = context.createImageData(PLANE_W, PLANE_H);
104
+ for (let y = 0; y < PLANE_H; y++) {
105
+ const l = 1 - y / (PLANE_H - 1);
106
+ const rowMax = maxChroma(l, color.h, space);
107
+ for (let x = 0; x < PLANE_W; x++) {
108
+ const rgb = colorToRgb({ l, c: x / (PLANE_W - 1) * rowMax, h: color.h, a: 1 }, "srgb");
109
+ const at = (y * PLANE_W + x) * 4;
110
+ for (let channel = 0; channel < 3; channel++) {
111
+ pixels.data[at + channel] = Math.round(clamp(rgb[channel] ?? 0) * 255);
112
+ }
113
+ pixels.data[at + 3] = 255;
114
+ }
115
+ }
116
+ context.putImageData(pixels, 0, 0);
117
+ };
118
+ useEffect(() => {
119
+ paint(canvasRef.current);
120
+ });
121
+ const opaque = formatColor({ ...color, a: 1 }, "oklch");
122
+ const hueTrack = Array.from({ length: HUE_STOPS }, (_, i) => {
123
+ const h = i * (360 / (HUE_STOPS - 1));
124
+ return formatColor({ l: color.l, c: saturation * maxChroma(color.l, h, space), h, a: 1 }, "oklch");
125
+ }).join(", ");
126
+ const typed = parseColor(value);
127
+ return /* @__PURE__ */ jsxs(Popover, { children: [
128
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
129
+ "button",
130
+ {
131
+ type: "button",
132
+ id: triggerId,
133
+ disabled,
134
+ "aria-labelledby": `${nameId} ${valueId}`,
135
+ "aria-describedby": describedBy,
136
+ "aria-required": ariaRequired,
137
+ "aria-invalid": bad || void 0,
138
+ className: cn(
139
+ CONTROL_BASE,
140
+ bad ? CONTROL_BORDER.invalid : CONTROL_BORDER.resting,
141
+ "flex cursor-pointer items-center gap-2.5 text-start",
142
+ className
143
+ ),
144
+ children: [
145
+ field?.labelId == null && /* @__PURE__ */ jsx("span", { id: nameId, className: "sr-only", children: label }),
146
+ /* @__PURE__ */ jsx(
147
+ "span",
148
+ {
149
+ "aria-hidden": "true",
150
+ className: "size-4 shrink-0 rounded-(--radius-xs) border border-(--rule-2)",
151
+ style: { background: `linear-gradient(${value}, ${value}), ${checkerboard("4px")}` }
152
+ }
153
+ ),
154
+ /* @__PURE__ */ jsx("span", { id: valueId, className: "mono-meta truncate", children: value })
155
+ ]
156
+ }
157
+ ) }),
158
+ /* @__PURE__ */ jsxs(PopoverContent, { label: `${label} picker`, className: "flex w-72 flex-col gap-3", children: [
159
+ /* @__PURE__ */ jsx(
160
+ ToggleGroup,
161
+ {
162
+ type: "single",
163
+ value: format,
164
+ onValueChange: (next) => next && commit(color, next),
165
+ "aria-label": "Colour notation",
166
+ className: "w-full",
167
+ children: Object.keys(FORMAT_LABELS).map((option) => /* @__PURE__ */ jsx(ToggleGroupItem, { value: option, className: "flex-1", children: FORMAT_LABELS[option] }, option))
168
+ }
169
+ ),
170
+ /* @__PURE__ */ jsxs(
171
+ "div",
172
+ {
173
+ ref: planeRef,
174
+ role: "group",
175
+ "aria-label": `${label} field`,
176
+ onPointerDown: (event) => {
177
+ if (event.button !== 0) return;
178
+ event.preventDefault();
179
+ event.currentTarget.setPointerCapture(event.pointerId);
180
+ trackPointer(event);
181
+ },
182
+ onPointerMove: (event) => {
183
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) trackPointer(event);
184
+ },
185
+ onPointerUp: (event) => {
186
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) {
187
+ event.currentTarget.releasePointerCapture(event.pointerId);
188
+ }
189
+ },
190
+ className: "relative h-40 w-full cursor-crosshair touch-none select-none rounded-(--radius) focus-within:[outline:var(--focus-w)_solid_var(--focus)] focus-within:[outline-offset:var(--focus-off)]",
191
+ children: [
192
+ /* @__PURE__ */ jsx(
193
+ "canvas",
194
+ {
195
+ ref: (node) => {
196
+ canvasRef.current = node;
197
+ if (!node) painted.current = "";
198
+ paint(node);
199
+ },
200
+ width: PLANE_W,
201
+ height: PLANE_H,
202
+ "aria-hidden": "true",
203
+ className: "block size-full rounded-(--radius)"
204
+ }
205
+ ),
206
+ /* @__PURE__ */ jsx(
207
+ "span",
208
+ {
209
+ "aria-hidden": "true",
210
+ className: "pointer-events-none absolute inset-0 rounded-(--radius) shadow-[inset_0_0_0_1px_var(--rule)]"
211
+ }
212
+ ),
213
+ /* @__PURE__ */ jsx(
214
+ "span",
215
+ {
216
+ "aria-hidden": "true",
217
+ className: "pointer-events-none absolute -m-1.5 size-3 rounded-(--radius-pill) border-2 border-(--paper) shadow-[0_0_0_1px_var(--rule-hard)]",
218
+ style: { left: `${saturation * 100}%`, top: `${(1 - color.l) * 100}%`, background: opaque }
219
+ }
220
+ ),
221
+ /* @__PURE__ */ jsx(
222
+ "input",
223
+ {
224
+ type: "range",
225
+ min: 0,
226
+ max: 100,
227
+ step: 1,
228
+ value: Math.round(saturation * 100),
229
+ "aria-label": "Chroma",
230
+ "aria-valuetext": `${Math.round(saturation * 100)} percent chroma`,
231
+ onChange: (event) => setPlane(Number(event.target.value) / 100, color.l),
232
+ className: "sr-only"
233
+ }
234
+ ),
235
+ /* @__PURE__ */ jsx(
236
+ "input",
237
+ {
238
+ type: "range",
239
+ min: 0,
240
+ max: 100,
241
+ step: 1,
242
+ value: Math.round(color.l * 100),
243
+ "aria-label": "Lightness",
244
+ "aria-valuetext": `${Math.round(color.l * 100)} percent lightness`,
245
+ onChange: (event) => setPlane(saturation, Number(event.target.value) / 100),
246
+ className: "sr-only"
247
+ }
248
+ )
249
+ ]
250
+ }
251
+ ),
252
+ /* @__PURE__ */ jsx(
253
+ ColorTrack,
254
+ {
255
+ name: "Hue",
256
+ max: 360,
257
+ step: 1,
258
+ value: color.h,
259
+ announce: `${Math.round(color.h)} degrees`,
260
+ thumb: opaque,
261
+ track: `linear-gradient(to right in oklab, ${hueTrack})`,
262
+ onChange: (h) => commit({ ...color, h, c: saturation * maxChroma(color.l, h, space) })
263
+ }
264
+ ),
265
+ /* @__PURE__ */ jsx(
266
+ ColorTrack,
267
+ {
268
+ name: "Opacity",
269
+ max: 100,
270
+ step: 1,
271
+ value: color.a * 100,
272
+ announce: `${Math.round(color.a * 100)} percent`,
273
+ thumb: formatColor(color, "oklch"),
274
+ track: `linear-gradient(to right, transparent, ${opaque}), ${checkerboard("8px")}`,
275
+ onChange: (percent) => commit({ ...color, a: percent / 100 })
276
+ }
277
+ ),
278
+ /* @__PURE__ */ jsx(
279
+ Input,
280
+ {
281
+ value,
282
+ spellCheck: false,
283
+ autoComplete: "off",
284
+ "aria-label": `${label} as CSS`,
285
+ invalid: typed === null,
286
+ onChange: (event) => {
287
+ const next = event.target.value;
288
+ emitted.current = next;
289
+ if (controlled === void 0) setUncontrolled(next);
290
+ const parsed = parseColor(next);
291
+ if (parsed) {
292
+ setColor(parsed);
293
+ setFormat(colorFormat(next));
294
+ }
295
+ onValueChange?.(next);
296
+ },
297
+ className: "mono-meta"
298
+ }
299
+ )
300
+ ] })
301
+ ] });
302
+ }
303
+ function ColorTrack({ name, max, step, value, announce, track, thumb, onChange }) {
304
+ return /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-3 text-sm text-(--ink-2)", children: [
305
+ /* @__PURE__ */ jsx("span", { className: "w-14 shrink-0", children: name }),
306
+ /* @__PURE__ */ jsx(
307
+ "input",
308
+ {
309
+ type: "range",
310
+ min: 0,
311
+ max,
312
+ step,
313
+ value,
314
+ "aria-valuetext": announce,
315
+ onChange: (event) => onChange(Number(event.target.value)),
316
+ style: { background: track },
317
+ className: cn(
318
+ "h-4 w-full cursor-pointer appearance-none rounded-(--radius-xs) shadow-[inset_0_0_0_1px_var(--rule)]",
319
+ "[&::-webkit-slider-thumb]:h-6 [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-(--radius-xs) [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-(--paper) [&::-webkit-slider-thumb]:bg-(--thumb) [&::-webkit-slider-thumb]:bg-clip-padding [&::-webkit-slider-thumb]:shadow-[0_0_0_1px_var(--rule-hard)]",
320
+ "[&::-moz-range-thumb]:h-6 [&::-moz-range-thumb]:w-4 [&::-moz-range-thumb]:rounded-(--radius-xs) [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-(--paper) [&::-moz-range-thumb]:bg-(--thumb) [&::-moz-range-thumb]:bg-clip-padding [&::-moz-range-thumb]:shadow-[0_0_0_1px_var(--rule-hard)]"
321
+ ),
322
+ ref: (node) => {
323
+ node?.style.setProperty("--thumb", thumb);
324
+ }
325
+ }
326
+ )
327
+ ] });
328
+ }
329
+ var ColorPicker_default = ColorPicker;
330
+ export {
331
+ ColorPicker,
332
+ ColorPicker_default as default
333
+ };
334
+ //# sourceMappingURL=ColorPicker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/ColorPicker/ColorPicker.tsx"],"sourcesContent":["'use client'\n\nimport { useEffect, useId, useRef, useState, type PointerEvent } from 'react'\nimport { cn } from '../../lib/cn'\nimport {\n clamp,\n colorFormat,\n colorToRgb,\n fitGamut,\n formatColor,\n maxChroma,\n parseColor,\n type Color,\n type ColorFormat,\n type ColorSpace,\n} from '../../lib/color'\nimport { CONTROL_BASE, CONTROL_BORDER, isInvalid } from '../../lib/control'\nimport { warnBlankName } from '../../lib/warn'\nimport { useFieldControl } from '../Field/field-control'\nimport { Input } from '../Input/Input'\nimport { Popover, PopoverContent, PopoverTrigger } from '../Popover/Popover'\nimport { ToggleGroup, ToggleGroupItem } from '../ToggleGroup/ToggleGroup'\n\n/** The plane's pixel grid. Wider than it is tall, like the panel holding it. */\nconst PLANE_W = 256\nconst PLANE_H = 160\n\n/** Hue stops in the strip behind the hue slider. Every 5°, plus the wrap. */\nconst HUE_STOPS = 73\n\nconst BLACK: Color = { l: 0, c: 0, h: 0, a: 1 }\n\nconst FORMAT_LABELS: Record<ColorFormat, string> = {\n hex: 'Hex',\n oklch: 'OKLCH',\n // Not \"Display P3\": the three segments share the strip evenly, and the long\n // name wraps to two lines inside its third and makes the whole row twice as\n // tall as the two beside it.\n p3: 'P3',\n}\n\n/**\n * A checkerboard, so alpha reads as alpha rather than as a lighter colour.\n *\n * Sized per surface: 8px checks read as a texture behind a full-width track and\n * as two grey squares inside a 16px swatch.\n */\nconst checkerboard = (square: string) =>\n `repeating-conic-gradient(var(--stone) 0% 25%, var(--paper) 0% 50%) 0 0 / ${square} ${square}`\n\nexport interface ColorPickerProps {\n /**\n * Names the control. Required — the trigger shows a colour, and a colour is\n * not a name.\n *\n * Announced together with the value, the way `Select`'s is: a reader hears\n * \"Brand colour, #a78bfa\". Inside a `Field` with a label, that label is used\n * and this one is not repeated.\n */\n label: string\n /** The colour, as any absolute CSS colour string. */\n value?: string\n defaultValue?: string\n /**\n * Fires with a CSS colour string in whichever notation the panel is set to —\n * so the notation a caller passes in is the notation they get back until\n * somebody changes it in the panel.\n */\n onValueChange?: (value: string) => void\n disabled?: boolean\n /** Paints the resting border with `--danger` and reflects `aria-invalid`. */\n invalid?: boolean\n className?: string\n /** The TRIGGER's id — the element a label points at. A `Field` sets it. */\n id?: string\n /** Ids of the copy describing the control. A `Field` sets it from hint, error and description. */\n 'aria-describedby'?: string\n /** The spelling a form library sets; read together with `invalid`. */\n 'aria-invalid'?: boolean | 'true' | 'false'\n /** Announced on the trigger. A `Field` sets it from `required`. */\n 'aria-required'?: boolean\n}\n\n/**\n * A colour, chosen or typed.\n *\n * The panel works in OKLCH, and that is the reason to reach for this rather\n * than `<input type=\"color\">`. In HSV — which is what the native picker and\n * most libraries use — dragging along a row of constant \"lightness\" walks\n * through colours the eye reads as getting darker, so a reader tuning a palette\n * is fighting the instrument. OKLCH's lightness is the one a person sees, so\n * two colours at the same height on the plane genuinely match, and the hue\n * strip stays at the lightness already chosen instead of showing a rainbow that\n * belongs to some other colour.\n *\n * The plane is normalised to the gamut ROW BY ROW: its right edge is the most\n * chroma that exists at that lightness and hue, so the whole surface is\n * reachable rather than a lens of colour inside a field of clipped duplicates.\n *\n * **What it accepts.** Hex, `rgb()`, `hsl()`, `oklch()` and\n * `color(display-p3 …)`, in both syntaxes. Named colours are not accepted:\n * resolving them needs a table of every CSS name or a live DOM, and a box that\n * takes \"rebeccapurple\" but not \"papayawhip\" is worse than one that takes\n * neither.\n *\n * **Keyboard.** The plane is a group of two real sliders — chroma across,\n * lightness up — so the arrows move it and a screen reader announces where it\n * is. That is the part a 2D canvas usually leaves out, and leaving it out makes\n * the control unusable rather than merely awkward.\n *\n * @example\n * <Field label=\"Brand colour\"><ColorPicker label=\"Brand colour\" defaultValue=\"#a78bfa\" /></Field>\n */\nexport function ColorPicker({\n label,\n value: controlled,\n defaultValue = '#000000',\n onValueChange,\n disabled,\n invalid,\n className,\n id,\n 'aria-describedby': describedBy,\n 'aria-invalid': ariaInvalid,\n 'aria-required': ariaRequired,\n}: ColorPickerProps) {\n warnBlankName('ColorPicker', 'label', label, 'the trigger is announced with no name')\n const bad = isInvalid(invalid, ariaInvalid)\n\n const field = useFieldControl()\n const generated = useId()\n const triggerId = id ?? generated\n const valueId = `${triggerId}-value`\n const nameId = field?.labelId ?? `${triggerId}-name`\n\n const [uncontrolled, setUncontrolled] = useState(defaultValue)\n const value = controlled ?? uncontrolled\n\n const [color, setColor] = useState<Color>(() => parseColor(defaultValue) ?? BLACK)\n const [format, setFormat] = useState<ColorFormat>(() => colorFormat(defaultValue))\n // What this component last wrote out, so its own emission does not read back\n // as an outside change and re-derive the hue it just preserved.\n const emitted = useRef<string | null>(null)\n\n // The plane's horizontal position, kept across the two rows where it cannot\n // be derived: at pure black and pure white every chroma is zero, so `c / max`\n // is 0/0 and the handle would jump to the left edge on the way past.\n const saturationRef = useRef(0)\n\n useEffect(() => {\n if (value === emitted.current) return\n const parsed = parseColor(value)\n if (!parsed) return\n // A grey has no hue — only the angle of its rounding error. Keeping the\n // last real one is what stops the strip spinning when a reader drags the\n // handle through the neutral column.\n setColor((prev) => ({ ...parsed, h: parsed.c < 1e-7 ? prev.h : parsed.h }))\n setFormat(colorFormat(value))\n }, [value])\n\n // Hex is bounded to sRGB; both of the others can address the wider gamut, so\n // the plane shows what the notation can actually hold.\n const space: ColorSpace = format === 'hex' ? 'srgb' : 'p3'\n const ceiling = maxChroma(color.l, color.h, space)\n const saturation = ceiling > 0 ? clamp(color.c / ceiling) : saturationRef.current\n\n useEffect(() => {\n if (ceiling > 0) saturationRef.current = saturation\n }, [ceiling, saturation])\n\n const commit = (next: Color, nextFormat: ColorFormat = format) => {\n const fitted = nextFormat === 'oklch' ? next : fitGamut(next, nextFormat === 'p3' ? 'p3' : 'srgb')\n setColor(fitted)\n setFormat(nextFormat)\n const text = formatColor(fitted, nextFormat)\n emitted.current = text\n if (controlled === undefined) setUncontrolled(text)\n onValueChange?.(text)\n }\n\n /** Moves the plane handle, in the 0–1 coordinates the plane is drawn in. */\n const setPlane = (nextSaturation: number, lightness: number) => {\n saturationRef.current = nextSaturation\n commit({ ...color, l: lightness, c: nextSaturation * maxChroma(lightness, color.h, space) })\n }\n\n const planeRef = useRef<HTMLDivElement>(null)\n const canvasRef = useRef<HTMLCanvasElement>(null)\n const painted = useRef<string>('')\n\n const trackPointer = (event: PointerEvent<HTMLDivElement>) => {\n const rect = planeRef.current?.getBoundingClientRect()\n if (!rect || rect.width === 0 || rect.height === 0) return\n setPlane(\n clamp((event.clientX - rect.left) / rect.width),\n clamp(1 - (event.clientY - rect.top) / rect.height),\n )\n }\n\n /**\n * Fills the plane, at most once per hue.\n *\n * Called from the canvas's own ref callback rather than only from an effect,\n * because the canvas does not exist until the panel is open AND Radix's\n * popper has measured it — which is a commit later than the one that opened\n * it. An effect keyed to the open state runs on the commit BEFORE the node\n * arrives, finds a ref holding null, and never runs again: a plane that\n * stayed blank until the reader happened to move the hue.\n */\n const paint = (canvas: HTMLCanvasElement | null) => {\n if (!canvas) return\n const key = `${color.h.toFixed(3)}:${space}`\n if (painted.current === key) return\n const context = canvas.getContext('2d')\n // A headless environment can hand back a 2D context with most of the\n // drawing surface missing. Nothing here is load-bearing — the plane is the\n // pointer's aim, and the two sliders under it work either way — so a\n // partial context means no plane rather than a thrown render.\n if (!context || typeof context.createImageData !== 'function') return\n painted.current = key\n\n const pixels = context.createImageData(PLANE_W, PLANE_H)\n for (let y = 0; y < PLANE_H; y++) {\n const l = 1 - y / (PLANE_H - 1)\n // Each row is normalised to its OWN reachable chroma. Painted against one\n // global maximum instead, the top and bottom thirds of the plane are\n // bands of clipped duplicates a reader can never pick apart.\n const rowMax = maxChroma(l, color.h, space)\n for (let x = 0; x < PLANE_W; x++) {\n const rgb = colorToRgb({ l, c: (x / (PLANE_W - 1)) * rowMax, h: color.h, a: 1 }, 'srgb')\n const at = (y * PLANE_W + x) * 4\n for (let channel = 0; channel < 3; channel++) {\n pixels.data[at + channel] = Math.round(clamp(rgb[channel] ?? 0) * 255)\n }\n pixels.data[at + 3] = 255\n }\n }\n context.putImageData(pixels, 0, 0)\n }\n\n /** Repaints a canvas that is already on screen when the hue moves under it. */\n useEffect(() => {\n paint(canvasRef.current)\n })\n\n const opaque = formatColor({ ...color, a: 1 }, 'oklch')\n const hueTrack = Array.from({ length: HUE_STOPS }, (_, i) => {\n const h = i * (360 / (HUE_STOPS - 1))\n return formatColor({ l: color.l, c: saturation * maxChroma(color.l, h, space), h, a: 1 }, 'oklch')\n }).join(', ')\n\n const typed = parseColor(value)\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n id={triggerId}\n disabled={disabled}\n // Named by the label AND by its own value, in that order — the same\n // arrangement Select uses, and for the same reason: `aria-label`\n // outranks the content, so naming it \"Brand colour\" would tell a\n // reader the noun and never the answer.\n aria-labelledby={`${nameId} ${valueId}`}\n aria-describedby={describedBy}\n aria-required={ariaRequired}\n aria-invalid={bad || undefined}\n className={cn(\n CONTROL_BASE,\n bad ? CONTROL_BORDER.invalid : CONTROL_BORDER.resting,\n 'flex cursor-pointer items-center gap-2.5 text-start',\n className,\n )}\n >\n {/* Standing outside a Field there is no label to point at, so the\n trigger carries its own — the name half of the two the\n `aria-labelledby` above names, with the value as the other. */}\n {field?.labelId == null && (\n <span id={nameId} className=\"sr-only\">\n {label}\n </span>\n )}\n <span\n aria-hidden=\"true\"\n className=\"size-4 shrink-0 rounded-(--radius-xs) border border-(--rule-2)\"\n // The colour as a flat GRADIENT, not as a colour. In the\n // `background` shorthand a plain colour is only legal in the last\n // layer, so `background: #a78bfa, <checks>` is invalid and the\n // whole declaration is dropped — which drew an empty swatch.\n style={{ background: `linear-gradient(${value}, ${value}), ${checkerboard('4px')}` }}\n />\n <span id={valueId} className=\"mono-meta truncate\">\n {value}\n </span>\n </button>\n </PopoverTrigger>\n\n <PopoverContent label={`${label} picker`} className=\"flex w-72 flex-col gap-3\">\n <ToggleGroup\n type=\"single\"\n value={format}\n // `type=\"single\"` commits an empty string when the pressed segment is\n // the selected one, which here would leave the panel with no notation\n // to write in at all.\n onValueChange={(next) => next && commit(color, next as ColorFormat)}\n aria-label=\"Colour notation\"\n className=\"w-full\"\n >\n {(Object.keys(FORMAT_LABELS) as ColorFormat[]).map((option) => (\n <ToggleGroupItem key={option} value={option} className=\"flex-1\">\n {FORMAT_LABELS[option]}\n </ToggleGroupItem>\n ))}\n </ToggleGroup>\n\n {/* A group of two real sliders rather than a canvas with key handlers:\n the arrows, Home and End are then the platform's, and a reader is\n told which axis they are on and where they are along it. The canvas\n underneath is what a pointer aims at. */}\n <div\n ref={planeRef}\n role=\"group\"\n aria-label={`${label} field`}\n onPointerDown={(event) => {\n if (event.button !== 0) return\n event.preventDefault()\n event.currentTarget.setPointerCapture(event.pointerId)\n trackPointer(event)\n }}\n onPointerMove={(event) => {\n if (event.currentTarget.hasPointerCapture(event.pointerId)) trackPointer(event)\n }}\n onPointerUp={(event) => {\n if (event.currentTarget.hasPointerCapture(event.pointerId)) {\n event.currentTarget.releasePointerCapture(event.pointerId)\n }\n }}\n // The focus ring has to be drawn HERE: the two sliders that take the\n // focus are `sr-only`, so the one the browser draws on them is\n // clipped away with the rest of the box.\n className=\"relative h-40 w-full cursor-crosshair touch-none select-none rounded-(--radius) focus-within:[outline:var(--focus-w)_solid_var(--focus)] focus-within:[outline-offset:var(--focus-off)]\"\n >\n <canvas\n ref={(node) => {\n canvasRef.current = node\n // Closing destroys the canvas and its bitmap with it, so the next\n // open has to repaint even at the same hue.\n if (!node) painted.current = ''\n paint(node)\n }}\n width={PLANE_W}\n height={PLANE_H}\n aria-hidden=\"true\"\n className=\"block size-full rounded-(--radius)\"\n />\n {/* A hairline over the plane, so the light corner still has an edge. */}\n <span\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute inset-0 rounded-(--radius) shadow-[inset_0_0_0_1px_var(--rule)]\"\n />\n <span\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute -m-1.5 size-3 rounded-(--radius-pill) border-2 border-(--paper) shadow-[0_0_0_1px_var(--rule-hard)]\"\n style={{ left: `${saturation * 100}%`, top: `${(1 - color.l) * 100}%`, background: opaque }}\n />\n <input\n type=\"range\"\n min={0}\n max={100}\n step={1}\n value={Math.round(saturation * 100)}\n aria-label=\"Chroma\"\n aria-valuetext={`${Math.round(saturation * 100)} percent chroma`}\n onChange={(event) => setPlane(Number(event.target.value) / 100, color.l)}\n className=\"sr-only\"\n />\n <input\n type=\"range\"\n min={0}\n max={100}\n step={1}\n value={Math.round(color.l * 100)}\n aria-label=\"Lightness\"\n aria-valuetext={`${Math.round(color.l * 100)} percent lightness`}\n onChange={(event) => setPlane(saturation, Number(event.target.value) / 100)}\n className=\"sr-only\"\n />\n </div>\n\n <ColorTrack\n name=\"Hue\"\n max={360}\n step={1}\n value={color.h}\n announce={`${Math.round(color.h)} degrees`}\n thumb={opaque}\n track={`linear-gradient(to right in oklab, ${hueTrack})`}\n onChange={(h) => commit({ ...color, h, c: saturation * maxChroma(color.l, h, space) })}\n />\n <ColorTrack\n name=\"Opacity\"\n max={100}\n step={1}\n value={color.a * 100}\n announce={`${Math.round(color.a * 100)} percent`}\n thumb={formatColor(color, 'oklch')}\n track={`linear-gradient(to right, transparent, ${opaque}), ${checkerboard('8px')}`}\n onChange={(percent) => commit({ ...color, a: percent / 100 })}\n />\n\n <Input\n value={value}\n spellCheck={false}\n autoComplete=\"off\"\n aria-label={`${label} as CSS`}\n invalid={typed === null}\n onChange={(event) => {\n const next = event.target.value\n emitted.current = next\n if (controlled === undefined) setUncontrolled(next)\n const parsed = parseColor(next)\n if (parsed) {\n setColor(parsed)\n setFormat(colorFormat(next))\n }\n onValueChange?.(next)\n }}\n className=\"mono-meta\"\n />\n </PopoverContent>\n </Popover>\n )\n}\n\ninterface ColorTrackProps {\n name: string\n max: number\n step: number\n value: number\n /** What the number MEANS, since a bare \"212\" on a hue slider is not a reading. */\n announce: string\n /** CSS for the strip behind the thumb. */\n track: string\n /** The colour the thumb is filled with. */\n thumb: string\n onChange: (value: number) => void\n}\n\n/**\n * One channel of the picker, as a native range input.\n *\n * Native rather than this system's `Slider`, and deliberately: the whole point\n * of these two is the strip BEHIND the thumb — a hue ramp taken at the current\n * lightness, an opacity ramp over a checkerboard — and Radix's track is not a\n * surface a caller can paint. A native range gives the same keyboard contract\n * for nothing, which is what made the trade worth taking.\n */\nfunction ColorTrack({ name, max, step, value, announce, track, thumb, onChange }: ColorTrackProps) {\n return (\n <label className=\"flex items-center gap-3 text-sm text-(--ink-2)\">\n <span className=\"w-14 shrink-0\">{name}</span>\n <input\n type=\"range\"\n min={0}\n max={max}\n step={step}\n value={value}\n aria-valuetext={announce}\n onChange={(event) => onChange(Number(event.target.value))}\n style={{ background: track }}\n className={cn(\n 'h-4 w-full cursor-pointer appearance-none rounded-(--radius-xs) shadow-[inset_0_0_0_1px_var(--rule)]',\n '[&::-webkit-slider-thumb]:h-6 [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-(--radius-xs) [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-(--paper) [&::-webkit-slider-thumb]:bg-(--thumb) [&::-webkit-slider-thumb]:bg-clip-padding [&::-webkit-slider-thumb]:shadow-[0_0_0_1px_var(--rule-hard)]',\n '[&::-moz-range-thumb]:h-6 [&::-moz-range-thumb]:w-4 [&::-moz-range-thumb]:rounded-(--radius-xs) [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-(--paper) [&::-moz-range-thumb]:bg-(--thumb) [&::-moz-range-thumb]:bg-clip-padding [&::-moz-range-thumb]:shadow-[0_0_0_1px_var(--rule-hard)]',\n )}\n // Read by both thumb rules above; a pseudo-element cannot take a style\n // attribute of its own, so the colour has to arrive as a variable.\n ref={(node) => {\n node?.style.setProperty('--thumb', thumb)\n }}\n />\n </label>\n )\n}\n\nexport default ColorPicker\n"],"mappings":"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","names":[]}
@@ -0,0 +1,77 @@
1
+ import * as react from 'react';
2
+
3
+ interface ComboboxOption {
4
+ value: string;
5
+ label: string;
6
+ /** Extra text matched by the filter but not shown as the label. */
7
+ keywords?: string[];
8
+ disabled?: boolean;
9
+ }
10
+ interface CommonProps {
11
+ options: ComboboxOption[];
12
+ /**
13
+ * Names the control. Required — the trigger's text is a value, not a label.
14
+ *
15
+ * Announced together with the summary rather than instead of it, so a reader
16
+ * hears "Tags, 3 selected". Inside a `Field` with a label, that label names
17
+ * the trigger and this one is not repeated.
18
+ */
19
+ label: string;
20
+ /** Shown on the trigger when nothing is chosen. */
21
+ placeholder?: string;
22
+ /** Placeholder inside the filter field. */
23
+ searchPlaceholder?: string;
24
+ /** Shown when the filter matches nothing. Say what WOULD match. */
25
+ emptyMessage?: string;
26
+ disabled?: boolean;
27
+ className?: string;
28
+ /** The TRIGGER's id — the element a label points at. A `Field` sets it. */
29
+ id?: string;
30
+ /** Ids of the copy describing the control. A `Field` sets it from hint, error and description. */
31
+ 'aria-describedby'?: string;
32
+ /** Paints the resting border with `--danger` and is announced. A `Field` sets it from `error`. */
33
+ 'aria-invalid'?: boolean | 'true' | 'false';
34
+ /** Announced on the trigger. A `Field` sets it from `required`. */
35
+ 'aria-required'?: boolean;
36
+ }
37
+ interface SingleProps extends CommonProps {
38
+ multiple?: false;
39
+ value?: string;
40
+ defaultValue?: string;
41
+ onValueChange?: (value: string) => void;
42
+ }
43
+ interface MultipleProps extends CommonProps {
44
+ /**
45
+ * Several at once. The trigger then summarises the choice rather than naming
46
+ * it — "3 selected" past a threshold, because a trigger that grows with its
47
+ * value reflows the form every time someone picks another one.
48
+ */
49
+ multiple: true;
50
+ value?: string[];
51
+ defaultValue?: string[];
52
+ onValueChange?: (value: string[]) => void;
53
+ }
54
+ type ComboboxProps = SingleProps | MultipleProps;
55
+ /**
56
+ * A select you can type into, choosing one or several.
57
+ *
58
+ * The line against `Select` is length, and it is not a matter of taste: a
59
+ * styled select is better up to roughly a dozen options, because a list nobody
60
+ * can filter is faster to scan than one they have to think about. Past that,
61
+ * this is the right answer.
62
+ *
63
+ * Filtering, the highlighted row and the arrow keys come from cmdk, which
64
+ * implements the ARIA combobox pattern properly: the highlight moves through
65
+ * `aria-activedescendant` while focus stays in the input. Hand-rolled comboboxes
66
+ * move focus into the list instead, and the typed text stops being editable.
67
+ *
68
+ * Controlled or uncontrolled, like every other form control here.
69
+ *
70
+ * @example
71
+ * <Combobox label="Framework" options={FRAMEWORKS} placeholder="Pick one" />
72
+ * @example
73
+ * <Combobox multiple label="Tags" options={TAGS} />
74
+ */
75
+ declare function Combobox(props: ComboboxProps): react.JSX.Element;
76
+
77
+ export { Combobox, type ComboboxOption, type ComboboxProps, Combobox as default };
@@ -0,0 +1,170 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { RiCheckLine, RiCloseLine, RiExpandUpDownLine } from "@remixicon/react";
4
+ import { useId, useState } from "react";
5
+ import { cn } from "../../lib/cn.js";
6
+ import { CONTROL_BORDER, isInvalid } from "../../lib/control.js";
7
+ import { useFieldControl } from "../Field/field-control.js";
8
+ import { warnBlankName } from "../../lib/warn.js";
9
+ import {
10
+ Command,
11
+ CommandEmpty,
12
+ CommandGroup,
13
+ CommandInput,
14
+ CommandItem,
15
+ CommandList
16
+ } from "../Command/Command.js";
17
+ import { Popover, PopoverContent, PopoverTrigger } from "../Popover/Popover.js";
18
+ const SUMMARISE_AFTER = 2;
19
+ function Combobox(props) {
20
+ const {
21
+ options,
22
+ label,
23
+ placeholder = "Select\u2026",
24
+ searchPlaceholder = "Search\u2026",
25
+ emptyMessage = "Nothing matches.",
26
+ disabled = false,
27
+ className,
28
+ id,
29
+ "aria-describedby": describedBy,
30
+ "aria-invalid": ariaInvalid,
31
+ "aria-required": ariaRequired
32
+ } = props;
33
+ const multiple = props.multiple === true;
34
+ warnBlankName("Combobox", "label", label, "the trigger is announced with no name");
35
+ const field = useFieldControl();
36
+ const generated = useId();
37
+ const triggerId = id ?? generated;
38
+ const summaryId = `${triggerId}-value`;
39
+ const nameId = field?.labelId ?? `${triggerId}-name`;
40
+ const bad = isInvalid(void 0, ariaInvalid);
41
+ const [open, setOpen] = useState(false);
42
+ const [uncontrolled, setUncontrolled] = useState(() => {
43
+ if (props.multiple === true) return props.defaultValue ?? [];
44
+ return props.defaultValue ? [props.defaultValue] : [];
45
+ });
46
+ const controlled = props.multiple === true ? props.value : props.value === void 0 ? void 0 : props.value === "" ? [] : [props.value];
47
+ const current = controlled ?? uncontrolled;
48
+ const commit = (next) => {
49
+ if (controlled === void 0) setUncontrolled(next);
50
+ if (props.multiple === true) props.onValueChange?.(next);
51
+ else props.onValueChange?.(next[0] ?? "");
52
+ };
53
+ const choose = (option) => {
54
+ if (multiple) {
55
+ commit(current.includes(option) ? current.filter((v) => v !== option) : [...current, option]);
56
+ return;
57
+ }
58
+ commit(current.includes(option) ? [] : [option]);
59
+ setOpen(false);
60
+ };
61
+ const chosen = options.filter((option) => current.includes(option.value));
62
+ const summary = chosen.length === 0 ? placeholder : chosen.length <= SUMMARISE_AFTER ? chosen.map((option) => option.label).join(", ") : `${chosen.length} selected`;
63
+ return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
64
+ /* @__PURE__ */ jsxs(
65
+ PopoverTrigger,
66
+ {
67
+ role: "combobox",
68
+ id: triggerId,
69
+ "aria-expanded": open,
70
+ "aria-labelledby": `${nameId} ${summaryId}`,
71
+ "aria-describedby": describedBy,
72
+ "aria-required": ariaRequired,
73
+ "aria-invalid": bad || void 0,
74
+ disabled,
75
+ className: cn(
76
+ "group flex w-full items-center justify-between gap-2 rounded-(--radius) border bg-(--paper) px-(--field-px) py-(--field-py) text-start text-sm transition-colors duration-(--duration-fast) hover:border-(--rule-hard) disabled:opacity-(--disabled-opacity) disabled:pointer-events-none",
77
+ bad ? CONTROL_BORDER.invalid : CONTROL_BORDER.resting,
78
+ chosen.length > 0 ? "text-(--ink)" : "text-(--ink-3-aa)",
79
+ className
80
+ ),
81
+ children: [
82
+ field?.labelId == null && /* @__PURE__ */ jsx("span", { id: nameId, className: "sr-only", children: label }),
83
+ /* @__PURE__ */ jsx("span", { id: summaryId, className: "truncate", children: summary }),
84
+ /* @__PURE__ */ jsxs("span", { className: "flex shrink-0 items-center gap-1", children: [
85
+ multiple && chosen.length > 0 && // A `<span role="button">` rather than a nested `<button>`, which
86
+ // is invalid inside the trigger and which browsers reparent — the
87
+ // clear control then falls outside the field entirely.
88
+ /* @__PURE__ */ jsx(
89
+ "span",
90
+ {
91
+ role: "button",
92
+ tabIndex: 0,
93
+ "aria-label": `Clear ${label}`,
94
+ onClick: (event) => {
95
+ event.preventDefault();
96
+ event.stopPropagation();
97
+ commit([]);
98
+ },
99
+ onKeyDown: (event) => {
100
+ if (event.key !== "Enter" && event.key !== " ") return;
101
+ event.preventDefault();
102
+ event.stopPropagation();
103
+ commit([]);
104
+ },
105
+ className: "grid size-5 cursor-pointer place-items-center rounded-(--radius-pill) text-(--ink-3-aa) transition-colors duration-(--duration-fast) hover:bg-(--stone) hover:text-(--ink)",
106
+ children: /* @__PURE__ */ jsx(RiCloseLine, { size: 12, "aria-hidden": true })
107
+ }
108
+ ),
109
+ /* @__PURE__ */ jsx(RiExpandUpDownLine, { size: 14, "aria-hidden": true, className: "text-(--ink-3-aa)" })
110
+ ] })
111
+ ]
112
+ }
113
+ ),
114
+ /* @__PURE__ */ jsx(
115
+ PopoverContent,
116
+ {
117
+ label,
118
+ className: "w-(--radix-popover-trigger-width) overflow-hidden p-0",
119
+ align: "start",
120
+ children: /* @__PURE__ */ jsxs(Command, { label: `${label}: ${searchPlaceholder}`, className: "rounded-none border-0", children: [
121
+ /* @__PURE__ */ jsx(CommandInput, { placeholder: searchPlaceholder }),
122
+ /* @__PURE__ */ jsxs(CommandList, { children: [
123
+ /* @__PURE__ */ jsx(CommandEmpty, { children: emptyMessage }),
124
+ /* @__PURE__ */ jsx(CommandGroup, { children: options.map((option) => {
125
+ const selected = current.includes(option.value);
126
+ return /* @__PURE__ */ jsxs(
127
+ CommandItem,
128
+ {
129
+ value: option.value,
130
+ keywords: [option.label, ...option.keywords ?? []],
131
+ disabled: option.disabled,
132
+ onSelect: choose,
133
+ "aria-selected": selected,
134
+ children: [
135
+ /* @__PURE__ */ jsx(
136
+ "span",
137
+ {
138
+ className: cn(
139
+ "grid size-4 shrink-0 place-items-center transition-opacity duration-(--duration-fast)",
140
+ multiple && "rounded-(--radius-sm) border border-(--rule-2) text-(--accent-foreground) data-[on=true]:border-(--accent) data-[on=true]:bg-(--accent)"
141
+ ),
142
+ "data-on": selected,
143
+ children: /* @__PURE__ */ jsx(
144
+ RiCheckLine,
145
+ {
146
+ size: 12,
147
+ "aria-hidden": true,
148
+ className: selected ? "opacity-100" : "opacity-0"
149
+ }
150
+ )
151
+ }
152
+ ),
153
+ option.label
154
+ ]
155
+ },
156
+ option.value
157
+ );
158
+ }) })
159
+ ] })
160
+ ] })
161
+ }
162
+ )
163
+ ] });
164
+ }
165
+ var Combobox_default = Combobox;
166
+ export {
167
+ Combobox,
168
+ Combobox_default as default
169
+ };
170
+ //# sourceMappingURL=Combobox.js.map