@@ -83,13 +83,14 @@ class ParcelAdvancedSearch extends AbstractAdvancedSearch {
8383 /** Show error message for a given input
8484 * @param {String } name Input name
8585 * @param {String } [message] Message to show (if none remove message)
86+ * @param {String } [what="error"] Message type (error, warning, info)
8687 * @private
8788 */
88- _showMessage ( name , message ) {
89+ _showMessage ( name , message , what ) {
8990 const div = this . element . querySelector ( "[name='" + name + "']" ) . parentElement . querySelector ( ".GPMessagesGroup" ) ;
9091 if ( message ) {
9192 const msg = document . createElement ( "p" ) ;
92- msg . className = "fr-message fr-message--error" ;
93+ msg . className = "fr-message fr-message--" + ( what || " error") ;
9394 msg . textContent = message ;
9495 div . innerHTML = "" ;
9596 div . appendChild ( msg ) ;
@@ -98,6 +99,38 @@ class ParcelAdvancedSearch extends AbstractAdvancedSearch {
9899 }
99100 }
100101
102+ /** Change the section
103+ * @private
104+ */
105+ setSection ( ) {
106+ const prefix = this . prefixInput . value ;
107+ const section = this . sectionInput . value ;
108+ this . numberList . innerHTML = "" ;
109+ this . _showMessage ( "section" , "chargement en cours" , "info" ) ;
110+ this . _fetchCadastre ( this . communeId , prefix , section ) . then ( data => {
111+ this . _showMessage ( "section" , "" ) ;
112+ const section = this . sectionInput . value ;
113+ if ( data && data . features && data . features [ 0 ] . properties . section === section ) {
114+ this . numberInput . focus ( ) ;
115+ const numbers = [ ] ;
116+ data . features . forEach ( numero => numbers . push ( numero . properties . numero ) ) ;
117+ // Sort numbers
118+ numbers . sort ( ) . forEach ( numero => {
119+ const option = document . createElement ( "li" ) ;
120+ option . value = option . textContent = numero . replace ( / ^ 0 { 1 , 4 } / g, "" ) ;
121+ option . addEventListener ( "click" , ( ) => {
122+ this . numberInput . value = option . value ;
123+ this . _onSearch ( ) ;
124+ this . numberInput . blur ( ) ;
125+ this . numberInput . ariaExpanded = "false" ;
126+ } ) ;
127+ this . numberList . appendChild ( option ) ;
128+ } ) ;
129+ this . filterListNumber ( ) ;
130+ }
131+ } ) ;
132+ }
133+
101134 /**
102135 * Show sections for a given prefix
103136 * @param {String } prefix Prefix code
@@ -110,10 +143,14 @@ class ParcelAdvancedSearch extends AbstractAdvancedSearch {
110143 section . value = "" ;
111144 section . textContent = "Sélectionner une section" ;
112145 this . sectionInput . appendChild ( section ) ;
146+ let previous = "" ;
113147 this . feuilles [ prefix ] . sort ( ) . forEach ( key => {
114- const section = document . createElement ( "option" ) ;
115- section . value = section . textContent = key ;
116- this . sectionInput . appendChild ( section ) ;
148+ if ( key !== previous ) {
149+ const section = document . createElement ( "option" ) ;
150+ section . value = section . textContent = key ;
151+ this . sectionInput . appendChild ( section ) ;
152+ previous = key ;
153+ }
117154 } ) ;
118155 this . sectionInput . removeAttribute ( "disabled" ) ;
119156 this . sectionInput . focus ( ) ;
@@ -133,8 +170,10 @@ class ParcelAdvancedSearch extends AbstractAdvancedSearch {
133170 this . communeId = id ;
134171 prefixInput . innerHTML = "" ;
135172 if ( id ) {
173+ this . _showMessage ( "commCode" , "chargement en cours" , "info" ) ;
136174 // Fetch prefixes and sections for the selected commune
137- this . _fetchFeuille ( id ) . then ( data => {
175+ this . _fetchCadastre ( id ) . then ( data => {
176+ this . _showMessage ( "commCode" , "" ) ;
138177 this . feuilles = { } ;
139178
140179 data . features . forEach ( feuille => {
@@ -143,10 +182,14 @@ class ParcelAdvancedSearch extends AbstractAdvancedSearch {
143182 }
144183 this . feuilles [ feuille . properties . com_abs ] . push ( feuille . properties . section ) ;
145184 } ) ;
185+ let previous = "" ;
146186 Object . keys ( this . feuilles ) . sort ( ) . forEach ( key => {
147- const prefix = document . createElement ( "option" ) ;
148- prefix . value = prefix . textContent = key ;
149- prefixInput . appendChild ( prefix ) ;
187+ if ( key !== previous ) {
188+ const prefix = document . createElement ( "option" ) ;
189+ prefix . value = prefix . textContent = key ;
190+ prefixInput . appendChild ( prefix ) ;
191+ previous = key ;
192+ }
150193 } ) ;
151194 this . setFeuille ( "000" ) ;
152195 } ) ;
@@ -223,13 +266,15 @@ class ParcelAdvancedSearch extends AbstractAdvancedSearch {
223266 if ( index >= autoOptions . length ) {
224267 index = - 1 ;
225268 }
269+ e . preventDefault ( ) ;
226270 break ;
227271 }
228272 case "ArrowUp" : {
229273 index -- ;
230274 if ( index < - 1 ) {
231275 index += autoOptions . length + 1 ;
232276 }
277+ e . preventDefault ( ) ;
233278 break ;
234279 }
235280 case "Enter" : {
@@ -238,16 +283,13 @@ class ParcelAdvancedSearch extends AbstractAdvancedSearch {
238283 autoOptions [ index ] . click ( ) ;
239284 index = - 1 ;
240285 }
241- e . preventDefault ( ) ;
242- e . stopPropagation ( ) ;
243286 break ;
244287 }
245288 default : {
246289 break ;
247290 }
248291 }
249292 if ( selectedIndex !== index ) {
250- e . preventDefault ( ) ;
251293 // Update selected option
252294 autoOptions [ selectedIndex ] ?. classList . remove ( "active" ) ;
253295 selectedIndex = index ;
@@ -261,9 +303,8 @@ class ParcelAdvancedSearch extends AbstractAdvancedSearch {
261303
262304 // Show autocomplete on input
263305 comCodeInput . addEventListener ( "keyup" , e => {
264- e . preventDefault ( ) ;
265- e . stopPropagation ( ) ;
266306 if ( [ "ArrowUp" , "ArrowDown" , "Enter" , "Escape" ] . includes ( e . key ) ) {
307+ e . preventDefault ( ) ;
267308 return ;
268309 }
269310 showAutocomplete ( e ) ;
@@ -284,18 +325,22 @@ class ParcelAdvancedSearch extends AbstractAdvancedSearch {
284325 } else {
285326 this . _clearMessages ( ) ;
286327 }
328+ this . _showMessage ( "commCode" , "chargement en cours" , "info" ) ;
287329 this . _fetchCommuneData ( comCodeInput . value ) . then ( data => {
330+ this . _showMessage ( "commCode" , "" ) ;
288331 // Clear previous suggestions
289332 autocompleteList . innerHTML = "" ;
290333 communeName = "" ;
291334 if ( data . length === 0 ) {
292335 // errror message
336+ this . _showMessage ( "commCode" , "Aucune commune ne correspond à ce code INSEE ou code postal." ) ;
337+ this . setCommune ( ) ;
293338 } else if ( data . length === 1 ) {
294339 communeName = comCodeInput . value = `${ data [ 0 ] . code } (${ data [ 0 ] . nom } )` ;
295340 this . setCommune ( data [ 0 ] . code ) ;
296341 } else {
297342 data . forEach ( commune => {
298- const type = commune . codesPostaux ? "code postal " : "code INSEE " ;
343+ const type = commune . codesPostaux ? "code INSEE " : "code postal " ;
299344 const option = document . createElement ( "li" ) ;
300345 option . className = "GPautoCompleteOption" ;
301346 option . setAttribute ( "role" , "option" ) ;
@@ -319,14 +364,56 @@ class ParcelAdvancedSearch extends AbstractAdvancedSearch {
319364 prefixInput . addEventListener ( "change" , ( ) => {
320365 this . setFeuille ( prefixInput . value ) ;
321366 } ) ;
322- // Fetch parcelles
367+ // Fetch parcelles number
323368 sectionInput . addEventListener ( "change" , ( ) => {
324369 if ( sectionInput . value ) {
325370 this . numberInput . removeAttribute ( "disabled" ) ;
371+ this . setSection ( ) ;
326372 } else {
327373 this . numberInput . setAttribute ( "disabled" , "disabled" ) ;
328374 }
329375 } ) ;
376+
377+ // Handle listbox for number input
378+ this . numberInput . addEventListener ( "focus" , ( ) => {
379+ this . numberInput . ariaExpanded = "true" ;
380+ } ) ;
381+ this . numberInput . addEventListener ( "blur" , ( e ) => {
382+ if ( e . relatedTarget !== this . numberList ) {
383+ this . numberInput . ariaExpanded = "false" ;
384+ }
385+ } ) ;
386+
387+ // Filter number list on keyup
388+ this . numberInput . addEventListener ( "keyup" , ( e ) => {
389+ this . filterListNumber ( ) ;
390+ if ( e . key === "Enter" ) {
391+ this . numberInput . ariaExpanded = "false" ;
392+ this . numberInput . blur ( ) ;
393+ }
394+ } ) ;
395+ }
396+
397+
398+ /** Filter listbox options
399+ */
400+ filterListNumber ( ) {
401+ const filter = this . numberInput . value . toUpperCase ( ) ;
402+ const options = this . numberList . querySelectorAll ( "li" ) ;
403+ let hasNumber = false ;
404+ options . forEach ( option => {
405+ if ( option . textContent . toUpperCase ( ) . indexOf ( filter ) > - 1 ) {
406+ option . style . display = "" ;
407+ hasNumber = true ;
408+ } else {
409+ option . style . display = "none" ;
410+ }
411+ } ) ;
412+ if ( ! hasNumber ) {
413+ this . _showMessage ( "numero" , "Aucun numéro de parcelle ne correspond à cette saisie." ) ;
414+ } else {
415+ this . _showMessage ( "numero" , "" ) ;
416+ }
330417 }
331418
332419 /**
@@ -401,16 +488,21 @@ class ParcelAdvancedSearch extends AbstractAdvancedSearch {
401488 return Promise . all ( responses . map ( res => res . json ( ) ) ) . then ( json => {
402489 return json [ 0 ] . concat ( json [ 1 ] ) ;
403490 } ) ;
491+ } ) . catch ( error => {
492+ this . _showMessage ( "commCode" , "Une erreur est survenue lors de la récupération des données de la commune." ) ;
493+ return [ ] ;
404494 } ) ;
405495 }
406496
407497 /**
408498 * Récupère les feuilles cadastrales d'une commune via le WFS Geopf
409499 * @private
410500 * @param {String } code Code INSEE de la commune
411- * @returns
501+ * @param {String } [prefix] Préfixe de la parcelle
502+ * @param {String } [section] Section de la parcelle
503+ * @returns {Promise } Promesse avec les données GeoJSON
412504 */
413- async _fetchFeuille ( code ) {
505+ async _fetchCadastre ( code , prefix , section ) {
414506 const domtom = [ "97" , "98" ] . includes ( code . slice ( 0 , 2 ) ) ;
415507 const dep = code . slice ( 0 , domtom ? 3 : 2 ) ;
416508 const com = code . slice ( domtom ? 3 : 2 , 5 ) ;
@@ -419,16 +511,33 @@ class ParcelAdvancedSearch extends AbstractAdvancedSearch {
419511 service : "WFS" ,
420512 version : "2.0.0" ,
421513 request : "GetFeature" ,
422- typename : "CADASTRALPARCELS.PARCELLAIRE_EXPRESS:feuille" ,
514+ typename : "CADASTRALPARCELS.PARCELLAIRE_EXPRESS:" + ( section ? "parcelle" : " feuille") ,
423515 outputFormat : "application/json" ,
424516 srsName : "CRS:84" ,
425517 count : "1000" ,
426- propertyName : "com_abs,section" ,
427- cql_filter : `code_dep='${ dep } ' and code_com='${ com } '`
518+ propertyName : section ? "com_abs,section,numero" : "com_abs,section" ,
519+ cql_filter : `code_dep='${ dep } ' and code_com='${ com } '` + ( section ? ` and com_abs=' ${ prefix } ' and section=' ${ section } '` : "" )
428520 } ;
429521 const queryString = new URLSearchParams ( params ) . toString ( ) ;
430522 const fullUrl = url + queryString ;
431- const response = await fetch ( fullUrl ) ;
523+ // Abort previous request
524+ if ( this . controller ) {
525+ this . controller . abort ( ) ;
526+ }
527+ // New request
528+ this . controller = new AbortController ( ) ;
529+ const response = await fetch ( fullUrl , {
530+ headers : {
531+ "Content-Type" : "application/json" ,
532+ } ,
533+ signal : this . controller . signal
534+ } ) . catch ( error => {
535+ return null ;
536+ } ) ;
537+ this . controller = null ;
538+ if ( ! response ) {
539+ return { } ;
540+ }
432541 const data = await response . json ( ) ;
433542 return data ;
434543 }
@@ -504,9 +613,18 @@ class ParcelAdvancedSearch extends AbstractAdvancedSearch {
504613 numberInput . name = "numero" ;
505614 numberInput . title = "Numéro de la parcelle" ;
506615 numberInput . autocomplete = "off" ;
616+ numberInput . ariaExpanded = "false" ;
507617 numberInput . id = Helper . getUid ( "ParcelAdvancedSearch-number-" ) ;
508618 numberInput . setAttribute ( "disabled" , "disabled" ) ;
509- this . _getLabelContainer ( "Numéro*" , "fr-input-group" , numberInput , "" ) ;
619+ const numDiv = this . _getLabelContainer ( "Numéro*" , "fr-input-group" , numberInput , "" ) ;
620+
621+ const numberList = this . numberList = document . createElement ( "ul" ) ;
622+ numberList . className = "GPautoCompleteList" ;
623+ numberList . id = Helper . getUid ( "GPautoCompleteList-" ) ;
624+ numberList . setAttribute ( "role" , "listbox" ) ;
625+ numberList . setAttribute ( "tabindex" , "-1" ) ;
626+ numberList . setAttribute ( "aria-label" , "Propositions" ) ;
627+ numDiv . insertBefore ( numberList , numberInput . nextSibling ) ;
510628 }
511629
512630 /** Do search
@@ -515,7 +633,9 @@ class ParcelAdvancedSearch extends AbstractAdvancedSearch {
515633 * @param {PointerEvent } e Événement de soumission
516634 */
517635 _onSearch ( e ) {
518- super . _onSearch ( e ) ;
636+ if ( e ) {
637+ super . _onSearch ( e ) ;
638+ }
519639
520640 this . _clearMessages ( ) ;
521641
@@ -526,7 +646,7 @@ class ParcelAdvancedSearch extends AbstractAdvancedSearch {
526646 const section = this . sectionInput . value ;
527647 const number = this . numberInput . value ;
528648 if ( ! prefix ) {
529- this . _showMessage ( "prefix" , "Le préfixe est obligatoire." ) ;
649+ // this._showMessage("prefix", "Le préfixe est obligatoire.");
530650 return ;
531651 } else if ( ! section ) {
532652 this . _showMessage ( "section" , "La section est obligatoire." ) ;
@@ -558,6 +678,7 @@ class ParcelAdvancedSearch extends AbstractAdvancedSearch {
558678 _onErase ( e ) {
559679 super . _onErase ( e ) ;
560680 this . setCommune ( ) ;
681+ this . _clearMessages ( ) ;
561682 }
562683
563684}
0 commit comments