runtimeSurfacesJs top-level constant
String
const runtimeSurfacesJs
Implementation
const String runtimeSurfacesJs = r'''
function surfaceState(el) {
if (!el) return 'closed';
return el.getAttribute('data-arcane-state') || 'closed';
}
function surfaceIsOpen(el) {
return surfaceState(el) === 'open';
}
function surfaceType(el) {
if (!el) return null;
return el.getAttribute('data-arcane-surface');
}
function surfaceId(el) {
if (!el) return null;
return el.getAttribute('data-arcane-id');
}
function surfaceGroup(el) {
if (!el) return null;
return el.getAttribute('data-arcane-surface-group');
}
function trapFocus(surface, e) {
if (!surface) return;
if (surface.getAttribute('data-arcane-focus-trap') === 'false') return;
const focusable = getFocusable(surface);
if (focusable.length === 0) {
e.preventDefault();
surface.focus();
return;
}
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (e.shiftKey) {
if (document.activeElement === first || !surface.contains(document.activeElement)) {
e.preventDefault();
last.focus();
}
} else {
if (document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
}
function applyAnchor(surfaceEl) {
if (!surfaceEl) return;
const anchorId = surfaceEl.getAttribute('data-arcane-anchor');
if (!anchorId) return;
const anchorEl = document.querySelector(
'[data-arcane-anchor-id="' + cssEscape(anchorId) + '"]'
);
if (!anchorEl) return;
positionAnchored(surfaceEl, anchorEl);
}
function positionAnchored(surfaceEl, anchorEl) {
const placement = surfaceEl.getAttribute('data-arcane-anchor-placement') || 'bottom';
const align = surfaceEl.getAttribute('data-arcane-anchor-align') || 'start';
const offset = parseInt(
surfaceEl.getAttribute('data-arcane-anchor-offset') || '8', 10
);
const rect = anchorEl.getBoundingClientRect();
const sw = surfaceEl.offsetWidth;
const sh = surfaceEl.offsetHeight;
const vw = window.innerWidth;
const vh = window.innerHeight;
let top = 0;
let left = 0;
let actualPlacement = placement;
if (placement === 'bottom' && rect.bottom + offset + sh > vh && rect.top - offset - sh > 0) {
actualPlacement = 'top';
} else if (placement === 'top' && rect.top - offset - sh < 0 && rect.bottom + offset + sh < vh) {
actualPlacement = 'bottom';
} else if (placement === 'right' && rect.right + offset + sw > vw && rect.left - offset - sw > 0) {
actualPlacement = 'left';
} else if (placement === 'left' && rect.left - offset - sw < 0 && rect.right + offset + sw < vw) {
actualPlacement = 'right';
}
if (actualPlacement === 'bottom') {
top = rect.bottom + offset;
left = align === 'end' ? rect.right - sw : (align === 'center' ? rect.left + (rect.width - sw) / 2 : rect.left);
} else if (actualPlacement === 'top') {
top = rect.top - sh - offset;
left = align === 'end' ? rect.right - sw : (align === 'center' ? rect.left + (rect.width - sw) / 2 : rect.left);
} else if (actualPlacement === 'right') {
left = rect.right + offset;
top = align === 'end' ? rect.bottom - sh : (align === 'center' ? rect.top + (rect.height - sh) / 2 : rect.top);
} else if (actualPlacement === 'left') {
left = rect.left - sw - offset;
top = align === 'end' ? rect.bottom - sh : (align === 'center' ? rect.top + (rect.height - sh) / 2 : rect.top);
}
if (left < 4) left = 4;
if (top < 4) top = 4;
if (left + sw > vw - 4) left = vw - sw - 4;
if (top + sh > vh - 4) top = vh - sh - 4;
surfaceEl.style.position = 'fixed';
surfaceEl.style.top = top + 'px';
surfaceEl.style.left = left + 'px';
surfaceEl.setAttribute('data-arcane-actual-placement', actualPlacement);
}
function openSurface(type, id, opts) {
opts = opts || {};
const el = querySurface(type, id);
if (!el) return false;
if (surfaceIsOpen(el)) return true;
const groupName = surfaceGroup(el);
const exclusive = el.getAttribute('data-arcane-exclusive') === 'true';
if (groupName) {
const peers = document.querySelectorAll(
'[data-arcane-surface-group="' + cssEscape(groupName) + '"][data-arcane-state="open"]'
);
for (let i = 0; i < peers.length; i++) {
if (peers[i] !== el) {
closeSurface(surfaceType(peers[i]), surfaceId(peers[i]), { silent: true });
}
}
}
if (exclusive) {
const all = document.querySelectorAll(
'[data-arcane-surface="' + type + '"][data-arcane-state="open"]'
);
for (let i = 0; i < all.length; i++) {
if (all[i] !== el) closeSurface(type, surfaceId(all[i]), { silent: true });
}
}
if (opts.trigger) {
el._arcanePrevFocus = opts.trigger;
} else {
el._arcanePrevFocus = document.activeElement;
}
el.removeAttribute('hidden');
el.setAttribute('data-arcane-state', 'open');
el.setAttribute('aria-hidden', 'false');
applyAnchor(el);
ARCANE.stack.push({ type: type, id: id, el: el });
if (type === 'dialog' || type === 'sheet' || type === 'drawer') {
document.body.classList.add('arcane-overlay-open');
document.body.setAttribute('data-arcane-overlay-open', 'true');
}
nextFrame(function() {
el.classList.add('arcane-surface-open');
el.classList.remove('arcane-surface-closing');
const focusTarget = el.querySelector('[data-arcane-autofocus]') ||
getFocusable(el)[0];
if (focusTarget) {
try { focusTarget.focus({ preventScroll: false }); } catch (e) { focusTarget.focus(); }
} else if (el.tabIndex >= 0) {
el.focus();
}
});
fireEvent(el, 'arcane:open', { type: type, id: id });
fireEvent(document, 'arcane:surface-open', { type: type, id: id });
if (!opts.skipAnchorListener && el.getAttribute('data-arcane-anchor')) {
el._arcaneAnchorReposition = function() { applyAnchor(el); };
window.addEventListener('resize', el._arcaneAnchorReposition);
window.addEventListener('scroll', el._arcaneAnchorReposition, true);
}
return true;
}
function closeSurface(type, id, opts) {
opts = opts || {};
let el;
if (id) {
el = querySurface(type, id);
} else {
const list = document.querySelectorAll(
'[data-arcane-surface="' + type + '"][data-arcane-state="open"]'
);
if (list.length > 0) el = list[list.length - 1];
}
if (!el) return false;
if (!surfaceIsOpen(el)) return false;
el.setAttribute('data-arcane-state', 'closed');
el.setAttribute('aria-hidden', 'true');
el.classList.remove('arcane-surface-open');
el.classList.add('arcane-surface-closing');
ARCANE.stack = ARCANE.stack.filter(function(entry) {
return entry.el !== el;
});
const stillOpen = document.querySelectorAll(
'[data-arcane-surface="dialog"][data-arcane-state="open"], ' +
'[data-arcane-surface="sheet"][data-arcane-state="open"], ' +
'[data-arcane-surface="drawer"][data-arcane-state="open"]'
).length;
if (stillOpen === 0) {
document.body.classList.remove('arcane-overlay-open');
document.body.removeAttribute('data-arcane-overlay-open');
}
if (el._arcaneAnchorReposition) {
window.removeEventListener('resize', el._arcaneAnchorReposition);
window.removeEventListener('scroll', el._arcaneAnchorReposition, true);
el._arcaneAnchorReposition = null;
}
const restoreFocus = el.getAttribute('data-arcane-restore-focus') !== 'false';
const prevFocus = el._arcanePrevFocus;
const finalize = function() {
el.setAttribute('hidden', '');
el.classList.remove('arcane-surface-closing');
el.style.position = '';
el.style.top = '';
el.style.left = '';
el.removeAttribute('data-arcane-actual-placement');
if (restoreFocus && prevFocus && prevFocus.focus) {
try { prevFocus.focus({ preventScroll: true }); } catch (e) { prevFocus.focus(); }
}
el._arcanePrevFocus = null;
};
if (ARCANE.config.reducedMotion || opts.immediate) {
finalize();
} else {
const cs = window.getComputedStyle(el);
const dur = parseFloat(cs.transitionDuration || '0') || 0;
if (dur > 0) {
let done = false;
const handler = function() {
if (done) return;
done = true;
el.removeEventListener('transitionend', handler);
finalize();
};
el.addEventListener('transitionend', handler);
setTimeout(handler, dur * 1000 + 100);
} else {
finalize();
}
}
if (!opts.silent) {
fireEvent(el, 'arcane:close', { type: type, id: id });
fireEvent(document, 'arcane:surface-close', { type: type, id: id });
}
return true;
}
function toggleSurface(type, id, opts) {
const el = querySurface(type, id);
if (!el) return false;
if (surfaceIsOpen(el)) return closeSurface(type, id, opts);
return openSurface(type, id, opts);
}
function dismissTopSurface() {
if (ARCANE.stack.length === 0) return false;
const top = ARCANE.stack[ARCANE.stack.length - 1];
return closeSurface(top.type, top.id);
}
ARCANE.surfaces.open = openSurface;
ARCANE.surfaces.close = closeSurface;
ARCANE.surfaces.toggle = toggleSurface;
ARCANE.surfaces.dismissTop = dismissTopSurface;
ARCANE.surfaces.applyAnchor = applyAnchor;
''';