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

@@ -12,9 +12,10 @@
@add-manga-click="$emit('add-manga-click', $event)" />
<main :class="[
'flex-1 mt-16 flex flex-col overflow-hidden',
'flex-1 flex flex-col overflow-hidden',
headerStore.shouldShowHeader ? 'mt-16' : 'mt-0',
isReaderMode ? '' : 'md:ml-60'
]">
]" style="transition: margin-top 300ms ease-in-out;">
<RouterView></RouterView>
</main>
</div>
@@ -23,10 +24,12 @@
<script setup>
import { computed, ref } from 'vue';
import { useRoute } from 'vue-router';
import { useHeaderStore } from '../../stores/headerStore';
import Header from './Header.vue';
import Sidebar from './Sidebar.vue';
const route = useRoute();
const headerStore = useHeaderStore();
const isSidebarOpen = ref(false);
// Détecte si on est en mode Reader

View File

@@ -4,6 +4,9 @@
<!-- Left section -->
<ToolbarSection :items="config.leftSection" />
<!-- Center section (optional slot) -->
<slot name="center" />
<!-- Right section -->
<ToolbarSection :items="config.rightSection" />
</div>

View File

@@ -1,11 +1,13 @@
<template>
<button
@click="$emit('click')"
:disabled="disabled"
:class="[
'flex flex-col items-center justify-center min-h-12 sm:min-h-14 w-min px-2 sm:ml-4 ml-1 rounded group text-white',
active
? 'text-green-500' // Style actif
: 'hover:text-green-500' // Effet de survol
: 'hover:text-green-500', // Effet de survol
disabled ? 'opacity-40 cursor-not-allowed' : ''
]"
:aria-label="label || 'Toolbar button'">
<component v-if="icon" :is="icon" class="h-5 w-5 sm:h-6 sm:w-6" />
@@ -30,6 +32,10 @@
active: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
}
});

View File

@@ -6,6 +6,7 @@
:icon="item.icon"
:label="item.label"
:active="item.active"
:disabled="item.disabled"
@click="item.onClick" />
<ToolbarDropdown
v-else-if="item.type === 'dropdown'"
@@ -14,7 +15,9 @@
:active="item.active"
:items="item.items" />
<Divider v-else-if="item.type === 'divider'" />
<!-- Ajoutez d'autres types d'éléments ici si nécessaire -->
<span
v-else-if="item.type === 'label'"
:class="['text-white px-1 select-none', item.class || 'text-xs']">{{ item.text }}</span>
</template>
</div>
</template>
@@ -36,6 +39,7 @@
item.type &&
(item.type === 'button' ||
item.type === 'divider' ||
item.type === 'label' ||
(item.type === 'dropdown' && Array.isArray(item.items)))
);
}

View File

@@ -4,19 +4,20 @@ export const useHeaderStore = defineStore('header', {
state: () => ({
isHeaderVisible: true,
isAutoHideEnabled: false,
isReaderToolbarVisible: true,
isReaderToolbarAutoHideEnabled: false,
lastScrollY: 0,
scrollDirection: 'up'
}),
getters: {
shouldShowHeader: (state) => {
// Si l'auto-hide n'est pas activé, toujours afficher le header
if (!state.isAutoHideEnabled) {
return true;
}
// Si l'auto-hide est activé, suivre la visibilité
if (!state.isAutoHideEnabled) return true;
return state.isHeaderVisible;
},
shouldShowReaderToolbar: (state) => {
if (!state.isReaderToolbarAutoHideEnabled) return true;
return state.isReaderToolbarVisible;
}
},
@@ -27,35 +28,47 @@ export const useHeaderStore = defineStore('header', {
disableAutoHide() {
this.isAutoHideEnabled = false;
this.isHeaderVisible = true; // Toujours visible quand désactivé
this.isHeaderVisible = true;
},
updateScrollDirection(scrollY) {
// Éviter les calculs inutiles si pas d'auto-hide
if (!this.isAutoHideEnabled) {
this.lastScrollY = scrollY;
return;
}
enableReaderToolbarAutoHide() {
this.isReaderToolbarAutoHideEnabled = true;
this.isReaderToolbarVisible = true;
},
// Détecter la direction du scroll avec un seuil pour éviter les micro-mouvements
disableReaderToolbarAutoHide() {
this.isReaderToolbarAutoHideEnabled = false;
this.isReaderToolbarVisible = true;
},
toggleReaderToolbarAutoHide() {
if (this.isReaderToolbarAutoHideEnabled) {
this.disableReaderToolbarAutoHide();
this.disableAutoHide();
} else {
this.enableReaderToolbarAutoHide();
this.enableAutoHide();
}
},
updateScrollDirection(scrollY) {
const scrollDifference = Math.abs(scrollY - this.lastScrollY);
if (scrollDifference < 5) {
// Mouvement trop petit, on ignore
return;
}
if (scrollY > this.lastScrollY && scrollY > 100) {
// Scroll vers le bas et suffisamment de scroll
if (this.scrollDirection !== 'down') {
this.scrollDirection = 'down';
this.isHeaderVisible = false;
if (this.isAutoHideEnabled) this.isHeaderVisible = false;
if (this.isReaderToolbarAutoHideEnabled) this.isReaderToolbarVisible = false;
}
} else if (scrollY < this.lastScrollY) {
// Scroll vers le haut
if (this.scrollDirection !== 'up') {
this.scrollDirection = 'up';
this.isHeaderVisible = true;
if (this.isAutoHideEnabled) this.isHeaderVisible = true;
if (this.isReaderToolbarAutoHideEnabled) this.isReaderToolbarVisible = true;
}
}