cdslibrary 1.2.11 → 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 -113
- 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,25 @@ 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
|
-
onPress={
|
|
132
|
-
onFinish && onFinish();
|
|
133
|
-
setModalVisible(false);
|
|
134
|
-
}}
|
|
136
|
+
onPress={handleClose}
|
|
135
137
|
/>
|
|
136
138
|
</View>
|
|
137
139
|
)}
|
|
138
140
|
</Animated.View>
|
|
141
|
+
|
|
139
142
|
<Animated.View
|
|
140
143
|
style={[
|
|
141
144
|
styles.overlay,
|
|
@@ -145,103 +148,74 @@ export const CDSBottomSheet = ({
|
|
|
145
148
|
},
|
|
146
149
|
]}
|
|
147
150
|
>
|
|
148
|
-
<Pressable onPress={
|
|
151
|
+
<Pressable onPress={handleClose} style={{ flex: 1 }} />
|
|
149
152
|
</Animated.View>
|
|
150
153
|
</>
|
|
151
154
|
);
|
|
152
|
-
}
|
|
155
|
+
};
|
|
153
156
|
|
|
154
157
|
const styles = StyleSheet.create({
|
|
155
158
|
overlay: {
|
|
156
159
|
position: "absolute",
|
|
157
160
|
top: 0,
|
|
158
161
|
left: 0,
|
|
159
|
-
|
|
160
|
-
|
|
162
|
+
right: 0,
|
|
163
|
+
bottom: 0,
|
|
164
|
+
zIndex: 98,
|
|
161
165
|
},
|
|
162
|
-
|
|
163
166
|
container: {
|
|
164
167
|
typeBottomSheet: {
|
|
165
168
|
maxHeight: '90%',
|
|
166
|
-
flex: 1,
|
|
167
|
-
justifyContent: "flex-end",
|
|
168
|
-
alignItems: "center",
|
|
169
|
-
zIndex: 99,
|
|
170
169
|
width: "100%",
|
|
171
170
|
position: "absolute",
|
|
172
171
|
bottom: 0,
|
|
172
|
+
left: 0,
|
|
173
|
+
right: 0,
|
|
174
|
+
zIndex: 99,
|
|
173
175
|
},
|
|
174
176
|
typeDrawer: {
|
|
175
|
-
position: "absolute",
|
|
176
|
-
right: 0,
|
|
177
|
+
position: "absolute",
|
|
178
|
+
right: 0,
|
|
177
179
|
top: 0,
|
|
178
180
|
bottom: 0,
|
|
179
|
-
justifyContent: "center",
|
|
180
|
-
zIndex: 99,
|
|
181
181
|
width: 600,
|
|
182
|
+
zIndex: 99,
|
|
183
|
+
...Platform.select({
|
|
184
|
+
web: { boxShadow: '-10px 0px 15px rgba(0,0,0,0.1)' }
|
|
185
|
+
})
|
|
182
186
|
},
|
|
183
187
|
},
|
|
184
|
-
|
|
185
188
|
scrollWrapper: {
|
|
186
|
-
flex: 1,
|
|
187
|
-
position: 'relative',
|
|
189
|
+
flex: 1,
|
|
190
|
+
position: 'relative',
|
|
188
191
|
width: '100%',
|
|
189
192
|
},
|
|
190
193
|
scrollArea: {
|
|
191
|
-
|
|
192
|
-
flexGrow: 1,
|
|
194
|
+
flex: 1,
|
|
193
195
|
width: '100%',
|
|
194
|
-
paddingBottom: 20,
|
|
195
196
|
},
|
|
196
|
-
|
|
197
197
|
scrollContent: {
|
|
198
|
-
paddingBottom: 20, // Aire al final del contenido
|
|
199
198
|
gap: 16,
|
|
200
199
|
},
|
|
201
|
-
|
|
202
200
|
fadeGradient: {
|
|
203
201
|
position: 'absolute',
|
|
204
202
|
bottom: 0,
|
|
205
203
|
left: 0,
|
|
206
204
|
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
|
-
},
|
|
205
|
+
height: 40,
|
|
226
206
|
},
|
|
227
207
|
actionsContainer: {
|
|
228
208
|
typeBottomSheet: {
|
|
229
|
-
|
|
209
|
+
flexDirection: "row",
|
|
230
210
|
width: "100%",
|
|
231
211
|
gap: 8,
|
|
212
|
+
paddingBottom: 20,
|
|
232
213
|
},
|
|
233
214
|
typeDrawer: {
|
|
234
|
-
marginTop: 'auto',
|
|
235
215
|
flexDirection: "row-reverse",
|
|
236
216
|
width: "100%",
|
|
237
217
|
gap: 8,
|
|
218
|
+
marginTop: 'auto',
|
|
238
219
|
},
|
|
239
220
|
},
|
|
240
|
-
|
|
241
|
-
customSlot: {
|
|
242
|
-
width: '100%',
|
|
243
|
-
height: 'auto',
|
|
244
|
-
},
|
|
245
|
-
|
|
246
|
-
});
|
|
247
|
-
|
|
221
|
+
});
|