@modhamanish/rn-mm-template 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/MMTemplate/App.tsx +15 -2
  2. package/MMTemplate/package.json +3 -0
  3. package/MMTemplate/src/components/AppText.tsx +125 -0
  4. package/MMTemplate/src/components/CustomToast.tsx +8 -6
  5. package/MMTemplate/src/components/FeatureItem.tsx +11 -8
  6. package/MMTemplate/src/components/InfoCard.tsx +10 -6
  7. package/MMTemplate/src/components/LanguageSwitcher.tsx +8 -7
  8. package/MMTemplate/src/components/TextInput.tsx +214 -23
  9. package/MMTemplate/src/components/ThemeSwitcher.tsx +12 -7
  10. package/MMTemplate/src/context/ThemeContext.tsx +15 -1
  11. package/MMTemplate/src/locales/en.json +19 -2
  12. package/MMTemplate/src/locales/hi.json +19 -2
  13. package/MMTemplate/src/navigation/AppStack.tsx +2 -0
  14. package/MMTemplate/src/navigation/MainTab.tsx +15 -4
  15. package/MMTemplate/src/navigation/routes.ts +2 -0
  16. package/MMTemplate/src/screens/AddNoteScreen.tsx +155 -0
  17. package/MMTemplate/src/screens/HomeScreen.tsx +60 -33
  18. package/MMTemplate/src/screens/LoginScreen.tsx +67 -25
  19. package/MMTemplate/src/screens/NoteScreen.tsx +241 -0
  20. package/MMTemplate/src/screens/ProfileScreen.tsx +26 -25
  21. package/MMTemplate/src/screens/SettingsScreen.tsx +17 -16
  22. package/MMTemplate/src/screens/WelcomeScreen.tsx +23 -23
  23. package/MMTemplate/src/services/axiosInstance.ts +41 -0
  24. package/MMTemplate/src/services/note.query.ts +40 -0
  25. package/MMTemplate/src/services/queryKeys.ts +4 -0
  26. package/MMTemplate/src/types/components.types.ts +47 -1
  27. package/MMTemplate/src/types/navigation.types.ts +2 -0
  28. package/MMTemplate/src/types/services.types.ts +6 -0
  29. package/MMTemplate/src/utils/storageHelper.ts +2 -0
  30. package/MMTemplate/src/utils/validationSchemas.ts +6 -0
  31. package/README.md +1 -1
  32. package/package.json +1 -1
@@ -1,14 +1,24 @@
1
1
  import React from 'react';
2
2
  import { SafeAreaProvider } from 'react-native-safe-area-context';
3
+ import { KeyboardProvider } from 'react-native-keyboard-controller';
4
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
5
+
3
6
  import { ThemeProvider } from './src/context/ThemeContext';
4
7
  import AppNavigator from './src/navigation/AppNavigator';
5
- import { KeyboardProvider } from 'react-native-keyboard-controller';
6
8
 
7
9
  import Toast, { ToastConfigParams } from 'react-native-toast-message';
8
10
  import { CustomToast } from './src/components/CustomToast';
9
11
  import { AuthProvider } from './src/context/AuthContext';
10
12
  import './src/utils/i18n';
11
13
 
14
+ const queryClient = new QueryClient({
15
+ defaultOptions: {
16
+ queries: {
17
+ refetchOnWindowFocus: false,
18
+ },
19
+ },
20
+ });
21
+
12
22
  const toastConfig = {
13
23
  error: ({
14
24
  text1,
@@ -23,13 +33,16 @@ const toastConfig = {
23
33
  onPress?: () => void;
24
34
  }>) => <CustomToast text1={text1} onPress={props?.onPress} type="success" />,
25
35
  };
36
+
26
37
  const App = () => {
27
38
  return (
28
39
  <KeyboardProvider>
29
40
  <SafeAreaProvider>
30
41
  <ThemeProvider>
31
42
  <AuthProvider>
32
- <AppNavigator />
43
+ <QueryClientProvider client={queryClient}>
44
+ <AppNavigator />
45
+ </QueryClientProvider>
33
46
  </AuthProvider>
34
47
  </ThemeProvider>
35
48
  <Toast config={toastConfig} />
@@ -14,6 +14,8 @@
14
14
  "@react-navigation/bottom-tabs": "^7.9.0",
15
15
  "@react-navigation/native": "^7.1.26",
16
16
  "@react-navigation/native-stack": "^7.9.0",
17
+ "@tanstack/react-query": "^5.90.20",
18
+ "axios": "^1.13.4",
17
19
  "formik": "^2.4.9",
18
20
  "i18next": "^25.7.3",
19
21
  "react": "19.2.0",
@@ -40,6 +42,7 @@
40
42
  "@react-native/eslint-config": "0.83.1",
41
43
  "@react-native/metro-config": "0.83.1",
42
44
  "@react-native/typescript-config": "0.83.1",
45
+ "@tanstack/eslint-plugin-query": "^5.91.4",
43
46
  "@types/jest": "^29.5.13",
44
47
  "@types/react": "^19.2.0",
45
48
  "@types/react-test-renderer": "^19.1.0",
@@ -0,0 +1,125 @@
1
+ import React, { FC, memo } from 'react';
2
+ import { Text, TextStyle } from 'react-native';
3
+ import { useTheme } from '../context/ThemeContext';
4
+ import { AppTextProps, AppTextSize } from '../types/components.types';
5
+
6
+ const FONTS: Record<string, string> | undefined = {
7
+ // Define custom font families here. If empty or undefined, the component will use default system fonts with font weights.
8
+ // extraBold: 'Poppins-ExtraBold',
9
+ // bold: 'Poppins-Bold',
10
+ // semiBold: 'Poppins-SemiBold',
11
+ // medium: 'Poppins-Medium',
12
+ // light: 'Poppins-Light',
13
+ // regular: 'Poppins-Regular',
14
+ };
15
+
16
+ const SIZES: Record<string, number> = {
17
+ xxsmall: 8,
18
+ xsmall: 10,
19
+ small: 12,
20
+ normal: 14,
21
+ body: 16,
22
+ large: 20,
23
+ xlarge: 24,
24
+ xxlarge: 32,
25
+ };
26
+
27
+ const AppText: FC<AppTextProps> = ({
28
+ children,
29
+ size = 'normal',
30
+ variant = 'regular',
31
+ color,
32
+ fontFamily,
33
+ transform,
34
+ style,
35
+ ...props
36
+ }) => {
37
+ const { colors } = useTheme();
38
+
39
+ const getFontSize = (s: AppTextSize): number => {
40
+ if (typeof s === 'number') return s;
41
+ return SIZES[s as string] || SIZES.normal;
42
+ };
43
+
44
+ const getTransform = (t?: string): any => {
45
+ if (!t) return undefined;
46
+ switch (t) {
47
+ case 'capital':
48
+ case 'uppercase':
49
+ return 'uppercase';
50
+ case 'small':
51
+ case 'lowercase':
52
+ return 'lowercase';
53
+ case 'first char capital':
54
+ case 'capitalize':
55
+ return 'capitalize';
56
+ default:
57
+ return t;
58
+ }
59
+ };
60
+
61
+ const getVariantStyle = (v: string): TextStyle => {
62
+ const hasFonts = !!FONTS && Object.keys(FONTS).length > 0;
63
+ switch (v) {
64
+ case 'h1':
65
+ return hasFonts
66
+ ? { fontSize: 32, fontFamily: FONTS.extraBold }
67
+ : { fontSize: 32, fontWeight: '800' };
68
+ case 'h2':
69
+ return hasFonts
70
+ ? { fontSize: 24, fontFamily: FONTS.bold }
71
+ : { fontSize: 24, fontWeight: '700' };
72
+ case 'h3':
73
+ return hasFonts
74
+ ? { fontSize: 20, fontFamily: FONTS.bold }
75
+ : { fontSize: 20, fontWeight: '700' };
76
+ case 'body1':
77
+ return hasFonts
78
+ ? { fontSize: 16, fontFamily: FONTS.regular }
79
+ : { fontSize: 16, fontWeight: '400' };
80
+ case 'body2':
81
+ return hasFonts
82
+ ? { fontSize: 14, fontFamily: FONTS.regular }
83
+ : { fontSize: 14, fontWeight: '400' };
84
+ case 'bold':
85
+ return hasFonts ? { fontFamily: FONTS.bold } : { fontWeight: '700' };
86
+ case 'semiBold':
87
+ return hasFonts
88
+ ? { fontFamily: FONTS.semiBold }
89
+ : { fontWeight: '600' };
90
+ case 'medium':
91
+ return hasFonts ? { fontFamily: FONTS.medium } : { fontWeight: '500' };
92
+ case 'light':
93
+ return hasFonts ? { fontFamily: FONTS.light } : { fontWeight: '300' };
94
+ case 'regular':
95
+ return hasFonts ? { fontFamily: FONTS.regular } : { fontWeight: '400' };
96
+ default:
97
+ return {};
98
+ }
99
+ };
100
+
101
+ const variantStyle = getVariantStyle(variant);
102
+
103
+ // If prop fontFamily is provided, it should override variant's fontFamily
104
+ // And we should still prevent variant's fontWeight (if any) from conflicting with it.
105
+ if (fontFamily && variantStyle.fontWeight) {
106
+ delete variantStyle.fontWeight;
107
+ }
108
+
109
+ const combinedStyles: TextStyle = {
110
+ fontSize: getFontSize(size),
111
+ color: color || colors.textColor,
112
+ textTransform: getTransform(transform),
113
+ ...variantStyle,
114
+ ...(fontFamily ? { fontFamily } : {}),
115
+ ...(style as object),
116
+ };
117
+
118
+ return (
119
+ <Text style={combinedStyles} allowFontScaling={false} {...props}>
120
+ {children}
121
+ </Text>
122
+ );
123
+ };
124
+
125
+ export default memo(AppText);
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
- import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
2
+ import { StyleSheet, TouchableOpacity, View } from 'react-native';
3
+ import AppText from './AppText';
3
4
 
4
5
  type CustomToastProps = {
5
6
  text1?: string;
@@ -12,10 +13,14 @@ export const CustomToast = ({ text1, onPress, type }: CustomToastProps) => {
12
13
 
13
14
  return (
14
15
  <View style={[styles.container, { backgroundColor }]}>
15
- <Text style={styles.text}>{text1}</Text>
16
+ <AppText variant="medium" style={styles.text}>
17
+ {text1}
18
+ </AppText>
16
19
  {onPress && (
17
20
  <TouchableOpacity onPress={onPress}>
18
- <Text style={styles.buttonText}>OK</Text>
21
+ <AppText variant="semiBold" size="body" style={styles.buttonText}>
22
+ OK
23
+ </AppText>
19
24
  </TouchableOpacity>
20
25
  )}
21
26
  </View>
@@ -36,11 +41,8 @@ const styles = StyleSheet.create({
36
41
  text: {
37
42
  flex: 1,
38
43
  color: 'white',
39
- fontWeight: '500',
40
44
  },
41
45
  buttonText: {
42
46
  color: 'white',
43
- fontSize: 16,
44
- fontWeight: '600',
45
47
  },
46
48
  });
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
- import { View, Text, StyleSheet } from 'react-native';
2
+ import { View, StyleSheet } from 'react-native';
3
+ import AppText from './AppText';
3
4
  import { useTheme } from '../context/ThemeContext';
4
5
  import { ThemeType } from '../theme/Colors';
5
6
 
@@ -19,10 +20,16 @@ const FeatureItem: React.FC<FeatureItemProps> = ({
19
20
 
20
21
  return (
21
22
  <View style={styles.container}>
22
- <Text style={styles.icon}>{icon}</Text>
23
+ <AppText size="large" style={styles.icon}>
24
+ {icon}
25
+ </AppText>
23
26
  <View style={styles.textContainer}>
24
- <Text style={styles.title}>{title}</Text>
25
- <Text style={styles.description}>{description}</Text>
27
+ <AppText variant="semiBold" size={15} style={styles.title}>
28
+ {title}
29
+ </AppText>
30
+ <AppText size={13} style={styles.description}>
31
+ {description}
32
+ </AppText>
26
33
  </View>
27
34
  </View>
28
35
  );
@@ -38,7 +45,6 @@ const getStyles = ({ colors }: ThemeType) =>
38
45
  marginBottom: 12,
39
46
  },
40
47
  icon: {
41
- fontSize: 20,
42
48
  marginRight: 12,
43
49
  marginTop: 2,
44
50
  },
@@ -46,13 +52,10 @@ const getStyles = ({ colors }: ThemeType) =>
46
52
  flex: 1,
47
53
  },
48
54
  title: {
49
- fontSize: 15,
50
- fontWeight: '600',
51
55
  color: colors.textColor,
52
56
  marginBottom: 2,
53
57
  },
54
58
  description: {
55
- fontSize: 13,
56
59
  color: colors.textColor + 'CC',
57
60
  lineHeight: 18,
58
61
  },
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
- import { View, Text, StyleSheet } from 'react-native';
2
+ import { View, StyleSheet } from 'react-native';
3
+ import AppText from './AppText';
3
4
  import { useTheme } from '../context/ThemeContext';
4
5
  import { ThemeType } from '../theme/Colors';
5
6
 
@@ -16,8 +17,14 @@ const InfoCard: React.FC<InfoCardProps> = ({ title, children, icon }) => {
16
17
  return (
17
18
  <View style={styles.card}>
18
19
  <View style={styles.header}>
19
- {icon && <Text style={styles.icon}>{icon}</Text>}
20
- <Text style={styles.title}>{title}</Text>
20
+ {icon && (
21
+ <AppText size="xlarge" style={styles.icon}>
22
+ {icon}
23
+ </AppText>
24
+ )}
25
+ <AppText variant="bold" size={18} style={styles.title}>
26
+ {title}
27
+ </AppText>
21
28
  </View>
22
29
  <View style={styles.content}>{children}</View>
23
30
  </View>
@@ -47,12 +54,9 @@ const getStyles = ({ colors }: ThemeType) =>
47
54
  marginBottom: 12,
48
55
  },
49
56
  icon: {
50
- fontSize: 24,
51
57
  marginRight: 8,
52
58
  },
53
59
  title: {
54
- fontSize: 18,
55
- fontWeight: '700',
56
60
  color: colors.primary,
57
61
  },
58
62
  content: {
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
- import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
2
+ import { View, TouchableOpacity, StyleSheet } from 'react-native';
3
+ import AppText from './AppText';
3
4
  import { useTranslation } from 'react-i18next';
4
5
  import { useTheme } from '../context/ThemeContext';
5
6
  import { ThemeType } from '../theme/Colors';
@@ -21,27 +22,29 @@ const LanguageSwitcher = () => {
21
22
  style={[styles.button, i18n.language === 'en' && styles.activeButton]}
22
23
  onPress={() => changeLanguage('en')}
23
24
  >
24
- <Text
25
+ <AppText
26
+ variant="semiBold"
25
27
  style={[
26
28
  styles.buttonText,
27
29
  i18n.language === 'en' && styles.activeButtonText,
28
30
  ]}
29
31
  >
30
32
  🇬🇧 English
31
- </Text>
33
+ </AppText>
32
34
  </TouchableOpacity>
33
35
  <TouchableOpacity
34
36
  style={[styles.button, i18n.language === 'hi' && styles.activeButton]}
35
37
  onPress={() => changeLanguage('hi')}
36
38
  >
37
- <Text
39
+ <AppText
40
+ variant="semiBold"
38
41
  style={[
39
42
  styles.buttonText,
40
43
  i18n.language === 'hi' && styles.activeButtonText,
41
44
  ]}
42
45
  >
43
46
  🇮🇳 हिंदी
44
- </Text>
47
+ </AppText>
45
48
  </TouchableOpacity>
46
49
  </View>
47
50
  );
@@ -70,8 +73,6 @@ const getStyles = ({ colors }: ThemeType) =>
70
73
  backgroundColor: colors.primary,
71
74
  },
72
75
  buttonText: {
73
- fontSize: 14,
74
- fontWeight: '600',
75
76
  color: colors.primary,
76
77
  },
77
78
  activeButtonText: {
@@ -1,11 +1,21 @@
1
- import React from 'react';
1
+ import React, { useState, useEffect } from 'react';
2
2
  import {
3
3
  TextInput as RNTextInput,
4
4
  TextInputProps,
5
5
  View,
6
- Text,
7
6
  StyleSheet,
7
+ StyleProp,
8
+ TextStyle,
9
+ ViewStyle,
8
10
  } from 'react-native';
11
+ import Animated, {
12
+ useSharedValue,
13
+ useAnimatedStyle,
14
+ withTiming,
15
+ interpolate,
16
+ Extrapolate,
17
+ } from 'react-native-reanimated';
18
+ import AppText from './AppText';
9
19
  import { useTheme } from '../context/ThemeContext';
10
20
  import { ThemeType } from '../theme/Colors';
11
21
 
@@ -13,6 +23,17 @@ interface CustomTextInputProps extends TextInputProps {
13
23
  label?: string;
14
24
  error?: string;
15
25
  touched?: boolean;
26
+ renderLeft?: () => React.ReactNode;
27
+ renderRight?: () => React.ReactNode;
28
+ leftIcon?: React.ReactNode;
29
+ rightIcon?: React.ReactNode;
30
+ prefix?: string;
31
+ suffix?: string;
32
+ prefixStyle?: StyleProp<TextStyle>;
33
+ suffixStyle?: StyleProp<TextStyle>;
34
+ containerStyle?: StyleProp<ViewStyle>;
35
+ inputContainerStyle?: StyleProp<ViewStyle>;
36
+ labelBackgroundColor?: string;
16
37
  }
17
38
 
18
39
  const TextInput: React.FC<CustomTextInputProps> = ({
@@ -20,22 +41,161 @@ const TextInput: React.FC<CustomTextInputProps> = ({
20
41
  error,
21
42
  touched,
22
43
  style,
44
+ renderLeft,
45
+ renderRight,
46
+ leftIcon,
47
+ rightIcon,
48
+ prefix,
49
+ suffix,
50
+ prefixStyle,
51
+ suffixStyle,
52
+ containerStyle,
53
+ inputContainerStyle,
54
+ labelBackgroundColor,
55
+ value,
56
+ placeholder,
57
+ onFocus,
58
+ onBlur,
23
59
  ...props
24
60
  }) => {
25
61
  const theme = useTheme();
26
62
  const styles = getStyles(theme);
63
+ const [isFocused, setIsFocused] = useState(false);
64
+ const [leftWidth, setLeftWidth] = useState(0);
27
65
 
66
+ const hasValue = value !== undefined && value !== '';
28
67
  const hasError = touched && error;
68
+ const labelBg = labelBackgroundColor || theme.colors.backgroundColor;
69
+
70
+ const labelAnimation = useSharedValue(hasValue ? 1 : 0);
71
+
72
+ useEffect(() => {
73
+ labelAnimation.value = withTiming(isFocused || hasValue ? 1 : 0, {
74
+ duration: 200,
75
+ });
76
+ }, [isFocused, hasValue, labelAnimation]);
77
+
78
+ const animatedLabelStyle = useAnimatedStyle(() => {
79
+ return {
80
+ transform: [
81
+ {
82
+ translateY: interpolate(
83
+ labelAnimation.value,
84
+ [0, 1],
85
+ [0, -28],
86
+ Extrapolate.CLAMP,
87
+ ),
88
+ },
89
+ {
90
+ translateX: interpolate(
91
+ labelAnimation.value,
92
+ [0, 1],
93
+ [leftWidth, -4],
94
+ Extrapolate.CLAMP,
95
+ ),
96
+ },
97
+ {
98
+ scale: interpolate(
99
+ labelAnimation.value,
100
+ [0, 1],
101
+ [1, 0.85],
102
+ Extrapolate.CLAMP,
103
+ ),
104
+ },
105
+ ],
106
+ backgroundColor: labelAnimation.value === 1 ? labelBg : 'transparent',
107
+ paddingHorizontal: interpolate(
108
+ labelAnimation.value,
109
+ [0, 1],
110
+ [0, 4],
111
+ Extrapolate.CLAMP,
112
+ ),
113
+ color:
114
+ isFocused || hasValue
115
+ ? hasError
116
+ ? theme.colors.primary
117
+ : theme.colors.primary
118
+ : theme.colors.textColor + '80',
119
+ };
120
+ });
121
+
122
+ const handleFocus = (e: any) => {
123
+ setIsFocused(true);
124
+ onFocus?.(e);
125
+ };
126
+
127
+ const handleBlur = (e: any) => {
128
+ setIsFocused(false);
129
+ onBlur?.(e);
130
+ };
131
+
132
+ const onLeftLayout = (event: any) => {
133
+ setLeftWidth(event.nativeEvent.layout.width);
134
+ };
135
+
136
+ const showPlaceholder = label ? isFocused : true;
29
137
 
30
138
  return (
31
- <View style={styles.container}>
32
- {label && <Text style={styles.label}>{label}</Text>}
33
- <RNTextInput
34
- style={[styles.input, hasError && styles.inputError, style]}
35
- placeholderTextColor={theme.colors.textColor + '60'}
36
- {...props}
37
- />
38
- {hasError && <Text style={styles.errorText}>{error}</Text>}
139
+ <View style={[styles.container, containerStyle]}>
140
+ <View
141
+ style={[
142
+ styles.inputWrapper,
143
+ isFocused && styles.inputWrapperFocused,
144
+ hasError && styles.inputWrapperError,
145
+ inputContainerStyle,
146
+ ]}
147
+ >
148
+ {label && (
149
+ <Animated.View
150
+ pointerEvents="none"
151
+ style={[styles.labelContainer, animatedLabelStyle]}
152
+ >
153
+ <AppText variant="medium">{label}</AppText>
154
+ </Animated.View>
155
+ )}
156
+
157
+ <View style={styles.leftElements} onLayout={onLeftLayout}>
158
+ {renderLeft ? (
159
+ renderLeft()
160
+ ) : typeof leftIcon === 'string' ? (
161
+ <AppText style={styles.iconLeft}>{leftIcon}</AppText>
162
+ ) : (
163
+ leftIcon
164
+ )}
165
+
166
+ {prefix && (
167
+ <AppText style={[styles.prefix, prefixStyle]}>{prefix}</AppText>
168
+ )}
169
+ </View>
170
+
171
+ <RNTextInput
172
+ style={[styles.input, style]}
173
+ placeholder={showPlaceholder ? placeholder : ''}
174
+ placeholderTextColor={theme.colors.textColor + '60'}
175
+ onFocus={handleFocus}
176
+ onBlur={handleBlur}
177
+ value={value}
178
+ {...props}
179
+ />
180
+
181
+ {suffix && (
182
+ <AppText style={[styles.suffix, suffixStyle]}>{suffix}</AppText>
183
+ )}
184
+
185
+ {renderRight ? (
186
+ renderRight()
187
+ ) : typeof rightIcon === 'string' ? (
188
+ <AppText style={styles.iconRight}>{rightIcon}</AppText>
189
+ ) : (
190
+ rightIcon
191
+ )}
192
+ </View>
193
+
194
+ {hasError && (
195
+ <AppText size="small" style={styles.errorText}>
196
+ {error}
197
+ </AppText>
198
+ )}
39
199
  </View>
40
200
  );
41
201
  };
@@ -45,30 +205,61 @@ export default TextInput;
45
205
  const getStyles = ({ colors }: ThemeType) =>
46
206
  StyleSheet.create({
47
207
  container: {
48
- marginBottom: 20,
49
- },
50
- label: {
51
- fontSize: 14,
52
- fontWeight: '600',
53
- color: colors.textColor,
54
- marginBottom: 8,
208
+ marginBottom: 24,
209
+ marginTop: 12,
55
210
  },
56
- input: {
211
+ inputWrapper: {
212
+ flexDirection: 'row',
213
+ alignItems: 'center',
57
214
  backgroundColor: colors.backgroundColor,
58
215
  borderWidth: 1,
59
216
  borderColor: colors.textColor + '30',
60
217
  borderRadius: 12,
61
- padding: 16,
218
+ paddingHorizontal: 12,
219
+ minHeight: 56,
220
+ },
221
+ inputWrapperFocused: {
222
+ borderColor: colors.primary,
223
+ },
224
+ inputWrapperError: {
225
+ borderColor: colors.primary,
226
+ },
227
+ labelContainer: {
228
+ position: 'absolute',
229
+ left: 12,
230
+ top: 16,
231
+ zIndex: 1,
232
+ },
233
+ input: {
234
+ flex: 1,
62
235
  fontSize: 16,
63
236
  color: colors.textColor,
237
+ paddingVertical: 12,
238
+ paddingHorizontal: 4,
64
239
  },
65
- inputError: {
66
- borderColor: colors.primary,
240
+ leftElements: {
241
+ flexDirection: 'row',
242
+ alignItems: 'center',
243
+ },
244
+ iconLeft: {
245
+ marginRight: 8,
246
+ fontSize: 20,
247
+ },
248
+ iconRight: {
249
+ marginLeft: 8,
250
+ fontSize: 20,
251
+ },
252
+ prefix: {
253
+ marginRight: 4,
254
+ color: colors.textColor + '80',
255
+ },
256
+ suffix: {
257
+ marginLeft: 4,
258
+ color: colors.textColor + '80',
67
259
  },
68
260
  errorText: {
69
261
  color: colors.primary,
70
- fontSize: 12,
71
- marginTop: 4,
262
+ marginTop: 6,
72
263
  marginLeft: 4,
73
264
  },
74
265
  });
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
- import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
2
+ import { View, TouchableOpacity, StyleSheet } from 'react-native';
3
+ import AppText from './AppText';
3
4
  import { useTheme } from '../context/ThemeContext';
4
5
  import { ThemeType } from '../theme/Colors';
5
6
 
@@ -15,17 +16,23 @@ const ThemeSwitcher = () => {
15
16
  style={[styles.button, !isDark && styles.activeButton]}
16
17
  onPress={() => !isDark || toggleTheme()}
17
18
  >
18
- <Text style={[styles.buttonText, !isDark && styles.activeButtonText]}>
19
+ <AppText
20
+ variant="semiBold"
21
+ style={[styles.buttonText, !isDark && styles.activeButtonText]}
22
+ >
19
23
  ☀️ Light
20
- </Text>
24
+ </AppText>
21
25
  </TouchableOpacity>
22
26
  <TouchableOpacity
23
27
  style={[styles.button, isDark && styles.activeButton]}
24
28
  onPress={() => isDark || toggleTheme()}
25
29
  >
26
- <Text style={[styles.buttonText, isDark && styles.activeButtonText]}>
30
+ <AppText
31
+ variant="semiBold"
32
+ style={[styles.buttonText, isDark && styles.activeButtonText]}
33
+ >
27
34
  🌙 Dark
28
- </Text>
35
+ </AppText>
29
36
  </TouchableOpacity>
30
37
  </View>
31
38
  );
@@ -53,8 +60,6 @@ const getStyles = ({ colors }: ThemeType) =>
53
60
  backgroundColor: colors.primary,
54
61
  },
55
62
  buttonText: {
56
- fontSize: 14,
57
- fontWeight: '600',
58
63
  color: colors.primary,
59
64
  },
60
65
  activeButtonText: {