Added theme components. Reorganized them. Prepping for Tech Tracker Rewrite
This commit is contained in:
		@@ -1,6 +1,6 @@
 | 
			
		||||
import { Image, StyleSheet, Platform } from 'react-native';
 | 
			
		||||
import ParallaxScrollView from '@/components/default/ParallaxScrollView';
 | 
			
		||||
import { ThemedText, ThemedView } from '@/components/theme/Theme';
 | 
			
		||||
import { ThemedText, ThemedView } from '@/components/theme';
 | 
			
		||||
 | 
			
		||||
const HomeScreen = () => {
 | 
			
		||||
  return (
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ import { StyleSheet, Image, Platform } from 'react-native';
 | 
			
		||||
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 { ThemedText, ThemedView } from '@/components/theme';
 | 
			
		||||
import { IconSymbol } from '@/components/ui/IconSymbol';
 | 
			
		||||
 | 
			
		||||
const TabTwoScreen = () => {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
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, ThemedTextButton, ThemedView } from '@/components/theme';
 | 
			
		||||
 | 
			
		||||
const NotFoundScreen = () => {
 | 
			
		||||
  return (
 | 
			
		||||
@@ -10,7 +9,7 @@ const NotFoundScreen = () => {
 | 
			
		||||
      <ThemedView style={styles.container}>
 | 
			
		||||
        <ThemedText type='title'>This screen doesn't exist.</ThemedText>
 | 
			
		||||
        <Link href='/'>
 | 
			
		||||
          <TextButton width={200} height={45} text='Go to home screen' fontSize={24} />
 | 
			
		||||
          <ThemedTextButton width={200} height={45} text='Go to home screen' fontSize={24} />
 | 
			
		||||
        </Link>
 | 
			
		||||
      </ThemedView>
 | 
			
		||||
    </>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
import { PropsWithChildren, useState } from 'react';
 | 
			
		||||
import { StyleSheet, TouchableOpacity } from 'react-native';
 | 
			
		||||
import { ThemedText, ThemedView } from '@/components/theme/Theme';
 | 
			
		||||
import { ThemedText, ThemedView } from '@/components/theme';
 | 
			
		||||
import { IconSymbol } from '@/components/ui/IconSymbol';
 | 
			
		||||
import { Colors } from '@/constants/Colors';
 | 
			
		||||
import { useColorScheme } from '@/hooks/useColorScheme';
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ import Animated, {
 | 
			
		||||
  useAnimatedStyle,
 | 
			
		||||
  useScrollViewOffset,
 | 
			
		||||
} from 'react-native-reanimated';
 | 
			
		||||
import { ThemedView } from '@/components/theme/Theme';
 | 
			
		||||
import { ThemedView } from '@/components/theme';
 | 
			
		||||
import { useBottomTabOverflow } from '@/components/ui/TabBarBackground';
 | 
			
		||||
import { useColorScheme } from '@/hooks/useColorScheme';
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,33 +0,0 @@
 | 
			
		||||
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,25 +1,30 @@
 | 
			
		||||
import { StyleSheet, Pressable } from 'react-native';
 | 
			
		||||
import { ThemedView } from '@/components/theme/Theme';
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { StyleSheet, Pressable, PressableProps } from 'react-native';
 | 
			
		||||
import { ThemedView } from '@/components/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 = {
 | 
			
		||||
type ThemedButtonProps = PressableProps & {
 | 
			
		||||
  width?: number;
 | 
			
		||||
  height?: number;
 | 
			
		||||
  onPress?: () => void;
 | 
			
		||||
  containerStyle?: object;
 | 
			
		||||
  buttonStyle?: object;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const Button = ({
 | 
			
		||||
const ThemedButton: React.FC<ThemedButtonProps> = ({
 | 
			
		||||
  width,
 | 
			
		||||
  height,
 | 
			
		||||
  children,
 | 
			
		||||
  onPress,
 | 
			
		||||
}: Props & React.ComponentProps<typeof Pressable>) => {
 | 
			
		||||
  containerStyle,
 | 
			
		||||
  buttonStyle,
 | 
			
		||||
  style,
 | 
			
		||||
  ...restProps // This now includes onPress automatically
 | 
			
		||||
}) => {
 | 
			
		||||
  const scheme = useColorScheme() ?? 'dark';
 | 
			
		||||
  
 | 
			
		||||
  return (
 | 
			
		||||
    <ThemedView
 | 
			
		||||
      style={[
 | 
			
		||||
@@ -28,18 +33,25 @@ const Button = ({
 | 
			
		||||
          width: width ?? DEFAULT_WIDTH,
 | 
			
		||||
          height: height ?? DEFAULT_HEIGHT,
 | 
			
		||||
        },
 | 
			
		||||
        containerStyle,
 | 
			
		||||
      ]}
 | 
			
		||||
    >
 | 
			
		||||
      <Pressable
 | 
			
		||||
        style={[styles.button, { backgroundColor: Colors[scheme].text }]}
 | 
			
		||||
        onPress={onPress}
 | 
			
		||||
        style={[
 | 
			
		||||
          styles.button, 
 | 
			
		||||
          { backgroundColor: Colors[scheme].text },
 | 
			
		||||
          buttonStyle,
 | 
			
		||||
          style,
 | 
			
		||||
        ]}
 | 
			
		||||
        {...restProps} // This passes onPress and all other Pressable props
 | 
			
		||||
      >
 | 
			
		||||
        {children}
 | 
			
		||||
      </Pressable>
 | 
			
		||||
    </ThemedView>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
export default Button;
 | 
			
		||||
 | 
			
		||||
export default ThemedButton;
 | 
			
		||||
 | 
			
		||||
const styles = StyleSheet.create({
 | 
			
		||||
  buttonContainer: {
 | 
			
		||||
							
								
								
									
										55
									
								
								components/theme/buttons/ThemedTextButton.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								components/theme/buttons/ThemedTextButton.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { TextStyle, PressableProps } from 'react-native';
 | 
			
		||||
import { ThemedButton, ThemedText } from '@/components/theme';
 | 
			
		||||
import { Colors } from '@/constants/Colors';
 | 
			
		||||
import { useColorScheme } from '@/hooks/useColorScheme';
 | 
			
		||||
 | 
			
		||||
const DEFAULT_FONT_SIZE = 16;
 | 
			
		||||
 | 
			
		||||
// Extend ThemedButton props (which already extends PressableProps)
 | 
			
		||||
type ThemedTextButtonProps = Omit<PressableProps, 'children'> & {
 | 
			
		||||
  width?: number;
 | 
			
		||||
  height?: number;
 | 
			
		||||
  text: string;
 | 
			
		||||
  fontSize?: number;
 | 
			
		||||
  textStyle?: TextStyle;
 | 
			
		||||
  containerStyle?: object;
 | 
			
		||||
  buttonStyle?: object;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ThemedTextButton: React.FC<ThemedTextButtonProps> = ({
 | 
			
		||||
  width,
 | 
			
		||||
  height,
 | 
			
		||||
  text,
 | 
			
		||||
  fontSize,
 | 
			
		||||
  textStyle,
 | 
			
		||||
  containerStyle,
 | 
			
		||||
  buttonStyle,
 | 
			
		||||
  ...restProps // This includes onPress and all other Pressable props
 | 
			
		||||
}) => {
 | 
			
		||||
  const scheme = useColorScheme() ?? 'dark';
 | 
			
		||||
  
 | 
			
		||||
  return (
 | 
			
		||||
    <ThemedButton 
 | 
			
		||||
      width={width} 
 | 
			
		||||
      height={height}
 | 
			
		||||
      containerStyle={containerStyle}
 | 
			
		||||
      buttonStyle={buttonStyle}
 | 
			
		||||
      {...restProps}
 | 
			
		||||
    >
 | 
			
		||||
      <ThemedText
 | 
			
		||||
        style={[
 | 
			
		||||
          {
 | 
			
		||||
            color: Colors[scheme].background,
 | 
			
		||||
            fontSize: fontSize ?? DEFAULT_FONT_SIZE,
 | 
			
		||||
          },
 | 
			
		||||
          textStyle,
 | 
			
		||||
        ]}
 | 
			
		||||
      >
 | 
			
		||||
        {text}
 | 
			
		||||
      </ThemedText>
 | 
			
		||||
    </ThemedButton>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default ThemedTextButton;
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { View, type ViewProps } from 'react-native';
 | 
			
		||||
import { type ViewProps } from 'react-native';
 | 
			
		||||
import { Text, type TextProps, StyleSheet } from 'react-native';
 | 
			
		||||
import { useThemeColor } from '@/hooks/useThemeColor';
 | 
			
		||||
 | 
			
		||||
@@ -12,12 +12,7 @@ export type ThemedTextProps = TextProps & {
 | 
			
		||||
  type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link';
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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 = ({
 | 
			
		||||
const ThemedText = ({
 | 
			
		||||
  style,
 | 
			
		||||
  lightColor,
 | 
			
		||||
  darkColor,
 | 
			
		||||
@@ -40,6 +35,7 @@ export const ThemedText = ({
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
export default ThemedText;
 | 
			
		||||
 | 
			
		||||
const styles = StyleSheet.create({
 | 
			
		||||
  default: {
 | 
			
		||||
							
								
								
									
										13
									
								
								components/theme/default/ThemedView.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								components/theme/default/ThemedView.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
import { View, type ViewProps } from 'react-native';
 | 
			
		||||
import { useThemeColor } from '@/hooks/useThemeColor';
 | 
			
		||||
 | 
			
		||||
export type ThemedViewProps = ViewProps & {
 | 
			
		||||
  lightColor?: string;
 | 
			
		||||
  darkColor?: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ThemedView = ({ style, lightColor, darkColor, ...otherProps }: ThemedViewProps) => {
 | 
			
		||||
  const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');
 | 
			
		||||
  return <View style={[{ backgroundColor }, style]} {...otherProps} />;
 | 
			
		||||
};
 | 
			
		||||
export default ThemedView;
 | 
			
		||||
							
								
								
									
										25
									
								
								components/theme/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								components/theme/index.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
import ThemedText from '@/components/theme/default/ThemedText';
 | 
			
		||||
import ThemedView from '@/components/theme/default/ThemedView';
 | 
			
		||||
import ThemedButton from '@/components/theme/buttons/ThemedButton';
 | 
			
		||||
import ThemedTextButton from '@/components/theme/buttons/ThemedTextButton';
 | 
			
		||||
import ThemedTextInput from '@/components/theme/inputs/ThemedTextInput';
 | 
			
		||||
import ThemedCard from '@/components/theme/ui/ThemedCard';
 | 
			
		||||
import ThemedBadge from '@/components/theme/ui/ThemedBadge';
 | 
			
		||||
import ThemedIcon from '@/components/theme/ui/ThemedIcon';
 | 
			
		||||
import ThemedSwitch from '@/components/theme/inputs/ThemedSwitch';
 | 
			
		||||
import ThemedAvatar from '@/components/theme/ui/ThemedAvatar';
 | 
			
		||||
import ThemedSearchBar from '@/components/theme/inputs/ThemedSearchBar';
 | 
			
		||||
 | 
			
		||||
export {
 | 
			
		||||
  ThemedText,
 | 
			
		||||
  ThemedView,
 | 
			
		||||
  ThemedButton,
 | 
			
		||||
  ThemedTextButton,
 | 
			
		||||
  ThemedTextInput,
 | 
			
		||||
  ThemedCard,
 | 
			
		||||
  ThemedBadge,
 | 
			
		||||
  ThemedIcon,
 | 
			
		||||
  ThemedSwitch,
 | 
			
		||||
  ThemedAvatar,
 | 
			
		||||
  ThemedSearchBar,
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										62
									
								
								components/theme/inputs/ThemedSearchBar.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								components/theme/inputs/ThemedSearchBar.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { StyleSheet, ViewProps, View, DimensionValue } from 'react-native';
 | 
			
		||||
import { Ionicons } from '@expo/vector-icons';
 | 
			
		||||
import { ThemedTextInput } from '@/components/theme';
 | 
			
		||||
import { Colors } from '@/constants/Colors';
 | 
			
		||||
import { useColorScheme } from '@/hooks/useColorScheme';
 | 
			
		||||
 | 
			
		||||
type ThemedSearchBarProps = ViewProps & {
 | 
			
		||||
  placeholder?: string;
 | 
			
		||||
  value: string;
 | 
			
		||||
  onChangeText: (text: string) => void;
 | 
			
		||||
  width?: DimensionValue;
 | 
			
		||||
  height?: number;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ThemedSearchBar: React.FC<ThemedSearchBarProps> = ({
 | 
			
		||||
  placeholder = 'Search',
 | 
			
		||||
  value,
 | 
			
		||||
  onChangeText,
 | 
			
		||||
  width = '100%',
 | 
			
		||||
  height = 40,
 | 
			
		||||
  style,
 | 
			
		||||
  ...restProps
 | 
			
		||||
}) => {
 | 
			
		||||
  const scheme = useColorScheme() ?? 'dark';
 | 
			
		||||
  
 | 
			
		||||
  return (
 | 
			
		||||
    <View style={[styles.container, { width }, style]} {...restProps}>
 | 
			
		||||
      <Ionicons 
 | 
			
		||||
        name="search" 
 | 
			
		||||
        size={20} 
 | 
			
		||||
        color={Colors[scheme].text} 
 | 
			
		||||
        style={styles.icon} 
 | 
			
		||||
      />
 | 
			
		||||
      <ThemedTextInput
 | 
			
		||||
        placeholder={placeholder}
 | 
			
		||||
        value={value}
 | 
			
		||||
        onChangeText={onChangeText}
 | 
			
		||||
        height={height}
 | 
			
		||||
        width={width}
 | 
			
		||||
        style={styles.input}
 | 
			
		||||
      />
 | 
			
		||||
    </View>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const styles = StyleSheet.create({
 | 
			
		||||
  container: {
 | 
			
		||||
    flexDirection: 'row',
 | 
			
		||||
    alignItems: 'center',
 | 
			
		||||
  },
 | 
			
		||||
  icon: {
 | 
			
		||||
    position: 'absolute',
 | 
			
		||||
    zIndex: 1,
 | 
			
		||||
    left: 15,
 | 
			
		||||
  },
 | 
			
		||||
  input: {
 | 
			
		||||
    paddingLeft: 45,
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export default ThemedSearchBar;
 | 
			
		||||
							
								
								
									
										26
									
								
								components/theme/inputs/ThemedSwitch.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								components/theme/inputs/ThemedSwitch.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { Switch, SwitchProps } from 'react-native';
 | 
			
		||||
import { Colors } from '@/constants/Colors';
 | 
			
		||||
import { useColorScheme } from '@/hooks/useColorScheme';
 | 
			
		||||
 | 
			
		||||
type ThemedSwitchProps = SwitchProps;
 | 
			
		||||
 | 
			
		||||
const ThemedSwitch: React.FC<ThemedSwitchProps> = ({
 | 
			
		||||
  ...props
 | 
			
		||||
}) => {
 | 
			
		||||
  const scheme = useColorScheme() ?? 'dark';
 | 
			
		||||
  
 | 
			
		||||
  return (
 | 
			
		||||
    <Switch
 | 
			
		||||
      trackColor={{ 
 | 
			
		||||
        false: Colors[scheme].border, 
 | 
			
		||||
        true: Colors[scheme].tint + '80' 
 | 
			
		||||
      }}
 | 
			
		||||
      thumbColor={props.value ? Colors[scheme].tint : Colors[scheme].card}
 | 
			
		||||
      ios_backgroundColor={Colors[scheme].border}
 | 
			
		||||
      {...props}
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default ThemedSwitch;
 | 
			
		||||
							
								
								
									
										63
									
								
								components/theme/inputs/ThemedTextInput.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								components/theme/inputs/ThemedTextInput.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { StyleSheet, TextInput, TextInputProps, DimensionValue } from 'react-native';
 | 
			
		||||
import { ThemedView } from '@/components/theme';
 | 
			
		||||
import { Colors } from '@/constants/Colors';
 | 
			
		||||
import { useColorScheme } from '@/hooks/useColorScheme';
 | 
			
		||||
 | 
			
		||||
const DEFAULT_WIDTH = 320;
 | 
			
		||||
const DEFAULT_HEIGHT = 50;
 | 
			
		||||
 | 
			
		||||
type ThemedTextInputProps = TextInputProps & {
 | 
			
		||||
  width?: DimensionValue;
 | 
			
		||||
  height?: DimensionValue
 | 
			
		||||
  containerStyle?: object;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ThemedTextInput: React.FC<ThemedTextInputProps> = ({
 | 
			
		||||
  width = DEFAULT_WIDTH,
 | 
			
		||||
  height = DEFAULT_HEIGHT,
 | 
			
		||||
  containerStyle,
 | 
			
		||||
  style,
 | 
			
		||||
  ...restProps
 | 
			
		||||
}) => {
 | 
			
		||||
  const scheme = useColorScheme() ?? 'dark';
 | 
			
		||||
  
 | 
			
		||||
  return (
 | 
			
		||||
    <ThemedView
 | 
			
		||||
      style={[
 | 
			
		||||
        styles.inputContainer,
 | 
			
		||||
        { width, height },
 | 
			
		||||
        containerStyle,
 | 
			
		||||
      ]}
 | 
			
		||||
    >
 | 
			
		||||
      <TextInput
 | 
			
		||||
        style={[
 | 
			
		||||
          styles.input,
 | 
			
		||||
          { 
 | 
			
		||||
            color: Colors[scheme].text,
 | 
			
		||||
            backgroundColor: Colors[scheme].background,
 | 
			
		||||
          },
 | 
			
		||||
          style,
 | 
			
		||||
        ]}
 | 
			
		||||
        placeholderTextColor={Colors[scheme].text + '80'} // 50% opacity
 | 
			
		||||
        {...restProps}
 | 
			
		||||
      />
 | 
			
		||||
    </ThemedView>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default ThemedTextInput;
 | 
			
		||||
 | 
			
		||||
const styles = StyleSheet.create({
 | 
			
		||||
  inputContainer: {
 | 
			
		||||
    padding: 3,
 | 
			
		||||
    borderRadius: 10,
 | 
			
		||||
  },
 | 
			
		||||
  input: {
 | 
			
		||||
    width: '100%',
 | 
			
		||||
    height: '100%',
 | 
			
		||||
    borderRadius: 8,
 | 
			
		||||
    paddingHorizontal: 15,
 | 
			
		||||
    fontSize: 16,
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										84
									
								
								components/theme/ui/ThemedAvatar.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								components/theme/ui/ThemedAvatar.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,84 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { StyleSheet, Image, ImageProps, View } from 'react-native';
 | 
			
		||||
import { ThemedText } from '@/components/theme';
 | 
			
		||||
import { Colors } from '@/constants/Colors';
 | 
			
		||||
import { useColorScheme } from '@/hooks/useColorScheme';
 | 
			
		||||
 | 
			
		||||
type ThemedAvatarProps = Omit<ImageProps, 'source'> & {
 | 
			
		||||
  size?: number;
 | 
			
		||||
  source?: ImageProps['source'];
 | 
			
		||||
  name?: string;
 | 
			
		||||
  borderWidth?: number;
 | 
			
		||||
  borderColor?: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ThemedAvatar: React.FC<ThemedAvatarProps> = ({
 | 
			
		||||
  size = 50,
 | 
			
		||||
  source,
 | 
			
		||||
  name,
 | 
			
		||||
  borderWidth = 0,
 | 
			
		||||
  borderColor,
 | 
			
		||||
  style,
 | 
			
		||||
  ...restProps
 | 
			
		||||
}) => {
 | 
			
		||||
  const scheme = useColorScheme() ?? 'dark';
 | 
			
		||||
  const border = borderColor || Colors[scheme].tint;
 | 
			
		||||
  
 | 
			
		||||
  // Get initials from name
 | 
			
		||||
  const getInitials = (name?: string) => {
 | 
			
		||||
    if (!name) return '';
 | 
			
		||||
    return name
 | 
			
		||||
      .split(' ')
 | 
			
		||||
      .map(part => part[0])
 | 
			
		||||
      .join('')
 | 
			
		||||
      .toUpperCase()
 | 
			
		||||
      .substring(0, 2);
 | 
			
		||||
  };
 | 
			
		||||
  
 | 
			
		||||
  const initials = getInitials(name);
 | 
			
		||||
  
 | 
			
		||||
  return (
 | 
			
		||||
    <View
 | 
			
		||||
      style={[
 | 
			
		||||
        styles.container,
 | 
			
		||||
        {
 | 
			
		||||
          width: size,
 | 
			
		||||
          height: size,
 | 
			
		||||
          borderRadius: size / 2,
 | 
			
		||||
          borderWidth,
 | 
			
		||||
          borderColor: border,
 | 
			
		||||
          backgroundColor: source ? 'transparent' : Colors[scheme].tint,
 | 
			
		||||
        },
 | 
			
		||||
      ]}
 | 
			
		||||
    >
 | 
			
		||||
      {source ? (
 | 
			
		||||
        <Image
 | 
			
		||||
          source={source}
 | 
			
		||||
          style={[
 | 
			
		||||
            {
 | 
			
		||||
              width: size - 2 * borderWidth,
 | 
			
		||||
              height: size - 2 * borderWidth,
 | 
			
		||||
              borderRadius: (size - 2 * borderWidth) / 2,
 | 
			
		||||
            },
 | 
			
		||||
            style,
 | 
			
		||||
          ]}
 | 
			
		||||
          {...restProps}
 | 
			
		||||
        />
 | 
			
		||||
      ) : (
 | 
			
		||||
        <ThemedText style={{ color: Colors[scheme].background, fontSize: size * 0.4 }}>
 | 
			
		||||
          {initials}
 | 
			
		||||
        </ThemedText>
 | 
			
		||||
      )}
 | 
			
		||||
    </View>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const styles = StyleSheet.create({
 | 
			
		||||
  container: {
 | 
			
		||||
    alignItems: 'center',
 | 
			
		||||
    justifyContent: 'center',
 | 
			
		||||
    overflow: 'hidden',
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export default ThemedAvatar;
 | 
			
		||||
							
								
								
									
										62
									
								
								components/theme/ui/ThemedBadge.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								components/theme/ui/ThemedBadge.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { StyleSheet, ViewProps } from 'react-native';
 | 
			
		||||
import { ThemedView, ThemedText } from '@/components/theme';
 | 
			
		||||
import { Colors } from '@/constants/Colors';
 | 
			
		||||
import { useColorScheme } from '@/hooks/useColorScheme';
 | 
			
		||||
 | 
			
		||||
type ThemedBadgeProps = ViewProps & {
 | 
			
		||||
  value: number | string;
 | 
			
		||||
  size?: number;
 | 
			
		||||
  color?: string;
 | 
			
		||||
  textColor?: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ThemedBadge: React.FC<ThemedBadgeProps> = ({
 | 
			
		||||
  value,
 | 
			
		||||
  size = 24,
 | 
			
		||||
  color,
 | 
			
		||||
  textColor,
 | 
			
		||||
  style,
 | 
			
		||||
  ...restProps
 | 
			
		||||
}) => {
 | 
			
		||||
  const scheme = useColorScheme() ?? 'dark';
 | 
			
		||||
  const badgeColor = color || Colors[scheme].tint;
 | 
			
		||||
  const badgeTextColor = textColor || Colors[scheme].background;
 | 
			
		||||
  
 | 
			
		||||
  return (
 | 
			
		||||
    <ThemedView
 | 
			
		||||
      style={[
 | 
			
		||||
        styles.badge,
 | 
			
		||||
        {
 | 
			
		||||
          width: size,
 | 
			
		||||
          height: size,
 | 
			
		||||
          borderRadius: size / 2,
 | 
			
		||||
          backgroundColor: badgeColor,
 | 
			
		||||
        },
 | 
			
		||||
        style,
 | 
			
		||||
      ]}
 | 
			
		||||
      {...restProps}
 | 
			
		||||
    >
 | 
			
		||||
      <ThemedText 
 | 
			
		||||
        style={[
 | 
			
		||||
          styles.text, 
 | 
			
		||||
          { color: badgeTextColor, fontSize: size * 0.5 }
 | 
			
		||||
        ]}
 | 
			
		||||
      >
 | 
			
		||||
        {value}
 | 
			
		||||
      </ThemedText>
 | 
			
		||||
    </ThemedView>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const styles = StyleSheet.create({
 | 
			
		||||
  badge: {
 | 
			
		||||
    alignItems: 'center',
 | 
			
		||||
    justifyContent: 'center',
 | 
			
		||||
  },
 | 
			
		||||
  text: {
 | 
			
		||||
    fontWeight: 'bold',
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export default ThemedBadge;
 | 
			
		||||
							
								
								
									
										64
									
								
								components/theme/ui/ThemedCard.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								components/theme/ui/ThemedCard.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { StyleSheet, ViewProps, Platform, DimensionValue } from 'react-native';
 | 
			
		||||
import { ThemedView } from '@/components/theme';
 | 
			
		||||
import { Colors } from '@/constants/Colors';
 | 
			
		||||
import { useColorScheme } from '@/hooks/useColorScheme';
 | 
			
		||||
 | 
			
		||||
type ThemedCardProps = ViewProps & {
 | 
			
		||||
  width?: DimensionValue;
 | 
			
		||||
  height?: DimensionValue;
 | 
			
		||||
  padding?: number;
 | 
			
		||||
  elevation?: number;
 | 
			
		||||
  borderRadius?: number;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ThemedCard: React.FC<ThemedCardProps> = ({
 | 
			
		||||
  width = '100%',
 | 
			
		||||
  height,
 | 
			
		||||
  padding = 16,
 | 
			
		||||
  elevation = 3,
 | 
			
		||||
  borderRadius = 12,
 | 
			
		||||
  style,
 | 
			
		||||
  children,
 | 
			
		||||
  ...restProps
 | 
			
		||||
}) => {
 | 
			
		||||
  const scheme = useColorScheme() ?? 'dark';
 | 
			
		||||
  
 | 
			
		||||
  return (
 | 
			
		||||
    <ThemedView
 | 
			
		||||
      style={[
 | 
			
		||||
        styles.card,
 | 
			
		||||
        {
 | 
			
		||||
          width,
 | 
			
		||||
          height,
 | 
			
		||||
          padding,
 | 
			
		||||
          borderRadius,
 | 
			
		||||
          backgroundColor: Colors[scheme].card,
 | 
			
		||||
          ...Platform.select({
 | 
			
		||||
            ios: {
 | 
			
		||||
              shadowColor: Colors[scheme].text,
 | 
			
		||||
              shadowOffset: { width: 0, height: elevation/2 },
 | 
			
		||||
              shadowOpacity: 0.1,
 | 
			
		||||
              shadowRadius: elevation,
 | 
			
		||||
            },
 | 
			
		||||
            android: {
 | 
			
		||||
              elevation,
 | 
			
		||||
            },
 | 
			
		||||
          }),
 | 
			
		||||
        },
 | 
			
		||||
        style,
 | 
			
		||||
      ]}
 | 
			
		||||
      {...restProps}
 | 
			
		||||
    >
 | 
			
		||||
      {children}
 | 
			
		||||
    </ThemedView>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const styles = StyleSheet.create({
 | 
			
		||||
  card: {
 | 
			
		||||
    overflow: 'hidden',
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export default ThemedCard;
 | 
			
		||||
							
								
								
									
										37
									
								
								components/theme/ui/ThemedDivider.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								components/theme/ui/ThemedDivider.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { StyleSheet, ViewProps, View, DimensionValue } from 'react-native';
 | 
			
		||||
import { Colors } from '@/constants/Colors';
 | 
			
		||||
import { useColorScheme } from '@/hooks/useColorScheme';
 | 
			
		||||
 | 
			
		||||
type ThemedDividerProps = ViewProps & {
 | 
			
		||||
  orientation?: 'horizontal' | 'vertical';
 | 
			
		||||
  thickness?: DimensionValue
 | 
			
		||||
  length?: DimensionValue
 | 
			
		||||
  color?: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ThemedDivider: React.FC<ThemedDividerProps> = ({
 | 
			
		||||
  orientation = 'horizontal',
 | 
			
		||||
  thickness = 1,
 | 
			
		||||
  length = '100%',
 | 
			
		||||
  style,
 | 
			
		||||
  ...restProps
 | 
			
		||||
}) => {
 | 
			
		||||
  const scheme = useColorScheme() ?? 'dark';
 | 
			
		||||
  const color = restProps.color || Colors[scheme].border;
 | 
			
		||||
  
 | 
			
		||||
  return (
 | 
			
		||||
    <View
 | 
			
		||||
      style={[
 | 
			
		||||
        orientation === 'horizontal' 
 | 
			
		||||
          ? { height: thickness, width: length } 
 | 
			
		||||
          : { width: thickness, height: length },
 | 
			
		||||
        { backgroundColor: color },
 | 
			
		||||
        style,
 | 
			
		||||
      ]}
 | 
			
		||||
      {...restProps}
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default ThemedDivider;
 | 
			
		||||
							
								
								
									
										34
									
								
								components/theme/ui/ThemedIcon.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								components/theme/ui/ThemedIcon.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { StyleSheet, ViewProps } from 'react-native';
 | 
			
		||||
import { Ionicons } from '@expo/vector-icons'; // Or your preferred icon library
 | 
			
		||||
import { Colors } from '@/constants/Colors';
 | 
			
		||||
import { useColorScheme } from '@/hooks/useColorScheme';
 | 
			
		||||
 | 
			
		||||
type ThemedIconProps = ViewProps & {
 | 
			
		||||
  name: string;
 | 
			
		||||
  size?: number;
 | 
			
		||||
  color?: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ThemedIcon: React.FC<ThemedIconProps> = ({
 | 
			
		||||
  name,
 | 
			
		||||
  size = 24,
 | 
			
		||||
  color,
 | 
			
		||||
  style,
 | 
			
		||||
  ...restProps
 | 
			
		||||
}) => {
 | 
			
		||||
  const scheme = useColorScheme() ?? 'dark';
 | 
			
		||||
  const iconColor = color || Colors[scheme].text;
 | 
			
		||||
  
 | 
			
		||||
  return (
 | 
			
		||||
    <Ionicons
 | 
			
		||||
      name={name}
 | 
			
		||||
      size={size}
 | 
			
		||||
      color={iconColor}
 | 
			
		||||
      style={style}
 | 
			
		||||
      {...restProps}
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default ThemedIcon;
 | 
			
		||||
@@ -1,26 +1,50 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 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';
 | 
			
		||||
// Dark mode colors
 | 
			
		||||
const dark = '#2e2f3d';
 | 
			
		||||
const tintColorDark = '#fff';
 | 
			
		||||
const iconColorDark = '#9BA1A6';
 | 
			
		||||
 | 
			
		||||
// Light mode colors
 | 
			
		||||
const light = '#ECEDEE';
 | 
			
		||||
const tintColorLight = '#0a7ea4';
 | 
			
		||||
const iconColorLight = '#687076';
 | 
			
		||||
 | 
			
		||||
export const Colors = {
 | 
			
		||||
  light: {
 | 
			
		||||
    text: '#11181C',
 | 
			
		||||
    background: '#fff',
 | 
			
		||||
    text: dark,
 | 
			
		||||
    background: light,
 | 
			
		||||
    tint: tintColorLight,
 | 
			
		||||
    icon: '#687076',
 | 
			
		||||
    tabIconDefault: '#687076',
 | 
			
		||||
    icon: iconColorLight,
 | 
			
		||||
    tabIconDefault: iconColorLight,
 | 
			
		||||
    tabIconSelected: tintColorLight,
 | 
			
		||||
    
 | 
			
		||||
    // New colors
 | 
			
		||||
    card: '#ffffff',
 | 
			
		||||
    border: '#d0d7de',
 | 
			
		||||
    notification: '#f85149',
 | 
			
		||||
    placeholder: '#8b949e',
 | 
			
		||||
    inactive: '#afb8c1',
 | 
			
		||||
    subtle: '#f6f8fa',
 | 
			
		||||
    error: '#e5484d',
 | 
			
		||||
    success: '#46954a',
 | 
			
		||||
    warning: '#daaa3f'
 | 
			
		||||
  },
 | 
			
		||||
  dark: {
 | 
			
		||||
    text: '#ECEDEE',
 | 
			
		||||
    background: '#151718',
 | 
			
		||||
    text: light,
 | 
			
		||||
    background: dark,
 | 
			
		||||
    tint: tintColorDark,
 | 
			
		||||
    icon: '#9BA1A6',
 | 
			
		||||
    tabIconDefault: '#9BA1A6',
 | 
			
		||||
    icon: iconColorDark,
 | 
			
		||||
    tabIconDefault: iconColorDark,
 | 
			
		||||
    tabIconSelected: tintColorDark,
 | 
			
		||||
    
 | 
			
		||||
    // New colors
 | 
			
		||||
    card: '#3a3b4a',
 | 
			
		||||
    border: '#444c56',
 | 
			
		||||
    notification: '#ff6a69',
 | 
			
		||||
    placeholder: '#636e7b',
 | 
			
		||||
    inactive: '#4d5560',
 | 
			
		||||
    subtle: '#272934',
 | 
			
		||||
    error: '#ff6369',
 | 
			
		||||
    success: '#3fb950',
 | 
			
		||||
    warning: '#d29922'
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user