dr-sandbox/app/web/static/run-modal.js
Michael Pilosov a4fc36352d compare: axes dropdown combines sync (scaled/locked) × aspect (1:1/3:2)
Canvas height now derives from column width via aspect-ratio (CSS custom
prop --canvas-aspect set by JS on the grid host), with --panel-h as a
ceiling. Dropdown options: scaled/locked × 1:1/3:2. Default scaled 3:2.
Legacy 'independent'/'locked' values still parse. Canvas resizes after
aspect changes via requestAnimationFrame.
2026-04-22 16:57:15 -06:00

70 lines
2.5 KiB
JavaScript

// run-modal.js — homepage click-hijack for embedding links. Opens a
// <dialog id="run-modal"> that renders the run's embedding via panel-grid.js.
import { mountPanels } from './panel-grid.js?v=4';
const dialog = document.getElementById('run-modal');
const host = document.getElementById('modal-panel-host');
const controls = document.getElementById('modal-compare-controls');
const closeBtn = document.getElementById('run-modal-close');
const runsSlot = document.getElementById('runs-slot');
let active = null; // { destroy } returned by mountPanels
function openFor(stem) {
if (!dialog || !host || !controls) return;
if (active) { try { active.destroy(); } catch (_) {} active = null; }
active = mountPanels({ host, controls, stems: [stem] });
if (typeof dialog.showModal === 'function') dialog.showModal();
else dialog.setAttribute('open', '');
}
function closeModal() {
if (!dialog) return;
if (dialog.open) dialog.close();
if (active) { try { active.destroy(); } catch (_) {} active = null; }
}
function onRunsClick(ev) {
if (ev.defaultPrevented) return;
if (ev.button !== undefined && ev.button !== 0) return;
if (ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey) return;
const a = ev.target.closest('a[data-role="embedding-link"]');
if (!a) return;
if (!runsSlot || !runsSlot.contains(a)) return;
const stem = a.dataset.stem;
if (!stem) return;
ev.preventDefault();
openFor(stem);
}
function wire() {
if (!dialog) return;
if (runsSlot) runsSlot.addEventListener('click', onRunsClick);
if (closeBtn) closeBtn.addEventListener('click', closeModal);
dialog.addEventListener('close', () => {
if (active) { try { active.destroy(); } catch (_) {} active = null; }
});
// Clicking the backdrop (native behavior fires a click on the dialog
// itself, with target === dialog) closes the modal.
dialog.addEventListener('click', (ev) => {
if (ev.target === dialog) closeModal();
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', wire);
} else {
wire();
}
// htmx re-renders #runs-slot every 3s. Delegation on #runs-slot survives the
// swap (the slot element itself is stable), so we don't need to re-bind —
// but we keep the hook in case a consumer ever replaces the slot wholesale.
document.body.addEventListener('htmx:afterSwap', (ev) => {
if (ev.detail?.target?.id === 'runs-slot' && runsSlot && !runsSlot._runModalWired) {
runsSlot.addEventListener('click', onRunsClick);
runsSlot._runModalWired = true;
}
});