We are on Build a screen

This commit is contained in:
Gabriel Brown 2024-10-08 16:58:50 -05:00
parent 37745648ad
commit 3d79e24ffb
47 changed files with 649 additions and 157 deletions

6
.gitignore vendored
View File

@ -12,3 +12,9 @@ web-build/
# macOS
.DS_Store
# @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb
# The following patterns were generated by expo-cli
expo-env.d.ts
# @end expo-cli

View File

@ -1,21 +1,28 @@
import { Tabs } from 'expo-router';
import React from 'react';
import { TabBarIcon } from '@/components/navigation/TabBarIcon';
import { Tabs } from "expo-router";
import { TabBarIcon } from '@/components/default/navigation/TabBarIcon';
import { Colors } from '@/constants/Colors';
import { useColorScheme } from '@/hooks/useColorScheme';
export default function TabLayout() {
const colorScheme = useColorScheme();
const TabLayout = () => {
const scheme = useColorScheme() ?? 'light';
return (
<Tabs
screenOptions={{
tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint,
tabBarActiveTintColor: Colors[scheme].tint,
headerShown: false,
}}>
headerStyle: {
backgroundColor: Colors[scheme].background,
},
headerShadowVisible: false,
headerTintColor: Colors[scheme].tint,
tabBarStyle: {
backgroundColor: Colors[scheme].background,
},
}}
>
<Tabs.Screen
name="index"
name='index'
options={{
title: 'Home',
tabBarIcon: ({ color, focused }) => (
@ -24,14 +31,15 @@ export default function TabLayout() {
}}
/>
<Tabs.Screen
name="explore"
name='settings'
options={{
title: 'Explore',
title: 'Settings',
tabBarIcon: ({ color, focused }) => (
<TabBarIcon name={focused ? 'code-slash' : 'code-slash-outline'} color={color} />
<TabBarIcon name={focused ? 'settings' : 'settings-outline'} color={color} />
),
}}
/>
</Tabs>
);
}
};
export default TabLayout;

View File

@ -1,70 +1,34 @@
import { Image, StyleSheet, Platform } from 'react-native';
import { StyleSheet } from "react-native";
import { ThemedText } from "@/components/ThemedText";
import { ThemedView } from "@/components/ThemedView";
import { HelloWave } from '@/components/HelloWave';
import ParallaxScrollView from '@/components/ParallaxScrollView';
import { ThemedText } from '@/components/ThemedText';
import { ThemedView } from '@/components/ThemedView';
import Button from "@/components/buttons/Button";
export default function HomeScreen() {
const Index = () => {
return (
<ParallaxScrollView
headerBackgroundColor={{ light: '#A1CEDC', dark: '#1D3D47' }}
headerImage={
<Image
source={require('@/assets/images/partial-react-logo.png')}
style={styles.reactLogo}
/>
}>
<ThemedView style={styles.titleContainer}>
<ThemedText type="title">Welcome!</ThemedText>
<HelloWave />
<ThemedView style={styles.container}>
<ThemedText style={styles.text}>
Home Screen
</ThemedText>
<ThemedView style={styles.footerContainer}>
<Button label="Choose a photo" />
</ThemedView>
<ThemedView style={styles.stepContainer}>
<ThemedText type="subtitle">Step 1: Try it</ThemedText>
<ThemedText>
Edit <ThemedText type="defaultSemiBold">app/(tabs)/index.tsx</ThemedText> to see changes.
Press{' '}
<ThemedText type="defaultSemiBold">
{Platform.select({ ios: 'cmd + d', android: 'cmd + m' })}
</ThemedText>{' '}
to open developer tools.
</ThemedText>
</ThemedView>
<ThemedView style={styles.stepContainer}>
<ThemedText type="subtitle">Step 2: Explore</ThemedText>
<ThemedText>
Tap the Explore tab to learn more about what's included in this starter app.
</ThemedText>
</ThemedView>
<ThemedView style={styles.stepContainer}>
<ThemedText type="subtitle">Step 3: Get a fresh start</ThemedText>
<ThemedText>
When you're ready, run{' '}
<ThemedText type="defaultSemiBold">npm run reset-project</ThemedText> to get a fresh{' '}
<ThemedText type="defaultSemiBold">app</ThemedText> directory. This will move the current{' '}
<ThemedText type="defaultSemiBold">app</ThemedText> to{' '}
<ThemedText type="defaultSemiBold">app-example</ThemedText>.
</ThemedText>
</ThemedView>
</ParallaxScrollView>
</ThemedView>
);
}
export default Index;
const styles = StyleSheet.create({
titleContainer: {
flexDirection: 'row',
container: {
flex: 1,
alignItems: 'center',
gap: 8,
justifyContent: 'center',
},
stepContainer: {
gap: 8,
marginBottom: 8,
text: {
fontSize: 24,
},
reactLogo: {
height: 178,
width: 290,
bottom: 0,
left: 0,
position: 'absolute',
footerContainer: {
flex: 1 / 3,
alignItems: 'center',
},
});

25
app/(tabs)/settings.tsx Normal file
View File

@ -0,0 +1,25 @@
import { StyleSheet } from "react-native";
import { ThemedText } from "@/components/ThemedText";
import { ThemedView } from "@/components/ThemedView";
const Settings = () => {
return (
<ThemedView style={styles.container}>
<ThemedText style={styles.text}>
Settings
</ThemedText>
</ThemedView>
);
};
export default Settings;
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
text: {
fontSize: 24,
},
});

View File

@ -1,32 +1,32 @@
import { Link, Stack } from 'expo-router';
import { StyleSheet } from 'react-native';
import { StyleSheet } from "react-native";
import { ThemedText } from "@/components/ThemedText";
import { ThemedView } from "@/components/ThemedView";
import { Link, Stack } from "expo-router";
import { ThemedText } from '@/components/ThemedText';
import { ThemedView } from '@/components/ThemedView';
export default function NotFoundScreen() {
const NotFoundScreen = () => {
return (
<>
<Stack.Screen options={{ title: 'Oops!' }} />
<Stack.Screen options={{ title: 'Page not found.' }} />
<ThemedView style={styles.container}>
<ThemedText type="title">This screen doesn't exist.</ThemedText>
<Link href="/" style={styles.link}>
<ThemedText type="link">Go to home screen!</ThemedText>
<Link href='/'>
<ThemedText style={styles.button}>
Go back home.
</ThemedText>
</Link>
</ThemedView>
</>
);
}
};
export default NotFoundScreen;
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
padding: 20,
alignItems: 'center',
},
link: {
marginTop: 15,
paddingVertical: 15,
button: {
fontSize: 24,
textDecorationLine: 'underline',
},
});

View File

@ -1,37 +1,10 @@
import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
import { useFonts } from 'expo-font';
import { Stack } from 'expo-router';
import * as SplashScreen from 'expo-splash-screen';
import { useEffect } from 'react';
import 'react-native-reanimated';
import { useColorScheme } from '@/hooks/useColorScheme';
// Prevent the splash screen from auto-hiding before asset loading is complete.
SplashScreen.preventAutoHideAsync();
import { Stack } from "expo-router";
export default function RootLayout() {
const colorScheme = useColorScheme();
const [loaded] = useFonts({
SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'),
});
useEffect(() => {
if (loaded) {
SplashScreen.hideAsync();
}
}, [loaded]);
if (!loaded) {
return null;
}
return (
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="+not-found" />
</Stack>
</ThemeProvider>
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="+not-found" />
</Stack>
);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 4.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 4.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 2.6 MiB

View File

@ -1,37 +0,0 @@
import { StyleSheet } from 'react-native';
import Animated, {
useSharedValue,
useAnimatedStyle,
withTiming,
withRepeat,
withSequence,
} from 'react-native-reanimated';
import { ThemedText } from '@/components/ThemedText';
export function HelloWave() {
const rotationAnimation = useSharedValue(0);
rotationAnimation.value = withRepeat(
withSequence(withTiming(25, { duration: 150 }), withTiming(0, { duration: 150 })),
4 // Run the animation 4 times
);
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ rotate: `${rotationAnimation.value}deg` }],
}));
return (
<Animated.View style={animatedStyle}>
<ThemedText style={styles.text}>👋</ThemedText>
</Animated.View>
);
}
const styles = StyleSheet.create({
text: {
fontSize: 28,
lineHeight: 32,
marginTop: -6,
},
});

View File

@ -0,0 +1,85 @@
import { StyleSheet, Pressable } from "react-native";
import { ThemedText } from "@/components/ThemedText";
import { ThemedView } from "@/components/ThemedView";
import { Colors } from '@/constants/Colors';
import { useColorScheme } from '@/hooks/useColorScheme';
import FontAwesome from "@expo/vector-icons/FontAwesome";
type Props = {
label: string;
theme?: 'primary';
onPress?: () => void;
};
const Button = ({ label, theme, onPress }: Props) => {
const scheme = useColorScheme() ?? 'light';
if (theme === 'primary') {
return (
<ThemedView style={styles.buttonContainer}>
<Pressable
style={[
styles.button,
{backgroundColor: Colors[scheme].text}
]}
onPress={() => alert('You pressed a button.')}
>
<ThemedText
style={[
styles.buttonLabel,
{color: Colors[scheme].background}
]}
>
{label}
</ThemedText>
</Pressable>
</ThemedView>
);
} else {
return (
<ThemedView style={styles.buttonContainer}>
<Pressable
style={[
styles.button,
{backgroundColor: Colors[scheme].text}
]}
onPress={() => alert('You pressed a button.')}
>
<ThemedText
style={[
styles.buttonLabel,
{color: Colors[scheme].background}
]}
>
{label}
</ThemedText>
</Pressable>
</ThemedView>
);
}
};
export default Button;
const styles = StyleSheet.create({
buttonContainer: {
width: 320,
height: 68,
marginHorizontal: 20,
alignItems: 'center',
justifyContent: 'center',
padding: 3,
},
button: {
borderRadius: 10,
width: '100%',
height: '100%',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
},
buttonIcon: {
paddingRight: 8,
},
buttonLabel: {
fontSize: 16,
},
});

View File

@ -17,7 +17,7 @@ export const Colors = {
},
dark: {
text: '#ECEDEE',
background: '#151718',
background: '#2e2f3d',
tint: tintColorDark,
icon: '#9BA1A6',
tabIconDefault: '#9BA1A6',

View File

@ -0,0 +1,37 @@
import { Tabs } from 'expo-router';
import React from 'react';
import { TabBarIcon } from '@/components/navigation/TabBarIcon';
import { Colors } from '@/constants/Colors';
import { useColorScheme } from '@/hooks/useColorScheme';
export default function TabLayout() {
const colorScheme = useColorScheme();
return (
<Tabs
screenOptions={{
tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint,
headerShown: false,
}}>
<Tabs.Screen
name="index"
options={{
title: 'Home',
tabBarIcon: ({ color, focused }) => (
<TabBarIcon name={focused ? 'home' : 'home-outline'} color={color} />
),
}}
/>
<Tabs.Screen
name="explore"
options={{
title: 'Explore',
tabBarIcon: ({ color, focused }) => (
<TabBarIcon name={focused ? 'code-slash' : 'code-slash-outline'} color={color} />
),
}}
/>
</Tabs>
);
}

View File

@ -0,0 +1,70 @@
import { Image, StyleSheet, Platform } from 'react-native';
import { HelloWave } from '@/components/HelloWave';
import ParallaxScrollView from '@/components/ParallaxScrollView';
import { ThemedText } from '@/components/ThemedText';
import { ThemedView } from '@/components/ThemedView';
export default function HomeScreen() {
return (
<ParallaxScrollView
headerBackgroundColor={{ light: '#A1CEDC', dark: '#1D3D47' }}
headerImage={
<Image
source={require('@/assets/images/partial-react-logo.png')}
style={styles.reactLogo}
/>
}>
<ThemedView style={styles.titleContainer}>
<ThemedText type="title">Welcome!</ThemedText>
<HelloWave />
</ThemedView>
<ThemedView style={styles.stepContainer}>
<ThemedText type="subtitle">Step 1: Try it</ThemedText>
<ThemedText>
Edit <ThemedText type="defaultSemiBold">app/(tabs)/index.tsx</ThemedText> to see changes.
Press{' '}
<ThemedText type="defaultSemiBold">
{Platform.select({ ios: 'cmd + d', android: 'cmd + m' })}
</ThemedText>{' '}
to open developer tools.
</ThemedText>
</ThemedView>
<ThemedView style={styles.stepContainer}>
<ThemedText type="subtitle">Step 2: Explore</ThemedText>
<ThemedText>
Tap the Explore tab to learn more about what's included in this starter app.
</ThemedText>
</ThemedView>
<ThemedView style={styles.stepContainer}>
<ThemedText type="subtitle">Step 3: Get a fresh start</ThemedText>
<ThemedText>
When you're ready, run{' '}
<ThemedText type="defaultSemiBold">npm run reset-project</ThemedText> to get a fresh{' '}
<ThemedText type="defaultSemiBold">app</ThemedText> directory. This will move the current{' '}
<ThemedText type="defaultSemiBold">app</ThemedText> to{' '}
<ThemedText type="defaultSemiBold">app-example</ThemedText>.
</ThemedText>
</ThemedView>
</ParallaxScrollView>
);
}
const styles = StyleSheet.create({
titleContainer: {
flexDirection: 'row',
alignItems: 'center',
gap: 8,
},
stepContainer: {
gap: 8,
marginBottom: 8,
},
reactLogo: {
height: 178,
width: 290,
bottom: 0,
left: 0,
position: 'absolute',
},
});

View File

@ -0,0 +1,32 @@
import { Link, Stack } from 'expo-router';
import { StyleSheet } from 'react-native';
import { ThemedText } from '@/components/ThemedText';
import { ThemedView } from '@/components/ThemedView';
export default function NotFoundScreen() {
return (
<>
<Stack.Screen options={{ title: 'Oops!' }} />
<ThemedView style={styles.container}>
<ThemedText type="title">This screen doesn't exist.</ThemedText>
<Link href="/" style={styles.link}>
<ThemedText type="link">Go to home screen!</ThemedText>
</Link>
</ThemedView>
</>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
padding: 20,
},
link: {
marginTop: 15,
paddingVertical: 15,
},
});

37
example/app/_layout.tsx Normal file
View File

@ -0,0 +1,37 @@
import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
import { useFonts } from 'expo-font';
import { Stack } from 'expo-router';
import * as SplashScreen from 'expo-splash-screen';
import { useEffect } from 'react';
import 'react-native-reanimated';
import { useColorScheme } from '@/hooks/useColorScheme';
// Prevent the splash screen from auto-hiding before asset loading is complete.
SplashScreen.preventAutoHideAsync();
export default function RootLayout() {
const colorScheme = useColorScheme();
const [loaded] = useFonts({
SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'),
});
useEffect(() => {
if (loaded) {
SplashScreen.hideAsync();
}
}, [loaded]);
if (!loaded) {
return null;
}
return (
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="+not-found" />
</Stack>
</ThemeProvider>
);
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -0,0 +1,41 @@
import Ionicons from '@expo/vector-icons/Ionicons';
import { PropsWithChildren, useState } from 'react';
import { StyleSheet, TouchableOpacity, useColorScheme } from 'react-native';
import { ThemedText } from '@/components/ThemedText';
import { ThemedView } from '@/components/ThemedView';
import { Colors } from '@/constants/Colors';
export function Collapsible({ children, title }: PropsWithChildren & { title: string }) {
const [isOpen, setIsOpen] = useState(false);
const theme = useColorScheme() ?? 'light';
return (
<ThemedView>
<TouchableOpacity
style={styles.heading}
onPress={() => setIsOpen((value) => !value)}
activeOpacity={0.8}>
<Ionicons
name={isOpen ? 'chevron-down' : 'chevron-forward-outline'}
size={18}
color={theme === 'light' ? Colors.light.icon : Colors.dark.icon}
/>
<ThemedText type="defaultSemiBold">{title}</ThemedText>
</TouchableOpacity>
{isOpen && <ThemedView style={styles.content}>{children}</ThemedView>}
</ThemedView>
);
}
const styles = StyleSheet.create({
heading: {
flexDirection: 'row',
alignItems: 'center',
gap: 6,
},
content: {
marginTop: 6,
marginLeft: 24,
},
});

View File

@ -0,0 +1,24 @@
import { Link } from 'expo-router';
import { openBrowserAsync } from 'expo-web-browser';
import { type ComponentProps } from 'react';
import { Platform } from 'react-native';
type Props = Omit<ComponentProps<typeof Link>, 'href'> & { href: string };
export function ExternalLink({ href, ...rest }: Props) {
return (
<Link
target="_blank"
{...rest}
href={href}
onPress={async (event) => {
if (Platform.OS !== 'web') {
// Prevent the default behavior of linking to the default browser on native.
event.preventDefault();
// Open the link in an in-app browser.
await openBrowserAsync(href);
}
}}
/>
);
}

View File

@ -0,0 +1,76 @@
import type { PropsWithChildren, ReactElement } from 'react';
import { StyleSheet, useColorScheme } from 'react-native';
import Animated, {
interpolate,
useAnimatedRef,
useAnimatedStyle,
useScrollViewOffset,
} from 'react-native-reanimated';
import { ThemedView } from '@/components/ThemedView';
const HEADER_HEIGHT = 250;
type Props = PropsWithChildren<{
headerImage: ReactElement;
headerBackgroundColor: { dark: string; light: string };
}>;
export default function ParallaxScrollView({
children,
headerImage,
headerBackgroundColor,
}: Props) {
const colorScheme = useColorScheme() ?? 'light';
const scrollRef = useAnimatedRef<Animated.ScrollView>();
const scrollOffset = useScrollViewOffset(scrollRef);
const headerAnimatedStyle = useAnimatedStyle(() => {
return {
transform: [
{
translateY: interpolate(
scrollOffset.value,
[-HEADER_HEIGHT, 0, HEADER_HEIGHT],
[-HEADER_HEIGHT / 2, 0, HEADER_HEIGHT * 0.75]
),
},
{
scale: interpolate(scrollOffset.value, [-HEADER_HEIGHT, 0, HEADER_HEIGHT], [2, 1, 1]),
},
],
};
});
return (
<ThemedView style={styles.container}>
<Animated.ScrollView ref={scrollRef} scrollEventThrottle={16}>
<Animated.View
style={[
styles.header,
{ backgroundColor: headerBackgroundColor[colorScheme] },
headerAnimatedStyle,
]}>
{headerImage}
</Animated.View>
<ThemedView style={styles.content}>{children}</ThemedView>
</Animated.ScrollView>
</ThemedView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
header: {
height: 250,
overflow: 'hidden',
},
content: {
flex: 1,
padding: 32,
gap: 16,
overflow: 'hidden',
},
});

View File

@ -0,0 +1,60 @@
import { Text, type TextProps, StyleSheet } from 'react-native';
import { useThemeColor } from '@/hooks/useThemeColor';
export type ThemedTextProps = TextProps & {
lightColor?: string;
darkColor?: string;
type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link';
};
export function ThemedText({
style,
lightColor,
darkColor,
type = 'default',
...rest
}: ThemedTextProps) {
const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text');
return (
<Text
style={[
{ color },
type === 'default' ? styles.default : undefined,
type === 'title' ? styles.title : undefined,
type === 'defaultSemiBold' ? styles.defaultSemiBold : undefined,
type === 'subtitle' ? styles.subtitle : undefined,
type === 'link' ? styles.link : undefined,
style,
]}
{...rest}
/>
);
}
const styles = StyleSheet.create({
default: {
fontSize: 16,
lineHeight: 24,
},
defaultSemiBold: {
fontSize: 16,
lineHeight: 24,
fontWeight: '600',
},
title: {
fontSize: 32,
fontWeight: 'bold',
lineHeight: 32,
},
subtitle: {
fontSize: 20,
fontWeight: 'bold',
},
link: {
lineHeight: 30,
fontSize: 16,
color: '#0a7ea4',
},
});

View File

@ -0,0 +1,14 @@
import { View, type ViewProps } from 'react-native';
import { useThemeColor } from '@/hooks/useThemeColor';
export type ThemedViewProps = ViewProps & {
lightColor?: string;
darkColor?: string;
};
export function ThemedView({ style, lightColor, darkColor, ...otherProps }: ThemedViewProps) {
const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');
return <View style={[{ backgroundColor }, style]} {...otherProps} />;
}

View File

@ -0,0 +1,9 @@
// You can explore the built-in icon families and icons on the web at https://icons.expo.fyi/
import Ionicons from '@expo/vector-icons/Ionicons';
import { type IconProps } from '@expo/vector-icons/build/createIconSet';
import { type ComponentProps } from 'react';
export function TabBarIcon({ style, ...rest }: IconProps<ComponentProps<typeof Ionicons>['name']>) {
return <Ionicons size={28} style={[{ marginBottom: -3 }, style]} {...rest} />;
}

View File

@ -0,0 +1,26 @@
/**
* Below are the colors that are used in the app. The colors are defined in the light and dark mode.
* There are many other ways to style your app. For example, [Nativewind](https://www.nativewind.dev/), [Tamagui](https://tamagui.dev/), [unistyles](https://reactnativeunistyles.vercel.app), etc.
*/
const tintColorLight = '#0a7ea4';
const tintColorDark = '#fff';
export const Colors = {
light: {
text: '#11181C',
background: '#fff',
tint: tintColorLight,
icon: '#687076',
tabIconDefault: '#687076',
tabIconSelected: tintColorLight,
},
dark: {
text: '#ECEDEE',
background: '#151718',
tint: tintColorDark,
icon: '#9BA1A6',
tabIconDefault: '#9BA1A6',
tabIconSelected: tintColorDark,
},
};

View File

@ -0,0 +1 @@
export { useColorScheme } from 'react-native';

View File

@ -0,0 +1,8 @@
// NOTE: The default React Native styling doesn't support server rendering.
// Server rendered styles should not change between the first render of the HTML
// and the first render on the client. Typically, web developers will use CSS media queries
// to render different styles on the client and server, these aren't directly supported in React Native
// but can be achieved using a styling library like Nativewind.
export function useColorScheme() {
return 'light';
}

View File

@ -0,0 +1,22 @@
/**
* Learn more about light and dark modes:
* https://docs.expo.dev/guides/color-schemes/
*/
import { useColorScheme } from 'react-native';
import { Colors } from '@/constants/Colors';
export function useThemeColor(
props: { light?: string; dark?: string },
colorName: keyof typeof Colors.light & keyof typeof Colors.dark
) {
const theme = useColorScheme() ?? 'light';
const colorFromProps = props[theme];
if (colorFromProps) {
return colorFromProps;
} else {
return Colors[theme][colorName];
}
}

10
package-lock.json generated
View File

@ -13,6 +13,7 @@
"expo": "~51.0.28",
"expo-constants": "~16.0.2",
"expo-font": "~12.0.9",
"expo-image": "~1.13.0",
"expo-linking": "~6.3.1",
"expo-router": "~3.5.23",
"expo-splash-screen": "~0.27.5",
@ -10102,6 +10103,15 @@
"expo": "*"
}
},
"node_modules/expo-image": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/expo-image/-/expo-image-1.13.0.tgz",
"integrity": "sha512-0NLDcFmEn4Nh1sXeRvNzDHT+Fl6FXtTol6ki6kYYH0/iDeSFWyIy/Fek6kzDDYAmhipSMR7buPf7VVoHseTbAA==",
"license": "MIT",
"peerDependencies": {
"expo": "*"
}
},
"node_modules/expo-keep-awake": {
"version": "13.0.2",
"resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-13.0.2.tgz",

View File

@ -33,7 +33,8 @@
"react-native-reanimated": "~3.10.1",
"react-native-safe-area-context": "4.10.5",
"react-native-screens": "3.31.1",
"react-native-web": "~0.19.10"
"react-native-web": "~0.19.10",
"expo-image": "~1.13.0"
},
"devDependencies": {
"@babel/core": "^7.20.0",