react-native-gesture-handler
Version:
Declarative API exposing native platform touch and gesture system to React Native
114 lines (94 loc) • 3.42 kB
text/typescript
import type { ValueOf } from '../../typeUtils';
import type { Gestures } from '../Gestures';
import type IGestureHandler from '../handlers/IGestureHandler';
type HandlerReadyCallback = (handler: IGestureHandler) => void;
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export default abstract class NodeManager {
private static gestures: Record<
number,
InstanceType<ValueOf<typeof Gestures>>
> = {};
private static observers: Map<number, Map<object, HandlerReadyCallback>> =
new Map();
public static getHandler(tag: number): IGestureHandler {
if (tag in this.gestures) {
return this.gestures[tag] as IGestureHandler;
}
throw new Error(`No handler for tag ${tag}`);
}
public static hasHandler(tag: number): boolean {
return tag in this.gestures;
}
public static createGestureHandler(
handlerTag: number,
handler: InstanceType<ValueOf<typeof Gestures>>
): void {
if (handlerTag in this.gestures) {
throw new Error(
`Handler with tag ${handlerTag} already exists. Please ensure that no Gesture instance is used across multiple GestureDetectors.`
);
}
this.gestures[handlerTag] = handler;
this.gestures[handlerTag].handlerTag = handlerTag;
const pending = this.observers.get(handlerTag);
if (pending) {
// Snapshot before iterating — callbacks may call back into observeHandler / cancelObservation
// and mutate the underlying map.
for (const callback of Array.from(pending.values())) {
callback(handler as IGestureHandler);
}
}
}
public static dropGestureHandler(handlerTag: number): void {
if (!(handlerTag in this.gestures)) {
return;
}
this.gestures[handlerTag].onDestroy();
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete this.gestures[handlerTag];
}
public static detachGestureHandler(handlerTag: number): void {
if (!(handlerTag in this.gestures)) {
return;
}
this.gestures[handlerTag].detach();
}
// Invokes `callback` every time a handler with `tag` is created and, if the handler already exists,
// immediately before returning. The observation persists until explicitly cancelled: the registry
// holds both `owner` and `callback` strongly, so callers MUST call `cancelObservation` or
// `cancelAllObservationsForOwner` when the owner is going away (typically in effect cleanup) to
// avoid leaking. Observing the same tag twice with the same `owner` replaces the previous callback.
public static observeHandler(
tag: number,
owner: object,
callback: HandlerReadyCallback
): void {
let table = this.observers.get(tag);
if (!table) {
table = new Map();
this.observers.set(tag, table);
}
table.set(owner, callback);
if (tag in this.gestures) {
callback(this.gestures[tag] as IGestureHandler);
}
}
public static cancelObservation(tag: number, owner: object): void {
const table = this.observers.get(tag);
if (!table) {
return;
}
table.delete(owner);
if (table.size === 0) {
this.observers.delete(tag);
}
}
public static cancelAllObservationsForOwner(owner: object): void {
for (const tag of this.observers.keys()) {
this.cancelObservation(tag, owner);
}
}
public static get nodes() {
return { ...this.gestures };
}
}