UNPKG

@dnb/eufemia

Version:

DNB Eufemia Design System UI Library

419 lines (418 loc) 14.2 kB
"use client"; import _extends from "@babel/runtime/helpers/esm/extends"; import _defineProperty from "@babel/runtime/helpers/esm/defineProperty"; function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } import "core-js/modules/es.string.replace.js"; import "core-js/modules/web.dom-collections.iterator.js"; import React, { useCallback, useContext, useEffect, useMemo, useRef } from 'react'; import classnames from 'classnames'; import { format, addMonths, addWeeks, addDays, isSameDay, isSameMonth, startOfDay, differenceInCalendarDays, differenceInMonths, lastDayOfMonth, setDate } from 'date-fns'; import { isDisabled, makeDayObject, toRange, getWeek, dayOffset, getCalendar } from './DatePickerCalc'; import Button from '../button/Button'; import DatePickerContext from './DatePickerContext'; import { DatePickerCalendarNav } from './DatePickerCalendarNavigator'; import { formatDate } from '../date-format/DateFormatUtils'; const defaultProps = { hideNavigation: false, hideDays: false, onlyMonth: false, hideNextMonthWeek: false, noAutoFocus: false, rtl: false, resetDate: true }; const arrowKeys = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']; const keysToHandle = ['Enter', 'Space', ...arrowKeys]; function DatePickerCalendar(restOfProps) { const props = _objectSpread(_objectSpread({}, defaultProps), restOfProps); const { updateDates, setHasClickedCalendarDay, startDate, endDate, maxDate, minDate, startMonth, endMonth, hoverDate, setHoverDate, setSubmittedDates, props: { onDaysRender, yearNavigation }, translation: { DatePicker: { firstDay: defaultFirstDayOfWeek, selectedMonth } } } = useContext(DatePickerContext); const { id, nr, rtl, month, isRange, firstDayOfWeek = defaultFirstDayOfWeek, hideNavigation, locale, hideDays, onSelect, onKeyDown, resetDate, noAutoFocus, hideNextMonthWeek, onlyMonth } = props; const tableRef = useRef(); const days = useRef({}); const cache = useRef({}); useEffect(() => { if (!noAutoFocus && nr === 0) { if (tableRef.current) { tableRef.current.focus({ preventScroll: true }); } } }, [noAutoFocus, nr]); useEffect(() => { setSubmittedDates({ startDate, endDate }); }, []); const onMouseLeaveHandler = useCallback(() => { setHoverDate(undefined); }, [setHoverDate]); const callOnSelect = useCallback(event => { onSelect === null || onSelect === void 0 ? void 0 : onSelect(event); }, [onSelect]); const getDays = useCallback(month => { let daysFromCalendar = getCalendar(month || new Date(), dayOffset(firstDayOfWeek), { onlyMonth, hideNextMonthWeek }).map(date => makeDayObject(date, { startDate, endDate, hoverDate, minDate, maxDate, month })); if (onDaysRender) { const changedDays = onDaysRender(daysFromCalendar, nr); if (Array.isArray(changedDays)) { daysFromCalendar = changedDays; } } days.current[format(month, 'yyyy-MM')] = daysFromCalendar; return daysFromCalendar; }, [endDate, firstDayOfWeek, hideNextMonthWeek, hoverDate, maxDate, minDate, nr, onDaysRender, onlyMonth, startDate]); const keyNavCalc = useCallback((date, keyCode) => { if (!arrowKeys.includes(keyCode)) { return date; } const dateHandler = /(ArrowLeft|ArrowRight)/g.test(keyCode) ? addDays : addWeeks; const shiftAmount = /(ArrowLeft|ArrowUp)/g.test(keyCode) ? -1 : 1; return dateHandler(date, shiftAmount); }, []); const findValid = useCallback((date, keyCode) => { if (!onDaysRender || !days.current) { return date; } const month = format(date, 'yyyy-MM'); if (!days.current[month]) { getDays(date); } if (Array.isArray(days.current[month])) { const foundDate = days.current[month].find(cur => isSameDay(cur.date, date)); if (foundDate !== null && foundDate !== void 0 && foundDate.date && (foundDate.isDisabled || foundDate.isSelectable === false || foundDate.isInactive)) { const nextDate = keyNavCalc(foundDate.date, keyCode); return findValid(nextDate, keyCode); } if (foundDate !== null && foundDate !== void 0 && foundDate.date) { return foundDate.date; } } return date; }, [onDaysRender, getDays, keyNavCalc]); const hasReachedEnd = useCallback(date => isDisabled(date, minDate, maxDate), [minDate, maxDate]); const onKeyDownHandler = useCallback(event => { const pressedKey = event.code; if (typeof onKeyDown === 'function') { return onKeyDown(event, tableRef, nr); } if (!keysToHandle.includes(pressedKey)) { return; } event.preventDefault(); event.persist(); const currentDates = { startDate, endDate, startMonth, endMonth }; const dateType = !isRange || nr === 0 ? 'start' : 'end'; const currentDate = currentDates[`${dateType}Date`]; let newDate = currentDate ? keyNavCalc(currentDate, pressedKey) : currentDates[`${dateType}Month`] || (isRange && nr === 1 ? addMonths(new Date(), 1) : new Date()); if (newDate === currentDate && (pressedKey === 'Enter' || pressedKey === 'Space')) { return callOnSelect({ event, nr, hidePicker: true }); } const dates = {}; const currentMonth = currentDates[`${dateType}Month`]; if (currentMonth && !currentDate || currentMonth && Math.abs(differenceInMonths(newDate, currentMonth)) > 1) { newDate = !isRange ? currentMonth : nr === 0 ? setDate(currentMonth, 1) : lastDayOfMonth(currentMonth); } else if (currentMonth && !isSameMonth(currentDate, currentMonth)) { dates[`${dateType}Month`] = newDate; } newDate = findValid(newDate, pressedKey); if (hasReachedEnd(newDate)) { return; } dates[`${dateType}Date`] = newDate; if (!isRange) { dates.endDate = newDate; } else { if (!startDate) { dates.startDate = newDate; } if (!endDate) { dates.endDate = newDate; } } if (onlyMonth || hideNavigation) { if (!isSameMonth(dates.startDate, startDate) || !isSameMonth(dates.endDate, startDate)) { return; } } updateDates(dates, () => { callOnSelect(_objectSpread({ event, nr, hidePicker: false }, dates)); }); if (tableRef && tableRef.current) { tableRef.current.focus({ preventScroll: true }); } }, [callOnSelect, findValid, hasReachedEnd, onKeyDown, startDate, endDate, updateDates, hideNavigation, isRange, keyNavCalc, nr, onlyMonth, endMonth, startMonth]); const cacheKey = 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 = useMemo(() => { if (cache.current[cacheKey]) { return cache.current[cacheKey]; } let count = 0; const days = getDays(month).reduce((acc, cur, i) => { acc[count] = acc[count] || []; acc[count].push(cur); if (i % 7 === 6) { count++; } return acc; }, {}); cache.current[cacheKey] = Object.values(days); return cache.current[cacheKey]; }, [cacheKey, getDays, month]); return React.createElement("div", { className: 'dnb-date-picker__calendar' + (rtl ? " rtl" : ""), lang: locale }, !hideNavigation && !onlyMonth && React.createElement("div", { className: "dnb-date-picker__header" }, React.createElement(DatePickerCalendarNav, { type: yearNavigation ? 'month' : 'both', id: id, nr: nr, date: month, locale: locale }), yearNavigation && React.createElement(DatePickerCalendarNav, { type: "year", id: id, nr: nr, date: month, locale: locale })), onlyMonth && React.createElement("div", { className: "dnb-date-picker__header dnb-date-picker__header--only-month-label" }, React.createElement("label", { id: `${id}--title`, className: "dnb-date-picker__header__title dnb-no-focus", title: selectedMonth.replace(/%s/, formatDate(month, { locale, options: { month: 'long' } })), tabIndex: -1 }, formatDate(month, { locale, options: { month: 'long' } }))), React.createElement("table", { role: "grid", className: "dnb-no-focus", tabIndex: 0, "aria-labelledby": `${id}--title`, onKeyDown: onKeyDownHandler, onMouseLeave: onMouseLeaveHandler, ref: tableRef }, !hideDays && !onlyMonth && React.createElement("thead", { "aria-hidden": true }, React.createElement("tr", { role: "row", className: "dnb-date-picker__labels" }, getWeek(dayOffset(firstDayOfWeek)).map((day, i) => React.createElement("th", { key: i, role: "columnheader", scope: "col", className: "dnb-date-picker__labels__day", "aria-label": formatDate(day, { locale, options: { weekday: 'long' } }) }, formatDate(day, { locale, options: { weekday: 'short' } }).substring(0, 2))))), React.createElement("tbody", null, weekDays.map((week, i) => { return React.createElement("tr", { key: 'week' + i, role: "row", className: "dnb-date-picker__days" }, week.map((day, i) => { const title = 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 = _objectSpread(_objectSpread({ tabIndex: -1 }, dateType && { id: `${id}--button-${dateType}` }), isSelectedDate && { ['aria-selected']: true }); const paramsButton = _objectSpread({}, isSelectedDate && { ['aria-current']: 'date' }); return React.createElement("td", _extends({ key: 'day' + i, role: "gridcell", className: classnames("dnb-date-picker__day dnb-no-focus", buildDayClassNames(day)) }, paramsCell), React.createElement(Button, _extends({ size: "medium", variant: "secondary", text: day.date.getDate(), bounding: true, disabled: handleAsDisabled, tabIndex: handleAsDisabled ? 0 : -1, "aria-disabled": handleAsDisabled, "aria-label": title }, paramsButton, { on_click: handleAsDisabled ? undefined : _ref => { let { event } = _ref; return onSelectRange({ day, isRange, startDate, endDate, resetDate, event, setHasClickedCalendarDay, onSelect: state => { updateDates(state, dates => callOnSelect(_objectSpread(_objectSpread({}, dates), {}, { event, nr, hidePicker: !isRange }))); } }); }, onMouseOver: handleAsDisabled ? undefined : () => onHoverDay({ day, hoverDate, setHoverDate }), onFocus: handleAsDisabled ? undefined : () => onHoverDay({ day, hoverDate, setHoverDate }) }))); })); })))); } export default DatePickerCalendar; function onSelectRange(_ref2) { let { day, isRange, startDate, endDate, onSelect, resetDate, event, setHasClickedCalendarDay } = _ref2; event.persist(); if (!isRange) { return onSelect({ startDate: startOfDay(day.date), endDate: startOfDay(day.date), event }); } setHasClickedCalendarDay(true); if (!startDate || resetDate && startDate && endDate) { return onSelect({ startDate: startOfDay(day.date), endDate: undefined, event }); } const daysToStartDate = Math.abs(differenceInCalendarDays(startDate, day.date)); const daysToEndDate = Math.abs(differenceInCalendarDays(endDate, day.date)); const range = toRange(endDate && !resetDate && daysToStartDate < daysToEndDate ? endDate : startDate, day.date); return onSelect({ startDate: startOfDay(range.startDate), endDate: startOfDay(range.endDate), event }); } function onHoverDay(_ref3) { let { day, hoverDate, setHoverDate } = _ref3; if (!isSameDay(day.date, hoverDate)) { setHoverDate === null || setHoverDate === void 0 ? void 0 : setHoverDate(day.date); } } function buildDayClassNames(day) { return classnames(day.className, day.isStartDate && 'dnb-date-picker__day--start-date', day.isEndDate && 'dnb-date-picker__day--end-date', day.isPreview && 'dnb-date-picker__day--preview', day.isWithinSelection && 'dnb-date-picker__day--within-selection', day.isSelectable && 'dnb-date-picker__day--selectable', day.isInactive && 'dnb-date-picker__day--inactive', day.isDisabled && 'dnb-date-picker__day--disabled', day.isToday && 'dnb-date-picker__day--today'); } //# sourceMappingURL=DatePickerCalendar.js.map