Mercurial > gemma
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> |