all repos — caroster @ v5.5

[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  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;