view client/src/application/Search.vue @ 1207:70116d392387

close bottleneck list: made searchbar collapse only if it was collapsed before opening the bottleneck list will expand the searchbar, closing the bottleneck list was always collapsing the searchbar too. Now it stays open if it was open beforeopening the bottleneck list
author Markus Kottlaender <markus@intevation.de>
date Mon, 19 Nov 2018 13:02:48 +0100
parents 0900b341cef0
children 9d93968db2cd
line wrap: on
line source

<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"
            >
            <ul v-if="showSearchbar && searchResults !== null && !showBottlenecks" class="list-group d-print-none">
                <li v-for="entry of searchResults" :key="entry.name" class="list-group-item">
                    <a href="#" @click.prevent="moveToSearchResult(entry)">{{entry.name}}</a>
                </li>
            </ul>
        </div>
    </div>
</template>

<style lang="sass" scoped>
  .searchcontainer
    margin-left: $offset
    height: $icon-height

  .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: 0.25rem !important

  .searchlabel
    background-color: white !important
    &.rounded-top-left
      border-radius: 0 !important
      border-top-left-radius: 0.25rem !important

  .input-group-text
    height: $icon-height
    width: $icon-width

  .list-group
    pointer-events: auto
    max-height: 40vh
    overflow: auto
</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 "../application/lib/errors.js";
import { HTTP } from "../application/lib/http";

const setFocus = () => document.querySelector("#search").focus();

export default {
  name: "search",
  data() {
    return {
      searchQueryIsDirty: false,
      searchResults: null,
      isSearching: false
    };
  },
  computed: {
    ...mapState("application", [
      "showSearchbar",
      "showBottlenecks",
      "showImportSoundingResults"
    ]),
    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",
        {
          "searchbar-collapsed": !this.showSearchbar,
          "searchbar-expanded": this.showSearchbar,
          "d-flex": !this.showImportSoundingResults,
          "d-none": this.showImportSoundingResults
        }
      ];
    },
    searchInputStyle() {
      return [
        "form-control ui-element search searchbar d-print-none border-0",
        { "rounded-top-right": this.showSearchbar && this.showBottlenecks }
      ];
    },
    searchButtonStyle() {
      return [
        "ui-element input-group-text p-0 d-flex border-0 justify-content-center searchlabel d-print-none",
        {
          rounded: !this.showSearchbar,
          "rounded-left": this.showSearchbar,
          "rounded-top-left": this.showSearchbar && this.showBottlenecks
        }
      ];
    }
  },
  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.showBottlenecks) {
        if (!this.showSearchbar) {
          setTimeout(setFocus, 300);
        }
        this.$store.commit("application/showSearchbar", !this.showSearchbar);
      }
    }
  }
};
</script>