import { classNames, create, createEventRegistrar, text, } from "@hesxenon/prelude/Dom.js"; import { flow, iife } from "@hesxenon/prelude/Function.js"; import { addDays, addMonths, getWeekWithOptions } from "date-fns/fp"; import { update } from "../../utils/Date"; import { Events } from "../events"; import { getMonthName, getWeeDayNames } from "../format"; import { connectSelection } from "../parts/selection"; import type { Connect, View } from "../uwc-datepicker"; const getStartOfPage = update((date) => { date.setDate(1); }); const getEndOfPage = flow( getStartOfPage, update((date) => { date.setMonth(date.getMonth() + 1); }), ); const connectGrid: Connect = (datepicker, opts) => { const on = createEventRegistrar(datepicker, opts.disconnect); const enWeekDayNames = getWeeDayNames( "en", datepicker["start-of-week"], "short", ); const localizedWeekDayNames = getWeeDayNames( datepicker.locale, datepicker["start-of-week"], "short", ); const localizedA11yWeekDayNames = getWeeDayNames( datepicker.locale, datepicker["start-of-week"], "long", ); const createGrid = () => { const days = create("ul", { role: "grid" }); days.append( create("li", { className: "placeholder", }), ); localizedWeekDayNames.forEach((name, i) => days.append( create( "li", { className: `weekday ${enWeekDayNames[i]!.toLowerCase()}`, title: localizedA11yWeekDayNames[i], ariaLabel: localizedA11yWeekDayNames[i], }, [text(name)], ), ), ); const now = new Date(); const start = iife(() => { const firstOfPage = getStartOfPage(datepicker.anchorDate); // find the previous start of the week return addDays( datepicker["start-of-week"] === "sunday" ? -firstOfPage.getDay() : -((firstOfPage.getDay() + 6) % 7), // add a full week - monday offset but limit to 7 days firstOfPage, ); }); const end = iife(() => { const lastOfPage = getEndOfPage(datepicker.anchorDate); // find the next end of the week return addDays( 7 - (lastOfPage.getDay() + (datepicker["start-of-week"] === "monday" ? 0 : 1)), lastOfPage, ); }); let cursor = start; while (cursor <= end) { const next = addDays(1, cursor); if ( cursor.getDay() === (datepicker["start-of-week"] === "monday" ? 1 : 0) ) { days.append( create( "li", { className: "weeknumber", }, [ text( getWeekWithOptions( { weekStartsOn: datepicker["start-of-week"] === "monday" ? 1 : 0, }, cursor, ), ), ], ), ); } const dataValue = cursor.toISOString(); days.append( create( "li", { "data-value": dataValue, className: classNames({ "previous-month": cursor < getStartOfPage(datepicker.anchorDate), "next-month": cursor > getEndOfPage(datepicker.anchorDate), current: cursor <= now && now < next, }), }, [text(cursor.getDate())], ), ); cursor = next; } return days; }; let grid = createGrid(); on(Events["anchor-dateChanged"], () => { grid.remove(); grid = createGrid(); datepicker.append(grid); }); datepicker.append(grid); }; export const dayView: View = { addOneUnit: addDays(1), connect: (datepicker, opts) => { connectSelection(datepicker, { addPages: addMonths, leapPages: 12, getStartOfPage, getLabel: (current, format) => `${getMonthName(current, datepicker.locale, format)} ${current.getFullYear()}`, disconnect: opts.disconnect, }); connectGrid(datepicker, opts); }, };