Refine overlay stacking behavior
This commit is contained in:
parent
8b2bc9bb1c
commit
0450ec9e1c
2 changed files with 49 additions and 5 deletions
53
script.js
53
script.js
|
@ -59,7 +59,7 @@
|
|||
// UX Manager
|
||||
const UX = (() => {
|
||||
let currentBase = null; // { view, el }
|
||||
const overlayStack = []; // [{ view, el, lastFocus }]
|
||||
const overlayStack = []; // [{ view, el, lastFocus, isHidden, isClosing }]
|
||||
|
||||
function ensureViewEl(view) {
|
||||
if (!view || !view.el) throw new Error('Invalid view');
|
||||
|
@ -67,6 +67,42 @@
|
|||
return view.el;
|
||||
}
|
||||
|
||||
function syncOverlayVisibility() {
|
||||
const topIndex = overlayStack.length - 1;
|
||||
overlayStack.forEach((entry, idx) => {
|
||||
const { el, view } = entry;
|
||||
const shouldHide = idx !== topIndex;
|
||||
if (shouldHide) {
|
||||
if (!el.hasAttribute('hidden')) el.setAttribute('hidden', '');
|
||||
el.setAttribute('aria-hidden', 'true');
|
||||
el.setAttribute('inert', '');
|
||||
el.inert = true;
|
||||
if (!entry.isHidden) {
|
||||
entry.isHidden = true;
|
||||
if (view && typeof view.onHide === 'function') view.onHide();
|
||||
}
|
||||
} else {
|
||||
if (el.hasAttribute('hidden')) el.removeAttribute('hidden');
|
||||
el.removeAttribute('aria-hidden');
|
||||
el.removeAttribute('inert');
|
||||
el.inert = false;
|
||||
entry.isHidden = false;
|
||||
}
|
||||
});
|
||||
|
||||
if (currentBase && currentBase.el) {
|
||||
if (overlayStack.length > 0) {
|
||||
currentBase.el.setAttribute('aria-hidden', 'true');
|
||||
currentBase.el.setAttribute('inert', '');
|
||||
currentBase.el.inert = true;
|
||||
} else {
|
||||
currentBase.el.removeAttribute('aria-hidden');
|
||||
currentBase.el.removeAttribute('inert');
|
||||
currentBase.el.inert = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function replace(view) {
|
||||
const el = ensureViewEl(view);
|
||||
const prev = currentBase;
|
||||
|
@ -80,6 +116,7 @@
|
|||
}
|
||||
$uxRoot.appendChild(el);
|
||||
currentBase = { view, el };
|
||||
syncOverlayVisibility();
|
||||
await fadeIn(el);
|
||||
if (typeof view.onShow === 'function') view.onShow();
|
||||
}
|
||||
|
@ -88,21 +125,27 @@
|
|||
const el = ensureViewEl(view);
|
||||
el.classList.add('ux-view--overlay');
|
||||
const lastFocus = document.activeElement;
|
||||
const prevEntry = overlayStack.length ? overlayStack[overlayStack.length - 1] : null;
|
||||
const entry = { view, el, lastFocus, isHidden: false, isClosing: false, prev: prevEntry };
|
||||
overlayStack.push(entry);
|
||||
$uxOverlays.appendChild(el);
|
||||
syncOverlayVisibility();
|
||||
await fadeIn(el);
|
||||
overlayStack.push({ view, el, lastFocus });
|
||||
$uxRoot.setAttribute('aria-hidden', 'true');
|
||||
if (typeof view.onShow === 'function') view.onShow();
|
||||
}
|
||||
|
||||
async function closeTop() {
|
||||
const top = overlayStack.pop();
|
||||
const top = overlayStack[overlayStack.length - 1];
|
||||
if (!top) return;
|
||||
if (top.isClosing) return;
|
||||
top.isClosing = true;
|
||||
const { view, el, lastFocus } = top;
|
||||
if (view && typeof view.onHide === 'function') view.onHide();
|
||||
await fadeOut(el);
|
||||
if (el.parentNode) el.parentNode.removeChild(el);
|
||||
if (typeof view.destroy === 'function') view.destroy();
|
||||
if (overlayStack.length === 0) $uxRoot.removeAttribute('aria-hidden');
|
||||
overlayStack.pop();
|
||||
syncOverlayVisibility();
|
||||
if (lastFocus && typeof lastFocus.focus === 'function') lastFocus.focus();
|
||||
}
|
||||
|
||||
|
|
1
site.css
1
site.css
|
@ -11,6 +11,7 @@
|
|||
.ux-overlays { position: absolute; inset: 0; pointer-events: none; }
|
||||
.ux-view { width: 100%; }
|
||||
.ux-view--overlay { position: absolute; inset: 0; z-index: 10; pointer-events: auto; }
|
||||
.ux-view[hidden] { display: none !important; }
|
||||
|
||||
.is-sr-only {
|
||||
position: absolute;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue