Skip to content

Commit 55ec1e1

Browse files
committed
change
1 parent fdb37d5 commit 55ec1e1

File tree

4 files changed

+55
-42
lines changed

4 files changed

+55
-42
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "patch",
3+
"comment": "fix: fix active tab indicator animation",
4+
"packageName": "@fluentui/react-tabs",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch"
7+
}

packages/react-components/react-tabs/library/src/components/Tab/useTabAnimatedIndicator.styles.ts

+24-26
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { makeStyles, mergeClasses } from '@griffel/react';
55
import { useTabListContext_unstable } from '../TabList/TabListContext';
66
import { TabRegisterData } from '../TabList/TabList.types';
77
import { tokens } from '@fluentui/react-theme';
8+
import { useAnimationFrame } from '@fluentui/react-utilities';
89

910
// eslint-disable-next-line @typescript-eslint/naming-convention
1011
const tabIndicatorCssVars_unstable = {
@@ -80,39 +81,36 @@ export const useTabAnimatedIndicatorStyles_unstable = (state: TabState): TabStat
8081
const [animationValues, setAnimationValues] = React.useState({ offset: 0, scale: 1 });
8182
const getRegisteredTabs = useTabListContext_unstable(ctx => ctx.getRegisteredTabs);
8283

83-
React.useEffect(() => {
84-
if (isValueDefined(lastAnimatedFrom)) {
85-
setAnimationValues({ offset: 0, scale: 1 });
86-
}
87-
}, [lastAnimatedFrom]);
84+
const [requestAnimationFrame] = useAnimationFrame();
85+
86+
if (selected) {
87+
const { previousSelectedValue, selectedValue, registeredTabs } = getRegisteredTabs();
8888

89-
React.useEffect(() => {
90-
if (selected) {
91-
const { previousSelectedValue, selectedValue, registeredTabs } = getRegisteredTabs();
89+
if (isValueDefined(previousSelectedValue) && lastAnimatedFrom !== previousSelectedValue) {
90+
const previousSelectedTabRect = getRegisteredTabRect(registeredTabs, previousSelectedValue);
91+
const selectedTabRect = getRegisteredTabRect(registeredTabs, selectedValue);
9292

93-
if (isValueDefined(previousSelectedValue) && lastAnimatedFrom !== previousSelectedValue) {
94-
const previousSelectedTabRect = getRegisteredTabRect(registeredTabs, previousSelectedValue);
95-
const selectedTabRect = getRegisteredTabRect(registeredTabs, selectedValue);
93+
if (selectedTabRect && previousSelectedTabRect) {
94+
const offset = vertical
95+
? previousSelectedTabRect.y - selectedTabRect.y
96+
: previousSelectedTabRect.x - selectedTabRect.x;
9697

97-
if (selectedTabRect && previousSelectedTabRect) {
98-
const offset = vertical
99-
? previousSelectedTabRect.y - selectedTabRect.y
100-
: previousSelectedTabRect.x - selectedTabRect.x;
98+
const scale = vertical
99+
? previousSelectedTabRect.height / selectedTabRect.height
100+
: previousSelectedTabRect.width / selectedTabRect.width;
101101

102-
const scale = vertical
103-
? previousSelectedTabRect.height / selectedTabRect.height
104-
: previousSelectedTabRect.width / selectedTabRect.width;
102+
setAnimationValues({ offset, scale });
103+
setLastAnimatedFrom(previousSelectedValue);
105104

106-
setAnimationValues({ offset, scale });
107-
setLastAnimatedFrom(previousSelectedValue);
108-
}
105+
// Reset the animation values after the animation is complete
106+
requestAnimationFrame(() => setAnimationValues({ offset: 0, scale: 1 }));
109107
}
110-
} else if (isValueDefined(lastAnimatedFrom)) {
111-
// need to clear the last animated from so that if this tab is selected again
112-
// from the same previous tab as last time, that animation still happens.
113-
setLastAnimatedFrom(undefined);
114108
}
115-
}, [selected, getRegisteredTabs, lastAnimatedFrom, vertical]);
109+
} else if (isValueDefined(lastAnimatedFrom)) {
110+
// need to clear the last animated from so that if this tab is selected again
111+
// from the same previous tab as last time, that animation still happens.
112+
setLastAnimatedFrom(undefined);
113+
}
116114

117115
// do not apply any animation if the tab is disabled
118116
if (disabled) {

packages/react-components/react-tabs/stories/src/Tabs/TabListDefault.stories.tsx

+12-4
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,18 @@ export const Default = (props: Partial<TabListProps>) => {
1919
return (
2020
<div className={styles.root}>
2121
<TabList {...props}>
22-
<Tab value="tab1">First Tab</Tab>
23-
<Tab value="tab2">Second Tab</Tab>
24-
<Tab value="tab3">Third Tab</Tab>
25-
<Tab value="tab4">Fourth Tab</Tab>
22+
<Tab value="tab1" key="tab1">
23+
First Tab
24+
</Tab>
25+
<Tab value="tab2" key="tab2">
26+
Second Tab
27+
</Tab>
28+
<Tab value="tab3" key="tab3">
29+
Third Tab
30+
</Tab>
31+
<Tab value="tab4" key="tab4">
32+
Fourth Tab
33+
</Tab>
2634
</TabList>
2735
</div>
2836
);

packages/react-components/react-tabs/stories/src/Tabs/index.stories.tsx

+12-12
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,18 @@ import descriptionMd from './TabListDescription.md';
44
import bestPracticesMd from './TabListBestPractices.md';
55

66
export { Default } from './TabListDefault.stories';
7-
export { Horizontal } from './TabListHorizontal.stories';
8-
export { Vertical } from './TabListVertical.stories';
9-
export { Appearance } from './TabListAppearance.stories';
10-
export { Disabled } from './TabListDisabled.stories';
11-
export { SizeSmall } from './TabListSizeSmall.stories';
12-
export { SizeMedium } from './TabListSizeMedium.stories';
13-
export { SizeLarge } from './TabListSizeLarge.stories';
14-
export { WithIcon } from './TabListWithIcon.stories';
15-
export { IconOnly } from './TabListIconOnly.stories';
16-
export { SelectTabOnFocus } from './TabListSelectTabOnFocus.stories';
17-
export { WithOverflow } from './TabListWithOverflow.stories';
18-
export { WithPanels } from './TabListWithPanels.stories';
7+
// export { Horizontal } from './TabListHorizontal.stories';
8+
// export { Vertical } from './TabListVertical.stories';
9+
// export { Appearance } from './TabListAppearance.stories';
10+
// export { Disabled } from './TabListDisabled.stories';
11+
// export { SizeSmall } from './TabListSizeSmall.stories';
12+
// export { SizeMedium } from './TabListSizeMedium.stories';
13+
// export { SizeLarge } from './TabListSizeLarge.stories';
14+
// export { WithIcon } from './TabListWithIcon.stories';
15+
// export { IconOnly } from './TabListIconOnly.stories';
16+
// export { SelectTabOnFocus } from './TabListSelectTabOnFocus.stories';
17+
// export { WithOverflow } from './TabListWithOverflow.stories';
18+
// export { WithPanels } from './TabListWithPanels.stories';
1919

2020
export default {
2121
title: 'Components/TabList',

0 commit comments

Comments
 (0)