forgot to commit the portal

This commit is contained in:
2026-02-06 04:15:34 -08:00
parent 6aac19870a
commit 2bb2ba2ea5

View File

@@ -0,0 +1,84 @@
<script lang="ts">
import { browser } from "$app/environment";
type PortalProps = {
/**
* Content to render into the portal target.
*/
children: import("svelte").Snippet;
/**
* Optional CSS selector for the portal target element.
* Defaults to `document.body`.
*/
targetSelector?: string;
/**
* Optional tag name used for the created mount node.
* Defaults to `div`.
*/
tagName?: keyof HTMLElementTagNameMap;
/**
* Optional class applied to the created mount node.
*/
class?: string;
/**
* Optional id applied to the created mount node.
*/
id?: string;
};
let {
children,
targetSelector,
tagName = "div",
class: className,
id,
}: PortalProps = $props();
function createMountNode() {
const el = document.createElement(tagName);
if (className) el.className = className;
if (id) el.id = id;
return el;
}
function resolveTarget(): Element | null {
if (!browser) return null;
if (targetSelector) return document.querySelector(targetSelector);
return document.body;
}
/**
* Attachment factory: creates a mount node and appends it to the target.
* MUST return either:
* - void, or
* - a cleanup function, or
* - an object with a destroy() method
*
* The TS types in this project expect the cleanup-function form.
*/
function portal(node: HTMLElement) {
const target = resolveTarget();
if (!target) return;
const mount = createMountNode();
target.appendChild(mount);
// Move the node into the mount (portaling)
mount.appendChild(node);
return () => {
// Remove mount (also removes `node` from DOM)
mount.remove();
};
}
</script>
{#if browser}
<div {@attach portal}>
{@render children()}
</div>
{/if}