@activecollab/components
Version:
ActiveCollab Components
345 lines (343 loc) • 14.2 kB
JavaScript
import React, { useCallback, useEffect, useMemo, useState } from "react";
import classnames from "classnames";
import moment from "moment";
import { DatePickerForm } from "./DatePickerForm";
import { SelectDateTarget } from "./SelectDateTarget";
import { StyledConfirmDialog, StyledSelectDate, StyledSelectDateButton } from "./Styles";
import { formatDate } from "../../utils/dateUtils";
import { useLocalization } from "../Localization";
import { Menu } from "../Menu/Menu";
import { Tooltip } from "../Tooltip/Tooltip";
const getUtcTimestampFromDate = date => {
return moment.utc([date.getFullYear(), date.getMonth(), date.getDate()]).unix();
};
const isDayInRange = (day, data) => {
if (data.repeating) {
const from = new Date(day.getFullYear(), data.from.getMonth(), data.from.getDate());
const to = new Date(day.getFullYear(), data.to.getMonth(), data.to.getDate());
return day >= from && day <= to;
}
return day >= data.from && day <= data.to;
};
export const SelectDate = _ref => {
let {
changeMode: mode = "instant",
trigger: labelType = "text",
saveButtonText = "Save",
cancelButtonText = "Cancel",
clearButtonText = "Clear",
modalHeaderText = "Discard changes?",
modalDiscardMessage = "All unsaved changes will be lost.",
modalDiscardBtnText = "OK",
modalCancelBtnText = "Cancel",
onDayClick,
onSave,
onCancel,
onToggleDatePicker,
required: dateRequired = false,
defaultTimezoneAware = false,
longDateFormat = false,
defaultShowDatePicker = false,
firstDayOfWeek = 0,
selectedDays,
selectionMode = "custom",
menuClassName,
targetClassName,
icon,
defaultLabelText = "Set...",
targetTextClassName,
targetIconClassName,
backgroundElementClass,
dateFormat: passedDateFormat,
defaultMonth,
daysToModify = [],
weekends = [],
weekendLabel = "Weekend",
nonWorkingDaysOfWeek = [],
nonWorkingDaysOfWeekLabel = "Unavailable",
weekendIsSelectable = false,
tooltipText,
popperTooltipClassName,
popperClassName,
popperTooltipStyle,
position,
disableDaysBefore
} = _ref;
const [timezoneAware, setTimezoneAware] = useState(defaultTimezoneAware);
const localization = useLocalization();
const dateFormat = passedDateFormat ? passedDateFormat : localization.dateFormat;
const labelText = useMemo(() => {
if (!selectedDays) {
return defaultLabelText;
} else {
const endDate = timezoneAware ? moment.unix(selectedDays.to).local() : moment.unix(selectedDays.to).utc();
const startDate = timezoneAware ? moment.unix(selectedDays.from).local() : moment.unix(selectedDays.from).utc();
const formattedEndDate = formatDate(endDate, dateFormat, longDateFormat);
const formattedStartDate = formatDate(startDate, dateFormat, longDateFormat);
if (selectedDays.from === selectedDays.to) {
return formattedEndDate;
}
return formattedStartDate + " - " + formattedEndDate;
}
}, [selectedDays, dateFormat, timezoneAware, defaultLabelText, longDateFormat]);
const modifiedDates = useMemo(() => {
if (typeof selectedDays === "object" && !!selectedDays.from && !!selectedDays.to) {
if (timezoneAware) {
return {
from: moment.unix(selectedDays.from).toDate(),
to: moment.unix(selectedDays.to).toDate()
};
}
const offsetFrom = moment.unix(selectedDays.from).utcOffset() * 60;
const offsetTo = moment.unix(selectedDays.to).utcOffset() * 60;
return {
from: new Date((selectedDays.from - offsetFrom) * 1000),
to: new Date((selectedDays.to - offsetTo) * 1000)
};
}
}, [selectedDays, timezoneAware]);
const handleModifiedSave = useCallback(selectedDates => {
setTimezoneAware(false);
if (typeof onSave === "function") {
if (typeof selectedDates === "object" && Object.prototype.hasOwnProperty.call(selectedDates, "from") && Object.prototype.hasOwnProperty.call(selectedDates, "to")) {
onSave({
from: getUtcTimestampFromDate(selectedDates.from),
to: getUtcTimestampFromDate(selectedDates.to)
});
} else {
onSave(undefined);
}
}
}, [onSave]);
const getDefaultMonth = useCallback(() => {
let date = new Date();
if (typeof selectedDays === "object" && !!selectedDays.from) {
date = timezoneAware ? new Date(selectedDays.from * 1000) : new Date((selectedDays.from - moment.unix(selectedDays.from).utcOffset() * 60) * 1000);
} else if (defaultMonth) {
date = defaultMonth;
}
// forcing second day in month
return new Date(date.getFullYear(), date.getMonth(), 2);
}, [defaultMonth, selectedDays, timezoneAware]);
const [month, setMonth] = useState(getDefaultMonth);
const handleMonthChange = useCallback(month => {
setMonth(month);
}, [setMonth]);
const [newDates, setNewDates] = useState(modifiedDates);
const [showDiscardModal, setShowDiscardModal] = useState(false);
const [showDatePicker, setShowDatePicker] = useState(defaultShowDatePicker);
const disabledSaveButton = useMemo(() => {
return (modifiedDates == null ? void 0 : modifiedDates.from) === (newDates == null ? void 0 : newDates.from) && (modifiedDates == null ? void 0 : modifiedDates.to) === (newDates == null ? void 0 : newDates.to);
}, [modifiedDates, newDates]);
useEffect(() => {
setShowDatePicker(defaultShowDatePicker);
}, [defaultShowDatePicker]);
useEffect(() => {
setNewDates(modifiedDates);
}, [modifiedDates]);
useEffect(() => {
typeof onToggleDatePicker === "function" && onToggleDatePicker(showDatePicker);
}, [showDatePicker, onToggleDatePicker]);
useEffect(() => {
if (!showDatePicker) {
setMonth(getDefaultMonth());
}
}, [showDatePicker, getDefaultMonth]);
const handleSave = useCallback(() => {
handleModifiedSave(newDates);
setShowDatePicker(false);
}, [newDates, handleModifiedSave]);
const handleChange = useCallback(dates => {
if (mode !== "atomic") {
handleModifiedSave(dates);
}
setNewDates(dates);
}, [mode, handleModifiedSave]);
const handleBeforeCloseMenu = useCallback(() => {
let result = true;
if (modifiedDates && !newDates || !modifiedDates && newDates) {
result = false;
}
const selectedDaysFrom = modifiedDates == null ? void 0 : modifiedDates.from;
const selectedDaysTo = modifiedDates == null ? void 0 : modifiedDates.to;
const newDatesFrom = newDates == null ? void 0 : newDates.from;
const newDatesTo = newDates == null ? void 0 : newDates.to;
if (selectedDaysFrom && newDatesFrom && selectedDaysTo && newDatesTo && (moment(selectedDaysFrom).format("DD-MM-YYYY") !== moment(newDatesFrom).format("DD-MM-YYYY") || moment(selectedDaysTo).format("DD-MM-YYYY") !== moment(newDatesTo).format("DD-MM-YYYY"))) {
result = false;
}
if (!result) {
setShowDiscardModal(true);
}
return result;
}, [modifiedDates, newDates]);
const handleClose = useCallback(() => {
if (mode !== "atomic" || handleBeforeCloseMenu()) {
typeof onCancel === "function" && onCancel();
setShowDatePicker(false);
}
}, [mode, onCancel, handleBeforeCloseMenu]);
const handleCancel = useCallback(event => {
event && event.preventDefault();
return handleClose();
}, [handleClose]);
const handleShowDatePicker = useCallback(() => {
setShowDatePicker(!showDatePicker);
}, [showDatePicker]);
const handleCloseDiscardModal = useCallback(() => {
setShowDiscardModal(false);
}, []);
const handleClear = useCallback(() => {
if (!dateRequired) {
setNewDates(undefined);
if (mode === "instant") {
handleModifiedSave(undefined);
}
}
}, [mode, dateRequired, handleModifiedSave]);
const handleSaveDiscardModal = useCallback(() => {
setNewDates(modifiedDates);
setShowDiscardModal(false);
setShowDatePicker(false);
}, [modifiedDates]);
const modifiers = useMemo(() => {
const userAvailabilities = daysToModify.filter(data => data.type === "user_day_off");
const globalDaysOff = daysToModify.filter(data => data.type === "global_day_off");
const selectableGlobalDaysOff = daysToModify.filter(data => data.type === "selectable_global_day_off");
return {
userAvailability: day => {
// construct new date because react-day-picker returns date with hours set
const currentDay = new Date(day.getFullYear(), day.getMonth(), day.getDate());
return userAvailabilities.some(data => isDayInRange(currentDay, data));
},
weekend: day => {
return weekends.some(value => day.getDay() === value);
},
nonWorkingDay: day => {
// return true if day is weekend day
if (weekends.some(value => value === day.getDay())) {
return true;
}
const nonWorkingDays = [...selectableGlobalDaysOff, ...globalDaysOff];
const currentDay = new Date(day.getFullYear(), day.getMonth(), day.getDate());
return nonWorkingDays.some(data => isDayInRange(currentDay, data));
},
day_disabled: day => {
if (disableDaysBefore) {
if (day.toJSON().slice(0, 10) < disableDaysBefore.toJSON().slice(0, 10)) {
return true;
}
}
// return true if day is weekend day
if (!weekendIsSelectable && weekends.some(value => value === day.getDay())) {
return true;
}
// construct new date because react-day-picker returns date with hours set
const currentDay = new Date(day.getFullYear(), day.getMonth(), day.getDate());
return globalDaysOff.some(data => isDayInRange(currentDay, data));
},
nonWorkingDaysOfWeek: {
dayOfWeek: nonWorkingDaysOfWeek
}
};
}, [daysToModify, nonWorkingDaysOfWeek, weekends, disableDaysBefore, weekendIsSelectable]);
const renderDay = useCallback(props => {
const titles = [];
if (weekends.some(value => props.date.getDay() === value)) {
titles.push(weekendLabel);
}
if (nonWorkingDaysOfWeek.includes(props.date.getDay())) {
titles.push(nonWorkingDaysOfWeekLabel);
}
// construct new date because react-day-picker returns date with hours set
const currentDay = new Date(props.date.getFullYear(), props.date.getMonth(), props.date.getDate());
daysToModify.forEach(data => {
if (isDayInRange(currentDay, data)) {
titles.push(data.title);
}
});
const child = /*#__PURE__*/React.createElement("div", {
className: "c-DayPicker-Day-Number"
}, props.date.getDate());
if (titles.length) {
const title = /*#__PURE__*/React.createElement("div", {
key: "title-wrapper-" + props.date.getDate()
}, titles.map((title, index) => /*#__PURE__*/React.createElement("div", {
key: "title-text-" + index
}, title)));
return /*#__PURE__*/React.createElement(Tooltip, {
title: title,
popperTooltipClassName: popperTooltipClassName,
popperTooltipStyle: popperTooltipStyle
}, child);
}
return child;
}, [weekends, nonWorkingDaysOfWeek, daysToModify, weekendLabel, nonWorkingDaysOfWeekLabel, popperTooltipClassName, popperTooltipStyle]);
const renderDatePickerForm = useCallback(() => {
return /*#__PURE__*/React.createElement(DatePickerForm, {
onMonthChange: handleMonthChange,
month: month,
mode: mode,
selectionMode: selectionMode,
dateRequired: dateRequired,
selectedDays: newDates,
firstDayOfWeek: firstDayOfWeek,
saveButtonText: saveButtonText,
cancelButtonText: cancelButtonText,
clearButtonText: clearButtonText,
onDayClick: onDayClick,
onSave: handleSave,
onChange: handleChange,
onCancel: handleCancel,
onClear: handleClear,
disabledSaveButton: disabledSaveButton,
modifiers: modifiers,
renderDay: renderDay
});
}, [handleMonthChange, month, mode, selectionMode, dateRequired, newDates, firstDayOfWeek, saveButtonText, cancelButtonText, clearButtonText, onDayClick, handleSave, handleChange, handleCancel, handleClear, disabledSaveButton, modifiers, renderDay]);
const renderTargetEl = useMemo(() => {
if (labelType === "icon" && icon) {
return /*#__PURE__*/React.createElement(SelectDateTarget, {
icon: icon,
title: tooltipText,
targetIconClassName: targetIconClassName,
forceHideTooltip: showDatePicker,
popperTooltipClassName: popperTooltipClassName,
popperTooltipStyle: popperTooltipStyle,
active: showDatePicker
});
}
if (typeof labelType === "function") {
return labelType(labelText);
}
return /*#__PURE__*/React.createElement(StyledSelectDateButton, {
type: "button",
className: classnames("date-picker-target", targetClassName)
}, /*#__PURE__*/React.createElement("span", {
className: targetTextClassName
}, labelText));
}, [icon, labelText, labelType, popperTooltipClassName, popperTooltipStyle, showDatePicker, targetClassName, targetIconClassName, targetTextClassName, tooltipText]);
return /*#__PURE__*/React.createElement(StyledSelectDate, {
className: "select-date"
}, labelType !== "inline" ? /*#__PURE__*/React.createElement(Menu, {
target: renderTargetEl,
open: showDatePicker,
onOpen: handleShowDatePicker,
onBeforeClose: handleBeforeCloseMenu,
onClose: handleClose,
position: position,
menuClassName: menuClassName,
popperClassName: popperClassName,
backgroundElementClass: backgroundElementClass
}, renderDatePickerForm()) : renderDatePickerForm(), mode === "atomic" ? /*#__PURE__*/React.createElement(StyledConfirmDialog, {
className: "modal-select-date",
open: showDiscardModal,
onCancel: handleCloseDiscardModal,
onConfirm: handleSaveDiscardModal,
dialogTitle: modalHeaderText,
dialogContent: modalDiscardMessage,
cancelBtnText: modalCancelBtnText,
confirmBtnText: modalDiscardBtnText
}) : null);
};
SelectDate.displayName = "SelectDate";
//# sourceMappingURL=SelectDate.js.map