cdslibrary 1.2.87 → 1.2.88
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.
|
@@ -118,10 +118,9 @@ const animatedStyle = useAnimatedStyle(() => {
|
|
|
118
118
|
return (
|
|
119
119
|
<>
|
|
120
120
|
<Animated.View
|
|
121
|
-
pointerEvents={isVisible ? "auto" : "none"} // Evita clics fantasma
|
|
122
121
|
style={[
|
|
123
122
|
styles.overlay,
|
|
124
|
-
{ backgroundColor: theme.surface.special.overlay },
|
|
123
|
+
{pointerEvents: isVisible ? "auto" : "none", backgroundColor: theme.surface.special.overlay },
|
|
125
124
|
backdropStyle
|
|
126
125
|
]}
|
|
127
126
|
>
|
|
@@ -131,6 +130,7 @@ const animatedStyle = useAnimatedStyle(() => {
|
|
|
131
130
|
style={[
|
|
132
131
|
isMobile ? styles.container.typeBottomSheet : styles.container.typeDrawer,
|
|
133
132
|
animatedStyle,
|
|
133
|
+
theme.shadows.lg,
|
|
134
134
|
{
|
|
135
135
|
backgroundColor: theme.surface.neutral.primary,
|
|
136
136
|
paddingTop: (hasClose ? theme.space['2xl'] : theme.space.xl),
|
|
@@ -239,7 +239,6 @@ const styles = StyleSheet.create({
|
|
|
239
239
|
zIndex: 99,
|
|
240
240
|
...Platform.select({
|
|
241
241
|
web: {
|
|
242
|
-
boxShadow: '-10px 0px 15px rgba(0,0,0,0.1)',
|
|
243
242
|
height: 'auto', // En web forzamos que sea auto
|
|
244
243
|
}
|
|
245
244
|
})
|
|
@@ -4,17 +4,33 @@ import { MaterialIcons } from "@expo/vector-icons";
|
|
|
4
4
|
import { useTheme } from "../context/CDSThemeContext";
|
|
5
5
|
import { CDSTooltip } from "./CDSTooltip"; // Asegúrate de importar tu Tooltip
|
|
6
6
|
|
|
7
|
-
export const CDSImageButton = ({ object, isActive, onPress, hasHelper, helperMessage }) => {
|
|
7
|
+
export const CDSImageButton = ({ object, isActive, onPress, hasHelper, helperMessage, arrowAlignment }) => {
|
|
8
8
|
const { theme } = useTheme();
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
// Estados para controlar el Tooltip interno
|
|
11
11
|
const [tooltipVisible, setTooltipVisible] = useState(false);
|
|
12
12
|
const [tooltipPos, setTooltipPos] = useState({ x: 0, y: 0 });
|
|
13
13
|
|
|
14
14
|
const handleHelperPress = (event) => {
|
|
15
|
-
//
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
// Si estamos en Web, usamos getBoundingClientRect para precisión total
|
|
16
|
+
if (event.target && event.target.getBoundingClientRect) {
|
|
17
|
+
const rect = event.target.getBoundingClientRect();
|
|
18
|
+
setTooltipPos({
|
|
19
|
+
x: rect.left,
|
|
20
|
+
y: rect.top,
|
|
21
|
+
width: rect.width,
|
|
22
|
+
height: rect.height
|
|
23
|
+
});
|
|
24
|
+
} else {
|
|
25
|
+
// Fallback para Mobile (Nativo)
|
|
26
|
+
const { pageX, pageY } = event.nativeEvent;
|
|
27
|
+
setTooltipPos({
|
|
28
|
+
x: pageX - 10, // Ajuste manual si no hay medida
|
|
29
|
+
y: pageY,
|
|
30
|
+
width: 20,
|
|
31
|
+
height: 20
|
|
32
|
+
});
|
|
33
|
+
}
|
|
18
34
|
setTooltipVisible(true);
|
|
19
35
|
};
|
|
20
36
|
|
|
@@ -26,8 +42,8 @@ export const CDSImageButton = ({ object, isActive, onPress, hasHelper, helperMes
|
|
|
26
42
|
style={[
|
|
27
43
|
styles.mainContainer,
|
|
28
44
|
{
|
|
29
|
-
backgroundColor: isActive
|
|
30
|
-
? theme.surface.neutral.primaryVariant
|
|
45
|
+
backgroundColor: isActive
|
|
46
|
+
? theme.surface.neutral.primaryVariant
|
|
31
47
|
: theme.surface.neutral.primary,
|
|
32
48
|
gap: theme.space.xs, // Reducimos un poco el gap para el label
|
|
33
49
|
padding: theme.space.sm,
|
|
@@ -35,7 +51,7 @@ export const CDSImageButton = ({ object, isActive, onPress, hasHelper, helperMes
|
|
|
35
51
|
borderWidth: 1,
|
|
36
52
|
borderColor: isActive ? theme.outline.neutral.focus : 'transparent',
|
|
37
53
|
...(!isActive && theme.shadows.lg),
|
|
38
|
-
transform: [{ scale: isActive ? 0.98 : 1 }]
|
|
54
|
+
transform: [{ scale: isActive ? 0.98 : 1 }]
|
|
39
55
|
},
|
|
40
56
|
]}
|
|
41
57
|
>
|
|
@@ -52,7 +68,7 @@ export const CDSImageButton = ({ object, isActive, onPress, hasHelper, helperMes
|
|
|
52
68
|
|
|
53
69
|
{/* Contenedor de Texto + Ícono de ayuda */}
|
|
54
70
|
<View style={styles.labelContainer}>
|
|
55
|
-
<Text
|
|
71
|
+
<Text
|
|
56
72
|
numberOfLines={1}
|
|
57
73
|
style={[
|
|
58
74
|
isActive ? theme.typography.semiBold.sm : theme.typography.regular.sm,
|
|
@@ -63,14 +79,14 @@ export const CDSImageButton = ({ object, isActive, onPress, hasHelper, helperMes
|
|
|
63
79
|
</Text>
|
|
64
80
|
|
|
65
81
|
{hasHelper && (
|
|
66
|
-
<TouchableOpacity
|
|
82
|
+
<TouchableOpacity
|
|
67
83
|
onPress={handleHelperPress}
|
|
68
84
|
hitSlop={{ top: 15, bottom: 15, left: 15, right: 15 }}
|
|
69
85
|
>
|
|
70
|
-
<MaterialIcons
|
|
71
|
-
name="help-outline"
|
|
72
|
-
size={14}
|
|
73
|
-
color={theme.text.neutral.secondary}
|
|
86
|
+
<MaterialIcons
|
|
87
|
+
name="help-outline"
|
|
88
|
+
size={14}
|
|
89
|
+
color={theme.text.neutral.secondary}
|
|
74
90
|
/>
|
|
75
91
|
</TouchableOpacity>
|
|
76
92
|
)}
|
|
@@ -78,12 +94,12 @@ export const CDSImageButton = ({ object, isActive, onPress, hasHelper, helperMes
|
|
|
78
94
|
</TouchableOpacity>
|
|
79
95
|
|
|
80
96
|
{/* Tooltip renderizado fuera del botón para evitar cortes de overflow */}
|
|
81
|
-
<CDSTooltip
|
|
97
|
+
<CDSTooltip
|
|
82
98
|
visible={tooltipVisible}
|
|
83
99
|
message={helperMessage}
|
|
84
100
|
targetPosition={tooltipPos}
|
|
85
101
|
onDismiss={() => setTooltipVisible(false)}
|
|
86
|
-
arrowAlign=
|
|
102
|
+
arrowAlign={arrowAlignment}
|
|
87
103
|
/>
|
|
88
104
|
</>
|
|
89
105
|
);
|
|
@@ -1,126 +1,98 @@
|
|
|
1
|
-
import React, { useEffect } from "react";
|
|
2
|
-
import { StyleSheet, Text, View, Modal,
|
|
3
|
-
import Animated, {
|
|
4
|
-
useSharedValue,
|
|
5
|
-
useAnimatedStyle,
|
|
6
|
-
withSpring,
|
|
7
|
-
withTiming,
|
|
8
|
-
} from "react-native-reanimated";
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import { StyleSheet, Text, View, Modal, Pressable, Dimensions } from "react-native";
|
|
3
|
+
import Animated, { useSharedValue, useAnimatedStyle, withTiming, runOnJS } from "react-native-reanimated";
|
|
9
4
|
import { useTheme } from "../context/CDSThemeContext";
|
|
10
5
|
|
|
11
|
-
|
|
6
|
+
const { width: SCREEN_WIDTH } = Dimensions.get('window');
|
|
7
|
+
|
|
8
|
+
export const CDSTooltip = ({ message, visible, onDismiss, targetPosition = { x: 0, y: 0, width: 0, height: 0 }, arrowAlign = 'center' }) => {
|
|
12
9
|
const { theme } = useTheme();
|
|
13
10
|
const opacity = useSharedValue(0);
|
|
14
|
-
const
|
|
11
|
+
const [shouldRender, setShouldRender] = useState(visible);
|
|
15
12
|
|
|
16
13
|
useEffect(() => {
|
|
17
14
|
if (visible) {
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
setShouldRender(true);
|
|
16
|
+
opacity.value = withTiming(1, { duration: 150 });
|
|
20
17
|
} else {
|
|
21
|
-
opacity.value = withTiming(0, { duration:
|
|
22
|
-
|
|
18
|
+
opacity.value = withTiming(0, { duration: 150 }, (finished) => {
|
|
19
|
+
if (finished) runOnJS(setShouldRender)(false);
|
|
20
|
+
});
|
|
23
21
|
}
|
|
24
22
|
}, [visible]);
|
|
25
23
|
|
|
26
|
-
const animatedStyle = useAnimatedStyle(() => ({
|
|
27
|
-
opacity: opacity.value,
|
|
28
|
-
// transform: [{ scale: scale.value }],
|
|
29
|
-
}));
|
|
24
|
+
const animatedStyle = useAnimatedStyle(() => ({ opacity: opacity.value }));
|
|
30
25
|
|
|
31
|
-
//
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
case 'left': return { left: 15 };
|
|
35
|
-
case 'right': return { right: 15 };
|
|
36
|
-
default: return { alignSelf: 'center' };
|
|
37
|
-
}
|
|
38
|
-
};
|
|
26
|
+
// --- LÓGICA DE POSICIONAMIENTO ---
|
|
27
|
+
const tooltipWidth = 320; // Ancho fijo para facilitar el cálculo de la flecha
|
|
28
|
+
const paddingScreen = 16;
|
|
39
29
|
|
|
40
|
-
//
|
|
41
|
-
const
|
|
42
|
-
const tooltipWidth = 200; // Ancho estimado o fijo
|
|
43
|
-
let leftPosition = targetPosition.x;
|
|
30
|
+
// Centro horizontal del icono/botón
|
|
31
|
+
const targetCenterX = targetPosition.x + (targetPosition.width / 2);
|
|
44
32
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
} else {
|
|
50
|
-
leftPosition = targetPosition.x - 25;
|
|
51
|
-
}
|
|
33
|
+
// Calculamos el Left del contenedor
|
|
34
|
+
let leftPos = targetCenterX - (tooltipWidth / 2);
|
|
35
|
+
if (arrowAlign === 'left') leftPos = targetCenterX - 30;
|
|
36
|
+
if (arrowAlign === 'right') leftPos = targetCenterX - tooltipWidth + 30;
|
|
52
37
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
left: Math.max(10, leftPosition), // Evita que se salga de la pantalla por la izquierda
|
|
56
|
-
width: tooltipWidth,
|
|
57
|
-
};
|
|
58
|
-
};
|
|
38
|
+
// Ajuste para que no choque con los bordes del celular/navegador
|
|
39
|
+
const finalLeft = Math.max(paddingScreen, Math.min(leftPos, SCREEN_WIDTH - tooltipWidth - paddingScreen));
|
|
59
40
|
|
|
60
|
-
|
|
41
|
+
// Calculamos dónde debe ir la flecha respecto al globo
|
|
42
|
+
const arrowLeft = targetCenterX - finalLeft - 8; // 8 es la mitad del ancho de la flecha
|
|
43
|
+
|
|
44
|
+
if (!shouldRender) return null;
|
|
61
45
|
|
|
62
46
|
return (
|
|
63
47
|
<Modal transparent visible={visible} animationType="none" onRequestClose={onDismiss}>
|
|
64
|
-
<
|
|
65
|
-
<View
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
48
|
+
<Pressable style={styles.overlay} onPress={onDismiss}>
|
|
49
|
+
<Animated.View
|
|
50
|
+
style={[
|
|
51
|
+
styles.tooltip,
|
|
52
|
+
animatedStyle,
|
|
53
|
+
theme.shadows.lg,
|
|
54
|
+
{
|
|
55
|
+
top: targetPosition.y + targetPosition.height + 8,
|
|
56
|
+
left: finalLeft,
|
|
57
|
+
width: tooltipWidth,
|
|
58
|
+
backgroundColor: theme.surface.special.tooltip,
|
|
59
|
+
borderRadius: theme.radius.sm,
|
|
60
|
+
padding: theme.space.sm,
|
|
61
|
+
}
|
|
62
|
+
]}
|
|
63
|
+
>
|
|
64
|
+
{/* Flecha dinámica */}
|
|
65
|
+
<View style={[
|
|
66
|
+
styles.arrowUp,
|
|
67
|
+
{ left: arrowLeft, borderBottomColor: theme.surface.special.tooltip }
|
|
68
|
+
]} />
|
|
84
69
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
{message}
|
|
94
|
-
</Text>
|
|
95
|
-
</Animated.View>
|
|
96
|
-
</View>
|
|
97
|
-
</TouchableWithoutFeedback>
|
|
70
|
+
<Text style={[
|
|
71
|
+
theme.typography.regular.xs,
|
|
72
|
+
{ color: theme.text.neutral.contrast, textAlign: 'center' }
|
|
73
|
+
]}>
|
|
74
|
+
{message}
|
|
75
|
+
</Text>
|
|
76
|
+
</Animated.View>
|
|
77
|
+
</Pressable>
|
|
98
78
|
</Modal>
|
|
99
79
|
);
|
|
100
80
|
};
|
|
101
81
|
|
|
102
82
|
const styles = StyleSheet.create({
|
|
103
|
-
overlay: {
|
|
104
|
-
tooltip: {
|
|
105
|
-
position: 'absolute',
|
|
106
|
-
zIndex: 10000,
|
|
107
|
-
elevation: 5,
|
|
108
|
-
shadowColor: "#000",
|
|
109
|
-
shadowOffset: { width: 0, height: 2 },
|
|
110
|
-
shadowOpacity: 0.2,
|
|
111
|
-
shadowRadius: 4,
|
|
112
|
-
},
|
|
83
|
+
overlay: { ...StyleSheet.absoluteFillObject, backgroundColor: 'transparent' },
|
|
84
|
+
tooltip: { position: 'absolute', zIndex: 10000 },
|
|
113
85
|
arrowUp: {
|
|
114
86
|
position: 'absolute',
|
|
115
|
-
top: -
|
|
87
|
+
top: -7,
|
|
116
88
|
width: 0,
|
|
117
89
|
height: 0,
|
|
118
90
|
borderLeftWidth: 8,
|
|
119
91
|
borderRightWidth: 8,
|
|
120
92
|
borderBottomWidth: 8,
|
|
121
|
-
borderStyle: 'solid',
|
|
122
|
-
backgroundColor: 'transparent',
|
|
123
93
|
borderLeftColor: 'transparent',
|
|
124
94
|
borderRightColor: 'transparent',
|
|
95
|
+
borderTopColor: 'transparent',
|
|
96
|
+
backgroundColor: 'transparent',
|
|
125
97
|
}
|
|
126
98
|
});
|