-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(motion): simplify variant creation w/functional approach, st…
…arting with Collapse
- Loading branch information
1 parent
2137714
commit 915f1e2
Showing
3 changed files
with
70 additions
and
42 deletions.
There are no files selected for viewing
104 changes: 62 additions & 42 deletions
104
...ct-components/react-motion-components-preview/library/src/components/Collapse/Collapse.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,49 +1,69 @@ | ||
import { | ||
motionTokens, | ||
type PresenceMotionFn, | ||
createPresenceComponent, | ||
createPresenceComponentVariant, | ||
} from '@fluentui/react-motion'; | ||
import { motionTokens, createPresenceComponent } from '@fluentui/react-motion'; | ||
import type { PresenceMotionFnCreator } from '../../types'; | ||
|
||
const { durationNormal, durationSlower, durationFast, curveEasyEaseMax } = motionTokens; | ||
|
||
type CollapseVariantParams = { | ||
/** Time (ms) for the enter transition (expand). Defaults to the `durationNormal` value (200 ms). */ | ||
enterDuration?: number; | ||
|
||
/** Time (ms) for the exit transition (collapse). Defaults to the `durationNormal` value (200 ms). */ | ||
exitDuration?: number; | ||
|
||
/** Easing curve for the enter transition (expand). Defaults to the `easeEaseMax` value. */ | ||
enterEasing?: string; | ||
|
||
/** Easing curve for the exit transition (expand). Defaults to the `easeEaseMax` value. */ | ||
exitEasing?: string; | ||
}; | ||
|
||
type CollapseRuntimeParams = { | ||
/** Whether to animate the opacity. Defaults to `true`. */ | ||
animateOpacity?: boolean; | ||
}; | ||
|
||
/** Define a presence motion for collapse/expand */ | ||
const collapseMotion: PresenceMotionFn<{ animateOpacity?: boolean }> = ({ element, animateOpacity = true }) => { | ||
const fromOpacity = animateOpacity ? 0 : 1; | ||
const toOpacity = 1; | ||
const fromHeight = '0'; // Could be a custom param in the future: start partially expanded | ||
const toHeight = `${element.scrollHeight}px`; | ||
const overflow = 'hidden'; | ||
|
||
const duration = motionTokens.durationNormal; | ||
const easing = motionTokens.curveEasyEaseMax; | ||
|
||
const enterKeyframes = [ | ||
{ opacity: fromOpacity, maxHeight: fromHeight, overflow }, | ||
// Transition to the height of the content, at 99.99% of the duration. | ||
{ opacity: toOpacity, maxHeight: toHeight, offset: 0.9999, overflow }, | ||
// On completion, remove the maxHeight because the content might need to expand later. | ||
// This extra keyframe is simpler than firing a callback on completion. | ||
{ opacity: toOpacity, maxHeight: 'unset', overflow }, | ||
]; | ||
|
||
const exitKeyframes = [ | ||
{ opacity: toOpacity, maxHeight: toHeight, overflow }, | ||
{ opacity: fromOpacity, maxHeight: fromHeight, overflow }, | ||
]; | ||
|
||
return { | ||
enter: { duration, easing, keyframes: enterKeyframes }, | ||
exit: { duration, easing, keyframes: exitKeyframes }, | ||
export const createCollapsePresence: PresenceMotionFnCreator<CollapseVariantParams, CollapseRuntimeParams> = | ||
({ | ||
enterDuration = durationNormal, | ||
exitDuration = durationNormal, | ||
enterEasing = curveEasyEaseMax, | ||
exitEasing = curveEasyEaseMax, | ||
} = {}) => | ||
({ element, animateOpacity = true }) => { | ||
const fromOpacity = animateOpacity ? 0 : 1; | ||
const toOpacity = 1; | ||
const fromHeight = '0'; // Could be a custom param in the future: start partially expanded | ||
const toHeight = `${element.scrollHeight}px`; | ||
const overflow = 'hidden'; | ||
|
||
const enterKeyframes = [ | ||
{ opacity: fromOpacity, maxHeight: fromHeight, overflow }, | ||
// Transition to the height of the content, at 99.99% of the duration. | ||
{ opacity: toOpacity, maxHeight: toHeight, offset: 0.9999, overflow }, | ||
// On completion, remove the maxHeight because the content might need to expand later. | ||
// This extra keyframe is simpler than firing a callback on completion. | ||
{ opacity: toOpacity, maxHeight: 'unset', overflow }, | ||
]; | ||
|
||
const exitKeyframes = [ | ||
{ opacity: toOpacity, maxHeight: toHeight, overflow }, | ||
{ opacity: fromOpacity, maxHeight: fromHeight, overflow }, | ||
]; | ||
|
||
return { | ||
enter: { enterDuration, enterEasing, keyframes: enterKeyframes }, | ||
exit: { exitDuration, exitEasing, keyframes: exitKeyframes }, | ||
}; | ||
}; | ||
}; | ||
|
||
/** A React component that applies collapse/expand transitions to its children. */ | ||
export const Collapse = createPresenceComponent(collapseMotion); | ||
export const Collapse = createPresenceComponent(createCollapsePresence()); | ||
|
||
export const CollapseSnappy = createPresenceComponentVariant(Collapse, { | ||
all: { duration: motionTokens.durationUltraFast }, | ||
}); | ||
export const CollapseSnappy = createPresenceComponent( | ||
createCollapsePresence({ enterDuration: durationFast, exitDuration: durationFast }), | ||
); | ||
|
||
export const CollapseExaggerated = createPresenceComponentVariant(Collapse, { | ||
enter: { duration: motionTokens.durationSlow, easing: motionTokens.curveEasyEaseMax }, | ||
exit: { duration: motionTokens.durationNormal, easing: motionTokens.curveEasyEaseMax }, | ||
}); | ||
export const CollapseExaggerated = createPresenceComponent( | ||
createCollapsePresence({ enterDuration: durationSlower, exitDuration: durationNormal }), | ||
); |
7 changes: 7 additions & 0 deletions
7
packages/react-components/react-motion-components-preview/library/src/types.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import type { MotionParam, PresenceMotionFn } from '@fluentui/react-motion'; | ||
|
||
// TODO: move to @fluentui/react-motion when stable | ||
export type PresenceMotionFnCreator< | ||
MotionVariantParams extends Record<string, MotionParam> = {}, | ||
MotionRuntimeParams extends Record<string, MotionParam> = {}, | ||
> = (variantParams?: MotionVariantParams) => PresenceMotionFn<MotionRuntimeParams>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters