@remix-run/ui 0.3.0 → 0.4.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 +50 -164
- 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/demos/reordering.js +9 -6
- package/dist/animation/demos/reordering.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 +125 -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} +3 -7
- package/dist/{components/combobox/combobox.js → combobox/primitives.js} +8 -61
- package/dist/combobox/primitives.js.map +1 -0
- 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/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} +3 -32
- package/dist/{components/menu/menu.js → menu/primitives.js} +97 -314
- 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/reconcile.js +11 -5
- package/dist/runtime/reconcile.js.map +1 -1
- package/dist/runtime/vdom.js +3 -0
- package/dist/runtime/vdom.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} +4 -6
- package/dist/{components/select/select.js → select/primitives.js} +11 -63
- package/dist/select/primitives.js.map +1 -0
- package/dist/server/stream.js +2 -2
- 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/{theme/presets/rmx-01/index.js → shared/style-values.js} +3 -34
- 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/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/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 +73 -44
- 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/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 +162 -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/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 +100 -0
- package/src/runtime/demos/readme.demo.tsx +16 -14
- package/src/runtime/reconcile.ts +11 -5
- package/src/runtime/vdom.ts +3 -0
- 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 +3 -3
- 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/tabs/README.md +141 -0
- package/src/tabs/index.tsx +244 -0
- package/src/tabs/primitives.ts +365 -0
- package/src/toggle/README.md +56 -0
- package/src/toggle/index.tsx +134 -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.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
|
@@ -1,22 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
attrs,
|
|
3
3
|
createMixin,
|
|
4
|
-
css,
|
|
5
4
|
on,
|
|
6
|
-
type CSSMixinDescriptor,
|
|
7
5
|
type ElementProps,
|
|
8
6
|
type Handle,
|
|
9
7
|
type MixinFactory,
|
|
10
8
|
type RemixNode,
|
|
11
9
|
} from '@remix-run/ui'
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
type AnchorTarget,
|
|
16
|
-
} from '../anchor/anchor.ts'
|
|
17
|
-
import { onOutsideClick } from '../../interactions/outside-click/outside-click-mixin.ts'
|
|
18
|
-
import { theme } from '../../theme/theme.ts'
|
|
19
|
-
import { lockScroll } from '../../utils/scroll-lock.ts'
|
|
10
|
+
import { anchor as positionAnchor, type AnchorOptions, type AnchorTarget } from '../anchor/index.ts'
|
|
11
|
+
import { onOutsideClick } from './outside-click.ts'
|
|
12
|
+
import { lockScroll } from './scroll-lock.ts'
|
|
20
13
|
|
|
21
14
|
export interface PopoverContext {
|
|
22
15
|
hideFocusTarget: HTMLElement | null
|
|
@@ -34,53 +27,6 @@ interface AnchorRef {
|
|
|
34
27
|
options: AnchorOptions
|
|
35
28
|
}
|
|
36
29
|
|
|
37
|
-
const popupViewportClampMaxHeight = '50dvh'
|
|
38
|
-
|
|
39
|
-
const popoverSurfaceTransitionCss: CSSMixinDescriptor = css({
|
|
40
|
-
opacity: 0,
|
|
41
|
-
'&:popover-open': {
|
|
42
|
-
opacity: 1,
|
|
43
|
-
},
|
|
44
|
-
'&:not(:popover-open)': {
|
|
45
|
-
pointerEvents: 'none',
|
|
46
|
-
transition: 'opacity 180ms ease-in, overlay 180ms ease-in, display 180ms ease-in',
|
|
47
|
-
transitionBehavior: 'allow-discrete',
|
|
48
|
-
},
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
const popoverContentCss: CSSMixinDescriptor = css({
|
|
52
|
-
flex: '1 1 auto',
|
|
53
|
-
minHeight: 0,
|
|
54
|
-
padding: theme.space.xs,
|
|
55
|
-
overflow: 'auto',
|
|
56
|
-
overscrollBehavior: 'contain',
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
const popoverSurfaceCss: CSSMixinDescriptor = css({
|
|
60
|
-
position: 'fixed',
|
|
61
|
-
inset: 'auto',
|
|
62
|
-
margin: 0,
|
|
63
|
-
padding: theme.space.none,
|
|
64
|
-
display: 'flex',
|
|
65
|
-
flexDirection: 'column',
|
|
66
|
-
minHeight: 0,
|
|
67
|
-
minWidth: '12rem',
|
|
68
|
-
maxWidth: `min(24rem, calc(100vw - (${theme.space.lg} * 2)))`,
|
|
69
|
-
maxHeight: popupViewportClampMaxHeight,
|
|
70
|
-
border: `1px solid ${theme.colors.border.subtle}`,
|
|
71
|
-
borderRadius: theme.radius.lg,
|
|
72
|
-
backgroundColor: theme.surface.lvl0,
|
|
73
|
-
color: theme.colors.text.primary,
|
|
74
|
-
overflow: 'hidden',
|
|
75
|
-
boxShadow: `${theme.shadow.xs}, ${theme.shadow.md}`,
|
|
76
|
-
'&::backdrop': {
|
|
77
|
-
background: 'transparent',
|
|
78
|
-
},
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
export const contentStyle = popoverContentCss
|
|
82
|
-
export const surfaceStyle = [popoverSurfaceCss, popoverSurfaceTransitionCss] as const
|
|
83
|
-
|
|
84
30
|
function PopoverProvider(handle: Handle<PopoverProps, PopoverContext>): () => RemixNode {
|
|
85
31
|
handle.context.set({
|
|
86
32
|
hideFocusTarget: null,
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { createMixin, on, type ElementProps, type MixinFactory } from '@remix-run/ui'
|
|
2
|
-
|
|
3
1
|
type ScrollLockState = {
|
|
4
2
|
count: number
|
|
5
3
|
documentOverflow: string
|
|
@@ -73,29 +71,3 @@ export function lockScroll(targetDocument: Document | undefined = globalThis.doc
|
|
|
73
71
|
view.scrollTo(currentState.scrollX, currentState.scrollY)
|
|
74
72
|
}
|
|
75
73
|
}
|
|
76
|
-
|
|
77
|
-
export const lockScrollOnToggle: MixinFactory<HTMLElement, [], ElementProps> = createMixin<
|
|
78
|
-
HTMLElement,
|
|
79
|
-
[],
|
|
80
|
-
ElementProps
|
|
81
|
-
>((handle) => {
|
|
82
|
-
let unlockScroll = () => {}
|
|
83
|
-
|
|
84
|
-
handle.signal.addEventListener('abort', () => {
|
|
85
|
-
unlockScroll()
|
|
86
|
-
unlockScroll = () => {}
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
return () => [
|
|
90
|
-
on('beforetoggle', (event) => {
|
|
91
|
-
unlockScroll()
|
|
92
|
-
unlockScroll = () => {}
|
|
93
|
-
|
|
94
|
-
if (event.newState !== 'open') {
|
|
95
|
-
return
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
unlockScroll = lockScroll((event.currentTarget as HTMLElement).ownerDocument)
|
|
99
|
-
}),
|
|
100
|
-
]
|
|
101
|
-
})
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# radio
|
|
2
|
+
|
|
3
|
+
`radio` is a style mixin for native radio inputs. It only owns radio visuals and the default `type="radio"` behavior for native `<input>` hosts.
|
|
4
|
+
|
|
5
|
+
## Primitive Usage
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import radio from 'remix/ui/radio'
|
|
9
|
+
|
|
10
|
+
function ShippingSpeed() {
|
|
11
|
+
return () => (
|
|
12
|
+
<fieldset>
|
|
13
|
+
<label>
|
|
14
|
+
<input defaultChecked mix={radio()} name="shipping-speed" value="standard" />
|
|
15
|
+
Standard
|
|
16
|
+
</label>
|
|
17
|
+
<label>
|
|
18
|
+
<input mix={radio({ size: 'lg' })} name="shipping-speed" value="express" />
|
|
19
|
+
Express
|
|
20
|
+
</label>
|
|
21
|
+
</fieldset>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Compose app-owned styles around the primitive when a radio option needs local layout:
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import radio from 'remix/ui/radio'
|
|
30
|
+
import { optionStyle } from './shipping.styles'
|
|
31
|
+
|
|
32
|
+
function ShippingOption() {
|
|
33
|
+
return () => (
|
|
34
|
+
<label mix={optionStyle}>
|
|
35
|
+
<input mix={radio()} name="shipping-speed" value="overnight" />
|
|
36
|
+
Overnight
|
|
37
|
+
</label>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## `remix/ui/radio`
|
|
43
|
+
|
|
44
|
+
- `radio(options)`: style mixin for native radio inputs or radio-like hosts.
|
|
45
|
+
- `RadioOptions`: accepts `size`.
|
|
46
|
+
- `RadioSize`: `'md'` or `'lg'`. Defaults to `'md'`.
|
|
47
|
+
|
|
48
|
+
## Behavior Notes
|
|
49
|
+
|
|
50
|
+
- `radio()` returns a mixin descriptor, so it composes with other mixins in the host element's `mix` prop.
|
|
51
|
+
- Native `<input>` hosts receive `type="radio"` unless an explicit `type` is provided.
|
|
52
|
+
- Radio state is native browser behavior. The mixin also styles `aria-checked="true"` and `data-state="checked"` for custom hosts.
|
|
53
|
+
- Disabled hosts use the shared disabled treatment through `disabled` or `aria-disabled="true"`.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { createMixin, css } from '@remix-run/ui'
|
|
2
|
+
import type { CSSMixinDescriptor, ElementProps, MixinDescriptor } from '@remix-run/ui'
|
|
3
|
+
import { renderMixinElement } from '../runtime/mixins/mixin.ts'
|
|
4
|
+
import { controlFocusShadow } from '../shared/focus-styles.ts'
|
|
5
|
+
|
|
6
|
+
export type RadioSize = 'md' | 'lg'
|
|
7
|
+
|
|
8
|
+
export interface RadioOptions {
|
|
9
|
+
size?: RadioSize
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type RadioMixin = readonly [
|
|
13
|
+
MixinDescriptor<Element, [], ElementProps>,
|
|
14
|
+
CSSMixinDescriptor,
|
|
15
|
+
CSSMixinDescriptor,
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
const checkedSelector = '&:checked, &[aria-checked="true"], &[data-state="checked"]'
|
|
19
|
+
const checkedMarkSelector =
|
|
20
|
+
'&:checked::before, &[aria-checked="true"]::before, &[data-state="checked"]::before'
|
|
21
|
+
|
|
22
|
+
const radioDefaultAttrs = createMixin<Element, [], ElementProps>((handle, hostType) => (props) => {
|
|
23
|
+
if (hostType !== 'input' || props.type !== undefined) {
|
|
24
|
+
return handle.element
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return renderMixinElement(handle.element, {
|
|
28
|
+
...props,
|
|
29
|
+
type: 'radio',
|
|
30
|
+
})
|
|
31
|
+
})()
|
|
32
|
+
|
|
33
|
+
const baseStyle: CSSMixinDescriptor = css({
|
|
34
|
+
appearance: 'none',
|
|
35
|
+
WebkitAppearance: 'none',
|
|
36
|
+
margin: 0,
|
|
37
|
+
boxSizing: 'border-box',
|
|
38
|
+
position: 'relative',
|
|
39
|
+
display: 'inline-grid',
|
|
40
|
+
placeItems: 'center',
|
|
41
|
+
width: 'var(--rmx-radio-size)',
|
|
42
|
+
height: 'var(--rmx-radio-size)',
|
|
43
|
+
minWidth: 'var(--rmx-radio-size)',
|
|
44
|
+
minHeight: 'var(--rmx-radio-size)',
|
|
45
|
+
padding: 0,
|
|
46
|
+
border: '1px solid rgba(0, 0, 0, 0.12)',
|
|
47
|
+
borderRadius: '9999px',
|
|
48
|
+
background: '#FFFFFF',
|
|
49
|
+
boxShadow: 'inset 0 1px 1px rgba(0, 0, 0, 0.06), inset 0 2px 2px rgba(0, 0, 0, 0.06)',
|
|
50
|
+
verticalAlign: 'middle',
|
|
51
|
+
flex: 'none',
|
|
52
|
+
'&::before': {
|
|
53
|
+
content: '""',
|
|
54
|
+
width: 'var(--rmx-radio-mark-size)',
|
|
55
|
+
height: 'var(--rmx-radio-mark-size)',
|
|
56
|
+
borderRadius: '99px',
|
|
57
|
+
background:
|
|
58
|
+
'linear-gradient(180deg, rgba(53, 115, 246, 0) 25%, rgba(53, 115, 246, 0.15) 100%), #FFFFFF',
|
|
59
|
+
boxShadow:
|
|
60
|
+
'0 1px 1px -0.5px #0944BE, 0 2px 2px -1px #0944BE, 0 4px 4px -2px #0944BE, 0 0 1px 1px rgba(0, 0, 0, 0.25), inset 0 0 2px 1px #FFFFFF',
|
|
61
|
+
opacity: 0,
|
|
62
|
+
pointerEvents: 'none',
|
|
63
|
+
},
|
|
64
|
+
'&:disabled, &[aria-disabled="true"]': {
|
|
65
|
+
opacity: 0.55,
|
|
66
|
+
},
|
|
67
|
+
[checkedSelector]: {
|
|
68
|
+
border: 0,
|
|
69
|
+
borderRadius: '99px',
|
|
70
|
+
background: 'linear-gradient(180deg, rgba(0, 0, 0, 0.1) 0%, rgba(0, 0, 0, 0) 75.48%), #3573F6',
|
|
71
|
+
backgroundBlendMode: 'overlay, normal',
|
|
72
|
+
boxShadow:
|
|
73
|
+
'0 1px 0 rgba(255, 255, 255, 0.6), 0 0 16px rgba(53, 115, 246, 0.25), inset 0 0 3px 1px rgba(0, 0, 0, 0.1)',
|
|
74
|
+
},
|
|
75
|
+
[checkedMarkSelector]: {
|
|
76
|
+
opacity: 1,
|
|
77
|
+
},
|
|
78
|
+
'&:focus-visible': {
|
|
79
|
+
outline: 0,
|
|
80
|
+
boxShadow: controlFocusShadow,
|
|
81
|
+
},
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
const sizeStyles = {
|
|
85
|
+
md: css({
|
|
86
|
+
'--rmx-radio-size': '16px',
|
|
87
|
+
'--rmx-radio-mark-size': '8px',
|
|
88
|
+
}),
|
|
89
|
+
lg: css({
|
|
90
|
+
'--rmx-radio-size': '20px',
|
|
91
|
+
'--rmx-radio-mark-size': '10px',
|
|
92
|
+
}),
|
|
93
|
+
} as const satisfies Record<RadioSize, CSSMixinDescriptor>
|
|
94
|
+
|
|
95
|
+
export function radio(options: RadioOptions = {}): RadioMixin {
|
|
96
|
+
let { size = 'md' } = options
|
|
97
|
+
return [radioDefaultAttrs, baseStyle, sizeStyles[size]]
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export default radio
|
|
@@ -196,12 +196,14 @@ function SlugForm(handle: Handle) {
|
|
|
196
196
|
function KeyboardTracker(handle: Handle) {
|
|
197
197
|
let keys: string[] = []
|
|
198
198
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
199
|
+
handle.queueTask(() => {
|
|
200
|
+
addEventListeners(document, handle.signal, {
|
|
201
|
+
keydown: (event) => {
|
|
202
|
+
keys.push(event.key)
|
|
203
|
+
if (keys.length > 10) keys.shift()
|
|
204
|
+
handle.update()
|
|
205
|
+
},
|
|
206
|
+
})
|
|
205
207
|
})
|
|
206
208
|
|
|
207
209
|
return () => <div>Keys: {keys.join(', ') || '(press some keys)'}</div>
|
|
@@ -457,14 +459,14 @@ function FormWithScroll(handle: Handle) {
|
|
|
457
459
|
// handle.signal - Clock
|
|
458
460
|
// ============================================================================
|
|
459
461
|
function Clock(handle: Handle) {
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
|
|
462
|
+
handle.queueTask(() => {
|
|
463
|
+
let interval = setInterval(() => {
|
|
464
|
+
handle.update()
|
|
465
|
+
}, 1000)
|
|
466
|
+
|
|
467
|
+
handle.signal.addEventListener('abort', () => clearInterval(interval), { once: true })
|
|
468
|
+
})
|
|
469
|
+
|
|
468
470
|
return () => <span>{new Date().toLocaleTimeString()}</span>
|
|
469
471
|
}
|
|
470
472
|
|
package/src/runtime/reconcile.ts
CHANGED
|
@@ -340,6 +340,10 @@ function enqueueMixinBindingUpdate(
|
|
|
340
340
|
let prevProps = getHostProps(node)
|
|
341
341
|
let nextProps = resolveNodeMixProps(node, this.frame, this.scheduler, state)
|
|
342
342
|
patchHostProps(prevProps, nextProps, this.node)
|
|
343
|
+
if (node._controlledState || shouldTrackControlledReflection(nextProps)) {
|
|
344
|
+
ensureControlledReflection(node, this.scheduler)
|
|
345
|
+
syncControlledReflection(node, nextProps)
|
|
346
|
+
}
|
|
343
347
|
|
|
344
348
|
dispatchMixinCommit(state)
|
|
345
349
|
done(state ? getMixinRuntimeSignal(state) : AbortSignal.abort())
|
|
@@ -700,10 +704,12 @@ function insert(
|
|
|
700
704
|
cursor = null
|
|
701
705
|
}
|
|
702
706
|
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
+
// Preserve frame-start markers for non-Frame nodes too, so a following <Frame>
|
|
708
|
+
// (e.g. the first child of a bare Fragment at a clientEntry boundary) can still
|
|
709
|
+
// claim its rmx:f marker during hydration instead of being re-inserted fresh.
|
|
710
|
+
// A rmx:f marker always belongs to a <Frame>, so no non-Frame node should
|
|
711
|
+
// consume one.
|
|
712
|
+
cursor = skipCommentsExceptFrameStart(cursor ?? null)
|
|
707
713
|
|
|
708
714
|
// Also check after skipComments in case we skipped past the anchor
|
|
709
715
|
if (cursor && anchor && cursor === anchor) {
|
|
@@ -1097,7 +1103,7 @@ function resolveClientFrame(node: VNode, runtime: FrameRuntime): void {
|
|
|
1097
1103
|
let resolveController = new AbortController()
|
|
1098
1104
|
node._frameResolveController = resolveController
|
|
1099
1105
|
|
|
1100
|
-
Promise.resolve(runtime.resolveFrame(frameSrc, resolveController.signal))
|
|
1106
|
+
Promise.resolve(runtime.resolveFrame(frameSrc, resolveController.signal, getFrameName(node)))
|
|
1101
1107
|
.then(async (content) => {
|
|
1102
1108
|
if (node._frameResolveToken !== token || resolveController.signal.aborted) return
|
|
1103
1109
|
node._frameFallbackRoot?.dispose()
|
package/src/runtime/vdom.ts
CHANGED
|
@@ -172,6 +172,9 @@ export function createRangeRoot(
|
|
|
172
172
|
export function createRoot(container: HTMLElement, options: VirtualRootOptions = {}): VirtualRoot {
|
|
173
173
|
let vroot: VNode | null = null
|
|
174
174
|
let styles = options.styleManager ?? defaultStyleManager
|
|
175
|
+
if (container.innerHTML.trim() !== '') {
|
|
176
|
+
styles.replaceServerStyles(container)
|
|
177
|
+
}
|
|
175
178
|
let hydrationCursor = container.innerHTML.trim() !== '' ? container.firstChild : undefined
|
|
176
179
|
|
|
177
180
|
let eventTarget = new TypedEventTarget<VirtualRootEventMap>()
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
`Select` is a button-triggered popup value picker backed by `listbox` and `popover`. Use it when the user should choose one stable string value from a finite set.
|
|
4
4
|
|
|
5
|
-
## Usage
|
|
5
|
+
## Component Usage
|
|
6
6
|
|
|
7
7
|
```tsx
|
|
8
|
-
import { Option, Select
|
|
8
|
+
import { Option, Select } from 'remix/ui/select'
|
|
9
|
+
import { onSelectChange } from 'remix/ui/select/primitives'
|
|
9
10
|
|
|
10
11
|
export function FrameworkSelect() {
|
|
11
12
|
return (
|
|
@@ -44,34 +45,34 @@ Use `textValue` when closed-trigger typeahead should match a different string fr
|
|
|
44
45
|
</Select>
|
|
45
46
|
```
|
|
46
47
|
|
|
48
|
+
## Primitive Usage
|
|
49
|
+
|
|
47
50
|
Use the lower-level primitives when the trigger or popup structure needs to be owned by another component. Keep the same provider, trigger, popover, list, option, and hidden-input relationship.
|
|
48
51
|
|
|
49
52
|
```tsx
|
|
50
53
|
import type { Handle } from 'remix/ui'
|
|
51
|
-
import * as button from 'remix/ui/button'
|
|
52
|
-
import * as listbox from 'remix/ui/listbox'
|
|
53
54
|
import * as popover from 'remix/ui/popover'
|
|
54
|
-
import
|
|
55
|
+
import { triggerStyle } from 'remix/ui/select'
|
|
56
|
+
import * as select from 'remix/ui/select/primitives'
|
|
57
|
+
import { listStyle, optionStyle, surfaceStyle } from './select.styles'
|
|
55
58
|
|
|
56
59
|
function SelectValue(handle: Handle) {
|
|
57
60
|
let context = handle.context.get(select.Context)
|
|
58
61
|
|
|
59
|
-
return () => <span
|
|
62
|
+
return () => <span>{context.displayedLabel}</span>
|
|
60
63
|
}
|
|
61
64
|
|
|
62
65
|
function IssueTypeSelect() {
|
|
63
66
|
return () => (
|
|
64
|
-
<select.Context defaultLabel="Select a type" name="issueType">
|
|
65
|
-
<button type="button" mix={[
|
|
67
|
+
<select.Context defaultLabel="Select a type" labelSwapDelayMs={100} name="issueType">
|
|
68
|
+
<button type="button" mix={[triggerStyle, select.trigger()]}>
|
|
66
69
|
<SelectValue />
|
|
67
70
|
</button>
|
|
68
71
|
<popover.Context>
|
|
69
|
-
<div mix={[
|
|
70
|
-
<div mix={[
|
|
71
|
-
<div mix={[
|
|
72
|
-
|
|
73
|
-
</div>
|
|
74
|
-
<div mix={[listbox.optionStyle, select.option({ label: 'Feature', value: 'feature' })]}>
|
|
72
|
+
<div mix={[surfaceStyle, select.popover()]}>
|
|
73
|
+
<div mix={[listStyle, select.list()]}>
|
|
74
|
+
<div mix={[optionStyle, select.option({ label: 'Bug', value: 'bug' })]}>Bug</div>
|
|
75
|
+
<div mix={[optionStyle, select.option({ label: 'Feature', value: 'feature' })]}>
|
|
75
76
|
Feature
|
|
76
77
|
</div>
|
|
77
78
|
</div>
|
|
@@ -83,15 +84,24 @@ function IssueTypeSelect() {
|
|
|
83
84
|
}
|
|
84
85
|
```
|
|
85
86
|
|
|
86
|
-
## `select
|
|
87
|
+
## `remix/ui/select`
|
|
87
88
|
|
|
88
89
|
- `Select`: composed trigger, popover, listbox, option list, and optional hidden input for form participation. Accepts `defaultLabel`, `defaultValue`, `disabled`, `name`, and button props.
|
|
89
|
-
- `Option`: option
|
|
90
|
+
- `Option`: option component that renders the standard check indicator and label slot. Accepts `label`, `value`, optional `disabled`, and optional `textValue`.
|
|
91
|
+
- `triggerStyle`: standard select trigger style for custom trigger composition.
|
|
92
|
+
- `SelectProps` and `SelectOptionProps`: public TypeScript props for the composed APIs.
|
|
93
|
+
|
|
94
|
+
## `remix/ui/select/primitives`
|
|
95
|
+
|
|
96
|
+
- `Context`: lower-level provider for custom composition. Accepts `defaultLabel`, `defaultValue`, `disabled`, `name`, and primitive-only `labelSwapDelayMs`; the label-swap delay defaults to `75` milliseconds.
|
|
97
|
+
- `trigger()`: wires the trigger button, open behavior, closed-trigger typeahead, and trigger ARIA attributes.
|
|
98
|
+
- `popover()`: wires the popover surface and keeps its min-width synced to the trigger before open.
|
|
99
|
+
- `list()`: wires the listbox root used inside the popover.
|
|
100
|
+
- `option(...)`: registers one selectable option. Accepts `label`, `value`, optional `disabled`, and optional `textValue`.
|
|
101
|
+
- `hiddenInput()`: mirrors the selected value into a hidden input for form participation.
|
|
90
102
|
- `onSelectChange(...)`: event mixin for the bubbling `SelectChangeEvent`.
|
|
91
|
-
- `Context`, `trigger()`, `popover()`, `list()`, `option(...)`, and `hiddenInput()`: lower-level composition primitives.
|
|
92
|
-
- `triggerStyle`: standard select trigger style.
|
|
93
103
|
- `SelectChangeEvent`: event with `value`, `label`, and `optionId`.
|
|
94
|
-
- `
|
|
104
|
+
- `SelectContextProps`, `SelectProps`, and `SelectOptionProps`: primitive prop types for custom composition.
|
|
95
105
|
|
|
96
106
|
## Behavior Notes
|
|
97
107
|
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { createElement, css } from '@remix-run/ui'
|
|
2
|
+
import type { CSSMixinDescriptor, Handle, Props, RemixNode } from '@remix-run/ui'
|
|
3
|
+
|
|
4
|
+
import * as popover from '@remix-run/ui/popover'
|
|
5
|
+
import * as select from '@remix-run/ui/select/primitives'
|
|
6
|
+
import type { SearchValue } from '../shared/typeahead.ts'
|
|
7
|
+
import { CheckIcon, ChevronVerticalIcon } from '../shared/icons.tsx'
|
|
8
|
+
import {
|
|
9
|
+
listboxIndicatorStyle,
|
|
10
|
+
listboxLabelStyle,
|
|
11
|
+
listboxListStyle,
|
|
12
|
+
listboxOptionStyle,
|
|
13
|
+
popoverSurfaceStyle,
|
|
14
|
+
} from '../shared/listbox-popover-styles.ts'
|
|
15
|
+
|
|
16
|
+
export interface SelectProps extends Omit<Props<'button'>, 'children' | 'name'> {
|
|
17
|
+
children?: RemixNode
|
|
18
|
+
defaultLabel: string
|
|
19
|
+
defaultValue?: string | null
|
|
20
|
+
disabled?: boolean
|
|
21
|
+
name?: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type SelectOptionProps = Props<'div'> & {
|
|
25
|
+
children?: RemixNode
|
|
26
|
+
disabled?: boolean
|
|
27
|
+
label: string
|
|
28
|
+
textValue?: SearchValue
|
|
29
|
+
value: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const selectTriggerShadow =
|
|
33
|
+
'0 2px 3px -1px rgba(0, 0, 0, 0.04), 0 3px 4px -1.5px rgba(0, 0, 0, 0.04), 0 4px 5px -2px rgba(0, 0, 0, 0.04), 0 0 0 1px rgba(0, 0, 0, 0.12)'
|
|
34
|
+
|
|
35
|
+
const selectTriggerFocusShadow =
|
|
36
|
+
'0 2px 3px -1px rgba(0, 0, 0, 0.04), 0 3px 4px -1.5px rgba(0, 0, 0, 0.04), 0 4px 5px -2px rgba(0, 0, 0, 0.04), 0 0 0 1px #3573F6, 0 0 0 4px rgba(53, 115, 246, 0.1), 0 6px 32px 4px rgba(53, 115, 246, 0.08), inset 0 0 8px 1px rgba(53, 115, 246, 0.05)'
|
|
37
|
+
|
|
38
|
+
const selectTriggerCss: CSSMixinDescriptor = css({
|
|
39
|
+
appearance: 'none',
|
|
40
|
+
margin: 0,
|
|
41
|
+
boxSizing: 'border-box',
|
|
42
|
+
width: '100%',
|
|
43
|
+
minWidth: 0,
|
|
44
|
+
height: '32px',
|
|
45
|
+
paddingBlock: '6px',
|
|
46
|
+
paddingInlineStart: '12px',
|
|
47
|
+
paddingInlineEnd: '8px',
|
|
48
|
+
display: 'flex',
|
|
49
|
+
alignItems: 'center',
|
|
50
|
+
justifyContent: 'flex-start',
|
|
51
|
+
gap: '6px',
|
|
52
|
+
border: 0,
|
|
53
|
+
borderRadius: '8px',
|
|
54
|
+
background: '#FFFFFF',
|
|
55
|
+
boxShadow: selectTriggerShadow,
|
|
56
|
+
color: '#101010',
|
|
57
|
+
fontFamily: '"Inter Variable", Inter, ui-sans-serif, system-ui, sans-serif',
|
|
58
|
+
fontStyle: 'normal',
|
|
59
|
+
fontWeight: 400,
|
|
60
|
+
fontSize: '13px',
|
|
61
|
+
lineHeight: '20px',
|
|
62
|
+
fontFeatureSettings: '"ss01" on, "cv01" on',
|
|
63
|
+
letterSpacing: 0,
|
|
64
|
+
textAlign: 'left',
|
|
65
|
+
textShadow: '0 1px 0 #FFFFFF',
|
|
66
|
+
whiteSpace: 'nowrap',
|
|
67
|
+
'&:hover, &:focus-visible, &[aria-expanded="true"], &[aria-expanded="true"]:hover, &[aria-expanded="true"]:focus-visible':
|
|
68
|
+
{
|
|
69
|
+
background: '#FFFFFF',
|
|
70
|
+
color: '#101010',
|
|
71
|
+
},
|
|
72
|
+
'&:active': {
|
|
73
|
+
background: '#FFFFFF',
|
|
74
|
+
},
|
|
75
|
+
'&:focus-visible': {
|
|
76
|
+
outline: 0,
|
|
77
|
+
boxShadow: selectTriggerFocusShadow,
|
|
78
|
+
},
|
|
79
|
+
'&[aria-expanded="true"]': {
|
|
80
|
+
boxShadow: selectTriggerFocusShadow,
|
|
81
|
+
},
|
|
82
|
+
'&:disabled': {
|
|
83
|
+
opacity: 0.55,
|
|
84
|
+
},
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
const triggerLabelCss: CSSMixinDescriptor = css({
|
|
88
|
+
display: 'inline-flex',
|
|
89
|
+
alignItems: 'center',
|
|
90
|
+
flex: '1 1 auto',
|
|
91
|
+
minWidth: 0,
|
|
92
|
+
overflow: 'hidden',
|
|
93
|
+
textOverflow: 'ellipsis',
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
const triggerIconCss: CSSMixinDescriptor = css({
|
|
97
|
+
display: 'inline-flex',
|
|
98
|
+
alignItems: 'center',
|
|
99
|
+
justifyContent: 'center',
|
|
100
|
+
width: '16px',
|
|
101
|
+
height: '16px',
|
|
102
|
+
color: '#707070',
|
|
103
|
+
flex: 'none',
|
|
104
|
+
'& > svg': {
|
|
105
|
+
display: 'block',
|
|
106
|
+
width: '100%',
|
|
107
|
+
height: '100%',
|
|
108
|
+
},
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
export const triggerStyle = selectTriggerCss
|
|
112
|
+
|
|
113
|
+
function SelectLabel(handle: Handle): () => RemixNode {
|
|
114
|
+
let context = handle.context.get(select.Context)
|
|
115
|
+
|
|
116
|
+
return () => <span mix={triggerLabelCss}>{context.displayedLabel}</span>
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function Select(handle: Handle<SelectProps>): () => RemixNode {
|
|
120
|
+
return () => {
|
|
121
|
+
let { children, defaultLabel, defaultValue, disabled, name, mix, ...buttonProps } = handle.props
|
|
122
|
+
|
|
123
|
+
return (
|
|
124
|
+
<select.Context
|
|
125
|
+
defaultLabel={defaultLabel}
|
|
126
|
+
defaultValue={defaultValue}
|
|
127
|
+
disabled={disabled}
|
|
128
|
+
name={name}
|
|
129
|
+
>
|
|
130
|
+
<button type="button" {...buttonProps} mix={[triggerStyle, select.trigger(), mix]}>
|
|
131
|
+
<SelectLabel />
|
|
132
|
+
<ChevronVerticalIcon mix={triggerIconCss} />
|
|
133
|
+
</button>
|
|
134
|
+
<popover.Context>
|
|
135
|
+
<div mix={[popoverSurfaceStyle, select.popover()]}>
|
|
136
|
+
<div mix={[listboxListStyle, select.list()]}>{children}</div>
|
|
137
|
+
</div>
|
|
138
|
+
</popover.Context>
|
|
139
|
+
{name && <input mix={select.hiddenInput()} />}
|
|
140
|
+
</select.Context>
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function Option(handle: Handle<SelectOptionProps>): () => RemixNode {
|
|
146
|
+
return () => {
|
|
147
|
+
let { label, value, disabled, textValue, children, mix, ...divProps } = handle.props
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
<div
|
|
151
|
+
{...divProps}
|
|
152
|
+
mix={[listboxOptionStyle, select.option({ value, label, disabled, textValue }), mix]}
|
|
153
|
+
>
|
|
154
|
+
<CheckIcon mix={listboxIndicatorStyle} />
|
|
155
|
+
<span mix={listboxLabelStyle}>{children ?? label}</span>
|
|
156
|
+
</div>
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
}
|