changeset 2566:83b938bf4da9

client: prepared store and minimized splitscreens for multiple simultaneous diagrams
author Markus Kottlaender <markus@intevation.de>
date Mon, 11 Mar 2019 11:34:54 +0100
parents 114979e97a6c
children 1cac2e58ef7d
files client/src/components/App.vue client/src/components/Splitscreen.vue client/src/components/Zoom.vue client/src/components/fairway/Fairwayprofile.vue client/src/components/fairway/Infobar.vue client/src/components/splitscreen/MinimizedSplitscreens.vue client/src/components/splitscreen/Splitscreen.vue client/src/components/ui/UIBoxHeader.vue client/src/main.js client/src/store/application.js client/src/store/bottlenecks.js client/src/store/fairway.js
diffstat 12 files changed, 261 insertions(+), 212 deletions(-) [+]
line wrap: on
line diff
--- a/client/src/components/App.vue	Mon Mar 11 11:14:44 2019 +0100
+++ b/client/src/components/App.vue	Mon Mar 11 11:34:54 2019 +0100
@@ -1,7 +1,7 @@
 <template>
   <div id="app" class="main">
     <div v-if="isAuthenticated" class="d-flex flex-column userinterface">
-      <div class="topbar d-flex pt-2 mx-2">
+      <div class="d-flex p-2">
         <div class="mr-auto d-flex">
           <Sidebar :routeName="routeName"></Sidebar>
           <div :class="searchContainer">
@@ -21,12 +21,9 @@
           <Toolbar v-if="isMapVisible"></Toolbar>
         </div>
       </div>
-      <div class="flex-fill"></div>
-      <div class="d-flex flex-row align-items-end">
-        <Infobar v-if="isMapVisible"></Infobar>
-      </div>
       <Zoom v-if="isMapVisible"></Zoom>
       <Splitscreen v-if="isMapVisible" />
+      <MinimizedSplitscreens v-if="isMapVisible" />
     </div>
     <router-view />
     <vue-snotify></vue-snotify>
@@ -45,11 +42,6 @@
   pointer-events: none;
 }
 
-.topbar {
-  position: relative;
-  z-index: 2;
-}
-
 #app {
   height: 100%;
   width: 100%;
@@ -103,7 +95,6 @@
   },
   components: {
     Profiles: () => import("./fairway/Profiles"),
-    Infobar: () => import("./fairway/Infobar"),
     Pdftool: () => import("./Pdftool"),
     Zoom: () => import("./Zoom"),
     Identify: () => import("./identify/Identify"),
@@ -113,7 +104,8 @@
     Contextbox: () => import("./Contextbox"),
     Toolbar: () => import("./toolbar/Toolbar"),
     Popup: () => import("./Popup"),
-    Splitscreen: () => import("./Splitscreen")
+    Splitscreen: () => import("./splitscreen/Splitscreen"),
+    MinimizedSplitscreens: () => import("./splitscreen/MinimizedSplitscreens")
   }
 };
 </script>
--- a/client/src/components/Splitscreen.vue	Mon Mar 11 11:14:44 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,94 +0,0 @@
-<template>
-  <div
-    :class="[
-      'splitscreen bg-white d-flex flex-column ui-element',
-      { show: showSplitscreen }
-    ]"
-  >
-    <UIBoxHeader
-      :icon="config.icon"
-      :title="config.title"
-      :closeCallback="close"
-      :collapseCallback="collapse"
-    />
-    <div class="d-flex flex-fill">
-      <transition name="fade">
-        <div
-          class="loading d-flex justify-content-center align-items-center"
-          v-if="splitscreenLoading"
-        >
-          <font-awesome-icon icon="spinner" spin />
-        </div>
-      </transition>
-      <component :is="config.component" />
-    </div>
-  </div>
-</template>
-
-<style lang="sass" scoped>
-.splitscreen
-  position: absolute
-  bottom: -50vh
-  left: 0
-  right: 0
-  height: 50vh
-  overflow: hidden
-  z-index: 1
-  box-shadow: 0 -.125rem .25rem rgba(0, 0, 0, 0.075)
-  transition: bottom 0.3s
-  &.show
-    bottom: 0
-
-  .loading
-    background: rgba(255, 255, 255, 0.96)
-    position: absolute
-    z-index: 99
-    top: 34px
-    right: 0
-    bottom: 0
-    left: 0
-</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 { mapState } from "vuex";
-
-export default {
-  components: {
-    Fairwayprofile: () => import("@/components/fairway/Fairwayprofile")
-  },
-  computed: {
-    ...mapState("application", [
-      "splitscreen",
-      "showSplitscreen",
-      "splitscreenLoading"
-    ]),
-    config() {
-      return this.splitscreen || { title: "", icon: "", component: "" };
-    }
-  },
-  methods: {
-    collapse() {
-      this.$store.commit("application/showSplitscreen", false);
-      if (this.config.collapseCallback) this.config.collapseCallback();
-    },
-    close() {
-      this.$store.commit("application/showSplitscreen", false);
-      if (this.config.closeCallback) this.config.closeCallback();
-    }
-  }
-};
-</script>
--- a/client/src/components/Zoom.vue	Mon Mar 11 11:14:44 2019 +0100
+++ b/client/src/components/Zoom.vue	Mon Mar 11 11:34:54 2019 +0100
@@ -1,22 +1,19 @@
 <template>
-  <div
-    class="d-flex buttoncontainer shadow-xs mb-2 position-absolute"
-    :style="showSplitscreen ? 'margin-bottom: 51vh !important' : ''"
-  >
+  <div :class="['zoom-buttons shadow-xs', { splitscreen: showSplitscreen }]">
     <button
-      class="zoomButton border-0 bg-white rounded-left ui-element"
+      class="zoom-button border-0 bg-white rounded-left ui-element"
       @click="zoomOut"
     >
       <font-awesome-icon icon="minus"></font-awesome-icon>
     </button>
     <button
-      class="zoomButton border-0 bg-white ui-element border-right"
+      class="zoom-button border-0 bg-white ui-element border-right"
       @click="refreshMap"
     >
       <font-awesome-icon icon="redo"></font-awesome-icon>
     </button>
     <button
-      class="zoomButton border-0 bg-white rounded-right ui-element border-right"
+      class="zoom-button border-0 bg-white rounded-right ui-element border-right"
       @click="zoomIn"
     >
       <font-awesome-icon icon="plus"></font-awesome-icon>
@@ -24,22 +21,25 @@
   </div>
 </template>
 
-<style lang="scss" scoped>
-.buttoncontainer {
-  bottom: 0;
-  left: 50%;
-  margin-left: -$icon-width;
-  transition: margin-bottom 0.3s;
-}
+<style lang="sass" scoped>
+.zoom-buttons
+  position: absolute
+  bottom: $small-offset
+  left: 50%
+  margin-left: -($icon-width * 1.5)
+  margin-bottom: 0
+  transition: margin-bottom 0.3s
+  &.splitscreen
+    margin-bottom: 50vh
 
-.zoomButton {
-  min-height: $icon-width;
-  min-width: $icon-width;
-  z-index: 1;
-  outline: none;
-  color: #666;
-}
+  .zoom-button
+    min-height: $icon-width
+    min-width: $icon-width
+    z-index: 1
+    outline: none
+    color: #666
 </style>
+
 <script>
 /* This is Free Software under GNU Affero General Public License v >= 3.0
  * without warranty, see README.md and license for details.
--- a/client/src/components/fairway/Fairwayprofile.vue	Mon Mar 11 11:14:44 2019 +0100
+++ b/client/src/components/fairway/Fairwayprofile.vue	Mon Mar 11 11:34:54 2019 +0100
@@ -46,13 +46,12 @@
     ...mapState("fairwayprofile", [
       "additionalSurvey",
       "currentProfile",
+      "startPoint",
       "endPoint",
       "fairwayData",
-      "minAlt",
       "maxAlt",
       "referenceWaterLevel",
       "selectedWaterLevel",
-      "startPoint",
       "waterLevels"
     ]),
     ...mapState("bottlenecks", ["selectedSurvey"]),
--- a/client/src/components/fairway/Infobar.vue	Mon Mar 11 11:14:44 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-<template>
-  <div
-    v-if="Object.keys(currentProfile).length"
-    class="ui-element shadow-xs infobar rounded bg-white ml-auto mb-2 mr-2"
-  >
-    <div class="d-flex flex-row justify-content-between h-100">
-      <h6 class="my-auto px-2">
-        {{ selectedBottleneck }} ({{ selectedSurvey.date_info }})
-      </h6>
-      <span
-        class="p-2 border-left d-flex align-items-center"
-        @click="$store.commit('application/showSplitscreen', true)"
-      >
-        <font-awesome-icon class="pointer" icon="angle-up"></font-awesome-icon>
-      </span>
-      <span
-        class="p-2 border-left d-flex align-items-center"
-        @click="$store.dispatch('fairwayprofile/clearSelection')"
-      >
-        <font-awesome-icon icon="times" class="pointer"></font-awesome-icon>
-      </span>
-    </div>
-  </div>
-</template>
-
-<style lang="scss" scoped>
-.infobar {
-  height: 2.2rem;
-}
-
-.infobar svg path {
-  fill: #666;
-}
-</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 { mapState } from "vuex";
-
-export default {
-  name: "infobar",
-  computed: {
-    ...mapState("application", ["showSplitscreen"]),
-    ...mapState("fairwayprofile", ["currentProfile"]),
-    ...mapState("bottlenecks", ["selectedBottleneck", "selectedSurvey"])
-  }
-};
-</script>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/components/splitscreen/MinimizedSplitscreens.vue	Mon Mar 11 11:34:54 2019 +0100
@@ -0,0 +1,61 @@
+<template>
+  <div class="minimizedSplitscreens ui-element">
+    <transition name="fade">
+      <UIBoxHeader
+        v-for="splitscreen in splitscreens"
+        :key="splitscreen.id"
+        :icon="splitscreen.icon"
+        :title="splitscreen.title"
+        :closeCallback="close(splitscreen)"
+        :expandCallback="expand(splitscreen)"
+        :collapsed="true"
+      />
+    </transition>
+  </div>
+</template>
+
+<style lang="sass" scoped>
+.minimizedSplitscreens
+  position: absolute
+  bottom: $small-offset
+  right: $small-offset
+</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@intevation.de>
+ */
+
+import { mapState } from "vuex";
+
+export default {
+  computed: {
+    ...mapState("application", ["splitscreens"])
+  },
+  methods: {
+    close(splitscreen) {
+      return () => {
+        if (splitscreen.closeCallback) splitscreen.closeCallback();
+        this.$store.commit("application/removeSplitscreen", splitscreen.id);
+      };
+    },
+    expand(splitscreen) {
+      return () => {
+        if (splitscreen.expandCallback) splitscreen.expandCallback();
+        this.$store.commit("application/activeSplitscreen", splitscreen.id);
+        this.$store.commit("application/showSplitscreen", true);
+      };
+    }
+  }
+};
+</script>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/components/splitscreen/Splitscreen.vue	Mon Mar 11 11:34:54 2019 +0100
@@ -0,0 +1,100 @@
+<template>
+  <div>
+    <div
+      :class="[
+        'splitscreen bg-white d-flex flex-column ui-element',
+        { show: showSplitscreen }
+      ]"
+    >
+      <UIBoxHeader
+        :icon="activeSplitscreen.icon"
+        :title="activeSplitscreen.title"
+        :closeCallback="close"
+        :collapseCallback="collapse"
+        v-if="activeSplitscreen"
+      />
+      <div class="d-flex flex-fill">
+        <transition name="fade">
+          <div
+            class="loading d-flex justify-content-center align-items-center"
+            v-if="splitscreenLoading"
+          >
+            <font-awesome-icon icon="spinner" spin />
+          </div>
+        </transition>
+        <component :is="activeSplitscreen.component" v-if="activeSplitscreen" />
+      </div>
+    </div>
+  </div>
+</template>
+
+<style lang="sass" scoped>
+.splitscreen
+  position: absolute
+  bottom: -50vh
+  left: 0
+  right: 0
+  height: 50vh
+  overflow: hidden
+  z-index: 1
+  box-shadow: 0 -.125rem .25rem rgba(0, 0, 0, 0.075)
+  transition: bottom 0.3s
+  &.show
+    bottom: 0
+
+  .loading
+    background: rgba(255, 255, 255, 0.96)
+    position: absolute
+    z-index: 99
+    top: 34px
+    right: 0
+    bottom: 0
+    left: 0
+</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 { mapState, mapGetters } from "vuex";
+
+export default {
+  components: {
+    Fairwayprofile: () => import("@/components/fairway/Fairwayprofile")
+  },
+  computed: {
+    ...mapState("application", ["showSplitscreen", "splitscreenLoading"]),
+    ...mapGetters("application", ["activeSplitscreen"])
+  },
+  methods: {
+    collapse() {
+      if (this.activeSplitscreen.collapseCallback)
+        this.activeSplitscreen.collapseCallback();
+      this.$store.commit("application/showSplitscreen", false);
+    },
+    close() {
+      if (this.activeSplitscreen.closeCallback)
+        this.activeSplitscreen.closeCallback();
+      this.$store.commit("application/showSplitscreen", false);
+      setTimeout(() => {
+        this.$store.commit(
+          "application/removeSplitscreen",
+          this.activeSplitscreen.id
+        );
+        this.$store.commit("application/activeSplitscreen", null);
+      }, 350);
+    }
+  }
+};
+</script>
--- a/client/src/components/ui/UIBoxHeader.vue	Mon Mar 11 11:14:44 2019 +0100
+++ b/client/src/components/ui/UIBoxHeader.vue	Mon Mar 11 11:34:54 2019 +0100
@@ -1,5 +1,5 @@
 <template>
-  <h6 class="box-header">
+  <h6 :class="['box-header', { 'rounded border-0 shadow-xs': collapsed }]">
     <span class="box-title">
       <font-awesome-icon
         :icon="icon"
@@ -10,8 +10,11 @@
       {{ $gettext(title) }}
     </span>
     <div class="box-controls">
-      <span @click="collapseCallback" v-if="collapseCallback">
-        <font-awesome-icon icon="angle-down" />
+      <span @click="collapseCallback" v-if="!collapsed && collapseCallback">
+        <font-awesome-icon :icon="['far', 'window-minimize']" />
+      </span>
+      <span @click="expandCallback" v-if="collapsed && expandCallback">
+        <font-awesome-icon :icon="['far', 'window-maximize']" />
       </span>
       <span @click="closeCallback" v-if="closeCallback">
         <font-awesome-icon icon="times" />
@@ -32,6 +35,7 @@
   margin-bottom: 0
   padding: 0.25rem
   font-weight: bold
+  background: white
   .box-title
     padding-left: 0.25rem
     .box-icon
@@ -66,6 +70,13 @@
  */
 
 export default {
-  props: ["icon", "title", "collapseCallback", "closeCallback"]
+  props: [
+    "icon",
+    "title",
+    "collapseCallback",
+    "closeCallback",
+    "expandCallback",
+    "collapsed"
+  ]
 };
 </script>
--- a/client/src/main.js	Mon Mar 11 11:14:44 2019 +0100
+++ b/client/src/main.js	Mon Mar 11 11:34:54 2019 +0100
@@ -95,6 +95,10 @@
   faWrench,
   faRedo
 } from "@fortawesome/free-solid-svg-icons";
+import {
+  faWindowMinimize,
+  faWindowMaximize
+} from "@fortawesome/free-regular-svg-icons";
 import { faAdn } from "@fortawesome/free-brands-svg-icons";
 
 library.add(
@@ -149,7 +153,9 @@
   faUsersCog,
   faWater,
   faWrench,
-  faRedo
+  faRedo,
+  faWindowMinimize,
+  faWindowMaximize
 );
 
 // register plugins
--- a/client/src/store/application.js	Mon Mar 11 11:14:44 2019 +0100
+++ b/client/src/store/application.js	Mon Mar 11 11:34:54 2019 +0100
@@ -23,11 +23,11 @@
     secondaryLogo: process.env.VUE_APP_SECONDARY_LOGO_URL,
     logoForPDF: process.env.VUE_APP_LOGO_FOR_PDF_URL,
     popup: null,
+    splitscreens: [],
+    splitscreenLoading: false,
+    showSplitscreen: false,
     showSidebar: false,
     showUsermenu: false,
-    showSplitscreen: false,
-    splitscreenLoading: false,
-    splitscreen: null,
     showSearchbar: false,
     showSearchbarLastState: false,
     showIdentify: false,
@@ -65,6 +65,9 @@
         versionStr += "+" + process.env.VUE_APP_HGREV;
 
       return versionStr;
+    },
+    activeSplitscreen: state => {
+      return state.splitscreens.find(s => s.id === state.activeSplitscreen);
     }
   },
   mutations: {
@@ -80,8 +83,17 @@
     splitscreenLoading: (state, loading) => {
       state.splitscreenLoading = loading;
     },
-    splitscreen: (state, config) => {
-      state.splitscreen = config;
+    activeSplitscreen: (state, id) => {
+      state.activeSplitscreen = id;
+    },
+    addSplitscreen: (state, config) => {
+      let index = state.splitscreens.findIndex(s => s.id === config.id);
+      if (index !== -1) state.splitscreens[index] = config;
+      else state.splitscreens.push(config);
+    },
+    removeSplitscreen: (state, id) => {
+      let index = state.splitscreens.findIndex(s => s.id === id);
+      if (index !== -1) state.splitscreens.splice(index, 1);
     },
     showUsermenu: (state, show) => {
       state.showUsermenu = show;
--- a/client/src/store/bottlenecks.js	Mon Mar 11 11:14:44 2019 +0100
+++ b/client/src/store/bottlenecks.js	Mon Mar 11 11:34:54 2019 +0100
@@ -67,6 +67,9 @@
           commit("selectedSurvey", null);
           commit("application/splitscreenLoading", true, { root: true });
           commit("application/showSplitscreen", false, { root: true });
+          commit("application/removeSplitscreen", "fairwayprofile", {
+            root: true
+          });
           setTimeout(() => {
             commit("fairwayprofile/clearCurrentProfile", null, { root: true });
           }, 350);
--- a/client/src/store/fairway.js	Mon Mar 11 11:14:44 2019 +0100
+++ b/client/src/store/fairway.js	Mon Mar 11 11:34:54 2019 +0100
@@ -289,20 +289,39 @@
               });
             })
             .finally(() => {
-              commit(
-                "application/splitscreen",
-                {
-                  component: "fairwayprofile",
-                  title: `${rootState.bottlenecks.selectedBottleneck} (${
-                    rootState.bottlenecks.selectedSurvey.date_info
-                  })`,
-                  icon: "chart-area",
-                  closeCallback: () => {
-                    dispatch("clearSelection");
-                  }
+              let splitscreenConf = {
+                id: "fairwayprofile",
+                component: "fairwayprofile",
+                title: `${rootState.bottlenecks.selectedBottleneck} (${
+                  rootState.bottlenecks.selectedSurvey.date_info
+                })`,
+                icon: "chart-area",
+                closeCallback: () => {
+                  dispatch("clearSelection");
                 },
-                { root: true }
-              );
+                expandCallback: () => {
+                  let bottleneck = rootState.bottlenecks.bottlenecksList.find(
+                    bn =>
+                      bn.properties.name ===
+                      rootState.bottlenecks.selectedBottleneck
+                  );
+                  commit(
+                    "map/moveToExtent",
+                    {
+                      feature: bottleneck,
+                      zoom: 17,
+                      preventZoomOut: true
+                    },
+                    { root: true }
+                  );
+                }
+              };
+              commit("application/addSplitscreen", splitscreenConf, {
+                root: true
+              });
+              commit("application/activeSplitscreen", "fairwayprofile", {
+                root: true
+              });
               commit("application/splitscreenLoading", false, { root: true });
               commit("profileLoading", false);
             });