@justanarthur/modo-ui 0.5.0 → 0.6.0-canary.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/DESIGN.md +608 -0
- package/README.md +61 -39
- package/blocks/sidebar-app/examples.mdx +33 -0
- package/blocks/sidebar-app/index.tsx +218 -0
- package/blocks/sidebar-app/inset-topbar.tsx +25 -0
- package/blocks/sidebar-app/menu-grid.ts +26 -0
- package/blocks/sidebar-app/nav-data.ts +36 -0
- package/blocks/sidebar-app/search-field.tsx +48 -0
- package/blocks/sidebar-app/user-footer.tsx +67 -0
- package/blocks/sidebar-app/workspace-header.tsx +129 -0
- package/components/AI/input-message/examples.mdx +124 -0
- package/components/AI/input-message/file-thumbnail.tsx +173 -0
- package/components/AI/input-message/index.tsx +1383 -0
- package/components/AI/thinking-indicator/examples.mdx +27 -0
- package/components/AI/thinking-indicator/index.tsx +210 -0
- package/components/accordion/examples.mdx +148 -0
- package/components/accordion/index.tsx +1034 -0
- package/components/avatar/examples.mdx +95 -0
- package/components/avatar/index.tsx +129 -0
- package/components/badge/change-demo.tsx +29 -0
- package/components/badge/examples.mdx +120 -0
- package/components/badge/index.tsx +225 -0
- package/components/badge/variants.ts +73 -0
- package/components/button/examples.mdx +164 -0
- package/components/button/index.tsx +416 -0
- package/components/button/loading-demo.tsx +25 -0
- package/components/button/variants.ts +166 -0
- package/components/card/examples.mdx +212 -0
- package/components/card/index.tsx +1037 -0
- package/components/copy/examples.mdx +87 -0
- package/components/copy/index.tsx +482 -0
- package/components/inputs/checkbox/examples.mdx +83 -0
- package/components/inputs/checkbox/index.tsx +611 -0
- package/components/inputs/checkbox/select-all-demo.tsx +48 -0
- package/components/inputs/color-picker/examples.mdx +72 -0
- package/components/inputs/color-picker/index.tsx +1955 -0
- package/components/inputs/color-picker/variants.ts +401 -0
- package/components/inputs/combobox/examples.mdx +273 -0
- package/components/inputs/combobox/index.tsx +1436 -0
- package/components/inputs/combobox/remote-demo.tsx +101 -0
- package/components/inputs/date-time/calendar/examples.mdx +34 -0
- package/components/inputs/date-time/calendar/index.tsx +351 -0
- package/components/inputs/date-time/calendar/use-month-window.ts +163 -0
- package/components/inputs/date-time/date-picker/examples.mdx +166 -0
- package/components/inputs/date-time/date-picker/index.tsx +385 -0
- package/components/inputs/date-time/date-picker/masked-date-input.tsx +336 -0
- package/components/inputs/date-time/date-range-picker/examples.mdx +29 -0
- package/components/inputs/date-time/date-range-picker/index.tsx +217 -0
- package/components/inputs/date-time/duration-picker/examples.mdx +16 -0
- package/components/inputs/date-time/duration-picker/index.tsx +189 -0
- package/components/inputs/date-time/month-picker/examples.mdx +16 -0
- package/components/inputs/date-time/month-picker/index.tsx +190 -0
- package/components/inputs/date-time/picker-shell.tsx +395 -0
- package/components/inputs/date-time/time-columns.tsx +91 -0
- package/components/inputs/date-time/time-picker/examples.mdx +26 -0
- package/components/inputs/date-time/time-picker/index.tsx +166 -0
- package/components/inputs/field/examples.mdx +113 -0
- package/components/inputs/field/index.tsx +267 -0
- package/components/inputs/file-dropzone/examples.mdx +73 -0
- package/components/inputs/file-dropzone/file-row.tsx +201 -0
- package/components/inputs/file-dropzone/index.tsx +296 -0
- package/components/inputs/file-dropzone/upload-demo.tsx +67 -0
- package/components/inputs/file-dropzone/variants.ts +94 -0
- package/components/inputs/input/examples.mdx +135 -0
- package/components/inputs/input/index.tsx +494 -0
- package/components/inputs/input/password-demo.tsx +30 -0
- package/components/inputs/label/examples.mdx +67 -0
- package/components/inputs/label/index.tsx +85 -0
- package/components/inputs/number-input/examples.mdx +56 -0
- package/components/inputs/number-input/index.tsx +214 -0
- package/components/inputs/radio/examples.mdx +77 -0
- package/components/inputs/radio/index.tsx +636 -0
- package/components/inputs/search/examples.mdx +74 -0
- package/components/inputs/search/index.tsx +333 -0
- package/components/inputs/select/examples.mdx +218 -0
- package/components/inputs/select/index.tsx +862 -0
- package/components/inputs/select/variants.ts +32 -0
- package/components/inputs/slider/examples.mdx +127 -0
- package/components/inputs/slider/index.tsx +1725 -0
- package/components/inputs/switch/examples.mdx +94 -0
- package/components/inputs/switch/index.tsx +504 -0
- package/components/inputs/textarea/examples.mdx +88 -0
- package/components/inputs/textarea/index.tsx +161 -0
- package/components/navigation/navigation-menu/examples.mdx +109 -0
- package/components/navigation/navigation-menu/index.tsx +613 -0
- package/components/navigation/pagination/examples.mdx +64 -0
- package/components/navigation/pagination/index.tsx +395 -0
- package/components/navigation/sidebar/examples.mdx +319 -0
- package/components/navigation/sidebar/index.tsx +453 -0
- package/components/navigation/sidebar/sidebar-core.tsx +1408 -0
- package/components/navigation/sidebar/sidebar-menu.tsx +1495 -0
- package/components/navigation/sidebar/sidebar.css +74 -0
- package/components/navigation/sidebar/variants.ts +55 -0
- package/components/navigation/tabs/examples.mdx +89 -0
- package/components/navigation/tabs/index.tsx +494 -0
- package/components/navigation/tabs-subtle/examples.mdx +49 -0
- package/components/navigation/tabs-subtle/index.tsx +512 -0
- package/components/overlays/alert-dialog/examples.mdx +123 -0
- package/components/overlays/alert-dialog/index.tsx +258 -0
- package/components/overlays/alert-dialog/pending-demo.tsx +36 -0
- package/components/overlays/command/examples.mdx +113 -0
- package/components/overlays/command/index.tsx +416 -0
- package/components/overlays/context-menu/examples.mdx +56 -0
- package/components/overlays/context-menu/index.tsx +279 -0
- package/components/overlays/dialog/examples.mdx +144 -0
- package/components/overlays/dialog/index.tsx +366 -0
- package/components/overlays/dropdown/dropdown-search.tsx +395 -0
- package/components/overlays/dropdown/examples.mdx +294 -0
- package/components/overlays/dropdown/index.tsx +1168 -0
- package/components/overlays/dropdown/menu-item.tsx +464 -0
- package/components/overlays/dropdown/menu-parts.tsx +660 -0
- package/components/overlays/popover/anchored-demo.tsx +59 -0
- package/components/overlays/popover/examples.mdx +180 -0
- package/components/overlays/popover/index.tsx +315 -0
- package/components/overlays/preview-card/examples.mdx +61 -0
- package/components/overlays/preview-card/index.tsx +204 -0
- package/components/overlays/sheet/examples.mdx +89 -0
- package/components/overlays/sheet/index.tsx +247 -0
- package/components/overlays/toast/examples.mdx +68 -0
- package/components/overlays/toast/global-demo.tsx +72 -0
- package/components/overlays/toast/index.tsx +474 -0
- package/components/overlays/toast/manager.ts +193 -0
- package/components/overlays/tooltip/examples.mdx +106 -0
- package/components/overlays/tooltip/index.tsx +519 -0
- package/components/progress/examples.mdx +59 -0
- package/components/progress/index.tsx +174 -0
- package/components/progress/progress.css +49 -0
- package/components/progress/step-demo.tsx +27 -0
- package/components/table/examples.mdx +184 -0
- package/components/table/index.tsx +510 -0
- package/components/table/sort-demo.tsx +61 -0
- package/dist/base.css +100 -0
- package/dist/blocks/sidebar-app/index.js +39 -26
- package/dist/components/AI/input-message/index.js +22 -16
- package/dist/components/AI/thinking-indicator/index.js +9 -6
- package/dist/components/accordion/index.js +16 -13
- package/dist/components/avatar/index.d.ts +46 -0
- package/dist/components/avatar/index.js +18 -0
- package/dist/components/badge/index.d.ts +18 -11
- package/dist/components/badge/index.js +11 -7
- package/dist/components/badge/variants.d.ts +33 -0
- package/dist/components/badge/variants.js +10 -0
- package/dist/components/button/index.d.ts +53 -17
- package/dist/components/button/index.js +10 -6
- package/dist/components/button/variants.d.ts +39 -0
- package/dist/components/button/variants.js +8 -0
- package/dist/components/card/index.d.ts +16 -3
- package/dist/components/card/index.js +16 -11
- package/dist/components/copy/index.js +13 -9
- package/dist/components/inputs/checkbox/index.d.ts +28 -9
- package/dist/components/inputs/checkbox/index.js +17 -13
- package/dist/components/inputs/color-picker/index.d.ts +13 -13
- package/dist/components/inputs/color-picker/index.js +25 -18
- package/dist/components/inputs/color-picker/variants.d.ts +61 -0
- package/dist/components/inputs/color-picker/variants.js +28 -0
- package/dist/components/inputs/combobox/index.d.ts +60 -17
- package/dist/components/inputs/combobox/index.js +26 -18
- package/dist/components/inputs/date-time/calendar/index.js +14 -11
- package/dist/components/inputs/date-time/date-picker/index.d.ts +53 -11
- package/dist/components/inputs/date-time/date-picker/index.js +33 -25
- package/dist/components/inputs/date-time/date-range-picker/index.d.ts +3 -3
- package/dist/components/inputs/date-time/date-range-picker/index.js +32 -24
- package/dist/components/inputs/date-time/duration-picker/index.d.ts +3 -3
- package/dist/components/inputs/date-time/duration-picker/index.js +32 -24
- package/dist/components/inputs/date-time/month-picker/index.d.ts +3 -3
- package/dist/components/inputs/date-time/month-picker/index.js +31 -23
- package/dist/components/inputs/date-time/time-picker/index.d.ts +3 -3
- package/dist/components/inputs/date-time/time-picker/index.js +32 -24
- package/dist/components/inputs/field/index.d.ts +116 -0
- package/dist/components/inputs/field/index.js +28 -0
- package/dist/components/inputs/file-dropzone/index.d.ts +165 -0
- package/dist/components/inputs/file-dropzone/index.js +32 -0
- package/dist/components/inputs/file-dropzone/variants.d.ts +35 -0
- package/dist/components/inputs/file-dropzone/variants.js +12 -0
- package/dist/components/inputs/input/index.d.ts +95 -13
- package/dist/components/inputs/input/index.js +17 -9
- package/dist/components/inputs/label/index.d.ts +32 -0
- package/dist/components/inputs/label/index.js +11 -0
- package/dist/components/inputs/number-input/index.d.ts +84 -0
- package/dist/components/inputs/number-input/index.js +20 -0
- package/dist/components/inputs/radio/index.d.ts +20 -5
- package/dist/components/inputs/radio/index.js +15 -12
- package/dist/components/inputs/search/index.js +25 -18
- package/dist/components/inputs/select/index.d.ts +41 -11
- package/dist/components/inputs/select/index.js +27 -14
- package/dist/components/inputs/select/variants.d.ts +5 -0
- package/dist/components/inputs/select/variants.js +6 -0
- package/dist/components/inputs/slider/index.js +7 -5
- package/dist/components/inputs/switch/index.d.ts +8 -2
- package/dist/components/inputs/switch/index.js +14 -12
- package/dist/components/inputs/textarea/index.d.ts +4 -0
- package/dist/components/inputs/textarea/index.js +8 -6
- package/dist/components/navigation/navigation-menu/index.d.ts +114 -0
- package/dist/components/navigation/navigation-menu/index.js +40 -0
- package/dist/components/navigation/pagination/index.d.ts +73 -0
- package/dist/components/navigation/pagination/index.js +39 -0
- package/dist/components/navigation/sidebar/index.d.ts +44 -21
- package/dist/components/navigation/sidebar/index.js +41 -31
- package/dist/components/navigation/sidebar/variants.d.ts +25 -0
- package/dist/components/navigation/sidebar/variants.js +28 -0
- package/dist/components/navigation/tabs/index.d.ts +8 -2
- package/dist/components/navigation/tabs/index.js +14 -10
- package/dist/components/navigation/tabs-subtle/index.js +12 -10
- package/dist/components/overlays/alert-dialog/index.d.ts +121 -15
- package/dist/components/overlays/alert-dialog/index.js +22 -13
- package/dist/components/overlays/command/index.js +24 -17
- package/dist/components/overlays/context-menu/index.d.ts +33 -17
- package/dist/components/overlays/context-menu/index.js +27 -18
- package/dist/components/overlays/dialog/index.js +17 -12
- package/dist/components/overlays/dropdown/index.d.ts +257 -31
- package/dist/components/overlays/dropdown/index.js +42 -17
- package/dist/components/overlays/popover/index.d.ts +31 -9
- package/dist/components/overlays/popover/index.js +12 -9
- package/dist/components/overlays/preview-card/index.js +11 -8
- package/dist/components/overlays/sheet/index.js +18 -13
- package/dist/components/overlays/toast/index.d.ts +123 -47
- package/dist/components/overlays/toast/index.js +18 -10
- package/dist/components/overlays/tooltip/index.d.ts +113 -21
- package/dist/components/overlays/tooltip/index.js +15 -6
- package/dist/components/progress/index.d.ts +56 -0
- package/dist/components/progress/index.js +15 -0
- package/dist/components/table/index.d.ts +63 -8
- package/dist/components/table/index.js +19 -9
- package/dist/index.d.ts +2201 -736
- package/dist/index.js +223 -82
- package/dist/lib/date-locale.d.ts +14 -0
- package/dist/lib/date-locale.js +16 -0
- package/dist/lib/field-classes.d.ts +144 -0
- package/dist/lib/field-classes.js +40 -0
- package/dist/lib/field-variants.d.ts +47 -0
- package/dist/lib/field-variants.js +26 -0
- package/dist/lib/fluid-hover-highlight.d.ts +112 -0
- package/dist/lib/fluid-hover-highlight.js +17 -0
- package/dist/lib/focus-ring.d.ts +18 -0
- package/dist/lib/focus-ring.js +10 -0
- package/dist/lib/goo-indicator.d.ts +46 -0
- package/dist/lib/goo-indicator.js +14 -0
- package/dist/lib/icon-context.d.ts +30 -0
- package/dist/lib/icon-context.js +12 -0
- package/dist/lib/inert.d.ts +9 -0
- package/dist/lib/inert.js +8 -0
- package/dist/lib/morph-layers.d.ts +93 -0
- package/dist/lib/morph-layers.js +13 -0
- package/dist/lib/morph-part.d.ts +21 -0
- package/dist/lib/morph-part.js +12 -0
- package/dist/lib/popup.d.ts +20 -0
- package/dist/lib/popup.js +12 -0
- package/dist/lib/reduced-motion.d.ts +9 -0
- package/dist/lib/reduced-motion.js +6 -0
- package/dist/lib/row-index.d.ts +10 -0
- package/dist/lib/row-index.js +8 -0
- package/dist/lib/row-order.d.ts +28 -0
- package/dist/lib/row-order.js +10 -0
- package/dist/lib/scroll-column.d.ts +27 -0
- package/dist/lib/scroll-column.js +18 -0
- package/dist/lib/shape-context.d.ts +30 -0
- package/dist/lib/shape-context.js +12 -0
- package/dist/lib/size-context.d.ts +94 -0
- package/dist/lib/size-context.js +20 -0
- package/dist/lib/size-scale.d.ts +79 -0
- package/dist/lib/size-scale.js +8 -0
- package/dist/lib/slot.d.ts +12 -0
- package/dist/lib/slot.js +6 -0
- package/dist/lib/springs.d.ts +44 -0
- package/dist/lib/springs.js +8 -0
- package/dist/lib/surface-classes.d.ts +19 -0
- package/dist/lib/surface-classes.js +10 -0
- package/dist/lib/surface-context.d.ts +9 -0
- package/dist/lib/surface-context.js +8 -0
- package/dist/lib/use-controllable-state.d.ts +2 -0
- package/dist/lib/use-controllable-state.js +6 -0
- package/dist/lib/use-fluid-hover.d.ts +138 -0
- package/dist/lib/use-fluid-hover.js +14 -0
- package/dist/lib/use-is-mobile.d.ts +2 -0
- package/dist/lib/use-is-mobile.js +6 -0
- package/dist/lib/use-keyboard-nav-gate.d.ts +20 -0
- package/dist/lib/use-keyboard-nav-gate.js +7 -0
- package/dist/lib/use-merge-split.d.ts +47 -0
- package/dist/lib/use-merge-split.js +15 -0
- package/dist/lib/use-morph.d.ts +101 -0
- package/dist/lib/use-morph.js +20 -0
- package/dist/lib/use-touch-primary.d.ts +2 -0
- package/dist/lib/use-touch-primary.js +6 -0
- package/dist/lib/utils.d.ts +3 -0
- package/dist/lib/utils.js +6 -0
- package/dist/preset.d.ts +17 -0
- package/dist/preset.js +22 -2
- package/dist/primitives/code/index.js +7 -5
- package/dist/primitives/craft/index.js +9 -6
- package/dist/primitives/fluid-hover/index.js +9 -8
- package/dist/primitives/morph/index.js +15 -10
- package/dist/primitives/motion/index.js +10 -8
- package/dist/primitives/scroll-area/index.js +4 -3
- package/dist/primitives/separator/index.d.ts +25 -0
- package/dist/primitives/separator/index.js +9 -0
- package/dist/primitives/sizes/index.js +6 -4
- package/dist/primitives/skeleton/index.d.ts +35 -0
- package/dist/primitives/skeleton/index.js +12 -0
- package/dist/primitives/surface/index.js +6 -4
- package/dist/shared/{components-3mkepka5.js → components-0jmqhx2k.js} +28 -25
- package/dist/shared/components-124epb7f.js +53 -0
- package/dist/shared/{components-gz8zf3ec.js → components-1k0wv53d.js} +15 -43
- package/dist/shared/components-26snnbs9.js +322 -0
- package/dist/shared/{components-2dxkv6zm.js → components-2c0qcfx4.js} +8 -7
- package/dist/shared/{components-473g814w.js → components-2mt9te6d.js} +243 -137
- package/dist/shared/{components-hawvy2qz.js → components-2z48g4fp.js} +9 -30
- package/dist/shared/components-34txk9rj.js +35 -0
- package/dist/shared/components-3h92d94m.js +23 -0
- package/dist/shared/{components-zfnmn5d4.js → components-3sp623ae.js} +36 -31
- package/dist/shared/{components-jwst3vtj.js → components-3wxkjgqn.js} +186 -76
- package/dist/shared/{components-s2bz1xpk.js → components-49bsynbw.js} +8 -5
- package/dist/shared/components-4bwsd46w.js +71 -0
- package/dist/shared/components-4n4f5m3e.js +297 -0
- package/dist/shared/{components-0q0sqfh4.js → components-4y816hwp.js} +27 -49
- package/dist/shared/components-52ngsgca.js +399 -0
- package/dist/shared/{components-y9y0g8er.js → components-52r5763g.js} +22 -19
- package/dist/shared/components-559tf7yb.js +28 -0
- package/dist/shared/components-5mkga9hj.js +440 -0
- package/dist/shared/{components-kwa6mrf9.js → components-5s0nfqvw.js} +14 -9
- package/dist/shared/{components-dk0gjr5a.js → components-60atskhp.js} +19 -18
- package/dist/shared/{components-rr7a6ja9.js → components-658b26nb.js} +21 -18
- package/dist/shared/{components-xxvdw7k1.js → components-65vwfjs6.js} +37 -34
- package/dist/shared/{components-frw34b65.js → components-6j2fh74k.js} +114 -123
- package/dist/shared/components-6zg1yft7.js +167 -0
- package/dist/shared/components-6zng858q.js +53 -0
- package/dist/shared/components-72w4ge68.js +64 -0
- package/dist/shared/{components-cv16mryv.js → components-78x40gee.js} +13 -7
- package/dist/shared/{components-b2wyq2cq.js → components-7d4r7hh1.js} +39 -57
- package/dist/shared/components-7ntvvegr.js +316 -0
- package/dist/shared/components-8nwsqjn8.js +34 -0
- package/dist/shared/{components-5xc2j8jz.js → components-8tt3xbb5.js} +19 -18
- package/dist/shared/{components-vc7vq5nz.js → components-9as3rf68.js} +6 -3
- package/dist/shared/components-9mys9jna.js +255 -0
- package/dist/shared/components-adkarj8n.js +35 -0
- package/dist/shared/{components-b3c5et5b.js → components-ae9fncz4.js} +32 -31
- package/dist/shared/{components-hqeq3w9q.js → components-awra5sff.js} +24 -16
- package/dist/shared/components-b1e239g3.js +28 -0
- package/dist/shared/{components-q5xj5c16.js → components-b1f2bjf1.js} +9 -8
- package/dist/shared/{components-qmmjx1qw.js → components-bn42vq6d.js} +71 -72
- package/dist/shared/components-c96g82th.js +1444 -0
- package/dist/shared/components-c9yr4ed1.js +44 -0
- package/dist/shared/components-d3shbwqq.js +95 -0
- package/dist/shared/{components-jjw69c4c.js → components-dcc02hs2.js} +7 -4
- package/dist/shared/{components-men63rwj.js → components-de99ykt7.js} +3 -2
- package/dist/shared/components-dhgk6mbt.js +16 -0
- package/dist/shared/components-drxrtnp2.js +25 -0
- package/dist/shared/{components-6p8tfcd6.js → components-ds9fqat9.js} +21 -18
- package/dist/shared/{components-mjthm1kf.js → components-egy4tyjt.js} +11 -8
- package/dist/shared/components-f0xfpkhh.js +82 -0
- package/dist/shared/components-f7h57m6t.js +59 -0
- package/dist/shared/{components-d6xtjht3.js → components-fjkpbg6n.js} +33 -30
- package/dist/shared/{components-gc5vtq2f.js → components-gh0qeey3.js} +63 -29
- package/dist/shared/{components-39jq9va1.js → components-gvbapfen.js} +29 -24
- package/dist/shared/{components-5vgpnxgy.js → components-gzesg7hy.js} +19 -16
- package/dist/shared/components-h6p86be3.js +19 -0
- package/dist/shared/components-h83az0bz.js +33 -0
- package/dist/shared/components-jjrj6ymn.js +104 -0
- package/dist/shared/{components-wvp1m52g.js → components-jp5v0m0f.js} +15 -14
- package/dist/shared/{components-zrkzcayz.js → components-jqpefjsn.js} +54 -6
- package/dist/shared/{components-1z6tdadk.js → components-k74j4hwh.js} +20 -17
- package/dist/shared/components-kswxnm3h.js +24 -0
- package/dist/shared/{components-jbbmdr74.js → components-mkkbbgb7.js} +36 -17
- package/dist/shared/{components-pbmqctfa.js → components-mpw667rb.js} +169 -65
- package/dist/shared/{components-s5ngg32c.js → components-paa08427.js} +121 -47
- package/dist/shared/{components-vfbhxf5r.js → components-pd42emw3.js} +6 -5
- package/dist/shared/{components-hw9yk32z.js → components-pmfszv7a.js} +35 -23
- package/dist/shared/{components-chacnk61.js → components-pw06rbxq.js} +19 -19
- package/dist/shared/components-q03cktx8.js +246 -0
- package/dist/shared/{components-kvr7f9hy.js → components-qdm3yh26.js} +19 -10
- package/dist/shared/{components-27nnvrwe.js → components-qs4rkqsj.js} +15 -14
- package/dist/shared/{components-s083f38z.js → components-r6gbmkew.js} +22 -21
- package/dist/shared/{components-ta7shw28.js → components-r8eqs7gz.js} +4 -4
- package/dist/shared/{components-sn1rxx1d.js → components-rm02h9nt.js} +12 -11
- package/dist/shared/{components-c6s6b43g.js → components-rrbpmfx4.js} +3 -2
- package/dist/shared/{components-n7mjr357.js → components-tz1fv35x.js} +3 -2
- package/dist/shared/components-tz4gaf1v.js +23 -0
- package/dist/shared/{components-zx6b6yby.js → components-vf45j12c.js} +4 -18
- package/dist/shared/{components-3ky44908.js → components-vfjc9jzz.js} +2 -3
- package/dist/shared/{components-39vmfq9s.js → components-vsa625vk.js} +21 -20
- package/dist/shared/components-wawnmkph.js +500 -0
- package/dist/shared/components-wktrrb4m.js +38 -0
- package/dist/shared/{components-8snj490h.js → components-wwd363me.js} +78 -32
- package/dist/shared/{components-5pnw8183.js → components-xcwvgcrd.js} +23 -22
- package/dist/shared/components-xdqmt6ed.js +20 -0
- package/dist/shared/{components-h2ajmhwn.js → components-y1w3a6wx.js} +107 -35
- package/dist/shared/{components-ar66gnt0.js → components-y6578b8h.js} +37 -34
- package/dist/shared/{components-59efxjd9.js → components-yhgq0qxx.js} +13 -12
- package/dist/shared/{components-jthe1pwb.js → components-yz84gpkq.js} +101 -404
- package/dist/shared/components-z3v0n0de.js +11 -0
- package/dist/shared/{components-7r3etddy.js → components-z9n19fyk.js} +19 -54
- package/dist/shared/components-zbvjzh1p.js +141 -0
- package/dist/styles.css +241 -16
- package/dist/tailwind.css +13 -0
- package/dist/tokens.css +34 -0
- package/index.ts +60 -0
- package/lib/date-locale.ts +50 -0
- package/lib/field-classes.ts +115 -0
- package/lib/field-variants.ts +117 -0
- package/lib/fluid-hover-highlight.tsx +139 -0
- package/lib/focus-ring.tsx +46 -0
- package/lib/goo-indicator.tsx +261 -0
- package/lib/icon-context.tsx +310 -0
- package/lib/inert.ts +22 -0
- package/lib/morph-layers.tsx +102 -0
- package/lib/morph-part.tsx +49 -0
- package/lib/popup.ts +52 -0
- package/lib/reduced-motion.ts +19 -0
- package/lib/row-index.tsx +29 -0
- package/lib/row-order.tsx +115 -0
- package/lib/scroll-column.tsx +244 -0
- package/lib/shape-context.tsx +124 -0
- package/lib/size-context.tsx +66 -0
- package/lib/size-scale.ts +115 -0
- package/lib/slot.ts +25 -0
- package/lib/springs.ts +62 -0
- package/lib/surface-classes.ts +58 -0
- package/lib/surface-context.tsx +21 -0
- package/lib/use-controllable-state.ts +44 -0
- package/lib/use-fluid-hover.ts +541 -0
- package/lib/use-is-mobile.ts +25 -0
- package/lib/use-keyboard-nav-gate.ts +45 -0
- package/lib/use-merge-split.tsx +209 -0
- package/lib/use-morph.ts +557 -0
- package/lib/use-touch-primary.tsx +43 -0
- package/lib/utils.ts +37 -0
- package/package.json +177 -4
- package/preset.ts +196 -0
- package/primitives/code/examples.mdx +27 -0
- package/primitives/code/index.tsx +80 -0
- package/primitives/craft/demos.tsx +173 -0
- package/primitives/craft/examples.mdx +138 -0
- package/primitives/craft/index.tsx +76 -0
- package/primitives/fluid-hover/examples.mdx +84 -0
- package/primitives/fluid-hover/index.tsx +316 -0
- package/primitives/morph/examples.mdx +135 -0
- package/primitives/morph/index.tsx +192 -0
- package/primitives/motion/examples.mdx +100 -0
- package/primitives/motion/index.tsx +160 -0
- package/primitives/scroll-area/examples.mdx +58 -0
- package/primitives/scroll-area/index.tsx +200 -0
- package/primitives/separator/examples.mdx +30 -0
- package/primitives/separator/index.tsx +53 -0
- package/primitives/sizes/examples.mdx +167 -0
- package/primitives/sizes/index.tsx +89 -0
- package/primitives/skeleton/examples.mdx +51 -0
- package/primitives/skeleton/index.tsx +72 -0
- package/primitives/skeleton/skeleton.css +15 -0
- package/primitives/surface/examples.mdx +173 -0
- package/primitives/surface/index.tsx +109 -0
- package/primitives/surface/surface.css +176 -0
- package/dist/shared/components-0sn7ypjw.js +0 -61
- package/dist/shared/components-1434p1mh.js +0 -123
- package/dist/shared/components-a86dytt0.js +0 -986
- package/dist/shared/components-gf5s6ap9.js +0 -42
- package/dist/shared/components-hyq23phv.js +0 -310
- package/dist/shared/components-ng72k97e.js +0 -128
- package/dist/shared/components-raknhppb.js +0 -64
- package/dist/shared/components-vynjtrk6.js +0 -19
- package/dist/shared/components-wy9jpqny.js +0 -146
- /package/dist/shared/{components-1v7rxytx.js → components-3ey0hzpb.js} +0 -0
- /package/dist/shared/{components-ezxhrmer.js → components-jcnjay5g.js} +0 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import Avatar from './index'
|
|
2
|
+
import SizeProvider from '../../primitives/sizes'
|
|
3
|
+
import { Building2 } from 'lucide-react'
|
|
4
|
+
|
|
5
|
+
# Picture and fallback
|
|
6
|
+
|
|
7
|
+
The picture fades in once it has loaded. Without one, or when it fails, initials (or a person glyph) show on a neutral ground.
|
|
8
|
+
|
|
9
|
+
<div className="flex items-center gap-3">
|
|
10
|
+
<Avatar>
|
|
11
|
+
<Avatar.Image alt="Ada Lovelace" src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'><radialGradient id='a' cx='.3' cy='.25' r='1'><stop stop-color='%23F5C6A5'/><stop offset='1' stop-color='%236B97FF'/></radialGradient><rect width='1' height='1' fill='url(%23a)'/></svg>" />
|
|
12
|
+
<Avatar.Fallback>AL</Avatar.Fallback>
|
|
13
|
+
</Avatar>
|
|
14
|
+
<Avatar>
|
|
15
|
+
<Avatar.Image alt="Grace Hopper" src="https://invalid.example/missing.png" />
|
|
16
|
+
<Avatar.Fallback>GH</Avatar.Fallback>
|
|
17
|
+
</Avatar>
|
|
18
|
+
<Avatar>
|
|
19
|
+
<Avatar.Fallback />
|
|
20
|
+
</Avatar>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
# Pale pictures
|
|
24
|
+
|
|
25
|
+
A 1px inset edge keeps a light picture from dissolving into the surface.
|
|
26
|
+
|
|
27
|
+
<div className="flex items-center gap-3">
|
|
28
|
+
<Avatar>
|
|
29
|
+
<Avatar.Image alt="" src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'><rect width='1' height='1' fill='white'/></svg>" />
|
|
30
|
+
<Avatar.Fallback>W</Avatar.Fallback>
|
|
31
|
+
</Avatar>
|
|
32
|
+
<Avatar shape="square">
|
|
33
|
+
<Avatar.Image alt="" src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'><rect width='1' height='1' fill='white'/></svg>" />
|
|
34
|
+
<Avatar.Fallback>W</Avatar.Fallback>
|
|
35
|
+
</Avatar>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
# Square
|
|
39
|
+
|
|
40
|
+
`shape="square"` is for teams, organizations and things. It takes the shape's item radius, so pill mode rounds it further.
|
|
41
|
+
|
|
42
|
+
<div className="flex items-center gap-3">
|
|
43
|
+
<Avatar shape="square">
|
|
44
|
+
<Avatar.Fallback>AC</Avatar.Fallback>
|
|
45
|
+
</Avatar>
|
|
46
|
+
<Avatar shape="square">
|
|
47
|
+
<Avatar.Fallback>
|
|
48
|
+
<Building2 size={16} strokeWidth={1.5} aria-hidden />
|
|
49
|
+
</Avatar.Fallback>
|
|
50
|
+
</Avatar>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
# Sizes
|
|
54
|
+
|
|
55
|
+
One control tall: 36px, and 28px in a compact region or with `size="compact"`. A size off the ladder goes through `className`.
|
|
56
|
+
|
|
57
|
+
<div className="flex flex-col gap-3">
|
|
58
|
+
<div className="flex items-center gap-3">
|
|
59
|
+
<Avatar>
|
|
60
|
+
<Avatar.Fallback>AL</Avatar.Fallback>
|
|
61
|
+
</Avatar>
|
|
62
|
+
<Avatar size="compact">
|
|
63
|
+
<Avatar.Fallback>AL</Avatar.Fallback>
|
|
64
|
+
</Avatar>
|
|
65
|
+
<Avatar size="compact" className="size-5 text-micro">
|
|
66
|
+
<Avatar.Fallback>A</Avatar.Fallback>
|
|
67
|
+
</Avatar>
|
|
68
|
+
</div>
|
|
69
|
+
<SizeProvider size="compact">
|
|
70
|
+
<div className="flex items-center gap-3">
|
|
71
|
+
<Avatar>
|
|
72
|
+
<Avatar.Fallback>GH</Avatar.Fallback>
|
|
73
|
+
</Avatar>
|
|
74
|
+
<Avatar>
|
|
75
|
+
<Avatar.Fallback />
|
|
76
|
+
</Avatar>
|
|
77
|
+
</div>
|
|
78
|
+
</SizeProvider>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
# A stack
|
|
82
|
+
|
|
83
|
+
Overlapping avatars with a ring of the page around each.
|
|
84
|
+
|
|
85
|
+
<div className="flex -space-x-2">
|
|
86
|
+
<Avatar className="ring-2 ring-background">
|
|
87
|
+
<Avatar.Fallback>AL</Avatar.Fallback>
|
|
88
|
+
</Avatar>
|
|
89
|
+
<Avatar className="ring-2 ring-background">
|
|
90
|
+
<Avatar.Fallback>GH</Avatar.Fallback>
|
|
91
|
+
</Avatar>
|
|
92
|
+
<Avatar className="ring-2 ring-background">
|
|
93
|
+
<Avatar.Fallback>+3</Avatar.Fallback>
|
|
94
|
+
</Avatar>
|
|
95
|
+
</div>
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Local addition (not part of Fluid Functionalism): a person's or a team's
|
|
3
|
+
* picture with a fallback, on Base UI's Avatar (image loading status, the
|
|
4
|
+
* fallback's delay). The 1px inset image edge follows upstream FF's media
|
|
5
|
+
* outline (see Card.Image); the image fades in on the fast tier.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Avatar as AvatarPrimitive } from '@base-ui/react/avatar'
|
|
9
|
+
import {
|
|
10
|
+
type ComponentPropsWithoutRef,
|
|
11
|
+
type ForwardRefExoticComponent,
|
|
12
|
+
forwardRef,
|
|
13
|
+
type ReactNode,
|
|
14
|
+
type RefAttributes,
|
|
15
|
+
} from 'react'
|
|
16
|
+
import { useIcon } from '../../lib/icon-context'
|
|
17
|
+
import { useShape } from '../../lib/shape-context'
|
|
18
|
+
import { type SizeVariant, useSize } from '../../lib/size-context'
|
|
19
|
+
import { cn } from '../../lib/utils'
|
|
20
|
+
import { SizeProvider } from '../../primitives/sizes'
|
|
21
|
+
|
|
22
|
+
// Literal tables: UnoCSS only generates the classes it can read whole.
|
|
23
|
+
const BOX = { default: 'size-9', compact: 'size-7' }
|
|
24
|
+
|
|
25
|
+
type AvatarImageProps = ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
|
|
26
|
+
|
|
27
|
+
/** The picture. It fades in once loaded; until then, or if it fails, the fallback shows. */
|
|
28
|
+
const AvatarImage = forwardRef<HTMLImageElement, AvatarImageProps>(({ className, ...props }, ref) => (
|
|
29
|
+
<AvatarPrimitive.Image
|
|
30
|
+
ref={ref}
|
|
31
|
+
{...props}
|
|
32
|
+
className={cn(
|
|
33
|
+
// An outline paints over the image (an inset shadow would sit beneath it).
|
|
34
|
+
'size-full rounded-[inherit] object-cover outline outline-1 -outline-offset-1 outline-image-edge',
|
|
35
|
+
'transition-opacity duration-fast data-[starting-style]:opacity-0',
|
|
36
|
+
className,
|
|
37
|
+
)}
|
|
38
|
+
/>
|
|
39
|
+
))
|
|
40
|
+
|
|
41
|
+
AvatarImage.displayName = 'AvatarImage'
|
|
42
|
+
|
|
43
|
+
type AvatarFallbackProps = ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
|
|
44
|
+
|
|
45
|
+
/** Initials or an icon, shown while the image loads or when there is none. Empty, it shows a person glyph. */
|
|
46
|
+
const AvatarFallback = forwardRef<HTMLSpanElement, AvatarFallbackProps>(({ className, children, ...props }, ref) => {
|
|
47
|
+
const size = useSize()
|
|
48
|
+
const User = useIcon('user')
|
|
49
|
+
return (
|
|
50
|
+
<AvatarPrimitive.Fallback
|
|
51
|
+
ref={ref}
|
|
52
|
+
{...props}
|
|
53
|
+
className={cn('flex size-full items-center justify-center rounded-[inherit]', className)}
|
|
54
|
+
>
|
|
55
|
+
{children ?? <User size={size.icon} strokeWidth={1.5} aria-hidden />}
|
|
56
|
+
</AvatarPrimitive.Fallback>
|
|
57
|
+
)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
AvatarFallback.displayName = 'AvatarFallback'
|
|
61
|
+
|
|
62
|
+
interface AvatarProps extends ComponentPropsWithoutRef<'span'> {
|
|
63
|
+
/** Pins the avatar to one step of the size ladder (36px default, 28px compact). Defaults to the surrounding SizeProvider. */
|
|
64
|
+
size?: SizeVariant
|
|
65
|
+
/** A circle for people; a square for teams, organizations and things, which takes the shape's item radius (rounded or pill). Defaults to `"circle"`. */
|
|
66
|
+
shape?: 'circle' | 'square'
|
|
67
|
+
/** Classes for the root: a size off the ladder (`size-5` in a dense row), a ground. */
|
|
68
|
+
className?: string
|
|
69
|
+
/** `Avatar.Image` and `Avatar.Fallback`. */
|
|
70
|
+
children?: ReactNode
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
type AvatarComponent = ForwardRefExoticComponent<AvatarProps & RefAttributes<HTMLSpanElement>> & {
|
|
74
|
+
Image: typeof AvatarImage
|
|
75
|
+
Fallback: typeof AvatarFallback
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* A picture of a person or a team, with initials or an icon when there is
|
|
80
|
+
* none.
|
|
81
|
+
*
|
|
82
|
+
* The avatar is one control tall (36px, 28px compact), so it lines up with
|
|
83
|
+
* the rows and buttons beside it, and follows the surrounding SizeProvider.
|
|
84
|
+
* Base UI's Avatar tracks the image: until it has loaded, or when it fails,
|
|
85
|
+
* the fallback shows on a neutral ground (the gray badge's accent); once it
|
|
86
|
+
* lands it fades in over the fallback. A 1px inset edge keeps a pale picture from dissolving into the
|
|
87
|
+
* surface. People are circles; `shape="square"` takes the shape context's
|
|
88
|
+
* item radius for teams and things.
|
|
89
|
+
*
|
|
90
|
+
* Statics:
|
|
91
|
+
* - `Avatar.Image` — the picture: `src`, `alt`, `onLoadingStatusChange`.
|
|
92
|
+
* - `Avatar.Fallback` — initials or an icon; empty, a person glyph. `delay`
|
|
93
|
+
* (ms) holds it back so a fast image never flashes it.
|
|
94
|
+
*
|
|
95
|
+
* @example {@include ./examples.mdx}
|
|
96
|
+
*/
|
|
97
|
+
const Avatar = forwardRef<HTMLSpanElement, AvatarProps>(
|
|
98
|
+
({ size: sizeProp, shape = 'circle', className, children, ...props }, ref) => {
|
|
99
|
+
const size = useSize(sizeProp)
|
|
100
|
+
const shapes = useShape()
|
|
101
|
+
const avatar = (
|
|
102
|
+
<AvatarPrimitive.Root
|
|
103
|
+
ref={ref}
|
|
104
|
+
{...props}
|
|
105
|
+
className={cn(
|
|
106
|
+
'relative inline-flex shrink-0 select-none items-center justify-center overflow-hidden align-middle',
|
|
107
|
+
'bg-accent text-foreground weight-medium',
|
|
108
|
+
BOX[size.variant],
|
|
109
|
+
size.text,
|
|
110
|
+
shape === 'circle' ? 'rounded-full' : shapes.item,
|
|
111
|
+
className,
|
|
112
|
+
)}
|
|
113
|
+
>
|
|
114
|
+
{children}
|
|
115
|
+
</AvatarPrimitive.Root>
|
|
116
|
+
)
|
|
117
|
+
// A size prop pins the fallback's glyph too.
|
|
118
|
+
return sizeProp ? <SizeProvider size={sizeProp}>{avatar}</SizeProvider> : avatar
|
|
119
|
+
},
|
|
120
|
+
) as AvatarComponent
|
|
121
|
+
|
|
122
|
+
Avatar.displayName = 'Avatar'
|
|
123
|
+
|
|
124
|
+
Object.assign(Avatar, { Image: AvatarImage, Fallback: AvatarFallback })
|
|
125
|
+
|
|
126
|
+
export type { AvatarFallbackProps, AvatarImageProps, AvatarProps }
|
|
127
|
+
export { Avatar, AvatarFallback, AvatarImage }
|
|
128
|
+
|
|
129
|
+
export default Avatar
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Local addition (not part of Fluid Functionalism): the docs stage of the
|
|
3
|
+
* Badge page's change morph. A Badge that steps through a list of props each
|
|
4
|
+
* time its button is pressed, so the example stays hook-free.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { useState } from 'react'
|
|
8
|
+
import Button from '../button'
|
|
9
|
+
import Badge, { type BadgeProps } from './index'
|
|
10
|
+
|
|
11
|
+
interface ChangeDemoProps {
|
|
12
|
+
/** The badge's props, one entry per press; after the last it starts over. */
|
|
13
|
+
steps: BadgeProps[]
|
|
14
|
+
/** Label of the button that steps. */
|
|
15
|
+
label?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default function ChangeDemo({ steps, label = 'Next' }: ChangeDemoProps) {
|
|
19
|
+
const [at, setAt] = useState(0)
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div className="flex items-center gap-3">
|
|
23
|
+
<Button variant="tertiary" size="compact" onClick={() => setAt((at + 1) % steps.length)}>
|
|
24
|
+
{label}
|
|
25
|
+
</Button>
|
|
26
|
+
<Badge {...steps[at]} />
|
|
27
|
+
</div>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import Badge from './index'
|
|
2
|
+
import ChangeDemo from './change-demo'
|
|
3
|
+
|
|
4
|
+
# Solid
|
|
5
|
+
|
|
6
|
+
The default variant: a tinted background in the badge's color.
|
|
7
|
+
|
|
8
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
9
|
+
<Badge color="violet">Fiction</Badge>
|
|
10
|
+
<Badge color="amber">Science</Badge>
|
|
11
|
+
<Badge color="green">Philosophy</Badge>
|
|
12
|
+
<Badge color="blue">History</Badge>
|
|
13
|
+
<Badge color="rose">Poetry</Badge>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
# Dot
|
|
17
|
+
|
|
18
|
+
`variant="dot"` keeps a neutral bordered badge and shows the color as an
|
|
19
|
+
indicator dot.
|
|
20
|
+
|
|
21
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
22
|
+
<Badge variant="dot" color="violet">Fiction</Badge>
|
|
23
|
+
<Badge variant="dot" color="amber">Science</Badge>
|
|
24
|
+
<Badge variant="dot" color="green">Philosophy</Badge>
|
|
25
|
+
<Badge variant="dot" color="blue">History</Badge>
|
|
26
|
+
<Badge variant="dot" color="rose">Poetry</Badge>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
# Outline
|
|
30
|
+
|
|
31
|
+
`variant="outline"` is the dot badge's neutral frame without the dot: a label
|
|
32
|
+
that carries no status.
|
|
33
|
+
|
|
34
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
35
|
+
<Badge variant="outline">Draft</Badge>
|
|
36
|
+
<Badge variant="outline">v2.4</Badge>
|
|
37
|
+
<Badge variant="outline" size="compact">Archived</Badge>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
# Status mapping
|
|
41
|
+
|
|
42
|
+
Hosts with a shadcn-style badge map its variants onto a hue: default and
|
|
43
|
+
secondary → `gray`, destructive → `red`, success → `green`, outline →
|
|
44
|
+
`variant="outline"`. The tint stays a wash with foreground text, never a solid
|
|
45
|
+
fill.
|
|
46
|
+
|
|
47
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
48
|
+
<Badge color="gray">Default</Badge>
|
|
49
|
+
<Badge color="gray">Secondary</Badge>
|
|
50
|
+
<Badge color="red">Destructive</Badge>
|
|
51
|
+
<Badge color="green">Success</Badge>
|
|
52
|
+
<Badge variant="outline">Outline</Badge>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
# Colors
|
|
56
|
+
|
|
57
|
+
All seventeen palette colors, solid and dot.
|
|
58
|
+
|
|
59
|
+
<div className="flex flex-col gap-3">
|
|
60
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
61
|
+
{['gray', 'red', 'orange', 'amber', 'yellow', 'lime', 'green', 'emerald', 'teal', 'cyan', 'blue', 'indigo', 'violet', 'purple', 'fuchsia', 'pink', 'rose'].map((c) => (
|
|
62
|
+
<Badge key={c} color={c}>
|
|
63
|
+
{c.charAt(0).toUpperCase() + c.slice(1)}
|
|
64
|
+
</Badge>
|
|
65
|
+
))}
|
|
66
|
+
</div>
|
|
67
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
68
|
+
{['gray', 'red', 'orange', 'amber', 'yellow', 'lime', 'green', 'emerald', 'teal', 'cyan', 'blue', 'indigo', 'violet', 'purple', 'fuchsia', 'pink', 'rose'].map((c) => (
|
|
69
|
+
<Badge key={c} variant="dot" color={c}>
|
|
70
|
+
{c.charAt(0).toUpperCase() + c.slice(1)}
|
|
71
|
+
</Badge>
|
|
72
|
+
))}
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
# Compact
|
|
77
|
+
|
|
78
|
+
`size="compact"` steps the badge down the size ladder (20px tall, 11px
|
|
79
|
+
text) for dense rows and table cells.
|
|
80
|
+
|
|
81
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
82
|
+
<Badge size="compact" color="blue">History</Badge>
|
|
83
|
+
<Badge size="compact" variant="dot" color="green">Philosophy</Badge>
|
|
84
|
+
<Badge size="compact">Draft</Badge>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
# Changes
|
|
88
|
+
|
|
89
|
+
Press a button. A new label springs the badge to its new width while the
|
|
90
|
+
text crossfades, a new color fades in, and the dot slides in or out with
|
|
91
|
+
the variant. `ChangeDemo` is a docs stage: a Badge that steps through its
|
|
92
|
+
`steps` on each press.
|
|
93
|
+
|
|
94
|
+
<div className="flex w-48 flex-col items-start gap-3">
|
|
95
|
+
<ChangeDemo
|
|
96
|
+
label="Add one"
|
|
97
|
+
steps={[
|
|
98
|
+
{ color: 'red', children: 8 },
|
|
99
|
+
{ color: 'red', children: 9 },
|
|
100
|
+
{ color: 'red', children: 10 },
|
|
101
|
+
{ color: 'red', children: 99 },
|
|
102
|
+
{ color: 'red', children: '99+' },
|
|
103
|
+
]}
|
|
104
|
+
/>
|
|
105
|
+
<ChangeDemo
|
|
106
|
+
label="Advance"
|
|
107
|
+
steps={[
|
|
108
|
+
{ children: 'Draft' },
|
|
109
|
+
{ color: 'amber', children: 'In review' },
|
|
110
|
+
{ color: 'green', children: 'Published' },
|
|
111
|
+
]}
|
|
112
|
+
/>
|
|
113
|
+
<ChangeDemo
|
|
114
|
+
label="Sync"
|
|
115
|
+
steps={[
|
|
116
|
+
{ color: 'blue', children: 'Syncing' },
|
|
117
|
+
{ variant: 'dot', color: 'green', children: 'Synced' },
|
|
118
|
+
]}
|
|
119
|
+
/>
|
|
120
|
+
</div>
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Ported from Fluid Functionalism — github.com/mickadesign/fluid-functionalism
|
|
3
|
+
* `registry/default/badge.tsx` @ b3587bdbd83fc66c2a6aae3817ffb856cb09260b
|
|
4
|
+
* MIT License © 2026 Micka Touillaud — fluidfunctionalism.com (notice: LICENSE.fluid-functionalism)
|
|
5
|
+
* Local modifications:
|
|
6
|
+
* - `"use client"` directive dropped (no RSC here).
|
|
7
|
+
* - `@/lib/*` imports rewritten to relative `../../lib/*` paths.
|
|
8
|
+
* - `BadgeProps` re-declares `variant` (inherited from cva's VariantProps
|
|
9
|
+
* upstream) and carries the FF docs API-table descriptions on every prop,
|
|
10
|
+
* so modo's parser lists them; types are unchanged for callers.
|
|
11
|
+
* - TSDoc with the FF docs page's examples added above the component;
|
|
12
|
+
* `export default Badge` added.
|
|
13
|
+
* - Styling reads DS tokens (AGENTS.md styling): `text-[Npx]` →
|
|
14
|
+
* `text-<role>[-compact]`; `font-medium` → `weight-medium`; literal colors →
|
|
15
|
+
* color tokens.
|
|
16
|
+
* - Changes animate (local, in the morph layer's language): a new label or
|
|
17
|
+
* variant springs the badge from its old width to its new one on
|
|
18
|
+
* `spring.moderate` (the content clipped on the way) while the label
|
|
19
|
+
* crossfades with Toast's blur; a color fades over `duration-moderate`, and
|
|
20
|
+
* the dot scales in and out with the label sliding to make room. The content
|
|
21
|
+
* sits in one inner span that carries the morph, so the root keeps the
|
|
22
|
+
* caller's `style`.
|
|
23
|
+
* - `badgeColors`, `badgeVariants` and the size aliases moved to `variants.ts`
|
|
24
|
+
* (server-callable, no JSX or hooks) and re-exported from here.
|
|
25
|
+
* - Variant `outline` (local): the dot badge's neutral hairline frame without
|
|
26
|
+
* the dot; it draws no color, so `color` has no effect on it.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import type { VariantProps } from 'class-variance-authority'
|
|
30
|
+
import { AnimatePresence, animate, motion } from 'motion/react'
|
|
31
|
+
import { forwardRef, type HTMLAttributes, type ReactNode, useLayoutEffect, useRef } from 'react'
|
|
32
|
+
import { useReduceMotion } from '../../lib/reduced-motion'
|
|
33
|
+
import { useShape } from '../../lib/shape-context'
|
|
34
|
+
import { useSizeVariant } from '../../lib/size-context'
|
|
35
|
+
import { spring } from '../../lib/springs'
|
|
36
|
+
import { cn } from '../../lib/utils'
|
|
37
|
+
import {
|
|
38
|
+
type BadgeColor,
|
|
39
|
+
type BadgeSize,
|
|
40
|
+
type BadgeSizeCanonical,
|
|
41
|
+
type BadgeVariant,
|
|
42
|
+
badgeColors,
|
|
43
|
+
badgeVariants,
|
|
44
|
+
legacySizeAliases,
|
|
45
|
+
} from './variants'
|
|
46
|
+
|
|
47
|
+
const { exit: _exit, ...enter } = spring.moderate
|
|
48
|
+
|
|
49
|
+
/** The label as text, when it is text: the key its crossfade runs on. */
|
|
50
|
+
function textOf(node: ReactNode) {
|
|
51
|
+
const parts = Array.isArray(node) ? node : [node]
|
|
52
|
+
if (parts.every(part => typeof part === 'string' || typeof part === 'number')) return parts.join('')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* Width morph (local): when the label or the variant changes, the content
|
|
56
|
+
span springs from its old width to its new one instead of jumping, and the
|
|
57
|
+
badge follows it. By the time the change commits the span already has its
|
|
58
|
+
new width, so a ResizeObserver keeps the resting one. At rest the inline
|
|
59
|
+
width is dropped. */
|
|
60
|
+
function useWidthMorph(change: string) {
|
|
61
|
+
const content = useRef<HTMLSpanElement>(null)
|
|
62
|
+
const rest = useRef(0)
|
|
63
|
+
const live = useRef<number | null>(null)
|
|
64
|
+
const reduced = useReduceMotion()
|
|
65
|
+
|
|
66
|
+
useLayoutEffect(() => {
|
|
67
|
+
const el = content.current
|
|
68
|
+
if (!el) return
|
|
69
|
+
const observer = new ResizeObserver(() => {
|
|
70
|
+
if (live.current === null) rest.current = el.offsetWidth
|
|
71
|
+
})
|
|
72
|
+
observer.observe(el)
|
|
73
|
+
return () => observer.disconnect()
|
|
74
|
+
}, [])
|
|
75
|
+
|
|
76
|
+
useLayoutEffect(() => {
|
|
77
|
+
const el = content.current
|
|
78
|
+
const from = live.current ?? rest.current
|
|
79
|
+
if (!el || !from) return
|
|
80
|
+
el.style.width = ''
|
|
81
|
+
const to = el.offsetWidth
|
|
82
|
+
rest.current = to
|
|
83
|
+
live.current = null
|
|
84
|
+
if (reduced || from === to) return
|
|
85
|
+
const write = (width: number) => {
|
|
86
|
+
live.current = width
|
|
87
|
+
el.style.width = `${width}px`
|
|
88
|
+
}
|
|
89
|
+
write(from)
|
|
90
|
+
const controls = animate(from, to, {
|
|
91
|
+
...enter,
|
|
92
|
+
onUpdate: write,
|
|
93
|
+
onComplete: () => {
|
|
94
|
+
live.current = null
|
|
95
|
+
el.style.width = ''
|
|
96
|
+
},
|
|
97
|
+
})
|
|
98
|
+
return () => controls.stop()
|
|
99
|
+
}, [change, reduced])
|
|
100
|
+
|
|
101
|
+
return content
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
interface BadgeProps
|
|
105
|
+
extends Omit<HTMLAttributes<HTMLSpanElement>, 'color'>,
|
|
106
|
+
Omit<VariantProps<typeof badgeVariants>, 'size'> {
|
|
107
|
+
/** Visual style. Solid uses a tinted background; dot shows a colored indicator; outline is the dot badge's neutral hairline frame without the dot (it ignores `color`). Defaults to `"solid"`. */
|
|
108
|
+
variant?: 'solid' | 'dot' | 'outline'
|
|
109
|
+
/** Step on the size ladder (see Sizes). Legacy sm/md/lg values resolve as aliases (sm → compact; md/lg → default). Defaults to the surrounding SizeProvider, else `"default"`. */
|
|
110
|
+
size?: 'default' | 'compact' | 'sm' | 'md' | 'lg'
|
|
111
|
+
/** Color from the Tailwind palette: gray, red, orange, amber, yellow, lime, green, emerald, teal, cyan, blue, indigo, violet, purple, fuchsia, pink, rose. Defaults to `"gray"`. */
|
|
112
|
+
color?: BadgeColor
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Compact label for status, category, or metadata. Supports solid, dot and
|
|
117
|
+
* outline variants with Tailwind colors.
|
|
118
|
+
*
|
|
119
|
+
* Solid badges tint their background with the chosen color mixed into the
|
|
120
|
+
* page background (gray uses the accent token); dot badges keep a neutral
|
|
121
|
+
* border and show the color as a small indicator; outline badges are the
|
|
122
|
+
* neutral frame alone, for a label that carries no status. The badge rides the size
|
|
123
|
+
* ladder — `size` pins it, otherwise it follows the surrounding SizeProvider
|
|
124
|
+
* — and takes its corner radius from the shape context. Changes animate: a
|
|
125
|
+
* new label or variant springs the width while the label crossfades, and a
|
|
126
|
+
* new color fades in. The palette is also exported as `badgeColors`, the class
|
|
127
|
+
* recipe as `badgeVariants` (both callable on the server).
|
|
128
|
+
*
|
|
129
|
+
* Status labels pick a hue rather than a fill of their own: a destructive or
|
|
130
|
+
* error label is `color="red"`, a success label `color="green"`, a secondary
|
|
131
|
+
* or default one `color="gray"`, and a neutral outlined one
|
|
132
|
+
* `variant="outline"` (see the Status mapping example).
|
|
133
|
+
*
|
|
134
|
+
* @example {@include ./examples.mdx}
|
|
135
|
+
*/
|
|
136
|
+
const Badge = forwardRef<HTMLSpanElement, BadgeProps>(
|
|
137
|
+
({ className, variant = 'solid', size: sizeProp, color = 'gray', children, style, ...props }, ref) => {
|
|
138
|
+
const shape = useShape()
|
|
139
|
+
// Resolve the size: explicit prop (legacy aliases mapped onto the
|
|
140
|
+
// canonical ladder) > surrounding SizeProvider > default.
|
|
141
|
+
const contextSize = useSizeVariant()
|
|
142
|
+
const size: BadgeSizeCanonical = sizeProp
|
|
143
|
+
? (legacySizeAliases[sizeProp] ?? (sizeProp as BadgeSizeCanonical))
|
|
144
|
+
: contextSize === 'compact'
|
|
145
|
+
? 'compact'
|
|
146
|
+
: 'default'
|
|
147
|
+
const colorValue = badgeColors[color]
|
|
148
|
+
const isSolid = variant === 'solid'
|
|
149
|
+
const dotSize = size === 'compact' ? 6 : 7
|
|
150
|
+
|
|
151
|
+
const colorStyle = isSolid
|
|
152
|
+
? color === 'gray'
|
|
153
|
+
? { backgroundColor: 'var(--accent)', color: 'var(--foreground)' }
|
|
154
|
+
: {
|
|
155
|
+
color: 'var(--foreground)',
|
|
156
|
+
backgroundColor: `color-mix(in srgb, ${colorValue} 15%, var(--background))`,
|
|
157
|
+
}
|
|
158
|
+
: {}
|
|
159
|
+
|
|
160
|
+
const dotColor = color === 'gray' ? 'var(--muted-foreground)' : colorValue
|
|
161
|
+
const label = textOf(children)
|
|
162
|
+
const content = useWidthMorph(`${variant} ${size} ${label}`)
|
|
163
|
+
|
|
164
|
+
return (
|
|
165
|
+
<span
|
|
166
|
+
ref={ref}
|
|
167
|
+
className={cn(
|
|
168
|
+
badgeVariants({ variant, size }),
|
|
169
|
+
shape.item,
|
|
170
|
+
'transition-[background-color] duration-moderate',
|
|
171
|
+
className,
|
|
172
|
+
)}
|
|
173
|
+
style={{ ...colorStyle, ...style }}
|
|
174
|
+
{...props}
|
|
175
|
+
>
|
|
176
|
+
{/* The morphing box: it may shrink below its content while the width
|
|
177
|
+
springs (min-w-0), clipping it sideways only, and it positions
|
|
178
|
+
whatever pops out of the flow on its way out. */}
|
|
179
|
+
<span ref={content} className="relative inline-flex min-w-0 items-center gap-[inherit] overflow-x-clip">
|
|
180
|
+
<AnimatePresence mode="popLayout" initial={false}>
|
|
181
|
+
{variant === 'dot' && (
|
|
182
|
+
<motion.span
|
|
183
|
+
key="dot"
|
|
184
|
+
className="shrink-0 rounded-full transition-[background-color] duration-moderate"
|
|
185
|
+
style={{
|
|
186
|
+
width: dotSize,
|
|
187
|
+
height: dotSize,
|
|
188
|
+
backgroundColor: dotColor,
|
|
189
|
+
}}
|
|
190
|
+
initial={{ opacity: 0, scale: 0 }}
|
|
191
|
+
animate={{ opacity: 1, scale: 1 }}
|
|
192
|
+
exit={{ opacity: 0, scale: 0, transition: spring.moderate.exit }}
|
|
193
|
+
transition={enter}
|
|
194
|
+
/>
|
|
195
|
+
)}
|
|
196
|
+
</AnimatePresence>
|
|
197
|
+
<AnimatePresence mode="popLayout" initial={false}>
|
|
198
|
+
{/* text-box needs a block container — the badge root is a flex
|
|
199
|
+
container, so the label gets its own span. Height is fixed
|
|
200
|
+
(h-*), so trimming only recenters the letterforms. */}
|
|
201
|
+
<motion.span
|
|
202
|
+
key={label ?? 'label'}
|
|
203
|
+
layout="position"
|
|
204
|
+
className="[text-box:trim-both_cap_alphabetic]"
|
|
205
|
+
initial={{ opacity: 0, filter: 'blur(4px)' }}
|
|
206
|
+
// At rest the label keeps no filter, which would make it its own stacking context.
|
|
207
|
+
animate={{ opacity: 1, filter: 'blur(0px)', transitionEnd: { filter: 'none' } }}
|
|
208
|
+
exit={{ opacity: 0, filter: 'blur(4px)', transition: spring.moderate.exit }}
|
|
209
|
+
transition={enter}
|
|
210
|
+
>
|
|
211
|
+
{children}
|
|
212
|
+
</motion.span>
|
|
213
|
+
</AnimatePresence>
|
|
214
|
+
</span>
|
|
215
|
+
</span>
|
|
216
|
+
)
|
|
217
|
+
},
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
Badge.displayName = 'Badge'
|
|
221
|
+
|
|
222
|
+
export type { BadgeColor, BadgeProps, BadgeSize, BadgeVariant }
|
|
223
|
+
export { Badge, badgeColors, badgeVariants }
|
|
224
|
+
|
|
225
|
+
export default Badge
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Ported from Fluid Functionalism — github.com/mickadesign/fluid-functionalism
|
|
3
|
+
* `registry/default/badge.tsx` @ b3587bdbd83fc66c2a6aae3817ffb856cb09260b
|
|
4
|
+
* MIT License © 2026 Micka Touillaud — fluidfunctionalism.com (notice: LICENSE.fluid-functionalism)
|
|
5
|
+
* Local modifications:
|
|
6
|
+
* - The palette (`badgeColors`), the class recipe (`badgeVariants`) and the size
|
|
7
|
+
* aliases, split out of `index.tsx` so server code can call them: no JSX, hooks
|
|
8
|
+
* or contexts here. `index.tsx` re-exports them.
|
|
9
|
+
* - Styling reads DS tokens (see `index.tsx`).
|
|
10
|
+
* - Variant `outline` (local): the dot badge's neutral hairline frame without the dot.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { cva } from 'class-variance-authority'
|
|
14
|
+
|
|
15
|
+
const badgeColors = {
|
|
16
|
+
gray: 'var(--badge-gray)',
|
|
17
|
+
red: 'var(--badge-red)',
|
|
18
|
+
orange: 'var(--badge-orange)',
|
|
19
|
+
amber: 'var(--badge-amber)',
|
|
20
|
+
yellow: 'var(--badge-yellow)',
|
|
21
|
+
lime: 'var(--badge-lime)',
|
|
22
|
+
green: 'var(--badge-green)',
|
|
23
|
+
emerald: 'var(--badge-emerald)',
|
|
24
|
+
teal: 'var(--badge-teal)',
|
|
25
|
+
cyan: 'var(--badge-cyan)',
|
|
26
|
+
blue: 'var(--badge-blue)',
|
|
27
|
+
indigo: 'var(--badge-indigo)',
|
|
28
|
+
violet: 'var(--badge-violet)',
|
|
29
|
+
purple: 'var(--badge-purple)',
|
|
30
|
+
fuchsia: 'var(--badge-fuchsia)',
|
|
31
|
+
pink: 'var(--badge-pink)',
|
|
32
|
+
rose: 'var(--badge-rose)',
|
|
33
|
+
} as const
|
|
34
|
+
|
|
35
|
+
type BadgeColor = keyof typeof badgeColors
|
|
36
|
+
|
|
37
|
+
type BadgeVariant = 'solid' | 'dot' | 'outline'
|
|
38
|
+
|
|
39
|
+
const badgeVariants = cva('inline-flex items-center weight-medium whitespace-nowrap', {
|
|
40
|
+
variants: {
|
|
41
|
+
variant: {
|
|
42
|
+
solid: '',
|
|
43
|
+
dot: 'border border-border text-foreground',
|
|
44
|
+
// The dot badge without its dot: a neutral hairline label.
|
|
45
|
+
outline: 'border border-border text-foreground',
|
|
46
|
+
},
|
|
47
|
+
// The two-step size ladder shared by every control — see /docs/sizes.
|
|
48
|
+
size: {
|
|
49
|
+
default: 'h-6 px-2.5 text-caption gap-1.5',
|
|
50
|
+
compact: 'h-5 px-2 text-caption-compact gap-1',
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
defaultVariants: {
|
|
54
|
+
variant: 'solid',
|
|
55
|
+
size: 'default',
|
|
56
|
+
},
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
type BadgeSizeCanonical = 'default' | 'compact'
|
|
60
|
+
|
|
61
|
+
/** Public size values: the canonical two-size scale plus the pre-sizes-system
|
|
62
|
+
* aliases, kept so existing call sites keep compiling. Aliases resolve onto
|
|
63
|
+
* the canonical ladder (sm → compact; md/lg → default). */
|
|
64
|
+
type BadgeSize = BadgeSizeCanonical | 'sm' | 'md' | 'lg'
|
|
65
|
+
|
|
66
|
+
const legacySizeAliases: Partial<Record<BadgeSize, BadgeSizeCanonical>> = {
|
|
67
|
+
sm: 'compact',
|
|
68
|
+
md: 'default',
|
|
69
|
+
lg: 'default',
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export type { BadgeColor, BadgeSize, BadgeSizeCanonical, BadgeVariant }
|
|
73
|
+
export { badgeColors, badgeVariants, legacySizeAliases }
|