/** * Hoover — components/modal.js * * Modal overlay system: openModal, closeModal, closeAllModals, formModal. * Renders directly into #modal-root using DOM manipulation (not vdom) to * avoid fighting with the main render cycle. */ import { esc } from '../helpers.js?v=6'; import { att_esc } from '../helpers.js?v=6'; import { apiSubmit } from '../api.js?v=6'; const _modalQueue = []; function _renderModals() { const root = document.getElementById('modal-root'); if (!root) return; root.innerHTML = ''; _modalQueue.forEach((m, idx) => { const wrap = document.createElement('div'); wrap.className = 'modal-overlay active'; wrap.onclick = (e) => { if (e.target === wrap) closeModal(idx); }; const content = document.createElement('div'); content.className = 'modal'; content.onclick = (e) => e.stopPropagation(); if (m.renderFn) { try { m.renderFn(content, idx); } catch (err) { content.textContent = err.message; } } wrap.appendChild(content); root.appendChild(wrap); }); } /** * Open a modal dialog. * * @param {function} renderFn – (contentEl, idx) => void, renders into contentEl */ export function openModal(renderFn) { _modalQueue.push({ renderFn, id: _modalQueue.length }); _renderModals(); } /** * Close a modal by index. Closes the topmost modal if index is omitted. * * @param {number} [idx] */ export function closeModal(idx) { if (idx === undefined) idx = _modalQueue.length - 1; if (idx >= 0 && idx < _modalQueue.length) _modalQueue.splice(idx, 1); _renderModals(); } /** * Close all open modals. */ export function closeAllModals() { _modalQueue.length = 0; _renderModals(); } /** * Render a standard modal layout: title, form fields, action buttons. * * @param {HTMLElement} inner – Modal content element to fill * @param {string} title – Modal title * @param {object[]} fields – Form field descriptors * @param {object[]} actions – Action button descriptors * * Field shape: * { label, id, [tag: 'input'|'select'|'textarea'], [type], [value], [placeholder], [options] } * * Action shape: * { label, cls, action, handler } */ export function formModal(inner, title, fields, actions) { inner.innerHTML = '