all repos — caroster @ fbc9e225b5270c61549fce0c2fd4c1c1faa05e48

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

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

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