UNPKG

pouncejs

Version:

A collection of UI components from Panther labs

311 lines (291 loc) 11.4 kB
import _extends from "@babel/runtime/helpers/extends"; import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/objectWithoutPropertiesLoose"; import React, { useState, useCallback } from 'react'; import IconButton from '../IconButton'; import Box from '../Box'; import Presets from './Presets'; import Flex from '../Flex'; import Button from '../Button'; import Heading from '../Heading'; import DoubleTextInput from './DoubleTextInput'; import DateWrapper from '../DateInput/DateWrapper'; import TimePicker from '../DateInput/TimePicker'; import Month from '../DateInput/Month'; import ClearButton from '../DateInput/ClearButton'; import { dateToDayjs, noop, now } from '../../utils/helpers'; import useEscapeKey from '../../utils/useEscapeKey'; import usePrevious from '../../utils/usePrevious'; import useOutsideClick from '../../utils/useOutsideClick'; import useDisclosure from '../../utils/useDisclosure'; /** * Converts the provided Dates into Dayjs objects, if they exist */ var datesToDayjs = function datesToDayjs(value, timezone) { return [dateToDayjs(value[0], timezone), dateToDayjs(value[1], timezone)]; }; var areDatesUndefined = function areDatesUndefined(dates) { return !!dates && dates.every(function (date) { return !date; }); }; var DateRangeInput = function DateRangeInput(_ref) { var _ref$value = _ref.value, value = _ref$value === void 0 ? [] : _ref$value, _ref$format = _ref.format, format = _ref$format === void 0 ? 'MM/DD/YYYY' : _ref$format, _ref$mode = _ref.mode, mode = _ref$mode === void 0 ? '24h' : _ref$mode, withTime = _ref.withTime, _ref$variant = _ref.variant, variant = _ref$variant === void 0 ? 'outline' : _ref$variant, _ref$disableReset = _ref.disableReset, disableReset = _ref$disableReset === void 0 ? false : _ref$disableReset, withPresets = _ref.withPresets, _ref$onChange = _ref.onChange, onChange = _ref$onChange === void 0 ? noop : _ref$onChange, labelStart = _ref.labelStart, alignment = _ref.alignment, labelEnd = _ref.labelEnd, placeholderStart = _ref.placeholderStart, placeholderEnd = _ref.placeholderEnd, _ref$timezone = _ref.timezone, timezone = _ref$timezone === void 0 ? 'local' : _ref$timezone, rest = _objectWithoutPropertiesLoose(_ref, ["value", "format", "mode", "withTime", "variant", "disableReset", "withPresets", "onChange", "labelStart", "alignment", "labelEnd", "placeholderStart", "placeholderEnd", "timezone"]); var _useState = useState(datesToDayjs(value, timezone)), currentDateRange = _useState[0], setCurrentRange = _useState[1]; var _useState2 = useState(currentDateRange[0] || now(timezone)), currentMonth = _useState2[0], setCurrentMonth = _useState2[1]; var _useDisclosure = useDisclosure(), isOpen = _useDisclosure.isOpen, open = _useDisclosure.open, close = _useDisclosure.close; var previousDateRange = usePrevious(datesToDayjs(value, timezone)); var ref = React.useRef(null); var targetRef = React.useRef(null); // Handles value & timezone updates outside of the component (i.e. a form has re-initialized or // has updated its values as a result of an API call) React.useEffect(function () { setCurrentRange(datesToDayjs(value, timezone)); }, [value, timezone]); var onCancel = useCallback(function () { setCurrentRange(datesToDayjs(value, timezone)); close(); }, [value, timezone, setCurrentRange, close]); var resetLabel = React.useMemo(function () { return withTime ? 'Clear Dates & Time' : 'Clear Dates'; }, [withTime]); var onApply = useCallback(function (e) { // To avoid inconsistent results round the start and end dates to the // nearest minute (or day if the time picker is disabled) var timeUnit = withTime ? 'minute' : 'day'; e.preventDefault(); onChange([currentDateRange[0] ? currentDateRange[0].startOf(timeUnit).toDate() : undefined, currentDateRange[1] ? currentDateRange[1].endOf(timeUnit).toDate() : undefined]); close(); }, [close, onChange, currentDateRange]); var onNextMonth = useCallback(function (e) { e.preventDefault(); var next = currentMonth.add(1, 'month'); setCurrentMonth(next); }, [currentMonth, setCurrentMonth]); var onPreviousMonth = useCallback(function (e) { e.preventDefault(); var next = currentMonth.subtract(1, 'month'); setCurrentMonth(next); }, [currentMonth, setCurrentMonth]); // Close on ESC key presses useEscapeKey({ ref: ref, callback: onCancel, disabled: !isOpen }); // Close popover on clicks outside useOutsideClick({ refs: [ref, targetRef], callback: onCancel, disabled: !isOpen }); var onDaySelect = useCallback(function (dateChanged) { if (!currentDateRange[0]) { return setCurrentRange([dateChanged]); } var start = currentDateRange[0]; var end = currentDateRange[1]; if (!start) { return setCurrentRange([dateChanged]); } if (!end) { if (dateChanged.isBefore(start, 'date')) { return setCurrentRange([dateChanged.hour(start.hour()).minute(start.minute()), start]); } return setCurrentRange([start, dateChanged]); } if (dateChanged.isBefore(start, 'date') || start.isSame(end, 'date')) { return setCurrentRange([dateChanged.hour(start.hour()).minute(start.minute())]); } if (dateChanged.isAfter(start, 'date') && dateChanged.isBefore(end, 'date')) { return setCurrentRange([dateChanged.hour(start.hour()).minute(start.minute()), end]); } return setCurrentRange([start, dateChanged.hour(end.hour()).minute(end.minute())]); }, [setCurrentRange, currentDateRange]); var onClear = useCallback(function () { setCurrentRange([undefined, undefined]); }, [setCurrentRange]); var isDisabled = React.useMemo(function () { if (areDatesUndefined(previousDateRange) && areDatesUndefined(currentDateRange)) { return true; } if (currentDateRange[0] && (!currentDateRange[1] || currentDateRange[0].isAfter(currentDateRange[1], withTime ? 'minute' : 'day'))) { return true; } return currentDateRange.every(function (date, index) { return date && previousDateRange && previousDateRange[index] && date.isSame(previousDateRange[index], withTime ? 'minute' : 'day'); }); }, [currentDateRange, previousDateRange]); var onPresetSelect = useCallback(function (_ref2) { var start = _ref2[0], end = _ref2[1]; setCurrentRange([start, end]); }, [setCurrentRange]); var onStartTimeUpdate = useCallback(function (timeUpdated) { return setCurrentRange([timeUpdated, currentDateRange[1]]); }, [setCurrentRange, currentDateRange]); var onEndTimeUpdate = useCallback(function (timeUpdated) { return setCurrentRange([currentDateRange[0], timeUpdated]); }, [setCurrentRange, currentDateRange]); var nextMonth = currentMonth.add(1, 'month'); return /*#__PURE__*/React.createElement(Box, { position: "relative", zIndex: 1, ref: targetRef }, /*#__PURE__*/React.createElement(Box, { onClick: open }, /*#__PURE__*/React.createElement(DoubleTextInput, _extends({}, rest, { variant: variant, from: currentDateRange[0] ? currentDateRange[0].format(format) : '', to: currentDateRange[1] ? currentDateRange[1].format(format) : '', labelFrom: labelStart, labelTo: labelEnd, placeholderFrom: placeholderStart, placeholderTo: placeholderEnd, onChangeFrom: noop, onChangeTo: noop, readOnly: true, autoComplete: "off" }))), /*#__PURE__*/React.createElement(Box, { position: "absolute", top: 2, right: 3, zIndex: 2 }, /*#__PURE__*/React.createElement(IconButton, { variant: "unstyled", "aria-label": "Toggle picker", size: "medium", icon: "calendar", onClick: isOpen ? onCancel : open })), /*#__PURE__*/React.createElement(DateWrapper, { ref: ref, targetRef: targetRef, alignment: alignment, isExpanded: isOpen }, /*#__PURE__*/React.createElement(Flex, null, withPresets && /*#__PURE__*/React.createElement(Presets, { setCurrentMonth: setCurrentMonth, timezone: timezone, onSelect: onPresetSelect, currentDateRange: currentDateRange }), /*#__PURE__*/React.createElement(Box, null, /*#__PURE__*/React.createElement(Flex, { align: "center", justify: "space-between", px: 4, py: 22, position: "relative" }, /*#__PURE__*/React.createElement(Box, { position: "absolute", left: 4 }, /*#__PURE__*/React.createElement(IconButton, { size: "medium", onClick: onPreviousMonth, icon: "arrow-back", "aria-label": "Go to previous month" })), /*#__PURE__*/React.createElement(Box, { as: Heading, fontSize: "medium", fontWeight: "bold", width: "50%", textAlign: "center" }, currentMonth.format('MMMM YYYY')), /*#__PURE__*/React.createElement(Box, { as: Heading, fontSize: "medium", fontWeight: "bold", width: "50%", textAlign: "center" }, nextMonth.format('MMMM YYYY')), /*#__PURE__*/React.createElement(Box, { position: "absolute", right: 4 }, /*#__PURE__*/React.createElement(IconButton, { onClick: onNextMonth, size: "medium", icon: "arrow-forward", "aria-label": "Go to next month" }))), /*#__PURE__*/React.createElement(Box, { px: 4, pb: 4 }, /*#__PURE__*/React.createElement(Flex, { spacing: 8 }, /*#__PURE__*/React.createElement(Box, null, /*#__PURE__*/React.createElement(Month, { onDaySelect: onDaySelect, daysSelected: currentDateRange, year: currentMonth.year(), month: currentMonth.month(), timezone: timezone })), /*#__PURE__*/React.createElement(Box, null, /*#__PURE__*/React.createElement(Month, { onDaySelect: onDaySelect, daysSelected: currentDateRange, year: nextMonth.year(), month: nextMonth.month(), timezone: timezone })))), withTime && /*#__PURE__*/React.createElement(Flex, { align: "center", justify: "space-between", borderTop: "1px solid", borderColor: "navyblue-300", p: 4 }, /*#__PURE__*/React.createElement(TimePicker, { label: "From Time", mode: mode, onTimeUpdate: onStartTimeUpdate, date: currentDateRange[0], timezone: timezone }), /*#__PURE__*/React.createElement(TimePicker, { mode: mode, label: "To Time", onTimeUpdate: onEndTimeUpdate, date: currentDateRange[1], timezone: timezone })), /*#__PURE__*/React.createElement(Flex, { align: "center", spacing: 3, justify: disableReset && 'center', borderTop: "1px solid", p: 3, borderColor: "navyblue-300" }, !disableReset && /*#__PURE__*/React.createElement(Box, { align: "center", justifySelf: "flex-start" }, /*#__PURE__*/React.createElement(ClearButton, { onClick: onClear }, resetLabel)), /*#__PURE__*/React.createElement(Flex, { align: "center", justifySelf: !disableReset && 'flex-end', ml: !disableReset && 'auto', justify: "center", spacing: 3 }, /*#__PURE__*/React.createElement(Button, { onClick: onCancel, size: "medium", variantColor: "gray-500" }, "Cancel"), /*#__PURE__*/React.createElement(Button, { disabled: isDisabled, onClick: onApply, size: "medium" }, "Apply"))))))); }; export default /*#__PURE__*/React.memo(DateRangeInput);