// ============================================================================ // Stylescape | Countdown // ============================================================================ // Drives `.ss-c-countdown` rolling-digit displays. // // Markup contract: // // ::… // // or an absolute target: // … // // Each `[data-unit]` child gets its `--ss-countdown-value` custom property // updated once a second; the SCSS module translates the digit column. // Elements without either data attribute are left untouched (static demos). // ============================================================================ type CountdownUnit = "days" | "hours" | "minutes" | "seconds"; export class Countdown { private static timers = new WeakMap(); /** Initialize every countdown on the page. */ public static initAll(selector: string = ".ss-c-countdown"): void { document.querySelectorAll(selector).forEach((el) => { Countdown.init(el); }); } /** Initialize a single countdown element. */ public static init(el: HTMLElement): void { const toAttr = el.dataset.countdownTo; const secondsAttr = el.dataset.countdownSeconds; if (!toAttr && !secondsAttr) return; // Clear a previous timer if re-initialized. const existing = Countdown.timers.get(el); if (existing) window.clearInterval(existing); let target: number; if (toAttr) { target = new Date(toAttr).getTime(); } else { target = Date.now() + Number(secondsAttr) * 1000; } const tick = (): void => { const remaining = Math.max( 0, Math.round((target - Date.now()) / 1000), ); const parts: Record = { days: Math.floor(remaining / 86400), hours: Math.floor((remaining % 86400) / 3600), minutes: Math.floor((remaining % 3600) / 60), seconds: remaining % 60, }; el.querySelectorAll("[data-unit]").forEach((unit) => { const kind = unit.dataset.unit as CountdownUnit; const value = parts[kind]; if (value !== undefined) { // The digit column covers 0–60; clamp days-style overflow. unit.style.setProperty( "--ss-countdown-value", String(Math.min(value, 60)), ); unit.setAttribute("aria-label", `${value} ${kind}`); } }); if (remaining <= 0) { const timer = Countdown.timers.get(el); if (timer) window.clearInterval(timer); el.dispatchEvent(new CustomEvent("ss:countdown:finished")); } }; tick(); const timer = window.setInterval(tick, 1000); Countdown.timers.set(el, timer); } }