- app.vue : Layout principal avec navbar et navigation - ToastContainer.vue : Système de notifications toast - app.css : Styles globaux avec DaisyUI et accessibilité - Configuration des paramètres d'affichage (zoom, densité, contraste) - Navigation responsive avec menu déroulant - Bouton 'Nouveau' avec actions rapides
95 lines
3.2 KiB
Vue
95 lines
3.2 KiB
Vue
<template>
|
|
<div class="toast-container fixed top-4 right-4 z-50 space-y-2 pointer-events-none">
|
|
<TransitionGroup name="toast">
|
|
<div
|
|
v-for="toast in toasts"
|
|
:key="toast.id"
|
|
class="toast-item"
|
|
:class="[
|
|
'transform transition-all duration-300 ease-in-out',
|
|
toast.visible ? 'translate-x-0 opacity-100 pointer-events-auto' : 'translate-x-full opacity-0 pointer-events-none'
|
|
]"
|
|
>
|
|
<div
|
|
class="alert shadow-lg max-w-sm"
|
|
:class="getToastClasses(toast.type)"
|
|
>
|
|
<div class="flex items-center gap-2">
|
|
<!-- Icon -->
|
|
<div class="flex-shrink-0">
|
|
<svg v-if="toast.type === 'success'" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
|
</svg>
|
|
<svg v-else-if="toast.type === 'error'" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
|
</svg>
|
|
<svg v-else-if="toast.type === 'warning'" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"></path>
|
|
</svg>
|
|
<svg v-else class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|
</svg>
|
|
</div>
|
|
|
|
<!-- Message -->
|
|
<div class="flex-1">
|
|
<span class="text-sm font-medium">{{ toast.message }}</span>
|
|
</div>
|
|
|
|
<!-- Close button -->
|
|
<button
|
|
@click="removeToast(toast.id)"
|
|
class="btn btn-ghost btn-xs"
|
|
>
|
|
<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="M6 18L18 6M6 6l12 12"></path>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</TransitionGroup>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { useToast } from '~/composables/useToast'
|
|
|
|
const { toasts, removeToast } = useToast()
|
|
|
|
const getToastClasses = (type) => {
|
|
switch (type) {
|
|
case 'success':
|
|
return 'alert-success'
|
|
case 'error':
|
|
return 'alert-error'
|
|
case 'warning':
|
|
return 'alert-warning'
|
|
case 'info':
|
|
return 'alert-info'
|
|
default:
|
|
return 'alert-info'
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.toast-enter-active,
|
|
.toast-leave-active {
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.toast-enter-from {
|
|
opacity: 0;
|
|
transform: translateX(100%);
|
|
}
|
|
|
|
.toast-leave-to {
|
|
opacity: 0;
|
|
transform: translateX(100%);
|
|
}
|
|
|
|
.toast-move {
|
|
transition: transform 0.3s ease;
|
|
}
|
|
</style> |