Mercurial > gemma
diff client/src/components/Search.vue @ 1558:0ded4c56978e
refac: component filestructure. remove admin/map hierarchy
author | Thomas Junk <thomas.junk@intevation.de> |
---|---|
date | Wed, 12 Dec 2018 09:22:20 +0100 |
parents | client/src/components/map/Search.vue@9b81ac91a43e |
children | f2d24dceecc7 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/components/Search.vue Wed Dec 12 09:22:20 2018 +0100 @@ -0,0 +1,292 @@ +<template> + <div :class="searchbarContainerStyle"> + <div class="input-group-prepend m-0 d-print-none"> + <span @click="toggleSearchbar" :class="searchButtonStyle" for="search"> + <font-awesome-icon icon="search"></font-awesome-icon> + </span> + </div> + <div + :class="[ + 'searchgroup', + { + 'searchgroup-collapsed': !showSearchbar, + big: + showContextBox && + ['bottlenecks', 'staging'].indexOf(contextBoxContent) !== -1 + } + ]" + > + <input + @keyup.enter="takeFirstSearchresult" + id="search" + v-model="searchQuery" + type="text" + :class="searchInputStyle" + /> + </div> + <div + v-if="showSearchbar && searchResults !== null && !showContextBox" + class="searchresults border-top ui-element bg-white rounded-bottom d-print-none position-absolute" + > + <div + v-for="entry of searchResults" + :key="entry.name" + class="border-top text-left" + > + <a + href="#" + @click.prevent="moveToSearchResult(entry)" + class="p-2 d-block text-nowrap" + > + <font-awesome-icon + icon="ship" + v-if="entry.type === 'bottleneck'" + class="mr-1" + fixed-width + /> + <font-awesome-icon + icon="water" + v-if="entry.type === 'rhm'" + class="mr-1" + fixed-width + /> + <font-awesome-icon + icon="city" + v-if="entry.type === 'city'" + class="mr-1" + fixed-width + /> + {{ entry.name }} + </a> + </div> + </div> + </div> +</template> + +<style lang="scss" scoped> +.searchcontainer { + opacity: 0.96; +} + +.searchcontainer .searchbar { + border-top-left-radius: 0 !important; + border-bottom-left-radius: 0 !important; +} + +.searchgroup { + margin-left: -3px; + transition: width 0.3s; + width: 300px; + overflow: hidden; +} + +.searchgroup.big { + width: 571px; +} + +.searchgroup-collapsed { + width: 0; +} + +.searchbar { + height: 2rem !important; + box-shadow: none !important; +} + +.searchbar.rounded-top-right { + border-radius: 0 !important; + border-top-right-radius: 0.25rem !important; +} + +.searchlabel.rounded-top-left { + border-radius: 0 !important; + border-top-left-radius: 0.25rem !important; +} + +.input-group-text { + height: 2rem; + width: 2rem; +} + +.input-group-prepend svg path { + fill: #666; +} + +.searchresults { + box-shadow: 0 0.1rem 0.5rem rgba(0, 0, 0, 0.2); + top: 2rem; + left: 0; + right: 0; + max-height: 24rem; + overflow: auto; +} + +.searchresults > div:first-child { + border-top: 0 !important; +} + +.searchresults a { + text-decoration: none; +} + +.searchresults a:hover { + background: #f8f8f8; +} +</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", + "showContextBox", + "contextBoxContent" + ]), + 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 shadow-xs", + { + "d-flex": this.contextBoxContent !== "imports", + "d-none": this.contextBoxContent === "imports" && this.showContextBox + } + ]; + }, + searchInputStyle() { + return [ + "form-control ui-element search searchbar d-print-none border-0", + { "rounded-top-right": this.showContextBox || 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.showContextBox || 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: this.$gettext("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.showContextBox) { + if (!this.showSearchbar) { + setTimeout(setFocus, 300); + } + this.$store.commit("application/showSearchbar", !this.showSearchbar); + } + } + } +}; +</script>