How does Portal work?
Source code:
export function Portal<T extends boolean = false, S extends boolean = false>(props: {
mount?: Node;
useShadow?: T;
isSVG?: S;
ref?:
| (S extends true ? SVGGElement : HTMLDivElement)
| ((
el: (T extends true ? { readonly shadowRoot: ShadowRoot } : {}) &
(S extends true ? SVGGElement : HTMLDivElement)
) => void);
children: JSX.Element;
}) {
const { useShadow } = props,
marker = document.createTextNode(""),
mount = () => props.mount || document.body,
owner = getOwner();
let content: undefined | (() => JSX.Element);
let hydrating = !!sharedConfig.context;
createEffect(
() => {
// basically we backdoor into a sort of renderEffect here
if (hydrating) (getOwner() as any).user = hydrating = false;
content || (content = runWithOwner(owner, () => createMemo(() => props.children)));
const el = mount();
if (el instanceof HTMLHeadElement) {
const [clean, setClean] = createSignal(false);
const cleanup = () => setClean(true);
createRoot(dispose => insert(el, () => (!clean() ? content!() : dispose()), null));
onCleanup(cleanup);
} else {
const container = createElement(props.isSVG ? "g" : "div", props.isSVG),
renderRoot =
useShadow && container.attachShadow
? container.attachShadow({ mode: "open" })
: container;
Object.defineProperty(container, "_$host", {
get() {
return marker.parentNode;
},
configurable: true
});
insert(renderRoot, content);
el.appendChild(container);
props.ref && (props as any).ref(container);
onCleanup(() => el.removeChild(container));
}
},
undefined,
{ render: !hydrating }
);
return marker;
}export function Portal<T extends boolean = false, S extends boolean = false>(props: {
mount?: Node;
useShadow?: T;
isSVG?: S;
ref?:
| (S extends true ? SVGGElement : HTMLDivElement)
| ((
el: (T extends true ? { readonly shadowRoot: ShadowRoot } : {}) &
(S extends true ? SVGGElement : HTMLDivElement)
) => void);
children: JSX.Element;
}) {
const { useShadow } = props,
marker = document.createTextNode(""),
mount = () => props.mount || document.body,
owner = getOwner();
let content: undefined | (() => JSX.Element);
let hydrating = !!sharedConfig.context;
createEffect(
() => {
// basically we backdoor into a sort of renderEffect here
if (hydrating) (getOwner() as any).user = hydrating = false;
content || (content = runWithOwner(owner, () => createMemo(() => props.children)));
const el = mount();
if (el instanceof HTMLHeadElement) {
const [clean, setClean] = createSignal(false);
const cleanup = () => setClean(true);
createRoot(dispose => insert(el, () => (!clean() ? content!() : dispose()), null));
onCleanup(cleanup);
} else {
const container = createElement(props.isSVG ? "g" : "div", props.isSVG),
renderRoot =
useShadow && container.attachShadow
? container.attachShadow({ mode: "open" })
: container;
Object.defineProperty(container, "_$host", {
get() {
return marker.parentNode;
},
configurable: true
});
insert(renderRoot, content);
el.appendChild(container);
props.ref && (props as any).ref(container);
onCleanup(() => el.removeChild(container));
}
},
undefined,
{ render: !hydrating }
);
return marker;
}