react-datetime-picker
Version:
A date range picker for your React app.
290 lines (289 loc) • 17.4 kB
JavaScript
;
'use client';
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var jsx_runtime_1 = require("react/jsx-runtime");
var react_1 = require("react");
var react_dom_1 = require("react-dom");
var make_event_props_1 = __importDefault(require("make-event-props"));
var clsx_1 = __importDefault(require("clsx"));
var react_calendar_1 = __importDefault(require("react-calendar"));
var react_clock_1 = __importDefault(require("react-clock"));
var react_fit_1 = __importDefault(require("react-fit"));
var DateTimeInput_js_1 = __importDefault(require("./DateTimeInput.js"));
var baseClassName = 'react-datetime-picker';
var outsideActionEvents = ['mousedown', 'focusin', 'touchstart'];
var allViews = ['hour', 'minute', 'second'];
var iconProps = {
xmlns: 'http://www.w3.org/2000/svg',
width: 19,
height: 19,
viewBox: '0 0 19 19',
stroke: 'black',
strokeWidth: 2,
};
var CalendarIcon = ((0, jsx_runtime_1.jsxs)("svg", __assign({}, iconProps, { className: "".concat(baseClassName, "__calendar-button__icon ").concat(baseClassName, "__button__icon"), children: [(0, jsx_runtime_1.jsx)("rect", { fill: "none", height: "15", width: "15", x: "2", y: "2" }), (0, jsx_runtime_1.jsx)("line", { x1: "6", x2: "6", y1: "0", y2: "4" }), (0, jsx_runtime_1.jsx)("line", { x1: "13", x2: "13", y1: "0", y2: "4" })] })));
var ClearIcon = ((0, jsx_runtime_1.jsxs)("svg", __assign({}, iconProps, { className: "".concat(baseClassName, "__clear-button__icon ").concat(baseClassName, "__button__icon"), children: [(0, jsx_runtime_1.jsx)("line", { x1: "4", x2: "15", y1: "4", y2: "15" }), (0, jsx_runtime_1.jsx)("line", { x1: "15", x2: "4", y1: "4", y2: "15" })] })));
function DateTimePicker(props) {
var amPmAriaLabel = props.amPmAriaLabel, autoFocus = props.autoFocus, calendarAriaLabel = props.calendarAriaLabel, _a = props.calendarIcon, calendarIcon = _a === void 0 ? CalendarIcon : _a, className = props.className, clearAriaLabel = props.clearAriaLabel, _b = props.clearIcon, clearIcon = _b === void 0 ? ClearIcon : _b, _c = props.closeWidgets, shouldCloseWidgetsOnSelect = _c === void 0 ? true : _c, dataTestid = props["data-testid"], dayAriaLabel = props.dayAriaLabel, dayPlaceholder = props.dayPlaceholder, disableCalendar = props.disableCalendar, disableClock = props.disableClock, disabled = props.disabled, format = props.format, hourAriaLabel = props.hourAriaLabel, hourPlaceholder = props.hourPlaceholder, id = props.id, _d = props.isCalendarOpen, isCalendarOpenProps = _d === void 0 ? null : _d, _e = props.isClockOpen, isClockOpenProps = _e === void 0 ? null : _e, locale = props.locale, maxDate = props.maxDate, _f = props.maxDetail, maxDetail = _f === void 0 ? 'minute' : _f, minDate = props.minDate, minuteAriaLabel = props.minuteAriaLabel, minutePlaceholder = props.minutePlaceholder, monthAriaLabel = props.monthAriaLabel, monthPlaceholder = props.monthPlaceholder, _g = props.name, name = _g === void 0 ? 'datetime' : _g, nativeInputAriaLabel = props.nativeInputAriaLabel, onCalendarClose = props.onCalendarClose, onCalendarOpen = props.onCalendarOpen, onChangeProps = props.onChange, onClockClose = props.onClockClose, onClockOpen = props.onClockOpen, onFocusProps = props.onFocus, onInvalidChange = props.onInvalidChange, _h = props.openWidgetsOnFocus, openWidgetsOnFocus = _h === void 0 ? true : _h, required = props.required, secondAriaLabel = props.secondAriaLabel, secondPlaceholder = props.secondPlaceholder, shouldCloseWidgets = props.shouldCloseWidgets, shouldOpenWidgets = props.shouldOpenWidgets, showLeadingZeros = props.showLeadingZeros, value = props.value, yearAriaLabel = props.yearAriaLabel, yearPlaceholder = props.yearPlaceholder, otherProps = __rest(props, ["amPmAriaLabel", "autoFocus", "calendarAriaLabel", "calendarIcon", "className", "clearAriaLabel", "clearIcon", "closeWidgets", 'data-testid', "dayAriaLabel", "dayPlaceholder", "disableCalendar", "disableClock", "disabled", "format", "hourAriaLabel", "hourPlaceholder", "id", "isCalendarOpen", "isClockOpen", "locale", "maxDate", "maxDetail", "minDate", "minuteAriaLabel", "minutePlaceholder", "monthAriaLabel", "monthPlaceholder", "name", "nativeInputAriaLabel", "onCalendarClose", "onCalendarOpen", "onChange", "onClockClose", "onClockOpen", "onFocus", "onInvalidChange", "openWidgetsOnFocus", "required", "secondAriaLabel", "secondPlaceholder", "shouldCloseWidgets", "shouldOpenWidgets", "showLeadingZeros", "value", "yearAriaLabel", "yearPlaceholder"]);
var _j = (0, react_1.useState)(isCalendarOpenProps), isCalendarOpen = _j[0], setIsCalendarOpen = _j[1];
var _k = (0, react_1.useState)(isClockOpenProps), isClockOpen = _k[0], setIsClockOpen = _k[1];
var wrapper = (0, react_1.useRef)(null);
var calendarWrapper = (0, react_1.useRef)(null);
var clockWrapper = (0, react_1.useRef)(null);
(0, react_1.useEffect)(function () {
setIsCalendarOpen(isCalendarOpenProps);
}, [isCalendarOpenProps]);
(0, react_1.useEffect)(function () {
setIsClockOpen(isClockOpenProps);
}, [isClockOpenProps]);
function openCalendar(_a) {
var reason = _a.reason;
if (shouldOpenWidgets) {
if (!shouldOpenWidgets({ reason: reason, widget: 'calendar' })) {
return;
}
}
setIsClockOpen(isClockOpen ? false : isClockOpen);
setIsCalendarOpen(true);
if (onCalendarOpen) {
onCalendarOpen();
}
}
var closeCalendar = (0, react_1.useCallback)(function (_a) {
var reason = _a.reason;
if (shouldCloseWidgets) {
if (!shouldCloseWidgets({ reason: reason, widget: 'calendar' })) {
return;
}
}
setIsCalendarOpen(false);
if (onCalendarClose) {
onCalendarClose();
}
}, [onCalendarClose, shouldCloseWidgets]);
function toggleCalendar() {
if (isCalendarOpen) {
closeCalendar({ reason: 'buttonClick' });
}
else {
openCalendar({ reason: 'buttonClick' });
}
}
function openClock(_a) {
var reason = _a.reason;
if (shouldOpenWidgets) {
if (!shouldOpenWidgets({ reason: reason, widget: 'clock' })) {
return;
}
}
setIsCalendarOpen(isCalendarOpen ? false : isCalendarOpen);
setIsClockOpen(true);
if (onClockOpen) {
onClockOpen();
}
}
var closeClock = (0, react_1.useCallback)(function (_a) {
var reason = _a.reason;
if (shouldCloseWidgets) {
if (!shouldCloseWidgets({ reason: reason, widget: 'clock' })) {
return;
}
}
setIsClockOpen(false);
if (onClockClose) {
onClockClose();
}
}, [onClockClose, shouldCloseWidgets]);
var closeWidgets = (0, react_1.useCallback)(function (_a) {
var reason = _a.reason;
closeCalendar({ reason: reason });
closeClock({ reason: reason });
}, [closeCalendar, closeClock]);
function onChange(value, shouldCloseWidgets) {
if (shouldCloseWidgets === void 0) { shouldCloseWidgets = shouldCloseWidgetsOnSelect; }
if (shouldCloseWidgets) {
closeWidgets({ reason: 'select' });
}
if (onChangeProps) {
onChangeProps(value);
}
}
function onDateChange(nextValue, shouldCloseWidgets) {
// React-Calendar passes an array of values when selectRange is enabled
var nextValueFrom = (Array.isArray(nextValue) ? nextValue : [nextValue])[0];
var valueFrom = (Array.isArray(value) ? value : [value])[0];
if (valueFrom && nextValueFrom) {
var valueFromDate = new Date(valueFrom);
var nextValueFromWithHour = new Date(nextValueFrom);
nextValueFromWithHour.setHours(valueFromDate.getHours(), valueFromDate.getMinutes(), valueFromDate.getSeconds(), valueFromDate.getMilliseconds());
onChange(nextValueFromWithHour, shouldCloseWidgets);
}
else {
onChange(nextValueFrom, shouldCloseWidgets);
}
}
function onFocus(event) {
if (onFocusProps) {
onFocusProps(event);
}
if (
// Internet Explorer still fires onFocus on disabled elements
disabled ||
!openWidgetsOnFocus ||
event.target.dataset.select === 'true') {
return;
}
switch (event.target.name) {
case 'day':
case 'month':
case 'year': {
if (isCalendarOpen) {
return;
}
openCalendar({ reason: 'focus' });
break;
}
case 'hour12':
case 'hour24':
case 'minute':
case 'second': {
if (isClockOpen) {
return;
}
openClock({ reason: 'focus' });
break;
}
default:
}
}
var onKeyDown = (0, react_1.useCallback)(function (event) {
if (event.key === 'Escape') {
closeWidgets({ reason: 'escape' });
}
}, [closeWidgets]);
function clear() {
onChange(null);
}
function stopPropagation(event) {
event.stopPropagation();
}
var onOutsideAction = (0, react_1.useCallback)(function (event) {
var wrapperEl = wrapper.current;
var calendarWrapperEl = calendarWrapper.current;
var clockWrapperEl = clockWrapper.current;
// Try event.composedPath first to handle clicks inside a Shadow DOM.
var target = ('composedPath' in event ? event.composedPath()[0] : event.target);
if (target &&
wrapperEl &&
!wrapperEl.contains(target) &&
(!calendarWrapperEl || !calendarWrapperEl.contains(target)) &&
(!clockWrapperEl || !clockWrapperEl.contains(target))) {
closeWidgets({ reason: 'outsideAction' });
}
}, [calendarWrapper, clockWrapper, closeWidgets, wrapper]);
var handleOutsideActionListeners = (0, react_1.useCallback)(function (shouldListen) {
if (shouldListen === void 0) { shouldListen = isCalendarOpen || isClockOpen; }
outsideActionEvents.forEach(function (event) {
if (shouldListen) {
document.addEventListener(event, onOutsideAction);
}
else {
document.removeEventListener(event, onOutsideAction);
}
});
if (shouldListen) {
document.addEventListener('keydown', onKeyDown);
}
else {
document.removeEventListener('keydown', onKeyDown);
}
}, [isCalendarOpen, isClockOpen, onOutsideAction, onKeyDown]);
(0, react_1.useEffect)(function () {
handleOutsideActionListeners();
return function () {
handleOutsideActionListeners(false);
};
}, [handleOutsideActionListeners]);
function renderInputs() {
var valueFrom = (Array.isArray(value) ? value : [value])[0];
var ariaLabelProps = {
amPmAriaLabel: amPmAriaLabel,
dayAriaLabel: dayAriaLabel,
hourAriaLabel: hourAriaLabel,
minuteAriaLabel: minuteAriaLabel,
monthAriaLabel: monthAriaLabel,
nativeInputAriaLabel: nativeInputAriaLabel,
secondAriaLabel: secondAriaLabel,
yearAriaLabel: yearAriaLabel,
};
var placeholderProps = {
dayPlaceholder: dayPlaceholder,
hourPlaceholder: hourPlaceholder,
minutePlaceholder: minutePlaceholder,
monthPlaceholder: monthPlaceholder,
secondPlaceholder: secondPlaceholder,
yearPlaceholder: yearPlaceholder,
};
return ((0, jsx_runtime_1.jsxs)("div", { className: "".concat(baseClassName, "__wrapper"), children: [(0, jsx_runtime_1.jsx)(DateTimeInput_js_1.default, __assign({}, ariaLabelProps, placeholderProps, {
// eslint-disable-next-line jsx-a11y/no-autofocus
autoFocus: autoFocus, className: "".concat(baseClassName, "__inputGroup"), disabled: disabled, format: format, isWidgetOpen: isCalendarOpen || isClockOpen, locale: locale, maxDate: maxDate, maxDetail: maxDetail, minDate: minDate, name: name, onChange: onChange, onInvalidChange: onInvalidChange, required: required, showLeadingZeros: showLeadingZeros, value: valueFrom })), clearIcon !== null && ((0, jsx_runtime_1.jsx)("button", { "aria-label": clearAriaLabel, className: "".concat(baseClassName, "__clear-button ").concat(baseClassName, "__button"), disabled: disabled, onClick: clear, onFocus: stopPropagation, type: "button", children: typeof clearIcon === 'function' ? (0, react_1.createElement)(clearIcon) : clearIcon })), calendarIcon !== null && !disableCalendar && ((0, jsx_runtime_1.jsx)("button", { "aria-expanded": isCalendarOpen || false, "aria-label": calendarAriaLabel, className: "".concat(baseClassName, "__calendar-button ").concat(baseClassName, "__button"), disabled: disabled, onClick: toggleCalendar, onFocus: stopPropagation, type: "button", children: typeof calendarIcon === 'function' ? (0, react_1.createElement)(calendarIcon) : calendarIcon }))] }));
}
function renderCalendar() {
if (isCalendarOpen === null || disableCalendar) {
return null;
}
var calendarProps = props.calendarProps, portalContainer = props.portalContainer, value = props.value;
var className = "".concat(baseClassName, "__calendar");
var classNames = (0, clsx_1.default)(className, "".concat(className, "--").concat(isCalendarOpen ? 'open' : 'closed'));
var calendar = ((0, jsx_runtime_1.jsx)(react_calendar_1.default, __assign({ locale: locale, maxDate: maxDate, minDate: minDate, onChange: function (value) { return onDateChange(value); }, value: value }, calendarProps)));
return portalContainer ? ((0, react_dom_1.createPortal)((0, jsx_runtime_1.jsx)("div", { ref: calendarWrapper, className: classNames, children: calendar }), portalContainer)) : ((0, jsx_runtime_1.jsx)(react_fit_1.default, { children: (0, jsx_runtime_1.jsx)("div", { ref: function (ref) {
if (ref && !isCalendarOpen) {
ref.removeAttribute('style');
}
}, className: classNames, children: calendar }) }));
}
function renderClock() {
if (isClockOpen === null || disableClock) {
return null;
}
var clockProps = props.clockProps, _a = props.maxDetail, maxDetail = _a === void 0 ? 'minute' : _a, portalContainer = props.portalContainer, value = props.value;
var className = "".concat(baseClassName, "__clock");
var classNames = (0, clsx_1.default)(className, "".concat(className, "--").concat(isClockOpen ? 'open' : 'closed'));
var valueFrom = (Array.isArray(value) ? value : [value])[0];
var maxDetailIndex = allViews.indexOf(maxDetail);
var clock = ((0, jsx_runtime_1.jsx)(react_clock_1.default, __assign({ locale: locale, renderMinuteHand: maxDetailIndex > 0, renderSecondHand: maxDetailIndex > 1, value: valueFrom }, clockProps)));
return portalContainer ? ((0, react_dom_1.createPortal)((0, jsx_runtime_1.jsx)("div", { ref: clockWrapper, className: classNames, children: clock }), portalContainer)) : ((0, jsx_runtime_1.jsx)(react_fit_1.default, { children: (0, jsx_runtime_1.jsx)("div", { ref: function (ref) {
if (ref && !isClockOpen) {
ref.removeAttribute('style');
}
}, className: classNames, children: clock }) }));
}
var eventProps = (0, react_1.useMemo)(function () { return (0, make_event_props_1.default)(otherProps); }, [otherProps]);
return ((0, jsx_runtime_1.jsxs)("div", __assign({ className: (0, clsx_1.default)(baseClassName, "".concat(baseClassName, "--").concat(isCalendarOpen || isClockOpen ? 'open' : 'closed'), "".concat(baseClassName, "--").concat(disabled ? 'disabled' : 'enabled'), className), "data-testid": dataTestid, id: id }, eventProps, { onFocus: onFocus, ref: wrapper, children: [renderInputs(), renderCalendar(), renderClock()] })));
}
exports.default = DateTimePicker;