- Ajout du store userPreferencesStore (thème, vue, tri, pagination, lecteur) - Page UserPreferencesPage pour configurer toutes les préférences - Câblage des prefs dans HomePage (viewMode, sortBy, itemsPerPage), readerStore (fallback prefs), ChapterReader (autoHide, autoFullscreen, sync), useNotifications (toastDuration) - Thème sombre (dark: Tailwind) sur tous les composants Vue : Layout, Pagination, NotificationToast, MangaCard, MangaVolume, MangaDetails, AddManga, HomePage, ActivityPage, JobItem, MangaDeleteModal, MangaEditModal, MangaPreferredSourcesModal, ManageChaptersModal, MangaChapterList, MangaChapter, ConversionPage, FileUploadArea, ConversionProgress, NewImportPage, FileImportCard, MangaMatchCard, StatusBadge, ImportResults - i18n partiellement initialisé Jeremy Guillot
104 lines
3.3 KiB
Vue
104 lines
3.3 KiB
Vue
<template>
|
|
<div class="fixed bottom-4 left-4 z-50 flex flex-col-reverse gap-2">
|
|
<TransitionGroup
|
|
name="notification"
|
|
tag="div"
|
|
class="flex flex-col-reverse gap-2"
|
|
>
|
|
<div
|
|
v-for="notification in notifications"
|
|
:key="notification.id"
|
|
:class="[
|
|
'max-w-md w-full bg-white dark:bg-gray-800 shadow-lg rounded-lg pointer-events-auto ring-1 ring-black ring-opacity-5 overflow-hidden',
|
|
getNotificationClass(notification.type)
|
|
]"
|
|
>
|
|
<div class="p-4">
|
|
<div class="flex items-start">
|
|
<div class="flex-shrink-0 mr-3">
|
|
<button
|
|
@click="removeNotification(notification.id)"
|
|
class="bg-white dark:bg-gray-800 rounded-md inline-flex text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
|
|
>
|
|
<span class="sr-only">Close</span>
|
|
<XMarkIcon class="h-5 w-5" />
|
|
</button>
|
|
</div>
|
|
<div class="flex-1 pt-0.5 min-w-0">
|
|
<p class="text-sm font-medium text-gray-900 dark:text-gray-100 break-words">
|
|
{{ notification.message }}
|
|
</p>
|
|
</div>
|
|
<div class="flex-shrink-0 ml-3">
|
|
<component :is="getIcon(notification.type)" :class="[
|
|
'h-6 w-6',
|
|
getIconClass(notification.type)
|
|
]" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</TransitionGroup>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import {
|
|
CheckCircleIcon,
|
|
ExclamationCircleIcon,
|
|
ExclamationTriangleIcon,
|
|
InformationCircleIcon,
|
|
XMarkIcon
|
|
} from '@heroicons/vue/24/outline';
|
|
import { useNotifications } from '../../composables/useNotifications';
|
|
|
|
const { notifications, removeNotification } = useNotifications();
|
|
|
|
const getIcon = (type) => {
|
|
const icons = {
|
|
success: CheckCircleIcon,
|
|
error: ExclamationCircleIcon,
|
|
warning: ExclamationTriangleIcon,
|
|
info: InformationCircleIcon
|
|
};
|
|
return icons[type] || InformationCircleIcon;
|
|
};
|
|
|
|
const getNotificationClass = (type) => {
|
|
const classes = {
|
|
success: 'border-r-4 border-green-400',
|
|
error: 'border-r-4 border-red-400',
|
|
warning: 'border-r-4 border-yellow-400',
|
|
info: 'border-r-4 border-blue-400'
|
|
};
|
|
return classes[type] || classes.info;
|
|
};
|
|
|
|
const getIconClass = (type) => {
|
|
const classes = {
|
|
success: 'text-green-400',
|
|
error: 'text-red-400',
|
|
warning: 'text-yellow-400',
|
|
info: 'text-blue-400'
|
|
};
|
|
return classes[type] || classes.info;
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
.notification-enter-active,
|
|
.notification-leave-active {
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.notification-enter-from {
|
|
opacity: 0;
|
|
transform: translateX(-100%);
|
|
}
|
|
|
|
.notification-leave-to {
|
|
opacity: 0;
|
|
transform: translateX(-100%);
|
|
}
|
|
</style>
|