feat: add combined codebase
This commit is contained in:
86
src/components/DeadlineTimer.ts
Normal file
86
src/components/DeadlineTimer.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import * as Y from "yjs";
|
||||
import { getDeadline } from "../state";
|
||||
|
||||
const DEADLINE_DURATION_MS = 2 * 60 * 1000; // 2 minutes
|
||||
|
||||
export function DeadlineTimer(
|
||||
deadlineMap: Y.Map<unknown>,
|
||||
onStartDeadline: (durationMs: number) => void,
|
||||
onClearDeadline: () => void,
|
||||
): HTMLElement {
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.className = "deadline-wrapper";
|
||||
|
||||
const timerEl = document.createElement("span");
|
||||
timerEl.className = "deadline-timer";
|
||||
|
||||
const startBtn = document.createElement("button");
|
||||
startBtn.className = "deadline-btn";
|
||||
startBtn.textContent = "Start 2-min vote";
|
||||
startBtn.setAttribute("aria-label", "Start a 2-minute voting deadline");
|
||||
|
||||
const clearBtn = document.createElement("button");
|
||||
clearBtn.className = "deadline-btn deadline-btn--clear";
|
||||
clearBtn.textContent = "Clear";
|
||||
clearBtn.setAttribute("aria-label", "Remove voting deadline");
|
||||
|
||||
wrapper.append(timerEl, startBtn, clearBtn);
|
||||
|
||||
let interval: ReturnType<typeof setInterval> | undefined;
|
||||
|
||||
function render() {
|
||||
const deadline = getDeadline(deadlineMap);
|
||||
const now = Date.now();
|
||||
|
||||
if (deadline === null) {
|
||||
// No deadline set
|
||||
timerEl.textContent = "";
|
||||
timerEl.className = "deadline-timer";
|
||||
startBtn.hidden = false;
|
||||
clearBtn.hidden = true;
|
||||
if (interval) {
|
||||
clearInterval(interval);
|
||||
interval = undefined;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
startBtn.hidden = true;
|
||||
clearBtn.hidden = false;
|
||||
|
||||
if (now >= deadline) {
|
||||
// Voting closed
|
||||
timerEl.textContent = "Voting closed";
|
||||
timerEl.className = "deadline-timer deadline-timer--closed";
|
||||
if (interval) {
|
||||
clearInterval(interval);
|
||||
interval = undefined;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Counting down
|
||||
const remaining = Math.ceil((deadline - now) / 1000);
|
||||
const mins = Math.floor(remaining / 60);
|
||||
const secs = remaining % 60;
|
||||
timerEl.textContent = `Voting closes in ${mins}:${secs.toString().padStart(2, "0")}`;
|
||||
timerEl.className = "deadline-timer deadline-timer--active";
|
||||
|
||||
if (!interval) {
|
||||
interval = setInterval(() => render(), 1000);
|
||||
}
|
||||
}
|
||||
|
||||
startBtn.addEventListener("click", () => {
|
||||
onStartDeadline(DEADLINE_DURATION_MS);
|
||||
});
|
||||
|
||||
clearBtn.addEventListener("click", () => {
|
||||
onClearDeadline();
|
||||
});
|
||||
|
||||
deadlineMap.observe(() => render());
|
||||
render();
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
Reference in New Issue
Block a user