From 0dfb7f190d5c7ab54c15d50f0811ca41432b7255 Mon Sep 17 00:00:00 2001 From: gibbyb Date: Tue, 28 Jan 2025 10:17:33 -0600 Subject: [PATCH] Pretty up code. Add all my default stuff that I like --- app/(tabs)/_layout.tsx | 27 +++++---- app/(tabs)/index.tsx | 14 ++--- app/(tabs)/{explore.tsx => settings.tsx} | 25 ++++---- app/+not-found.tsx | 20 +++---- app/_layout.tsx | 10 ++-- components/HelloWave.tsx | 40 ------------- components/ThemedView.tsx | 14 ----- components/__tests__/ThemedText-test.tsx | 10 ---- .../__snapshots__/ThemedText-test.tsx.snap | 24 -------- components/{ => default}/Collapsible.tsx | 12 ++-- components/{ => default}/ExternalLink.tsx | 8 +-- components/{ => default}/HapticTab.tsx | 4 +- .../{ => default}/ParallaxScrollView.tsx | 26 +++++---- .../{ThemedText.tsx => theme/Theme.tsx} | 18 ++++-- components/theme/buttons/Button.tsx | 58 +++++++++++++++++++ components/theme/buttons/TextButton.tsx | 33 +++++++++++ components/ui/IconSymbol.ios.tsx | 6 +- components/ui/IconSymbol.tsx | 10 ++-- components/ui/TabBarBackground.ios.tsx | 9 +-- components/ui/TabBarBackground.tsx | 5 +- 20 files changed, 191 insertions(+), 182 deletions(-) rename app/(tabs)/{explore.tsx => settings.tsx} (87%) delete mode 100644 components/HelloWave.tsx delete mode 100644 components/ThemedView.tsx delete mode 100644 components/__tests__/ThemedText-test.tsx delete mode 100644 components/__tests__/__snapshots__/ThemedText-test.tsx.snap rename components/{ => default}/Collapsible.tsx (76%) rename components/{ => default}/ExternalLink.tsx (78%) rename components/{ => default}/HapticTab.tsx (88%) rename components/{ => default}/ParallaxScrollView.tsx (75%) rename components/{ThemedText.tsx => theme/Theme.tsx} (72%) create mode 100644 components/theme/buttons/Button.tsx create mode 100644 components/theme/buttons/TextButton.tsx diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx index 0063071..db364e6 100644 --- a/app/(tabs)/_layout.tsx +++ b/app/(tabs)/_layout.tsx @@ -1,21 +1,23 @@ import { Tabs } from 'expo-router'; import React from 'react'; import { Platform } from 'react-native'; - -import { HapticTab } from '@/components/HapticTab'; +import { HapticTab } from '@/components/default/HapticTab'; import { IconSymbol } from '@/components/ui/IconSymbol'; import TabBarBackground from '@/components/ui/TabBarBackground'; import { Colors } from '@/constants/Colors'; import { useColorScheme } from '@/hooks/useColorScheme'; -export default function TabLayout() { - const colorScheme = useColorScheme(); +const TabLayout = () => { + const scheme = useColorScheme() ?? 'dark'; return ( @@ -35,12 +41,13 @@ export default function TabLayout() { }} /> , + title: 'Settings', + tabBarIcon: ({ color }) => , }} /> ); -} +}; +export default TabLayout; diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index f0a1f3e..11ace04 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -1,14 +1,12 @@ 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'; -import ParallaxScrollView from '@/components/ParallaxScrollView'; -import { ThemedText } from '@/components/ThemedText'; -import { ThemedView } from '@/components/ThemedView'; - -export default function HomeScreen() { +const HomeScreen = () => { return ( Welcome! - Step 1: Try it @@ -53,7 +50,8 @@ export default function HomeScreen() { ); -} +}; +export default HomeScreen; const styles = StyleSheet.create({ titleContainer: { diff --git a/app/(tabs)/explore.tsx b/app/(tabs)/settings.tsx similarity index 87% rename from app/(tabs)/explore.tsx rename to app/(tabs)/settings.tsx index 016867b..406376e 100644 --- a/app/(tabs)/explore.tsx +++ b/app/(tabs)/settings.tsx @@ -1,27 +1,21 @@ import { StyleSheet, Image, Platform } from 'react-native'; - -import { Collapsible } from '@/components/Collapsible'; -import { ExternalLink } from '@/components/ExternalLink'; -import ParallaxScrollView from '@/components/ParallaxScrollView'; -import { ThemedText } from '@/components/ThemedText'; -import { ThemedView } from '@/components/ThemedView'; +import { Collapsible } from '@/components/default/Collapsible'; +import { ExternalLink } from '@/components/default/ExternalLink'; +import ParallaxScrollView from '@/components/default/ParallaxScrollView'; +import { ThemedText, ThemedView } from '@/components/theme/Theme'; import { IconSymbol } from '@/components/ui/IconSymbol'; -export default function TabTwoScreen() { +const TabTwoScreen = () => { return ( + } > - Explore + Settings This app includes example code to help you get started. @@ -94,7 +88,8 @@ export default function TabTwoScreen() { ); -} +}; +export default TabTwoScreen; const styles = StyleSheet.create({ headerImage: { diff --git a/app/+not-found.tsx b/app/+not-found.tsx index ba91e7f..59a6497 100644 --- a/app/+not-found.tsx +++ b/app/+not-found.tsx @@ -1,22 +1,22 @@ import { Link, Stack } from 'expo-router'; import { StyleSheet } from 'react-native'; +import { ThemedText, ThemedView } from '@/components/theme/Theme'; +import TextButton from '@/components/theme/buttons/TextButton'; -import { ThemedText } from '@/components/ThemedText'; -import { ThemedView } from '@/components/ThemedView'; - -export default function NotFoundScreen() { +const NotFoundScreen = () => { return ( <> - + This screen doesn't exist. - - Go to home screen! + + ); -} +}; +export default NotFoundScreen; const styles = StyleSheet.create({ container: { @@ -25,8 +25,4 @@ const styles = StyleSheet.create({ justifyContent: 'center', padding: 20, }, - link: { - marginTop: 15, - paddingVertical: 15, - }, }); diff --git a/app/_layout.tsx b/app/_layout.tsx index 13ca20f..efe8a45 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -5,14 +5,13 @@ import * as SplashScreen from 'expo-splash-screen'; import { StatusBar } from 'expo-status-bar'; 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 RootLayout = () => { + const scheme = useColorScheme() ?? 'dark'; const [loaded] = useFonts({ SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'), }); @@ -28,7 +27,7 @@ export default function RootLayout() { } return ( - + @@ -36,4 +35,5 @@ export default function RootLayout() { ); -} +}; +export default RootLayout; diff --git a/components/HelloWave.tsx b/components/HelloWave.tsx deleted file mode 100644 index 7e4fd88..0000000 --- a/components/HelloWave.tsx +++ /dev/null @@ -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 ( - - 👋 - - ); -} - -const styles = StyleSheet.create({ - text: { - fontSize: 28, - lineHeight: 32, - marginTop: -6, - }, -}); diff --git a/components/ThemedView.tsx b/components/ThemedView.tsx deleted file mode 100644 index 4d2cb09..0000000 --- a/components/ThemedView.tsx +++ /dev/null @@ -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 ; -} diff --git a/components/__tests__/ThemedText-test.tsx b/components/__tests__/ThemedText-test.tsx deleted file mode 100644 index 1ac3225..0000000 --- a/components/__tests__/ThemedText-test.tsx +++ /dev/null @@ -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(Snapshot test!).toJSON(); - - expect(tree).toMatchSnapshot(); -}); diff --git a/components/__tests__/__snapshots__/ThemedText-test.tsx.snap b/components/__tests__/__snapshots__/ThemedText-test.tsx.snap deleted file mode 100644 index b68e53e..0000000 --- a/components/__tests__/__snapshots__/ThemedText-test.tsx.snap +++ /dev/null @@ -1,24 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders correctly 1`] = ` - - Snapshot test! - -`; diff --git a/components/Collapsible.tsx b/components/default/Collapsible.tsx similarity index 76% rename from components/Collapsible.tsx rename to components/default/Collapsible.tsx index e8942c8..90219fb 100644 --- a/components/Collapsible.tsx +++ b/components/default/Collapsible.tsx @@ -1,15 +1,13 @@ import { PropsWithChildren, useState } from 'react'; import { StyleSheet, TouchableOpacity } from 'react-native'; - -import { ThemedText } from '@/components/ThemedText'; -import { ThemedView } from '@/components/ThemedView'; +import { ThemedText, ThemedView } from '@/components/theme/Theme'; import { IconSymbol } from '@/components/ui/IconSymbol'; import { Colors } from '@/constants/Colors'; 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 theme = useColorScheme() ?? 'light'; + const scheme = useColorScheme() ?? 'dark'; return ( @@ -22,7 +20,7 @@ export function Collapsible({ children, title }: PropsWithChildren & { title: st name='chevron.right' size={18} 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' }] }} /> @@ -31,7 +29,7 @@ export function Collapsible({ children, title }: PropsWithChildren & { title: st {isOpen && {children}} ); -} +}; const styles = StyleSheet.create({ heading: { diff --git a/components/ExternalLink.tsx b/components/default/ExternalLink.tsx similarity index 78% rename from components/ExternalLink.tsx rename to components/default/ExternalLink.tsx index 18f611f..1c0e962 100644 --- a/components/ExternalLink.tsx +++ b/components/default/ExternalLink.tsx @@ -1,16 +1,16 @@ -import { Link } from 'expo-router'; +import { Link, RelativePathString } from 'expo-router'; import { openBrowserAsync } from 'expo-web-browser'; import { type ComponentProps } from 'react'; import { Platform } from 'react-native'; type Props = Omit, 'href'> & { href: string }; -export function ExternalLink({ href, ...rest }: Props) { +export const ExternalLink = ({ href, ...rest }: Props) => { return ( { if (Platform.OS !== 'web') { // Prevent the default behavior of linking to the default browser on native. @@ -21,4 +21,4 @@ export function ExternalLink({ href, ...rest }: Props) { }} /> ); -} +}; diff --git a/components/HapticTab.tsx b/components/default/HapticTab.tsx similarity index 88% rename from components/HapticTab.tsx rename to components/default/HapticTab.tsx index 7f3981c..8579166 100644 --- a/components/HapticTab.tsx +++ b/components/default/HapticTab.tsx @@ -2,7 +2,7 @@ import { BottomTabBarButtonProps } from '@react-navigation/bottom-tabs'; import { PlatformPressable } from '@react-navigation/elements'; import * as Haptics from 'expo-haptics'; -export function HapticTab(props: BottomTabBarButtonProps) { +export const HapticTab = (props: BottomTabBarButtonProps) => { return ( ); -} +}; diff --git a/components/ParallaxScrollView.tsx b/components/default/ParallaxScrollView.tsx similarity index 75% rename from components/ParallaxScrollView.tsx rename to components/default/ParallaxScrollView.tsx index 8b2aa7e..2af4f0a 100644 --- a/components/ParallaxScrollView.tsx +++ b/components/default/ParallaxScrollView.tsx @@ -6,8 +6,7 @@ import Animated, { useAnimatedStyle, useScrollViewOffset, } from 'react-native-reanimated'; - -import { ThemedView } from '@/components/ThemedView'; +import { ThemedView } from '@/components/theme/Theme'; import { useBottomTabOverflow } from '@/components/ui/TabBarBackground'; import { useColorScheme } from '@/hooks/useColorScheme'; @@ -16,14 +15,16 @@ const HEADER_HEIGHT = 250; type Props = PropsWithChildren<{ headerImage: ReactElement; headerBackgroundColor: { dark: string; light: string }; + headerHeight?: number; }>; -export default function ParallaxScrollView({ +const ParallaxScrollView = ({ children, headerImage, headerBackgroundColor, -}: Props) { - const colorScheme = useColorScheme() ?? 'light'; + headerHeight = HEADER_HEIGHT, +}: Props) => { + const scheme = useColorScheme() ?? 'dark'; const scrollRef = useAnimatedRef(); const scrollOffset = useScrollViewOffset(scrollRef); const bottom = useBottomTabOverflow(); @@ -33,12 +34,12 @@ export default function ParallaxScrollView({ { translateY: interpolate( scrollOffset.value, - [-HEADER_HEIGHT, 0, HEADER_HEIGHT], - [-HEADER_HEIGHT / 2, 0, HEADER_HEIGHT * 0.75], + [-headerHeight, 0, headerHeight], + [-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({ @@ -65,14 +69,14 @@ export default function ParallaxScrollView({ ); -} +}; +export default ParallaxScrollView; const styles = StyleSheet.create({ container: { flex: 1, }, header: { - height: HEADER_HEIGHT, overflow: 'hidden', }, content: { diff --git a/components/ThemedText.tsx b/components/theme/Theme.tsx similarity index 72% rename from components/ThemedText.tsx rename to components/theme/Theme.tsx index c0e1a78..0defec1 100644 --- a/components/ThemedText.tsx +++ b/components/theme/Theme.tsx @@ -1,22 +1,30 @@ +import { View, type ViewProps } from 'react-native'; import { Text, type TextProps, StyleSheet } from 'react-native'; - import { useThemeColor } from '@/hooks/useThemeColor'; +export type ThemedViewProps = ViewProps & { + lightColor?: string; + darkColor?: string; +}; export type ThemedTextProps = TextProps & { lightColor?: string; darkColor?: string; 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 ; +}; + +export const ThemedText = ({ style, lightColor, darkColor, type = 'default', ...rest -}: ThemedTextProps) { +}: ThemedTextProps) => { const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text'); - return ( ); -} +}; const styles = StyleSheet.create({ default: { diff --git a/components/theme/buttons/Button.tsx b/components/theme/buttons/Button.tsx new file mode 100644 index 0000000..46bf696 --- /dev/null +++ b/components/theme/buttons/Button.tsx @@ -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) => { + const scheme = useColorScheme() ?? 'dark'; + return ( + + + {children} + + + ); +}; +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', + }, +}); diff --git a/components/theme/buttons/TextButton.tsx b/components/theme/buttons/TextButton.tsx new file mode 100644 index 0000000..65490f5 --- /dev/null +++ b/components/theme/buttons/TextButton.tsx @@ -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 ( + + ); +}; +export default TextButton; diff --git a/components/ui/IconSymbol.ios.tsx b/components/ui/IconSymbol.ios.tsx index d8501ae..d0bd772 100644 --- a/components/ui/IconSymbol.ios.tsx +++ b/components/ui/IconSymbol.ios.tsx @@ -1,7 +1,7 @@ import { SymbolView, SymbolViewProps, SymbolWeight } from 'expo-symbols'; import { StyleProp, ViewStyle } from 'react-native'; -export function IconSymbol({ +export const IconSymbol = ({ name, size = 24, color, @@ -13,7 +13,7 @@ export function IconSymbol({ color: string; style?: StyleProp; weight?: SymbolWeight; -}) { +}) => { return ( ); -} +}; diff --git a/components/ui/IconSymbol.tsx b/components/ui/IconSymbol.tsx index f1fabd4..3864699 100644 --- a/components/ui/IconSymbol.tsx +++ b/components/ui/IconSymbol.tsx @@ -3,7 +3,7 @@ import MaterialIcons from '@expo/vector-icons/MaterialIcons'; import { SymbolWeight } from 'expo-symbols'; 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. 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. */ -export function IconSymbol({ +export const IconSymbol = ({ name, size = 24, color, @@ -36,8 +36,8 @@ export function IconSymbol({ name: IconSymbolName; size?: number; color: string | OpaqueColorValue; - style?: StyleProp; + style?: StyleProp; weight?: SymbolWeight; -}) { +}) => { return ; -} +}; diff --git a/components/ui/TabBarBackground.ios.tsx b/components/ui/TabBarBackground.ios.tsx index b7fc921..d220d4a 100644 --- a/components/ui/TabBarBackground.ios.tsx +++ b/components/ui/TabBarBackground.ios.tsx @@ -3,7 +3,7 @@ import { BlurView } from 'expo-blur'; import { StyleSheet } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; -export default function BlurTabBarBackground() { +const BlurTabBarBackground = () => { return ( ); -} +}; +export default BlurTabBarBackground; -export function useBottomTabOverflow() { +export const useBottomTabOverflow = () => { const tabHeight = useBottomTabBarHeight(); const { bottom } = useSafeAreaInsets(); return tabHeight - bottom; -} +}; diff --git a/components/ui/TabBarBackground.tsx b/components/ui/TabBarBackground.tsx index 70d1c3c..9f14010 100644 --- a/components/ui/TabBarBackground.tsx +++ b/components/ui/TabBarBackground.tsx @@ -1,6 +1,5 @@ // This is a shim for web and Android where the tab bar is generally opaque. export default undefined; - -export function useBottomTabOverflow() { +export const useBottomTabOverflow = () => { return 0; -} +};