ptitlutins/app/components/organisms/Notifications.vue
2026-06-10 23:37:21 +02:00

96 lines
1.9 KiB
Vue

<template>
<div class="notifications" aria-live="polite">
<TransitionGroup name="toast">
<div
v-for="n in store.items"
:key="n.id"
:class="['toast', `toast--${n.type}`]"
role="status"
>
<Icon
:name="
n.type === 'success' ? 'carbon:checkmark-filled' : 'carbon:warning-filled'
"
size="1.1rem"
class="toast__icon"
/>
<span class="toast__message">{{ n.message }}</span>
<IconButton
icon="carbon:close"
:label="$t('common.dismiss')"
variant="ghost"
size="sm"
@click="store.dismiss(n.id)"
/>
</div>
</TransitionGroup>
</div>
</template>
<script setup lang="ts">
const store = useNotificationsStore()
</script>
<style scoped>
.notifications {
position: fixed;
top: 5rem;
right: var(--space-4);
z-index: 50;
display: flex;
flex-direction: column;
gap: var(--space-2);
max-width: 360px;
}
.toast {
display: flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-3);
background: var(--surface-card);
border: 1px solid var(--border-default);
/* Colour is doubled by the icon + message, never the sole signal. */
border-left-width: 3px;
border-radius: var(--radius-lg);
box-shadow: var(--shadow-3);
}
.toast--success {
border-left-color: var(--success);
}
.toast--success .toast__icon {
color: var(--success);
}
.toast--error {
border-left-color: var(--danger);
}
.toast--error .toast__icon {
color: var(--danger);
}
.toast__icon {
flex-shrink: 0;
}
.toast__message {
flex: 1;
min-width: 0;
font-family: var(--font-body);
font-size: var(--text-sm);
color: var(--text-body);
}
.toast-enter-active,
.toast-leave-active {
transition:
opacity 200ms ease,
transform 200ms ease;
}
.toast-enter-from,
.toast-leave-to {
opacity: 0;
transform: translateX(16px);
}
</style>