cdslibrary 1.1.3 → 1.1.4
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/CDSAccordion.jsx +150 -0
- package/components/CDSOffer.jsx +64 -0
- package/components/CDSTable.jsx +129 -0
- package/index.js +4 -1
- package/package.json +3 -2
- package/tokens/CDSsemanticColors.js +17 -7
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { View, Text, StyleSheet, Pressable } from "react-native";
|
|
3
|
+
import Animated, {
|
|
4
|
+
useAnimatedStyle,
|
|
5
|
+
useSharedValue,
|
|
6
|
+
withTiming,
|
|
7
|
+
Easing,
|
|
8
|
+
interpolate,
|
|
9
|
+
} from "react-native-reanimated";
|
|
10
|
+
import { useTheme } from "../context/CDSThemeContext";
|
|
11
|
+
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
|
12
|
+
|
|
13
|
+
export const CDSAccordion = ({ title, description, children, defaultExpanded = false }) => {
|
|
14
|
+
const { theme } = useTheme();
|
|
15
|
+
const [expanded, setExpanded] = useState(defaultExpanded);
|
|
16
|
+
const [contentHeight, setContentHeight] = useState(0); // Altura real medida
|
|
17
|
+
|
|
18
|
+
const animation = useSharedValue(defaultExpanded ? 1 : 0);
|
|
19
|
+
|
|
20
|
+
const toggleAccordion = () => {
|
|
21
|
+
const newValue = !expanded;
|
|
22
|
+
setExpanded(newValue);
|
|
23
|
+
|
|
24
|
+
animation.value = withTiming(newValue ? 1 : 0, {
|
|
25
|
+
duration: 250,
|
|
26
|
+
easing: Easing.bezier(0.4, 0, 0.2, 1)
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Función que captura el tamaño real del contenido al renderizarse
|
|
31
|
+
const onLayout = (event) => {
|
|
32
|
+
const { height } = event.nativeEvent.layout;
|
|
33
|
+
// Solo actualizamos si la altura es distinta de cero y ha cambiado
|
|
34
|
+
if (height > 0 && height !== contentHeight) {
|
|
35
|
+
setContentHeight(height);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const bodyStyle = useAnimatedStyle(() => {
|
|
40
|
+
return {
|
|
41
|
+
opacity: animation.value,
|
|
42
|
+
// Animamos de 0 a la altura medida exactamente
|
|
43
|
+
height: interpolate(animation.value, [0, 1], [0, contentHeight]),
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<View style={[
|
|
49
|
+
styles.container,
|
|
50
|
+
{
|
|
51
|
+
backgroundColor: theme.surface.neutral.primary,
|
|
52
|
+
borderRadius: theme.radius.lg,
|
|
53
|
+
borderColor: theme.outline.neutral.primary,
|
|
54
|
+
borderWidth: 1
|
|
55
|
+
}
|
|
56
|
+
]}>
|
|
57
|
+
<Pressable
|
|
58
|
+
onPress={toggleAccordion}
|
|
59
|
+
style={[
|
|
60
|
+
styles.header,
|
|
61
|
+
{
|
|
62
|
+
borderTopRightRadius: theme.radius.lg,
|
|
63
|
+
borderTopLeftRadius: theme.radius.lg,
|
|
64
|
+
// Si no está expandido, redondeamos también abajo
|
|
65
|
+
borderBottomRightRadius: expanded ? 0 : theme.radius.lg,
|
|
66
|
+
borderBottomLeftRadius: expanded ? 0 : theme.radius.lg,
|
|
67
|
+
},
|
|
68
|
+
expanded && {
|
|
69
|
+
backgroundColor: theme.surface.brand.primary,
|
|
70
|
+
borderBottomWidth: 1,
|
|
71
|
+
borderBottomColor: theme.outline.brand.primary
|
|
72
|
+
}
|
|
73
|
+
]}
|
|
74
|
+
>
|
|
75
|
+
<Text style={[styles.title, theme.typography.semiBold.md]}>
|
|
76
|
+
{title}
|
|
77
|
+
</Text>
|
|
78
|
+
<MaterialCommunityIcons
|
|
79
|
+
name={expanded ? "minus" : "plus"}
|
|
80
|
+
size={24}
|
|
81
|
+
color={theme.text.neutral.primary}
|
|
82
|
+
/>
|
|
83
|
+
</Pressable>
|
|
84
|
+
|
|
85
|
+
<Animated.View style={[styles.content, bodyStyle, { overflow: 'hidden' }]}>
|
|
86
|
+
{/* CONTENEDOR DE MEDICIÓN: Es invisible y absoluto, solo sirve para saber cuánto mide el children */}
|
|
87
|
+
<View
|
|
88
|
+
onLayout={onLayout}
|
|
89
|
+
style={styles.measureContainer}
|
|
90
|
+
pointerEvents="none"
|
|
91
|
+
>
|
|
92
|
+
<View style={styles.paddingWrapper}>
|
|
93
|
+
{description && (
|
|
94
|
+
<Text style={[styles.description, theme.typography.regular.sm]}>
|
|
95
|
+
{description}
|
|
96
|
+
</Text>
|
|
97
|
+
)}
|
|
98
|
+
{children}
|
|
99
|
+
</View>
|
|
100
|
+
</View>
|
|
101
|
+
|
|
102
|
+
{/* CONTENEDOR VISIBLE: El que el usuario realmente ve animarse */}
|
|
103
|
+
<View style={styles.paddingWrapper}>
|
|
104
|
+
{description && (
|
|
105
|
+
<Text style={[styles.description, theme.typography.regular.sm]}>
|
|
106
|
+
{description}
|
|
107
|
+
</Text>
|
|
108
|
+
)}
|
|
109
|
+
<View style={styles.children}>{children}</View>
|
|
110
|
+
</View>
|
|
111
|
+
</Animated.View>
|
|
112
|
+
</View>
|
|
113
|
+
);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const styles = StyleSheet.create({
|
|
117
|
+
container: {
|
|
118
|
+
width: "100%",
|
|
119
|
+
marginBottom: 16,
|
|
120
|
+
},
|
|
121
|
+
header: {
|
|
122
|
+
flexDirection: "row",
|
|
123
|
+
justifyContent: "space-between",
|
|
124
|
+
alignItems: "center",
|
|
125
|
+
padding: 16,
|
|
126
|
+
minHeight: 56,
|
|
127
|
+
},
|
|
128
|
+
title: {
|
|
129
|
+
flex: 1
|
|
130
|
+
},
|
|
131
|
+
content: {
|
|
132
|
+
paddingHorizontal: 16,
|
|
133
|
+
},
|
|
134
|
+
measureContainer: {
|
|
135
|
+
position: 'absolute',
|
|
136
|
+
left: 16,
|
|
137
|
+
right: 16,
|
|
138
|
+
opacity: 0, // No se ve, pero ocupa espacio para onLayout
|
|
139
|
+
},
|
|
140
|
+
paddingWrapper: {
|
|
141
|
+
paddingBottom: 16,
|
|
142
|
+
paddingTop: 8,
|
|
143
|
+
},
|
|
144
|
+
description: {
|
|
145
|
+
marginBottom: 8,
|
|
146
|
+
},
|
|
147
|
+
children: {
|
|
148
|
+
width: '100%',
|
|
149
|
+
}
|
|
150
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
|
|
2
|
+
import { View, Text, StyleSheet, } from "react-native";
|
|
3
|
+
import { useTheme } from "../context/CDSThemeContext";
|
|
4
|
+
import { Dimensions } from "react-native";
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
const { width } = Dimensions.get("window");
|
|
8
|
+
const isMobile = width <= 768
|
|
9
|
+
|
|
10
|
+
export const CDSOffer = ({ description, minValue, maxValue }) => {
|
|
11
|
+
|
|
12
|
+
const { theme } = useTheme();
|
|
13
|
+
console.log(theme)
|
|
14
|
+
return (
|
|
15
|
+
<View style={[styles.mainContainer, { gap: theme.space.xs }]}>
|
|
16
|
+
<Text style={theme.typography.regular.md}>Por tu</Text>
|
|
17
|
+
<Text style={theme.typography.semiBold.lg}>{description}</Text>
|
|
18
|
+
<Text style={theme.typography.regular.md}>Hoy podrías obtener:</Text>
|
|
19
|
+
<View style={[styles.minMaxContainer, { flexDirection: isMobile ? 'column' : 'row' }]}>
|
|
20
|
+
<View style={[styles.containerMin, { backgroundColor: theme.surface.neutral.secondary, borderColor: theme.outline.neutral.tertiaryVariant, gap: theme.space.xs, padding: theme.space.sm }]}>
|
|
21
|
+
<Text style={theme.typography.regular.md}>Desde</Text>
|
|
22
|
+
<Text style={[theme.typography.h3, { textAlign: 'center' }]}>{`$${minValue}`}</Text>
|
|
23
|
+
</View>
|
|
24
|
+
<View style={[styles.containerMax, { marginTop: isMobile ? -theme.space.xs : 0, marginLeft: isMobile ? 0 : -theme.space.xl, backgroundColor: theme.surface.brand.primaryVariant, borderColor: theme.outline.brand.secondary, gap: theme.space.xs, padding: theme.space.sm }]}>
|
|
25
|
+
<Text style={[theme.typography.regular.md, { color: theme.text.brand.primary }]}>Hasta</Text>
|
|
26
|
+
<Text style={[theme.typography.h1, { textAlign: 'center', color: theme.text.brand.primary }]}>{`$${maxValue}`}</Text>
|
|
27
|
+
</View>
|
|
28
|
+
</View>
|
|
29
|
+
</View>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
const styles = StyleSheet.create({
|
|
35
|
+
|
|
36
|
+
mainContainer: {
|
|
37
|
+
width: '100%',
|
|
38
|
+
maxWidth: 800,
|
|
39
|
+
alignItems: 'center',
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
minMaxContainer: {
|
|
43
|
+
width: '100%',
|
|
44
|
+
flexDirection: 'row',
|
|
45
|
+
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
containerMin: {
|
|
49
|
+
flexGrow: 1,
|
|
50
|
+
justifyContent: 'space-around',
|
|
51
|
+
borderRadius: 8,
|
|
52
|
+
flexDirection: 'column',
|
|
53
|
+
borderWidth: 1,
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
containerMax: {
|
|
57
|
+
flexGrow: 1,
|
|
58
|
+
justifyContent: 'space-around',
|
|
59
|
+
borderRadius: 8,
|
|
60
|
+
flexDirection: 'column',
|
|
61
|
+
borderWidth: 1,
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
});
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, Text, StyleSheet, ScrollView, useWindowDimensions } from 'react-native';
|
|
3
|
+
import { useTheme } from '../context/CDSThemeContext';
|
|
4
|
+
|
|
5
|
+
export const CDSTable = ({ columns, data, breakpoint = 500 }) => {
|
|
6
|
+
const { theme } = useTheme();
|
|
7
|
+
const { width: screenWidth } = useWindowDimensions();
|
|
8
|
+
|
|
9
|
+
// Aquí definimos que "Mobile" es cualquier pantalla menor al breakpoint
|
|
10
|
+
const isSmallScreen = screenWidth < breakpoint;
|
|
11
|
+
|
|
12
|
+
const TableContent = (
|
|
13
|
+
<View style={[
|
|
14
|
+
styles.tableContainer,
|
|
15
|
+
{
|
|
16
|
+
borderColor: theme.outline.neutral.primary,
|
|
17
|
+
borderRadius: theme.radius.md,
|
|
18
|
+
// Si es pantalla pequeña, ancho libre para que crezca a la derecha.
|
|
19
|
+
// Si es pantalla grande, ocupa el 100% del padre.
|
|
20
|
+
width: isSmallScreen ? undefined : '100%',
|
|
21
|
+
}
|
|
22
|
+
]}>
|
|
23
|
+
{/* Encabezado */}
|
|
24
|
+
<View style={[
|
|
25
|
+
styles.row,
|
|
26
|
+
styles.header,
|
|
27
|
+
{ backgroundColor: theme.surface.neutral.primaryVariant, borderBottomColor: theme.outline.neutral.primary }
|
|
28
|
+
]}>
|
|
29
|
+
{columns.map((col, index) => (
|
|
30
|
+
<View
|
|
31
|
+
key={`header-${index}`}
|
|
32
|
+
style={[
|
|
33
|
+
styles.cell,
|
|
34
|
+
{ padding: theme.space.xs },
|
|
35
|
+
col.width ? { width: col.width } : { flex: 1 }
|
|
36
|
+
]}
|
|
37
|
+
>
|
|
38
|
+
<Text style={[styles.headerText, theme.typography.semiBold.md]}>
|
|
39
|
+
{col.title}
|
|
40
|
+
</Text>
|
|
41
|
+
</View>
|
|
42
|
+
))}
|
|
43
|
+
</View>
|
|
44
|
+
|
|
45
|
+
{/* Filas */}
|
|
46
|
+
{data.map((item, rowIndex) => (
|
|
47
|
+
<View
|
|
48
|
+
key={`row-${rowIndex}`}
|
|
49
|
+
style={[
|
|
50
|
+
styles.row,
|
|
51
|
+
rowIndex !== data.length - 1 && { borderBottomWidth: 1 },
|
|
52
|
+
{ borderBottomColor: theme.outline.neutral.primary }
|
|
53
|
+
]}
|
|
54
|
+
>
|
|
55
|
+
{columns.map((col, colIndex) => (
|
|
56
|
+
<View
|
|
57
|
+
key={`cell-${rowIndex}-${colIndex}`}
|
|
58
|
+
style={[
|
|
59
|
+
styles.cell,
|
|
60
|
+
{ padding: theme.space.xs },
|
|
61
|
+
col.width ? { width: col.width } : { flex: 1 }
|
|
62
|
+
]}
|
|
63
|
+
>
|
|
64
|
+
<Text style={[styles.cellText, theme.typography.regular.md]}>
|
|
65
|
+
{item[col.key]}
|
|
66
|
+
</Text>
|
|
67
|
+
</View>
|
|
68
|
+
))}
|
|
69
|
+
</View>
|
|
70
|
+
))}
|
|
71
|
+
</View>
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (isSmallScreen) {
|
|
75
|
+
return (
|
|
76
|
+
<View style={styles.scrollWrapper}>
|
|
77
|
+
<ScrollView
|
|
78
|
+
horizontal
|
|
79
|
+
showsHorizontalScrollIndicator={true}
|
|
80
|
+
// Importante: No ponemos alignItems: 'flex-start' aquí para que no muerda el final
|
|
81
|
+
contentContainerStyle={styles.scrollContent}
|
|
82
|
+
>
|
|
83
|
+
{TableContent}
|
|
84
|
+
</ScrollView>
|
|
85
|
+
</View>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<View style={styles.fullWidthContainer}>
|
|
91
|
+
{TableContent}
|
|
92
|
+
</View>
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const styles = StyleSheet.create({
|
|
97
|
+
scrollWrapper: {
|
|
98
|
+
width: '100%',
|
|
99
|
+
},
|
|
100
|
+
scrollContent: {
|
|
101
|
+
// Asegura que la tabla pueda expandirse horizontalmente sin restricciones
|
|
102
|
+
minWidth: '100%',
|
|
103
|
+
paddingRight: 20, // Espacio extra al final del scroll para que la última columna no pegue
|
|
104
|
+
},
|
|
105
|
+
fullWidthContainer: {
|
|
106
|
+
width: '100%',
|
|
107
|
+
},
|
|
108
|
+
tableContainer: {
|
|
109
|
+
borderWidth: 1,
|
|
110
|
+
overflow: 'hidden',
|
|
111
|
+
},
|
|
112
|
+
row: {
|
|
113
|
+
flexDirection: 'row',
|
|
114
|
+
},
|
|
115
|
+
header: {
|
|
116
|
+
borderBottomWidth: 1,
|
|
117
|
+
},
|
|
118
|
+
cell: {
|
|
119
|
+
justifyContent: 'center',
|
|
120
|
+
// Ancho mínimo por columna para que siempre sea legible en scroll
|
|
121
|
+
minWidth: 100,
|
|
122
|
+
},
|
|
123
|
+
headerText: {
|
|
124
|
+
textAlign: 'center',
|
|
125
|
+
},
|
|
126
|
+
cellText: {
|
|
127
|
+
textAlign: 'center',
|
|
128
|
+
},
|
|
129
|
+
});
|
package/index.js
CHANGED
|
@@ -15,6 +15,9 @@ export {CDSNavBar} from './components/CDSNavBar';
|
|
|
15
15
|
export {CDSCardFeedback} from './components/CDSCardFeedback';
|
|
16
16
|
export {CDSSplashScreen} from './components/CDSSplashScreen';
|
|
17
17
|
export {CDSButtonGroup} from './components/CDSButtonGroup';
|
|
18
|
-
export {CDSSelect} from './components/CDSSelect';
|
|
18
|
+
export {CDSSelect} from './components/CDSSelect';
|
|
19
|
+
export {CDSLoader} from './components/CDSLoader';
|
|
20
|
+
export {CDSOffer} from './components/CDSOffer';
|
|
21
|
+
export {CDSTable} from './components/CDSTable';
|
|
19
22
|
|
|
20
23
|
export {CDSThemeProvider, useTheme} from './context/CDSThemeContext';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cdslibrary",
|
|
3
3
|
"license": "0BSD",
|
|
4
|
-
"version": "1.1.
|
|
4
|
+
"version": "1.1.4",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"author": "Nat Viramontes",
|
|
7
7
|
"description": "A library of components for the CDS project",
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
"react": "19.1.0",
|
|
26
26
|
"react-dom": "^19.1.0",
|
|
27
27
|
"react-native": "0.81.5",
|
|
28
|
-
"react-native-
|
|
28
|
+
"react-native-gesture-handler": "~2.20.2",
|
|
29
|
+
"react-native-reanimated": "~3.16.1",
|
|
29
30
|
"react-native-safe-area-context": "~5.6.0",
|
|
30
31
|
"react-native-screens": "~4.16.0",
|
|
31
32
|
"react-native-web": "^0.21.0"
|
|
@@ -14,9 +14,6 @@ export const CDSsemanticColors = {
|
|
|
14
14
|
tertiaryVariant: CDSprimitiveColors.neutral[600],
|
|
15
15
|
secondaryVariant: CDSprimitiveColors.neutral[100],
|
|
16
16
|
},
|
|
17
|
-
brand: {
|
|
18
|
-
primary: CDSprimitiveColors.brand[600],
|
|
19
|
-
},
|
|
20
17
|
feedback: {
|
|
21
18
|
info: CDSprimitiveColors.blue[300],
|
|
22
19
|
success: CDSprimitiveColors.green[300],
|
|
@@ -30,6 +27,13 @@ export const CDSsemanticColors = {
|
|
|
30
27
|
primary: CDSprimitiveColors.neutral[800],
|
|
31
28
|
pressed: CDSprimitiveColors.neutral[1000],
|
|
32
29
|
},
|
|
30
|
+
brand:{
|
|
31
|
+
primary: CDSprimitiveColors.brand[50],
|
|
32
|
+
primaryVariant: CDSprimitiveColors.brand[100],
|
|
33
|
+
secondary: CDSprimitiveColors.brand[200],
|
|
34
|
+
tertiary: CDSprimitiveColors.brand[400],
|
|
35
|
+
tertiaryVariant: CDSprimitiveColors.brand[800],
|
|
36
|
+
},
|
|
33
37
|
feedback: {
|
|
34
38
|
info: CDSprimitiveColors.blue[100],
|
|
35
39
|
success: CDSprimitiveColors.green[100],
|
|
@@ -119,9 +123,6 @@ export const CDSsemanticColors = {
|
|
|
119
123
|
tertiaryVariant: CDSprimitiveColors.neutral[600],
|
|
120
124
|
secondaryVariant: CDSprimitiveColors.neutral[975],
|
|
121
125
|
},
|
|
122
|
-
brand: {
|
|
123
|
-
primary: CDSprimitiveColors.brand[150],
|
|
124
|
-
},
|
|
125
126
|
feedback: {
|
|
126
127
|
info: CDSprimitiveColors.blue[350],
|
|
127
128
|
success: CDSprimitiveColors.green[350],
|
|
@@ -135,6 +136,15 @@ export const CDSsemanticColors = {
|
|
|
135
136
|
primary: CDSprimitiveColors.neutral[200],
|
|
136
137
|
pressed: CDSprimitiveColors.neutral[100],
|
|
137
138
|
},
|
|
139
|
+
|
|
140
|
+
brand:{
|
|
141
|
+
primary: CDSprimitiveColors.neutral[900],
|
|
142
|
+
primaryVariant: CDSprimitiveColors.brand[800],
|
|
143
|
+
secondary: CDSprimitiveColors.brand[150],
|
|
144
|
+
tertiary: CDSprimitiveColors.brand[400],
|
|
145
|
+
tertiaryVariant: CDSprimitiveColors.brand[700],
|
|
146
|
+
},
|
|
147
|
+
|
|
138
148
|
feedback: {
|
|
139
149
|
info: CDSprimitiveColors.blue[250],
|
|
140
150
|
success: CDSprimitiveColors.green[250],
|
|
@@ -198,7 +208,7 @@ export const CDSsemanticColors = {
|
|
|
198
208
|
placeholder: CDSprimitiveColors.neutral[300],
|
|
199
209
|
},
|
|
200
210
|
brand: {
|
|
201
|
-
primary: CDSprimitiveColors.brand[
|
|
211
|
+
primary: CDSprimitiveColors.brand[50],
|
|
202
212
|
secondary: CDSprimitiveColors.brand[700],
|
|
203
213
|
tertiary: CDSprimitiveColors.brand[900],
|
|
204
214
|
},
|