@wordpress/components
Version:
UI components for WordPress.
260 lines (222 loc) • 8.99 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.DatePicker = DatePicker;
exports.default = void 0;
var _element = require("@wordpress/element");
var _useLilius = require("use-lilius");
var _dateFns = require("date-fns");
var _i18n = require("@wordpress/i18n");
var _icons = require("@wordpress/icons");
var _date = require("@wordpress/date");
var _styles = require("./styles");
var _utils = require("../utils");
var _button = _interopRequireDefault(require("../../button"));
var _constants = require("../constants");
/**
* External dependencies
*/
/**
* WordPress dependencies
*/
/**
* DatePicker is a React component that renders a calendar for date selection.
*
* ```jsx
* import { DatePicker } from '@wordpress/components';
* import { useState } from '@wordpress/element';
*
* const MyDatePicker = () => {
* const [ date, setDate ] = useState( new Date() );
*
* return (
* <DatePicker
* currentDate={ date }
* onChange={ ( newDate ) => setDate( newDate ) }
* />
* );
* };
* ```
*/
function DatePicker(_ref) {
let {
currentDate,
onChange,
events = [],
isInvalidDate,
onMonthPreviewed,
startOfWeek: weekStartsOn = 0
} = _ref;
const date = currentDate ? (0, _utils.inputToDate)(currentDate) : new Date();
const {
calendar,
viewing,
setSelected,
setViewing,
isSelected,
viewPreviousMonth,
viewNextMonth
} = (0, _useLilius.useLilius)({
selected: [(0, _dateFns.startOfDay)(date)],
viewing: (0, _dateFns.startOfDay)(date),
weekStartsOn
}); // Used to implement a roving tab index. Tracks the day that receives focus
// when the user tabs into the calendar.
const [focusable, setFocusable] = (0, _element.useState)((0, _dateFns.startOfDay)(date)); // Allows us to only programmatically focus() a day when focus was already
// within the calendar. This stops us stealing focus from e.g. a TimePicker
// input.
const [isFocusWithinCalendar, setIsFocusWithinCalendar] = (0, _element.useState)(false); // Update internal state when currentDate prop changes.
const [prevCurrentDate, setPrevCurrentDate] = (0, _element.useState)(currentDate);
if (currentDate !== prevCurrentDate) {
setPrevCurrentDate(currentDate);
setSelected([(0, _dateFns.startOfDay)(date)]);
setViewing((0, _dateFns.startOfDay)(date));
setFocusable((0, _dateFns.startOfDay)(date));
}
return (0, _element.createElement)(_styles.Wrapper, {
className: "components-datetime__date",
role: "application",
"aria-label": (0, _i18n.__)('Calendar')
}, (0, _element.createElement)(_styles.Navigator, null, (0, _element.createElement)(_button.default, {
icon: (0, _i18n.isRTL)() ? _icons.arrowRight : _icons.arrowLeft,
variant: "tertiary",
"aria-label": (0, _i18n.__)('View previous month'),
onClick: () => {
viewPreviousMonth();
setFocusable((0, _dateFns.subMonths)(focusable, 1));
onMonthPreviewed === null || onMonthPreviewed === void 0 ? void 0 : onMonthPreviewed((0, _dateFns.format)((0, _dateFns.subMonths)(viewing, 1), _constants.TIMEZONELESS_FORMAT));
}
}), (0, _element.createElement)(_styles.NavigatorHeading, {
level: 3
}, (0, _element.createElement)("strong", null, (0, _date.dateI18n)('F', viewing, -viewing.getTimezoneOffset())), ' ', (0, _date.dateI18n)('Y', viewing, -viewing.getTimezoneOffset())), (0, _element.createElement)(_button.default, {
icon: (0, _i18n.isRTL)() ? _icons.arrowLeft : _icons.arrowRight,
variant: "tertiary",
"aria-label": (0, _i18n.__)('View next month'),
onClick: () => {
viewNextMonth();
setFocusable((0, _dateFns.addMonths)(focusable, 1));
onMonthPreviewed === null || onMonthPreviewed === void 0 ? void 0 : onMonthPreviewed((0, _dateFns.format)((0, _dateFns.addMonths)(viewing, 1), _constants.TIMEZONELESS_FORMAT));
}
})), (0, _element.createElement)(_styles.Calendar, {
onFocus: () => setIsFocusWithinCalendar(true),
onBlur: () => setIsFocusWithinCalendar(false)
}, calendar[0][0].map(day => (0, _element.createElement)(_styles.DayOfWeek, {
key: day.toString()
}, (0, _date.dateI18n)('D', day, -day.getTimezoneOffset()))), calendar[0].map(week => week.map((day, index) => {
if (!(0, _dateFns.isSameMonth)(day, viewing)) {
return null;
}
return (0, _element.createElement)(Day, {
key: day.toString(),
day: day,
column: index + 1,
isSelected: isSelected(day),
isFocusable: (0, _dateFns.isEqual)(day, focusable),
isFocusAllowed: isFocusWithinCalendar,
isToday: (0, _dateFns.isSameDay)(day, new Date()),
isInvalid: isInvalidDate ? isInvalidDate(day) : false,
numEvents: events.filter(event => (0, _dateFns.isSameDay)(event.date, day)).length,
onClick: () => {
setSelected([day]);
setFocusable(day);
onChange === null || onChange === void 0 ? void 0 : onChange((0, _dateFns.format)( // Don't change the selected date's time fields.
new Date(day.getFullYear(), day.getMonth(), day.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds()), _constants.TIMEZONELESS_FORMAT));
},
onKeyDown: event => {
let nextFocusable;
if (event.key === 'ArrowLeft') {
nextFocusable = (0, _dateFns.addDays)(day, (0, _i18n.isRTL)() ? 1 : -1);
}
if (event.key === 'ArrowRight') {
nextFocusable = (0, _dateFns.addDays)(day, (0, _i18n.isRTL)() ? -1 : 1);
}
if (event.key === 'ArrowUp') {
nextFocusable = (0, _dateFns.subWeeks)(day, 1);
}
if (event.key === 'ArrowDown') {
nextFocusable = (0, _dateFns.addWeeks)(day, 1);
}
if (event.key === 'PageUp') {
nextFocusable = (0, _dateFns.subMonths)(day, 1);
}
if (event.key === 'PageDown') {
nextFocusable = (0, _dateFns.addMonths)(day, 1);
}
if (event.key === 'Home') {
nextFocusable = (0, _dateFns.startOfWeek)(day);
}
if (event.key === 'End') {
nextFocusable = (0, _dateFns.startOfDay)((0, _dateFns.endOfWeek)(day));
}
if (nextFocusable) {
event.preventDefault();
setFocusable(nextFocusable);
if (!(0, _dateFns.isSameMonth)(nextFocusable, viewing)) {
setViewing(nextFocusable);
onMonthPreviewed === null || onMonthPreviewed === void 0 ? void 0 : onMonthPreviewed((0, _dateFns.format)(nextFocusable, _constants.TIMEZONELESS_FORMAT));
}
}
}
});
}))));
}
function Day(_ref2) {
let {
day,
column,
isSelected,
isFocusable,
isFocusAllowed,
isToday,
isInvalid,
numEvents,
onClick,
onKeyDown
} = _ref2;
const ref = (0, _element.useRef)(); // Focus the day when it becomes focusable, e.g. because an arrow key is
// pressed. Only do this if focus is allowed - this stops us stealing focus
// from e.g. a TimePicker input.
(0, _element.useEffect)(() => {
if (ref.current && isFocusable && isFocusAllowed) {
ref.current.focus();
} // isFocusAllowed is not a dep as there is no point calling focus() on
// an already focused element.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isFocusable]);
return (0, _element.createElement)(_styles.DayButton, {
ref: ref,
className: "components-datetime__date__day" // Unused, for backwards compatibility.
,
disabled: isInvalid,
tabIndex: isFocusable ? 0 : -1,
"aria-label": getDayLabel(day, isSelected, numEvents),
column: column,
isSelected: isSelected,
isToday: isToday,
hasEvents: numEvents > 0,
onClick: onClick,
onKeyDown: onKeyDown
}, (0, _date.dateI18n)('j', day, -day.getTimezoneOffset()));
}
function getDayLabel(date, isSelected, numEvents) {
const {
formats
} = (0, _date.getSettings)();
const localizedDate = (0, _date.dateI18n)(formats.date, date, -date.getTimezoneOffset());
if (isSelected && numEvents > 0) {
return (0, _i18n.sprintf)( // translators: 1: The calendar date. 2: Number of events on the calendar date.
(0, _i18n._n)('%1$s. Selected. There is %2$d event', '%1$s. Selected. There are %2$d events', numEvents), localizedDate, numEvents);
} else if (isSelected) {
return (0, _i18n.sprintf)( // translators: %s: The calendar date.
(0, _i18n.__)('%1$s. Selected'), localizedDate);
} else if (numEvents > 0) {
return (0, _i18n.sprintf)( // translators: 1: The calendar date. 2: Number of events on the calendar date.
(0, _i18n._n)('%1$s. There is %2$d event', '%1$s. There are %2$d events', numEvents), localizedDate, numEvents);
}
return localizedDate;
}
var _default = DatePicker;
exports.default = _default;
//# sourceMappingURL=index.js.map