pouncejs
Version:
A collection of UI components from Panther labs
311 lines (291 loc) • 11.4 kB
JavaScript
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);