@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,662 +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/default/input-copy.tsx` @ b3587bdbd83fc66c2a6aae3817ffb856cb09260b
5
- * MIT License © 2026 Micka Touillaud — fluidfunctionalism.com (notice: LICENSE.fluid-functionalism)
6
- * Local modifications:
7
- * - `"use client"` directive dropped (no RSC here).
8
- * - `framer-motion` → `motion/react`; `@/lib/{utils,icon-context,shape-context,
9
- * size-context,springs}` rewritten to `../../lib/*` (the `@/lib/font-weight`
10
- * import went with the inline `fontVariationSettings`, below);
11
- * `@/registry/radix/tooltip` → `../overlays/tooltip`.
12
- * - React 18 types: `useRef<ReturnType<typeof setTimeout>>(null)` →
13
- * `useRef<ReturnType<typeof setTimeout> | null>(null)` (React 19 allows the
14
- * 1-arg null form, React 18's overloads don't).
15
- * - `CopyProps`: member docs replaced by the FF docs API-table text with the
16
- * defaults spelled into the prose (modo reads defaults from the description, not
17
- * from the destructuring), and `className` re-declared so it is documented.
18
- * - The tooltip comment's "Radix closes it on pointer down" reads "Base UI" here —
19
- * this port's Tooltip is the Base UI flavor; behaviour is the same.
20
- * - modo item: TSDoc from the FF "InputCopy" docs page. Renamed `Copy` (item
21
- * `copy`, outside the inputs group): `InputCopy*` exports → `Copy*`.
22
- * - Styling reads DS tokens (AGENTS.md styling): inline
23
- * `fontVariationSettings` → `weight-*`; the hex focus-ring fallback →
24
- * `ring-focus-ring` / `border-focus-ring`; literal colors → color tokens;
25
- * `duration-80|120|160` and tier-length JS durations → `duration-<tier>` /
26
- * `spring.*`.
27
- * - The copied state morphs (local, in the morph layer's language): the copy
28
- * glyph's two sheets melt through the shared goo filter (`GooFilter`,
29
- * `GOO_BLUR_RATIO`) into a disc tinted like Toast's state chip
30
- * (`--status-success`, `--status-error` for a failure), behind the check or
31
- * ✕, and split back into the glyph when the state times out. The glyphs
32
- * crossfade over one fixed slot instead of `mode="wait"`; the button
33
- * variant keeps that one glyph slot and swaps only its word.
34
- * - `CopyProps` is a union on `variant`: `value` is required except for the chip.
35
- * - Two more variants (local, additive): `"chip"` (an inline `<span>` chip with
36
- * `children`, copying its text or `value`) and `"glyph"` (the bare copy glyph,
37
- * no visible value, named by `aria-label`). Both keep a stable accessible name
38
- * and announce the result through a `role="status"` live region. `value` is
39
- * optional for the chip, the root's ref is typed `HTMLElement`, and the strings
40
- * became the `labels` prop with the same English defaults.
41
- */
42
-
43
- import { AnimatePresence, animate, motion, useMotionValue } from 'motion/react'
44
- import {
45
- forwardRef,
46
- type HTMLAttributes,
47
- type ReactNode,
48
- type Ref,
49
- useCallback,
50
- useEffect,
51
- useId,
52
- useLayoutEffect,
53
- useRef,
54
- useState,
55
- } from 'react'
56
- import { type IconComponent, useIcon } from '../../lib/icon-context'
57
- import { GooFilter } from '../../lib/morph-layers'
58
- import { useReduceMotion } from '../../lib/reduced-motion'
59
- import { useShape } from '../../lib/shape-context'
60
- import { type SizeVariant, useSize } from '../../lib/size-context'
61
- import { spring } from '../../lib/springs'
62
- import { useSurface } from '../../lib/surface-context'
63
- import { GOO_BLUR_RATIO } from '../../lib/use-morph'
64
- import { cn } from '../../lib/utils'
65
- import { Tooltip } from '../overlays/tooltip'
66
-
67
- type CopyVariant = 'icon' | 'button' | 'chip' | 'glyph'
68
- type CopyAlign = 'right' | 'left'
69
- type CopyStatus = 'idle' | 'copied' | 'error'
70
-
71
- interface Rect {
72
- x: number
73
- y: number
74
- w: number
75
- h: number
76
- r: number
77
- }
78
-
79
- // The copied state's disc (local): lucide's copy glyph is two sheets on a
80
- // 24-unit grid; scaled to the 14px glyph, they melt through the goo filter
81
- // into one disc behind the check, and split back when the state times out.
82
- const GLYPH = 14
83
- const DISC = 20
84
- const UNIT = GLYPH / 24
85
- const sheet = (at: number): Rect => ({ x: at * UNIT, y: at * UNIT, w: 14 * UNIT, h: 14 * UNIT, r: 2 * UNIT })
86
- const BACK = sheet(2)
87
- const FRONT = sheet(8)
88
- const DISC_RECT: Rect = { x: (GLYPH - DISC) / 2, y: (GLYPH - DISC) / 2, w: DISC, h: DISC, r: DISC / 2 }
89
- const BLUR = (DISC / 2) * GOO_BLUR_RATIO
90
- // The goo layer: the disc plus room for the blur on every side, in glyph coordinates.
91
- const LAYER = { at: DISC_RECT.x - BLUR * 3, size: DISC + BLUR * 6 }
92
-
93
- // Literal per state so the class extractor sees each one. The disc's tint is
94
- // the layer's opacity, applied after the goo filter, which needs opaque shapes.
95
- const TONE_BG = { copied: 'bg-status-success', error: 'bg-status-error' }
96
- const TONE_TEXT = { copied: 'text-status-success', error: 'text-status-error' }
97
- const CHECK_STROKE: Record<'icon' | 'button', string> = {
98
- icon: '[&_svg]:stroke-[1.5] [&_svg]:transition-[stroke-width] [&_svg]:duration-fast group-hover:[&_svg]:stroke-[2]',
99
- button: '[&_svg]:stroke-[2]',
100
- }
101
-
102
- const { exit: _exit, ...enter } = spring.slow
103
-
104
- function placeSheet(el: HTMLElement | null, a: Rect, b: Rect, p: number) {
105
- if (!el) return
106
- const lerp = (from: number, to: number) => from + (to - from) * p
107
- // The slow tier overshoots; a size never goes below zero on the way back.
108
- const w = Math.max(0, lerp(a.w, b.w))
109
- const h = Math.max(0, lerp(a.h, b.h))
110
- el.style.left = `${lerp(a.x, b.x) - LAYER.at}px`
111
- el.style.top = `${lerp(a.y, b.y) - LAYER.at}px`
112
- el.style.width = `${w}px`
113
- el.style.height = `${h}px`
114
- el.style.borderRadius = `${Math.min(Math.max(0, lerp(a.r, b.r)), w / 2, h / 2)}px`
115
- }
116
-
117
- /** The goo layer behind the glyph: two sheets on one progress value, idle at 0, copied (or failed) at 1. */
118
- function CopyDisc({ status }: { status: CopyStatus }) {
119
- const layer = useRef<HTMLSpanElement>(null)
120
- const back = useRef<HTMLSpanElement>(null)
121
- const front = useRef<HTMLSpanElement>(null)
122
- const progress = useMotionValue(0)
123
- const reduced = useReduceMotion()
124
- const gooId = `copy-goo-${useId().replace(/:/g, '')}`
125
- const tone = useRef<'copied' | 'error'>('copied')
126
- const on = status !== 'idle'
127
- if (on) tone.current = status
128
-
129
- useLayoutEffect(() => {
130
- if (!on && progress.get() === 0) return
131
- const render = (p: number) => {
132
- if (layer.current) layer.current.style.visibility = p > 0 ? 'visible' : 'hidden'
133
- placeSheet(back.current, BACK, DISC_RECT, p)
134
- placeSheet(front.current, FRONT, DISC_RECT, p)
135
- }
136
- const target = on ? 1 : 0
137
- if (reduced) {
138
- progress.jump(target)
139
- return render(target)
140
- }
141
- const controls = animate(progress, target, { ...(on ? enter : spring.slow.exit), onUpdate: render })
142
- return () => controls.stop()
143
- }, [on, reduced])
144
-
145
- return (
146
- // Structural geometry: the layer box around the glyph slot, sized for the blur.
147
- <span
148
- aria-hidden
149
- className="pointer-events-none absolute"
150
- style={{ left: LAYER.at, top: LAYER.at, width: LAYER.size, height: LAYER.size }}
151
- >
152
- <GooFilter id={gooId} blur={BLUR} />
153
- {/* The filter is the effect itself: it melts the two token-filled sheets into one. */}
154
- <span ref={layer} className="invisible absolute inset-0 opacity-16" style={{ filter: `url(#${gooId})` }}>
155
- <span ref={back} className={cn('absolute', TONE_BG[tone.current])} />
156
- <span ref={front} className={cn('absolute', TONE_BG[tone.current])} />
157
- </span>
158
- </span>
159
- )
160
- }
161
-
162
- const CHECK_PATH = 'M6 12L10 16L18 8'
163
- const ERROR_PATH = 'M9 9L15 15M15 9L9 15'
164
-
165
- /** The action's glyph slot: the copy icon, or the check / ✕ drawing itself over its disc. */
166
- function CopyGlyph({
167
- status,
168
- copyCount,
169
- variant,
170
- CopyIcon,
171
- }: {
172
- status: CopyStatus
173
- copyCount: number
174
- variant: 'icon' | 'button'
175
- CopyIcon: IconComponent
176
- }) {
177
- return (
178
- <span className="relative flex size-3.5 shrink-0 items-center justify-center">
179
- <CopyDisc status={status} />
180
- <AnimatePresence initial={false}>
181
- {status === 'idle' ? (
182
- <motion.span
183
- key="copy"
184
- initial={{ opacity: 0, scale: 0.8 }}
185
- animate={{ opacity: 1, scale: 1 }}
186
- exit={{ opacity: 0, scale: 0.8, transition: spring.fast.exit }}
187
- transition={spring.fast}
188
- className="absolute inset-0 flex items-center justify-center"
189
- >
190
- <CopyIcon
191
- size={14}
192
- strokeWidth={1.5}
193
- className="transition-[stroke-width] duration-fast group-hover:stroke-[2]"
194
- />
195
- </motion.span>
196
- ) : (
197
- <motion.span
198
- key={`${status}-${copyCount}`}
199
- initial={{ opacity: 0, scale: 0.6 }}
200
- animate={{ opacity: 1, scale: 1 }}
201
- exit={{ opacity: 0, scale: 0.8, transition: spring.fast.exit }}
202
- transition={spring.fast}
203
- className={cn(
204
- 'absolute inset-0 flex items-center justify-center',
205
- TONE_TEXT[status],
206
- CHECK_STROKE[variant],
207
- )}
208
- >
209
- <svg
210
- width={14}
211
- height={14}
212
- viewBox="2 4 20 16"
213
- fill="none"
214
- stroke="currentColor"
215
- strokeLinecap="round"
216
- strokeLinejoin="round"
217
- >
218
- <motion.path
219
- d={status === 'error' ? ERROR_PATH : CHECK_PATH}
220
- initial={{ pathLength: 0 }}
221
- animate={{
222
- pathLength: 1,
223
- transition: { duration: spring.fast.duration, ease: 'easeOut' },
224
- }}
225
- />
226
- </svg>
227
- </motion.span>
228
- )}
229
- </AnimatePresence>
230
- </span>
231
- )
232
- }
233
-
234
- interface CopyLabels {
235
- /** The idle tooltip, and the accessible name when there is no `label`. Defaults to `"Copy to clipboard"`. */
236
- copy?: string
237
- /** The copied state: tooltip, accessible name, the button variant's word and the announcement. Defaults to `"Copied"`. */
238
- copied?: string
239
- /** The failed state: tooltip, accessible name and the announcement. Defaults to `"Copy failed"`. */
240
- failed?: string
241
- /** The button variant's word at rest, and the accessible name when a `label` joins it. Defaults to `"Copy"`. */
242
- word?: string
243
- /** The button variant's word after a failed copy. Defaults to `"Failed"`. */
244
- failedWord?: string
245
- }
246
-
247
- interface CopyBaseProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
248
- /** The text to copy, shown in the field. Required for every variant except `"chip"`, which copies its own text when this is left out. */
249
- value?: string
250
- /** What the chip shows (`variant="chip"` only): any inline content, copied as its text unless `value` is given. Defaults to `value`. */
251
- children?: ReactNode
252
- /** Optional label displayed above the input. */
253
- label?: string
254
- /** Callback fired after the value is successfully copied. */
255
- onCopy?: () => void
256
- /** Disables the input and copy button. Defaults to `false`. */
257
- disabled?: boolean
258
- /** `"icon"`: the value with a trailing copy glyph and a tooltip. `"button"`: the value with a visible Copy word. `"chip"`: an inline chip around `children` that copies its text. `"glyph"`: only the copy glyph, no visible value. Defaults to `"icon"`. */
259
- variant?: CopyVariant
260
- /** Position of the copy action relative to the value. Defaults to `"right"`. */
261
- align?: CopyAlign
262
- /** Pins the field to one step of the size ladder (default 36px, compact 28px — see Sizes). Omitted, it follows the surrounding SizeProvider. */
263
- size?: SizeVariant
264
- /** Extra classes for the wrapper around the label and the field. */
265
- className?: string
266
- /** The accessible name of the `"chip"` and `"glyph"` buttons. A glyph has no visible text, so name it; a chip is named by its content and, without this, adds the copy hint. Defaults to `labels.copy`. */
267
- 'aria-label'?: string
268
- /** The words of the control, for another language. Each key falls back to its English default. */
269
- labels?: CopyLabels
270
- }
271
-
272
- // `value` is the one prop whose requirement depends on the variant: the field
273
- // shows it and the glyph has nothing else to copy, so only the chip may omit it.
274
- type CopyProps = CopyBaseProps &
275
- ({ variant?: Exclude<CopyVariant, 'chip'>; value: string } | { variant: 'chip'; value?: string })
276
-
277
- // The chip's fill: `bg-muted` in light (a step darker than any substrate), and in
278
- // dark three surface steps above the substrate it sits on, so it still reads inside
279
- // a Card or Dialog. Literal per level for the class extractor, like SURFACE_BG.
280
- const DARK_CHIP_BG: Record<number, string> = {
281
- 1: 'dark:bg-surface-1',
282
- 2: 'dark:bg-surface-2',
283
- 3: 'dark:bg-surface-3',
284
- 4: 'dark:bg-surface-4',
285
- 5: 'dark:bg-surface-5',
286
- 6: 'dark:bg-surface-6',
287
- 7: 'dark:bg-surface-7',
288
- 8: 'dark:bg-surface-8',
289
- }
290
-
291
- const LABELS: Required<CopyLabels> = {
292
- copy: 'Copy to clipboard',
293
- copied: 'Copied',
294
- failed: 'Copy failed',
295
- word: 'Copy',
296
- failedWord: 'Failed',
297
- }
298
-
299
- /**
300
- * Read-only input with a copy-to-clipboard button and animated check
301
- * feedback.
302
- *
303
- * The whole row is the button: click anywhere on it and the value goes to
304
- * the clipboard, the copy glyph's two sheets melt into a tinted disc (the
305
- * goo of Morph) behind a check that draws itself, and the tooltip flips to
306
- * "Copied" for 2 seconds; then the disc splits back into the glyph. The value is monospaced and
307
- * highlights on hover so it reads as one selectable token rather than a
308
- * text field, and the async Clipboard API falls back to an off-screen
309
- * textarea + `execCommand` where it is unavailable (insecure context,
310
- * permissions policy) — a failure animates a ✕ and says so. `variant`
311
- * switches between the icon-only affordance (with a tooltip) and a labelled
312
- * Copy button, `align` moves that action to the leading edge, and the field
313
- * follows the surrounding SizeProvider unless `size` pins it.
314
- *
315
- * Two variants have no field. `chip` is an inline `<span>` around `children`
316
- * (the root is a span, so it sits in a sentence): it copies its own text, or
317
- * `value`, and shows the same glyph after it. `glyph` is the copy glyph alone,
318
- * for a place where the value is already on screen; give it an `aria-label`.
319
- * Both are real buttons, keep a stable accessible name and announce "Copied"
320
- * (or the failure) through a live region, so a screen reader hears the result
321
- * of Enter or Space. `labels` translates every word.
322
- *
323
- * @example {@include ./examples.mdx}
324
- */
325
- const Copy = forwardRef<HTMLElement, CopyProps>(
326
- (
327
- {
328
- value,
329
- children,
330
- label,
331
- onCopy,
332
- disabled,
333
- variant = 'icon',
334
- align = 'right',
335
- size,
336
- className,
337
- labels,
338
- 'aria-label': ariaLabel,
339
- ...props
340
- },
341
- ref,
342
- ) => {
343
- const CopyIcon = useIcon('copy')
344
- // "copied" and "error" both occupy the same animation slot on the button
345
- const [status, setStatus] = useState<CopyStatus>('idle')
346
- const [copyCount, setCopyCount] = useState(0)
347
- // "idle" = normal tooltip behavior, "copied" = force open, "suppressed" = force closed
348
- const [tooltipState, setTooltipState] = useState<'idle' | 'copied' | 'suppressed'>('idle')
349
- const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
350
- const tooltipVisibleRef = useRef(false)
351
- const tooltipWasVisibleRef = useRef(false)
352
- const textRef = useRef<HTMLSpanElement>(null)
353
- const shape = useShape()
354
- const sizeClasses = useSize(size)
355
- const chipLevel = Math.min(useSurface() + 3, 8)
356
- const words = { ...LABELS, ...labels }
357
- const chip = variant === 'chip'
358
- const glyph = variant === 'glyph'
359
- const compact = sizeClasses.variant === 'compact'
360
- // The row's height comes from the padded children, so the paddings step
361
- // down with the ladder (py-2 → 36px total, py-1 → 28px).
362
- const rowPy = compact ? 'py-1' : 'py-2'
363
-
364
- // Associate the visible label with the button: the button's accessible
365
- // name reads "Copy <label>" (its own state label + the field label).
366
- const generatedId = useId()
367
- const labelId = label ? `${generatedId}-label` : undefined
368
- const buttonId = `${generatedId}-button`
369
-
370
- const handlePointerDown = useCallback(() => {
371
- // Capture tooltip visibility before Base UI closes it on pointer down
372
- tooltipWasVisibleRef.current = tooltipVisibleRef.current
373
- }, [])
374
-
375
- // execCommand fallback for when the async Clipboard API is unavailable or
376
- // denied (insecure context, permissions policy) — copies via a temporary
377
- // off-screen textarea.
378
- const copyViaExecCommand = useCallback((text: string) => {
379
- const textarea = document.createElement('textarea')
380
- textarea.value = text
381
- textarea.setAttribute('readonly', '')
382
- textarea.style.position = 'fixed'
383
- textarea.style.opacity = '0'
384
- document.body.appendChild(textarea)
385
- textarea.select()
386
- let ok = false
387
- try {
388
- ok = document.execCommand('copy')
389
- } catch {
390
- ok = false
391
- }
392
- document.body.removeChild(textarea)
393
- return ok
394
- }, [])
395
-
396
- const handleCopy = useCallback(async () => {
397
- if (disabled) return
398
- // A chip without `value` copies what it shows.
399
- const text = value ?? textRef.current?.textContent ?? ''
400
- let ok = true
401
- try {
402
- await navigator.clipboard.writeText(text)
403
- } catch {
404
- ok = copyViaExecCommand(text)
405
- }
406
- setStatus(ok ? 'copied' : 'error')
407
- setCopyCount(c => c + 1)
408
- setTooltipState(tooltipWasVisibleRef.current ? 'copied' : 'suppressed')
409
- if (ok) onCopy?.()
410
- if (timeoutRef.current) clearTimeout(timeoutRef.current)
411
- timeoutRef.current = setTimeout(() => {
412
- setStatus('idle')
413
- setTooltipState('suppressed')
414
- }, 2000)
415
- }, [value, disabled, onCopy, copyViaExecCommand])
416
-
417
- const handleTooltipOpenChange = useCallback((open: boolean) => {
418
- tooltipVisibleRef.current = open
419
- }, [])
420
-
421
- useEffect(() => {
422
- return () => {
423
- if (timeoutRef.current) clearTimeout(timeoutRef.current)
424
- }
425
- }, [])
426
-
427
- const handleMouseEnter = useCallback(() => {
428
- setTooltipState(prev => (prev === 'suppressed' ? 'idle' : prev))
429
- }, [])
430
-
431
- const handleMouseLeave = useCallback(() => {
432
- setTooltipState(prev => (prev === 'copied' ? 'suppressed' : prev))
433
- }, [])
434
-
435
- // The chip and the glyph keep one accessible name and say the result
436
- // through a live region: a changed aria-label on a focused button is not
437
- // reliably read out. A new node per copy, so a second copy is announced too.
438
- const announcement = (
439
- <span role="status" className="sr-only">
440
- {status !== 'idle' && <span key={copyCount}>{status === 'error' ? words.failed : words.copied}</span>}
441
- </span>
442
- )
443
-
444
- if (chip) {
445
- return (
446
- <span
447
- ref={ref as Ref<HTMLSpanElement>}
448
- className={cn(
449
- 'relative inline-flex rounded-box bg-muted',
450
- DARK_CHIP_BG[chipLevel],
451
- disabled && 'opacity-50 pointer-events-none',
452
- className,
453
- )}
454
- {...(props as HTMLAttributes<HTMLSpanElement>)}
455
- >
456
- <button
457
- id={buttonId}
458
- type="button"
459
- onPointerDown={handlePointerDown}
460
- onClick={handleCopy}
461
- disabled={disabled}
462
- aria-label={ariaLabel}
463
- className={cn(
464
- 'group relative inline-flex cursor-pointer select-none items-center gap-1 rounded-box px-1 py-0.5 text-foreground outline-none',
465
- 'focus-visible:ring-1 focus-visible:ring-focus-ring',
466
- )}
467
- >
468
- <span
469
- aria-hidden
470
- className="pointer-events-none absolute inset-0 rounded-box bg-hover opacity-0 transition-opacity duration-fast group-hover:opacity-100"
471
- />
472
- <span ref={textRef} className={cn('relative font-mono', compact ? 'text-caption-compact' : 'text-caption')}>
473
- {children ?? value}
474
- </span>
475
- {!ariaLabel && <span className="sr-only">, {words.copy}</span>}
476
- <span className="relative text-muted-foreground transition-colors duration-fast group-hover:text-foreground">
477
- <CopyGlyph status={status} copyCount={copyCount} variant="icon" CopyIcon={CopyIcon} />
478
- </span>
479
- </button>
480
- {announcement}
481
- </span>
482
- )
483
- }
484
-
485
- if (glyph) {
486
- const name = ariaLabel ?? words.copy
487
- return (
488
- <span
489
- ref={ref as Ref<HTMLSpanElement>}
490
- className={cn('inline-flex', disabled && 'opacity-50 pointer-events-none', className)}
491
- onMouseEnter={handleMouseEnter}
492
- onMouseLeave={handleMouseLeave}
493
- {...(props as HTMLAttributes<HTMLSpanElement>)}
494
- >
495
- <Tooltip
496
- content={tooltipState === 'idle' ? name : status === 'error' ? words.failed : words.copied}
497
- delayDuration={500}
498
- sideOffset={2}
499
- forceOpen={tooltipState === 'copied' ? true : tooltipState === 'suppressed' ? false : undefined}
500
- onOpenChange={handleTooltipOpenChange}
501
- >
502
- <button
503
- id={buttonId}
504
- type="button"
505
- onPointerDown={handlePointerDown}
506
- onClick={handleCopy}
507
- disabled={disabled}
508
- aria-label={name}
509
- className={cn(
510
- 'group inline-flex shrink-0 cursor-pointer items-center justify-center text-muted-foreground outline-none transition-colors duration-fast',
511
- 'hover:bg-hover hover:text-foreground active:bg-active focus-visible:ring-1 focus-visible:ring-focus-ring',
512
- shape.button,
513
- compact ? 'size-7' : 'size-9',
514
- )}
515
- >
516
- <CopyGlyph status={status} copyCount={copyCount} variant="icon" CopyIcon={CopyIcon} />
517
- </button>
518
- </Tooltip>
519
- {announcement}
520
- </span>
521
- )
522
- }
523
-
524
- const actionElement =
525
- variant === 'button' ? (
526
- <span
527
- className={cn(
528
- 'shrink-0 flex items-center gap-1.5 px-1.5 transition-colors duration-fast',
529
- rowPy,
530
- sizeClasses.text,
531
- 'text-muted-foreground group-hover:text-foreground',
532
- 'weight-normal',
533
- )}
534
- >
535
- <CopyGlyph status={status} copyCount={copyCount} variant="button" CopyIcon={CopyIcon} />
536
- <span className="select-none inline-grid text-left">
537
- {[words.word, words.copied, words.failedWord].map(word => (
538
- <span key={word} className="col-start-1 row-start-1 invisible" aria-hidden="true">
539
- {word}
540
- </span>
541
- ))}
542
- <AnimatePresence mode="wait" initial={false}>
543
- <motion.span
544
- key={status}
545
- className={cn('col-start-1 row-start-1', status === 'error' && 'text-destructive')}
546
- initial={{ opacity: 0, scale: 0.6 }}
547
- animate={{ opacity: 1, scale: 1 }}
548
- exit={{ opacity: 0, scale: 0.8, transition: spring.fast.exit }}
549
- transition={spring.fast}
550
- >
551
- {status === 'copied' ? words.copied : status === 'error' ? words.failedWord : words.word}
552
- </motion.span>
553
- </AnimatePresence>
554
- </span>
555
- </span>
556
- ) : (
557
- <span
558
- className={cn(
559
- 'shrink-0 flex px-1.5 transition-colors duration-fast',
560
- rowPy,
561
- 'text-muted-foreground group-hover:text-foreground',
562
- )}
563
- >
564
- <CopyGlyph status={status} copyCount={copyCount} variant="icon" CopyIcon={CopyIcon} />
565
- </span>
566
- )
567
-
568
- const valueElement = (
569
- <span
570
- className={cn(
571
- 'flex-1 min-w-0 text-left text-foreground font-mono select-none truncate',
572
- sizeClasses.text,
573
- rowPy,
574
- align === 'left' ? 'pl-1' : 'pl-0',
575
- 'weight-normal',
576
- )}
577
- >
578
- <mark className="bg-transparent text-foreground transition-colors duration-fast group-hover:bg-brand/20 group-hover:text-foreground">
579
- {value}
580
- </mark>
581
- </span>
582
- )
583
-
584
- const buttonContent =
585
- align === 'left' ? (
586
- <>
587
- {actionElement}
588
- {valueElement}
589
- </>
590
- ) : (
591
- <>
592
- {valueElement}
593
- {actionElement}
594
- </>
595
- )
596
-
597
- const button = (
598
- <button
599
- id={buttonId}
600
- type="button"
601
- onPointerDown={handlePointerDown}
602
- onClick={handleCopy}
603
- disabled={disabled}
604
- aria-label={
605
- status === 'copied' ? words.copied : status === 'error' ? words.failed : label ? words.word : words.copy
606
- }
607
- aria-labelledby={label ? `${buttonId} ${labelId}` : undefined}
608
- className={cn(
609
- 'group flex items-center w-full cursor-pointer outline-none transition-all duration-fast',
610
- 'focus-visible:ring-1 focus-visible:ring-focus-ring',
611
- shape.input,
612
- )}
613
- >
614
- {buttonContent}
615
- </button>
616
- )
617
-
618
- return (
619
- <div
620
- ref={ref as Ref<HTMLDivElement>}
621
- className={cn('flex flex-col gap-0.5', disabled && 'opacity-50 pointer-events-none', className)}
622
- onMouseEnter={handleMouseEnter}
623
- onMouseLeave={handleMouseLeave}
624
- aria-label={ariaLabel}
625
- {...props}
626
- >
627
- {label && (
628
- <span
629
- id={labelId}
630
- className={cn(
631
- 'text-muted-foreground',
632
- sizeClasses.text,
633
- align === 'left' ? 'pl-1' : 'pl-0',
634
- 'weight-normal',
635
- )}
636
- >
637
- {label}
638
- </span>
639
- )}
640
- {variant === 'icon' ? (
641
- <Tooltip
642
- content={tooltipState === 'idle' ? words.copy : status === 'error' ? words.failed : words.copied}
643
- delayDuration={500}
644
- sideOffset={2}
645
- forceOpen={tooltipState === 'copied' ? true : tooltipState === 'suppressed' ? false : undefined}
646
- onOpenChange={handleTooltipOpenChange}
647
- >
648
- {button}
649
- </Tooltip>
650
- ) : (
651
- button
652
- )}
653
- </div>
654
- )
655
- },
656
- )
657
-
658
- Copy.displayName = 'Copy'
659
-
660
- export type { CopyAlign, CopyLabels, CopyProps, CopyVariant }
661
- export { Copy }
662
- export default Copy