mirror of
https://github.com/BradNut/boredgame
synced 2025-09-08 17:40:22 +00:00
Add toast to app.
This commit is contained in:
parent
e84f212772
commit
c159270dbd
5 changed files with 153 additions and 0 deletions
29
src/lib/Portal.svelte
Normal file
29
src/lib/Portal.svelte
Normal 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>
|
||||
47
src/lib/toast/Toast.svelte
Normal file
47
src/lib/toast/Toast.svelte
Normal 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>
|
||||
35
src/lib/toast/ToastMessage.svelte
Normal file
35
src/lib/toast/ToastMessage.svelte
Normal 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
29
src/lib/toast/toast.ts
Normal 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();
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue