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