Add toast to app.

This commit is contained in:
Bradley Shellnut 2022-05-05 23:51:20 -07:00
parent e84f212772
commit c159270dbd
5 changed files with 153 additions and 0 deletions

29
src/lib/Portal.svelte Normal file
View file

@ -0,0 +1,29 @@
<script lang="ts">
function portal(node: HTMLElement) {
let target;
function update() {
target = document.querySelector('body');
if (target) target.appendChild(node);
node.hidden = false;
}
function destroy() {
if (node.parentNode) {
// Child will tell the parent to remove itself
node.parentNode.removeChild(node);
}
}
update();
return {
update,
destroy
};
}
</script>
<div use:portal hidden>
<slot />
</div>

View file

@ -0,0 +1,47 @@
<script lang="ts">
import { fly, fade } from 'svelte/transition';
import { flip } from 'svelte/animate';
import Portal from '../Portal.svelte';
import ToastMessage from './ToastMessage.svelte';
import { toast } from './toast';
</script>
<Portal>
<div class="toast-wrapper">
{#each $toast as message (message.id)}
<div
on:click={() => toast.remove(message.id)}
in:fly={{ opacity: 0, x: 100 }}
out:fade
animate:flip
class={`toast ${message.type.toLowerCase()}`}
>
<ToastMessage {message} />
</div>
{/each}
</div>
</Portal>
<style>
.toast-wrapper {
position: fixed;
bottom: 0;
right: 20px;
}
.toast {
overflow: hidden;
position: relative;
margin-bottom: 1rem;
color: white;
background: var(--toast-background, #625df5);
padding: 20px;
border-radius: 15px;
box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.3);
}
.toast.error {
background: var(--toast-error-background, #e54b4b);
color: black;
}
</style>

View file

@ -0,0 +1,35 @@
<script lang="ts">
import type { ToastData } from '$lib/types';
import { onMount } from 'svelte';
import { tweened } from 'svelte/motion';
import { toast } from './toast';
export let toastData: ToastData;
let progress = tweened(100, { duration: toastData.duration });
onMount(async () => {
await progress.set(0);
toast.remove(toastData.id);
});
</script>
<div class="progress" style={`width: ${$progress}%;`} />
<p>{toastData.message}</p>
<style>
.progress {
height: 8px;
background: white;
opacity: 0.3;
position: absolute;
top: 0;
left: 0;
right: 0;
}
p {
margin: 0;
}
</style>

29
src/lib/toast/toast.ts Normal file
View file

@ -0,0 +1,29 @@
import type { ToastData } from '$lib/types';
import { writable } from 'svelte/store';
// Custom store
const newToast = () => {
const { subscribe, update } = writable([]);
function send(message: string, { duration = 2000, type = 'INFO' } = {}) {
const id = Math.floor(Math.random() * 1000);
const newMessage = {
id,
duration,
type,
message
};
update((store) => [...store, newMessage]);
}
function remove(id: number) {
update((store) => {
const newStore = store.filter((item: ToastData) => item.id !== id);
return [...newStore];
});
}
return { subscribe, send, remove };
};
export const toast = newToast();

View file

@ -1,3 +1,16 @@
export enum ToastType {
INFO,
ERROR,
WARNING
}
export type ToastData = {
id: number;
duration: number;
type: ToastType;
message: string;
}
export type GameType = {
id: string;
handle: string;