UNPKG

react-native-flip

Version:
512 lines (466 loc) 15.1 kB
// Previous types exported gesture handlers as classes which creates an interface and variable, both named the same as class. // Without those types, we'd introduce breaking change, forcing users to prefix every handler type specification with typeof // e.g. React.createRef<TapGestureHandler> -> React.createRef<typeof TapGestureHandler>. // See https://www.typescriptlang.org/docs/handbook/classes.html#constructor-functions for reference. import * as React from 'react'; import createHandler from './createHandler'; import PlatformConstants from '../PlatformConstants'; import { State } from '../State'; import { ValueOf } from '../typeUtils'; export interface GestureEventPayload { handlerTag: number; numberOfPointers: number; state: ValueOf<typeof State>; } export interface HandlerStateChangeEventPayload { handlerTag: number; numberOfPointers: number; state: ValueOf<typeof State>; oldState: ValueOf<typeof State>; } //TODO(TS) events in handlers export interface GestureEvent<ExtraEventPayloadT = Record<string, unknown>> { nativeEvent: Readonly<GestureEventPayload & ExtraEventPayloadT>; } export interface HandlerStateChangeEvent< ExtraEventPayloadT = Record<string, unknown> > { nativeEvent: Readonly<HandlerStateChangeEventPayload & ExtraEventPayloadT>; } // Events payloads are types instead of interfaces due to TS limitation. // See https://github.com/microsoft/TypeScript/issues/15300 for more info. export type BaseGestureHandlerProps< ExtraEventPayloadT extends Record<string, unknown> = Record<string, unknown> > = { id?: string; enabled?: boolean; minPointers?: number; waitFor?: React.Ref<unknown> | React.Ref<unknown>[]; simultaneousHandlers?: React.Ref<unknown> | React.Ref<unknown>[]; shouldCancelWhenOutside?: boolean; hitSlop?: | number // TODO(TS) take into consideration types from GestureHandler#setHitSlop | Partial< Record< 'left' | 'right' | 'top' | 'bottom' | 'vertical' | 'horizontal', number > > | Record<'width' | 'left', number> | Record<'width' | 'right', number> | Record<'height' | 'top', number> | Record<'height' | 'bottom', number>; // TODO(TS) - fix event types onBegan?: (event: HandlerStateChangeEvent) => void; onFailed?: (event: HandlerStateChangeEvent) => void; onCancelled?: (event: HandlerStateChangeEvent) => void; onActivated?: (event: HandlerStateChangeEvent) => void; onEnded?: (event: HandlerStateChangeEvent) => void; //TODO(TS) consider using NativeSyntheticEvent onGestureEvent?: (event: GestureEvent<ExtraEventPayloadT>) => void; onHandlerStateChange?: ( event: HandlerStateChangeEvent<ExtraEventPayloadT> ) => void; }; export const baseProps = [ 'id', 'enabled', 'minPointers', 'waitFor', 'simultaneousHandlers', 'shouldCancelWhenOutside', 'hitSlop', 'onBegan', 'onFailed', 'onCancelled', 'onActivated', 'onEnded', 'onGestureEvent', 'onHandlerStateChange', ] as const; export type TapGestureHandlerEventPayload = { x: number; y: number; absoluteX: number; absoluteY: number; }; export interface TapGestureHandlerProps extends BaseGestureHandlerProps<TapGestureHandlerEventPayload> { minPointers?: number; maxDurationMs?: number; maxDelayMs?: number; numberOfTaps?: number; maxDeltaX?: number; maxDeltaY?: number; maxDist?: number; } export type TapGestureHandler = typeof TapGestureHandler; // eslint-disable-next-line @typescript-eslint/no-redeclare -- backward compatibility; see description on the top of this file export const TapGestureHandler = createHandler< TapGestureHandlerProps, TapGestureHandlerEventPayload >({ name: 'TapGestureHandler', allowedProps: [ ...baseProps, 'maxDurationMs', 'maxDelayMs', 'numberOfTaps', 'maxDeltaX', 'maxDeltaY', 'maxDist', 'minPointers', ] as const, config: {}, }); export type FlingGestureHandlerEventPayload = { x: number; y: number; absoluteX: number; absoluteY: number; }; export interface FlingGestureHandlerProps extends BaseGestureHandlerProps<FlingGestureHandlerEventPayload> { direction?: number; numberOfPointers?: number; } export type FlingGestureHandler = typeof FlingGestureHandler; // eslint-disable-next-line @typescript-eslint/no-redeclare -- backward compatibility; see description on the top of this file export const FlingGestureHandler = createHandler< FlingGestureHandlerProps, FlingGestureHandlerEventPayload >({ name: 'FlingGestureHandler', allowedProps: [...baseProps, 'numberOfPointers', 'direction'] as const, config: {}, }); class ForceTouchFallback extends React.Component { static forceTouchAvailable = false; componentDidMount() { console.warn( 'ForceTouchGestureHandler is not available on this platform. Please use ForceTouchGestureHandler.forceTouchAvailable to conditionally render other components that would provide a fallback behavior specific to your usecase' ); } render() { return this.props.children; } } export type ForceTouchGestureHandlerEventPayload = { x: number; y: number; absoluteX: number; absoluteY: number; force: number; }; export interface ForceTouchGestureHandlerProps extends BaseGestureHandlerProps<ForceTouchGestureHandlerEventPayload> { minForce?: number; maxForce?: number; feedbackOnActivation?: boolean; } export type ForceTouchGestureHandler = typeof ForceTouchGestureHandler & { forceTouchAvailable: boolean; }; // eslint-disable-next-line @typescript-eslint/no-redeclare -- backward compatibility; see description on the top of this file export const ForceTouchGestureHandler = PlatformConstants?.forceTouchAvailable ? createHandler< ForceTouchGestureHandlerProps, ForceTouchGestureHandlerEventPayload >({ name: 'ForceTouchGestureHandler', allowedProps: [ ...baseProps, 'minForce', 'maxForce', 'feedbackOnActivation', ] as const, config: {}, }) : ForceTouchFallback; (ForceTouchGestureHandler as ForceTouchGestureHandler).forceTouchAvailable = PlatformConstants?.forceTouchAvailable || false; export type LongPressGestureHandlerEventPayload = { x: number; y: number; absoluteX: number; absoluteY: number; }; export interface LongPressGestureHandlerProps extends BaseGestureHandlerProps<LongPressGestureHandlerEventPayload> { minDurationMs?: number; maxDist?: number; } export type LongPressGestureHandler = typeof LongPressGestureHandler; // eslint-disable-next-line @typescript-eslint/no-redeclare -- backward compatibility; see description on the top of this file export const LongPressGestureHandler = createHandler< LongPressGestureHandlerProps, LongPressGestureHandlerEventPayload >({ name: 'LongPressGestureHandler', allowedProps: [...baseProps, 'minDurationMs', 'maxDist'] as const, config: {}, }); function validatePanGestureHandlerProps(props: PanGestureHandlerProps) { if (props.minDeltaX && props.activeOffsetX) { throw new Error( `It's not supported use minDeltaX with activeOffsetXStart or activeOffsetXEnd` ); } if (props.maxDeltaX && props.failOffsetX) { throw new Error( `It's not supported use minDeltaX with activeOffsetXStart or activeOffsetXEnd` ); } if (props.minDeltaY && props.activeOffsetY) { throw new Error( `It's not supported use minDeltaX with activeOffsetYStart or activeOffsetYEnd` ); } if (props.maxDeltaY && props.failOffsetY) { throw new Error( `It's not supported use minDeltaX with activeOffsetYStart or activeOffsetYEnd` ); } if ( Array.isArray(props.activeOffsetX) && (props.activeOffsetX[0] > 0 || props.activeOffsetX[1] < 0) ) { throw new Error( `First element of activeOffsetX should be negative, a the second one should be positive` ); } if ( Array.isArray(props.activeOffsetY) && (props.activeOffsetY[0] > 0 || props.activeOffsetY[1] < 0) ) { throw new Error( `First element of activeOffsetY should be negative, a the second one should be positive` ); } if ( Array.isArray(props.failOffsetX) && (props.failOffsetX[0] > 0 || props.failOffsetX[1] < 0) ) { throw new Error( `First element of failOffsetX should be negative, a the second one should be positive` ); } if ( Array.isArray(props.failOffsetY) && (props.failOffsetY[0] > 0 || props.failOffsetY[1] < 0) ) { throw new Error( `First element of failOffsetY should be negative, a the second one should be positive` ); } } function transformPanGestureHandlerProps(props: PanGestureHandlerProps) { type InternalPanGHKeys = | 'activeOffsetXStart' | 'activeOffsetXEnd' | 'failOffsetXStart' | 'failOffsetXEnd' | 'activeOffsetYStart' | 'activeOffsetYEnd' | 'failOffsetYStart' | 'failOffsetYEnd'; type PanGestureHandlerInternalProps = PanGestureHandlerProps & Partial<Record<InternalPanGHKeys, number>>; const res: PanGestureHandlerInternalProps = { ...props }; if (props.minDeltaX !== undefined) { delete res.minDeltaX; res.activeOffsetXStart = -props.minDeltaX; res.activeOffsetXEnd = props.minDeltaX; } if (props.maxDeltaX !== undefined) { delete res.maxDeltaX; res.failOffsetXStart = -props.maxDeltaX; res.failOffsetXEnd = props.maxDeltaX; } if (props.minOffsetX !== undefined) { delete res.minOffsetX; if (props.minOffsetX < 0) { res.activeOffsetXStart = props.minOffsetX; } else { res.activeOffsetXEnd = props.minOffsetX; } } if (props.minDeltaY !== undefined) { delete res.minDeltaY; res.activeOffsetYStart = -props.minDeltaY; res.activeOffsetYEnd = props.minDeltaY; } if (props.maxDeltaY !== undefined) { delete res.maxDeltaY; res.failOffsetYStart = -props.maxDeltaY; res.failOffsetYEnd = props.maxDeltaY; } if (props.minOffsetY !== undefined) { delete res.minOffsetY; if (props.minOffsetY < 0) { res.activeOffsetYStart = props.minOffsetY; } else { res.activeOffsetYEnd = props.minOffsetY; } } if (props.activeOffsetX !== undefined) { delete res.activeOffsetX; if (Array.isArray(props.activeOffsetX)) { res.activeOffsetXStart = props.activeOffsetX[0]; res.activeOffsetXEnd = props.activeOffsetX[1]; } else if (props.activeOffsetX < 0) { res.activeOffsetXStart = props.activeOffsetX; } else { res.activeOffsetXEnd = props.activeOffsetX; } } if (props.activeOffsetY !== undefined) { delete res.activeOffsetY; if (Array.isArray(props.activeOffsetY)) { res.activeOffsetYStart = props.activeOffsetY[0]; res.activeOffsetYEnd = props.activeOffsetY[1]; } else if (props.activeOffsetY < 0) { res.activeOffsetYStart = props.activeOffsetY; } else { res.activeOffsetYEnd = props.activeOffsetY; } } if (props.failOffsetX !== undefined) { delete res.failOffsetX; if (Array.isArray(props.failOffsetX)) { res.failOffsetXStart = props.failOffsetX[0]; res.failOffsetXEnd = props.failOffsetX[1]; } else if (props.failOffsetX < 0) { res.failOffsetXStart = props.failOffsetX; } else { res.failOffsetXEnd = props.failOffsetX; } } if (props.failOffsetY !== undefined) { delete res.failOffsetY; if (Array.isArray(props.failOffsetY)) { res.failOffsetYStart = props.failOffsetY[0]; res.failOffsetYEnd = props.failOffsetY[1]; } else if (props.failOffsetY < 0) { res.failOffsetYStart = props.failOffsetY; } else { res.failOffsetYEnd = props.failOffsetY; } } return res; } function managePanProps(props: PanGestureHandlerProps) { if (__DEV__) { validatePanGestureHandlerProps(props); } return transformPanGestureHandlerProps(props); } export type PanGestureHandlerEventPayload = { x: number; y: number; absoluteX: number; absoluteY: number; translationX: number; translationY: number; velocityX: number; velocityY: number; }; export interface PanGestureHandlerProps extends BaseGestureHandlerProps<PanGestureHandlerEventPayload> { /** @deprecated use activeOffsetX*/ minDeltaX?: number; /** @deprecated use activeOffsetY*/ minDeltaY?: number; /** @deprecated use failOffsetX*/ maxDeltaX?: number; /** @deprecated use failOffsetY*/ maxDeltaY?: number; /** @deprecated use activeOffsetX*/ minOffsetX?: number; /** @deprecated use failOffsetY*/ minOffsetY?: number; activeOffsetY?: number | number[]; activeOffsetX?: number | number[]; failOffsetY?: number | number[]; failOffsetX?: number | number[]; minDist?: number; minVelocity?: number; minVelocityX?: number; minVelocityY?: number; minPointers?: number; maxPointers?: number; avgTouches?: boolean; enableTrackpadTwoFingerGesture?: boolean; } export type PanGestureHandler = typeof PanGestureHandler; // eslint-disable-next-line @typescript-eslint/no-redeclare -- backward compatibility; see description on the top of this file export const PanGestureHandler = createHandler< PanGestureHandlerProps, PanGestureHandlerEventPayload >({ name: 'PanGestureHandler', allowedProps: [ ...baseProps, 'activeOffsetY', 'activeOffsetX', 'failOffsetY', 'failOffsetX', 'minDist', 'minVelocity', 'minVelocityX', 'minVelocityY', 'minPointers', 'maxPointers', 'avgTouches', 'enableTrackpadTwoFingerGesture', ] as const, config: {}, transformProps: managePanProps, customNativeProps: [ 'activeOffsetYStart', 'activeOffsetYEnd', 'activeOffsetXStart', 'activeOffsetXEnd', 'failOffsetYStart', 'failOffsetYEnd', 'failOffsetXStart', 'failOffsetXEnd', ], }); export type PinchGestureHandlerEventPayload = { scale: number; focalX: number; focalY: number; velocity: number; }; export interface PinchGestureHandlerProps extends BaseGestureHandlerProps<PinchGestureHandlerEventPayload> {} export type PinchGestureHandler = typeof PinchGestureHandler; // eslint-disable-next-line @typescript-eslint/no-redeclare -- backward compatibility; see description on the top of this file export const PinchGestureHandler = createHandler< PinchGestureHandlerProps, PinchGestureHandlerEventPayload >({ name: 'PinchGestureHandler', allowedProps: baseProps, config: {}, }); export type RotationGestureHandlerEventPayload = { rotation: number; anchorX: number; anchorY: number; velocity: number; }; export interface RotationGestureHandlerProps extends BaseGestureHandlerProps<RotationGestureHandlerEventPayload> {} export type RotationGestureHandler = typeof RotationGestureHandler; // eslint-disable-next-line @typescript-eslint/no-redeclare -- backward compatibility; see description on the top of this file export const RotationGestureHandler = createHandler< RotationGestureHandlerProps, RotationGestureHandlerEventPayload >({ name: 'RotationGestureHandler', allowedProps: baseProps, config: {}, });