-
Notifications
You must be signed in to change notification settings - Fork 197
/
Copy pathCountrySelect.js
155 lines (137 loc) · 3.58 KB
/
CountrySelect.js
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import React, { useCallback, useMemo } from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'
import getUnicodeFlagIcon from 'country-flag-icons/unicode'
export default function CountrySelect({
value,
onChange,
options,
disabled,
readOnly,
...rest
}) {
const onChange_ = useCallback((event) => {
const value = event.target.value
onChange(value === 'ZZ' ? undefined : value)
}, [onChange])
const selectedOption = useMemo(() => {
return getSelectedOption(options, value)
}, [options, value])
// "ZZ" means "International".
// (HTML requires each `<option/>` have some string `value`).
return (
<select
{...rest}
disabled={disabled || readOnly}
readOnly={readOnly}
value={value || 'ZZ'}
onChange={onChange_}>
{options.map(({ value, label, divider }) => (
<option
key={divider ? '|' : value || 'ZZ'}
value={divider ? '|' : value || 'ZZ'}
disabled={divider ? true : false}
style={divider ? DIVIDER_STYLE : undefined}>
{label}
</option>
))}
</select>
)
}
CountrySelect.propTypes = {
/**
* A two-letter country code.
* Example: "US", "RU", etc.
*/
value: PropTypes.string,
/**
* A function of `value: string`.
* Updates the `value` property.
*/
onChange: PropTypes.func.isRequired,
// `<select/>` options.
options: PropTypes.arrayOf(PropTypes.shape({
value: PropTypes.string,
label: PropTypes.string,
divider: PropTypes.bool
})).isRequired,
// `readonly` attribute doesn't work on a `<select/>`.
// https://github.com/catamphetamine/react-phone-number-input/issues/419#issuecomment-1764384480
// https://www.delftstack.com/howto/html/html-select-readonly/
// To work around that, if `readOnly: true` property is passed
// to this component, it behaves analogous to `disabled: true`.
disabled: PropTypes.bool,
readOnly: PropTypes.bool
}
const DIVIDER_STYLE = {
fontSize: '1px',
backgroundColor: 'currentColor',
color: 'inherit'
}
export function CountrySelectWithIcon({
value,
options,
className,
iconComponent: Icon,
getIconAspectRatio,
arrowComponent: Arrow = DefaultArrowComponent,
unicodeFlags,
...rest
}) {
const selectedOption = useMemo(() => {
return getSelectedOption(options, value)
}, [options, value])
return (
<div className="PhoneInputCountry">
<CountrySelect
{...rest}
value={value}
options={options}
className={classNames('PhoneInputCountrySelect', className)}
/>
{/* Either a Unicode flag icon or an SVG flag icon. */}
{selectedOption && (
unicodeFlags && value ? (
<div className="PhoneInputCountryIconUnicode">
{getUnicodeFlagIcon(value)}
</div>
) : (
<Icon
aria-hidden
country={value}
label={selectedOption.label}
aspectRatio={unicodeFlags ? 1 : undefined}
/>
)
)}
<Arrow/>
</div>
)
}
CountrySelectWithIcon.propTypes = {
// Country flag component.
iconComponent: PropTypes.elementType,
// Select arrow component.
arrowComponent: PropTypes.elementType,
// Set to `true` to render Unicode flag icons instead of SVG images.
unicodeFlags: PropTypes.bool
}
function DefaultArrowComponent() {
return <div className="PhoneInputCountrySelectArrow"/>
}
function getSelectedOption(options, value) {
for (const option of options) {
if (!option.divider) {
if (isSameOptionValue(option.value, value)) {
return option
}
}
}
}
function isSameOptionValue(value1, value2) {
// `undefined` is identical to `null`: both mean "no country selected".
if (value1 === undefined || value1 === null) {
return value2 === undefined || value2 === null
}
return value1 === value2
}