UNPKG

@activecollab/components

Version:

ActiveCollab Components

345 lines (343 loc) • 14.2 kB
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