aboutsummaryrefslogtreecommitdiff
path: root/viewer/src/views/item_handlers/async/AsyncEpubViewer.vue
diff options
context:
space:
mode:
Diffstat (limited to 'viewer/src/views/item_handlers/async/AsyncEpubViewer.vue')
-rw-r--r--viewer/src/views/item_handlers/async/AsyncEpubViewer.vue180
1 files changed, 180 insertions, 0 deletions
diff --git a/viewer/src/views/item_handlers/async/AsyncEpubViewer.vue b/viewer/src/views/item_handlers/async/AsyncEpubViewer.vue
new file mode 100644
index 0000000..b5c0cb4
--- /dev/null
+++ b/viewer/src/views/item_handlers/async/AsyncEpubViewer.vue
@@ -0,0 +1,180 @@
1<!--
2-- ldgallery - A static generator which turns a collection of tagged
3-- pictures into a searchable web gallery.
4--
5-- Copyright (C) 2022 Pacien TRAN-GIRARD
6--
7-- This program is free software: you can redistribute it and/or modify
8-- it under the terms of the GNU Affero General Public License as
9-- published by the Free Software Foundation, either version 3 of the
10-- License, or (at your option) any later version.
11--
12-- This program is distributed in the hope that it will be useful,
13-- but WITHOUT ANY WARRANTY; without even the implied warranty of
14-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15-- GNU Affero General Public License for more details.
16--
17-- You should have received a copy of the GNU Affero General Public License
18-- along with this program. If not, see <https://www.gnu.org/licenses/>.
19-->
20
21<template>
22 <div :class="$style.container">
23 <div
24 ref="view"
25 :class="$style.epubView"
26 class="scrollbar"
27 />
28
29 <ul
30 v-if="prevSection || nextSection"
31 :class="$style.navBar"
32 >
33 <li>
34 <LdLink
35 v-if="prevSection"
36 @click="goToPrevSection"
37 >
38 <fa-icon
39 :icon="faSquareCaretLeft"
40 size="lg"
41 alt="«"
42 />
43 {{ prevSectionLabel }}
44 </LdLink>
45 </li>
46
47 <li>
48 {{ currSectionLabel }}
49 </li>
50
51 <li>
52 <LdLink
53 v-if="nextSection"
54 @click="goToNextSection"
55 >
56 {{ nextSectionLabel }}
57 <fa-icon
58 :icon="faSquareCaretRight"
59 size="lg"
60 alt="»"
61 />
62 </LdLink>
63 </li>
64 </ul>
65 </div>
66</template>
67
68<script setup lang="ts">
69import { EPUBItem } from '@/@types/gallery';
70import { useItemResource } from '@/services/ui/ldItemResourceUrl';
71import { useUiStore } from '@/store/uiStore';
72import ePub, { Rendition } from 'epubjs';
73import { SpineItem } from 'epubjs/types/section';
74import { computed, PropType, Ref, ref, toRef, watch } from 'vue';
75import { useI18n } from 'vue-i18n';
76import LdLink from '@/components/LdLink.vue';
77import {
78 faSquareCaretLeft,
79 faSquareCaretRight,
80} from '@fortawesome/free-solid-svg-icons';
81
82const { t } = useI18n();
83const uiStore = useUiStore();
84
85const props = defineProps({
86 item: { type: Object as PropType<EPUBItem>, required: true },
87});
88
89const { itemResourceUrl } = useItemResource(toRef(props, 'item'));
90
91const view = ref<HTMLDivElement>();
92const rendition = ref<Rendition>();
93const currSection = ref<SpineItem>();
94const prevSection = ref<SpineItem>();
95const nextSection = ref<SpineItem>();
96
97const book = computed(() => ePub(itemResourceUrl.value));
98
99watch([book, view], ([book, view]) => {
100 if (!view) return;
101 view.innerHTML = '';
102 rendition.value = book.renderTo(view, {
103 flow: 'scrolled-doc',
104 width: '100%',
105 });
106});
107
108watch(rendition, async(rendition, oldRendition) => {
109 if (!rendition) return;
110 oldRendition?.off('rendered', updateNavigation);
111 await rendition.display();
112 rendition.on('rendered', updateNavigation);
113});
114
115watch(() => uiStore.fullWidth, () => {
116 // Simulate a window resize to force EPub to resize the container
117 setTimeout(() => window.dispatchEvent(new Event('resize')));
118});
119
120function updateNavigation(currentSection: SpineItem) {
121 currSection.value = currentSection;
122 prevSection.value = currentSection.prev();
123 nextSection.value = currentSection.next();
124}
125
126const currSectionLabel = computed(() => getSectionTitle(currSection) ?? '');
127const prevSectionLabel = computed(() =>
128 getSectionTitle(prevSection) ?? t('epubViewer.previousSection'));
129const nextSectionLabel = computed(() =>
130 getSectionTitle(nextSection) ?? t('epubViewer.nextSection'));
131
132function getSectionTitle(section: Ref<SpineItem | undefined>): string | null {
133 if (!section.value?.href) return null;
134 return book.value?.navigation.get(section.value.href).label;
135}
136
137function goToPrevSection() {
138 rendition.value?.prev();
139}
140
141function goToNextSection() {
142 rendition.value?.next();
143}
144</script>
145
146<style lang="scss" module>
147@import "~@/assets/scss/theme";
148
149.container {
150 display: flex;
151 flex-direction: column;
152 height: 100%;
153 background-color: $viewer-epub-background;
154}
155
156.epubView {
157 flex: 1;
158 overflow-x: hidden;
159}
160
161.navBar {
162 display: flex;
163 flex-direction: row;
164 list-style-type: none;
165 margin: 0;
166 padding: .75em;
167
168 background-color: $panel-bottom-bgcolor;
169 color: $panel-bottom-txtcolor;
170
171 > li {
172 flex: 1;
173 text-align: center;
174
175 > a {
176 padding: .5em;
177 }
178 }
179}
180</style>