UNPKG

react-native-gesture-handler

Version:

Declarative API exposing native platform touch and gesture system to React Native

181 lines (161 loc) 5.41 kB
import { useMemo } from 'react'; import { Reanimated } from '../../../handlers/gestures/reanimatedWrapper'; import { tagMessage } from '../../../utils'; import type { BaseGestureConfig, ExcludeInternalConfigProps, SingleGestureName, } from '../../types'; import { isNativeAnimatedEvent, shouldHandleTouchEvents } from './eventUtils'; import { allowedNativeProps, EMPTY_WHITE_LIST, PropsToFilter, PropsWhiteLists, } from './propsWhiteList'; import { hasWorkletEventHandlers, maybeUnpackValue } from './reanimatedUtils'; export function resolveInternalConfigProps< TConfig extends object, THandlerData, TExtendedHandlerData extends THandlerData, >(config: BaseGestureConfig<TConfig, THandlerData, TExtendedHandlerData>) { if ( __DEV__ && isNativeAnimatedEvent(config.onUpdate) && !config.useAnimated ) { console.warn( tagMessage( 'You are using Animated.event in onUpdate without setting useAnimated to true. ' + 'This may lead to unexpected behavior. If you intend to use Animated.event, ' + 'please set useAnimated to true in the gesture config.' ) ); } config.dispatchesAnimatedEvents = config.useAnimated || isNativeAnimatedEvent(config.onUpdate); // Validate that the user is not trying to mix Animated and Reanimated before updating the config. if ( __DEV__ && config.dispatchesAnimatedEvents && (config.disableReanimated === false || config.runOnJS === false) ) { throw new Error( tagMessage( 'Animated cannot be used together with Reanimated in the same gesture. Please choose either Animated or Reanimated for handling gesture events.' ) ); } if (config.dispatchesAnimatedEvents) { config.disableReanimated = true; } config.shouldUseReanimatedDetector = !config.disableReanimated && Reanimated !== undefined && hasWorkletEventHandlers(config) && !config.dispatchesAnimatedEvents; config.needsPointerData = shouldHandleTouchEvents(config); } export function prepareConfigForNativeSide< TConfig extends object, THandlerData, TExtendedHandlerData extends THandlerData, >( handlerType: SingleGestureName, config: BaseGestureConfig<TConfig, THandlerData, TExtendedHandlerData> ) { // @ts-ignore Seems like TypeScript can't infer the type here properly because of generic const filteredConfig: BaseGestureConfig< TConfig, THandlerData, TExtendedHandlerData > = { dispatchesReanimatedEvents: config.shouldUseReanimatedDetector && !maybeUnpackValue(config.runOnJS), }; const handlerPropsWhiteList = PropsWhiteLists.get(handlerType) ?? EMPTY_WHITE_LIST; for (const [key, value] of Object.entries(config)) { // @ts-ignore That's the point, we want to see if key exists in the whitelists if (allowedNativeProps.has(key) || handlerPropsWhiteList.has(key)) { (filteredConfig as Record<string, unknown>)[key] = Reanimated?.isSharedValue(value) ? value.value : value; } else if (PropsToFilter.has(key)) { continue; } else { console.warn( tagMessage( `${key} is not a valid property for ${handlerType} and will be ignored.` ) ); } } return filteredConfig; } function cloneConfig< TConfig, THandlerData, TExtendedHandlerData extends THandlerData, >( config: ExcludeInternalConfigProps< BaseGestureConfig<TConfig, THandlerData, TExtendedHandlerData> > ): BaseGestureConfig<TConfig, THandlerData, TExtendedHandlerData> { return { ...config } as BaseGestureConfig< TConfig, THandlerData, TExtendedHandlerData >; } function remapProps< TConfig extends object, TInternalConfig extends Record<string, unknown>, >( config: TConfig & TInternalConfig, propsMapping: ReadonlyMap<string, string> ): TInternalConfig { type MergedConfig = TConfig & TInternalConfig; propsMapping.forEach((internalKey, key) => { if (key in config) { config[internalKey as keyof MergedConfig] = config[key as keyof MergedConfig]; // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete config[key as keyof MergedConfig]; } }); return config; } const DEFAULT_PROPS_MAPPING = new Map<string, string>(); const DEFAULT_PROPS_TRANSFORMER = <TConfig extends object>( config: TConfig ): TConfig => config; export function useClonedAndRemappedConfig< TConfig extends Record<string, unknown>, THandlerData, TInternalConfig extends Record<string, unknown> = TConfig, TExtendedHandlerData extends THandlerData = THandlerData, >( config: ExcludeInternalConfigProps< BaseGestureConfig<TConfig, THandlerData, TExtendedHandlerData> >, propsMapping: ReadonlyMap<string, string> = DEFAULT_PROPS_MAPPING, propsTransformer: ( config: TInternalConfig ) => TInternalConfig = DEFAULT_PROPS_TRANSFORMER ): BaseGestureConfig<TInternalConfig, THandlerData, TExtendedHandlerData> { return useMemo(() => { const clonedConfig = cloneConfig< TConfig, THandlerData, TExtendedHandlerData >(config); const transformedConfig = propsTransformer( remapProps<TConfig, TInternalConfig>( clonedConfig as TConfig & TInternalConfig, propsMapping ) ); resolveInternalConfigProps(transformedConfig); return transformedConfig; }, [config, propsMapping, propsTransformer]); }