UNPKG

simple-calendar-widget

Version:

A super simple calendar widget written in TypeScript and SCSS

177 lines (141 loc) 4.65 kB
type CalendarWidgetStringOrArrayOfStrings = Record<string, string | string[]>; interface CalendarWidgetOptions { startYear: number; startMonth: number; container: string; translations: CalendarWidgetStringOrArrayOfStrings; } const doc: Document = document; const defaultDays: string[] = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; const defaultMonths: string[] = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'Septemer', 'October', 'November', 'December' ]; const selectors: Record<string, string> = { body: '#ts-calendar__body', year: '#ts-calendar__year', month: '#ts-calendar__month' }; const today: Date = new Date(); const defaultStartYear: number = today.getFullYear(); const defaultStartMonth: number = today.getMonth(); const defaultStartDay: number = today.getDate(); const get = (selector: string): HTMLElement | null => doc.querySelector(selector); const add = (type: string): HTMLElement => doc.createElement(type); // prettier-ignore const onClick = (selector: string, callback: (event: MouseEvent) => void): void => get(selector)?.addEventListener('click', callback); const renderCaption = (translations: CalendarWidgetStringOrArrayOfStrings): string => ` <caption> <button id="ts-calendar__prev" title="${translations.prevMonthTitle}">${translations.prevMonth}</button> <span id="ts-calendar__month"></span> <span id="ts-calendar__year"></span> <button id="ts-calendar__next" title="${translations.nextMonthTitle}">${translations.nextMonth}</button> </caption> `; const renderDays = (days: string[]): string => days.map((day: string): string => `<th>${day}</th>`).join(''); const renderHeader = (translations: CalendarWidgetStringOrArrayOfStrings): string => ` <thead> <tr> ${renderDays(translations.days as string[])} </tr> </thead> `; const renderTable = (translations: CalendarWidgetStringOrArrayOfStrings): string => ` <table class="simple-calendar-widget"> ${renderCaption(translations)} ${renderHeader(translations)} <tbody id="ts-calendar__body"></tbody> </table> `; const renderCalendar = ( year: number, month: number, selectorsList: Record<string, string>, translations: CalendarWidgetStringOrArrayOfStrings ): void => { const bodyEl: HTMLElement | null = get(selectorsList.body); const yearEl: HTMLElement | null = get(selectorsList.year); const monthEl: HTMLElement | null = get(selectorsList.month); const firstDay: number = new Date(year, month).getDay(); const allDays: number = 32 - new Date(year, month, 32).getDate(); let day = 1; if (bodyEl) { bodyEl.innerHTML = ''; } if (yearEl) { yearEl.innerHTML = year.toString(); } if (monthEl) { monthEl.innerHTML = translations.months[month]; } for (let i = 0; i < 6; i++) { const tr: HTMLElement = add('tr'); for (let j = 0; j < 7; j++) { const td: HTMLElement = add('td'); if (i === 0 && j < firstDay) { td.classList.add('is--empty'); } else if (day > allDays) { break; } else { td.innerHTML = day.toString(); if (year === defaultStartYear && month === defaultStartMonth && day === defaultStartDay) { td.classList.add('is--today'); } day++; } tr.append(td); } bodyEl?.append(tr); } }; const addListeners = ( startYear: number, startMonth: number, translations: CalendarWidgetStringOrArrayOfStrings ): void => { onClick('#ts-calendar__prev', (event: MouseEvent) => { event.preventDefault(); const isFirstMonth: boolean = startMonth === 0; startMonth = isFirstMonth ? 11 : startMonth - 1; startYear = isFirstMonth ? startYear - 1 : startYear; renderCalendar(startYear, startMonth, selectors, translations); }); onClick('#ts-calendar__next', (event: MouseEvent) => { event.preventDefault(); const isLastMonth: boolean = startMonth === 11; startMonth = isLastMonth ? 0 : startMonth + 1; startYear = isLastMonth ? startYear + 1 : startYear; renderCalendar(startYear, startMonth, selectors, translations); }); }; const defaultTranslations: CalendarWidgetStringOrArrayOfStrings = { days: defaultDays, months: defaultMonths, prevMonth: '&larr;', nextMonth: '&rarr;', prevMonthTitle: 'Previous month', nextMonthTitle: 'Next month' }; export const renderCalendarWidget = ({ startYear = defaultStartYear, startMonth = defaultStartMonth, container = 'body', translations = defaultTranslations }: CalendarWidgetOptions): void => { const el = get(container); if (el) { el.innerHTML = renderTable(translations); } renderCalendar(startYear, startMonth, selectors, translations); addListeners(startYear, startMonth, translations); };