reactanggal
Version:
A date picker component for React
545 lines (478 loc) • 22.7 kB
JavaScript
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var React = require('react');
var React__default = _interopDefault(React);
var dateFns = require('date-fns');
var pakaiClass = _interopDefault(require('pakai-class'));
var esm = require('date-fns/esm');
var ReactanggalContext = React.createContext({
setPreSelectionYear: function setPreSelectionYear() {},
setPreSelection: function setPreSelection() {},
setStep: function setStep() {},
setCurrentSelected: function setCurrentSelected() {},
preSelection: new Date(),
preSelectionYear: new Date(),
minDate: null,
maxDate: null,
showOutsideMonth: false,
forceFocus: false,
setForceFocus: function setForceFocus() {}
});
var myGetYearStart = function myGetYearStart(year, totalYears) {
var getPreSelectionYear = dateFns.getYear(year);
var currentYearStart = +getPreSelectionYear % +totalYears;
currentYearStart = +getPreSelectionYear - (currentYearStart !== 0 ? currentYearStart : totalYears) + 1;
return currentYearStart;
};
var CalendarHeader = function CalendarHeader() {
var _useContext = React.useContext(ReactanggalContext),
setStep = _useContext.setStep,
step = _useContext.step,
_useContext$setPreSel = _useContext.setPreSelection,
setPreSelection = _useContext$setPreSel === void 0 ? function () {} : _useContext$setPreSel,
preSelection = _useContext.preSelection,
setPreSelectionYear = _useContext.setPreSelectionYear,
preSelectionYear = _useContext.preSelectionYear,
minDate = _useContext.minDate,
maxDate = _useContext.maxDate,
setForceFocus = _useContext.setForceFocus;
var handleStep = function handleStep() {
if (step === 0) {
setPreSelectionYear(preSelection);
setStep(1);
setForceFocus(true);
} else setStep(0);
};
var handleNext = function handleNext() {
if (handleDisabledNext) return;
if (step === 0) setPreSelection(dateFns.addMonths(preSelection, 1));else if (step === 1) setPreSelectionYear(dateFns.addYears(preSelectionYear, 24));else if (step === 2) setPreSelectionYear(dateFns.addYears(preSelectionYear, 1));
};
var handlePrevious = function handlePrevious() {
if (handleDisabledPrevious) return;
if (step === 0) setPreSelection(dateFns.subMonths(preSelection, 1));else if (step === 1) setPreSelectionYear(dateFns.subYears(preSelectionYear, 24));else if (step === 2) setPreSelectionYear(dateFns.subYears(preSelectionYear, 1));
};
var renderStepView = React.useMemo(function () {
if (step === 0) return dateFns.format(preSelection, 'MMMM yyyy');
if (step === 1) {
var currentYearStart = myGetYearStart(preSelectionYear, 24);
return currentYearStart + " - " + (+currentYearStart + 23);
}
if (step === 2) return dateFns.getYear(preSelectionYear);
return;
}, [step, preSelection, preSelectionYear]);
var handleDisabledPrevious = React.useMemo(function () {
if (step === 0 && minDate) return dateFns.isBefore(dateFns.endOfMonth(dateFns.subMonths(preSelection, 1)), minDate);
if (step === 1) {
var firstYearPreSelectionYear = myGetYearStart(preSelectionYear, 24);
if (minDate) return firstYearPreSelectionYear - 1 < dateFns.getYear(minDate);
return !Boolean(firstYearPreSelectionYear - 1);
}
if (step === 2) {
if (minDate) return dateFns.isBefore(dateFns.endOfYear(dateFns.subYears(preSelectionYear, 1)), minDate);
return !Boolean(dateFns.getYear(preSelectionYear) - 1);
}
return;
}, [preSelectionYear, step, preSelection, minDate]);
var handleDisabledNext = React.useMemo(function () {
if (!maxDate) return false;
if (step === 0) return dateFns.isAfter(dateFns.startOfMonth(dateFns.addMonths(preSelection, 1)), maxDate);
if (step === 1) return myGetYearStart(preSelectionYear, 24) + 24 > dateFns.getYear(maxDate);
if (step === 2) return dateFns.isAfter(dateFns.startOfYear(dateFns.addYears(preSelectionYear, 1)), maxDate);
return;
}, [step, preSelection, maxDate, preSelectionYear]);
return React__default.createElement("div", {
className: "reactanggal__header"
}, React__default.createElement("button", {
className: "reactanggal__button reactanggal__header-period-control-button",
onClick: handleStep
}, renderStepView), React__default.createElement("div", null, React__default.createElement("button", {
disabled: handleDisabledPrevious,
className: "reactanggal__button reactanggal__header-arrow-button reactanggal__header-arrow-button--previous",
onClick: handlePrevious
}), React__default.createElement("button", {
disabled: handleDisabledNext,
className: "reactanggal__button reactanggal__header-arrow-button reactanggal__header-arrow-button--next",
onClick: handleNext
})));
};
var monthsArr = [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]];
var firstMonth = dateFns.startOfYear(new Date());
var Months = function Months() {
return React__default.createElement("div", null, monthsArr.map(function (el, i) {
return React__default.createElement("div", {
key: "monthColumn-" + i,
className: "reactanggal__calendar-months"
}, el.map(function (num) {
return React__default.createElement(Month, {
key: "month-" + num,
month: num
});
}));
}));
};
var Month = function Month(_ref) {
var month = _ref.month;
var monthRef = React.useRef(null);
var _useContext = React.useContext(ReactanggalContext),
currentSelected = _useContext.currentSelected,
setPreSelectionYear = _useContext.setPreSelectionYear,
preSelectionYear = _useContext.preSelectionYear,
_useContext$setPreSel = _useContext.setPreSelection,
setPreSelection = _useContext$setPreSel === void 0 ? function () {} : _useContext$setPreSel,
setStep = _useContext.setStep,
minDate = _useContext.minDate,
maxDate = _useContext.maxDate,
forceFocus = _useContext.forceFocus,
setForceFocus = _useContext.setForceFocus;
var isFocusable = React.useMemo(function () {
return month === dateFns.getMonth(preSelectionYear);
}, [month, preSelectionYear]);
var isDisabled = React.useMemo(function () {
if (!minDate && !maxDate) return;
return minDate && month < dateFns.getMonth(minDate) && dateFns.isSameYear(preSelectionYear, minDate) || maxDate && month > dateFns.getMonth(maxDate) && dateFns.isSameYear(preSelectionYear, maxDate);
}, [minDate, maxDate, month, preSelectionYear]);
var handleClick = React.useCallback(function () {
if (isDisabled) return;
setPreSelection(dateFns.setMonth(preSelectionYear, month));
setStep(0);
setForceFocus(true);
}, [isDisabled, setPreSelection, preSelectionYear, setStep, setForceFocus]);
React.useEffect(function () {
if (forceFocus && isFocusable) {
var _monthRef$current;
setForceFocus(false);
(_monthRef$current = monthRef.current) === null || _monthRef$current === void 0 ? void 0 : _monthRef$current.focus();
}
}, [forceFocus, isFocusable, setForceFocus]);
var handleKeyDown = React.useCallback(function (e) {
if (!isFocusable) return;
var key = e.key;
if (key !== ' ' && key !== 'Enter' && key !== 'ArrowUp' && key !== 'ArrowDown' && key !== 'ArrowRight' && key !== 'ArrowLeft') return;
if (key === ' ' || key === 'Enter') {
handleClick();
return;
}
var newPreSelection;
var disabledMove;
if (key === 'ArrowUp' || key === 'ArrowLeft') {
newPreSelection = dateFns.subMonths(preSelectionYear, key === 'ArrowUp' ? 4 : 1);
disabledMove = minDate && dateFns.isAfter(dateFns.startOfMonth(minDate), newPreSelection);
} else if (key === 'ArrowDown' || key === 'ArrowRight') {
newPreSelection = dateFns.addMonths(preSelectionYear, key === 'ArrowDown' ? 4 : 1);
disabledMove = maxDate && dateFns.isAfter(newPreSelection, dateFns.endOfMonth(maxDate));
}
if (!disabledMove && newPreSelection) {
setPreSelectionYear(newPreSelection);
setForceFocus(true);
}
}, [isFocusable, handleClick, minDate, maxDate, preSelectionYear, setPreSelectionYear, setForceFocus]);
return React__default.createElement("div", {
ref: monthRef,
onKeyDown: handleKeyDown,
tabIndex: isFocusable ? 0 : -1,
className: pakaiClass("reactanggal__button reactanggal__calendar-month", +month === +dateFns.getMonth(new Date()) && dateFns.isSameYear(preSelectionYear, new Date()) && 'reactanggal__calendar-month--today', currentSelected && +month === +dateFns.getMonth(currentSelected) && dateFns.isSameYear(preSelectionYear, currentSelected) && 'reactanggal__calendar-month--selected', isDisabled && 'reactanggal__calendar-month--disabled'),
onClick: handleClick
}, dateFns.format(dateFns.addMonths(firstMonth, month), 'MMMM'));
};
var Day = function Day(_ref) {
var day = _ref.day;
var dayRef = React.useRef(null);
var _useContext = React.useContext(ReactanggalContext),
currentSelected = _useContext.currentSelected,
setCurrentSelected = _useContext.setCurrentSelected,
_useContext$setPreSel = _useContext.setPreSelection,
setPreSelection = _useContext$setPreSel === void 0 ? function () {} : _useContext$setPreSel,
preSelection = _useContext.preSelection,
showOutsideMonth = _useContext.showOutsideMonth,
minDate = _useContext.minDate,
maxDate = _useContext.maxDate,
forceFocus = _useContext.forceFocus,
setForceFocus = _useContext.setForceFocus;
var isOutsideMonth = React.useMemo(function () {
return !dateFns.isSameMonth(day, preSelection);
}, [day, preSelection]);
var isFocusable = React.useMemo(function () {
return dateFns.isSameDay(day, preSelection);
}, [day, preSelection]);
var isDisabled = React.useMemo(function () {
if (!minDate && !maxDate) return;
return minDate && dateFns.isBefore(day, minDate) || maxDate && dateFns.isAfter(day, maxDate);
}, [minDate, day, maxDate]);
var handleClick = function handleClick() {
if (isDisabled) return;
if (isOutsideMonth && !showOutsideMonth) return;
if (!(typeof setCurrentSelected === 'function')) return;
setCurrentSelected(day);
setPreSelection(day);
setForceFocus(true);
};
React.useEffect(function () {
if (forceFocus && isFocusable) {
var _dayRef$current;
setForceFocus(false);
(_dayRef$current = dayRef.current) === null || _dayRef$current === void 0 ? void 0 : _dayRef$current.focus();
}
}, [forceFocus, isFocusable, setForceFocus]);
var handleKeyDown = React.useCallback(function (e) {
if (!isFocusable) return;
var key = e.key;
if (key !== ' ' && key !== 'Enter' && key !== 'ArrowUp' && key !== 'ArrowDown' && key !== 'ArrowRight' && key !== 'ArrowLeft') return;
if (key === ' ' || key === 'Enter') {
setCurrentSelected(preSelection);
return;
}
var newPreSelection;
var disabledMove;
if (key === 'ArrowUp' || key === 'ArrowLeft') {
newPreSelection = key === 'ArrowUp' ? dateFns.subWeeks(preSelection, 1) : dateFns.subDays(preSelection, 1);
disabledMove = minDate && dateFns.isBefore(newPreSelection, minDate);
} else if (key === 'ArrowDown' || key === 'ArrowRight') {
newPreSelection = key === 'ArrowDown' ? dateFns.addWeeks(preSelection, 1) : esm.addDays(preSelection, 1);
disabledMove = maxDate && dateFns.isAfter(newPreSelection, maxDate);
}
if (!disabledMove && newPreSelection) {
setPreSelection(newPreSelection);
setForceFocus(true);
}
}, [setCurrentSelected, preSelection, setPreSelection, setForceFocus, isFocusable, minDate, maxDate]);
return React__default.createElement("div", {
onKeyDown: handleKeyDown,
ref: dayRef,
tabIndex: isFocusable ? 0 : -1,
className: pakaiClass('reactanggal__calendar-day reactanggal__calendar-day-num reactanggal__button', dateFns.isSameDay(day, new Date()) && 'reactanggal__calendar-day-num--today', currentSelected && dateFns.isSameDay(day, currentSelected) && 'reactanggal__calendar-day-num--selected', isOutsideMonth && "reactanggal__calendar-day-num--" + (showOutsideMonth ? 'outside' : 'hidden'), isDisabled && 'reactanggal__calendar-day-num--disabled'),
onClick: handleClick
}, dateFns.getDate(day));
};
var Week = function Week(_ref) {
var weekStart = _ref.weekStart,
week = _ref.week;
var renderDays = React.useMemo(function () {
return [0, 1, 2, 3, 4, 5, 6].map(function (el) {
return React__default.createElement(Day, {
key: "week-" + week + "-day-" + el,
day: dateFns.addDays(weekStart, el)
});
});
}, [weekStart, week]);
return React__default.createElement("div", {
className: "reactanggal__calendar-week"
}, renderDays);
};
var Years = function Years(_ref) {
var yearStart = _ref.yearStart,
yearColumn = _ref.yearColumn;
var renderYears = React.useMemo(function () {
var years = [];
for (var i = 0; i < 4; i++) {
years.push(React__default.createElement(Year, {
key: "yearColumn-" + yearColumn + "-year-" + i,
year: +yearStart + i
}));
}
return years;
}, [yearColumn, yearStart]);
return React__default.createElement("div", {
className: "reactanggal__calendar-years"
}, renderYears);
};
var Year = function Year(_ref2) {
var year = _ref2.year;
var yearRef = React.useRef(null);
var _useContext = React.useContext(ReactanggalContext),
currentSelected = _useContext.currentSelected,
preSelection = _useContext.preSelection,
preSelectionYear = _useContext.preSelectionYear,
_useContext$setPreSel = _useContext.setPreSelection,
setPreSelection = _useContext$setPreSel === void 0 ? function () {} : _useContext$setPreSel,
setPreSelectionYear = _useContext.setPreSelectionYear,
setStep = _useContext.setStep,
minDate = _useContext.minDate,
maxDate = _useContext.maxDate,
forceFocus = _useContext.forceFocus,
setForceFocus = _useContext.setForceFocus;
var isFocusable = React.useMemo(function () {
return +year === +dateFns.getYear(preSelectionYear);
}, [year, preSelectionYear]);
var isDisabled = React.useMemo(function () {
if (!minDate && !maxDate) return;
return minDate && year < dateFns.getYear(minDate) || maxDate && year > dateFns.getYear(maxDate);
}, [minDate, maxDate, year]);
var handleClick = React.useCallback(function () {
if (isDisabled) return;
var newValue = dateFns.setYear(preSelection, year);
if (minDate) {
if (dateFns.isBefore(newValue, minDate)) {
newValue = dateFns.setMonth(newValue, dateFns.getMonth(minDate));
if (dateFns.isBefore(newValue, minDate)) newValue = minDate;
}
} else if (maxDate) {
if (dateFns.isAfter(newValue, maxDate)) {
newValue = dateFns.setMonth(newValue, dateFns.getMonth(maxDate));
if (dateFns.isAfter(newValue, maxDate)) newValue = maxDate;
}
}
setPreSelectionYear(newValue);
setPreSelection(newValue);
setStep(2);
setForceFocus(true);
}, [isDisabled, setPreSelectionYear, preSelection, year, setPreSelection, setStep, setForceFocus, maxDate, minDate]);
React.useEffect(function () {
if (forceFocus && isFocusable) {
var _yearRef$current;
setForceFocus(false);
(_yearRef$current = yearRef.current) === null || _yearRef$current === void 0 ? void 0 : _yearRef$current.focus();
}
}, [forceFocus, isFocusable, setForceFocus]);
var handleKeyDown = React.useCallback(function (e) {
if (!isFocusable) return;
var key = e.key;
if (key !== ' ' && key !== 'Enter' && key !== 'ArrowUp' && key !== 'ArrowDown' && key !== 'ArrowRight' && key !== 'ArrowLeft') return;
if (key === ' ' || key === 'Enter') {
handleClick();
return;
}
var newPreSelection;
var disabledMove;
if (key === 'ArrowUp' || key === 'ArrowLeft') {
newPreSelection = dateFns.subYears(preSelectionYear, key === 'ArrowUp' ? 4 : 1);
var getYearNewPre = dateFns.getYear(newPreSelection);
disabledMove = minDate && getYearNewPre < dateFns.getYear(minDate) || getYearNewPre < 1;
} else if (key === 'ArrowDown' || key === 'ArrowRight') {
newPreSelection = esm.addYears(preSelectionYear, key === 'ArrowDown' ? 4 : 1);
disabledMove = maxDate && dateFns.getYear(newPreSelection) > dateFns.getYear(maxDate);
}
if (!disabledMove && newPreSelection) {
setPreSelectionYear(newPreSelection);
setForceFocus(true);
}
}, [isFocusable, handleClick, minDate, maxDate, preSelectionYear, setPreSelectionYear, setForceFocus]);
return React__default.createElement("div", {
ref: yearRef,
onKeyDown: handleKeyDown,
tabIndex: isFocusable ? 0 : -1,
className: pakaiClass("reactanggal__button reactanggal__calendar-year", +year === +dateFns.getYear(new Date()) && 'reactanggal__calendar-year--today', currentSelected && +year === +dateFns.getYear(currentSelected) && 'reactanggal__calendar-year--selected', isDisabled && 'reactanggal__calendar-year--disabled'),
onClick: handleClick
}, year);
};
var CalendarBody = function CalendarBody() {
var _useContext = React.useContext(ReactanggalContext),
step = _useContext.step,
preSelection = _useContext.preSelection,
preSelectionYear = _useContext.preSelectionYear;
var renderDayNames = React.useMemo(function () {
var weekStart = dateFns.startOfWeek(new Date());
return [0, 1, 2, 3, 4, 5, 6].map(function (el) {
return React__default.createElement("div", {
key: "dayname-" + el,
className: "reactanggal__calendar-day reactanggal__calendar-day-name"
}, dateFns.format(dateFns.addDays(weekStart, el), 'EEEEEE'));
});
}, []);
var renderWeeks = React.useMemo(function () {
if (step !== 0) return;
var weeks = [];
var currentWeekStart = dateFns.startOfWeek(dateFns.startOfMonth(preSelection));
for (var i = 0; i < dateFns.getWeeksInMonth(preSelection); i++) {
weeks.push(React__default.createElement(Week, {
key: "week-" + i,
weekStart: currentWeekStart,
week: i
}));
currentWeekStart = dateFns.addWeeks(currentWeekStart, 1);
}
return weeks;
}, [preSelection, step]);
var renderYearsColumn = React.useMemo(function () {
if (step !== 1) return;
var yearsColumn = [];
var currentYearStart = myGetYearStart(preSelectionYear, 24);
for (var i = 0; i < 6; i++) {
yearsColumn.push(React__default.createElement(Years, {
key: "yearColumn-" + i,
yearStart: currentYearStart,
yearColumn: i
}));
currentYearStart = currentYearStart + 4;
}
return yearsColumn;
}, [step, preSelectionYear]);
var renderMonths = React.useMemo(function () {
if (step !== 2) return;
return React__default.createElement(Months, null);
}, [step]);
var renderCalender = React.useMemo(function () {
if (step === 0) return renderWeeks;else if (step === 1) return renderYearsColumn;else if (step === 2) return renderMonths;
return;
}, [step, renderMonths, renderWeeks, renderYearsColumn]);
return React__default.createElement("div", null, step === 0 && React__default.createElement("div", {
className: "reactanggal__calendar-weeks-header"
}, renderDayNames), React__default.createElement("div", {
className: "reactanggal__calendar-body"
}, renderCalender));
};
var Reactanggal = function Reactanggal(_ref) {
var selected = _ref.selected,
_ref$onChange = _ref.onChange,
onChange = _ref$onChange === void 0 ? function () {} : _ref$onChange,
minDate = _ref.minDate,
maxDate = _ref.maxDate,
_ref$showOutsideMonth = _ref.showOutsideMonth,
showOutsideMonth = _ref$showOutsideMonth === void 0 ? false : _ref$showOutsideMonth;
var _useState = React.useState(0),
step = _useState[0],
setStep = _useState[1];
var _useState2 = React.useState(selected || minDate || maxDate || new Date()),
preSelection = _useState2[0],
setPreSelection = _useState2[1];
var _useState3 = React.useState(new Date()),
preSelectionYear = _useState3[0],
setPreSelectionYear = _useState3[1];
var _useState4 = React.useState(true),
forceFocus = _useState4[0],
setForceFocus = _useState4[1];
return React__default.createElement(ReactanggalContext.Provider, {
value: {
currentSelected: selected,
setCurrentSelected: onChange,
step: step,
setStep: setStep,
preSelection: preSelection,
setPreSelection: setPreSelection,
preSelectionYear: preSelectionYear,
setPreSelectionYear: setPreSelectionYear,
minDate: minDate ? dateFns.startOfDay(minDate) : minDate,
maxDate: maxDate ? dateFns.endOfDay(maxDate) : maxDate,
showOutsideMonth: showOutsideMonth,
forceFocus: forceFocus,
setForceFocus: setForceFocus
}
}, React__default.createElement(ReactanggalRoot, null));
};
var ReactanggalRoot = function ReactanggalRoot() {
var calendarRef = React.useRef(null);
React.useEffect(function () {
var handleKeyDown = function handleKeyDown(e) {
var _calendarRef$current;
if (e.key != 'Tab') return;
var focusableElements = (_calendarRef$current = calendarRef.current) === null || _calendarRef$current === void 0 ? void 0 : _calendarRef$current.querySelectorAll('button:not([disabled]), [tabindex="0"]');
if (e.target === (focusableElements === null || focusableElements === void 0 ? void 0 : focusableElements[focusableElements.length - 1]) && !e.shiftKey) {
var _focusableElements$;
e.preventDefault();
(_focusableElements$ = focusableElements[0]) === null || _focusableElements$ === void 0 ? void 0 : _focusableElements$.focus();
} else if (e.target === (focusableElements === null || focusableElements === void 0 ? void 0 : focusableElements[0]) && e.shiftKey) {
var _focusableElements;
e.preventDefault();
(_focusableElements = focusableElements[focusableElements.length - 1]) === null || _focusableElements === void 0 ? void 0 : _focusableElements.focus();
}
};
window.addEventListener("keydown", handleKeyDown);
return function () {
return window.removeEventListener("keydown", handleKeyDown);
};
}, []);
return React__default.createElement("div", {
ref: calendarRef,
className: "reactanggal__calendar"
}, React__default.createElement(CalendarHeader, null), React__default.createElement(CalendarBody, null));
};
module.exports = Reactanggal;
//# sourceMappingURL=index.js.map