Merge pull request #357 from facebook/updates-mar-27
Update from Friday March 27
This commit is contained in:
commit
0c83766e3b
|
@ -92,7 +92,7 @@
|
|||
/* Begin PBXFileReference section */
|
||||
00481BDB1AC0C7FA00671115 /* RCTWebSocketDebugger.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocketDebugger.xcodeproj; path = ../../Libraries/RCTWebSocketDebugger/RCTWebSocketDebugger.xcodeproj; sourceTree = "<group>"; };
|
||||
00481BE91AC0C89D00671115 /* libicucore.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libicucore.dylib; path = usr/lib/libicucore.dylib; sourceTree = SDKROOT; };
|
||||
00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = ../../Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj; sourceTree = "<absolute>"; };
|
||||
00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = ../../Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj; sourceTree = "<group>"; };
|
||||
00C302AF1ABCB8E700DB3ED1 /* RCTAdSupport.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAdSupport.xcodeproj; path = ../../Libraries/AdSupport/RCTAdSupport.xcodeproj; sourceTree = "<group>"; };
|
||||
00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = ../../Libraries/Geolocation/RCTGeolocation.xcodeproj; sourceTree = "<group>"; };
|
||||
00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = ../../Libraries/Image/RCTImage.xcodeproj; sourceTree = "<group>"; };
|
||||
|
|
|
@ -15,245 +15,133 @@
|
|||
|
||||
var React = require('react-native');
|
||||
var {
|
||||
PixelRatio,
|
||||
Navigator,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
TabBarIOS,
|
||||
ScrollView,
|
||||
Text,
|
||||
View,
|
||||
TouchableHighlight,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} = React;
|
||||
|
||||
var SAMPLE_TEXT = 'Top Pushes. Middle Replaces. Bottom Pops.';
|
||||
|
||||
var _getRandomRoute = function() {
|
||||
return {
|
||||
backButtonTitle: 'Back' + ('' + 10 * Math.random()).substr(0, 1),
|
||||
content:
|
||||
SAMPLE_TEXT + '\nHere\'s a random number ' + Math.random(),
|
||||
title: Math.random() > 0.5 ? 'Hello' : 'There',
|
||||
rightButtonTitle: Math.random() > 0.5 ? 'Right' : 'Button',
|
||||
title: '#' + Math.ceil(Math.random() * 1000),
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
var SampleNavigationBarRouteMapper = {
|
||||
rightContentForRoute: function(route, navigator) {
|
||||
if (route.rightButtonTitle) {
|
||||
return (
|
||||
<Text style={[styles.titleText, styles.filterText]}>
|
||||
{route.rightButtonTitle}
|
||||
</Text>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
titleContentForRoute: function(route, navigator) {
|
||||
class NavButton extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<TouchableHighlight
|
||||
onPress={() => navigator.push(_getRandomRoute())}>
|
||||
<View>
|
||||
<Text style={styles.titleText}>{route.title}</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
},
|
||||
iconForRoute: function(route, navigator) {
|
||||
var onPress =
|
||||
navigator.popToRoute.bind(navigator, route);
|
||||
return (
|
||||
<TouchableHighlight onPress={onPress}>
|
||||
<View style={styles.crumbIconPlaceholder} />
|
||||
</TouchableHighlight>
|
||||
);
|
||||
},
|
||||
separatorForRoute: function(route, navigator) {
|
||||
return (
|
||||
<TouchableHighlight onPress={navigator.pop}>
|
||||
<View style={styles.crumbSeparatorPlaceholder} />
|
||||
style={styles.button}
|
||||
underlayColor="#B5B5B5"
|
||||
onPress={this.props.onPress}>
|
||||
<Text style={styles.buttonText}>{this.props.text}</Text>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
var _delay = 400; // Just to test for race conditions with native nav.
|
||||
|
||||
var renderScene = function(route, navigator) {
|
||||
var content = route.content;
|
||||
return (
|
||||
<ScrollView>
|
||||
<View style={styles.scene}>
|
||||
<TouchableHighlight
|
||||
onPress={_pushRouteLater(navigator.push)}>
|
||||
<View style={styles.button}>
|
||||
<Text style={styles.buttonText}>request push soon</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
onPress={_pushRouteLater(navigator.replace)}>
|
||||
<View style={styles.button}>
|
||||
<Text>{content}</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
onPress={_pushRouteLater(navigator.replace)}>
|
||||
<View style={styles.button}>
|
||||
<Text>{content}</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
onPress={_pushRouteLater(navigator.replace)}>
|
||||
<View style={styles.button}>
|
||||
<Text>{content}</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
onPress={_pushRouteLater(navigator.replace)}>
|
||||
<View style={styles.button}>
|
||||
<Text>{content}</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
onPress={_pushRouteLater(navigator.replace)}>
|
||||
<View style={styles.button}>
|
||||
<Text>{content}</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
onPress={_popRouteLater(navigator.pop)}>
|
||||
<View style={styles.button}>
|
||||
<Text style={styles.buttonText}>request pop soon</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
onPress={
|
||||
_immediatelySetTwoItemsLater(
|
||||
navigator.immediatelyResetRouteStack
|
||||
)
|
||||
}>
|
||||
<View style={styles.button}>
|
||||
<Text style={styles.buttonText}>Immediate set two routes</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
onPress={_popToTopLater(navigator.popToTop)}>
|
||||
<View style={styles.button}>
|
||||
<Text style={styles.buttonText}>pop to top soon</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
var _popToTopLater = function(popToTop) {
|
||||
return () => setTimeout(popToTop, _delay);
|
||||
};
|
||||
|
||||
var _pushRouteLater = function(push) {
|
||||
return () => setTimeout(
|
||||
() => push(_getRandomRoute()),
|
||||
_delay
|
||||
);
|
||||
};
|
||||
|
||||
var _immediatelySetTwoItemsLater = function(immediatelyResetRouteStack) {
|
||||
return () => setTimeout(
|
||||
() => immediatelyResetRouteStack([
|
||||
_getRandomRoute(),
|
||||
_getRandomRoute(),
|
||||
])
|
||||
);
|
||||
};
|
||||
|
||||
var _popRouteLater = function(pop) {
|
||||
return () => setTimeout(pop, _delay);
|
||||
};
|
||||
}
|
||||
|
||||
var BreadcrumbNavSample = React.createClass({
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
selectedTab: 0,
|
||||
componentWillMount: function() {
|
||||
this._navBarRouteMapper = {
|
||||
rightContentForRoute: function(route, navigator) {
|
||||
return null;
|
||||
},
|
||||
titleContentForRoute: function(route, navigator) {
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={() => navigator.push(_getRandomRoute())}>
|
||||
<View>
|
||||
<Text style={styles.titleText}>{route.title}</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
},
|
||||
iconForRoute: function(route, navigator) {
|
||||
return (
|
||||
<TouchableOpacity onPress={() => {
|
||||
navigator.popToRoute(route);
|
||||
}}>
|
||||
<View style={styles.crumbIconPlaceholder} />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
},
|
||||
separatorForRoute: function(route, navigator) {
|
||||
return (
|
||||
<TouchableOpacity onPress={navigator.pop}>
|
||||
<View style={styles.crumbSeparatorPlaceholder} />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var initialRoute = {
|
||||
backButtonTitle: 'Start', // no back button for initial scene
|
||||
content: SAMPLE_TEXT,
|
||||
title: 'Campaigns',
|
||||
rightButtonTitle: 'Filter',
|
||||
};
|
||||
_renderScene: function(route, navigator) {
|
||||
return (
|
||||
<TabBarIOS>
|
||||
<TabBarIOS.Item
|
||||
selected={this.state.selectedTab === 0}
|
||||
onPress={this.onTabSelect.bind(this, 0)}
|
||||
icon={require('image!tabnav_list')}
|
||||
title="One">
|
||||
<Navigator
|
||||
debugOverlay={false}
|
||||
style={[styles.appContainer]}
|
||||
initialRoute={initialRoute}
|
||||
renderScene={renderScene}
|
||||
navigationBar={
|
||||
<Navigator.BreadcrumbNavigationBar
|
||||
navigationBarRouteMapper={SampleNavigationBarRouteMapper}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</TabBarIOS.Item>
|
||||
<TabBarIOS.Item
|
||||
selected={this.state.selectedTab === 1}
|
||||
onPress={this.onTabSelect.bind(this, 1)}
|
||||
icon={require('image!tabnav_notification')}
|
||||
title="Two">
|
||||
<Navigator
|
||||
configureScene={() => Navigator.SceneConfigs.FloatFromBottom}
|
||||
debugOverlay={false}
|
||||
style={[styles.appContainer]}
|
||||
initialRoute={initialRoute}
|
||||
renderScene={renderScene}
|
||||
navigationBar={
|
||||
<Navigator.BreadcrumbNavigationBar
|
||||
navigationBarRouteMapper={SampleNavigationBarRouteMapper}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</TabBarIOS.Item>
|
||||
</TabBarIOS>
|
||||
<ScrollView style={styles.scene}>
|
||||
<NavButton
|
||||
onPress={() => { navigator.push(_getRandomRoute()) }}
|
||||
text="Push"
|
||||
/>
|
||||
<NavButton
|
||||
onPress={() => { navigator.immediatelyResetRouteStack([_getRandomRoute(), _getRandomRoute()]) }}
|
||||
text="Reset w/ 2 scenes"
|
||||
/>
|
||||
<NavButton
|
||||
onPress={() => { navigator.popToTop() }}
|
||||
text="Pop to top"
|
||||
/>
|
||||
<NavButton
|
||||
onPress={() => { navigator.replace(_getRandomRoute()) }}
|
||||
text="Replace"
|
||||
/>
|
||||
<NavButton
|
||||
onPress={() => { this.props.navigator.pop(); }}
|
||||
text="Close breadcrumb example"
|
||||
/>
|
||||
</ScrollView>
|
||||
);
|
||||
},
|
||||
|
||||
onTabSelect: function(tab, event) {
|
||||
if (this.state.selectedTab !== tab) {
|
||||
this.setState({selectedTab: tab});
|
||||
}
|
||||
render: function() {
|
||||
return (
|
||||
<Navigator
|
||||
style={styles.container}
|
||||
initialRoute={_getRandomRoute()}
|
||||
renderScene={this._renderScene}
|
||||
navigationBar={
|
||||
<Navigator.BreadcrumbNavigationBar
|
||||
navigationBarRouteMapper={this._navBarRouteMapper}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
navigationItem: {
|
||||
backgroundColor: '#eeeeee',
|
||||
},
|
||||
scene: {
|
||||
paddingTop: 50,
|
||||
flex: 1,
|
||||
},
|
||||
button: {
|
||||
backgroundColor: '#cccccc',
|
||||
margin: 50,
|
||||
marginTop: 26,
|
||||
padding: 10,
|
||||
backgroundColor: 'white',
|
||||
padding: 15,
|
||||
borderBottomWidth: 1 / PixelRatio.get(),
|
||||
borderBottomColor: '#CDCDCD',
|
||||
},
|
||||
buttonText: {
|
||||
fontSize: 12,
|
||||
textAlign: 'center',
|
||||
fontSize: 17,
|
||||
fontWeight: '500',
|
||||
},
|
||||
appContainer: {
|
||||
container: {
|
||||
overflow: 'hidden',
|
||||
backgroundColor: '#dddddd',
|
||||
flex: 1,
|
||||
|
@ -262,13 +150,9 @@ var styles = StyleSheet.create({
|
|||
fontSize: 18,
|
||||
color: '#666666',
|
||||
textAlign: 'center',
|
||||
fontWeight: '500',
|
||||
fontWeight: 'bold',
|
||||
lineHeight: 32,
|
||||
},
|
||||
filterText: {
|
||||
color: '#5577ff',
|
||||
},
|
||||
// TODO: Accept icons from route.
|
||||
crumbIconPlaceholder: {
|
||||
flex: 1,
|
||||
backgroundColor: '#666666',
|
||||
|
|
|
@ -16,8 +16,10 @@
|
|||
var React = require('react-native');
|
||||
var {
|
||||
Navigator,
|
||||
PixelRatio,
|
||||
StyleSheet,
|
||||
ScrollView,
|
||||
TabBarIOS,
|
||||
Text,
|
||||
TouchableHighlight,
|
||||
View,
|
||||
|
@ -25,178 +27,186 @@ var {
|
|||
|
||||
var _getRandomRoute = function() {
|
||||
return {
|
||||
randNumber: Math.random(),
|
||||
randNumber: Math.ceil(Math.random() * 1000),
|
||||
};
|
||||
};
|
||||
|
||||
var INIT_ROUTE = _getRandomRoute();
|
||||
class NavButton extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<TouchableHighlight
|
||||
style={styles.button}
|
||||
underlayColor="#B5B5B5"
|
||||
onPress={this.props.onPress}>
|
||||
<Text style={styles.buttonText}>{this.props.text}</Text>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var ROUTE_STACK = [
|
||||
_getRandomRoute(),
|
||||
_getRandomRoute(),
|
||||
INIT_ROUTE,
|
||||
_getRandomRoute(),
|
||||
_getRandomRoute(),
|
||||
];
|
||||
var renderScene = function(route, navigator) {
|
||||
return (
|
||||
<ScrollView style={styles.scene}>
|
||||
<View style={styles.scroll}>
|
||||
<Text>{route.randNumber}</Text>
|
||||
<TouchableHighlight
|
||||
onPress={() => {
|
||||
navigator.jumpBack();
|
||||
}}>
|
||||
<View style={styles.button}>
|
||||
<Text style={styles.buttonText}>jumpBack</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
onPress={() => {
|
||||
navigator.jumpForward();
|
||||
}}>
|
||||
<View style={styles.button}>
|
||||
<Text style={styles.buttonText}>jumpForward</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
onPress={() => {
|
||||
navigator.jumpTo(INIT_ROUTE);
|
||||
}}>
|
||||
<View style={styles.button}>
|
||||
<Text style={styles.buttonText}>jumpTo initial route</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
onPress={() => {
|
||||
navigator.push(_getRandomRoute());
|
||||
}}>
|
||||
<View style={styles.button}>
|
||||
<Text style={styles.buttonText}>destructive: push</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
onPress={() => {
|
||||
navigator.replace(_getRandomRoute());
|
||||
}}>
|
||||
<View style={styles.button}>
|
||||
<Text style={styles.buttonText}>destructive: replace</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
onPress={() => {
|
||||
navigator.pop();
|
||||
}}>
|
||||
<View style={styles.button}>
|
||||
<Text style={styles.buttonText}>destructive: pop</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
onPress={() => {
|
||||
navigator.immediatelyResetRouteStack([
|
||||
_getRandomRoute(),
|
||||
_getRandomRoute(),
|
||||
]);
|
||||
}}>
|
||||
<View style={styles.button}>
|
||||
<Text style={styles.buttonText}>destructive: Immediate set two routes</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
onPress={() => {
|
||||
navigator.popToTop();
|
||||
}}>
|
||||
<View style={styles.button}>
|
||||
<Text style={styles.buttonText}>destructive: pop to top</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
var INIT_ROUTE_INDEX = 1;
|
||||
|
||||
class JumpingNavBar extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.navBar}>
|
||||
{this.props.routeStack.map((route, index) => (
|
||||
<TouchableHighlight onPress={() => {
|
||||
this.props.navigator.jumpTo(route);
|
||||
}}>
|
||||
<View style={styles.navButton}>
|
||||
<Text
|
||||
style={[
|
||||
styles.navButtonText,
|
||||
this.props.navState.toIndex === index && styles.navButtonActive
|
||||
]}>
|
||||
{index}
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
))}
|
||||
<View style={styles.tabs}>
|
||||
<TabBarIOS
|
||||
selectedTab={'tab_' + this.props.tabIndex}>
|
||||
<TabBarIOS.Item
|
||||
name="tab_0"
|
||||
icon={require('image!tabnav_notification')}
|
||||
selected={this.props.tabIndex === 0}
|
||||
onPress={() => { this.props.onTabIndex(0); }}
|
||||
children={<View />}
|
||||
/>
|
||||
<TabBarIOS.Item
|
||||
name="tab_1"
|
||||
icon={require('image!tabnav_list')}
|
||||
selected={this.props.tabIndex === 1}
|
||||
onPress={() => { this.props.onTabIndex(1); }}
|
||||
children={<View />}
|
||||
/>
|
||||
<TabBarIOS.Item
|
||||
name="tab_2"
|
||||
icon={require('image!tabnav_settings')}
|
||||
selected={this.props.tabIndex === 2}
|
||||
onPress={() => { this.props.onTabIndex(2); }}
|
||||
children={<View />}
|
||||
/>
|
||||
</TabBarIOS>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var JumpingNavSample = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
tabIndex: INIT_ROUTE_INDEX,
|
||||
};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<Navigator
|
||||
debugOverlay={false}
|
||||
style={[styles.appContainer]}
|
||||
initialRoute={INIT_ROUTE}
|
||||
style={styles.appContainer}
|
||||
ref={(navigator) => {
|
||||
this._navigator = navigator;
|
||||
}}
|
||||
initialRoute={ROUTE_STACK[INIT_ROUTE_INDEX]}
|
||||
initialRouteStack={ROUTE_STACK}
|
||||
renderScene={renderScene}
|
||||
navigationBar={<JumpingNavBar routeStack={ROUTE_STACK} />}
|
||||
renderScene={this.renderScene}
|
||||
navigationBar={
|
||||
<JumpingNavBar
|
||||
routeStack={ROUTE_STACK}
|
||||
tabIndex={this.state.tabIndex}
|
||||
onTabIndex={(index) => {
|
||||
this.setState({ tabIndex: index }, () => {
|
||||
this._navigator.jumpTo(ROUTE_STACK[index]);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
}
|
||||
onWillFocus={(route) => {
|
||||
this.setState({
|
||||
tabIndex: ROUTE_STACK.indexOf(route),
|
||||
});
|
||||
}}
|
||||
shouldJumpOnBackstackPop={true}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
renderScene: function(route, navigator) {
|
||||
var backBtn;
|
||||
var forwardBtn;
|
||||
if (ROUTE_STACK.indexOf(route) !== 0) {
|
||||
backBtn = (
|
||||
<NavButton
|
||||
onPress={() => {
|
||||
navigator.jumpBack();
|
||||
}}
|
||||
text="jumpBack"
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (ROUTE_STACK.indexOf(route) !== ROUTE_STACK.length - 1) {
|
||||
forwardBtn = (
|
||||
<NavButton
|
||||
onPress={() => {
|
||||
navigator.jumpForward();
|
||||
}}
|
||||
text="jumpForward"
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<ScrollView style={styles.scene}>
|
||||
<Text style={styles.messageText}>#{route.randNumber}</Text>
|
||||
{backBtn}
|
||||
{forwardBtn}
|
||||
<NavButton
|
||||
onPress={() => {
|
||||
navigator.jumpTo(ROUTE_STACK[1]);
|
||||
}}
|
||||
text="jumpTo middle route"
|
||||
/>
|
||||
<NavButton
|
||||
onPress={() => {
|
||||
this.props.navigator.pop();
|
||||
}}
|
||||
text="Exit Navigation Example"
|
||||
/>
|
||||
<NavButton
|
||||
onPress={() => {
|
||||
this.props.navigator.push({
|
||||
message: 'Came from jumping example',
|
||||
});
|
||||
}}
|
||||
text="Nav Menu"
|
||||
/>
|
||||
</ScrollView>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
scene: {
|
||||
backgroundColor: '#eeeeee',
|
||||
},
|
||||
scroll: {
|
||||
flex: 1,
|
||||
},
|
||||
button: {
|
||||
backgroundColor: '#cccccc',
|
||||
margin: 50,
|
||||
marginTop: 26,
|
||||
padding: 10,
|
||||
backgroundColor: 'white',
|
||||
padding: 15,
|
||||
borderBottomWidth: 1 / PixelRatio.get(),
|
||||
borderBottomColor: '#CDCDCD',
|
||||
},
|
||||
buttonText: {
|
||||
fontSize: 12,
|
||||
textAlign: 'center',
|
||||
fontSize: 17,
|
||||
fontWeight: '500',
|
||||
},
|
||||
appContainer: {
|
||||
overflow: 'hidden',
|
||||
backgroundColor: '#dddddd',
|
||||
flex: 1,
|
||||
},
|
||||
navBar: {
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: 90,
|
||||
flexDirection: 'row',
|
||||
messageText: {
|
||||
fontSize: 17,
|
||||
fontWeight: '500',
|
||||
padding: 15,
|
||||
marginTop: 50,
|
||||
marginLeft: 15,
|
||||
},
|
||||
navButton: {
|
||||
scene: {
|
||||
flex: 1,
|
||||
paddingTop: 20,
|
||||
backgroundColor: '#EAEAEA',
|
||||
},
|
||||
navButtonText: {
|
||||
textAlign: 'center',
|
||||
fontSize: 32,
|
||||
marginTop: 25,
|
||||
},
|
||||
navButtonActive: {
|
||||
color: 'green',
|
||||
},
|
||||
tabs: {
|
||||
height: 50,
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = JumpingNavSample;
|
||||
|
|
|
@ -16,15 +16,30 @@
|
|||
|
||||
var React = require('react-native');
|
||||
var {
|
||||
PixelRatio,
|
||||
Navigator,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableHighlight,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} = React;
|
||||
|
||||
var cssVar = require('cssVar');
|
||||
|
||||
class NavButton extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<TouchableHighlight
|
||||
style={styles.button}
|
||||
underlayColor="#B5B5B5"
|
||||
onPress={this.props.onPress}>
|
||||
<Text style={styles.buttonText}>{this.props.text}</Text>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var NavigationBarRouteMapper = {
|
||||
|
||||
|
@ -35,26 +50,27 @@ var NavigationBarRouteMapper = {
|
|||
|
||||
var previousRoute = navState.routeStack[index - 1];
|
||||
return (
|
||||
<TouchableHighlight onPress={() => navigator.pop()}>
|
||||
<View>
|
||||
<TouchableOpacity
|
||||
onPress={() => navigator.pop()}>
|
||||
<View style={styles.navBarLeftButton}>
|
||||
<Text style={[styles.navBarText, styles.navBarButtonText]}>
|
||||
{previousRoute.title}
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
},
|
||||
|
||||
RightButton: function(route, navigator, index, navState) {
|
||||
return (
|
||||
<TouchableHighlight
|
||||
<TouchableOpacity
|
||||
onPress={() => navigator.push(newRandomRoute())}>
|
||||
<View>
|
||||
<View style={styles.navBarRightButton}>
|
||||
<Text style={[styles.navBarText, styles.navBarButtonText]}>
|
||||
Next
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -70,8 +86,7 @@ var NavigationBarRouteMapper = {
|
|||
|
||||
function newRandomRoute() {
|
||||
return {
|
||||
content: 'Hello World!',
|
||||
title: 'Random ' + Math.round(Math.random() * 100),
|
||||
title: '#' + Math.ceil(Math.random() * 1000),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -79,37 +94,63 @@ var NavigationBarSample = React.createClass({
|
|||
|
||||
render: function() {
|
||||
return (
|
||||
<View style={styles.appContainer}>
|
||||
<Navigator
|
||||
debugOverlay={false}
|
||||
style={styles.appContainer}
|
||||
initialRoute={newRandomRoute()}
|
||||
renderScene={(route, navigator) => (
|
||||
<View style={styles.scene}>
|
||||
<Text>{route.content}</Text>
|
||||
</View>
|
||||
)}
|
||||
navigationBar={
|
||||
<Navigator.NavigationBar
|
||||
navigationBarRouteMapper={NavigationBarRouteMapper}
|
||||
<Navigator
|
||||
debugOverlay={false}
|
||||
style={styles.appContainer}
|
||||
initialRoute={newRandomRoute()}
|
||||
renderScene={(route, navigator) => (
|
||||
<ScrollView style={styles.scene}>
|
||||
<Text style={styles.messageText}>{route.content}</Text>
|
||||
<NavButton
|
||||
onPress={() => {
|
||||
navigator.immediatelyResetRouteStack([
|
||||
newRandomRoute(),
|
||||
newRandomRoute(),
|
||||
newRandomRoute(),
|
||||
]);
|
||||
}}
|
||||
text="Reset w/ 3 scenes"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
<NavButton
|
||||
onPress={() => {
|
||||
this.props.navigator.pop();
|
||||
}}
|
||||
text="Exit NavigationBar Example"
|
||||
/>
|
||||
</ScrollView>
|
||||
)}
|
||||
navigationBar={
|
||||
<Navigator.NavigationBar
|
||||
navigationBarRouteMapper={NavigationBarRouteMapper}
|
||||
navigationBarStyles={styles.navBar}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
appContainer: {
|
||||
overflow: 'hidden',
|
||||
backgroundColor: '#ffffff',
|
||||
flex: 1,
|
||||
messageText: {
|
||||
fontSize: 17,
|
||||
fontWeight: '500',
|
||||
padding: 15,
|
||||
marginTop: 50,
|
||||
marginLeft: 15,
|
||||
},
|
||||
scene: {
|
||||
paddingTop: 50,
|
||||
flex: 1,
|
||||
button: {
|
||||
backgroundColor: 'white',
|
||||
padding: 15,
|
||||
borderBottomWidth: 1 / PixelRatio.get(),
|
||||
borderBottomColor: '#CDCDCD',
|
||||
},
|
||||
buttonText: {
|
||||
fontSize: 17,
|
||||
fontWeight: '500',
|
||||
},
|
||||
navBar: {
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
navBarText: {
|
||||
fontSize: 16,
|
||||
|
@ -120,9 +161,20 @@ var styles = StyleSheet.create({
|
|||
fontWeight: '500',
|
||||
marginVertical: 9,
|
||||
},
|
||||
navBarLeftButton: {
|
||||
paddingLeft: 10,
|
||||
},
|
||||
navBarRightButton: {
|
||||
paddingRight: 10,
|
||||
},
|
||||
navBarButtonText: {
|
||||
color: cssVar('fbui-accent-blue'),
|
||||
},
|
||||
scene: {
|
||||
flex: 1,
|
||||
paddingTop: 20,
|
||||
backgroundColor: '#EAEAEA',
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = NavigationBarSample;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
var React = require('react-native');
|
||||
var {
|
||||
Navigator,
|
||||
PixelRatio,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
|
@ -25,30 +26,78 @@ var BreadcrumbNavSample = require('./BreadcrumbNavSample');
|
|||
var NavigationBarSample = require('./NavigationBarSample');
|
||||
var JumpingNavSample = require('./JumpingNavSample');
|
||||
|
||||
class NavButton extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<TouchableHighlight
|
||||
style={styles.button}
|
||||
underlayColor="#B5B5B5"
|
||||
onPress={this.props.onPress}>
|
||||
<Text style={styles.buttonText}>{this.props.text}</Text>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NavMenu extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<ScrollView style={styles.scene}>
|
||||
<TouchableHighlight style={styles.button} onPress={() => {
|
||||
this.props.navigator.push({ id: 'breadcrumbs' });
|
||||
}}>
|
||||
<Text style={styles.buttonText}>Breadcrumbs Example</Text>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight style={styles.button} onPress={() => {
|
||||
this.props.navigator.push({ id: 'navbar' });
|
||||
}}>
|
||||
<Text style={styles.buttonText}>Navbar Example</Text>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight style={styles.button} onPress={() => {
|
||||
this.props.navigator.push({ id: 'jumping' });
|
||||
}}>
|
||||
<Text style={styles.buttonText}>Jumping Example</Text>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight style={styles.button} onPress={() => {
|
||||
this.props.onExampleExit();
|
||||
}}>
|
||||
<Text style={styles.buttonText}>Exit Navigator Example</Text>
|
||||
</TouchableHighlight>
|
||||
<Text style={styles.messageText}>{this.props.message}</Text>
|
||||
<NavButton
|
||||
onPress={() => {
|
||||
this.props.navigator.push({
|
||||
message: 'Swipe right to dismiss',
|
||||
sceneConfig: Navigator.SceneConfigs.FloatFromRight,
|
||||
});
|
||||
}}
|
||||
text="Float in from right"
|
||||
/>
|
||||
<NavButton
|
||||
onPress={() => {
|
||||
this.props.navigator.push({
|
||||
message: 'Swipe down to dismiss',
|
||||
sceneConfig: Navigator.SceneConfigs.FloatFromBottom,
|
||||
});
|
||||
}}
|
||||
text="Float in from bottom"
|
||||
/>
|
||||
<NavButton
|
||||
onPress={() => {
|
||||
this.props.navigator.pop();
|
||||
}}
|
||||
text="Pop"
|
||||
/>
|
||||
<NavButton
|
||||
onPress={() => {
|
||||
this.props.navigator.popToTop();
|
||||
}}
|
||||
text="Pop to top"
|
||||
/>
|
||||
<NavButton
|
||||
onPress={() => {
|
||||
this.props.navigator.push({ id: 'navbar' });
|
||||
}}
|
||||
text="Navbar Example"
|
||||
/>
|
||||
<NavButton
|
||||
onPress={() => {
|
||||
this.props.navigator.push({ id: 'jumping' });
|
||||
}}
|
||||
text="Jumping Example"
|
||||
/>
|
||||
<NavButton
|
||||
onPress={() => {
|
||||
this.props.navigator.push({ id: 'breadcrumbs' });
|
||||
}}
|
||||
text="Breadcrumbs Example"
|
||||
/>
|
||||
<NavButton
|
||||
onPress={() => {
|
||||
this.props.onExampleExit();
|
||||
}}
|
||||
text="Exit <Navigator> Example"
|
||||
/>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
@ -63,19 +112,20 @@ var TabBarExample = React.createClass({
|
|||
|
||||
renderScene: function(route, nav) {
|
||||
switch (route.id) {
|
||||
case 'menu':
|
||||
case 'navbar':
|
||||
return <NavigationBarSample navigator={nav} />;
|
||||
case 'breadcrumbs':
|
||||
return <BreadcrumbNavSample navigator={nav} />;
|
||||
case 'jumping':
|
||||
return <JumpingNavSample navigator={nav} />;
|
||||
default:
|
||||
return (
|
||||
<NavMenu
|
||||
message={route.message}
|
||||
navigator={nav}
|
||||
onExampleExit={this.props.onExampleExit}
|
||||
/>
|
||||
);
|
||||
case 'navbar':
|
||||
return <NavigationBarSample />;
|
||||
case 'breadcrumbs':
|
||||
return <BreadcrumbNavSample />;
|
||||
case 'jumping':
|
||||
return <JumpingNavSample />;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -83,9 +133,14 @@ var TabBarExample = React.createClass({
|
|||
return (
|
||||
<Navigator
|
||||
style={styles.container}
|
||||
initialRoute={{ id: 'menu', }}
|
||||
initialRoute={{ message: "First Scene", }}
|
||||
renderScene={this.renderScene}
|
||||
configureScene={(route) => Navigator.SceneConfigs.FloatFromBottom}
|
||||
configureScene={(route) => {
|
||||
if (route.sceneConfig) {
|
||||
return route.sceneConfig;
|
||||
}
|
||||
return Navigator.SceneConfigs.FloatFromBottom;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
@ -93,18 +148,30 @@ var TabBarExample = React.createClass({
|
|||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
messageText: {
|
||||
fontSize: 17,
|
||||
fontWeight: '500',
|
||||
padding: 15,
|
||||
marginTop: 50,
|
||||
marginLeft: 15,
|
||||
},
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
button: {
|
||||
backgroundColor: 'white',
|
||||
padding: 15,
|
||||
borderBottomWidth: 1 / PixelRatio.get(),
|
||||
borderBottomColor: '#CDCDCD',
|
||||
},
|
||||
buttonText: {
|
||||
fontSize: 17,
|
||||
fontWeight: '500',
|
||||
},
|
||||
scene: {
|
||||
flex: 1,
|
||||
paddingTop: 64,
|
||||
paddingTop: 20,
|
||||
backgroundColor: '#EAEAEA',
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:UIExplorer.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
Binary file not shown.
Before Width: | Height: | Size: 262 KiB After Width: | Height: | Size: 253 KiB |
|
@ -11,14 +11,18 @@
|
|||
*/
|
||||
'use strict';
|
||||
|
||||
var POPAnimation = require('POPAnimation');
|
||||
var POPAnimationOrNull = require('POPAnimation');
|
||||
|
||||
if (!POPAnimation) {
|
||||
if (!POPAnimationOrNull) {
|
||||
// POP animation isn't available in the OSS fork - this is a temporary
|
||||
// workaround to enable its availability to be determined at runtime.
|
||||
module.exports = (null : ?{});
|
||||
} else {
|
||||
|
||||
// At this point, POPAnimationOrNull is guaranteed to be
|
||||
// non-null. Bring it local to preserve type refinement.
|
||||
var POPAnimation = POPAnimationOrNull;
|
||||
|
||||
var invariant = require('invariant');
|
||||
var warning = require('warning');
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule POPAnimation
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
|
@ -14,7 +15,7 @@ var RCTPOPAnimationManager = require('NativeModules').POPAnimationManager;
|
|||
if (!RCTPOPAnimationManager) {
|
||||
// POP animation isn't available in the OSS fork - this is a temporary
|
||||
// workaround to enable its availability to be determined at runtime.
|
||||
module.exports = null;
|
||||
module.exports = (null: ?Object);
|
||||
} else {
|
||||
|
||||
var ReactPropTypes = require('ReactPropTypes');
|
||||
|
@ -64,6 +65,20 @@ var Types = {
|
|||
spring: RCTTypes.spring,
|
||||
};
|
||||
|
||||
type Attrs = {
|
||||
type?: $Enum<typeof Types>;
|
||||
property?: $Enum<typeof Properties>;
|
||||
fromValue?: any;
|
||||
toValue?: any;
|
||||
duration?: any;
|
||||
velocity?: any;
|
||||
deceleration?: any;
|
||||
springBounciness?: any;
|
||||
dynamicsFriction?: any;
|
||||
dynamicsMass?: any;
|
||||
dynamicsTension?: any;
|
||||
}
|
||||
|
||||
var POPAnimation = {
|
||||
Types: Types,
|
||||
Properties: Properties,
|
||||
|
@ -83,11 +98,11 @@ var POPAnimation = {
|
|||
}),
|
||||
|
||||
lastUsedTag: 0,
|
||||
allocateTagForAnimation: function() {
|
||||
allocateTagForAnimation: function(): number {
|
||||
return ++this.lastUsedTag;
|
||||
},
|
||||
|
||||
createAnimation: function(typeName, attrs) {
|
||||
createAnimation: function(typeName: number, attrs: Attrs): number {
|
||||
var tag = this.allocateTagForAnimation();
|
||||
|
||||
if (__DEV__) {
|
||||
|
@ -107,35 +122,35 @@ var POPAnimation = {
|
|||
return tag;
|
||||
},
|
||||
|
||||
createSpringAnimation: function(attrs) {
|
||||
createSpringAnimation: function(attrs: Attrs): number {
|
||||
return this.createAnimation(this.Types.spring, attrs);
|
||||
},
|
||||
|
||||
createDecayAnimation: function(attrs) {
|
||||
createDecayAnimation: function(attrs: Attrs): number {
|
||||
return this.createAnimation(this.Types.decay, attrs);
|
||||
},
|
||||
|
||||
createLinearAnimation: function(attrs) {
|
||||
createLinearAnimation: function(attrs: Attrs): number {
|
||||
return this.createAnimation(this.Types.linear, attrs);
|
||||
},
|
||||
|
||||
createEaseInAnimation: function(attrs) {
|
||||
createEaseInAnimation: function(attrs: Attrs): number {
|
||||
return this.createAnimation(this.Types.easeIn, attrs);
|
||||
},
|
||||
|
||||
createEaseOutAnimation: function(attrs) {
|
||||
createEaseOutAnimation: function(attrs: Attrs): number {
|
||||
return this.createAnimation(this.Types.easeOut, attrs);
|
||||
},
|
||||
|
||||
createEaseInEaseOutAnimation: function(attrs) {
|
||||
createEaseInEaseOutAnimation: function(attrs: Attrs): number {
|
||||
return this.createAnimation(this.Types.easeInEaseOut, attrs);
|
||||
},
|
||||
|
||||
addAnimation: function(nodeHandle, anim, callback) {
|
||||
addAnimation: function(nodeHandle: any, anim: number, callback: Function) {
|
||||
RCTPOPAnimationManager.addAnimation(nodeHandle, anim, callback);
|
||||
},
|
||||
|
||||
removeAnimation: function(nodeHandle, anim) {
|
||||
removeAnimation: function(nodeHandle: any, anim: number) {
|
||||
RCTPOPAnimationManager.removeAnimation(nodeHandle, anim);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -95,20 +95,20 @@ var styles = StyleSheet.create({
|
|||
});
|
||||
|
||||
/**
|
||||
* Use `ReactNavigator` to transition between different scenes in your app. To
|
||||
* Use `Navigator` to transition between different scenes in your app. To
|
||||
* accomplish this, provide route objects to the navigator to identify each
|
||||
* scene, and also a `renderScene` function that the navigator can use to
|
||||
* render the scene for a given route.
|
||||
*
|
||||
* To change the animation or gesture properties of the scene, provide a
|
||||
* `configureScene` prop to get the config object for a given route. See
|
||||
* `ReactNavigator.SceneConfigs` for default animations and more info on
|
||||
* `Navigator.SceneConfigs` for default animations and more info on
|
||||
* scene config options.
|
||||
*
|
||||
* ### Basic Usage
|
||||
*
|
||||
* ```
|
||||
* <ReactNavigator
|
||||
* <Navigator
|
||||
* initialRoute={{name: 'My First Scene', index: 0}}
|
||||
* renderScene={(route, navigator) =>
|
||||
* <MySceneComponent
|
||||
|
@ -132,7 +132,7 @@ var styles = StyleSheet.create({
|
|||
*
|
||||
* ### Navigation Methods
|
||||
*
|
||||
* `ReactNavigator` can be told to navigate in two ways. If you have a ref to
|
||||
* `Navigator` can be told to navigate in two ways. If you have a ref to
|
||||
* the element, you can invoke several methods on it to trigger navigation:
|
||||
*
|
||||
* - `jumpBack()` - Jump backward without unmounting the current scene
|
||||
|
@ -174,7 +174,7 @@ var Navigator = React.createClass({
|
|||
* configuration object
|
||||
*
|
||||
* ```
|
||||
* (route) => ReactNavigator.SceneConfigs.FloatFromRight
|
||||
* (route) => Navigator.SceneConfigs.FloatFromRight
|
||||
* ```
|
||||
*/
|
||||
configureScene: PropTypes.func,
|
||||
|
@ -230,7 +230,7 @@ var Navigator = React.createClass({
|
|||
navigationBar: PropTypes.node,
|
||||
|
||||
/**
|
||||
* Optionally provide the navigator object from a parent ReactNavigator
|
||||
* Optionally provide the navigator object from a parent Navigator
|
||||
*/
|
||||
navigator: PropTypes.object,
|
||||
|
||||
|
@ -316,7 +316,7 @@ var Navigator = React.createClass({
|
|||
parentNavigator: this.props.navigator,
|
||||
// We want to bubble focused routes to the top navigation stack. If we
|
||||
// are a child navigator, this allows us to call props.navigator.on*Focus
|
||||
// of the topmost ReactNavigator
|
||||
// of the topmost Navigator
|
||||
onWillFocus: this.props.onWillFocus,
|
||||
onDidFocus: this.props.onDidFocus,
|
||||
};
|
||||
|
|
|
@ -37,7 +37,8 @@ var Geolocation = {
|
|||
getCurrentPosition: function(
|
||||
geo_success: Function,
|
||||
geo_error?: Function,
|
||||
geo_options?: Object) {
|
||||
geo_options?: Object
|
||||
) {
|
||||
invariant(
|
||||
typeof geo_success === 'function',
|
||||
'Must provide a valid geo_success callback.'
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule Image
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
|
@ -106,7 +107,9 @@ var Image = React.createClass({
|
|||
|
||||
render: function() {
|
||||
var style = flattenStyle([styles.base, this.props.style]);
|
||||
invariant(style, "style must be initialized");
|
||||
var source = this.props.source;
|
||||
invariant(source, "source must be initialized");
|
||||
var isNetwork = source.uri && source.uri.match(/^https?:/);
|
||||
invariant(
|
||||
!(isNetwork && source.isStatic),
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ImageResizeMode
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ImageStylePropTypes
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
|
@ -39,8 +40,8 @@ var unsupportedProps = Object.keys({
|
|||
paddingHorizontal: null,
|
||||
});
|
||||
|
||||
for (var key in unsupportedProps) {
|
||||
delete ImageStylePropTypes[key];
|
||||
for (var i = 0; i < unsupportedProps.length; i++) {
|
||||
delete ImageStylePropTypes[unsupportedProps[i]];
|
||||
}
|
||||
|
||||
module.exports = ImageStylePropTypes;
|
||||
|
|
|
@ -75,6 +75,7 @@
|
|||
} else {
|
||||
_downloadToken = [_imageDownloader downloadImageForURL:imageURL size:self.bounds.size scale:RCTScreenScale() block:^(UIImage *image, NSError *error) {
|
||||
if (image) {
|
||||
[self.layer removeAnimationForKey:@"contents"];
|
||||
self.layer.contentsScale = image.scale;
|
||||
self.layer.contents = (__bridge id)image.CGImage;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule InteractionManager
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
|
@ -70,7 +71,7 @@ var InteractionManager = {
|
|||
/**
|
||||
* Schedule a function to run after all interactions have completed.
|
||||
*/
|
||||
runAfterInteractions(callback) {
|
||||
runAfterInteractions(callback: Function) {
|
||||
invariant(
|
||||
typeof callback === 'function',
|
||||
'Must specify a function to schedule.'
|
||||
|
@ -82,7 +83,7 @@ var InteractionManager = {
|
|||
/**
|
||||
* Notify manager that an interaction has started.
|
||||
*/
|
||||
createInteractionHandle() {
|
||||
createInteractionHandle(): number {
|
||||
scheduleUpdate();
|
||||
var handle = ++_inc;
|
||||
_addInteractionSet.add(handle);
|
||||
|
@ -92,7 +93,7 @@ var InteractionManager = {
|
|||
/**
|
||||
* Notify manager that an interaction has completed.
|
||||
*/
|
||||
clearInteractionHandle(handle) {
|
||||
clearInteractionHandle(handle: number) {
|
||||
invariant(
|
||||
!!handle,
|
||||
'Must provide a handle to clear.'
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* @providesModule InteractionMixin
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
|
@ -21,7 +22,7 @@ var InteractionMixin = {
|
|||
}
|
||||
},
|
||||
|
||||
_interactionMixinHandles: [],
|
||||
_interactionMixinHandles: ([]: Array<number>),
|
||||
|
||||
createInteractionHandle: function() {
|
||||
var handle = InteractionManager.createInteractionHandle();
|
||||
|
@ -29,7 +30,7 @@ var InteractionMixin = {
|
|||
return handle;
|
||||
},
|
||||
|
||||
clearInteractionHandle: function(clearHandle) {
|
||||
clearInteractionHandle: function(clearHandle: number) {
|
||||
InteractionManager.clearInteractionHandle(clearHandle);
|
||||
this._interactionMixinHandles = this._interactionMixinHandles.filter(
|
||||
handle => handle !== clearHandle
|
||||
|
@ -41,7 +42,7 @@ var InteractionMixin = {
|
|||
*
|
||||
* @param {function} callback
|
||||
*/
|
||||
runAfterInteractions: function(callback) {
|
||||
runAfterInteractions: function(callback: Function) {
|
||||
InteractionManager.runAfterInteractions(callback);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ExceptionsManager
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
|
@ -18,7 +19,13 @@ var parseErrorStack = require('parseErrorStack');
|
|||
|
||||
var sourceMapPromise;
|
||||
|
||||
function handleException(e) {
|
||||
type Exception = {
|
||||
sourceURL: string;
|
||||
line: number;
|
||||
message: string;
|
||||
}
|
||||
|
||||
function handleException(e: Exception) {
|
||||
var stack = parseErrorStack(e);
|
||||
console.error(
|
||||
'Error: ' +
|
||||
|
|
|
@ -21,74 +21,8 @@ var _initialURL = RCTLinkingManager &&
|
|||
|
||||
var DEVICE_NOTIF_EVENT = 'openURL';
|
||||
|
||||
/**
|
||||
* `LinkingIOS` gives you an interface to interact with both incoming and
|
||||
* outgoing app links.
|
||||
*
|
||||
* ### Basic Usage
|
||||
*
|
||||
* #### Handling deep links
|
||||
*
|
||||
* If your app was launched from an external URL registered with your app, you can
|
||||
* access and handle it from any component you want with the following:
|
||||
*
|
||||
* ```
|
||||
* componentDidMount() {
|
||||
* var url = LinkingIOS.popInitialURL();
|
||||
* if (url) { ... }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* If you also want to listen to incoming app links during your app's
|
||||
* execution you'll need to add the following lines to you `*AppDelegate.m`:
|
||||
*
|
||||
* ```
|
||||
* - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
|
||||
* return [RCTLinkingManager application:application openURL:url sourceApplication:sourceApplication annotation:annotation];
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* And in your React component, you'll then be able to listen to the events from
|
||||
* `LinkingIOS` as follows
|
||||
*
|
||||
* ```
|
||||
* componentDidMount() {
|
||||
* LinkingIOS.addEventListener('url', this._handleOpenURL);
|
||||
* },
|
||||
* componentWillUnmount() {
|
||||
* LinkingIOS.removeEventListener('url', this._handleOpenURL);
|
||||
* },
|
||||
* _handleOpenURL(event) {
|
||||
* console.log(event.url);
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* #### Triggering App links
|
||||
*
|
||||
* To trigger an app link (browser, email, or custom schemes) you can call:
|
||||
*
|
||||
* ```
|
||||
* LinkingIOS.openURL(url)
|
||||
* ```
|
||||
*
|
||||
* If you want to check if a URL can be opened by an installed app on the system you can call
|
||||
*
|
||||
* ```
|
||||
* LinkingIOS.canOpenURL(url, (supported) => {
|
||||
* if (!supported) {
|
||||
* AlertIOS.alert('Can\'t handle url: ' + url);
|
||||
* } else {
|
||||
* LinkingIOS.openURL(url);
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
class LinkingIOS {
|
||||
/**
|
||||
* Add a handler to LinkingIOS changes by listening to the `url` event type
|
||||
* and providing the handler
|
||||
*/
|
||||
static addEventListener(type: string, handler: Function) {
|
||||
static addEventListener(type, handler) {
|
||||
invariant(
|
||||
type === 'url',
|
||||
'LinkingIOS only supports `url` events'
|
||||
|
@ -101,10 +35,7 @@ class LinkingIOS {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a handler by passing the `url` event type and the handler
|
||||
*/
|
||||
static removeEventListener(type: string, handler: Function ) {
|
||||
static removeEventListener(type, handler) {
|
||||
invariant(
|
||||
type === 'url',
|
||||
'LinkingIOS only supports `url` events'
|
||||
|
@ -116,12 +47,7 @@ class LinkingIOS {
|
|||
_notifHandlers[handler] = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to open the given `url` with any of the installed apps.
|
||||
* If multiple applications can open `url`, the one that opens
|
||||
* is undefined.
|
||||
*/
|
||||
static openURL(url: string) {
|
||||
static openURL(url) {
|
||||
invariant(
|
||||
typeof url === 'string',
|
||||
'Invalid url: should be a string'
|
||||
|
@ -129,11 +55,7 @@ class LinkingIOS {
|
|||
RCTLinkingManager.openURL(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether an installed app can handle a given `url`.
|
||||
* The callback function will be called with `bool supported` as the only argument
|
||||
*/
|
||||
static canOpenURL(url: string, callback: Function) {
|
||||
static canOpenURL(url, callback) {
|
||||
invariant(
|
||||
typeof url === 'string',
|
||||
'Invalid url: should be a string'
|
||||
|
@ -145,11 +67,7 @@ class LinkingIOS {
|
|||
RCTLinkingManager.canOpenURL(url, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the app launch was triggered by an app link, it will pop the link URL,
|
||||
* otherwise it will return `null`
|
||||
*/
|
||||
static popInitialURL(): ?string {
|
||||
static popInitialURL() {
|
||||
var initialURL = _initialURL;
|
||||
_initialURL = null;
|
||||
return initialURL;
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTBridgeModule.h"
|
||||
#import "../../React/Base/RCTBridgeModule.h"
|
||||
|
||||
@interface RCTLinkingManager : NSObject <RCTBridgeModule>
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTBridgeModule.h"
|
||||
#import "../../React/Base/RCTBridgeModule.h"
|
||||
|
||||
@interface RCTPushNotificationManager : NSObject <RCTBridgeModule>
|
||||
|
||||
|
|
105
React.podspec
105
React.podspec
|
@ -1,93 +1,78 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = "React"
|
||||
s.version = "0.1.0"
|
||||
s.summary = "Build high quality mobile apps using React."
|
||||
s.description = <<-DESC
|
||||
React Native apps are built using the React JS
|
||||
framework, and render directly to native UIKit
|
||||
elements using a fully asynchronous architecture.
|
||||
There is no browser and no HTML. We have picked what
|
||||
we think is the best set of features from these and
|
||||
other technologies to build what we hope to become
|
||||
the best product development framework available,
|
||||
with an emphasis on iteration speed, developer
|
||||
delight, continuity of technology, and absolutely
|
||||
beautiful and fast products with no compromises in
|
||||
quality or capability.
|
||||
DESC
|
||||
s.homepage = "http://facebook.github.io/react-native/"
|
||||
s.license = "BSD"
|
||||
s.author = "Facebook"
|
||||
s.source = { :git => "https://github.com/facebook/react-native.git", :tag => "v#{s.version}" }
|
||||
s.default_subspec = 'Core'
|
||||
s.requires_arc = true
|
||||
s.platform = :ios, "7.0"
|
||||
s.prepare_command = 'npm install'
|
||||
s.preserve_paths = "cli.js", "Libraries/**/*.js", "lint", "linter.js", "node_modules", "package.json", "packager", "PATENTS", "react-native-cli"
|
||||
|
||||
s.subspec 'Core' do |ss|
|
||||
ss.libraries = 'icucore'
|
||||
ss.source_files = "React/**/*.{c,h,m}"
|
||||
ss.exclude_files = "**/__tests__/*", "IntegrationTests/*"
|
||||
ss.frameworks = "JavaScriptCore"
|
||||
end
|
||||
s.name = "React"
|
||||
s.version = "0.1.0"
|
||||
s.summary = "Build high quality mobile apps using React."
|
||||
s.description= <<-DESC
|
||||
React Native apps are built using the React JS framework,
|
||||
and render directly to native UIKit elements using a fully
|
||||
asynchronous architecture. There is no browser and no HTML.
|
||||
We have picked what we think is the best set of features from
|
||||
these and other technologies to build what we hope to become
|
||||
the best product development framework available, with an
|
||||
emphasis on iteration speed, developer delight, continuity
|
||||
of technology, and absolutely beautiful and fast products
|
||||
with no compromises in quality or capability.
|
||||
DESC
|
||||
s.homepage = "http://facebook.github.io/react-native/"
|
||||
s.license = "BSD"
|
||||
s.author = "Facebook"
|
||||
s.platform = :ios, "7.0"
|
||||
s.source = { :git => "https://github.com/facebook/react-native.git", :tag => "v#{s.version}" }
|
||||
s.source_files = "React/**/*.{c,h,m}"
|
||||
s.resources = "Resources/*.png"
|
||||
s.preserve_paths = "cli.js", "Libraries/**/*.js", "lint", "linter.js", "node_modules", "package.json", "packager", "PATENTS", "react-native-cli"
|
||||
s.exclude_files = "**/__tests__/*", "IntegrationTests/*"
|
||||
s.frameworks = "JavaScriptCore"
|
||||
s.requires_arc = true
|
||||
s.prepare_command = 'npm install'
|
||||
s.libraries = 'icucore'
|
||||
|
||||
s.subspec 'RCTActionSheet' do |ss|
|
||||
ss.dependency 'React/Core'
|
||||
ss.source_files = "Libraries/ActionSheetIOS/*.{h,m}"
|
||||
ss.source_files = "Libraries/ActionSheetIOS/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/ActionSheetIOS/*.js"
|
||||
end
|
||||
|
||||
s.subspec 'RCTAdSupport' do |ss|
|
||||
ss.dependency 'React/Core'
|
||||
ss.source_files = "Libraries/AdSupport/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/AdSupport/*.js"
|
||||
ss.source_files = "Libraries/RCTAdSupport/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/RCTAdSupport/*.js"
|
||||
end
|
||||
|
||||
s.subspec 'RCTAnimation' do |ss|
|
||||
ss.dependency 'React/Core'
|
||||
ss.source_files = "Libraries/Animation/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/Animation/*.js"
|
||||
ss.source_files = "Libraries/Animation/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/Animation/*.js"
|
||||
end
|
||||
|
||||
s.subspec 'RCTGeolocation' do |ss|
|
||||
ss.dependency 'React/Core'
|
||||
ss.source_files = "Libraries/Geolocation/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/Geolocation/*.js"
|
||||
ss.source_files = "Libraries/Geolocation/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/Geolocation/*.js"
|
||||
end
|
||||
|
||||
s.subspec 'RCTImage' do |ss|
|
||||
ss.dependency 'React/Core'
|
||||
ss.source_files = "Libraries/Image/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/Image/*.js"
|
||||
ss.source_files = "Libraries/Image/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/Image/*.js"
|
||||
end
|
||||
|
||||
s.subspec 'RCTNetwork' do |ss|
|
||||
ss.dependency 'React/Core'
|
||||
ss.source_files = "Libraries/Network/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/Network/*.js"
|
||||
ss.source_files = "Libraries/Network/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/Network/*.js"
|
||||
end
|
||||
|
||||
s.subspec 'RCTPushNotification' do |ss|
|
||||
ss.dependency 'React/Core'
|
||||
ss.source_files = "Libraries/PushNotificationIOS/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/PushNotificationIOS/*.js"
|
||||
ss.source_files = "Libraries/PushNotificationIOS/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/PushNotificationIOS/*.js"
|
||||
end
|
||||
|
||||
s.subspec 'RCTWebSocketDebugger' do |ss|
|
||||
ss.dependency 'React/Core'
|
||||
ss.source_files = "Libraries/RCTWebSocketDebugger/*.{h,m}"
|
||||
ss.source_files = "Libraries/RCTWebSocketDebugger/*.{h,m}"
|
||||
end
|
||||
|
||||
s.subspec 'RCTText' do |ss|
|
||||
ss.dependency 'React/Core'
|
||||
ss.source_files = "Libraries/Text/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/Text/*.js"
|
||||
ss.source_files = "Libraries/Text/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/Text/*.js"
|
||||
end
|
||||
|
||||
s.subspec 'RCTVibration' do |ss|
|
||||
ss.dependency 'React/Core'
|
||||
ss.source_files = "Libraries/Vibration/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/Vibration/*.js"
|
||||
ss.source_files = "Libraries/Vibration/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/Vibration/*.js"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,10 +10,11 @@
|
|||
#import <QuartzCore/QuartzCore.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "Layout.h"
|
||||
#import "RCTAnimationType.h"
|
||||
#import "../Layout/Layout.h"
|
||||
#import "../Views/RCTAnimationType.h"
|
||||
#import "../Views/RCTPointerEvents.h"
|
||||
|
||||
#import "RCTLog.h"
|
||||
#import "RCTPointerEvents.h"
|
||||
|
||||
/**
|
||||
* This class provides a collection of conversion functions for mapping
|
||||
|
|
|
@ -453,22 +453,21 @@ RCT_CGSTRUCT_CONVERTER(CGAffineTransform, (@[
|
|||
return [self UIImage:json].CGImage;
|
||||
}
|
||||
|
||||
#ifndef __IPHONE_8_2
|
||||
#if !defined(__IPHONE_8_2) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_2
|
||||
|
||||
// These constants are defined in iPhone SDK 8.2
|
||||
// They'll work fine in earlier iOS versions, but the app cannot be built with
|
||||
// an SDK version < 8.2 unless we redefine them here. This will be removed
|
||||
// in a future version of React, once 8.2 is more widely adopted.
|
||||
// These constants are defined in iPhone SDK 8.2, but the app cannot run on
|
||||
// iOS < 8.2 unless we redefine them here. If you target iOS 8.2 or above
|
||||
// as a base target, the standard constants will be used instead.
|
||||
|
||||
static const CGFloat UIFontWeightUltraLight = -0.8;
|
||||
static const CGFloat UIFontWeightThin = -0.6;
|
||||
static const CGFloat UIFontWeightLight = -0.4;
|
||||
static const CGFloat UIFontWeightRegular = 0;
|
||||
static const CGFloat UIFontWeightMedium = 0.23;
|
||||
static const CGFloat UIFontWeightSemibold = 0.3;
|
||||
static const CGFloat UIFontWeightBold = 0.4;
|
||||
static const CGFloat UIFontWeightHeavy = 0.56;
|
||||
static const CGFloat UIFontWeightBlack = 0.62;
|
||||
#define UIFontWeightUltraLight -0.8
|
||||
#define UIFontWeightThin -0.6
|
||||
#define UIFontWeightLight -0.4
|
||||
#define UIFontWeightRegular 0
|
||||
#define UIFontWeightMedium 0.23
|
||||
#define UIFontWeightSemibold 0.3
|
||||
#define UIFontWeightBold 0.4
|
||||
#define UIFontWeightHeavy 0.56
|
||||
#define UIFontWeightBlack 0.62
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTBridgeModule.h"
|
||||
#import "RCTInvalidating.h"
|
||||
#import "RCTViewManager.h"
|
||||
#import "../Base/RCTBridge.h"
|
||||
#import "../Base/RCTBridgeModule.h"
|
||||
#import "../Base/RCTInvalidating.h"
|
||||
#import "../Views/RCTViewManager.h"
|
||||
|
||||
@protocol RCTScrollableProtocol;
|
||||
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "Layout.h"
|
||||
#import "../Layout/Layout.h"
|
||||
|
||||
#import "RCTViewNodeProtocol.h"
|
||||
|
||||
@class RCTSparseArray;
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTBridgeModule.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTLog.h"
|
||||
#import "../Base/RCTBridgeModule.h"
|
||||
#import "../Base/RCTConvert.h"
|
||||
#import "../Base/RCTLog.h"
|
||||
|
||||
@class RCTBridge;
|
||||
@class RCTEventDispatcher;
|
||||
|
|
|
@ -69,11 +69,7 @@ if (options.assetRoots) {
|
|||
options.assetRoots = options.assetRoots.split(',');
|
||||
}
|
||||
} else {
|
||||
if (__dirname.match(/node_modules\/react-native\/packager$/)) {
|
||||
options.assetRoots = [path.resolve(__dirname, '../../..')];
|
||||
} else {
|
||||
options.assetRoots = [path.resolve(__dirname, '..')];
|
||||
}
|
||||
options.assetRoots = [path.resolve(__dirname, '..')];
|
||||
}
|
||||
|
||||
console.log('\n' +
|
||||
|
|
|
@ -10,4 +10,4 @@
|
|||
ulimit -n 4096
|
||||
|
||||
THIS_DIR=$(dirname "$0")
|
||||
node $THIS_DIR/packager.js "$@"
|
||||
node "$THIS_DIR/packager.js" "$@"
|
||||
|
|
|
@ -821,6 +821,55 @@ describe('DependencyGraph', function() {
|
|||
});
|
||||
});
|
||||
|
||||
pit('updates module dependencies on asset add', function() {
|
||||
var root = '/root';
|
||||
var filesystem = fs.__setMockFilesystem({
|
||||
'root': {
|
||||
'index.js': [
|
||||
'/**',
|
||||
' * @providesModule index',
|
||||
' */',
|
||||
'require("image!foo")'
|
||||
].join('\n'),
|
||||
},
|
||||
});
|
||||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
assetRoots: [root],
|
||||
assetExts: ['png'],
|
||||
fileWatcher: fileWatcher
|
||||
});
|
||||
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
.toEqual([
|
||||
{ id: 'index', altId: '/root/index.js',
|
||||
path: '/root/index.js',
|
||||
dependencies: ['image!foo']
|
||||
}
|
||||
]);
|
||||
|
||||
filesystem.root['foo.png'] = '';
|
||||
triggerFileChange('add', 'foo.png', root);
|
||||
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
.toEqual([
|
||||
{ id: 'index', altId: '/root/index.js',
|
||||
path: '/root/index.js',
|
||||
dependencies: ['image!foo']
|
||||
},
|
||||
{ id: 'image!foo',
|
||||
path: '/root/foo.png',
|
||||
dependencies: [],
|
||||
isAsset: true,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
pit('runs changes through ignore filter', function() {
|
||||
var root = '/root';
|
||||
var filesystem = fs.__setMockFilesystem({
|
||||
|
|
|
@ -86,7 +86,11 @@ DependecyGraph.prototype.load = function() {
|
|||
DependecyGraph.prototype.getOrderedDependencies = function(entryPath) {
|
||||
var absolutePath = this._getAbsolutePath(entryPath);
|
||||
if (absolutePath == null) {
|
||||
throw new Error('Cannot find entry file in any of the roots: ' + entryPath);
|
||||
throw new NotFoundError(
|
||||
'Cannot find entry file %s in any of the roots: %j',
|
||||
entryPath,
|
||||
this._roots
|
||||
);
|
||||
}
|
||||
|
||||
var module = this._graph[absolutePath];
|
||||
|
@ -478,7 +482,12 @@ DependecyGraph.prototype._lookupPackage = function(modulePath) {
|
|||
/**
|
||||
* Process a filewatcher change event.
|
||||
*/
|
||||
DependecyGraph.prototype._processFileChange = function(eventType, filePath, root, stat) {
|
||||
DependecyGraph.prototype._processFileChange = function(
|
||||
eventType,
|
||||
filePath,
|
||||
root,
|
||||
stat
|
||||
) {
|
||||
var absPath = path.join(root, filePath);
|
||||
if (this._ignoreFilePath(absPath)) {
|
||||
return;
|
||||
|
@ -486,6 +495,11 @@ DependecyGraph.prototype._processFileChange = function(eventType, filePath, root
|
|||
|
||||
this._debugUpdateEvents.push({event: eventType, path: filePath});
|
||||
|
||||
if (this._assetExts.indexOf(extname(filePath)) > -1) {
|
||||
this._processAssetChange(eventType, absPath);
|
||||
return;
|
||||
}
|
||||
|
||||
var isPackage = path.basename(filePath) === 'package.json';
|
||||
if (eventType === 'delete') {
|
||||
if (isPackage) {
|
||||
|
@ -520,7 +534,8 @@ DependecyGraph.prototype.getDebugInfo = function() {
|
|||
};
|
||||
|
||||
/**
|
||||
* Searches all roots for the file and returns the first one that has file of the same path.
|
||||
* Searches all roots for the file and returns the first one that has file of
|
||||
* the same path.
|
||||
*/
|
||||
DependecyGraph.prototype._getAbsolutePath = function(filePath) {
|
||||
if (isAbsolutePath(filePath)) {
|
||||
|
@ -543,12 +558,43 @@ DependecyGraph.prototype._buildAssetMap = function() {
|
|||
return q();
|
||||
}
|
||||
|
||||
var self = this;
|
||||
return buildAssetMap(this._assetRoots, this._assetExts)
|
||||
.then(function(map) {
|
||||
self._assetMap = map;
|
||||
return map;
|
||||
this._assetMap = Object.create(null);
|
||||
return buildAssetMap(
|
||||
this._assetRoots,
|
||||
this._processAsset.bind(this)
|
||||
);
|
||||
};
|
||||
|
||||
DependecyGraph.prototype._processAsset = function(file) {
|
||||
var ext = extname(file);
|
||||
if (this._assetExts.indexOf(ext) !== -1) {
|
||||
var name = assetName(file, ext);
|
||||
if (this._assetMap[name] != null) {
|
||||
debug('Conflcting assets', name);
|
||||
}
|
||||
|
||||
this._assetMap[name] = new ModuleDescriptor({
|
||||
id: 'image!' + name,
|
||||
path: path.resolve(file),
|
||||
isAsset: true,
|
||||
dependencies: [],
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
DependecyGraph.prototype._processAssetChange = function(eventType, file) {
|
||||
if (this._assetMap == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var name = assetName(file, extname(file));
|
||||
if (eventType === 'change' || eventType === 'delete') {
|
||||
delete this._assetMap[name];
|
||||
}
|
||||
|
||||
if (eventType === 'change' || eventType === 'add') {
|
||||
this._processAsset(file);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -623,15 +669,14 @@ function readAndStatDir(dir) {
|
|||
* Given a list of roots and list of extensions find all the files in
|
||||
* the directory with that extension and build a map of those assets.
|
||||
*/
|
||||
function buildAssetMap(roots, exts) {
|
||||
function buildAssetMap(roots, processAsset) {
|
||||
var queue = roots.slice(0);
|
||||
var map = Object.create(null);
|
||||
|
||||
function search() {
|
||||
var root = queue.shift();
|
||||
|
||||
if (root == null) {
|
||||
return q(map);
|
||||
return q();
|
||||
}
|
||||
|
||||
return readAndStatDir(root).spread(function(files, stats) {
|
||||
|
@ -639,21 +684,7 @@ function buildAssetMap(roots, exts) {
|
|||
if (stats[i].isDirectory()) {
|
||||
queue.push(file);
|
||||
} else {
|
||||
var ext = path.extname(file).replace(/^\./, '');
|
||||
if (exts.indexOf(ext) !== -1) {
|
||||
var assetName = path.basename(file, '.' + ext)
|
||||
.replace(/@[\d\.]+x/, '');
|
||||
if (map[assetName] != null) {
|
||||
debug('Conflcting assets', assetName);
|
||||
}
|
||||
|
||||
map[assetName] = new ModuleDescriptor({
|
||||
id: 'image!' + assetName,
|
||||
path: path.resolve(file),
|
||||
isAsset: true,
|
||||
dependencies: [],
|
||||
});
|
||||
}
|
||||
processAsset(file);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -664,4 +695,24 @@ function buildAssetMap(roots, exts) {
|
|||
return search();
|
||||
}
|
||||
|
||||
function assetName(file, ext) {
|
||||
return path.basename(file, '.' + ext).replace(/@[\d\.]+x/, '');
|
||||
}
|
||||
|
||||
function extname(name) {
|
||||
return path.extname(name).replace(/^\./, '');
|
||||
}
|
||||
|
||||
|
||||
function NotFoundError() {
|
||||
Error.call(this);
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
var msg = util.format.apply(util, arguments);
|
||||
this.message = msg;
|
||||
this.type = this.name = 'NotFoundError';
|
||||
this.status = 404;
|
||||
}
|
||||
|
||||
NotFoundError.__proto__ = Error.prototype;
|
||||
|
||||
module.exports = DependecyGraph;
|
||||
|
|
|
@ -51,15 +51,15 @@ var validateOpts = declareOpts({
|
|||
type: 'array',
|
||||
default: [],
|
||||
},
|
||||
fileWatcher: {
|
||||
type: 'object',
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
function HasteDependencyResolver(options) {
|
||||
var opts = validateOpts(options);
|
||||
|
||||
this._fileWatcher = opts.nonPersistent
|
||||
? FileWatcher.createDummyWatcher()
|
||||
: new FileWatcher(opts.projectRoots);
|
||||
|
||||
this._depGraph = new DependencyGraph({
|
||||
roots: opts.projectRoots,
|
||||
assetRoots: opts.assetRoots,
|
||||
|
@ -67,7 +67,7 @@ function HasteDependencyResolver(options) {
|
|||
return filepath.indexOf('__tests__') !== -1 ||
|
||||
(opts.blacklistRE && opts.blacklistRE.test(filepath));
|
||||
},
|
||||
fileWatcher: this._fileWatcher,
|
||||
fileWatcher: opts.fileWatcher,
|
||||
});
|
||||
|
||||
|
||||
|
@ -164,10 +164,6 @@ HasteDependencyResolver.prototype.wrapModule = function(module, code) {
|
|||
});
|
||||
};
|
||||
|
||||
HasteDependencyResolver.prototype.end = function() {
|
||||
return this._fileWatcher.end();
|
||||
};
|
||||
|
||||
HasteDependencyResolver.prototype.getDebugInfo = function() {
|
||||
return this._depGraph.getDebugInfo();
|
||||
};
|
||||
|
|
|
@ -21,6 +21,7 @@ describe('FileWatcher', function() {
|
|||
var Watcher;
|
||||
|
||||
beforeEach(function() {
|
||||
require('mock-modules').dumpCache();
|
||||
FileWatcher = require('../');
|
||||
Watcher = require('sane').WatchmanWatcher;
|
||||
Watcher.prototype.once.mockImplementation(function(type, callback) {
|
||||
|
|
|
@ -16,7 +16,7 @@ var exec = require('child_process').exec;
|
|||
|
||||
var Promise = q.Promise;
|
||||
|
||||
var detectingWatcherClass = new Promise(function(resolve, reject) {
|
||||
var detectingWatcherClass = new Promise(function(resolve) {
|
||||
exec('which watchman', function(err, out) {
|
||||
if (err || out.length === 0) {
|
||||
resolve(sane.NodeWatcher);
|
||||
|
@ -30,14 +30,23 @@ module.exports = FileWatcher;
|
|||
|
||||
var MAX_WAIT_TIME = 3000;
|
||||
|
||||
function FileWatcher(projectRoots) {
|
||||
var self = this;
|
||||
// Singleton
|
||||
var fileWatcher = null;
|
||||
|
||||
function FileWatcher(rootConfigs) {
|
||||
if (fileWatcher) {
|
||||
// This allows us to optimize watching in the future by merging roots etc.
|
||||
throw new Error('FileWatcher can only be instantiated once');
|
||||
}
|
||||
|
||||
fileWatcher = this;
|
||||
|
||||
this._loading = q.all(
|
||||
projectRoots.map(createWatcher)
|
||||
rootConfigs.map(createWatcher)
|
||||
).then(function(watchers) {
|
||||
watchers.forEach(function(watcher) {
|
||||
watcher.on('all', function(type, filepath, root) {
|
||||
self.emit('all', type, filepath, root);
|
||||
fileWatcher.emit('all', type, filepath, root);
|
||||
});
|
||||
});
|
||||
return watchers;
|
||||
|
@ -50,21 +59,14 @@ util.inherits(FileWatcher, EventEmitter);
|
|||
FileWatcher.prototype.end = function() {
|
||||
return this._loading.then(function(watchers) {
|
||||
watchers.forEach(function(watcher) {
|
||||
delete watchersByRoot[watcher._root];
|
||||
return q.ninvoke(watcher, 'close');
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var watchersByRoot = Object.create(null);
|
||||
|
||||
function createWatcher(root) {
|
||||
if (watchersByRoot[root] != null) {
|
||||
return Promise.resolve(watchersByRoot[root]);
|
||||
}
|
||||
|
||||
function createWatcher(rootConfig) {
|
||||
return detectingWatcherClass.then(function(Watcher) {
|
||||
var watcher = new Watcher(root, {glob: ['**/*.js', '**/package.json']});
|
||||
var watcher = new Watcher(rootConfig.dir, rootConfig.globs);
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
var rejectTimeout = setTimeout(function() {
|
||||
|
@ -77,8 +79,6 @@ function createWatcher(root) {
|
|||
|
||||
watcher.once('ready', function() {
|
||||
clearTimeout(rejectTimeout);
|
||||
watchersByRoot[root] = watcher;
|
||||
watcher._root = root;
|
||||
resolve(watcher);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -56,6 +56,14 @@ var validateOpts = declareOpts({
|
|||
type: 'array',
|
||||
required: false,
|
||||
},
|
||||
assetExts: {
|
||||
type: 'array',
|
||||
default: ['png'],
|
||||
},
|
||||
fileWatcher: {
|
||||
type: 'object',
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
function Packager(options) {
|
||||
|
@ -70,6 +78,7 @@ function Packager(options) {
|
|||
nonPersistent: opts.nonPersistent,
|
||||
moduleFormat: opts.moduleFormat,
|
||||
assetRoots: opts.assetRoots,
|
||||
fileWatcher: opts.fileWatcher,
|
||||
});
|
||||
|
||||
this._transformer = new Transformer({
|
||||
|
@ -83,10 +92,7 @@ function Packager(options) {
|
|||
}
|
||||
|
||||
Packager.prototype.kill = function() {
|
||||
return q.all([
|
||||
this._transformer.kill(),
|
||||
this._resolver.end(),
|
||||
]);
|
||||
return this._transformer.kill();
|
||||
};
|
||||
|
||||
Packager.prototype.package = function(main, runModule, sourceMapUrl, isDev) {
|
||||
|
|
|
@ -55,18 +55,49 @@ var validateOpts = declareOpts({
|
|||
type: 'array',
|
||||
required: false,
|
||||
},
|
||||
assetExts: {
|
||||
type: 'array',
|
||||
default: ['png'],
|
||||
},
|
||||
});
|
||||
|
||||
function Server(options) {
|
||||
var opts = validateOpts(options);
|
||||
|
||||
this._projectRoots = opts.projectRoots;
|
||||
this._packages = Object.create(null);
|
||||
this._packager = new Packager(opts);
|
||||
this._changeWatchers = [];
|
||||
|
||||
var watchRootConfigs = opts.projectRoots.map(function(dir) {
|
||||
return {
|
||||
dir: dir,
|
||||
globs: [
|
||||
'**/*.js',
|
||||
'**/package.json',
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
if (opts.assetRoots != null) {
|
||||
watchRootConfigs = watchRootConfigs.concat(
|
||||
opts.assetRoots.map(function(dir) {
|
||||
return {
|
||||
dir: dir,
|
||||
globs: opts.assetExts.map(function(ext) {
|
||||
return '**/*.' + ext;
|
||||
}),
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
this._fileWatcher = options.nonPersistent
|
||||
? FileWatcher.createDummyWatcher()
|
||||
: new FileWatcher(options.projectRoots);
|
||||
: new FileWatcher(watchRootConfigs);
|
||||
|
||||
var packagerOpts = Object.create(opts);
|
||||
packagerOpts.fileWatcher = this._fileWatcher;
|
||||
this._packager = new Packager(packagerOpts);
|
||||
|
||||
var onFileChange = this._onFileChange.bind(this);
|
||||
this._fileWatcher.on('all', onFileChange);
|
||||
|
@ -246,6 +277,9 @@ Server.prototype.processRequest = function(req, res, next) {
|
|||
function getOptionsFromUrl(reqUrl) {
|
||||
// `true` to parse the query param as an object.
|
||||
var urlObj = url.parse(reqUrl, true);
|
||||
// node v0.11.14 bug see https://github.com/facebook/react-native/issues/218
|
||||
urlObj.query = urlObj.query || {};
|
||||
|
||||
var pathname = urlObj.pathname;
|
||||
|
||||
// Backwards compatibility. Options used to be as added as '.' to the
|
||||
|
@ -281,11 +315,11 @@ function getBoolOptionFromQuery(query, opt, defaultVal) {
|
|||
}
|
||||
|
||||
function handleError(res, error) {
|
||||
res.writeHead(500, {
|
||||
res.writeHead(error.status || 500, {
|
||||
'Content-Type': 'application/json; charset=UTF-8',
|
||||
});
|
||||
|
||||
if (error.type === 'TransformError') {
|
||||
if (error.type === 'TransformError' || error.type === 'NotFoundError') {
|
||||
res.end(JSON.stringify(error));
|
||||
} else {
|
||||
console.error(error.stack || error);
|
||||
|
|
Loading…
Reference in New Issue