@xetwa/design-system 1.0.33 → 1.0.35
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/ModuleCard/ModuleCard.stories.tsx +35 -1
- package/src/components/Cards/ModuleCard/ModuleCard.tsx +19 -11
- package/src/components/DataDisplay/ProgressBar/ProgressBar.stories.tsx +9 -0
- package/src/components/DataDisplay/ProgressBar/ProgressBar.tsx +27 -2
- package/src/components/DataDisplay/StatBadge/StatBadge.tsx +3 -3
package/package.json
CHANGED
|
@@ -24,6 +24,7 @@ const meta = {
|
|
|
24
24
|
tagLabel: { control: 'text' },
|
|
25
25
|
progress: { control: 'number' },
|
|
26
26
|
segments: { control: 'number' },
|
|
27
|
+
size: { control: 'radio', options: ['md', 'lg', 'xl'] },
|
|
27
28
|
},
|
|
28
29
|
decorators: [
|
|
29
30
|
(Story) => (
|
|
@@ -94,7 +95,6 @@ export const Grid: Story = {
|
|
|
94
95
|
/>
|
|
95
96
|
<ModuleCard
|
|
96
97
|
status="locked"
|
|
97
|
-
number={3}
|
|
98
98
|
title="A minha família"
|
|
99
99
|
subtitle="5 lições"
|
|
100
100
|
/>
|
|
@@ -106,3 +106,37 @@ export const Grid: Story = {
|
|
|
106
106
|
</View>
|
|
107
107
|
),
|
|
108
108
|
};
|
|
109
|
+
|
|
110
|
+
export const Sizes: Story = {
|
|
111
|
+
args: {
|
|
112
|
+
status: 'active',
|
|
113
|
+
number: 1,
|
|
114
|
+
title: 'Tamanhos do Card',
|
|
115
|
+
subtitle: 'Compara os tamanhos disponíveis',
|
|
116
|
+
},
|
|
117
|
+
render: () => (
|
|
118
|
+
<View style={{ gap: 16, width: '100%' }}>
|
|
119
|
+
<ModuleCard
|
|
120
|
+
status="active"
|
|
121
|
+
number={1}
|
|
122
|
+
title="Tamanho Médio (md)"
|
|
123
|
+
subtitle="O padrão para a maioria das listas"
|
|
124
|
+
size="md"
|
|
125
|
+
/>
|
|
126
|
+
<ModuleCard
|
|
127
|
+
status="active"
|
|
128
|
+
number={2}
|
|
129
|
+
title="Tamanho Grande (lg)"
|
|
130
|
+
subtitle="Destaque moderado para ecrãs com espaço"
|
|
131
|
+
size="lg"
|
|
132
|
+
/>
|
|
133
|
+
<ModuleCard
|
|
134
|
+
status="active"
|
|
135
|
+
number={3}
|
|
136
|
+
title="Tamanho Extra (xl)"
|
|
137
|
+
subtitle="Máxima legibilidade e presença"
|
|
138
|
+
size="xl"
|
|
139
|
+
/>
|
|
140
|
+
</View>
|
|
141
|
+
),
|
|
142
|
+
};
|
|
@@ -30,6 +30,8 @@ export interface ModuleCardProps {
|
|
|
30
30
|
progress?: number;
|
|
31
31
|
/** Número de segmentos na barra de progresso. */
|
|
32
32
|
segments?: number;
|
|
33
|
+
/** Tamanho do cartão (controla paddings, ícones e fontes). */
|
|
34
|
+
size?: 'md' | 'lg' | 'xl';
|
|
33
35
|
onPress?: () => void;
|
|
34
36
|
/** Cor customizada para o título. */
|
|
35
37
|
titleColor?: string;
|
|
@@ -47,6 +49,7 @@ export const ModuleCard = ({
|
|
|
47
49
|
tagLabel,
|
|
48
50
|
progress,
|
|
49
51
|
segments,
|
|
52
|
+
size = 'md',
|
|
50
53
|
titleColor,
|
|
51
54
|
subtitleColor,
|
|
52
55
|
onPress,
|
|
@@ -112,7 +115,7 @@ export const ModuleCard = ({
|
|
|
112
115
|
} : {};
|
|
113
116
|
|
|
114
117
|
const renderLeftIconBox = () => {
|
|
115
|
-
const bSize = isMobileScreen ? 'xl' : 'xxl';
|
|
118
|
+
const bSize = size === 'xl' ? 'xxxl' : size === 'lg' ? 'xxl' : isMobileScreen ? 'xl' : 'xxl';
|
|
116
119
|
if (status === 'locked') {
|
|
117
120
|
return (
|
|
118
121
|
<Badge
|
|
@@ -133,8 +136,8 @@ export const ModuleCard = ({
|
|
|
133
136
|
);
|
|
134
137
|
}
|
|
135
138
|
if (status === 'dark') {
|
|
136
|
-
const boxSize = isMobileScreen ? scale(34) : scale(44);
|
|
137
|
-
const capSize = isMobileScreen ? scale(18) : scale(24);
|
|
139
|
+
const boxSize = size === 'xl' ? scale(54) : size === 'lg' ? scale(48) : isMobileScreen ? scale(34) : scale(44);
|
|
140
|
+
const capSize = size === 'xl' ? scale(30) : size === 'lg' ? scale(28) : isMobileScreen ? scale(18) : scale(24);
|
|
138
141
|
return (
|
|
139
142
|
<View style={[
|
|
140
143
|
styles.darkIconBox,
|
|
@@ -148,16 +151,21 @@ export const ModuleCard = ({
|
|
|
148
151
|
</View>
|
|
149
152
|
);
|
|
150
153
|
}
|
|
154
|
+
|
|
151
155
|
return (
|
|
152
156
|
<Badge
|
|
153
157
|
variant="primary"
|
|
154
|
-
size={bSize}
|
|
158
|
+
size={bSize as any}
|
|
155
159
|
content={number}
|
|
156
160
|
/>
|
|
157
161
|
);
|
|
158
162
|
};
|
|
159
163
|
|
|
160
|
-
const colGap = isMobileScreen ? scale(8) : scale(12);
|
|
164
|
+
const colGap = size === 'xl' ? scale(16) : size === 'lg' ? scale(14) : isMobileScreen ? scale(8) : scale(12);
|
|
165
|
+
const cardPaddingV = size === 'xl' ? scale(20) : size === 'lg' ? scale(16) : isMobileScreen ? scale(10) : scale(14);
|
|
166
|
+
const cardPaddingH = size === 'xl' ? scale(20) : size === 'lg' ? scale(18) : isMobileScreen ? scale(12) : scale(15);
|
|
167
|
+
const titleFontSize = size === 'xl' ? scaleText(18) : size === 'lg' ? scaleText(16) : isMobileScreen ? scaleText(14) : scaleText(16);
|
|
168
|
+
const subtitleFontSize = size === 'xl' ? scaleText(14) : size === 'lg' ? scaleText(13) : isMobileScreen ? scaleText(10) : scaleText(12);
|
|
161
169
|
|
|
162
170
|
return (
|
|
163
171
|
<Pressable
|
|
@@ -166,8 +174,8 @@ export const ModuleCard = ({
|
|
|
166
174
|
containerStyle,
|
|
167
175
|
status === 'active' && activeShadow,
|
|
168
176
|
{
|
|
169
|
-
paddingVertical:
|
|
170
|
-
paddingHorizontal:
|
|
177
|
+
paddingVertical: cardPaddingV,
|
|
178
|
+
paddingHorizontal: cardPaddingH,
|
|
171
179
|
borderRadius: scale(18),
|
|
172
180
|
},
|
|
173
181
|
style,
|
|
@@ -181,11 +189,11 @@ export const ModuleCard = ({
|
|
|
181
189
|
</View>
|
|
182
190
|
|
|
183
191
|
<View style={[styles.content, { marginLeft: colGap }]}>
|
|
184
|
-
<Typography style={[
|
|
192
|
+
<Typography variant="h2" style={[
|
|
185
193
|
styles.title,
|
|
186
194
|
{
|
|
187
195
|
color: titleStyleColor,
|
|
188
|
-
fontSize:
|
|
196
|
+
fontSize: titleFontSize,
|
|
189
197
|
marginBottom: scale(2),
|
|
190
198
|
}
|
|
191
199
|
]}>
|
|
@@ -193,11 +201,11 @@ export const ModuleCard = ({
|
|
|
193
201
|
</Typography>
|
|
194
202
|
|
|
195
203
|
{(subtitle || subtext) && (
|
|
196
|
-
<Typography style={[
|
|
204
|
+
<Typography variant="bodyMd" style={[
|
|
197
205
|
styles.subtitle,
|
|
198
206
|
{
|
|
199
207
|
color: subtitleStyleColor,
|
|
200
|
-
fontSize:
|
|
208
|
+
fontSize: subtitleFontSize,
|
|
201
209
|
}
|
|
202
210
|
]}>
|
|
203
211
|
{subtitle}
|
|
@@ -58,6 +58,15 @@ export const Success: Story = {
|
|
|
58
58
|
},
|
|
59
59
|
};
|
|
60
60
|
|
|
61
|
+
export const WithLabels: Story = {
|
|
62
|
+
args: {
|
|
63
|
+
progress: 0.82,
|
|
64
|
+
size: 'sm',
|
|
65
|
+
labelTopLeft: 'Compreensão',
|
|
66
|
+
labelTopRight: '82%',
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
61
70
|
export const Sizes: Story = {
|
|
62
71
|
args: { progress: 0 },
|
|
63
72
|
render: () => (
|
|
@@ -2,6 +2,7 @@ import { useEffect, useState } from 'react';
|
|
|
2
2
|
import { View, StyleSheet, Animated, Easing } from 'react-native';
|
|
3
3
|
import type { ViewStyle } from 'react-native';
|
|
4
4
|
import { theme } from '../../../styles/theme';
|
|
5
|
+
import { Typography } from '../../Typography/Typography';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Barra de progresso para exibir a percentagem de conclusão de uma tarefa ou módulo.
|
|
@@ -21,6 +22,10 @@ export interface ProgressBarProps {
|
|
|
21
22
|
fillColor?: string;
|
|
22
23
|
/** Cor customizada forçada do trilho vazio */
|
|
23
24
|
trackColor?: string;
|
|
25
|
+
/** Label apresentada no canto superior esquerdo */
|
|
26
|
+
labelTopLeft?: string;
|
|
27
|
+
/** Label apresentada no canto superior direito (ex: percentagem) */
|
|
28
|
+
labelTopRight?: string;
|
|
24
29
|
style?: ViewStyle | ViewStyle[];
|
|
25
30
|
}
|
|
26
31
|
|
|
@@ -31,6 +36,8 @@ export const ProgressBar = ({
|
|
|
31
36
|
segments = 1,
|
|
32
37
|
fillColor,
|
|
33
38
|
trackColor,
|
|
39
|
+
labelTopLeft,
|
|
40
|
+
labelTopRight,
|
|
34
41
|
style,
|
|
35
42
|
}: ProgressBarProps) => {
|
|
36
43
|
const [animatedProgress] = useState(() => new Animated.Value(0));
|
|
@@ -60,8 +67,19 @@ export const ProgressBar = ({
|
|
|
60
67
|
const segmentRange = 1 / safeSegments;
|
|
61
68
|
|
|
62
69
|
return (
|
|
63
|
-
<View style={
|
|
64
|
-
{
|
|
70
|
+
<View style={style}>
|
|
71
|
+
{(labelTopLeft || labelTopRight) && (
|
|
72
|
+
<View style={styles.labelsWrapper}>
|
|
73
|
+
{labelTopLeft ? (
|
|
74
|
+
<Typography variant="btnSm">{labelTopLeft}</Typography>
|
|
75
|
+
) : <View />}
|
|
76
|
+
{labelTopRight ? (
|
|
77
|
+
<Typography variant="btnSm" color={defaultFillColor}>{labelTopRight}</Typography>
|
|
78
|
+
) : <View />}
|
|
79
|
+
</View>
|
|
80
|
+
)}
|
|
81
|
+
<View style={styles.container}>
|
|
82
|
+
{segmentsArray.map((_, index) => {
|
|
65
83
|
const start = index * segmentRange;
|
|
66
84
|
const end = (index + 1) * segmentRange;
|
|
67
85
|
|
|
@@ -92,11 +110,18 @@ export const ProgressBar = ({
|
|
|
92
110
|
</View>
|
|
93
111
|
);
|
|
94
112
|
})}
|
|
113
|
+
</View>
|
|
95
114
|
</View>
|
|
96
115
|
);
|
|
97
116
|
};
|
|
98
117
|
|
|
99
118
|
const styles = StyleSheet.create({
|
|
119
|
+
labelsWrapper: {
|
|
120
|
+
flexDirection: 'row',
|
|
121
|
+
justifyContent: 'space-between',
|
|
122
|
+
alignItems: 'center',
|
|
123
|
+
marginBottom: 6,
|
|
124
|
+
},
|
|
100
125
|
container: {
|
|
101
126
|
flexDirection: 'row',
|
|
102
127
|
width: '100%',
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { useEffect, useRef } from 'react';
|
|
2
|
-
import { View, StyleSheet, Animated } from 'react-native';
|
|
3
|
-
import { theme } from '../../../styles/theme';
|
|
4
1
|
import { Flame, Heart } from 'lucide-react-native';
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
|
+
import { Animated, StyleSheet, View } from 'react-native';
|
|
5
4
|
import { useResponsive } from '../../../hooks/useResponsive';
|
|
5
|
+
import { theme } from '../../../styles/theme';
|
|
6
6
|
import { Typography } from '../../Typography/Typography';
|
|
7
7
|
|
|
8
8
|
export type StatBadgeVariant = 'streak' | 'xp' | 'lives';
|