this repo has no description
at main 3.8 kB view raw
1<script lang="ts"> 2 import { getToasts, dismissToast, type Toast } from '../lib/toast.svelte' 3 4 const toasts = $derived(getToasts()) 5 6 function handleDismiss(id: number) { 7 dismissToast(id) 8 } 9 10 function getIcon(type: Toast['type']): string { 11 switch (type) { 12 case 'success': 13 return '✓' 14 case 'error': 15 return '!' 16 case 'warning': 17 return '⚠' 18 case 'info': 19 return 'i' 20 } 21 } 22</script> 23 24{#if toasts.length > 0} 25 <div class="toast-container" role="region" aria-label="Notifications"> 26 {#each toasts as toast (toast.id)} 27 <div 28 class="toast toast-{toast.type}" 29 class:dismissing={toast.dismissing} 30 role="alert" 31 aria-live="polite" 32 > 33 <span class="toast-icon">{getIcon(toast.type)}</span> 34 <span class="toast-message">{toast.message}</span> 35 <button 36 type="button" 37 class="toast-dismiss" 38 onclick={() => handleDismiss(toast.id)} 39 aria-label="Dismiss notification" 40 > 41 x 42 </button> 43 </div> 44 {/each} 45 </div> 46{/if} 47 48<style> 49 .toast-container { 50 position: fixed; 51 top: var(--space-6); 52 right: var(--space-6); 53 z-index: 9999; 54 display: flex; 55 flex-direction: column; 56 gap: var(--space-3); 57 max-width: min(400px, calc(100vw - var(--space-12))); 58 pointer-events: none; 59 } 60 61 .toast { 62 display: flex; 63 align-items: flex-start; 64 gap: var(--space-3); 65 padding: var(--space-4); 66 border-radius: var(--radius-lg); 67 box-shadow: var(--shadow-lg); 68 pointer-events: auto; 69 animation: toast-in 0.1s ease-out; 70 } 71 72 .toast.dismissing { 73 animation: toast-out 0.15s ease-in forwards; 74 } 75 76 @keyframes toast-in { 77 from { 78 opacity: 0; 79 transform: scale(0.95); 80 } 81 to { 82 opacity: 1; 83 transform: scale(1); 84 } 85 } 86 87 @keyframes toast-out { 88 from { 89 opacity: 1; 90 transform: scale(1); 91 } 92 to { 93 opacity: 0; 94 transform: scale(0.95); 95 } 96 } 97 98 .toast-success { 99 background: var(--success-bg); 100 border: 1px solid var(--success-border); 101 color: var(--success-text); 102 } 103 104 .toast-error { 105 background: var(--error-bg); 106 border: 1px solid var(--error-border); 107 color: var(--error-text); 108 } 109 110 .toast-warning { 111 background: var(--warning-bg); 112 border: 1px solid var(--warning-border); 113 color: var(--warning-text); 114 } 115 116 .toast-info { 117 background: var(--accent-muted); 118 border: 1px solid var(--accent); 119 color: var(--text-primary); 120 } 121 122 .toast-icon { 123 flex-shrink: 0; 124 width: 20px; 125 height: 20px; 126 display: flex; 127 align-items: center; 128 justify-content: center; 129 border-radius: 50%; 130 font-size: var(--text-xs); 131 font-weight: var(--font-bold); 132 } 133 134 .toast-success .toast-icon { 135 background: var(--success-text); 136 color: var(--success-bg); 137 } 138 139 .toast-error .toast-icon { 140 background: var(--error-text); 141 color: var(--error-bg); 142 } 143 144 .toast-warning .toast-icon { 145 background: var(--warning-text); 146 color: var(--warning-bg); 147 } 148 149 .toast-info .toast-icon { 150 background: var(--accent); 151 color: var(--bg-card); 152 } 153 154 .toast-message { 155 flex: 1; 156 font-size: var(--text-sm); 157 line-height: 1.4; 158 } 159 160 .toast-dismiss { 161 flex-shrink: 0; 162 width: 20px; 163 height: 20px; 164 padding: 0; 165 border: none; 166 background: transparent; 167 cursor: pointer; 168 opacity: 0.6; 169 font-size: var(--text-sm); 170 line-height: 1; 171 color: inherit; 172 border-radius: var(--radius-sm); 173 } 174 175 .toast-dismiss:hover { 176 opacity: 1; 177 background: rgba(0, 0, 0, 0.1); 178 } 179 180 @media (max-width: 480px) { 181 .toast-container { 182 top: var(--space-4); 183 right: var(--space-4); 184 left: var(--space-4); 185 max-width: none; 186 } 187 } 188</style>