@fremtind/jkl-datepicker-react
Version:
Jøkul react datepicker component
506 lines (505 loc) • 19 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __objRest = (source, exclude) => {
var target = {};
for (var prop in source)
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
target[prop] = source[prop];
if (source != null && __getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(source)) {
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
target[prop] = source[prop];
}
return target;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var Calendar_exports = {};
__export(Calendar_exports, {
Calendar: () => Calendar
});
module.exports = __toCommonJS(Calendar_exports);
var import_jkl_button_react = require("@fremtind/jkl-button-react");
var import_jkl_icons_react = require("@fremtind/jkl-icons-react");
var import_jkl_react_hooks = require("@fremtind/jkl-react-hooks");
var import_react = __toESM(require("react"));
var import_react_dom = require("react-dom");
var import_calendarReducer = require("./calendarReducer");
var import_useCalendar = require("./useCalendar");
var import_utils = require("./utils");
const defaultMonths = [
"Januar",
"Februar",
"Mars",
"April",
"Mai",
"Juni",
"Juli",
"August",
"September",
"Oktober",
"November",
"Desember"
];
const defaultDays = ["man", "tir", "ons", "tor", "fre", "l\xF8r", "s\xF8n"];
const Calendar = (0, import_react.forwardRef)(
(props, ref) => {
const _a = props, {
date,
defaultSelected,
density,
minDate,
maxDate,
days = defaultDays,
months = defaultMonths,
monthLabel = "Velg m\xE5ned",
yearLabel = "Velg \xE5r",
yearsToShow = import_utils.DEFAULT_YEARS_TO_SHOW,
onTabOutside
} = _a, rest = __objRest(_a, [
"date",
"defaultSelected",
"density",
"minDate",
"maxDate",
"days",
"months",
"monthLabel",
"yearLabel",
"yearsToShow",
"onTabOutside"
]);
const id = (0, import_jkl_react_hooks.useId)("jkl-calendar");
const [{ offset, selectedDate, shownDate }, dispatch] = (0, import_react.useReducer)(
import_calendarReducer.calendarReducer,
(0, import_utils.getInitialDateShown)(date, defaultSelected, minDate, maxDate),
import_calendarReducer.calendarInitializer
);
const shownMonth = shownDate.getMonth();
const shownYear = shownDate.getFullYear();
(0, import_react.useEffect)(() => {
dispatch({
type: "SET_SELECTED_DATE",
newDate: (0, import_utils.getInitialDateShown)(
date,
defaultSelected,
minDate,
maxDate
)
});
}, [date, defaultSelected, minDate, maxDate]);
const onOffsetChanged = (0, import_react.useCallback)((newOffset) => {
dispatch({
type: "SET_OFFSET",
newOffset
});
}, []);
const {
calendars,
getBackProps,
getDateProps,
getForwardProps,
handleOffsetChanged
} = (0, import_useCalendar.useCalendar)(__spreadValues({
date: selectedDate,
selected: selectedDate,
minDate,
maxDate,
offset,
onOffsetChanged,
firstDayOfWeek: 1
}, rest));
const calendarPaddingRef = (0, import_react.useRef)(null);
const doFocusChange = (0, import_react.useCallback)(
(offsetDiff) => {
if (!calendarPaddingRef.current) {
return;
}
const e = document.activeElement;
const buttons = calendarPaddingRef.current.querySelectorAll(
'button.jkl-calendar-date-button:not([data-adjacent="true"]'
);
const changeFocusTo = async (nextButton) => {
e == null ? void 0 : e.setAttribute("tabindex", "-1");
nextButton.setAttribute("tabindex", "0");
nextButton.focus();
};
buttons.forEach((el, i) => {
const newNodeKey = i + offsetDiff;
if (el == e) {
if (newNodeKey <= buttons.length - 1 && newNodeKey >= 0) {
changeFocusTo(buttons[newNodeKey]);
} else if (offsetDiff < 0) {
if ((0, import_utils.isBackDisabled)({ calendars, minDate })) {
return;
}
(0, import_react_dom.flushSync)(() => {
handleOffsetChanged(
offset - (0, import_utils.subtractMonth)({
calendars,
offset: 1,
minDate
})
);
});
if (!calendarPaddingRef.current) {
return;
}
const newButtons = calendarPaddingRef.current.querySelectorAll(
'button.jkl-calendar-date-button:not([data-adjacent="true"]'
);
if (newButtons[newButtons.length + newNodeKey]) {
newButtons[0].setAttribute("tabindex", "-1");
changeFocusTo(
newButtons[newButtons.length + newNodeKey]
);
}
} else {
if ((0, import_utils.isForwardDisabled)({ calendars, maxDate })) {
return;
}
(0, import_react_dom.flushSync)(() => {
handleOffsetChanged(
offset + (0, import_utils.addMonth)({
calendars,
offset: 1,
maxDate
})
);
});
if (!calendarPaddingRef.current) {
return;
}
const newButtons = calendarPaddingRef.current.querySelectorAll(
'button.jkl-calendar-date-button:not([data-adjacent="true"]'
);
if (newButtons[newNodeKey - buttons.length]) {
newButtons[0].setAttribute("tabindex", "-1");
changeFocusTo(
newButtons[newNodeKey - buttons.length]
);
}
}
}
});
},
[
handleOffsetChanged,
calendarPaddingRef,
offset,
calendars,
maxDate,
minDate
]
);
const handleArrowNavigation = (0, import_react.useCallback)(
(event) => {
switch (event.key) {
case "ArrowUp":
doFocusChange(-7);
event.preventDefault();
break;
case "ArrowRight":
doFocusChange(1);
event.preventDefault();
break;
case "ArrowDown":
doFocusChange(7);
event.preventDefault();
break;
case "ArrowLeft":
doFocusChange(-1);
event.preventDefault();
break;
default:
break;
}
},
[doFocusChange]
);
const handleTabInside = (0, import_react.useCallback)(
(event) => {
var _a2;
if (event.key !== "Tab")
return;
const focusableElements = (_a2 = calendarPaddingRef.current) == null ? void 0 : _a2.querySelectorAll(
'button:not([disabled]):not([tabindex="-1"]), select'
);
if (!focusableElements)
return;
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
if (!event.shiftKey && document.activeElement === lastElement) {
firstElement.focus();
event.preventDefault();
} else if (event.shiftKey && document.activeElement === firstElement) {
lastElement.focus();
event.preventDefault();
}
},
[]
);
const isFocusableDate = (0, import_react.useCallback)(
(dateInfo) => {
const { date: date2, selected, selectable, prevMonth, nextMonth } = dateInfo;
if (!selectable) {
return false;
}
if (selected) {
return true;
}
if (date2.toString() === (minDate == null ? void 0 : minDate.toString())) {
return true;
}
if (!prevMonth && !nextMonth && shownDate.getFullYear() === date2.getFullYear() && selectedDate.getMonth() !== date2.getMonth() && date2.getDate() === 1) {
return true;
}
return false;
},
[shownDate, minDate, selectedDate]
);
const handleGotoEdgeMonth = (0, import_react.useCallback)(() => {
if (
// Vi er i ferd med å gå til første måned
minDate && shownDate.getFullYear() - minDate.getFullYear() === 0 && shownDate.getMonth() - minDate.getMonth() === 1
) {
document.querySelectorAll(
".jkl-calendar-navigation__arrow"
)[1].focus();
} else if (
// Vi er i ferd med å gå til siste måned
maxDate && maxDate.getFullYear() - shownDate.getFullYear() === 0 && maxDate.getMonth() - shownDate.getMonth() === 1
) {
document.querySelectorAll(
".jkl-calendar-navigation__arrow"
)[0].focus();
}
}, [minDate, maxDate, shownDate]);
const handleYearChange = (0, import_react.useCallback)(
(event) => {
if (event.target.value.length !== 4) {
return;
}
const year = Number.parseInt(event.target.value);
if (Number.isNaN(year)) {
return;
}
let offset2 = (year - shownDate.getFullYear()) * 12;
const expectedDate = new Date(
shownDate.getFullYear(),
shownDate.getMonth() + offset2,
shownDate.getDate()
);
if (maxDate && maxDate.getFullYear() === expectedDate.getFullYear() && maxDate.getMonth() < expectedDate.getMonth()) {
offset2 -= expectedDate.getMonth() - maxDate.getMonth();
} else if (minDate && minDate.getFullYear() === expectedDate.getFullYear() && minDate.getMonth() > expectedDate.getMonth()) {
offset2 += minDate.getMonth() - expectedDate.getMonth();
}
dispatch({
type: "ADD_OFFSET",
addedOffset: offset2
});
return;
},
[shownDate, minDate, maxDate]
);
const handleMonthChange = (0, import_react.useCallback)(
(event) => {
if (!selectedDate && !date) {
return;
}
const yearDiff = shownDate.getFullYear() - (selectedDate || /* @__PURE__ */ new Date()).getFullYear();
const monthDiff = Number.parseInt(event.target.value) - (selectedDate || /* @__PURE__ */ new Date()).getMonth();
dispatch({
type: "SET_OFFSET",
newOffset: yearDiff * 12 + monthDiff
});
return;
},
[selectedDate, date, shownDate]
);
const yearSelectOptions = (0, import_utils.getYearSelectOptions)(
shownYear,
minDate,
maxDate,
yearsToShow
);
const monthSelectOptions = (0, import_utils.getMonthSelectOptions)(
shownYear,
months,
minDate,
maxDate
);
return /* @__PURE__ */ import_react.default.createElement(
"div",
{
ref,
id,
className: "jkl-calendar",
"data-testid": "jkl-calendar"
},
/* @__PURE__ */ import_react.default.createElement(
"div",
{
className: "jkl-calendar__padding",
ref: calendarPaddingRef,
onKeyDown: handleTabInside
},
/* @__PURE__ */ import_react.default.createElement("fieldset", { className: "jkl-calendar-navigation" }, /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement(
import_jkl_button_react.Button,
__spreadProps(__spreadValues({}, getBackProps({
calendars,
onClick: handleGotoEdgeMonth
})), {
variant: "ghost",
icon: /* @__PURE__ */ import_react.default.createElement(import_jkl_icons_react.ArrowLeftIcon, { variant: "medium", bold: true })
})
), /* @__PURE__ */ import_react.default.createElement(
import_jkl_button_react.Button,
__spreadProps(__spreadValues({}, getForwardProps({
calendars,
onClick: handleGotoEdgeMonth
})), {
variant: "ghost",
icon: /* @__PURE__ */ import_react.default.createElement(import_jkl_icons_react.ArrowRightIcon, { variant: "medium", bold: true })
})
)), /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement("div", { className: "jkl-calendar-navigation-dropdown" }, /* @__PURE__ */ import_react.default.createElement(
"select",
{
onChange: handleMonthChange,
className: "jkl-calendar-navigation-dropdown__select",
"aria-label": monthLabel,
value: shownMonth.toString()
},
monthSelectOptions.map(
({ label, value }) => /* @__PURE__ */ import_react.default.createElement("option", { key: value, value }, label)
)
), /* @__PURE__ */ import_react.default.createElement(
import_jkl_icons_react.ChevronDownIcon,
{
bold: true,
className: "jkl-calendar-navigation-dropdown__chevron"
}
)), /* @__PURE__ */ import_react.default.createElement("div", { className: "jkl-calendar-navigation-dropdown" }, /* @__PURE__ */ import_react.default.createElement(
"select",
{
onChange: handleYearChange,
className: "jkl-calendar-navigation-dropdown__select",
"aria-label": yearLabel,
value: shownYear.toString()
},
yearSelectOptions.map((year) => /* @__PURE__ */ import_react.default.createElement("option", { key: year, value: year }, year))
), /* @__PURE__ */ import_react.default.createElement(
import_jkl_icons_react.ChevronDownIcon,
{
bold: true,
className: "jkl-calendar-navigation-dropdown__chevron"
}
)))),
calendars.map((calendar) => /* @__PURE__ */ import_react.default.createElement(
"table",
{
className: "jkl-calendar-table",
key: "".concat(calendar.month).concat(calendar.year),
"data-testid": "jkl-datepicker-calendar"
},
/* @__PURE__ */ import_react.default.createElement("caption", { className: "jkl-sr-only" }, months[calendar.month], ", ", calendar.year),
/* @__PURE__ */ import_react.default.createElement("thead", null, /* @__PURE__ */ import_react.default.createElement("tr", null, days.map((weekday) => /* @__PURE__ */ import_react.default.createElement(
"th",
{
key: "".concat(calendar.month).concat(calendar.year).concat(weekday)
},
weekday
)))),
/* @__PURE__ */ import_react.default.createElement("tbody", { "data-testid": "jkl-datepicker-dates" }, calendar.weeks.map((week, weekIndex) => /* @__PURE__ */ import_react.default.createElement(
"tr",
{
key: "".concat(calendar.month).concat(calendar.year).concat(weekIndex)
},
week.map((dateInfo, index) => {
const key = "".concat(calendar.month).concat(calendar.year).concat(weekIndex).concat(index);
if (typeof dateInfo === "string") {
return /* @__PURE__ */ import_react.default.createElement(
"td",
{
className: "jkl-calendar__date jkl-calendar__date--empty",
key
},
dateInfo
);
}
const {
date: date2,
selectable,
today,
prevMonth,
nextMonth
} = dateInfo;
return /* @__PURE__ */ import_react.default.createElement("td", { key }, /* @__PURE__ */ import_react.default.createElement(
"button",
__spreadProps(__spreadValues({}, getDateProps({
dateObj: dateInfo
})), {
type: "button",
className: "jkl-calendar-date-button",
tabIndex: isFocusableDate(
dateInfo
) ? 0 : -1,
"aria-label": "".concat(date2.getDate(), ". ").concat(months[date2.getMonth()].toLowerCase()),
"aria-current": today ? "date" : void 0,
"data-adjacent": prevMonth || nextMonth ? "true" : void 0,
disabled: !selectable,
onKeyDown: handleArrowNavigation
}),
/* @__PURE__ */ import_react.default.createElement("span", { "aria-hidden": "true" }, date2.getDate())
));
})
)))
))
)
);
}
);
Calendar.displayName = "Calendar";
//# sourceMappingURL=Calendar.js.map