// ── 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 = `
`;
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();
});