cdslibrary 1.2.89 → 1.2.90

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.
@@ -80,7 +80,7 @@ export const CDSInput = ({ label, type, keyboard, placeholder, value, onChangeTe
80
80
  return {
81
81
  borderColor,
82
82
  borderWidth: 1 + (flashValue.value * 2),
83
- transform: [{ scale: 1 + (flashValue.value * 0.01) }]
83
+ transform: [{ scale: 1 + (flashValue.value * 0.02) }]
84
84
  };
85
85
  });
86
86
 
@@ -1,151 +1,122 @@
1
- import React, { useState, useRef, useEffect } from "react";
1
+ import React, { useState, useEffect } from "react";
2
2
  import {
3
3
  View, Text, TouchableOpacity, StyleSheet, ScrollView,
4
- Animated, Pressable, Platform
4
+ Pressable, Modal, SafeAreaView
5
5
  } from "react-native";
6
6
  import { MaterialIcons } from "@expo/vector-icons";
7
7
  import { useTheme } from "../context/CDSThemeContext";
8
8
 
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
- onMouseEnter={() => Platform.OS === 'web' && setIsHovered(true)}
17
- onMouseLeave={() => Platform.OS === 'web' && setIsHovered(false)}
18
- style={({ pressed }) => [
19
- styles.optionItem,
20
- {
21
- paddingHorizontal: theme.space.sm,
22
- backgroundColor: (pressed || isHovered)
23
- ? theme.surface.neutral.secondary
24
- : isSelected ? theme.surface.neutral.tertiary : 'transparent'
25
- }
26
- ]}
27
- >
28
- <View style={styles.optionContent}>
29
- <Text style={[
30
- theme.typography.regular.sm,
31
- {
32
- color: theme.text.neutral.primary,
33
- fontWeight: isSelected ? '700' : '400'
34
- }
35
- ]}>
36
- {item.label}
37
- </Text>
38
- {isSelected && <MaterialIcons name="check" size={18} color={theme.text.neutral.primary} />}
39
- </View>
40
- </Pressable>
41
- );
42
- };
43
-
44
- export const CDSSelect = ({ label, options = [], onSelect, selectedValue = null }) => {
9
+ export const CDSSelect = ({ label, options = [], onSelect, selectedValue = null, placeholder = 'Selecciona una opción' }) => {
45
10
  const { theme } = useTheme();
46
- const [isOpen, setIsOpen] = useState(false);
47
- const animatedValue = useRef(new Animated.Value(0)).current;
48
- const containerRef = useRef(null);
49
-
11
+ const [modalVisible, setModalVisible] = useState(false);
50
12
  const [selectedItem, setSelectedItem] = useState(
51
13
  options.find(opt => opt.value === selectedValue) || null
52
14
  );
53
15
 
16
+ // Altura fija por cada fila (puedes ajustarla según tu diseño)
17
+ const ITEM_HEIGHT = 52;
18
+ // Mostramos 7.5 opciones para que la última se vea cortada y sugiera scroll
19
+ const MAX_VISIBLE_OPTIONS = 7.5;
20
+ const MAX_HEIGHT = ITEM_HEIGHT * MAX_VISIBLE_OPTIONS;
21
+
54
22
  useEffect(() => {
55
23
  const found = options.find(opt => opt.value === selectedValue);
56
24
  setSelectedItem(found || null);
57
25
  }, [selectedValue, options]);
58
26
 
59
- const toggleDropdown = () => {
60
- if (isOpen) {
61
- Animated.timing(animatedValue, {
62
- toValue: 0,
63
- duration: 250,
64
- useNativeDriver: false
65
- }).start(() => setIsOpen(false));
66
- } else {
67
- setIsOpen(true);
68
- requestAnimationFrame(() => {
69
- Animated.timing(animatedValue, {
70
- toValue: 1,
71
- duration: 300,
72
- useNativeDriver: false
73
- }).start();
74
- });
75
- }
76
- };
77
-
78
27
  const handleSelect = (item) => {
79
28
  setSelectedItem(item);
80
- toggleDropdown();
29
+ setModalVisible(false);
81
30
  if (onSelect) onSelect(item.value);
82
31
  };
83
32
 
84
- const animatedLayout = {
85
- maxHeight: animatedValue.interpolate({
86
- inputRange: [0, 1],
87
- outputRange: [0, 240]
88
- }),
89
- opacity: animatedValue,
90
- marginTop: animatedValue.interpolate({
91
- inputRange: [0, 1],
92
- outputRange: [0, theme.space.sm]
93
- }),
94
- };
95
-
96
33
  return (
97
- <View style={styles.mainContainer} ref={containerRef} collapsable={false}>
34
+ <View style={styles.mainContainer}>
98
35
  {label && (
99
36
  <Text style={[theme.typography.label, { color: theme.text.neutral.primary, marginBottom: theme.space.sm }]}>
100
37
  {label}
101
38
  </Text>
102
39
  )}
103
40
 
41
+ {/* Trigger del Select */}
104
42
  <TouchableOpacity
105
- activeOpacity={0.8}
43
+ activeOpacity={0.7}
106
44
  style={[
107
45
  styles.inputContainer,
108
46
  {
109
47
  backgroundColor: theme.surface.neutral.primary,
110
- borderColor: selectedItem ? theme.outline.neutral.tertiaryVariant : theme.outline.neutral.primary,
48
+ borderColor: theme.outline.neutral.primary,
111
49
  borderRadius: theme.radius.sm,
112
50
  paddingHorizontal: theme.space.sm
113
51
  }
114
52
  ]}
115
- onPress={toggleDropdown}
53
+ onPress={() => setModalVisible(true)}
116
54
  >
117
55
  <Text style={selectedItem ? theme.typography.inputText.value : theme.typography.inputText.placeholder}>
118
- {selectedItem ? selectedItem.label : 'Selecciona una opción'}
56
+ {selectedItem ? selectedItem.label : placeholder}
119
57
  </Text>
120
- <MaterialIcons name={isOpen ? "expand-less" : "expand-more"} size={24} color={theme.text.neutral.primary} />
58
+ <MaterialIcons name="expand-more" size={24} color={theme.text.neutral.primary} />
121
59
  </TouchableOpacity>
122
60
 
123
- {isOpen && (
124
- <Animated.View
125
- style={[
126
- styles.dropdownInline,
127
- animatedLayout,
128
- {
129
- backgroundColor: theme.surface.neutral.primary,
130
- borderRadius: theme.radius.sm,
131
- borderColor: theme.outline.neutral.primary,
132
- overflow: 'hidden',
133
- }
134
- ]}
61
+ {/* Selector Nativo / Modal */}
62
+ <Modal
63
+ visible={modalVisible}
64
+ transparent={true}
65
+ animationType="fade"
66
+ onRequestClose={() => setModalVisible(false)}
67
+ >
68
+ <Pressable
69
+ style={styles.modalOverlay}
70
+ onPress={() => setModalVisible(false)}
135
71
  >
136
- <ScrollView bounces={false} nestedScrollEnabled={true}>
137
- {options.map((item, index) => (
138
- <SelectOption
139
- key={item.value?.toString() || index.toString()}
140
- item={item}
141
- isSelected={selectedItem?.value === item.value}
142
- onSelect={handleSelect}
143
- theme={theme}
144
- />
145
- ))}
146
- </ScrollView>
147
- </Animated.View>
148
- )}
72
+ <SafeAreaView style={styles.modalContent}>
73
+ <View style={[
74
+ styles.optionsContainer,
75
+ {
76
+ backgroundColor: theme.surface.neutral.primary,
77
+ borderRadius: theme.radius.md,
78
+ maxHeight: MAX_HEIGHT + 60 // +60 para compensar el header del modal
79
+ }
80
+ ]}>
81
+ {/* Header del Modal */}
82
+ <View style={[styles.modalHeader, { borderBottomColor: theme.outline.neutral.primary }]}>
83
+ <Text style={theme.typography.semiBold.md}>{'Selecciona una opción'}</Text>
84
+ <TouchableOpacity onPress={() => setModalVisible(false)} hitSlop={{top: 10, bottom: 10, left: 10, right: 10}}>
85
+ <MaterialIcons name="close" size={24} color={theme.text.neutral.primary} />
86
+ </TouchableOpacity>
87
+ </View>
88
+
89
+ {/* Lista Scrolleable */}
90
+ <ScrollView
91
+ bounces={false}
92
+ showsVerticalScrollIndicator={true}
93
+ nestedScrollEnabled={true}
94
+ >
95
+ {options.map((item, index) => (
96
+ <TouchableOpacity
97
+ key={item.value?.toString() || index.toString()}
98
+ onPress={() => handleSelect(item)}
99
+ style={[
100
+ styles.optionItem,
101
+ {
102
+ height: ITEM_HEIGHT,
103
+ backgroundColor: selectedItem?.value === item.value ? theme.surface.neutral.secondary : 'transparent'
104
+ }
105
+ ]}
106
+ >
107
+ <Text style={[theme.typography.regular.md, { color: theme.text.neutral.primary }]}>
108
+ {item.label}
109
+ </Text>
110
+ {selectedItem?.value === item.value && (
111
+ <MaterialIcons name="check" size={20} color={theme.text.neutral.primary} />
112
+ )}
113
+ </TouchableOpacity>
114
+ ))}
115
+ </ScrollView>
116
+ </View>
117
+ </SafeAreaView>
118
+ </Pressable>
119
+ </Modal>
149
120
  </View>
150
121
  );
151
122
  };
@@ -153,9 +124,37 @@ export const CDSSelect = ({ label, options = [], onSelect, selectedValue = null
153
124
  const styles = StyleSheet.create({
154
125
  mainContainer: { width: '100%' },
155
126
  inputContainer: { width: '100%', height: 48, borderWidth: 1, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' },
156
- dropdownInline: {
157
- borderWidth: 1,
127
+ modalOverlay: {
128
+ flex: 1,
129
+ backgroundColor: 'rgba(0,0,0,0.6)', // Un poco más oscuro para mejor enfoque
130
+ justifyContent: 'center',
131
+ alignItems: 'center',
132
+ padding: 24
133
+ },
134
+ modalContent: {
135
+ width: '100%',
136
+ maxWidth: 400,
137
+ },
138
+ optionsContainer: {
139
+ width: '100%',
140
+ overflow: 'hidden',
141
+ elevation: 5,
142
+ shadowColor: '#000',
143
+ shadowOffset: { width: 0, height: 4 },
144
+ shadowOpacity: 0.3,
145
+ shadowRadius: 4.65,
146
+ },
147
+ modalHeader: {
148
+ flexDirection: 'row',
149
+ justifyContent: 'space-between',
150
+ alignItems: 'center',
151
+ padding: 16,
152
+ borderBottomWidth: 1,
158
153
  },
159
- optionItem: { height: 48, justifyContent: 'center' },
160
- optionContent: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }
154
+ optionItem: {
155
+ paddingHorizontal: 16,
156
+ flexDirection: 'row',
157
+ justifyContent: 'space-between',
158
+ alignItems: 'center',
159
+ }
161
160
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cdslibrary",
3
3
  "license": "0BSD",
4
- "version": "1.2.89",
4
+ "version": "1.2.90",
5
5
  "main": "index.js",
6
6
  "author": "Nat Viramontes",
7
7
  "description": "A library of components for the CDS project",