frontend/containers/AddressAutofill/index.tsx (view raw)
1import {useState} from 'react';
2import TextField, {TextFieldProps} from '@mui/material/TextField';
3import InputAdornment from '@mui/material/InputAdornment';
4import PlaceOutlinedIcon from '@mui/icons-material/PlaceOutlined';
5import Autocomplete from '@mui/material/Autocomplete';
6import {debounce} from '@mui/material/utils';
7import {SessionToken, AddressAutofillSuggestion} from '@mapbox/search-js-core';
8import {useTranslation} from 'react-i18next';
9import useLocale from '../../hooks/useLocale';
10
11interface Props {
12 address: string;
13 onSelect: ({
14 location,
15 address,
16 }: {
17 location: [number, number];
18 address: string;
19 }) => void;
20 label: string;
21 textFieldProps?: TextFieldProps;
22}
23
24const AddressAutofill = ({
25 address = '',
26 onSelect,
27 label,
28 textFieldProps,
29}: Props) => {
30 const {t} = useTranslation();
31 const {locale} = useLocale();
32 const [value, setValue] = useState(address);
33 const sessionToken = new SessionToken();
34
35 const [options, setOptions] = useState(
36 [] as Array<AddressAutofillSuggestion>
37 );
38
39 const onChange = async (e, selectedOption) => {
40 const body = await fetch(
41 '/api/mapbox/autofill/retrieve?' +
42 new URLSearchParams({
43 sessionToken,
44 locale,
45 }),
46 {body: JSON.stringify(selectedOption), method: 'POST'}
47 ).then(response => response.json());
48 setValue(selectedOption);
49 onSelect({
50 address: selectedOption.full_address,
51 location: body.features[0]?.geometry?.coordinates,
52 });
53 };
54
55 const updateOptions = debounce(async (e, search: string) => {
56 if (search !== '') {
57 await fetch(
58 '/api/mapbox/autofill/suggest?' +
59 new URLSearchParams({
60 search,
61 sessionToken,
62 locale,
63 })
64 )
65 .then(response => response.json())
66 .then(body => {
67 setOptions(body.suggestions);
68 });
69 }
70 }, 400);
71
72 return (
73 <Autocomplete
74 freeSolo
75 disableClearable
76 getOptionLabel={option =>
77 option.full_address || option.place_name || address
78 }
79 options={options}
80 autoComplete
81 value={value}
82 filterOptions={x => x}
83 noOptionsText={t('autocomplete.noMatch')}
84 onChange={onChange}
85 onInputChange={updateOptions}
86 renderInput={params => (
87 <TextField
88 label={label}
89 multiline
90 maxRows={4}
91 InputProps={{
92 type: 'search',
93 endAdornment: (
94 <InputAdornment position="end" sx={{mr: -0.5}}>
95 <PlaceOutlinedIcon />
96 </InputAdornment>
97 ),
98 }}
99 {...params}
100 {...textFieldProps}
101 />
102 )}
103 renderOption={(props, option) => {
104 return <li {...props}>{option.full_address || option.place_name}</li>;
105 }}
106 />
107 );
108};
109
110export default AddressAutofill;