cdslibrary 1.0.56 → 1.1.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.
- package/assets/animations/animation.json +1 -0
- package/assets/animations/animationLoaderWhite.json +1 -0
- package/components/CDSBottomSheet.jsx +148 -69
- package/components/CDSButton.jsx +10 -11
- package/components/CDSButtonGroup.jsx +52 -0
- package/components/CDSCardFeedback.jsx +121 -0
- package/components/CDSInput.jsx +37 -30
- package/components/CDSLoader.jsx +80 -0
- package/components/CDSNavBar.jsx +81 -0
- package/components/CDSOnboarding.jsx +0 -1
- package/components/CDSSelect.jsx +133 -0
- package/components/CDSSplashScreen.jsx +81 -0
- package/components/CDSSwitch.jsx +2 -2
- package/components/onboardingItem.jsx +3 -3
- package/context/CDSThemeContext.js +60 -27
- package/index.js +8 -6
- package/package.json +17 -9
- package/tokens/CDSprimitiveTokens.js +64 -6
- package/tokens/CDSsemanticColors.js +8 -7
- package/tokens/CDSsemanticTextStyles.js +186 -198
- package/assets/onboarding/image1.png +0 -0
- package/assets/onboarding/image1D.png +0 -0
- package/assets/onboarding/image2.png +0 -0
- package/assets/onboarding/image2D.png +0 -0
- package/assets/onboarding/image3.png +0 -0
- package/assets/onboarding/image3D.png +0 -0
- package/assets/onboarding/slides.js +0 -22
|
@@ -4,7 +4,9 @@ import { CDSButton } from "./CDSButton";
|
|
|
4
4
|
|
|
5
5
|
import { MaterialIcons } from "@expo/vector-icons";
|
|
6
6
|
import { useTheme } from "../context/CDSThemeContext";
|
|
7
|
-
import {
|
|
7
|
+
import { ScrollView } from "react-native-web";
|
|
8
|
+
import { LinearGradient } from 'expo-linear-gradient';
|
|
9
|
+
|
|
8
10
|
|
|
9
11
|
const { height, width } = Dimensions.get("window");
|
|
10
12
|
|
|
@@ -14,82 +16,119 @@ export const CDSBottomSheet = ({
|
|
|
14
16
|
type,
|
|
15
17
|
isVisible,
|
|
16
18
|
hasClose,
|
|
17
|
-
title,
|
|
18
|
-
description
|
|
19
|
+
title = 'title',
|
|
20
|
+
description,
|
|
19
21
|
customSlot,
|
|
20
|
-
primaryButtonLabel,
|
|
22
|
+
primaryButtonLabel = "Aceptar",
|
|
21
23
|
primaryButtonOnPress,
|
|
22
24
|
secondaryButtonLabel = "Cancelar",
|
|
23
25
|
onFinish,
|
|
24
26
|
}) => {
|
|
25
27
|
|
|
26
|
-
const [childHeight, setChildHeight] = useState(0);
|
|
27
28
|
const handleLayout = (event) => {
|
|
28
29
|
const { width } = event.nativeEvent.layout;
|
|
29
30
|
setChildHeight(width); // Guardamos el ancho en el estado
|
|
30
|
-
};
|
|
31
|
+
};
|
|
31
32
|
|
|
32
33
|
const { theme } = useTheme();
|
|
33
|
-
const textStyles = getSemanticTextStyles(theme);
|
|
34
34
|
|
|
35
35
|
const [modalVisible, setModalVisible] = useState(isVisible);
|
|
36
36
|
const [modalOpacity] = useState(new Animated.Value(0));
|
|
37
37
|
const [slidePosition] = useState(new Animated.Value(height));
|
|
38
38
|
|
|
39
39
|
useEffect(() => {
|
|
40
|
+
const targetValue = isMobile ? height : width; // Valor fuera de pantalla
|
|
41
|
+
|
|
40
42
|
Animated.parallel([
|
|
41
43
|
Animated.timing(modalOpacity, {
|
|
42
44
|
toValue: modalVisible ? 1 : 0,
|
|
43
|
-
duration:
|
|
45
|
+
duration: 400,
|
|
44
46
|
useNativeDriver: false,
|
|
45
47
|
}),
|
|
46
48
|
Animated.timing(slidePosition, {
|
|
47
|
-
toValue: modalVisible ? 0 :
|
|
48
|
-
duration:
|
|
49
|
+
toValue: modalVisible ? 0 : targetValue, // 0 es visible, targetValue es oculto
|
|
50
|
+
duration: 400,
|
|
49
51
|
useNativeDriver: false,
|
|
50
52
|
}),
|
|
51
53
|
]).start();
|
|
52
|
-
}, [modalVisible]);
|
|
53
|
-
|
|
54
|
+
}, [modalVisible, isMobile]); // Añadimos isMobile a las dependencias
|
|
54
55
|
return (
|
|
55
56
|
<>
|
|
56
|
-
<Animated.View
|
|
57
|
-
|
|
58
|
-
{
|
|
59
|
-
|
|
57
|
+
<Animated.View
|
|
58
|
+
onLayout={handleLayout}
|
|
59
|
+
style={[
|
|
60
|
+
isMobile ? styles.container.typeBottomSheet : styles.container.typeDrawer,
|
|
61
|
+
{
|
|
62
|
+
opacity: modalOpacity,
|
|
63
|
+
transform: [
|
|
64
|
+
isMobile
|
|
65
|
+
? { translateY: slidePosition } // Sube en móvil
|
|
66
|
+
: { translateX: slidePosition } // Entra lateral en desktop
|
|
67
|
+
],
|
|
68
|
+
backgroundColor: theme.surface.neutral.primary, paddingHorizontal: theme.space.sm,
|
|
69
|
+
paddingTop: theme.space.xl,
|
|
70
|
+
gap: theme.space.sm,
|
|
71
|
+
}, isMobile ? [styles.container.typeBottomSheet, {
|
|
72
|
+
borderTopLeftRadius: theme.radius.lg,
|
|
73
|
+
borderTopRightRadius: theme.radius.lg,
|
|
74
|
+
|
|
75
|
+
}] : [styles.container.typeDrawer, {
|
|
76
|
+
borderBottomLeftRadius: theme.radius.lg, borderTopLeftRadius: theme.radius.lg, paddingBottom: theme.space.md,
|
|
77
|
+
},
|
|
78
|
+
]]}
|
|
60
79
|
>
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
80
|
+
|
|
81
|
+
{hasClose && (
|
|
82
|
+
<MaterialIcons
|
|
83
|
+
name={"close"}
|
|
84
|
+
size={theme.typography.icon.lg}
|
|
85
|
+
color={theme.text.neutral.primary}
|
|
86
|
+
onPress={() => setModalVisible(false)}
|
|
87
|
+
style={{ position: "absolute", right: theme.space.sm, top: theme.space.sm }}
|
|
88
|
+
/>
|
|
89
|
+
)}
|
|
90
|
+
{title && <Text style={theme.typography.bold.lg}>{title}</Text>}
|
|
91
|
+
<View style={styles.scrollWrapper}> {/* 👈 Nuevo View para envolver */}
|
|
92
|
+
<ScrollView
|
|
93
|
+
style={styles.scrollArea}
|
|
94
|
+
contentContainerStyle={[styles.scrollContent, {
|
|
95
|
+
flexGrow: 0,
|
|
96
|
+
paddingBottom: theme.space.md
|
|
97
|
+
}]}
|
|
98
|
+
showsVerticalScrollIndicator={true}
|
|
99
|
+
persistentScrollbar={true}
|
|
100
|
+
indicatorStyle={theme.name === 'dark' ? 'white' : 'black'}
|
|
101
|
+
>
|
|
102
|
+
{description && (
|
|
103
|
+
<Text style={[theme.typography.regular.md, { marginBottom: theme.space.md }]}>{description}</Text>
|
|
104
|
+
)}
|
|
105
|
+
{customSlot && <View style={styles.customSlot}>{customSlot}</View>}
|
|
106
|
+
</ScrollView>
|
|
107
|
+
|
|
108
|
+
{/* El Gradiente */}
|
|
109
|
+
<LinearGradient
|
|
110
|
+
colors={['transparent', theme.surface.neutral.primary]} // Ajusta los colores
|
|
111
|
+
style={styles.fadeGradient}
|
|
112
|
+
pointerEvents="none" // Importante para que no bloquee el click en los botones debajo
|
|
113
|
+
/>
|
|
92
114
|
</View>
|
|
115
|
+
{type !== "informative" && (
|
|
116
|
+
<View style={isMobile ? styles.actionsContainer.typeBottomSheet : styles.actionsContainer.typeDrawer}>
|
|
117
|
+
<CDSButton
|
|
118
|
+
label={primaryButtonLabel}
|
|
119
|
+
onPress={
|
|
120
|
+
onFinish
|
|
121
|
+
? () => setModalVisible(!modalVisible)
|
|
122
|
+
: primaryButtonOnPress
|
|
123
|
+
}
|
|
124
|
+
/>
|
|
125
|
+
<CDSButton
|
|
126
|
+
label={secondaryButtonLabel}
|
|
127
|
+
type="ghost"
|
|
128
|
+
onPress={() => setModalVisible(!modalVisible)}
|
|
129
|
+
/>
|
|
130
|
+
</View>
|
|
131
|
+
)}
|
|
93
132
|
</Animated.View>
|
|
94
133
|
<Animated.View
|
|
95
134
|
style={[
|
|
@@ -111,51 +150,91 @@ const styles = StyleSheet.create({
|
|
|
111
150
|
position: "absolute",
|
|
112
151
|
top: 0,
|
|
113
152
|
left: 0,
|
|
114
|
-
width: "
|
|
115
|
-
height: "
|
|
116
|
-
zIndex: 9,
|
|
153
|
+
width: "100%",
|
|
154
|
+
height: "100%"
|
|
117
155
|
},
|
|
118
156
|
|
|
119
|
-
|
|
157
|
+
container: {
|
|
120
158
|
typeBottomSheet: {
|
|
159
|
+
maxHeight: '90%',
|
|
121
160
|
flex: 1,
|
|
122
161
|
justifyContent: "flex-end",
|
|
123
162
|
alignItems: "center",
|
|
124
163
|
zIndex: 99,
|
|
125
|
-
pointerEvents: "none",
|
|
126
164
|
width: "100%",
|
|
165
|
+
position: "absolute",
|
|
166
|
+
bottom: 0,
|
|
127
167
|
},
|
|
128
|
-
|
|
168
|
+
typeDrawer: {
|
|
169
|
+
position: "absolute", // Clave para el Drawer
|
|
170
|
+
right: 0, // Anclado a la derecha
|
|
171
|
+
top: 0,
|
|
172
|
+
bottom: 0,
|
|
129
173
|
justifyContent: "center",
|
|
130
|
-
alignItems: "center",
|
|
131
174
|
zIndex: 99,
|
|
175
|
+
width: 600,
|
|
132
176
|
},
|
|
133
177
|
},
|
|
134
|
-
|
|
178
|
+
|
|
179
|
+
scrollWrapper: {
|
|
180
|
+
flex: 1, // Ocupa todo el espacio vertical disponible
|
|
181
|
+
position: 'relative', // Para que el gradiente se posicione absolutamente dentro de él
|
|
182
|
+
width: '100%',
|
|
183
|
+
},
|
|
184
|
+
scrollArea: {
|
|
185
|
+
flexShrink: 1,
|
|
186
|
+
flexGrow: 1,
|
|
187
|
+
width: '100%',
|
|
188
|
+
paddingBottom: 20,
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
scrollContent: {
|
|
192
|
+
paddingBottom: 20, // Aire al final del contenido
|
|
193
|
+
gap: 16,
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
fadeGradient: {
|
|
197
|
+
position: 'absolute',
|
|
198
|
+
bottom: 0,
|
|
199
|
+
left: 0,
|
|
200
|
+
right: 0,
|
|
201
|
+
height: 60, // Ajusta la altura del gradiente según tu diseño
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
infoContainer: {
|
|
135
205
|
typeBottomSheet: {
|
|
136
206
|
width: "100%",
|
|
137
|
-
borderTopLeftRadius: 16,
|
|
138
|
-
borderTopRightRadius: 16,
|
|
139
|
-
paddingHorizontal: 16,
|
|
140
|
-
paddingVertical: 24,
|
|
141
|
-
gap: 16,
|
|
142
207
|
zIndex: 100,
|
|
143
208
|
pointerEvents: "all",
|
|
144
209
|
},
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
paddingHorizontal: 16,
|
|
149
|
-
paddingVertical: 24,
|
|
150
|
-
gap: 16,
|
|
210
|
+
typeDrawer: {
|
|
211
|
+
flex: 1, // Ocupa todo el alto disponible en el Drawer
|
|
212
|
+
width: "100%",
|
|
151
213
|
zIndex: 100,
|
|
152
214
|
pointerEvents: "all",
|
|
215
|
+
// Opcional: una sombra elegante para Desktop
|
|
216
|
+
...Platform.select({
|
|
217
|
+
web: { boxShadow: '-10px 0px 15px rgba(0,0,0,0.2)' }
|
|
218
|
+
})
|
|
153
219
|
},
|
|
154
220
|
},
|
|
155
221
|
actionsContainer: {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
222
|
+
typeBottomSheet: {
|
|
223
|
+
flex: "row",
|
|
224
|
+
width: "100%",
|
|
225
|
+
gap: 8,
|
|
226
|
+
},
|
|
227
|
+
typeDrawer: {
|
|
228
|
+
marginTop: 'auto',
|
|
229
|
+
flexDirection: "row-reverse",
|
|
230
|
+
width: "100%",
|
|
231
|
+
gap: 8,
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
|
|
235
|
+
customSlot: {
|
|
236
|
+
width: '100%',
|
|
237
|
+
height: 'auto',
|
|
159
238
|
},
|
|
160
239
|
|
|
161
240
|
});
|
package/components/CDSButton.jsx
CHANGED
|
@@ -3,7 +3,6 @@ import { Text, StyleSheet, TouchableOpacity } from "react-native";
|
|
|
3
3
|
import { Dimensions, Platform } from "react-native";
|
|
4
4
|
import { MaterialIcons } from "@expo/vector-icons";
|
|
5
5
|
import { useTheme } from "../context/CDSThemeContext";
|
|
6
|
-
import { getSemanticTextStyles } from "../tokens/CDSsemanticTextStyles";
|
|
7
6
|
|
|
8
7
|
const { width } = Dimensions.get("window");
|
|
9
8
|
const isMobile = width <= 768
|
|
@@ -11,13 +10,14 @@ const isMobile = width <= 768
|
|
|
11
10
|
export const CDSButton = ({
|
|
12
11
|
label,
|
|
13
12
|
type = "primary",
|
|
13
|
+
variant,
|
|
14
14
|
size,
|
|
15
15
|
onPress,
|
|
16
16
|
icon,
|
|
17
17
|
flexend,
|
|
18
18
|
}) => {
|
|
19
19
|
const { theme } = useTheme();
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
|
|
22
22
|
const backgroundColor =
|
|
23
23
|
type === "disabled"
|
|
@@ -38,11 +38,11 @@ export const CDSButton = ({
|
|
|
38
38
|
onPress={type !== "disabled" ? onPress : null}
|
|
39
39
|
activeOpacity={0.7}
|
|
40
40
|
style={[
|
|
41
|
-
styles.container,
|
|
42
|
-
{ backgroundColor },
|
|
41
|
+
styles.container,
|
|
42
|
+
{ flexGrow: (variant === "fill" ? 1 : 0), backgroundColor, borderRadius: theme.radius.full },
|
|
43
43
|
flexend && { justifyContent: "flex-end", paddingHorizontal: 0 },
|
|
44
44
|
{
|
|
45
|
-
paddingHorizontal: type === "icon" ?
|
|
45
|
+
paddingHorizontal: type === "icon" ? theme.space.xs : theme.space.sm,
|
|
46
46
|
...(isMobile && { width: "100%" })
|
|
47
47
|
},
|
|
48
48
|
type === "secondary" && {
|
|
@@ -52,7 +52,6 @@ export const CDSButton = ({
|
|
|
52
52
|
type === 'link' && {
|
|
53
53
|
paddingHorizontal: 0,
|
|
54
54
|
paddingVertical: 0,
|
|
55
|
-
width: 'auto',
|
|
56
55
|
minWidth: 1,
|
|
57
56
|
}
|
|
58
57
|
]}
|
|
@@ -62,9 +61,9 @@ export const CDSButton = ({
|
|
|
62
61
|
style={[
|
|
63
62
|
type === "link"
|
|
64
63
|
? size === "small"
|
|
65
|
-
?
|
|
66
|
-
:
|
|
67
|
-
:
|
|
64
|
+
? theme.typography.link.small
|
|
65
|
+
: theme.typography.link.regular
|
|
66
|
+
: theme.typography.buttonText,
|
|
68
67
|
{ color: textColor },
|
|
69
68
|
]}
|
|
70
69
|
>
|
|
@@ -74,7 +73,7 @@ export const CDSButton = ({
|
|
|
74
73
|
{icon && (
|
|
75
74
|
<MaterialIcons
|
|
76
75
|
name={icon}
|
|
77
|
-
size={
|
|
76
|
+
size={theme.typography.icon.sm}
|
|
78
77
|
color={textColor}
|
|
79
78
|
/>
|
|
80
79
|
)}
|
|
@@ -84,7 +83,7 @@ export const CDSButton = ({
|
|
|
84
83
|
|
|
85
84
|
const styles = StyleSheet.create({
|
|
86
85
|
container: {
|
|
87
|
-
|
|
86
|
+
alignSelf: "center",
|
|
88
87
|
flexDirection: "row",
|
|
89
88
|
alignItems: "center",
|
|
90
89
|
justifyContent: "center",
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
|
|
2
|
+
import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
|
|
3
|
+
import { Dimensions, Platform } from "react-native";
|
|
4
|
+
import { MaterialIcons } from "@expo/vector-icons";
|
|
5
|
+
import { useTheme } from "../context/CDSThemeContext";
|
|
6
|
+
import { CDSButton } from "./CDSButton";
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
import React, { useState } from "react";
|
|
10
|
+
|
|
11
|
+
export const CDSButtonGroup = ({ options, onSelect, label='Label' }) => {
|
|
12
|
+
const { theme } = useTheme();
|
|
13
|
+
const [selectedIndex, setSelectedIndex] = useState(null);
|
|
14
|
+
|
|
15
|
+
const handlePress = (index, label) => {
|
|
16
|
+
setSelectedIndex(index);
|
|
17
|
+
if (onSelect) onSelect(label);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<View style={[styles.container, { gap: theme.space.xs }]}>
|
|
22
|
+
<Text style={[theme.typography.label]}>{label}</Text>
|
|
23
|
+
<View style={[styles.actionsContainer, { gap: theme.space.xs }]}>
|
|
24
|
+
{options.map((option, index) => (
|
|
25
|
+
<CDSButton key={index} variant={option.variant}
|
|
26
|
+
label={option.label}
|
|
27
|
+
// Si está seleccionado es 'primary', si no 'secondary'
|
|
28
|
+
type={selectedIndex === index ? "primary" : "secondary"}
|
|
29
|
+
isActive={selectedIndex === index}
|
|
30
|
+
onPress={() => handlePress(index, option.label)}
|
|
31
|
+
/>
|
|
32
|
+
|
|
33
|
+
))}
|
|
34
|
+
</View>
|
|
35
|
+
</View>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const styles = StyleSheet.create({
|
|
40
|
+
container:{
|
|
41
|
+
width: '100%',
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
actionsContainer: {
|
|
45
|
+
flex: 1,
|
|
46
|
+
flexDirection: 'row',
|
|
47
|
+
width: '100%',
|
|
48
|
+
backgroundColor: 'red',
|
|
49
|
+
alignItems: 'center',
|
|
50
|
+
justifyContent: 'center',
|
|
51
|
+
},
|
|
52
|
+
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { useState, useRef } from "react";
|
|
2
|
+
import { View, Text, StyleSheet, TouchableOpacity, Platform, Animated } from "react-native";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
import { MaterialIcons } from "@expo/vector-icons";
|
|
6
|
+
import { useTheme } from "../context/CDSThemeContext";
|
|
7
|
+
|
|
8
|
+
export const CDSCardFeedback = ({
|
|
9
|
+
|
|
10
|
+
style,
|
|
11
|
+
title,
|
|
12
|
+
description,
|
|
13
|
+
secondAction,
|
|
14
|
+
onPressSecondAction,
|
|
15
|
+
onClose }) => {
|
|
16
|
+
const { theme } = useTheme();
|
|
17
|
+
const [isVisible, setIsVisible] = useState(true); // 3. Estado inicial: visible
|
|
18
|
+
const fadeAnim = useRef(new Animated.Value(1)).current;
|
|
19
|
+
// 4. Si isVisible es falso, no renderizamos nada (devolvemos null)
|
|
20
|
+
|
|
21
|
+
const handleClose = () => {
|
|
22
|
+
// Iniciar la animación de desvanecimiento
|
|
23
|
+
Animated.timing(fadeAnim, {
|
|
24
|
+
toValue: 0,
|
|
25
|
+
duration: 300,
|
|
26
|
+
useNativeDriver: Platform.OS !== 'web'
|
|
27
|
+
}).start(() => {
|
|
28
|
+
// Una vez que la animación termina, ocultar el componente
|
|
29
|
+
setIsVisible(false);
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
if (!isVisible) return null;
|
|
33
|
+
|
|
34
|
+
const feedbackStyles = {
|
|
35
|
+
success: {
|
|
36
|
+
bg: theme.surface.feedback.success,
|
|
37
|
+
border: theme.outline.feedback.success,
|
|
38
|
+
text: theme.text.feedback.success,
|
|
39
|
+
icon: 'check-circle',
|
|
40
|
+
},
|
|
41
|
+
warning: {
|
|
42
|
+
bg: theme.surface.feedback.warning,
|
|
43
|
+
border: theme.outline.feedback.warning,
|
|
44
|
+
text: theme.text.feedback.warning,
|
|
45
|
+
icon: 'warning',
|
|
46
|
+
},
|
|
47
|
+
error: {
|
|
48
|
+
bg: theme.surface.feedback.error,
|
|
49
|
+
border: theme.outline.feedback.error,
|
|
50
|
+
text: theme.text.feedback.error,
|
|
51
|
+
icon: 'error',
|
|
52
|
+
},
|
|
53
|
+
neutral: {
|
|
54
|
+
bg: theme.surface.neutral.primaryVariant,
|
|
55
|
+
border: theme.outline.tertiary,
|
|
56
|
+
text: theme.text.neutral.primary,
|
|
57
|
+
icon: 'lightbulb',
|
|
58
|
+
},
|
|
59
|
+
info: {
|
|
60
|
+
bg: theme.surface.feedback.info,
|
|
61
|
+
border: theme.outline.feedback.info,
|
|
62
|
+
text: theme.text.feedback.info,
|
|
63
|
+
icon: 'info',
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const currentStyle = feedbackStyles[style] || feedbackStyles.info;
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<Animated.View style={[styles.container, {
|
|
70
|
+
backgroundColor: currentStyle.bg,
|
|
71
|
+
borderColor: currentStyle.border,
|
|
72
|
+
borderRadius: theme.radius.md,
|
|
73
|
+
padding: theme.space.sm,
|
|
74
|
+
gap: theme.space.xs,
|
|
75
|
+
opacity: fadeAnim,
|
|
76
|
+
transform: [{
|
|
77
|
+
scale: fadeAnim.interpolate({
|
|
78
|
+
inputRange: [0, 1],
|
|
79
|
+
outputRange: [0.95, 1] // Se encoge ligeramente al 95%
|
|
80
|
+
})
|
|
81
|
+
}]
|
|
82
|
+
}]}>
|
|
83
|
+
|
|
84
|
+
<View style={[styles.titleContainer, { gap: theme.space.xs }]}>
|
|
85
|
+
<MaterialIcons
|
|
86
|
+
name={currentStyle.icon}
|
|
87
|
+
size={theme.typography.icon.md}
|
|
88
|
+
color={currentStyle.text}
|
|
89
|
+
/>
|
|
90
|
+
<Text style={[styles.title, theme.typography.bold.lg, { color: currentStyle.text }]}>{title || 'Title'}</Text>
|
|
91
|
+
</View>
|
|
92
|
+
{description && <Text style={[styles.description, theme.typography.regular.md, { color: currentStyle.text }]}>{description}</Text>}
|
|
93
|
+
<View style={[styles.actionsContainer, { gap: theme.space.xs }]}>
|
|
94
|
+
{secondAction && <TouchableOpacity onPress={onPressSecondAction}>{<Text style={[theme.typography.bold.sm, { color: currentStyle.text }]}>{secondAction}</Text>}</TouchableOpacity>}
|
|
95
|
+
<TouchableOpacity onPress={() => {
|
|
96
|
+
handleClose()
|
|
97
|
+
}}>{<Text style={[theme.typography.bold.sm, { color: currentStyle.text }]}>{'Cerrar'}</Text>}</TouchableOpacity></View>
|
|
98
|
+
</Animated.View>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const styles = StyleSheet.create({
|
|
103
|
+
container: {
|
|
104
|
+
width: '100%',
|
|
105
|
+
justifyContent: 'space-between',
|
|
106
|
+
borderWidth: 1,
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
titleContainer: {
|
|
110
|
+
flexDirection: 'row',
|
|
111
|
+
alignItems: 'center',
|
|
112
|
+
},
|
|
113
|
+
description: {
|
|
114
|
+
},
|
|
115
|
+
actionsContainer: {
|
|
116
|
+
flexDirection: 'row',
|
|
117
|
+
width: '100%',
|
|
118
|
+
justifyContent: 'flex-end',
|
|
119
|
+
|
|
120
|
+
},
|
|
121
|
+
});
|
package/components/CDSInput.jsx
CHANGED
|
@@ -1,54 +1,61 @@
|
|
|
1
|
-
import
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { View, Text, TextInput, StyleSheet } from "react-native";
|
|
2
3
|
import { useTheme } from "../context/CDSThemeContext";
|
|
3
|
-
import {getSemanticTextStyles} from "../tokens/CDSsemanticTextStyles";
|
|
4
|
-
import { useState } from "react";
|
|
5
4
|
|
|
6
5
|
export const CDSInput = ({ label, type, keyboard, placeholder }) => {
|
|
7
6
|
const { theme } = useTheme();
|
|
8
|
-
const textStyles = getSemanticTextStyles(theme);
|
|
9
7
|
const [text, setText] = useState("");
|
|
8
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
10
9
|
|
|
11
10
|
return (
|
|
12
|
-
<View style={[styles.container]}>
|
|
13
|
-
|
|
14
|
-
style={[
|
|
15
|
-
|
|
16
|
-
]}
|
|
17
|
-
>
|
|
18
|
-
{label}
|
|
19
|
-
</Text>
|
|
20
|
-
{text === "" && (
|
|
21
|
-
<Text style={[styles.fakePlaceholder, textStyles.inputText.placeholder]}>
|
|
22
|
-
{placeholder}
|
|
11
|
+
<View style={[styles.container, { gap: theme.space.xs }]}>
|
|
12
|
+
{label && (
|
|
13
|
+
<Text style={[theme.typography.label, { color: theme.text.neutral.primary }]}>
|
|
14
|
+
{label}
|
|
23
15
|
</Text>
|
|
24
16
|
)}
|
|
17
|
+
|
|
25
18
|
<TextInput
|
|
26
|
-
style={[
|
|
19
|
+
style={[
|
|
20
|
+
styles.textBox,
|
|
21
|
+
theme.typography.inputText.value, // La tipografía se aplica aquí y la hereda el placeholder
|
|
22
|
+
{
|
|
23
|
+
backgroundColor: theme.surface.neutral.primary,
|
|
24
|
+
// Cambia el color del borde si está en focus
|
|
25
|
+
borderColor: isFocused
|
|
26
|
+
? theme.outline.neutral.focus : text ? theme.outline.neutral.tertiaryVariant
|
|
27
|
+
: theme.outline.neutral.primary,
|
|
28
|
+
borderRadius: theme.radius.sm,
|
|
29
|
+
paddingHorizontal: theme.space.xs,
|
|
30
|
+
color: theme.text.neutral.primary,
|
|
31
|
+
// Elimina el borde azul en Web/Android
|
|
32
|
+
outlineStyle: 'none',
|
|
33
|
+
},
|
|
34
|
+
]}
|
|
35
|
+
placeholder={placeholder}
|
|
36
|
+
placeholderTextColor={theme.typography.inputText.placeholder}
|
|
27
37
|
keyboardType={keyboard}
|
|
28
|
-
secureTextEntry={type === "password"
|
|
38
|
+
secureTextEntry={type === "password"}
|
|
29
39
|
onChangeText={setText}
|
|
30
40
|
value={text}
|
|
41
|
+
// Manejo de estados de Focus
|
|
42
|
+
onFocus={() => setIsFocused(true)}
|
|
43
|
+
onBlur={() => setIsFocused(false)}
|
|
44
|
+
underlineColorAndroid="transparent"
|
|
31
45
|
/>
|
|
32
46
|
</View>
|
|
33
47
|
);
|
|
34
|
-
}
|
|
48
|
+
};
|
|
35
49
|
|
|
36
50
|
const styles = StyleSheet.create({
|
|
37
51
|
container: {
|
|
38
52
|
width: "100%",
|
|
39
|
-
gap: 8,
|
|
40
|
-
},
|
|
41
|
-
|
|
42
|
-
fakePlaceholder: {
|
|
43
|
-
position: "absolute",
|
|
44
|
-
zIndex: 1,
|
|
45
|
-
left: 14,
|
|
46
|
-
top: 40,
|
|
47
53
|
},
|
|
48
|
-
|
|
49
54
|
textBox: {
|
|
50
|
-
padding: 12,
|
|
51
|
-
borderRadius: 8,
|
|
52
55
|
borderWidth: 1,
|
|
56
|
+
height: 48,
|
|
57
|
+
// Asegura que la transición de color no sea brusca en web
|
|
58
|
+
transitionProperty: 'border-color',
|
|
59
|
+
transitionDuration: '0.2s',
|
|
53
60
|
},
|
|
54
|
-
});
|
|
61
|
+
});
|