const DEFAULT_POPOVER_CLASS = 'absolute left-0 right-0 top-full mt-2 z-[50] transition-all duration-200 pointer-events-none'; const DEFAULT_POPOVER_STYLE = 'opacity:0; transform:translateY(-6px) scale(0.98);'; const DEFAULT_PANEL_CLASS = 'rounded-[1.35rem] py-3'; const DEFAULT_PANEL_STYLE = 'background:rgb(var(--sunken-rgb)); border:1px solid rgb(var(--border-input-rgb)); box-shadow:var(--shadow-shell);'; const DEFAULT_OPEN_TRIGGER_STYLE = { background: 'rgb(var(--sunken-rgb))', borderColor: 'rgb(var(--border-input-rgb))', }; const DEFAULT_CLOSED_TRIGGER_STYLE = { background: 'rgb(var(--card-rgb))', borderColor: 'rgb(var(--border-card-rgb))', }; function byId(idOrElement) { if (!idOrElement) return null; if (typeof idOrElement === 'string') return document.getElementById(idOrElement); return idOrElement; } function setStyles(el, styles = {}, important = false) { if (!el) return; Object.entries(styles).forEach(([name, value]) => { if (value == null) return; if (important) { const cssName = name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`); el.style.setProperty(cssName, value, 'important'); } else el.style[name] = value; }); } export function createCalendarPopoverHTML({ id, calendarHTML, popoverClass = DEFAULT_POPOVER_CLASS, popoverStyle = DEFAULT_POPOVER_STYLE, panelClass = DEFAULT_PANEL_CLASS, panelStyle = DEFAULT_PANEL_STYLE, wrapInPanel = true, }) { const body = wrapInPanel ? `
${calendarHTML}
` : calendarHTML; return `
${body}
`; } export function syncCalendarPopoverVisibility({ popup, isOpen, chevron, trigger, openTriggerStyle = DEFAULT_OPEN_TRIGGER_STYLE, closedTriggerStyle = DEFAULT_CLOSED_TRIGGER_STYLE, triggerImportant = false, }) { const popupEl = byId(popup); if (popupEl) { popupEl.style.opacity = isOpen ? '1' : '0'; popupEl.style.transform = isOpen ? 'translateY(0) scale(1)' : 'translateY(-6px) scale(0.98)'; popupEl.style.pointerEvents = isOpen ? 'auto' : 'none'; } const chevronEl = byId(chevron); if (chevronEl) chevronEl.style.transform = isOpen ? 'rotate(180deg)' : 'rotate(0deg)'; setStyles( byId(trigger), isOpen ? openTriggerStyle : closedTriggerStyle, triggerImportant, ); } export function stabilizeSwipeCalendarLayout({ calendar, viewport, hideViewport = false, maxAttempts = 8, }) { const viewportEl = byId(viewport); if (hideViewport && viewportEl) { viewportEl.style.opacity = '0'; viewportEl.style.visibility = 'hidden'; viewportEl.style.transition = 'opacity 120ms ease'; } calendar?.render?.(); const ensureStableLayout = (attempt = 0) => { const width = viewportEl ? (viewportEl.clientWidth || viewportEl.getBoundingClientRect().width) : 0; if (viewportEl && width < 8 && attempt < maxAttempts) { requestAnimationFrame(() => ensureStableLayout(attempt + 1)); return; } calendar?.reapplyLayout?.(); calendar?.resetTrackPosition?.(); requestAnimationFrame(() => { calendar?.reapplyLayout?.(); calendar?.resetTrackPosition?.(); if (hideViewport && viewportEl) { viewportEl.style.visibility = 'visible'; viewportEl.style.opacity = '1'; } }); }; requestAnimationFrame(() => ensureStableLayout()); } export function createCalendarPopoverController({ popupId, viewportId, triggerId, chevronId, getCalendar, openTriggerStyle = DEFAULT_OPEN_TRIGGER_STYLE, closedTriggerStyle = DEFAULT_CLOSED_TRIGGER_STYLE, triggerImportant = false, hideViewportDuringLayout = false, }) { const calendar = () => (typeof getCalendar === 'function' ? getCalendar() : null); const sync = (isOpen) => { syncCalendarPopoverVisibility({ popup: popupId, isOpen, chevron: chevronId, trigger: triggerId, openTriggerStyle, closedTriggerStyle, triggerImportant, }); }; const stabilize = () => { stabilizeSwipeCalendarLayout({ calendar: calendar(), viewport: viewportId, hideViewport: hideViewportDuringLayout, }); }; const close = ({ clearPendingRange = false } = {}) => { sync(false); const instance = calendar(); if (clearPendingRange) instance?.clearPendingRange?.(); instance?.resetTrackPosition?.(); }; const open = () => { sync(true); stabilize(); }; return { sync, open, close, stabilize, }; }