diff --git a/Examples/UIExplorer/ImageExample.js b/Examples/UIExplorer/ImageExample.js
index faa3267a1..b886bf861 100644
--- a/Examples/UIExplorer/ImageExample.js
+++ b/Examples/UIExplorer/ImageExample.js
@@ -21,10 +21,41 @@ var {
StyleSheet,
Text,
View,
+ ActivityIndicatorIOS
} = React;
var ImageCapInsetsExample = require('./ImageCapInsetsExample');
+var NetworkImageExample = React.createClass({
+ watchID: (null: ?number),
+
+ getInitialState: function() {
+ return {
+ error: false,
+ loading: true,
+ progress: 0
+ };
+ },
+ render: function() {
+ var loader = this.state.loading ?
+
+ {this.state.progress}%
+
+ : null;
+ return this.state.error ?
+ {this.state.error} :
+ this.setState({error: e.nativeEvent.error})}
+ onLoadProgress={(e) => this.setState({progress: Math.max(0, Math.round(100 * e.nativeEvent.written / e.nativeEvent.total))}) }
+ onLoadEnd={() => this.setState({loading: false, error: false})}
+ onLoadAbort={() => this.setState({error: 'Loading has aborted'})} >
+ {loader}
+ ;
+ }
+});
+
exports.displayName = (undefined: ?string);
exports.framework = 'React';
exports.title = '';
@@ -59,6 +90,22 @@ exports.examples = [
);
},
},
+ {
+ title: 'Error Handler',
+ render: function() {
+ return (
+
+ );
+ },
+ },
+ {
+ title: 'Image Download Progress',
+ render: function() {
+ return (
+
+ );
+ },
+ },
{
title: 'Border Color',
render: function() {
@@ -300,6 +347,12 @@ var styles = StyleSheet.create({
width: 38,
height: 38,
},
+ progress: {
+ flex: 1,
+ alignItems: 'center',
+ flexDirection: 'row',
+ width: 100
+ },
leftMargin: {
marginLeft: 10,
},
diff --git a/Examples/UIExplorer/TextExample.ios.js b/Examples/UIExplorer/TextExample.ios.js
index cbc3e5b41..4aff00e73 100644
--- a/Examples/UIExplorer/TextExample.ios.js
+++ b/Examples/UIExplorer/TextExample.ios.js
@@ -369,25 +369,6 @@ exports.examples = [
);
},
-}, {
- title: 'allowFontScaling attribute',
- render: function() {
- return (
-
-
- By default, text will respect Text Size accessibility setting on iOS.
- It means that all font sizes will be increased or descreased depending on the value of Text Size setting in
- {" "}Settings.app - Display & Brightness - Text Size
-
-
- You can disable scaling for your Text component by passing {"\""}allowFontScaling={"{"}false{"}\""} prop.
-
-
- This text will not scale.
-
-
- );
- },
}];
var styles = StyleSheet.create({
diff --git a/Examples/UIExplorer/TransformExample.js b/Examples/UIExplorer/TransformExample.js
index 9e848c3a5..7da9eb5ac 100644
--- a/Examples/UIExplorer/TransformExample.js
+++ b/Examples/UIExplorer/TransformExample.js
@@ -1,61 +1,85 @@
/**
- * Copyright 2004-present Facebook. All Rights Reserved.
+ * The examples provided by Facebook are for non-commercial testing and
+ * evaluation purposes only.
*
- * @providesModule TransformExample
+ * Facebook reserves all rights not expressly granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
+ * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
'use strict';
-var React = require('React');
+var React = require('react-native');
var {
+ Animated,
StyleSheet,
+ Text,
View,
} = React;
-var TimerMixin = require('react-timer-mixin');
-var UIExplorerBlock = require('./UIExplorerBlock');
-var UIExplorerPage = require('./UIExplorerPage');
-
-var TransformExample = React.createClass({
-
- mixins: [TimerMixin],
-
+var Flip = React.createClass({
getInitialState() {
return {
- interval: this.setInterval(this._update, 800),
- pulse: false,
+ theta: new Animated.Value(45),
};
},
+ componentDidMount() {
+ this._animate();
+ },
+
+ _animate() {
+ this.state.theta.setValue(0);
+ Animated.timing(this.state.theta, {
+ toValue: 360,
+ duration: 5000,
+ }).start(this._animate);
+ },
+
render() {
return (
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ This text is flipping great.
+
+
+
+
+ On the flip side...
+
+
+
);
- },
-
- _update() {
- this.setState({
- pulse: !this.state.pulse,
- });
- },
-
+ }
});
var styles = StyleSheet.create({
+ container: {
+ height: 500,
+ },
box1: {
left: 0,
backgroundColor: 'green',
@@ -88,7 +112,7 @@ var styles = StyleSheet.create({
},
box3step1: {
left: 0,
- backgroundColor: '#ffb6c1', // lightpink
+ backgroundColor: 'lightpink',
height: 50,
position: 'absolute',
top: 0,
@@ -99,7 +123,7 @@ var styles = StyleSheet.create({
},
box3step2: {
left: 0,
- backgroundColor: '#ff69b4', //hotpink
+ backgroundColor: 'hotpink',
height: 50,
opacity: 0.5,
position: 'absolute',
@@ -113,7 +137,7 @@ var styles = StyleSheet.create({
},
box3step3: {
left: 0,
- backgroundColor: '#ff1493', // deeppink
+ backgroundColor: 'deeppink',
height: 50,
opacity: 0.5,
position: 'absolute',
@@ -129,7 +153,7 @@ var styles = StyleSheet.create({
},
box4: {
left: 0,
- backgroundColor: '#ff8c00', // darkorange
+ backgroundColor: 'darkorange',
height: 50,
position: 'absolute',
top: 0,
@@ -141,7 +165,7 @@ var styles = StyleSheet.create({
width: 100,
},
box5: {
- backgroundColor: '#800000', // maroon
+ backgroundColor: 'maroon',
height: 50,
position: 'absolute',
right: 0,
@@ -155,7 +179,110 @@ var styles = StyleSheet.create({
{scale: 2},
],
},
+ flipCardContainer: {
+ marginVertical: 40,
+ flex: 1,
+ alignSelf: 'center',
+ },
+ flipCard: {
+ width: 200,
+ height: 200,
+ alignItems: 'center',
+ justifyContent: 'center',
+ backgroundColor: 'blue',
+ backfaceVisibility: 'hidden',
+ },
+ flipText: {
+ width: 90,
+ fontSize: 20,
+ color: 'white',
+ fontWeight: 'bold',
+ }
});
-
-module.exports = TransformExample;
+exports.title = 'Transforms';
+exports.description = 'View transforms';
+exports.examples = [
+ {
+ title: 'Perspective',
+ description: 'perspective: 850, rotateX: Animated.timing(0 -> 360)',
+ render(): ReactElement { return ; }
+ },
+ {
+ title: 'Translate, Rotate, Scale',
+ description: "translateX: 100, translateY: 50, rotate: '30deg', scaleX: 2, scaleY: 2",
+ render() {
+ return (
+
+
+
+ );
+ }
+ },
+ {
+ title: 'Scale, Translate, Rotate, ',
+ description: "scaleX: 2, scaleY: 2, translateX: 100, translateY: 50, rotate: '30deg'",
+ render() {
+ return (
+
+
+
+ );
+ }
+ },
+ {
+ title: 'Rotate',
+ description: "rotate: '30deg'",
+ render() {
+ return (
+
+
+
+ );
+ }
+ },
+ {
+ title: 'Rotate, Scale',
+ description: "rotate: '30deg', scaleX: 2, scaleY: 2",
+ render() {
+ return (
+
+
+
+ );
+ }
+ },
+ {
+ title: 'Rotate, Scale, Translate ',
+ description: "rotate: '30deg', scaleX: 2, scaleY: 2, translateX: 100, translateY: 50",
+ render() {
+ return (
+
+
+
+ );
+ }
+ },
+ {
+ title: 'Translate, Scale, Rotate',
+ description: "translate: [200, 350], scale: 2.5, rotate: '-0.2rad'",
+ render() {
+ return (
+
+
+
+ );
+ }
+ },
+ {
+ title: 'Translate, Rotate, Scale',
+ description: "translate: [-50, 35], rotate: '50deg', scale: 2",
+ render() {
+ return (
+
+
+
+ );
+ }
+ }
+];
diff --git a/Examples/UIExplorer/UIExplorer/AppDelegate.m b/Examples/UIExplorer/UIExplorer/AppDelegate.m
index 6a5ad20e5..f19fdf364 100644
--- a/Examples/UIExplorer/UIExplorer/AppDelegate.m
+++ b/Examples/UIExplorer/UIExplorer/AppDelegate.m
@@ -43,7 +43,7 @@
* Load from pre-bundled file on disk. To re-generate the static bundle, `cd`
* to your Xcode project folder and run
*
- * $ curl 'http://localhost:8081/Examples/UIExplorer/UIExplorerApp.includeRequire.runModule.bundle' -o main.jsbundle
+ * $ curl 'http://localhost:8081/Examples/UIExplorer/UIExplorerApp.ios.includeRequire.runModule.bundle' -o main.jsbundle
*
* then add the `main.jsbundle` file to your project and uncomment this line:
*/
diff --git a/Examples/UIExplorer/UIExplorerList.js b/Examples/UIExplorer/UIExplorerList.js
index 217797e26..20edd13b0 100644
--- a/Examples/UIExplorer/UIExplorerList.js
+++ b/Examples/UIExplorer/UIExplorerList.js
@@ -80,6 +80,7 @@ if (Platform.OS === 'ios') {
require('./AlertIOSExample'),
require('./AppStateIOSExample'),
require('./AsyncStorageExample'),
+ require('./TransformExample'),
require('./BorderExample'),
require('./CameraRollExample.ios'),
require('./NetInfoExample'),
diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTBridgeTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTBridgeTests.m
index 280aaf3b3..60f41747b 100644
--- a/Examples/UIExplorer/UIExplorerUnitTests/RCTBridgeTests.m
+++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTBridgeTests.m
@@ -24,7 +24,7 @@
@property (nonatomic, strong, readonly) RCTBridge *batchedBridge;
-- (void)_handleBuffer:(id)buffer context:(NSNumber *)context;
+- (void)_handleBuffer:(id)buffer;
- (void)setUp;
@end
@@ -57,7 +57,6 @@ RCT_EXPORT_MODULE()
- (void)executeJSCall:(__unused NSString *)name
method:(__unused NSString *)method
arguments:(__unused NSArray *)arguments
- context:(__unused NSNumber *)executorID
callback:(RCTJavaScriptCallback)onComplete
{
onComplete(nil, nil);
@@ -155,7 +154,7 @@ RCT_EXPORT_MODULE(TestModule)
NSArray *args = @[@1234, @5678, @"stringy", @{@"a": @1}, @42];
NSArray *buffer = @[@[testModuleID], @[testMethodID], @[args], @[], @1234567];
- [_bridge.batchedBridge _handleBuffer:buffer context:RCTGetExecutorID(executor)];
+ [_bridge.batchedBridge _handleBuffer:buffer];
dispatch_sync(_methodQueue, ^{
// clear the queue
diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTContextExecutorTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTContextExecutorTests.m
index 02450fab6..18b4118ba 100644
--- a/Examples/UIExplorer/UIExplorerUnitTests/RCTContextExecutorTests.m
+++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTContextExecutorTests.m
@@ -33,7 +33,6 @@
{
[super setUp];
_executor = [[RCTContextExecutor alloc] init];
- RCTSetExecutorID(_executor);
[_executor setUp];
}
@@ -139,7 +138,6 @@ static uint64_t _get_time_nanoseconds(void)
[_executor executeJSCall:@"module"
method:@"method"
arguments:params
- context:RCTGetExecutorID(_executor)
callback:^(id json, __unused NSError *unused) {
XCTAssert([json isEqual:@YES], @"Invalid return");
}];
diff --git a/Libraries/ART/RCTConvert+ART.m b/Libraries/ART/RCTConvert+ART.m
index e6bc09286..7a607a12c 100644
--- a/Libraries/ART/RCTConvert+ART.m
+++ b/Libraries/ART/RCTConvert+ART.m
@@ -87,7 +87,7 @@ RCT_ENUM_CONVERTER(CTTextAlignment, (@{
}
NSDictionary *fontDict = dict[@"font"];
- CTFontRef font = (__bridge CTFontRef)[self UIFont:nil withFamily:fontDict[@"fontFamily"] size:fontDict[@"fontSize"] weight:fontDict[@"fontWeight"] style:fontDict[@"fontStyle"] scaleMultiplier:1.0];
+ CTFontRef font = (__bridge CTFontRef)[self UIFont:nil withFamily:fontDict[@"fontFamily"] size:fontDict[@"fontSize"] weight:fontDict[@"fontWeight"] style:fontDict[@"fontStyle"]];
if (!font) {
return frame;
}
diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js
index c0b1981e1..141cefab0 100644
--- a/Libraries/Components/TextInput/TextInput.js
+++ b/Libraries/Components/TextInput/TextInput.js
@@ -411,7 +411,7 @@ var TextInput = React.createClass({
_renderIOS: function() {
var textContainer;
- var props = this.props;
+ var props = Object.assign({},this.props);
props.style = [styles.input, this.props.style];
if (!props.multiline) {
diff --git a/Libraries/Components/View/ViewStylePropTypes.js b/Libraries/Components/View/ViewStylePropTypes.js
index ce147e096..64ace6d7b 100644
--- a/Libraries/Components/View/ViewStylePropTypes.js
+++ b/Libraries/Components/View/ViewStylePropTypes.js
@@ -21,6 +21,7 @@ var TransformPropTypes = require('TransformPropTypes');
var ViewStylePropTypes = {
...LayoutPropTypes,
...TransformPropTypes,
+ backfaceVisibility: ReactPropTypes.oneOf(['visible', 'hidden']),
backgroundColor: ReactPropTypes.string,
borderColor: ReactPropTypes.string,
borderTopColor: ReactPropTypes.string,
diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js
index bc5d65791..573223771 100644
--- a/Libraries/CustomComponents/Navigator/Navigator.js
+++ b/Libraries/CustomComponents/Navigator/Navigator.js
@@ -220,11 +220,6 @@ var Navigator = React.createClass({
*/
onDidFocus: PropTypes.func,
- /**
- * Will be called with (ref, indexInStack, route) when the scene ref changes
- */
- onItemRef: PropTypes.func,
-
/**
* Optionally provide a navigation bar that persists across scene
* transitions
@@ -318,7 +313,6 @@ var Navigator = React.createClass({
onPanResponderMove: this._handlePanResponderMove,
onPanResponderTerminate: this._handlePanResponderTerminate,
});
- this._itemRefs = {};
this._interactionHandle = null;
this._emitWillFocus(this.state.routeStack[this.state.presentedIndex]);
},
@@ -1006,22 +1000,10 @@ var Navigator = React.createClass({
return this.state.routeStack.slice();
},
- _handleItemRef: function(itemId, route, ref) {
- this._itemRefs[itemId] = ref;
- var itemIndex = this.state.idStack.indexOf(itemId);
- if (itemIndex === -1) {
- return;
- }
- this.props.onItemRef && this.props.onItemRef(ref, itemIndex, route);
- },
-
_cleanScenesPastIndex: function(index) {
var newStackLength = index + 1;
// Remove any unneeded rendered routes.
if (newStackLength < this.state.routeStack.length) {
- this.state.idStack.slice(newStackLength).map((removingId) => {
- this._itemRefs[removingId] = null;
- });
this.setState({
sceneConfigStack: this.state.sceneConfigStack.slice(0, newStackLength),
idStack: this.state.idStack.slice(0, newStackLength),
@@ -1031,38 +1013,22 @@ var Navigator = React.createClass({
},
_renderScene: function(route, i) {
- var child = this.props.renderScene(
- route,
- this
- );
var disabledSceneStyle = null;
if (i !== this.state.presentedIndex) {
disabledSceneStyle = styles.disabledScene;
}
- var originalRef = child.ref;
- if (originalRef != null && typeof originalRef !== 'function') {
- console.warn(
- 'String refs are not supported for navigator scenes. Use a callback ' +
- 'ref instead. Ignoring ref: ' + originalRef
- );
- originalRef = null;
- }
return (
{
return (this.state.transitionFromIndex != null) || (this.state.transitionFromIndex != null);
}}
style={[styles.baseScene, this.props.sceneStyle, disabledSceneStyle]}>
- {React.cloneElement(child, {
- ref: component => {
- this._handleItemRef(this.state.idStack[i], route, component);
- if (originalRef) {
- originalRef(component);
- }
- }
- })}
+ {this.props.renderScene(
+ route,
+ this
+ )}
);
},
diff --git a/Libraries/Image/RCTDownloadTaskWrapper.m b/Libraries/Image/RCTDownloadTaskWrapper.m
index b910cb476..e9a64369b 100644
--- a/Libraries/Image/RCTDownloadTaskWrapper.m
+++ b/Libraries/Image/RCTDownloadTaskWrapper.m
@@ -66,7 +66,6 @@ static void *const RCTDownloadTaskWrapperProgressBlockKey = (void *)&RCTDownload
task.rct_completionBlock = completionBlock;
task.rct_progressBlock = progressBlock;
- [task resume];
return task;
}
diff --git a/Libraries/Image/RCTImageDownloader.m b/Libraries/Image/RCTImageDownloader.m
index 697214249..f32d895cb 100644
--- a/Libraries/Image/RCTImageDownloader.m
+++ b/Libraries/Image/RCTImageDownloader.m
@@ -14,7 +14,8 @@
#import "RCTLog.h"
#import "RCTUtils.h"
-typedef void (^RCTCachedDataDownloadBlock)(BOOL cached, NSData *data, NSError *error);
+typedef void (^RCTCachedDataDownloadBlock)(BOOL cached, NSURLResponse *response,
+ NSData *data, NSError *error);
CGSize RCTTargetSizeForClipRect(CGRect);
CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
@@ -80,24 +81,36 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
_pendingBlocks[cacheKey] = [NSMutableArray arrayWithObject:block];
__weak RCTImageDownloader *weakSelf = self;
- RCTCachedDataDownloadBlock runBlocks = ^(BOOL cached, NSData *data, NSError *error) {
+ RCTCachedDataDownloadBlock runBlocks = ^(BOOL cached, NSURLResponse *response, NSData *data, NSError *error) {
+
+ if (!error && [response isKindOfClass:[NSHTTPURLResponse class]]) {
+ NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
+ if (httpResponse.statusCode != 200) {
+ data = nil;
+ error = [[NSError alloc] initWithDomain:NSURLErrorDomain
+ code:httpResponse.statusCode
+ userInfo:nil];
+ }
+ }
+
dispatch_async(_processingQueue, ^{
RCTImageDownloader *strongSelf = weakSelf;
NSArray *blocks = strongSelf->_pendingBlocks[cacheKey];
[strongSelf->_pendingBlocks removeObjectForKey:cacheKey];
for (RCTCachedDataDownloadBlock downloadBlock in blocks) {
- downloadBlock(cached, data, error);
+ downloadBlock(cached, response, data, error);
}
});
};
NSURLRequest *request = [NSURLRequest requestWithURL:url];
task = [_downloadTaskWrapper downloadData:url progressBlock:progressBlock completionBlock:^(NSURLResponse *response, NSData *data, NSError *error) {
+
if (!cancelled) {
- runBlocks(NO, data, error);
+ runBlocks(NO, response, data, error);
}
- if (response) {
+ if (response && !error) {
RCTImageDownloader *strongSelf = weakSelf;
NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data userInfo:nil storagePolicy:NSURLCacheStorageAllowed];
[strongSelf->_cache storeCachedResponse:cachedResponse forRequest:request];
@@ -111,7 +124,7 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
}
if (cachedResponse) {
- runBlocks(YES, cachedResponse.data, nil);
+ runBlocks(YES, cachedResponse.response, cachedResponse.data, nil);
} else {
[task resume];
}
@@ -123,7 +136,7 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
- (RCTImageDownloadCancellationBlock)downloadDataForURL:(NSURL *)url progressBlock:(RCTDataProgressBlock)progressBlock block:(RCTDataDownloadBlock)block
{
- return [self _downloadDataForURL:url progressBlock:progressBlock block:^(BOOL cached, NSData *data, NSError *error) {
+ return [self _downloadDataForURL:url progressBlock:progressBlock block:^(BOOL cached, NSURLResponse *response, NSData *data, NSError *error) {
block(data, error);
}];
}
diff --git a/Libraries/Image/RCTNetworkImageView.m b/Libraries/Image/RCTNetworkImageView.m
index f8dc28157..20d297b46 100644
--- a/Libraries/Image/RCTNetworkImageView.m
+++ b/Libraries/Image/RCTNetworkImageView.m
@@ -86,6 +86,8 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
{
if (![_imageURL isEqual:imageURL] && _downloadToken) {
[_imageDownloader cancelDownload:_downloadToken];
+ NSDictionary *event = @{ @"target": self.reactTag };
+ [_eventDispatcher sendInputEventWithName:@"loadAbort" body:event];
_downloadToken = nil;
}
@@ -146,7 +148,7 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
loadEndHandler();
});
} else if (error) {
- errorHandler([error description]);
+ errorHandler([error localizedDescription]);
}
}];
} else {
@@ -170,7 +172,7 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
loadEndHandler();
});
} else if (error) {
- errorHandler([error description]);
+ errorHandler([error localizedDescription]);
}
}];
}
diff --git a/Libraries/Image/RCTNetworkImageViewManager.m b/Libraries/Image/RCTNetworkImageViewManager.m
index 29d990b27..f42ef48f1 100644
--- a/Libraries/Image/RCTNetworkImageViewManager.m
+++ b/Libraries/Image/RCTNetworkImageViewManager.m
@@ -47,7 +47,7 @@ RCT_CUSTOM_VIEW_PROPERTY(tintColor, UIColor, RCTNetworkImageView)
return @{
@"loadStart": @{ @"registrationName": @"onLoadStart" },
@"loadProgress": @{ @"registrationName": @"onLoadProgress" },
- @"loaded": @{ @"registrationName": @"onLoaded" },
+ @"loaded": @{ @"registrationName": @"onLoadEnd" },
@"loadError": @{ @"registrationName": @"onLoadError" },
@"loadAbort": @{ @"registrationName": @"onLoadAbort" },
};
diff --git a/Libraries/Inspector/Inspector.js b/Libraries/Inspector/Inspector.js
index 6b1ac5789..6015bcf3f 100644
--- a/Libraries/Inspector/Inspector.js
+++ b/Libraries/Inspector/Inspector.js
@@ -20,10 +20,21 @@ var StyleSheet = require('StyleSheet');
var UIManager = require('NativeModules').UIManager;
var View = require('View');
+var REACT_DEVTOOLS_HOOK: ?Object = typeof window !== 'undefined' ? window.__REACT_DEVTOOLS_BACKEND__ : null;
+
+if (REACT_DEVTOOLS_HOOK) {
+ // required for devtools to be able to edit react native styles
+ REACT_DEVTOOLS_HOOK.resolveRNStyle = require('flattenStyle');
+}
+
class Inspector extends React.Component {
+ _subs: ?Array<() => void>;
+
constructor(props: Object) {
super(props);
+
this.state = {
+ devtoolsBackend: null,
panelPos: 'bottom',
inspecting: true,
perfing: false,
@@ -31,6 +42,63 @@ class Inspector extends React.Component {
};
}
+ componentDidMount() {
+ if (REACT_DEVTOOLS_HOOK) {
+ this.attachToDevtools = this.attachToDevtools.bind(this);
+ REACT_DEVTOOLS_HOOK.addStartupListener(this.attachToDevtools);
+ // if devtools is already started
+ // TODO(jared): should addStartupListener just go ahead and call the
+ // listener if the devtools is already started? might be unexpected...
+ // is there some name other than `addStartupListener` that would be
+ // better?
+ if (REACT_DEVTOOLS_HOOK.backend) {
+ this.attachToDevtools(REACT_DEVTOOLS_HOOK.backend);
+ }
+ }
+ }
+
+ componentWillUnmount() {
+ if (this._subs) {
+ this._subs.map(fn => fn());
+ }
+ if (REACT_DEVTOOLS_HOOK) {
+ REACT_DEVTOOLS_HOOK.removeStartupListener(this.attachToDevtools);
+ }
+ }
+
+ attachToDevtools(backend: Object) {
+ var _hideWait = null;
+ var hlSub = backend.sub('highlight', ({node, name, props}) => {
+ clearTimeout(_hideWait);
+ UIManager.measure(node, (x, y, width, height, left, top) => {
+ this.setState({
+ hierarchy: [],
+ inspected: {
+ frame: {left, top, width, height},
+ style: props ? props.style : {},
+ },
+ });
+ });
+ });
+ var hideSub = backend.sub('hideHighlight', () => {
+ // we wait to actually hide in order to avoid flicker
+ _hideWait = setTimeout(() => {
+ this.setState({
+ inspected: null,
+ });
+ }, 100);
+ });
+ this._subs = [hlSub, hideSub];
+
+ backend.on('shutdown', () => {
+ this.setState({devtoolsBackend: null});
+ this._subs = null;
+ });
+ this.setState({
+ devtoolsBackend: backend,
+ });
+ }
+
setSelection(i: number) {
var instance = this.state.hierarchy[i];
var publicInstance = instance.getPublicInstance();
@@ -46,6 +114,9 @@ class Inspector extends React.Component {
}
onTouchInstance(instance: Object, frame: Object, pointerY: number) {
+ if (this.state.devtoolsBackend) {
+ this.state.devtoolsBackend.selectFromReactInstance(instance, true);
+ }
var hierarchy = InspectorUtils.getOwnerHierarchy(instance);
var publicInstance = instance.getPublicInstance();
var props = publicInstance.props || {};
@@ -88,6 +159,7 @@ class Inspector extends React.Component {
/>}
- {contents}
+ {!this.props.devtoolsIsOpen && contents}