const STYLE_ID = 'calendar-popover-liquid-styles'; 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 = 'calendar-liquid-panel rounded-[1.35rem] py-3'; const DEFAULT_PANEL_STYLE = 'background-image:none !important;'; 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))', }; export function ensureCalendarPopoverStyles() { if (typeof document === 'undefined') return; if (document.getElementById(STYLE_ID)) return; const style = document.createElement('style'); style.id = STYLE_ID; style.textContent = ` .calendar-liquid-panel { background: rgba(255, 255, 255, 0.2) !important; border: 1px solid rgba(255, 255, 255, 0.32) !important; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6), inset 0 -1px 0 rgba(var(--overlay-rgb), 0.06), 0 8px 20px rgba(var(--overlay-rgb), 0.14), 0 18px 38px rgba(var(--overlay-rgb), 0.18) !important; backdrop-filter: blur(28px) saturate(180%); -webkit-backdrop-filter: blur(28px) saturate(180%); } .dark .calendar-liquid-panel { background: rgba(255, 255, 255, 0.04) !important; border: 1px solid rgba(255, 255, 255, 0.12) !important; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.24), inset 0 -1px 0 rgba(0, 0, 0, 0.22), 0 10px 24px rgba(0, 0, 0, 0.3), 0 24px 54px rgba(0, 0, 0, 0.38) !important; } .calendar-liquid-btn { background: rgba(255, 255, 255, 0.16) !important; border: 1px solid rgba(255, 255, 255, 0.24) !important; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.45), inset 0 -1px 0 rgba(var(--overlay-rgb), 0.08), 0 6px 14px rgba(var(--overlay-rgb), 0.14) !important; backdrop-filter: blur(22px) saturate(165%); -webkit-backdrop-filter: blur(22px) saturate(165%); transition: background 180ms ease, border-color 180ms ease, transform 180ms ease; } .calendar-liquid-btn:hover { background: rgba(255, 255, 255, 0.22) !important; transform: translateY(-1px); } .dark .calendar-liquid-btn { background: rgba(255, 255, 255, 0.06) !important; border: 1px solid rgba(255, 255, 255, 0.12) !important; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), inset 0 -1px 0 rgba(0, 0, 0, 0.2), 0 8px 18px rgba(0, 0, 0, 0.28) !important; } .dark .calendar-liquid-btn:hover { background: rgba(255, 255, 255, 0.1) !important; } .calendar-liquid-btn input, .calendar-liquid-btn input:focus, .calendar-liquid-btn input:active { background: transparent !important; background-color: transparent !important; background-image: none !important; border: none !important; box-shadow: none !important; outline: none !important; appearance: none !important; -webkit-appearance: none !important; -moz-appearance: textfield !important; backdrop-filter: none !important; -webkit-backdrop-filter: none !important; filter: none !important; } .calendar-liquid-btn input[type='number']::-webkit-outer-spin-button, .calendar-liquid-btn input[type='number']::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } .calendar-liquid-btn input:-webkit-autofill, .calendar-liquid-btn input:-webkit-autofill:hover, .calendar-liquid-btn input:-webkit-autofill:focus { -webkit-text-fill-color: rgb(var(--text-body-rgb)); -webkit-box-shadow: 0 0 0 1000px transparent inset !important; box-shadow: 0 0 0 1000px transparent inset !important; transition: background-color 9999s ease-in-out 0s; } `; document.head.appendChild(style); } 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, }) { ensureCalendarPopoverStyles(); 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, }; }