GraphModal.svelte
1 <script lang="ts"> 2 import { onMount } from "svelte"; 3 4 interface Node { 5 id: string; 6 title: string; 7 category: string; 8 val: number; 9 } 10 11 interface Link { 12 source: string; 13 target: string; 14 } 15 16 interface Props { 17 nodes: Node[]; 18 links: Link[]; 19 } 20 21 let { nodes, links }: Props = $props(); 22 23 let isOpen = $state(false); 24 let modalRef: HTMLDivElement | undefined = $state(undefined); 25 26 export function open() { 27 isOpen = true; 28 document.body.style.overflow = "hidden"; 29 } 30 31 export function close() { 32 isOpen = false; 33 document.body.style.overflow = ""; 34 } 35 36 function handleBackdropClick(e: MouseEvent) { 37 if (e.target === modalRef) { 38 close(); 39 } 40 } 41 42 function handleKeydown(e: KeyboardEvent) { 43 if (e.key === "Escape" && isOpen) { 44 close(); 45 } 46 } 47 48 onMount(() => { 49 document.addEventListener("keydown", handleKeydown); 50 return () => { 51 document.removeEventListener("keydown", handleKeydown); 52 }; 53 }); 54 </script> 55 56 {#if isOpen} 57 <!-- svelte-ignore a11y_click_events_have_key_events --> 58 <!-- svelte-ignore a11y_no_static_element_interactions --> 59 <div 60 bind:this={modalRef} 61 onclick={handleBackdropClick} 62 class="fixed inset-0 z-50 bg-pink-950/30 backdrop-blur-sm flex items-center justify-center p-4" 63 > 64 <div 65 class="w-full max-w-5xl h-[80vh] bg-white/95 rounded-2xl border border-dashed border-pink-200 shadow-2xl flex flex-col overflow-hidden" 66 role="dialog" 67 aria-modal="true" 68 aria-labelledby="graph-modal-title" 69 > 70 <!-- Header --> 71 <div class="flex items-center justify-between px-6 py-4 border-b border-dashed border-pink-200"> 72 <h2 id="graph-modal-title" class="font-display text-xl font-bold text-pink-950"> 73 Notes Graph 74 </h2> 75 <button 76 onclick={close} 77 class="p-2 rounded-lg text-pink-950/60 hover:text-pink-950 hover:bg-pink-100/50 transition-colors" 78 aria-label="Close graph" 79 > 80 <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"> 81 <path 82 stroke-linecap="round" 83 stroke-linejoin="round" 84 stroke-width="2" 85 d="M6 18L18 6M6 6l12 12" 86 /> 87 </svg> 88 </button> 89 </div> 90 91 <!-- Graph Container --> 92 <div class="flex-1 relative"> 93 {#await import('./GraphView.svelte') then { default: GraphView }} 94 <GraphView {nodes} {links} client:visible /> 95 {/await} 96 </div> 97 </div> 98 </div> 99 {/if}