UNPKG

@dnb/eufemia

Version:

DNB Eufemia Design System UI Library

560 lines (559 loc) 19.5 kB
"use strict"; "use client"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _includes = _interopRequireDefault(require("core-js-pure/stable/instance/includes.js")); var _push = _interopRequireDefault(require("core-js-pure/stable/instance/push.js")); var _react = require("react"); var _clsx = require("clsx"); var _useMountEffect = _interopRequireDefault(require("../../shared/helpers/useMountEffect.js")); var _dateFns = require("date-fns"); var _DatePickerCalc = require("./DatePickerCalc.js"); var _Button = _interopRequireDefault(require("../button/Button.js")); var _DatePickerContext = _interopRequireDefault(require("./DatePickerContext.js")); var _useIsomorphicLayoutEffect = _interopRequireDefault(require("../../shared/helpers/useIsomorphicLayoutEffect.js")); var _DatePickerCalendarNavigator = require("./DatePickerCalendarNavigator.js"); var _DateFormatUtils = require("../date-format/DateFormatUtils.js"); var _jsxRuntime = require("react/jsx-runtime"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } const datePickerCalendarDefaultProps = { hideNavigation: false, hideDays: false, onlyMonth: false, hideMonthLabel: false, hideNextMonthWeek: false, rtl: false, resetDate: true }; const arrowKeys = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']; const keysToHandle = ['Enter', 'Space', ...arrowKeys]; function DatePickerCalendar(restOfProps) { const props = { ...datePickerCalendarDefaultProps, ...restOfProps }; const { updateDates, setHasClickedCalendarDay, startDate, endDate, maxDate, minDate, startMonth, endMonth, hoverDate, setHoverDate, setSubmittedDates, views, setViews, props: { onDaysRender, yearNavigation }, translation: { DatePicker: { firstDay: defaultFirstDayOfWeek, selectedMonth } } } = (0, _react.useContext)(_DatePickerContext.default); const { id, nr, rtl, month, isRange, firstDayOfWeek = defaultFirstDayOfWeek, hideNavigation, locale, hideDays, onSelect, onKeyDown, resetDate, hideNextMonthWeek, onlyMonth, hideMonthLabel } = props; const tableRef = (0, _react.useRef)(null); const days = (0, _react.useRef)({}); const cache = (0, _react.useRef)({}); const focusedDateRef = (0, _react.useRef)(null); const pendingFocusDateRef = (0, _react.useRef)(null); const currentDatesRef = (0, _react.useRef)({ startDate, endDate, startMonth, endMonth }); (0, _useMountEffect.default)(() => { setSubmittedDates({ startDate, endDate }); }); (0, _react.useEffect)(() => { currentDatesRef.current = { startDate, endDate, startMonth, endMonth }; }, [endDate, endMonth, startDate, startMonth]); const onMouseLeaveHandler = (0, _react.useCallback)(() => { setHoverDate(undefined); }, [setHoverDate]); const focusDayButton = (0, _react.useCallback)(date => { if (tableRef.current) { const dateStr = (0, _dateFns.format)(date, 'yyyy-MM-dd'); const button = tableRef.current.querySelector(`td[data-date="${dateStr}"] button`); button?.focus({ preventScroll: true }); } }, []); const focusEndCalendar = (0, _react.useCallback)(() => { const viewsContainer = tableRef.current?.closest('.dnb-date-picker__views'); const endTable = viewsContainer?.querySelectorAll('table')?.[1]; const endDateValue = currentDatesRef.current.endDate; const endButton = endDateValue ? endTable?.querySelector(`td[data-date="${(0, _dateFns.format)(endDateValue, 'yyyy-MM-dd')}"] button`) : null; if (endButton) { endButton.focus({ preventScroll: true }); } else { ; endTable?.focus({ preventScroll: true }); } }, []); (0, _useIsomorphicLayoutEffect.default)(() => { if (pendingFocusDateRef.current) { focusDayButton(pendingFocusDateRef.current); pendingFocusDateRef.current = null; } }, [views, focusDayButton]); const callOnSelect = (0, _react.useCallback)(event => { onSelect?.(event); }, [onSelect]); const getDays = (0, _react.useCallback)(month => { let daysFromCalendar = (0, _DatePickerCalc.getCalendar)(month || new Date(), (0, _DatePickerCalc.dayOffset)(firstDayOfWeek), { onlyMonth, hideNextMonthWeek }).map(date => (0, _DatePickerCalc.makeDayObject)(date, { startDate, endDate, hoverDate, minDate, maxDate, month })); if (onDaysRender) { const changedDays = onDaysRender(daysFromCalendar, nr); if (Array.isArray(changedDays)) { daysFromCalendar = changedDays; } } days.current[(0, _dateFns.format)(month, 'yyyy-MM')] = daysFromCalendar; return daysFromCalendar; }, [endDate, firstDayOfWeek, hideNextMonthWeek, hoverDate, maxDate, minDate, nr, onDaysRender, onlyMonth, startDate]); const keyNavCalc = (0, _react.useCallback)((date, keyCode) => { if (!(0, _includes.default)(arrowKeys).call(arrowKeys, keyCode)) { return date; } const dateHandler = /(ArrowLeft|ArrowRight)/g.test(keyCode) ? _dateFns.addDays : _dateFns.addWeeks; const shiftAmount = /(ArrowLeft|ArrowUp)/g.test(keyCode) ? -1 : 1; return dateHandler(date, shiftAmount); }, []); const findValid = (0, _react.useCallback)((date, keyCode) => { if (!onDaysRender || !days.current) { return date; } const month = (0, _dateFns.format)(date, 'yyyy-MM'); if (!days.current[month]) { getDays(date); } if (Array.isArray(days.current[month])) { const foundDate = days.current[month].find(cur => (0, _dateFns.isSameDay)(cur.date, date)); if (foundDate?.date && (foundDate.isDisabled || foundDate.isSelectable === false || foundDate.isInactive)) { const nextDate = keyNavCalc(foundDate.date, keyCode); return findValid(nextDate, keyCode); } if (foundDate?.date) { return foundDate.date; } } return date; }, [onDaysRender, getDays, keyNavCalc]); const hasReachedEnd = (0, _react.useCallback)(date => (0, _DatePickerCalc.isDisabled)(date, minDate, maxDate), [minDate, maxDate]); const onKeyDownHandler = (0, _react.useCallback)(event => { const pressedKey = event.code; if (typeof onKeyDown === 'function') { return onKeyDown(event, tableRef, nr); } if (!(0, _includes.default)(keysToHandle).call(keysToHandle, pressedKey)) { return; } if (tableRef.current) { const td = document.activeElement?.closest('td[data-date]'); if (td && tableRef.current.contains(td)) { const dateStr = td.getAttribute('data-date'); if (dateStr) { focusedDateRef.current = (0, _dateFns.startOfDay)(new Date(dateStr)); } } } if (pressedKey === 'Enter' || pressedKey === 'Space') { event.preventDefault(); if (focusedDateRef.current) { const focusedDate = focusedDateRef.current; if (!isRange) { focusedDateRef.current = null; updateDates({ startDate: (0, _dateFns.startOfDay)(focusedDate), endDate: (0, _dateFns.startOfDay)(focusedDate) }, allDates => { callOnSelect({ ...allDates, event, nr, hidePicker: true }); }); return; } setHasClickedCalendarDay(true); const { startDate: currentStart, endDate: currentEnd } = currentDatesRef.current; if (!currentStart || resetDate && currentStart && currentEnd) { updateDates({ startDate: (0, _dateFns.startOfDay)(focusedDate), endDate: undefined }, allDates => { callOnSelect({ ...allDates, event, nr, hidePicker: false }); }); } else { focusedDateRef.current = null; const range = (0, _DatePickerCalc.toRange)(currentStart, focusedDate); updateDates({ startDate: (0, _dateFns.startOfDay)(range.startDate), endDate: (0, _dateFns.startOfDay)(range.endDate) }, allDates => { callOnSelect({ ...allDates, event, nr, hidePicker: true }); }); } return; } if (isRange && nr === 0) { focusEndCalendar(); } else { callOnSelect({ event, nr, hidePicker: true }); } return; } event.preventDefault(); const currentDates = currentDatesRef.current; const dateType = !isRange || nr === 0 ? 'start' : 'end'; const currentFocused = focusedDateRef.current || currentDates[`${dateType}Date`]; const viewMonth = views.find(v => v.nr === nr)?.month; let newDate = currentFocused ? keyNavCalc(currentFocused, pressedKey) : viewMonth || (isRange && nr === 1 ? (0, _dateFns.addMonths)(new Date(), 1) : new Date()); if (viewMonth && !currentFocused || viewMonth && currentFocused && Math.abs((0, _dateFns.differenceInMonths)(newDate, viewMonth)) > 1) { newDate = !isRange ? viewMonth : (0, _dateFns.setDate)(viewMonth, 1); } newDate = findValid(newDate, pressedKey); if (hasReachedEnd(newDate)) { return; } if (onlyMonth || hideNavigation) { if (viewMonth && !(0, _dateFns.isSameMonth)(newDate, viewMonth)) { return; } } focusedDateRef.current = newDate; if (viewMonth && !(0, _dateFns.isSameMonth)(newDate, viewMonth)) { if (isRange) { const otherNr = nr === 0 ? 1 : 0; const otherViewMonth = views.find(v => v.nr === otherNr)?.month; if (otherViewMonth && (0, _dateFns.isSameMonth)(newDate, otherViewMonth)) { const viewsContainer = tableRef.current?.closest('.dnb-date-picker__views'); const otherTable = viewsContainer?.querySelectorAll('table')?.[otherNr]; const dateStr = (0, _dateFns.format)(newDate, 'yyyy-MM-dd'); const button = otherTable?.querySelector(`td[data-date="${dateStr}"] button`); if (button) { focusedDateRef.current = null; button.focus({ preventScroll: true }); return; } } } tableRef.current?.focus({ preventScroll: true }); const updatedViews = views.map(view => { if (view.nr === nr) { return { ...view, month: newDate }; } return view; }); setViews(updatedViews); pendingFocusDateRef.current = newDate; } else { focusDayButton(newDate); } }, [callOnSelect, findValid, focusDayButton, focusEndCalendar, hasReachedEnd, onKeyDown, updateDates, hideNavigation, isRange, keyNavCalc, nr, onlyMonth, resetDate, setHasClickedCalendarDay, views, setViews]); const cacheKey = (0, _react.useMemo)(() => { return [nr, month, firstDayOfWeek, onlyMonth, hideNextMonthWeek, startDate, endDate, hoverDate, maxDate, minDate].join('|'); }, [nr, month, firstDayOfWeek, onlyMonth, hideNextMonthWeek, startDate, endDate, hoverDate, maxDate, minDate]); const weekDays = (0, _react.useMemo)(() => { if (cache.current[cacheKey]) { return cache.current[cacheKey]; } let count = 0; const days = getDays(month).reduce((acc, cur, i) => { var _context; acc[count] = acc[count] || []; (0, _push.default)(_context = acc[count]).call(_context, cur); if (i % 7 === 6) { count++; } return acc; }, {}); cache.current[cacheKey] = Object.values(days); return cache.current[cacheKey]; }, [cacheKey, getDays, month]); return (0, _jsxRuntime.jsxs)("div", { className: (0, _clsx.clsx)('dnb-date-picker__calendar', rtl && 'rtl'), lang: locale, children: [!hideNavigation && !onlyMonth && (0, _jsxRuntime.jsxs)("div", { className: "dnb-date-picker__header", children: [(0, _jsxRuntime.jsx)(_DatePickerCalendarNavigator.DatePickerCalendarNav, { type: yearNavigation ? 'month' : 'both', id: id, nr: nr, date: month, locale: locale }), yearNavigation && (0, _jsxRuntime.jsx)(_DatePickerCalendarNavigator.DatePickerCalendarNav, { type: "year", id: id, nr: nr, date: month, locale: locale })] }), onlyMonth && !hideMonthLabel && (0, _jsxRuntime.jsx)("div", { className: "dnb-date-picker__header dnb-date-picker__header--only-month-label", children: (0, _jsxRuntime.jsx)("label", { id: `${id}--title`, className: "dnb-date-picker__header__title dnb-no-focus", title: selectedMonth.replace(/%s/, (0, _DateFormatUtils.formatDate)(month, { locale, options: { month: 'long' } })), tabIndex: -1, children: (0, _DateFormatUtils.formatDate)(month, { locale, options: { month: 'long' } }) }) }), (0, _jsxRuntime.jsxs)("table", { role: "grid", className: "dnb-no-focus", tabIndex: 0, "aria-labelledby": onlyMonth && hideMonthLabel ? undefined : `${id}--title`, onKeyDown: onKeyDownHandler, onMouseLeave: onMouseLeaveHandler, ref: tableRef, children: [!hideDays && !onlyMonth && (0, _jsxRuntime.jsx)("thead", { "aria-hidden": true, children: (0, _jsxRuntime.jsx)("tr", { role: "row", className: "dnb-date-picker__labels", children: (0, _DatePickerCalc.getWeek)((0, _DatePickerCalc.dayOffset)(firstDayOfWeek)).map(day => (0, _jsxRuntime.jsx)("th", { role: "columnheader", scope: "col", className: "dnb-date-picker__labels__day", "aria-label": (0, _DateFormatUtils.formatDate)(day, { locale, options: { weekday: 'long' } }), children: (0, _DateFormatUtils.formatDate)(day, { locale, options: { weekday: 'short' } }).substring(0, 2) }, day.toISOString())) }) }), (0, _jsxRuntime.jsx)("tbody", { children: weekDays.map((week, i) => { return (0, _jsxRuntime.jsx)("tr", { role: "row", className: "dnb-date-picker__days", children: week.map((day, i) => { const title = (0, _DateFormatUtils.formatDate)(day.date, { locale, options: { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' } }); const handleAsDisabled = day.isLastMonth || day.isNextMonth || day.isDisabled || day.isInactive; const dateType = day.isStartDate ? 'start' : day.isEndDate ? 'end' : undefined; const isSelectedDate = nr === 0 ? day.isStartDate : day.isEndDate; const paramsCell = { tabIndex: -1, ...(dateType && { id: `${id}--button-${dateType}` }), ...(isSelectedDate && { ['aria-selected']: true }) }; const paramsButton = { ...(isSelectedDate && { ['aria-current']: 'date' }) }; return (0, _jsxRuntime.jsx)("td", { role: "gridcell", "data-date": (0, _dateFns.format)(day.date, 'yyyy-MM-dd'), className: (0, _clsx.clsx)('dnb-date-picker__day', 'dnb-no-focus', buildDayClassNames(day)), ...paramsCell, children: (0, _jsxRuntime.jsx)(_Button.default, { size: "medium", variant: "secondary", text: day.date.getDate(), bounding: true, disabled: handleAsDisabled, tabIndex: handleAsDisabled ? 0 : -1, "aria-disabled": handleAsDisabled, "aria-label": title, ...paramsButton, onClick: handleAsDisabled ? undefined : ({ event }) => onSelectRange({ day, isRange, startDate, endDate, resetDate, event, setHasClickedCalendarDay, onSelect: state => { updateDates(state, dates => callOnSelect({ ...dates, event, nr, hidePicker: !isRange })); } }), onMouseOver: handleAsDisabled ? undefined : () => onHoverDay({ day, hoverDate, setHoverDate }), onFocus: handleAsDisabled ? undefined : () => onHoverDay({ day, hoverDate, setHoverDate }) }) }, 'day' + i); }) }, 'week' + i); }) })] })] }); } var _default = exports.default = DatePickerCalendar; function onSelectRange({ day, isRange, startDate, endDate, onSelect, resetDate, event, setHasClickedCalendarDay }) { if (!isRange) { return onSelect({ startDate: (0, _dateFns.startOfDay)(day.date), endDate: (0, _dateFns.startOfDay)(day.date), event }); } setHasClickedCalendarDay(true); if (!startDate || resetDate && startDate && endDate) { return onSelect({ startDate: (0, _dateFns.startOfDay)(day.date), endDate: undefined, event }); } const daysToStartDate = Math.abs((0, _dateFns.differenceInCalendarDays)(startDate, day.date)); const daysToEndDate = Math.abs((0, _dateFns.differenceInCalendarDays)(endDate, day.date)); const range = (0, _DatePickerCalc.toRange)(endDate && !resetDate && daysToStartDate < daysToEndDate ? endDate : startDate, day.date); return onSelect({ startDate: (0, _dateFns.startOfDay)(range.startDate), endDate: (0, _dateFns.startOfDay)(range.endDate), event }); } function onHoverDay({ day, hoverDate, setHoverDate }) { if (day.isStartDate || day.isEndDate) { return; } if (!(0, _dateFns.isSameDay)(day.date, hoverDate)) { setHoverDate?.(day.date); } } function buildDayClassNames(day) { return (0, _clsx.clsx)({ 'dnb-date-picker__day--start-date': day.isStartDate, 'dnb-date-picker__day--end-date': day.isEndDate, 'dnb-date-picker__day--preview': day.isPreview, 'dnb-date-picker__day--within-selection': day.isWithinSelection, 'dnb-date-picker__day--selectable': day.isSelectable, 'dnb-date-picker__day--inactive': day.isInactive, 'dnb-date-picker__day--disabled': day.isDisabled, 'dnb-date-picker__day--today': day.isToday }, day.className); } //# sourceMappingURL=DatePickerCalendar.js.map