forgot to commit the portal
This commit is contained in:
84
src/lib/components/util/Portal.svelte
Normal file
84
src/lib/components/util/Portal.svelte
Normal 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}
|
||||
Reference in New Issue
Block a user