61 lines
2.6 KiB
JavaScript
61 lines
2.6 KiB
JavaScript
// Desk calendar: in-game date, term progress, next decision dates, the brass speed
|
||
// controls and the way into the menu.
|
||
|
||
import { t, dateParts } from "../i18n/i18n.js";
|
||
import { dateOfDay } from "../engine/index.js";
|
||
import { $ } from "./desk.js";
|
||
|
||
export function createCalendar(root, bal, { onSpeed, onMenu }) {
|
||
const speeds = bal.time.speeds;
|
||
root.innerHTML = `
|
||
<div class="cal__head"><span class="cal__weekday"></span></div>
|
||
<div class="cal__body">
|
||
<div class="cal__day"></div>
|
||
<div class="cal__month"></div>
|
||
<div class="cal__count"></div>
|
||
<div class="cal__term"><span></span></div>
|
||
<dl class="cal__next">
|
||
<dt>${t("cal.briefing")}</dt><dd data-next="briefing"></dd>
|
||
<dt>${t("cal.review")}</dt><dd data-next="review"></dd>
|
||
</dl>
|
||
<div class="cal__controls">
|
||
<button class="brass" data-speed="0" title="${t("cal.pause")}" aria-label="${t("cal.pause")}">II</button>
|
||
${speeds.map((s) => `<button class="brass" data-speed="${s}" aria-label="${t("cal.speed", { n: s })}">${s}×</button>`).join("")}
|
||
</div>
|
||
<button class="brass cal__menu" data-menu>${t("cal.menu")}</button>
|
||
</div>`;
|
||
|
||
root.addEventListener("click", (e) => {
|
||
const b = e.target.closest("button");
|
||
if (!b) return;
|
||
if (b.dataset.menu !== undefined) onMenu();
|
||
else onSpeed(Number(b.dataset.speed));
|
||
});
|
||
|
||
const until = (day, every) => (every - (day % every)) % every;
|
||
const when = (n) => {
|
||
if (n === 0) return t("cal.today");
|
||
return n === 1 ? t("cal.tomorrow") : t("cal.inDays", { n });
|
||
};
|
||
|
||
function update(state) {
|
||
const p = dateParts(dateOfDay(state.startDate, state.day).iso);
|
||
$(root, ".cal__weekday").textContent = p.weekday;
|
||
$(root, ".cal__day").textContent = p.day;
|
||
$(root, ".cal__month").textContent = `${p.month} ${p.year}`;
|
||
const shown = Math.min(state.day + 1, bal.time.termDays);
|
||
$(root, ".cal__count").textContent = t("cal.dayCount", { day: shown, total: bal.time.termDays });
|
||
$(root, ".cal__term span").style.width = `${(state.day / bal.time.termDays) * 100}%`;
|
||
|
||
const over = Boolean(state.over);
|
||
$(root, '[data-next="briefing"]').textContent = over ? "–" : when(state.pending.weeklyCard ? 0 : until(state.day, bal.time.daysPerWeek));
|
||
$(root, '[data-next="review"]').textContent = over ? "–" : when(state.pending.review ? 0 : until(state.day, bal.time.daysPerQuarter));
|
||
|
||
for (const b of root.querySelectorAll("button[data-speed]")) {
|
||
b.setAttribute("aria-pressed", String(Number(b.dataset.speed) === state.speed));
|
||
}
|
||
}
|
||
|
||
return { update };
|
||
}
|