@samitouri / QOS-React-2 / commits / e3f4eb7272

Fork legacy-events folder into react-dom and react-native (#19228)

Dan Abramov committed Jul 1, 2020 at 16:37 UTC e3f4eb7272d4ca0ee49f27577156b57eeb07cf73
76 files changed +2422 -107
packages/legacy-events/package.json deleted
-5
@@ -1,5 +0,0 @@
1 -{
2 - "private": true,
3 - "name": "legacy-events",
4 - "version": "0.0.0"
5 -}
packages/react-dom/src/client/ReactDOM.js
+1 -1
@@ -42,7 +42,7 @@ import {canUseDOM} from 'shared/ExecutionEnvironment';
42 import {
43 eventNameDispatchConfigs,
44 injectEventPluginsByName,
45 -} from 'legacy-events/EventPluginRegistry';
45 +} from '../legacy-events/EventPluginRegistry';
46 import ReactVersion from 'shared/ReactVersion';
47 import invariant from 'shared/invariant';
48 import {
packages/react-dom/src/client/ReactDOMClientInjection.js
+2 -2
@@ -5,7 +5,7 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import {setComponentTree} from 'legacy-events/EventPluginUtils';
8 +import {setComponentTree} from '../legacy-events/EventPluginUtils';
9
10 import {
11 getFiberCurrentPropsFromNode,
@@ -29,7 +29,7 @@ import {
29 injectEventPluginOrder,
30 injectEventPluginsByName,
31 injectEventPlugins,
32 -} from 'legacy-events/EventPluginRegistry';
32 +} from '../legacy-events/EventPluginRegistry';
33 import {enableModernEventSystem} from 'shared/ReactFeatureFlags';
34
35 if (enableModernEventSystem) {
packages/react-dom/src/client/ReactDOMComponent.js
+1 -1
@@ -7,7 +7,7 @@
7 * @flow
8 */
9
10 -import {registrationNameModules} from 'legacy-events/EventPluginRegistry';
10 +import {registrationNameModules} from '../legacy-events/EventPluginRegistry';
11 import {canUseDOM} from 'shared/ExecutionEnvironment';
12 import invariant from 'shared/invariant';
13 import {
packages/react-dom/src/client/ReactDOMComponentTree.js
+1 -1
@@ -17,7 +17,7 @@ import type {
17 SuspenseInstance,
18 Props,
19 } from './ReactDOMHostConfig';
20 -import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
20 +import type {DOMTopLevelEventType} from '../legacy-events/TopLevelEventTypes';
21
22 import {
23 HostComponent,
packages/react-dom/src/client/ReactDOMEventHandle.js
+1 -1
@@ -7,7 +7,7 @@
7 * @flow
8 */
9
10 -import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
10 +import type {DOMTopLevelEventType} from '../legacy-events/TopLevelEventTypes';
11 import type {EventPriority, ReactScopeInstance} from 'shared/ReactTypes';
12 import type {
13 ReactDOMEventHandle,
packages/react-dom/src/client/ReactDOMHostConfig.js
+1 -1
@@ -7,7 +7,7 @@
7 * @flow
8 */
9
10 -import type {TopLevelType} from 'legacy-events/TopLevelEventTypes';
10 +import type {TopLevelType} from '../legacy-events/TopLevelEventTypes';
11 import type {Fiber, FiberRoot} from 'react-reconciler/src/ReactInternalTypes';
12 import type {
13 BoundingRect,
packages/react-dom/src/events/DOMEventProperties.js
+2 -2
@@ -11,11 +11,11 @@ import type {EventPriority} from 'shared/ReactTypes';
11 import type {
12 TopLevelType,
13 DOMTopLevelEventType,
14 -} from 'legacy-events/TopLevelEventTypes';
14 +} from '../legacy-events/TopLevelEventTypes';
15 import type {
16 DispatchConfig,
17 CustomDispatchConfig,
18 -} from 'legacy-events/ReactSyntheticEventType';
18 +} from '../legacy-events/ReactSyntheticEventType';
19
20 import * as DOMTopLevelEventTypes from './DOMTopLevelEventTypes';
21 import {
packages/react-dom/src/events/DOMLegacyEventPluginSystem.js
+10 -10
@@ -7,15 +7,15 @@
7 * @flow
8 */
9
10 -import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
11 -import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
10 +import type {AnyNativeEvent} from '../legacy-events/PluginModuleType';
11 +import type {DOMTopLevelEventType} from '../legacy-events/TopLevelEventTypes';
12 import type {ElementListenerMap} from '../client/ReactDOMComponentTree';
13 import type {EventSystemFlags} from './EventSystemFlags';
14 import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
15 -import type {LegacyPluginModule} from 'legacy-events/PluginModuleType';
16 -import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
17 -import type {TopLevelType} from 'legacy-events/TopLevelEventTypes';
18 -import forEachAccumulated from 'legacy-events/forEachAccumulated';
15 +import type {LegacyPluginModule} from '../legacy-events/PluginModuleType';
16 +import type {ReactSyntheticEvent} from '../legacy-events/ReactSyntheticEventType';
17 +import type {TopLevelType} from '../legacy-events/TopLevelEventTypes';
18 +import forEachAccumulated from '../legacy-events/forEachAccumulated';
19
20 import {
21 HostRoot,
@@ -23,10 +23,10 @@ import {
23 HostText,
24 } from 'react-reconciler/src/ReactWorkTags';
25 import {IS_FIRST_ANCESTOR, PLUGIN_EVENT_SYSTEM} from './EventSystemFlags';
26 -import {runEventsInBatch} from 'legacy-events/EventBatching';
27 -import {plugins} from 'legacy-events/EventPluginRegistry';
28 -import accumulateInto from 'legacy-events/accumulateInto';
29 -import {registrationNameDependencies} from 'legacy-events/EventPluginRegistry';
26 +import {runEventsInBatch} from '../legacy-events/EventBatching';
27 +import {plugins} from '../legacy-events/EventPluginRegistry';
28 +import accumulateInto from '../legacy-events/accumulateInto';
29 +import {registrationNameDependencies} from '../legacy-events/EventPluginRegistry';
30
31 import getEventTarget from './getEventTarget';
32 import {
packages/react-dom/src/events/DOMModernPluginEventSystem.js
+6 -6
@@ -7,8 +7,8 @@
7 * @flow
8 */
9
10 -import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
11 -import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
10 +import type {AnyNativeEvent} from '../legacy-events/PluginModuleType';
11 +import type {DOMTopLevelEventType} from '../legacy-events/TopLevelEventTypes';
12 import type {
13 ElementListenerMap,
14 ElementListenerMapEntry,
@@ -22,14 +22,14 @@ import type {
22 DispatchQueueItem,
23 DispatchQueueItemPhase,
24 DispatchQueueItemPhaseEntry,
25 -} from 'legacy-events/PluginModuleType';
25 +} from '../legacy-events/PluginModuleType';
26 import type {
27 ReactSyntheticEvent,
28 CustomDispatchConfig,
29 -} from 'legacy-events/ReactSyntheticEventType';
29 +} from '../legacy-events/ReactSyntheticEventType';
30
31 -import {registrationNameDependencies} from 'legacy-events/EventPluginRegistry';
32 -import {plugins} from 'legacy-events/EventPluginRegistry';
31 +import {registrationNameDependencies} from '../legacy-events/EventPluginRegistry';
32 +import {plugins} from '../legacy-events/EventPluginRegistry';
33 import {
34 PLUGIN_EVENT_SYSTEM,
35 LEGACY_FB_SUPPORT,
packages/react-dom/src/events/DOMTopLevelEventTypes.js
+2 -2
@@ -7,12 +7,12 @@
7 * @flow
8 */
9
10 -import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
10 +import type {DOMTopLevelEventType} from '../legacy-events/TopLevelEventTypes';
11
12 import {
13 unsafeCastStringToDOMTopLevelType,
14 unsafeCastDOMTopLevelTypeToString,
15 -} from 'legacy-events/TopLevelEventTypes';
15 +} from '../legacy-events/TopLevelEventTypes';
16 import getVendorPrefixedEventName from './getVendorPrefixedEventName';
17
18 /**
packages/react-dom/src/events/DeprecatedDOMEventResponderSystem.js
+2 -2
@@ -12,7 +12,7 @@ import {
12 PASSIVE_NOT_SUPPORTED,
13 RESPONDER_EVENT_SYSTEM,
14 } from './EventSystemFlags';
15 -import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
15 +import type {AnyNativeEvent} from '../legacy-events/PluginModuleType';
16 import {
17 HostComponent,
18 ScopeComponent,
@@ -25,7 +25,7 @@ import type {
25 ReactDOMResponderContext,
26 ReactDOMResponderEvent,
27 } from '../shared/ReactDOMTypes';
28 -import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
28 +import type {DOMTopLevelEventType} from '../legacy-events/TopLevelEventTypes';
29 import {
30 batchedEventUpdates,
31 discreteUpdates,
packages/react-dom/src/events/ReactDOMEventListener.js
+2 -2
@@ -7,11 +7,11 @@
7 * @flow
8 */
9
10 -import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
10 +import type {AnyNativeEvent} from '../legacy-events/PluginModuleType';
11 import type {EventPriority} from 'shared/ReactTypes';
12 import type {FiberRoot} from 'react-reconciler/src/ReactInternalTypes';
13 import type {Container, SuspenseInstance} from '../client/ReactDOMHostConfig';
14 -import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
14 +import type {DOMTopLevelEventType} from '../legacy-events/TopLevelEventTypes';
15
16 // Intentionally not named imports because Rollup would use dynamic dispatch for
17 // CommonJS interop named imports.
packages/react-dom/src/events/ReactDOMEventReplaying.js
+3 -3
@@ -7,9 +7,9 @@
7 * @flow
8 */
9
10 -import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
10 +import type {AnyNativeEvent} from '../legacy-events/PluginModuleType';
11 import type {Container, SuspenseInstance} from '../client/ReactDOMHostConfig';
12 -import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
12 +import type {DOMTopLevelEventType} from '../legacy-events/TopLevelEventTypes';
13 import type {ElementListenerMap} from '../client/ReactDOMComponentTree';
14 import type {EventSystemFlags} from './EventSystemFlags';
15 import type {FiberRoot} from 'react-reconciler/src/ReactInternalTypes';
@@ -36,7 +36,7 @@ import {
36 getClosestInstanceFromNode,
37 getEventListenerMap,
38 } from '../client/ReactDOMComponentTree';
39 -import {unsafeCastDOMTopLevelTypeToString} from 'legacy-events/TopLevelEventTypes';
39 +import {unsafeCastDOMTopLevelTypeToString} from '../legacy-events/TopLevelEventTypes';
40 import {HostRoot, SuspenseComponent} from 'react-reconciler/src/ReactWorkTags';
41
42 let attemptSynchronousHydration: (fiber: Object) => void;
packages/react-dom/src/events/SyntheticAnimationEvent.js
+1 -1
@@ -5,7 +5,7 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import SyntheticEvent from 'legacy-events/SyntheticEvent';
8 +import SyntheticEvent from '../legacy-events/SyntheticEvent';
9
10 /**
11 * @interface Event
packages/react-dom/src/events/SyntheticClipboardEvent.js
+1 -1
@@ -5,7 +5,7 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import SyntheticEvent from 'legacy-events/SyntheticEvent';
8 +import SyntheticEvent from '../legacy-events/SyntheticEvent';
9
10 /**
11 * @interface Event
packages/react-dom/src/events/SyntheticCompositionEvent.js
+1 -1
@@ -5,7 +5,7 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import SyntheticEvent from 'legacy-events/SyntheticEvent';
8 +import SyntheticEvent from '../legacy-events/SyntheticEvent';
9
10 /**
11 * @interface Event
packages/react-dom/src/events/SyntheticInputEvent.js
+1 -1
@@ -5,7 +5,7 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import SyntheticEvent from 'legacy-events/SyntheticEvent';
8 +import SyntheticEvent from '../legacy-events/SyntheticEvent';
9
10 /**
11 * @interface Event
packages/react-dom/src/events/SyntheticTransitionEvent.js
+1 -1
@@ -5,7 +5,7 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import SyntheticEvent from 'legacy-events/SyntheticEvent';
8 +import SyntheticEvent from '../legacy-events/SyntheticEvent';
9
10 /**
11 * @interface Event
packages/react-dom/src/events/SyntheticUIEvent.js
+1 -1
@@ -5,7 +5,7 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import SyntheticEvent from 'legacy-events/SyntheticEvent';
8 +import SyntheticEvent from '../legacy-events/SyntheticEvent';
9
10 const SyntheticUIEvent = SyntheticEvent.extend({
11 view: null,
packages/react-dom/src/events/getEventModifierState.js
+1 -1
@@ -12,7 +12,7 @@
12 * @see http://www.w3.org/TR/DOM-Level-3-Events/#keys-Modifiers
13 */
14
15 -import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
15 +import type {AnyNativeEvent} from '../legacy-events/PluginModuleType';
16
17 const modifierKeyToProp = {
18 Alt: 'altKey',
packages/react-dom/src/events/plugins/LegacyBeforeInputEventPlugin.js
+1 -1
@@ -5,7 +5,7 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import type {TopLevelType} from 'legacy-events/TopLevelEventTypes';
8 +import type {TopLevelType} from '../../legacy-events/TopLevelEventTypes';
9
10 import {canUseDOM} from 'shared/ExecutionEnvironment';
11
packages/react-dom/src/events/plugins/LegacyChangeEventPlugin.js
+2 -2
@@ -5,8 +5,8 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import {runEventsInBatch} from 'legacy-events/EventBatching';
9 -import SyntheticEvent from 'legacy-events/SyntheticEvent';
8 +import {runEventsInBatch} from '../../legacy-events/EventBatching';
9 +import SyntheticEvent from '../../legacy-events/SyntheticEvent';
10 import isTextInputElement from '../isTextInputElement';
11 import {canUseDOM} from 'shared/ExecutionEnvironment';
12
packages/react-dom/src/events/plugins/LegacySelectEventPlugin.js
+2 -2
@@ -6,7 +6,7 @@
6 */
7
8 import {canUseDOM} from 'shared/ExecutionEnvironment';
9 -import SyntheticEvent from 'legacy-events/SyntheticEvent';
9 +import SyntheticEvent from '../../legacy-events/SyntheticEvent';
10 import isTextInputElement from '../isTextInputElement';
11 import shallowEqual from 'shared/shallowEqual';
12
@@ -30,7 +30,7 @@ import {hasSelectionCapabilities} from '../../client/ReactInputSelection';
30 import {DOCUMENT_NODE} from '../../shared/HTMLNodeType';
31 import {accumulateTwoPhaseDispatches} from '../DOMLegacyEventPluginSystem';
32
33 -import {registrationNameDependencies} from 'legacy-events/EventPluginRegistry';
33 +import {registrationNameDependencies} from '../../legacy-events/EventPluginRegistry';
34
35 const skipSelectionChangeEvent =
36 canUseDOM && 'documentMode' in document && document.documentMode <= 11;
packages/react-dom/src/events/plugins/LegacySimpleEventPlugin.js
+4 -4
@@ -10,12 +10,12 @@
10 import type {
11 TopLevelType,
12 DOMTopLevelEventType,
13 -} from 'legacy-events/TopLevelEventTypes';
14 -import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
13 +} from '../../legacy-events/TopLevelEventTypes';
14 +import type {ReactSyntheticEvent} from '../../legacy-events/ReactSyntheticEventType';
15 import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
16 -import type {LegacyPluginModule} from 'legacy-events/PluginModuleType';
16 +import type {LegacyPluginModule} from '../../legacy-events/PluginModuleType';
17
18 -import SyntheticEvent from 'legacy-events/SyntheticEvent';
18 +import SyntheticEvent from '../../legacy-events/SyntheticEvent';
19
20 import * as DOMTopLevelEventTypes from '../DOMTopLevelEventTypes';
21 import {
packages/react-dom/src/events/plugins/ModernBeforeInputEventPlugin.js
+1 -1
@@ -5,7 +5,7 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import type {TopLevelType} from 'legacy-events/TopLevelEventTypes';
8 +import type {TopLevelType} from '../../legacy-events/TopLevelEventTypes';
9
10 import {canUseDOM} from 'shared/ExecutionEnvironment';
11
packages/react-dom/src/events/plugins/ModernChangeEventPlugin.js
+1 -1
@@ -5,7 +5,7 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import SyntheticEvent from 'legacy-events/SyntheticEvent';
8 +import SyntheticEvent from '../../legacy-events/SyntheticEvent';
9 import isTextInputElement from '../isTextInputElement';
10 import {canUseDOM} from 'shared/ExecutionEnvironment';
11
packages/react-dom/src/events/plugins/ModernSelectEventPlugin.js
+1 -1
@@ -6,7 +6,7 @@
6 */
7
8 import {canUseDOM} from 'shared/ExecutionEnvironment';
9 -import SyntheticEvent from 'legacy-events/SyntheticEvent';
9 +import SyntheticEvent from '../../legacy-events/SyntheticEvent';
10 import isTextInputElement from '../isTextInputElement';
11 import shallowEqual from 'shared/shallowEqual';
12
packages/react-dom/src/events/plugins/ModernSimpleEventPlugin.js
+3 -3
@@ -10,15 +10,15 @@
10 import type {
11 TopLevelType,
12 DOMTopLevelEventType,
13 -} from 'legacy-events/TopLevelEventTypes';
13 +} from '../../legacy-events/TopLevelEventTypes';
14 import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
15 import type {
16 ModernPluginModule,
17 DispatchQueue,
18 -} from 'legacy-events/PluginModuleType';
18 +} from '../../legacy-events/PluginModuleType';
19 import type {EventSystemFlags} from '../EventSystemFlags';
20
21 -import SyntheticEvent from 'legacy-events/SyntheticEvent';
21 +import SyntheticEvent from '../../legacy-events/SyntheticEvent';
22
23 import * as DOMTopLevelEventTypes from '../DOMTopLevelEventTypes';
24 import {
packages/react-dom/src/legacy-events/EventBatching.js renamed
packages/react-dom/src/legacy-events/EventPluginRegistry.js renamed
packages/react-dom/src/legacy-events/EventPluginUtils.js renamed
packages/react-dom/src/legacy-events/PluginModuleType.js renamed
packages/react-dom/src/legacy-events/ReactGenericBatching.js renamed
packages/react-dom/src/legacy-events/ReactSyntheticEventType.js renamed
packages/react-dom/src/legacy-events/ResponderEventPlugin.js renamed
packages/react-dom/src/legacy-events/ResponderSyntheticEvent.js renamed
packages/react-dom/src/legacy-events/ResponderTopLevelEventTypes.js renamed
packages/react-dom/src/legacy-events/ResponderTouchHistoryStore.js renamed
packages/react-dom/src/legacy-events/SyntheticEvent.js renamed
packages/react-dom/src/legacy-events/TopLevelEventTypes.js renamed
packages/react-dom/src/legacy-events/accumulate.js renamed
packages/react-dom/src/legacy-events/accumulateInto.js renamed
packages/react-dom/src/legacy-events/forEachAccumulated.js renamed
packages/react-dom/src/shared/ReactDOMTypes.js
+1 -1
@@ -14,7 +14,7 @@ import type {
14 EventPriority,
15 ReactScopeInstance,
16 } from 'shared/ReactTypes';
17 -import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
17 +import type {DOMTopLevelEventType} from '../legacy-events/TopLevelEventTypes';
18
19 type AnyNativeEvent = Event | KeyboardEvent | MouseEvent | Touch;
20
packages/react-dom/src/shared/ReactDOMUnknownPropertyHook.js
+1 -1
@@ -8,7 +8,7 @@
8 import {
9 registrationNameModules,
10 possibleRegistrationNames,
11 -} from 'legacy-events/EventPluginRegistry';
11 +} from '../legacy-events/EventPluginRegistry';
12
13 import {
14 ATTRIBUTE_NAME_CHAR,
packages/react-dom/src/test-utils/ReactTestUtils.js
+3 -3
@@ -15,13 +15,13 @@ import {
15 HostComponent,
16 HostText,
17 } from 'react-reconciler/src/ReactWorkTags';
18 -import SyntheticEvent from 'legacy-events/SyntheticEvent';
18 +import SyntheticEvent from '../legacy-events/SyntheticEvent';
19 import invariant from 'shared/invariant';
20 import {ELEMENT_NODE} from '../shared/HTMLNodeType';
21 import * as DOMTopLevelEventTypes from '../events/DOMTopLevelEventTypes';
22 import act from './ReactTestUtilsAct';
23 -import forEachAccumulated from 'legacy-events/forEachAccumulated';
24 -import accumulateInto from 'legacy-events/accumulateInto';
23 +import forEachAccumulated from '../legacy-events/forEachAccumulated';
24 +import accumulateInto from '../legacy-events/accumulateInto';
25 import {enableModernEventSystem} from 'shared/ReactFeatureFlags';
26 import {
27 rethrowCaughtError,
packages/react-native-renderer/src/ReactFabric.js
+1 -1
@@ -27,7 +27,7 @@ import {
27 } from 'react-reconciler/src/ReactFiberReconciler';
28
29 import {createPortal as createPortalImpl} from 'react-reconciler/src/ReactPortal';
30 -import {setBatchingImplementation} from 'legacy-events/ReactGenericBatching';
30 +import {setBatchingImplementation} from './legacy-events/ReactGenericBatching';
31 import ReactVersion from 'shared/ReactVersion';
32
33 // Module provided by RN:
packages/react-native-renderer/src/ReactFabricEventEmitter.js
+9 -9
@@ -7,19 +7,19 @@
7 * @flow
8 */
9
10 -import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
10 +import type {AnyNativeEvent} from './legacy-events/PluginModuleType';
11 import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
12 -import type {LegacyPluginModule} from 'legacy-events/PluginModuleType';
13 -import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
14 -import type {TopLevelType} from 'legacy-events/TopLevelEventTypes';
12 +import type {LegacyPluginModule} from './legacy-events/PluginModuleType';
13 +import type {ReactSyntheticEvent} from './legacy-events/ReactSyntheticEventType';
14 +import type {TopLevelType} from './legacy-events/TopLevelEventTypes';
15
16 -import {registrationNameModules} from 'legacy-events/EventPluginRegistry';
17 -import {batchedUpdates} from 'legacy-events/ReactGenericBatching';
18 -import accumulateInto from 'legacy-events/accumulateInto';
16 +import {registrationNameModules} from './legacy-events/EventPluginRegistry';
17 +import {batchedUpdates} from './legacy-events/ReactGenericBatching';
18 +import accumulateInto from './legacy-events/accumulateInto';
19
20 -import {plugins} from 'legacy-events/EventPluginRegistry';
20 +import {plugins} from './legacy-events/EventPluginRegistry';
21 import getListener from './ReactNativeGetListener';
22 -import {runEventsInBatch} from 'legacy-events/EventBatching';
22 +import {runEventsInBatch} from './legacy-events/EventBatching';
23
24 export {getListener, registrationNameModules as registrationNames};
25
packages/react-native-renderer/src/ReactFabricInjection.js
+2 -2
@@ -14,9 +14,9 @@ import {
14 getInstanceFromNode,
15 getNodeFromInstance,
16 } from './ReactFabricComponentTree';
17 -import {setComponentTree} from 'legacy-events/EventPluginUtils';
17 +import {setComponentTree} from './legacy-events/EventPluginUtils';
18 import ReactFabricGlobalResponderHandler from './ReactFabricGlobalResponderHandler';
19 -import ResponderEventPlugin from 'legacy-events/ResponderEventPlugin';
19 +import ResponderEventPlugin from './legacy-events/ResponderEventPlugin';
20
21 setComponentTree(
22 getFiberCurrentPropsFromNode,
packages/react-native-renderer/src/ReactNativeBridgeEventPlugin.js
+5 -5
@@ -7,16 +7,16 @@
7 * @flow
8 */
9
10 -import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
11 -import type {TopLevelType} from 'legacy-events/TopLevelEventTypes';
12 -import SyntheticEvent from 'legacy-events/SyntheticEvent';
10 +import type {AnyNativeEvent} from './legacy-events/PluginModuleType';
11 +import type {TopLevelType} from './legacy-events/TopLevelEventTypes';
12 +import SyntheticEvent from './legacy-events/SyntheticEvent';
13 import invariant from 'shared/invariant';
14
15 // Module provided by RN:
16 import {ReactNativeViewConfigRegistry} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
17 -import accumulateInto from 'legacy-events/accumulateInto';
17 +import accumulateInto from './legacy-events/accumulateInto';
18 import getListener from './ReactNativeGetListener';
19 -import forEachAccumulated from 'legacy-events/forEachAccumulated';
19 +import forEachAccumulated from './legacy-events/forEachAccumulated';
20 import {HostComponent} from 'react-reconciler/src/ReactWorkTags';
21
22 const {
packages/react-native-renderer/src/ReactNativeEventEmitter.js
+10 -10
@@ -7,18 +7,18 @@
7 * @flow
8 */
9
10 -import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
10 +import type {AnyNativeEvent} from './legacy-events/PluginModuleType';
11 import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
12 -import type {LegacyPluginModule} from 'legacy-events/PluginModuleType';
13 -import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
14 -import type {TopLevelType} from 'legacy-events/TopLevelEventTypes';
15 -
16 -import {registrationNameModules} from 'legacy-events/EventPluginRegistry';
17 -import {batchedUpdates} from 'legacy-events/ReactGenericBatching';
18 -import {runEventsInBatch} from 'legacy-events/EventBatching';
19 -import {plugins} from 'legacy-events/EventPluginRegistry';
12 +import type {LegacyPluginModule} from './legacy-events/PluginModuleType';
13 +import type {ReactSyntheticEvent} from './legacy-events/ReactSyntheticEventType';
14 +import type {TopLevelType} from './legacy-events/TopLevelEventTypes';
15 +
16 +import {registrationNameModules} from './legacy-events/EventPluginRegistry';
17 +import {batchedUpdates} from './legacy-events/ReactGenericBatching';
18 +import {runEventsInBatch} from './legacy-events/EventBatching';
19 +import {plugins} from './legacy-events/EventPluginRegistry';
20 import getListener from './ReactNativeGetListener';
21 -import accumulateInto from 'legacy-events/accumulateInto';
21 +import accumulateInto from './legacy-events/accumulateInto';
22
23 import {getInstanceFromNode} from './ReactNativeComponentTree';
24
packages/react-native-renderer/src/ReactNativeGetListener.js
+1 -1
@@ -9,7 +9,7 @@
9 import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
10
11 import invariant from 'shared/invariant';
12 -import {getFiberCurrentPropsFromNode} from 'legacy-events/EventPluginUtils';
12 +import {getFiberCurrentPropsFromNode} from './legacy-events/EventPluginUtils';
13
14 export default function getListener(
15 inst: Fiber,
packages/react-native-renderer/src/ReactNativeInjection.js
+2 -2
@@ -14,10 +14,10 @@ import {
14 getInstanceFromNode,
15 getNodeFromInstance,
16 } from './ReactNativeComponentTree';
17 -import {setComponentTree} from 'legacy-events/EventPluginUtils';
17 +import {setComponentTree} from './legacy-events/EventPluginUtils';
18 import {receiveEvent, receiveTouches} from './ReactNativeEventEmitter';
19 import ReactNativeGlobalResponderHandler from './ReactNativeGlobalResponderHandler';
20 -import ResponderEventPlugin from 'legacy-events/ResponderEventPlugin';
20 +import ResponderEventPlugin from './legacy-events/ResponderEventPlugin';
21
22 // Module provided by RN:
23 import {RCTEventEmitter} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
packages/react-native-renderer/src/ReactNativeInjectionShared.js
+2 -2
@@ -16,11 +16,11 @@
16 // Module provided by RN:
17 import 'react-native/Libraries/ReactPrivate/ReactNativePrivateInitializeCore';
18
19 -import ResponderEventPlugin from 'legacy-events/ResponderEventPlugin';
19 +import ResponderEventPlugin from './legacy-events/ResponderEventPlugin';
20 import {
21 injectEventPluginOrder,
22 injectEventPluginsByName,
23 -} from 'legacy-events/EventPluginRegistry';
23 +} from './legacy-events/EventPluginRegistry';
24
25 import ReactNativeBridgeEventPlugin from './ReactNativeBridgeEventPlugin';
26 import ReactNativeEventPluginOrder from './ReactNativeEventPluginOrder';
packages/react-native-renderer/src/ReactNativeRenderer.js
+1 -1
@@ -30,7 +30,7 @@ import {createPortal as createPortalImpl} from 'react-reconciler/src/ReactPortal
30 import {
31 setBatchingImplementation,
32 batchedUpdates,
33 -} from 'legacy-events/ReactGenericBatching';
33 +} from './legacy-events/ReactGenericBatching';
34 import ReactVersion from 'shared/ReactVersion';
35 // Module provided by RN:
36 import {UIManager} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
packages/react-native-renderer/src/__tests__/EventPluginRegistry-test.internal.js
+1 -1
@@ -19,7 +19,7 @@ describe('EventPluginRegistry', () => {
19 // The public API surface of this is covered by other tests so
20 // if `EventPluginRegistry` is ever deleted, these tests should be
21 // safe to remove too.
22 - EventPluginRegistry = require('legacy-events/EventPluginRegistry');
22 + EventPluginRegistry = require('react-native-renderer/src/legacy-events/EventPluginRegistry');
23
24 createPlugin = function(properties) {
25 return Object.assign({extractEvents: function() {}}, properties);
packages/react-native-renderer/src/__tests__/ReactNativeEvents-test.internal.js
+2 -1
@@ -68,7 +68,8 @@ beforeEach(() => {
68 .RCTEventEmitter;
69 React = require('react');
70 ReactNative = require('react-native-renderer');
71 - ResponderEventPlugin = require('legacy-events/ResponderEventPlugin').default;
71 + ResponderEventPlugin = require('react-native-renderer/src/legacy-events/ResponderEventPlugin')
72 + .default;
73 UIManager = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface')
74 .UIManager;
75 createReactNativeComponentClass = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface')
packages/react-native-renderer/src/__tests__/ResponderEventPlugin-test.internal.js
+5 -4
@@ -407,9 +407,9 @@ describe('ResponderEventPlugin', () => {
407 beforeEach(() => {
408 jest.resetModules();
409
410 - EventBatching = require('legacy-events/EventBatching');
411 - EventPluginUtils = require('legacy-events/EventPluginUtils');
412 - ResponderEventPlugin = require('legacy-events/ResponderEventPlugin')
410 + EventBatching = require('react-native-renderer/src/legacy-events/EventBatching');
411 + EventPluginUtils = require('react-native-renderer/src/legacy-events/EventPluginUtils');
412 + ResponderEventPlugin = require('react-native-renderer/src/legacy-events/ResponderEventPlugin')
413 .default;
414
415 deleteAllListeners(GRANDPARENT_INST);
@@ -1380,8 +1380,9 @@ describe('ResponderEventPlugin', () => {
1380 // ResponderEventPlugin uses `getLowestCommonAncestor`
1381 const React = require('react');
1382 const ReactTestUtils = require('react-dom/test-utils');
1383 - const getLowestCommonAncestor = require('legacy-events/ResponderEventPlugin')
1383 + const getLowestCommonAncestor = require('react-native-renderer/src/legacy-events/ResponderEventPlugin')
1384 .getLowestCommonAncestor;
1385 + // This works by accident and will likely break in the future.
1386 const ReactDOMComponentTree = require('react-dom/src/client/ReactDOMComponentTree');
1387
1388 class ChildComponent extends React.Component {
packages/react-native-renderer/src/legacy-events/EventBatching.js new
+66
@@ -0,0 +1,66 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its 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 + * @flow
7 + */
8 +
9 +import invariant from 'shared/invariant';
10 +import {rethrowCaughtError} from 'shared/ReactErrorUtils';
11 +
12 +import type {ReactSyntheticEvent} from './ReactSyntheticEventType';
13 +import accumulateInto from './accumulateInto';
14 +import forEachAccumulated from './forEachAccumulated';
15 +import {executeDispatchesInOrder} from './EventPluginUtils';
16 +
17 +/**
18 + * Internal queue of events that have accumulated their dispatches and are
19 + * waiting to have their dispatches executed.
20 + */
21 +let eventQueue: ?(Array<ReactSyntheticEvent> | ReactSyntheticEvent) = null;
22 +
23 +/**
24 + * Dispatches an event and releases it back into the pool, unless persistent.
25 + *
26 + * @param {?object} event Synthetic event to be dispatched.
27 + * @private
28 + */
29 +const executeDispatchesAndRelease = function(event: ReactSyntheticEvent) {
30 + if (event) {
31 + executeDispatchesInOrder(event);
32 +
33 + if (!event.isPersistent()) {
34 + event.constructor.release(event);
35 + }
36 + }
37 +};
38 +const executeDispatchesAndReleaseTopLevel = function(e) {
39 + return executeDispatchesAndRelease(e);
40 +};
41 +
42 +export function runEventsInBatch(
43 + events: Array<ReactSyntheticEvent> | ReactSyntheticEvent | null,
44 +) {
45 + if (events !== null) {
46 + eventQueue = accumulateInto(eventQueue, events);
47 + }
48 +
49 + // Set `eventQueue` to null before processing it so that we can tell if more
50 + // events get enqueued while processing.
51 + const processingEventQueue = eventQueue;
52 + eventQueue = null;
53 +
54 + if (!processingEventQueue) {
55 + return;
56 + }
57 +
58 + forEachAccumulated(processingEventQueue, executeDispatchesAndReleaseTopLevel);
59 + invariant(
60 + !eventQueue,
61 + 'processEventQueue(): Additional events were enqueued while processing ' +
62 + 'an event queue. Support for this has not yet been implemented.',
63 + );
64 + // This would be a good time to rethrow if any of the event handlers threw.
65 + rethrowCaughtError();
66 +}
packages/react-native-renderer/src/legacy-events/EventPluginRegistry.js new
+270
@@ -0,0 +1,270 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its 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 {DispatchConfig} from './ReactSyntheticEventType';
11 +import type {
12 + AnyNativeEvent,
13 + PluginName,
14 + LegacyPluginModule,
15 + ModernPluginModule,
16 +} from './PluginModuleType';
17 +
18 +import invariant from 'shared/invariant';
19 +
20 +type NamesToPlugins = {
21 + [key: PluginName]:
22 + | LegacyPluginModule<AnyNativeEvent>
23 + | ModernPluginModule<AnyNativeEvent>,
24 + ...,
25 +};
26 +type EventPluginOrder = null | Array<PluginName>;
27 +
28 +/**
29 + * Injectable ordering of event plugins.
30 + */
31 +let eventPluginOrder: EventPluginOrder = null;
32 +
33 +/**
34 + * Injectable mapping from names to event plugin modules.
35 + */
36 +const namesToPlugins: NamesToPlugins = {};
37 +
38 +/**
39 + * Recomputes the plugin list using the injected plugins and plugin ordering.
40 + *
41 + * @private
42 + */
43 +function recomputePluginOrdering(): void {
44 + if (!eventPluginOrder) {
45 + // Wait until an `eventPluginOrder` is injected.
46 + return;
47 + }
48 + for (const pluginName in namesToPlugins) {
49 + const pluginModule = namesToPlugins[pluginName];
50 + const pluginIndex = eventPluginOrder.indexOf(pluginName);
51 + invariant(
52 + pluginIndex > -1,
53 + 'EventPluginRegistry: Cannot inject event plugins that do not exist in ' +
54 + 'the plugin ordering, `%s`.',
55 + pluginName,
56 + );
57 + if (plugins[pluginIndex]) {
58 + continue;
59 + }
60 + invariant(
61 + pluginModule.extractEvents,
62 + 'EventPluginRegistry: Event plugins must implement an `extractEvents` ' +
63 + 'method, but `%s` does not.',
64 + pluginName,
65 + );
66 + plugins[pluginIndex] = pluginModule;
67 + const publishedEvents = pluginModule.eventTypes;
68 + for (const eventName in publishedEvents) {
69 + invariant(
70 + publishEventForPlugin(
71 + publishedEvents[eventName],
72 + pluginModule,
73 + eventName,
74 + ),
75 + 'EventPluginRegistry: Failed to publish event `%s` for plugin `%s`.',
76 + eventName,
77 + pluginName,
78 + );
79 + }
80 + }
81 +}
82 +
83 +/**
84 + * Publishes an event so that it can be dispatched by the supplied plugin.
85 + *
86 + * @param {object} dispatchConfig Dispatch configuration for the event.
87 + * @param {object} PluginModule Plugin publishing the event.
88 + * @return {boolean} True if the event was successfully published.
89 + * @private
90 + */
91 +function publishEventForPlugin(
92 + dispatchConfig: DispatchConfig,
93 + pluginModule:
94 + | LegacyPluginModule<AnyNativeEvent>
95 + | ModernPluginModule<AnyNativeEvent>,
96 + eventName: string,
97 +): boolean {
98 + invariant(
99 + !eventNameDispatchConfigs.hasOwnProperty(eventName),
100 + 'EventPluginRegistry: More than one plugin attempted to publish the same ' +
101 + 'event name, `%s`.',
102 + eventName,
103 + );
104 + eventNameDispatchConfigs[eventName] = dispatchConfig;
105 +
106 + const phasedRegistrationNames = dispatchConfig.phasedRegistrationNames;
107 + if (phasedRegistrationNames) {
108 + for (const phaseName in phasedRegistrationNames) {
109 + if (phasedRegistrationNames.hasOwnProperty(phaseName)) {
110 + const phasedRegistrationName = phasedRegistrationNames[phaseName];
111 + publishRegistrationName(
112 + phasedRegistrationName,
113 + pluginModule,
114 + eventName,
115 + );
116 + }
117 + }
118 + return true;
119 + } else if (dispatchConfig.registrationName) {
120 + publishRegistrationName(
121 + dispatchConfig.registrationName,
122 + pluginModule,
123 + eventName,
124 + );
125 + return true;
126 + }
127 + return false;
128 +}
129 +
130 +/**
131 + * Publishes a registration name that is used to identify dispatched events.
132 + *
133 + * @param {string} registrationName Registration name to add.
134 + * @param {object} PluginModule Plugin publishing the event.
135 + * @private
136 + */
137 +function publishRegistrationName(
138 + registrationName: string,
139 + pluginModule:
140 + | LegacyPluginModule<AnyNativeEvent>
141 + | ModernPluginModule<AnyNativeEvent>,
142 + eventName: string,
143 +): void {
144 + invariant(
145 + !registrationNameModules[registrationName],
146 + 'EventPluginRegistry: More than one plugin attempted to publish the same ' +
147 + 'registration name, `%s`.',
148 + registrationName,
149 + );
150 + registrationNameModules[registrationName] = pluginModule;
151 + registrationNameDependencies[registrationName] =
152 + pluginModule.eventTypes[eventName].dependencies;
153 +
154 + if (__DEV__) {
155 + const lowerCasedName = registrationName.toLowerCase();
156 + possibleRegistrationNames[lowerCasedName] = registrationName;
157 +
158 + if (registrationName === 'onDoubleClick') {
159 + possibleRegistrationNames.ondblclick = registrationName;
160 + }
161 + }
162 +}
163 +
164 +/**
165 + * Registers plugins so that they can extract and dispatch events.
166 + */
167 +
168 +/**
169 + * Ordered list of injected plugins.
170 + */
171 +export const plugins = [];
172 +
173 +/**
174 + * Mapping from event name to dispatch config
175 + */
176 +export const eventNameDispatchConfigs = {};
177 +
178 +/**
179 + * Mapping from registration name to plugin module
180 + */
181 +export const registrationNameModules = {};
182 +
183 +/**
184 + * Mapping from registration name to event name
185 + */
186 +export const registrationNameDependencies = {};
187 +
188 +/**
189 + * Mapping from lowercase registration names to the properly cased version,
190 + * used to warn in the case of missing event handlers. Available
191 + * only in __DEV__.
192 + * @type {Object}
193 + */
194 +export const possibleRegistrationNames = __DEV__ ? {} : (null: any);
195 +// Trust the developer to only use possibleRegistrationNames in __DEV__
196 +
197 +/**
198 + * Injects an ordering of plugins (by plugin name). This allows the ordering
199 + * to be decoupled from injection of the actual plugins so that ordering is
200 + * always deterministic regardless of packaging, on-the-fly injection, etc.
201 + *
202 + * @param {array} InjectedEventPluginOrder
203 + * @internal
204 + */
205 +export function injectEventPluginOrder(
206 + injectedEventPluginOrder: EventPluginOrder,
207 +): void {
208 + invariant(
209 + !eventPluginOrder,
210 + 'EventPluginRegistry: Cannot inject event plugin ordering more than ' +
211 + 'once. You are likely trying to load more than one copy of React.',
212 + );
213 + // Clone the ordering so it cannot be dynamically mutated.
214 + eventPluginOrder = Array.prototype.slice.call(injectedEventPluginOrder);
215 + recomputePluginOrdering();
216 +}
217 +
218 +/**
219 + * Injects plugins to be used by plugin event system. The plugin names must be
220 + * in the ordering injected by `injectEventPluginOrder`.
221 + *
222 + * Plugins can be injected as part of page initialization or on-the-fly.
223 + *
224 + * @param {object} injectedNamesToPlugins Map from names to plugin modules.
225 + * @internal
226 + */
227 +export function injectEventPluginsByName(
228 + injectedNamesToPlugins: NamesToPlugins,
229 +): void {
230 + let isOrderingDirty = false;
231 + for (const pluginName in injectedNamesToPlugins) {
232 + if (!injectedNamesToPlugins.hasOwnProperty(pluginName)) {
233 + continue;
234 + }
235 + const pluginModule = injectedNamesToPlugins[pluginName];
236 + if (
237 + !namesToPlugins.hasOwnProperty(pluginName) ||
238 + namesToPlugins[pluginName] !== pluginModule
239 + ) {
240 + invariant(
241 + !namesToPlugins[pluginName],
242 + 'EventPluginRegistry: Cannot inject two different event plugins ' +
243 + 'using the same name, `%s`.',
244 + pluginName,
245 + );
246 + namesToPlugins[pluginName] = pluginModule;
247 + isOrderingDirty = true;
248 + }
249 + }
250 + if (isOrderingDirty) {
251 + recomputePluginOrdering();
252 + }
253 +}
254 +
255 +export function injectEventPlugins(
256 + eventPlugins: [ModernPluginModule<AnyNativeEvent>],
257 +): void {
258 + for (let i = 0; i < eventPlugins.length; i++) {
259 + const pluginModule = eventPlugins[i];
260 + plugins.push(pluginModule);
261 + const publishedEvents = pluginModule.eventTypes;
262 + for (const eventName in publishedEvents) {
263 + publishEventForPlugin(
264 + publishedEvents[eventName],
265 + pluginModule,
266 + eventName,
267 + );
268 + }
269 + }
270 +}
packages/react-native-renderer/src/legacy-events/EventPluginUtils.js new
+172
@@ -0,0 +1,172 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its 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 +
8 +import {invokeGuardedCallbackAndCatchFirstError} from 'shared/ReactErrorUtils';
9 +import invariant from 'shared/invariant';
10 +
11 +export let getFiberCurrentPropsFromNode = null;
12 +export let getInstanceFromNode = null;
13 +export let getNodeFromInstance = null;
14 +
15 +export function setComponentTree(
16 + getFiberCurrentPropsFromNodeImpl,
17 + getInstanceFromNodeImpl,
18 + getNodeFromInstanceImpl,
19 +) {
20 + getFiberCurrentPropsFromNode = getFiberCurrentPropsFromNodeImpl;
21 + getInstanceFromNode = getInstanceFromNodeImpl;
22 + getNodeFromInstance = getNodeFromInstanceImpl;
23 + if (__DEV__) {
24 + if (!getNodeFromInstance || !getInstanceFromNode) {
25 + console.error(
26 + 'EventPluginUtils.setComponentTree(...): Injected ' +
27 + 'module is missing getNodeFromInstance or getInstanceFromNode.',
28 + );
29 + }
30 + }
31 +}
32 +
33 +let validateEventDispatches;
34 +if (__DEV__) {
35 + validateEventDispatches = function(event) {
36 + const dispatchListeners = event._dispatchListeners;
37 + const dispatchInstances = event._dispatchInstances;
38 +
39 + const listenersIsArr = Array.isArray(dispatchListeners);
40 + const listenersLen = listenersIsArr
41 + ? dispatchListeners.length
42 + : dispatchListeners
43 + ? 1
44 + : 0;
45 +
46 + const instancesIsArr = Array.isArray(dispatchInstances);
47 + const instancesLen = instancesIsArr
48 + ? dispatchInstances.length
49 + : dispatchInstances
50 + ? 1
51 + : 0;
52 +
53 + if (instancesIsArr !== listenersIsArr || instancesLen !== listenersLen) {
54 + console.error('EventPluginUtils: Invalid `event`.');
55 + }
56 + };
57 +}
58 +
59 +/**
60 + * Dispatch the event to the listener.
61 + * @param {SyntheticEvent} event SyntheticEvent to handle
62 + * @param {function} listener Application-level callback
63 + * @param {*} inst Internal component instance
64 + */
65 +export function executeDispatch(event, listener, inst) {
66 + const type = event.type || 'unknown-event';
67 + event.currentTarget = getNodeFromInstance(inst);
68 + invokeGuardedCallbackAndCatchFirstError(type, listener, undefined, event);
69 + event.currentTarget = null;
70 +}
71 +
72 +/**
73 + * Standard/simple iteration through an event's collected dispatches.
74 + */
75 +export function executeDispatchesInOrder(event) {
76 + const dispatchListeners = event._dispatchListeners;
77 + const dispatchInstances = event._dispatchInstances;
78 + if (__DEV__) {
79 + validateEventDispatches(event);
80 + }
81 + if (Array.isArray(dispatchListeners)) {
82 + for (let i = 0; i < dispatchListeners.length; i++) {
83 + if (event.isPropagationStopped()) {
84 + break;
85 + }
86 + // Listeners and Instances are two parallel arrays that are always in sync.
87 + executeDispatch(event, dispatchListeners[i], dispatchInstances[i]);
88 + }
89 + } else if (dispatchListeners) {
90 + executeDispatch(event, dispatchListeners, dispatchInstances);
91 + }
92 + event._dispatchListeners = null;
93 + event._dispatchInstances = null;
94 +}
95 +
96 +/**
97 + * Standard/simple iteration through an event's collected dispatches, but stops
98 + * at the first dispatch execution returning true, and returns that id.
99 + *
100 + * @return {?string} id of the first dispatch execution who's listener returns
101 + * true, or null if no listener returned true.
102 + */
103 +function executeDispatchesInOrderStopAtTrueImpl(event) {
104 + const dispatchListeners = event._dispatchListeners;
105 + const dispatchInstances = event._dispatchInstances;
106 + if (__DEV__) {
107 + validateEventDispatches(event);
108 + }
109 + if (Array.isArray(dispatchListeners)) {
110 + for (let i = 0; i < dispatchListeners.length; i++) {
111 + if (event.isPropagationStopped()) {
112 + break;
113 + }
114 + // Listeners and Instances are two parallel arrays that are always in sync.
115 + if (dispatchListeners[i](event, dispatchInstances[i])) {
116 + return dispatchInstances[i];
117 + }
118 + }
119 + } else if (dispatchListeners) {
120 + if (dispatchListeners(event, dispatchInstances)) {
121 + return dispatchInstances;
122 + }
123 + }
124 + return null;
125 +}
126 +
127 +/**
128 + * @see executeDispatchesInOrderStopAtTrueImpl
129 + */
130 +export function executeDispatchesInOrderStopAtTrue(event) {
131 + const ret = executeDispatchesInOrderStopAtTrueImpl(event);
132 + event._dispatchInstances = null;
133 + event._dispatchListeners = null;
134 + return ret;
135 +}
136 +
137 +/**
138 + * Execution of a "direct" dispatch - there must be at most one dispatch
139 + * accumulated on the event or it is considered an error. It doesn't really make
140 + * sense for an event with multiple dispatches (bubbled) to keep track of the
141 + * return values at each dispatch execution, but it does tend to make sense when
142 + * dealing with "direct" dispatches.
143 + *
144 + * @return {*} The return value of executing the single dispatch.
145 + */
146 +export function executeDirectDispatch(event) {
147 + if (__DEV__) {
148 + validateEventDispatches(event);
149 + }
150 + const dispatchListener = event._dispatchListeners;
151 + const dispatchInstance = event._dispatchInstances;
152 + invariant(
153 + !Array.isArray(dispatchListener),
154 + 'executeDirectDispatch(...): Invalid `event`.',
155 + );
156 + event.currentTarget = dispatchListener
157 + ? getNodeFromInstance(dispatchInstance)
158 + : null;
159 + const res = dispatchListener ? dispatchListener(event) : null;
160 + event.currentTarget = null;
161 + event._dispatchListeners = null;
162 + event._dispatchInstances = null;
163 + return res;
164 +}
165 +
166 +/**
167 + * @param {SyntheticEvent} event
168 + * @return {boolean} True iff number of dispatches accumulated is greater than 0.
169 + */
170 +export function hasDispatches(event) {
171 + return !!event._dispatchListeners;
172 +}
packages/react-native-renderer/src/legacy-events/PluginModuleType.js new
+65
@@ -0,0 +1,65 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its 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 {Fiber} from 'react-reconciler/src/ReactInternalTypes';
11 +import type {
12 + DispatchConfig,
13 + ReactSyntheticEvent,
14 +} from './ReactSyntheticEventType';
15 +import type {TopLevelType} from './TopLevelEventTypes';
16 +
17 +export type EventTypes = {[key: string]: DispatchConfig, ...};
18 +
19 +export type AnyNativeEvent = Event | KeyboardEvent | MouseEvent | TouchEvent;
20 +
21 +export type PluginName = string;
22 +
23 +export type EventSystemFlags = number;
24 +
25 +export type LegacyPluginModule<NativeEvent> = {
26 + eventTypes: EventTypes,
27 + extractEvents: (
28 + topLevelType: TopLevelType,
29 + targetInst: null | Fiber,
30 + nativeTarget: NativeEvent,
31 + nativeEventTarget: null | EventTarget,
32 + eventSystemFlags?: number,
33 + container?: null | EventTarget,
34 + ) => ?ReactSyntheticEvent,
35 + tapMoveThreshold?: number,
36 +};
37 +
38 +export type DispatchQueueItemPhaseEntry = {|
39 + instance: null | Fiber,
40 + listener: Function,
41 + currentTarget: EventTarget,
42 +|};
43 +
44 +export type DispatchQueueItemPhase = Array<DispatchQueueItemPhaseEntry>;
45 +
46 +export type DispatchQueueItem = {|
47 + event: ReactSyntheticEvent,
48 + capture: DispatchQueueItemPhase,
49 + bubble: DispatchQueueItemPhase,
50 +|};
51 +
52 +export type DispatchQueue = Array<DispatchQueueItem>;
53 +
54 +export type ModernPluginModule<NativeEvent> = {
55 + eventTypes: EventTypes,
56 + extractEvents: (
57 + dispatchQueue: DispatchQueue,
58 + topLevelType: TopLevelType,
59 + targetInst: null | Fiber,
60 + nativeTarget: NativeEvent,
61 + nativeEventTarget: null | EventTarget,
62 + eventSystemFlags: number,
63 + container: null | EventTarget,
64 + ) => void,
65 +};
packages/react-native-renderer/src/legacy-events/ReactGenericBatching.js new
+83
@@ -0,0 +1,83 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its 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 +
8 +// Used as a way to call batchedUpdates when we don't have a reference to
9 +// the renderer. Such as when we're dispatching events or if third party
10 +// libraries need to call batchedUpdates. Eventually, this API will go away when
11 +// everything is batched by default. We'll then have a similar API to opt-out of
12 +// scheduled work and instead do synchronous work.
13 +
14 +// Defaults
15 +let batchedUpdatesImpl = function(fn, bookkeeping) {
16 + return fn(bookkeeping);
17 +};
18 +let discreteUpdatesImpl = function(fn, a, b, c, d) {
19 + return fn(a, b, c, d);
20 +};
21 +let flushDiscreteUpdatesImpl = function() {};
22 +let batchedEventUpdatesImpl = batchedUpdatesImpl;
23 +
24 +let isInsideEventHandler = false;
25 +let isBatchingEventUpdates = false;
26 +
27 +export function batchedUpdates(fn, bookkeeping) {
28 + if (isInsideEventHandler) {
29 + // If we are currently inside another batch, we need to wait until it
30 + // fully completes before restoring state.
31 + return fn(bookkeeping);
32 + }
33 + isInsideEventHandler = true;
34 + try {
35 + return batchedUpdatesImpl(fn, bookkeeping);
36 + } finally {
37 + isInsideEventHandler = false;
38 + }
39 +}
40 +
41 +export function batchedEventUpdates(fn, a, b) {
42 + if (isBatchingEventUpdates) {
43 + // If we are currently inside another batch, we need to wait until it
44 + // fully completes before restoring state.
45 + return fn(a, b);
46 + }
47 + isBatchingEventUpdates = true;
48 + try {
49 + return batchedEventUpdatesImpl(fn, a, b);
50 + } finally {
51 + isBatchingEventUpdates = false;
52 + }
53 +}
54 +
55 +export function discreteUpdates(fn, a, b, c, d) {
56 + const prevIsInsideEventHandler = isInsideEventHandler;
57 + isInsideEventHandler = true;
58 + try {
59 + return discreteUpdatesImpl(fn, a, b, c, d);
60 + } finally {
61 + isInsideEventHandler = prevIsInsideEventHandler;
62 + if (!isInsideEventHandler) {
63 + }
64 + }
65 +}
66 +
67 +export function flushDiscreteUpdatesIfNeeded(timeStamp: number) {
68 + if (!isInsideEventHandler) {
69 + flushDiscreteUpdatesImpl();
70 + }
71 +}
72 +
73 +export function setBatchingImplementation(
74 + _batchedUpdatesImpl,
75 + _discreteUpdatesImpl,
76 + _flushDiscreteUpdatesImpl,
77 + _batchedEventUpdatesImpl,
78 +) {
79 + batchedUpdatesImpl = _batchedUpdatesImpl;
80 + discreteUpdatesImpl = _discreteUpdatesImpl;
81 + flushDiscreteUpdatesImpl = _flushDiscreteUpdatesImpl;
82 + batchedEventUpdatesImpl = _batchedEventUpdatesImpl;
83 +}
packages/react-native-renderer/src/legacy-events/ReactSyntheticEventType.js new
+49
@@ -0,0 +1,49 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its 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 type for SyntheticEvent class that includes private properties
8 + * @flow
9 + */
10 +
11 +import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
12 +import type {EventPriority} from 'shared/ReactTypes';
13 +import type {TopLevelType} from './TopLevelEventTypes';
14 +
15 +export type DispatchConfig = {|
16 + dependencies?: Array<TopLevelType>,
17 + phasedRegistrationNames: {|
18 + bubbled: null | string,
19 + captured: null | string,
20 + |},
21 + registrationName?: string,
22 + eventPriority: EventPriority,
23 +|};
24 +
25 +export type CustomDispatchConfig = {|
26 + phasedRegistrationNames: {|
27 + bubbled: null,
28 + captured: null,
29 + |},
30 + registrationName?: string,
31 + customEvent: true,
32 +|};
33 +
34 +export type ReactSyntheticEvent = {|
35 + dispatchConfig: DispatchConfig | CustomDispatchConfig,
36 + getPooled: (
37 + dispatchConfig: DispatchConfig | CustomDispatchConfig,
38 + targetInst: Fiber,
39 + nativeTarget: Event,
40 + nativeEventTarget: EventTarget,
41 + ) => ReactSyntheticEvent,
42 + isPersistent: () => boolean,
43 + isPropagationStopped: () => boolean,
44 + _dispatchInstances?: null | Array<Fiber | null> | Fiber,
45 + _dispatchListeners?: null | Array<Function> | Function,
46 + _targetInst: Fiber,
47 + type: string,
48 + currentTarget: null | EventTarget,
49 +|};
packages/react-native-renderer/src/legacy-events/ResponderEventPlugin.js new
+802
@@ -0,0 +1,802 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its 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 +
8 +import {
9 + executeDirectDispatch,
10 + hasDispatches,
11 + executeDispatchesInOrderStopAtTrue,
12 + getInstanceFromNode,
13 + getFiberCurrentPropsFromNode,
14 +} from './EventPluginUtils';
15 +import ResponderSyntheticEvent from './ResponderSyntheticEvent';
16 +import ResponderTouchHistoryStore from './ResponderTouchHistoryStore';
17 +import accumulate from './accumulate';
18 +import {
19 + TOP_SCROLL,
20 + TOP_SELECTION_CHANGE,
21 + TOP_TOUCH_CANCEL,
22 + isStartish,
23 + isMoveish,
24 + isEndish,
25 + startDependencies,
26 + moveDependencies,
27 + endDependencies,
28 +} from './ResponderTopLevelEventTypes';
29 +import accumulateInto from './accumulateInto';
30 +import forEachAccumulated from './forEachAccumulated';
31 +import {HostComponent} from 'react-reconciler/src/ReactWorkTags';
32 +import invariant from 'shared/invariant';
33 +
34 +/**
35 + * Instance of element that should respond to touch/move types of interactions,
36 + * as indicated explicitly by relevant callbacks.
37 + */
38 +let responderInst = null;
39 +
40 +/**
41 + * Count of current touches. A textInput should become responder iff the
42 + * selection changes while there is a touch on the screen.
43 + */
44 +let trackedTouchCount = 0;
45 +
46 +const changeResponder = function(nextResponderInst, blockHostResponder) {
47 + const oldResponderInst = responderInst;
48 + responderInst = nextResponderInst;
49 + if (ResponderEventPlugin.GlobalResponderHandler !== null) {
50 + ResponderEventPlugin.GlobalResponderHandler.onChange(
51 + oldResponderInst,
52 + nextResponderInst,
53 + blockHostResponder,
54 + );
55 + }
56 +};
57 +
58 +const eventTypes = {
59 + /**
60 + * On a `touchStart`/`mouseDown`, is it desired that this element become the
61 + * responder?
62 + */
63 + startShouldSetResponder: {
64 + phasedRegistrationNames: {
65 + bubbled: 'onStartShouldSetResponder',
66 + captured: 'onStartShouldSetResponderCapture',
67 + },
68 + dependencies: startDependencies,
69 + },
70 +
71 + /**
72 + * On a `scroll`, is it desired that this element become the responder? This
73 + * is usually not needed, but should be used to retroactively infer that a
74 + * `touchStart` had occurred during momentum scroll. During a momentum scroll,
75 + * a touch start will be immediately followed by a scroll event if the view is
76 + * currently scrolling.
77 + *
78 + * TODO: This shouldn't bubble.
79 + */
80 + scrollShouldSetResponder: {
81 + phasedRegistrationNames: {
82 + bubbled: 'onScrollShouldSetResponder',
83 + captured: 'onScrollShouldSetResponderCapture',
84 + },
85 + dependencies: [TOP_SCROLL],
86 + },
87 +
88 + /**
89 + * On text selection change, should this element become the responder? This
90 + * is needed for text inputs or other views with native selection, so the
91 + * JS view can claim the responder.
92 + *
93 + * TODO: This shouldn't bubble.
94 + */
95 + selectionChangeShouldSetResponder: {
96 + phasedRegistrationNames: {
97 + bubbled: 'onSelectionChangeShouldSetResponder',
98 + captured: 'onSelectionChangeShouldSetResponderCapture',
99 + },
100 + dependencies: [TOP_SELECTION_CHANGE],
101 + },
102 +
103 + /**
104 + * On a `touchMove`/`mouseMove`, is it desired that this element become the
105 + * responder?
106 + */
107 + moveShouldSetResponder: {
108 + phasedRegistrationNames: {
109 + bubbled: 'onMoveShouldSetResponder',
110 + captured: 'onMoveShouldSetResponderCapture',
111 + },
112 + dependencies: moveDependencies,
113 + },
114 +
115 + /**
116 + * Direct responder events dispatched directly to responder. Do not bubble.
117 + */
118 + responderStart: {
119 + registrationName: 'onResponderStart',
120 + dependencies: startDependencies,
121 + },
122 + responderMove: {
123 + registrationName: 'onResponderMove',
124 + dependencies: moveDependencies,
125 + },
126 + responderEnd: {
127 + registrationName: 'onResponderEnd',
128 + dependencies: endDependencies,
129 + },
130 + responderRelease: {
131 + registrationName: 'onResponderRelease',
132 + dependencies: endDependencies,
133 + },
134 + responderTerminationRequest: {
135 + registrationName: 'onResponderTerminationRequest',
136 + dependencies: [],
137 + },
138 + responderGrant: {
139 + registrationName: 'onResponderGrant',
140 + dependencies: [],
141 + },
142 + responderReject: {
143 + registrationName: 'onResponderReject',
144 + dependencies: [],
145 + },
146 + responderTerminate: {
147 + registrationName: 'onResponderTerminate',
148 + dependencies: [],
149 + },
150 +};
151 +
152 +// Start of inline: the below functions were inlined from
153 +// EventPropagator.js, as they deviated from ReactDOM's newer
154 +// implementations.
155 +
156 +function getParent(inst) {
157 + do {
158 + inst = inst.return;
159 + // TODO: If this is a HostRoot we might want to bail out.
160 + // That is depending on if we want nested subtrees (layers) to bubble
161 + // events to their parent. We could also go through parentNode on the
162 + // host node but that wouldn't work for React Native and doesn't let us
163 + // do the portal feature.
164 + } while (inst && inst.tag !== HostComponent);
165 + if (inst) {
166 + return inst;
167 + }
168 + return null;
169 +}
170 +
171 +/**
172 + * Return the lowest common ancestor of A and B, or null if they are in
173 + * different trees.
174 + */
175 +export function getLowestCommonAncestor(instA, instB) {
176 + let depthA = 0;
177 + for (let tempA = instA; tempA; tempA = getParent(tempA)) {
178 + depthA++;
179 + }
180 + let depthB = 0;
181 + for (let tempB = instB; tempB; tempB = getParent(tempB)) {
182 + depthB++;
183 + }
184 +
185 + // If A is deeper, crawl up.
186 + while (depthA - depthB > 0) {
187 + instA = getParent(instA);
188 + depthA--;
189 + }
190 +
191 + // If B is deeper, crawl up.
192 + while (depthB - depthA > 0) {
193 + instB = getParent(instB);
194 + depthB--;
195 + }
196 +
197 + // Walk in lockstep until we find a match.
198 + let depth = depthA;
199 + while (depth--) {
200 + if (instA === instB || instA === instB.alternate) {
201 + return instA;
202 + }
203 + instA = getParent(instA);
204 + instB = getParent(instB);
205 + }
206 + return null;
207 +}
208 +
209 +/**
210 + * Return if A is an ancestor of B.
211 + */
212 +function isAncestor(instA, instB) {
213 + while (instB) {
214 + if (instA === instB || instA === instB.alternate) {
215 + return true;
216 + }
217 + instB = getParent(instB);
218 + }
219 + return false;
220 +}
221 +
222 +/**
223 + * Simulates the traversal of a two-phase, capture/bubble event dispatch.
224 + */
225 +function traverseTwoPhase(inst, fn, arg) {
226 + const path = [];
227 + while (inst) {
228 + path.push(inst);
229 + inst = getParent(inst);
230 + }
231 + let i;
232 + for (i = path.length; i-- > 0; ) {
233 + fn(path[i], 'captured', arg);
234 + }
235 + for (i = 0; i < path.length; i++) {
236 + fn(path[i], 'bubbled', arg);
237 + }
238 +}
239 +
240 +function getListener(inst, registrationName) {
241 + const stateNode = inst.stateNode;
242 + if (stateNode === null) {
243 + // Work in progress (ex: onload events in incremental mode).
244 + return null;
245 + }
246 + const props = getFiberCurrentPropsFromNode(stateNode);
247 + if (props === null) {
248 + // Work in progress.
249 + return null;
250 + }
251 + const listener = props[registrationName];
252 + invariant(
253 + !listener || typeof listener === 'function',
254 + 'Expected `%s` listener to be a function, instead got a value of `%s` type.',
255 + registrationName,
256 + typeof listener,
257 + );
258 + return listener;
259 +}
260 +
261 +function listenerAtPhase(inst, event, propagationPhase: PropagationPhases) {
262 + const registrationName =
263 + event.dispatchConfig.phasedRegistrationNames[propagationPhase];
264 + return getListener(inst, registrationName);
265 +}
266 +
267 +function accumulateDirectionalDispatches(inst, phase, event) {
268 + if (__DEV__) {
269 + if (!inst) {
270 + console.error('Dispatching inst must not be null');
271 + }
272 + }
273 + const listener = listenerAtPhase(inst, event, phase);
274 + if (listener) {
275 + event._dispatchListeners = accumulateInto(
276 + event._dispatchListeners,
277 + listener,
278 + );
279 + event._dispatchInstances = accumulateInto(event._dispatchInstances, inst);
280 + }
281 +}
282 +
283 +/**
284 + * Accumulates without regard to direction, does not look for phased
285 + * registration names. Same as `accumulateDirectDispatchesSingle` but without
286 + * requiring that the `dispatchMarker` be the same as the dispatched ID.
287 + */
288 +function accumulateDispatches(
289 + inst: Object,
290 + ignoredDirection: ?boolean,
291 + event: Object,
292 +): void {
293 + if (inst && event && event.dispatchConfig.registrationName) {
294 + const registrationName = event.dispatchConfig.registrationName;
295 + const listener = getListener(inst, registrationName);
296 + if (listener) {
297 + event._dispatchListeners = accumulateInto(
298 + event._dispatchListeners,
299 + listener,
300 + );
301 + event._dispatchInstances = accumulateInto(event._dispatchInstances, inst);
302 + }
303 + }
304 +}
305 +
306 +/**
307 + * Accumulates dispatches on an `SyntheticEvent`, but only for the
308 + * `dispatchMarker`.
309 + * @param {SyntheticEvent} event
310 + */
311 +function accumulateDirectDispatchesSingle(event: Object) {
312 + if (event && event.dispatchConfig.registrationName) {
313 + accumulateDispatches(event._targetInst, null, event);
314 + }
315 +}
316 +
317 +function accumulateDirectDispatches(events: ?(Array<Object> | Object)) {
318 + forEachAccumulated(events, accumulateDirectDispatchesSingle);
319 +}
320 +
321 +function accumulateTwoPhaseDispatchesSingleSkipTarget(event) {
322 + if (event && event.dispatchConfig.phasedRegistrationNames) {
323 + const targetInst = event._targetInst;
324 + const parentInst = targetInst ? getParent(targetInst) : null;
325 + traverseTwoPhase(parentInst, accumulateDirectionalDispatches, event);
326 + }
327 +}
328 +
329 +function accumulateTwoPhaseDispatchesSkipTarget(events) {
330 + forEachAccumulated(events, accumulateTwoPhaseDispatchesSingleSkipTarget);
331 +}
332 +
333 +function accumulateTwoPhaseDispatchesSingle(event) {
334 + if (event && event.dispatchConfig.phasedRegistrationNames) {
335 + traverseTwoPhase(event._targetInst, accumulateDirectionalDispatches, event);
336 + }
337 +}
338 +
339 +function accumulateTwoPhaseDispatches(events) {
340 + forEachAccumulated(events, accumulateTwoPhaseDispatchesSingle);
341 +}
342 +// End of inline
343 +
344 +/**
345 + *
346 + * Responder System:
347 + * ----------------
348 + *
349 + * - A global, solitary "interaction lock" on a view.
350 + * - If a node becomes the responder, it should convey visual feedback
351 + * immediately to indicate so, either by highlighting or moving accordingly.
352 + * - To be the responder means, that touches are exclusively important to that
353 + * responder view, and no other view.
354 + * - While touches are still occurring, the responder lock can be transferred to
355 + * a new view, but only to increasingly "higher" views (meaning ancestors of
356 + * the current responder).
357 + *
358 + * Responder being granted:
359 + * ------------------------
360 + *
361 + * - Touch starts, moves, and scrolls can cause an ID to become the responder.
362 + * - We capture/bubble `startShouldSetResponder`/`moveShouldSetResponder` to
363 + * the "appropriate place".
364 + * - If nothing is currently the responder, the "appropriate place" is the
365 + * initiating event's `targetID`.
366 + * - If something *is* already the responder, the "appropriate place" is the
367 + * first common ancestor of the event target and the current `responderInst`.
368 + * - Some negotiation happens: See the timing diagram below.
369 + * - Scrolled views automatically become responder. The reasoning is that a
370 + * platform scroll view that isn't built on top of the responder system has
371 + * began scrolling, and the active responder must now be notified that the
372 + * interaction is no longer locked to it - the system has taken over.
373 + *
374 + * - Responder being released:
375 + * As soon as no more touches that *started* inside of descendants of the
376 + * *current* responderInst, an `onResponderRelease` event is dispatched to the
377 + * current responder, and the responder lock is released.
378 + *
379 + * TODO:
380 + * - on "end", a callback hook for `onResponderEndShouldRemainResponder` that
381 + * determines if the responder lock should remain.
382 + * - If a view shouldn't "remain" the responder, any active touches should by
383 + * default be considered "dead" and do not influence future negotiations or
384 + * bubble paths. It should be as if those touches do not exist.
385 + * -- For multitouch: Usually a translate-z will choose to "remain" responder
386 + * after one out of many touches ended. For translate-y, usually the view
387 + * doesn't wish to "remain" responder after one of many touches end.
388 + * - Consider building this on top of a `stopPropagation` model similar to
389 + * `W3C` events.
390 + * - Ensure that `onResponderTerminate` is called on touch cancels, whether or
391 + * not `onResponderTerminationRequest` returns `true` or `false`.
392 + *
393 + */
394 +
395 +/* Negotiation Performed
396 + +-----------------------+
397 + / \
398 +Process low level events to + Current Responder + wantsResponderID
399 +determine who to perform negot-| (if any exists at all) |
400 +iation/transition | Otherwise just pass through|
401 +-------------------------------+----------------------------+------------------+
402 +Bubble to find first ID | |
403 +to return true:wantsResponderID| |
404 + | |
405 + +-------------+ | |
406 + | onTouchStart| | |
407 + +------+------+ none | |
408 + | return| |
409 ++-----------v-------------+true| +------------------------+ |
410 +|onStartShouldSetResponder|----->|onResponderStart (cur) |<-----------+
411 ++-----------+-------------+ | +------------------------+ | |
412 + | | | +--------+-------+
413 + | returned true for| false:REJECT +-------->|onResponderReject
414 + | wantsResponderID | | | +----------------+
415 + | (now attempt | +------------------+-----+ |
416 + | handoff) | | onResponder | |
417 + +------------------->| TerminationRequest| |
418 + | +------------------+-----+ |
419 + | | | +----------------+
420 + | true:GRANT +-------->|onResponderGrant|
421 + | | +--------+-------+
422 + | +------------------------+ | |
423 + | | onResponderTerminate |<-----------+
424 + | +------------------+-----+ |
425 + | | | +----------------+
426 + | +-------->|onResponderStart|
427 + | | +----------------+
428 +Bubble to find first ID | |
429 +to return true:wantsResponderID| |
430 + | |
431 + +-------------+ | |
432 + | onTouchMove | | |
433 + +------+------+ none | |
434 + | return| |
435 ++-----------v-------------+true| +------------------------+ |
436 +|onMoveShouldSetResponder |----->|onResponderMove (cur) |<-----------+
437 ++-----------+-------------+ | +------------------------+ | |
438 + | | | +--------+-------+
439 + | returned true for| false:REJECT +-------->|onResponderRejec|
440 + | wantsResponderID | | | +----------------+
441 + | (now attempt | +------------------+-----+ |
442 + | handoff) | | onResponder | |
443 + +------------------->| TerminationRequest| |
444 + | +------------------+-----+ |
445 + | | | +----------------+
446 + | true:GRANT +-------->|onResponderGrant|
447 + | | +--------+-------+
448 + | +------------------------+ | |
449 + | | onResponderTerminate |<-----------+
450 + | +------------------+-----+ |
451 + | | | +----------------+
452 + | +-------->|onResponderMove |
453 + | | +----------------+
454 + | |
455 + | |
456 + Some active touch started| |
457 + inside current responder | +------------------------+ |
458 + +------------------------->| onResponderEnd | |
459 + | | +------------------------+ |
460 + +---+---------+ | |
461 + | onTouchEnd | | |
462 + +---+---------+ | |
463 + | | +------------------------+ |
464 + +------------------------->| onResponderEnd | |
465 + No active touches started| +-----------+------------+ |
466 + inside current responder | | |
467 + | v |
468 + | +------------------------+ |
469 + | | onResponderRelease | |
470 + | +------------------------+ |
471 + | |
472 + + + */
473 +
474 +/**
475 + * A note about event ordering in the `EventPluginRegistry`.
476 + *
477 + * Suppose plugins are injected in the following order:
478 + *
479 + * `[R, S, C]`
480 + *
481 + * To help illustrate the example, assume `S` is `SimpleEventPlugin` (for
482 + * `onClick` etc) and `R` is `ResponderEventPlugin`.
483 + *
484 + * "Deferred-Dispatched Events":
485 + *
486 + * - The current event plugin system will traverse the list of injected plugins,
487 + * in order, and extract events by collecting the plugin's return value of
488 + * `extractEvents()`.
489 + * - These events that are returned from `extractEvents` are "deferred
490 + * dispatched events".
491 + * - When returned from `extractEvents`, deferred-dispatched events contain an
492 + * "accumulation" of deferred dispatches.
493 + * - These deferred dispatches are accumulated/collected before they are
494 + * returned, but processed at a later time by the `EventPluginRegistry` (hence the
495 + * name deferred).
496 + *
497 + * In the process of returning their deferred-dispatched events, event plugins
498 + * themselves can dispatch events on-demand without returning them from
499 + * `extractEvents`. Plugins might want to do this, so that they can use event
500 + * dispatching as a tool that helps them decide which events should be extracted
501 + * in the first place.
502 + *
503 + * "On-Demand-Dispatched Events":
504 + *
505 + * - On-demand-dispatched events are not returned from `extractEvents`.
506 + * - On-demand-dispatched events are dispatched during the process of returning
507 + * the deferred-dispatched events.
508 + * - They should not have side effects.
509 + * - They should be avoided, and/or eventually be replaced with another
510 + * abstraction that allows event plugins to perform multiple "rounds" of event
511 + * extraction.
512 + *
513 + * Therefore, the sequence of event dispatches becomes:
514 + *
515 + * - `R`s on-demand events (if any) (dispatched by `R` on-demand)
516 + * - `S`s on-demand events (if any) (dispatched by `S` on-demand)
517 + * - `C`s on-demand events (if any) (dispatched by `C` on-demand)
518 + * - `R`s extracted events (if any) (dispatched by `EventPluginRegistry`)
519 + * - `S`s extracted events (if any) (dispatched by `EventPluginRegistry`)
520 + * - `C`s extracted events (if any) (dispatched by `EventPluginRegistry`)
521 + *
522 + * In the case of `ResponderEventPlugin`: If the `startShouldSetResponder`
523 + * on-demand dispatch returns `true` (and some other details are satisfied) the
524 + * `onResponderGrant` deferred dispatched event is returned from
525 + * `extractEvents`. The sequence of dispatch executions in this case
526 + * will appear as follows:
527 + *
528 + * - `startShouldSetResponder` (`ResponderEventPlugin` dispatches on-demand)
529 + * - `touchStartCapture` (`EventPluginRegistry` dispatches as usual)
530 + * - `touchStart` (`EventPluginRegistry` dispatches as usual)
531 + * - `responderGrant/Reject` (`EventPluginRegistry` dispatches as usual)
532 + */
533 +
534 +function setResponderAndExtractTransfer(
535 + topLevelType,
536 + targetInst,
537 + nativeEvent,
538 + nativeEventTarget,
539 +) {
540 + const shouldSetEventType = isStartish(topLevelType)
541 + ? eventTypes.startShouldSetResponder
542 + : isMoveish(topLevelType)
543 + ? eventTypes.moveShouldSetResponder
544 + : topLevelType === TOP_SELECTION_CHANGE
545 + ? eventTypes.selectionChangeShouldSetResponder
546 + : eventTypes.scrollShouldSetResponder;
547 +
548 + // TODO: stop one short of the current responder.
549 + const bubbleShouldSetFrom = !responderInst
550 + ? targetInst
551 + : getLowestCommonAncestor(responderInst, targetInst);
552 +
553 + // When capturing/bubbling the "shouldSet" event, we want to skip the target
554 + // (deepest ID) if it happens to be the current responder. The reasoning:
555 + // It's strange to get an `onMoveShouldSetResponder` when you're *already*
556 + // the responder.
557 + const skipOverBubbleShouldSetFrom = bubbleShouldSetFrom === responderInst;
558 + const shouldSetEvent = ResponderSyntheticEvent.getPooled(
559 + shouldSetEventType,
560 + bubbleShouldSetFrom,
561 + nativeEvent,
562 + nativeEventTarget,
563 + );
564 + shouldSetEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
565 + if (skipOverBubbleShouldSetFrom) {
566 + accumulateTwoPhaseDispatchesSkipTarget(shouldSetEvent);
567 + } else {
568 + accumulateTwoPhaseDispatches(shouldSetEvent);
569 + }
570 + const wantsResponderInst = executeDispatchesInOrderStopAtTrue(shouldSetEvent);
571 + if (!shouldSetEvent.isPersistent()) {
572 + shouldSetEvent.constructor.release(shouldSetEvent);
573 + }
574 +
575 + if (!wantsResponderInst || wantsResponderInst === responderInst) {
576 + return null;
577 + }
578 + let extracted;
579 + const grantEvent = ResponderSyntheticEvent.getPooled(
580 + eventTypes.responderGrant,
581 + wantsResponderInst,
582 + nativeEvent,
583 + nativeEventTarget,
584 + );
585 + grantEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
586 +
587 + accumulateDirectDispatches(grantEvent);
588 + const blockHostResponder = executeDirectDispatch(grantEvent) === true;
589 + if (responderInst) {
590 + const terminationRequestEvent = ResponderSyntheticEvent.getPooled(
591 + eventTypes.responderTerminationRequest,
592 + responderInst,
593 + nativeEvent,
594 + nativeEventTarget,
595 + );
596 + terminationRequestEvent.touchHistory =
597 + ResponderTouchHistoryStore.touchHistory;
598 + accumulateDirectDispatches(terminationRequestEvent);
599 + const shouldSwitch =
600 + !hasDispatches(terminationRequestEvent) ||
601 + executeDirectDispatch(terminationRequestEvent);
602 + if (!terminationRequestEvent.isPersistent()) {
603 + terminationRequestEvent.constructor.release(terminationRequestEvent);
604 + }
605 +
606 + if (shouldSwitch) {
607 + const terminateEvent = ResponderSyntheticEvent.getPooled(
608 + eventTypes.responderTerminate,
609 + responderInst,
610 + nativeEvent,
611 + nativeEventTarget,
612 + );
613 + terminateEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
614 + accumulateDirectDispatches(terminateEvent);
615 + extracted = accumulate(extracted, [grantEvent, terminateEvent]);
616 + changeResponder(wantsResponderInst, blockHostResponder);
617 + } else {
618 + const rejectEvent = ResponderSyntheticEvent.getPooled(
619 + eventTypes.responderReject,
620 + wantsResponderInst,
621 + nativeEvent,
622 + nativeEventTarget,
623 + );
624 + rejectEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
625 + accumulateDirectDispatches(rejectEvent);
626 + extracted = accumulate(extracted, rejectEvent);
627 + }
628 + } else {
629 + extracted = accumulate(extracted, grantEvent);
630 + changeResponder(wantsResponderInst, blockHostResponder);
631 + }
632 + return extracted;
633 +}
634 +
635 +/**
636 + * A transfer is a negotiation between a currently set responder and the next
637 + * element to claim responder status. Any start event could trigger a transfer
638 + * of responderInst. Any move event could trigger a transfer.
639 + *
640 + * @param {string} topLevelType Record from `BrowserEventConstants`.
641 + * @return {boolean} True if a transfer of responder could possibly occur.
642 + */
643 +function canTriggerTransfer(topLevelType, topLevelInst, nativeEvent) {
644 + return (
645 + topLevelInst &&
646 + // responderIgnoreScroll: We are trying to migrate away from specifically
647 + // tracking native scroll events here and responderIgnoreScroll indicates we
648 + // will send topTouchCancel to handle canceling touch events instead
649 + ((topLevelType === TOP_SCROLL && !nativeEvent.responderIgnoreScroll) ||
650 + (trackedTouchCount > 0 && topLevelType === TOP_SELECTION_CHANGE) ||
651 + isStartish(topLevelType) ||
652 + isMoveish(topLevelType))
653 + );
654 +}
655 +
656 +/**
657 + * Returns whether or not this touch end event makes it such that there are no
658 + * longer any touches that started inside of the current `responderInst`.
659 + *
660 + * @param {NativeEvent} nativeEvent Native touch end event.
661 + * @return {boolean} Whether or not this touch end event ends the responder.
662 + */
663 +function noResponderTouches(nativeEvent) {
664 + const touches = nativeEvent.touches;
665 + if (!touches || touches.length === 0) {
666 + return true;
667 + }
668 + for (let i = 0; i < touches.length; i++) {
669 + const activeTouch = touches[i];
670 + const target = activeTouch.target;
671 + if (target !== null && target !== undefined && target !== 0) {
672 + // Is the original touch location inside of the current responder?
673 + const targetInst = getInstanceFromNode(target);
674 + if (isAncestor(responderInst, targetInst)) {
675 + return false;
676 + }
677 + }
678 + }
679 + return true;
680 +}
681 +
682 +const ResponderEventPlugin = {
683 + /* For unit testing only */
684 + _getResponder: function() {
685 + return responderInst;
686 + },
687 +
688 + eventTypes: eventTypes,
689 +
690 + /**
691 + * We must be resilient to `targetInst` being `null` on `touchMove` or
692 + * `touchEnd`. On certain platforms, this means that a native scroll has
693 + * assumed control and the original touch targets are destroyed.
694 + */
695 + extractEvents: function(
696 + topLevelType,
697 + targetInst,
698 + nativeEvent,
699 + nativeEventTarget,
700 + eventSystemFlags,
701 + ) {
702 + if (isStartish(topLevelType)) {
703 + trackedTouchCount += 1;
704 + } else if (isEndish(topLevelType)) {
705 + if (trackedTouchCount >= 0) {
706 + trackedTouchCount -= 1;
707 + } else {
708 + if (__DEV__) {
709 + console.warn(
710 + 'Ended a touch event which was not counted in `trackedTouchCount`.',
711 + );
712 + }
713 + return null;
714 + }
715 + }
716 +
717 + ResponderTouchHistoryStore.recordTouchTrack(topLevelType, nativeEvent);
718 +
719 + let extracted = canTriggerTransfer(topLevelType, targetInst, nativeEvent)
720 + ? setResponderAndExtractTransfer(
721 + topLevelType,
722 + targetInst,
723 + nativeEvent,
724 + nativeEventTarget,
725 + )
726 + : null;
727 + // Responder may or may not have transferred on a new touch start/move.
728 + // Regardless, whoever is the responder after any potential transfer, we
729 + // direct all touch start/move/ends to them in the form of
730 + // `onResponderMove/Start/End`. These will be called for *every* additional
731 + // finger that move/start/end, dispatched directly to whoever is the
732 + // current responder at that moment, until the responder is "released".
733 + //
734 + // These multiple individual change touch events are are always bookended
735 + // by `onResponderGrant`, and one of
736 + // (`onResponderRelease/onResponderTerminate`).
737 + const isResponderTouchStart = responderInst && isStartish(topLevelType);
738 + const isResponderTouchMove = responderInst && isMoveish(topLevelType);
739 + const isResponderTouchEnd = responderInst && isEndish(topLevelType);
740 + const incrementalTouch = isResponderTouchStart
741 + ? eventTypes.responderStart
742 + : isResponderTouchMove
743 + ? eventTypes.responderMove
744 + : isResponderTouchEnd
745 + ? eventTypes.responderEnd
746 + : null;
747 +
748 + if (incrementalTouch) {
749 + const gesture = ResponderSyntheticEvent.getPooled(
750 + incrementalTouch,
751 + responderInst,
752 + nativeEvent,
753 + nativeEventTarget,
754 + );
755 + gesture.touchHistory = ResponderTouchHistoryStore.touchHistory;
756 + accumulateDirectDispatches(gesture);
757 + extracted = accumulate(extracted, gesture);
758 + }
759 +
760 + const isResponderTerminate =
761 + responderInst && topLevelType === TOP_TOUCH_CANCEL;
762 + const isResponderRelease =
763 + responderInst &&
764 + !isResponderTerminate &&
765 + isEndish(topLevelType) &&
766 + noResponderTouches(nativeEvent);
767 + const finalTouch = isResponderTerminate
768 + ? eventTypes.responderTerminate
769 + : isResponderRelease
770 + ? eventTypes.responderRelease
771 + : null;
772 + if (finalTouch) {
773 + const finalEvent = ResponderSyntheticEvent.getPooled(
774 + finalTouch,
775 + responderInst,
776 + nativeEvent,
777 + nativeEventTarget,
778 + );
779 + finalEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
780 + accumulateDirectDispatches(finalEvent);
781 + extracted = accumulate(extracted, finalEvent);
782 + changeResponder(null);
783 + }
784 +
785 + return extracted;
786 + },
787 +
788 + GlobalResponderHandler: null,
789 +
790 + injection: {
791 + /**
792 + * @param {{onChange: (ReactID, ReactID) => void} GlobalResponderHandler
793 + * Object that handles any change in responder. Use this to inject
794 + * integration with an existing touch handling system etc.
795 + */
796 + injectGlobalResponderHandler(GlobalResponderHandler) {
797 + ResponderEventPlugin.GlobalResponderHandler = GlobalResponderHandler;
798 + },
799 + },
800 +};
801 +
802 +export default ResponderEventPlugin;
packages/react-native-renderer/src/legacy-events/ResponderSyntheticEvent.js new
+21
@@ -0,0 +1,21 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its 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 +
8 +import SyntheticEvent from './SyntheticEvent';
9 +
10 +/**
11 + * `touchHistory` isn't actually on the native event, but putting it in the
12 + * interface will ensure that it is cleaned up when pooled/destroyed. The
13 + * `ResponderEventPlugin` will populate it appropriately.
14 + */
15 +const ResponderSyntheticEvent = SyntheticEvent.extend({
16 + touchHistory: function(nativeEvent) {
17 + return null; // Actually doesn't even look at the native event.
18 + },
19 +});
20 +
21 +export default ResponderSyntheticEvent;
packages/react-native-renderer/src/legacy-events/ResponderTopLevelEventTypes.js new
+31
@@ -0,0 +1,31 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its 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 +export const TOP_TOUCH_START = 'topTouchStart';
11 +export const TOP_TOUCH_MOVE = 'topTouchMove';
12 +export const TOP_TOUCH_END = 'topTouchEnd';
13 +export const TOP_TOUCH_CANCEL = 'topTouchCancel';
14 +export const TOP_SCROLL = 'topScroll';
15 +export const TOP_SELECTION_CHANGE = 'topSelectionChange';
16 +
17 +export function isStartish(topLevelType: mixed): boolean {
18 + return topLevelType === TOP_TOUCH_START;
19 +}
20 +
21 +export function isMoveish(topLevelType: mixed): boolean {
22 + return topLevelType === TOP_TOUCH_MOVE;
23 +}
24 +
25 +export function isEndish(topLevelType: mixed): boolean {
26 + return topLevelType === TOP_TOUCH_END || topLevelType === TOP_TOUCH_CANCEL;
27 +}
28 +
29 +export const startDependencies = [TOP_TOUCH_START];
30 +export const moveDependencies = [TOP_TOUCH_MOVE];
31 +export const endDependencies = [TOP_TOUCH_CANCEL, TOP_TOUCH_END];
packages/react-native-renderer/src/legacy-events/ResponderTouchHistoryStore.js new
+222
@@ -0,0 +1,222 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its 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 invariant from 'shared/invariant';
11 +
12 +import {isStartish, isMoveish, isEndish} from './ResponderTopLevelEventTypes';
13 +
14 +/**
15 + * Tracks the position and time of each active touch by `touch.identifier`. We
16 + * should typically only see IDs in the range of 1-20 because IDs get recycled
17 + * when touches end and start again.
18 + */
19 +type TouchRecord = {|
20 + touchActive: boolean,
21 + startPageX: number,
22 + startPageY: number,
23 + startTimeStamp: number,
24 + currentPageX: number,
25 + currentPageY: number,
26 + currentTimeStamp: number,
27 + previousPageX: number,
28 + previousPageY: number,
29 + previousTimeStamp: number,
30 +|};
31 +
32 +const MAX_TOUCH_BANK = 20;
33 +const touchBank: Array<TouchRecord> = [];
34 +const touchHistory = {
35 + touchBank,
36 + numberActiveTouches: 0,
37 + // If there is only one active touch, we remember its location. This prevents
38 + // us having to loop through all of the touches all the time in the most
39 + // common case.
40 + indexOfSingleActiveTouch: -1,
41 + mostRecentTimeStamp: 0,
42 +};
43 +
44 +type Touch = {
45 + identifier: ?number,
46 + pageX: number,
47 + pageY: number,
48 + timestamp: number,
49 + ...
50 +};
51 +type TouchEvent = {
52 + changedTouches: Array<Touch>,
53 + touches: Array<Touch>,
54 + ...
55 +};
56 +
57 +function timestampForTouch(touch: Touch): number {
58 + // The legacy internal implementation provides "timeStamp", which has been
59 + // renamed to "timestamp". Let both work for now while we iron it out
60 + // TODO (evv): rename timeStamp to timestamp in internal code
61 + return (touch: any).timeStamp || touch.timestamp;
62 +}
63 +
64 +/**
65 + * TODO: Instead of making gestures recompute filtered velocity, we could
66 + * include a built in velocity computation that can be reused globally.
67 + */
68 +function createTouchRecord(touch: Touch): TouchRecord {
69 + return {
70 + touchActive: true,
71 + startPageX: touch.pageX,
72 + startPageY: touch.pageY,
73 + startTimeStamp: timestampForTouch(touch),
74 + currentPageX: touch.pageX,
75 + currentPageY: touch.pageY,
76 + currentTimeStamp: timestampForTouch(touch),
77 + previousPageX: touch.pageX,
78 + previousPageY: touch.pageY,
79 + previousTimeStamp: timestampForTouch(touch),
80 + };
81 +}
82 +
83 +function resetTouchRecord(touchRecord: TouchRecord, touch: Touch): void {
84 + touchRecord.touchActive = true;
85 + touchRecord.startPageX = touch.pageX;
86 + touchRecord.startPageY = touch.pageY;
87 + touchRecord.startTimeStamp = timestampForTouch(touch);
88 + touchRecord.currentPageX = touch.pageX;
89 + touchRecord.currentPageY = touch.pageY;
90 + touchRecord.currentTimeStamp = timestampForTouch(touch);
91 + touchRecord.previousPageX = touch.pageX;
92 + touchRecord.previousPageY = touch.pageY;
93 + touchRecord.previousTimeStamp = timestampForTouch(touch);
94 +}
95 +
96 +function getTouchIdentifier({identifier}: Touch): number {
97 + invariant(identifier != null, 'Touch object is missing identifier.');
98 + if (__DEV__) {
99 + if (identifier > MAX_TOUCH_BANK) {
100 + console.error(
101 + 'Touch identifier %s is greater than maximum supported %s which causes ' +
102 + 'performance issues backfilling array locations for all of the indices.',
103 + identifier,
104 + MAX_TOUCH_BANK,
105 + );
106 + }
107 + }
108 + return identifier;
109 +}
110 +
111 +function recordTouchStart(touch: Touch): void {
112 + const identifier = getTouchIdentifier(touch);
113 + const touchRecord = touchBank[identifier];
114 + if (touchRecord) {
115 + resetTouchRecord(touchRecord, touch);
116 + } else {
117 + touchBank[identifier] = createTouchRecord(touch);
118 + }
119 + touchHistory.mostRecentTimeStamp = timestampForTouch(touch);
120 +}
121 +
122 +function recordTouchMove(touch: Touch): void {
123 + const touchRecord = touchBank[getTouchIdentifier(touch)];
124 + if (touchRecord) {
125 + touchRecord.touchActive = true;
126 + touchRecord.previousPageX = touchRecord.currentPageX;
127 + touchRecord.previousPageY = touchRecord.currentPageY;
128 + touchRecord.previousTimeStamp = touchRecord.currentTimeStamp;
129 + touchRecord.currentPageX = touch.pageX;
130 + touchRecord.currentPageY = touch.pageY;
131 + touchRecord.currentTimeStamp = timestampForTouch(touch);
132 + touchHistory.mostRecentTimeStamp = timestampForTouch(touch);
133 + } else {
134 + if (__DEV__) {
135 + console.warn(
136 + 'Cannot record touch move without a touch start.\n' +
137 + 'Touch Move: %s\n' +
138 + 'Touch Bank: %s',
139 + printTouch(touch),
140 + printTouchBank(),
141 + );
142 + }
143 + }
144 +}
145 +
146 +function recordTouchEnd(touch: Touch): void {
147 + const touchRecord = touchBank[getTouchIdentifier(touch)];
148 + if (touchRecord) {
149 + touchRecord.touchActive = false;
150 + touchRecord.previousPageX = touchRecord.currentPageX;
151 + touchRecord.previousPageY = touchRecord.currentPageY;
152 + touchRecord.previousTimeStamp = touchRecord.currentTimeStamp;
153 + touchRecord.currentPageX = touch.pageX;
154 + touchRecord.currentPageY = touch.pageY;
155 + touchRecord.currentTimeStamp = timestampForTouch(touch);
156 + touchHistory.mostRecentTimeStamp = timestampForTouch(touch);
157 + } else {
158 + if (__DEV__) {
159 + console.warn(
160 + 'Cannot record touch end without a touch start.\n' +
161 + 'Touch End: %s\n' +
162 + 'Touch Bank: %s',
163 + printTouch(touch),
164 + printTouchBank(),
165 + );
166 + }
167 + }
168 +}
169 +
170 +function printTouch(touch: Touch): string {
171 + return JSON.stringify({
172 + identifier: touch.identifier,
173 + pageX: touch.pageX,
174 + pageY: touch.pageY,
175 + timestamp: timestampForTouch(touch),
176 + });
177 +}
178 +
179 +function printTouchBank(): string {
180 + let printed = JSON.stringify(touchBank.slice(0, MAX_TOUCH_BANK));
181 + if (touchBank.length > MAX_TOUCH_BANK) {
182 + printed += ' (original size: ' + touchBank.length + ')';
183 + }
184 + return printed;
185 +}
186 +
187 +const ResponderTouchHistoryStore = {
188 + recordTouchTrack(topLevelType: string, nativeEvent: TouchEvent): void {
189 + if (isMoveish(topLevelType)) {
190 + nativeEvent.changedTouches.forEach(recordTouchMove);
191 + } else if (isStartish(topLevelType)) {
192 + nativeEvent.changedTouches.forEach(recordTouchStart);
193 + touchHistory.numberActiveTouches = nativeEvent.touches.length;
194 + if (touchHistory.numberActiveTouches === 1) {
195 + touchHistory.indexOfSingleActiveTouch =
196 + nativeEvent.touches[0].identifier;
197 + }
198 + } else if (isEndish(topLevelType)) {
199 + nativeEvent.changedTouches.forEach(recordTouchEnd);
200 + touchHistory.numberActiveTouches = nativeEvent.touches.length;
201 + if (touchHistory.numberActiveTouches === 1) {
202 + for (let i = 0; i < touchBank.length; i++) {
203 + const touchTrackToCheck = touchBank[i];
204 + if (touchTrackToCheck != null && touchTrackToCheck.touchActive) {
205 + touchHistory.indexOfSingleActiveTouch = i;
206 + break;
207 + }
208 + }
209 + if (__DEV__) {
210 + const activeRecord = touchBank[touchHistory.indexOfSingleActiveTouch];
211 + if (activeRecord == null || !activeRecord.touchActive) {
212 + console.error('Cannot find single active touch.');
213 + }
214 + }
215 + }
216 + }
217 + },
218 +
219 + touchHistory,
220 +};
221 +
222 +export default ResponderTouchHistoryStore;
packages/react-native-renderer/src/legacy-events/SyntheticEvent.js new
+366
@@ -0,0 +1,366 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its 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 +
8 +/* eslint valid-typeof: 0 */
9 +
10 +import invariant from 'shared/invariant';
11 +import {enableModernEventSystem} from 'shared/ReactFeatureFlags';
12 +
13 +const EVENT_POOL_SIZE = 10;
14 +
15 +/**
16 + * @interface Event
17 + * @see http://www.w3.org/TR/DOM-Level-3-Events/
18 + */
19 +const EventInterface = {
20 + type: null,
21 + target: null,
22 + // currentTarget is set when dispatching; no use in copying it here
23 + currentTarget: function() {
24 + return null;
25 + },
26 + eventPhase: null,
27 + bubbles: null,
28 + cancelable: null,
29 + timeStamp: function(event) {
30 + return event.timeStamp || Date.now();
31 + },
32 + defaultPrevented: null,
33 + isTrusted: null,
34 +};
35 +
36 +function functionThatReturnsTrue() {
37 + return true;
38 +}
39 +
40 +function functionThatReturnsFalse() {
41 + return false;
42 +}
43 +
44 +/**
45 + * Synthetic events are dispatched by event plugins, typically in response to a
46 + * top-level event delegation handler.
47 + *
48 + * These systems should generally use pooling to reduce the frequency of garbage
49 + * collection. The system should check `isPersistent` to determine whether the
50 + * event should be released into the pool after being dispatched. Users that
51 + * need a persisted event should invoke `persist`.
52 + *
53 + * Synthetic events (and subclasses) implement the DOM Level 3 Events API by
54 + * normalizing browser quirks. Subclasses do not necessarily have to implement a
55 + * DOM interface; custom application-specific events can also subclass this.
56 + *
57 + * @param {object} dispatchConfig Configuration used to dispatch this event.
58 + * @param {*} targetInst Marker identifying the event target.
59 + * @param {object} nativeEvent Native browser event.
60 + * @param {DOMEventTarget} nativeEventTarget Target node.
61 + */
62 +function SyntheticEvent(
63 + dispatchConfig,
64 + targetInst,
65 + nativeEvent,
66 + nativeEventTarget,
67 +) {
68 + if (__DEV__) {
69 + // these have a getter/setter for warnings
70 + delete this.nativeEvent;
71 + delete this.preventDefault;
72 + delete this.stopPropagation;
73 + delete this.isDefaultPrevented;
74 + delete this.isPropagationStopped;
75 + }
76 +
77 + this.dispatchConfig = dispatchConfig;
78 + this._targetInst = targetInst;
79 + this.nativeEvent = nativeEvent;
80 + if (!enableModernEventSystem) {
81 + this._dispatchListeners = null;
82 + this._dispatchInstances = null;
83 + }
84 +
85 + const Interface = this.constructor.Interface;
86 + for (const propName in Interface) {
87 + if (!Interface.hasOwnProperty(propName)) {
88 + continue;
89 + }
90 + if (__DEV__) {
91 + delete this[propName]; // this has a getter/setter for warnings
92 + }
93 + const normalize = Interface[propName];
94 + if (normalize) {
95 + this[propName] = normalize(nativeEvent);
96 + } else {
97 + if (propName === 'target') {
98 + this.target = nativeEventTarget;
99 + } else {
100 + this[propName] = nativeEvent[propName];
101 + }
102 + }
103 + }
104 +
105 + const defaultPrevented =
106 + nativeEvent.defaultPrevented != null
107 + ? nativeEvent.defaultPrevented
108 + : nativeEvent.returnValue === false;
109 + if (defaultPrevented) {
110 + this.isDefaultPrevented = functionThatReturnsTrue;
111 + } else {
112 + this.isDefaultPrevented = functionThatReturnsFalse;
113 + }
114 + this.isPropagationStopped = functionThatReturnsFalse;
115 + return this;
116 +}
117 +
118 +Object.assign(SyntheticEvent.prototype, {
119 + preventDefault: function() {
120 + this.defaultPrevented = true;
121 + const event = this.nativeEvent;
122 + if (!event) {
123 + return;
124 + }
125 +
126 + if (event.preventDefault) {
127 + event.preventDefault();
128 + } else if (typeof event.returnValue !== 'unknown') {
129 + event.returnValue = false;
130 + }
131 + this.isDefaultPrevented = functionThatReturnsTrue;
132 + },
133 +
134 + stopPropagation: function() {
135 + const event = this.nativeEvent;
136 + if (!event) {
137 + return;
138 + }
139 +
140 + if (event.stopPropagation) {
141 + event.stopPropagation();
142 + } else if (typeof event.cancelBubble !== 'unknown') {
143 + // The ChangeEventPlugin registers a "propertychange" event for
144 + // IE. This event does not support bubbling or cancelling, and
145 + // any references to cancelBubble throw "Member not found". A
146 + // typeof check of "unknown" circumvents this issue (and is also
147 + // IE specific).
148 + event.cancelBubble = true;
149 + }
150 +
151 + this.isPropagationStopped = functionThatReturnsTrue;
152 + },
153 +
154 + /**
155 + * We release all dispatched `SyntheticEvent`s after each event loop, adding
156 + * them back into the pool. This allows a way to hold onto a reference that
157 + * won't be added back into the pool.
158 + */
159 + persist: function() {
160 + // Modern event system doesn't use pooling.
161 + if (!enableModernEventSystem) {
162 + this.isPersistent = functionThatReturnsTrue;
163 + }
164 + },
165 +
166 + /**
167 + * Checks if this event should be released back into the pool.
168 + *
169 + * @return {boolean} True if this should not be released, false otherwise.
170 + */
171 + isPersistent: enableModernEventSystem
172 + ? functionThatReturnsTrue
173 + : functionThatReturnsFalse,
174 +
175 + /**
176 + * `PooledClass` looks for `destructor` on each instance it releases.
177 + */
178 + destructor: function() {
179 + // Modern event system doesn't use pooling.
180 + if (!enableModernEventSystem) {
181 + const Interface = this.constructor.Interface;
182 + for (const propName in Interface) {
183 + if (__DEV__) {
184 + Object.defineProperty(
185 + this,
186 + propName,
187 + getPooledWarningPropertyDefinition(propName, Interface[propName]),
188 + );
189 + } else {
190 + this[propName] = null;
191 + }
192 + }
193 + this.dispatchConfig = null;
194 + this._targetInst = null;
195 + this.nativeEvent = null;
196 + this.isDefaultPrevented = functionThatReturnsFalse;
197 + this.isPropagationStopped = functionThatReturnsFalse;
198 + this._dispatchListeners = null;
199 + this._dispatchInstances = null;
200 + if (__DEV__) {
201 + Object.defineProperty(
202 + this,
203 + 'nativeEvent',
204 + getPooledWarningPropertyDefinition('nativeEvent', null),
205 + );
206 + Object.defineProperty(
207 + this,
208 + 'isDefaultPrevented',
209 + getPooledWarningPropertyDefinition(
210 + 'isDefaultPrevented',
211 + functionThatReturnsFalse,
212 + ),
213 + );
214 + Object.defineProperty(
215 + this,
216 + 'isPropagationStopped',
217 + getPooledWarningPropertyDefinition(
218 + 'isPropagationStopped',
219 + functionThatReturnsFalse,
220 + ),
221 + );
222 + Object.defineProperty(
223 + this,
224 + 'preventDefault',
225 + getPooledWarningPropertyDefinition('preventDefault', () => {}),
226 + );
227 + Object.defineProperty(
228 + this,
229 + 'stopPropagation',
230 + getPooledWarningPropertyDefinition('stopPropagation', () => {}),
231 + );
232 + }
233 + }
234 + },
235 +});
236 +
237 +SyntheticEvent.Interface = EventInterface;
238 +
239 +/**
240 + * Helper to reduce boilerplate when creating subclasses.
241 + */
242 +SyntheticEvent.extend = function(Interface) {
243 + const Super = this;
244 +
245 + const E = function() {};
246 + E.prototype = Super.prototype;
247 + const prototype = new E();
248 +
249 + function Class() {
250 + return Super.apply(this, arguments);
251 + }
252 + Object.assign(prototype, Class.prototype);
253 + Class.prototype = prototype;
254 + Class.prototype.constructor = Class;
255 +
256 + Class.Interface = Object.assign({}, Super.Interface, Interface);
257 + Class.extend = Super.extend;
258 + addEventPoolingTo(Class);
259 +
260 + return Class;
261 +};
262 +
263 +addEventPoolingTo(SyntheticEvent);
264 +
265 +/**
266 + * Helper to nullify syntheticEvent instance properties when destructing
267 + *
268 + * @param {String} propName
269 + * @param {?object} getVal
270 + * @return {object} defineProperty object
271 + */
272 +function getPooledWarningPropertyDefinition(propName, getVal) {
273 + const isFunction = typeof getVal === 'function';
274 + return {
275 + configurable: true,
276 + set: set,
277 + get: get,
278 + };
279 +
280 + function set(val) {
281 + const action = isFunction ? 'setting the method' : 'setting the property';
282 + warn(action, 'This is effectively a no-op');
283 + return val;
284 + }
285 +
286 + function get() {
287 + const action = isFunction
288 + ? 'accessing the method'
289 + : 'accessing the property';
290 + const result = isFunction
291 + ? 'This is a no-op function'
292 + : 'This is set to null';
293 + warn(action, result);
294 + return getVal;
295 + }
296 +
297 + function warn(action, result) {
298 + if (__DEV__) {
299 + console.error(
300 + "This synthetic event is reused for performance reasons. If you're seeing this, " +
301 + "you're %s `%s` on a released/nullified synthetic event. %s. " +
302 + 'If you must keep the original synthetic event around, use event.persist(). ' +
303 + 'See https://fb.me/react-event-pooling for more information.',
304 + action,
305 + propName,
306 + result,
307 + );
308 + }
309 + }
310 +}
311 +
312 +function createOrGetPooledEvent(
313 + dispatchConfig,
314 + targetInst,
315 + nativeEvent,
316 + nativeInst,
317 +) {
318 + const EventConstructor = this;
319 + // Modern event system doesn't use pooling.
320 + if (!enableModernEventSystem) {
321 + if (EventConstructor.eventPool.length) {
322 + const instance = EventConstructor.eventPool.pop();
323 + EventConstructor.call(
324 + instance,
325 + dispatchConfig,
326 + targetInst,
327 + nativeEvent,
328 + nativeInst,
329 + );
330 + return instance;
331 + }
332 + }
333 + return new EventConstructor(
334 + dispatchConfig,
335 + targetInst,
336 + nativeEvent,
337 + nativeInst,
338 + );
339 +}
340 +
341 +function releasePooledEvent(event) {
342 + // Modern event system doesn't use pooling.
343 + if (!enableModernEventSystem) {
344 + const EventConstructor = this;
345 + invariant(
346 + event instanceof EventConstructor,
347 + 'Trying to release an event instance into a pool of a different type.',
348 + );
349 + event.destructor();
350 + if (EventConstructor.eventPool.length < EVENT_POOL_SIZE) {
351 + EventConstructor.eventPool.push(event);
352 + }
353 + }
354 +}
355 +
356 +function addEventPoolingTo(EventConstructor) {
357 + EventConstructor.getPooled = createOrGetPooledEvent;
358 +
359 + // Modern event system doesn't use pooling.
360 + if (!enableModernEventSystem) {
361 + EventConstructor.eventPool = [];
362 + EventConstructor.release = releasePooledEvent;
363 + }
364 +}
365 +
366 +export default SyntheticEvent;
packages/react-native-renderer/src/legacy-events/TopLevelEventTypes.js new
+39
@@ -0,0 +1,39 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its 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 +export type RNTopLevelEventType =
11 + | 'topMouseDown'
12 + | 'topMouseMove'
13 + | 'topMouseUp'
14 + | 'topScroll'
15 + | 'topSelectionChange'
16 + | 'topTouchCancel'
17 + | 'topTouchEnd'
18 + | 'topTouchMove'
19 + | 'topTouchStart';
20 +
21 +export opaque type DOMTopLevelEventType = string;
22 +
23 +// Do not use the below two methods directly!
24 +// Instead use constants exported from DOMTopLevelEventTypes in ReactDOM.
25 +// (It is the only module that is allowed to access these methods.)
26 +
27 +export function unsafeCastStringToDOMTopLevelType(
28 + topLevelType: string,
29 +): DOMTopLevelEventType {
30 + return topLevelType;
31 +}
32 +
33 +export function unsafeCastDOMTopLevelTypeToString(
34 + topLevelType: DOMTopLevelEventType,
35 +): string {
36 + return topLevelType;
37 +}
38 +
39 +export type TopLevelType = DOMTopLevelEventType | RNTopLevelEventType;
packages/react-native-renderer/src/legacy-events/accumulate.js new
+45
@@ -0,0 +1,45 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its 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 invariant from 'shared/invariant';
11 +
12 +/**
13 + * Accumulates items that must not be null or undefined.
14 + *
15 + * This is used to conserve memory by avoiding array allocations.
16 + *
17 + * @return {*|array<*>} An accumulation of items.
18 + */
19 +function accumulate<T>(
20 + current: ?(T | Array<T>),
21 + next: T | Array<T>,
22 +): T | Array<T> {
23 + invariant(
24 + next != null,
25 + 'accumulate(...): Accumulated items must not be null or undefined.',
26 + );
27 +
28 + if (current == null) {
29 + return next;
30 + }
31 +
32 + // Both are not empty. Warning: Never call x.concat(y) when you are not
33 + // certain that x is an Array (x could be a string with concat method).
34 + if (Array.isArray(current)) {
35 + return current.concat(next);
36 + }
37 +
38 + if (Array.isArray(next)) {
39 + return [current].concat(next);
40 + }
41 +
42 + return [current, next];
43 +}
44 +
45 +export default accumulate;
packages/react-native-renderer/src/legacy-events/accumulateInto.js new
+57
@@ -0,0 +1,57 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its 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 invariant from 'shared/invariant';
11 +
12 +/**
13 + * Accumulates items that must not be null or undefined into the first one. This
14 + * is used to conserve memory by avoiding array allocations, and thus sacrifices
15 + * API cleanness. Since `current` can be null before being passed in and not
16 + * null after this function, make sure to assign it back to `current`:
17 + *
18 + * `a = accumulateInto(a, b);`
19 + *
20 + * This API should be sparingly used. Try `accumulate` for something cleaner.
21 + *
22 + * @return {*|array<*>} An accumulation of items.
23 + */
24 +
25 +function accumulateInto<T>(
26 + current: ?(Array<T> | T),
27 + next: T | Array<T>,
28 +): T | Array<T> {
29 + invariant(
30 + next != null,
31 + 'accumulateInto(...): Accumulated items must not be null or undefined.',
32 + );
33 +
34 + if (current == null) {
35 + return next;
36 + }
37 +
38 + // Both are not empty. Warning: Never call x.concat(y) when you are not
39 + // certain that x is an Array (x could be a string with concat method).
40 + if (Array.isArray(current)) {
41 + if (Array.isArray(next)) {
42 + current.push.apply(current, next);
43 + return current;
44 + }
45 + current.push(next);
46 + return current;
47 + }
48 +
49 + if (Array.isArray(next)) {
50 + // A bit too dangerous to mutate `next`.
51 + return [current].concat(next);
52 + }
53 +
54 + return [current, next];
55 +}
56 +
57 +export default accumulateInto;
packages/react-native-renderer/src/legacy-events/forEachAccumulated.js new
+31
@@ -0,0 +1,31 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its 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 +/**
11 + * @param {array} arr an "accumulation" of items which is either an Array or
12 + * a single item. Useful when paired with the `accumulate` module. This is a
13 + * simple utility that allows us to reason about a collection of items, but
14 + * handling the case when there is exactly one item (and we do not need to
15 + * allocate an array).
16 + * @param {function} cb Callback invoked with each element or a collection.
17 + * @param {?} [scope] Scope used as `this` in a callback.
18 + */
19 +function forEachAccumulated<T>(
20 + arr: ?(Array<T> | T),
21 + cb: (elem: T) => void,
22 + scope: ?any,
23 +) {
24 + if (Array.isArray(arr)) {
25 + arr.forEach(cb, scope);
26 + } else if (arr) {
27 + cb.call(scope, arr);
28 + }
29 +}
30 +
31 +export default forEachAccumulated;
scripts/flow/react-native-host-hooks.js
+1 -1
@@ -16,7 +16,7 @@ import type {
16 ReactNativeBaseComponentViewConfig,
17 ViewConfigGetter,
18 } from 'react-native-renderer/src/ReactNativeTypes';
19 -import type {RNTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
19 +import type {RNTopLevelEventType} from 'react-native-renderer/src/legacy-events/TopLevelEventTypes';
20 import type {CapturedError} from 'react-reconciler/src/ReactCapturedValue';
21 import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
22
scripts/shared/pathsByLanguageVersion.js
-1
@@ -15,7 +15,6 @@ const esNextPaths = [
15 'packages/dom-event-testing-library/**/*.js',
16 'packages/react-interactions/**/*.js',
17 'packages/react-interactions/**/*.js',
18 - 'packages/legacy-events/**/*.js',
18 'packages/shared/**/*.js',
19 // Shims and Flow environment
20 'scripts/flow/*.js',