@remix-run/ui 0.3.0 → 0.5.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 +116 -157
- package/dist/accordion/index.d.ts +46 -0
- package/dist/accordion/index.js +142 -0
- package/dist/accordion/index.js.map +1 -0
- package/dist/accordion/primitives.d.ts +104 -0
- package/dist/accordion/primitives.js +312 -0
- package/dist/accordion/primitives.js.map +1 -0
- package/dist/{components/anchor/anchor.js → anchor/index.js} +1 -1
- package/dist/anchor/index.js.map +1 -0
- package/dist/animation/animate-layout-mixin.js +1 -1
- package/dist/animation/animate-layout-mixin.js.map +1 -1
- package/dist/animation/animate-mixins.js +2 -2
- package/dist/animation/animate-mixins.js.map +1 -1
- package/dist/animation/demos/color-interpolation.js +1 -1
- package/dist/animation/demos/color-interpolation.js.map +1 -1
- package/dist/animation/demos/drag-release.d.ts +1 -1
- package/dist/animation/demos/hold-to-confirm.d.ts +3 -3
- package/dist/animation/demos/material-ripple.js +1 -3
- package/dist/animation/demos/material-ripple.js.map +1 -1
- package/dist/animation/demos/reordering.js +9 -6
- package/dist/animation/demos/reordering.js.map +1 -1
- package/dist/animation/index.js +4 -4
- package/dist/animation/spring.js.map +1 -1
- package/dist/{components/breadcrumbs/breadcrumbs.js → breadcrumbs/index.js} +19 -19
- package/dist/breadcrumbs/index.js.map +1 -0
- package/dist/button/index.d.ts +15 -0
- package/dist/button/index.js +128 -0
- package/dist/button/index.js.map +1 -0
- package/dist/checkbox/index.d.ts +14 -0
- package/dist/checkbox/index.js +127 -0
- package/dist/checkbox/index.js.map +1 -0
- package/dist/combobox/index.d.ts +21 -0
- package/dist/combobox/index.js +58 -0
- package/dist/combobox/index.js.map +1 -0
- package/dist/{components/combobox/combobox.d.ts → combobox/primitives.d.ts} +4 -8
- package/dist/{components/combobox/combobox.js → combobox/primitives.js} +8 -61
- package/dist/combobox/primitives.js.map +1 -0
- package/dist/dev/refresh.d.ts +1 -0
- package/dist/dev/refresh.js +2 -0
- package/dist/dev/refresh.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +14 -14
- package/dist/input/index.d.ts +13 -0
- package/dist/input/index.js +121 -0
- package/dist/input/index.js.map +1 -0
- package/dist/jsx-runtime.js +2 -2
- package/dist/listbox/flash-attribute.js.map +1 -0
- package/dist/{components/listbox/listbox.d.ts → listbox/index.d.ts} +2 -6
- package/dist/{components/listbox/listbox.js → listbox/index.js} +4 -82
- package/dist/listbox/index.js.map +1 -0
- package/dist/menu/hover-aim.js.map +1 -0
- package/dist/menu/index.d.ts +42 -0
- package/dist/menu/index.js +250 -0
- package/dist/menu/index.js.map +1 -0
- package/dist/{components/menu/menu.d.ts → menu/primitives.d.ts} +4 -33
- package/dist/{components/menu/menu.js → menu/primitives.js} +98 -315
- package/dist/menu/primitives.js.map +1 -0
- package/dist/{components/popover/popover.d.ts → popover/index.d.ts} +2 -4
- package/dist/{components/popover/popover.js → popover/index.js} +5 -48
- package/dist/popover/index.js.map +1 -0
- package/dist/{interactions/outside-click/outside-click-mixin.js → popover/outside-click.js} +1 -1
- package/dist/popover/outside-click.js.map +1 -0
- package/dist/popover/scroll-lock.d.ts +1 -0
- package/dist/{utils → popover}/scroll-lock.js +0 -18
- package/dist/popover/scroll-lock.js.map +1 -0
- package/dist/radio/index.d.ts +12 -0
- package/dist/radio/index.js +77 -0
- package/dist/radio/index.js.map +1 -0
- package/dist/runtime/client-entry-boundary.d.ts +14 -0
- package/dist/runtime/client-entry-boundary.js +29 -0
- package/dist/runtime/client-entry-boundary.js.map +1 -0
- package/dist/runtime/component.d.ts +19 -4
- package/dist/runtime/component.js +39 -9
- package/dist/runtime/component.js.map +1 -1
- package/dist/runtime/core/attributes.js +36 -3
- package/dist/runtime/core/attributes.js.map +1 -1
- package/dist/runtime/core/children.d.ts +2 -0
- package/dist/runtime/core/children.js +26 -2
- package/dist/runtime/core/children.js.map +1 -1
- package/dist/runtime/core/props.js +2 -2
- package/dist/runtime/core/vnode.d.ts +3 -3
- package/dist/runtime/core/vnode.js +22 -1
- package/dist/runtime/core/vnode.js.map +1 -1
- package/dist/runtime/create-element.d.ts +2 -2
- package/dist/runtime/create-element.js +3 -2
- package/dist/runtime/create-element.js.map +1 -1
- package/dist/runtime/diff-dom.js +294 -219
- package/dist/runtime/diff-dom.js.map +1 -1
- package/dist/runtime/diff-props.js +3 -3
- package/dist/runtime/dom.d.ts +12 -0
- package/dist/runtime/element-function.d.ts +3 -0
- package/dist/runtime/element-function.js +2 -0
- package/dist/runtime/element-function.js.map +1 -0
- package/dist/runtime/form-navigation.d.ts +25 -0
- package/dist/runtime/form-navigation.js +132 -0
- package/dist/runtime/form-navigation.js.map +1 -0
- package/dist/runtime/frame-resolution.d.ts +5 -0
- package/dist/runtime/frame-resolution.js +10 -0
- package/dist/runtime/frame-resolution.js.map +1 -0
- package/dist/runtime/frame.d.ts +58 -15
- package/dist/runtime/frame.js +367 -114
- package/dist/runtime/frame.js.map +1 -1
- package/dist/runtime/jsx.d.ts +1 -1
- package/dist/runtime/jsx.js +1 -1
- package/dist/runtime/jsx.js.map +1 -1
- package/dist/runtime/key.d.ts +1 -0
- package/dist/runtime/key.js +2 -0
- package/dist/runtime/key.js.map +1 -0
- package/dist/runtime/mixins/attrs-mixin.js +2 -2
- package/dist/runtime/mixins/link-mixin.js +4 -3
- package/dist/runtime/mixins/link-mixin.js.map +1 -1
- package/dist/runtime/mixins/mixin.d.ts +15 -10
- package/dist/runtime/mixins/mixin.js +4 -4
- package/dist/runtime/mixins/mixin.js.map +1 -1
- package/dist/runtime/mixins/on-mixin.js +1 -1
- package/dist/runtime/mixins/ref-mixin.js +1 -1
- package/dist/runtime/navigation.d.ts +4 -1
- package/dist/runtime/navigation.js +157 -32
- package/dist/runtime/navigation.js.map +1 -1
- package/dist/runtime/reconcile.d.ts +11 -10
- package/dist/runtime/reconcile.js +557 -423
- package/dist/runtime/reconcile.js.map +1 -1
- package/dist/runtime/refresh.d.ts +28 -0
- package/dist/runtime/refresh.js +31 -0
- package/dist/runtime/refresh.js.map +1 -0
- package/dist/runtime/render.js +1 -1
- package/dist/runtime/render.js.map +1 -1
- package/dist/runtime/run.d.ts +3 -1
- package/dist/runtime/run.js +15 -7
- package/dist/runtime/run.js.map +1 -1
- package/dist/runtime/scheduler.js +8 -16
- package/dist/runtime/scheduler.js.map +1 -1
- package/dist/runtime/to-vnode.d.ts +2 -2
- package/dist/runtime/to-vnode.js +68 -20
- package/dist/runtime/to-vnode.js.map +1 -1
- package/dist/runtime/vdom.d.ts +4 -2
- package/dist/runtime/vdom.js +89 -41
- package/dist/runtime/vdom.js.map +1 -1
- package/dist/runtime/vnode.d.ts +138 -67
- package/dist/runtime/vnode.js +17 -12
- package/dist/runtime/vnode.js.map +1 -1
- package/dist/select/index.d.ts +19 -0
- package/dist/select/index.js +95 -0
- package/dist/select/index.js.map +1 -0
- package/dist/select/keydown.js.map +1 -0
- package/dist/{components/select/select.d.ts → select/primitives.d.ts} +5 -7
- package/dist/{components/select/select.js → select/primitives.js} +11 -63
- package/dist/select/primitives.js.map +1 -0
- package/dist/server/stream.js +22 -29
- package/dist/server/stream.js.map +1 -1
- package/dist/shared/focus-styles.d.ts +1 -0
- package/dist/shared/focus-styles.js +2 -0
- package/dist/shared/focus-styles.js.map +1 -0
- package/dist/shared/icons.d.ts +9 -0
- package/dist/shared/icons.js +42 -0
- package/dist/shared/icons.js.map +1 -0
- package/dist/shared/listbox-popover-styles.d.ts +6 -0
- package/dist/shared/listbox-popover-styles.js +137 -0
- package/dist/shared/listbox-popover-styles.js.map +1 -0
- package/dist/shared/style-values.d.ts +78 -0
- package/dist/shared/style-values.js +88 -0
- package/dist/shared/style-values.js.map +1 -0
- package/dist/{interactions/typeahead/typeahead-mixin.js → shared/typeahead.js} +1 -1
- package/dist/shared/typeahead.js.map +1 -0
- package/dist/shared/wait-for-css-transition.js.map +1 -0
- package/dist/shared/wait.js.map +1 -0
- package/dist/style/css-mixin.js +3 -3
- package/dist/style/index.js +2 -2
- package/dist/style/style.js +25 -6
- package/dist/style/style.js.map +1 -1
- package/dist/style/stylesheet.d.ts +0 -1
- package/dist/style/stylesheet.js +13 -54
- package/dist/style/stylesheet.js.map +1 -1
- package/dist/tabs/index.d.ts +32 -0
- package/dist/tabs/index.js +157 -0
- package/dist/tabs/index.js.map +1 -0
- package/dist/tabs/primitives.d.ts +57 -0
- package/dist/tabs/primitives.js +238 -0
- package/dist/tabs/primitives.js.map +1 -0
- package/dist/test.js +1 -1
- package/dist/toggle/index.d.ts +9 -0
- package/dist/toggle/index.js +108 -0
- package/dist/toggle/index.js.map +1 -0
- package/dist/toggle/primitives.d.ts +29 -0
- package/dist/toggle/primitives.js +127 -0
- package/dist/toggle/primitives.js.map +1 -0
- package/package.json +81 -49
- package/src/{components/accordion → accordion}/README.md +50 -14
- package/src/accordion/index.tsx +285 -0
- package/src/accordion/primitives.tsx +512 -0
- package/src/{components/anchor → anchor}/README.md +37 -2
- package/src/animation/animate-mixins.ts +3 -3
- package/src/animation/demos/color-interpolation.tsx +1 -1
- package/src/animation/demos/reordering.tsx +9 -6
- package/src/{components/breadcrumbs → breadcrumbs}/README.md +4 -4
- package/src/{components/breadcrumbs/breadcrumbs.tsx → breadcrumbs/index.tsx} +18 -18
- package/src/button/README.md +44 -0
- package/src/button/index.ts +168 -0
- package/src/checkbox/README.md +59 -0
- package/src/checkbox/index.ts +164 -0
- package/src/{components/combobox → combobox}/README.md +58 -9
- package/src/combobox/index.tsx +112 -0
- package/src/{components/combobox/combobox.tsx → combobox/primitives.tsx} +5 -91
- package/src/dev/refresh.ts +1 -0
- package/src/index.ts +1 -1
- package/src/input/README.md +52 -0
- package/src/input/index.ts +147 -0
- package/src/{components/listbox → listbox}/README.md +9 -41
- package/src/{components/listbox/listbox.ts → listbox/index.ts} +2 -87
- package/src/{components/menu → menu}/README.md +55 -14
- package/src/menu/index.tsx +354 -0
- package/src/{components/menu/menu.tsx → menu/primitives.tsx} +99 -413
- package/src/{components/popover → popover}/README.md +20 -39
- package/src/{components/popover/popover.ts → popover/index.ts} +3 -57
- package/src/{utils → popover}/scroll-lock.ts +0 -28
- package/src/radio/README.md +53 -0
- package/src/radio/index.ts +101 -0
- package/src/runtime/client-entry-boundary.ts +54 -0
- package/src/runtime/component.ts +65 -16
- package/src/runtime/core/attributes.ts +35 -1
- package/src/runtime/core/children.ts +24 -2
- package/src/runtime/core/vnode.ts +25 -4
- package/src/runtime/create-element.ts +6 -5
- package/src/runtime/demos/readme.demo.tsx +20 -18
- package/src/runtime/diff-dom.ts +358 -243
- package/src/runtime/dom.ts +331 -214
- package/src/runtime/element-function.ts +1 -0
- package/src/runtime/event-listeners.ts +1 -1
- package/src/runtime/form-navigation.ts +184 -0
- package/src/runtime/frame-resolution.ts +13 -0
- package/src/runtime/frame.ts +482 -124
- package/src/runtime/jsx.ts +3 -5
- package/src/runtime/key.ts +1 -0
- package/src/runtime/mixins/link-mixin.ts +1 -0
- package/src/runtime/mixins/mixin.ts +21 -18
- package/src/runtime/navigation.ts +204 -30
- package/src/runtime/reconcile.ts +769 -760
- package/src/runtime/refresh.ts +46 -0
- package/src/runtime/run.ts +12 -2
- package/src/runtime/scheduler.ts +9 -29
- package/src/runtime/to-vnode.ts +85 -23
- package/src/runtime/vdom.ts +91 -69
- package/src/runtime/vnode.ts +192 -88
- package/src/{components/select → select}/README.md +29 -19
- package/src/select/index.tsx +159 -0
- package/src/{components/select/select.tsx → select/primitives.tsx} +10 -98
- package/src/server/stream.ts +25 -31
- package/src/shared/focus-styles.ts +2 -0
- package/src/shared/icons.tsx +62 -0
- package/src/shared/listbox-popover-styles.ts +146 -0
- package/src/shared/style-values.ts +165 -0
- package/src/style/style.ts +25 -6
- package/src/style/stylesheet.ts +32 -57
- package/src/tabs/README.md +141 -0
- package/src/tabs/index.tsx +245 -0
- package/src/tabs/primitives.ts +365 -0
- package/src/test/README.md +151 -60
- package/src/toggle/README.md +56 -0
- package/src/toggle/index.tsx +136 -0
- package/src/toggle/primitives.ts +192 -0
- package/dist/animation/demos/animation.demo.d.ts +0 -5
- package/dist/animation/demos/animation.demo.js +0 -92
- package/dist/animation/demos/animation.demo.js.map +0 -1
- package/dist/animation/demos/spring.demo.d.ts +0 -6
- package/dist/animation/demos/spring.demo.js +0 -304
- package/dist/animation/demos/spring.demo.js.map +0 -1
- package/dist/components/accordion/accordion.d.ts +0 -92
- package/dist/components/accordion/accordion.js +0 -335
- package/dist/components/accordion/accordion.js.map +0 -1
- package/dist/components/accordion/demos/card.demo.d.ts +0 -7
- package/dist/components/accordion/demos/card.demo.js +0 -59
- package/dist/components/accordion/demos/card.demo.js.map +0 -1
- package/dist/components/accordion/demos/multiple.demo.d.ts +0 -7
- package/dist/components/accordion/demos/multiple.demo.js +0 -20
- package/dist/components/accordion/demos/multiple.demo.js.map +0 -1
- package/dist/components/accordion/demos/overview.demo.d.ts +0 -7
- package/dist/components/accordion/demos/overview.demo.js +0 -20
- package/dist/components/accordion/demos/overview.demo.js.map +0 -1
- package/dist/components/anchor/anchor.js.map +0 -1
- package/dist/components/breadcrumbs/basic.demo.d.ts +0 -6
- package/dist/components/breadcrumbs/basic.demo.js +0 -15
- package/dist/components/breadcrumbs/basic.demo.js.map +0 -1
- package/dist/components/breadcrumbs/breadcrumbs.js.map +0 -1
- package/dist/components/breadcrumbs/separator.demo.d.ts +0 -6
- package/dist/components/breadcrumbs/separator.demo.js +0 -15
- package/dist/components/breadcrumbs/separator.demo.js.map +0 -1
- package/dist/components/button/button.d.ts +0 -105
- package/dist/components/button/button.js +0 -207
- package/dist/components/button/button.js.map +0 -1
- package/dist/components/button/demos/aliases.demo.d.ts +0 -7
- package/dist/components/button/demos/aliases.demo.js +0 -19
- package/dist/components/button/demos/aliases.demo.js.map +0 -1
- package/dist/components/button/demos/basic.demo.d.ts +0 -7
- package/dist/components/button/demos/basic.demo.js +0 -20
- package/dist/components/button/demos/basic.demo.js.map +0 -1
- package/dist/components/button/demos/states.demo.d.ts +0 -7
- package/dist/components/button/demos/states.demo.js +0 -30
- package/dist/components/button/demos/states.demo.js.map +0 -1
- package/dist/components/combobox/combobox.js.map +0 -1
- package/dist/components/combobox/overview.demo.d.ts +0 -7
- package/dist/components/combobox/overview.demo.js +0 -154
- package/dist/components/combobox/overview.demo.js.map +0 -1
- package/dist/components/glyph/glyph.d.ts +0 -14
- package/dist/components/glyph/glyph.js +0 -65
- package/dist/components/glyph/glyph.js.map +0 -1
- package/dist/components/listbox/listbox.js.map +0 -1
- package/dist/components/listbox/overview.demo.d.ts +0 -7
- package/dist/components/listbox/overview.demo.js +0 -65
- package/dist/components/listbox/overview.demo.js.map +0 -1
- package/dist/components/menu/demos/bubbling.demo.d.ts +0 -6
- package/dist/components/menu/demos/bubbling.demo.js +0 -16
- package/dist/components/menu/demos/bubbling.demo.js.map +0 -1
- package/dist/components/menu/demos/context-trigger.demo.d.ts +0 -7
- package/dist/components/menu/demos/context-trigger.demo.js +0 -87
- package/dist/components/menu/demos/context-trigger.demo.js.map +0 -1
- package/dist/components/menu/demos/overview.demo.d.ts +0 -7
- package/dist/components/menu/demos/overview.demo.js +0 -33
- package/dist/components/menu/demos/overview.demo.js.map +0 -1
- package/dist/components/menu/hover-aim.js.map +0 -1
- package/dist/components/menu/menu.js.map +0 -1
- package/dist/components/popover/overview.demo.d.ts +0 -7
- package/dist/components/popover/overview.demo.js +0 -65
- package/dist/components/popover/overview.demo.js.map +0 -1
- package/dist/components/popover/popover.js.map +0 -1
- package/dist/components/select/deconstructed.demo.d.ts +0 -7
- package/dist/components/select/deconstructed.demo.js +0 -54
- package/dist/components/select/deconstructed.demo.js.map +0 -1
- package/dist/components/select/overview.demo.d.ts +0 -6
- package/dist/components/select/overview.demo.js +0 -28
- package/dist/components/select/overview.demo.js.map +0 -1
- package/dist/components/select/select.js.map +0 -1
- package/dist/components/separator/separator.d.ts +0 -2
- package/dist/components/separator/separator.js +0 -15
- package/dist/components/separator/separator.js.map +0 -1
- package/dist/components/tabs/tabs.d.ts +0 -78
- package/dist/components/tabs/tabs.js +0 -339
- package/dist/components/tabs/tabs.js.map +0 -1
- package/dist/interactions/keydown/keydown.js.map +0 -1
- package/dist/interactions/outside-click/outside-click-mixin.js.map +0 -1
- package/dist/interactions/typeahead/typeahead-mixin.js.map +0 -1
- package/dist/runtime/demos/basic.demo.d.ts +0 -6
- package/dist/runtime/demos/basic.demo.js +0 -16
- package/dist/runtime/demos/basic.demo.js.map +0 -1
- package/dist/runtime/demos/controlled-uncontrolled-values.demo.d.ts +0 -6
- package/dist/runtime/demos/controlled-uncontrolled-values.demo.js +0 -77
- package/dist/runtime/demos/controlled-uncontrolled-values.demo.js.map +0 -1
- package/dist/runtime/demos/keyed-list.demo.d.ts +0 -6
- package/dist/runtime/demos/keyed-list.demo.js +0 -62
- package/dist/runtime/demos/keyed-list.demo.js.map +0 -1
- package/dist/runtime/demos/readme.demo.d.ts +0 -5
- package/dist/runtime/demos/readme.demo.js +0 -396
- package/dist/runtime/demos/readme.demo.js.map +0 -1
- package/dist/theme/contract.d.ts +0 -61
- package/dist/theme/contract.js +0 -131
- package/dist/theme/contract.js.map +0 -1
- package/dist/theme/glyph-contract.d.ts +0 -11
- package/dist/theme/glyph-contract.js +0 -34
- package/dist/theme/glyph-contract.js.map +0 -1
- package/dist/theme/presets/rmx-01/glyphs.d.ts +0 -2
- package/dist/theme/presets/rmx-01/glyphs.js +0 -252
- package/dist/theme/presets/rmx-01/glyphs.js.map +0 -1
- package/dist/theme/presets/rmx-01/index.d.ts +0 -4
- package/dist/theme/presets/rmx-01/index.js +0 -119
- package/dist/theme/presets/rmx-01/index.js.map +0 -1
- package/dist/theme/runtime.d.ts +0 -2
- package/dist/theme/runtime.js +0 -84
- package/dist/theme/runtime.js.map +0 -1
- package/dist/theme/theme.d.ts +0 -6
- package/dist/theme/theme.js +0 -5
- package/dist/theme/theme.js.map +0 -1
- package/dist/utils/flash-attribute.js.map +0 -1
- package/dist/utils/scroll-lock.d.ts +0 -3
- package/dist/utils/scroll-lock.js.map +0 -1
- package/dist/utils/wait-for-css-transition.js.map +0 -1
- package/dist/utils/wait.js.map +0 -1
- package/src/animation/demos/animation.demo.tsx +0 -194
- package/src/animation/demos/spring.demo.tsx +0 -385
- package/src/components/accordion/accordion.tsx +0 -565
- package/src/components/accordion/demos/card.demo.tsx +0 -114
- package/src/components/accordion/demos/multiple.demo.tsx +0 -57
- package/src/components/accordion/demos/overview.demo.tsx +0 -57
- package/src/components/breadcrumbs/basic.demo.tsx +0 -18
- package/src/components/breadcrumbs/separator.demo.tsx +0 -19
- package/src/components/button/README.md +0 -44
- package/src/components/button/button.tsx +0 -286
- package/src/components/button/demos/aliases.demo.tsx +0 -28
- package/src/components/button/demos/basic.demo.tsx +0 -31
- package/src/components/button/demos/states.demo.tsx +0 -53
- package/src/components/combobox/overview.demo.tsx +0 -200
- package/src/components/glyph/README.md +0 -72
- package/src/components/glyph/glyph.tsx +0 -110
- package/src/components/listbox/overview.demo.tsx +0 -102
- package/src/components/menu/demos/bubbling.demo.tsx +0 -35
- package/src/components/menu/demos/context-trigger.demo.tsx +0 -133
- package/src/components/menu/demos/overview.demo.tsx +0 -77
- package/src/components/popover/overview.demo.tsx +0 -144
- package/src/components/select/deconstructed.demo.tsx +0 -96
- package/src/components/select/overview.demo.tsx +0 -52
- package/src/components/separator/separator.ts +0 -19
- package/src/components/tabs/README.md +0 -105
- package/src/components/tabs/tabs.tsx +0 -504
- package/src/runtime/demos/basic.demo.tsx +0 -21
- package/src/runtime/demos/controlled-uncontrolled-values.demo.tsx +0 -177
- package/src/runtime/demos/keyed-list.demo.tsx +0 -94
- package/src/theme/README.md +0 -103
- package/src/theme/contract.ts +0 -215
- package/src/theme/glyph-contract.ts +0 -59
- package/src/theme/presets/rmx-01/glyphs.tsx +0 -308
- package/src/theme/presets/rmx-01/index.ts +0 -123
- package/src/theme/runtime.ts +0 -119
- package/src/theme/theme.ts +0 -15
- package/src/utils/scroll-lock/README.md +0 -33
- /package/dist/{components/anchor/anchor.d.ts → anchor/index.d.ts} +0 -0
- /package/dist/{components/breadcrumbs/breadcrumbs.d.ts → breadcrumbs/index.d.ts} +0 -0
- /package/dist/{utils → listbox}/flash-attribute.d.ts +0 -0
- /package/dist/{utils → listbox}/flash-attribute.js +0 -0
- /package/dist/{components/menu → menu}/hover-aim.d.ts +0 -0
- /package/dist/{components/menu → menu}/hover-aim.js +0 -0
- /package/dist/{interactions/outside-click/outside-click-mixin.d.ts → popover/outside-click.d.ts} +0 -0
- /package/dist/{interactions/keydown → select}/keydown.d.ts +0 -0
- /package/dist/{interactions/keydown → select}/keydown.js +0 -0
- /package/dist/{interactions/typeahead/typeahead-mixin.d.ts → shared/typeahead.d.ts} +0 -0
- /package/dist/{utils → shared}/wait-for-css-transition.d.ts +0 -0
- /package/dist/{utils → shared}/wait-for-css-transition.js +0 -0
- /package/dist/{utils → shared}/wait.d.ts +0 -0
- /package/dist/{utils → shared}/wait.js +0 -0
- /package/src/{components/anchor/anchor.ts → anchor/index.ts} +0 -0
- /package/src/{utils → listbox}/flash-attribute.ts +0 -0
- /package/src/{components/menu → menu}/hover-aim.ts +0 -0
- /package/src/{interactions/outside-click/outside-click-mixin.ts → popover/outside-click.ts} +0 -0
- /package/src/{interactions/keydown → select}/keydown.ts +0 -0
- /package/src/{interactions/typeahead/typeahead-mixin.ts → shared/typeahead.ts} +0 -0
- /package/src/{utils → shared}/wait-for-css-transition.ts +0 -0
- /package/src/{utils → shared}/wait.ts +0 -0
package/src/style/style.ts
CHANGED
|
@@ -19,8 +19,22 @@ export interface CSSProps extends DOMStyleProperties {
|
|
|
19
19
|
type StyleObject = Record<string, unknown>
|
|
20
20
|
|
|
21
21
|
// Convert camelCase CSS properties to kebab-case
|
|
22
|
+
// Property names repeat constantly across renders; cache conversions instead
|
|
23
|
+
// of running a regex each time.
|
|
24
|
+
const CAMEL_TO_KEBAB_CACHE_LIMIT = 256
|
|
25
|
+
const camelToKebabCache = new Map<string, string>()
|
|
26
|
+
|
|
22
27
|
function camelToKebab(str: string): string {
|
|
23
|
-
|
|
28
|
+
let cached = camelToKebabCache.get(str)
|
|
29
|
+
if (cached === undefined) {
|
|
30
|
+
cached = str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)
|
|
31
|
+
if (camelToKebabCache.size >= CAMEL_TO_KEBAB_CACHE_LIMIT) {
|
|
32
|
+
let oldest = camelToKebabCache.keys().next()
|
|
33
|
+
if (!oldest.done) camelToKebabCache.delete(oldest.value)
|
|
34
|
+
}
|
|
35
|
+
camelToKebabCache.set(str, cached)
|
|
36
|
+
}
|
|
37
|
+
return cached
|
|
24
38
|
}
|
|
25
39
|
|
|
26
40
|
// Properties that should remain unitless (numeric values without px)
|
|
@@ -81,18 +95,23 @@ function isKeyframesAtRule(key: string): boolean {
|
|
|
81
95
|
)
|
|
82
96
|
}
|
|
83
97
|
|
|
84
|
-
// Generate a hash for style objects to create unique class names
|
|
98
|
+
// Generate a hash for style objects to create unique class names.
|
|
99
|
+
// Class names are content-addressed — a collision silently applies the wrong
|
|
100
|
+
// styles — so use two independent 32-bit FNV-1a passes (~64 bits of hash
|
|
101
|
+
// space) rather than a single 32-bit hash. Must be deterministic across
|
|
102
|
+
// server and client.
|
|
85
103
|
function hashStyle(obj: any): string {
|
|
86
104
|
// Sort keys to ensure consistent hashing, but include values in the string
|
|
87
105
|
let sortedEntries = Object.entries(obj).sort(([a], [b]) => a.localeCompare(b))
|
|
88
106
|
let str = JSON.stringify(sortedEntries)
|
|
89
|
-
let
|
|
107
|
+
let h1 = 0x811c9dc5
|
|
108
|
+
let h2 = 0xcbf29ce4
|
|
90
109
|
for (let i = 0; i < str.length; i++) {
|
|
91
110
|
let char = str.charCodeAt(i)
|
|
92
|
-
|
|
93
|
-
|
|
111
|
+
h1 = Math.imul(h1 ^ char, 0x01000193) >>> 0
|
|
112
|
+
h2 = Math.imul(h2 ^ char, 0x01000193) >>> 0
|
|
94
113
|
}
|
|
95
|
-
return
|
|
114
|
+
return h1.toString(36) + h2.toString(36)
|
|
96
115
|
}
|
|
97
116
|
|
|
98
117
|
// Convert style object to CSS text
|
package/src/style/stylesheet.ts
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
import { REMIX_UI_STYLE_LAYER } from './layers.ts'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// # Style ownership model
|
|
4
|
+
//
|
|
5
|
+
// Rules are content-addressed: a class name is a hash of the style object that
|
|
6
|
+
// produced it, so `.rmxc-x` always means the same declarations. A rule can
|
|
7
|
+
// never become *wrong* — only unused. That observation drives a two-tier
|
|
8
|
+
// lifetime model on a single document-level registry:
|
|
9
|
+
//
|
|
10
|
+
// - **Server-adopted rules are pinned.** Once a `<style data-rmx>` tag is
|
|
11
|
+
// adopted, its rule stays for the life of the manager. Frame reloads,
|
|
12
|
+
// island hydration, and streamed templates never need to agree on which
|
|
13
|
+
// scope "owns" a shared rule — adoption is additive and idempotent, and the
|
|
14
|
+
// registry is bounded by the set of unique style objects the server ever
|
|
15
|
+
// renders, not by render count.
|
|
16
|
+
//
|
|
17
|
+
// - **Client-inserted rules are refcounted.** Dynamic style objects (e.g.
|
|
18
|
+
// interpolated values) mint a new class per distinct value, so rules that
|
|
19
|
+
// only ever existed client-side are dropped when the last css mixin using
|
|
20
|
+
// them releases its ref. A refcounted rule upgrades to pinned if a server
|
|
21
|
+
// tag for the same selector is adopted later.
|
|
22
|
+
|
|
23
|
+
type RuleEntry = { count: number; index: number; pinned: boolean }
|
|
4
24
|
export type ServerStyleSource = ParentNode | Iterable<Node>
|
|
5
25
|
|
|
6
26
|
export interface StyleManager {
|
|
@@ -10,7 +30,6 @@ export interface StyleManager {
|
|
|
10
30
|
getGeneration(): number
|
|
11
31
|
reset(): void
|
|
12
32
|
adoptServerStyles(source: ServerStyleSource): Set<string>
|
|
13
|
-
replaceServerStyles(source: ServerStyleSource): void
|
|
14
33
|
selectors(): IterableIterator<string>
|
|
15
34
|
dispose(): void
|
|
16
35
|
}
|
|
@@ -84,17 +103,8 @@ export function createStyleManager(layer: string = REMIX_UI_STYLE_LAYER): StyleM
|
|
|
84
103
|
let stylesheet: CSSStyleSheet | null = null
|
|
85
104
|
let generation = 0
|
|
86
105
|
|
|
87
|
-
// Track usage count and rule index per className
|
|
88
|
-
// Using an object to track both count and index together
|
|
89
106
|
let ruleMap = new Map<string, RuleEntry>()
|
|
90
107
|
|
|
91
|
-
// Selectors currently held by a server-style adoption ref. We track this
|
|
92
|
-
// separately from `ruleMap` so `replaceServerStyles` can release only the
|
|
93
|
-
// adoption refs of selectors absent from the next page, without disturbing
|
|
94
|
-
// selectors that exist solely because a client-side css mixin inserted them
|
|
95
|
-
// (e.g. transient UI state that the server never rendered).
|
|
96
|
-
let adoptedSelectors = new Set<string>()
|
|
97
|
-
|
|
98
108
|
function getStylesheet(): CSSStyleSheet {
|
|
99
109
|
if (!stylesheet) {
|
|
100
110
|
stylesheet = new CSSStyleSheet()
|
|
@@ -122,15 +132,11 @@ export function createStyleManager(layer: string = REMIX_UI_STYLE_LAYER): StyleM
|
|
|
122
132
|
let selector = getStyleSelector(styleEl)
|
|
123
133
|
if (!selector) return undefined
|
|
124
134
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
|
|
130
|
-
let entry = ruleMap.get(selector)!
|
|
131
|
-
entry.count++
|
|
132
|
-
adoptedSelectors.add(selector)
|
|
133
|
-
}
|
|
135
|
+
let entry = ruleMap.get(selector)
|
|
136
|
+
if (entry) {
|
|
137
|
+
// The rule already exists — either a previous adoption or a client-side
|
|
138
|
+
// css mixin inserted it first. Pin it so it outlives any mixin refs.
|
|
139
|
+
entry.pinned = true
|
|
134
140
|
styleEl.remove()
|
|
135
141
|
return selector
|
|
136
142
|
}
|
|
@@ -145,8 +151,7 @@ export function createStyleManager(layer: string = REMIX_UI_STYLE_LAYER): StyleM
|
|
|
145
151
|
let sheet = getStylesheet()
|
|
146
152
|
let index = sheet.cssRules.length
|
|
147
153
|
sheet.insertRule(cssText, index)
|
|
148
|
-
ruleMap.set(selector, { count:
|
|
149
|
-
adoptedSelectors.add(selector)
|
|
154
|
+
ruleMap.set(selector, { count: 0, index, pinned: true })
|
|
150
155
|
styleEl.remove()
|
|
151
156
|
return selector
|
|
152
157
|
} catch {
|
|
@@ -157,7 +162,7 @@ export function createStyleManager(layer: string = REMIX_UI_STYLE_LAYER): StyleM
|
|
|
157
162
|
|
|
158
163
|
function has(className: string) {
|
|
159
164
|
let entry = ruleMap.get(className)
|
|
160
|
-
return entry !== undefined && entry.count > 0
|
|
165
|
+
return entry !== undefined && (entry.pinned || entry.count > 0)
|
|
161
166
|
}
|
|
162
167
|
|
|
163
168
|
function getGeneration() {
|
|
@@ -168,8 +173,8 @@ export function createStyleManager(layer: string = REMIX_UI_STYLE_LAYER): StyleM
|
|
|
168
173
|
let entry = ruleMap.get(className)
|
|
169
174
|
|
|
170
175
|
if (entry) {
|
|
171
|
-
//
|
|
172
|
-
entry.count++
|
|
176
|
+
// Pinned rules are permanent; there is nothing to count.
|
|
177
|
+
if (!entry.pinned) entry.count++
|
|
173
178
|
return
|
|
174
179
|
}
|
|
175
180
|
|
|
@@ -179,13 +184,13 @@ export function createStyleManager(layer: string = REMIX_UI_STYLE_LAYER): StyleM
|
|
|
179
184
|
// This may throw for invalid CSS. If it does, we intentionally let it
|
|
180
185
|
// bubble so the rule is not tracked unless insertion actually succeeds.
|
|
181
186
|
sheet.insertRule(`@layer ${getStyleLayerName(className, layer)} { ${rule} }`, index)
|
|
182
|
-
ruleMap.set(className, { count: 1, index })
|
|
187
|
+
ruleMap.set(className, { count: 1, index, pinned: false })
|
|
183
188
|
}
|
|
184
189
|
|
|
185
190
|
function remove(className: string) {
|
|
186
191
|
let entry = ruleMap.get(className)
|
|
187
192
|
|
|
188
|
-
if (!entry) return
|
|
193
|
+
if (!entry || entry.pinned) return
|
|
189
194
|
|
|
190
195
|
// Decrement count
|
|
191
196
|
entry.count--
|
|
@@ -200,9 +205,7 @@ export function createStyleManager(layer: string = REMIX_UI_STYLE_LAYER): StyleM
|
|
|
200
205
|
|
|
201
206
|
// Remove from tracking
|
|
202
207
|
ruleMap.delete(className)
|
|
203
|
-
adoptedSelectors.delete(className)
|
|
204
208
|
|
|
205
|
-
// TODO: just search and remove, stop re-indexing
|
|
206
209
|
if (!stylesheet) return
|
|
207
210
|
stylesheet.deleteRule(indexToDelete)
|
|
208
211
|
|
|
@@ -218,7 +221,6 @@ export function createStyleManager(layer: string = REMIX_UI_STYLE_LAYER): StyleM
|
|
|
218
221
|
function reset() {
|
|
219
222
|
clearStylesheet()
|
|
220
223
|
ruleMap.clear()
|
|
221
|
-
adoptedSelectors.clear()
|
|
222
224
|
removeStylesheet()
|
|
223
225
|
generation++
|
|
224
226
|
}
|
|
@@ -235,31 +237,6 @@ export function createStyleManager(layer: string = REMIX_UI_STYLE_LAYER): StyleM
|
|
|
235
237
|
return adopted
|
|
236
238
|
}
|
|
237
239
|
|
|
238
|
-
// Snapshot the currently-adopted server selectors, adopt the incoming
|
|
239
|
-
// server styles additively, then release the adoption ref of any prior-only
|
|
240
|
-
// selectors.
|
|
241
|
-
//
|
|
242
|
-
// Refcount-aware semantics keep preserved DOM styled: a prior-only selector
|
|
243
|
-
// with no live css-mixin ref drops immediately, but one still referenced by
|
|
244
|
-
// an active mixin (e.g. inside a hydration region that the DOM diff skipped)
|
|
245
|
-
// stays in the stylesheet and is only fully removed when that mixin's
|
|
246
|
-
// `remove` event eventually fires. This avoids the FOUC that a hard
|
|
247
|
-
// `reset()` would cause between the style swap and the hydration re-render.
|
|
248
|
-
//
|
|
249
|
-
// Only adoption refs are considered — selectors that exist solely because a
|
|
250
|
-
// client-side css mixin inserted them (e.g. transient UI state never
|
|
251
|
-
// rendered by the server) are unaffected.
|
|
252
|
-
function replaceServerStyles(source: ServerStyleSource): void {
|
|
253
|
-
let prior = new Set(adoptedSelectors)
|
|
254
|
-
let adopted = adoptServerStyles(source)
|
|
255
|
-
for (let selector of prior) {
|
|
256
|
-
if (!adopted.has(selector)) {
|
|
257
|
-
adoptedSelectors.delete(selector)
|
|
258
|
-
remove(selector)
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
240
|
function selectors(): IterableIterator<string> {
|
|
264
241
|
return ruleMap.keys()
|
|
265
242
|
}
|
|
@@ -268,7 +245,6 @@ export function createStyleManager(layer: string = REMIX_UI_STYLE_LAYER): StyleM
|
|
|
268
245
|
removeStylesheet()
|
|
269
246
|
// Clear internal state
|
|
270
247
|
ruleMap.clear()
|
|
271
|
-
adoptedSelectors.clear()
|
|
272
248
|
generation++
|
|
273
249
|
}
|
|
274
250
|
|
|
@@ -279,7 +255,6 @@ export function createStyleManager(layer: string = REMIX_UI_STYLE_LAYER): StyleM
|
|
|
279
255
|
getGeneration,
|
|
280
256
|
reset,
|
|
281
257
|
adoptServerStyles,
|
|
282
|
-
replaceServerStyles,
|
|
283
258
|
selectors,
|
|
284
259
|
dispose,
|
|
285
260
|
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# tabs
|
|
2
|
+
|
|
3
|
+
`Tabs` renders a tab control with one active tab and matching panels. Use it when related views share the same page space and the owning component may either control the active tab or let the tabs context manage it.
|
|
4
|
+
|
|
5
|
+
## Component Usage
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Tabs, TabList, Tab, TabPanel } from 'remix/ui/tabs'
|
|
9
|
+
|
|
10
|
+
export function ProjectTabs() {
|
|
11
|
+
return (
|
|
12
|
+
<Tabs defaultActiveTab="overview">
|
|
13
|
+
<TabList aria-label="Project sections">
|
|
14
|
+
<Tab name="overview">Overview</Tab>
|
|
15
|
+
<Tab name="activity">Activity</Tab>
|
|
16
|
+
<Tab name="settings">Settings</Tab>
|
|
17
|
+
</TabList>
|
|
18
|
+
|
|
19
|
+
<TabPanel name="overview">Project summary.</TabPanel>
|
|
20
|
+
<TabPanel name="activity">Recent changes.</TabPanel>
|
|
21
|
+
<TabPanel name="settings">Project settings.</TabPanel>
|
|
22
|
+
</Tabs>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Control the active tab when state should live in the owning component:
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
import type { Handle } from 'remix/ui'
|
|
31
|
+
import { Tabs, TabList, Tab, TabPanel } from 'remix/ui/tabs'
|
|
32
|
+
|
|
33
|
+
export function ControlledTabs(handle: Handle) {
|
|
34
|
+
let activeTab = 'overview'
|
|
35
|
+
|
|
36
|
+
return () => (
|
|
37
|
+
<Tabs
|
|
38
|
+
activeTab={activeTab}
|
|
39
|
+
onActiveTabChange={(nextActiveTab) => {
|
|
40
|
+
activeTab = nextActiveTab
|
|
41
|
+
void handle.update()
|
|
42
|
+
}}
|
|
43
|
+
>
|
|
44
|
+
<TabList aria-label="Project sections">
|
|
45
|
+
<Tab name="overview">Overview</Tab>
|
|
46
|
+
<Tab name="activity">Activity</Tab>
|
|
47
|
+
</TabList>
|
|
48
|
+
|
|
49
|
+
<TabPanel name="overview">Project summary.</TabPanel>
|
|
50
|
+
<TabPanel name="activity">Recent changes.</TabPanel>
|
|
51
|
+
</Tabs>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Listen for bubbling `TabsChangeEvent` events with `onTabsChange` from `remix/ui/tabs/primitives`.
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
import { onTabsChange } from 'remix/ui/tabs/primitives'
|
|
60
|
+
import { Tabs, TabList, Tab, TabPanel } from 'remix/ui/tabs'
|
|
61
|
+
|
|
62
|
+
export function TrackedTabs() {
|
|
63
|
+
return (
|
|
64
|
+
<div
|
|
65
|
+
mix={[
|
|
66
|
+
onTabsChange((event) => {
|
|
67
|
+
console.log(event.previousActiveTab, event.activeTab)
|
|
68
|
+
}),
|
|
69
|
+
]}
|
|
70
|
+
>
|
|
71
|
+
<Tabs defaultActiveTab="overview">
|
|
72
|
+
<TabList aria-label="Project sections">
|
|
73
|
+
<Tab name="overview">Overview</Tab>
|
|
74
|
+
<Tab name="activity">Activity</Tab>
|
|
75
|
+
</TabList>
|
|
76
|
+
<TabPanel name="overview">Project summary.</TabPanel>
|
|
77
|
+
<TabPanel name="activity">Recent changes.</TabPanel>
|
|
78
|
+
</Tabs>
|
|
79
|
+
</div>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Primitive Usage
|
|
85
|
+
|
|
86
|
+
Use the lower-level primitives when app code owns the markup and styles:
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
import * as tabs from 'remix/ui/tabs/primitives'
|
|
90
|
+
import { listStyle, panelStyle, rootStyle, tabStyle } from './tabs.styles'
|
|
91
|
+
|
|
92
|
+
export function PrimitiveTabs() {
|
|
93
|
+
return (
|
|
94
|
+
<tabs.Context defaultActiveTab="overview">
|
|
95
|
+
<div mix={[rootStyle, tabs.root()]}>
|
|
96
|
+
<div mix={[listStyle, tabs.list()]} aria-label="Project sections">
|
|
97
|
+
<button mix={[tabStyle, tabs.tab({ name: 'overview' })]} type="button">
|
|
98
|
+
Overview
|
|
99
|
+
</button>
|
|
100
|
+
<button mix={[tabStyle, tabs.tab({ name: 'activity' })]} type="button">
|
|
101
|
+
Activity
|
|
102
|
+
</button>
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<div mix={[panelStyle, tabs.panel({ name: 'overview' })]}>Project summary.</div>
|
|
106
|
+
<div mix={[panelStyle, tabs.panel({ name: 'activity' })]}>Recent changes.</div>
|
|
107
|
+
</div>
|
|
108
|
+
</tabs.Context>
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## `remix/ui/tabs`
|
|
114
|
+
|
|
115
|
+
- `Tabs`: root component. Supports controlled `activeTab`, uncontrolled `defaultActiveTab`, `onActiveTabChange`, `disabled`, and `size: 'md' | 'lg'`.
|
|
116
|
+
- `TabList`: tablist wrapper that lays out the tabs in a track and applies the tablist primitive.
|
|
117
|
+
- `Tab`: button for one tab `name`. The active tab uses the toggle slider treatment and button-sized text. Pass `disabled` to prevent activation.
|
|
118
|
+
- `TabPanel`: panel for one tab `name`.
|
|
119
|
+
- `rootStyle`, `listStyle`, `tabStyle`, and `panelStyle`: flat style mixins used by the component markup.
|
|
120
|
+
- `onTabsChange` and `TabsChangeEvent`: re-exported primitive event helpers.
|
|
121
|
+
- `TabsProps`, `TabListProps`, `TabProps`, `TabPanelProps`, and `TabsSize`: public TypeScript types for the composed APIs.
|
|
122
|
+
|
|
123
|
+
## `remix/ui/tabs/primitives`
|
|
124
|
+
|
|
125
|
+
- `Context`: lower-level provider for custom tabs composition.
|
|
126
|
+
- `root()`: wires the root element and bubbling change events.
|
|
127
|
+
- `list()`: wires the tablist role and disabled state.
|
|
128
|
+
- `tab({ name, disabled })`: wires a tab button with selected state, ids, keyboard activation, and pointer activation.
|
|
129
|
+
- `panel({ name })`: wires the matching tab panel id, hidden state, inert state, and label relationship.
|
|
130
|
+
- `onTabsChange(...)`: event mixin for the bubbling `TabsChangeEvent`.
|
|
131
|
+
- `TabsChangeEvent`: bubbling event with `activeTab` and `previousActiveTab`.
|
|
132
|
+
- `TabsContextProps`, `TabsContextValue`, `TabsRegisteredTab`, `TabsActivationDirection`, `TabsRootOptions`, `TabListOptions`, `TabOptions`, and `TabPanelOptions`: primitive prop and option types for custom composition.
|
|
133
|
+
|
|
134
|
+
## Behavior Notes
|
|
135
|
+
|
|
136
|
+
- If neither `activeTab` nor `defaultActiveTab` is provided, the first enabled tab becomes active.
|
|
137
|
+
- Arrow keys activate the next or previous enabled tab. `Home` and `End` activate the first and last enabled tabs.
|
|
138
|
+
- `Enter`, `Space`, and pointer clicks activate the focused tab.
|
|
139
|
+
- Root `disabled` disables every tab. Tab `disabled` only disables that tab.
|
|
140
|
+
- Tabs and panels are linked with generated ids through `aria-controls`, `aria-labelledby`, and `aria-selected`.
|
|
141
|
+
- Inactive panels receive `hidden`, `inert`, and `data-state="inactive"`.
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import {
|
|
2
|
+
css,
|
|
3
|
+
type CSSMixinDescriptor,
|
|
4
|
+
type Handle,
|
|
5
|
+
type Props,
|
|
6
|
+
type RemixNode,
|
|
7
|
+
} from '@remix-run/ui'
|
|
8
|
+
import * as tabs from '@remix-run/ui/tabs/primitives'
|
|
9
|
+
|
|
10
|
+
export { TabsChangeEvent, onTabsChange } from '@remix-run/ui/tabs/primitives'
|
|
11
|
+
|
|
12
|
+
export type TabsSize = 'md' | 'lg'
|
|
13
|
+
|
|
14
|
+
export interface TabsProps extends Omit<Props<'div'>, 'children'> {
|
|
15
|
+
activeTab?: string
|
|
16
|
+
children?: RemixNode
|
|
17
|
+
defaultActiveTab?: string
|
|
18
|
+
disabled?: boolean
|
|
19
|
+
onActiveTabChange?: (activeTab: string) => void
|
|
20
|
+
size?: TabsSize
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface TabListProps extends Omit<Props<'div'>, 'children'> {
|
|
24
|
+
children?: RemixNode
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface TabProps extends Omit<Props<'button'>, 'children' | 'type'> {
|
|
28
|
+
children?: RemixNode
|
|
29
|
+
disabled?: boolean
|
|
30
|
+
name: string
|
|
31
|
+
type?: 'button' | 'submit' | 'reset'
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface TabPanelProps extends Omit<Props<'div'>, 'children'> {
|
|
35
|
+
children?: RemixNode
|
|
36
|
+
name: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const tabSliderBackground =
|
|
40
|
+
'linear-gradient(180deg, rgba(0, 0, 0, 0) 33%, light-dark(rgba(0, 0, 0, 0.04), rgba(255, 255, 255, 0.08)) 100%), light-dark(#FFFFFF, #1a1a1a)'
|
|
41
|
+
const tabSliderShadow =
|
|
42
|
+
'0 0 0 0.5px light-dark(rgba(0, 0, 0, 0.06), rgba(255, 255, 255, 0.12)), 0 1px 1px -0.5px rgba(0, 0, 0, 0.12), 0 2px 2px -1px rgba(0, 0, 0, 0.12), 0 4px 4px -2px rgba(0, 0, 0, 0.12), inset 0 0 2px 1px light-dark(#FFFFFF, rgba(255, 255, 255, 0.08))'
|
|
43
|
+
|
|
44
|
+
const tabsRootCss: CSSMixinDescriptor = css({
|
|
45
|
+
'--rmx-tabs-height': '32px',
|
|
46
|
+
'--rmx-tabs-track-padding': '2px',
|
|
47
|
+
'--rmx-tabs-tab-padding-inline': '12px',
|
|
48
|
+
'--rmx-tabs-tab-shadow': '0 0 0 0 rgba(0, 0, 0, 0)',
|
|
49
|
+
'--rmx-tabs-tab-font-size': '12px',
|
|
50
|
+
'--rmx-tabs-tab-line-height': '17px',
|
|
51
|
+
'--rmx-tabs-gap': '12px',
|
|
52
|
+
boxSizing: 'border-box',
|
|
53
|
+
display: 'grid',
|
|
54
|
+
gap: 'var(--rmx-tabs-gap)',
|
|
55
|
+
minWidth: 0,
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
const tabsListCss: CSSMixinDescriptor = css({
|
|
59
|
+
boxSizing: 'border-box',
|
|
60
|
+
display: 'inline-flex',
|
|
61
|
+
alignItems: 'center',
|
|
62
|
+
gap: '2px',
|
|
63
|
+
width: 'max-content',
|
|
64
|
+
maxWidth: '100%',
|
|
65
|
+
minHeight: 'var(--rmx-tabs-height)',
|
|
66
|
+
padding: 'var(--rmx-tabs-track-padding)',
|
|
67
|
+
border: 0,
|
|
68
|
+
borderRadius: '9999px',
|
|
69
|
+
background:
|
|
70
|
+
'linear-gradient(180deg, light-dark(rgba(0, 0, 0, 0.06), rgba(255, 255, 255, 0.08)) 0%, rgba(0, 0, 0, 0) 100%), light-dark(#EBEBEB, #2c2c2c)',
|
|
71
|
+
boxShadow:
|
|
72
|
+
'inset 0 0 4px 1px rgba(0, 0, 0, 0.08), inset 0 1px 1px rgba(0, 0, 0, 0.02), inset 0 2px 2px rgba(0, 0, 0, 0.02)',
|
|
73
|
+
overflowX: 'auto',
|
|
74
|
+
scrollbarWidth: 'none',
|
|
75
|
+
'&::-webkit-scrollbar': {
|
|
76
|
+
display: 'none',
|
|
77
|
+
},
|
|
78
|
+
'&[aria-disabled="true"]': {
|
|
79
|
+
opacity: 0.55,
|
|
80
|
+
},
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
const tabsTabCss: CSSMixinDescriptor = css({
|
|
84
|
+
'--rmx-tabs-tab-focus-shadow':
|
|
85
|
+
'0 0 0 1px light-dark(#3573F6, #6eaaff), var(--rmx-tabs-tab-shadow), 0 0 0 4px light-dark(rgba(53, 115, 246, 0.1), rgba(110, 170, 255, 0.18)), 0 6px 32px 4px light-dark(rgba(53, 115, 246, 0.08), rgba(110, 170, 255, 0.14)), inset 0 0 8px 1px light-dark(rgba(53, 115, 246, 0.05), rgba(110, 170, 255, 0.1))',
|
|
86
|
+
appearance: 'none',
|
|
87
|
+
margin: 0,
|
|
88
|
+
boxSizing: 'border-box',
|
|
89
|
+
position: 'relative',
|
|
90
|
+
cursor: 'pointer',
|
|
91
|
+
display: 'inline-flex',
|
|
92
|
+
alignItems: 'center',
|
|
93
|
+
justifyContent: 'center',
|
|
94
|
+
flexDirection: 'row',
|
|
95
|
+
gap: '4px',
|
|
96
|
+
width: 'max-content',
|
|
97
|
+
maxWidth: '100%',
|
|
98
|
+
minWidth: 0,
|
|
99
|
+
height: 'calc(var(--rmx-tabs-height) - (var(--rmx-tabs-track-padding) * 2))',
|
|
100
|
+
minHeight: 'calc(var(--rmx-tabs-height) - (var(--rmx-tabs-track-padding) * 2))',
|
|
101
|
+
paddingBlock: 0,
|
|
102
|
+
paddingInline: 'var(--rmx-tabs-tab-padding-inline)',
|
|
103
|
+
border: 0,
|
|
104
|
+
borderRadius: '999px',
|
|
105
|
+
background: 'transparent',
|
|
106
|
+
boxShadow: 'var(--rmx-tabs-tab-shadow)',
|
|
107
|
+
color: 'light-dark(#707070, #b3b3b3)',
|
|
108
|
+
fontFamily: '"Inter Variable", Inter, ui-sans-serif, system-ui, sans-serif',
|
|
109
|
+
fontStyle: 'normal',
|
|
110
|
+
fontWeight: 500,
|
|
111
|
+
fontSize: 'var(--rmx-tabs-tab-font-size)',
|
|
112
|
+
lineHeight: 'var(--rmx-tabs-tab-line-height)',
|
|
113
|
+
fontFeatureSettings: '"ss01" on, "cv01" on',
|
|
114
|
+
letterSpacing: 0,
|
|
115
|
+
textAlign: 'center',
|
|
116
|
+
textDecoration: 'none',
|
|
117
|
+
textShadow: 'none',
|
|
118
|
+
userSelect: 'none',
|
|
119
|
+
WebkitUserSelect: 'none',
|
|
120
|
+
verticalAlign: 'top',
|
|
121
|
+
whiteSpace: 'nowrap',
|
|
122
|
+
'&[data-state="inactive"]:hover:not(:disabled):not([aria-disabled="true"])': {
|
|
123
|
+
background: 'light-dark(rgba(16, 16, 16, 0.05), rgba(236, 236, 236, 0.1))',
|
|
124
|
+
color: 'light-dark(#101010, #ececec)',
|
|
125
|
+
},
|
|
126
|
+
'&[data-state="inactive"]:active:not(:disabled):not([aria-disabled="true"])': {
|
|
127
|
+
background: 'light-dark(rgba(16, 16, 16, 0.08), rgba(236, 236, 236, 0.14))',
|
|
128
|
+
},
|
|
129
|
+
'&[data-state="active"]': {
|
|
130
|
+
background: tabSliderBackground,
|
|
131
|
+
'--rmx-tabs-tab-shadow': tabSliderShadow,
|
|
132
|
+
color: 'light-dark(#101010, #ececec)',
|
|
133
|
+
textShadow: '0 1px 0 light-dark(#FFFFFF, rgb(0 0 0 / 0.35))',
|
|
134
|
+
},
|
|
135
|
+
'&[data-state="active"]:hover:not(:disabled):not([aria-disabled="true"])': {
|
|
136
|
+
background: tabSliderBackground,
|
|
137
|
+
},
|
|
138
|
+
'&[data-state="active"]:active:not(:disabled):not([aria-disabled="true"])': {
|
|
139
|
+
background: tabSliderBackground,
|
|
140
|
+
},
|
|
141
|
+
'&:disabled, &[aria-disabled="true"]': {
|
|
142
|
+
cursor: 'not-allowed',
|
|
143
|
+
opacity: 0.55,
|
|
144
|
+
},
|
|
145
|
+
'&:focus-visible': {
|
|
146
|
+
outline: 0,
|
|
147
|
+
boxShadow: 'var(--rmx-tabs-tab-focus-shadow)',
|
|
148
|
+
},
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
const tabsPanelCss: CSSMixinDescriptor = css({
|
|
152
|
+
minWidth: 0,
|
|
153
|
+
color: 'light-dark(#101010, #ececec)',
|
|
154
|
+
fontFamily: '"Inter Variable", Inter, ui-sans-serif, system-ui, sans-serif',
|
|
155
|
+
fontSize: '13px',
|
|
156
|
+
lineHeight: '20px',
|
|
157
|
+
letterSpacing: 0,
|
|
158
|
+
'&[hidden]': {
|
|
159
|
+
display: 'none',
|
|
160
|
+
},
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
const sizeStyles = {
|
|
164
|
+
md: css({}),
|
|
165
|
+
lg: css({
|
|
166
|
+
'--rmx-tabs-height': '36px',
|
|
167
|
+
'--rmx-tabs-tab-padding-inline': '14px',
|
|
168
|
+
'--rmx-tabs-tab-font-size': '13px',
|
|
169
|
+
'--rmx-tabs-tab-line-height': '20px',
|
|
170
|
+
'--rmx-tabs-gap': '14px',
|
|
171
|
+
}),
|
|
172
|
+
} as const satisfies Record<TabsSize, CSSMixinDescriptor>
|
|
173
|
+
|
|
174
|
+
export const rootStyle = tabsRootCss
|
|
175
|
+
export const listStyle = tabsListCss
|
|
176
|
+
export const tabStyle = tabsTabCss
|
|
177
|
+
export const panelStyle = tabsPanelCss
|
|
178
|
+
|
|
179
|
+
export function Tabs(handle: Handle<TabsProps>): () => RemixNode {
|
|
180
|
+
return () => {
|
|
181
|
+
let {
|
|
182
|
+
activeTab,
|
|
183
|
+
children,
|
|
184
|
+
defaultActiveTab,
|
|
185
|
+
disabled,
|
|
186
|
+
mix,
|
|
187
|
+
onActiveTabChange,
|
|
188
|
+
size = 'md',
|
|
189
|
+
...divProps
|
|
190
|
+
} = handle.props
|
|
191
|
+
|
|
192
|
+
return (
|
|
193
|
+
<tabs.Context
|
|
194
|
+
activeTab={activeTab}
|
|
195
|
+
defaultActiveTab={defaultActiveTab}
|
|
196
|
+
disabled={disabled}
|
|
197
|
+
onActiveTabChange={onActiveTabChange}
|
|
198
|
+
>
|
|
199
|
+
<div {...divProps} mix={[rootStyle, sizeStyles[size], tabs.root(), mix]}>
|
|
200
|
+
{children}
|
|
201
|
+
</div>
|
|
202
|
+
</tabs.Context>
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function TabList(handle: Handle<TabListProps>): () => RemixNode {
|
|
208
|
+
return () => {
|
|
209
|
+
let { children, mix, ...divProps } = handle.props
|
|
210
|
+
|
|
211
|
+
return (
|
|
212
|
+
<div {...divProps} mix={[listStyle, tabs.list(), mix]}>
|
|
213
|
+
{children}
|
|
214
|
+
</div>
|
|
215
|
+
)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function Tab(handle: Handle<TabProps>): () => RemixNode {
|
|
220
|
+
return () => {
|
|
221
|
+
let { children, disabled, mix, name, type, ...buttonProps } = handle.props
|
|
222
|
+
|
|
223
|
+
return (
|
|
224
|
+
<button
|
|
225
|
+
{...buttonProps}
|
|
226
|
+
mix={[tabStyle, tabs.tab({ disabled, name }), mix]}
|
|
227
|
+
type={type ?? 'button'}
|
|
228
|
+
>
|
|
229
|
+
{children}
|
|
230
|
+
</button>
|
|
231
|
+
)
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function TabPanel(handle: Handle<TabPanelProps>): () => RemixNode {
|
|
236
|
+
return () => {
|
|
237
|
+
let { children, mix, name, ...divProps } = handle.props
|
|
238
|
+
|
|
239
|
+
return (
|
|
240
|
+
<div {...divProps} mix={[panelStyle, tabs.panel({ name }), mix]}>
|
|
241
|
+
{children}
|
|
242
|
+
</div>
|
|
243
|
+
)
|
|
244
|
+
}
|
|
245
|
+
}
|