amis-rpc-design/node_modules/react-native/Libraries/StyleSheet/processTransform.js
2023-10-07 19:42:30 +08:00

280 lines
7.8 KiB
JavaScript

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow
*/
'use strict';
const stringifySafe = require('../Utilities/stringifySafe').default;
const invariant = require('invariant');
/**
* Generate a transform matrix based on the provided transforms, and use that
* within the style object instead.
*
* This allows us to provide an API that is similar to CSS, where transforms may
* be applied in an arbitrary order, and yet have a universal, singular
* interface to native code.
*/
function processTransform(
transform: Array<Object> | string,
): Array<Object> | Array<number> {
if (typeof transform === 'string') {
const regex = new RegExp(/(\w+)\(([^)]+)\)/g);
let transformArray: Array<Object> = [];
let matches;
while ((matches = regex.exec(transform))) {
const {key, value} = _getKeyAndValueFromCSSTransform(
matches[1],
matches[2],
);
if (value !== undefined) {
transformArray.push({[key]: value});
}
}
transform = transformArray;
}
if (__DEV__) {
_validateTransforms(transform);
}
return transform;
}
const _getKeyAndValueFromCSSTransform: (
key:
| string
| $TEMPORARY$string<'matrix'>
| $TEMPORARY$string<'perspective'>
| $TEMPORARY$string<'rotate'>
| $TEMPORARY$string<'rotateX'>
| $TEMPORARY$string<'rotateY'>
| $TEMPORARY$string<'rotateZ'>
| $TEMPORARY$string<'scale'>
| $TEMPORARY$string<'scaleX'>
| $TEMPORARY$string<'scaleY'>
| $TEMPORARY$string<'skewX'>
| $TEMPORARY$string<'skewY'>
| $TEMPORARY$string<'translate'>
| $TEMPORARY$string<'translate3d'>
| $TEMPORARY$string<'translateX'>
| $TEMPORARY$string<'translateY'>,
args: string,
) => {key: string, value?: number[] | number | string} = (key, args) => {
const argsWithUnitsRegex = new RegExp(/([+-]?\d+(\.\d+)?)([a-zA-Z]+)?/g);
switch (key) {
case 'matrix':
return {key, value: args.match(/[+-]?\d+(\.\d+)?/g)?.map(Number)};
case 'translate':
case 'translate3d':
const parsedArgs = [];
let missingUnitOfMeasurement = false;
let matches;
while ((matches = argsWithUnitsRegex.exec(args))) {
const value = Number(matches[1]);
const unitOfMeasurement = matches[3];
if (value !== 0 && !unitOfMeasurement) {
missingUnitOfMeasurement = true;
}
parsedArgs.push(value);
}
if (__DEV__) {
invariant(
!missingUnitOfMeasurement,
`Transform with key ${key} must have units unless the provided value is 0, found %s`,
`${key}(${args})`,
);
if (key === 'translate') {
invariant(
parsedArgs?.length === 1 || parsedArgs?.length === 2,
'Transform with key translate must be an string with 1 or 2 parameters, found %s: %s',
parsedArgs?.length,
`${key}(${args})`,
);
} else {
invariant(
parsedArgs?.length === 3,
'Transform with key translate3d must be an string with 3 parameters, found %s: %s',
parsedArgs?.length,
`${key}(${args})`,
);
}
}
if (parsedArgs?.length === 1) {
parsedArgs.push(0);
}
return {key: 'translate', value: parsedArgs};
case 'translateX':
case 'translateY':
case 'perspective':
const argMatches = argsWithUnitsRegex.exec(args);
if (!argMatches?.length) {
return {key, value: undefined};
}
const value = Number(argMatches[1]);
const unitOfMeasurement = argMatches[3];
if (__DEV__) {
invariant(
value === 0 || unitOfMeasurement,
`Transform with key ${key} must have units unless the provided value is 0, found %s`,
`${key}(${args})`,
);
}
return {key, value};
default:
return {key, value: !isNaN(args) ? Number(args) : args};
}
};
function _validateTransforms(transform: Array<Object>): void {
transform.forEach(transformation => {
const keys = Object.keys(transformation);
invariant(
keys.length === 1,
'You must specify exactly one property per transform object. Passed properties: %s',
stringifySafe(transformation),
);
const key = keys[0];
const value = transformation[key];
_validateTransform(key, value, transformation);
});
}
function _validateTransform(
key:
| string
| $TEMPORARY$string<'matrix'>
| $TEMPORARY$string<'perspective'>
| $TEMPORARY$string<'rotate'>
| $TEMPORARY$string<'rotateX'>
| $TEMPORARY$string<'rotateY'>
| $TEMPORARY$string<'rotateZ'>
| $TEMPORARY$string<'scale'>
| $TEMPORARY$string<'scaleX'>
| $TEMPORARY$string<'scaleY'>
| $TEMPORARY$string<'skewX'>
| $TEMPORARY$string<'skewY'>
| $TEMPORARY$string<'translate'>
| $TEMPORARY$string<'translateX'>
| $TEMPORARY$string<'translateY'>,
value: any | number | string,
transformation: any,
) {
invariant(
!value.getValue,
'You passed an Animated.Value to a normal component. ' +
'You need to wrap that component in an Animated. For example, ' +
'replace <View /> by <Animated.View />.',
);
const multivalueTransforms = ['matrix', 'translate'];
if (multivalueTransforms.indexOf(key) !== -1) {
invariant(
Array.isArray(value),
'Transform with key of %s must have an array as the value: %s',
key,
stringifySafe(transformation),
);
}
switch (key) {
case 'matrix':
invariant(
value.length === 9 || value.length === 16,
'Matrix transform must have a length of 9 (2d) or 16 (3d). ' +
'Provided matrix has a length of %s: %s',
/* $FlowFixMe[prop-missing] (>=0.84.0 site=react_native_fb) This
* comment suppresses an error found when Flow v0.84 was deployed. To
* see the error, delete this comment and run Flow. */
value.length,
stringifySafe(transformation),
);
break;
case 'translate':
invariant(
value.length === 2 || value.length === 3,
'Transform with key translate must be an array of length 2 or 3, found %s: %s',
/* $FlowFixMe[prop-missing] (>=0.84.0 site=react_native_fb) This
* comment suppresses an error found when Flow v0.84 was deployed. To
* see the error, delete this comment and run Flow. */
value.length,
stringifySafe(transformation),
);
break;
case 'rotateX':
case 'rotateY':
case 'rotateZ':
case 'rotate':
case 'skewX':
case 'skewY':
invariant(
typeof value === 'string',
'Transform with key of "%s" must be a string: %s',
key,
stringifySafe(transformation),
);
invariant(
value.indexOf('deg') > -1 || value.indexOf('rad') > -1,
'Rotate transform must be expressed in degrees (deg) or radians ' +
'(rad): %s',
stringifySafe(transformation),
);
break;
case 'perspective':
invariant(
typeof value === 'number',
'Transform with key of "%s" must be a number: %s',
key,
stringifySafe(transformation),
);
invariant(
value !== 0,
'Transform with key of "%s" cannot be zero: %s',
key,
stringifySafe(transformation),
);
break;
case 'translateX':
case 'translateY':
case 'scale':
case 'scaleX':
case 'scaleY':
invariant(
typeof value === 'number',
'Transform with key of "%s" must be a number: %s',
key,
stringifySafe(transformation),
);
break;
default:
invariant(
false,
'Invalid transform %s: %s',
key,
stringifySafe(transformation),
);
}
}
module.exports = processTransform;