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
- // Capturamos la posición global para el tooltip
16
- const { pageX, pageY } = event.nativeEvent;
17
- setTooltipPos({ x: pageX, y: pageY });
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="center"
102
+ arrowAlign={arrowAlignment}
87
103
  />
88
104
  </>
89
105
  );
@@ -61,6 +61,7 @@ export const CDSImageButtonGroup = ({ array, onSelect, isCentered }) => {
61
61
  onPress={() => handlePress(item.id)}
62
62
  hasHelper={item.helper ? true : false}
63
63
  helperMessage={item.helper}
64
+ arrowAlignment={item.arrowAlignment}
64
65
  />
65
66
  ))}
66
67
  </ScrollView>
@@ -1,126 +1,98 @@
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";
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
- export const CDSTooltip = ({ message, visible, onDismiss, targetPosition = { x: 0, y: 0 }, arrowAlign = 'center' }) => {
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 scale = useSharedValue(1);
11
+ const [shouldRender, setShouldRender] = useState(visible);
15
12
 
16
13
  useEffect(() => {
17
14
  if (visible) {
18
- opacity.value = withTiming(1, { duration: 200 });
19
- // scale.value = withSpring(1, { damping: 12 });
15
+ setShouldRender(true);
16
+ opacity.value = withTiming(1, { duration: 150 });
20
17
  } else {
21
- opacity.value = withTiming(0, { duration: 200 });
22
- // scale.value = withTiming(0.8);
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
- // 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
- };
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
- // 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;
30
+ // Centro horizontal del icono/botón
31
+ const targetCenterX = targetPosition.x + (targetPosition.width / 2);
44
32
 
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
- }
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
- 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
- };
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
- if (!visible && opacity.value === 0) return null;
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
- <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
- ]} />
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
- {/* 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>
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: { 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
- },
83
+ overlay: { ...StyleSheet.absoluteFillObject, backgroundColor: 'transparent' },
84
+ tooltip: { position: 'absolute', zIndex: 10000 },
113
85
  arrowUp: {
114
86
  position: 'absolute',
115
- top: -8,
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
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cdslibrary",
3
3
  "license": "0BSD",
4
- "version": "1.2.87",
4
+ "version": "1.2.88",
5
5
  "main": "index.js",
6
6
  "author": "Nat Viramontes",
7
7
  "description": "A library of components for the CDS project",