diff --git a/Examples/UIExplorer/NavigationExperimental/NavigationAnimatedExample.js b/Examples/UIExplorer/NavigationExperimental/NavigationAnimatedExample.js index 705f7d6be432eb..b4bd9bc43526c8 100644 --- a/Examples/UIExplorer/NavigationExperimental/NavigationAnimatedExample.js +++ b/Examples/UIExplorer/NavigationExperimental/NavigationAnimatedExample.js @@ -99,8 +99,10 @@ class NavigationAnimatedExample extends React.Component { _renderHeader(/*NavigationSceneRendererProps*/ props) { return ( state.key} + navigationProps={props} + renderTitleComponent={(navigationProps, scene) => { + return {scene.navigationState.key}; + }} /> ); } @@ -144,7 +146,7 @@ const styles = StyleSheet.create({ flex: 1, }, scrollView: { - marginTop: 64 + marginTop: NavigationHeader.HEIGHT, }, }); diff --git a/Examples/UIExplorer/NavigationExperimental/NavigationCompositionExample.js b/Examples/UIExplorer/NavigationExperimental/NavigationCompositionExample.js index 084dc1f39b0fe7..78876c76cf2814 100644 --- a/Examples/UIExplorer/NavigationExperimental/NavigationCompositionExample.js +++ b/Examples/UIExplorer/NavigationExperimental/NavigationCompositionExample.js @@ -27,7 +27,6 @@ const { } = React; const { - AnimatedView: NavigationAnimatedView, CardStack: NavigationCardStack, Container: NavigationContainer, Header: NavigationHeader, @@ -178,8 +177,10 @@ class ExampleTabScreen extends React.Component { _renderHeader(props: NavigationSceneRendererProps) { return ( stateTypeTitleMap(state)} + navigationProps={props} + renderTitleComponent={(navigationProps, scene) => { + return {stateTypeTitleMap(scene.navigationState)}; + }} /> ); } @@ -284,7 +285,7 @@ const styles = StyleSheet.create({ flex: 1, }, scrollView: { - marginTop: 64 + marginTop: NavigationHeader.HEIGHT }, tabContent: { flex: 1, diff --git a/Examples/UIExplorer/UIExplorerApp.ios.js b/Examples/UIExplorer/UIExplorerApp.ios.js index 384ea1c18c73d7..d098a99cbd617c 100644 --- a/Examples/UIExplorer/UIExplorerApp.ios.js +++ b/Examples/UIExplorer/UIExplorerApp.ios.js @@ -25,25 +25,19 @@ const UIExplorerStateTitleMap = require('./UIExplorerStateTitleMap'); const { Alert, - Animated, AppRegistry, NavigationExperimental, SnapshotViewIOS, StyleSheet, - Text, - TouchableHighlight, View, } = React; const { CardStack: NavigationCardStack, Header: NavigationHeader, - Reducer: NavigationReducer, RootContainer: NavigationRootContainer, } = NavigationExperimental; -import type { Value } from 'Animated'; - import type { NavigationSceneRendererProps } from 'NavigationTypeDefinition'; import type { UIExplorerNavigationState } from './UIExplorerNavigationReducer'; @@ -127,9 +121,11 @@ class UIExplorerApp extends React.Component { _renderOverlay(props: NavigationSceneRendererProps): ReactElement { return ( { + return {UIExplorerStateTitleMap(scene.navigationState)}; + }} /> ); } @@ -165,7 +161,7 @@ const styles = StyleSheet.create({ }, exampleContainer: { flex: 1, - paddingTop: 60, + paddingTop: NavigationHeader.HEIGHT, }, }); diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationHeader.js b/Libraries/CustomComponents/NavigationExperimental/NavigationHeader.js index 3b1c5aa4dfce4b..1dd497b434464f 100644 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationHeader.js +++ b/Libraries/CustomComponents/NavigationExperimental/NavigationHeader.js @@ -27,136 +27,230 @@ */ 'use strict'; -const Animated = require('Animated'); -const Image = require('Image'); +const React = require('react-native'); const NavigationContainer = require('NavigationContainer'); +const NavigationHeaderTitle = require('NavigationHeaderTitle'); +const NavigationHeaderBackButton = require('NavigationHeaderBackButton'); const NavigationPropTypes = require('NavigationPropTypes'); -const NavigationRootContainer = require('NavigationRootContainer'); -const React = require('react-native'); -const StyleSheet = require('StyleSheet'); -const Text = require('Text'); -const TouchableOpacity = require('TouchableOpacity'); -const View = require('View'); + +const { + Animated, + Platform, + StyleSheet, + View, +} = React; import type { - NavigationState, NavigationSceneRendererProps, + NavigationScene, } from 'NavigationTypeDefinition'; -type Props = NavigationSceneRendererProps & { - getTitle: (navState: NavigationState) => string, +type Renderer = (props: NavigationSceneRendererProps, scene: NavigationScene) => ?ReactElement; + +type DefaultProps = { + renderTitleComponent: Renderer; + renderLeftComponent: Renderer; }; -const {PropTypes} = React; +type Props = { + navigationProps: NavigationSceneRendererProps; + renderTitleComponent: Renderer; + renderLeftComponent: Renderer; + renderRightComponent: Renderer; + style?: any; +} -const NavigationHeaderPropTypes = { - ...NavigationPropTypes.SceneRenderer, - getTitle: PropTypes.func.isRequired, -}; +const APPBAR_HEIGHT = Platform.OS === 'ios' ? 44 : 56; +const STATUSBAR_HEIGHT = Platform.OS === 'ios' ? 20 : 0; -class NavigationHeader extends React.Component { - _handleBackPress: Function; +class NavigationHeader extends React.Component { + static defaultProps = { + renderTitleComponent: (props, scene) => { + const pageState = scene.navigationState; - props: Props; + return {pageState.title ? pageState.title : ''}; + }, + renderLeftComponent: (props, scene) => scene.index !== 0 ? : null + }; - componentWillMount(): void { - this._handleBackPress = this._handleBackPress.bind(this); + static propTypes = { + navigationProps: React.PropTypes.shape(NavigationPropTypes.SceneRenderer).isRequired, + renderTitleComponent: React.PropTypes.func, + renderLeftComponent: React.PropTypes.func, + renderRightComponent: React.PropTypes.func, + style: View.propTypes.style, + }; + + _renderLeftComponent(scene: NavigationScene) { + const { + renderLeftComponent, + navigationProps, + } = this.props; + + if (renderLeftComponent) { + const { + index, + navigationState, + } = scene; + + return ( + + {renderLeftComponent(navigationProps, scene)} + + ); + } + + return null; } - render(): ReactElement { - var state = this.props.navigationState; - return ( - - {state.children.map(this._renderTitle, this)} - {this._renderBackButton()} - - ); + _renderRightComponent(scene: NavigationScene) { + const { + renderRightComponent, + navigationProps, + } = this.props; + + if (renderRightComponent) { + const { + index, + navigationState, + } = scene; + + return ( + + {renderRightComponent(navigationProps, scene)} + + ); + } + + return null; } - _renderBackButton(): ?ReactElement { - if (this.props.navigationState.index === 0) { - return null; + _renderTitleComponent(scene: NavigationScene) { + const { + renderTitleComponent, + navigationProps, + } = this.props; + + if (renderTitleComponent) { + const { + index, + navigationState, + } = scene; + + return ( + + {renderTitleComponent(navigationProps, scene)} + + ); } - return ( - - - - ); + + return null; } - _renderTitle(childState: NavigationState, index:number): ?ReactElement { + render() { + const { scenes } = this.props.navigationProps; + return ( - - {this.props.getTitle(childState)} - + + {scenes.map(this._renderLeftComponent, this)} + {scenes.map(this._renderTitleComponent, this)} + {scenes.map(this._renderRightComponent, this)} + ); } - - _handleBackPress(): void { - this.props.onNavigate(NavigationRootContainer.getBackAction()); - } } -NavigationHeader.propTypes = NavigationHeaderPropTypes; - -NavigationHeader = NavigationContainer.create(NavigationHeader); - const styles = StyleSheet.create({ - title: { - textAlign: 'center', - marginTop: 10, - fontSize: 18, - fontWeight: '500', - color: '#0A0A0A', + appbar: { position: 'absolute', - top: 20, + top: 0, left: 0, right: 0, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'flex-start', + backgroundColor: Platform.OS === 'ios' ? '#EFEFF2' : '#FFF', + borderBottomWidth: Platform.OS === 'ios' ? StyleSheet.hairlineWidth : 0, + borderBottomColor: 'rgba(0, 0, 0, .15)', + height: APPBAR_HEIGHT + STATUSBAR_HEIGHT, + marginBottom: 16, // This is needed for elevation shadow + elevation: 2, }, - header: { - backgroundColor: '#EFEFF2', - paddingTop: 20, - top: 0, - height: 64, - right: 0, - left: 0, - borderBottomWidth: 0.5, - borderBottomColor: '#828287', + + title: { position: 'absolute', + top: 0, + bottom: 0, + left: APPBAR_HEIGHT, + right: APPBAR_HEIGHT, + marginTop: STATUSBAR_HEIGHT, }, - backButton: { - width: 29, - height: 37, + + left: { position: 'absolute', - bottom: 4, - left: 2, - padding: 8, - }, - backButtonImage: { - width: 13, - height: 21, + top: 0, + bottom: 0, + left: 0, + marginTop: STATUSBAR_HEIGHT, }, + + right: { + position: 'absolute', + top: 0, + bottom: 0, + right: 0, + marginTop: STATUSBAR_HEIGHT, + } }); +NavigationHeader = NavigationContainer.create(NavigationHeader); + +NavigationHeader.HEIGHT = APPBAR_HEIGHT + STATUSBAR_HEIGHT; +NavigationHeader.Title = NavigationHeaderTitle; +NavigationHeader.BackButton = NavigationHeaderBackButton; + module.exports = NavigationHeader; diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationHeaderBackButton.js b/Libraries/CustomComponents/NavigationExperimental/NavigationHeaderBackButton.js new file mode 100644 index 00000000000000..4cd4a867676c9b --- /dev/null +++ b/Libraries/CustomComponents/NavigationExperimental/NavigationHeaderBackButton.js @@ -0,0 +1,59 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @providesModule NavigationHeaderBackButton + * @flow +*/ +'use strict'; + +const React = require('react-native'); +const NavigationContainer = require('NavigationContainer'); +const NavigationRootContainer = require('NavigationRootContainer'); + +const { + Image, + Platform, + StyleSheet, + TouchableOpacity, +} = React; + +type Props = { + onNavigate: Function +} + +const NavigationHeaderBackButton = (props: Props) => ( + props.onNavigate(NavigationRootContainer.getBackAction())}> + + +); + +NavigationHeaderBackButton.propTypes = { + onNavigate: React.PropTypes.func.isRequired +}; + +const styles = StyleSheet.create({ + buttonContainer: { + flex: 1, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + }, + button: { + height: 24, + width: 24, + margin: Platform.OS === 'ios' ? 10 : 16, + resizeMode: 'contain' + } +}); + +module.exports = NavigationContainer.create(NavigationHeaderBackButton); diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationHeaderTitle.js b/Libraries/CustomComponents/NavigationExperimental/NavigationHeaderTitle.js new file mode 100644 index 00000000000000..94fd4c00a8d409 --- /dev/null +++ b/Libraries/CustomComponents/NavigationExperimental/NavigationHeaderTitle.js @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2015, Facebook, Inc. All rights reserved. + * + * Facebook, Inc. ("Facebook") owns all right, title and interest, including + * all intellectual property and other proprietary rights, in and to the React + * Native CustomComponents software (the "Software"). Subject to your + * compliance with these terms, you are hereby granted a non-exclusive, + * worldwide, royalty-free copyright license to (1) use and copy the Software; + * and (2) reproduce and distribute the Software as part of your own software + * ("Your Software"). Facebook reserves all rights not expressly granted to + * you in this license agreement. + * + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @providesModule NavigationHeaderTitle + * @flow + */ +'use strict'; + +const React = require('react-native'); + +const { + Platform, + StyleSheet, + View, + Text, +} = React; + +type Props = { + children: ReactElement; + style: any; + textStyle: any; +} + +const NavigationHeaderTitle = ({ children, style, textStyle }: Props) => ( + + {children} + +); + +const styles = StyleSheet.create({ + title: { + flex: 1, + flexDirection: 'row', + alignItems: 'center', + marginHorizontal: 16 + }, + + titleText: { + flex: 1, + fontSize: 18, + fontWeight: '500', + color: 'rgba(0, 0, 0, .9)', + textAlign: Platform.OS === 'ios' ? 'center' : 'left' + } +}); + +NavigationHeaderTitle.propTypes = { + children: React.PropTypes.string.isRequired, + style: View.propTypes.style, + textStyle: Text.propTypes.style +}; + +module.exports = NavigationHeaderTitle; diff --git a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@1.5x.android.png b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@1.5x.android.png new file mode 100644 index 00000000000000..ad03a63bf3caba Binary files /dev/null and b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@1.5x.android.png differ diff --git a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@1.5x.ios.png b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@1.5x.ios.png new file mode 100644 index 00000000000000..e43fa0622f1994 Binary files /dev/null and b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@1.5x.ios.png differ diff --git a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@1x.android.png b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@1x.android.png new file mode 100644 index 00000000000000..083db295f474b9 Binary files /dev/null and b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@1x.android.png differ diff --git a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@1x.ios.png b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@1x.ios.png new file mode 100644 index 00000000000000..4244656b92fb70 Binary files /dev/null and b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@1x.ios.png differ diff --git a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@2x.android.png b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@2x.android.png new file mode 100644 index 00000000000000..6de0a1cbb365df Binary files /dev/null and b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@2x.android.png differ diff --git a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@2x.ios.png b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@2x.ios.png new file mode 100644 index 00000000000000..a8b6e9a63e5954 Binary files /dev/null and b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@2x.ios.png differ diff --git a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@3x.android.png b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@3x.android.png new file mode 100644 index 00000000000000..15a983a67d97c9 Binary files /dev/null and b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@3x.android.png differ diff --git a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@3x.ios.png b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@3x.ios.png new file mode 100644 index 00000000000000..07ea37b4e1e252 Binary files /dev/null and b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@3x.ios.png differ diff --git a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@4x.android.png b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@4x.android.png new file mode 100644 index 00000000000000..17e52e8550e566 Binary files /dev/null and b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@4x.android.png differ diff --git a/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@4x.ios.png b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@4x.ios.png new file mode 100644 index 00000000000000..7899053f61b38b Binary files /dev/null and b/Libraries/CustomComponents/NavigationExperimental/assets/back-icon@4x.ios.png differ diff --git a/Libraries/CustomComponents/NavigationExperimental/back_chevron.png b/Libraries/CustomComponents/NavigationExperimental/back_chevron.png deleted file mode 100644 index af21cf9d5718b7..00000000000000 Binary files a/Libraries/CustomComponents/NavigationExperimental/back_chevron.png and /dev/null differ