Skip to content

Commit 05d0850

Browse files
authored
Experimental Event API: Press event properties (facebook#15586)
1 parent 51e66cf commit 05d0850

File tree

2 files changed

+296
-34
lines changed

2 files changed

+296
-34
lines changed

packages/react-events/src/Press.js

Lines changed: 143 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,9 @@
1010
import type {
1111
ReactResponderEvent,
1212
ReactResponderContext,
13-
ReactResponderDispatchEventOptions,
1413
} from 'shared/ReactTypes';
1514

1615
import React from 'react';
17-
1816
import {isEventPositionWithinTouchHitTarget} from './utils';
1917

2018
type PressProps = {
@@ -86,6 +84,13 @@ type PressEvent = {|
8684
target: Element | Document,
8785
type: PressEventType,
8886
pointerType: PointerType,
87+
timeStamp: null | number,
88+
clientX: null | number,
89+
clientY: null | number,
90+
pageX: null | number,
91+
pageY: null | number,
92+
screenX: null | number,
93+
screenY: null | number,
8994
|};
9095

9196
const DEFAULT_PRESS_END_DELAY_MS = 0;
@@ -133,58 +138,112 @@ function createPressEvent(
133138
type: PressEventType,
134139
target: Element | Document,
135140
pointerType: PointerType,
141+
event: ?ReactResponderEvent,
142+
delay: number,
136143
): PressEvent {
144+
let clientX = null;
145+
let clientY = null;
146+
let pageX = null;
147+
let pageY = null;
148+
let screenX = null;
149+
let screenY = null;
150+
let timeStamp = null;
151+
152+
if (event) {
153+
const nativeEvent = (event.nativeEvent: any);
154+
timeStamp = nativeEvent.timeStamp + delay;
155+
// Only check for one property, checking for all of them is costly. We can assume
156+
// if clientX exists, so do the rest.
157+
let eventObject;
158+
if (nativeEvent.clientX !== undefined) {
159+
eventObject = (nativeEvent: any);
160+
} else if (isTouchEvent(nativeEvent)) {
161+
eventObject = getTouchFromPressEvent(nativeEvent);
162+
}
163+
if (eventObject) {
164+
({clientX, clientY, pageX, pageY, screenX, screenY} = eventObject);
165+
}
166+
}
137167
return {
138168
target,
139169
type,
140170
pointerType,
171+
timeStamp,
172+
clientX,
173+
clientY,
174+
pageX,
175+
pageY,
176+
screenX,
177+
screenY,
141178
};
142179
}
143180

144181
function dispatchEvent(
182+
event: ?ReactResponderEvent,
145183
context: ReactResponderContext,
146184
state: PressState,
147185
name: PressEventType,
148186
listener: (e: Object) => void,
149-
options?: ReactResponderDispatchEventOptions,
187+
discrete: boolean,
188+
delay: number,
150189
): void {
151190
const target = ((state.pressTarget: any): Element | Document);
152191
const pointerType = state.pointerType;
153-
const syntheticEvent = createPressEvent(name, target, pointerType);
154-
context.dispatchEvent(
155-
syntheticEvent,
156-
listener,
157-
options || {
158-
discrete: true,
159-
},
192+
const syntheticEvent = createPressEvent(
193+
name,
194+
target,
195+
pointerType,
196+
event,
197+
delay,
160198
);
199+
context.dispatchEvent(syntheticEvent, listener, {
200+
discrete,
201+
});
161202
}
162203

163204
function dispatchPressChangeEvent(
205+
event: ?ReactResponderEvent,
164206
context: ReactResponderContext,
165207
props: PressProps,
166208
state: PressState,
209+
delay: number,
167210
): void {
168211
const bool = state.isActivePressed;
169212
const listener = () => {
170213
props.onPressChange(bool);
171214
};
172-
dispatchEvent(context, state, 'presschange', listener);
215+
dispatchEvent(event, context, state, 'presschange', listener, true, delay);
173216
}
174217

175218
function dispatchLongPressChangeEvent(
219+
event: ?ReactResponderEvent,
176220
context: ReactResponderContext,
177221
props: PressProps,
178222
state: PressState,
223+
delay: number,
179224
): void {
180225
const bool = state.isLongPressed;
181226
const listener = () => {
182227
props.onLongPressChange(bool);
183228
};
184-
dispatchEvent(context, state, 'longpresschange', listener);
229+
dispatchEvent(
230+
event,
231+
context,
232+
state,
233+
'longpresschange',
234+
listener,
235+
true,
236+
delay,
237+
);
185238
}
186239

187-
function activate(event, context, props, state) {
240+
function activate(
241+
event: ReactResponderEvent,
242+
context,
243+
props,
244+
state,
245+
delay: number,
246+
) {
188247
const nativeEvent: any = event.nativeEvent;
189248
const {x, y} = getEventPageCoords(nativeEvent);
190249
const wasActivePressed = state.isActivePressed;
@@ -197,26 +256,48 @@ function activate(event, context, props, state) {
197256
}
198257

199258
if (props.onPressStart) {
200-
dispatchEvent(context, state, 'pressstart', props.onPressStart);
259+
dispatchEvent(
260+
event,
261+
context,
262+
state,
263+
'pressstart',
264+
props.onPressStart,
265+
true,
266+
delay,
267+
);
201268
}
202269
if (!wasActivePressed && props.onPressChange) {
203-
dispatchPressChangeEvent(context, props, state);
270+
dispatchPressChangeEvent(event, context, props, state, delay);
204271
}
205272
}
206273

207-
function deactivate(context, props, state) {
274+
function deactivate(
275+
event: ?ReactResponderEvent,
276+
context,
277+
props,
278+
state,
279+
delay: number,
280+
) {
208281
const wasLongPressed = state.isLongPressed;
209282
state.isActivePressed = false;
210283
state.isLongPressed = false;
211284

212285
if (props.onPressEnd) {
213-
dispatchEvent(context, state, 'pressend', props.onPressEnd);
286+
dispatchEvent(
287+
event,
288+
context,
289+
state,
290+
'pressend',
291+
props.onPressEnd,
292+
true,
293+
delay,
294+
);
214295
}
215296
if (props.onPressChange) {
216-
dispatchPressChangeEvent(context, props, state);
297+
dispatchPressChangeEvent(event, context, props, state, delay);
217298
}
218299
if (wasLongPressed && props.onLongPressChange) {
219-
dispatchLongPressChangeEvent(context, props, state);
300+
dispatchLongPressChangeEvent(event, context, props, state, delay);
220301
}
221302
}
222303

@@ -233,9 +314,9 @@ function dispatchPressStartEvents(
233314
state.pressEndTimeout = null;
234315
}
235316

236-
const dispatch = () => {
317+
const dispatch = (delay: number) => {
237318
state.isActivePressStart = true;
238-
activate(event, context, props, state);
319+
activate(event, context, props, state, delay);
239320

240321
if (
241322
(props.onLongPress || props.onLongPressChange) &&
@@ -250,10 +331,24 @@ function dispatchPressStartEvents(
250331
state.isLongPressed = true;
251332
state.longPressTimeout = null;
252333
if (props.onLongPress) {
253-
dispatchEvent(context, state, 'longpress', props.onLongPress);
334+
dispatchEvent(
335+
event,
336+
context,
337+
state,
338+
'longpress',
339+
props.onLongPress,
340+
true,
341+
delayLongPress + delay,
342+
);
254343
}
255344
if (props.onLongPressChange) {
256-
dispatchLongPressChangeEvent(context, props, state);
345+
dispatchLongPressChangeEvent(
346+
event,
347+
context,
348+
props,
349+
state,
350+
delayLongPress + delay,
351+
);
257352
}
258353
}, delayLongPress);
259354
}
@@ -268,10 +363,10 @@ function dispatchPressStartEvents(
268363
if (delayPressStart > 0) {
269364
state.pressStartTimeout = context.setTimeout(() => {
270365
state.pressStartTimeout = null;
271-
dispatch();
366+
dispatch(delayPressStart);
272367
}, delayPressStart);
273368
} else {
274-
dispatch();
369+
dispatch(0);
275370
}
276371
}
277372
}
@@ -299,7 +394,7 @@ function dispatchPressEndEvents(
299394
// don't activate if a press has moved beyond the responder region
300395
if (state.isPressWithinResponderRegion && event != null) {
301396
// if we haven't yet activated (due to delays), activate now
302-
activate(event, context, props, state);
397+
activate(event, context, props, state, 0);
303398
activationWasForced = true;
304399
}
305400
}
@@ -316,10 +411,10 @@ function dispatchPressEndEvents(
316411
if (delayPressEnd > 0) {
317412
state.pressEndTimeout = context.setTimeout(() => {
318413
state.pressEndTimeout = null;
319-
deactivate(context, props, state);
414+
deactivate(event, context, props, state, delayPressEnd);
320415
}, delayPressEnd);
321416
} else {
322-
deactivate(context, props, state);
417+
deactivate(event, context, props, state, 0);
323418
}
324419
}
325420
}
@@ -368,8 +463,8 @@ function calculateResponderRegion(
368463
) {
369464
const pressRetentionOffset = context.objectAssign(
370465
{},
371-
...DEFAULT_PRESS_RETENTION_OFFSET,
372-
...props.pressRetentionOffset,
466+
DEFAULT_PRESS_RETENTION_OFFSET,
467+
props.pressRetentionOffset,
373468
);
374469

375470
const clientRect = target.getBoundingClientRect();
@@ -656,9 +751,15 @@ const PressResponder = {
656751
if (state.isPressWithinResponderRegion) {
657752
if (state.isPressed) {
658753
if (props.onPressMove) {
659-
dispatchEvent(context, state, 'pressmove', props.onPressMove, {
660-
discrete: false,
661-
});
754+
dispatchEvent(
755+
event,
756+
context,
757+
state,
758+
'pressmove',
759+
props.onPressMove,
760+
false,
761+
0,
762+
);
662763
}
663764
if (
664765
state.activationPosition != null &&
@@ -735,7 +836,15 @@ const PressResponder = {
735836
props.onLongPressShouldCancelPress()
736837
)
737838
) {
738-
dispatchEvent(context, state, 'press', props.onPress);
839+
dispatchEvent(
840+
event,
841+
context,
842+
state,
843+
'press',
844+
props.onPress,
845+
true,
846+
0,
847+
);
739848
}
740849
}
741850
}

0 commit comments

Comments
 (0)