This commit is contained in:
Timothy Jaeryang Baek committed 2026-07-27 03:59:12 -04:00
1 parent a9a3e5b95c
commit e8fda1c7a0
1 file changed
+50 -26
+50 -26
View File
@@ -31,6 +31,8 @@
let triggerEl: HTMLElement | null = null; let triggerEl: HTMLElement | null = null;
let contentEl: HTMLElement | null = null; let contentEl: HTMLElement | null = null;
let previouslyFocused: HTMLElement | null = null;
let shouldFocusContent = false;
let positionFrame: number | undefined; let positionFrame: number | undefined;
let settleTimers: number[] = []; let settleTimers: number[] = [];
let resolvedMaxHeight = maxHeight; let resolvedMaxHeight = maxHeight;
@@ -226,31 +228,57 @@
} }
} }
async function toggleOpen() { async function afterOpen() {
show = !show; await tick();
onOpenChange(show); positionContent();
// Re-check after transition renders real dimensions
if (visualViewportAware) {
scheduleSettledPositionUpdates();
} else {
setTimeout(positionContent, 50);
}
if (shouldFocusContent) {
shouldFocusContent = false;
contentEl?.focus();
}
}
function openDropdown(focusContent = false) {
if (show) return;
if (focusContent) {
previouslyFocused =
document.activeElement instanceof HTMLElement ? document.activeElement : null;
shouldFocusContent = true;
}
show = true;
onOpenChange(true);
}
function closeDropdown(restoreFocus = !!contentEl?.contains(document.activeElement)) {
if (!show) return;
show = false;
onOpenChange(false);
shouldFocusContent = false;
if (restoreFocus && previouslyFocused?.isConnected) {
previouslyFocused.focus();
}
previouslyFocused = null;
}
function toggleOpen() {
if (show) { if (show) {
await tick(); closeDropdown();
positionContent(); } else {
// Re-check after transition renders real dimensions openDropdown(true);
if (visualViewportAware) {
scheduleSettledPositionUpdates();
} else {
setTimeout(positionContent, 50);
}
} }
} }
// React to external show changes (e.g. bind:show toggled by parent component) // React to external show changes (e.g. bind:show toggled by parent component)
$: if (show) { $: if (show) {
tick().then(() => { afterOpen();
positionContent();
if (visualViewportAware) {
scheduleSettledPositionUpdates();
} else {
setTimeout(positionContent, 50);
}
});
} }
function handleWindowPointerDown(event: PointerEvent) { function handleWindowPointerDown(event: PointerEvent) {
@@ -258,21 +286,18 @@
if (!(event.target instanceof Node)) return; if (!(event.target instanceof Node)) return;
if (triggerEl?.contains(event.target)) return; if (triggerEl?.contains(event.target)) return;
if (contentEl?.contains(event.target)) return; if (contentEl?.contains(event.target)) return;
show = false; closeDropdown(false);
onOpenChange(false);
} }
function handleKeydown(event: KeyboardEvent) { function handleKeydown(event: KeyboardEvent) {
if (event.key === 'Escape' && show) { if (event.key === 'Escape' && show) {
show = false; closeDropdown();
onOpenChange(false);
} }
} }
/** Close the dropdown programmatically */ /** Close the dropdown programmatically */
export function close() { export function close() {
show = false; closeDropdown();
onOpenChange(false);
} }
import { onMount, onDestroy } from 'svelte'; import { onMount, onDestroy } from 'svelte';
@@ -317,7 +342,6 @@
{#if show} {#if show}
<!-- svelte-ignore a11y-click-events-have-key-events --> <!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div <div
use:portal use:portal
bind:this={contentEl} bind:this={contentEl}