Fork legacy-events folder into react-dom and react-native (#19228)
This commit is contained in:
parent
cd0275dcb2
commit
e3f4eb7272
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"private": true,
|
||||
"name": "legacy-events",
|
||||
"version": "0.0.0"
|
||||
}
|
|
@ -42,7 +42,7 @@ import {canUseDOM} from 'shared/ExecutionEnvironment';
|
|||
import {
|
||||
eventNameDispatchConfigs,
|
||||
injectEventPluginsByName,
|
||||
} from 'legacy-events/EventPluginRegistry';
|
||||
} from '../legacy-events/EventPluginRegistry';
|
||||
import ReactVersion from 'shared/ReactVersion';
|
||||
import invariant from 'shared/invariant';
|
||||
import {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {setComponentTree} from 'legacy-events/EventPluginUtils';
|
||||
import {setComponentTree} from '../legacy-events/EventPluginUtils';
|
||||
|
||||
import {
|
||||
getFiberCurrentPropsFromNode,
|
||||
|
@ -29,7 +29,7 @@ import {
|
|||
injectEventPluginOrder,
|
||||
injectEventPluginsByName,
|
||||
injectEventPlugins,
|
||||
} from 'legacy-events/EventPluginRegistry';
|
||||
} from '../legacy-events/EventPluginRegistry';
|
||||
import {enableModernEventSystem} from 'shared/ReactFeatureFlags';
|
||||
|
||||
if (enableModernEventSystem) {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* @flow
|
||||
*/
|
||||
|
||||
import {registrationNameModules} from 'legacy-events/EventPluginRegistry';
|
||||
import {registrationNameModules} from '../legacy-events/EventPluginRegistry';
|
||||
import {canUseDOM} from 'shared/ExecutionEnvironment';
|
||||
import invariant from 'shared/invariant';
|
||||
import {
|
||||
|
|
|
@ -17,7 +17,7 @@ import type {
|
|||
SuspenseInstance,
|
||||
Props,
|
||||
} from './ReactDOMHostConfig';
|
||||
import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
|
||||
import type {DOMTopLevelEventType} from '../legacy-events/TopLevelEventTypes';
|
||||
|
||||
import {
|
||||
HostComponent,
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* @flow
|
||||
*/
|
||||
|
||||
import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
|
||||
import type {DOMTopLevelEventType} from '../legacy-events/TopLevelEventTypes';
|
||||
import type {EventPriority, ReactScopeInstance} from 'shared/ReactTypes';
|
||||
import type {
|
||||
ReactDOMEventHandle,
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* @flow
|
||||
*/
|
||||
|
||||
import type {TopLevelType} from 'legacy-events/TopLevelEventTypes';
|
||||
import type {TopLevelType} from '../legacy-events/TopLevelEventTypes';
|
||||
import type {Fiber, FiberRoot} from 'react-reconciler/src/ReactInternalTypes';
|
||||
import type {
|
||||
BoundingRect,
|
||||
|
|
|
@ -11,11 +11,11 @@ import type {EventPriority} from 'shared/ReactTypes';
|
|||
import type {
|
||||
TopLevelType,
|
||||
DOMTopLevelEventType,
|
||||
} from 'legacy-events/TopLevelEventTypes';
|
||||
} from '../legacy-events/TopLevelEventTypes';
|
||||
import type {
|
||||
DispatchConfig,
|
||||
CustomDispatchConfig,
|
||||
} from 'legacy-events/ReactSyntheticEventType';
|
||||
} from '../legacy-events/ReactSyntheticEventType';
|
||||
|
||||
import * as DOMTopLevelEventTypes from './DOMTopLevelEventTypes';
|
||||
import {
|
||||
|
|
|
@ -7,15 +7,15 @@
|
|||
* @flow
|
||||
*/
|
||||
|
||||
import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
|
||||
import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
|
||||
import type {AnyNativeEvent} from '../legacy-events/PluginModuleType';
|
||||
import type {DOMTopLevelEventType} from '../legacy-events/TopLevelEventTypes';
|
||||
import type {ElementListenerMap} from '../client/ReactDOMComponentTree';
|
||||
import type {EventSystemFlags} from './EventSystemFlags';
|
||||
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
|
||||
import type {LegacyPluginModule} from 'legacy-events/PluginModuleType';
|
||||
import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
|
||||
import type {TopLevelType} from 'legacy-events/TopLevelEventTypes';
|
||||
import forEachAccumulated from 'legacy-events/forEachAccumulated';
|
||||
import type {LegacyPluginModule} from '../legacy-events/PluginModuleType';
|
||||
import type {ReactSyntheticEvent} from '../legacy-events/ReactSyntheticEventType';
|
||||
import type {TopLevelType} from '../legacy-events/TopLevelEventTypes';
|
||||
import forEachAccumulated from '../legacy-events/forEachAccumulated';
|
||||
|
||||
import {
|
||||
HostRoot,
|
||||
|
@ -23,10 +23,10 @@ import {
|
|||
HostText,
|
||||
} from 'react-reconciler/src/ReactWorkTags';
|
||||
import {IS_FIRST_ANCESTOR, PLUGIN_EVENT_SYSTEM} from './EventSystemFlags';
|
||||
import {runEventsInBatch} from 'legacy-events/EventBatching';
|
||||
import {plugins} from 'legacy-events/EventPluginRegistry';
|
||||
import accumulateInto from 'legacy-events/accumulateInto';
|
||||
import {registrationNameDependencies} from 'legacy-events/EventPluginRegistry';
|
||||
import {runEventsInBatch} from '../legacy-events/EventBatching';
|
||||
import {plugins} from '../legacy-events/EventPluginRegistry';
|
||||
import accumulateInto from '../legacy-events/accumulateInto';
|
||||
import {registrationNameDependencies} from '../legacy-events/EventPluginRegistry';
|
||||
|
||||
import getEventTarget from './getEventTarget';
|
||||
import {
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
* @flow
|
||||
*/
|
||||
|
||||
import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
|
||||
import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
|
||||
import type {AnyNativeEvent} from '../legacy-events/PluginModuleType';
|
||||
import type {DOMTopLevelEventType} from '../legacy-events/TopLevelEventTypes';
|
||||
import type {
|
||||
ElementListenerMap,
|
||||
ElementListenerMapEntry,
|
||||
|
@ -22,14 +22,14 @@ import type {
|
|||
DispatchQueueItem,
|
||||
DispatchQueueItemPhase,
|
||||
DispatchQueueItemPhaseEntry,
|
||||
} from 'legacy-events/PluginModuleType';
|
||||
} from '../legacy-events/PluginModuleType';
|
||||
import type {
|
||||
ReactSyntheticEvent,
|
||||
CustomDispatchConfig,
|
||||
} from 'legacy-events/ReactSyntheticEventType';
|
||||
} from '../legacy-events/ReactSyntheticEventType';
|
||||
|
||||
import {registrationNameDependencies} from 'legacy-events/EventPluginRegistry';
|
||||
import {plugins} from 'legacy-events/EventPluginRegistry';
|
||||
import {registrationNameDependencies} from '../legacy-events/EventPluginRegistry';
|
||||
import {plugins} from '../legacy-events/EventPluginRegistry';
|
||||
import {
|
||||
PLUGIN_EVENT_SYSTEM,
|
||||
LEGACY_FB_SUPPORT,
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
* @flow
|
||||
*/
|
||||
|
||||
import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
|
||||
import type {DOMTopLevelEventType} from '../legacy-events/TopLevelEventTypes';
|
||||
|
||||
import {
|
||||
unsafeCastStringToDOMTopLevelType,
|
||||
unsafeCastDOMTopLevelTypeToString,
|
||||
} from 'legacy-events/TopLevelEventTypes';
|
||||
} from '../legacy-events/TopLevelEventTypes';
|
||||
import getVendorPrefixedEventName from './getVendorPrefixedEventName';
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
PASSIVE_NOT_SUPPORTED,
|
||||
RESPONDER_EVENT_SYSTEM,
|
||||
} from './EventSystemFlags';
|
||||
import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
|
||||
import type {AnyNativeEvent} from '../legacy-events/PluginModuleType';
|
||||
import {
|
||||
HostComponent,
|
||||
ScopeComponent,
|
||||
|
@ -25,7 +25,7 @@ import type {
|
|||
ReactDOMResponderContext,
|
||||
ReactDOMResponderEvent,
|
||||
} from '../shared/ReactDOMTypes';
|
||||
import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
|
||||
import type {DOMTopLevelEventType} from '../legacy-events/TopLevelEventTypes';
|
||||
import {
|
||||
batchedEventUpdates,
|
||||
discreteUpdates,
|
||||
|
|
|
@ -7,11 +7,11 @@
|
|||
* @flow
|
||||
*/
|
||||
|
||||
import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
|
||||
import type {AnyNativeEvent} from '../legacy-events/PluginModuleType';
|
||||
import type {EventPriority} from 'shared/ReactTypes';
|
||||
import type {FiberRoot} from 'react-reconciler/src/ReactInternalTypes';
|
||||
import type {Container, SuspenseInstance} from '../client/ReactDOMHostConfig';
|
||||
import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
|
||||
import type {DOMTopLevelEventType} from '../legacy-events/TopLevelEventTypes';
|
||||
|
||||
// Intentionally not named imports because Rollup would use dynamic dispatch for
|
||||
// CommonJS interop named imports.
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
* @flow
|
||||
*/
|
||||
|
||||
import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
|
||||
import type {AnyNativeEvent} from '../legacy-events/PluginModuleType';
|
||||
import type {Container, SuspenseInstance} from '../client/ReactDOMHostConfig';
|
||||
import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
|
||||
import type {DOMTopLevelEventType} from '../legacy-events/TopLevelEventTypes';
|
||||
import type {ElementListenerMap} from '../client/ReactDOMComponentTree';
|
||||
import type {EventSystemFlags} from './EventSystemFlags';
|
||||
import type {FiberRoot} from 'react-reconciler/src/ReactInternalTypes';
|
||||
|
@ -36,7 +36,7 @@ import {
|
|||
getClosestInstanceFromNode,
|
||||
getEventListenerMap,
|
||||
} from '../client/ReactDOMComponentTree';
|
||||
import {unsafeCastDOMTopLevelTypeToString} from 'legacy-events/TopLevelEventTypes';
|
||||
import {unsafeCastDOMTopLevelTypeToString} from '../legacy-events/TopLevelEventTypes';
|
||||
import {HostRoot, SuspenseComponent} from 'react-reconciler/src/ReactWorkTags';
|
||||
|
||||
let attemptSynchronousHydration: (fiber: Object) => void;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import SyntheticEvent from 'legacy-events/SyntheticEvent';
|
||||
import SyntheticEvent from '../legacy-events/SyntheticEvent';
|
||||
|
||||
/**
|
||||
* @interface Event
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import SyntheticEvent from 'legacy-events/SyntheticEvent';
|
||||
import SyntheticEvent from '../legacy-events/SyntheticEvent';
|
||||
|
||||
/**
|
||||
* @interface Event
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import SyntheticEvent from 'legacy-events/SyntheticEvent';
|
||||
import SyntheticEvent from '../legacy-events/SyntheticEvent';
|
||||
|
||||
/**
|
||||
* @interface Event
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import SyntheticEvent from 'legacy-events/SyntheticEvent';
|
||||
import SyntheticEvent from '../legacy-events/SyntheticEvent';
|
||||
|
||||
/**
|
||||
* @interface Event
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import SyntheticEvent from 'legacy-events/SyntheticEvent';
|
||||
import SyntheticEvent from '../legacy-events/SyntheticEvent';
|
||||
|
||||
/**
|
||||
* @interface Event
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import SyntheticEvent from 'legacy-events/SyntheticEvent';
|
||||
import SyntheticEvent from '../legacy-events/SyntheticEvent';
|
||||
|
||||
const SyntheticUIEvent = SyntheticEvent.extend({
|
||||
view: null,
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
* @see http://www.w3.org/TR/DOM-Level-3-Events/#keys-Modifiers
|
||||
*/
|
||||
|
||||
import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
|
||||
import type {AnyNativeEvent} from '../legacy-events/PluginModuleType';
|
||||
|
||||
const modifierKeyToProp = {
|
||||
Alt: 'altKey',
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import type {TopLevelType} from 'legacy-events/TopLevelEventTypes';
|
||||
import type {TopLevelType} from '../../legacy-events/TopLevelEventTypes';
|
||||
|
||||
import {canUseDOM} from 'shared/ExecutionEnvironment';
|
||||
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {runEventsInBatch} from 'legacy-events/EventBatching';
|
||||
import SyntheticEvent from 'legacy-events/SyntheticEvent';
|
||||
import {runEventsInBatch} from '../../legacy-events/EventBatching';
|
||||
import SyntheticEvent from '../../legacy-events/SyntheticEvent';
|
||||
import isTextInputElement from '../isTextInputElement';
|
||||
import {canUseDOM} from 'shared/ExecutionEnvironment';
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import {canUseDOM} from 'shared/ExecutionEnvironment';
|
||||
import SyntheticEvent from 'legacy-events/SyntheticEvent';
|
||||
import SyntheticEvent from '../../legacy-events/SyntheticEvent';
|
||||
import isTextInputElement from '../isTextInputElement';
|
||||
import shallowEqual from 'shared/shallowEqual';
|
||||
|
||||
|
@ -30,7 +30,7 @@ import {hasSelectionCapabilities} from '../../client/ReactInputSelection';
|
|||
import {DOCUMENT_NODE} from '../../shared/HTMLNodeType';
|
||||
import {accumulateTwoPhaseDispatches} from '../DOMLegacyEventPluginSystem';
|
||||
|
||||
import {registrationNameDependencies} from 'legacy-events/EventPluginRegistry';
|
||||
import {registrationNameDependencies} from '../../legacy-events/EventPluginRegistry';
|
||||
|
||||
const skipSelectionChangeEvent =
|
||||
canUseDOM && 'documentMode' in document && document.documentMode <= 11;
|
||||
|
|
|
@ -10,12 +10,12 @@
|
|||
import type {
|
||||
TopLevelType,
|
||||
DOMTopLevelEventType,
|
||||
} from 'legacy-events/TopLevelEventTypes';
|
||||
import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
|
||||
} from '../../legacy-events/TopLevelEventTypes';
|
||||
import type {ReactSyntheticEvent} from '../../legacy-events/ReactSyntheticEventType';
|
||||
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
|
||||
import type {LegacyPluginModule} from 'legacy-events/PluginModuleType';
|
||||
import type {LegacyPluginModule} from '../../legacy-events/PluginModuleType';
|
||||
|
||||
import SyntheticEvent from 'legacy-events/SyntheticEvent';
|
||||
import SyntheticEvent from '../../legacy-events/SyntheticEvent';
|
||||
|
||||
import * as DOMTopLevelEventTypes from '../DOMTopLevelEventTypes';
|
||||
import {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import type {TopLevelType} from 'legacy-events/TopLevelEventTypes';
|
||||
import type {TopLevelType} from '../../legacy-events/TopLevelEventTypes';
|
||||
|
||||
import {canUseDOM} from 'shared/ExecutionEnvironment';
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import SyntheticEvent from 'legacy-events/SyntheticEvent';
|
||||
import SyntheticEvent from '../../legacy-events/SyntheticEvent';
|
||||
import isTextInputElement from '../isTextInputElement';
|
||||
import {canUseDOM} from 'shared/ExecutionEnvironment';
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import {canUseDOM} from 'shared/ExecutionEnvironment';
|
||||
import SyntheticEvent from 'legacy-events/SyntheticEvent';
|
||||
import SyntheticEvent from '../../legacy-events/SyntheticEvent';
|
||||
import isTextInputElement from '../isTextInputElement';
|
||||
import shallowEqual from 'shared/shallowEqual';
|
||||
|
||||
|
|
|
@ -10,15 +10,15 @@
|
|||
import type {
|
||||
TopLevelType,
|
||||
DOMTopLevelEventType,
|
||||
} from 'legacy-events/TopLevelEventTypes';
|
||||
} from '../../legacy-events/TopLevelEventTypes';
|
||||
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
|
||||
import type {
|
||||
ModernPluginModule,
|
||||
DispatchQueue,
|
||||
} from 'legacy-events/PluginModuleType';
|
||||
} from '../../legacy-events/PluginModuleType';
|
||||
import type {EventSystemFlags} from '../EventSystemFlags';
|
||||
|
||||
import SyntheticEvent from 'legacy-events/SyntheticEvent';
|
||||
import SyntheticEvent from '../../legacy-events/SyntheticEvent';
|
||||
|
||||
import * as DOMTopLevelEventTypes from '../DOMTopLevelEventTypes';
|
||||
import {
|
||||
|
|
|
@ -14,7 +14,7 @@ import type {
|
|||
EventPriority,
|
||||
ReactScopeInstance,
|
||||
} from 'shared/ReactTypes';
|
||||
import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
|
||||
import type {DOMTopLevelEventType} from '../legacy-events/TopLevelEventTypes';
|
||||
|
||||
type AnyNativeEvent = Event | KeyboardEvent | MouseEvent | Touch;
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import {
|
||||
registrationNameModules,
|
||||
possibleRegistrationNames,
|
||||
} from 'legacy-events/EventPluginRegistry';
|
||||
} from '../legacy-events/EventPluginRegistry';
|
||||
|
||||
import {
|
||||
ATTRIBUTE_NAME_CHAR,
|
||||
|
|
|
@ -15,13 +15,13 @@ import {
|
|||
HostComponent,
|
||||
HostText,
|
||||
} from 'react-reconciler/src/ReactWorkTags';
|
||||
import SyntheticEvent from 'legacy-events/SyntheticEvent';
|
||||
import SyntheticEvent from '../legacy-events/SyntheticEvent';
|
||||
import invariant from 'shared/invariant';
|
||||
import {ELEMENT_NODE} from '../shared/HTMLNodeType';
|
||||
import * as DOMTopLevelEventTypes from '../events/DOMTopLevelEventTypes';
|
||||
import act from './ReactTestUtilsAct';
|
||||
import forEachAccumulated from 'legacy-events/forEachAccumulated';
|
||||
import accumulateInto from 'legacy-events/accumulateInto';
|
||||
import forEachAccumulated from '../legacy-events/forEachAccumulated';
|
||||
import accumulateInto from '../legacy-events/accumulateInto';
|
||||
import {enableModernEventSystem} from 'shared/ReactFeatureFlags';
|
||||
import {
|
||||
rethrowCaughtError,
|
||||
|
|
|
@ -27,7 +27,7 @@ import {
|
|||
} from 'react-reconciler/src/ReactFiberReconciler';
|
||||
|
||||
import {createPortal as createPortalImpl} from 'react-reconciler/src/ReactPortal';
|
||||
import {setBatchingImplementation} from 'legacy-events/ReactGenericBatching';
|
||||
import {setBatchingImplementation} from './legacy-events/ReactGenericBatching';
|
||||
import ReactVersion from 'shared/ReactVersion';
|
||||
|
||||
// Module provided by RN:
|
||||
|
|
|
@ -7,19 +7,19 @@
|
|||
* @flow
|
||||
*/
|
||||
|
||||
import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
|
||||
import type {AnyNativeEvent} from './legacy-events/PluginModuleType';
|
||||
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
|
||||
import type {LegacyPluginModule} from 'legacy-events/PluginModuleType';
|
||||
import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
|
||||
import type {TopLevelType} from 'legacy-events/TopLevelEventTypes';
|
||||
import type {LegacyPluginModule} from './legacy-events/PluginModuleType';
|
||||
import type {ReactSyntheticEvent} from './legacy-events/ReactSyntheticEventType';
|
||||
import type {TopLevelType} from './legacy-events/TopLevelEventTypes';
|
||||
|
||||
import {registrationNameModules} from 'legacy-events/EventPluginRegistry';
|
||||
import {batchedUpdates} from 'legacy-events/ReactGenericBatching';
|
||||
import accumulateInto from 'legacy-events/accumulateInto';
|
||||
import {registrationNameModules} from './legacy-events/EventPluginRegistry';
|
||||
import {batchedUpdates} from './legacy-events/ReactGenericBatching';
|
||||
import accumulateInto from './legacy-events/accumulateInto';
|
||||
|
||||
import {plugins} from 'legacy-events/EventPluginRegistry';
|
||||
import {plugins} from './legacy-events/EventPluginRegistry';
|
||||
import getListener from './ReactNativeGetListener';
|
||||
import {runEventsInBatch} from 'legacy-events/EventBatching';
|
||||
import {runEventsInBatch} from './legacy-events/EventBatching';
|
||||
|
||||
export {getListener, registrationNameModules as registrationNames};
|
||||
|
||||
|
|
|
@ -14,9 +14,9 @@ import {
|
|||
getInstanceFromNode,
|
||||
getNodeFromInstance,
|
||||
} from './ReactFabricComponentTree';
|
||||
import {setComponentTree} from 'legacy-events/EventPluginUtils';
|
||||
import {setComponentTree} from './legacy-events/EventPluginUtils';
|
||||
import ReactFabricGlobalResponderHandler from './ReactFabricGlobalResponderHandler';
|
||||
import ResponderEventPlugin from 'legacy-events/ResponderEventPlugin';
|
||||
import ResponderEventPlugin from './legacy-events/ResponderEventPlugin';
|
||||
|
||||
setComponentTree(
|
||||
getFiberCurrentPropsFromNode,
|
||||
|
|
|
@ -7,16 +7,16 @@
|
|||
* @flow
|
||||
*/
|
||||
|
||||
import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
|
||||
import type {TopLevelType} from 'legacy-events/TopLevelEventTypes';
|
||||
import SyntheticEvent from 'legacy-events/SyntheticEvent';
|
||||
import type {AnyNativeEvent} from './legacy-events/PluginModuleType';
|
||||
import type {TopLevelType} from './legacy-events/TopLevelEventTypes';
|
||||
import SyntheticEvent from './legacy-events/SyntheticEvent';
|
||||
import invariant from 'shared/invariant';
|
||||
|
||||
// Module provided by RN:
|
||||
import {ReactNativeViewConfigRegistry} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
|
||||
import accumulateInto from 'legacy-events/accumulateInto';
|
||||
import accumulateInto from './legacy-events/accumulateInto';
|
||||
import getListener from './ReactNativeGetListener';
|
||||
import forEachAccumulated from 'legacy-events/forEachAccumulated';
|
||||
import forEachAccumulated from './legacy-events/forEachAccumulated';
|
||||
import {HostComponent} from 'react-reconciler/src/ReactWorkTags';
|
||||
|
||||
const {
|
||||
|
|
|
@ -7,18 +7,18 @@
|
|||
* @flow
|
||||
*/
|
||||
|
||||
import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
|
||||
import type {AnyNativeEvent} from './legacy-events/PluginModuleType';
|
||||
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
|
||||
import type {LegacyPluginModule} from 'legacy-events/PluginModuleType';
|
||||
import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
|
||||
import type {TopLevelType} from 'legacy-events/TopLevelEventTypes';
|
||||
import type {LegacyPluginModule} from './legacy-events/PluginModuleType';
|
||||
import type {ReactSyntheticEvent} from './legacy-events/ReactSyntheticEventType';
|
||||
import type {TopLevelType} from './legacy-events/TopLevelEventTypes';
|
||||
|
||||
import {registrationNameModules} from 'legacy-events/EventPluginRegistry';
|
||||
import {batchedUpdates} from 'legacy-events/ReactGenericBatching';
|
||||
import {runEventsInBatch} from 'legacy-events/EventBatching';
|
||||
import {plugins} from 'legacy-events/EventPluginRegistry';
|
||||
import {registrationNameModules} from './legacy-events/EventPluginRegistry';
|
||||
import {batchedUpdates} from './legacy-events/ReactGenericBatching';
|
||||
import {runEventsInBatch} from './legacy-events/EventBatching';
|
||||
import {plugins} from './legacy-events/EventPluginRegistry';
|
||||
import getListener from './ReactNativeGetListener';
|
||||
import accumulateInto from 'legacy-events/accumulateInto';
|
||||
import accumulateInto from './legacy-events/accumulateInto';
|
||||
|
||||
import {getInstanceFromNode} from './ReactNativeComponentTree';
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
|
||||
|
||||
import invariant from 'shared/invariant';
|
||||
import {getFiberCurrentPropsFromNode} from 'legacy-events/EventPluginUtils';
|
||||
import {getFiberCurrentPropsFromNode} from './legacy-events/EventPluginUtils';
|
||||
|
||||
export default function getListener(
|
||||
inst: Fiber,
|
||||
|
|
|
@ -14,10 +14,10 @@ import {
|
|||
getInstanceFromNode,
|
||||
getNodeFromInstance,
|
||||
} from './ReactNativeComponentTree';
|
||||
import {setComponentTree} from 'legacy-events/EventPluginUtils';
|
||||
import {setComponentTree} from './legacy-events/EventPluginUtils';
|
||||
import {receiveEvent, receiveTouches} from './ReactNativeEventEmitter';
|
||||
import ReactNativeGlobalResponderHandler from './ReactNativeGlobalResponderHandler';
|
||||
import ResponderEventPlugin from 'legacy-events/ResponderEventPlugin';
|
||||
import ResponderEventPlugin from './legacy-events/ResponderEventPlugin';
|
||||
|
||||
// Module provided by RN:
|
||||
import {RCTEventEmitter} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
|
||||
|
|
|
@ -16,11 +16,11 @@
|
|||
// Module provided by RN:
|
||||
import 'react-native/Libraries/ReactPrivate/ReactNativePrivateInitializeCore';
|
||||
|
||||
import ResponderEventPlugin from 'legacy-events/ResponderEventPlugin';
|
||||
import ResponderEventPlugin from './legacy-events/ResponderEventPlugin';
|
||||
import {
|
||||
injectEventPluginOrder,
|
||||
injectEventPluginsByName,
|
||||
} from 'legacy-events/EventPluginRegistry';
|
||||
} from './legacy-events/EventPluginRegistry';
|
||||
|
||||
import ReactNativeBridgeEventPlugin from './ReactNativeBridgeEventPlugin';
|
||||
import ReactNativeEventPluginOrder from './ReactNativeEventPluginOrder';
|
||||
|
|
|
@ -30,7 +30,7 @@ import {createPortal as createPortalImpl} from 'react-reconciler/src/ReactPortal
|
|||
import {
|
||||
setBatchingImplementation,
|
||||
batchedUpdates,
|
||||
} from 'legacy-events/ReactGenericBatching';
|
||||
} from './legacy-events/ReactGenericBatching';
|
||||
import ReactVersion from 'shared/ReactVersion';
|
||||
// Module provided by RN:
|
||||
import {UIManager} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
|
||||
|
|
|
@ -19,7 +19,7 @@ describe('EventPluginRegistry', () => {
|
|||
// The public API surface of this is covered by other tests so
|
||||
// if `EventPluginRegistry` is ever deleted, these tests should be
|
||||
// safe to remove too.
|
||||
EventPluginRegistry = require('legacy-events/EventPluginRegistry');
|
||||
EventPluginRegistry = require('react-native-renderer/src/legacy-events/EventPluginRegistry');
|
||||
|
||||
createPlugin = function(properties) {
|
||||
return Object.assign({extractEvents: function() {}}, properties);
|
||||
|
|
|
@ -68,7 +68,8 @@ beforeEach(() => {
|
|||
.RCTEventEmitter;
|
||||
React = require('react');
|
||||
ReactNative = require('react-native-renderer');
|
||||
ResponderEventPlugin = require('legacy-events/ResponderEventPlugin').default;
|
||||
ResponderEventPlugin = require('react-native-renderer/src/legacy-events/ResponderEventPlugin')
|
||||
.default;
|
||||
UIManager = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface')
|
||||
.UIManager;
|
||||
createReactNativeComponentClass = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface')
|
||||
|
|
|
@ -407,9 +407,9 @@ describe('ResponderEventPlugin', () => {
|
|||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
|
||||
EventBatching = require('legacy-events/EventBatching');
|
||||
EventPluginUtils = require('legacy-events/EventPluginUtils');
|
||||
ResponderEventPlugin = require('legacy-events/ResponderEventPlugin')
|
||||
EventBatching = require('react-native-renderer/src/legacy-events/EventBatching');
|
||||
EventPluginUtils = require('react-native-renderer/src/legacy-events/EventPluginUtils');
|
||||
ResponderEventPlugin = require('react-native-renderer/src/legacy-events/ResponderEventPlugin')
|
||||
.default;
|
||||
|
||||
deleteAllListeners(GRANDPARENT_INST);
|
||||
|
@ -1380,8 +1380,9 @@ describe('ResponderEventPlugin', () => {
|
|||
// ResponderEventPlugin uses `getLowestCommonAncestor`
|
||||
const React = require('react');
|
||||
const ReactTestUtils = require('react-dom/test-utils');
|
||||
const getLowestCommonAncestor = require('legacy-events/ResponderEventPlugin')
|
||||
const getLowestCommonAncestor = require('react-native-renderer/src/legacy-events/ResponderEventPlugin')
|
||||
.getLowestCommonAncestor;
|
||||
// This works by accident and will likely break in the future.
|
||||
const ReactDOMComponentTree = require('react-dom/src/client/ReactDOMComponentTree');
|
||||
|
||||
class ChildComponent extends React.Component {
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import invariant from 'shared/invariant';
|
||||
import {rethrowCaughtError} from 'shared/ReactErrorUtils';
|
||||
|
||||
import type {ReactSyntheticEvent} from './ReactSyntheticEventType';
|
||||
import accumulateInto from './accumulateInto';
|
||||
import forEachAccumulated from './forEachAccumulated';
|
||||
import {executeDispatchesInOrder} from './EventPluginUtils';
|
||||
|
||||
/**
|
||||
* Internal queue of events that have accumulated their dispatches and are
|
||||
* waiting to have their dispatches executed.
|
||||
*/
|
||||
let eventQueue: ?(Array<ReactSyntheticEvent> | ReactSyntheticEvent) = null;
|
||||
|
||||
/**
|
||||
* Dispatches an event and releases it back into the pool, unless persistent.
|
||||
*
|
||||
* @param {?object} event Synthetic event to be dispatched.
|
||||
* @private
|
||||
*/
|
||||
const executeDispatchesAndRelease = function(event: ReactSyntheticEvent) {
|
||||
if (event) {
|
||||
executeDispatchesInOrder(event);
|
||||
|
||||
if (!event.isPersistent()) {
|
||||
event.constructor.release(event);
|
||||
}
|
||||
}
|
||||
};
|
||||
const executeDispatchesAndReleaseTopLevel = function(e) {
|
||||
return executeDispatchesAndRelease(e);
|
||||
};
|
||||
|
||||
export function runEventsInBatch(
|
||||
events: Array<ReactSyntheticEvent> | ReactSyntheticEvent | null,
|
||||
) {
|
||||
if (events !== null) {
|
||||
eventQueue = accumulateInto(eventQueue, events);
|
||||
}
|
||||
|
||||
// Set `eventQueue` to null before processing it so that we can tell if more
|
||||
// events get enqueued while processing.
|
||||
const processingEventQueue = eventQueue;
|
||||
eventQueue = null;
|
||||
|
||||
if (!processingEventQueue) {
|
||||
return;
|
||||
}
|
||||
|
||||
forEachAccumulated(processingEventQueue, executeDispatchesAndReleaseTopLevel);
|
||||
invariant(
|
||||
!eventQueue,
|
||||
'processEventQueue(): Additional events were enqueued while processing ' +
|
||||
'an event queue. Support for this has not yet been implemented.',
|
||||
);
|
||||
// This would be a good time to rethrow if any of the event handlers threw.
|
||||
rethrowCaughtError();
|
||||
}
|
|
@ -0,0 +1,270 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import type {DispatchConfig} from './ReactSyntheticEventType';
|
||||
import type {
|
||||
AnyNativeEvent,
|
||||
PluginName,
|
||||
LegacyPluginModule,
|
||||
ModernPluginModule,
|
||||
} from './PluginModuleType';
|
||||
|
||||
import invariant from 'shared/invariant';
|
||||
|
||||
type NamesToPlugins = {
|
||||
[key: PluginName]:
|
||||
| LegacyPluginModule<AnyNativeEvent>
|
||||
| ModernPluginModule<AnyNativeEvent>,
|
||||
...,
|
||||
};
|
||||
type EventPluginOrder = null | Array<PluginName>;
|
||||
|
||||
/**
|
||||
* Injectable ordering of event plugins.
|
||||
*/
|
||||
let eventPluginOrder: EventPluginOrder = null;
|
||||
|
||||
/**
|
||||
* Injectable mapping from names to event plugin modules.
|
||||
*/
|
||||
const namesToPlugins: NamesToPlugins = {};
|
||||
|
||||
/**
|
||||
* Recomputes the plugin list using the injected plugins and plugin ordering.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function recomputePluginOrdering(): void {
|
||||
if (!eventPluginOrder) {
|
||||
// Wait until an `eventPluginOrder` is injected.
|
||||
return;
|
||||
}
|
||||
for (const pluginName in namesToPlugins) {
|
||||
const pluginModule = namesToPlugins[pluginName];
|
||||
const pluginIndex = eventPluginOrder.indexOf(pluginName);
|
||||
invariant(
|
||||
pluginIndex > -1,
|
||||
'EventPluginRegistry: Cannot inject event plugins that do not exist in ' +
|
||||
'the plugin ordering, `%s`.',
|
||||
pluginName,
|
||||
);
|
||||
if (plugins[pluginIndex]) {
|
||||
continue;
|
||||
}
|
||||
invariant(
|
||||
pluginModule.extractEvents,
|
||||
'EventPluginRegistry: Event plugins must implement an `extractEvents` ' +
|
||||
'method, but `%s` does not.',
|
||||
pluginName,
|
||||
);
|
||||
plugins[pluginIndex] = pluginModule;
|
||||
const publishedEvents = pluginModule.eventTypes;
|
||||
for (const eventName in publishedEvents) {
|
||||
invariant(
|
||||
publishEventForPlugin(
|
||||
publishedEvents[eventName],
|
||||
pluginModule,
|
||||
eventName,
|
||||
),
|
||||
'EventPluginRegistry: Failed to publish event `%s` for plugin `%s`.',
|
||||
eventName,
|
||||
pluginName,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes an event so that it can be dispatched by the supplied plugin.
|
||||
*
|
||||
* @param {object} dispatchConfig Dispatch configuration for the event.
|
||||
* @param {object} PluginModule Plugin publishing the event.
|
||||
* @return {boolean} True if the event was successfully published.
|
||||
* @private
|
||||
*/
|
||||
function publishEventForPlugin(
|
||||
dispatchConfig: DispatchConfig,
|
||||
pluginModule:
|
||||
| LegacyPluginModule<AnyNativeEvent>
|
||||
| ModernPluginModule<AnyNativeEvent>,
|
||||
eventName: string,
|
||||
): boolean {
|
||||
invariant(
|
||||
!eventNameDispatchConfigs.hasOwnProperty(eventName),
|
||||
'EventPluginRegistry: More than one plugin attempted to publish the same ' +
|
||||
'event name, `%s`.',
|
||||
eventName,
|
||||
);
|
||||
eventNameDispatchConfigs[eventName] = dispatchConfig;
|
||||
|
||||
const phasedRegistrationNames = dispatchConfig.phasedRegistrationNames;
|
||||
if (phasedRegistrationNames) {
|
||||
for (const phaseName in phasedRegistrationNames) {
|
||||
if (phasedRegistrationNames.hasOwnProperty(phaseName)) {
|
||||
const phasedRegistrationName = phasedRegistrationNames[phaseName];
|
||||
publishRegistrationName(
|
||||
phasedRegistrationName,
|
||||
pluginModule,
|
||||
eventName,
|
||||
);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else if (dispatchConfig.registrationName) {
|
||||
publishRegistrationName(
|
||||
dispatchConfig.registrationName,
|
||||
pluginModule,
|
||||
eventName,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes a registration name that is used to identify dispatched events.
|
||||
*
|
||||
* @param {string} registrationName Registration name to add.
|
||||
* @param {object} PluginModule Plugin publishing the event.
|
||||
* @private
|
||||
*/
|
||||
function publishRegistrationName(
|
||||
registrationName: string,
|
||||
pluginModule:
|
||||
| LegacyPluginModule<AnyNativeEvent>
|
||||
| ModernPluginModule<AnyNativeEvent>,
|
||||
eventName: string,
|
||||
): void {
|
||||
invariant(
|
||||
!registrationNameModules[registrationName],
|
||||
'EventPluginRegistry: More than one plugin attempted to publish the same ' +
|
||||
'registration name, `%s`.',
|
||||
registrationName,
|
||||
);
|
||||
registrationNameModules[registrationName] = pluginModule;
|
||||
registrationNameDependencies[registrationName] =
|
||||
pluginModule.eventTypes[eventName].dependencies;
|
||||
|
||||
if (__DEV__) {
|
||||
const lowerCasedName = registrationName.toLowerCase();
|
||||
possibleRegistrationNames[lowerCasedName] = registrationName;
|
||||
|
||||
if (registrationName === 'onDoubleClick') {
|
||||
possibleRegistrationNames.ondblclick = registrationName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers plugins so that they can extract and dispatch events.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Ordered list of injected plugins.
|
||||
*/
|
||||
export const plugins = [];
|
||||
|
||||
/**
|
||||
* Mapping from event name to dispatch config
|
||||
*/
|
||||
export const eventNameDispatchConfigs = {};
|
||||
|
||||
/**
|
||||
* Mapping from registration name to plugin module
|
||||
*/
|
||||
export const registrationNameModules = {};
|
||||
|
||||
/**
|
||||
* Mapping from registration name to event name
|
||||
*/
|
||||
export const registrationNameDependencies = {};
|
||||
|
||||
/**
|
||||
* Mapping from lowercase registration names to the properly cased version,
|
||||
* used to warn in the case of missing event handlers. Available
|
||||
* only in __DEV__.
|
||||
* @type {Object}
|
||||
*/
|
||||
export const possibleRegistrationNames = __DEV__ ? {} : (null: any);
|
||||
// Trust the developer to only use possibleRegistrationNames in __DEV__
|
||||
|
||||
/**
|
||||
* Injects an ordering of plugins (by plugin name). This allows the ordering
|
||||
* to be decoupled from injection of the actual plugins so that ordering is
|
||||
* always deterministic regardless of packaging, on-the-fly injection, etc.
|
||||
*
|
||||
* @param {array} InjectedEventPluginOrder
|
||||
* @internal
|
||||
*/
|
||||
export function injectEventPluginOrder(
|
||||
injectedEventPluginOrder: EventPluginOrder,
|
||||
): void {
|
||||
invariant(
|
||||
!eventPluginOrder,
|
||||
'EventPluginRegistry: Cannot inject event plugin ordering more than ' +
|
||||
'once. You are likely trying to load more than one copy of React.',
|
||||
);
|
||||
// Clone the ordering so it cannot be dynamically mutated.
|
||||
eventPluginOrder = Array.prototype.slice.call(injectedEventPluginOrder);
|
||||
recomputePluginOrdering();
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects plugins to be used by plugin event system. The plugin names must be
|
||||
* in the ordering injected by `injectEventPluginOrder`.
|
||||
*
|
||||
* Plugins can be injected as part of page initialization or on-the-fly.
|
||||
*
|
||||
* @param {object} injectedNamesToPlugins Map from names to plugin modules.
|
||||
* @internal
|
||||
*/
|
||||
export function injectEventPluginsByName(
|
||||
injectedNamesToPlugins: NamesToPlugins,
|
||||
): void {
|
||||
let isOrderingDirty = false;
|
||||
for (const pluginName in injectedNamesToPlugins) {
|
||||
if (!injectedNamesToPlugins.hasOwnProperty(pluginName)) {
|
||||
continue;
|
||||
}
|
||||
const pluginModule = injectedNamesToPlugins[pluginName];
|
||||
if (
|
||||
!namesToPlugins.hasOwnProperty(pluginName) ||
|
||||
namesToPlugins[pluginName] !== pluginModule
|
||||
) {
|
||||
invariant(
|
||||
!namesToPlugins[pluginName],
|
||||
'EventPluginRegistry: Cannot inject two different event plugins ' +
|
||||
'using the same name, `%s`.',
|
||||
pluginName,
|
||||
);
|
||||
namesToPlugins[pluginName] = pluginModule;
|
||||
isOrderingDirty = true;
|
||||
}
|
||||
}
|
||||
if (isOrderingDirty) {
|
||||
recomputePluginOrdering();
|
||||
}
|
||||
}
|
||||
|
||||
export function injectEventPlugins(
|
||||
eventPlugins: [ModernPluginModule<AnyNativeEvent>],
|
||||
): void {
|
||||
for (let i = 0; i < eventPlugins.length; i++) {
|
||||
const pluginModule = eventPlugins[i];
|
||||
plugins.push(pluginModule);
|
||||
const publishedEvents = pluginModule.eventTypes;
|
||||
for (const eventName in publishedEvents) {
|
||||
publishEventForPlugin(
|
||||
publishedEvents[eventName],
|
||||
pluginModule,
|
||||
eventName,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {invokeGuardedCallbackAndCatchFirstError} from 'shared/ReactErrorUtils';
|
||||
import invariant from 'shared/invariant';
|
||||
|
||||
export let getFiberCurrentPropsFromNode = null;
|
||||
export let getInstanceFromNode = null;
|
||||
export let getNodeFromInstance = null;
|
||||
|
||||
export function setComponentTree(
|
||||
getFiberCurrentPropsFromNodeImpl,
|
||||
getInstanceFromNodeImpl,
|
||||
getNodeFromInstanceImpl,
|
||||
) {
|
||||
getFiberCurrentPropsFromNode = getFiberCurrentPropsFromNodeImpl;
|
||||
getInstanceFromNode = getInstanceFromNodeImpl;
|
||||
getNodeFromInstance = getNodeFromInstanceImpl;
|
||||
if (__DEV__) {
|
||||
if (!getNodeFromInstance || !getInstanceFromNode) {
|
||||
console.error(
|
||||
'EventPluginUtils.setComponentTree(...): Injected ' +
|
||||
'module is missing getNodeFromInstance or getInstanceFromNode.',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let validateEventDispatches;
|
||||
if (__DEV__) {
|
||||
validateEventDispatches = function(event) {
|
||||
const dispatchListeners = event._dispatchListeners;
|
||||
const dispatchInstances = event._dispatchInstances;
|
||||
|
||||
const listenersIsArr = Array.isArray(dispatchListeners);
|
||||
const listenersLen = listenersIsArr
|
||||
? dispatchListeners.length
|
||||
: dispatchListeners
|
||||
? 1
|
||||
: 0;
|
||||
|
||||
const instancesIsArr = Array.isArray(dispatchInstances);
|
||||
const instancesLen = instancesIsArr
|
||||
? dispatchInstances.length
|
||||
: dispatchInstances
|
||||
? 1
|
||||
: 0;
|
||||
|
||||
if (instancesIsArr !== listenersIsArr || instancesLen !== listenersLen) {
|
||||
console.error('EventPluginUtils: Invalid `event`.');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch the event to the listener.
|
||||
* @param {SyntheticEvent} event SyntheticEvent to handle
|
||||
* @param {function} listener Application-level callback
|
||||
* @param {*} inst Internal component instance
|
||||
*/
|
||||
export function executeDispatch(event, listener, inst) {
|
||||
const type = event.type || 'unknown-event';
|
||||
event.currentTarget = getNodeFromInstance(inst);
|
||||
invokeGuardedCallbackAndCatchFirstError(type, listener, undefined, event);
|
||||
event.currentTarget = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard/simple iteration through an event's collected dispatches.
|
||||
*/
|
||||
export function executeDispatchesInOrder(event) {
|
||||
const dispatchListeners = event._dispatchListeners;
|
||||
const dispatchInstances = event._dispatchInstances;
|
||||
if (__DEV__) {
|
||||
validateEventDispatches(event);
|
||||
}
|
||||
if (Array.isArray(dispatchListeners)) {
|
||||
for (let i = 0; i < dispatchListeners.length; i++) {
|
||||
if (event.isPropagationStopped()) {
|
||||
break;
|
||||
}
|
||||
// Listeners and Instances are two parallel arrays that are always in sync.
|
||||
executeDispatch(event, dispatchListeners[i], dispatchInstances[i]);
|
||||
}
|
||||
} else if (dispatchListeners) {
|
||||
executeDispatch(event, dispatchListeners, dispatchInstances);
|
||||
}
|
||||
event._dispatchListeners = null;
|
||||
event._dispatchInstances = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard/simple iteration through an event's collected dispatches, but stops
|
||||
* at the first dispatch execution returning true, and returns that id.
|
||||
*
|
||||
* @return {?string} id of the first dispatch execution who's listener returns
|
||||
* true, or null if no listener returned true.
|
||||
*/
|
||||
function executeDispatchesInOrderStopAtTrueImpl(event) {
|
||||
const dispatchListeners = event._dispatchListeners;
|
||||
const dispatchInstances = event._dispatchInstances;
|
||||
if (__DEV__) {
|
||||
validateEventDispatches(event);
|
||||
}
|
||||
if (Array.isArray(dispatchListeners)) {
|
||||
for (let i = 0; i < dispatchListeners.length; i++) {
|
||||
if (event.isPropagationStopped()) {
|
||||
break;
|
||||
}
|
||||
// Listeners and Instances are two parallel arrays that are always in sync.
|
||||
if (dispatchListeners[i](event, dispatchInstances[i])) {
|
||||
return dispatchInstances[i];
|
||||
}
|
||||
}
|
||||
} else if (dispatchListeners) {
|
||||
if (dispatchListeners(event, dispatchInstances)) {
|
||||
return dispatchInstances;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see executeDispatchesInOrderStopAtTrueImpl
|
||||
*/
|
||||
export function executeDispatchesInOrderStopAtTrue(event) {
|
||||
const ret = executeDispatchesInOrderStopAtTrueImpl(event);
|
||||
event._dispatchInstances = null;
|
||||
event._dispatchListeners = null;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execution of a "direct" dispatch - there must be at most one dispatch
|
||||
* accumulated on the event or it is considered an error. It doesn't really make
|
||||
* sense for an event with multiple dispatches (bubbled) to keep track of the
|
||||
* return values at each dispatch execution, but it does tend to make sense when
|
||||
* dealing with "direct" dispatches.
|
||||
*
|
||||
* @return {*} The return value of executing the single dispatch.
|
||||
*/
|
||||
export function executeDirectDispatch(event) {
|
||||
if (__DEV__) {
|
||||
validateEventDispatches(event);
|
||||
}
|
||||
const dispatchListener = event._dispatchListeners;
|
||||
const dispatchInstance = event._dispatchInstances;
|
||||
invariant(
|
||||
!Array.isArray(dispatchListener),
|
||||
'executeDirectDispatch(...): Invalid `event`.',
|
||||
);
|
||||
event.currentTarget = dispatchListener
|
||||
? getNodeFromInstance(dispatchInstance)
|
||||
: null;
|
||||
const res = dispatchListener ? dispatchListener(event) : null;
|
||||
event.currentTarget = null;
|
||||
event._dispatchListeners = null;
|
||||
event._dispatchInstances = null;
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {SyntheticEvent} event
|
||||
* @return {boolean} True iff number of dispatches accumulated is greater than 0.
|
||||
*/
|
||||
export function hasDispatches(event) {
|
||||
return !!event._dispatchListeners;
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
|
||||
import type {
|
||||
DispatchConfig,
|
||||
ReactSyntheticEvent,
|
||||
} from './ReactSyntheticEventType';
|
||||
import type {TopLevelType} from './TopLevelEventTypes';
|
||||
|
||||
export type EventTypes = {[key: string]: DispatchConfig, ...};
|
||||
|
||||
export type AnyNativeEvent = Event | KeyboardEvent | MouseEvent | TouchEvent;
|
||||
|
||||
export type PluginName = string;
|
||||
|
||||
export type EventSystemFlags = number;
|
||||
|
||||
export type LegacyPluginModule<NativeEvent> = {
|
||||
eventTypes: EventTypes,
|
||||
extractEvents: (
|
||||
topLevelType: TopLevelType,
|
||||
targetInst: null | Fiber,
|
||||
nativeTarget: NativeEvent,
|
||||
nativeEventTarget: null | EventTarget,
|
||||
eventSystemFlags?: number,
|
||||
container?: null | EventTarget,
|
||||
) => ?ReactSyntheticEvent,
|
||||
tapMoveThreshold?: number,
|
||||
};
|
||||
|
||||
export type DispatchQueueItemPhaseEntry = {|
|
||||
instance: null | Fiber,
|
||||
listener: Function,
|
||||
currentTarget: EventTarget,
|
||||
|};
|
||||
|
||||
export type DispatchQueueItemPhase = Array<DispatchQueueItemPhaseEntry>;
|
||||
|
||||
export type DispatchQueueItem = {|
|
||||
event: ReactSyntheticEvent,
|
||||
capture: DispatchQueueItemPhase,
|
||||
bubble: DispatchQueueItemPhase,
|
||||
|};
|
||||
|
||||
export type DispatchQueue = Array<DispatchQueueItem>;
|
||||
|
||||
export type ModernPluginModule<NativeEvent> = {
|
||||
eventTypes: EventTypes,
|
||||
extractEvents: (
|
||||
dispatchQueue: DispatchQueue,
|
||||
topLevelType: TopLevelType,
|
||||
targetInst: null | Fiber,
|
||||
nativeTarget: NativeEvent,
|
||||
nativeEventTarget: null | EventTarget,
|
||||
eventSystemFlags: number,
|
||||
container: null | EventTarget,
|
||||
) => void,
|
||||
};
|
|
@ -0,0 +1,83 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
// Used as a way to call batchedUpdates when we don't have a reference to
|
||||
// the renderer. Such as when we're dispatching events or if third party
|
||||
// libraries need to call batchedUpdates. Eventually, this API will go away when
|
||||
// everything is batched by default. We'll then have a similar API to opt-out of
|
||||
// scheduled work and instead do synchronous work.
|
||||
|
||||
// Defaults
|
||||
let batchedUpdatesImpl = function(fn, bookkeeping) {
|
||||
return fn(bookkeeping);
|
||||
};
|
||||
let discreteUpdatesImpl = function(fn, a, b, c, d) {
|
||||
return fn(a, b, c, d);
|
||||
};
|
||||
let flushDiscreteUpdatesImpl = function() {};
|
||||
let batchedEventUpdatesImpl = batchedUpdatesImpl;
|
||||
|
||||
let isInsideEventHandler = false;
|
||||
let isBatchingEventUpdates = false;
|
||||
|
||||
export function batchedUpdates(fn, bookkeeping) {
|
||||
if (isInsideEventHandler) {
|
||||
// If we are currently inside another batch, we need to wait until it
|
||||
// fully completes before restoring state.
|
||||
return fn(bookkeeping);
|
||||
}
|
||||
isInsideEventHandler = true;
|
||||
try {
|
||||
return batchedUpdatesImpl(fn, bookkeeping);
|
||||
} finally {
|
||||
isInsideEventHandler = false;
|
||||
}
|
||||
}
|
||||
|
||||
export function batchedEventUpdates(fn, a, b) {
|
||||
if (isBatchingEventUpdates) {
|
||||
// If we are currently inside another batch, we need to wait until it
|
||||
// fully completes before restoring state.
|
||||
return fn(a, b);
|
||||
}
|
||||
isBatchingEventUpdates = true;
|
||||
try {
|
||||
return batchedEventUpdatesImpl(fn, a, b);
|
||||
} finally {
|
||||
isBatchingEventUpdates = false;
|
||||
}
|
||||
}
|
||||
|
||||
export function discreteUpdates(fn, a, b, c, d) {
|
||||
const prevIsInsideEventHandler = isInsideEventHandler;
|
||||
isInsideEventHandler = true;
|
||||
try {
|
||||
return discreteUpdatesImpl(fn, a, b, c, d);
|
||||
} finally {
|
||||
isInsideEventHandler = prevIsInsideEventHandler;
|
||||
if (!isInsideEventHandler) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function flushDiscreteUpdatesIfNeeded(timeStamp: number) {
|
||||
if (!isInsideEventHandler) {
|
||||
flushDiscreteUpdatesImpl();
|
||||
}
|
||||
}
|
||||
|
||||
export function setBatchingImplementation(
|
||||
_batchedUpdatesImpl,
|
||||
_discreteUpdatesImpl,
|
||||
_flushDiscreteUpdatesImpl,
|
||||
_batchedEventUpdatesImpl,
|
||||
) {
|
||||
batchedUpdatesImpl = _batchedUpdatesImpl;
|
||||
discreteUpdatesImpl = _discreteUpdatesImpl;
|
||||
flushDiscreteUpdatesImpl = _flushDiscreteUpdatesImpl;
|
||||
batchedEventUpdatesImpl = _batchedEventUpdatesImpl;
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* Flow type for SyntheticEvent class that includes private properties
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
|
||||
import type {EventPriority} from 'shared/ReactTypes';
|
||||
import type {TopLevelType} from './TopLevelEventTypes';
|
||||
|
||||
export type DispatchConfig = {|
|
||||
dependencies?: Array<TopLevelType>,
|
||||
phasedRegistrationNames: {|
|
||||
bubbled: null | string,
|
||||
captured: null | string,
|
||||
|},
|
||||
registrationName?: string,
|
||||
eventPriority: EventPriority,
|
||||
|};
|
||||
|
||||
export type CustomDispatchConfig = {|
|
||||
phasedRegistrationNames: {|
|
||||
bubbled: null,
|
||||
captured: null,
|
||||
|},
|
||||
registrationName?: string,
|
||||
customEvent: true,
|
||||
|};
|
||||
|
||||
export type ReactSyntheticEvent = {|
|
||||
dispatchConfig: DispatchConfig | CustomDispatchConfig,
|
||||
getPooled: (
|
||||
dispatchConfig: DispatchConfig | CustomDispatchConfig,
|
||||
targetInst: Fiber,
|
||||
nativeTarget: Event,
|
||||
nativeEventTarget: EventTarget,
|
||||
) => ReactSyntheticEvent,
|
||||
isPersistent: () => boolean,
|
||||
isPropagationStopped: () => boolean,
|
||||
_dispatchInstances?: null | Array<Fiber | null> | Fiber,
|
||||
_dispatchListeners?: null | Array<Function> | Function,
|
||||
_targetInst: Fiber,
|
||||
type: string,
|
||||
currentTarget: null | EventTarget,
|
||||
|};
|
|
@ -0,0 +1,802 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {
|
||||
executeDirectDispatch,
|
||||
hasDispatches,
|
||||
executeDispatchesInOrderStopAtTrue,
|
||||
getInstanceFromNode,
|
||||
getFiberCurrentPropsFromNode,
|
||||
} from './EventPluginUtils';
|
||||
import ResponderSyntheticEvent from './ResponderSyntheticEvent';
|
||||
import ResponderTouchHistoryStore from './ResponderTouchHistoryStore';
|
||||
import accumulate from './accumulate';
|
||||
import {
|
||||
TOP_SCROLL,
|
||||
TOP_SELECTION_CHANGE,
|
||||
TOP_TOUCH_CANCEL,
|
||||
isStartish,
|
||||
isMoveish,
|
||||
isEndish,
|
||||
startDependencies,
|
||||
moveDependencies,
|
||||
endDependencies,
|
||||
} from './ResponderTopLevelEventTypes';
|
||||
import accumulateInto from './accumulateInto';
|
||||
import forEachAccumulated from './forEachAccumulated';
|
||||
import {HostComponent} from 'react-reconciler/src/ReactWorkTags';
|
||||
import invariant from 'shared/invariant';
|
||||
|
||||
/**
|
||||
* Instance of element that should respond to touch/move types of interactions,
|
||||
* as indicated explicitly by relevant callbacks.
|
||||
*/
|
||||
let responderInst = null;
|
||||
|
||||
/**
|
||||
* Count of current touches. A textInput should become responder iff the
|
||||
* selection changes while there is a touch on the screen.
|
||||
*/
|
||||
let trackedTouchCount = 0;
|
||||
|
||||
const changeResponder = function(nextResponderInst, blockHostResponder) {
|
||||
const oldResponderInst = responderInst;
|
||||
responderInst = nextResponderInst;
|
||||
if (ResponderEventPlugin.GlobalResponderHandler !== null) {
|
||||
ResponderEventPlugin.GlobalResponderHandler.onChange(
|
||||
oldResponderInst,
|
||||
nextResponderInst,
|
||||
blockHostResponder,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const eventTypes = {
|
||||
/**
|
||||
* On a `touchStart`/`mouseDown`, is it desired that this element become the
|
||||
* responder?
|
||||
*/
|
||||
startShouldSetResponder: {
|
||||
phasedRegistrationNames: {
|
||||
bubbled: 'onStartShouldSetResponder',
|
||||
captured: 'onStartShouldSetResponderCapture',
|
||||
},
|
||||
dependencies: startDependencies,
|
||||
},
|
||||
|
||||
/**
|
||||
* On a `scroll`, is it desired that this element become the responder? This
|
||||
* is usually not needed, but should be used to retroactively infer that a
|
||||
* `touchStart` had occurred during momentum scroll. During a momentum scroll,
|
||||
* a touch start will be immediately followed by a scroll event if the view is
|
||||
* currently scrolling.
|
||||
*
|
||||
* TODO: This shouldn't bubble.
|
||||
*/
|
||||
scrollShouldSetResponder: {
|
||||
phasedRegistrationNames: {
|
||||
bubbled: 'onScrollShouldSetResponder',
|
||||
captured: 'onScrollShouldSetResponderCapture',
|
||||
},
|
||||
dependencies: [TOP_SCROLL],
|
||||
},
|
||||
|
||||
/**
|
||||
* On text selection change, should this element become the responder? This
|
||||
* is needed for text inputs or other views with native selection, so the
|
||||
* JS view can claim the responder.
|
||||
*
|
||||
* TODO: This shouldn't bubble.
|
||||
*/
|
||||
selectionChangeShouldSetResponder: {
|
||||
phasedRegistrationNames: {
|
||||
bubbled: 'onSelectionChangeShouldSetResponder',
|
||||
captured: 'onSelectionChangeShouldSetResponderCapture',
|
||||
},
|
||||
dependencies: [TOP_SELECTION_CHANGE],
|
||||
},
|
||||
|
||||
/**
|
||||
* On a `touchMove`/`mouseMove`, is it desired that this element become the
|
||||
* responder?
|
||||
*/
|
||||
moveShouldSetResponder: {
|
||||
phasedRegistrationNames: {
|
||||
bubbled: 'onMoveShouldSetResponder',
|
||||
captured: 'onMoveShouldSetResponderCapture',
|
||||
},
|
||||
dependencies: moveDependencies,
|
||||
},
|
||||
|
||||
/**
|
||||
* Direct responder events dispatched directly to responder. Do not bubble.
|
||||
*/
|
||||
responderStart: {
|
||||
registrationName: 'onResponderStart',
|
||||
dependencies: startDependencies,
|
||||
},
|
||||
responderMove: {
|
||||
registrationName: 'onResponderMove',
|
||||
dependencies: moveDependencies,
|
||||
},
|
||||
responderEnd: {
|
||||
registrationName: 'onResponderEnd',
|
||||
dependencies: endDependencies,
|
||||
},
|
||||
responderRelease: {
|
||||
registrationName: 'onResponderRelease',
|
||||
dependencies: endDependencies,
|
||||
},
|
||||
responderTerminationRequest: {
|
||||
registrationName: 'onResponderTerminationRequest',
|
||||
dependencies: [],
|
||||
},
|
||||
responderGrant: {
|
||||
registrationName: 'onResponderGrant',
|
||||
dependencies: [],
|
||||
},
|
||||
responderReject: {
|
||||
registrationName: 'onResponderReject',
|
||||
dependencies: [],
|
||||
},
|
||||
responderTerminate: {
|
||||
registrationName: 'onResponderTerminate',
|
||||
dependencies: [],
|
||||
},
|
||||
};
|
||||
|
||||
// Start of inline: the below functions were inlined from
|
||||
// EventPropagator.js, as they deviated from ReactDOM's newer
|
||||
// implementations.
|
||||
|
||||
function getParent(inst) {
|
||||
do {
|
||||
inst = inst.return;
|
||||
// TODO: If this is a HostRoot we might want to bail out.
|
||||
// That is depending on if we want nested subtrees (layers) to bubble
|
||||
// events to their parent. We could also go through parentNode on the
|
||||
// host node but that wouldn't work for React Native and doesn't let us
|
||||
// do the portal feature.
|
||||
} while (inst && inst.tag !== HostComponent);
|
||||
if (inst) {
|
||||
return inst;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the lowest common ancestor of A and B, or null if they are in
|
||||
* different trees.
|
||||
*/
|
||||
export function getLowestCommonAncestor(instA, instB) {
|
||||
let depthA = 0;
|
||||
for (let tempA = instA; tempA; tempA = getParent(tempA)) {
|
||||
depthA++;
|
||||
}
|
||||
let depthB = 0;
|
||||
for (let tempB = instB; tempB; tempB = getParent(tempB)) {
|
||||
depthB++;
|
||||
}
|
||||
|
||||
// If A is deeper, crawl up.
|
||||
while (depthA - depthB > 0) {
|
||||
instA = getParent(instA);
|
||||
depthA--;
|
||||
}
|
||||
|
||||
// If B is deeper, crawl up.
|
||||
while (depthB - depthA > 0) {
|
||||
instB = getParent(instB);
|
||||
depthB--;
|
||||
}
|
||||
|
||||
// Walk in lockstep until we find a match.
|
||||
let depth = depthA;
|
||||
while (depth--) {
|
||||
if (instA === instB || instA === instB.alternate) {
|
||||
return instA;
|
||||
}
|
||||
instA = getParent(instA);
|
||||
instB = getParent(instB);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if A is an ancestor of B.
|
||||
*/
|
||||
function isAncestor(instA, instB) {
|
||||
while (instB) {
|
||||
if (instA === instB || instA === instB.alternate) {
|
||||
return true;
|
||||
}
|
||||
instB = getParent(instB);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates the traversal of a two-phase, capture/bubble event dispatch.
|
||||
*/
|
||||
function traverseTwoPhase(inst, fn, arg) {
|
||||
const path = [];
|
||||
while (inst) {
|
||||
path.push(inst);
|
||||
inst = getParent(inst);
|
||||
}
|
||||
let i;
|
||||
for (i = path.length; i-- > 0; ) {
|
||||
fn(path[i], 'captured', arg);
|
||||
}
|
||||
for (i = 0; i < path.length; i++) {
|
||||
fn(path[i], 'bubbled', arg);
|
||||
}
|
||||
}
|
||||
|
||||
function getListener(inst, registrationName) {
|
||||
const stateNode = inst.stateNode;
|
||||
if (stateNode === null) {
|
||||
// Work in progress (ex: onload events in incremental mode).
|
||||
return null;
|
||||
}
|
||||
const props = getFiberCurrentPropsFromNode(stateNode);
|
||||
if (props === null) {
|
||||
// Work in progress.
|
||||
return null;
|
||||
}
|
||||
const listener = props[registrationName];
|
||||
invariant(
|
||||
!listener || typeof listener === 'function',
|
||||
'Expected `%s` listener to be a function, instead got a value of `%s` type.',
|
||||
registrationName,
|
||||
typeof listener,
|
||||
);
|
||||
return listener;
|
||||
}
|
||||
|
||||
function listenerAtPhase(inst, event, propagationPhase: PropagationPhases) {
|
||||
const registrationName =
|
||||
event.dispatchConfig.phasedRegistrationNames[propagationPhase];
|
||||
return getListener(inst, registrationName);
|
||||
}
|
||||
|
||||
function accumulateDirectionalDispatches(inst, phase, event) {
|
||||
if (__DEV__) {
|
||||
if (!inst) {
|
||||
console.error('Dispatching inst must not be null');
|
||||
}
|
||||
}
|
||||
const listener = listenerAtPhase(inst, event, phase);
|
||||
if (listener) {
|
||||
event._dispatchListeners = accumulateInto(
|
||||
event._dispatchListeners,
|
||||
listener,
|
||||
);
|
||||
event._dispatchInstances = accumulateInto(event._dispatchInstances, inst);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accumulates without regard to direction, does not look for phased
|
||||
* registration names. Same as `accumulateDirectDispatchesSingle` but without
|
||||
* requiring that the `dispatchMarker` be the same as the dispatched ID.
|
||||
*/
|
||||
function accumulateDispatches(
|
||||
inst: Object,
|
||||
ignoredDirection: ?boolean,
|
||||
event: Object,
|
||||
): void {
|
||||
if (inst && event && event.dispatchConfig.registrationName) {
|
||||
const registrationName = event.dispatchConfig.registrationName;
|
||||
const listener = getListener(inst, registrationName);
|
||||
if (listener) {
|
||||
event._dispatchListeners = accumulateInto(
|
||||
event._dispatchListeners,
|
||||
listener,
|
||||
);
|
||||
event._dispatchInstances = accumulateInto(event._dispatchInstances, inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accumulates dispatches on an `SyntheticEvent`, but only for the
|
||||
* `dispatchMarker`.
|
||||
* @param {SyntheticEvent} event
|
||||
*/
|
||||
function accumulateDirectDispatchesSingle(event: Object) {
|
||||
if (event && event.dispatchConfig.registrationName) {
|
||||
accumulateDispatches(event._targetInst, null, event);
|
||||
}
|
||||
}
|
||||
|
||||
function accumulateDirectDispatches(events: ?(Array<Object> | Object)) {
|
||||
forEachAccumulated(events, accumulateDirectDispatchesSingle);
|
||||
}
|
||||
|
||||
function accumulateTwoPhaseDispatchesSingleSkipTarget(event) {
|
||||
if (event && event.dispatchConfig.phasedRegistrationNames) {
|
||||
const targetInst = event._targetInst;
|
||||
const parentInst = targetInst ? getParent(targetInst) : null;
|
||||
traverseTwoPhase(parentInst, accumulateDirectionalDispatches, event);
|
||||
}
|
||||
}
|
||||
|
||||
function accumulateTwoPhaseDispatchesSkipTarget(events) {
|
||||
forEachAccumulated(events, accumulateTwoPhaseDispatchesSingleSkipTarget);
|
||||
}
|
||||
|
||||
function accumulateTwoPhaseDispatchesSingle(event) {
|
||||
if (event && event.dispatchConfig.phasedRegistrationNames) {
|
||||
traverseTwoPhase(event._targetInst, accumulateDirectionalDispatches, event);
|
||||
}
|
||||
}
|
||||
|
||||
function accumulateTwoPhaseDispatches(events) {
|
||||
forEachAccumulated(events, accumulateTwoPhaseDispatchesSingle);
|
||||
}
|
||||
// End of inline
|
||||
|
||||
/**
|
||||
*
|
||||
* Responder System:
|
||||
* ----------------
|
||||
*
|
||||
* - A global, solitary "interaction lock" on a view.
|
||||
* - If a node becomes the responder, it should convey visual feedback
|
||||
* immediately to indicate so, either by highlighting or moving accordingly.
|
||||
* - To be the responder means, that touches are exclusively important to that
|
||||
* responder view, and no other view.
|
||||
* - While touches are still occurring, the responder lock can be transferred to
|
||||
* a new view, but only to increasingly "higher" views (meaning ancestors of
|
||||
* the current responder).
|
||||
*
|
||||
* Responder being granted:
|
||||
* ------------------------
|
||||
*
|
||||
* - Touch starts, moves, and scrolls can cause an ID to become the responder.
|
||||
* - We capture/bubble `startShouldSetResponder`/`moveShouldSetResponder` to
|
||||
* the "appropriate place".
|
||||
* - If nothing is currently the responder, the "appropriate place" is the
|
||||
* initiating event's `targetID`.
|
||||
* - If something *is* already the responder, the "appropriate place" is the
|
||||
* first common ancestor of the event target and the current `responderInst`.
|
||||
* - Some negotiation happens: See the timing diagram below.
|
||||
* - Scrolled views automatically become responder. The reasoning is that a
|
||||
* platform scroll view that isn't built on top of the responder system has
|
||||
* began scrolling, and the active responder must now be notified that the
|
||||
* interaction is no longer locked to it - the system has taken over.
|
||||
*
|
||||
* - Responder being released:
|
||||
* As soon as no more touches that *started* inside of descendants of the
|
||||
* *current* responderInst, an `onResponderRelease` event is dispatched to the
|
||||
* current responder, and the responder lock is released.
|
||||
*
|
||||
* TODO:
|
||||
* - on "end", a callback hook for `onResponderEndShouldRemainResponder` that
|
||||
* determines if the responder lock should remain.
|
||||
* - If a view shouldn't "remain" the responder, any active touches should by
|
||||
* default be considered "dead" and do not influence future negotiations or
|
||||
* bubble paths. It should be as if those touches do not exist.
|
||||
* -- For multitouch: Usually a translate-z will choose to "remain" responder
|
||||
* after one out of many touches ended. For translate-y, usually the view
|
||||
* doesn't wish to "remain" responder after one of many touches end.
|
||||
* - Consider building this on top of a `stopPropagation` model similar to
|
||||
* `W3C` events.
|
||||
* - Ensure that `onResponderTerminate` is called on touch cancels, whether or
|
||||
* not `onResponderTerminationRequest` returns `true` or `false`.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Negotiation Performed
|
||||
+-----------------------+
|
||||
/ \
|
||||
Process low level events to + Current Responder + wantsResponderID
|
||||
determine who to perform negot-| (if any exists at all) |
|
||||
iation/transition | Otherwise just pass through|
|
||||
-------------------------------+----------------------------+------------------+
|
||||
Bubble to find first ID | |
|
||||
to return true:wantsResponderID| |
|
||||
| |
|
||||
+-------------+ | |
|
||||
| onTouchStart| | |
|
||||
+------+------+ none | |
|
||||
| return| |
|
||||
+-----------v-------------+true| +------------------------+ |
|
||||
|onStartShouldSetResponder|----->|onResponderStart (cur) |<-----------+
|
||||
+-----------+-------------+ | +------------------------+ | |
|
||||
| | | +--------+-------+
|
||||
| returned true for| false:REJECT +-------->|onResponderReject
|
||||
| wantsResponderID | | | +----------------+
|
||||
| (now attempt | +------------------+-----+ |
|
||||
| handoff) | | onResponder | |
|
||||
+------------------->| TerminationRequest| |
|
||||
| +------------------+-----+ |
|
||||
| | | +----------------+
|
||||
| true:GRANT +-------->|onResponderGrant|
|
||||
| | +--------+-------+
|
||||
| +------------------------+ | |
|
||||
| | onResponderTerminate |<-----------+
|
||||
| +------------------+-----+ |
|
||||
| | | +----------------+
|
||||
| +-------->|onResponderStart|
|
||||
| | +----------------+
|
||||
Bubble to find first ID | |
|
||||
to return true:wantsResponderID| |
|
||||
| |
|
||||
+-------------+ | |
|
||||
| onTouchMove | | |
|
||||
+------+------+ none | |
|
||||
| return| |
|
||||
+-----------v-------------+true| +------------------------+ |
|
||||
|onMoveShouldSetResponder |----->|onResponderMove (cur) |<-----------+
|
||||
+-----------+-------------+ | +------------------------+ | |
|
||||
| | | +--------+-------+
|
||||
| returned true for| false:REJECT +-------->|onResponderRejec|
|
||||
| wantsResponderID | | | +----------------+
|
||||
| (now attempt | +------------------+-----+ |
|
||||
| handoff) | | onResponder | |
|
||||
+------------------->| TerminationRequest| |
|
||||
| +------------------+-----+ |
|
||||
| | | +----------------+
|
||||
| true:GRANT +-------->|onResponderGrant|
|
||||
| | +--------+-------+
|
||||
| +------------------------+ | |
|
||||
| | onResponderTerminate |<-----------+
|
||||
| +------------------+-----+ |
|
||||
| | | +----------------+
|
||||
| +-------->|onResponderMove |
|
||||
| | +----------------+
|
||||
| |
|
||||
| |
|
||||
Some active touch started| |
|
||||
inside current responder | +------------------------+ |
|
||||
+------------------------->| onResponderEnd | |
|
||||
| | +------------------------+ |
|
||||
+---+---------+ | |
|
||||
| onTouchEnd | | |
|
||||
+---+---------+ | |
|
||||
| | +------------------------+ |
|
||||
+------------------------->| onResponderEnd | |
|
||||
No active touches started| +-----------+------------+ |
|
||||
inside current responder | | |
|
||||
| v |
|
||||
| +------------------------+ |
|
||||
| | onResponderRelease | |
|
||||
| +------------------------+ |
|
||||
| |
|
||||
+ + */
|
||||
|
||||
/**
|
||||
* A note about event ordering in the `EventPluginRegistry`.
|
||||
*
|
||||
* Suppose plugins are injected in the following order:
|
||||
*
|
||||
* `[R, S, C]`
|
||||
*
|
||||
* To help illustrate the example, assume `S` is `SimpleEventPlugin` (for
|
||||
* `onClick` etc) and `R` is `ResponderEventPlugin`.
|
||||
*
|
||||
* "Deferred-Dispatched Events":
|
||||
*
|
||||
* - The current event plugin system will traverse the list of injected plugins,
|
||||
* in order, and extract events by collecting the plugin's return value of
|
||||
* `extractEvents()`.
|
||||
* - These events that are returned from `extractEvents` are "deferred
|
||||
* dispatched events".
|
||||
* - When returned from `extractEvents`, deferred-dispatched events contain an
|
||||
* "accumulation" of deferred dispatches.
|
||||
* - These deferred dispatches are accumulated/collected before they are
|
||||
* returned, but processed at a later time by the `EventPluginRegistry` (hence the
|
||||
* name deferred).
|
||||
*
|
||||
* In the process of returning their deferred-dispatched events, event plugins
|
||||
* themselves can dispatch events on-demand without returning them from
|
||||
* `extractEvents`. Plugins might want to do this, so that they can use event
|
||||
* dispatching as a tool that helps them decide which events should be extracted
|
||||
* in the first place.
|
||||
*
|
||||
* "On-Demand-Dispatched Events":
|
||||
*
|
||||
* - On-demand-dispatched events are not returned from `extractEvents`.
|
||||
* - On-demand-dispatched events are dispatched during the process of returning
|
||||
* the deferred-dispatched events.
|
||||
* - They should not have side effects.
|
||||
* - They should be avoided, and/or eventually be replaced with another
|
||||
* abstraction that allows event plugins to perform multiple "rounds" of event
|
||||
* extraction.
|
||||
*
|
||||
* Therefore, the sequence of event dispatches becomes:
|
||||
*
|
||||
* - `R`s on-demand events (if any) (dispatched by `R` on-demand)
|
||||
* - `S`s on-demand events (if any) (dispatched by `S` on-demand)
|
||||
* - `C`s on-demand events (if any) (dispatched by `C` on-demand)
|
||||
* - `R`s extracted events (if any) (dispatched by `EventPluginRegistry`)
|
||||
* - `S`s extracted events (if any) (dispatched by `EventPluginRegistry`)
|
||||
* - `C`s extracted events (if any) (dispatched by `EventPluginRegistry`)
|
||||
*
|
||||
* In the case of `ResponderEventPlugin`: If the `startShouldSetResponder`
|
||||
* on-demand dispatch returns `true` (and some other details are satisfied) the
|
||||
* `onResponderGrant` deferred dispatched event is returned from
|
||||
* `extractEvents`. The sequence of dispatch executions in this case
|
||||
* will appear as follows:
|
||||
*
|
||||
* - `startShouldSetResponder` (`ResponderEventPlugin` dispatches on-demand)
|
||||
* - `touchStartCapture` (`EventPluginRegistry` dispatches as usual)
|
||||
* - `touchStart` (`EventPluginRegistry` dispatches as usual)
|
||||
* - `responderGrant/Reject` (`EventPluginRegistry` dispatches as usual)
|
||||
*/
|
||||
|
||||
function setResponderAndExtractTransfer(
|
||||
topLevelType,
|
||||
targetInst,
|
||||
nativeEvent,
|
||||
nativeEventTarget,
|
||||
) {
|
||||
const shouldSetEventType = isStartish(topLevelType)
|
||||
? eventTypes.startShouldSetResponder
|
||||
: isMoveish(topLevelType)
|
||||
? eventTypes.moveShouldSetResponder
|
||||
: topLevelType === TOP_SELECTION_CHANGE
|
||||
? eventTypes.selectionChangeShouldSetResponder
|
||||
: eventTypes.scrollShouldSetResponder;
|
||||
|
||||
// TODO: stop one short of the current responder.
|
||||
const bubbleShouldSetFrom = !responderInst
|
||||
? targetInst
|
||||
: getLowestCommonAncestor(responderInst, targetInst);
|
||||
|
||||
// When capturing/bubbling the "shouldSet" event, we want to skip the target
|
||||
// (deepest ID) if it happens to be the current responder. The reasoning:
|
||||
// It's strange to get an `onMoveShouldSetResponder` when you're *already*
|
||||
// the responder.
|
||||
const skipOverBubbleShouldSetFrom = bubbleShouldSetFrom === responderInst;
|
||||
const shouldSetEvent = ResponderSyntheticEvent.getPooled(
|
||||
shouldSetEventType,
|
||||
bubbleShouldSetFrom,
|
||||
nativeEvent,
|
||||
nativeEventTarget,
|
||||
);
|
||||
shouldSetEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
|
||||
if (skipOverBubbleShouldSetFrom) {
|
||||
accumulateTwoPhaseDispatchesSkipTarget(shouldSetEvent);
|
||||
} else {
|
||||
accumulateTwoPhaseDispatches(shouldSetEvent);
|
||||
}
|
||||
const wantsResponderInst = executeDispatchesInOrderStopAtTrue(shouldSetEvent);
|
||||
if (!shouldSetEvent.isPersistent()) {
|
||||
shouldSetEvent.constructor.release(shouldSetEvent);
|
||||
}
|
||||
|
||||
if (!wantsResponderInst || wantsResponderInst === responderInst) {
|
||||
return null;
|
||||
}
|
||||
let extracted;
|
||||
const grantEvent = ResponderSyntheticEvent.getPooled(
|
||||
eventTypes.responderGrant,
|
||||
wantsResponderInst,
|
||||
nativeEvent,
|
||||
nativeEventTarget,
|
||||
);
|
||||
grantEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
|
||||
|
||||
accumulateDirectDispatches(grantEvent);
|
||||
const blockHostResponder = executeDirectDispatch(grantEvent) === true;
|
||||
if (responderInst) {
|
||||
const terminationRequestEvent = ResponderSyntheticEvent.getPooled(
|
||||
eventTypes.responderTerminationRequest,
|
||||
responderInst,
|
||||
nativeEvent,
|
||||
nativeEventTarget,
|
||||
);
|
||||
terminationRequestEvent.touchHistory =
|
||||
ResponderTouchHistoryStore.touchHistory;
|
||||
accumulateDirectDispatches(terminationRequestEvent);
|
||||
const shouldSwitch =
|
||||
!hasDispatches(terminationRequestEvent) ||
|
||||
executeDirectDispatch(terminationRequestEvent);
|
||||
if (!terminationRequestEvent.isPersistent()) {
|
||||
terminationRequestEvent.constructor.release(terminationRequestEvent);
|
||||
}
|
||||
|
||||
if (shouldSwitch) {
|
||||
const terminateEvent = ResponderSyntheticEvent.getPooled(
|
||||
eventTypes.responderTerminate,
|
||||
responderInst,
|
||||
nativeEvent,
|
||||
nativeEventTarget,
|
||||
);
|
||||
terminateEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
|
||||
accumulateDirectDispatches(terminateEvent);
|
||||
extracted = accumulate(extracted, [grantEvent, terminateEvent]);
|
||||
changeResponder(wantsResponderInst, blockHostResponder);
|
||||
} else {
|
||||
const rejectEvent = ResponderSyntheticEvent.getPooled(
|
||||
eventTypes.responderReject,
|
||||
wantsResponderInst,
|
||||
nativeEvent,
|
||||
nativeEventTarget,
|
||||
);
|
||||
rejectEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
|
||||
accumulateDirectDispatches(rejectEvent);
|
||||
extracted = accumulate(extracted, rejectEvent);
|
||||
}
|
||||
} else {
|
||||
extracted = accumulate(extracted, grantEvent);
|
||||
changeResponder(wantsResponderInst, blockHostResponder);
|
||||
}
|
||||
return extracted;
|
||||
}
|
||||
|
||||
/**
|
||||
* A transfer is a negotiation between a currently set responder and the next
|
||||
* element to claim responder status. Any start event could trigger a transfer
|
||||
* of responderInst. Any move event could trigger a transfer.
|
||||
*
|
||||
* @param {string} topLevelType Record from `BrowserEventConstants`.
|
||||
* @return {boolean} True if a transfer of responder could possibly occur.
|
||||
*/
|
||||
function canTriggerTransfer(topLevelType, topLevelInst, nativeEvent) {
|
||||
return (
|
||||
topLevelInst &&
|
||||
// responderIgnoreScroll: We are trying to migrate away from specifically
|
||||
// tracking native scroll events here and responderIgnoreScroll indicates we
|
||||
// will send topTouchCancel to handle canceling touch events instead
|
||||
((topLevelType === TOP_SCROLL && !nativeEvent.responderIgnoreScroll) ||
|
||||
(trackedTouchCount > 0 && topLevelType === TOP_SELECTION_CHANGE) ||
|
||||
isStartish(topLevelType) ||
|
||||
isMoveish(topLevelType))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this touch end event makes it such that there are no
|
||||
* longer any touches that started inside of the current `responderInst`.
|
||||
*
|
||||
* @param {NativeEvent} nativeEvent Native touch end event.
|
||||
* @return {boolean} Whether or not this touch end event ends the responder.
|
||||
*/
|
||||
function noResponderTouches(nativeEvent) {
|
||||
const touches = nativeEvent.touches;
|
||||
if (!touches || touches.length === 0) {
|
||||
return true;
|
||||
}
|
||||
for (let i = 0; i < touches.length; i++) {
|
||||
const activeTouch = touches[i];
|
||||
const target = activeTouch.target;
|
||||
if (target !== null && target !== undefined && target !== 0) {
|
||||
// Is the original touch location inside of the current responder?
|
||||
const targetInst = getInstanceFromNode(target);
|
||||
if (isAncestor(responderInst, targetInst)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const ResponderEventPlugin = {
|
||||
/* For unit testing only */
|
||||
_getResponder: function() {
|
||||
return responderInst;
|
||||
},
|
||||
|
||||
eventTypes: eventTypes,
|
||||
|
||||
/**
|
||||
* We must be resilient to `targetInst` being `null` on `touchMove` or
|
||||
* `touchEnd`. On certain platforms, this means that a native scroll has
|
||||
* assumed control and the original touch targets are destroyed.
|
||||
*/
|
||||
extractEvents: function(
|
||||
topLevelType,
|
||||
targetInst,
|
||||
nativeEvent,
|
||||
nativeEventTarget,
|
||||
eventSystemFlags,
|
||||
) {
|
||||
if (isStartish(topLevelType)) {
|
||||
trackedTouchCount += 1;
|
||||
} else if (isEndish(topLevelType)) {
|
||||
if (trackedTouchCount >= 0) {
|
||||
trackedTouchCount -= 1;
|
||||
} else {
|
||||
if (__DEV__) {
|
||||
console.warn(
|
||||
'Ended a touch event which was not counted in `trackedTouchCount`.',
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
ResponderTouchHistoryStore.recordTouchTrack(topLevelType, nativeEvent);
|
||||
|
||||
let extracted = canTriggerTransfer(topLevelType, targetInst, nativeEvent)
|
||||
? setResponderAndExtractTransfer(
|
||||
topLevelType,
|
||||
targetInst,
|
||||
nativeEvent,
|
||||
nativeEventTarget,
|
||||
)
|
||||
: null;
|
||||
// Responder may or may not have transferred on a new touch start/move.
|
||||
// Regardless, whoever is the responder after any potential transfer, we
|
||||
// direct all touch start/move/ends to them in the form of
|
||||
// `onResponderMove/Start/End`. These will be called for *every* additional
|
||||
// finger that move/start/end, dispatched directly to whoever is the
|
||||
// current responder at that moment, until the responder is "released".
|
||||
//
|
||||
// These multiple individual change touch events are are always bookended
|
||||
// by `onResponderGrant`, and one of
|
||||
// (`onResponderRelease/onResponderTerminate`).
|
||||
const isResponderTouchStart = responderInst && isStartish(topLevelType);
|
||||
const isResponderTouchMove = responderInst && isMoveish(topLevelType);
|
||||
const isResponderTouchEnd = responderInst && isEndish(topLevelType);
|
||||
const incrementalTouch = isResponderTouchStart
|
||||
? eventTypes.responderStart
|
||||
: isResponderTouchMove
|
||||
? eventTypes.responderMove
|
||||
: isResponderTouchEnd
|
||||
? eventTypes.responderEnd
|
||||
: null;
|
||||
|
||||
if (incrementalTouch) {
|
||||
const gesture = ResponderSyntheticEvent.getPooled(
|
||||
incrementalTouch,
|
||||
responderInst,
|
||||
nativeEvent,
|
||||
nativeEventTarget,
|
||||
);
|
||||
gesture.touchHistory = ResponderTouchHistoryStore.touchHistory;
|
||||
accumulateDirectDispatches(gesture);
|
||||
extracted = accumulate(extracted, gesture);
|
||||
}
|
||||
|
||||
const isResponderTerminate =
|
||||
responderInst && topLevelType === TOP_TOUCH_CANCEL;
|
||||
const isResponderRelease =
|
||||
responderInst &&
|
||||
!isResponderTerminate &&
|
||||
isEndish(topLevelType) &&
|
||||
noResponderTouches(nativeEvent);
|
||||
const finalTouch = isResponderTerminate
|
||||
? eventTypes.responderTerminate
|
||||
: isResponderRelease
|
||||
? eventTypes.responderRelease
|
||||
: null;
|
||||
if (finalTouch) {
|
||||
const finalEvent = ResponderSyntheticEvent.getPooled(
|
||||
finalTouch,
|
||||
responderInst,
|
||||
nativeEvent,
|
||||
nativeEventTarget,
|
||||
);
|
||||
finalEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
|
||||
accumulateDirectDispatches(finalEvent);
|
||||
extracted = accumulate(extracted, finalEvent);
|
||||
changeResponder(null);
|
||||
}
|
||||
|
||||
return extracted;
|
||||
},
|
||||
|
||||
GlobalResponderHandler: null,
|
||||
|
||||
injection: {
|
||||
/**
|
||||
* @param {{onChange: (ReactID, ReactID) => void} GlobalResponderHandler
|
||||
* Object that handles any change in responder. Use this to inject
|
||||
* integration with an existing touch handling system etc.
|
||||
*/
|
||||
injectGlobalResponderHandler(GlobalResponderHandler) {
|
||||
ResponderEventPlugin.GlobalResponderHandler = GlobalResponderHandler;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default ResponderEventPlugin;
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import SyntheticEvent from './SyntheticEvent';
|
||||
|
||||
/**
|
||||
* `touchHistory` isn't actually on the native event, but putting it in the
|
||||
* interface will ensure that it is cleaned up when pooled/destroyed. The
|
||||
* `ResponderEventPlugin` will populate it appropriately.
|
||||
*/
|
||||
const ResponderSyntheticEvent = SyntheticEvent.extend({
|
||||
touchHistory: function(nativeEvent) {
|
||||
return null; // Actually doesn't even look at the native event.
|
||||
},
|
||||
});
|
||||
|
||||
export default ResponderSyntheticEvent;
|
31
packages/react-native-renderer/src/legacy-events/ResponderTopLevelEventTypes.js
vendored
Normal file
31
packages/react-native-renderer/src/legacy-events/ResponderTopLevelEventTypes.js
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
export const TOP_TOUCH_START = 'topTouchStart';
|
||||
export const TOP_TOUCH_MOVE = 'topTouchMove';
|
||||
export const TOP_TOUCH_END = 'topTouchEnd';
|
||||
export const TOP_TOUCH_CANCEL = 'topTouchCancel';
|
||||
export const TOP_SCROLL = 'topScroll';
|
||||
export const TOP_SELECTION_CHANGE = 'topSelectionChange';
|
||||
|
||||
export function isStartish(topLevelType: mixed): boolean {
|
||||
return topLevelType === TOP_TOUCH_START;
|
||||
}
|
||||
|
||||
export function isMoveish(topLevelType: mixed): boolean {
|
||||
return topLevelType === TOP_TOUCH_MOVE;
|
||||
}
|
||||
|
||||
export function isEndish(topLevelType: mixed): boolean {
|
||||
return topLevelType === TOP_TOUCH_END || topLevelType === TOP_TOUCH_CANCEL;
|
||||
}
|
||||
|
||||
export const startDependencies = [TOP_TOUCH_START];
|
||||
export const moveDependencies = [TOP_TOUCH_MOVE];
|
||||
export const endDependencies = [TOP_TOUCH_CANCEL, TOP_TOUCH_END];
|
222
packages/react-native-renderer/src/legacy-events/ResponderTouchHistoryStore.js
vendored
Normal file
222
packages/react-native-renderer/src/legacy-events/ResponderTouchHistoryStore.js
vendored
Normal file
|
@ -0,0 +1,222 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import invariant from 'shared/invariant';
|
||||
|
||||
import {isStartish, isMoveish, isEndish} from './ResponderTopLevelEventTypes';
|
||||
|
||||
/**
|
||||
* Tracks the position and time of each active touch by `touch.identifier`. We
|
||||
* should typically only see IDs in the range of 1-20 because IDs get recycled
|
||||
* when touches end and start again.
|
||||
*/
|
||||
type TouchRecord = {|
|
||||
touchActive: boolean,
|
||||
startPageX: number,
|
||||
startPageY: number,
|
||||
startTimeStamp: number,
|
||||
currentPageX: number,
|
||||
currentPageY: number,
|
||||
currentTimeStamp: number,
|
||||
previousPageX: number,
|
||||
previousPageY: number,
|
||||
previousTimeStamp: number,
|
||||
|};
|
||||
|
||||
const MAX_TOUCH_BANK = 20;
|
||||
const touchBank: Array<TouchRecord> = [];
|
||||
const touchHistory = {
|
||||
touchBank,
|
||||
numberActiveTouches: 0,
|
||||
// If there is only one active touch, we remember its location. This prevents
|
||||
// us having to loop through all of the touches all the time in the most
|
||||
// common case.
|
||||
indexOfSingleActiveTouch: -1,
|
||||
mostRecentTimeStamp: 0,
|
||||
};
|
||||
|
||||
type Touch = {
|
||||
identifier: ?number,
|
||||
pageX: number,
|
||||
pageY: number,
|
||||
timestamp: number,
|
||||
...
|
||||
};
|
||||
type TouchEvent = {
|
||||
changedTouches: Array<Touch>,
|
||||
touches: Array<Touch>,
|
||||
...
|
||||
};
|
||||
|
||||
function timestampForTouch(touch: Touch): number {
|
||||
// The legacy internal implementation provides "timeStamp", which has been
|
||||
// renamed to "timestamp". Let both work for now while we iron it out
|
||||
// TODO (evv): rename timeStamp to timestamp in internal code
|
||||
return (touch: any).timeStamp || touch.timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Instead of making gestures recompute filtered velocity, we could
|
||||
* include a built in velocity computation that can be reused globally.
|
||||
*/
|
||||
function createTouchRecord(touch: Touch): TouchRecord {
|
||||
return {
|
||||
touchActive: true,
|
||||
startPageX: touch.pageX,
|
||||
startPageY: touch.pageY,
|
||||
startTimeStamp: timestampForTouch(touch),
|
||||
currentPageX: touch.pageX,
|
||||
currentPageY: touch.pageY,
|
||||
currentTimeStamp: timestampForTouch(touch),
|
||||
previousPageX: touch.pageX,
|
||||
previousPageY: touch.pageY,
|
||||
previousTimeStamp: timestampForTouch(touch),
|
||||
};
|
||||
}
|
||||
|
||||
function resetTouchRecord(touchRecord: TouchRecord, touch: Touch): void {
|
||||
touchRecord.touchActive = true;
|
||||
touchRecord.startPageX = touch.pageX;
|
||||
touchRecord.startPageY = touch.pageY;
|
||||
touchRecord.startTimeStamp = timestampForTouch(touch);
|
||||
touchRecord.currentPageX = touch.pageX;
|
||||
touchRecord.currentPageY = touch.pageY;
|
||||
touchRecord.currentTimeStamp = timestampForTouch(touch);
|
||||
touchRecord.previousPageX = touch.pageX;
|
||||
touchRecord.previousPageY = touch.pageY;
|
||||
touchRecord.previousTimeStamp = timestampForTouch(touch);
|
||||
}
|
||||
|
||||
function getTouchIdentifier({identifier}: Touch): number {
|
||||
invariant(identifier != null, 'Touch object is missing identifier.');
|
||||
if (__DEV__) {
|
||||
if (identifier > MAX_TOUCH_BANK) {
|
||||
console.error(
|
||||
'Touch identifier %s is greater than maximum supported %s which causes ' +
|
||||
'performance issues backfilling array locations for all of the indices.',
|
||||
identifier,
|
||||
MAX_TOUCH_BANK,
|
||||
);
|
||||
}
|
||||
}
|
||||
return identifier;
|
||||
}
|
||||
|
||||
function recordTouchStart(touch: Touch): void {
|
||||
const identifier = getTouchIdentifier(touch);
|
||||
const touchRecord = touchBank[identifier];
|
||||
if (touchRecord) {
|
||||
resetTouchRecord(touchRecord, touch);
|
||||
} else {
|
||||
touchBank[identifier] = createTouchRecord(touch);
|
||||
}
|
||||
touchHistory.mostRecentTimeStamp = timestampForTouch(touch);
|
||||
}
|
||||
|
||||
function recordTouchMove(touch: Touch): void {
|
||||
const touchRecord = touchBank[getTouchIdentifier(touch)];
|
||||
if (touchRecord) {
|
||||
touchRecord.touchActive = true;
|
||||
touchRecord.previousPageX = touchRecord.currentPageX;
|
||||
touchRecord.previousPageY = touchRecord.currentPageY;
|
||||
touchRecord.previousTimeStamp = touchRecord.currentTimeStamp;
|
||||
touchRecord.currentPageX = touch.pageX;
|
||||
touchRecord.currentPageY = touch.pageY;
|
||||
touchRecord.currentTimeStamp = timestampForTouch(touch);
|
||||
touchHistory.mostRecentTimeStamp = timestampForTouch(touch);
|
||||
} else {
|
||||
if (__DEV__) {
|
||||
console.warn(
|
||||
'Cannot record touch move without a touch start.\n' +
|
||||
'Touch Move: %s\n' +
|
||||
'Touch Bank: %s',
|
||||
printTouch(touch),
|
||||
printTouchBank(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function recordTouchEnd(touch: Touch): void {
|
||||
const touchRecord = touchBank[getTouchIdentifier(touch)];
|
||||
if (touchRecord) {
|
||||
touchRecord.touchActive = false;
|
||||
touchRecord.previousPageX = touchRecord.currentPageX;
|
||||
touchRecord.previousPageY = touchRecord.currentPageY;
|
||||
touchRecord.previousTimeStamp = touchRecord.currentTimeStamp;
|
||||
touchRecord.currentPageX = touch.pageX;
|
||||
touchRecord.currentPageY = touch.pageY;
|
||||
touchRecord.currentTimeStamp = timestampForTouch(touch);
|
||||
touchHistory.mostRecentTimeStamp = timestampForTouch(touch);
|
||||
} else {
|
||||
if (__DEV__) {
|
||||
console.warn(
|
||||
'Cannot record touch end without a touch start.\n' +
|
||||
'Touch End: %s\n' +
|
||||
'Touch Bank: %s',
|
||||
printTouch(touch),
|
||||
printTouchBank(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function printTouch(touch: Touch): string {
|
||||
return JSON.stringify({
|
||||
identifier: touch.identifier,
|
||||
pageX: touch.pageX,
|
||||
pageY: touch.pageY,
|
||||
timestamp: timestampForTouch(touch),
|
||||
});
|
||||
}
|
||||
|
||||
function printTouchBank(): string {
|
||||
let printed = JSON.stringify(touchBank.slice(0, MAX_TOUCH_BANK));
|
||||
if (touchBank.length > MAX_TOUCH_BANK) {
|
||||
printed += ' (original size: ' + touchBank.length + ')';
|
||||
}
|
||||
return printed;
|
||||
}
|
||||
|
||||
const ResponderTouchHistoryStore = {
|
||||
recordTouchTrack(topLevelType: string, nativeEvent: TouchEvent): void {
|
||||
if (isMoveish(topLevelType)) {
|
||||
nativeEvent.changedTouches.forEach(recordTouchMove);
|
||||
} else if (isStartish(topLevelType)) {
|
||||
nativeEvent.changedTouches.forEach(recordTouchStart);
|
||||
touchHistory.numberActiveTouches = nativeEvent.touches.length;
|
||||
if (touchHistory.numberActiveTouches === 1) {
|
||||
touchHistory.indexOfSingleActiveTouch =
|
||||
nativeEvent.touches[0].identifier;
|
||||
}
|
||||
} else if (isEndish(topLevelType)) {
|
||||
nativeEvent.changedTouches.forEach(recordTouchEnd);
|
||||
touchHistory.numberActiveTouches = nativeEvent.touches.length;
|
||||
if (touchHistory.numberActiveTouches === 1) {
|
||||
for (let i = 0; i < touchBank.length; i++) {
|
||||
const touchTrackToCheck = touchBank[i];
|
||||
if (touchTrackToCheck != null && touchTrackToCheck.touchActive) {
|
||||
touchHistory.indexOfSingleActiveTouch = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (__DEV__) {
|
||||
const activeRecord = touchBank[touchHistory.indexOfSingleActiveTouch];
|
||||
if (activeRecord == null || !activeRecord.touchActive) {
|
||||
console.error('Cannot find single active touch.');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
touchHistory,
|
||||
};
|
||||
|
||||
export default ResponderTouchHistoryStore;
|
|
@ -0,0 +1,366 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/* eslint valid-typeof: 0 */
|
||||
|
||||
import invariant from 'shared/invariant';
|
||||
import {enableModernEventSystem} from 'shared/ReactFeatureFlags';
|
||||
|
||||
const EVENT_POOL_SIZE = 10;
|
||||
|
||||
/**
|
||||
* @interface Event
|
||||
* @see http://www.w3.org/TR/DOM-Level-3-Events/
|
||||
*/
|
||||
const EventInterface = {
|
||||
type: null,
|
||||
target: null,
|
||||
// currentTarget is set when dispatching; no use in copying it here
|
||||
currentTarget: function() {
|
||||
return null;
|
||||
},
|
||||
eventPhase: null,
|
||||
bubbles: null,
|
||||
cancelable: null,
|
||||
timeStamp: function(event) {
|
||||
return event.timeStamp || Date.now();
|
||||
},
|
||||
defaultPrevented: null,
|
||||
isTrusted: null,
|
||||
};
|
||||
|
||||
function functionThatReturnsTrue() {
|
||||
return true;
|
||||
}
|
||||
|
||||
function functionThatReturnsFalse() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synthetic events are dispatched by event plugins, typically in response to a
|
||||
* top-level event delegation handler.
|
||||
*
|
||||
* These systems should generally use pooling to reduce the frequency of garbage
|
||||
* collection. The system should check `isPersistent` to determine whether the
|
||||
* event should be released into the pool after being dispatched. Users that
|
||||
* need a persisted event should invoke `persist`.
|
||||
*
|
||||
* Synthetic events (and subclasses) implement the DOM Level 3 Events API by
|
||||
* normalizing browser quirks. Subclasses do not necessarily have to implement a
|
||||
* DOM interface; custom application-specific events can also subclass this.
|
||||
*
|
||||
* @param {object} dispatchConfig Configuration used to dispatch this event.
|
||||
* @param {*} targetInst Marker identifying the event target.
|
||||
* @param {object} nativeEvent Native browser event.
|
||||
* @param {DOMEventTarget} nativeEventTarget Target node.
|
||||
*/
|
||||
function SyntheticEvent(
|
||||
dispatchConfig,
|
||||
targetInst,
|
||||
nativeEvent,
|
||||
nativeEventTarget,
|
||||
) {
|
||||
if (__DEV__) {
|
||||
// these have a getter/setter for warnings
|
||||
delete this.nativeEvent;
|
||||
delete this.preventDefault;
|
||||
delete this.stopPropagation;
|
||||
delete this.isDefaultPrevented;
|
||||
delete this.isPropagationStopped;
|
||||
}
|
||||
|
||||
this.dispatchConfig = dispatchConfig;
|
||||
this._targetInst = targetInst;
|
||||
this.nativeEvent = nativeEvent;
|
||||
if (!enableModernEventSystem) {
|
||||
this._dispatchListeners = null;
|
||||
this._dispatchInstances = null;
|
||||
}
|
||||
|
||||
const Interface = this.constructor.Interface;
|
||||
for (const propName in Interface) {
|
||||
if (!Interface.hasOwnProperty(propName)) {
|
||||
continue;
|
||||
}
|
||||
if (__DEV__) {
|
||||
delete this[propName]; // this has a getter/setter for warnings
|
||||
}
|
||||
const normalize = Interface[propName];
|
||||
if (normalize) {
|
||||
this[propName] = normalize(nativeEvent);
|
||||
} else {
|
||||
if (propName === 'target') {
|
||||
this.target = nativeEventTarget;
|
||||
} else {
|
||||
this[propName] = nativeEvent[propName];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const defaultPrevented =
|
||||
nativeEvent.defaultPrevented != null
|
||||
? nativeEvent.defaultPrevented
|
||||
: nativeEvent.returnValue === false;
|
||||
if (defaultPrevented) {
|
||||
this.isDefaultPrevented = functionThatReturnsTrue;
|
||||
} else {
|
||||
this.isDefaultPrevented = functionThatReturnsFalse;
|
||||
}
|
||||
this.isPropagationStopped = functionThatReturnsFalse;
|
||||
return this;
|
||||
}
|
||||
|
||||
Object.assign(SyntheticEvent.prototype, {
|
||||
preventDefault: function() {
|
||||
this.defaultPrevented = true;
|
||||
const event = this.nativeEvent;
|
||||
if (!event) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.preventDefault) {
|
||||
event.preventDefault();
|
||||
} else if (typeof event.returnValue !== 'unknown') {
|
||||
event.returnValue = false;
|
||||
}
|
||||
this.isDefaultPrevented = functionThatReturnsTrue;
|
||||
},
|
||||
|
||||
stopPropagation: function() {
|
||||
const event = this.nativeEvent;
|
||||
if (!event) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.stopPropagation) {
|
||||
event.stopPropagation();
|
||||
} else if (typeof event.cancelBubble !== 'unknown') {
|
||||
// The ChangeEventPlugin registers a "propertychange" event for
|
||||
// IE. This event does not support bubbling or cancelling, and
|
||||
// any references to cancelBubble throw "Member not found". A
|
||||
// typeof check of "unknown" circumvents this issue (and is also
|
||||
// IE specific).
|
||||
event.cancelBubble = true;
|
||||
}
|
||||
|
||||
this.isPropagationStopped = functionThatReturnsTrue;
|
||||
},
|
||||
|
||||
/**
|
||||
* We release all dispatched `SyntheticEvent`s after each event loop, adding
|
||||
* them back into the pool. This allows a way to hold onto a reference that
|
||||
* won't be added back into the pool.
|
||||
*/
|
||||
persist: function() {
|
||||
// Modern event system doesn't use pooling.
|
||||
if (!enableModernEventSystem) {
|
||||
this.isPersistent = functionThatReturnsTrue;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if this event should be released back into the pool.
|
||||
*
|
||||
* @return {boolean} True if this should not be released, false otherwise.
|
||||
*/
|
||||
isPersistent: enableModernEventSystem
|
||||
? functionThatReturnsTrue
|
||||
: functionThatReturnsFalse,
|
||||
|
||||
/**
|
||||
* `PooledClass` looks for `destructor` on each instance it releases.
|
||||
*/
|
||||
destructor: function() {
|
||||
// Modern event system doesn't use pooling.
|
||||
if (!enableModernEventSystem) {
|
||||
const Interface = this.constructor.Interface;
|
||||
for (const propName in Interface) {
|
||||
if (__DEV__) {
|
||||
Object.defineProperty(
|
||||
this,
|
||||
propName,
|
||||
getPooledWarningPropertyDefinition(propName, Interface[propName]),
|
||||
);
|
||||
} else {
|
||||
this[propName] = null;
|
||||
}
|
||||
}
|
||||
this.dispatchConfig = null;
|
||||
this._targetInst = null;
|
||||
this.nativeEvent = null;
|
||||
this.isDefaultPrevented = functionThatReturnsFalse;
|
||||
this.isPropagationStopped = functionThatReturnsFalse;
|
||||
this._dispatchListeners = null;
|
||||
this._dispatchInstances = null;
|
||||
if (__DEV__) {
|
||||
Object.defineProperty(
|
||||
this,
|
||||
'nativeEvent',
|
||||
getPooledWarningPropertyDefinition('nativeEvent', null),
|
||||
);
|
||||
Object.defineProperty(
|
||||
this,
|
||||
'isDefaultPrevented',
|
||||
getPooledWarningPropertyDefinition(
|
||||
'isDefaultPrevented',
|
||||
functionThatReturnsFalse,
|
||||
),
|
||||
);
|
||||
Object.defineProperty(
|
||||
this,
|
||||
'isPropagationStopped',
|
||||
getPooledWarningPropertyDefinition(
|
||||
'isPropagationStopped',
|
||||
functionThatReturnsFalse,
|
||||
),
|
||||
);
|
||||
Object.defineProperty(
|
||||
this,
|
||||
'preventDefault',
|
||||
getPooledWarningPropertyDefinition('preventDefault', () => {}),
|
||||
);
|
||||
Object.defineProperty(
|
||||
this,
|
||||
'stopPropagation',
|
||||
getPooledWarningPropertyDefinition('stopPropagation', () => {}),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
SyntheticEvent.Interface = EventInterface;
|
||||
|
||||
/**
|
||||
* Helper to reduce boilerplate when creating subclasses.
|
||||
*/
|
||||
SyntheticEvent.extend = function(Interface) {
|
||||
const Super = this;
|
||||
|
||||
const E = function() {};
|
||||
E.prototype = Super.prototype;
|
||||
const prototype = new E();
|
||||
|
||||
function Class() {
|
||||
return Super.apply(this, arguments);
|
||||
}
|
||||
Object.assign(prototype, Class.prototype);
|
||||
Class.prototype = prototype;
|
||||
Class.prototype.constructor = Class;
|
||||
|
||||
Class.Interface = Object.assign({}, Super.Interface, Interface);
|
||||
Class.extend = Super.extend;
|
||||
addEventPoolingTo(Class);
|
||||
|
||||
return Class;
|
||||
};
|
||||
|
||||
addEventPoolingTo(SyntheticEvent);
|
||||
|
||||
/**
|
||||
* Helper to nullify syntheticEvent instance properties when destructing
|
||||
*
|
||||
* @param {String} propName
|
||||
* @param {?object} getVal
|
||||
* @return {object} defineProperty object
|
||||
*/
|
||||
function getPooledWarningPropertyDefinition(propName, getVal) {
|
||||
const isFunction = typeof getVal === 'function';
|
||||
return {
|
||||
configurable: true,
|
||||
set: set,
|
||||
get: get,
|
||||
};
|
||||
|
||||
function set(val) {
|
||||
const action = isFunction ? 'setting the method' : 'setting the property';
|
||||
warn(action, 'This is effectively a no-op');
|
||||
return val;
|
||||
}
|
||||
|
||||
function get() {
|
||||
const action = isFunction
|
||||
? 'accessing the method'
|
||||
: 'accessing the property';
|
||||
const result = isFunction
|
||||
? 'This is a no-op function'
|
||||
: 'This is set to null';
|
||||
warn(action, result);
|
||||
return getVal;
|
||||
}
|
||||
|
||||
function warn(action, result) {
|
||||
if (__DEV__) {
|
||||
console.error(
|
||||
"This synthetic event is reused for performance reasons. If you're seeing this, " +
|
||||
"you're %s `%s` on a released/nullified synthetic event. %s. " +
|
||||
'If you must keep the original synthetic event around, use event.persist(). ' +
|
||||
'See https://fb.me/react-event-pooling for more information.',
|
||||
action,
|
||||
propName,
|
||||
result,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createOrGetPooledEvent(
|
||||
dispatchConfig,
|
||||
targetInst,
|
||||
nativeEvent,
|
||||
nativeInst,
|
||||
) {
|
||||
const EventConstructor = this;
|
||||
// Modern event system doesn't use pooling.
|
||||
if (!enableModernEventSystem) {
|
||||
if (EventConstructor.eventPool.length) {
|
||||
const instance = EventConstructor.eventPool.pop();
|
||||
EventConstructor.call(
|
||||
instance,
|
||||
dispatchConfig,
|
||||
targetInst,
|
||||
nativeEvent,
|
||||
nativeInst,
|
||||
);
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
return new EventConstructor(
|
||||
dispatchConfig,
|
||||
targetInst,
|
||||
nativeEvent,
|
||||
nativeInst,
|
||||
);
|
||||
}
|
||||
|
||||
function releasePooledEvent(event) {
|
||||
// Modern event system doesn't use pooling.
|
||||
if (!enableModernEventSystem) {
|
||||
const EventConstructor = this;
|
||||
invariant(
|
||||
event instanceof EventConstructor,
|
||||
'Trying to release an event instance into a pool of a different type.',
|
||||
);
|
||||
event.destructor();
|
||||
if (EventConstructor.eventPool.length < EVENT_POOL_SIZE) {
|
||||
EventConstructor.eventPool.push(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addEventPoolingTo(EventConstructor) {
|
||||
EventConstructor.getPooled = createOrGetPooledEvent;
|
||||
|
||||
// Modern event system doesn't use pooling.
|
||||
if (!enableModernEventSystem) {
|
||||
EventConstructor.eventPool = [];
|
||||
EventConstructor.release = releasePooledEvent;
|
||||
}
|
||||
}
|
||||
|
||||
export default SyntheticEvent;
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
export type RNTopLevelEventType =
|
||||
| 'topMouseDown'
|
||||
| 'topMouseMove'
|
||||
| 'topMouseUp'
|
||||
| 'topScroll'
|
||||
| 'topSelectionChange'
|
||||
| 'topTouchCancel'
|
||||
| 'topTouchEnd'
|
||||
| 'topTouchMove'
|
||||
| 'topTouchStart';
|
||||
|
||||
export opaque type DOMTopLevelEventType = string;
|
||||
|
||||
// Do not use the below two methods directly!
|
||||
// Instead use constants exported from DOMTopLevelEventTypes in ReactDOM.
|
||||
// (It is the only module that is allowed to access these methods.)
|
||||
|
||||
export function unsafeCastStringToDOMTopLevelType(
|
||||
topLevelType: string,
|
||||
): DOMTopLevelEventType {
|
||||
return topLevelType;
|
||||
}
|
||||
|
||||
export function unsafeCastDOMTopLevelTypeToString(
|
||||
topLevelType: DOMTopLevelEventType,
|
||||
): string {
|
||||
return topLevelType;
|
||||
}
|
||||
|
||||
export type TopLevelType = DOMTopLevelEventType | RNTopLevelEventType;
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import invariant from 'shared/invariant';
|
||||
|
||||
/**
|
||||
* Accumulates items that must not be null or undefined.
|
||||
*
|
||||
* This is used to conserve memory by avoiding array allocations.
|
||||
*
|
||||
* @return {*|array<*>} An accumulation of items.
|
||||
*/
|
||||
function accumulate<T>(
|
||||
current: ?(T | Array<T>),
|
||||
next: T | Array<T>,
|
||||
): T | Array<T> {
|
||||
invariant(
|
||||
next != null,
|
||||
'accumulate(...): Accumulated items must not be null or undefined.',
|
||||
);
|
||||
|
||||
if (current == null) {
|
||||
return next;
|
||||
}
|
||||
|
||||
// Both are not empty. Warning: Never call x.concat(y) when you are not
|
||||
// certain that x is an Array (x could be a string with concat method).
|
||||
if (Array.isArray(current)) {
|
||||
return current.concat(next);
|
||||
}
|
||||
|
||||
if (Array.isArray(next)) {
|
||||
return [current].concat(next);
|
||||
}
|
||||
|
||||
return [current, next];
|
||||
}
|
||||
|
||||
export default accumulate;
|
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import invariant from 'shared/invariant';
|
||||
|
||||
/**
|
||||
* Accumulates items that must not be null or undefined into the first one. This
|
||||
* is used to conserve memory by avoiding array allocations, and thus sacrifices
|
||||
* API cleanness. Since `current` can be null before being passed in and not
|
||||
* null after this function, make sure to assign it back to `current`:
|
||||
*
|
||||
* `a = accumulateInto(a, b);`
|
||||
*
|
||||
* This API should be sparingly used. Try `accumulate` for something cleaner.
|
||||
*
|
||||
* @return {*|array<*>} An accumulation of items.
|
||||
*/
|
||||
|
||||
function accumulateInto<T>(
|
||||
current: ?(Array<T> | T),
|
||||
next: T | Array<T>,
|
||||
): T | Array<T> {
|
||||
invariant(
|
||||
next != null,
|
||||
'accumulateInto(...): Accumulated items must not be null or undefined.',
|
||||
);
|
||||
|
||||
if (current == null) {
|
||||
return next;
|
||||
}
|
||||
|
||||
// Both are not empty. Warning: Never call x.concat(y) when you are not
|
||||
// certain that x is an Array (x could be a string with concat method).
|
||||
if (Array.isArray(current)) {
|
||||
if (Array.isArray(next)) {
|
||||
current.push.apply(current, next);
|
||||
return current;
|
||||
}
|
||||
current.push(next);
|
||||
return current;
|
||||
}
|
||||
|
||||
if (Array.isArray(next)) {
|
||||
// A bit too dangerous to mutate `next`.
|
||||
return [current].concat(next);
|
||||
}
|
||||
|
||||
return [current, next];
|
||||
}
|
||||
|
||||
export default accumulateInto;
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {array} arr an "accumulation" of items which is either an Array or
|
||||
* a single item. Useful when paired with the `accumulate` module. This is a
|
||||
* simple utility that allows us to reason about a collection of items, but
|
||||
* handling the case when there is exactly one item (and we do not need to
|
||||
* allocate an array).
|
||||
* @param {function} cb Callback invoked with each element or a collection.
|
||||
* @param {?} [scope] Scope used as `this` in a callback.
|
||||
*/
|
||||
function forEachAccumulated<T>(
|
||||
arr: ?(Array<T> | T),
|
||||
cb: (elem: T) => void,
|
||||
scope: ?any,
|
||||
) {
|
||||
if (Array.isArray(arr)) {
|
||||
arr.forEach(cb, scope);
|
||||
} else if (arr) {
|
||||
cb.call(scope, arr);
|
||||
}
|
||||
}
|
||||
|
||||
export default forEachAccumulated;
|
|
@ -16,7 +16,7 @@ import type {
|
|||
ReactNativeBaseComponentViewConfig,
|
||||
ViewConfigGetter,
|
||||
} from 'react-native-renderer/src/ReactNativeTypes';
|
||||
import type {RNTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
|
||||
import type {RNTopLevelEventType} from 'react-native-renderer/src/legacy-events/TopLevelEventTypes';
|
||||
import type {CapturedError} from 'react-reconciler/src/ReactCapturedValue';
|
||||
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@ const esNextPaths = [
|
|||
'packages/dom-event-testing-library/**/*.js',
|
||||
'packages/react-interactions/**/*.js',
|
||||
'packages/react-interactions/**/*.js',
|
||||
'packages/legacy-events/**/*.js',
|
||||
'packages/shared/**/*.js',
|
||||
// Shims and Flow environment
|
||||
'scripts/flow/*.js',
|
||||
|
|
Loading…
Reference in New Issue