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.vue173
1 files changed, 68 insertions, 105 deletions
diff --git a/viewer/src/views/MainLayout.vue b/viewer/src/views/MainLayout.vue
index 2347ba7..9c84116 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,131 +18,94 @@
18--> 18-->
19 19
20<template> 20<template>
21 <div :class="{ [$style.fullscreen]: $uiStore.fullscreen, [$style.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 <PanelTop v-if="isReady" :class="[$style.layout, $style.layoutTop]" />
24 <PanelLeft v-if="isReady" :class="[$style.layout, $style.layoutLeft]" />
25 <b-loading v-if="isLoading" active />
26 <SplashScreen 23 <SplashScreen
27 v-else-if="$uiStore.splashScreenEnabled" 24 v-else-if="uiStore.splashScreenEnabled"
28 :class="$style.layout" 25 @validation="validateSpashScreen"
29 @validation="$uiStore.validateSpashScreen()"
30 /> 26 />
31 <router-view v-else ref="content" :class="[$style.layout, $style.layoutContent]" class="scrollbar" tabindex="01" /> 27 <template v-else-if="galleryStore.config && galleryStore.galleryIndex">
32 <ld-key-press :keycode="27" @action="$uiStore.toggleFullscreen(false)" /> 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>
33 </div> 37 </div>
34</template> 38</template>
35 39
36<script lang="ts"> 40<script setup lang="ts">
37import { ScrollPosition } from "@/@types/scrollposition"; 41import LdLoading from '@/components/LdLoading.vue';
38import { Component, Ref, Vue, Watch } from "vue-property-decorator"; 42import { useLdKeepFocus } from '@/services/ui/ldKeepFocus';
39import { Route } from "vue-router"; 43import { useLdSaveScroll } from '@/services/ui/ldSaveScroll';
40import PanelLeft from "./PanelLeft.vue"; 44import { useGalleryStore } from '@/store/galleryStore';
41import PanelTop from "./PanelTop.vue"; 45import { useUiStore } from '@/store/uiStore';
42import SplashScreen from "./SplashScreen.vue"; 46import { VueInstance } from '@vueuse/core';
43 47import { createToast } from 'mosha-vue-toastify';
44@Component({ 48import { nextTick, ref } from 'vue';
45 components: { 49import LayoutLeft from './layout/left/LayoutLeft.vue';
46 PanelLeft, 50import LayoutTop from './layout/top/LayoutTop.vue';
47 PanelTop, 51import SplashScreen from './SplashScreen.vue';
48 SplashScreen, 52
49 }, 53const uiStore = useUiStore();
50}) 54const galleryStore = useGalleryStore();
51export default class MainLayout extends Vue { 55
52 @Ref() readonly content?: Vue; 56const content = ref<VueInstance>();
53 57const isLoading = ref(true);
54 isLoading: boolean = true; 58
55 scrollPositions: ScrollPosition = {}; 59useLdSaveScroll(content);
56 60const { moveFocus } = useLdKeepFocus(content);
57 get contentDiv(): HTMLDivElement | null { 61
58 return (this.content?.$el as HTMLDivElement) ?? null; 62history.replaceState({ ldgallery: 'ENTRYPOINT' }, '');
59 } 63fetchGalleryItems();
60 64
61 get isReady(): boolean { 65function fetchGalleryItems() {
62 return ( 66 isLoading.value = true;
63 !this.$uiStore.splashScreenEnabled && 67 galleryStore
64 !this.isLoading && 68 .fetchConfig()
65 this.$galleryStore.config !== null && 69 .then(uiStore.initFromConfig)
66 this.$galleryStore.currentPath !== null 70 .then(galleryStore.fetchGalleryItems)
67 ); 71 .then(moveFocus)
68 } 72 .finally(() => (isLoading.value = false))
69 73 .catch(displayError);
70 mounted() { 74}
71 history.replaceState({ ldgallery: "ENTRYPOINT" }, "");
72 this.fetchGalleryItems();
73 document.body.addEventListener("fullscreenchange", this.onFullscreenChange);
74 }
75
76 destroyed() {
77 document.body.removeEventListener("fullscreenchange", this.onFullscreenChange);
78 }
79
80 moveFocusToContentDiv() {
81 setTimeout(() => this.contentDiv?.focus());
82 }
83
84 @Watch("$route")
85 routeChanged(newRoute: Route, oldRoute: Route) {
86 if (!this.contentDiv) return;
87 this.scrollPositions[oldRoute.path] = this.contentDiv.scrollTop;
88 this.$nextTick(() => (this.contentDiv!.scrollTop = this.scrollPositions[newRoute.path]));
89 this.moveFocusToContentDiv();
90 }
91
92 fetchGalleryItems() {
93 this.isLoading = true;
94 this.$galleryStore
95 .fetchConfig()
96 .then(this.$uiStore.initFromConfig)
97 .then(this.$galleryStore.fetchGalleryItems)
98 .then(this.moveFocusToContentDiv)
99 .finally(() => (this.isLoading = false))
100 .catch(this.displayError);
101 }
102
103 displayError(reason: any) {
104 this.$buefy.snackbar.open({
105 message: `${reason}`,
106 actionText: this.$t("snack.retry"),
107 position: "is-top",
108 type: "is-danger",
109 indefinite: true,
110 onAction: this.fetchGalleryItems,
111 });
112 }
113
114 isFullscreenActive(): boolean {
115 return Boolean(document.fullscreenElement);
116 }
117 75
118 @Watch("$uiStore.fullscreen") 76function displayError(reason: unknown) {
119 applyFullscreen(fullscreen: boolean) { 77 createToast(String(reason), {
120 const isFullscreenActive = this.isFullscreenActive(); 78 type: 'danger',
121 if (fullscreen && !isFullscreenActive) document.body.requestFullscreen(); 79 position: 'top-center',
122 else if (isFullscreenActive) document.exitFullscreen(); 80 timeout: 10000,
123 } 81 showIcon: true,
82 onClose: () => !isLoading.value && fetchGalleryItems(),
83 });
84}
124 85
125 onFullscreenChange() { 86function validateSpashScreen() {
126 this.$uiStore.toggleFullscreen(this.isFullscreenActive()); 87 uiStore.validateSpashScreen();
127 } 88 nextTick(moveFocus);
128} 89}
129</script> 90</script>
130 91
131<style lang="scss" module> 92<style lang="scss" module>
132@import "~@/assets/scss/theme.scss"; 93@import "~@/assets/scss/theme";
133 94
95:root {
96 --layout-top: #{$layout-top};
97 --layout-left: #{$layout-left};
98}
134:global(body), 99:global(body),
135:global(html) { 100:global(html) {
101 font-family: $family-sans-serif;
136 height: 100%; 102 height: 100%;
137 overflow: hidden; 103 overflow: hidden;
138 touch-action: none; 104 touch-action: none;
139 background-color: $content-bgcolor; 105 background-color: $content-bgcolor;
140 --layout-top: #{$layout-top};
141 --layout-left: #{$layout-left};
142} 106}
143.layout { 107.layout {
144 position: fixed; 108 position: fixed;
145 transition: all $transition-flex-expand linear;
146 top: 0; 109 top: 0;
147 bottom: 0; 110 bottom: 0;
148 left: 0; 111 left: 0;