UNPKG

grommet

Version:

focus on the essential experience

494 lines (423 loc) 19.1 kB
"use strict"; exports.__esModule = true; exports.Calendar = void 0; var _react = _interopRequireWildcard(require("react")); var _styledComponents = require("styled-components"); var _defaultProps = require("../../default-props"); var _Box = require("../Box"); var _Button = require("../Button"); var _Heading = require("../Heading"); var _Keyboard = require("../Keyboard"); var _StyledCalendar = require("./StyledCalendar"); var _utils = require("./utils"); function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } var headingPadMap = { small: 'xsmall', medium: 'small', large: 'medium' }; var normalizeReference = function normalizeReference(reference, date, dates) { var normalizedReference; if (reference) { normalizedReference = new Date(reference); } else if (date) { normalizedReference = new Date(date); } else if (dates && dates.length > 0) { if (typeof dates[0] === 'string') { normalizedReference = new Date(dates[0]); } else if (Array.isArray(dates[0])) { normalizedReference = new Date(dates[0][0]); } else { normalizedReference = new Date(); normalizedReference.setHours(0, 0, 0, 0); } } else { normalizedReference = new Date(); normalizedReference.setHours(0, 0, 0, 0); } return normalizedReference; }; var buildDisplayBounds = function buildDisplayBounds(reference, firstDayOfWeek) { var start = new Date(reference); start.setDate(1); // first of month // In case Sunday is the first day of the month, and the user asked for Monday // to be the first day of the week, then we need to include Sunday and six // days prior. start = start.getDay() === 0 && firstDayOfWeek === 1 ? start = (0, _utils.subtractDays)(start, 6) : // beginning of week start = (0, _utils.subtractDays)(start, start.getDay() - firstDayOfWeek); var end = (0, _utils.addDays)(start, 7 * 5 + 7); // 5 weeks to end of week return [start, end]; }; var Calendar = /*#__PURE__*/(0, _react.forwardRef)(function (_ref, ref) { var _ref$animate = _ref.animate, animate = _ref$animate === void 0 ? true : _ref$animate, validBounds = _ref.bounds, dateProp = _ref.date, datesProp = _ref.dates, daysOfWeek = _ref.daysOfWeek, disabled = _ref.disabled, _ref$firstDayOfWeek = _ref.firstDayOfWeek, firstDayOfWeek = _ref$firstDayOfWeek === void 0 ? 0 : _ref$firstDayOfWeek, header = _ref.header, _ref$locale = _ref.locale, locale = _ref$locale === void 0 ? 'en-US' : _ref$locale, onReference = _ref.onReference, onSelect = _ref.onSelect, range = _ref.range, referenceProp = _ref.reference, _ref$showAdjacentDays = _ref.showAdjacentDays, showAdjacentDays = _ref$showAdjacentDays === void 0 ? true : _ref$showAdjacentDays, _ref$size = _ref.size, size = _ref$size === void 0 ? 'medium' : _ref$size, rest = _objectWithoutPropertiesLoose(_ref, ["animate", "bounds", "date", "dates", "daysOfWeek", "disabled", "firstDayOfWeek", "header", "locale", "onReference", "onSelect", "range", "reference", "showAdjacentDays", "size"]); var theme = (0, _react.useContext)(_styledComponents.ThemeContext) || _defaultProps.defaultProps.theme; // set date when caller changes it, allows us to change it internally too var _useState = (0, _react.useState)(dateProp), date = _useState[0], setDate = _useState[1]; (0, _react.useEffect)(function () { return setDate(dateProp); }, [dateProp]); // set dates when caller changes it, allows us to change it internally too var _useState2 = (0, _react.useState)(datesProp), dates = _useState2[0], setDates = _useState2[1]; (0, _react.useEffect)(function () { return setDates(datesProp); }, [datesProp]); // set reference based on what the caller passed or date/dates. var _useState3 = (0, _react.useState)(normalizeReference(referenceProp, date, dates)), reference = _useState3[0], setReference = _useState3[1]; (0, _react.useEffect)(function () { return setReference(normalizeReference(referenceProp, dateProp, datesProp)); }, [dateProp, datesProp, referenceProp]); // calculate the bounds we display based on the reference var _useState4 = (0, _react.useState)(buildDisplayBounds(reference, firstDayOfWeek)), displayBounds = _useState4[0], setDisplayBounds = _useState4[1]; var _useState5 = (0, _react.useState)(), targetDisplayBounds = _useState5[0], setTargetDisplayBounds = _useState5[1]; var _useState6 = (0, _react.useState)(), slide = _useState6[0], setSlide = _useState6[1]; // When the reference changes, we need to update the displayBounds. // This is easy when we aren't animating. If we are animating, // we temporarily increase the displayBounds to be the union of the old // and new ones and set slide to drive the animation. We keep track // of where we are heading via targetDisplayBounds. When the animation // finishes, we prune displayBounds down to where we are headed and // clear the slide and targetDisplayBounds. (0, _react.useEffect)(function () { var nextDisplayBounds = buildDisplayBounds(reference, firstDayOfWeek); if (!animate) { setDisplayBounds(nextDisplayBounds); } else { setTargetDisplayBounds(nextDisplayBounds); } }, [animate, firstDayOfWeek, reference]); (0, _react.useEffect)(function () { if (targetDisplayBounds) { if (targetDisplayBounds[0].getTime() < displayBounds[0].getTime()) { setDisplayBounds([targetDisplayBounds[0], displayBounds[1]]); setSlide({ direction: 'down', weeks: (0, _utils.daysApart)(displayBounds[0], targetDisplayBounds[0]) / 7 }); } else if (targetDisplayBounds[1].getTime() > displayBounds[1].getTime()) { setDisplayBounds([displayBounds[0], targetDisplayBounds[1]]); setSlide({ direction: 'up', weeks: (0, _utils.daysApart)(targetDisplayBounds[1], displayBounds[1]) / 7 }); } // Wait for animation to finish before cleaning up. var timer = setTimeout(function () { setDisplayBounds(targetDisplayBounds); setTargetDisplayBounds(undefined); setSlide(undefined); }, 400 // Empirically determined. ); return function () { return clearTimeout(timer); }; } setSlide(undefined); return undefined; }, [displayBounds, targetDisplayBounds]); // We have to deal with reference being the end of a month with more // days than the month we are changing to. So, we always set reference // to the first of the month before changing the month. var previousMonth = (0, _react.useMemo)(function () { return (0, _utils.endOfMonth)((0, _utils.subtractMonths)((0, _utils.startOfMonth)(reference), 1)); }, [reference]); var nextMonth = (0, _react.useMemo)(function () { return (0, _utils.startOfMonth)((0, _utils.addMonths)((0, _utils.startOfMonth)(reference), 1)); }, [reference]); var _useState7 = (0, _react.useState)(), focus = _useState7[0], setFocus = _useState7[1]; var _useState8 = (0, _react.useState)(), active = _useState8[0], setActive = _useState8[1]; // when working on a range, remember the last selected date so we know // how to handle subsequent date selection var _useState9 = (0, _react.useState)(), lastSelectedDate = _useState9[0], setLastSelectedDate = _useState9[1]; var changeReference = (0, _react.useCallback)(function (nextReference) { if ((0, _utils.betweenDates)(nextReference, validBounds)) { setReference(nextReference); if (onReference) onReference(nextReference.toISOString()); } }, [onReference, validBounds]); var selectDate = (0, _react.useCallback)(function (selectedDate) { var nextDates; var nextDate; if (!range) { nextDate = selectedDate; } else if (!dates) { if (!date) { nextDate = selectedDate; } else { var priorDate = new Date(date); var selDate = new Date(selectedDate); if (priorDate.getTime() < selDate.getTime()) { nextDates = [[date, selectedDate]]; nextDate = undefined; } else if (priorDate.getTime() > selDate.getTime()) { nextDates = [[selectedDate, date]]; nextDate = undefined; } else { nextDate = undefined; } } } else { // have dates var priorDates = dates[0].map(function (d) { return new Date(d); }); var previousDate = new Date(lastSelectedDate || dates[0][0]); var _selDate = new Date(selectedDate); if (_selDate.getTime() === priorDates[0].getTime()) { var _dates$ = dates[0]; nextDate = _dates$[1]; nextDates = undefined; } else if (_selDate.getTime() === priorDates[1].getTime()) { var _dates$2 = dates[0]; nextDate = _dates$2[0]; nextDates = undefined; } else if (_selDate.getTime() < previousDate.getTime()) { if (_selDate.getTime() < priorDates[0].getTime()) { nextDates = [[selectedDate, dates[0][1]]]; } else if (_selDate.getTime() > priorDates[0].getTime()) { nextDates = [[dates[0][0], selectedDate]]; } } else if (_selDate.getTime() > previousDate.getTime()) { if (_selDate.getTime() > priorDates[1].getTime()) { nextDates = [[dates[0][0], selectedDate]]; } else if (_selDate.getTime() < priorDates[1].getTime()) { nextDates = [[selectedDate, dates[0][1]]]; } } } setDates(nextDates); if (!dates) setDate(nextDate); setActive(new Date(selectedDate)); setLastSelectedDate(selectedDate); if (onSelect) onSelect(nextDates || nextDate); }, [date, dates, lastSelectedDate, onSelect, range]); var renderCalendarHeader = function renderCalendarHeader() { var PreviousIcon = size === 'small' ? theme.calendar.icons.small.previous : theme.calendar.icons.previous; var NextIcon = size === 'small' ? theme.calendar.icons.small.next : theme.calendar.icons.next; return /*#__PURE__*/_react["default"].createElement(_Box.Box, { direction: "row", justify: "between", align: "center" }, /*#__PURE__*/_react["default"].createElement(_Box.Box, { flex: true, pad: { horizontal: headingPadMap[size] || 'small' } }, /*#__PURE__*/_react["default"].createElement(_Heading.Heading, { level: size === 'small' ? theme.calendar.heading && theme.calendar.heading.level || 4 : (theme.calendar.heading && theme.calendar.heading.level || 4) - 1, size: size, margin: "none" }, reference.toLocaleDateString(locale, { month: 'long', year: 'numeric' }))), /*#__PURE__*/_react["default"].createElement(_Box.Box, { flex: false, direction: "row", align: "center" }, /*#__PURE__*/_react["default"].createElement(_Button.Button, { a11yTitle: previousMonth.toLocaleDateString(locale, { month: 'long', year: 'numeric' }), icon: /*#__PURE__*/_react["default"].createElement(PreviousIcon, { size: size !== 'small' ? size : undefined }), disabled: !(0, _utils.betweenDates)(previousMonth, validBounds), onClick: function onClick() { return changeReference(previousMonth); } }), /*#__PURE__*/_react["default"].createElement(_Button.Button, { a11yTitle: nextMonth.toLocaleDateString(locale, { month: 'long', year: 'numeric' }), icon: /*#__PURE__*/_react["default"].createElement(NextIcon, { size: size !== 'small' ? size : undefined }), disabled: !(0, _utils.betweenDates)(nextMonth, validBounds), onClick: function onClick() { return changeReference(nextMonth); } }))); }; var renderDaysOfWeek = function renderDaysOfWeek() { var day = new Date(displayBounds[0]); var days = []; while (days.length < 7) { days.push( /*#__PURE__*/_react["default"].createElement(_StyledCalendar.StyledDayContainer, { key: days.length, sizeProp: size }, /*#__PURE__*/_react["default"].createElement(_StyledCalendar.StyledDay, { otherMonth: true, sizeProp: size }, day.toLocaleDateString(locale, { weekday: 'narrow' })))); day = (0, _utils.addDays)(day, 1); } return /*#__PURE__*/_react["default"].createElement(_StyledCalendar.StyledWeek, null, days); }; var weeks = []; var day = new Date(displayBounds[0]); var days; var firstDayInMonth; while (day.getTime() < displayBounds[1].getTime()) { if (day.getDay() === firstDayOfWeek) { if (days) { weeks.push( /*#__PURE__*/_react["default"].createElement(_StyledCalendar.StyledWeek, { key: day.getTime() }, days)); } days = []; } var otherMonth = day.getMonth() !== reference.getMonth(); if (!showAdjacentDays && otherMonth) { days.push( /*#__PURE__*/_react["default"].createElement(_StyledCalendar.StyledDayContainer, { key: day.getTime(), sizeProp: size }, /*#__PURE__*/_react["default"].createElement(_StyledCalendar.StyledDay, { sizeProp: size }))); } else { (function () { var dateString = day.toISOString(); // this.dayRefs[dateString] = React.createRef(); var selected = false; var inRange = false; var selectedState = (0, _utils.withinDates)(day, date || dates); if (selectedState === 2) { selected = true; } else if (selectedState === 1) { inRange = true; } var dayDisabled = (0, _utils.withinDates)(day, disabled) || validBounds && !(0, _utils.betweenDates)(day, validBounds); if (!firstDayInMonth && !dayDisabled && day.getMonth() === reference.getMonth()) { firstDayInMonth = dateString; } days.push( /*#__PURE__*/_react["default"].createElement(_StyledCalendar.StyledDayContainer, { key: day.getTime(), sizeProp: size }, /*#__PURE__*/_react["default"].createElement(_Button.Button, { a11yTitle: day.toDateString(), plain: true, tabIndex: -1, active: active && active.getTime() === day.getTime(), disabled: dayDisabled, onClick: function onClick() { return selectDate(dateString); }, onMouseOver: function onMouseOver() { return setActive(new Date(dateString)); }, onMouseOut: function onMouseOut() { return setActive(undefined); }, onFocus: function onFocus() {}, onBlur: function onBlur() {} }, /*#__PURE__*/_react["default"].createElement(_StyledCalendar.StyledDay, { inRange: inRange, otherMonth: day.getMonth() !== reference.getMonth(), isSelected: selected, sizeProp: size }, day.getDate())))); })(); } day = (0, _utils.addDays)(day, 1); } weeks.push( /*#__PURE__*/_react["default"].createElement(_StyledCalendar.StyledWeek, { key: day.getTime() }, days)); return /*#__PURE__*/_react["default"].createElement(_StyledCalendar.StyledCalendar, _extends({ ref: ref, sizeProp: size }, rest), /*#__PURE__*/_react["default"].createElement(_Box.Box, null, header ? header({ date: reference, locale: locale, onPreviousMonth: function onPreviousMonth() { return changeReference(previousMonth); }, onNextMonth: function onNextMonth() { return changeReference(nextMonth); }, previousInBound: (0, _utils.betweenDates)(previousMonth, validBounds), nextInBound: (0, _utils.betweenDates)(nextMonth, validBounds) }) : renderCalendarHeader(previousMonth, nextMonth), daysOfWeek && renderDaysOfWeek(), /*#__PURE__*/_react["default"].createElement(_Keyboard.Keyboard, { onEnter: function onEnter() { return selectDate(active.toISOString()); }, onUp: function onUp(event) { event.preventDefault(); event.stopPropagation(); // so the page doesn't scroll setActive((0, _utils.addDays)(active, -7)); }, onDown: function onDown(event) { event.preventDefault(); event.stopPropagation(); // so the page doesn't scroll setActive((0, _utils.addDays)(active, 7)); }, onLeft: function onLeft() { return active && setActive((0, _utils.addDays)(active, -1)); }, onRight: function onRight() { return active && setActive((0, _utils.addDays)(active, 1)); } }, /*#__PURE__*/_react["default"].createElement(_StyledCalendar.StyledWeeksContainer, { sizeProp: size, tabIndex: 0, focus: focus, onFocus: function onFocus() { setFocus(true); if (date && (0, _utils.betweenDates)(new Date(date), displayBounds)) { setActive(new Date(date)); } else { setActive(new Date(firstDayInMonth)); } }, onBlur: function onBlur() { setFocus(false); setActive(undefined); } }, /*#__PURE__*/_react["default"].createElement(_StyledCalendar.StyledWeeks, { slide: slide, sizeProp: size }, weeks))))); }); Calendar.displayName = 'Calendar'; var CalendarDoc; if (process.env.NODE_ENV !== 'production') { // eslint-disable-next-line global-require CalendarDoc = require('./doc').doc(Calendar); } var CalendarWrapper = CalendarDoc || Calendar; exports.Calendar = CalendarWrapper;