cdslibrary 1.0.56 → 1.1.3

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.
@@ -0,0 +1,80 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { View, StyleSheet, Modal } from "react-native";
3
+ import LottieView from "lottie-react-native";
4
+ import Animated, {
5
+ useSharedValue,
6
+ useAnimatedStyle,
7
+ withTiming,
8
+ withDelay, // 👈 Importamos withDelay
9
+ runOnJS
10
+ } from "react-native-reanimated";
11
+ import { useTheme } from "../context/CDSThemeContext";
12
+
13
+ export const CDSLoader = ({ visible = false }) => {
14
+ const { theme } = useTheme();
15
+ const [shouldRender, setShouldRender] = useState(visible);
16
+ const opacity = useSharedValue(0);
17
+
18
+ useEffect(() => {
19
+ if (visible) {
20
+ setShouldRender(true);
21
+ // Entrada rápida para que no se sienta lag al empezar
22
+ opacity.value = withTiming(1, { duration: 300 });
23
+ } else {
24
+ // 1. Espera 800ms antes de hacer nada (Sostenido)
25
+ // 2. Desvanece lentamente en 1200ms
26
+ opacity.value = withDelay(
27
+ 1000,
28
+ withTiming(0, { duration: 1200 }, (isFinished) => {
29
+ if (isFinished) {
30
+ runOnJS(setShouldRender)(false);
31
+ }
32
+ })
33
+ );
34
+ }
35
+ }, [visible]);
36
+
37
+ const animatedStyle = useAnimatedStyle(() => ({
38
+ opacity: opacity.value,
39
+ }));
40
+
41
+ if (!shouldRender && !visible) return null;
42
+
43
+ return (
44
+ <Modal transparent visible={shouldRender} animationType="none">
45
+ <Animated.View style={[styles.overlay, animatedStyle,
46
+ { backgroundColor: theme.overlay },]}>
47
+ <View
48
+ style={[
49
+ styles.loaderContainer,
50
+ ]}
51
+ >
52
+ <LottieView
53
+ autoPlay
54
+ loop
55
+ source={require("../assets/animations/animationLoaderWhite.json")}
56
+ resizeMode="contain"
57
+ />
58
+ </View>
59
+ </Animated.View>
60
+ </Modal>
61
+ );
62
+ };
63
+
64
+ const styles = StyleSheet.create({
65
+ overlay: {
66
+ height: '100%',
67
+ width: '100%',
68
+ justifyContent: "center",
69
+ alignItems: "center",
70
+ zIndex: 1000,
71
+ },
72
+ loaderContainer: {
73
+ padding: 20,
74
+ // O si usas fijos, asegúrate que sean mayores al estilo del Lottie
75
+ minWidth: 180,
76
+ minHeight: 180,
77
+ justifyContent: "center",
78
+ alignItems: "center",
79
+ },
80
+ });
@@ -0,0 +1,81 @@
1
+
2
+ import { View, Text, Dimensions, StyleSheet, Image, TouchableOpacity } from "react-native";
3
+
4
+
5
+ import { MaterialIcons } from "@expo/vector-icons";
6
+ import { useTheme } from "../context/CDSThemeContext";
7
+ import { getSemanticTextStyles } from "../tokens/CDSsemanticTextStyles";
8
+ import { useNavigation } from "@react-navigation/native";
9
+
10
+ const { height, width } = Dimensions.get("window");
11
+
12
+ const isMobile = width <= 878;
13
+ export const CDSNavBar = ({
14
+
15
+ type,
16
+ isExpanded,
17
+ hasClose,
18
+ title,
19
+ hasBack, }) => {
20
+
21
+ const { theme, isDarkMode } = useTheme();
22
+
23
+ const navigation = useNavigation();
24
+
25
+ const handleBackPress = () => {
26
+ if (hasBack) {
27
+ navigation.goBack();
28
+ }
29
+ };
30
+
31
+ const handleClosePress = () => {
32
+ if (hasClose) {
33
+ navigation.goBack();
34
+ }
35
+ };
36
+
37
+ return (
38
+ <View style={[styles.container, { backgroundColor: theme.surface.neutral.primary, borderColor: theme.outline.brand.primary }]}>
39
+ <TouchableOpacity onPress={handleBackPress}>
40
+ <MaterialIcons name="arrow-back-ios" size={theme.typography.icon.md} color={theme.text.neutral.primary} style={{ cursor: hasBack ? 'pointer' : 'default', opacity: hasBack ? 1 : 0 }} />
41
+ </TouchableOpacity>
42
+ {type === 'title' ? <Text style={[styles.title, theme.typography.semiBold.lg]}>{title || 'title'}</Text> : <Image
43
+ source={
44
+ isDarkMode
45
+ ? require("../assets/images/logoMonteTW.png")
46
+ : require("../assets/images/logoMonte.png")
47
+ }
48
+ style={styles.logo}
49
+ resizeMode="contain"
50
+ />}
51
+ <TouchableOpacity onPress={handleClosePress}>
52
+ <MaterialIcons name="close" size={theme.typography.icon.md} color={theme.text.neutral.primary} style={{ cursor: hasClose ? 'pointer' : 'default', opacity: hasClose ? 1 : 0 }} />
53
+ </TouchableOpacity>
54
+ </View>
55
+ )
56
+ }
57
+
58
+ const styles = StyleSheet.create({
59
+ container: {
60
+ width: '100%',
61
+ height: 56,
62
+ flexDirection: 'row',
63
+ alignItems: 'center',
64
+ justifyContent: 'space-between',
65
+ paddingHorizontal: 16,
66
+ borderBottomWidth: 1,
67
+ borderBottomColor: '#E0E0E0',
68
+ },
69
+
70
+ title: {
71
+ width: '100%',
72
+ textAlign: 'center',
73
+ },
74
+
75
+ logo: {
76
+ flex: 1,
77
+ height: '80%',
78
+ resizeMode: 'contain',
79
+ },
80
+
81
+ });
@@ -3,7 +3,6 @@ import { Dimensions } from "react-native";
3
3
  import {CDSBottomSheet} from "./CDSBottomSheet";
4
4
  import {CDSCarousel} from "./CDSCarousel";
5
5
  import { useTheme } from "../context/CDSThemeContext";
6
- import { Onboarding } from "./Onboarding";
7
6
  const { width } = Dimensions.get("window");
8
7
 
9
8
  export const CDSOnboarding = () => {
@@ -0,0 +1,133 @@
1
+ import React, { useState, useRef } from "react";
2
+ import { View, Text, TouchableOpacity, StyleSheet, FlatList, Animated } from "react-native";
3
+ import { MaterialIcons } from "@expo/vector-icons";
4
+ import { useTheme } from "../context/CDSThemeContext";
5
+
6
+ export const CDSSelect = ({ label, options = [], onSelect, placeholder = "Selecciona una opción" }) => {
7
+ const { theme } = useTheme();
8
+ const [isOpen, setIsOpen] = useState(false);
9
+ const [selected, setSelected] = useState(null);
10
+
11
+ // Referencias para la animación
12
+ const animatedValue = useRef(new Animated.Value(0)).current;
13
+
14
+ const toggleDropdown = () => {
15
+ if (isOpen) {
16
+ // Animación de cierre
17
+ Animated.timing(animatedValue, {
18
+ toValue: 0,
19
+ duration: 200,
20
+ useNativeDriver: true,
21
+ }).start(() => setIsOpen(false));
22
+ } else {
23
+ setIsOpen(true);
24
+ // Animación de apertura
25
+ Animated.timing(animatedValue, {
26
+ toValue: 1,
27
+ duration: 300,
28
+ useNativeDriver: true,
29
+ }).start();
30
+ }
31
+ };
32
+
33
+ const handleSelect = (item) => {
34
+ setSelected(item);
35
+ toggleDropdown();
36
+ if (onSelect) onSelect(item.value);
37
+ };
38
+
39
+ // Interpolación para el desplazamiento y la opacidad
40
+ const dropdownStyle = {
41
+ opacity: animatedValue,
42
+ transform: [
43
+ {
44
+ translateY: animatedValue.interpolate({
45
+ inputRange: [0, 1],
46
+ outputRange: [-10, 0], // Sube 10px mientras desaparece
47
+ }),
48
+ },
49
+ ],
50
+ };
51
+ return (
52
+ <View style={[styles.mainContainer, { zIndex: isOpen ? 1000 : 1, gap: theme.space.xs }]}>
53
+ {label && <Text style={[theme.typography.label, { color: theme.text.neutral.primary }]}>{label}</Text>}
54
+
55
+ <TouchableOpacity
56
+ activeOpacity={0.8}
57
+ style={[
58
+ styles.inputContainer,
59
+ theme.typography.inputText.value, { backgroundColor: theme.surface.neutral.primary, borderColor: selected ? theme.outline.neutral.tertiaryVariant : theme.outline.neutral.primary, borderRadius: theme.radius.sm, paddingHorizontal: theme.space.xs }
60
+ ]}
61
+ onPress={toggleDropdown}
62
+ >
63
+ <Text style={selected ? theme.typography.inputText.value : theme.typography.inputText.placeholder}>
64
+ {selected ? selected.label : placeholder}
65
+ </Text>
66
+ <MaterialIcons
67
+ name={isOpen ? "expand-less" : "expand-more"}
68
+ size={theme.typography.icon.md}
69
+ color={theme.text.neutral.primary}
70
+ />
71
+ </TouchableOpacity>
72
+
73
+ {isOpen && (
74
+ <Animated.View style={[
75
+ styles.dropdown,
76
+ dropdownStyle,
77
+ theme.shadows.lg,
78
+ {
79
+ backgroundColor: theme.surface.neutral.primary,
80
+ borderRadius: theme.radius.sm,
81
+ borderColor: theme.outline.neutral.primary,
82
+ elevation: 8,
83
+ maxHeight: 240,
84
+ }
85
+ ]}>
86
+ <FlatList
87
+ data={options}
88
+ keyExtractor={(item) => item.value.toString()}
89
+ showsVerticalScrollIndicator={true}
90
+ persistentScrollbar={true}
91
+ renderItem={({ item }) => (
92
+ <TouchableOpacity
93
+ style={[styles.optionItem, { paddingHorizontal: theme.space.xs }]}
94
+ onPress={() => handleSelect(item)}
95
+ >
96
+ <Text style={[theme.typography.regular.sm, { color: theme.text.neutral.primary }]}>
97
+ {item.label}
98
+ </Text>
99
+ </TouchableOpacity>
100
+ )}
101
+ />
102
+ </Animated.View>
103
+ )}
104
+ </View>
105
+ );
106
+ };
107
+
108
+ const styles = StyleSheet.create({
109
+ mainContainer: {
110
+ width: '100%',
111
+ },
112
+ inputContainer: {
113
+ height: 48,
114
+ borderWidth: 1,
115
+ flexDirection: 'row',
116
+ alignItems: 'center',
117
+ justifyContent: 'space-between',
118
+ },
119
+
120
+
121
+ dropdown: {
122
+ position: 'absolute',
123
+ top: 90,
124
+ width: '100%',
125
+ borderWidth: 1,
126
+ overflow: 'hidden', // Necesario para que el contenido no se salga de los bordes redondeados
127
+ },
128
+ optionItem: {
129
+ height: 48, // 👈 Altura fija por ítem para controlar el límite de 5
130
+ paddingHorizontal: 18,
131
+ justifyContent: 'center'
132
+ }
133
+ });
@@ -0,0 +1,81 @@
1
+ import { View, StyleSheet } from "react-native";
2
+ import LottieView from "lottie-react-native";
3
+ import { useRef, useEffect } from "react";
4
+ import Animated, {
5
+ useSharedValue,
6
+ useAnimatedStyle,
7
+ withTiming,
8
+ } from "react-native-reanimated";
9
+ import { useTheme } from "../context/CDSThemeContext";
10
+
11
+ export const CDSSplashScreen = ({ navigation, nextScreen }) => {
12
+ const { theme } = useTheme();
13
+ const animation = useRef(null);
14
+ const translateY = useSharedValue(0); // Definir un sharedValue para la posición vertical.
15
+ const size = useSharedValue(200); // Definir un sharedValue para el tamaño inicial.
16
+ const backgroundOpacity = useSharedValue(1);
17
+ useEffect(() => {
18
+ // Después de 6 segundos, mover la animación hacia arriba y cambiar el tamaño.
19
+ setTimeout(() => {
20
+ translateY.value = withTiming(-350, { duration: 1000 }); // Mover hacia arriba
21
+ size.value = withTiming(120, { duration: 1000 }); // Reducir el tamaño
22
+ backgroundOpacity.value = withTiming(0, { duration: 1000 });
23
+ // Esperar 1 segundo más antes de hacer el cambio de pantalla
24
+ setTimeout(() => {
25
+ navigation.replace(nextScreen);
26
+ }, 600);
27
+ }, 6000); // Esperar 6 segundos antes de la animación
28
+ }, [translateY, size]);
29
+
30
+ // Aplicar el estilo animado para la animación
31
+ const animatedStyle = useAnimatedStyle(() => {
32
+ return {
33
+ transform: [{ translateY: translateY.value }],
34
+ width: size.value, // Usar el tamaño animado
35
+ height: size.value, // Usar el tamaño animado
36
+ };
37
+ },[]);
38
+
39
+ return (
40
+ <View style={styles.background}>
41
+ <View
42
+ style={[
43
+ styles.animationContainer,
44
+ { backgroundColor: theme.surface.special.progress },
45
+ ]}
46
+ >
47
+ <Animated.View
48
+ style={[
49
+ { justifyContent: "center", alignItems: "center" },
50
+ animatedStyle,
51
+ ]}
52
+ >
53
+ <LottieView
54
+ autoPlay
55
+ loop={false}
56
+ source={require("../assets/animations/animation.json")}
57
+ style={{ width: "100%", height: "100%" }} // Usar 100% del tamaño del contenedor
58
+ />
59
+ </Animated.View>
60
+ </View>
61
+ </View>
62
+ );
63
+ }
64
+
65
+ const styles = StyleSheet.create({
66
+ background: {
67
+ flex: 1,
68
+ justifyContent: "center",
69
+ alignItems: "center",
70
+ backgroundColor: 'blue',
71
+ width: "100%",
72
+ },
73
+ animationContainer: {
74
+ flex: 1,
75
+ width: "100%",
76
+ justifyContent: "center",
77
+ alignItems: "center",
78
+ gap: 16,
79
+ padding: 16,
80
+ },
81
+ });
@@ -5,14 +5,14 @@ import {getSemanticTextStyles} from "../tokens/CDSsemanticTextStyles";
5
5
 
6
6
  export const CDSSwitch = ({ label }) => {
7
7
  const { theme } = useTheme();
8
- const textStyles = getSemanticTextStyles(theme);
8
+
9
9
  const [isEnabled, setIsEnabled] = useState(false);
10
10
 
11
11
  const toggleSwitch = () => setIsEnabled((previousState) => !previousState);
12
12
 
13
13
  return (
14
14
  <View style={styles.container}>
15
- <Text style={textStyles.label}>
15
+ <Text style={theme.typography.label}>
16
16
  {label}
17
17
  </Text>
18
18
  <Switch onValueChange={toggleSwitch} value={isEnabled} />
@@ -4,13 +4,13 @@ import { useTheme } from "../context/CDSThemeContext";
4
4
  export const OnboardingItem = ({ item, parentWidth }) => {
5
5
 
6
6
  const { theme } = useTheme();
7
- const textStyles = getSemanticTextStyles(theme);
7
+
8
8
 
9
9
  return (
10
10
  <View style={[styles.container, {width: parentWidth}]}>
11
11
  <View style={[styles.textContainer]}>
12
- <Text style={textStyles.bold.lg}>{item.title}</Text>
13
- <Text style={textStyles.regular.md}>{item.description}</Text>
12
+ <Text style={theme.typography.bold.lg}>{item.title}</Text>
13
+ <Text style={theme.typography.regular.md}>{item.description}</Text>
14
14
  </View>
15
15
  <Image source={item.image} resizeMode="contain" style={[styles.image,]} />
16
16
  </View>
@@ -1,52 +1,85 @@
1
1
  import React, { createContext, useState, useContext, useEffect } from 'react';
2
- import { Appearance, Platform } from 'react-native';
3
- import {CDSsemanticColors} from '../tokens/CDSsemanticColors';
2
+ import { Appearance, Platform, Dimensions } from 'react-native';
4
3
 
4
+ // Importa tus tokens
5
+ import { CDSsemanticColors } from '../tokens/CDSsemanticColors';
6
+ import { getSemanticTextStyles } from '../tokens/CDSsemanticTextStyles';
7
+ import { CDSPrimitiveShadows, CDSPrimitiveSpacing, CDSPrimiviteRadius } from '../tokens/CDSprimitiveTokens';
5
8
 
6
9
  const ThemeContext = createContext();
7
10
 
11
+ // Función para limpiar los objetos {mobile, desktop}
12
+ const resolveTokens = (primitives, isMobile) => {
13
+ const resolved = {};
14
+ for (const key in primitives) {
15
+ const value = primitives[key];
16
+ resolved[key] = (value && typeof value === 'object' && 'mobile' in value)
17
+ ? (isMobile ? value.mobile : value.desktop)
18
+ : value;
19
+ }
20
+ return resolved;
21
+ };
22
+
8
23
  export const CDSThemeProvider = ({ children }) => {
24
+ // 1. Definimos la función para obtener el tema inicial antes del estado
9
25
 
10
- const getSystemTheme = () => {
26
+ useEffect(() => {
27
+ const dimensionsSub = Dimensions.addEventListener('change', ({ window }) => {
28
+ setWindowWidth(window.width); // Esto es lo que dispara el re-render
29
+ });
30
+ return () => dimensionsSub.remove();
31
+ }, []);
32
+ const getInitialThemeMode = () => {
11
33
  if (Platform.OS === 'web') {
12
34
  return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
13
35
  }
14
36
  return Appearance.getColorScheme() || 'light';
15
37
  };
16
38
 
17
- const [theme, setTheme] = useState(getSystemTheme);
39
+ // 2. Estados principales
40
+ const [mode, setMode] = useState(getInitialThemeMode()); // Aquí es donde estaba el error
41
+ const [windowWidth, setWindowWidth] = useState(Dimensions.get('window').width);
42
+
43
+ const isMobile = windowWidth <= 768;
44
+
45
+ // 3. Efectos para cambios globales
46
+ useEffect(() => {
47
+ const themeSub = Appearance.addChangeListener(({ colorScheme }) => {
48
+ setMode(colorScheme || 'light');
49
+ });
50
+
51
+ const dimensionsSub = Dimensions.addEventListener('change', ({ window }) => {
52
+ setWindowWidth(window.width);
53
+ });
54
+
55
+ return () => {
56
+ themeSub.remove();
57
+ dimensionsSub.remove();
58
+ };
59
+ }, []);
18
60
 
19
61
  const toggleTheme = () => {
20
- setTheme(theme === 'light' ? 'dark' : 'light');
62
+ setMode(prev => (prev === 'light' ? 'dark' : 'light'));
21
63
  };
22
64
 
23
- const isDarkMode = theme === 'dark';
65
+ // 4. Construcción del objeto final (IMPORTANTE: colors primero)
66
+ const colors = CDSsemanticColors[mode];
24
67
 
25
- useEffect(() => {
26
- if (Platform.OS === 'web') {
27
- const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
28
- const handleChange = (e) => {
29
- setTheme(e.matches ? 'dark' : 'light');
30
- };
31
- mediaQuery.addEventListener('change', handleChange);
32
-
33
- return () => {
34
- mediaQuery.removeEventListener('change', handleChange);
35
- };
36
- } else {
37
- const subscription = Appearance.addChangeListener(({ colorScheme }) => {
38
- setTheme(colorScheme);
39
- });
40
-
41
- return () => subscription.remove();
42
- }
43
- }, []);
68
+ const themeValue = {
69
+ ...colors,
70
+ mode: mode,
71
+ isMobile,
72
+ space: resolveTokens(CDSPrimitiveSpacing, isMobile),
73
+ radius: CDSPrimiviteRadius,
74
+ typography: getSemanticTextStyles(colors, isMobile),
75
+ shadows: CDSPrimitiveShadows(mode),
76
+ };
44
77
 
45
78
  return (
46
- <ThemeContext.Provider value={{ theme: CDSsemanticColors[theme], toggleTheme, isDarkMode }}>
79
+ <ThemeContext.Provider value={{ theme: themeValue, toggleTheme, isDarkMode: mode === 'dark' }}>
47
80
  {children}
48
81
  </ThemeContext.Provider>
49
82
  );
50
83
  };
51
84
 
52
- export const useTheme = () => useContext(ThemeContext);
85
+ export const useTheme = () => useContext(ThemeContext);
package/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
 
2
- // import { registerRootComponent } from 'expo';
2
+ import { registerRootComponent } from 'expo';
3
3
 
4
- // import App from './App';
5
- // registerRootComponent(App);
4
+ import App from './App';
5
+ registerRootComponent(App);
6
6
 
7
7
  export {CDSBottomSheet} from './components/CDSBottomSheet';
8
8
  export {CDSButton} from './components/CDSButton';
@@ -11,8 +11,10 @@ export {CDSCarousel} from './components/CDSCarousel';
11
11
  export {CDSInput} from './components/CDSInput';
12
12
  export {CDSOnboarding} from './components/CDSOnboarding';
13
13
  export {CDSSwitch} from './components/CDSSwitch';
14
-
14
+ export {CDSNavBar} from './components/CDSNavBar';
15
+ export {CDSCardFeedback} from './components/CDSCardFeedback';
16
+ export {CDSSplashScreen} from './components/CDSSplashScreen';
17
+ export {CDSButtonGroup} from './components/CDSButtonGroup';
18
+ export {CDSSelect} from './components/CDSSelect';
15
19
 
16
20
  export {CDSThemeProvider, useTheme} from './context/CDSThemeContext';
17
- export {getSemanticTextStyles} from './tokens/CDSsemanticTextStyles';
18
- export {CDSsemanticColors} from './tokens/CDSsemanticColors';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cdslibrary",
3
3
  "license": "0BSD",
4
- "version": "1.0.56",
4
+ "version": "1.1.3",
5
5
  "main": "index.js",
6
6
  "author": "Nat Viramontes",
7
7
  "description": "A library of components for the CDS project",
@@ -13,17 +13,25 @@
13
13
  },
14
14
  "dependencies": {
15
15
  "@expo-google-fonts/inter": "^0.2.3",
16
- "@expo/metro-runtime": "~4.0.1",
17
- "@expo/vector-icons": "^14.0.4",
16
+ "@expo/metro-runtime": "~6.1.2",
17
+ "@expo/vector-icons": "^15.0.3",
18
+ "@lottiefiles/dotlottie-react": "^0.13.5",
19
+ "@react-navigation/native": "^7.1.28",
18
20
  "cli": "^1.0.1",
19
- "expo": "~52.0.38",
20
- "expo-status-bar": "~2.0.1",
21
- "react": "18.3.1",
22
- "react-native": "0.76.7",
23
- "react-native-web": "~0.19.13"
21
+ "expo": "^54.0.31",
22
+ "expo-linear-gradient": "~15.0.8",
23
+ "expo-status-bar": "~3.0.9",
24
+ "lottie-react-native": "^7.3.5",
25
+ "react": "19.1.0",
26
+ "react-dom": "^19.1.0",
27
+ "react-native": "0.81.5",
28
+ "react-native-reanimated": "^4.2.1",
29
+ "react-native-safe-area-context": "~5.6.0",
30
+ "react-native-screens": "~4.16.0",
31
+ "react-native-web": "^0.21.0"
24
32
  },
25
33
  "devDependencies": {
26
- "@babel/core": "^7.20.0"
34
+ "@babel/core": "^7.28.6"
27
35
  },
28
36
  "private": false
29
37
  }