diff options
Diffstat (limited to 'viewer/src/views/MainLayout.vue')
-rw-r--r-- | viewer/src/views/MainLayout.vue | 93 |
1 files changed, 63 insertions, 30 deletions
diff --git a/viewer/src/views/MainLayout.vue b/viewer/src/views/MainLayout.vue index 6ef9a3b..2347ba7 100644 --- a/viewer/src/views/MainLayout.vue +++ b/viewer/src/views/MainLayout.vue | |||
@@ -18,31 +18,55 @@ | |||
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 | <ld-title :gallery-title="$galleryStore.galleryTitle" :current-item="$galleryStore.currentItem" /> |
23 | <panel-top v-if="isReady" class="layout layout-top" /> | 23 | <PanelTop v-if="isReady" :class="[$style.layout, $style.layoutTop]" /> |
24 | <panel-left v-if="isReady" class="layout layout-left" /> | 24 | <PanelLeft v-if="isReady" :class="[$style.layout, $style.layoutLeft]" /> |
25 | <router-view v-if="!isLoading" ref="content" class="layout layout-content scrollbar" /> | 25 | <b-loading v-if="isLoading" active /> |
26 | <b-loading :active="isLoading" is-full-page /> | 26 | <SplashScreen |
27 | v-else-if="$uiStore.splashScreenEnabled" | ||
28 | :class="$style.layout" | ||
29 | @validation="$uiStore.validateSpashScreen()" | ||
30 | /> | ||
31 | <router-view v-else ref="content" :class="[$style.layout, $style.layoutContent]" class="scrollbar" tabindex="01" /> | ||
27 | <ld-key-press :keycode="27" @action="$uiStore.toggleFullscreen(false)" /> | 32 | <ld-key-press :keycode="27" @action="$uiStore.toggleFullscreen(false)" /> |
28 | </div> | 33 | </div> |
29 | </template> | 34 | </template> |
30 | 35 | ||
31 | <script lang="ts"> | 36 | <script lang="ts"> |
32 | import { Component, Vue, Ref, Watch } from "vue-property-decorator"; | 37 | import { ScrollPosition } from "@/@types/scrollposition"; |
38 | import { Component, Ref, Vue, Watch } from "vue-property-decorator"; | ||
39 | import { Route } from "vue-router"; | ||
33 | import PanelLeft from "./PanelLeft.vue"; | 40 | import PanelLeft from "./PanelLeft.vue"; |
34 | import PanelTop from "./PanelTop.vue"; | 41 | import PanelTop from "./PanelTop.vue"; |
35 | import { Route } from "vue-router"; | 42 | import SplashScreen from "./SplashScreen.vue"; |
36 | 43 | ||
37 | @Component({ | 44 | @Component({ |
38 | components: { PanelLeft, PanelTop }, | 45 | components: { |
46 | PanelLeft, | ||
47 | PanelTop, | ||
48 | SplashScreen, | ||
49 | }, | ||
39 | }) | 50 | }) |
40 | export default class MainLayout extends Vue { | 51 | export default class MainLayout extends Vue { |
41 | @Ref() readonly content!: Vue; | 52 | @Ref() readonly content?: Vue; |
42 | 53 | ||
43 | isLoading: boolean = true; | 54 | isLoading: boolean = true; |
44 | scrollPositions: ScrollPosition = {}; | 55 | scrollPositions: ScrollPosition = {}; |
45 | 56 | ||
57 | get contentDiv(): HTMLDivElement | null { | ||
58 | return (this.content?.$el as HTMLDivElement) ?? null; | ||
59 | } | ||
60 | |||
61 | get isReady(): boolean { | ||
62 | return ( | ||
63 | !this.$uiStore.splashScreenEnabled && | ||
64 | !this.isLoading && | ||
65 | this.$galleryStore.config !== null && | ||
66 | this.$galleryStore.currentPath !== null | ||
67 | ); | ||
68 | } | ||
69 | |||
46 | mounted() { | 70 | mounted() { |
47 | history.replaceState({ ldgallery: "ENTRYPOINT" }, ""); | 71 | history.replaceState({ ldgallery: "ENTRYPOINT" }, ""); |
48 | this.fetchGalleryItems(); | 72 | this.fetchGalleryItems(); |
@@ -53,11 +77,16 @@ export default class MainLayout extends Vue { | |||
53 | document.body.removeEventListener("fullscreenchange", this.onFullscreenChange); | 77 | document.body.removeEventListener("fullscreenchange", this.onFullscreenChange); |
54 | } | 78 | } |
55 | 79 | ||
80 | moveFocusToContentDiv() { | ||
81 | setTimeout(() => this.contentDiv?.focus()); | ||
82 | } | ||
83 | |||
56 | @Watch("$route") | 84 | @Watch("$route") |
57 | routeChanged(newRoute: Route, oldRoute: Route) { | 85 | routeChanged(newRoute: Route, oldRoute: Route) { |
58 | const el = this.content.$el; | 86 | if (!this.contentDiv) return; |
59 | this.scrollPositions[oldRoute.path] = el.scrollTop; | 87 | this.scrollPositions[oldRoute.path] = this.contentDiv.scrollTop; |
60 | this.$nextTick(() => (el.scrollTop = this.scrollPositions[newRoute.path])); | 88 | this.$nextTick(() => (this.contentDiv!.scrollTop = this.scrollPositions[newRoute.path])); |
89 | this.moveFocusToContentDiv(); | ||
61 | } | 90 | } |
62 | 91 | ||
63 | fetchGalleryItems() { | 92 | fetchGalleryItems() { |
@@ -66,18 +95,15 @@ export default class MainLayout extends Vue { | |||
66 | .fetchConfig() | 95 | .fetchConfig() |
67 | .then(this.$uiStore.initFromConfig) | 96 | .then(this.$uiStore.initFromConfig) |
68 | .then(this.$galleryStore.fetchGalleryItems) | 97 | .then(this.$galleryStore.fetchGalleryItems) |
98 | .then(this.moveFocusToContentDiv) | ||
69 | .finally(() => (this.isLoading = false)) | 99 | .finally(() => (this.isLoading = false)) |
70 | .catch(this.displayError); | 100 | .catch(this.displayError); |
71 | } | 101 | } |
72 | 102 | ||
73 | get isReady() { | ||
74 | return !this.isLoading && this.$galleryStore.config && this.$galleryStore.currentPath !== null; | ||
75 | } | ||
76 | |||
77 | displayError(reason: any) { | 103 | displayError(reason: any) { |
78 | this.$buefy.snackbar.open({ | 104 | this.$buefy.snackbar.open({ |
79 | message: `${reason}`, | 105 | message: `${reason}`, |
80 | actionText: "Retry", | 106 | actionText: this.$t("snack.retry"), |
81 | position: "is-top", | 107 | position: "is-top", |
82 | type: "is-danger", | 108 | type: "is-danger", |
83 | indefinite: true, | 109 | indefinite: true, |
@@ -85,23 +111,28 @@ export default class MainLayout extends Vue { | |||
85 | }); | 111 | }); |
86 | } | 112 | } |
87 | 113 | ||
114 | isFullscreenActive(): boolean { | ||
115 | return Boolean(document.fullscreenElement); | ||
116 | } | ||
117 | |||
88 | @Watch("$uiStore.fullscreen") | 118 | @Watch("$uiStore.fullscreen") |
89 | applyFullscreen(fullscreen: boolean) { | 119 | applyFullscreen(fullscreen: boolean) { |
90 | if (fullscreen && !document.fullscreen) document.body.requestFullscreen(); | 120 | const isFullscreenActive = this.isFullscreenActive(); |
91 | else if (document.fullscreen) document.exitFullscreen(); | 121 | if (fullscreen && !isFullscreenActive) document.body.requestFullscreen(); |
122 | else if (isFullscreenActive) document.exitFullscreen(); | ||
92 | } | 123 | } |
93 | 124 | ||
94 | onFullscreenChange() { | 125 | onFullscreenChange() { |
95 | this.$uiStore.toggleFullscreen(document.fullscreen); | 126 | this.$uiStore.toggleFullscreen(this.isFullscreenActive()); |
96 | } | 127 | } |
97 | } | 128 | } |
98 | </script> | 129 | </script> |
99 | 130 | ||
100 | <style lang="scss"> | 131 | <style lang="scss" module> |
101 | @import "~@/assets/scss/theme.scss"; | 132 | @import "~@/assets/scss/theme.scss"; |
102 | 133 | ||
103 | body, | 134 | :global(body), |
104 | html { | 135 | :global(html) { |
105 | height: 100%; | 136 | height: 100%; |
106 | overflow: hidden; | 137 | overflow: hidden; |
107 | touch-action: none; | 138 | touch-action: none; |
@@ -116,20 +147,23 @@ html { | |||
116 | bottom: 0; | 147 | bottom: 0; |
117 | left: 0; | 148 | left: 0; |
118 | right: 0; | 149 | right: 0; |
119 | &.layout-top { | 150 | &.layoutTop { |
120 | height: $layout-top; | 151 | height: $layout-top; |
121 | z-index: 1; | 152 | z-index: 1; |
122 | } | 153 | } |
123 | &.layout-left { | 154 | &.layoutLeft { |
124 | top: $layout-top; | 155 | top: $layout-top; |
125 | width: $layout-left; | 156 | width: $layout-left; |
126 | z-index: 2; | 157 | z-index: 2; |
127 | } | 158 | } |
128 | &.layout-content { | 159 | &.layoutContent { |
129 | top: var(--layout-top); | 160 | top: var(--layout-top); |
130 | left: var(--layout-left); | 161 | left: var(--layout-left); |
131 | z-index: 3; | 162 | z-index: 3; |
132 | overflow-x: hidden; | 163 | overflow-x: hidden; |
164 | &:focus { | ||
165 | outline: none; | ||
166 | } | ||
133 | } | 167 | } |
134 | } | 168 | } |
135 | .fullscreen { | 169 | .fullscreen { |
@@ -146,17 +180,16 @@ html { | |||
146 | } | 180 | } |
147 | 181 | ||
148 | .layout { | 182 | .layout { |
149 | &.layout-top { | 183 | &.layoutTop { |
150 | background-color: $panel-top-bgcolor; | 184 | background-color: $panel-top-bgcolor; |
151 | color: $panel-top-txtcolor; | 185 | color: $panel-top-txtcolor; |
152 | } | 186 | } |
153 | &.layout-left { | 187 | &.layoutLeft { |
154 | background-color: $panel-left-bgcolor; | 188 | background-color: $panel-left-bgcolor; |
155 | color: $panel-left-txtcolor; | 189 | color: $panel-left-txtcolor; |
156 | } | 190 | } |
157 | &.layout-content { | 191 | &.layoutContent { |
158 | background-color: $content-bgcolor; | 192 | background-color: $content-bgcolor; |
159 | } | 193 | } |
160 | } | 194 | } |
161 | // ===== | ||
162 | </style> | 195 | </style> |