feat: add theme support

This commit is contained in:
Brent Vatne
2019-09-03 17:10:40 -07:00
parent 0c6b66236b
commit 3550163f56
6 changed files with 2134 additions and 2448 deletions

View File

@@ -1,31 +1,44 @@
import * as React from 'react';
import { registerRootComponent } from 'expo';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import { createStackNavigator } from 'react-navigation';
import { createAppContainer } from '@react-navigation/native';
import { View, TouchableOpacity, StyleSheet } from 'react-native';
import {
Assets as StackAssets,
createStackNavigator,
} from 'react-navigation-stack';
import { Themed, createAppContainer } from '@react-navigation/native';
import { ThemeColors, useTheme } from '@react-navigation/core';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { Asset } from 'expo-asset';
import BottomTabs from './src/BottomTabs';
import MaterialTopTabs from './src/MaterialTopTabs';
class Home extends React.Component {
render() {
return (
<View>
<TouchableOpacity
style={styles.item}
onPress={() => this.props.navigation.push('BottomTabs')}
>
<Text>Bottom tabs</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.item}
onPress={() => this.props.navigation.push('MaterialTopTabs')}
>
<Text>Material top tabs</Text>
</TouchableOpacity>
</View>
);
}
}
// Load the back button etc
Asset.loadAsync(StackAssets);
const Home = props => {
let theme = useTheme();
return (
<View>
<TouchableOpacity
activeOpacity={0.8}
style={theme === 'dark' ? styles.itemDark : styles.itemLight}
onPress={() => props.navigation.push('BottomTabs')}
>
<Themed.Text>Bottom tabs</Themed.Text>
</TouchableOpacity>
<TouchableOpacity
activeOpacity={0.8}
style={theme === 'dark' ? styles.itemDark : styles.itemLight}
onPress={() => props.navigation.push('MaterialTopTabs')}
>
<Themed.Text>Material top tabs</Themed.Text>
</TouchableOpacity>
<Themed.StatusBar />
</View>
);
};
const List = createStackNavigator({
Home: {
@@ -42,15 +55,75 @@ const List = createStackNavigator({
},
});
const App = createAppContainer(List);
const Navigation = createAppContainer(List);
const App = () => {
let [theme, setTheme] = React.useState('light');
return (
<View style={styles.container}>
<Navigation theme={theme} />
<View style={styles.buttonContainer}>
<TouchableOpacity
style={[
styles.button,
{
backgroundColor: ThemeColors[theme].bodyContent,
borderColor: ThemeColors[theme].bodyBorder,
shadowColor: ThemeColors[theme].label,
},
]}
onPress={() => {
setTheme(theme === 'light' ? 'dark' : 'light');
}}
>
<MaterialCommunityIcons
name="theme-light-dark"
size={30}
color={ThemeColors[theme].label}
/>
</TouchableOpacity>
</View>
</View>
);
};
const styles = {
item: {
container: {
flex: 1,
},
buttonContainer: {
position: 'absolute',
bottom: 60,
right: 20,
},
button: {
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.4,
shadowRadius: 2,
borderRadius: 25,
width: 50,
height: 50,
alignItems: 'center',
justifyContent: 'center',
elevation: 5,
borderWidth: 1,
},
itemLight: {
padding: 16,
backgroundColor: '#fff',
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#eee',
},
itemDark: {
padding: 16,
backgroundColor: ThemeColors.dark.bodyContent,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: ThemeColors.dark.bodyBorder,
},
};
registerRootComponent(App);

View File

@@ -1,9 +1,9 @@
{
"expo": {
"name": "React Navigation Tabs Example",
"description": "Demonstrates the various capabilities of react-navigation-tabs: https://github.com/react-navigation/tabs",
"description": "Demonstrates the various capabilities of react-navigation-tabs: https://github.com/react-navigation/react-navigation-tabs",
"slug": "react-navigation-tabs-demos",
"sdkVersion": "32.0.0",
"sdkVersion": "33.0.0",
"version": "1.0.0",
"primaryColor": "#2196f3",
"packagerOpts": {

View File

@@ -9,26 +9,27 @@
},
"main": "App.js",
"dependencies": {
"@expo/vector-icons": "^10.0.1",
"@react-navigation/core": "^3.4.0",
"@react-navigation/native": "^3.3.0",
"expo": "32.0.6",
"react": "16.5.0",
"react-native": "https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz",
"@expo/vector-icons": "^10.0.0",
"@react-navigation/core": "3.5.0-alpha.7",
"@react-navigation/native": "^3.6.0-alpha.1",
"expo": "^33.0.7",
"expo-asset": "^6.0.0",
"expo-constants": "~5.0.1",
"react": "16.8.3",
"react-native": "https://github.com/expo/react-native/archive/sdk-33.0.0.tar.gz",
"react-native-safe-area-view": "^0.13.1",
"react-native-screens": "^1.0.0-alpha.22",
"react-native-tab-view": "^2.0.3",
"react-navigation": "^3.6.0"
"react-native-screens": "1.0.0-alpha.22",
"react-native-tab-view": "^1.2.0",
"react-navigation-stack": "1.4.0-alpha.0"
},
"devDependencies": {
"babel-plugin-module-resolver": "^3.2.0",
"babel-preset-expo": "^5.1.1",
"babel-preset-expo": "^5.0.0",
"glob-to-regexp": "^0.4.0"
},
"resolutions": {
"**/@expo/vector-icons": "10.0.1",
"**/@react-navigation/core": "3.4.0",
"**/hoist-non-react-statics": "2.5.0",
"**/react-native-tab-view": "2.0.3"
"**/react-native-tab-view": "1.2.0",
"**/prop-types": "15.6.1"
}
}

View File

@@ -0,0 +1,78 @@
import * as React from 'react';
import { View, Image, ScrollView, Dimensions, StyleSheet } from 'react-native';
import { withNavigation } from '@react-navigation/core';
@withNavigation
class NavigationAwareScrollView extends React.Component {
componentDidMount() {
this.props.navigation.addListener('willFocus', () => {
this._isFocused = true;
});
this.props.navigation.addListener('willBlur', () => {
this._isFocused = false;
});
this.props.navigation.addListener('refocus', () => {
if (this._isFocused) {
this._component.scrollTo({ x: 0, y: 0 });
}
});
}
setNativeProps(props) {
this._component.setNativeProps(props);
}
_setComponentRef(c) {
this._component = c;
}
getNode() {
return this._component;
}
render() {
return (
<ScrollView
{...this.props}
ref={view => {
this._component = view;
}}
/>
);
}
}
export default function PhotoGrid({ id }) {
const PHOTOS = Array.from({ length: 24 }).map(
(_, i) => `https://unsplash.it/300/300/?random&__id=${id}${i}`
);
return (
<NavigationAwareScrollView contentContainerStyle={styles.content}>
{PHOTOS.map(uri => (
<View key={uri} style={styles.item}>
<Image source={{ uri }} style={styles.photo} />
</View>
))}
</NavigationAwareScrollView>
);
}
const styles = StyleSheet.create({
content: {
flexDirection: 'row',
flexWrap: 'wrap',
padding: 4,
},
item: {
height: Dimensions.get('window').width / 2,
width: '50%',
padding: 4,
},
photo: {
flex: 1,
resizeMode: 'cover',
},
});

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,7 @@ import {
Keyboard,
Platform,
} from 'react-native';
import { ThemeColors, ThemeContext } from '@react-navigation/core';
import { SafeAreaView } from '@react-navigation/native';
import CrossFadeIcon from './CrossFadeIcon';
@@ -20,12 +21,17 @@ type LabelPosition =
| Position
| ((options: { deviceOrientation: Orientation }) => Position);
export type ThemedColor = {
light: string,
dark: string,
};
export type TabBarOptions = {
keyboardHidesTabBar: boolean,
activeTintColor?: string,
inactiveTintColor?: string,
activeBackgroundColor?: string,
inactiveBackgroundColor?: string,
activeTintColor?: string | ThemedColor,
inactiveTintColor?: string | ThemedColor,
activeBackgroundColor?: string | ThemedColor,
inactiveBackgroundColor?: string | ThemedColor,
allowFontScaling: boolean,
showLabel: boolean,
showIcon: boolean,
@@ -93,11 +99,19 @@ class TouchableWithoutFeedbackWrapper extends React.Component<*> {
}
class TabBarBottom extends React.Component<Props, State> {
static contextType = ThemeContext;
static defaultProps = {
keyboardHidesTabBar: true,
activeTintColor: '#007AFF',
activeTintColor: {
light: '#007AFF',
dark: '#fff',
},
inactiveTintColor: {
light: '#8e8e93',
dark: '#7f7f7f',
},
activeBackgroundColor: 'transparent',
inactiveTintColor: '#8E8E93',
inactiveBackgroundColor: 'transparent',
showLabel: true,
showIcon: true,
@@ -166,23 +180,62 @@ class TabBarBottom extends React.Component<Props, State> {
});
};
_getActiveTintColor = () => {
let { activeTintColor } = this.props;
if (!activeTintColor) {
return;
} else if (typeof activeTintColor === 'string') {
return activeTintColor;
}
return activeTintColor[this.context];
};
_getInactiveTintColor = () => {
let { inactiveTintColor } = this.props;
if (!inactiveTintColor) {
return;
} else if (typeof inactiveTintColor === 'string') {
return inactiveTintColor;
}
return inactiveTintColor[this.context];
};
_getActiveBackgroundColor = () => {
let { activeBackgroundColor } = this.props;
if (!activeBackgroundColor) {
return;
} else if (typeof activeBackgroundColor === 'string') {
return activeBackgroundColor;
}
return activeBackgroundColor[this.context];
};
_getInactiveBackgroundColor = () => {
let { inactiveBackgroundColor } = this.props;
if (!inactiveBackgroundColor) {
return;
} else if (typeof inactiveBackgroundColor === 'string') {
return inactiveBackgroundColor;
}
return inactiveBackgroundColor[this.context];
};
_renderLabel = ({ route, focused }) => {
const {
activeTintColor,
inactiveTintColor,
labelStyle,
showLabel,
showIcon,
allowFontScaling,
} = this.props;
const { labelStyle, showLabel, showIcon, allowFontScaling } = this.props;
if (showLabel === false) {
return null;
}
const activeTintColor = this._getActiveTintColor();
const inactiveTintColor = this._getInactiveTintColor();
const label = this.props.getLabelText({ route });
const horizontal = this._shouldUseHorizontalLabels();
const tintColor = focused ? activeTintColor : inactiveTintColor;
const horizontal = this._shouldUseHorizontalLabels();
if (typeof label === 'string') {
return (
@@ -214,20 +267,15 @@ class TabBarBottom extends React.Component<Props, State> {
};
_renderIcon = ({ route, focused }) => {
const {
navigation,
activeTintColor,
inactiveTintColor,
renderIcon,
showIcon,
showLabel,
} = this.props;
const { navigation, renderIcon, showIcon, showLabel } = this.props;
if (showIcon === false) {
return null;
}
const horizontal = this._shouldUseHorizontalLabels();
const activeTintColor = this._getActiveTintColor();
const inactiveTintColor = this._getInactiveTintColor();
const activeOpacity = focused ? 1 : 0;
const inactiveOpacity = focused ? 0 : 1;
@@ -302,8 +350,6 @@ class TabBarBottom extends React.Component<Props, State> {
const {
navigation,
keyboardHidesTabBar,
activeBackgroundColor,
inactiveBackgroundColor,
onTabPress,
onTabLongPress,
safeAreaInset,
@@ -312,9 +358,14 @@ class TabBarBottom extends React.Component<Props, State> {
} = this.props;
const { routes } = navigation.state;
const isDark = this.context === 'dark';
const activeBackgroundColor = this._getActiveBackgroundColor();
const inactiveBackgroundColor = this._getInactiveBackgroundColor();
const tabBarStyle = [
styles.tabBar,
isDark ? styles.tabBarDark : styles.tabBarLight,
this._shouldUseHorizontalLabels() && !Platform.isPad
? styles.tabBarCompact
: styles.tabBarRegular,
@@ -408,11 +459,17 @@ const COMPACT_HEIGHT = 29;
const styles = StyleSheet.create({
tabBar: {
backgroundColor: '#fff',
borderTopWidth: StyleSheet.hairlineWidth,
borderTopColor: 'rgba(0, 0, 0, .3)',
flexDirection: 'row',
},
tabBarLight: {
backgroundColor: ThemeColors.light.header,
borderTopColor: ThemeColors.light.headerBorder,
},
tabBarDark: {
backgroundColor: ThemeColors.dark.header,
borderTopColor: ThemeColors.dark.headerBorder,
},
container: {
left: 0,
right: 0,