aboutsummaryrefslogtreecommitdiff
path: root/viewer/src/views/MainLayout.vue
diff options
context:
space:
mode:
Diffstat (limited to 'viewer/src/views/MainLayout.vue')
-rw-r--r--viewer/src/views/MainLayout.vue159
1 files changed, 78 insertions, 81 deletions
diff --git a/viewer/src/views/MainLayout.vue b/viewer/src/views/MainLayout.vue
index 6ef9a3b..d8b3300 100644
--- a/viewer/src/views/MainLayout.vue
+++ b/viewer/src/views/MainLayout.vue
@@ -1,7 +1,7 @@
1<!-- ldgallery - A static generator which turns a collection of tagged 1<!-- ldgallery - A static generator which turns a collection of tagged
2-- pictures into a searchable web gallery. 2-- pictures into a searchable web gallery.
3-- 3--
4-- Copyright (C) 2019-2020 Guillaume FOUET 4-- Copyright (C) 2019-2022 Guillaume FOUET
5-- 5--
6-- This program is free software: you can redistribute it and/or modify 6-- This program is free software: you can redistribute it and/or modify
7-- it under the terms of the GNU Affero General Public License as 7-- it under the terms of the GNU Affero General Public License as
@@ -18,118 +18,116 @@
18--> 18-->
19 19
20<template> 20<template>
21 <div :class="{ fullscreen: $uiStore.fullscreen, fullwidth: $uiStore.fullWidth }"> 21 <div :class="{ [$style.fullscreen]: uiStore.fullscreen, [$style.fullwidth]: uiStore.fullWidth }">
22 <ld-title :gallery-title="$galleryStore.galleryTitle" :current-item="$galleryStore.currentItem" /> 22 <LdLoading v-if="isLoading" />
23 <panel-top v-if="isReady" class="layout layout-top" /> 23 <SplashScreen
24 <panel-left v-if="isReady" class="layout layout-left" /> 24 v-else-if="uiStore.splashScreenEnabled"
25 <router-view v-if="!isLoading" ref="content" class="layout layout-content scrollbar" /> 25 @validation="validateSpashScreen"
26 <b-loading :active="isLoading" is-full-page /> 26 />
27 <ld-key-press :keycode="27" @action="$uiStore.toggleFullscreen(false)" /> 27 <template v-else-if="galleryStore.config && galleryStore.galleryIndex">
28 <LayoutTop :class="[$style.layout, $style.layoutTop]" />
29 <LayoutLeft :class="[$style.layout, $style.layoutLeft]" />
30 <router-view
31 ref="content"
32 :class="[$style.layout, $style.layoutContent]"
33 class="scrollbar"
34 tabindex="2"
35 />
36 </template>
28 </div> 37 </div>
29</template> 38</template>
30 39
31<script lang="ts"> 40<script setup lang="ts">
32import { Component, Vue, Ref, Watch } from "vue-property-decorator"; 41import LdLoading from '@/components/LdLoading.vue';
33import PanelLeft from "./PanelLeft.vue"; 42import { useLdKeepFocus } from '@/services/ui/ldKeepFocus';
34import PanelTop from "./PanelTop.vue"; 43import { useLdSaveScroll } from '@/services/ui/ldSaveScroll';
35import { Route } from "vue-router"; 44import { useGalleryStore } from '@/store/galleryStore';
45import { useUiStore } from '@/store/uiStore';
46import { VueInstance } from '@vueuse/core';
47import { createToast } from 'mosha-vue-toastify';
48import { nextTick, ref } from 'vue';
49import LayoutLeft from './layout/left/LayoutLeft.vue';
50import LayoutTop from './layout/top/LayoutTop.vue';
51import SplashScreen from './SplashScreen.vue';
36 52
37@Component({ 53const uiStore = useUiStore();
38 components: { PanelLeft, PanelTop }, 54const galleryStore = useGalleryStore();
39})
40export default class MainLayout extends Vue {
41 @Ref() readonly content!: Vue;
42 55
43 isLoading: boolean = true; 56const content = ref<VueInstance>();
44 scrollPositions: ScrollPosition = {}; 57const isLoading = ref(true);
45 58
46 mounted() { 59useLdSaveScroll(content);
47 history.replaceState({ ldgallery: "ENTRYPOINT" }, ""); 60const { moveFocus } = useLdKeepFocus(content);
48 this.fetchGalleryItems();
49 document.body.addEventListener("fullscreenchange", this.onFullscreenChange);
50 }
51
52 destroyed() {
53 document.body.removeEventListener("fullscreenchange", this.onFullscreenChange);
54 }
55
56 @Watch("$route")
57 routeChanged(newRoute: Route, oldRoute: Route) {
58 const el = this.content.$el;
59 this.scrollPositions[oldRoute.path] = el.scrollTop;
60 this.$nextTick(() => (el.scrollTop = this.scrollPositions[newRoute.path]));
61 }
62 61
63 fetchGalleryItems() { 62history.replaceState({ ldgallery: 'ENTRYPOINT' }, '');
64 this.isLoading = true; 63fetchGalleryItems();
65 this.$galleryStore
66 .fetchConfig()
67 .then(this.$uiStore.initFromConfig)
68 .then(this.$galleryStore.fetchGalleryItems)
69 .finally(() => (this.isLoading = false))
70 .catch(this.displayError);
71 }
72
73 get isReady() {
74 return !this.isLoading && this.$galleryStore.config && this.$galleryStore.currentPath !== null;
75 }
76 64
77 displayError(reason: any) { 65function fetchGalleryItems() {
78 this.$buefy.snackbar.open({ 66 isLoading.value = true;
79 message: `${reason}`, 67 galleryStore
80 actionText: "Retry", 68 .fetchConfig()
81 position: "is-top", 69 .then(uiStore.initFromConfig)
82 type: "is-danger", 70 .then(galleryStore.fetchGalleryItems)
83 indefinite: true, 71 .then(moveFocus)
84 onAction: this.fetchGalleryItems, 72 .finally(() => (isLoading.value = false))
85 }); 73 .catch(displayError);
86 } 74}
87 75
88 @Watch("$uiStore.fullscreen") 76function displayError(reason: unknown) {
89 applyFullscreen(fullscreen: boolean) { 77 createToast(String(reason), {
90 if (fullscreen && !document.fullscreen) document.body.requestFullscreen(); 78 type: 'danger',
91 else if (document.fullscreen) document.exitFullscreen(); 79 position: 'top-center',
92 } 80 timeout: 10000,
81 showIcon: true,
82 onClose: () => !isLoading.value && fetchGalleryItems(),
83 });
84}
93 85
94 onFullscreenChange() { 86function validateSpashScreen() {
95 this.$uiStore.toggleFullscreen(document.fullscreen); 87 uiStore.validateSpashScreen();
96 } 88 nextTick(moveFocus);
97} 89}
98</script> 90</script>
99 91
100<style lang="scss"> 92<style lang="scss" module>
101@import "~@/assets/scss/theme.scss"; 93@import "~@/assets/scss/theme";
102 94
103body, 95:root {
104html { 96 --layout-top: #{$layout-top};
97 --layout-left: #{$layout-left};
98}
99:global(body),
100:global(html) {
101 font-family: $family-sans-serif;
105 height: 100%; 102 height: 100%;
106 overflow: hidden; 103 overflow: hidden;
107 touch-action: none; 104 touch-action: none;
108 background-color: $content-bgcolor; 105 background-color: $content-bgcolor;
109 --layout-top: #{$layout-top}; 106 margin: 0;
110 --layout-left: #{$layout-left};
111} 107}
112.layout { 108.layout {
113 position: fixed; 109 position: fixed;
114 transition: all $transition-flex-expand linear;
115 top: 0; 110 top: 0;
116 bottom: 0; 111 bottom: 0;
117 left: 0; 112 left: 0;
118 right: 0; 113 right: 0;
119 &.layout-top { 114 &.layoutTop {
120 height: $layout-top; 115 height: $layout-top;
121 z-index: 1; 116 z-index: 1;
122 } 117 }
123 &.layout-left { 118 &.layoutLeft {
124 top: $layout-top; 119 top: $layout-top;
125 width: $layout-left; 120 width: $layout-left;
126 z-index: 2; 121 z-index: 2;
127 } 122 }
128 &.layout-content { 123 &.layoutContent {
129 top: var(--layout-top); 124 top: var(--layout-top);
130 left: var(--layout-left); 125 left: var(--layout-left);
131 z-index: 3; 126 z-index: 3;
132 overflow-x: hidden; 127 overflow-x: hidden;
128 &:focus {
129 outline: none;
130 }
133 } 131 }
134} 132}
135.fullscreen { 133.fullscreen {
@@ -146,17 +144,16 @@ html {
146} 144}
147 145
148.layout { 146.layout {
149 &.layout-top { 147 &.layoutTop {
150 background-color: $panel-top-bgcolor; 148 background-color: $panel-top-bgcolor;
151 color: $panel-top-txtcolor; 149 color: $panel-top-txtcolor;
152 } 150 }
153 &.layout-left { 151 &.layoutLeft {
154 background-color: $panel-left-bgcolor; 152 background-color: $panel-left-bgcolor;
155 color: $panel-left-txtcolor; 153 color: $panel-left-txtcolor;
156 } 154 }
157 &.layout-content { 155 &.layoutContent {
158 background-color: $content-bgcolor; 156 background-color: $content-bgcolor;
159 } 157