an atproto based link aggregator
at main 67 lines 1.7 kB view raw
1<script lang="ts"> 2 interface Props { 3 open: boolean; 4 onclose: () => void; 5 title?: string; 6 } 7 8 let { open, onclose, title, children }: Props & { children: import('svelte').Snippet } = $props(); 9 10 function handleKeydown(e: KeyboardEvent) { 11 if (e.key === 'Escape') { 12 onclose(); 13 } 14 } 15 16 function handleBackdropClick(e: MouseEvent) { 17 if (e.target === e.currentTarget) { 18 onclose(); 19 } 20 } 21</script> 22 23<svelte:window onkeydown={handleKeydown} /> 24 25{#if open} 26 <!-- svelte-ignore a11y_click_events_have_key_events --> 27 <!-- svelte-ignore a11y_no_static_element_interactions --> 28 <div 29 class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4" 30 onclick={handleBackdropClick} 31 > 32 <div 33 class="bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-md w-full max-h-[90vh] overflow-y-auto" 34 role="dialog" 35 aria-modal="true" 36 aria-labelledby={title ? 'modal-title' : undefined} 37 > 38 {#if title} 39 <div 40 class="flex items-center justify-between px-4 py-3 border-b border-gray-200 dark:border-gray-700" 41 > 42 <h2 id="modal-title" class="text-lg font-semibold text-gray-900 dark:text-gray-100"> 43 {title} 44 </h2> 45 <button 46 type="button" 47 onclick={onclose} 48 class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300" 49 aria-label="Close" 50 > 51 <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> 52 <path 53 stroke-linecap="round" 54 stroke-linejoin="round" 55 stroke-width="2" 56 d="M6 18L18 6M6 6l12 12" 57 /> 58 </svg> 59 </button> 60 </div> 61 {/if} 62 <div class="p-4"> 63 {@render children()} 64 </div> 65 </div> 66 </div> 67{/if}