136 lines
3.4 KiB
Vue
136 lines
3.4 KiB
Vue
<template>
|
|
<div id="top" />
|
|
<NavBar @menuInteraction="updateHideScrollBar" />
|
|
<main>
|
|
<div v-if="appLoading" class="app-container">
|
|
<div class="app-loading">
|
|
<Loader />
|
|
</div>
|
|
</div>
|
|
<div v-else class="app-container" :class="{ 'hide-scroll': hideScrollBar }">
|
|
<router-view v-if="appConfig" />
|
|
<NoConfig v-else />
|
|
</div>
|
|
<div class="container scroll">
|
|
<div
|
|
class="scroll-button"
|
|
:class="{ 'display-button': displayScrollButton }"
|
|
@click="scrollToTop"
|
|
>
|
|
<i class="fa fa-chevron-up" aria-hidden="true"></i>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
<Footer
|
|
v-if="appConfig"
|
|
:version="appConfig ? appConfig.version : ''"
|
|
:adminContact="appConfig.admin_contact"
|
|
/>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, ref, onBeforeMount, onMounted } from 'vue'
|
|
import type { ComputedRef } from 'vue'
|
|
|
|
import Footer from '@/components/Footer.vue'
|
|
import NavBar from '@/components/NavBar.vue'
|
|
import NoConfig from '@/components/NoConfig.vue'
|
|
import { ROOT_STORE } from '@/store/constants'
|
|
import type { TAppConfig } from '@/types/application'
|
|
import type { TLanguage } from '@/types/locales'
|
|
import { useStore } from '@/use/useStore'
|
|
import { isLanguageSupported } from '@/utils/locales'
|
|
|
|
const store = useStore()
|
|
|
|
const appConfig: ComputedRef<TAppConfig> = computed(
|
|
() => store.getters[ROOT_STORE.GETTERS.APP_CONFIG]
|
|
)
|
|
const appLoading: ComputedRef<boolean> = computed(
|
|
() => store.getters[ROOT_STORE.GETTERS.APP_LOADING]
|
|
)
|
|
const hideScrollBar = ref(false)
|
|
const displayScrollButton = ref(false)
|
|
|
|
onBeforeMount(() => {
|
|
initLanguage()
|
|
store.dispatch(ROOT_STORE.ACTIONS.GET_APPLICATION_CONFIG)
|
|
})
|
|
onMounted(() => scroll())
|
|
|
|
function updateHideScrollBar(isMenuOpen: boolean) {
|
|
hideScrollBar.value = isMenuOpen
|
|
}
|
|
function isScrolledToBottom(element: Element): boolean {
|
|
return (
|
|
element.getBoundingClientRect().top < window.innerHeight &&
|
|
element.getBoundingClientRect().bottom >= 0
|
|
)
|
|
}
|
|
function scroll() {
|
|
window.onscroll = () => {
|
|
const bottom = document.querySelector('#bottom')
|
|
displayScrollButton.value = bottom !== null && isScrolledToBottom(bottom)
|
|
}
|
|
}
|
|
function scrollToTop() {
|
|
window.scrollTo({
|
|
top: 0,
|
|
behavior: 'smooth',
|
|
})
|
|
setTimeout(() => {
|
|
displayScrollButton.value = false
|
|
}, 300)
|
|
}
|
|
function initLanguage() {
|
|
let language: TLanguage = 'en'
|
|
try {
|
|
const navigatorLanguage = navigator.language.split('-')[0]
|
|
if (isLanguageSupported(navigatorLanguage)) {
|
|
language = navigatorLanguage
|
|
}
|
|
} catch (e) {
|
|
language = 'en'
|
|
}
|
|
store.dispatch(ROOT_STORE.ACTIONS.UPDATE_APPLICATION_LANGUAGE, language)
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
@import '~@/scss/base.scss';
|
|
.app-container {
|
|
height: $app-height;
|
|
|
|
&.hide-scroll {
|
|
overflow: hidden;
|
|
}
|
|
|
|
.app-loading {
|
|
display: flex;
|
|
align-items: center;
|
|
height: 100%;
|
|
}
|
|
}
|
|
|
|
.scroll {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
position: fixed;
|
|
bottom: 42px;
|
|
right: -15px;
|
|
padding: 0 $default-padding * 2.5;
|
|
|
|
.scroll-button {
|
|
background-color: var(--scroll-button-bg-color);
|
|
border-radius: $border-radius;
|
|
box-shadow: 1px 1px 3px var(--app-shadow-color);
|
|
display: none;
|
|
padding: 0 $default-padding;
|
|
|
|
&.display-button {
|
|
display: block;
|
|
}
|
|
}
|
|
}
|
|
</style>
|