mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-27 19:25:11 +08:00
[react-packager][streamline oss] Move open sourced JS source to react-native-github
This commit is contained in:
13
Libraries/ReactIOS/IOSDefaultEventPluginOrder.js
Normal file
13
Libraries/ReactIOS/IOSDefaultEventPluginOrder.js
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* @providesModule IOSDefaultEventPluginOrder
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var IOSDefaultEventPluginOrder = [
|
||||
'ResponderEventPlugin',
|
||||
'IOSNativeBridgeEventPlugin'
|
||||
];
|
||||
|
||||
module.exports = IOSDefaultEventPluginOrder;
|
||||
72
Libraries/ReactIOS/IOSNativeBridgeEventPlugin.js
Normal file
72
Libraries/ReactIOS/IOSNativeBridgeEventPlugin.js
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* @providesModule IOSNativeBridgeEventPlugin
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var EventPropagators = require('EventPropagators');
|
||||
var NativeModulesDeprecated = require('NativeModulesDeprecated');
|
||||
var SyntheticEvent = require('SyntheticEvent');
|
||||
|
||||
var merge = require('merge');
|
||||
var warning = require('warning');
|
||||
|
||||
var RKUIManager = NativeModulesDeprecated.RKUIManager;
|
||||
|
||||
var customBubblingEventTypes = RKUIManager.customBubblingEventTypes;
|
||||
var customDirectEventTypes = RKUIManager.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,
|
||||
topLevelTarget,
|
||||
topLevelTargetID,
|
||||
nativeEvent) {
|
||||
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;
|
||||
|
||||
123
Libraries/ReactIOS/NativeMethodsMixin.js
Normal file
123
Libraries/ReactIOS/NativeMethodsMixin.js
Normal file
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* @providesModule NativeMethodsMixin
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var NativeModules = require('NativeModules');
|
||||
var NativeModulesDeprecated = require('NativeModulesDeprecated');
|
||||
var RKUIManager = NativeModules.RKUIManager;
|
||||
var RKUIManagerDeprecated = NativeModulesDeprecated.RKUIManager;
|
||||
var RKPOPAnimationManagerDeprecated = NativeModulesDeprecated.RKPOPAnimationManager;
|
||||
var TextInputState = require('TextInputState');
|
||||
|
||||
var flattenStyle = require('flattenStyle');
|
||||
var invariant = require('invariant');
|
||||
var mergeFast = require('mergeFast');
|
||||
|
||||
var animationIDInvariant = function(funcName, anim) {
|
||||
invariant(
|
||||
anim,
|
||||
funcName + ' must be called with a valid animation ID returned from' +
|
||||
' POPAnimation.createAnimation, received: "' + anim + '"'
|
||||
);
|
||||
};
|
||||
|
||||
var NativeMethodsMixin = {
|
||||
addAnimation: function(anim, callback) {
|
||||
animationIDInvariant('addAnimation', anim);
|
||||
RKPOPAnimationManagerDeprecated.addAnimation(this.getNodeHandle(), anim, callback);
|
||||
},
|
||||
|
||||
removeAnimation: function(anim) {
|
||||
animationIDInvariant('removeAnimation', anim);
|
||||
RKPOPAnimationManagerDeprecated.removeAnimation(this.getNodeHandle(), anim);
|
||||
},
|
||||
|
||||
measure: function(callback) {
|
||||
RKUIManagerDeprecated.measure(this.getNodeHandle(), callback);
|
||||
},
|
||||
|
||||
measureLayout: function(relativeToNativeNode, onSuccess, onFail) {
|
||||
RKUIManager.measureLayout(
|
||||
this.getNodeHandle(),
|
||||
relativeToNativeNode,
|
||||
onFail,
|
||||
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.
|
||||
*/
|
||||
setNativeProps: function(nativeProps) {
|
||||
// nativeProps contains a style attribute that's going to be flattened
|
||||
// and all the attributes expanded in place. In order to make this
|
||||
// process do as few allocations and copies as possible, we return
|
||||
// one if the other is empty. Only if both have values then we create
|
||||
// a new object and merge.
|
||||
var hasOnlyStyle = true;
|
||||
for (var key in nativeProps) {
|
||||
if (key !== 'style') {
|
||||
hasOnlyStyle = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
var style = flattenStyle(nativeProps.style);
|
||||
|
||||
var props = null;
|
||||
if (hasOnlyStyle) {
|
||||
props = style;
|
||||
} else if (!style) {
|
||||
props = nativeProps;
|
||||
} else {
|
||||
props = mergeFast(nativeProps, style);
|
||||
}
|
||||
|
||||
RKUIManagerDeprecated.updateView(
|
||||
this.getNodeHandle(),
|
||||
this.viewConfig.uiViewClassName,
|
||||
props
|
||||
);
|
||||
},
|
||||
|
||||
focus: function() {
|
||||
TextInputState.focusTextInput(this.getNodeHandle());
|
||||
},
|
||||
|
||||
blur: function() {
|
||||
TextInputState.blurTextInput(this.getNodeHandle());
|
||||
}
|
||||
};
|
||||
|
||||
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__) {
|
||||
invariant(
|
||||
!NativeMethodsMixin.componentWillMount &&
|
||||
!NativeMethodsMixin.componentWillReceiveProps,
|
||||
'Do not override existing functions.'
|
||||
);
|
||||
NativeMethodsMixin.componentWillMount = function () {
|
||||
throwOnStylesProp(this, this.props);
|
||||
};
|
||||
NativeMethodsMixin.componentWillReceiveProps = function (newProps) {
|
||||
throwOnStylesProp(this, newProps);
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = NativeMethodsMixin;
|
||||
21
Libraries/ReactIOS/NativeModules/RKRawText.js
Normal file
21
Libraries/ReactIOS/NativeModules/RKRawText.js
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* @providesModule RKRawText
|
||||
* @typechecks static-only
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var ReactIOSViewAttributes = require('ReactIOSViewAttributes');
|
||||
|
||||
var createReactIOSNativeComponentClass = require('createReactIOSNativeComponentClass');
|
||||
|
||||
var RKRawText = createReactIOSNativeComponentClass({
|
||||
validAttributes: {
|
||||
text: true,
|
||||
},
|
||||
uiViewClassName: 'RCTRawText',
|
||||
});
|
||||
|
||||
module.exports = RKRawText;
|
||||
21
Libraries/ReactIOS/React.js
Normal file
21
Libraries/ReactIOS/React.js
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* 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 React
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = require('ReactIOS');
|
||||
117
Libraries/ReactIOS/ReactIOS.js
Normal file
117
Libraries/ReactIOS/ReactIOS.js
Normal file
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* @providesModule ReactIOS
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var ReactComponent = require('ReactComponent');
|
||||
var ReactCompositeComponent = require('ReactCompositeComponent');
|
||||
var ReactContext = require('ReactContext');
|
||||
var ReactCurrentOwner = require('ReactCurrentOwner');
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactElementValidator = require('ReactElementValidator');
|
||||
var ReactInstanceHandles = require('ReactInstanceHandles');
|
||||
var ReactIOSDefaultInjection = require('ReactIOSDefaultInjection');
|
||||
var ReactIOSMount = require('ReactIOSMount');
|
||||
var ReactLegacyElement = require('ReactLegacyElement');
|
||||
var ReactPropTypes = require('ReactPropTypes');
|
||||
|
||||
var deprecated = require('deprecated');
|
||||
var invariant = require('invariant');
|
||||
|
||||
ReactIOSDefaultInjection.inject();
|
||||
|
||||
var createElement = ReactElement.createElement;
|
||||
var createFactory = ReactElement.createFactory;
|
||||
|
||||
if (__DEV__) {
|
||||
createElement = ReactElementValidator.createElement;
|
||||
createFactory = ReactElementValidator.createFactory;
|
||||
}
|
||||
|
||||
// TODO: Drop legacy elements once classes no longer export these factories
|
||||
createElement = ReactLegacyElement.wrapCreateElement(
|
||||
createElement
|
||||
);
|
||||
createFactory = ReactLegacyElement.wrapCreateFactory(
|
||||
createFactory
|
||||
);
|
||||
|
||||
var resolveDefaultProps = function(element) {
|
||||
// Could be optimized, but not currently in heavy use.
|
||||
var defaultProps = element.type.defaultProps;
|
||||
var props = element.props;
|
||||
for (var propName in defaultProps) {
|
||||
if (props[propName] === undefined) {
|
||||
props[propName] = defaultProps[propName];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Experimental optimized element creation
|
||||
var augmentElement = function(element) {
|
||||
if (__DEV__) {
|
||||
invariant(
|
||||
false,
|
||||
'This optimized path should never be used in DEV mode because ' +
|
||||
'it does not provide validation. Check your JSX transform.'
|
||||
);
|
||||
}
|
||||
element._owner = ReactCurrentOwner.current;
|
||||
element._context = ReactContext.current;
|
||||
if (element.type.defaultProps) {
|
||||
resolveDefaultProps(element);
|
||||
}
|
||||
return element;
|
||||
};
|
||||
|
||||
var render = function(component, mountInto) {
|
||||
ReactIOSMount.renderComponent(component, mountInto);
|
||||
};
|
||||
|
||||
var ReactIOS = {
|
||||
hasReactIOSInitialized: false,
|
||||
PropTypes: ReactPropTypes,
|
||||
createClass: ReactCompositeComponent.createClass,
|
||||
createElement: createElement,
|
||||
createFactory: createFactory,
|
||||
_augmentElement: augmentElement,
|
||||
render: render,
|
||||
unmountComponentAtNode: ReactIOSMount.unmountComponentAtNode,
|
||||
/**
|
||||
* Used by the debugger.
|
||||
*/
|
||||
__internals: {
|
||||
Component: ReactComponent,
|
||||
CurrentOwner: ReactCurrentOwner,
|
||||
InstanceHandles: ReactInstanceHandles,
|
||||
Mount: ReactIOSMount,
|
||||
},
|
||||
|
||||
// Hook for JSX spread, don't use this for anything else.
|
||||
__spread: Object.assign,
|
||||
|
||||
unmountComponentAtNodeAndRemoveContainer: ReactIOSMount.unmountComponentAtNodeAndRemoveContainer,
|
||||
isValidClass: ReactElement.isValidFactory,
|
||||
isValidElement: ReactElement.isValidElement,
|
||||
|
||||
// Deprecations (remove for 0.13)
|
||||
renderComponent: deprecated(
|
||||
'React',
|
||||
'renderComponent',
|
||||
'render',
|
||||
this,
|
||||
render
|
||||
),
|
||||
isValidComponent: deprecated(
|
||||
'React',
|
||||
'isValidComponent',
|
||||
'isValidElement',
|
||||
this,
|
||||
ReactElement.isValidElement
|
||||
)
|
||||
};
|
||||
|
||||
module.exports = ReactIOS;
|
||||
68
Libraries/ReactIOS/ReactIOSComponentEnvironment.js
Normal file
68
Libraries/ReactIOS/ReactIOSComponentEnvironment.js
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* @providesModule ReactIOSComponentEnvironment
|
||||
*/
|
||||
'use strict';
|
||||
var RKUIManager = require('NativeModulesDeprecated').RKUIManager;
|
||||
|
||||
var ReactIOSDOMIDOperations = require('ReactIOSDOMIDOperations');
|
||||
var ReactIOSReconcileTransaction = require('ReactIOSReconcileTransaction');
|
||||
var ReactIOSTagHandles = require('ReactIOSTagHandles');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
|
||||
var ReactIOSComponentEnvironment = {
|
||||
|
||||
/**
|
||||
* Will need to supply something that implements this.
|
||||
*/
|
||||
BackendIDOperations: ReactIOSDOMIDOperations,
|
||||
|
||||
/**
|
||||
* Nothing to do for UIKit bridge.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
unmountIDFromEnvironment: function(/*rootNodeID*/) {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {DOMElement} Element to clear.
|
||||
*/
|
||||
clearNode: function(/*containerView*/) {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* @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.
|
||||
ReactIOSTagHandles.associateRootNodeIDWithMountedNodeHandle(
|
||||
mountImage.rootNodeID,
|
||||
mountImage.tag
|
||||
);
|
||||
var addChildTags = [mountImage.tag];
|
||||
var addAtIndices = [0];
|
||||
RKUIManager.manageChildren(
|
||||
ReactIOSTagHandles.mostRecentMountedNodeHandleForRootNodeID(containerID),
|
||||
null, // moveFromIndices
|
||||
null, // moveToIndices
|
||||
addChildTags,
|
||||
addAtIndices,
|
||||
null // removeAtIndices
|
||||
);
|
||||
}
|
||||
),
|
||||
|
||||
ReactReconcileTransaction: ReactIOSReconcileTransaction,
|
||||
};
|
||||
|
||||
module.exports = ReactIOSComponentEnvironment;
|
||||
66
Libraries/ReactIOS/ReactIOSComponentMixin.js
Normal file
66
Libraries/ReactIOS/ReactIOSComponentMixin.js
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* @providesModule ReactIOSComponentMixin
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var ReactIOSTagHandles = require('ReactIOSTagHandles');
|
||||
|
||||
/**
|
||||
* 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 `ReactIOSNativeComponent`) 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
|
||||
*
|
||||
*
|
||||
* `mountImage`: A way to represent the potential to create lower level
|
||||
* resources whos `nodeHandle` can be discovered immediately by knowing the
|
||||
* `rootNodeID`. Today's web react represents this with `innerHTML` annotated
|
||||
* with DOM ids that match the `rootNodeID`.
|
||||
*
|
||||
* Opaque name TodaysWebReact FutureWebWorkerReact ReactNative
|
||||
* ----------------------------------------------------------------------------
|
||||
* mountImage innerHTML innerHTML {rootNodeID, tag}
|
||||
*
|
||||
*/
|
||||
var ReactIOSComponentMixin = {
|
||||
/**
|
||||
* This has no particular meaning in ReactIOS. If this were in the DOM, this
|
||||
* would return the DOM node. There should be nothing that invokes this
|
||||
* method. Any current callers of this are mistaken - they should be invoking
|
||||
* `getNodeHandle`.
|
||||
*/
|
||||
getNativeNode: function() {
|
||||
return ReactIOSTagHandles.rootNodeIDToTag[this._rootNodeID];
|
||||
},
|
||||
|
||||
getNodeHandle: function() {
|
||||
return ReactIOSTagHandles.rootNodeIDToTag[this._rootNodeID];
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = ReactIOSComponentMixin;
|
||||
98
Libraries/ReactIOS/ReactIOSDOMIDOperations.js
Normal file
98
Libraries/ReactIOS/ReactIOSDOMIDOperations.js
Normal file
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* @providesModule ReactIOSDOMIDOperations
|
||||
* @typechecks static-only
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var ReactIOSTagHandles = require('ReactIOSTagHandles');
|
||||
var ReactMultiChildUpdateTypes = require('ReactMultiChildUpdateTypes');
|
||||
var RKUIManager = require('NativeModulesDeprecated').RKUIManager;
|
||||
var ReactPerf = require('ReactPerf');
|
||||
|
||||
/**
|
||||
* 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 = ReactIOSTagHandles.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;
|
||||
ReactIOSTagHandles.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];
|
||||
RKUIManager.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 ReactIOSDOMIDOperations = {
|
||||
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 = ReactIOSTagHandles.mostRecentMountedNodeHandleForRootNodeID(id);
|
||||
RKUIManager.replaceExistingNonRootView(oldTag, mountImage.tag);
|
||||
ReactIOSTagHandles.associateRootNodeIDWithMountedNodeHandle(id, mountImage.tag);
|
||||
}
|
||||
),
|
||||
};
|
||||
|
||||
module.exports = ReactIOSDOMIDOperations;
|
||||
94
Libraries/ReactIOS/ReactIOSDefaultInjection.js
Normal file
94
Libraries/ReactIOS/ReactIOSDefaultInjection.js
Normal file
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* @providesModule ReactIOSDefaultInjection
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Make sure `setTimeout`/`setInterval` are patched correctly.
|
||||
*/
|
||||
require('InitializeJavaScriptAppEngine');
|
||||
var EventPluginHub = require('EventPluginHub');
|
||||
var EventPluginUtils = require('EventPluginUtils');
|
||||
var IOSDefaultEventPluginOrder = require('IOSDefaultEventPluginOrder');
|
||||
var IOSNativeBridgeEventPlugin = require('IOSNativeBridgeEventPlugin');
|
||||
var NodeHandle = require('NodeHandle');
|
||||
var ReactComponent = require('ReactComponent');
|
||||
var ReactCompositeComponent = require('ReactCompositeComponent');
|
||||
var ReactDefaultBatchingStrategy = require('ReactDefaultBatchingStrategy');
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactInstanceHandles = require('ReactInstanceHandles');
|
||||
var ReactIOSComponentEnvironment = require('ReactIOSComponentEnvironment');
|
||||
var ReactIOSComponentMixin = require('ReactIOSComponentMixin');
|
||||
var ReactIOSGlobalInteractionHandler = require('ReactIOSGlobalInteractionHandler');
|
||||
var ReactIOSGlobalResponderHandler = require('ReactIOSGlobalResponderHandler');
|
||||
var ReactIOSMount = require('ReactIOSMount');
|
||||
var ReactTextComponent = require('ReactTextComponent');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
var ResponderEventPlugin = require('ResponderEventPlugin');
|
||||
var RKRawText = require('RKRawText');
|
||||
var UniversalWorkerNodeHandle = require('UniversalWorkerNodeHandle');
|
||||
|
||||
// Just to ensure this gets packaged, since its only caller is from Native.
|
||||
require('RCTEventEmitter');
|
||||
require('RCTLog');
|
||||
require('RCTJSTimers');
|
||||
|
||||
function inject() {
|
||||
/**
|
||||
* Inject module for resolving DOM hierarchy and plugin ordering.
|
||||
*/
|
||||
EventPluginHub.injection.injectEventPluginOrder(IOSDefaultEventPluginOrder);
|
||||
EventPluginHub.injection.injectInstanceHandle(ReactInstanceHandles);
|
||||
|
||||
ResponderEventPlugin.injection.injectGlobalResponderHandler(
|
||||
ReactIOSGlobalResponderHandler
|
||||
);
|
||||
|
||||
ResponderEventPlugin.injection.injectGlobalInteractionHandler(
|
||||
ReactIOSGlobalInteractionHandler
|
||||
);
|
||||
|
||||
/**
|
||||
* Some important event plugins included by default (without having to require
|
||||
* them).
|
||||
*/
|
||||
EventPluginHub.injection.injectEventPluginsByName({
|
||||
'ResponderEventPlugin': ResponderEventPlugin,
|
||||
'IOSNativeBridgeEventPlugin': IOSNativeBridgeEventPlugin
|
||||
});
|
||||
|
||||
ReactUpdates.injection.injectReconcileTransaction(
|
||||
ReactIOSComponentEnvironment.ReactReconcileTransaction
|
||||
);
|
||||
|
||||
ReactUpdates.injection.injectBatchingStrategy(
|
||||
ReactDefaultBatchingStrategy
|
||||
);
|
||||
|
||||
ReactComponent.injection.injectEnvironment(
|
||||
ReactIOSComponentEnvironment
|
||||
);
|
||||
|
||||
EventPluginUtils.injection.injectMount(ReactIOSMount);
|
||||
|
||||
ReactCompositeComponent.injection.injectMixin(ReactIOSComponentMixin);
|
||||
|
||||
ReactTextComponent.inject(function(initialText) {
|
||||
// RKRawText is a class so we can't invoke it directly. Instead of using
|
||||
// a factory, we use the internal fast path to create a descriptor.
|
||||
// RKRawText is not quite a class yet, so we access the real class from
|
||||
// the type property. TODO: Change this once factory wrappers are gone.
|
||||
return new ReactElement(RKRawText.type, null, null, null, null, {
|
||||
text: initialText
|
||||
});
|
||||
});
|
||||
|
||||
NodeHandle.injection.injectImplementation(UniversalWorkerNodeHandle);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
inject: inject,
|
||||
};
|
||||
191
Libraries/ReactIOS/ReactIOSEventEmitter.js
Normal file
191
Libraries/ReactIOS/ReactIOSEventEmitter.js
Normal file
@@ -0,0 +1,191 @@
|
||||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* @providesModule ReactIOSEventEmitter
|
||||
* @typechecks static-only
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var EventPluginHub = require('EventPluginHub');
|
||||
var ReactEventEmitterMixin = require('ReactEventEmitterMixin');
|
||||
var ReactIOSTagHandles = require('ReactIOSTagHandles');
|
||||
var NodeHandle = require('NodeHandle');
|
||||
var EventConstants = require('EventConstants');
|
||||
|
||||
var merge = require('merge');
|
||||
var warning = require('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, indices) {
|
||||
var rippedOut = [];
|
||||
for (var i = 0; i < indices.length; i++) {
|
||||
var index = indices[i];
|
||||
rippedOut.push(touches[index]);
|
||||
touches[index] = null;
|
||||
}
|
||||
var fillAt = 0;
|
||||
for (var j = 0; j < touches.length; j++) {
|
||||
var cur = touches[j];
|
||||
if (cur !== null) {
|
||||
touches[fillAt++] = cur;
|
||||
}
|
||||
}
|
||||
touches.length = fillAt;
|
||||
return rippedOut;
|
||||
};
|
||||
|
||||
/**
|
||||
* `ReactIOSEventEmitter` is used to attach top-level event listeners. For example:
|
||||
*
|
||||
* ReactIOSEventEmitter.putListener('myID', 'onClick', myFunction);
|
||||
*
|
||||
* This would allocate a "registration" of `('onClick', myFunction)` on 'myID'.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
var ReactIOSEventEmitter = 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 occured on.
|
||||
* @param {TopLevelType} topLevelType Top level type of event.
|
||||
* @param {object} nativeEventParam Object passed from native.
|
||||
*/
|
||||
_receiveRootNodeIDEvent: function(rootNodeID, topLevelType, nativeEventParam) {
|
||||
var nativeEvent = nativeEventParam || EMPTY_NATIVE_EVENT;
|
||||
ReactIOSEventEmitter.handleTopLevel(
|
||||
topLevelType,
|
||||
rootNodeID,
|
||||
rootNodeID,
|
||||
nativeEvent
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Publically 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 occured on.
|
||||
* @param {TopLevelType} topLevelType Top level type of event.
|
||||
* @param {object} nativeEventParam Object passed from native.
|
||||
*/
|
||||
receiveEvent: function(tag, topLevelType, nativeEventParam) {
|
||||
var rootNodeID = ReactIOSTagHandles.tagToRootNodeID[tag];
|
||||
ReactIOSEventEmitter._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, touches, changedIndices) {
|
||||
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 fullfill 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 < ReactIOSTagHandles.tagsStartAt) {
|
||||
if (__DEV__) {
|
||||
warning(
|
||||
false,
|
||||
'A view is reporting that a touch occured on tag zero.'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
rootNodeID = NodeHandle.getRootNodeID(target);
|
||||
}
|
||||
}
|
||||
ReactIOSEventEmitter._receiveRootNodeIDEvent(
|
||||
rootNodeID,
|
||||
eventTopLevelType,
|
||||
nativeEvent
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = ReactIOSEventEmitter;
|
||||
25
Libraries/ReactIOS/ReactIOSGlobalInteractionHandler.js
Normal file
25
Libraries/ReactIOS/ReactIOSGlobalInteractionHandler.js
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @providesModule ReactIOSGlobalInteractionHandler
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var InteractionManager = require('InteractionManager');
|
||||
|
||||
// Interaction handle is created/cleared when responder is granted or
|
||||
// released/terminated.
|
||||
var interactionHandle = null;
|
||||
|
||||
var ReactIOSGlobalInteractionHandler = {
|
||||
onChange: function(numberActiveTouches) {
|
||||
if (numberActiveTouches === 0) {
|
||||
if (interactionHandle) {
|
||||
InteractionManager.clearInteractionHandle(interactionHandle);
|
||||
interactionHandle = null;
|
||||
}
|
||||
} else if (!interactionHandle) {
|
||||
interactionHandle = InteractionManager.createInteractionHandle();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = ReactIOSGlobalInteractionHandler;
|
||||
21
Libraries/ReactIOS/ReactIOSGlobalResponderHandler.js
Normal file
21
Libraries/ReactIOS/ReactIOSGlobalResponderHandler.js
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @providesModule ReactIOSGlobalResponderHandler
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var RKUIManager = require('NativeModules').RKUIManager;
|
||||
var ReactIOSTagHandles = require('ReactIOSTagHandles');
|
||||
|
||||
var ReactIOSGlobalResponderHandler = {
|
||||
onChange: function(from, to) {
|
||||
if (to !== null) {
|
||||
RKUIManager.setJSResponder(
|
||||
ReactIOSTagHandles.mostRecentMountedNodeHandleForRootNodeID(to)
|
||||
);
|
||||
} else {
|
||||
RKUIManager.clearJSResponder();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = ReactIOSGlobalResponderHandler;
|
||||
123
Libraries/ReactIOS/ReactIOSMount.js
Normal file
123
Libraries/ReactIOS/ReactIOSMount.js
Normal file
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* @providesModule ReactIOSMount
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var RKUIManager = require('NativeModulesDeprecated').RKUIManager;
|
||||
|
||||
var ReactIOSTagHandles = require('ReactIOSTagHandles');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
|
||||
var instantiateReactComponent = require('instantiateReactComponent');
|
||||
var invariant = require('invariant');
|
||||
|
||||
var TOP_ROOT_NODE_IDS = {};
|
||||
|
||||
function instanceNumberToChildRootID(rootNodeID, instanceNumber) {
|
||||
return rootNodeID + '[' + instanceNumber + ']';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ReactIOSMount = {
|
||||
instanceCount: 0,
|
||||
|
||||
_instancesByContainerID: {},
|
||||
|
||||
/**
|
||||
* @param {ReactComponent} instance Instance to render.
|
||||
* @param {containerTag} containerView Handle to native view tag
|
||||
*/
|
||||
renderComponent: function(descriptor, containerTag) {
|
||||
var instance = instantiateReactComponent(descriptor);
|
||||
|
||||
if (!ReactIOSTagHandles.reactTagIsNativeTopRootID(containerTag)) {
|
||||
console.error('You cannot render into anything but a top root');
|
||||
return;
|
||||
}
|
||||
|
||||
var topRootNodeID = ReactIOSTagHandles.allocateRootNodeIDForTag(containerTag);
|
||||
ReactIOSTagHandles.associateRootNodeIDWithMountedNodeHandle(
|
||||
topRootNodeID,
|
||||
containerTag
|
||||
);
|
||||
TOP_ROOT_NODE_IDS[topRootNodeID] = true;
|
||||
|
||||
var childRootNodeID = instanceNumberToChildRootID(
|
||||
topRootNodeID,
|
||||
ReactIOSMount.instanceCount++
|
||||
);
|
||||
ReactIOSMount._instancesByContainerID[topRootNodeID] = instance;
|
||||
instance.mountComponentIntoNode(childRootNodeID, topRootNodeID);
|
||||
},
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
ReactIOSMount.unmountComponentAtNode(containerTag);
|
||||
// call back into native to remove all of the subviews from this container
|
||||
RKUIManager.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) {
|
||||
var containerID = ReactIOSTagHandles.tagToRootNodeID[containerTag];
|
||||
|
||||
invariant(
|
||||
TOP_ROOT_NODE_IDS[containerID],
|
||||
'We only currently support removing components from the root node'
|
||||
);
|
||||
var instance = ReactIOSMount._instancesByContainerID[containerID];
|
||||
if (!instance) {
|
||||
console.error('Tried to unmount a component that does not exist');
|
||||
return false;
|
||||
}
|
||||
ReactIOSMount.unmountComponentFromNode(instance, containerID);
|
||||
delete ReactIOSMount._instancesByContainerID[containerID];
|
||||
delete TOP_ROOT_NODE_IDS[containerID];
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Unmounts a component and sends messages back to iOS to remove its subviews.
|
||||
*
|
||||
* @param {ReactComponent} instance React component instance.
|
||||
* @param {int} containerID ID of container we're removing from.
|
||||
* @final
|
||||
* @internal
|
||||
* @see {ReactIOSMount.unmountComponentAtNode}
|
||||
*/
|
||||
unmountComponentFromNode: function(instance, containerID) {
|
||||
// call back into native to remove all of the subviews from this container
|
||||
instance.unmountComponent();
|
||||
var containerTag =
|
||||
ReactIOSTagHandles.mostRecentMountedNodeHandleForRootNodeID(containerID);
|
||||
RKUIManager.removeSubviewsFromContainerWithID(containerTag);
|
||||
},
|
||||
|
||||
getNode: function(id) {
|
||||
return id;
|
||||
}
|
||||
};
|
||||
|
||||
ReactIOSMount.renderComponent = ReactPerf.measure(
|
||||
'ReactMount',
|
||||
'_renderNewRootComponent',
|
||||
ReactIOSMount.renderComponent
|
||||
);
|
||||
|
||||
module.exports = ReactIOSMount;
|
||||
265
Libraries/ReactIOS/ReactIOSNativeComponent.js
Normal file
265
Libraries/ReactIOS/ReactIOSNativeComponent.js
Normal file
@@ -0,0 +1,265 @@
|
||||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* @providesModule ReactIOSNativeComponent
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var NativeMethodsMixin = require('NativeMethodsMixin');
|
||||
var ReactComponent = require('ReactComponent');
|
||||
var ReactIOSComponentMixin = require('ReactIOSComponentMixin');
|
||||
var ReactIOSEventEmitter = require('ReactIOSEventEmitter');
|
||||
var ReactIOSStyleAttributes = require('ReactIOSStyleAttributes');
|
||||
var ReactIOSTagHandles = require('ReactIOSTagHandles');
|
||||
var ReactMultiChild = require('ReactMultiChild');
|
||||
var RKUIManager = require('NativeModulesDeprecated').RKUIManager;
|
||||
|
||||
var styleDiffer = require('styleDiffer');
|
||||
var deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationInDev');
|
||||
var diffRawProperties = require('diffRawProperties');
|
||||
var flattenStyle = require('flattenStyle');
|
||||
var warning = require('warning');
|
||||
|
||||
var registrationNames = ReactIOSEventEmitter.registrationNames;
|
||||
var putListener = ReactIOSEventEmitter.putListener;
|
||||
var deleteAllListeners = ReactIOSEventEmitter.deleteAllListeners;
|
||||
|
||||
/**
|
||||
* @constructor ReactIOSNativeComponent
|
||||
* @extends ReactComponent
|
||||
* @extends ReactMultiChild
|
||||
* @param {!object} UIKit View Configuration.
|
||||
*/
|
||||
var ReactIOSNativeComponent = function(viewConfig) {
|
||||
this.viewConfig = viewConfig;
|
||||
this.props = null;
|
||||
this.previousFlattenedStyle = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates and caches arrays of the form:
|
||||
*
|
||||
* [0, 1, 2, 3]
|
||||
* [0, 1, 2, 3, 4]
|
||||
* [0, 1]
|
||||
*
|
||||
* @param {number} size Size of array to generate.
|
||||
* @return {Array<number>} Array with values that mirror the index.
|
||||
*/
|
||||
var cachedIndexArray = function(size) {
|
||||
var cachedResult = cachedIndexArray._cache[size];
|
||||
if (!cachedResult) {
|
||||
var arr = [];
|
||||
for (var i = 0; i < size; i++) {
|
||||
arr[i] = i;
|
||||
}
|
||||
return cachedIndexArray._cache[size] = arr;
|
||||
} else {
|
||||
return cachedResult;
|
||||
}
|
||||
};
|
||||
cachedIndexArray._cache = {};
|
||||
|
||||
/**
|
||||
* Mixin for containers that contain UIViews. NOTE: markup is rendered markup
|
||||
* which is a `viewID` ... see the return value for `mountComponent` !
|
||||
*/
|
||||
ReactIOSNativeComponent.Mixin = {
|
||||
unmountComponent: function() {
|
||||
deleteAllListeners(this._rootNodeID);
|
||||
ReactComponent.Mixin.unmountComponent.call(this);
|
||||
this.unmountChildren();
|
||||
},
|
||||
|
||||
/**
|
||||
* 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
|
||||
* `ReactIOSTagHandles`.
|
||||
*/
|
||||
initializeChildren: function(children, containerTag, transaction) {
|
||||
var mountImages = this.mountChildren(children, transaction);
|
||||
// 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) {
|
||||
var indexes = cachedIndexArray(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; i < mountImages.length; 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'
|
||||
);
|
||||
ReactIOSTagHandles.associateRootNodeIDWithMountedNodeHandle(
|
||||
childID,
|
||||
childTag
|
||||
);
|
||||
createdTags[i] = mountImage.tag;
|
||||
}
|
||||
RKUIManager
|
||||
.manageChildren(containerTag, null, null, createdTags, indexes, null);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Beware, this function has side effect to store this.previousFlattenedStyle!
|
||||
*
|
||||
* @param {!object} prevProps Previous properties
|
||||
* @param {!object} nextProps Next properties
|
||||
* @param {!object} validAttributes Set of valid attributes and how they
|
||||
* should be diffed
|
||||
*/
|
||||
computeUpdatedProperties: function(prevProps, nextProps, validAttributes) {
|
||||
if (__DEV__) {
|
||||
for (var key in nextProps) {
|
||||
if (nextProps.hasOwnProperty(key) &&
|
||||
nextProps[key] &&
|
||||
validAttributes[key]) {
|
||||
deepFreezeAndThrowOnMutationInDev(nextProps[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var updatePayload = diffRawProperties(
|
||||
null, // updatePayload
|
||||
prevProps,
|
||||
nextProps,
|
||||
validAttributes
|
||||
);
|
||||
|
||||
// The style property is a deeply nested element which includes numbers
|
||||
// to represent static objects. Most of the time, it doesn't change across
|
||||
// renders, so it's faster to spend the time checking if it is different
|
||||
// before actually doing the expensive flattening operation in order to
|
||||
// compute the diff.
|
||||
if (styleDiffer(nextProps.style, prevProps.style)) {
|
||||
var nextFlattenedStyle = flattenStyle(nextProps.style);
|
||||
updatePayload = diffRawProperties(
|
||||
updatePayload,
|
||||
this.previousFlattenedStyle,
|
||||
nextFlattenedStyle,
|
||||
ReactIOSStyleAttributes
|
||||
);
|
||||
this.previousFlattenedStyle = nextFlattenedStyle;
|
||||
}
|
||||
|
||||
return updatePayload;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Updates the component's currently mounted representation.
|
||||
*
|
||||
* @param {ReactReconcileTransaction} transaction
|
||||
* @param {object} prevDescriptor
|
||||
* @internal
|
||||
*/
|
||||
updateComponent: function(transaction, prevDescriptor) {
|
||||
ReactComponent.Mixin.updateComponent.call(
|
||||
this,
|
||||
transaction,
|
||||
prevDescriptor
|
||||
);
|
||||
var nextDescriptor = this._currentElement;
|
||||
|
||||
var updatePayload = this.computeUpdatedProperties(
|
||||
prevDescriptor.props,
|
||||
nextDescriptor.props,
|
||||
this.viewConfig.validAttributes
|
||||
);
|
||||
|
||||
if (updatePayload) {
|
||||
RKUIManager.updateView(
|
||||
ReactIOSTagHandles.mostRecentMountedNodeHandleForRootNodeID(this._rootNodeID),
|
||||
this.viewConfig.uiViewClassName,
|
||||
updatePayload
|
||||
);
|
||||
}
|
||||
|
||||
this._reconcileListenersUponUpdate(
|
||||
prevDescriptor.props,
|
||||
nextDescriptor.props
|
||||
);
|
||||
this.updateChildren(this.props.children, transaction);
|
||||
},
|
||||
|
||||
/**
|
||||
* @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])) {
|
||||
putListener(this._rootNodeID, key, nextProps[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, mountDepth) {
|
||||
ReactComponent.Mixin.mountComponent.call(
|
||||
this,
|
||||
rootID,
|
||||
transaction,
|
||||
mountDepth
|
||||
);
|
||||
var tag = ReactIOSTagHandles.allocateTag();
|
||||
|
||||
this.previousFlattenedStyle = {};
|
||||
var updatePayload = this.computeUpdatedProperties(
|
||||
{}, // previous props
|
||||
this.props, // next props
|
||||
this.viewConfig.validAttributes
|
||||
);
|
||||
RKUIManager.createView(tag, this.viewConfig.uiViewClassName, updatePayload);
|
||||
|
||||
this._registerListenersUponCreation(this.props);
|
||||
this.initializeChildren(this.props.children, tag, transaction);
|
||||
return {
|
||||
rootNodeID: rootID,
|
||||
tag: tag
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Order of mixins is important. ReactIOSNativeComponent overrides methods in
|
||||
* ReactMultiChild.
|
||||
*/
|
||||
Object.assign(
|
||||
ReactIOSNativeComponent.prototype,
|
||||
ReactComponent.Mixin,
|
||||
ReactMultiChild.Mixin,
|
||||
ReactIOSNativeComponent.Mixin,
|
||||
NativeMethodsMixin,
|
||||
ReactIOSComponentMixin
|
||||
);
|
||||
|
||||
module.exports = ReactIOSNativeComponent;
|
||||
99
Libraries/ReactIOS/ReactIOSReconcileTransaction.js
Normal file
99
Libraries/ReactIOS/ReactIOSReconcileTransaction.js
Normal file
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* @providesModule ReactIOSReconcileTransaction
|
||||
* @typechecks static-only
|
||||
*/
|
||||
|
||||
"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 ReactIOSReconcileTransaction
|
||||
*/
|
||||
function ReactIOSReconcileTransaction() {
|
||||
this.reinitializeTransaction();
|
||||
this.reactMountReady = CallbackQueue.getPooled(null);
|
||||
}
|
||||
|
||||
var Mixin = {
|
||||
/**
|
||||
* @see Transaction
|
||||
* @abstract
|
||||
* @final
|
||||
* @return {array<object>} List of operation wrap proceedures.
|
||||
* 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 resused.
|
||||
*/
|
||||
destructor: function() {
|
||||
CallbackQueue.release(this.reactMountReady);
|
||||
this.reactMountReady = null;
|
||||
}
|
||||
};
|
||||
|
||||
Object.assign(
|
||||
ReactIOSReconcileTransaction.prototype,
|
||||
Transaction.Mixin,
|
||||
ReactIOSReconcileTransaction,
|
||||
Mixin
|
||||
);
|
||||
|
||||
PooledClass.addPoolingTo(ReactIOSReconcileTransaction);
|
||||
|
||||
module.exports = ReactIOSReconcileTransaction;
|
||||
25
Libraries/ReactIOS/ReactIOSStyleAttributes.js
Normal file
25
Libraries/ReactIOS/ReactIOSStyleAttributes.js
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* @providesModule ReactIOSStyleAttributes
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var TextStylePropTypes = require('TextStylePropTypes');
|
||||
var ViewStylePropTypes = require('ViewStylePropTypes');
|
||||
|
||||
var deepDiffer = require('deepDiffer');
|
||||
var keyMirror = require('keyMirror');
|
||||
var matricesDiffer = require('matricesDiffer');
|
||||
var merge = require('merge');
|
||||
|
||||
var ReactIOSStyleAttributes = merge(
|
||||
keyMirror(ViewStylePropTypes),
|
||||
keyMirror(TextStylePropTypes)
|
||||
);
|
||||
|
||||
ReactIOSStyleAttributes.transformMatrix = { diff: matricesDiffer };
|
||||
ReactIOSStyleAttributes.shadowOffset = { diff: deepDiffer };
|
||||
|
||||
module.exports = ReactIOSStyleAttributes;
|
||||
88
Libraries/ReactIOS/ReactIOSTagHandles.js
Normal file
88
Libraries/ReactIOS/ReactIOSTagHandles.js
Normal file
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* @providesModule ReactIOSTagHandles
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var invariant = require('invariant');
|
||||
var warning = require('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 ReactIOSTagHandles = {
|
||||
tagsStartAt: INITIAL_TAG_COUNT,
|
||||
tagCount: INITIAL_TAG_COUNT,
|
||||
|
||||
allocateTag: function() {
|
||||
// Skip over root IDs as those are reserved for native
|
||||
while (this.reactTagIsNativeTopRootID(ReactIOSTagHandles.tagCount)) {
|
||||
ReactIOSTagHandles.tagCount++;
|
||||
}
|
||||
var tag = ReactIOSTagHandles.tagCount;
|
||||
ReactIOSTagHandles.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, tag) {
|
||||
warning(rootNodeID && tag, 'Root node or tag is null when associating');
|
||||
ReactIOSTagHandles.tagToRootNodeID[tag] = rootNodeID;
|
||||
ReactIOSTagHandles.rootNodeIDToTag[rootNodeID] = tag;
|
||||
},
|
||||
|
||||
allocateRootNodeIDForTag: function(tag) {
|
||||
invariant(
|
||||
this.reactTagIsNativeTopRootID(tag),
|
||||
'Expect a native root tag, instead got ', tag
|
||||
);
|
||||
return '.r[' + tag + ']{TOP_LEVEL}';
|
||||
},
|
||||
|
||||
reactTagIsNativeTopRootID: function(reactTag) {
|
||||
// We reserve all tags that are 1 mod 10 for native root views
|
||||
return reactTag % 10 === 1;
|
||||
},
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
return ReactIOSTagHandles.rootNodeIDToTag[rootNodeID];
|
||||
},
|
||||
|
||||
tagToRootNodeID: [],
|
||||
|
||||
rootNodeIDToTag: {}
|
||||
};
|
||||
|
||||
module.exports = ReactIOSTagHandles;
|
||||
31
Libraries/ReactIOS/ReactIOSViewAttributes.js
Normal file
31
Libraries/ReactIOS/ReactIOSViewAttributes.js
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* @providesModule ReactIOSViewAttributes
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var merge = require('merge');
|
||||
|
||||
var ReactIOSViewAttributes = {};
|
||||
|
||||
ReactIOSViewAttributes.UIView = {
|
||||
pointerEvents: true,
|
||||
accessible: true,
|
||||
accessibilityLabel: true,
|
||||
testID: true,
|
||||
};
|
||||
|
||||
ReactIOSViewAttributes.RKView = merge(
|
||||
ReactIOSViewAttributes.UIView, {
|
||||
|
||||
// This is a special performance property exposed by RKView and useful for
|
||||
// scrolling content when there are many subviews, most of which are offscreen.
|
||||
// For this property to be effective, it must be applied to a view that contains
|
||||
// many subviews that extend outside its bound. The subviews must also have
|
||||
// overflow: hidden, as should the containing view (or one of its superviews).
|
||||
removeClippedSubviews: true
|
||||
});
|
||||
|
||||
module.exports = ReactIOSViewAttributes;
|
||||
30
Libraries/ReactIOS/ReactTextComponent.js
Normal file
30
Libraries/ReactIOS/ReactTextComponent.js
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* 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 ReactTextComponent
|
||||
* @typechecks static-only
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var InjectedTextComponent = null;
|
||||
var ReactTextComponent = function() {
|
||||
return InjectedTextComponent.apply(this, arguments);
|
||||
};
|
||||
ReactTextComponent.inject = function(textComponent) {
|
||||
InjectedTextComponent = textComponent;
|
||||
};
|
||||
|
||||
module.exports = ReactTextComponent;
|
||||
29
Libraries/ReactIOS/createReactIOSNativeComponentClass.js
Normal file
29
Libraries/ReactIOS/createReactIOSNativeComponentClass.js
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* @providesModule createReactIOSNativeComponentClass
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactLegacyElement = require('ReactLegacyElement');
|
||||
var ReactIOSNativeComponent = require('ReactIOSNativeComponent');
|
||||
|
||||
/**
|
||||
* @param {string} config iOS View configuration.
|
||||
* @private
|
||||
*/
|
||||
var createReactIOSNativeComponentClass = function(viewConfig) {
|
||||
var Constructor = function(props) {
|
||||
};
|
||||
Constructor.displayName = viewConfig.uiViewClassName;
|
||||
Constructor.prototype = new ReactIOSNativeComponent(viewConfig);
|
||||
Constructor.prototype.constructor = Constructor;
|
||||
|
||||
return ReactLegacyElement.wrapFactory(
|
||||
ReactElement.createFactory(Constructor)
|
||||
);
|
||||
};
|
||||
|
||||
module.exports = createReactIOSNativeComponentClass;
|
||||
85
Libraries/ReactIOS/diffRawProperties.js
Normal file
85
Libraries/ReactIOS/diffRawProperties.js
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* @providesModule diffRawProperties
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* diffRawProperties takes two sets of props and a set of valid attributes
|
||||
* and write to updatePayload the values that changed or were deleted
|
||||
*
|
||||
* @param {?object} updatePayload Overriden with the props that changed.
|
||||
* @param {!object} prevProps Previous properties to diff against current
|
||||
* properties. These properties are as supplied to component construction.
|
||||
* @param {!object} prevProps Next "current" properties to diff against
|
||||
* previous. These properties are as supplied to component construction.
|
||||
* @return {?object}
|
||||
*/
|
||||
function diffRawProperties(updatePayload, prevProps, nextProps, validAttributes) {
|
||||
var validAttributeConfig;
|
||||
var nextProp;
|
||||
var prevProp;
|
||||
var isScalar;
|
||||
var shouldUpdate;
|
||||
|
||||
for (var propKey in nextProps) {
|
||||
validAttributeConfig = validAttributes[propKey];
|
||||
if (!validAttributeConfig) {
|
||||
continue; // not a valid native prop
|
||||
}
|
||||
prevProp = prevProps && prevProps[propKey];
|
||||
nextProp = nextProps[propKey];
|
||||
if (prevProp !== nextProp) {
|
||||
// If you want a property's diff to be detected, you must configure it
|
||||
// to be so - *or* it must be a scalar property. For now, we'll allow
|
||||
// creation with any attribute that is not scalar, but we should
|
||||
// eventually even reject those unless they are properly configured.
|
||||
isScalar = typeof nextProp !== 'object' || nextProp === null;
|
||||
shouldUpdate = isScalar ||
|
||||
!prevProp ||
|
||||
validAttributeConfig.diff &&
|
||||
validAttributeConfig.diff(prevProp, nextProp);
|
||||
|
||||
if (shouldUpdate) {
|
||||
updatePayload = updatePayload || {};
|
||||
updatePayload[propKey] = nextProp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
validAttributeConfig = validAttributes[propKey];
|
||||
if (!validAttributeConfig) {
|
||||
continue; // not a valid native prop
|
||||
}
|
||||
if (updatePayload && updatePayload[propKey] !== undefined) {
|
||||
continue; // Prop already specified
|
||||
}
|
||||
prevProp = prevProps[propKey];
|
||||
nextProp = nextProps && nextProps[propKey];
|
||||
if (prevProp !== nextProp) {
|
||||
if (nextProp === undefined) {
|
||||
nextProp = null; // null is a sentinel we explicitly send to native
|
||||
}
|
||||
// If you want a property's diff to be detected, you must configure it
|
||||
// to be so - *or* it must be a scalar property. For now, we'll allow
|
||||
// creation with any attribute that is not scalar, but we should
|
||||
// eventually even reject those unless they are properly configured.
|
||||
isScalar = typeof nextProp !== 'object' || nextProp === null;
|
||||
shouldUpdate = isScalar && prevProp !== nextProp ||
|
||||
validAttributeConfig.diff &&
|
||||
validAttributeConfig.diff(prevProp, nextProp);
|
||||
if (shouldUpdate) {
|
||||
updatePayload = updatePayload || {};
|
||||
updatePayload[propKey] = nextProp;
|
||||
}
|
||||
}
|
||||
}
|
||||
return updatePayload;
|
||||
}
|
||||
|
||||
module.exports = diffRawProperties;
|
||||
18
Libraries/ReactIOS/nativePropType.js
Normal file
18
Libraries/ReactIOS/nativePropType.js
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* @providesModule nativePropType
|
||||
*/
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* A simple wrapper for prop types to mark them as native, which will allow them
|
||||
* to be passed over the bridge to be applied to the native component if
|
||||
* processed by `validAttributesFromPropTypes`.
|
||||
*/
|
||||
function nativePropType(propType) {
|
||||
propType.isNative = true;
|
||||
return propType;
|
||||
}
|
||||
|
||||
module.exports = nativePropType;
|
||||
20
Libraries/ReactIOS/renderApplication.ios.js
Normal file
20
Libraries/ReactIOS/renderApplication.ios.js
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* @providesModule renderApplication
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('React');
|
||||
|
||||
var invariant = require('invariant');
|
||||
|
||||
function renderApplication(RootComponent, initialProps, rootTag) {
|
||||
invariant(
|
||||
rootTag,
|
||||
'Expect to have a valid rootTag, instead got ', rootTag
|
||||
);
|
||||
React.render(<RootComponent {...initialProps} />, rootTag);
|
||||
}
|
||||
|
||||
module.exports = renderApplication;
|
||||
Reference in New Issue
Block a user