@drivy/cobalt
Version:
Opinionated design system for Drivy's projects.
206 lines (203 loc) • 11.1 kB
JavaScript
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