diff --git a/.flowconfig b/.flowconfig index 9953a269f..bf06f66d3 100644 --- a/.flowconfig +++ b/.flowconfig @@ -36,5 +36,12 @@ Examples/UIExplorer/ImageMocks.js [options] module.system=haste +suppress_type=$FlowIssue +suppress_type=$FlowFixMe +suppress_type=$FixMe + +suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe +suppress_comment=\\(.\\|\n\\)*\\$FlowIssue #[0-9]+ + [version] -0.11.0 +0.12.0 diff --git a/Examples/Movies/MovieCell.js b/Examples/Movies/MovieCell.js index e6a9b1078..341460e58 100644 --- a/Examples/Movies/MovieCell.js +++ b/Examples/Movies/MovieCell.js @@ -34,11 +34,14 @@ var MovieCell = React.createClass({ var criticsScore = this.props.movie.ratings.critics_score; return ( - + {/* $FlowIssue #7363964 - There's a bug in Flow where you cannot + * omit a property or set it to undefined if it's inside a shape, + * even if it isn't required */} + {/* $FlowIssue #7363964 - There's a bug in Flow where you cannot + * omit a property or set it to undefined if it's inside a shape, + * even if it isn't required */} - + ); @@ -293,6 +304,17 @@ var styles = StyleSheet.create({ color: '#b9dc2f', }, + // CELL IMAGE + + imageX: { + width: 34, + height: 42, + }, + imageO: { + width: 45, + height: 41, + }, + // GAME OVER overlay: { diff --git a/Examples/UIExplorer/AccessibilityIOSExample.js b/Examples/UIExplorer/AccessibilityIOSExample.js index c22c349bb..1a86e5459 100644 --- a/Examples/UIExplorer/AccessibilityIOSExample.js +++ b/Examples/UIExplorer/AccessibilityIOSExample.js @@ -55,7 +55,7 @@ var AccessibilityIOSExample = React.createClass({ }, }); -exports.title = 'AcccessibilityIOS'; +exports.title = 'AccessibilityIOS'; exports.description = 'Interface to show iOS\' accessibility samples'; exports.examples = [ { diff --git a/Examples/UIExplorer/ImageMocks.js b/Examples/UIExplorer/ImageMocks.js index 3f1883fa6..8335f93e5 100644 --- a/Examples/UIExplorer/ImageMocks.js +++ b/Examples/UIExplorer/ImageMocks.js @@ -16,26 +16,36 @@ 'use strict'; declare module 'image!story-background' { + /* $FlowIssue #7387208 - There's a flow bug preventing this type from flowing + * into a proptype shape */ declare var uri: string; declare var isStatic: boolean; } declare module 'image!uie_comment_highlighted' { + /* $FlowIssue #7387208 - There's a flow bug preventing this type from flowing + * into a proptype shape */ declare var uri: string; declare var isStatic: boolean; } declare module 'image!uie_comment_normal' { + /* $FlowIssue #7387208 - There's a flow bug preventing this type from flowing + * into a proptype shape */ declare var uri: string; declare var isStatic: boolean; } declare module 'image!uie_thumb_normal' { + /* $FlowIssue #7387208 - There's a flow bug preventing this type from flowing + * into a proptype shape */ declare var uri: string; declare var isStatic: boolean; } declare module 'image!uie_thumb_selected' { + /* $FlowIssue #7387208 - There's a flow bug preventing this type from flowing + * into a proptype shape */ declare var uri: string; declare var isStatic: boolean; } diff --git a/Examples/UIExplorer/MapViewExample.js b/Examples/UIExplorer/MapViewExample.js index 5df00236d..23789c7f1 100644 --- a/Examples/UIExplorer/MapViewExample.js +++ b/Examples/UIExplorer/MapViewExample.js @@ -163,8 +163,8 @@ var MapViewExample = React.createClass({ style={styles.map} onRegionChange={this._onRegionChange} onRegionChangeComplete={this._onRegionChangeComplete} - region={this.state.mapRegion} - annotations={this.state.annotations} + region={this.state.mapRegion || undefined} + annotations={this.state.annotations || undefined} /> ); } else { - ContentWrapper = ScrollView; + ContentWrapper = (ScrollView: ReactClass); wrapperProps.keyboardShouldPersistTaps = true; wrapperProps.keyboardDismissMode = 'interactive'; } diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTContextExecutorTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTContextExecutorTests.m index 1ba3eaf56..ba2bf87bb 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTContextExecutorTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTContextExecutorTests.m @@ -57,7 +57,7 @@ static uint64_t _get_time_nanoseconds(void) JSContextGroupRef group = JSContextGroupCreate(); JSGlobalContextRef context = JSGlobalContextCreateInGroup(group, NULL); - id message = @[@[@1, @2, @3, @4], @[@{@"a": @1}, @{@"b": @2}], [NSNull null]]; + id message = @[@[@1, @2, @3, @4], @[@{@"a": @1}, @{@"b": @2}], (id)kCFNull]; NSString *code = RCTJSONStringify(message, NULL); JSStringRef script = JSStringCreateWithCFString((__bridge CFStringRef)code); JSValueRef error = NULL; diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTConvert_NSURLTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTConvert_NSURLTests.m index 05c07fa06..c36c09a8e 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTConvert_NSURLTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTConvert_NSURLTests.m @@ -28,7 +28,7 @@ TEST_PATH(name, _input, [[[NSBundle mainBundle] bundlePath] stringByAppendingPat // Basic tests TEST_URL(basic, @"http://example.com", @"http://example.com") -TEST_URL(null, [NSNull null], nil) +TEST_URL(null, (id)kCFNull, nil) // Local files TEST_PATH(fileURL, @"file:///blah/hello.jsbundle", @"/blah/hello.jsbundle") diff --git a/Examples/UIExplorer/XHRExample.js b/Examples/UIExplorer/XHRExample.js index c5b350c70..2a3b1ebbb 100644 --- a/Examples/UIExplorer/XHRExample.js +++ b/Examples/UIExplorer/XHRExample.js @@ -17,11 +17,17 @@ var React = require('react-native'); var { + AlertIOS, + CameraRoll, + Image, + LinkingIOS, + PixelRatio, ProgressViewIOS, StyleSheet, - View, Text, + TextInput, TouchableHighlight, + View, } = React; class Downloader extends React.Component { @@ -109,6 +115,182 @@ class Downloader extends React.Component { } } +var PAGE_SIZE = 20; + +class FormUploader extends React.Component { + + _isMounted: boolean; + _fetchRandomPhoto: () => void; + _addTextParam: () => void; + _upload: () => void; + + constructor(props) { + super(props); + this.state = { + isUploading: false, + randomPhoto: null, + textParams: [], + }; + this._isMounted = true; + this._fetchRandomPhoto = this._fetchRandomPhoto.bind(this); + this._addTextParam = this._addTextParam.bind(this); + this._upload = this._upload.bind(this); + + this._fetchRandomPhoto(); + } + + _fetchRandomPhoto() { + CameraRoll.getPhotos( + {first: PAGE_SIZE}, + (data) => { + console.log('isMounted', this._isMounted); + if (!this._isMounted) { + return; + } + var edges = data.edges; + var edge = edges[Math.floor(Math.random() * edges.length)]; + var randomPhoto = edge && edge.node && edge.node.image; + if (randomPhoto) { + this.setState({randomPhoto}); + } + }, + (error) => undefined + ); + } + + _addTextParam() { + var textParams = this.state.textParams; + textParams.push({name: '', value: ''}); + this.setState({textParams}); + } + + componentWillUnmount() { + this._isMounted = false; + } + + _onTextParamNameChange(index, text) { + var textParams = this.state.textParams; + textParams[index].name = text; + this.setState({textParams}); + } + + _onTextParamValueChange(index, text) { + var textParams = this.state.textParams; + textParams[index].value = text; + this.setState({textParams}); + } + + _upload() { + var xhr = new XMLHttpRequest(); + xhr.open('POST', 'http://posttestserver.com/post.php'); + xhr.onload = () => { + this.setState({isUploading: false}); + if (xhr.status !== 200) { + AlertIOS.alert( + 'Upload failed', + 'Expected HTTP 200 OK response, got ' + xhr.status + ); + return; + } + if (!xhr.responseText) { + AlertIOS.alert( + 'Upload failed', + 'No response payload.' + ); + return; + } + var index = xhr.responseText.indexOf('http://www.posttestserver.com/'); + if (index === -1) { + AlertIOS.alert( + 'Upload failed', + 'Invalid response payload.' + ); + return; + } + var url = xhr.responseText.slice(index).split('\n')[0]; + LinkingIOS.openURL(url); + }; + var formdata = new FormData(); + if (this.state.randomPhoto) { + formdata.append('image', {...this.state.randomPhoto, name: 'image.jpg'}); + } + this.state.textParams.forEach( + (param) => formdata.append(param.name, param.value) + ); + xhr.send(formdata); + this.setState({isUploading: true}); + } + + render() { + var image = null; + if (this.state.randomPhoto) { + image = ( + + ); + } + var textItems = this.state.textParams.map((item, index) => ( + + + = + + + )); + var uploadButtonLabel = this.state.isUploading ? 'Uploading...' : 'Upload'; + var uploadButton = ( + + {uploadButtonLabel} + + ); + if (!this.state.isUploading) { + uploadButton = ( + + {uploadButton} + + ); + } + return ( + + + + Random photo from your library + ( + update + ) + + {image} + + {textItems} + + + Add a text param + + + + {uploadButton} + + + ); + } +} + + exports.framework = 'React'; exports.title = 'XMLHttpRequest'; exports.description = 'XMLHttpRequest'; @@ -117,6 +299,11 @@ exports.examples = [{ render() { return ; } +}, { + title: 'multipart/form-data Upload', + render() { + return ; + } }]; var styles = StyleSheet.create({ @@ -126,6 +313,52 @@ var styles = StyleSheet.create({ }, button: { backgroundColor: '#eeeeee', - padding: 10, + padding: 8, + }, + paramRow: { + flexDirection: 'row', + paddingVertical: 8, + alignItems: 'center', + borderBottomWidth: 1 / PixelRatio.get(), + borderBottomColor: 'grey', + }, + photoLabel: { + flex: 1, + }, + randomPhoto: { + width: 50, + height: 50, + }, + textButton: { + color: 'blue', + }, + addTextParamButton: { + marginTop: 8, + }, + textInput: { + flex: 1, + borderRadius: 3, + borderColor: 'grey', + borderWidth: 1, + height: 30, + paddingLeft: 8, + }, + equalSign: { + paddingHorizontal: 4, + }, + uploadButton: { + marginTop: 16, + }, + uploadButtonBox: { + flex: 1, + paddingVertical: 12, + alignItems: 'center', + backgroundColor: 'blue', + borderRadius: 4, + }, + uploadButtonLabel: { + color: 'white', + fontSize: 16, + fontWeight: '500', }, }); diff --git a/Libraries/ActionSheetIOS/RCTActionSheetManager.m b/Libraries/ActionSheetIOS/RCTActionSheetManager.m index 75798efaf..4883aa647 100644 --- a/Libraries/ActionSheetIOS/RCTActionSheetManager.m +++ b/Libraries/ActionSheetIOS/RCTActionSheetManager.m @@ -10,6 +10,7 @@ #import "RCTActionSheetManager.h" #import "RCTLog.h" +#import "RCTUtils.h" @interface RCTActionSheetManager () @@ -90,7 +91,7 @@ RCT_EXPORT_METHOD(showShareActionSheetWithOptions:(NSDictionary *)options if (activityError) { failureCallback(@[[activityError localizedDescription]]); } else { - successCallback(@[@(completed), (activityType ?: [NSNull null])]); + successCallback(@[@(completed), RCTNullIfNil(activityType)]); } }; } else { @@ -100,7 +101,7 @@ RCT_EXPORT_METHOD(showShareActionSheetWithOptions:(NSDictionary *)options if (![UIActivityViewController instancesRespondToSelector:@selector(completionWithItemsHandler)]) { // Legacy iOS 7 implementation share.completionHandler = ^(NSString *activityType, BOOL completed) { - successCallback(@[@(completed), (activityType ?: [NSNull null])]); + successCallback(@[@(completed), RCTNullIfNil(activityType)]); }; } else @@ -109,7 +110,7 @@ RCT_EXPORT_METHOD(showShareActionSheetWithOptions:(NSDictionary *)options { // iOS 8 version share.completionWithItemsHandler = ^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) { - successCallback(@[@(completed), (activityType ?: [NSNull null])]); + successCallback(@[@(completed), RCTNullIfNil(activityType)]); }; } } diff --git a/Libraries/AppStateIOS/AppStateIOS.ios.js b/Libraries/AppStateIOS/AppStateIOS.ios.js index e123d23be..f7dddcd57 100644 --- a/Libraries/AppStateIOS/AppStateIOS.ios.js +++ b/Libraries/AppStateIOS/AppStateIOS.ios.js @@ -122,7 +122,11 @@ var AppStateIOS = { _eventHandlers[type].delete(handler); }, - currentState: (null : ?String), + // TODO: getCurrentAppState callback seems to be called at a really late stage + // after app launch. Trying to get currentState when mounting App component + // will likely to have the initial value here. + // Initialize to 'active' instead of null. + currentState: ('active' : ?string), }; diff --git a/Libraries/Components/MapView/MapView.js b/Libraries/Components/MapView/MapView.js index 3e387835c..50e36954b 100644 --- a/Libraries/Components/MapView/MapView.js +++ b/Libraries/Components/MapView/MapView.js @@ -82,6 +82,19 @@ var MapView = React.createClass({ */ scrollEnabled: React.PropTypes.bool, + /** + * The map type to be displayed. + * + * - standard: standard road map (default) + * - satellite: satellite view + * - hybrid: satellite view with roads and points of interest overlayed + */ + mapType: React.PropTypes.oneOf([ + 'standard', + 'satellite', + 'hybrid', + ]), + /** * The region to be displayed by the map. * diff --git a/Libraries/Components/Navigation/NavigatorIOS.ios.js b/Libraries/Components/Navigation/NavigatorIOS.ios.js index ef3ecd158..9ecf6bdc8 100644 --- a/Libraries/Components/Navigation/NavigatorIOS.ios.js +++ b/Libraries/Components/Navigation/NavigatorIOS.ios.js @@ -83,16 +83,16 @@ var NavigatorTransitionerIOS = React.createClass({ type Route = { component: Function; title: string; - passProps: Object; - backButtonTitle: string; - backButtonIcon: Object; - leftButtonTitle: string; - leftButtonIcon: Object; - onLeftButtonPress: Function; - rightButtonTitle: string; - rightButtonIcon: Object; - onRightButtonPress: Function; - wrapperStyle: any; + passProps?: Object; + backButtonTitle?: string; + backButtonIcon?: Object; + leftButtonTitle?: string; + leftButtonIcon?: Object; + onLeftButtonPress?: Function; + rightButtonTitle?: string; + rightButtonIcon?: Object; + onRightButtonPress?: Function; + wrapperStyle?: any; }; type State = { diff --git a/Libraries/Components/ScrollView/ScrollView.js b/Libraries/Components/ScrollView/ScrollView.js index 8d274bdc5..8952fb0d1 100644 --- a/Libraries/Components/ScrollView/ScrollView.js +++ b/Libraries/Components/ScrollView/ScrollView.js @@ -200,6 +200,16 @@ var ScrollView = React.createClass({ this.refs[SCROLLVIEW].setNativeProps(props); }, + /** + * Returns a reference to the underlying scroll responder, which supports + * operations like `scrollTo`. All ScrollView-like components should + * implement this method so that they can be composed while providing access + * to the underlying scroll responder's methods. + */ + getScrollResponder: function(): ReactComponent { + return this; + }, + getInnerViewNode: function(): any { return React.findNodeHandle(this.refs[INNERVIEW]); }, @@ -299,7 +309,7 @@ var ScrollView = React.createClass({ onResponderRelease: this.scrollResponderHandleResponderRelease, onResponderReject: this.scrollResponderHandleResponderReject, }; - + var ScrollViewClass; if (Platform.OS === 'ios') { ScrollViewClass = RCTScrollView; diff --git a/Libraries/Components/WebView/WebView.android.js b/Libraries/Components/WebView/WebView.android.js index 18491d52c..872e2b3bd 100644 --- a/Libraries/Components/WebView/WebView.android.js +++ b/Libraries/Components/WebView/WebView.android.js @@ -43,6 +43,11 @@ var WebView = React.createClass({ startInLoadingState: PropTypes.bool, // force WebView to show loadingView on first load style: View.propTypes.style, javaScriptEnabledAndroid: PropTypes.bool, + /** + * Sets the user-agent for this WebView. The user-agent can also be set in native through + * WebViewConfig, but this can and will overwrite that config. + */ + userAgent: PropTypes.string, /** * Used to locate this view in end-to-end tests. */ @@ -91,6 +96,7 @@ var WebView = React.createClass({ key="webViewKey" style={webViewStyles} url={this.props.url} + userAgent={this.props.userAgent} javaScriptEnabledAndroid={this.props.javaScriptEnabledAndroid} contentInset={this.props.contentInset} automaticallyAdjustContentInsets={this.props.automaticallyAdjustContentInsets} @@ -172,6 +178,7 @@ var RCTWebView = createReactNativeComponentClass({ validAttributes: merge(ReactNativeViewAttributes.UIView, { url: true, javaScriptEnabledAndroid: true, + userAgent: true, }), uiViewClassName: 'RCTWebView', }); diff --git a/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js b/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js index 48be13ea1..439534ddd 100644 --- a/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js +++ b/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js @@ -313,14 +313,14 @@ var FromTheFrontAndroid = { opacity: { from: 0, to: 1, - min: 0, + min: 0.5, max: 1, type: 'linear', extrapolate: false, round: 100, }, transformTranslate: { - from: {x: 0, y: 50, z: 0}, + from: {x: 0, y: 100, z: 0}, to: {x: 0, y: 0, z: 0}, min: 0, max: 1, @@ -329,7 +329,7 @@ var FromTheFrontAndroid = { round: PixelRatio.get(), }, translateY: { - from: 50, + from: 100, to: 0, min: 0, max: 1, @@ -432,6 +432,8 @@ var NavigatorSceneConfigs = { FloatFromBottomAndroid: { ...BaseConfig, gestures: null, + defaultTransitionVelocity: 3, + springFriction: 20, animationInterpolators: { into: buildStyleInterpolator(FromTheFrontAndroid), out: buildStyleInterpolator(ToTheBackAndroid), diff --git a/Libraries/Image/RCTImageLoader.m b/Libraries/Image/RCTImageLoader.m index f28502d7e..04fa17f5d 100644 --- a/Libraries/Image/RCTImageLoader.m +++ b/Libraries/Image/RCTImageLoader.m @@ -22,7 +22,7 @@ #import "RCTLog.h" #import "RCTUtils.h" -static void RCTDispatchCallbackOnMainQueue(void (^ __nonnull callback)(NSError *, id), NSError *error, UIImage *image) +static void RCTDispatchCallbackOnMainQueue(void (^callback)(NSError *, id), NSError *error, UIImage *image) { if ([NSThread isMainThread]) { callback(error, image); diff --git a/Libraries/ReactIOS/InspectorOverlay/BorderBox.js b/Libraries/Inspector/BorderBox.js similarity index 100% rename from Libraries/ReactIOS/InspectorOverlay/BorderBox.js rename to Libraries/Inspector/BorderBox.js diff --git a/Libraries/ReactIOS/InspectorOverlay/BoxInspector.js b/Libraries/Inspector/BoxInspector.js similarity index 100% rename from Libraries/ReactIOS/InspectorOverlay/BoxInspector.js rename to Libraries/Inspector/BoxInspector.js diff --git a/Libraries/ReactIOS/InspectorOverlay/ElementBox.js b/Libraries/Inspector/ElementBox.js similarity index 100% rename from Libraries/ReactIOS/InspectorOverlay/ElementBox.js rename to Libraries/Inspector/ElementBox.js diff --git a/Libraries/ReactIOS/InspectorOverlay/ElementProperties.js b/Libraries/Inspector/ElementProperties.js similarity index 98% rename from Libraries/ReactIOS/InspectorOverlay/ElementProperties.js rename to Libraries/Inspector/ElementProperties.js index 310374fb1..855423845 100644 --- a/Libraries/ReactIOS/InspectorOverlay/ElementProperties.js +++ b/Libraries/Inspector/ElementProperties.js @@ -11,15 +11,15 @@ */ 'use strict'; +var BoxInspector = require('BoxInspector'); +var PropTypes = require('ReactPropTypes'); var React = require('React'); +var StyleInspector = require('StyleInspector'); var StyleSheet = require('StyleSheet'); var Text = require('Text'); -var View = require('View'); -var PropTypes = require('ReactPropTypes'); -var BoxInspector = require('BoxInspector'); -var StyleInspector = require('StyleInspector'); var TouchableHighlight = require('TouchableHighlight'); var TouchableWithoutFeedback = require('TouchableWithoutFeedback'); +var View = require('View'); var flattenStyle = require('flattenStyle'); var mapWithSeparator = require('mapWithSeparator'); @@ -93,7 +93,6 @@ var styles = StyleSheet.create({ justifyContent: 'space-between', }, info: { - backgroundColor: 'rgba(0, 0, 0, 0.7)', padding: 10, }, path: { diff --git a/Libraries/Inspector/Inspector.js b/Libraries/Inspector/Inspector.js new file mode 100644 index 000000000..46615d967 --- /dev/null +++ b/Libraries/Inspector/Inspector.js @@ -0,0 +1,115 @@ +/** + * 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 Inspector + * @flow + */ +'use strict'; + +var Dimensions = require('Dimensions'); +var InspectorOverlay = require('InspectorOverlay'); +var InspectorPanel = require('InspectorPanel'); +var InspectorUtils = require('InspectorUtils'); +var React = require('React'); +var StyleSheet = require('StyleSheet'); +var UIManager = require('NativeModules').UIManager; +var View = require('View'); + +class Inspector extends React.Component { + constructor(props: Object) { + super(props); + this.state = { + panelPos: 'bottom', + inspecting: true, + inspected: null, + }; + } + + setSelection(i: number) { + var instance = this.state.hierarchy[i]; + var publicInstance = instance.getPublicInstance(); + UIManager.measure(React.findNodeHandle(instance), (x, y, width, height, left, top) => { + this.setState({ + inspected: { + frame: {left, top, width, height}, + style: publicInstance.props ? publicInstance.props.style : {}, + }, + selection: i, + }); + }); + } + + onTouchInstance(instance: Object, frame: Object, pointerY: number) { + var hierarchy = InspectorUtils.getOwnerHierarchy(instance); + var publicInstance = instance.getPublicInstance(); + var props = publicInstance.props || {}; + this.setState({ + panelPos: pointerY > Dimensions.get('window').height / 2 ? 'top' : 'bottom', + selection: hierarchy.length - 1, + hierarchy, + inspected: { + style: props.style || {}, + frame, + }, + }); + } + + setInspecting(val: bool) { + this.setState({ + inspecting: val, + }); + } + + render() { + var panelPosition; + if (this.state.panelPos === 'bottom') { + panelPosition = {bottom: -Dimensions.get('window').height}; + } else { + panelPosition = {top: 0}; + } + return ( + + {this.state.inspecting && + } + + + + + ); + } +} + +var styles = StyleSheet.create({ + container: { + position: 'absolute', + backgroundColor: 'transparent', + top: 0, + left: 0, + right: 0, + height: 0, + }, + panelContainer: { + position: 'absolute', + left: 0, + right: 0, + }, +}); + +module.exports = Inspector; diff --git a/Libraries/Inspector/InspectorOverlay.js b/Libraries/Inspector/InspectorOverlay.js new file mode 100644 index 000000000..cf9dd8d68 --- /dev/null +++ b/Libraries/Inspector/InspectorOverlay.js @@ -0,0 +1,81 @@ +/** + * 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 InspectorOverlay + * @flow + */ +'use strict'; + +var Dimensions = require('Dimensions'); +var InspectorUtils = require('InspectorUtils'); +var React = require('React'); +var StyleSheet = require('StyleSheet'); +var UIManager = require('NativeModules').UIManager; +var View = require('View'); +var ElementBox = require('ElementBox'); + +var PropTypes = React.PropTypes; + +type EventLike = { + nativeEvent: Object; +}; + +var InspectorOverlay = React.createClass({ + propTypes: { + inspectedViewTag: PropTypes.object, + onTouchInstance: PropTypes.func.isRequired, + }, + + findViewForTouchEvent: function(e: EventLike) { + var {locationX, locationY} = e.nativeEvent.touches[0]; + UIManager.findSubviewIn( + this.props.inspectedViewTag, + [locationX, locationY], + (nativeViewTag, left, top, width, height) => { + var instance = InspectorUtils.findInstanceByNativeTag(this.props.rootTag, nativeViewTag); + if (!instance) { + return; + } + this.props.onTouchInstance(instance, {left, top, width, height}, locationY); + } + ); + }, + + shouldSetResponser: function(e: EventLike): bool { + this.findViewForTouchEvent(e); + return true; + }, + + render: function() { + var content = null; + if (this.props.inspected) { + content = ; + } + + return ( + + {content} + + ); + } +}); + +var styles = StyleSheet.create({ + inspector: { + backgroundColor: 'transparent', + position: 'absolute', + left: 0, + top: 0, + right: 0, + }, +}); + +module.exports = InspectorOverlay; diff --git a/Libraries/Inspector/InspectorPanel.js b/Libraries/Inspector/InspectorPanel.js new file mode 100644 index 000000000..7f49f19dc --- /dev/null +++ b/Libraries/Inspector/InspectorPanel.js @@ -0,0 +1,119 @@ +/** + * 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 InspectorPanel + * @flow + */ +'use strict'; + +var React = require('React'); +var StyleSheet = require('StyleSheet'); +var Text = require('Text'); +var View = require('View'); +var ElementProperties = require('ElementProperties'); +var TouchableHighlight = require('TouchableHighlight'); + +var PropTypes = React.PropTypes; + +class InspectorPanel extends React.Component { + renderWaiting() { + if (this.props.inspecting) { + return ( + + Tap something to inspect it + + ); + } + return Nothing is inspected; + } + + render() { + var contents; + if (this.props.inspected) { + contents = ( + + ); + } else { + contents = ( + + {this.renderWaiting()} + + ); + } + return ( + + {contents} + +