Compare commits

..

5 Commits

Author SHA1 Message Date
Satyajit Sahoo
d0e4e1f6fb chore: publish
- @react-navigation/bottom-tabs@5.0.0-alpha.26
 - @react-navigation/drawer@5.0.0-alpha.28
 - @react-navigation/material-bottom-tabs@5.0.0-alpha.25
 - @react-navigation/material-top-tabs@5.0.0-alpha.23
 - @react-navigation/native-stack@5.0.0-alpha.17
 - @react-navigation/native@5.0.0-alpha.19
 - @react-navigation/stack@5.0.0-alpha.44
2019-12-14 22:43:24 +01:00
Satyajit Sahoo
00fc616de0 feat: add custom theme support (#211) 2019-12-14 22:25:25 +01:00
Satyajit Sahoo
703edb3569 chore: fix loading back icon on Android 2019-12-14 06:37:21 +01:00
Satyajit Sahoo
38a38b021a refactor: use function component for bottom tab bar 2019-12-13 17:11:50 +01:00
Satyajit Sahoo
42bc37d2ff chore: update auth flow example 2019-12-12 13:36:43 +01:00
53 changed files with 1176 additions and 770 deletions

View File

@@ -54,6 +54,23 @@ module.exports = {
}, {}), }, {}),
}, },
server: {
enhanceMiddleware: middleware => {
return (req, res, next) => {
const assets = '/packages/stack/src/views/assets';
if (req.url.startsWith(assets)) {
req.url = req.url.replace(
assets,
'/assets/../packages/stack/src/views/assets'
);
}
return middleware(req, res, next);
};
},
},
transformer: { transformer: {
getTransformOptions: async () => ({ getTransformOptions: async () => ({
transform: { transform: {

View File

@@ -20,6 +20,7 @@
"dependencies": { "dependencies": {
"@expo/vector-icons": "^10.0.0", "@expo/vector-icons": "^10.0.0",
"@react-native-community/masked-view": "0.1.5", "@react-native-community/masked-view": "0.1.5",
"color": "^3.1.2",
"expo": "^36.0.0", "expo": "^36.0.0",
"expo-asset": "~8.0.0", "expo-asset": "~8.0.0",
"query-string": "^6.9.0", "query-string": "^6.9.0",

View File

@@ -9,11 +9,27 @@ import {
} from '@react-navigation/stack'; } from '@react-navigation/stack';
type AuthStackParams = { type AuthStackParams = {
splash: undefined; Splash: undefined;
home: undefined; Home: undefined;
'sign-in': undefined; SignIn: undefined;
PostSignOut: undefined;
}; };
const AUTH_CONTEXT_ERROR =
'Authentication context not found. Have your wrapped your components with AuthContext.Consumer?';
const AuthContext = React.createContext<{
signIn: () => void;
signOut: () => void;
}>({
signIn: () => {
throw new Error(AUTH_CONTEXT_ERROR);
},
signOut: () => {
throw new Error(AUTH_CONTEXT_ERROR);
},
});
const SplashScreen = () => { const SplashScreen = () => {
return ( return (
<View style={styles.content}> <View style={styles.content}>
@@ -22,27 +38,27 @@ const SplashScreen = () => {
); );
}; };
const SignInScreen = ({ onSignIn }: { onSignIn: (token: string) => void }) => { const SignInScreen = () => {
const { signIn } = React.useContext(AuthContext);
return ( return (
<View style={styles.content}> <View style={styles.content}>
<TextInput placeholder="Username" style={styles.input} /> <TextInput placeholder="Username" style={styles.input} />
<TextInput placeholder="Password" secureTextEntry style={styles.input} /> <TextInput placeholder="Password" secureTextEntry style={styles.input} />
<Button <Button mode="contained" onPress={signIn} style={styles.button}>
mode="contained"
onPress={() => onSignIn('token')}
style={styles.button}
>
Sign in Sign in
</Button> </Button>
</View> </View>
); );
}; };
const HomeScreen = ({ onSignOut }: { onSignOut: () => void }) => { const HomeScreen = () => {
const { signOut } = React.useContext(AuthContext);
return ( return (
<View style={styles.content}> <View style={styles.content}>
<Title style={styles.text}>Signed in successfully 🎉</Title> <Title style={styles.text}>Signed in successfully 🎉</Title>
<Button onPress={onSignOut} style={styles.button}> <Button onPress={signOut} style={styles.button}>
Sign out Sign out
</Button> </Button>
</View> </View>
@@ -105,36 +121,44 @@ export default function SimpleStackScreen({ navigation }: Props) {
headerShown: false, headerShown: false,
}); });
const authContext = React.useMemo(
() => ({
signIn: () => dispatch({ type: 'SIGN_IN', token: 'dummy-auth-token' }),
signOut: () => dispatch({ type: 'SIGN_OUT' }),
}),
[]
);
return ( return (
<SimpleStack.Navigator <AuthContext.Provider value={authContext}>
screenOptions={{ <SimpleStack.Navigator
headerLeft: () => ( screenOptions={{
<HeaderBackButton onPress={() => navigation.goBack()} /> headerLeft: () => (
), <HeaderBackButton onPress={() => navigation.goBack()} />
}} ),
> }}
{state.isLoading ? ( >
<SimpleStack.Screen {state.isLoading ? (
name="splash" <SimpleStack.Screen
component={SplashScreen} name="Splash"
options={{ title: `Auth Flow` }} component={SplashScreen}
/> options={{ title: 'Auth Flow' }}
) : state.userToken === undefined ? ( />
<SimpleStack.Screen name="sign-in" options={{ title: `Sign in` }}> ) : state.userToken === undefined ? (
{() => ( <SimpleStack.Screen
<SignInScreen name="SignIn"
onSignIn={token => dispatch({ type: 'SIGN_IN', token })} options={{ title: 'Sign in' }}
/> component={SignInScreen}
)} />
</SimpleStack.Screen> ) : (
) : ( <SimpleStack.Screen
<SimpleStack.Screen name="home" options={{ title: 'Home' }}> name="Home"
{() => ( options={{ title: 'Home' }}
<HomeScreen onSignOut={() => dispatch({ type: 'SIGN_OUT' })} /> component={HomeScreen}
)} />
</SimpleStack.Screen> )}
)} </SimpleStack.Navigator>
</SimpleStack.Navigator> </AuthContext.Provider>
); );
} }

View File

@@ -1,5 +1,4 @@
import * as React from 'react'; import * as React from 'react';
import { StyleSheet } from 'react-native';
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs'; import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
import Albums from '../Shared/Albums'; import Albums from '../Shared/Albums';
import Contacts from '../Shared/Contacts'; import Contacts from '../Shared/Contacts';
@@ -15,13 +14,7 @@ const MaterialTopTabs = createMaterialTopTabNavigator<MaterialTopTabParams>();
export default function MaterialTopTabsScreen() { export default function MaterialTopTabsScreen() {
return ( return (
<MaterialTopTabs.Navigator <MaterialTopTabs.Navigator>
tabBarOptions={{
style: styles.tabBar,
labelStyle: styles.tabLabel,
indicatorStyle: styles.tabIndicator,
}}
>
<MaterialTopTabs.Screen <MaterialTopTabs.Screen
name="chat" name="chat"
component={Chat} component={Chat}
@@ -40,15 +33,3 @@ export default function MaterialTopTabsScreen() {
</MaterialTopTabs.Navigator> </MaterialTopTabs.Navigator>
); );
} }
const styles = StyleSheet.create({
tabBar: {
backgroundColor: 'white',
},
tabLabel: {
color: 'black',
},
tabIndicator: {
backgroundColor: 'tomato',
},
});

View File

@@ -7,6 +7,7 @@ import {
RouteProp, RouteProp,
ParamListBase, ParamListBase,
useFocusEffect, useFocusEffect,
useTheme,
} from '@react-navigation/native'; } from '@react-navigation/native';
import { DrawerNavigationProp } from '@react-navigation/drawer'; import { DrawerNavigationProp } from '@react-navigation/drawer';
import { StackNavigationProp } from '@react-navigation/stack'; import { StackNavigationProp } from '@react-navigation/stack';
@@ -23,90 +24,113 @@ type NativeStackParams = {
type NativeStackNavigation = NativeStackNavigationProp<NativeStackParams>; type NativeStackNavigation = NativeStackNavigationProp<NativeStackParams>;
const Title = ({ children }: { children: React.ReactNode }) => {
const { colors } = useTheme();
return <Text style={[styles.title, { color: colors.text }]}>{children}</Text>;
};
const Paragraph = ({ children }: { children: React.ReactNode }) => {
const { colors } = useTheme();
return (
<Text style={[styles.paragraph, { color: colors.text }]}>{children}</Text>
);
};
const ArticleScreen = ({ const ArticleScreen = ({
navigation, navigation,
}: { }: {
navigation: NativeStackNavigation; navigation: NativeStackNavigation;
route: RouteProp<NativeStackParams, 'article'>; route: RouteProp<NativeStackParams, 'article'>;
}) => ( }) => {
<ScrollView style={styles.container} contentContainerStyle={styles.content}> const { colors } = useTheme();
<View style={styles.buttons}>
<Button return (
mode="contained" <ScrollView
onPress={() => navigation.push('album')} style={{ backgroundColor: colors.card }}
style={styles.button} contentContainerStyle={styles.content}
> >
Push album <View style={styles.buttons}>
</Button> <Button
<Button mode="contained"
mode="outlined" onPress={() => navigation.push('album')}
onPress={() => navigation.goBack()} style={styles.button}
style={styles.button} >
> Push album
Go back </Button>
</Button> <Button
</View> mode="outlined"
<Text style={styles.title}>What is Lorem Ipsum?</Text> onPress={() => navigation.goBack()}
<Text style={styles.paragraph}> style={styles.button}
Lorem Ipsum is simply dummy text of the printing and typesetting industry. >
Lorem Ipsum has been the industry&apos;s standard dummy text ever since Go back
the 1500s, when an unknown printer took a galley of type and scrambled it </Button>
to make a type specimen book. It has survived not only five centuries, but </View>
also the leap into electronic typesetting, remaining essentially <Title>What is Lorem Ipsum?</Title>
unchanged. It was popularised in the 1960s with the release of Letraset <Paragraph>
sheets containing Lorem Ipsum passages, and more recently with desktop Lorem Ipsum is simply dummy text of the printing and typesetting
publishing software like Aldus PageMaker including versions of Lorem industry. Lorem Ipsum has been the industry&apos;s standard dummy text
Ipsum. ever since the 1500s, when an unknown printer took a galley of type and
</Text> scrambled it to make a type specimen book. It has survived not only five
<Text style={styles.title}>Where does it come from?</Text> centuries, but also the leap into electronic typesetting, remaining
<Text style={styles.paragraph}> essentially unchanged. It was popularised in the 1960s with the release
Contrary to popular belief, Lorem Ipsum is not simply random text. It has of Letraset sheets containing Lorem Ipsum passages, and more recently
roots in a piece of classical Latin literature from 45 BC, making it over with desktop publishing software like Aldus PageMaker including versions
2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney of Lorem Ipsum.
College in Virginia, looked up one of the more obscure Latin words, </Paragraph>
consectetur, from a Lorem Ipsum passage, and going through the cites of <Title>Where does it come from?</Title>
the word in classical literature, discovered the undoubtable source. Lorem <Paragraph>
Ipsum comes from sections 1.10.32 and 1.10.33 of &quot;de Finibus Bonorum Contrary to popular belief, Lorem Ipsum is not simply random text. It
et Malorum&quot; (The Extremes of Good and Evil) by Cicero, written in 45 has roots in a piece of classical Latin literature from 45 BC, making it
BC. This book is a treatise on the theory of ethics, very popular during over 2000 years old. Richard McClintock, a Latin professor at
the Renaissance. The first line of Lorem Ipsum, &quot;Lorem ipsum dolor Hampden-Sydney College in Virginia, looked up one of the more obscure
sit amet..&quot;, comes from a line in section 1.10.32. Latin words, consectetur, from a Lorem Ipsum passage, and going through
</Text> the cites of the word in classical literature, discovered the
<Text style={styles.paragraph}> undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33
The standard chunk of Lorem Ipsum used since the 1500s is reproduced below of &quot;de Finibus Bonorum et Malorum&quot; (The Extremes of Good and
for those interested. Sections 1.10.32 and 1.10.33 from &quot;de Finibus Evil) by Cicero, written in 45 BC. This book is a treatise on the theory
Bonorum et Malorum&quot; by Cicero are also reproduced in their exact of ethics, very popular during the Renaissance. The first line of Lorem
original form, accompanied by English versions from the 1914 translation Ipsum, &quot;Lorem ipsum dolor sit amet..&quot;, comes from a line in
by H. Rackham. section 1.10.32.
</Text> </Paragraph>
<Text style={styles.title}>Why do we use it?</Text> <Paragraph>
<Text style={styles.paragraph}> The standard chunk of Lorem Ipsum used since the 1500s is reproduced
It is a long established fact that a reader will be distracted by the below for those interested. Sections 1.10.32 and 1.10.33 from &quot;de
readable content of a page when looking at its layout. The point of using Finibus Bonorum et Malorum&quot; by Cicero are also reproduced in their
Lorem Ipsum is that it has a more-or-less normal distribution of letters, exact original form, accompanied by English versions from the 1914
as opposed to using &quot;Content here, content here&quot;, making it look translation by H. Rackham.
like readable English. Many desktop publishing packages and web page </Paragraph>
editors now use Lorem Ipsum as their default model text, and a search for <Title>Why do we use it?</Title>
&quot;lorem ipsum&quot; will uncover many web sites still in their <Paragraph>
infancy. Various versions have evolved over the years, sometimes by It is a long established fact that a reader will be distracted by the
accident, sometimes on purpose (injected humour and the like). readable content of a page when looking at its layout. The point of
</Text> using Lorem Ipsum is that it has a more-or-less normal distribution of
<Text style={styles.title}>Where can I get some?</Text> letters, as opposed to using &quot;Content here, content here&quot;,
<Text style={styles.paragraph}> making it look like readable English. Many desktop publishing packages
There are many variations of passages of Lorem Ipsum available, but the and web page editors now use Lorem Ipsum as their default model text,
majority have suffered alteration in some form, by injected humour, or and a search for &quot;lorem ipsum&quot; will uncover many web sites
randomised words which don&apos;t look even slightly believable. If you still in their infancy. Various versions have evolved over the years,
are going to use a passage of Lorem Ipsum, you need to be sure there sometimes by accident, sometimes on purpose (injected humour and the
isn&apos;t anything embarrassing hidden in the middle of text. All the like).
Lorem Ipsum generators on the Internet tend to repeat predefined chunks as </Paragraph>
necessary, making this the first true generator on the Internet. It uses a <Title>Where can I get some?</Title>
dictionary of over 200 Latin words, combined with a handful of model <Paragraph>
sentence structures, to generate Lorem Ipsum which looks reasonable. The There are many variations of passages of Lorem Ipsum available, but the
generated Lorem Ipsum is therefore always free from repetition, injected majority have suffered alteration in some form, by injected humour, or
humour, or non-characteristic words etc. randomised words which don&apos;t look even slightly believable. If you
</Text> are going to use a passage of Lorem Ipsum, you need to be sure there
</ScrollView> isn&apos;t anything embarrassing hidden in the middle of text. All the
); Lorem Ipsum generators on the Internet tend to repeat predefined chunks
as necessary, making this the first true generator on the Internet. It
uses a dictionary of over 200 Latin words, combined with a handful of
model sentence structures, to generate Lorem Ipsum which looks
reasonable. The generated Lorem Ipsum is therefore always free from
repetition, injected humour, or non-characteristic words etc.
</Paragraph>
</ScrollView>
);
};
const AlbumsScreen = ({ const AlbumsScreen = ({
navigation, navigation,
@@ -191,21 +215,16 @@ const styles = StyleSheet.create({
button: { button: {
margin: 8, margin: 8,
}, },
container: {
backgroundColor: 'white',
},
content: { content: {
paddingVertical: 16, paddingVertical: 16,
}, },
title: { title: {
color: '#000',
fontWeight: 'bold', fontWeight: 'bold',
fontSize: 24, fontSize: 24,
marginVertical: 8, marginVertical: 8,
marginHorizontal: 16, marginHorizontal: 16,
}, },
paragraph: { paragraph: {
color: '#000',
fontSize: 16, fontSize: 16,
lineHeight: 24, lineHeight: 24,
marginVertical: 8, marginVertical: 8,

View File

@@ -38,7 +38,7 @@ export default function Albums() {
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
backgroundColor: '#343C46', backgroundColor: '#000',
}, },
content: { content: {
flexDirection: 'row', flexDirection: 'row',

View File

@@ -1,6 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import { View, Text, Image, ScrollView, StyleSheet } from 'react-native'; import { View, Text, Image, ScrollView, StyleSheet } from 'react-native';
import { useScrollToTop } from '@react-navigation/native'; import { useScrollToTop, useTheme } from '@react-navigation/native';
type Props = { type Props = {
date?: string; date?: string;
@@ -19,10 +19,12 @@ export default function Article({
useScrollToTop(ref); useScrollToTop(ref);
const { colors } = useTheme();
return ( return (
<ScrollView <ScrollView
ref={ref} ref={ref}
style={styles.container} style={{ backgroundColor: colors.card }}
contentContainerStyle={styles.content} contentContainerStyle={styles.content}
> >
<View style={styles.author}> <View style={styles.author}>
@@ -31,24 +33,26 @@ export default function Article({
source={require('../../assets/avatar-1.png')} source={require('../../assets/avatar-1.png')}
/> />
<View style={styles.meta}> <View style={styles.meta}>
<Text style={styles.name}>{author.name}</Text> <Text style={[styles.name, { color: colors.text }]}>
<Text style={styles.timestamp}>{date}</Text> {author.name}
</Text>
<Text style={[styles.timestamp, { color: colors.text }]}>{date}</Text>
</View> </View>
</View> </View>
<Text style={styles.title}>Lorem Ipsum</Text> <Text style={[styles.title, { color: colors.text }]}>Lorem Ipsum</Text>
<Text style={styles.paragraph}> <Text style={[styles.paragraph, { color: colors.text }]}>
Contrary to popular belief, Lorem Ipsum is not simply random text. It Contrary to popular belief, Lorem Ipsum is not simply random text. It
has roots in a piece of classical Latin literature from 45 BC, making it has roots in a piece of classical Latin literature from 45 BC, making it
over 2000 years old. over 2000 years old.
</Text> </Text>
<Image style={styles.image} source={require('../../assets/book.jpg')} /> <Image style={styles.image} source={require('../../assets/book.jpg')} />
<Text style={styles.paragraph}> <Text style={[styles.paragraph, { color: colors.text }]}>
Richard McClintock, a Latin professor at Hampden-Sydney College in Richard McClintock, a Latin professor at Hampden-Sydney College in
Virginia, looked up one of the more obscure Latin words, consectetur, Virginia, looked up one of the more obscure Latin words, consectetur,
from a Lorem Ipsum passage, and going through the cites of the word in from a Lorem Ipsum passage, and going through the cites of the word in
classical literature, discovered the undoubtable source. classical literature, discovered the undoubtable source.
</Text> </Text>
<Text style={styles.paragraph}> <Text style={[styles.paragraph, { color: colors.text }]}>
Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of &quot;de Finibus Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of &quot;de Finibus
Bonorum et Malorum&quot; (The Extremes of Good and Evil) by Cicero, Bonorum et Malorum&quot; (The Extremes of Good and Evil) by Cicero,
written in 45 BC. This book is a treatise on the theory of ethics, very written in 45 BC. This book is a treatise on the theory of ethics, very
@@ -61,9 +65,6 @@ export default function Article({
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: {
backgroundColor: 'white',
},
content: { content: {
paddingVertical: 16, paddingVertical: 16,
}, },
@@ -77,13 +78,12 @@ const styles = StyleSheet.create({
justifyContent: 'center', justifyContent: 'center',
}, },
name: { name: {
color: '#000',
fontWeight: 'bold', fontWeight: 'bold',
fontSize: 16, fontSize: 16,
lineHeight: 24, lineHeight: 24,
}, },
timestamp: { timestamp: {
color: '#999', opacity: 0.5,
fontSize: 14, fontSize: 14,
lineHeight: 21, lineHeight: 21,
}, },
@@ -93,14 +93,12 @@ const styles = StyleSheet.create({
borderRadius: 24, borderRadius: 24,
}, },
title: { title: {
color: '#000',
fontWeight: 'bold', fontWeight: 'bold',
fontSize: 36, fontSize: 36,
marginVertical: 8, marginVertical: 8,
marginHorizontal: 16, marginHorizontal: 16,
}, },
paragraph: { paragraph: {
color: '#000',
fontSize: 16, fontSize: 16,
lineHeight: 24, lineHeight: 24,
marginVertical: 8, marginVertical: 8,

View File

@@ -7,7 +7,8 @@ import {
ScrollView, ScrollView,
StyleSheet, StyleSheet,
} from 'react-native'; } from 'react-native';
import { useScrollToTop } from '@react-navigation/native'; import { useScrollToTop, useTheme } from '@react-navigation/native';
import Color from 'color';
const MESSAGES = [ const MESSAGES = [
'okay', 'okay',
@@ -21,6 +22,8 @@ export default function Chat() {
useScrollToTop(ref); useScrollToTop(ref);
const { colors } = useTheme();
return ( return (
<View style={styles.container}> <View style={styles.container}>
<ScrollView <ScrollView
@@ -45,9 +48,12 @@ export default function Chat() {
} }
/> />
<View <View
style={[styles.bubble, odd ? styles.received : styles.sent]} style={[
styles.bubble,
{ backgroundColor: odd ? colors.primary : colors.card },
]}
> >
<Text style={odd ? styles.receivedText : styles.sentText}> <Text style={{ color: odd ? 'white' : colors.text }}>
{text} {text}
</Text> </Text>
</View> </View>
@@ -56,7 +62,14 @@ export default function Chat() {
})} })}
</ScrollView> </ScrollView>
<TextInput <TextInput
style={styles.input} style={[
styles.input,
{ backgroundColor: colors.card, color: colors.text },
]}
placeholderTextColor={Color(colors.text)
.alpha(0.5)
.rgb()
.string()}
placeholder="Write a message" placeholder="Write a message"
underlineColorAndroid="transparent" underlineColorAndroid="transparent"
/> />
@@ -67,7 +80,6 @@ export default function Chat() {
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
backgroundColor: '#eceff1',
}, },
inverted: { inverted: {
transform: [{ scaleY: -1 }], transform: [{ scaleY: -1 }],
@@ -97,22 +109,9 @@ const styles = StyleSheet.create({
paddingHorizontal: 16, paddingHorizontal: 16,
borderRadius: 20, borderRadius: 20,
}, },
sent: {
backgroundColor: '#cfd8dc',
},
received: {
backgroundColor: '#2196F3',
},
sentText: {
color: 'black',
},
receivedText: {
color: 'white',
},
input: { input: {
height: 48, height: 48,
paddingVertical: 12, paddingVertical: 12,
paddingHorizontal: 24, paddingHorizontal: 24,
backgroundColor: 'white',
}, },
}); });

View File

@@ -1,6 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import { View, Text, FlatList, StyleSheet } from 'react-native'; import { View, Text, FlatList, StyleSheet } from 'react-native';
import { useScrollToTop } from '@react-navigation/native'; import { useScrollToTop, useTheme } from '@react-navigation/native';
type Item = { name: string; number: number }; type Item = { name: string; number: number };
@@ -57,27 +57,35 @@ const CONTACTS: Item[] = [
{ name: 'Vincent Sandoval', number: 2606111495 }, { name: 'Vincent Sandoval', number: 2606111495 },
]; ];
class ContactItem extends React.PureComponent<{ const ContactItem = React.memo(
item: { name: string; number: number }; ({ item }: { item: { name: string; number: number } }) => {
}> { const { colors } = useTheme();
render() {
const { item } = this.props;
return ( return (
<View style={styles.item}> <View style={[styles.item, { backgroundColor: colors.card }]}>
<View style={styles.avatar}> <View style={styles.avatar}>
<Text style={styles.letter}> <Text style={styles.letter}>
{item.name.slice(0, 1).toUpperCase()} {item.name.slice(0, 1).toUpperCase()}
</Text> </Text>
</View> </View>
<View style={styles.details}> <View style={styles.details}>
<Text style={styles.name}>{item.name}</Text> <Text style={[styles.name, { color: colors.text }]}>{item.name}</Text>
<Text style={styles.number}>{item.number}</Text> <Text style={[styles.number, { color: colors.text, opacity: 0.5 }]}>
{item.number}
</Text>
</View> </View>
</View> </View>
); );
} }
} );
const ItemSeparator = () => {
const { colors } = useTheme();
return (
<View style={[styles.separator, { backgroundColor: colors.border }]} />
);
};
export default function Contacts() { export default function Contacts() {
const ref = React.useRef<FlatList<Item>>(null); const ref = React.useRef<FlatList<Item>>(null);
@@ -86,8 +94,6 @@ export default function Contacts() {
const renderItem = ({ item }: { item: Item }) => <ContactItem item={item} />; const renderItem = ({ item }: { item: Item }) => <ContactItem item={item} />;
const ItemSeparator = () => <View style={styles.separator} />;
return ( return (
<FlatList <FlatList
ref={ref} ref={ref}
@@ -101,7 +107,6 @@ export default function Contacts() {
const styles = StyleSheet.create({ const styles = StyleSheet.create({
item: { item: {
backgroundColor: 'white',
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
padding: 8, padding: 8,
@@ -124,14 +129,11 @@ const styles = StyleSheet.create({
name: { name: {
fontWeight: 'bold', fontWeight: 'bold',
fontSize: 14, fontSize: 14,
color: 'black',
}, },
number: { number: {
fontSize: 12, fontSize: 12,
color: '#999',
}, },
separator: { separator: {
height: StyleSheet.hairlineWidth, height: StyleSheet.hairlineWidth,
backgroundColor: 'rgba(0, 0, 0, .08)',
}, },
}); });

View File

@@ -1,7 +1,23 @@
import * as React from 'react'; import * as React from 'react';
import { ScrollView, AsyncStorage, YellowBox } from 'react-native'; import {
View,
ScrollView,
AsyncStorage,
YellowBox,
Platform,
StatusBar,
} from 'react-native';
import { MaterialIcons } from '@expo/vector-icons'; import { MaterialIcons } from '@expo/vector-icons';
import { Appbar, List } from 'react-native-paper'; import {
Provider as PaperProvider,
DefaultTheme as PaperLightTheme,
DarkTheme as PaperDarkTheme,
Subheading,
Appbar,
List,
Switch,
Divider,
} from 'react-native-paper';
import { Asset } from 'expo-asset'; import { Asset } from 'expo-asset';
import { import {
InitialState, InitialState,
@@ -9,6 +25,8 @@ import {
useLinking, useLinking,
NavigationContainerRef, NavigationContainerRef,
NavigationNativeContainer, NavigationNativeContainer,
DefaultTheme,
DarkTheme,
} from '@react-navigation/native'; } from '@react-navigation/native';
import { import {
createDrawerNavigator, createDrawerNavigator,
@@ -72,7 +90,8 @@ const SCREENS = {
const Drawer = createDrawerNavigator<RootDrawerParamList>(); const Drawer = createDrawerNavigator<RootDrawerParamList>();
const Stack = createStackNavigator<RootStackParamList>(); const Stack = createStackNavigator<RootStackParamList>();
const PERSISTENCE_KEY = 'NAVIGATION_STATE'; const NAVIGATION_PERSISTENCE_KEY = 'NAVIGATION_STATE';
const THEME_PERSISTENCE_KEY = 'THEME_TYPE';
Asset.loadAsync(StackAssets); Asset.loadAsync(StackAssets);
@@ -102,6 +121,8 @@ export default function App() {
}, },
}); });
const [theme, setTheme] = React.useState(DefaultTheme);
const [isReady, setIsReady] = React.useState(false); const [isReady, setIsReady] = React.useState(false);
const [initialState, setInitialState] = React.useState< const [initialState, setInitialState] = React.useState<
InitialState | undefined InitialState | undefined
@@ -113,7 +134,9 @@ export default function App() {
let state = await getInitialState(); let state = await getInitialState();
if (state === undefined) { if (state === undefined) {
const savedState = await AsyncStorage.getItem(PERSISTENCE_KEY); const savedState = await AsyncStorage.getItem(
NAVIGATION_PERSISTENCE_KEY
);
state = savedState ? JSON.parse(savedState) : undefined; state = savedState ? JSON.parse(savedState) : undefined;
} }
@@ -121,6 +144,14 @@ export default function App() {
setInitialState(state); setInitialState(state);
} }
} finally { } finally {
try {
const themeName = await AsyncStorage.getItem(THEME_PERSISTENCE_KEY);
setTheme(themeName === 'dark' ? DarkTheme : DefaultTheme);
} catch (e) {
// Ignore
}
setIsReady(true); setIsReady(true);
} }
}; };
@@ -128,78 +159,126 @@ export default function App() {
restoreState(); restoreState();
}, [getInitialState]); }, [getInitialState]);
const paperTheme = React.useMemo(() => {
const t = theme.dark ? PaperDarkTheme : PaperLightTheme;
return {
...t,
colors: {
...t.colors,
...theme.colors,
surface: theme.colors.card,
accent: theme.dark ? 'rgb(255, 55, 95)' : 'rgb(255, 45, 85)',
},
};
}, [theme.colors, theme.dark]);
if (!isReady) { if (!isReady) {
return null; return null;
} }
return ( return (
<NavigationNativeContainer <PaperProvider theme={paperTheme}>
ref={containerRef} {Platform.OS === 'ios' && (
initialState={initialState} <StatusBar barStyle={theme.dark ? 'light-content' : 'dark-content'} />
onStateChange={state => )}
AsyncStorage.setItem(PERSISTENCE_KEY, JSON.stringify(state)) <NavigationNativeContainer
} ref={containerRef}
> initialState={initialState}
<Drawer.Navigator> onStateChange={state =>
<Drawer.Screen AsyncStorage.setItem(
name="Root" NAVIGATION_PERSISTENCE_KEY,
options={{ JSON.stringify(state)
title: 'Examples', )
drawerIcon: ({ size, color }) => ( }
<MaterialIcons size={size} color={color} name="folder" /> theme={theme}
), >
}} <Drawer.Navigator>
> <Drawer.Screen
{({ name="Root"
navigation, options={{
}: { title: 'Examples',
navigation: DrawerNavigationProp<RootDrawerParamList>; drawerIcon: ({ size, color }) => (
}) => ( <MaterialIcons size={size} color={color} name="folder" />
<Stack.Navigator> ),
<Stack.Screen }}
name="Home" >
options={{ {({
title: 'Examples', navigation,
headerLeft: () => ( }: {
<Appbar.Action navigation: DrawerNavigationProp<RootDrawerParamList>;
icon="menu" }) => (
onPress={() => navigation.toggleDrawer()} <Stack.Navigator>
/> <Stack.Screen
), name="Home"
}} options={{
> title: 'Examples',
{({ headerLeft: () => (
navigation, <Appbar.Action
}: { color={theme.colors.text}
navigation: StackNavigationProp<RootStackParamList>; icon="menu"
}) => ( onPress={() => navigation.toggleDrawer()}
<ScrollView> />
{(Object.keys(SCREENS) as Array<keyof typeof SCREENS>).map( ),
name => ( }}
>
{({
navigation,
}: {
navigation: StackNavigationProp<RootStackParamList>;
}) => (
<ScrollView
style={{ backgroundColor: theme.colors.background }}
>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
padding: 16,
}}
>
<Subheading>Dark theme</Subheading>
<Switch
value={theme.dark}
onValueChange={() => {
AsyncStorage.setItem(
THEME_PERSISTENCE_KEY,
theme.dark ? 'light' : 'dark'
);
setTheme(t => (t.dark ? DefaultTheme : DarkTheme));
}}
/>
</View>
<Divider />
{(Object.keys(SCREENS) as Array<
keyof typeof SCREENS
>).map(name => (
<List.Item <List.Item
key={name} key={name}
title={SCREENS[name].title} title={SCREENS[name].title}
onPress={() => navigation.push(name)} onPress={() => navigation.push(name)}
/> />
) ))}
)} </ScrollView>
</ScrollView> )}
</Stack.Screen>
{(Object.keys(SCREENS) as Array<keyof typeof SCREENS>).map(
name => (
<Stack.Screen
key={name}
name={name}
component={SCREENS[name].component}
options={{ title: SCREENS[name].title }}
/>
)
)} )}
</Stack.Screen> </Stack.Navigator>
{(Object.keys(SCREENS) as Array<keyof typeof SCREENS>).map( )}
name => ( </Drawer.Screen>
<Stack.Screen </Drawer.Navigator>
key={name} </NavigationNativeContainer>
name={name} </PaperProvider>
component={SCREENS[name].component}
options={{ title: SCREENS[name].title }}
/>
)
)}
</Stack.Navigator>
)}
</Drawer.Screen>
</Drawer.Navigator>
</NavigationNativeContainer>
); );
} }

View File

@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.0.0-alpha.26](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/bottom-tabs@5.0.0-alpha.25...@react-navigation/bottom-tabs@5.0.0-alpha.26) (2019-12-14)
### Features
* add custom theme support ([#211](https://github.com/react-navigation/navigation-ex/issues/211)) ([00fc616](https://github.com/react-navigation/navigation-ex/commit/00fc616de0572bade8aa85052cdc8290360b1d7f))
# [5.0.0-alpha.25](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/bottom-tabs@5.0.0-alpha.24...@react-navigation/bottom-tabs@5.0.0-alpha.25) (2019-12-11) # [5.0.0-alpha.25](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/bottom-tabs@5.0.0-alpha.24...@react-navigation/bottom-tabs@5.0.0-alpha.25) (2019-12-11)
**Note:** Version bump only for package @react-navigation/bottom-tabs **Note:** Version bump only for package @react-navigation/bottom-tabs

View File

@@ -10,7 +10,7 @@
"android", "android",
"tab" "tab"
], ],
"version": "5.0.0-alpha.25", "version": "5.0.0-alpha.26",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -33,10 +33,12 @@
"clean": "del lib" "clean": "del lib"
}, },
"dependencies": { "dependencies": {
"@react-navigation/routers": "^5.0.0-alpha.16" "@react-navigation/routers": "^5.0.0-alpha.16",
"color": "^3.1.2"
}, },
"devDependencies": { "devDependencies": {
"@react-native-community/bob": "^0.7.0", "@react-native-community/bob": "^0.7.0",
"@types/color": "^3.0.0",
"@types/react": "^16.9.11", "@types/react": "^16.9.11",
"@types/react-native": "^0.60.22", "@types/react-native": "^0.60.22",
"del-cli": "^3.0.0", "del-cli": "^3.0.0",

View File

@@ -10,22 +10,15 @@ import {
Dimensions, Dimensions,
} from 'react-native'; } from 'react-native';
import { import {
Route,
NavigationContext, NavigationContext,
CommonActions, CommonActions,
useTheme,
} from '@react-navigation/native'; } from '@react-navigation/native';
import { SafeAreaConsumer } from 'react-native-safe-area-context'; import { SafeAreaConsumer } from 'react-native-safe-area-context';
import BottomTabItem from './BottomTabItem'; import BottomTabItem from './BottomTabItem';
import { BottomTabBarProps } from '../types'; import { BottomTabBarProps } from '../types';
type State = {
dimensions: { height: number; width: number };
layout: { height: number; width: number };
keyboard: boolean;
visible: Animated.Value;
};
type Props = BottomTabBarProps & { type Props = BottomTabBarProps & {
activeTintColor?: string; activeTintColor?: string;
inactiveTintColor?: string; inactiveTintColor?: string;
@@ -34,97 +27,101 @@ type Props = BottomTabBarProps & {
const DEFAULT_TABBAR_HEIGHT = 50; const DEFAULT_TABBAR_HEIGHT = 50;
const DEFAULT_MAX_TAB_ITEM_WIDTH = 125; const DEFAULT_MAX_TAB_ITEM_WIDTH = 125;
export default class BottomTabBar extends React.Component<Props, State> { export default function BottomTabBar({
static defaultProps = { state,
keyboardHidesTabBar: false, navigation,
adaptive: true, descriptors,
}; activeBackgroundColor,
activeTintColor,
adaptive = true,
allowFontScaling,
inactiveBackgroundColor,
inactiveTintColor,
keyboardHidesTabBar = false,
labelPosition,
labelStyle,
showIcon,
showLabel,
style,
tabStyle,
}: Props) {
const { colors } = useTheme();
state = { const [dimensions, setDimensions] = React.useState(Dimensions.get('window'));
dimensions: Dimensions.get('window'), const [layout, setLayout] = React.useState({ height: 0, width: 0 });
layout: { height: 0, width: 0 }, const [keyboardShown, setKeyboardShown] = React.useState(false);
keyboard: false,
visible: new Animated.Value(1),
};
componentDidMount() { const [visible] = React.useState(() => new Animated.Value(0));
Dimensions.addEventListener('change', this.handleOrientationChange);
if (Platform.OS === 'ios') { const { routes } = state;
Keyboard.addListener('keyboardWillShow', this.handleKeyboardShow);
Keyboard.addListener('keyboardWillHide', this.handleKeyboardHide);
} else {
Keyboard.addListener('keyboardDidShow', this.handleKeyboardShow);
Keyboard.addListener('keyboardDidHide', this.handleKeyboardHide);
}
}
componentWillUnmount() { React.useEffect(() => {
Dimensions.removeEventListener('change', this.handleOrientationChange); if (keyboardShown) {
Animated.timing(visible, {
if (Platform.OS === 'ios') {
Keyboard.removeListener('keyboardWillShow', this.handleKeyboardShow);
Keyboard.removeListener('keyboardWillHide', this.handleKeyboardHide);
} else {
Keyboard.removeListener('keyboardDidShow', this.handleKeyboardShow);
Keyboard.removeListener('keyboardDidHide', this.handleKeyboardHide);
}
}
private handleOrientationChange = ({ window }: { window: ScaledSize }) => {
this.setState({ dimensions: window });
};
private handleKeyboardShow = () =>
this.setState({ keyboard: true }, () =>
Animated.timing(this.state.visible, {
toValue: 0, toValue: 0,
duration: 200, duration: 200,
useNativeDriver: true, useNativeDriver: true,
}).start() }).start();
); }
}, [keyboardShown, visible]);
private handleKeyboardHide = () => React.useEffect(() => {
Animated.timing(this.state.visible, { const handleOrientationChange = ({ window }: { window: ScaledSize }) => {
toValue: 1, setDimensions(window);
duration: 250, };
useNativeDriver: true,
}).start(({ finished }) => {
if (finished) {
this.setState({ keyboard: false });
}
});
private handleLayout = (e: LayoutChangeEvent) => { const handleKeyboardShow = () => setKeyboardShown(true);
const { layout } = this.state;
const { height, width } = e.nativeEvent.layout;
if (height === layout.height && width === layout.width) { const handleKeyboardHide = () =>
return; Animated.timing(visible, {
toValue: 1,
duration: 250,
useNativeDriver: true,
}).start(({ finished }) => {
if (finished) {
setKeyboardShown(false);
}
});
Dimensions.addEventListener('change', handleOrientationChange);
if (Platform.OS === 'ios') {
Keyboard.addListener('keyboardWillShow', handleKeyboardShow);
Keyboard.addListener('keyboardWillHide', handleKeyboardHide);
} else {
Keyboard.addListener('keyboardDidShow', handleKeyboardShow);
Keyboard.addListener('keyboardDidHide', handleKeyboardHide);
} }
this.setState({ return () => {
layout: { Dimensions.removeEventListener('change', handleOrientationChange);
height,
width, if (Platform.OS === 'ios') {
}, Keyboard.removeListener('keyboardWillShow', handleKeyboardShow);
Keyboard.removeListener('keyboardWillHide', handleKeyboardHide);
} else {
Keyboard.removeListener('keyboardDidShow', handleKeyboardShow);
Keyboard.removeListener('keyboardDidHide', handleKeyboardHide);
}
};
}, [visible]);
const handleLayout = (e: LayoutChangeEvent) => {
const { height, width } = e.nativeEvent.layout;
setLayout(layout => {
if (height === layout.height && width === layout.width) {
return layout;
} else {
return {
height,
width,
};
}
}); });
}; };
private getLabelText = ({ route }: { route: Route<string> }) => { const shouldUseHorizontalLabels = () => {
const { options } = this.props.descriptors[route.key];
return options.tabBarLabel !== undefined
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name;
};
private shouldUseHorizontalLabels = () => {
const { state, adaptive, tabStyle, labelPosition } = this.props;
const { dimensions } = this.state;
const { routes } = state;
const isLandscape = dimensions.width > dimensions.height; const isLandscape = dimensions.width > dimensions.height;
if (labelPosition) { if (labelPosition) {
@@ -165,126 +162,114 @@ export default class BottomTabBar extends React.Component<Props, State> {
} }
}; };
render() { return (
const { <SafeAreaConsumer>
state, {insets => (
navigation, <Animated.View
descriptors, style={[
keyboardHidesTabBar, styles.tabBar,
activeTintColor, {
inactiveTintColor, backgroundColor: colors.card,
activeBackgroundColor, borderTopColor: colors.border,
inactiveBackgroundColor, },
labelStyle, keyboardHidesTabBar
showIcon, ? {
showLabel, // When the keyboard is shown, slide down the tab bar
allowFontScaling, transform: [
style, {
tabStyle, translateY: visible.interpolate({
} = this.props; inputRange: [0, 1],
const { routes } = state; outputRange: [layout.height, 0],
}),
},
],
// Absolutely position the tab bar so that the content is below it
// This is needed to avoid gap at bottom when the tab bar is hidden
position: keyboardShown ? 'absolute' : null,
}
: null,
{
height: DEFAULT_TABBAR_HEIGHT + (insets ? insets.bottom : 0),
paddingBottom: insets ? insets.bottom : 0,
},
style,
]}
pointerEvents={keyboardHidesTabBar && keyboardShown ? 'none' : 'auto'}
>
<View style={styles.content} onLayout={handleLayout}>
{routes.map((route, index) => {
const focused = index === state.index;
const { options } = descriptors[route.key];
return ( const onPress = () => {
<SafeAreaConsumer> const event = navigation.emit({
{insets => ( type: 'tabPress',
<Animated.View target: route.key,
style={[ });
styles.tabBar,
keyboardHidesTabBar
? {
// When the keyboard is shown, slide down the tab bar
transform: [
{
translateY: this.state.visible.interpolate({
inputRange: [0, 1],
outputRange: [this.state.layout.height, 0],
}),
},
],
// Absolutely position the tab bar so that the content is below it
// This is needed to avoid gap at bottom when the tab bar is hidden
position: this.state.keyboard ? 'absolute' : null,
}
: null,
{
height: DEFAULT_TABBAR_HEIGHT + (insets ? insets.bottom : 0),
paddingBottom: insets ? insets.bottom : 0,
},
style,
]}
pointerEvents={
keyboardHidesTabBar && this.state.keyboard ? 'none' : 'auto'
}
>
<View style={styles.content} onLayout={this.handleLayout}>
{routes.map((route, index) => {
const focused = index === state.index;
const { options } = descriptors[route.key];
const onPress = () => { if (!focused && !event.defaultPrevented) {
const event = navigation.emit({ navigation.dispatch({
type: 'tabPress', ...CommonActions.navigate(route.name),
target: route.key, target: state.key,
}); });
}
};
if (!focused && !event.defaultPrevented) { const onLongPress = () => {
navigation.dispatch({ navigation.emit({
...CommonActions.navigate(route.name), type: 'tabLongPress',
target: state.key, target: route.key,
}); });
} };
};
const onLongPress = () => { const label =
navigation.emit({ options.tabBarLabel !== undefined
type: 'tabLongPress', ? options.tabBarLabel
target: route.key, : options.title !== undefined
}); ? options.title
}; : route.name;
const label = this.getLabelText({ route }); const accessibilityLabel =
const accessibilityLabel = options.tabBarAccessibilityLabel !== undefined
options.tabBarAccessibilityLabel !== undefined ? options.tabBarAccessibilityLabel
? options.tabBarAccessibilityLabel : typeof label === 'string'
: typeof label === 'string' ? `${label}, tab, ${index + 1} of ${routes.length}`
? `${label}, tab, ${index + 1} of ${routes.length}` : undefined;
: undefined;
return ( return (
<NavigationContext.Provider <NavigationContext.Provider
key={route.key} key={route.key}
value={descriptors[route.key].navigation} value={descriptors[route.key].navigation}
> >
<BottomTabItem <BottomTabItem
route={route} route={route}
focused={focused} focused={focused}
horizontal={this.shouldUseHorizontalLabels()} horizontal={shouldUseHorizontalLabels()}
onPress={onPress} onPress={onPress}
onLongPress={onLongPress} onLongPress={onLongPress}
accessibilityLabel={accessibilityLabel} accessibilityLabel={accessibilityLabel}
testID={options.tabBarTestID} testID={options.tabBarTestID}
allowFontScaling={allowFontScaling} allowFontScaling={allowFontScaling}
activeTintColor={activeTintColor} activeTintColor={activeTintColor}
inactiveTintColor={inactiveTintColor} inactiveTintColor={inactiveTintColor}
activeBackgroundColor={activeBackgroundColor} activeBackgroundColor={activeBackgroundColor}
inactiveBackgroundColor={inactiveBackgroundColor} inactiveBackgroundColor={inactiveBackgroundColor}
button={options.tabBarButton} button={options.tabBarButton}
icon={options.tabBarIcon} icon={options.tabBarIcon}
label={label} label={label}
showIcon={showIcon} showIcon={showIcon}
showLabel={showLabel} showLabel={showLabel}
labelStyle={labelStyle} labelStyle={labelStyle}
style={tabStyle} style={tabStyle}
/> />
</NavigationContext.Provider> </NavigationContext.Provider>
); );
})} })}
</View> </View>
</Animated.View> </Animated.View>
)} )}
</SafeAreaConsumer> </SafeAreaConsumer>
); );
}
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
@@ -292,9 +277,7 @@ const styles = StyleSheet.create({
left: 0, left: 0,
right: 0, right: 0,
bottom: 0, bottom: 0,
backgroundColor: '#fff',
borderTopWidth: StyleSheet.hairlineWidth, borderTopWidth: StyleSheet.hairlineWidth,
borderTopColor: 'rgba(0, 0, 0, .3)',
elevation: 8, elevation: 8,
}, },
content: { content: {

View File

@@ -8,7 +8,8 @@ import {
ViewStyle, ViewStyle,
TextStyle, TextStyle,
} from 'react-native'; } from 'react-native';
import { Route } from '@react-navigation/native'; import { Route, useTheme } from '@react-navigation/native';
import Color from 'color';
import TabBarIcon from './TabBarIcon'; import TabBarIcon from './TabBarIcon';
import { BottomTabBarButtonProps } from '../types'; import { BottomTabBarButtonProps } from '../types';
@@ -113,8 +114,8 @@ export default function BottomTabBarItem({
onPress, onPress,
onLongPress, onLongPress,
horizontal, horizontal,
activeTintColor = '#007AFF', activeTintColor: customActiveTintColor,
inactiveTintColor = '#8E8E93', inactiveTintColor: customInactiveTintColor,
activeBackgroundColor = 'transparent', activeBackgroundColor = 'transparent',
inactiveBackgroundColor = 'transparent', inactiveBackgroundColor = 'transparent',
showLabel = true, showLabel = true,
@@ -123,6 +124,20 @@ export default function BottomTabBarItem({
labelStyle, labelStyle,
style, style,
}: Props) { }: Props) {
const { colors } = useTheme();
const activeTintColor =
customActiveTintColor === undefined
? colors.primary
: customActiveTintColor;
const inactiveTintColor =
customInactiveTintColor === undefined
? Color(colors.text)
.mix(Color(colors.card), 0.5)
.hex()
: customInactiveTintColor;
const renderLabel = ({ focused }: { focused: boolean }) => { const renderLabel = ({ focused }: { focused: boolean }) => {
if (showLabel === false) { if (showLabel === false) {
return null; return null;

View File

@@ -2,6 +2,7 @@ import * as React from 'react';
import { View, StyleSheet } from 'react-native'; import { View, StyleSheet } from 'react-native';
import { TabNavigationState } from '@react-navigation/routers'; import { TabNavigationState } from '@react-navigation/routers';
import { useTheme } from '@react-navigation/native';
// eslint-disable-next-line import/no-unresolved // eslint-disable-next-line import/no-unresolved
import { ScreenContainer } from 'react-native-screens'; import { ScreenContainer } from 'react-native-screens';
@@ -25,6 +26,26 @@ type State = {
loaded: number[]; loaded: number[];
}; };
function SceneContent({
isFocused,
children,
}: {
isFocused: boolean;
children: React.ReactNode;
}) {
const { colors } = useTheme();
return (
<View
accessibilityElementsHidden={!isFocused}
importantForAccessibility={isFocused ? 'auto' : 'no-hide-descendants'}
style={[styles.content, { backgroundColor: colors.background }]}
>
{children}
</View>
);
}
export default class BottomTabView extends React.Component<Props, State> { export default class BottomTabView extends React.Component<Props, State> {
static defaultProps = { static defaultProps = {
lazy: true, lazy: true,
@@ -97,15 +118,9 @@ export default class BottomTabView extends React.Component<Props, State> {
style={StyleSheet.absoluteFill} style={StyleSheet.absoluteFill}
isVisible={isFocused} isVisible={isFocused}
> >
<View <SceneContent isFocused={isFocused}>
accessibilityElementsHidden={!isFocused}
importantForAccessibility={
isFocused ? 'auto' : 'no-hide-descendants'
}
style={styles.content}
>
{descriptors[route.key].render()} {descriptors[route.key].render()}
</View> </SceneContent>
</ResourceSavingScene> </ResourceSavingScene>
); );
})} })}

View File

@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.0.0-alpha.28](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/drawer@5.0.0-alpha.27...@react-navigation/drawer@5.0.0-alpha.28) (2019-12-14)
### Features
* add custom theme support ([#211](https://github.com/react-navigation/navigation-ex/issues/211)) ([00fc616](https://github.com/react-navigation/navigation-ex/commit/00fc616de0572bade8aa85052cdc8290360b1d7f))
# [5.0.0-alpha.27](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/drawer@5.0.0-alpha.26...@react-navigation/drawer@5.0.0-alpha.27) (2019-12-11) # [5.0.0-alpha.27](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/drawer@5.0.0-alpha.26...@react-navigation/drawer@5.0.0-alpha.27) (2019-12-11)
**Note:** Version bump only for package @react-navigation/drawer **Note:** Version bump only for package @react-navigation/drawer

View File

@@ -11,7 +11,7 @@
"material", "material",
"drawer" "drawer"
], ],
"version": "5.0.0-alpha.27", "version": "5.0.0-alpha.28",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -34,7 +34,8 @@
"clean": "del lib" "clean": "del lib"
}, },
"dependencies": { "dependencies": {
"@react-navigation/routers": "^5.0.0-alpha.16" "@react-navigation/routers": "^5.0.0-alpha.16",
"color": "^3.1.2"
}, },
"devDependencies": { "devDependencies": {
"@react-native-community/bob": "^0.7.0", "@react-native-community/bob": "^0.7.0",

View File

@@ -10,6 +10,7 @@ export { default as DrawerView } from './views/DrawerView';
export { default as DrawerItem } from './views/DrawerItem'; export { default as DrawerItem } from './views/DrawerItem';
export { default as DrawerItemList } from './views/DrawerItemList'; export { default as DrawerItemList } from './views/DrawerItemList';
export { default as DrawerContent } from './views/DrawerContent'; export { default as DrawerContent } from './views/DrawerContent';
export { default as DrawerContentScrollView } from './views/DrawerContentScrollView';
/** /**
* Utilities * Utilities

View File

@@ -121,7 +121,7 @@ export type DrawerNavigationOptions = {
export type DrawerContentComponentProps<T = DrawerContentOptions> = T & { export type DrawerContentComponentProps<T = DrawerContentOptions> = T & {
state: DrawerNavigationState; state: DrawerNavigationState;
navigation: NavigationHelpers<ParamListBase>; navigation: DrawerNavigationHelpers;
descriptors: DrawerDescriptorMap; descriptors: DrawerDescriptorMap;
/** /**
* Animated node which represents the current progress of the drawer's open state. * Animated node which represents the current progress of the drawer's open state.

View File

@@ -1,36 +1,12 @@
import * as React from 'react'; import * as React from 'react';
import { ScrollView, StyleSheet } from 'react-native';
import { useSafeArea } from 'react-native-safe-area-context';
import DrawerItemList from './DrawerItemList'; import DrawerItemList from './DrawerItemList';
import { DrawerContentComponentProps } from '../types'; import { DrawerContentComponentProps } from '../types';
import DrawerContentScrollView from './DrawerContentScrollView';
export default function DrawerContent({ export default function DrawerContent(props: DrawerContentComponentProps) {
contentContainerStyle,
style,
drawerPosition,
...rest
}: DrawerContentComponentProps) {
const insets = useSafeArea();
return ( return (
<ScrollView <DrawerContentScrollView {...props}>
contentContainerStyle={[ <DrawerItemList {...props} />
{ </DrawerContentScrollView>
paddingTop: insets.top + 4,
paddingLeft: drawerPosition === 'left' ? insets.left : 0,
paddingRight: drawerPosition === 'right' ? insets.right : 0,
},
contentContainerStyle,
]}
style={[styles.container, style]}
>
<DrawerItemList {...rest} />
</ScrollView>
); );
} }
const styles = StyleSheet.create({
container: {
flex: 1,
},
});

View File

@@ -0,0 +1,43 @@
import * as React from 'react';
import { ScrollView, StyleSheet, ScrollViewProps } from 'react-native';
import { useSafeArea } from 'react-native-safe-area-context';
import { useTheme } from '@react-navigation/native';
type Props = ScrollViewProps & {
drawerPosition: 'left' | 'right';
children: React.ReactNode;
};
export default function DrawerContentScrollView({
contentContainerStyle,
style,
drawerPosition,
children,
...rest
}: Props) {
const insets = useSafeArea();
const { colors } = useTheme();
return (
<ScrollView
{...rest}
contentContainerStyle={[
{
paddingTop: insets.top + 4,
paddingLeft: drawerPosition === 'left' ? insets.left : 0,
paddingRight: drawerPosition === 'right' ? insets.right : 0,
},
contentContainerStyle,
]}
style={[styles.container, { backgroundColor: colors.card }, style]}
>
{children}
</ScrollView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
});

View File

@@ -7,6 +7,8 @@ import {
ViewStyle, ViewStyle,
TextStyle, TextStyle,
} from 'react-native'; } from 'react-native';
import { useTheme } from '@react-navigation/native';
import Color from 'color';
import TouchableItem from './TouchableItem'; import TouchableItem from './TouchableItem';
type Props = { type Props = {
@@ -61,19 +63,29 @@ type Props = {
/** /**
* A component used to show an action item with an icon and a label in a navigation drawer. * A component used to show an action item with an icon and a label in a navigation drawer.
*/ */
export default function DrawerItem({ export default function DrawerItem(props: Props) {
icon, const { colors } = useTheme();
label,
labelStyle, const {
focused = false, icon,
activeTintColor = '#6200ee', label,
inactiveTintColor = 'rgba(0, 0, 0, .68)', labelStyle,
activeBackgroundColor = 'rgba(98, 0, 238, 0.12)', focused = false,
inactiveBackgroundColor = 'transparent', activeTintColor = colors.primary,
style, inactiveTintColor = Color(colors.text)
onPress, .alpha(0.68)
...rest .rgb()
}: Props) { .string(),
activeBackgroundColor = Color(activeTintColor)
.alpha(0.12)
.rgb()
.string(),
inactiveBackgroundColor = 'transparent',
style,
onPress,
...rest
} = props;
const { borderRadius = 4 } = StyleSheet.flatten(style || {}); const { borderRadius = 4 } = StyleSheet.flatten(style || {});
const color = focused ? activeTintColor : inactiveTintColor; const color = focused ? activeTintColor : inactiveTintColor;
const backgroundColor = focused const backgroundColor = focused

View File

@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.0.0-alpha.25](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-bottom-tabs@5.0.0-alpha.24...@react-navigation/material-bottom-tabs@5.0.0-alpha.25) (2019-12-14)
### Features
* add custom theme support ([#211](https://github.com/react-navigation/navigation-ex/issues/211)) ([00fc616](https://github.com/react-navigation/navigation-ex/commit/00fc616de0572bade8aa85052cdc8290360b1d7f))
# [5.0.0-alpha.24](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-bottom-tabs@5.0.0-alpha.23...@react-navigation/material-bottom-tabs@5.0.0-alpha.24) (2019-12-11) # [5.0.0-alpha.24](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-bottom-tabs@5.0.0-alpha.23...@react-navigation/material-bottom-tabs@5.0.0-alpha.24) (2019-12-11)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs **Note:** Version bump only for package @react-navigation/material-bottom-tabs

View File

@@ -11,7 +11,7 @@
"material", "material",
"tab" "tab"
], ],
"version": "5.0.0-alpha.24", "version": "5.0.0-alpha.25",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",

View File

@@ -1,8 +1,8 @@
import * as React from 'react'; import * as React from 'react';
import { StyleSheet } from 'react-native'; import { StyleSheet } from 'react-native';
import { BottomNavigation } from 'react-native-paper'; import { BottomNavigation, DefaultTheme, DarkTheme } from 'react-native-paper';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import { Route } from '@react-navigation/native'; import { Route, useTheme } from '@react-navigation/native';
import { TabNavigationState, TabActions } from '@react-navigation/routers'; import { TabNavigationState, TabActions } from '@react-navigation/routers';
import { import {
@@ -25,9 +25,25 @@ export default function MaterialBottomTabView({
descriptors, descriptors,
...rest ...rest
}: Props) { }: Props) {
const { dark, colors } = useTheme();
const theme = React.useMemo(() => {
const t = dark ? DarkTheme : DefaultTheme;
return {
...t,
colors: {
...t.colors,
...colors,
surface: colors.card,
},
};
}, [colors, dark]);
return ( return (
<BottomNavigation <BottomNavigation
{...rest} {...rest}
theme={theme}
navigationState={state} navigationState={state}
onIndexChange={(index: number) => onIndexChange={(index: number) =>
navigation.dispatch({ navigation.dispatch({

View File

@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.0.0-alpha.23](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-top-tabs@5.0.0-alpha.22...@react-navigation/material-top-tabs@5.0.0-alpha.23) (2019-12-14)
### Features
* add custom theme support ([#211](https://github.com/react-navigation/navigation-ex/issues/211)) ([00fc616](https://github.com/react-navigation/navigation-ex/commit/00fc616de0572bade8aa85052cdc8290360b1d7f))
# [5.0.0-alpha.22](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-top-tabs@5.0.0-alpha.21...@react-navigation/material-top-tabs@5.0.0-alpha.22) (2019-12-11) # [5.0.0-alpha.22](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-top-tabs@5.0.0-alpha.21...@react-navigation/material-top-tabs@5.0.0-alpha.22) (2019-12-11)
**Note:** Version bump only for package @react-navigation/material-top-tabs **Note:** Version bump only for package @react-navigation/material-top-tabs

View File

@@ -11,7 +11,7 @@
"material", "material",
"tab" "tab"
], ],
"version": "5.0.0-alpha.22", "version": "5.0.0-alpha.23",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -34,7 +34,8 @@
"clean": "del lib" "clean": "del lib"
}, },
"dependencies": { "dependencies": {
"@react-navigation/routers": "^5.0.0-alpha.16" "@react-navigation/routers": "^5.0.0-alpha.16",
"color": "^3.1.2"
}, },
"devDependencies": { "devDependencies": {
"@react-native-community/bob": "^0.7.0", "@react-native-community/bob": "^0.7.0",

View File

@@ -1,29 +1,46 @@
import * as React from 'react'; import * as React from 'react';
import { View, Text, StyleSheet } from 'react-native'; import { View, Text, StyleSheet } from 'react-native';
import { TabBar } from 'react-native-tab-view'; import { TabBar } from 'react-native-tab-view';
import { Route } from '@react-navigation/native'; import { Route, useTheme } from '@react-navigation/native';
import Color from 'color';
import { MaterialTopTabBarProps } from '../types'; import { MaterialTopTabBarProps } from '../types';
export default function TabBarTop({ export default function TabBarTop(props: MaterialTopTabBarProps) {
state, const { colors } = useTheme();
navigation,
descriptors, const {
activeTintColor = 'rgba(255, 255, 255, 1)', state,
inactiveTintColor = 'rgba(255, 255, 255, 0.7)', navigation,
allowFontScaling = true, descriptors,
iconStyle, activeTintColor = colors.text,
labelStyle, inactiveTintColor = Color(activeTintColor)
showIcon = false, .alpha(0.5)
showLabel = true, .rgb()
...rest .string(),
}: MaterialTopTabBarProps) { allowFontScaling = true,
showIcon = false,
showLabel = true,
pressColor = Color(activeTintColor)
.alpha(0.08)
.rgb()
.string(),
iconStyle,
labelStyle,
indicatorStyle,
style,
...rest
} = props;
return ( return (
<TabBar <TabBar
{...rest} {...rest}
navigationState={state} navigationState={state}
activeColor={activeTintColor} activeColor={activeTintColor}
inactiveColor={inactiveTintColor} inactiveColor={inactiveTintColor}
indicatorStyle={[{ backgroundColor: colors.primary }, indicatorStyle]}
style={[{ backgroundColor: colors.card }, style]}
pressColor={pressColor}
getAccessibilityLabel={({ route }) => getAccessibilityLabel={({ route }) =>
descriptors[route.key].options.tabBarAccessibilityLabel descriptors[route.key].options.tabBarAccessibilityLabel
} }

View File

@@ -1,6 +1,7 @@
import * as React from 'react'; import * as React from 'react';
import { View } from 'react-native';
import { TabView, SceneRendererProps } from 'react-native-tab-view'; import { TabView, SceneRendererProps } from 'react-native-tab-view';
import { Route } from '@react-navigation/native'; import { Route, useTheme } from '@react-navigation/native';
import { TabNavigationState, TabActions } from '@react-navigation/routers'; import { TabNavigationState, TabActions } from '@react-navigation/routers';
import MaterialTopTabBar from './MaterialTopTabBar'; import MaterialTopTabBar from './MaterialTopTabBar';
@@ -18,6 +19,16 @@ type Props = MaterialTopTabNavigationConfig & {
tabBarPosition: 'top' | 'bottom'; tabBarPosition: 'top' | 'bottom';
}; };
function SceneContent({ children }: { children: React.ReactNode }) {
const { colors } = useTheme();
return (
<View style={{ flex: 1, backgroundColor: colors.background }}>
{children}
</View>
);
}
export default class MaterialTopTabView extends React.PureComponent<Props> { export default class MaterialTopTabView extends React.PureComponent<Props> {
static defaultProps = { static defaultProps = {
tabBarPosition: 'top', tabBarPosition: 'top',
@@ -93,7 +104,9 @@ export default class MaterialTopTabView extends React.PureComponent<Props> {
target: state.key, target: state.key,
}) })
} }
renderScene={({ route }) => descriptors[route.key].render()} renderScene={({ route }) => (
<SceneContent>{descriptors[route.key].render()}</SceneContent>
)}
navigationState={state} navigationState={state}
renderTabBar={this.renderTabBar} renderTabBar={this.renderTabBar}
renderLazyPlaceholder={this.renderLazyPlaceholder} renderLazyPlaceholder={this.renderLazyPlaceholder}

View File

@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.0.0-alpha.17](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/native-stack@5.0.0-alpha.16...@react-navigation/native-stack@5.0.0-alpha.17) (2019-12-14)
### Features
* add custom theme support ([#211](https://github.com/react-navigation/navigation-ex/issues/211)) ([00fc616](https://github.com/react-navigation/navigation-ex/commit/00fc616de0572bade8aa85052cdc8290360b1d7f))
# [5.0.0-alpha.16](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/native-stack@5.0.0-alpha.15...@react-navigation/native-stack@5.0.0-alpha.16) (2019-12-11) # [5.0.0-alpha.16](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/native-stack@5.0.0-alpha.15...@react-navigation/native-stack@5.0.0-alpha.16) (2019-12-11)
**Note:** Version bump only for package @react-navigation/native-stack **Note:** Version bump only for package @react-navigation/native-stack

View File

@@ -6,7 +6,7 @@
"react-native", "react-native",
"react-navigation" "react-navigation"
], ],
"version": "5.0.0-alpha.16", "version": "5.0.0-alpha.17",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",

View File

@@ -6,7 +6,7 @@ import {
ScreenStackHeaderRightView, ScreenStackHeaderRightView,
// eslint-disable-next-line import/no-unresolved // eslint-disable-next-line import/no-unresolved
} from 'react-native-screens'; } from 'react-native-screens';
import { Route } from '@react-navigation/native'; import { Route, useTheme } from '@react-navigation/native';
import { NativeStackNavigationOptions } from '../types'; import { NativeStackNavigationOptions } from '../types';
type Props = NativeStackNavigationOptions & { type Props = NativeStackNavigationOptions & {
@@ -14,6 +14,7 @@ type Props = NativeStackNavigationOptions & {
}; };
export default function HeaderConfig(props: Props) { export default function HeaderConfig(props: Props) {
const { colors } = useTheme();
const { const {
route, route,
title, title,
@@ -52,17 +53,23 @@ export default function HeaderConfig(props: Props) {
titleColor={ titleColor={
headerTitleStyle.color !== undefined headerTitleStyle.color !== undefined
? headerTitleStyle.color ? headerTitleStyle.color
: headerTintColor : headerTintColor !== undefined
? headerTintColor
: colors.text
} }
backTitle={headerBackTitleVisible ? headerBackTitle : ''} backTitle={headerBackTitleVisible ? headerBackTitle : ''}
backTitleFontFamily={headerBackTitleStyle.fontFamily} backTitleFontFamily={headerBackTitleStyle.fontFamily}
backTitleFontSize={headerBackTitleStyle.fontSize} backTitleFontSize={headerBackTitleStyle.fontSize}
color={headerTintColor} color={headerTintColor !== undefined ? headerTintColor : colors.primary}
gestureEnabled={gestureEnabled === undefined ? true : gestureEnabled} gestureEnabled={gestureEnabled === undefined ? true : gestureEnabled}
largeTitle={headerLargeTitle} largeTitle={headerLargeTitle}
largeTitleFontFamily={headerLargeTitleStyle.fontFamily} largeTitleFontFamily={headerLargeTitleStyle.fontFamily}
largeTitleFontSize={headerLargeTitleStyle.fontSize} largeTitleFontSize={headerLargeTitleStyle.fontSize}
backgroundColor={headerStyle.backgroundColor} backgroundColor={
headerStyle.backgroundColor !== undefined
? headerStyle.backgroundColor
: colors.card
}
> >
{headerRight !== undefined ? ( {headerRight !== undefined ? (
<ScreenStackHeaderRightView>{headerRight()}</ScreenStackHeaderRightView> <ScreenStackHeaderRightView>{headerRight()}</ScreenStackHeaderRightView>

View File

@@ -9,6 +9,7 @@ import {
ScreenProps, ScreenProps,
// eslint-disable-next-line import/no-unresolved // eslint-disable-next-line import/no-unresolved
} from 'react-native-screens'; } from 'react-native-screens';
import { useTheme } from '@react-navigation/native';
import HeaderConfig from './HeaderConfig'; import HeaderConfig from './HeaderConfig';
import { import {
NativeStackNavigationHelpers, NativeStackNavigationHelpers,
@@ -34,8 +35,10 @@ export default function NativeStackView({
navigation, navigation,
descriptors, descriptors,
}: Props) { }: Props) {
const { colors } = useTheme();
return ( return (
<ScreenStack style={styles.scenes}> <ScreenStack style={styles.container}>
{state.routes.map(route => { {state.routes.map(route => {
const { options, render: renderScene } = descriptors[route.key]; const { options, render: renderScene } = descriptors[route.key];
const { presentation = 'push', animation, contentStyle } = options; const { presentation = 'push', animation, contentStyle } = options;
@@ -55,7 +58,15 @@ export default function NativeStackView({
}} }}
> >
<HeaderConfig {...options} route={route} /> <HeaderConfig {...options} route={route} />
<View style={[styles.content, contentStyle]}>{renderScene()}</View> <View
style={[
styles.container,
{ backgroundColor: colors.background },
contentStyle,
]}
>
{renderScene()}
</View>
</Screen> </Screen>
); );
})} })}
@@ -64,11 +75,7 @@ export default function NativeStackView({
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
content: { container: {
flex: 1,
backgroundColor: '#eee',
},
scenes: {
flex: 1, flex: 1,
}, },
}); });

View File

@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.0.0-alpha.19](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/native@5.0.0-alpha.18...@react-navigation/native@5.0.0-alpha.19) (2019-12-14)
### Features
* add custom theme support ([#211](https://github.com/react-navigation/navigation-ex/issues/211)) ([00fc616](https://github.com/react-navigation/navigation-ex/commit/00fc616de0572bade8aa85052cdc8290360b1d7f))
# [5.0.0-alpha.18](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/native@5.0.0-alpha.17...@react-navigation/native@5.0.0-alpha.18) (2019-12-11) # [5.0.0-alpha.18](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/native@5.0.0-alpha.17...@react-navigation/native@5.0.0-alpha.18) (2019-12-11)
**Note:** Version bump only for package @react-navigation/native **Note:** Version bump only for package @react-navigation/native

View File

@@ -7,7 +7,7 @@
"ios", "ios",
"android" "android"
], ],
"version": "5.0.0-alpha.18", "version": "5.0.0-alpha.19",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",

View File

@@ -4,7 +4,14 @@ import {
NavigationContainerProps, NavigationContainerProps,
NavigationContainerRef, NavigationContainerRef,
} from '@react-navigation/core'; } from '@react-navigation/core';
import ThemeProvider from './theming/ThemeProvider';
import DefaultTheme from './theming/DefaultTheme';
import useBackButton from './useBackButton'; import useBackButton from './useBackButton';
import { Theme } from './types';
type Props = NavigationContainerProps & {
theme?: Theme;
};
/** /**
* Container component which holds the navigation state * Container component which holds the navigation state
@@ -13,11 +20,12 @@ import useBackButton from './useBackButton';
* *
* @param props.initialState Initial state object for the navigation tree. * @param props.initialState Initial state object for the navigation tree.
* @param props.onStateChange Callback which is called with the latest navigation state when it changes. * @param props.onStateChange Callback which is called with the latest navigation state when it changes.
* @param props.theme Theme object for the navigators.
* @param props.children Child elements to render the content. * @param props.children Child elements to render the content.
* @param props.ref Ref object which refers to the navigation object containing helper methods. * @param props.ref Ref object which refers to the navigation object containing helper methods.
*/ */
const NavigationNativeContainer = React.forwardRef(function NativeContainer( const NavigationNativeContainer = React.forwardRef(function NativeContainer(
props: NavigationContainerProps, { theme = DefaultTheme, ...rest }: Props,
ref: React.Ref<NavigationContainerRef> ref: React.Ref<NavigationContainerRef>
) { ) {
const refContainer = React.useRef<NavigationContainerRef>(null); const refContainer = React.useRef<NavigationContainerRef>(null);
@@ -27,11 +35,9 @@ const NavigationNativeContainer = React.forwardRef(function NativeContainer(
React.useImperativeHandle(ref, () => refContainer.current); React.useImperativeHandle(ref, () => refContainer.current);
return ( return (
<NavigationContainer <ThemeProvider value={theme}>
{...props} <NavigationContainer {...rest} ref={refContainer} />
ref={refContainer} </ThemeProvider>
children={props.children}
/>
); );
}); });

View File

@@ -5,3 +5,8 @@ export { default as NavigationNativeContainer } from './NavigationNativeContaine
export { default as useBackButton } from './useBackButton'; export { default as useBackButton } from './useBackButton';
export { default as useLinking } from './useLinking'; export { default as useLinking } from './useLinking';
export { default as useScrollToTop } from './useScrollToTop'; export { default as useScrollToTop } from './useScrollToTop';
export { default as DefaultTheme } from './theming/DefaultTheme';
export { default as DarkTheme } from './theming/DarkTheme';
export { default as ThemeProvider } from './theming/ThemeProvider';
export { default as useTheme } from './theming/useTheme';

View File

@@ -0,0 +1,14 @@
import { Theme } from '../types';
const DarkTheme: Theme = {
dark: true,
colors: {
primary: 'rgb(10, 132, 255)',
background: 'rgb(1, 1, 1)',
card: 'rgb(18, 18, 18)',
text: 'rgb(229, 229, 231)',
border: 'rgb(39, 39, 41)',
},
};
export default DarkTheme;

View File

@@ -0,0 +1,14 @@
import { Theme } from '../types';
const DefaultTheme: Theme = {
dark: false,
colors: {
primary: 'rgb(0, 122, 255)',
background: 'rgb(242, 242, 242)',
card: 'rgb(255, 255, 255)',
text: 'rgb(28, 28, 30)',
border: 'rgb(199, 199, 204)',
},
};
export default DefaultTheme;

View File

@@ -0,0 +1,7 @@
import * as React from 'react';
import DefaultTheme from './DefaultTheme';
import { Theme } from '../types';
const ThemeContext = React.createContext<Theme>(DefaultTheme);
export default ThemeContext;

View File

@@ -0,0 +1,14 @@
import * as React from 'react';
import ThemeContext from './ThemeContext';
import { Theme } from '../types';
type Props = {
value: Theme;
children: React.ReactNode;
};
export default function ThemeProvider({ value, children }: Props) {
return (
<ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
);
}

View File

@@ -0,0 +1,8 @@
import * as React from 'react';
import ThemeContext from './ThemeContext';
export default function useTheme() {
const theme = React.useContext(ThemeContext);
return theme;
}

View File

@@ -0,0 +1,10 @@
export type Theme = {
dark: boolean;
colors: {
primary: string;
background: string;
card: string;
text: string;
border: string;
};
};

View File

@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.0.0-alpha.44](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/stack@5.0.0-alpha.43...@react-navigation/stack@5.0.0-alpha.44) (2019-12-14)
### Features
* add custom theme support ([#211](https://github.com/react-navigation/navigation-ex/issues/211)) ([00fc616](https://github.com/react-navigation/navigation-ex/commit/00fc616de0572bade8aa85052cdc8290360b1d7f))
# [5.0.0-alpha.43](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/stack@5.0.0-alpha.42...@react-navigation/stack@5.0.0-alpha.43) (2019-12-11) # [5.0.0-alpha.43](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/stack@5.0.0-alpha.42...@react-navigation/stack@5.0.0-alpha.43) (2019-12-11)
**Note:** Version bump only for package @react-navigation/stack **Note:** Version bump only for package @react-navigation/stack

View File

@@ -10,7 +10,7 @@
"android", "android",
"stack" "stack"
], ],
"version": "5.0.0-alpha.43", "version": "5.0.0-alpha.44",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",

View File

@@ -406,6 +406,10 @@ export type StackHeaderTitleProps = {
* Whether title font should scale to respect Text Size accessibility settings. * Whether title font should scale to respect Text Size accessibility settings.
*/ */
allowFontScaling?: boolean; allowFontScaling?: boolean;
/**
* Tint color for the header.
*/
tintColor?: string;
/** /**
* Content of the title element. Usually the title string. * Content of the title element. Usually the title string.
*/ */

View File

@@ -8,45 +8,56 @@ import {
LayoutChangeEvent, LayoutChangeEvent,
} from 'react-native'; } from 'react-native';
import Animated from 'react-native-reanimated'; import Animated from 'react-native-reanimated';
import { useTheme } from '@react-navigation/native';
import MaskedView from '../MaskedView'; import MaskedView from '../MaskedView';
import TouchableItem from '../TouchableItem'; import TouchableItem from '../TouchableItem';
import { StackHeaderLeftButtonProps } from '../../types'; import { StackHeaderLeftButtonProps } from '../../types';
type Props = StackHeaderLeftButtonProps & { type Props = StackHeaderLeftButtonProps;
tintColor: string;
};
type State = { export default function HeaderBackButton({
fullLabelWidth?: number; disabled,
}; allowFontScaling,
backImage,
label,
labelStyle,
labelVisible = Platform.OS === 'ios',
onLabelLayout,
onPress,
pressColorAndroid: customPressColorAndroid,
screenLayout,
tintColor: customTintColor,
titleLayout,
truncatedLabel = 'Back',
}: Props) {
const { dark, colors } = useTheme();
class HeaderBackButton extends React.Component<Props, State> { const [initialLabelWidth, setInitialLabelWidth] = React.useState<
static defaultProps = { undefined | number
pressColorAndroid: 'rgba(0, 0, 0, .32)', >(undefined);
tintColor: Platform.select({
ios: '#037aff',
web: '#5f6368',
}),
labelVisible: Platform.OS === 'ios',
truncatedLabel: 'Back',
};
state: State = {}; const tintColor =
customTintColor !== undefined
? customTintColor
: Platform.select({
ios: colors.primary,
default: colors.text,
});
private handleLabelLayout = (e: LayoutChangeEvent) => { const pressColorAndroid =
const { onLabelLayout } = this.props; customPressColorAndroid !== undefined
? customPressColorAndroid
: dark
? 'rgba(255, 255, 255, .32)'
: 'rgba(0, 0, 0, .32)';
const handleLabelLayout = (e: LayoutChangeEvent) => {
onLabelLayout && onLabelLayout(e); onLabelLayout && onLabelLayout(e);
this.setState({ setInitialLabelWidth(e.nativeEvent.layout.x + e.nativeEvent.layout.width);
fullLabelWidth: e.nativeEvent.layout.x + e.nativeEvent.layout.width,
});
}; };
private shouldTruncateLabel = () => { const shouldTruncateLabel = () => {
const { titleLayout, screenLayout, label } = this.props;
const { fullLabelWidth: initialLabelWidth } = this.state;
return ( return (
!label || !label ||
(initialLabelWidth && (initialLabelWidth &&
@@ -56,9 +67,7 @@ class HeaderBackButton extends React.Component<Props, State> {
); );
}; };
private renderBackImage = () => { const renderBackImage = () => {
const { backImage, labelVisible, tintColor } = this.props;
if (backImage) { if (backImage) {
return backImage({ tintColor }); return backImage({ tintColor });
} else { } else {
@@ -76,19 +85,8 @@ class HeaderBackButton extends React.Component<Props, State> {
} }
}; };
private renderLabel() { const renderLabel = () => {
const { const leftLabelText = shouldTruncateLabel() ? truncatedLabel : label;
label,
truncatedLabel,
allowFontScaling,
labelVisible,
backImage,
labelStyle,
tintColor,
screenLayout,
} = this.props;
const leftLabelText = this.shouldTruncateLabel() ? truncatedLabel : label;
if (!labelVisible || leftLabelText === undefined) { if (!labelVisible || leftLabelText === undefined) {
return null; return null;
@@ -109,7 +107,7 @@ class HeaderBackButton extends React.Component<Props, State> {
onLayout={ onLayout={
// This measurement is used to determine if we should truncate the label when it doesn't fit // This measurement is used to determine if we should truncate the label when it doesn't fit
// Only measure it when label is not truncated because we want the measurement of full label // Only measure it when label is not truncated because we want the measurement of full label
leftLabelText === label ? this.handleLabelLayout : undefined leftLabelText === label ? handleLabelLayout : undefined
} }
style={[ style={[
styles.label, styles.label,
@@ -145,42 +143,37 @@ class HeaderBackButton extends React.Component<Props, State> {
{labelElement} {labelElement}
</MaskedView> </MaskedView>
); );
} };
private handlePress = () => const handlePress = () => onPress && requestAnimationFrame(onPress);
this.props.onPress && requestAnimationFrame(this.props.onPress);
render() { return (
const { pressColorAndroid, label, disabled } = this.props; <TouchableItem
disabled={disabled}
return ( accessible
<TouchableItem accessibilityRole="button"
disabled={disabled} accessibilityComponentType="button"
accessible accessibilityLabel={
accessibilityRole="button" label && label !== 'Back' ? `${label}, back` : 'Go back'
accessibilityComponentType="button" }
accessibilityLabel={ accessibilityTraits="button"
label && label !== 'Back' ? `${label}, back` : 'Go back' testID="header-back"
} delayPressIn={0}
accessibilityTraits="button" onPress={disabled ? undefined : handlePress}
testID="header-back" pressColor={pressColorAndroid}
delayPressIn={0} style={[styles.container, disabled && styles.disabled]}
onPress={disabled ? undefined : this.handlePress} hitSlop={Platform.select({
pressColor={pressColorAndroid} ios: undefined,
style={[styles.container, disabled && styles.disabled]} default: { top: 16, right: 16, bottom: 16, left: 16 },
hitSlop={Platform.select({ })}
ios: undefined, borderless
default: { top: 16, right: 16, bottom: 16, left: 16 }, >
})} <React.Fragment>
borderless {renderBackImage()}
> {renderLabel()}
<React.Fragment> </React.Fragment>
{this.renderBackImage()} </TouchableItem>
{this.renderLabel()} );
</React.Fragment>
</TouchableItem>
);
}
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
@@ -253,5 +246,3 @@ const styles = StyleSheet.create({
transform: [{ scaleX: I18nManager.isRTL ? -1 : 1 }], transform: [{ scaleX: I18nManager.isRTL ? -1 : 1 }],
}, },
}); });
export default HeaderBackButton;

View File

@@ -1,21 +1,35 @@
import * as React from 'react'; import * as React from 'react';
import { StyleSheet, Platform, ViewProps } from 'react-native'; import { StyleSheet, Platform, ViewProps } from 'react-native';
import Animated from 'react-native-reanimated'; import Animated from 'react-native-reanimated';
import { useTheme } from '@react-navigation/native';
export default function HeaderBackground({ style, ...rest }: ViewProps) { export default function HeaderBackground({ style, ...rest }: ViewProps) {
return <Animated.View style={[styles.container, style]} {...rest} />; const { colors } = useTheme();
return (
<Animated.View
style={[
styles.container,
{
backgroundColor: colors.card,
borderBottomColor: colors.border,
shadowColor: colors.border,
},
style,
]}
{...rest}
/>
);
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
backgroundColor: 'white',
...Platform.select({ ...Platform.select({
android: { android: {
elevation: 4, elevation: 4,
}, },
ios: { ios: {
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowOpacity: 0.85, shadowOpacity: 0.85,
shadowRadius: 0, shadowRadius: 0,
shadowOffset: { shadowOffset: {
@@ -25,7 +39,6 @@ const styles = StyleSheet.create({
}, },
default: { default: {
borderBottomWidth: StyleSheet.hairlineWidth, borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: 'rgba(0, 0, 0, 0.20)',
}, },
}), }),
}, },

View File

@@ -339,7 +339,8 @@ export default class HeaderSegment extends React.Component<Props, State> {
children: currentTitle, children: currentTitle,
onLayout: this.handleTitleLayout, onLayout: this.handleTitleLayout,
allowFontScaling: titleAllowFontScaling, allowFontScaling: titleAllowFontScaling,
style: [{ color: headerTintColor }, customTitleStyle], tintColor: headerTintColor,
style: customTitleStyle,
})} })}
</Animated.View> </Animated.View>
{right ? ( {right ? (

View File

@@ -1,14 +1,26 @@
import * as React from 'react'; import * as React from 'react';
import { StyleSheet, Platform, TextProps } from 'react-native'; import { StyleSheet, Platform, TextProps } from 'react-native';
import Animated from 'react-native-reanimated'; import Animated from 'react-native-reanimated';
import { useTheme } from '@react-navigation/native';
type Props = TextProps & { type Props = TextProps & {
tintColor?: string;
children?: string; children?: string;
}; };
export default function HeaderTitle({ style, ...rest }: Props) { export default function HeaderTitle({ tintColor, style, ...rest }: Props) {
const { colors } = useTheme();
return ( return (
<Animated.Text numberOfLines={1} {...rest} style={[styles.title, style]} /> <Animated.Text
numberOfLines={1}
{...rest}
style={[
styles.title,
{ color: tintColor === undefined ? colors.text : tintColor },
style,
]}
/>
); );
} }
@@ -17,17 +29,14 @@ const styles = StyleSheet.create({
ios: { ios: {
fontSize: 17, fontSize: 17,
fontWeight: '600', fontWeight: '600',
color: 'rgba(0, 0, 0, .9)',
}, },
android: { android: {
fontSize: 20, fontSize: 20,
fontWeight: '500', fontWeight: '500',
color: 'rgba(0, 0, 0, .9)',
}, },
default: { default: {
fontSize: 18, fontSize: 18,
fontWeight: '500', fontWeight: '500',
color: '#3c4043',
}, },
}), }),
}); });

View File

@@ -853,11 +853,7 @@ export default class Card extends React.Component<Props> {
<PointerEventsView <PointerEventsView
active={active} active={active}
progress={this.props.current} progress={this.props.current}
style={[ style={[styles.content, contentStyle]}
styles.content,
transparent ? styles.transparent : styles.opaque,
contentStyle,
]}
> >
<StackCardAnimationContext.Provider value={animationContext}> <StackCardAnimationContext.Provider value={animationContext}>
{children} {children}
@@ -905,10 +901,4 @@ const styles = StyleSheet.create({
height: 3, height: 3,
shadowOffset: { width: 1, height: -1 }, shadowOffset: { width: 1, height: -1 },
}, },
transparent: {
backgroundColor: 'transparent',
},
opaque: {
backgroundColor: '#eee',
},
}); });

View File

@@ -2,7 +2,7 @@ import * as React from 'react';
import { View, StyleSheet, StyleProp, ViewStyle } from 'react-native'; import { View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
import Animated from 'react-native-reanimated'; import Animated from 'react-native-reanimated';
import { StackNavigationState } from '@react-navigation/routers'; import { StackNavigationState } from '@react-navigation/routers';
import { Route } from '@react-navigation/native'; import { Route, useTheme } from '@react-navigation/native';
import { Props as HeaderContainerProps } from '../Header/HeaderContainer'; import { Props as HeaderContainerProps } from '../Header/HeaderContainer';
import Card from './Card'; import Card from './Card';
import { Scene, Layout, StackHeaderMode, TransitionPreset } from '../../types'; import { Scene, Layout, StackHeaderMode, TransitionPreset } from '../../types';
@@ -53,30 +53,58 @@ type Props = TransitionPreset & {
floatingHeaderHeight: number; floatingHeaderHeight: number;
}; };
export default class CardContainer extends React.PureComponent<Props> { export default function CardContainer({
private handleOpen = () => { active,
const { scene, onTransitionEnd, onOpenRoute } = this.props; cardOverlayEnabled,
cardShadowEnabled,
cardStyle,
cardStyleInterpolator,
cardTransparent,
closing,
current,
floatingHeaderHeight,
focused,
gestureDirection,
gestureEnabled,
gestureResponseDistance,
gestureVelocityImpact,
getPreviousRoute,
headerMode,
headerShown,
headerStyleInterpolator,
headerTransparent,
index,
layout,
onCloseRoute,
onGoBack,
onOpenRoute,
onPageChangeCancel,
onPageChangeConfirm,
onPageChangeStart,
onTransitionEnd,
onTransitionStart,
previousScene,
renderHeader,
renderScene,
safeAreaInsetBottom,
safeAreaInsetLeft,
safeAreaInsetRight,
safeAreaInsetTop,
scene,
state,
transitionSpec,
}: Props) {
const handleOpen = () => {
onTransitionEnd && onTransitionEnd({ route: scene.route }, false); onTransitionEnd && onTransitionEnd({ route: scene.route }, false);
onOpenRoute({ route: scene.route }); onOpenRoute({ route: scene.route });
}; };
private handleClose = () => { const handleClose = () => {
const { scene, onTransitionEnd, onCloseRoute } = this.props;
onTransitionEnd && onTransitionEnd({ route: scene.route }, true); onTransitionEnd && onTransitionEnd({ route: scene.route }, true);
onCloseRoute({ route: scene.route }); onCloseRoute({ route: scene.route });
}; };
private handleTransitionStart = ({ closing }: { closing: boolean }) => { const handleTransitionStart = ({ closing }: { closing: boolean }) => {
const {
scene,
onTransitionStart,
onPageChangeConfirm,
onPageChangeCancel,
onGoBack,
} = this.props;
if (closing) { if (closing) {
onPageChangeConfirm && onPageChangeConfirm(); onPageChangeConfirm && onPageChangeConfirm();
} else { } else {
@@ -87,103 +115,70 @@ export default class CardContainer extends React.PureComponent<Props> {
closing && onGoBack({ route: scene.route }); closing && onGoBack({ route: scene.route });
}; };
render() { const insets = {
const { top: safeAreaInsetTop,
index, right: safeAreaInsetRight,
layout, bottom: safeAreaInsetBottom,
active, left: safeAreaInsetLeft,
focused, };
closing,
current,
state,
scene,
previousScene,
safeAreaInsetTop,
safeAreaInsetRight,
safeAreaInsetBottom,
safeAreaInsetLeft,
cardTransparent,
cardOverlayEnabled,
cardShadowEnabled,
cardStyle,
onPageChangeStart,
onPageChangeCancel,
gestureEnabled,
gestureResponseDistance,
gestureVelocityImpact,
floatingHeaderHeight,
headerShown,
getPreviousRoute,
headerMode,
headerTransparent,
renderHeader,
renderScene,
gestureDirection,
transitionSpec,
cardStyleInterpolator,
headerStyleInterpolator,
} = this.props;
const insets = { const { colors } = useTheme();
top: safeAreaInsetTop,
right: safeAreaInsetRight,
bottom: safeAreaInsetBottom,
left: safeAreaInsetLeft,
};
return ( return (
<Card <Card
index={index} index={index}
active={active} active={active}
transparent={cardTransparent} transparent={cardTransparent}
gestureDirection={gestureDirection} gestureDirection={gestureDirection}
layout={layout} layout={layout}
insets={insets} insets={insets}
current={current} current={current}
next={scene.progress.next} next={scene.progress.next}
closing={closing} closing={closing}
onOpen={this.handleOpen} onOpen={handleOpen}
onClose={this.handleClose} onClose={handleClose}
overlayEnabled={cardOverlayEnabled} overlayEnabled={cardOverlayEnabled}
shadowEnabled={cardShadowEnabled} shadowEnabled={cardShadowEnabled}
onTransitionStart={this.handleTransitionStart} onTransitionStart={handleTransitionStart}
onGestureBegin={onPageChangeStart} onGestureBegin={onPageChangeStart}
onGestureCanceled={onPageChangeCancel} onGestureCanceled={onPageChangeCancel}
gestureEnabled={gestureEnabled} gestureEnabled={gestureEnabled}
gestureResponseDistance={gestureResponseDistance} gestureResponseDistance={gestureResponseDistance}
gestureVelocityImpact={gestureVelocityImpact} gestureVelocityImpact={gestureVelocityImpact}
transitionSpec={transitionSpec} transitionSpec={transitionSpec}
styleInterpolator={cardStyleInterpolator} styleInterpolator={cardStyleInterpolator}
accessibilityElementsHidden={!focused} accessibilityElementsHidden={!focused}
importantForAccessibility={focused ? 'auto' : 'no-hide-descendants'} importantForAccessibility={focused ? 'auto' : 'no-hide-descendants'}
pointerEvents="box-none" pointerEvents="box-none"
containerStyle={ containerStyle={
headerMode === 'float' && !headerTransparent && headerShown !== false headerMode === 'float' && !headerTransparent && headerShown !== false
? { marginTop: floatingHeaderHeight } ? { marginTop: floatingHeaderHeight }
: null : null
} }
contentStyle={cardStyle} contentStyle={[
style={StyleSheet.absoluteFill} {
> backgroundColor: cardTransparent ? 'transparent' : colors.background,
<View style={styles.container}> },
<View style={styles.scene}> cardStyle,
{renderScene({ route: scene.route })} ]}
</View> style={StyleSheet.absoluteFill}
{headerMode === 'screen' >
? renderHeader({ <View style={styles.container}>
mode: 'screen', <View style={styles.scene}>{renderScene({ route: scene.route })}</View>
layout, {headerMode === 'screen'
insets, ? renderHeader({
scenes: [previousScene, scene], mode: 'screen',
state, layout,
getPreviousRoute, insets,
styleInterpolator: headerStyleInterpolator, scenes: [previousScene, scene],
}) state,
: null} getPreviousRoute,
</View> styleInterpolator: headerStyleInterpolator,
</Card> })
); : null}
} </View>
</Card>
);
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({

View File

@@ -2721,6 +2721,25 @@
resolved "https://registry.yarnpkg.com/@types/cli-table/-/cli-table-0.3.0.tgz#f1857156bf5fd115c6a2db260ba0be1f8fc5671c" resolved "https://registry.yarnpkg.com/@types/cli-table/-/cli-table-0.3.0.tgz#f1857156bf5fd115c6a2db260ba0be1f8fc5671c"
integrity sha512-QnZUISJJXyhyD6L1e5QwXDV/A5i2W1/gl6D6YMc8u0ncPepbv/B4w3S+izVvtAg60m6h+JP09+Y/0zF2mojlFQ== integrity sha512-QnZUISJJXyhyD6L1e5QwXDV/A5i2W1/gl6D6YMc8u0ncPepbv/B4w3S+izVvtAg60m6h+JP09+Y/0zF2mojlFQ==
"@types/color-convert@*":
version "1.9.0"
resolved "https://registry.yarnpkg.com/@types/color-convert/-/color-convert-1.9.0.tgz#bfa8203e41e7c65471e9841d7e306a7cd8b5172d"
integrity sha512-OKGEfULrvSL2VRbkl/gnjjgbbF7ycIlpSsX7Nkab4MOWi5XxmgBYvuiQ7lcCFY5cPDz7MUNaKgxte2VRmtr4Fg==
dependencies:
"@types/color-name" "*"
"@types/color-name@*":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
"@types/color@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/color/-/color-3.0.0.tgz#40f8a6bf2fd86e969876b339a837d8ff1b0a6e30"
integrity sha512-5qqtNia+m2I0/85+pd2YzAXaTyKO8j+svirO5aN+XaQJ5+eZ8nx0jPtEWZLxCi50xwYsX10xUHetFzfb1WEs4Q==
dependencies:
"@types/color-convert" "*"
"@types/eslint-visitor-keys@^1.0.0": "@types/eslint-visitor-keys@^1.0.0":
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"