style(reader): améliorer la toolbar et l'UI du mode scroll

- Corriger la troncature de la toolbar (max-height 4rem → 5rem)
- Animer la toolbar en translateY pour un effet "bloc uni" avec le header
- Corriger le bug d'auto-hide du header après switch simple → scroll
- Augmenter la taille du titre de chapitre dans la toolbar (text-sm font-medium)
- Harmoniser le bouton scroll-to-top avec le style des ToolbarButtons
- Ajouter support de prop `class` sur les labels de ToolbarSection
This commit is contained in:
ext.jeremy.guillot@maxicoffee.domains
2026-03-15 16:50:02 +01:00
parent cc702cff19
commit 9c47c717d0
18 changed files with 396 additions and 562 deletions

View File

@@ -0,0 +1,178 @@
<template>
<Toolbar :config="toolbarConfig">
<template #center>
<!-- Mode simple : navigation entre pages -->
<div v-if="store.readingMode === 'single'" class="flex items-center gap-1">
<button
@click="store.previousPage()"
:disabled="store.isFirstPage"
class="nav-btn"
title="Page précédente"
>
<ChevronLeftIcon class="h-4 w-4" />
</button>
<span class="text-white text-sm w-16 text-center">
{{ store.currentPage + 1 }} / {{ store.totalPages }}
</span>
<button
@click="store.nextPage()"
:disabled="store.isLastPage"
class="nav-btn"
title="Page suivante"
>
<ChevronRightIcon class="h-4 w-4" />
</button>
</div>
<!-- Mode scroll : navigation entre chapitres (ordre inversé en RTL) -->
<div v-else class="flex items-center gap-1">
<button
@click="leftChapterAction"
:disabled="!canGoLeftChapter || store.isLoading"
class="chapter-nav-btn"
:title="store.readingDirection === 'rtl' ? 'Chapitre suivant' : 'Chapitre précédent'"
>
<ChevronDoubleLeftIcon class="h-4 w-4 flex-shrink-0" />
<span class="text-xs">{{ store.readingDirection === 'rtl' ? 'Suivant' : 'Précédent' }}</span>
</button>
<button
@click="rightChapterAction"
:disabled="!canGoRightChapter || store.isLoading"
class="chapter-nav-btn"
:title="store.readingDirection === 'rtl' ? 'Chapitre précédent' : 'Chapitre suivant'"
>
<span class="text-xs">{{ store.readingDirection === 'rtl' ? 'Précédent' : 'Suivant' }}</span>
<ChevronDoubleRightIcon class="h-4 w-4 flex-shrink-0" />
</button>
</div>
</template>
</Toolbar>
</template>
<script setup>
import {
ArrowLeftIcon,
ChevronDoubleLeftIcon,
ChevronDoubleRightIcon,
ChevronLeftIcon,
ChevronRightIcon,
DocumentIcon,
EyeIcon,
EyeSlashIcon,
ListBulletIcon,
MinusIcon,
PlusIcon
} from '@heroicons/vue/24/outline';
import { computed } from 'vue';
import { useRouter } from 'vue-router';
import Toolbar from '../../../../shared/components/ui/Toolbar.vue';
import { useHeaderStore } from '../../../../shared/stores/headerStore';
import { useReaderStore } from '../../application/store/readerStore';
const props = defineProps({
chapterReaderRef: {
type: Object,
default: null
}
});
const store = useReaderStore();
const headerStore = useHeaderStore();
const router = useRouter();
// Vue auto-unwrap les refs dans le template : chapterReaderRef est déjà l'instance
const reader = computed(() => props.chapterReaderRef);
const goBack = () => {
const mangaId = store.currentChapter?.mangaId;
if (mangaId) {
router.push({ name: 'manga-details', params: { id: mangaId } });
} else {
router.back();
}
};
const toggleReadingMode = () => reader.value?.toggleReadingMode();
const toggleReadingDirection = () => reader.value?.toggleReadingDirection();
const zoomIn = () => store.setZoom(Math.min(store.zoom + 0.1, 2));
const zoomOut = () => store.setZoom(Math.max(store.zoom - 0.1, 0.5));
// En RTL, le bouton gauche (◄◄) avance dans l'histoire (chapitre suivant)
const isRtl = computed(() => store.readingDirection === 'rtl');
const leftChapterAction = () => isRtl.value ? store.goToNextChapter() : store.goToPreviousChapter();
const rightChapterAction = () => isRtl.value ? store.goToPreviousChapter() : store.goToNextChapter();
const canGoLeftChapter = computed(() => isRtl.value ? store.hasNextChapter : store.hasPreviousChapter);
const canGoRightChapter = computed(() => isRtl.value ? store.hasPreviousChapter : store.hasNextChapter);
const toolbarConfig = computed(() => ({
leftSection: [
{
type: 'button',
icon: ArrowLeftIcon,
label: 'Retour',
onClick: goBack,
},
{
type: 'label',
text: store.currentChapter?.title || '',
class: 'text-sm font-medium',
},
...(store.currentChapter?.number != null ? [{
type: 'label',
text: `Ch.${store.currentChapter.number}`,
}] : []),
],
rightSection: [
{
type: 'button',
icon: store.readingMode === 'single' ? ListBulletIcon : DocumentIcon,
label: store.readingMode === 'single' ? 'Scroll' : 'Simple',
active: store.readingMode === 'infinite',
onClick: toggleReadingMode,
},
{
type: 'button',
label: store.readingDirection.toUpperCase(),
active: store.readingDirection === 'rtl',
onClick: toggleReadingDirection,
},
{ type: 'divider' },
{
type: 'button',
icon: MinusIcon,
disabled: store.zoom <= 0.5,
onClick: zoomOut,
},
{
type: 'label',
text: `${Math.round(store.zoom * 100)}%`,
},
{
type: 'button',
icon: PlusIcon,
disabled: store.zoom >= 2,
onClick: zoomIn,
},
...(store.readingMode === 'infinite' ? [
{ type: 'divider' },
{
type: 'button',
icon: headerStore.isReaderToolbarAutoHideEnabled ? EyeSlashIcon : EyeIcon,
active: headerStore.isReaderToolbarAutoHideEnabled,
title: headerStore.isReaderToolbarAutoHideEnabled ? 'Toolbar auto-masquée' : 'Toolbar toujours visible',
onClick: () => headerStore.toggleReaderToolbarAutoHide(),
},
] : []),
],
}));
</script>
<style lang="postcss" scoped>
.nav-btn {
@apply flex items-center justify-center w-7 h-7 rounded bg-gray-700 hover:bg-gray-600 disabled:opacity-40 disabled:cursor-not-allowed transition-colors text-white;
}
.chapter-nav-btn {
@apply flex items-center justify-between gap-1 h-7 w-28 px-2 rounded bg-gray-700 hover:bg-gray-600 disabled:opacity-40 disabled:cursor-not-allowed transition-colors text-white;
}
</style>