Move React Core Integration to a Dependency

Summary:Adding the react native renderer dependency and various fixes to support React 15.

Don't use dispatchID for touchableHandleResponderGrant

This callback argument was removed because "IDs" no longer exist. Instead, we'll
use the tag from the event target.

The corresponding PR on React Core is: https://github.com/facebook/react/pull/6338

Reviewed By: spicyj

Differential Revision: D3159788

fb-gh-sync-id: 60e5cd2aa0af69d83fcdac3dfde0a85a748cb7b9
fbshipit-source-id: 60e5cd2aa0af69d83fcdac3dfde0a85a748cb7b9
This commit is contained in:
Sebastian Markbage 2016-04-21 09:27:50 -07:00 committed by Facebook Github Bot 4
parent 033e7c80bf
commit 47a470a97c
40 changed files with 62 additions and 3942 deletions

View File

@ -360,10 +360,10 @@ var TouchableMixin = {
/**
* Place as callback for a DOM element's `onResponderGrant` event.
* @param {SyntheticEvent} e Synthetic event from event system.
* @param {string} dispatchID ID of node that e was dispatched to.
*
*/
touchableHandleResponderGrant: function(e, dispatchID) {
touchableHandleResponderGrant: function(e) {
var dispatchID = e.currentTarget;
// Since e is used in a callback invoked on another event loop
// (as in setTimeout etc), we need to call e.persist() on the
// event to make sure it doesn't get reused in the event object pool.

View File

@ -16,28 +16,8 @@
'use strict';
var BatchedBridge = require('BatchedBridge');
var DebugComponentOwnershipModule = require('NativeModules').DebugComponentOwnershipModule;
var InspectorUtils = require('InspectorUtils');
var ReactNativeTagHandles = require('ReactNativeTagHandles');
function componentToString(component) {
return component.getName ? component.getName() : 'Unknown';
}
function getRootTagForTag(tag: number): ?number {
var rootNodeID = ReactNativeTagHandles.tagToRootNodeID[tag];
if (!rootNodeID) {
return null;
}
var rootID = ReactNativeTagHandles.getNativeTopRootIDFromNodeID(rootNodeID);
if (!rootID) {
return null;
}
return ReactNativeTagHandles.rootNodeIDToTag[rootID];
}
var RCTDebugComponentOwnership = {
/**
* Asynchronously returns the owner hierarchy as an array of strings. Request id is
* passed along to the native module so that the native module can identify the
@ -46,12 +26,10 @@ var RCTDebugComponentOwnership = {
* Example returned owner hierarchy: ['RootView', 'Dialog', 'TitleView', 'Text']
*/
getOwnerHierarchy: function(requestID: number, tag: number) {
var rootTag = getRootTagForTag(tag);
var instance = InspectorUtils.findInstanceByNativeTag(rootTag, tag);
var ownerHierarchy = instance ?
InspectorUtils.getOwnerHierarchy(instance).map(componentToString) :
null;
DebugComponentOwnershipModule.receiveOwnershipHierarchy(requestID, tag, ownerHierarchy);
// Consider cleaning up these unused modules in a separate diff.
throw new Error(
'This seems to be unused. Will disable until it is needed again.'
);
},
};

View File

@ -64,12 +64,27 @@ function setupDevtools() {
console.error('Failed to eval: ' + e.message);
return;
}
// This is breaking encapsulation of the React package. Move plz.
var ReactNativeComponentTree = require('ReactNativeComponentTree');
window.__REACT_DEVTOOLS_GLOBAL_HOOK__.inject({
CurrentOwner: require('ReactCurrentOwner'),
InstanceHandles: require('ReactInstanceHandles'),
ComponentTree: {
getClosestInstanceFromNode: function (node) {
return ReactNativeComponentTree.getClosestInstanceFromNode(node);
},
getNodeFromInstance: function (inst) {
// inst is an internal instance (but could be a composite)
while (inst._renderedComponent) {
inst = inst._renderedComponent;
}
if (inst) {
return ReactNativeComponentTree.getNodeFromInstance(inst);
} else {
return null;
}
}
},
Mount: require('ReactNativeMount'),
Reconciler: require('ReactReconciler'),
TextComponent: require('ReactNativeTextComponent'),
Reconciler: require('ReactReconciler')
});
ws.onmessage = handleMessage;
}

View File

@ -9,6 +9,9 @@
* @providesModule Inspector
* @flow
*/
/* eslint-disable dot-notation, no-dimensions-get-window */
'use strict';
var Dimensions = require('Dimensions');
@ -16,7 +19,6 @@ var InspectorOverlay = require('InspectorOverlay');
var InspectorPanel = require('InspectorPanel');
var InspectorUtils = require('InspectorUtils');
var React = require('React');
var ReactNative = require('ReactNative');
var StyleSheet = require('StyleSheet');
var Touchable = require('Touchable');
var UIManager = require('NativeModules').UIManager;
@ -28,6 +30,23 @@ if (window.__REACT_DEVTOOLS_GLOBAL_HOOK__) {
}
class Inspector extends React.Component {
props: {
inspectedViewTag: ?number,
rootTag: ?number,
onRequestRerenderApp: (callback: (tag: ?number) => void) => void
};
state: {
devtoolsAgent: ?Object,
hierarchy: any,
panelPos: string,
inspecting: bool,
selection: ?number,
perfing: bool,
inspected: any,
inspectedViewTag: any,
};
_subs: ?Array<() => void>;
constructor(props: Object) {
@ -35,17 +54,19 @@ class Inspector extends React.Component {
this.state = {
devtoolsAgent: null,
hierarchy: null,
panelPos: 'bottom',
inspecting: true,
perfing: false,
inspected: null,
selection: null,
inspectedViewTag: this.props.inspectedViewTag,
};
}
componentDidMount() {
if (window.__REACT_DEVTOOLS_GLOBAL_HOOK__) {
this.attachToDevtools = this.attachToDevtools.bind(this);
(this : any).attachToDevtools = this.attachToDevtools.bind(this);
window.__REACT_DEVTOOLS_GLOBAL_HOOK__.on('react-devtools', this.attachToDevtools);
// if devtools is already started
if (window.__REACT_DEVTOOLS_GLOBAL_HOOK__.reactDevtoolsAgent) {
@ -107,8 +128,8 @@ class Inspector extends React.Component {
var instance = this.state.hierarchy[i];
// if we inspect a stateless component we can't use the getPublicInstance method
// therefore we use the internal _instance property directly.
var publicInstance = instance._instance || {};
UIManager.measure(ReactNative.findNodeHandle(instance), (x, y, width, height, left, top) => {
var publicInstance = instance['_instance'] || {};
UIManager.measure(instance.getNativeNode(), (x, y, width, height, left, top) => {
this.setState({
inspected: {
frame: {left, top, width, height},

View File

@ -10,10 +10,7 @@
*/
'use strict';
var ReactInstanceHandles = require('ReactInstanceHandles');
var ReactInstanceMap = require('ReactInstanceMap');
var ReactNativeMount = require('ReactNativeMount');
var ReactNativeTagHandles = require('ReactNativeTagHandles');
var ReactNativeComponentTree = require('ReactNativeComponentTree');
function traverseOwnerTreeUp(hierarchy, instance) {
if (instance) {
@ -22,38 +19,8 @@ function traverseOwnerTreeUp(hierarchy, instance) {
}
}
function findInstance(component, targetID) {
if (targetID === findRootNodeID(component)) {
return component;
}
if (component._renderedComponent) {
return findInstance(component._renderedComponent, targetID);
} else {
for (var key in component._renderedChildren) {
var child = component._renderedChildren[key];
if (ReactInstanceHandles.isAncestorIDOf(findRootNodeID(child), targetID)) {
var instance = findInstance(child, targetID);
if (instance) {
return instance;
}
}
}
}
}
function findRootNodeID(component) {
var internalInstance = ReactInstanceMap.get(component);
return internalInstance ? internalInstance._rootNodeID : component._rootNodeID;
}
function findInstanceByNativeTag(rootTag, nativeTag) {
var containerID = ReactNativeTagHandles.tagToRootNodeID[rootTag];
var rootInstance = ReactNativeMount._instancesByContainerID[containerID];
var targetID = ReactNativeTagHandles.tagToRootNodeID[nativeTag];
if (!targetID) {
return undefined;
}
return findInstance(rootInstance, targetID);
return ReactNativeComponentTree.getInstanceFromNode(nativeTag);
}
function getOwnerHierarchy(instance) {

View File

@ -11,7 +11,6 @@
*/
'use strict';
var ReactNativeTagHandles = require('ReactNativeTagHandles');
var UIManager = require('UIManager');
type OnSuccessCallback = (
@ -41,18 +40,21 @@ type OnErrorCallback = (error: any) => void
*
* This is an IOS specific implementation.
*
* @param {string} rootNodeID ID of the platform specific node to be measured.
* @param {number} tag ID of the platform specific node to be measured.
* @param {function} onError `func(error)`
* @param {function} onSuccess `func(left, top, width, height, pageX, pageY)`
*/
var queryLayoutByID = function(
rootNodeID: string,
tag: ?number,
onError: OnErrorCallback,
onSuccess: OnSuccessCallback
): void {
if (tag == null) {
return;
}
// Native bridge doesn't *yet* surface errors.
UIManager.measure(
ReactNativeTagHandles.rootNodeIDToTag[rootNodeID],
tag,
onSuccess
);
};

View File

@ -1,19 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule IOSDefaultEventPluginOrder
* @flow
*/
'use strict';
var IOSDefaultEventPluginOrder = [
'ResponderEventPlugin',
'IOSNativeBridgeEventPlugin'
];
module.exports = IOSDefaultEventPluginOrder;

View File

@ -1,75 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule IOSNativeBridgeEventPlugin
* @flow
*/
'use strict';
var EventPropagators = require('EventPropagators');
var SyntheticEvent = require('SyntheticEvent');
var UIManager = require('UIManager');
var merge = require('merge');
var warning = require('fbjs/lib/warning');
var customBubblingEventTypes = UIManager.customBubblingEventTypes;
var customDirectEventTypes = UIManager.customDirectEventTypes;
var allTypesByEventName = {};
for (var bubblingTypeName in customBubblingEventTypes) {
allTypesByEventName[bubblingTypeName] = customBubblingEventTypes[bubblingTypeName];
}
for (var directTypeName in customDirectEventTypes) {
warning(
!customBubblingEventTypes[directTypeName],
'Event cannot be both direct and bubbling: %s',
directTypeName
);
allTypesByEventName[directTypeName] = customDirectEventTypes[directTypeName];
}
var IOSNativeBridgeEventPlugin = {
eventTypes: merge(customBubblingEventTypes, customDirectEventTypes),
/**
* @param {string} topLevelType Record from `EventConstants`.
* @param {DOMEventTarget} topLevelTarget The listening component root node.
* @param {string} topLevelTargetID ID of `topLevelTarget`.
* @param {object} nativeEvent Native browser event.
* @return {*} An accumulation of synthetic events.
* @see {EventPluginHub.extractEvents}
*/
extractEvents: function(
topLevelType: string,
topLevelTarget: EventTarget,
topLevelTargetID: string,
nativeEvent: Event
): ?Object {
var bubbleDispatchConfig = customBubblingEventTypes[topLevelType];
var directDispatchConfig = customDirectEventTypes[topLevelType];
var event = SyntheticEvent.getPooled(
bubbleDispatchConfig || directDispatchConfig,
topLevelTargetID,
nativeEvent
);
if (bubbleDispatchConfig) {
EventPropagators.accumulateTwoPhaseDispatches(event);
} else if (directDispatchConfig) {
EventPropagators.accumulateDirectDispatches(event);
} else {
return null;
}
return event;
}
};
module.exports = IOSNativeBridgeEventPlugin;

View File

@ -1,219 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule NativeMethodsMixin
* @flow
*/
'use strict';
var ReactNativeAttributePayload = require('ReactNativeAttributePayload');
var TextInputState = require('TextInputState');
var UIManager = require('UIManager');
var findNodeHandle = require('findNodeHandle');
var invariant = require('fbjs/lib/invariant');
type MeasureOnSuccessCallback = (
x: number,
y: number,
width: number,
height: number,
pageX: number,
pageY: number
) => void
type MeasureInWindowOnSuccessCallback = (
x: number,
y: number,
width: number,
height: number,
) => void
type MeasureLayoutOnSuccessCallback = (
left: number,
top: number,
width: number,
height: number
) => void
function warnForStyleProps(props, validAttributes) {
for (var key in validAttributes.style) {
if (!(validAttributes[key] || props[key] === undefined)) {
console.error(
'You are setting the style `{ ' + key + ': ... }` as a prop. You ' +
'should nest it in a style object. ' +
'E.g. `{ style: { ' + key + ': ... } }`'
);
}
}
}
/**
* `NativeMethodsMixin` provides methods to access the underlying native
* component directly. This can be useful in cases when you want to focus
* a view or measure its on-screen dimensions, for example.
*
* The methods described here are available on most of the default components
* provided by React Native. Note, however, that they are *not* available on
* composite components that aren't directly backed by a native view. This will
* generally include most components that you define in your own app. For more
* information, see [Direct
* Manipulation](docs/direct-manipulation.html).
*/
var NativeMethodsMixin = {
/**
* Determines the location on screen, width, and height of the given view and
* returns the values via an async callback. If successful, the callback will
* be called with the following arguments:
*
* - x
* - y
* - width
* - height
* - pageX
* - pageY
*
* Note that these measurements are not available until after the rendering
* has been completed in native. If you need the measurements as soon as
* possible, consider using the [`onLayout`
* prop](docs/view.html#onlayout) instead.
*/
measure: function(callback: MeasureOnSuccessCallback) {
UIManager.measure(
findNodeHandle(this),
mountSafeCallback(this, callback)
);
},
/**
* Determines the location of the given view in the window and returns the
* values via an async callback. If the React root view is embedded in
* another native view, this will give you the absolute coordinates. If
* successful, the callback will be called with the following
* arguments:
*
* - x
* - y
* - width
* - height
*
* Note that these measurements are not available until after the rendering
* has been completed in native.
*/
measureInWindow: function(callback: MeasureInWindowOnSuccessCallback) {
UIManager.measureInWindow(
findNodeHandle(this),
mountSafeCallback(this, callback)
);
},
/**
* Like [`measure()`](#measure), but measures the view relative an ancestor,
* specified as `relativeToNativeNode`. This means that the returned x, y
* are relative to the origin x, y of the ancestor view.
*
* As always, to obtain a native node handle for a component, you can use
* `React.findNodeHandle(component)`.
*/
measureLayout: function(
relativeToNativeNode: number,
onSuccess: MeasureLayoutOnSuccessCallback,
onFail: () => void /* currently unused */
) {
UIManager.measureLayout(
findNodeHandle(this),
relativeToNativeNode,
mountSafeCallback(this, onFail),
mountSafeCallback(this, onSuccess)
);
},
/**
* This function sends props straight to native. They will not participate in
* future diff process - this means that if you do not include them in the
* next render, they will remain active (see [Direct
* Manipulation](docs/direct-manipulation.html)).
*/
setNativeProps: function(nativeProps: Object) {
if (__DEV__) {
warnForStyleProps(nativeProps, this.viewConfig.validAttributes);
}
var updatePayload = ReactNativeAttributePayload.create(
nativeProps,
this.viewConfig.validAttributes
);
UIManager.updateView(
findNodeHandle(this),
this.viewConfig.uiViewClassName,
updatePayload
);
},
/**
* Requests focus for the given input or view. The exact behavior triggered
* will depend on the platform and type of view.
*/
focus: function() {
TextInputState.focusTextInput(findNodeHandle(this));
},
/**
* Removes focus from an input or view. This is the opposite of `focus()`.
*/
blur: function() {
TextInputState.blurTextInput(findNodeHandle(this));
}
};
function throwOnStylesProp(component, props) {
if (props.styles !== undefined) {
var owner = component._owner || null;
var name = component.constructor.displayName;
var msg = '`styles` is not a supported property of `' + name + '`, did ' +
'you mean `style` (singular)?';
if (owner && owner.constructor && owner.constructor.displayName) {
msg += '\n\nCheck the `' + owner.constructor.displayName + '` parent ' +
' component.';
}
throw new Error(msg);
}
}
if (__DEV__) {
// hide this from Flow since we can't define these properties outside of
// __DEV__ without actually implementing them (setting them to undefined
// isn't allowed by ReactClass)
var NativeMethodsMixin_DEV = (NativeMethodsMixin: any);
invariant(
!NativeMethodsMixin_DEV.componentWillMount &&
!NativeMethodsMixin_DEV.componentWillReceiveProps,
'Do not override existing functions.'
);
NativeMethodsMixin_DEV.componentWillMount = function () {
throwOnStylesProp(this, this.props);
};
NativeMethodsMixin_DEV.componentWillReceiveProps = function (newProps) {
throwOnStylesProp(this, newProps);
};
}
/**
* In the future, we should cleanup callbacks by cancelling them instead of
* using this.
*/
var mountSafeCallback = function(context: ReactComponent, callback: ?Function): any {
return function() {
if (!callback || (context.isMounted && !context.isMounted())) {
return;
}
return callback.apply(context, arguments);
};
};
module.exports = NativeMethodsMixin;

View File

@ -1,43 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule React
* @flow
*/
'use strict';
const ReactIsomorphic = require('ReactIsomorphic');
const ReactNativeImpl = require('ReactNativeImpl');
const warning = require('fbjs/lib/warning');
const React = { ...ReactIsomorphic };
const dedupe = {};
for (const key in ReactNativeImpl) {
React[key] = ReactNativeImpl[key];
if (__DEV__) {
Object.defineProperty(React, key, {
get: function() {
warning(
dedupe[key],
'React.' + key + ' is deprecated. Use ReactNative.' + key +
' from the "react-native" package instead.'
);
dedupe[key] = true;
return ReactNativeImpl[key];
},
set: function(value) {
// Useful for hacky solutions like createExamplePage.
ReactNativeImpl[key] = value;
},
});
}
}
module.exports = React;

View File

@ -1,16 +0,0 @@
/**
* Copyright 2004-present Facebook. All Rights Reserved.
*
* @providesModule ReactDOM
*/
'use strict';
var ReactUpdates = require('ReactUpdates');
// Temporary shim required for ReactTestUtils and Relay.
var ReactDOM = {
unstable_batchedUpdates: ReactUpdates.batchedUpdates,
};
module.exports = ReactDOM;

View File

@ -1,57 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNative
* @flow
*/
'use strict';
const ReactIsomorphic = require('ReactIsomorphic');
const ReactNativeImpl = require('ReactNativeImpl');
const warning = require('fbjs/lib/warning');
const ReactNative = { ...ReactNativeImpl };
const dedupe = {};
if (__DEV__) {
for (const key in ReactNativeImpl) {
Object.defineProperty(ReactNative, key, {
get: function() {
return ReactNativeImpl[key];
},
set: function(value) {
// Useful for hacky solutions like createExamplePage.
ReactNativeImpl[key] = value;
},
});
}
}
for (const key in ReactIsomorphic) {
ReactNative[key] = ReactIsomorphic[key];
if (__DEV__) {
Object.defineProperty(ReactNative, key, {
get: function() {
warning(
dedupe[key],
'ReactNative.' + key + ' is deprecated. Use React.' + key +
' from the "react" package instead.'
);
dedupe[key] = true;
return ReactIsomorphic[key];
},
set: function(value) {
// Useful for hacky solutions like createExamplePage.
ReactIsomorphic[key] = value;
},
});
}
}
module.exports = ReactNative;

View File

@ -1,546 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeAttributePayload
* @flow
*/
'use strict';
var Platform = require('Platform');
var ReactNativePropRegistry = require('ReactNativePropRegistry');
var deepDiffer = require('deepDiffer');
var flattenStyle = require('flattenStyle');
var emptyObject = {};
/**
* Create a payload that contains all the updates between two sets of props.
*
* These helpers are all encapsulated into a single module, because they use
* mutation as a performance optimization which leads to subtle shared
* dependencies between the code paths. To avoid this mutable state leaking
* across modules, I've kept them isolated to this module.
*/
type AttributeDiffer = (prevProp: mixed, nextProp: mixed) => boolean;
type AttributePreprocessor = (nextProp: mixed) => mixed;
type CustomAttributeConfiguration =
{ diff: AttributeDiffer, process: AttributePreprocessor } |
{ diff: AttributeDiffer } |
{ process: AttributePreprocessor };
type AttributeConfiguration =
{ [key: string]: (
CustomAttributeConfiguration | AttributeConfiguration /*| boolean*/
) };
type NestedNode = Array<NestedNode> | Object | number;
// Tracks removed keys
var removedKeys = null;
var removedKeyCount = 0;
function translateKey(propKey: string) : string {
if (propKey === 'transform') {
// We currently special case the key for `transform`. iOS uses the
// transformMatrix name and Android uses the decomposedMatrix name.
// TODO: We could unify these names and just use the name `transform`
// all the time. Just need to update the native side.
if (Platform.OS === 'android') {
return 'decomposedMatrix';
} else {
return 'transformMatrix';
}
}
return propKey;
}
function defaultDiffer(prevProp: mixed, nextProp: mixed) : boolean {
if (typeof nextProp !== 'object' || nextProp === null) {
// Scalars have already been checked for equality
return true;
} else {
// For objects and arrays, the default diffing algorithm is a deep compare
return deepDiffer(prevProp, nextProp);
}
}
function resolveObject(idOrObject: number | Object) : Object {
if (typeof idOrObject === 'number') {
return ReactNativePropRegistry.getByID(idOrObject);
}
return idOrObject;
}
function restoreDeletedValuesInNestedArray(
updatePayload: Object,
node: NestedNode,
validAttributes: AttributeConfiguration
) {
if (Array.isArray(node)) {
var i = node.length;
while (i-- && removedKeyCount > 0) {
restoreDeletedValuesInNestedArray(
updatePayload,
node[i],
validAttributes
);
}
} else if (node && removedKeyCount > 0) {
var obj = resolveObject(node);
for (var propKey in removedKeys) {
if (!removedKeys[propKey]) {
continue;
}
var nextProp = obj[propKey];
if (nextProp === undefined) {
continue;
}
var attributeConfig = validAttributes[propKey];
if (!attributeConfig) {
continue; // not a valid native prop
}
if (typeof nextProp === 'function') {
nextProp = true;
}
if (typeof nextProp === 'undefined') {
nextProp = null;
}
if (typeof attributeConfig !== 'object') {
// case: !Object is the default case
updatePayload[propKey] = nextProp;
} else if (typeof attributeConfig.diff === 'function' ||
typeof attributeConfig.process === 'function') {
// case: CustomAttributeConfiguration
var nextValue = typeof attributeConfig.process === 'function' ?
attributeConfig.process(nextProp) :
nextProp;
updatePayload[propKey] = nextValue;
}
removedKeys[propKey] = false;
removedKeyCount--;
}
}
}
function diffNestedArrayProperty(
updatePayload:? Object,
prevArray: Array<NestedNode>,
nextArray: Array<NestedNode>,
validAttributes: AttributeConfiguration
) : ?Object {
var minLength = prevArray.length < nextArray.length ?
prevArray.length :
nextArray.length;
var i;
for (i = 0; i < minLength; i++) {
// Diff any items in the array in the forward direction. Repeated keys
// will be overwritten by later values.
updatePayload = diffNestedProperty(
updatePayload,
prevArray[i],
nextArray[i],
validAttributes
);
}
for (; i < prevArray.length; i++) {
// Clear out all remaining properties.
updatePayload = clearNestedProperty(
updatePayload,
prevArray[i],
validAttributes
);
}
for (; i < nextArray.length; i++) {
// Add all remaining properties.
updatePayload = addNestedProperty(
updatePayload,
nextArray[i],
validAttributes
);
}
return updatePayload;
}
function diffNestedProperty(
updatePayload:? Object,
prevProp: NestedNode,
nextProp: NestedNode,
validAttributes: AttributeConfiguration
) : ?Object {
if (!updatePayload && prevProp === nextProp) {
// If no properties have been added, then we can bail out quickly on object
// equality.
return updatePayload;
}
if (!prevProp || !nextProp) {
if (nextProp) {
return addNestedProperty(
updatePayload,
nextProp,
validAttributes
);
}
if (prevProp) {
return clearNestedProperty(
updatePayload,
prevProp,
validAttributes
);
}
return updatePayload;
}
if (!Array.isArray(prevProp) && !Array.isArray(nextProp)) {
// Both are leaves, we can diff the leaves.
return diffProperties(
updatePayload,
resolveObject(prevProp),
resolveObject(nextProp),
validAttributes
);
}
if (Array.isArray(prevProp) && Array.isArray(nextProp)) {
// Both are arrays, we can diff the arrays.
return diffNestedArrayProperty(
updatePayload,
prevProp,
nextProp,
validAttributes
);
}
if (Array.isArray(prevProp)) {
return diffProperties(
updatePayload,
// $FlowFixMe - We know that this is always an object when the input is.
flattenStyle(prevProp),
// $FlowFixMe - We know that this isn't an array because of above flow.
resolveObject(nextProp),
validAttributes
);
}
return diffProperties(
updatePayload,
resolveObject(prevProp),
// $FlowFixMe - We know that this is always an object when the input is.
flattenStyle(nextProp),
validAttributes
);
}
/**
* addNestedProperty takes a single set of props and valid attribute
* attribute configurations. It processes each prop and adds it to the
* updatePayload.
*/
function addNestedProperty(
updatePayload:? Object,
nextProp: NestedNode,
validAttributes: AttributeConfiguration
) {
if (!nextProp) {
return updatePayload;
}
if (!Array.isArray(nextProp)) {
// Add each property of the leaf.
return addProperties(
updatePayload,
resolveObject(nextProp),
validAttributes
);
}
for (var i = 0; i < nextProp.length; i++) {
// Add all the properties of the array.
updatePayload = addNestedProperty(
updatePayload,
nextProp[i],
validAttributes
);
}
return updatePayload;
}
/**
* clearNestedProperty takes a single set of props and valid attributes. It
* adds a null sentinel to the updatePayload, for each prop key.
*/
function clearNestedProperty(
updatePayload:? Object,
prevProp: NestedNode,
validAttributes: AttributeConfiguration
) : ?Object {
if (!prevProp) {
return updatePayload;
}
if (!Array.isArray(prevProp)) {
// Add each property of the leaf.
return clearProperties(
updatePayload,
resolveObject(prevProp),
validAttributes
);
}
for (var i = 0; i < prevProp.length; i++) {
// Add all the properties of the array.
updatePayload = clearNestedProperty(
updatePayload,
prevProp[i],
validAttributes
);
}
return updatePayload;
}
/**
* diffProperties takes two sets of props and a set of valid attributes
* and write to updatePayload the values that changed or were deleted.
* If no updatePayload is provided, a new one is created and returned if
* anything changed.
*/
function diffProperties(
updatePayload: ?Object,
prevProps: Object,
nextProps: Object,
validAttributes: AttributeConfiguration
): ?Object {
var attributeConfig : ?(CustomAttributeConfiguration | AttributeConfiguration);
var nextProp;
var prevProp;
var altKey;
for (var propKey in nextProps) {
attributeConfig = validAttributes[propKey];
if (!attributeConfig) {
continue; // not a valid native prop
}
altKey = translateKey(propKey);
if (!validAttributes[altKey]) {
// If there is no config for the alternative, bail out. Helps ART.
altKey = propKey;
}
prevProp = prevProps[propKey];
nextProp = nextProps[propKey];
// functions are converted to booleans as markers that the associated
// events should be sent from native.
if (typeof nextProp === 'function') {
nextProp = (true : any);
// If nextProp is not a function, then don't bother changing prevProp
// since nextProp will win and go into the updatePayload regardless.
if (typeof prevProp === 'function') {
prevProp = (true : any);
}
}
// An explicit value of undefined is treated as a null because it overrides
// any other preceeding value.
if (typeof nextProp === 'undefined') {
nextProp = (null : any);
if (typeof prevProp === 'undefined') {
prevProp = (null : any);
}
}
if (removedKeys) {
removedKeys[propKey] = false;
}
if (updatePayload && updatePayload[altKey] !== undefined) {
// Something else already triggered an update to this key because another
// value diffed. Since we're now later in the nested arrays our value is
// more important so we need to calculate it and override the existing
// value. It doesn't matter if nothing changed, we'll set it anyway.
// Pattern match on: attributeConfig
if (typeof attributeConfig !== 'object') {
// case: !Object is the default case
updatePayload[altKey] = nextProp;
} else if (typeof attributeConfig.diff === 'function' ||
typeof attributeConfig.process === 'function') {
// case: CustomAttributeConfiguration
var nextValue = typeof attributeConfig.process === 'function' ?
attributeConfig.process(nextProp) :
nextProp;
updatePayload[altKey] = nextValue;
}
continue;
}
if (prevProp === nextProp) {
continue; // nothing changed
}
// Pattern match on: attributeConfig
if (typeof attributeConfig !== 'object') {
// case: !Object is the default case
if (defaultDiffer(prevProp, nextProp)) {
// a normal leaf has changed
(updatePayload || (updatePayload = {}))[altKey] = nextProp;
}
} else if (typeof attributeConfig.diff === 'function' ||
typeof attributeConfig.process === 'function') {
// case: CustomAttributeConfiguration
var shouldUpdate = prevProp === undefined || (
typeof attributeConfig.diff === 'function' ?
attributeConfig.diff(prevProp, nextProp) :
defaultDiffer(prevProp, nextProp)
);
if (shouldUpdate) {
var nextValue = typeof attributeConfig.process === 'function' ?
attributeConfig.process(nextProp) :
nextProp;
(updatePayload || (updatePayload = {}))[altKey] = nextValue;
}
} else {
// default: fallthrough case when nested properties are defined
removedKeys = null;
removedKeyCount = 0;
updatePayload = diffNestedProperty(
updatePayload,
prevProp,
nextProp,
attributeConfig
);
if (removedKeyCount > 0 && updatePayload) {
restoreDeletedValuesInNestedArray(
updatePayload,
nextProp,
attributeConfig
);
removedKeys = null;
}
}
}
// Also iterate through all the previous props to catch any that have been
// removed and make sure native gets the signal so it can reset them to the
// default.
for (var propKey in prevProps) {
if (nextProps[propKey] !== undefined) {
continue; // we've already covered this key in the previous pass
}
attributeConfig = validAttributes[propKey];
if (!attributeConfig) {
continue; // not a valid native prop
}
altKey = translateKey(propKey);
if (!attributeConfig[altKey]) {
// If there is no config for the alternative, bail out. Helps ART.
altKey = propKey;
}
if (updatePayload && updatePayload[altKey] !== undefined) {
// This was already updated to a diff result earlier.
continue;
}
prevProp = prevProps[propKey];
if (prevProp === undefined) {
continue; // was already empty anyway
}
// Pattern match on: attributeConfig
if (typeof attributeConfig !== 'object' ||
typeof attributeConfig.diff === 'function' ||
typeof attributeConfig.process === 'function') {
// case: CustomAttributeConfiguration | !Object
// Flag the leaf property for removal by sending a sentinel.
(updatePayload || (updatePayload = {}))[altKey] = null;
if (!removedKeys) {
removedKeys = {};
}
if (!removedKeys[propKey]) {
removedKeys[propKey] = true;
removedKeyCount++;
}
} else {
// default:
// This is a nested attribute configuration where all the properties
// were removed so we need to go through and clear out all of them.
updatePayload = clearNestedProperty(
updatePayload,
prevProp,
attributeConfig
);
}
}
return updatePayload;
}
/**
* addProperties adds all the valid props to the payload after being processed.
*/
function addProperties(
updatePayload: ?Object,
props: Object,
validAttributes: AttributeConfiguration
) : ?Object {
// TODO: Fast path
return diffProperties(updatePayload, emptyObject, props, validAttributes);
}
/**
* clearProperties clears all the previous props by adding a null sentinel
* to the payload for each valid key.
*/
function clearProperties(
updatePayload: ?Object,
prevProps: Object,
validAttributes: AttributeConfiguration
) :? Object {
// TODO: Fast path
return diffProperties(updatePayload, prevProps, emptyObject, validAttributes);
}
var ReactNativeAttributePayload = {
create: function(
props: Object,
validAttributes: AttributeConfiguration
) : ?Object {
return addProperties(
null, // updatePayload
props,
validAttributes
);
},
diff: function(
prevProps: Object,
nextProps: Object,
validAttributes: AttributeConfiguration
) : ?Object {
return diffProperties(
null, // updatePayload
prevProps,
nextProps,
validAttributes
);
}
};
module.exports = ReactNativeAttributePayload;

View File

@ -1,239 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeBaseComponent
* @flow
*/
'use strict';
var NativeMethodsMixin = require('NativeMethodsMixin');
var ReactNativeAttributePayload = require('ReactNativeAttributePayload');
var ReactNativeEventEmitter = require('ReactNativeEventEmitter');
var ReactNativeTagHandles = require('ReactNativeTagHandles');
var ReactMultiChild = require('ReactMultiChild');
var UIManager = require('UIManager');
var deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationInDev');
var invariant = require('fbjs/lib/invariant');
var warning = require('fbjs/lib/warning');
var registrationNames = ReactNativeEventEmitter.registrationNames;
var putListener = ReactNativeEventEmitter.putListener;
var deleteListener = ReactNativeEventEmitter.deleteListener;
var deleteAllListeners = ReactNativeEventEmitter.deleteAllListeners;
type ReactNativeBaseComponentViewConfig = {
validAttributes: Object;
uiViewClassName: string;
}
// require('UIManagerStatTracker').install(); // uncomment to enable
/**
* @constructor ReactNativeBaseComponent
* @extends ReactComponent
* @extends ReactMultiChild
* @param {!object} UIKit View Configuration.
*/
var ReactNativeBaseComponent = function(
viewConfig: ReactNativeBaseComponentViewConfig
) {
this.viewConfig = viewConfig;
};
/**
* Mixin for containers that contain UIViews. NOTE: markup is rendered markup
* which is a `viewID` ... see the return value for `mountComponent` !
*/
ReactNativeBaseComponent.Mixin = {
getPublicInstance: function() {
// TODO: This should probably use a composite wrapper
return this;
},
construct: function(element) {
this._currentElement = element;
},
unmountComponent: function() {
deleteAllListeners(this._rootNodeID);
this.unmountChildren();
this._rootNodeID = null;
},
/**
* Every native component is responsible for allocating its own `tag`, and
* issuing the native `createView` command. But it is not responsible for
* recording the fact that its own `rootNodeID` is associated with a
* `nodeHandle`. Only the code that actually adds its `nodeHandle` (`tag`) as
* a child of a container can confidently record that in
* `ReactNativeTagHandles`.
*/
initializeChildren: function(children, containerTag, transaction, context) {
var mountImages = this.mountChildren(children, transaction, context);
// In a well balanced tree, half of the nodes are in the bottom row and have
// no children - let's avoid calling out to the native bridge for a large
// portion of the children.
if (mountImages.length) {
// TODO: Pool these per platform view class. Reusing the `mountImages`
// array would likely be a jit deopt.
var createdTags = [];
for (var i = 0, l = mountImages.length; i < l; i++) {
var mountImage = mountImages[i];
var childTag = mountImage.tag;
var childID = mountImage.rootNodeID;
warning(
mountImage && mountImage.rootNodeID && mountImage.tag,
'Mount image returned does not have required data'
);
ReactNativeTagHandles.associateRootNodeIDWithMountedNodeHandle(
childID,
childTag
);
createdTags[i] = mountImage.tag;
}
UIManager.setChildren(containerTag, createdTags);
}
},
/**
* Updates the component's currently mounted representation.
*
* @param {object} nextElement
* @param {ReactReconcileTransaction} transaction
* @param {object} context
* @internal
*/
receiveComponent: function(nextElement, transaction, context) {
var prevElement = this._currentElement;
this._currentElement = nextElement;
if (__DEV__) {
for (var key in this.viewConfig.validAttributes) {
if (nextElement.props.hasOwnProperty(key)) {
deepFreezeAndThrowOnMutationInDev(nextElement.props[key]);
}
}
}
var updatePayload = ReactNativeAttributePayload.diff(
prevElement.props,
nextElement.props,
this.viewConfig.validAttributes
);
if (updatePayload) {
UIManager.updateView(
ReactNativeTagHandles.mostRecentMountedNodeHandleForRootNodeID(this._rootNodeID),
this.viewConfig.uiViewClassName,
updatePayload
);
}
this._reconcileListenersUponUpdate(
prevElement.props,
nextElement.props
);
this.updateChildren(nextElement.props.children, transaction, context);
},
/**
* @param {object} initialProps Native component props.
*/
_registerListenersUponCreation: function(initialProps) {
for (var key in initialProps) {
// NOTE: The check for `!props[key]`, is only possible because this method
// registers listeners the *first* time a component is created.
if (registrationNames[key] && initialProps[key]) {
var listener = initialProps[key];
putListener(this._rootNodeID, key, listener);
}
}
},
/**
* Reconciles event listeners, adding or removing if necessary.
* @param {object} prevProps Native component props including events.
* @param {object} nextProps Next native component props including events.
*/
_reconcileListenersUponUpdate: function(prevProps, nextProps) {
for (var key in nextProps) {
if (registrationNames[key] && (nextProps[key] !== prevProps[key])) {
if (nextProps[key]) {
putListener(this._rootNodeID, key, nextProps[key]);
} else {
deleteListener(this._rootNodeID, key);
}
}
}
},
/**
* @param {string} rootID Root ID of this subtree.
* @param {Transaction} transaction For creating/updating.
* @return {string} Unique iOS view tag.
*/
mountComponent: function(rootID, transaction, context) {
this._rootNodeID = rootID;
var tag = ReactNativeTagHandles.allocateTag();
if (__DEV__) {
for (var key in this.viewConfig.validAttributes) {
if (this._currentElement.props.hasOwnProperty(key)) {
deepFreezeAndThrowOnMutationInDev(this._currentElement.props[key]);
}
}
}
var updatePayload = ReactNativeAttributePayload.create(
this._currentElement.props,
this.viewConfig.validAttributes
);
var nativeTopRootID = ReactNativeTagHandles.getNativeTopRootIDFromNodeID(rootID);
if (nativeTopRootID == null) {
invariant(
false,
'nativeTopRootID not found for tag ' + tag + ' view type ' +
this.viewConfig.uiViewClassName + ' with rootID ' + rootID);
}
UIManager.createView(
tag,
this.viewConfig.uiViewClassName,
ReactNativeTagHandles.rootNodeIDToTag[nativeTopRootID],
updatePayload
);
this._registerListenersUponCreation(this._currentElement.props);
this.initializeChildren(
this._currentElement.props.children,
tag,
transaction,
context
);
return {
rootNodeID: rootID,
tag: tag
};
}
};
/**
* Order of mixins is important. ReactNativeBaseComponent overrides methods in
* ReactMultiChild.
*/
Object.assign(
ReactNativeBaseComponent.prototype,
ReactMultiChild.Mixin,
ReactNativeBaseComponent.Mixin,
NativeMethodsMixin
);
module.exports = ReactNativeBaseComponent;

View File

@ -1,42 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeComponentEnvironment
* @flow
*/
'use strict';
var ReactNativeDOMIDOperations = require('ReactNativeDOMIDOperations');
var ReactNativeReconcileTransaction = require('ReactNativeReconcileTransaction');
var ReactNativeComponentEnvironment = {
processChildrenUpdates: ReactNativeDOMIDOperations.dangerouslyProcessChildrenUpdates,
replaceNodeWithMarkupByID: ReactNativeDOMIDOperations.dangerouslyReplaceNodeWithMarkupByID,
/**
* Nothing to do for UIKit bridge.
*
* @private
*/
unmountIDFromEnvironment: function(/*rootNodeID*/) {
},
/**
* @param {DOMElement} Element to clear.
*/
clearNode: function(/*containerView*/) {
},
ReactReconcileTransaction: ReactNativeReconcileTransaction,
};
module.exports = ReactNativeComponentEnvironment;

View File

@ -1,102 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeDOMIDOperations
* @flow
*/
'use strict';
var ReactNativeTagHandles = require('ReactNativeTagHandles');
var ReactMultiChildUpdateTypes = require('ReactMultiChildUpdateTypes');
var ReactPerf = require('ReactPerf');
var UIManager = require('UIManager');
/**
* Updates a component's children by processing a series of updates.
* For each of the update/create commands, the `fromIndex` refers to the index
* that the item existed at *before* any of the updates are applied, and the
* `toIndex` refers to the index after *all* of the updates are applied
* (including deletes/moves). TODO: refactor so this can be shared with
* DOMChildrenOperations.
*
* @param {array<object>} updates List of update configurations.
* @param {array<string>} markup List of markup strings - in the case of React
* IOS, the ids of new components assumed to be already created.
*/
var dangerouslyProcessChildrenUpdates = function(childrenUpdates, markupList) {
if (!childrenUpdates.length) {
return;
}
var byContainerTag = {};
// Group by parent ID - send them across the bridge in separate commands per
// containerID.
for (var i = 0; i < childrenUpdates.length; i++) {
var update = childrenUpdates[i];
var containerTag = ReactNativeTagHandles.mostRecentMountedNodeHandleForRootNodeID(update.parentID);
var updates = byContainerTag[containerTag] || (byContainerTag[containerTag] = {});
if (update.type === ReactMultiChildUpdateTypes.MOVE_EXISTING) {
(updates.moveFromIndices || (updates.moveFromIndices = [])).push(update.fromIndex);
(updates.moveToIndices || (updates.moveToIndices = [])).push(update.toIndex);
} else if (update.type === ReactMultiChildUpdateTypes.REMOVE_NODE) {
(updates.removeAtIndices || (updates.removeAtIndices = [])).push(update.fromIndex);
} else if (update.type === ReactMultiChildUpdateTypes.INSERT_MARKUP) {
var mountImage = markupList[update.markupIndex];
var tag = mountImage.tag;
var rootNodeID = mountImage.rootNodeID;
ReactNativeTagHandles.associateRootNodeIDWithMountedNodeHandle(rootNodeID, tag);
(updates.addAtIndices || (updates.addAtIndices = [])).push(update.toIndex);
(updates.addChildTags || (updates.addChildTags = [])).push(tag);
}
}
// Note this enumeration order will be different on V8! Move `byContainerTag`
// to a sparse array as soon as we confirm there are not horrible perf
// penalties.
for (var updateParentTagString in byContainerTag) {
var updateParentTagNumber = +updateParentTagString;
var childUpdatesToSend = byContainerTag[updateParentTagNumber];
UIManager.manageChildren(
updateParentTagNumber,
childUpdatesToSend.moveFromIndices,
childUpdatesToSend.moveToIndices,
childUpdatesToSend.addChildTags,
childUpdatesToSend.addAtIndices,
childUpdatesToSend.removeAtIndices
);
}
};
/**
* Operations used to process updates to DOM nodes. This is made injectable via
* `ReactComponent.DOMIDOperations`.
*/
var ReactNativeDOMIDOperations = {
dangerouslyProcessChildrenUpdates: ReactPerf.measure(
// FIXME(frantic): #4441289 Hack to avoid modifying react-tools
'ReactDOMIDOperations',
'dangerouslyProcessChildrenUpdates',
dangerouslyProcessChildrenUpdates
),
/**
* Replaces a view that exists in the document with markup.
*
* @param {string} id ID of child to be replaced.
* @param {string} markup Mount image to replace child with id.
*/
dangerouslyReplaceNodeWithMarkupByID: ReactPerf.measure(
'ReactDOMIDOperations',
'dangerouslyReplaceNodeWithMarkupByID',
function(id, mountImage) {
var oldTag = ReactNativeTagHandles.mostRecentMountedNodeHandleForRootNodeID(id);
UIManager.replaceExistingNonRootView(oldTag, mountImage.tag);
ReactNativeTagHandles.associateRootNodeIDWithMountedNodeHandle(id, mountImage.tag);
}
),
};
module.exports = ReactNativeDOMIDOperations;

View File

@ -1,113 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeDefaultInjection
* @flow
*/
'use strict';
/**
* Make sure essential globals are available and are patched correctly. Please don't remove this
* line. Bundles created by react-packager `require` it before executing any application code. This
* ensures it exists in the dependency graph and can be `require`d.
*/
require('InitializeJavaScriptAppEngine');
var EventPluginHub = require('EventPluginHub');
var EventPluginUtils = require('EventPluginUtils');
var IOSDefaultEventPluginOrder = require('IOSDefaultEventPluginOrder');
var IOSNativeBridgeEventPlugin = require('IOSNativeBridgeEventPlugin');
var NodeHandle = require('NodeHandle');
var ReactElement = require('ReactElement');
var ReactComponentEnvironment = require('ReactComponentEnvironment');
var ReactDefaultBatchingStrategy = require('ReactDefaultBatchingStrategy');
var ReactEmptyComponent = require('ReactEmptyComponent');
var ReactInstanceHandles = require('ReactInstanceHandles');
var ReactNativeComponentEnvironment = require('ReactNativeComponentEnvironment');
var ReactNativeGlobalInteractionHandler = require('ReactNativeGlobalInteractionHandler');
var ReactNativeGlobalResponderHandler = require('ReactNativeGlobalResponderHandler');
var ReactNativeMount = require('ReactNativeMount');
var ReactNativeTextComponent = require('ReactNativeTextComponent');
var ReactNativeComponent = require('ReactNativeComponent');
var ReactUpdates = require('ReactUpdates');
var ResponderEventPlugin = require('ResponderEventPlugin');
var UniversalWorkerNodeHandle = require('UniversalWorkerNodeHandle');
var invariant = require('fbjs/lib/invariant');
// Just to ensure this gets packaged, since its only caller is from Native.
require('RCTEventEmitter');
require('RCTLog');
require('JSTimersExecution');
function inject() {
/**
* Inject module for resolving DOM hierarchy and plugin ordering.
*/
EventPluginHub.injection.injectEventPluginOrder(IOSDefaultEventPluginOrder);
EventPluginHub.injection.injectInstanceHandle(ReactInstanceHandles);
ResponderEventPlugin.injection.injectGlobalResponderHandler(
ReactNativeGlobalResponderHandler
);
ResponderEventPlugin.injection.injectGlobalInteractionHandler(
ReactNativeGlobalInteractionHandler
);
/**
* Some important event plugins included by default (without having to require
* them).
*/
EventPluginHub.injection.injectEventPluginsByName({
'ResponderEventPlugin': ResponderEventPlugin,
'IOSNativeBridgeEventPlugin': IOSNativeBridgeEventPlugin
});
ReactUpdates.injection.injectReconcileTransaction(
ReactNativeComponentEnvironment.ReactReconcileTransaction
);
ReactUpdates.injection.injectBatchingStrategy(
ReactDefaultBatchingStrategy
);
ReactComponentEnvironment.injection.injectEnvironment(
ReactNativeComponentEnvironment
);
var EmptyComponent = () => {
// Can't import View at the top because it depends on React to make its composite
var View = require('View');
return ReactElement.createElement(View, {
collapsable: true,
style: { position: 'absolute' }
});
};
ReactEmptyComponent.injection.injectEmptyComponent(EmptyComponent);
EventPluginUtils.injection.injectMount(ReactNativeMount);
ReactNativeComponent.injection.injectTextComponentClass(
ReactNativeTextComponent
);
ReactNativeComponent.injection.injectGenericComponentClass(function(tag) {
// Show a nicer error message for non-function tags
var info = '';
if (typeof tag === 'string' && /^[a-z]/.test(tag)) {
info += ' Each component name should start with an uppercase letter.';
}
invariant(false, 'Expected a component class, got %s.%s', tag, info);
});
NodeHandle.injection.injectImplementation(UniversalWorkerNodeHandle);
}
module.exports = {
inject: inject,
};

View File

@ -1,214 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeEventEmitter
* @flow
*/
'use strict';
var EventPluginHub = require('EventPluginHub');
var ReactEventEmitterMixin = require('ReactEventEmitterMixin');
var ReactNativeTagHandles = require('ReactNativeTagHandles');
var NodeHandle = require('NodeHandle');
var EventConstants = require('EventConstants');
var merge = require('merge');
var warning = require('fbjs/lib/warning');
var topLevelTypes = EventConstants.topLevelTypes;
/**
* Version of `ReactBrowserEventEmitter` that works on the receiving side of a
* serialized worker boundary.
*/
// Shared default empty native event - conserve memory.
var EMPTY_NATIVE_EVENT = {};
/**
* Selects a subsequence of `Touch`es, without destroying `touches`.
*
* @param {Array<Touch>} touches Deserialized touch objects.
* @param {Array<number>} indices Indices by which to pull subsequence.
* @return {Array<Touch>} Subsequence of touch objects.
*/
var touchSubsequence = function(touches, indices) {
var ret = [];
for (var i = 0; i < indices.length; i++) {
ret.push(touches[indices[i]]);
}
return ret;
};
/**
* TODO: Pool all of this.
*
* Destroys `touches` by removing touch objects at indices `indices`. This is
* to maintain compatibility with W3C touch "end" events, where the active
* touches don't include the set that has just been "ended".
*
* @param {Array<Touch>} touches Deserialized touch objects.
* @param {Array<number>} indices Indices to remove from `touches`.
* @return {Array<Touch>} Subsequence of removed touch objects.
*/
var removeTouchesAtIndices = function(
touches: Array<Object>,
indices: Array<number>
): Array<Object> {
var rippedOut = [];
// use an unsafe downcast to alias to nullable elements,
// so we can delete and then compact.
var temp: Array<?Object> = (touches: Array<any>);
for (var i = 0; i < indices.length; i++) {
var index = indices[i];
rippedOut.push(touches[index]);
temp[index] = null;
}
var fillAt = 0;
for (var j = 0; j < temp.length; j++) {
var cur = temp[j];
if (cur !== null) {
temp[fillAt++] = cur;
}
}
temp.length = fillAt;
return rippedOut;
};
/**
* `ReactNativeEventEmitter` is used to attach top-level event listeners. For example:
*
* ReactNativeEventEmitter.putListener('myID', 'onClick', myFunction);
*
* This would allocate a "registration" of `('onClick', myFunction)` on 'myID'.
*
* @internal
*/
var ReactNativeEventEmitter = merge(ReactEventEmitterMixin, {
registrationNames: EventPluginHub.registrationNameModules,
putListener: EventPluginHub.putListener,
getListener: EventPluginHub.getListener,
deleteListener: EventPluginHub.deleteListener,
deleteAllListeners: EventPluginHub.deleteAllListeners,
/**
* Internal version of `receiveEvent` in terms of normalized (non-tag)
* `rootNodeID`.
*
* @see receiveEvent.
*
* @param {rootNodeID} rootNodeID React root node ID that event occurred on.
* @param {TopLevelType} topLevelType Top level type of event.
* @param {object} nativeEventParam Object passed from native.
*/
_receiveRootNodeIDEvent: function(
rootNodeID: ?string,
topLevelType: string,
nativeEventParam: Object
) {
var nativeEvent = nativeEventParam || EMPTY_NATIVE_EVENT;
ReactNativeEventEmitter.handleTopLevel(
topLevelType,
rootNodeID,
rootNodeID,
nativeEvent,
nativeEvent.target
);
},
/**
* Publicly exposed method on module for native objc to invoke when a top
* level event is extracted.
* @param {rootNodeID} rootNodeID React root node ID that event occurred on.
* @param {TopLevelType} topLevelType Top level type of event.
* @param {object} nativeEventParam Object passed from native.
*/
receiveEvent: function(
tag: number,
topLevelType: string,
nativeEventParam: Object
) {
var rootNodeID = ReactNativeTagHandles.tagToRootNodeID[tag];
ReactNativeEventEmitter._receiveRootNodeIDEvent(
rootNodeID,
topLevelType,
nativeEventParam
);
},
/**
* Simple multi-wrapper around `receiveEvent` that is intended to receive an
* efficient representation of `Touch` objects, and other information that
* can be used to construct W3C compliant `Event` and `Touch` lists.
*
* This may create dispatch behavior that differs than web touch handling. We
* loop through each of the changed touches and receive it as a single event.
* So two `touchStart`/`touchMove`s that occur simultaneously are received as
* two separate touch event dispatches - when they arguably should be one.
*
* This implementation reuses the `Touch` objects themselves as the `Event`s
* since we dispatch an event for each touch (though that might not be spec
* compliant). The main purpose of reusing them is to save allocations.
*
* TODO: Dispatch multiple changed touches in one event. The bubble path
* could be the first common ancestor of all the `changedTouches`.
*
* One difference between this behavior and W3C spec: cancelled touches will
* not appear in `.touches`, or in any future `.touches`, though they may
* still be "actively touching the surface".
*
* Web desktop polyfills only need to construct a fake touch event with
* identifier 0, also abandoning traditional click handlers.
*/
receiveTouches: function(
eventTopLevelType: string,
touches: Array<Object>,
changedIndices: Array<number>
) {
var changedTouches =
eventTopLevelType === topLevelTypes.topTouchEnd ||
eventTopLevelType === topLevelTypes.topTouchCancel ?
removeTouchesAtIndices(touches, changedIndices) :
touchSubsequence(touches, changedIndices);
for (var jj = 0; jj < changedTouches.length; jj++) {
var touch = changedTouches[jj];
// Touch objects can fulfill the role of `DOM` `Event` objects if we set
// the `changedTouches`/`touches`. This saves allocations.
touch.changedTouches = changedTouches;
touch.touches = touches;
var nativeEvent = touch;
var rootNodeID = null;
var target = nativeEvent.target;
if (target !== null && target !== undefined) {
if (target < ReactNativeTagHandles.tagsStartAt) {
if (__DEV__) {
warning(
false,
'A view is reporting that a touch occured on tag zero.'
);
}
} else {
rootNodeID = NodeHandle.getRootNodeID(target);
}
}
ReactNativeEventEmitter._receiveRootNodeIDEvent(
rootNodeID,
eventTopLevelType,
nativeEvent
);
}
}
});
module.exports = ReactNativeEventEmitter;

View File

@ -1,33 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeGlobalInteractionHandler
* @flow
*/
'use strict';
var InteractionManager = require('InteractionManager');
// Interaction handle is created/cleared when responder is granted or
// released/terminated.
var interactionHandle = null;
var ReactNativeGlobalInteractionHandler = {
onChange: function(numberActiveTouches: number) {
if (numberActiveTouches === 0) {
if (interactionHandle) {
InteractionManager.clearInteractionHandle(interactionHandle);
interactionHandle = null;
}
} else if (!interactionHandle) {
interactionHandle = InteractionManager.createInteractionHandle();
}
}
};
module.exports = ReactNativeGlobalInteractionHandler;

View File

@ -1,30 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeGlobalResponderHandler
* @flow
*/
'use strict';
var ReactNativeTagHandles = require('ReactNativeTagHandles');
var UIManager = require('UIManager');
var ReactNativeGlobalResponderHandler = {
onChange: function(from: string, to: string, blockNativeResponder: boolean) {
if (to !== null) {
UIManager.setJSResponder(
ReactNativeTagHandles.mostRecentMountedNodeHandleForRootNodeID(to),
blockNativeResponder
);
} else {
UIManager.clearJSResponder();
}
}
};
module.exports = ReactNativeGlobalResponderHandler;

View File

@ -1,64 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeImpl
* @flow
*/
'use strict';
// Require ReactNativeDefaultInjection first for its side effects of setting up
// the JS environment
var ReactNativeDefaultInjection = require('ReactNativeDefaultInjection');
var ReactCurrentOwner = require('ReactCurrentOwner');
var ReactElement = require('ReactElement');
var ReactInstanceHandles = require('ReactInstanceHandles');
var ReactNativeMount = require('ReactNativeMount');
var ReactUpdates = require('ReactUpdates');
var findNodeHandle = require('findNodeHandle');
ReactNativeDefaultInjection.inject();
var render = function(
element: ReactElement,
mountInto: number,
callback?: ?(() => void)
): ?ReactComponent {
return ReactNativeMount.renderComponent(element, mountInto, callback);
};
var ReactNative = {
hasReactNativeInitialized: false,
findNodeHandle: findNodeHandle,
render: render,
unmountComponentAtNode: ReactNativeMount.unmountComponentAtNode,
/* eslint-disable camelcase */
unstable_batchedUpdates: ReactUpdates.batchedUpdates,
/* eslint-enable camelcase */
unmountComponentAtNodeAndRemoveContainer: ReactNativeMount.unmountComponentAtNodeAndRemoveContainer,
};
// Inject the runtime into a devtools global hook regardless of browser.
// Allows for debugging when the hook is injected on the page.
/* globals __REACT_DEVTOOLS_GLOBAL_HOOK__ */
if (
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject === 'function') {
__REACT_DEVTOOLS_GLOBAL_HOOK__.inject({
CurrentOwner: ReactCurrentOwner,
InstanceHandles: ReactInstanceHandles,
Mount: ReactNativeMount,
Reconciler: require('ReactReconciler'),
TextComponent: require('ReactNativeTextComponent'),
});
}
module.exports = ReactNative;

View File

@ -1,270 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeMount
* @flow
*/
'use strict';
var ReactElement = require('ReactElement');
var ReactNativeTagHandles = require('ReactNativeTagHandles');
var ReactPerf = require('ReactPerf');
var ReactReconciler = require('ReactReconciler');
var ReactUpdateQueue = require('ReactUpdateQueue');
var ReactUpdates = require('ReactUpdates');
var UIManager = require('UIManager');
var emptyObject = require('fbjs/lib/emptyObject');
var instantiateReactComponent = require('instantiateReactComponent');
var shouldUpdateReactComponent = require('shouldUpdateReactComponent');
function instanceNumberToChildRootID(rootNodeID, instanceNumber) {
return rootNodeID + '[' + instanceNumber + ']';
}
/**
* Temporary (?) hack so that we can store all top-level pending updates on
* composites instead of having to worry about different types of components
* here.
*/
var TopLevelWrapper = function() {};
TopLevelWrapper.prototype.isReactComponent = {};
if (__DEV__) {
TopLevelWrapper.displayName = 'TopLevelWrapper';
}
TopLevelWrapper.prototype.render = function() {
// this.props is actually a ReactElement
return this.props;
};
/**
* Mounts this component and inserts it into the DOM.
*
* @param {ReactComponent} componentInstance The instance to mount.
* @param {number} rootID ID of the root node.
* @param {number} container container element to mount into.
* @param {ReactReconcileTransaction} transaction
*/
function mountComponentIntoNode(
componentInstance,
rootID,
container,
transaction) {
var markup = ReactReconciler.mountComponent(
componentInstance, rootID, transaction, emptyObject
);
componentInstance._renderedComponent._topLevelWrapper = componentInstance;
ReactNativeMount._mountImageIntoNode(markup, container);
}
/**
* Batched mount.
*
* @param {ReactComponent} componentInstance The instance to mount.
* @param {number} rootID ID of the root node.
* @param {number} container container element to mount into.
*/
function batchedMountComponentIntoNode(
componentInstance,
rootID,
container) {
var transaction = ReactUpdates.ReactReconcileTransaction.getPooled();
transaction.perform(
mountComponentIntoNode,
null,
componentInstance,
rootID,
container,
transaction
);
ReactUpdates.ReactReconcileTransaction.release(transaction);
}
/**
* As soon as `ReactMount` is refactored to not rely on the DOM, we can share
* code between the two. For now, we'll hard code the ID logic.
*/
var ReactNativeMount = {
instanceCount: 0,
_instancesByContainerID: {},
// these two functions are needed by React Devtools
findNodeHandle: require('findNodeHandle'),
nativeTagToRootNodeID: function (nativeTag: number): string {
return ReactNativeTagHandles.tagToRootNodeID[nativeTag];
},
/**
* @param {ReactComponent} instance Instance to render.
* @param {containerTag} containerView Handle to native view tag
*/
renderComponent: function(
nextElement: ReactElement,
containerTag: number,
callback?: ?(() => void)
): ?ReactComponent {
var nextWrappedElement = new ReactElement(
TopLevelWrapper,
null,
null,
null,
null,
null,
nextElement
);
var topRootNodeID = ReactNativeTagHandles.tagToRootNodeID[containerTag];
if (topRootNodeID) {
var prevComponent = ReactNativeMount._instancesByContainerID[topRootNodeID];
if (prevComponent) {
var prevWrappedElement = prevComponent._currentElement;
var prevElement = prevWrappedElement.props;
if (shouldUpdateReactComponent(prevElement, nextElement)) {
ReactUpdateQueue.enqueueElementInternal(prevComponent, nextWrappedElement);
if (callback) {
ReactUpdateQueue.enqueueCallbackInternal(prevComponent, callback);
}
return prevComponent;
} else {
ReactNativeMount.unmountComponentAtNode(containerTag);
}
}
}
if (!ReactNativeTagHandles.reactTagIsNativeTopRootID(containerTag)) {
console.error('You cannot render into anything but a top root');
return;
}
var topRootNodeID = ReactNativeTagHandles.allocateRootNodeIDForTag(containerTag);
ReactNativeTagHandles.associateRootNodeIDWithMountedNodeHandle(
topRootNodeID,
containerTag
);
var instance = instantiateReactComponent(nextWrappedElement);
ReactNativeMount._instancesByContainerID[topRootNodeID] = instance;
var childRootNodeID = instanceNumberToChildRootID(
topRootNodeID,
ReactNativeMount.instanceCount++
);
// The initial render is synchronous but any updates that happen during
// rendering, in componentWillMount or componentDidMount, will be batched
// according to the current batching strategy.
ReactUpdates.batchedUpdates(
batchedMountComponentIntoNode,
instance,
childRootNodeID,
topRootNodeID
);
var component = instance.getPublicInstance();
if (callback) {
callback.call(component);
}
return component;
},
/**
* @param {View} view View tree image.
* @param {number} containerViewID View to insert sub-view into.
*/
_mountImageIntoNode: ReactPerf.measure(
// FIXME(frantic): #4441289 Hack to avoid modifying react-tools
'ReactComponentBrowserEnvironment',
'mountImageIntoNode',
function(mountImage, containerID) {
// Since we now know that the `mountImage` has been mounted, we can
// mark it as such.
ReactNativeTagHandles.associateRootNodeIDWithMountedNodeHandle(
mountImage.rootNodeID,
mountImage.tag
);
UIManager.setChildren(
ReactNativeTagHandles.mostRecentMountedNodeHandleForRootNodeID(containerID),
[mountImage.tag]
);
}
),
/**
* Standard unmounting of the component that is rendered into `containerID`,
* but will also execute a command to remove the actual container view
* itself. This is useful when a client is cleaning up a React tree, and also
* knows that the container will no longer be needed. When executing
* asynchronously, it's easier to just have this method be the one that calls
* for removal of the view.
*/
unmountComponentAtNodeAndRemoveContainer: function(
containerTag: number
) {
ReactNativeMount.unmountComponentAtNode(containerTag);
// call back into native to remove all of the subviews from this container
UIManager.removeRootView(containerTag);
},
/**
* Unmount component at container ID by iterating through each child component
* that has been rendered and unmounting it. There should just be one child
* component at this time.
*/
unmountComponentAtNode: function(containerTag: number): boolean {
if (!ReactNativeTagHandles.reactTagIsNativeTopRootID(containerTag)) {
console.error('You cannot render into anything but a top root');
return false;
}
var containerID = ReactNativeTagHandles.tagToRootNodeID[containerTag];
var instance = ReactNativeMount._instancesByContainerID[containerID];
if (!instance) {
return false;
}
ReactNativeMount.unmountComponentFromNode(instance, containerID);
delete ReactNativeMount._instancesByContainerID[containerID];
return true;
},
/**
* Unmounts a component and sends messages back to iOS to remove its subviews.
*
* @param {ReactComponent} instance React component instance.
* @param {string} containerID ID of container we're removing from.
* @final
* @internal
* @see {ReactNativeMount.unmountComponentAtNode}
*/
unmountComponentFromNode: function(
instance: ReactComponent,
containerID: string
) {
// Call back into native to remove all of the subviews from this container
ReactReconciler.unmountComponent(instance);
var containerTag =
ReactNativeTagHandles.mostRecentMountedNodeHandleForRootNodeID(containerID);
UIManager.removeSubviewsFromContainerWithID(containerTag);
},
getNode: function(rootNodeID: string): number {
return ReactNativeTagHandles.rootNodeIDToTag[rootNodeID];
},
getID: function(nativeTag: number): string {
return ReactNativeTagHandles.tagToRootNodeID[nativeTag];
}
};
ReactNativeMount.renderComponent = ReactPerf.measure(
'ReactMount',
'_renderNewRootComponent',
ReactNativeMount.renderComponent
);
module.exports = ReactNativeMount;

View File

@ -1,44 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativePropRegistry
* @flow
*/
'use strict';
var objects = {};
var uniqueID = 1;
var emptyObject = {};
class ReactNativePropRegistry {
static register(object: Object): number {
var id = ++uniqueID;
if (__DEV__) {
Object.freeze(object);
}
objects[id] = object;
return id;
}
static getByID(id: number): Object {
if (!id) {
// Used in the style={[condition && id]} pattern,
// we want it to be a no-op when the value is false or null
return emptyObject;
}
var object = objects[id];
if (!object) {
console.warn('Invalid style with id `' + id + '`. Skipping ...');
return emptyObject;
}
return object;
}
}
module.exports = ReactNativePropRegistry;

View File

@ -1,103 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeReconcileTransaction
* @flow
*/
'use strict';
var CallbackQueue = require('CallbackQueue');
var PooledClass = require('PooledClass');
var Transaction = require('Transaction');
/**
* Provides a `CallbackQueue` queue for collecting `onDOMReady` callbacks during
* the performing of the transaction.
*/
var ON_DOM_READY_QUEUEING = {
/**
* Initializes the internal `onDOMReady` queue.
*/
initialize: function() {
this.reactMountReady.reset();
},
/**
* After DOM is flushed, invoke all registered `onDOMReady` callbacks.
*/
close: function() {
this.reactMountReady.notifyAll();
}
};
/**
* Executed within the scope of the `Transaction` instance. Consider these as
* being member methods, but with an implied ordering while being isolated from
* each other.
*/
var TRANSACTION_WRAPPERS = [ON_DOM_READY_QUEUEING];
/**
* Currently:
* - The order that these are listed in the transaction is critical:
* - Suppresses events.
* - Restores selection range.
*
* Future:
* - Restore document/overflow scroll positions that were unintentionally
* modified via DOM insertions above the top viewport boundary.
* - Implement/integrate with customized constraint based layout system and keep
* track of which dimensions must be remeasured.
*
* @class ReactNativeReconcileTransaction
*/
function ReactNativeReconcileTransaction() {
this.reinitializeTransaction();
this.reactMountReady = CallbackQueue.getPooled(null);
}
var Mixin = {
/**
* @see Transaction
* @abstract
* @final
* @return {array<object>} List of operation wrap procedures.
* TODO: convert to array<TransactionWrapper>
*/
getTransactionWrappers: function() {
return TRANSACTION_WRAPPERS;
},
/**
* @return {object} The queue to collect `onDOMReady` callbacks with.
* TODO: convert to ReactMountReady
*/
getReactMountReady: function() {
return this.reactMountReady;
},
/**
* `PooledClass` looks for this, and will invoke this before allowing this
* instance to be reused.
*/
destructor: function() {
CallbackQueue.release(this.reactMountReady);
this.reactMountReady = null;
}
};
Object.assign(
ReactNativeReconcileTransaction.prototype,
Transaction.Mixin,
ReactNativeReconcileTransaction,
Mixin
);
PooledClass.addPoolingTo(ReactNativeReconcileTransaction);
module.exports = ReactNativeReconcileTransaction;

View File

@ -1,113 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeTagHandles
* @flow
*/
'use strict';
var invariant = require('fbjs/lib/invariant');
var warning = require('fbjs/lib/warning');
/**
* Keeps track of allocating and associating native "tags" which are numeric,
* unique view IDs. All the native tags are negative numbers, to avoid
* collisions, but in the JS we keep track of them as positive integers to store
* them effectively in Arrays. So we must refer to them as "inverses" of the
* native tags (that are * normally negative).
*
* It *must* be the case that every `rootNodeID` always maps to the exact same
* `tag` forever. The easiest way to accomplish this is to never delete
* anything from this table.
* Why: Because `dangerouslyReplaceNodeWithMarkupByID` relies on being able to
* unmount a component with a `rootNodeID`, then mount a new one in its place,
*/
var INITIAL_TAG_COUNT = 1;
var NATIVE_TOP_ROOT_ID_SEPARATOR = '{TOP_LEVEL}';
var ReactNativeTagHandles = {
tagsStartAt: INITIAL_TAG_COUNT,
tagCount: INITIAL_TAG_COUNT,
allocateTag: function(): number {
// Skip over root IDs as those are reserved for native
while (this.reactTagIsNativeTopRootID(ReactNativeTagHandles.tagCount)) {
ReactNativeTagHandles.tagCount++;
}
var tag = ReactNativeTagHandles.tagCount;
ReactNativeTagHandles.tagCount++;
return tag;
},
/**
* This associates the *last* observed *native* mounting between `rootNodeID`
* and some `tag`. This association doesn't imply that `rootNodeID` is still
* natively mounted as `tag`. The only reason why we don't clear the
* association when the `rootNodeID` is unmounted, is that we don't have a
* convenient time to disassociate them (otherwise we would).
* `unmountComponent` isn't the correct time because that doesn't imply that
* the native node has been natively unmounted.
*/
associateRootNodeIDWithMountedNodeHandle: function(
rootNodeID: ?string,
tag: ?number
) {
warning(rootNodeID && tag, 'Root node or tag is null when associating');
if (rootNodeID && tag) {
ReactNativeTagHandles.tagToRootNodeID[tag] = rootNodeID;
ReactNativeTagHandles.rootNodeIDToTag[rootNodeID] = tag;
}
},
allocateRootNodeIDForTag: function(tag: number): string {
invariant(
this.reactTagIsNativeTopRootID(tag),
'Expect a native root tag, instead got ', tag
);
return '.r[' + tag + ']' + NATIVE_TOP_ROOT_ID_SEPARATOR;
},
reactTagIsNativeTopRootID: function(reactTag: number): bool {
// We reserve all tags that are 1 mod 10 for native root views
return reactTag % 10 === 1;
},
getNativeTopRootIDFromNodeID: function(nodeID: ?string): ?string {
if (!nodeID) {
return null;
}
var index = nodeID.indexOf(NATIVE_TOP_ROOT_ID_SEPARATOR);
if (index === -1) {
return null;
}
return nodeID.substr(0, index + NATIVE_TOP_ROOT_ID_SEPARATOR.length);
},
/**
* Returns the native `nodeHandle` (`tag`) that was most recently *natively*
* mounted at the `rootNodeID`. Just because a React component has been
* mounted, that doesn't mean that its native node has been mounted. The
* native node is mounted when we actually make the call to insert the
* `nodeHandle` (`tag`) into the native hierarchy.
*
* @param {string} rootNodeID Root node ID to find most recently mounted tag
* for. Again, this doesn't imply that it is still currently mounted.
* @return {number} Tag ID of native view for most recent mounting of
* `rootNodeID`.
*/
mostRecentMountedNodeHandleForRootNodeID: function(
rootNodeID: string
): number {
return ReactNativeTagHandles.rootNodeIDToTag[rootNodeID];
},
tagToRootNodeID: ([] : Array<string>),
rootNodeIDToTag: ({} : {[key: string]: number})
};
module.exports = ReactNativeTagHandles;

View File

@ -1,78 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeTextComponent
*/
'use strict';
var ReactNativeTagHandles = require('ReactNativeTagHandles');
var UIManager = require('UIManager');
var invariant = require('fbjs/lib/invariant');
var ReactNativeTextComponent = function(props) {
// This constructor and its argument is currently used by mocks.
};
Object.assign(ReactNativeTextComponent.prototype, {
construct: function(text) {
// This is really a ReactText (ReactNode), not a ReactElement
this._currentElement = text;
this._stringText = '' + text;
this._rootNodeID = null;
},
mountComponent: function(rootID, transaction, context) {
invariant(
context.isInAParentText,
'RawText "' + this._stringText + '" must be wrapped in an explicit ' +
'<Text> component.'
);
this._rootNodeID = rootID;
var tag = ReactNativeTagHandles.allocateTag();
var nativeTopRootID = ReactNativeTagHandles.getNativeTopRootIDFromNodeID(rootID);
UIManager.createView(
tag,
'RCTRawText',
nativeTopRootID ? ReactNativeTagHandles.rootNodeIDToTag[nativeTopRootID] : null,
{text: this._stringText}
);
return {
rootNodeID: rootID,
tag: tag,
};
},
receiveComponent: function(nextText, transaction, context) {
if (nextText !== this._currentElement) {
this._currentElement = nextText;
var nextStringText = '' + nextText;
if (nextStringText !== this._stringText) {
this._stringText = nextStringText;
UIManager.updateView(
ReactNativeTagHandles.mostRecentMountedNodeHandleForRootNodeID(
this._rootNodeID
),
'RCTRawText',
{text: this._stringText}
);
}
}
},
unmountComponent: function() {
this._currentElement = null;
this._stringText = null;
this._rootNodeID = null;
}
});
module.exports = ReactNativeTextComponent;

View File

@ -1,345 +0,0 @@
/**
* Copyright 2004-present Facebook. All Rights Reserved.
*
* @providesModule ReactNativeAttributePayload-benchmark
*/
'use strict';
var StyleSheet = require('StyleSheet');
// Various example props
var small1 = {
accessible: true,
accessibilityLabel: 'Hello',
collapsable: true,
};
var small2 = {
accessible: true,
accessibilityLabel: 'Hello 2',
collapsable: false,
needsOffscreenAlphaCompositing: true,
};
var small3 = {
accessible: true,
accessibilityLabel: 'Hello 2',
};
var medium1 = {
...small1,
onLayout: function() {},
onAccessibilityTap: true,
onMagicTap: function() {},
collapsable: true,
needsOffscreenAlphaCompositing: true,
style: {
backgroundColor: 'rgba(255, 0, 0, 0.5)'
}
};
var medium2 = {
...small2,
onLayout: function() {},
onAccessibilityTap: true,
onMagicTap: function() {},
collapsable: true,
needsOffscreenAlphaCompositing: true,
style: {
backgroundColor: 'rgba(128, 0, 0, 1)',
color: [128, 0, 0],
shadowColor: 56,
textDecorationColor: 34,
tintColor: 45,
transform: [
{perspective: 5},
{scale: 5},
{scaleX: 10},
{scaleY: 10},
{translateX: 2},
{translateY: 5}
],
}
};
var medium3 = {
...small3,
onAccessibilityTap: true,
onMagicTap: function() {},
needsOffscreenAlphaCompositing: true,
style: {
backgroundColor: 'rgba(255, 0, 0, 0.5)',
color: [128, 0, 0],
shadowColor: 56,
textDecorationColor: 34,
tintColor: 45,
transform: [
{perspective: 5},
{scale: 5},
{scaleX: 12},
{scaleY: 16},
{translateX: 10},
{translateY: 5}
],
}
};
var style1 = {
backgroundColor: 'rgba(10,0,0,1)',
borderColor: 'rgba(10,0,0,1)',
color: [255, 0, 0],
shadowColor: 54,
textDecorationColor: 34,
tintColor: 45,
transform: [
{perspective: 5},
{scale: 5},
{scaleX: 2},
{scaleY: 3},
{translateX: 2},
{translateY: 3}
],
};
var style1b = {
backgroundColor: 'rgba(10,0,0,1)',
borderColor: 'rgba(10,0,0,1)',
color: [128, 0, 0],
shadowColor: 56,
textDecorationColor: 34,
tintColor: 45,
transform: [
{perspective: 5},
{scale: 5},
{scaleX: 10},
{scaleY: 10},
{translateX: 2},
{translateY: 5}
],
};
var style2 = {
shadowOffset: { width: 10, height: 15 },
resizeMode: 'contain', // 'cover'
overflow: 'visible', // 'hidden'
opacity: 0.5,
width: 123,
height: 43,
top: 13,
left: 43,
right: 12,
bottom: 123,
margin: 13,
padding: 53,
paddingRight: 523,
borderWidth: 63,
borderRadius: 123,
};
var style3 = {
position: 'absolute', // 'relative'
flexDirection: 'row', // 'column'
flexWrap: 'wrap', // 'nowrap'
justifyContent: 'flex-start', // 'flex-end'
alignItems: 'center',
alignSelf: 'auto',
flex: 0,
};
var style3b = {
position: 'relative',
flexDirection: 'column',
flexWrap: 'nowrap',
justifyContent: 'flex-end',
};
var { regStyle2, regStyle3 } = StyleSheet.create({
regStyle2: style2,
regStyle3: style3
});
var large1 = {
...medium1,
style: {
...medium1.style,
...style1,
...style2,
...style3
}
};
var large2 = {
...medium2,
style: [
[regStyle2, style1],
regStyle3
]
};
var large3 = {
...medium3,
style: [
[regStyle2, style1b],
style3
]
};
var large4 = {
...medium3,
style: [
[regStyle2, style1b],
style3b
]
};
// Clones, to test
var clone = function (obj) {
var str = JSON.stringify(obj, function(k, v) {
return typeof v === 'function' ? 'FUNCTION' : v;
});
return JSON.parse(str, function(k, v) {
return v === 'FUNCTION' ? function() {} : v;
});
};
var small4 = clone(small3);
var medium4 = clone(medium3);
var large5 = clone(large4);
// Test combinations
var variants = {
s1: small1,
s2: small2,
s3: small3,
s4: small4,
m1: medium1,
m2: medium2,
m3: medium3,
m4: medium4,
l1: large1,
l2: large2,
l3: large3,
l4: large4,
l5: large5,
};
var commonCases = [
// Reference equality
'l1l1',
// Equal but not reference equal
's3s4',
'm3m4',
'l4l5',
// Complex base style with a small change in the end
'l3l4',
'l4l3',
];
// Differ
var validAttributes = require('ReactNativeViewAttributes').UIView;
var Differ = require('ReactNativeAttributePayload');
// Runner
var numberOfBenchmarks = 0;
var totalTimeForAllBenchmarks = 0;
var numberOfCommonCases = 0;
var totalTimeForAllCommonCases = 0;
var results = {};
function runBenchmarkOnce(value1, value2) {
// Warm up the differ. This is needed if the differ uses state to store the
// previous values.
Differ.diff({}, value1, validAttributes);
var cache = Differ.previousFlattenedStyle;
var start = Date.now();
for (var i = 0; i < 1000; i++) {
Differ.diff(value1, value2, validAttributes);
Differ.previousFlattenedStyle = cache;
}
var end = Date.now();
return (end - start);
}
function runBenchmark(key1, key2, value1, value2) {
if (results.hasOwnProperty(key1 + key2)) {
// dedupe same test that runs twice. E.g. key1 === key2
return;
}
var totalTime = 0;
var nthRuns = 5;
for (var i = 0; i < nthRuns; i++) {
totalTime += runBenchmarkOnce(value1, value2);
}
var runTime = totalTime / nthRuns;
results[key1 + key2] = runTime;
totalTimeForAllBenchmarks += runTime;
numberOfBenchmarks++;
if (commonCases.indexOf(key1 + key2) > -1) {
numberOfCommonCases++;
totalTimeForAllCommonCases += runTime;
}
}
function runAllCombinations() {
for (var outerKey in variants) {
for (var innerKey in variants) {
if (variants.hasOwnProperty(outerKey) &&
variants.hasOwnProperty(innerKey)) {
runBenchmark(
outerKey,
innerKey,
variants[outerKey],
variants[innerKey]
);
}
}
}
}
function formatResult() {
var str =
'Average runtime: ' +
(totalTimeForAllBenchmarks / numberOfBenchmarks) +
' units\n';
var worstCase = 0;
for (var key in results) {
if (results[key] > worstCase) {
worstCase = results[key];
}
}
str += 'Common cases: ' +
(totalTimeForAllCommonCases / numberOfCommonCases) +
' units\n';
str += 'Worst case: ' + worstCase + ' units\n';
str += 'Per combination:\n';
for (var key in results) {
str += key + ':\u00A0' + results[key] + ', ';
}
return str;
}
runAllCombinations();
module.exports = formatResult();

View File

@ -1,231 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
'use strict';
jest.dontMock('ReactNativeAttributePayload');
jest.dontMock('ReactNativePropRegistry');
jest.dontMock('deepDiffer');
jest.dontMock('flattenStyle');
var ReactNativeAttributePayload = require('ReactNativeAttributePayload');
var ReactNativePropRegistry = require('ReactNativePropRegistry');
var diff = ReactNativeAttributePayload.diff;
describe('ReactNativeAttributePayload', function() {
it('should work with simple example', () => {
expect(diff(
{a: 1, c: 3},
{b: 2, c: 3},
{a: true, b: true}
)).toEqual({a: null, b: 2});
});
it('should skip fields that are equal', () => {
expect(diff(
{a: 1, b: 'two', c: true, d: false, e: undefined, f: 0},
{a: 1, b: 'two', c: true, d: false, e: undefined, f: 0},
{a: true, b: true, c: true, d: true, e: true, f: true}
)).toEqual(null);
});
it('should remove fields', () => {
expect(diff(
{a: 1},
{},
{a: true}
)).toEqual({a: null});
});
it('should remove fields that are set to undefined', () => {
expect(diff(
{a: 1},
{a: undefined},
{a: true}
)).toEqual({a: null});
});
it('should ignore invalid fields', () => {
expect(diff(
{a: 1},
{b: 2},
{}
)).toEqual(null);
});
it('should use the diff attribute', () => {
var diffA = jest.genMockFunction().mockImpl((a, b) => true);
var diffB = jest.genMockFunction().mockImpl((a, b) => false);
expect(diff(
{a: [1], b: [3]},
{a: [2], b: [4]},
{a: {diff: diffA}, b: {diff: diffB}}
)).toEqual({a: [2]});
expect(diffA).toBeCalledWith([1], [2]);
expect(diffB).toBeCalledWith([3], [4]);
});
it('should not use the diff attribute on addition/removal', () => {
var diffA = jest.genMockFunction();
var diffB = jest.genMockFunction();
expect(diff(
{a: [1]},
{b: [2]},
{a: {diff: diffA}, b: {diff: diffB}}
)).toEqual({a: null, b: [2]});
expect(diffA).not.toBeCalled();
expect(diffB).not.toBeCalled();
});
it('should do deep diffs of Objects by default', () => {
expect(diff(
{a: [1], b: {k: [3,4]}, c: {k: [4,4]} },
{a: [2], b: {k: [3,4]}, c: {k: [4,5]} },
{a: true, b: true, c: true}
)).toEqual({a: [2], c: {k: [4,5]}});
});
it('should work with undefined styles', () => {
expect(diff(
{ style: { a: '#ffffff', b: 1 } },
{ style: undefined },
{ style: { b: true } }
)).toEqual({ b: null });
expect(diff(
{ style: undefined },
{ style: { a: '#ffffff', b: 1 } },
{ style: { b: true } }
)).toEqual({ b: 1 });
expect(diff(
{ style: undefined },
{ style: undefined },
{ style: { b: true } }
)).toEqual(null);
});
it('should work with empty styles', () => {
expect(diff(
{a: 1, c: 3},
{},
{a: true, b: true}
)).toEqual({a: null});
expect(diff(
{},
{a: 1, c: 3},
{a: true, b: true}
)).toEqual({a: 1});
expect(diff(
{},
{},
{a: true, b: true}
)).toEqual(null);
});
it('should flatten nested styles and predefined styles', () => {
var validStyleAttribute = { someStyle: { foo: true, bar: true } };
expect(diff(
{},
{ someStyle: [{ foo: 1 }, { bar: 2 }]},
validStyleAttribute
)).toEqual({ foo: 1, bar: 2 });
expect(diff(
{ someStyle: [{ foo: 1 }, { bar: 2 }]},
{},
validStyleAttribute
)).toEqual({ foo: null, bar: null });
var barStyle = ReactNativePropRegistry.register({
bar: 3,
});
expect(diff(
{},
{ someStyle: [[{ foo: 1 }, { foo: 2 }], barStyle]},
validStyleAttribute
)).toEqual({ foo: 2, bar: 3 });
});
it('should reset a value to a previous if it is removed', () => {
var validStyleAttribute = { someStyle: { foo: true, bar: true } };
expect(diff(
{ someStyle: [{ foo: 1 }, { foo: 3 }]},
{ someStyle: [{ foo: 1 }, { bar: 2 }]},
validStyleAttribute
)).toEqual({ foo: 1, bar: 2 });
});
it('should not clear removed props if they are still in another slot', () => {
var validStyleAttribute = { someStyle: { foo: true, bar: true } };
expect(diff(
{ someStyle: [{}, { foo: 3, bar: 2 }]},
{ someStyle: [{ foo: 3 }, { bar: 2 }]},
validStyleAttribute
)).toEqual({ foo: 3 }); // this should ideally be null. heuristic tradeoff.
expect(diff(
{ someStyle: [{}, { foo: 3, bar: 2 }]},
{ someStyle: [{ foo: 1, bar: 1 }, { bar: 2 }]},
validStyleAttribute
)).toEqual({ bar: 2, foo: 1 });
});
it('should clear a prop if a later style is explicit null/undefined', () => {
var validStyleAttribute = { someStyle: { foo: true, bar: true } };
expect(diff(
{ someStyle: [{}, { foo: 3, bar: 2 }]},
{ someStyle: [{ foo: 1 }, { bar: 2, foo: null }]},
validStyleAttribute
)).toEqual({ foo: null });
expect(diff(
{ someStyle: [{ foo: 3 }, { foo: null, bar: 2 }]},
{ someStyle: [{ foo: null }, { bar: 2 }]},
validStyleAttribute
)).toEqual({ foo: null });
expect(diff(
{ someStyle: [{ foo: 1 }, { foo: null }]},
{ someStyle: [{ foo: 2 }, { foo: null }]},
validStyleAttribute
)).toEqual({ foo: null }); // this should ideally be null. heuristic.
// Test the same case with object equality because an early bailout doesn't
// work in this case.
var fooObj = { foo: 3 };
expect(diff(
{ someStyle: [{ foo: 1 }, fooObj]},
{ someStyle: [{ foo: 2 }, fooObj]},
validStyleAttribute
)).toEqual({ foo: 3 }); // this should ideally be null. heuristic.
expect(diff(
{ someStyle: [{ foo: 1 }, { foo: 3 }]},
{ someStyle: [{ foo: 2 }, { foo: undefined }]},
validStyleAttribute
)).toEqual({ foo: null }); // this should ideally be null. heuristic.
});
// Function properties are just markers to native that events should be sent.
it('should convert functions to booleans', () => {
// Note that if the property changes from one function to another, we don't
// need to send an update.
expect(diff(
{a: function() { return 1; }, b: function() { return 2; }, c: 3},
{b: function() { return 9; }, c: function() { return 3; }, },
{a: true, b: true, c: true}
)).toEqual({a: null, c: true});
});
});

View File

@ -1,46 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule createReactNativeComponentClass
* @flow
*/
'use strict';
var ReactNativeBaseComponent = require('ReactNativeBaseComponent');
// See also ReactNativeBaseComponent
type ReactNativeBaseComponentViewConfig = {
validAttributes: Object;
uiViewClassName: string;
propTypes?: Object,
}
/**
* @param {string} config iOS View configuration.
* @private
*/
var createReactNativeComponentClass = function(
viewConfig: ReactNativeBaseComponentViewConfig
): ReactClass<any> {
var Constructor = function(element) {
this._currentElement = element;
this._rootNodeID = null;
this._renderedChildren = null;
};
Constructor.displayName = viewConfig.uiViewClassName;
Constructor.viewConfig = viewConfig;
Constructor.propTypes = viewConfig.propTypes;
Constructor.prototype = new ReactNativeBaseComponent(viewConfig);
Constructor.prototype.constructor = Constructor;
return ((Constructor: any): ReactClass);
};
module.exports = createReactNativeComponentClass;

View File

@ -1,112 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule findNodeHandle
* @flow
*/
'use strict';
var ReactCurrentOwner = require('ReactCurrentOwner');
var ReactInstanceMap = require('ReactInstanceMap');
var ReactNativeTagHandles = require('ReactNativeTagHandles');
var invariant = require('fbjs/lib/invariant');
var warning = require('fbjs/lib/warning');
/**
* ReactNative vs ReactWeb
* -----------------------
* React treats some pieces of data opaquely. This means that the information
* is first class (it can be passed around), but cannot be inspected. This
* allows us to build infrastructure that reasons about resources, without
* making assumptions about the nature of those resources, and this allows that
* infra to be shared across multiple platforms, where the resources are very
* different. General infra (such as `ReactMultiChild`) reasons opaquely about
* the data, but platform specific code (such as `ReactNativeBaseComponent`) can
* make assumptions about the data.
*
*
* `rootNodeID`, uniquely identifies a position in the generated native view
* tree. Many layers of composite components (created with `React.createClass`)
* can all share the same `rootNodeID`.
*
* `nodeHandle`: A sufficiently unambiguous way to refer to a lower level
* resource (dom node, native view etc). The `rootNodeID` is sufficient for web
* `nodeHandle`s, because the position in a tree is always enough to uniquely
* identify a DOM node (we never have nodes in some bank outside of the
* document). The same would be true for `ReactNative`, but we must maintain a
* mapping that we can send efficiently serializable
* strings across native boundaries.
*
* Opaque name TodaysWebReact FutureWebWorkerReact ReactNative
* ----------------------------------------------------------------------------
* nodeHandle N/A rootNodeID tag
*/
function findNodeHandle(componentOrHandle: any): ?number {
if (__DEV__) {
var owner = ReactCurrentOwner.current;
if (owner !== null) {
warning(
owner._warnedAboutRefsInRender,
'%s is accessing findNodeHandle inside its render(). ' +
'render() should be a pure function of props and state. It should ' +
'never access something that requires stale data from the previous ' +
'render, such as refs. Move this logic to componentDidMount and ' +
'componentDidUpdate instead.',
owner.getName() || 'A component'
);
owner._warnedAboutRefsInRender = true;
}
}
if (componentOrHandle == null) {
return null;
}
if (typeof componentOrHandle === 'number') {
// Already a node handle
return componentOrHandle;
}
var component = componentOrHandle;
// TODO (balpert): Wrap iOS native components in a composite wrapper, then
// ReactInstanceMap.get here will always succeed for mounted components
var internalInstance = ReactInstanceMap.get(component);
if (internalInstance) {
return ReactNativeTagHandles.rootNodeIDToTag[internalInstance._rootNodeID];
} else {
var rootNodeID = component._rootNodeID;
if (rootNodeID) {
return ReactNativeTagHandles.rootNodeIDToTag[rootNodeID];
} else {
invariant(
(
// Native
typeof component === 'object' &&
'_rootNodeID' in component
) || (
// Composite
component.render != null &&
typeof component.render === 'function'
),
'findNodeHandle(...): Argument is not a component ' +
'(type: %s, keys: %s)',
typeof component,
Object.keys(component)
);
invariant(
false,
'findNodeHandle(...): Unable to find node handle for unmounted ' +
'component.'
);
}
}
}
module.exports = findNodeHandle;

View File

@ -14,7 +14,7 @@ var warning = require('fbjs/lib/warning');
if (__DEV__) {
var warningDedupe = {};
var addonWarn = function addonWarn(prevName, newPackageName) {
var addonWarn = function(prevName, newPackageName) {
warning(
warningDedupe[prevName],
'React.addons.' + prevName + ' is deprecated. Please import the "' +
@ -155,12 +155,6 @@ var ReactNative = {
}
return require('ReactUpdates').batchedUpdates;
},
get cloneWithProps() {
if (__DEV__) {
addonWarn('cloneWithProps', 'react-addons-clone-with-props');
}
return require('cloneWithProps');
},
get createFragment() {
if (__DEV__) {
addonWarn('createFragment', 'react-addons-create-fragment');

View File

@ -1,378 +0,0 @@
/**
* @providesModule PanResponder
*/
"use strict";
var TouchHistoryMath = require('TouchHistoryMath');
var currentCentroidXOfTouchesChangedAfter =
TouchHistoryMath.currentCentroidXOfTouchesChangedAfter;
var currentCentroidYOfTouchesChangedAfter =
TouchHistoryMath.currentCentroidYOfTouchesChangedAfter;
var previousCentroidXOfTouchesChangedAfter =
TouchHistoryMath.previousCentroidXOfTouchesChangedAfter;
var previousCentroidYOfTouchesChangedAfter =
TouchHistoryMath.previousCentroidYOfTouchesChangedAfter;
var currentCentroidX = TouchHistoryMath.currentCentroidX;
var currentCentroidY = TouchHistoryMath.currentCentroidY;
/**
* `PanResponder` reconciles several touches into a single gesture. It makes
* single-touch gestures resilient to extra touches, and can be used to
* recognize simple multi-touch gestures.
*
* It provides a predictable wrapper of the responder handlers provided by the
* [gesture responder system](docs/gesture-responder-system.html).
* For each handler, it provides a new `gestureState` object alongside the
* native event object:
*
* ```
* onPanResponderMove: (event, gestureState) => {}
* ```
*
* A native event is a synthetic touch event with the following form:
*
* - `nativeEvent`
* + `changedTouches` - Array of all touch events that have changed since the last event
* + `identifier` - The ID of the touch
* + `locationX` - The X position of the touch, relative to the element
* + `locationY` - The Y position of the touch, relative to the element
* + `pageX` - The X position of the touch, relative to the root element
* + `pageY` - The Y position of the touch, relative to the root element
* + `target` - The node id of the element receiving the touch event
* + `timestamp` - A time identifier for the touch, useful for velocity calculation
* + `touches` - Array of all current touches on the screen
*
* A `gestureState` object has the following:
*
* - `stateID` - ID of the gestureState- persisted as long as there at least
* one touch on screen
* - `moveX` - the latest screen coordinates of the recently-moved touch
* - `moveY` - the latest screen coordinates of the recently-moved touch
* - `x0` - the screen coordinates of the responder grant
* - `y0` - the screen coordinates of the responder grant
* - `dx` - accumulated distance of the gesture since the touch started
* - `dy` - accumulated distance of the gesture since the touch started
* - `vx` - current velocity of the gesture
* - `vy` - current velocity of the gesture
* - `numberActiveTouches` - Number of touches currently on screen
*
* ### Basic Usage
*
* ```
* componentWillMount: function() {
* this._panResponder = PanResponder.create({
* // Ask to be the responder:
* onStartShouldSetPanResponder: (evt, gestureState) => true,
* onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
* onMoveShouldSetPanResponder: (evt, gestureState) => true,
* onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
*
* onPanResponderGrant: (evt, gestureState) => {
* // The guesture has started. Show visual feedback so the user knows
* // what is happening!
*
* // gestureState.{x,y}0 will be set to zero now
* },
* onPanResponderMove: (evt, gestureState) => {
* // The most recent move distance is gestureState.move{X,Y}
*
* // The accumulated gesture distance since becoming responder is
* // gestureState.d{x,y}
* },
* onPanResponderTerminationRequest: (evt, gestureState) => true,
* onPanResponderRelease: (evt, gestureState) => {
* // The user has released all touches while this view is the
* // responder. This typically means a gesture has succeeded
* },
* onPanResponderTerminate: (evt, gestureState) => {
* // Another component has become the responder, so this gesture
* // should be cancelled
* },
* onShouldBlockNativeResponder: (evt, gestureState) => {
* // Returns whether this component should block native components from becoming the JS
* // responder. Returns true by default. Is currently only supported on android.
* return true;
* },
* });
* },
*
* render: function() {
* return (
* <View {...this._panResponder.panHandlers} />
* );
* },
*
* ```
*
* ### Working Example
*
* To see it in action, try the
* [PanResponder example in UIExplorer](https://github.com/facebook/react-native/blob/master/Examples/UIExplorer/PanResponderExample.js)
*/
var PanResponder = {
/**
*
* A graphical explanation of the touch data flow:
*
* +----------------------------+ +--------------------------------+
* | ResponderTouchHistoryStore | |TouchHistoryMath |
* +----------------------------+ +----------+---------------------+
* |Global store of touchHistory| |Allocation-less math util |
* |including activeness, start | |on touch history (centroids |
* |position, prev/cur position.| |and multitouch movement etc) |
* | | | |
* +----^-----------------------+ +----^---------------------------+
* | |
* | (records relevant history |
* | of touches relevant for |
* | implementing higher level |
* | gestures) |
* | |
* +----+-----------------------+ +----|---------------------------+
* | ResponderEventPlugin | | | Your App/Component |
* +----------------------------+ +----|---------------------------+
* |Negotiates which view gets | Low level | | High level |
* |onResponderMove events. | events w/ | +-+-------+ events w/ |
* |Also records history into | touchHistory| | Pan | multitouch + |
* |ResponderTouchHistoryStore. +---------------->Responder+-----> accumulative|
* +----------------------------+ attached to | | | distance and |
* each event | +---------+ velocity. |
* | |
* | |
* +--------------------------------+
*
*
*
* Gesture that calculates cumulative movement over time in a way that just
* "does the right thing" for multiple touches. The "right thing" is very
* nuanced. When moving two touches in opposite directions, the cumulative
* distance is zero in each dimension. When two touches move in parallel five
* pixels in the same direction, the cumulative distance is five, not ten. If
* two touches start, one moves five in a direction, then stops and the other
* touch moves fives in the same direction, the cumulative distance is ten.
*
* This logic requires a kind of processing of time "clusters" of touch events
* so that two touch moves that essentially occur in parallel but move every
* other frame respectively, are considered part of the same movement.
*
* Explanation of some of the non-obvious fields:
*
* - moveX/moveY: If no move event has been observed, then `(moveX, moveY)` is
* invalid. If a move event has been observed, `(moveX, moveY)` is the
* centroid of the most recently moved "cluster" of active touches.
* (Currently all move have the same timeStamp, but later we should add some
* threshold for what is considered to be "moving"). If a palm is
* accidentally counted as a touch, but a finger is moving greatly, the palm
* will move slightly, but we only want to count the single moving touch.
* - x0/y0: Centroid location (non-cumulative) at the time of becoming
* responder.
* - dx/dy: Cumulative touch distance - not the same thing as sum of each touch
* distance. Accounts for touch moves that are clustered together in time,
* moving the same direction. Only valid when currently responder (otherwise,
* it only represents the drag distance below the threshold).
* - vx/vy: Velocity.
*/
_initializeGestureState: function(gestureState) {
gestureState.moveX = 0;
gestureState.moveY = 0;
gestureState.x0 = 0;
gestureState.y0 = 0;
gestureState.dx = 0;
gestureState.dy = 0;
gestureState.vx = 0;
gestureState.vy = 0;
gestureState.numberActiveTouches = 0;
// All `gestureState` accounts for timeStamps up until:
gestureState._accountsForMovesUpTo = 0;
},
/**
* This is nuanced and is necessary. It is incorrect to continuously take all
* active *and* recently moved touches, find the centroid, and track how that
* result changes over time. Instead, we must take all recently moved
* touches, and calculate how the centroid has changed just for those
* recently moved touches, and append that change to an accumulator. This is
* to (at least) handle the case where the user is moving three fingers, and
* then one of the fingers stops but the other two continue.
*
* This is very different than taking all of the recently moved touches and
* storing their centroid as `dx/dy`. For correctness, we must *accumulate
* changes* in the centroid of recently moved touches.
*
* There is also some nuance with how we handle multiple moved touches in a
* single event. With the way `ReactNativeEventEmitter` dispatches touches as
* individual events, multiple touches generate two 'move' events, each of
* them triggering `onResponderMove`. But with the way `PanResponder` works,
* all of the gesture inference is performed on the first dispatch, since it
* looks at all of the touches (even the ones for which there hasn't been a
* native dispatch yet). Therefore, `PanResponder` does not call
* `onResponderMove` passed the first dispatch. This diverges from the
* typical responder callback pattern (without using `PanResponder`), but
* avoids more dispatches than necessary.
*/
_updateGestureStateOnMove: function(gestureState, touchHistory) {
gestureState.numberActiveTouches = touchHistory.numberActiveTouches;
gestureState.moveX = currentCentroidXOfTouchesChangedAfter(
touchHistory,
gestureState._accountsForMovesUpTo
);
gestureState.moveY = currentCentroidYOfTouchesChangedAfter(
touchHistory,
gestureState._accountsForMovesUpTo
);
var movedAfter = gestureState._accountsForMovesUpTo;
var prevX = previousCentroidXOfTouchesChangedAfter(touchHistory, movedAfter);
var x = currentCentroidXOfTouchesChangedAfter(touchHistory, movedAfter);
var prevY = previousCentroidYOfTouchesChangedAfter(touchHistory, movedAfter);
var y = currentCentroidYOfTouchesChangedAfter(touchHistory, movedAfter);
var nextDX = gestureState.dx + (x - prevX);
var nextDY = gestureState.dy + (y - prevY);
// TODO: This must be filtered intelligently.
var dt =
(touchHistory.mostRecentTimeStamp - gestureState._accountsForMovesUpTo);
gestureState.vx = (nextDX - gestureState.dx) / dt;
gestureState.vy = (nextDY - gestureState.dy) / dt;
gestureState.dx = nextDX;
gestureState.dy = nextDY;
gestureState._accountsForMovesUpTo = touchHistory.mostRecentTimeStamp;
},
/**
* @param {object} config Enhanced versions of all of the responder callbacks
* that provide not only the typical `ResponderSyntheticEvent`, but also the
* `PanResponder` gesture state. Simply replace the word `Responder` with
* `PanResponder` in each of the typical `onResponder*` callbacks. For
* example, the `config` object would look like:
*
* - `onMoveShouldSetPanResponder: (e, gestureState) => {...}`
* - `onMoveShouldSetPanResponderCapture: (e, gestureState) => {...}`
* - `onStartShouldSetPanResponder: (e, gestureState) => {...}`
* - `onStartShouldSetPanResponderCapture: (e, gestureState) => {...}`
* - `onPanResponderReject: (e, gestureState) => {...}`
* - `onPanResponderGrant: (e, gestureState) => {...}`
* - `onPanResponderStart: (e, gestureState) => {...}`
* - `onPanResponderEnd: (e, gestureState) => {...}`
* - `onPanResponderRelease: (e, gestureState) => {...}`
* - `onPanResponderMove: (e, gestureState) => {...}`
* - `onPanResponderTerminate: (e, gestureState) => {...}`
* - `onPanResponderTerminationRequest: (e, gestureState) => {...}`
* - `onShouldBlockNativeResponder: (e, gestureState) => {...}`
*
* In general, for events that have capture equivalents, we update the
* gestureState once in the capture phase and can use it in the bubble phase
* as well.
*
* Be careful with onStartShould* callbacks. They only reflect updated
* `gestureState` for start/end events that bubble/capture to the Node.
* Once the node is the responder, you can rely on every start/end event
* being processed by the gesture and `gestureState` being updated
* accordingly. (numberActiveTouches) may not be totally accurate unless you
* are the responder.
*/
create: function(config) {
var gestureState = {
// Useful for debugging
stateID: Math.random(),
};
PanResponder._initializeGestureState(gestureState);
var panHandlers = {
onStartShouldSetResponder: function(e) {
return config.onStartShouldSetPanResponder === undefined ? false :
config.onStartShouldSetPanResponder(e, gestureState);
},
onMoveShouldSetResponder: function(e) {
return config.onMoveShouldSetPanResponder === undefined ? false :
config.onMoveShouldSetPanResponder(e, gestureState);
},
onStartShouldSetResponderCapture: function(e) {
// TODO: Actually, we should reinitialize the state any time
// touches.length increases from 0 active to > 0 active.
if (e.nativeEvent.touches.length === 1) {
PanResponder._initializeGestureState(gestureState);
}
gestureState.numberActiveTouches = e.touchHistory.numberActiveTouches;
return config.onStartShouldSetPanResponderCapture !== undefined ?
config.onStartShouldSetPanResponderCapture(e, gestureState) : false;
},
onMoveShouldSetResponderCapture: function(e) {
var touchHistory = e.touchHistory;
// Responder system incorrectly dispatches should* to current responder
// Filter out any touch moves past the first one - we would have
// already processed multi-touch geometry during the first event.
if (gestureState._accountsForMovesUpTo === touchHistory.mostRecentTimeStamp) {
return false;
}
PanResponder._updateGestureStateOnMove(gestureState, touchHistory);
return config.onMoveShouldSetPanResponderCapture ?
config.onMoveShouldSetPanResponderCapture(e, gestureState) : false;
},
onResponderGrant: function(e) {
gestureState.x0 = currentCentroidX(e.touchHistory);
gestureState.y0 = currentCentroidY(e.touchHistory);
gestureState.dx = 0;
gestureState.dy = 0;
config.onPanResponderGrant && config.onPanResponderGrant(e, gestureState);
// TODO: t7467124 investigate if this can be removed
return config.onShouldBlockNativeResponder === undefined ? true :
config.onShouldBlockNativeResponder();
},
onResponderReject: function(e) {
config.onPanResponderReject && config.onPanResponderReject(e, gestureState);
},
onResponderRelease: function(e) {
config.onPanResponderRelease && config.onPanResponderRelease(e, gestureState);
PanResponder._initializeGestureState(gestureState);
},
onResponderStart: function(e) {
var touchHistory = e.touchHistory;
gestureState.numberActiveTouches = touchHistory.numberActiveTouches;
config.onPanResponderStart && config.onPanResponderStart(e, gestureState);
},
onResponderMove: function(e) {
var touchHistory = e.touchHistory;
// Guard against the dispatch of two touch moves when there are two
// simultaneously changed touches.
if (gestureState._accountsForMovesUpTo === touchHistory.mostRecentTimeStamp) {
return;
}
// Filter out any touch moves past the first one - we would have
// already processed multi-touch geometry during the first event.
PanResponder._updateGestureStateOnMove(gestureState, touchHistory);
config.onPanResponderMove && config.onPanResponderMove(e, gestureState);
},
onResponderEnd: function(e) {
var touchHistory = e.touchHistory;
gestureState.numberActiveTouches = touchHistory.numberActiveTouches;
config.onPanResponderEnd && config.onPanResponderEnd(e, gestureState);
},
onResponderTerminate: function(e) {
config.onPanResponderTerminate &&
config.onPanResponderTerminate(e, gestureState);
PanResponder._initializeGestureState(gestureState);
},
onResponderTerminationRequest: function(e) {
return config.onPanResponderTerminationRequest === undefined ? true :
config.onPanResponderTerminationRequest(e, gestureState);
},
};
return {panHandlers: panHandlers};
},
};
module.exports = PanResponder;

View File

@ -1,122 +0,0 @@
/**
* @providesModule TouchHistoryMath
*/
"use strict";
var TouchHistoryMath = {
/**
* This code is optimized and not intended to look beautiful. This allows
* computing of touch centroids that have moved after `touchesChangedAfter`
* timeStamp. You can compute the current centroid involving all touches
* moves after `touchesChangedAfter`, or you can compute the previous
* centroid of all touches that were moved after `touchesChangedAfter`.
*
* @param {TouchHistoryMath} touchHistory Standard Responder touch track
* data.
* @param {number} touchesChangedAfter timeStamp after which moved touches
* are considered "actively moving" - not just "active".
* @param {boolean} isXAxis Consider `x` dimension vs. `y` dimension.
* @param {boolean} ofCurrent Compute current centroid for actively moving
* touches vs. previous centroid of now actively moving touches.
* @return {number} value of centroid in specified dimension.
*/
centroidDimension: function(touchHistory, touchesChangedAfter, isXAxis, ofCurrent) {
var touchBank = touchHistory.touchBank;
var total = 0;
var count = 0;
var oneTouchData = touchHistory.numberActiveTouches === 1 ?
touchHistory.touchBank[touchHistory.indexOfSingleActiveTouch] : null;
if (oneTouchData !== null) {
if (oneTouchData.touchActive && oneTouchData.currentTimeStamp > touchesChangedAfter) {
total += ofCurrent && isXAxis ? oneTouchData.currentPageX :
ofCurrent && !isXAxis ? oneTouchData.currentPageY :
!ofCurrent && isXAxis ? oneTouchData.previousPageX :
oneTouchData.previousPageY;
count = 1;
}
} else {
for (var i = 0; i < touchBank.length; i++) {
var touchTrack = touchBank[i];
if (touchTrack !== null &&
touchTrack !== undefined &&
touchTrack.touchActive &&
touchTrack.currentTimeStamp >= touchesChangedAfter) {
var toAdd; // Yuck, program temporarily in invalid state.
if (ofCurrent && isXAxis) {
toAdd = touchTrack.currentPageX;
} else if (ofCurrent && !isXAxis) {
toAdd = touchTrack.currentPageY;
} else if (!ofCurrent && isXAxis) {
toAdd = touchTrack.previousPageX;
} else {
toAdd = touchTrack.previousPageY;
}
total += toAdd;
count++;
}
}
}
return count > 0 ? total / count : TouchHistoryMath.noCentroid;
},
currentCentroidXOfTouchesChangedAfter: function(touchHistory, touchesChangedAfter) {
return TouchHistoryMath.centroidDimension(
touchHistory,
touchesChangedAfter,
true, // isXAxis
true // ofCurrent
);
},
currentCentroidYOfTouchesChangedAfter: function(touchHistory, touchesChangedAfter) {
return TouchHistoryMath.centroidDimension(
touchHistory,
touchesChangedAfter,
false, // isXAxis
true // ofCurrent
);
},
previousCentroidXOfTouchesChangedAfter: function(touchHistory, touchesChangedAfter) {
return TouchHistoryMath.centroidDimension(
touchHistory,
touchesChangedAfter,
true, // isXAxis
false // ofCurrent
);
},
previousCentroidYOfTouchesChangedAfter: function(touchHistory, touchesChangedAfter) {
return TouchHistoryMath.centroidDimension(
touchHistory,
touchesChangedAfter,
false, // isXAxis
false // ofCurrent
);
},
currentCentroidX: function(touchHistory) {
return TouchHistoryMath.centroidDimension(
touchHistory,
0, // touchesChangedAfter
true, // isXAxis
true // ofCurrent
);
},
currentCentroidY: function(touchHistory) {
return TouchHistoryMath.centroidDimension(
touchHistory,
0, // touchesChangedAfter
false, // isXAxis
true // ofCurrent
);
},
noCentroid: -1,
};
module.exports = TouchHistoryMath;

View File

@ -1,83 +0,0 @@
/**
* @providesModule NodeHandle
*/
/**
* A "handle" is a serializable representation of the underlying platform's
* native node abstraction. This allows reasoning about nodes behind a thread
* (worker) boundary. On some platforms (DOM main thread) the node handle *is*
* an actual DOM node - `NodeHandle` (and potentially other libraries)
* abstract away those differences so you can write code that doesn't depend
* on whether or not you are running in a worker. For example, you could write
* application code:
*
*
* SomeLibrary.measureNodeHandle(myNodeHandle, cb)
*
* Where `measureNodeHandle` knows how to handle actual DOM nodes if running
* in a worker thread, and knows how to handle numeric IDs if running in a
* worker thread.
*
* The only other requirement of a platform/environment is that it always be
* possible to extract the React rootNodeID in a blocking manner (see
* `getRootNodeID`).
*
* +------------------+ +------------------+ +------------------+
* | | | | | |
* | ReactJS | | YourUtilities | | Animation Utils |
* | | | | | |
* +------------------+ +------------------+ +------------------+
*
* +------------------------------------------------------------+
* | Async Platform Independent Node Interface |
* +------------------------------------------------------------+
* | |
* | NodeIterface: |
* | -measure(nodeHandle, cb) |
* | -setProperties(nodeHandle, cb) |
* | -manageChildren(nodeHandle, nodeHandles, cb) |
* | ... |
* | |
* | Note: This may be a simplification. We could break up much |
* | of this functionality into several smaller libraries, each |
* | one requiring a . |
* +------------------------------------------------------------+
*
* +------------------------------------------------------------+
* | Platform Implementations |
* | ----------------------------------------- |
* | React Canvas | React DOM Worker | React DOM main |
* +------------------------------------------------------------+
* | | | |
* |-measure(..) |-measure(..) |-measure(..) |
* |-setProperties(..) |-setProperties(..) |-setProperties(..) |
* |-manageChildren(..)|-manageChildren(..) |-manageChildren(..)|
* | ... | ... | ... |
* +-----------------------------o------------------------------+
* | Worker simply ^
* | marshals commands |
* | to Web DOM thread. |
* +---------------------+
*/
var NodeHandle = {
/**
* Injection
*/
injection: {
injectImplementation: function(Impl) {
NodeHandle._Implementation = Impl;
}
},
_Implementation: null,
/**
* @param {NodeHandle} nodeHandle The handle to the low level resource.
* @return {string} React root node ID.
*/
getRootNodeID: function(nodeHandle) {
return NodeHandle._Implementation.getRootNodeID(nodeHandle);
}
};
module.exports = NodeHandle;

View File

@ -1,19 +0,0 @@
/**
* @providesModule UniversalWorkerNodeHandle
*/
var ReactNativeTagHandles = require('ReactNativeTagHandles');
var invariant = require('fbjs/lib/invariant');
var UniversalWorkerNodeHandle = {
getRootNodeID: function(nodeHandle) {
invariant(
nodeHandle !== undefined && nodeHandle !== null && nodeHandle !== 0,
'No node handle defined'
);
return ReactNativeTagHandles.tagToRootNodeID[nodeHandle];
}
};
module.exports = UniversalWorkerNodeHandle;

View File

@ -1,51 +0,0 @@
/**
* Copyright 2013-2014 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @providesModule ExecutionEnvironment
*
* NB: This is a temporary override that has not yet been merged upstream. It is NOT actually part
* of react-core (yet)
*
* Stubs SignedSource<<7afee88a05412d0c4eef54817419648e>>
*/
/*jslint evil: true */
"use strict";
var canUseDOM = false;
/**
* Simple, lightweight module assisting with the detection and context of
* Worker. Helps avoid circular dependencies and allows code to reason about
* whether or not they are in a Worker, even if they never include the main
* `ReactWorker` dependency.
*/
var ExecutionEnvironment = {
canUseDOM: canUseDOM,
canUseWorkers: typeof Worker !== 'undefined',
canUseEventListeners:
canUseDOM && !!(window.addEventListener || window.attachEvent),
canUseViewport: canUseDOM && !!window.screen,
isInWorker: !canUseDOM // For now, this is true - might change in the future.
};
module.exports = ExecutionEnvironment;

View File

@ -1,48 +0,0 @@
/**
* Copyright 2013-2014 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @providesModule ExecutionEnvironment
*
* Stubs SignedSource<<7afee88a05412d0c4eef54817419648e>>
*/
/*jslint evil: true */
"use strict";
var canUseDOM = false;
/**
* Simple, lightweight module assisting with the detection and context of
* Worker. Helps avoid circular dependencies and allows code to reason about
* whether or not they are in a Worker, even if they never include the main
* `ReactWorker` dependency.
*/
var ExecutionEnvironment = {
canUseDOM: canUseDOM,
canUseWorkers: typeof Worker !== 'undefined',
canUseEventListeners:
canUseDOM && !!(window.addEventListener || window.attachEvent),
canUseViewport: canUseDOM && !!window.screen,
isInWorker: !canUseDOM // For now, this is true - might change in the future.
};
module.exports = ExecutionEnvironment;

View File

@ -126,7 +126,7 @@
"react-native": "local-cli/wrong-react-native.js"
},
"peerDependencies": {
"react": "^0.14.5"
"react": "15.0.2-alpha.1"
},
"dependencies": {
"absolute-path": "^0.0.0",
@ -145,7 +145,7 @@
"core-js": "^2.2.2",
"debug": "^2.2.0",
"event-target-shim": "^1.0.5",
"fbjs": "^0.7.2",
"fbjs": "^0.8.0",
"fbjs-scripts": "^0.4.0",
"graceful-fs": "^4.1.2",
"image-size": "^0.3.5",
@ -189,7 +189,7 @@
"flow-bin": "0.23.0",
"jest-cli": "0.9.2",
"portfinder": "0.4.0",
"react": "^0.14.5",
"react": "15.0.2-alpha.1",
"shelljs": "0.6.0"
}
}

View File

@ -14,8 +14,6 @@ var path = require('path');
// modulePathIgnorePatterns.
var sharedBlacklist = [
/node_modules[/\\]react[/\\]dist[/\\].*/,
'node_modules/react/lib/React.js',
'node_modules/react/lib/ReactDOM.js',
'downstream/core/invariant.js',