all repos — caroster @ ccf258ce9c59a35ff59b8dbcb5312dec2527216c

[Octree] Group carpool to your event https://caroster.io

frontend/components/PhoneInput/index.tsx (view raw)

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