@activecollab/components
Version:
ActiveCollab Components
270 lines (268 loc) • 10.3 kB
JavaScript
import React, { useCallback, useMemo, useState, useEffect } from "react";
import classNames from "classnames";
import moment from "moment-timezone";
import { StyledButton, StyledButtonGroup, StyledDiv, StyledSpan, StyledControl } from "./Styles";
import { DatePicker } from "../../DatePicker/DatePicker";
import { ArrowRightIcon } from "../../Icons";
import ArrowLeftIcon from "../../Icons/collection/ArrowLeft";
/**
* This component allow you to select date range. It accepts two parameters "from" and "to" as valid format
* onChange and onBack and onForward it will return "from: Date" and "to: Date" as function arguments
*/
export const DateStepper = _ref => {
let {
step,
formatCallback,
onChange,
from,
to,
onBack,
onForward,
minValue,
maxValue,
className,
period = "week",
withDatePicker = true,
defaultMonth = moment(),
enableYearPicker,
position = "bottom",
popperClassName,
mode = "outlined",
alwaysShowDate = false,
disableArrows = false
} = _ref;
const [fromDate, setFromDate] = useState(moment.utc(from));
const [toDate, setToDate] = useState(moment.utc(to));
const getDatesByPeriod = useCallback(interval => {
const date = fromDate ? fromDate : moment();
if (step === "daily") {
date.add(interval, "days");
return [date, date.clone()];
} else if (step === "weekly") {
date.add(interval, "weeks");
return [date.clone().startOf(period), date.clone().endOf(period)];
} else if (step === "monthly") {
date.add(interval, "months");
return [date.clone().startOf("month"), date.clone().endOf("month")];
} else if (step === "quarterly") {
date.add(interval, "quarters");
return [date.clone().startOf("quarter"), date.clone().endOf("quarter")];
} else if (step === "yearly") {
date.add(interval, "years");
return [date.clone().startOf("year"), date.clone().endOf("year")];
} else if (step === "range") {
// For range step, calculate N days based on current selection
// Handle case where toDate is invalid or at epoch (1970)
const isToDateInvalid = !toDate.isValid() || toDate.year() === 1970;
const effectiveToDate = isToDateInvalid ? fromDate : toDate;
const daysDiff = effectiveToDate.diff(fromDate, "days");
// If same day selected (daysDiff = 0), move by 1 day
const daysToMove = daysDiff === 0 ? 1 : daysDiff + 1;
const newFromDate = fromDate.clone().add(interval * daysToMove, "days");
const newToDate = daysDiff === 0 ? newFromDate.clone() : effectiveToDate.clone().add(interval * daysToMove, "days");
return [newFromDate, newToDate];
}
return [date, date];
}, [fromDate, toDate, period, step]);
const getDateFormatted = useMemo(() => {
if (formatCallback) {
return formatCallback(fromDate, toDate);
}
const actualYear = moment().year();
const showWeekOrCustomDateFormat = (start, end) => {
if (start.year() === actualYear && end.year() === actualYear) {
return start.utc().format("MMM DD") + " - " + end.utc().format("MMM DD");
} else {
return start.utc().format("MMM DD YYYY") + " - " + end.utc().format("MMM DD YYYY");
}
};
if (step === "daily") {
if (fromDate.year() === actualYear) {
return fromDate.utc().format("MMM DD");
}
return fromDate.utc().format("MMM DD YYYY");
} else if (step === "weekly") {
const startDate = fromDate.clone().utc().startOf(period);
const endDate = startDate.clone().utc().endOf(period);
return showWeekOrCustomDateFormat(startDate, endDate);
} else if (step === "monthly") {
return fromDate.utc().format("MMM YYYY");
} else if (step === "quarterly") {
return "Q" + fromDate.utc().quarter() + "/" + fromDate.utc().format("YYYY");
} else if (step === "yearly") {
return fromDate.utc().format("YYYY");
} else if (step === "range") {
// For range step, if toDate is invalid or at epoch (1970), only show fromDate
if (!toDate.isValid() || toDate.year() === 1970) {
if (fromDate.year() === actualYear) {
return fromDate.utc().format("MMM DD");
}
return fromDate.utc().format("MMM DD YYYY");
}
// If from and to are the same date, show only once
if (fromDate.format("YYYY-MM-DD") === toDate.format("YYYY-MM-DD")) {
if (fromDate.year() === actualYear) {
return fromDate.utc().format("MMM DD");
}
return fromDate.utc().format("MMM DD YYYY");
}
return showWeekOrCustomDateFormat(fromDate, toDate);
}
return "";
}, [fromDate, toDate, step, formatCallback, period]);
const datePickerMode = useMemo(() => {
if (step === "range") return "custom";
if (step === "yearly") return "custom";
return step;
}, [step]);
const onDatePickerChange = useCallback(dates => {
const fromDateTemp = moment.utc(Number(dates == null ? void 0 : dates.from) * 1000);
const toDateTemp = moment.utc(Number(dates == null ? void 0 : dates.to) * 1000);
setFromDate(fromDateTemp);
setToDate(toDateTemp);
if (onChange && moment.isMoment(fromDateTemp) && moment.isMoment(toDateTemp)) {
onChange(fromDateTemp.toDate(), toDateTemp.toDate());
}
}, [onChange, setFromDate, setToDate]);
const isAfterMaxDate = useMemo(() => {
return moment.isMoment(maxValue) && moment(moment.utc(maxValue).format("YYYY-MM-DD")).isSameOrBefore(toDate.utc().format("YYYY-MM-DD"));
}, [maxValue, toDate]);
const isBeforeMinDate = useMemo(() => {
return moment.isMoment(minValue) && moment.utc(moment.utc(minValue).format("YYYY-MM-DD")).isSameOrAfter(toDate.utc().format("YYYY-MM-DD"));
}, [minValue, toDate]);
useEffect(() => {
if (isBeforeMinDate) {
if (step === "daily") {
setFromDate(moment.utc(minValue));
setToDate(moment.utc(minValue));
}
}
if (isAfterMaxDate) {
if (step === "daily") {
setFromDate(moment.utc(maxValue));
setToDate(moment.utc(maxValue));
}
}
}, [step, minValue, maxValue, isBeforeMinDate, isAfterMaxDate]);
const onRightClickHandler = useCallback(() => {
if (isAfterMaxDate) {
return;
}
const [newFrom, newTo] = getDatesByPeriod(1);
setFromDate(moment.utc(newFrom));
setToDate(moment.utc(newTo));
if (onChange) {
onChange(moment.utc(newFrom).toDate(), moment.utc(newTo).toDate());
}
if (onForward) {
onForward(moment.utc(newFrom).toDate(), moment.utc(newTo).toDate());
}
}, [isAfterMaxDate, getDatesByPeriod, onChange, onForward]);
const onLeftClickHandler = useCallback(() => {
if (isBeforeMinDate) {
return;
}
const [newFrom, newTo] = getDatesByPeriod(-1);
setFromDate(moment.utc(newFrom));
setToDate(moment.utc(newTo));
if (onChange) {
onChange(moment.utc(newFrom).toDate(), moment.utc(newTo).toDate());
}
if (onBack) {
onBack(moment.utc(newFrom).toDate(), moment.utc(newTo).toDate());
}
}, [isBeforeMinDate, getDatesByPeriod, onChange, onBack]);
useEffect(() => {
setFromDate(moment.utc(from));
setToDate(moment.utc(to));
}, [from, to]);
const dateStepperWidth = (fromDay, toDay, range) => {
if (fromDay.utc().year() !== moment.utc().year() || toDay.utc().year() !== moment.utc().year()) {
switch (range) {
case "daily":
case "monthly":
return {
minWidth: "122px"
};
case "weekly":
case "custom":
return {
minWidth: "232px"
};
}
}
switch (range) {
case "monthly":
return {
minWidth: "122px"
};
case "weekly":
case "custom":
return {
minWidth: "182px"
};
}
return {
minWidth: "102px"
};
};
const isDisabled = useCallback(day => {
const dayFormat = moment.utc(day).format("YYYY-MM-DD");
const isBefore = moment.isMoment(maxValue) && moment(moment.utc(maxValue).format("YYYY-MM-DD")).isBefore(dayFormat);
const isAfter = moment.isMoment(minValue) && moment(moment.utc(minValue).format("YYYY-MM-DD")).isAfter(dayFormat);
return isBefore || isAfter;
}, [maxValue, minValue]);
const modifiers = useMemo(() => ({
disabled: day => ({
matched: isDisabled(day),
title: null
}),
nonWorkingDay: day => ({
matched: isDisabled(day),
title: null
})
}), [isDisabled]);
return /*#__PURE__*/React.createElement(StyledButtonGroup, {
className: classNames("c-date-stepper", className)
}, !disableArrows ? /*#__PURE__*/React.createElement(StyledControl, {
type: "button",
variant: mode === "flat" ? "text gray" : "secondary",
onClick: onLeftClickHandler,
disabled: isBeforeMinDate
}, /*#__PURE__*/React.createElement(ArrowLeftIcon, null)) : null, /*#__PURE__*/React.createElement(StyledDiv, {
$isTargetable: step === "yearly" || !withDatePicker,
$isRounded: disableArrows,
$mode: mode,
$alwaysShowDate: alwaysShowDate,
style: dateStepperWidth(fromDate, toDate, step)
}, withDatePicker && step !== "yearly" ? /*#__PURE__*/React.createElement(DatePicker, {
month: defaultMonth,
target: /*#__PURE__*/React.createElement(StyledButton, {
$step: step,
type: "button"
}, getDateFormatted),
mode: datePickerMode,
selected: {
from: moment.utc(fromDate).unix(),
to: moment.utc(toDate).unix()
},
onChange: onDatePickerChange,
firstDayOfWeek: period === "week" ? 0 : 1,
modifiers: modifiers,
disabledDaysAfter: maxValue,
disabledDaysBefore: minValue,
disableYearPicker: !enableYearPicker,
popperClassName: popperClassName,
position: position,
key: datePickerMode,
instant: true,
required: true
}) : /*#__PURE__*/React.createElement(StyledSpan, null, getDateFormatted)), !disableArrows ? /*#__PURE__*/React.createElement(StyledControl, {
type: "button",
variant: mode === "flat" ? "text gray" : "secondary",
onClick: onRightClickHandler,
disabled: isAfterMaxDate
}, /*#__PURE__*/React.createElement(ArrowRightIcon, null)) : null);
};
DateStepper.displayName = "DateStepper";
//# sourceMappingURL=DateStepper.js.map