@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.
- package/README.md +66 -164
- package/dist/base.css +4 -141
- package/dist/blocks/sidebar-app/index.js +26 -40
- package/dist/components/AI/input-message/index.js +16 -23
- package/dist/components/AI/thinking-indicator/index.js +6 -9
- package/dist/components/accordion/index.d.ts +11 -39
- package/dist/components/accordion/index.js +13 -18
- package/dist/components/badge/index.d.ts +11 -18
- package/dist/components/badge/index.js +7 -12
- package/dist/components/button/index.d.ts +17 -53
- package/dist/components/button/index.js +6 -11
- package/dist/components/card/index.d.ts +9 -32
- package/dist/components/card/index.js +11 -17
- package/dist/components/copy/index.d.ts +8 -41
- package/dist/components/copy/index.js +9 -15
- package/dist/components/inputs/checkbox/index.d.ts +9 -28
- package/dist/components/inputs/checkbox/index.js +13 -18
- package/dist/components/inputs/color-picker/index.d.ts +14 -22
- package/dist/components/inputs/color-picker/index.js +18 -26
- package/dist/components/inputs/combobox/index.d.ts +20 -85
- package/dist/components/inputs/combobox/index.js +18 -27
- package/dist/components/inputs/date-time/calendar/index.d.ts +18 -24
- package/dist/components/inputs/date-time/calendar/index.js +11 -15
- package/dist/components/inputs/date-time/date-picker/index.d.ts +16 -105
- package/dist/components/inputs/date-time/date-picker/index.js +25 -35
- package/dist/components/inputs/date-time/date-range-picker/index.d.ts +12 -106
- package/dist/components/inputs/date-time/date-range-picker/index.js +24 -35
- package/dist/components/inputs/date-time/duration-picker/index.d.ts +13 -66
- package/dist/components/inputs/date-time/duration-picker/index.js +24 -33
- package/dist/components/inputs/date-time/month-picker/index.d.ts +10 -60
- package/dist/components/inputs/date-time/month-picker/index.js +23 -32
- package/dist/components/inputs/date-time/time-picker/index.d.ts +10 -58
- package/dist/components/inputs/date-time/time-picker/index.js +24 -33
- package/dist/components/inputs/input/index.d.ts +13 -95
- package/dist/components/inputs/input/index.js +9 -18
- package/dist/components/inputs/radio/index.d.ts +5 -20
- package/dist/components/inputs/radio/index.js +12 -16
- package/dist/components/inputs/search/index.d.ts +3 -3
- package/dist/components/inputs/search/index.js +18 -26
- package/dist/components/inputs/select/index.d.ts +23 -99
- package/dist/components/inputs/select/index.js +14 -29
- package/dist/components/inputs/slider/index.d.ts +0 -9
- package/dist/components/inputs/slider/index.js +5 -8
- package/dist/components/inputs/switch/index.d.ts +2 -8
- package/dist/components/inputs/switch/index.js +12 -15
- package/dist/components/inputs/textarea/index.d.ts +1 -20
- package/dist/components/inputs/textarea/index.js +6 -9
- package/dist/components/navigation/sidebar/index.d.ts +21 -44
- package/dist/components/navigation/sidebar/index.js +31 -42
- package/dist/components/navigation/tabs/index.d.ts +2 -8
- package/dist/components/navigation/tabs/index.js +10 -15
- package/dist/components/navigation/tabs-subtle/index.js +10 -13
- package/dist/components/overlays/alert-dialog/index.d.ts +16 -135
- package/dist/components/overlays/alert-dialog/index.js +13 -23
- package/dist/components/overlays/command/index.d.ts +20 -25
- package/dist/components/overlays/command/index.js +17 -25
- package/dist/components/overlays/context-menu/index.d.ts +17 -39
- package/dist/components/overlays/context-menu/index.js +18 -29
- package/dist/components/overlays/dialog/index.d.ts +16 -42
- package/dist/components/overlays/dialog/index.js +12 -20
- package/dist/components/overlays/dropdown/index.d.ts +34 -304
- package/dist/components/overlays/dropdown/index.js +17 -44
- package/dist/components/overlays/popover/index.d.ts +12 -38
- package/dist/components/overlays/popover/index.js +9 -13
- package/dist/components/overlays/preview-card/index.js +8 -12
- package/dist/components/overlays/sheet/index.d.ts +8 -25
- package/dist/components/overlays/sheet/index.js +13 -19
- package/dist/components/overlays/toast/index.d.ts +50 -209
- package/dist/components/overlays/toast/index.js +10 -23
- package/dist/components/overlays/tooltip/index.d.ts +21 -123
- package/dist/components/overlays/tooltip/index.js +6 -16
- package/dist/components/table/index.d.ts +8 -63
- package/dist/components/table/index.js +9 -20
- package/dist/index.d.ts +1048 -2999
- package/dist/index.js +82 -231
- package/dist/preset.d.ts +0 -22
- package/dist/preset.js +2 -24
- package/dist/primitives/code/index.js +5 -8
- package/dist/primitives/craft/index.js +6 -10
- package/dist/primitives/fluid-hover/index.js +8 -10
- package/dist/primitives/morph/index.js +10 -16
- package/dist/primitives/motion/index.js +8 -11
- package/dist/primitives/scroll-area/index.js +3 -5
- package/dist/primitives/sizes/index.js +4 -6
- package/dist/primitives/surface/index.js +4 -6
- package/dist/shared/{components-c7rwx0ap.js → components-0q0sqfh4.js} +49 -27
- package/dist/shared/components-0sn7ypjw.js +61 -0
- package/dist/shared/{components-vc864c51.js → components-11da9ajg.js} +5 -8
- package/dist/shared/components-1434p1mh.js +123 -0
- package/dist/shared/{components-c74gf16d.js → components-1z6tdadk.js} +24 -40
- package/dist/shared/{components-bae8sbjx.js → components-27nnvrwe.js} +14 -15
- package/dist/shared/{components-qh1cvj6p.js → components-2dxkv6zm.js} +7 -8
- package/dist/shared/{components-y0kfy2g4.js → components-2ecg8svx.js} +152 -234
- package/dist/shared/{components-c5w5zk0s.js → components-2ph6t61s.js} +34 -37
- package/dist/shared/{components-4j3t39dd.js → components-39jq9va1.js} +24 -29
- package/dist/shared/components-3ky44908.js +9 -0
- package/dist/shared/{components-syynwm1r.js → components-3mkepka5.js} +25 -28
- package/dist/shared/{components-wgtjt2af.js → components-4700p9kz.js} +22 -24
- package/dist/shared/{components-yhgq0qxx.js → components-59efxjd9.js} +12 -13
- package/dist/shared/{components-91zw3d9f.js → components-5vgpnxgy.js} +27 -51
- package/dist/shared/{components-kz5q11pp.js → components-5xc2j8jz.js} +18 -19
- package/dist/shared/{components-zkk3fmw8.js → components-6p8tfcd6.js} +29 -57
- package/dist/shared/{components-4nck8acq.js → components-7r3etddy.js} +54 -19
- package/dist/shared/{components-svkt577j.js → components-8snj490h.js} +32 -78
- package/dist/shared/{components-ctqmge8t.js → components-b0eac83j.js} +30 -9
- package/dist/shared/{components-9sttq78t.js → components-b2wyq2cq.js} +59 -41
- package/dist/shared/{components-pv1dyrtn.js → components-b3c5et5b.js} +38 -49
- package/dist/shared/{components-rrbpmfx4.js → components-c6s6b43g.js} +2 -3
- package/dist/shared/{components-pwayrkw9.js → components-chacnk61.js} +19 -19
- package/dist/shared/{components-71nbt03m.js → components-cv16mryv.js} +7 -26
- package/dist/shared/{components-hgv6tygd.js → components-d6xtjht3.js} +30 -35
- package/dist/shared/{components-z8fedth5.js → components-dk0gjr5a.js} +18 -19
- package/dist/shared/{components-cet3jpa0.js → components-fzrq6tr3.js} +34 -37
- package/dist/shared/{components-cnead5qm.js → components-gc5vtq2f.js} +30 -66
- package/dist/shared/components-gf5s6ap9.js +42 -0
- package/dist/shared/{components-0h78g3b5.js → components-gz8zf3ec.js} +55 -56
- package/dist/shared/{components-mattt5jp.js → components-h2ajmhwn.js} +35 -107
- package/dist/shared/{components-m06yq8wk.js → components-hqeq3w9q.js} +23 -38
- package/dist/shared/{components-7qx17na9.js → components-hw9yk32z.js} +24 -36
- package/dist/shared/components-hyq23phv.js +310 -0
- package/dist/shared/{components-9jmjnkqd.js → components-jbbmdr74.js} +22 -41
- package/dist/shared/{components-dcc02hs2.js → components-jjw69c4c.js} +4 -7
- package/dist/shared/{components-303jf96s.js → components-jthe1pwb.js} +420 -139
- package/dist/shared/{components-dtca4s6b.js → components-jwst3vtj.js} +76 -224
- package/dist/shared/{components-dc5q749x.js → components-kvr7f9hy.js} +10 -19
- package/dist/shared/{components-s8yqk2dc.js → components-kwa6mrf9.js} +11 -46
- package/dist/shared/{components-de99ykt7.js → components-men63rwj.js} +2 -3
- package/dist/shared/{components-qx27excf.js → components-mjthm1kf.js} +9 -13
- package/dist/shared/{components-1e64t8aj.js → components-n42c79bz.js} +22 -23
- package/dist/shared/{components-tz1fv35x.js → components-n7mjr357.js} +2 -3
- package/dist/shared/components-ng72k97e.js +128 -0
- package/dist/shared/{components-w2jqa6hx.js → components-pbmqctfa.js} +65 -169
- package/dist/shared/{components-px2zm7dc.js → components-q5xj5c16.js} +8 -9
- package/dist/shared/components-raknhppb.js +64 -0
- package/dist/shared/{components-2a7hcand.js → components-rr7a6ja9.js} +27 -59
- package/dist/shared/{components-005jt2nz.js → components-s083f38z.js} +34 -164
- package/dist/shared/components-s5ngg32c.js +267 -0
- package/dist/shared/components-sn1rxx1d.js +143 -0
- package/dist/shared/{components-r8eqs7gz.js → components-ta7shw28.js} +4 -4
- package/dist/shared/{components-9as3rf68.js → components-vc7vq5nz.js} +3 -6
- package/dist/shared/{components-ft3zdc79.js → components-vfbhxf5r.js} +5 -6
- package/dist/shared/components-vynjtrk6.js +19 -0
- package/dist/shared/{components-p0fm0bab.js → components-w1r67ag9.js} +149 -294
- package/dist/shared/{components-xd3emmy4.js → components-w90endve.js} +72 -71
- package/dist/shared/{components-t5pk5nmc.js → components-wvp1m52g.js} +102 -149
- package/dist/shared/components-wy9jpqny.js +146 -0
- package/dist/shared/{components-yxmxn2jp.js → components-y9y0g8er.js} +28 -52
- package/dist/shared/components-ypp01w3p.js +986 -0
- package/dist/shared/{components-czdvj1a8.js → components-zfnmn5d4.js} +36 -82
- package/dist/shared/{components-jqpefjsn.js → components-zrkzcayz.js} +6 -54
- package/dist/shared/{components-vf45j12c.js → components-zx6b6yby.js} +18 -4
- package/dist/styles.css +32 -320
- package/dist/tailwind.css +0 -16
- package/dist/tokens.css +0 -67
- package/package.json +4 -227
- package/DESIGN.md +0 -623
- package/blocks/sidebar-app/examples.mdx +0 -33
- package/blocks/sidebar-app/index.tsx +0 -218
- package/blocks/sidebar-app/inset-topbar.tsx +0 -25
- package/blocks/sidebar-app/menu-grid.ts +0 -26
- package/blocks/sidebar-app/nav-data.ts +0 -36
- package/blocks/sidebar-app/search-field.tsx +0 -48
- package/blocks/sidebar-app/user-footer.tsx +0 -67
- package/blocks/sidebar-app/workspace-header.tsx +0 -129
- package/components/AI/input-message/examples.mdx +0 -130
- package/components/AI/input-message/file-thumbnail.tsx +0 -173
- package/components/AI/input-message/index.tsx +0 -1385
- package/components/AI/thinking-indicator/examples.mdx +0 -27
- package/components/AI/thinking-indicator/index.tsx +0 -210
- package/components/accordion/examples.mdx +0 -233
- package/components/accordion/index.tsx +0 -1071
- package/components/avatar/examples.mdx +0 -95
- package/components/avatar/index.tsx +0 -129
- package/components/badge/change-demo.tsx +0 -29
- package/components/badge/examples.mdx +0 -124
- package/components/badge/index.tsx +0 -225
- package/components/badge/variants.ts +0 -73
- package/components/button/examples.mdx +0 -168
- package/components/button/index.tsx +0 -424
- package/components/button/loading-demo.tsx +0 -25
- package/components/button/variants.ts +0 -168
- package/components/card/examples.mdx +0 -251
- package/components/card/index.tsx +0 -1070
- package/components/copy/examples.mdx +0 -125
- package/components/copy/index.tsx +0 -662
- package/components/inputs/checkbox/examples.mdx +0 -87
- package/components/inputs/checkbox/index.tsx +0 -611
- package/components/inputs/checkbox/select-all-demo.tsx +0 -48
- package/components/inputs/color-picker/examples.mdx +0 -111
- package/components/inputs/color-picker/index.tsx +0 -1981
- package/components/inputs/color-picker/variants.ts +0 -401
- package/components/inputs/combobox/examples.mdx +0 -366
- package/components/inputs/combobox/index.tsx +0 -1527
- package/components/inputs/combobox/remote-demo.tsx +0 -105
- package/components/inputs/date-time/calendar/examples.mdx +0 -45
- package/components/inputs/date-time/calendar/index.tsx +0 -370
- package/components/inputs/date-time/calendar/use-month-window.ts +0 -173
- package/components/inputs/date-time/date-picker/examples.mdx +0 -243
- package/components/inputs/date-time/date-picker/index.tsx +0 -476
- package/components/inputs/date-time/date-picker/masked-date-input.tsx +0 -348
- package/components/inputs/date-time/date-range-picker/examples.mdx +0 -93
- package/components/inputs/date-time/date-range-picker/index.tsx +0 -525
- package/components/inputs/date-time/duration-picker/examples.mdx +0 -40
- package/components/inputs/date-time/duration-picker/index.tsx +0 -253
- package/components/inputs/date-time/month-picker/examples.mdx +0 -45
- package/components/inputs/date-time/month-picker/index.tsx +0 -254
- package/components/inputs/date-time/picker-shell.tsx +0 -581
- package/components/inputs/date-time/time-columns.tsx +0 -94
- package/components/inputs/date-time/time-picker/examples.mdx +0 -57
- package/components/inputs/date-time/time-picker/index.tsx +0 -227
- package/components/inputs/field/examples.mdx +0 -117
- package/components/inputs/field/index.tsx +0 -268
- package/components/inputs/file-dropzone/examples.mdx +0 -83
- package/components/inputs/file-dropzone/file-row.tsx +0 -201
- package/components/inputs/file-dropzone/index.tsx +0 -316
- package/components/inputs/file-dropzone/upload-demo.tsx +0 -67
- package/components/inputs/file-dropzone/variants.ts +0 -94
- package/components/inputs/input/examples.mdx +0 -166
- package/components/inputs/input/index.tsx +0 -494
- package/components/inputs/input/password-demo.tsx +0 -30
- package/components/inputs/label/examples.mdx +0 -67
- package/components/inputs/label/index.tsx +0 -85
- package/components/inputs/number-input/examples.mdx +0 -69
- package/components/inputs/number-input/index.tsx +0 -221
- package/components/inputs/radio/examples.mdx +0 -77
- package/components/inputs/radio/index.tsx +0 -636
- package/components/inputs/search/examples.mdx +0 -74
- package/components/inputs/search/index.tsx +0 -333
- package/components/inputs/select/examples.mdx +0 -296
- package/components/inputs/select/index.tsx +0 -1044
- package/components/inputs/select/variants.ts +0 -32
- package/components/inputs/slider/examples.mdx +0 -153
- package/components/inputs/slider/index.tsx +0 -1867
- package/components/inputs/switch/examples.mdx +0 -96
- package/components/inputs/switch/index.tsx +0 -504
- package/components/inputs/textarea/examples.mdx +0 -126
- package/components/inputs/textarea/index.tsx +0 -207
- package/components/navigation/navigation-menu/examples.mdx +0 -136
- package/components/navigation/navigation-menu/index.tsx +0 -704
- package/components/navigation/pagination/examples.mdx +0 -74
- package/components/navigation/pagination/index.tsx +0 -401
- package/components/navigation/sidebar/examples.mdx +0 -325
- package/components/navigation/sidebar/index.tsx +0 -453
- package/components/navigation/sidebar/sidebar-core.tsx +0 -1408
- package/components/navigation/sidebar/sidebar-menu.tsx +0 -1495
- package/components/navigation/sidebar/sidebar.css +0 -74
- package/components/navigation/sidebar/variants.ts +0 -55
- package/components/navigation/tabs/examples.mdx +0 -89
- package/components/navigation/tabs/index.tsx +0 -494
- package/components/navigation/tabs-subtle/examples.mdx +0 -49
- package/components/navigation/tabs-subtle/index.tsx +0 -512
- package/components/overlays/alert-dialog/examples.mdx +0 -123
- package/components/overlays/alert-dialog/index.tsx +0 -276
- package/components/overlays/alert-dialog/pending-demo.tsx +0 -36
- package/components/overlays/command/examples.mdx +0 -113
- package/components/overlays/command/index.tsx +0 -425
- package/components/overlays/context-menu/examples.mdx +0 -56
- package/components/overlays/context-menu/index.tsx +0 -279
- package/components/overlays/dialog/examples.mdx +0 -165
- package/components/overlays/dialog/index.tsx +0 -451
- package/components/overlays/dropdown/dropdown-search.tsx +0 -395
- package/components/overlays/dropdown/examples.mdx +0 -444
- package/components/overlays/dropdown/index.tsx +0 -1241
- package/components/overlays/dropdown/menu-item.tsx +0 -510
- package/components/overlays/dropdown/menu-parts.tsx +0 -725
- package/components/overlays/popover/anchored-demo.tsx +0 -59
- package/components/overlays/popover/examples.mdx +0 -203
- package/components/overlays/popover/index.tsx +0 -317
- package/components/overlays/preview-card/examples.mdx +0 -61
- package/components/overlays/preview-card/index.tsx +0 -204
- package/components/overlays/sheet/examples.mdx +0 -101
- package/components/overlays/sheet/index.tsx +0 -307
- package/components/overlays/toast/examples.mdx +0 -85
- package/components/overlays/toast/global-demo.tsx +0 -72
- package/components/overlays/toast/index.tsx +0 -544
- package/components/overlays/toast/manager.ts +0 -193
- package/components/overlays/tooltip/examples.mdx +0 -106
- package/components/overlays/tooltip/index.tsx +0 -539
- package/components/progress/examples.mdx +0 -59
- package/components/progress/index.tsx +0 -174
- package/components/progress/progress.css +0 -49
- package/components/progress/step-demo.tsx +0 -27
- package/components/table/examples.mdx +0 -188
- package/components/table/index.tsx +0 -535
- package/components/table/sort-demo.tsx +0 -61
- package/dist/components/avatar/index.d.ts +0 -46
- package/dist/components/avatar/index.js +0 -19
- package/dist/components/badge/variants.d.ts +0 -33
- package/dist/components/badge/variants.js +0 -10
- package/dist/components/button/variants.d.ts +0 -39
- package/dist/components/button/variants.js +0 -8
- package/dist/components/inputs/color-picker/variants.d.ts +0 -61
- package/dist/components/inputs/color-picker/variants.js +0 -28
- package/dist/components/inputs/field/index.d.ts +0 -116
- package/dist/components/inputs/field/index.js +0 -29
- package/dist/components/inputs/file-dropzone/index.d.ts +0 -167
- package/dist/components/inputs/file-dropzone/index.js +0 -33
- package/dist/components/inputs/file-dropzone/variants.d.ts +0 -35
- package/dist/components/inputs/file-dropzone/variants.js +0 -12
- package/dist/components/inputs/label/index.d.ts +0 -32
- package/dist/components/inputs/label/index.js +0 -11
- package/dist/components/inputs/number-input/index.d.ts +0 -87
- package/dist/components/inputs/number-input/index.js +0 -21
- package/dist/components/inputs/select/variants.d.ts +0 -5
- package/dist/components/inputs/select/variants.js +0 -6
- package/dist/components/navigation/navigation-menu/index.d.ts +0 -126
- package/dist/components/navigation/navigation-menu/index.js +0 -41
- package/dist/components/navigation/pagination/index.d.ts +0 -75
- package/dist/components/navigation/pagination/index.js +0 -41
- package/dist/components/navigation/sidebar/variants.d.ts +0 -25
- package/dist/components/navigation/sidebar/variants.js +0 -28
- package/dist/components/progress/index.d.ts +0 -56
- package/dist/components/progress/index.js +0 -15
- package/dist/docs/index.d.ts +0 -83
- package/dist/docs/index.js +0 -288
- package/dist/docs/vite.d.ts +0 -31
- package/dist/docs/vite.js +0 -45
- package/dist/lib/date-locale.d.ts +0 -26
- package/dist/lib/date-locale.js +0 -24
- package/dist/lib/field-classes.d.ts +0 -149
- package/dist/lib/field-classes.js +0 -41
- package/dist/lib/field-variants.d.ts +0 -47
- package/dist/lib/field-variants.js +0 -26
- package/dist/lib/fluid-hover-highlight.d.ts +0 -112
- package/dist/lib/fluid-hover-highlight.js +0 -17
- package/dist/lib/focus-ring.d.ts +0 -18
- package/dist/lib/focus-ring.js +0 -11
- package/dist/lib/goo-indicator.d.ts +0 -46
- package/dist/lib/goo-indicator.js +0 -14
- package/dist/lib/icon-context.d.ts +0 -30
- package/dist/lib/icon-context.js +0 -12
- package/dist/lib/inert.d.ts +0 -9
- package/dist/lib/inert.js +0 -8
- package/dist/lib/morph-layers.d.ts +0 -93
- package/dist/lib/morph-layers.js +0 -13
- package/dist/lib/morph-part.d.ts +0 -21
- package/dist/lib/morph-part.js +0 -12
- package/dist/lib/popup.d.ts +0 -20
- package/dist/lib/popup.js +0 -12
- package/dist/lib/reduced-motion.d.ts +0 -9
- package/dist/lib/reduced-motion.js +0 -6
- package/dist/lib/row-index.d.ts +0 -10
- package/dist/lib/row-index.js +0 -8
- package/dist/lib/row-order.d.ts +0 -28
- package/dist/lib/row-order.js +0 -10
- package/dist/lib/scroll-column.d.ts +0 -31
- package/dist/lib/scroll-column.js +0 -19
- package/dist/lib/shape-context.d.ts +0 -30
- package/dist/lib/shape-context.js +0 -13
- package/dist/lib/shape-radius.d.ts +0 -19
- package/dist/lib/shape-radius.js +0 -6
- package/dist/lib/size-context.d.ts +0 -94
- package/dist/lib/size-context.js +0 -20
- package/dist/lib/size-scale.d.ts +0 -79
- package/dist/lib/size-scale.js +0 -8
- package/dist/lib/slot.d.ts +0 -39
- package/dist/lib/slot.js +0 -14
- package/dist/lib/springs.d.ts +0 -44
- package/dist/lib/springs.js +0 -8
- package/dist/lib/surface-classes.d.ts +0 -19
- package/dist/lib/surface-classes.js +0 -10
- package/dist/lib/surface-context.d.ts +0 -9
- package/dist/lib/surface-context.js +0 -8
- package/dist/lib/use-controllable-state.d.ts +0 -2
- package/dist/lib/use-controllable-state.js +0 -6
- package/dist/lib/use-fluid-hover.d.ts +0 -138
- package/dist/lib/use-fluid-hover.js +0 -14
- package/dist/lib/use-is-mobile.d.ts +0 -2
- package/dist/lib/use-is-mobile.js +0 -6
- package/dist/lib/use-keyboard-nav-gate.d.ts +0 -20
- package/dist/lib/use-keyboard-nav-gate.js +0 -7
- package/dist/lib/use-merge-split.d.ts +0 -47
- package/dist/lib/use-merge-split.js +0 -15
- package/dist/lib/use-morph.d.ts +0 -101
- package/dist/lib/use-morph.js +0 -20
- package/dist/lib/use-touch-primary.d.ts +0 -2
- package/dist/lib/use-touch-primary.js +0 -6
- package/dist/lib/utils.d.ts +0 -3
- package/dist/lib/utils.js +0 -6
- package/dist/primitives/separator/index.d.ts +0 -25
- package/dist/primitives/separator/index.js +0 -9
- package/dist/primitives/skeleton/index.d.ts +0 -35
- package/dist/primitives/skeleton/index.js +0 -13
- package/dist/shared/components-01w9qcfa.js +0 -277
- package/dist/shared/components-124epb7f.js +0 -53
- package/dist/shared/components-26snnbs9.js +0 -322
- package/dist/shared/components-2krsc300.js +0 -254
- package/dist/shared/components-2qbqqpgq.js +0 -1545
- package/dist/shared/components-34txk9rj.js +0 -35
- package/dist/shared/components-3h92d94m.js +0 -23
- package/dist/shared/components-4vfm04wq.js +0 -82
- package/dist/shared/components-54nq988h.js +0 -123
- package/dist/shared/components-7s7pykk5.js +0 -281
- package/dist/shared/components-868v7scv.js +0 -399
- package/dist/shared/components-8jrk5eye.js +0 -448
- package/dist/shared/components-adkarj8n.js +0 -35
- package/dist/shared/components-b1e239g3.js +0 -28
- package/dist/shared/components-b94t6qtj.js +0 -143
- package/dist/shared/components-bsqynhaj.js +0 -179
- package/dist/shared/components-c9yr4ed1.js +0 -44
- package/dist/shared/components-d3shbwqq.js +0 -95
- package/dist/shared/components-dhgk6mbt.js +0 -16
- package/dist/shared/components-drxrtnp2.js +0 -25
- package/dist/shared/components-f7h57m6t.js +0 -59
- package/dist/shared/components-g3eafs70.js +0 -337
- package/dist/shared/components-gx4h5rbb.js +0 -40
- package/dist/shared/components-gyt7my41.js +0 -328
- package/dist/shared/components-h6p86be3.js +0 -19
- package/dist/shared/components-h83az0bz.js +0 -33
- package/dist/shared/components-hdzrqqpx.js +0 -55
- package/dist/shared/components-krtx3m5b.js +0 -84
- package/dist/shared/components-kswxnm3h.js +0 -24
- package/dist/shared/components-mhza2wdn.js +0 -482
- package/dist/shared/components-mrbaxt8q.js +0 -62
- package/dist/shared/components-pt5ftm4p.js +0 -38
- package/dist/shared/components-rtadhd9j.js +0 -64
- package/dist/shared/components-tv7b8tet.js +0 -271
- package/dist/shared/components-tz4gaf1v.js +0 -23
- package/dist/shared/components-wbbckbw8.js +0 -34
- package/dist/shared/components-wknae3m4.js +0 -298
- package/dist/shared/components-xdqmt6ed.js +0 -20
- package/dist/shared/components-xjkdvnbg.js +0 -107
- package/dist/shared/components-z3v0n0de.js +0 -11
- package/dist/tokens/colors.css +0 -230
- package/dist/tokens/motion.css +0 -40
- package/dist/tokens/radius.css +0 -10
- package/dist/tokens/spacing.css +0 -9
- package/dist/tokens/typography.css +0 -119
- package/docs/chrome.css +0 -120
- package/docs/chrome.tsx +0 -283
- package/docs/index.ts +0 -9
- package/docs/vite.ts +0 -85
- package/index.ts +0 -60
- package/lib/date-locale.ts +0 -195
- package/lib/field-classes.ts +0 -121
- package/lib/field-variants.ts +0 -117
- package/lib/fluid-hover-highlight.tsx +0 -139
- package/lib/focus-ring.tsx +0 -46
- package/lib/goo-indicator.tsx +0 -261
- package/lib/icon-context.tsx +0 -310
- package/lib/inert.ts +0 -22
- package/lib/morph-layers.tsx +0 -102
- package/lib/morph-part.tsx +0 -49
- package/lib/popup.ts +0 -52
- package/lib/reduced-motion.ts +0 -19
- package/lib/row-index.tsx +0 -29
- package/lib/row-order.tsx +0 -126
- package/lib/scroll-column.tsx +0 -273
- package/lib/shape-context.tsx +0 -138
- package/lib/shape-radius.ts +0 -67
- package/lib/size-context.tsx +0 -66
- package/lib/size-scale.ts +0 -115
- package/lib/slot.ts +0 -115
- package/lib/springs.ts +0 -62
- package/lib/surface-classes.ts +0 -58
- package/lib/surface-context.tsx +0 -21
- package/lib/use-controllable-state.ts +0 -44
- package/lib/use-fluid-hover.ts +0 -541
- package/lib/use-is-mobile.ts +0 -25
- package/lib/use-keyboard-nav-gate.ts +0 -45
- package/lib/use-merge-split.tsx +0 -209
- package/lib/use-morph.ts +0 -575
- package/lib/use-touch-primary.tsx +0 -43
- package/lib/utils.ts +0 -37
- package/preset.ts +0 -199
- package/primitives/code/examples.mdx +0 -27
- package/primitives/code/index.tsx +0 -80
- package/primitives/craft/demos.tsx +0 -173
- package/primitives/craft/examples.mdx +0 -142
- package/primitives/craft/index.tsx +0 -76
- package/primitives/fluid-hover/examples.mdx +0 -84
- package/primitives/fluid-hover/index.tsx +0 -316
- package/primitives/morph/examples.mdx +0 -139
- package/primitives/morph/index.tsx +0 -192
- package/primitives/motion/examples.mdx +0 -100
- package/primitives/motion/index.tsx +0 -160
- package/primitives/scroll-area/examples.mdx +0 -58
- package/primitives/scroll-area/index.tsx +0 -204
- package/primitives/separator/examples.mdx +0 -30
- package/primitives/separator/index.tsx +0 -53
- package/primitives/sizes/examples.mdx +0 -167
- package/primitives/sizes/index.tsx +0 -89
- package/primitives/skeleton/examples.mdx +0 -51
- package/primitives/skeleton/index.tsx +0 -72
- package/primitives/skeleton/skeleton.css +0 -15
- package/primitives/surface/examples.mdx +0 -173
- package/primitives/surface/index.tsx +0 -109
- package/primitives/surface/surface.css +0 -176
- /package/dist/shared/{components-3ey0hzpb.js → components-1v7rxytx.js} +0 -0
- /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
|