feat: mise à jour des règles de configuration pour l'API Platform et ajout de nouveaux composants pour le lecteur, incluant la gestion des pages infinies et des contrôles de lecture
This commit is contained in:
parent
d123166dcb
commit
346fede878
@@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<div class="infinite-reader" ref="containerRef">
|
||||
<div v-for="(page, index) in pages" :key="index" class="page-wrapper">
|
||||
<div v-if="page?.loading" class="loading">
|
||||
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-primary"></div>
|
||||
</div>
|
||||
<div v-else-if="page?.error" class="error">
|
||||
{{ page.error }}
|
||||
</div>
|
||||
<ReaderPage v-else-if="page?.base64Content" :page-data="page" :page-number="index + 1" :zoom="zoom" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted, watch } from 'vue';
|
||||
import { nextTick } from 'vue';
|
||||
import ReaderPage from './ReaderPage.vue';
|
||||
|
||||
const props = defineProps({
|
||||
pages: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
zoom: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['pageVisible']);
|
||||
|
||||
const containerRef = ref(null);
|
||||
const observer = ref(null);
|
||||
|
||||
const observeIntersection = entries => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
const pageIndex = parseInt(entry.target.getAttribute('data-page-index'));
|
||||
emit('pageVisible', pageIndex);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const setupIntersectionObserver = () => {
|
||||
if (observer.value) {
|
||||
observer.value.disconnect();
|
||||
}
|
||||
|
||||
observer.value = new IntersectionObserver(observeIntersection, {
|
||||
root: null,
|
||||
threshold: 0.5
|
||||
});
|
||||
|
||||
nextTick(() => {
|
||||
const pageElements = containerRef.value?.querySelectorAll('.page-wrapper');
|
||||
if (pageElements) {
|
||||
pageElements.forEach((element, index) => {
|
||||
element.setAttribute('data-page-index', index);
|
||||
observer.value.observe(element);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.pages,
|
||||
() => {
|
||||
setupIntersectionObserver();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
setupIntersectionObserver();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (observer.value) {
|
||||
observer.value.disconnect();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="postcss" scoped>
|
||||
.infinite-reader {
|
||||
@apply flex-1 flex flex-col items-center overflow-y-auto py-8;
|
||||
height: calc(100vh - 8rem);
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
.page-wrapper {
|
||||
@apply w-full flex justify-center min-h-[200px] mb-4;
|
||||
}
|
||||
|
||||
.loading,
|
||||
.error {
|
||||
@apply flex items-center justify-center;
|
||||
width: 70vw;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.error {
|
||||
@apply text-red-500 text-xl bg-red-500/10 rounded-lg;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user