@react-native-ama/animations 1.2.0 → 2.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/hooks/useAnimation.js +4 -4
- package/dist/hooks/useAnimationDuration.js +2 -2
- package/dist/hooks/useReanimatedAnimationBuilder.js +4 -4
- package/dist/hooks/useReanimatedTiming.d.ts +1 -1
- package/dist/hooks/useReanimatedTiming.js +2 -2
- package/dist/internals/animationConstants.d.ts +3 -0
- package/dist/internals/animationConstants.js +32 -0
- package/dist/internals/animationStates.d.ts +2 -0
- package/dist/internals/animationStates.js +43 -0
- package/dist/internals/useAMAContextSafe.d.ts +3 -0
- package/dist/internals/useAMAContextSafe.js +17 -0
- package/dist/utils/isMotionAnimation.d.ts +1 -1
- package/dist/utils/isMotionAnimation.js +2 -2
- package/package.json +79 -19
- package/src/components/AnimatedContainer.test.tsx +6 -12
- package/src/components/AnimatedContainer.tsx +2 -2
- package/src/hooks/useAnimation.test.ts +0 -1
- package/src/hooks/useAnimation.ts +3 -3
- package/src/hooks/useAnimationDuration.test.ts +0 -1
- package/src/hooks/useAnimationDuration.ts +2 -3
- package/src/hooks/useReanimatedAnimationBuilder.test.ts +1 -2
- package/src/hooks/useReanimatedAnimationBuilder.ts +3 -3
- package/src/hooks/useReanimatedTiming.test.ts +3 -4
- package/src/hooks/useReanimatedTiming.ts +3 -4
- package/src/internals/animationConstants.ts +33 -0
- package/src/internals/animationStates.test.ts +71 -0
- package/src/internals/animationStates.ts +55 -0
- package/src/internals/useAMAContextSafe.test.ts +44 -0
- package/src/internals/useAMAContextSafe.ts +13 -0
- package/src/utils/isMotionAnimation.ts +1 -1
|
@@ -35,12 +35,12 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
35
35
|
};
|
|
36
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
37
|
exports.useAnimation = void 0;
|
|
38
|
-
const
|
|
39
|
-
const
|
|
38
|
+
const useAMAContextSafe_1 = require("../internals/useAMAContextSafe");
|
|
39
|
+
const animationStates_1 = require("../internals/animationStates");
|
|
40
40
|
const React = __importStar(require("react"));
|
|
41
41
|
const react_native_1 = require("react-native");
|
|
42
42
|
const useAnimation = ({ duration, useNativeDriver, from, to, skipIfReduceMotionEnabled = false, }) => {
|
|
43
|
-
const { isReduceMotionEnabled } = (0,
|
|
43
|
+
const { isReduceMotionEnabled } = (0, useAMAContextSafe_1.useAMAContextSafe)();
|
|
44
44
|
const progress = React.useRef(new react_native_1.Animated.Value(0)).current;
|
|
45
45
|
const reduceMotionProgress = React.useRef(new react_native_1.Animated.Value(0)).current;
|
|
46
46
|
const hasOnlyMotionAnimation = React.useRef(false);
|
|
@@ -62,7 +62,7 @@ const useAnimation = ({ duration, useNativeDriver, from, to, skipIfReduceMotionE
|
|
|
62
62
|
useNativeDriver,
|
|
63
63
|
});
|
|
64
64
|
};
|
|
65
|
-
const _a = (0,
|
|
65
|
+
const _a = (0, animationStates_1.interpolateAnimationStates)(from, to, isReduceMotionEnabled, progress, reduceMotionProgress), { __hasOnlyMotionAnimation } = _a, style = __rest(_a, ["__hasOnlyMotionAnimation"]);
|
|
66
66
|
hasOnlyMotionAnimation.current = __hasOnlyMotionAnimation;
|
|
67
67
|
return {
|
|
68
68
|
animatedStyle: style,
|
|
@@ -4,11 +4,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.useAnimationDuration = void 0;
|
|
7
|
-
const
|
|
7
|
+
const useAMAContextSafe_1 = require("../internals/useAMAContextSafe");
|
|
8
8
|
const react_1 = __importDefault(require("react"));
|
|
9
9
|
const isMotionAnimation_1 = require("../utils/isMotionAnimation");
|
|
10
10
|
const useAnimationDuration = () => {
|
|
11
|
-
const { isReduceMotionEnabled } = (0,
|
|
11
|
+
const { isReduceMotionEnabled } = (0, useAMAContextSafe_1.useAMAContextSafe)();
|
|
12
12
|
const getAnimationDuration = react_1.default.useCallback((propertyToAnimate, duration) => {
|
|
13
13
|
return isReduceMotionEnabled && (0, isMotionAnimation_1.isMotionAnimation)(propertyToAnimate)
|
|
14
14
|
? 0
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.useReanimatedAnimationBuilder = void 0;
|
|
4
|
-
const
|
|
5
|
-
const
|
|
4
|
+
const useAMAContextSafe_1 = require("../internals/useAMAContextSafe");
|
|
5
|
+
const animationConstants_1 = require("../internals/animationConstants");
|
|
6
6
|
const react_native_reanimated_1 = require("react-native-reanimated");
|
|
7
7
|
const useReanimatedAnimationBuilder = ({ from, to, exitFrom, exitTo, duration, }) => {
|
|
8
|
-
const { isReduceMotionEnabled } = (0,
|
|
8
|
+
const { isReduceMotionEnabled } = (0, useAMAContextSafe_1.useAMAContextSafe)();
|
|
9
9
|
const animationBuilder = (initial, final) => {
|
|
10
10
|
return (values) => {
|
|
11
11
|
'worklet';
|
|
@@ -18,7 +18,7 @@ const useReanimatedAnimationBuilder = ({ from, to, exitFrom, exitTo, duration, }
|
|
|
18
18
|
// @ts-ignore
|
|
19
19
|
typeof toValue === 'string' ? values[toValue] || 0 : toValue;
|
|
20
20
|
const realDuration = isReduceMotionEnabled &&
|
|
21
|
-
|
|
21
|
+
animationConstants_1.MOTION_ANIMATIONS.includes(key)
|
|
22
22
|
? 0
|
|
23
23
|
: duration;
|
|
24
24
|
const newValue = Array.isArray(value)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MotionAnimationKey } from '
|
|
1
|
+
import type { MotionAnimationKey } from '../internals/animationConstants';
|
|
2
2
|
import { AnimationCallback, WithSpringConfig, WithTimingConfig } from 'react-native-reanimated';
|
|
3
3
|
export declare const useReanimatedTiming: () => {
|
|
4
4
|
withTiming: (propertyKey: MotionAnimationKey, toValue: number, config?: WithTimingConfig, callback?: AnimationCallback) => number;
|
|
@@ -4,12 +4,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.useReanimatedTiming = void 0;
|
|
7
|
-
const
|
|
7
|
+
const useAMAContextSafe_1 = require("../internals/useAMAContextSafe");
|
|
8
8
|
const react_1 = __importDefault(require("react"));
|
|
9
9
|
const react_native_reanimated_1 = require("react-native-reanimated");
|
|
10
10
|
const isMotionAnimation_1 = require("../utils/isMotionAnimation");
|
|
11
11
|
const useReanimatedTiming = () => {
|
|
12
|
-
const { isReduceMotionEnabled } = (0,
|
|
12
|
+
const { isReduceMotionEnabled } = (0, useAMAContextSafe_1.useAMAContextSafe)();
|
|
13
13
|
const withTiming = react_1.default.useCallback((propertyKey, toValue, config = {}, callback) => {
|
|
14
14
|
if (isReduceMotionEnabled && (0, isMotionAnimation_1.isMotionAnimation)(propertyKey)) {
|
|
15
15
|
config.duration = 0;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MOTION_ANIMATIONS = void 0;
|
|
4
|
+
exports.MOTION_ANIMATIONS = [
|
|
5
|
+
'left',
|
|
6
|
+
'bottom',
|
|
7
|
+
'top',
|
|
8
|
+
'right',
|
|
9
|
+
'transform',
|
|
10
|
+
'translateX',
|
|
11
|
+
'translateY',
|
|
12
|
+
'scaleX',
|
|
13
|
+
'scaleY',
|
|
14
|
+
'scale',
|
|
15
|
+
'width',
|
|
16
|
+
'height',
|
|
17
|
+
'padding',
|
|
18
|
+
'paddingBottom',
|
|
19
|
+
'paddingLeft',
|
|
20
|
+
'paddingRight',
|
|
21
|
+
'paddingTop',
|
|
22
|
+
'paddingVertical',
|
|
23
|
+
'paddingHorizontal',
|
|
24
|
+
'margin',
|
|
25
|
+
'marginBottom',
|
|
26
|
+
'marginLeft',
|
|
27
|
+
'marginRight',
|
|
28
|
+
'marginTop',
|
|
29
|
+
'marginHorizontal',
|
|
30
|
+
'marginVertical',
|
|
31
|
+
'rotation',
|
|
32
|
+
];
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import type { Animated } from 'react-native';
|
|
2
|
+
export declare const interpolateAnimationStates: (from: Record<string, any>, to: Record<string, any>, isReduceMotionEnabled: boolean, progressValue: Animated.Value, reduceMotionProgressValue: Animated.Value) => Record<string, any>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
+
var t = {};
|
|
4
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
+
t[p] = s[p];
|
|
6
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
+
t[p[i]] = s[p[i]];
|
|
10
|
+
}
|
|
11
|
+
return t;
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.interpolateAnimationStates = void 0;
|
|
15
|
+
const animationConstants_1 = require("./animationConstants");
|
|
16
|
+
const interpolateAnimationStates = (from, to, isReduceMotionEnabled, progressValue, reduceMotionProgressValue) => {
|
|
17
|
+
return Object.keys(from).reduce((outputAnimation, key) => {
|
|
18
|
+
const isMotionAnimation = animationConstants_1.MOTION_ANIMATIONS.includes(key);
|
|
19
|
+
const progressKey = isReduceMotionEnabled && isMotionAnimation
|
|
20
|
+
? reduceMotionProgressValue
|
|
21
|
+
: progressValue;
|
|
22
|
+
if (Array.isArray(from[key])) {
|
|
23
|
+
outputAnimation[key] = from[key].map((animationObject, index) => {
|
|
24
|
+
const _a = (0, exports.interpolateAnimationStates)(animationObject, to[key][index], isReduceMotionEnabled, progressValue, reduceMotionProgressValue), { __hasOnlyMotionAnimation } = _a, style = __rest(_a, ["__hasOnlyMotionAnimation"]);
|
|
25
|
+
outputAnimation.__hasOnlyMotionAnimation =
|
|
26
|
+
outputAnimation.__hasOnlyMotionAnimation &&
|
|
27
|
+
__hasOnlyMotionAnimation;
|
|
28
|
+
return style;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
outputAnimation[key] = progressKey.interpolate({
|
|
33
|
+
inputRange: [0, 1],
|
|
34
|
+
// @ts-ignore
|
|
35
|
+
outputRange: [from[key], to[key]],
|
|
36
|
+
});
|
|
37
|
+
outputAnimation.__hasOnlyMotionAnimation =
|
|
38
|
+
outputAnimation.__hasOnlyMotionAnimation && isMotionAnimation;
|
|
39
|
+
}
|
|
40
|
+
return outputAnimation;
|
|
41
|
+
}, { __hasOnlyMotionAnimation: true });
|
|
42
|
+
};
|
|
43
|
+
exports.interpolateAnimationStates = interpolateAnimationStates;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useAMAContextSafe = void 0;
|
|
4
|
+
const AMACore = require('@react-native-ama/core');
|
|
5
|
+
const SAFE_DEFAULTS = {
|
|
6
|
+
isReduceMotionEnabled: false,
|
|
7
|
+
};
|
|
8
|
+
const useAMAContextSafe = () => {
|
|
9
|
+
var _a, _b;
|
|
10
|
+
try {
|
|
11
|
+
return (_b = (_a = AMACore === null || AMACore === void 0 ? void 0 : AMACore.useAMAContext) === null || _a === void 0 ? void 0 : _a.call(AMACore)) !== null && _b !== void 0 ? _b : SAFE_DEFAULTS;
|
|
12
|
+
}
|
|
13
|
+
catch (_c) {
|
|
14
|
+
return SAFE_DEFAULTS;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
exports.useAMAContextSafe = useAMAContextSafe;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { type MotionAnimationKey } from '
|
|
1
|
+
import { type MotionAnimationKey } from '../internals/animationConstants';
|
|
2
2
|
export declare const isMotionAnimation: (key: MotionAnimationKey) => boolean;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.isMotionAnimation = void 0;
|
|
4
|
-
const
|
|
4
|
+
const animationConstants_1 = require("../internals/animationConstants");
|
|
5
5
|
const isMotionAnimation = (key) => {
|
|
6
|
-
return
|
|
6
|
+
return animationConstants_1.MOTION_ANIMATIONS.includes(key);
|
|
7
7
|
};
|
|
8
8
|
exports.isMotionAnimation = isMotionAnimation;
|
package/package.json
CHANGED
|
@@ -1,33 +1,53 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-native-ama/animations",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"types": "dist/index.d.ts",
|
|
6
|
-
"main": "dist/index.js",
|
|
3
|
+
"version": "2.0.0-beta.0",
|
|
4
|
+
"sideEffects": false,
|
|
7
5
|
"exports": {
|
|
8
|
-
".":
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
".": {
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"react-native": "./src/index.ts",
|
|
9
|
+
"default": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"./AnimatedContainer": {
|
|
12
|
+
"types": "./dist/components/AnimatedContainer.d.ts",
|
|
13
|
+
"default": "./dist/components/AnimatedContainer.js"
|
|
14
|
+
},
|
|
15
|
+
"./useAnimation": {
|
|
16
|
+
"types": "./dist/hooks/useAnimation.d.ts",
|
|
17
|
+
"default": "./dist/hooks/useAnimation.js"
|
|
18
|
+
},
|
|
19
|
+
"./useAnimationDuration": {
|
|
20
|
+
"types": "./dist/hooks/useAnimationDuration.d.ts",
|
|
21
|
+
"default": "./dist/hooks/useAnimationDuration.js"
|
|
22
|
+
},
|
|
23
|
+
"./useReanimatedAnimationBuilder": {
|
|
24
|
+
"types": "./dist/hooks/useReanimatedAnimationBuilder.d.ts",
|
|
25
|
+
"default": "./dist/hooks/useReanimatedAnimationBuilder.js"
|
|
26
|
+
},
|
|
27
|
+
"./useReanimatedTiming": {
|
|
28
|
+
"types": "./dist/hooks/useReanimatedTiming.d.ts",
|
|
29
|
+
"default": "./dist/hooks/useReanimatedTiming.js"
|
|
30
|
+
}
|
|
15
31
|
},
|
|
16
32
|
"files": [
|
|
17
33
|
"src",
|
|
18
34
|
"dist"
|
|
19
35
|
],
|
|
20
36
|
"scripts": {
|
|
21
|
-
"build": "rm -rf dist && tsc -p ./tsconfig.build.json",
|
|
22
|
-
"typecheck": "tsc --noEmit"
|
|
23
|
-
|
|
24
|
-
"dependencies": {
|
|
25
|
-
"@react-native-ama/core": "~1.2.0",
|
|
26
|
-
"@react-native-ama/internal": "~1.2.0"
|
|
37
|
+
"build": "rm -rf dist && ../../node_modules/.bin/tsc -p ./tsconfig.build.json",
|
|
38
|
+
"typecheck": "../../node_modules/.bin/tsc --noEmit",
|
|
39
|
+
"test": "jest"
|
|
27
40
|
},
|
|
28
41
|
"peerDependencies": {
|
|
42
|
+
"@react-native-ama/core": "~2.0.0-beta.0",
|
|
29
43
|
"react": "*",
|
|
30
|
-
"react-native": "*"
|
|
44
|
+
"react-native": "*",
|
|
45
|
+
"react-native-reanimated": ">=2.0.0"
|
|
46
|
+
},
|
|
47
|
+
"peerDependenciesMeta": {
|
|
48
|
+
"@react-native-ama/core": {
|
|
49
|
+
"optional": true
|
|
50
|
+
}
|
|
31
51
|
},
|
|
32
52
|
"keywords": [
|
|
33
53
|
"react-native",
|
|
@@ -47,5 +67,45 @@
|
|
|
47
67
|
"provenance": true
|
|
48
68
|
},
|
|
49
69
|
"author": "",
|
|
50
|
-
"license": "MIT"
|
|
70
|
+
"license": "MIT",
|
|
71
|
+
"jest": {
|
|
72
|
+
"preset": "react-native",
|
|
73
|
+
"setupFilesAfterEnv": [
|
|
74
|
+
"<rootDir>/jest.setup.js"
|
|
75
|
+
],
|
|
76
|
+
"modulePathIgnorePatterns": [
|
|
77
|
+
"<rootDir>/node_modules",
|
|
78
|
+
"<rootDir>/dist/"
|
|
79
|
+
],
|
|
80
|
+
"collectCoverageFrom": [
|
|
81
|
+
"src/**/*.{ts,tsx}",
|
|
82
|
+
"!src/index.ts"
|
|
83
|
+
],
|
|
84
|
+
"coverageThreshold": {
|
|
85
|
+
"global": {
|
|
86
|
+
"statements": 100,
|
|
87
|
+
"branches": 95,
|
|
88
|
+
"functions": 100,
|
|
89
|
+
"lines": 100
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"transformIgnorePatterns": [
|
|
93
|
+
"node_modules/(?!(react-native-reanimated|expo|@expo)/)"
|
|
94
|
+
],
|
|
95
|
+
"transform": {
|
|
96
|
+
"\\.[jt]sx?$": "babel-jest"
|
|
97
|
+
},
|
|
98
|
+
"moduleDirectories": [
|
|
99
|
+
"node_modules",
|
|
100
|
+
"../../playground/node_modules"
|
|
101
|
+
],
|
|
102
|
+
"moduleNameMapper": {
|
|
103
|
+
"@react-native/js-polyfills/error-guard": "<rootDir>/../../jest-mocks/error-guard.js",
|
|
104
|
+
"^(\\.{1,2}/)*ama\\.config\\.json$": "<rootDir>/../../jest-mocks/ama-config.js",
|
|
105
|
+
"^(\\.{1,2}/)*ReactNativeAmaModule$": "<rootDir>/../../jest-mocks/ReactNativeAmaModule.js",
|
|
106
|
+
"^(\\.{1,2}/)*ReactNativeAmaView(\\.web)?$": "<rootDir>/../../jest-mocks/ReactNativeAmaModule.js",
|
|
107
|
+
"^@react-native-ama/core$": "<rootDir>/../../jest-mocks/ama-core.js"
|
|
108
|
+
},
|
|
109
|
+
"verbose": true
|
|
110
|
+
}
|
|
51
111
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { AutofocusContainer } from '@react-native-ama/core';
|
|
2
2
|
import { render } from '@testing-library/react-native';
|
|
3
3
|
import * as React from 'react';
|
|
4
|
-
|
|
5
4
|
import * as UseReanimatedAnimationBuilder from '../hooks/useReanimatedAnimationBuilder';
|
|
6
5
|
import { AnimatedContainer } from './AnimatedContainer';
|
|
7
6
|
|
|
@@ -27,14 +26,16 @@ describe('AnimatedContainer', () => {
|
|
|
27
26
|
expect(useReanimatedAnimationBuilder).toHaveBeenCalledWith({
|
|
28
27
|
from,
|
|
29
28
|
to,
|
|
29
|
+
exitFrom: undefined,
|
|
30
|
+
exitTo: undefined,
|
|
30
31
|
duration: 300,
|
|
31
|
-
exit: undefined,
|
|
32
32
|
});
|
|
33
33
|
|
|
34
34
|
expect(getByTestId('test-id').props).toEqual({
|
|
35
35
|
children: undefined,
|
|
36
36
|
entering: 'entering',
|
|
37
37
|
exiting: 'exiting',
|
|
38
|
+
ref: null,
|
|
38
39
|
style: undefined,
|
|
39
40
|
testID: 'test-id',
|
|
40
41
|
});
|
|
@@ -52,14 +53,16 @@ describe('AnimatedContainer', () => {
|
|
|
52
53
|
expect(useReanimatedAnimationBuilder).toHaveBeenCalledWith({
|
|
53
54
|
from,
|
|
54
55
|
to,
|
|
56
|
+
exitFrom: undefined,
|
|
57
|
+
exitTo: undefined,
|
|
55
58
|
duration: 600,
|
|
56
|
-
exit: undefined,
|
|
57
59
|
});
|
|
58
60
|
|
|
59
61
|
expect(getByTestId('test-id').props).toEqual({
|
|
60
62
|
children: undefined,
|
|
61
63
|
entering: 'entering',
|
|
62
64
|
exiting: 'exiting',
|
|
65
|
+
ref: null,
|
|
63
66
|
style: undefined,
|
|
64
67
|
testID: 'test-id',
|
|
65
68
|
});
|
|
@@ -81,15 +84,6 @@ describe('AnimatedContainer', () => {
|
|
|
81
84
|
});
|
|
82
85
|
|
|
83
86
|
jest.mock('../hooks/useReanimatedAnimationBuilder');
|
|
84
|
-
jest.mock('../hooks/useFocus', () => {
|
|
85
|
-
return {
|
|
86
|
-
useFocus: () => {
|
|
87
|
-
return {
|
|
88
|
-
setFocus: jest.fn(),
|
|
89
|
-
};
|
|
90
|
-
},
|
|
91
|
-
};
|
|
92
|
-
});
|
|
93
87
|
|
|
94
88
|
jest.mock('react-native-reanimated', () => {
|
|
95
89
|
const { View } = require('react-native');
|
|
@@ -3,7 +3,6 @@ import * as React from 'react';
|
|
|
3
3
|
import type { PropsWithChildren } from 'react';
|
|
4
4
|
import type { ViewProps } from 'react-native';
|
|
5
5
|
import Animated, { AnimateProps } from 'react-native-reanimated';
|
|
6
|
-
|
|
7
6
|
import {
|
|
8
7
|
AnimatedEntryViewStyle,
|
|
9
8
|
AnimatedExitViewStyle,
|
|
@@ -55,7 +54,8 @@ export const AnimatedContainer = React.forwardRef<
|
|
|
55
54
|
entering={entering}
|
|
56
55
|
exiting={exiting}
|
|
57
56
|
ref={forwardRef}
|
|
58
|
-
{...rest}
|
|
57
|
+
{...rest}
|
|
58
|
+
>
|
|
59
59
|
{children}
|
|
60
60
|
</Animated.View>
|
|
61
61
|
</Wrapper>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { interpolateAnimationStates } from '
|
|
1
|
+
import { useAMAContextSafe } from '../internals/useAMAContextSafe';
|
|
2
|
+
import { interpolateAnimationStates } from '../internals/animationStates';
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
import type { ViewStyle } from 'react-native';
|
|
5
5
|
import { Animated } from 'react-native';
|
|
@@ -19,7 +19,7 @@ export const useAnimation = ({
|
|
|
19
19
|
to,
|
|
20
20
|
skipIfReduceMotionEnabled = false,
|
|
21
21
|
}: UseAnimation) => {
|
|
22
|
-
const { isReduceMotionEnabled } =
|
|
22
|
+
const { isReduceMotionEnabled } = useAMAContextSafe();
|
|
23
23
|
const progress = React.useRef(new Animated.Value(0)).current;
|
|
24
24
|
const reduceMotionProgress = React.useRef(new Animated.Value(0)).current;
|
|
25
25
|
const hasOnlyMotionAnimation = React.useRef(false);
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useAMAContextSafe } from '../internals/useAMAContextSafe';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import type { ViewStyle } from 'react-native';
|
|
4
|
-
|
|
5
4
|
import { isMotionAnimation } from '../utils/isMotionAnimation';
|
|
6
5
|
|
|
7
6
|
export const useAnimationDuration = () => {
|
|
8
|
-
const { isReduceMotionEnabled } =
|
|
7
|
+
const { isReduceMotionEnabled } = useAMAContextSafe();
|
|
9
8
|
|
|
10
9
|
const getAnimationDuration = React.useCallback(
|
|
11
10
|
(propertyToAnimate: keyof ViewStyle, duration: number) => {
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import * as AMAProvider from '@react-native-ama/core';
|
|
2
2
|
import { renderHook } from '@testing-library/react-native';
|
|
3
|
-
|
|
4
3
|
import { useReanimatedAnimationBuilder } from './useReanimatedAnimationBuilder';
|
|
5
4
|
|
|
6
5
|
beforeEach(() => {
|
|
7
6
|
jest.clearAllMocks();
|
|
8
7
|
});
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
var withTiming: jest.Mock;
|
|
11
10
|
|
|
12
11
|
describe('useReanimatedAnimationBuilder', () => {
|
|
13
12
|
describe('entering animation', () => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { MOTION_ANIMATIONS } from '
|
|
1
|
+
import { useAMAContextSafe } from '../internals/useAMAContextSafe';
|
|
2
|
+
import { MOTION_ANIMATIONS } from '../internals/animationConstants';
|
|
3
3
|
import type { ViewProps, ViewStyle } from 'react-native';
|
|
4
4
|
import {
|
|
5
5
|
EntryAnimationsValues,
|
|
@@ -26,7 +26,7 @@ export const useReanimatedAnimationBuilder = ({
|
|
|
26
26
|
exitTo,
|
|
27
27
|
duration,
|
|
28
28
|
}: UseReanimatedAnimationBuilder) => {
|
|
29
|
-
const { isReduceMotionEnabled } =
|
|
29
|
+
const { isReduceMotionEnabled } = useAMAContextSafe();
|
|
30
30
|
|
|
31
31
|
const animationBuilder = <InitialValues, FinalValues>(
|
|
32
32
|
initial: InitialValues,
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as AMAProvider from '@react-native-ama/core';
|
|
2
|
-
import { MOTION_ANIMATIONS } from '
|
|
2
|
+
import { MOTION_ANIMATIONS } from '../internals/animationConstants';
|
|
3
3
|
import { renderHook } from '@testing-library/react-native';
|
|
4
|
-
|
|
5
4
|
import { useReanimatedTiming } from './useReanimatedTiming';
|
|
6
5
|
|
|
7
6
|
beforeEach(() => {
|
|
@@ -79,8 +78,8 @@ const amaContextValues = {
|
|
|
79
78
|
isScreenReaderEnabled: false,
|
|
80
79
|
};
|
|
81
80
|
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
var withSpring: jest.Mock;
|
|
82
|
+
var withTiming: jest.Mock;
|
|
84
83
|
|
|
85
84
|
function mockReanimated() {
|
|
86
85
|
withSpring = jest.fn();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import type { MotionAnimationKey } from '../internals/animationConstants';
|
|
2
|
+
import { useAMAContextSafe } from '../internals/useAMAContextSafe';
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import {
|
|
5
5
|
AnimationCallback,
|
|
@@ -8,11 +8,10 @@ import {
|
|
|
8
8
|
WithSpringConfig,
|
|
9
9
|
WithTimingConfig,
|
|
10
10
|
} from 'react-native-reanimated';
|
|
11
|
-
|
|
12
11
|
import { isMotionAnimation } from '../utils/isMotionAnimation';
|
|
13
12
|
|
|
14
13
|
export const useReanimatedTiming = () => {
|
|
15
|
-
const { isReduceMotionEnabled } =
|
|
14
|
+
const { isReduceMotionEnabled } = useAMAContextSafe();
|
|
16
15
|
|
|
17
16
|
const withTiming = React.useCallback(
|
|
18
17
|
(
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ViewStyle } from 'react-native';
|
|
2
|
+
|
|
3
|
+
export type MotionAnimationKey = Partial<keyof ViewStyle> | 'scale';
|
|
4
|
+
|
|
5
|
+
export const MOTION_ANIMATIONS: MotionAnimationKey[] = [
|
|
6
|
+
'left',
|
|
7
|
+
'bottom',
|
|
8
|
+
'top',
|
|
9
|
+
'right',
|
|
10
|
+
'transform',
|
|
11
|
+
'translateX',
|
|
12
|
+
'translateY',
|
|
13
|
+
'scaleX',
|
|
14
|
+
'scaleY',
|
|
15
|
+
'scale',
|
|
16
|
+
'width',
|
|
17
|
+
'height',
|
|
18
|
+
'padding',
|
|
19
|
+
'paddingBottom',
|
|
20
|
+
'paddingLeft',
|
|
21
|
+
'paddingRight',
|
|
22
|
+
'paddingTop',
|
|
23
|
+
'paddingVertical',
|
|
24
|
+
'paddingHorizontal',
|
|
25
|
+
'margin',
|
|
26
|
+
'marginBottom',
|
|
27
|
+
'marginLeft',
|
|
28
|
+
'marginRight',
|
|
29
|
+
'marginTop',
|
|
30
|
+
'marginHorizontal',
|
|
31
|
+
'marginVertical',
|
|
32
|
+
'rotation',
|
|
33
|
+
];
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Animated } from 'react-native';
|
|
2
|
+
import { interpolateAnimationStates } from './animationStates';
|
|
3
|
+
|
|
4
|
+
const makeValue = () => {
|
|
5
|
+
const value = new Animated.Value(0);
|
|
6
|
+
// react-native mock exposes .interpolate; ensure it returns something
|
|
7
|
+
return value;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
describe('interpolateAnimationStates', () => {
|
|
11
|
+
it('interpolates scalar properties using the progress value', () => {
|
|
12
|
+
const progress = makeValue();
|
|
13
|
+
const reduceMotionProgress = makeValue();
|
|
14
|
+
const result = interpolateAnimationStates(
|
|
15
|
+
{ opacity: 0 },
|
|
16
|
+
{ opacity: 1 },
|
|
17
|
+
false,
|
|
18
|
+
progress,
|
|
19
|
+
reduceMotionProgress,
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
expect(result).toHaveProperty('opacity');
|
|
23
|
+
expect(result.__hasOnlyMotionAnimation).toBe(false);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('uses reduceMotionProgressValue for motion properties when reduce motion is enabled', () => {
|
|
27
|
+
const progress = makeValue();
|
|
28
|
+
const reduceMotionProgress = makeValue();
|
|
29
|
+
const result = interpolateAnimationStates(
|
|
30
|
+
{ translateX: 0 },
|
|
31
|
+
{ translateX: 100 },
|
|
32
|
+
true,
|
|
33
|
+
progress,
|
|
34
|
+
reduceMotionProgress,
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
expect(result).toHaveProperty('translateX');
|
|
38
|
+
expect(result.__hasOnlyMotionAnimation).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('handles array values (transform arrays) recursively', () => {
|
|
42
|
+
const progress = makeValue();
|
|
43
|
+
const reduceMotionProgress = makeValue();
|
|
44
|
+
const result = interpolateAnimationStates(
|
|
45
|
+
{ transform: [{ translateX: 0 }, { scale: 1 }] },
|
|
46
|
+
{ transform: [{ translateX: 100 }, { scale: 2 }] },
|
|
47
|
+
false,
|
|
48
|
+
progress,
|
|
49
|
+
reduceMotionProgress,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
expect(Array.isArray(result.transform)).toBe(true);
|
|
53
|
+
expect(result.transform).toHaveLength(2);
|
|
54
|
+
expect(result.transform[0]).toHaveProperty('translateX');
|
|
55
|
+
expect(result.transform[1]).toHaveProperty('scale');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('sets __hasOnlyMotionAnimation false when mixing motion and non-motion props', () => {
|
|
59
|
+
const progress = makeValue();
|
|
60
|
+
const reduceMotionProgress = makeValue();
|
|
61
|
+
const result = interpolateAnimationStates(
|
|
62
|
+
{ opacity: 0, translateX: 0 },
|
|
63
|
+
{ opacity: 1, translateX: 100 },
|
|
64
|
+
false,
|
|
65
|
+
progress,
|
|
66
|
+
reduceMotionProgress,
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
expect(result.__hasOnlyMotionAnimation).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { Animated, ViewStyle } from 'react-native';
|
|
2
|
+
import { MOTION_ANIMATIONS } from './animationConstants';
|
|
3
|
+
|
|
4
|
+
export const interpolateAnimationStates = (
|
|
5
|
+
from: Record<string, any>,
|
|
6
|
+
to: Record<string, any>,
|
|
7
|
+
isReduceMotionEnabled: boolean,
|
|
8
|
+
progressValue: Animated.Value,
|
|
9
|
+
reduceMotionProgressValue: Animated.Value,
|
|
10
|
+
): Record<string, any> => {
|
|
11
|
+
return Object.keys(from).reduce(
|
|
12
|
+
(outputAnimation, key) => {
|
|
13
|
+
const isMotionAnimation = MOTION_ANIMATIONS.includes(
|
|
14
|
+
key as keyof ViewStyle,
|
|
15
|
+
);
|
|
16
|
+
const progressKey =
|
|
17
|
+
isReduceMotionEnabled && isMotionAnimation
|
|
18
|
+
? reduceMotionProgressValue
|
|
19
|
+
: progressValue;
|
|
20
|
+
|
|
21
|
+
if (Array.isArray(from[key])) {
|
|
22
|
+
outputAnimation[key] = from[key].map(
|
|
23
|
+
(animationObject: Record<string, any>, index: number) => {
|
|
24
|
+
const { __hasOnlyMotionAnimation, ...style } =
|
|
25
|
+
interpolateAnimationStates(
|
|
26
|
+
animationObject,
|
|
27
|
+
to[key][index],
|
|
28
|
+
isReduceMotionEnabled,
|
|
29
|
+
progressValue,
|
|
30
|
+
reduceMotionProgressValue,
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
outputAnimation.__hasOnlyMotionAnimation =
|
|
34
|
+
outputAnimation.__hasOnlyMotionAnimation &&
|
|
35
|
+
__hasOnlyMotionAnimation;
|
|
36
|
+
|
|
37
|
+
return style;
|
|
38
|
+
},
|
|
39
|
+
);
|
|
40
|
+
} else {
|
|
41
|
+
outputAnimation[key] = progressKey.interpolate({
|
|
42
|
+
inputRange: [0, 1],
|
|
43
|
+
// @ts-ignore
|
|
44
|
+
outputRange: [from[key], to[key]],
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
outputAnimation.__hasOnlyMotionAnimation =
|
|
48
|
+
outputAnimation.__hasOnlyMotionAnimation && isMotionAnimation;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return outputAnimation;
|
|
52
|
+
},
|
|
53
|
+
{ __hasOnlyMotionAnimation: true } as Record<string, any>,
|
|
54
|
+
);
|
|
55
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react-native';
|
|
2
|
+
|
|
3
|
+
beforeEach(() => {
|
|
4
|
+
jest.clearAllMocks();
|
|
5
|
+
jest.resetModules();
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
describe('useAMAContextSafe', () => {
|
|
9
|
+
it('returns isReduceMotionEnabled from core when core is present', () => {
|
|
10
|
+
jest.doMock('@react-native-ama/core', () => ({
|
|
11
|
+
useAMAContext: () => ({ isReduceMotionEnabled: true }),
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
const { useAMAContextSafe: safe } =
|
|
15
|
+
require('./useAMAContextSafe') as typeof import('./useAMAContextSafe');
|
|
16
|
+
const { result } = renderHook(() => safe());
|
|
17
|
+
|
|
18
|
+
expect(result.current.isReduceMotionEnabled).toBe(true);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('returns isReduceMotionEnabled: false when core is absent', () => {
|
|
22
|
+
jest.doMock('@react-native-ama/core', () => null);
|
|
23
|
+
|
|
24
|
+
const { useAMAContextSafe: safe } =
|
|
25
|
+
require('./useAMAContextSafe') as typeof import('./useAMAContextSafe');
|
|
26
|
+
const { result } = renderHook(() => safe());
|
|
27
|
+
|
|
28
|
+
expect(result.current.isReduceMotionEnabled).toBe(false);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('returns isReduceMotionEnabled: false when useAMAContext throws', () => {
|
|
32
|
+
jest.doMock('@react-native-ama/core', () => ({
|
|
33
|
+
useAMAContext: () => {
|
|
34
|
+
throw new Error('Please wrap your app with <AMAProvider />');
|
|
35
|
+
},
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
const { useAMAContextSafe: safe } =
|
|
39
|
+
require('./useAMAContextSafe') as typeof import('./useAMAContextSafe');
|
|
40
|
+
const { result } = renderHook(() => safe());
|
|
41
|
+
|
|
42
|
+
expect(result.current.isReduceMotionEnabled).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const AMACore = require('@react-native-ama/core');
|
|
2
|
+
|
|
3
|
+
const SAFE_DEFAULTS = {
|
|
4
|
+
isReduceMotionEnabled: false,
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const useAMAContextSafe = (): { isReduceMotionEnabled: boolean } => {
|
|
8
|
+
try {
|
|
9
|
+
return AMACore?.useAMAContext?.() ?? SAFE_DEFAULTS;
|
|
10
|
+
} catch {
|
|
11
|
+
return SAFE_DEFAULTS;
|
|
12
|
+
}
|
|
13
|
+
};
|