Skip to content
Snippets Groups Projects
Geocoder.vue 4.77 KiB
Newer Older
DESPRES Damien's avatar
DESPRES Damien committed
<template>
Florent Lavelle's avatar
Florent Lavelle committed
  <div
    :class="{ expanded: isExpanded }"
    class="geocoder-container"
  >
    <button
      class="button-geocoder"
      @click="isExpanded = !isExpanded"
    >
      <i class="search icon" />
    </button>
DESPRES Damien's avatar
DESPRES Damien committed
    <Multiselect
Florent Lavelle's avatar
Florent Lavelle committed
      v-if="isExpanded"
DESPRES Damien's avatar
DESPRES Damien committed
      v-model="selection"
Florent Lavelle's avatar
Florent Lavelle committed
      class="expanded-geocoder"
DESPRES Damien's avatar
DESPRES Damien committed
      :options="addresses"
      :options-limit="5"
      :allow-empty="true"
      track-by="label"
      label="label"
      :show-labels="false"
      :reset-after="true"
      select-label=""
      selected-label=""
      deselect-label=""
      :searchable="true"
      :placeholder="placeholder"
      :show-no-results="true"
      :loading="loading"
      :clear-on-select="false"
      :preserve-search="true"
      @search-change="search"
      @select="select"
      @close="close"
    >
      <template
        slot="option"
        slot-scope="props"
      >
        <div class="option__desc">
          <span class="option__title">{{ props.option.label }}</span>
        </div>
      </template>
      <template slot="clear">
        <div
          v-if="selection"
          class="multiselect__clear"
          @click.prevent.stop="selection = null"
        >
          <i class="close icon" />
        </div>
      </template>
      <span slot="noResult">
        Aucun résultat.
      </span>
      <span slot="noOptions">
        Saisissez les premiers caractères ...
      </span>
    </Multiselect>
    <div style="display: none;">
      <div
        id="marker"
        title="Marker"
      />
    </div>
  </div>
</template>

<script>
import Multiselect from 'vue-multiselect';
import { Subject } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
import axios from 'axios';
import mapService from '@/services/map-service';
const apiAdressAxios = axios.create({
  baseURL: 'https://api-adresse.data.gouv.fr',
  withCredentials: false,
});
export default {
  name: 'Geocoder',
  components: {
    Multiselect
  },
  data() {
    return {
      loading: false,
      selection: null,
      text: null,
      selectedAddress: null,
      addresses: [],
      resultats: [],
Florent Lavelle's avatar
Florent Lavelle committed
      placeholder: 'Rechercher une adresse ...',
      isExpanded: false
DESPRES Damien's avatar
DESPRES Damien committed
    };
  },
  mounted() {
    this.addressTextChange = new Subject();
    this.addressTextChange.pipe(debounceTime(200)).subscribe((res) => this.getAddresses(res));
  },
  methods: {
    getAddresses(query){
      const limit = 5;
      apiAdressAxios.get(`https://api-adresse.data.gouv.fr/search/?q=${query}&limit=${limit}`)
        .then((retour) => {
          this.resultats = retour.data.features;
          this.addresses = retour.data.features.map(x=>x.properties);
DESPRES Damien's avatar
DESPRES Damien committed
        });
    },
    selectAddresse(event) {
      this.selectedAddress = event;
      if (this.selectedAddress !== null && this.selectedAddress.geometry) {
        let zoomlevel = 14;
        const { type } = this.selectedAddress.properties;
DESPRES Damien's avatar
DESPRES Damien committed
        if (type === 'housenumber') {
          zoomlevel = 19;
        } else if (type === 'street') {
          zoomlevel = 16;
        } else if (type === 'locality') {
          zoomlevel = 16;
        }
        // On fait le zoom
        mapService.zoomTo(this.selectedAddress.geometry.coordinates, zoomlevel);
        // On ajoute un point pour localiser la ville
        mapService.addOverlay(this.selectedAddress.geometry.coordinates);
      }
    },
    search(text) {
      this.text = text;
      this.addressTextChange.next(this.text);
    },
    select(e) {
      this.selectAddresse(this.resultats.find(x=>x.properties.label === e.label));
DESPRES Damien's avatar
DESPRES Damien committed
      this.$emit('select', e);
    },
    close() {
      this.$emit('close', this.selection);
    }
  }
};
</script>

Florent Lavelle's avatar
Florent Lavelle committed
<style scoped lang="less">
Florent Lavelle's avatar
Florent Lavelle committed
.geocoder-container {
DESPRES Damien's avatar
DESPRES Damien committed
  position: absolute;
Florent Lavelle's avatar
Florent Lavelle committed
  right: 0.5em;
  // 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);
DESPRES Damien's avatar
DESPRES Damien committed
  pointer-events: auto;
Florent Lavelle's avatar
Florent Lavelle committed
  border: 2px solid rgba(0,0,0,.2);
  background-clip: padding-box;
  padding: 0;
  border-radius: 2px;
Florent Lavelle's avatar
Florent Lavelle committed
  display: flex;

  .button-geocoder {
    border: none;
    padding: 0;
    margin: 0;
    text-align: center;
    background-color: #fff;
    color: rgb(39, 39, 39);
    width: 28px;
    height: 28px;
    font: 700 18px Lucida Console,Monaco,monospace;
    border-radius: 2px;
    line-height: 1.15;

    i {
      margin: 0;
      font-size: 0.9em;
    }
  }
  .button-geocoder:hover {
    cursor: pointer;
    background-color: #ebebeb;
  }

  .expanded-geocoder {
    max-width: 400px;
  }
}

.expanded {
  .button-geocoder {
    height: 40px;
    color: rgb(99, 99, 99);
  }
DESPRES Damien's avatar
DESPRES Damien committed
}
DESPRES Damien's avatar
DESPRES Damien committed
#marker {
Florent Lavelle's avatar
Florent Lavelle committed
  width: 20px;
  height: 20px;
  border: 1px solid rgb(136, 66, 0);
  border-radius: 10px;
  background-color: rgb(201, 114, 15);
  opacity: 0.7;
DESPRES Damien's avatar
DESPRES Damien committed
}
</style>