@justanarthur/modo-ui 0.6.0-canary.9 → 0.6.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 (490) hide show
  1. package/README.md +66 -164
  2. package/dist/base.css +4 -141
  3. package/dist/blocks/sidebar-app/index.js +26 -40
  4. package/dist/components/AI/input-message/index.js +16 -23
  5. package/dist/components/AI/thinking-indicator/index.js +6 -9
  6. package/dist/components/accordion/index.d.ts +11 -39
  7. package/dist/components/accordion/index.js +13 -18
  8. package/dist/components/badge/index.d.ts +11 -18
  9. package/dist/components/badge/index.js +7 -12
  10. package/dist/components/button/index.d.ts +17 -53
  11. package/dist/components/button/index.js +6 -11
  12. package/dist/components/card/index.d.ts +9 -32
  13. package/dist/components/card/index.js +11 -17
  14. package/dist/components/copy/index.d.ts +8 -41
  15. package/dist/components/copy/index.js +9 -15
  16. package/dist/components/inputs/checkbox/index.d.ts +9 -28
  17. package/dist/components/inputs/checkbox/index.js +13 -18
  18. package/dist/components/inputs/color-picker/index.d.ts +14 -22
  19. package/dist/components/inputs/color-picker/index.js +18 -26
  20. package/dist/components/inputs/combobox/index.d.ts +20 -85
  21. package/dist/components/inputs/combobox/index.js +18 -27
  22. package/dist/components/inputs/date-time/calendar/index.d.ts +18 -24
  23. package/dist/components/inputs/date-time/calendar/index.js +11 -15
  24. package/dist/components/inputs/date-time/date-picker/index.d.ts +16 -105
  25. package/dist/components/inputs/date-time/date-picker/index.js +25 -35
  26. package/dist/components/inputs/date-time/date-range-picker/index.d.ts +12 -106
  27. package/dist/components/inputs/date-time/date-range-picker/index.js +24 -35
  28. package/dist/components/inputs/date-time/duration-picker/index.d.ts +13 -66
  29. package/dist/components/inputs/date-time/duration-picker/index.js +24 -33
  30. package/dist/components/inputs/date-time/month-picker/index.d.ts +10 -60
  31. package/dist/components/inputs/date-time/month-picker/index.js +23 -32
  32. package/dist/components/inputs/date-time/time-picker/index.d.ts +10 -58
  33. package/dist/components/inputs/date-time/time-picker/index.js +24 -33
  34. package/dist/components/inputs/input/index.d.ts +13 -95
  35. package/dist/components/inputs/input/index.js +9 -18
  36. package/dist/components/inputs/radio/index.d.ts +5 -20
  37. package/dist/components/inputs/radio/index.js +12 -16
  38. package/dist/components/inputs/search/index.d.ts +3 -3
  39. package/dist/components/inputs/search/index.js +18 -26
  40. package/dist/components/inputs/select/index.d.ts +23 -99
  41. package/dist/components/inputs/select/index.js +14 -29
  42. package/dist/components/inputs/slider/index.d.ts +0 -9
  43. package/dist/components/inputs/slider/index.js +5 -8
  44. package/dist/components/inputs/switch/index.d.ts +2 -8
  45. package/dist/components/inputs/switch/index.js +12 -15
  46. package/dist/components/inputs/textarea/index.d.ts +1 -20
  47. package/dist/components/inputs/textarea/index.js +6 -9
  48. package/dist/components/navigation/sidebar/index.d.ts +21 -44
  49. package/dist/components/navigation/sidebar/index.js +31 -42
  50. package/dist/components/navigation/tabs/index.d.ts +2 -8
  51. package/dist/components/navigation/tabs/index.js +10 -15
  52. package/dist/components/navigation/tabs-subtle/index.js +10 -13
  53. package/dist/components/overlays/alert-dialog/index.d.ts +16 -135
  54. package/dist/components/overlays/alert-dialog/index.js +13 -23
  55. package/dist/components/overlays/command/index.d.ts +20 -25
  56. package/dist/components/overlays/command/index.js +17 -25
  57. package/dist/components/overlays/context-menu/index.d.ts +17 -39
  58. package/dist/components/overlays/context-menu/index.js +18 -29
  59. package/dist/components/overlays/dialog/index.d.ts +16 -42
  60. package/dist/components/overlays/dialog/index.js +12 -20
  61. package/dist/components/overlays/dropdown/index.d.ts +34 -304
  62. package/dist/components/overlays/dropdown/index.js +17 -44
  63. package/dist/components/overlays/popover/index.d.ts +12 -38
  64. package/dist/components/overlays/popover/index.js +9 -13
  65. package/dist/components/overlays/preview-card/index.js +8 -12
  66. package/dist/components/overlays/sheet/index.d.ts +8 -25
  67. package/dist/components/overlays/sheet/index.js +13 -19
  68. package/dist/components/overlays/toast/index.d.ts +50 -209
  69. package/dist/components/overlays/toast/index.js +10 -23
  70. package/dist/components/overlays/tooltip/index.d.ts +21 -123
  71. package/dist/components/overlays/tooltip/index.js +6 -16
  72. package/dist/components/table/index.d.ts +8 -63
  73. package/dist/components/table/index.js +9 -20
  74. package/dist/index.d.ts +1048 -2999
  75. package/dist/index.js +82 -231
  76. package/dist/preset.d.ts +0 -22
  77. package/dist/preset.js +2 -24
  78. package/dist/primitives/code/index.js +5 -8
  79. package/dist/primitives/craft/index.js +6 -10
  80. package/dist/primitives/fluid-hover/index.js +8 -10
  81. package/dist/primitives/morph/index.js +10 -16
  82. package/dist/primitives/motion/index.js +8 -11
  83. package/dist/primitives/scroll-area/index.js +3 -5
  84. package/dist/primitives/sizes/index.js +4 -6
  85. package/dist/primitives/surface/index.js +4 -6
  86. package/dist/shared/{components-c7rwx0ap.js → components-0q0sqfh4.js} +49 -27
  87. package/dist/shared/components-0sn7ypjw.js +61 -0
  88. package/dist/shared/{components-vc864c51.js → components-11da9ajg.js} +5 -8
  89. package/dist/shared/components-1434p1mh.js +123 -0
  90. package/dist/shared/{components-c74gf16d.js → components-1z6tdadk.js} +24 -40
  91. package/dist/shared/{components-bae8sbjx.js → components-27nnvrwe.js} +14 -15
  92. package/dist/shared/{components-qh1cvj6p.js → components-2dxkv6zm.js} +7 -8
  93. package/dist/shared/{components-y0kfy2g4.js → components-2ecg8svx.js} +152 -234
  94. package/dist/shared/{components-c5w5zk0s.js → components-2ph6t61s.js} +34 -37
  95. package/dist/shared/{components-4j3t39dd.js → components-39jq9va1.js} +24 -29
  96. package/dist/shared/components-3ky44908.js +9 -0
  97. package/dist/shared/{components-syynwm1r.js → components-3mkepka5.js} +25 -28
  98. package/dist/shared/{components-wgtjt2af.js → components-4700p9kz.js} +22 -24
  99. package/dist/shared/{components-yhgq0qxx.js → components-59efxjd9.js} +12 -13
  100. package/dist/shared/{components-91zw3d9f.js → components-5vgpnxgy.js} +27 -51
  101. package/dist/shared/{components-kz5q11pp.js → components-5xc2j8jz.js} +18 -19
  102. package/dist/shared/{components-zkk3fmw8.js → components-6p8tfcd6.js} +29 -57
  103. package/dist/shared/{components-4nck8acq.js → components-7r3etddy.js} +54 -19
  104. package/dist/shared/{components-svkt577j.js → components-8snj490h.js} +32 -78
  105. package/dist/shared/{components-ctqmge8t.js → components-b0eac83j.js} +30 -9
  106. package/dist/shared/{components-9sttq78t.js → components-b2wyq2cq.js} +59 -41
  107. package/dist/shared/{components-pv1dyrtn.js → components-b3c5et5b.js} +38 -49
  108. package/dist/shared/{components-rrbpmfx4.js → components-c6s6b43g.js} +2 -3
  109. package/dist/shared/{components-pwayrkw9.js → components-chacnk61.js} +19 -19
  110. package/dist/shared/{components-71nbt03m.js → components-cv16mryv.js} +7 -26
  111. package/dist/shared/{components-hgv6tygd.js → components-d6xtjht3.js} +30 -35
  112. package/dist/shared/{components-z8fedth5.js → components-dk0gjr5a.js} +18 -19
  113. package/dist/shared/{components-cet3jpa0.js → components-fzrq6tr3.js} +34 -37
  114. package/dist/shared/{components-cnead5qm.js → components-gc5vtq2f.js} +30 -66
  115. package/dist/shared/components-gf5s6ap9.js +42 -0
  116. package/dist/shared/{components-0h78g3b5.js → components-gz8zf3ec.js} +55 -56
  117. package/dist/shared/{components-mattt5jp.js → components-h2ajmhwn.js} +35 -107
  118. package/dist/shared/{components-m06yq8wk.js → components-hqeq3w9q.js} +23 -38
  119. package/dist/shared/{components-7qx17na9.js → components-hw9yk32z.js} +24 -36
  120. package/dist/shared/components-hyq23phv.js +310 -0
  121. package/dist/shared/{components-9jmjnkqd.js → components-jbbmdr74.js} +22 -41
  122. package/dist/shared/{components-dcc02hs2.js → components-jjw69c4c.js} +4 -7
  123. package/dist/shared/{components-303jf96s.js → components-jthe1pwb.js} +420 -139
  124. package/dist/shared/{components-dtca4s6b.js → components-jwst3vtj.js} +76 -224
  125. package/dist/shared/{components-dc5q749x.js → components-kvr7f9hy.js} +10 -19
  126. package/dist/shared/{components-s8yqk2dc.js → components-kwa6mrf9.js} +11 -46
  127. package/dist/shared/{components-de99ykt7.js → components-men63rwj.js} +2 -3
  128. package/dist/shared/{components-qx27excf.js → components-mjthm1kf.js} +9 -13
  129. package/dist/shared/{components-1e64t8aj.js → components-n42c79bz.js} +22 -23
  130. package/dist/shared/{components-tz1fv35x.js → components-n7mjr357.js} +2 -3
  131. package/dist/shared/components-ng72k97e.js +128 -0
  132. package/dist/shared/{components-w2jqa6hx.js → components-pbmqctfa.js} +65 -169
  133. package/dist/shared/{components-px2zm7dc.js → components-q5xj5c16.js} +8 -9
  134. package/dist/shared/components-raknhppb.js +64 -0
  135. package/dist/shared/{components-2a7hcand.js → components-rr7a6ja9.js} +27 -59
  136. package/dist/shared/{components-005jt2nz.js → components-s083f38z.js} +34 -164
  137. package/dist/shared/components-s5ngg32c.js +267 -0
  138. package/dist/shared/components-sn1rxx1d.js +143 -0
  139. package/dist/shared/{components-r8eqs7gz.js → components-ta7shw28.js} +4 -4
  140. package/dist/shared/{components-9as3rf68.js → components-vc7vq5nz.js} +3 -6
  141. package/dist/shared/{components-ft3zdc79.js → components-vfbhxf5r.js} +5 -6
  142. package/dist/shared/components-vynjtrk6.js +19 -0
  143. package/dist/shared/{components-p0fm0bab.js → components-w1r67ag9.js} +149 -294
  144. package/dist/shared/{components-xd3emmy4.js → components-w90endve.js} +72 -71
  145. package/dist/shared/{components-t5pk5nmc.js → components-wvp1m52g.js} +102 -149
  146. package/dist/shared/components-wy9jpqny.js +146 -0
  147. package/dist/shared/{components-yxmxn2jp.js → components-y9y0g8er.js} +28 -52
  148. package/dist/shared/components-ypp01w3p.js +986 -0
  149. package/dist/shared/{components-czdvj1a8.js → components-zfnmn5d4.js} +36 -82
  150. package/dist/shared/{components-jqpefjsn.js → components-zrkzcayz.js} +6 -54
  151. package/dist/shared/{components-vf45j12c.js → components-zx6b6yby.js} +18 -4
  152. package/dist/styles.css +32 -320
  153. package/dist/tailwind.css +0 -16
  154. package/dist/tokens.css +0 -67
  155. package/package.json +4 -227
  156. package/DESIGN.md +0 -623
  157. package/blocks/sidebar-app/examples.mdx +0 -33
  158. package/blocks/sidebar-app/index.tsx +0 -218
  159. package/blocks/sidebar-app/inset-topbar.tsx +0 -25
  160. package/blocks/sidebar-app/menu-grid.ts +0 -26
  161. package/blocks/sidebar-app/nav-data.ts +0 -36
  162. package/blocks/sidebar-app/search-field.tsx +0 -48
  163. package/blocks/sidebar-app/user-footer.tsx +0 -67
  164. package/blocks/sidebar-app/workspace-header.tsx +0 -129
  165. package/components/AI/input-message/examples.mdx +0 -130
  166. package/components/AI/input-message/file-thumbnail.tsx +0 -173
  167. package/components/AI/input-message/index.tsx +0 -1385
  168. package/components/AI/thinking-indicator/examples.mdx +0 -27
  169. package/components/AI/thinking-indicator/index.tsx +0 -210
  170. package/components/accordion/examples.mdx +0 -233
  171. package/components/accordion/index.tsx +0 -1071
  172. package/components/avatar/examples.mdx +0 -95
  173. package/components/avatar/index.tsx +0 -129
  174. package/components/badge/change-demo.tsx +0 -29
  175. package/components/badge/examples.mdx +0 -124
  176. package/components/badge/index.tsx +0 -225
  177. package/components/badge/variants.ts +0 -73
  178. package/components/button/examples.mdx +0 -168
  179. package/components/button/index.tsx +0 -424
  180. package/components/button/loading-demo.tsx +0 -25
  181. package/components/button/variants.ts +0 -168
  182. package/components/card/examples.mdx +0 -251
  183. package/components/card/index.tsx +0 -1070
  184. package/components/copy/examples.mdx +0 -125
  185. package/components/copy/index.tsx +0 -662
  186. package/components/inputs/checkbox/examples.mdx +0 -87
  187. package/components/inputs/checkbox/index.tsx +0 -611
  188. package/components/inputs/checkbox/select-all-demo.tsx +0 -48
  189. package/components/inputs/color-picker/examples.mdx +0 -111
  190. package/components/inputs/color-picker/index.tsx +0 -1981
  191. package/components/inputs/color-picker/variants.ts +0 -401
  192. package/components/inputs/combobox/examples.mdx +0 -366
  193. package/components/inputs/combobox/index.tsx +0 -1527
  194. package/components/inputs/combobox/remote-demo.tsx +0 -105
  195. package/components/inputs/date-time/calendar/examples.mdx +0 -45
  196. package/components/inputs/date-time/calendar/index.tsx +0 -370
  197. package/components/inputs/date-time/calendar/use-month-window.ts +0 -173
  198. package/components/inputs/date-time/date-picker/examples.mdx +0 -243
  199. package/components/inputs/date-time/date-picker/index.tsx +0 -476
  200. package/components/inputs/date-time/date-picker/masked-date-input.tsx +0 -348
  201. package/components/inputs/date-time/date-range-picker/examples.mdx +0 -93
  202. package/components/inputs/date-time/date-range-picker/index.tsx +0 -525
  203. package/components/inputs/date-time/duration-picker/examples.mdx +0 -40
  204. package/components/inputs/date-time/duration-picker/index.tsx +0 -253
  205. package/components/inputs/date-time/month-picker/examples.mdx +0 -45
  206. package/components/inputs/date-time/month-picker/index.tsx +0 -254
  207. package/components/inputs/date-time/picker-shell.tsx +0 -581
  208. package/components/inputs/date-time/time-columns.tsx +0 -94
  209. package/components/inputs/date-time/time-picker/examples.mdx +0 -57
  210. package/components/inputs/date-time/time-picker/index.tsx +0 -227
  211. package/components/inputs/field/examples.mdx +0 -117
  212. package/components/inputs/field/index.tsx +0 -268
  213. package/components/inputs/file-dropzone/examples.mdx +0 -83
  214. package/components/inputs/file-dropzone/file-row.tsx +0 -201
  215. package/components/inputs/file-dropzone/index.tsx +0 -316
  216. package/components/inputs/file-dropzone/upload-demo.tsx +0 -67
  217. package/components/inputs/file-dropzone/variants.ts +0 -94
  218. package/components/inputs/input/examples.mdx +0 -166
  219. package/components/inputs/input/index.tsx +0 -494
  220. package/components/inputs/input/password-demo.tsx +0 -30
  221. package/components/inputs/label/examples.mdx +0 -67
  222. package/components/inputs/label/index.tsx +0 -85
  223. package/components/inputs/number-input/examples.mdx +0 -69
  224. package/components/inputs/number-input/index.tsx +0 -221
  225. package/components/inputs/radio/examples.mdx +0 -77
  226. package/components/inputs/radio/index.tsx +0 -636
  227. package/components/inputs/search/examples.mdx +0 -74
  228. package/components/inputs/search/index.tsx +0 -333
  229. package/components/inputs/select/examples.mdx +0 -296
  230. package/components/inputs/select/index.tsx +0 -1044
  231. package/components/inputs/select/variants.ts +0 -32
  232. package/components/inputs/slider/examples.mdx +0 -153
  233. package/components/inputs/slider/index.tsx +0 -1867
  234. package/components/inputs/switch/examples.mdx +0 -96
  235. package/components/inputs/switch/index.tsx +0 -504
  236. package/components/inputs/textarea/examples.mdx +0 -126
  237. package/components/inputs/textarea/index.tsx +0 -207
  238. package/components/navigation/navigation-menu/examples.mdx +0 -136
  239. package/components/navigation/navigation-menu/index.tsx +0 -704
  240. package/components/navigation/pagination/examples.mdx +0 -74
  241. package/components/navigation/pagination/index.tsx +0 -401
  242. package/components/navigation/sidebar/examples.mdx +0 -325
  243. package/components/navigation/sidebar/index.tsx +0 -453
  244. package/components/navigation/sidebar/sidebar-core.tsx +0 -1408
  245. package/components/navigation/sidebar/sidebar-menu.tsx +0 -1495
  246. package/components/navigation/sidebar/sidebar.css +0 -74
  247. package/components/navigation/sidebar/variants.ts +0 -55
  248. package/components/navigation/tabs/examples.mdx +0 -89
  249. package/components/navigation/tabs/index.tsx +0 -494
  250. package/components/navigation/tabs-subtle/examples.mdx +0 -49
  251. package/components/navigation/tabs-subtle/index.tsx +0 -512
  252. package/components/overlays/alert-dialog/examples.mdx +0 -123
  253. package/components/overlays/alert-dialog/index.tsx +0 -276
  254. package/components/overlays/alert-dialog/pending-demo.tsx +0 -36
  255. package/components/overlays/command/examples.mdx +0 -113
  256. package/components/overlays/command/index.tsx +0 -425
  257. package/components/overlays/context-menu/examples.mdx +0 -56
  258. package/components/overlays/context-menu/index.tsx +0 -279
  259. package/components/overlays/dialog/examples.mdx +0 -165
  260. package/components/overlays/dialog/index.tsx +0 -451
  261. package/components/overlays/dropdown/dropdown-search.tsx +0 -395
  262. package/components/overlays/dropdown/examples.mdx +0 -444
  263. package/components/overlays/dropdown/index.tsx +0 -1241
  264. package/components/overlays/dropdown/menu-item.tsx +0 -510
  265. package/components/overlays/dropdown/menu-parts.tsx +0 -725
  266. package/components/overlays/popover/anchored-demo.tsx +0 -59
  267. package/components/overlays/popover/examples.mdx +0 -203
  268. package/components/overlays/popover/index.tsx +0 -317
  269. package/components/overlays/preview-card/examples.mdx +0 -61
  270. package/components/overlays/preview-card/index.tsx +0 -204
  271. package/components/overlays/sheet/examples.mdx +0 -101
  272. package/components/overlays/sheet/index.tsx +0 -307
  273. package/components/overlays/toast/examples.mdx +0 -85
  274. package/components/overlays/toast/global-demo.tsx +0 -72
  275. package/components/overlays/toast/index.tsx +0 -544
  276. package/components/overlays/toast/manager.ts +0 -193
  277. package/components/overlays/tooltip/examples.mdx +0 -106
  278. package/components/overlays/tooltip/index.tsx +0 -539
  279. package/components/progress/examples.mdx +0 -59
  280. package/components/progress/index.tsx +0 -174
  281. package/components/progress/progress.css +0 -49
  282. package/components/progress/step-demo.tsx +0 -27
  283. package/components/table/examples.mdx +0 -188
  284. package/components/table/index.tsx +0 -535
  285. package/components/table/sort-demo.tsx +0 -61
  286. package/dist/components/avatar/index.d.ts +0 -46
  287. package/dist/components/avatar/index.js +0 -19
  288. package/dist/components/badge/variants.d.ts +0 -33
  289. package/dist/components/badge/variants.js +0 -10
  290. package/dist/components/button/variants.d.ts +0 -39
  291. package/dist/components/button/variants.js +0 -8
  292. package/dist/components/inputs/color-picker/variants.d.ts +0 -61
  293. package/dist/components/inputs/color-picker/variants.js +0 -28
  294. package/dist/components/inputs/field/index.d.ts +0 -116
  295. package/dist/components/inputs/field/index.js +0 -29
  296. package/dist/components/inputs/file-dropzone/index.d.ts +0 -167
  297. package/dist/components/inputs/file-dropzone/index.js +0 -33
  298. package/dist/components/inputs/file-dropzone/variants.d.ts +0 -35
  299. package/dist/components/inputs/file-dropzone/variants.js +0 -12
  300. package/dist/components/inputs/label/index.d.ts +0 -32
  301. package/dist/components/inputs/label/index.js +0 -11
  302. package/dist/components/inputs/number-input/index.d.ts +0 -87
  303. package/dist/components/inputs/number-input/index.js +0 -21
  304. package/dist/components/inputs/select/variants.d.ts +0 -5
  305. package/dist/components/inputs/select/variants.js +0 -6
  306. package/dist/components/navigation/navigation-menu/index.d.ts +0 -126
  307. package/dist/components/navigation/navigation-menu/index.js +0 -41
  308. package/dist/components/navigation/pagination/index.d.ts +0 -75
  309. package/dist/components/navigation/pagination/index.js +0 -41
  310. package/dist/components/navigation/sidebar/variants.d.ts +0 -25
  311. package/dist/components/navigation/sidebar/variants.js +0 -28
  312. package/dist/components/progress/index.d.ts +0 -56
  313. package/dist/components/progress/index.js +0 -15
  314. package/dist/docs/index.d.ts +0 -83
  315. package/dist/docs/index.js +0 -288
  316. package/dist/docs/vite.d.ts +0 -31
  317. package/dist/docs/vite.js +0 -45
  318. package/dist/lib/date-locale.d.ts +0 -26
  319. package/dist/lib/date-locale.js +0 -24
  320. package/dist/lib/field-classes.d.ts +0 -149
  321. package/dist/lib/field-classes.js +0 -41
  322. package/dist/lib/field-variants.d.ts +0 -47
  323. package/dist/lib/field-variants.js +0 -26
  324. package/dist/lib/fluid-hover-highlight.d.ts +0 -112
  325. package/dist/lib/fluid-hover-highlight.js +0 -17
  326. package/dist/lib/focus-ring.d.ts +0 -18
  327. package/dist/lib/focus-ring.js +0 -11
  328. package/dist/lib/goo-indicator.d.ts +0 -46
  329. package/dist/lib/goo-indicator.js +0 -14
  330. package/dist/lib/icon-context.d.ts +0 -30
  331. package/dist/lib/icon-context.js +0 -12
  332. package/dist/lib/inert.d.ts +0 -9
  333. package/dist/lib/inert.js +0 -8
  334. package/dist/lib/morph-layers.d.ts +0 -93
  335. package/dist/lib/morph-layers.js +0 -13
  336. package/dist/lib/morph-part.d.ts +0 -21
  337. package/dist/lib/morph-part.js +0 -12
  338. package/dist/lib/popup.d.ts +0 -20
  339. package/dist/lib/popup.js +0 -12
  340. package/dist/lib/reduced-motion.d.ts +0 -9
  341. package/dist/lib/reduced-motion.js +0 -6
  342. package/dist/lib/row-index.d.ts +0 -10
  343. package/dist/lib/row-index.js +0 -8
  344. package/dist/lib/row-order.d.ts +0 -28
  345. package/dist/lib/row-order.js +0 -10
  346. package/dist/lib/scroll-column.d.ts +0 -31
  347. package/dist/lib/scroll-column.js +0 -19
  348. package/dist/lib/shape-context.d.ts +0 -30
  349. package/dist/lib/shape-context.js +0 -13
  350. package/dist/lib/shape-radius.d.ts +0 -19
  351. package/dist/lib/shape-radius.js +0 -6
  352. package/dist/lib/size-context.d.ts +0 -94
  353. package/dist/lib/size-context.js +0 -20
  354. package/dist/lib/size-scale.d.ts +0 -79
  355. package/dist/lib/size-scale.js +0 -8
  356. package/dist/lib/slot.d.ts +0 -39
  357. package/dist/lib/slot.js +0 -14
  358. package/dist/lib/springs.d.ts +0 -44
  359. package/dist/lib/springs.js +0 -8
  360. package/dist/lib/surface-classes.d.ts +0 -19
  361. package/dist/lib/surface-classes.js +0 -10
  362. package/dist/lib/surface-context.d.ts +0 -9
  363. package/dist/lib/surface-context.js +0 -8
  364. package/dist/lib/use-controllable-state.d.ts +0 -2
  365. package/dist/lib/use-controllable-state.js +0 -6
  366. package/dist/lib/use-fluid-hover.d.ts +0 -138
  367. package/dist/lib/use-fluid-hover.js +0 -14
  368. package/dist/lib/use-is-mobile.d.ts +0 -2
  369. package/dist/lib/use-is-mobile.js +0 -6
  370. package/dist/lib/use-keyboard-nav-gate.d.ts +0 -20
  371. package/dist/lib/use-keyboard-nav-gate.js +0 -7
  372. package/dist/lib/use-merge-split.d.ts +0 -47
  373. package/dist/lib/use-merge-split.js +0 -15
  374. package/dist/lib/use-morph.d.ts +0 -101
  375. package/dist/lib/use-morph.js +0 -20
  376. package/dist/lib/use-touch-primary.d.ts +0 -2
  377. package/dist/lib/use-touch-primary.js +0 -6
  378. package/dist/lib/utils.d.ts +0 -3
  379. package/dist/lib/utils.js +0 -6
  380. package/dist/primitives/separator/index.d.ts +0 -25
  381. package/dist/primitives/separator/index.js +0 -9
  382. package/dist/primitives/skeleton/index.d.ts +0 -35
  383. package/dist/primitives/skeleton/index.js +0 -13
  384. package/dist/shared/components-01w9qcfa.js +0 -277
  385. package/dist/shared/components-124epb7f.js +0 -53
  386. package/dist/shared/components-26snnbs9.js +0 -322
  387. package/dist/shared/components-2krsc300.js +0 -254
  388. package/dist/shared/components-2qbqqpgq.js +0 -1545
  389. package/dist/shared/components-34txk9rj.js +0 -35
  390. package/dist/shared/components-3h92d94m.js +0 -23
  391. package/dist/shared/components-4vfm04wq.js +0 -82
  392. package/dist/shared/components-54nq988h.js +0 -123
  393. package/dist/shared/components-7s7pykk5.js +0 -281
  394. package/dist/shared/components-868v7scv.js +0 -399
  395. package/dist/shared/components-8jrk5eye.js +0 -448
  396. package/dist/shared/components-adkarj8n.js +0 -35
  397. package/dist/shared/components-b1e239g3.js +0 -28
  398. package/dist/shared/components-b94t6qtj.js +0 -143
  399. package/dist/shared/components-bsqynhaj.js +0 -179
  400. package/dist/shared/components-c9yr4ed1.js +0 -44
  401. package/dist/shared/components-d3shbwqq.js +0 -95
  402. package/dist/shared/components-dhgk6mbt.js +0 -16
  403. package/dist/shared/components-drxrtnp2.js +0 -25
  404. package/dist/shared/components-f7h57m6t.js +0 -59
  405. package/dist/shared/components-g3eafs70.js +0 -337
  406. package/dist/shared/components-gx4h5rbb.js +0 -40
  407. package/dist/shared/components-gyt7my41.js +0 -328
  408. package/dist/shared/components-h6p86be3.js +0 -19
  409. package/dist/shared/components-h83az0bz.js +0 -33
  410. package/dist/shared/components-hdzrqqpx.js +0 -55
  411. package/dist/shared/components-krtx3m5b.js +0 -84
  412. package/dist/shared/components-kswxnm3h.js +0 -24
  413. package/dist/shared/components-mhza2wdn.js +0 -482
  414. package/dist/shared/components-mrbaxt8q.js +0 -62
  415. package/dist/shared/components-pt5ftm4p.js +0 -38
  416. package/dist/shared/components-rtadhd9j.js +0 -64
  417. package/dist/shared/components-tv7b8tet.js +0 -271
  418. package/dist/shared/components-tz4gaf1v.js +0 -23
  419. package/dist/shared/components-wbbckbw8.js +0 -34
  420. package/dist/shared/components-wknae3m4.js +0 -298
  421. package/dist/shared/components-xdqmt6ed.js +0 -20
  422. package/dist/shared/components-xjkdvnbg.js +0 -107
  423. package/dist/shared/components-z3v0n0de.js +0 -11
  424. package/dist/tokens/colors.css +0 -230
  425. package/dist/tokens/motion.css +0 -40
  426. package/dist/tokens/radius.css +0 -10
  427. package/dist/tokens/spacing.css +0 -9
  428. package/dist/tokens/typography.css +0 -119
  429. package/docs/chrome.css +0 -120
  430. package/docs/chrome.tsx +0 -283
  431. package/docs/index.ts +0 -9
  432. package/docs/vite.ts +0 -85
  433. package/index.ts +0 -60
  434. package/lib/date-locale.ts +0 -195
  435. package/lib/field-classes.ts +0 -121
  436. package/lib/field-variants.ts +0 -117
  437. package/lib/fluid-hover-highlight.tsx +0 -139
  438. package/lib/focus-ring.tsx +0 -46
  439. package/lib/goo-indicator.tsx +0 -261
  440. package/lib/icon-context.tsx +0 -310
  441. package/lib/inert.ts +0 -22
  442. package/lib/morph-layers.tsx +0 -102
  443. package/lib/morph-part.tsx +0 -49
  444. package/lib/popup.ts +0 -52
  445. package/lib/reduced-motion.ts +0 -19
  446. package/lib/row-index.tsx +0 -29
  447. package/lib/row-order.tsx +0 -126
  448. package/lib/scroll-column.tsx +0 -273
  449. package/lib/shape-context.tsx +0 -138
  450. package/lib/shape-radius.ts +0 -67
  451. package/lib/size-context.tsx +0 -66
  452. package/lib/size-scale.ts +0 -115
  453. package/lib/slot.ts +0 -115
  454. package/lib/springs.ts +0 -62
  455. package/lib/surface-classes.ts +0 -58
  456. package/lib/surface-context.tsx +0 -21
  457. package/lib/use-controllable-state.ts +0 -44
  458. package/lib/use-fluid-hover.ts +0 -541
  459. package/lib/use-is-mobile.ts +0 -25
  460. package/lib/use-keyboard-nav-gate.ts +0 -45
  461. package/lib/use-merge-split.tsx +0 -209
  462. package/lib/use-morph.ts +0 -575
  463. package/lib/use-touch-primary.tsx +0 -43
  464. package/lib/utils.ts +0 -37
  465. package/preset.ts +0 -199
  466. package/primitives/code/examples.mdx +0 -27
  467. package/primitives/code/index.tsx +0 -80
  468. package/primitives/craft/demos.tsx +0 -173
  469. package/primitives/craft/examples.mdx +0 -142
  470. package/primitives/craft/index.tsx +0 -76
  471. package/primitives/fluid-hover/examples.mdx +0 -84
  472. package/primitives/fluid-hover/index.tsx +0 -316
  473. package/primitives/morph/examples.mdx +0 -139
  474. package/primitives/morph/index.tsx +0 -192
  475. package/primitives/motion/examples.mdx +0 -100
  476. package/primitives/motion/index.tsx +0 -160
  477. package/primitives/scroll-area/examples.mdx +0 -58
  478. package/primitives/scroll-area/index.tsx +0 -204
  479. package/primitives/separator/examples.mdx +0 -30
  480. package/primitives/separator/index.tsx +0 -53
  481. package/primitives/sizes/examples.mdx +0 -167
  482. package/primitives/sizes/index.tsx +0 -89
  483. package/primitives/skeleton/examples.mdx +0 -51
  484. package/primitives/skeleton/index.tsx +0 -72
  485. package/primitives/skeleton/skeleton.css +0 -15
  486. package/primitives/surface/examples.mdx +0 -173
  487. package/primitives/surface/index.tsx +0 -109
  488. package/primitives/surface/surface.css +0 -176
  489. /package/dist/shared/{components-3ey0hzpb.js → components-1v7rxytx.js} +0 -0
  490. /package/dist/shared/{components-jcnjay5g.js → components-ezxhrmer.js} +0 -0
@@ -1,636 +0,0 @@
1
- // biome-ignore-all lint: vendored upstream code keeps its own patterns (see the header)
2
- /*
3
- * Ported from Fluid Functionalism — github.com/mickadesign/fluid-functionalism
4
- * `registry/base/radio-group.tsx` @ b3587bdbd83fc66c2a6aae3817ffb856cb09260b
5
- * MIT License © 2026 Micka Touillaud — fluidfunctionalism.com (notice: LICENSE.fluid-functionalism)
6
- * Local modifications: `"use client"` dropped; `@/lib/*` and `@/hooks/*` imports
7
- * rewritten to `../../../lib/*` (`SizeProvider` to `../../../primitives/sizes`),
8
- * `@/components/ui/fluid-hover-highlight` to `../../../lib/fluid-hover-highlight`,
9
- * `framer-motion` to `motion/react`;
10
- * uncontrolled mode added — `defaultSelectedIndex` + group-level
11
- * `onSelectedIndexChange` twin `selectedIndex`, and `defaultValue` twins
12
- * `value` (`onValueChange` now also fires uncontrolled), both backed by
13
- * `useControllableState`; the group context carries a `selectIndex` setter
14
- * that every `RadioItem` selection calls (after its own `onValueChange` /
15
- * `onSelect`), so items need no `selected` / `onSelect` of their own; arrow-key
16
- * focus guarded for `noUncheckedIndexedAccess`; modo docs — TSDoc with FF's
17
- * docs/API text, statics typed via a cast on the root, default export.
18
- * Styling reads DS tokens (AGENTS.md styling): inline `fontVariationSettings`
19
- * → `weight-*`; the hex focus-ring fallback → `ring-focus-ring` /
20
- * `border-focus-ring`; `duration-80|120|160` and tier-length JS durations →
21
- * `duration-<tier>` / `spring.*`; the unchecked `border-neutral-400` /
22
- * `dark:border-neutral-500` → `border-control`.
23
- * The selected background is a `GooIndicator` (`lib/goo-indicator.tsx`): it
24
- * melts from row to row instead of sliding.
25
- * Renamed for modo (item `radio`): `RadioItem` → `Radio`, the default
26
- * export; `RadioGroup` → its `Radio.Group` static. A row's `index` is
27
- * optional: the group numbers its rows in order (`lib/row-index.tsx`).
28
- * Local: `label` is optional and takes any node; a `Radio` without one is a
29
- * bare circle, Base UI's Radio.Root itself (focus, arrow keys, `id` /
30
- * `<label htmlFor>`, `aria-label(ledby)`), laid out however the caller likes
31
- * inside a value-mode group, which then draws no rows of its own (no hover,
32
- * selected background or ring), and keeps its border when picked. `disabled`
33
- * on a row or the whole group (which then dims as a whole); `name` and
34
- * `required` on the group reach Base UI's RadioGroup for forms (value mode).
35
- */
36
-
37
- import { Radio as RadioPrimitive } from '@base-ui/react/radio'
38
- import { RadioGroup as RadioGroupPrimitive } from '@base-ui/react/radio-group'
39
- import { AnimatePresence, motion } from 'motion/react'
40
- import {
41
- Children,
42
- createContext,
43
- type ForwardRefExoticComponent,
44
- forwardRef,
45
- type HTMLAttributes,
46
- isValidElement,
47
- type ReactNode,
48
- type RefAttributes,
49
- useContext,
50
- useEffect,
51
- useId,
52
- useRef,
53
- useState,
54
- } from 'react'
55
- import { FluidHoverHighlight } from '../../../lib/fluid-hover-highlight'
56
- import { GooIndicator } from '../../../lib/goo-indicator'
57
- import { isDisabledRow } from '../../../lib/popup'
58
- import { IndexedRows, useRowIndex } from '../../../lib/row-index'
59
- import { useShape } from '../../../lib/shape-context'
60
- import { type SizeVariant, useSize } from '../../../lib/size-context'
61
- import { spring } from '../../../lib/springs'
62
- import { useControllableState } from '../../../lib/use-controllable-state'
63
- import { useFluidHover, useRegisterFluidHoverItem } from '../../../lib/use-fluid-hover'
64
- import { cn } from '../../../lib/utils'
65
- import { SizeProvider } from '../../../primitives/sizes'
66
-
67
- interface RadioGroupContextValue {
68
- registerItem: (index: number, element: HTMLElement | null) => void
69
- activeIndex: number | null
70
- selectedIndex: number | null
71
- selectedValue?: string
72
- onValueChange?: (value: string) => void
73
- /** Whether any item in the group is currently selected. Drives the roving
74
- * tabindex fallback: with no selection, the first item must stay tabbable
75
- * or the whole group becomes unreachable by keyboard. */
76
- hasSelection: boolean
77
- /** Local: selects a row by index, through `onSelectedIndexChange`. */
78
- selectIndex: (index: number) => void
79
- /** Local: the whole group is disabled. */
80
- disabled: boolean
81
- }
82
-
83
- const RadioGroupContext = createContext<RadioGroupContextValue | null>(null)
84
-
85
- function useRadioGroupContext() {
86
- const ctx = useContext(RadioGroupContext)
87
- if (!ctx) throw new Error('Radio must be used within a Radio.Group')
88
- return ctx
89
- }
90
-
91
- interface RadioGroupProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onSelect' | 'defaultValue'> {
92
- /** `Radio` rows, or bare radios (no `label`) in a layout of your own (see Bare). */
93
- children: ReactNode
94
- /** Index of the currently selected item. Controlled — pair it with `onSelectedIndexChange` (or per-item `onSelect`). */
95
- selectedIndex?: number
96
- /** Selected index on first render when uncontrolled. Defaults to no selection. */
97
- defaultSelectedIndex?: number
98
- /** Called with the new index whenever an item is selected. */
99
- onSelectedIndexChange?: (index: number) => void
100
- /** Selected item `value` (value mode; items then need a `value`). Controlled; wraps the group in Base UI's RadioGroup for form integration. */
101
- value?: string
102
- /** Selected item `value` on first render when uncontrolled (value mode). */
103
- defaultValue?: string
104
- /** Called with the selected item's `value` when it changes (value mode). */
105
- onValueChange?: (value: string) => void
106
- /** Disables every radio in the group. Defaults to `false`. */
107
- disabled?: boolean
108
- /** Form field name for the selected `value` (value mode): a hidden input carries it. */
109
- name?: string
110
- /** Marks the group required in a form (value mode). Defaults to `false`. */
111
- required?: boolean
112
- /** Pins the group's rows to one step of the size ladder (default 36px, compact 28px). Defaults to the surrounding SizeProvider. */
113
- size?: SizeVariant
114
- }
115
-
116
- // `Radio.Group`: the list, its selection, hover highlight and focus ring.
117
- const RadioGroup = forwardRef<HTMLDivElement, RadioGroupProps>(
118
- (
119
- {
120
- children,
121
- selectedIndex: selectedIndexProp,
122
- defaultSelectedIndex,
123
- onSelectedIndexChange,
124
- value: valueProp,
125
- defaultValue,
126
- onValueChange: onValueChangeProp,
127
- disabled = false,
128
- name,
129
- required,
130
- size,
131
- className,
132
- ...props
133
- },
134
- ref,
135
- ) => {
136
- const [selectedIndex, selectIndex] = useControllableState<number | undefined>(
137
- selectedIndexProp,
138
- defaultSelectedIndex,
139
- onSelectedIndexChange as ((index: number | undefined) => void) | undefined,
140
- )
141
- const [value, onValueChange] = useControllableState<string | undefined>(
142
- valueProp,
143
- defaultValue,
144
- onValueChangeProp as ((value: string | undefined) => void) | undefined,
145
- )
146
-
147
- const containerRef = useRef<HTMLDivElement>(null)
148
- const childValues = Children.toArray(children)
149
- .filter(isValidElement)
150
- .map(child => (child.props as { value?: string }).value)
151
- const hover = useFluidHover(containerRef, { isItemDisabled: isDisabledRow })
152
- const { activeIndex, setActiveIndex, itemRects, handlers, registerItem } = hover
153
-
154
- const [focusedIndex, setFocusedIndex] = useState<number | null>(null)
155
- const resolvedSelectedIndex =
156
- value !== undefined ? childValues.findIndex(childValue => childValue === value) : (selectedIndex ?? -1)
157
- // Covers all three selection APIs: value, selectedIndex, per-item selected.
158
- const hasSelection =
159
- resolvedSelectedIndex >= 0 ||
160
- Children.toArray(children)
161
- .filter(isValidElement)
162
- .some(child => (child.props as { selected?: boolean }).selected === true)
163
-
164
- const focusRect = focusedIndex !== null ? itemRects[focusedIndex] : null
165
- const selectedRect = resolvedSelectedIndex >= 0 ? itemRects[resolvedSelectedIndex] : null
166
- const shape = useShape()
167
-
168
- // Bare radios lay themselves out: no direct child is a labelled row, so
169
- // the group draws no rows and leaves the arrow keys to Base UI's group.
170
- const hasRows = Children.toArray(children).some(
171
- child => isValidElement(child) && (child.props as { label?: ReactNode }).label !== undefined,
172
- )
173
- const setRef = (node: HTMLDivElement | null) => {
174
- ;(containerRef as React.MutableRefObject<HTMLDivElement | null>).current = node
175
- if (typeof ref === 'function') ref(node)
176
- else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node
177
- }
178
-
179
- if (!hasRows) {
180
- const bare = (
181
- <RadioGroupContext.Provider
182
- value={{
183
- registerItem,
184
- activeIndex,
185
- selectedIndex: null,
186
- selectedValue: value,
187
- onValueChange,
188
- hasSelection,
189
- selectIndex,
190
- disabled,
191
- }}
192
- >
193
- <RadioGroupPrimitive
194
- ref={setRef}
195
- // `null`: nothing picked yet, still controlled.
196
- value={value ?? null}
197
- onValueChange={v => onValueChange(v as string)}
198
- disabled={disabled}
199
- name={name}
200
- required={required}
201
- className={cn('relative flex flex-col gap-2', className)}
202
- {...(props as HTMLAttributes<HTMLDivElement>)}
203
- >
204
- {children}
205
- </RadioGroupPrimitive>
206
- </RadioGroupContext.Provider>
207
- )
208
- return size ? <SizeProvider size={size}>{bare}</SizeProvider> : bare
209
- }
210
-
211
- const content = (
212
- <div
213
- ref={node => {
214
- ;(containerRef as React.MutableRefObject<HTMLDivElement | null>).current = node
215
- if (typeof ref === 'function') ref(node)
216
- else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node
217
- }}
218
- onMouseEnter={handlers.onMouseEnter}
219
- onMouseMove={handlers.onMouseMove}
220
- onMouseLeave={handlers.onMouseLeave}
221
- onClick={handlers.onClick}
222
- onFocus={e => {
223
- const indexAttr = (e.target as HTMLElement)
224
- .closest('[data-fluid-hover-index]')
225
- ?.getAttribute('data-fluid-hover-index')
226
- if (indexAttr != null) {
227
- const idx = Number(indexAttr)
228
- setActiveIndex(idx)
229
- setFocusedIndex((e.target as HTMLElement).matches(':focus-visible') ? idx : null)
230
- }
231
- }}
232
- onBlur={e => {
233
- if (containerRef.current?.contains(e.relatedTarget as Node)) return
234
- setFocusedIndex(null)
235
- setActiveIndex(null)
236
- }}
237
- onKeyDown={e => {
238
- // Scope to row wrappers only. The hidden radio primitive also
239
- // carries role="radio", so a bare [role="radio"] selector matches
240
- // twice per row and arrows land on the invisible control.
241
- const items = Array.from(
242
- containerRef.current?.querySelectorAll('[data-fluid-hover-index]:not([aria-disabled="true"])') ?? [],
243
- ) as HTMLElement[]
244
- const currentIdx = items.indexOf(e.target as HTMLElement)
245
- if (currentIdx === -1) return
246
-
247
- // In value mode this handler is merged with Base UI RadioGroup's
248
- // composite onto the same element; suppress the composite's own
249
- // roving focus (it targets the hidden sr-only radios).
250
- const preventBaseUI = (e as unknown as { preventBaseUIHandler?: () => void }).preventBaseUIHandler
251
-
252
- if (['ArrowDown', 'ArrowUp', 'ArrowRight', 'ArrowLeft'].includes(e.key)) {
253
- e.preventDefault()
254
- preventBaseUI?.()
255
- const next = ['ArrowDown', 'ArrowRight'].includes(e.key)
256
- ? (currentIdx + 1) % items.length
257
- : (currentIdx - 1 + items.length) % items.length
258
- items[next]?.focus()
259
- items[next]?.click()
260
- } else if (e.key === 'Home') {
261
- e.preventDefault()
262
- preventBaseUI?.()
263
- items[0]?.focus()
264
- items[0]?.click()
265
- } else if (e.key === 'End') {
266
- e.preventDefault()
267
- preventBaseUI?.()
268
- items[items.length - 1]?.focus()
269
- items[items.length - 1]?.click()
270
- }
271
- }}
272
- role="radiogroup"
273
- aria-disabled={disabled || undefined}
274
- className={cn(
275
- 'relative flex flex-col w-72 max-w-full select-none transition-[opacity] duration-fast',
276
- disabled && 'opacity-50 pointer-events-none',
277
- className,
278
- )}
279
- {...props}
280
- >
281
- {selectedRect && <GooIndicator rect={selectedRect} className={cn('bg-active', shape.bg)} />}
282
-
283
- {/* Hover background */}
284
- <FluidHoverHighlight hover={hover} className={shape.bg} />
285
-
286
- {/* Focus ring */}
287
- <AnimatePresence>
288
- {focusRect && (
289
- <motion.div
290
- className={`absolute ${shape.focusRing} pointer-events-none z-20 border border-focus-ring`}
291
- initial={false}
292
- animate={{
293
- left: focusRect.left - 2,
294
- top: focusRect.top - 2,
295
- width: focusRect.width + 4,
296
- height: focusRect.height + 4,
297
- }}
298
- exit={{ opacity: 0, transition: spring.fast.exit }}
299
- transition={{
300
- ...spring.fast,
301
- opacity: { duration: spring.fast.duration },
302
- }}
303
- />
304
- )}
305
- </AnimatePresence>
306
-
307
- <IndexedRows>{children}</IndexedRows>
308
- </div>
309
- )
310
-
311
- // If `value` is provided (controlled-by-value mode), always wrap with the
312
- // Base UI RadioGroup primitive — even when `onValueChange` is absent. The
313
- // inner `<RadioPrimitive.Root>` rendered by each Radio requires a
314
- // parent RadioGroup context; without it, Base UI crashes on context reads.
315
- // The wrapper just doesn't forward changes when the consumer doesn't ask
316
- // to be notified.
317
- // A size prop pins every row in the group to one ladder step.
318
- const withSize = (node: ReactNode) => (size ? <SizeProvider size={size}>{node}</SizeProvider> : node)
319
-
320
- if (value !== undefined) {
321
- return withSize(
322
- <RadioGroupContext.Provider
323
- value={{
324
- registerItem,
325
- activeIndex,
326
- selectedIndex: resolvedSelectedIndex >= 0 ? resolvedSelectedIndex : null,
327
- selectedValue: value,
328
- onValueChange,
329
- hasSelection,
330
- selectIndex,
331
- disabled,
332
- }}
333
- >
334
- <RadioGroupPrimitive
335
- value={value}
336
- onValueChange={v => onValueChange(v as string)}
337
- disabled={disabled}
338
- name={name}
339
- required={required}
340
- render={content}
341
- />
342
- </RadioGroupContext.Provider>,
343
- )
344
- }
345
-
346
- return withSize(
347
- <RadioGroupContext.Provider
348
- value={{
349
- registerItem,
350
- activeIndex,
351
- selectedIndex: selectedIndex ?? null,
352
- hasSelection,
353
- selectIndex,
354
- disabled,
355
- }}
356
- >
357
- {content}
358
- </RadioGroupContext.Provider>,
359
- )
360
- },
361
- )
362
-
363
- RadioGroup.displayName = 'RadioGroup'
364
-
365
- interface RadioProps extends HTMLAttributes<HTMLDivElement> {
366
- /** Label beside the circle, any node. Omit it for a bare circle in a layout of your own (see Bare): it then needs a `value`, and a name from `aria-label`, `aria-labelledby` or a `<label htmlFor>` pointing at its `id`. */
367
- label?: ReactNode
368
- /** Position within the `Radio.Group`: the row's `selectedIndex`. Defaults to its order among the group's rows. */
369
- index?: number
370
- /** Whether this item is selected. Defaults to the group's selection. */
371
- selected?: boolean
372
- /** Called when this item is selected (the group's change callbacks fire too). */
373
- onSelect?: () => void
374
- /** The item's value in a value-mode group, forwarded to the underlying Base UI radio for forms. A bare radio needs one. */
375
- value?: string
376
- /** Dims the radio and ignores presses; the hover and the arrow keys skip it. Defaults to the group's `disabled`. */
377
- disabled?: boolean
378
- }
379
-
380
- type RadioComponent = ForwardRefExoticComponent<RadioProps & RefAttributes<HTMLDivElement>> & {
381
- Group: typeof RadioGroup
382
- }
383
-
384
- interface RadioCircleProps {
385
- selected: boolean
386
- /** Lit: the border darkens. */
387
- active: boolean
388
- compact: boolean
389
- /** Already selected at mount: the dot is there, not scaled in. */
390
- skipAnimation: boolean
391
- /** A bare circle keeps its border when picked: a lone dot would read as a bullet. */
392
- bare: boolean
393
- }
394
-
395
- /** The circle's insides: the border and the dot that scales in. */
396
- function RadioCircle({ selected, active, compact, skipAnimation, bare }: RadioCircleProps) {
397
- return (
398
- <>
399
- {/* Border */}
400
- <div
401
- className={cn(
402
- 'absolute inset-0 rounded-full border-solid transition-all duration-fast',
403
- selected
404
- ? bare
405
- ? 'border-[1.5px] border-control'
406
- : 'border-[1.5px] border-transparent'
407
- : active
408
- ? 'border-[1.5px] border-control'
409
- : 'border-[1.5px] border-border',
410
- )}
411
- />
412
- {/* Dot */}
413
- <AnimatePresence>
414
- {selected && (
415
- <motion.div
416
- className="absolute inset-0 flex items-center justify-center"
417
- initial={{
418
- opacity: skipAnimation ? 1 : 0,
419
- scale: skipAnimation ? 1 : 0.3,
420
- }}
421
- animate={{ opacity: 1, scale: 1 }}
422
- exit={{ opacity: 0, scale: 0.3, transition: { duration: 0.04 } }}
423
- transition={spring.fast}
424
- >
425
- <div className={cn('rounded-full bg-foreground', compact ? 'w-[7px] h-[7px]' : 'w-[8px] h-[8px]')} />
426
- </motion.div>
427
- )}
428
- </AnimatePresence>
429
- </>
430
- )
431
- }
432
-
433
- /**
434
- * Radio buttons with fluid hover and animated selection.
435
- *
436
- * Each `Radio` is one row of a `Radio.Group`. The selected background melts
437
- * to the chosen row, a fluid hover highlight melts between rows (the liquid
438
- * indicators in Morph), the dot scales in, and the label animates to
439
- * semibold without shifting its width. Arrow keys, Home and End move focus
440
- * and select, with a roving tabindex on the selected row. The group numbers
441
- * its rows in order, so they need no `index`. FF's per-row `selected` /
442
- * `onSelect` still work and override the group for their row.
443
- *
444
- * Without a `label` a radio is just the circle: Base UI's radio, picked by
445
- * its `value`, named by a `<label htmlFor>` pointing at its `id` or by
446
- * `aria-label`. Lay bare radios out however you like inside the group (a
447
- * row with a description, a grid of cards); the group then draws no rows of
448
- * its own and Base UI moves focus and selection with the arrow keys.
449
- *
450
- * Statics:
451
- * - `Radio.Group` — the list. Select by index — uncontrolled with
452
- * `defaultSelectedIndex`, controlled with `selectedIndex` +
453
- * `onSelectedIndexChange` — or by row `value` (`defaultValue` / `value` +
454
- * `onValueChange`), which also renders Base UI radios for form
455
- * integration (`name`, `required`); `disabled` turns every radio off;
456
- * `size` pins every row to one step of the size ladder.
457
- *
458
- * @example {@include ./examples.mdx}
459
- */
460
- const Radio = forwardRef<HTMLDivElement, RadioProps>(
461
- ({ label, index: indexProp, selected, onSelect, value, disabled: disabledProp, className, ...props }, ref) => {
462
- const internalRef = useRef<HTMLDivElement>(null)
463
- const hasMounted = useRef(false)
464
- const {
465
- registerItem,
466
- activeIndex,
467
- selectedIndex,
468
- selectedValue,
469
- onValueChange,
470
- hasSelection,
471
- selectIndex,
472
- disabled: groupDisabled,
473
- } = useRadioGroupContext()
474
- const index = useRowIndex(indexProp)
475
- const labelId = useId()
476
- const [hovered, setHovered] = useState(false)
477
- const bare = label === undefined
478
- const disabled = disabledProp ?? groupDisabled
479
-
480
- // A bare radio is no row: it joins neither the hover nor the numbering.
481
- useRegisterFluidHoverItem(bare ? undefined : registerItem, index, internalRef)
482
-
483
- useEffect(() => {
484
- hasMounted.current = true
485
- }, [])
486
-
487
- const isActive = bare ? hovered : activeIndex === index
488
- const skipAnimation = !hasMounted.current
489
- const shape = useShape()
490
- const sizeClasses = useSize()
491
- const compact = sizeClasses.variant === 'compact'
492
- const isSelected =
493
- value !== undefined && selectedValue !== undefined
494
- ? selectedValue === value
495
- : (selected ?? selectedIndex === index)
496
-
497
- const handleSelect = () => {
498
- if (disabled) return
499
- if (value !== undefined) {
500
- onValueChange?.(value)
501
- }
502
- onSelect?.()
503
- selectIndex(index)
504
- }
505
-
506
- const setRef = (node: HTMLDivElement | null) => {
507
- ;(internalRef as React.MutableRefObject<HTMLDivElement | null>).current = node
508
- if (typeof ref === 'function') ref(node)
509
- else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node
510
- }
511
- const circle = (
512
- <RadioCircle
513
- selected={isSelected}
514
- active={isActive}
515
- compact={compact}
516
- skipAnimation={skipAnimation}
517
- bare={bare}
518
- />
519
- )
520
-
521
- if (bare) {
522
- return (
523
- <RadioPrimitive.Root
524
- ref={setRef}
525
- render={<div />}
526
- value={value}
527
- disabled={disabled}
528
- // Base UI's group picks it; this reports the row's own `onSelect`.
529
- onClick={() => {
530
- if (!disabled) onSelect?.()
531
- }}
532
- onPointerEnter={e => {
533
- if (e.pointerType === 'mouse') setHovered(true)
534
- }}
535
- onPointerLeave={() => setHovered(false)}
536
- className={cn(
537
- 'relative inline-flex shrink-0 cursor-pointer rounded-full outline-none transition-[opacity] duration-fast',
538
- 'focus-visible:ring-1 focus-visible:ring-focus-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background',
539
- compact ? 'size-3.5' : 'size-4',
540
- disabled && 'opacity-50 pointer-events-none',
541
- className,
542
- )}
543
- // The root renders a div (`render`); its handlers are typed for Base UI's span.
544
- {...(props as HTMLAttributes<HTMLSpanElement>)}
545
- >
546
- {circle}
547
- </RadioPrimitive.Root>
548
- )
549
- }
550
-
551
- return (
552
- <div
553
- ref={setRef}
554
- data-fluid-hover-index={index}
555
- data-disabled={disabled || undefined}
556
- // Roving tabindex: selected item is the tab stop; with no selection the
557
- // first item takes it so the group stays keyboard-reachable.
558
- tabIndex={disabled ? -1 : isSelected ? 0 : !hasSelection && index === 0 ? 0 : -1}
559
- role="radio"
560
- aria-checked={isSelected}
561
- aria-disabled={disabled || undefined}
562
- aria-label={typeof label === 'string' ? label : undefined}
563
- aria-labelledby={typeof label !== 'string' ? labelId : undefined}
564
- onClick={handleSelect}
565
- onMouseDown={e => {
566
- // Clicking the 15px radio circle would natively focus the hidden
567
- // primitive (nearest focusable ancestor of the click target), after
568
- // which arrow-key nav dead-zones: the group keydown handler can't
569
- // find the target among the row wrappers. Prevent the native focus
570
- // move (click still fires) and land focus on the row instead. Skip
571
- // genuinely interactive children so we don't hijack their focus.
572
- const interactive = (e.target as HTMLElement).closest(
573
- 'button:not([tabindex="-1"]), a[href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
574
- )
575
- if (interactive && interactive !== e.currentTarget) return
576
- e.preventDefault()
577
- e.currentTarget.focus()
578
- }}
579
- onKeyDown={e => {
580
- if (e.key === ' ' || e.key === 'Enter') {
581
- e.preventDefault()
582
- handleSelect()
583
- }
584
- }}
585
- className={cn(
586
- // Fixed height (was py-1.5 around a 19.5px line box ≈ 31.5px) so the
587
- // text-box trim on the label doesn't shrink the row.
588
- `relative z-10 flex ${sizeClasses.control} items-center ${sizeClasses.gap} ${shape.item} ${sizeClasses.px} cursor-pointer outline-none transition-[opacity] duration-fast`,
589
- // A disabled group dims as a whole, selected background included.
590
- disabled && 'pointer-events-none',
591
- disabled && !groupDisabled && 'opacity-50',
592
- className,
593
- )}
594
- {...props}
595
- >
596
- {/* Radio circle */}
597
- <div className={cn('relative shrink-0', compact ? 'w-[14px] h-[14px]' : 'w-[16px] h-[16px]')}>{circle}</div>
598
-
599
- {/* Label */}
600
- {/* Both stacked spans carry the text-box trim so the invisible bold
601
- sizer and the visible label keep identical boxes. */}
602
- <span className={cn('inline-grid', sizeClasses.text)}>
603
- <span
604
- className="col-start-1 row-start-1 invisible [text-box:trim-both_cap_alphabetic] weight-semibold"
605
- aria-hidden="true"
606
- >
607
- {label}
608
- </span>
609
- <span
610
- id={labelId}
611
- className={cn(
612
- 'col-start-1 row-start-1 transition-[color,font-variation-settings] duration-fast [text-box:trim-both_cap_alphabetic]',
613
- isSelected || isActive ? 'text-foreground' : 'text-muted-foreground',
614
- isSelected ? 'weight-semibold' : 'weight-normal',
615
- )}
616
- >
617
- {label}
618
- </span>
619
- </span>
620
-
621
- {/* Hidden Base UI Radio input for accessibility */}
622
- {value !== undefined && (
623
- <RadioPrimitive.Root value={value} disabled={disabled} className="sr-only" tabIndex={-1} aria-hidden />
624
- )}
625
- </div>
626
- )
627
- },
628
- ) as RadioComponent
629
-
630
- Radio.displayName = 'Radio'
631
-
632
- Object.assign(Radio, { Group: RadioGroup })
633
-
634
- export type { RadioGroupProps, RadioProps }
635
- export { Radio, RadioGroup }
636
- export default Radio