Mercurial > gemma
diff client/src/components/map/Search.vue @ 1272:bc55ffaeb639
cleaned up client/src directory
better organization of files and directories, better naming, separation of admin and map context
author | Markus Kottlaender <markus@intevation.de> |
---|---|
date | Thu, 22 Nov 2018 07:07:12 +0100 |
parents | |
children | a7dd8a3356fc |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/components/map/Search.vue Thu Nov 22 07:07:12 2018 +0100 @@ -0,0 +1,223 @@ +<template> + <div :class="searchbarContainerStyle"> + <div class="input-group-prepend"> + <span @click="toggleSearchbar" :class="searchButtonStyle" for="search"> + <i class="fa fa-search d-print-none"></i> + </span> + </div> + <div class="searchgroup flex-fill"> + <input + @keyup.enter="takeFirstSearchresult" + v-if="showSearchbar" + id="search" + v-model="searchQuery" + type="text" + :class="searchInputStyle" + > + <div v-if="showSearchbar && searchResults !== null && !showInContextBox" class="searchresults border-top ui-element bg-white rounded-bottom d-print-none"> + <div v-for="entry of searchResults" :key="entry.name" class="border-top py-2"> + <a href="#" @click.prevent="moveToSearchResult(entry)">{{ entry.name }}</a> + </div> + </div> + </div> + </div> +</template> + +<style lang="sass" scoped> + .searchcontainer + height: $icon-height + opacity: $slight-transparent + + .searchbar-expanded + min-width: 600px + .searchbar + border-top-left-radius: 0 !important + border-bottom-left-radius: 0 !important + + .searchbar-collapsed + width: $icon-width !important + transition: $transition-fast + + .searchbar + height: $icon-height !important + box-shadow: none !important + &.rounded-top-right + border-radius: 0 !important + border-top-right-radius: $border-radius !important + + .searchlabel + &.rounded-top-left + border-radius: 0 !important + border-top-left-radius: $border-radius !important + + .input-group-text + height: $icon-height + width: $icon-width + + .input-group-prepend + .fa-search + color: #666 + + .searchresults + margin-left: -31px + max-height: 20rem + overflow: auto + > div:first-child + border-top: 0 !important +</style> + +<script> +/* + * This is Free Software under GNU Affero General Public License v >= 3.0 + * without warranty, see README.md and license for details. + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * License-Filename: LICENSES/AGPL-3.0.txt + * + * Copyright (C) 2018 by via donau + * – Österreichische Wasserstraßen-Gesellschaft mbH + * Software engineering by Intevation GmbH + * + * Author(s): + * Markus Kottländer <markus.kottlaender@intevation.de> + */ +import debounce from "lodash.debounce"; +import { mapState } from "vuex"; + +import { displayError } from "../../lib/errors.js"; +import { HTTP } from "../../lib/http"; + +const setFocus = () => document.querySelector("#search").focus(); + +export default { + name: "search", + data() { + return { + searchQueryIsDirty: false, + searchResults: null, + isSearching: false + }; + }, + computed: { + ...mapState("application", ["showSearchbar", "showInContextBox"]), + searchQuery: { + get() { + return this.$store.state.application.searchQuery; + }, + set(value) { + this.$store.commit("application/searchQuery", value); + } + }, + searchIndicator: function() { + if (this.isSearching) { + return "⟳"; + } else if (this.searchQueryIsDirty) { + return ""; + } else { + return "✓"; + } + }, + searchbarContainerStyle() { + return [ + "input-group searchcontainer ml-3 shadow-xs", + { + "searchbar-collapsed": !this.showSearchbar, + "searchbar-expanded": this.showSearchbar, + "d-flex": this.showInContextBox !== "imports", + "d-none": this.showInContextBox === "imports" + } + ]; + }, + searchInputStyle() { + return [ + "form-control ui-element search searchbar d-print-none border-0", + { "rounded-top-right": this.showInContextBox || this.searchResults } + ]; + }, + searchButtonStyle() { + return [ + "ui-element input-group-text p-0 d-flex border-0 justify-content-center searchlabel bg-white d-print-none", + { + rounded: !this.showSearchbar, + "rounded-left": this.showSearchbar, + "rounded-top-left": + this.showSearchbar && (this.showInContextBox || this.searchResults) + } + ]; + } + }, + watch: { + searchQuery: function() { + this.searchQueryIsDirty = true; + this.triggerSearch(); + } + }, + methods: { + takeFirstSearchresult() { + if (!this.searchResults || this.searchResults.length != 1) return; + this.moveToSearchResult(this.searchResults[0]); + }, + triggerSearch: debounce(function() { + this.doSearch(); + }, 500), + doSearch() { + this.isCalculating = true; + this.searchResults = null; + + if (this.searchQuery == "") { + return; + } + + HTTP.post( + "/search", + { string: this.searchQuery }, + { + headers: { + "X-Gemma-Auth": localStorage.getItem("token"), + "Content-type": "text/xml; charset=UTF-8" + } + } + ) + .then(response => { + // console.log("got:", response.data); + this.searchResults = response.data; + }) + .catch(error => { + const { status, data } = error.response; + displayError({ + title: "Backend Error", + message: `${status}: ${data.message || data}` + }); + }); + + this.isCalculating = false; + this.searchQueryIsDirty = false; + }, + moveToSearchResult(resultEntry) { + // DEBUG console.log("Moving to", resultEntry); + if (resultEntry.geom.type == "Point") { + let zoom = 11; + if (resultEntry.type === "bottleneck") zoom = 17; + if (resultEntry.type === "rhm") zoom = 15; + if (resultEntry.type === "city") zoom = 13; + + this.$store.commit("map/moveMap", { + coordinates: resultEntry.geom.coordinates, + zoom, + preventZoomOut: true + }); + } + // this.searchQuery = ""; // clear search query again + this.toggleSearchbar(); + }, + toggleSearchbar() { + if (!this.showInContextBox) { + if (!this.showSearchbar) { + setTimeout(setFocus, 300); + } + this.$store.commit("application/showSearchbar", !this.showSearchbar); + } + } + } +}; +</script>