fix: dropdown menu fixes

This commit is contained in:
Maya 2026-02-19 20:08:14 +03:00
parent e9db7a9568
commit e61214619b
No known key found for this signature in database
1 changed files with 65 additions and 32 deletions

View File

@ -1,7 +1,6 @@
<script lang="ts">
import { duration, fade, transition } from "$lib/util/animation";
import { ChevronDown } from "lucide-svelte";
import { onMount } from "svelte";
import { quintOut } from "svelte/easing";
type Props = {
@ -26,10 +25,10 @@
let hover = $state(false);
let isUp = $state(false);
let dropdown = $state<HTMLDivElement>();
const toggle = () => {
open = !open;
};
let menuElement = $state<HTMLDivElement>();
let button = $state<HTMLButtonElement>();
let clickHandler: ((e: MouseEvent) => void) | null = null;
let resizeHandler: (() => void) | null = null;
const getValue = (option: string | { value: string; label: string }) =>
typeof option === "string" ? option : option.value;
@ -49,15 +48,48 @@
toggle();
};
onMount(() => {
const click = (e: MouseEvent) => {
if (dropdown && !dropdown.contains(e.target as Node)) {
open = false;
}
const toggle = () => {
open = !open;
};
const updateMenuPosition = () => {
if (open && menuElement && button) {
const rect = button.getBoundingClientRect();
menuElement.style.top = `${rect.bottom + 4}px`;
menuElement.style.left = `${rect.left}px`;
menuElement.style.width = `${rect.width}px`;
}
};
// outside clicks
$effect(() => {
clickHandler = (e: MouseEvent) => {
if (dropdown && !dropdown.contains(e.target as Node)) open = false;
};
window.addEventListener("click", click);
return () => window.removeEventListener("click", click);
window.addEventListener("click", clickHandler);
return () => {
if (clickHandler) window.removeEventListener("click", clickHandler);
};
});
// dropdown menu positioning
$effect(() => {
if (open && menuElement && button) {
resizeHandler = updateMenuPosition;
window.addEventListener("resize", resizeHandler);
document.body.appendChild(menuElement);
menuElement.style.position = "fixed";
updateMenuPosition();
return () => {
if (resizeHandler)
window.removeEventListener("resize", resizeHandler);
if (menuElement?.parentNode === document.body)
document.body.removeChild(menuElement);
};
}
});
</script>
@ -68,6 +100,7 @@
bind:this={dropdown}
>
<button
bind:this={button}
class="font-display w-full {settingsStyle
? 'justify-between'
: 'justify-center'} overflow-hidden relative cursor-pointer {settingsStyle
@ -82,7 +115,6 @@
onmouseleave={() => (hover = false)}
{disabled}
>
<!-- <p>{selected}</p> -->
<div class="grid grid-cols-1 grid-rows-1 w-fit flex-grow-0">
{#key selected}
<p
@ -121,23 +153,24 @@
: 0}deg); transition: transform {duration}ms {transition};"
/>
</button>
{#if open}
<div
style={hover ? "will-change: opacity, fade, transform" : ""}
transition:fade={{
duration,
easing: quintOut,
}}
class="w-full shadow-xl bg-panel-alt shadow-black/25 absolute overflow-hidden top-full mt-1 left-0 z-50 bg-background rounded-xl max-h-[30vh] overflow-y-auto"
>
{#each options as option}
<button
class="w-full p-2 px-4 text-left hover:bg-panel"
onclick={() => select(option)}
>
{getLabel(option)}
</button>
{/each}
</div>
{/if}
</div>
{#if open}
<div
bind:this={menuElement}
transition:fade={{
duration,
easing: quintOut,
}}
class="shadow-xl bg-panel-alt shadow-black/25 overflow-hidden z-[9999] bg-background rounded-xl max-h-[23.5vh] overflow-y-auto"
>
{#each options as option}
<button
class="w-full p-2 px-4 text-left hover:bg-panel font-normal text-sm text-muted"
onclick={() => select(option)}
>
{getLabel(option)}
</button>
{/each}
</div>
{/if}