From 9366d3548ae7a8711702b6b8d46493fc0ed86ee8 Mon Sep 17 00:00:00 2001 From: Robert Penner Date: Thu, 12 Dec 2024 22:31:00 +0000 Subject: [PATCH 1/4] refactor(Collapse): streamline atom functions --- .../src/components/Collapse/Collapse.ts | 56 +------ .../src/components/Collapse/collapse-atoms.ts | 143 ++++++------------ 2 files changed, 54 insertions(+), 145 deletions(-) diff --git a/packages/react-components/react-motion-components-preview/library/src/components/Collapse/Collapse.ts b/packages/react-components/react-motion-components-preview/library/src/components/Collapse/Collapse.ts index 6aa4b7bb66d5d..601022d2b57c2 100644 --- a/packages/react-components/react-motion-components-preview/library/src/components/Collapse/Collapse.ts +++ b/packages/react-components/react-motion-components-preview/library/src/components/Collapse/Collapse.ts @@ -1,14 +1,7 @@ import { motionTokens, createPresenceComponent, AtomMotion } from '@fluentui/react-motion'; import type { PresenceMotionFnCreator } from '../../types'; import type { CollapseDelayedVariantParams, CollapseRuntimeParams, CollapseVariantParams } from './collapse-types'; -import { - sizeEnterAtom, - whitespaceEnterAtom, - opacityEnterAtom, - opacityExitAtom, - sizeExitAtom, - whitespaceExitAtom, -} from './collapse-atoms'; +import { sizeEnterAtom, opacityAtom, whitespaceAtom, sizeExitAtom } from './collapse-atoms'; /** Define a presence motion for collapse/expand that can stagger the size and opacity motions by a given delay. */ export const createCollapseDelayedPresence: PresenceMotionFnCreator< @@ -32,27 +25,12 @@ export const createCollapseDelayedPresence: PresenceMotionFnCreator< // ----- ENTER ----- // The enter transition is an array of up to 3 motion atoms: size, whitespace and opacity. const enterAtoms: AtomMotion[] = [ - sizeEnterAtom({ - orientation, - duration: enterSizeDuration, - easing: enterEasing, - element, - }), - whitespaceEnterAtom({ - orientation, - duration: enterSizeDuration, - easing: enterEasing, - }), + sizeEnterAtom(orientation, enterSizeDuration, enterEasing, element), + whitespaceAtom('enter', orientation, enterSizeDuration, enterEasing), ]; // Fade in only if animateOpacity is true. Otherwise, leave opacity unaffected. if (animateOpacity) { - enterAtoms.push( - opacityEnterAtom({ - duration: enterOpacityDuration, - easing: enterEasing, - delay: enterDelay, - }), - ); + enterAtoms.push({ ...opacityAtom('enter', enterOpacityDuration, enterEasing), delay: enterDelay, fill: 'both' }); } // ----- EXIT ----- @@ -60,30 +38,10 @@ export const createCollapseDelayedPresence: PresenceMotionFnCreator< const exitAtoms: AtomMotion[] = []; // Fade out only if animateOpacity is true. Otherwise, leave opacity unaffected. if (animateOpacity) { - exitAtoms.push( - opacityExitAtom({ - duration: exitOpacityDuration, - easing: exitEasing, - }), - ); + exitAtoms.push(opacityAtom('exit', exitOpacityDuration, exitEasing)); } - exitAtoms.push( - sizeExitAtom({ - orientation, - duration: exitSizeDuration, - easing: exitEasing, - element, - delay: exitDelay, - }), - ); - exitAtoms.push( - whitespaceExitAtom({ - orientation, - duration: exitSizeDuration, - easing: exitEasing, - delay: exitDelay, - }), - ); + exitAtoms.push(sizeExitAtom(orientation, exitSizeDuration, exitEasing, element, exitDelay)); + exitAtoms.push(whitespaceAtom('exit', orientation, exitSizeDuration, exitEasing, exitDelay)); return { enter: enterAtoms, diff --git a/packages/react-components/react-motion-components-preview/library/src/components/Collapse/collapse-atoms.ts b/packages/react-components/react-motion-components-preview/library/src/components/Collapse/collapse-atoms.ts index 0ce27aa7d91c8..9d5e8d45a21f3 100644 --- a/packages/react-components/react-motion-components-preview/library/src/components/Collapse/collapse-atoms.ts +++ b/packages/react-components/react-motion-components-preview/library/src/components/Collapse/collapse-atoms.ts @@ -1,5 +1,5 @@ -import { AtomMotion } from '@fluentui/react-motion/src/types'; -import type { CollapseOrientation } from './collapse-types'; +import { AtomMotion, motionTokens, PresenceDirection } from '@fluentui/react-motion'; +import { CollapseOrientation } from './collapse-types'; // ----- SIZE ----- @@ -11,19 +11,13 @@ const sizeValuesForOrientation = (orientation: CollapseOrientation, element: Ele return { sizeName, overflowName, toSize }; }; -export const sizeEnterAtom = ({ - orientation, - duration, - easing, - element, - fromSize = '0', -}: { - orientation: CollapseOrientation; - duration: number; - easing: string; - element: HTMLElement; - fromSize?: string; -}): AtomMotion => { +export const sizeEnterAtom = ( + orientation: CollapseOrientation, + duration: number, + easing: string, + element: HTMLElement, + fromSize: string = '0', +): AtomMotion => { const { sizeName, overflowName, toSize } = sizeValuesForOrientation(orientation, element); return { @@ -37,21 +31,14 @@ export const sizeEnterAtom = ({ }; }; -export const sizeExitAtom = ({ - orientation, - duration, - easing, - element, - delay = 0, - fromSize = '0', -}: { - orientation: CollapseOrientation; - duration: number; - easing: string; - element: HTMLElement; - delay?: number; - fromSize?: string; -}): AtomMotion => { +export const sizeExitAtom = ( + orientation: CollapseOrientation, + duration: number, + easing: string, + element: HTMLElement, + delay: number = 0, + fromSize: string = '0', +): AtomMotion => { const { sizeName, overflowName, toSize } = sizeValuesForOrientation(orientation, element); return { @@ -90,80 +77,44 @@ const whitespaceValuesForOrientation = (orientation: CollapseOrientation) => { // Because a height of zero does not eliminate padding or margin, // we will create keyframes to animate them to zero. -export const whitespaceEnterAtom = ({ - orientation, - duration, - easing, -}: { - orientation: CollapseOrientation; - duration: number; - easing: string; -}): AtomMotion => { +export const whitespaceAtom = ( + direction: PresenceDirection, + orientation: CollapseOrientation, + duration: number, + easing: string, + delay: number = 0, +): AtomMotion => { const { paddingStart, paddingEnd, marginStart, marginEnd } = whitespaceValuesForOrientation(orientation); - return { - // Animate from whitespace of zero to the current whitespace, by omitting the ending keyframe. - keyframes: [{ [paddingStart]: '0', [paddingEnd]: '0', [marginStart]: '0', [marginEnd]: '0', offset: 0 }], + // The keyframe with zero whitespace is at the start for enter and at the end for exit. + const offset = direction === 'enter' ? 0 : 1; + const keyframes = [{ [paddingStart]: '0', [paddingEnd]: '0', [marginStart]: '0', [marginEnd]: '0', offset }]; + + const atom: AtomMotion = { + keyframes, duration, easing, + delay, }; + if (direction === 'exit') { + atom.fill = 'forwards'; + } + return atom; }; -export const whitespaceExitAtom = ({ - orientation, - duration, - easing, - delay = 0, -}: { - orientation: CollapseOrientation; - duration: number; - easing: string; - delay?: number; -}): AtomMotion => { - const { paddingStart, paddingEnd, marginStart, marginEnd } = whitespaceValuesForOrientation(orientation); +// ----- OPACITY ----- + +export const opacityAtom = ( + direction: PresenceDirection, + duration: number, + easing: string = motionTokens.curveLinear, +) => { + const keyframes = [{ opacity: 0 }, { opacity: 1 }]; + if (direction === 'exit') { + keyframes.reverse(); + } return { - // Animate from the current whitespace to whitespace of zero, by using offset 1 and omitting the starting keyframe. - keyframes: [{ [paddingStart]: '0', [paddingEnd]: '0', [marginStart]: '0', [marginEnd]: '0', offset: 1 }], + keyframes, duration, easing, - fill: 'forwards', - delay, }; }; - -// ----- OPACITY ----- - -export const opacityEnterAtom = ({ - duration, - easing, - delay = 0, - fromOpacity = 0, - toOpacity = 1, -}: { - duration: number; - easing: string; - delay?: number; - fromOpacity?: number; - toOpacity?: number; -}): AtomMotion => ({ - keyframes: [{ opacity: fromOpacity }, { opacity: toOpacity }], - duration, - easing, - delay, - fill: 'both', -}); - -export const opacityExitAtom = ({ - duration, - easing, - fromOpacity = 0, - toOpacity = 1, -}: { - duration: number; - easing: string; - fromOpacity?: number; - toOpacity?: number; -}): AtomMotion => ({ - keyframes: [{ opacity: toOpacity }, { opacity: fromOpacity }], - duration, - easing, -}); From f497d9a7758149e84cd99ee6483259680c5592a5 Mon Sep 17 00:00:00 2001 From: Robert Penner Date: Thu, 12 Dec 2024 22:48:26 +0000 Subject: [PATCH 2/4] chore: `yarn change` --- ...nents-preview-67537ce1-b461-4bec-bbcb-61d9202e6632.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@fluentui-react-motion-components-preview-67537ce1-b461-4bec-bbcb-61d9202e6632.json diff --git a/change/@fluentui-react-motion-components-preview-67537ce1-b461-4bec-bbcb-61d9202e6632.json b/change/@fluentui-react-motion-components-preview-67537ce1-b461-4bec-bbcb-61d9202e6632.json new file mode 100644 index 0000000000000..1456c1f2713ac --- /dev/null +++ b/change/@fluentui-react-motion-components-preview-67537ce1-b461-4bec-bbcb-61d9202e6632.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "refactor(Collapse): streamline atom functions", + "packageName": "@fluentui/react-motion-components-preview", + "email": "robertpenner@microsoft.com", + "dependentChangeType": "patch" +} From 5cf1291ad574d0bfcf1eca20844f46c2d8fe2bfe Mon Sep 17 00:00:00 2001 From: Robert Penner Date: Mon, 16 Dec 2024 18:14:13 +0000 Subject: [PATCH 3/4] refactor(Collapse): move fade atom to own file - put in a new atoms folder that is shared amongst motion components - rename from opacityAtom to fadeAtom --- .../library/src/atoms/fade-atom.ts | 13 +++++++++++++ .../src/components/Collapse/Collapse.ts | 7 ++++--- .../src/components/Collapse/collapse-atoms.ts | 18 ------------------ 3 files changed, 17 insertions(+), 21 deletions(-) create mode 100644 packages/react-components/react-motion-components-preview/library/src/atoms/fade-atom.ts diff --git a/packages/react-components/react-motion-components-preview/library/src/atoms/fade-atom.ts b/packages/react-components/react-motion-components-preview/library/src/atoms/fade-atom.ts new file mode 100644 index 0000000000000..6b2a9d5d4f4b4 --- /dev/null +++ b/packages/react-components/react-motion-components-preview/library/src/atoms/fade-atom.ts @@ -0,0 +1,13 @@ +import { PresenceDirection, motionTokens } from '@fluentui/react-motion'; + +export const fadeAtom = (direction: PresenceDirection, duration: number, easing: string = motionTokens.curveLinear) => { + const keyframes = [{ opacity: 0 }, { opacity: 1 }]; + if (direction === 'exit') { + keyframes.reverse(); + } + return { + keyframes, + duration, + easing, + }; +}; diff --git a/packages/react-components/react-motion-components-preview/library/src/components/Collapse/Collapse.ts b/packages/react-components/react-motion-components-preview/library/src/components/Collapse/Collapse.ts index 601022d2b57c2..a830927d4703b 100644 --- a/packages/react-components/react-motion-components-preview/library/src/components/Collapse/Collapse.ts +++ b/packages/react-components/react-motion-components-preview/library/src/components/Collapse/Collapse.ts @@ -1,7 +1,8 @@ import { motionTokens, createPresenceComponent, AtomMotion } from '@fluentui/react-motion'; import type { PresenceMotionFnCreator } from '../../types'; import type { CollapseDelayedVariantParams, CollapseRuntimeParams, CollapseVariantParams } from './collapse-types'; -import { sizeEnterAtom, opacityAtom, whitespaceAtom, sizeExitAtom } from './collapse-atoms'; +import { sizeEnterAtom, sizeExitAtom, whitespaceAtom } from './collapse-atoms'; +import { fadeAtom } from '../../atoms/fade-atom'; /** Define a presence motion for collapse/expand that can stagger the size and opacity motions by a given delay. */ export const createCollapseDelayedPresence: PresenceMotionFnCreator< @@ -30,7 +31,7 @@ export const createCollapseDelayedPresence: PresenceMotionFnCreator< ]; // Fade in only if animateOpacity is true. Otherwise, leave opacity unaffected. if (animateOpacity) { - enterAtoms.push({ ...opacityAtom('enter', enterOpacityDuration, enterEasing), delay: enterDelay, fill: 'both' }); + enterAtoms.push({ ...fadeAtom('enter', enterOpacityDuration, enterEasing), delay: enterDelay, fill: 'both' }); } // ----- EXIT ----- @@ -38,7 +39,7 @@ export const createCollapseDelayedPresence: PresenceMotionFnCreator< const exitAtoms: AtomMotion[] = []; // Fade out only if animateOpacity is true. Otherwise, leave opacity unaffected. if (animateOpacity) { - exitAtoms.push(opacityAtom('exit', exitOpacityDuration, exitEasing)); + exitAtoms.push(fadeAtom('exit', exitOpacityDuration, exitEasing)); } exitAtoms.push(sizeExitAtom(orientation, exitSizeDuration, exitEasing, element, exitDelay)); exitAtoms.push(whitespaceAtom('exit', orientation, exitSizeDuration, exitEasing, exitDelay)); diff --git a/packages/react-components/react-motion-components-preview/library/src/components/Collapse/collapse-atoms.ts b/packages/react-components/react-motion-components-preview/library/src/components/Collapse/collapse-atoms.ts index 9d5e8d45a21f3..1985a46100e8e 100644 --- a/packages/react-components/react-motion-components-preview/library/src/components/Collapse/collapse-atoms.ts +++ b/packages/react-components/react-motion-components-preview/library/src/components/Collapse/collapse-atoms.ts @@ -100,21 +100,3 @@ export const whitespaceAtom = ( } return atom; }; - -// ----- OPACITY ----- - -export const opacityAtom = ( - direction: PresenceDirection, - duration: number, - easing: string = motionTokens.curveLinear, -) => { - const keyframes = [{ opacity: 0 }, { opacity: 1 }]; - if (direction === 'exit') { - keyframes.reverse(); - } - return { - keyframes, - duration, - easing, - }; -}; From fca343d6e567d918d78ed4762ae92dc34209745b Mon Sep 17 00:00:00 2001 From: Robert Penner Date: Wed, 18 Dec 2024 17:44:24 +0000 Subject: [PATCH 4/4] refactor(Collapse): change atom functions to named arguments --- .../library/src/atoms/fade-atom.ts | 26 ++++++- .../src/components/Collapse/Collapse.ts | 24 +++++-- .../src/components/Collapse/collapse-atoms.ts | 68 ++++++++++++------- 3 files changed, 84 insertions(+), 34 deletions(-) diff --git a/packages/react-components/react-motion-components-preview/library/src/atoms/fade-atom.ts b/packages/react-components/react-motion-components-preview/library/src/atoms/fade-atom.ts index 6b2a9d5d4f4b4..91b369be83ff7 100644 --- a/packages/react-components/react-motion-components-preview/library/src/atoms/fade-atom.ts +++ b/packages/react-components/react-motion-components-preview/library/src/atoms/fade-atom.ts @@ -1,7 +1,27 @@ -import { PresenceDirection, motionTokens } from '@fluentui/react-motion'; +import { AtomMotion, PresenceDirection, motionTokens } from '@fluentui/react-motion'; -export const fadeAtom = (direction: PresenceDirection, duration: number, easing: string = motionTokens.curveLinear) => { - const keyframes = [{ opacity: 0 }, { opacity: 1 }]; +interface FadeAtomParams { + direction: PresenceDirection; + duration: number; + easing?: string; + fromValue?: number; +} + +/** + * Generates a motion atom object for a fade in or fade out. + * @param direction - The functional direction of the motion: 'enter' or 'exit'. + * @param duration - The duration of the motion in milliseconds. + * @param easing - The easing curve for the motion. Defaults to `motionTokens.curveLinear`. + * @param fromValue - The starting opacity value. Defaults to 0. + * @returns A motion atom object with opacity keyframes and the supplied duration and easing. + */ +export const fadeAtom = ({ + direction, + duration, + easing = motionTokens.curveLinear, + fromValue = 0, +}: FadeAtomParams): AtomMotion => { + const keyframes = [{ opacity: fromValue }, { opacity: 1 }]; if (direction === 'exit') { keyframes.reverse(); } diff --git a/packages/react-components/react-motion-components-preview/library/src/components/Collapse/Collapse.ts b/packages/react-components/react-motion-components-preview/library/src/components/Collapse/Collapse.ts index a830927d4703b..22c8862cc3b82 100644 --- a/packages/react-components/react-motion-components-preview/library/src/components/Collapse/Collapse.ts +++ b/packages/react-components/react-motion-components-preview/library/src/components/Collapse/Collapse.ts @@ -26,12 +26,16 @@ export const createCollapseDelayedPresence: PresenceMotionFnCreator< // ----- ENTER ----- // The enter transition is an array of up to 3 motion atoms: size, whitespace and opacity. const enterAtoms: AtomMotion[] = [ - sizeEnterAtom(orientation, enterSizeDuration, enterEasing, element), - whitespaceAtom('enter', orientation, enterSizeDuration, enterEasing), + sizeEnterAtom({ orientation, duration: enterSizeDuration, easing: enterEasing, element }), + whitespaceAtom({ direction: 'enter', orientation, duration: enterSizeDuration, easing: enterEasing }), ]; // Fade in only if animateOpacity is true. Otherwise, leave opacity unaffected. if (animateOpacity) { - enterAtoms.push({ ...fadeAtom('enter', enterOpacityDuration, enterEasing), delay: enterDelay, fill: 'both' }); + enterAtoms.push({ + ...fadeAtom({ direction: 'enter', duration: enterOpacityDuration, easing: enterEasing }), + delay: enterDelay, + fill: 'both', + }); } // ----- EXIT ----- @@ -39,10 +43,18 @@ export const createCollapseDelayedPresence: PresenceMotionFnCreator< const exitAtoms: AtomMotion[] = []; // Fade out only if animateOpacity is true. Otherwise, leave opacity unaffected. if (animateOpacity) { - exitAtoms.push(fadeAtom('exit', exitOpacityDuration, exitEasing)); + exitAtoms.push(fadeAtom({ direction: 'exit', duration: exitOpacityDuration, easing: exitEasing })); } - exitAtoms.push(sizeExitAtom(orientation, exitSizeDuration, exitEasing, element, exitDelay)); - exitAtoms.push(whitespaceAtom('exit', orientation, exitSizeDuration, exitEasing, exitDelay)); + exitAtoms.push( + sizeExitAtom({ orientation, duration: exitSizeDuration, easing: exitEasing, element, delay: exitDelay }), + whitespaceAtom({ + direction: 'exit', + orientation, + duration: exitSizeDuration, + easing: exitEasing, + delay: exitDelay, + }), + ); return { enter: enterAtoms, diff --git a/packages/react-components/react-motion-components-preview/library/src/components/Collapse/collapse-atoms.ts b/packages/react-components/react-motion-components-preview/library/src/components/Collapse/collapse-atoms.ts index 1985a46100e8e..6129461834cff 100644 --- a/packages/react-components/react-motion-components-preview/library/src/components/Collapse/collapse-atoms.ts +++ b/packages/react-components/react-motion-components-preview/library/src/components/Collapse/collapse-atoms.ts @@ -1,4 +1,4 @@ -import { AtomMotion, motionTokens, PresenceDirection } from '@fluentui/react-motion'; +import { AtomMotion, PresenceDirection } from '@fluentui/react-motion'; import { CollapseOrientation } from './collapse-types'; // ----- SIZE ----- @@ -11,13 +11,21 @@ const sizeValuesForOrientation = (orientation: CollapseOrientation, element: Ele return { sizeName, overflowName, toSize }; }; -export const sizeEnterAtom = ( - orientation: CollapseOrientation, - duration: number, - easing: string, - element: HTMLElement, - fromSize: string = '0', -): AtomMotion => { +interface SizeEnterAtomParams { + orientation: CollapseOrientation; + duration: number; + easing: string; + element: HTMLElement; + fromSize?: string; +} + +export const sizeEnterAtom = ({ + orientation, + duration, + easing, + element, + fromSize = '0', +}: SizeEnterAtomParams): AtomMotion => { const { sizeName, overflowName, toSize } = sizeValuesForOrientation(orientation, element); return { @@ -31,14 +39,18 @@ export const sizeEnterAtom = ( }; }; -export const sizeExitAtom = ( - orientation: CollapseOrientation, - duration: number, - easing: string, - element: HTMLElement, - delay: number = 0, - fromSize: string = '0', -): AtomMotion => { +interface SizeExitAtomParams extends SizeEnterAtomParams { + delay?: number; +} + +export const sizeExitAtom = ({ + orientation, + duration, + easing, + element, + delay = 0, + fromSize = '0', +}: SizeExitAtomParams): AtomMotion => { const { sizeName, overflowName, toSize } = sizeValuesForOrientation(orientation, element); return { @@ -75,15 +87,21 @@ const whitespaceValuesForOrientation = (orientation: CollapseOrientation) => { }; }; -// Because a height of zero does not eliminate padding or margin, -// we will create keyframes to animate them to zero. -export const whitespaceAtom = ( - direction: PresenceDirection, - orientation: CollapseOrientation, - duration: number, - easing: string, - delay: number = 0, -): AtomMotion => { +interface WhitespaceAtomParams { + direction: PresenceDirection; + orientation: CollapseOrientation; + duration: number; + easing: string; + delay?: number; +} + +export const whitespaceAtom = ({ + direction, + orientation, + duration, + easing, + delay = 0, +}: WhitespaceAtomParams): AtomMotion => { const { paddingStart, paddingEnd, marginStart, marginEnd } = whitespaceValuesForOrientation(orientation); // The keyframe with zero whitespace is at the start for enter and at the end for exit. const offset = direction === 'enter' ? 0 : 1;