forked from primer/react
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Select.tsx
103 lines (89 loc) · 3.22 KB
/
Select.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import React from 'react'
import styled from 'styled-components'
import TextInputWrapper, {StyledWrapperProps} from './_TextInputWrapper'
export type SelectProps = Omit<
Omit<React.ComponentPropsWithoutRef<'select'>, 'size'> & Omit<StyledWrapperProps, 'variant'>,
'multiple' | 'hasLeadingVisual' | 'hasTrailingVisual' | 'as'
>
const StyledSelect = styled.select`
appearance: none;
border: 0;
color: currentColor;
font-size: inherit;
outline: none;
width: 100%;
/* Firefox hacks: */
/* 1. Makes Firefox's native dropdown menu's background match the theme.
background-color should be 'transparent', but Firefox uses the background-color on
<select> to determine the background color used for the dropdown menu.
*/
background-color: inherit;
/* 2. Prevents visible overlap of partially transparent background colors.
'colors.input.disabledBg' happens to be partially transparent in light mode, so we use a
transparent background-color on a disabled <select>. */
&:disabled {
background-color: transparent;
}
/* 3. Maintain dark bg color in Firefox on Windows high-contrast mode
Firefox makes the <select>'s background color white when setting 'background-color: transparent;' */
@media screen and (forced-colors: active) {
&:disabled {
background-color: -moz-combobox;
}
}
`
const ArrowIndicatorSVG: React.FC<{className?: string}> = ({className}) => (
<svg width="16" height="16" fill="currentColor" xmlns="http://www.w3.org/2000/svg" className={className}>
<path d="m4.074 9.427 3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.043 9H4.251a.25.25 0 0 0-.177.427ZM4.074 7.47 7.47 4.073a.25.25 0 0 1 .354 0L11.22 7.47a.25.25 0 0 1-.177.426H4.251a.25.25 0 0 1-.177-.426Z" />
</svg>
)
const ArrowIndicator = styled(ArrowIndicatorSVG)`
pointer-events: none;
position: absolute;
right: 4px;
top: 50%;
transform: translateY(-50%);
`
const Select = React.forwardRef<HTMLSelectElement, SelectProps>(
({block, children, contrast, disabled, placeholder, size, required, validationStatus, ...rest}: SelectProps, ref) => (
<TextInputWrapper
sx={{
overflow: 'hidden',
position: 'relative',
'@media screen and (forced-colors: active)': {
svg: {
fill: disabled ? 'GrayText' : 'FieldText'
}
}
}}
block={block}
contrast={contrast}
disabled={disabled}
size={size}
validationStatus={validationStatus}
>
<StyledSelect
ref={ref}
required={required}
disabled={disabled}
aria-invalid={validationStatus === 'error' ? 'true' : 'false'}
data-hasplaceholder={Boolean(placeholder)}
{...rest}
>
{placeholder && (
<option value="" disabled={required} selected hidden={required}>
{placeholder}
</option>
)}
{children}
</StyledSelect>
<ArrowIndicator />
</TextInputWrapper>
)
)
const Option: React.FC<React.HTMLProps<HTMLOptionElement> & {value: string}> = props => <option {...props} />
const OptGroup: React.FC<React.HTMLProps<HTMLOptGroupElement>> = props => <optgroup {...props} />
export default Object.assign(Select, {
Option,
OptGroup
})