mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Refactor event object creation for the experimental event API (#15295)
* Refactor event object creation for the experimental event API
This commit is contained in:
@@ -7,29 +7,29 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import SyntheticEvent from 'events/SyntheticEvent';
|
||||
import type {AnyNativeEvent} from 'events/PluginModuleType';
|
||||
import type {ReactEventResponderEventType} from 'shared/ReactTypes';
|
||||
|
||||
export type EventResponderContext = {
|
||||
event: AnyNativeEvent,
|
||||
eventTarget: EventTarget,
|
||||
eventTarget: Element | Document,
|
||||
eventType: string,
|
||||
isPassive: () => boolean,
|
||||
isPassiveSupported: () => boolean,
|
||||
dispatchEvent: (
|
||||
name: string,
|
||||
listener: (e: SyntheticEvent) => void | null,
|
||||
pressTarget: EventTarget | null,
|
||||
discrete: boolean,
|
||||
extraProperties?: Object,
|
||||
dispatchEvent: <E>(
|
||||
eventObject: E,
|
||||
{
|
||||
capture?: boolean,
|
||||
discrete?: boolean,
|
||||
stopPropagation?: boolean,
|
||||
},
|
||||
) => void,
|
||||
isTargetWithinElement: (
|
||||
childTarget: EventTarget,
|
||||
parentTarget: EventTarget,
|
||||
childTarget: Element | Document,
|
||||
parentTarget: Element | Document,
|
||||
) => boolean,
|
||||
isTargetOwned: EventTarget => boolean,
|
||||
isTargetWithinEventComponent: EventTarget => boolean,
|
||||
isTargetOwned: (Element | Document) => boolean,
|
||||
isTargetWithinEventComponent: (Element | Document) => boolean,
|
||||
isPositionWithinTouchHitTarget: (x: number, y: number) => boolean,
|
||||
addRootEventTypes: (
|
||||
rootEventTypes: Array<ReactEventResponderEventType>,
|
||||
@@ -37,6 +37,6 @@ export type EventResponderContext = {
|
||||
removeRootEventTypes: (
|
||||
rootEventTypes: Array<ReactEventResponderEventType>,
|
||||
) => void,
|
||||
requestOwnership: (target: EventTarget | null) => boolean,
|
||||
releaseOwnership: (target: EventTarget | null) => boolean,
|
||||
requestOwnership: (target: Element | Document | null) => boolean,
|
||||
releaseOwnership: (target: Element | Document | null) => boolean,
|
||||
};
|
||||
|
||||
+132
-58
@@ -18,15 +18,13 @@ import type {
|
||||
ReactEventResponderEventType,
|
||||
} from 'shared/ReactTypes';
|
||||
import type {DOMTopLevelEventType} from 'events/TopLevelEventTypes';
|
||||
import SyntheticEvent from 'events/SyntheticEvent';
|
||||
import {runEventsInBatch} from 'events/EventBatching';
|
||||
import {interactiveUpdates} from 'events/ReactGenericBatching';
|
||||
import {executeDispatch} from 'events/EventPluginUtils';
|
||||
import {batchedUpdates, interactiveUpdates} from 'events/ReactGenericBatching';
|
||||
import type {Fiber} from 'react-reconciler/src/ReactFiber';
|
||||
|
||||
import {getClosestInstanceFromNode} from '../client/ReactDOMComponentTree';
|
||||
|
||||
import {enableEventAPI} from 'shared/ReactFeatureFlags';
|
||||
import {invokeGuardedCallbackAndCatchFirstError} from 'shared/ReactErrorUtils';
|
||||
import warning from 'shared/warning';
|
||||
|
||||
let listenToResponderEventTypesImpl;
|
||||
|
||||
@@ -36,6 +34,8 @@ export function setListenToResponderEventTypes(
|
||||
listenToResponderEventTypesImpl = _listenToResponderEventTypesImpl;
|
||||
}
|
||||
|
||||
const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;
|
||||
|
||||
const rootEventTypesToEventComponents: Map<
|
||||
DOMTopLevelEventType | string,
|
||||
Set<Fiber>,
|
||||
@@ -45,12 +45,76 @@ const targetEventTypeCached: Map<
|
||||
Set<DOMTopLevelEventType>,
|
||||
> = new Map();
|
||||
const targetOwnership: Map<EventTarget, Fiber> = new Map();
|
||||
const eventsWithStopPropagation:
|
||||
| WeakSet
|
||||
| Set<$Shape<PartialEventObject>> = new PossiblyWeakSet();
|
||||
|
||||
type EventListener = (event: SyntheticEvent) => void;
|
||||
type PartialEventObject = {
|
||||
listener: ($Shape<PartialEventObject>) => void,
|
||||
target: Element | Document,
|
||||
type: string,
|
||||
};
|
||||
type EventQueue = {
|
||||
bubble: null | Array<$Shape<PartialEventObject>>,
|
||||
capture: null | Array<$Shape<PartialEventObject>>,
|
||||
discrete: boolean,
|
||||
phase: EventQueuePhase,
|
||||
};
|
||||
type EventQueuePhase = 0 | 1;
|
||||
|
||||
function copyEventProperties(eventData, syntheticEvent) {
|
||||
for (let propName in eventData) {
|
||||
syntheticEvent[propName] = eventData[propName];
|
||||
const DURING_EVENT_PHASE = 0;
|
||||
const AFTER_EVENT_PHASE = 1;
|
||||
|
||||
function createEventQueue(phase: EventQueuePhase): EventQueue {
|
||||
return {
|
||||
bubble: null,
|
||||
capture: null,
|
||||
discrete: false,
|
||||
phase,
|
||||
};
|
||||
}
|
||||
|
||||
function processEvent(event: $Shape<PartialEventObject>): void {
|
||||
const type = event.type;
|
||||
const listener = event.listener;
|
||||
invokeGuardedCallbackAndCatchFirstError(type, listener, undefined, event);
|
||||
}
|
||||
|
||||
function processEvents(
|
||||
bubble: null | Array<$Shape<PartialEventObject>>,
|
||||
capture: null | Array<$Shape<PartialEventObject>>,
|
||||
): void {
|
||||
let i, length;
|
||||
|
||||
if (capture !== null) {
|
||||
for (i = capture.length; i-- > 0; ) {
|
||||
const event = capture[i];
|
||||
processEvent(capture[i]);
|
||||
if (eventsWithStopPropagation.has(event)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bubble !== null) {
|
||||
for (i = 0, length = bubble.length; i < length; ++i) {
|
||||
const event = bubble[i];
|
||||
processEvent(event);
|
||||
if (eventsWithStopPropagation.has(event)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function processEventQueue(eventQueue: EventQueue): void {
|
||||
const {bubble, capture, discrete} = eventQueue;
|
||||
|
||||
if (discrete) {
|
||||
interactiveUpdates(() => {
|
||||
processEvents(bubble, capture);
|
||||
});
|
||||
} else {
|
||||
processEvents(bubble, capture);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +134,7 @@ function DOMEventResponderContext(
|
||||
this._discreteEvents = null;
|
||||
this._nonDiscreteEvents = null;
|
||||
this._isBatching = true;
|
||||
this._eventQueue = createEventQueue(DURING_EVENT_PHASE);
|
||||
}
|
||||
|
||||
DOMEventResponderContext.prototype.isPassive = function(): boolean {
|
||||
@@ -81,49 +146,66 @@ DOMEventResponderContext.prototype.isPassiveSupported = function(): boolean {
|
||||
};
|
||||
|
||||
DOMEventResponderContext.prototype.dispatchEvent = function(
|
||||
eventName: string,
|
||||
eventListener: EventListener,
|
||||
eventTarget: AnyNativeEvent,
|
||||
discrete: boolean,
|
||||
extraProperties?: Object,
|
||||
possibleEventObject: Object,
|
||||
{
|
||||
capture,
|
||||
discrete,
|
||||
stopPropagation,
|
||||
}: {
|
||||
capture?: boolean,
|
||||
discrete?: boolean,
|
||||
stopPropagation?: boolean,
|
||||
},
|
||||
): void {
|
||||
const eventTargetFiber = getClosestInstanceFromNode(eventTarget);
|
||||
const syntheticEvent = SyntheticEvent.getPooled(
|
||||
null,
|
||||
eventTargetFiber,
|
||||
this.event,
|
||||
eventTarget,
|
||||
);
|
||||
if (extraProperties !== undefined) {
|
||||
copyEventProperties(extraProperties, syntheticEvent);
|
||||
}
|
||||
syntheticEvent.type = eventName;
|
||||
syntheticEvent._dispatchInstances = [eventTargetFiber];
|
||||
syntheticEvent._dispatchListeners = [eventListener];
|
||||
const eventQueue = this._eventQueue;
|
||||
const {listener, target, type} = possibleEventObject;
|
||||
|
||||
if (this._isBatching) {
|
||||
let events;
|
||||
if (discrete) {
|
||||
events = this._discreteEvents;
|
||||
if (events === null) {
|
||||
events = this._discreteEvents = [];
|
||||
}
|
||||
} else {
|
||||
events = this._nonDiscreteEvents;
|
||||
if (events === null) {
|
||||
events = this._nonDiscreteEvents = [];
|
||||
}
|
||||
if (listener == null || target == null || type == null) {
|
||||
throw new Error(
|
||||
'context.dispatchEvent: "listener", "target" and "type" fields on event object are required.',
|
||||
);
|
||||
}
|
||||
if (__DEV__) {
|
||||
possibleEventObject.preventDefault = () => {
|
||||
// Update this warning when we have a story around dealing with preventDefault
|
||||
warning(
|
||||
false,
|
||||
'preventDefault() is no longer available on event objects created from event responder modules.',
|
||||
);
|
||||
};
|
||||
possibleEventObject.stopPropagation = () => {
|
||||
// Update this warning when we have a story around dealing with stopPropgation
|
||||
warning(
|
||||
false,
|
||||
'stopPropagation() is no longer available on event objects created from event responder modules.',
|
||||
);
|
||||
};
|
||||
}
|
||||
const eventObject = ((possibleEventObject: any): $Shape<PartialEventObject>);
|
||||
let events;
|
||||
|
||||
if (capture) {
|
||||
events = eventQueue.capture;
|
||||
if (events === null) {
|
||||
events = eventQueue.capture = [];
|
||||
}
|
||||
events.push(syntheticEvent);
|
||||
} else {
|
||||
if (discrete) {
|
||||
interactiveUpdates(() => {
|
||||
executeDispatch(syntheticEvent, eventListener, eventTargetFiber);
|
||||
});
|
||||
} else {
|
||||
executeDispatch(syntheticEvent, eventListener, eventTargetFiber);
|
||||
events = eventQueue.bubble;
|
||||
if (events === null) {
|
||||
events = eventQueue.bubble = [];
|
||||
}
|
||||
}
|
||||
if (discrete) {
|
||||
eventQueue.discrete = true;
|
||||
}
|
||||
events.push(eventObject);
|
||||
|
||||
if (stopPropagation) {
|
||||
eventsWithStopPropagation.add(eventObject);
|
||||
}
|
||||
if (eventQueue.phase === AFTER_EVENT_PHASE) {
|
||||
batchedUpdates(processEventQueue, eventQueue);
|
||||
}
|
||||
};
|
||||
|
||||
DOMEventResponderContext.prototype.isTargetWithinEventComponent = function(
|
||||
@@ -318,17 +400,9 @@ export function runResponderEventsInBatch(
|
||||
);
|
||||
}
|
||||
}
|
||||
// Run batched events
|
||||
const discreteEvents = context._discreteEvents;
|
||||
if (discreteEvents !== null) {
|
||||
interactiveUpdates(() => {
|
||||
runEventsInBatch(discreteEvents);
|
||||
});
|
||||
}
|
||||
const nonDiscreteEvents = context._nonDiscreteEvents;
|
||||
if (nonDiscreteEvents !== null) {
|
||||
runEventsInBatch(nonDiscreteEvents);
|
||||
}
|
||||
context._isBatching = false;
|
||||
processEventQueue(context._eventQueue);
|
||||
// In order to capture and process async events from responder modules
|
||||
// we create a new event queue.
|
||||
context._eventQueue = createEventQueue(AFTER_EVENT_PHASE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,12 +228,12 @@ describe('DOMEventResponderSystem', () => {
|
||||
['click'],
|
||||
(context, props) => {
|
||||
if (props.onMagicClick) {
|
||||
context.dispatchEvent(
|
||||
'magicclick',
|
||||
props.onMagicClick,
|
||||
context.eventTarget,
|
||||
false,
|
||||
);
|
||||
const event = {
|
||||
listener: props.onMagicClick,
|
||||
target: context.eventTarget,
|
||||
type: 'magicclick',
|
||||
};
|
||||
context.dispatchEvent(event, {discrete: true});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
Vendored
+41
-12
@@ -14,7 +14,7 @@ const targetEventTypes = ['pointerdown', 'pointercancel'];
|
||||
const rootEventTypes = ['pointerup', {name: 'pointermove', passive: false}];
|
||||
|
||||
type DragState = {
|
||||
dragTarget: null | EventTarget,
|
||||
dragTarget: null | Element | Document,
|
||||
isPointerDown: boolean,
|
||||
isDragging: boolean,
|
||||
startX: number,
|
||||
@@ -33,18 +33,45 @@ if (typeof window !== 'undefined' && window.PointerEvent === undefined) {
|
||||
});
|
||||
}
|
||||
|
||||
type EventData = {
|
||||
diffX: number,
|
||||
diffY: number,
|
||||
};
|
||||
type DragEventType = 'dragend' | 'dragchange' | 'dragmove';
|
||||
|
||||
type DragEvent = {|
|
||||
listener: DragEvent => void,
|
||||
target: Element | Document,
|
||||
type: DragEventType,
|
||||
diffX?: number,
|
||||
diffY?: number,
|
||||
|};
|
||||
|
||||
function createDragEvent(
|
||||
type: DragEventType,
|
||||
target: Element | Document,
|
||||
listener: DragEvent => void,
|
||||
eventData?: EventData,
|
||||
): DragEvent {
|
||||
return {
|
||||
listener,
|
||||
target,
|
||||
type,
|
||||
...eventData,
|
||||
};
|
||||
}
|
||||
|
||||
function dispatchDragEvent(
|
||||
context: EventResponderContext,
|
||||
name: string,
|
||||
listener: (e: Object) => void,
|
||||
name: DragEventType,
|
||||
listener: DragEvent => void,
|
||||
state: DragState,
|
||||
discrete: boolean,
|
||||
eventData?: {
|
||||
diffX: number,
|
||||
diffY: number,
|
||||
},
|
||||
eventData?: EventData,
|
||||
): void {
|
||||
context.dispatchEvent(name, listener, state.dragTarget, discrete, eventData);
|
||||
const target = ((state.dragTarget: any): Element | Document);
|
||||
const syntheticEvent = createDragEvent(name, target, listener, eventData);
|
||||
context.dispatchEvent(syntheticEvent, {discrete});
|
||||
}
|
||||
|
||||
const DragResponder = {
|
||||
@@ -112,10 +139,11 @@ const DragResponder = {
|
||||
const dragChangeEventListener = () => {
|
||||
props.onDragChange(true);
|
||||
};
|
||||
context.dispatchEvent(
|
||||
dispatchDragEvent(
|
||||
context,
|
||||
'dragchange',
|
||||
dragChangeEventListener,
|
||||
state.dragTarget,
|
||||
state,
|
||||
true,
|
||||
);
|
||||
}
|
||||
@@ -160,10 +188,11 @@ const DragResponder = {
|
||||
const dragChangeEventListener = () => {
|
||||
props.onDragChange(false);
|
||||
};
|
||||
context.dispatchEvent(
|
||||
dispatchDragEvent(
|
||||
context,
|
||||
'dragchange',
|
||||
dragChangeEventListener,
|
||||
state.dragTarget,
|
||||
state,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
Vendored
+34
-8
@@ -19,24 +19,49 @@ type FocusState = {
|
||||
isFocused: boolean,
|
||||
};
|
||||
|
||||
type FocusEventType = 'focus' | 'blur' | 'focuschange';
|
||||
|
||||
type FocusEvent = {|
|
||||
listener: FocusEvent => void,
|
||||
target: Element | Document,
|
||||
type: FocusEventType,
|
||||
|};
|
||||
|
||||
function createFocusEvent(
|
||||
type: FocusEventType,
|
||||
target: Element | Document,
|
||||
listener: FocusEvent => void,
|
||||
): FocusEvent {
|
||||
return {
|
||||
listener,
|
||||
target,
|
||||
type,
|
||||
};
|
||||
}
|
||||
|
||||
function dispatchFocusInEvents(context: EventResponderContext, props: Object) {
|
||||
const {event, eventTarget} = context;
|
||||
if (context.isTargetWithinEventComponent((event: any).relatedTarget)) {
|
||||
return;
|
||||
}
|
||||
if (props.onFocus) {
|
||||
context.dispatchEvent('focus', props.onFocus, eventTarget, true);
|
||||
const syntheticEvent = createFocusEvent(
|
||||
'focus',
|
||||
eventTarget,
|
||||
props.onFocus,
|
||||
);
|
||||
context.dispatchEvent(syntheticEvent, {discrete: true});
|
||||
}
|
||||
if (props.onFocusChange) {
|
||||
const focusChangeEventListener = () => {
|
||||
props.onFocusChange(true);
|
||||
};
|
||||
context.dispatchEvent(
|
||||
const syntheticEvent = createFocusEvent(
|
||||
'focuschange',
|
||||
focusChangeEventListener,
|
||||
eventTarget,
|
||||
true,
|
||||
focusChangeEventListener,
|
||||
);
|
||||
context.dispatchEvent(syntheticEvent, {discrete: true});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,18 +71,19 @@ function dispatchFocusOutEvents(context: EventResponderContext, props: Object) {
|
||||
return;
|
||||
}
|
||||
if (props.onBlur) {
|
||||
context.dispatchEvent('blur', props.onBlur, eventTarget, true);
|
||||
const syntheticEvent = createFocusEvent('blur', eventTarget, props.onBlur);
|
||||
context.dispatchEvent(syntheticEvent, {discrete: true});
|
||||
}
|
||||
if (props.onFocusChange) {
|
||||
const focusChangeEventListener = () => {
|
||||
props.onFocusChange(false);
|
||||
};
|
||||
context.dispatchEvent(
|
||||
const syntheticEvent = createFocusEvent(
|
||||
'focuschange',
|
||||
focusChangeEventListener,
|
||||
eventTarget,
|
||||
true,
|
||||
focusChangeEventListener,
|
||||
);
|
||||
context.dispatchEvent(syntheticEvent, {discrete: true});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+38
-8
@@ -23,6 +23,26 @@ type HoverState = {
|
||||
isTouched: boolean,
|
||||
};
|
||||
|
||||
type HoverEventType = 'hoverstart' | 'hoverend' | 'hoverchange';
|
||||
|
||||
type HoverEvent = {|
|
||||
listener: HoverEvent => void,
|
||||
target: Element | Document,
|
||||
type: HoverEventType,
|
||||
|};
|
||||
|
||||
function createHoverEvent(
|
||||
type: HoverEventType,
|
||||
target: Element | Document,
|
||||
listener: HoverEvent => void,
|
||||
): HoverEvent {
|
||||
return {
|
||||
listener,
|
||||
target,
|
||||
type,
|
||||
};
|
||||
}
|
||||
|
||||
// In the case we don't have PointerEvents (Safari), we listen to touch events
|
||||
// too
|
||||
if (typeof window !== 'undefined' && window.PointerEvent === undefined) {
|
||||
@@ -39,18 +59,23 @@ function dispatchHoverStartEvents(
|
||||
return;
|
||||
}
|
||||
if (props.onHoverStart) {
|
||||
context.dispatchEvent('hoverstart', props.onHoverStart, eventTarget, true);
|
||||
const syntheticEvent = createHoverEvent(
|
||||
'hoverstart',
|
||||
eventTarget,
|
||||
props.onHoverStart,
|
||||
);
|
||||
context.dispatchEvent(syntheticEvent, {discrete: true});
|
||||
}
|
||||
if (props.onHoverChange) {
|
||||
const hoverChangeEventListener = () => {
|
||||
props.onHoverChange(true);
|
||||
};
|
||||
context.dispatchEvent(
|
||||
const syntheticEvent = createHoverEvent(
|
||||
'hoverchange',
|
||||
hoverChangeEventListener,
|
||||
eventTarget,
|
||||
true,
|
||||
hoverChangeEventListener,
|
||||
);
|
||||
context.dispatchEvent(syntheticEvent, {discrete: true});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,18 +85,23 @@ function dispatchHoverEndEvents(context: EventResponderContext, props: Object) {
|
||||
return;
|
||||
}
|
||||
if (props.onHoverEnd) {
|
||||
context.dispatchEvent('hoverend', props.onHoverEnd, eventTarget, true);
|
||||
const syntheticEvent = createHoverEvent(
|
||||
'hoverend',
|
||||
eventTarget,
|
||||
props.onHoverEnd,
|
||||
);
|
||||
context.dispatchEvent(syntheticEvent, {discrete: true});
|
||||
}
|
||||
if (props.onHoverChange) {
|
||||
const hoverChangeEventListener = () => {
|
||||
props.onHoverChange(false);
|
||||
};
|
||||
context.dispatchEvent(
|
||||
const syntheticEvent = createHoverEvent(
|
||||
'hoverchange',
|
||||
hoverChangeEventListener,
|
||||
eventTarget,
|
||||
true,
|
||||
hoverChangeEventListener,
|
||||
);
|
||||
context.dispatchEvent(syntheticEvent, {discrete: true});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+44
-31
@@ -36,13 +36,13 @@ type PressProps = {
|
||||
delayLongPress: number,
|
||||
delayPressEnd: number,
|
||||
delayPressStart: number,
|
||||
onLongPress: (e: Object) => void,
|
||||
onLongPress: (e: PressEvent) => void,
|
||||
onLongPressChange: boolean => void,
|
||||
onLongPressShouldCancelPress: () => boolean,
|
||||
onPress: (e: Object) => void,
|
||||
onPress: (e: PressEvent) => void,
|
||||
onPressChange: boolean => void,
|
||||
onPressEnd: (e: Object) => void,
|
||||
onPressStart: (e: Object) => void,
|
||||
onPressEnd: (e: PressEvent) => void,
|
||||
onPressStart: (e: PressEvent) => void,
|
||||
pressRententionOffset: Object,
|
||||
};
|
||||
|
||||
@@ -52,17 +52,45 @@ type PressState = {
|
||||
isLongPressed: boolean,
|
||||
isPressed: boolean,
|
||||
longPressTimeout: null | TimeoutID,
|
||||
pressTarget: null | EventTarget,
|
||||
pressTarget: null | Element | Document,
|
||||
shouldSkipMouseAfterTouch: boolean,
|
||||
};
|
||||
|
||||
type PressEventType =
|
||||
| 'press'
|
||||
| 'pressstart'
|
||||
| 'pressend'
|
||||
| 'presschange'
|
||||
| 'longpress'
|
||||
| 'longpresschange';
|
||||
|
||||
type PressEvent = {|
|
||||
listener: PressEvent => void,
|
||||
target: Element | Document,
|
||||
type: PressEventType,
|
||||
|};
|
||||
|
||||
function createPressEvent(
|
||||
type: PressEventType,
|
||||
target: Element | Document,
|
||||
listener: PressEvent => void,
|
||||
): PressEvent {
|
||||
return {
|
||||
listener,
|
||||
target,
|
||||
type,
|
||||
};
|
||||
}
|
||||
|
||||
function dispatchPressEvent(
|
||||
context: EventResponderContext,
|
||||
state: PressState,
|
||||
name: string,
|
||||
name: PressEventType,
|
||||
listener: (e: Object) => void,
|
||||
): void {
|
||||
context.dispatchEvent(name, listener, state.pressTarget, true);
|
||||
const target = ((state.pressTarget: any): Element | Document);
|
||||
const syntheticEvent = createPressEvent(name, target, listener);
|
||||
context.dispatchEvent(syntheticEvent, {discrete: true});
|
||||
}
|
||||
|
||||
function dispatchPressStartEvents(
|
||||
@@ -97,9 +125,10 @@ function dispatchPressStartEvents(
|
||||
if (props.onLongPress) {
|
||||
const longPressEventListener = e => {
|
||||
props.onLongPress(e);
|
||||
if (e.nativeEvent.defaultPrevented) {
|
||||
state.defaultPrevented = true;
|
||||
}
|
||||
// TODO address this again at some point
|
||||
// if (e.nativeEvent.defaultPrevented) {
|
||||
// state.defaultPrevented = true;
|
||||
// }
|
||||
};
|
||||
dispatchPressEvent(context, state, 'longpress', longPressEventListener);
|
||||
}
|
||||
@@ -193,24 +222,7 @@ const PressResponder = {
|
||||
) {
|
||||
return;
|
||||
}
|
||||
let keyPressEventListener = props.onPress;
|
||||
|
||||
// Wrap listener with prevent default behaviour, unless
|
||||
// we are dealing with an anchor. Anchor tags are special beacuse
|
||||
// we need to use the "click" event, to properly allow browser
|
||||
// heuristics for cancelling link clicks. Furthermore, iOS and
|
||||
// Android can show previous of anchor tags that requires working
|
||||
// with click rather than touch events (and mouse down/up).
|
||||
if (!isAnchorTagElement(eventTarget)) {
|
||||
keyPressEventListener = e => {
|
||||
if (!e.isDefaultPrevented() && !e.nativeEvent.defaultPrevented) {
|
||||
e.preventDefault();
|
||||
state.defaultPrevented = true;
|
||||
props.onPress(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
dispatchPressEvent(context, state, 'press', keyPressEventListener);
|
||||
dispatchPressEvent(context, state, 'press', props.onPress);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -332,9 +344,10 @@ const PressResponder = {
|
||||
) {
|
||||
const pressEventListener = e => {
|
||||
props.onPress(e);
|
||||
if (e.nativeEvent.defaultPrevented) {
|
||||
state.defaultPrevented = true;
|
||||
}
|
||||
// TODO address this again at some point
|
||||
// if (e.nativeEvent.defaultPrevented) {
|
||||
// state.defaultPrevented = true;
|
||||
// }
|
||||
};
|
||||
dispatchPressEvent(context, state, 'press', pressEventListener);
|
||||
}
|
||||
|
||||
Vendored
+35
-8
@@ -23,18 +23,45 @@ if (typeof window !== 'undefined' && window.PointerEvent === undefined) {
|
||||
});
|
||||
}
|
||||
|
||||
type EventData = {
|
||||
diffX: number,
|
||||
diffY: number,
|
||||
};
|
||||
type SwipeEventType = 'swipeleft' | 'swiperight' | 'swipeend' | 'swipemove';
|
||||
|
||||
type SwipeEvent = {|
|
||||
listener: SwipeEvent => void,
|
||||
target: Element | Document,
|
||||
type: SwipeEventType,
|
||||
diffX?: number,
|
||||
diffY?: number,
|
||||
|};
|
||||
|
||||
function createSwipeEvent(
|
||||
type: SwipeEventType,
|
||||
target: Element | Document,
|
||||
listener: SwipeEvent => void,
|
||||
eventData?: EventData,
|
||||
): SwipeEvent {
|
||||
return {
|
||||
listener,
|
||||
target,
|
||||
type,
|
||||
...eventData,
|
||||
};
|
||||
}
|
||||
|
||||
function dispatchSwipeEvent(
|
||||
context: EventResponderContext,
|
||||
name: string,
|
||||
listener: (e: Object) => void,
|
||||
name: SwipeEventType,
|
||||
listener: SwipeEvent => void,
|
||||
state: SwipeState,
|
||||
discrete: boolean,
|
||||
eventData?: {
|
||||
diffX: number,
|
||||
diffY: number,
|
||||
},
|
||||
eventData?: EventData,
|
||||
) {
|
||||
context.dispatchEvent(name, listener, state.swipeTarget, discrete, eventData);
|
||||
const target = ((state.swipeTarget: any): Element | Document);
|
||||
const syntheticEvent = createSwipeEvent(name, target, listener, eventData);
|
||||
context.dispatchEvent(syntheticEvent, {discrete});
|
||||
}
|
||||
|
||||
type SwipeState = {
|
||||
@@ -44,7 +71,7 @@ type SwipeState = {
|
||||
startX: number,
|
||||
startY: number,
|
||||
touchId: null | number,
|
||||
swipeTarget: null | EventTarget,
|
||||
swipeTarget: null | Element | Document,
|
||||
x: number,
|
||||
y: number,
|
||||
};
|
||||
|
||||
@@ -388,8 +388,10 @@ describe('Event responder: Press', () => {
|
||||
|
||||
events = [];
|
||||
ref.current.dispatchEvent(createKeyboardEvent('keydown', {key: 'Enter'}));
|
||||
// Outer press should not occur as inner press will preventDefault
|
||||
expect(events).toEqual(['keydown', 'inner: onPress']);
|
||||
// TODO update this test once we have a form of stopPropagation in
|
||||
// the responder system again. This test had to be updated because
|
||||
// we have removed stopPropagation() from synthetic events.
|
||||
expect(events).toEqual(['keydown', 'inner: onPress', 'outer: onPress']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user