Skip to content

Commit 7ede445

Browse files
committed
feat(Rotate): rename story
1 parent cc2af9a commit 7ede445

File tree

3 files changed

+117
-331
lines changed

3 files changed

+117
-331
lines changed
Lines changed: 116 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -1,206 +1,184 @@
11
import * as React from 'react';
2-
import {
3-
makeStyles,
4-
tokens,
5-
Button,
6-
Card,
7-
CardHeader,
8-
CardPreview,
9-
Body1,
10-
Title3,
11-
Caption1,
12-
} from '@fluentui/react-components';
2+
import { makeStyles, tokens, Button, Card, Title3, Body2, Caption1, motionTokens } from '@fluentui/react-components';
133
import { Rotate } from '@fluentui/react-motion-components-preview';
14-
import { Star20Filled, ThumbLike20Filled, Eye20Filled } from '@fluentui/react-icons';
4+
import { RotateParams } from '../../../library/src/components/Rotate/rotate-types';
155

166
const useClasses = makeStyles({
177
container: {
188
display: 'flex',
199
flexDirection: 'column',
2010
gap: '20px',
2111
padding: '20px',
22-
maxWidth: '900px',
12+
maxWidth: '1000px',
13+
perspective: '500px',
2314
},
2415
controls: {
2516
display: 'flex',
2617
gap: '10px',
2718
alignItems: 'center',
19+
flexWrap: 'wrap',
2820
marginBottom: '20px',
2921
},
30-
cardGrid: {
22+
patternsGrid: {
3123
display: 'grid',
32-
gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))',
24+
gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))',
3325
gap: '20px',
34-
perspective: '1000px',
26+
perspective: '800px',
3527
},
36-
cardContainer: {
37-
position: 'relative',
38-
height: '200px',
39-
},
40-
card: {
41-
height: '100%',
28+
patternCard: {
29+
height: '140px',
30+
display: 'flex',
31+
flexDirection: 'column',
32+
alignItems: 'center',
33+
justifyContent: 'center',
34+
gap: tokens.spacingVerticalS,
4235
cursor: 'pointer',
43-
transition: 'transform 0.2s',
36+
transition: 'transform 0.2s ease',
4437
'&:hover': {
4538
transform: 'translateY(-2px)',
46-
boxShadow: tokens.shadow28,
39+
boxShadow: tokens.shadow16,
4740
},
4841
},
49-
cardPreview: {
50-
height: '120px',
51-
display: 'flex',
52-
alignItems: 'center',
53-
justifyContent: 'center',
54-
fontSize: tokens.fontSizeBase600,
55-
position: 'relative',
56-
},
57-
cardBack: {
58-
backgroundColor: tokens.colorNeutralBackground1,
59-
border: `1px solid ${tokens.colorNeutralStroke2}`,
60-
borderRadius: tokens.borderRadiusMedium,
61-
padding: tokens.spacingVerticalM,
62-
height: '100%',
63-
display: 'flex',
64-
flexDirection: 'column',
65-
justifyContent: 'center',
66-
alignItems: 'center',
67-
gap: tokens.spacingVerticalS,
42+
patternTitle: {
43+
color: tokens.colorNeutralForeground1,
6844
},
69-
stats: {
70-
display: 'flex',
71-
gap: tokens.spacingHorizontalS,
72-
alignItems: 'center',
45+
patternDescription: {
46+
color: tokens.colorNeutralForeground2,
47+
textAlign: 'center',
7348
},
74-
statItem: {
49+
demoIcon: {
50+
width: '48px',
51+
height: '48px',
52+
borderRadius: tokens.borderRadiusMedium,
7553
display: 'flex',
7654
alignItems: 'center',
77-
gap: tokens.spacingHorizontalXS,
55+
justifyContent: 'center',
56+
fontSize: '24px',
57+
fontWeight: 'bold',
58+
color: tokens.colorNeutralForegroundOnBrand,
7859
},
7960
});
8061

81-
const cardData = [
62+
const curveSpringRelaxed = `linear(0.000 0.000%, 0.06073 1.000%, 0.1215 2.000%, 0.1822 3.000%, 0.2429 4.000%, 0.3036 5.000%, 0.3644 6.000%, 0.4251 7.000%, 0.4858 8.000%, 0.5465 9.000%, 0.6073 10.00%, 0.6680 11.00%, 0.7287 12.00%, 0.7895 13.00%, 0.8502 14.00%, 0.9109 15.00%, 0.9716 16.00%, 1.031 17.00%, 1.085 18.00%, 1.131 19.00%, 1.168 20.00%, 1.198 21.00%, 1.220 22.00%, 1.234 23.00%, 1.241 24.00%, 1.242 25.00%, 1.236 26.00%, 1.226 27.00%, 1.211 28.00%, 1.192 29.00%, 1.171 30.00%, 1.148 31.00%, 1.124 32.00%, 1.099 33.00%, 1.074 34.00%, 1.050 35.00%, 1.028 36.00%, 1.007 37.00%, 0.9880 38.00%, 0.9714 39.00%, 0.9572 40.00%, 0.9455 41.00%, 0.9364 42.00%, 0.9298 43.00%, 0.9255 44.00%, 0.9235 45.00%, 0.9236 46.00%, 0.9255 47.00%, 0.9291 48.00%, 0.9339 49.00%, 0.9399 50.00%, 0.9467 51.00%, 0.9541 52.00%, 0.9618 53.00%, 0.9697 54.00%, 0.9774 55.00%, 0.9849 56.00%, 0.9920 57.00%, 0.9986 58.00%, 1.004 59.00%, 1.010 60.00%, 1.014 61.00%, 1.018 62.00%, 1.020 63.00%, 1.022 64.00%, 1.024 65.00%, 1.024 66.00%, 1.024 67.00%, 1.023 68.00%, 1.022 69.00%, 1.021 70.00%, 1.019 71.00%, 1.017 72.00%, 1.014 73.00%, 1.012 74.00%, 1.009 75.00%, 1.007 76.00%, 1.004 77.00%, 1.002 78.00%, 1.000 79.00%, 0.9984 80.00%, 0.9968 81.00%, 0.9954 82.00%, 0.9943 83.00%, 0.9935 84.00%, 0.9929 85.00%, 0.9925 86.00%, 0.9923 87.00%, 0.9924 88.00%, 0.9926 89.00%, 0.9930 90.00%, 0.9935 91.00%, 0.9941 92.00%, 0.9948 93.00%, 0.9956 94.00%, 0.9964 95.00%, 0.9972 96.00%, 0.9979 97.00%, 0.9987 98.00%, 0.9994 99.00%, 1.000 100.0%)`;
63+
64+
type RotatePattern = {
65+
id: string;
66+
name: string;
67+
description: string;
68+
icon: string;
69+
color: string;
70+
axis: Required<RotateParams['axis']>;
71+
angle: Required<RotateParams['angle']>;
72+
duration: Required<RotateParams['duration']>;
73+
easing: Required<RotateParams['easing']>;
74+
exitEasing: Required<RotateParams['easing']>;
75+
exitDuration: Required<RotateParams['duration']>;
76+
};
77+
78+
const patterns: RotatePattern[] = [
8279
{
83-
id: 1,
84-
title: 'Design System',
85-
subtitle: 'Fluent UI Components',
86-
color: tokens.colorPaletteBlueBackground2,
87-
stats: { likes: 324, views: 1205, rating: 4.8 },
88-
description: 'A comprehensive design system for modern applications',
80+
id: 'flip-horizontal',
81+
name: 'Horizontal Flip',
82+
description: 'Y-axis rotation',
83+
icon: '↔️',
84+
color: tokens.colorPaletteBlueForeground2,
85+
axis: 'y',
86+
angle: 180,
87+
easing: curveSpringRelaxed,
88+
exitEasing: motionTokens.curveDecelerateMid,
89+
duration: 2000,
90+
exitDuration: motionTokens.durationUltraSlow,
8991
},
9092
{
91-
id: 2,
92-
title: 'React Motion',
93-
subtitle: 'Animation Library',
94-
color: tokens.colorPaletteGreenBackground2,
95-
stats: { likes: 156, views: 892, rating: 4.6 },
96-
description: 'Smooth and performant animations for React components',
93+
id: 'flip-vertical',
94+
name: 'Vertical Flip',
95+
description: 'X-axis rotation',
96+
icon: '↕️',
97+
color: tokens.colorPaletteGreenForeground2,
98+
axis: 'x',
99+
angle: 180,
100+
easing: curveSpringRelaxed,
101+
exitEasing: motionTokens.curveDecelerateMid,
102+
duration: 2000,
103+
exitDuration: motionTokens.durationUltraSlow,
97104
},
98105
{
99-
id: 3,
100-
title: 'TypeScript',
101-
subtitle: 'Type Safety',
102-
color: tokens.colorPalettePurpleBackground2,
103-
stats: { likes: 445, views: 2103, rating: 4.9 },
104-
description: 'Strongly typed JavaScript for better development experience',
106+
id: 'spin',
107+
name: 'Spin',
108+
description: 'Z-axis rotation',
109+
icon: '🔄',
110+
color: tokens.colorPaletteRedForeground2,
111+
axis: 'z',
112+
angle: 180,
113+
easing: curveSpringRelaxed,
114+
exitEasing: motionTokens.curveDecelerateMid,
115+
duration: 2000,
116+
exitDuration: motionTokens.durationUltraSlow,
105117
},
106118
];
107119

108120
export const CardFlip = () => {
109121
const classes = useClasses();
110-
const [flippedCards, setFlippedCards] = React.useState<Set<number>>(new Set());
111-
const [autoFlip, setAutoFlip] = React.useState(false);
122+
const [activePatterns, setActivePatterns] = React.useState<Set<string>>(new Set(patterns.map(p => p.id)));
112123

113-
const toggleCard = (cardId: number) => {
114-
setFlippedCards(prev => {
124+
const togglePattern = (patternId: string) => {
125+
setActivePatterns(prev => {
115126
const newSet = new Set(prev);
116-
if (newSet.has(cardId)) {
117-
newSet.delete(cardId);
127+
if (newSet.has(patternId)) {
128+
newSet.delete(patternId);
118129
} else {
119-
newSet.add(cardId);
130+
newSet.add(patternId);
120131
}
121132
return newSet;
122133
});
123134
};
124135

125-
const flipAllCards = () => {
126-
if (flippedCards.size === cardData.length) {
127-
setFlippedCards(new Set());
136+
const toggleAllPatterns = () => {
137+
if (activePatterns.size === patterns.length) {
138+
// All are showing, so hide all
139+
setActivePatterns(new Set());
128140
} else {
129-
setFlippedCards(new Set(cardData.map(card => card.id)));
141+
// Some or none are showing, so show all
142+
setActivePatterns(new Set(patterns.map(p => p.id)));
130143
}
131144
};
132145

133-
React.useEffect(() => {
134-
if (!autoFlip) return;
135-
136-
const interval = setInterval(() => {
137-
const randomCard = cardData[Math.floor(Math.random() * cardData.length)];
138-
toggleCard(randomCard.id);
139-
}, 1500);
140-
141-
return () => clearInterval(interval);
142-
}, [autoFlip]);
146+
const allPatternsVisible = activePatterns.size === patterns.length;
143147

144148
return (
145149
<div className={classes.container}>
146150
<div className={classes.controls}>
147-
<Button appearance="primary" onClick={flipAllCards}>
148-
{flippedCards.size === cardData.length ? 'Flip All Back' : 'Flip All Cards'}
149-
</Button>
150-
<Button appearance={autoFlip ? 'primary' : 'secondary'} onClick={() => setAutoFlip(!autoFlip)}>
151-
{autoFlip ? 'Stop Auto Flip' : 'Auto Flip'}
152-
</Button>
151+
<Button onClick={toggleAllPatterns}>{allPatternsVisible ? 'Hide All' : 'Show All'}</Button>
153152
</div>
154153

155-
<div className={classes.cardGrid}>
156-
{cardData.map(card => (
157-
<div key={card.id} className={classes.cardContainer}>
158-
<Rotate
159-
visible={!flippedCards.has(card.id)}
160-
axis="Y"
161-
angle={0}
162-
exitAngle={180}
163-
duration={600}
164-
easing="cubic-bezier(0.4, 0, 0.2, 1)"
165-
>
166-
<Card className={classes.card} onClick={() => toggleCard(card.id)}>
167-
<CardPreview className={classes.cardPreview} style={{ backgroundColor: card.color }}>
168-
<Title3>{card.title}</Title3>
169-
</CardPreview>
170-
<CardHeader header={<Body1>{card.title}</Body1>} description={<Caption1>{card.subtitle}</Caption1>} />
171-
</Card>
172-
</Rotate>
173-
154+
<div className={classes.patternsGrid}>
155+
{patterns.map(pattern => (
156+
<div key={pattern.id}>
174157
<Rotate
175-
visible={flippedCards.has(card.id)}
176-
axis="Y"
177-
angle={-180}
178-
exitAngle={0}
179-
duration={600}
180-
easing="cubic-bezier(0.4, 0, 0.2, 1)"
158+
visible={activePatterns.has(pattern.id)}
159+
axis={pattern.axis}
160+
angle={pattern.angle}
161+
duration={pattern.duration}
162+
easing={pattern.easing}
163+
exitEasing={pattern.exitEasing}
164+
exitDuration={pattern.exitDuration}
165+
animateOpacity={false}
181166
>
182-
<div className={classes.cardBack} onClick={() => toggleCard(card.id)}>
183-
<Title3>{card.title}</Title3>
184-
<Body1 style={{ textAlign: 'center', margin: tokens.spacingVerticalM }}>{card.description}</Body1>
185-
<div className={classes.stats}>
186-
<div className={classes.statItem}>
187-
<ThumbLike20Filled color={tokens.colorPaletteRedForeground2} />
188-
<Caption1>{card.stats.likes}</Caption1>
189-
</div>
190-
<div className={classes.statItem}>
191-
<Eye20Filled color={tokens.colorPaletteBlueForeground2} />
192-
<Caption1>{card.stats.views}</Caption1>
193-
</div>
194-
<div className={classes.statItem}>
195-
<Star20Filled color={tokens.colorPaletteYellowForeground2} />
196-
<Caption1>{card.stats.rating}</Caption1>
197-
</div>
167+
<Card className={classes.patternCard} onClick={() => togglePattern(pattern.id)}>
168+
<div className={classes.demoIcon} style={{ backgroundColor: pattern.color }}>
169+
{pattern.icon}
198170
</div>
199-
</div>
171+
<Title3 className={classes.patternTitle}>{pattern.name}</Title3>
172+
<Caption1 className={classes.patternDescription}>{pattern.description}</Caption1>
173+
</Card>
200174
</Rotate>
201175
</div>
202176
))}
203177
</div>
178+
179+
<Body2 style={{ textAlign: 'center', color: tokens.colorNeutralForeground2, marginTop: '20px' }}>
180+
Click any pattern to see its rotation effect, or use the controls above to see them all in sequence
181+
</Body2>
204182
</div>
205183
);
206184
};
@@ -209,7 +187,7 @@ CardFlip.parameters = {
209187
docs: {
210188
description: {
211189
story:
212-
'Demonstrates card flip animations using Y-axis rotation. Click cards to flip them and reveal additional information on the back.',
190+
'A collection of common single-axis rotation patterns that you can use as starting points for your own animations. Each pattern demonstrates rotation around a specific axis (X, Y, or Z) with spring-relaxed easing.',
213191
},
214192
},
215193
};

0 commit comments

Comments
 (0)