Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@fluentui/react-motion

Package Overview
Dependencies
Maintainers
12
Versions
130
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@fluentui/react-motion - npm Package Compare versions

Comparing version 9.0.0 to 9.1.0

18

CHANGELOG.md
# Change Log - @fluentui/react-motion
This log was last generated on Thu, 06 Jun 2024 15:22:17 GMT and should not be manually modified.
This log was last generated on Wed, 12 Jun 2024 13:16:08 GMT and should not be manually modified.
<!-- Start content -->
## [9.1.0](https://github.com/microsoft/fluentui/tree/@fluentui/react-motion_v9.1.0)
Wed, 12 Jun 2024 13:16:08 GMT
[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-motion_v9.0.0..@fluentui/react-motion_v9.1.0)
### Minor changes
- feat: add support for params ([PR #31566](https://github.com/microsoft/fluentui/pull/31566) by olfedias@microsoft.com)
- feat: Add consistent start and finish lifecycle callbacks ([PR #31644](https://github.com/microsoft/fluentui/pull/31644) by lingfangao@hotmail.com)
### Patches
- fix(motions): improve compat for jsdom & jest ([PR #31602](https://github.com/microsoft/fluentui/pull/31602) by olfedias@microsoft.com)
## [9.0.0](https://github.com/microsoft/fluentui/tree/@fluentui/react-motion_v9.0.0)
Thu, 06 Jun 2024 15:22:17 GMT
Thu, 06 Jun 2024 15:26:35 GMT
[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-motions-preview_v0.3.2..@fluentui/react-motion_v9.0.0)

@@ -11,0 +25,0 @@

@@ -7,5 +7,5 @@ import * as React_2 from 'react';

export declare type AtomMotionFn = (params: {
export declare type AtomMotionFn<MotionParams extends Record<string, MotionParam> = {}> = (params: {
element: HTMLElement;
}) => AtomMotion | AtomMotion[];
} & MotionParams) => AtomMotion | AtomMotion[];

@@ -17,5 +17,5 @@ /**

*/
export declare function createMotionComponent(value: AtomMotion | AtomMotion[] | AtomMotionFn): React_2.FC<MotionComponentProps>;
export declare function createMotionComponent<MotionParams extends Record<string, MotionParam> = {}>(value: AtomMotion | AtomMotion[] | AtomMotionFn<MotionParams>): React_2.FC<MotionComponentProps & MotionParams>;
export declare function createPresenceComponent(value: PresenceMotion | PresenceMotionFn): React_2.FC<PresenceComponentProps>;
export declare function createPresenceComponent<MotionParams extends Record<string, MotionParam> = {}>(value: PresenceMotion | PresenceMotionFn<MotionParams>): React_2.FC<PresenceComponentProps & MotionParams>;

@@ -49,2 +49,17 @@ export declare const curves: {

imperativeRef?: React_2.Ref<MotionImperativeRef | undefined>;
/**
* Callback that is called when the whole motion finishes.
*
* A motion definition can contain multiple animations and therefore multiple "finish" events. The callback is
* triggered once all animations have finished with "null" instead of an event object to avoid ambiguity.
*/
onMotionFinish?: (ev: null) => void;
/**
* Callback that is called when the whole motion starts.
*
* A motion definition can contain multiple animations and therefore multiple "start" events. The callback is
* triggered when the first animation is started. There is no official "start" event with the Web Animations API.
* so the callback is triggered with "null".
*/
onMotionStart?: (ev: null) => void;
};

@@ -59,2 +74,10 @@

/**
* @internal
*
* A motion param should be a primitive value that can be serialized to JSON and could be potentially used a plain
* dependency for React hooks.
*/
declare type MotionParam = boolean | number | string;
export declare const motionTokens: {

@@ -99,2 +122,12 @@ curveAccelerateMax: "cubic-bezier(0.9,0.1,1,0.2)";

}) => void;
/**
* Callback that is called when the whole motion starts.
*
* A motion definition can contain multiple animations and therefore multiple "start" events. The callback is
* triggered when the first animation is started. There is no official "start" event with the Web Animations API.
* so the callback is triggered with "null".
*/
onMotionStart?: (ev: null, data: {
direction: 'enter' | 'exit';
}) => void;
/** Defines whether a component is visible; triggers the "enter" or "exit" motions. */

@@ -145,6 +178,6 @@ visible?: boolean;

export declare type PresenceMotionFn = (params: {
export declare type PresenceMotionFn<MotionParams extends Record<string, MotionParam> = {}> = (params: {
element: HTMLElement;
}) => PresenceMotion;
} & MotionParams) => PresenceMotion;
export { }

@@ -20,16 +20,32 @@ "use strict";

const Atom = (props)=>{
const { children, imperativeRef } = props;
const { children, imperativeRef, onMotionFinish: onMotionFinishProp, onMotionStart: onMotionStartProp, ..._rest } = props;
const params = _rest;
const child = (0, _getChildElement.getChildElement)(children);
const handleRef = (0, _useMotionImperativeRef.useMotionImperativeRef)(imperativeRef);
const elementRef = _react.useRef();
const paramsRef = _react.useRef(params);
const isReducedMotion = (0, _useIsReducedMotion.useIsReducedMotion)();
const onMotionStart = (0, _reactutilities.useEventCallback)(()=>{
onMotionStartProp === null || onMotionStartProp === void 0 ? void 0 : onMotionStartProp(null);
});
const onMotionFinish = (0, _reactutilities.useEventCallback)(()=>{
onMotionFinishProp === null || onMotionFinishProp === void 0 ? void 0 : onMotionFinishProp(null);
});
(0, _reactutilities.useIsomorphicLayoutEffect)(()=>{
// Heads up!
// We store the params in a ref to avoid re-rendering the component when the params change.
paramsRef.current = params;
});
(0, _reactutilities.useIsomorphicLayoutEffect)(()=>{
const element = elementRef.current;
if (element) {
const atoms = typeof value === 'function' ? value({
element
element,
...paramsRef.current
}) : value;
onMotionStart();
const handle = (0, _animateAtoms.animateAtoms)(element, atoms, {
isReducedMotion: isReducedMotion()
});
handle.onfinish = onMotionFinish;
handleRef.current = handle;

@@ -42,3 +58,5 @@ return ()=>{

handleRef,
isReducedMotion
isReducedMotion,
onMotionFinish,
onMotionStart
]);

@@ -45,0 +63,0 @@ return /*#__PURE__*/ _react.cloneElement(children, {

48

lib-commonjs/factories/createPresenceComponent.js

@@ -21,3 +21,3 @@ "use strict";

function shouldSkipAnimation(appear, isFirstMount, visible) {
return !appear && isFirstMount && visible;
return !appear && isFirstMount && !!visible;
}

@@ -27,6 +27,8 @@ function createPresenceComponent(value) {

const itemContext = _react.useContext(_PresenceGroupChildContext.PresenceGroupChildContext);
const { appear, children, imperativeRef, onMotionFinish, visible, unmountOnExit } = {
const merged = {
...itemContext,
...props
};
const { appear, children, imperativeRef, onExit, onMotionFinish, onMotionStart, visible, unmountOnExit, ..._rest } = merged;
const params = _rest;
const [mounted, setMounted] = (0, _useMountedState.useMountedState)(visible, unmountOnExit);

@@ -37,22 +39,28 @@ const child = (0, _getChildElement.getChildElement)(children);

const ref = (0, _reactutilities.useMergedRefs)(elementRef, child.ref);
const optionsRef = _react.useRef({});
const optionsRef = _react.useRef({
appear,
params
});
const isFirstMount = (0, _reactutilities.useFirstMount)();
const isReducedMotion = (0, _useIsReducedMotion.useIsReducedMotion)();
const onEnterFinish = (0, _reactutilities.useEventCallback)(()=>{
onMotionFinish === null || onMotionFinish === void 0 ? void 0 : onMotionFinish(null, {
direction: 'enter'
const handleMotionStart = (0, _reactutilities.useEventCallback)((direction)=>{
onMotionStart === null || onMotionStart === void 0 ? void 0 : onMotionStart(null, {
direction
});
});
const onExitFinish = (0, _reactutilities.useEventCallback)(()=>{
const handleMotionFinish = (0, _reactutilities.useEventCallback)((direction)=>{
onMotionFinish === null || onMotionFinish === void 0 ? void 0 : onMotionFinish(null, {
direction: 'exit'
direction
});
if (unmountOnExit) {
if (direction === 'exit' && unmountOnExit) {
setMounted(false);
itemContext === null || itemContext === void 0 ? void 0 : itemContext.onExit();
onExit === null || onExit === void 0 ? void 0 : onExit();
}
});
(0, _reactutilities.useIsomorphicLayoutEffect)(()=>{
// Heads up!
// We store the params in a ref to avoid re-rendering the component when the params change.
optionsRef.current = {
appear
appear,
params
};

@@ -66,9 +74,15 @@ });

const presenceMotion = typeof value === 'function' ? value({
element
element,
...optionsRef.current.params
}) : value;
const atoms = visible ? presenceMotion.enter : presenceMotion.exit;
const direction = visible ? 'enter' : 'exit';
const forceFinishMotion = !visible && isFirstMount;
if (!forceFinishMotion) {
handleMotionStart(direction);
}
const handle = (0, _animateAtoms.animateAtoms)(element, atoms, {
isReducedMotion: isReducedMotion()
});
if (!visible && isFirstMount) {
if (forceFinishMotion) {
// Heads up!

@@ -80,3 +94,5 @@ // .finish() is used there to skip animation on first mount, but apply animation styles immediately

handleRef.current = handle;
handle.onfinish = visible ? onEnterFinish : onExitFinish;
handle.onfinish = ()=>{
handleMotionFinish(direction);
};
return ()=>{

@@ -89,4 +105,4 @@ handle.cancel();

isReducedMotion,
onEnterFinish,
onExitFinish,
handleMotionFinish,
handleMotionStart,
visible

@@ -93,0 +109,0 @@ ]);

@@ -39,2 +39,14 @@ "use strict";

set onfinish (callback){
// Heads up!
// Jest uses jsdom as the default environment, which doesn't support the Web Animations API. This no-op is
// necessary to avoid errors in tests.
//
// See https://github.com/microsoft/fluentui/issues/31593
// See https://github.com/jsdom/jsdom/issues/3429
if (process.env.NODE_ENV === 'test') {
if (animations.length === 0) {
callback();
return;
}
}
Promise.all(animations.map((animation)=>animation.finished)).then(()=>{

@@ -41,0 +53,0 @@ callback();

@@ -1,2 +0,2 @@

import { useIsomorphicLayoutEffect, useMergedRefs } from '@fluentui/react-utilities';
import { useEventCallback, useIsomorphicLayoutEffect, useMergedRefs } from '@fluentui/react-utilities';
import * as React from 'react';

@@ -13,16 +13,32 @@ import { useIsReducedMotion } from '../hooks/useIsReducedMotion';

const Atom = (props)=>{
const { children, imperativeRef } = props;
const { children, imperativeRef, onMotionFinish: onMotionFinishProp, onMotionStart: onMotionStartProp, ..._rest } = props;
const params = _rest;
const child = getChildElement(children);
const handleRef = useMotionImperativeRef(imperativeRef);
const elementRef = React.useRef();
const paramsRef = React.useRef(params);
const isReducedMotion = useIsReducedMotion();
const onMotionStart = useEventCallback(()=>{
onMotionStartProp === null || onMotionStartProp === void 0 ? void 0 : onMotionStartProp(null);
});
const onMotionFinish = useEventCallback(()=>{
onMotionFinishProp === null || onMotionFinishProp === void 0 ? void 0 : onMotionFinishProp(null);
});
useIsomorphicLayoutEffect(()=>{
// Heads up!
// We store the params in a ref to avoid re-rendering the component when the params change.
paramsRef.current = params;
});
useIsomorphicLayoutEffect(()=>{
const element = elementRef.current;
if (element) {
const atoms = typeof value === 'function' ? value({
element
element,
...paramsRef.current
}) : value;
onMotionStart();
const handle = animateAtoms(element, atoms, {
isReducedMotion: isReducedMotion()
});
handle.onfinish = onMotionFinish;
handleRef.current = handle;

@@ -35,3 +51,5 @@ return ()=>{

handleRef,
isReducedMotion
isReducedMotion,
onMotionFinish,
onMotionStart
]);

@@ -38,0 +56,0 @@ return React.cloneElement(children, {

@@ -10,3 +10,3 @@ import { useEventCallback, useFirstMount, useIsomorphicLayoutEffect, useMergedRefs } from '@fluentui/react-utilities';

function shouldSkipAnimation(appear, isFirstMount, visible) {
return !appear && isFirstMount && visible;
return !appear && isFirstMount && !!visible;
}

@@ -16,6 +16,8 @@ export function createPresenceComponent(value) {

const itemContext = React.useContext(PresenceGroupChildContext);
const { appear, children, imperativeRef, onMotionFinish, visible, unmountOnExit } = {
const merged = {
...itemContext,
...props
};
const { appear, children, imperativeRef, onExit, onMotionFinish, onMotionStart, visible, unmountOnExit, ..._rest } = merged;
const params = _rest;
const [mounted, setMounted] = useMountedState(visible, unmountOnExit);

@@ -26,22 +28,28 @@ const child = getChildElement(children);

const ref = useMergedRefs(elementRef, child.ref);
const optionsRef = React.useRef({});
const optionsRef = React.useRef({
appear,
params
});
const isFirstMount = useFirstMount();
const isReducedMotion = useIsReducedMotion();
const onEnterFinish = useEventCallback(()=>{
onMotionFinish === null || onMotionFinish === void 0 ? void 0 : onMotionFinish(null, {
direction: 'enter'
const handleMotionStart = useEventCallback((direction)=>{
onMotionStart === null || onMotionStart === void 0 ? void 0 : onMotionStart(null, {
direction
});
});
const onExitFinish = useEventCallback(()=>{
const handleMotionFinish = useEventCallback((direction)=>{
onMotionFinish === null || onMotionFinish === void 0 ? void 0 : onMotionFinish(null, {
direction: 'exit'
direction
});
if (unmountOnExit) {
if (direction === 'exit' && unmountOnExit) {
setMounted(false);
itemContext === null || itemContext === void 0 ? void 0 : itemContext.onExit();
onExit === null || onExit === void 0 ? void 0 : onExit();
}
});
useIsomorphicLayoutEffect(()=>{
// Heads up!
// We store the params in a ref to avoid re-rendering the component when the params change.
optionsRef.current = {
appear
appear,
params
};

@@ -55,9 +63,15 @@ });

const presenceMotion = typeof value === 'function' ? value({
element
element,
...optionsRef.current.params
}) : value;
const atoms = visible ? presenceMotion.enter : presenceMotion.exit;
const direction = visible ? 'enter' : 'exit';
const forceFinishMotion = !visible && isFirstMount;
if (!forceFinishMotion) {
handleMotionStart(direction);
}
const handle = animateAtoms(element, atoms, {
isReducedMotion: isReducedMotion()
});
if (!visible && isFirstMount) {
if (forceFinishMotion) {
// Heads up!

@@ -69,3 +83,5 @@ // .finish() is used there to skip animation on first mount, but apply animation styles immediately

handleRef.current = handle;
handle.onfinish = visible ? onEnterFinish : onExitFinish;
handle.onfinish = ()=>{
handleMotionFinish(direction);
};
return ()=>{

@@ -79,4 +95,4 @@ handle.cancel();

isReducedMotion,
onEnterFinish,
onExitFinish,
handleMotionFinish,
handleMotionStart,
visible

@@ -83,0 +99,0 @@ ]);

@@ -29,2 +29,14 @@ export function animateAtoms(element, value, options) {

set onfinish (callback){
// Heads up!
// Jest uses jsdom as the default environment, which doesn't support the Web Animations API. This no-op is
// necessary to avoid errors in tests.
//
// See https://github.com/microsoft/fluentui/issues/31593
// See https://github.com/jsdom/jsdom/issues/3429
if (process.env.NODE_ENV === 'test') {
if (animations.length === 0) {
callback();
return;
}
}
Promise.all(animations.map((animation)=>animation.finished)).then(()=>{

@@ -31,0 +43,0 @@ callback();

{
"name": "@fluentui/react-motion",
"version": "9.0.0",
"version": "9.1.0",
"description": "A package with utilities & motion definitions using Web Animations API",

@@ -26,6 +26,5 @@ "main": "lib-commonjs/index.js",

"start": "yarn storybook",
"storybook": "start-storybook",
"storybook": "yarn --cwd ../stories storybook",
"test": "jest --passWithNoTests",
"test-ssr": "test-ssr \"./stories/**/*.stories.tsx\"",
"type-check": "tsc -b tsconfig.json"
"type-check": "just-scripts type-check"
},

@@ -32,0 +31,0 @@ "devDependencies": {

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc