cdslibrary 1.2.85 → 1.2.86
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/CDSInput.jsx +63 -52
- package/package.json +1 -1
package/components/CDSInput.jsx
CHANGED
|
@@ -5,66 +5,84 @@ import Animated, {
|
|
|
5
5
|
useSharedValue,
|
|
6
6
|
withSequence,
|
|
7
7
|
withTiming,
|
|
8
|
+
withDelay,
|
|
8
9
|
interpolateColor
|
|
9
10
|
} from "react-native-reanimated";
|
|
10
11
|
import { useTheme } from "../context/CDSThemeContext";
|
|
11
12
|
|
|
13
|
+
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput);
|
|
14
|
+
|
|
12
15
|
export const CDSInput = ({ label, type, keyboard, placeholder, value, onChangeText, animationTrigger, returnKeyType, onFocus }) => {
|
|
13
16
|
const { theme } = useTheme();
|
|
14
17
|
const [isFocused, setIsFocused] = useState(false);
|
|
15
18
|
|
|
16
19
|
const flashValue = useSharedValue(0);
|
|
20
|
+
const textHighlight = useSharedValue(0);
|
|
17
21
|
const isInternalChange = useRef(false);
|
|
18
22
|
|
|
23
|
+
const isReadOnly = type === 'readOnly';
|
|
24
|
+
|
|
25
|
+
// --- Colores de seguridad ---
|
|
26
|
+
const colorBase = theme?.text?.neutral?.primary
|
|
27
|
+
const colorReadOnlyText = theme?.text?.neutral?.secondary
|
|
28
|
+
const colorFocus = theme?.surface?.special?.progress
|
|
29
|
+
const colorTertiary = theme?.outline?.neutral?.tertiaryVariant
|
|
30
|
+
const colorPrimaryOutline = theme?.outline?.neutral?.primary
|
|
31
|
+
|
|
19
32
|
useEffect(() => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
33
|
+
// ELIMINAMOS la restricción de !isReadOnly para que se anime siempre
|
|
34
|
+
if (!isInternalChange.current && value) {
|
|
35
|
+
flashValue.value = withDelay(400, withSequence(
|
|
36
|
+
withTiming(1, { duration: 400 }),
|
|
37
|
+
withTiming(0, { duration: 600 })
|
|
38
|
+
));
|
|
39
|
+
|
|
40
|
+
textHighlight.value = withSequence(
|
|
41
|
+
withTiming(1, { duration: 400 }),
|
|
23
42
|
withTiming(0, { duration: 600 })
|
|
24
43
|
);
|
|
25
44
|
}
|
|
26
45
|
isInternalChange.current = false;
|
|
27
|
-
}, [value, animationTrigger]);
|
|
28
|
-
|
|
29
|
-
const handleTextChange = (inputText) => {
|
|
30
|
-
isInternalChange.current = true;
|
|
31
|
-
|
|
32
|
-
if (keyboard === "decimal-pad") {
|
|
33
|
-
let cleaned = inputText.replace(',', '.').replace(/[^0-9.]/g, "");
|
|
34
|
-
const parts = cleaned.split('.');
|
|
35
|
-
if (parts.length > 2) cleaned = parts[0] + '.' + parts.slice(1).join('');
|
|
36
|
-
onChangeText && onChangeText(cleaned);
|
|
37
|
-
} else {
|
|
38
|
-
onChangeText && onChangeText(inputText);
|
|
39
|
-
}
|
|
40
|
-
};
|
|
46
|
+
}, [value, animationTrigger]); // Quitamos isReadOnly de las dependencias para evitar disparos extra
|
|
41
47
|
|
|
42
48
|
const animatedBoxStyle = useAnimatedStyle(() => {
|
|
43
49
|
const borderColor = interpolateColor(
|
|
44
50
|
flashValue.value,
|
|
45
51
|
[0, 1],
|
|
46
52
|
[
|
|
47
|
-
isFocused ?
|
|
48
|
-
|
|
53
|
+
isFocused ? colorFocus : value ? colorTertiary : colorPrimaryOutline,
|
|
54
|
+
colorFocus
|
|
49
55
|
]
|
|
50
56
|
);
|
|
51
57
|
|
|
52
58
|
return {
|
|
53
|
-
borderColor
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
borderWidth: 1 + (flashValue.value * 1),
|
|
59
|
+
borderColor,
|
|
60
|
+
borderWidth: 1 + (flashValue.value * 2),
|
|
61
|
+
transform: [{ scale: 1 + (flashValue.value * 0.06) }]
|
|
57
62
|
};
|
|
58
63
|
});
|
|
59
64
|
|
|
65
|
+
const animatedTextStyle = useAnimatedStyle(() => {
|
|
66
|
+
// Definimos el color inicial según si es readOnly o no
|
|
67
|
+
const startColor = isReadOnly ? colorReadOnlyText : colorBase;
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
color: interpolateColor(
|
|
71
|
+
textHighlight.value,
|
|
72
|
+
[0, 1],
|
|
73
|
+
[startColor, colorFocus] // El color de "brillo" sigue siendo el de foco
|
|
74
|
+
),
|
|
75
|
+
letterSpacing: textHighlight.value * 1,
|
|
76
|
+
opacity: isReadOnly ? 0.85 + (textHighlight.value * 0.3) : 0.8 + (textHighlight.value * 0.2),
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
if (!theme) return null;
|
|
60
81
|
|
|
61
82
|
return (
|
|
62
83
|
<View style={styles.container}>
|
|
63
84
|
{label && (
|
|
64
|
-
<Text style={[
|
|
65
|
-
theme.typography.label,
|
|
66
|
-
{ color: theme.text.neutral.primary, marginBottom: theme.space.xs }
|
|
67
|
-
]}>
|
|
85
|
+
<Text style={[theme.typography.label, { color: colorBase, marginBottom: theme.space?.xs || 4 }]}>
|
|
68
86
|
{label}
|
|
69
87
|
</Text>
|
|
70
88
|
)}
|
|
@@ -73,35 +91,34 @@ export const CDSInput = ({ label, type, keyboard, placeholder, value, onChangeTe
|
|
|
73
91
|
styles.wrapper,
|
|
74
92
|
animatedBoxStyle,
|
|
75
93
|
{
|
|
76
|
-
backgroundColor:
|
|
77
|
-
borderRadius: theme.radius
|
|
94
|
+
backgroundColor: isReadOnly ? theme.surface.neutral.primaryVariant : theme.surface.neutral.primary,
|
|
95
|
+
borderRadius: theme.radius?.sm || 8,
|
|
78
96
|
}
|
|
79
97
|
]}>
|
|
80
|
-
<
|
|
98
|
+
<AnimatedTextInput
|
|
81
99
|
style={[
|
|
82
100
|
styles.textBox,
|
|
83
|
-
theme.typography.inputText
|
|
101
|
+
theme.typography.inputText?.value,
|
|
102
|
+
animatedTextStyle,
|
|
84
103
|
{
|
|
85
|
-
paddingHorizontal: theme.space
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
...Platform.select({
|
|
89
|
-
web: { outlineStyle: 'none' }
|
|
90
|
-
})
|
|
104
|
+
paddingHorizontal: theme.space?.sm || 12,
|
|
105
|
+
fontWeight: '600',
|
|
106
|
+
...Platform.select({ web: { outlineStyle: 'none' } })
|
|
91
107
|
},
|
|
92
108
|
]}
|
|
93
109
|
placeholder={placeholder}
|
|
94
110
|
placeholderTextColor={theme.text.neutral.placeholder}
|
|
95
111
|
keyboardType={keyboard}
|
|
96
|
-
onChangeText={
|
|
112
|
+
onChangeText={(txt) => {
|
|
113
|
+
isInternalChange.current = true;
|
|
114
|
+
onChangeText && onChangeText(txt);
|
|
115
|
+
}}
|
|
97
116
|
value={value}
|
|
98
|
-
editable={
|
|
117
|
+
editable={!isReadOnly}
|
|
99
118
|
secureTextEntry={type === "password"}
|
|
100
|
-
onFocus={() => { setIsFocused(true); onFocus }}
|
|
119
|
+
onFocus={() => { setIsFocused(true); onFocus && onFocus(); }}
|
|
101
120
|
onBlur={() => setIsFocused(false)}
|
|
102
121
|
underlineColorAndroid="transparent"
|
|
103
|
-
returnKeyType={returnKeyType}
|
|
104
|
-
dataSet={{ lpignore: "true" }}
|
|
105
122
|
/>
|
|
106
123
|
</Animated.View>
|
|
107
124
|
</View>
|
|
@@ -109,20 +126,14 @@ export const CDSInput = ({ label, type, keyboard, placeholder, value, onChangeTe
|
|
|
109
126
|
};
|
|
110
127
|
|
|
111
128
|
const styles = StyleSheet.create({
|
|
112
|
-
container: {
|
|
113
|
-
width: "100%",
|
|
114
|
-
alignSelf: 'stretch',
|
|
115
|
-
},
|
|
129
|
+
container: { width: "100%", alignSelf: 'stretch' },
|
|
116
130
|
wrapper: {
|
|
117
131
|
width: '100%',
|
|
118
132
|
height: 48,
|
|
119
|
-
flexDirection: 'row',
|
|
133
|
+
flexDirection: 'row',
|
|
120
134
|
alignItems: 'center',
|
|
121
135
|
borderWidth: 1,
|
|
136
|
+
overflow: 'hidden'
|
|
122
137
|
},
|
|
123
|
-
textBox: {
|
|
124
|
-
flex: 1, // Esto es lo más importante: ocupa todo el espacio sobrante
|
|
125
|
-
height: '100%',
|
|
126
|
-
width: '100%',
|
|
127
|
-
},
|
|
138
|
+
textBox: { flex: 1, height: '100%', width: '100%' },
|
|
128
139
|
});
|