grommet
Version:
focus on the essential experience
478 lines (408 loc) • 16.3 kB
JavaScript
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; }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
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); }
import React, { Component } from 'react';
import { compose } from 'recompose';
import { withTheme } from 'styled-components';
import { defaultProps } from '../../default-props';
import { Box } from '../Box';
import { Button } from '../Button';
import { Heading } from '../Heading';
import { Keyboard } from '../Keyboard';
import { StyledCalendar, StyledDay, StyledDayContainer, StyledWeek, StyledWeeks, StyledWeeksContainer } from './StyledCalendar';
import { addDays, addMonths, betweenDates, daysApart, endOfMonth, startOfMonth, subtractDays, subtractMonths, withinDates, updateDateRange } from './utils';
var headingPadMap = {
small: 'xsmall',
medium: 'small',
large: 'medium'
};
var buildStartEnd = function buildStartEnd(reference, firstDayOfWeek) {
var start = new Date(reference);
start.setDate(1); // first of month
start = subtractDays(start, start.getDay() - firstDayOfWeek); // beginning of week
var end = addDays(start, 7 * 5 + 7); // 5 weeks to end of week
return {
start: start,
end: end
};
};
var buildState = function buildState(props) {
var date = props.date,
dates = props.dates,
firstDayOfWeek = props.firstDayOfWeek,
reference = props.reference;
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();
}
} else {
normalizedReference = new Date();
}
return _extends({}, buildStartEnd(normalizedReference, firstDayOfWeek), {
reference: normalizedReference
});
};
var Calendar =
/*#__PURE__*/
function (_Component) {
_inheritsLoose(Calendar, _Component);
function Calendar() {
var _this;
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = _Component.call.apply(_Component, [this].concat(args)) || this;
_defineProperty(_assertThisInitialized(_this), "state", {});
_defineProperty(_assertThisInitialized(_this), "dayRefs", {});
_defineProperty(_assertThisInitialized(_this), "clearSlideStateLater", function () {
clearTimeout(_this.timer);
_this.timer = setTimeout(function () {
var targetStartEnd = _this.state.targetStartEnd;
if (targetStartEnd) {
_this.setState({
start: targetStartEnd.start,
end: targetStartEnd.end,
targetStartEnd: undefined,
slide: undefined
});
} // Wait for animation to finish before cleaning up. Empirically determined.
}, 800);
});
_defineProperty(_assertThisInitialized(_this), "setReference", function (reference) {
var _this$props = _this.props,
animate = _this$props.animate,
bounds = _this$props.bounds,
firstDayOfWeek = _this$props.firstDayOfWeek,
onReference = _this$props.onReference;
var _this$state = _this.state,
start = _this$state.start,
end = _this$state.end,
targetStartEnd = _this$state.targetStartEnd;
if (betweenDates(reference, bounds)) {
var nextStartEnd = buildStartEnd(reference, firstDayOfWeek);
var nextState = {
reference: reference
}; // if we're changing too fast, bypass animation
if (!animate || targetStartEnd) {
nextState.targetStartEnd = nextStartEnd;
nextState.start = nextStartEnd.start;
nextState.end = nextStartEnd.end;
nextState.targetStartEnd = undefined;
nextState.slide = undefined;
} else {
nextState.targetStartEnd = nextStartEnd;
if (nextStartEnd.start.getTime() < start.getTime()) {
nextState.start = nextStartEnd.start;
nextState.slide = {
direction: 'down',
weeks: daysApart(start, nextStartEnd.start) / 7
};
} else if (nextStartEnd.end.getTime() > end.getTime()) {
nextState.end = nextStartEnd.end;
nextState.slide = {
direction: 'up',
weeks: daysApart(nextStartEnd.end, end) / 7
};
}
}
_this.clearSlideStateLater();
_this.setState(nextState, function () {
if (onReference) {
onReference(reference.toISOString());
}
});
}
});
_defineProperty(_assertThisInitialized(_this), "onFocus", function (day) {
return function () {
var bounds = _this.props.bounds;
var reference = _this.state.reference;
if (betweenDates(day, bounds)) {
_this.setState({
focused: day
}, function () {
if (day.getMonth() !== reference.getMonth()) {
_this.setReference(day);
}
});
}
};
});
_defineProperty(_assertThisInitialized(_this), "onClickDay", function (dateString) {
return function () {
var _this$props2 = _this.props,
onSelect = _this$props2.onSelect,
range = _this$props2.range;
if (range) {
var nextState = updateDateRange(dateString, _this.state);
_this.setState(nextState);
if (onSelect) {
onSelect(nextState.dates || nextState.date || undefined);
}
} else if (onSelect) {
onSelect(dateString);
}
};
});
_defineProperty(_assertThisInitialized(_this), "setFocus", function (day) {
var ref = _this.dayRefs[day.toISOString()];
if (ref && ref.current) {
ref.current.focus();
}
});
_defineProperty(_assertThisInitialized(_this), "renderCalendarHeader", function (previousMonth, nextMonth) {
var _this$props3 = _this.props,
bounds = _this$props3.bounds,
locale = _this$props3.locale,
size = _this$props3.size,
theme = _this$props3.theme;
var reference = _this.state.reference;
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 React.createElement(Box, {
direction: "row",
justify: "between",
align: "center"
}, React.createElement(Box, {
flex: true,
pad: {
horizontal: headingPadMap[size] || 'small'
}
}, React.createElement(Heading, {
level: size === 'small' ? 4 : 3,
size: size,
margin: "none"
}, reference.toLocaleDateString(locale, {
month: 'long',
year: 'numeric'
}))), React.createElement(Box, {
flex: false,
direction: "row",
align: "center"
}, React.createElement(Button, {
a11yTitle: previousMonth.toLocaleDateString(locale, {
month: 'long',
year: 'numeric'
}),
icon: React.createElement(PreviousIcon, {
size: size !== 'small' ? size : undefined
}),
disabled: !betweenDates(previousMonth, bounds),
onClick: function onClick() {
return _this.setReference(previousMonth);
}
}), React.createElement(Button, {
a11yTitle: nextMonth.toLocaleDateString(locale, {
month: 'long',
year: 'numeric'
}),
icon: React.createElement(NextIcon, {
size: size !== 'small' ? size : undefined
}),
disabled: !betweenDates(nextMonth, bounds),
onClick: function onClick() {
return _this.setReference(nextMonth);
}
})));
});
_defineProperty(_assertThisInitialized(_this), "renderDaysOfWeek", function (locale, size, start) {
var day = new Date(start);
var days = [];
while (days.length < 7) {
days.push(React.createElement(StyledDayContainer, {
key: days.length,
sizeProp: size
}, React.createElement(StyledDay, {
otherMonth: true,
sizeProp: size
}, day.toLocaleDateString(locale, {
weekday: 'narrow'
}))));
day = addDays(day, 1);
}
return React.createElement(StyledWeek, null, days);
});
return _this;
}
Calendar.getDerivedStateFromProps = function getDerivedStateFromProps(nextProps, prevState) {
var reference = nextProps.reference;
var prevReference = prevState.reference;
if (Object.prototype.hasOwnProperty.call(nextProps, 'date') || Object.prototype.hasOwnProperty.call(nextProps, 'dates') || !prevReference || reference) {
var state = {};
if (Object.prototype.hasOwnProperty.call(nextProps, 'date') || Object.prototype.hasOwnProperty.call(nextProps, 'dates')) {
state.date = nextProps.date;
state.dates = nextProps.dates;
}
if (!prevReference || reference) {
state = _extends({}, state, buildState(nextProps));
}
return state;
}
return null;
};
var _proto = Calendar.prototype;
_proto.componentDidUpdate = function componentDidUpdate() {
var focused = this.state.focused;
if (focused) {
var ref = this.dayRefs[focused.toISOString()];
if (ref && ref.current && ref.current !== document.activeElement) {
ref.current.focus();
}
}
};
_proto.componentWillUnmount = function componentWillUnmount() {
clearTimeout(this.timer);
};
_proto.render = function render() {
var _this2 = this;
var _this$props4 = this.props,
bounds = _this$props4.bounds,
dateProp = _this$props4.date,
datesProp = _this$props4.dates,
disabled = _this$props4.disabled,
daysOfWeek = _this$props4.daysOfWeek,
firstDayOfWeek = _this$props4.firstDayOfWeek,
header = _this$props4.header,
locale = _this$props4.locale,
onReference = _this$props4.onReference,
onSelect = _this$props4.onSelect,
range = _this$props4.range,
showAdjacentDays = _this$props4.showAdjacentDays,
size = _this$props4.size,
theme = _this$props4.theme,
rest = _objectWithoutPropertiesLoose(_this$props4, ["bounds", "date", "dates", "disabled", "daysOfWeek", "firstDayOfWeek", "header", "locale", "onReference", "onSelect", "range", "showAdjacentDays", "size", "theme"]);
var _this$state2 = this.state,
date = _this$state2.date,
dates = _this$state2.dates,
focused = _this$state2.focused,
start = _this$state2.start,
reference = _this$state2.reference,
end = _this$state2.end,
slide = _this$state2.slide; // 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 = endOfMonth(subtractMonths(startOfMonth(reference), 1));
var nextMonth = startOfMonth(addMonths(startOfMonth(reference), 1));
var weeks = [];
var day = new Date(start);
var days;
this.dayRefs = {};
while (day.getTime() < end.getTime()) {
if (day.getDay() === firstDayOfWeek) {
if (days) {
weeks.push(React.createElement(StyledWeek, {
key: day.getTime()
}, days));
}
days = [];
}
var otherMonth = day.getMonth() !== reference.getMonth();
if (!showAdjacentDays && otherMonth) {
days.push(React.createElement(StyledDayContainer, {
key: day.getTime(),
sizeProp: size
}, React.createElement(StyledDay, {
sizeProp: size
})));
} else {
var dateString = day.toISOString();
this.dayRefs[dateString] = React.createRef();
var selected = false;
var inRange = false;
var selectedState = withinDates(day, date || dates);
if (selectedState === 2) {
selected = true;
} else if (selectedState === 1) {
inRange = true;
}
var dayDisabled = withinDates(day, disabled) || bounds && !betweenDates(day, bounds);
days.push(React.createElement(StyledDayContainer, {
key: day.getTime(),
sizeProp: size
}, React.createElement(Button, {
ref: this.dayRefs[dateString],
a11yTitle: day.toDateString(),
plain: true,
hoverIndicator: !dayDisabled,
disabled: dayDisabled,
onClick: this.onClickDay(dateString),
onFocus: this.onFocus(day),
onBlur: function onBlur() {
return _this2.setState({
focused: false
});
}
}, React.createElement(StyledDay, {
inRange: inRange,
otherMonth: day.getMonth() !== reference.getMonth(),
isSelected: selected,
sizeProp: size
}, day.getDate()))));
}
day = addDays(day, 1);
}
weeks.push(React.createElement(StyledWeek, {
key: day.getTime()
}, days));
return React.createElement(StyledCalendar, _extends({
sizeProp: size
}, rest), React.createElement(Keyboard, {
onUp: function onUp(event) {
event.preventDefault();
_this2.setFocus(addDays(focused, -7));
},
onDown: function onDown(event) {
event.preventDefault();
_this2.setFocus(addDays(focused, 7));
},
onLeft: function onLeft() {
return focused && _this2.setFocus(addDays(focused, -1));
},
onRight: function onRight() {
return focused && _this2.setFocus(addDays(focused, 1));
}
}, React.createElement(Box, null, header ? header({
date: reference,
locale: locale,
onPreviousMonth: function onPreviousMonth() {
return _this2.setReference(previousMonth);
},
onNextMonth: function onNextMonth() {
return _this2.setReference(nextMonth);
},
previousInBound: betweenDates(previousMonth, bounds),
nextInBound: betweenDates(nextMonth, bounds)
}) : this.renderCalendarHeader(previousMonth, nextMonth), daysOfWeek && this.renderDaysOfWeek(locale, size, start), React.createElement(StyledWeeksContainer, {
sizeProp: size
}, React.createElement(StyledWeeks, {
slide: slide,
sizeProp: size
}, weeks)))));
};
return Calendar;
}(Component);
_defineProperty(Calendar, "defaultProps", {
animate: true,
firstDayOfWeek: 0,
size: 'medium',
locale: 'en-US',
showAdjacentDays: true
});
Object.setPrototypeOf(Calendar.defaultProps, defaultProps);
var CalendarDoc;
if (process.env.NODE_ENV !== 'production') {
CalendarDoc = require('./doc').doc(Calendar); // eslint-disable-line global-require
}
var CalendarWrapper = compose(withTheme)(CalendarDoc || Calendar);
export { CalendarWrapper as Calendar };