import classNames from 'classnames' import { formatISODate, newDateYMD, formatReadableDate } from 'shared-utils/date-utils' import type { TDateRangeMap } from 'shared-types/calendar' import type { TDayViewData } from './types' export interface IDayViewContext { year: number month: number activeSelected: string[] focusedDate: string | null // Side-effect ref: tracks which day gets tabindex="0" (first non-disabled day) tabIndexSetRef: React.MutableRefObject isDayDisabled: (date: Date, isSelected: boolean) => boolean } export interface IDayCellClassContext { range: boolean activeSelected: string[] rangeHovered: Date | null inRange: TDateRangeMap isExcluded: (date: Date) => boolean } export function getDayViewData(dayCounter: number, today: Date, ctx: IDayViewContext): TDayViewData { const currentDate = newDateYMD(ctx.year, ctx.month, dayCounter) const currentDateISO = formatISODate(currentDate) const isToday = currentDateISO === formatISODate(today) const isSelected = ctx.activeSelected.includes(currentDateISO) const isDisabledVal = ctx.isDayDisabled(currentDate, isSelected) let tabindex: string if (ctx.focusedDate) { tabindex = ctx.focusedDate === currentDateISO && !isDisabledVal ? '0' : '-1' } else if (!isDisabledVal && ctx.tabIndexSetRef.current === 0) { ctx.tabIndexSetRef.current = dayCounter tabindex = '0' } else { tabindex = ctx.tabIndexSetRef.current === dayCounter ? '0' : '-1' } return { currentDate, currentDateISO, isToday, isSelected, isDisabled: isDisabledVal, ariaLabel: formatReadableDate(currentDate), tabindex, } } export function getDayCellClasses(data: TDayViewData, ctx: IDayCellClassContext): string { const { currentDateISO, isToday, isSelected } = data const isRangeStart = ctx.range && (ctx.activeSelected.length === 2 || ctx.rangeHovered !== null) && currentDateISO === ctx.activeSelected[0] const isRangeEnd = ctx.range && ctx.activeSelected.length === 2 && currentDateISO === ctx.activeSelected[1] return classNames({ 'pkt-cal-today': isToday, 'pkt-cal-selected': isSelected, 'pkt-cal-in-range': ctx.inRange[currentDateISO], 'pkt-cal-excluded': ctx.isExcluded(data.currentDate), 'pkt-cal-in-range-first': isRangeStart, 'pkt-cal-in-range-last': isRangeEnd, 'pkt-cal-range-hover': ctx.rangeHovered !== null && currentDateISO === formatISODate(ctx.rangeHovered), }) } export function getDayButtonClasses(data: TDayViewData, ctx: IDayCellClassContext): string { const { currentDateISO, isToday, isSelected, isDisabled: isDisabledVal } = data const isRangeStart = ctx.range && (ctx.activeSelected.length === 2 || ctx.rangeHovered !== null) && currentDateISO === ctx.activeSelected[0] const isRangeEnd = ctx.range && ctx.activeSelected.length === 2 && currentDateISO === ctx.activeSelected[1] return classNames({ 'pkt-calendar__date': true, 'pkt-calendar__date--today': isToday, 'pkt-calendar__date--selected': isSelected, 'pkt-calendar__date--disabled': isDisabledVal, 'pkt-calendar__date--in-range': ctx.inRange[currentDateISO], 'pkt-calendar__date--in-range-hover': ctx.rangeHovered !== null && currentDateISO === formatISODate(ctx.rangeHovered), 'pkt-calendar__date--range-start': isRangeStart, 'pkt-calendar__date--range-end': isRangeEnd, }) }