Compare commits

..
11 Commits
Author SHA1 Message Date
Mike Grabowski 3336fb5b55 [0.25.1] Bump version numbers 2016-05-04 21:13:57 +02:00
Mike Grabowski 418a0b27df [0.25.0] Bump version numbers 2016-05-04 18:32:17 +02:00
Mike Grabowski 938fa5a012 Revert "[0.25.0] Bump version numbers"
This reverts commit 07888e5efd.
2016-05-04 18:32:10 +02:00
Konstantin Raev 0eb187958d Fixed path to regenerator-runtime
Summary:
Since 0.8.43 regenerator has regenerator-runtime dependency.
Fixes one js test in trunk
Closes https://github.com/facebook/react-native/pull/7351

Reviewed By: davidaurelio

Differential Revision: D3252699

Pulled By: bestander

fb-gh-sync-id: cedb122eb8e9f3a5e40a5313482bc2282511c71e
fbshipit-source-id: cedb122eb8e9f3a5e40a5313482bc2282511c71e
2016-05-04 17:46:36 +02:00
Konstantin Raev 26e506f130 fixed website deployment condition logic
Summary:Rookie mistake I made.

**Test plan (required)**

See that https://circleci.com/gh/facebook/react-native/5462 succeeds deploying the website correctly.
Closes https://github.com/facebook/react-native/pull/7063

Differential Revision: D3196198

fb-gh-sync-id: 14fdc94fad0b1e5ff74cc90bb89d2bd1bf29bfa3
fbshipit-source-id: 14fdc94fad0b1e5ff74cc90bb89d2bd1bf29bfa3
2016-05-04 16:24:39 +02:00
Mike Grabowski 07888e5efd [0.25.0] Bump version numbers 2016-05-04 14:49:53 +02:00
Mike Grabowski 08da2cda0b Document how to disable YellowBox
Summary:As mentioned in #7121 by javache, this can already be disabled. Might be useful if users upgrade to 0.25 and want to hide warnings about wrong React imported (something they can't really control until community upgrades).

This should be mentioned in the release notes once it's merged.
Closes https://github.com/facebook/react-native/pull/7140

Differential Revision: D3212212

fb-gh-sync-id: 7a7afa2b7925e16d6ebfd20f4fe26d1cb9d603e5
fbshipit-source-id: 7a7afa2b7925e16d6ebfd20f4fe26d1cb9d603e5
2016-05-04 14:05:37 +02:00
Mike Grabowski b4ba7b5533 Update imports in generated projects
Summary:Fixes warnings when React API is used by requiring 'react-native'.
Closes https://github.com/facebook/react-native/pull/7060

Differential Revision: D3196109

Pulled By: mkonicek

fb-gh-sync-id: b47afc138cd7909ad53c0d67a65e3a124b87134b
fbshipit-source-id: b47afc138cd7909ad53c0d67a65e3a124b87134b
2016-05-04 14:05:03 +02:00
Dave Miller bcf91bcd16 Fix crash caused by accessibility being turned on and using a Modal
Summary: When the ModalHostView is added as a child of whatever view holds it, if accessibility is turned on, Android will walk up to the root and then walk all children of the child and verify that they are indeed children of the root.  Since ModalHostView actually adds its children to a new ReactDialogViewGroup which has the Dialog as a parent, there is a disagreement about the tree deep in the bowels of View when it performs that walk.  The trick is to stop from adding the children of the ModalHostView when walking for accessibility.  The accessibility of those children views are properly handled by the hosting Dialog.

Reviewed By: andreicoman11

Differential Revision: D3230033

fb-gh-sync-id: 1e5ac334c996b1d5f50c75ded60805d8b871477a
fbshipit-source-id: 1e5ac334c996b1d5f50c75ded60805d8b871477a
2016-05-04 14:04:06 +02:00
Joshua Sierles 028936334c Use latest simulator by not specifying the OS version
Summary:Appetize asked for this so they can better support us, since they will usually have more simulators available for the latest versions.
Closes https://github.com/facebook/react-native/pull/7061

Differential Revision: D3196110

Pulled By: mkonicek

fb-gh-sync-id: 2406ab6e1ee352f0148957e851d7f483a2182f90
fbshipit-source-id: 2406ab6e1ee352f0148957e851d7f483a2182f90
2016-05-04 14:03:53 +02:00
Mike Grabowski 2e6fd4af4a [0.25.0-rc] Bump version numbers 2016-04-18 20:49:25 +02:00
942 changed files with 13191 additions and 28728 deletions
+3 -5
View File
@@ -221,19 +221,17 @@
"react/display-name": 0,
"react/jsx-boolean-value": 0,
"react/jsx-no-duplicate-props": 2,
"react/jsx-no-undef": 1,
"react/jsx-sort-props": 0,
"react/jsx-uses-react": 1,
"react/jsx-uses-react": 0,
"react/jsx-uses-vars": 1,
"react/no-did-mount-set-state": [1, "allow-in-func"],
"react/no-did-update-set-state": [1, "allow-in-func"],
"react/no-multi-comp": 0,
"react/no-string-refs": 1,
"react/no-unknown-property": 0,
"react/prop-types": 0,
"react/react-in-jsx-scope": 1,
"react/react-in-jsx-scope": 0,
"react/self-closing-comp": 1,
"react/wrap-multilines": 0,
"react/wrap-multilines": 0
}
}
+6 -7
View File
@@ -15,6 +15,8 @@
# Ignore react and fbjs where there are overlaps, but don't ignore
# anything that react-native relies on
.*/node_modules/fbjs/lib/Map.js
.*/node_modules/fbjs/lib/fetch.js
.*/node_modules/fbjs/lib/ExecutionEnvironment.js
.*/node_modules/fbjs/lib/ErrorUtils.js
# Flow has a built-in definition for the 'react' module which we prefer to use
@@ -46,9 +48,6 @@
# Ignore BUCK generated folders
.*\.buckd/
# Ignore RNPM
.*/local-cli/rnpm/.*
.*/node_modules/is-my-json-valid/test/.*\.json
.*/node_modules/iconv-lite/encodings/tables/.*\.json
.*/node_modules/y18n/test/.*\.json
@@ -82,15 +81,15 @@ esproposal.class_instance_fields=enable
munge_underscores=true
module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub'
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\)$' -> 'RelativeImageStub'
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FixMe
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(2[0-5]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(2[0-5]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(2[0-3]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(2[0-3]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
[version]
^0.25.0
0.23.0
-3
View File
@@ -49,6 +49,3 @@ node_modules
# OS X
.DS_Store
# Test generated files
/ReactAndroid/src/androidTest/assets/AndroidTestBundle.js
-2
View File
@@ -1,2 +0,0 @@
# rnpm
/local-cli/rnpm
+4 -3
View File
@@ -3,12 +3,13 @@ language: objective-c
osx_image: xcode7.3
install:
- brew update
- brew reinstall nvm xctool
- mkdir -p /Users/travis/build/facebook/.nvm
- export NVM_DIR="/Users/travis/build/facebook/.nvm"
- brew install nvm
- source $(brew --prefix nvm)/nvm.sh
# testing with npm@2 for diversity, Circle tests with npm@3
- nvm install 4.4.3
- nvm install 4.4.0
- rm -Rf "${TMPDIR}/jest_preprocess_cache"
- npm config set spin=false
- npm config set progress=false
@@ -16,7 +17,7 @@ install:
script:
- if [[ "$TEST_TYPE" = objc ]]; then travis_retry ./scripts/objc-test.sh; fi
- if [[ "$TEST_TYPE" = e2e-objc ]]; then node ./scripts/run-ci-e2e-tests.js --ios --js --retries 3; fi
- if [[ "$TEST_TYPE" = e2e-objc ]]; then travis_retry node ./scripts/run-ci-e2e-tests.js --ios --js; fi
- if [[ "$TEST_TYPE" = js ]]; then npm run flow check; fi
- if [[ "$TEST_TYPE" = js ]]; then npm test -- --maxWorkers=1; fi
+2 -8
View File
@@ -267,10 +267,7 @@
INFOPLIST_FILE = "$(SRCROOT)/2048/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",
);
OTHER_LDFLAGS = "-ObjC";
PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = 2048;
};
@@ -288,10 +285,7 @@
INFOPLIST_FILE = "$(SRCROOT)/2048/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",
);
OTHER_LDFLAGS = "-ObjC";
PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = 2048;
};
+3
View File
@@ -45,6 +45,9 @@ var MovieCell = React.createClass({
onShowUnderlay={this.props.onHighlight}
onHideUnderlay={this.props.onUnhighlight}>
<View style={styles.row}>
{/* $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 */}
<Image
source={getImageSource(this.props.movie, 'det')}
style={styles.cellImage}
+3
View File
@@ -34,6 +34,9 @@ var MovieScreen = React.createClass({
return (
<ScrollView contentContainerStyle={styles.contentContainer}>
<View style={styles.mainSection}>
{/* $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 */}
<Image
source={getImageSource(this.props.movie, 'det')}
style={styles.detailsImage}
@@ -357,10 +357,7 @@
INFOPLIST_FILE = "$(SRCROOT)/Movies/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",
);
OTHER_LDFLAGS = "-ObjC";
PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = Movies;
USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../Libraries/**";
@@ -379,10 +376,7 @@
INFOPLIST_FILE = "$(SRCROOT)/Movies/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",
);
OTHER_LDFLAGS = "-ObjC";
PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = Movies;
USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../Libraries/**";
@@ -3,7 +3,7 @@
*/
'use strict';
jest.unmock('../getImageSource');
jest.dontMock('../getImageSource');
var getImageSource = require('../getImageSource');
@@ -327,10 +327,7 @@
INFOPLIST_FILE = "$(SRCROOT)/TicTacToe/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",
);
OTHER_LDFLAGS = "-ObjC";
PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = TicTacToe;
};
@@ -348,10 +345,7 @@
INFOPLIST_FILE = "$(SRCROOT)/TicTacToe/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",
);
OTHER_LDFLAGS = "-ObjC";
PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = TicTacToe;
};
-1
View File
@@ -13,7 +13,6 @@
*/
'use strict';
var React = require('react');
var ReactNative = require('react-native');
var {
StyleSheet,
-1
View File
@@ -13,7 +13,6 @@
*/
'use strict';
var React = require('react');
var ReactNative = require('react-native');
var {
Image,
@@ -1,171 +0,0 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* 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.
*
* @flow
*/
'use strict';
const React = require('react');
const ReactNative = require('react-native');
const {
LayoutAnimation,
StyleSheet,
Text,
View,
TouchableOpacity,
} = ReactNative;
const AddRemoveExample = React.createClass({
getInitialState() {
return {
views: [],
};
},
componentWillUpdate() {
LayoutAnimation.easeInEaseOut();
},
_onPressAddView() {
this.setState((state) => ({views: [...state.views, {}]}));
},
_onPressRemoveView() {
this.setState((state) => ({views: state.views.slice(0, -1)}));
},
render() {
const views = this.state.views.map((view, i) =>
<View key={i} style={styles.view}>
<Text>{i}</Text>
</View>
);
return (
<View style={styles.container}>
<TouchableOpacity onPress={this._onPressAddView}>
<View style={styles.button}>
<Text>Add view</Text>
</View>
</TouchableOpacity>
<TouchableOpacity onPress={this._onPressRemoveView}>
<View style={styles.button}>
<Text>Remove view</Text>
</View>
</TouchableOpacity>
<View style={styles.viewContainer}>
{views}
</View>
</View>
);
},
});
const GreenSquare = () =>
<View style={styles.greenSquare}>
<Text>Green square</Text>
</View>;
const BlueSquare = () =>
<View style={styles.blueSquare}>
<Text>Blue square</Text>
</View>;
const CrossFadeExample = React.createClass({
getInitialState() {
return {
toggled: false,
};
},
_onPressToggle() {
LayoutAnimation.easeInEaseOut();
this.setState((state) => ({toggled: !state.toggled}));
},
render() {
return (
<View style={styles.container}>
<TouchableOpacity onPress={this._onPressToggle}>
<View style={styles.button}>
<Text>Toggle</Text>
</View>
</TouchableOpacity>
<View style={styles.viewContainer}>
{
this.state.toggled ?
<GreenSquare /> :
<BlueSquare />
}
</View>
</View>
);
},
});
const styles = StyleSheet.create({
container: {
flex: 1,
},
button: {
borderRadius: 5,
backgroundColor: '#eeeeee',
padding: 10,
marginBottom: 10,
},
buttonText: {
fontSize: 16,
},
viewContainer: {
flex: 1,
flexDirection: 'row',
flexWrap: 'wrap',
},
view: {
height: 54,
width: 54,
backgroundColor: 'red',
margin: 8,
alignItems: 'center',
justifyContent: 'center',
},
greenSquare: {
width: 150,
height: 150,
backgroundColor: 'green',
alignItems: 'center',
justifyContent: 'center',
},
blueSquare: {
width: 150,
height: 150,
backgroundColor: 'blue',
alignItems: 'center',
justifyContent: 'center',
},
});
exports.title = 'Layout Animation';
exports.description = 'Layout animation';
exports.examples = [{
title: 'Add and remove views',
render(): ReactElement {
return <AddRemoveExample />;
},
}, {
title: 'Cross fade views',
render(): ReactElement {
return <CrossFadeExample />;
},
}];
+7 -18
View File
@@ -58,20 +58,17 @@ var ListViewSimpleExample = React.createClass({
dataSource={this.state.dataSource}
renderRow={this._renderRow}
renderScrollComponent={props => <RecyclerViewBackedScrollView {...props} />}
renderSeparator={this._renderSeperator}
renderSeparator={(sectionID, rowID) => <View key={`${sectionID}-${rowID}`} style={styles.separator} />}
/>
</UIExplorerPage>
);
},
_renderRow: function(rowData: string, sectionID: number, rowID: number, highlightRow: (sectionID: number, rowID: number) => void) {
_renderRow: function(rowData: string, sectionID: number, rowID: number) {
var rowHash = Math.abs(hashCode(rowData));
var imgSource = THUMB_URLS[rowHash % THUMB_URLS.length];
return (
<TouchableHighlight onPress={() => {
this._pressRow(rowID);
highlightRow(sectionID, rowID);
}}>
<TouchableHighlight onPress={() => this._pressRow(rowID)}>
<View>
<View style={styles.row}>
<Image style={styles.thumb} source={imgSource} />
@@ -99,18 +96,6 @@ var ListViewSimpleExample = React.createClass({
this._genRows(this._pressData)
)});
},
_renderSeperator: function(sectionID: number, rowID: number, adjacentRowHighlighted: bool) {
return (
<View
key={`${sectionID}-${rowID}`}
style={{
height: adjacentRowHighlighted ? 4 : 1,
backgroundColor: adjacentRowHighlighted ? '#3B5998' : '#CCCCCC',
}}
/>
);
}
});
var THUMB_URLS = [
@@ -145,6 +130,10 @@ var styles = StyleSheet.create({
padding: 10,
backgroundColor: '#F6F6F6',
},
separator: {
height: 1,
backgroundColor: '#CCCCCC',
},
thumb: {
width: 64,
height: 64,
+8 -18
View File
@@ -66,7 +66,7 @@ var Button = React.createClass({
var ModalExample = React.createClass({
getInitialState() {
return {
animationType: 'none',
animated: true,
modalVisible: false,
transparent: false,
};
@@ -76,8 +76,8 @@ var ModalExample = React.createClass({
this.setState({modalVisible: visible});
},
_setAnimationType(type) {
this.setState({animationType: type});
_toggleAnimated() {
this.setState({animated: !this.state.animated});
},
_toggleTransparent() {
@@ -91,21 +91,18 @@ var ModalExample = React.createClass({
var innerContainerTransparentStyle = this.state.transparent
? {backgroundColor: '#fff', padding: 20}
: null;
var activeButtonStyle = {
backgroundColor: '#ddd'
};
return (
<View>
<Modal
animationType={this.state.animationType}
animated={this.state.animated}
transparent={this.state.transparent}
visible={this.state.modalVisible}
onRequestClose={() => {this._setModalVisible(false)}}
>
<View style={[styles.container, modalBackgroundStyle]}>
<View style={[styles.innerContainer, innerContainerTransparentStyle]}>
<Text>This modal was presented {this.state.animationType === 'none' ? 'without' : 'with'} animation.</Text>
<Text>This modal was presented {this.state.animated ? 'with' : 'without'} animation.</Text>
<Button
onPress={this._setModalVisible.bind(this, false)}
style={styles.modalButton}>
@@ -114,17 +111,10 @@ var ModalExample = React.createClass({
</View>
</View>
</Modal>
<View style={styles.row}>
<Text style={styles.rowTitle}>Animation Type</Text>
<Button onPress={this._setAnimationType.bind(this, 'none')} style={this.state.animationType === 'none' ? activeButtonStyle : {}}>
none
</Button>
<Button onPress={this._setAnimationType.bind(this, 'slide')} style={this.state.animationType === 'slide' ? activeButtonStyle : {}}>
slide
</Button>
<Button onPress={this._setAnimationType.bind(this, 'fade')} style={this.state.animationType === 'fade' ? activeButtonStyle : {}}>
fade
</Button>
<Text style={styles.rowTitle}>Animated</Text>
<Switch value={this.state.animated} onValueChange={this._toggleAnimated} />
</View>
<View style={styles.row}>
@@ -0,0 +1,165 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* 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 ReactNative = require('react-native');
var {
NavigationExperimental,
StyleSheet,
ScrollView,
Text,
TouchableHighlight,
TouchableOpacity
} = ReactNative;
var _getRandomRoute = function() {
return {
title: '#' + Math.ceil(Math.random() * 1000),
};
};
var Navigator = NavigationExperimental.LegacyNavigator;
class NavButton extends React.Component {
render() {
return (
<TouchableHighlight
style={styles.button}
underlayColor="#B5B5B5"
onPress={this.props.onPress}>
<Text style={styles.buttonText}>{this.props.text}</Text>
</TouchableHighlight>
);
}
}
var BreadcrumbNavSample = React.createClass({
componentWillMount: function() {
this._navBarRouteMapper = {
rightContentForRoute: function(route, navigator) {
return null;
},
titleContentForRoute: function(route, navigator) {
return (
<TouchableOpacity
onPress={() => navigator.push(_getRandomRoute())}>
<Text style={styles.titleText}>{route.title}</Text>
</TouchableOpacity>
);
},
iconForRoute: function(route, navigator) {
return (
<TouchableOpacity
onPress={() => { navigator.popToRoute(route); }}
style={styles.crumbIconPlaceholder}
/>
);
},
separatorForRoute: function(route, navigator) {
return (
<TouchableOpacity
onPress={navigator.pop}
style={styles.crumbSeparatorPlaceholder}
/>
);
}
};
},
_renderScene: function(route, navigator) {
return (
<ScrollView style={styles.scene}>
<NavButton
onPress={() => { navigator.push(_getRandomRoute()); }}
text="Push"
/>
<NavButton
onPress={() => { navigator.immediatelyResetRouteStack([_getRandomRoute(), _getRandomRoute()]); }}
text="Reset w/ 2 scenes"
/>
<NavButton
onPress={() => { navigator.popToTop(); }}
text="Pop to top"
/>
<NavButton
onPress={() => { navigator.replace(_getRandomRoute()); }}
text="Replace"
/>
<NavButton
onPress={() => { this.props.navigator.pop(); }}
text="Close breadcrumb example"
/>
</ScrollView>
);
},
render: function() {
return (
<Navigator
style={styles.container}
initialRoute={_getRandomRoute()}
renderScene={this._renderScene}
navigationBar={
<Navigator.BreadcrumbNavigationBar
routeMapper={this._navBarRouteMapper}
/>
}
/>
);
},
});
var styles = StyleSheet.create({
scene: {
paddingTop: 50,
flex: 1,
},
button: {
backgroundColor: 'white',
padding: 15,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#CDCDCD',
},
buttonText: {
fontSize: 17,
fontWeight: '500',
},
container: {
overflow: 'hidden',
backgroundColor: '#dddddd',
flex: 1,
},
titleText: {
fontSize: 18,
color: '#666666',
textAlign: 'center',
fontWeight: 'bold',
lineHeight: 32,
},
crumbIconPlaceholder: {
flex: 1,
backgroundColor: '#666666',
},
crumbSeparatorPlaceholder: {
flex: 1,
backgroundColor: '#aaaaaa',
},
});
module.exports = BreadcrumbNavSample;
@@ -0,0 +1,219 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* 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 ReactNative = require('react-native');
var {
NavigationExperimental,
StyleSheet,
ScrollView,
TabBarIOS,
Text,
TouchableHighlight,
View,
} = ReactNative;
var _getRandomRoute = function() {
return {
randNumber: Math.ceil(Math.random() * 1000),
};
};
var Navigator = NavigationExperimental.LegacyNavigator;
class NavButton extends React.Component {
render() {
return (
<TouchableHighlight
style={styles.button}
underlayColor="#B5B5B5"
onPress={this.props.onPress}>
<Text style={styles.buttonText}>{this.props.text}</Text>
</TouchableHighlight>
);
}
}
var ROUTE_STACK = [
_getRandomRoute(),
_getRandomRoute(),
_getRandomRoute(),
];
var INIT_ROUTE_INDEX = 1;
class JumpingNavBar extends React.Component {
constructor(props) {
super(props);
this.state = {
tabIndex: props.initTabIndex,
};
}
handleWillFocus(route) {
var tabIndex = ROUTE_STACK.indexOf(route);
this.setState({ tabIndex, });
}
render() {
return (
<View style={styles.tabs}>
<TabBarIOS>
<TabBarIOS.Item
icon={require('image!tabnav_notification')}
selected={this.state.tabIndex === 0}
onPress={() => {
this.props.onTabIndex(0);
this.setState({ tabIndex: 0, });
}}>
<View />
</TabBarIOS.Item>
<TabBarIOS.Item
icon={require('image!tabnav_list')}
selected={this.state.tabIndex === 1}
onPress={() => {
this.props.onTabIndex(1);
this.setState({ tabIndex: 1, });
}}>
<View />
</TabBarIOS.Item>
<TabBarIOS.Item
icon={require('image!tabnav_settings')}
selected={this.state.tabIndex === 2}
onPress={() => {
this.props.onTabIndex(2);
this.setState({ tabIndex: 2, });
}}>
<View />
</TabBarIOS.Item>
</TabBarIOS>
</View>
);
}
}
var JumpingNavSample = React.createClass({
render: function() {
return (
<Navigator
debugOverlay={false}
style={styles.appContainer}
ref={(navigator) => {
this._navigator = navigator;
}}
initialRoute={ROUTE_STACK[INIT_ROUTE_INDEX]}
initialRouteStack={ROUTE_STACK}
renderScene={this.renderScene}
configureScene={() => ({
...Navigator.SceneConfigs.HorizontalSwipeJump,
})}
navigationBar={
<JumpingNavBar
ref={(navBar) => { this.navBar = navBar; }}
initTabIndex={INIT_ROUTE_INDEX}
routeStack={ROUTE_STACK}
onTabIndex={(index) => {
this._navigator.jumpTo(ROUTE_STACK[index]);
}}
/>
}
/>
);
},
renderScene: function(route, navigator) {
var backBtn;
var forwardBtn;
if (ROUTE_STACK.indexOf(route) !== 0) {
backBtn = (
<NavButton
onPress={() => {
navigator.jumpBack();
}}
text="jumpBack"
/>
);
}
if (ROUTE_STACK.indexOf(route) !== ROUTE_STACK.length - 1) {
forwardBtn = (
<NavButton
onPress={() => {
navigator.jumpForward();
}}
text="jumpForward"
/>
);
}
return (
<ScrollView style={styles.scene}>
<Text style={styles.messageText}>#{route.randNumber}</Text>
{backBtn}
{forwardBtn}
<NavButton
onPress={() => {
navigator.jumpTo(ROUTE_STACK[1]);
}}
text="jumpTo middle route"
/>
<NavButton
onPress={() => {
this.props.navigator.pop();
}}
text="Exit Navigation Example"
/>
<NavButton
onPress={() => {
this.props.navigator.push({
message: 'Came from jumping example',
});
}}
text="Nav Menu"
/>
</ScrollView>
);
},
});
var styles = StyleSheet.create({
button: {
backgroundColor: 'white',
padding: 15,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#CDCDCD',
},
buttonText: {
fontSize: 17,
fontWeight: '500',
},
appContainer: {
overflow: 'hidden',
backgroundColor: '#dddddd',
flex: 1,
},
messageText: {
fontSize: 17,
fontWeight: '500',
padding: 15,
marginTop: 50,
marginLeft: 15,
},
scene: {
flex: 1,
paddingTop: 20,
backgroundColor: '#EAEAEA',
},
tabs: {
height: 50,
}
});
module.exports = JumpingNavSample;
@@ -0,0 +1,220 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* 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 ReactNative = require('react-native');
var {
NavigationExperimental,
ScrollView,
StyleSheet,
Text,
TouchableHighlight,
} = ReactNative;
var BreadcrumbNavSample = require('./BreadcrumbNavSample');
var NavigationBarSample = require('./NavigationBarSample');
var JumpingNavSample = require('./JumpingNavSample');
var Navigator = NavigationExperimental.LegacyNavigator;
class NavButton extends React.Component {
render() {
return (
<TouchableHighlight
style={styles.button}
underlayColor="#B5B5B5"
onPress={this.props.onPress}>
<Text style={styles.buttonText}>{this.props.text}</Text>
</TouchableHighlight>
);
}
}
class NavMenu extends React.Component {
render() {
return (
<ScrollView style={styles.scene}>
<Text style={styles.messageText}>{this.props.message}</Text>
<NavButton
onPress={() => {
this.props.navigator.push({
message: 'Swipe right to dismiss',
sceneConfig: Navigator.SceneConfigs.FloatFromRight,
});
}}
text="Float in from right"
/>
<NavButton
onPress={() => {
this.props.navigator.push({
message: 'Swipe down to dismiss',
sceneConfig: Navigator.SceneConfigs.FloatFromBottom,
});
}}
text="Float in from bottom"
/>
<NavButton
onPress={() => {
this.props.navigator.pop();
}}
text="Pop"
/>
<NavButton
onPress={() => {
this.props.navigator.popToTop();
}}
text="Pop to top"
/>
<NavButton
onPress={() => {
this.props.navigator.push({ id: 'navbar' });
}}
text="Navbar Example"
/>
<NavButton
onPress={() => {
this.props.navigator.push({ id: 'jumping' });
}}
text="Jumping Example"
/>
<NavButton
onPress={() => {
this.props.navigator.push({ id: 'breadcrumbs' });
}}
text="Breadcrumbs Example"
/>
<NavButton
onPress={() => {
this.props.onExampleExit();
}}
text="Exit <LegacyNavigator> Example"
/>
</ScrollView>
);
}
}
var TabBarExample = React.createClass({
statics: {
title: '<LegacyNavigator> (Experimental)',
description: 'Experimental navigator that ports the API of the current ' +
'Navigator component',
},
renderScene: function(route, nav) {
switch (route.id) {
case 'navbar':
return <NavigationBarSample navigator={nav} />;
case 'breadcrumbs':
return <BreadcrumbNavSample navigator={nav} />;
case 'jumping':
return <JumpingNavSample navigator={nav} />;
default:
return (
<NavMenu
message={route.message}
navigator={nav}
onExampleExit={this.props.onExampleExit}
/>
);
}
},
render: function() {
return (
<Navigator
ref={this._setNavigatorRef}
style={styles.container}
initialRoute={{ message: 'First Scene', }}
renderScene={this.renderScene}
configureScene={(route) => {
if (route.sceneConfig) {
return route.sceneConfig;
}
return Navigator.SceneConfigs.FloatFromBottom;
}}
/>
);
},
componentWillUnmount: function() {
this._listeners && this._listeners.forEach(listener => listener.remove());
},
_setNavigatorRef: function(navigator) {
if (navigator !== this._navigator) {
this._navigator = navigator;
if (navigator) {
var callback = (event) => {
console.log(
`LegacyNavigator: event ${event.type}`,
{
route: JSON.stringify(event.data.route),
target: event.target,
type: event.type,
}
);
};
// Observe focus change events from the owner.
this._listeners = [
navigator.navigationContext.addListener('willfocus', callback),
navigator.navigationContext.addListener('didfocus', callback),
];
}
}
},
});
var styles = StyleSheet.create({
messageText: {
fontSize: 17,
fontWeight: '500',
padding: 15,
marginTop: 50,
marginLeft: 15,
},
container: {
flex: 1,
},
button: {
backgroundColor: 'white',
padding: 15,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#CDCDCD',
},
buttonText: {
fontSize: 17,
fontWeight: '500',
},
scene: {
flex: 1,
paddingTop: 20,
backgroundColor: '#ccc',
}
});
TabBarExample.external = true;
module.exports = TabBarExample;
@@ -0,0 +1,202 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* 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 ReactNative = require('react-native');
var {
NavigationExperimental,
ScrollView,
StyleSheet,
Text,
TouchableHighlight,
TouchableOpacity,
} = ReactNative;
var Navigator = NavigationExperimental.LegacyNavigator;
class NavButton extends React.Component {
render() {
return (
<TouchableHighlight
style={styles.button}
underlayColor="#B5B5B5"
onPress={this.props.onPress}>
<Text style={styles.buttonText}>{this.props.text}</Text>
</TouchableHighlight>
);
}
}
var NavigationBarRouteMapper = {
LeftButton: function(route, navigator, index, navState) {
if (index === 0) {
return null;
}
var previousRoute = navState.routeStack[index - 1];
return (
<TouchableOpacity
onPress={() => navigator.pop()}
style={styles.navBarLeftButton}>
<Text style={[styles.navBarText, styles.navBarButtonText]}>
{previousRoute.title}
</Text>
</TouchableOpacity>
);
},
RightButton: function(route, navigator, index, navState) {
return (
<TouchableOpacity
onPress={() => navigator.push(newRandomRoute())}
style={styles.navBarRightButton}>
<Text style={[styles.navBarText, styles.navBarButtonText]}>
Next
</Text>
</TouchableOpacity>
);
},
Title: function(route, navigator, index, navState) {
return (
<Text style={[styles.navBarText, styles.navBarTitleText]}>
{route.title} [{index}]
</Text>
);
},
};
function newRandomRoute() {
return {
title: '#' + Math.ceil(Math.random() * 1000),
};
}
var NavigationBarSample = React.createClass({
componentWillMount: function() {
var navigator = this.props.navigator;
var callback = (event) => {
console.log(
`NavigationBarSample : event ${event.type}`,
{
route: JSON.stringify(event.data.route),
target: event.target,
type: event.type,
}
);
};
// Observe focus change events from this component.
this._listeners = [
navigator.navigationContext.addListener('willfocus', callback),
navigator.navigationContext.addListener('didfocus', callback),
];
},
componentWillUnmount: function() {
this._listeners && this._listeners.forEach(listener => listener.remove());
},
render: function() {
return (
<Navigator
debugOverlay={false}
style={styles.appContainer}
initialRoute={newRandomRoute()}
renderScene={(route, navigator) => (
<ScrollView style={styles.scene}>
<Text style={styles.messageText}>{route.content}</Text>
<NavButton
onPress={() => {
navigator.immediatelyResetRouteStack([
newRandomRoute(),
newRandomRoute(),
newRandomRoute(),
]);
}}
text="Reset w/ 3 scenes"
/>
<NavButton
onPress={() => {
this.props.navigator.pop();
}}
text="Exit NavigationBar Example"
/>
</ScrollView>
)}
navigationBar={
<Navigator.NavigationBar
routeMapper={NavigationBarRouteMapper}
style={styles.navBar}
/>
}
/>
);
},
});
var styles = StyleSheet.create({
messageText: {
fontSize: 17,
fontWeight: '500',
padding: 15,
marginTop: 50,
marginLeft: 15,
},
button: {
backgroundColor: 'white',
padding: 15,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#CDCDCD',
},
buttonText: {
fontSize: 17,
fontWeight: '500',
},
navBar: {
backgroundColor: 'white',
},
navBarText: {
fontSize: 16,
marginVertical: 10,
},
navBarTitleText: {
color: '#373E4D',
fontWeight: '500',
marginVertical: 9,
},
navBarLeftButton: {
paddingLeft: 10,
},
navBarRightButton: {
paddingRight: 10,
},
navBarButtonText: {
color: '#5890FF',
},
scene: {
flex: 1,
paddingTop: 20,
backgroundColor: '#EAEAEA',
},
});
module.exports = NavigationBarSample;
@@ -37,9 +37,10 @@ const {
Card: NavigationCard,
Header: NavigationHeader,
Reducer: NavigationReducer,
RootContainer: NavigationRootContainer,
} = NavigationExperimental;
const ExampleReducer = NavigationReducer.StackReducer({
const NavigationBasicReducer = NavigationReducer.StackReducer({
getPushedReducerForAction: (action) => {
if (action.type === 'push') {
return (state) => state || {key: action.key};
@@ -57,41 +58,37 @@ const ExampleReducer = NavigationReducer.StackReducer({
});
class NavigationAnimatedExample extends React.Component {
constructor(props, context) {
super(props, context);
this.state = ExampleReducer();
}
componentWillMount() {
this._renderCard = this._renderCard.bind(this);
this._renderHeader = this._renderHeader.bind(this);
this._renderNavigation = this._renderNavigation.bind(this);
this._renderScene = this._renderScene.bind(this);
this._renderTitleComponent = this._renderTitleComponent.bind(this);
this._handleAction = this._handleAction.bind(this);
}
_handleAction(action): boolean {
if (!action) {
return false;
}
const newState = ExampleReducer(this.state, action);
if (newState === this.state) {
return false;
}
this.setState(newState);
return true;
}
handleBackAction(): boolean {
return this._handleAction({ type: 'BackAction', });
}
render() {
return (
<NavigationRootContainer
reducer={NavigationBasicReducer}
ref={navRootContainer => { this.navRootContainer = navRootContainer; }}
persistenceKey="NavigationAnimExampleState"
renderNavigation={this._renderNavigation}
/>
);
}
handleBackAction() {
return (
this.navRootContainer &&
this.navRootContainer.handleNavigation(NavigationRootContainer.getBackAction())
);
}
_renderNavigation(navigationState, onNavigate) {
if (!navigationState) {
return null;
}
return (
<NavigationAnimatedView
navigationState={this.state}
navigationState={navigationState}
style={styles.animatedView}
onNavigate={this._handleAction}
renderOverlay={this._renderHeader}
applyAnimation={(pos, navState) => {
Animated.timing(pos, {toValue: navState.index, duration: 500}).start();
@@ -1,11 +1,4 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
@@ -29,10 +22,12 @@ const {
} = ReactNative;
const NavigationExampleRow = require('./NavigationExampleRow');
const {
RootContainer: NavigationRootContainer,
Reducer: NavigationReducer,
} = NavigationExperimental;
const StackReducer = NavigationReducer.StackReducer;
const ExampleReducer = NavigationReducer.StackReducer({
const NavigationBasicReducer = NavigationReducer.StackReducer({
getPushedReducerForAction: (action) => {
if (action.type === 'push') {
return (state) => state || {key: action.key};
@@ -50,51 +45,47 @@ const ExampleReducer = NavigationReducer.StackReducer({
});
const NavigationBasicExample = React.createClass({
getInitialState: function() {
return ExampleReducer();
},
render: function() {
return (
<ScrollView style={styles.topView}>
<NavigationExampleRow
text={`Current page: ${this.state.children[this.state.index].key}`}
/>
<NavigationExampleRow
text={`Push page #${this.state.children.length}`}
onPress={() => {
this._handleAction({ type: 'push', key: 'page #' + this.state.children.length });
}}
/>
<NavigationExampleRow
text="pop"
onPress={() => {
this._handleAction({ type: 'BackAction' });
}}
/>
<NavigationExampleRow
text="Exit Basic Nav Example"
onPress={this.props.onExampleExit}
/>
</ScrollView>
<NavigationRootContainer
reducer={NavigationBasicReducer}
persistenceKey="NavigationBasicExampleState"
ref={navRootContainer => { this.navRootContainer = navRootContainer; }}
renderNavigation={(navState, onNavigate) => {
if (!navState) { return null; }
return (
<ScrollView style={styles.topView}>
<NavigationExampleRow
text={`Current page: ${navState.children[navState.index].key}`}
/>
<NavigationExampleRow
text={`Push page #${navState.children.length}`}
onPress={() => {
onNavigate({ type: 'push', key: 'page #' + navState.children.length });
}}
/>
<NavigationExampleRow
text="pop"
onPress={() => {
onNavigate(NavigationRootContainer.getBackAction());
}}
/>
<NavigationExampleRow
text="Exit Basic Nav Example"
onPress={this.props.onExampleExit}
/>
</ScrollView>
);
}}
/>
);
},
_handleAction(action) {
if (!action) {
return false;
}
const newState = ExampleReducer(this.state, action);
if (newState === this.state) {
return false;
}
this.setState(newState);
return true;
},
handleBackAction() {
return this._handleAction({ type: 'BackAction' });
handleBackAction: function() {
return (
this.navRootContainer &&
this.navRootContainer.handleNavigation(NavigationRootContainer.getBackAction())
);
},
});
@@ -33,15 +33,18 @@ const {
const {
CardStack: NavigationCardStack,
StateUtils: NavigationStateUtils,
RootContainer: NavigationRootContainer,
} = NavigationExperimental;
function createReducer(initialState) {
return (currentState = initialState, action) => {
return (currentState, action) => {
switch (action.type) {
case 'RootContainerInitialAction':
return initialState;
case 'push':
return NavigationStateUtils.push(currentState, {key: action.key});
case 'BackAction':
case 'back':
case 'pop':
return currentState.index > 0 ?
@@ -64,46 +67,35 @@ class NavigationCardStackExample extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
isHorizontal: true,
navState: ExampleReducer(undefined, {}),
};
this.state = {isHorizontal: true};
}
componentWillMount() {
this._renderNavigation = this._renderNavigation.bind(this);
this._renderScene = this._renderScene.bind(this);
this._toggleDirection = this._toggleDirection.bind(this);
this._handleAction = this._handleAction.bind(this);
}
render() {
return (
<NavigationCardStack
direction={this.state.isHorizontal ? 'horizontal' : 'vertical'}
navigationState={this.state.navState}
onNavigate={this._handleAction}
renderScene={this._renderScene}
<NavigationRootContainer
reducer={ExampleReducer}
renderNavigation={this._renderNavigation}
style={styles.main}
/>
);
}
_handleAction(action): boolean {
if (!action) {
return false;
}
const newState = ExampleReducer(this.state.navState, action);
if (newState === this.state.navState) {
return false;
}
this.setState({
navState: newState,
});
return true;
}
handleBackAction(): boolean {
return this._handleAction({ type: 'BackAction', });
_renderNavigation(navigationState, onNavigate) {
return (
<NavigationCardStack
direction={this.state.isHorizontal ? 'horizontal' : 'vertical'}
navigationState={navigationState}
onNavigate={onNavigate}
renderScene={this._renderScene}
style={styles.main}
/>
);
}
_renderScene(/*NavigationSceneRendererProps*/ props) {
@@ -36,8 +36,11 @@ const {
const {
CardStack: NavigationCardStack,
Container: NavigationContainer,
Header: NavigationHeader,
Reducer: NavigationReducer,
RootContainer: NavigationRootContainer,
View: NavigationView,
} = NavigationExperimental;
@@ -174,7 +177,6 @@ class ExampleTabScreen extends React.Component {
<NavigationCardStack
style={styles.tabContent}
navigationState={this.props.navigationState}
onNavigate={this.props.onNavigate}
renderOverlay={this._renderHeader}
renderScene={this._renderScene}
/>
@@ -223,42 +225,40 @@ class ExampleTabScreen extends React.Component {
);
}
}
ExampleTabScreen = NavigationContainer.create(ExampleTabScreen);
class NavigationCompositionExample extends React.Component {
state: NavigationParentState;
constructor() {
super();
this.state = ExampleAppReducer(undefined, {});
}
handleAction(action: Object): boolean {
if (!action) {
return false;
}
const newState = ExampleAppReducer(this.state, action);
if (newState === this.state) {
return false;
}
this.setState(newState);
return true;
navRootContainer: NavigationRootContainer;
render() {
return (
<NavigationRootContainer
reducer={ExampleAppReducer}
persistenceKey="NavigationCompositionState"
ref={navRootContainer => { this.navRootContainer = navRootContainer; }}
renderNavigation={this.renderApp.bind(this)}
/>
);
}
handleBackAction(): boolean {
return this.handleAction({ type: 'BackAction' });
return (
this.navRootContainer &&
this.navRootContainer.handleNavigation(NavigationRootContainer.getBackAction())
);
}
render() {
if (!this.state) {
renderApp(navigationState: NavigationParentState, onNavigate: Function) {
if (!navigationState) {
return null;
}
return (
<View style={styles.topView}>
<ExampleMainView
navigationState={this.state}
navigationState={navigationState}
onExampleExit={this.props.onExampleExit}
onNavigate={this.handleAction.bind(this)}
/>
<NavigationExampleTabBar
tabs={this.state.children}
index={this.state.index}
onNavigate={this.handleAction.bind(this)}
tabs={navigationState.children}
index={navigationState.index}
/>
</View>
);
@@ -266,35 +266,34 @@ class NavigationCompositionExample extends React.Component {
}
class ExampleMainView extends React.Component {
_renderScene: Function;
_handleNavigation: Function;
_renderScene: NavigationSceneRenderer;
componentWillMount() {
this._renderScene = this._renderScene.bind(this);
this._handleNavigation = this._handleNavigation.bind(this);
}
render() {
return (
<View style={styles.tabsContent}>
{this._renderScene()}
</View>
);
}
_renderScene(): ReactElement {
const {navigationState} = this.props;
const childState = navigationState.children[navigationState.index];
return (
<ExampleTabScreen
key={'tab_screen' + childState.key}
navigationState={childState}
onNavigate={this._handleNavigation}
<NavigationView
navigationState={this.props.navigationState}
style={styles.tabsContent}
renderScene={this._renderScene}
/>
);
}
_handleNavigation(action: Object) {
_renderScene(props: NavigationSceneRendererProps): ReactElement {
const {scene} = props;
return (
<ExampleTabScreen
key={'tab_screen' + scene.key}
navigationState={scene.navigationState}
onNavigate={this._handleNavigation.bind(this, scene.key)}
/>
);
}
_handleNavigation(tabKey, action) {
if (ExampleExitAction.match(action)) {
this.props.onExampleExit();
return;
@@ -303,6 +302,8 @@ class ExampleMainView extends React.Component {
}
}
ExampleMainView = NavigationContainer.create(ExampleMainView);
const styles = StyleSheet.create({
topView: {
flex: 1,
@@ -1,11 +1,4 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
@@ -20,20 +13,24 @@
*/
'use strict';
const NavigationExperimental = require('NavigationExperimental');
const React = require('react');
const StyleSheet = require('StyleSheet');
const Text = require('Text');
const TouchableOpacity = require('TouchableOpacity');
const View = require('View');
var React = require('react');
var ReactNative = require('react-native');
var {
NavigationExperimental,
StyleSheet,
Text,
TouchableOpacity,
View,
} = ReactNative;
const {
Container: NavigationContainer,
Reducer: NavigationReducer,
} = NavigationExperimental;
const {
JumpToAction,
} = NavigationReducer.TabsReducer;
const NavigationExampleTabBar = React.createClass({
var NavigationExampleTabBar = React.createClass({
render: function() {
return (
<View style={styles.tabBar}>
@@ -42,7 +39,7 @@ const NavigationExampleTabBar = React.createClass({
);
},
_renderTab: function(tab, index) {
const textStyle = [styles.tabButtonText];
var textStyle = [styles.tabButtonText];
if (this.props.index === index) {
textStyle.push(styles.selectedTabButtonText);
}
@@ -61,6 +58,8 @@ const NavigationExampleTabBar = React.createClass({
},
});
NavigationExampleTabBar = NavigationContainer.create(NavigationExampleTabBar);
const styles = StyleSheet.create({
tabBar: {
height: 50,
@@ -1,11 +1,4 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
@@ -20,19 +13,22 @@
*/
'use strict';
const AsyncStorage = require('AsyncStorage');
const NavigationExampleRow = require('./NavigationExampleRow');
const React = require('react');
const ScrollView = require('ScrollView');
const StyleSheet = require('StyleSheet');
const View = require('View');
var React = require('react');
var ReactNative = require('react-native');
var {
AsyncStorage,
ScrollView,
StyleSheet,
View,
} = ReactNative;
var NavigationExampleRow = require('./NavigationExampleRow');
/*
* Heads up! This file is not the real navigation example- only a utility to switch between them.
*
* To learn how to use the Navigation API, take a look at the following example files:
*/
const EXAMPLES = {
var EXAMPLES = {
'Tabs': require('./NavigationTabsExample'),
'Basic': require('./NavigationBasicExample'),
'Animated Example': require('./NavigationAnimatedExample'),
@@ -41,9 +37,9 @@ const EXAMPLES = {
'Tic Tac Toe': require('./NavigationTicTacToeExample'),
};
const EXAMPLE_STORAGE_KEY = 'NavigationExperimentalExample';
var EXAMPLE_STORAGE_KEY = 'NavigationExperimentalExample';
const NavigationExperimentalExample = React.createClass({
var NavigationExperimentalExample = React.createClass({
statics: {
title: 'Navigation (Experimental)',
description: 'Upcoming navigation APIs and animated navigation views',
@@ -78,7 +74,7 @@ const NavigationExperimentalExample = React.createClass({
},
_renderMenu: function() {
let exitRow = null;
var exitRow = null;
if (this.props.onExampleExit) {
exitRow = (
<NavigationExampleRow
@@ -134,7 +130,7 @@ const NavigationExperimentalExample = React.createClass({
return this._renderMenu();
}
if (EXAMPLES[this.state.example]) {
const Component = EXAMPLES[this.state.example];
var Component = EXAMPLES[this.state.example];
return (
<Component
onExampleExit={this._exitInnerExample}
@@ -1,11 +1,4 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
@@ -29,6 +22,8 @@ const {
View,
} = ReactNative;
const {
Container: NavigationContainer,
RootContainer: NavigationRootContainer,
Reducer: NavigationReducer,
} = NavigationExperimental;
@@ -60,6 +55,7 @@ class ExmpleTabPage extends React.Component {
);
}
}
ExmpleTabPage = NavigationContainer.create(ExmpleTabPage);
const ExampleTabsReducer = NavigationReducer.TabsReducer({
tabReducers: [
@@ -70,40 +66,36 @@ const ExampleTabsReducer = NavigationReducer.TabsReducer({
});
class NavigationTabsExample extends React.Component {
constructor() {
super();
this.state = ExampleTabsReducer(undefined, {});
}
render() {
return (
<View style={styles.topView}>
<ExmpleTabPage
tabs={this.state.children}
index={this.state.index}
onExampleExit={this.props.onExampleExit}
onNavigate={this.handleAction.bind(this)}
/>
<NavigationExampleTabBar
tabs={this.state.children}
index={this.state.index}
onNavigate={this.handleAction.bind(this)}
/>
</View>
<NavigationRootContainer
reducer={ExampleTabsReducer}
persistenceKey="NAV_EXAMPLE_STATE_TABS"
ref={navRootContainer => { this.navRootContainer = navRootContainer; }}
renderNavigation={(navigationState) => {
if (!navigationState) { return null; }
return (
<View style={styles.topView}>
<ExmpleTabPage
tabs={navigationState.children}
index={navigationState.index}
onExampleExit={this.props.onExampleExit}
/>
<NavigationExampleTabBar
tabs={navigationState.children}
index={navigationState.index}
/>
</View>
);
}}
/>
);
}
handleAction(action) {
if (!action) {
return false;
}
const newState = ExampleTabsReducer(this.state, action);
if (newState === this.state) {
return false;
}
this.setState(newState);
return true;
}
handleBackAction() {
return this.handleAction({ type: 'BackAction' });
return (
this.navRootContainer &&
this.navRootContainer.handleNavigation(NavigationRootContainer.getBackAction())
);
}
}
@@ -1,11 +1,4 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
@@ -23,11 +16,19 @@
*/
'use strict';
const React = require('react');
const StyleSheet = require('StyleSheet');
const Text = require('Text');
const TouchableHighlight = require('TouchableHighlight');
const View = require('View');
var React = require('react');
var ReactNative = require('react-native');
var {
NavigationExperimental,
StyleSheet,
Text,
TouchableHighlight,
View,
} = ReactNative;
const {
Container: NavigationContainer,
RootContainer: NavigationRootContainer,
} = NavigationExperimental;
type GameGrid = Array<Array<?string>>;
@@ -40,7 +41,7 @@ function parseGame(game: string): GameGrid {
for (let i = 0; i < 3; i++) {
const row = Array(3);
for (let j = 0; j < 3; j++) {
const turnIndex = gameTurns.indexOf(rowLetterMap[i] + j);
const turnIndex = gameTurns.indexOf(rowLetterMap[i]+j);
if (turnIndex === -1) {
row[j] = null;
} else {
@@ -59,13 +60,13 @@ function playTurn(game: string, row: number, col: number): string {
function getWinner(gameString: string): ?string {
const game = parseGame(gameString);
for (let i = 0; i < 3; i++) {
for (var i = 0; i < 3; i++) {
if (game[i][0] !== null && game[i][0] === game[i][1] &&
game[i][0] === game[i][2]) {
return game[i][0];
}
}
for (let i = 0; i < 3; i++) {
for (var i = 0; i < 3; i++) {
if (game[0][i] !== null && game[0][i] === game[1][i] &&
game[0][i] === game[2][i]) {
return game[0][i];
@@ -87,8 +88,8 @@ function isGameOver(gameString: string): boolean {
return true;
}
const game = parseGame(gameString);
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
if (game[i][j] === null) {
return false;
}
@@ -156,9 +157,10 @@ function GameEndOverlay(props) {
</View>
);
}
GameEndOverlay = NavigationContainer.create(GameEndOverlay);
function TicTacToeGame(props) {
const rows = parseGame(props.game).map((cells, row) =>
var rows = parseGame(props.game).map((cells, row) =>
<View key={'row' + row} style={styles.row}>
{cells.map((player, col) =>
<Cell
@@ -182,11 +184,11 @@ function TicTacToeGame(props) {
</View>
<GameEndOverlay
game={props.game}
onNavigate={props.onNavigate}
/>
</View>
);
}
TicTacToeGame = NavigationContainer.create(TicTacToeGame);
const GameActions = {
Turn: (row, col) => ({type: 'TicTacToeTurnAction', row, col }),
@@ -206,34 +208,21 @@ function GameReducer(lastGame: ?string, action: Object): string {
return lastGame;
}
type AppState = {
game: string,
};
class NavigationTicTacToeExample extends React.Component {
_handleAction: Function;
state: AppState;
constructor() {
super();
this._handleAction = this._handleAction.bind(this);
this.state = {
game: ''
};
}
_handleAction(action: Object) {
const newState = GameReducer(this.state.game, action);
if (newState !== this.state.game) {
this.setState({
game: newState,
});
}
}
static GameView = TicTacToeGame;
static GameReducer = GameReducer;
static GameActions = GameActions;
render() {
return (
<TicTacToeGame
game={this.state && this.state.game}
onExampleExit={this.props.onExampleExit}
onNavigate={this._handleAction}
<NavigationRootContainer
reducer={GameReducer}
persistenceKey="TicTacToeGameState"
renderNavigation={(game) => (
<TicTacToeGame
game={game}
onExampleExit={this.props.onExampleExit}
/>
)}
/>
);
}
@@ -43,17 +43,11 @@ var Button = React.createClass({
class NotificationExample extends React.Component {
componentWillMount() {
// Add listener for push notifications
PushNotificationIOS.addEventListener('notification', this._onNotification);
// Add listener for local notifications
PushNotificationIOS.addEventListener('localNotification', this._onLocalNotification);
}
componentWillUnmount() {
// Remove listener for push notifications
PushNotificationIOS.removeEventListener('notification', this._onNotification);
// Remove listener for local notifications
PushNotificationIOS.removeEventListener('localNotification', this._onLocalNotification);
}
render() {
@@ -63,11 +57,6 @@ class NotificationExample extends React.Component {
onPress={this._sendNotification}
label="Send fake notification"
/>
<Button
onPress={this._sendLocalNotification}
label="Send fake local notification"
/>
</View>
);
}
@@ -83,31 +72,9 @@ class NotificationExample extends React.Component {
});
}
_sendLocalNotification() {
require('RCTDeviceEventEmitter').emit('localNotificationReceived', {
aps: {
alert: 'Sample local notification',
badge: '+1',
sound: 'default',
category: 'REACT_NATIVE'
},
});
}
_onNotification(notification) {
AlertIOS.alert(
'Push Notification Received',
'Alert message: ' + notification.getMessage(),
[{
text: 'Dismiss',
onPress: null,
}]
);
}
_onLocalNotification(notification){
AlertIOS.alert(
'Local Notification Received',
'Notification Received',
'Alert message: ' + notification.getMessage(),
[{
text: 'Dismiss',
-2
View File
@@ -54,7 +54,6 @@ var TabBarExample = React.createClass({
render: function() {
return (
<TabBarIOS
unselectedTintColor="yellow"
tintColor="white"
barTintColor="darkslateblue">
<TabBarIOS.Item
@@ -82,7 +81,6 @@ var TabBarExample = React.createClass({
</TabBarIOS.Item>
<TabBarIOS.Item
icon={require('./flux.png')}
renderAsOriginal
title="More"
selected={this.state.selectedTab === 'greenTab'}
onPress={() => {
+7 -15
View File
@@ -1,11 +1,4 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
@@ -162,19 +155,19 @@ exports.examples = [
render: function() {
return (
<View>
<Text style={{fontSize: 20, fontWeight: '100'}}>
<Text style={{fontWeight: '100'}}>
Move fast and be ultralight
</Text>
<Text style={{fontSize: 20, fontWeight: '200'}}>
<Text style={{fontWeight: '200'}}>
Move fast and be light
</Text>
<Text style={{fontSize: 20, fontWeight: 'normal'}}>
<Text style={{fontWeight: 'normal'}}>
Move fast and be normal
</Text>
<Text style={{fontSize: 20, fontWeight: 'bold'}}>
<Text style={{fontWeight: 'bold'}}>
Move fast and be bold
</Text>
<Text style={{fontSize: 20, fontWeight: '900'}}>
<Text style={{fontWeight: '900'}}>
Move fast and be ultrabold
</Text>
</View>
@@ -422,13 +415,12 @@ exports.examples = [
);
},
}, {
title: 'Inline views',
title: 'Inline images',
render: function() {
return (
<View>
<Text>
This text contains an inline blue view <View style={{width: 25, height: 25, backgroundColor: 'steelblue'}} /> and
an inline image <Image source={require('./flux.png')} style={{width: 30, height: 11, resizeMode: 'cover'}}/>. Neat, huh?
This text contains an inline image <Image source={require('./flux.png')} style={{width: 30, height: 11, resizeMode: 'cover'}}/>. Neat, huh?
</Text>
</View>
);
@@ -407,31 +407,6 @@ exports.examples = [
);
}
},
{
title: 'fontFamily, fontWeight and fontStyle',
render: function() {
return (
<View>
<TextInput
style={[styles.singleLine, {fontFamily: 'sans-serif'}]}
placeholder="Custom fonts like Sans-Serif are supported"
/>
<TextInput
style={[styles.singleLine, {fontFamily: 'sans-serif', fontWeight: 'bold'}]}
placeholder="Sans-Serif bold"
/>
<TextInput
style={[styles.singleLine, {fontFamily: 'sans-serif', fontStyle: 'italic'}]}
placeholder="Sans-Serif italic"
/>
<TextInput
style={[styles.singleLine, {fontFamily: 'serif'}]}
placeholder="Serif"
/>
</View>
);
}
},
{
title: 'Passwords',
render: function() {
@@ -531,43 +506,4 @@ exports.examples = [
return <TokenizedTextExample />;
}
},
{
title: 'Return key',
render: function() {
var returnKeyTypes = [
'none',
'go',
'search',
'send',
'done',
'previous',
'next',
];
var returnKeyLabels = [
'Compile',
'React Native',
];
var examples = returnKeyTypes.map((type) => {
return (
<TextInput
key={type}
returnKeyType={type}
placeholder={'returnKeyType: ' + type}
style={styles.singleLine}
/>
);
});
var types = returnKeyLabels.map((type) => {
return (
<TextInput
key={type}
returnKeyLabel={type}
placeholder={'returnKeyLabel: ' + type}
style={styles.singleLine}
/>
);
});
return <View>{examples}{types}</View>;
}
},
];
@@ -955,6 +955,10 @@
004D28A61AAF61C70097A701 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
HEADER_SEARCH_PATHS = (
"$(inherited)",
@@ -968,6 +972,12 @@
"$(inherited)",
"$(PROJECT_DIR)/UIExplorerUnitTests",
);
OTHER_LDFLAGS = (
"$(inherited)",
"-framework",
XCTest,
"-ObjC",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
};
@@ -976,6 +986,10 @@
004D28A71AAF61C70097A701 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../../React/**",
@@ -988,6 +1002,12 @@
"$(inherited)",
"$(PROJECT_DIR)/UIExplorerUnitTests",
);
OTHER_LDFLAGS = (
"$(inherited)",
"-framework",
XCTest,
"-ObjC",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
};
@@ -1007,6 +1027,7 @@
INFOPLIST_FILE = "$(SRCROOT)/UIExplorer/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited)";
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.react.uiapp;
PRODUCT_NAME = UIExplorer;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -1032,6 +1053,7 @@
INFOPLIST_FILE = "$(SRCROOT)/UIExplorer/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited)";
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.react.uiapp;
PRODUCT_NAME = UIExplorer;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -1049,6 +1071,10 @@
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
@@ -1057,6 +1083,12 @@
INFOPLIST_FILE = UIExplorerIntegrationTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.3;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
OTHER_LDFLAGS = (
"$(inherited)",
"-framework",
XCTest,
"-ObjC",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.React.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/UIExplorer.app/UIExplorer";
@@ -1069,10 +1101,20 @@
BUNDLE_LOADER = "$(TEST_HOST)";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = UIExplorerIntegrationTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.3;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
OTHER_LDFLAGS = (
"$(inherited)",
"-framework",
XCTest,
"-ObjC",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.React.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/UIExplorer.app/UIExplorer";
@@ -1136,10 +1178,6 @@
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",
);
SDKROOT = iphoneos;
WARNING_CFLAGS = (
"-Wextra",
@@ -1198,10 +1236,6 @@
);
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
MTL_ENABLE_DEBUG_INFO = NO;
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",
);
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
WARNING_CFLAGS = (
+3 -8
View File
@@ -1,11 +1,4 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
@@ -32,7 +25,9 @@ export type UIExplorerExampleAction = {
openExample: string;
};
export type UIExplorerAction = UIExplorerListWithFilterAction | UIExplorerExampleAction;
import type {BackAction} from 'NavigationRootContainer';
export type UIExplorerAction = BackAction | UIExplorerListWithFilterAction | UIExplorerExampleAction;
function ExampleListWithFilter(filter: ?string): UIExplorerListWithFilterAction {
return {
+68 -66
View File
@@ -1,11 +1,4 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
@@ -23,24 +16,29 @@
*/
'use strict';
const AppRegistry = require('AppRegistry');
const AsyncStorage = require('AsyncStorage');
const BackAndroid = require('BackAndroid');
const Dimensions = require('Dimensions');
const DrawerLayoutAndroid = require('DrawerLayoutAndroid');
const Linking = require('Linking');
const React = require('react');
const StatusBar = require('StatusBar');
const StyleSheet = require('StyleSheet');
const ToolbarAndroid = require('ToolbarAndroid');
const ReactNative = require('react-native');
const {
AppRegistry,
BackAndroid,
Dimensions,
DrawerLayoutAndroid,
NavigationExperimental,
StyleSheet,
ToolbarAndroid,
View,
StatusBar,
} = ReactNative;
const {
RootContainer: NavigationRootContainer,
} = NavigationExperimental;
const UIExplorerExampleList = require('./UIExplorerExampleList');
const UIExplorerList = require('./UIExplorerList');
const UIExplorerNavigationReducer = require('./UIExplorerNavigationReducer');
const UIExplorerStateTitleMap = require('./UIExplorerStateTitleMap');
const URIActionMap = require('./URIActionMap');
const View = require('View');
const DRAWER_WIDTH_LEFT = 56;
var DRAWER_WIDTH_LEFT = 56;
type Props = {
exampleFromAppetizeParams: string,
@@ -51,13 +49,14 @@ type State = {
};
class UIExplorerApp extends React.Component {
_handleAction: Function;
_renderDrawerContent: Function;
_handleOpenInitialExample: Function;
state: State;
constructor(props: Props) {
super(props);
this._handleAction = this._handleAction.bind(this);
this._renderDrawerContent = this._renderDrawerContent.bind(this);
this._handleOpenInitialExample = this._handleOpenInitialExample.bind(this);
this.state = {
initialExampleUri: props.exampleFromAppetizeParams,
};
}
componentWillMount() {
@@ -65,28 +64,38 @@ class UIExplorerApp extends React.Component {
}
componentDidMount() {
Linking.getInitialURL().then((url) => {
AsyncStorage.getItem('UIExplorerAppState', (err, storedString) => {
const exampleAction = URIActionMap(this.props.exampleFromAppetizeParams);
const urlAction = URIActionMap(url);
const launchAction = exampleAction || urlAction;
if (err || !storedString) {
const initialAction = launchAction || {type: 'InitialAction'};
this.setState(UIExplorerNavigationReducer(null, initialAction));
return;
}
const storedState = JSON.parse(storedString);
if (launchAction) {
this.setState(UIExplorerNavigationReducer(storedState, launchAction));
return;
}
this.setState(storedState);
});
});
// There's a race condition if we try to navigate to the specified example
// from the initial props at the same time the navigation logic is setting
// up the initial navigation state. This hack adds a delay to avoid this
// scenario. So after the initial example list is shown, we then transition
// to the initial example.
setTimeout(this._handleOpenInitialExample, 500);
}
render() {
if (!this.state) {
return (
<NavigationRootContainer
persistenceKey="UIExplorerStateNavState"
ref={navRootRef => { this._navigationRootRef = navRootRef; }}
reducer={UIExplorerNavigationReducer}
renderNavigation={this._renderApp.bind(this)}
linkingActionMap={URIActionMap}
/>
);
}
_handleOpenInitialExample() {
if (this.state.initialExampleUri) {
const exampleAction = URIActionMap(this.state.initialExampleUri);
if (exampleAction && this._navigationRootRef) {
this._navigationRootRef.handleNavigation(exampleAction);
}
}
this.setState({initialExampleUri: null});
}
_renderApp(navigationState, onNavigate) {
if (!navigationState) {
return null;
}
return (
@@ -101,42 +110,42 @@ class UIExplorerApp extends React.Component {
this._overrideBackPressForDrawerLayout = false;
}}
ref={(drawer) => { this.drawer = drawer; }}
renderNavigationView={this._renderDrawerContent}
renderNavigationView={this._renderDrawerContent.bind(this, onNavigate)}
statusBarBackgroundColor="#589c90">
{this._renderApp()}
{this._renderNavigation(navigationState, onNavigate)}
</DrawerLayoutAndroid>
);
}
_renderDrawerContent() {
_renderDrawerContent(onNavigate) {
return (
<View style={styles.drawerContentWrapper}>
<UIExplorerExampleList
list={UIExplorerList}
displayTitleRow={true}
disableSearch={true}
onNavigate={this._handleAction}
onNavigate={(action) => {
this.drawer && this.drawer.closeDrawer();
onNavigate(action);
}}
/>
</View>
);
}
_renderApp() {
const {
externalExample,
stack,
} = this.state;
if (externalExample) {
const Component = UIExplorerList.Modules[externalExample];
_renderNavigation(navigationState, onNavigate) {
if (navigationState.externalExample) {
var Component = UIExplorerList.Modules[navigationState.externalExample];
return (
<Component
onExampleExit={() => {
this._handleAction({ type: 'BackAction' });
onNavigate(NavigationRootContainer.getBackAction());
}}
ref={(example) => { this._exampleRef = example; }}
/>
);
}
const {stack} = navigationState;
const title = UIExplorerStateTitleMap(stack.children[stack.index]);
const index = stack.children.length <= 1 ? 1 : stack.index;
@@ -169,7 +178,6 @@ class UIExplorerApp extends React.Component {
title={title}
/>
<UIExplorerExampleList
onNavigate={this._handleAction}
list={UIExplorerList}
{...stack.children[0]}
/>
@@ -177,17 +185,6 @@ class UIExplorerApp extends React.Component {
);
}
_handleAction(action: Object): boolean {
this.drawer && this.drawer.closeDrawer();
const newState = UIExplorerNavigationReducer(this.state, action);
if (this.state !== newState) {
this.setState(newState);
AsyncStorage.setItem('UIExplorerAppState', JSON.stringify(this.state));
return true;
}
return false;
}
_handleBackButtonPress() {
if (this._overrideBackPressForDrawerLayout) {
// This hack is necessary because drawer layout provides an imperative API
@@ -203,7 +200,12 @@ class UIExplorerApp extends React.Component {
) {
return true;
}
return this._handleAction({ type: 'BackAction' });
if (this._navigationRootRef) {
return this._navigationRootRef.handleNavigation(
NavigationRootContainer.getBackAction()
);
}
return false;
}
}
+45 -52
View File
@@ -23,8 +23,6 @@
*/
'use strict';
const AsyncStorage = require('AsyncStorage');
const Linking = require('Linking');
const React = require('react');
const ReactNative = require('react-native');
const UIExplorerList = require('./UIExplorerList.ios');
@@ -44,6 +42,7 @@ const {
const {
CardStack: NavigationCardStack,
Header: NavigationHeader,
RootContainer: NavigationRootContainer,
} = NavigationExperimental;
import type { NavigationSceneRendererProps } from 'NavigationTypeDefinition';
@@ -56,86 +55,81 @@ type Props = {
exampleFromAppetizeParams: string,
};
type State = UIExplorerNavigationState & {
externalExample?: string,
type State = {
initialExampleUri: ?string,
};
class UIExplorerApp extends React.Component {
_navigationRootRef: ?NavigationRootContainer;
_renderNavigation: Function;
_renderOverlay: Function;
_renderScene: Function;
_renderCard: Function;
_renderTitleComponent: Function;
_handleAction: Function;
_handleOpenInitialExample: Function;
state: State;
constructor(props: Props) {
super(props);
this._handleOpenInitialExample = this._handleOpenInitialExample.bind(this);
this.state = {
initialExampleUri: props.exampleFromAppetizeParams,
};
}
componentWillMount() {
this._handleAction = this._handleAction.bind(this);
this._renderNavigation = this._renderNavigation.bind(this);
this._renderOverlay = this._renderOverlay.bind(this);
this._renderScene = this._renderScene.bind(this);
this._renderTitleComponent = this._renderTitleComponent.bind(this);
}
componentDidMount() {
Linking.getInitialURL().then((url) => {
AsyncStorage.getItem('UIExplorerAppState', (err, storedString) => {
const exampleAction = URIActionMap(this.props.exampleFromAppetizeParams);
const urlAction = URIActionMap(url);
const launchAction = exampleAction || urlAction;
if (err || !storedString) {
const initialAction = launchAction || {type: 'InitialAction'};
this.setState(UIExplorerNavigationReducer(null, initialAction));
return;
}
const storedState = JSON.parse(storedString);
if (launchAction) {
this.setState(UIExplorerNavigationReducer(storedState, launchAction));
return;
}
this.setState(storedState);
});
});
Linking.addEventListener('url', (url) => {
this._handleAction(URIActionMap(url));
});
// There's a race condition if we try to navigate to the specified example
// from the initial props at the same time the navigation logic is setting
// up the initial navigation state. This hack adds a delay to avoid this
// scenario. So after the initial example list is shown, we then transition
// to the initial example.
setTimeout(this._handleOpenInitialExample, 500);
}
_handleAction(action: Object) {
if (!action) {
return;
}
const newState = UIExplorerNavigationReducer(this.state, action);
if (this.state !== newState) {
this.setState(newState);
AsyncStorage.setItem('UIExplorerAppState', JSON.stringify(this.state));
}
}
render() {
if (!this.state) {
return (
<NavigationRootContainer
persistenceKey="UIExplorerState"
reducer={UIExplorerNavigationReducer}
ref={navRootRef => { this._navigationRootRef = navRootRef; }}
renderNavigation={this._renderNavigation}
linkingActionMap={URIActionMap}
/>
);
}
_handleOpenInitialExample() {
if (this.state.initialExampleUri) {
const exampleAction = URIActionMap(this.state.initialExampleUri);
if (exampleAction && this._navigationRootRef) {
this._navigationRootRef.handleNavigation(exampleAction);
}
}
this.setState({initialExampleUri: null});
}
_renderNavigation(navigationState: UIExplorerNavigationState, onNavigate: Function) {
if (!navigationState) {
return null;
}
if (this.state.externalExample) {
const Component = UIExplorerList.Modules[this.state.externalExample];
if (navigationState.externalExample) {
var Component = UIExplorerList.Modules[navigationState.externalExample];
return (
<Component
onExampleExit={() => {
this._handleAction({ type: 'BackAction' });
onNavigate(NavigationRootContainer.getBackAction());
}}
/>
);
}
const {stack} = navigationState;
return (
<NavigationCardStack
navigationState={this.state.stack}
navigationState={stack}
style={styles.container}
renderOverlay={this._renderOverlay}
renderScene={this._renderScene}
onNavigate={this._handleAction}
/>
);
}
@@ -162,7 +156,6 @@ class UIExplorerApp extends React.Component {
if (state.key === 'AppList') {
return (
<UIExplorerExampleList
onNavigate={this._handleAction}
list={UIExplorerList}
style={styles.exampleContainer}
{...state}
@@ -201,9 +194,9 @@ AppRegistry.registerComponent('UIExplorerApp', () => UIExplorerApp);
UIExplorerList.ComponentExamples.concat(UIExplorerList.APIExamples).forEach((Example: UIExplorerExample) => {
const ExampleModule = Example.module;
if (ExampleModule.displayName) {
const Snapshotter = React.createClass({
var Snapshotter = React.createClass({
render: function() {
const Renderable = UIExplorerExampleList.makeRenderable(ExampleModule);
var Renderable = UIExplorerExampleList.makeRenderable(ExampleModule);
return (
<SnapshotViewIOS>
<Renderable />
+25 -24
View File
@@ -1,11 +1,4 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
@@ -22,20 +15,26 @@
*/
'use strict';
const ListView = require('ListView');
const React = require('react');
const StyleSheet = require('StyleSheet');
const Text = require('Text');
const TextInput = require('TextInput');
const TouchableHighlight = require('TouchableHighlight');
const View = require('View');
const ReactNative = require('react-native');
const UIExplorerActions = require('./UIExplorerActions');
const {
ListView,
NavigationExperimental,
StyleSheet,
Text,
TextInput,
TouchableHighlight,
View,
} = ReactNative;
const createExamplePage = require('./createExamplePage');
const {
Container: NavigationContainer,
} = NavigationExperimental;
import type {
UIExplorerExample,
} from './UIExplorerList.ios';
} from './UIExplorerList.ios'
const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2,
@@ -55,13 +54,6 @@ class UIExplorerExampleList extends React.Component {
}) {
}
static makeRenderable(example: any): ReactClass<any> {
return example.examples ?
createExamplePage(null, example) :
example;
}
render(): ?ReactElement {
const filterText = this.props.filter || '';
const filterRegex = new RegExp(String(filterText), 'i');
@@ -162,11 +154,20 @@ class UIExplorerExampleList extends React.Component {
}
_handleRowPress(exampleKey: string): void {
this.props.onNavigate(UIExplorerActions.ExampleAction(exampleKey));
this.props.onNavigate(UIExplorerActions.ExampleAction(exampleKey))
}
}
const styles = StyleSheet.create({
function makeRenderable(example: any): ReactClass<any> {
return example.examples ?
createExamplePage(null, example) :
example;
}
UIExplorerExampleList = NavigationContainer.create(UIExplorerExampleList);
UIExplorerExampleList.makeRenderable = makeRenderable;
var styles = StyleSheet.create({
listContainer: {
flex: 1,
},
@@ -138,6 +138,12 @@ static ControlBlock propertiesUpdateBlock()
- (void)setUp
{
#if __LP64__
RCTAssert(NO, @"Tests should be run on 32-bit device simulators (e.g. iPhone 5)");
#endif
NSOperatingSystemVersion version = [NSProcessInfo processInfo].operatingSystemVersion;
RCTAssert((version.majorVersion == 8 && version.minorVersion >= 3) || version.majorVersion >= 9, @"Tests should be run on iOS 8.3+, found %zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion);
_runner = RCTInitRunnerForApp(@"IntegrationTests/RCTRootViewIntegrationTestApp", nil);
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 KiB

After

Width:  |  Height:  |  Size: 360 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

After

Width:  |  Height:  |  Size: 153 KiB

@@ -31,6 +31,12 @@
- (void)setUp
{
#if __LP64__
RCTAssert(NO, @"Tests should be run on 32-bit device simulators (e.g. iPhone 5)");
#endif
NSOperatingSystemVersion version = [NSProcessInfo processInfo].operatingSystemVersion;
RCTAssert((version.majorVersion == 8 && version.minorVersion >= 3) || version.majorVersion >= 9, @"Tests should be run on iOS 8.3+, found %zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion);
_runner = RCTInitRunnerForApp(@"IntegrationTests/IntegrationTestsApp", nil);
}
@@ -32,6 +32,12 @@
- (void)setUp
{
#if __LP64__
RCTAssert(NO, @"Tests should be run on 32-bit device simulators (e.g. iPhone 5)");
#endif
NSOperatingSystemVersion version = [NSProcessInfo processInfo].operatingSystemVersion;
RCTAssert((version.majorVersion == 8 && version.minorVersion >= 3) || version.majorVersion >= 9, @"Tests should be run on iOS 8.3+, found %zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion);
_runner = RCTInitRunnerForApp(@"Examples/UIExplorer/UIExplorerApp.ios", nil);
_runner.recordMode = NO;
}
+2 -10
View File
@@ -40,8 +40,8 @@ var ComponentExamples: Array<UIExplorerExample> = [
module: require('./ModalExample'),
},
{
key: 'PickerExample',
module: require('./PickerExample'),
key: 'PickerAndroidExample',
module: require('./PickerAndroidExample'),
},
{
key: 'ProgressBarAndroidExample',
@@ -138,10 +138,6 @@ const APIExamples = [
key: 'LinkingExample',
module: require('./LinkingExample'),
},
{
key: 'LayoutAnimationExample',
module: require('./LayoutAnimationExample'),
},
{
key: 'LayoutExample',
module: require('./LayoutExample'),
@@ -178,10 +174,6 @@ const APIExamples = [
key: 'VibrationExample',
module: require('./VibrationExample'),
},
{
key: 'WebSocketExample',
module: require('./WebSocketExample'),
},
{
key: 'XHRExample',
module: require('./XHRExample'),
+6 -25
View File
@@ -1,11 +1,4 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
@@ -27,7 +20,7 @@ export type UIExplorerExample = {
module: Object;
};
const ComponentExamples: Array<UIExplorerExample> = [
var ComponentExamples: Array<UIExplorerExample> = [
{
key: 'ActivityIndicatorIOSExample',
module: require('./ActivityIndicatorIOSExample'),
@@ -76,10 +69,6 @@ const ComponentExamples: Array<UIExplorerExample> = [
key: 'NavigatorIOSExample',
module: require('./NavigatorIOSExample'),
},
{
key: 'PickerExample',
module: require('./PickerExample'),
},
{
key: 'PickerIOSExample',
module: require('./PickerIOSExample'),
@@ -142,7 +131,7 @@ const ComponentExamples: Array<UIExplorerExample> = [
},
];
const APIExamples: Array<UIExplorerExample> = [
var APIExamples: Array<UIExplorerExample> = [
{
key: 'AccessibilityIOSExample',
module: require('./AccessibilityIOSExample'),
@@ -155,10 +144,6 @@ const APIExamples: Array<UIExplorerExample> = [
key: 'AdSupportIOSExample',
module: require('./AdSupportIOSExample'),
},
{
key: 'AlertExample',
module: require('./AlertExample').AlertExample,
},
{
key: 'AlertIOSExample',
module: require('./AlertIOSExample'),
@@ -207,10 +192,6 @@ const APIExamples: Array<UIExplorerExample> = [
key: 'ImageEditingExample',
module: require('./ImageEditingExample'),
},
{
key: 'LayoutAnimationExample',
module: require('./LayoutAnimationExample'),
},
{
key: 'LayoutExample',
module: require('./LayoutExample'),
@@ -223,6 +204,10 @@ const APIExamples: Array<UIExplorerExample> = [
key: 'NavigationExperimentalExample',
module: require('./NavigationExperimental/NavigationExperimentalExample'),
},
{
key: 'NavigationExperimentalLegacyNavigatorExample',
module: require('./NavigationExperimental/LegacyNavigator/LegacyNavigatorExample'),
},
{
key: 'NetInfoExample',
module: require('./NetInfoExample'),
@@ -263,10 +248,6 @@ const APIExamples: Array<UIExplorerExample> = [
key: 'VibrationExample',
module: require('./VibrationExample'),
},
{
key: 'WebSocketExample',
module: require('./WebSocketExample'),
},
{
key: 'XHRExample',
module: require('./XHRExample.ios'),
@@ -133,4 +133,52 @@ RCTAssertEqualSizes(a.size, b.size); \
RCTAssertEqualRects(expected, result);
}
- (void)testPlaceholderImage
{
CGSize size = {45, 22};
CGFloat expectedScale = 1.0;
UIImage *image = RCTGetPlaceholderImage(size, nil);
RCTAssertEqualSizes(size, image.size);
XCTAssertEqual(expectedScale, image.scale);
}
- (void)testPlaceholderNonintegralSize
{
CGSize size = {3.0/2, 7.0/3};
CGFloat expectedScale = 6;
CGSize pixelSize = {
round(size.width * expectedScale),
round(size.height * expectedScale)
};
UIImage *image = RCTGetPlaceholderImage(size, nil);
RCTAssertEqualSizes(size, image.size);
XCTAssertEqual(pixelSize.width, CGImageGetWidth(image.CGImage));
XCTAssertEqual(pixelSize.height, CGImageGetHeight(image.CGImage));
XCTAssertEqual(expectedScale, image.scale);
}
- (void)testPlaceholderSquareImage
{
CGSize size = {333, 333};
CGFloat expectedScale = 1.0/333;
CGSize pixelSize = {1, 1};
UIImage *image = RCTGetPlaceholderImage(size, nil);
RCTAssertEqualSizes(size, image.size);
XCTAssertEqual(pixelSize.width, CGImageGetWidth(image.CGImage));
XCTAssertEqual(pixelSize.height, CGImageGetHeight(image.CGImage));
XCTAssertEqual(expectedScale, image.scale);
}
- (void)testPlaceholderNonsquareImage
{
CGSize size = {640, 480};
CGFloat expectedScale = 1.0/160;
CGSize pixelSize = {4, 3};
UIImage *image = RCTGetPlaceholderImage(size, nil);
RCTAssertEqualSizes(size, image.size);
XCTAssertEqual(pixelSize.width, CGImageGetWidth(image.CGImage));
XCTAssertEqual(pixelSize.height, CGImageGetHeight(image.CGImage));
XCTAssertEqual(expectedScale, image.scale);
}
@end
@@ -19,6 +19,8 @@
#import "RCTJSCExecutor.h"
#import "RCTUtils.h"
#define RUN_PERF_TESTS 0
@interface RCTJSCExecutorTests : XCTestCase
@end
@@ -47,4 +49,164 @@
[_executor invalidate];
}
#if RUN_PERF_TESTS
static uint64_t _get_time_nanoseconds(void)
{
static struct mach_timebase_info tb_info = {0, 0};
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
int ret = mach_timebase_info(&tb_info);
assert(0 == ret);
});
return (mach_absolute_time() * tb_info.numer) / tb_info.denom;
}
- (void)testDeserializationPerf
{
// This test case checks the assumption that deserializing objects from JavaScript
// values one-by-one via ObjC JSC API is slower than using JSON string
// You might want to switch your tests schema to "Release" build configuration
JSContextGroupRef group = JSContextGroupCreate();
JSGlobalContextRef context = JSGlobalContextCreateInGroup(group, 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;
JSValueRef value = JSEvaluateScript(context, script, NULL, NULL, 0, &error);
XCTAssertTrue(error == NULL);
id obj;
uint64_t start = _get_time_nanoseconds();
for (int i = 0; i < 10000; i++) {
JSStringRef jsonJSString = JSValueCreateJSONString(context, value, 0, nil);
NSString *jsonString = (__bridge NSString *)JSStringCopyCFString(kCFAllocatorDefault, jsonJSString);
JSStringRelease(jsonJSString);
obj = RCTJSONParse(jsonString, NULL);
}
NSLog(@"JSON Parse time: %.2fms", (_get_time_nanoseconds() - start) / 1000000.0);
JSStringRelease(script);
JSGlobalContextRelease(context);
JSContextGroupRelease(group);
}
- (void)testJavaScriptCallSpeed
{
/**
* Since we almost don't change the RCTJSCExecutor logic, and this test is
* very likely to become flaky (specially across different devices) leave it
* to be run manually
*
* Previous Values: If you change the executor code, you should update this values
*/
int const runs = 4e5;
int const frequency = 10;
double const threshold = 0.1;
static double const expectedTimes[] = {
0x1.6199943826cf1p+13,
0x1.a3bc0a81551c3p+13,
0x1.d49fbb8602fe3p+13,
0x1.d1f64085ecb7bp+13,
};
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSString *script = @" \
var modules = { \
module: { \
method: function () { \
return true; \
} \
} \
}; \
var Bridge = { \
callFunctionReturnFlushedQueue: function(module, method, args) { \
modules[module].apply(modules[module], args); \
} \
}; \
function require(module) { \
return Bridge; \
} \
";
[_executor executeApplicationScript:[script dataUsingEncoding:NSUTF8StringEncoding] sourceURL:[NSURL URLWithString:@"http://localhost:8081/"] onComplete:^(__unused NSError *error) {
NSMutableArray *params = [NSMutableArray new];
id data = @1;
for (int i = 0; i < 4; i++) {
double samples[runs / frequency];
int size = runs / frequency;
double total = 0;
for (int j = 0; j < runs; j++) {
@autoreleasepool {
double start = _get_time_nanoseconds();
[_executor callFunctionOnModule:@"module"
method:@"method"
arguments:params
callback:^(__unused id json, __unused NSError *unused) {
}];
double run = _get_time_nanoseconds() - start;
if ((j % frequency) == frequency - 1) { // Warmup
total += run;
samples[j/frequency] = run;
}
}
}
double mean = total / size;
double variance = 0;
for (int j = 0; j < size; j++) {
variance += pow(samples[j] - mean, 2);
}
variance /= size;
double standardDeviation = sqrt(variance);
double marginOfError = standardDeviation * 1.645;
double lower = mean - marginOfError;
double upper = mean + marginOfError;
int s = 0;
total = 0;
for (int j = 0; j < size; j++) {
double v = samples[j];
if (v >= lower && v <= upper) {
samples[s++] = v;
total += v;
}
}
mean = total / s;
lower = mean * (1.0 - threshold);
upper = mean * (1.0 + threshold);
double expected = expectedTimes[i];
NSLog(@"Previous: %lf, New: %f -> %a", expected, mean, mean);
if (upper < expected) {
NSLog(@"You made JS calls with %d argument(s) %.2lf%% faster :) - Remember to update the tests with the new value: %a",
i, (1 - (double)mean / expected) * 100, mean);
}
XCTAssertTrue(lower < expected, @"You made JS calls with %d argument(s) %.2lf%% slower :( - If that's *really* necessary, update the tests with the new value: %a",
i, ((double)mean / expected - 1) * 100, mean);
[params addObject:data];
}
dispatch_semaphore_signal(semaphore);
}];
while (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW)) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]];
}
}
#endif
@end
@@ -221,16 +221,10 @@ RCT_EXPORT_MODULE()
- (void)testCustomSetBridgeModuleInitializedAtBridgeStartup
{
XCTAssertFalse(_customSetBridgeModuleNotificationSent);
__block RCTTestCustomSetBridgeModule *module;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
module = [_bridge moduleForClass:[RCTTestCustomSetBridgeModule class]];
});
RUN_RUNLOOP_WHILE(!module);
RUN_RUNLOOP_WHILE(!_customSetBridgeModuleNotificationSent);
XCTAssertTrue(_customSetBridgeModuleNotificationSent);
XCTAssertFalse(module.setBridgeOnMainThread);
RCTTestCustomSetBridgeModule *module = [_bridge moduleForClass:[RCTTestCustomSetBridgeModule class]];
XCTAssertTrue(module.setBridgeOnMainThread);
XCTAssertEqual(module.bridge, _bridge.batchedBridge);
XCTAssertNotNil(module.methodQueue);
}
+1
View File
@@ -46,6 +46,7 @@ var ViewBorderStyleExample = React.createClass({
<View>
<View style={{
borderWidth: 1,
borderRadius: 5,
borderStyle: this.state.showBorder ? 'dashed' : null,
padding: 5
}}>
@@ -109,7 +109,6 @@ var ViewPagerAndroidExample = React.createClass({
return {
page: 0,
animationsAreEnabled: true,
scrollEnabled: true,
progress: {
position: 0,
offset: 0,
@@ -168,7 +167,6 @@ var ViewPagerAndroidExample = React.createClass({
<ViewPagerAndroid
style={styles.viewPager}
initialPage={0}
scrollEnabled={this.state.scrollEnabled}
onPageScroll={this.onPageScroll}
onPageSelected={this.onPageSelected}
onPageScrollStateChanged={this.onPageScrollStateChanged}
@@ -176,13 +174,6 @@ var ViewPagerAndroidExample = React.createClass({
ref={viewPager => { this.viewPager = viewPager; }}>
{pages}
</ViewPagerAndroid>
<View style={styles.buttons}>
<Button
enabled={true}
text={this.state.scrollEnabled ? 'Scroll Enabled' : 'Scroll Disabled'}
onPress={() => this.setState({scrollEnabled: !this.state.scrollEnabled})}
/>
</View>
<View style={styles.buttons}>
{ animationsAreEnabled ?
<Button
-286
View File
@@ -1,286 +0,0 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* 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.
*
* @flow
*/
'use strict';
/* eslint-env browser */
const React = require('react');
const ReactNative = require('react-native');
const {
PixelRatio,
StyleSheet,
Text,
TextInput,
TouchableOpacity,
View,
} = ReactNative;
const DEFAULT_WS_URL = 'ws://localhost:5555/';
const WS_EVENTS = [
'close',
'error',
'message',
'open',
];
const WS_STATES = [
/* 0 */ 'CONNECTING',
/* 1 */ 'OPEN',
/* 2 */ 'CLOSING',
/* 3 */ 'CLOSED',
];
class Button extends React.Component {
render(): ReactElement {
const label = <Text style={styles.buttonLabel}>{this.props.label}</Text>;
if (this.props.disabled) {
return (
<View style={[styles.button, styles.disabledButton]}>
{label}
</View>
);
}
return (
<TouchableOpacity
onPress={this.props.onPress}
style={styles.button}>
{label}
</TouchableOpacity>
);
}
}
class Row extends React.Component {
render(): ReactElement {
return (
<View style={styles.row}>
<Text>{this.props.label}</Text>
<Text>{this.props.value}</Text>
</View>
);
}
}
function showValue(value) {
if (value === undefined || value === null) {
return '(no value)';
}
console.log('typeof Uint8Array', typeof Uint8Array);
if (typeof ArrayBuffer !== 'undefined' &&
typeof Uint8Array !== 'undefined' &&
value instanceof ArrayBuffer) {
return `ArrayBuffer {${Array.from(new Uint8Array(value))}}`;
}
return value;
}
type State = {
url: string;
socket: ?WebSocket;
socketState: ?number;
lastSocketEvent: ?string;
lastMessage: ?string | ?ArrayBuffer;
outgoingMessage: string;
};
class WebSocketExample extends React.Component<any, any, State> {
static title = 'WebSocket';
static description = 'WebSocket API';
state: State = {
url: DEFAULT_WS_URL,
socket: null,
socketState: null,
lastSocketEvent: null,
lastMessage: null,
outgoingMessage: '',
};
_connect = () => {
const socket = new WebSocket(this.state.url);
WS_EVENTS.forEach(ev => socket.addEventListener(ev, this._onSocketEvent));
this.setState({
socket,
socketState: socket.readyState,
});
};
_disconnect = () => {
if (!this.state.socket) {
return;
}
this.state.socket.close();
};
// Ideally this would be a MessageEvent, but Flow's definition
// doesn't inherit from Event, so it's 'any' for now.
// See https://github.com/facebook/flow/issues/1654.
_onSocketEvent = (event: any) => {
const state: any = {
socketState: event.target.readyState,
lastSocketEvent: event.type,
};
if (event.type === 'message') {
state.lastMessage = event.data;
}
this.setState(state);
};
_sendText = () => {
if (!this.state.socket) {
return;
}
this.state.socket.send(this.state.outgoingMessage);
this.setState({outgoingMessage: ''});
};
_sendBinary = () => {
if (!this.state.socket ||
typeof ArrayBuffer === 'undefined' ||
typeof Uint8Array === 'undefined') {
return;
}
const {outgoingMessage} = this.state;
const buffer = new Uint8Array(outgoingMessage.length);
for (let i = 0; i < outgoingMessage.length; i++) {
buffer[i] = outgoingMessage.charCodeAt(i);
}
this.state.socket.send(buffer);
this.setState({outgoingMessage: ''});
};
render(): ReactElement {
const socketState = WS_STATES[this.state.socketState || -1];
const canConnect =
!this.state.socket ||
this.state.socket.readyState >= WebSocket.CLOSING;
const canSend = !!this.state.socket;
return (
<View style={styles.container}>
<View style={styles.note}>
<Text>Pro tip:</Text>
<Text style={styles.monospace}>
node Examples/UIExplorer/websocket_test_server.js
</Text>
<Text>
{' in the '}
<Text style={styles.monospace}>react-native</Text>
{' directory starts a test server.'}
</Text>
</View>
<Row
label="Current WebSocket state"
value={showValue(socketState)}
/>
<Row
label="Last WebSocket event"
value={showValue(this.state.lastSocketEvent)}
/>
<Row
label="Last message received"
value={showValue(this.state.lastMessage)}
/>
<TextInput
style={styles.textInput}
autoCorrect={false}
placeholder="Server URL..."
onChangeText={(url) => this.setState({url})}
value={this.state.url}
/>
<Button
onPress={this._connect}
label="Connect"
disabled={!canConnect}
/>
<Button
onPress={this._disconnect}
label="Disconnect"
disabled={canConnect}
/>
<TextInput
style={styles.textInput}
autoCorrect={false}
placeholder="Type message here..."
onChangeText={(outgoingMessage) => this.setState({outgoingMessage})}
value={this.state.outgoingMessage}
/>
<View style={styles.buttonRow}>
<Button
onPress={this._sendText}
label="Send as text"
disabled={!canSend}
/>
<Button
onPress={this._sendBinary}
label="Send as binary"
disabled={!canSend}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
note: {
padding: 8,
margin: 4,
backgroundColor: 'white',
},
monospace: {
fontFamily: 'courier',
fontSize: 11,
},
row: {
height: 40,
padding: 4,
backgroundColor: 'white',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
borderBottomWidth: 1 / PixelRatio.get(),
borderColor: 'grey',
},
button: {
margin: 8,
padding: 8,
borderRadius: 4,
backgroundColor: 'blue',
alignSelf: 'center',
},
disabledButton: {
opacity: 0.5,
},
buttonLabel: {
color: 'white',
},
buttonRow: {
flexDirection: 'row',
justifyContent: 'center',
},
textInput: {
height: 40,
backgroundColor: 'white',
margin: 8,
padding: 8,
},
});
module.exports = WebSocketExample;
-20
View File
@@ -22,7 +22,6 @@ var {
Text,
TouchableHighlight,
View,
WebView,
} = ReactNative;
var RCTNetworking = require('RCTNetworking');
@@ -46,7 +45,6 @@ class XHRExampleCookies extends React.Component {
var url = `https://${domain}/cookies/set?a=${a}&b=${b}`;
fetch(url).then((response) => {
this.setStatus(`Cookies a=${a}, b=${b} set`);
this.refreshWebview();
});
this.setState({
@@ -61,7 +59,6 @@ class XHRExampleCookies extends React.Component {
return response.json();
}).then((data) => {
this.setStatus(`Got cookies ${JSON.stringify(data.cookies)} from server`);
this.refreshWebview();
});
this.setStatus('Getting cookies...');
@@ -70,14 +67,9 @@ class XHRExampleCookies extends React.Component {
clearCookies() {
RCTNetworking.clearCookies((cleared) => {
this.setStatus('Cookies cleared, had cookies=' + cleared);
this.refreshWebview();
});
}
refreshWebview() {
this.refs.webview.reload();
}
setStatus(status: string) {
this.setState({status});
}
@@ -121,18 +113,6 @@ class XHRExampleCookies extends React.Component {
</View>
</TouchableHighlight>
<Text>{this.state.status}</Text>
<TouchableHighlight
style={styles.wrapper}
onPress={this.refreshWebview.bind(this)}>
<View style={styles.button}>
<Text>Refresh Webview</Text>
</View>
</TouchableHighlight>
<WebView
ref="webview"
source={{uri: 'http://httpbin.org/cookies'}}
style={{height: 100}}
/>
</View>
);
}
@@ -1,43 +0,0 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* 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.
*
* @flow
*/
'use strict';
/* eslint-env node */
const WebSocket = require('ws');
console.log(`\
Test server for WebSocketExample
This will send each incoming message right back to the other side.a
Restart with the '--binary' command line flag to have it respond with an
ArrayBuffer instead of a string.
`);
const respondWithBinary = process.argv.indexOf('--binary') !== -1;
const server = new WebSocket.Server({port: 5555});
server.on('connection', (ws) => {
ws.on('message', (message) => {
console.log('Received message:', message);
if (respondWithBinary) {
message = new Buffer(message);
}
ws.send(message);
});
console.log('Incoming connection!');
ws.send('Why hello there!');
});
+1 -1
View File
@@ -23,7 +23,7 @@ var merge = require('merge');
// Diff Helpers
function arrayDiffer(a, b) {
if (a == null || b == null) {
if (a == null) {
return true;
}
if (a.length !== b.length) {
@@ -69,7 +69,6 @@ class Animated {
__getNativeConfig(): Object {
throw new Error('This JS animated node type cannot be used as native animated node');
}
toJSON(): any { return this.__getValue(); }
}
type AnimationConfig = {
@@ -83,7 +82,7 @@ type AnimationConfig = {
class Animation {
__active: bool;
__isInteraction: bool;
__nativeId: number;
__nativeTag: number;
__onEnd: ?EndCallback;
start(
fromValue: number,
@@ -92,11 +91,7 @@ class Animation {
previousAnimation: ?Animation,
animatedValue: AnimatedValue
): void {}
stop(): void {
if (this.__nativeId) {
NativeAnimatedAPI.stopAnimation(this.__nativeId);
}
}
stop(): void {}
_getNativeAnimationConfig(): any {
// Subclasses that have corresponding animation implementation done in native
// should override this method
@@ -110,9 +105,9 @@ class Animation {
}
__startNativeAnimation(animatedValue: AnimatedValue): void {
animatedValue.__makeNative();
this.__nativeId = NativeAnimatedHelper.generateNewAnimationId();
this.__nativeTag = NativeAnimatedHelper.generateNewAnimationTag();
NativeAnimatedAPI.startAnimatingNode(
this.__nativeId,
this.__nativeTag,
animatedValue.__getNativeTag(),
this._getNativeAnimationConfig(),
this.__debouncedOnEnd.bind(this)
@@ -316,7 +311,6 @@ class TimingAnimation extends Animation {
}
stop(): void {
super.stop();
this.__active = false;
clearTimeout(this._timeout);
window.cancelAnimationFrame(this._animationFrame);
@@ -387,7 +381,6 @@ class DecayAnimation extends Animation {
}
stop(): void {
super.stop();
this.__active = false;
window.cancelAnimationFrame(this._animationFrame);
this.__debouncedOnEnd({finished: false});
@@ -602,7 +595,6 @@ class SpringAnimation extends Animation {
}
stop(): void {
super.stop();
this.__active = false;
window.cancelAnimationFrame(this._animationFrame);
this.__debouncedOnEnd({finished: false});
@@ -653,7 +645,7 @@ class AnimatedValue extends AnimatedWithChildren {
this._animation.stop();
this._animation = null;
}
this._updateValue(value, !this.__isNative /* don't perform a flush for natively driven values */);
this._updateValue(value);
if (this.__isNative) {
NativeAnimatedAPI.setAnimatedNodeValue(this.__getNativeTag(), value);
}
@@ -713,7 +705,7 @@ class AnimatedValue extends AnimatedWithChildren {
* 0-10.
*/
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
return new AnimatedInterpolation(this, config);
return new AnimatedInterpolation(this, Interpolation.create(config));
}
/**
@@ -731,9 +723,7 @@ class AnimatedValue extends AnimatedWithChildren {
animation.start(
this._value,
(value) => {
// Natively driven animations will never call into that callback, therefore we can always pass `flush = true`
// to allow the updated value to propagate to native with `setNativeProps`
this._updateValue(value, true /* flush */);
this._updateValue(value);
},
(result) => {
this._animation = null;
@@ -763,11 +753,9 @@ class AnimatedValue extends AnimatedWithChildren {
this._tracking = tracking;
}
_updateValue(value: number, flush: bool): void {
_updateValue(value: number): void {
this._value = value;
if (flush) {
_flush(this);
}
_flush(this);
for (var key in this._listeners) {
this._listeners[key]({value: this.__getValue()});
}
@@ -924,14 +912,12 @@ class AnimatedValueXY extends AnimatedWithChildren {
class AnimatedInterpolation extends AnimatedWithChildren {
_parent: Animated;
_config: InterpolationConfigType;
_interpolation: (input: number) => number | string;
constructor(parent: Animated, config: InterpolationConfigType) {
constructor(parent: Animated, interpolation: (input: number) => number | string) {
super();
this._parent = parent;
this._config = config;
this._interpolation = Interpolation.create(config);
this._interpolation = interpolation;
}
__getValue(): number | string {
@@ -944,7 +930,7 @@ class AnimatedInterpolation extends AnimatedWithChildren {
}
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
return new AnimatedInterpolation(this, config);
return new AnimatedInterpolation(this, Interpolation.create(config));
}
__attach(): void {
@@ -953,15 +939,6 @@ class AnimatedInterpolation extends AnimatedWithChildren {
__detach(): void {
this._parent.__removeChild(this);
super.__detach();
}
__getNativeConfig(): any {
NativeAnimatedHelper.validateInterpolation(this._config);
return {
...this._config,
type: 'interpolation',
};
}
}
@@ -975,18 +952,12 @@ class AnimatedAddition extends AnimatedWithChildren {
this._b = typeof b === 'number' ? new AnimatedValue(b) : b;
}
__makeNative() {
this._a.__makeNative();
this._b.__makeNative();
super.__makeNative();
}
__getValue(): number {
return this._a.__getValue() + this._b.__getValue();
}
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
return new AnimatedInterpolation(this, config);
return new AnimatedInterpolation(this, Interpolation.create(config));
}
__attach(): void {
@@ -997,14 +968,6 @@ class AnimatedAddition extends AnimatedWithChildren {
__detach(): void {
this._a.__removeChild(this);
this._b.__removeChild(this);
super.__detach();
}
__getNativeConfig(): any {
return {
type: 'addition',
input: [this._a.__getNativeTag(), this._b.__getNativeTag()],
};
}
}
@@ -1018,18 +981,12 @@ class AnimatedMultiplication extends AnimatedWithChildren {
this._b = typeof b === 'number' ? new AnimatedValue(b) : b;
}
__makeNative() {
super.__makeNative();
this._a.__makeNative();
this._b.__makeNative();
}
__getValue(): number {
return this._a.__getValue() * this._b.__getValue();
}
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
return new AnimatedInterpolation(this, config);
return new AnimatedInterpolation(this, Interpolation.create(config));
}
__attach(): void {
@@ -1040,14 +997,6 @@ class AnimatedMultiplication extends AnimatedWithChildren {
__detach(): void {
this._a.__removeChild(this);
this._b.__removeChild(this);
super.__detach();
}
__getNativeConfig(): any {
return {
type: 'multiplication',
input: [this._a.__getNativeTag(), this._b.__getNativeTag()],
};
}
}
@@ -1066,7 +1015,7 @@ class AnimatedModulo extends AnimatedWithChildren {
}
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
return new AnimatedInterpolation(this, config);
return new AnimatedInterpolation(this, Interpolation.create(config));
}
__attach(): void {
@@ -1445,7 +1394,7 @@ function createAnimatedComponent(Component: any): any {
for (var key in ViewStylePropTypes) {
if (!Component.propTypes[key] && props[key] !== undefined) {
console.warn(
console.error(
'You are setting the style `{ ' + key + ': ... }` as a prop. You ' +
'should nest it in a style object. ' +
'E.g. `{ style: { ' + key + ': ... } }`'
+1 -1
View File
@@ -233,7 +233,7 @@ function createInterpolationFromStringOutputRange(
// 'rgba(${interpolations[0](input)}, ${interpolations[1](input)}, ...'
return outputRange[0].replace(stringShapeRegex, () => {
const val = +interpolations[i++](input);
const rounded = shouldRound && i < 4 ? Math.round(val) : Math.round(val * 1000) / 1000;
const rounded = shouldRound && i < 4 ? Math.round(val) : val;
return String(rounded);
});
};
+6 -23
View File
@@ -16,7 +16,7 @@ var NativeAnimatedModule = require('NativeModules').NativeAnimatedModule;
var invariant = require('fbjs/lib/invariant');
var __nativeAnimatedNodeTagCount = 1; /* used for animated nodes */
var __nativeAnimationIdCount = 1; /* used for started animations */
var __nativeAnimationTagCount = 1; /* used for started animations */
type EndResult = {finished: bool};
type EndCallback = (result: EndResult) => void;
@@ -38,13 +38,9 @@ var API = {
assertNativeAnimatedModule();
NativeAnimatedModule.disconnectAnimatedNodes(parentTag, childTag);
},
startAnimatingNode: function(animationId: number, nodeTag: number, config: Object, endCallback: EndCallback): void {
startAnimatingNode: function(animationTag: number, nodeTag: number, config: Object, endCallback: EndCallback): void {
assertNativeAnimatedModule();
NativeAnimatedModule.startAnimatingNode(animationId, nodeTag, config, endCallback);
},
stopAnimation: function(animationId: number) {
assertNativeAnimatedModule();
NativeAnimatedModule.stopAnimation(animationId);
NativeAnimatedModule.startAnimatingNode(nodeTag, config, endCallback);
},
setAnimatedNodeValue: function(nodeTag: number, value: number): void {
assertNativeAnimatedModule();
@@ -101,24 +97,12 @@ function validateStyles(styles: Object): void {
}
}
function validateInterpolation(config: Object): void {
var SUPPORTED_INTERPOLATION_PARAMS = {
inputRange: true,
outputRange: true,
};
for (var key in config) {
if (!SUPPORTED_INTERPOLATION_PARAMS.hasOwnProperty(key)) {
throw new Error(`Interpolation property '${key}' is not supported by native animated module`);
}
}
}
function generateNewNodeTag(): number {
return __nativeAnimatedNodeTagCount++;
}
function generateNewAnimationId(): number {
return __nativeAnimationIdCount++;
function generateNewAnimationTag(): number {
return __nativeAnimationTagCount++;
}
function assertNativeAnimatedModule(): void {
@@ -129,8 +113,7 @@ module.exports = {
API,
validateProps,
validateStyles,
validateInterpolation,
generateNewNodeTag,
generateNewAnimationId,
generateNewAnimationTag,
assertNativeAnimatedModule,
};
@@ -9,7 +9,7 @@
'use strict';
jest
.disableAutomock()
.autoMockOff()
.setMock('Text', {})
.setMock('View', {})
.setMock('Image', {})
@@ -22,7 +22,7 @@ describe('Animated', () => {
it('works end to end', () => {
var anim = new Animated.Value(0);
var callback = jest.fn();
var callback = jest.genMockFunction();
var node = new Animated.__PropsOnlyForTests({
style: {
@@ -75,7 +75,7 @@ describe('Animated', () => {
it('does not detach on updates', () => {
var anim = new Animated.Value(0);
anim.__detach = jest.fn();
anim.__detach = jest.genMockFunction();
var c = new Animated.View();
c.props = {
@@ -101,11 +101,11 @@ describe('Animated', () => {
it('stops animation when detached', () => {
// jest environment doesn't have cancelAnimationFrame :(
if (!window.cancelAnimationFrame) {
window.cancelAnimationFrame = jest.fn();
window.cancelAnimationFrame = jest.genMockFunction();
}
var anim = new Animated.Value(0);
var callback = jest.fn();
var callback = jest.genMockFunction();
var c = new Animated.View();
c.props = {
@@ -125,14 +125,14 @@ describe('Animated', () => {
it('triggers callback when spring is at rest', () => {
var anim = new Animated.Value(0);
var callback = jest.fn();
var callback = jest.genMockFunction();
Animated.spring(anim, {toValue: 0, velocity: 0}).start(callback);
expect(callback).toBeCalled();
});
it('send toValue when a spring stops', () => {
var anim = new Animated.Value(0);
var listener = jest.fn();
var listener = jest.genMockFunction();
anim.addListener(listener);
Animated.spring(anim, {toValue: 15}).start();
jest.runAllTimers();
@@ -142,24 +142,21 @@ describe('Animated', () => {
expect(anim.__getValue()).toBe(15);
});
it('convert to JSON', () => {
expect(JSON.stringify(new Animated.Value(10))).toBe('10');
});
});
describe('Animated Sequence', () => {
it('works with an empty sequence', () => {
var cb = jest.fn();
var cb = jest.genMockFunction();
Animated.sequence([]).start(cb);
expect(cb).toBeCalledWith({finished: true});
});
it('sequences well', () => {
var anim1 = {start: jest.fn()};
var anim2 = {start: jest.fn()};
var cb = jest.fn();
var anim1 = {start: jest.genMockFunction()};
var anim2 = {start: jest.genMockFunction()};
var cb = jest.genMockFunction();
var seq = Animated.sequence([anim1, anim2]);
@@ -182,9 +179,9 @@ describe('Animated Sequence', () => {
});
it('supports interrupting sequence', () => {
var anim1 = {start: jest.fn()};
var anim2 = {start: jest.fn()};
var cb = jest.fn();
var anim1 = {start: jest.genMockFunction()};
var anim2 = {start: jest.genMockFunction()};
var cb = jest.genMockFunction();
Animated.sequence([anim1, anim2]).start(cb);
@@ -196,9 +193,9 @@ describe('Animated Sequence', () => {
});
it('supports stopping sequence', () => {
var anim1 = {start: jest.fn(), stop: jest.fn()};
var anim2 = {start: jest.fn(), stop: jest.fn()};
var cb = jest.fn();
var anim1 = {start: jest.genMockFunction(), stop: jest.genMockFunction()};
var anim2 = {start: jest.genMockFunction(), stop: jest.genMockFunction()};
var cb = jest.genMockFunction();
var seq = Animated.sequence([anim1, anim2]);
seq.start(cb);
@@ -218,14 +215,14 @@ describe('Animated Sequence', () => {
describe('Animated Parallel', () => {
it('works with an empty parallel', () => {
var cb = jest.fn();
var cb = jest.genMockFunction();
Animated.parallel([]).start(cb);
expect(cb).toBeCalledWith({finished: true});
});
it('works with an empty element in array', () => {
var anim1 = {start: jest.fn()};
var cb = jest.fn();
var anim1 = {start: jest.genMockFunction()};
var cb = jest.genMockFunction();
Animated.parallel([null, anim1]).start(cb);
expect(anim1.start).toBeCalled();
@@ -235,9 +232,9 @@ describe('Animated Parallel', () => {
});
it('parellelizes well', () => {
var anim1 = {start: jest.fn()};
var anim2 = {start: jest.fn()};
var cb = jest.fn();
var anim1 = {start: jest.genMockFunction()};
var anim2 = {start: jest.genMockFunction()};
var cb = jest.genMockFunction();
var par = Animated.parallel([anim1, anim2]);
@@ -258,9 +255,9 @@ describe('Animated Parallel', () => {
});
it('supports stopping parallel', () => {
var anim1 = {start: jest.fn(), stop: jest.fn()};
var anim2 = {start: jest.fn(), stop: jest.fn()};
var cb = jest.fn();
var anim1 = {start: jest.genMockFunction(), stop: jest.genMockFunction()};
var anim2 = {start: jest.genMockFunction(), stop: jest.genMockFunction()};
var cb = jest.genMockFunction();
var seq = Animated.parallel([anim1, anim2]);
seq.start(cb);
@@ -279,10 +276,10 @@ describe('Animated Parallel', () => {
it('does not call stop more than once when stopping', () => {
var anim1 = {start: jest.fn(), stop: jest.fn()};
var anim2 = {start: jest.fn(), stop: jest.fn()};
var anim3 = {start: jest.fn(), stop: jest.fn()};
var cb = jest.fn();
var anim1 = {start: jest.genMockFunction(), stop: jest.genMockFunction()};
var anim2 = {start: jest.genMockFunction(), stop: jest.genMockFunction()};
var anim3 = {start: jest.genMockFunction(), stop: jest.genMockFunction()};
var cb = jest.genMockFunction();
var seq = Animated.parallel([anim1, anim2, anim3]);
seq.start(cb);
@@ -309,8 +306,8 @@ describe('Animated Parallel', () => {
describe('Animated delays', () => {
it('should call anim after delay in sequence', () => {
var anim = {start: jest.fn(), stop: jest.fn()};
var cb = jest.fn();
var anim = {start: jest.genMockFunction(), stop: jest.genMockFunction()};
var cb = jest.genMockFunction();
Animated.sequence([
Animated.delay(1000),
anim,
@@ -322,7 +319,7 @@ describe('Animated delays', () => {
expect(cb).toBeCalledWith({finished: true});
});
it('should run stagger to end', () => {
var cb = jest.fn();
var cb = jest.genMockFunction();
Animated.stagger(1000, [
Animated.delay(1000),
Animated.delay(1000),
@@ -344,7 +341,7 @@ describe('Animated Events', () => {
});
it('should call listeners', () => {
var value = new Animated.Value(0);
var listener = jest.fn();
var listener = jest.genMockFunction();
var handler = Animated.event(
[{foo: value}],
{listener},
@@ -369,14 +366,14 @@ describe('Animated Interactions', () => {
});
afterEach(()=> {
jest.unmock('InteractionManager');
jest.dontMock('InteractionManager');
});
it('registers an interaction by default', () => {
InteractionManager.createInteractionHandle.mockReturnValue(777);
var value = new Animated.Value(0);
var callback = jest.fn();
var callback = jest.genMockFunction();
Animated.timing(value, {
toValue: 100,
duration: 100,
@@ -390,7 +387,7 @@ describe('Animated Interactions', () => {
it('does not register an interaction when specified', () => {
var value = new Animated.Value(0);
var callback = jest.fn();
var callback = jest.genMockFunction();
Animated.timing(value, {
toValue: 100,
duration: 100,
@@ -454,7 +451,7 @@ describe('Animated Vectors', () => {
it('should animate vectors', () => {
var vec = new Animated.ValueXY();
var callback = jest.fn();
var callback = jest.genMockFunction();
var node = new Animated.__PropsOnlyForTests({
style: {
@@ -539,7 +536,7 @@ describe('Animated Vectors', () => {
describe('Animated Listeners', () => {
it('should get updates', () => {
var value1 = new Animated.Value(0);
var listener = jest.fn();
var listener = jest.genMockFunction();
var id = value1.addListener(listener);
value1.setValue(42);
expect(listener.mock.calls.length).toBe(1);
@@ -557,7 +554,7 @@ describe('Animated Listeners', () => {
it('should removeAll', () => {
var value1 = new Animated.Value(0);
var listener = jest.fn();
var listener = jest.genMockFunction();
[1,2,3,4].forEach(() => value1.addListener(listener));
value1.setValue(42);
expect(listener.mock.calls.length).toBe(4);
@@ -9,7 +9,7 @@
'use strict';
jest
.disableAutomock()
.autoMockOff()
.setMock('Text', {})
.setMock('View', {})
.setMock('Image', {})
@@ -24,19 +24,18 @@ describe('Animated', () => {
beforeEach(() => {
var nativeAnimatedModule = require('NativeModules').NativeAnimatedModule;
nativeAnimatedModule.createAnimatedNode = jest.fn();
nativeAnimatedModule.connectAnimatedNodes = jest.fn();
nativeAnimatedModule.disconnectAnimatedNodes = jest.fn();
nativeAnimatedModule.startAnimatingNode = jest.fn();
nativeAnimatedModule.stopAnimation = jest.fn();
nativeAnimatedModule.setAnimatedNodeValue = jest.fn();
nativeAnimatedModule.connectAnimatedNodeToView = jest.fn();
nativeAnimatedModule.disconnectAnimatedNodeFromView = jest.fn();
nativeAnimatedModule.dropAnimatedNode = jest.fn();
nativeAnimatedModule.createAnimatedNode = jest.genMockFunction();
nativeAnimatedModule.connectAnimatedNodes = jest.genMockFunction();
nativeAnimatedModule.disconnectAnimatedNodes = jest.genMockFunction();
nativeAnimatedModule.startAnimatingNode = jest.genMockFunction();
nativeAnimatedModule.setAnimatedNodeValue = jest.genMockFunction();
nativeAnimatedModule.connectAnimatedNodeToView = jest.genMockFunction();
nativeAnimatedModule.disconnectAnimatedNodeFromView = jest.genMockFunction();
nativeAnimatedModule.dropAnimatedNode = jest.genMockFunction();
// jest environment doesn't have cancelAnimationFrame :(
if (!window.cancelAnimationFrame) {
window.cancelAnimationFrame = jest.fn();
window.cancelAnimationFrame = jest.genMockFunction();
}
});
@@ -60,7 +59,6 @@ describe('Animated', () => {
expect(nativeAnimatedModule.connectAnimatedNodes.mock.calls.length).toBe(2);
expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith(
jasmine.any(Number),
jasmine.any(Number),
{type: 'frames', frames: jasmine.any(Array), toValue: jasmine.any(Number)},
jasmine.any(Function)
@@ -92,114 +90,12 @@ describe('Animated', () => {
.toBeCalledWith(jasmine.any(Number), { type: 'props', props: { style: jasmine.any(Number) }});
});
it('sends a valid graph description for Animated.add nodes', () => {
var first = new Animated.Value(1);
var second = new Animated.Value(2);
var c = new Animated.View();
c.props = {
style: {
opacity: Animated.add(first, second),
},
};
c.componentWillMount();
Animated.timing(first, {toValue: 2, duration: 1000, useNativeDriver: true}).start();
Animated.timing(second, {toValue: 3, duration: 1000, useNativeDriver: true}).start();
var nativeAnimatedModule = require('NativeModules').NativeAnimatedModule;
expect(nativeAnimatedModule.createAnimatedNode)
.toBeCalledWith(jasmine.any(Number), { type: 'addition', input: jasmine.any(Array) });
var additionCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter(
(call) => call[1].type === 'addition'
);
expect(additionCalls.length).toBe(1);
var additionCall = additionCalls[0];
var additionNodeTag = additionCall[0];
var additionConnectionCalls = nativeAnimatedModule.connectAnimatedNodes.mock.calls.filter(
(call) => call[1] === additionNodeTag
);
expect(additionConnectionCalls.length).toBe(2);
expect(nativeAnimatedModule.createAnimatedNode)
.toBeCalledWith(additionCall[1].input[0], { type: 'value', value: 1 });
expect(nativeAnimatedModule.createAnimatedNode)
.toBeCalledWith(additionCall[1].input[1], { type: 'value', value: 2 });
});
it('sends a valid graph description for Animated.multiply nodes', () => {
var first = new Animated.Value(2);
var second = new Animated.Value(1);
var c = new Animated.View();
c.props = {
style: {
opacity: Animated.multiply(first, second),
},
};
c.componentWillMount();
Animated.timing(first, {toValue: 5, duration: 1000, useNativeDriver: true}).start();
Animated.timing(second, {toValue: -1, duration: 1000, useNativeDriver: true}).start();
var nativeAnimatedModule = require('NativeModules').NativeAnimatedModule;
expect(nativeAnimatedModule.createAnimatedNode)
.toBeCalledWith(jasmine.any(Number), { type: 'multiplication', input: jasmine.any(Array) });
var multiplicationCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter(
(call) => call[1].type === 'multiplication'
);
expect(multiplicationCalls.length).toBe(1);
var multiplicationCall = multiplicationCalls[0];
var multiplicationNodeTag = multiplicationCall[0];
var multiplicationConnectionCalls = nativeAnimatedModule.connectAnimatedNodes.mock.calls.filter(
(call) => call[1] === multiplicationNodeTag
);
expect(multiplicationConnectionCalls.length).toBe(2);
expect(nativeAnimatedModule.createAnimatedNode)
.toBeCalledWith(multiplicationCall[1].input[0], { type: 'value', value: 2 });
expect(nativeAnimatedModule.createAnimatedNode)
.toBeCalledWith(multiplicationCall[1].input[1], { type: 'value', value: 1 });
});
it('sends a valid graph description for interpolate() nodes', () => {
var node = new Animated.Value(10);
var c = new Animated.View();
c.props = {
style: {
opacity: node.interpolate({
inputRange: [10, 20],
outputRange: [0, 1],
}),
},
};
c.componentWillMount();
Animated.timing(node, {toValue: 20, duration: 1000, useNativeDriver: true}).start();
var nativeAnimatedModule = require('NativeModules').NativeAnimatedModule;
expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(jasmine.any(Number), { type: 'value', value: 10 });
expect(nativeAnimatedModule.createAnimatedNode)
.toBeCalledWith(jasmine.any(Number), {
type: 'interpolation',
inputRange: [10, 20],
outputRange: [0, 1],
});
var interpolationNodeTag = nativeAnimatedModule.createAnimatedNode.mock.calls.find(
(call) => call[1].type === 'interpolation'
)[0];
var valueNodeTag = nativeAnimatedModule.createAnimatedNode.mock.calls.find(
(call) => call[1].type === 'value'
)[0];
expect(nativeAnimatedModule.connectAnimatedNodes).toBeCalledWith(valueNodeTag, interpolationNodeTag);
});
it('sends a valid timing animation description', () => {
var anim = new Animated.Value(0);
Animated.timing(anim, {toValue: 10, duration: 1000, useNativeDriver: true}).start();
var nativeAnimatedModule = require('NativeModules').NativeAnimatedModule;
expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith(
jasmine.any(Number),
jasmine.any(Number),
{type: 'frames', frames: jasmine.any(Array), toValue: jasmine.any(Number)},
jasmine.any(Function)
@@ -210,27 +106,10 @@ describe('Animated', () => {
var anim = new Animated.Value(0);
Animated.timing(anim, {toValue: 10, duration: 1000, useNativeDriver: true}).start();
var c = new Animated.View();
c.props = {
style: {
opacity: anim,
},
};
c.componentWillMount();
// We expect `setValue` not to propagate down to `setNativeProps`, otherwise it may try to access `setNativeProps`
// via component refs table that we override here.
c.refs = {
node: {
setNativeProps: jest.genMockFunction(),
},
};
anim.setValue(0.5);
anim.setValue(5);
var nativeAnimatedModule = require('NativeModules').NativeAnimatedModule;
expect(nativeAnimatedModule.setAnimatedNodeValue).toBeCalledWith(jasmine.any(Number), 0.5);
expect(c.refs.node.setNativeProps.mock.calls.length).toBe(0);
expect(nativeAnimatedModule.setAnimatedNodeValue).toBeCalledWith(jasmine.any(Number), 5);
});
it('doesn\'t call into native API if useNativeDriver is set to false', () => {
@@ -300,25 +179,6 @@ describe('Animated', () => {
expect(animation.start).toThrowError(/left/);
});
it('fails for unsupported interpolation parameters', () => {
var anim = new Animated.Value(0);
var c = new Animated.View();
c.props = {
style: {
opacity: anim.interpolate({
inputRange: [0, 100],
outputRange: [0, 1],
extrapolate: 'clamp',
}),
},
};
c.componentWillMount();
var animation = Animated.timing(anim, {toValue: 100, duration: 50, useNativeDriver: true});
expect(animation.start).toThrowError(/extrapolate/);
});
it('works for any `static` props and styles', () => {
// Passing "unsupported" props should work just fine as long as they are not animated
var anim = new Animated.Value(0);
@@ -341,22 +201,4 @@ describe('Animated', () => {
.toBeCalledWith(jasmine.any(Number), { type: 'props', props: { style: jasmine.any(Number) }});
});
it('send stopAnimation command to native', () => {
var value = new Animated.Value(0);
var animation = Animated.timing(value, {toValue: 10, duration: 50, useNativeDriver: true});
var nativeAnimatedModule = require('NativeModules').NativeAnimatedModule;
animation.start();
expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith(
jasmine.any(Number),
jasmine.any(Number),
{type: 'frames', frames: jasmine.any(Array), toValue: jasmine.any(Number)},
jasmine.any(Function)
);
var animationId = nativeAnimatedModule.startAnimatingNode.mock.calls[0][0];
animation.stop();
expect(nativeAnimatedModule.stopAnimation).toBeCalledWith(animationId);
});
});
@@ -8,7 +8,7 @@
*/
'use strict';
jest.unmock('Easing');
jest.dontMock('Easing');
var Easing = require('Easing');
describe('Easing', () => {
@@ -9,9 +9,9 @@
'use strict';
jest
.unmock('Interpolation')
.unmock('Easing')
.unmock('normalizeColor');
.dontMock('Interpolation')
.dontMock('Easing')
.dontMock('normalizeColor');
var Interpolation = require('Interpolation');
var Easing = require('Easing');
@@ -294,14 +294,4 @@ describe('Interpolation', () => {
outputRange: ['20deg', '30rad'],
})).toThrow();
});
it('should round the alpha channel of a color to the nearest thousandth', () => {
var interpolation = Interpolation.create({
inputRange: [0, 1],
outputRange: ['rgba(0, 0, 0, 0)', 'rgba(0, 0, 0, 1)'],
});
expect(interpolation(1e-12)).toBe('rgba(0, 0, 0, 0)');
expect(interpolation(2 / 3)).toBe('rgba(0, 0, 0, 0.667)');
});
});
@@ -1,6 +1,6 @@
/* eslint-disable */
jest.unmock('bezier');
jest.dontMock('bezier');
var bezier = require('bezier');
var identity = function (x) { return x; };
+3 -7
View File
@@ -12,12 +12,10 @@
'use strict';
var BatchedBridge = require('BatchedBridge');
var BugReporting = require('BugReporting');
var ReactNative = require('ReactNative');
var invariant = require('fbjs/lib/invariant');
var renderApplication = require('renderApplication');
const infoLog = require('infoLog');
if (__DEV__) {
// In order to use Cmd+P to record/dump perf data, we need to make sure
@@ -26,7 +24,6 @@ if (__DEV__) {
}
var runnables = {};
var runCount = 1;
type ComponentProvider = () => ReactClass<any>;
@@ -82,14 +79,13 @@ var AppRegistry = {
},
runApplication: function(appKey: string, appParameters: any): void {
const msg =
console.log(
'Running application "' + appKey + '" with appParams: ' +
JSON.stringify(appParameters) + '. ' +
'__DEV__ === ' + String(__DEV__) +
', development-level warning are ' + (__DEV__ ? 'ON' : 'OFF') +
', performance optimizations are ' + (__DEV__ ? 'OFF' : 'ON');
infoLog(msg);
BugReporting.addSource('AppRegistry.runApplication' + runCount++, () => msg);
', performance optimizations are ' + (__DEV__ ? 'OFF' : 'ON')
);
invariant(
runnables[appKey] && runnables[appKey].run,
'Application ' + appKey + ' has not been registered. This ' +
+2 -2
View File
@@ -13,7 +13,8 @@
const MessageQueue = require('MessageQueue');
const BatchedBridge = new MessageQueue(
() => global.__fbBatchedBridgeConfig
__fbBatchedBridgeConfig.remoteModuleConfig,
__fbBatchedBridgeConfig.localModulesConfig,
);
// TODO: Move these around to solve the cycle in a cleaner way.
@@ -23,7 +24,6 @@ const JSTimersExecution = require('JSTimersExecution');
BatchedBridge.registerCallableModule('Systrace', Systrace);
BatchedBridge.registerCallableModule('JSTimersExecution', JSTimersExecution);
BatchedBridge.registerCallableModule('HeapCapture', require('HeapCapture'));
if (__DEV__) {
BatchedBridge.registerCallableModule('HMRClient', require('HMRClient'));
@@ -51,14 +51,65 @@ Object.keys(RemoteModules).forEach((moduleName) => {
module = config && BatchedBridge.processModuleConfig(config, module.moduleID);
RemoteModules[moduleName] = module;
}
Object.defineProperty(NativeModules, moduleName, {
configurable: true,
enumerable: true,
value: module,
});
return module;
},
});
});
/**
* Copies the ViewManager constants and commands into UIManager. This is
* only needed for iOS, which puts the constants in the ViewManager
* namespace instead of UIManager, unlike Android.
*
* We'll eventually move this logic to UIManager.js, once all
* the call sites accessing NativeModules.UIManager directly have
* been removed #9344445
*/
const UIManager = NativeModules.UIManager;
UIManager && Object.keys(UIManager).forEach(viewName => {
const viewConfig = UIManager[viewName];
if (viewConfig.Manager) {
let constants;
/* $FlowFixMe - nice try. Flow doesn't like getters */
Object.defineProperty(viewConfig, 'Constants', {
configurable: true,
enumerable: true,
get: () => {
if (constants) {
return constants;
}
constants = {};
const viewManager = NativeModules[normalizePrefix(viewConfig.Manager)];
viewManager && Object.keys(viewManager).forEach(key => {
const value = viewManager[key];
if (typeof value !== 'function') {
constants[key] = value;
}
});
return constants;
},
});
let commands;
/* $FlowFixMe - nice try. Flow doesn't like getters */
Object.defineProperty(viewConfig, 'Commands', {
configurable: true,
enumerable: true,
get: () => {
if (commands) {
return commands;
}
commands = {};
const viewManager = NativeModules[normalizePrefix(viewConfig.Manager)];
viewManager && Object.keys(viewManager).forEach((key, index) => {
const value = viewManager[key];
if (typeof value === 'function') {
commands[key] = index;
}
});
return commands;
},
});
}
});
module.exports = NativeModules;
@@ -11,15 +11,13 @@
*/
'use strict';
const BatchedBridge = require('BatchedBridge');
var BatchedBridge = require('BatchedBridge');
var ReactNativeEventEmitter = require('ReactNativeEventEmitter');
const RCTEventEmitter = {
register(eventEmitter: any) {
BatchedBridge.registerCallableModule(
'RCTEventEmitter',
eventEmitter
);
}
};
BatchedBridge.registerCallableModule(
'RCTEventEmitter',
ReactNativeEventEmitter
);
module.exports = RCTEventEmitter;
// Completely locally implemented - no native hooks.
module.exports = ReactNativeEventEmitter;
@@ -10,19 +10,21 @@ var NativeModules = {
}),
},
Timing: {
createTimer: jest.fn(),
deleteTimer: jest.fn(),
createTimer: jest.genMockFunction(),
deleteTimer: jest.genMockFunction(),
},
GraphPhotoUpload: {
upload: jest.fn(),
upload: jest.genMockFunction(),
},
FacebookSDK: {
login: jest.fn(),
logout: jest.fn(),
queryGraphPath: jest.fn((path, method, params, callback) => callback()),
login: jest.genMockFunction(),
logout: jest.genMockFunction(),
queryGraphPath: jest.genMockFunction().mockImpl(
(path, method, params, callback) => callback()
),
},
DataManager: {
queryData: jest.fn(),
queryData: jest.genMockFunction(),
},
UIManager: {
customBubblingEventTypes: {},
@@ -36,10 +38,10 @@ var NativeModules = {
},
},
AsyncLocalStorage: {
getItem: jest.fn(),
setItem: jest.fn(),
removeItem: jest.fn(),
clear: jest.fn(),
getItem: jest.genMockFunction(),
setItem: jest.genMockFunction(),
removeItem: jest.genMockFunction(),
clear: jest.genMockFunction(),
},
SourceCode: {
scriptURL: null,
@@ -50,10 +52,10 @@ var NativeModules = {
},
ModalFullscreenViewManager: {},
AlertManager: {
alertWithArgs: jest.fn(),
alertWithArgs: jest.genMockFunction(),
},
Clipboard: {
setString: jest.fn(),
setString: jest.genMockFunction(),
},
};
-105
View File
@@ -1,105 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule BugReporting
* @flow
*/
'use strict';
const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
const Map = require('Map');
const infoLog = require('infoLog');
import type EmitterSubscription from 'EmitterSubscription';
type ExtraData = { [key: string]: string };
type SourceCallback = () => string;
type DebugData = { extras: ExtraData, files: ExtraData };
function defaultExtras() {
BugReporting.addFileSource('react_hierarchy.txt', () => require('dumpReactTree')());
}
/**
* A simple class for collecting bug report data. Components can add sources that will be queried when a bug report
* is created via `collectExtraData`. For example, a list component might add a source that provides the list of rows
* that are currently visible on screen. Components should also remember to call `remove()` on the object that is
* returned by `addSource` when they are unmounted.
*/
class BugReporting {
static _extraSources: Map<string, SourceCallback> = new Map();
static _fileSources: Map<string, SourceCallback> = new Map();
static _subscription: ?EmitterSubscription = null;
static _maybeInit() {
if (!BugReporting._subscription) {
BugReporting._subscription = RCTDeviceEventEmitter
.addListener('collectBugExtraData', BugReporting.collectExtraData, null);
defaultExtras();
}
}
/**
* Maps a string key to a simple callback that should return a string payload to be attached
* to a bug report. Source callbacks are called when `collectExtraData` is called.
*
* Returns an object to remove the source when the component unmounts.
*
* Conflicts trample with a warning.
*/
static addSource(key: string, callback: SourceCallback): {remove: () => void} {
return this._addSource(key, callback, BugReporting._extraSources);
}
/**
* Maps a string key to a simple callback that should return a string payload to be attached
* to a bug report. Source callbacks are called when `collectExtraData` is called.
*
* Returns an object to remove the source when the component unmounts.
*
* Conflicts trample with a warning.
*/
static addFileSource(key: string, callback: SourceCallback): {remove: () => void} {
return this._addSource(key, callback, BugReporting._fileSources);
}
static _addSource(key: string, callback: SourceCallback, source: Map<string, SourceCallback>): {remove: () => void} {
BugReporting._maybeInit();
if (source.has(key)) {
console.warn(`BugReporting.add* called multiple times for same key '${key}'`);
}
source.set(key, callback);
return {remove: () => { source.delete(key); }};
}
/**
* This can be called from a native bug reporting flow, or from JS code.
*
* If available, this will call `NativeModules.BugReporting.setExtraData(extraData)`
* after collecting `extraData`.
*/
static collectExtraData(): DebugData {
const extraData: ExtraData = {};
for (const [key, callback] of BugReporting._extraSources) {
extraData[key] = callback();
}
const fileData: ExtraData = {};
for (const [key, callback] of BugReporting._fileSources) {
fileData[key] = callback();
}
infoLog('BugReporting extraData:', extraData);
const BugReportingNativeModule = require('NativeModules').BugReporting;
BugReportingNativeModule &&
BugReportingNativeModule.setExtraData &&
BugReportingNativeModule.setExtraData(extraData, fileData);
return { extras: extraData, files: fileData };
}
}
module.exports = BugReporting;
-142
View File
@@ -1,142 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule dumpReactTree
* @flow
*/
'use strict';
const ReactNativeMount = require('ReactNativeMount');
const getReactData = require('getReactData');
const INDENTATION_SIZE = 2;
const MAX_DEPTH = 2;
const MAX_STRING_LENGTH = 50;
/**
* Dump all React Native root views and their content. This function tries
* it best to get the content but ultimately relies on implementation details
* of React and will fail in future versions.
*/
function dumpReactTree() {
try {
return getReactTree();
} catch (e) {
return 'Failed to dump react tree: ' + e;
}
}
function getReactTree() {
let output = '';
const rootIds = Object.getOwnPropertyNames(ReactNativeMount._instancesByContainerID);
for (const rootId of rootIds) {
const instance = ReactNativeMount._instancesByContainerID[rootId];
output += `============ Root ID: ${rootId} ============\n`;
output += dumpNode(instance, 0);
output += `============ End root ID: ${rootId} ============\n`;
}
return output;
}
function dumpNode(node: Object, identation: number) {
const data = getReactData(node);
if (data.nodeType === 'Text') {
return indent(identation) + data.text + '\n';
} else if (data.nodeType === 'Empty') {
return '';
}
let output = indent(identation) + `<${data.name}`;
if (data.nodeType === 'Composite') {
for (const propName of Object.getOwnPropertyNames(data.props || {})) {
if (isNormalProp(propName)) {
try {
const value = convertValue(data.props[propName]);
if (value) {
output += ` ${propName}=${value}`;
}
} catch (e) {
const message = `[Failed to get property: ${e}]`;
output += ` ${propName}=${message}`;
}
}
}
}
let childOutput = '';
for (const child of data.children || []) {
childOutput += dumpNode(child, identation + 1);
}
if (childOutput) {
output += '>\n' + childOutput + indent(identation) + `</${data.name}>\n`;
} else {
output += ' />\n';
}
return output;
}
function isNormalProp(name: string): boolean {
switch (name) {
case 'children':
case 'key':
case 'ref':
return false;
default:
return true;
}
}
function convertObject(object: Object, depth: number) {
if (depth >= MAX_DEPTH) {
return '[...omitted]';
}
let output = '{';
let first = true;
for (const key of Object.getOwnPropertyNames(object)) {
if (!first) {
output += ', ';
}
output += `${key}: ${convertValue(object[key], depth + 1)}`;
first = false;
}
return output + '}';
}
function convertValue(value, depth = 0): ?string {
if (!value) {
return null;
}
switch (typeof value) {
case 'string':
return JSON.stringify(possiblyEllipsis(value).replace('\n', '\\n'));
case 'boolean':
case 'number':
return JSON.stringify(value);
case 'function':
return '[function]';
case 'object':
return convertObject(value, depth);
default:
return null;
}
}
function possiblyEllipsis(value: string) {
if (value.length > MAX_STRING_LENGTH) {
return value.slice(0, MAX_STRING_LENGTH) + '...';
} else {
return value;
}
}
function indent(size: number) {
return ' '.repeat(size * INDENTATION_SIZE);
}
module.exports = dumpReactTree;
-168
View File
@@ -1,168 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule getReactData
* @flow
*/
'use strict';
/**
* Convert a react internal instance to a sanitized data object.
*
* This is shamelessly stolen from react-devtools:
* https://github.com/facebook/react-devtools/blob/master/backend/getData.js
*/
function getData(element: Object): Object {
var children = null;
var props = null;
var state = null;
var context = null;
var updater = null;
var name = null;
var type = null;
var text = null;
var publicInstance = null;
var nodeType = 'Native';
// If the parent is a native node without rendered children, but with
// multiple string children, then the `element` that gets passed in here is
// a plain value -- a string or number.
if (typeof element !== 'object') {
nodeType = 'Text';
text = element + '';
} else if (element._currentElement === null || element._currentElement === false) {
nodeType = 'Empty';
} else if (element._renderedComponent) {
nodeType = 'NativeWrapper';
children = [element._renderedComponent];
props = element._instance.props;
state = element._instance.state;
context = element._instance.context;
if (context && Object.keys(context).length === 0) {
context = null;
}
} else if (element._renderedChildren) {
children = childrenList(element._renderedChildren);
} else if (element._currentElement && element._currentElement.props) {
// This is a native node without rendered children -- meaning the children
// prop is just a string or (in the case of the <option>) a list of
// strings & numbers.
children = element._currentElement.props.children;
}
if (!props && element._currentElement && element._currentElement.props) {
props = element._currentElement.props;
}
// != used deliberately here to catch undefined and null
if (element._currentElement != null) {
type = element._currentElement.type;
if (typeof type === 'string') {
name = type;
} else if (element.getName) {
nodeType = 'Composite';
name = element.getName();
// 0.14 top-level wrapper
// TODO(jared): The backend should just act as if these don't exist.
if (element._renderedComponent && element._currentElement.props === element._renderedComponent._currentElement) {
nodeType = 'Wrapper';
}
if (name === null) {
name = 'No display name';
}
} else if (element._stringText) {
nodeType = 'Text';
text = element._stringText;
} else {
name = type.displayName || type.name || 'Unknown';
}
}
if (element._instance) {
var inst = element._instance;
updater = {
setState: inst.setState && inst.setState.bind(inst),
forceUpdate: inst.forceUpdate && inst.forceUpdate.bind(inst),
setInProps: inst.forceUpdate && setInProps.bind(null, element),
setInState: inst.forceUpdate && setInState.bind(null, inst),
setInContext: inst.forceUpdate && setInContext.bind(null, inst),
};
publicInstance = inst;
// TODO: React ART currently falls in this bucket, but this doesn't
// actually make sense and we should clean this up after stabilizing our
// API for backends
if (inst._renderedChildren) {
children = childrenList(inst._renderedChildren);
}
}
return {
nodeType,
type,
name,
props,
state,
context,
children,
text,
updater,
publicInstance,
};
}
function setInProps(internalInst, path: Array<string | number>, value: any) {
var element = internalInst._currentElement;
internalInst._currentElement = {
...element,
props: copyWithSet(element.props, path, value),
};
internalInst._instance.forceUpdate();
}
function setInState(inst, path: Array<string | number>, value: any) {
setIn(inst.state, path, value);
inst.forceUpdate();
}
function setInContext(inst, path: Array<string | number>, value: any) {
setIn(inst.context, path, value);
inst.forceUpdate();
}
function setIn(obj: Object, path: Array<string | number>, value: any) {
var last = path.pop();
var parent = path.reduce((obj_, attr) => obj_ ? obj_[attr] : null, obj);
if (parent) {
parent[last] = value;
}
}
function childrenList(children) {
var res = [];
for (var name in children) {
res.push(children[name]);
}
return res;
}
function copyWithSetImpl(obj, path, idx, value) {
if (idx >= path.length) {
return value;
}
var key = path[idx];
var updated = Array.isArray(obj) ? obj.slice() : {...obj};
// $FlowFixMe number or string is fine here
updated[key] = copyWithSetImpl(obj[key], path, idx + 1, value);
return updated;
}
function copyWithSet(obj: Object | Array<any>, path: Array<string | number>, value: any): Object | Array<any> {
return copyWithSetImpl(obj, path, 0, value);
}
module.exports = getData;
@@ -11,7 +11,6 @@
#import <AssetsLibrary/AssetsLibrary.h>
#import <libkern/OSAtomic.h>
#import <MobileCoreServices/MobileCoreServices.h>
#import "RCTBridge.h"
#import "RCTUtils.h"
@@ -54,13 +53,10 @@ RCT_EXPORT_MODULE()
ALAssetRepresentation *representation = [asset defaultRepresentation];
NSInteger length = (NSInteger)representation.size;
CFStringRef MIMEType = UTTypeCopyPreferredTagWithClass((__bridge CFStringRef _Nonnull)(representation.UTI), kUTTagClassMIMEType);
NSURLResponse *response =
[[NSURLResponse alloc] initWithURL:request.URL
MIMEType:(__bridge NSString *)(MIMEType)
MIMEType:representation.UTI
expectedContentLength:length
textEncodingName:nil];
+2 -9
View File
@@ -108,15 +108,8 @@ didFinishPickingMediaWithInfo:(NSDictionary<NSString *, id> *)info
BOOL isMovie = [mediaType isEqualToString:(NSString *)kUTTypeMovie];
NSString *key = isMovie ? UIImagePickerControllerMediaURL : UIImagePickerControllerReferenceURL;
NSURL *imageURL = info[key];
UIImage *image = info[UIImagePickerControllerOriginalImage];
NSNumber *width = 0;
NSNumber *height = 0;
if (image) {
height = @(image.size.height);
width = @(image.size.width);
}
if (imageURL) {
[self _dismissPicker:picker args:@[imageURL.absoluteString, RCTNullIfNil(height), RCTNullIfNil(width)]];
[self _dismissPicker:picker args:@[imageURL.absoluteString]];
return;
}
@@ -127,7 +120,7 @@ didFinishPickingMediaWithInfo:(NSDictionary<NSString *, id> *)info
// WARNING: Using ImageStoreManager may cause a memory leak because the
// image isn't automatically removed from store once we're done using it.
[_bridge.imageStoreManager storeImage:originalImage withBlock:^(NSString *tempImageTag) {
[self _dismissPicker:picker args:tempImageTag ? @[tempImageTag, height, width] : nil];
[self _dismissPicker:picker args:tempImageTag ? @[tempImageTag] : nil];
}];
}
@@ -16,6 +16,7 @@
var NativeMethodsMixin = require('NativeMethodsMixin');
var PropTypes = require('ReactPropTypes');
var React = require('React');
var RCTDatePickerIOSConsts = require('UIManager').RCTDatePicker.Constants;
var StyleSheet = require('StyleSheet');
var View = require('View');
@@ -141,8 +142,8 @@ var DatePickerIOS = React.createClass({
var styles = StyleSheet.create({
datePickerIOS: {
height: 216,
width: 320,
height: RCTDatePickerIOSConsts.ComponentHeight,
width: RCTDatePickerIOSConsts.ComponentWidth,
},
});
@@ -127,7 +127,7 @@ var DrawerLayoutAndroid = React.createClass({
* - idle, meaning there is no interaction with the navigation view happening at the time
* - dragging, meaning there is currently an interaction with the navigation view
* - settling, meaning that there was an interaction with the navigation view, and the
* navigation view is now finishing its closing or opening animation
* navigation view is now finishing it's closing or opening animation
*/
onDrawerStateChanged: ReactPropTypes.func,
/**
-1
View File
@@ -351,7 +351,6 @@ const MapView = React.createClass({
}
var viewIndex = children.length;
children.push(React.cloneElement(view, {
// $FlowFixMe - An array of styles should be fine
style: [styles.annotationView, view.props.style || {}]
}));
}
@@ -308,15 +308,6 @@ var NavigatorIOS = React.createClass({
*/
translucent: PropTypes.bool,
/**
* A Boolean value that indicates whether the interactive pop gesture is enabled. Useful
* for enabling/disabling the back swipe navigation gesture. If this prop is not provided,
* the default behavior is for the back swipe gesture to be enabled when the navigation bar
* is shown and disabled when the navigation bar is hidden. Once you've provided
* the interactivePopGestureEnabled prop, you can never restore the default behavior.
*/
interactivePopGestureEnabled: PropTypes.bool,
},
navigator: (undefined: ?Object),
@@ -695,8 +686,7 @@ var NavigatorIOS = React.createClass({
style={styles.transitioner}
vertical={this.props.vertical}
requestedTopOfStack={this.state.requestedTopOfStack}
onNavigationComplete={this._handleNavigationComplete}
interactivePopGestureEnabled={this.props.interactivePopGestureEnabled}>
onNavigationComplete={this._handleNavigationComplete}>
{items}
</NavigatorTransitionerIOS>
</StaticContainer>
@@ -52,11 +52,7 @@ var PickerAndroid = React.createClass({
},
getInitialState: function() {
var state = this._stateFromProps(this.props);
return {
...state,
initialSelectedIndex: state.selectedIndex,
};
return this._stateFromProps(this.props);
},
componentWillReceiveProps: function(nextProps) {
@@ -91,7 +87,7 @@ var PickerAndroid = React.createClass({
mode: this.props.mode,
onSelect: this._onChange,
prompt: this.props.prompt,
selected: this.state.initialSelectedIndex,
selected: this.state.selectedIndex,
testID: this.props.testID,
style: [styles.pickerAndroid, this.props.style],
};
@@ -109,24 +105,15 @@ var PickerAndroid = React.createClass({
this.props.onValueChange(null, position);
}
}
this._lastNativePosition = event.nativeEvent.position;
this.forceUpdate();
},
componentDidMount: function() {
this._lastNativePosition = this.state.initialSelectedIndex;
},
componentDidUpdate: function() {
// The picker is a controlled component. This means we expect the
// on*Change handlers to be in charge of updating our
// `selectedValue` prop. That way they can also
// disallow/undo/mutate the selection of certain values. In other
// words, the embedder of this component should be the source of
// truth, not the native component.
if (this.refs[REF_PICKER] && this.state.selectedIndex !== this._lastNativePosition) {
if (this.refs[REF_PICKER] && this.state.selectedIndex !== event.nativeEvent.position) {
this.refs[REF_PICKER].setNativeProps({selected: this.state.selectedIndex});
this._lastNativePosition = this.state.selectedIndex;
}
},
});
@@ -13,6 +13,7 @@
var Image = require('Image');
var NativeMethodsMixin = require('NativeMethodsMixin');
var NativeModules = require('NativeModules');
var PropTypes = require('ReactPropTypes');
var React = require('React');
var StyleSheet = require('StyleSheet');
@@ -71,7 +72,7 @@ var ProgressViewIOS = React.createClass({
var styles = StyleSheet.create({
progressView: {
height: 2,
height: NativeModules.ProgressViewManager.ComponentHeight
},
});
@@ -20,7 +20,7 @@ const View = require('View');
const requireNativeComponent = require('requireNativeComponent');
if (Platform.OS === 'android') {
var RefreshLayoutConsts = require('UIManager').AndroidSwipeRefreshLayout.Constants;
var RefreshLayoutConsts = require('NativeModules').UIManager.AndroidSwipeRefreshLayout.Constants;
} else {
var RefreshLayoutConsts = {SIZE: {}};
}
@@ -86,7 +86,7 @@ const RefreshControl = React.createClass({
/**
* Whether the view should be indicating an active refresh.
*/
refreshing: React.PropTypes.bool.isRequired,
refreshing: React.PropTypes.bool,
/**
* The color of the refresh indicator.
* @platform ios
@@ -121,32 +121,10 @@ const RefreshControl = React.createClass({
* Size of the refresh indicator, see RefreshControl.SIZE.
* @platform android
*/
size: React.PropTypes.oneOf([RefreshLayoutConsts.SIZE.DEFAULT, RefreshLayoutConsts.SIZE.LARGE]),
/**
* Progress view top offset
* @platform android
*/
progressViewOffset: React.PropTypes.number,
size: React.PropTypes.oneOf(RefreshLayoutConsts.SIZE.DEFAULT, RefreshLayoutConsts.SIZE.LARGE),
},
_nativeRef: (null: any),
_lastNativeRefreshing: false,
componentDidMount() {
this._lastNativeRefreshing = this.props.refreshing;
},
componentDidUpdate(prevProps: {refreshing: boolean}) {
// RefreshControl is a controlled component so if the native refreshing
// value doesn't match the current js refreshing prop update it to
// the js value.
if (this.props.refreshing !== prevProps.refreshing) {
this._lastNativeRefreshing = this.props.refreshing;
} else if (this.props.refreshing !== this._lastNativeRefreshing) {
this._nativeRef.setNativeProps({refreshing: this.props.refreshing});
this._lastNativeRefreshing = this.props.refreshing;
}
},
_nativeRef: {},
render() {
return (
@@ -159,13 +137,11 @@ const RefreshControl = React.createClass({
},
_onRefresh() {
this._lastNativeRefreshing = true;
this.props.onRefresh && this.props.onRefresh();
// The native component will start refreshing so force an update to
// make sure it stays in sync with the js component.
this.forceUpdate();
if (this._nativeRef) {
this._nativeRef.setNativeProps({refreshing: this.props.refreshing});
}
},
});
+9 -5
View File
@@ -13,7 +13,8 @@
var Dimensions = require('Dimensions');
var Platform = require('Platform');
var Keyboard = require('Keyboard');
var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
var React = require('React');
var ReactNative = require('ReactNative');
var Subscribable = require('Subscribable');
var TextInputState = require('TextInputState');
@@ -22,6 +23,9 @@ var UIManager = require('UIManager');
var { ScrollViewManager } = require('NativeModules');
var invariant = require('fbjs/lib/invariant');
var warning = require('fbjs/lib/warning');
import type ReactComponent from 'ReactComponent';
/**
* Mixin that can be integrated in order to handle scrolling that plays well
@@ -476,10 +480,10 @@ var ScrollResponderMixin = {
componentWillMount: function() {
this.keyboardWillOpenTo = null;
this.additionalScrollOffset = 0;
this.addListenerOn(Keyboard, 'keyboardWillShow', this.scrollResponderKeyboardWillShow);
this.addListenerOn(Keyboard, 'keyboardWillHide', this.scrollResponderKeyboardWillHide);
this.addListenerOn(Keyboard, 'keyboardDidShow', this.scrollResponderKeyboardDidShow);
this.addListenerOn(Keyboard, 'keyboardDidHide', this.scrollResponderKeyboardDidHide);
this.addListenerOn(RCTDeviceEventEmitter, 'keyboardWillShow', this.scrollResponderKeyboardWillShow);
this.addListenerOn(RCTDeviceEventEmitter, 'keyboardWillHide', this.scrollResponderKeyboardWillHide);
this.addListenerOn(RCTDeviceEventEmitter, 'keyboardDidShow', this.scrollResponderKeyboardDidShow);
this.addListenerOn(RCTDeviceEventEmitter, 'keyboardDidHide', this.scrollResponderKeyboardDidHide);
},
/**
@@ -5,6 +5,7 @@
*/
'use strict';
var NativeMethodsMixin = require('NativeMethodsMixin');
var React = require('React');
var ScrollResponder = require('ScrollResponder');
var ScrollView = require('ScrollView');
+57 -87
View File
@@ -11,26 +11,30 @@
*/
'use strict';
const ColorPropType = require('ColorPropType');
const EdgeInsetsPropType = require('EdgeInsetsPropType');
const Platform = require('Platform');
const PointPropType = require('PointPropType');
const RCTScrollViewManager = require('NativeModules').ScrollViewManager;
const React = require('React');
const ReactNative = require('ReactNative');
const ScrollResponder = require('ScrollResponder');
const StyleSheet = require('StyleSheet');
const StyleSheetPropType = require('StyleSheetPropType');
const View = require('View');
const ViewStylePropTypes = require('ViewStylePropTypes');
var ColorPropType = require('ColorPropType');
var EdgeInsetsPropType = require('EdgeInsetsPropType');
var Platform = require('Platform');
var PointPropType = require('PointPropType');
var RCTScrollView = require('NativeModules').UIManager.RCTScrollView;
var RCTScrollViewManager = require('NativeModules').ScrollViewManager;
var React = require('React');
var ReactNative = require('ReactNative');
var ScrollResponder = require('ScrollResponder');
var StyleSheet = require('StyleSheet');
var StyleSheetPropType = require('StyleSheetPropType');
var View = require('View');
var ViewStylePropTypes = require('ViewStylePropTypes');
const deprecatedPropType = require('deprecatedPropType');
const dismissKeyboard = require('dismissKeyboard');
const flattenStyle = require('flattenStyle');
const invariant = require('fbjs/lib/invariant');
const processDecelerationRate = require('processDecelerationRate');
const PropTypes = React.PropTypes;
const requireNativeComponent = require('requireNativeComponent');
var deprecatedPropType = require('deprecatedPropType');
var dismissKeyboard = require('dismissKeyboard');
var flattenStyle = require('flattenStyle');
var invariant = require('fbjs/lib/invariant');
var requireNativeComponent = require('requireNativeComponent');
var processDecelerationRate = require('processDecelerationRate');
var PropTypes = React.PropTypes;
var SCROLLVIEW = 'ScrollView';
var INNERVIEW = 'InnerScrollView';
/**
* Component that wraps platform ScrollView while providing
@@ -47,7 +51,7 @@ const requireNativeComponent = require('requireNativeComponent');
* Doesn't yet support other contained responders from blocking this scroll
* view from becoming the responder.
*/
const ScrollView = React.createClass({
var ScrollView = React.createClass({
propTypes: {
...View.propTypes,
/**
@@ -59,7 +63,7 @@ const ScrollView = React.createClass({
automaticallyAdjustContentInsets: PropTypes.bool,
/**
* The amount by which the scroll view content is inset from the edges
* of the scroll view. Defaults to `{top: 0, left: 0, bottom: 0, right: 0}`.
* of the scroll view. Defaults to `{0, 0, 0, 0}`.
* @platform ios
*/
contentInset: EdgeInsetsPropType,
@@ -115,7 +119,7 @@ const ScrollView = React.createClass({
* </ScrollView>
* );
* ...
* const styles = StyleSheet.create({
* var styles = StyleSheet.create({
* contentContainer: {
* paddingVertical: 20
* }
@@ -206,11 +210,8 @@ const ScrollView = React.createClass({
*/
onScrollAnimationEnd: PropTypes.func,
/**
* Called when scrollable content view of the ScrollView changes.
*
* Handler function is passed the content width and content height as parameters: `(contentWidth, contentHeight)`
*
* It's implemented using onLayout handler attached to the content container
* Called when scrollable content view of the ScrollView changes. It's
* implemented using onLayout handler attached to the content container
* which this ScrollView renders.
*/
onContentSizeChange: PropTypes.func,
@@ -218,6 +219,7 @@ const ScrollView = React.createClass({
* When true, the scroll view stops on multiples of the scroll view's size
* when scrolling. This can be used for horizontal pagination. The default
* value is false.
* @platform ios
*/
pagingEnabled: PropTypes.bool,
/**
@@ -327,15 +329,6 @@ const ScrollView = React.createClass({
* @platform android
*/
endFillColor: ColorPropType,
/**
* Tag used to log scroll performance on this scroll view. Will force
* momentum events to be turned on (see sendMomentumEvents). This doesn't do
* anything out of the box and you need to implement a custom native
* FpsListener for it to be useful.
* @platform android
*/
scrollPerfTag: PropTypes.string,
},
mixins: [ScrollResponder.Mixin],
@@ -345,7 +338,7 @@ const ScrollView = React.createClass({
},
setNativeProps: function(props: Object) {
this._scrollViewRef && this._scrollViewRef.setNativeProps(props);
this.refs[SCROLLVIEW].setNativeProps(props);
},
/**
@@ -363,16 +356,16 @@ const ScrollView = React.createClass({
* implement this method so that they can be composed while providing access
* to the underlying scroll responder's methods.
*/
getScrollResponder: function(): ScrollView {
getScrollResponder: function(): ReactComponent {
return this;
},
getScrollableNode: function(): any {
return ReactNative.findNodeHandle(this._scrollViewRef);
return ReactNative.findNodeHandle(this.refs[SCROLLVIEW]);
},
getInnerViewNode: function(): any {
return ReactNative.findNodeHandle(this._innerViewRef);
return ReactNative.findNodeHandle(this.refs[INNERVIEW]);
},
/**
@@ -396,6 +389,7 @@ const ScrollView = React.createClass({
} else {
({x, y, animated} = y || {});
}
// $FlowFixMe - Don't know how to pass Mixin correctly. Postpone for now
this.getScrollResponder().scrollResponderScrollTo({x: x || 0, y: y || 0, animated: animated !== false});
},
@@ -410,7 +404,7 @@ const ScrollView = React.createClass({
_handleScroll: function(e: Object) {
if (__DEV__) {
if (this.props.onScroll && !this.props.scrollEventThrottle && Platform.OS === 'ios') {
console.log( // eslint-disable-line no-console-disallow
console.log(
'You specified `onScroll` on a <ScrollView> but not ' +
'`scrollEventThrottle`. You will only receive one event. ' +
'Using `16` you get all the events but be aware that it may ' +
@@ -428,29 +422,18 @@ const ScrollView = React.createClass({
},
_handleContentOnLayout: function(e: Object) {
const {width, height} = e.nativeEvent.layout;
var {width, height} = e.nativeEvent.layout;
this.props.onContentSizeChange && this.props.onContentSizeChange(width, height);
},
_scrollViewRef: (null: ?ScrollView),
_setScrollViewRef: function(ref: ?ScrollView) {
this._scrollViewRef = ref;
},
_innerViewRef: (null: ?View),
_setInnerViewRef: function(ref: ?View) {
this._innerViewRef = ref;
},
render: function() {
const contentContainerStyle = [
var contentContainerStyle = [
this.props.horizontal && styles.contentContainerHorizontal,
this.props.contentContainerStyle,
];
let style, childLayoutProps;
if (__DEV__ && this.props.style) {
style = flattenStyle(this.props.style);
childLayoutProps = ['alignItems', 'justifyContent']
var style = flattenStyle(this.props.style);
var childLayoutProps = ['alignItems', 'justifyContent']
.filter((prop) => style && style[prop] !== undefined);
invariant(
childLayoutProps.length === 0,
@@ -459,34 +442,34 @@ const ScrollView = React.createClass({
);
}
let contentSizeChangeProps = {};
var contentSizeChangeProps = {};
if (this.props.onContentSizeChange) {
contentSizeChangeProps = {
onLayout: this._handleContentOnLayout,
};
}
const contentContainer =
var contentContainer =
<View
{...contentSizeChangeProps}
ref={this._setInnerViewRef}
ref={INNERVIEW}
style={contentContainerStyle}
removeClippedSubviews={this.props.removeClippedSubviews}
collapsable={false}>
{this.props.children}
</View>;
const alwaysBounceHorizontal =
var alwaysBounceHorizontal =
this.props.alwaysBounceHorizontal !== undefined ?
this.props.alwaysBounceHorizontal :
this.props.horizontal;
const alwaysBounceVertical =
var alwaysBounceVertical =
this.props.alwaysBounceVertical !== undefined ?
this.props.alwaysBounceVertical :
!this.props.horizontal;
const props = {
var props = {
...this.props,
alwaysBounceHorizontal,
alwaysBounceVertical,
@@ -510,7 +493,7 @@ const ScrollView = React.createClass({
sendMomentumEvents: (this.props.onMomentumScrollBegin || this.props.onMomentumScrollEnd) ? true : false,
};
const onRefreshStart = this.props.onRefreshStart;
var onRefreshStart = this.props.onRefreshStart;
if (onRefreshStart) {
// this is necessary because if we set it on props, even when empty,
// it'll trigger the default pull-to-refresh behavior on native.
@@ -518,12 +501,12 @@ const ScrollView = React.createClass({
function() { onRefreshStart && onRefreshStart(this.endRefreshing); }.bind(this);
}
const { decelerationRate } = this.props;
var { decelerationRate } = this.props;
if (decelerationRate) {
props.decelerationRate = processDecelerationRate(decelerationRate);
}
let ScrollViewClass;
var ScrollViewClass;
if (Platform.OS === 'ios') {
ScrollViewClass = RCTScrollView;
} else if (Platform.OS === 'android') {
@@ -538,12 +521,12 @@ const ScrollView = React.createClass({
'ScrollViewClass must not be undefined'
);
const refreshControl = this.props.refreshControl;
var refreshControl = this.props.refreshControl;
if (refreshControl) {
if (Platform.OS === 'ios') {
// On iOS the RefreshControl is a child of the ScrollView.
return (
<ScrollViewClass {...props} ref={this._setScrollViewRef}>
<ScrollViewClass {...props} ref={SCROLLVIEW}>
{refreshControl}
{contentContainer}
</ScrollViewClass>
@@ -555,21 +538,21 @@ const ScrollView = React.createClass({
return React.cloneElement(
refreshControl,
{style: props.style},
<ScrollViewClass {...props} style={styles.base} ref={this._setScrollViewRef}>
<ScrollViewClass {...props} style={styles.base} ref={SCROLLVIEW}>
{contentContainer}
</ScrollViewClass>
);
}
}
return (
<ScrollViewClass {...props} ref={this._setScrollViewRef}>
<ScrollViewClass {...props} ref={SCROLLVIEW}>
{contentContainer}
</ScrollViewClass>
);
}
});
const styles = StyleSheet.create({
var styles = StyleSheet.create({
base: {
flex: 1,
},
@@ -579,29 +562,16 @@ const styles = StyleSheet.create({
},
});
let nativeOnlyProps, AndroidScrollView, AndroidHorizontalScrollView, RCTScrollView;
if (Platform.OS === 'android') {
nativeOnlyProps = {
nativeOnly: {
sendMomentumEvents: true,
}
};
AndroidScrollView = requireNativeComponent('RCTScrollView', ScrollView, nativeOnlyProps);
AndroidHorizontalScrollView = requireNativeComponent(
var nativeOnlyProps = { nativeOnly : { 'sendMomentumEvents' : true } };
var AndroidScrollView = requireNativeComponent('RCTScrollView', ScrollView, nativeOnlyProps);
var AndroidHorizontalScrollView = requireNativeComponent(
'AndroidHorizontalScrollView',
ScrollView,
nativeOnlyProps
);
} else if (Platform.OS === 'ios') {
nativeOnlyProps = {
nativeOnly: {
onMomentumScrollBegin: true,
onMomentumScrollEnd : true,
onScrollBeginDrag: true,
onScrollEndDrag: true,
}
};
RCTScrollView = requireNativeComponent('RCTScrollView', ScrollView, nativeOnlyProps);
var RCTScrollView = requireNativeComponent('RCTScrollView', ScrollView);
}
module.exports = ScrollView;
@@ -12,6 +12,7 @@
'use strict';
var NativeMethodsMixin = require('NativeMethodsMixin');
var NativeModules = require('NativeModules');
var PropTypes = require('ReactPropTypes');
var React = require('React');
var StyleSheet = require('StyleSheet');
@@ -119,7 +120,7 @@ var SegmentedControlIOS = React.createClass({
var styles = StyleSheet.create({
segmentedControl: {
height: 28,
height: NativeModules.SegmentedControlManager.ComponentHeight
},
});
@@ -11,6 +11,4 @@
*/
'use strict';
const NativeEventEmitter = require('NativeEventEmitter');
module.exports = new NativeEventEmitter('StatusBarManager');
module.exports = null;
@@ -11,26 +11,24 @@
*/
'use strict';
const NativeEventEmitter = require('NativeEventEmitter');
const StatusBar = require('StatusBar');
const StatusBarManager = require('NativeModules').StatusBarManager;
import type {StatusBarStyle, StatusBarAnimation} from 'StatusBar';
/**
* Deprecated. Use `StatusBar` instead.
*/
class StatusBarIOS extends NativeEventEmitter {
const StatusBarIOS = {
setStyle(style: StatusBarStyle, animated?: boolean) {
console.warn('`StatusBarIOS.setStyle` is deprecated. Use `StatusBar.setBarStyle` instead.');
StatusBar.setBarStyle(style, animated);
}
},
setHidden(hidden: boolean, animation?: StatusBarAnimation) {
console.warn('`StatusBarIOS.setHidden` is deprecated. Use `StatusBar.setHidden` instead.');
StatusBar.setHidden(hidden, animation);
}
},
setNetworkActivityIndicatorVisible(visible: boolean) {
console.warn(
@@ -38,7 +36,7 @@ class StatusBarIOS extends NativeEventEmitter {
'Use `StatusBar.setNetworkActivityIndicatorVisible` instead.'
);
StatusBar.setNetworkActivityIndicatorVisible(visible);
}
}
},
};
module.exports = new StatusBarIOS(StatusBarManager);
module.exports = StatusBarIOS;
@@ -27,10 +27,6 @@ var TabBarIOS = React.createClass({
propTypes: {
...View.propTypes,
style: View.propTypes.style,
/**
* Color of text on unselected tabs
*/
unselectedTintColor: ColorPropType,
/**
* Color of the currently selected tab icon
*/
@@ -49,7 +45,6 @@ var TabBarIOS = React.createClass({
return (
<RCTTabBar
style={[styles.tabGroup, this.props.style]}
unselectedTintColor={this.props.unselectedTintColor}
tintColor={this.props.tintColor}
barTintColor={this.props.barTintColor}
translucent={this.props.translucent !== false}>
@@ -62,11 +62,6 @@ var TabBarItemIOS = React.createClass({
* component to set selected={true}.
*/
onPress: React.PropTypes.func,
/**
* If set to true it renders the image as original,
* it defaults to being displayed as a template
*/
renderAsOriginal: React.PropTypes.bool,
/**
* It specifies whether the children are visible or not. If you see a
* blank content, you probably forgot to add a selected one.
+10 -45
View File
@@ -120,7 +120,6 @@ var TextInput = React.createClass({
* Determines which keyboard to open, e.g.`numeric`.
*
* The following values work across platforms:
*
* - default
* - numeric
* - email-address
@@ -151,54 +150,22 @@ var TextInput = React.createClass({
'dark',
]),
/**
* Determines how the return key should look. On Android you can also use
* `returnKeyLabel`.
*
* The following values work across platforms:
*
* - done
* - go
* - next
* - search
* - send
*
* The following values work on Android only:
*
* - none
* - previous
*
* The following values work on iOS only:
*
* - default
* - emergency-call
* - google
* - join
* - route
* - yahoo
* Determines how the return key should look.
* @platform ios
*/
returnKeyType: PropTypes.oneOf([
// Cross-platform
'done',
'go',
'next',
'search',
'send',
// Android-only
'none',
'previous',
// iOS-only
'default',
'emergency-call',
'go',
'google',
'join',
'next',
'route',
'search',
'send',
'yahoo',
'done',
'emergency-call',
]),
/**
* Sets the return key to the label. Use it instead of `returnKeyType`.
* @platform android
*/
returnKeyLabel: PropTypes.string,
/**
* Limits the maximum number of characters that can be entered. Use this
* instead of implementing the logic in JS to avoid flicker.
@@ -476,8 +443,8 @@ var TextInput = React.createClass({
!(props.value && childCount),
'Cannot specify both value and children.'
);
if (childCount >= 1) {
children = <Text style={props.style}>{children}</Text>;
if (childCount > 1) {
children = <Text>{children}</Text>;
}
if (props.inputView) {
children = [children, props.inputView];
@@ -564,8 +531,6 @@ var TextInput = React.createClass({
children={children}
editable={this.props.editable}
selectTextOnFocus={this.props.selectTextOnFocus}
returnKeyType={this.props.returnKeyType}
returnKeyLabel={this.props.returnKeyLabel}
/>;
return (
+5 -2
View File
@@ -360,10 +360,10 @@ var TouchableMixin = {
/**
* Place as callback for a DOM element's `onResponderGrant` event.
* @param {SyntheticEvent} e Synthetic event from event system.
* @param {string} dispatchID ID of node that e was dispatched to.
*
*/
touchableHandleResponderGrant: function(e) {
var dispatchID = e.currentTarget;
touchableHandleResponderGrant: function(e, dispatchID) {
// Since e is used in a callback invoked on another event loop
// (as in setTimeout etc), we need to call e.persist() on the
// event to make sure it doesn't get reused in the event object pool.
@@ -756,5 +756,8 @@ var Touchable = {
);
}
};
if (Touchable.TOUCH_TARGET_DEBUG) {
console.warn('Touchable.TOUCH_TARGET_DEBUG is enabled');
}
module.exports = Touchable;
@@ -55,7 +55,7 @@ var TouchableOpacity = React.createClass({
...TouchableWithoutFeedback.propTypes,
/**
* Determines what the opacity of the wrapped view should be when touch is
* active. Defaults to 0.2.
* active.
*/
activeOpacity: React.PropTypes.number,
},
@@ -123,12 +123,6 @@ var ViewPagerAndroid = React.createClass({
'none', // default
'on-drag',
]),
/**
* When false, the content does not scroll.
* The default value is true.
*/
scrollEnabled: ReactPropTypes.bool,
},
componentDidMount: function() {
@@ -207,7 +201,7 @@ var ViewPagerAndroid = React.createClass({
/**
* A helper function to scroll to a specific page in the ViewPager.
* The transition between pages will *not* be animated.
* The transition between pages will be *not* be animated.
*/
setPageWithoutAnimation: function(selectedPage: number) {
UIManager.dispatchViewManagerCommand(
@@ -264,14 +264,6 @@ var WebView = React.createClass({
);
},
stopLoading: function() {
UIManager.dispatchViewManagerCommand(
this.getWebViewHandle(),
UIManager.RCTWebView.Commands.stopLoading,
null
);
},
/**
* We return an event with a bunch of fields including:
* url, title, loading, canGoBack, canGoForward
@@ -362,14 +362,6 @@ var WebView = React.createClass({
);
},
stopLoading: function() {
UIManager.dispatchViewManagerCommand(
this.getWebViewHandle(),
UIManager.RCTWebView.Commands.stopLoading,
null
);
},
/**
* We return an event with a bunch of fields including:
* url, title, loading, canGoBack, canGoForward
@@ -56,10 +56,10 @@ var DEFAULT_SCROLL_CALLBACK_THROTTLE = 50;
/**
* ListView - A core component designed for efficient display of vertically
* scrolling lists of changing data. The minimal API is to create a
* [`ListView.DataSource`](docs/listviewdatasource.html), populate it with a simple
* array of data blobs, and instantiate a `ListView` component with that data
* source and a `renderRow` callback which takes a blob from the data array and
* returns a renderable component.
* `ListView.DataSource`, populate it with a simple array of data blobs, and
* instantiate a `ListView` component with that data source and a `renderRow`
* callback which takes a blob from the data array and returns a renderable
* component.
*
* Minimal example:
*
@@ -117,9 +117,7 @@ var ListView = React.createClass({
*/
propTypes: {
...ScrollView.propTypes,
/**
* An instance of [ListView.DataSource](docs/listviewdatasource.html) to use
*/
dataSource: PropTypes.instanceOf(ListViewDataSource).isRequired,
/**
* (sectionID, rowID, adjacentRowHighlighted) => renderable
@@ -137,10 +135,9 @@ var ListView = React.createClass({
* a renderable component to be rendered as the row. By default the data
* is exactly what was put into the data source, but it's also possible to
* provide custom extractors. ListView can be notified when a row is
* being highlighted by calling `highlightRow(sectionID, rowID)`. This
* sets a boolean value of adjacentRowHighlighted in renderSeparator, allowing you
* to control the separators above and below the highlighted row. The highlighted
* state of a row can be reset by calling highlightRow(null).
* being highlighted by calling highlightRow function. The separators above and
* below will be hidden when a row is highlighted. The highlighted state of
* a row can be reset by calling highlightRow(null).
*/
renderRow: PropTypes.func.isRequired,
/**
@@ -596,19 +593,12 @@ var ListView = React.createClass({
var rowID = rowIDs[rowIdx];
var frame = this._childFrames[totalIndex];
totalIndex++;
if(this.props.renderSeparator &&
(rowIdx !== rowIDs.length - 1 || sectionIdx === allRowIDs.length - 1)){
totalIndex++;
}
if (!frame) {
break;
}
var rowVisible = visibleSection[rowID];
var min = isVertical ? frame.y : frame.x;
var max = min + (isVertical ? frame.height : frame.width);
if ((!min && !max) || (min === max)) {
break;
}
if (min > visibleMax || max < visibleMin) {
if (rowVisible) {
visibilityChanged = true;
@@ -35,9 +35,9 @@
const Animated = require('Animated');
const NavigationCardStackPanResponder = require('NavigationCardStackPanResponder');
const NavigationCardStackStyleInterpolator = require('NavigationCardStackStyleInterpolator');
const NavigationContainer = require('NavigationContainer');
const NavigationPagerPanResponder = require('NavigationPagerPanResponder');
const NavigationPagerStyleInterpolator = require('NavigationPagerStyleInterpolator');
const NavigationPointerEventsContainer = require('NavigationPointerEventsContainer');
const NavigationPropTypes = require('NavigationPropTypes');
const React = require('React');
const ReactComponentWithPureRenderMixin = require('ReactComponentWithPureRenderMixin');
@@ -50,40 +50,20 @@ import type {
NavigationSceneRendererProps,
} from 'NavigationTypeDefinition';
type SceneViewProps = {
sceneRenderer: NavigationSceneRenderer,
sceneRendererProps: NavigationSceneRendererProps,
};
type Props = NavigationSceneRendererProps & {
onComponentRef: (ref: any) => void,
panHandlers: ?NavigationPanPanHandlers,
pointerEvents: string,
renderScene: NavigationSceneRenderer,
style: any,
panHandlers: ?NavigationPanPanHandlers,
renderScene: NavigationSceneRenderer,
};
const {PropTypes} = React;
class SceneView extends React.Component<any, SceneViewProps, any> {
static propTypes = {
sceneRenderer: PropTypes.func.isRequired,
sceneRendererProps:
PropTypes.shape(NavigationPropTypes.SceneRenderer).isRequired,
};
shouldComponentUpdate(nextProps: SceneViewProps, nextState: any): boolean {
return (
nextProps.sceneRendererProps.scene.navigationState !==
this.props.sceneRendererProps.scene.navigationState
);
}
render(): ?ReactElement {
return this.props.sceneRenderer(this.props.sceneRendererProps);
}
}
const propTypes = {
...NavigationPropTypes.SceneRenderer,
style: PropTypes.any,
panHandlers: NavigationPropTypes.panHandlers,
renderScene: PropTypes.func.isRequired,
};
/**
* Component that renders the scene as card for the <NavigationCardStack />.
@@ -91,15 +71,6 @@ class SceneView extends React.Component<any, SceneViewProps, any> {
class NavigationCard extends React.Component<any, Props, any> {
props: Props;
static propTypes = {
...NavigationPropTypes.SceneRendererProps,
onComponentRef: PropTypes.func.isRequired,
panHandlers: NavigationPropTypes.panHandlers,
pointerEvents: PropTypes.string.isRequired,
renderScene: PropTypes.func.isRequired,
style: PropTypes.any,
};
shouldComponentUpdate(nextProps: Props, nextState: any): boolean {
return ReactComponentWithPureRenderMixin.shouldComponentUpdate.call(
this,
@@ -111,40 +82,50 @@ class NavigationCard extends React.Component<any, Props, any> {
render(): ReactElement {
const {
panHandlers,
pointerEvents,
renderScene,
style,
...props, /* NavigationSceneRendererProps */
} = this.props;
const viewStyle = style === undefined ?
NavigationCardStackStyleInterpolator.forHorizontal(props) :
style;
let viewStyle = null;
if (style === undefined) {
// fall back to default style.
viewStyle = NavigationCardStackStyleInterpolator.forHorizontal(props);
} else {
viewStyle = style;
}
const viewPanHandlers = panHandlers === undefined ?
NavigationCardStackPanResponder.forHorizontal(props) :
panHandlers;
const {
navigationState,
scene,
} = props;
const interactive = navigationState.index === scene.index && !scene.isStale;
const pointerEvents = interactive ? 'auto' : 'none';
let viewPanHandlers = null;
if (interactive) {
if (panHandlers === undefined) {
// fall back to default pan handlers.
viewPanHandlers = NavigationCardStackPanResponder.forHorizontal(props);
} else {
viewPanHandlers = panHandlers;
}
}
return (
<Animated.View
{...viewPanHandlers}
pointerEvents={pointerEvents}
ref={this.props.onComponentRef}
style={[styles.main, viewStyle]}>
<SceneView
sceneRenderer={renderScene}
sceneRendererProps={props}
/>
{renderScene(props)}
</Animated.View>
);
}
static CardStackPanResponder = NavigationCardStackPanResponder;
static CardStackStyleInterpolator = NavigationCardStackStyleInterpolator;
static PagerPanResponder = NavigationPagerPanResponder;
static PagerStyleInterpolator = NavigationPagerStyleInterpolator;
}
NavigationCard.propTypes = propTypes;
const styles = StyleSheet.create({
main: {
backgroundColor: '#E9E9EF',
@@ -160,6 +141,13 @@ const styles = StyleSheet.create({
},
});
NavigationCard = NavigationPointerEventsContainer.create(NavigationCard);
module.exports = NavigationCard;
const NavigationCardContainer = NavigationContainer.create(NavigationCard);
// Export these buil-in interaction modules.
NavigationCardContainer.CardStackPanResponder = NavigationCardStackPanResponder;
NavigationCardContainer.CardStackStyleInterpolator = NavigationCardStackStyleInterpolator;
NavigationCardContainer.PagerPanResponder = NavigationPagerPanResponder;
NavigationCardContainer.PagerStyleInterpolator = NavigationPagerStyleInterpolator;
module.exports = NavigationCardContainer;
@@ -35,6 +35,7 @@
const NavigationAnimatedView = require('NavigationAnimatedView');
const NavigationCard = require('NavigationCard');
const NavigationCardStackStyleInterpolator = require('NavigationCardStackStyleInterpolator');
const NavigationContainer = require('NavigationContainer');
const NavigationCardStackPanResponder = require('NavigationCardStackPanResponder');
const NavigationPropTypes = require('NavigationPropTypes');
const React = require('React');
@@ -47,7 +48,6 @@ const {PropTypes} = React;
const {Directions} = NavigationCardStackPanResponder;
import type {
NavigationActionCaller,
NavigationParentState,
NavigationSceneRenderer,
NavigationSceneRendererProps,
@@ -60,16 +60,10 @@ import type {
type Props = {
direction: NavigationGestureDirection,
navigationState: NavigationParentState,
onNavigate: NavigationActionCaller,
renderOverlay: ?NavigationSceneRenderer,
renderScene: NavigationSceneRenderer,
};
type DefaultProps = {
direction: NavigationGestureDirection,
renderOverlay: ?NavigationSceneRenderer,
};
/**
* A controlled navigation view that renders a stack of cards.
*
@@ -84,18 +78,17 @@ type DefaultProps = {
* +-+ |
* +------------+
*/
class NavigationCardStack extends React.Component<DefaultProps, Props, void> {
class NavigationCardStack extends React.Component {
_renderScene : NavigationSceneRenderer;
static propTypes = {
direction: PropTypes.oneOf([Directions.HORIZONTAL, Directions.VERTICAL]),
navigationState: NavigationPropTypes.navigationParentState.isRequired,
onNavigate: NavigationPropTypes.SceneRendererProps.onNavigate,
renderOverlay: PropTypes.func,
renderScene: PropTypes.func.isRequired,
};
static defaultProps: DefaultProps = {
static defaultProps = {
direction: Directions.HORIZONTAL,
renderOverlay: emptyFunction.thatReturnsNull,
};
@@ -108,7 +101,7 @@ class NavigationCardStack extends React.Component<DefaultProps, Props, void> {
this._renderScene = this._renderScene.bind(this);
}
shouldComponentUpdate(nextProps: Object, nextState: void): boolean {
shouldComponentUpdate(nextProps: Object, nextState: Object): boolean {
return ReactComponentWithPureRenderMixin.shouldComponentUpdate.call(
this,
nextProps,
@@ -122,8 +115,6 @@ class NavigationCardStack extends React.Component<DefaultProps, Props, void> {
navigationState={this.props.navigationState}
renderOverlay={this.props.renderOverlay}
renderScene={this._renderScene}
onNavigate={this.props.onNavigate}
// $FlowFixMe - style should be declared
style={[styles.animatedView, this.props.style]}
/>
);
@@ -158,4 +149,4 @@ const styles = StyleSheet.create({
},
});
module.exports = NavigationCardStack;
module.exports = NavigationContainer.create(NavigationCardStack);
@@ -51,28 +51,6 @@ import type {
* +------------+
*/
/**
* Render the initial style when the initial layout isn't measured yet.
*/
function forInitial(props: NavigationSceneRendererProps): Object {
const {
navigationState,
scene,
} = props;
const focused = navigationState.index === scene.index;
const opacity = focused ? 1 : 0;
// If not focused, move the scene to the far away.
const translate = focused ? 0 : 1000000;
return {
opacity,
transform: [
{ translateX: translate },
{ translateY: translate },
],
};
}
function forHorizontal(props: NavigationSceneRendererProps): Object {
const {
layout,
@@ -80,10 +58,6 @@ function forHorizontal(props: NavigationSceneRendererProps): Object {
scene,
} = props;
if (!layout.isMeasured) {
return forInitial(props);
}
const index = scene.index;
const inputRange = [index - 1, index, index + 1];
const width = layout.initWidth;
@@ -121,10 +95,6 @@ function forVertical(props: NavigationSceneRendererProps): Object {
scene,
} = props;
if (!layout.isMeasured) {
return forInitial(props);
}
const index = scene.index;
const inputRange = [index - 1, index, index + 1];
const height = layout.initHeight;
@@ -34,6 +34,7 @@
const React = require('React');
const ReactNative = require('react-native');
const NavigationContainer = require('NavigationContainer');
const NavigationHeaderTitle = require('NavigationHeaderTitle');
const NavigationHeaderBackButton = require('NavigationHeaderBackButton');
const NavigationPropTypes = require('NavigationPropTypes');
@@ -48,7 +49,6 @@ const {
} = ReactNative;
import type {
NavigationActionCaller,
NavigationSceneRenderer,
NavigationSceneRendererProps,
NavigationStyleInterpolator,
@@ -64,9 +64,8 @@ type Props = NavigationSceneRendererProps & {
renderLeftComponent: NavigationSceneRenderer,
renderRightComponent: NavigationSceneRenderer,
renderTitleComponent: NavigationSceneRenderer,
onNavigate: NavigationActionCaller,
style?: any,
viewProps?: any,
style?: any;
viewProps?: any;
};
type SubViewName = 'left' | 'title' | 'right';
@@ -87,14 +86,7 @@ class NavigationHeader extends React.Component<DefaultProps, Props, any> {
},
renderLeftComponent: (props: NavigationSceneRendererProps) => {
if (props.scene.index === 0) {
return null;
}
return (
<NavigationHeaderBackButton
onNavigate={props.onNavigate}
/>
);
return props.scene.index > 0 ? <NavigationHeaderBackButton /> : null;
},
renderRightComponent: (props: NavigationSceneRendererProps) => {
@@ -103,7 +95,7 @@ class NavigationHeader extends React.Component<DefaultProps, Props, any> {
};
static propTypes = {
...NavigationPropTypes.SceneRendererProps,
...NavigationPropTypes.SceneRenderer,
renderLeftComponent: PropTypes.func,
renderRightComponent: PropTypes.func,
renderTitleComponent: PropTypes.func,
@@ -207,11 +199,6 @@ class NavigationHeader extends React.Component<DefaultProps, Props, any> {
</Animated.View>
);
}
static HEIGHT = APPBAR_HEIGHT + STATUSBAR_HEIGHT;
static Title = NavigationHeaderTitle;
static BackButton = NavigationHeaderBackButton;
}
const styles = StyleSheet.create({
@@ -257,4 +244,10 @@ const styles = StyleSheet.create({
},
});
NavigationHeader = NavigationContainer.create(NavigationHeader);
NavigationHeader.HEIGHT = APPBAR_HEIGHT + STATUSBAR_HEIGHT;
NavigationHeader.Title = NavigationHeaderTitle;
NavigationHeader.BackButton = NavigationHeaderBackButton;
module.exports = NavigationHeader;
@@ -1,11 +1,4 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
@@ -25,6 +18,8 @@
const React = require('react');
const ReactNative = require('react-native');
const NavigationContainer = require('NavigationContainer');
const NavigationRootContainer = require('NavigationRootContainer');
const {
Image,
@@ -38,7 +33,7 @@ type Props = {
}
const NavigationHeaderBackButton = (props: Props) => (
<TouchableOpacity style={styles.buttonContainer} onPress={() => props.onNavigate({type: 'BackAction'})}>
<TouchableOpacity style={styles.buttonContainer} onPress={() => props.onNavigate(NavigationRootContainer.getBackAction())}>
<Image style={styles.button} source={require('./assets/back-icon.png')} />
</TouchableOpacity>
);
@@ -62,4 +57,4 @@ const styles = StyleSheet.create({
}
});
module.exports = NavigationHeaderBackButton;
module.exports = NavigationContainer.create(NavigationHeaderBackButton);
@@ -0,0 +1,446 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* Facebook, Inc. ("Facebook") owns all right, title and interest, including
* all intellectual property and other proprietary rights, in and to the React
* Native CustomComponents software (the "Software"). Subject to your
* compliance with these terms, you are hereby granted a non-exclusive,
* worldwide, royalty-free copyright license to (1) use and copy the Software;
* and (2) reproduce and distribute the Software as part of your own software
* ("Your Software"). Facebook reserves all rights not expressly granted to
* you in this license agreement.
*
* THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
* IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
* EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @providesModule NavigationLegacyNavigator
* @flow
*/
'use strict';
const Animated = require('Animated');
const NavigationAnimatedValueSubscription = require('NavigationAnimatedValueSubscription');
const NavigationAnimatedView = require('NavigationAnimatedView');
const NavigationCard = require('NavigationCard');
const NavigationCardStackStyleInterpolator = require('NavigationCardStackStyleInterpolator');
const NavigationContext = require('NavigationContext');
const NavigationLegacyNavigatorRouteStack = require('NavigationLegacyNavigatorRouteStack');
const NavigationCardStackPanResponder = require('NavigationCardStackPanResponder');
const NavigationPagerPanResponder = require('NavigationPagerPanResponder');
const NavigationPagerStyleInterpolator = require('NavigationPagerStyleInterpolator');
const NavigatorBreadcrumbNavigationBar = require('NavigatorBreadcrumbNavigationBar');
const NavigatorNavigationBar = require('NavigatorNavigationBar');
const NavigatorSceneConfigs = require('NavigatorSceneConfigs');
const React = require('React');
const ReactComponentWithPureRenderMixin = require('ReactComponentWithPureRenderMixin');
import type {
NavigationActionCaller,
NavigationAnimatedValue,
NavigationAnimationSetter,
NavigationParentState,
NavigationSceneRenderer,
NavigationSceneRendererProps,
} from 'NavigationTypeDefinition';
type Props = {
configureScene: any,
initialRoute: any,
initialRouteStack: any,
navigationBar: any,
navigationBarNavigator: any,
navigator: any,
onDidFocus: any,
onWillFocus: any,
renderScene: any,
renderScene: any,
style: any,
};
type State = {
presentedIndex: number,
routeStack: Array<any>,
};
const RouteStack = NavigationLegacyNavigatorRouteStack;
/**
* NavigationLegacyNavigator is meant to replace Navigator seemlessly with
* minimum API changes.
*
* While the APIs remain compatible with Navigator, it is built with good
* intention by using the new Navigation API such as
* `NavigationAnimatedView`...etc.
*/
class NavigationLegacyNavigator extends React.Component<any, Props, State> {
static BreadcrumbNavigationBar: any;
static NavigationBar: any;
static SceneConfigs: any;
_applyAnimation: NavigationAnimationSetter;
_navigationBarRef: any;
_onNavigationBarRef: (ref: any) => void;
_onPositionChange: (data: {value: number}) => void;
_positionListener: ?NavigationAnimatedValueSubscription;
_previousStack: NavigationLegacyNavigatorRouteStack;
_renderCard: NavigationSceneRenderer;
_renderHeader: NavigationSceneRenderer;
_renderScene: NavigationSceneRenderer;
_routeFocused: any;
_routeToFocus: any;
_onNavigate: NavigationActionCaller;
_stack: NavigationLegacyNavigatorRouteStack;
_useAnimation: boolean;
navigationContext: NavigationContext;
parentNavigator: any;
props: Props;
state: State;
constructor(props: Props, context: any) {
super(props, context);
const stack = this._getInitialRouteStack();
// Unfortunately, due to historical reasons, the `state` has been exposed
// as public members of the navigator, therefore we'd keep private state
// as private members.
this._previousStack = stack;
this._stack = stack;
this._useAnimation = false;
// Legacy members portred from `Navigator`.
this.parentNavigator = props.navigator;
this.navigationContext = new NavigationContext();
this.state = {
routeStack: stack.toArray(),
presentedIndex: stack.index,
};
}
jumpTo(route: any): void {
this._applyStack(this._stack.jumpTo(route));
}
jumpForward(): void {
this._applyStack(this._stack.jumpForward());
}
jumpBack(): void {
this._applyStack(this._stack.jumpBack());
}
push(route: any): void {
this._applyStack(this._stack.push(route));
}
pop(): void {
this._applyStack(this._stack.pop());
}
replaceAtIndex(route: any, index: number): void {
this._applyStack(this._stack.replaceAtIndex(index, route));
}
replace(route: any): void {
this.replaceAtIndex(route, this._stack.index);
}
replacePrevious(route: any): void {
this.replaceAtIndex(route, this._stack.index - 1);
}
popToTop(): void {
this._applyStack(this._stack.slice(0, 1));
}
popToRoute(route: any): void {
this._applyStack(this._stack.popToRoute(route));
}
replacePreviousAndPop(route: any): void {
this._applyStack(this._stack.replacePreviousAndPop(route));
}
resetTo(route: any): void {
this._applyStack(this._stack.resetTo(route));
}
immediatelyResetRouteStack(routes: Array<any>): void {
this._applyStack(this._stack.resetRoutes(routes), true);
}
getCurrentRoutes(): Array<any> {
return this._stack.toArray();
}
// Life cycle and private methods below.
shouldComponentUpdate(nextProps: Object, nextState: Object): boolean {
return ReactComponentWithPureRenderMixin.shouldComponentUpdate.call(
this,
nextProps,
nextState
);
}
componentWillMount(): void {
this._applyAnimation = this._applyAnimation.bind(this);
this._onNavigate = this._onNavigate.bind(this);
this._onNavigationBarRef = this._onNavigationBarRef.bind(this);
this._onPositionChange = this._onPositionChange.bind(this);
this._renderCard = this._renderCard.bind(this);
this._renderHeader = this._renderHeader.bind(this);
this._renderScene = this._renderScene.bind(this);
this._willFocus();
}
componentDidMount(): void {
this._didFocus();
}
componentWillUnmount(): void {
this._positionListener && this._positionListener.remove();
}
componentWillUpdate(nextProps: Props, nextState: State): void {
this._willFocus();
}
componentDidUpdate(prevProps: Props, prevState: State): void {
if (this._useAnimation) {
// will play animation.
return;
}
this._didFocus();
}
render(): ReactElement {
return (
<NavigationAnimatedView
applyAnimation={this._applyAnimation}
navigationState={this._stack.toNavigationState()}
onNavigate={this._onNavigate}
renderOverlay={this._renderHeader}
renderScene={this._renderCard}
style={this.props.style}
/>
);
}
_getInitialRouteStack(): RouteStack {
const {initialRouteStack, initialRoute} = this.props;
const routes = initialRouteStack || [initialRoute];
const index = initialRoute ?
routes.indexOf(initialRoute) :
routes.length - 1;
return new RouteStack(index, routes);
}
_renderHeader(props: NavigationSceneRendererProps): ?ReactElement {
// `_renderHeader` is always called before `_renderCard`. We should
// subscribe to the position here.
this._positionListener && this._positionListener.remove();
this._positionListener = new NavigationAnimatedValueSubscription(
props.position,
this._onPositionChange,
);
const {navigationBar, navigationBarNavigator} = this.props;
if (!navigationBar) {
return null;
}
return React.cloneElement(
navigationBar,
{
key: 'header_' + props.scene.key,
ref: this._onNavigationBarRef,
navigator: navigationBarNavigator || this,
navState: {...this.state},
}
);
}
_renderCard(props: NavigationSceneRendererProps): ReactElement {
const {scene} = props;
const {configureScene} = this.props;
// Default getters for style and pan responders.
let styleGetter = NavigationCardStackStyleInterpolator.forHorizontal;
let panResponderGetter = NavigationCardStackPanResponder.forHorizontal;
if (configureScene) {
const route = RouteStack.getRouteByNavigationState(scene.navigationState);
const config = configureScene(route, this.state.routeStack);
if (config) {
const gestures = config.gestures || {};
if (gestures.pop && gestures.pop.direction === 'left-to-right') {
// pass, will use default getters.
} else if (gestures.pop && gestures.pop.direction === 'top-to-bottom') {
styleGetter = NavigationCardStackStyleInterpolator.forVertical;
panResponderGetter = NavigationCardStackPanResponder.forVertical;
} else if (
gestures.jumpBack &&
gestures.jumpForward &&
gestures.jumpBack.direction === 'left-to-right' &&
gestures.jumpForward.direction === 'right-to-left'
) {
styleGetter = NavigationPagerStyleInterpolator.forHorizontal;
panResponderGetter = NavigationPagerPanResponder.forHorizontal;
} else if (__DEV__) {
console.warn('unsupported scene configuration', config);
}
}
}
const style = styleGetter(props);
const panHandlers = panResponderGetter(props);
return (
<NavigationCard
{...props}
key={'card_' + props.scene.key}
panHandlers={panHandlers}
renderScene={this._renderScene}
style={style}
/>
);
}
_renderScene(props: NavigationSceneRendererProps): ReactElement {
const {navigationState} = props.scene;
const route = RouteStack.getRouteByNavigationState(navigationState);
return this.props.renderScene(route, this);
}
_applyStack(
stack: NavigationLegacyNavigatorRouteStack,
noAnimation: ?boolean,
): void {
if (stack !== this._stack) {
this._previousStack = this._stack;
this._stack = stack;
this._useAnimation = noAnimation ||
this._previousStack.index !== stack.index;
this.setState({
presentedIndex: stack.index,
routeStack: stack.toArray(),
});
}
}
_onNavigationBarRef(navigationBarRef: any): void {
this._navigationBarRef = navigationBarRef;
const {navigationBar} = this.props;
if (navigationBar && typeof navigationBar.ref === 'function') {
navigationBar.ref(navigationBarRef);
}
}
_onPositionChange(data: {value: number}): void {
const fromIndex = this._previousStack.index;
const toIndex = this._stack.index;
if (
fromIndex !== toIndex &&
this._navigationBarRef &&
typeof this._navigationBarRef.updateProgress === 'function'
) {
const progress = (data.value - fromIndex) / (toIndex - fromIndex);
this._navigationBarRef.updateProgress(progress, fromIndex, toIndex);
}
const diff = this._stack.index - data.value;
// When animation stops, the `diff` can still be very a small non-zero value
// (e.g. 0.00000002). Call `willFocus` when `diff` is small enough.
if (diff < 0.05) {
this._didFocus();
}
}
_applyAnimation(
position: NavigationAnimatedValue,
nextState: NavigationParentState,
prevState: NavigationParentState,
): void {
const {index} = nextState;
if (!this._useAnimation) {
position.setValue(index);
return;
}
Animated.timing(
position,
{
duration: 500,
toValue: index,
}
).start();
}
_willFocus(): void {
const route = this._stack.get(this._stack.index);
if (this._routeToFocus === route) {
return;
}
this._routeToFocus = route;
this.navigationContext.emit('willfocus', {route: route});
this.props.onWillFocus && this.props.onWillFocus(route);
}
_didFocus(): void {
const route = this._stack.get(this._stack.index);
if (this._routeFocused === route) {
return;
}
this._routeFocused = route;
this.navigationContext.emit('didfocus', {route: route});
this.props.onDidFocus && this.props.onDidFocus(route);
}
_onNavigate(action: any): void {
switch (action) {
case NavigationCardStackPanResponder.Actions.BACK:
this.pop();
break;
case NavigationPagerPanResponder.Actions.JUMP_BACK:
this.jumpBack();
break;
case NavigationPagerPanResponder.Actions.JUMP_FORWARD:
this.jumpForward();
break;
default:
if (__DEV__) {
console.warn('unsupported gesture action', action);
}
}
}
}
// Legacy static members.
NavigationLegacyNavigator.BreadcrumbNavigationBar = NavigatorBreadcrumbNavigationBar;
NavigationLegacyNavigator.NavigationBar = NavigatorNavigationBar;
NavigationLegacyNavigator.SceneConfigs = NavigatorSceneConfigs;
module.exports = NavigationLegacyNavigator;

Some files were not shown because too many files have changed in this diff Show More