161 lines
3.1 KiB
Svelte
161 lines
3.1 KiB
Svelte
<script lang="ts">
|
|
import { setContext } from 'svelte';
|
|
import { writable } from 'svelte/store';
|
|
import SvelteMarkdown from 'svelte-markdown'
|
|
type Notif = {
|
|
title: string;
|
|
description: string;
|
|
type: 'alert' | 'info' | 'success' | 'error';
|
|
};
|
|
type Notification = {
|
|
title: string;
|
|
description: string;
|
|
type: 'alert' | 'info' | 'success' | 'error';
|
|
id: number;
|
|
deleted: boolean;
|
|
};
|
|
|
|
const notifications = writable<Notification[]>([]);
|
|
|
|
const getId = () => {
|
|
return Math.round(Date.now() * Math.random());
|
|
};
|
|
|
|
const toast = (notif: Notif) => {
|
|
let id = getId();
|
|
notifications.update((n) => {
|
|
return [...n, { ...notif, id, deleted: false }];
|
|
});
|
|
setTimeout(() => {
|
|
notifications.update((n) => {
|
|
return n.map((o) => {
|
|
if (o.id == id) {
|
|
return { ...o, deleted: true };
|
|
}
|
|
return o;
|
|
});
|
|
});
|
|
setTimeout(() => {
|
|
notifications.update((n) => {
|
|
return n.filter((o) => o.id != id);
|
|
});
|
|
}, 500);
|
|
}, 3000);
|
|
};
|
|
|
|
const alert = (title: string, description: string) => {
|
|
toast({ title, description, type: 'alert' });
|
|
};
|
|
const info = (title: string, description: string) => {
|
|
toast({ title, description, type: 'info' });
|
|
};
|
|
const success = (title: string, description: string) => {
|
|
toast({ title, description, type: 'success' });
|
|
};
|
|
const error = (title: string, description: string) => {
|
|
toast({ title, description, type: 'error' });
|
|
};
|
|
setContext('notif', { toast, alert, info, success, error });
|
|
</script>
|
|
|
|
<slot />
|
|
|
|
<div class="notifs" class:empty ={$notifications.length == 0}>
|
|
{#each $notifications as n}
|
|
<div
|
|
on:click={() => {
|
|
n.deleted = true;
|
|
setTimeout(() => {
|
|
notifications.update((o) => o.filter((i) => i.id != n.id));
|
|
}, 500);
|
|
}}
|
|
class={n.type}
|
|
on:keydown={() => {}}
|
|
class:deleted={n.deleted}
|
|
>
|
|
<h1>{n.title}</h1>
|
|
<p><SvelteMarkdown source={n.description} /></p>
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
|
|
<style lang="scss">
|
|
.notifs {
|
|
position: absolute;
|
|
right: 0;
|
|
top: 0;
|
|
padding: 30px;
|
|
gap: 10px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
width: 500px;
|
|
z-index: 1000;
|
|
|
|
div {
|
|
background-color: $background;
|
|
z-index: 1000;
|
|
padding: 10px;
|
|
cursor: pointer;
|
|
position: relative;
|
|
overflow: hidden;
|
|
word-wrap: break-word;
|
|
h1 {
|
|
font-size: 1.1em;
|
|
}
|
|
p {
|
|
font-size: 0.9em;
|
|
}
|
|
|
|
&::before,
|
|
&::after {
|
|
content: '';
|
|
display: block;
|
|
height: 3px;
|
|
position: absolute;
|
|
bottom: 0;
|
|
right: 0;
|
|
left: 0;
|
|
}
|
|
&::before {
|
|
z-index: 3;
|
|
background-color: $background-light;
|
|
transition: 3s;
|
|
width: 0%;
|
|
animation: slide 3s forwards ease-in-out;
|
|
}
|
|
&::after {
|
|
width: 100%;
|
|
|
|
z-index: 2;
|
|
}
|
|
&.deleted {
|
|
transition: 0.5s;
|
|
opacity: 0;
|
|
}
|
|
}
|
|
}
|
|
.empty{
|
|
z-index: -20;
|
|
}
|
|
.alert::after{
|
|
background-color: $orange;
|
|
}
|
|
.info::after{
|
|
background-color: $bleu;
|
|
}
|
|
.error::after{
|
|
background-color: $red;
|
|
}
|
|
.success::after{
|
|
background-color: $green;
|
|
}
|
|
@keyframes slide {
|
|
from {
|
|
width: 0%;
|
|
}
|
|
to {
|
|
width: 100%;
|
|
}
|
|
}
|
|
</style>
|