mirror of
https://github.com/status-im/react-navigation.git
synced 2025-02-24 17:18:09 +00:00
Header transition presets with support for standard iOS transition style (#3526)
Header transition presets with approximate support for UIKit transition style
This commit is contained in:
parent
5febb81a1c
commit
3c3668c952
@ -1,7 +1,7 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Constants, ScreenOrientation } from 'expo';
|
import { Asset, Constants, ScreenOrientation } from 'expo';
|
||||||
|
|
||||||
ScreenOrientation.allow(ScreenOrientation.Orientation.ALL);
|
ScreenOrientation.allow(ScreenOrientation.Orientation.ALL);
|
||||||
|
|
||||||
@ -28,13 +28,14 @@ import StacksInTabs from './StacksInTabs';
|
|||||||
import StacksOverTabs from './StacksOverTabs';
|
import StacksOverTabs from './StacksOverTabs';
|
||||||
import StacksWithKeys from './StacksWithKeys';
|
import StacksWithKeys from './StacksWithKeys';
|
||||||
import SimpleStack from './SimpleStack';
|
import SimpleStack from './SimpleStack';
|
||||||
|
import StackWithHeaderPreset from './StackWithHeaderPreset';
|
||||||
import SimpleTabs from './SimpleTabs';
|
import SimpleTabs from './SimpleTabs';
|
||||||
import TabAnimations from './TabAnimations';
|
import TabAnimations from './TabAnimations';
|
||||||
|
|
||||||
const ExampleInfo = {
|
const ExampleInfo = {
|
||||||
SimpleStack: {
|
SimpleStack: {
|
||||||
name: 'Stack Example',
|
name: 'Stack Example',
|
||||||
description: 'A card stack!',
|
description: 'A card stack',
|
||||||
},
|
},
|
||||||
SimpleTabs: {
|
SimpleTabs: {
|
||||||
name: 'Tabs Example',
|
name: 'Tabs Example',
|
||||||
@ -44,6 +45,10 @@ const ExampleInfo = {
|
|||||||
name: 'Drawer Example',
|
name: 'Drawer Example',
|
||||||
description: 'Android-style drawer navigation',
|
description: 'Android-style drawer navigation',
|
||||||
},
|
},
|
||||||
|
StackWithHeaderPreset: {
|
||||||
|
name: 'UIKit-style Header Transitions',
|
||||||
|
description: 'Masked back button and sliding header items. iOS only.',
|
||||||
|
},
|
||||||
// MultipleDrawer: {
|
// MultipleDrawer: {
|
||||||
// name: 'Multiple Drawer Example',
|
// name: 'Multiple Drawer Example',
|
||||||
// description: 'Add any drawer you need',
|
// description: 'Add any drawer you need',
|
||||||
@ -102,6 +107,7 @@ const ExampleRoutes = {
|
|||||||
// MultipleDrawer: {
|
// MultipleDrawer: {
|
||||||
// screen: MultipleDrawer,
|
// screen: MultipleDrawer,
|
||||||
// },
|
// },
|
||||||
|
StackWithHeaderPreset: StackWithHeaderPreset,
|
||||||
TabsInDrawer: TabsInDrawer,
|
TabsInDrawer: TabsInDrawer,
|
||||||
CustomTabs: CustomTabs,
|
CustomTabs: CustomTabs,
|
||||||
CustomTransitioner: CustomTransitioner,
|
CustomTransitioner: CustomTransitioner,
|
||||||
@ -128,6 +134,11 @@ class MainScreen extends React.Component<any, State> {
|
|||||||
scrollY: new Animated.Value(0),
|
scrollY: new Animated.Value(0),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
Asset.fromModule(require('react-navigation/src/views/assets/back-icon-mask.png')).downloadAsync();
|
||||||
|
Asset.fromModule(require('react-navigation/src/views/assets/back-icon.png')).downloadAsync();
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { navigation } = this.props;
|
const { navigation } = this.props;
|
||||||
|
|
||||||
|
@ -19,14 +19,12 @@ type MyNavScreenProps = {
|
|||||||
|
|
||||||
class MyBackButton extends React.Component<any, any> {
|
class MyBackButton extends React.Component<any, any> {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return <Button onPress={this._navigateBack} title="Custom Back" />;
|
||||||
<Button onPress={this._navigateBack} title="Custom Back" />
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_navigateBack = () => {
|
_navigateBack = () => {
|
||||||
this.props.navigation.goBack(null);
|
this.props.navigation.goBack(null);
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const MyBackButtonWithNavigation = withNavigation(MyBackButton);
|
const MyBackButtonWithNavigation = withNavigation(MyBackButton);
|
||||||
@ -108,7 +106,7 @@ type MyPhotosScreenProps = {
|
|||||||
class MyPhotosScreen extends React.Component<MyPhotosScreenProps> {
|
class MyPhotosScreen extends React.Component<MyPhotosScreenProps> {
|
||||||
static navigationOptions = {
|
static navigationOptions = {
|
||||||
title: 'Photos',
|
title: 'Photos',
|
||||||
headerLeft: <MyBackButtonWithNavigation />
|
headerLeft: <MyBackButtonWithNavigation />,
|
||||||
};
|
};
|
||||||
_s0: NavigationEventSubscription;
|
_s0: NavigationEventSubscription;
|
||||||
_s1: NavigationEventSubscription;
|
_s1: NavigationEventSubscription;
|
||||||
@ -180,7 +178,8 @@ MyProfileScreen.navigationOptions = props => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const SimpleStack = StackNavigator({
|
const SimpleStack = StackNavigator(
|
||||||
|
{
|
||||||
Home: {
|
Home: {
|
||||||
screen: MyHomeScreen,
|
screen: MyHomeScreen,
|
||||||
},
|
},
|
||||||
@ -192,6 +191,7 @@ const SimpleStack = StackNavigator({
|
|||||||
path: 'photos/:name',
|
path: 'photos/:name',
|
||||||
screen: MyPhotosScreen,
|
screen: MyPhotosScreen,
|
||||||
},
|
},
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export default SimpleStack;
|
export default SimpleStack;
|
||||||
|
68
examples/NavigationPlayground/js/StackWithHeaderPreset.js
Normal file
68
examples/NavigationPlayground/js/StackWithHeaderPreset.js
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
import type { NavigationScreenProp } from 'react-navigation';
|
||||||
|
|
||||||
|
import * as React from 'react';
|
||||||
|
import { Button, ScrollView, StatusBar } from 'react-native';
|
||||||
|
import { StackNavigator, SafeAreaView } from 'react-navigation';
|
||||||
|
|
||||||
|
type NavScreenProps = {
|
||||||
|
navigation: NavigationScreenProp<*>,
|
||||||
|
};
|
||||||
|
|
||||||
|
class HomeScreen extends React.Component<NavScreenProps> {
|
||||||
|
static navigationOptions = {
|
||||||
|
title: 'Welcome',
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { navigation } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SafeAreaView>
|
||||||
|
<Button
|
||||||
|
onPress={() => navigation.push('Other')}
|
||||||
|
title="Push another screen"
|
||||||
|
/>
|
||||||
|
<Button onPress={() => navigation.pop()} title="Pop" />
|
||||||
|
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||||
|
<StatusBar barStyle="default" />
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OtherScreen extends React.Component<NavScreenProps> {
|
||||||
|
static navigationOptions = {
|
||||||
|
title: 'Your title here',
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { navigation } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SafeAreaView>
|
||||||
|
<Button
|
||||||
|
onPress={() => navigation.push('Other')}
|
||||||
|
title="Push another screen"
|
||||||
|
/>
|
||||||
|
<Button onPress={() => navigation.pop()} title="Pop" />
|
||||||
|
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||||
|
<StatusBar barStyle="default" />
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const StackWithHeaderPreset = StackNavigator(
|
||||||
|
{
|
||||||
|
Home: HomeScreen,
|
||||||
|
Other: OtherScreen,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headerTransitionPreset: 'uikit',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export default StackWithHeaderPreset;
|
1
flow/react-navigation.js
vendored
1
flow/react-navigation.js
vendored
@ -408,6 +408,7 @@ declare module 'react-navigation' {
|
|||||||
declare export type NavigationStackViewConfig = {|
|
declare export type NavigationStackViewConfig = {|
|
||||||
mode?: 'card' | 'modal',
|
mode?: 'card' | 'modal',
|
||||||
headerMode?: HeaderMode,
|
headerMode?: HeaderMode,
|
||||||
|
headerTransitionPreset?: 'fade-in-place' | 'uikit',
|
||||||
cardStyle?: ViewStyleProp,
|
cardStyle?: ViewStyleProp,
|
||||||
transitionConfig?: () => TransitionConfig,
|
transitionConfig?: () => TransitionConfig,
|
||||||
onTransitionStart?: () => void,
|
onTransitionStart?: () => void,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "react-navigation",
|
"name": "react-navigation",
|
||||||
"version": "1.0.3",
|
"version": "1.1.0",
|
||||||
"description": "Routing and navigation for your React Native apps",
|
"description": "Routing and navigation for your React Native apps",
|
||||||
"main": "src/react-navigation.js",
|
"main": "src/react-navigation.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -15,6 +15,7 @@ export default (routeConfigMap, stackConfig = {}) => {
|
|||||||
initialRouteParams,
|
initialRouteParams,
|
||||||
paths,
|
paths,
|
||||||
headerMode,
|
headerMode,
|
||||||
|
headerTransitionPreset,
|
||||||
mode,
|
mode,
|
||||||
cardStyle,
|
cardStyle,
|
||||||
transitionConfig,
|
transitionConfig,
|
||||||
@ -38,6 +39,7 @@ export default (routeConfigMap, stackConfig = {}) => {
|
|||||||
<CardStackTransitioner
|
<CardStackTransitioner
|
||||||
{...props}
|
{...props}
|
||||||
headerMode={headerMode}
|
headerMode={headerMode}
|
||||||
|
headerTransitionPreset={headerTransitionPreset}
|
||||||
mode={mode}
|
mode={mode}
|
||||||
cardStyle={cardStyle}
|
cardStyle={cardStyle}
|
||||||
transitionConfig={transitionConfig}
|
transitionConfig={transitionConfig}
|
||||||
|
@ -167,7 +167,14 @@ exports[`DrawerNavigator renders successfully 1`] = `
|
|||||||
accessible={true}
|
accessible={true}
|
||||||
collapsable={undefined}
|
collapsable={undefined}
|
||||||
hasTVPreferredFocus={undefined}
|
hasTVPreferredFocus={undefined}
|
||||||
hitSlop={undefined}
|
hitSlop={
|
||||||
|
Object {
|
||||||
|
"bottom": 15,
|
||||||
|
"left": 15,
|
||||||
|
"right": 15,
|
||||||
|
"top": 15,
|
||||||
|
}
|
||||||
|
}
|
||||||
isTVSelectable={true}
|
isTVSelectable={true}
|
||||||
nativeID={undefined}
|
nativeID={undefined}
|
||||||
onLayout={undefined}
|
onLayout={undefined}
|
||||||
|
@ -79,6 +79,7 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
|
|||||||
collapsable={undefined}
|
collapsable={undefined}
|
||||||
getScreenDetails={[Function]}
|
getScreenDetails={[Function]}
|
||||||
headerMode={undefined}
|
headerMode={undefined}
|
||||||
|
headerTransitionPreset={undefined}
|
||||||
index={0}
|
index={0}
|
||||||
layout={
|
layout={
|
||||||
Object {
|
Object {
|
||||||
@ -89,7 +90,9 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
|
|||||||
"width": 0,
|
"width": 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
leftButtonInterpolator={[Function]}
|
||||||
leftInterpolator={[Function]}
|
leftInterpolator={[Function]}
|
||||||
|
leftLabelInterpolator={[Function]}
|
||||||
mode="float"
|
mode="float"
|
||||||
navigation={
|
navigation={
|
||||||
Object {
|
Object {
|
||||||
@ -127,8 +130,10 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
|
|||||||
"getStateForAction": [Function],
|
"getStateForAction": [Function],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
titleFromLeftInterpolator={[Function]}
|
||||||
titleInterpolator={[Function]}
|
titleInterpolator={[Function]}
|
||||||
transitionConfig={undefined}
|
transitionConfig={undefined}
|
||||||
|
transitionPreset="fade-in-place"
|
||||||
>
|
>
|
||||||
<View
|
<View
|
||||||
collapsable={undefined}
|
collapsable={undefined}
|
||||||
@ -184,11 +189,6 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
|
|||||||
"position": "absolute",
|
"position": "absolute",
|
||||||
"right": 70,
|
"right": 70,
|
||||||
"top": 0,
|
"top": 0,
|
||||||
"transform": Array [
|
|
||||||
Object {
|
|
||||||
"translateX": 0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@ -204,7 +204,7 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
|
|||||||
Object {
|
Object {
|
||||||
"color": "rgba(0, 0, 0, .9)",
|
"color": "rgba(0, 0, 0, .9)",
|
||||||
"fontSize": 17,
|
"fontSize": 17,
|
||||||
"fontWeight": "600",
|
"fontWeight": "700",
|
||||||
"marginHorizontal": 16,
|
"marginHorizontal": 16,
|
||||||
"textAlign": "center",
|
"textAlign": "center",
|
||||||
}
|
}
|
||||||
@ -318,6 +318,7 @@ exports[`StackNavigator renders successfully 1`] = `
|
|||||||
collapsable={undefined}
|
collapsable={undefined}
|
||||||
getScreenDetails={[Function]}
|
getScreenDetails={[Function]}
|
||||||
headerMode={undefined}
|
headerMode={undefined}
|
||||||
|
headerTransitionPreset={undefined}
|
||||||
index={0}
|
index={0}
|
||||||
layout={
|
layout={
|
||||||
Object {
|
Object {
|
||||||
@ -328,7 +329,9 @@ exports[`StackNavigator renders successfully 1`] = `
|
|||||||
"width": 0,
|
"width": 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
leftButtonInterpolator={[Function]}
|
||||||
leftInterpolator={[Function]}
|
leftInterpolator={[Function]}
|
||||||
|
leftLabelInterpolator={[Function]}
|
||||||
mode="float"
|
mode="float"
|
||||||
navigation={
|
navigation={
|
||||||
Object {
|
Object {
|
||||||
@ -366,8 +369,10 @@ exports[`StackNavigator renders successfully 1`] = `
|
|||||||
"getStateForAction": [Function],
|
"getStateForAction": [Function],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
titleFromLeftInterpolator={[Function]}
|
||||||
titleInterpolator={[Function]}
|
titleInterpolator={[Function]}
|
||||||
transitionConfig={undefined}
|
transitionConfig={undefined}
|
||||||
|
transitionPreset="fade-in-place"
|
||||||
>
|
>
|
||||||
<View
|
<View
|
||||||
collapsable={undefined}
|
collapsable={undefined}
|
||||||
@ -423,11 +428,6 @@ exports[`StackNavigator renders successfully 1`] = `
|
|||||||
"position": "absolute",
|
"position": "absolute",
|
||||||
"right": 0,
|
"right": 0,
|
||||||
"top": 0,
|
"top": 0,
|
||||||
"transform": Array [
|
|
||||||
Object {
|
|
||||||
"translateX": 0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@ -443,7 +443,7 @@ exports[`StackNavigator renders successfully 1`] = `
|
|||||||
Object {
|
Object {
|
||||||
"color": "rgba(0, 0, 0, .9)",
|
"color": "rgba(0, 0, 0, .9)",
|
||||||
"fontSize": 17,
|
"fontSize": 17,
|
||||||
"fontWeight": "600",
|
"fontWeight": "700",
|
||||||
"marginHorizontal": 16,
|
"marginHorizontal": 16,
|
||||||
"textAlign": "center",
|
"textAlign": "center",
|
||||||
}
|
}
|
||||||
|
@ -137,6 +137,7 @@ class CardStack extends React.Component {
|
|||||||
...passProps,
|
...passProps,
|
||||||
scene,
|
scene,
|
||||||
mode: headerMode,
|
mode: headerMode,
|
||||||
|
transitionPreset: this._getHeaderTransitionPreset(),
|
||||||
getScreenDetails: this._getScreenDetails,
|
getScreenDetails: this._getScreenDetails,
|
||||||
leftInterpolator: headerLeftInterpolator,
|
leftInterpolator: headerLeftInterpolator,
|
||||||
titleInterpolator: headerTitleInterpolator,
|
titleInterpolator: headerTitleInterpolator,
|
||||||
@ -363,6 +364,21 @@ class CardStack extends React.Component {
|
|||||||
return 'float';
|
return 'float';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_getHeaderTransitionPreset() {
|
||||||
|
// On Android or with header mode screen, we always just use in-place,
|
||||||
|
// we ignore the option entirely (at least until we have other presets)
|
||||||
|
if (Platform.OS === 'android' || this._getHeaderMode() === 'screen') {
|
||||||
|
return 'fade-in-place';
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: validations: 'fade-in-place' or 'uikit' are valid
|
||||||
|
if (this.props.headerTransitionPreset) {
|
||||||
|
return this.props.headerTransitionPreset;
|
||||||
|
} else {
|
||||||
|
return 'fade-in-place';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_renderInnerScene(SceneComponent, scene) {
|
_renderInnerScene(SceneComponent, scene) {
|
||||||
const { navigation } = this._getScreenDetails(scene);
|
const { navigation } = this._getScreenDetails(scene);
|
||||||
const { screenProps } = this.props;
|
const { screenProps } = this.props;
|
||||||
|
@ -57,6 +57,7 @@ class CardStackTransitioner extends React.Component {
|
|||||||
const {
|
const {
|
||||||
screenProps,
|
screenProps,
|
||||||
headerMode,
|
headerMode,
|
||||||
|
headerTransitionPreset,
|
||||||
mode,
|
mode,
|
||||||
router,
|
router,
|
||||||
cardStyle,
|
cardStyle,
|
||||||
@ -66,6 +67,7 @@ class CardStackTransitioner extends React.Component {
|
|||||||
<CardStack
|
<CardStack
|
||||||
screenProps={screenProps}
|
screenProps={screenProps}
|
||||||
headerMode={headerMode}
|
headerMode={headerMode}
|
||||||
|
headerTransitionPreset={headerTransitionPreset}
|
||||||
mode={mode}
|
mode={mode}
|
||||||
router={router}
|
router={router}
|
||||||
cardStyle={cardStyle}
|
cardStyle={cardStyle}
|
||||||
|
@ -3,8 +3,10 @@ import React from 'react';
|
|||||||
import {
|
import {
|
||||||
Animated,
|
Animated,
|
||||||
Dimensions,
|
Dimensions,
|
||||||
|
Image,
|
||||||
Platform,
|
Platform,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
|
MaskedViewIOS,
|
||||||
View,
|
View,
|
||||||
ViewPropTypes,
|
ViewPropTypes,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
@ -12,6 +14,7 @@ import SafeAreaView from 'react-native-safe-area-view';
|
|||||||
|
|
||||||
import HeaderTitle from './HeaderTitle';
|
import HeaderTitle from './HeaderTitle';
|
||||||
import HeaderBackButton from './HeaderBackButton';
|
import HeaderBackButton from './HeaderBackButton';
|
||||||
|
import ModularHeaderBackButton from './ModularHeaderBackButton';
|
||||||
import HeaderStyleInterpolator from './HeaderStyleInterpolator';
|
import HeaderStyleInterpolator from './HeaderStyleInterpolator';
|
||||||
import withOrientation from '../withOrientation';
|
import withOrientation from '../withOrientation';
|
||||||
|
|
||||||
@ -19,9 +22,18 @@ const APPBAR_HEIGHT = Platform.OS === 'ios' ? 44 : 56;
|
|||||||
const STATUSBAR_HEIGHT = Platform.OS === 'ios' ? 20 : 0;
|
const STATUSBAR_HEIGHT = Platform.OS === 'ios' ? 20 : 0;
|
||||||
const TITLE_OFFSET = Platform.OS === 'ios' ? 70 : 56;
|
const TITLE_OFFSET = Platform.OS === 'ios' ? 70 : 56;
|
||||||
|
|
||||||
|
const getAppBarHeight = isLandscape => {
|
||||||
|
return Platform.OS === 'ios'
|
||||||
|
? isLandscape && !Platform.isPad ? 32 : 44
|
||||||
|
: 56;
|
||||||
|
};
|
||||||
|
|
||||||
class Header extends React.PureComponent {
|
class Header extends React.PureComponent {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
leftInterpolator: HeaderStyleInterpolator.forLeft,
|
leftInterpolator: HeaderStyleInterpolator.forLeft,
|
||||||
|
leftButtonInterpolator: HeaderStyleInterpolator.forLeftButton,
|
||||||
|
leftLabelInterpolator: HeaderStyleInterpolator.forLeftLabel,
|
||||||
|
titleFromLeftInterpolator: HeaderStyleInterpolator.forCenterFromLeft,
|
||||||
titleInterpolator: HeaderStyleInterpolator.forCenter,
|
titleInterpolator: HeaderStyleInterpolator.forCenter,
|
||||||
rightInterpolator: HeaderStyleInterpolator.forRight,
|
rightInterpolator: HeaderStyleInterpolator.forRight,
|
||||||
};
|
};
|
||||||
@ -116,15 +128,14 @@ class Header extends React.PureComponent {
|
|||||||
|
|
||||||
_renderLeftComponent = props => {
|
_renderLeftComponent = props => {
|
||||||
const { options } = this.props.getScreenDetails(props.scene);
|
const { options } = this.props.getScreenDetails(props.scene);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
React.isValidElement(options.headerLeft) ||
|
React.isValidElement(options.headerLeft) ||
|
||||||
options.headerLeft === null
|
options.headerLeft === null
|
||||||
) {
|
) {
|
||||||
return options.headerLeft;
|
return options.headerLeft;
|
||||||
}
|
}
|
||||||
if (props.scene.index === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const backButtonTitle = this._getBackButtonTitleString(props.scene);
|
const backButtonTitle = this._getBackButtonTitleString(props.scene);
|
||||||
const truncatedBackButtonTitle = this._getTruncatedBackButtonTitle(
|
const truncatedBackButtonTitle = this._getTruncatedBackButtonTitle(
|
||||||
props.scene
|
props.scene
|
||||||
@ -147,6 +158,36 @@ class Header extends React.PureComponent {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_renderModularLeftComponent = (
|
||||||
|
props,
|
||||||
|
ButtonContainerComponent,
|
||||||
|
LabelContainerComponent
|
||||||
|
) => {
|
||||||
|
const { options } = this.props.getScreenDetails(props.scene);
|
||||||
|
const backButtonTitle = this._getBackButtonTitleString(props.scene);
|
||||||
|
const truncatedBackButtonTitle = this._getTruncatedBackButtonTitle(
|
||||||
|
props.scene
|
||||||
|
);
|
||||||
|
const width = this.state.widths[props.scene.key]
|
||||||
|
? (this.props.layout.initWidth - this.state.widths[props.scene.key]) / 2
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModularHeaderBackButton
|
||||||
|
onPress={this._navigateBack}
|
||||||
|
ButtonContainerComponent={ButtonContainerComponent}
|
||||||
|
LabelContainerComponent={LabelContainerComponent}
|
||||||
|
pressColorAndroid={options.headerPressColorAndroid}
|
||||||
|
tintColor={options.headerTintColor}
|
||||||
|
buttonImage={options.headerBackImage}
|
||||||
|
title={backButtonTitle}
|
||||||
|
truncatedTitle={truncatedBackButtonTitle}
|
||||||
|
titleStyle={options.headerBackTitleStyle}
|
||||||
|
width={width}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
_renderRightComponent = props => {
|
_renderRightComponent = props => {
|
||||||
const details = this.props.getScreenDetails(props.scene);
|
const details = this.props.getScreenDetails(props.scene);
|
||||||
const { headerRight } = details.options;
|
const { headerRight } = details.options;
|
||||||
@ -154,16 +195,42 @@ class Header extends React.PureComponent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_renderLeft(props) {
|
_renderLeft(props) {
|
||||||
|
const { options } = this.props.getScreenDetails(props.scene);
|
||||||
|
|
||||||
|
if (props.scene.index === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { transitionPreset } = this.props;
|
||||||
|
|
||||||
|
// On Android, or if we have a custom header left, or if we have a custom back image, we
|
||||||
|
// do not use the modular header (which is the one that imitates UINavigationController)
|
||||||
|
if (
|
||||||
|
transitionPreset !== 'uikit' ||
|
||||||
|
options.headerBackImage ||
|
||||||
|
options.headerLeft ||
|
||||||
|
options.headerLeft === null
|
||||||
|
) {
|
||||||
return this._renderSubView(
|
return this._renderSubView(
|
||||||
props,
|
props,
|
||||||
'left',
|
'left',
|
||||||
this._renderLeftComponent,
|
this._renderLeftComponent,
|
||||||
this.props.leftInterpolator
|
this.props.leftInterpolator
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
return this._renderModularSubView(
|
||||||
|
props,
|
||||||
|
'left',
|
||||||
|
this._renderModularLeftComponent,
|
||||||
|
this.props.leftLabelInterpolator,
|
||||||
|
this.props.leftButtonInterpolator
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderTitle(props, options) {
|
_renderTitle(props, options) {
|
||||||
const style = {};
|
const style = {};
|
||||||
|
const { transitionPreset } = this.props;
|
||||||
|
|
||||||
if (Platform.OS === 'android') {
|
if (Platform.OS === 'android') {
|
||||||
if (!options.hasLeftComponent) {
|
if (!options.hasLeftComponent) {
|
||||||
@ -185,7 +252,9 @@ class Header extends React.PureComponent {
|
|||||||
{ ...props, style },
|
{ ...props, style },
|
||||||
'title',
|
'title',
|
||||||
this._renderTitleComponent,
|
this._renderTitleComponent,
|
||||||
this.props.titleInterpolator
|
transitionPreset === 'uikit'
|
||||||
|
? this.props.titleFromLeftInterpolator
|
||||||
|
: this.props.titleInterpolator
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,6 +267,59 @@ class Header extends React.PureComponent {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_renderModularSubView(
|
||||||
|
props,
|
||||||
|
name,
|
||||||
|
renderer,
|
||||||
|
labelStyleInterpolator,
|
||||||
|
buttonStyleInterpolator
|
||||||
|
) {
|
||||||
|
const { scene } = props;
|
||||||
|
const { index, isStale, key } = scene;
|
||||||
|
|
||||||
|
const offset = this.props.navigation.state.index - index;
|
||||||
|
|
||||||
|
if (Math.abs(offset) > 2) {
|
||||||
|
// Scene is far away from the active scene. Hides it to avoid unnecessary
|
||||||
|
// rendering.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ButtonContainer = ({ children }) => (
|
||||||
|
<Animated.View
|
||||||
|
style={[buttonStyleInterpolator({ ...this.props, ...props })]}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Animated.View>
|
||||||
|
);
|
||||||
|
|
||||||
|
const LabelContainer = ({ children }) => (
|
||||||
|
<Animated.View
|
||||||
|
style={[labelStyleInterpolator({ ...this.props, ...props })]}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Animated.View>
|
||||||
|
);
|
||||||
|
|
||||||
|
const subView = renderer(props, ButtonContainer, LabelContainer);
|
||||||
|
|
||||||
|
if (subView === null) {
|
||||||
|
return subView;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pointerEvents = offset !== 0 || isStale ? 'none' : 'box-none';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
key={`${name}_${key}`}
|
||||||
|
pointerEvents={pointerEvents}
|
||||||
|
style={[styles.item, styles[name], props.style]}
|
||||||
|
>
|
||||||
|
{subView}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
_renderSubView(props, name, renderer, styleInterpolator) {
|
_renderSubView(props, name, renderer, styleInterpolator) {
|
||||||
const { scene } = props;
|
const { scene } = props;
|
||||||
const { index, isStale, key } = scene;
|
const { index, isStale, key } = scene;
|
||||||
@ -246,16 +368,47 @@ class Header extends React.PureComponent {
|
|||||||
hasRightComponent: !!right,
|
hasRightComponent: !!right,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const wrapperProps = {
|
||||||
|
style: [StyleSheet.absoluteFill, styles.header],
|
||||||
|
key: `scene_${props.scene.key}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const { isLandscape, transitionPreset } = this.props;
|
||||||
|
const { options } = this.props.getScreenDetails(props.scene);
|
||||||
|
|
||||||
|
if (
|
||||||
|
options.headerLeft ||
|
||||||
|
options.headerBackImage ||
|
||||||
|
Platform.OS === 'android' ||
|
||||||
|
transitionPreset !== 'uikit'
|
||||||
|
) {
|
||||||
return (
|
return (
|
||||||
<View
|
<View {...wrapperProps}>
|
||||||
style={[StyleSheet.absoluteFill, styles.header]}
|
|
||||||
key={`scene_${props.scene.key}`}
|
|
||||||
>
|
|
||||||
{title}
|
{title}
|
||||||
{left}
|
{left}
|
||||||
{right}
|
{right}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<MaskedViewIOS
|
||||||
|
{...wrapperProps}
|
||||||
|
maskElement={
|
||||||
|
<View style={styles.iconMaskContainer}>
|
||||||
|
<Image
|
||||||
|
source={require('../assets/back-icon-mask.png')}
|
||||||
|
style={styles.iconMask}
|
||||||
|
/>
|
||||||
|
<View style={styles.iconMaskFillerRect} />
|
||||||
|
</View>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
{left}
|
||||||
|
{right}
|
||||||
|
</MaskedViewIOS>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -293,8 +446,7 @@ class Header extends React.PureComponent {
|
|||||||
|
|
||||||
const { options } = this.props.getScreenDetails(scene);
|
const { options } = this.props.getScreenDetails(scene);
|
||||||
const { headerStyle } = options;
|
const { headerStyle } = options;
|
||||||
const appBarHeight =
|
const appBarHeight = getAppBarHeight(isLandscape);
|
||||||
Platform.OS === 'ios' ? (isLandscape && !Platform.isPad ? 32 : 44) : 56;
|
|
||||||
const containerStyles = [
|
const containerStyles = [
|
||||||
styles.container,
|
styles.container,
|
||||||
{
|
{
|
||||||
@ -350,6 +502,25 @@ const styles = StyleSheet.create({
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
},
|
},
|
||||||
|
iconMaskContainer: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
iconMaskFillerRect: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: '#d8d8d8',
|
||||||
|
marginLeft: -3,
|
||||||
|
},
|
||||||
|
iconMask: {
|
||||||
|
// These are mostly the same as the icon in ModularHeaderBackButton
|
||||||
|
height: 21,
|
||||||
|
width: 12,
|
||||||
|
marginLeft: 9,
|
||||||
|
marginTop: -0.5, // resizes down to 20.5
|
||||||
|
alignSelf: 'center',
|
||||||
|
resizeMode: 'contain',
|
||||||
|
},
|
||||||
title: {
|
title: {
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
left: TITLE_OFFSET,
|
left: TITLE_OFFSET,
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
import { I18nManager } from 'react-native';
|
import { Dimensions, I18nManager } from 'react-native';
|
||||||
|
|
||||||
import getSceneIndicesForInterpolationInputRange from '../../utils/getSceneIndicesForInterpolationInputRange';
|
import getSceneIndicesForInterpolationInputRange from '../../utils/getSceneIndicesForInterpolationInputRange';
|
||||||
|
|
||||||
|
const crossFadeInterpolation = (first, index, last) => ({
|
||||||
|
inputRange: [first, index - 0.75, index - 0.5, index, index + 0.5, last],
|
||||||
|
outputRange: [0, 0, 0.2, 1, 0.5, 0],
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility that builds the style for the navigation header.
|
* Utility that builds the style for the navigation header.
|
||||||
*
|
*
|
||||||
@ -23,16 +27,7 @@ function forLeft(props) {
|
|||||||
const index = scene.index;
|
const index = scene.index;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
opacity: position.interpolate({
|
opacity: position.interpolate(crossFadeInterpolation(first, index, last)),
|
||||||
inputRange: [
|
|
||||||
first,
|
|
||||||
first + Math.abs(index - first) / 2,
|
|
||||||
index,
|
|
||||||
last - Math.abs(last - index) / 2,
|
|
||||||
last,
|
|
||||||
],
|
|
||||||
outputRange: [0, 0, 1, 0, 0],
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,21 +39,9 @@ function forCenter(props) {
|
|||||||
|
|
||||||
const { first, last } = interpolate;
|
const { first, last } = interpolate;
|
||||||
const index = scene.index;
|
const index = scene.index;
|
||||||
const inputRange = [first, index, last];
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
opacity: position.interpolate({
|
opacity: position.interpolate(crossFadeInterpolation(first, index, last)),
|
||||||
inputRange,
|
|
||||||
outputRange: [0, 1, 0],
|
|
||||||
}),
|
|
||||||
transform: [
|
|
||||||
{
|
|
||||||
translateX: position.interpolate({
|
|
||||||
inputRange,
|
|
||||||
outputRange: I18nManager.isRTL ? [-200, 0, 200] : [200, 0, -200],
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,16 +53,125 @@ function forRight(props) {
|
|||||||
const { first, last } = interpolate;
|
const { first, last } = interpolate;
|
||||||
const index = scene.index;
|
const index = scene.index;
|
||||||
|
|
||||||
|
return {
|
||||||
|
opacity: position.interpolate(crossFadeInterpolation(first, index, last)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iOS UINavigationController style interpolators
|
||||||
|
*/
|
||||||
|
|
||||||
|
function forLeftButton(props) {
|
||||||
|
const { position, scene, scenes } = props;
|
||||||
|
const interpolate = getSceneIndicesForInterpolationInputRange(props);
|
||||||
|
|
||||||
|
if (!interpolate) return { opacity: 0 };
|
||||||
|
|
||||||
|
const { first, last } = interpolate;
|
||||||
|
const index = scene.index;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
opacity: position.interpolate({
|
opacity: position.interpolate({
|
||||||
inputRange: [first, index, last],
|
inputRange: [
|
||||||
outputRange: [0, 1, 0],
|
first,
|
||||||
|
first + Math.abs(index - first) / 2,
|
||||||
|
index,
|
||||||
|
last - Math.abs(last - index) / 2,
|
||||||
|
last,
|
||||||
|
],
|
||||||
|
outputRange: [0, 0.5, 1, 0.5, 0],
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTE: this offset calculation is a an approximation that gives us
|
||||||
|
* decent results in many cases, but it is ultimately a poor substitute
|
||||||
|
* for text measurement. See the comment on title for more information.
|
||||||
|
*
|
||||||
|
* - 70 is the width of the left button area.
|
||||||
|
* - 25 is the width of the left button icon (to account for label offset)
|
||||||
|
*/
|
||||||
|
const LEFT_LABEL_OFFSET = Dimensions.get('window').width / 2 - 70 - 25;
|
||||||
|
function forLeftLabel(props) {
|
||||||
|
const { position, scene, scenes } = props;
|
||||||
|
const interpolate = getSceneIndicesForInterpolationInputRange(props);
|
||||||
|
|
||||||
|
if (!interpolate) return { opacity: 0 };
|
||||||
|
|
||||||
|
const { first, last } = interpolate;
|
||||||
|
const index = scene.index;
|
||||||
|
|
||||||
|
const offset = LEFT_LABEL_OFFSET;
|
||||||
|
|
||||||
|
return {
|
||||||
|
// For now we fade out the label before fading in the title, so the
|
||||||
|
// differences between the label and title position can be hopefully not so
|
||||||
|
// noticable to the user
|
||||||
|
opacity: position.interpolate({
|
||||||
|
inputRange: [first, index - 0.35, index, index + 0.5, last],
|
||||||
|
outputRange: [0, 0, 1, 0, 0],
|
||||||
|
}),
|
||||||
|
transform: [
|
||||||
|
{
|
||||||
|
translateX: position.interpolate({
|
||||||
|
inputRange: [first, index, last],
|
||||||
|
outputRange: I18nManager.isRTL
|
||||||
|
? [-offset, 0, offset]
|
||||||
|
: [offset, 0, -offset],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTE: this offset calculation is a an approximation that gives us
|
||||||
|
* decent results in many cases, but it is ultimately a poor substitute
|
||||||
|
* for text measurement. We want the back button label to transition
|
||||||
|
* smoothly into the title text and to do this we need to understand
|
||||||
|
* where the title is positioned within the title container (since it is
|
||||||
|
* centered).
|
||||||
|
*
|
||||||
|
* - 70 is the width of the left button area.
|
||||||
|
* - 25 is the width of the left button icon (to account for label offset)
|
||||||
|
*/
|
||||||
|
const TITLE_OFFSET_IOS = Dimensions.get('window').width / 2 - 70 + 25;
|
||||||
|
function forCenterFromLeft(props) {
|
||||||
|
const { position, scene } = props;
|
||||||
|
const interpolate = getSceneIndicesForInterpolationInputRange(props);
|
||||||
|
|
||||||
|
if (!interpolate) return { opacity: 0 };
|
||||||
|
|
||||||
|
const { first, last } = interpolate;
|
||||||
|
const index = scene.index;
|
||||||
|
const inputRange = [first, index - 0.5, index, index + 0.5, last];
|
||||||
|
const offset = TITLE_OFFSET_IOS;
|
||||||
|
|
||||||
|
return {
|
||||||
|
opacity: position.interpolate({
|
||||||
|
inputRange: [first, index - 0.5, index, index + 0.7, last],
|
||||||
|
outputRange: [0, 0, 1, 0, 0],
|
||||||
|
}),
|
||||||
|
transform: [
|
||||||
|
{
|
||||||
|
translateX: position.interpolate({
|
||||||
|
inputRange: [first, index, last],
|
||||||
|
outputRange: I18nManager.isRTL
|
||||||
|
? [-offset, 0, offset]
|
||||||
|
: [offset, 0, -offset],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
forLeft,
|
forLeft,
|
||||||
|
forLeftButton,
|
||||||
|
forLeftLabel,
|
||||||
|
forCenterFromLeft,
|
||||||
forCenter,
|
forCenter,
|
||||||
forRight,
|
forRight,
|
||||||
};
|
};
|
||||||
|
@ -15,7 +15,7 @@ const HeaderTitle = ({ style, ...rest }) => (
|
|||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
title: {
|
title: {
|
||||||
fontSize: Platform.OS === 'ios' ? 17 : 20,
|
fontSize: Platform.OS === 'ios' ? 17 : 20,
|
||||||
fontWeight: Platform.OS === 'ios' ? '600' : '500',
|
fontWeight: Platform.OS === 'ios' ? '700' : '500',
|
||||||
color: 'rgba(0, 0, 0, .9)',
|
color: 'rgba(0, 0, 0, .9)',
|
||||||
textAlign: Platform.OS === 'ios' ? 'center' : 'left',
|
textAlign: Platform.OS === 'ios' ? 'center' : 'left',
|
||||||
marginHorizontal: 16,
|
marginHorizontal: 16,
|
||||||
|
118
src/views/Header/ModularHeaderBackButton.js
Normal file
118
src/views/Header/ModularHeaderBackButton.js
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { I18nManager, Image, Text, View, StyleSheet } from 'react-native';
|
||||||
|
|
||||||
|
import TouchableItem from '../TouchableItem';
|
||||||
|
|
||||||
|
class ModularHeaderBackButton extends React.PureComponent {
|
||||||
|
static defaultProps = {
|
||||||
|
tintColor: '#037aff',
|
||||||
|
truncatedTitle: 'Back',
|
||||||
|
// eslint-disable-next-line global-require
|
||||||
|
buttonImage: require('../assets/back-icon.png'),
|
||||||
|
};
|
||||||
|
|
||||||
|
state = {};
|
||||||
|
|
||||||
|
_onTextLayout = e => {
|
||||||
|
if (this.state.initialTextWidth) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
initialTextWidth: e.nativeEvent.layout.x + e.nativeEvent.layout.width,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
buttonImage,
|
||||||
|
onPress,
|
||||||
|
width,
|
||||||
|
title,
|
||||||
|
titleStyle,
|
||||||
|
tintColor,
|
||||||
|
truncatedTitle,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const renderTruncated =
|
||||||
|
this.state.initialTextWidth && width
|
||||||
|
? this.state.initialTextWidth > width
|
||||||
|
: false;
|
||||||
|
|
||||||
|
let backButtonTitle = renderTruncated ? truncatedTitle : title;
|
||||||
|
|
||||||
|
// TODO: When we've sorted out measuring in the header, let's revisit this.
|
||||||
|
// This is clearly a bad workaround.
|
||||||
|
if (backButtonTitle.length > 8) {
|
||||||
|
backButtonTitle = truncatedTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { ButtonContainerComponent, LabelContainerComponent } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TouchableItem
|
||||||
|
accessibilityComponentType="button"
|
||||||
|
accessibilityLabel={backButtonTitle}
|
||||||
|
accessibilityTraits="button"
|
||||||
|
testID="header-back"
|
||||||
|
delayPressIn={0}
|
||||||
|
onPress={onPress}
|
||||||
|
style={styles.container}
|
||||||
|
borderless
|
||||||
|
>
|
||||||
|
<View style={styles.container}>
|
||||||
|
<ButtonContainerComponent>
|
||||||
|
<Image
|
||||||
|
style={[
|
||||||
|
styles.icon,
|
||||||
|
!!title && styles.iconWithTitle,
|
||||||
|
!!tintColor && { tintColor },
|
||||||
|
]}
|
||||||
|
source={buttonImage}
|
||||||
|
/>
|
||||||
|
</ButtonContainerComponent>
|
||||||
|
{typeof backButtonTitle === 'string' && (
|
||||||
|
<LabelContainerComponent>
|
||||||
|
<Text
|
||||||
|
onLayout={this._onTextLayout}
|
||||||
|
style={[
|
||||||
|
styles.title,
|
||||||
|
!!tintColor && { color: tintColor },
|
||||||
|
titleStyle,
|
||||||
|
]}
|
||||||
|
numberOfLines={1}
|
||||||
|
>
|
||||||
|
{backButtonTitle}
|
||||||
|
</Text>
|
||||||
|
</LabelContainerComponent>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</TouchableItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
alignItems: 'center',
|
||||||
|
flexDirection: 'row',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 17,
|
||||||
|
paddingRight: 10,
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
height: 21,
|
||||||
|
width: 12,
|
||||||
|
marginLeft: 9,
|
||||||
|
marginRight: 22,
|
||||||
|
marginVertical: 12,
|
||||||
|
resizeMode: 'contain',
|
||||||
|
transform: [{ scaleX: I18nManager.isRTL ? -1 : 1 }],
|
||||||
|
},
|
||||||
|
iconWithTitle: {
|
||||||
|
marginRight: 3,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default ModularHeaderBackButton;
|
@ -21,6 +21,7 @@ export default class TouchableItem extends React.Component {
|
|||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
borderless: false,
|
borderless: false,
|
||||||
pressColor: 'rgba(0, 0, 0, .32)',
|
pressColor: 'rgba(0, 0, 0, .32)',
|
||||||
|
hitSlop: { top: 15, left: 15, right: 15, bottom: 15 },
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
BIN
src/views/assets/back-icon-mask.png
Normal file
BIN
src/views/assets/back-icon-mask.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
Binary file not shown.
Before Width: | Height: | Size: 379 B After Width: | Height: | Size: 491 B |
Loading…
x
Reference in New Issue
Block a user