Files
Mangarr/assets/vue/app/domain/reader/presentation/components/SingleModeReader.vue

217 lines
5.9 KiB
Vue

<template>
<div class="single-mode-reader">
<!-- Zone cliquable pour navigation -->
<div class="page-navigation-wrapper" @click="handlePageClick">
<!-- Zone de navigation gauche (invisible) -->
<div
class="navigation-zone left-zone"
@click.stop="goToPrevious"
@mouseenter="showLeftHint"
@mouseleave="hideLeftHint"
title="Page précédente"
></div>
<!-- Page centrale -->
<div class="page-content">
<ReaderPage
:page-data="pageData"
:page-number="pageNumber"
:zoom="zoom"
/>
</div>
<!-- Zone de navigation droite (invisible) -->
<div
class="navigation-zone right-zone"
@click.stop="goToNext"
@mouseenter="showRightHint"
@mouseleave="hideRightHint"
title="Page suivante"
></div>
</div>
<!-- Indicateurs visuels de navigation -->
<div class="navigation-hints">
<div class="hint left-hint" v-if="canGoToPrevious && (showNavigationHints || showLeftHintHover)">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
</div>
<div class="hint right-hint" v-if="canGoToNext && (showNavigationHints || showRightHintHover)">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
</div>
</div>
</div>
</template>
<script setup>
import { computed, ref } from 'vue';
import { useReaderStore } from '../../application/store/readerStore';
import ReaderPage from './ReaderPage.vue';
const props = defineProps({
pageData: {
type: Object,
required: true
},
pageNumber: {
type: Number,
required: true
},
zoom: {
type: Number,
required: true
}
});
const store = useReaderStore();
// État pour afficher les indicateurs de navigation
const showNavigationHints = ref(false);
const showLeftHintHover = ref(false);
const showRightHintHover = ref(false);
let hintTimeout = null;
// Computed pour vérifier les possibilités de navigation
const canGoToPrevious = computed(() => {
return !store.isFirstPage || store.hasPreviousChapter;
});
const canGoToNext = computed(() => {
return !store.isLastPage || store.hasNextChapter;
});
// Navigation vers la page/chapitre précédent
const goToPrevious = async () => {
if (!store.isFirstPage) {
// Page précédente dans le même chapitre
await store.previousPage();
} else if (store.hasPreviousChapter) {
// Chapitre précédent (le store gère automatiquement la navigation vers la dernière page)
await store.goToPreviousChapter();
}
showNavigationHints.value = true;
clearTimeout(hintTimeout);
hintTimeout = setTimeout(() => {
showNavigationHints.value = false;
}, 1000);
};
// Navigation vers la page/chapitre suivant
const goToNext = async () => {
if (!store.isLastPage) {
// Page suivante dans le même chapitre
await store.nextPage();
} else if (store.hasNextChapter) {
// Première page du chapitre suivant
await store.goToNextChapter();
// Le store va charger le chapitre suivant et se positionner automatiquement à la première page
}
showNavigationHints.value = true;
clearTimeout(hintTimeout);
hintTimeout = setTimeout(() => {
showNavigationHints.value = false;
}, 1000);
};
// Gestion du clic général sur la page (fallback)
const handlePageClick = (event) => {
// Si le clic n'a pas été intercepté par les zones, on navigue vers la page suivante
goToNext();
};
// Gestion des hints au hover
const showLeftHint = () => {
showLeftHintHover.value = true;
};
const hideLeftHint = () => {
showLeftHintHover.value = false;
};
const showRightHint = () => {
showRightHintHover.value = true;
};
const hideRightHint = () => {
showRightHintHover.value = false;
};
</script>
<style lang="postcss" scoped>
.single-mode-reader {
@apply relative w-full h-full flex items-center justify-center;
}
.page-navigation-wrapper {
@apply relative w-full h-full flex items-center justify-center cursor-pointer;
}
.page-content {
@apply flex-1 h-full flex items-center justify-center;
pointer-events: none; /* Empêche les clics sur l'image elle-même */
}
.navigation-zone {
@apply absolute top-0 bottom-0 z-10;
width: 33%; /* 1/3 de la largeur pour chaque zone */
}
.left-zone {
@apply left-0;
cursor: pointer;
}
.right-zone {
@apply right-0;
cursor: pointer;
}
/* Indicateurs visuels de navigation */
.navigation-hints {
@apply absolute inset-0 pointer-events-none z-20;
}
.hint {
@apply absolute top-1/2 transform -translate-y-1/2;
@apply bg-black/50 text-white p-2 rounded-full;
@apply transition-all duration-300;
}
.left-hint {
@apply left-4;
animation: slideInLeft 0.3s ease-out;
}
.right-hint {
@apply right-4;
animation: slideInRight 0.3s ease-out;
}
@keyframes slideInLeft {
from {
opacity: 0;
transform: translateY(-50%) translateX(-20px);
}
to {
opacity: 1;
transform: translateY(-50%) translateX(0);
}
}
@keyframes slideInRight {
from {
opacity: 0;
transform: translateY(-50%) translateX(20px);
}
to {
opacity: 1;
transform: translateY(-50%) translateX(0);
}
}
/* Pas d'effet hover background - les flèches apparaissent à la place */
</style>