mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Compare commits
40
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b19128e34b | ||
|
|
0ea5f8138b | ||
|
|
1948a0d939 | ||
|
|
405c573e0d | ||
|
|
963fd60d52 | ||
|
|
adc0963b3e | ||
|
|
1fc664dee8 | ||
|
|
6c3360825b | ||
|
|
6cb80eac2d | ||
|
|
11f16ebdbb | ||
|
|
124783fbcc | ||
|
|
187a4698f0 | ||
|
|
4f816e7e88 | ||
|
|
c4ee9490be | ||
|
|
fa0e28efb4 | ||
|
|
0ad5eb73c5 | ||
|
|
b432b7adca | ||
|
|
c2839863e2 | ||
|
|
95aa86bda6 | ||
|
|
67260d1e6a | ||
|
|
bda953ea71 | ||
|
|
2b38ee9e25 | ||
|
|
c8b1c73bd4 | ||
|
|
ce0d641856 | ||
|
|
e2578ccdf0 | ||
|
|
5bec52bab6 | ||
|
|
376b586f80 | ||
|
|
a9d5a0c3b4 | ||
|
|
ba0b8f603a | ||
|
|
5f09ca4273 | ||
|
|
34d8a2b487 | ||
|
|
adeb5ff940 | ||
|
|
8589094d41 | ||
|
|
0adb1b35e8 | ||
|
|
0756c663bc | ||
|
|
9d6087ffc3 | ||
|
|
8b4d2376f5 | ||
|
|
3cdb567908 | ||
|
|
39eddc1a56 | ||
|
|
c72a44874b |
@@ -13,6 +13,7 @@
|
||||
|
||||
; Ignore unexpected extra "@providesModule"
|
||||
.*/node_modules/.*/node_modules/fbjs/.*
|
||||
+.*/node_modules/react-dom/.*
|
||||
|
||||
; Ignore duplicate module providers
|
||||
; For RN Apps installed via npm, "Libraries" folder is inside
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
|
||||
<!--Just to show permissions example-->
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
|
||||
@@ -27,9 +27,11 @@ var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
ActivityIndicator,
|
||||
Alert,
|
||||
CameraRoll,
|
||||
Image,
|
||||
ListView,
|
||||
PermissionsAndroid,
|
||||
Platform,
|
||||
StyleSheet,
|
||||
View,
|
||||
@@ -137,13 +139,27 @@ var CameraRollView = React.createClass({
|
||||
}
|
||||
},
|
||||
|
||||
_fetch: function(clear?: boolean) {
|
||||
_fetch: async function(clear?: boolean) {
|
||||
if (clear) {
|
||||
this.setState(this.getInitialState(), this.fetch);
|
||||
return;
|
||||
}
|
||||
|
||||
var fetchParams: Object = {
|
||||
if (Platform.OS === 'android') {
|
||||
const result = await PermissionsAndroid.request(
|
||||
PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
|
||||
{
|
||||
title: 'Permission Explanation',
|
||||
message: 'UIExplorer would like to access your pictures.',
|
||||
},
|
||||
);
|
||||
if (result !== 'granted') {
|
||||
Alert.alert('Access to pictures was denied.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const fetchParams: Object = {
|
||||
first: this.props.batchSize,
|
||||
groupTypes: this.props.groupTypes,
|
||||
assetType: this.props.assetType,
|
||||
@@ -156,8 +172,12 @@ var CameraRollView = React.createClass({
|
||||
fetchParams.after = this.state.lastCursor;
|
||||
}
|
||||
|
||||
CameraRoll.getPhotos(fetchParams)
|
||||
.then((data) => this._appendAssets(data), (e) => logError(e));
|
||||
try {
|
||||
const data = await CameraRoll.getPhotos(fetchParams);
|
||||
this._appendAssets(data);
|
||||
} catch (e) {
|
||||
logError(e);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -178,6 +198,7 @@ var CameraRollView = React.createClass({
|
||||
onEndReached={this._onEndReached}
|
||||
style={styles.container}
|
||||
dataSource={this.state.dataSource}
|
||||
enableEmptySections
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
const {
|
||||
Animated,
|
||||
FlatList,
|
||||
StyleSheet,
|
||||
View,
|
||||
@@ -47,6 +48,8 @@ const {
|
||||
renderSmallSwitchOption,
|
||||
} = require('./ListExampleShared');
|
||||
|
||||
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);
|
||||
|
||||
const VIEWABILITY_CONFIG = {
|
||||
minimumViewTime: 3000,
|
||||
viewAreaCoveragePercentThreshold: 100,
|
||||
@@ -66,18 +69,34 @@ class FlatListExample extends React.PureComponent {
|
||||
logViewable: false,
|
||||
virtualized: true,
|
||||
};
|
||||
|
||||
_onChangeFilterText = (filterText) => {
|
||||
this.setState({filterText});
|
||||
};
|
||||
|
||||
_onChangeScrollToIndex = (text) => {
|
||||
this._listRef.scrollToIndex({viewPosition: 0.5, index: Number(text)});
|
||||
this._listRef.getNode().scrollToIndex({viewPosition: 0.5, index: Number(text)});
|
||||
};
|
||||
|
||||
_scrollPos = new Animated.Value(0);
|
||||
_scrollSinkX = Animated.event(
|
||||
[{nativeEvent: { contentOffset: { x: this._scrollPos } }}],
|
||||
{useNativeDriver: true},
|
||||
);
|
||||
_scrollSinkY = Animated.event(
|
||||
[{nativeEvent: { contentOffset: { y: this._scrollPos } }}],
|
||||
{useNativeDriver: true},
|
||||
);
|
||||
|
||||
componentDidUpdate() {
|
||||
this._listRef.recordInteraction(); // e.g. flipping logViewable switch
|
||||
this._listRef.getNode().recordInteraction(); // e.g. flipping logViewable switch
|
||||
}
|
||||
|
||||
render() {
|
||||
const filterRegex = new RegExp(String(this.state.filterText), 'i');
|
||||
const filter = (item) => (filterRegex.test(item.text) || filterRegex.test(item.title));
|
||||
const filter = (item) => (
|
||||
filterRegex.test(item.text) || filterRegex.test(item.title)
|
||||
);
|
||||
const filteredData = this.state.data.filter(filter);
|
||||
return (
|
||||
<UIExplorerPage
|
||||
@@ -93,7 +112,6 @@ class FlatListExample extends React.PureComponent {
|
||||
<PlainInput
|
||||
onChangeText={this._onChangeScrollToIndex}
|
||||
placeholder="scrollToIndex..."
|
||||
style={styles.searchTextInput}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.options}>
|
||||
@@ -102,22 +120,37 @@ class FlatListExample extends React.PureComponent {
|
||||
{renderSmallSwitchOption(this, 'fixedHeight')}
|
||||
{renderSmallSwitchOption(this, 'logViewable')}
|
||||
{renderSmallSwitchOption(this, 'debug')}
|
||||
<Animated.View style={[styles.spindicator, {
|
||||
transform: [
|
||||
{rotate: this._scrollPos.interpolate({
|
||||
inputRange: [0, 5000],
|
||||
outputRange: ['0deg', '360deg'],
|
||||
extrapolate: 'extend',
|
||||
})}
|
||||
]
|
||||
}]} />
|
||||
</View>
|
||||
</View>
|
||||
<SeparatorComponent />
|
||||
<FlatList
|
||||
HeaderComponent={HeaderComponent}
|
||||
FooterComponent={FooterComponent}
|
||||
SeparatorComponent={SeparatorComponent}
|
||||
<AnimatedFlatList
|
||||
ItemSeparatorComponent={SeparatorComponent}
|
||||
ListHeaderComponent={HeaderComponent}
|
||||
ListFooterComponent={FooterComponent}
|
||||
data={filteredData}
|
||||
debug={this.state.debug}
|
||||
disableVirtualization={!this.state.virtualized}
|
||||
getItemLayout={this.state.fixedHeight ? this._getItemLayout : undefined}
|
||||
getItemLayout={this.state.fixedHeight ?
|
||||
this._getItemLayout :
|
||||
undefined
|
||||
}
|
||||
horizontal={this.state.horizontal}
|
||||
key={(this.state.horizontal ? 'h' : 'v') + (this.state.fixedHeight ? 'f' : 'd')}
|
||||
key={(this.state.horizontal ? 'h' : 'v') +
|
||||
(this.state.fixedHeight ? 'f' : 'd')
|
||||
}
|
||||
legacyImplementation={false}
|
||||
numColumns={1}
|
||||
onRefresh={this._onRefresh}
|
||||
onScroll={this.state.horizontal ? this._scrollSinkX : this._scrollSinkY}
|
||||
onViewableItemsChanged={this._onViewableItemsChanged}
|
||||
ref={this._captureRef}
|
||||
refreshing={false}
|
||||
@@ -145,26 +178,35 @@ class FlatListExample extends React.PureComponent {
|
||||
};
|
||||
_shouldItemUpdate(prev, next) {
|
||||
/**
|
||||
* Note that this does not check state.horizontal or state.fixedheight because we blow away the
|
||||
* whole list by changing the key in those cases. Make sure that you do the same in your code,
|
||||
* or incorporate all relevant data into the item data, or skip this optimization entirely.
|
||||
* Note that this does not check state.horizontal or state.fixedheight
|
||||
* because we blow away the whole list by changing the key in those cases.
|
||||
* Make sure that you do the same in your code, or incorporate all relevant
|
||||
* data into the item data, or skip this optimization entirely.
|
||||
*/
|
||||
return prev.item !== next.item;
|
||||
}
|
||||
// This is called when items change viewability by scrolling into or out of the viewable area.
|
||||
// This is called when items change viewability by scrolling into or out of
|
||||
// the viewable area.
|
||||
_onViewableItemsChanged = (info: {
|
||||
changed: Array<{
|
||||
key: string, isViewable: boolean, item: any, index: ?number, section?: any
|
||||
key: string,
|
||||
isViewable: boolean,
|
||||
item: any,
|
||||
index: ?number,
|
||||
section?: any,
|
||||
}>
|
||||
}
|
||||
) => {
|
||||
// Impressions can be logged here
|
||||
if (this.state.logViewable) {
|
||||
infoLog('onViewableItemsChanged: ', info.changed.map((v) => ({...v, item: '...'})));
|
||||
infoLog(
|
||||
'onViewableItemsChanged: ',
|
||||
info.changed.map((v) => ({...v, item: '...'})),
|
||||
);
|
||||
}
|
||||
};
|
||||
_pressItem = (key: number) => {
|
||||
this._listRef.recordInteraction();
|
||||
this._listRef.getNode().recordInteraction();
|
||||
pressItem(this, key);
|
||||
};
|
||||
_listRef: FlatList<*>;
|
||||
@@ -180,6 +222,12 @@ const styles = StyleSheet.create({
|
||||
searchRow: {
|
||||
paddingHorizontal: 10,
|
||||
},
|
||||
spindicator: {
|
||||
marginLeft: 'auto',
|
||||
width: 2,
|
||||
height: 16,
|
||||
backgroundColor: 'darkgray',
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = FlatListExample;
|
||||
|
||||
@@ -97,9 +97,9 @@ class MultiColumnExample extends React.PureComponent {
|
||||
</View>
|
||||
<SeparatorComponent />
|
||||
<FlatList
|
||||
FooterComponent={FooterComponent}
|
||||
HeaderComponent={HeaderComponent}
|
||||
SeparatorComponent={SeparatorComponent}
|
||||
ItemSeparatorComponent={SeparatorComponent}
|
||||
ListFooterComponent={FooterComponent}
|
||||
ListHeaderComponent={HeaderComponent}
|
||||
getItemLayout={this.state.fixedHeight ? this._getItemLayout : undefined}
|
||||
data={filteredData}
|
||||
key={this.state.numColumns + (this.state.fixedHeight ? 'f' : 'v')}
|
||||
|
||||
-486
@@ -1,486 +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.
|
||||
*
|
||||
* 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';
|
||||
|
||||
const NavigationExampleRow = require('./NavigationExampleRow');
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
|
||||
/**
|
||||
* Basic example that shows how to use <NavigationCardStack /> to build
|
||||
* an app with composite navigation system.
|
||||
* @providesModule NavigationCardStack-NavigationHeader-Tabs-example
|
||||
*/
|
||||
|
||||
const {
|
||||
Component,
|
||||
PropTypes,
|
||||
} = React;
|
||||
|
||||
const {
|
||||
NavigationExperimental,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
const {
|
||||
CardStack: NavigationCardStack,
|
||||
Header: NavigationHeader,
|
||||
PropTypes: NavigationPropTypes,
|
||||
StateUtils: NavigationStateUtils,
|
||||
} = NavigationExperimental;
|
||||
|
||||
// First Step.
|
||||
// Define what app navigation state will look like.
|
||||
function createAppNavigationState(): Object {
|
||||
return {
|
||||
// Three tabs.
|
||||
tabs: {
|
||||
index: 0,
|
||||
routes: [
|
||||
{key: 'apple'},
|
||||
{key: 'banana'},
|
||||
{key: 'orange'},
|
||||
],
|
||||
},
|
||||
// Scenes for the `apple` tab.
|
||||
apple: {
|
||||
index: 0,
|
||||
routes: [{key: 'Apple Home'}],
|
||||
},
|
||||
// Scenes for the `banana` tab.
|
||||
banana: {
|
||||
index: 0,
|
||||
routes: [{key: 'Banana Home'}],
|
||||
},
|
||||
// Scenes for the `orange` tab.
|
||||
orange: {
|
||||
index: 0,
|
||||
routes: [{key: 'Orange Home'}],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Next step.
|
||||
// Define what app navigation state shall be updated.
|
||||
function updateAppNavigationState(
|
||||
state: Object,
|
||||
action: Object,
|
||||
): Object {
|
||||
let {type} = action;
|
||||
if (type === 'BackAction') {
|
||||
type = 'pop';
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'push': {
|
||||
// Push a route into the scenes stack.
|
||||
const route: Object = action.route;
|
||||
const {tabs} = state;
|
||||
const tabKey = tabs.routes[tabs.index].key;
|
||||
const scenes = state[tabKey];
|
||||
const nextScenes = NavigationStateUtils.push(scenes, route);
|
||||
if (scenes !== nextScenes) {
|
||||
return {
|
||||
...state,
|
||||
[tabKey]: nextScenes,
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'pop': {
|
||||
// Pops a route from the scenes stack.
|
||||
const {tabs} = state;
|
||||
const tabKey = tabs.routes[tabs.index].key;
|
||||
const scenes = state[tabKey];
|
||||
const nextScenes = NavigationStateUtils.pop(scenes);
|
||||
if (scenes !== nextScenes) {
|
||||
return {
|
||||
...state,
|
||||
[tabKey]: nextScenes,
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'selectTab': {
|
||||
// Switches the tab.
|
||||
const tabKey: string = action.tabKey;
|
||||
const tabs = NavigationStateUtils.jumpTo(state.tabs, tabKey);
|
||||
if (tabs !== state.tabs) {
|
||||
return {
|
||||
...state,
|
||||
tabs,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
// Next step.
|
||||
// Defines a helper function that creates a HOC (higher-order-component)
|
||||
// which provides a function `navigate` through component props. The
|
||||
// `navigate` function will be used to invoke navigation changes.
|
||||
// This serves a convenient way for a component to navigate.
|
||||
function createAppNavigationContainer(ComponentClass) {
|
||||
const key = '_yourAppNavigationContainerNavigateCall';
|
||||
|
||||
class Container extends Component {
|
||||
static contextTypes = {
|
||||
[key]: PropTypes.func,
|
||||
};
|
||||
|
||||
static childContextTypes = {
|
||||
[key]: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
navigate: PropTypes.func,
|
||||
};
|
||||
|
||||
getChildContext(): Object {
|
||||
return {
|
||||
[key]: this.context[key] || this.props.navigate,
|
||||
};
|
||||
}
|
||||
|
||||
render(): React.Element {
|
||||
const navigate = this.context[key] || this.props.navigate;
|
||||
return <ComponentClass {...this.props} navigate={navigate} />;
|
||||
}
|
||||
}
|
||||
|
||||
return Container;
|
||||
}
|
||||
|
||||
// Next step.
|
||||
// Define a component for your application that owns the navigation state.
|
||||
class YourApplication extends Component {
|
||||
|
||||
static propTypes = {
|
||||
onExampleExit: PropTypes.func,
|
||||
};
|
||||
|
||||
// This sets up the initial navigation state.
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
// This sets up the initial navigation state.
|
||||
this.state = createAppNavigationState();
|
||||
this._navigate = this._navigate.bind(this);
|
||||
}
|
||||
|
||||
render(): React.Element {
|
||||
// User your own navigator (see next step).
|
||||
return (
|
||||
<YourNavigator
|
||||
appNavigationState={this.state}
|
||||
navigate={this._navigate}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// This public method is optional. If exists, the UI explorer will call it
|
||||
// the "back button" is pressed. Normally this is the cases for Android only.
|
||||
handleBackAction(): boolean {
|
||||
return this._navigate({type: 'pop'});
|
||||
}
|
||||
|
||||
// This handles the navigation state changes. You're free and responsible
|
||||
// to define the API that changes that navigation state. In this exmaple,
|
||||
// we'd simply use a `updateAppNavigationState` to update the navigation
|
||||
// state.
|
||||
_navigate(action: Object): void {
|
||||
if (action.type === 'exit') {
|
||||
// Exits the example. `this.props.onExampleExit` is provided
|
||||
// by the UI Explorer.
|
||||
this.props.onExampleExit && this.props.onExampleExit();
|
||||
return;
|
||||
}
|
||||
|
||||
const state = updateAppNavigationState(
|
||||
this.state,
|
||||
action,
|
||||
);
|
||||
|
||||
// `updateAppNavigationState` (which uses NavigationStateUtils) gives you
|
||||
// back the same `state` if nothing has changed. You could use
|
||||
// that to avoid redundant re-rendering.
|
||||
if (this.state !== state) {
|
||||
this.setState(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Next step.
|
||||
// Define your own controlled navigator.
|
||||
const YourNavigator = createAppNavigationContainer(class extends Component {
|
||||
static propTypes = {
|
||||
appNavigationState: PropTypes.shape({
|
||||
apple: NavigationPropTypes.navigationState.isRequired,
|
||||
banana: NavigationPropTypes.navigationState.isRequired,
|
||||
orange: NavigationPropTypes.navigationState.isRequired,
|
||||
tabs: NavigationPropTypes.navigationState.isRequired,
|
||||
}),
|
||||
navigate: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
// This sets up the methods (e.g. Pop, Push) for navigation.
|
||||
constructor(props: any, context: any) {
|
||||
super(props, context);
|
||||
this._back = this._back.bind(this);
|
||||
this._renderHeader = this._renderHeader.bind(this);
|
||||
this._renderScene = this._renderScene.bind(this);
|
||||
}
|
||||
|
||||
// Now use the `NavigationCardStack` to render the scenes.
|
||||
render(): React.Element {
|
||||
const {appNavigationState} = this.props;
|
||||
const {tabs} = appNavigationState;
|
||||
const tabKey = tabs.routes[tabs.index].key;
|
||||
const scenes = appNavigationState[tabKey];
|
||||
|
||||
return (
|
||||
<View style={styles.navigator}>
|
||||
<NavigationCardStack
|
||||
key={'stack_' + tabKey}
|
||||
onNavigateBack={this._back}
|
||||
navigationState={scenes}
|
||||
renderHeader={this._renderHeader}
|
||||
renderScene={this._renderScene}
|
||||
style={styles.navigatorCardStack}
|
||||
/>
|
||||
<YourTabs
|
||||
navigationState={tabs}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
// Render the header.
|
||||
// The detailed spec of `sceneProps` is defined at `NavigationTypeDefinition`
|
||||
// as type `NavigationSceneRendererProps`.
|
||||
_renderHeader(sceneProps: Object): React.Element {
|
||||
return (
|
||||
<YourHeader
|
||||
{...sceneProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Render a scene for route.
|
||||
// The detailed spec of `sceneProps` is defined at `NavigationTypeDefinition`
|
||||
// as type `NavigationSceneRendererProps`.
|
||||
_renderScene(sceneProps: Object): React.Element {
|
||||
return (
|
||||
<YourScene
|
||||
{...sceneProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
_back() {
|
||||
this.props.navigate({type: 'pop'});
|
||||
}
|
||||
});
|
||||
|
||||
// Next step.
|
||||
// Define your own header.
|
||||
const YourHeader = createAppNavigationContainer(class extends Component {
|
||||
static propTypes = {
|
||||
...NavigationPropTypes.SceneRendererProps,
|
||||
navigate: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
constructor(props: Object, context: any) {
|
||||
super(props, context);
|
||||
this._back = this._back.bind(this);
|
||||
this._renderTitleComponent = this._renderTitleComponent.bind(this);
|
||||
}
|
||||
|
||||
render(): React.Element {
|
||||
return (
|
||||
<NavigationHeader
|
||||
{...this.props}
|
||||
renderTitleComponent={this._renderTitleComponent}
|
||||
onNavigateBack={this._back}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
_back(): void {
|
||||
this.props.navigate({type: 'pop'});
|
||||
}
|
||||
|
||||
_renderTitleComponent(props: Object): React.Element {
|
||||
return (
|
||||
<NavigationHeader.Title>
|
||||
{props.scene.route.key}
|
||||
</NavigationHeader.Title>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Next step.
|
||||
// Define your own scene.
|
||||
const YourScene = createAppNavigationContainer(class extends Component {
|
||||
static propTypes = {
|
||||
...NavigationPropTypes.SceneRendererProps,
|
||||
navigate: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
constructor(props: Object, context: any) {
|
||||
super(props, context);
|
||||
this._exit = this._exit.bind(this);
|
||||
this._popRoute = this._popRoute.bind(this);
|
||||
this._pushRoute = this._pushRoute.bind(this);
|
||||
}
|
||||
|
||||
render(): React.Element {
|
||||
return (
|
||||
<ScrollView>
|
||||
<NavigationExampleRow
|
||||
text="Push Route"
|
||||
onPress={this._pushRoute}
|
||||
/>
|
||||
<NavigationExampleRow
|
||||
text="Pop Route"
|
||||
onPress={this._popRoute}
|
||||
/>
|
||||
<NavigationExampleRow
|
||||
text="Exit Header + Scenes + Tabs Example"
|
||||
onPress={this._exit}
|
||||
/>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
_pushRoute(): void {
|
||||
// Just push a route with a new unique key.
|
||||
const route = {key: '[' + this.props.scenes.length + ']-' + Date.now()};
|
||||
this.props.navigate({type: 'push', route});
|
||||
}
|
||||
|
||||
_popRoute(): void {
|
||||
this.props.navigate({type: 'pop'});
|
||||
}
|
||||
|
||||
_exit(): void {
|
||||
this.props.navigate({type: 'exit'});
|
||||
}
|
||||
});
|
||||
|
||||
// Next step.
|
||||
// Define your own tabs.
|
||||
const YourTabs = createAppNavigationContainer(class extends Component {
|
||||
static propTypes = {
|
||||
navigationState: NavigationPropTypes.navigationState.isRequired,
|
||||
navigate: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
constructor(props: Object, context: any) {
|
||||
super(props, context);
|
||||
}
|
||||
|
||||
render(): React.Element {
|
||||
return (
|
||||
<View style={styles.tabs}>
|
||||
{this.props.navigationState.routes.map(this._renderTab, this)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_renderTab(route: Object, index: number): React.Element {
|
||||
return (
|
||||
<YourTab
|
||||
key={route.key}
|
||||
route={route}
|
||||
selected={this.props.navigationState.index === index}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Next step.
|
||||
// Define your own Tab
|
||||
const YourTab = createAppNavigationContainer(class extends Component {
|
||||
|
||||
static propTypes = {
|
||||
navigate: PropTypes.func.isRequired,
|
||||
route: NavigationPropTypes.navigationRoute.isRequired,
|
||||
selected: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
constructor(props: Object, context: any) {
|
||||
super(props, context);
|
||||
this._onPress = this._onPress.bind(this);
|
||||
}
|
||||
|
||||
render(): React.Element {
|
||||
const style = [styles.tabText];
|
||||
if (this.props.selected) {
|
||||
style.push(styles.tabSelected);
|
||||
}
|
||||
return (
|
||||
<TouchableOpacity style={styles.tab} onPress={this._onPress}>
|
||||
<Text style={style}>
|
||||
{this.props.route.key}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
_onPress() {
|
||||
this.props.navigate({type: 'selectTab', tabKey: this.props.route.key});
|
||||
}
|
||||
});
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
navigator: {
|
||||
flex: 1,
|
||||
},
|
||||
navigatorCardStack: {
|
||||
flex: 20,
|
||||
},
|
||||
tabs: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
},
|
||||
tab: {
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#fff',
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
tabText: {
|
||||
color: '#222',
|
||||
fontWeight: '500',
|
||||
},
|
||||
tabSelected: {
|
||||
color: 'blue',
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = YourApplication;
|
||||
-198
@@ -1,198 +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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @providesModule NavigationCardStack-NoGesture-example
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const NavigationExampleRow = require('./NavigationExampleRow');
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
|
||||
/**
|
||||
* Basic example that shows how to use <NavigationCardStack /> to build
|
||||
* an app with controlled navigation system but without gestures.
|
||||
*/
|
||||
const {
|
||||
NavigationExperimental,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
} = ReactNative;
|
||||
|
||||
const {
|
||||
CardStack: NavigationCardStack,
|
||||
StateUtils: NavigationStateUtils,
|
||||
} = NavigationExperimental;
|
||||
|
||||
// Step 1:
|
||||
// Define a component for your application.
|
||||
class YourApplication extends React.Component {
|
||||
|
||||
// This sets up the initial navigation state.
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
// This defines the initial navigation state.
|
||||
navigationState: {
|
||||
index: 0, // starts with first route focused.
|
||||
routes: [{key: 'Welcome'}], // starts with only one route.
|
||||
},
|
||||
};
|
||||
|
||||
this._exit = this._exit.bind(this);
|
||||
this._onNavigationChange = this._onNavigationChange.bind(this);
|
||||
}
|
||||
|
||||
// User your own navigator (see Step 2).
|
||||
render(): React.Element {
|
||||
return (
|
||||
<YourNavigator
|
||||
navigationState={this.state.navigationState}
|
||||
onNavigationChange={this._onNavigationChange}
|
||||
onExit={this._exit}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// This handles the navigation state changes. You're free and responsible
|
||||
// to define the API that changes that navigation state. In this exmaple,
|
||||
// we'd simply use a `function(type: string)` to update the navigation state.
|
||||
_onNavigationChange(type: string): void {
|
||||
let {navigationState} = this.state;
|
||||
switch (type) {
|
||||
case 'push':
|
||||
// push a new route.
|
||||
const route = {key: 'route-' + Date.now()};
|
||||
navigationState = NavigationStateUtils.push(navigationState, route);
|
||||
break;
|
||||
|
||||
case 'pop':
|
||||
navigationState = NavigationStateUtils.pop(navigationState);
|
||||
break;
|
||||
}
|
||||
|
||||
// NavigationStateUtils gives you back the same `navigationState` if nothing
|
||||
// has changed. You could use that to avoid redundant re-rendering.
|
||||
if (this.state.navigationState !== navigationState) {
|
||||
this.setState({navigationState});
|
||||
}
|
||||
}
|
||||
|
||||
// Exits the example. `this.props.onExampleExit` is provided
|
||||
// by the UI Explorer.
|
||||
_exit(): void {
|
||||
this.props.onExampleExit && this.props.onExampleExit();
|
||||
}
|
||||
|
||||
// This public method is optional. If exists, the UI explorer will call it
|
||||
// the "back button" is pressed. Normally this is the cases for Android only.
|
||||
handleBackAction(): boolean {
|
||||
return this._onNavigationChange('pop');
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2:
|
||||
// Define your own controlled navigator.
|
||||
//
|
||||
// +------------+
|
||||
// +-+ |
|
||||
// +-+ | |
|
||||
// | | | |
|
||||
// | | | Active |
|
||||
// | | | Scene |
|
||||
// | | | |
|
||||
// +-+ | |
|
||||
// +-+ |
|
||||
// +------------+
|
||||
//
|
||||
class YourNavigator extends React.Component {
|
||||
|
||||
// This sets up the methods (e.g. Pop, Push) for navigation.
|
||||
constructor(props: any, context: any) {
|
||||
super(props, context);
|
||||
|
||||
this._onPushRoute = this.props.onNavigationChange.bind(null, 'push');
|
||||
this._onPopRoute = this.props.onNavigationChange.bind(null, 'pop');
|
||||
|
||||
this._renderScene = this._renderScene.bind(this);
|
||||
}
|
||||
|
||||
// Now use the `NavigationCardStack` to render the scenes.
|
||||
render(): React.Element {
|
||||
return (
|
||||
<NavigationCardStack
|
||||
onNavigateBack={this._onPopRoute}
|
||||
navigationState={this.props.navigationState}
|
||||
renderScene={this._renderScene}
|
||||
style={styles.navigator}
|
||||
enableGestures={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Render a scene for route.
|
||||
// The detailed spec of `sceneProps` is defined at `NavigationTypeDefinition`
|
||||
// as type `NavigationSceneRendererProps`.
|
||||
_renderScene(sceneProps: Object): React.Element {
|
||||
return (
|
||||
<YourScene
|
||||
route={sceneProps.scene.route}
|
||||
onPushRoute={this._onPushRoute}
|
||||
onPopRoute={this._onPopRoute}
|
||||
onExit={this.props.onExit}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3:
|
||||
// Define your own scene.
|
||||
class YourScene extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<ScrollView>
|
||||
<NavigationExampleRow
|
||||
text={'route = ' + this.props.route.key}
|
||||
/>
|
||||
<NavigationExampleRow
|
||||
text="Push Route"
|
||||
onPress={this.props.onPushRoute}
|
||||
/>
|
||||
<NavigationExampleRow
|
||||
text="Pop Route"
|
||||
onPress={this.props.onPopRoute}
|
||||
/>
|
||||
<NavigationExampleRow
|
||||
text="Exit Card Stack Example"
|
||||
onPress={this.props.onExit}
|
||||
/>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
navigator: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = YourApplication;
|
||||
@@ -1,196 +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.
|
||||
*
|
||||
* 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.
|
||||
* @providesModule NavigationCardStack-example
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const NavigationExampleRow = require('./NavigationExampleRow');
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
|
||||
/**
|
||||
* Basic example that shows how to use <NavigationCardStack /> to build
|
||||
* an app with controlled navigation system.
|
||||
*/
|
||||
const {
|
||||
NavigationExperimental,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
} = ReactNative;
|
||||
|
||||
const {
|
||||
CardStack: NavigationCardStack,
|
||||
StateUtils: NavigationStateUtils,
|
||||
} = NavigationExperimental;
|
||||
|
||||
// Step 1:
|
||||
// Define a component for your application.
|
||||
class YourApplication extends React.Component {
|
||||
|
||||
// This sets up the initial navigation state.
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
// This defines the initial navigation state.
|
||||
navigationState: {
|
||||
index: 0, // starts with first route focused.
|
||||
routes: [{key: 'Welcome'}], // starts with only one route.
|
||||
},
|
||||
};
|
||||
|
||||
this._exit = this._exit.bind(this);
|
||||
this._onNavigationChange = this._onNavigationChange.bind(this);
|
||||
}
|
||||
|
||||
// User your own navigator (see Step 2).
|
||||
render(): React.Element {
|
||||
return (
|
||||
<YourNavigator
|
||||
navigationState={this.state.navigationState}
|
||||
onNavigationChange={this._onNavigationChange}
|
||||
onExit={this._exit}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// This handles the navigation state changes. You're free and responsible
|
||||
// to define the API that changes that navigation state. In this exmaple,
|
||||
// we'd simply use a `function(type: string)` to update the navigation state.
|
||||
_onNavigationChange(type: string): void {
|
||||
let {navigationState} = this.state;
|
||||
switch (type) {
|
||||
case 'push':
|
||||
// push a new route.
|
||||
const route = {key: 'route-' + Date.now()};
|
||||
navigationState = NavigationStateUtils.push(navigationState, route);
|
||||
break;
|
||||
|
||||
case 'pop':
|
||||
navigationState = NavigationStateUtils.pop(navigationState);
|
||||
break;
|
||||
}
|
||||
|
||||
// NavigationStateUtils gives you back the same `navigationState` if nothing
|
||||
// has changed. You could use that to avoid redundant re-rendering.
|
||||
if (this.state.navigationState !== navigationState) {
|
||||
this.setState({navigationState});
|
||||
}
|
||||
}
|
||||
|
||||
// Exits the example. `this.props.onExampleExit` is provided
|
||||
// by the UI Explorer.
|
||||
_exit(): void {
|
||||
this.props.onExampleExit && this.props.onExampleExit();
|
||||
}
|
||||
|
||||
// This public method is optional. If exists, the UI explorer will call it
|
||||
// the "back button" is pressed. Normally this is the cases for Android only.
|
||||
handleBackAction(): boolean {
|
||||
return this._onNavigationChange('pop');
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2:
|
||||
// Define your own controlled navigator.
|
||||
//
|
||||
// +------------+
|
||||
// +-+ |
|
||||
// +-+ | |
|
||||
// | | | |
|
||||
// | | | Active |
|
||||
// | | | Scene |
|
||||
// | | | |
|
||||
// +-+ | |
|
||||
// +-+ |
|
||||
// +------------+
|
||||
//
|
||||
class YourNavigator extends React.Component {
|
||||
|
||||
// This sets up the methods (e.g. Pop, Push) for navigation.
|
||||
constructor(props: any, context: any) {
|
||||
super(props, context);
|
||||
|
||||
this._onPushRoute = this.props.onNavigationChange.bind(null, 'push');
|
||||
this._onPopRoute = this.props.onNavigationChange.bind(null, 'pop');
|
||||
|
||||
this._renderScene = this._renderScene.bind(this);
|
||||
}
|
||||
|
||||
// Now use the `NavigationCardStack` to render the scenes.
|
||||
render(): React.Element {
|
||||
return (
|
||||
<NavigationCardStack
|
||||
onNavigateBack={this._onPopRoute}
|
||||
navigationState={this.props.navigationState}
|
||||
renderScene={this._renderScene}
|
||||
style={styles.navigator}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Render a scene for route.
|
||||
// The detailed spec of `sceneProps` is defined at `NavigationTypeDefinition`
|
||||
// as type `NavigationSceneRendererProps`.
|
||||
_renderScene(sceneProps: Object): React.Element {
|
||||
return (
|
||||
<YourScene
|
||||
route={sceneProps.scene.route}
|
||||
onPushRoute={this._onPushRoute}
|
||||
onPopRoute={this._onPopRoute}
|
||||
onExit={this.props.onExit}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3:
|
||||
// Define your own scene.
|
||||
class YourScene extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<ScrollView>
|
||||
<NavigationExampleRow
|
||||
text={'route = ' + this.props.route.key}
|
||||
/>
|
||||
<NavigationExampleRow
|
||||
text="Push Route"
|
||||
onPress={this.props.onPushRoute}
|
||||
/>
|
||||
<NavigationExampleRow
|
||||
text="Pop Route"
|
||||
onPress={this.props.onPopRoute}
|
||||
/>
|
||||
<NavigationExampleRow
|
||||
text="Exit Card Stack Example"
|
||||
onPress={this.props.onExit}
|
||||
/>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
navigator: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = YourApplication;
|
||||
@@ -1,74 +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.
|
||||
*
|
||||
* 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.
|
||||
* @providesModule NavigationExampleRow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
Text,
|
||||
PixelRatio,
|
||||
StyleSheet,
|
||||
View,
|
||||
TouchableHighlight,
|
||||
} = ReactNative;
|
||||
|
||||
class NavigationExampleRow extends React.Component {
|
||||
render() {
|
||||
if (this.props.onPress) {
|
||||
return (
|
||||
<TouchableHighlight
|
||||
style={styles.row}
|
||||
underlayColor="#D0D0D0"
|
||||
onPress={this.props.onPress}>
|
||||
<Text style={styles.buttonText}>
|
||||
{this.props.text}
|
||||
</Text>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<View style={styles.row}>
|
||||
<Text style={styles.rowText}>
|
||||
{this.props.text}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
row: {
|
||||
padding: 15,
|
||||
backgroundColor: 'white',
|
||||
borderBottomWidth: 1 / PixelRatio.get(),
|
||||
borderBottomColor: '#CDCDCD',
|
||||
},
|
||||
rowText: {
|
||||
fontSize: 17,
|
||||
},
|
||||
buttonText: {
|
||||
fontSize: 17,
|
||||
fontWeight: '500',
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = NavigationExampleRow;
|
||||
@@ -1,153 +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.
|
||||
*
|
||||
* 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.
|
||||
* @providesModule NavigationExperimentalExample
|
||||
*/
|
||||
'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');
|
||||
|
||||
/*
|
||||
* 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 = {
|
||||
'CardStack + Header + Tabs Example': require('./NavigationCardStack-NavigationHeader-Tabs-example'),
|
||||
'CardStack Example': require('./NavigationCardStack-example'),
|
||||
'CardStack Without Gestures Example': require('./NavigationCardStack-NoGesture-example'),
|
||||
'Transitioner + Animated View Example': require('./NavigationTransitioner-AnimatedView-example'),
|
||||
'Transitioner + Animated View Pager Example': require('./NavigationTransitioner-AnimatedView-pager-example'),
|
||||
};
|
||||
|
||||
const EXAMPLE_STORAGE_KEY = 'NavigationExperimentalExample';
|
||||
|
||||
class NavigationExperimentalExample extends React.Component {
|
||||
static title = 'Navigation (Experimental)';
|
||||
static description = 'Upcoming navigation APIs and animated navigation views';
|
||||
static external = true;
|
||||
|
||||
state = {
|
||||
example: null,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
AsyncStorage.getItem(EXAMPLE_STORAGE_KEY, (err, example) => {
|
||||
if (err || !example || !EXAMPLES[example]) {
|
||||
this.setState({
|
||||
example: 'menu',
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
example,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
setExample = (example) => {
|
||||
this.setState({
|
||||
example,
|
||||
});
|
||||
AsyncStorage.setItem(EXAMPLE_STORAGE_KEY, example);
|
||||
};
|
||||
|
||||
_renderMenu = () => {
|
||||
let exitRow = null;
|
||||
if (this.props.onExampleExit) {
|
||||
exitRow = (
|
||||
<NavigationExampleRow
|
||||
text="Exit Navigation Examples"
|
||||
onPress={this.props.onExampleExit}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<View style={styles.menu}>
|
||||
<ScrollView>
|
||||
{this._renderExampleList()}
|
||||
{exitRow}
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
_renderExampleList = () => {
|
||||
return Object.keys(EXAMPLES).map(exampleName => (
|
||||
<NavigationExampleRow
|
||||
key={exampleName}
|
||||
text={exampleName}
|
||||
onPress={() => {
|
||||
this.setExample(exampleName);
|
||||
}}
|
||||
/>
|
||||
));
|
||||
};
|
||||
|
||||
_exitInnerExample = () => {
|
||||
this.setExample('menu');
|
||||
};
|
||||
|
||||
handleBackAction = () => {
|
||||
const wasHandledByExample = (
|
||||
this.exampleRef &&
|
||||
this.exampleRef.handleBackAction &&
|
||||
this.exampleRef.handleBackAction()
|
||||
);
|
||||
if (wasHandledByExample) {
|
||||
return true;
|
||||
}
|
||||
if (this.state.example && this.state.example !== 'menu') {
|
||||
this._exitInnerExample();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.example === 'menu') {
|
||||
return this._renderMenu();
|
||||
}
|
||||
if (EXAMPLES[this.state.example]) {
|
||||
const Component = EXAMPLES[this.state.example];
|
||||
return (
|
||||
<Component
|
||||
onExampleExit={this._exitInnerExample}
|
||||
ref={exampleRef => { this.exampleRef = exampleRef; }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
menu: {
|
||||
backgroundColor: '#E9E9EF',
|
||||
flex: 1,
|
||||
marginTop: 20,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = NavigationExperimentalExample;
|
||||
-256
@@ -1,256 +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.
|
||||
*
|
||||
* 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
|
||||
* @providesModule NavigationTransitioner-AnimatedView-example
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const NavigationExampleRow = require('./NavigationExampleRow');
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
|
||||
/**
|
||||
* Basic example that shows how to use <NavigationTransitioner /> and
|
||||
* <Animated.View /> to build a stack of animated scenes that render the
|
||||
* navigation state.
|
||||
*/
|
||||
|
||||
|
||||
import type {
|
||||
NavigationSceneRendererProps,
|
||||
NavigationState,
|
||||
NavigationTransitionProps,
|
||||
NavigationTransitionSpec,
|
||||
} from 'NavigationTypeDefinition';
|
||||
|
||||
const {
|
||||
Component,
|
||||
PropTypes,
|
||||
} = React;
|
||||
|
||||
const {
|
||||
Animated,
|
||||
Easing,
|
||||
NavigationExperimental,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
} = ReactNative;
|
||||
|
||||
const {
|
||||
PropTypes: NavigationPropTypes,
|
||||
StateUtils: NavigationStateUtils,
|
||||
Transitioner: NavigationTransitioner,
|
||||
} = NavigationExperimental;
|
||||
|
||||
function reducer(state: ?NavigationState, action: any): NavigationState {
|
||||
if (!state) {
|
||||
return {
|
||||
index: 0,
|
||||
routes: [{key: 'route-1'}],
|
||||
};
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case 'push':
|
||||
const route = {key: 'route-' + (state.routes.length + 1)};
|
||||
return NavigationStateUtils.push(state, route);
|
||||
case 'pop':
|
||||
return NavigationStateUtils.pop(state);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
class Example extends Component {
|
||||
state: NavigationState;
|
||||
constructor(props: any, context: any) {
|
||||
super(props, context);
|
||||
this.state = reducer();
|
||||
}
|
||||
|
||||
render(): React.Element<any> {
|
||||
return (
|
||||
<ExampleNavigator
|
||||
navigationState={this.state}
|
||||
navigate={action => this._navigate(action)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
_navigate(action: any): boolean {
|
||||
if (action === 'exit') {
|
||||
// Exits the example. `this.props.onExampleExit` is provided
|
||||
// by the UI Explorer.
|
||||
this.props.onExampleExit && this.props.onExampleExit();
|
||||
return false;
|
||||
}
|
||||
|
||||
const state = reducer(this.state, action);
|
||||
if (state === this.state) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.setState(state);
|
||||
return true;
|
||||
}
|
||||
|
||||
// This public method is optional. If exists, the UI explorer will call it
|
||||
// the "back button" is pressed. Normally this is the cases for Android only.
|
||||
handleBackAction(): boolean {
|
||||
return this._navigate('pop');
|
||||
}
|
||||
}
|
||||
|
||||
class ExampleNavigator extends Component {
|
||||
props: {
|
||||
navigate: Function,
|
||||
navigationState: NavigationState,
|
||||
};
|
||||
|
||||
static propTypes: {
|
||||
navigationState: NavigationPropTypes.navigationState.isRequired,
|
||||
navigate: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
render(): React.Element<any> {
|
||||
return (
|
||||
<NavigationTransitioner
|
||||
navigationState={this.props.navigationState}
|
||||
render={(transitionProps) => this._render(transitionProps)}
|
||||
configureTransition={this._configureTransition}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
_render(
|
||||
transitionProps: NavigationTransitionProps,
|
||||
): Array<React.Element<any>> {
|
||||
return transitionProps.scenes.map((scene) => {
|
||||
const sceneProps = {
|
||||
...transitionProps,
|
||||
scene,
|
||||
};
|
||||
return this._renderScene(sceneProps);
|
||||
});
|
||||
}
|
||||
|
||||
_renderScene(
|
||||
sceneProps: NavigationSceneRendererProps,
|
||||
): React.Element<any> {
|
||||
return (
|
||||
<ExampleScene
|
||||
{...sceneProps}
|
||||
key={sceneProps.scene.key}
|
||||
navigate={this.props.navigate}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
_configureTransition(): NavigationTransitionSpec {
|
||||
const easing: any = Easing.inOut(Easing.ease);
|
||||
return {
|
||||
duration: 500,
|
||||
easing,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class ExampleScene extends Component {
|
||||
props: NavigationSceneRendererProps & {
|
||||
navigate: Function,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
...NavigationPropTypes.SceneRendererProps,
|
||||
navigate: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
render(): React.Element<any> {
|
||||
const {scene, navigate} = this.props;
|
||||
return (
|
||||
<Animated.View
|
||||
style={[styles.scene, this._getAnimatedStyle()]}>
|
||||
<ScrollView style={styles.scrollView}>
|
||||
<NavigationExampleRow
|
||||
text={scene.route.key}
|
||||
/>
|
||||
<NavigationExampleRow
|
||||
text="Push Route"
|
||||
onPress={() => navigate('push')}
|
||||
/>
|
||||
<NavigationExampleRow
|
||||
text="Pop Route"
|
||||
onPress={() => navigate('pop')}
|
||||
/>
|
||||
<NavigationExampleRow
|
||||
text="Exit NavigationTransitioner Example"
|
||||
onPress={() => navigate('exit')}
|
||||
/>
|
||||
</ScrollView>
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
|
||||
_getAnimatedStyle(): Object {
|
||||
const {
|
||||
layout,
|
||||
position,
|
||||
scene,
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
index,
|
||||
} = scene;
|
||||
|
||||
const inputRange = [index - 1, index, index + 1];
|
||||
const width = layout.initWidth;
|
||||
const translateX = position.interpolate({
|
||||
inputRange,
|
||||
outputRange: ([width, 0, -10]: Array<number>),
|
||||
});
|
||||
|
||||
return {
|
||||
transform: [
|
||||
{ translateX },
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
scene: {
|
||||
backgroundColor: '#E9E9EF',
|
||||
bottom: 0,
|
||||
flex: 1,
|
||||
left: 0,
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
shadowColor: 'black',
|
||||
shadowOffset: {width: 0, height: 0},
|
||||
shadowOpacity: 0.4,
|
||||
shadowRadius: 10,
|
||||
top: 0,
|
||||
},
|
||||
scrollView: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = Example;
|
||||
-265
@@ -1,265 +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.
|
||||
*
|
||||
* 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
|
||||
* @providesModule NavigationTransitioner-AnimatedView-pager-example
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const NavigationExampleRow = require('./NavigationExampleRow');
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
|
||||
/**
|
||||
* Basic example that shows how to use <NavigationTransitioner /> and
|
||||
* <Animated.View /> to build a list of animated scenes that render the
|
||||
* navigation state.
|
||||
*/
|
||||
|
||||
import type {
|
||||
NavigationSceneRendererProps,
|
||||
NavigationState,
|
||||
NavigationTransitionProps,
|
||||
} from 'NavigationTypeDefinition';
|
||||
|
||||
const {
|
||||
Component,
|
||||
PropTypes,
|
||||
} = React;
|
||||
|
||||
const {
|
||||
Animated,
|
||||
NavigationExperimental,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
const {
|
||||
PropTypes: NavigationPropTypes,
|
||||
StateUtils: NavigationStateUtils,
|
||||
Transitioner: NavigationTransitioner,
|
||||
Card: NavigationCard,
|
||||
} = NavigationExperimental;
|
||||
|
||||
const {
|
||||
PagerPanResponder: NavigationPagerPanResponder,
|
||||
PagerStyleInterpolator: NavigationPagerStyleInterpolator,
|
||||
} = NavigationCard;
|
||||
|
||||
function reducer(state: ?NavigationState, action: any): NavigationState {
|
||||
if (!state) {
|
||||
return {
|
||||
index: 0,
|
||||
routes: [
|
||||
{key: 'Step 1', color: '#ff0000'},
|
||||
{key: 'Step 2', color: '#ff7f00'},
|
||||
{key: 'Step 3', color: '#ffff00'},
|
||||
{key: 'Step 4', color: '#00ff00'},
|
||||
{key: 'Step 5', color: '#0000ff'},
|
||||
{key: 'Step 6', color: '#4b0082'},
|
||||
{key: 'Step 7', color: '#8f00ff'},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case 'back':
|
||||
return NavigationStateUtils.back(state);
|
||||
case 'forward':
|
||||
return NavigationStateUtils.forward(state);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
class Example extends Component {
|
||||
state: NavigationState;
|
||||
constructor(props: any, context: any) {
|
||||
super(props, context);
|
||||
this.state = reducer();
|
||||
}
|
||||
|
||||
render(): React.Element<any> {
|
||||
return (
|
||||
<View style={styles.example}>
|
||||
<ExampleNavigator
|
||||
navigationState={this.state}
|
||||
navigate={action => this._navigate(action)}
|
||||
/>
|
||||
<NavigationExampleRow
|
||||
text="Exit"
|
||||
onPress={() => this._navigate('exit')}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_navigate(action: string): boolean {
|
||||
if (action === 'exit') {
|
||||
// Exits the example. `this.props.onExampleExit` is provided
|
||||
// by the UI Explorer.
|
||||
this.props.onExampleExit && this.props.onExampleExit();
|
||||
return false;
|
||||
}
|
||||
|
||||
const state = reducer(this.state, action);
|
||||
if (state === this.state) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.setState(state);
|
||||
return true;
|
||||
}
|
||||
|
||||
// This public method is optional. If exists, the UI explorer will call it
|
||||
// the "back button" is pressed. Normally this is the cases for Android only.
|
||||
handleBackAction(): boolean {
|
||||
return this._navigate('back');
|
||||
}
|
||||
}
|
||||
|
||||
class ExampleNavigator extends Component {
|
||||
_render: Function;
|
||||
_renderScene: Function;
|
||||
|
||||
props: {
|
||||
navigate: Function,
|
||||
navigationState: NavigationState,
|
||||
};
|
||||
|
||||
static propTypes: {
|
||||
navigationState: NavigationPropTypes.navigationState.isRequired,
|
||||
navigate: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this._render = this._render.bind(this);
|
||||
this._renderScene = this._renderScene.bind(this);
|
||||
}
|
||||
|
||||
render(): React.Element<any> {
|
||||
return (
|
||||
<NavigationTransitioner
|
||||
navigationState={this.props.navigationState}
|
||||
render={this._render}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
_render(
|
||||
transitionProps: NavigationTransitionProps,
|
||||
): React.Element<any> {
|
||||
const scenes = transitionProps.scenes.map((scene) => {
|
||||
const sceneProps = {
|
||||
...transitionProps,
|
||||
scene,
|
||||
};
|
||||
return this._renderScene(sceneProps);
|
||||
});
|
||||
return (
|
||||
<View style={styles.navigator}>
|
||||
{scenes}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_renderScene(
|
||||
sceneProps: NavigationSceneRendererProps,
|
||||
): React.Element<any> {
|
||||
return (
|
||||
<ExampleScene
|
||||
{...sceneProps}
|
||||
key={sceneProps.scene.key + 'scene'}
|
||||
navigate={this.props.navigate}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ExampleScene extends Component {
|
||||
props: NavigationSceneRendererProps & {
|
||||
navigate: Function,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
...NavigationPropTypes.SceneRendererProps,
|
||||
navigate: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
render(): React.Element<any> {
|
||||
const {scene, navigate} = this.props;
|
||||
|
||||
const panHandlers = NavigationPagerPanResponder.forHorizontal({
|
||||
...this.props,
|
||||
onNavigateBack: () => navigate('back'),
|
||||
onNavigateForward: () => navigate('forward'),
|
||||
});
|
||||
|
||||
const route: any = scene.route;
|
||||
const style = [
|
||||
styles.scene,
|
||||
{backgroundColor: route.color},
|
||||
NavigationPagerStyleInterpolator.forHorizontal(this.props),
|
||||
];
|
||||
|
||||
return (
|
||||
<Animated.View
|
||||
{...panHandlers}
|
||||
style={style}>
|
||||
<View style={styles.heading}>
|
||||
<Text style={styles.headingText}>
|
||||
{scene.route.key}
|
||||
</Text>
|
||||
</View>
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
example: {
|
||||
flex: 1,
|
||||
},
|
||||
navigator: {
|
||||
flex: 1,
|
||||
},
|
||||
scene: {
|
||||
backgroundColor: '#000',
|
||||
bottom: 0,
|
||||
flex: 1,
|
||||
left: 0,
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: 0,
|
||||
},
|
||||
heading: {
|
||||
alignItems : 'center',
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
headingText: {
|
||||
color: '#222',
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = Example;
|
||||
@@ -81,7 +81,9 @@ class SectionListExample extends React.PureComponent {
|
||||
};
|
||||
render() {
|
||||
const filterRegex = new RegExp(String(this.state.filterText), 'i');
|
||||
const filter = (item) => (filterRegex.test(item.text) || filterRegex.test(item.title));
|
||||
const filter = (item) => (
|
||||
filterRegex.test(item.text) || filterRegex.test(item.title)
|
||||
);
|
||||
const filteredData = this.state.data.filter(filter);
|
||||
return (
|
||||
<UIExplorerPage
|
||||
@@ -104,8 +106,12 @@ class SectionListExample extends React.PureComponent {
|
||||
<SectionList
|
||||
ListHeaderComponent={HeaderComponent}
|
||||
ListFooterComponent={FooterComponent}
|
||||
SectionSeparatorComponent={() => <CustomSeparatorComponent text="SECTION SEPARATOR" />}
|
||||
ItemSeparatorComponent={() => <CustomSeparatorComponent text="ITEM SEPARATOR" />}
|
||||
SectionSeparatorComponent={() =>
|
||||
<CustomSeparatorComponent text="SECTION SEPARATOR" />
|
||||
}
|
||||
ItemSeparatorComponent={() =>
|
||||
<CustomSeparatorComponent text="ITEM SEPARATOR" />
|
||||
}
|
||||
enableVirtualization={this.state.virtualized}
|
||||
onRefresh={() => alert('onRefresh: nothing to refresh :P')}
|
||||
onViewableItemsChanged={this._onViewableItemsChanged}
|
||||
@@ -117,8 +123,8 @@ class SectionListExample extends React.PureComponent {
|
||||
{title: 'Item In Header Section', text: 'Section s1', key: '0'},
|
||||
]},
|
||||
{key: 's2', data: [
|
||||
{noImage: true, title: 'First item', text: 'Section s2', key: '0'},
|
||||
{noImage: true, title: 'Second item', text: 'Section s2', key: '1'},
|
||||
{noImage: true, title: '1st item', text: 'Section s2', key: '0'},
|
||||
{noImage: true, title: '2nd item', text: 'Section s2', key: '1'},
|
||||
]},
|
||||
{key: 'Filtered Items', data: filteredData},
|
||||
]}
|
||||
@@ -127,11 +133,18 @@ class SectionListExample extends React.PureComponent {
|
||||
</UIExplorerPage>
|
||||
);
|
||||
}
|
||||
_renderItemComponent = ({item}) => <ItemComponent item={item} onPress={this._pressItem} />;
|
||||
// This is called when items change viewability by scrolling into our out of the viewable area.
|
||||
_renderItemComponent = ({item}) => (
|
||||
<ItemComponent item={item} onPress={this._pressItem} />
|
||||
);
|
||||
// This is called when items change viewability by scrolling into our out of
|
||||
// the viewable area.
|
||||
_onViewableItemsChanged = (info: {
|
||||
changed: Array<{
|
||||
key: string, isViewable: boolean, item: {columns: Array<*>}, index: ?number, section?: any
|
||||
key: string,
|
||||
isViewable: boolean,
|
||||
item: {columns: Array<*>},
|
||||
index: ?number,
|
||||
section?: any
|
||||
}>},
|
||||
) => {
|
||||
// Impressions can be logged here
|
||||
|
||||
@@ -192,10 +192,6 @@ const APIExamples: Array<UIExplorerExample> = [
|
||||
key: 'NativeAnimationsExample',
|
||||
module: require('./NativeAnimationsExample'),
|
||||
},
|
||||
{
|
||||
key: 'NavigationExperimentalExample',
|
||||
module: require('./NavigationExperimental/NavigationExperimentalExample'),
|
||||
},
|
||||
{
|
||||
key: 'NetInfoExample',
|
||||
module: require('./NetInfoExample'),
|
||||
|
||||
@@ -298,11 +298,6 @@ const APIExamples: Array<UIExplorerExample> = [
|
||||
module: require('./NativeAnimationsExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'NavigationExperimentalExample',
|
||||
module: require('./NavigationExperimental/NavigationExperimentalExample'),
|
||||
supportsTVOS: true,
|
||||
},
|
||||
{
|
||||
key: 'NetInfoExample',
|
||||
module: require('./NetInfoExample'),
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var ReactPropTypes = require('React').PropTypes
|
||||
// $FlowFixMe `checkPropTypes` is not in Flow's built in React typedefs yet.
|
||||
var {PropTypes, checkPropTypes} = require('React');
|
||||
var RCTCameraRollManager = require('NativeModules').CameraRollManager;
|
||||
|
||||
var createStrictShapeTypeChecker = require('createStrictShapeTypeChecker');
|
||||
@@ -47,64 +48,65 @@ var getPhotosParamChecker = createStrictShapeTypeChecker({
|
||||
* The number of photos wanted in reverse order of the photo application
|
||||
* (i.e. most recent first for SavedPhotos).
|
||||
*/
|
||||
first: ReactPropTypes.number.isRequired,
|
||||
first: PropTypes.number.isRequired,
|
||||
|
||||
/**
|
||||
* A cursor that matches `page_info { end_cursor }` returned from a previous
|
||||
* call to `getPhotos`
|
||||
*/
|
||||
after: ReactPropTypes.string,
|
||||
after: PropTypes.string,
|
||||
|
||||
/**
|
||||
* Specifies which group types to filter the results to.
|
||||
*/
|
||||
groupTypes: ReactPropTypes.oneOf(GROUP_TYPES_OPTIONS),
|
||||
groupTypes: PropTypes.oneOf(GROUP_TYPES_OPTIONS),
|
||||
|
||||
/**
|
||||
* Specifies filter on group names, like 'Recent Photos' or custom album
|
||||
* titles.
|
||||
*/
|
||||
groupName: ReactPropTypes.string,
|
||||
groupName: PropTypes.string,
|
||||
|
||||
/**
|
||||
* Specifies filter on asset type
|
||||
*/
|
||||
assetType: ReactPropTypes.oneOf(ASSET_TYPE_OPTIONS),
|
||||
assetType: PropTypes.oneOf(ASSET_TYPE_OPTIONS),
|
||||
|
||||
/**
|
||||
* Filter by mimetype (e.g. image/jpeg).
|
||||
*/
|
||||
mimeTypes: ReactPropTypes.arrayOf(ReactPropTypes.string),
|
||||
mimeTypes: PropTypes.arrayOf(PropTypes.string),
|
||||
});
|
||||
|
||||
/**
|
||||
* Shape of the return value of the `getPhotos` function.
|
||||
*/
|
||||
var getPhotosReturnChecker = createStrictShapeTypeChecker({
|
||||
edges: ReactPropTypes.arrayOf(createStrictShapeTypeChecker({
|
||||
// $FlowFixMe(>=0.41.0)
|
||||
edges: PropTypes.arrayOf(createStrictShapeTypeChecker({
|
||||
node: createStrictShapeTypeChecker({
|
||||
type: ReactPropTypes.string.isRequired,
|
||||
group_name: ReactPropTypes.string.isRequired,
|
||||
type: PropTypes.string.isRequired,
|
||||
group_name: PropTypes.string.isRequired,
|
||||
image: createStrictShapeTypeChecker({
|
||||
uri: ReactPropTypes.string.isRequired,
|
||||
height: ReactPropTypes.number.isRequired,
|
||||
width: ReactPropTypes.number.isRequired,
|
||||
isStored: ReactPropTypes.bool,
|
||||
uri: PropTypes.string.isRequired,
|
||||
height: PropTypes.number.isRequired,
|
||||
width: PropTypes.number.isRequired,
|
||||
isStored: PropTypes.bool,
|
||||
}).isRequired,
|
||||
timestamp: ReactPropTypes.number.isRequired,
|
||||
timestamp: PropTypes.number.isRequired,
|
||||
location: createStrictShapeTypeChecker({
|
||||
latitude: ReactPropTypes.number,
|
||||
longitude: ReactPropTypes.number,
|
||||
altitude: ReactPropTypes.number,
|
||||
heading: ReactPropTypes.number,
|
||||
speed: ReactPropTypes.number,
|
||||
latitude: PropTypes.number,
|
||||
longitude: PropTypes.number,
|
||||
altitude: PropTypes.number,
|
||||
heading: PropTypes.number,
|
||||
speed: PropTypes.number,
|
||||
}),
|
||||
}).isRequired,
|
||||
})).isRequired,
|
||||
page_info: createStrictShapeTypeChecker({
|
||||
has_next_page: ReactPropTypes.bool.isRequired,
|
||||
start_cursor: ReactPropTypes.string,
|
||||
end_cursor: ReactPropTypes.string,
|
||||
has_next_page: PropTypes.bool.isRequired,
|
||||
start_cursor: PropTypes.string,
|
||||
end_cursor: PropTypes.string,
|
||||
}).isRequired,
|
||||
});
|
||||
|
||||
@@ -213,7 +215,7 @@ class CameraRoll {
|
||||
*/
|
||||
static getPhotos(params) {
|
||||
if (__DEV__) {
|
||||
getPhotosParamChecker({params}, 'params', 'CameraRoll.getPhotos');
|
||||
checkPropTypes({params: getPhotosParamChecker}, {params}, 'params', 'CameraRoll.getPhotos');
|
||||
}
|
||||
if (arguments.length > 1) {
|
||||
console.warn('CameraRoll.getPhotos(tag, success, error) is deprecated. Use the returned Promise instead');
|
||||
@@ -221,7 +223,8 @@ class CameraRoll {
|
||||
if (__DEV__) {
|
||||
const callback = arguments[1];
|
||||
successCallback = (response) => {
|
||||
getPhotosReturnChecker(
|
||||
checkPropTypes(
|
||||
{response: getPhotosReturnChecker},
|
||||
{response},
|
||||
'response',
|
||||
'CameraRoll.getPhotos callback'
|
||||
|
||||
@@ -46,19 +46,24 @@ const requireNativeComponent = require('requireNativeComponent');
|
||||
* view from becoming the responder.
|
||||
*
|
||||
*
|
||||
* `<ScrollView>` vs `<ListView>` - which one to use?
|
||||
* ScrollView simply renders all its react child components at once. That
|
||||
* makes it very easy to understand and use.
|
||||
* On the other hand, this has a performance downside. Imagine you have a very
|
||||
* long list of items you want to display, worth of couple of your ScrollView’s
|
||||
* heights. Creating JS components and native views upfront for all its items,
|
||||
* which may not even be shown, will contribute to slow rendering of your
|
||||
* screen and increased memory usage.
|
||||
* `<ScrollView>` vs [`<FlatList>`](/react-native/docs/flatlist.html) - which one to use?
|
||||
*
|
||||
* This is where ListView comes into play. ListView renders items lazily,
|
||||
* just when they are about to appear. This laziness comes at cost of a more
|
||||
* complicated API, which is worth it unless you are rendering a small fixed
|
||||
* set of items.
|
||||
* `ScrollView` simply renders all its react child components at once. That
|
||||
* makes it very easy to understand and use.
|
||||
*
|
||||
* On the other hand, this has a performance downside. Imagine you have a very
|
||||
* long list of items you want to display, maybe several screens worth of
|
||||
* content. Creating JS components and native views for everythign all at once,
|
||||
* much of which may not even be shown, will contribute to slow rendering and
|
||||
* increased memory usage.
|
||||
*
|
||||
* This is where `FlatList` comes into play. `FlatList` renders items lazily,
|
||||
* just when they are about to appear, and removes items that scroll way off
|
||||
* screen to save memory and processing time.
|
||||
*
|
||||
* `FlatList` is also handy if you want to render separators between your items,
|
||||
* multiple columns, infinite scroll loading, or any number of other features it
|
||||
* supports out of the box.
|
||||
*/
|
||||
const ScrollView = React.createClass({
|
||||
propTypes: {
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
|
||||
const MetroListView = require('MetroListView'); // Used as a fallback legacy option
|
||||
const React = require('React');
|
||||
const ReactNative = require('ReactNative');
|
||||
const View = require('View');
|
||||
const VirtualizedList = require('VirtualizedList');
|
||||
|
||||
@@ -65,18 +66,18 @@ type RequiredProps<ItemT> = {
|
||||
data: ?Array<ItemT>,
|
||||
};
|
||||
type OptionalProps<ItemT> = {
|
||||
/**
|
||||
* Rendered in between each item, but not at the top or bottom.
|
||||
*/
|
||||
ItemSeparatorComponent?: ?ReactClass<any>,
|
||||
/**
|
||||
* Rendered at the bottom of all the items.
|
||||
*/
|
||||
FooterComponent?: ?ReactClass<any>,
|
||||
ListFooterComponent?: ?ReactClass<any>,
|
||||
/**
|
||||
* Rendered at the top of all the items.
|
||||
*/
|
||||
HeaderComponent?: ?ReactClass<any>,
|
||||
/**
|
||||
* Rendered in between each item, but not at the top or bottom.
|
||||
*/
|
||||
SeparatorComponent?: ?ReactClass<any>,
|
||||
ListHeaderComponent?: ?ReactClass<any>,
|
||||
/**
|
||||
* `getItemLayout` is an optional optimizations that let us skip measurement of dynamic content if
|
||||
* you know the height of items a priori. `getItemLayout` is the most efficient, and is easy to
|
||||
@@ -87,7 +88,7 @@ type OptionalProps<ItemT> = {
|
||||
* )}
|
||||
*
|
||||
* Remember to include separator length (height or width) in your offset calculation if you
|
||||
* specify `SeparatorComponent`.
|
||||
* specify `ItemSeparatorComponent`.
|
||||
*/
|
||||
getItemLayout?: (data: ?Array<ItemT>, index: number) =>
|
||||
{length: number, offset: number, index: number},
|
||||
@@ -131,13 +132,6 @@ type OptionalProps<ItemT> = {
|
||||
* Optional custom style for multi-item rows generated when numColumns > 1
|
||||
*/
|
||||
columnWrapperStyle?: StyleObj,
|
||||
/**
|
||||
* Optional optimization to minimize re-rendering items.
|
||||
*/
|
||||
shouldItemUpdate: (
|
||||
prevInfo: {item: ItemT, index: number},
|
||||
nextInfo: {item: ItemT, index: number}
|
||||
) => boolean,
|
||||
/**
|
||||
* See `ViewabilityHelper` for flow type and further documentation.
|
||||
*/
|
||||
@@ -179,12 +173,16 @@ type DefaultProps = typeof defaultProps;
|
||||
*
|
||||
* - Internal state is not preserved when content scrolls out of the render window. Make sure all
|
||||
* your data is captured in the item data or external stores like Flux, Redux, or Relay.
|
||||
* - This is a `PureComponent` which means that it will not re-render if `props` remain shallow-
|
||||
* equal. Make sure that everything your `renderItem` function depends on is passed as a prop that
|
||||
* is not `===` after updates, otherwise your UI may not update on changes. This includes the
|
||||
* `data` prop and parent component state.
|
||||
* - In order to constrain memory and enable smooth scrolling, content is rendered asynchronously
|
||||
* offscreen. This means it's possible to scroll faster than the fill rate ands momentarily see
|
||||
* blank content. This is a tradeoff that can be adjusted to suit the needs of each application,
|
||||
* and we are working on improving it behind the scenes.
|
||||
* - By default, the list looks for a `key` prop on each item and uses that for the React key.
|
||||
* Alternatively, you can provide a custom keyExtractor prop.
|
||||
* Alternatively, you can provide a custom `keyExtractor` prop.
|
||||
*/
|
||||
class FlatList<ItemT> extends React.PureComponent<DefaultProps, Props<ItemT>, void> {
|
||||
static defaultProps: DefaultProps = defaultProps;
|
||||
@@ -231,6 +229,14 @@ class FlatList<ItemT> extends React.PureComponent<DefaultProps, Props<ItemT>, vo
|
||||
this._listRef.recordInteraction();
|
||||
}
|
||||
|
||||
getScrollableNode() {
|
||||
if (this._listRef && this._listRef.getScrollableNode) {
|
||||
return this._listRef.getScrollableNode();
|
||||
} else {
|
||||
return ReactNative.findNodeHandle(this._listRef);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this._checkProps(this.props);
|
||||
}
|
||||
|
||||
@@ -165,12 +165,16 @@ type DefaultProps = typeof VirtualizedSectionList.defaultProps;
|
||||
*
|
||||
* - Internal state is not preserved when content scrolls out of the render window. Make sure all
|
||||
* your data is captured in the item data or external stores like Flux, Redux, or Relay.
|
||||
* - This is a `PureComponent` which means that it will not re-render if `props` remain shallow-
|
||||
* equal. Make sure that everything your `renderItem` function depends on is passed as a prop that
|
||||
* is not `===` after updates, otherwise your UI may not update on changes. This includes the
|
||||
* `data` prop and parent component state.
|
||||
* - In order to constrain memory and enable smooth scrolling, content is rendered asynchronously
|
||||
* offscreen. This means it's possible to scroll faster than the fill rate ands momentarily see
|
||||
* blank content. This is a tradeoff that can be adjusted to suit the needs of each application,
|
||||
* and we are working on improving it behind the scenes.
|
||||
* - By default, the list looks for a `key` prop on each item and uses that for the React key.
|
||||
* Alternatively, you can provide a custom keyExtractor prop.
|
||||
* Alternatively, you can provide a custom `keyExtractor` prop.
|
||||
*/
|
||||
class SectionList<SectionT: SectionBase<any>>
|
||||
extends React.PureComponent<DefaultProps, Props<SectionT>, void>
|
||||
@@ -179,16 +183,8 @@ class SectionList<SectionT: SectionBase<any>>
|
||||
static defaultProps: DefaultProps = VirtualizedSectionList.defaultProps;
|
||||
|
||||
render() {
|
||||
const {ListFooterComponent, ListHeaderComponent, ItemSeparatorComponent} = this.props;
|
||||
const List = this.props.legacyImplementation ? MetroListView : VirtualizedSectionList;
|
||||
return (
|
||||
<List
|
||||
{...this.props}
|
||||
FooterComponent={ListFooterComponent}
|
||||
HeaderComponent={ListHeaderComponent}
|
||||
SeparatorComponent={ItemSeparatorComponent}
|
||||
/>
|
||||
);
|
||||
return <List {...this.props} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,15 +42,6 @@ export type ViewabilityConfig = {|
|
||||
* render.
|
||||
*/
|
||||
waitForInteraction?: boolean,
|
||||
|
||||
/**
|
||||
* Criteria to filter out certain scroll events so they don't count as interactions. By default,
|
||||
* any non-zero scroll offset will be considered an interaction.
|
||||
*/
|
||||
scrollInteractionFilter?: {|
|
||||
minimumOffset?: number, // scrolls with an offset less than this are ignored.
|
||||
minimumElapsed?: number, // scrolls that happen before this are ignored.
|
||||
|},
|
||||
|};
|
||||
|
||||
/**
|
||||
@@ -58,7 +49,7 @@ export type ViewabilityConfig = {|
|
||||
* layout.
|
||||
*
|
||||
* An item is said to be in a "viewable" state when any of the following
|
||||
* is true for longer than `minViewTime` milliseconds (after an interaction if `waitForInteraction`
|
||||
* is true for longer than `minimumViewTime` milliseconds (after an interaction if `waitForInteraction`
|
||||
* is true):
|
||||
*
|
||||
* - Occupying >= `viewAreaCoveragePercentThreshold` of the view area XOR fraction of the item
|
||||
@@ -74,10 +65,6 @@ class ViewabilityHelper {
|
||||
_viewableItems: Map<string, ViewToken> = new Map();
|
||||
|
||||
constructor(config: ViewabilityConfig = {viewAreaCoveragePercentThreshold: 0}) {
|
||||
invariant(
|
||||
config.scrollInteractionFilter == null || config.waitForInteraction,
|
||||
'scrollInteractionFilter only works in conjunction with waitForInteraction',
|
||||
);
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
@@ -158,22 +145,11 @@ class ViewabilityHelper {
|
||||
renderRange?: {first: number, last: number}, // Optional optimization to reduce the scan size
|
||||
): void {
|
||||
const updateTime = Date.now();
|
||||
if (this._lastUpdateTime === 0 && getFrameMetrics(0)) {
|
||||
if (this._lastUpdateTime === 0 && itemCount > 0 && getFrameMetrics(0)) {
|
||||
// Only count updates after the first item is rendered and has a frame.
|
||||
this._lastUpdateTime = updateTime;
|
||||
}
|
||||
const updateElapsed = this._lastUpdateTime ? updateTime - this._lastUpdateTime : 0;
|
||||
if (this._config.waitForInteraction && !this._hasInteracted && scrollOffset !== 0) {
|
||||
const filter = this._config.scrollInteractionFilter;
|
||||
if (filter) {
|
||||
if ((filter.minimumOffset == null || scrollOffset >= filter.minimumOffset) &&
|
||||
(filter.minimumElapsed == null || updateElapsed >= filter.minimumElapsed)) {
|
||||
this._hasInteracted = true;
|
||||
}
|
||||
} else {
|
||||
this._hasInteracted = true;
|
||||
}
|
||||
}
|
||||
if (this._config.waitForInteraction && !this._hasInteracted) {
|
||||
return;
|
||||
}
|
||||
@@ -195,13 +171,13 @@ class ViewabilityHelper {
|
||||
}
|
||||
this._viewableIndices = viewableIndices;
|
||||
this._lastUpdateTime = updateTime;
|
||||
if (this._config.minViewTime && updateElapsed < this._config.minViewTime) {
|
||||
if (this._config.minimumViewTime && updateElapsed < this._config.minimumViewTime) {
|
||||
const handle = setTimeout(
|
||||
() => {
|
||||
this._timers.delete(handle);
|
||||
this._onUpdateSync(viewableIndices, onViewableItemsChanged, createViewToken);
|
||||
},
|
||||
this._config.minViewTime,
|
||||
this._config.minimumViewTime,
|
||||
);
|
||||
this._timers.add(handle);
|
||||
} else {
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
|
||||
const Batchinator = require('Batchinator');
|
||||
const React = require('React');
|
||||
const ReactNative = require('ReactNative');
|
||||
const RefreshControl = require('RefreshControl');
|
||||
const ScrollView = require('ScrollView');
|
||||
const View = require('View');
|
||||
@@ -49,19 +50,6 @@ import type {ViewabilityConfig, ViewToken} from 'ViewabilityHelper';
|
||||
type Item = any;
|
||||
type renderItemType = (info: {item: Item, index: number}) => ?React.Element<any>;
|
||||
|
||||
/**
|
||||
* Renders a virtual list of items given a data blob and accessor functions. Items that are outside
|
||||
* the render window (except for the initial items at the top) are 'virtualized' e.g. unmounted or
|
||||
* never rendered in the first place. This improves performance and saves memory for large data
|
||||
* sets, but will reset state on items that scroll too far out of the render window.
|
||||
*
|
||||
* TODO: Note that LayoutAnimation and sticky section headers both have bugs when used with this and
|
||||
* are therefor not supported, but new Animated impl might work?
|
||||
* https://github.com/facebook/react-native/pull/11315
|
||||
*
|
||||
* TODO: removeClippedSubviews might not be necessary and may cause bugs?
|
||||
*
|
||||
*/
|
||||
type RequiredProps = {
|
||||
renderItem: renderItemType,
|
||||
/**
|
||||
@@ -71,12 +59,9 @@ type RequiredProps = {
|
||||
data?: any,
|
||||
};
|
||||
type OptionalProps = {
|
||||
FooterComponent?: ?ReactClass<any>,
|
||||
HeaderComponent?: ?ReactClass<any>,
|
||||
SeparatorComponent?: ?ReactClass<any>,
|
||||
/**
|
||||
* `debug` will turn on extra logging and visual overlays to aid with debugging both usage and
|
||||
* implementation.
|
||||
* implementation, but with a significant perf hit.
|
||||
*/
|
||||
debug?: ?boolean,
|
||||
/**
|
||||
@@ -85,13 +70,28 @@ type OptionalProps = {
|
||||
* this for debugging purposes.
|
||||
*/
|
||||
disableVirtualization: boolean,
|
||||
getItem: (items: any, index: number) => ?Item,
|
||||
getItemCount: (items: any) => number,
|
||||
getItemLayout?: (items: any, index: number) =>
|
||||
/**
|
||||
* A generic accessor for extracting an item from any sort of data blob.
|
||||
*/
|
||||
getItem: (data: any, index: number) => ?Item,
|
||||
/**
|
||||
* Determines how many items are in the data blob.
|
||||
*/
|
||||
getItemCount: (data: any) => number,
|
||||
getItemLayout?: (data: any, index: number) =>
|
||||
{length: number, offset: number, index: number}, // e.g. height, y
|
||||
horizontal?: ?boolean,
|
||||
/**
|
||||
* How many items to render in the initial batch. This should be enough to fill the screen but not
|
||||
* much more.
|
||||
*/
|
||||
initialNumToRender: number,
|
||||
keyExtractor: (item: Item, index: number) => string,
|
||||
/**
|
||||
* The maximum number of items to render in each incremental render batch. The more rendered at
|
||||
* once, the better the fill rate, but responsiveness my suffer because rendering content may
|
||||
* interfere with responding to button taps or other interactions.
|
||||
*/
|
||||
maxToRenderPerBatch: number,
|
||||
onEndReached?: ?(info: {distanceFromEnd: number}) => void,
|
||||
onEndReachedThreshold?: ?number, // units of visible length
|
||||
@@ -110,15 +110,34 @@ type OptionalProps = {
|
||||
* Set this true while waiting for new data from a refresh.
|
||||
*/
|
||||
refreshing?: ?boolean,
|
||||
/**
|
||||
* A native optimization that removes clipped subviews (those outside the parent) from the view
|
||||
* hierarchy to offload work from the native rendering system. They are still kept around so no
|
||||
* memory is saved and state is preserved.
|
||||
*/
|
||||
removeClippedSubviews?: boolean,
|
||||
/**
|
||||
* Render a custom scroll component, e.g. with a differently styled `RefreshControl`.
|
||||
*/
|
||||
renderScrollComponent: (props: Object) => React.Element<any>,
|
||||
shouldItemUpdate: (
|
||||
props: {item: Item, index: number},
|
||||
nextProps: {item: Item, index: number}
|
||||
) => boolean,
|
||||
/**
|
||||
* Amount of time between low-pri item render batches, e.g. for rendering items quite a ways off
|
||||
* screen. Similar fill rate/responsiveness tradeoff as `maxToRenderPerBatch`.
|
||||
*/
|
||||
updateCellsBatchingPeriod: number,
|
||||
viewabilityConfig?: ViewabilityConfig,
|
||||
windowSize: number, // units of visible length
|
||||
/**
|
||||
* Determines the maximum number of items rendered outside of the visible area, in units of
|
||||
* visible lengths. So if your list fills the screen, then `windowSize={21}` (the default) will
|
||||
* render the visible screen area plus up to 10 screens above and 10 below the viewport. Reducing
|
||||
* this number will reduce memory consumption and may improve performance, but will increase the
|
||||
* chance that fast scrolling may reveal momentary blank areas of unrendered content.
|
||||
*/
|
||||
windowSize: number,
|
||||
};
|
||||
export type Props = RequiredProps & OptionalProps;
|
||||
|
||||
@@ -142,12 +161,22 @@ type State = {first: number, last: number};
|
||||
*
|
||||
* - Internal state is not preserved when content scrolls out of the render window. Make sure all
|
||||
* your data is captured in the item data or external stores like Flux, Redux, or Relay.
|
||||
* - This is a `PureComponent` which means that it will not re-render if `props` remain shallow-
|
||||
* equal. Make sure that everything your `renderItem` function depends on is passed as a prop that
|
||||
* is not `===` after updates, otherwise your UI may not update on changes. This includes the
|
||||
* `data` prop and parent component state.
|
||||
* - In order to constrain memory and enable smooth scrolling, content is rendered asynchronously
|
||||
* offscreen. This means it's possible to scroll faster than the fill rate ands momentarily see
|
||||
* blank content. This is a tradeoff that can be adjusted to suit the needs of each application,
|
||||
* and we are working on improving it behind the scenes.
|
||||
* - By default, the list looks for a `key` prop on each item and uses that for the React key.
|
||||
* Alternatively, you can provide a custom keyExtractor prop.
|
||||
* Alternatively, you can provide a custom `keyExtractor` prop.
|
||||
*
|
||||
* NOTE: `LayoutAnimation` and sticky section headers both have bugs when used with this and are
|
||||
* therefore not officially supported yet.
|
||||
*
|
||||
* NOTE: `removeClippedSubviews` might not be necessary and may cause bugs. If you see issues with
|
||||
* content not rendering, try disabling it, and we may change the default there.
|
||||
*/
|
||||
class VirtualizedList extends React.PureComponent<OptionalProps, Props, State> {
|
||||
props: Props;
|
||||
@@ -206,6 +235,14 @@ class VirtualizedList extends React.PureComponent<OptionalProps, Props, State> {
|
||||
this._updateViewableItems(this.props.data);
|
||||
}
|
||||
|
||||
getScrollableNode() {
|
||||
if (this._scrollRef && this._scrollRef.getScrollableNode) {
|
||||
return this._scrollRef.getScrollableNode();
|
||||
} else {
|
||||
return ReactNative.findNodeHandle(this._scrollRef);
|
||||
}
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
disableVirtualization: false,
|
||||
getItem: (data: any, index: number) => data[index],
|
||||
@@ -262,8 +299,10 @@ class VirtualizedList extends React.PureComponent<OptionalProps, Props, State> {
|
||||
super(props);
|
||||
invariant(
|
||||
!props.onScroll || !props.onScroll.__isNative,
|
||||
'VirtualizedList does not support AnimatedEvent with onScroll and useNativeDriver',
|
||||
'Components based on VirtualizedList must be wrapped with Animated.createAnimatedComponent ' +
|
||||
'to support native onScroll events with useNativeDriver',
|
||||
);
|
||||
|
||||
this._updateCellsToRenderBatcher = new Batchinator(
|
||||
this._updateCellsToRender,
|
||||
this.props.updateCellsBatchingPeriod,
|
||||
@@ -293,7 +332,7 @@ class VirtualizedList extends React.PureComponent<OptionalProps, Props, State> {
|
||||
}
|
||||
|
||||
_pushCells(cells, first, last) {
|
||||
const {SeparatorComponent, data, getItem, getItemCount, keyExtractor} = this.props;
|
||||
const {ItemSeparatorComponent, data, getItem, getItemCount, keyExtractor} = this.props;
|
||||
const end = getItemCount(data) - 1;
|
||||
last = Math.min(end, last);
|
||||
for (let ii = first; ii <= last; ii++) {
|
||||
@@ -311,19 +350,19 @@ class VirtualizedList extends React.PureComponent<OptionalProps, Props, State> {
|
||||
parentProps={this.props}
|
||||
/>
|
||||
);
|
||||
if (SeparatorComponent && ii < end) {
|
||||
cells.push(<SeparatorComponent key={'sep' + ii}/>);
|
||||
if (ItemSeparatorComponent && ii < end) {
|
||||
cells.push(<ItemSeparatorComponent key={'sep' + ii}/>);
|
||||
}
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const {FooterComponent, HeaderComponent} = this.props;
|
||||
const {ListFooterComponent, ListHeaderComponent} = this.props;
|
||||
const {data, disableVirtualization, horizontal} = this.props;
|
||||
const cells = [];
|
||||
if (HeaderComponent) {
|
||||
if (ListHeaderComponent) {
|
||||
cells.push(
|
||||
<View key="$header" onLayout={this._onLayoutHeader}>
|
||||
<HeaderComponent />
|
||||
<ListHeaderComponent />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -363,10 +402,10 @@ class VirtualizedList extends React.PureComponent<OptionalProps, Props, State> {
|
||||
);
|
||||
}
|
||||
}
|
||||
if (FooterComponent) {
|
||||
if (ListFooterComponent) {
|
||||
cells.push(
|
||||
<View key="$footer" onLayout={this._onLayoutFooter}>
|
||||
<FooterComponent />
|
||||
<ListFooterComponent />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -376,6 +415,7 @@ class VirtualizedList extends React.PureComponent<OptionalProps, Props, State> {
|
||||
onContentSizeChange: this._onContentSizeChange,
|
||||
onLayout: this._onLayout,
|
||||
onScroll: this._onScroll,
|
||||
onScrollBeginDrag: this._onScrollBeginDrag,
|
||||
ref: this._captureScrollRef,
|
||||
scrollEventThrottle: 50, // TODO: Android support
|
||||
},
|
||||
@@ -570,6 +610,10 @@ class VirtualizedList extends React.PureComponent<OptionalProps, Props, State> {
|
||||
this._updateCellsToRenderBatcher.schedule();
|
||||
};
|
||||
|
||||
_onScrollBeginDrag = (e): void => {
|
||||
this._viewabilityHelper.recordInteraction();
|
||||
this.props.onScrollBeginDrag && this.props.onScrollBeginDrag(e);
|
||||
};
|
||||
_updateCellsToRender = () => {
|
||||
const {data, disableVirtualization, getItemCount, onEndReachedThreshold} = this.props;
|
||||
this._updateViewableItems(data);
|
||||
|
||||
@@ -84,7 +84,7 @@ type OptionalProps<SectionT: SectionBase> = {
|
||||
renderSectionHeader?: ?({section: SectionT}) => ?React.Element<*>,
|
||||
/**
|
||||
* Rendered at the bottom of every Section, except the very last one, in place of the normal
|
||||
* SeparatorComponent.
|
||||
* ItemSeparatorComponent.
|
||||
*/
|
||||
SectionSeparatorComponent?: ?ReactClass<*>,
|
||||
/**
|
||||
@@ -267,10 +267,8 @@ class VirtualizedSectionList<SectionT: SectionBase>
|
||||
return {
|
||||
childProps: {
|
||||
...props,
|
||||
FooterComponent: this.props.ListFooterComponent,
|
||||
HeaderComponent: this.props.ListHeaderComponent,
|
||||
renderItem: this._renderItem,
|
||||
SeparatorComponent: undefined, // Rendered with renderItem
|
||||
ItemSeparatorComponent: undefined, // Rendered with renderItem
|
||||
data: props.sections,
|
||||
getItemCount: () => itemCount,
|
||||
getItem,
|
||||
|
||||
@@ -249,9 +249,9 @@ describe('onUpdate', function() {
|
||||
);
|
||||
|
||||
it(
|
||||
'minViewTime delays callback',
|
||||
'minimumViewTime delays callback',
|
||||
function() {
|
||||
const helper = new ViewabilityHelper({minViewTime: 350, viewAreaCoveragePercentThreshold: 0});
|
||||
const helper = new ViewabilityHelper({minimumViewTime: 350, viewAreaCoveragePercentThreshold: 0});
|
||||
rowFrames = {
|
||||
a: {y: 0, height: 200},
|
||||
b: {y: 200, height: 200},
|
||||
@@ -279,9 +279,9 @@ describe('onUpdate', function() {
|
||||
);
|
||||
|
||||
it(
|
||||
'minViewTime skips briefly visible items',
|
||||
'minimumViewTime skips briefly visible items',
|
||||
function() {
|
||||
const helper = new ViewabilityHelper({minViewTime: 350, viewAreaCoveragePercentThreshold: 0});
|
||||
const helper = new ViewabilityHelper({minimumViewTime: 350, viewAreaCoveragePercentThreshold: 0});
|
||||
rowFrames = {
|
||||
a: {y: 0, height: 250},
|
||||
b: {y: 250, height: 200},
|
||||
@@ -316,14 +316,11 @@ describe('onUpdate', function() {
|
||||
);
|
||||
|
||||
it(
|
||||
'waitForInteraction blocks callback until scroll',
|
||||
'waitForInteraction blocks callback until interaction',
|
||||
function() {
|
||||
const helper = new ViewabilityHelper({
|
||||
waitForInteraction: true,
|
||||
viewAreaCoveragePercentThreshold: 0,
|
||||
scrollInteractionFilter: {
|
||||
minimumOffset: 20,
|
||||
},
|
||||
});
|
||||
rowFrames = {
|
||||
a: {y: 0, height: 200},
|
||||
@@ -340,15 +337,9 @@ describe('onUpdate', function() {
|
||||
onViewableItemsChanged,
|
||||
);
|
||||
expect(onViewableItemsChanged).not.toBeCalled();
|
||||
helper.onUpdate(
|
||||
data.length,
|
||||
10, // not far enough to meet minimumOffset
|
||||
100,
|
||||
getFrameMetrics,
|
||||
createViewToken,
|
||||
onViewableItemsChanged,
|
||||
);
|
||||
expect(onViewableItemsChanged).not.toBeCalled();
|
||||
|
||||
helper.recordInteraction();
|
||||
|
||||
helper.onUpdate(
|
||||
data.length,
|
||||
20,
|
||||
|
||||
@@ -37,7 +37,6 @@ const NavigationHeaderStyleInterpolator = require('NavigationHeaderStyleInterpol
|
||||
const NavigationHeaderTitle = require('NavigationHeaderTitle');
|
||||
const NavigationPropTypes = require('NavigationPropTypes');
|
||||
const React = require('React');
|
||||
const ReactComponentWithPureRenderMixin = require('react/lib/ReactComponentWithPureRenderMixin');
|
||||
const ReactNative = require('react-native');
|
||||
const TVEventHandler = require('TVEventHandler');
|
||||
|
||||
@@ -82,7 +81,7 @@ const APPBAR_HEIGHT = Platform.OS === 'ios' ? 44 : 56;
|
||||
const STATUSBAR_HEIGHT = Platform.OS === 'ios' ? 20 : 0;
|
||||
const {PropTypes} = React;
|
||||
|
||||
class NavigationHeader extends React.Component<DefaultProps, Props, any> {
|
||||
class NavigationHeader extends React.PureComponent<DefaultProps, Props, any> {
|
||||
props: Props;
|
||||
|
||||
static defaultProps = {
|
||||
@@ -121,14 +120,6 @@ class NavigationHeader extends React.Component<DefaultProps, Props, any> {
|
||||
viewProps: PropTypes.shape(View.propTypes),
|
||||
};
|
||||
|
||||
shouldComponentUpdate(nextProps: Props, nextState: any): boolean {
|
||||
return ReactComponentWithPureRenderMixin.shouldComponentUpdate.call(
|
||||
this,
|
||||
nextProps,
|
||||
nextState
|
||||
);
|
||||
}
|
||||
|
||||
_tvEventHandler: TVEventHandler;
|
||||
|
||||
componentDidMount(): void {
|
||||
|
||||
@@ -11,12 +11,13 @@
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var {PropTypes} = require('React');
|
||||
var UIManager = require('UIManager');
|
||||
|
||||
var createStrictShapeTypeChecker = require('createStrictShapeTypeChecker');
|
||||
var keyMirror = require('fbjs/lib/keyMirror');
|
||||
|
||||
// $FlowFixMe checkPropTypes not yet landed to Flow
|
||||
var {checkPropTypes, PropTypes} = require('react');
|
||||
|
||||
var TypesEnum = {
|
||||
spring: true,
|
||||
linear: true,
|
||||
@@ -33,7 +34,7 @@ var PropertiesEnum = {
|
||||
};
|
||||
var Properties = keyMirror(PropertiesEnum);
|
||||
|
||||
var animChecker = createStrictShapeTypeChecker({
|
||||
var animType = PropTypes.shape({
|
||||
duration: PropTypes.number,
|
||||
delay: PropTypes.number,
|
||||
springDamping: PropTypes.number,
|
||||
@@ -55,11 +56,11 @@ type Anim = {
|
||||
property?: $Enum<typeof PropertiesEnum>,
|
||||
}
|
||||
|
||||
var configChecker = createStrictShapeTypeChecker({
|
||||
var configType = PropTypes.shape({
|
||||
duration: PropTypes.number.isRequired,
|
||||
create: animChecker,
|
||||
update: animChecker,
|
||||
delete: animChecker,
|
||||
create: animType,
|
||||
update: animType,
|
||||
delete: animType,
|
||||
});
|
||||
|
||||
type Config = {
|
||||
@@ -69,9 +70,13 @@ type Config = {
|
||||
delete?: Anim,
|
||||
}
|
||||
|
||||
function checkConfig(config: Config, location: string, name: string) {
|
||||
checkPropTypes({config: configType}, {config}, location, name);
|
||||
}
|
||||
|
||||
function configureNext(config: Config, onAnimationDidEnd?: Function) {
|
||||
if (__DEV__) {
|
||||
configChecker({config}, 'config', 'LayoutAnimation.configureNext');
|
||||
checkConfig(config, 'config', 'LayoutAnimation.configureNext');
|
||||
}
|
||||
UIManager.configureNextLayoutAnimation(
|
||||
config, onAnimationDidEnd || function() {}, function() { /* unused */ }
|
||||
@@ -151,7 +156,7 @@ var LayoutAnimation = {
|
||||
create,
|
||||
Types,
|
||||
Properties,
|
||||
configChecker: configChecker,
|
||||
checkConfig,
|
||||
Presets,
|
||||
easeInEaseOut: configureNext.bind(
|
||||
null, Presets.easeInEaseOut
|
||||
|
||||
@@ -18,6 +18,18 @@ const NavigationPropTypes = require('NavigationPropTypes');
|
||||
const NavigationStateUtils = require('NavigationStateUtils');
|
||||
const NavigationTransitioner = require('NavigationTransitioner');
|
||||
|
||||
const warning = require('fbjs/lib/warning');
|
||||
|
||||
// This warning will only be reached if the user has required the module
|
||||
warning(
|
||||
false,
|
||||
'NavigationExperimental is deprecated and will be removed in a future ' +
|
||||
'version of React Native. The NavigationExperimental views live on in ' +
|
||||
'the React-Navigation project, which also makes it easy to declare ' +
|
||||
'navigation logic for your app. Learn more at https://reactnavigation.org/'
|
||||
);
|
||||
|
||||
|
||||
const NavigationExperimental = {
|
||||
// Core
|
||||
StateUtils: NavigationStateUtils,
|
||||
|
||||
@@ -394,16 +394,22 @@ const textColor = 'white';
|
||||
const rowGutter = 1;
|
||||
const rowHeight = 46;
|
||||
|
||||
// For unknown reasons, setting elevation: Number.MAX_VALUE causes remote debugging to
|
||||
// hang on iOS (some sort of overflow maybe). Setting it to Number.MAX_SAFE_INTEGER fixes the iOS issue, but since
|
||||
// elevation is an android-only style property we might as well remove it altogether for iOS.
|
||||
// See: https://github.com/facebook/react-native/issues/12223
|
||||
const elevation = Platform.OS === 'android' ? Number.MAX_SAFE_INTEGER : undefined;
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
fullScreen: {
|
||||
height: '100%',
|
||||
elevation: Number.MAX_VALUE
|
||||
elevation: elevation
|
||||
},
|
||||
inspector: {
|
||||
backgroundColor: backgroundColor(0.95),
|
||||
height: '100%',
|
||||
paddingTop: 5,
|
||||
elevation: Number.MAX_VALUE
|
||||
elevation:elevation
|
||||
},
|
||||
inspectorButtons: {
|
||||
flexDirection: 'row',
|
||||
@@ -451,7 +457,7 @@ var styles = StyleSheet.create({
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
elevation: Number.MAX_VALUE
|
||||
elevation: elevation
|
||||
},
|
||||
listRow: {
|
||||
backgroundColor: backgroundColor(0.95),
|
||||
|
||||
@@ -11,4 +11,4 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = '16.0.0-alpha.3';
|
||||
module.exports = '16.0.0-alpha.6';
|
||||
|
||||
@@ -122,50 +122,6 @@ const ReactNative = {
|
||||
get ColorPropType() { return require('ColorPropType'); },
|
||||
get EdgeInsetsPropType() { return require('EdgeInsetsPropType'); },
|
||||
get PointPropType() { return require('PointPropType'); },
|
||||
|
||||
// See http://facebook.github.io/react/docs/addons.html
|
||||
addons: {
|
||||
get PureRenderMixin() {
|
||||
if (__DEV__) {
|
||||
addonWarn('PureRenderMixin', 'react-addons-pure-render-mixin');
|
||||
}
|
||||
return require('react/lib/ReactComponentWithPureRenderMixin');
|
||||
},
|
||||
get TestModule() {
|
||||
if (__DEV__) {
|
||||
warning(
|
||||
warningDedupe.TestModule,
|
||||
'React.addons.TestModule is deprecated. ' +
|
||||
'Use ReactNative.NativeModules.TestModule instead.'
|
||||
);
|
||||
warningDedupe.TestModule = true;
|
||||
}
|
||||
return require('NativeModules').TestModule;
|
||||
},
|
||||
get batchedUpdates() {
|
||||
if (__DEV__) {
|
||||
warning(
|
||||
warningDedupe.batchedUpdates,
|
||||
'React.addons.batchedUpdates is deprecated. ' +
|
||||
'Use ReactNative.unstable_batchedUpdates instead.'
|
||||
);
|
||||
warningDedupe.batchedUpdates = true;
|
||||
}
|
||||
return require('ReactUpdates').batchedUpdates;
|
||||
},
|
||||
get createFragment() {
|
||||
if (__DEV__) {
|
||||
addonWarn('createFragment', 'react-addons-create-fragment');
|
||||
}
|
||||
return require('react/lib/ReactFragment').create;
|
||||
},
|
||||
get update() {
|
||||
if (__DEV__) {
|
||||
addonWarn('update', 'react-addons-update');
|
||||
}
|
||||
return require('react/lib/update');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Better error messages when accessing React APIs on ReactNative
|
||||
|
||||
@@ -185,8 +185,9 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
||||
|
||||
- (NSString *)formatFrameSource:(RCTJSStackFrame *)stackFrame
|
||||
{
|
||||
NSString *fileName = RCTNilIfNull(stackFrame.file) ? [stackFrame.file lastPathComponent] : @"<unknown file>";
|
||||
NSString *lineInfo = [NSString stringWithFormat:@"%@:%zd",
|
||||
[stackFrame.file lastPathComponent],
|
||||
fileName,
|
||||
stackFrame.lineNumber];
|
||||
|
||||
if (stackFrame.column != 0) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
VERSION_NAME=1000.0.0-master
|
||||
VERSION_NAME=0.43.5
|
||||
GROUP=com.facebook.react
|
||||
|
||||
POM_NAME=ReactNative
|
||||
|
||||
@@ -16,7 +16,3 @@ android_library(
|
||||
react_native_target("java/com/facebook/react/modules/core:core"),
|
||||
],
|
||||
)
|
||||
|
||||
project_config(
|
||||
src_target = ":accessibilityinfo",
|
||||
)
|
||||
|
||||
@@ -153,6 +153,12 @@ public class UIImplementation {
|
||||
int newWidth,
|
||||
int newHeight) {
|
||||
ReactShadowNode cssNode = mShadowNodeRegistry.getNode(nodeViewTag);
|
||||
if (cssNode == null) {
|
||||
FLog.w(
|
||||
ReactConstants.TAG,
|
||||
"Tried to update size of non-existent tag: " + nodeViewTag);
|
||||
return;
|
||||
}
|
||||
cssNode.setStyleWidth(newWidth);
|
||||
cssNode.setStyleHeight(newHeight);
|
||||
|
||||
|
||||
@@ -304,13 +304,14 @@ public class ReactModalHostView extends ViewGroup implements LifecycleEventListe
|
||||
protected void onSizeChanged(final int w, final int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
if (getChildCount() > 0) {
|
||||
final int viewTag = getChildAt(0).getId();
|
||||
ReactContext reactContext = (ReactContext) getContext();
|
||||
reactContext.runOnNativeModulesQueueThread(
|
||||
new GuardedRunnable(reactContext) {
|
||||
@Override
|
||||
public void runGuarded() {
|
||||
((ReactContext) getContext()).getNativeModule(UIManagerModule.class)
|
||||
.updateNodeSize(getChildAt(0).getId(), w, h);
|
||||
.updateNodeSize(viewTag, w, h);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -42,9 +42,14 @@ public class YogaNode implements YogaNodeAPI<YogaNode> {
|
||||
private long mNativePointer;
|
||||
private Object mData;
|
||||
|
||||
private boolean mHasSetPadding = false;
|
||||
private boolean mHasSetMargin = false;
|
||||
private boolean mHasSetBorder = false;
|
||||
/* Those flags needs be in sync with YGJNI.cpp */
|
||||
private final static int MARGIN = 1;
|
||||
private final static int PADDING = 2;
|
||||
private final static int BORDER = 4;
|
||||
|
||||
@DoNotStrip
|
||||
private int mEdgeSetFlag = 0;
|
||||
|
||||
private boolean mHasSetPosition = false;
|
||||
|
||||
@DoNotStrip
|
||||
@@ -81,6 +86,8 @@ public class YogaNode implements YogaNodeAPI<YogaNode> {
|
||||
private float mBorderBottom = 0;
|
||||
@DoNotStrip
|
||||
private int mLayoutDirection = 0;
|
||||
@DoNotStrip
|
||||
private boolean mHasNewLayout = true;
|
||||
|
||||
private native long jni_YGNodeNew();
|
||||
public YogaNode() {
|
||||
@@ -111,15 +118,26 @@ public class YogaNode implements YogaNodeAPI<YogaNode> {
|
||||
private native void jni_YGNodeReset(long nativePointer);
|
||||
@Override
|
||||
public void reset() {
|
||||
mHasSetPadding = false;
|
||||
mHasSetMargin = false;
|
||||
mHasSetBorder = false;
|
||||
mEdgeSetFlag = 0;
|
||||
mHasSetPosition = false;
|
||||
mHasNewLayout = true;
|
||||
|
||||
mWidth = YogaConstants.UNDEFINED;
|
||||
mHeight = YogaConstants.UNDEFINED;
|
||||
mTop = YogaConstants.UNDEFINED;
|
||||
mLeft = YogaConstants.UNDEFINED;
|
||||
mMarginLeft = 0;
|
||||
mMarginTop = 0;
|
||||
mMarginRight = 0;
|
||||
mMarginBottom = 0;
|
||||
mPaddingLeft = 0;
|
||||
mPaddingTop = 0;
|
||||
mPaddingRight = 0;
|
||||
mPaddingBottom = 0;
|
||||
mBorderLeft = 0;
|
||||
mBorderTop = 0;
|
||||
mBorderRight = 0;
|
||||
mBorderBottom = 0;
|
||||
mLayoutDirection = 0;
|
||||
|
||||
mMeasureFunction = null;
|
||||
@@ -180,10 +198,9 @@ public class YogaNode implements YogaNodeAPI<YogaNode> {
|
||||
jni_YGNodeCalculateLayout(mNativePointer, width, height);
|
||||
}
|
||||
|
||||
private native boolean jni_YGNodeHasNewLayout(long nativePointer);
|
||||
@Override
|
||||
public boolean hasNewLayout() {
|
||||
return jni_YGNodeHasNewLayout(mNativePointer);
|
||||
return mHasNewLayout;
|
||||
}
|
||||
|
||||
private native void jni_YGNodeMarkDirty(long nativePointer);
|
||||
@@ -198,18 +215,17 @@ public class YogaNode implements YogaNodeAPI<YogaNode> {
|
||||
return jni_YGNodeIsDirty(mNativePointer);
|
||||
}
|
||||
|
||||
private native void jni_YGNodeMarkLayoutSeen(long nativePointer);
|
||||
@Override
|
||||
public void markLayoutSeen() {
|
||||
jni_YGNodeMarkLayoutSeen(mNativePointer);
|
||||
}
|
||||
|
||||
private native void jni_YGNodeCopyStyle(long dstNativePointer, long srcNativePointer);
|
||||
@Override
|
||||
public void copyStyle(YogaNode srcNode) {
|
||||
jni_YGNodeCopyStyle(mNativePointer, srcNode.mNativePointer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markLayoutSeen() {
|
||||
mHasNewLayout = false;
|
||||
}
|
||||
|
||||
private native int jni_YGNodeStyleGetDirection(long nativePointer);
|
||||
@Override
|
||||
public YogaDirection getStyleDirection() {
|
||||
@@ -381,7 +397,7 @@ public class YogaNode implements YogaNodeAPI<YogaNode> {
|
||||
private native Object jni_YGNodeStyleGetMargin(long nativePointer, int edge);
|
||||
@Override
|
||||
public YogaValue getMargin(YogaEdge edge) {
|
||||
if (!mHasSetMargin) {
|
||||
if (!((mEdgeSetFlag & MARGIN) == MARGIN)) {
|
||||
return YogaValue.UNDEFINED;
|
||||
}
|
||||
return (YogaValue) jni_YGNodeStyleGetMargin(mNativePointer, edge.intValue());
|
||||
@@ -390,28 +406,28 @@ public class YogaNode implements YogaNodeAPI<YogaNode> {
|
||||
private native void jni_YGNodeStyleSetMargin(long nativePointer, int edge, float margin);
|
||||
@Override
|
||||
public void setMargin(YogaEdge edge, float margin) {
|
||||
mHasSetMargin = true;
|
||||
mEdgeSetFlag |= MARGIN;
|
||||
jni_YGNodeStyleSetMargin(mNativePointer, edge.intValue(), margin);
|
||||
}
|
||||
|
||||
private native void jni_YGNodeStyleSetMarginPercent(long nativePointer, int edge, float percent);
|
||||
@Override
|
||||
public void setMarginPercent(YogaEdge edge, float percent) {
|
||||
mHasSetMargin = true;
|
||||
mEdgeSetFlag |= MARGIN;
|
||||
jni_YGNodeStyleSetMarginPercent(mNativePointer, edge.intValue(), percent);
|
||||
}
|
||||
|
||||
private native void jni_YGNodeStyleSetMarginAuto(long nativePointer, int edge);
|
||||
@Override
|
||||
public void setMarginAuto(YogaEdge edge) {
|
||||
mHasSetMargin = true;
|
||||
mEdgeSetFlag |= MARGIN;
|
||||
jni_YGNodeStyleSetMarginAuto(mNativePointer, edge.intValue());
|
||||
}
|
||||
|
||||
private native Object jni_YGNodeStyleGetPadding(long nativePointer, int edge);
|
||||
@Override
|
||||
public YogaValue getPadding(YogaEdge edge) {
|
||||
if (!mHasSetPadding) {
|
||||
if (!((mEdgeSetFlag & PADDING) == PADDING)) {
|
||||
return YogaValue.UNDEFINED;
|
||||
}
|
||||
return (YogaValue) jni_YGNodeStyleGetPadding(mNativePointer, edge.intValue());
|
||||
@@ -420,21 +436,21 @@ public class YogaNode implements YogaNodeAPI<YogaNode> {
|
||||
private native void jni_YGNodeStyleSetPadding(long nativePointer, int edge, float padding);
|
||||
@Override
|
||||
public void setPadding(YogaEdge edge, float padding) {
|
||||
mHasSetPadding = true;
|
||||
mEdgeSetFlag |= PADDING;
|
||||
jni_YGNodeStyleSetPadding(mNativePointer, edge.intValue(), padding);
|
||||
}
|
||||
|
||||
private native void jni_YGNodeStyleSetPaddingPercent(long nativePointer, int edge, float percent);
|
||||
@Override
|
||||
public void setPaddingPercent(YogaEdge edge, float percent) {
|
||||
mHasSetPadding = true;
|
||||
mEdgeSetFlag |= PADDING;
|
||||
jni_YGNodeStyleSetPaddingPercent(mNativePointer, edge.intValue(), percent);
|
||||
}
|
||||
|
||||
private native float jni_YGNodeStyleGetBorder(long nativePointer, int edge);
|
||||
@Override
|
||||
public float getBorder(YogaEdge edge) {
|
||||
if (!mHasSetBorder) {
|
||||
if (!((mEdgeSetFlag & BORDER) == BORDER)) {
|
||||
return YogaConstants.UNDEFINED;
|
||||
}
|
||||
return jni_YGNodeStyleGetBorder(mNativePointer, edge.intValue());
|
||||
@@ -443,7 +459,7 @@ public class YogaNode implements YogaNodeAPI<YogaNode> {
|
||||
private native void jni_YGNodeStyleSetBorder(long nativePointer, int edge, float border);
|
||||
@Override
|
||||
public void setBorder(YogaEdge edge, float border) {
|
||||
mHasSetBorder = true;
|
||||
mEdgeSetFlag |= BORDER;
|
||||
jni_YGNodeStyleSetBorder(mNativePointer, edge.intValue(), border);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,54 +24,74 @@ static void YGTransferLayoutDirection(YGNodeRef node, alias_ref<jobject> javaNod
|
||||
}
|
||||
|
||||
static void YGTransferLayoutOutputsRecursive(YGNodeRef root) {
|
||||
if (auto obj = YGNodeJobject(root)->lockLocal()) {
|
||||
static auto widthField = obj->getClass()->getField<jfloat>("mWidth");
|
||||
static auto heightField = obj->getClass()->getField<jfloat>("mHeight");
|
||||
static auto leftField = obj->getClass()->getField<jfloat>("mLeft");
|
||||
static auto topField = obj->getClass()->getField<jfloat>("mTop");
|
||||
if(YGNodeGetHasNewLayout(root)){
|
||||
if (auto obj = YGNodeJobject(root)->lockLocal()) {
|
||||
static auto widthField = obj->getClass()->getField<jfloat>("mWidth");
|
||||
static auto heightField = obj->getClass()->getField<jfloat>("mHeight");
|
||||
static auto leftField = obj->getClass()->getField<jfloat>("mLeft");
|
||||
static auto topField = obj->getClass()->getField<jfloat>("mTop");
|
||||
|
||||
static auto marginLeftField = obj->getClass()->getField<jfloat>("mMarginLeft");
|
||||
static auto marginTopField = obj->getClass()->getField<jfloat>("mMarginTop");
|
||||
static auto marginRightField = obj->getClass()->getField<jfloat>("mMarginRight");
|
||||
static auto marginBottomField = obj->getClass()->getField<jfloat>("mMarginBottom");
|
||||
static auto marginLeftField = obj->getClass()->getField<jfloat>("mMarginLeft");
|
||||
static auto marginTopField = obj->getClass()->getField<jfloat>("mMarginTop");
|
||||
static auto marginRightField = obj->getClass()->getField<jfloat>("mMarginRight");
|
||||
static auto marginBottomField = obj->getClass()->getField<jfloat>("mMarginBottom");
|
||||
|
||||
static auto paddingLeftField = obj->getClass()->getField<jfloat>("mPaddingLeft");
|
||||
static auto paddingTopField = obj->getClass()->getField<jfloat>("mPaddingTop");
|
||||
static auto paddingRightField = obj->getClass()->getField<jfloat>("mPaddingRight");
|
||||
static auto paddingBottomField = obj->getClass()->getField<jfloat>("mPaddingBottom");
|
||||
static auto paddingLeftField = obj->getClass()->getField<jfloat>("mPaddingLeft");
|
||||
static auto paddingTopField = obj->getClass()->getField<jfloat>("mPaddingTop");
|
||||
static auto paddingRightField = obj->getClass()->getField<jfloat>("mPaddingRight");
|
||||
static auto paddingBottomField = obj->getClass()->getField<jfloat>("mPaddingBottom");
|
||||
|
||||
static auto borderLeftField = obj->getClass()->getField<jfloat>("mBorderLeft");
|
||||
static auto borderTopField = obj->getClass()->getField<jfloat>("mBorderTop");
|
||||
static auto borderRightField = obj->getClass()->getField<jfloat>("mBorderRight");
|
||||
static auto borderBottomField = obj->getClass()->getField<jfloat>("mBorderBottom");
|
||||
static auto borderLeftField = obj->getClass()->getField<jfloat>("mBorderLeft");
|
||||
static auto borderTopField = obj->getClass()->getField<jfloat>("mBorderTop");
|
||||
static auto borderRightField = obj->getClass()->getField<jfloat>("mBorderRight");
|
||||
static auto borderBottomField = obj->getClass()->getField<jfloat>("mBorderBottom");
|
||||
|
||||
obj->setFieldValue(widthField, YGNodeLayoutGetWidth(root));
|
||||
obj->setFieldValue(heightField, YGNodeLayoutGetHeight(root));
|
||||
obj->setFieldValue(leftField, YGNodeLayoutGetLeft(root));
|
||||
obj->setFieldValue(topField, YGNodeLayoutGetTop(root));
|
||||
static auto edgeSetFlagField = obj->getClass()->getField<jint>("mEdgeSetFlag");
|
||||
static auto hasNewLayoutField = obj->getClass()->getField<jboolean>("mHasNewLayout");
|
||||
|
||||
obj->setFieldValue(marginLeftField, YGNodeLayoutGetMargin(root, YGEdgeLeft));
|
||||
obj->setFieldValue(marginTopField, YGNodeLayoutGetMargin(root, YGEdgeTop));
|
||||
obj->setFieldValue(marginRightField, YGNodeLayoutGetMargin(root, YGEdgeRight));
|
||||
obj->setFieldValue(marginBottomField, YGNodeLayoutGetMargin(root, YGEdgeBottom));
|
||||
/* Those flags needs be in sync with YogaNode.java */
|
||||
const int MARGIN = 1;
|
||||
const int PADDING = 2;
|
||||
const int BORDER = 4;
|
||||
|
||||
obj->setFieldValue(paddingLeftField, YGNodeLayoutGetPadding(root, YGEdgeLeft));
|
||||
obj->setFieldValue(paddingTopField, YGNodeLayoutGetPadding(root, YGEdgeTop));
|
||||
obj->setFieldValue(paddingRightField, YGNodeLayoutGetPadding(root, YGEdgeRight));
|
||||
obj->setFieldValue(paddingBottomField, YGNodeLayoutGetPadding(root, YGEdgeBottom));
|
||||
int hasEdgeSetFlag = (int)obj->getFieldValue(edgeSetFlagField);
|
||||
|
||||
obj->setFieldValue(borderLeftField, YGNodeLayoutGetBorder(root, YGEdgeLeft));
|
||||
obj->setFieldValue(borderTopField, YGNodeLayoutGetBorder(root, YGEdgeTop));
|
||||
obj->setFieldValue(borderRightField, YGNodeLayoutGetBorder(root, YGEdgeRight));
|
||||
obj->setFieldValue(borderBottomField, YGNodeLayoutGetBorder(root, YGEdgeBottom));
|
||||
obj->setFieldValue(widthField, YGNodeLayoutGetWidth(root));
|
||||
obj->setFieldValue(heightField, YGNodeLayoutGetHeight(root));
|
||||
obj->setFieldValue(leftField, YGNodeLayoutGetLeft(root));
|
||||
obj->setFieldValue(topField, YGNodeLayoutGetTop(root));
|
||||
|
||||
YGTransferLayoutDirection(root, obj);
|
||||
if((hasEdgeSetFlag & MARGIN) == MARGIN){
|
||||
obj->setFieldValue(marginLeftField, YGNodeLayoutGetMargin(root, YGEdgeLeft));
|
||||
obj->setFieldValue(marginTopField, YGNodeLayoutGetMargin(root, YGEdgeTop));
|
||||
obj->setFieldValue(marginRightField, YGNodeLayoutGetMargin(root, YGEdgeRight));
|
||||
obj->setFieldValue(marginBottomField, YGNodeLayoutGetMargin(root, YGEdgeBottom));
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < YGNodeGetChildCount(root); i++) {
|
||||
YGTransferLayoutOutputsRecursive(YGNodeGetChild(root, i));
|
||||
if((hasEdgeSetFlag & PADDING) == PADDING){
|
||||
obj->setFieldValue(paddingLeftField, YGNodeLayoutGetPadding(root, YGEdgeLeft));
|
||||
obj->setFieldValue(paddingTopField, YGNodeLayoutGetPadding(root, YGEdgeTop));
|
||||
obj->setFieldValue(paddingRightField, YGNodeLayoutGetPadding(root, YGEdgeRight));
|
||||
obj->setFieldValue(paddingBottomField, YGNodeLayoutGetPadding(root, YGEdgeBottom));
|
||||
}
|
||||
|
||||
if((hasEdgeSetFlag & BORDER) == BORDER){
|
||||
obj->setFieldValue(borderLeftField, YGNodeLayoutGetBorder(root, YGEdgeLeft));
|
||||
obj->setFieldValue(borderTopField, YGNodeLayoutGetBorder(root, YGEdgeTop));
|
||||
obj->setFieldValue(borderRightField, YGNodeLayoutGetBorder(root, YGEdgeRight));
|
||||
obj->setFieldValue(borderBottomField, YGNodeLayoutGetBorder(root, YGEdgeBottom));
|
||||
}
|
||||
|
||||
obj->setFieldValue<jboolean>(hasNewLayoutField, true);
|
||||
YGTransferLayoutDirection(root, obj);
|
||||
YGNodeSetHasNewLayout(root, false);
|
||||
|
||||
for (uint32_t i = 0; i < YGNodeGetChildCount(root); i++) {
|
||||
YGTransferLayoutOutputsRecursive(YGNodeGetChild(root, i));
|
||||
}
|
||||
} else {
|
||||
YGLog(YGLogLevelError, "Java YGNode was GCed during layout calculation\n");
|
||||
}
|
||||
} else {
|
||||
YGLog(YGLogLevelError, "Java YGNode was GCed during layout calculation\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,14 +262,6 @@ void jni_YGNodeSetHasBaselineFunc(alias_ref<jobject>,
|
||||
hasBaselineFunc ? YGJNIBaselineFunc : NULL);
|
||||
}
|
||||
|
||||
jboolean jni_YGNodeHasNewLayout(alias_ref<jobject>, jlong nativePointer) {
|
||||
return (jboolean) YGNodeGetHasNewLayout(_jlong2YGNodeRef(nativePointer));
|
||||
}
|
||||
|
||||
void jni_YGNodeMarkLayoutSeen(alias_ref<jobject>, jlong nativePointer) {
|
||||
YGNodeSetHasNewLayout(_jlong2YGNodeRef(nativePointer), false);
|
||||
}
|
||||
|
||||
void jni_YGNodeCopyStyle(alias_ref<jobject>, jlong dstNativePointer, jlong srcNativePointer) {
|
||||
YGNodeCopyStyle(_jlong2YGNodeRef(dstNativePointer), _jlong2YGNodeRef(srcNativePointer));
|
||||
}
|
||||
@@ -398,10 +410,8 @@ jint JNI_OnLoad(JavaVM *vm, void *) {
|
||||
YGMakeNativeMethod(jni_YGNodeInsertChild),
|
||||
YGMakeNativeMethod(jni_YGNodeRemoveChild),
|
||||
YGMakeNativeMethod(jni_YGNodeCalculateLayout),
|
||||
YGMakeNativeMethod(jni_YGNodeHasNewLayout),
|
||||
YGMakeNativeMethod(jni_YGNodeMarkDirty),
|
||||
YGMakeNativeMethod(jni_YGNodeIsDirty),
|
||||
YGMakeNativeMethod(jni_YGNodeMarkLayoutSeen),
|
||||
YGMakeNativeMethod(jni_YGNodeSetHasMeasureFunc),
|
||||
YGMakeNativeMethod(jni_YGNodeSetHasBaselineFunc),
|
||||
YGMakeNativeMethod(jni_YGNodeCopyStyle),
|
||||
|
||||
@@ -49,6 +49,8 @@ public:
|
||||
JSExecutor& executor, folly::dynamic&& calls, bool isEndOfBatch) override {
|
||||
ExecutorToken token = m_nativeToJs->getTokenForExecutor(executor);
|
||||
m_nativeQueue->runOnQueue([this, token, calls=std::move(calls), isEndOfBatch] () mutable {
|
||||
m_batchHadNativeModuleCalls = m_batchHadNativeModuleCalls || !calls.empty();
|
||||
|
||||
// An exception anywhere in here stops processing of the batch. This
|
||||
// was the behavior of the Android bridge, and since exception handling
|
||||
// terminates the whole bridge, there's not much point in continuing.
|
||||
@@ -57,7 +59,10 @@ public:
|
||||
token, call.moduleId, call.methodId, std::move(call.arguments), call.callId);
|
||||
}
|
||||
if (isEndOfBatch) {
|
||||
m_callback->onBatchComplete();
|
||||
if (m_batchHadNativeModuleCalls) {
|
||||
m_callback->onBatchComplete();
|
||||
m_batchHadNativeModuleCalls = false;
|
||||
}
|
||||
m_callback->decrementPendingJSCalls();
|
||||
}
|
||||
});
|
||||
@@ -85,6 +90,7 @@ private:
|
||||
std::shared_ptr<ModuleRegistry> m_registry;
|
||||
std::unique_ptr<MessageQueueThread> m_nativeQueue;
|
||||
std::shared_ptr<InstanceCallback> m_callback;
|
||||
bool m_batchHadNativeModuleCalls = false;
|
||||
};
|
||||
|
||||
NativeToJsBridge::NativeToJsBridge(
|
||||
|
||||
+1
-1
@@ -73,7 +73,7 @@ test:
|
||||
# build test APK
|
||||
- buck install ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=1
|
||||
# run installed apk with tests
|
||||
- node ./scripts/run-android-ci-instrumentation-tests.js --retries 3 --path ./ReactAndroid/src/androidTest/java/com/facebook/react/tests --package com.facebook.react.tests
|
||||
# - node ./scripts/run-android-ci-instrumentation-tests.js --retries 3 --path ./ReactAndroid/src/androidTest/java/com/facebook/react/tests --package com.facebook.react.tests
|
||||
|
||||
# Android e2e test
|
||||
- source scripts/circle-ci-android-setup.sh && retry3 node ./scripts/run-ci-e2e-tests.js --android --js --retries 2
|
||||
|
||||
@@ -144,7 +144,7 @@ npm install -g react-native-cli
|
||||
|
||||
### Xcode
|
||||
|
||||
The easiest way to install Xcode is via the [Mac App Store](https://itunes.apple.com/us/app/xcode/id497799835?mt=12). Installing Xcode will also install the iOS Simulator and all the necessary tools to build your iOS app.
|
||||
The easiest way to install Xcode 8 is via the [Mac App Store](https://itunes.apple.com/us/app/xcode/id497799835?mt=12). Installing Xcode will also install the iOS Simulator and all the necessary tools to build your iOS app.
|
||||
|
||||
You will also need to install the Xcode Command Line Tools. Open Xcode, then choose "Preferences..." from the Xcode menu. Go to the Locations panel and install the tools by selecting the most recent version in the Command Line Tools dropdown.
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
|
||||
const InspectorProxy = require('./util/inspectorProxy.js');
|
||||
const ReactPackager = require('../../packager/react-packager');
|
||||
const TerminalReporter = require('../../packager/src/lib/TerminalReporter');
|
||||
|
||||
const attachHMRServer = require('./util/attachHMRServer');
|
||||
const connect = require('connect');
|
||||
@@ -88,6 +87,21 @@ function getPackagerServer(args, config) {
|
||||
const providesModuleNodeModules =
|
||||
args.providesModuleNodeModules || defaultProvidesModuleNodeModules;
|
||||
|
||||
let LogReporter;
|
||||
if (args.customLogReporterPath) {
|
||||
try {
|
||||
// First we let require resolve it, so we can require packages in node_modules
|
||||
// as expected. eg: require('my-package/reporter');
|
||||
LogReporter = require(args.customLogReporterPath);
|
||||
} catch(e) {
|
||||
// If that doesn't work, then we next try relative to the cwd, eg:
|
||||
// require('./reporter');
|
||||
LogReporter = require(path.resolve(args.customLogReporterPath));
|
||||
}
|
||||
} else {
|
||||
LogReporter = require('../../packager/src/lib/TerminalReporter');
|
||||
}
|
||||
|
||||
return ReactPackager.createServer({
|
||||
assetExts: defaultAssetExts.concat(args.assetExts),
|
||||
blacklistRE: config.getBlacklistRE(),
|
||||
@@ -98,7 +112,7 @@ function getPackagerServer(args, config) {
|
||||
platforms: defaultPlatforms.concat(args.platforms),
|
||||
projectRoots: args.projectRoots,
|
||||
providesModuleNodeModules: providesModuleNodeModules,
|
||||
reporter: new TerminalReporter(),
|
||||
reporter: new LogReporter(),
|
||||
resetCache: args.resetCache,
|
||||
transformModulePath: transformModulePath,
|
||||
verbose: args.verbose,
|
||||
|
||||
@@ -118,6 +118,9 @@ module.exports = {
|
||||
}, {
|
||||
command: '--reset-cache, --resetCache',
|
||||
description: 'Removes cached files',
|
||||
}, {
|
||||
command: '--custom-log-reporter-path, --customLogReporterPath [string]',
|
||||
description: 'Path to a JavaScript file that exports a log reporter as a replacement for TerminalReporter',
|
||||
}, {
|
||||
command: '--verbose',
|
||||
description: 'Enables logging',
|
||||
|
||||
@@ -15,11 +15,6 @@ const spawnOpts = {
|
||||
stdin: 'inherit',
|
||||
};
|
||||
|
||||
const projectDir = process.cwd();
|
||||
const isYarnAvailable =
|
||||
yarn.getYarnVersionIfAvailable() &&
|
||||
yarn.isGlobalCliUsingYarn(projectDir);
|
||||
|
||||
/**
|
||||
* Execute npm or yarn command
|
||||
*
|
||||
@@ -30,6 +25,11 @@ const isYarnAvailable =
|
||||
function callYarnOrNpm(yarnCommand, npmCommand) {
|
||||
let command;
|
||||
|
||||
const projectDir = process.cwd();
|
||||
const isYarnAvailable =
|
||||
yarn.getYarnVersionIfAvailable() &&
|
||||
yarn.isGlobalCliUsingYarn(projectDir);
|
||||
|
||||
if (isYarnAvailable) {
|
||||
command = yarnCommand;
|
||||
} else {
|
||||
|
||||
@@ -21,11 +21,9 @@ function getYarnVersionIfAvailable() {
|
||||
let yarnVersion;
|
||||
try {
|
||||
// execSync returns a Buffer -> convert to string
|
||||
if (process.platform.startsWith('win')) {
|
||||
yarnVersion = (execSync('yarn --version').toString() || '').trim();
|
||||
} else {
|
||||
yarnVersion = (execSync('yarn --version 2>/dev/null').toString() || '').trim();
|
||||
}
|
||||
yarnVersion = (execSync('yarn --version', {
|
||||
stdio: [ 0, 'pipe', 'ignore', ]
|
||||
}).toString() || '').trim();
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
+6
-6
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native",
|
||||
"version": "1000.0.0",
|
||||
"version": "0.43.5",
|
||||
"description": "A framework for building native apps using React",
|
||||
"license": "BSD-3-Clause",
|
||||
"repository": {
|
||||
@@ -127,7 +127,7 @@
|
||||
"react-native": "local-cli/wrong-react-native.js"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "~16.0.0-alpha.3"
|
||||
"react": "16.0.0-alpha.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"absolute-path": "^0.0.0",
|
||||
@@ -222,10 +222,10 @@
|
||||
"jest-repl": "19.0.2",
|
||||
"jest-runtime": "19.0.2",
|
||||
"mock-fs": "^3.11.0",
|
||||
"react": "~16.0.0-alpha.3",
|
||||
"react-dom": "~16.0.0-alpha.3",
|
||||
"react-test-renderer": "~16.0.0-alpha.3",
|
||||
"react": "16.0.0-alpha.6",
|
||||
"react-dom": "16.0.0-alpha.6",
|
||||
"react-test-renderer": "16.0.0-alpha.6",
|
||||
"shelljs": "0.6.0",
|
||||
"sinon": "^2.0.0-pre.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,11 @@ const affixes = {prefix: 'metro-bundler-symbolicate', suffix: '.sock'};
|
||||
const childPath = require.resolve('./worker');
|
||||
|
||||
exports.createWorker = (): Symbolicate => {
|
||||
const socket = xpipe.eq(temp.path(affixes));
|
||||
// There are issues with named sockets on windows that cause the connection to
|
||||
// close too early so run the symbolicate server on a random localhost port.
|
||||
const socket = process.platform === 'win32'
|
||||
? 34712
|
||||
: xpipe.eq(temp.path(affixes));
|
||||
const child = new LockingPromise(new LazyPromise(() => startupChild(socket)));
|
||||
|
||||
return (stack, sourceMaps) =>
|
||||
@@ -53,6 +57,7 @@ function startupChild(socket) {
|
||||
child.removeAllListeners();
|
||||
resolve(child);
|
||||
});
|
||||
// $FlowFixMe ChildProcess.send should accept any type.
|
||||
child.send(socket);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user