You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

112 lines
2.6 KiB
Svelte

<script lang="ts">
import { tick, onDestroy } from 'svelte';
import { createEventDispatcher } from 'svelte';
export let open: boolean = false;
export let ariaLabel: string = 'Diálogo';
export let id: string | undefined;
const dispatch = createEventDispatcher();
let panelEl: HTMLElement | null = null;
let lastActive: Element | null = null;
function close() {
open = false;
dispatch('closed');
}
function handleKeydown(e: KeyboardEvent) {
if (e.key === 'Escape') {
e.preventDefault();
close();
} else if (e.key === 'Tab' && panelEl) {
const focusables = Array.from(
panelEl.querySelectorAll<HTMLElement>(
'a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])'
)
).filter((el) => el.offsetParent !== null);
if (focusables.length === 0) {
e.preventDefault();
return;
}
const first = focusables[0];
const last = focusables[focusables.length - 1];
const active = document.activeElement as HTMLElement | null;
if (e.shiftKey) {
if (active === first || !panelEl.contains(active)) {
e.preventDefault();
last.focus();
}
} else {
if (active === last) {
e.preventDefault();
first.focus();
}
}
}
}
$: if (open) {
lastActive = document.activeElement;
tick().then(() => {
panelEl?.focus();
document.body.style.overflow = 'hidden';
});
} else {
document.body.style.overflow = '';
if (lastActive instanceof HTMLElement) {
tick().then(() => lastActive?.focus());
}
}
onDestroy(() => {
document.body.style.overflow = '';
});
</script>
{#if open}
<div class="popover-overlay" on:click={close} />
<div
class="popover-panel"
role="dialog"
aria-modal="true"
{id}
aria-label={ariaLabel}
tabindex="-1"
bind:this={panelEl}
on:keydown={handleKeydown}
>
<slot />
</div>
{/if}
<style>
.popover-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.35);
z-index: 1000;
}
.popover-panel {
position: fixed;
z-index: 1001;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
max-width: min(420px, 92vw);
width: 92vw;
background: var(--color-surface);
color: var(--color-text);
border: 1px solid var(--color-border);
border-radius: 10px;
box-shadow: var(--shadow-lg);
padding: 12px;
outline: none;
}
@media (prefers-color-scheme: dark) {
.popover-overlay {
background: rgba(0, 0, 0, 0.5);
}
}
</style>