Pretty up code. Add all my default stuff that I like
This commit is contained in:
parent
5a821fc6b5
commit
0dfb7f190d
@ -1,21 +1,23 @@
|
|||||||
import { Tabs } from 'expo-router';
|
import { Tabs } from 'expo-router';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Platform } from 'react-native';
|
import { Platform } from 'react-native';
|
||||||
|
import { HapticTab } from '@/components/default/HapticTab';
|
||||||
import { HapticTab } from '@/components/HapticTab';
|
|
||||||
import { IconSymbol } from '@/components/ui/IconSymbol';
|
import { IconSymbol } from '@/components/ui/IconSymbol';
|
||||||
import TabBarBackground from '@/components/ui/TabBarBackground';
|
import TabBarBackground from '@/components/ui/TabBarBackground';
|
||||||
import { Colors } from '@/constants/Colors';
|
import { Colors } from '@/constants/Colors';
|
||||||
import { useColorScheme } from '@/hooks/useColorScheme';
|
import { useColorScheme } from '@/hooks/useColorScheme';
|
||||||
|
|
||||||
export default function TabLayout() {
|
const TabLayout = () => {
|
||||||
const colorScheme = useColorScheme();
|
const scheme = useColorScheme() ?? 'dark';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs
|
<Tabs
|
||||||
screenOptions={{
|
screenOptions={{
|
||||||
tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint,
|
tabBarActiveTintColor: Colors[scheme].tint,
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
|
headerStyle: {
|
||||||
|
backgroundColor: Colors[scheme].background,
|
||||||
|
},
|
||||||
tabBarButton: HapticTab,
|
tabBarButton: HapticTab,
|
||||||
tabBarBackground: TabBarBackground,
|
tabBarBackground: TabBarBackground,
|
||||||
tabBarStyle: Platform.select({
|
tabBarStyle: Platform.select({
|
||||||
@ -23,7 +25,11 @@ export default function TabLayout() {
|
|||||||
// Use a transparent background on iOS to show the blur effect
|
// Use a transparent background on iOS to show the blur effect
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
},
|
},
|
||||||
default: {},
|
default: {
|
||||||
|
backgroundColor: Colors[scheme].background,
|
||||||
|
borderTopColor: Colors[scheme].tint,
|
||||||
|
borderTopWidth: 1,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -35,12 +41,13 @@ export default function TabLayout() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Tabs.Screen
|
<Tabs.Screen
|
||||||
name='explore'
|
name='settings'
|
||||||
options={{
|
options={{
|
||||||
title: 'Explore',
|
title: 'Settings',
|
||||||
tabBarIcon: ({ color }) => <IconSymbol size={28} name='paperplane.fill' color={color} />,
|
tabBarIcon: ({ color }) => <IconSymbol size={28} name='gearshape.fill' color={color} />,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
export default TabLayout;
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
import { Image, StyleSheet, Platform } from 'react-native';
|
import { Image, StyleSheet, Platform } from 'react-native';
|
||||||
|
import ParallaxScrollView from '@/components/default/ParallaxScrollView';
|
||||||
|
import { ThemedText, ThemedView } from '@/components/theme/Theme';
|
||||||
|
|
||||||
import { HelloWave } from '@/components/HelloWave';
|
const HomeScreen = () => {
|
||||||
import ParallaxScrollView from '@/components/ParallaxScrollView';
|
|
||||||
import { ThemedText } from '@/components/ThemedText';
|
|
||||||
import { ThemedView } from '@/components/ThemedView';
|
|
||||||
|
|
||||||
export default function HomeScreen() {
|
|
||||||
return (
|
return (
|
||||||
<ParallaxScrollView
|
<ParallaxScrollView
|
||||||
headerBackgroundColor={{ light: '#A1CEDC', dark: '#1D3D47' }}
|
headerBackgroundColor={{ light: '#A1CEDC', dark: '#1D3D47' }}
|
||||||
|
headerHeight={200}
|
||||||
headerImage={
|
headerImage={
|
||||||
<Image
|
<Image
|
||||||
source={require('@/assets/images/partial-react-logo.png')}
|
source={require('@/assets/images/partial-react-logo.png')}
|
||||||
@ -18,7 +16,6 @@ export default function HomeScreen() {
|
|||||||
>
|
>
|
||||||
<ThemedView style={styles.titleContainer}>
|
<ThemedView style={styles.titleContainer}>
|
||||||
<ThemedText type='title'>Welcome!</ThemedText>
|
<ThemedText type='title'>Welcome!</ThemedText>
|
||||||
<HelloWave />
|
|
||||||
</ThemedView>
|
</ThemedView>
|
||||||
<ThemedView style={styles.stepContainer}>
|
<ThemedView style={styles.stepContainer}>
|
||||||
<ThemedText type='subtitle'>Step 1: Try it</ThemedText>
|
<ThemedText type='subtitle'>Step 1: Try it</ThemedText>
|
||||||
@ -53,7 +50,8 @@ export default function HomeScreen() {
|
|||||||
</ThemedView>
|
</ThemedView>
|
||||||
</ParallaxScrollView>
|
</ParallaxScrollView>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
export default HomeScreen;
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
titleContainer: {
|
titleContainer: {
|
||||||
|
@ -1,27 +1,21 @@
|
|||||||
import { StyleSheet, Image, Platform } from 'react-native';
|
import { StyleSheet, Image, Platform } from 'react-native';
|
||||||
|
import { Collapsible } from '@/components/default/Collapsible';
|
||||||
import { Collapsible } from '@/components/Collapsible';
|
import { ExternalLink } from '@/components/default/ExternalLink';
|
||||||
import { ExternalLink } from '@/components/ExternalLink';
|
import ParallaxScrollView from '@/components/default/ParallaxScrollView';
|
||||||
import ParallaxScrollView from '@/components/ParallaxScrollView';
|
import { ThemedText, ThemedView } from '@/components/theme/Theme';
|
||||||
import { ThemedText } from '@/components/ThemedText';
|
|
||||||
import { ThemedView } from '@/components/ThemedView';
|
|
||||||
import { IconSymbol } from '@/components/ui/IconSymbol';
|
import { IconSymbol } from '@/components/ui/IconSymbol';
|
||||||
|
|
||||||
export default function TabTwoScreen() {
|
const TabTwoScreen = () => {
|
||||||
return (
|
return (
|
||||||
<ParallaxScrollView
|
<ParallaxScrollView
|
||||||
headerBackgroundColor={{ light: '#D0D0D0', dark: '#353636' }}
|
headerBackgroundColor={{ light: '#D0D0D0', dark: '#353636' }}
|
||||||
|
headerHeight={150}
|
||||||
headerImage={
|
headerImage={
|
||||||
<IconSymbol
|
<IconSymbol size={200} color='#808080' name='gear.circle' style={styles.headerImage} />
|
||||||
size={310}
|
|
||||||
color='#808080'
|
|
||||||
name='chevron.left.forwardslash.chevron.right'
|
|
||||||
style={styles.headerImage}
|
|
||||||
/>
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<ThemedView style={styles.titleContainer}>
|
<ThemedView style={styles.titleContainer}>
|
||||||
<ThemedText type='title'>Explore</ThemedText>
|
<ThemedText type='title'>Settings</ThemedText>
|
||||||
</ThemedView>
|
</ThemedView>
|
||||||
<ThemedText>This app includes example code to help you get started.</ThemedText>
|
<ThemedText>This app includes example code to help you get started.</ThemedText>
|
||||||
<Collapsible title='File-based routing'>
|
<Collapsible title='File-based routing'>
|
||||||
@ -94,7 +88,8 @@ export default function TabTwoScreen() {
|
|||||||
</Collapsible>
|
</Collapsible>
|
||||||
</ParallaxScrollView>
|
</ParallaxScrollView>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
export default TabTwoScreen;
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
headerImage: {
|
headerImage: {
|
@ -1,22 +1,22 @@
|
|||||||
import { Link, Stack } from 'expo-router';
|
import { Link, Stack } from 'expo-router';
|
||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet } from 'react-native';
|
||||||
|
import { ThemedText, ThemedView } from '@/components/theme/Theme';
|
||||||
|
import TextButton from '@/components/theme/buttons/TextButton';
|
||||||
|
|
||||||
import { ThemedText } from '@/components/ThemedText';
|
const NotFoundScreen = () => {
|
||||||
import { ThemedView } from '@/components/ThemedView';
|
|
||||||
|
|
||||||
export default function NotFoundScreen() {
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack.Screen options={{ title: 'Oops!' }} />
|
<Stack.Screen options={{ title: 'Page not found' }} />
|
||||||
<ThemedView style={styles.container}>
|
<ThemedView style={styles.container}>
|
||||||
<ThemedText type='title'>This screen doesn't exist.</ThemedText>
|
<ThemedText type='title'>This screen doesn't exist.</ThemedText>
|
||||||
<Link href='/' style={styles.link}>
|
<Link href='/'>
|
||||||
<ThemedText type='link'>Go to home screen!</ThemedText>
|
<TextButton width={200} height={45} text='Go to home screen' fontSize={24} />
|
||||||
</Link>
|
</Link>
|
||||||
</ThemedView>
|
</ThemedView>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
export default NotFoundScreen;
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
@ -25,8 +25,4 @@ const styles = StyleSheet.create({
|
|||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
padding: 20,
|
padding: 20,
|
||||||
},
|
},
|
||||||
link: {
|
|
||||||
marginTop: 15,
|
|
||||||
paddingVertical: 15,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
@ -5,14 +5,13 @@ import * as SplashScreen from 'expo-splash-screen';
|
|||||||
import { StatusBar } from 'expo-status-bar';
|
import { StatusBar } from 'expo-status-bar';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import 'react-native-reanimated';
|
import 'react-native-reanimated';
|
||||||
|
|
||||||
import { useColorScheme } from '@/hooks/useColorScheme';
|
import { useColorScheme } from '@/hooks/useColorScheme';
|
||||||
|
|
||||||
// Prevent the splash screen from auto-hiding before asset loading is complete.
|
// Prevent the splash screen from auto-hiding before asset loading is complete.
|
||||||
SplashScreen.preventAutoHideAsync();
|
SplashScreen.preventAutoHideAsync();
|
||||||
|
|
||||||
export default function RootLayout() {
|
const RootLayout = () => {
|
||||||
const colorScheme = useColorScheme();
|
const scheme = useColorScheme() ?? 'dark';
|
||||||
const [loaded] = useFonts({
|
const [loaded] = useFonts({
|
||||||
SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'),
|
SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'),
|
||||||
});
|
});
|
||||||
@ -28,7 +27,7 @@ export default function RootLayout() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
|
<ThemeProvider value={scheme === 'dark' ? DarkTheme : DefaultTheme}>
|
||||||
<Stack>
|
<Stack>
|
||||||
<Stack.Screen name='(tabs)' options={{ headerShown: false }} />
|
<Stack.Screen name='(tabs)' options={{ headerShown: false }} />
|
||||||
<Stack.Screen name='+not-found' />
|
<Stack.Screen name='+not-found' />
|
||||||
@ -36,4 +35,5 @@ export default function RootLayout() {
|
|||||||
<StatusBar style='auto' />
|
<StatusBar style='auto' />
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
export default RootLayout;
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
import { useEffect } from 'react';
|
|
||||||
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);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
});
|
|
@ -1,14 +0,0 @@
|
|||||||
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} />;
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import renderer from 'react-test-renderer';
|
|
||||||
|
|
||||||
import { ThemedText } from '../ThemedText';
|
|
||||||
|
|
||||||
it(`renders correctly`, () => {
|
|
||||||
const tree = renderer.create(<ThemedText>Snapshot test!</ThemedText>).toJSON();
|
|
||||||
|
|
||||||
expect(tree).toMatchSnapshot();
|
|
||||||
});
|
|
@ -1,24 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`renders correctly 1`] = `
|
|
||||||
<Text
|
|
||||||
style={
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"color": "#11181C",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fontSize": 16,
|
|
||||||
"lineHeight": 24,
|
|
||||||
},
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Snapshot test!
|
|
||||||
</Text>
|
|
||||||
`;
|
|
@ -1,15 +1,13 @@
|
|||||||
import { PropsWithChildren, useState } from 'react';
|
import { PropsWithChildren, useState } from 'react';
|
||||||
import { StyleSheet, TouchableOpacity } from 'react-native';
|
import { StyleSheet, TouchableOpacity } from 'react-native';
|
||||||
|
import { ThemedText, ThemedView } from '@/components/theme/Theme';
|
||||||
import { ThemedText } from '@/components/ThemedText';
|
|
||||||
import { ThemedView } from '@/components/ThemedView';
|
|
||||||
import { IconSymbol } from '@/components/ui/IconSymbol';
|
import { IconSymbol } from '@/components/ui/IconSymbol';
|
||||||
import { Colors } from '@/constants/Colors';
|
import { Colors } from '@/constants/Colors';
|
||||||
import { useColorScheme } from '@/hooks/useColorScheme';
|
import { useColorScheme } from '@/hooks/useColorScheme';
|
||||||
|
|
||||||
export function Collapsible({ children, title }: PropsWithChildren & { title: string }) {
|
export const Collapsible = ({ children, title }: PropsWithChildren & { title: string }) => {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const theme = useColorScheme() ?? 'light';
|
const scheme = useColorScheme() ?? 'dark';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemedView>
|
<ThemedView>
|
||||||
@ -22,7 +20,7 @@ export function Collapsible({ children, title }: PropsWithChildren & { title: st
|
|||||||
name='chevron.right'
|
name='chevron.right'
|
||||||
size={18}
|
size={18}
|
||||||
weight='medium'
|
weight='medium'
|
||||||
color={theme === 'light' ? Colors.light.icon : Colors.dark.icon}
|
color={scheme === 'light' ? Colors.light.icon : Colors.dark.icon}
|
||||||
style={{ transform: [{ rotate: isOpen ? '90deg' : '0deg' }] }}
|
style={{ transform: [{ rotate: isOpen ? '90deg' : '0deg' }] }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@ -31,7 +29,7 @@ export function Collapsible({ children, title }: PropsWithChildren & { title: st
|
|||||||
{isOpen && <ThemedView style={styles.content}>{children}</ThemedView>}
|
{isOpen && <ThemedView style={styles.content}>{children}</ThemedView>}
|
||||||
</ThemedView>
|
</ThemedView>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
heading: {
|
heading: {
|
@ -1,16 +1,16 @@
|
|||||||
import { Link } from 'expo-router';
|
import { Link, RelativePathString } from 'expo-router';
|
||||||
import { openBrowserAsync } from 'expo-web-browser';
|
import { openBrowserAsync } from 'expo-web-browser';
|
||||||
import { type ComponentProps } from 'react';
|
import { type ComponentProps } from 'react';
|
||||||
import { Platform } from 'react-native';
|
import { Platform } from 'react-native';
|
||||||
|
|
||||||
type Props = Omit<ComponentProps<typeof Link>, 'href'> & { href: string };
|
type Props = Omit<ComponentProps<typeof Link>, 'href'> & { href: string };
|
||||||
|
|
||||||
export function ExternalLink({ href, ...rest }: Props) {
|
export const ExternalLink = ({ href, ...rest }: Props) => {
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
target='_blank'
|
target='_blank'
|
||||||
{...rest}
|
{...rest}
|
||||||
href={href}
|
href={href as RelativePathString}
|
||||||
onPress={async (event) => {
|
onPress={async (event) => {
|
||||||
if (Platform.OS !== 'web') {
|
if (Platform.OS !== 'web') {
|
||||||
// Prevent the default behavior of linking to the default browser on native.
|
// Prevent the default behavior of linking to the default browser on native.
|
||||||
@ -21,4 +21,4 @@ export function ExternalLink({ href, ...rest }: Props) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
};
|
@ -2,7 +2,7 @@ import { BottomTabBarButtonProps } from '@react-navigation/bottom-tabs';
|
|||||||
import { PlatformPressable } from '@react-navigation/elements';
|
import { PlatformPressable } from '@react-navigation/elements';
|
||||||
import * as Haptics from 'expo-haptics';
|
import * as Haptics from 'expo-haptics';
|
||||||
|
|
||||||
export function HapticTab(props: BottomTabBarButtonProps) {
|
export const HapticTab = (props: BottomTabBarButtonProps) => {
|
||||||
return (
|
return (
|
||||||
<PlatformPressable
|
<PlatformPressable
|
||||||
{...props}
|
{...props}
|
||||||
@ -15,4 +15,4 @@ export function HapticTab(props: BottomTabBarButtonProps) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
};
|
@ -6,8 +6,7 @@ import Animated, {
|
|||||||
useAnimatedStyle,
|
useAnimatedStyle,
|
||||||
useScrollViewOffset,
|
useScrollViewOffset,
|
||||||
} from 'react-native-reanimated';
|
} from 'react-native-reanimated';
|
||||||
|
import { ThemedView } from '@/components/theme/Theme';
|
||||||
import { ThemedView } from '@/components/ThemedView';
|
|
||||||
import { useBottomTabOverflow } from '@/components/ui/TabBarBackground';
|
import { useBottomTabOverflow } from '@/components/ui/TabBarBackground';
|
||||||
import { useColorScheme } from '@/hooks/useColorScheme';
|
import { useColorScheme } from '@/hooks/useColorScheme';
|
||||||
|
|
||||||
@ -16,14 +15,16 @@ const HEADER_HEIGHT = 250;
|
|||||||
type Props = PropsWithChildren<{
|
type Props = PropsWithChildren<{
|
||||||
headerImage: ReactElement;
|
headerImage: ReactElement;
|
||||||
headerBackgroundColor: { dark: string; light: string };
|
headerBackgroundColor: { dark: string; light: string };
|
||||||
|
headerHeight?: number;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export default function ParallaxScrollView({
|
const ParallaxScrollView = ({
|
||||||
children,
|
children,
|
||||||
headerImage,
|
headerImage,
|
||||||
headerBackgroundColor,
|
headerBackgroundColor,
|
||||||
}: Props) {
|
headerHeight = HEADER_HEIGHT,
|
||||||
const colorScheme = useColorScheme() ?? 'light';
|
}: Props) => {
|
||||||
|
const scheme = useColorScheme() ?? 'dark';
|
||||||
const scrollRef = useAnimatedRef<Animated.ScrollView>();
|
const scrollRef = useAnimatedRef<Animated.ScrollView>();
|
||||||
const scrollOffset = useScrollViewOffset(scrollRef);
|
const scrollOffset = useScrollViewOffset(scrollRef);
|
||||||
const bottom = useBottomTabOverflow();
|
const bottom = useBottomTabOverflow();
|
||||||
@ -33,12 +34,12 @@ export default function ParallaxScrollView({
|
|||||||
{
|
{
|
||||||
translateY: interpolate(
|
translateY: interpolate(
|
||||||
scrollOffset.value,
|
scrollOffset.value,
|
||||||
[-HEADER_HEIGHT, 0, HEADER_HEIGHT],
|
[-headerHeight, 0, headerHeight],
|
||||||
[-HEADER_HEIGHT / 2, 0, HEADER_HEIGHT * 0.75],
|
[-headerHeight / 2, 0, headerHeight * 0.75],
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
scale: interpolate(scrollOffset.value, [-HEADER_HEIGHT, 0, HEADER_HEIGHT], [2, 1, 1]),
|
scale: interpolate(scrollOffset.value, [-headerHeight, 0, headerHeight], [2, 1, 1]),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@ -55,7 +56,10 @@ export default function ParallaxScrollView({
|
|||||||
<Animated.View
|
<Animated.View
|
||||||
style={[
|
style={[
|
||||||
styles.header,
|
styles.header,
|
||||||
{ backgroundColor: headerBackgroundColor[colorScheme] },
|
{
|
||||||
|
backgroundColor: headerBackgroundColor[scheme],
|
||||||
|
height: headerHeight,
|
||||||
|
},
|
||||||
headerAnimatedStyle,
|
headerAnimatedStyle,
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
@ -65,14 +69,14 @@ export default function ParallaxScrollView({
|
|||||||
</Animated.ScrollView>
|
</Animated.ScrollView>
|
||||||
</ThemedView>
|
</ThemedView>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
export default ParallaxScrollView;
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
},
|
||||||
header: {
|
header: {
|
||||||
height: HEADER_HEIGHT,
|
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
@ -1,22 +1,30 @@
|
|||||||
|
import { View, type ViewProps } from 'react-native';
|
||||||
import { Text, type TextProps, StyleSheet } from 'react-native';
|
import { Text, type TextProps, StyleSheet } from 'react-native';
|
||||||
|
|
||||||
import { useThemeColor } from '@/hooks/useThemeColor';
|
import { useThemeColor } from '@/hooks/useThemeColor';
|
||||||
|
|
||||||
|
export type ThemedViewProps = ViewProps & {
|
||||||
|
lightColor?: string;
|
||||||
|
darkColor?: string;
|
||||||
|
};
|
||||||
export type ThemedTextProps = TextProps & {
|
export type ThemedTextProps = TextProps & {
|
||||||
lightColor?: string;
|
lightColor?: string;
|
||||||
darkColor?: string;
|
darkColor?: string;
|
||||||
type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link';
|
type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link';
|
||||||
};
|
};
|
||||||
|
|
||||||
export function ThemedText({
|
export const ThemedView = ({ style, lightColor, darkColor, ...otherProps }: ThemedViewProps) => {
|
||||||
|
const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');
|
||||||
|
return <View style={[{ backgroundColor }, style]} {...otherProps} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ThemedText = ({
|
||||||
style,
|
style,
|
||||||
lightColor,
|
lightColor,
|
||||||
darkColor,
|
darkColor,
|
||||||
type = 'default',
|
type = 'default',
|
||||||
...rest
|
...rest
|
||||||
}: ThemedTextProps) {
|
}: ThemedTextProps) => {
|
||||||
const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text');
|
const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
@ -31,7 +39,7 @@ export function ThemedText({
|
|||||||
{...rest}
|
{...rest}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
default: {
|
default: {
|
58
components/theme/buttons/Button.tsx
Normal file
58
components/theme/buttons/Button.tsx
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { StyleSheet, Pressable } from 'react-native';
|
||||||
|
import { ThemedView } from '@/components/theme/Theme';
|
||||||
|
import { Colors } from '@/constants/Colors';
|
||||||
|
import { useColorScheme } from '@/hooks/useColorScheme';
|
||||||
|
import { StyleProp, ViewStyle } from 'react-native';
|
||||||
|
|
||||||
|
const DEFAULT_WIDTH = 320;
|
||||||
|
const DEFAULT_HEIGHT = 68;
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
onPress?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Button = ({
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
children,
|
||||||
|
onPress,
|
||||||
|
}: Props & React.ComponentProps<typeof Pressable>) => {
|
||||||
|
const scheme = useColorScheme() ?? 'dark';
|
||||||
|
return (
|
||||||
|
<ThemedView
|
||||||
|
style={[
|
||||||
|
styles.buttonContainer,
|
||||||
|
{
|
||||||
|
width: width ?? DEFAULT_WIDTH,
|
||||||
|
height: height ?? DEFAULT_HEIGHT,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Pressable
|
||||||
|
style={[styles.button, { backgroundColor: Colors[scheme].text }]}
|
||||||
|
onPress={onPress}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Pressable>
|
||||||
|
</ThemedView>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default Button;
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
buttonContainer: {
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: 3,
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
borderRadius: 10,
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
|
});
|
33
components/theme/buttons/TextButton.tsx
Normal file
33
components/theme/buttons/TextButton.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import Button from '@/components/theme/buttons/Button';
|
||||||
|
import { ThemedText } from '@/components/theme/Theme';
|
||||||
|
import { Colors } from '@/constants/Colors';
|
||||||
|
import { useColorScheme } from '@/hooks/useColorScheme';
|
||||||
|
|
||||||
|
const DEFAULT_FONT_SIZE = 16;
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
text: string;
|
||||||
|
fontSize?: number;
|
||||||
|
onPress?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const TextButton = ({ width, height, text, fontSize, onPress }: Props) => {
|
||||||
|
const scheme = useColorScheme() ?? 'dark';
|
||||||
|
return (
|
||||||
|
<Button width={width} height={height} onPress={onPress}>
|
||||||
|
<ThemedText
|
||||||
|
style={[
|
||||||
|
{
|
||||||
|
color: Colors[scheme].background,
|
||||||
|
fontSize: fontSize ?? DEFAULT_FONT_SIZE,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</ThemedText>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default TextButton;
|
@ -1,7 +1,7 @@
|
|||||||
import { SymbolView, SymbolViewProps, SymbolWeight } from 'expo-symbols';
|
import { SymbolView, SymbolViewProps, SymbolWeight } from 'expo-symbols';
|
||||||
import { StyleProp, ViewStyle } from 'react-native';
|
import { StyleProp, ViewStyle } from 'react-native';
|
||||||
|
|
||||||
export function IconSymbol({
|
export const IconSymbol = ({
|
||||||
name,
|
name,
|
||||||
size = 24,
|
size = 24,
|
||||||
color,
|
color,
|
||||||
@ -13,7 +13,7 @@ export function IconSymbol({
|
|||||||
color: string;
|
color: string;
|
||||||
style?: StyleProp<ViewStyle>;
|
style?: StyleProp<ViewStyle>;
|
||||||
weight?: SymbolWeight;
|
weight?: SymbolWeight;
|
||||||
}) {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<SymbolView
|
<SymbolView
|
||||||
weight={weight}
|
weight={weight}
|
||||||
@ -29,4 +29,4 @@ export function IconSymbol({
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
|
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
|
||||||
import { SymbolWeight } from 'expo-symbols';
|
import { SymbolWeight } from 'expo-symbols';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { OpaqueColorValue, StyleProp, ViewStyle } from 'react-native';
|
import { OpaqueColorValue, StyleProp, TextStyle } from 'react-native';
|
||||||
|
|
||||||
// Add your SFSymbol to MaterialIcons mappings here.
|
// Add your SFSymbol to MaterialIcons mappings here.
|
||||||
const MAPPING = {
|
const MAPPING = {
|
||||||
@ -27,7 +27,7 @@ export type IconSymbolName = keyof typeof MAPPING;
|
|||||||
*
|
*
|
||||||
* Icon `name`s are based on SFSymbols and require manual mapping to MaterialIcons.
|
* Icon `name`s are based on SFSymbols and require manual mapping to MaterialIcons.
|
||||||
*/
|
*/
|
||||||
export function IconSymbol({
|
export const IconSymbol = ({
|
||||||
name,
|
name,
|
||||||
size = 24,
|
size = 24,
|
||||||
color,
|
color,
|
||||||
@ -36,8 +36,8 @@ export function IconSymbol({
|
|||||||
name: IconSymbolName;
|
name: IconSymbolName;
|
||||||
size?: number;
|
size?: number;
|
||||||
color: string | OpaqueColorValue;
|
color: string | OpaqueColorValue;
|
||||||
style?: StyleProp<ViewStyle>;
|
style?: StyleProp<TextStyle>;
|
||||||
weight?: SymbolWeight;
|
weight?: SymbolWeight;
|
||||||
}) {
|
}) => {
|
||||||
return <MaterialIcons color={color} size={size} name={MAPPING[name]} style={style} />;
|
return <MaterialIcons color={color} size={size} name={MAPPING[name]} style={style} />;
|
||||||
}
|
};
|
||||||
|
@ -3,7 +3,7 @@ import { BlurView } from 'expo-blur';
|
|||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet } from 'react-native';
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
|
|
||||||
export default function BlurTabBarBackground() {
|
const BlurTabBarBackground = () => {
|
||||||
return (
|
return (
|
||||||
<BlurView
|
<BlurView
|
||||||
// System chrome material automatically adapts to the system's theme
|
// System chrome material automatically adapts to the system's theme
|
||||||
@ -13,10 +13,11 @@ export default function BlurTabBarBackground() {
|
|||||||
style={StyleSheet.absoluteFill}
|
style={StyleSheet.absoluteFill}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
export default BlurTabBarBackground;
|
||||||
|
|
||||||
export function useBottomTabOverflow() {
|
export const useBottomTabOverflow = () => {
|
||||||
const tabHeight = useBottomTabBarHeight();
|
const tabHeight = useBottomTabBarHeight();
|
||||||
const { bottom } = useSafeAreaInsets();
|
const { bottom } = useSafeAreaInsets();
|
||||||
return tabHeight - bottom;
|
return tabHeight - bottom;
|
||||||
}
|
};
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
// This is a shim for web and Android where the tab bar is generally opaque.
|
// This is a shim for web and Android where the tab bar is generally opaque.
|
||||||
export default undefined;
|
export default undefined;
|
||||||
|
export const useBottomTabOverflow = () => {
|
||||||
export function useBottomTabOverflow() {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user