107 lines
3.5 KiB
JavaScript
107 lines
3.5 KiB
JavaScript
function createTodayBreak({
|
|
timer, sheet, openButtons = null, cancelButton, presetButtons = null, form = null,
|
|
minutesInput = null, error = null, status, resumeButton,
|
|
qs = null, queryAll = null,
|
|
historyRef = null, windowRef = null,
|
|
now = () => Date.now(), setIntervalRef = setInterval, clearIntervalRef = clearInterval,
|
|
onChange = () => {}, onResume = () => {},
|
|
}) {
|
|
queryAll ||= selector => globalThis.document?.querySelectorAll(selector) || [];
|
|
historyRef ||= globalThis.history;
|
|
windowRef ||= globalThis.window;
|
|
sheet ||= qs?.('#today-break-sheet');
|
|
openButtons ||= queryAll?.('[data-today-break-open]') || [];
|
|
cancelButton ||= qs?.('#cancel-today-break');
|
|
presetButtons ||= queryAll?.('[data-today-break-minutes]') || [];
|
|
form ||= qs?.('#today-break-custom-form');
|
|
minutesInput ||= qs?.('#today-break-custom-minutes');
|
|
error ||= qs?.('#today-break-error');
|
|
status ||= qs?.('#today-break-status');
|
|
resumeButton ||= qs?.('#resume-today-break');
|
|
let ticker = null;
|
|
let historyEntry = false;
|
|
|
|
const stopTicker = () => {
|
|
if (ticker !== null) clearIntervalRef(ticker);
|
|
ticker = null;
|
|
};
|
|
const render = () => {
|
|
const pending = timer.breakSnapshot();
|
|
if (!pending) {
|
|
stopTicker();
|
|
status.hidden = true;
|
|
resumeButton.hidden = true;
|
|
openButtons.forEach(button => { button.hidden = false; });
|
|
return false;
|
|
}
|
|
status.hidden = false;
|
|
resumeButton.hidden = false;
|
|
openButtons.forEach(button => { button.hidden = true; });
|
|
const remaining = Math.max(0, pending.deadline_at - now());
|
|
if (pending.expired || remaining <= 0) {
|
|
status.textContent = 'Break over · ready to resume';
|
|
} else {
|
|
const seconds = Math.ceil(remaining / 1000);
|
|
const minutes = Math.floor(seconds / 60);
|
|
status.textContent = 'On break · resume in ' + minutes + ':' + String(seconds % 60).padStart(2, '0');
|
|
}
|
|
if (ticker === null) ticker = setIntervalRef(render, 1000);
|
|
return true;
|
|
};
|
|
const close = () => {
|
|
if (sheet.open) sheet.close();
|
|
if (historyEntry) {
|
|
historyEntry = false;
|
|
historyRef?.back();
|
|
}
|
|
};
|
|
const start = minutes => {
|
|
if (!timer.startBreak(Number(minutes))) return false;
|
|
if (error) error.textContent = '';
|
|
close();
|
|
render();
|
|
onChange();
|
|
return true;
|
|
};
|
|
const open = () => {
|
|
if (!timer.snapshot().identity) return false;
|
|
if (!sheet.open) {
|
|
sheet.showModal();
|
|
if (historyRef) {
|
|
historyRef.pushState({...(historyRef.state || {}), todayBreak:true}, '');
|
|
historyEntry = true;
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
|
|
openButtons.forEach(button => button.addEventListener('click', open));
|
|
cancelButton?.addEventListener('click', close);
|
|
windowRef?.addEventListener('popstate', () => {
|
|
historyEntry = false;
|
|
if (sheet.open) sheet.close();
|
|
});
|
|
presetButtons.forEach(button => button.addEventListener('click', () =>
|
|
start(button.dataset.todayBreakMinutes)
|
|
));
|
|
form?.addEventListener('submit', event => {
|
|
event.preventDefault();
|
|
if (!start(Number(minutesInput?.value))) {
|
|
if (error) error.textContent = 'Choose a whole number from 1 to 120 minutes.';
|
|
minutesInput?.focus();
|
|
}
|
|
});
|
|
resumeButton.addEventListener('click', () => {
|
|
const pending = timer.breakSnapshot();
|
|
if (!timer.resumeBreak()) return;
|
|
onResume(pending.identity);
|
|
render();
|
|
onChange();
|
|
});
|
|
render();
|
|
|
|
return { open, close, start, render };
|
|
}
|
|
|
|
if (typeof module !== 'undefined' && module.exports) module.exports = createTodayBreak;
|