@xetwa/design-system 1.0.37 → 1.0.39

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xetwa/design-system",
3
- "version": "1.0.37",
3
+ "version": "1.0.39",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -75,6 +75,19 @@ export const ActiveDark: Story = {
75
75
  subtitle: 'Regatear, números, comida',
76
76
  subtext: '• 3/5',
77
77
  tagLabel: 'AULA MARCADA',
78
+ onSchedulePress: () => console.log('Agendar Aula'),
79
+ onReviewPress: () => console.log('Rever'),
80
+ },
81
+ };
82
+
83
+ export const ExamMode: Story = {
84
+ args: {
85
+ status: 'active-dark',
86
+ title: 'No mercado de Dakar',
87
+ subtitle: 'Exame de módulo',
88
+ subtext: '• 3/5',
89
+ tagLabel: 'POR FAZER',
90
+ onExamPress: () => console.log('Fazer Exame'),
78
91
  },
79
92
  };
80
93
 
@@ -129,6 +142,16 @@ export const Grid: Story = {
129
142
  subtitle="Conversação e prática"
130
143
  subtext="• 4/4"
131
144
  tagLabel="AULA MARCADA"
145
+ onSchedulePress={() => {}}
146
+ onReviewPress={() => {}}
147
+ />
148
+ <ModuleCard
149
+ status="active-dark"
150
+ title="No mercado de Dakar"
151
+ subtitle="Exame de módulo"
152
+ subtext="• 3/5"
153
+ tagLabel="POR FAZER"
154
+ onExamPress={() => {}}
132
155
  />
133
156
  </View>
134
157
  ),
@@ -4,7 +4,7 @@ import { theme } from '../../../styles/theme';
4
4
  import { Tag } from '../../DataDisplay/Tag/Tag';
5
5
  import { ProgressBar } from '../../DataDisplay/ProgressBar/ProgressBar';
6
6
  import { Badge } from '../../DataDisplay/Badge/Badge';
7
- import { Lock, Check, GraduationCap, RotateCcw, CalendarPlus } from 'lucide-react-native';
7
+ import { Lock, Check, GraduationCap, RotateCcw, CalendarPlus, PenTool } from 'lucide-react-native';
8
8
  import { useResponsive } from '../../../hooks/useResponsive';
9
9
  import { Typography } from '../../Typography/Typography';
10
10
  import React, { useRef } from 'react';
@@ -16,7 +16,7 @@ export type ModuleCardStatus = 'active' | 'locked' | 'completed' | 'dark' | 'act
16
16
  *
17
17
  * Regras de Animação e Clique:
18
18
  * - `active`: O cartão como um todo tem animação de escala e é clicável via `onPress`.
19
- * - `active-dark`: O cartão é estático. Apenas os botões internos de "Agendar" (`onSchedulePress`) e "Rever" (`onReviewPress`) têm animação individual.
19
+ * - `active-dark`: O cartão é estático. Apenas os botões internos de "Agendar" (`onSchedulePress`), "Exame" (`onExamPress`) e "Rever" (`onReviewPress`) têm animação individual quando fornecidos.
20
20
  * - `completed`: O cartão é estático. Apenas o ícone/botão superior direito de "Rever" (`onReviewPress`) tem animação individual.
21
21
  * - `locked` / `dark`: O cartão não é interativo.
22
22
  */
@@ -47,6 +47,8 @@ export interface ModuleCardProps {
47
47
  style?: ViewStyle | ViewStyle[];
48
48
  /** Função de clique no botão de Agendar (modo active/dark). */
49
49
  onSchedulePress?: () => void;
50
+ /** Função de clique no botão principal de Exame (modo active/dark). */
51
+ onExamPress?: () => void;
50
52
  /** Função de clique no botão de Rever (modo completed/active/dark). */
51
53
  onReviewPress?: () => void;
52
54
  }
@@ -66,12 +68,14 @@ export const ModuleCard = ({
66
68
  onPress,
67
69
  style,
68
70
  onSchedulePress,
71
+ onExamPress,
69
72
  onReviewPress,
70
73
  }: ModuleCardProps) => {
71
74
  const { scale, scaleText, width } = useResponsive();
72
75
  const isMobileScreen = width < 400;
73
76
 
74
77
  const scheduleScale = useRef(new Animated.Value(1)).current;
78
+ const examScale = useRef(new Animated.Value(1)).current;
75
79
  const reviewScale = useRef(new Animated.Value(1)).current;
76
80
  const completedReviewScale = useRef(new Animated.Value(1)).current;
77
81
  const scaleAnim = useRef(new Animated.Value(1)).current;
@@ -226,18 +230,7 @@ export const ModuleCard = ({
226
230
 
227
231
  const subtitleVariant = size === 'xl' ? 'bodyLg' : size === 'lg' ? 'bodyMd' : 'bodySm';
228
232
 
229
- let displaySubtext = subtext;
230
- if (status === 'active-dark') {
231
- if (subtext) {
232
- if (subtext.trim().startsWith('•')) {
233
- displaySubtext = `Aula de revisão ${subtext.trim()}`;
234
- } else {
235
- displaySubtext = `Aula de revisão • ${subtext}`;
236
- }
237
- } else {
238
- displaySubtext = 'Aula de revisão';
239
- }
240
- }
233
+
241
234
 
242
235
  return (
243
236
  <Animated.View style={[{ transform: [{ scale: scaleAnim }] }, style]}>
@@ -276,7 +269,7 @@ export const ModuleCard = ({
276
269
  {title}
277
270
  </Typography>
278
271
 
279
- {(subtitle || subtext || displaySubtext) && (
272
+ {(subtitle || subtext) && (
280
273
  <View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginTop: scale(2) }}>
281
274
  <Typography variant={subtitleVariant} style={[
282
275
  styles.subtitle,
@@ -285,9 +278,9 @@ export const ModuleCard = ({
285
278
  flexShrink: 1, // Para o texto encolher se a tag ocupar espaço
286
279
  }
287
280
  ]}>
288
- {status !== 'active-dark' && subtitle}
289
- {status !== 'active-dark' && subtitle && displaySubtext && ' '}
290
- {displaySubtext && <Typography variant={subtitleVariant} style={styles.subtext}>{displaySubtext}</Typography>}
281
+ {subtitle}
282
+ {subtitle && subtext && ' '}
283
+ {subtext && <Typography variant={subtitleVariant} style={styles.subtext}>{subtext}</Typography>}
291
284
  </Typography>
292
285
  {tagLabel && (status === 'active-dark' || status === 'active') && (
293
286
  <Tag
@@ -340,31 +333,52 @@ export const ModuleCard = ({
340
333
 
341
334
  {status === 'active-dark' && (
342
335
  <View style={styles.compactDarkFooter}>
343
- <Animated.View style={{ transform: [{ scale: scheduleScale }] }}>
344
- <Pressable
345
- onPress={onSchedulePress || (() => {})}
346
- onPressIn={() => animateBtnIn(scheduleScale)}
347
- onPressOut={() => animateBtnOut(scheduleScale)}
348
- hitSlop={8}
349
- style={{ flexDirection: 'row', alignItems: 'center', backgroundColor: theme.colors.primary, paddingHorizontal: scale(14), paddingVertical: scale(6), borderRadius: 100 }}
350
- >
351
- <CalendarPlus stroke={theme.colors.white} strokeWidth={2.5} size={scale(14)} />
352
- <Typography variant="label" style={{ color: theme.colors.white, fontSize: scaleText(12), fontWeight: 'bold', marginLeft: scale(6) }}>Agendar Aula</Typography>
353
- </Pressable>
354
- </Animated.View>
336
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: scale(8) }}>
337
+ {onSchedulePress && (
338
+ <Animated.View style={{ transform: [{ scale: scheduleScale }] }}>
339
+ <Pressable
340
+ onPress={onSchedulePress}
341
+ onPressIn={() => animateBtnIn(scheduleScale)}
342
+ onPressOut={() => animateBtnOut(scheduleScale)}
343
+ hitSlop={8}
344
+ style={{ flexDirection: 'row', alignItems: 'center', backgroundColor: theme.colors.primary, paddingHorizontal: scale(14), paddingVertical: scale(6), borderRadius: 100 }}
345
+ >
346
+ <CalendarPlus stroke={theme.colors.white} strokeWidth={2.5} size={scale(14)} />
347
+ <Typography variant="label" style={{ color: theme.colors.white, fontSize: scaleText(12), fontWeight: 'bold', marginLeft: scale(6) }}>Agendar Aula</Typography>
348
+ </Pressable>
349
+ </Animated.View>
350
+ )}
355
351
 
356
- <Animated.View style={{ transform: [{ scale: reviewScale }] }}>
357
- <Pressable
358
- style={styles.compactReviewBtn}
359
- onPress={onReviewPress || (() => {})}
360
- onPressIn={() => animateBtnIn(reviewScale)}
361
- onPressOut={() => animateBtnOut(reviewScale)}
362
- hitSlop={8}
363
- >
364
- <RotateCcw stroke={theme.colors.primary} strokeWidth={2.5} size={scale(16)} />
365
- <Typography variant="label" style={[styles.reviewIconText, { color: theme.colors.primary, fontSize: scaleText(10), marginLeft: scale(4), marginTop: 0 }]}>REVER</Typography>
366
- </Pressable>
367
- </Animated.View>
352
+ {onExamPress && (
353
+ <Animated.View style={{ transform: [{ scale: examScale }] }}>
354
+ <Pressable
355
+ onPress={onExamPress}
356
+ onPressIn={() => animateBtnIn(examScale)}
357
+ onPressOut={() => animateBtnOut(examScale)}
358
+ hitSlop={8}
359
+ style={{ flexDirection: 'row', alignItems: 'center', backgroundColor: theme.colors.primary, paddingHorizontal: scale(14), paddingVertical: scale(6), borderRadius: 100 }}
360
+ >
361
+ <PenTool stroke={theme.colors.white} strokeWidth={2.5} size={scale(14)} />
362
+ <Typography variant="label" style={{ color: theme.colors.white, fontSize: scaleText(12), fontWeight: 'bold', marginLeft: scale(6) }}>Fazer Exame</Typography>
363
+ </Pressable>
364
+ </Animated.View>
365
+ )}
366
+ </View>
367
+
368
+ {onReviewPress && (
369
+ <Animated.View style={{ transform: [{ scale: reviewScale }] }}>
370
+ <Pressable
371
+ style={styles.compactReviewBtn}
372
+ onPress={onReviewPress}
373
+ onPressIn={() => animateBtnIn(reviewScale)}
374
+ onPressOut={() => animateBtnOut(reviewScale)}
375
+ hitSlop={8}
376
+ >
377
+ <RotateCcw stroke={theme.colors.primary} strokeWidth={2.5} size={scale(16)} />
378
+ <Typography variant="label" style={[styles.reviewIconText, { color: theme.colors.primary, fontSize: scaleText(10), marginLeft: scale(4), marginTop: 0 }]}>REVER</Typography>
379
+ </Pressable>
380
+ </Animated.View>
381
+ )}
368
382
  </View>
369
383
  )}
370
384