Initial public release of React Navigation

This commit is contained in:
Adam Miskiewicz
2017-01-26 11:49:39 -08:00
committed by James Ide
commit c37ad8a0a9
391 changed files with 51942 additions and 0 deletions

32
.babelrc Normal file
View File

@@ -0,0 +1,32 @@
{
"env": {
// For RN example development
"development": {
"presets": ["react-native"],
"plugins": [
"transform-flow-strip-types",
],
},
// For Jest
"test": {
"presets": ["react-native"],
"plugins": [
"transform-flow-strip-types",
],
},
// For publishing to NPM for RN
"publish-rn": {
"presets": ["react-native-syntax"],
"plugins": [
"transform-flow-strip-types",
],
},
// For publishing to NPM for RN
"publish-web": {
"presets": ["es2015", "stage-1", "react"],
"plugins": [
"transform-flow-strip-types",
],
},
},
}

77
.eslintrc Normal file
View File

@@ -0,0 +1,77 @@
{
"extends": "airbnb",
"parser": "babel-eslint",
"plugins": [
"flowtype"
],
"env": {
"jasmine": true
},
"rules": {
"no-underscore-dangle": 0,
"no-use-before-define": 0,
"no-unused-expressions": 0,
"new-cap": 0,
"no-plusplus": 0,
"no-class-assign": 0,
"no-duplicate-imports": 0,
"import/extensions": 0,
"import/no-extraneous-dependencies": 0,
"import/no-unresolved": 0,
"react/jsx-filename-extension": [
0, { "extensions": [".js", ".jsx"] }
],
"react/forbid-prop-types": 0,
"react/sort-comp": 0,
"react/prefer-stateless-function": 0,
"flowtype/boolean-style": [
2,
"boolean"
],
"flowtype/define-flow-type": 1,
"flowtype/generic-spacing": [
2,
"never"
],
"flowtype/no-weak-types": 1,
"flowtype/require-parameter-type": 2,
"flowtype/require-return-type": [
0,
"always",
{
"annotateUndefined": "never"
}
],
"flowtype/require-valid-file-annotation": 2,
"flowtype/semi": [
2,
"always"
],
"flowtype/space-after-type-colon": [
2,
"always"
],
"flowtype/space-before-generic-bracket": [
2,
"never"
],
"flowtype/space-before-type-colon": [
2,
"never"
],
"flowtype/union-intersection-spacing": [
2,
"always"
],
"flowtype/use-flow-type": 1,
"flowtype/valid-syntax": 1
},
"settings": {
"flowtype": {
"onlyFilesWithFlowAnnotation": false
}
}
}

55
.flowconfig Normal file
View File

@@ -0,0 +1,55 @@
[ignore]
; We fork some components by platform
.*/*[.]android.js
; Ignore templates for 'react-native init'
.*/local-cli/templates/.*
; Ignore the website subdir
.*/node_modules/react-native/website/.*
; Ignore "BUCK" generated dirs
.*/node_modules/react-native/\.buckd/
; Ignore unexpected extra "@providesModule"
.*/node_modules/.*/node_modules/fbjs/.*
; Ignore duplicate module providers
; For RN Apps installed via npm, "Libraries" folder is inside
; "node_modules/react-native" but in the source repo it is in the root
.*/node_modules/react-native/Libraries/react-native/React.js
.*/node_modules/react-native/Libraries/react-native/ReactNative.js
<PROJECT_ROOT>/lib
<PROJECT_ROOT>/lib-rn
<PROJECT_ROOT>/examples
<PROJECT_ROOT>/website
[include]
[libs]
node_modules/react-native/Libraries/react-native/react-native-interface.js
node_modules/react-native/flow/
flow/
[options]
module.system=haste
experimental.strict_type_args=true
munge_underscores=true
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FixMe
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-7]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(3[0-7]\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
unsafe.enable_getters_and_setters=true
[version]
^0.35.0

16
.gitignore vendored Normal file
View File

@@ -0,0 +1,16 @@
# VSCode
.vscode/
jsconfig.json
# NodeJS
npm-debug.log
node_modules
lib-rn
lib
yarn-error.log
# OS X
.DS_Store
# Exponent
.exponent

1
.npmignore Normal file
View File

@@ -0,0 +1 @@
examples

1
.watchmanconfig Normal file
View File

@@ -0,0 +1 @@
{}

26
LICENSE Normal file
View File

@@ -0,0 +1,26 @@
BSD License
For React Navigation software
Copyright (c) 2016-present, React Navigation Contributors. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

17
PULL_REQUEST_TEMPLATE.md Normal file
View File

@@ -0,0 +1,17 @@
Please provide enough information so that others can review your pull request:
Explain the **motivation** for making this change. What existing problem does the pull request solve?
Prefer **small pull requests**. These are much easier to review and more likely to get merged. Make sure the PR does only one thing, otherwise split it.
**Test plan (required)**
Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes UI.
Make sure you test on both platforms if your change affects both platforms.
The code must pass tests and shouldn't add more Flow errors.
**Code formatting**
Look around. Match the style of the rest of the codebase.

42
README.md Normal file
View File

@@ -0,0 +1,42 @@
# React Navigation [![Circle CI](https://circleci.com/gh/reactjs/react-navigation.svg?style=svg&circle-token=296a074544f10b6580652bd283b2c1817154dc20)](https://circleci.com/gh/reactjs/react-navigation)
*Learn once, navigate anywhere.*
Browse the docs on [reactnavigation.org](https://navigate:navigate@reactnavigation.org/). (The username/pass is `navigate:navigate`)
## [Getting started](https://reactnavigation.org/docs/intro/)
1. Create a new React Native App
```
react-native init SimpleApp
cd SimpleApp
```
2. Install the latest version of react-navigation from npm
```
yarn add react-navigation
```
or
```
npm install --save react-navigation
```
3. Run the new app
```
react-native run-android # or:
react-native run-ios
```
## Advanced guide
- [Redux integration](https://reactnavigation.org/docs/guides/redux)
- [Web integration](https://reactnavigation.org/docs/guides/web)
- [Deep linking](https://reactnavigation.org/docs/guides/linking)
- [Contributors guide](https://reactnavigation.org/docs/guides/contributors)
## React Navigation API
- [Navigators](https://reactnavigation.org/docs/navigators/)
- [Routers](https://reactnavigation.org/docs/routers/)
- [Views](https://reactnavigation.org/docs/views/)

23
circle.yml Normal file
View File

@@ -0,0 +1,23 @@
machine:
node:
version: 6.1.0
pre:
- mkdir ~/.yarn-cache
dependencies:
pre:
# Install Yarn
- sudo apt-key adv --fetch-keys http://dl.yarnpkg.com/debian/pubkey.gpg
- echo "deb http://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
- sudo apt-get update -qq
- sudo apt-get install -y -qq yarn
cache_directories:
- "~/.yarn-cache"
override:
- yarn install
- cd website && yarn install
deployment:
website-prod:
branch: master
commands:
- yarn run build-docs
- ./scripts/deploy-website.sh

View File

@@ -0,0 +1,120 @@
# DrawerNavigator
Used to easily set up a screen with a drawer navigation.
```js
class MyHomeScreen extends React.Component {
static navigationOptions = {
drawer: () => ({
label: 'Home',
icon: ({ tintColor }) => (
<Image
source={require('./chats-icon.png')}
style={[styles.icon, {tintColor: tintColor}]}
/>
),
}),
}
render() {
return (
<Button
onPress={() => this.props.navigation.navigate('Notifications')}
label="Go to notifications"
/>
);
}
}
class MyNotificationsScreen extends React.Component {
static navigationOptions = {
drawer: () => ({
label: 'Notifications',
icon: ({ tintColor }) => (
<Image
source={require('./notif-icon.png')}
style={[styles.tabIcon, {tintColor: tintColor}]}
/>
),
}),
}
render() {
return (
<Button
onPress={() => this.props.navigation.goBack()}
label="Go back home"
/>
);
}
}
const styles = StyleSheet.create({
icon: {
width: 24,
height: 24,
},
});
const MyApp = DrawerNavigator({
Home: {
screen: MyHomeScreen,
},
Notifications: {
screen: MyNotificationsScreen,
},
});
```
To open and close drawer, navigate to `'DrawerOpen'` and `'DrawerClose'` respectively.
```js
this.props.navigation.navigate('DrawerOpen'); // open drawer
this.props.navigation.navigate('DrawerClose'); // close drawer
```
## API Definition
```js
DrawerNavigator(RouteConfigs, DrawerNavigatorConfig)
```
### Drawer Navigator Options
- `drawerWidth` - Width of the drawer
- `contentComponent` - Component to use to render the navigation items. Receives the `navigation` prop for the drawer. Defaults to `DrawerView.Items`.
- `contentOptions` - Configure the drawer content, see below.
Several options get passed to the underlying router to modify navigation logic:
- `initialRouteName` - The routeName for the initial route.
- `order` - Array of routeNames which defines the order of the drawer items.
- `paths` - Provide a mapping of routeName to path config, which overrides the paths set in the routeConfigs.
- `backBehavior` - Should the back button cause switch to the initial route? If yes, set to `initialRoute`, otherwise `none`. Defaults to `initialRoute` behavior.
### `contentOptions` for `DrawerView.Items`
- `activeTintColor` - label and icon color of the active label
- `activeBackgroundColor` - background color of the active label
- `inactiveTintColor` - label and icon color of the inactive label
- `inactiveBackgroundColor` - background color of the inactive label
- `style` - style object for the content section
Example:
```js
contentOptions: {
activeTintColor: '#e91e63',
style: {
marginVertical: 0,
}
}
```
### Navigator Props
The navigator component created by `DrawerNavigator(...)` takes the following props,
- `screenProps` - Props to pass to each child screen

View File

@@ -0,0 +1,21 @@
# Navigators
Navigators allow you define you application's navigation structure. Navigators also render common elements such as headers and tab bars which you can configure.
Under the hood, navigators are plain React components.
## Built-in Navigators
`react-navigation` includes the following functions to help you create navigators:
- [StackNavigator](/docs/navigators/stack) - Renders one screen at a time and provides transitions between screens. When a new screen is opened it is placed on top of the stack.
- [TabNavigator](/docs/navigators/tab) - Renders a tab bar that lets the user switch between several screens
- [DrawerNavigator](/docs/navigators/drawer) - Provides a drawer that slides in from the left of the screen
## Rendering screens with Navigators
The navigators render application screens which are just React components.
To learn how to create screens, read about:
- [Screen `navigation` prop](/docs/navigators/navigation-prop) to allow the screen to dispatch navigation actions, such as opening another screen
- [Screen `navigationOptions`](/docs/navigators/navigation-options) to customize how the screen gets presented by the navigator (e.g. header title, tab label)

View File

@@ -0,0 +1,130 @@
# StackNavigator
Provides a way for your app to transition between screens where each new screen is placed on top of a stack.
By default the StackNavigator is configured to have the familiar iOS and Android look & feel: new screens slide in from the right on iOS, fade in from the bottom on Android. On iOS the StackNavigator can also be configured to a modal style where screens slide in from the bottom.
```jsx
class MyHomeScreen extends React.Component {
static navigationOptions = {
title: 'Home',
}
render() {
return (
<Button
onPress={() => this.props.navigation.navigate('Profile', {name: 'Lucy'})}
label="Go to Lucy's profile"
/>
);
}
}
const ModalStack = StackNavigator({
Home: {
screen: MyHomeScreen,
},
Profile: {
path: 'people/:name',
screen: MyProfileScreen,
},
});
```
## API Definition
```js
StackNavigator(RouteConfigs, StackNavigatorConfig)
```
### RouteConfigs
The route configs object is a mapping from route name to a route config, which tells the navigator what to present for that route.
```js
StackNavigator({
// For each screen that you can navigate to, create a new entry like this:
Profile: {
// `ProfileScreen` is a React component that will be the main content of the screen.
screen: ProfileScreen,
// When `ProfileScreen` is loaded by the StackNavigator, it will be given a navigation prop.
// Optional: When deep linking or using react-navigation in a web app, this path is used:
path: 'people/:username',
// The action and route params are extracted from the path.
// Optional: Override the navigation options for the screen
navigationOptions: {
title: ({state}) => `${state.params.username}'s Profile'`,
},
},
...MyOtherRoutes,
});
```
### StackNavigatorConfig
Options for the router:
- `initialRouteName` - Sets the default screen of the stack. Must match one of the keys in route configs.
- `initialRouteParams` - The params for the initial route
- `paths` - A mapping of overrides for the paths set in the route configs
Visual options:
- `mode` - Defines the style for rendering and transitions:
- `card` - Use the standard iOS and Android screen transitions. This is the default.
- `modal` - Make the screens slide in from the bottom which is a common iOS pattern. Only works on iOS, has no effect on Android.
- `headerMode` - Specifies how the header should be rendered:
- `float` - Render a single header that stays at the top and animates as screens are changed. This is a common pattern on iOS.
- `screen` - Each screen has a header attached to it and the header fades in and out together with the screen. This is a common pattern on Android.
- `none` - No header will be rendered.
### Screen Navigation Options
Usually you define static `navigationOptions` on your screen component. For example:
```jsx
class ProfileScreen extends React.Component {
static navigationOptions = {
title: `${state.params.name}'s Profile!`,
header: ({ state, setParams }) => ({
// Render a button on the right side of the header
// When pressed switches the screen to edit mode.
right: (
<Button
title={state.params.editing ? 'Done' : 'Edit'}
onPress={() => setParams({editing: state.params.editing ? false : true})}
/>
),
}),
};
...
```
All `navigationOptions` for the `StackNavigator`:
- `title` - a title (string) displayed in the header
- `header` - a config object for the header bar:
- `visible` - Boolean toggle of header visibility. Only works when `headerMode` is `screen`.
- `title` - Title string used by the navigation bar, or a custom React component
- `right` - Custom component displayed on the right side of the header
### Examples
See the examples [SimpleStack.js](https://github.com/reactjs/react-navigation/tree/master/examples/NavigationPlayground/js/SimpleStack.js) and [ModalStack.js](https://github.com/reactjs/react-navigation/tree/master/examples/NavigationPlayground/js/ModalStack.js) which you can run locally as part of the [NavigationPlayground](https://github.com/reactjs/react-navigation/tree/master/examples/NavigationPlayground) app.
### Navigator Props
The navigator component created by `StackNavigator(...)` takes the following props,
- `screenProps` - Props to pass to each child screen

View File

@@ -0,0 +1,143 @@
# TabNavigator
Used to easily set up a screen with several tabs with a TabRouter.
```js
class MyHomeScreen extends React.Component {
static navigationOptions = {
tabBar: {
label: 'Home',
// Note: By default the icon is only shown on iOS. Search the showIcon option below.
icon: ({ tintColor }) => (
<Image
source={require('./chats-icon.png')}
style={[styles.icon, {tintColor: tintColor}]}
/>
),
},
}
render() {
return (
<Button
onPress={() => this.props.navigation.navigate('Notifications')}
label="Go to notifications"
/>
);
}
}
class MyNotificationsScreen extends React.Component {
static navigationOptions = {
tabBar: {
label: 'Notifications',
icon: ({ tintColor }) => (
<Image
source={require('./notif-icon.png')}
style={[styles.tabIcon, {tintColor: tintColor}]}
/>
),
},
}
render() {
return (
<Button
onPress={() => this.props.navigation.goBack()}
label="Go back home"
/>
);
}
}
const styles = StyleSheet.create({
icon: {
width: 26,
height: 26,
},
});
const MyApp = TabNavigator({
Home: {
screen: MyHomeScreen,
},
Notifications: {
screen: MyNotificationsScreen,
},
}, {
tabBarOptions: {
activeTintColor: '#e91e63',
},
});
```
### Tab Navigator Options
- `tabBarComponent` - component to use as the tab bar, e.g. `TabView.TabBarBottom`
(this is the default on iOS), `TabView.TabBarTop`
(this is the default on Android)
- `tabBarPosition` - position of the tab bar, can be `'top'` or `'bottom'`
- `swipeEnabled` - whether to allow swiping between tabs
- `animationEnabled` - whether to animate when changing tabs
- `lazyLoad` - whether to lazily render tabs as needed as opposed to rendering them upfront
- `tabBarOptions` - configure the tab bar, see below.
Several options get passed to the underlying router to modify navigation logic:
- `initialTab` - The routeName for the initial tab route when first loading
- `order` - Array of routeNames which defines the order of the tabs
- `paths` - Provide a mapping of routeName to path config, which overrides the paths set in the routeConfigs.
- `backBehavior` - Should the back button cause a tab switch to the initial tab? If yes, set to `initialTab`, otherwise `none`. Defaults to `initialTab` behavior.
### `tabBarOptions` for `TabBarBottom` (default tab bar on iOS)
- `activeTintColor` - label and icon color of the active tab
- `activeBackgroundColor` - background color of the active tab
- `inactiveTintColor` - label and icon color of the inactive tab
- `inactiveBackgroundColor` - background color of the inactive tab
- `style` - style object for the tab bar
Example:
```js
tabBarOptions: {
activeTintColor: '#e91e63',
style: {
backgroundColor: 'blue',
}
}
```
### `tabBarOptions` for `TabBarTop` (default tab bar on Android)
- `activeTintColor` - label and icon color of the active tab
- `inactiveTintColor` - label and icon color of the inactive tab
- `showIcon` - whether to show icon for tab, default is false
- `showLabel` - whether to show label for tab, default is true
- `upperCaseLabel` - whether to make label uppercase, default is true
- `pressColor` - color for material ripple (Android >= 5.0 only)
- `pressOpacity` - opacity for pressed tab (iOS and Android < 5.0 only)
- `scrollEnabled` - whether to enable scrollable tabs
- `tabStyle` - style object for the tab
- `indicatorStyle` - style object for the tab indicator (line at the bottom of the tab)
- `labelStyle` - style object for the tab label
- `style` - style object for the tab bar
Example:
```js
tabBarOptions: {
labelStyle: {
fontSize: 12,
},
style: {
backgroundColor: 'blue',
}
}
```
### Navigator Props
The navigator component created by `TabNavigator(...)` takes the following props,
- `screenProps` - Props to pass to each child screen

130
docs/api/routers/Routers.md Normal file
View File

@@ -0,0 +1,130 @@
# Routers
Routers define a component's navigation state, and they allow the developer to define paths and actions that can be handled.
## Built-In Routers
`react-navigation` ships with a few standard routers:
- [StackRouter](/docs/routers/stack)
- [TabRouter](/docs/routers/tabs)
## Using Routers
To make a navigator manually, put a static `router` on a component. (To quickly make a navigator with a built-in component, it may be easier to use a [Navigator Factory](/docs/navigators) instead)
```js
class MyNavigator extends React.Component {
static router = StackRouter(routes, config);
...
}
```
Now you can use this component as a `screen` in another navigator, and the navigation logic for `MyNavigator` will be defined by this `StackRouter`.
## Customizing Routers
See the [Custom Router API spec](/docs/routers/api) to learn about the API of `StackRouter` and `TabRouter`. You can override the router functions as you see fit:
### Custom Navigation Actions
To override navigation behavior, you can override the navigation state logic in `getStateForAction`, and manually manipulate the `routes` and `index`.
```js
const MyApp = StackNavigator({
Home: { screen: HomeScreen },
Profile: { screen: ProfileScreen },
}, {
initialRouteName: 'Home',
})
const MyApp.router = {
...MyApp.router,
getStateForAction(action, state) {
if (state && action.type === 'PushTwoProfiles') {
const routes = [
...state.routes,
{key: 'A', routeName: 'Profile', params: { name: action.name1 }},
{key: 'B', routeName: 'Profile', params: { name: action.name2 }},
];
return {
...state,
routes,
index: routes.length - 1,
};
}
return MyApp.router.getStateForAction(action, state);
},
};
```
### Blocking Navigation Actions
Sometimes you may want to prevent some navigation activity, depending on your route.
```js
const MyStackRouter = StackRouter({
Home: { screen: HomeScreen },
Profile: { screen: ProfileScreen },
}, {
initialRouteName: 'Home',
})
const MyAppRouter = {
...MyStackRouter,
getStateForAction(action, state) {
if (
state &&
action.type === 'Back' &&
state.routes[state.index].params.isEditing
) {
// Returning null from getStateForAction means that the action
// has been handled/blocked, but there is not a new state
return null;
}
return MyStackRouter.getStateForAction(action, state);
},
};
```
### Handling Custom URIs
Perhaps your app has a unique URI which the built-in routers cannot handle. You can always extend the router `getActionForPathAndParams`.
```js
const MyApp = StackNavigator({
Home: { screen: HomeScreen },
Profile: { screen: ProfileScreen },
}, {
initialRouteName: 'Home',
})
MyApp.router = {
...MyApp.router,
getActionForPathAndParams(path, params) {
if (
path === 'my/custom/path' &&
params.magic === 'yes'
) {
// returns a profile navigate action for /my/custom/path?magic=yes
return {
type: 'Navigate',
routeName: 'Profile',
action: {
// This child action will get passed to the child router
// ProfileScreen.router.getStateForAction to get the child
// navigation state.
type: 'Navigate',
routeName: 'Friends',
},
};
return null;
}
return MyApp.router.getStateForAction(action, state);
},
};
```

View File

@@ -0,0 +1,101 @@
## Custom Router API
You can make your own router by building an object with the following functions:
```js
const MyRouter = {
getStateForAction: (action) => ({}),
getActionForPathAndParams: (path, params) => null,
getPathAndParamsForState: (state) => null,
getComponentForState: (state) => MyScreen,
getComponentForRouteName: (routeName) => MyScreen,
};
// Now, you can make a navigator by putting the router on it:
class MyNavigator extends React.Component {
static router = MyRouter;
render() {
...
}
}
```
![Routers manage the relationship between URIs, actions, and navigation state](/assets/routers-concept-map.png)
### `getStateForAction(action, state)`
Defines the navigation state in response to a given action. This function will be run when an action gets passed into `props.navigation.dispatch(`, or when any of the helper functions are called, like `navigation.navigate(`.
Typically this should return a navigation state, with the following form:
```
{
index: 1, // identifies which route in the routes array is active
routes: [
{
// Each route needs a name to identify the type.
routeName: 'MyRouteName',
// A unique identifier for this route in the routes array:
key: 'myroute-123',
// (used to specify the re-ordering of routes)
// Routes can have any data, as long as key and routeName are correct
...randomRouteData,
},
...moreRoutes,
]
}
```
If the router has handled the action externally, or wants to swallow it without changing the navigation state, this function will return `null`.
### `getComponentRouteName(routeName)`
Returns the child component or navigator for the given route name.
Say a router `getStateForAction` outputs a state like this:
```js
{
index: 1,
routes: [
{ key: 'A', routeName: 'Foo' },
{ key: 'B', routeName: 'Bar' },
],
}
```
Based on the routeNames in the state, the router is responsible for returning valid components when calling `router.getComponentRouteName('Foo')` or `router.getComponentRouteName('Bar')`.
### `getComponentForState(state)`
Returns the active component for a deep navigation state.
### `getActionForPathAndParams`
Returns an optional navigation action that should be used when the user navigates to this path and provides optional query parameters.
### `getPathAndParamsForState`
Returns the path and params that can be put into the URL to link the user back to the same spot in the app.
The path/params that are output from this should form an action when passed back into the router's `getActionForPathAndParams`. That action should take you to a similar state once passed through `getStateForAction`.
### `getScreenConfig`
Used to retrieve the navigation options for a route. Must provide the screen's current navigation prop, and the name of the option to be retrieved.
- `navigation` - This is the navigation prop that the screen will use, where the state refers to the screen's route/state. Dispatch will trigger actions in the context of that screen.
- `optionName` - What named option is being fetched, such as 'title'
Inside an example view, perhaps you need to fetch the configured title:
```js
// First, prepare a navigation prop for your child, or re-use one if already available.
const childNavigation = addNavigationHelpers({
// In this case we use navigation.state.index because we want the title for the active route.
state: navigation.state.routes[navigation.state.index],
dispatch: navigation.dispatch,
})
const screenTitle = this.props.router.getScreenConfig(childNavigation, 'title');
```

View File

@@ -0,0 +1,64 @@
# StackRouter
Manage the logical navigation stack, including pushing, popping, and handling path parsing to create a deep stack.
Let's take a look at a simple stack router:
```js
const MyApp = StackRouter({
Home: { screen: HomeScreen },
Profile: { screen: ProfileScreen },
}, {
initialRouteName: 'Home',
})
```
### RouteConfig
A basic stack router have a route config. Here is an example configuration:
```js
const MyApp = StackRouter({ // This is the RouteConfig:
Home: {
screen: HomeScreen,
path: '',
},
Profile: {
screen: ProfileScreen,
path: 'profile/:name',
},
Settings {
// This can be handy to lazily require a screen:
getScreen: () => require('Settings').default,
// Note: Child navigators cannot be configured using getScreen because
// the router will not be accessible. Navigators must be configured
// using `screen: MyNavigator`
path: 'settings',
},
});
```
Each item in the config may have the following:
- `path` - Specify the path and params to be parsed for item in the stack
- `screen` - Specify the screen component or child navigator
- `getScreen` - Set a lazy getter for a screen component (but not navigators)
### StackConfig
Config options that are also passed to the stack router.
- `initalRouteName` - The routeName for the default route when the stack first loads
- `initialRouteParams` - Default params of the initial route
- `paths` - Provide a mapping of routeName to path config, which overrides the paths set in the routeConfigs.
### Supported Actions
The stack router may respond to the following navigation actions. The router will generally delegate the action handling to a child router, if possible.
- Navigate - Will push a new route on the stack if the routeName matches one of the router's routeConfigs
- Back - Goes back (pops)
- Reset - Clears the stack and provides new actions to create a fully new navigation state
- SetParams - An action that a screen dispatches to change the params of the current route.

View File

@@ -0,0 +1,60 @@
# TabRouter
Manage a set of tabs in the application, handle jumping to tabs, and handle the back button press to jump to the initial tab.
Let's take a look at a simple tabs router:
```js
const MyApp = TabRouter({
Home: { screen: HomeScreen },
Settings: { screen: SettingsScreen },
}, {
initialTab: 'Home',
})
```
### RouteConfig
A tabs router has a routeConfig for each possible tab:
```js
const MyApp = TabRouter({ // This is the RouteConfig:
Home: {
screen: HomeScreen,
path: 'main',
},
Settings: {
// This can be handy to lazily require a tab:
getScreen: () => require('./SettingsScreen').default,
// Note: Child navigators cannot be configured using getScreen because
// the router will not be accessible. Navigators must be configured
// using `screen: MyNavigator`
path: 'settings',
},
});
```
Each item in the config may have the following:
- `path` - Specify the path for each tab
- `screen` - Specify the screen component or child navigator
- `getScreen` - Set a lazy getter for a screen component (but not navigators)
### Tab Router Config
Config options that are also passed to the router.
- `initialTab` - The routeName for the initial tab route when first loading
- `order` - Array of routeNames which defines the order of the tabs
- `paths` - Provide a mapping of routeName to path config, which overrides the paths set in the routeConfigs.
- `backBehavior` - Should the back button cause a tab switch to the initial tab? If yes, set to `initialTab`, otherwise `none`. Defaults to `initialTab` behavior.
### Supported Actions
The tabs router may respond to the following navigation actions. The router will generally delegate the action handling to a child router, if possible.
- Navigate - Will jump to the routeName if it matches a tab
- Back - Goes to the first tab, if not already selected
- SetParams - An action that a screen dispatches to change the params of the current route.

View File

@@ -0,0 +1,191 @@
# Transitioner
`Transitioner` is a React component that helps manage transitions for complex animated components. It manages the timing of animations and keeps track of various screens as they enter and leave, but it doesn't know what anything looks like, because rendering is entirely deferred to the developer.
Under the covers, `Transitioner` is used to implement `CardStack`, and hence the `StackNavigator`.
The most useful thing `Transitioner` does is to take in a prop of the current navigation state. When routes are removed from that navigation state, `Transitioner` will coordinate the transition away from those routes, keeping them on screen even though they are gone from the navigation state.
## Example
```jsx
class MyNavView extends Component {
...
render() {
return (
<Transitioner
configureTransition={this._configureTransition}
navigationState={this.props.navigation.state}
render={this._render}
onTransitionStart={this.onTransitionStart}
onTransitionEnd={this.onTransitionEnd}
/>
);
}
```
## Props
### `configureTransition` function
Invoked on `Transitioner.componentWillReceiveProps`, this function allows customization of animation parameters such as `duration`. The value returned from this function will be fed into a timing function, by default `Animated.timing()`, as its config.
#### Examples
```js
_configureTransition(transitionProps, prevTransitionProps) {
return {
// duration in milliseconds, default: 250
duration: 500,
// An easing function from `Easing`, default: Easing.inOut(Easing.ease)
easing: Easing.bounce,
}
}
```
Note: `duration` and `easing` are only applicable when the timing function is `Animated.timing`. We can also use a different timing function and its corresponding config parameters, like so:
```js
_configureTransition(transitionProps, prevTransitionProps) {
return {
// A timing function, default: Animated.timing.
timing: Animated.spring,
// Some parameters relevant to Animated.spring
friction: 1,
tension: 0.5,
}
}
```
#### Flow definition
```js
configureTransition: (
transitionProps: NavigationTransitionProps,
prevTransitionProps: ?NavigationTransitionProps,
) => NavigationTransitionSpec,
```
#### Parameters
- `transitionProps`: the current [NavigationTransitionProps](https://github.com/reactjs/react-navigation/blob/master/src/TypeDefinition.js#L273) created from the current navigation state and props
- `prevTransitionProps`: the previous [NavigationTransitionProps](https://github.com/reactjs/react-navigation/blob/master/src/TypeDefinition.js#L273) created from the previous navigation state and props
#### Returns
- An object of type [NavigationTransitionSpec](https://github.com/reactjs/react-navigation/blob/master/src/TypeDefinition.js#L316) that will be fed into an Animated timing function as its config
### `navigationState` object
A plain object that represents the navigation state
#### Example value
```js
{
// Index refers to the active child route in the routes array.
index: 1,
routes: [
{ key: 'DF2FGWGAS-12', routeName: 'ContactHome' },
{ key: 'DF2FGWGAS-13', routeName: 'ContactDetail', params: { personId: 123 } }
]
}
```
#### Flow definition
```js
export type NavigationState = {
index: number,
routes: Array<NavigationRoute>,
};
```
For more information about the `NavigationRoute` type, check out its [flow definition](https://github.com/reactjs/react-navigation/blob/master/src/TypeDefinition.js#L32).
### `render` function
Invoked from `Transitioner.render()`. This function performs the actual rendering delegated from `Transitioner`. In this function, we can use the information included in the `transitionProps` and `prevTransitionProps` parameters to render scenes, create animations and handle gestures.
There are a few important properties of the `transitionProps` and `prevTransitionProps` parameters that are useful for the tasks mentioned above:
- `scenes: Array<NavigationScene>` - a list of all available scenes
- `position: NavigationAnimatedValue` - the progressive index of the transitioner's navigation state
- `progress: NavigationAnimatedValue` - the value that represents the progress of the transition when navigation state changes from one to another. Its numberic value will range from 0 to 1.
For the complete list of properties of `NavigationTransitionProps`, check out its [flow definition](https://github.com/reactjs/react-navigation/blob/master/src/TypeDefinition.js#L273).
#### Examples
`transitionProps.scenes` is the list of all available scenes. It is up to the implementor to determine how to lay them out on the screen. For example, we can render the scenes as a stack of cards like so:
```jsx
_render(transitionProps, prevTransitionProps) {
const scenes = transitionProps.scenes.map(scene => this._renderScene(transitionProps, scene));
return (
<View style={styles.stack}>
{scenes}
</View>
);
}
```
We can then use an `Animated.View` to animate the transition. To create necessary animated style properties, such as `opacity`, we can interpolate on `position` and `progress` values that come with `transitionProps`:
```jsx
_renderScene(transitionProps, scene) {
const { position } = transitionProps;
const { index } = scene;
const opacity = position.interpolate({
inputRange: [index-1, index, index+1],
outputRange: [0, 1, 0],
});
// The prop `router` is populated when we call `createNavigator`.
const Scene = this.props.router.getComponent(scene.route.routeName);
return (
<Animated.View style={{ opacity }}>
{ Scene }
</Animated.View>
)
}
```
The above code creates a cross fade animation during transition.
For a comprehensive tutorial on how to create custom transitions, see this [blog post](http://www.reactnativediary.com/2016/12/20/navigation-experimental-custom-transition-1.html).
#### Flow definition
```js
render: (transitionProps: NavigationTransitionProps, prevTransitionProps: ?NavigationTransitionProps) => React.Element<*>,
```
#### Parameters
- `transitionProps`: the current [NavigationTransitionProps](https://github.com/reactjs/react-navigation/blob/master/src/TypeDefinition.js#L273) created from the current state and props
- `prevTransitionProps`: the previous [NavigationTransitionProps](https://github.com/reactjs/react-navigation/blob/master/src/TypeDefinition.js#L273) created from the previous state and props
#### Returns
- A ReactElement, which will be used to render the Transitioner component
### `onTransitionStart` function
Invoked when the transition animation is about to start.
#### Flow definition
```js
onTransitionStart: (transitionProps: NavigationTransitionProps, prevTransitionProps: ?NavigationTransitionProps) => void,
```
#### Parameters
- `transitionProps`: the current [NavigationTransitionProps](https://github.com/reactjs/react-navigation/blob/master/src/TypeDefinition.js#L273) created from the current state and props
- `prevTransitionProps`: the previous [NavigationTransitionProps](https://github.com/reactjs/react-navigation/blob/master/src/TypeDefinition.js#L273) created from the previous state and props
#### Returns
- none.
### `onTransitionEnd` function
Invoked once the transition animation completes.
#### Flow definition
```js
onTransitionEnd: () => void
```
#### Parameters
- none.
#### Returns
- none.

17
docs/api/views/Views.md Normal file
View File

@@ -0,0 +1,17 @@
# Views
Navigation views are presentation components that take a [`router`](/docs/api/routers) and a [`navigation`](/docs/navigators/navigation-prop) prop, and can display several screens, as specified by the `navigation.state`.
Navigation views are controlled React components that can present the current navigation state. They manage switching of screens, animations and gestures. They also present persistent navigation views such as tab bars and headers.
## Built in Views
- [CardStack](https://github.com/reactjs/react-navigation/blob/master/src/views/CardStack.js) - Present a stack that looks suitable on any platform
+ [Card](https://github.com/reactjs/react-navigation/blob/master/src/views/Card.js) - Present one card from the card stack, with gestures
+ [Header](https://github.com/reactjs/react-navigation/blob/master/src/views/Header.js) - The header view for the card stack
- [Tabs](https://github.com/reactjs/react-navigation/blob/master/src/views/TabView) - A configurable tab switcher / pager
- [Drawer](https://github.com/reactjs/react-navigation/tree/master/src/views/Drawer) - A view with a drawer that slides from the left
## [Transitioner](/docs/views/transitioner)
`Transitioner` manages the animations during the transition and can be used to build fully custom navigation views. It is used inside the `CardStack` view. [Learn more about Transitioner here.](/docs/views/transitioner)

View File

@@ -0,0 +1,61 @@
# Introducing React Navigation for React Native
Today we're excited to introduce React Navigation, a flexible navigation library for React Native and web, including customizable views for React Native, routers for any platform, and navigators that make it super easy to get started. We aim to provide a simple and extensible solution which enables developers to share one navigation paradigm for all of their React apps.
## Start Quick with pre-built Navigators
A navigator is a React component with a static `.router` declared on it. To make it super easy to get started, React Navigation ships with a few navigator factories, pairing common views with routers.
For example, the provided `StackNavigator` makes it easy to use a `CardStack` view and a `StackRouter` together:
```js
const MyApp = StackNavigator({
Home: {screen: HomeScreen},
Profile: {screen: ProfileScreen},
});
```
Each of these screens are just React components, and they can easily set their own title:
```js
class HomeScreen extends React.Component {
static navigationOptions = {
title: 'Home',
};
render() {
const { navigate } = this.props.navigation;
return (
<Button
onPress={() => navigate('Profile', { name: 'A' })}
title="Go to A's profile"
/>
);
}
}
```
To learn more, [continue with the getting started guide](/docs/intro).
## Performant Views on React Native
Animations and gestures are critical for smooth navigation in a mobile app. React Navigation utilizes React Native's Animated library to provide 60fps animations that are driven from the native thread.
The views are designed to be highly extensible. For your app, you may want to build a custom modal, fork the stack header, or even utilize the underlying `<Transitioner>` component to build an entirely custom navigation presentation.
## Routers for Every Platform
In React Navigation, routers manage the [relationship between actions, state, and URIs](/docs/routers/api). The routers are cross-platform, and there is example code for iOS, Android, and web. Several routers are included, including [`TabRouter`](/docs/routers/tab) and [`StackRouter`](/docs/routers/stack), and it is encouraged to [override their behavior as needed](/docs/routers).
The routers are composable and can be useful for structuring your app. A common navigation structure in iOS is to have an independent navigation stack for each tab, where all tabs can be covered by a modal. This is three layers of router: a card stack, within tabs, all within a modal stack. So unlike our experience on web apps, the navigation state of mobile apps is too complex to encode into a single URI. Routers in `react-navigation` map from URIs to navigation actions, which are then used to compute navigation state.
## Future
React Navigation is born from the React Native community's need for an extensible yet easy-to-use navigation solution. It replaces and improves upon several navigation libraries in the ecosystem, including Ex-Navigation and React Native's Navigator and NavigationExperimental components.
Until the community lands on one navigation solution that works well on the web and React Native, we will forever be destined to re-invent navigation. We are extremely sensitive about the burden of change that accompanies a new navigation library, so we aim to provide a solution that will work long into the future. We are excited to support React Navigation for any platform, including cutting-edge frontiers like hybrid native apps, web server rendering, and ReactVR.
The first beta of React Navigation is available today on npm and GitHub, and you can [get started here](/docs/intro). We're excited to hear feedback from the React community, and together we still have a long way to go before our dream is realized. We'd love to see the community flourish with beautiful navigation views, custom router integrations, and more easy-to-use navigators. All of these individual contributions can work together seamlessly. If you have improvements for the built-in components, please [follow the contributors guide](/docs/guides/contributors) and dive right in!

View File

@@ -0,0 +1,229 @@
# Common Navigation Spec
### Introduction
It is useful to have “one standard way” to handle navigation in a React app. Unfortunately, we've learned that a single navigation library cannot be the right fit for every application. There is often a tradeoff between several useful features:
* Simplicity
* Supporting complex animations
* Navigating between natively-implemented screens and JavaScript screens
* Precise fidelity to the native UI controls
* Default support for deep linking and the Android back button
Although the React Native community will need more than one navigation library, we can make them work nicely together. The goal of this document is to specify a common API for navigation libraries. Consistency will make several things easier for React Native developers:
* Combining multiple navigation libraries in one application
* Switching out navigation libraries when requirements change
* Learning navigation once, and applying that knowledge in different applications
Navigation libraries don't have to implement all of this API - it's just a recommendation.
(TODO: When we actually publish this spec, we should mention here what libraries are supporting it, what libraries will support it, and (link to?) how to migrate from other now-discouraged navigation libraries.)
## Key Concepts
#### Navigation Container
The parent component which hosts a navigation-aware component. It must provide the `navigation` prop, and usually uses the child component's static `router` to determine navigation state.
#### Navigation-Aware Component
A React component which can observe and initiate navigation in an app. It uses the navigation prop to see navigation state and request actions. It may expose a router to define navigation state and URI handling.
The card stack of your application may be a navigation-aware component. Also, one screen of your app that handles the Android back button is a navigation-aware component.
#### Navigation State
The object that defines the navigation state of your component, passed in as a prop. A router can define the state, which optionally specifies the title and URI of the component.
#### Action
A JSON object used to request changes in the app's navigation state.
#### Router
Defines the navigation behavior of a component by defining navigation state as a function of actions, and allows URIs to be optionally converted into an action that can be handled.
#### Navigator
A navigation-aware component that hosts other navigation-aware components. Most navigators are expected to delegate all router logic, manage child navigation state, and pass up actions as they are dispatched.
## Specification
### The `navigation` prop
The navigation prop should be provided to components who need access to navigation. If provided, it must follow this interface:
```javascript
type BackAction = {type: 'Back'};
type URIAction = {type: 'URI', uri: string};
interface Navigation<S, A> {
dispatch(action: (A | BackAction | URIAction)): boolean;
state: S;
}
```
#### navigation.state
The controlled navigation state prop, as requested by the parent.
```javascript
const MyView = ({ navigation }) => {
switch (navigation.state.myRequestedView) {
case 'ViewA': return <ViewA />;
case 'ViewB': return <ViewB />;
default: return <OtherView />;
}
}
```
#### navigation.dispatch(action)
The channel that a component can call to request navigation from its parent. When calling `dispatch`, you must provide an action object with a `type`. There are two special action types: 'Back' and 'URI'.
```javascript
const MyLink = ({ navigation }) => (
<Button onPress={() => {
navigation.dispatch({
type: 'MyNavigationRequest',
myParam: 42,
});
}>
Press me to navigate
</Button>
);
```
### The static `router`
A router object may be statically defined on your component. If defined, it must follow this interface:
```javascript
type BackAction = {type: 'Back'};
type URIAction = {type: 'URI', uri: string};
interface Router<S, A> {
getStateForAction(action: (A | BackAction | URIAction), lastState: ?S): ?S;
getActionForURI(uri: string): ?A;
}
```
The state and action types of the static router must match the state and action types associated with the navigation prop passed into the component.
#### router.getStateForAction(action, lastState)
This function is defined on the static router and is used to define the expected navigation state.
```javascript
class ScreenWithEditMode extends React.Component {
static router = {
getStateForAction: (action, prevState) => {
return { isEditing: true };
},
};
render() {
// this.props.navigation.state.isEditing === true
...
}
}
```
`getStateForAction` must **always** return a navigation state that can be rendered by the component when passed in as the `navigation.state` prop.
If null is returned, we are signaling that the previous navigation state has not changed, but the action is handled. This is usually used in cases where the action is being swallowed.
#### router.getActionForURI(uri)
Return an action if a URI can be handled, otherwise return `null`
### Special Actions
There are two special actions that can be fired into `navigation.dispatch` and can be handled by your `router.getStateForAction`.
#### Back Action
This action means the same thing as an Android back button press.
```
type BackAction = { type: 'Back' };
```
#### URI Open Action
Used to request the enclosing app or OS to open a link at a particular URI. If it is a web URI like `http` or `https`, the app may open a WebView to present the page. Or the app may open the URI in a web browser. In some cases, an app may choose to block a URI action or handle it differently.
```
type URIAction = { type: 'URI', uri: string };
```
### Special Navigation State
The state defined by `router.getStateForAction` can contain special navigation properties that may be relevant to your app. The title and current URI of a component may change over time, and the parent often needs to observe the behavior.
#### `state.title`
If the navigation state contains 'title', it will be used as the title for the given component. This is relevant for top-level components on the web to update the browser title, and is relevant in mobile apps where a title is shown in the header.
#### `state.uri`
A URI can also be put in `state.uri`, which will signal to the parent how it may be possible to deep link into a similar navigation state. In web apps, this will be used to keep the URI bar in sync with the current navigation state of the app.
## Use Cases
### "Block the Android back button on one screen of my app"
To block the Android back button:
```
class Foo extends React.Component {
static router = {
getStateForAction(action, prevState = {}) {
if (action.type === 'Back') return null;
else return prevState;
},
};
render() {
...
```
Because we return null, we signal to our container that the action has been handled but the state does not change. The parent should not handle the back behavior at this point, and nothing should be re-rendered.
### "Link deeply into one screen of my app"
```
class Foo extends React.Component {
static router = {
getStateForAction(action, prevState = {deep: false}) {
if (action.type === 'GoDeep') return { deep: true };
else return prevState;
},
getActionForURI(uri) {
if (uri === 'myapp://foo')
return {type: 'Go'};
else if (uri === 'myapp://foo_deep')
return {type: 'GoDeep'};
return null;
},
};
render() {
// this.props.navigation.state.deep may be true or false
...
```
Based on the state URI we may decide to return an action. If an action is returned, `getStateForAction` is expected to output the correct state for a deep link.
## Reference Implementations
A library to that helps easily produce navigation-aware components: https://github.com/reactjs/react-navigation . (Also uses a HOC to provide navigation containers when needed.)
A simple navigation container: https://gist.github.com/ericvicenti/77d190e2ec408012255937400e34bdb1
A web implementation of a navigation container: https://gist.github.com/ericvicenti/55bef95fcd8558029a3bae8483baea6c

View File

@@ -0,0 +1,79 @@
# Contributors Guide
## Environment
React navigation was initially developed on macOS 10.12, with node 7+, and react-native v0.39+. Please open issues when uncovering problems in different environments.
## Development
### 0. Basic Install
```
git clone git@github.com:reactjs/react-navigation.git
cd react-navigation
npm install
```
### 1. Run the native playground
```
cd examples/NavigationPlayground
npm install
cd ../..
npm start
# In a seperate terminal tab:
npm run run-playground-android
npm run run-playground-ios
```
### 2. Run the website
For development mode and live-reloading:
```
cd website
npm install
npm run start
```
To run the website in production mode with server rendering:
```
npm run prod
```
### 3. Run tests, run flow
```
jest
flow
```
Tests must pass for your changes to be accepted and merged.
Flow is not yet passing, but your code should be flow checked and we expect that your changes do not introduce any flow errors.
### 4. Developing Docs
The docs are indexed in [App.js](https://github.com/reactjs/react-navigation/blob/master/website/src/App.js), where all the pages are declared alongside the titles. To test the docs, follow the above instructions for running the website. Changing existing markdown files should not require any testing.
The markdown from the `docs` folder gets generated and dumped into a json file as a part of the build step. To see updated docs appear in the website, re-run the build step by running `npm run build-docs` from the `react-navigation` root folder.
## Submitting Contributions
### New views or unique features
Often navigation needs are specific to certain apps. If your changes are unique to your app, you may want to fork the view or router that has changed. You can keep the source code in your app, or publish it on npm as a `react-navigation` compatible view or router.
This library is intended to include highly standard and generic navigation patterns. But it
### Major Changes
Before embarking on any major changes, please file an issue describing the suggested change and motivation. We may already have thought about it and we want to make sure we all are on the same page before starting on any big changes.
### Minor Bugfixes
Simple bug fixes are welcomed in pull requests! Please check for duplicate PRs before posting.

View File

@@ -0,0 +1,110 @@
# Custom Navigators
A navigator is any React component that has a [router](/docs/routers/) on it. Here is a basic one, which uses the [router's API](/docs/routers/api) to get the active component to render:
```js
class MyNavigator extends React.Component {
static router = MyRouter;
render() {
const { state, dispatch } = this.props.navigation;
const { routes, index } = state;
// Figure out what to render based on the navigation state and the router:
const Component = MyRouter.getComponentForState(state);
// The state of the active child screen can be found at routes[index]
let childNavigation = { dispatch, state: routes[index] };
// If we want, we can also tinker with the dispatch function here, to limit
// or augment our children's actions
// Assuming our children want the convenience of calling .navigate() and so on,
// we should call addNavigationHelpers to augment our navigation prop:
childNavigation = addNavigationHelpers(childNavigation);
return <Component navigation={childNavigation} />;
}
}
```
## Navigation Prop
The navigation prop passed down to a navigator only includes `state` and `dispatch`. This is the current state of the navigator, and an event channel to send action requests.
All navigators are controlled components: they always display what is coming in through `props.navigation.state`, and their only way to change the state is to send actions into `props.navigation.dispatch`.
Navigators can specify custom behavior to parent navigators by [customizing their router](/docs/routers/). For example, a navigator is able to specify when actions should be blocked by returning null from `router.getStateForAction`. Or a navigator can specify custom URI handling by overriding `router.getActionForPathAndParams` to output a relevant navigation action, and handling that action in `router.getStateForAction`.
### Navigation State
The navigation state that is passed into a navigator's `props.navigation.state` has the following structure:
```
{
index: 1, // identifies which route in the routes array is active
routes: [
{
// Each route needs a name, which routers will use to associate each route
// with a react component
routeName: 'MyRouteName',
// A unique id for this route, used to keep order in the routes array:
key: 'myroute-123',
// Routes can have any additional data. The included routers have `params`
...customRouteData,
},
...moreRoutes,
]
}
```
### Navigation Dispatchers
A navigator can dispatch navigation actions, such as 'Go to a URI', 'Go back'.
The dispatcher will return `true` if the action was successfully handled, otherwise `false`.
## Navigation Containers
The built in navigators can automatically behave like top-level navigators when the navigation prop is missing. This functionality provides a transparent navigation container, which is where the top-level navigation prop comes from.
When rendering one of the included navigators, the navigation prop is optional. When it is missing, the container steps in and manages its own navigation state. It also handles URLs, external linking, and Android back button integration.
For the purpose of convenience, the built-in navigators have this ability because behind the scenes they use `createNavigationContainer`. Usually, navigators require a navigation prop in order to function.
### `containerOptions`
These options can be used to configure a navigator when it is used at the top level.
An error will be thrown if a navigator is configured with `containerOptions` and also receives a `navigation` prop, because in that case it would be unclear if the navigator should handle its own state.
- `URIPrefix` - The prefix of the URIs that the app might handle. This will be used when handling a [deep link](/docs/guides/linking) to extract the path passed to the router.
## API for building custom navigators
To help developers implement custom navigators, the following utilities are provided with React Navigation:
### `createNavigator`
This utility combines a [router](/docs/routers/) and a [navigation view](/docs/views/) together in a standard way:
```js
const MyApp = createNavigator(MyRouter)(MyView);
```
All this does behind the scenes is:
```js
const MyApp = ({ navigation }) => (
<MyView router={MyRouter} navigation={navigation} />
);
MyApp.router = MyRouter;
```
### `addNavigationHelpers`
Takes in a bare navigator navigation prop with `state` and `dispatch`, and augments it with all the various functions in a screen navigation prop, such as `navigation.navigate()` and `navigation.goBack()`. These functions are simply helpers to create the actions and send them into `dispatch`.
### `createNavigationContainer`
If you want your navigator to be usable as a top-level component, (without a navigation prop being passed in), you can use `createNavigationContainer`. This utility will make your navigator act like a top-level navigator when the navigation prop is missing. It will manage the app state, and integrate with app-level nav features, like handling incoming and outgoing links, and Android back button behavior.

View File

@@ -0,0 +1,53 @@
## Customizing Navigation Views
Modify the presentation of navigation, including styles, animations and gestures.
## Customizing Routers
Building a custom router allows you to change the navigation logic of your component, manage navigation state, and define behavior for URIs.
A router can be defined like this:
```
class MyNavigationAwareComponent extends React.Component {
static router = {
// Defines the navigation state for a component:
getStateForAction: (action: {type: string}, lastState?: any) => {
const state = lastState = { myMode: 'default' };
if (action.type === 'MyAction') {
return { myMode: 'action' };
} else if (action.type === 'Back') {
return { myMode: 'blockBackButton' };
} else {
return state;
}
},
// Defines if a component can handle a particular URI.
// If it does, return an action to be passed to `getStateForAction`
getActionForURI: (uri: string) => {
if (uri === 'myapp://myAction') {
return { type: 'MyAction' };
}
return null;
},
};
render() {
// render something based on this.props.navigation.state
...
}
onButtonPress = () => {
this.props.navigation.dispatch({ type: 'MyAction' });
};
...
}
```

107
docs/guides/Deep-Linking.md Normal file
View File

@@ -0,0 +1,107 @@
# Deep Linking
In this guide we will set up our app to handle external URIs. Let's start with the SimpleApp that [we created in the getting started guide](/docs/intro).
In this example, we want a URI like `mychat://chats/Taylor` to open our app and link straight into Taylor's chat page.
## Configuration
Previously, we had defined a navigator like this:
```js
const SimpleApp = StackNavigator({
Home: { screen: HomeScreen },
Chat: { screen: ChatScreen },
});
```
We want paths like `chat/Taylor` to link to a "Chat" screen with the `user` passed as a param. Let's re-configure our chat screen with a `path` that tells the router what relative path to match against, and what params to extract. This path spec would be `chat/:user`.
```js
const SimpleApp = StackNavigator({
Home: { screen: HomeScreen },
Chat: {
screen: ChatScreen,
path: 'chat/:user',
},
});
```
### URI Prefix
Next, let's configure our navigation container to extract the path from the app's incoming URI. When configuring a top-level navigator, we can provide `containerOptions`:
```js
const SimpleApp = StackNavigator({
...
}, {
containerOptions: {
// on Android, the URI prefix typically contains a host in addition to scheme
URIPrefix: Platform.OS == 'android' ? 'mychat://mychat/' : 'mychat://',
},
});
```
## iOS
Let's configure the native iOS app to open based on the `mychat://` URI scheme.
In `SimpleApp/ios/SimpleApp/AppDelegate.m`:
```
// Add the header at the top of the file:
#import <React/RCTLinkingManager.h>
// Add this above the `@end`:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
return [RCTLinkingManager application:application openURL:url
sourceApplication:sourceApplication annotation:annotation];
}
```
In Xcode, open the project at `SimpleApp/ios/SimpleApp.xcodeproj`. Select the project in sidebar and navigate to the info tab. Scroll down to "URL Types" and add one. In the new URL type, set the identifier and the url scheme to your desired url scheme.
![Xcode project info URL types with mychat added](/assets/xcode-linking.png)
Now you can press play in Xcode, or re-build on the command line:
```sh
react-native run-ios
```
To test the URI in iOS, open safari and type `mychat://chat/Taylor`.
## Android
To configure the external linking in Android, you can create a new intent in the manifest.
In `SimpleApp/android/app/src/main/AndroidManifest.xml`, add the new `VIEW` type `intent-filter` inside the `MainActivity` entry:
```
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="mychat"
android:host="mychat" />
</intent-filter>
```
Now, re-install the app:
```sh
react-native run-android
```
To test the intent handling in Android, run the following:
```
adb shell am start -W -a android.intent.action.VIEW -d "mychat://mychat/chat/Taylor" com.simpleapp
```
```phone-example
linking
```

View File

@@ -0,0 +1,98 @@
# Configuring the Header
In the previous example, we created a StackNavigator to display severals screens in our app.
When navigating to a chat screen, we can specify params for the new route by providing them to the navigate function. In this case we want to provide the name of the person on the chat screen:
```js
this.props.navigation.navigate('Chat', { user: 'Lucy' });
```
The `user` param can be accessed from the profile screen:
```js
class ChatScreen extends React.Component {
const { params } = this.props.navigation.state;
render() {
return <Text>Chat with {params.user}</Text>;
}
}
```
### Setting the Header Title
Next, the header title can be configured to use the screen param:
```js
class ChatScreen extends React.Component {
static navigationOptions = {
// // Title may be a simple string:
// title: 'Hello',
// Or the title string may be a function of the navigation prop:
title: ({ navigation }) => `Chat with ${navigation.state.params.user}`
};
...
}
```
```phone-example
basic-header
```
### Adding a Right Button
Then we can add a [`header` navigation option](/docs/navigators/navigation-options#Stack-Navigation-Options) that allows us to add a custom right button:
```js
static navigationOptions = {
header: {
right: <Button title="Info" />,
},
...
```
```phone-example
header-button
```
Just like `title`, the `header` option can be defined as a function of the [navigation prop](/docs/navigators/navigation-prop). Lets render a different button based on the route params, and set up the button to call `navigation.setParams` when pressed.
```js
static navigationOptions = {
title: ({ state }) => {
if (state.params.mode === 'info') {
return `${state.params.user}'s Contact Info`;
}
return `Chat with ${state.params.user}`;
},
header: ({ state, setParams }) => {
// The navigation prop has functions like setParams, goBack, and navigate.
let right = (
<Button
title={`${state.params.user}'s info`}
onPress={() => setParams({ mode: 'info' }}
/>
);
if (state.params.mode === 'info') {
right = (
<Button
title="Done"
onPress={() => setParams({ mode: 'none' }}
/>
);
}
return { right };
},
...
```
Now, the header can interact with the screen route/state:
```phone-example
header-interaction
```
To see the rest of the header options, see the [navigation options document](/docs/navigators/navigation-options#Stack-Navigation-Options).

124
docs/guides/Guide-Intro.md Normal file
View File

@@ -0,0 +1,124 @@
# Hello Mobile Navigation
Let's use React Navigation to build a simple chat application for Android and iOS.
## Setup and Installation
First, make sure you're [all set up to use React Native](http://facebook.github.io/react-native/docs/getting-started.html). Next, create a new project and add `react-navigation`:
```sh
# Create a new React Native App
react-native init SimpleApp
cd SimpleApp
# Install the latest version of react-navigation from npm
npm install --save react-navigation
# Run the new app
react-native run-android # or:
react-native run-ios
```
Verify that you can successfully see the bare sample app run on iOS and/or Android:
```phone-example
bare-project
```
We want to share code on iOS and Android, so lets delete the contents of `index.ios.js` and `index.android.js` and replace it with `import './App';`.
Now lets create the new file for our app implementation, `App.js`.
## Introducing Stack Navigator
For our app, we want to use the `StackNavigator` because we want a conceptual 'stack' navigation, where each new screen is put on the top of the stack, and going back removes a screen from the top of the stack. Lets start with just one screen:
```js
import React from 'react';
import {
AppRegistry,
Text,
} from 'react-native';
import { StackNavigator } from 'react-navigation';
class HomeScreen extends React.Component {
static navigationOptions = {
title: 'Welcome',
};
render() {
return <Text>Hello, World!</Text>;
}
}
const SimpleApp = StackNavigator({
Home: { screen: HomeScreen },
});
AppRegistry.registerComponent('SimpleApp', () => SimpleApp);
```
The `title` of the screen is configurable on the [static `navigationOptions`](/docs/navigators/navigation-options), where many options can be set to configure the presentation of the screen in the navigator.
Now the same screen should appear on both iPhone and Android apps:
```phone-example
first-screen
```
## Adding a New Screen
Lets create a button in the `HomeScreen` component that links to our second page:
```js
class HomeScreen extends React.Component {
static navigationOptions = {
title: 'Welcome',
};
render() {
const { navigate } = this.props.navigation;
return (
<View>
<Text>Hello, Chat App!</Text>
<Button
onPress={() => navigate('Chat', { user: 'Lucy' })}
title="Chat with Lucy"
/>
</View>
);
}
}
```
We're using the navigate function from the [screen navigation prop](/docs/navigators/navigation-prop). In addition to specifying the target `routeName`, we can also pass params that will be put into the new route.
Now let's create the Chat screen that displays the `name` param passed in through the route:
```js
class ChatScreen extends React.Component {
static navigationOptions = {
// Nav options can be defined as a function of the navigation prop:
title: ({ state }) => `Chat with ${state.params.user}`,
};
render() {
// The screen's current route is passed in to `props.navigation.state`:
const { params } = this.props.navigation.state;
return (
<View>
<Text>Chat with {params.user}</Text>
</View>
);
}
}
const SimpleApp = StackNavigator({
Home: { screen: HomeScreen },
Chat: { screen: ChatScreen },
});
```
Now you can navigate to your new screen, and go back:
```phone-example
first-navigation
```

View File

@@ -0,0 +1,72 @@
# Nesting Navigators
It is common in mobile apps to compose various forms of navigation. The routers and navigators in React Navigation are composable, which allows you to define a complicated navigation structure for your app.
For our chat app, we want to put several tabs on the first screen, to view recent chat threads or all contacts.
## Introducing Tab Navigator
Lets create a new `TabNavigator` in our `App.js`:
```js
class RecentChatsScreen extends React.Component {
render() {
return <Text>List of recent chats</Text>
}
}
class AllContactsScreen extends React.Component {
render() {
return <Text>List of all contacts</Text>
}
}
const MainScreenNavigator = TabNavigator({
Recent: { screen: RecentChatsScreen },
All: { screen: AllContactsScreen },
});
```
If the `MainScreenNavigator` was rendered as the top-level navigator component, it would look like this:
```phone-example
simple-tabs
```
## Nesting a Navigator in a screen
We want these tabs to be visible in the first screen of the app, but new screens in the stack should cover the tabs.
Lets add our tabs navigator as a screen in our top-level `StackNavigator` that we set up in the [previous step](/docs/intro/).
```js
const SimpleApp = StackNavigator({
Home: { screen: MainScreenNavigator },
Chat: { screen: ChatScreen },
});
```
Because `MainScreenNavigator` is being used as a screen, we can give it `navigationOptions`:
```js
MainScreenNavigator.navigationOptions = {
title: 'My Chats',
};
```
Lets also add a button to each tab that links to a chat:
```js
<Button
onPress={() => this.props.navigation.navigate('Chat', { user: 'Lucy' })}
title="Chat with Lucy"
/>
```
Now we have put one navigator inside another, and we can `navigate` between navigators:
```phone-example
nested
```

View File

@@ -0,0 +1,174 @@
## Common React Navigation API - Hybrid Integration
This is a purely speculative API that demonstrates how it may be possible to integrate the [JS navigation API](./Common-Navigation-Spec.md) in a hybrid app.
## Setting up a screen
It should be possible to register new screens from JS into native. In your main bundle:
```
const HybridNavigationModule = require('NativeModules').HybridNavigation;
HybridNavigationModule.registerScreens([
{
type: 'Marketplace',
screen: MarketplaceScreen,
},
{
type: 'Product',
screen: ProductScreen,
},
]);
```
## Linking to JS
Now, your native code can open a react screen by type name:
```
// please pretend this is Obj-C or Java syntax:
CoreHybridNavigation.openReactScreen('Profile', {id: 123});
```
## Linking to Native
If JS product code wants to request navigation to a screen that may *or may not* be in native, it can do this:
```
const MarketplaceScreen = ({ navigation }) => (
<View>
<Button onPress={() => navigation.dispatch({
type: 'Product',
id: 42,
})}>
See product 42
</Button>
</View>
);
```
Inside the infra:
```
class InfraScreen extends React.Component {
constructor() {
const {initURI, type} = this.props;
const ScreenView = ScreenRegistry[type].screen;
const router = ScreenView.router;
const deepLinkAction = router.getActionForURI(initURI);
const initAction = deepLinkAction || {type: 'init'}
const nav = router.getStateForAction(initAction);
this.state = {
nav,
};
HybridNavigationModule.setNavOptions(this.state.nav);
}
componentWillUpdate() {
HybridNavigationModule.setNavOptions(this.state.nav);
}
dispatch = (action) => {
const {type} = this.props;
const ScreenView = ScreenRegistry[type].screen;
const {getStateForAction} = ScreenView.router;
const newNavState = getStateForAction(action, this.state.nav);
if (newNavState !== this.state.nav) {
this.setState({ nav: newNavState });
return true;
}
if (action.type === 'URI') {
HybridNavigationModule.openURI(action.uri);
return true;
}
if (action.type === 'Back') {
HybridNavigationModule.goBack();
return true;
}
HybridNavigationModule.openAction(action);
return true;
}
render() {
const {type} = this.props;
const ScreenView = ScreenRegistry[type].screen;
const navigation = {
dispatch: this.dispatch,
state: this.state.nav,
};
return <ScreenView navigation={navigation} />;
}
}
```
## Setting title
```
MarketplaceScreen.router = {
getStateForAction(action, lastState) {
return lastState || {title: 'Marketplace Home'};
},
};
```
A HOC could be used to make this feel more elegant.
## Disabling/Enabling the right button
```
const TestScreen = ({ navigation }) => (
<View>
<Button onPress={() => navigation.dispatch({
type: 'ToggleMyButtonPressability',
})}>
{navigation.state.rightButtonEnabled ? 'Disable' : 'Enable'} right button
</Button>
<Text>Pressed {navigation.state} times</Text>
</View>
);
TestScreen.router = {
getStateForAction(action, lastState = {}) {
let state = lastState || {
rightButtonEnabled: true,
rightButtonTitle: 'Tap Me',
pressCount: 0,
};
if (action.type === 'ToggleMyButtonPressability') {
state = {
...state,
rightButtonEnabled: !state.rightButtonEnabled,
};
} else if (action.type === 'RightButtonPress') {
state = {
...state,
pressCount: state.pressCount + 1,
};
}
return state;
},
};
```
## Before JS starts
A JSON file could be defined for native to consume before JS spins up:
```
{
"screens": [
{
"type": "Profile",
"path": "/users/:id?name=:name",
"params": {
"name": "string",
"id": "number"
},
"title": "%name%' s Profile",
"rightButtonTitle": "Message %name%"
},
{
...
}
]
}
```
This seems like a pain to set up, so we can statically analyze our JS and autogenerate this JSON! If the JS in an app changes, there could be a way for JS to report the new routing configuration to native for use on the next cold start.

View File

@@ -0,0 +1,46 @@
# Redux Integration
To handle your app's navigation state in redux, you can pass your own `navigation` prop to a navigator. Your navigation prop must provide the current state, as well as access to a dispatcher to handle navigation options.
With redux, your app's state is defined by a reducer. Each navigation router effectively has a reducer, called `getStateForAction`. The following is a minimal example of how you might use navigators within a redux application:
```
const AppNavigator = StackNavigator(AppRouteConfigs);
const appReducer = combineReducers({
nav: (state, action) => (
AppNavigator.router.getStateForAction(action, state)
),
...
});
@connect(state => {
nav: state.nav,
})
class AppWithNavigationState extends React.Component {
render() {
return (
<AppNavigator navigation={{
dispatch: this.props.dispatch,
state: this.props.nav,
}} />
);
}
}
const store = createStore(appReducer);
class App extends React.Component {
render() {
return (
<Provider store={store}>
<AppWithNavigationState />
</Provider>
);
}
}
```
Now, your navigation state is stored with redux, and you can fire navigation actions using redux.
When a navigator is given a `navigation` prop, it relinquishes control of the state. So you are now responsible for persisting state, handling deep linking, integrating the back button, etc.

View File

@@ -0,0 +1,57 @@
# Screen Navigation Options
Each screen can configure several aspects about how it gets presented in parent navigators.
#### Two Ways to specify each option
**Static configuration:** Each navigation option can either be directly assigned:
```js
class MyScreen extends React.Component {
static navigationOptions = {
title: 'Great',
};
...
```
**Dynamic Configuration**
Or, each option can be a function that takes the following arguments, and returns the value of the option.
- `navigation` - the [navigation prop](/docs/intro/navigation-prop) for the screen, with the screen's route at `navigation.state`
- `childRouter` - The child router, if the screen is a navigator
```js
class ProfileScreen extends React.Component {
static navigationOptions = {
title: (navigation, childRouter) => {
return navigation.state.params.name + "'s Profile!";
},
};
...
```
#### Generic Navigation Options
The `title` navigation option is generic between every navigator. It is used to set the title string for a given screen.
```js
class MyScreen extends React.Component {
static navigationOptions = {
title: 'Great',
};
...
```
Unlike the other nav options which are only utilized by the navigator view, the title option can be used by the environment to update the title in the browser window or app switcher.
## Tab Navigation Options
Coming Soon
## Stack Navigation Options
Coming Soon

View File

@@ -0,0 +1,142 @@
# Screen Navigation Prop
Each screen in your app will recieve a navigation prop, which contains the following:
## `navigate` - Link to other screens
Call this to link to another screen in your app. Takes the following arguments:
- `routeName` - A destination routeName that has been registered somewhere in the app's router
- `params` - Params to merge into the destination route
- `action` - (advanced) The sub-action to run in the child router, if the screen is a navigator.
```js
class HomeScreen extends React.Component {
render() {
const {navigate} = this.props.navigation;
return (
<View>
<Text>This is the home screen of the app</Text>
<Button
onPress={() => navigate('Profile', {name: 'Brent'})}
title="Go to Brent's profile"
/>
</View>
)
}
}
```
## `state` - The screen's current state/route
A screen has access to it's route via `this.props.navigation.state`. Each will contain:
- `routeName` - the name of the route config in the router
- `key` - a unique identifier used to sort routes
- `params` - an optional object of string options for this screen
```js
class ProfileScreen extends React.Component {
render() {
const {state} = this.props.navigation;
// state.routeName === 'Profile'
return (
<Text>Name: {state.params.name}</Text>
);
}
}
```
## `setParams` - Make changes to route params
Firing the `setParams` action allows a screen to change the params in the route, which is useful for updating the header buttons and title.
```js
class ProfileScreen extends React.Component {
render() {
const {setParams} = this.props.navigation;
return (
<Button
onPress={() => setParams({name: 'Lucy'})}
title="Set title name to 'Lucy'"
/>
)
}
}
```
## `goBack` - Close the active screen and move back
```js
class HomeScreen extends React.Component {
render() {
const {navigate} = this.props.navigation;
return (
<View>
<Button
onPress={() => goBack()}
title="Go back from this HomeScreen"
/>
<Button
onPress={() => goBack(null)}
title="Go back anywhere"
/>
<Button
onPress={() => goBack('screen-123')}
title="Go back from screen-123"
/>
</View>
)
}
}
```
Optionally provide a key, which specifies the route to go back from. By default, goBack will close the route that it is called from. If the goal is to go back *anywhere*, without specifying what is getting closed, call `.goBack(null);`
## `dispatch` - Send an action to the router
Use dispatch to send any navigation action to the router. The other navigation functions use dispatch behind the scenes.
The following actions are supported:
### Navigate
```js
{
type: 'Navigate',
routeName: 'Profile',
params: {},
// navigate can have a nested navigate action that will be run inside the child router
action: {type: 'Navigate', routeName: 'SubProfileRoute'}
}
```
### Reset
The `Reset` action wipes the whole navigation state and replaces it with the result of several actions.
```js
{
type: 'Reset',
actions: ,
}
```
### SetParams
When dispatching `SetParams`, the router will produce a new state that has changed the params of a particular route, as identified by the key
```js
{
type: 'SetParams',
params: {}, // these are the new params that will be merged into the existing route params
// The key of the route that should get the new params
key: 'screen-123',
}
```

View File

@@ -0,0 +1,16 @@
# Web Integration
React navigation routers work on web and allow you to share navigation logic with native apps. The views currently bundled in `react-navigation` currently only work on React Native, but that may change with future-facing projects like [react-primitives](https://github.com/lelandrichardson/react-primitives).
## Example App
[This website](https://github.com/reactjs/react-navigation/blob/master/website/) is built with react navigation, specifically using `createNavigator` and `TabRouter`.
See the source code of the site here: [App.js](https://github.com/reactjs/react-navigation/blob/master/website/src/App.js).
To see how the app gets rendered on the server, see [Server.js](https://github.com/reactjs/react-navigation/blob/master/website/src/Server.js). On the browser, the App wakes up and gets rendered with [BrowserAppContainer.js](https://github.com/reactjs/react-navigation/blob/master/website/src/BrowserAppContainer.js).
## More Coming Soon
Soon this guide will be replaced with a more thorough walkthrough of react-navigation usage on the web.

View File

@@ -0,0 +1,3 @@
{
"presets": ["react-native"]
}

View File

@@ -0,0 +1,6 @@
[android]
target = Google Inc.:Google APIs:23
[maven_repositories]
central = https://repo1.maven.org/maven2

View File

@@ -0,0 +1,44 @@
[ignore]
; We fork some components by platform
.*/*[.]android.js
; Ignore "BUCK" generated dirs
<PROJECT_ROOT>/\.buckd/
; Ignore unexpected extra "@providesModule"
.*/node_modules/.*/node_modules/fbjs/.*
; Ignore duplicate module providers
; For RN Apps installed via npm, "Libraries" folder is inside
; "node_modules/react-native" but in the source repo it is in the root
.*/Libraries/react-native/React.js
.*/Libraries/react-native/ReactNative.js
[include]
[libs]
node_modules/react-native/Libraries/react-native/react-native-interface.js
node_modules/react-native/flow
flow/
[options]
module.system=haste
experimental.strict_type_args=true
munge_underscores=true
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FixMe
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-6]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(3[0-6]\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
unsafe.enable_getters_and_setters=true
[version]
^0.36.0

1
examples/HelloHybrid/.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
*.pbxproj -text

53
examples/HelloHybrid/.gitignore vendored Normal file
View File

@@ -0,0 +1,53 @@
# OSX
#
.DS_Store
# Xcode
#
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
project.xcworkspace
# Android/IntelliJ
#
build/
.idea
.gradle
local.properties
*.iml
# node.js
#
node_modules/
npm-debug.log
# BUCK
buck-out/
\.buckd/
android/app/libs
*.keystore
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1,117 @@
/**
* @flow
*/
import React, { Component } from 'react';
import {
NativeModules,
} from 'react-native';
import {
addNavigationHelpers,
} from 'react-navigation';
const {
HybridNavigationManager,
} = NativeModules;
const HybridContainer = (ReactScreens) => {
class HybridAppScreen extends Component {
constructor(props) {
super(props);
const { name, params, rootTag } = props;
let ScreenView = ReactScreens[name];
let screenKey = name;
let navState = null;
const action = { type: 'Navigate', routeName: name, params };
if (!ScreenView) {
// Deep linking magic here. Try each screen to see if the state changes
// in response to this action. The first screen who returns
// a new state for the action is used
Object.keys(ReactScreens).forEach(screenId => {
if (!ScreenView) {
const V = ReactScreens[screenId];
if (!V || !V.router || !V.router.getStateForAction) {
return;
}
const baseState = V.router.getStateForAction({ type: 'Init' });
const linkedState = V.router.getStateForAction(action, baseState);
if (baseState !== linkedState) {
ScreenView = V;
navState = linkedState;
screenKey = screenId;
}
}
});
}
if (!ScreenView) {
screenKey = 'NotFoundComponent';
ScreenView = ReactScreens.NotFoundComponent;
}
if (!navState) {
const router = ScreenView.router;
navState = router && router.getStateForAction(action);
}
let title = null;
const defaultNavState = { routeName: name, key: `screen-${rootTag}`, params };
if (navState) {
const { routes, index } = navState;
const router = ScreenView.router;
const routeConfig = router && router.getScreenConfig({
state: routes[index], dispatch: () => {}
}, index, true);
title = routeConfig && routeConfig.title;
}
this.state = {
navState: navState || defaultNavState,
screenKey,
};
HybridNavigationManager.setTitle(rootTag, title || name);
}
dispatch = (action) => {
const { name, rootTag } = this.props;
const ScreenView = ReactScreens[this.state.screenKey] || ReactScreens.NotFoundComponent;
const router = ScreenView.router;
const navState = router && router.getStateForAction(action, this.state.navState);
if (navState && navState !== this.state.navState) {
this.setState({ navState });
return true;
}
if (action.type === 'Navigate') {
HybridNavigationManager.navigate(action.routeName, action.params || {});
}
return true;
}
render() {
const Component = ReactScreens[this.state.screenKey] || ReactScreens.NotFoundComponent;
const navigation = addNavigationHelpers({
state: this.state.navState,
dispatch: this.dispatch,
});
return (
<Component navigation={navigation} />
);
}
componentWillUpdate(props, state) {
const { name, rootTag } = props;
const ScreenView = ReactScreens[name];
if (!ScreenView) {
console.log('Experiencing an error! Fix me!')
}
let title = null;
if (state.navState) {
const { routes, index } = state.navState;
const router = ScreenView.router;
const routeConfig = router && router.getScreenConfig({
state: routes[index], dispatch: () => {}
}, index, true);
title = (routeConfig && routeConfig.title) || route.routeName;
}
HybridNavigationManager.setTitle(rootTag, title || name);
}
}
return HybridAppScreen;
};
export default HybridContainer;

View File

@@ -0,0 +1,17 @@
# Hello Hybrid Example
A reference implementation for integrating react-navigation into a native app.
iOS only right now. Help needed to support Android!
## Setup:
```
cd react-navigation
npm install
cd examples/HelloHybrid
npm install
open ios/HelloHybrid.xcodeproj
cd ../..
react-native start
```

View File

@@ -0,0 +1,12 @@
import 'react-native';
import React from 'react';
import Index from '../index.android.js';
// Note: test renderer must be required after react-native.
import renderer from 'react-test-renderer';
it('renders correctly', () => {
const tree = renderer.create(
<Index />
);
});

View File

@@ -0,0 +1,12 @@
import 'react-native';
import React from 'react';
import Index from '../index.ios.js';
// Note: test renderer must be required after react-native.
import renderer from 'react-test-renderer';
it('renders correctly', () => {
const tree = renderer.create(
<Index />
);
});

View File

@@ -0,0 +1,66 @@
import re
# To learn about Buck see [Docs](https://buckbuild.com/).
# To run your application with Buck:
# - install Buck
# - `npm start` - to start the packager
# - `cd android`
# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
# - `buck install -r android/app` - compile, install and run application
#
lib_deps = []
for jarfile in glob(['libs/*.jar']):
name = 'jars__' + re.sub(r'^.*/([^/]+)\.jar$', r'\1', jarfile)
lib_deps.append(':' + name)
prebuilt_jar(
name = name,
binary_jar = jarfile,
)
for aarfile in glob(['libs/*.aar']):
name = 'aars__' + re.sub(r'^.*/([^/]+)\.aar$', r'\1', aarfile)
lib_deps.append(':' + name)
android_prebuilt_aar(
name = name,
aar = aarfile,
)
android_library(
name = 'all-libs',
exported_deps = lib_deps
)
android_library(
name = 'app-code',
srcs = glob([
'src/main/java/**/*.java',
]),
deps = [
':all-libs',
':build_config',
':res',
],
)
android_build_config(
name = 'build_config',
package = 'com.hellohybrid',
)
android_resource(
name = 'res',
res = 'src/main/res',
package = 'com.hellohybrid',
)
android_binary(
name = 'app',
package_type = 'debug',
manifest = 'src/main/AndroidManifest.xml',
keystore = '//android/keystores:debug',
deps = [
':app-code',
],
)

View File

@@ -0,0 +1,139 @@
apply plugin: "com.android.application"
import com.android.build.OutputFile
/**
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
* and bundleReleaseJsAndAssets).
* These basically call `react-native bundle` with the correct arguments during the Android build
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
* bundle directly from the development server. Below you can see all the possible configurations
* and their defaults. If you decide to add a configuration block, make sure to add it before the
* `apply from: "../../node_modules/react-native/react.gradle"` line.
*
* project.ext.react = [
* // the name of the generated asset file containing your JS bundle
* bundleAssetName: "index.android.bundle",
*
* // the entry file for bundle generation
* entryFile: "index.android.js",
*
* // whether to bundle JS and assets in debug mode
* bundleInDebug: false,
*
* // whether to bundle JS and assets in release mode
* bundleInRelease: true,
*
* // whether to bundle JS and assets in another build variant (if configured).
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
* // The configuration property can be in the following formats
* // 'bundleIn${productFlavor}${buildType}'
* // 'bundleIn${buildType}'
* // bundleInFreeDebug: true,
* // bundleInPaidRelease: true,
* // bundleInBeta: true,
*
* // the root of your project, i.e. where "package.json" lives
* root: "../../",
*
* // where to put the JS bundle asset in debug mode
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
*
* // where to put the JS bundle asset in release mode
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in debug mode
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in release mode
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
*
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
* // for example, you might want to remove it from here.
* inputExcludes: ["android/**", "ios/**"],
*
* // override which node gets called and with what additional arguments
* nodeExecutableAndArgs: ["node"]
*
* // supply additional arguments to the packager
* extraPackagerArgs: []
* ]
*/
apply from: "../../node_modules/react-native/react.gradle"
/**
* Set this to true to create two separate APKs instead of one:
* - An APK that only works on ARM devices
* - An APK that only works on x86 devices
* The advantage is the size of the APK is reduced by about 4MB.
* Upload all the APKs to the Play Store and people will download
* the correct one based on the CPU architecture of their device.
*/
def enableSeparateBuildPerCPUArchitecture = false
/**
* Run Proguard to shrink the Java bytecode in release builds.
*/
def enableProguardInReleaseBuilds = false
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
applicationId "com.hellohybrid"
minSdkVersion 16
targetSdkVersion 22
versionCode 1
versionName "1.0"
ndk {
abiFilters "armeabi-v7a", "x86"
}
}
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
include "armeabi-v7a", "x86"
}
}
buildTypes {
release {
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
// applicationVariants are e.g. debug, release
applicationVariants.all { variant ->
variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here:
// http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits
def versionCodes = ["armeabi-v7a":1, "x86":2]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
}
}
}
}
dependencies {
compile fileTree(dir: "libs", include: ["*.jar"])
compile "com.android.support:appcompat-v7:23.0.1"
compile "com.facebook.react:react-native:+" // From node_modules
}
// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) {
from configurations.compile
into 'libs'
}

View File

@@ -0,0 +1,66 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Disabling obfuscation is useful if you collect stack traces from production crashes
# (unless you are using a system that supports de-obfuscate the stack traces).
-dontobfuscate
# React Native
# Keep our interfaces so they can be used by other ProGuard rules.
# See http://sourceforge.net/p/proguard/bugs/466/
-keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip
-keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters
-keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip
# Do not strip any method/class that is annotated with @DoNotStrip
-keep @com.facebook.proguard.annotations.DoNotStrip class *
-keep @com.facebook.common.internal.DoNotStrip class *
-keepclassmembers class * {
@com.facebook.proguard.annotations.DoNotStrip *;
@com.facebook.common.internal.DoNotStrip *;
}
-keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * {
void set*(***);
*** get*();
}
-keep class * extends com.facebook.react.bridge.JavaScriptModule { *; }
-keep class * extends com.facebook.react.bridge.NativeModule { *; }
-keepclassmembers,includedescriptorclasses class * { native <methods>; }
-keepclassmembers class * { @com.facebook.react.uimanager.UIProp <fields>; }
-keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp <methods>; }
-keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup <methods>; }
-dontwarn com.facebook.react.**
# okhttp
-keepattributes Signature
-keepattributes *Annotation*
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-dontwarn okhttp3.**
# okio
-keep class sun.misc.Unsafe { *; }
-dontwarn java.nio.file.*
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
-dontwarn okio.**

View File

@@ -0,0 +1,31 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.hellohybrid"
android:versionCode="1"
android:versionName="1.0">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="22" />
<application
android:name=".MainApplication"
android:allowBackup="true"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application>
</manifest>

View File

@@ -0,0 +1,15 @@
package com.hellohybrid;
import com.facebook.react.ReactActivity;
public class MainActivity extends ReactActivity {
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "HelloHybrid";
}
}

View File

@@ -0,0 +1,42 @@
package com.hellohybrid;
import android.app.Application;
import android.util.Log;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
import java.util.Arrays;
import java.util.List;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
protected boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage()
);
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@@ -0,0 +1,3 @@
<resources>
<string name="app_name">HelloHybrid</string>
</resources>

View File

@@ -0,0 +1,8 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
</style>
</resources>

View File

@@ -0,0 +1,24 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
mavenLocal()
jcenter()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
}
}
}

View File

@@ -0,0 +1,20 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
android.useDeprecatedNdk=true

Binary file not shown.

View File

@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip

164
examples/HelloHybrid/android/gradlew vendored Executable file
View File

@@ -0,0 +1,164 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

View File

@@ -0,0 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -0,0 +1,8 @@
keystore(
name = 'debug',
store = 'debug.keystore',
properties = 'debug.keystore.properties',
visibility = [
'PUBLIC',
],
)

View File

@@ -0,0 +1,4 @@
key.store=debug.keystore
key.alias=androiddebugkey
key.store.password=android
key.alias.password=android

View File

@@ -0,0 +1,3 @@
rootProject.name = 'HelloHybrid'
include ':app'

View File

@@ -0,0 +1,53 @@
/**
* Sample React Native App
* https://github.com/facebook/react-native
* @flow
*/
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
} from 'react-native';
export default class HelloHybrid extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text style={styles.instructions}>
To get started, edit index.android.js
</Text>
<Text style={styles.instructions}>
Double tap R on your keyboard to reload,{'\n'}
Shake or press menu button for dev menu
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
AppRegistry.registerComponent('HelloHybrid', () => HelloHybrid);

View File

@@ -0,0 +1,43 @@
/**
* @flow
*/
import React from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
} from 'react-native';
import HybridContainer from './HybridContainer';
const NotFoundComponent = () => (
<View style={styles.container}>
<Text style={styles.welcome}>
Screen not found!
</Text>
</View>
);
const HelloHybrid = HybridContainer({
Settings: require('./screens/Settings'),
Story: require('./screens/Story'),
NotFoundComponent,
});
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
AppRegistry.registerComponent('HelloHybrid', () => HelloHybrid);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,129 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0620"
version = "1.3">
<BuildAction
parallelizeBuildables = "NO"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "83CBBA2D1A601D0E00E9B192"
BuildableName = "libReact.a"
BlueprintName = "React"
ReferencedContainer = "container:../node_modules/react-native/React/React.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "HelloHybrid.app"
BlueprintName = "HelloHybrid"
ReferencedContainer = "container:HelloHybrid.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
BuildableName = "HelloHybridTests.xctest"
BlueprintName = "HelloHybridTests"
ReferencedContainer = "container:HelloHybrid.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
BuildableName = "HelloHybridTests.xctest"
BlueprintName = "HelloHybridTests"
ReferencedContainer = "container:HelloHybrid.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "HelloHybrid.app"
BlueprintName = "HelloHybrid"
ReferencedContainer = "container:HelloHybrid.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "HelloHybrid.app"
BlueprintName = "HelloHybrid"
ReferencedContainer = "container:HelloHybrid.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "HelloHybrid.app"
BlueprintName = "HelloHybrid"
ReferencedContainer = "container:HelloHybrid.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -0,0 +1,13 @@
#import <UIKit/UIKit.h>
#import <React/RCTRootView.h>
#import "AppNavigationDelegate.h"
@interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate, AppNavigationDelegate>
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) UIViewController *rootController;
@property (strong, nonatomic) UINavigationController *nav;
@property (strong, nonatomic) RCTBridge *bridge;
@end

View File

@@ -0,0 +1,54 @@
#import "AppDelegate.h"
#import "HomeViewController.h"
#import "ProfileViewController.h"
#import "ReactViewController.h"
#import "HybridNavigationManager.h"
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
_bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions: launchOptions];
_window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
_rootController = [[HomeViewController alloc] initWithDelegate:self];
_nav = [[UINavigationController alloc] initWithRootViewController:self.rootController];
[_window addSubview:_nav.view];
[_window setRootViewController:_nav];
[_window makeKeyAndVisible];
return YES;
}
#pragma mark AppNavigationDelegate
- (void)openViewWithName:(NSString *)name andParams:(NSDictionary *)params
{
if ([name isEqualToString:@"Profile"]) {
ProfileViewController *profileView = [[ProfileViewController alloc] initWithDelegate: self];
[_nav pushViewController:profileView animated:true];
return;
}
ReactViewController *reactView = [[ReactViewController alloc] initWithDelegate:self bridge:_bridge viewName:name viewParams:params];
[_nav pushViewController:reactView animated:true];
}
#pragma mark RCTBridgeDelegate
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"examples/HelloHybrid/index.ios" fallbackResource:nil];
}
- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge;
{
return @[
[[HybridNavigationManager alloc] initWithBridge:_bridge navigationDelegate:self],
];
}
@end

View File

@@ -0,0 +1,9 @@
#import <UIKit/UIKit.h>
@protocol AppNavigationDelegate <NSObject>
- (void)openViewWithName:(NSString *)name andParams:(NSDictionary *)params;
@optional
@end

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7702" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7701"/>
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Powered by React Native" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
<rect key="frame" x="20" y="439" width="441" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="HelloHybrid" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
<rect key="frame" x="20" y="140" width="441" height="43"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
<constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
<constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
<constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="548" y="455"/>
</view>
</objects>
</document>

View File

@@ -0,0 +1,12 @@
#import <UIKit/UIKit.h>
#import "AppNavigationDelegate.h"
@interface HomeViewController : UIViewController {
id<AppNavigationDelegate> navigation;
}
- (id) initWithDelegate:(id<AppNavigationDelegate>)delegate;
@end

View File

@@ -0,0 +1,57 @@
#import "HomeViewController.h"
@interface HomeViewController ()
@end
@implementation HomeViewController
- (id)initWithDelegate:(id<AppNavigationDelegate>)delegate;
{
self = [super init];
if (self) {
navigation = delegate;
}
return self;
}
- (void)loadView {
CGRect rect = [UIScreen mainScreen].bounds;
self.view = [[UIView alloc] initWithFrame:rect];
self.view.backgroundColor = [UIColor whiteColor];
self.navigationItem.title = @"Home Screen";
UILabel *labelView = [[UILabel alloc] initWithFrame:CGRectMake(5,80,self.view.frame.size.width,50)];
labelView.text = @"This screen is native";
labelView.textAlignment = NSTextAlignmentCenter;
labelView.textColor = [UIColor blackColor];
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button addTarget:self
action:@selector(openProfile:)
forControlEvents:UIControlEventTouchUpInside];
[button setTitle:@"Open Profile" forState:UIControlStateNormal];
button.frame = CGRectMake(10.0, 120.0, self.view.frame.size.width, 40.0);
UIButton *button2 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button2 addTarget:self
action:@selector(openSettings:)
forControlEvents:UIControlEventTouchUpInside];
[button2 setTitle:@"Open Settings" forState:UIControlStateNormal];
button2.frame = CGRectMake(10.0, 160.0, self.view.frame.size.width, 40.0);
[self.view addSubview:button];
[self.view addSubview:button2];
[self.view addSubview:labelView];
}
- (void) openProfile:(UIButton*)sender
{
[navigation openViewWithName:@"Profile" andParams:@{}];
}
- (void) openSettings:(UIButton*)sender
{
[navigation openViewWithName:@"Settings" andParams:@{}];
}
@end

View File

@@ -0,0 +1,9 @@
#import <React/RCTBridgeModule.h>
#import "AppNavigationDelegate.h"
@interface HybridNavigationManager : NSObject <RCTBridgeModule>
- (id)initWithBridge:(RCTBridge *)bridge navigationDelegate:(id<AppNavigationDelegate>)delegate;
@end

View File

@@ -0,0 +1,42 @@
#import "AppNavigationDelegate.h"
#import "HybridNavigationManager.h"
#import <React/RCTLog.h>
#import <React/RCTRootView.h>
#import <React/RCTUIManager.h>
@implementation HybridNavigationManager {
__weak id<AppNavigationDelegate> _delegate;
}
@synthesize bridge = _bridge;
- (id)initWithBridge:(RCTBridge *)bridge navigationDelegate:(id<AppNavigationDelegate>)delegate;
{
if (self = [super init]) {
_bridge = bridge;
_delegate = delegate;
}
return self;
}
RCT_EXPORT_MODULE();
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
RCT_EXPORT_METHOD(navigate:(NSString *)name params:(NSDictionary *)params)
{
[_delegate openViewWithName:name andParams:params];
}
RCT_EXPORT_METHOD(setTitle:(nonnull NSNumber *)rootTag title:(nonnull NSString *)title)
{
RCTRootView *rootView = (RCTRootView *)[_bridge.uiManager viewForReactTag: rootTag];
rootView.reactViewController.navigationItem.title = title;
}
@end

View File

@@ -0,0 +1,48 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>NSLocationWhenInUseUsageDescription</key>
<string></string>
<key>NSAppTransportSecurity</key>
<!--See http://ste.vn/2015/06/10/configuring-app-transport-security-ios-9-osx-10-11/ -->
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
</dict>
</plist>

View File

@@ -0,0 +1,12 @@
#import <UIKit/UIKit.h>
#import "AppNavigationDelegate.h"
@interface ProfileViewController : UIViewController {
id<AppNavigationDelegate> navigation;
}
- (id) initWithDelegate:(id<AppNavigationDelegate>)delegate;
@end

View File

@@ -0,0 +1,70 @@
#import "ProfileViewController.h"
@interface ProfileViewController ()
@end
@implementation ProfileViewController
- (id)initWithDelegate:(id<AppNavigationDelegate>)delegate;
{
self = [super init];
if (self) {
navigation = delegate;
}
return self;
}
- (void)loadView {
self.view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.view.backgroundColor = [UIColor whiteColor];
self.navigationItem.title = @"Profile Screen";
UILabel *labelView = [[UILabel alloc] initWithFrame:CGRectMake(5,80,self.view.frame.size.width,50)];
labelView.text = @"This screen is native";
labelView.textAlignment = NSTextAlignmentCenter;
labelView.textColor = [UIColor blackColor];
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button addTarget:self
action:@selector(openStory:)
forControlEvents:UIControlEventTouchUpInside];
[button setTitle:@"Open Story" forState:UIControlStateNormal];
button.frame = CGRectMake(10.0, 120.0, self.view.frame.size.width, 40.0);
UIButton *button2 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button2 addTarget:self
action:@selector(openSettings:)
forControlEvents:UIControlEventTouchUpInside];
[button2 setTitle:@"Open Settings" forState:UIControlStateNormal];
button2.frame = CGRectMake(10.0, 160.0, self.view.frame.size.width, 40.0);
UIButton *button3 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button3 addTarget:self
action:@selector(openAdvancedSettings:)
forControlEvents:UIControlEventTouchUpInside];
[button3 setTitle:@"Open Advanced Settings" forState:UIControlStateNormal];
button3.frame = CGRectMake(10.0, 200.0, self.view.frame.size.width, 40.0);
[self.view addSubview:button];
[self.view addSubview:button2];
[self.view addSubview:button3];
[self.view addSubview:labelView];
}
- (void)openStory:(UIButton*)sender
{
[navigation openViewWithName:@"Story" andParams:@{ @"id": @"4242" }];
}
- (void)openSettings:(UIButton*)sender
{
[navigation openViewWithName:@"Settings" andParams:@{}];
}
- (void)openAdvancedSettings:(UIButton*)sender
{
[navigation openViewWithName:@"AdvancedSettings" andParams:@{}];
}
@end

View File

@@ -0,0 +1,16 @@
#import <UIKit/UIKit.h>
#import "AppNavigationDelegate.h"
#import <React/RCTRootView.h>
@interface ReactViewController : UIViewController {
id<AppNavigationDelegate> navigation;
NSString* name;
NSDictionary* params;
RCTBridge* bridge;
}
- (id)initWithDelegate:(id<AppNavigationDelegate>)delegate bridge:(RCTBridge *)bridge viewName:(NSString *)name viewParams:(NSDictionary *)params;
@end

View File

@@ -0,0 +1,34 @@
#import "ReactViewController.h"
#import <React/RCTRootView.h>
@interface ReactViewController ()
@end
@implementation ReactViewController
- (id)initWithDelegate:(id<AppNavigationDelegate>)delegate bridge:(RCTBridge *)inBridge viewName:(NSString *)inName viewParams:(NSDictionary *)inParams
{
self = [super init];
if (self) {
navigation = delegate;
name = inName;
params = inParams;
bridge = inBridge;
}
return self;
}
- (void)loadView {
self.navigationItem.title = @"";
self.view = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"HelloHybrid" initialProperties: @{ @"name": name, @"params": params }];
self.view.backgroundColor = [UIColor whiteColor];
}
- (void)requestClose:(UIButton*)sender
{
[self dismissViewControllerAnimated:true completion:^{}];
}
@end

View File

@@ -0,0 +1,10 @@
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

View File

@@ -0,0 +1,62 @@
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
#import <React/RCTLog.h>
#import <React/RCTRootView.h>
#define TIMEOUT_SECONDS 600
#define TEXT_TO_LOOK_FOR @"Welcome to React Native!"
@interface HelloHybridTests : XCTestCase
@end
@implementation HelloHybridTests
- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test
{
if (test(view)) {
return YES;
}
for (UIView *subview in [view subviews]) {
if ([self findSubviewInView:subview matching:test]) {
return YES;
}
}
return NO;
}
- (void)testRendersWelcomeScreen
{
UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
BOOL foundElement = NO;
__block NSString *redboxError = nil;
RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
if (level >= RCTLogLevelError) {
redboxError = message;
}
});
while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
[[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
[[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) {
if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
return YES;
}
return NO;
}];
}
RCTSetLogFunction(RCTDefaultLogFunction);
XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
}
@end

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@@ -0,0 +1,25 @@
{
"name": "HelloHybrid",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"test": "jest"
},
"dependencies": {
"react": "^15.4.2",
"react-native": "^0.40.0",
"react-navigation": "file:../.."
},
"jest": {
"preset": "jest-react-native"
},
"devDependencies": {
"babel-jest": "16.0.0",
"babel-preset-react-native": "1.9.0",
"jest": "16.0.2",
"jest-react-native": "16.0.0",
"react-test-renderer": "15.3.2",
"whatwg-fetch": "1.0.0"
}
}

View File

@@ -0,0 +1,83 @@
import React, { Component } from 'react';
import {
SegmentedControlIOS,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
import {
TabNavigator,
} from 'react-navigation';
const BasicSettingsScreen = () => (
<Text style={styles.welcome}>
Settings, Built in React!
</Text>
);
const AdvancedSettingsScreen = () => (
<Text style={styles.welcome}>
Advanced settings - also React!
</Text>
);
const TabView = ({children, navigation, tabs}) => (
<View style={styles.container}>
<SegmentedControlIOS
style={styles.tabBar}
values={tabs.map(tab => tab.myLabel)}
selectedIndex={navigation.state.index}
onChange={({nativeEvent}) => {
navigation.dispatch({
type: tabs[nativeEvent.selectedSegmentIndex].key,
});
}}
/>
{children}
</View>
);
const SettingsScreen = TabNavigator({
Settings: {
screen: BasicSettingsScreen,
navigationOptions: {
title: () => 'Settings',
},
},
AdvancedSettings: {
screen: AdvancedSettingsScreen,
navigationOptions: {
title: () => 'Advanced Settings',
},
},
});
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
paddingTop: 80,
},
tabBar: {
alignSelf: 'stretch',
height: 40,
margin: 10,
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
link: {
textAlign: 'center',
color: '#0A5FFF',
fontSize: 16,
marginVertical: 10,
},
});
module.exports = SettingsScreen;

View File

@@ -0,0 +1,59 @@
import React, { Component } from 'react';
import {
StyleSheet,
ScrollView,
Text,
TouchableOpacity,
View,
} from 'react-native';
const Story = ({navigation}) => (
<View style={styles.container}>
<ScrollView>
<Text style={styles.welcome}>
React Screen. Story ID: {JSON.stringify(navigation.state)}
</Text>
<TouchableOpacity
onPress={() => {
navigation.navigate('Profile', {id: '9876'});
}}>
<Text style={styles.link}>Navigate to native profile</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
navigation.navigate('Settings');
}}>
<Text style={styles.link}>Navigate to react settings</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
navigation.navigate('RandomLink');
}}>
<Text style={styles.link}>Navigate to unimpemented page</Text>
</TouchableOpacity>
</ScrollView>
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
marginTop: 80,
},
link: {
textAlign: 'center',
color: '#0A5FFF',
fontSize: 16,
marginVertical: 10,
},
});
module.exports = Story;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
{
"presets": ["react-native"]
}

View File

@@ -0,0 +1,6 @@
[android]
target = Google Inc.:Google APIs:23
[maven_repositories]
central = https://repo1.maven.org/maven2

View File

@@ -0,0 +1,44 @@
[ignore]
; We fork some components by platform
.*/*[.]android.js
; Ignore "BUCK" generated dirs
<PROJECT_ROOT>/\.buckd/
; Ignore unexpected extra "@providesModule"
.*/node_modules/.*/node_modules/fbjs/.*
; Ignore duplicate module providers
; For RN Apps installed via npm, "Libraries" folder is inside
; "node_modules/react-native" but in the source repo it is in the root
.*/Libraries/react-native/React.js
.*/Libraries/react-native/ReactNative.js
[include]
[libs]
node_modules/react-native/Libraries/react-native/react-native-interface.js
node_modules/react-native/flow
flow/
[options]
module.system=haste
experimental.strict_type_args=true
munge_underscores=true
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FixMe
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-6]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(3[0-6]\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
unsafe.enable_getters_and_setters=true
[version]
^0.36.0

View File

@@ -0,0 +1 @@
*.pbxproj -text

53
examples/LinkingExample/.gitignore vendored Normal file
View File

@@ -0,0 +1,53 @@
# OSX
#
.DS_Store
# Xcode
#
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
project.xcworkspace
# Android/IntelliJ
#
build/
.idea
.gradle
local.properties
*.iml
# node.js
#
node_modules/
npm-debug.log
# BUCK
buck-out/
\.buckd/
android/app/libs
*.keystore
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1,48 @@
import React, { Component } from 'react';
import {
StyleSheet,
Text,
Linking,
View,
} from 'react-native';
export default class LinkingExample extends Component {
state = { initUrl: null };
async componentDidMount() {
const initUrl = await Linking.getInitialURL();
this.setState({ initUrl });
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
{this.state.initUrl}
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});

View File

@@ -0,0 +1,12 @@
import 'react-native';
import React from 'react';
import Index from '../index.android.js';
// Note: test renderer must be required after react-native.
import renderer from 'react-test-renderer';
it('renders correctly', () => {
const tree = renderer.create(
<Index />
);
});

View File

@@ -0,0 +1,12 @@
import 'react-native';
import React from 'react';
import Index from '../index.ios.js';
// Note: test renderer must be required after react-native.
import renderer from 'react-test-renderer';
it('renders correctly', () => {
const tree = renderer.create(
<Index />
);
});

View File

@@ -0,0 +1,66 @@
import re
# To learn about Buck see [Docs](https://buckbuild.com/).
# To run your application with Buck:
# - install Buck
# - `npm start` - to start the packager
# - `cd android`
# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
# - `buck install -r android/app` - compile, install and run application
#
lib_deps = []
for jarfile in glob(['libs/*.jar']):
name = 'jars__' + re.sub(r'^.*/([^/]+)\.jar$', r'\1', jarfile)
lib_deps.append(':' + name)
prebuilt_jar(
name = name,
binary_jar = jarfile,
)
for aarfile in glob(['libs/*.aar']):
name = 'aars__' + re.sub(r'^.*/([^/]+)\.aar$', r'\1', aarfile)
lib_deps.append(':' + name)
android_prebuilt_aar(
name = name,
aar = aarfile,
)
android_library(
name = 'all-libs',
exported_deps = lib_deps
)
android_library(
name = 'app-code',
srcs = glob([
'src/main/java/**/*.java',
]),
deps = [
':all-libs',
':build_config',
':res',
],
)
android_build_config(
name = 'build_config',
package = 'com.linkingexample',
)
android_resource(
name = 'res',
res = 'src/main/res',
package = 'com.linkingexample',
)
android_binary(
name = 'app',
package_type = 'debug',
manifest = 'src/main/AndroidManifest.xml',
keystore = '//android/keystores:debug',
deps = [
':app-code',
],
)

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