cdslibrary 1.2.49 → 1.2.51
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.
|
@@ -1,10 +1,22 @@
|
|
|
1
|
-
import React, { useEffect,
|
|
2
|
-
import { View, Text,
|
|
1
|
+
import React, { useEffect, forwardRef, useCallback } from "react";
|
|
2
|
+
import { View, Text, Dimensions, StyleSheet, Pressable, Platform, ScrollView } from "react-native";
|
|
3
3
|
import { CDSButton } from "./CDSButton";
|
|
4
4
|
import { MaterialIcons } from "@expo/vector-icons";
|
|
5
5
|
import { useTheme } from "../context/CDSThemeContext";
|
|
6
6
|
import { LinearGradient } from 'expo-linear-gradient';
|
|
7
7
|
|
|
8
|
+
// --- NUEVO: Imports de Reanimated y Gesture Handler ---
|
|
9
|
+
import Animated, {
|
|
10
|
+
useSharedValue,
|
|
11
|
+
useAnimatedStyle,
|
|
12
|
+
withSpring,
|
|
13
|
+
withTiming,
|
|
14
|
+
runOnJS,
|
|
15
|
+
interpolate,
|
|
16
|
+
Extrapolation
|
|
17
|
+
} from "react-native-reanimated";
|
|
18
|
+
import { GestureDetector, Gesture } from "react-native-gesture-handler";
|
|
19
|
+
|
|
8
20
|
const { height, width } = Dimensions.get("window");
|
|
9
21
|
|
|
10
22
|
const bottomSheetRender = ({
|
|
@@ -20,145 +32,180 @@ const bottomSheetRender = ({
|
|
|
20
32
|
onFinish,
|
|
21
33
|
}, ref) => {
|
|
22
34
|
const { theme } = useTheme();
|
|
23
|
-
const isMobile = theme.isMobile
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}),
|
|
43
|
-
]).start(() => {
|
|
44
|
-
// Importante: onFinish se ejecuta SOLO cuando la animación termina
|
|
45
|
-
if (onFinish) onFinish();
|
|
46
|
-
if (primaryButtonOnPress) primaryButtonOnPress();
|
|
47
|
-
setModalVisible(false);
|
|
35
|
+
const isMobile = theme.isMobile;
|
|
36
|
+
|
|
37
|
+
// --- 1. Estado de Animación (Reanimated SharedValues) ---
|
|
38
|
+
const startPos = isMobile ? height : width; // Abajo en mobile, Derecha en desktop
|
|
39
|
+
const translation = useSharedValue(startPos);
|
|
40
|
+
const opacity = useSharedValue(0);
|
|
41
|
+
|
|
42
|
+
// --- 2. Funciones de Cierre (Wrapper para JS Thread) ---
|
|
43
|
+
const triggerOnFinish = useCallback(() => {
|
|
44
|
+
if (onFinish) onFinish();
|
|
45
|
+
if (primaryButtonOnPress) primaryButtonOnPress();
|
|
46
|
+
}, [onFinish, primaryButtonOnPress]);
|
|
47
|
+
|
|
48
|
+
const handleClose = useCallback(() => {
|
|
49
|
+
"worklet"; // Marca para ejecutar en UI thread
|
|
50
|
+
translation.value = withTiming(startPos, { duration: 300 }, (finished) => {
|
|
51
|
+
if (finished) {
|
|
52
|
+
runOnJS(triggerOnFinish)();
|
|
53
|
+
}
|
|
48
54
|
});
|
|
49
|
-
|
|
55
|
+
opacity.value = withTiming(0, { duration: 300 });
|
|
56
|
+
}, [startPos, triggerOnFinish]);
|
|
50
57
|
|
|
58
|
+
// --- 3. Lógica del Gesto (Solo Mobile) ---
|
|
59
|
+
const pan = Gesture.Pan()
|
|
60
|
+
.enabled(isMobile) // Desactivamos gesto en Desktop
|
|
61
|
+
.onUpdate((event) => {
|
|
62
|
+
// Solo permitir arrastrar hacia abajo (valores positivos)
|
|
63
|
+
if (event.translationY > 0) {
|
|
64
|
+
translation.value = event.translationY;
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
.onEnd((event) => {
|
|
68
|
+
// Si arrastra más de 100px o hace un "flick" rápido -> CERRAR
|
|
69
|
+
if (event.translationY > 100 || event.velocityY > 600) {
|
|
70
|
+
handleClose();
|
|
71
|
+
} else {
|
|
72
|
+
// Si no, rebota a posición original
|
|
73
|
+
translation.value = withSpring(0, { damping: 15, stiffness: 100 });
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// --- 4. Efectos de Entrada/Salida ---
|
|
51
78
|
useEffect(() => {
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
Animated.timing(slidePosition, {
|
|
60
|
-
toValue: 0,
|
|
61
|
-
duration: 400,
|
|
62
|
-
useNativeDriver: false,
|
|
63
|
-
}),
|
|
64
|
-
]).start();
|
|
79
|
+
if (isVisible) {
|
|
80
|
+
translation.value = withSpring(0, { damping: 15, stiffness: 90 });
|
|
81
|
+
opacity.value = withTiming(1, { duration: 300 });
|
|
82
|
+
} else {
|
|
83
|
+
// Si se controla false desde fuera
|
|
84
|
+
translation.value = withTiming(startPos, { duration: 300 });
|
|
85
|
+
opacity.value = withTiming(0, { duration: 300 });
|
|
65
86
|
}
|
|
66
|
-
}, [
|
|
87
|
+
}, [isVisible, isMobile]);
|
|
88
|
+
|
|
89
|
+
// --- 5. Estilos Animados ---
|
|
90
|
+
const animatedStyle = useAnimatedStyle(() => {
|
|
91
|
+
return {
|
|
92
|
+
opacity: opacity.value,
|
|
93
|
+
transform: [
|
|
94
|
+
isMobile
|
|
95
|
+
? { translateY: translation.value }
|
|
96
|
+
: { translateX: translation.value }
|
|
97
|
+
],
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const backdropStyle = useAnimatedStyle(() => {
|
|
102
|
+
// Interpolamos para que el fondo se aclare mientras arrastras
|
|
103
|
+
const currentY = translation.value;
|
|
104
|
+
const opacityVal = interpolate(currentY, [0, height], [1, 0], Extrapolation.CLAMP);
|
|
105
|
+
return {
|
|
106
|
+
opacity: opacityVal,
|
|
107
|
+
};
|
|
108
|
+
});
|
|
67
109
|
|
|
68
110
|
return (
|
|
69
111
|
<>
|
|
70
112
|
<Animated.View
|
|
71
113
|
style={[
|
|
72
|
-
|
|
73
|
-
{
|
|
74
|
-
|
|
75
|
-
transform: [
|
|
76
|
-
isMobile ? { translateY: slidePosition } : { translateX: slidePosition }
|
|
77
|
-
],
|
|
78
|
-
backgroundColor: theme.surface.neutral.primary,
|
|
79
|
-
paddingHorizontal: theme.space.md,
|
|
80
|
-
paddingTop: theme.space.xl,
|
|
81
|
-
gap: theme.space.md,
|
|
82
|
-
...(isMobile ? {
|
|
83
|
-
borderTopLeftRadius: theme.radius.lg,
|
|
84
|
-
borderTopRightRadius: theme.radius.lg,
|
|
85
|
-
} : {
|
|
86
|
-
borderBottomLeftRadius: theme.radius.lg,
|
|
87
|
-
borderTopLeftRadius: theme.radius.lg,
|
|
88
|
-
paddingBottom: theme.space.md,
|
|
89
|
-
})
|
|
90
|
-
}
|
|
114
|
+
styles.overlay,
|
|
115
|
+
{ backgroundColor: theme.surface.special.overlay },
|
|
116
|
+
backdropStyle // Aplicamos estilo animado aquí
|
|
91
117
|
]}
|
|
92
118
|
>
|
|
93
|
-
{
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
{
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
onPress={() => {
|
|
134
|
-
handleClose();
|
|
135
|
-
}}
|
|
119
|
+
<Pressable onPress={() => runOnJS(handleClose)()} style={{ flex: 1 }} />
|
|
120
|
+
</Animated.View>
|
|
121
|
+
|
|
122
|
+
<GestureDetector gesture={pan}>
|
|
123
|
+
<Animated.View
|
|
124
|
+
style={[
|
|
125
|
+
isMobile ? styles.container.typeBottomSheet : styles.container.typeDrawer,
|
|
126
|
+
animatedStyle, // Inyectamos la animación de Reanimated
|
|
127
|
+
{
|
|
128
|
+
backgroundColor: theme.surface.neutral.primary,
|
|
129
|
+
// Mantenemos tu lógica exacta de padding y gaps
|
|
130
|
+
paddingTop: (hasClose ? theme.space['2xl'] : theme.space.md),
|
|
131
|
+
gap: theme.space.md,
|
|
132
|
+
...(isMobile ? {
|
|
133
|
+
borderTopLeftRadius: theme.radius.lg,
|
|
134
|
+
borderTopRightRadius: theme.radius.lg,
|
|
135
|
+
} : {
|
|
136
|
+
borderBottomLeftRadius: theme.radius.lg,
|
|
137
|
+
borderTopLeftRadius: theme.radius.lg,
|
|
138
|
+
paddingBottom: theme.space.md,
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
]}
|
|
142
|
+
>
|
|
143
|
+
{/* --- NUEVO: La línea visual (Handle) ---
|
|
144
|
+
Se agrega position absolute para NO afectar tus paddings/espacios originales */}
|
|
145
|
+
{isMobile && (
|
|
146
|
+
<View style={styles.handleBarContainer}>
|
|
147
|
+
<View style={[styles.handleBarLine, { backgroundColor: theme.outline.neutral.tertiary }]} />
|
|
148
|
+
</View>
|
|
149
|
+
)}
|
|
150
|
+
|
|
151
|
+
{hasClose && (
|
|
152
|
+
<MaterialIcons
|
|
153
|
+
name="close"
|
|
154
|
+
size={theme.typography.icon.lg}
|
|
155
|
+
color={theme.text.neutral.primary}
|
|
156
|
+
// Usamos runOnJS porque onPress espera una función normal
|
|
157
|
+
onPress={() => runOnJS(handleClose)()}
|
|
158
|
+
style={{ position: "absolute", right: theme.space.md, top: theme.space.md, zIndex: 10 }}
|
|
136
159
|
/>
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
160
|
+
)}
|
|
161
|
+
|
|
162
|
+
{!!title && <Text style={[theme.typography.h3, {marginHorizontal: theme.space.md}]}>{title}</Text>}
|
|
163
|
+
|
|
164
|
+
<View style={styles.scrollWrapper}>
|
|
165
|
+
<ScrollView
|
|
166
|
+
ref={ref}
|
|
167
|
+
style={styles.scrollArea}
|
|
168
|
+
contentContainerStyle={[styles.scrollContent, { paddingBottom: theme.space.sm }]}
|
|
169
|
+
showsVerticalScrollIndicator={true}
|
|
170
|
+
bounces={true}
|
|
171
|
+
alwaysBounceVertical={false}
|
|
172
|
+
scrollEventThrottle={16}
|
|
173
|
+
>
|
|
174
|
+
{!!description && (
|
|
175
|
+
<Text style={[theme.typography.regular.md, { marginBottom: theme.space.md }]}>
|
|
176
|
+
{description}
|
|
177
|
+
</Text>
|
|
178
|
+
)}
|
|
179
|
+
{customSlot}
|
|
180
|
+
</ScrollView>
|
|
181
|
+
<LinearGradient
|
|
182
|
+
colors={['transparent', theme.surface.neutral.primary]}
|
|
183
|
+
style={styles.fadeGradient}
|
|
184
|
+
pointerEvents="none"
|
|
141
185
|
/>
|
|
142
186
|
</View>
|
|
143
|
-
)}
|
|
144
|
-
</Animated.View>
|
|
145
187
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
188
|
+
{type !== "informative" && (
|
|
189
|
+
<View style={[isMobile ? styles.actionsContainer.typeBottomSheet : styles.actionsContainer.typeDrawer, {paddingHorizontal: theme.space.md, paddingVertical: theme.space.lg}]}>
|
|
190
|
+
<CDSButton
|
|
191
|
+
label={primaryButtonLabel}
|
|
192
|
+
onPress={() => runOnJS(handleClose)()}
|
|
193
|
+
/>
|
|
194
|
+
{secondaryButtonLabel && (<CDSButton
|
|
195
|
+
label={secondaryButtonLabel}
|
|
196
|
+
type="ghost"
|
|
197
|
+
onPress={() => runOnJS(handleClose)()}
|
|
198
|
+
/>)}
|
|
199
|
+
</View>
|
|
200
|
+
)}
|
|
201
|
+
</Animated.View>
|
|
202
|
+
</GestureDetector>
|
|
157
203
|
</>
|
|
158
204
|
);
|
|
159
205
|
};
|
|
160
206
|
|
|
161
207
|
const styles = StyleSheet.create({
|
|
208
|
+
// --- Estilos originales intactos ---
|
|
162
209
|
overlay: {
|
|
163
210
|
position: "absolute",
|
|
164
211
|
top: 0,
|
|
@@ -214,7 +261,6 @@ const styles = StyleSheet.create({
|
|
|
214
261
|
flexDirection: "column",
|
|
215
262
|
width: "100%",
|
|
216
263
|
gap: 8,
|
|
217
|
-
paddingBottom: 20,
|
|
218
264
|
},
|
|
219
265
|
typeDrawer: {
|
|
220
266
|
flexDirection: "row-reverse",
|
|
@@ -223,7 +269,20 @@ const styles = StyleSheet.create({
|
|
|
223
269
|
marginTop: 'auto',
|
|
224
270
|
},
|
|
225
271
|
},
|
|
272
|
+
// --- NUEVOS ESTILOS PARA EL HANDLE ---
|
|
273
|
+
handleBarContainer: {
|
|
274
|
+
position: 'absolute',
|
|
275
|
+
top: 8, // Un poco de margen superior
|
|
276
|
+
width: '100%',
|
|
277
|
+
alignItems: 'center',
|
|
278
|
+
zIndex: 1, // Debajo del botón de cerrar
|
|
279
|
+
},
|
|
280
|
+
handleBarLine: {
|
|
281
|
+
width: 40,
|
|
282
|
+
height: 4,
|
|
283
|
+
borderRadius: 2,
|
|
284
|
+
opacity: 0.5,
|
|
285
|
+
}
|
|
226
286
|
});
|
|
227
287
|
|
|
228
|
-
|
|
229
288
|
export const CDSBottomSheet = forwardRef(bottomSheetRender);
|
package/components/CDSButton.jsx
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { Text, StyleSheet, TouchableOpacity } from "react-native";
|
|
1
|
+
import React, { useRef, useEffect } from "react";
|
|
2
|
+
import { Text, StyleSheet, TouchableOpacity, Animated } from "react-native";
|
|
3
3
|
import { MaterialIcons } from "@expo/vector-icons";
|
|
4
4
|
import { useTheme } from "../context/CDSThemeContext";
|
|
5
5
|
|
|
6
|
-
|
|
7
6
|
export const CDSButton = ({
|
|
8
7
|
label,
|
|
9
8
|
type = "primary",
|
|
@@ -14,8 +13,36 @@ export const CDSButton = ({
|
|
|
14
13
|
flexend,
|
|
15
14
|
}) => {
|
|
16
15
|
const { theme } = useTheme();
|
|
17
|
-
const isMobile = theme.isMobile
|
|
18
|
-
|
|
16
|
+
const isMobile = theme.isMobile;
|
|
17
|
+
|
|
18
|
+
// 1. Referencia de animación
|
|
19
|
+
const animatedValue = useRef(new Animated.Value(0)).current;
|
|
20
|
+
|
|
21
|
+
// 2. Disparar entrada al montar
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
Animated.timing(animatedValue, {
|
|
24
|
+
toValue: 1,
|
|
25
|
+
duration: 400,
|
|
26
|
+
useNativeDriver: false // Para manejar maxHeight y otros layouts
|
|
27
|
+
}).start();
|
|
28
|
+
}, []);
|
|
29
|
+
|
|
30
|
+
// 3. Estructura de animatedLayout unificada
|
|
31
|
+
const animatedLayout = {
|
|
32
|
+
opacity: animatedValue,
|
|
33
|
+
transform: [{
|
|
34
|
+
scale: animatedValue.interpolate({
|
|
35
|
+
inputRange: [0, 1],
|
|
36
|
+
outputRange: [0.95, 1]
|
|
37
|
+
})
|
|
38
|
+
}],
|
|
39
|
+
// Permite que el botón aparezca empujando suavemente
|
|
40
|
+
maxHeight: animatedValue.interpolate({
|
|
41
|
+
inputRange: [0, 1],
|
|
42
|
+
outputRange: [0, (variant === "fill" || isMobile ? 48 : 56)]
|
|
43
|
+
}),
|
|
44
|
+
};
|
|
45
|
+
|
|
19
46
|
const backgroundColor =
|
|
20
47
|
type === "disabled"
|
|
21
48
|
? theme.surface.action.disabled
|
|
@@ -31,46 +58,62 @@ export const CDSButton = ({
|
|
|
31
58
|
: theme.text.neutral.contrast;
|
|
32
59
|
|
|
33
60
|
return (
|
|
34
|
-
<
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
61
|
+
<Animated.View style={[
|
|
62
|
+
animatedLayout,
|
|
63
|
+
{
|
|
64
|
+
overflow: 'hidden',
|
|
65
|
+
alignSelf: (variant === "fill" || isMobile) ? "stretch" : "center",
|
|
66
|
+
flexGrow: (variant === "fill" || isMobile ? 1 : 0),
|
|
67
|
+
}
|
|
68
|
+
]}>
|
|
69
|
+
<TouchableOpacity
|
|
70
|
+
onPress={type !== "disabled" ? onPress : null}
|
|
71
|
+
activeOpacity={0.7}
|
|
72
|
+
style={[
|
|
73
|
+
styles.container,
|
|
74
|
+
{
|
|
75
|
+
paddingHorizontal: type === "icon" ? theme.space.sm : theme.space.md,
|
|
76
|
+
paddingVertical: theme.space.sm,
|
|
77
|
+
backgroundColor,
|
|
78
|
+
borderRadius: theme.radius.full,
|
|
79
|
+
gap: theme.space.sm,
|
|
80
|
+
height: (variant === "fill" || isMobile ? 48 : 56), // Altura fija interna
|
|
81
|
+
},
|
|
82
|
+
flexend && { justifyContent: "flex-end", paddingHorizontal: 0 },
|
|
83
|
+
type === "secondary" && {
|
|
84
|
+
borderColor: theme.outline.action.primary,
|
|
85
|
+
borderWidth: 1,
|
|
86
|
+
},
|
|
87
|
+
type === 'link' && {
|
|
88
|
+
paddingHorizontal: 0,
|
|
89
|
+
paddingVertical: 0,
|
|
90
|
+
minWidth: 1,
|
|
91
|
+
}
|
|
92
|
+
]}
|
|
93
|
+
>
|
|
94
|
+
{type !== "icon" && (
|
|
95
|
+
<Text
|
|
96
|
+
style={[
|
|
97
|
+
type === "link"
|
|
98
|
+
? size === "small"
|
|
99
|
+
? theme.typography.link.small
|
|
100
|
+
: theme.typography.link.regular
|
|
101
|
+
: theme.typography.buttonText,
|
|
102
|
+
{ color: textColor },
|
|
103
|
+
]}
|
|
104
|
+
>
|
|
105
|
+
{label}
|
|
106
|
+
</Text>
|
|
107
|
+
)}
|
|
108
|
+
{icon && (
|
|
109
|
+
<MaterialIcons
|
|
110
|
+
name={icon}
|
|
111
|
+
size={theme.typography.icon.sm}
|
|
112
|
+
color={textColor}
|
|
113
|
+
/>
|
|
114
|
+
)}
|
|
115
|
+
</TouchableOpacity>
|
|
116
|
+
</Animated.View>
|
|
74
117
|
);
|
|
75
118
|
};
|
|
76
119
|
|
|
@@ -80,5 +123,4 @@ const styles = StyleSheet.create({
|
|
|
80
123
|
alignItems: "center",
|
|
81
124
|
justifyContent: "center",
|
|
82
125
|
},
|
|
83
|
-
});
|
|
84
|
-
|
|
126
|
+
});
|
|
@@ -8,7 +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
|
-
|
|
11
|
+
|
|
12
12
|
return (
|
|
13
13
|
<TouchableOpacity
|
|
14
14
|
onPress={onPress}
|
|
@@ -19,11 +19,11 @@ export const CDSImageButton = ({ object, isActive, onPress }) => { // 👈 Añad
|
|
|
19
19
|
? theme.surface.neutral.primaryVariant // Color cuando está presionado
|
|
20
20
|
: theme.surface.neutral.primary,
|
|
21
21
|
gap: theme.space.sm,
|
|
22
|
-
padding: theme.space.
|
|
22
|
+
padding: theme.space.sm,
|
|
23
23
|
// borderColor: isActive
|
|
24
24
|
// ? theme.outline.neutral.primary // Borde de color de marca si está activo
|
|
25
25
|
// : theme.outline.neutral.primary,
|
|
26
|
-
borderRadius: theme.radius.
|
|
26
|
+
borderRadius: theme.radius.md,
|
|
27
27
|
// Si está activo, quitamos la sombra o usamos una más pequeña (md)
|
|
28
28
|
...(!isActive && theme.shadows.lg),
|
|
29
29
|
// Si quieres que se vea "hundido", podrías bajar la escala
|
|
@@ -38,6 +38,7 @@ export const CDSImageButton = ({ object, isActive, onPress }) => { // 👈 Añad
|
|
|
38
38
|
flexGrow: 1,
|
|
39
39
|
width: "100%",
|
|
40
40
|
height: 80,
|
|
41
|
+
borderRadius: theme.radius.sm,
|
|
41
42
|
}}
|
|
42
43
|
/>
|
|
43
44
|
<Text style={isActive ? theme.typography.semiBold.sm : theme.typography.regular.sm }>
|
|
@@ -55,7 +56,7 @@ mainContainer:{
|
|
|
55
56
|
alignItems: 'center',
|
|
56
57
|
flexGrow: 1,
|
|
57
58
|
maxWidth: 160,
|
|
58
|
-
minWidth:
|
|
59
|
+
minWidth: 96,
|
|
59
60
|
height: 'auto'
|
|
60
61
|
}
|
|
61
62
|
|
|
@@ -3,8 +3,9 @@ import { View, Animated, StyleSheet, ScrollView } from 'react-native';
|
|
|
3
3
|
import { CDSImageButton } from './CDSImageButton';
|
|
4
4
|
import { useTheme } from "../context/CDSThemeContext";
|
|
5
5
|
|
|
6
|
-
export const CDSImageButtonGroup = ({ array, onSelect }) => {
|
|
6
|
+
export const CDSImageButtonGroup = ({ array, onSelect, isCentered }) => {
|
|
7
7
|
const { theme } = useTheme();
|
|
8
|
+
const isMobile = theme.isMobile
|
|
8
9
|
const [selectedId, setSelectedId] = useState(null);
|
|
9
10
|
const animatedValue = useRef(new Animated.Value(0)).current;
|
|
10
11
|
|
|
@@ -31,10 +32,6 @@ export const CDSImageButtonGroup = ({ array, onSelect }) => {
|
|
|
31
32
|
outputRange: [0, 250]
|
|
32
33
|
}),
|
|
33
34
|
opacity: animatedValue,
|
|
34
|
-
marginTop: animatedValue.interpolate({
|
|
35
|
-
inputRange: [0, 1],
|
|
36
|
-
outputRange: [0, theme.space.md]
|
|
37
|
-
})
|
|
38
35
|
};
|
|
39
36
|
|
|
40
37
|
return (
|
|
@@ -45,14 +42,15 @@ export const CDSImageButtonGroup = ({ array, onSelect }) => {
|
|
|
45
42
|
<ScrollView
|
|
46
43
|
horizontal={true}
|
|
47
44
|
showsHorizontalScrollIndicator={false}
|
|
48
|
-
// IMPORTANTE: overflow visible para que no corte sombras,
|
|
49
|
-
// pero necesitamos que el ScrollView sepa su límite
|
|
50
45
|
style={styles.scrollView}
|
|
51
46
|
contentContainerStyle={[
|
|
52
47
|
styles.scrollContent,
|
|
53
48
|
{
|
|
49
|
+
justifyContent: (isCentered ? 'center' : 'flex-start'),
|
|
50
|
+
|
|
54
51
|
gap: theme.space.sm,
|
|
55
|
-
|
|
52
|
+
padding: theme.space.md,
|
|
53
|
+
width: (isMobile ? 'auto' : '100%')
|
|
56
54
|
}
|
|
57
55
|
]}
|
|
58
56
|
>
|
|
@@ -72,13 +70,11 @@ export const CDSImageButtonGroup = ({ array, onSelect }) => {
|
|
|
72
70
|
const styles = StyleSheet.create({
|
|
73
71
|
scrollView: {
|
|
74
72
|
width: '100%',
|
|
75
|
-
overflow: 'visible'
|
|
73
|
+
overflow: 'visible',
|
|
76
74
|
},
|
|
77
75
|
scrollContent: {
|
|
78
|
-
|
|
76
|
+
width: '100%',
|
|
79
77
|
flexDirection: 'row',
|
|
80
78
|
alignItems: 'center',
|
|
81
|
-
paddingVertical: 10,
|
|
82
|
-
overflow: 'visible'
|
|
83
79
|
}
|
|
84
80
|
});
|
package/package.json
CHANGED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import React, { useEffect } from 'react';
|
|
2
|
-
import { Platform } from 'react-native';
|
|
3
|
-
|
|
4
|
-
const CustomSlotDebug = ({ customSlot, isVisible }) => {
|
|
5
|
-
useEffect(() => {
|
|
6
|
-
console.log('Platform:', Platform.OS);
|
|
7
|
-
console.log('Custom Slot:', customSlot);
|
|
8
|
-
console.log('Is Visible:', isVisible);
|
|
9
|
-
}, [customSlot, isVisible]);
|
|
10
|
-
|
|
11
|
-
return <>{customSlot}</>;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export default CustomSlotDebug;
|