// ── Custom dialog system ── import { $ } from './util.js'; const overlay = $('dialog'); const titleEl = $('dialog-title'); const msgEl = $('dialog-msg'); const inputEl = $('dialog-input'); const datepickerEl = $('dialog-datepicker'); const cancelBtn = $('dialog-cancel'); const okBtn = $('dialog-ok'); const closeBtn = $('dialog-close'); let resolve = null; let dpMode = false; let dpYear, dpMonth, dpSelected; const DAYS = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su']; const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; function canCancel() { return !cancelBtn.classList.contains('hidden'); } function close(result) { overlay.classList.remove('open'); datepickerEl.classList.add('hidden'); datepickerEl.innerHTML = ''; dpMode = false; if (resolve) { resolve(result); resolve = null; } } function submit() { if (dpMode) { close(dpSelected || null); } else { close(inputEl.classList.contains('hidden') ? true : inputEl.value); } } function pad(n) { return String(n).padStart(2, '0'); } function renderDatepicker() { const today = new Date(); const todayStr = `${today.getFullYear()}-${pad(today.getMonth() + 1)}-${pad(today.getDate())}`; const firstDay = new Date(dpYear, dpMonth, 1).getDay(); const startOffset = (firstDay + 6) % 7; const daysInMonth = new Date(dpYear, dpMonth + 1, 0).getDate(); const prevDays = new Date(dpYear, dpMonth, 0).getDate(); let html = `
${MONTHS[dpMonth]} ${dpYear}
`; for (const d of DAYS) html += `
${d}
`; for (let i = 0; i < 42; i++) { const dayNum = i - startOffset + 1; let ds, display, outClass = ''; if (dayNum < 1) { display = prevDays + dayNum; const pm = dpMonth === 0 ? 11 : dpMonth - 1; const py = dpMonth === 0 ? dpYear - 1 : dpYear; ds = `${py}-${pad(pm + 1)}-${pad(display)}`; outClass = 'dp-out'; } else if (dayNum > daysInMonth) { display = dayNum - daysInMonth; const nm = dpMonth === 11 ? 0 : dpMonth + 1; const ny = dpMonth === 11 ? dpYear + 1 : dpYear; ds = `${ny}-${pad(nm + 1)}-${pad(display)}`; outClass = 'dp-out'; } else { display = dayNum; ds = `${dpYear}-${pad(dpMonth + 1)}-${pad(dayNum)}`; } const isToday = ds === todayStr ? 'dp-today' : ''; const isSel = ds === dpSelected ? 'dp-selected' : ''; html += `
${display}
`; } html += '
'; datepickerEl.innerHTML = html; // Nav buttons datepickerEl.querySelectorAll('[data-dp-nav]').forEach(btn => { btn.addEventListener('click', e => { e.preventDefault(); dpMonth += parseInt(btn.dataset.dpNav); if (dpMonth > 11) { dpMonth = 0; dpYear++; } if (dpMonth < 0) { dpMonth = 11; dpYear--; } renderDatepicker(); }); }); // Day click datepickerEl.querySelectorAll('[data-dp-date]').forEach(day => { day.addEventListener('click', () => { dpSelected = day.dataset.dpDate; // Parse selected date to navigate to its month const [y, m] = dpSelected.split('-').map(Number); if (y !== dpYear || m - 1 !== dpMonth) { dpYear = y; dpMonth = m - 1; } renderDatepicker(); }); day.addEventListener('dblclick', () => { dpSelected = day.dataset.dpDate; submit(); }); }); } export function showDialog({ title, message = '', input = false, placeholder = '', defaultValue = '', danger = false, okLabel = 'Ok', cancelLabel = 'Cancel' }) { return new Promise(res => { resolve = res; titleEl.textContent = title; msgEl.textContent = message; msgEl.classList.toggle('hidden', !message); dpMode = input === 'date'; if (dpMode) { inputEl.classList.add('hidden'); datepickerEl.classList.remove('hidden'); const d = defaultValue ? new Date(defaultValue + 'T00:00:00') : new Date(); dpYear = d.getFullYear(); dpMonth = d.getMonth(); dpSelected = defaultValue || `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`; renderDatepicker(); } else { datepickerEl.classList.add('hidden'); datepickerEl.innerHTML = ''; inputEl.classList.toggle('hidden', input === false); inputEl.value = defaultValue; inputEl.placeholder = placeholder; inputEl.type = input === 'password' ? 'password' : 'text'; } okBtn.textContent = okLabel; okBtn.classList.toggle('border-t-danger', danger); okBtn.classList.toggle('text-t-danger', danger); okBtn.classList.toggle('border-t-accent', !danger); okBtn.classList.toggle('text-t-accent', !danger); cancelBtn.textContent = cancelLabel; cancelBtn.classList.toggle('hidden', !cancelLabel); if (closeBtn) closeBtn.classList.toggle('hidden', !cancelLabel); overlay.classList.add('open'); if (input && !dpMode) inputEl.focus(); else okBtn.focus(); }); } export function isDialogOpen() { return overlay.classList.contains('open'); } // Expose to plugins via the hyprcrd API if (typeof window !== 'undefined' && window.hyprcrd?.dialog) { window.hyprcrd.dialog.show = showDialog; } okBtn.addEventListener('click', submit); cancelBtn.addEventListener('click', () => { if (canCancel()) close(null); }); if (closeBtn) closeBtn.addEventListener('click', () => { if (canCancel()) close(null); }); overlay.addEventListener('click', e => { if (e.target === overlay && canCancel()) close(null); }); overlay.addEventListener('keydown', e => { if (e.key === 'Escape' && canCancel()) close(null); if (e.key === 'Enter') submit(); });