main
js 237 lines 6.99 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @flow
8 */
9
10 import type {DOMEventName} from '../../events/DOMEventNames';
11 import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
12 import type {AnyNativeEvent} from '../../events/PluginModuleType';
13 import type {DispatchQueue} from '../DOMPluginEventSystem';
14 import type {EventSystemFlags} from '../EventSystemFlags';
15 import type {ReactSyntheticEvent} from '../ReactSyntheticEventType';
16
17 import {
18 SyntheticEvent,
19 SyntheticKeyboardEvent,
20 SyntheticFocusEvent,
21 SyntheticMouseEvent,
22 SyntheticDragEvent,
23 SyntheticTouchEvent,
24 SyntheticAnimationEvent,
25 SyntheticTransitionEvent,
26 SyntheticUIEvent,
27 SyntheticWheelEvent,
28 SyntheticClipboardEvent,
29 SyntheticPointerEvent,
30 SyntheticSubmitEvent,
31 SyntheticToggleEvent,
32 } from '../../events/SyntheticEvent';
33
34 import {
35 ANIMATION_END,
36 ANIMATION_ITERATION,
37 ANIMATION_START,
38 TRANSITION_END,
39 } from '../DOMEventNames';
40 import {
41 topLevelEventsToReactNames,
42 registerSimpleEvents,
43 } from '../DOMEventProperties';
44 import {
45 accumulateSinglePhaseListeners,
46 accumulateEventHandleNonManagedNodeListeners,
47 } from '../DOMPluginEventSystem';
48 import {
49 IS_EVENT_HANDLE_NON_MANAGED_NODE,
50 IS_CAPTURE_PHASE,
51 } from '../EventSystemFlags';
52
53 import getEventCharCode from '../getEventCharCode';
54
55 import {enableCreateEventHandleAPI} from 'shared/ReactFeatureFlags';
56
57 function extractEvents(
58 dispatchQueue: DispatchQueue,
59 domEventName: DOMEventName,
60 targetInst: null | Fiber,
61 nativeEvent: AnyNativeEvent,
62 nativeEventTarget: null | EventTarget,
63 eventSystemFlags: EventSystemFlags,
64 targetContainer: EventTarget,
65 ): void {
66 const reactName = topLevelEventsToReactNames.get(domEventName);
67 if (reactName === undefined) {
68 return;
69 }
70 let SyntheticEventCtor = SyntheticEvent;
71 let reactEventType: string = domEventName;
72 switch (domEventName) {
73 case 'keypress':
74 // Firefox creates a keypress event for function keys too. This removes
75 // the unwanted keypress events. Enter is however both printable and
76 // non-printable. One would expect Tab to be as well (but it isn't).
77 // TODO: Fixed in https://bugzilla.mozilla.org/show_bug.cgi?id=968056. Can
78 // probably remove.
79 if (getEventCharCode(nativeEvent as any as KeyboardEvent) === 0) {
80 return;
81 }
82 /* falls through */
83 case 'keydown':
84 case 'keyup':
85 SyntheticEventCtor = SyntheticKeyboardEvent;
86 break;
87 case 'focusin':
88 reactEventType = 'focus';
89 SyntheticEventCtor = SyntheticFocusEvent;
90 break;
91 case 'focusout':
92 reactEventType = 'blur';
93 SyntheticEventCtor = SyntheticFocusEvent;
94 break;
95 case 'beforeblur':
96 case 'afterblur':
97 SyntheticEventCtor = SyntheticFocusEvent;
98 break;
99 case 'click':
100 // Firefox creates a click event on right mouse clicks. This removes the
101 // unwanted click events.
102 // TODO: Fixed in https://phabricator.services.mozilla.com/D26793. Can
103 // probably remove.
104 if (nativeEvent.button === 2) {
105 return;
106 }
107 /* falls through */
108 case 'auxclick':
109 case 'dblclick':
110 case 'mousedown':
111 case 'mousemove':
112 case 'mouseup':
113 // TODO: Disabled elements should not respond to mouse events
114 /* falls through */
115 case 'mouseout':
116 case 'mouseover':
117 case 'contextmenu':
118 SyntheticEventCtor = SyntheticMouseEvent;
119 break;
120 case 'drag':
121 case 'dragend':
122 case 'dragenter':
123 case 'dragexit':
124 case 'dragleave':
125 case 'dragover':
126 case 'dragstart':
127 case 'drop':
128 SyntheticEventCtor = SyntheticDragEvent;
129 break;
130 case 'touchcancel':
131 case 'touchend':
132 case 'touchmove':
133 case 'touchstart':
134 SyntheticEventCtor = SyntheticTouchEvent;
135 break;
136 case ANIMATION_END:
137 case ANIMATION_ITERATION:
138 case ANIMATION_START:
139 SyntheticEventCtor = SyntheticAnimationEvent;
140 break;
141 case TRANSITION_END:
142 SyntheticEventCtor = SyntheticTransitionEvent;
143 break;
144 case 'scroll':
145 case 'scrollend':
146 SyntheticEventCtor = SyntheticUIEvent;
147 break;
148 case 'wheel':
149 SyntheticEventCtor = SyntheticWheelEvent;
150 break;
151 case 'copy':
152 case 'cut':
153 case 'paste':
154 SyntheticEventCtor = SyntheticClipboardEvent;
155 break;
156 case 'gotpointercapture':
157 case 'lostpointercapture':
158 case 'pointercancel':
159 case 'pointerdown':
160 case 'pointermove':
161 case 'pointerout':
162 case 'pointerover':
163 case 'pointerup':
164 SyntheticEventCtor = SyntheticPointerEvent;
165 break;
166 case 'submit':
167 SyntheticEventCtor = SyntheticSubmitEvent;
168 break;
169 case 'toggle':
170 case 'beforetoggle':
171 // MDN claims <details> should not receive ToggleEvent contradicting the spec: https://html.spec.whatwg.org/multipage/indices.html#event-toggle
172 SyntheticEventCtor = SyntheticToggleEvent;
173 break;
174 default:
175 // Unknown event. This is used by createEventHandle.
176 break;
177 }
178
179 const inCapturePhase = (eventSystemFlags & IS_CAPTURE_PHASE) !== 0;
180 if (
181 enableCreateEventHandleAPI &&
182 eventSystemFlags & IS_EVENT_HANDLE_NON_MANAGED_NODE
183 ) {
184 const listeners = accumulateEventHandleNonManagedNodeListeners(
185 // TODO: this cast may not make sense for events like
186 // "focus" where React listens to e.g. "focusin".
187 reactEventType as any as DOMEventName,
188 targetContainer,
189 inCapturePhase,
190 );
191 if (listeners.length > 0) {
192 // Intentionally create event lazily.
193 const event: ReactSyntheticEvent = new SyntheticEventCtor(
194 reactName,
195 reactEventType,
196 null,
197 nativeEvent,
198 nativeEventTarget,
199 );
200 dispatchQueue.push({event, listeners});
201 }
202 } else {
203 // Some events don't bubble in the browser.
204 // In the past, React has always bubbled them, but this can be surprising.
205 // We're going to try aligning closer to the browser behavior by not bubbling
206 // them in React either. We'll start by not bubbling onScroll, and then expand.
207 const accumulateTargetOnly =
208 !inCapturePhase &&
209 // TODO: ideally, we'd eventually add all events from
210 // nonDelegatedEvents list in DOMPluginEventSystem.
211 // Then we can remove this special list.
212 // This is a breaking change that can wait until React 18.
213 (domEventName === 'scroll' || domEventName === 'scrollend');
214
215 const listeners = accumulateSinglePhaseListeners(
216 targetInst,
217 reactName,
218 nativeEvent.type,
219 inCapturePhase,
220 accumulateTargetOnly,
221 nativeEvent,
222 );
223 if (listeners.length > 0) {
224 // Intentionally create event lazily.
225 const event: ReactSyntheticEvent = new SyntheticEventCtor(
226 reactName,
227 reactEventType,
228 null,
229 nativeEvent,
230 nativeEventTarget,
231 );
232 dispatchQueue.push({event, listeners});
233 }
234 }
235 }
236
237 export {registerSimpleEvents as registerEvents, extractEvents};