cdslibrary 1.2.41 → 1.2.43

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.
@@ -49,7 +49,7 @@ export const CDSAccordion = ({ title, description, children, defaultExpanded = f
49
49
  styles.container,
50
50
  {
51
51
  backgroundColor: theme.surface.neutral.primary,
52
- borderRadius: theme.radius.lg,
52
+ borderRadius: theme.radius.md,
53
53
  borderColor: theme.outline.neutral.primary,
54
54
  borderWidth: 1
55
55
  }
@@ -59,11 +59,11 @@ export const CDSAccordion = ({ title, description, children, defaultExpanded = f
59
59
  style={[
60
60
  styles.header,
61
61
  {
62
- borderTopRightRadius: theme.radius.lg,
63
- borderTopLeftRadius: theme.radius.lg,
62
+ borderTopRightRadius: theme.radius.md,
63
+ borderTopLeftRadius: theme.radius.md,
64
64
  // Si no está expandido, redondeamos también abajo
65
- borderBottomRightRadius: expanded ? 0 : theme.radius.lg,
66
- borderBottomLeftRadius: expanded ? 0 : theme.radius.lg,
65
+ borderBottomRightRadius: expanded ? 0 : theme.radius.md,
66
+ borderBottomLeftRadius: expanded ? 0 : theme.radius.md,
67
67
  },
68
68
  expanded && {
69
69
  backgroundColor: theme.surface.brand.primary,
@@ -1,29 +1,64 @@
1
- import React, { useState } from "react";
2
- import { View, Text, TextInput, StyleSheet, Platform } from "react-native"; // Añadido Platform
1
+ import React, { useState, useEffect, useRef } from "react";
2
+ import { View, Text, TextInput, StyleSheet, Platform } from "react-native";
3
+ import Animated, {
4
+ useAnimatedStyle,
5
+ useSharedValue,
6
+ withSequence,
7
+ withTiming,
8
+ interpolateColor
9
+ } from "react-native-reanimated";
3
10
  import { useTheme } from "../context/CDSThemeContext";
4
11
 
5
- // Agregamos 'value' y 'onChangeText' a las props para que sea un "Controlled Component"
6
- export const CDSInput = ({ label, type, keyboard, placeholder, value, onChangeText }) => {
12
+ export const CDSInput = ({ label, type, keyboard, placeholder, value, onChangeText, animationTrigger }) => {
7
13
  const { theme } = useTheme();
8
14
  const [isFocused, setIsFocused] = useState(false);
15
+
16
+ // Valor animado para el "flash" (0 a 1)
17
+ const flashValue = useSharedValue(0);
18
+ // Ref para detectar si el cambio vino del teclado o del código
19
+ const isInternalChange = useRef(false);
20
+
21
+ useEffect(() => {
22
+ if (!isInternalChange.current) {
23
+ flashValue.value = withSequence(
24
+ withTiming(1, { duration: 200 }),
25
+ withTiming(0, { duration: 600 })
26
+ );
27
+ }
28
+ isInternalChange.current = false;
29
+ }, [value, animationTrigger]);
9
30
 
10
31
  const handleTextChange = (inputText) => {
32
+ isInternalChange.current = true; // Marcamos que el cambio es del usuario
33
+
11
34
  if (keyboard === "numeric" || keyboard === "decimal-pad") {
12
- let cleaned = inputText.replace(',', '.');
13
- cleaned = cleaned.replace(/[^0-9.]/g, "");
14
-
35
+ let cleaned = inputText.replace(',', '.').replace(/[^0-9.]/g, "");
15
36
  const parts = cleaned.split('.');
16
- if (parts.length > 2) {
17
- cleaned = parts[0] + '.' + parts.slice(1).join('');
18
- }
19
-
20
- // Enviamos el valor limpio al padre (index.js)
37
+ if (parts.length > 2) cleaned = parts[0] + '.' + parts.slice(1).join('');
21
38
  onChangeText && onChangeText(cleaned);
22
39
  } else {
23
40
  onChangeText && onChangeText(inputText);
24
41
  }
25
42
  };
26
43
 
44
+ // Estilo animado para el borde y el fondo
45
+ const animatedBoxStyle = useAnimatedStyle(() => {
46
+ const borderColor = interpolateColor(
47
+ flashValue.value,
48
+ [0, 1],
49
+ [
50
+ isFocused ? theme.outline.neutral.focus : value ? theme.outline.neutral.tertiaryVariant : theme.outline.neutral.primary,
51
+ theme.outline.neutral.focus// Color del "brillo" (usamos el color de focus)
52
+ ]
53
+ );
54
+
55
+ return {
56
+ borderColor: borderColor,
57
+ transform: [{ scale: 1 + flashValue.value * 0.02 }], // Pequeño pulso
58
+ borderWidth: 1 + flashValue.value * 1,
59
+ };
60
+ });
61
+
27
62
  return (
28
63
  <View style={[styles.container, { gap: theme.space.sm }]}>
29
64
  {label && (
@@ -32,49 +67,51 @@ export const CDSInput = ({ label, type, keyboard, placeholder, value, onChangeTe
32
67
  </Text>
33
68
  )}
34
69
 
35
- <TextInput
36
- pointerEvents={type === 'readOnly' && 'none'}
37
- style={[
38
- styles.textBox,
39
- theme.typography.inputText.value,
40
- {
70
+ <Animated.View style={[
71
+ styles.wrapper,
72
+ animatedBoxStyle,
73
+ {
41
74
  backgroundColor: type === 'readOnly' ? theme.surface.action.disabled : theme.surface.neutral.primary,
42
- borderColor: isFocused
43
- ? theme.outline.neutral.focus
44
- : value ? theme.outline.neutral.tertiaryVariant // Cambiado 'text' por 'value'
45
- : theme.outline.neutral.primary,
46
75
  borderRadius: theme.radius.sm,
47
- paddingHorizontal: theme.space.sm,
48
- color: theme.text.neutral.primary,
49
- outlineStyle: 'none',
50
-
51
- },
52
- ]}
53
- placeholder={placeholder}
54
- placeholderTextColor={theme.text.neutral.placeholder} // Usar el color directo del tema es más seguro
55
- keyboardType={keyboard === "numeric" ? (Platform.OS === 'ios' ? 'decimal-pad' : 'numeric') : keyboard}
56
- onChangeText={handleTextChange}
57
- value={value} // Ahora recibe el valor del simulador
58
- secureTextEntry={type === "password"}
59
- onFocus={() => setIsFocused(true)}
60
- onBlur={() => setIsFocused(false)}
61
- underlineColorAndroid="transparent"
62
- />
76
+ }
77
+ ]}>
78
+ <TextInput
79
+ pointerEvents={type === 'readOnly' ? 'none' : 'auto'}
80
+ style={[
81
+ styles.textBox,
82
+ theme.typography.inputText.value,
83
+ {
84
+ paddingHorizontal: theme.space.sm,
85
+ color: theme.text.neutral.primary,
86
+ // @ts-ignore
87
+ outlineStyle: 'none',
88
+ },
89
+ ]}
90
+ placeholder={placeholder}
91
+ placeholderTextColor={theme.text.neutral.placeholder}
92
+ keyboardType={keyboard === "numeric" ? (Platform.OS === 'ios' ? 'decimal-pad' : 'numeric') : keyboard}
93
+ onChangeText={handleTextChange}
94
+ value={value}
95
+ editable={type !== 'readOnly'}
96
+ secureTextEntry={type === "password"}
97
+ onFocus={() => setIsFocused(true)}
98
+ onBlur={() => setIsFocused(false)}
99
+ underlineColorAndroid="transparent"
100
+ />
101
+ </Animated.View>
63
102
  </View>
64
103
  );
65
104
  };
66
105
 
67
106
  const styles = StyleSheet.create({
68
107
  container: { width: "100%" },
69
- textBox: {
70
- borderWidth: 1,
108
+ wrapper: {
71
109
  height: 48,
72
- // transitionProperty solo funciona en Web, en móvil se ignora sin error
73
- ...Platform.select({
74
- web: {
75
- transitionProperty: 'border-color',
76
- transitionDuration: '0.2s',
77
- }
78
- })
110
+ borderWidth: 1,
111
+ justifyContent: 'center',
112
+ },
113
+ textBox: {
114
+ flex: 1,
115
+ height: '100%',
79
116
  },
80
117
  });
@@ -1,60 +1,99 @@
1
1
  import React, { useEffect } from "react";
2
- import { StyleSheet, Text, TouchableOpacity } from "react-native";
3
- import Animated, { SlideInDown, SlideOutDown } from "react-native-reanimated";
2
+ import { StyleSheet, Text, TouchableOpacity, Dimensions } from "react-native";
3
+ import Animated, {
4
+ useSharedValue,
5
+ useAnimatedStyle,
6
+ withSpring,
7
+ withTiming,
8
+ runOnJS
9
+ } from "react-native-reanimated";
10
+ import { GestureDetector, Gesture, } from "react-native-gesture-handler";
4
11
  import { useTheme } from "../context/CDSThemeContext";
5
12
 
6
- export const CDSSnackBar = ({ message, visible, onDismiss, hasClose }) => {
13
+ const { height: SCREEN_HEIGHT } = Dimensions.get("window");
14
+
15
+ export const CDSSnackBar = ({ message, visible, onDismiss, action }) => {
7
16
  const { theme } = useTheme();
17
+ const translateY = useSharedValue(100); // Empezamos fuera de pantalla
18
+
19
+ // Sincronizar visibilidad con animación
20
+ useEffect(() => {
21
+ if (visible) {
22
+ translateY.value = withSpring(0, { damping: 15 });
23
+ } else {
24
+ translateY.value = withTiming(150);
25
+ }
26
+ }, [visible]);
8
27
 
28
+ // Lógica de auto-ocultado
9
29
  useEffect(() => {
10
30
  let timer;
11
31
  if (visible) {
12
- // Configuramos el temporizador a 5000ms (5 segundos)
13
32
  timer = setTimeout(() => {
14
33
  if (onDismiss) onDismiss();
15
- }, 5000);
34
+ }, 4000);
16
35
  }
17
-
18
- // Limpieza: si el componente se desmonta o el usuario lo cierra antes,
19
- // cancelamos el timer para evitar fugas de memoria.
20
36
  return () => clearTimeout(timer);
21
37
  }, [visible, onDismiss]);
22
38
 
23
- // Reanimated necesita que el componente se desmonte para ejecutar 'exiting'
24
- if (!visible) return null;
39
+ // Configuración del Gesto (Pan Gesture)
40
+ const gesture = Gesture.Pan()
41
+ .onUpdate((event) => {
42
+ // Solo permitimos arrastrar hacia abajo (valores positivos de Y)
43
+ if (event.translationY > 0) {
44
+ translateY.value = event.translationY;
45
+ }
46
+ })
47
+ .onEnd((event) => {
48
+ // Si se arrastró más de 50px o la velocidad es alta, cerramos
49
+ if (event.translationY > 50 || event.velocityY > 500) {
50
+ translateY.value = withTiming(150, {}, () => {
51
+ if (onDismiss) runOnJS(onDismiss)();
52
+ });
53
+ } else {
54
+ // Si no, regresa a su posición original
55
+ translateY.value = withSpring(0);
56
+ }
57
+ });
58
+
59
+ const animatedStyle = useAnimatedStyle(() => ({
60
+ transform: [{ translateY: translateY.value }],
61
+ }));
62
+
63
+ if (!visible && translateY.value >= 100) return null;
25
64
 
26
65
  return (
27
- <Animated.View
28
- entering={SlideInDown.duration(400)}
29
- exiting={SlideOutDown.duration(300)}
30
- style={[
31
- styles.snackbar,
32
- {
33
- backgroundColor: theme.surface.special.display,
34
- bottom: theme.space.lg,
35
- padding: theme.space.md,
36
- borderRadius: theme.radius.sm,
37
- }
38
- ]}
39
- >
40
- <Text style={[
41
- theme.typography.regular.sm,
42
- { color: theme.text.neutral.contrast, flex: 1 }
43
- ]}>
44
- {message}
45
- </Text>
66
+ <GestureDetector gesture={gesture}>
67
+ <Animated.View
68
+ style={[
69
+ styles.snackbar,
70
+ animatedStyle,
71
+ {
72
+ backgroundColor: theme.surface.special.snackbar,
73
+ bottom: theme.space.lg,
74
+ padding: theme.space.md,
75
+ borderRadius: theme.radius.sm,
76
+ }
77
+ ]}
78
+ >
79
+ <Text style={[
80
+ theme.typography.regular.sm,
81
+ { color: theme.text.neutral.contrast, flex: 1 }
82
+ ]}>
83
+ {message}
84
+ </Text>
46
85
 
47
- {hasClose && (
48
- <TouchableOpacity onPress={onDismiss} style={styles.actionButton}>
49
- <Text style={{
50
- color: theme.colors?.brand?.secondary || theme.text.neutral.contrast,
51
- fontWeight: '700'
52
- }}>
53
- CERRAR
54
- </Text>
55
- </TouchableOpacity>
56
- )}
57
- </Animated.View>
86
+ {action && (
87
+ <TouchableOpacity onPress={onDismiss} style={styles.actionButton}>
88
+ <Text style={[theme.typography.label, {
89
+ color: theme.text.neutral.contrast,
90
+ }]}>
91
+ {action}
92
+ </Text>
93
+ </TouchableOpacity>
94
+ )}
95
+ </Animated.View>
96
+ </GestureDetector>
58
97
  );
59
98
  };
60
99
 
@@ -67,11 +106,6 @@ const styles = StyleSheet.create({
67
106
  flexDirection: 'row',
68
107
  alignItems: 'center',
69
108
  justifyContent: 'space-between',
70
- elevation: 5,
71
- shadowColor: '#000',
72
- shadowOffset: { width: 0, height: 2 },
73
- shadowOpacity: 0.25,
74
- shadowRadius: 3.84,
75
109
  zIndex: 1000,
76
110
  },
77
111
  actionButton: {
package/index.js CHANGED
@@ -1,8 +1,9 @@
1
1
 
2
- // import { registerRootComponent } from 'expo';
2
+ import 'react-native-gesture-handler';
3
+ import { registerRootComponent } from 'expo';
3
4
 
4
- // import App from './App';
5
- // registerRootComponent(App);
5
+ import App from './App';
6
+ registerRootComponent(App);
6
7
 
7
8
  export {CDSBottomSheet} from './components/CDSBottomSheet';
8
9
  export {CDSButton} from './components/CDSButton';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cdslibrary",
3
3
  "license": "0BSD",
4
- "version": "1.2.41",
4
+ "version": "1.2.43",
5
5
  "main": "index.js",
6
6
  "author": "Nat Viramontes",
7
7
  "description": "A library of components for the CDS project",
@@ -25,7 +25,7 @@
25
25
  "react": "*",
26
26
  "react-dom": "*",
27
27
  "react-native": "*",
28
- "react-native-gesture-handler": "*",
28
+ "react-native-gesture-handler": "^2.30.0",
29
29
  "react-native-reanimated": ">=3.0.0",
30
30
  "react-native-safe-area-context": "*",
31
31
  "react-native-screens": "*"
@@ -53,6 +53,7 @@ export const CDSsemanticColors = {
53
53
  special: {
54
54
  overlay: CDSprimitiveColors.overlay.light,
55
55
  scroll: CDSprimitiveColors.neutral[400],
56
+ snackbar: CDSprimitiveColors.neutral[700],
56
57
  tooltip: CDSprimitiveColors.neutral[500],
57
58
  progress: CDSprimitiveColors.brand[600],
58
59
  progressbarBg: CDSprimitiveColors.neutral[400],
@@ -164,6 +165,7 @@ export const CDSsemanticColors = {
164
165
  special: {
165
166
  overlay: CDSprimitiveColors.overlay.dark,
166
167
  scroll: CDSprimitiveColors.neutral[400],
168
+ snackbar: CDSprimitiveColors.neutral[400],
167
169
  tooltip: CDSprimitiveColors.neutral[500],
168
170
  progress: CDSprimitiveColors.brand[600],
169
171
  progressbarBg: CDSprimitiveColors.neutral[400],