cdslibrary 1.2.10 → 1.2.12
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.
- package/components/CDSBottomSheet.jsx +87 -112
- package/package.json +1 -1
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
import React, { useEffect,
|
|
2
|
-
import { View, Text, Animated, Dimensions, StyleSheet, Pressable, Platform } from "react-native";
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import { View, Text, Animated, Dimensions, StyleSheet, Pressable, Platform, ScrollView } from "react-native";
|
|
3
3
|
import { CDSButton } from "./CDSButton";
|
|
4
|
-
|
|
5
4
|
import { MaterialIcons } from "@expo/vector-icons";
|
|
6
5
|
import { useTheme } from "../context/CDSThemeContext";
|
|
7
|
-
import { ScrollView } from "react-native-web";
|
|
8
6
|
import { LinearGradient } from 'expo-linear-gradient';
|
|
9
7
|
|
|
10
|
-
|
|
11
8
|
const { height, width } = Dimensions.get("window");
|
|
12
|
-
|
|
13
9
|
const isMobile = width <= 878;
|
|
14
10
|
|
|
15
11
|
export const CDSBottomSheet = ({
|
|
@@ -24,89 +20,99 @@ export const CDSBottomSheet = ({
|
|
|
24
20
|
secondaryButtonLabel = "Cancelar",
|
|
25
21
|
onFinish,
|
|
26
22
|
}) => {
|
|
27
|
-
|
|
28
|
-
const handleLayout = (event) => {
|
|
29
|
-
const { height: layoutHeight } = event.nativeEvent.layout;
|
|
30
|
-
setChildHeight(layoutHeight);
|
|
31
|
-
};
|
|
32
|
-
|
|
33
23
|
const { theme } = useTheme();
|
|
34
|
-
const [childHeight, setChildHeight] = useState(0);
|
|
35
24
|
const [modalVisible, setModalVisible] = useState(isVisible);
|
|
36
25
|
const [modalOpacity] = useState(new Animated.Value(0));
|
|
37
|
-
const [slidePosition] = useState(new Animated.Value(height));
|
|
38
|
-
|
|
26
|
+
const [slidePosition] = useState(new Animated.Value(isMobile ? height : width));
|
|
39
27
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const targetValue = isMobile ? height : width;
|
|
28
|
+
// Función unificada para cerrar con animación
|
|
29
|
+
const handleClose = () => {
|
|
30
|
+
const targetValue = isMobile ? height : width;
|
|
43
31
|
|
|
44
32
|
Animated.parallel([
|
|
45
33
|
Animated.timing(modalOpacity, {
|
|
46
|
-
toValue:
|
|
47
|
-
duration:
|
|
34
|
+
toValue: 0,
|
|
35
|
+
duration: 350,
|
|
48
36
|
useNativeDriver: false,
|
|
49
37
|
}),
|
|
50
38
|
Animated.timing(slidePosition, {
|
|
51
|
-
toValue:
|
|
52
|
-
duration:
|
|
39
|
+
toValue: targetValue,
|
|
40
|
+
duration: 350,
|
|
53
41
|
useNativeDriver: false,
|
|
54
42
|
}),
|
|
55
|
-
]).start()
|
|
56
|
-
|
|
43
|
+
]).start(() => {
|
|
44
|
+
// Importante: onFinish se ejecuta SOLO cuando la animación termina
|
|
45
|
+
if (onFinish) onFinish();
|
|
46
|
+
setModalVisible(false);
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (modalVisible) {
|
|
52
|
+
Animated.parallel([
|
|
53
|
+
Animated.timing(modalOpacity, {
|
|
54
|
+
toValue: 1,
|
|
55
|
+
duration: 400,
|
|
56
|
+
useNativeDriver: false,
|
|
57
|
+
}),
|
|
58
|
+
Animated.timing(slidePosition, {
|
|
59
|
+
toValue: 0,
|
|
60
|
+
duration: 400,
|
|
61
|
+
useNativeDriver: false,
|
|
62
|
+
}),
|
|
63
|
+
]).start();
|
|
64
|
+
}
|
|
65
|
+
}, [modalVisible]);
|
|
66
|
+
|
|
57
67
|
return (
|
|
58
68
|
<>
|
|
59
69
|
<Animated.View
|
|
60
|
-
onLayout={handleLayout}
|
|
61
70
|
style={[
|
|
62
71
|
isMobile ? styles.container.typeBottomSheet : styles.container.typeDrawer,
|
|
63
72
|
{
|
|
64
73
|
opacity: modalOpacity,
|
|
65
74
|
transform: [
|
|
66
|
-
isMobile
|
|
67
|
-
? { translateY: slidePosition } // Sube en móvil
|
|
68
|
-
: { translateX: slidePosition } // Entra lateral en desktop
|
|
75
|
+
isMobile ? { translateY: slidePosition } : { translateX: slidePosition }
|
|
69
76
|
],
|
|
70
|
-
backgroundColor: theme.surface.neutral.primary,
|
|
77
|
+
backgroundColor: theme.surface.neutral.primary,
|
|
78
|
+
paddingHorizontal: theme.space.sm,
|
|
71
79
|
paddingTop: theme.space.xl,
|
|
72
80
|
gap: theme.space.sm,
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
...(isMobile ? {
|
|
82
|
+
borderTopLeftRadius: theme.radius.lg,
|
|
83
|
+
borderTopRightRadius: theme.radius.lg,
|
|
84
|
+
} : {
|
|
85
|
+
borderBottomLeftRadius: theme.radius.lg,
|
|
86
|
+
borderTopLeftRadius: theme.radius.lg,
|
|
87
|
+
paddingBottom: theme.space.md,
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
]}
|
|
80
91
|
>
|
|
81
|
-
|
|
82
92
|
{hasClose && (
|
|
83
93
|
<MaterialIcons
|
|
84
|
-
name=
|
|
94
|
+
name="close"
|
|
85
95
|
size={theme.typography.icon.lg}
|
|
86
96
|
color={theme.text.neutral.primary}
|
|
87
|
-
onPress={
|
|
88
|
-
|
|
89
|
-
setModalVisible(false);
|
|
90
|
-
}}
|
|
91
|
-
style={{ position: "absolute", right: theme.space.sm, top: theme.space.sm }}
|
|
97
|
+
onPress={handleClose}
|
|
98
|
+
style={{ position: "absolute", right: theme.space.sm, top: theme.space.sm, zIndex: 10 }}
|
|
92
99
|
/>
|
|
93
100
|
)}
|
|
94
|
-
|
|
101
|
+
|
|
102
|
+
{!!title && <Text style={theme.typography.bold.lg}>{title}</Text>}
|
|
103
|
+
|
|
95
104
|
<View style={styles.scrollWrapper}>
|
|
96
105
|
<ScrollView
|
|
97
106
|
style={styles.scrollArea}
|
|
98
|
-
contentContainerStyle={[styles.scrollContent, {
|
|
99
|
-
flexGrow: 0,
|
|
100
|
-
paddingBottom: theme.space.md
|
|
101
|
-
}]}
|
|
107
|
+
contentContainerStyle={[styles.scrollContent, { paddingBottom: theme.space.md }]}
|
|
102
108
|
showsVerticalScrollIndicator={true}
|
|
103
|
-
persistentScrollbar={true}
|
|
104
|
-
indicatorStyle={theme.name === 'dark' ? 'white' : 'black'}
|
|
105
109
|
>
|
|
106
|
-
{description && (
|
|
107
|
-
<Text style={[theme.typography.regular.md, { marginBottom: theme.space.md }]}>
|
|
110
|
+
{!!description && (
|
|
111
|
+
<Text style={[theme.typography.regular.md, { marginBottom: theme.space.md }]}>
|
|
112
|
+
{description}
|
|
113
|
+
</Text>
|
|
108
114
|
)}
|
|
109
|
-
{customSlot
|
|
115
|
+
{customSlot}
|
|
110
116
|
</ScrollView>
|
|
111
117
|
<LinearGradient
|
|
112
118
|
colors={['transparent', theme.surface.neutral.primary]}
|
|
@@ -114,27 +120,25 @@ export const CDSBottomSheet = ({
|
|
|
114
120
|
pointerEvents="none"
|
|
115
121
|
/>
|
|
116
122
|
</View>
|
|
123
|
+
|
|
117
124
|
{type !== "informative" && (
|
|
118
125
|
<View style={isMobile ? styles.actionsContainer.typeBottomSheet : styles.actionsContainer.typeDrawer}>
|
|
119
126
|
<CDSButton
|
|
120
127
|
label={primaryButtonLabel}
|
|
121
128
|
onPress={() => {
|
|
122
|
-
primaryButtonOnPress()
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
}
|
|
129
|
+
if (primaryButtonOnPress) primaryButtonOnPress();
|
|
130
|
+
handleClose();
|
|
131
|
+
}}
|
|
126
132
|
/>
|
|
127
133
|
<CDSButton
|
|
128
134
|
label={secondaryButtonLabel}
|
|
129
135
|
type="ghost"
|
|
130
|
-
onPress={
|
|
131
|
-
onFinish();
|
|
132
|
-
setModalVisible(false);
|
|
133
|
-
}}
|
|
136
|
+
onPress={handleClose}
|
|
134
137
|
/>
|
|
135
138
|
</View>
|
|
136
139
|
)}
|
|
137
140
|
</Animated.View>
|
|
141
|
+
|
|
138
142
|
<Animated.View
|
|
139
143
|
style={[
|
|
140
144
|
styles.overlay,
|
|
@@ -144,103 +148,74 @@ export const CDSBottomSheet = ({
|
|
|
144
148
|
},
|
|
145
149
|
]}
|
|
146
150
|
>
|
|
147
|
-
<Pressable onPress={
|
|
151
|
+
<Pressable onPress={handleClose} style={{ flex: 1 }} />
|
|
148
152
|
</Animated.View>
|
|
149
153
|
</>
|
|
150
154
|
);
|
|
151
|
-
}
|
|
155
|
+
};
|
|
152
156
|
|
|
153
157
|
const styles = StyleSheet.create({
|
|
154
158
|
overlay: {
|
|
155
159
|
position: "absolute",
|
|
156
160
|
top: 0,
|
|
157
161
|
left: 0,
|
|
158
|
-
|
|
159
|
-
|
|
162
|
+
right: 0,
|
|
163
|
+
bottom: 0,
|
|
164
|
+
zIndex: 98,
|
|
160
165
|
},
|
|
161
|
-
|
|
162
166
|
container: {
|
|
163
167
|
typeBottomSheet: {
|
|
164
168
|
maxHeight: '90%',
|
|
165
|
-
flex: 1,
|
|
166
|
-
justifyContent: "flex-end",
|
|
167
|
-
alignItems: "center",
|
|
168
|
-
zIndex: 99,
|
|
169
169
|
width: "100%",
|
|
170
170
|
position: "absolute",
|
|
171
171
|
bottom: 0,
|
|
172
|
+
left: 0,
|
|
173
|
+
right: 0,
|
|
174
|
+
zIndex: 99,
|
|
172
175
|
},
|
|
173
176
|
typeDrawer: {
|
|
174
|
-
position: "absolute",
|
|
175
|
-
right: 0,
|
|
177
|
+
position: "absolute",
|
|
178
|
+
right: 0,
|
|
176
179
|
top: 0,
|
|
177
180
|
bottom: 0,
|
|
178
|
-
justifyContent: "center",
|
|
179
|
-
zIndex: 99,
|
|
180
181
|
width: 600,
|
|
182
|
+
zIndex: 99,
|
|
183
|
+
...Platform.select({
|
|
184
|
+
web: { boxShadow: '-10px 0px 15px rgba(0,0,0,0.1)' }
|
|
185
|
+
})
|
|
181
186
|
},
|
|
182
187
|
},
|
|
183
|
-
|
|
184
188
|
scrollWrapper: {
|
|
185
|
-
flex: 1,
|
|
186
|
-
position: 'relative',
|
|
189
|
+
flex: 1,
|
|
190
|
+
position: 'relative',
|
|
187
191
|
width: '100%',
|
|
188
192
|
},
|
|
189
193
|
scrollArea: {
|
|
190
|
-
|
|
191
|
-
flexGrow: 1,
|
|
194
|
+
flex: 1,
|
|
192
195
|
width: '100%',
|
|
193
|
-
paddingBottom: 20,
|
|
194
196
|
},
|
|
195
|
-
|
|
196
197
|
scrollContent: {
|
|
197
|
-
paddingBottom: 20, // Aire al final del contenido
|
|
198
198
|
gap: 16,
|
|
199
199
|
},
|
|
200
|
-
|
|
201
200
|
fadeGradient: {
|
|
202
201
|
position: 'absolute',
|
|
203
202
|
bottom: 0,
|
|
204
203
|
left: 0,
|
|
205
204
|
right: 0,
|
|
206
|
-
height:
|
|
207
|
-
},
|
|
208
|
-
|
|
209
|
-
infoContainer: {
|
|
210
|
-
typeBottomSheet: {
|
|
211
|
-
width: "100%",
|
|
212
|
-
zIndex: 100,
|
|
213
|
-
pointerEvents: "all",
|
|
214
|
-
},
|
|
215
|
-
typeDrawer: {
|
|
216
|
-
flex: 1, // Ocupa todo el alto disponible en el Drawer
|
|
217
|
-
width: "100%",
|
|
218
|
-
zIndex: 100,
|
|
219
|
-
pointerEvents: "all",
|
|
220
|
-
// Opcional: una sombra elegante para Desktop
|
|
221
|
-
...Platform.select({
|
|
222
|
-
web: { boxShadow: '-10px 0px 15px rgba(0,0,0,0.2)' }
|
|
223
|
-
})
|
|
224
|
-
},
|
|
205
|
+
height: 40,
|
|
225
206
|
},
|
|
226
207
|
actionsContainer: {
|
|
227
208
|
typeBottomSheet: {
|
|
228
|
-
|
|
209
|
+
flexDirection: "row",
|
|
229
210
|
width: "100%",
|
|
230
211
|
gap: 8,
|
|
212
|
+
paddingBottom: 20,
|
|
231
213
|
},
|
|
232
214
|
typeDrawer: {
|
|
233
|
-
marginTop: 'auto',
|
|
234
215
|
flexDirection: "row-reverse",
|
|
235
216
|
width: "100%",
|
|
236
217
|
gap: 8,
|
|
218
|
+
marginTop: 'auto',
|
|
237
219
|
},
|
|
238
220
|
},
|
|
239
|
-
|
|
240
|
-
customSlot: {
|
|
241
|
-
width: '100%',
|
|
242
|
-
height: 'auto',
|
|
243
|
-
},
|
|
244
|
-
|
|
245
|
-
});
|
|
246
|
-
|
|
221
|
+
});
|