UNPKG

@wordpress/components

Version:
249 lines (241 loc) 9.25 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.DatePicker = DatePicker; exports.default = void 0; var _dateFns = require("date-fns"); var _i18n = require("@wordpress/i18n"); var _icons = require("@wordpress/icons"); var _date = require("@wordpress/date"); var _element = require("@wordpress/element"); var _useLilius = require("./use-lilius"); var _styles = require("./styles"); var _utils = require("../utils"); var _button = _interopRequireDefault(require("../../button")); var _constants = require("../constants"); var _jsxRuntime = require("react/jsx-runtime"); /** * External dependencies */ /** * WordPress dependencies */ /** * Internal 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({ currentDate, onChange, events = [], isInvalidDate, onMonthPreviewed, startOfWeek: weekStartsOn = 0 }) { 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 /*#__PURE__*/(0, _jsxRuntime.jsxs)(_styles.Wrapper, { className: "components-datetime__date", role: "application", "aria-label": (0, _i18n.__)('Calendar'), children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_styles.Navigator, { children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_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?.((0, _dateFns.format)((0, _dateFns.subMonths)(viewing, 1), _constants.TIMEZONELESS_FORMAT)); }, size: "compact" }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_styles.NavigatorHeading, { level: 3, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("strong", { children: (0, _date.dateI18n)('F', viewing, -viewing.getTimezoneOffset()) }), ' ', (0, _date.dateI18n)('Y', viewing, -viewing.getTimezoneOffset())] }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_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?.((0, _dateFns.format)((0, _dateFns.addMonths)(viewing, 1), _constants.TIMEZONELESS_FORMAT)); }, size: "compact" })] }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_styles.Calendar, { onFocus: () => setIsFocusWithinCalendar(true), onBlur: () => setIsFocusWithinCalendar(false), children: [calendar[0][0].map(day => /*#__PURE__*/(0, _jsxRuntime.jsx)(_styles.DayOfWeek, { children: (0, _date.dateI18n)('D', day, -day.getTimezoneOffset()) }, day.toString())), calendar[0].map(week => week.map((day, index) => { if (!(0, _dateFns.isSameMonth)(day, viewing)) { return null; } return /*#__PURE__*/(0, _jsxRuntime.jsx)(Day, { 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?.((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?.((0, _dateFns.format)(nextFocusable, _constants.TIMEZONELESS_FORMAT)); } } } }, day.toString()); }))] })] }); } function Day({ day, column, isSelected, isFocusable, isFocusAllowed, isToday, isInvalid, numEvents, onClick, onKeyDown }) { 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. }, [isFocusable]); return /*#__PURE__*/(0, _jsxRuntime.jsx)(_styles.DayButton, { __next40pxDefaultSize: true, 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, children: (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: 1: 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 = exports.default = DatePicker; //# sourceMappingURL=index.js.map