dropdown-nested.svelte (1878B)
1 <script lang="ts"> 2 import { fmt_cl } from "@radroots/apps-lib"; 3 import { onDestroy, onMount, type Snippet } from "svelte"; 4 5 let { 6 basis, 7 primary, 8 dropdown, 9 }: { 10 basis?: { 11 classes_primary?: string; 12 classes_dropdown?: string; 13 }; 14 primary: Snippet; 15 dropdown: Snippet; 16 } = $props(); 17 18 let el_container: HTMLDetailsElement | null = $state(null); 19 let el_dropdown: HTMLElement | null = $state(null); 20 21 let shift_x = $state(0); 22 23 $effect(() => { 24 const rect_primary = el_container?.getBoundingClientRect(); 25 const rect_dropdown = el_dropdown?.getBoundingClientRect(); 26 if (rect_primary && rect_dropdown) 27 shift_x = rect_dropdown.width - rect_primary.width; 28 }); 29 30 const handle_click = (event: MouseEvent) => { 31 if (!el_container || !el_dropdown) return; 32 if ( 33 !el_container.contains(event.target as Node) || 34 el_dropdown.contains(event.target as Node) 35 ) 36 el_container.open = false; 37 }; 38 39 onMount(() => { 40 document.addEventListener("click", handle_click); 41 }); 42 43 onDestroy(() => { 44 document.removeEventListener("click", handle_click); 45 }); 46 </script> 47 48 <details bind:this={el_container} class="dropdown dropdown-bottom"> 49 <summary class={`${fmt_cl(basis?.classes_primary)} flex`}> 50 {@render primary()} 51 </summary> 52 <ul 53 bind:this={el_dropdown} 54 class={`dropdown-content flex flex-col pt-1`} 55 style={`transform: translateX(-${shift_x}px);`} 56 > 57 <ul 58 class={`z-50 ${fmt_cl( 59 basis?.classes_dropdown || `menu min-w-52 p-2 bg-white shadow`, 60 )} flex flex-col w-full justify-center items-center`} 61 > 62 {@render dropdown()} 63 </ul> 64 </ul> 65 </details>