Event API: normalize event timeStamp property to be in event system (#15598)

This commit is contained in:
Dominic Gannaway
2019-05-09 16:05:25 +01:00
committed by GitHub
parent f405515cea
commit 49ed3e10df
8 changed files with 80 additions and 76 deletions
+21 -6
View File
@@ -64,6 +64,7 @@ type ResponderTimer = {|
instance: ReactEventComponentInstance,
func: () => void,
id: Symbol,
timeStamp: number,
|};
const activeTimeouts: Map<Symbol, ResponderTimeout> = new Map();
@@ -90,6 +91,7 @@ const responderOwners: Map<
> = new Map();
let globalOwner = null;
let currentTimeStamp = 0;
let currentTimers = new Map();
let currentInstance: null | ReactEventComponentInstance = null;
let currentEventQueue: null | EventQueue = null;
@@ -101,11 +103,11 @@ const eventResponderContext: ReactResponderContext = {
{discrete}: ReactResponderDispatchEventOptions,
): void {
validateResponderContext();
const {target, type} = possibleEventObject;
const {target, type, timeStamp} = possibleEventObject;
if (target == null || type == null) {
if (target == null || type == null || timeStamp == null) {
throw new Error(
'context.dispatchEvent: "target" and "type" fields on event object are required.',
'context.dispatchEvent: "target", "timeStamp", and "type" fields on event object are required.',
);
}
if (__DEV__) {
@@ -313,7 +315,7 @@ const eventResponderContext: ReactResponderContext = {
if (timeout === undefined) {
const timers = new Map();
const id = setTimeout(() => {
processTimers(timers);
processTimers(timers, delay);
}, delay);
timeout = {
id,
@@ -325,6 +327,7 @@ const eventResponderContext: ReactResponderContext = {
instance: ((currentInstance: any): ReactEventComponentInstance),
func,
id: timerId,
timeStamp: currentTimeStamp,
});
activeTimeouts.set(timerId, timeout);
return timerId;
@@ -408,6 +411,9 @@ const eventResponderContext: ReactResponderContext = {
}
return currentTarget;
},
getTimeStamp(): number {
return currentTimeStamp;
},
};
function isTargetWithinEventComponent(target: Element | Document): boolean {
@@ -478,13 +484,17 @@ function isFiberHostComponentFocusable(fiber: Fiber): boolean {
);
}
function processTimers(timers: Map<Symbol, ResponderTimer>): void {
function processTimers(
timers: Map<Symbol, ResponderTimer>,
delay: number,
): void {
const timersArr = Array.from(timers.values());
currentEventQueue = createEventQueue();
try {
for (let i = 0; i < timersArr.length; i++) {
const {instance, func, id} = timersArr[i];
const {instance, func, id, timeStamp} = timersArr[i];
currentInstance = instance;
currentTimeStamp = timeStamp + delay;
try {
func();
} finally {
@@ -496,6 +506,7 @@ function processTimers(timers: Map<Symbol, ResponderTimer>): void {
currentTimers = null;
currentInstance = null;
currentEventQueue = null;
currentTimeStamp = 0;
}
}
@@ -861,8 +872,11 @@ export function dispatchEventForResponderEventSystem(
const previousEventQueue = currentEventQueue;
const previousInstance = currentInstance;
const previousTimers = currentTimers;
const previousTimeStamp = currentTimeStamp;
currentTimers = null;
currentEventQueue = createEventQueue();
// We might want to control timeStamp another way here
currentTimeStamp = (nativeEvent: any).timeStamp;
try {
traverseAndHandleEventResponderInstances(
topLevelType,
@@ -876,6 +890,7 @@ export function dispatchEventForResponderEventSystem(
currentTimers = previousTimers;
currentInstance = previousInstance;
currentEventQueue = previousEventQueue;
currentTimeStamp = previousTimeStamp;
}
}
}
@@ -422,6 +422,7 @@ describe('DOMEventResponderSystem', () => {
target: event.target,
type: 'magicclick',
phase: 'bubble',
timeStamp: context.getTimeStamp(),
};
context.dispatchEvent(syntheticEvent, props.onMagicClick, {
discrete: true,
@@ -434,6 +435,7 @@ describe('DOMEventResponderSystem', () => {
target: event.target,
type: 'magicclick',
phase: 'capture',
timeStamp: context.getTimeStamp(),
};
context.dispatchEvent(syntheticEvent, props.onMagicClick, {
discrete: true,
@@ -477,6 +479,7 @@ describe('DOMEventResponderSystem', () => {
target: event.target,
type: 'press',
phase,
timeStamp: context.getTimeStamp(),
};
context.dispatchEvent(pressEvent, props.onPress, {discrete: true});
@@ -486,6 +489,7 @@ describe('DOMEventResponderSystem', () => {
target: event.target,
type: 'longpress',
phase,
timeStamp: context.getTimeStamp(),
};
context.dispatchEvent(longPressEvent, props.onLongPress, {
discrete: true,
@@ -497,6 +501,7 @@ describe('DOMEventResponderSystem', () => {
target: event.target,
type: 'longpresschange',
phase,
timeStamp: context.getTimeStamp(),
};
context.dispatchEvent(longPressChangeEvent, props.onLongPressChange, {
discrete: true,
@@ -901,6 +906,7 @@ describe('DOMEventResponderSystem', () => {
const syntheticEvent = {
target: event.target,
type: 'click',
timeStamp: context.getTimeStamp(),
};
context.dispatchEvent(syntheticEvent, props.onClick, {
discrete: true,
+4 -1
View File
@@ -50,11 +50,13 @@ type DragEventType = 'dragstart' | 'dragend' | 'dragchange' | 'dragmove';
type DragEvent = {|
target: Element | Document,
type: DragEventType,
timeStamp: number,
diffX?: number,
diffY?: number,
|};
function createDragEvent(
context: ReactResponderContext,
type: DragEventType,
target: Element | Document,
eventData?: EventData,
@@ -62,6 +64,7 @@ function createDragEvent(
return {
target,
type,
timeStamp: context.getTimeStamp(),
...eventData,
};
}
@@ -75,7 +78,7 @@ function dispatchDragEvent(
eventData?: EventData,
): void {
const target = ((state.dragTarget: any): Element | Document);
const syntheticEvent = createDragEvent(name, target, eventData);
const syntheticEvent = createDragEvent(context, name, target, eventData);
context.dispatchEvent(syntheticEvent, listener, {discrete});
}
+17 -6
View File
@@ -33,6 +33,7 @@ type FocusEventType = 'focus' | 'blur' | 'focuschange' | 'focusvisiblechange';
type FocusEvent = {|
target: Element | Document,
type: FocusEventType,
timeStamp: number,
|};
const targetEventTypes = [
@@ -56,12 +57,14 @@ const rootEventTypes = [
];
function createFocusEvent(
context: ReactResponderContext,
type: FocusEventType,
target: Element | Document,
): FocusEvent {
return {
target,
type,
timeStamp: context.getTimeStamp(),
};
}
@@ -72,21 +75,25 @@ function dispatchFocusInEvents(
) {
const target = ((state.focusTarget: any): Element | Document);
if (props.onFocus) {
const syntheticEvent = createFocusEvent('focus', target);
const syntheticEvent = createFocusEvent(context, 'focus', target);
context.dispatchEvent(syntheticEvent, props.onFocus, {discrete: true});
}
if (props.onFocusChange) {
const listener = () => {
props.onFocusChange(true);
};
const syntheticEvent = createFocusEvent('focuschange', target);
const syntheticEvent = createFocusEvent(context, 'focuschange', target);
context.dispatchEvent(syntheticEvent, listener, {discrete: true});
}
if (props.onFocusVisibleChange && state.isLocalFocusVisible) {
const listener = () => {
props.onFocusVisibleChange(true);
};
const syntheticEvent = createFocusEvent('focusvisiblechange', target);
const syntheticEvent = createFocusEvent(
context,
'focusvisiblechange',
target,
);
context.dispatchEvent(syntheticEvent, listener, {discrete: true});
}
}
@@ -98,14 +105,14 @@ function dispatchFocusOutEvents(
) {
const target = ((state.focusTarget: any): Element | Document);
if (props.onBlur) {
const syntheticEvent = createFocusEvent('blur', target);
const syntheticEvent = createFocusEvent(context, 'blur', target);
context.dispatchEvent(syntheticEvent, props.onBlur, {discrete: true});
}
if (props.onFocusChange) {
const listener = () => {
props.onFocusChange(false);
};
const syntheticEvent = createFocusEvent('focuschange', target);
const syntheticEvent = createFocusEvent(context, 'focuschange', target);
context.dispatchEvent(syntheticEvent, listener, {discrete: true});
}
dispatchFocusVisibleOutEvent(context, props, state);
@@ -121,7 +128,11 @@ function dispatchFocusVisibleOutEvent(
const listener = () => {
props.onFocusVisibleChange(false);
};
const syntheticEvent = createFocusEvent('focusvisiblechange', target);
const syntheticEvent = createFocusEvent(
context,
'focusvisiblechange',
target,
);
context.dispatchEvent(syntheticEvent, listener, {discrete: true});
state.isLocalFocusVisible = false;
}
+7
View File
@@ -42,6 +42,7 @@ type HoverEventType = 'hoverstart' | 'hoverend' | 'hoverchange' | 'hovermove';
type HoverEvent = {|
target: Element | Document,
type: HoverEventType,
timeStamp: number,
|};
const DEFAULT_HOVER_END_DELAY_MS = 0;
@@ -60,12 +61,14 @@ if (typeof window !== 'undefined' && window.PointerEvent === undefined) {
}
function createHoverEvent(
context: ReactResponderContext,
type: HoverEventType,
target: Element | Document,
): HoverEvent {
return {
target,
type,
timeStamp: context.getTimeStamp(),
};
}
@@ -79,6 +82,7 @@ function dispatchHoverChangeEvent(
props.onHoverChange(bool);
};
const syntheticEvent = createHoverEvent(
context,
'hoverchange',
((state.hoverTarget: any): Element | Document),
);
@@ -115,6 +119,7 @@ function dispatchHoverStartEvents(
if (props.onHoverStart) {
const syntheticEvent = createHoverEvent(
context,
'hoverstart',
((target: any): Element | Document),
);
@@ -174,6 +179,7 @@ function dispatchHoverEndEvents(
if (props.onHoverEnd) {
const syntheticEvent = createHoverEvent(
context,
'hoverend',
((target: any): Element | Document),
);
@@ -311,6 +317,7 @@ const HoverResponder = {
} else {
if (props.onHoverMove && state.hoverTarget !== null) {
const syntheticEvent = createHoverEvent(
context,
'hovermove',
state.hoverTarget,
);
+20 -62
View File
@@ -84,7 +84,7 @@ type PressEvent = {|
target: Element | Document,
type: PressEventType,
pointerType: PointerType,
timeStamp: null | number,
timeStamp: number,
clientX: null | number,
clientY: null | number,
pageX: null | number,
@@ -135,23 +135,22 @@ if (typeof window !== 'undefined' && window.PointerEvent === undefined) {
}
function createPressEvent(
context: ReactResponderContext,
type: PressEventType,
target: Element | Document,
pointerType: PointerType,
event: ?ReactResponderEvent,
delay: number,
): PressEvent {
const timeStamp = context.getTimeStamp();
let clientX = null;
let clientY = null;
let pageX = null;
let pageY = null;
let screenX = null;
let screenY = null;
let timeStamp = null;
if (event) {
const nativeEvent = (event.nativeEvent: any);
timeStamp = nativeEvent.timeStamp + delay;
// Only check for one property, checking for all of them is costly. We can assume
// if clientX exists, so do the rest.
let eventObject;
@@ -185,16 +184,15 @@ function dispatchEvent(
name: PressEventType,
listener: (e: Object) => void,
discrete: boolean,
delay: number,
): void {
const target = ((state.pressTarget: any): Element | Document);
const pointerType = state.pointerType;
const syntheticEvent = createPressEvent(
context,
name,
target,
pointerType,
event,
delay,
);
context.dispatchEvent(syntheticEvent, listener, {
discrete,
@@ -206,13 +204,12 @@ function dispatchPressChangeEvent(
context: ReactResponderContext,
props: PressProps,
state: PressState,
delay: number,
): void {
const bool = state.isActivePressed;
const listener = () => {
props.onPressChange(bool);
};
dispatchEvent(event, context, state, 'presschange', listener, true, delay);
dispatchEvent(event, context, state, 'presschange', listener, true);
}
function dispatchLongPressChangeEvent(
@@ -220,30 +217,15 @@ function dispatchLongPressChangeEvent(
context: ReactResponderContext,
props: PressProps,
state: PressState,
delay: number,
): void {
const bool = state.isLongPressed;
const listener = () => {
props.onLongPressChange(bool);
};
dispatchEvent(
event,
context,
state,
'longpresschange',
listener,
true,
delay,
);
dispatchEvent(event, context, state, 'longpresschange', listener, true);
}
function activate(
event: ReactResponderEvent,
context,
props,
state,
delay: number,
) {
function activate(event: ReactResponderEvent, context, props, state) {
const nativeEvent: any = event.nativeEvent;
const {x, y} = getEventPageCoords(nativeEvent);
const wasActivePressed = state.isActivePressed;
@@ -263,41 +245,26 @@ function activate(
'pressstart',
props.onPressStart,
true,
delay,
);
}
if (!wasActivePressed && props.onPressChange) {
dispatchPressChangeEvent(event, context, props, state, delay);
dispatchPressChangeEvent(event, context, props, state);
}
}
function deactivate(
event: ?ReactResponderEvent,
context,
props,
state,
delay: number,
) {
function deactivate(event: ?ReactResponderEvent, context, props, state) {
const wasLongPressed = state.isLongPressed;
state.isActivePressed = false;
state.isLongPressed = false;
if (props.onPressEnd) {
dispatchEvent(
event,
context,
state,
'pressend',
props.onPressEnd,
true,
delay,
);
dispatchEvent(event, context, state, 'pressend', props.onPressEnd, true);
}
if (props.onPressChange) {
dispatchPressChangeEvent(event, context, props, state, delay);
dispatchPressChangeEvent(event, context, props, state);
}
if (wasLongPressed && props.onLongPressChange) {
dispatchLongPressChangeEvent(event, context, props, state, delay);
dispatchLongPressChangeEvent(event, context, props, state);
}
}
@@ -314,9 +281,9 @@ function dispatchPressStartEvents(
state.pressEndTimeout = null;
}
const dispatch = (delay: number) => {
const dispatch = () => {
state.isActivePressStart = true;
activate(event, context, props, state, delay);
activate(event, context, props, state);
if (
(props.onLongPress || props.onLongPressChange) &&
@@ -338,17 +305,10 @@ function dispatchPressStartEvents(
'longpress',
props.onLongPress,
true,
delayLongPress + delay,
);
}
if (props.onLongPressChange) {
dispatchLongPressChangeEvent(
event,
context,
props,
state,
delayLongPress + delay,
);
dispatchLongPressChangeEvent(event, context, props, state);
}
}, delayLongPress);
}
@@ -363,10 +323,10 @@ function dispatchPressStartEvents(
if (delayPressStart > 0) {
state.pressStartTimeout = context.setTimeout(() => {
state.pressStartTimeout = null;
dispatch(delayPressStart);
dispatch();
}, delayPressStart);
} else {
dispatch(0);
dispatch();
}
}
}
@@ -394,7 +354,7 @@ function dispatchPressEndEvents(
// don't activate if a press has moved beyond the responder region
if (state.isPressWithinResponderRegion && event != null) {
// if we haven't yet activated (due to delays), activate now
activate(event, context, props, state, 0);
activate(event, context, props, state);
activationWasForced = true;
}
}
@@ -411,10 +371,10 @@ function dispatchPressEndEvents(
if (delayPressEnd > 0) {
state.pressEndTimeout = context.setTimeout(() => {
state.pressEndTimeout = null;
deactivate(event, context, props, state, delayPressEnd);
deactivate(event, context, props, state);
}, delayPressEnd);
} else {
deactivate(event, context, props, state, 0);
deactivate(event, context, props, state);
}
}
}
@@ -758,7 +718,6 @@ const PressResponder = {
'pressmove',
props.onPressMove,
false,
0,
);
}
if (
@@ -843,7 +802,6 @@ const PressResponder = {
'press',
props.onPress,
true,
0,
);
}
}
+4 -1
View File
@@ -40,11 +40,13 @@ type SwipeEventType = 'swipeleft' | 'swiperight' | 'swipeend' | 'swipemove';
type SwipeEvent = {|
target: Element | Document,
type: SwipeEventType,
timeStamp: number,
diffX?: number,
diffY?: number,
|};
function createSwipeEvent(
context: ReactResponderContext,
type: SwipeEventType,
target: Element | Document,
eventData?: EventData,
@@ -52,6 +54,7 @@ function createSwipeEvent(
return {
target,
type,
timeStamp: context.getTimeStamp(),
...eventData,
};
}
@@ -65,7 +68,7 @@ function dispatchSwipeEvent(
eventData?: EventData,
) {
const target = ((state.swipeTarget: any): Element | Document);
const syntheticEvent = createSwipeEvent(name, target, eventData);
const syntheticEvent = createSwipeEvent(context, name, target, eventData);
context.dispatchEvent(syntheticEvent, listener, {discrete});
}
+1
View File
@@ -193,4 +193,5 @@ export type ReactResponderContext = {
event: ReactResponderEvent,
): '' | 'mouse' | 'keyboard' | 'pen' | 'touch',
getEventCurrentTarget(event: ReactResponderEvent): Element,
getTimeStamp: () => number,
};