view client/src/application/Topbar.vue @ 1119:6d4cc4389c8f store-refactoring

merged default into store-refactoring
author Markus Kottlaender <markus@intevation.de>
date Tue, 06 Nov 2018 09:12:05 +0100
parents 595654ad3f66 ef7c102497b8
children d9e6a1f6f394
line wrap: on
line source

<template>
    <div class="topbar d-flex flex-row">
        <div @click="toggleSidebar">
            <i class="ui-element menubutton d-print-none fa fa-bars"></i>
        </div>
        <div v-if="routeName == 'mainview'" :class="searchbarContainerStyle">
            <div class="input-group-prepend shadow">
                <span @click="toggleSearchbar" class="ui-element input-group-text searchlabel d-print-none" for="search">
                    <i class="fa fa-search d-print-none"></i>
                </span>
            </div>
            <div class="searchgroup">
                <input @keyup.enter="takeFirstSearchresult" v-if="!searchbarCollapsed" id="search" v-model="searchQuery" type="text" class="form-control ui-element search searchbar d-print-none">
                <ul v-if="!searchbarCollapsed && searchResults !== null " 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>
        <div v-if="routeName == 'mainview' && Object.keys(currentProfile).length" class="splitbutton">
            <i @click="splitScreen" class="ui-element splitscreen fa fa-window-restore shadow"></i>
        </div>
        <div class="">
            <Layers v-if="routeName == 'mainview'"></Layers>
        </div>
        <div class="">
            <Identify v-if="routeName == 'mainview'"></Identify>
        </div>
    </div>
</template>

<style lang="scss">
.searchgroup {
  width: 90%;
}
.splitbutton {
  height: $icon-height;
}
.list-group {
  pointer-events: auto;
}
.splitscreen {
  background-color: white;
  padding: $small-offset;
  margin-right: $small-offset;
  margin-left: $offset;
  border-radius: $border-radius;
  height: $icon-height;
  width: $icon-width;
}

.menubutton {
  background-color: white;
  padding: $small-offset;
  border-radius: $border-radius;
  margin-left: $offset;
  height: $icon-height;
  width: $icon-width;
}

.searchcontainer {
  height: $icon-height;
  border-radius: 0.25rem;
}

.searchbar-expanded {
  margin-left: 22vw;
  margin-right: auto;
  width: $searchbar-width !important;
}

.searchbar-collapsed {
  margin-left: auto;
  margin-right: $small-offset;
  width: $icon-width !important;
  transition: $transition-fast;
}

.searchbar {
  margin-left: auto;
  margin-right: auto;
  height: $icon-height !important;
}

.searchlabel {
  background-color: white !important;
}

.topbar {
  padding-top: $offset;
  margin-right: $offset;
}

.logout {
  font-size: x-large;
}
</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):
 * Thomas Junk <thomas.junk@intevation.de>
 */
import debounce from "lodash.debounce";
import { fromLonLat } from "ol/proj";
import { mapState } from "vuex";

import { displayError } from "../application/lib/errors.js";
import { HTTP } from "../application/lib/http";
import Identify from "../identify/Identify";
import Layers from "../layers/Layers";

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

export default {
  name: "topbar",
  components: {
    Identify: Identify,
    Layers: Layers
  },
  data() {
    return {
      searchbarCollapsed: true,
      searchQuery: "",
      searchQueryIsDirty: false,
      searchResults: null,
      isSearching: false
    };
  },
  computed: {
    ...mapState("map", ["openLayersMap"]),
    ...mapState("fairwayprofile", ["currentProfile"]),
    searchIndicator: function() {
      if (this.isSearching) {
        return "⟳";
      } else if (this.searchQueryIsDirty) {
        return "";
      } else {
        return "✓";
      }
    },
    searchbarContainerStyle() {
      return {
        "input-group": true,
        searchcontainer: true,
        "searchbar-collapsed": this.searchbarCollapsed,
        "searchbar-expanded": !this.searchbarCollapsed
      };
    }
  },
  props: ["routeName"],
  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 view = this.openLayersMap.getView();
        const currentZoom = view.getZoom();
        let newZoom = 11;
        if (resultEntry.type === "bottleneck")
          newZoom = Math.max(17, currentZoom);
        if (resultEntry.type === "rhm")
          newZoom = Math.max(15, currentZoom);
        if (resultEntry.type === "city")
          newZoom = Math.max(13, currentZoom);
        view.animate(
          {
            zoom: newZoom,
            center: fromLonLat(
              resultEntry.geom.coordinates,
              view.getProjection()
            )
          },
          700
        );
      }
      // this.searchQuery = ""; // clear search query again
      this.toggleSearchbar();
    },
    toggleSearchbar() {
      if (this.searchbarCollapsed) {
        setTimeout(setFocus, 300);
      }
      this.searchbarCollapsed = !this.searchbarCollapsed;
    },
    toggleSidebar() {
      this.$store.commit("application/toggleSidebar");
    },
    splitScreen() {
      if (Object.keys(this.currentProfile).length == 0) return;
      this.$store.commit("application/toggleSplitScreen");
    }
  }
};
</script>