Files
olivv-cs-ComfyUI-FunPack/app/ui/composer/internals/portal.js
T

57 lines
2.4 KiB
JavaScript

// One overlay root, appended to <body>.
//
// Every floating thing lives here rather than beside whatever opened it. An
// overlay nested inside app layout inherits that layout's stacking context, so
// its z-index is measured against its siblings instead of the page -- which is
// how v4 ended up with 99999 and 200000 and still had two things fighting.
let root = null;
export const ROOT_ID = "composer-overlays";
export function portal() {
if (root && root.isConnected) return root;
root = document.getElementById(ROOT_ID);
if (!root) {
root = document.createElement("div");
root.id = ROOT_ID;
// NO POSITIONING, no transform, no filter, no contain, no z-index -- ever.
//
// Each of those makes this element a stacking context, and then the whole
// ladder is measured INSIDE it rather than against the page: every rung,
// from a tooltip at 300 to a modal at 800, collapses to whatever this one
// element resolves to. It said exactly that and then set `position: fixed`,
// which creates a stacking context all by itself -- so a menu at z 500 was
// painted under a button at z 1, and the only reason it was not obvious
// everywhere is that almost nothing in the app claims a z-index at all.
//
// Nothing is needed here anyway: every layer mounted into this root is
// itself `position: fixed`, so a static, zero-sized parent lays nothing out
// and intercepts nothing.
root.style.pointerEvents = "none";
}
if (!root.isConnected) document.body.appendChild(root);
return root;
}
/** Put `node` on the overlay root. Children re-enable their own pointer events. */
export function mount(node) {
node.style.pointerEvents = "auto";
portal().appendChild(node);
return node;
}
export function unmount(node) {
// Both halves matter: with no root yet, an unparented node's parentNode is
// also null, and `null === null` would send removeChild to nothing.
// A field still focused would vanish without its blur, and a value typed there with it: let it commit first.
const active = typeof document !== "undefined" && document.activeElement;
if (node && active && active !== document.body && node.contains(active) && typeof active.blur === "function") active.blur();
if (root && node && node.parentNode === root) root.removeChild(node);
}
export function _resetPortal() {
if (root && root.isConnected) root.remove();
root = null;
}