frontend: add screen layout
Pedro Lucas Porcellis porcellis@eletrotupi.com · 5 months ago
51a7c2bc2232d2d4a32f2314cb9bd484478cb323 Browse filesParents
7a3f55eChanged files
2
Additions
+159
Deletions
0
/dev/null → frontend/components/ui/ScreenLayout.tsx
1 +
import React from 'react';
2 +
3 +
import {
4 +
View,
5 +
ScrollView,
6 +
StyleSheet,
7 +
Image,
8 +
Pressable,
9 +
StatusBar,
10 +
} from 'react-native';
11 +
12 +
import { SafeAreaView } from 'react-native-safe-area-context';
13 +
import { IconSymbol } from '@/components/ui/icon-symbol';
14 +
import { useThemeColor } from '@/hooks/use-theme-color';
15 +
import { Typography, Spacing, BorderRadius } from '@/constants/theme'
16 +
import { ThemedText } from '@/components/misc/themed-text'
17 +
18 +
interface ScreenLayoutProps {
19 +
children: React.ReactNode;
20 +
userName?: string;
21 +
userAvatar?: string;
22 +
onNotificationPress?: () => void;
23 +
showNotificationBadge?: boolean;
24 +
scrollEnabled?: boolean;
25 +
}
26 +
27 +
export const ScreenLayout: React.FC<ScreenLayoutProps> = ({
28 +
children,
29 +
userName = 'Usuário',
30 +
userAvatar,
31 +
onNotificationPress,
32 +
showNotificationBadge = false,
33 +
scrollEnabled = true,
34 +
}) => {
35 +
const textColor = useThemeColor({}, 'text');
36 +
const backgroundColor = useThemeColor({}, 'background');
37 +
const surfaceColor = useThemeColor({}, 'surface');
38 +
const dividerColor = useThemeColor({}, 'divider');
39 +
const accentBlue = useThemeColor({}, 'accentBlue');
40 +
const tintColor = useThemeColor({}, 'tint');
41 +
42 +
const styles = StyleSheet.create({
43 +
container: {
44 +
flex: 1,
45 +
backgroundColor: backgroundColor,
46 +
},
47 +
safeAreaView: {
48 +
flex: 1,
49 +
backgroundColor: backgroundColor,
50 +
},
51 +
header: {
52 +
paddingHorizontal: Spacing.containerPadding,
53 +
paddingVertical: 12,
54 +
backgroundColor: surfaceColor,
55 +
borderBottomWidth: 1,
56 +
borderBottomColor: dividerColor,
57 +
flexDirection: 'row',
58 +
justifyContent: 'space-between',
59 +
alignItems: 'center',
60 +
},
61 +
headerLeft: {
62 +
flexDirection: 'row',
63 +
alignItems: 'center',
64 +
gap: Spacing.inlineGapSm,
65 +
},
66 +
avatar: {
67 +
width: 40,
68 +
height: 40,
69 +
borderRadius: 20,
70 +
backgroundColor: accentBlue,
71 +
},
72 +
userName: {
73 +
fontSize: Typography.bodyLg.fontSize,
74 +
fontWeight: '600',
75 +
color: textColor,
76 +
},
77 +
notificationButton: {
78 +
width: 40,
79 +
height: 40,
80 +
borderRadius: BorderRadius.md,
81 +
justifyContent: 'center',
82 +
alignItems: 'center',
83 +
activeOpacity: 0.7,
84 +
},
85 +
notificationBadge: {
86 +
position: 'absolute',
87 +
top: 4,
88 +
right: 4,
89 +
width: 8,
90 +
height: 8,
91 +
borderRadius: 4,
92 +
backgroundColor: tintColor,
93 +
},
94 +
scrollContent: {
95 +
paddingHorizontal: Spacing.containerPadding,
96 +
paddingVertical: Spacing.sectionGap,
97 +
},
98 +
});
99 +
100 +
return (
101 +
<View style={styles.container}>
102 +
<StatusBar
103 +
barStyle="dark-content"
104 +
backgroundColor={surfaceColor}
105 +
/>
106 +
<SafeAreaView style={styles.safeAreaView}>
107 +
<View style={styles.header}>
108 +
<View style={styles.headerLeft}>
109 +
{userAvatar ? (
110 +
<Image
111 +
source={{ uri: userAvatar }}
112 +
style={styles.avatar}
113 +
/>
114 +
) : (
115 +
<View style={styles.avatar} />
116 +
)}
117 +
<View>
118 +
<ThemedText style={styles.userName}>
119 +
Olá, {userName}
120 +
</ThemedText>
121 +
</View>
122 +
</View>
123 +
124 +
<Pressable
125 +
style={styles.notificationButton}
126 +
onPress={onNotificationPress}
127 +
hitSlop={8}
128 +
>
129 +
<View
130 +
style={{
131 +
fontSize: 24,
132 +
color: textColor,
133 +
}}
134 +
>
135 +
<IconSymbol size={24} name="notifications" color={textColor} />
136 +
</View>
137 +
{showNotificationBadge && (
138 +
<View style={styles.notificationBadge} />
139 +
)}
140 +
</Pressable>
141 +
</View>
142 +
143 +
<ScrollView
144 +
scrollEnabled={scrollEnabled}
145 +
showsVerticalScrollIndicator={false}
146 +
contentContainerStyle={scrollEnabled ? undefined : { flex: 1 }}
147 +
scrollEventThrottle={16}
148 +
>
149 +
<View style={styles.scrollContent}>
150 +
{children}
151 +
</View>
152 +
</ScrollView>
153 +
</SafeAreaView>
154 +
</View>
155 +
);
156 +
};
157 +
158 +
export default ScreenLayout;
frontend/components/ui/icon-symbol.tsx
1 1
// Fallback for using MaterialIcons on Android and web.
2 2
3 3
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
4 4
import { SymbolWeight, SymbolViewProps } from 'expo-symbols';
5 5
import { ComponentProps } from 'react';
16 16
* TODO: We might get rid of this completely and just wrap a <MeterialIcons>
17 17
* since we won't have iOS stuff
18 18
*/
19 19
const MAPPING = {
20 20
'house.fill': 'home',
21 +
'notifications': 'notifications',
21 22
'add-circle': 'add-circle',
22 23
'history': 'history',
23 24
'lightbulb-outline': 'lightbulb-outline',
24 25
'paperplane.fill': 'send',
25 26
'chevron.left.forwardslash.chevron.right': 'code',
45 46
style?: StyleProp<TextStyle>;
46 47
weight?: SymbolWeight;
47 48
}) {
48 49
return <MaterialIcons color={color} size={size} name={MAPPING[name]} style={style} />;
49 50
}