grommet
Version:
focus on the essential experience
494 lines (423 loc) • 19.1 kB
JavaScript
"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;