@motion-core/motion-gpu
Version:
Framework-agnostic WebGPU runtime for fullscreen WGSL shaders with explicit Svelte, React, and Vue adapter entrypoints.
130 lines (129 loc) • 4.92 kB
JavaScript
//#region src/lib/core/error-overlay-stack.ts
var stacks = /* @__PURE__ */ new WeakMap();
function getTopEntry(stack) {
return stack.entries[stack.entries.length - 1];
}
function getActiveHTMLElement(document) {
const HTMLElementConstructor = document.defaultView?.HTMLElement;
const activeElement = document.activeElement;
return HTMLElementConstructor && activeElement instanceof HTMLElementConstructor ? activeElement : null;
}
function getBodyChildContaining(document, element) {
let current = element;
while (current?.parentElement && current.parentElement !== document.body) current = current.parentElement;
return current?.parentElement === document.body ? current : null;
}
function restoreBackgroundElement(stack, element) {
const wasInert = stack.backgroundElements.get(element);
if (wasInert === void 0) return;
element.inert = wasInert;
stack.backgroundElements.delete(element);
}
function reconcileBackground(stack) {
const topEntry = getTopEntry(stack);
const activeBodyChild = topEntry ? getBodyChildContaining(stack.document, topEntry.portalRoot) : null;
const nextBackgroundElements = /* @__PURE__ */ new Set();
const HTMLElementConstructor = stack.document.defaultView?.HTMLElement;
if (topEntry && HTMLElementConstructor) for (const child of stack.document.body.children) {
if (!(child instanceof HTMLElementConstructor) || child === activeBodyChild) continue;
nextBackgroundElements.add(child);
}
for (const element of stack.backgroundElements.keys()) if (!nextBackgroundElements.has(element)) restoreBackgroundElement(stack, element);
for (const element of nextBackgroundElements) {
if (!stack.backgroundElements.has(element)) stack.backgroundElements.set(element, element.inert);
element.inert = true;
}
}
function focusTopEntry(stack) {
const topEntry = getTopEntry(stack);
if (!topEntry?.dialog.isConnected) return;
topEntry.dialog.focus({ preventScroll: true });
}
function createStack(document) {
const stack = {
document,
entries: [],
backgroundElements: /* @__PURE__ */ new Map(),
baselineFocus: getActiveHTMLElement(document),
focusInListener: () => void 0,
keydownListener: () => void 0,
observer: null
};
stack.focusInListener = (event) => {
const topEntry = getTopEntry(stack);
const target = event.target;
if (!topEntry || !target || topEntry.dialog.contains(target)) return;
focusTopEntry(stack);
};
stack.keydownListener = (event) => {
if (event.key !== "Escape" || event.defaultPrevented) return;
const topEntry = getTopEntry(stack);
if (!topEntry || topEntry.dismissRequested) return;
event.preventDefault();
topEntry.dismissRequested = true;
try {
topEntry.onDismiss();
} catch (error) {
topEntry.dismissRequested = false;
throw error;
}
};
document.addEventListener("focusin", stack.focusInListener);
document.addEventListener("keydown", stack.keydownListener);
const MutationObserverConstructor = document.defaultView?.MutationObserver;
if (MutationObserverConstructor) {
stack.observer = new MutationObserverConstructor(() => {
reconcileBackground(stack);
});
stack.observer.observe(document.body, { childList: true });
}
stacks.set(document, stack);
return stack;
}
function destroyStack(stack) {
stack.document.removeEventListener("focusin", stack.focusInListener);
stack.document.removeEventListener("keydown", stack.keydownListener);
stack.observer?.disconnect();
for (const element of Array.from(stack.backgroundElements.keys())) restoreBackgroundElement(stack, element);
const restoreTarget = stack.baselineFocus;
stacks.delete(stack.document);
if (restoreTarget?.isConnected) restoreTarget.focus({ preventScroll: true });
}
function registerErrorOverlay({ dialog, portalRoot, onDismiss }) {
const document = dialog.ownerDocument;
const stack = stacks.get(document) ?? createStack(document);
const entry = {
dialog,
portalRoot,
onDismiss,
dismissRequested: false
};
stack.entries.push(entry);
const activeBodyChild = getBodyChildContaining(document, portalRoot);
if (activeBodyChild) restoreBackgroundElement(stack, activeBodyChild);
focusTopEntry(stack);
reconcileBackground(stack);
let registered = true;
return () => {
if (!registered) return;
registered = false;
const entryIndex = stack.entries.indexOf(entry);
if (entryIndex === -1) return;
const wasTopEntry = entryIndex === stack.entries.length - 1;
stack.entries.splice(entryIndex, 1);
if (stack.entries.length === 0) {
destroyStack(stack);
return;
}
if (wasTopEntry) {
const nextTopEntry = getTopEntry(stack);
const nextActiveBodyChild = nextTopEntry ? getBodyChildContaining(document, nextTopEntry.portalRoot) : null;
if (nextActiveBodyChild) restoreBackgroundElement(stack, nextActiveBodyChild);
focusTopEntry(stack);
}
reconcileBackground(stack);
};
}
//#endregion
export { registerErrorOverlay };
//# sourceMappingURL=error-overlay-stack.js.map