create-expo-cljs-app
Version:
Create a react native application with Expo and Shadow-CLJS!
280 lines (243 loc) • 8.53 kB
text/typescript
import { FlingGestureHandlerEventPayload } from '../FlingGestureHandler';
import { ForceTouchGestureHandlerEventPayload } from '../ForceTouchGestureHandler';
import {
HitSlop,
CommonGestureConfig,
GestureTouchEvent,
GestureStateChangeEvent,
GestureUpdateEvent,
} from '../gestureHandlerCommon';
import { getNextHandlerTag } from '../handlersRegistry';
import { GestureStateManagerType } from './gestureStateManager';
import { LongPressGestureHandlerEventPayload } from '../LongPressGestureHandler';
import { PanGestureHandlerEventPayload } from '../PanGestureHandler';
import { PinchGestureHandlerEventPayload } from '../PinchGestureHandler';
import { RotationGestureHandlerEventPayload } from '../RotationGestureHandler';
import { TapGestureHandlerEventPayload } from '../TapGestureHandler';
import { NativeViewGestureHandlerPayload } from '../NativeViewGestureHandler';
export type GestureType =
| BaseGesture<Record<string, unknown>>
| BaseGesture<Record<string, never>>
| BaseGesture<TapGestureHandlerEventPayload>
| BaseGesture<PanGestureHandlerEventPayload>
| BaseGesture<LongPressGestureHandlerEventPayload>
| BaseGesture<RotationGestureHandlerEventPayload>
| BaseGesture<PinchGestureHandlerEventPayload>
| BaseGesture<FlingGestureHandlerEventPayload>
| BaseGesture<ForceTouchGestureHandlerEventPayload>
| BaseGesture<NativeViewGestureHandlerPayload>;
export type GestureRef =
| number
| GestureType
| React.RefObject<GestureType | undefined>
| React.RefObject<React.ComponentType | undefined>; // allow adding a ref to a gesture handler
export interface BaseGestureConfig
extends CommonGestureConfig,
Record<string, unknown> {
ref?: React.MutableRefObject<GestureType | undefined>;
requireToFail?: GestureRef[];
simultaneousWith?: GestureRef[];
needsPointerData?: boolean;
manualActivation?: boolean;
}
type TouchEventHandlerType = (
event: GestureTouchEvent,
stateManager: GestureStateManagerType
) => void;
export type HandlerCallbacks<EventPayloadT extends Record<string, unknown>> = {
handlerTag: number;
onBegin?: (event: GestureStateChangeEvent<EventPayloadT>) => void;
onStart?: (event: GestureStateChangeEvent<EventPayloadT>) => void;
onEnd?: (
event: GestureStateChangeEvent<EventPayloadT>,
success: boolean
) => void;
onFinalize?: (
event: GestureStateChangeEvent<EventPayloadT>,
success: boolean
) => void;
onUpdate?: (event: GestureUpdateEvent<EventPayloadT>) => void;
onTouchesDown?: TouchEventHandlerType;
onTouchesMove?: TouchEventHandlerType;
onTouchesUp?: TouchEventHandlerType;
onTouchesCancelled?: TouchEventHandlerType;
isWorklet: boolean[];
};
export const CALLBACK_TYPE = {
UNDEFINED: 0,
BEGAN: 1,
START: 2,
UPDATE: 3,
END: 4,
FINALIZE: 5,
TOUCHES_DOWN: 6,
TOUCHES_MOVE: 7,
TOUCHES_UP: 8,
TOUCHES_CANCELLED: 9,
} as const;
// Allow using CALLBACK_TYPE as object and type
// eslint-disable-next-line @typescript-eslint/no-redeclare
export type CALLBACK_TYPE = typeof CALLBACK_TYPE[keyof typeof CALLBACK_TYPE];
export abstract class Gesture {
/**
* Return array of gestures, providing the same interface for creating and updating
* handlers, no matter which object was used to create gesture instance.
*/
abstract toGestureArray(): GestureType[];
/**
* Assign handlerTag to the gesture instance and set ref.current (if a ref is set)
*/
abstract initialize(): void;
/**
* Make sure that values of properties defining relations are arrays. Do any necessary
* preprocessing required to configure relations between handlers. Called just before
* updating the handler on the native side.
*/
abstract prepare(): void;
}
export abstract class BaseGesture<
EventPayloadT extends Record<string, unknown>
> extends Gesture {
public handlerTag = -1;
public handlerName = '';
public config: BaseGestureConfig = {};
public handlers: HandlerCallbacks<EventPayloadT> = {
handlerTag: -1,
isWorklet: [false, false, false, false],
};
private addDependency(
key: 'simultaneousWith' | 'requireToFail',
gesture: Exclude<GestureRef, number>
) {
const value = this.config[key];
this.config[key] = value
? Array<GestureRef>().concat(value, gesture)
: [gesture];
}
withRef(ref: React.MutableRefObject<GestureType | undefined>) {
this.config.ref = ref;
return this;
}
protected isWorklet(
callback:
| TouchEventHandlerType
| ((event: GestureUpdateEvent<EventPayloadT>) => void)
| ((event: GestureStateChangeEvent<EventPayloadT>) => void)
) {
//@ts-ignore if callback is a worklet, the property will be available, if not then the check will return false
return callback.__workletHash !== undefined;
}
onBegin(callback: (event: GestureStateChangeEvent<EventPayloadT>) => void) {
this.handlers.onBegin = callback;
this.handlers.isWorklet[CALLBACK_TYPE.BEGAN] = this.isWorklet(callback);
return this;
}
onStart(callback: (event: GestureStateChangeEvent<EventPayloadT>) => void) {
this.handlers.onStart = callback;
this.handlers.isWorklet[CALLBACK_TYPE.START] = this.isWorklet(callback);
return this;
}
onEnd(
callback: (
event: GestureStateChangeEvent<EventPayloadT>,
success: boolean
) => void
) {
this.handlers.onEnd = callback;
//@ts-ignore if callback is a worklet, the property will be available, if not then the check will return false
this.handlers.isWorklet[CALLBACK_TYPE.END] = this.isWorklet(callback);
return this;
}
onFinalize(
callback: (
event: GestureStateChangeEvent<EventPayloadT>,
success: boolean
) => void
) {
this.handlers.onFinalize = callback;
//@ts-ignore if callback is a worklet, the property will be available, if not then the check will return false
this.handlers.isWorklet[CALLBACK_TYPE.FINALIZE] = this.isWorklet(callback);
return this;
}
onTouchesDown(callback: TouchEventHandlerType) {
this.config.needsPointerData = true;
this.handlers.onTouchesDown = callback;
this.handlers.isWorklet[CALLBACK_TYPE.TOUCHES_DOWN] = this.isWorklet(
callback
);
return this;
}
onTouchesMove(callback: TouchEventHandlerType) {
this.config.needsPointerData = true;
this.handlers.onTouchesMove = callback;
this.handlers.isWorklet[CALLBACK_TYPE.TOUCHES_MOVE] = this.isWorklet(
callback
);
return this;
}
onTouchesUp(callback: TouchEventHandlerType) {
this.config.needsPointerData = true;
this.handlers.onTouchesUp = callback;
this.handlers.isWorklet[CALLBACK_TYPE.TOUCHES_UP] = this.isWorklet(
callback
);
return this;
}
onTouchesCancelled(callback: TouchEventHandlerType) {
this.config.needsPointerData = true;
this.handlers.onTouchesCancelled = callback;
this.handlers.isWorklet[CALLBACK_TYPE.TOUCHES_CANCELLED] = this.isWorklet(
callback
);
return this;
}
enabled(enabled: boolean) {
this.config.enabled = enabled;
return this;
}
shouldCancelWhenOutside(value: boolean) {
this.config.shouldCancelWhenOutside = value;
return this;
}
hitSlop(hitSlop: HitSlop) {
this.config.hitSlop = hitSlop;
return this;
}
simultaneousWithExternalGesture(...gestures: Exclude<GestureRef, number>[]) {
for (const gesture of gestures) {
this.addDependency('simultaneousWith', gesture);
}
return this;
}
requireExternalGestureToFail(...gestures: Exclude<GestureRef, number>[]) {
for (const gesture of gestures) {
this.addDependency('requireToFail', gesture);
}
return this;
}
initialize() {
this.handlerTag = getNextHandlerTag();
this.handlers = { ...this.handlers, handlerTag: this.handlerTag };
if (this.config.ref) {
this.config.ref.current = this as GestureType;
}
}
toGestureArray(): GestureType[] {
return [this as GestureType];
}
// eslint-disable-next-line @typescript-eslint/no-empty-function
prepare() {}
}
export abstract class ContinousBaseGesture<
EventPayloadT extends Record<string, unknown>
> extends BaseGesture<EventPayloadT> {
onUpdate(callback: (event: GestureUpdateEvent<EventPayloadT>) => void) {
this.handlers.onUpdate = callback;
this.handlers.isWorklet[CALLBACK_TYPE.UPDATE] = this.isWorklet(callback);
return this;
}
manualActivation(manualActivation: boolean) {
this.config.manualActivation = manualActivation;
return this;
}
}