@xetwa/design-system 1.0.40 → 1.0.41
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.
- package/package.json +1 -1
- package/src/components/Cards/BoardingPassCard/BoardingPassCard.tsx +25 -22
- package/src/components/Cards/ModuleCard/ModuleCard.tsx +6 -7
- package/src/components/Navigation/TabBar/TabBar.stories.tsx +80 -0
- package/src/components/Navigation/TabBar/TabBar.tsx +245 -0
- package/src/index.ts +1 -0
- package/src/styles/theme.ts +106 -31
- package/src/utils/iconMap.ts +9 -1
package/package.json
CHANGED
|
@@ -132,17 +132,17 @@ export const BoardingPassCard = ({
|
|
|
132
132
|
styles.container,
|
|
133
133
|
{
|
|
134
134
|
borderRadius: scale(20),
|
|
135
|
-
minHeight: scale(
|
|
135
|
+
minHeight: scale(240),
|
|
136
136
|
},
|
|
137
137
|
style
|
|
138
138
|
]}>
|
|
139
139
|
{/* Top Section */}
|
|
140
140
|
<View style={[
|
|
141
|
-
styles.topSection,
|
|
142
|
-
{
|
|
141
|
+
styles.topSection,
|
|
142
|
+
{
|
|
143
143
|
backgroundColor: currentHeaderBg,
|
|
144
|
-
paddingTop: scale(
|
|
145
|
-
paddingBottom: scale(
|
|
144
|
+
paddingTop: scale(24),
|
|
145
|
+
paddingBottom: scale(24),
|
|
146
146
|
paddingHorizontal: scale(18),
|
|
147
147
|
}
|
|
148
148
|
]}>
|
|
@@ -158,7 +158,7 @@ export const BoardingPassCard = ({
|
|
|
158
158
|
]} />
|
|
159
159
|
|
|
160
160
|
<View style={[styles.headerRow, { marginBottom: scale(16) }]}>
|
|
161
|
-
<Typography variant="
|
|
161
|
+
<Typography variant="labelHeavy" style={[styles.headerText]}>CARTÃO DE EMBARQUE</Typography>
|
|
162
162
|
<View style={styles.headerPlaneContainer}>
|
|
163
163
|
<Plane stroke={theme.colors.white} strokeWidth={2.2} size={scale(22)} />
|
|
164
164
|
</View>
|
|
@@ -166,8 +166,8 @@ export const BoardingPassCard = ({
|
|
|
166
166
|
|
|
167
167
|
<View style={styles.routeRow}>
|
|
168
168
|
<View style={styles.cityCol}>
|
|
169
|
-
<Typography variant="
|
|
170
|
-
<Typography variant="
|
|
169
|
+
<Typography variant="display2" style={[styles.cityCode]}>{originCode}</Typography>
|
|
170
|
+
<Typography variant="small" style={[styles.cityName]}>{originName}</Typography>
|
|
171
171
|
</View>
|
|
172
172
|
|
|
173
173
|
<View style={[styles.flightLineContainer, { height: scale(30), marginHorizontal: scale(12) }]}>
|
|
@@ -178,8 +178,8 @@ export const BoardingPassCard = ({
|
|
|
178
178
|
</View>
|
|
179
179
|
|
|
180
180
|
<View style={[styles.cityCol, { alignItems: 'flex-end' }]}>
|
|
181
|
-
<Typography variant="
|
|
182
|
-
<Typography variant="
|
|
181
|
+
<Typography variant="display2" style={[styles.cityCode]}>{destCode}</Typography>
|
|
182
|
+
<Typography variant="small" style={[styles.cityName]}>{destName}</Typography>
|
|
183
183
|
</View>
|
|
184
184
|
</View>
|
|
185
185
|
</View>
|
|
@@ -188,7 +188,7 @@ export const BoardingPassCard = ({
|
|
|
188
188
|
<View style={[
|
|
189
189
|
styles.bottomSection,
|
|
190
190
|
{
|
|
191
|
-
paddingVertical: scale(
|
|
191
|
+
paddingVertical: scale(20),
|
|
192
192
|
paddingHorizontal: scale(18),
|
|
193
193
|
}
|
|
194
194
|
]}>
|
|
@@ -205,8 +205,8 @@ export const BoardingPassCard = ({
|
|
|
205
205
|
{flagIconNode ? flagIconNode : <Flag countryCode={countryName} size={scale(40)} />}
|
|
206
206
|
</View>
|
|
207
207
|
<View style={styles.countryContent}>
|
|
208
|
-
<Typography variant="
|
|
209
|
-
<Typography variant="
|
|
208
|
+
<Typography variant="subtitle1" style={[styles.countryName]}>{countryName}</Typography>
|
|
209
|
+
<Typography variant="label" style={[styles.countrySubtitle]}>{countrySubtitle}</Typography>
|
|
210
210
|
</View>
|
|
211
211
|
</View>
|
|
212
212
|
|
|
@@ -220,12 +220,12 @@ export const BoardingPassCard = ({
|
|
|
220
220
|
|
|
221
221
|
<View style={[styles.footerRow, { gap: scale(14) }]}>
|
|
222
222
|
<View style={styles.footerCol}>
|
|
223
|
-
<Typography variant="
|
|
224
|
-
<Typography variant="
|
|
223
|
+
<Typography variant="labelHeavy" style={[styles.footerLabel]}>MÓDULOS</Typography>
|
|
224
|
+
<Typography variant="subtitle1" style={[styles.footerValue]}>{modulesCount}</Typography>
|
|
225
225
|
</View>
|
|
226
226
|
<View style={styles.footerCol}>
|
|
227
|
-
<Typography variant="
|
|
228
|
-
<Typography variant="
|
|
227
|
+
<Typography variant="labelHeavy" style={[styles.footerLabel]}>FOCO</Typography>
|
|
228
|
+
<Typography variant="subtitle1" style={[styles.footerValue]}>{focusText}</Typography>
|
|
229
229
|
</View>
|
|
230
230
|
</View>
|
|
231
231
|
</View>
|
|
@@ -241,6 +241,10 @@ const styles = StyleSheet.create({
|
|
|
241
241
|
minHeight: 230,
|
|
242
242
|
...theme.shadows.lg,
|
|
243
243
|
},
|
|
244
|
+
headerText: {
|
|
245
|
+
color: theme.colors.white,
|
|
246
|
+
opacity: 0.9,
|
|
247
|
+
},
|
|
244
248
|
topSection: {
|
|
245
249
|
paddingTop: 16,
|
|
246
250
|
paddingBottom: 16,
|
|
@@ -266,11 +270,6 @@ const styles = StyleSheet.create({
|
|
|
266
270
|
headerPlaneContainer: {
|
|
267
271
|
transform: [{ rotate: '0deg' }],
|
|
268
272
|
},
|
|
269
|
-
headerText: {
|
|
270
|
-
letterSpacing: 1.2,
|
|
271
|
-
textTransform: 'uppercase',
|
|
272
|
-
color: 'rgba(255, 255, 255, 0.75)',
|
|
273
|
-
},
|
|
274
273
|
routeRow: {
|
|
275
274
|
flexDirection: 'row',
|
|
276
275
|
alignItems: 'center',
|
|
@@ -345,6 +344,10 @@ const styles = StyleSheet.create({
|
|
|
345
344
|
countrySubtitle: {
|
|
346
345
|
color: '#9a8478',
|
|
347
346
|
},
|
|
347
|
+
footerLabel: {
|
|
348
|
+
color: theme.colors.brown[500],
|
|
349
|
+
marginBottom: 4,
|
|
350
|
+
},
|
|
348
351
|
divider: {
|
|
349
352
|
borderTopWidth: 1.5,
|
|
350
353
|
borderColor: '#f3e7da',
|
|
@@ -235,9 +235,8 @@ export const ModuleCard = ({
|
|
|
235
235
|
const cardPaddingV = size === 'xl' ? scale(20) : size === 'lg' ? scale(16) : isMobileScreen ? scale(10) : scale(14);
|
|
236
236
|
const cardPaddingH = size === 'xl' ? scale(20) : size === 'lg' ? scale(18) : isMobileScreen ? scale(12) : scale(15);
|
|
237
237
|
|
|
238
|
-
const titleVariant = size === 'xl' ? '
|
|
239
|
-
|
|
240
|
-
const subtitleVariant = size === 'xl' ? 'bodyLg' : size === 'lg' ? 'bodyMd' : 'bodySm';
|
|
238
|
+
const titleVariant = size === 'xl' ? 'h4' : size === 'lg' ? 'h6' : 'subtitle1';
|
|
239
|
+
const subtitleVariant = size === 'xl' ? 'bodySm' : size === 'lg' ? 'caption' : 'label';
|
|
241
240
|
|
|
242
241
|
|
|
243
242
|
|
|
@@ -330,7 +329,7 @@ export const ModuleCard = ({
|
|
|
330
329
|
hitSlop={10}
|
|
331
330
|
>
|
|
332
331
|
<RotateCcw stroke={theme.colors.primary} strokeWidth={2.5} size={isMobileScreen ? scale(20) : scale(24)} />
|
|
333
|
-
<Typography variant="
|
|
332
|
+
<Typography variant="smallHeavy" style={[styles.reviewIconText, { fontSize: scaleText(9) }]}>REVER</Typography>
|
|
334
333
|
</Pressable>
|
|
335
334
|
</Animated.View>
|
|
336
335
|
) : null}
|
|
@@ -368,7 +367,7 @@ export const ModuleCard = ({
|
|
|
368
367
|
style={{ flexDirection: 'row', alignItems: 'center', backgroundColor: theme.colors.primary, paddingHorizontal: scale(14), paddingVertical: scale(6), borderRadius: 100 }}
|
|
369
368
|
>
|
|
370
369
|
<CalendarPlus stroke={theme.colors.white} strokeWidth={2.5} size={scale(14)} />
|
|
371
|
-
<Typography variant="
|
|
370
|
+
<Typography variant="bodySmHeavy" style={{ color: theme.colors.white, marginLeft: scale(6) }}>Agendar Aula</Typography>
|
|
372
371
|
</Pressable>
|
|
373
372
|
</Animated.View>
|
|
374
373
|
)}
|
|
@@ -383,7 +382,7 @@ export const ModuleCard = ({
|
|
|
383
382
|
style={{ flexDirection: 'row', alignItems: 'center', backgroundColor: theme.colors.primary, paddingHorizontal: scale(14), paddingVertical: scale(6), borderRadius: 100 }}
|
|
384
383
|
>
|
|
385
384
|
<PenTool stroke={theme.colors.white} strokeWidth={2.5} size={scale(14)} />
|
|
386
|
-
<Typography variant="
|
|
385
|
+
<Typography variant="bodySmHeavy" style={{ color: theme.colors.white, marginLeft: scale(6) }}>Fazer Exame</Typography>
|
|
387
386
|
</Pressable>
|
|
388
387
|
</Animated.View>
|
|
389
388
|
)}
|
|
@@ -399,7 +398,7 @@ export const ModuleCard = ({
|
|
|
399
398
|
hitSlop={8}
|
|
400
399
|
>
|
|
401
400
|
<RotateCcw stroke={theme.colors.primary} strokeWidth={2.5} size={scale(16)} />
|
|
402
|
-
<Typography variant="
|
|
401
|
+
<Typography variant="smallHeavy" style={[styles.reviewIconText, { color: theme.colors.primary, marginLeft: scale(4), marginTop: 0 }]}>REVER</Typography>
|
|
403
402
|
</Pressable>
|
|
404
403
|
</Animated.View>
|
|
405
404
|
)}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import React, { useState } from 'react';
|
|
3
|
+
import { View } from 'react-native';
|
|
4
|
+
import { TabBar } from './TabBar';
|
|
5
|
+
|
|
6
|
+
const meta = {
|
|
7
|
+
title: 'Components/Navigation/TabBar',
|
|
8
|
+
component: TabBar,
|
|
9
|
+
parameters: {
|
|
10
|
+
layout: 'fullscreen',
|
|
11
|
+
docs: {
|
|
12
|
+
description: {
|
|
13
|
+
component: 'TabBar component with 3 variants (A: Pill, B: Top Bar, C: Dot below) and notification badges.',
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
tags: ['autodocs'],
|
|
18
|
+
argTypes: {
|
|
19
|
+
variant: { control: 'radio', options: ['A', 'B', 'C'] },
|
|
20
|
+
safeAreaBottom: { control: 'number' },
|
|
21
|
+
activeItemId: { control: 'text' },
|
|
22
|
+
},
|
|
23
|
+
decorators: [
|
|
24
|
+
(Story) => (
|
|
25
|
+
<View style={{ flex: 1, justifyContent: 'flex-end', backgroundColor: '#f5f0eb' }}>
|
|
26
|
+
<Story />
|
|
27
|
+
</View>
|
|
28
|
+
),
|
|
29
|
+
],
|
|
30
|
+
} satisfies Meta<typeof TabBar>;
|
|
31
|
+
|
|
32
|
+
export default meta;
|
|
33
|
+
type Story = StoryObj<typeof TabBar>;
|
|
34
|
+
|
|
35
|
+
const defaultItems = [
|
|
36
|
+
{ id: 'inicio', label: 'Início', icon: 'Home' },
|
|
37
|
+
{ id: 'percurso', label: 'Percurso', icon: 'Compass' },
|
|
38
|
+
{ id: 'aulas', label: 'Aulas', icon: 'GraduationCap' },
|
|
39
|
+
{ id: 'dossier', label: 'Dossier', hasBadge: true, icon: 'FolderOpen' },
|
|
40
|
+
{ id: 'perfil', label: 'Perfil', icon: 'User' },
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
export const VariantB: Story = {
|
|
44
|
+
args: {
|
|
45
|
+
items: defaultItems,
|
|
46
|
+
activeItemId: 'inicio',
|
|
47
|
+
variant: 'B',
|
|
48
|
+
safeAreaBottom: 20,
|
|
49
|
+
},
|
|
50
|
+
render: function Render(args) {
|
|
51
|
+
const [activeId, setActiveId] = useState(args.activeItemId);
|
|
52
|
+
return <TabBar {...args} activeItemId={activeId} onItemPress={setActiveId} />;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const VariantA: Story = {
|
|
57
|
+
args: {
|
|
58
|
+
items: defaultItems,
|
|
59
|
+
activeItemId: 'inicio',
|
|
60
|
+
variant: 'A',
|
|
61
|
+
safeAreaBottom: 20,
|
|
62
|
+
},
|
|
63
|
+
render: function Render(args) {
|
|
64
|
+
const [activeId, setActiveId] = useState(args.activeItemId);
|
|
65
|
+
return <TabBar {...args} activeItemId={activeId} onItemPress={setActiveId} />;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export const VariantC: Story = {
|
|
70
|
+
args: {
|
|
71
|
+
items: defaultItems,
|
|
72
|
+
activeItemId: 'inicio',
|
|
73
|
+
variant: 'C',
|
|
74
|
+
safeAreaBottom: 20,
|
|
75
|
+
},
|
|
76
|
+
render: function Render(args) {
|
|
77
|
+
const [activeId, setActiveId] = useState(args.activeItemId);
|
|
78
|
+
return <TabBar {...args} activeItemId={activeId} onItemPress={setActiveId} />;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import React, { useRef, useEffect } from 'react';
|
|
2
|
+
import { View, StyleSheet, Pressable, Animated, Platform } from 'react-native';
|
|
3
|
+
import type { ViewStyle } from 'react-native';
|
|
4
|
+
import { theme } from '../../../styles/theme';
|
|
5
|
+
import { Typography } from '../../Typography/Typography';
|
|
6
|
+
import { useResponsive } from '../../../hooks/useResponsive';
|
|
7
|
+
import { IconMap } from '../../../utils/iconMap';
|
|
8
|
+
|
|
9
|
+
export type TabBarVariant = 'A' | 'B' | 'C';
|
|
10
|
+
|
|
11
|
+
export interface TabBarItem {
|
|
12
|
+
id: string;
|
|
13
|
+
label: string;
|
|
14
|
+
icon: string;
|
|
15
|
+
hasBadge?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface TabBarProps {
|
|
19
|
+
items: TabBarItem[];
|
|
20
|
+
activeItemId: string;
|
|
21
|
+
onItemPress: (id: string) => void;
|
|
22
|
+
variant?: TabBarVariant;
|
|
23
|
+
safeAreaBottom?: number;
|
|
24
|
+
style?: ViewStyle | ViewStyle[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const TabBarItemComponent = ({
|
|
28
|
+
item,
|
|
29
|
+
isActive,
|
|
30
|
+
variant,
|
|
31
|
+
iconSize,
|
|
32
|
+
onItemPress,
|
|
33
|
+
}: {
|
|
34
|
+
item: TabBarItem;
|
|
35
|
+
isActive: boolean;
|
|
36
|
+
variant: TabBarVariant;
|
|
37
|
+
iconSize: number;
|
|
38
|
+
onItemPress: (id: string) => void;
|
|
39
|
+
}) => {
|
|
40
|
+
const { scale, scaleText } = useResponsive();
|
|
41
|
+
const color = isActive ? theme.colors.primary : theme.colors.brown[300];
|
|
42
|
+
|
|
43
|
+
// Animação de escala ao pressionar
|
|
44
|
+
const scaleAnim = useRef(new Animated.Value(1)).current;
|
|
45
|
+
|
|
46
|
+
const handlePressIn = () => {
|
|
47
|
+
Animated.timing(scaleAnim, {
|
|
48
|
+
toValue: 0.9,
|
|
49
|
+
duration: 100,
|
|
50
|
+
useNativeDriver: true,
|
|
51
|
+
}).start();
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const handlePressOut = () => {
|
|
55
|
+
Animated.spring(scaleAnim, {
|
|
56
|
+
toValue: 1,
|
|
57
|
+
friction: 4,
|
|
58
|
+
tension: 40,
|
|
59
|
+
useNativeDriver: true,
|
|
60
|
+
}).start();
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Animação de transição quando se torna ativo
|
|
64
|
+
const activeAnim = useRef(new Animated.Value(isActive ? 1 : 0)).current;
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
Animated.spring(activeAnim, {
|
|
67
|
+
toValue: isActive ? 1 : 0,
|
|
68
|
+
friction: 7,
|
|
69
|
+
tension: 60,
|
|
70
|
+
useNativeDriver: true,
|
|
71
|
+
}).start();
|
|
72
|
+
}, [isActive]);
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<Pressable
|
|
76
|
+
onPress={() => onItemPress(item.id)}
|
|
77
|
+
onPressIn={handlePressIn}
|
|
78
|
+
onPressOut={handlePressOut}
|
|
79
|
+
style={styles.itemContainer}
|
|
80
|
+
>
|
|
81
|
+
<Animated.View style={[styles.itemInner, { transform: [{ scale: scaleAnim }] }]}>
|
|
82
|
+
{/* Variante B: Barra no topo */}
|
|
83
|
+
{variant === 'B' && (
|
|
84
|
+
<Animated.View
|
|
85
|
+
style={[
|
|
86
|
+
styles.indicatorB,
|
|
87
|
+
{
|
|
88
|
+
width: scale(28),
|
|
89
|
+
height: scale(4),
|
|
90
|
+
opacity: activeAnim,
|
|
91
|
+
transform: [
|
|
92
|
+
{ translateY: activeAnim.interpolate({ inputRange: [0, 1], outputRange: [-4, 0] }) },
|
|
93
|
+
{ scaleX: activeAnim.interpolate({ inputRange: [0, 1], outputRange: [0.4, 1] }) }
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
]}
|
|
97
|
+
/>
|
|
98
|
+
)}
|
|
99
|
+
|
|
100
|
+
{/* Variante A: Pill no ícone */}
|
|
101
|
+
<View
|
|
102
|
+
style={[
|
|
103
|
+
styles.iconWrapper,
|
|
104
|
+
variant === 'A' && isActive && styles.pillA,
|
|
105
|
+
variant === 'A' && isActive && { paddingHorizontal: scale(16), paddingVertical: scale(6), borderRadius: scale(100) },
|
|
106
|
+
]}
|
|
107
|
+
>
|
|
108
|
+
{(() => {
|
|
109
|
+
const IconComponent = IconMap[item.icon];
|
|
110
|
+
if (!IconComponent) return null;
|
|
111
|
+
return <IconComponent color={color} size={iconSize} strokeWidth={2.5} />;
|
|
112
|
+
})()}
|
|
113
|
+
|
|
114
|
+
{/* Badge de Notificação */}
|
|
115
|
+
{item.hasBadge && (
|
|
116
|
+
<View
|
|
117
|
+
style={[
|
|
118
|
+
styles.badge,
|
|
119
|
+
{
|
|
120
|
+
width: scale(9),
|
|
121
|
+
height: scale(9),
|
|
122
|
+
borderRadius: scale(4.5),
|
|
123
|
+
borderWidth: scale(2),
|
|
124
|
+
top: scale(-3),
|
|
125
|
+
right: scale(-4),
|
|
126
|
+
// Adjust badge position for pill
|
|
127
|
+
...(variant === 'A' && isActive && { top: scale(3), right: scale(8) })
|
|
128
|
+
}
|
|
129
|
+
]}
|
|
130
|
+
/>
|
|
131
|
+
)}
|
|
132
|
+
</View>
|
|
133
|
+
|
|
134
|
+
<Typography
|
|
135
|
+
variant="smallHeavy"
|
|
136
|
+
style={[
|
|
137
|
+
styles.label,
|
|
138
|
+
{ color, fontSize: scaleText(9.5) },
|
|
139
|
+
variant === 'C' && { marginBottom: scale(4) }
|
|
140
|
+
]}
|
|
141
|
+
>
|
|
142
|
+
{item.label}
|
|
143
|
+
</Typography>
|
|
144
|
+
|
|
145
|
+
{/* Variante C: Dot abaixo da label */}
|
|
146
|
+
{variant === 'C' && isActive && (
|
|
147
|
+
<View style={[styles.dotC, { width: scale(4), height: scale(4), borderRadius: scale(2) }]} />
|
|
148
|
+
)}
|
|
149
|
+
</Animated.View>
|
|
150
|
+
</Pressable>
|
|
151
|
+
);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
export const TabBar = ({
|
|
155
|
+
items,
|
|
156
|
+
activeItemId,
|
|
157
|
+
onItemPress,
|
|
158
|
+
variant = 'B',
|
|
159
|
+
safeAreaBottom = 0,
|
|
160
|
+
style,
|
|
161
|
+
}: TabBarProps) => {
|
|
162
|
+
const { scale, scaleText, width } = useResponsive();
|
|
163
|
+
const isDesktop = width >= 768;
|
|
164
|
+
|
|
165
|
+
const iconSize = isDesktop ? scale(22) : scale(21);
|
|
166
|
+
const bottomPadding = Math.max(scale(14), safeAreaBottom);
|
|
167
|
+
|
|
168
|
+
return (
|
|
169
|
+
<View
|
|
170
|
+
style={[
|
|
171
|
+
styles.container,
|
|
172
|
+
{
|
|
173
|
+
paddingBottom: bottomPadding,
|
|
174
|
+
paddingHorizontal: scale(6),
|
|
175
|
+
},
|
|
176
|
+
style,
|
|
177
|
+
]}
|
|
178
|
+
>
|
|
179
|
+
{items.map((item) => (
|
|
180
|
+
<TabBarItemComponent
|
|
181
|
+
key={item.id}
|
|
182
|
+
item={item}
|
|
183
|
+
isActive={activeItemId === item.id}
|
|
184
|
+
variant={variant}
|
|
185
|
+
iconSize={iconSize}
|
|
186
|
+
onItemPress={onItemPress}
|
|
187
|
+
/>
|
|
188
|
+
))}
|
|
189
|
+
</View>
|
|
190
|
+
);
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const styles = StyleSheet.create({
|
|
194
|
+
container: {
|
|
195
|
+
flexDirection: 'row',
|
|
196
|
+
justifyContent: 'space-around',
|
|
197
|
+
alignItems: 'flex-start',
|
|
198
|
+
backgroundColor: theme.colors.white,
|
|
199
|
+
borderTopWidth: 1.5,
|
|
200
|
+
borderTopColor: theme.colors.brown[100],
|
|
201
|
+
width: '100%',
|
|
202
|
+
minHeight: 56,
|
|
203
|
+
},
|
|
204
|
+
itemContainer: {
|
|
205
|
+
flex: 1,
|
|
206
|
+
alignItems: 'center',
|
|
207
|
+
justifyContent: 'flex-start',
|
|
208
|
+
minWidth: 60,
|
|
209
|
+
},
|
|
210
|
+
itemInner: {
|
|
211
|
+
alignItems: 'center',
|
|
212
|
+
paddingTop: 9, // Espaço entre border-top e ícone (Variant B spec)
|
|
213
|
+
position: 'relative',
|
|
214
|
+
width: '100%',
|
|
215
|
+
},
|
|
216
|
+
iconWrapper: {
|
|
217
|
+
position: 'relative',
|
|
218
|
+
marginBottom: 4,
|
|
219
|
+
alignItems: 'center',
|
|
220
|
+
justifyContent: 'center',
|
|
221
|
+
},
|
|
222
|
+
pillA: {
|
|
223
|
+
backgroundColor: theme.colors.primaryLight,
|
|
224
|
+
},
|
|
225
|
+
indicatorB: {
|
|
226
|
+
position: 'absolute',
|
|
227
|
+
top: 0,
|
|
228
|
+
backgroundColor: theme.colors.primary,
|
|
229
|
+
borderBottomLeftRadius: 4,
|
|
230
|
+
borderBottomRightRadius: 4,
|
|
231
|
+
},
|
|
232
|
+
label: {
|
|
233
|
+
textAlign: 'center',
|
|
234
|
+
},
|
|
235
|
+
dotC: {
|
|
236
|
+
position: 'absolute',
|
|
237
|
+
bottom: -6,
|
|
238
|
+
backgroundColor: theme.colors.primary,
|
|
239
|
+
},
|
|
240
|
+
badge: {
|
|
241
|
+
position: 'absolute',
|
|
242
|
+
backgroundColor: theme.colors.error,
|
|
243
|
+
borderColor: theme.colors.white,
|
|
244
|
+
},
|
|
245
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -45,6 +45,7 @@ export * from './components/Inputs/TextInput/TextInput';
|
|
|
45
45
|
export * from './components/Mascots/Vava/Vava';
|
|
46
46
|
export * from './components/Mascots/Yaya/Yaya';
|
|
47
47
|
export * from './components/Navigation/BottomTabBar/BottomTabBar';
|
|
48
|
+
export * from './components/Navigation/TabBar/TabBar';
|
|
48
49
|
export * from './components/Navigation/PaginationDots/PaginationDots';
|
|
49
50
|
export * from './components/Navigation/ProgressMap/ProgressMap';
|
|
50
51
|
export * from './components/Navigation/WorldProgressMap/WorldProgressMap';
|
package/src/styles/theme.ts
CHANGED
|
@@ -172,86 +172,161 @@ export const theme = {
|
|
|
172
172
|
},
|
|
173
173
|
|
|
174
174
|
typography: {
|
|
175
|
+
// FAMÍLIA FREDOKA (Títulos e Destaques)
|
|
175
176
|
display1: {
|
|
176
177
|
...font('Fredoka-Bold', '700'),
|
|
177
178
|
fontSize: 28,
|
|
178
|
-
lineHeight:
|
|
179
|
-
desktopFontSize:
|
|
179
|
+
lineHeight: 28, // ajustado para line-height 1 se não especificado
|
|
180
|
+
desktopFontSize: 34,
|
|
180
181
|
},
|
|
181
182
|
display2: {
|
|
182
183
|
...font('Fredoka-Bold', '700'),
|
|
183
|
-
fontSize:
|
|
184
|
-
lineHeight:
|
|
185
|
-
desktopFontSize:
|
|
184
|
+
fontSize: 27,
|
|
185
|
+
lineHeight: 27,
|
|
186
|
+
desktopFontSize: 32,
|
|
186
187
|
},
|
|
187
188
|
h1: {
|
|
188
189
|
...font('Fredoka-Bold', '700'),
|
|
189
190
|
fontSize: 22,
|
|
190
|
-
lineHeight:
|
|
191
|
+
lineHeight: 22,
|
|
191
192
|
desktopFontSize: 28,
|
|
192
193
|
},
|
|
193
194
|
h2: {
|
|
194
|
-
...font('Fredoka-
|
|
195
|
-
fontSize:
|
|
196
|
-
lineHeight:
|
|
197
|
-
desktopFontSize:
|
|
195
|
+
...font('Fredoka-Bold', '700'),
|
|
196
|
+
fontSize: 20,
|
|
197
|
+
lineHeight: 20,
|
|
198
|
+
desktopFontSize: 24,
|
|
198
199
|
},
|
|
199
200
|
h3: {
|
|
200
201
|
...font('Fredoka-SemiBold', '600'),
|
|
201
|
-
fontSize:
|
|
202
|
+
fontSize: 20,
|
|
203
|
+
lineHeight: 24, // Sem line-height fixo no spec, 1.2
|
|
204
|
+
desktopFontSize: 24,
|
|
205
|
+
},
|
|
206
|
+
h4: {
|
|
207
|
+
...font('Fredoka-SemiBold', '600'),
|
|
208
|
+
fontSize: 19,
|
|
202
209
|
lineHeight: 22,
|
|
210
|
+
desktopFontSize: 22,
|
|
211
|
+
},
|
|
212
|
+
h5: {
|
|
213
|
+
...font('Fredoka-Bold', '700'),
|
|
214
|
+
fontSize: 18,
|
|
215
|
+
lineHeight: 22,
|
|
216
|
+
desktopFontSize: 20,
|
|
217
|
+
},
|
|
218
|
+
h6: {
|
|
219
|
+
...font('Fredoka-SemiBold', '600'),
|
|
220
|
+
fontSize: 17,
|
|
221
|
+
lineHeight: 18.7, // line-height 1.1 * 17
|
|
203
222
|
desktopFontSize: 19,
|
|
204
223
|
},
|
|
205
|
-
|
|
206
|
-
...font('
|
|
207
|
-
fontSize:
|
|
208
|
-
lineHeight:
|
|
209
|
-
|
|
210
|
-
desktopFontSize: 11,
|
|
224
|
+
subtitle1: {
|
|
225
|
+
...font('Fredoka-SemiBold', '600'),
|
|
226
|
+
fontSize: 16,
|
|
227
|
+
lineHeight: 16, // line-height 1
|
|
228
|
+
desktopFontSize: 18,
|
|
211
229
|
},
|
|
212
|
-
|
|
213
|
-
...font('
|
|
214
|
-
fontSize:
|
|
230
|
+
subtitle2: {
|
|
231
|
+
...font('Fredoka-Bold', '700'),
|
|
232
|
+
fontSize: 14,
|
|
233
|
+
lineHeight: 18,
|
|
234
|
+
desktopFontSize: 16,
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
// FAMÍLIA NUNITO (Corpo, Labels, Listas e Infos)
|
|
238
|
+
bodyLgHeavy: {
|
|
239
|
+
...font('Nunito-ExtraBold', '800'),
|
|
240
|
+
fontSize: 16,
|
|
215
241
|
lineHeight: 24,
|
|
216
|
-
desktopFontSize:
|
|
242
|
+
desktopFontSize: 18,
|
|
243
|
+
},
|
|
244
|
+
bodyMdHeavy: {
|
|
245
|
+
...font('Nunito-ExtraBold', '800'),
|
|
246
|
+
fontSize: 15,
|
|
247
|
+
lineHeight: 22,
|
|
248
|
+
desktopFontSize: 16,
|
|
217
249
|
},
|
|
218
250
|
bodyMd: {
|
|
219
|
-
...font('Nunito-
|
|
220
|
-
fontSize:
|
|
251
|
+
...font('Nunito-SemiBold', '600'),
|
|
252
|
+
fontSize: 15,
|
|
221
253
|
lineHeight: 22,
|
|
254
|
+
desktopFontSize: 16,
|
|
255
|
+
},
|
|
256
|
+
bodySmHeavy: {
|
|
257
|
+
...font('Nunito-ExtraBold', '800'),
|
|
258
|
+
fontSize: 13.5,
|
|
259
|
+
lineHeight: 20,
|
|
222
260
|
desktopFontSize: 15,
|
|
223
261
|
},
|
|
224
262
|
bodySm: {
|
|
225
|
-
...font('Nunito-
|
|
226
|
-
fontSize:
|
|
227
|
-
lineHeight:
|
|
228
|
-
desktopFontSize:
|
|
263
|
+
...font('Nunito-Bold', '700'),
|
|
264
|
+
fontSize: 13.5,
|
|
265
|
+
lineHeight: 20,
|
|
266
|
+
desktopFontSize: 15,
|
|
267
|
+
},
|
|
268
|
+
captionItalic: {
|
|
269
|
+
...font('Nunito-SemiBoldItalic', '600'),
|
|
270
|
+
fontSize: 12.5,
|
|
271
|
+
lineHeight: 18.75, // line-height 1.5 * 12.5
|
|
272
|
+
desktopFontSize: 14,
|
|
273
|
+
fontStyle: 'italic',
|
|
229
274
|
},
|
|
230
275
|
caption: {
|
|
276
|
+
...font('Nunito-Bold', '700'),
|
|
277
|
+
fontSize: 12.5,
|
|
278
|
+
lineHeight: 18,
|
|
279
|
+
desktopFontSize: 14,
|
|
280
|
+
},
|
|
281
|
+
labelHeavy: {
|
|
282
|
+
...font('Nunito-Black', '900'), // O spec diz 800, mas no Nunito "Black" (900) dá melhor leitura p/ labels, ou podemos usar ExtraBold (800). Vamos usar ExtraBold (800) como no spec:
|
|
283
|
+
// wait, refiz para respeitar o 800
|
|
231
284
|
...font('Nunito-ExtraBold', '800'),
|
|
232
|
-
fontSize:
|
|
285
|
+
fontSize: 11,
|
|
233
286
|
lineHeight: 16,
|
|
287
|
+
letterSpacing: 0.88, // 11 * 0.08em
|
|
288
|
+
textTransform: 'uppercase',
|
|
289
|
+
desktopFontSize: 12,
|
|
290
|
+
},
|
|
291
|
+
label: {
|
|
292
|
+
...font('Nunito-Bold', '700'),
|
|
293
|
+
fontSize: 11,
|
|
294
|
+
lineHeight: 16,
|
|
295
|
+
desktopFontSize: 12,
|
|
296
|
+
},
|
|
297
|
+
smallHeavy: {
|
|
298
|
+
...font('Nunito-ExtraBold', '800'),
|
|
299
|
+
fontSize: 10,
|
|
300
|
+
lineHeight: 14,
|
|
234
301
|
desktopFontSize: 11,
|
|
235
302
|
},
|
|
303
|
+
small: {
|
|
304
|
+
...font('Nunito-Bold', '700'),
|
|
305
|
+
fontSize: 10,
|
|
306
|
+
lineHeight: 14,
|
|
307
|
+
desktopFontSize: 11,
|
|
308
|
+
},
|
|
309
|
+
|
|
310
|
+
// Botões (mantemos originais porque são usados no MainButton)
|
|
236
311
|
btnLg: {
|
|
237
312
|
...font('Nunito-Black', '900'),
|
|
238
313
|
fontSize: 16,
|
|
239
314
|
lineHeight: 20,
|
|
240
|
-
letterSpacing: 0.64,
|
|
315
|
+
letterSpacing: 0.64,
|
|
241
316
|
desktopFontSize: 17,
|
|
242
317
|
},
|
|
243
318
|
btnMd: {
|
|
244
319
|
...font('Nunito-Black', '900'),
|
|
245
320
|
fontSize: 14,
|
|
246
321
|
lineHeight: 18,
|
|
247
|
-
letterSpacing: 0.56,
|
|
322
|
+
letterSpacing: 0.56,
|
|
248
323
|
desktopFontSize: 15,
|
|
249
324
|
},
|
|
250
325
|
btnSm: {
|
|
251
326
|
...font('Nunito-Black', '900'),
|
|
252
327
|
fontSize: 12,
|
|
253
328
|
lineHeight: 16,
|
|
254
|
-
letterSpacing: 0.48,
|
|
329
|
+
letterSpacing: 0.48,
|
|
255
330
|
desktopFontSize: 12,
|
|
256
331
|
},
|
|
257
332
|
},
|
package/src/utils/iconMap.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FileText } from 'lucide-react';
|
|
2
|
+
import { Play, Volume2, Volume1, ArrowRight, X, Check, ChevronRight, ChevronLeft, Info, AlertTriangle, Book, Mic, Home, Compass, GraduationCap, FolderOpen, User, ShoppingCart } from 'lucide-react-native';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Para evitar que o Vite e o Metro (React Native) tentem empacotar os 1400+ ícones
|
|
@@ -21,4 +22,11 @@ export const IconMap: Record<string, any> = {
|
|
|
21
22
|
AlertTriangle,
|
|
22
23
|
Book,
|
|
23
24
|
Mic,
|
|
25
|
+
Home,
|
|
26
|
+
Compass,
|
|
27
|
+
GraduationCap,
|
|
28
|
+
FolderOpen,
|
|
29
|
+
User,
|
|
30
|
+
ShoppingCart,
|
|
31
|
+
FileText,
|
|
24
32
|
};
|