blob: afe42e7480ad86102363287b89ea1766ce4bc657 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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);
}
},
};
}
|