UNPKG

reactanggal

Version:
545 lines (478 loc) 22.7 kB
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