This commit is contained in:
satyajit.happy
2019-05-16 16:47:14 +02:00
parent a982873d62
commit 0799b43046
3 changed files with 72 additions and 243 deletions

View File

@@ -0,0 +1,72 @@
import * as React from 'react';
export type Route = {
key: string;
routeName: string;
} & (NavigationState | undefined);
export type NavigationEventName =
| 'willFocus'
| 'didFocus'
| 'willBlur'
| 'didBlur';
export type NavigationState = {
key: string;
index: number;
routes: Route[];
isTransitioning?: boolean;
params?: { [key: string]: unknown };
};
export type NavigationProp<RouteName = string, Params = object> = {
emit(eventName: string): void;
navigate(routeName: RouteName): void;
goBack(): void;
goBack(key: string | null): void;
addListener: (
event: NavigationEventName,
callback: () => void
) => { remove: () => void };
isFocused(): boolean;
state: NavigationState;
setParams(params: Params): void;
getParam(): Params;
dispatch(action: { type: string }): void;
dangerouslyGetParent(): NavigationProp | undefined;
};
export type NavigationTabOptions = {
title?: string;
tabBarLabel?: React.ReactNode;
tabBarIcon?:
| React.ReactNode
| ((props: {
focused: boolean;
tintColor: string;
horizontal: boolean;
}) => React.ReactNode);
tabBarTestID?: string;
tabBarOnPress?: (props: {
navigation: NavigationProp;
defaultHandler: () => void;
}) => void;
tabBarOnLongPress?: (props: {
navigation: NavigationProp;
defaultHandler: () => void;
}) => void;
tabBarAccessibilityLabel?: string;
};
export type SceneDescriptor = {
key: string;
options: NavigationTabOptions;
navigation: NavigationProp;
getComponent(): React.ComponentType;
};
export type Screen = React.ComponentType<any> & {
navigationOptions?: NavigationTabOptions & {
[key: string]: any;
};
};

View File

@@ -1,206 +0,0 @@
/* @flow */
import * as React from 'react';
import {
TabRouter,
StackActions,
SceneView,
createNavigator,
SwitchActions,
} from '@react-navigation/core';
export type InjectedProps = {|
getLabelText: (props: { route: any }) => any,
getAccessibilityLabel: (props: { route: any }) => string,
getTestID: (props: { route: any }) => string,
renderIcon: (props: {
route: any,
focused: boolean,
tintColor: string,
horizontal?: boolean,
}) => React.Node,
renderScene: (props: { route: any }) => ?React.Node,
onIndexChange: (index: number) => any,
onTabPress: (props: { route: any }) => mixed,
onTabLongPress: (props: { route: any }) => mixed,
navigation: any,
descriptors: any,
screenProps?: any,
|};
export default function createTabNavigator(TabView: React.ComponentType<*>) {
class NavigationView extends React.Component<*, *> {
_renderScene = ({ route }) => {
const { screenProps, descriptors } = this.props;
const descriptor = descriptors[route.key];
const TabComponent = descriptor.getComponent();
return (
<SceneView
screenProps={screenProps}
navigation={descriptor.navigation}
component={TabComponent}
/>
);
};
_renderIcon = ({ route, focused, tintColor, horizontal = false }) => {
const { descriptors } = this.props;
const descriptor = descriptors[route.key];
const options = descriptor.options;
if (options.tabBarIcon) {
return typeof options.tabBarIcon === 'function'
? options.tabBarIcon({ focused, tintColor, horizontal })
: options.tabBarIcon;
}
return null;
};
_getLabelText = ({ route }) => {
const { descriptors } = this.props;
const descriptor = descriptors[route.key];
const options = descriptor.options;
if (options.tabBarLabel) {
return options.tabBarLabel;
}
if (typeof options.title === 'string') {
return options.title;
}
return route.routeName;
};
_getAccessibilityLabel = ({ route }) => {
const { descriptors } = this.props;
const descriptor = descriptors[route.key];
const options = descriptor.options;
if (typeof options.tabBarAccessibilityLabel !== 'undefined') {
return options.tabBarAccessibilityLabel;
}
const label = this._getLabelText({ route });
if (typeof label === 'string') {
const { routes } = this.props.navigation.state;
return `${label}, tab, ${routes.indexOf(route) + 1} of ${
routes.length
}`;
}
};
_getTestID = ({ route }) => {
const { descriptors } = this.props;
const descriptor = descriptors[route.key];
const options = descriptor.options;
return options.tabBarTestID;
};
_makeDefaultHandler = ({ route, navigation }) => () => {
if (navigation.isFocused()) {
if (route.hasOwnProperty('index') && route.index > 0) {
// If current tab has a nested navigator, pop to top
navigation.dispatch(StackActions.popToTop({ key: route.key }));
} else {
navigation.emit('refocus');
}
} else {
this._jumpTo(route.routeName);
}
};
_handleTabPress = ({ route }) => {
this._isTabPress = true;
// After tab press, handleIndexChange will be called synchronously
// So we reset it in promise callback
Promise.resolve().then(() => (this._isTabPress = false));
const { descriptors } = this.props;
const descriptor = descriptors[route.key];
const { navigation, options } = descriptor;
const defaultHandler = this._makeDefaultHandler({ route, navigation });
if (options.tabBarOnPress) {
options.tabBarOnPress({ navigation, defaultHandler });
} else {
defaultHandler();
}
};
_handleTabLongPress = ({ route }) => {
const { descriptors } = this.props;
const descriptor = descriptors[route.key];
const { navigation, options } = descriptor;
const defaultHandler = this._makeDefaultHandler({ route, navigation });
if (options.tabBarOnLongPress) {
options.tabBarOnLongPress({ navigation, defaultHandler });
} else {
defaultHandler();
}
};
_handleIndexChange = index => {
if (this._isTabPress) {
this._isTabPress = false;
return;
}
this._jumpTo(this.props.navigation.state.routes[index].routeName);
};
_jumpTo = routeName => {
const { navigation } = this.props;
navigation.dispatch(
SwitchActions.jumpTo({
routeName,
key: navigation.state.key,
preserveFocus: true,
})
);
};
_isTabPress: boolean = false;
render() {
const { descriptors, navigation, screenProps } = this.props;
const { state } = navigation;
const route = state.routes[state.index];
const descriptor = descriptors[route.key];
const options = {
...this.props.navigationConfig,
...descriptor.options,
};
return (
<TabView
{...options}
getLabelText={this._getLabelText}
getAccessibilityLabel={this._getAccessibilityLabel}
getTestID={this._getTestID}
renderIcon={this._renderIcon}
renderScene={this._renderScene}
onIndexChange={this._handleIndexChange}
onTabPress={this._handleTabPress}
onTabLongPress={this._handleTabLongPress}
navigation={navigation}
descriptors={descriptors}
screenProps={screenProps}
/>
);
}
}
return (routes: *, config: * = {}) => {
const router = TabRouter(routes, config);
return createNavigator(NavigationView, router, config);
};
}

View File

@@ -1,37 +0,0 @@
import * as React from 'react';
import { Dimensions } from 'react-native';
import hoistNonReactStatic from 'hoist-non-react-statics';
export const isOrientationLandscape = ({ width, height }) => width > height;
export default function withDimensions(WrappedComponent) {
const { width, height } = Dimensions.get('window');
class EnhancedComponent extends React.Component {
static displayName = `withDimensions(${WrappedComponent.displayName})`;
state = {
dimensions: { width, height },
isLandscape: isOrientationLandscape({ width, height }),
};
componentDidMount() {
Dimensions.addEventListener('change', this.handleOrientationChange);
}
componentWillUnmount() {
Dimensions.removeEventListener('change', this.handleOrientationChange);
}
handleOrientationChange = ({ window }) => {
const isLandscape = isOrientationLandscape(window);
this.setState({ isLandscape });
};
render() {
return <WrappedComponent {...this.props} {...this.state} />;
}
}
return hoistNonReactStatic(EnhancedComponent, WrappedComponent);
}