Coves frontend - a photon fork
at main 83 lines 2.0 kB view raw
1<script lang="ts" module> 2 interface PageScopedError { 3 scope: string 4 message: string 5 } 6 7 let errors = $state<PageScopedError[]>([]) 8 9 export function pushError(args: { scope: string; message: string }) { 10 errors.push(args) 11 } 12 13 export function clearErrorScope(scope: string | null | undefined) { 14 errors = errors.filter((error) => error.scope != scope) 15 } 16</script> 17 18<script lang="ts"> 19 import { onDestroy, type Snippet } from 'svelte' 20 import { ExclamationTriangle, Icon } from 'svelte-hero-icons/dist' 21 import { expoOut } from 'svelte/easing' 22 import { fly, slide } from 'svelte/transition' 23 24 onDestroy(() => { 25 clearErrorScope(scope) 26 }) 27 interface Props { 28 scope?: string | undefined | null 29 message?: string 30 class?: string 31 children?: Snippet 32 } 33 34 let { scope, message, children, class: clazz = '' }: Props = $props() 35 36 let scopedErrors = $derived( 37 errors.filter((e) => e.scope == scope || e.scope == 'global'), 38 ) 39</script> 40 41{#if scopedErrors.length > 0 || message} 42 <div 43 class={['flex flex-col gap-4', clazz]} 44 in:slide={{ duration: 400, easing: expoOut }} 45 out:slide={{ duration: 400, delay: 400, easing: expoOut }} 46 > 47 <div 48 in:fly|global={{ 49 y: -8, 50 duration: 400, 51 delay: 200, 52 opacity: 0, 53 easing: expoOut, 54 }} 55 out:fly={{ y: -8, duration: 400, opacity: 0, easing: expoOut }} 56 class="info-container material-error" 57 > 58 <Icon 59 src={ExclamationTriangle} 60 size="20" 61 micro 62 class="inline-block rounded-lg clear-both float-left mr-2" 63 /> 64 {#if message} 65 {message} 66 {/if} 67 {@render children?.()} 68 {#each scopedErrors as error} 69 <p>{error.message}</p> 70 {/each} 71 </div> 72 </div> 73{/if} 74 75<style> 76 @reference '../../../app.css'; 77 78 .info-container { 79 border-radius: var(--radius-2xl); 80 padding: calc(var(--spacing) * 2.5) calc(var(--spacing) * 3); 81 font-size: var(--text-sm); 82 } 83</style>