cdslibrary 1.2.22 → 1.2.24

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.
@@ -81,14 +81,14 @@ export const CDSCardFeedback = ({
81
81
  }]
82
82
  }]}>
83
83
 
84
- <View style={[styles.titleContainer, { gap: theme.space.xs }]}>
84
+ {title && <View style={[styles.titleContainer, { gap: theme.space.xs }]}>
85
85
  <MaterialIcons
86
86
  name={currentStyle.icon}
87
87
  size={theme.typography.icon.md}
88
88
  color={currentStyle.text}
89
89
  />
90
- <Text style={[styles.title, theme.typography.bold.lg, { color: currentStyle.text }]}>{title || 'Title'}</Text>
91
- </View>
90
+ <Text style={[styles.title, theme.typography.bold.lg, { color: currentStyle.text }]}>{title}</Text>
91
+ </View>}
92
92
  {description && <Text style={[styles.description, theme.typography.regular.md, { color: currentStyle.text }]}>{description}</Text>}
93
93
  <View style={[styles.actionsContainer, { gap: theme.space.xs }]}>
94
94
  {secondAction && <TouchableOpacity onPress={onPressSecondAction}>{<Text style={[theme.typography.bold.sm, { color: currentStyle.text }]}>{secondAction}</Text>}</TouchableOpacity>}
@@ -1,133 +1,153 @@
1
- import React, { useState, useRef } from "react";
2
- import { View, Text, TouchableOpacity, StyleSheet, FlatList, Animated } from "react-native";
1
+ import React, { useState, useRef, useEffect } from "react";
2
+ import {
3
+ View, Text, TouchableOpacity, StyleSheet, FlatList,
4
+ Animated, Modal, Pressable, Dimensions, Platform
5
+ } from "react-native";
3
6
  import { MaterialIcons } from "@expo/vector-icons";
4
7
  import { useTheme } from "../context/CDSThemeContext";
5
8
 
6
- export const CDSSelect = ({ label, options = [], onSelect, placeholder = "Selecciona una opción" }) => {
9
+ // Componente Interno para la Opción con soporte de Hover
10
+ const SelectOption = ({ item, isSelected, onSelect, theme }) => {
11
+ const [isHovered, setIsHovered] = useState(false);
12
+
13
+ return (
14
+ <Pressable
15
+ onPress={() => onSelect(item)}
16
+ // Eventos de Hover para Web
17
+ onMouseEnter={() => Platform.OS === 'web' && setIsHovered(true)}
18
+ onMouseLeave={() => Platform.OS === 'web' && setIsHovered(false)}
19
+ style={({ pressed }) => [
20
+ styles.optionItem,
21
+ {
22
+ paddingHorizontal: theme.space.xs,
23
+ backgroundColor: (pressed || isHovered)
24
+ ? theme.surface.neutral.secondary
25
+ : isSelected ? theme.surface.neutral.tertiary : 'transparent'
26
+ }
27
+ ]}
28
+ >
29
+ <View style={styles.optionContent}>
30
+ <Text style={[
31
+ theme.typography.regular.sm,
32
+ {
33
+ color: theme.text.neutral.primary,
34
+ fontWeight: isSelected ? '700' : '400'
35
+ }
36
+ ]}>
37
+ {item.label}
38
+ </Text>
39
+ {isSelected && <MaterialIcons name="check" size={18} color={theme.text.neutral.primary} />}
40
+ </View>
41
+ </Pressable>
42
+ );
43
+ };
44
+
45
+ export const CDSSelect = ({ label, options = [], onSelect, placeholder, selectedValue = null }) => {
7
46
  const { theme } = useTheme();
8
47
  const [isOpen, setIsOpen] = useState(false);
9
- const [selected, setSelected] = useState(null);
10
-
11
- // Referencias para la animación
48
+ const [dropdownTop, setDropdownTop] = useState(0);
49
+ const [dropdownWidth, setDropdownWidth] = useState(0);
50
+ const [dropdownLeft, setDropdownLeft] = useState(0);
51
+
52
+ const containerRef = useRef(null);
12
53
  const animatedValue = useRef(new Animated.Value(0)).current;
13
54
 
55
+ const [selectedItem, setSelectedItem] = useState(
56
+ options.find(opt => opt.value === selectedValue) || null
57
+ );
58
+
59
+ useEffect(() => {
60
+ const found = options.find(opt => opt.value === selectedValue);
61
+ setSelectedItem(found || null);
62
+ }, [selectedValue, options]);
63
+
14
64
  const toggleDropdown = () => {
15
65
  if (isOpen) {
16
- // Animación de cierre
17
- Animated.timing(animatedValue, {
18
- toValue: 0,
19
- duration: 200,
20
- useNativeDriver: true,
21
- }).start(() => setIsOpen(false));
66
+ Animated.timing(animatedValue, { toValue: 0, duration: 200, useNativeDriver: true })
67
+ .start(() => setIsOpen(false));
22
68
  } else {
23
- setIsOpen(true);
24
- // Animación de apertura
25
- Animated.timing(animatedValue, {
26
- toValue: 1,
27
- duration: 300,
28
- useNativeDriver: true,
29
- }).start();
69
+ containerRef.current.measure((fx, fy, width, height, px, py) => {
70
+ setDropdownTop(py + height);
71
+ setDropdownWidth(width);
72
+ setDropdownLeft(px); // Usamos px para alineación exacta en X
73
+ setIsOpen(true);
74
+ Animated.timing(animatedValue, { toValue: 1, duration: 300, useNativeDriver: true }).start();
75
+ });
30
76
  }
31
77
  };
32
78
 
33
79
  const handleSelect = (item) => {
34
- setSelected(item);
80
+ setSelectedItem(item);
35
81
  toggleDropdown();
36
82
  if (onSelect) onSelect(item.value);
37
83
  };
38
84
 
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
85
  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>}
86
+ <View style={styles.mainContainer} ref={containerRef} collapsable={false}>
87
+ {label && <Text style={[theme.typography.label, { color: theme.text.neutral.primary, marginBottom: theme.space.xs }]}>{label}</Text>}
54
88
 
55
89
  <TouchableOpacity
56
90
  activeOpacity={0.8}
57
91
  style={[
58
92
  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 }
93
+ {
94
+ backgroundColor: theme.surface.neutral.primary,
95
+ borderColor: selectedItem ? theme.outline.neutral.tertiaryVariant : theme.outline.neutral.primary,
96
+ borderRadius: theme.radius.sm,
97
+ paddingHorizontal: theme.space.xs
98
+ }
60
99
  ]}
61
100
  onPress={toggleDropdown}
62
101
  >
63
- <Text style={selected ? theme.typography.inputText.value : theme.typography.inputText.placeholder}>
64
- {selected ? selected.label : placeholder}
102
+ <Text style={selectedItem ? theme.typography.inputText.value : theme.typography.inputText.placeholder}>
103
+ {selectedItem ? selectedItem.label : placeholder}
65
104
  </Text>
66
- <MaterialIcons
67
- name={isOpen ? "expand-less" : "expand-more"}
68
- size={theme.typography.icon.md}
69
- color={theme.text.neutral.primary}
70
- />
105
+ <MaterialIcons name={isOpen ? "expand-less" : "expand-more"} size={24} color={theme.text.neutral.primary} />
71
106
  </TouchableOpacity>
72
107
 
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
- )}
108
+ <Modal transparent visible={isOpen} animationType="none" onRequestClose={toggleDropdown}>
109
+ <Pressable style={styles.modalOverlay} onPress={toggleDropdown}>
110
+ <Animated.View
111
+ style={[
112
+ styles.dropdown,
113
+ {
114
+ opacity: animatedValue,
115
+ transform: [{ translateY: animatedValue.interpolate({ inputRange: [0, 1], outputRange: [-10, 0] }) }],
116
+ backgroundColor: theme.surface.neutral.primary,
117
+ borderRadius: theme.radius.sm,
118
+ borderColor: theme.outline.neutral.primary,
119
+ top: dropdownTop,
120
+ width: dropdownWidth,
121
+ left: dropdownLeft,
122
+ elevation: 8,
123
+ maxHeight: 240,
124
+ }
125
+ ]}
126
+ >
127
+ <FlatList
128
+ data={options}
129
+ keyExtractor={(item) => item.value.toString()}
130
+ renderItem={({ item }) => (
131
+ <SelectOption
132
+ item={item}
133
+ isSelected={item.value === selectedValue}
134
+ onSelect={handleSelect}
135
+ theme={theme}
136
+ />
137
+ )}
138
+ />
139
+ </Animated.View>
140
+ </Pressable>
141
+ </Modal>
104
142
  </View>
105
143
  );
106
144
  };
107
145
 
108
146
  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
- }
147
+ mainContainer: { width: '100%' },
148
+ inputContainer: { height: 48, borderWidth: 1, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' },
149
+ modalOverlay: { flex: 1, backgroundColor: 'transparent' },
150
+ dropdown: { position: 'absolute', borderWidth: 1, overflow: 'hidden' },
151
+ optionItem: { height: 48, justifyContent: 'center' },
152
+ optionContent: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }
133
153
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cdslibrary",
3
3
  "license": "0BSD",
4
- "version": "1.2.22",
4
+ "version": "1.2.24",
5
5
  "main": "index.js",
6
6
  "author": "Nat Viramontes",
7
7
  "description": "A library of components for the CDS project",