@appannie/react-infinite-calendar
Version:
Infinite scrolling date-picker built with React, with localization, themes, keyboard support, and more.
207 lines (188 loc) • 8.25 kB
JavaScript
import _objectSpread from '@babel/runtime/helpers/objectSpread2';
import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
import _defineProperty from '@babel/runtime/helpers/defineProperty';
import React, { useMemo, useCallback, useState, useEffect } from 'react';
import VirtualList from 'react-tiny-virtual-list';
import classNames from 'classnames';
import { getMonthsForYear, chunk, isRange, emptyFn } from '../utils/index.js';
import { isWithinRange, isBefore, startOfMonth, isAfter, getMonth, format, isSameMonth, addYears, endOfMonth } from '../utils/dateFnV2.js';
import styles from './quarters.scss.js';
import { parseDate } from '../utils/parse.js';
var SPACING = 0;
var isDateDisabled = function isDateDisabled(_ref) {
var date = _ref.date,
min = _ref.min,
minDate = _ref.minDate,
max = _ref.max,
maxDate = _ref.maxDate;
return isBefore(date, startOfMonth(min)) || isBefore(date, startOfMonth(minDate)) || isAfter(date, startOfMonth(max)) || isAfter(date, startOfMonth(maxDate));
};
var getSelected = function getSelected(selected) {
if (isRange(selected)) {
return {
start: startOfMonth(selected.start),
end: endOfMonth(selected.end)
};
}
return {
start: parseDate(format(selected, 'yyyy-MM-dd')),
end: parseDate(format(selected, 'yyyy-MM-dd'))
};
};
var Quarters = function Quarters(props) {
var height = props.height,
hideOnSelect = props.hideOnSelect,
locale = props.locale,
max = props.max,
maxDate = props.maxDate,
min = props.min,
minDate = props.minDate,
scrollToDate = props.scrollToDate,
today = props.today,
setDisplay = props.setDisplay,
theme = props.theme,
handlers = props.handlers,
width = props.width,
_props$onSelect = props.onSelect,
onSelect = _props$onSelect === void 0 ? emptyFn : _props$onSelect,
_props$showQuarters = props.showQuarters,
showQuarters = _props$showQuarters === void 0 ? true : _props$showQuarters,
selected = props.selected,
years = props.years,
_props$fiscalYearStar = props.fiscalYearStart,
fiscalYearStart = _props$fiscalYearStar === void 0 ? 1 : _props$fiscalYearStar;
var _getSelected = getSelected(selected),
start = _getSelected.start,
end = _getSelected.end;
var selectedYearIndex = useMemo(function () {
var yearsSliced = years.slice(0, years.length);
return yearsSliced.indexOf(start.getFullYear());
}, [years, start]);
var handleClick = useCallback(function (date, e) {
onSelect(date, e, function (date) {
return scrollToDate(date);
});
if (hideOnSelect) {
window.requestAnimationFrame(function () {
return setDisplay('quarters');
});
}
}, [hideOnSelect, onSelect, scrollToDate, setDisplay]);
var renderMonths = useCallback(function (chunked) {
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("article", {
className: "quarter-label"
}, chunked.map(function (months, index) {
var isSelected = months.some(function (month) {
return isWithinRange(month, start, end);
});
return /*#__PURE__*/React.createElement("label", {
key: "Q".concat(index + 1),
className: classNames('label', _defineProperty({}, styles.selected, isSelected))
}, /*#__PURE__*/React.createElement("span", null, "Q".concat(index + 1)));
})), /*#__PURE__*/React.createElement("article", {
className: "quarterly-view"
}, chunked.map(function (months) {
var _classNames2;
var isDisabled = months.some(function (month) {
var disabled = isDateDisabled({
date: month,
min: min,
minDate: minDate,
max: max,
maxDate: maxDate
});
return disabled;
});
var isSelected = months.some(function (month) {
return isWithinRange(month, start, end);
});
return /*#__PURE__*/React.createElement("div", {
key: "".concat(getMonth(months[0]))
}, /*#__PURE__*/React.createElement("ol", Object.assign({
className: classNames(styles.month, (_classNames2 = {}, _defineProperty(_classNames2, styles.selected, isSelected && !isDisabled), _defineProperty(_classNames2, styles.disabled, isDisabled), _classNames2)),
onClick: function onClick(e) {
e.stopPropagation();
if (!isDisabled) {
handleClick(months[0], e);
}
}
}, handlers), months.map(function (date, index) {
return /*#__PURE__*/React.createElement("li", {
key: index,
"data-month": "".concat(format(date, 'yyyy-MM-dd')),
className: classNames(_defineProperty({}, styles.selected, isSameMonth(date, start) || isSameMonth(date, end)))
}, /*#__PURE__*/React.createElement("div", {
className: styles.selection
}, format(date, 'MMM', {
locale: locale === null || locale === void 0 ? void 0 : locale.locale
})));
})));
})));
}, [handleClick, handlers, locale, max, maxDate, min, minDate, start, end]);
var currentYear = today.getFullYear();
var yearsSliced = years.slice(0, years.length);
var rowHeight = 164;
var heights = yearsSliced.map(function (val, index) {
return index === 0 || index === yearsSliced.length - 1 ? rowHeight + SPACING : rowHeight;
});
var isYearLess = yearsSliced.length * rowHeight < height + 40;
var containerHeight = isYearLess ? yearsSliced.length * rowHeight + 2 * SPACING : height + 40; // Scroll to selected year
var _useState = useState(0),
_useState2 = _slicedToArray(_useState, 2),
scrollOffset = _useState2[0],
setScrollOffset = _useState2[1];
useEffect(function () {
if (!isYearLess && selectedYearIndex !== -1) {
var top = heights.slice(0, selectedYearIndex).reduce(function (acc, val) {
return acc + val;
}, 0);
setScrollOffset(top);
} // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
var onScroll = function onScroll(scrollTop) {
setScrollOffset(scrollTop);
};
return /*#__PURE__*/React.createElement(VirtualList, {
className: styles.list,
width: width,
height: containerHeight,
itemCount: yearsSliced.length,
estimatedItemSize: rowHeight,
itemSize: function itemSize(index) {
return heights[index];
},
scrollOffset: scrollOffset,
onScroll: onScroll,
renderItem: function renderItem(_ref2) {
var _classNames4;
var index = _ref2.index,
style = _ref2.style;
var year = yearsSliced[index];
var isActive = index === selectedYearIndex;
var months = getMonthsForYear(year, start.getDate());
var appendages = months.slice(0, fiscalYearStart - 1).map(function (date) {
return addYears(date, 1);
});
var fiscalYear = [].concat(_toConsumableArray(months.slice(fiscalYearStart - 1, months.length)), _toConsumableArray(appendages));
var chunked = chunk(fiscalYear, 4);
return /*#__PURE__*/React.createElement("div", {
key: index,
className: classNames(styles.year, (_classNames4 = {}, _defineProperty(_classNames4, styles.active, showQuarters && isActive), _defineProperty(_classNames4, styles.withQuarters, showQuarters), _defineProperty(_classNames4, styles.first, index === 0), _defineProperty(_classNames4, styles.last, index === yearsSliced.length - 1), _classNames4)),
style: _objectSpread(_objectSpread({}, style), {
color: typeof theme.selectionColor === 'function' ? theme.selectionColor(new Date(year, 0, 1)) : theme.selectionColor
}),
role: "row"
}, /*#__PURE__*/React.createElement("label", {
className: classNames('year-label', _defineProperty({}, styles.currentYear, currentYear === year))
}, /*#__PURE__*/React.createElement("span", null, year)), showQuarters && renderMonths(chunked));
}
});
};
var defaultQuartersDisplayOptions = {
showHeader: false,
showWeekdays: false,
hideYearsOnSelect: false
};
export default Quarters;
export { defaultQuartersDisplayOptions };