<!-- -- ldgallery - A static generator which turns a collection of tagged -- pictures into a searchable web gallery. -- -- Copyright (C) 2022 Pacien TRAN-GIRARD -- -- This program is free software: you can redistribute it and/or modify -- it under the terms of the GNU Affero General Public License as -- published by the Free Software Foundation, either version 3 of the -- License, or (at your option) any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU Affero General Public License for more details. -- -- You should have received a copy of the GNU Affero General Public License -- along with this program. If not, see <https://www.gnu.org/licenses/>. --> <template> <div :class="$style.container"> <div ref="view" :class="$style.epubView" class="scrollbar" /> <ul v-if="prevSection || nextSection" :class="$style.navBar" > <li> <LdLink v-if="prevSection" @click="goToPrevSection" > <fa-icon :icon="faSquareCaretLeft" size="lg" alt="«" /> {{ prevSectionLabel }} </LdLink> </li> <li> {{ currSectionLabel }} </li> <li> <LdLink v-if="nextSection" @click="goToNextSection" > {{ nextSectionLabel }} <fa-icon :icon="faSquareCaretRight" size="lg" alt="»" /> </LdLink> </li> </ul> </div> </template> <script setup lang="ts"> import { EPUBItem } from '@/@types/gallery'; import { useItemResource } from '@/services/ui/ldItemResourceUrl'; import { useUiStore } from '@/store/uiStore'; import ePub, { Rendition } from 'epubjs'; import { SpineItem } from 'epubjs/types/section'; import { computed, PropType, Ref, ref, toRef, watch } from 'vue'; import { useI18n } from 'vue-i18n'; import LdLink from '@/components/LdLink.vue'; import { faSquareCaretLeft, faSquareCaretRight, } from '@fortawesome/free-solid-svg-icons'; const { t } = useI18n(); const uiStore = useUiStore(); const props = defineProps({ item: { type: Object as PropType<EPUBItem>, required: true }, }); const { itemResourceUrl } = useItemResource(toRef(props, 'item')); const view = ref<HTMLDivElement>(); const rendition = ref<Rendition>(); const currSection = ref<SpineItem>(); const prevSection = ref<SpineItem>(); const nextSection = ref<SpineItem>(); const book = computed(() => ePub(itemResourceUrl.value)); watch([book, view], ([book, view]) => { if (!view) return; view.innerHTML = ''; rendition.value = book.renderTo(view, { flow: 'scrolled-doc', width: '100%', }); }); watch(rendition, async(rendition, oldRendition) => { if (!rendition) return; oldRendition?.off('rendered', updateNavigation); await rendition.display(); rendition.on('rendered', updateNavigation); }); watch(() => uiStore.fullWidth, () => { // Simulate a window resize to force EPub to resize the container setTimeout(() => window.dispatchEvent(new Event('resize'))); }); function updateNavigation(currentSection: SpineItem) { currSection.value = currentSection; prevSection.value = currentSection.prev(); nextSection.value = currentSection.next(); } const currSectionLabel = computed(() => getSectionTitle(currSection) ?? ''); const prevSectionLabel = computed(() => getSectionTitle(prevSection) ?? t('epubViewer.previousSection')); const nextSectionLabel = computed(() => getSectionTitle(nextSection) ?? t('epubViewer.nextSection')); function getSectionTitle(section: Ref<SpineItem | undefined>): string | null { if (!section.value?.href) return null; return book.value?.navigation.get(section.value.href).label; } function goToPrevSection() { rendition.value?.prev(); } function goToNextSection() { rendition.value?.next(); } </script> <style lang="scss" module> @import "~@/assets/scss/theme"; .container { display: flex; flex-direction: column; height: 100%; background-color: $viewer-epub-background; } .epubView { flex: 1; overflow-x: hidden; } .navBar { display: flex; flex-direction: row; list-style-type: none; margin: 0; padding: .75em; background-color: $panel-bottom-bgcolor; color: $panel-bottom-txtcolor; > li { flex: 1; text-align: center; > a { padding: .5em; } } } </style>