Skip to content

Commit

Permalink
feat(native): use PanResponder for react-native-web (#2985)
Browse files Browse the repository at this point in the history
  • Loading branch information
CodyJasonBennett authored Sep 4, 2023
1 parent 72d7f66 commit 8d0b6ee
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ const Container = React.memo(
},
},
} as LayoutChangeEvent)
}, [onLayout])

ref = { current: { props } }
}, [])
React.useImperativeHandle(ref, () => props)

return null
}),
Expand All @@ -36,6 +36,10 @@ export const StyleSheet = {
},
}

export const PanResponder = {
create: () => ({ panHandlers: {} }),
}

export const Image = {
getSize(_uri: string, res: Function, rej?: Function) {
res(1, 1)
Expand Down

This file was deleted.

This file was deleted.

3 changes: 1 addition & 2 deletions packages/fiber/src/native/Canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,7 @@ const CanvasImpl = /*#__PURE__*/ React.forwardRef<View, Props>(
// Overwrite onCreated to apply RN bindings
onCreated: (state: RootState) => {
// Bind events after creation
const handlers = state.events.connect?.(viewRef.current)
setBind(handlers)
setBind(state.events.handlers)

// Bind render to RN bridge
const context = state.gl.getContext() as ExpoWebGLRenderingContext
Expand Down
78 changes: 30 additions & 48 deletions packages/fiber/src/native/events.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,43 @@
import { UseBoundStore } from 'zustand'
import { RootState } from '../core/store'
import { createEvents, DomEvent, EventManager, Events } from '../core/events'
import { GestureResponderEvent } from 'react-native'
/* eslint-disable import/default, import/no-named-as-default, import/no-named-as-default-member */
// @ts-ignore
import Pressability from 'react-native/Libraries/Pressability/Pressability'
/* eslint-enable import/default, import/no-named-as-default, import/no-named-as-default-member */

const EVENTS = {
PRESS: 'onPress',
PRESSIN: 'onPressIn',
PRESSOUT: 'onPressOut',
LONGPRESS: 'onLongPress',

HOVERIN: 'onHoverIn',
HOVEROUT: 'onHoverOut',
PRESSMOVE: 'onPressMove',
}

const DOM_EVENTS = {
[EVENTS.PRESS]: 'onClick',
[EVENTS.PRESSIN]: 'onPointerDown',
[EVENTS.PRESSOUT]: 'onPointerUp',
[EVENTS.LONGPRESS]: 'onDoubleClick',

[EVENTS.HOVERIN]: 'onPointerOver',
[EVENTS.HOVEROUT]: 'onPointerOut',
[EVENTS.PRESSMOVE]: 'onPointerMove',
}
import { type GestureResponderEvent, PanResponder } from 'react-native'

/** Default R3F event manager for react-native */
export function createTouchEvents(store: UseBoundStore<RootState>): EventManager<HTMLElement> {
const { handlePointer } = createEvents(store)

const handleTouch = (event: GestureResponderEvent, name: keyof typeof EVENTS) => {
const handleTouch = (event: GestureResponderEvent, name: string): true => {
event.persist()

// Apply offset
;(event as any).nativeEvent.offsetX = event.nativeEvent.locationX
;(event as any).nativeEvent.offsetY = event.nativeEvent.locationY

// Emulate DOM event
const callback = handlePointer(DOM_EVENTS[name])
return callback(event.nativeEvent as any)
const callback = handlePointer(name)
callback(event.nativeEvent as any)

return true
}

const responder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onMoveShouldSetPanResponder: () => true,
onMoveShouldSetPanResponderCapture: () => true,
onPanResponderTerminationRequest: () => true,
onStartShouldSetPanResponderCapture: (e) => handleTouch(e, 'onPointerCapture'),
onPanResponderStart: (e) => handleTouch(e, 'onPointerDown'),
onPanResponderMove: (e) => handleTouch(e, 'onPointerMove'),
onPanResponderEnd: (e, state) => {
handleTouch(e, 'onPointerUp')
if (Math.hypot(state.dx, state.dy) < 20) handleTouch(e, 'onClick')
},
onPanResponderRelease: (e) => handleTouch(e, 'onPointerLeave'),
onPanResponderTerminate: (e) => handleTouch(e, 'onLostPointerCapture'),
onPanResponderReject: (e) => handleTouch(e, 'onLostPointerCapture'),
})

return {
priority: 1,
enabled: true,
Expand All @@ -56,34 +49,23 @@ export function createTouchEvents(store: UseBoundStore<RootState>): EventManager
},

connected: undefined,
handlers: Object.values(EVENTS).reduce(
(acc, name) => ({
...acc,
[name]: (event: GestureResponderEvent) => handleTouch(event, name as keyof typeof EVENTS),
}),
{},
) as unknown as Events,
handlers: responder.panHandlers as unknown as Events,
update: () => {
const { events, internal } = store.getState()
if (internal.lastEvent?.current && events.handlers) events.handlers.onPointerMove(internal.lastEvent.current)
if (internal.lastEvent?.current && events.handlers) {
handlePointer('onPointerMove')(internal.lastEvent.current)
}
},
connect: () => {
const { set, events } = store.getState()
events.disconnect?.()

const connected = new Pressability(events.handlers)
set((state) => ({ events: { ...state.events, connected } }))

const handlers = connected.getEventHandlers()
return handlers
set((state) => ({ events: { ...state.events, connected: true } }))
},
disconnect: () => {
const { set, events } = store.getState()
const { set } = store.getState()

if (events.connected) {
events.connected.reset()
set((state) => ({ events: { ...state.events, connected: undefined } }))
}
set((state) => ({ events: { ...state.events, connected: false } }))
},
}
}

0 comments on commit 8d0b6ee

Please sign in to comment.