UNPKG

@activecollab/components

Version:

ActiveCollab Components

270 lines (268 loc) • 10.3 kB
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