adattato html, test htmx con componente svelte
This commit is contained in:
@@ -4,21 +4,24 @@
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
export let title = '';
|
||||
export let open = false;
|
||||
|
||||
let rootEl: HTMLElement;
|
||||
let panelEl: HTMLElement;
|
||||
let hostEl: HTMLElement;
|
||||
|
||||
function isOpen(): boolean {
|
||||
return !!hostEl?.hasAttribute('open');
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
open = false;
|
||||
rootEl?.removeAttribute('open');
|
||||
rootEl?.dispatchEvent(
|
||||
hostEl?.removeAttribute('open');
|
||||
hostEl?.dispatchEvent(
|
||||
new CustomEvent('ui:close', { bubbles: true, composed: true })
|
||||
);
|
||||
}
|
||||
|
||||
function onKeyDown(event: KeyboardEvent) {
|
||||
if (!open) return;
|
||||
if (!isOpen()) return;
|
||||
|
||||
if (event.key === 'Escape') {
|
||||
event.preventDefault();
|
||||
@@ -64,10 +67,10 @@
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
const host = (rootEl.getRootNode() as ShadowRoot).host as HTMLElement;
|
||||
hostEl = (rootEl.getRootNode() as ShadowRoot).host as HTMLElement;
|
||||
|
||||
const observer = new MutationObserver(() => {
|
||||
open = host.hasAttribute('open');
|
||||
if (open) {
|
||||
if (isOpen()) {
|
||||
setTimeout(() => {
|
||||
const autofocus = panelEl?.querySelector<HTMLElement>('[autofocus]');
|
||||
(autofocus || panelEl)?.focus();
|
||||
@@ -75,37 +78,48 @@
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(host, { attributes: true, attributeFilter: ['open'] });
|
||||
observer.observe(hostEl, { attributes: true, attributeFilter: ['open'] });
|
||||
|
||||
if (isOpen()) {
|
||||
setTimeout(() => {
|
||||
(panelEl as HTMLElement | undefined)?.focus();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
return () => observer.disconnect();
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if open}
|
||||
<div class="overlay" on:click={onBackdropClick} on:keydown={onKeyDown} role="presentation">
|
||||
<div class="panel" role="dialog" aria-modal="true" tabindex="-1" bind:this={panelEl}>
|
||||
<header class="header">
|
||||
<h3>{title}</h3>
|
||||
<button type="button" class="close" on:click={closeModal} aria-label="Close">×</button>
|
||||
</header>
|
||||
<section class="body">
|
||||
<slot />
|
||||
</section>
|
||||
</div>
|
||||
<div class="overlay" on:click={onBackdropClick} on:keydown={onKeyDown} role="presentation" bind:this={rootEl}>
|
||||
<div class="panel" role="dialog" aria-modal="true" tabindex="-1" bind:this={panelEl}>
|
||||
<header class="header">
|
||||
<h3>{title}</h3>
|
||||
<button type="button" class="close" on:click={closeModal} aria-label="Close">×</button>
|
||||
</header>
|
||||
<section class="body">
|
||||
<slot />
|
||||
</section>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div bind:this={rootEl} hidden></div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: var(--ui-overlay);
|
||||
display: grid;
|
||||
display: none;
|
||||
place-items: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
:host([open]) .overlay {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.panel {
|
||||
width: min(640px, calc(100vw - 32px));
|
||||
max-height: calc(100vh - 48px);
|
||||
|
||||
Reference in New Issue
Block a user