UNPKG

react-native-reanimated

Version:

More powerful alternative to Animated library for React Native.

119 lines (102 loc) 3.56 kB
'use strict'; import { ReanimatedError } from '../errors'; import type { UnknownRecord, ValueProcessor, ValueProcessorContext, } from '../types'; import { ValueProcessorTarget } from '../types'; import { isRecord } from '../utils'; const MAX_PROCESS_DEPTH = 10; type CreatePropsBuilderParams<TPropsConfig> = { config: TPropsConfig; processConfigValue: ( configValue: TPropsConfig[keyof TPropsConfig], propertyKey: keyof TPropsConfig ) => ValueProcessor | TPropsConfig[keyof TPropsConfig] | undefined; }; type PropsBuilderResult<TProps> = { build( props: Partial<TProps>, options?: { target?: ValueProcessorTarget; includeUnprocessed?: boolean; } ): UnknownRecord; }; export default function createPropsBuilder< TProps extends UnknownRecord, TPropsConfig extends UnknownRecord, >({ processConfigValue, config, }: CreatePropsBuilderParams<TPropsConfig>): PropsBuilderResult<TProps> { const processedConfig = Object.entries(config).reduce< Record<string, ValueProcessor | true> >((acc, [key, configValue]) => { let processedValue: ReturnType<typeof processConfigValue> = configValue as TPropsConfig[keyof TPropsConfig]; let depth = 0; while (processedValue) { if (++depth > MAX_PROCESS_DEPTH) { throw new ReanimatedError( `Max process depth for props builder reached for property ${key}` ); } // If the value returned from the processConfigValue function is a function, // that means it's a terminal value that will be used to process the value // of the property. We can break the loop at this point. if (typeof processedValue === 'function' || processedValue === true) { acc[key] = processedValue as ValueProcessor | true; break; } // Otherwise, we need to continue processing the value. processedValue = processConfigValue( processedValue, key as keyof TPropsConfig ); } return acc; }, {}); return { build(props, options) { 'worklet'; const context: ValueProcessorContext = { target: options?.target ?? ValueProcessorTarget.Default, }; const result: UnknownRecord = {}; for (const property in props) { const configValue = processedConfig[property]; const value = props[property]; // Simple case, no need for processing if (configValue === true) { result[property] = value; continue; } // Prop is not supported or value is undefined if (!configValue || value === undefined) { if (options?.includeUnprocessed) { result[property] = value; } continue; } const processedValue = configValue(value, context); if (isRecord(processedValue) && !isRecord(value)) { // The value processor may return multiple values for a single property // as a record of new property names and processed values. In such a case, // we want to store properties from this record in the result object only // if they are not already present (we don't want to override properties // explicitly specified by the user). for (const processedKey in processedValue) { if (!(processedKey in result)) { result[processedKey] = processedValue[processedKey]; } } } else { result[property] = processedValue; } } return result; }, }; }