diff options
Diffstat (limited to 'src/adapters/portal.ts')
| -rw-r--r-- | src/adapters/portal.ts | 28 |
1 files changed, 28 insertions, 0 deletions
diff --git a/src/adapters/portal.ts b/src/adapters/portal.ts new file mode 100644 index 0000000..afe42e7 --- /dev/null +++ b/src/adapters/portal.ts @@ -0,0 +1,28 @@ +/** + * A Svelte `use:` action that teleports a node to `document.body`, escaping any + * ancestor that establishes a containing block for `position: fixed` (most + * commonly an ancestor with a `transform`, `filter`, `perspective`, or + * `will-change` — e.g. the sidebar's `transform: translateX(...)` container). + * + * Without this, a `position: fixed` modal rendered inside such an ancestor is + * positioned relative to the ANCESTOR, not the viewport (so it only covers the + * sidebar area instead of the full screen). Moving the node to `document.body` + * restores viewport-relative `fixed` positioning. Svelte still owns the node's + * lifecycle (children, bindings, events); we just relocate it + remove it on + * destroy as hygiene. + * + * No-op safely when there is no `document` (SSR / jsdom guards). + */ +export function portal(node: HTMLElement): { destroy(): void } { + if (typeof document === "undefined") { + return { destroy() {} }; + } + document.body.appendChild(node); + return { + destroy() { + if (node.parentNode === document.body) { + document.body.removeChild(node); + } + }, + }; +} |
