frontend/components/PhoneInput/index.tsx (view raw)
1import React, {useState} from 'react';
2import {
3 InputAdornment,
4 MenuItem,
5 Select,
6 TextField,
7 Typography,
8} from '@mui/material';
9import {
10 defaultCountries,
11 FlagImage,
12 getActiveFormattingMask,
13 parseCountry,
14 usePhoneInput,
15} from 'react-international-phone';
16
17import 'react-international-phone/style.css';
18
19interface Props {
20 value: string;
21 onChange: (phoneNumber: string) => void;
22 label: string;
23}
24
25const PhoneInput = ({value, onChange, label}: Props) => {
26 const [phone, setPhone] = useState(value);
27
28 const browserLocales = navigator.language.split('-');
29 const defaultCountry =
30 browserLocales[browserLocales.length - 1].toLowerCase();
31
32 const {inputValue, handlePhoneValueChange, inputRef, country, setCountry} =
33 usePhoneInput({
34 defaultCountry: defaultCountry || defaultCountries[0][1],
35 value: phone,
36 countries: defaultCountries,
37 onChange: data => {
38 setPhone(data.phone);
39 const mask = getActiveFormattingMask({
40 phone: data.phone,
41 country: data.country,
42 });
43 const digitnumbers = mask.split('').filter(c => c === '.').length;
44 const isValid =
45 data.phone.length === 1 + data.country.dialCode.length + digitnumbers;
46 if (isValid) {
47 onChange(data.phone);
48 } else {
49 onChange('');
50 }
51 },
52 });
53
54 return (
55 <TextField
56 fullWidth
57 color={value === phone ? 'primary' : 'error'}
58 label={label}
59 value={inputValue}
60 onChange={handlePhoneValueChange}
61 type="tel"
62 inputRef={inputRef}
63 InputProps={{
64 startAdornment: (
65 <InputAdornment
66 position="start"
67 style={{marginRight: '2px', marginLeft: '-8px'}}
68 >
69 <Select
70 MenuProps={{
71 style: {
72 height: '300px',
73 width: '360px',
74 top: '10px',
75 left: '-34px',
76 },
77 transformOrigin: {
78 vertical: 'top',
79 horizontal: 'left',
80 },
81 }}
82 sx={{
83 width: 'max-content',
84 // Remove default outline (display only on focus)
85 fieldset: {
86 display: 'none',
87 },
88 '&.Mui-focused:has(div[aria-expanded="false"])': {
89 fieldset: {
90 display: 'block',
91 },
92 },
93 // Update default spacing
94 '.MuiSelect-select': {
95 padding: '8px',
96 paddingRight: '24px !important',
97 },
98 svg: {
99 right: 0,
100 },
101 }}
102 value={country.iso2}
103 onChange={e => setCountry(e.target.value)}
104 renderValue={value => (
105 <FlagImage iso2={value} style={{display: 'flex'}} />
106 )}
107 >
108 {defaultCountries.map(c => {
109 const country = parseCountry(c);
110 return (
111 <MenuItem key={country.iso2} value={country.iso2}>
112 <FlagImage
113 iso2={country.iso2}
114 style={{marginRight: '8px'}}
115 />
116 <Typography marginRight="8px">{country.name}</Typography>
117 <Typography color="gray">+{country.dialCode}</Typography>
118 </MenuItem>
119 );
120 })}
121 </Select>
122 </InputAdornment>
123 ),
124 }}
125 />
126 );
127};
128
129export default PhoneInput;