import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
import assign from 'shared/assign';
import getEventCharCode from './getEventCharCode';
type EventInterfaceType = {
[propName: string]: 0 | ((event: {[propName: string]: mixed, ...}) => mixed),
};
function functionThatReturnsTrue() {
return true;
}
function functionThatReturnsFalse() {
return false;
}
function createSyntheticEvent(Interface: EventInterfaceType) {
function SyntheticBaseEvent(
reactName: string | null,
reactEventType: string,
targetInst: Fiber | null,
nativeEvent: {[propName: string]: mixed, ...},
nativeEventTarget: null | EventTarget,
) {
this._reactName = reactName;
this._targetInst = targetInst;
this.type = reactEventType;
this.nativeEvent = nativeEvent;
this.target = nativeEventTarget;
this.currentTarget = null;
for (const propName in Interface) {
if (!Interface.hasOwnProperty(propName)) {
continue;
}
const normalize = Interface[propName];
if (normalize) {
this[propName] = normalize(nativeEvent);
} else {
this[propName] = nativeEvent[propName];
}
}
const defaultPrevented =
nativeEvent.defaultPrevented != null
? nativeEvent.defaultPrevented
: nativeEvent.returnValue === false;
if (defaultPrevented) {
this.isDefaultPrevented = functionThatReturnsTrue;
} else {
this.isDefaultPrevented = functionThatReturnsFalse;
}
this.isPropagationStopped = functionThatReturnsFalse;
return this;
}
assign(SyntheticBaseEvent.prototype, {
preventDefault: function () {
this.defaultPrevented = true;
const event = this.nativeEvent;
if (!event) {
return;
}
if (event.preventDefault) {
event.preventDefault();
} else if (typeof event.returnValue !== 'unknown') {
event.returnValue = false;
}
this.isDefaultPrevented = functionThatReturnsTrue;
},
stopPropagation: function () {
const event = this.nativeEvent;
if (!event) {
return;
}
if (event.stopPropagation) {
event.stopPropagation();
} else if (typeof event.cancelBubble !== 'unknown') {
event.cancelBubble = true;
}
this.isPropagationStopped = functionThatReturnsTrue;
},
persist: function () {
},
isPersistent: functionThatReturnsTrue,
});
return SyntheticBaseEvent;
}
const EventInterface = {
eventPhase: 0,
bubbles: 0,
cancelable: 0,
timeStamp: function (event: {[propName: string]: mixed}) {
return event.timeStamp || Date.now();
},
defaultPrevented: 0,
isTrusted: 0,
};
export const SyntheticEvent: $FlowFixMe = createSyntheticEvent(EventInterface);
const UIEventInterface: EventInterfaceType = {
...EventInterface,
view: 0,
detail: 0,
};
export const SyntheticUIEvent: $FlowFixMe =
createSyntheticEvent(UIEventInterface);
let lastMovementX;
let lastMovementY;
let lastMouseEvent;
function updateMouseMovementPolyfillState(event: {[propName: string]: mixed}) {
if (event !== lastMouseEvent) {
if (lastMouseEvent && event.type === 'mousemove') {
lastMovementX = event.screenX - lastMouseEvent.screenX;
lastMovementY = event.screenY - lastMouseEvent.screenY;
} else {
lastMovementX = 0;
lastMovementY = 0;
}
lastMouseEvent = event;
}
}
const MouseEventInterface: EventInterfaceType = {
...UIEventInterface,
screenX: 0,
screenY: 0,
clientX: 0,
clientY: 0,
pageX: 0,
pageY: 0,
ctrlKey: 0,
shiftKey: 0,
altKey: 0,
metaKey: 0,
getModifierState: getEventModifierState,
button: 0,
buttons: 0,
relatedTarget: function (event) {
if (event.relatedTarget === undefined)
return event.fromElement === event.srcElement
? event.toElement
: event.fromElement;
return event.relatedTarget;
},
movementX: function (event) {
if ('movementX' in event) {
return event.movementX;
}
updateMouseMovementPolyfillState(event);
return lastMovementX;
},
movementY: function (event) {
if ('movementY' in event) {
return event.movementY;
}
return lastMovementY;
},
};
export const SyntheticMouseEvent: $FlowFixMe =
createSyntheticEvent(MouseEventInterface);
const DragEventInterface: EventInterfaceType = {
...MouseEventInterface,
dataTransfer: 0,
};
export const SyntheticDragEvent: $FlowFixMe =
createSyntheticEvent(DragEventInterface);
const FocusEventInterface: EventInterfaceType = {
...UIEventInterface,
relatedTarget: 0,
};
export const SyntheticFocusEvent: $FlowFixMe =
createSyntheticEvent(FocusEventInterface);
const AnimationEventInterface: EventInterfaceType = {
...EventInterface,
animationName: 0,
elapsedTime: 0,
pseudoElement: 0,
};
export const SyntheticAnimationEvent: $FlowFixMe = createSyntheticEvent(
AnimationEventInterface,
);
const ClipboardEventInterface: EventInterfaceType = {
...EventInterface,
clipboardData: function (event) {
return 'clipboardData' in event
? event.clipboardData
: window.clipboardData;
},
};
export const SyntheticClipboardEvent: $FlowFixMe = createSyntheticEvent(
ClipboardEventInterface,
);
const CompositionEventInterface: EventInterfaceType = {
...EventInterface,
data: 0,
};
export const SyntheticCompositionEvent: $FlowFixMe = createSyntheticEvent(
CompositionEventInterface,
);
export const SyntheticInputEvent = SyntheticCompositionEvent;
const normalizeKey = {
Esc: 'Escape',
Spacebar: ' ',
Left: 'ArrowLeft',
Up: 'ArrowUp',
Right: 'ArrowRight',
Down: 'ArrowDown',
Del: 'Delete',
Win: 'OS',
Menu: 'ContextMenu',
Apps: 'ContextMenu',
Scroll: 'ScrollLock',
MozPrintableKey: 'Unidentified',
};
const translateToKey = {
'8': 'Backspace',
'9': 'Tab',
'12': 'Clear',
'13': 'Enter',
'16': 'Shift',
'17': 'Control',
'18': 'Alt',
'19': 'Pause',
'20': 'CapsLock',
'27': 'Escape',
'32': ' ',
'33': 'PageUp',
'34': 'PageDown',
'35': 'End',
'36': 'Home',
'37': 'ArrowLeft',
'38': 'ArrowUp',
'39': 'ArrowRight',
'40': 'ArrowDown',
'45': 'Insert',
'46': 'Delete',
'112': 'F1',
'113': 'F2',
'114': 'F3',
'115': 'F4',
'116': 'F5',
'117': 'F6',
'118': 'F7',
'119': 'F8',
'120': 'F9',
'121': 'F10',
'122': 'F11',
'123': 'F12',
'144': 'NumLock',
'145': 'ScrollLock',
'224': 'Meta',
};
function getEventKey(nativeEvent: {[propName: string]: mixed}) {
if (nativeEvent.key) {
const key =
normalizeKey[nativeEvent.key] || nativeEvent.key;
if (key !== 'Unidentified') {
return key;
}
}
if (nativeEvent.type === 'keypress') {
const charCode = getEventCharCode(
nativeEvent,
);
return charCode === 13 ? 'Enter' : String.fromCharCode(charCode);
}
if (nativeEvent.type === 'keydown' || nativeEvent.type === 'keyup') {
return translateToKey[nativeEvent.keyCode] || 'Unidentified';
}
return '';
}
const modifierKeyToProp = {
Alt: 'altKey',
Control: 'ctrlKey',
Meta: 'metaKey',
Shift: 'shiftKey',
};
function modifierStateGetter(keyArg) {
const syntheticEvent = this;
const nativeEvent = syntheticEvent.nativeEvent;
if (nativeEvent.getModifierState) {
return nativeEvent.getModifierState(keyArg);
}
const keyProp = modifierKeyToProp[keyArg];
return keyProp ? !!nativeEvent[keyProp] : false;
}
function getEventModifierState(nativeEvent: {[propName: string]: mixed}) {
return modifierStateGetter;
}
const KeyboardEventInterface = {
...UIEventInterface,
key: getEventKey,
code: 0,
location: 0,
ctrlKey: 0,
shiftKey: 0,
altKey: 0,
metaKey: 0,
repeat: 0,
locale: 0,
getModifierState: getEventModifierState,
charCode: function (event: {[propName: string]: mixed}) {
if (event.type === 'keypress') {
return getEventCharCode(
event,
);
}
return 0;
},
keyCode: function (event: {[propName: string]: mixed}) {
if (event.type === 'keydown' || event.type === 'keyup') {
return event.keyCode;
}
return 0;
},
which: function (event: {[propName: string]: mixed}) {
if (event.type === 'keypress') {
return getEventCharCode(
event,
);
}
if (event.type === 'keydown' || event.type === 'keyup') {
return event.keyCode;
}
return 0;
},
};
export const SyntheticKeyboardEvent: $FlowFixMe = createSyntheticEvent(
KeyboardEventInterface,
);
const PointerEventInterface = {
...MouseEventInterface,
pointerId: 0,
width: 0,
height: 0,
pressure: 0,
tangentialPressure: 0,
tiltX: 0,
tiltY: 0,
twist: 0,
pointerType: 0,
isPrimary: 0,
};
export const SyntheticPointerEvent: $FlowFixMe = createSyntheticEvent(
PointerEventInterface,
);
const TouchEventInterface = {
...UIEventInterface,
touches: 0,
targetTouches: 0,
changedTouches: 0,
altKey: 0,
metaKey: 0,
ctrlKey: 0,
shiftKey: 0,
getModifierState: getEventModifierState,
};
export const SyntheticTouchEvent: $FlowFixMe =
createSyntheticEvent(TouchEventInterface);
const TransitionEventInterface = {
...EventInterface,
propertyName: 0,
elapsedTime: 0,
pseudoElement: 0,
};
export const SyntheticTransitionEvent: $FlowFixMe = createSyntheticEvent(
TransitionEventInterface,
);
const WheelEventInterface = {
...MouseEventInterface,
deltaX(event: {[propName: string]: mixed}) {
return 'deltaX' in event
? event.deltaX
:
'wheelDeltaX' in event
?
-event.wheelDeltaX
: 0;
},
deltaY(event: {[propName: string]: mixed}) {
return 'deltaY' in event
? event.deltaY
:
'wheelDeltaY' in event
?
-event.wheelDeltaY
:
'wheelDelta' in event
?
-event.wheelDelta
: 0;
},
deltaZ: 0,
deltaMode: 0,
};
export const SyntheticWheelEvent: $FlowFixMe =
createSyntheticEvent(WheelEventInterface);
const ToggleEventInterface = {
...EventInterface,
newState: 0,
oldState: 0,
};
export const SyntheticToggleEvent: $FlowFixMe =
createSyntheticEvent(ToggleEventInterface);