UNPKG

@drivy/cobalt

Version:

Opinionated design system for Drivy's projects.

206 lines (203 loc) 11.1 kB
import React, { useMemo, useState, useEffect } from 'react'; import { CalendarRangePickerDay as MemoizedComponent } from './CalendarRangePickerDay.js'; import cx from 'classnames'; import { format, isSameDay, differenceInCalendarDays } from 'date-fns'; import { getMonthDaysByWeeks, getWeekDays } from '../utils.js'; function isDisabled(day, firstAvailableDate, lastAvailableDate) { return (day.getTime() < firstAvailableDate.getTime() || (lastAvailableDate != null && day.getTime() > lastAvailableDate.getTime())); } function isInvalidOppositeBound({ isEditingStartDate, isEditingEndDate, isStartDay, isEndDay, startDate, endDate, }) { return (startDate != null && endDate != null && startDate > endDate && ((isEditingStartDate && isEndDay) || (isEditingEndDate && isStartDay))); } function isDayOutOfRange({ day, rangeConstraints, startDate, }) { return (rangeConstraints != null && rangeConstraints.max != null && startDate != null && differenceInCalendarDays(day, startDate) >= rangeConstraints.max.value); } function isDayInvalid({ day, isDaySelected, rangeConstraints, rangeTooShort, rangeExceeded, isEditingStartDate, isEditingEndDate, isStartDay, isEndDay, isDayInvalidForSelectionFn, startDate, endDate, }) { if (!isDaySelected) return false; let isInvalid = false; if (!isEditingStartDate && !isEditingEndDate) { isInvalid = rangeTooShort || rangeExceeded; } else if (rangeTooShort && (!isStartDay || isEndDay)) { isInvalid = true; } else if (isInvalidOppositeBound({ isEditingStartDate, isEditingEndDate, isStartDay, isEndDay, startDate, endDate, })) { isInvalid = true; } else { isInvalid = isDayOutOfRange({ day, rangeConstraints, startDate, }); } if (!isInvalid && isDayInvalidForSelectionFn && ((isEditingStartDate && isStartDay) || (isEditingEndDate && isEndDay))) { isInvalid = isDayInvalidForSelectionFn(day); } return isInvalid; } function isSelected({ day, startDate, endDate, isStartDay, isEndDay, isOutOfRangeStartDate, rangeTooShort, isEditingStartDate, isEditingEndDate, }) { return !!(isStartDay || isEndDay || ((!rangeTooShort || (!isEditingStartDate && !isEditingEndDate)) && !isOutOfRangeStartDate && endDate && day.getTime() <= endDate.getTime() && startDate && day.getTime() > startDate.getTime())); } function isRangeExceeded(rangeInDays, rangeLimit) { return rangeLimit != null && rangeInDays > rangeLimit; } function isRangeTooShort(rangeInDays, rangeLimit) { return rangeInDays <= 0 ? false : rangeLimit != null && rangeInDays < rangeLimit; } function buildTooltipMessage({ rangeInDays, isEditingStartDate, isStartDay, isEditingEndDate, isEndDay, rangeConstraints, }) { var _a, _b, _c, _d; let tooltipMessage; if (rangeConstraints && rangeInDays >= 0 && ((isEditingStartDate && isStartDay) || (isEditingEndDate && isEndDay))) { if (isRangeTooShort(rangeInDays, (_a = rangeConstraints.min) === null || _a === void 0 ? void 0 : _a.value)) { tooltipMessage = (_b = rangeConstraints === null || rangeConstraints === void 0 ? void 0 : rangeConstraints.min) === null || _b === void 0 ? void 0 : _b.message; } else if (isRangeExceeded(rangeInDays, (_c = rangeConstraints.max) === null || _c === void 0 ? void 0 : _c.value)) { tooltipMessage = (_d = rangeConstraints === null || rangeConstraints === void 0 ? void 0 : rangeConstraints.max) === null || _d === void 0 ? void 0 : _d.message; } } return tooltipMessage; } function getRangeInDays(range) { return range.startDate && range.endDate ? differenceInCalendarDays(range.endDate, range.startDate) + 1 : 0; } function CalendarRangePickerMonth({ date, onSelectDate, onChangeDate, onLeaveDate, startDate, endDate, isEditingStartDate = false, isEditingEndDate = false, firstAvailableDate, lastAvailableDate, isDayDisabledFn, isDayInvalidForSelectionFn, hasDayNotificationFn, dayHoverTooltipFn, rangeConstraints, isSundayFirstDayOfWeek, locale, }) { var _a, _b; const byWeeks = useMemo(() => getMonthDaysByWeeks(date, isSundayFirstDayOfWeek), []); // we use a state for the selected Date to allow the onSelect event to be memoized then still triggering some logic with updated data thanks to the setter+useEffect dependency mechanism const [selectedDate, setSelectedDate] = useState(null); const today = new Date().setHours(0, 0, 0, 0); const weekdays = useMemo(() => getWeekDays(isSundayFirstDayOfWeek), []); const rangeInDays = getRangeInDays({ startDate, endDate }); const rangeExceeded = isRangeExceeded(rangeInDays, (_a = rangeConstraints === null || rangeConstraints === void 0 ? void 0 : rangeConstraints.max) === null || _a === void 0 ? void 0 : _a.value); const rangeTooShort = isRangeTooShort(rangeInDays, (_b = rangeConstraints === null || rangeConstraints === void 0 ? void 0 : rangeConstraints.min) === null || _b === void 0 ? void 0 : _b.value); const isOutOfRangeStartDate = isEditingStartDate && rangeExceeded; const onMouseEnter = (day, disabled) => { var _a, _b; const status = {}; if (disabled) { status.isDisabled = true; } if (!disabled) { const currentRange = isEditingStartDate ? { startDate: day, endDate: endDate } : { startDate: startDate, endDate: day }; const currentRangeInDays = getRangeInDays({ startDate: currentRange.startDate, endDate: currentRange.endDate, }); const currentRangeExceeded = isRangeExceeded(currentRangeInDays, (_a = rangeConstraints === null || rangeConstraints === void 0 ? void 0 : rangeConstraints.max) === null || _a === void 0 ? void 0 : _a.value); if (currentRangeExceeded) { status.isRangeExceeded = true; } else { const currentRangeTooShort = isRangeTooShort(currentRangeInDays, (_b = rangeConstraints === null || rangeConstraints === void 0 ? void 0 : rangeConstraints.min) === null || _b === void 0 ? void 0 : _b.value); if (currentRangeTooShort) { status.isRangeTooShort = true; } else { status.isCustomInvalid = isDayInvalidForSelectionFn != null && isDayInvalidForSelectionFn(day); } } } onChangeDate(day, status); }; useEffect(() => { var _a, _b; // on Date selection... if (selectedDate) { if (isEditingStartDate) { onSelectDate(selectedDate, isRangeExceeded(getRangeInDays({ startDate: selectedDate, endDate }), (_a = rangeConstraints === null || rangeConstraints === void 0 ? void 0 : rangeConstraints.max) === null || _a === void 0 ? void 0 : _a.value)); } else { onSelectDate(selectedDate, isRangeExceeded(getRangeInDays({ startDate, endDate: selectedDate }), (_b = rangeConstraints === null || rangeConstraints === void 0 ? void 0 : rangeConstraints.max) === null || _b === void 0 ? void 0 : _b.value)); } } }, [selectedDate]); return (React.createElement("div", { className: cx("cobalt-CalendarRangePicker__month", { "cobalt-CalendarRangePicker__month--invalid": rangeExceeded && !isEditingStartDate, }), "data-month": format(date, "yyyy-MM") }, React.createElement("div", { className: "cobalt-CalendarRangePicker__month-header" }, format(date, "MMMM yyyy", { locale })), React.createElement("div", { className: "cobalt-CalendarRangePicker__month__weeks-container" }, React.createElement("div", { className: "cobalt-CalendarRangePicker__month__week-header" }, weekdays.map((weekday) => (React.createElement("div", { key: weekday.getTime(), className: "cobalt-CalendarRangePicker__month__day-header" }, format(weekday, "iiiiii", { locale }))))), byWeeks.map((week, index) => (React.createElement("div", { key: index, className: "cobalt-CalendarRangePicker__month__week" }, week.map((day) => { const isStartDay = startDate != null && isSameDay(day, startDate); const isEndDay = endDate != null && isSameDay(day, endDate); const disabled = isDisabled(day, firstAvailableDate, lastAvailableDate) || (isDayDisabledFn && isDayDisabledFn(day)); const hasNotification = !disabled && hasDayNotificationFn != null && hasDayNotificationFn(day); const isRange = rangeInDays !== 0 && (isStartDay || isEndDay) && (!rangeTooShort || (!isEditingStartDate && !isEditingEndDate)); const isDaySelected = isSelected({ day, startDate, endDate, isStartDay, isEndDay, isOutOfRangeStartDate, rangeTooShort, isEditingStartDate, isEditingEndDate, }); const tooltip = buildTooltipMessage({ rangeInDays, isEditingStartDate: !!isEditingStartDate, isEditingEndDate: !!isEditingEndDate, isStartDay, isEndDay, rangeConstraints, }); return (React.createElement(MemoizedComponent, { key: day.getTime(), date: day, onMouseEnter: onMouseEnter, onMouseLeave: onLeaveDate, onSelect: setSelectedDate, isToday: isSameDay(day, today), isActive: (isEditingStartDate && isStartDay) || (isEditingEndDate && isEndDay), isStartDay: isStartDay, isEndDay: isEndDay, isRange: isRange, isEditingStartDate: isEditingStartDate, isEditingEndDate: isEditingEndDate, isInvalid: isDayInvalid({ day, isDayInvalidForSelectionFn, isDaySelected, startDate, rangeConstraints, rangeTooShort, rangeExceeded, isEditingStartDate, isEditingEndDate, isStartDay, isEndDay, endDate, }), hasNotification: hasNotification, isDisabled: disabled, tooltipMessage: tooltip, hoverTooltipMessage: dayHoverTooltipFn ? dayHoverTooltipFn(day) : undefined, isSelected: isDaySelected })); }))))))); } export { CalendarRangePickerMonth }; //# sourceMappingURL=CalendarRangePickerMonth.js.map