From df39c4bfba714c7d50558900d45b929933652ac6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timoth=C3=A9e=20Poussard?= <tpoussard@neogeo.fr>
Date: Thu, 24 Aug 2023 16:11:18 +0200
Subject: [PATCH] geocoder increase limit, add coordinates, improve behavior &
 style

---
 src/components/Map/Geocoder.vue    | 143 +++++++++++++++++++++++------
 src/components/Map/Geolocation.vue |   2 +-
 src/services/map-service.js        |   8 ++
 3 files changed, 126 insertions(+), 27 deletions(-)

diff --git a/src/components/Map/Geocoder.vue b/src/components/Map/Geocoder.vue
index 028a6a75..bced9885 100644
--- a/src/components/Map/Geocoder.vue
+++ b/src/components/Map/Geocoder.vue
@@ -1,20 +1,21 @@
 <template>
   <div
-    :class="{ expanded: isExpanded }"
-    class="geocoder-container"
+    id="geocoder-container"
+    :class="{ isExpanded }"
   >
     <button
       class="button-geocoder"
-      @click="isExpanded = !isExpanded"
+      @click="toggleGeocoder"
     >
       <i class="search icon" />
     </button>
     <Multiselect
       v-if="isExpanded"
+      ref="multiselect"
       v-model="selection"
       class="expanded-geocoder"
       :options="addresses"
-      :options-limit="5"
+      :options-limit="limit"
       :allow-empty="true"
       track-by="label"
       label="label"
@@ -31,6 +32,7 @@
       :preserve-search="true"
       @search-change="search"
       @select="select"
+      @open="retrievePreviousPlaces"
       @close="close"
     >
       <template
@@ -76,14 +78,18 @@ const apiAdressAxios = axios.create({
   baseURL: 'https://api-adresse.data.gouv.fr',
   withCredentials: false,
 });
+
 export default {
   name: 'Geocoder',
+
   components: {
     Multiselect
   },
+
   data() {
     return {
       loading: false,
+      limit: 10,
       selection: null,
       text: null,
       selectedAddress: null,
@@ -93,19 +99,36 @@ export default {
       isExpanded: false
     };
   },
+
   mounted() {
     this.addressTextChange = new Subject();
     this.addressTextChange.pipe(debounceTime(200)).subscribe((res) => this.getAddresses(res));
   },
+
   methods: {
+    toggleGeocoder() {
+      this.isExpanded = !this.isExpanded;
+      if (this.isExpanded) {
+        this.retrievePreviousPlaces();
+        this.$nextTick(()=> this.$refs.multiselect.activate());
+      }
+    },
+
     getAddresses(query){
-      const limit = 5;
-      apiAdressAxios.get(`https://api-adresse.data.gouv.fr/search/?q=${query}&limit=${limit}`)
+      if (query.length < 3) {
+        this.addresses = [];
+        return;
+      }
+      const coords = mapService.getMapCenter();
+      let url = `https://api-adresse.data.gouv.fr/search/?q=${query}&limit=${this.limit}`;
+      if (coords) url += `&lon=${coords[0]}&lat=${coords[1]}`;
+      apiAdressAxios.get(url)
         .then((retour) => {
           this.resultats = retour.data.features;
           this.addresses = retour.data.features.map(x=>x.properties);
         });
     },
+
     selectAddresse(event) {
       this.selectedAddress = event;
       if (this.selectedAddress !== null && this.selectedAddress.geometry) {
@@ -122,27 +145,59 @@ export default {
         mapService.zoomTo(this.selectedAddress.geometry.coordinates, zoomlevel);
         // On ajoute un point pour localiser la ville
         mapService.addOverlay(this.selectedAddress.geometry.coordinates);
+        this.setLocalstorageSelectedAdress(this.selectedAddress);
       }
     },
+
     search(text) {
       this.text = text;
       this.addressTextChange.next(this.text);
     },
+
     select(e) {
       this.selectAddresse(this.resultats.find(x=>x.properties.label === e.label));
       this.$emit('select', e);
     },
+
     close() {
       this.$emit('close', this.selection);
-    }
+    },
+
+    setLocalstorageSelectedAdress(newAdress) {
+      let selectedAdresses = JSON.parse(localStorage.getItem('geocontrib-selected-adresses'));
+      selectedAdresses = Array.isArray(selectedAdresses) ? selectedAdresses : [];
+      selectedAdresses = [ newAdress, ...selectedAdresses ];
+
+      const uniqueLabels = [...new Set(selectedAdresses.map(el => el.properties.label))];
+      const uniqueAdresses = uniqueLabels.map((label) => {
+        return selectedAdresses.find(adress => adress.properties.label === label);
+      });
+
+      localStorage.setItem(
+        'geocontrib-selected-adresses',
+        JSON.stringify(uniqueAdresses.slice(0, 5))
+      );
+    },
+
+    getLocalstorageSelectedAdress() {
+      return JSON.parse(localStorage.getItem('geocontrib-selected-adresses')) || [];
+    },
+
+    retrievePreviousPlaces() {
+      const previousAdresses = this.getLocalstorageSelectedAdress();
+      if (previousAdresses.length > 0) {
+        this.addresses = previousAdresses.map(x=>x.properties);
+        this.resultats = previousAdresses;
+      }
+    },
   }
 };
 </script>
 
-<style scoped lang="less">
-.geocoder-container {
+<style lang="less">
+#geocoder-container {
   position: absolute;
-  right: 0.5em;
+  right: 6px;
   // each button have (more or less depends on borders) .5em space between
   // zoom buttons are 60px high, geolocation and full screen button is 34px high with borders
   top: calc(1.6em + 60px + 34px + 34px);
@@ -151,7 +206,7 @@ export default {
   border: 2px solid rgba(0,0,0,.2);
   background-clip: padding-box;
   padding: 0;
-  border-radius: 2px;
+  border-radius: 4px;
   display: flex;
 
   .button-geocoder {
@@ -161,8 +216,8 @@ export default {
     text-align: center;
     background-color: #fff;
     color: rgb(39, 39, 39);
-    width: 28px;
-    height: 28px;
+    width: 30px;
+    height: 30px;
     font: 700 18px Lucida Console,Monaco,monospace;
     border-radius: 2px;
     line-height: 1.15;
@@ -180,21 +235,57 @@ export default {
   .expanded-geocoder {
     max-width: 400px;
   }
-}
 
-.expanded {
-  .button-geocoder {
-    height: 40px;
-    color: rgb(99, 99, 99);
+  &&.isExpanded {
+    .button-geocoder {
+      height: 41px;
+      color: rgb(99, 99, 99);
+      border-radius: 2px 0 0 2px;
+    }
   }
-}
 
-#marker {
-  width: 20px;
-  height: 20px;
-  border: 1px solid rgb(136, 66, 0);
-  border-radius: 10px;
-  background-color: rgb(201, 114, 15);
-  opacity: 0.7;
+  #marker {
+    width: 20px;
+    height: 20px;
+    border: 1px solid rgb(136, 66, 0);
+    border-radius: 10px;
+    background-color: rgb(201, 114, 15);
+    opacity: 0.7;
+  }
+  
+  // /* keep placeholder width when opening dropdown */
+  .multiselect {
+    min-width: 208px;
+  }
+  /* keep font-weight from overide of semantic classes */
+  .multiselect__placeholder, .multiselect__content, .multiselect__tags  {
+    font-weight: initial !important;
+  }
+  /* keep placeholder eigth */
+  .multiselect .multiselect__placeholder {
+    margin-bottom: 9px !important;
+    padding-top: 1px;
+  }
+  /* keep placeholder height when opening dropdown without selection */
+  input.multiselect__input {
+    padding: 3px 0 0 0 !important;
+  }
+  /* keep placeholder height when opening dropdown with already a value selected */
+  .multiselect__tags .multiselect__single {
+    padding: 1px 0 0 0 !important;
+    margin-bottom: 9px;
+  }
+  .multiselect__tags {
+    border: 0 !important;
+    min-height: 41px !important;
+  }
+  .multiselect input {
+    line-height: 1em !important;
+    padding: 0 !important;
+  }
+  .multiselect__content-wrapper {
+    border: 2px solid rgba(0,0,0,.2);
+  }
 }
 </style>
+      
\ No newline at end of file
diff --git a/src/components/Map/Geolocation.vue b/src/components/Map/Geolocation.vue
index cc83f8c9..d94c83b4 100644
--- a/src/components/Map/Geolocation.vue
+++ b/src/components/Map/Geolocation.vue
@@ -51,7 +51,7 @@ button.button-geolocation {
   width: 30px;
   height: 30px;
   font: 700 18px Lucida Console,Monaco,monospace;
-  border-radius: 4px;
+  border-radius: 2px;
   line-height: 1.15;
   cursor: pointer;
 }
diff --git a/src/services/map-service.js b/src/services/map-service.js
index 01d6cd8b..b9c5b31c 100644
--- a/src/services/map-service.js
+++ b/src/services/map-service.js
@@ -714,6 +714,14 @@ const mapService = {
       stopEvent: false
     });
     this.map.addOverlay(marker);
+  },
+
+  getMapCenter() {
+    const location = this.map.getView().getCenter();
+    if (location) {
+      return transform(location, 'EPSG:3857', 'EPSG:4326');
+    }
+    return null;
   }
 };
 
-- 
GitLab