cdslibrary 1.2.47 → 1.2.49
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/components/CDSBottomSheet.jsx +1 -1
- package/components/CDSButton.jsx +2 -4
- package/components/CDSCardFeedback.jsx +84 -78
- package/components/CDSImageButton.jsx +8 -5
- package/components/CDSImageButtonGroup.jsx +60 -13
- package/components/CDSNavBar.jsx +5 -8
- package/components/CDSOffer.jsx +3 -5
- package/components/CDSSelect.jsx +40 -31
- package/package.json +1 -1
- package/tokens/CDSsemanticTextStyles.js +1 -3
|
@@ -6,7 +6,6 @@ import { useTheme } from "../context/CDSThemeContext";
|
|
|
6
6
|
import { LinearGradient } from 'expo-linear-gradient';
|
|
7
7
|
|
|
8
8
|
const { height, width } = Dimensions.get("window");
|
|
9
|
-
const isMobile = width <= 878;
|
|
10
9
|
|
|
11
10
|
const bottomSheetRender = ({
|
|
12
11
|
type,
|
|
@@ -21,6 +20,7 @@ const bottomSheetRender = ({
|
|
|
21
20
|
onFinish,
|
|
22
21
|
}, ref) => {
|
|
23
22
|
const { theme } = useTheme();
|
|
23
|
+
const isMobile = theme.isMobile
|
|
24
24
|
const [modalVisible, setModalVisible] = useState(isVisible);
|
|
25
25
|
const [modalOpacity] = useState(new Animated.Value(0));
|
|
26
26
|
const [slidePosition] = useState(new Animated.Value(isMobile ? height : width));
|
package/components/CDSButton.jsx
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Text, StyleSheet, TouchableOpacity } from "react-native";
|
|
3
|
-
import { Dimensions, Platform } from "react-native";
|
|
4
3
|
import { MaterialIcons } from "@expo/vector-icons";
|
|
5
4
|
import { useTheme } from "../context/CDSThemeContext";
|
|
6
5
|
|
|
7
|
-
const { width } = Dimensions.get("window");
|
|
8
|
-
const isMobile = width <= 768
|
|
9
6
|
|
|
10
7
|
export const CDSButton = ({
|
|
11
8
|
label,
|
|
@@ -17,7 +14,8 @@ export const CDSButton = ({
|
|
|
17
14
|
flexend,
|
|
18
15
|
}) => {
|
|
19
16
|
const { theme } = useTheme();
|
|
20
|
-
|
|
17
|
+
const isMobile = theme.isMobile
|
|
18
|
+
|
|
21
19
|
const backgroundColor =
|
|
22
20
|
type === "disabled"
|
|
23
21
|
? theme.surface.action.disabled
|
|
@@ -1,121 +1,127 @@
|
|
|
1
|
-
import { useState, useRef } from "react";
|
|
1
|
+
import React, { useState, useRef } from "react";
|
|
2
2
|
import { View, Text, StyleSheet, TouchableOpacity, Platform, Animated } from "react-native";
|
|
3
|
-
|
|
4
|
-
|
|
5
3
|
import { MaterialIcons } from "@expo/vector-icons";
|
|
6
4
|
import { useTheme } from "../context/CDSThemeContext";
|
|
7
5
|
|
|
8
6
|
export const CDSCardFeedback = ({
|
|
9
|
-
|
|
10
7
|
style,
|
|
11
8
|
title,
|
|
12
9
|
description,
|
|
13
10
|
secondAction,
|
|
14
11
|
onPressSecondAction,
|
|
15
|
-
onClose
|
|
12
|
+
onClose
|
|
13
|
+
}) => {
|
|
16
14
|
const { theme } = useTheme();
|
|
17
|
-
const [isVisible, setIsVisible] = useState(true);
|
|
15
|
+
const [isVisible, setIsVisible] = useState(true);
|
|
18
16
|
const fadeAnim = useRef(new Animated.Value(1)).current;
|
|
19
|
-
// 4. Si isVisible es falso, no renderizamos nada (devolvemos null)
|
|
20
17
|
|
|
21
18
|
const handleClose = () => {
|
|
22
|
-
//
|
|
19
|
+
// IMPORTANTE: useNativeDriver debe ser false para animar maxHeight y padding
|
|
23
20
|
Animated.timing(fadeAnim, {
|
|
24
21
|
toValue: 0,
|
|
25
22
|
duration: 300,
|
|
26
|
-
useNativeDriver:
|
|
23
|
+
useNativeDriver: false
|
|
27
24
|
}).start(() => {
|
|
28
|
-
// Una vez que la animación termina, ocultar el componente
|
|
29
25
|
setIsVisible(false);
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
if (onClose) onClose();
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|
|
32
30
|
if (!isVisible) return null;
|
|
33
31
|
|
|
34
32
|
const feedbackStyles = {
|
|
35
|
-
success: {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
}
|
|
33
|
+
success: { bg: theme.surface.feedback.success, border: theme.outline.feedback.success, text: theme.text.feedback.success, icon: 'check-circle' },
|
|
34
|
+
warning: { bg: theme.surface.feedback.warning, border: theme.outline.feedback.warning, text: theme.text.feedback.warning, icon: 'warning' },
|
|
35
|
+
error: { bg: theme.surface.feedback.error, border: theme.outline.feedback.error, text: theme.text.feedback.error, icon: 'error' },
|
|
36
|
+
neutral: { bg: theme.surface.neutral.primaryVariant, border: theme.outline.tertiary, text: theme.text.neutral.primary, icon: 'lightbulb' },
|
|
37
|
+
info: { bg: theme.surface.feedback.info, border: theme.outline.feedback.info, text: theme.text.feedback.info, icon: 'info' }
|
|
38
|
+
};
|
|
39
|
+
|
|
66
40
|
const currentStyle = feedbackStyles[style] || feedbackStyles.info;
|
|
67
41
|
|
|
42
|
+
// Interpolamos todos los valores físicos para que colapsen a 0
|
|
43
|
+
const animatedLayout = {
|
|
44
|
+
maxHeight: fadeAnim.interpolate({
|
|
45
|
+
inputRange: [0, 1],
|
|
46
|
+
outputRange: [0, 600] // Suficiente para el contenido
|
|
47
|
+
}),
|
|
48
|
+
opacity: fadeAnim,
|
|
49
|
+
// Animamos el padding y el margen para que no queden espacios vacíos al cerrar
|
|
50
|
+
paddingVertical: fadeAnim.interpolate({
|
|
51
|
+
inputRange: [0, 1],
|
|
52
|
+
outputRange: [0, theme.space.md]
|
|
53
|
+
}),
|
|
54
|
+
marginBottom: fadeAnim.interpolate({
|
|
55
|
+
inputRange: [0, 1],
|
|
56
|
+
outputRange: [0, theme.space.sm]
|
|
57
|
+
}),
|
|
58
|
+
borderWidth: 1,
|
|
59
|
+
|
|
60
|
+
};
|
|
61
|
+
|
|
68
62
|
return (
|
|
69
|
-
<Animated.View style={[
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
63
|
+
<Animated.View style={[
|
|
64
|
+
styles.container,
|
|
65
|
+
animatedLayout,
|
|
66
|
+
{
|
|
67
|
+
backgroundColor: currentStyle.bg,
|
|
68
|
+
borderColor: currentStyle.border,
|
|
69
|
+
borderRadius: theme.radius.md,
|
|
70
|
+
paddingHorizontal: theme.space.md,
|
|
71
|
+
gap: theme.space.sm,
|
|
72
|
+
overflow: 'hidden', // Evita que el texto se salga mientras se achica
|
|
73
|
+
}
|
|
74
|
+
]}>
|
|
75
|
+
{title && (
|
|
76
|
+
<View style={[styles.titleContainer, { gap: theme.space.sm }]}>
|
|
77
|
+
<MaterialIcons
|
|
78
|
+
name={currentStyle.icon}
|
|
79
|
+
size={theme.typography.icon.md}
|
|
80
|
+
color={currentStyle.text}
|
|
81
|
+
/>
|
|
82
|
+
<Text style={[styles.title, theme.typography.bold.lg, { color: currentStyle.text }]}>
|
|
83
|
+
{title}
|
|
84
|
+
</Text>
|
|
85
|
+
</View>
|
|
86
|
+
)}
|
|
87
|
+
|
|
88
|
+
{description && (
|
|
89
|
+
<Text style={[styles.description, theme.typography.regular.md, { color: currentStyle.text }]}>
|
|
90
|
+
{description}
|
|
91
|
+
</Text>
|
|
92
|
+
)}
|
|
83
93
|
|
|
84
|
-
{title && <View style={[styles.titleContainer, { gap: theme.space.sm }]}>
|
|
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}</Text>
|
|
91
|
-
</View>}
|
|
92
|
-
{description && <Text style={[styles.description, theme.typography.regular.md, { color: currentStyle.text }]}>{description}</Text>}
|
|
93
94
|
<View style={[styles.actionsContainer, { gap: theme.space.sm }]}>
|
|
94
|
-
{secondAction &&
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
{secondAction && (
|
|
96
|
+
<TouchableOpacity onPress={onPressSecondAction}>
|
|
97
|
+
<Text style={[theme.typography.bold.sm, { color: currentStyle.text }]}>
|
|
98
|
+
{secondAction}
|
|
99
|
+
</Text>
|
|
100
|
+
</TouchableOpacity>
|
|
101
|
+
)}
|
|
102
|
+
<TouchableOpacity onPress={handleClose}>
|
|
103
|
+
<Text style={[theme.typography.bold.sm, { color: currentStyle.text }]}>
|
|
104
|
+
{'Cerrar'}
|
|
105
|
+
</Text>
|
|
106
|
+
</TouchableOpacity>
|
|
107
|
+
</View>
|
|
98
108
|
</Animated.View>
|
|
99
|
-
)
|
|
100
|
-
}
|
|
109
|
+
);
|
|
110
|
+
};
|
|
101
111
|
|
|
102
112
|
const styles = StyleSheet.create({
|
|
103
113
|
container: {
|
|
104
114
|
width: '100%',
|
|
105
115
|
justifyContent: 'space-between',
|
|
106
|
-
borderWidth: 1,
|
|
107
116
|
},
|
|
108
|
-
|
|
109
117
|
titleContainer: {
|
|
110
118
|
flexDirection: 'row',
|
|
111
119
|
alignItems: 'center',
|
|
112
120
|
},
|
|
113
|
-
description: {
|
|
114
|
-
},
|
|
121
|
+
description: {},
|
|
115
122
|
actionsContainer: {
|
|
116
123
|
flexDirection: 'row',
|
|
117
124
|
width: '100%',
|
|
118
125
|
justifyContent: 'flex-end',
|
|
119
|
-
|
|
120
126
|
},
|
|
121
|
-
});
|
|
127
|
+
});
|
|
@@ -8,6 +8,7 @@ import { useTheme } from "../context/CDSThemeContext";
|
|
|
8
8
|
export const CDSImageButton = ({ object, isActive, onPress }) => { // 👈 Añadimos isActive
|
|
9
9
|
const { theme } = useTheme();
|
|
10
10
|
|
|
11
|
+
console.log(theme.typography)
|
|
11
12
|
return (
|
|
12
13
|
<TouchableOpacity
|
|
13
14
|
onPress={onPress}
|
|
@@ -18,7 +19,7 @@ export const CDSImageButton = ({ object, isActive, onPress }) => { // 👈 Añad
|
|
|
18
19
|
? theme.surface.neutral.primaryVariant // Color cuando está presionado
|
|
19
20
|
: theme.surface.neutral.primary,
|
|
20
21
|
gap: theme.space.sm,
|
|
21
|
-
padding: theme.space.
|
|
22
|
+
padding: theme.space.md,
|
|
22
23
|
// borderColor: isActive
|
|
23
24
|
// ? theme.outline.neutral.primary // Borde de color de marca si está activo
|
|
24
25
|
// : theme.outline.neutral.primary,
|
|
@@ -34,11 +35,12 @@ export const CDSImageButton = ({ object, isActive, onPress }) => { // 👈 Añad
|
|
|
34
35
|
source={object.image}
|
|
35
36
|
resizeMode="cover"
|
|
36
37
|
style={{
|
|
38
|
+
flexGrow: 1,
|
|
37
39
|
width: "100%",
|
|
38
|
-
height:
|
|
40
|
+
height: 80,
|
|
39
41
|
}}
|
|
40
42
|
/>
|
|
41
|
-
<Text style={
|
|
43
|
+
<Text style={isActive ? theme.typography.semiBold.sm : theme.typography.regular.sm }>
|
|
42
44
|
{object.label}
|
|
43
45
|
</Text>
|
|
44
46
|
</TouchableOpacity>
|
|
@@ -52,8 +54,9 @@ const styles = StyleSheet.create ({
|
|
|
52
54
|
mainContainer:{
|
|
53
55
|
alignItems: 'center',
|
|
54
56
|
flexGrow: 1,
|
|
55
|
-
maxWidth:
|
|
56
|
-
|
|
57
|
+
maxWidth: 160,
|
|
58
|
+
minWidth: 120,
|
|
59
|
+
height: 'auto'
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
})
|
|
@@ -1,28 +1,61 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import React, { useState, useRef, useEffect } from 'react';
|
|
2
|
+
import { View, Animated, StyleSheet, ScrollView } from 'react-native';
|
|
2
3
|
import { CDSImageButton } from './CDSImageButton';
|
|
3
|
-
import {
|
|
4
|
-
|
|
4
|
+
import { useTheme } from "../context/CDSThemeContext";
|
|
5
5
|
|
|
6
6
|
export const CDSImageButtonGroup = ({ array, onSelect }) => {
|
|
7
|
+
const { theme } = useTheme();
|
|
7
8
|
const [selectedId, setSelectedId] = useState(null);
|
|
9
|
+
const animatedValue = useRef(new Animated.Value(0)).current;
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
Animated.timing(animatedValue, {
|
|
13
|
+
toValue: 1,
|
|
14
|
+
duration: 400,
|
|
15
|
+
useNativeDriver: false
|
|
16
|
+
}).start();
|
|
17
|
+
}, []);
|
|
8
18
|
|
|
9
19
|
const handlePress = (id) => {
|
|
10
20
|
const nextId = id === selectedId ? null : id;
|
|
11
21
|
setSelectedId(nextId);
|
|
12
|
-
|
|
13
|
-
// Avisamos al padre qué objeto se seleccionó (o null si se deseleccionó)
|
|
14
22
|
if (onSelect) {
|
|
15
23
|
const selectedItem = array.find(item => item.id === nextId);
|
|
16
24
|
onSelect(selectedItem || null);
|
|
17
25
|
}
|
|
18
26
|
};
|
|
19
27
|
|
|
28
|
+
const animatedLayout = {
|
|
29
|
+
maxHeight: animatedValue.interpolate({
|
|
30
|
+
inputRange: [0, 1],
|
|
31
|
+
outputRange: [0, 250]
|
|
32
|
+
}),
|
|
33
|
+
opacity: animatedValue,
|
|
34
|
+
marginTop: animatedValue.interpolate({
|
|
35
|
+
inputRange: [0, 1],
|
|
36
|
+
outputRange: [0, theme.space.md]
|
|
37
|
+
})
|
|
38
|
+
};
|
|
39
|
+
|
|
20
40
|
return (
|
|
21
|
-
<View style={
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
41
|
+
<Animated.View style={[
|
|
42
|
+
animatedLayout,
|
|
43
|
+
{ width: '100%' } // Asegura que el contenedor ocupe todo el ancho disponible
|
|
44
|
+
]}>
|
|
45
|
+
<ScrollView
|
|
46
|
+
horizontal={true}
|
|
47
|
+
showsHorizontalScrollIndicator={false}
|
|
48
|
+
// IMPORTANTE: overflow visible para que no corte sombras,
|
|
49
|
+
// pero necesitamos que el ScrollView sepa su límite
|
|
50
|
+
style={styles.scrollView}
|
|
51
|
+
contentContainerStyle={[
|
|
52
|
+
styles.scrollContent,
|
|
53
|
+
{
|
|
54
|
+
gap: theme.space.sm,
|
|
55
|
+
paddingHorizontal: theme.space.md,
|
|
56
|
+
}
|
|
57
|
+
]}
|
|
58
|
+
>
|
|
26
59
|
{array.map((item) => (
|
|
27
60
|
<CDSImageButton
|
|
28
61
|
key={item.id}
|
|
@@ -31,7 +64,21 @@ export const CDSImageButtonGroup = ({ array, onSelect }) => {
|
|
|
31
64
|
onPress={() => handlePress(item.id)}
|
|
32
65
|
/>
|
|
33
66
|
))}
|
|
34
|
-
</
|
|
35
|
-
</View>
|
|
67
|
+
</ScrollView>
|
|
68
|
+
</Animated.View>
|
|
36
69
|
);
|
|
37
|
-
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const styles = StyleSheet.create({
|
|
73
|
+
scrollView: {
|
|
74
|
+
width: '100%',
|
|
75
|
+
overflow: 'visible'
|
|
76
|
+
},
|
|
77
|
+
scrollContent: {
|
|
78
|
+
// Quitamos el justifyContent: 'center' porque impide el scroll si hay pocos elementos
|
|
79
|
+
flexDirection: 'row',
|
|
80
|
+
alignItems: 'center',
|
|
81
|
+
paddingVertical: 10,
|
|
82
|
+
overflow: 'visible'
|
|
83
|
+
}
|
|
84
|
+
});
|
package/components/CDSNavBar.jsx
CHANGED
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
|
|
2
|
-
import { View, Text,
|
|
2
|
+
import { View, Text, StyleSheet, Image, TouchableOpacity } from "react-native";
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
import { MaterialIcons } from "@expo/vector-icons";
|
|
6
6
|
import { useTheme } from "../context/CDSThemeContext";
|
|
7
|
-
import { getSemanticTextStyles } from "../tokens/CDSsemanticTextStyles";
|
|
8
7
|
import { useNavigation } from "@react-navigation/native";
|
|
9
8
|
|
|
10
|
-
const { height, width } = Dimensions.get("window");
|
|
11
|
-
|
|
12
|
-
const isMobile = width <= 878;
|
|
13
9
|
export const CDSNavBar = ({
|
|
14
|
-
|
|
10
|
+
|
|
15
11
|
type,
|
|
16
12
|
isExpanded,
|
|
17
13
|
hasClose,
|
|
18
14
|
title,
|
|
19
15
|
hasBack, }) => {
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
|
|
17
|
+
const { theme, isDarkMode } = useTheme();
|
|
18
|
+
const isMobile = theme.isMobile
|
|
22
19
|
|
|
23
20
|
const navigation = useNavigation();
|
|
24
21
|
|
package/components/CDSOffer.jsx
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
|
|
2
2
|
import { View, Text, StyleSheet, } from "react-native";
|
|
3
3
|
import { useTheme } from "../context/CDSThemeContext";
|
|
4
|
-
import { Dimensions } from "react-native";
|
|
5
4
|
|
|
6
5
|
|
|
7
|
-
const { width } = Dimensions.get("window");
|
|
8
|
-
const isMobile = width <= 768
|
|
9
6
|
|
|
10
7
|
export const CDSOffer = ({ description, minValue, maxValue }) => {
|
|
11
|
-
|
|
8
|
+
|
|
12
9
|
const { theme } = useTheme();
|
|
13
|
-
|
|
10
|
+
const isMobile = theme.isMobile
|
|
11
|
+
|
|
14
12
|
return (
|
|
15
13
|
<View style={[styles.mainContainer, { gap: theme.space.md }]}>
|
|
16
14
|
<Text style={theme.typography.regular.md}>Por tu</Text>
|
package/components/CDSSelect.jsx
CHANGED
|
@@ -13,7 +13,6 @@ const SelectOption = ({ item, isSelected, onSelect, theme }) => {
|
|
|
13
13
|
return (
|
|
14
14
|
<Pressable
|
|
15
15
|
onPress={() => onSelect(item)}
|
|
16
|
-
// Eventos de Hover para Web
|
|
17
16
|
onMouseEnter={() => Platform.OS === 'web' && setIsHovered(true)}
|
|
18
17
|
onMouseLeave={() => Platform.OS === 'web' && setIsHovered(false)}
|
|
19
18
|
style={({ pressed }) => [
|
|
@@ -45,12 +44,8 @@ const SelectOption = ({ item, isSelected, onSelect, theme }) => {
|
|
|
45
44
|
export const CDSSelect = ({ label, options = [], onSelect, selectedValue = null }) => {
|
|
46
45
|
const { theme } = useTheme();
|
|
47
46
|
const [isOpen, setIsOpen] = useState(false);
|
|
48
|
-
const [dropdownTop, setDropdownTop] = useState(0);
|
|
49
|
-
const [dropdownWidth, setDropdownWidth] = useState(0);
|
|
50
|
-
const [dropdownLeft, setDropdownLeft] = useState(0);
|
|
51
|
-
|
|
52
|
-
const containerRef = useRef(null);
|
|
53
47
|
const animatedValue = useRef(new Animated.Value(0)).current;
|
|
48
|
+
const containerRef = useRef(null);
|
|
54
49
|
|
|
55
50
|
const [selectedItem, setSelectedItem] = useState(
|
|
56
51
|
options.find(opt => opt.value === selectedValue) || null
|
|
@@ -63,36 +58,49 @@ export const CDSSelect = ({ label, options = [], onSelect, selectedValue = null
|
|
|
63
58
|
|
|
64
59
|
const toggleDropdown = () => {
|
|
65
60
|
if (isOpen) {
|
|
66
|
-
Animated.timing(animatedValue, {
|
|
67
|
-
|
|
61
|
+
Animated.timing(animatedValue, {
|
|
62
|
+
toValue: 0,
|
|
63
|
+
duration: 250,
|
|
64
|
+
useNativeDriver: false
|
|
65
|
+
}).start(() => setIsOpen(false));
|
|
68
66
|
} else {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
67
|
+
setIsOpen(true);
|
|
68
|
+
requestAnimationFrame(() => {
|
|
69
|
+
Animated.timing(animatedValue, {
|
|
70
|
+
toValue: 1,
|
|
71
|
+
duration: 300,
|
|
72
|
+
useNativeDriver: false
|
|
73
|
+
}).start();
|
|
75
74
|
});
|
|
76
75
|
}
|
|
77
76
|
};
|
|
78
77
|
|
|
79
|
-
const handleSelect = (item) => {
|
|
80
|
-
// 1. Actualizamos localmente PRIMERO para que el cambio sea instantáneo
|
|
78
|
+
const handleSelect = (item) => {
|
|
81
79
|
setSelectedItem(item);
|
|
82
|
-
|
|
83
|
-
// 2. Cerramos el menú
|
|
84
80
|
toggleDropdown();
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
81
|
+
if (onSelect) onSelect(item.value);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// Estructura idéntica a CardFeedback para mantener consistencia
|
|
85
|
+
const animatedLayout = {
|
|
86
|
+
maxHeight: animatedValue.interpolate({
|
|
87
|
+
inputRange: [0, 1],
|
|
88
|
+
outputRange: [0, 240]
|
|
89
|
+
}),
|
|
90
|
+
opacity: animatedValue,
|
|
91
|
+
marginTop: animatedValue.interpolate({
|
|
92
|
+
inputRange: [0, 1],
|
|
93
|
+
outputRange: [0, theme.space.sm]
|
|
94
|
+
}),
|
|
91
95
|
};
|
|
92
96
|
|
|
93
97
|
return (
|
|
94
98
|
<View style={styles.mainContainer} ref={containerRef} collapsable={false}>
|
|
95
|
-
{label &&
|
|
99
|
+
{label && (
|
|
100
|
+
<Text style={[theme.typography.label, { color: theme.text.neutral.primary, marginBottom: theme.space.sm }]}>
|
|
101
|
+
{label}
|
|
102
|
+
</Text>
|
|
103
|
+
)}
|
|
96
104
|
|
|
97
105
|
<TouchableOpacity
|
|
98
106
|
activeOpacity={0.8}
|
|
@@ -114,15 +122,15 @@ const handleSelect = (item) => {
|
|
|
114
122
|
</TouchableOpacity>
|
|
115
123
|
|
|
116
124
|
{isOpen && (
|
|
117
|
-
<View
|
|
125
|
+
<Animated.View
|
|
118
126
|
style={[
|
|
119
127
|
styles.dropdownInline,
|
|
128
|
+
animatedLayout, // Aplicamos el objeto de animación aquí
|
|
120
129
|
{
|
|
121
|
-
marginTop: theme.space.sm,
|
|
122
130
|
backgroundColor: theme.surface.neutral.primary,
|
|
123
131
|
borderRadius: theme.radius.sm,
|
|
124
132
|
borderColor: theme.outline.neutral.primary,
|
|
125
|
-
|
|
133
|
+
overflow: 'hidden',
|
|
126
134
|
}
|
|
127
135
|
]}
|
|
128
136
|
>
|
|
@@ -137,10 +145,9 @@ const handleSelect = (item) => {
|
|
|
137
145
|
theme={theme}
|
|
138
146
|
/>
|
|
139
147
|
)}
|
|
140
|
-
// Importante para que el scroll funcione dentro de la lista expandida
|
|
141
148
|
nestedScrollEnabled={true}
|
|
142
149
|
/>
|
|
143
|
-
</View>
|
|
150
|
+
</Animated.View>
|
|
144
151
|
)}
|
|
145
152
|
</View>
|
|
146
153
|
);
|
|
@@ -149,7 +156,9 @@ const handleSelect = (item) => {
|
|
|
149
156
|
const styles = StyleSheet.create({
|
|
150
157
|
mainContainer: { width: '100%' },
|
|
151
158
|
inputContainer: { height: 48, borderWidth: 1, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' },
|
|
152
|
-
dropdownInline: {
|
|
159
|
+
dropdownInline: {
|
|
160
|
+
borderWidth: 1,
|
|
161
|
+
},
|
|
153
162
|
optionItem: { height: 48, justifyContent: 'center' },
|
|
154
163
|
optionContent: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }
|
|
155
164
|
});
|
package/package.json
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { useFonts, Inter_400Regular, Inter_600SemiBold, Inter_700Bold } from "@expo-google-fonts/inter";
|
|
2
|
-
|
|
3
1
|
import { CDSprimitiveFontValues } from "./CDSprimitiveTokens";
|
|
4
2
|
|
|
5
3
|
|
|
@@ -67,7 +65,7 @@ export const getSemanticTextStyles = (theme, isMobile) => {
|
|
|
67
65
|
sm: {
|
|
68
66
|
fontFamily: CDSprimitiveFontValues.family.interRegular,
|
|
69
67
|
fontSize: getVal('size', 'sm'),
|
|
70
|
-
lineHeight: getVal('lineHeight', '
|
|
68
|
+
lineHeight: getVal('lineHeight', 'sm'), // Usando xs ya que sm no estaba en tu lista
|
|
71
69
|
color: theme.text.neutral.primary,
|
|
72
70
|
},
|
|
73
71
|
xs: {
|