@appannie/react-infinite-calendar
Version:
Infinite scrolling date-picker built with React, with localization, themes, keyboard support, and more.
453 lines (394 loc) • 16.1 kB
JavaScript
import _classCallCheck from '@babel/runtime/helpers/classCallCheck';
import _createClass from '@babel/runtime/helpers/createClass';
import _assertThisInitialized from '@babel/runtime/helpers/assertThisInitialized';
import _inherits from '@babel/runtime/helpers/inherits';
import _createSuper from '@babel/runtime/helpers/createSuper';
import _defineProperty from '@babel/runtime/helpers/defineProperty';
import React, { Component } from 'react';
import classNames from 'classnames';
import { emptyFn, ScrollSpeed, debounce, getValidSelection, range } from '../utils/index.js';
import { defaultProps } from 'recompose';
import displayOptions from '../utils/defaultDisplayOptions.js';
import defaultLocale from '../utils/defaultLocale.js';
import defaultTheme from '../utils/defaultTheme.js';
import Today, { DIRECTION_UP, DIRECTION_DOWN } from '../Today/index.js';
import CurrentMonth from '../CurrentMonth/index.js';
import Header from '../Header/index.js';
import MonthList from '../MonthList/index.js';
import Weekdays from '../Weekdays/index.js';
import Quarters, { defaultQuartersDisplayOptions } from '../Quarters/index.js';
import Years from '../Years/index.js';
import Day from '../Day/index.js';
import { format, startOfDay } from '../utils/dateFnV2.js';
import { parseDate } from '../utils/parse.js';
import containerStyles from './Container.scss.js';
var withDefaultProps = defaultProps({
autoFocus: true,
DayComponent: Day,
display: 'days',
displayOptions: {},
HeaderComponent: Header,
height: 500,
isWeeklySelection: false,
isQuarterlySelection: false,
keyboardSupport: true,
max: new Date(2050, 11, 31),
maxDate: new Date(2050, 11, 31),
min: new Date(1980, 0, 1),
minDate: new Date(1980, 0, 1),
onHighlightedDateChange: emptyFn,
onScroll: emptyFn,
onScrollEnd: emptyFn,
onSelect: emptyFn,
passThrough: {},
rowHeight: 40,
tabIndex: 1,
width: 400,
YearsComponent: Years,
QuartersComponent: Quarters,
fiscalYearStart: 1
});
var Calendar = /*#__PURE__*/function (_Component) {
_inherits(Calendar, _Component);
var _super = _createSuper(Calendar);
function Calendar(props) {
var _this;
_classCallCheck(this, Calendar);
_this = _super.apply(this, arguments);
_defineProperty(_assertThisInitialized(_this), "_displayOptions", {});
_defineProperty(_assertThisInitialized(_this), "_locale", {});
_defineProperty(_assertThisInitialized(_this), "_theme", {});
_defineProperty(_assertThisInitialized(_this), "getCurrentOffset", function () {
return _this.scrollTop;
});
_defineProperty(_assertThisInitialized(_this), "getDateOffset", function (date) {
return _this._MonthList && _this._MonthList.current && _this._MonthList.current.getDateOffset(date);
});
_defineProperty(_assertThisInitialized(_this), "scrollTo", function (offset) {
return _this._MonthList && _this._MonthList.current && _this._MonthList.current.scrollTo(offset);
});
_defineProperty(_assertThisInitialized(_this), "scrollToDate", function () {
var date = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : new Date();
var offset = arguments.length > 1 ? arguments[1] : undefined;
var shouldAnimate = arguments.length > 2 ? arguments[2] : undefined;
var display = _this.props.display;
return _this._MonthList && _this._MonthList.current && _this._MonthList.current.scrollToDate(date, offset, shouldAnimate && display === 'days', function () {
return _this.setState({
isScrolling: false
});
});
});
_defineProperty(_assertThisInitialized(_this), "getScrollSpeed", new ScrollSpeed().getScrollSpeed);
_defineProperty(_assertThisInitialized(_this), "handleScroll", function (scrollTop, e) {
var _this$props = _this.props,
onScroll = _this$props.onScroll,
rowHeight = _this$props.rowHeight;
var isScrolling = _this.state.isScrolling;
var _this$getDisplayOptio = _this.getDisplayOptions(),
showCurrentMonth = _this$getDisplayOptio.showCurrentMonth,
showTodayHelper = _this$getDisplayOptio.showTodayHelper,
showOverlay = _this$getDisplayOptio.showOverlay;
var scrollSpeed = Math.abs(_this.getScrollSpeed(scrollTop));
_this.scrollTop = scrollTop; // We only want to display the months overlay if the user is rapidly scrolling
if (showOverlay && scrollSpeed > rowHeight && !isScrolling) {
_this.setState({
isScrolling: true
});
}
if (showCurrentMonth) {
_this.updateCurrentMonth();
}
if (showTodayHelper) {
_this.updateTodayHelperPosition(scrollSpeed);
}
onScroll(scrollTop, e);
_this.handleScrollEnd();
});
_defineProperty(_assertThisInitialized(_this), "handleScrollEnd", debounce(function () {
var onScrollEnd = _this.props.onScrollEnd;
var isScrolling = _this.state.isScrolling;
var _this$getDisplayOptio2 = _this.getDisplayOptions(),
showTodayHelper = _this$getDisplayOptio2.showTodayHelper;
if (isScrolling) {
_this.setState({
isScrolling: false
});
}
if (showTodayHelper) {
_this.updateTodayHelperPosition(0);
}
onScrollEnd(_this.scrollTop);
}, 150));
_defineProperty(_assertThisInitialized(_this), "updateCurrentMonth", function () {
_this._MonthList && _this._MonthList.current && _this.setState({
currentMonth: _this._MonthList.current.currentMonth
});
});
_defineProperty(_assertThisInitialized(_this), "updateTodayHelperPosition", function (scrollSpeed) {
var today = _this.today;
var scrollTop = _this.scrollTop;
var showToday = _this.state.showToday;
var _this$props2 = _this.props,
height = _this$props2.height,
rowHeight = _this$props2.rowHeight;
var _this$getDisplayOptio3 = _this.getDisplayOptions(),
todayHelperRowOffset = _this$getDisplayOptio3.todayHelperRowOffset;
var newState;
if (!_this._todayOffset) {
_this._todayOffset = _this.getDateOffset(today);
} // Today is above the fold
if (scrollTop >= _this._todayOffset + (height - rowHeight) / 2 + rowHeight * todayHelperRowOffset) {
if (showToday !== DIRECTION_UP) newState = DIRECTION_UP;
} // Today is below the fold
else if (scrollTop <= _this._todayOffset - height / 2 - rowHeight * (todayHelperRowOffset + 1)) {
if (showToday !== DIRECTION_DOWN) newState = DIRECTION_DOWN;
} else if (showToday && scrollSpeed <= 1) {
newState = false;
}
if (scrollTop === 0) {
newState = false;
}
if (newState != null) {
_this.setState({
showToday: newState
});
}
});
_defineProperty(_assertThisInitialized(_this), "setDisplay", function (display) {
_this.setState({
display: display
});
});
_this.updateYears(props);
_this.state = {
display: props.display
};
_this._MonthList = /*#__PURE__*/React.createRef();
return _this;
}
_createClass(Calendar, [{
key: "componentDidMount",
value: function componentDidMount() {
var autoFocus = this.props.autoFocus;
var _this$getDisplayOptio4 = this.getDisplayOptions(),
showCurrentMonth = _this$getDisplayOptio4.showCurrentMonth;
if (autoFocus) {
this.node.focus();
}
if (showCurrentMonth) {
this.updateCurrentMonth();
}
}
}, {
key: "UNSAFE_componentWillUpdate",
value: function UNSAFE_componentWillUpdate(nextProps) {
var _this$props3 = this.props,
min = _this$props3.min,
minDate = _this$props3.minDate,
max = _this$props3.max,
maxDate = _this$props3.maxDate;
if (nextProps.min !== min || nextProps.minDate !== minDate || nextProps.max !== max || nextProps.maxDate !== maxDate) {
this.updateYears(nextProps);
}
if (nextProps.display !== this.props.display) {
this.setState({
display: nextProps.display
});
}
}
}, {
key: "updateYears",
value: function updateYears() {
var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.props;
this._min = parseDate(props.min);
this._max = parseDate(props.max);
this._minDate = parseDate(props.minDate);
this._maxDate = parseDate(props.maxDate);
var min = this._min.getFullYear();
var minMonth = this._min.getMonth();
var max = this._max.getFullYear();
var maxMonth = this._max.getMonth();
var months = [];
var year, month;
for (year = min; year <= max; year++) {
for (month = 0; month < 12; month++) {
if (year === min && month < minMonth || year === max && month > maxMonth) {
continue;
}
months.push({
month: month,
year: year
});
}
}
this.months = months;
}
}, {
key: "getDisabledDates",
value: function getDisabledDates(disabledDates) {
return disabledDates && disabledDates.map(function (date) {
return format(parseDate(date), 'yyyy-MM-dd');
});
}
}, {
key: "getDisplayOptions",
value: function getDisplayOptions() {
var displayOptions$1 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.props.displayOptions;
return Object.assign(this._displayOptions, displayOptions, displayOptions$1, this.props.display === 'quarters' ? defaultQuartersDisplayOptions : {});
}
}, {
key: "getLocale",
value: function getLocale() {
return Object.assign(this._locale, defaultLocale, this.props.locale);
}
}, {
key: "getTheme",
value: function getTheme() {
return Object.assign(this._theme, defaultTheme, this.props.theme);
}
}, {
key: "render",
value: function render() {
var _this2 = this;
var _this$props4 = this.props,
className = _this$props4.className,
passThrough = _this$props4.passThrough,
DayComponent = _this$props4.DayComponent,
disabledDays = _this$props4.disabledDays,
displayDate = _this$props4.displayDate,
height = _this$props4.height,
HeaderComponent = _this$props4.HeaderComponent,
rowHeight = _this$props4.rowHeight,
scrollDate = _this$props4.scrollDate,
initialScrollDate = _this$props4.initialScrollDate,
selected = _this$props4.selected,
tabIndex = _this$props4.tabIndex,
width = _this$props4.width,
YearsComponent = _this$props4.YearsComponent,
QuartersComponent = _this$props4.QuartersComponent,
minDate = _this$props4.minDate,
maxDate = _this$props4.maxDate,
min = _this$props4.min,
max = _this$props4.max,
fiscalYearStart = _this$props4.fiscalYearStart;
var _this$getDisplayOptio5 = this.getDisplayOptions(),
hideYearsOnSelect = _this$getDisplayOptio5.hideYearsOnSelect,
layout = _this$getDisplayOptio5.layout,
overscanMonthCount = _this$getDisplayOptio5.overscanMonthCount,
shouldHeaderAnimate = _this$getDisplayOptio5.shouldHeaderAnimate,
showCurrentMonth = _this$getDisplayOptio5.showCurrentMonth,
showHeader = _this$getDisplayOptio5.showHeader,
showMonthsForYears = _this$getDisplayOptio5.showMonthsForYears,
showOverlay = _this$getDisplayOptio5.showOverlay,
showTodayHelper = _this$getDisplayOptio5.showTodayHelper,
showWeekdays = _this$getDisplayOptio5.showWeekdays;
var _this$state = this.state,
display = _this$state.display,
isScrolling = _this$state.isScrolling,
showToday = _this$state.showToday,
currentMonth = _this$state.currentMonth;
var disabledDates = this.getDisabledDates(this.props.disabledDates);
var locale = this.getLocale();
var theme = this.getTheme();
var today = this.today = startOfDay(new Date());
var validSelection = getValidSelection(selected, minDate || min, maxDate || max);
return /*#__PURE__*/React.createElement("div", Object.assign({
tabIndex: tabIndex,
className: classNames(className, containerStyles.root, _defineProperty({}, containerStyles.landscape, layout === 'landscape')),
style: {
color: theme.textColor["default"],
width: width
},
"aria-label": "Calendar",
ref: function ref(node) {
_this2.node = node;
}
}, passThrough.rootNode), showHeader && /*#__PURE__*/React.createElement(HeaderComponent, Object.assign({
selected: validSelection,
shouldAnimate: Boolean(shouldHeaderAnimate && display !== 'years'),
layout: layout,
theme: theme,
locale: locale,
scrollToDate: this.scrollToDate,
setDisplay: this.setDisplay,
dateFormat: locale.headerFormat,
display: display,
displayDate: displayDate
}, passThrough.Header)), /*#__PURE__*/React.createElement("div", {
className: containerStyles.wrapper
}, showWeekdays && /*#__PURE__*/React.createElement(Weekdays, {
weekdays: locale.weekdays,
weekStartsOn: locale.weekStartsOn,
theme: theme
}), /*#__PURE__*/React.createElement("div", {
className: containerStyles.listWrapper
}, showCurrentMonth && /*#__PURE__*/React.createElement(CurrentMonth, {
currentMonth: currentMonth,
theme: theme
}), showTodayHelper && /*#__PURE__*/React.createElement(Today, {
scrollToDate: this.scrollToDate,
show: showToday,
today: today,
theme: theme,
todayLabel: locale.todayLabel["long"]
}), display !== 'quarters' && /*#__PURE__*/React.createElement(MonthList, {
ref: this._MonthList,
DayComponent: DayComponent,
disabledDates: disabledDates,
disabledDays: disabledDays,
height: height,
isScrolling: isScrolling,
locale: locale,
maxDate: this._maxDate,
min: this._min,
minDate: this._minDate,
months: this.months,
onScroll: this.handleScroll,
overscanMonthCount: overscanMonthCount,
passThrough: passThrough,
theme: theme,
today: today,
rowHeight: rowHeight,
selected: validSelection,
scrollDate: scrollDate,
showOverlay: showOverlay,
width: width,
initialScrollDate: initialScrollDate
})), display === 'quarters' && /*#__PURE__*/React.createElement(QuartersComponent, Object.assign({
height: height,
locale: locale,
max: this._max,
maxDate: this._maxDate,
min: this._min,
minDate: this._minDate,
scrollToDate: this.scrollToDate,
selected: validSelection,
setDisplay: this.setDisplay,
theme: theme,
today: today,
width: width,
years: range(this._min.getFullYear(), this._max.getFullYear() + 1),
fiscalYearStart: fiscalYearStart
}, passThrough.Quarters)), display === 'years' && /*#__PURE__*/React.createElement(YearsComponent, Object.assign({
height: height,
hideOnSelect: hideYearsOnSelect,
locale: locale,
max: this._max,
maxDate: this._maxDate,
min: this._min,
minDate: this._minDate,
scrollToDate: this.scrollToDate,
selected: validSelection,
setDisplay: this.setDisplay,
showMonths: showMonthsForYears,
theme: theme,
today: today,
width: width,
years: range(this._min.getFullYear(), this._max.getFullYear() + 1)
}, passThrough.Years))));
}
}]);
return Calendar;
}(Component);
export default Calendar;
export { withDefaultProps };