UNPKG

react-native-gesture-handler

Version:

Experimental implementation of a new declarative API for gesture handling in react-native

155 lines (125 loc) 4.17 kB
import { findNodeHandle } from 'react-native'; import type IGestureHandler from '../handlers/IGestureHandler'; import { GestureHandlerDelegate, MeasureResult, } from './GestureHandlerDelegate'; import PointerEventManager from './PointerEventManager'; import TouchEventManager from './TouchEventManager'; import { State } from '../../State'; import { isPointerInBounds } from '../utils'; import EventManager from './EventManager'; import { Config } from '../interfaces'; import { MouseButton } from '../../handlers/gestureHandlerCommon'; export class GestureHandlerWebDelegate implements GestureHandlerDelegate<HTMLElement, IGestureHandler> { private view!: HTMLElement; private gestureHandler!: IGestureHandler; private eventManagers: EventManager<unknown>[] = []; getView(): HTMLElement { return this.view; } init(viewRef: number, handler: IGestureHandler): void { if (!viewRef) { throw new Error( `Cannot find HTML Element for handler ${handler.getTag()}` ); } this.gestureHandler = handler; this.view = findNodeHandle(viewRef) as unknown as HTMLElement; const config = handler.getConfig(); this.addContextMenuListeners(config); this.view.style['userSelect'] = config.userSelect ?? 'none'; this.view.style['webkitUserSelect'] = config.userSelect ?? 'none'; this.view.style['touchAction'] = config.touchAction ?? 'none'; // @ts-ignore This one disables default events on Safari this.view.style['WebkitTouchCallout'] = 'none'; this.eventManagers.push(new PointerEventManager(this.view)); this.eventManagers.push(new TouchEventManager(this.view)); this.eventManagers.forEach((manager) => this.gestureHandler.attachEventManager(manager) ); } isPointerInBounds({ x, y }: { x: number; y: number }): boolean { return isPointerInBounds(this.view, { x, y }); } measureView(): MeasureResult { const rect = this.view.getBoundingClientRect(); return { pageX: rect.left, pageY: rect.top, width: rect.width, height: rect.height, }; } reset(): void { this.eventManagers.forEach((manager: EventManager<unknown>) => manager.resetManager() ); } tryResetCursor() { const config = this.gestureHandler.getConfig(); if ( config.activeCursor && config.activeCursor !== 'auto' && this.gestureHandler.getState() === State.ACTIVE ) { this.view.style.cursor = 'auto'; } } private shouldDisableContextMenu(config: Config) { return ( (config.enableContextMenu === undefined && this.gestureHandler.isButtonInConfig(MouseButton.RIGHT)) || config.enableContextMenu === false ); } private addContextMenuListeners(config: Config): void { if (this.shouldDisableContextMenu(config)) { this.view.addEventListener('contextmenu', this.disableContextMenu); } else if (config.enableContextMenu) { this.view.addEventListener('contextmenu', this.enableContextMenu); } } private removeContextMenuListeners(config: Config): void { if (this.shouldDisableContextMenu(config)) { this.view.removeEventListener('contextmenu', this.disableContextMenu); } else if (config.enableContextMenu) { this.view.removeEventListener('contextmenu', this.enableContextMenu); } } private disableContextMenu(this: void, e: MouseEvent): void { e.preventDefault(); } private enableContextMenu(this: void, e: MouseEvent): void { e.stopPropagation(); } onBegin(): void { // no-op for now } onActivate(): void { const config = this.gestureHandler.getConfig(); if ( (!this.view.style.cursor || this.view.style.cursor === 'auto') && config.activeCursor ) { this.view.style.cursor = config.activeCursor; } } onEnd(): void { this.tryResetCursor(); } onCancel(): void { this.tryResetCursor(); } onFail(): void { this.tryResetCursor(); } public destroy(config: Config): void { this.removeContextMenuListeners(config); this.eventManagers.forEach((manager) => { manager.unregisterListeners(); }); } }