Implement ComboAdresseSearch
This commit is contained in:
parent
b6c94a3758
commit
8a2f079acb
6 changed files with 60 additions and 96 deletions
|
@ -11,3 +11,7 @@
|
|||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.form [data-react-class='MapEditor'] [data-reach-combobox-input] {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
|
44
app/javascript/components/ComboAdresseSearch.js
Normal file
44
app/javascript/components/ComboAdresseSearch.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
import React, { useCallback } from 'react';
|
||||
import { ReactQueryCacheProvider } from 'react-query';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import ComboSearch from './ComboSearch';
|
||||
import { queryCache } from './shared/queryCache';
|
||||
|
||||
function ComboAdresseSearch({
|
||||
mandatory,
|
||||
placeholder,
|
||||
hiddenFieldId,
|
||||
onChange,
|
||||
transformResult = ({ properties: { label } }) => [label, label],
|
||||
allowInputValues = true
|
||||
}) {
|
||||
const transformResults = useCallback((_, { features }) => features);
|
||||
|
||||
return (
|
||||
<ReactQueryCacheProvider queryCache={queryCache}>
|
||||
<ComboSearch
|
||||
placeholder={placeholder}
|
||||
required={mandatory}
|
||||
hiddenFieldId={hiddenFieldId}
|
||||
onChange={onChange}
|
||||
allowInputValues={allowInputValues}
|
||||
scope="adresse"
|
||||
minimumInputLength={2}
|
||||
transformResult={transformResult}
|
||||
transformResults={transformResults}
|
||||
/>
|
||||
</ReactQueryCacheProvider>
|
||||
);
|
||||
}
|
||||
|
||||
ComboAdresseSearch.propTypes = {
|
||||
placeholder: PropTypes.string,
|
||||
mandatory: PropTypes.bool,
|
||||
hiddenFieldId: PropTypes.string,
|
||||
transformResult: PropTypes.func,
|
||||
allowInputValues: PropTypes.bool,
|
||||
onChange: PropTypes.func
|
||||
};
|
||||
|
||||
export default ComboAdresseSearch;
|
|
@ -1,88 +0,0 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { getJSON } from '@utils';
|
||||
import {
|
||||
Combobox,
|
||||
ComboboxInput,
|
||||
ComboboxPopover,
|
||||
ComboboxList,
|
||||
ComboboxOption
|
||||
} from '@reach/combobox';
|
||||
import '@reach/combobox/styles.css';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
let cache = {};
|
||||
const useAddressSearch = (searchTerm) => {
|
||||
const [addresses, setAddresses] = useState([]);
|
||||
useEffect(() => {
|
||||
if (searchTerm.trim() !== '') {
|
||||
let isFresh = true;
|
||||
fetchAddresses(searchTerm).then((addresses) => {
|
||||
if (isFresh) setAddresses(addresses);
|
||||
});
|
||||
return () => (isFresh = false);
|
||||
}
|
||||
}, [searchTerm]);
|
||||
return addresses;
|
||||
};
|
||||
|
||||
const fetchAddresses = (value) => {
|
||||
if (cache[value]) {
|
||||
return Promise.resolve(cache[value]);
|
||||
}
|
||||
const url = `https://api-adresse.data.gouv.fr/search/`;
|
||||
return getJSON(url, { q: value, limit: 5 }, 'get').then((result) => {
|
||||
if (result) {
|
||||
cache[value] = result;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
};
|
||||
|
||||
const SearchInput = ({ getCoords }) => {
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const addresses = useAddressSearch(searchTerm);
|
||||
const handleSearchTermChange = (event) => {
|
||||
setSearchTerm(event.target.value);
|
||||
};
|
||||
return (
|
||||
<Combobox aria-label="addresses">
|
||||
<ComboboxInput
|
||||
placeholder="Rechercher une adresse : saisissez au moins 2 caractères"
|
||||
className="address-search-input"
|
||||
style={{
|
||||
font: 'inherit',
|
||||
padding: '.25rem .5rem',
|
||||
width: '100%',
|
||||
minHeight: '62px'
|
||||
}}
|
||||
onChange={handleSearchTermChange}
|
||||
/>
|
||||
{addresses.features && (
|
||||
<ComboboxPopover className="shadow-popup">
|
||||
{addresses.features.length > 0 ? (
|
||||
<ComboboxList>
|
||||
{addresses.features.map((feature) => {
|
||||
const str = `${feature.properties.name}, ${feature.properties.city}`;
|
||||
return (
|
||||
<ComboboxOption
|
||||
onClick={() => getCoords(feature.geometry.coordinates)}
|
||||
key={str}
|
||||
value={str}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</ComboboxList>
|
||||
) : (
|
||||
<span style={{ display: 'block', margin: 8 }}>Aucun résultat</span>
|
||||
)}
|
||||
</ComboboxPopover>
|
||||
)}
|
||||
</Combobox>
|
||||
);
|
||||
};
|
||||
|
||||
SearchInput.propTypes = {
|
||||
getCoords: PropTypes.func
|
||||
};
|
||||
|
||||
export default SearchInput;
|
|
@ -9,7 +9,7 @@ import { getJSON, ajax, fire } from '@utils';
|
|||
|
||||
import { getMapStyle, SwitchMapStyle } from '../MapStyles';
|
||||
|
||||
import SearchInput from './SearchInput';
|
||||
import ComboAdresseSearch from '../ComboAdresseSearch';
|
||||
import {
|
||||
polygonCadastresFill,
|
||||
polygonCadastresLine,
|
||||
|
@ -279,9 +279,11 @@ function MapEditor({ featureCollection, url, preview, options }) {
|
|||
marginBottom: '50px'
|
||||
}}
|
||||
>
|
||||
<SearchInput
|
||||
getCoords={(searchTerm) => {
|
||||
setCoords(searchTerm);
|
||||
<ComboAdresseSearch
|
||||
placeholder="Rechercher une adresse : saisissez au moins 2 caractères"
|
||||
allowInputValues={false}
|
||||
onChange={(_, { geometry: { coordinates } }) => {
|
||||
setCoords(coordinates);
|
||||
setZoom([17]);
|
||||
}}
|
||||
/>
|
||||
|
|
3
app/javascript/loaders/ComboAdresseSearch.js
Normal file
3
app/javascript/loaders/ComboAdresseSearch.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import Loadable from '../components/Loadable';
|
||||
|
||||
export default Loadable(() => import('../components/ComboAdresseSearch'));
|
|
@ -1,4 +1,3 @@
|
|||
= form.select :value, [champ.value].compact,
|
||||
{ include_blank: true },
|
||||
required: champ.mandatory?,
|
||||
class: 'select2 adresse'
|
||||
- hidden_field_id = SecureRandom.uuid
|
||||
= form.hidden_field :value, { data: { uuid: hidden_field_id } }
|
||||
= react_component("ComboAdresseSearch", mandatory: champ.mandatory?, hiddenFieldId: hidden_field_id)
|
||||
|
|
Loading…
Reference in a new issue