Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
3a72995
refactor(Calendar): add motion components for slide and fade
robertpenner Jan 13, 2025
9a66a51
refactor(Calendar): migrate month row slide to motion components
robertpenner Jan 13, 2025
a35a005
refactor(Calendar): start migrating week row slide to motion components
robertpenner Jan 13, 2025
db0a382
fix(motion): update presence .In and .Out to use MotionComponent type
robertpenner Sep 10, 2025
a350faa
refactor(Calendar): migrate to variants of Slide motion component
robertpenner Sep 10, 2025
f6cdbb8
TEMP: default animationDirection to Horizontal for testing
robertpenner Sep 10, 2025
172d733
reactor(Calendar): create horizontal slide motion components for mont…
robertpenner Sep 10, 2025
405f336
refactor(Calendar): simplify conditional motion; consolidate to Direc…
robertpenner Sep 11, 2025
c24ba37
chore: yarn change
robertpenner Sep 11, 2025
a19dddf
refactor: document Calendar motion constants & plans to migrate to to…
robertpenner Sep 11, 2025
2391720
refactor: rename motions.ts to calendarMotions.ts
robertpenner Sep 11, 2025
42559c0
chore: remove unused code
robertpenner Sep 11, 2025
6d912e8
chore: uncomment CSS animation code
robertpenner Sep 11, 2025
11b64d4
chore(Calendar): change default animationDirection back to Vertical
robertpenner Nov 3, 2025
ddab24e
docs(Calendar): add migration plan for transitioning to Fluent UI v9 …
robertpenner Jan 16, 2026
89272bd
docs(react-calendar-compat): Update MOTION_MIGRATION_PLAN.md
robertpenner Jan 16, 2026
2ab2bf5
refactor(react-calendar-compat): Use motion tokens in DirectionalSlide
robertpenner Jan 16, 2026
4a684c4
refactor(react-calendar-compat): Convert CalendarGridRow to forwardRef
robertpenner Jan 16, 2026
dfc0ef4
refactor(react-calendar-compat): Remove wrapper divs from Directional…
robertpenner Jan 16, 2026
be74384
refactor(react-calendar-compat): Remove CSS slide animations from Cal…
robertpenner Jan 16, 2026
1eff636
refactor(react-calendar-compat): Update animation duration constants
robertpenner Jan 16, 2026
ad0e8f4
deps(react-calendar-compat): Add react-motion dependencies
robertpenner Jan 16, 2026
b00bf02
refactor(react-calendar-compat): Add 'use client' directive to Calend…
robertpenner Jan 16, 2026
f0c0748
feat(react-calendar-compat): Add DirectionalSlide to CalendarYear gri…
robertpenner Jan 16, 2026
7baac93
refactor(react-calendar-compat): Remove CSS slide animations from Cal…
robertpenner Jan 16, 2026
a4c5553
refactor(react-calendar-compat): Mark unused animation constants as d…
robertpenner Jan 16, 2026
a0dc05a
feat(react-calendar-compat): add HeaderFade motion component
robertpenner Jan 16, 2026
234be5d
feat(react-calendar-compat): migrate CalendarDay header to HeaderFade
robertpenner Jan 16, 2026
3ac6178
feat(react-calendar-compat): migrate CalendarMonth header to HeaderFade
robertpenner Jan 16, 2026
714c607
feat(react-calendar-compat): migrate CalendarYear header to HeaderFade
robertpenner Jan 16, 2026
dc07cb3
refactor(react-calendar-compat): remove CSS fade animation from Calen…
robertpenner Jan 16, 2026
420514a
docs(react-calendar-compat): update migration plan to mark Phase 2 co…
robertpenner Jan 16, 2026
354d9ed
refactor(react-calendar-compat): update animation constants to reflec…
robertpenner Jan 16, 2026
f291d4a
feat(react-calendar-compat): migrate CalendarDayGrid weekday labels t…
robertpenner Mar 12, 2026
0579a4c
chore: update yarn.lock
robertpenner Mar 12, 2026
2b20e6f
chore: update version of react-motion-components-preview in Calendar …
robertpenner Mar 12, 2026
82814eb
chore: delete migration plan doc
robertpenner Mar 12, 2026
44740c2
revert(react-motion): remove PresenceComponent type change for separa…
robertpenner Mar 16, 2026
dc92720
chore(react-calendar-compat): remove redundant CSS animation removal …
robertpenner Mar 16, 2026
73903d6
refactor(react-calendar-compat): remove HeaderFade component and redu…
robertpenner Mar 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "refactor(react-calendar): migrate to motion components",
"packageName": "@fluentui/react-calendar-compat",
"email": "robertpenner@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
"@fluentui/keyboard-keys": "^9.0.8",
"@fluentui/react-icons": "^2.0.245",
"@fluentui/react-jsx-runtime": "^9.4.1",
"@fluentui/react-motion": "^9.11.5",
"@fluentui/react-motion-components-preview": "^0.15.2",
"@fluentui/react-shared-contexts": "^9.26.2",
"@fluentui/react-tabster": "^9.26.13",
"@fluentui/react-theme": "^9.2.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { useCalendarDayStyles_unstable } from './useCalendarDayStyles.styles';
import type { ICalendarDayGrid } from '../CalendarDayGrid/CalendarDayGrid.types';
import type { CalendarDayProps, CalendarDayStyles } from './CalendarDay.types';
import type { JSXElement } from '@fluentui/react-utilities';
import { AnimationDirection } from '../../Calendar';

/**
* @internal
Expand Down Expand Up @@ -40,7 +41,7 @@ export const CalendarDay: React.FunctionComponent<CalendarDayProps> = props => {
onNavigateDate,
showWeekNumbers,
dateRangeType,
animationDirection,
animationDirection = AnimationDirection.Vertical,
} = props;

const classNames = useCalendarDayStyles_unstable({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import { tokens } from '@fluentui/react-theme';
import { makeStyles, mergeClasses, shorthands } from '@griffel/react';
import { DURATION_2, EASING_FUNCTION_2, FADE_IN } from '../../utils/animations';
import type { SlotClassNames } from '@fluentui/react-utilities';
import type { CalendarDayStyles, CalendarDayStyleProps } from './CalendarDay.types';

Expand Down Expand Up @@ -64,12 +63,6 @@ const useMonthAndYearStyles = makeStyles({
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
},
animation: {
animationDuration: DURATION_2,
animationFillMode: 'both',
animationName: FADE_IN,
animationTimingFunction: EASING_FUNCTION_2,
},
headerIsClickable: {
'&:hover': {
backgroundColor: tokens.colorBrandBackgroundInvertedHover,
Expand Down Expand Up @@ -166,7 +159,6 @@ export const useCalendarDayStyles_unstable = (props: CalendarDayStyleProps): Cal
monthAndYear: mergeClasses(
calendarDayClassNames.monthAndYear,
monthAndYearStyles.base,
monthAndYearStyles.animation,
headerIsClickable && monthAndYearStyles.headerIsClickable,
),
monthComponents: mergeClasses(calendarDayClassNames.monthComponents, monthComponentsStyles.base),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { useWeekCornerStyles, WeekCorners } from './useWeekCornerStyles.styles';
import { mergeClasses } from '@griffel/react';
import type { Day } from '../../utils';
import type { CalendarDayGridProps } from './CalendarDayGrid.types';
import { DirectionalSlide } from '../../utils/calendarMotions';
import { AnimationDirection } from '../../Calendar';

export interface DayInfo extends Day {
onSelected: () => void;
Expand Down Expand Up @@ -75,6 +77,7 @@ export const CalendarDayGrid: React.FunctionComponent<CalendarDayGridProps> = pr

const weeks = useWeeks(props, onSelectDate, getSetRefCallback);
const animateBackwards = useAnimateBackwards(weeks);

const [getWeekCornerStyles, calculateRoundedStyles] = useWeekCornerStyles(props);

React.useImperativeHandle(
Expand Down Expand Up @@ -130,7 +133,7 @@ export const CalendarDayGrid: React.FunctionComponent<CalendarDayGridProps> = pr
showWeekNumbers,
labelledBy,
lightenDaysOutsideNavigatedMonth,
animationDirection,
animationDirection = AnimationDirection.Vertical,
} = props;

const classNames = useCalendarDayGridStyles_unstable({
Expand All @@ -156,6 +159,8 @@ export const CalendarDayGrid: React.FunctionComponent<CalendarDayGridProps> = pr
} as const;

const arrowNavigationAttributes = useArrowNavigationGroup({ axis: 'grid-linear' });
const firstWeek = weeks[0];
const finalWeek = weeks![weeks!.length - 1];

return (
<table
Expand All @@ -169,34 +174,40 @@ export const CalendarDayGrid: React.FunctionComponent<CalendarDayGridProps> = pr
>
<tbody>
<CalendarMonthHeaderRow {...props} classNames={classNames} weeks={weeks} />
<CalendarGridRow
{...props}
{...partialWeekProps}
week={weeks[0]}
weekIndex={-1}
rowClassName={classNames.firstTransitionWeek}
aria-role="presentation"
ariaHidden={true}
/>
{weeks!.slice(1, weeks!.length - 1).map((week: DayInfo[], weekIndex: number) => (
<DirectionalSlide key={'firstTransitionWeek_' + firstWeek[0].key} {...{ animationDirection, animateBackwards }}>
Copy link
Copy Markdown
Member

@layershifter layershifter Mar 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do need a key there? To re-trigger a motion?

<CalendarGridRow
{...props}
{...partialWeekProps}
key={weekIndex}
week={week}
weekIndex={weekIndex}
rowClassName={classNames.weekRow}
week={firstWeek}
weekIndex={-1}
rowClassName={classNames.firstTransitionWeek}
aria-role="presentation"
ariaHidden={true}
/>
</DirectionalSlide>
{weeks!.slice(1, weeks!.length - 1).map((week: DayInfo[], weekIndex: number) => (
<DirectionalSlide key={weekIndex + '_' + week[0].key} {...{ animationDirection, animateBackwards }}>
<CalendarGridRow
{...props}
{...partialWeekProps}
key={weekIndex}
week={week}
weekIndex={weekIndex}
rowClassName={classNames.weekRow}
/>
</DirectionalSlide>
))}
<CalendarGridRow
{...props}
{...partialWeekProps}
week={weeks![weeks!.length - 1]}
weekIndex={-2}
rowClassName={classNames.lastTransitionWeek}
aria-role="presentation"
ariaHidden={true}
/>
<DirectionalSlide key={'lastTransitionWeek_' + finalWeek[0].key} {...{ animationDirection, animateBackwards }}>
<CalendarGridRow
{...props}
{...partialWeekProps}
week={finalWeek}
weekIndex={-2}
rowClassName={classNames.lastTransitionWeek}
aria-role="presentation"
ariaHidden={true}
/>
</DirectionalSlide>
</tbody>
</table>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use client';

import * as React from 'react';
import { getWeekNumbersInMonth } from '../../utils';
import { CalendarGridDayCell } from './CalendarGridDayCell';
Expand Down Expand Up @@ -28,7 +30,7 @@ export interface CalendarGridRowProps extends CalendarDayGridProps {
/**
* @internal
*/
export const CalendarGridRow: React.FunctionComponent<CalendarGridRowProps> = props => {
export const CalendarGridRow = React.forwardRef<HTMLTableRowElement, CalendarGridRowProps>((props, ref) => {
const {
ariaHidden,
classNames,
Expand All @@ -52,7 +54,7 @@ export const CalendarGridRow: React.FunctionComponent<CalendarGridRowProps> = pr
: '';

return (
<tr role={ariaRole} aria-hidden={ariaHidden} className={rowClassName} key={weekIndex + '_' + week[0].key}>
<tr ref={ref} role={ariaRole} aria-hidden={ariaHidden} className={rowClassName} key={weekIndex + '_' + week[0].key}>
{showWeekNumbers && weekNumbers && (
<th
className={classNames.weekNumberCell}
Expand All @@ -69,4 +71,6 @@ export const CalendarGridRow: React.FunctionComponent<CalendarGridRowProps> = pr
))}
</tr>
);
};
});

CalendarGridRow.displayName = 'CalendarGridRow';
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import * as React from 'react';
import { mergeClasses } from '@griffel/react';
import { motionTokens } from '@fluentui/react-motion';
import { Fade } from '@fluentui/react-motion-components-preview';
import { DAYS_IN_WEEK } from '../../utils';
import type { CalendarDayGridProps, CalendarDayGridStyles } from './CalendarDayGrid.types';
import type { DayInfo } from './CalendarDayGrid';
Expand Down Expand Up @@ -41,16 +43,17 @@ export const CalendarMonthHeaderRow: React.FunctionComponent<CalendarDayMonthHea
const i = (index + firstDayOfWeek) % DAYS_IN_WEEK;
const label = strings.days[i];
return (
<th
className={mergeClasses(classNames.dayCell, classNames.weekDayLabelCell)}
scope="col"
key={dayLabels[i] + ' ' + index}
title={label}
aria-label={label}
tabIndex={allFocusable ? 0 : undefined}
>
{dayLabels[i]}
</th>
<Fade.In key={dayLabels[i] + ' ' + index} duration={motionTokens.durationGentle}>
<th
className={mergeClasses(classNames.dayCell, classNames.weekDayLabelCell)}
scope="col"
title={label}
aria-label={label}
tabIndex={allFocusable ? 0 : undefined}
>
{dayLabels[i]}
</th>
</Fade.In>
);
})}
</tr>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,6 @@

import { tokens } from '@fluentui/react-theme';
import { makeStyles, mergeClasses, shorthands } from '@griffel/react';
import {
DURATION_2,
DURATION_3,
EASING_FUNCTION_1,
EASING_FUNCTION_2,
FADE_IN,
FADE_OUT,
SLIDE_DOWN_IN20,
SLIDE_DOWN_OUT20,
SLIDE_LEFT_IN20,
SLIDE_RIGHT_IN20,
SLIDE_UP_IN20,
SLIDE_UP_OUT20,
TRANSITION_ROW_DISAPPEARANCE,
} from '../../utils';
import { AnimationDirection } from '../Calendar/Calendar.types';
import { weekCornersClassNames } from './useWeekCornerStyles.styles';
import { createFocusOutlineStyle } from '@fluentui/react-tabster';
import type { SlotClassNames } from '@fluentui/react-utilities';
Expand Down Expand Up @@ -180,32 +164,11 @@ const useWeekRowStyles = makeStyles({
zIndex: 1,
},
},
animation: {
animationDuration: DURATION_3,
animationFillMode: 'both',
animationTimingFunction: EASING_FUNCTION_1,
},
horizontalBackward: {
animationName: [FADE_IN, SLIDE_RIGHT_IN20],
},
horizontalForward: {
animationName: [FADE_IN, SLIDE_LEFT_IN20],
},
verticalBackward: {
animationName: [FADE_IN, SLIDE_DOWN_IN20],
},
verticalForward: {
animationName: [FADE_IN, SLIDE_UP_IN20],
},
});

const useWeekDayLabelCellStyles = makeStyles({
base: {
userSelect: 'none',
animationDuration: DURATION_2,
animationFillMode: 'both',
animationName: FADE_IN,
animationTimingFunction: EASING_FUNCTION_2,
},
});

Expand Down Expand Up @@ -318,16 +281,9 @@ const useFirstTransitionWeekStyles = makeStyles({
height: 0,
opacity: 0,
overflow: 'hidden',

position: 'absolute',
width: 0,
},
verticalForward: {
animationDuration: DURATION_3,
animationFillMode: 'both',
animationName: [FADE_OUT, SLIDE_UP_OUT20, TRANSITION_ROW_DISAPPEARANCE],
animationTimingFunction: EASING_FUNCTION_1,
},
});

const useLastTransitionWeekStyles = makeStyles({
Expand All @@ -339,12 +295,6 @@ const useLastTransitionWeekStyles = makeStyles({
position: 'absolute',
width: 0,
},
verticalBackward: {
animationDuration: DURATION_3,
animationFillMode: 'both',
animationName: [FADE_OUT, SLIDE_DOWN_OUT20, TRANSITION_ROW_DISAPPEARANCE],
animationTimingFunction: EASING_FUNCTION_1,
},
});

const useDayMarkerStyles = makeStyles({
Expand Down Expand Up @@ -410,7 +360,7 @@ export const useCalendarDayGridStyles_unstable = (props: CalendarDayGridStylePro
const cornerBorderAndRadiusStyles = useCornerBorderAndRadiusStyles();
const dayTodayMarkerStyles = useDayTodayMarkerStyles();

const { animateBackwards, animationDirection, lightenDaysOutsideNavigatedMonth, showWeekNumbers } = props;
const { lightenDaysOutsideNavigatedMonth, showWeekNumbers } = props;

return {
wrapper: mergeClasses(calendarDayGridClassNames.wrapper, wrapperStyles.base),
Expand All @@ -427,19 +377,7 @@ export const useCalendarDayGridStyles_unstable = (props: CalendarDayGridStylePro
),
daySelected: mergeClasses(calendarDayGridClassNames.daySelected, daySelectedStyles.base),
daySingleSelected: mergeClasses(calendarDayGridClassNames.daySingleSelected, daySingleSelectedStyles.base),
weekRow: mergeClasses(
calendarDayGridClassNames.weekRow,
weekRowStyles.base,
animateBackwards !== undefined && weekRowStyles.animation,
animateBackwards !== undefined &&
(animationDirection === AnimationDirection.Horizontal
? animateBackwards
? weekRowStyles.horizontalBackward
: weekRowStyles.horizontalForward
: animateBackwards
? weekRowStyles.verticalBackward
: weekRowStyles.verticalForward),
),
weekRow: mergeClasses(calendarDayGridClassNames.weekRow, weekRowStyles.base),
weekDayLabelCell: mergeClasses(calendarDayGridClassNames.weekDayLabelCell, weekDayLabelCellStyles.base),
weekNumberCell: mergeClasses(calendarDayGridClassNames.weekNumberCell, weekNumberCellStyles.base),
dayOutsideBounds: mergeClasses(calendarDayGridClassNames.dayOutsideBounds, dayOutsideBoundsStyles.base),
Expand All @@ -449,22 +387,8 @@ export const useCalendarDayGridStyles_unstable = (props: CalendarDayGridStylePro
),
dayButton: mergeClasses(calendarDayGridClassNames.dayButton, dayButtonStyles.base),
dayIsToday: mergeClasses(calendarDayGridClassNames.dayIsToday, dayIsTodayStyles.base),
firstTransitionWeek: mergeClasses(
calendarDayGridClassNames.firstTransitionWeek,
firstTransitionWeekStyles.base,
animateBackwards !== undefined &&
animationDirection !== AnimationDirection.Horizontal &&
!animateBackwards &&
firstTransitionWeekStyles.verticalForward,
),
lastTransitionWeek: mergeClasses(
calendarDayGridClassNames.lastTransitionWeek,
lastTransitionWeekStyles.base,
animateBackwards !== undefined &&
animationDirection !== AnimationDirection.Horizontal &&
animateBackwards &&
lastTransitionWeekStyles.verticalBackward,
),
firstTransitionWeek: mergeClasses(calendarDayGridClassNames.firstTransitionWeek, firstTransitionWeekStyles.base),
lastTransitionWeek: mergeClasses(calendarDayGridClassNames.lastTransitionWeek, lastTransitionWeekStyles.base),
dayMarker: mergeClasses(calendarDayGridClassNames.dayMarker, dayMarkerStyles.base),
dayTodayMarker: mergeClasses(calendarDayGridClassNames.dayTodayMarker, dayTodayMarkerStyles.base),
};
Expand Down
Loading
Loading