@itwin/itwinui-react
Version:
A react component library for iTwinUI
607 lines (606 loc) • 20.4 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true,
});
function _export(target, all) {
for (var name in all)
Object.defineProperty(target, name, {
enumerable: true,
get: all[name],
});
}
_export(exports, {
DatePicker: function () {
return DatePicker;
},
generateLocalizedStrings: function () {
return generateLocalizedStrings;
},
});
const _interop_require_default = require('@swc/helpers/_/_interop_require_default');
const _interop_require_wildcard = require('@swc/helpers/_/_interop_require_wildcard');
const _classnames = /*#__PURE__*/ _interop_require_default._(
require('classnames'),
);
const _react = /*#__PURE__*/ _interop_require_wildcard._(require('react'));
const _index = require('../../utils/index.js');
const _IconButton = require('../Buttons/IconButton.js');
const _TimePicker = require('../TimePicker/TimePicker.js');
const _Popover = require('../Popover/Popover.js');
const isSameDay = (a, b) =>
a &&
b &&
a.getFullYear() === b.getFullYear() &&
a.getMonth() === b.getMonth() &&
a.getDate() === b.getDate();
const isInDateRange = (date, startDate, endDate) => {
if (!date || !startDate || !endDate) return false;
let minDate = new Date(startDate);
let maxDate = new Date(endDate);
let testDate = new Date(date);
testDate && testDate.setHours(0, 0, 0, 0);
minDate && minDate.setHours(0, 0, 0, 0);
maxDate && maxDate.setHours(0, 0, 0, 0);
return testDate > minDate && testDate < maxDate;
};
const isSingleOnChange = (onChange, enableRangeSelect) => !enableRangeSelect;
const defaultMonths = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
];
const defaultShortDays = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
const defaultLongDays = [
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
];
const generateLocalizedStrings = (locale) => {
let shortWeekDayFormatter = new Intl.DateTimeFormat(locale, {
weekday: 'short',
});
let longWeekDayFormatter = new Intl.DateTimeFormat(locale, {
weekday: 'long',
});
let monthFormatter = new Intl.DateTimeFormat(locale, {
month: 'long',
});
let months = [
monthFormatter.format(new Date(2020, 0, 1)),
monthFormatter.format(new Date(2020, 1, 1)),
monthFormatter.format(new Date(2020, 2, 1)),
monthFormatter.format(new Date(2020, 3, 1)),
monthFormatter.format(new Date(2020, 4, 1)),
monthFormatter.format(new Date(2020, 5, 1)),
monthFormatter.format(new Date(2020, 6, 1)),
monthFormatter.format(new Date(2020, 7, 1)),
monthFormatter.format(new Date(2020, 8, 1)),
monthFormatter.format(new Date(2020, 9, 1)),
monthFormatter.format(new Date(2020, 10, 1)),
monthFormatter.format(new Date(2020, 11, 1)),
];
let days = [
longWeekDayFormatter.format(new Date(2020, 10, 1)),
longWeekDayFormatter.format(new Date(2020, 10, 2)),
longWeekDayFormatter.format(new Date(2020, 10, 3)),
longWeekDayFormatter.format(new Date(2020, 10, 4)),
longWeekDayFormatter.format(new Date(2020, 10, 5)),
longWeekDayFormatter.format(new Date(2020, 10, 6)),
longWeekDayFormatter.format(new Date(2020, 10, 7)),
];
let shortDays = [
shortWeekDayFormatter.format(new Date(2020, 10, 1)).slice(0, 2),
shortWeekDayFormatter.format(new Date(2020, 10, 2)).slice(0, 2),
shortWeekDayFormatter.format(new Date(2020, 10, 3)).slice(0, 2),
shortWeekDayFormatter.format(new Date(2020, 10, 4)).slice(0, 2),
shortWeekDayFormatter.format(new Date(2020, 10, 5)).slice(0, 2),
shortWeekDayFormatter.format(new Date(2020, 10, 6)).slice(0, 2),
shortWeekDayFormatter.format(new Date(2020, 10, 7)).slice(0, 2),
];
return {
months,
shortDays,
days,
};
};
const DatePicker = _react.forwardRef((props, forwardedRef) => {
let {
date,
onChange,
localizedNames,
className,
setFocus = false,
showTime = false,
use12Hours = false,
precision,
hourStep,
minuteStep,
secondStep,
useCombinedRenderer,
combinedRenderer,
hourRenderer,
minuteRenderer,
secondRenderer,
meridiemRenderer,
showYearSelection = false,
enableRangeSelect = false,
startDate,
endDate,
monthYearProps,
calendarProps,
monthProps,
weekDayProps,
dayProps,
weekProps,
isDateDisabled,
applyBackground = true,
showDatesOutsideMonth = true,
...rest
} = props;
let logWarning = (0, _index.useWarningLogger)();
if ('development' === process.env.NODE_ENV) {
let onlyOneRangePropPassed = (startDate ? 1 : 0) + (endDate ? 1 : 0) === 1;
if (enableRangeSelect && onlyOneRangePropPassed)
logWarning(
'`DatePicker` with `enableRangeSelect` needs *both* `startDate` and `endDate` to either be `Date` or `undefined`. Passing `Date` to just one of them is not allowed.',
);
}
let monthNames = localizedNames?.months ?? defaultMonths;
let shortDays = localizedNames?.shortDays ?? defaultShortDays;
let longDays = localizedNames?.days ?? defaultLongDays;
let [selectedDay, setSelectedDay] = _react.useState(date);
let [selectedStartDay, setSelectedStartDay] = _react.useState(startDate);
let [selectedEndDay, setSelectedEndDay] = _react.useState(endDate);
let [focusedDay, setFocusedDay] = _react.useState(
selectedStartDay ?? selectedDay ?? new Date(),
);
let [displayedMonthIndex, setDisplayedMonthIndex] = _react.useState(
selectedStartDay?.getMonth() ??
selectedDay?.getMonth() ??
new Date().getMonth(),
);
let [displayedYear, setDisplayedYear] = _react.useState(
selectedStartDay?.getFullYear() ??
selectedDay?.getFullYear() ??
new Date().getFullYear(),
);
let [isSelectingStartDate, setIsSelectingStartDate] = _react.useState(true);
let needFocus = _react.useRef(setFocus);
_react.useEffect(() => {
if (needFocus.current) needFocus.current = false;
});
let setMonthAndYear = _react.useCallback((newMonth, newYear) => {
setDisplayedMonthIndex(newMonth);
setDisplayedYear(newYear);
}, []);
_react.useEffect(() => {
let currentDate = new Date();
setSelectedDay(date);
setSelectedStartDay(startDate);
setSelectedEndDay(endDate);
if (!enableRangeSelect) setFocusedDay(date ?? currentDate);
setMonthAndYear(
startDate?.getMonth() ?? date?.getMonth() ?? currentDate.getMonth(),
startDate?.getFullYear() ??
date?.getFullYear() ??
currentDate.getFullYear(),
);
}, [date, setMonthAndYear, startDate, endDate, enableRangeSelect]);
let popoverInitialFocusContext = _react.useContext(
_Popover.PopoverInitialFocusContext,
);
(0, _index.useLayoutEffect)(() => {
if (setFocus && popoverInitialFocusContext)
popoverInitialFocusContext.setInitialFocus(-1);
}, [popoverInitialFocusContext, setFocus]);
let days = _react.useMemo(() => {
let offsetToFirst = new Date(
displayedYear,
displayedMonthIndex,
1,
).getDay();
if (0 === offsetToFirst && showDatesOutsideMonth) offsetToFirst = 7;
let daysInMonth = [];
for (let i = 1; i <= 42; i++) {
let adjustedDay = i - offsetToFirst;
daysInMonth.push(
new Date(displayedYear, displayedMonthIndex, adjustedDay),
);
}
return daysInMonth;
}, [displayedMonthIndex, displayedYear, showDatesOutsideMonth]);
let weeks = _react.useMemo(() => {
let weeksInMonth = [];
let weekCount = Math.ceil(days.length / 7);
for (let i = 0; i < weekCount; i++)
weeksInMonth.push(days.slice(7 * i, (i + 1) * 7));
return weeksInMonth;
}, [days]);
let getNewFocusedDate = (newYear, newMonth) => {
let currentDate = selectedStartDay ?? selectedDay ?? new Date();
let newDate = new Date(
newYear,
newMonth,
currentDate.getDate(),
currentDate.getHours(),
currentDate.getMinutes(),
currentDate.getSeconds(),
);
return newDate;
};
let handleMoveToPreviousYear = () => {
let newYear = displayedYear - 1;
setMonthAndYear(displayedMonthIndex, newYear);
setFocusedDay(getNewFocusedDate(newYear, displayedMonthIndex));
};
let handleMoveToNextYear = () => {
let newYear = displayedYear + 1;
setMonthAndYear(displayedMonthIndex, newYear);
setFocusedDay(getNewFocusedDate(newYear, displayedMonthIndex));
};
let handleMoveToPreviousMonth = () => {
let newMonth = 0 !== displayedMonthIndex ? displayedMonthIndex - 1 : 11;
let newYear = 0 !== displayedMonthIndex ? displayedYear : displayedYear - 1;
setMonthAndYear(newMonth, newYear);
setFocusedDay(getNewFocusedDate(newYear, newMonth));
};
let handleMoveToNextMonth = () => {
let newMonth = 11 !== displayedMonthIndex ? displayedMonthIndex + 1 : 0;
let newYear =
11 !== displayedMonthIndex ? displayedYear : displayedYear + 1;
setMonthAndYear(newMonth, newYear);
setFocusedDay(getNewFocusedDate(newYear, newMonth));
};
let onDayClick = (day) => {
if (enableRangeSelect)
if (isSelectingStartDate) {
if (day.getMonth() !== selectedStartDay?.getMonth())
setMonthAndYear(day.getMonth(), day.getFullYear());
let currentStartDate = selectedStartDay ?? new Date();
let newStartDate = new Date(
day.getFullYear(),
day.getMonth(),
day.getDate(),
currentStartDate.getHours(),
currentStartDate.getMinutes(),
currentStartDate.getSeconds(),
);
setSelectedStartDay(newStartDate);
setFocusedDay(newStartDate);
if ((0, _index.isBefore)(newStartDate, selectedEndDay))
selectedEndDay && onChange?.(newStartDate, selectedEndDay);
else {
setSelectedEndDay(newStartDate);
onChange?.(newStartDate, newStartDate);
}
setIsSelectingStartDate(false);
} else {
if (day.getMonth() !== selectedEndDay?.getMonth())
setMonthAndYear(day.getMonth(), day.getFullYear());
let currentEndDate = selectedEndDay ?? new Date();
let newEndDate = new Date(
day.getFullYear(),
day.getMonth(),
day.getDate(),
currentEndDate.getHours(),
currentEndDate.getMinutes(),
currentEndDate.getSeconds(),
);
setFocusedDay(newEndDate);
if ((0, _index.isBefore)(newEndDate, selectedStartDay)) {
setSelectedStartDay(newEndDate);
selectedEndDay && onChange?.(newEndDate, selectedEndDay);
} else {
setSelectedEndDay(newEndDate);
selectedStartDay && onChange?.(selectedStartDay, newEndDate);
setIsSelectingStartDate(true);
}
}
else {
if (day.getMonth() !== selectedDay?.getMonth())
setMonthAndYear(day.getMonth(), day.getFullYear());
let currentDate = selectedDay ?? new Date();
let newDate = new Date(
day.getFullYear(),
day.getMonth(),
day.getDate(),
currentDate.getHours(),
currentDate.getMinutes(),
currentDate.getSeconds(),
);
setSelectedDay(newDate);
setFocusedDay(newDate);
isSingleOnChange(onChange, enableRangeSelect) && onChange?.(newDate);
}
};
let handleCalendarKeyDown = (event) => {
if (event.altKey) return;
if (!focusedDay) return;
let adjustedFocusedDay = new Date(focusedDay);
switch (event.key) {
case 'ArrowDown':
adjustedFocusedDay.setDate(focusedDay.getDate() + 7);
if (adjustedFocusedDay.getMonth() !== displayedMonthIndex)
handleMoveToNextMonth();
setFocusedDay(adjustedFocusedDay);
needFocus.current = true;
event.preventDefault();
break;
case 'ArrowUp':
adjustedFocusedDay.setDate(focusedDay.getDate() - 7);
if (adjustedFocusedDay.getMonth() !== displayedMonthIndex)
handleMoveToPreviousMonth();
setFocusedDay(adjustedFocusedDay);
needFocus.current = true;
event.preventDefault();
break;
case 'ArrowLeft':
adjustedFocusedDay.setDate(focusedDay.getDate() - 1);
if (adjustedFocusedDay.getMonth() !== displayedMonthIndex)
handleMoveToPreviousMonth();
setFocusedDay(adjustedFocusedDay);
needFocus.current = true;
event.preventDefault();
break;
case 'ArrowRight':
adjustedFocusedDay.setDate(focusedDay.getDate() + 1);
if (adjustedFocusedDay.getMonth() !== displayedMonthIndex)
handleMoveToNextMonth();
setFocusedDay(adjustedFocusedDay);
needFocus.current = true;
event.preventDefault();
break;
case 'Enter':
case ' ':
case 'Spacebar':
if (!isDateDisabled?.(focusedDay)) onDayClick(focusedDay);
event.preventDefault();
break;
}
};
let getDayClass = (day) => {
if (day.getMonth() !== displayedMonthIndex)
return 'iui-calendar-day-outside-month';
let dayClass = 'iui-calendar-day';
let isSelectedDay =
isSameDay(day, selectedDay) ||
(isSameDay(day, selectedStartDay) && isSameDay(day, selectedEndDay));
if (isSelectedDay) dayClass += '-selected';
else if (isSameDay(day, selectedStartDay)) dayClass += '-range-start';
else if (isSameDay(day, selectedEndDay)) dayClass += '-range-end';
if (
selectedStartDay &&
selectedEndDay &&
isInDateRange(day, selectedStartDay, selectedEndDay)
)
dayClass += '-range';
if (isSameDay(day, new Date())) dayClass += '-today';
return dayClass;
};
let dateTableId = (0, _index.useId)();
return _react.createElement(
_index.Box,
{
className: (0, _classnames.default)(
'iui-date-picker',
{
'iui-popover-surface': applyBackground,
},
className,
),
ref: forwardedRef,
...rest,
},
_react.createElement(
'div',
null,
_react.createElement(
_index.Box,
{
as: 'div',
...monthYearProps,
className: (0, _classnames.default)(
'iui-calendar-month-year',
monthYearProps?.className,
),
},
showYearSelection &&
_react.createElement(
_IconButton.IconButton,
{
styleType: 'borderless',
onClick: handleMoveToPreviousYear,
'aria-label': 'Previous year',
size: 'small',
},
_react.createElement(_index.SvgChevronLeftDouble, null),
),
_react.createElement(
_IconButton.IconButton,
{
styleType: 'borderless',
onClick: handleMoveToPreviousMonth,
'aria-label': 'Previous month',
size: 'small',
},
_react.createElement(_index.SvgChevronLeft, null),
),
_react.createElement(
'span',
{
'aria-live': 'polite',
},
_react.createElement(
_index.Box,
{
as: 'span',
id: dateTableId,
title: monthNames[displayedMonthIndex],
...monthProps,
className: (0, _classnames.default)(
'iui-calendar-month',
monthProps?.className,
),
},
monthNames[displayedMonthIndex],
),
' ',
displayedYear,
),
_react.createElement(
_IconButton.IconButton,
{
styleType: 'borderless',
onClick: handleMoveToNextMonth,
'aria-label': 'Next month',
size: 'small',
},
_react.createElement(_index.SvgChevronRight, null),
),
showYearSelection &&
_react.createElement(
_IconButton.IconButton,
{
styleType: 'borderless',
onClick: handleMoveToNextYear,
'aria-label': 'Next year',
size: 'small',
},
_react.createElement(_index.SvgChevronRightDouble, null),
),
),
_react.createElement(
_index.Box,
{
as: 'div',
...weekDayProps,
className: (0, _classnames.default)(
'iui-calendar-weekdays',
weekDayProps?.className,
),
},
shortDays.map((day, index) =>
_react.createElement(
'div',
{
key: day,
title: longDays[index],
},
day,
),
),
),
_react.createElement(
'div',
{
onKeyDown: handleCalendarKeyDown,
role: 'listbox',
'aria-labelledby': dateTableId,
...calendarProps,
},
weeks.map((weekDays, weekIndex) =>
_react.createElement(
_index.Box,
{
as: 'div',
key: `week-${displayedMonthIndex}-${weekIndex}`,
...weekProps,
className: (0, _classnames.default)(
'iui-calendar-week',
weekProps?.className,
),
},
weekDays.map((weekDay, dayIndex) => {
let dateValue = weekDay.getDate();
let isDisabled = isDateDisabled?.(weekDay);
let isOutsideMonth = weekDay.getMonth() !== displayedMonthIndex;
if (isOutsideMonth && !showDatesOutsideMonth)
return _react.createElement(_index.Box, {
key: `day-${displayedMonthIndex}-${dayIndex}`,
className: (0, _classnames.default)(
getDayClass(weekDay),
dayProps?.className,
),
'aria-hidden': true,
});
return _react.createElement(
_index.Box,
{
as: 'div',
key: `day-${displayedMonthIndex}-${dayIndex}`,
onClick: () => !isDisabled && onDayClick(weekDay),
role: 'option',
tabIndex: isSameDay(weekDay, focusedDay) ? 0 : -1,
'aria-disabled': isDisabled ? 'true' : void 0,
ref: (element) => {
if (isSameDay(weekDay, focusedDay) && needFocus.current)
setTimeout(() => {
element?.focus();
});
},
...dayProps,
className: (0, _classnames.default)(
getDayClass(weekDay),
dayProps?.className,
),
},
dateValue,
);
}),
),
),
),
),
showTime &&
_react.createElement(_TimePicker.TimePicker, {
date: selectedStartDay ?? selectedDay,
use12Hours: use12Hours,
precision: precision,
hourStep: hourStep,
minuteStep: minuteStep,
secondStep: secondStep,
useCombinedRenderer: useCombinedRenderer,
combinedRenderer: combinedRenderer,
hourRenderer: hourRenderer,
minuteRenderer: minuteRenderer,
secondRenderer: secondRenderer,
meridiemRenderer: meridiemRenderer,
onChange: (date) =>
isSingleOnChange(onChange, enableRangeSelect)
? onChange?.(date)
: onChange?.(
new Date(
selectedStartDay?.getFullYear() ?? date.getFullYear(),
selectedStartDay?.getMonth() ?? date.getMonth(),
selectedStartDay?.getDate() ?? date.getDate(),
date.getHours(),
date.getMinutes(),
date.getSeconds(),
),
new Date(
selectedEndDay?.getFullYear() ?? date.getFullYear(),
selectedEndDay?.getMonth() ?? date.getMonth(),
selectedEndDay?.getDate() ?? date.getDate(),
date.getHours(),
date.getMinutes(),
date.getSeconds(),
),
),
}),
);
});
if ('development' === process.env.NODE_ENV)
DatePicker.displayName = 'DatePicker';