120 lines
3.0 KiB
Vue
120 lines
3.0 KiB
Vue
|
<template>
|
||
|
<div>
|
||
|
<span class="form">
|
||
|
<input type="text" placeholder="place" v-model="searchInput" @keydown="doSearch" />
|
||
|
<button @click="clearSearch()" :disabled="searchInput.length === 0">
|
||
|
<i class="fa fa-trash-alt"></i>
|
||
|
</button>
|
||
|
</span>
|
||
|
|
||
|
<p v-if="searchStatus">{{ searchStatus }}</p>
|
||
|
<ul class="results" v-if="results.length > 0">
|
||
|
<li
|
||
|
v-for="result in results"
|
||
|
@click="openPopup(result)"
|
||
|
>
|
||
|
<router-link :to="{ name: 'position', params: {
|
||
|
lat: result.coordinates[0],
|
||
|
lng: result.coordinates[1] } }">{{result.label}}</router-link>
|
||
|
|
||
|
<i class="fa fa-save" @click="save(result)"></i>
|
||
|
</li>
|
||
|
</ul>
|
||
|
<p v-if="searchInput.length > 0 && searchStatus === null && results.length == 0">
|
||
|
No results found.
|
||
|
</p>
|
||
|
</div>
|
||
|
</template>
|
||
|
|
||
|
<script>
|
||
|
import debounce from 'lodash.debounce';
|
||
|
|
||
|
import store from '../store';
|
||
|
|
||
|
const GEOCODING_URL = 'https://demo.addok.xyz/search/?q={query}&limit=20';
|
||
|
|
||
|
export default {
|
||
|
name: 'Menu',
|
||
|
|
||
|
data() {
|
||
|
return {
|
||
|
store,
|
||
|
searchInput: '',
|
||
|
searchStatus: null,
|
||
|
results: [],
|
||
|
}
|
||
|
},
|
||
|
|
||
|
methods: {
|
||
|
debouncedSearch: debounce(async function() {
|
||
|
if (this.searchInput.length === 0) {
|
||
|
// Just cleared the input.
|
||
|
this.searchStatus = null;
|
||
|
this.results = [];
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Launch search.
|
||
|
this.searchStatus = "Searching...";
|
||
|
|
||
|
let resp = await fetch(GEOCODING_URL.replace('{query}', this.searchInput));
|
||
|
let results = await resp.json();
|
||
|
this.searchStatus = null;
|
||
|
|
||
|
this.results = (results && results.features && results.features.map(feature => {
|
||
|
let { properties: {label: label}, geometry: { coordinates: coordinates }} = feature;
|
||
|
coordinates.unshift(coordinates.pop());
|
||
|
return {label, coordinates}
|
||
|
})) || [];
|
||
|
}, 300),
|
||
|
|
||
|
doSearch() {
|
||
|
if (this.searchInput.length > 0) {
|
||
|
this.searchStatus = "Waiting for more characters...";
|
||
|
this.results = [];
|
||
|
}
|
||
|
this.debouncedSearch();
|
||
|
},
|
||
|
|
||
|
openPopup(loc) {
|
||
|
this.store.openPopup(loc.label, loc.coordinates[0], loc.coordinates[1]);
|
||
|
},
|
||
|
|
||
|
clearSearch() {
|
||
|
this.searchInput = '';
|
||
|
this.searchStatus = null;
|
||
|
this.results = [];
|
||
|
},
|
||
|
|
||
|
async save(loc) {
|
||
|
await store.addLocation(loc);
|
||
|
this.clearSearch();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
</script>
|
||
|
|
||
|
<style scoped>
|
||
|
.form {
|
||
|
display: flex;
|
||
|
align-items: center;
|
||
|
}
|
||
|
|
||
|
input {
|
||
|
flex-grow: 2;
|
||
|
margin-left: 0;
|
||
|
margin-right: 5px;
|
||
|
}
|
||
|
|
||
|
button {
|
||
|
display: block;
|
||
|
padding: 10px;
|
||
|
background-color: white;
|
||
|
border: 0;
|
||
|
}
|
||
|
|
||
|
.fa-save, .fa-trash-alt {
|
||
|
cursor: pointer;
|
||
|
}
|
||
|
</style>
|