first commit

This commit is contained in:
Stefan Hacker
2026-04-03 09:38:48 +02:00
commit 37ad745546
47450 changed files with 3120798 additions and 0 deletions
@@ -0,0 +1,3 @@
import * as React from 'react';
import type { ICalendarMonthProps } from './CalendarMonth.types';
export declare const CalendarMonthBase: React.FunctionComponent<ICalendarMonthProps>;
@@ -0,0 +1,223 @@
import * as React from 'react';
import { FocusZone } from '../../../FocusZone';
import { addYears, setMonth, getYearStart, getYearEnd, getMonthStart, getMonthEnd, compareDatePart, DEFAULT_DATE_FORMATTING, } from '@fluentui/date-time-utilities';
import { Icon } from '../../../Icon';
import { getStyles } from './CalendarMonth.styles';
import { css, getRTL, classNamesFunction, KeyCodes, format, getPropsWithDefaults } from '@fluentui/utilities';
import { CalendarYear } from '../CalendarYear/CalendarYear';
import { usePrevious } from '@fluentui/react-hooks';
import { defaultCalendarNavigationIcons } from '../defaults';
var MONTHS_PER_ROW = 4;
var getClassNames = classNamesFunction();
var DEFAULT_PROPS = {
styles: getStyles,
strings: undefined,
navigationIcons: defaultCalendarNavigationIcons,
dateTimeFormatter: DEFAULT_DATE_FORMATTING,
yearPickerHidden: false,
};
function useAnimateBackwards(_a) {
var navigatedDate = _a.navigatedDate;
var currentYear = navigatedDate.getFullYear();
var previousYear = usePrevious(currentYear);
if (previousYear === undefined || previousYear === currentYear) {
return undefined;
}
else {
return previousYear > currentYear;
}
}
function useFocusLogic(_a) {
var componentRef = _a.componentRef;
var navigatedMonthRef = React.useRef(null);
var calendarYearRef = React.useRef(null);
var focusOnUpdate = React.useRef(false);
var focus = React.useCallback(function () {
if (calendarYearRef.current) {
calendarYearRef.current.focus();
}
else if (navigatedMonthRef.current) {
navigatedMonthRef.current.focus();
}
}, []);
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 [navigatedMonthRef, calendarYearRef, focusOnNextUpdate];
}
export var CalendarMonthBase = function (propsWithoutDefaults) {
var _a, _b;
var props = getPropsWithDefaults(DEFAULT_PROPS, propsWithoutDefaults);
var _c = useFocusLogic(props), navigatedMonthRef = _c[0], calendarYearRef = _c[1], focusOnNextUpdate = _c[2];
var _d = React.useState(false), isYearPickerVisible = _d[0], setIsYearPickerVisible = _d[1];
var animateBackwards = useAnimateBackwards(props);
var navigatedDate = props.navigatedDate, selectedDate = props.selectedDate, strings = props.strings, _e = props.today, today = _e === void 0 ? new Date() : _e, navigationIcons = props.navigationIcons, dateTimeFormatter = props.dateTimeFormatter, minDate = props.minDate, maxDate = props.maxDate, theme = props.theme, styles = props.styles, className = props.className, allFocusable = props.allFocusable, highlightCurrentMonth = props.highlightCurrentMonth, highlightSelectedMonth = props.highlightSelectedMonth, animationDirection = props.animationDirection, yearPickerHidden = props.yearPickerHidden, onNavigateDate = props.onNavigateDate;
var selectMonthCallback = function (newMonth) {
return function () { return onSelectMonth(newMonth); };
};
var onSelectNextYear = function () {
onNavigateDate(addYears(navigatedDate, 1), false);
};
var onSelectPrevYear = function () {
onNavigateDate(addYears(navigatedDate, -1), false);
};
var onSelectMonth = function (newMonth) {
var _a;
// If header is clickable the calendars are overlayed, switch back to day picker when month is clicked
(_a = props.onHeaderSelect) === null || _a === void 0 ? void 0 : _a.call(props);
onNavigateDate(setMonth(navigatedDate, newMonth), true);
};
var onHeaderSelect = function () {
var _a;
if (!yearPickerHidden) {
focusOnNextUpdate();
setIsYearPickerVisible(true);
}
else {
(_a = props.onHeaderSelect) === null || _a === void 0 ? void 0 : _a.call(props);
}
};
var onSelectYear = function (selectedYear) {
focusOnNextUpdate();
var navYear = navigatedDate.getFullYear();
if (navYear !== selectedYear) {
var newNavigationDate = new Date(navigatedDate.getTime());
newNavigationDate.setFullYear(selectedYear);
// for min and max dates, adjust the new navigation date - perhaps this should be
// checked on the master navigation date handler (i.e. in Calendar)
if (maxDate && newNavigationDate > maxDate) {
newNavigationDate = setMonth(newNavigationDate, maxDate.getMonth());
}
else if (minDate && newNavigationDate < minDate) {
newNavigationDate = setMonth(newNavigationDate, minDate.getMonth());
}
onNavigateDate(newNavigationDate, true);
}
setIsYearPickerVisible(false);
};
var onYearPickerHeaderSelect = function (focus) {
focusOnNextUpdate();
setIsYearPickerVisible(false);
};
// navigationIcons has a default value in defaultProps, but typescript doesn't recognize this
var leftNavigationIcon = navigationIcons.leftNavigation;
var rightNavigationIcon = navigationIcons.rightNavigation;
var dateFormatter = dateTimeFormatter;
// determine if previous/next years are in bounds
var isPrevYearInBounds = minDate ? compareDatePart(minDate, getYearStart(navigatedDate)) < 0 : true;
var isNextYearInBounds = maxDate ? compareDatePart(getYearEnd(navigatedDate), maxDate) < 0 : true;
var classNames = getClassNames(styles, {
theme: theme,
className: className,
hasHeaderClickCallback: !!props.onHeaderSelect || !yearPickerHidden,
highlightCurrent: highlightCurrentMonth,
highlightSelected: highlightSelectedMonth,
animateBackwards: animateBackwards,
animationDirection: animationDirection,
});
if (isYearPickerVisible) {
var _f = getYearStrings(props), onRenderYear = _f[0], yearStrings = _f[1];
// use navigated date for the year picker
return (React.createElement(CalendarYear, { key: 'calendarYear', minYear: minDate ? minDate.getFullYear() : undefined, maxYear: maxDate ? maxDate.getFullYear() : undefined,
// eslint-disable-next-line react/jsx-no-bind
onSelectYear: onSelectYear, navigationIcons: navigationIcons,
// eslint-disable-next-line react/jsx-no-bind
onHeaderSelect: onYearPickerHeaderSelect, selectedYear: selectedDate ? selectedDate.getFullYear() : navigatedDate ? navigatedDate.getFullYear() : undefined, navigatedYear: navigatedDate.getFullYear(), onRenderYear: onRenderYear, strings: yearStrings, componentRef: calendarYearRef, styles: styles, highlightCurrentYear: highlightCurrentMonth, highlightSelectedYear: highlightSelectedMonth, animationDirection: animationDirection }));
}
var rowIndexes = [];
for (var i = 0; i < strings.shortMonths.length / MONTHS_PER_ROW; i++) {
rowIndexes.push(i);
}
var yearString = dateFormatter.formatYear(navigatedDate);
var headerAriaLabel = strings.monthPickerHeaderAriaLabel
? format(strings.monthPickerHeaderAriaLabel, yearString)
: yearString;
return (React.createElement("div", { className: classNames.root },
React.createElement("div", { className: classNames.headerContainer },
React.createElement("button", { className: classNames.currentItemButton, onClick: onHeaderSelect, onKeyDown: onButtonKeyDown(onHeaderSelect), "aria-label": headerAriaLabel, "data-is-focusable": !!props.onHeaderSelect || !yearPickerHidden, tabIndex: !!props.onHeaderSelect || !yearPickerHidden ? 0 : -1, type: "button" },
React.createElement("span", { "aria-live": "polite", "aria-atomic": "true" }, yearString)),
React.createElement("div", { className: classNames.navigationButtonsContainer },
React.createElement("button", { className: css(classNames.navigationButton, (_a = {},
_a[classNames.disabled] = !isPrevYearInBounds,
_a)), "aria-disabled": !isPrevYearInBounds, tabIndex: isPrevYearInBounds ? undefined : allFocusable ? 0 : -1, onClick: isPrevYearInBounds ? onSelectPrevYear : undefined, onKeyDown: isPrevYearInBounds ? onButtonKeyDown(onSelectPrevYear) : undefined, title: strings.prevYearAriaLabel
? strings.prevYearAriaLabel + ' ' + dateFormatter.formatYear(addYears(navigatedDate, -1))
: undefined, type: "button" },
React.createElement(Icon, { iconName: getRTL() ? rightNavigationIcon : leftNavigationIcon })),
React.createElement("button", { className: css(classNames.navigationButton, (_b = {},
_b[classNames.disabled] = !isNextYearInBounds,
_b)), "aria-disabled": !isNextYearInBounds, tabIndex: isNextYearInBounds ? undefined : allFocusable ? 0 : -1, onClick: isNextYearInBounds ? onSelectNextYear : undefined, onKeyDown: isNextYearInBounds ? onButtonKeyDown(onSelectNextYear) : undefined, title: strings.nextYearAriaLabel
? strings.nextYearAriaLabel + ' ' + dateFormatter.formatYear(addYears(navigatedDate, 1))
: undefined, type: "button" },
React.createElement(Icon, { iconName: getRTL() ? leftNavigationIcon : rightNavigationIcon })))),
React.createElement(FocusZone, null,
React.createElement("div", { className: classNames.gridContainer, role: "grid", "aria-label": yearString }, rowIndexes.map(function (rowNum) {
var monthsForRow = strings.shortMonths.slice(rowNum * MONTHS_PER_ROW, (rowNum + 1) * MONTHS_PER_ROW);
return (React.createElement("div", { key: 'monthRow_' + rowNum + navigatedDate.getFullYear(), role: "row", className: classNames.buttonRow }, monthsForRow.map(function (month, index) {
var _a;
var monthIndex = rowNum * MONTHS_PER_ROW + index;
var indexedMonth = setMonth(navigatedDate, monthIndex);
var isNavigatedMonth = navigatedDate.getMonth() === monthIndex;
var isSelectedMonth = selectedDate.getMonth() === monthIndex;
var isSelectedYear = selectedDate.getFullYear() === navigatedDate.getFullYear();
var isInBounds = (minDate ? compareDatePart(minDate, getMonthEnd(indexedMonth)) < 1 : true) &&
(maxDate ? compareDatePart(getMonthStart(indexedMonth), maxDate) < 1 : true);
return (React.createElement("button", { ref: isNavigatedMonth ? navigatedMonthRef : undefined, role: 'gridcell', className: css(classNames.itemButton, (_a = {},
_a[classNames.current] = highlightCurrentMonth && isCurrentMonth(monthIndex, navigatedDate.getFullYear(), today),
_a[classNames.selected] = highlightSelectedMonth && isSelectedMonth && isSelectedYear,
_a[classNames.disabled] = !isInBounds,
_a)), disabled: !allFocusable && !isInBounds, key: monthIndex, onClick: isInBounds ? selectMonthCallback(monthIndex) : undefined, onKeyDown: isInBounds ? onButtonKeyDown(selectMonthCallback(monthIndex)) : undefined, "aria-label": dateFormatter.formatMonth(indexedMonth, strings), "aria-selected": isNavigatedMonth, "data-is-focusable": isInBounds ? true : undefined, type: "button" }, month));
})));
})))));
};
CalendarMonthBase.displayName = 'CalendarMonthBase';
function getYearStrings(_a) {
var strings = _a.strings, navigatedDate = _a.navigatedDate, dateTimeFormatter = _a.dateTimeFormatter;
var yearToString = function (year) {
if (dateTimeFormatter) {
// create a date based on the current nav date
var yearFormattingDate = new Date(navigatedDate.getTime());
yearFormattingDate.setFullYear(year);
return dateTimeFormatter.formatYear(yearFormattingDate);
}
return String(year);
};
var yearRangeToString = function (yearRange) {
return "".concat(yearToString(yearRange.fromYear), " - ").concat(yearToString(yearRange.toYear));
};
var yearRangeToNextDecadeLabel = function (yearRange) {
return strings.nextYearRangeAriaLabel ? "".concat(strings.nextYearRangeAriaLabel, " ").concat(yearRangeToString(yearRange)) : '';
};
var yearRangeToPrevDecadeLabel = function (yearRange) {
return strings.prevYearRangeAriaLabel ? "".concat(strings.prevYearRangeAriaLabel, " ").concat(yearRangeToString(yearRange)) : '';
};
return [
yearToString,
{
rangeAriaLabel: yearRangeToString,
prevRangeAriaLabel: yearRangeToPrevDecadeLabel,
nextRangeAriaLabel: yearRangeToNextDecadeLabel,
headerAriaLabelFormatString: strings.yearPickerHeaderAriaLabel,
},
];
}
function isCurrentMonth(month, year, today) {
return today.getFullYear() === year && today.getMonth() === month;
}
function onButtonKeyDown(callback) {
return function (ev) {
// eslint-disable-next-line @typescript-eslint/no-deprecated
switch (ev.which) {
case KeyCodes.enter:
callback();
break;
}
};
}
//# sourceMappingURL=CalendarMonth.base.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
import * as React from 'react';
import type { ICalendarMonthProps } from './CalendarMonth.types';
export declare const CalendarMonth: React.FunctionComponent<ICalendarMonthProps>;
@@ -0,0 +1,5 @@
import { CalendarMonthBase } from './CalendarMonth.base';
import { getStyles } from './CalendarMonth.styles';
import { styled } from '../../../Utilities';
export var CalendarMonth = styled(CalendarMonthBase, getStyles, undefined, { scope: 'CalendarMonth' });
//# sourceMappingURL=CalendarMonth.js.map
@@ -0,0 +1 @@
{"version":3,"file":"CalendarMonth.js","sourceRoot":"../src/","sources":["components/Calendar/CalendarMonth/CalendarMonth.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAG5C,MAAM,CAAC,IAAM,aAAa,GAAiD,MAAM,CAC/E,iBAAiB,EACjB,SAAS,EACT,SAAS,EACT,EAAE,KAAK,EAAE,eAAe,EAAE,CAC3B,CAAC","sourcesContent":["import * as React from 'react';\nimport { CalendarMonthBase } from './CalendarMonth.base';\nimport { getStyles } from './CalendarMonth.styles';\nimport { styled } from '../../../Utilities';\nimport type { ICalendarMonthProps } from './CalendarMonth.types';\n\nexport const CalendarMonth: React.FunctionComponent<ICalendarMonthProps> = styled(\n CalendarMonthBase,\n getStyles,\n undefined,\n { scope: 'CalendarMonth' },\n);\n"]}
@@ -0,0 +1,2 @@
import type { ICalendarMonthStyleProps, ICalendarMonthStyles } from './CalendarMonth.types';
export declare const getStyles: (props: ICalendarMonthStyleProps) => ICalendarMonthStyles;
@@ -0,0 +1,12 @@
import { getStyles as getPickerStyles } from '../CalendarPicker/CalendarPicker.styles';
export var getStyles = function (props) {
/* Return styles from the base class.
* If this component has extra styles not in the base, apply them here i.e.:
* const myStyle: IStyle = {
* display: "block"
* }; *
* return {...getPickerStyles(props), myStyle};
*/
return getPickerStyles(props);
};
//# sourceMappingURL=CalendarMonth.styles.js.map
@@ -0,0 +1 @@
{"version":3,"file":"CalendarMonth.styles.js","sourceRoot":"../src/","sources":["components/Calendar/CalendarMonth/CalendarMonth.styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,IAAI,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAGvF,MAAM,CAAC,IAAM,SAAS,GAAG,UAAC,KAA+B;IACvD;;;;;;OAMG;IAEH,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC,CAAC","sourcesContent":["import { getStyles as getPickerStyles } from '../CalendarPicker/CalendarPicker.styles';\nimport type { ICalendarMonthStyleProps, ICalendarMonthStyles } from './CalendarMonth.types';\n\nexport const getStyles = (props: ICalendarMonthStyleProps): ICalendarMonthStyles => {\n /* Return styles from the base class.\n * If this component has extra styles not in the base, apply them here i.e.:\n * const myStyle: IStyle = {\n * display: \"block\"\n * }; *\n * return {...getPickerStyles(props), myStyle};\n */\n\n return getPickerStyles(props);\n};\n"]}
@@ -0,0 +1,117 @@
import { AnimationDirection } from '../Calendar.types';
import type { IBaseProps, IRefObject, IStyleFunctionOrObject } from '@fluentui/utilities';
import type { ICalendarNavigationIcons } from '../Calendar.types';
import type { ITheme } from '@fluentui/style-utilities';
import type { ICalendarPickerStyleProps, ICalendarPickerStyles } from '../CalendarPicker/CalendarPicker.types';
import type { ICalendarStrings, IDateFormatting } from '@fluentui/date-time-utilities';
/**
* {@docCategory Calendar}
*/
export interface ICalendarMonth {
focus(): void;
}
/**
* {@docCategory Calendar}
*/
export interface ICalendarMonthProps extends IBaseProps<ICalendarMonth> {
/**
* Optional callback to access the ICalendarMonth interface. Use this instead of ref for accessing
* the public methods and properties of the component.
*/
componentRef?: IRefObject<ICalendarMonth>;
/**
* Customized styles for the calendar month component
*/
styles?: IStyleFunctionOrObject<ICalendarMonthStyleProps, ICalendarMonthStyles>;
/**
* Theme (provided through customization).
*/
theme?: ITheme;
/**
* Localized strings to use in the Calendar
*/
strings: ICalendarStrings;
/**
* The currently selected date
*/
selectedDate: Date;
/**
* The currently navigated date
*/
navigatedDate: Date;
/**
* Callback issued when a month is selected
* @param date - The date the user selected
* @param selectedDateRangeArray - The resultant list of dates that are selected based on the date range type set
* for the component.
*/
onSelectDate?: (date: Date, selectedDateRangeArray?: Date[]) => void;
/**
* Callback issued when the year is navigated
* @param date - The date that is navigated to
* @param focusOnNavigatedDay - Whether to set the focus to the navigated date.
*/
onNavigateDate: (date: Date, focusOnNavigatedDay: boolean) => void;
/**
* Custom navigation icons.
*/
navigationIcons?: ICalendarNavigationIcons;
/**
* Value of today. If unspecified, current time in client machine will be used.
*/
today?: Date;
/**
* Callback function when the header is selected
*/
onHeaderSelect?: () => void;
/**
* Apply additional formatting to dates, for example localized date formatting.
*/
dateTimeFormatter?: IDateFormatting;
/**
* If set the Calendar will not allow navigation to or selection of a date earlier than this value.
*/
minDate?: Date;
/**
* If set the Calendar will not allow navigation to or selection of a date later than this value.
*/
maxDate?: Date;
/**
* Whether the month picker should highlight the current month
* @defaultvalue false
*/
highlightCurrentMonth?: boolean;
/**
* Whether the month picker should highlight the selected month
* @defaultvalue false
*/
highlightSelectedMonth?: boolean;
/**
* Allows all dates and buttons to be focused, including disabled ones
* @defaultvalue false
*/
allFocusable?: boolean;
/**
* Additional CSS class(es) to apply to the CalendarMonth.
*/
className?: string;
/**
* Whether the year picker is hidden
* @defaultvalue false
*/
yearPickerHidden?: boolean;
/**
* The cardinal directions for animation to occur during transitions, either horizontal or veritcal
*/
animationDirection?: AnimationDirection;
}
/**
* {@docCategory Calendar}
*/
export interface ICalendarMonthStyleProps extends ICalendarPickerStyleProps {
}
/**
* {@docCategory Calendar}
*/
export interface ICalendarMonthStyles extends ICalendarPickerStyles {
}
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=CalendarMonth.types.js.map
@@ -0,0 +1 @@
{"version":3,"file":"CalendarMonth.types.js","sourceRoot":"../src/","sources":["components/Calendar/CalendarMonth/CalendarMonth.types.ts"],"names":[],"mappings":"","sourcesContent":["import { AnimationDirection } from '../Calendar.types';\nimport type { IBaseProps, IRefObject, IStyleFunctionOrObject } from '@fluentui/utilities';\nimport type { ICalendarNavigationIcons } from '../Calendar.types';\nimport type { ITheme } from '@fluentui/style-utilities';\nimport type { ICalendarPickerStyleProps, ICalendarPickerStyles } from '../CalendarPicker/CalendarPicker.types';\nimport type { ICalendarStrings, IDateFormatting } from '@fluentui/date-time-utilities';\n\n/**\n * {@docCategory Calendar}\n */\nexport interface ICalendarMonth {\n focus(): void;\n}\n\n/**\n * {@docCategory Calendar}\n */\nexport interface ICalendarMonthProps extends IBaseProps<ICalendarMonth> {\n /**\n * Optional callback to access the ICalendarMonth interface. Use this instead of ref for accessing\n * the public methods and properties of the component.\n */\n componentRef?: IRefObject<ICalendarMonth>;\n\n /**\n * Customized styles for the calendar month component\n */\n styles?: IStyleFunctionOrObject<ICalendarMonthStyleProps, ICalendarMonthStyles>;\n\n /**\n * Theme (provided through customization).\n */\n theme?: ITheme;\n\n /**\n * Localized strings to use in the Calendar\n */\n strings: ICalendarStrings;\n\n /**\n * The currently selected date\n */\n selectedDate: Date;\n\n /**\n * The currently navigated date\n */\n navigatedDate: Date;\n\n /**\n * Callback issued when a month is selected\n * @param date - The date the user selected\n * @param selectedDateRangeArray - The resultant list of dates that are selected based on the date range type set\n * for the component.\n */\n onSelectDate?: (date: Date, selectedDateRangeArray?: Date[]) => void;\n\n /**\n * Callback issued when the year is navigated\n * @param date - The date that is navigated to\n * @param focusOnNavigatedDay - Whether to set the focus to the navigated date.\n */\n onNavigateDate: (date: Date, focusOnNavigatedDay: boolean) => void;\n\n /**\n * Custom navigation icons.\n */\n navigationIcons?: ICalendarNavigationIcons;\n\n /**\n * Value of today. If unspecified, current time in client machine will be used.\n */\n today?: Date;\n\n /**\n * Callback function when the header is selected\n */\n onHeaderSelect?: () => void;\n\n /**\n * Apply additional formatting to dates, for example localized date formatting.\n */\n dateTimeFormatter?: IDateFormatting;\n\n /**\n * If set the Calendar will not allow navigation to or selection of a date earlier than this value.\n */\n minDate?: Date;\n\n /**\n * If set the Calendar will not allow navigation to or selection of a date later than this value.\n */\n maxDate?: Date;\n\n /**\n * Whether the month picker should highlight the current month\n * @defaultvalue false\n */\n highlightCurrentMonth?: boolean;\n\n /**\n * Whether the month picker should highlight the selected month\n * @defaultvalue false\n */\n highlightSelectedMonth?: boolean;\n\n /**\n * Allows all dates and buttons to be focused, including disabled ones\n * @defaultvalue false\n */\n allFocusable?: boolean;\n\n /**\n * Additional CSS class(es) to apply to the CalendarMonth.\n */\n className?: string;\n\n /**\n * Whether the year picker is hidden\n * @defaultvalue false\n */\n yearPickerHidden?: boolean;\n\n /**\n * The cardinal directions for animation to occur during transitions, either horizontal or veritcal\n */\n animationDirection?: AnimationDirection;\n}\n\n/**\n * {@docCategory Calendar}\n */\nexport interface ICalendarMonthStyleProps extends ICalendarPickerStyleProps {}\n\n/**\n * {@docCategory Calendar}\n */\nexport interface ICalendarMonthStyles extends ICalendarPickerStyles {}\n"]}