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