┌─ TS ───────────────────────────────────────────────────────────────────────┐
│ // Classic cal(1) month grid, Monday-first, 20 cells wide. Marked days │
│ // get a * prefix in the separator column so the grid never shifts. │
│ import { center, checkGrid } from "../../lib/ascii"; │
│ │
│ export interface Props { │
│ year: number; │
│ /** 1-12. */ │
│ month: number; │
│ marks?: number[]; │
│ } │
│ │
│ export const CAL_W = 20; │
│ │
│ const MONTHS = [ │
│ "JANUARY", "FEBRUARY", "MARCH", "APRIL", "MAY", "JUNE", │
│ "JULY", "AUGUST", "SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER", │
│ ]; │
│ │
│ export function buildCalendar(year: number, month: number, marks: number[] = []): │
│ string { │
│ const marked = new Set(marks); │
│ // Monday-first offset of the 1st. │
│ const offset = (new Date(Date.UTC(year, month - 1, 1)).getUTCDay() + 6) % 7; │
│ const days = new Date(Date.UTC(year, month, 0)).getUTCDate(); │
│ │
│ const lines = [center(`${MONTHS[month - 1]} ${year}`, CAL_W), "MO TU WE TH FR SA │
│ SU"]; │
│ │
│ let week: string[] = Array.from({ length: offset }, () => " "); │
│ for (let d = 1; d <= days; d++) { │
│ week.push(String(d).padStart(2)); │
│ if (week.length === 7) { │
│ lines.push(weekLine(week, marked)); │
│ week = []; │
│ } │
│ } │
│ if (week.length) { │
│ while (week.length < 7) week.push(" "); │
│ lines.push(weekLine(week, marked)); │
│ } │
│ │
│ return checkGrid(lines.join("\n"), CAL_W, "calendar"); │
│ } │
│ │
│ function weekLine(cells: string[], marked: Set<number>): string { │
│ let out = ""; │
│ cells.forEach((c, i) => { │
│ const day = parseInt(c, 10); │
│ out += (i === 0 ? "" : marked.has(day) ? "*" : " ") + c; │
│ }); │
│ // A marked Monday has no separator column; flag it inside the cell. │
│ const first = parseInt(cells[0], 10); │
│ if (marked.has(first) && first < 10) out = "*" + out.slice(1); │
│ return out.trimEnd(); │
│ } │
└────────────────────────────────────────────────────────────────────────────────────┘
┌─ TS ─────────────────────────────────┐
│ // Classic cal(1) month grid, Monday-first, │
│ 20 cells wide. Marked days │
│ // get a * prefix in the separator column so │
│ the grid never shifts. │
│ import { center, checkGrid } from "../../lib │
│ /ascii"; │
│ │
│ export interface Props { │
│ year: number; │
│ /** 1-12. */ │
│ month: number; │
│ marks?: number[]; │
│ } │
│ │
│ export const CAL_W = 20; │
│ │
│ const MONTHS = [ │
│ "JANUARY", "FEBRUARY", "MARCH", "APRIL", " │
│ MAY", "JUNE", │
│ "JULY", "AUGUST", "SEPTEMBER", "OCTOBER", │
│ "NOVEMBER", "DECEMBER", │
│ ]; │
│ │
│ export function buildCalendar(year: number, │
│ month: number, marks: number[] = []): string │
│ { │
│ const marked = new Set(marks); │
│ // Monday-first offset of the 1st. │
│ const offset = (new Date(Date.UTC(year, mo │
│ nth - 1, 1)).getUTCDay() + 6) % 7; │
│ const days = new Date(Date.UTC(year, month │
│ , 0)).getUTCDate(); │
│ │
│ const lines = [center(`${MONTHS[month - 1] │
│ } ${year}`, CAL_W), "MO TU WE TH FR SA SU"]; │
│ │
│ let week: string[] = Array.from({ length: │
│ offset }, () => " "); │
│ for (let d = 1; d <= days; d++) { │
│ week.push(String(d).padStart(2)); │
│ if (week.length === 7) { │
│ lines.push(weekLine(week, marked)); │
│ week = []; │
│ } │
│ } │
│ if (week.length) { │
│ while (week.length < 7) week.push(" "); │
│ lines.push(weekLine(week, marked)); │
│ } │
│ │
│ return checkGrid(lines.join("\n"), CAL_W, │
│ "calendar"); │
│ } │
│ │
│ function weekLine(cells: string[], marked: S │
│ et<number>): string { │
│ let out = ""; │
│ cells.forEach((c, i) => { │
│ const day = parseInt(c, 10); │
│ out += (i === 0 ? "" : marked.has(day) ? │
│ "*" : " ") + c; │
│ }); │
│ // A marked Monday has no separator column │
│ ; flag it inside the cell. │
│ const first = parseInt(cells[0], 10); │
│ if (marked.has(first) && first < 10) out = │
│ "*" + out.slice(1); │
│ return out.trimEnd(); │
│ } │
└──────────────────────────────────────────────┘