mirror of
https://github.com/zhigang1992/react-native-web.git
synced 2026-04-23 04:00:04 +08:00
[add] NativeEventEmitter export
Export NativeEventEmitter and provide React Native's implementation.
This commit is contained in:
@@ -63,7 +63,9 @@
|
||||
],
|
||||
"lint-staged": {
|
||||
"packages/react-native-web/src/index.js": [
|
||||
"node ./scripts/babel/createModuleMap.js"
|
||||
"node ./scripts/babel/createModuleMap.js",
|
||||
"prettier --write ./packages/babel-plugin-react-native-web/src/moduleMap.js",
|
||||
"git add ./packages/babel-plugin-react-native-web/src/moduleMap.js"
|
||||
],
|
||||
"**/*.js": [
|
||||
"prettier --write",
|
||||
|
||||
@@ -27,6 +27,7 @@ module.exports = {
|
||||
Linking: true,
|
||||
ListView: true,
|
||||
Modal: true,
|
||||
NativeEventEmitter: true,
|
||||
NativeModules: true,
|
||||
NetInfo: true,
|
||||
PanResponder: true,
|
||||
|
||||
11
packages/react-native-web/src/exports/NativeEventEmitter/index.js
vendored
Normal file
11
packages/react-native-web/src/exports/NativeEventEmitter/index.js
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Copyright (c) 2016-present, Nicolas Gallagher.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import NativeEventEmitter from '../../vendor/react-native/NativeEventEmitter';
|
||||
export default NativeEventEmitter;
|
||||
3
packages/react-native-web/src/index.js
vendored
3
packages/react-native-web/src/index.js
vendored
@@ -23,6 +23,7 @@ import Keyboard from './exports/Keyboard';
|
||||
import InteractionManager from './exports/InteractionManager';
|
||||
import LayoutAnimation from './exports/LayoutAnimation';
|
||||
import Linking from './exports/Linking';
|
||||
import NativeEventEmitter from './exports/NativeEventEmitter';
|
||||
import NetInfo from './exports/NetInfo';
|
||||
import PanResponder from './exports/PanResponder';
|
||||
import PixelRatio from './exports/PixelRatio';
|
||||
@@ -93,6 +94,7 @@ export {
|
||||
Keyboard,
|
||||
LayoutAnimation,
|
||||
Linking,
|
||||
NativeEventEmitter,
|
||||
NetInfo,
|
||||
PanResponder,
|
||||
PixelRatio,
|
||||
@@ -162,6 +164,7 @@ const ReactNative = {
|
||||
Keyboard,
|
||||
LayoutAnimation,
|
||||
Linking,
|
||||
NativeEventEmitter,
|
||||
NetInfo,
|
||||
PanResponder,
|
||||
PixelRatio,
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @noflow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
class NativeEventEmitter {
|
||||
addListener() {}
|
||||
emit() {}
|
||||
listeners() {}
|
||||
once() {}
|
||||
removeAllListeners() {}
|
||||
removeCurrentListener() {}
|
||||
removeListener() {}
|
||||
removeSubscription() {}
|
||||
}
|
||||
|
||||
export default NativeEventEmitter;
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import NativeModules from '../../../exports/NativeModules';
|
||||
import NativeEventEmitter from '../../../modules/NativeEventEmitter';
|
||||
import NativeEventEmitter from '../NativeEventEmitter';
|
||||
|
||||
import type {AnimationConfig} from './animations/Animation';
|
||||
import type {EventConfig} from './AnimatedEvent';
|
||||
|
||||
67
packages/react-native-web/src/vendor/react-native/NativeEventEmitter/index.js
vendored
Normal file
67
packages/react-native-web/src/vendor/react-native/NativeEventEmitter/index.js
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule NativeEventEmitter
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
import EventEmitter from '../emitter/EventEmitter';
|
||||
import Platform from '../../../exports/Platform';
|
||||
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
|
||||
import type EmitterSubscription from '../emitter/EmitterSubscription';
|
||||
|
||||
type NativeModule = {
|
||||
+addListener: (eventType: string) => void,
|
||||
+removeListeners: (count: number) => void,
|
||||
};
|
||||
|
||||
/**
|
||||
* Abstract base class for implementing event-emitting modules. This implements
|
||||
* a subset of the standard EventEmitter node module API.
|
||||
*/
|
||||
class NativeEventEmitter extends EventEmitter {
|
||||
_nativeModule: ?NativeModule;
|
||||
|
||||
constructor(nativeModule: ?NativeModule) {
|
||||
super();
|
||||
if (Platform.OS === 'ios') {
|
||||
invariant(nativeModule, 'Native module cannot be null.');
|
||||
this._nativeModule = nativeModule;
|
||||
}
|
||||
}
|
||||
|
||||
addListener(
|
||||
eventType: string,
|
||||
listener: Function,
|
||||
context: ?Object,
|
||||
): EmitterSubscription {
|
||||
if (this._nativeModule != null) {
|
||||
this._nativeModule.addListener(eventType);
|
||||
}
|
||||
return super.addListener(eventType, listener, context);
|
||||
}
|
||||
|
||||
removeAllListeners(eventType: string) {
|
||||
invariant(eventType, 'eventType argument is required.');
|
||||
const count = this.listeners(eventType).length;
|
||||
if (this._nativeModule != null) {
|
||||
this._nativeModule.removeListeners(count);
|
||||
}
|
||||
super.removeAllListeners(eventType);
|
||||
}
|
||||
|
||||
removeSubscription(subscription: EmitterSubscription) {
|
||||
if (this._nativeModule != null) {
|
||||
this._nativeModule.removeListeners(1);
|
||||
}
|
||||
super.removeSubscription(subscription);
|
||||
}
|
||||
}
|
||||
|
||||
export default NativeEventEmitter;
|
||||
59
packages/react-native-web/src/vendor/react-native/emitter/EmitterSubscription.js
vendored
Normal file
59
packages/react-native-web/src/vendor/react-native/emitter/EmitterSubscription.js
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule EmitterSubscription
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
import EventSubscription from './EventSubscription';
|
||||
|
||||
import type EventEmitter from './EventEmitter';
|
||||
import type EventSubscriptionVendor from './EventSubscriptionVendor';
|
||||
|
||||
/**
|
||||
* EmitterSubscription represents a subscription with listener and context data.
|
||||
*/
|
||||
class EmitterSubscription extends EventSubscription {
|
||||
|
||||
emitter: EventEmitter;
|
||||
listener: Function;
|
||||
context: ?Object;
|
||||
|
||||
/**
|
||||
* @param {EventEmitter} emitter - The event emitter that registered this
|
||||
* subscription
|
||||
* @param {EventSubscriptionVendor} subscriber - The subscriber that controls
|
||||
* this subscription
|
||||
* @param {function} listener - Function to invoke when the specified event is
|
||||
* emitted
|
||||
* @param {*} context - Optional context object to use when invoking the
|
||||
* listener
|
||||
*/
|
||||
constructor(
|
||||
emitter: EventEmitter,
|
||||
subscriber: EventSubscriptionVendor,
|
||||
listener: Function,
|
||||
context: ?Object
|
||||
) {
|
||||
super(subscriber);
|
||||
this.emitter = emitter;
|
||||
this.listener = listener;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes this subscription from the emitter that registered it.
|
||||
* Note: we're overriding the `remove()` method of EventSubscription here
|
||||
* but deliberately not calling `super.remove()` as the responsibility
|
||||
* for removing the subscription lies with the EventEmitter.
|
||||
*/
|
||||
remove() {
|
||||
this.emitter.removeSubscription(this);
|
||||
}
|
||||
}
|
||||
|
||||
export default EmitterSubscription;
|
||||
218
packages/react-native-web/src/vendor/react-native/emitter/EventEmitter.js
vendored
Normal file
218
packages/react-native-web/src/vendor/react-native/emitter/EventEmitter.js
vendored
Normal file
@@ -0,0 +1,218 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule EventEmitter
|
||||
* @noflow
|
||||
* @typecheck
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
import EmitterSubscription from './EmitterSubscription';
|
||||
import EventSubscriptionVendor from './EventSubscriptionVendor';
|
||||
|
||||
import emptyFunction from 'fbjs/lib/emptyFunction';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
|
||||
/**
|
||||
* @class EventEmitter
|
||||
* @description
|
||||
* An EventEmitter is responsible for managing a set of listeners and publishing
|
||||
* events to them when it is told that such events happened. In addition to the
|
||||
* data for the given event it also sends a event control object which allows
|
||||
* the listeners/handlers to prevent the default behavior of the given event.
|
||||
*
|
||||
* The emitter is designed to be generic enough to support all the different
|
||||
* contexts in which one might want to emit events. It is a simple multicast
|
||||
* mechanism on top of which extra functionality can be composed. For example, a
|
||||
* more advanced emitter may use an EventHolder and EventFactory.
|
||||
*/
|
||||
class EventEmitter {
|
||||
|
||||
_subscriber: EventSubscriptionVendor;
|
||||
_currentSubscription: ?EmitterSubscription;
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*
|
||||
* @param {EventSubscriptionVendor} subscriber - Optional subscriber instance
|
||||
* to use. If omitted, a new subscriber will be created for the emitter.
|
||||
*/
|
||||
constructor(subscriber: ?EventSubscriptionVendor) {
|
||||
this._subscriber = subscriber || new EventSubscriptionVendor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener to be invoked when events of the specified type are
|
||||
* emitted. An optional calling context may be provided. The data arguments
|
||||
* emitted will be passed to the listener function.
|
||||
*
|
||||
* TODO: Annotate the listener arg's type. This is tricky because listeners
|
||||
* can be invoked with varargs.
|
||||
*
|
||||
* @param {string} eventType - Name of the event to listen to
|
||||
* @param {function} listener - Function to invoke when the specified event is
|
||||
* emitted
|
||||
* @param {*} context - Optional context object to use when invoking the
|
||||
* listener
|
||||
*/
|
||||
addListener(
|
||||
eventType: string, listener: Function, context: ?Object): EmitterSubscription {
|
||||
|
||||
return (this._subscriber.addSubscription(
|
||||
eventType,
|
||||
new EmitterSubscription(this, this._subscriber, listener, context)
|
||||
) : any);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to addListener, except that the listener is removed after it is
|
||||
* invoked once.
|
||||
*
|
||||
* @param {string} eventType - Name of the event to listen to
|
||||
* @param {function} listener - Function to invoke only once when the
|
||||
* specified event is emitted
|
||||
* @param {*} context - Optional context object to use when invoking the
|
||||
* listener
|
||||
*/
|
||||
once(eventType: string, listener: Function, context: ?Object): EmitterSubscription {
|
||||
return this.addListener(eventType, (...args) => {
|
||||
this.removeCurrentListener();
|
||||
listener.apply(context, args);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all of the registered listeners, including those registered as
|
||||
* listener maps.
|
||||
*
|
||||
* @param {?string} eventType - Optional name of the event whose registered
|
||||
* listeners to remove
|
||||
*/
|
||||
removeAllListeners(eventType: ?string) {
|
||||
this._subscriber.removeAllSubscriptions(eventType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an API that can be called during an eventing cycle to remove the
|
||||
* last listener that was invoked. This allows a developer to provide an event
|
||||
* object that can remove the listener (or listener map) during the
|
||||
* invocation.
|
||||
*
|
||||
* If it is called when not inside of an emitting cycle it will throw.
|
||||
*
|
||||
* @throws {Error} When called not during an eventing cycle
|
||||
*
|
||||
* @example
|
||||
* var subscription = emitter.addListenerMap({
|
||||
* someEvent: function(data, event) {
|
||||
* console.log(data);
|
||||
* emitter.removeCurrentListener();
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* emitter.emit('someEvent', 'abc'); // logs 'abc'
|
||||
* emitter.emit('someEvent', 'def'); // does not log anything
|
||||
*/
|
||||
removeCurrentListener() {
|
||||
invariant(
|
||||
!!this._currentSubscription,
|
||||
'Not in an emitting cycle; there is no current subscription'
|
||||
);
|
||||
this.removeSubscription(this._currentSubscription);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a specific subscription. Called by the `remove()` method of the
|
||||
* subscription itself to ensure any necessary cleanup is performed.
|
||||
*/
|
||||
removeSubscription(subscription: EmitterSubscription) {
|
||||
invariant(
|
||||
subscription.emitter === this,
|
||||
'Subscription does not belong to this emitter.'
|
||||
);
|
||||
this._subscriber.removeSubscription(subscription);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of listeners that are currently registered for the given
|
||||
* event.
|
||||
*
|
||||
* @param {string} eventType - Name of the event to query
|
||||
* @returns {array}
|
||||
*/
|
||||
listeners(eventType: string): [EmitterSubscription] {
|
||||
const subscriptions: ?[EmitterSubscription] = (this._subscriber.getSubscriptionsForType(eventType): any);
|
||||
return subscriptions
|
||||
? subscriptions.filter(emptyFunction.thatReturnsTrue).map(
|
||||
function(subscription) {
|
||||
return subscription.listener;
|
||||
})
|
||||
: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits an event of the given type with the given data. All handlers of that
|
||||
* particular type will be notified.
|
||||
*
|
||||
* @param {string} eventType - Name of the event to emit
|
||||
* @param {...*} Arbitrary arguments to be passed to each registered listener
|
||||
*
|
||||
* @example
|
||||
* emitter.addListener('someEvent', function(message) {
|
||||
* console.log(message);
|
||||
* });
|
||||
*
|
||||
* emitter.emit('someEvent', 'abc'); // logs 'abc'
|
||||
*/
|
||||
emit(eventType: string) {
|
||||
const subscriptions: ?[EmitterSubscription] = (this._subscriber.getSubscriptionsForType(eventType): any);
|
||||
if (subscriptions) {
|
||||
for (let i = 0, l = subscriptions.length; i < l; i++) {
|
||||
const subscription = subscriptions[i];
|
||||
|
||||
// The subscription may have been removed during this event loop.
|
||||
if (subscription) {
|
||||
this._currentSubscription = subscription;
|
||||
subscription.listener.apply(
|
||||
subscription.context,
|
||||
Array.prototype.slice.call(arguments, 1)
|
||||
);
|
||||
}
|
||||
}
|
||||
this._currentSubscription = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given listener for event of specific type.
|
||||
*
|
||||
* @param {string} eventType - Name of the event to emit
|
||||
* @param {function} listener - Function to invoke when the specified event is
|
||||
* emitted
|
||||
*
|
||||
* @example
|
||||
* emitter.removeListener('someEvent', function(message) {
|
||||
* console.log(message);
|
||||
* }); // removes the listener if already registered
|
||||
*
|
||||
*/
|
||||
removeListener(eventType: String, listener) {
|
||||
const subscriptions: ?[EmitterSubscription] = (this._subscriber.getSubscriptionsForType(eventType): any);
|
||||
if (subscriptions) {
|
||||
for (let i = 0, l = subscriptions.length; i < l; i++) {
|
||||
const subscription = subscriptions[i];
|
||||
|
||||
// The subscription may have been removed during this event loop.
|
||||
// its listener matches the listener in method parameters
|
||||
if (subscription && subscription.listener === listener) {
|
||||
subscription.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default EventEmitter;
|
||||
163
packages/react-native-web/src/vendor/react-native/emitter/EventEmitterWithHolding.js
vendored
Normal file
163
packages/react-native-web/src/vendor/react-native/emitter/EventEmitterWithHolding.js
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule EventEmitterWithHolding
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
import type EmitterSubscription from './EmitterSubscription';
|
||||
import type EventEmitter from './EventEmitter';
|
||||
import type EventHolder from './EventHolder';
|
||||
|
||||
/**
|
||||
* @class EventEmitterWithHolding
|
||||
* @description
|
||||
* An EventEmitterWithHolding decorates an event emitter and enables one to
|
||||
* "hold" or cache events and then have a handler register later to actually
|
||||
* handle them.
|
||||
*
|
||||
* This is separated into its own decorator so that only those who want to use
|
||||
* the holding functionality have to and others can just use an emitter. Since
|
||||
* it implements the emitter interface it can also be combined with anything
|
||||
* that uses an emitter.
|
||||
*/
|
||||
class EventEmitterWithHolding {
|
||||
|
||||
_emitter: EventEmitter;
|
||||
_eventHolder: EventHolder;
|
||||
_currentEventToken: ?Object;
|
||||
_emittingHeldEvents: boolean;
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {object} emitter - The object responsible for emitting the actual
|
||||
* events.
|
||||
* @param {object} holder - The event holder that is responsible for holding
|
||||
* and then emitting held events.
|
||||
*/
|
||||
constructor(emitter: EventEmitter, holder: EventHolder) {
|
||||
this._emitter = emitter;
|
||||
this._eventHolder = holder;
|
||||
this._currentEventToken = null;
|
||||
this._emittingHeldEvents = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see EventEmitter#addListener
|
||||
*/
|
||||
addListener(eventType: string, listener: Function, context: ?Object) {
|
||||
return this._emitter.addListener(eventType, listener, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see EventEmitter#once
|
||||
*/
|
||||
once(eventType: string, listener: Function, context: ?Object) {
|
||||
return this._emitter.once(eventType, listener, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener to be invoked when events of the specified type are
|
||||
* emitted. An optional calling context may be provided. The data arguments
|
||||
* emitted will be passed to the listener function. In addition to subscribing
|
||||
* to all subsequent events, this method will also handle any events that have
|
||||
* already been emitted, held, and not released.
|
||||
*
|
||||
* @param {string} eventType - Name of the event to listen to
|
||||
* @param {function} listener - Function to invoke when the specified event is
|
||||
* emitted
|
||||
* @param {*} context - Optional context object to use when invoking the
|
||||
* listener
|
||||
*
|
||||
* @example
|
||||
* emitter.emitAndHold('someEvent', 'abc');
|
||||
*
|
||||
* emitter.addRetroactiveListener('someEvent', function(message) {
|
||||
* console.log(message);
|
||||
* }); // logs 'abc'
|
||||
*/
|
||||
addRetroactiveListener(
|
||||
eventType: string, listener: Function, context: ?Object): EmitterSubscription {
|
||||
const subscription = this._emitter.addListener(eventType, listener, context);
|
||||
|
||||
this._emittingHeldEvents = true;
|
||||
this._eventHolder.emitToListener(eventType, listener, context);
|
||||
this._emittingHeldEvents = false;
|
||||
|
||||
return subscription;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see EventEmitter#removeAllListeners
|
||||
*/
|
||||
removeAllListeners(eventType: string) {
|
||||
this._emitter.removeAllListeners(eventType);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see EventEmitter#removeCurrentListener
|
||||
*/
|
||||
removeCurrentListener() {
|
||||
this._emitter.removeCurrentListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see EventEmitter#listeners
|
||||
*/
|
||||
listeners(eventType: string) /* TODO: Annotate return type here */ {
|
||||
return this._emitter.listeners(eventType);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see EventEmitter#emit
|
||||
*/
|
||||
emit(eventType: string, ...args: any) {
|
||||
this._emitter.emit(eventType, ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits an event of the given type with the given data, and holds that event
|
||||
* in order to be able to dispatch it to a later subscriber when they say they
|
||||
* want to handle held events.
|
||||
*
|
||||
* @param {string} eventType - Name of the event to emit
|
||||
* @param {...*} Arbitrary arguments to be passed to each registered listener
|
||||
*
|
||||
* @example
|
||||
* emitter.emitAndHold('someEvent', 'abc');
|
||||
*
|
||||
* emitter.addRetroactiveListener('someEvent', function(message) {
|
||||
* console.log(message);
|
||||
* }); // logs 'abc'
|
||||
*/
|
||||
emitAndHold(eventType: string, ...args: any) {
|
||||
this._currentEventToken = this._eventHolder.holdEvent(eventType, ...args);
|
||||
this._emitter.emit(eventType, ...args);
|
||||
this._currentEventToken = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see EventHolder#releaseCurrentEvent
|
||||
*/
|
||||
releaseCurrentEvent() {
|
||||
if (this._currentEventToken) {
|
||||
this._eventHolder.releaseEvent(this._currentEventToken);
|
||||
} else if (this._emittingHeldEvents) {
|
||||
this._eventHolder.releaseCurrentEvent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see EventHolder#releaseEventType
|
||||
* @param {string} eventType
|
||||
*/
|
||||
releaseHeldEventType(eventType: string) {
|
||||
this._eventHolder.releaseEventType(eventType);
|
||||
}
|
||||
}
|
||||
|
||||
export default EventEmitterWithHolding;
|
||||
119
packages/react-native-web/src/vendor/react-native/emitter/EventHolder.js
vendored
Normal file
119
packages/react-native-web/src/vendor/react-native/emitter/EventHolder.js
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule EventHolder
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
|
||||
class EventHolder {
|
||||
|
||||
_heldEvents: Object;
|
||||
_currentEventKey: ?Object;
|
||||
|
||||
constructor() {
|
||||
this._heldEvents = {};
|
||||
this._currentEventKey = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds a given event for processing later.
|
||||
*
|
||||
* TODO: Annotate return type better. The structural type of the return here
|
||||
* is pretty obvious.
|
||||
*
|
||||
* @param {string} eventType - Name of the event to hold and later emit
|
||||
* @param {...*} Arbitrary arguments to be passed to each registered listener
|
||||
* @return {object} Token that can be used to release the held event
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* holder.holdEvent({someEvent: 'abc'});
|
||||
*
|
||||
* holder.emitToHandler({
|
||||
* someEvent: function(data, event) {
|
||||
* console.log(data);
|
||||
* }
|
||||
* }); //logs 'abc'
|
||||
*
|
||||
*/
|
||||
holdEvent(eventType: string, ...args: any) {
|
||||
this._heldEvents[eventType] = this._heldEvents[eventType] || [];
|
||||
const eventsOfType = this._heldEvents[eventType];
|
||||
const key = {
|
||||
eventType: eventType,
|
||||
index: eventsOfType.length
|
||||
};
|
||||
eventsOfType.push(args);
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits the held events of the specified type to the given listener.
|
||||
*
|
||||
* @param {?string} eventType - Optional name of the events to replay
|
||||
* @param {function} listener - The listener to which to dispatch the event
|
||||
* @param {?object} context - Optional context object to use when invoking
|
||||
* the listener
|
||||
*/
|
||||
emitToListener(eventType: ?string , listener: Function, context: ?Object) {
|
||||
const eventsOfType = this._heldEvents[eventType];
|
||||
if (!eventsOfType) {
|
||||
return;
|
||||
}
|
||||
const origEventKey = this._currentEventKey;
|
||||
eventsOfType.forEach((/*?array*/ eventHeld, /*number*/ index) => {
|
||||
if (!eventHeld) {
|
||||
return;
|
||||
}
|
||||
this._currentEventKey = {
|
||||
eventType: eventType,
|
||||
index: index
|
||||
};
|
||||
listener.apply(context, eventHeld);
|
||||
});
|
||||
this._currentEventKey = origEventKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an API that can be called during an eventing cycle to release
|
||||
* the last event that was invoked, so that it is no longer "held".
|
||||
*
|
||||
* If it is called when not inside of an emitting cycle it will throw.
|
||||
*
|
||||
* @throws {Error} When called not during an eventing cycle
|
||||
*/
|
||||
releaseCurrentEvent() {
|
||||
invariant(
|
||||
this._currentEventKey !== null,
|
||||
'Not in an emitting cycle; there is no current event'
|
||||
);
|
||||
this._currentEventKey && this.releaseEvent(this._currentEventKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the event corresponding to the handle that was returned when the
|
||||
* event was first held.
|
||||
*
|
||||
* @param {object} token - The token returned from holdEvent
|
||||
*/
|
||||
releaseEvent(token: Object) {
|
||||
delete this._heldEvents[token.eventType][token.index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases all events of a certain type.
|
||||
*
|
||||
* @param {string} type
|
||||
*/
|
||||
releaseEventType(type: string) {
|
||||
this._heldEvents[type] = [];
|
||||
}
|
||||
}
|
||||
|
||||
export default EventHolder;
|
||||
40
packages/react-native-web/src/vendor/react-native/emitter/EventSubscription.js
vendored
Normal file
40
packages/react-native-web/src/vendor/react-native/emitter/EventSubscription.js
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule EventSubscription
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
import type EventSubscriptionVendor from './EventSubscriptionVendor';
|
||||
|
||||
/**
|
||||
* EventSubscription represents a subscription to a particular event. It can
|
||||
* remove its own subscription.
|
||||
*/
|
||||
class EventSubscription {
|
||||
|
||||
eventType: string;
|
||||
key: number;
|
||||
subscriber: EventSubscriptionVendor;
|
||||
|
||||
/**
|
||||
* @param {EventSubscriptionVendor} subscriber the subscriber that controls
|
||||
* this subscription.
|
||||
*/
|
||||
constructor(subscriber: EventSubscriptionVendor) {
|
||||
this.subscriber = subscriber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes this subscription from the subscriber that controls it.
|
||||
*/
|
||||
remove() {
|
||||
this.subscriber.removeSubscription(this);
|
||||
}
|
||||
}
|
||||
|
||||
export default EventSubscription;
|
||||
98
packages/react-native-web/src/vendor/react-native/emitter/EventSubscriptionVendor.js
vendored
Normal file
98
packages/react-native-web/src/vendor/react-native/emitter/EventSubscriptionVendor.js
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule EventSubscriptionVendor
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
|
||||
import type EventSubscription from './EventSubscription';
|
||||
|
||||
/**
|
||||
* EventSubscriptionVendor stores a set of EventSubscriptions that are
|
||||
* subscribed to a particular event type.
|
||||
*/
|
||||
class EventSubscriptionVendor {
|
||||
|
||||
_subscriptionsForType: Object;
|
||||
_currentSubscription: ?EventSubscription;
|
||||
|
||||
constructor() {
|
||||
this._subscriptionsForType = {};
|
||||
this._currentSubscription = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a subscription keyed by an event type.
|
||||
*
|
||||
* @param {string} eventType
|
||||
* @param {EventSubscription} subscription
|
||||
*/
|
||||
addSubscription(
|
||||
eventType: string, subscription: EventSubscription): EventSubscription {
|
||||
invariant(
|
||||
subscription.subscriber === this,
|
||||
'The subscriber of the subscription is incorrectly set.');
|
||||
if (!this._subscriptionsForType[eventType]) {
|
||||
this._subscriptionsForType[eventType] = [];
|
||||
}
|
||||
const key = this._subscriptionsForType[eventType].length;
|
||||
this._subscriptionsForType[eventType].push(subscription);
|
||||
subscription.eventType = eventType;
|
||||
subscription.key = key;
|
||||
return subscription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a bulk set of the subscriptions.
|
||||
*
|
||||
* @param {?string} eventType - Optional name of the event type whose
|
||||
* registered supscriptions to remove, if null remove all subscriptions.
|
||||
*/
|
||||
removeAllSubscriptions(eventType: ?string) {
|
||||
if (eventType === undefined) {
|
||||
this._subscriptionsForType = {};
|
||||
} else {
|
||||
delete this._subscriptionsForType[eventType];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a specific subscription. Instead of calling this function, call
|
||||
* `subscription.remove()` directly.
|
||||
*
|
||||
* @param {object} subscription
|
||||
*/
|
||||
removeSubscription(subscription: Object) {
|
||||
const eventType = subscription.eventType;
|
||||
const key = subscription.key;
|
||||
|
||||
const subscriptionsForType = this._subscriptionsForType[eventType];
|
||||
if (subscriptionsForType) {
|
||||
delete subscriptionsForType[key];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array of subscriptions that are currently registered for the
|
||||
* given event type.
|
||||
*
|
||||
* Note: This array can be potentially sparse as subscriptions are deleted
|
||||
* from it when they are removed.
|
||||
*
|
||||
* TODO: This returns a nullable array. wat?
|
||||
*
|
||||
* @param {string} eventType
|
||||
* @returns {?array}
|
||||
*/
|
||||
getSubscriptionsForType(eventType: string): ?[EventSubscription] {
|
||||
return this._subscriptionsForType[eventType];
|
||||
}
|
||||
}
|
||||
|
||||
export default EventSubscriptionVendor;
|
||||
137
packages/react-native-web/src/vendor/react-native/emitter/EventValidator.js
vendored
Normal file
137
packages/react-native-web/src/vendor/react-native/emitter/EventValidator.js
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule EventValidator
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const __DEV__ = process.env.NODE_ENV !== 'production';
|
||||
|
||||
/**
|
||||
* EventValidator is designed to validate event types to make it easier to catch
|
||||
* common mistakes. It accepts a map of all of the different types of events
|
||||
* that the emitter can emit. Then, if a user attempts to emit an event that is
|
||||
* not one of those specified types the emitter will throw an error. Also, it
|
||||
* provides a relatively simple matcher so that if it thinks that you likely
|
||||
* mistyped the event name it will suggest what you might have meant to type in
|
||||
* the error message.
|
||||
*/
|
||||
const EventValidator = {
|
||||
/**
|
||||
* @param {Object} emitter - The object responsible for emitting the actual
|
||||
* events
|
||||
* @param {Object} types - The collection of valid types that will be used to
|
||||
* check for errors
|
||||
* @return {Object} A new emitter with event type validation
|
||||
* @example
|
||||
* const types = {someEvent: true, anotherEvent: true};
|
||||
* const emitter = EventValidator.addValidation(emitter, types);
|
||||
*/
|
||||
addValidation: function(emitter: Object, types: Object) {
|
||||
const eventTypes = Object.keys(types);
|
||||
const emitterWithValidation = Object.create(emitter);
|
||||
|
||||
Object.assign(emitterWithValidation, {
|
||||
emit: function emit(type, a, b, c, d, e, _) {
|
||||
assertAllowsEventType(type, eventTypes);
|
||||
return emitter.emit.call(this, type, a, b, c, d, e, _);
|
||||
}
|
||||
});
|
||||
|
||||
return emitterWithValidation;
|
||||
}
|
||||
};
|
||||
|
||||
function assertAllowsEventType(type, allowedTypes) {
|
||||
if (allowedTypes.indexOf(type) === -1) {
|
||||
throw new TypeError(errorMessageFor(type, allowedTypes));
|
||||
}
|
||||
}
|
||||
|
||||
function errorMessageFor(type, allowedTypes) {
|
||||
let message = 'Unknown event type "' + type + '". ';
|
||||
if (__DEV__) {
|
||||
message += recommendationFor(type, allowedTypes);
|
||||
}
|
||||
message += 'Known event types: ' + allowedTypes.join(', ') + '.';
|
||||
return message;
|
||||
}
|
||||
|
||||
// Allow for good error messages
|
||||
if (__DEV__) {
|
||||
var recommendationFor = function (type, allowedTypes) {
|
||||
const closestTypeRecommendation = closestTypeFor(type, allowedTypes);
|
||||
if (isCloseEnough(closestTypeRecommendation, type)) {
|
||||
return 'Did you mean "' + closestTypeRecommendation.type + '"? ';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
var closestTypeFor = function (type, allowedTypes) {
|
||||
const typeRecommendations = allowedTypes.map(
|
||||
typeRecommendationFor.bind(this, type)
|
||||
);
|
||||
return typeRecommendations.sort(recommendationSort)[0];
|
||||
};
|
||||
|
||||
var typeRecommendationFor = function (type, recommendedType) {
|
||||
return {
|
||||
type: recommendedType,
|
||||
distance: damerauLevenshteinDistance(type, recommendedType)
|
||||
};
|
||||
};
|
||||
|
||||
var recommendationSort = function (recommendationA, recommendationB) {
|
||||
if (recommendationA.distance < recommendationB.distance) {
|
||||
return -1;
|
||||
} else if (recommendationA.distance > recommendationB.distance) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
var isCloseEnough = function (closestType, actualType) {
|
||||
return (closestType.distance / actualType.length) < 0.334;
|
||||
};
|
||||
|
||||
var damerauLevenshteinDistance = function (a, b) {
|
||||
let i, j;
|
||||
const d = [];
|
||||
|
||||
for (i = 0; i <= a.length; i++) {
|
||||
d[i] = [i];
|
||||
}
|
||||
|
||||
for (j = 1; j <= b.length; j++) {
|
||||
d[0][j] = j;
|
||||
}
|
||||
|
||||
for (i = 1; i <= a.length; i++) {
|
||||
for (j = 1; j <= b.length; j++) {
|
||||
const cost = a.charAt(i - 1) === b.charAt(j - 1) ? 0 : 1;
|
||||
|
||||
d[i][j] = Math.min(
|
||||
d[i - 1][j] + 1,
|
||||
d[i][j - 1] + 1,
|
||||
d[i - 1][j - 1] + cost
|
||||
);
|
||||
|
||||
if (i > 1 && j > 1 &&
|
||||
a.charAt(i - 1) === b.charAt(j - 2) &&
|
||||
a.charAt(i - 2) === b.charAt(j - 1)) {
|
||||
d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + cost);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return d[a.length][b.length];
|
||||
};
|
||||
}
|
||||
|
||||
export default EventValidator;
|
||||
137
packages/react-native-web/src/vendor/react-native/emitter/mixInEventEmitter.js
vendored
Normal file
137
packages/react-native-web/src/vendor/react-native/emitter/mixInEventEmitter.js
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule mixInEventEmitter
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
import EventEmitter from './EventEmitter';
|
||||
import EventEmitterWithHolding from './EventEmitterWithHolding';
|
||||
import EventHolder from './EventHolder';
|
||||
import EventValidator from './EventValidator';
|
||||
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error
|
||||
* found when Flow v0.54 was deployed. To see the error delete this comment and
|
||||
* run Flow. */
|
||||
import keyOf from 'fbjs/lib/keyOf';
|
||||
|
||||
import type EmitterSubscription from './EmitterSubscription';
|
||||
|
||||
const __DEV__ = process.env.NODE_ENV !== 'production';
|
||||
const TYPES_KEY = keyOf({__types: true});
|
||||
|
||||
/**
|
||||
* API to setup an object or constructor to be able to emit data events.
|
||||
*
|
||||
* @example
|
||||
* function Dog() { ...dog stuff... }
|
||||
* mixInEventEmitter(Dog, {bark: true});
|
||||
*
|
||||
* var puppy = new Dog();
|
||||
* puppy.addListener('bark', function (volume) {
|
||||
* console.log('Puppy', this, 'barked at volume:', volume);
|
||||
* });
|
||||
* puppy.emit('bark', 'quiet');
|
||||
* // Puppy <puppy> barked at volume: quiet
|
||||
*
|
||||
*
|
||||
* // A "singleton" object may also be commissioned:
|
||||
*
|
||||
* var Singleton = {};
|
||||
* mixInEventEmitter(Singleton, {lonely: true});
|
||||
* Singleton.emit('lonely', true);
|
||||
*/
|
||||
function mixInEventEmitter(cls: Function | Object, types: Object) {
|
||||
invariant(types, 'Must supply set of valid event types');
|
||||
|
||||
// If this is a constructor, write to the prototype, otherwise write to the
|
||||
// singleton object.
|
||||
const target = cls.prototype || cls;
|
||||
|
||||
invariant(!target.__eventEmitter, 'An active emitter is already mixed in');
|
||||
|
||||
const ctor = cls.constructor;
|
||||
if (ctor) {
|
||||
invariant(
|
||||
ctor === Object || ctor === Function,
|
||||
'Mix EventEmitter into a class, not an instance'
|
||||
);
|
||||
}
|
||||
|
||||
// Keep track of the provided types, union the types if they already exist,
|
||||
// which allows for prototype subclasses to provide more types.
|
||||
if (target.hasOwnProperty(TYPES_KEY)) {
|
||||
Object.assign(target.__types, types);
|
||||
} else if (target.__types) {
|
||||
target.__types = Object.assign({}, target.__types, types);
|
||||
} else {
|
||||
target.__types = types;
|
||||
}
|
||||
Object.assign(target, EventEmitterMixin);
|
||||
}
|
||||
|
||||
const EventEmitterMixin = {
|
||||
emit: function(eventType, a, b, c, d, e, _) {
|
||||
return this.__getEventEmitter().emit(eventType, a, b, c, d, e, _);
|
||||
},
|
||||
|
||||
emitAndHold: function(eventType, a, b, c, d, e, _) {
|
||||
return this.__getEventEmitter().emitAndHold(eventType, a, b, c, d, e, _);
|
||||
},
|
||||
|
||||
addListener: function(eventType, listener, context): EmitterSubscription {
|
||||
return this.__getEventEmitter().addListener(eventType, listener, context);
|
||||
},
|
||||
|
||||
once: function(eventType, listener, context) {
|
||||
return this.__getEventEmitter().once(eventType, listener, context);
|
||||
},
|
||||
|
||||
addRetroactiveListener: function(eventType, listener, context) {
|
||||
return this.__getEventEmitter().addRetroactiveListener(
|
||||
eventType,
|
||||
listener,
|
||||
context
|
||||
);
|
||||
},
|
||||
|
||||
addListenerMap: function(listenerMap, context) {
|
||||
return this.__getEventEmitter().addListenerMap(listenerMap, context);
|
||||
},
|
||||
|
||||
addRetroactiveListenerMap: function(listenerMap, context) {
|
||||
return this.__getEventEmitter().addListenerMap(listenerMap, context);
|
||||
},
|
||||
|
||||
removeAllListeners: function() {
|
||||
this.__getEventEmitter().removeAllListeners();
|
||||
},
|
||||
|
||||
removeCurrentListener: function() {
|
||||
this.__getEventEmitter().removeCurrentListener();
|
||||
},
|
||||
|
||||
releaseHeldEventType: function(eventType) {
|
||||
this.__getEventEmitter().releaseHeldEventType(eventType);
|
||||
},
|
||||
|
||||
__getEventEmitter: function() {
|
||||
if (!this.__eventEmitter) {
|
||||
let emitter = new EventEmitter();
|
||||
if (__DEV__) {
|
||||
emitter = EventValidator.addValidation(emitter, this.__types);
|
||||
}
|
||||
|
||||
const holder = new EventHolder();
|
||||
this.__eventEmitter = new EventEmitterWithHolding(emitter, holder);
|
||||
}
|
||||
return this.__eventEmitter;
|
||||
}
|
||||
};
|
||||
|
||||
export default mixInEventEmitter;
|
||||
Reference in New Issue
Block a user