2021-10-26 16:21:47 +02:00
|
|
|
|
import React, { useState, useMemo } from 'react';
|
2021-02-16 13:54:16 +01:00
|
|
|
|
import { QueryClientProvider } from 'react-query';
|
2021-06-15 14:36:37 +02:00
|
|
|
|
import { matchSorter } from 'match-sorter';
|
2020-10-07 17:42:41 +02:00
|
|
|
|
|
|
|
|
|
import ComboSearch from './ComboSearch';
|
2021-07-20 12:41:16 +02:00
|
|
|
|
import { queryClient } from './shared/queryClient';
|
2021-10-26 16:21:47 +02:00
|
|
|
|
import { ComboDepartementsSearch } from './ComboDepartementsSearch';
|
2021-07-20 12:41:16 +02:00
|
|
|
|
|
|
|
|
|
// Avoid hiding similar matches for precise queries (like "Sainte Marie")
|
|
|
|
|
function searchResultsLimit(term) {
|
|
|
|
|
return term.length > 5 ? 10 : 5;
|
|
|
|
|
}
|
2020-10-07 17:42:41 +02:00
|
|
|
|
|
2021-06-15 14:36:37 +02:00
|
|
|
|
function expandResultsWithMultiplePostalCodes(term, results) {
|
2021-06-16 09:28:07 +02:00
|
|
|
|
// A single result may have several associated postal codes.
|
|
|
|
|
// To make the search results more precise, we want to generate
|
|
|
|
|
// an actual result for each postal code.
|
2021-06-15 14:36:37 +02:00
|
|
|
|
const expandedResults = results.flatMap((result) =>
|
|
|
|
|
result.codesPostaux.map((codePostal) => ({
|
|
|
|
|
...result,
|
|
|
|
|
codesPostaux: [codePostal]
|
|
|
|
|
}))
|
|
|
|
|
);
|
2021-06-16 09:28:07 +02:00
|
|
|
|
|
|
|
|
|
// Some very large cities (like Paris) have A LOT of associated postal codes.
|
|
|
|
|
// As we generated one result per postal code, we now have a lot of results
|
|
|
|
|
// for the same city. If the number of results is above the threshold, we use
|
|
|
|
|
// local search to narrow the results.
|
|
|
|
|
const limit = searchResultsLimit(term);
|
2021-06-15 14:36:37 +02:00
|
|
|
|
if (expandedResults.length > limit) {
|
|
|
|
|
return matchSorter(expandedResults, term, {
|
|
|
|
|
keys: [(item) => `${item.nom} (${item.codesPostaux[0]})`, 'code'],
|
|
|
|
|
sorter: (rankedItems) => rankedItems
|
|
|
|
|
}).slice(0, limit + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return expandedResults;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-26 16:21:47 +02:00
|
|
|
|
const placeholderDepartements = [
|
|
|
|
|
['63 – Puy-de-Dôme', 'Clermont-Ferrand'],
|
|
|
|
|
['77 – Seine-et-Marne', 'Melun'],
|
|
|
|
|
['22 – Côtes d’Armor', 'Saint-Brieuc'],
|
|
|
|
|
['47 – Lot-et-Garonne', 'Agen']
|
|
|
|
|
];
|
|
|
|
|
const [placeholderDepartement, placeholderCommune] =
|
|
|
|
|
placeholderDepartements[
|
|
|
|
|
Math.floor(Math.random() * (placeholderDepartements.length - 1))
|
|
|
|
|
];
|
|
|
|
|
|
2020-10-07 17:42:41 +02:00
|
|
|
|
function ComboCommunesSearch(params) {
|
2021-11-17 12:52:47 +01:00
|
|
|
|
const hiddenDepartementFieldId = `${params.hiddenFieldId}:departement`;
|
|
|
|
|
const hiddenDepartementField = useMemo(
|
|
|
|
|
() =>
|
|
|
|
|
document.querySelector(`input[data-attr="${hiddenDepartementFieldId}"]`),
|
|
|
|
|
[params.hiddenFieldId]
|
|
|
|
|
);
|
|
|
|
|
const hiddenCodeDepartementField = useMemo(
|
|
|
|
|
() =>
|
|
|
|
|
document.querySelector(
|
|
|
|
|
`input[data-attr="${params.hiddenFieldId}:code_departement"]`
|
|
|
|
|
),
|
|
|
|
|
[params.hiddenFieldId]
|
|
|
|
|
);
|
2021-10-26 16:21:47 +02:00
|
|
|
|
const inputId = useMemo(
|
|
|
|
|
() =>
|
|
|
|
|
document.querySelector(`input[data-uuid="${params.hiddenFieldId}"]`)?.id,
|
|
|
|
|
[params.hiddenFieldId]
|
|
|
|
|
);
|
2021-11-17 12:52:47 +01:00
|
|
|
|
const [departementCode, setDepartementCode] = useState(
|
|
|
|
|
() => hiddenCodeDepartementField?.value
|
|
|
|
|
);
|
|
|
|
|
const departementValue = useMemo(
|
|
|
|
|
() => hiddenDepartementField?.value,
|
|
|
|
|
[hiddenDepartementField]
|
|
|
|
|
);
|
2021-10-26 16:21:47 +02:00
|
|
|
|
const departementDescribedBy = `${inputId}_departement_notice`;
|
|
|
|
|
const communeDescribedBy = `${inputId}_commune_notice`;
|
|
|
|
|
|
2020-10-07 17:42:41 +02:00
|
|
|
|
return (
|
2021-02-16 13:54:16 +01:00
|
|
|
|
<QueryClientProvider client={queryClient}>
|
2021-10-26 16:21:47 +02:00
|
|
|
|
<div>
|
|
|
|
|
<div className="notice" id={departementDescribedBy}>
|
|
|
|
|
<p>
|
|
|
|
|
Choisissez le département dans lequel se situe la commune. Vous
|
|
|
|
|
pouvez entrer le nom ou le code.
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
<ComboDepartementsSearch
|
2021-11-17 12:52:47 +01:00
|
|
|
|
value={departementValue}
|
2021-10-26 16:21:47 +02:00
|
|
|
|
inputId={!departementCode ? inputId : null}
|
|
|
|
|
aria-describedby={departementDescribedBy}
|
|
|
|
|
placeholder={placeholderDepartement}
|
2021-11-17 12:52:47 +01:00
|
|
|
|
required={params.mandatory}
|
|
|
|
|
onChange={(value, result) => {
|
2021-10-26 16:21:47 +02:00
|
|
|
|
setDepartementCode(result?.code);
|
2021-11-17 12:52:47 +01:00
|
|
|
|
if (hiddenDepartementField && hiddenCodeDepartementField) {
|
|
|
|
|
hiddenDepartementField.setAttribute('value', value);
|
|
|
|
|
hiddenCodeDepartementField.setAttribute('value', result?.code);
|
|
|
|
|
}
|
2021-10-26 16:21:47 +02:00
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
{departementCode ? (
|
|
|
|
|
<div>
|
|
|
|
|
<div className="notice" id={communeDescribedBy}>
|
|
|
|
|
<p>
|
|
|
|
|
Choisissez la commune. Vous pouver entre le nom ou le code postal.
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
<ComboSearch
|
|
|
|
|
autoFocus
|
|
|
|
|
inputId={inputId}
|
|
|
|
|
aria-describedby={communeDescribedBy}
|
|
|
|
|
placeholder={placeholderCommune}
|
|
|
|
|
required={params.mandatory}
|
|
|
|
|
hiddenFieldId={params.hiddenFieldId}
|
|
|
|
|
scope="communes"
|
|
|
|
|
scopeExtra={departementCode}
|
|
|
|
|
minimumInputLength={2}
|
|
|
|
|
transformResult={({ code, nom, codesPostaux }) => [
|
|
|
|
|
code,
|
|
|
|
|
`${nom} (${codesPostaux[0]})`
|
|
|
|
|
]}
|
|
|
|
|
transformResults={expandResultsWithMultiplePostalCodes}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
) : null}
|
2021-02-16 13:54:16 +01:00
|
|
|
|
</QueryClientProvider>
|
2020-10-07 17:42:41 +02:00
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default ComboCommunesSearch;
|