UNPKG

react-native-reanimated

Version:

More powerful alternative to Animated library for React Native.

179 lines (156 loc) 5.47 kB
'use strict'; import { ReanimatedError } from '../../../../../common'; import type { AnyRecord, CSSTransitionProperties, CSSTransitionProperty, } from '../../../../types'; import { areArraysEqual, convertPropertyToArray, deepEqual, } from '../../../../utils'; import type { NormalizedCSSTransitionConfig, NormalizedCSSTransitionConfigUpdates, NormalizedSingleCSSTransitionSettings, } from '../../types'; import { normalizeDelay, normalizeDuration, normalizeTimingFunction, } from '../common'; import { normalizeTransitionBehavior } from './settings'; import type { ExpandedCSSTransitionConfigProperties } from './shorthand'; import { createEmptyTransitionConfig, parseTransitionShorthand, } from './shorthand'; export const ERROR_MESSAGES = { invalidTransitionProperty: ( transitionProperty: CSSTransitionProperty | undefined | string[] ) => `Invalid transition property "${JSON.stringify(transitionProperty)}"`, }; function getExpandedConfigProperties( config: CSSTransitionProperties ): ExpandedCSSTransitionConfigProperties { const result: AnyRecord = config.transition ? parseTransitionShorthand(config.transition) : createEmptyTransitionConfig(); for (const [key, value] of Object.entries(config)) { result[key] = convertPropertyToArray(value); } return result as ExpandedCSSTransitionConfigProperties; } const hasTransition = ({ transitionProperty, ...rest }: ExpandedCSSTransitionConfigProperties) => { if (transitionProperty.length) { const hasNone = transitionProperty[0] === 'none'; // We allow either all values to be 'none' or none of them to be 'none' if (transitionProperty.some((prop) => (prop === 'none') !== hasNone)) { throw new ReanimatedError( ERROR_MESSAGES.invalidTransitionProperty(transitionProperty) ); } return !hasNone; } // transitionProperty defaults to 'all' if not specified but there are // other transition properties return Object.values(rest).some((value) => value.length); }; export function normalizeCSSTransitionProperties( config: CSSTransitionProperties ): NormalizedCSSTransitionConfig | null { const expandedProperties = getExpandedConfigProperties(config); if (!hasTransition(expandedProperties)) { return null; } const { transitionProperty, transitionDuration, transitionTimingFunction, transitionDelay, transitionBehavior, } = expandedProperties; const specificProperties: string[] = []; let allPropertiesTransition = false; const settings: Record<string, NormalizedSingleCSSTransitionSettings> = {}; if (!transitionProperty.length) { // For cases when transition property hasn't been explicitly specified // (e.g. when only the transitionDuration is set) transitionProperty.push('all'); } // Go from the last to the first property to ensure that the last // one entry for the same property is used without having to override // it multiple times if specified more than once (we just take the last // occurrence and ignore remaining ones) for (let i = transitionProperty.length - 1; i >= 0; i--) { const property = transitionProperty[i]; // Continue if there was a prop with the same name specified later // (we don't want to override the last occurrence of the property) if (settings?.[property]) { continue; } if (property === 'all') { allPropertiesTransition = true; } else { specificProperties.push(property); } settings[property] = { duration: normalizeDuration( transitionDuration[i % transitionDuration.length] ), timingFunction: normalizeTimingFunction( transitionTimingFunction[i % transitionTimingFunction.length] ), delay: normalizeDelay(transitionDelay[i % transitionDelay.length]), allowDiscrete: normalizeTransitionBehavior( transitionBehavior[i % transitionBehavior.length] ), }; // 'all' transition property overrides all properties before it, // so we don't need to process them if (allPropertiesTransition) { break; } } return { properties: allPropertiesTransition ? 'all' : specificProperties.reverse(), settings, }; } export function getNormalizedCSSTransitionConfigUpdates( oldConfig: NormalizedCSSTransitionConfig, newConfig: NormalizedCSSTransitionConfig ): NormalizedCSSTransitionConfigUpdates { const configUpdates: NormalizedCSSTransitionConfigUpdates = {}; if ( oldConfig.properties !== newConfig.properties && (!Array.isArray(oldConfig.properties) || !Array.isArray(newConfig.properties) || !areArraysEqual(oldConfig.properties, newConfig.properties)) ) { configUpdates.properties = newConfig.properties; } const newSettingsKeys = Object.keys(newConfig.settings); const oldSettingsKeys = Object.keys(oldConfig.settings); if (newSettingsKeys.length !== oldSettingsKeys.length) { configUpdates.settings = newConfig.settings; } else { for (const key of newSettingsKeys) { if ( !oldConfig.settings[key] || // TODO - think of a better way to compare settings (necessary for // timing functions comparison). Maybe add some custom way instead // of deepEqual !deepEqual(oldConfig.settings[key], newConfig.settings[key]) ) { configUpdates.settings = newConfig.settings; break; } } } return configUpdates; }