Rework app-shell layout
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
// Full-viewport Overlay component (mobile). Distinct from the .modal dialog:
|
||||
// this hosts a whole rail (the folder tree or the right-rail widgets/TOC) at
|
||||
// full screen with its own scroll and a top-right close control. It reuses the
|
||||
// low-level focus/Escape/bfcache patterns from modal.js rather than that
|
||||
// component's panel chrome.
|
||||
//
|
||||
// openOverlay(node, opts) MOVES `node` (an existing rail <aside>) into the
|
||||
// overlay body and restores it to its original DOM position on close, so the
|
||||
// rail keeps its state (expanded tree folders, rendered TOC). Only one overlay
|
||||
// is open at a time.
|
||||
(function () {
|
||||
var overlay = null;
|
||||
var bodyEl = null;
|
||||
var closeBtn = null;
|
||||
var hosted = null; // the moved node
|
||||
var placeholder = null; // marks where to restore it
|
||||
var prevFocus = null;
|
||||
var onKeydown = null;
|
||||
|
||||
function build() {
|
||||
overlay = document.createElement('div');
|
||||
overlay.className = 'overlay';
|
||||
overlay.setAttribute('role', 'dialog');
|
||||
overlay.setAttribute('aria-modal', 'true');
|
||||
|
||||
var bar = document.createElement('div');
|
||||
bar.className = 'overlay-bar';
|
||||
|
||||
closeBtn = document.createElement('button');
|
||||
closeBtn.type = 'button';
|
||||
closeBtn.className = 'btn btn-fab overlay-close';
|
||||
closeBtn.title = 'Close';
|
||||
closeBtn.setAttribute('aria-label', 'Close');
|
||||
closeBtn.textContent = '×';
|
||||
closeBtn.addEventListener('click', close);
|
||||
bar.appendChild(closeBtn);
|
||||
|
||||
bodyEl = document.createElement('div');
|
||||
bodyEl.className = 'overlay-body';
|
||||
// Any navigation or anchor activation inside a rail dismisses the
|
||||
// overlay: tree/calendar links navigate away, TOC links scroll the
|
||||
// page underneath — either way the user should see the result, and
|
||||
// closing synchronously (before navigation) avoids a bfcache snapshot
|
||||
// that would restore the overlay open on back-nav.
|
||||
bodyEl.addEventListener('click', function (e) {
|
||||
if (e.target.closest('a')) close();
|
||||
});
|
||||
|
||||
overlay.appendChild(bar);
|
||||
overlay.appendChild(bodyEl);
|
||||
}
|
||||
|
||||
function open(node, opts) {
|
||||
if (overlay && overlay.parentNode) close();
|
||||
if (!overlay) build();
|
||||
opts = opts || {};
|
||||
|
||||
prevFocus = document.activeElement;
|
||||
hosted = node;
|
||||
placeholder = document.createComment('overlay-slot');
|
||||
node.parentNode.insertBefore(placeholder, node);
|
||||
bodyEl.appendChild(node);
|
||||
|
||||
document.body.appendChild(overlay);
|
||||
overlay.classList.add('is-open');
|
||||
|
||||
onKeydown = function (e) {
|
||||
if (e.key === 'Escape') { e.preventDefault(); close(); }
|
||||
};
|
||||
document.addEventListener('keydown', onKeydown);
|
||||
|
||||
setTimeout(function () { closeBtn.focus(); }, 0);
|
||||
|
||||
if (typeof opts.onOpen === 'function') opts.onOpen();
|
||||
return { close: close };
|
||||
}
|
||||
|
||||
function close() {
|
||||
if (!overlay || !overlay.parentNode) return;
|
||||
if (onKeydown) {
|
||||
document.removeEventListener('keydown', onKeydown);
|
||||
onKeydown = null;
|
||||
}
|
||||
// Restore the hosted rail to its original spot in the shell.
|
||||
if (hosted && placeholder && placeholder.parentNode) {
|
||||
placeholder.parentNode.insertBefore(hosted, placeholder);
|
||||
placeholder.parentNode.removeChild(placeholder);
|
||||
}
|
||||
hosted = null;
|
||||
placeholder = null;
|
||||
overlay.classList.remove('is-open');
|
||||
overlay.parentNode.removeChild(overlay);
|
||||
|
||||
var toRestore = prevFocus;
|
||||
prevFocus = null;
|
||||
if (toRestore && toRestore.focus) {
|
||||
try { toRestore.focus(); } catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
// Belt-and-suspenders bfcache guard: force the overlay closed (restoring
|
||||
// the rail) if the page is being hidden while it is still open.
|
||||
window.addEventListener('pagehide', close);
|
||||
|
||||
window.openOverlay = open;
|
||||
window.closeOverlay = close;
|
||||
})();
|
||||
Reference in New Issue
Block a user