comparison client/src/application/Search.vue @ 1196:a397b52981b8

html cleanup got rid of Topbar.vue to avoid unnecessary component nesting
author Markus Kottlaender <markus@intevation.de>
date Mon, 19 Nov 2018 08:24:47 +0100
parents
children 37889ae85133
comparison
equal deleted inserted replaced
1195:486d66a9565c 1196:a397b52981b8
1 <template>
2 <div :class="searchbarContainerStyle">
3 <div class="input-group-prepend shadow">
4 <span @click="toggleSearchbar" :class="searchButtonStyle" for="search">
5 <i class="fa fa-search d-print-none"></i>
6 </span>
7 </div>
8 <div class="searchgroup flex-fill">
9 <input
10 @keyup.enter="takeFirstSearchresult"
11 v-if="showSearchbar"
12 id="search"
13 v-model="searchQuery"
14 type="text"
15 class="form-control ui-element search searchbar d-print-none"
16 >
17 <ul v-if="showSearchbar && searchResults !== null " class="list-group d-print-none">
18 <li v-for="entry of searchResults" :key="entry.name" class="list-group-item">
19 <a href="#" @click.prevent="moveToSearchResult(entry)">{{entry.name}}</a>
20 </li>
21 </ul>
22 </div>
23 </div>
24 </template>
25
26 <style lang="sass" scoped>
27 .searchcontainer
28 height: $icon-height
29 border-radius: 0.25rem
30
31 .searchbar-expanded
32 margin-left: auto
33 margin-right: auto
34 padding-left: $offset
35 width: $searchbar-width !important
36 .searchbar
37 border-top-left-radius: 0 !important
38 border-bottom-left-radius: 0 !important
39
40
41 .searchbar-collapsed
42 margin-left: auto
43 width: $icon-width !important
44 transition: $transition-fast
45
46 .searchbar
47 margin-left: auto
48 margin-right: auto
49 height: $icon-height !important
50
51 .searchlabel
52 background-color: white !important
53
54 .input-group-text
55 height: $icon-height
56 width: $icon-width
57
58 .list-group
59 pointer-events: auto
60 </style>
61
62 <script>
63 /*
64 * This is Free Software under GNU Affero General Public License v >= 3.0
65 * without warranty, see README.md and license for details.
66 *
67 * SPDX-License-Identifier: AGPL-3.0-or-later
68 * License-Filename: LICENSES/AGPL-3.0.txt
69 *
70 * Copyright (C) 2018 by via donau
71 * – Österreichische Wasserstraßen-Gesellschaft mbH
72 * Software engineering by Intevation GmbH
73 *
74 * Author(s):
75 * Markus Kottländer <markus.kottlaender@intevation.de>
76 */
77 import debounce from "lodash.debounce";
78 import { mapState } from "vuex";
79
80 import { displayError } from "../application/lib/errors.js";
81 import { HTTP } from "../application/lib/http";
82
83 const setFocus = () => document.querySelector("#search").focus();
84
85 export default {
86 name: "search",
87 data() {
88 return {
89 searchQuery: "",
90 searchQueryIsDirty: false,
91 searchResults: null,
92 isSearching: false
93 };
94 },
95 computed: {
96 ...mapState("application", ["showSearchbar"]),
97 searchIndicator: function() {
98 if (this.isSearching) {
99 return "⟳";
100 } else if (this.searchQueryIsDirty) {
101 return "";
102 } else {
103 return "✓";
104 }
105 },
106 searchbarContainerStyle() {
107 return [
108 "d-flex input-group searchcontainer",
109 {
110 "searchbar-collapsed": !this.showSearchbar,
111 "searchbar-expanded": this.showSearchbar
112 }
113 ];
114 },
115 searchButtonStyle() {
116 return [
117 "ui-element input-group-text p-0 d-flex justify-content-center searchlabel d-print-none",
118 {
119 rounded: !this.showSearchbar,
120 "rounded-left": this.showSearchbar
121 }
122 ];
123 }
124 },
125 watch: {
126 searchQuery: function() {
127 this.searchQueryIsDirty = true;
128 this.triggerSearch();
129 }
130 },
131 methods: {
132 takeFirstSearchresult() {
133 if (!this.searchResults || this.searchResults.length != 1) return;
134 this.moveToSearchResult(this.searchResults[0]);
135 },
136 triggerSearch: debounce(function() {
137 this.doSearch();
138 }, 500),
139 doSearch() {
140 this.isCalculating = true;
141 this.searchResults = null;
142
143 if (this.searchQuery == "") {
144 return;
145 }
146
147 HTTP.post(
148 "/search",
149 { string: this.searchQuery },
150 {
151 headers: {
152 "X-Gemma-Auth": localStorage.getItem("token"),
153 "Content-type": "text/xml; charset=UTF-8"
154 }
155 }
156 )
157 .then(response => {
158 // console.log("got:", response.data);
159 this.searchResults = response.data;
160 })
161 .catch(error => {
162 const { status, data } = error.response;
163 displayError({
164 title: "Backend Error",
165 message: `${status}: ${data.message || data}`
166 });
167 });
168
169 this.isCalculating = false;
170 this.searchQueryIsDirty = false;
171 },
172 moveToSearchResult(resultEntry) {
173 // DEBUG console.log("Moving to", resultEntry);
174 if (resultEntry.geom.type == "Point") {
175 let zoom = 11;
176 if (resultEntry.type === "bottleneck") zoom = 17;
177 if (resultEntry.type === "rhm") zoom = 15;
178 if (resultEntry.type === "city") zoom = 13;
179
180 this.$store.commit("map/moveMap", {
181 coordinates: resultEntry.geom.coordinates,
182 zoom,
183 preventZoomOut: true
184 });
185 }
186 // this.searchQuery = ""; // clear search query again
187 this.toggleSearchbar();
188 },
189 toggleSearchbar() {
190 if (!this.showSearchbar) {
191 setTimeout(setFocus, 300);
192 }
193 this.$store.commit("application/showSearchbar", !this.showSearchbar);
194 }
195 }
196 };
197 </script>