cdslibrary 1.2.86 → 1.2.87
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.
|
@@ -2,62 +2,105 @@ import React, { useState } from "react";
|
|
|
2
2
|
import { Text, StyleSheet, TouchableOpacity, View, Image } from "react-native";
|
|
3
3
|
import { MaterialIcons } from "@expo/vector-icons";
|
|
4
4
|
import { useTheme } from "../context/CDSThemeContext";
|
|
5
|
+
import { CDSTooltip } from "./CDSTooltip"; // Asegúrate de importar tu Tooltip
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export const CDSImageButton = ({ object, isActive, onPress }) => { // 👈 Añadimos isActive
|
|
7
|
+
export const CDSImageButton = ({ object, isActive, onPress, hasHelper, helperMessage }) => {
|
|
9
8
|
const { theme } = useTheme();
|
|
9
|
+
|
|
10
|
+
// Estados para controlar el Tooltip interno
|
|
11
|
+
const [tooltipVisible, setTooltipVisible] = useState(false);
|
|
12
|
+
const [tooltipPos, setTooltipPos] = useState({ x: 0, y: 0 });
|
|
10
13
|
|
|
14
|
+
const handleHelperPress = (event) => {
|
|
15
|
+
// Capturamos la posición global para el tooltip
|
|
16
|
+
const { pageX, pageY } = event.nativeEvent;
|
|
17
|
+
setTooltipPos({ x: pageX, y: pageY });
|
|
18
|
+
setTooltipVisible(true);
|
|
19
|
+
};
|
|
11
20
|
|
|
12
21
|
return (
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
{
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
/>
|
|
44
|
-
<Text style={isActive ? theme.typography.semiBold.sm : theme.typography.regular.sm }>
|
|
45
|
-
{object.label}
|
|
46
|
-
</Text>
|
|
47
|
-
</TouchableOpacity>
|
|
48
|
-
);
|
|
49
|
-
};
|
|
22
|
+
<>
|
|
23
|
+
<TouchableOpacity
|
|
24
|
+
onPress={onPress}
|
|
25
|
+
activeOpacity={0.7}
|
|
26
|
+
style={[
|
|
27
|
+
styles.mainContainer,
|
|
28
|
+
{
|
|
29
|
+
backgroundColor: isActive
|
|
30
|
+
? theme.surface.neutral.primaryVariant
|
|
31
|
+
: theme.surface.neutral.primary,
|
|
32
|
+
gap: theme.space.xs, // Reducimos un poco el gap para el label
|
|
33
|
+
padding: theme.space.sm,
|
|
34
|
+
borderRadius: theme.radius.md,
|
|
35
|
+
borderWidth: 1,
|
|
36
|
+
borderColor: isActive ? theme.outline.neutral.focus : 'transparent',
|
|
37
|
+
...(!isActive && theme.shadows.lg),
|
|
38
|
+
transform: [{ scale: isActive ? 0.98 : 1 }]
|
|
39
|
+
},
|
|
40
|
+
]}
|
|
41
|
+
>
|
|
42
|
+
<Image
|
|
43
|
+
source={object.image}
|
|
44
|
+
resizeMode="cover"
|
|
45
|
+
style={{
|
|
46
|
+
width: "100%",
|
|
47
|
+
height: 80,
|
|
48
|
+
borderRadius: theme.radius.sm,
|
|
49
|
+
marginBottom: 4
|
|
50
|
+
}}
|
|
51
|
+
/>
|
|
50
52
|
|
|
53
|
+
{/* Contenedor de Texto + Ícono de ayuda */}
|
|
54
|
+
<View style={styles.labelContainer}>
|
|
55
|
+
<Text
|
|
56
|
+
numberOfLines={1}
|
|
57
|
+
style={[
|
|
58
|
+
isActive ? theme.typography.semiBold.sm : theme.typography.regular.sm,
|
|
59
|
+
{ color: theme.text.neutral.primary }
|
|
60
|
+
]}
|
|
61
|
+
>
|
|
62
|
+
{object.label}
|
|
63
|
+
</Text>
|
|
51
64
|
|
|
65
|
+
{hasHelper && (
|
|
66
|
+
<TouchableOpacity
|
|
67
|
+
onPress={handleHelperPress}
|
|
68
|
+
hitSlop={{ top: 15, bottom: 15, left: 15, right: 15 }}
|
|
69
|
+
>
|
|
70
|
+
<MaterialIcons
|
|
71
|
+
name="help-outline"
|
|
72
|
+
size={14}
|
|
73
|
+
color={theme.text.neutral.secondary}
|
|
74
|
+
/>
|
|
75
|
+
</TouchableOpacity>
|
|
76
|
+
)}
|
|
77
|
+
</View>
|
|
78
|
+
</TouchableOpacity>
|
|
52
79
|
|
|
53
|
-
|
|
80
|
+
{/* Tooltip renderizado fuera del botón para evitar cortes de overflow */}
|
|
81
|
+
<CDSTooltip
|
|
82
|
+
visible={tooltipVisible}
|
|
83
|
+
message={helperMessage}
|
|
84
|
+
targetPosition={tooltipPos}
|
|
85
|
+
onDismiss={() => setTooltipVisible(false)}
|
|
86
|
+
arrowAlign="center"
|
|
87
|
+
/>
|
|
88
|
+
</>
|
|
89
|
+
);
|
|
90
|
+
};
|
|
54
91
|
|
|
55
|
-
|
|
92
|
+
const styles = StyleSheet.create({
|
|
93
|
+
mainContainer: {
|
|
56
94
|
alignItems: 'center',
|
|
57
95
|
flexGrow: 1,
|
|
58
96
|
maxWidth: 160,
|
|
59
97
|
minWidth: 96,
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
98
|
+
},
|
|
99
|
+
labelContainer: {
|
|
100
|
+
flexDirection: 'row',
|
|
101
|
+
alignItems: 'center',
|
|
102
|
+
justifyContent: 'center',
|
|
103
|
+
gap: 4, // Espacio entre el texto y el ícono
|
|
104
|
+
width: '100%',
|
|
105
|
+
}
|
|
106
|
+
});
|
|
@@ -59,6 +59,8 @@ export const CDSImageButtonGroup = ({ array, onSelect, isCentered }) => {
|
|
|
59
59
|
object={item}
|
|
60
60
|
isActive={selectedId === item.id}
|
|
61
61
|
onPress={() => handlePress(item.id)}
|
|
62
|
+
hasHelper={item.helper ? true : false}
|
|
63
|
+
helperMessage={item.helper}
|
|
62
64
|
/>
|
|
63
65
|
))}
|
|
64
66
|
</ScrollView>
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import React, { useEffect } from "react";
|
|
2
|
+
import { StyleSheet, Text, View, Modal, TouchableWithoutFeedback } from "react-native";
|
|
3
|
+
import Animated, {
|
|
4
|
+
useSharedValue,
|
|
5
|
+
useAnimatedStyle,
|
|
6
|
+
withSpring,
|
|
7
|
+
withTiming,
|
|
8
|
+
} from "react-native-reanimated";
|
|
9
|
+
import { useTheme } from "../context/CDSThemeContext";
|
|
10
|
+
|
|
11
|
+
export const CDSTooltip = ({ message, visible, onDismiss, targetPosition = { x: 0, y: 0 }, arrowAlign = 'center' }) => {
|
|
12
|
+
const { theme } = useTheme();
|
|
13
|
+
const opacity = useSharedValue(0);
|
|
14
|
+
const scale = useSharedValue(1);
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (visible) {
|
|
18
|
+
opacity.value = withTiming(1, { duration: 200 });
|
|
19
|
+
// scale.value = withSpring(1, { damping: 12 });
|
|
20
|
+
} else {
|
|
21
|
+
opacity.value = withTiming(0, { duration: 200 });
|
|
22
|
+
// scale.value = withTiming(0.8);
|
|
23
|
+
}
|
|
24
|
+
}, [visible]);
|
|
25
|
+
|
|
26
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
27
|
+
opacity: opacity.value,
|
|
28
|
+
// transform: [{ scale: scale.value }],
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
// 1. Alineación de la flecha dentro del globo
|
|
32
|
+
const getArrowStyle = () => {
|
|
33
|
+
switch (arrowAlign) {
|
|
34
|
+
case 'left': return { left: 15 };
|
|
35
|
+
case 'right': return { right: 15 };
|
|
36
|
+
default: return { alignSelf: 'center' };
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// 2. Alineación del globo respecto al clic (x)
|
|
41
|
+
const getTooltipContainerStyle = () => {
|
|
42
|
+
const tooltipWidth = 200; // Ancho estimado o fijo
|
|
43
|
+
let leftPosition = targetPosition.x;
|
|
44
|
+
|
|
45
|
+
if (arrowAlign === 'center') {
|
|
46
|
+
leftPosition = targetPosition.x - (tooltipWidth / 2);
|
|
47
|
+
} else if (arrowAlign === 'right') {
|
|
48
|
+
leftPosition = targetPosition.x - tooltipWidth + 25; // 25 es el margen de la flecha
|
|
49
|
+
} else {
|
|
50
|
+
leftPosition = targetPosition.x - 25;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
top: targetPosition.y + 24, // Debajo del clic
|
|
55
|
+
left: Math.max(10, leftPosition), // Evita que se salga de la pantalla por la izquierda
|
|
56
|
+
width: tooltipWidth,
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
if (!visible && opacity.value === 0) return null;
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<Modal transparent visible={visible} animationType="none" onRequestClose={onDismiss}>
|
|
64
|
+
<TouchableWithoutFeedback onPress={onDismiss}>
|
|
65
|
+
<View style={styles.overlay}>
|
|
66
|
+
<Animated.View
|
|
67
|
+
style={[
|
|
68
|
+
styles.tooltip,
|
|
69
|
+
animatedStyle,
|
|
70
|
+
getTooltipContainerStyle(),
|
|
71
|
+
{
|
|
72
|
+
backgroundColor: theme.surface.special.tooltip,
|
|
73
|
+
borderRadius: theme.radius.sm,
|
|
74
|
+
padding: theme.space.sm,
|
|
75
|
+
}
|
|
76
|
+
]}
|
|
77
|
+
>
|
|
78
|
+
{/* La Flecha */}
|
|
79
|
+
<View style={[
|
|
80
|
+
styles.arrowUp,
|
|
81
|
+
getArrowStyle(),
|
|
82
|
+
{ borderBottomColor: theme.surface.special.tooltip }
|
|
83
|
+
]} />
|
|
84
|
+
|
|
85
|
+
{/* El Texto con alineación dinámica */}
|
|
86
|
+
<Text style={[
|
|
87
|
+
theme.typography.regular.xs,
|
|
88
|
+
{
|
|
89
|
+
color: theme.text.neutral.contrast,
|
|
90
|
+
textAlign: arrowAlign // 'left', 'right' o 'center'
|
|
91
|
+
}
|
|
92
|
+
]}>
|
|
93
|
+
{message}
|
|
94
|
+
</Text>
|
|
95
|
+
</Animated.View>
|
|
96
|
+
</View>
|
|
97
|
+
</TouchableWithoutFeedback>
|
|
98
|
+
</Modal>
|
|
99
|
+
);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const styles = StyleSheet.create({
|
|
103
|
+
overlay: { flex: 1, backgroundColor: 'transparent' },
|
|
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
|
+
},
|
|
113
|
+
arrowUp: {
|
|
114
|
+
position: 'absolute',
|
|
115
|
+
top: -8,
|
|
116
|
+
width: 0,
|
|
117
|
+
height: 0,
|
|
118
|
+
borderLeftWidth: 8,
|
|
119
|
+
borderRightWidth: 8,
|
|
120
|
+
borderBottomWidth: 8,
|
|
121
|
+
borderStyle: 'solid',
|
|
122
|
+
backgroundColor: 'transparent',
|
|
123
|
+
borderLeftColor: 'transparent',
|
|
124
|
+
borderRightColor: 'transparent',
|
|
125
|
+
}
|
|
126
|
+
});
|
package/index.js
CHANGED
|
@@ -25,6 +25,7 @@ export {CDSSnackBar} from './components/CDSSnackBar';
|
|
|
25
25
|
export {CDSImageButton} from './components/CDSImageButton'
|
|
26
26
|
export {CDSImageButtonGroup} from './components/CDSImageButtonGroup'
|
|
27
27
|
export {CDSCheckbox} from './components/CDSCheckbox';
|
|
28
|
+
export {CDSTooltip} from './components/CDSTooltip';
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
export {CDSThemeProvider, useTheme} from './context/CDSThemeContext';
|
package/package.json
CHANGED
|
@@ -54,7 +54,7 @@ export const CDSsemanticColors = {
|
|
|
54
54
|
overlay: CDSprimitiveColors.overlay.light,
|
|
55
55
|
scroll: CDSprimitiveColors.neutral[400],
|
|
56
56
|
snackbar: CDSprimitiveColors.neutral[700],
|
|
57
|
-
tooltip: CDSprimitiveColors.neutral[
|
|
57
|
+
tooltip: CDSprimitiveColors.neutral[600],
|
|
58
58
|
progress: CDSprimitiveColors.brand[600],
|
|
59
59
|
progressbarBg: CDSprimitiveColors.neutral[400],
|
|
60
60
|
display: CDSprimitiveColors.neutral[800],
|