- 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
216 lines
8.2 KiB
Vue
216 lines
8.2 KiB
Vue
<template>
|
|
<div class="reader-settings">
|
|
<Transition
|
|
enter-active-class="transition-all duration-300 ease-out"
|
|
leave-active-class="transition-all duration-300 ease-in"
|
|
enter-from-class="opacity-0 translate-y-4 scale-95"
|
|
enter-to-class="opacity-100 translate-y-0 scale-100"
|
|
leave-from-class="opacity-100 translate-y-0 scale-100"
|
|
leave-to-class="opacity-0 translate-y-4 scale-95"
|
|
>
|
|
<div v-show="open" class="settings-panel" ref="panelRef">
|
|
|
|
<!-- Paramètres des doubles pages (mobile uniquement) -->
|
|
<div class="settings-section" v-if="isMobile">
|
|
<h3 class="section-title">
|
|
<svg class="w-5 h-5 inline mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
|
</svg>
|
|
Doubles pages (Mobile)
|
|
</h3>
|
|
|
|
<div class="setting-item">
|
|
<label class="setting-label">
|
|
<input
|
|
type="checkbox"
|
|
:checked="doublePageSettings.autoDetect"
|
|
@change="onDoublePageAutoDetectChange($event.target.checked)"
|
|
class="setting-checkbox"
|
|
/>
|
|
<span>Détection automatique</span>
|
|
</label>
|
|
<p class="setting-description">
|
|
Détecter et optimiser automatiquement l'affichage des doubles pages sur mobile
|
|
</p>
|
|
</div>
|
|
|
|
<div v-if="doublePageSettings.autoDetect" class="setting-item">
|
|
<label class="setting-label">Mode d'affichage</label>
|
|
<select
|
|
:value="doublePageMode"
|
|
@change="onDoublePageModeChange($event.target.value)"
|
|
class="setting-select"
|
|
>
|
|
<option value="rotate">Rotation suggérée</option>
|
|
<option value="scroll">Défilement horizontal</option>
|
|
<option value="normal">Affichage normal</option>
|
|
</select>
|
|
<p class="setting-description">
|
|
<span v-if="doublePageMode === 'rotate'">Suggère de tourner l'appareil pour une meilleure lecture</span>
|
|
<span v-else-if="doublePageMode === 'scroll'">Permet le défilement horizontal pour naviguer dans la page (commence à droite)</span>
|
|
<span v-else>Affichage standard sans optimisation spéciale</span>
|
|
</p>
|
|
</div>
|
|
|
|
<div v-if="doublePageSettings.autoDetect" class="setting-item">
|
|
<label class="setting-label">
|
|
Sensibilité de détection: {{ doublePageSettings.detectionThreshold.toFixed(1) }}
|
|
</label>
|
|
<input
|
|
type="range"
|
|
:value="doublePageSettings.detectionThreshold"
|
|
@input="onDetectionThresholdChange($event.target.value)"
|
|
min="1.0"
|
|
max="2.5"
|
|
step="0.1"
|
|
class="setting-slider"
|
|
/>
|
|
<p class="setting-description">
|
|
Plus la valeur est faible, plus la détection est sensible (1.4 recommandé)
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Réinitialiser -->
|
|
<div class="settings-section">
|
|
<div class="setting-actions">
|
|
<button @click="onResetPreferences" class="action-button reset">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
|
</svg>
|
|
Réinitialiser les préférences
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Transition>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
|
|
|
const props = defineProps({
|
|
doublePageMode: {
|
|
type: String,
|
|
default: 'rotate'
|
|
},
|
|
doublePageSettings: {
|
|
type: Object,
|
|
default: () => ({
|
|
autoDetect: true,
|
|
mobileOnly: true,
|
|
detectionThreshold: 1.4
|
|
})
|
|
},
|
|
open: {
|
|
type: Boolean,
|
|
default: false
|
|
}
|
|
});
|
|
|
|
const emit = defineEmits([
|
|
'toggleSettings',
|
|
'doublePageModeChange',
|
|
'doublePageAutoDetectChange',
|
|
'detectionThresholdChange',
|
|
'resetPreferences',
|
|
]);
|
|
|
|
const isMobile = computed(() => window.innerWidth < 768);
|
|
const panelRef = ref(null);
|
|
|
|
const handleClickOutside = (event) => {
|
|
if (props.open && panelRef.value && !panelRef.value.contains(event.target)) {
|
|
emit('toggleSettings');
|
|
}
|
|
};
|
|
|
|
onMounted(() => document.addEventListener('click', handleClickOutside, true));
|
|
onUnmounted(() => document.removeEventListener('click', handleClickOutside, true));
|
|
|
|
const onDoublePageModeChange = (mode) => emit('doublePageModeChange', mode);
|
|
const onDoublePageAutoDetectChange = (enabled) => emit('doublePageAutoDetectChange', enabled);
|
|
const onDetectionThresholdChange = (threshold) => emit('detectionThresholdChange', parseFloat(threshold));
|
|
const onResetPreferences = () => {
|
|
emit('resetPreferences');
|
|
emit('toggleSettings');
|
|
};
|
|
</script>
|
|
|
|
<style lang="postcss" scoped>
|
|
.reader-settings {
|
|
@apply relative;
|
|
}
|
|
|
|
.settings-panel {
|
|
@apply fixed top-20 right-4 z-40 w-80 max-w-[calc(100vw-2rem)] bg-gray-800 rounded-lg shadow-xl border border-gray-700 max-h-[80vh] overflow-y-auto;
|
|
}
|
|
|
|
@media (max-width: 480px) {
|
|
.settings-panel {
|
|
width: 90vw;
|
|
max-width: calc(100vw - 1rem);
|
|
right: 0.5rem;
|
|
}
|
|
}
|
|
|
|
.settings-section {
|
|
@apply p-4 border-b border-gray-700 last:border-b-0;
|
|
}
|
|
|
|
.section-title {
|
|
@apply text-white font-semibold text-lg mb-3 flex items-center;
|
|
}
|
|
|
|
.setting-item {
|
|
@apply mb-4 last:mb-0;
|
|
}
|
|
|
|
.setting-label {
|
|
@apply flex items-center gap-2 text-white font-medium text-sm mb-2 cursor-pointer;
|
|
}
|
|
|
|
.setting-checkbox {
|
|
@apply w-4 h-4 text-blue-600 bg-gray-700 border-gray-600 rounded focus:ring-blue-500 focus:ring-2;
|
|
}
|
|
|
|
.setting-select {
|
|
@apply w-full bg-gray-700 border border-gray-600 text-white text-sm rounded-lg px-3 py-2 focus:ring-blue-500 focus:border-blue-500;
|
|
}
|
|
|
|
.setting-slider {
|
|
@apply w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer mb-2;
|
|
}
|
|
|
|
.setting-slider::-webkit-slider-thumb {
|
|
@apply appearance-none w-4 h-4 bg-blue-600 rounded-full cursor-pointer;
|
|
}
|
|
|
|
.setting-slider::-moz-range-thumb {
|
|
@apply w-4 h-4 bg-blue-600 rounded-full cursor-pointer border-none;
|
|
}
|
|
|
|
.setting-description {
|
|
@apply text-gray-400 text-xs leading-relaxed;
|
|
}
|
|
|
|
.setting-actions {
|
|
@apply flex gap-2;
|
|
}
|
|
|
|
.action-button {
|
|
@apply flex items-center gap-2 px-3 py-2 rounded-lg text-sm font-medium transition-colors;
|
|
}
|
|
|
|
.action-button.reset {
|
|
@apply bg-red-600 hover:bg-red-700 text-white;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.settings-panel {
|
|
@apply right-2 w-72;
|
|
}
|
|
}
|
|
</style>
|