UNPKG

@renderlesskit/react

Version:

Collection of headless components/hooks that are accessible, composable, customizable from low level to build your own UI & Design System powered by Reakit

183 lines (158 loc) 5.37 kB
/** * All credit goes to [React Spectrum](https://github.com/adobe/react-spectrum) * We improved the Calendar from Stately [useCalendarState](https://github.com/adobe/react-spectrum/tree/main/packages/%40react-stately/calendar) * to work with Reakit System */ import * as React from "react"; import { unstable_useId as useId } from "reakit"; import { useUpdateEffect } from "@chakra-ui/hooks"; import { useDateFormatter } from "@react-aria/i18n"; import { addDays, addMonths, addWeeks, addYears, endOfMonth, getDaysInMonth, isSameMonth, startOfDay, startOfMonth, subDays, subMonths, subWeeks, subYears, toUTCString, useControllableState } from "../utils"; import { announce } from "../utils/LiveAnnouncer"; import { generateDaysInMonthArray, useWeekDays, useWeekStart } from "./helpers"; export function useCalendarState() { var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var { value: initialValue, defaultValue = toUTCString(new Date()), onChange, minValue, maxValue, isDisabled = false, isReadOnly = false, autoFocus = false } = props; var [value, setValue] = useControllableState({ value: initialValue, defaultValue, onChange }); var date = React.useMemo(() => new Date(value), [value]); var minDateValue = React.useMemo(() => minValue ? new Date(minValue) : new Date(-864e13), [minValue]); var maxDateValue = React.useMemo(() => maxValue ? new Date(maxValue) : new Date(864e13), [maxValue]); var [currentMonth, setCurrentMonth] = React.useState(date); var [focusedDate, setFocusedDate] = React.useState(date); var [isFocused, setFocused] = React.useState(autoFocus); var month = currentMonth.getMonth(); var year = currentMonth.getFullYear(); var weekStart = useWeekStart(); var weekDays = useWeekDays(weekStart); var monthStartsAt = (startOfMonth(currentMonth).getDay() - weekStart) % 7; if (monthStartsAt < 0) { monthStartsAt += 7; } var days = getDaysInMonth(currentMonth); var weeksInMonth = Math.ceil((monthStartsAt + days) / 7); // Get 2D Date arrays in 7 days a week format var daysInMonth = React.useMemo(() => generateDaysInMonthArray(month, monthStartsAt, weeksInMonth, year), [month, monthStartsAt, weeksInMonth, year]); function isInvalidDateRange(value) { var min = new Date(minDateValue); var max = new Date(maxDateValue); return value < min || value > max; } // Sets focus to a specific cell date function focusCell(date) { if (isInvalidDateRange(date)) return; if (!isSameMonth(date, currentMonth)) { setCurrentMonth(startOfMonth(date)); } setFocusedDate(date); } var dateFormatter = useDateFormatter({ dateStyle: "full" }); var announceSelectedDate = React.useCallback(value => { if (!value) return; announce("Selected Date: ".concat(dateFormatter.format(value))); }, [dateFormatter]); var setDate = React.useCallback(value => { if (!isDisabled && !isReadOnly) { setValue(toUTCString(value)); announceSelectedDate(value); } }, [announceSelectedDate, isDisabled, isReadOnly, setValue]); // TODO // This runs only once when the component is mounted // Controlled state doesn't change the claender position // React.useEffect(() => { // const clampedDate = clamp(date, { // start: minDateValue, // end: maxDateValue, // }); // setDate(clampedDate); // setCurrentMonth(clampedDate); // setFocusedDate(clampedDate); // }, [date, maxDateValue, minDateValue, setDate]); var monthFormatter = useDateFormatter({ month: "long", year: "numeric" }); // Announce when the current month changes useUpdateEffect(() => { // announce the new month with a change from the Previous or Next button if (!isFocused) { announce(monthFormatter.format(currentMonth)); } // handle an update to the current month from the Previous or Next button // rather than move focus, we announce the new month value }, [currentMonth]); var { id: calendarId } = useId({ id: props.id, baseId: "calendar" }); return { dateValue: date, setDateValue: setDate, calendarId, month, year, weekStart, weekDays, daysInMonth, isDisabled, isFocused, isReadOnly, setFocused, currentMonth, setCurrentMonth, focusedDate, focusCell, setFocusedDate, focusNextDay() { focusCell(addDays(focusedDate, 1)); }, focusPreviousDay() { focusCell(subDays(focusedDate, 1)); }, focusNextWeek() { focusCell(addWeeks(focusedDate, 1)); }, focusPreviousWeek() { focusCell(subWeeks(focusedDate, 1)); }, focusNextMonth() { focusCell(addMonths(focusedDate, 1)); }, focusPreviousMonth() { focusCell(subMonths(focusedDate, 1)); }, focusStartOfMonth() { focusCell(startOfMonth(focusedDate)); }, focusEndOfMonth() { focusCell(endOfMonth(startOfDay(focusedDate))); }, focusNextYear() { focusCell(addYears(focusedDate, 1)); }, focusPreviousYear() { focusCell(subYears(focusedDate, 1)); }, selectFocusedDate() { setDate(focusedDate); }, selectDate(date) { setDate(date); }, isInvalidDateRange, isRangeCalendar: false }; } //# sourceMappingURL=CalendarState.js.map