Skip to content

Commit ed173f3

Browse files
committed
docs(Rotate): improve layout of animation controls
1 parent 6a93fc4 commit ed173f3

File tree

1 file changed

+128
-70
lines changed

1 file changed

+128
-70
lines changed

packages/react-components/react-motion-components-preview/stories/src/Rotate/RotateDefault.stories.tsx

Lines changed: 128 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
RadioGroup,
1111
Radio,
1212
motionTokens,
13+
Button,
1314
} from '@fluentui/react-components';
1415
import { Rotate } from '@fluentui/react-motion-components-preview';
1516
import { Axis3D } from '../../../library/src/atoms/rotate-atom';
@@ -20,30 +21,68 @@ const useClasses = makeStyles({
2021
container: {
2122
display: 'grid',
2223
gridTemplate: `"controls ." "card card" / 1fr 1fr`,
23-
gap: `${tokens.spacingVerticalXL} ${tokens.spacingHorizontalMNudge}`, // 20px 10px
24-
// perspective: '1000px',
24+
gap: `${tokens.spacingVerticalXL} ${tokens.spacingHorizontalMNudge}`,
2525
overflow: 'clip',
2626
},
2727
card: {
2828
gridArea: 'card',
29-
padding: tokens.spacingHorizontalMNudge, // 10px
29+
padding: tokens.spacingHorizontalMNudge,
3030
},
3131
controls: {
3232
display: 'flex',
3333
flexDirection: 'column',
3434
gridArea: 'controls',
35-
3635
border: `${tokens.strokeWidthThicker} solid ${tokens.colorNeutralForeground3}`,
3736
borderRadius: tokens.borderRadiusMedium,
3837
boxShadow: tokens.shadow16,
39-
padding: tokens.spacingHorizontalMNudge, // 10px
38+
padding: tokens.spacingHorizontalL,
39+
gap: tokens.spacingVerticalL,
4040
},
41-
field: {
41+
controlSection: {
42+
display: 'flex',
43+
flexDirection: 'column',
44+
gap: tokens.spacingVerticalM,
45+
},
46+
sectionHeader: {
47+
fontSize: tokens.fontSizeBase200,
48+
fontWeight: tokens.fontWeightSemibold,
49+
color: tokens.colorNeutralForeground2,
50+
marginBottom: tokens.spacingVerticalXS,
51+
borderBottom: `1px solid ${tokens.colorNeutralStroke2}`,
52+
paddingBottom: tokens.spacingVerticalXS,
53+
},
54+
toggleGroup: {
55+
display: 'grid',
56+
gridTemplateColumns: '1fr 1fr',
57+
gap: tokens.spacingHorizontalM,
58+
},
59+
ctaButton: {
4260
flex: 1,
4361
},
62+
field: {
63+
display: 'flex',
64+
flexDirection: 'column',
65+
gap: tokens.spacingVerticalXS,
66+
},
67+
sliderField: {
68+
display: 'flex',
69+
flexDirection: 'column',
70+
gap: tokens.spacingVerticalXS,
71+
},
4472
sliderWrapper: {
4573
display: 'flex',
4674
alignItems: 'center',
75+
gap: tokens.spacingHorizontalS,
76+
},
77+
sliderLabel: {
78+
fontSize: tokens.fontSizeBase200,
79+
fontWeight: tokens.fontWeightMedium,
80+
color: tokens.colorNeutralForeground1,
81+
},
82+
valueDisplay: {
83+
fontSize: tokens.fontSizeBase100,
84+
color: tokens.colorNeutralForeground2,
85+
fontFamily: tokens.fontFamilyMonospace,
4786
},
4887
});
4988

@@ -78,75 +117,94 @@ export const Default = (props: React.ComponentProps<typeof Rotate>) => {
78117
return (
79118
<div className={classes.container} style={{ perspective }}>
80119
<div className={classes.controls}>
81-
<Field className={classes.field}>
82-
<Switch label="Visible" checked={visible} onChange={() => setVisible(v => !v)} />
83-
</Field>
84-
85-
<Field className={classes.field}>
86-
<Switch
87-
label="Autoplay"
88-
checked={autoplay}
89-
onChange={() => {
90-
if (!autoplay) {
91-
setVisible(!visible);
92-
}
93-
return setAutoplay(v => !v);
94-
}}
95-
/>
96-
</Field>
120+
{/* Animation Controls Section */}
121+
<div className={classes.controlSection}>
122+
<div className={classes.toggleGroup}>
123+
<Button className={classes.ctaButton} appearance="primary" size="large" onClick={() => setVisible(v => !v)}>
124+
{visible ? 'Hide' : 'Show'}
125+
</Button>
126+
<Field>
127+
<Switch
128+
label="Autoplay"
129+
checked={autoplay}
130+
onChange={() => {
131+
if (!autoplay) {
132+
setVisible(!visible);
133+
}
134+
return setAutoplay(v => !v);
135+
}}
136+
/>
137+
</Field>
138+
</div>
139+
</div>
97140

98-
<Field className={classes.field}>
99-
<Label>Rotation Axis</Label>
100-
<RadioGroup value={axis} onChange={(_, data) => setAxis(data.value as Axis3D)} layout="horizontal">
101-
<Radio value="x" label="x" />
102-
<Radio value="y" label="y" />
103-
<Radio value="z" label="z" />
104-
</RadioGroup>
105-
</Field>
141+
{/* Rotation Settings Section */}
142+
<div className={classes.controlSection}>
143+
<Field className={classes.field}>
144+
<Label className={classes.sliderLabel}>Rotation Axis</Label>
145+
<RadioGroup value={axis} onChange={(_, data) => setAxis(data.value as Axis3D)} layout="horizontal">
146+
<Radio value="x" label="X-axis" />
147+
<Radio value="y" label="Y-axis" />
148+
<Radio value="z" label="Z-axis" />
149+
</RadioGroup>
150+
</Field>
106151

107-
<label htmlFor={enterAngleSliderId}>Enter Angle: {angle}°</label>
108-
<div className={classes.sliderWrapper}>
109-
<Label aria-hidden>{angleMin}</Label>
110-
<Slider
111-
min={angleMin}
112-
max={angleMax}
113-
defaultValue={angle}
114-
id={enterAngleSliderId}
115-
onChange={(_, data) => {
116-
setEnterAngle(data.value);
117-
}}
118-
/>
119-
<Label aria-hidden>{angleMax}</Label>
152+
<Field className={classes.sliderField}>
153+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
154+
<Label htmlFor={enterAngleSliderId} className={classes.sliderLabel}>
155+
Enter Angle
156+
</Label>
157+
<span className={classes.valueDisplay}>{angle}°</span>
158+
</div>
159+
<Slider
160+
min={angleMin}
161+
max={angleMax}
162+
defaultValue={angle}
163+
id={enterAngleSliderId}
164+
onChange={(_, data) => {
165+
setEnterAngle(data.value);
166+
}}
167+
/>
168+
</Field>
120169
</div>
121170

122-
<Label htmlFor={durationSliderId}>duration: {duration}</Label>
123-
<div className={classes.sliderWrapper}>
124-
<Label aria-hidden>{durationMin}</Label>
125-
<Slider
126-
min={durationMin}
127-
max={durationMax}
128-
defaultValue={motionTokens.durationUltraSlow} // 500ms
129-
id={durationSliderId}
130-
onChange={(_, data) => {
131-
setDuration(data.value);
132-
}}
133-
/>
134-
<Label aria-hidden>{durationMax}</Label>
135-
</div>
171+
{/* Timing & Perspective Section */}
172+
<div className={classes.controlSection}>
173+
<Field className={classes.sliderField}>
174+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
175+
<Label htmlFor={durationSliderId} className={classes.sliderLabel}>
176+
Duration
177+
</Label>
178+
<span className={classes.valueDisplay}>{duration}ms</span>
179+
</div>
180+
<Slider
181+
min={durationMin}
182+
max={durationMax}
183+
defaultValue={motionTokens.durationUltraSlow}
184+
id={durationSliderId}
185+
onChange={(_, data) => {
186+
setDuration(data.value);
187+
}}
188+
/>
189+
</Field>
136190

137-
<Label htmlFor={perspectiveSliderId}>perspective: {perspective}</Label>
138-
<div className={classes.sliderWrapper}>
139-
<Label aria-hidden>{perspectiveMin}</Label>
140-
<Slider
141-
min={perspectiveMin}
142-
max={perspectiveMax}
143-
defaultValue={1000}
144-
id={perspectiveSliderId}
145-
onChange={(_, data) => {
146-
setPerspective(`${data.value}px`);
147-
}}
148-
/>
149-
<Label aria-hidden>{perspectiveMax}</Label>
191+
<Field className={classes.sliderField}>
192+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
193+
<Label htmlFor={perspectiveSliderId} className={classes.sliderLabel}>
194+
Perspective
195+
</Label>
196+
<span className={classes.valueDisplay}>{perspective}</span>
197+
</div>
198+
<Slider
199+
min={perspectiveMin}
200+
max={perspectiveMax}
201+
defaultValue={1000}
202+
id={perspectiveSliderId}
203+
onChange={(_, data) => {
204+
setPerspective(`${data.value}px`);
205+
}}
206+
/>
207+
</Field>
150208
</div>
151209
</div>
152210

0 commit comments

Comments
 (0)