@fluentui/react
Version:
Reusable React components for building web experiences.
258 lines • 13.8 kB
JavaScript
import { __assign } from "tslib";
import * as React from 'react';
import { DayOfWeek, FirstWeekOfYear, DateRangeType, addMonths, addYears, DEFAULT_CALENDAR_STRINGS, DEFAULT_DATE_FORMATTING, } from '@fluentui/date-time-utilities';
import { CalendarDay } from './CalendarDay/CalendarDay';
import { CalendarMonth } from './CalendarMonth/CalendarMonth';
import { css, KeyCodes, classNamesFunction, focusAsync, format, FocusRects, getPropsWithDefaults, getWindow, } from '@fluentui/utilities';
import { useControllableValue } from '@fluentui/react-hooks';
import { defaultCalendarNavigationIcons } from './defaults';
var MIN_SIZE_FORCE_OVERLAY = 440;
var getClassNames = classNamesFunction();
var defaultWorkWeekDays = [
DayOfWeek.Monday,
DayOfWeek.Tuesday,
DayOfWeek.Wednesday,
DayOfWeek.Thursday,
DayOfWeek.Friday,
];
var DEFAULT_PROPS = {
isMonthPickerVisible: true,
isDayPickerVisible: true,
showMonthPickerAsOverlay: false,
today: new Date(),
firstDayOfWeek: DayOfWeek.Sunday,
dateRangeType: DateRangeType.Day,
showGoToToday: true,
strings: DEFAULT_CALENDAR_STRINGS,
highlightCurrentMonth: false,
highlightSelectedMonth: false,
navigationIcons: defaultCalendarNavigationIcons,
showWeekNumbers: false,
firstWeekOfYear: FirstWeekOfYear.FirstDay,
dateTimeFormatter: DEFAULT_DATE_FORMATTING,
showSixWeeksByDefault: false,
workWeekDays: defaultWorkWeekDays,
showCloseButton: false,
allFocusable: false,
};
function useDateState(props) {
var value = props.value, todayProp = props.today, onSelectDate = props.onSelectDate;
var today = React.useMemo(function () {
if (todayProp === undefined) {
return new Date();
}
return todayProp;
}, [todayProp]);
/** The currently selected date in the calendar */
var _a = useControllableValue(value, today), _b = _a[0], selectedDate = _b === void 0 ? today : _b, setSelectedDate = _a[1];
/** The currently focused date in the day picker, but not necessarily selected */
var _c = React.useState(value), _d = _c[0], navigatedDay = _d === void 0 ? today : _d, setNavigatedDay = _c[1];
/** The currently focused date in the month picker, but not necessarily selected */
var _e = React.useState(value), _f = _e[0], navigatedMonth = _f === void 0 ? today : _f, setNavigatedMonth = _e[1];
/** If using a controlled value, when that value changes, navigate to that date */
var _g = React.useState(value), _h = _g[0], lastSelectedDate = _h === void 0 ? today : _h, setLastSelectedDate = _g[1];
if (value && lastSelectedDate.valueOf() !== value.valueOf()) {
setNavigatedDay(value);
setNavigatedMonth(value);
setLastSelectedDate(value);
}
var navigateMonth = function (date) {
setNavigatedMonth(date);
};
var navigateDay = function (date) {
setNavigatedMonth(date);
setNavigatedDay(date);
};
var onDateSelected = function (date, selectedDateRangeArray) {
setNavigatedMonth(date);
setNavigatedDay(date);
setSelectedDate(date);
onSelectDate === null || onSelectDate === void 0 ? void 0 : onSelectDate(date, selectedDateRangeArray);
};
return [selectedDate, navigatedDay, navigatedMonth, onDateSelected, navigateDay, navigateMonth];
}
function useVisibilityState(props) {
/** State used to show/hide month picker */
var _a = useControllableValue(getShowMonthPickerAsOverlay(props) ? undefined : props.isMonthPickerVisible, false), _b = _a[0], isMonthPickerVisible = _b === void 0 ? true : _b, setIsMonthPickerVisible = _a[1];
/** State used to show/hide day picker */
var _c = useControllableValue(getShowMonthPickerAsOverlay(props) ? undefined : props.isDayPickerVisible, true), _d = _c[0], isDayPickerVisible = _d === void 0 ? true : _d, setIsDayPickerVisible = _c[1];
var toggleDayMonthPickerVisibility = function () {
setIsMonthPickerVisible(!isMonthPickerVisible);
setIsDayPickerVisible(!isDayPickerVisible);
};
return [isMonthPickerVisible, isDayPickerVisible, toggleDayMonthPickerVisibility];
}
function useFocusLogic(_a, isDayPickerVisible, isMonthPickerVisible) {
var componentRef = _a.componentRef;
var dayPicker = React.useRef(null);
var monthPicker = React.useRef(null);
var focusOnUpdate = React.useRef(false);
var focus = React.useCallback(function () {
if (isDayPickerVisible && dayPicker.current) {
focusAsync(dayPicker.current);
}
else if (isMonthPickerVisible && monthPicker.current) {
focusAsync(monthPicker.current);
}
}, [isDayPickerVisible, isMonthPickerVisible]);
React.useImperativeHandle(componentRef, function () { return ({ focus: focus }); }, [focus]);
React.useEffect(function () {
if (focusOnUpdate.current) {
focus();
focusOnUpdate.current = false;
}
});
var focusOnNextUpdate = function () {
focusOnUpdate.current = true;
};
return [dayPicker, monthPicker, focusOnNextUpdate];
}
export var CalendarBase = React.forwardRef(function (propsWithoutDefaults, forwardedRef) {
var props = getPropsWithDefaults(DEFAULT_PROPS, propsWithoutDefaults);
var _a = useDateState(props), selectedDate = _a[0], navigatedDay = _a[1], navigatedMonth = _a[2], onDateSelected = _a[3], navigateDay = _a[4], navigateMonth = _a[5];
var _b = useVisibilityState(props), isMonthPickerVisible = _b[0], isDayPickerVisible = _b[1], toggleDayMonthPickerVisibility = _b[2];
var _c = useFocusLogic(props, isDayPickerVisible, isMonthPickerVisible), dayPicker = _c[0], monthPicker = _c[1], focusOnNextUpdate = _c[2];
var renderGoToTodayButton = function () {
var goTodayEnabled = showGoToToday;
if (goTodayEnabled && today) {
goTodayEnabled =
navigatedDay.getFullYear() !== today.getFullYear() ||
navigatedDay.getMonth() !== today.getMonth() ||
navigatedMonth.getFullYear() !== today.getFullYear() ||
navigatedMonth.getMonth() !== today.getMonth();
}
return (showGoToToday && (React.createElement("button", { className: css('js-goToday', classes.goTodayButton), onClick: onGotoToday, onKeyDown: onButtonKeyDown(onGotoToday), type: "button", disabled: !goTodayEnabled }, strings.goToToday)));
};
var onNavigateDayDate = function (date, focusOnNavigatedDay) {
navigateDay(date);
if (focusOnNavigatedDay) {
focusOnNextUpdate();
}
};
var onNavigateMonthDate = function (date, focusOnNavigatedDay) {
if (focusOnNavigatedDay) {
focusOnNextUpdate();
}
if (!focusOnNavigatedDay) {
navigateMonth(date);
return;
}
if (monthPickerOnly) {
onDateSelected(date);
}
navigateDay(date);
};
var onHeaderSelect = getShowMonthPickerAsOverlay(props)
? function () {
toggleDayMonthPickerVisibility();
focusOnNextUpdate();
}
: undefined;
var onGotoToday = function () {
navigateDay(today);
focusOnNextUpdate();
};
var onButtonKeyDown = function (callback) {
return function (ev) {
// eslint-disable-next-line deprecation/deprecation
switch (ev.which) {
case KeyCodes.enter:
case KeyCodes.space:
callback();
break;
}
};
};
var onDatePickerPopupKeyDown = function (ev) {
var _a;
// eslint-disable-next-line deprecation/deprecation
switch (ev.which) {
case KeyCodes.enter:
ev.preventDefault();
break;
case KeyCodes.backspace:
ev.preventDefault();
break;
case KeyCodes.escape:
(_a = props.onDismiss) === null || _a === void 0 ? void 0 : _a.call(props);
break;
case KeyCodes.pageUp:
if (ev.ctrlKey) {
// go to next year
navigateDay(addYears(navigatedDay, 1));
}
else {
// go to next month
navigateDay(addMonths(navigatedDay, 1));
}
ev.preventDefault();
break;
case KeyCodes.pageDown:
if (ev.ctrlKey) {
// go to previous year
navigateDay(addYears(navigatedDay, -1));
}
else {
// go to previous month
navigateDay(addMonths(navigatedDay, -1));
}
ev.preventDefault();
break;
default:
break;
}
};
var rootClass = 'ms-DatePicker';
var firstDayOfWeek = props.firstDayOfWeek, dateRangeType = props.dateRangeType, strings = props.strings, showGoToToday = props.showGoToToday, highlightCurrentMonth = props.highlightCurrentMonth, highlightSelectedMonth = props.highlightSelectedMonth, navigationIcons = props.navigationIcons, minDate = props.minDate, maxDate = props.maxDate, restrictedDates = props.restrictedDates, id = props.id, className = props.className, showCloseButton = props.showCloseButton, allFocusable = props.allFocusable, styles = props.styles, showWeekNumbers = props.showWeekNumbers, theme = props.theme, calendarDayProps = props.calendarDayProps, calendarMonthProps = props.calendarMonthProps, dateTimeFormatter = props.dateTimeFormatter, _d = props.today, today = _d === void 0 ? new Date() : _d;
var showMonthPickerAsOverlay = getShowMonthPickerAsOverlay(props);
var monthPickerOnly = !showMonthPickerAsOverlay && !isDayPickerVisible;
var overlaidWithButton = showMonthPickerAsOverlay && showGoToToday;
var classes = getClassNames(styles, {
theme: theme,
className: className,
isMonthPickerVisible: isMonthPickerVisible,
isDayPickerVisible: isDayPickerVisible,
monthPickerOnly: monthPickerOnly,
showMonthPickerAsOverlay: showMonthPickerAsOverlay,
overlaidWithButton: overlaidWithButton,
overlayedWithButton: overlaidWithButton,
showGoToToday: showGoToToday,
showWeekNumbers: showWeekNumbers,
});
var todayDateString = '';
var selectedDateString = '';
if (dateTimeFormatter && strings.todayDateFormatString) {
todayDateString = format(strings.todayDateFormatString, dateTimeFormatter.formatMonthDayYear(today, strings));
}
if (dateTimeFormatter && strings.selectedDateFormatString) {
var dateStringFormatter = monthPickerOnly
? dateTimeFormatter.formatMonthYear
: dateTimeFormatter.formatMonthDayYear;
selectedDateString = format(strings.selectedDateFormatString, dateStringFormatter(selectedDate, strings));
}
var selectionAndTodayString = selectedDateString + ', ' + todayDateString;
return (React.createElement("div", { id: id, ref: forwardedRef, role: "group", "aria-label": selectionAndTodayString, className: css(rootClass, classes.root, className, 'ms-slideDownIn10'), onKeyDown: onDatePickerPopupKeyDown },
React.createElement("div", { className: classes.liveRegion, "aria-live": "polite", "aria-atomic": "true" },
React.createElement("span", null, selectedDateString)),
isDayPickerVisible && (React.createElement(CalendarDay, __assign({ selectedDate: selectedDate, navigatedDate: navigatedDay, today: props.today, onSelectDate: onDateSelected,
// eslint-disable-next-line react/jsx-no-bind
onNavigateDate: onNavigateDayDate, onDismiss: props.onDismiss, firstDayOfWeek: firstDayOfWeek, dateRangeType: dateRangeType, strings: strings,
// eslint-disable-next-line react/jsx-no-bind
onHeaderSelect: onHeaderSelect, navigationIcons: navigationIcons, showWeekNumbers: props.showWeekNumbers, firstWeekOfYear: props.firstWeekOfYear, dateTimeFormatter: props.dateTimeFormatter, showSixWeeksByDefault: props.showSixWeeksByDefault, minDate: minDate, maxDate: maxDate, restrictedDates: restrictedDates, workWeekDays: props.workWeekDays, componentRef: dayPicker, showCloseButton: showCloseButton, allFocusable: allFocusable }, calendarDayProps))),
isDayPickerVisible && isMonthPickerVisible && React.createElement("div", { className: classes.divider }),
isMonthPickerVisible ? (React.createElement("div", { className: classes.monthPickerWrapper },
React.createElement(CalendarMonth, __assign({ navigatedDate: navigatedMonth, selectedDate: navigatedDay, strings: strings,
// eslint-disable-next-line react/jsx-no-bind
onNavigateDate: onNavigateMonthDate, today: props.today, highlightCurrentMonth: highlightCurrentMonth, highlightSelectedMonth: highlightSelectedMonth,
// eslint-disable-next-line react/jsx-no-bind
onHeaderSelect: onHeaderSelect, navigationIcons: navigationIcons, dateTimeFormatter: props.dateTimeFormatter, minDate: minDate, maxDate: maxDate, componentRef: monthPicker }, calendarMonthProps)),
renderGoToTodayButton())) : (renderGoToTodayButton()),
React.createElement(FocusRects, null)));
});
CalendarBase.displayName = 'CalendarBase';
function getShowMonthPickerAsOverlay(_a) {
var showMonthPickerAsOverlay = _a.showMonthPickerAsOverlay, isDayPickerVisible = _a.isDayPickerVisible;
var win = getWindow();
return showMonthPickerAsOverlay || (isDayPickerVisible && win && win.innerWidth <= MIN_SIZE_FORCE_OVERLAY);
}
//# sourceMappingURL=Calendar.base.js.map