react-native-anim-inp
Version:
A lightweight and easy to use floating animated input! :)
231 lines (215 loc) • 6.07 kB
JavaScript
import React, { useState, useEffect, useCallback, forwardRef } from "react";
import { View, TextInput, Animated, Text } from "react-native";
import { TextInputMask } from "react-native-masked-text";
import styles from "./styles";
const AnimatedTextInput = ({
placeholder,
errorText,
valid,
errorColor,
disabled,
value,
prefix,
sufix,
styleInput,
styleLabel,
styleError,
styleContent,
styleBodyContent,
mask,
maskOptions = {},
innerRef,
selectionColor,
labelInitialSize,
labelFinalSize,
textInputFontSize,
onChangeEnd = () => null,
...others
}) => {
const [showInput, setShowInput] = useState(false);
const [showError, setShowError] = useState(false);
const [animatedIsFocused] = useState(new Animated.Value(1));
const [isInputFocused, setInputFocus] = useState(false);
const inputFontSize = styleLabel.fontSize || styles.label.fontSize;
const labelFontSize = styleLabel.fontSize || styles.label.fontSize;
const errorFontSize = styleError.fontSize || styles.error.fontSize;
useEffect(() => {
setShowError(!valid);
if (value) {
setShowInput(true);
}
if (value && !showInput) {
startAnimation();
}
animationView();
}, [
valid,
value,
animationView,
animationLabelFontSize,
animatedIsFocused,
startAnimation,
showInput,
]);
const onBlur = () => {
setInputFocus(false);
if (!value) {
setShowInput(false);
setShowError(false);
startAnimation();
}
};
const onFocus = () => {
setInputFocus(true);
if (!showInput) {
startAnimation();
}
};
const borderColor = () => {
const borderStyle = {};
borderStyle.borderBottomColor =
styleBodyContent.borderBottomColor ||
styles.bodyContent.borderBottomColor;
if (showError) {
borderStyle.borderBottomColor = errorColor || "#d32f2f";
}
return borderStyle;
};
const setContentHeight = () => {
const fontsHeight = labelFontSize + inputFontSize + errorFontSize + 10;
const internalVerticalSpaces = 16;
return fontsHeight + internalVerticalSpaces;
};
const getErrorContentSpace = () => {
return errorFontSize + 2;
};
const startAnimation = useCallback(() => {
Animated.timing(animatedIsFocused, {
useNativeDriver: false,
toValue: showInput ? 1 : 0,
duration: 150,
}).start(() => {
if (!showInput) {
setShowInput(true);
}
});
}, [animatedIsFocused, showInput]);
const animationView = useCallback(() => {
const sizeShow = 15 + labelFontSize + inputFontSize + 5;
const sizeHide = 15 + labelFontSize;
const inputAdjust = {
height: animatedIsFocused.interpolate({
inputRange: [0, 1],
outputRange: [sizeShow, sizeHide],
}),
};
return inputAdjust;
}, [animatedIsFocused, inputFontSize, labelFontSize]);
const animationLabelFontSize = useCallback(() => {
const fontAdjust = {
fontSize: animatedIsFocused.interpolate({
inputRange: [0, 1],
outputRange: [
labelFinalSize || labelFontSize,
labelInitialSize || inputFontSize,
],
}),
};
return fontAdjust;
}, [animatedIsFocused, inputFontSize, labelFontSize]);
return (
<View
style={[styles.content, styleContent, { height: setContentHeight() }]}
>
<Animated.View
style={[
styles.bodyContent,
styleBodyContent,
borderColor(showError),
{
marginBottom: showError ? 0 : getErrorContentSpace(),
borderBottomWidth: isInputFocused ? 1.5 : 0.5,
},
animationView(),
]}
>
<View style={styles.wrapper}>
<Animated.Text
style={[styles.label, styleLabel, animationLabelFontSize()]}
onPress={() => !disabled && onFocus()}
>
{placeholder}
</Animated.Text>
{showInput && (
<View style={styles.toucheableLineContent}>
<>{prefix}</>
{!!mask ? (
<TextInputMask
{...others}
value={value}
pointerEvents={disabled ? "box-none" : "auto"}
selectionColor={selectionColor}
autoFocus
blurOnSubmit
editable={!disabled}
onBlur={() => onBlur()}
style={[
styles.input,
styleInput,
textInputFontSize && { fontSize: textInputFontSize },
]}
onEndEditing={() => {
onChangeEnd();
onBlur();
}}
type={mask || "cpf"}
options={maskOptions}
ref={innerRef}
/>
) : (
<TextInput
{...others}
value={value}
pointerEvents={disabled ? "box-none" : "auto"}
selectionColor={selectionColor}
autoFocus
blurOnSubmit
editable={!disabled}
onBlur={() => onBlur()}
style={[styles.input, styleInput]}
onEndEditing={() => onBlur()}
ref={innerRef}
/>
)}
</View>
)}
</View>
<View style={styles.sufix}>{sufix}</View>
</Animated.View>
{showError && (
<Text
style={[
styles.error,
errorColor && { color: errorColor },
styleError,
]}
>
{errorText}
</Text>
)}
</View>
);
};
const AnimatedInput = forwardRef((props, ref) => (
<AnimatedTextInput {...props} innerRef={ref} />
));
AnimatedInput.defaultProps = {
valid: true,
disabled: false,
value: "",
styleInput: {},
styleBodyContent: {},
styleLabel: {},
styleError: {},
};
export default AnimatedInput;