import type {AnyNativeEvent} from '../PluginModuleType';
import type {DOMEventName} from '../DOMEventNames';
import type {DispatchQueue} from '../DOMPluginEventSystem';
import type {EventSystemFlags} from '../EventSystemFlags';
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
import type {KnownReactSyntheticEvent} from '../ReactSyntheticEventType';
import {registerDirectEvent} from '../EventRegistry';
import {isReplayingEvent} from '../CurrentReplayingEvent';
import {SyntheticMouseEvent, SyntheticPointerEvent} from '../SyntheticEvent';
import {
getClosestInstanceFromNode,
getNodeFromInstance,
isContainerMarkedAsRoot,
} from '../../client/ReactDOMComponentTree';
import {accumulateEnterLeaveTwoPhaseListeners} from '../DOMPluginEventSystem';
import {
HostComponent,
HostSingleton,
HostText,
} from 'react-reconciler/src/ReactWorkTags';
import {getNearestMountedFiber} from 'react-reconciler/src/ReactFiberTreeReflection';
function registerEvents() {
registerDirectEvent('onMouseEnter', ['mouseout', 'mouseover']);
registerDirectEvent('onMouseLeave', ['mouseout', 'mouseover']);
registerDirectEvent('onPointerEnter', ['pointerout', 'pointerover']);
registerDirectEvent('onPointerLeave', ['pointerout', 'pointerover']);
}
function extractEvents(
dispatchQueue: DispatchQueue,
domEventName: DOMEventName,
targetInst: null | Fiber,
nativeEvent: AnyNativeEvent,
nativeEventTarget: null | EventTarget,
eventSystemFlags: EventSystemFlags,
targetContainer: EventTarget,
) {
const isOverEvent =
domEventName === 'mouseover' || domEventName === 'pointerover';
const isOutEvent =
domEventName === 'mouseout' || domEventName === 'pointerout';
if (isOverEvent && !isReplayingEvent(nativeEvent)) {
const related =
(nativeEvent: any).relatedTarget || (nativeEvent: any).fromElement;
if (related) {
if (
getClosestInstanceFromNode(related) ||
isContainerMarkedAsRoot(related)
) {
return;
}
}
}
if (!isOutEvent && !isOverEvent) {
return;
}
let win;
if ((nativeEventTarget: any).window === nativeEventTarget) {
win = nativeEventTarget;
} else {
const doc = (nativeEventTarget: any).ownerDocument;
if (doc) {
win = doc.defaultView || doc.parentWindow;
} else {
win = window;
}
}
let from;
let to;
if (isOutEvent) {
const related = nativeEvent.relatedTarget || (nativeEvent: any).toElement;
from = targetInst;
to = related ? getClosestInstanceFromNode((related: any)) : null;
if (to !== null) {
const nearestMounted = getNearestMountedFiber(to);
const tag = to.tag;
if (
to !== nearestMounted ||
(tag !== HostComponent && tag !== HostSingleton && tag !== HostText)
) {
to = null;
}
}
} else {
from = null;
to = targetInst;
}
if (from === to) {
return;
}
let SyntheticEventCtor = SyntheticMouseEvent;
let leaveEventType = 'onMouseLeave';
let enterEventType = 'onMouseEnter';
let eventTypePrefix = 'mouse';
if (domEventName === 'pointerout' || domEventName === 'pointerover') {
SyntheticEventCtor = SyntheticPointerEvent;
leaveEventType = 'onPointerLeave';
enterEventType = 'onPointerEnter';
eventTypePrefix = 'pointer';
}
const fromNode = from == null ? win : getNodeFromInstance(from);
const toNode = to == null ? win : getNodeFromInstance(to);
const leave: KnownReactSyntheticEvent = new SyntheticEventCtor(
leaveEventType,
eventTypePrefix + 'leave',
from,
nativeEvent,
nativeEventTarget,
);
leave.target = fromNode;
leave.relatedTarget = toNode;
let enter: KnownReactSyntheticEvent | null = null;
const nativeTargetInst = getClosestInstanceFromNode((nativeEventTarget: any));
if (nativeTargetInst === targetInst) {
const enterEvent: KnownReactSyntheticEvent = new SyntheticEventCtor(
enterEventType,
eventTypePrefix + 'enter',
to,
nativeEvent,
nativeEventTarget,
);
enterEvent.target = toNode;
enterEvent.relatedTarget = fromNode;
enter = enterEvent;
}
accumulateEnterLeaveTwoPhaseListeners(dispatchQueue, leave, enter, from, to);
}
export {registerEvents, extractEvents};