@codecademy/gamut
68.2.268.2.3-alpha.794432.0
dist/DatePicker/Calendar/CalendarBody.js+
dist/DatePicker/Calendar/CalendarBody.jsNew file+143
Index: package/dist/DatePicker/Calendar/CalendarBody.js
===================================================================
--- package/dist/DatePicker/Calendar/CalendarBody.js
+++ package/dist/DatePicker/Calendar/CalendarBody.js
@@ -0,0 +1,143 @@
+import { useCallback, useEffect, useMemo, useRef } from 'react';
+import * as React from 'react';
+import { useIsoFirstWeekday, useResolvedLocale } from '../utils/locale';
+import { getDatesWithRow, getMonthGrid, isDateDisabled, isDateInRange, isSameDay } from './utils/dateGrid';
+import { CalendarTable, DateCell, TableHeader } from './utils/elements';
+import { formatDateForAriaLabel, getWeekdayNames } from './utils/format';
+import { keyHandler } from './utils/keyHandler';
+import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
+export const CalendarBody = ({
+ displayDate,
+ selectedDate,
+ endDate = null,
+ disabledDates = [],
+ onDateSelect,
+ locale,
+ weekStartsOn,
+ labelledById,
+ focusedDate,
+ onFocusedDateChange,
+ onDisplayDateChange,
+ onEscapeKeyPress,
+ hasAdjacentMonthRight,
+ hasAdjacentMonthLeft,
+ focusGridSync
+}) => {
+ const resolvedLocale = useResolvedLocale(locale);
+ const firstWeekday = useIsoFirstWeekday(resolvedLocale, weekStartsOn);
+ const year = displayDate.getFullYear();
+ const month = displayDate.getMonth();
+ const weeks = getMonthGrid(year, month, firstWeekday);
+ const weekdayLabels = getWeekdayNames('short', resolvedLocale, firstWeekday);
+ const weekdayFullNames = getWeekdayNames('long', resolvedLocale, firstWeekday);
+ const buttonRefs = useRef(new Map());
+ const tableRef = useRef(null);
+ const datesWithRow = useMemo(() => getDatesWithRow(weeks), [weeks]);
+ const focusTarget = focusedDate ?? selectedDate;
+ const isToday = useCallback(date => date !== null && isSameDay(date, new Date()), []);
+ const focusButton = useCallback(date => {
+ if (date === null) return false;
+ const key = new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();
+ const el = buttonRefs.current.get(key);
+ if (!el) return false;
+ el.focus();
+ return true;
+ }, []);
+ useEffect(() => {
+ // Keep the roving tabindex / focused day aligned with `focusTarget` when it makes sense for a11y.
+ if (focusTarget === null) return;
+
+ // Standalone calendar (e.g. Storybook): always move DOM focus to the active day.
+ if (!focusGridSync) {
+ focusButton(focusTarget);
+ return;
+ }
+ const inGrid = tableRef.current?.contains(document.activeElement);
+ const requested = focusGridSync.gridFocusRequested;
+
+ // Focus is already in this grid (keyboard nav): update which day is focused as `focusTarget` changes.
+ if (inGrid) {
+ focusButton(focusTarget);
+ return;
+ }
+
+ // DatePicker opened via keyboard / ArrowDown: parent asked to move focus into the grid once.
+ if (requested) {
+ const success = focusButton(focusTarget);
+ if (success) {
+ focusGridSync.onGridFocusRequestHandled();
+ }
+ }
+ // If !inGrid && !requested (e.g. calendar opened with the mouse): leave focus on the input — do not call focusButton.
+ }, [focusTarget, focusButton, focusGridSync]);
+ const handleKeyDown = useCallback((e, date) => keyHandler({
+ e,
+ date,
+ onFocusedDateChange,
+ datesWithRow,
+ month,
+ year,
+ disabledDates,
+ onDateSelect,
+ onEscapeKeyPress,
+ onDisplayDateChange,
+ hasAdjacentMonthRight,
+ hasAdjacentMonthLeft
+ }), [onFocusedDateChange, datesWithRow, month, year, disabledDates, onDateSelect, onEscapeKeyPress, onDisplayDateChange, hasAdjacentMonthLeft, hasAdjacentMonthRight]);
+ const setButtonRef = useCallback((date, el) => {
+ const k = new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();
+ if (el) buttonRefs.current.set(k, el);else buttonRefs.current.delete(k);
+ }, []);
+ return /*#__PURE__*/_jsxs(CalendarTable, {
+ "aria-labelledby": labelledById,
+ ref: tableRef,
+ role: "grid",
+ children: [/*#__PURE__*/_jsx("thead", {
+ children: /*#__PURE__*/_jsx("tr", {
+ children: weekdayLabels.map((label, i) => /*#__PURE__*/_jsx(TableHeader, {
+ abbr: weekdayFullNames[i],
+ scope: "col",
+ children: label
+ }, label))
+ })
+ }), /*#__PURE__*/_jsx("tbody", {
+ children: weeks.map((week, rowIndex) => /*#__PURE__*/_jsx("tr", {
+ children: week.map((date, colIndex) => {
+ if (date === null) {
+ return (
+ /*#__PURE__*/
+ // eslint-disable-next-line jsx-a11y/control-has-associated-label
+ _jsx("td", {
+ role: "gridcell"
+ }, `empty-${rowIndex}-${colIndex}`)
+ );
+ }
+ const selected = isSameDay(date, selectedDate) || isSameDay(date, endDate);
+ const range = !!selectedDate && !!endDate;
+ const inRange = range && isDateInRange(date, selectedDate, endDate);
+ const disabled = isDateDisabled(date, disabledDates);
+ const today = isToday(date);
+ // this is making the selected date a differnet color bc it is focused, look into further
+ const isFocused = focusTarget !== null && isSameDay(date, focusTarget);
+ return /*#__PURE__*/_jsx(DateCell, {
+ "aria-label": formatDateForAriaLabel(date, resolvedLocale),
+ "aria-selected": selected,
+ isDisabled: disabled,
+ isInRange: inRange,
+ isRangeEnd: range && isSameDay(date, endDate),
+ isRangeStart: range && isSameDay(date, selectedDate),
+ isSelected: selected,
+ isToday: today,
+ ref: el => setButtonRef(date, el),
+ role: "gridcell",
+ tabIndex: isFocused ? 0 : -1,
+ onClick: () => onDateSelect(date),
+ onFocus: () => onFocusedDateChange?.(date),
+ onKeyDown: e => handleKeyDown(e, date),
+ children: date.getDate()
+ }, date.getTime());
+ })
+ }, week.join('-')))
+ })]
+ });
+};
\ No newline at end of file