import React, { useEffect, useState, useRef } from "react"; import { View, TextInput, TouchableWithoutFeedback, ViewStyle, TextInputFocusEventData, NativeSyntheticEvent, } from "react-native"; import Animated, { Extrapolate, interpolate, useAnimatedStyle, useSharedValue, withDelay, withTiming, } from "react-native-reanimated"; import { Entypo } from "@expo/vector-icons"; import { DeleteButtonProps, PlaceholderProps, AnimatedInputProps, } from "./prop-types"; const AnimatedIcon = Animated.createAnimatedComponent(Entypo); const AnimatedTextInput = React.forwardRef( ( { placeholder, placeholderTextColor, backgroundColor, borderColor, deleteIconColor, style, containerStyle, placeholderTextStyle, defaultValue, onChangeText, onFocus, onBlur, ...rest }: AnimatedInputProps, ref ) => { const [text, setText] = useState(defaultValue ?? ""); const [isFocused, setIsFocused] = useState(false); const inputRef = ref ?? useRef(null); const styles = { container: { borderColor: borderColor, borderWidth: 1, height: 60, fontSize: 16, borderRadius: 10, flexDirection: "row", alignItems: "center", ...containerStyle, } as ViewStyle, textInput: { fontSize: 16, flex: 1, paddingHorizontal: 20, }, }; // ---> Animations const deleteButtonAnimationProgress = useSharedValue(text === "" ? 0 : 1); const placeholderAnimationProgress = useSharedValue(text === "" ? 0 : 1); useEffect(() => { // enter delete button if (text === "") deleteButtonAnimationProgress.value = withTiming(0); // exit delete button else deleteButtonAnimationProgress.value = withTiming(1); if (isFocused) { // set placeholder to upper position placeholderAnimationProgress.value = withDelay( 20, withTiming(1, { duration: 350 }) ); } else if (text === "") { // set placeholder to default position when loses focus and text-field is empty placeholderAnimationProgress.value = withDelay( 20, withTiming(0, { duration: 350 }) ); } }, [isFocused, text]); // ---> Functions const focusInput = () => { // @ts-ignore inputRef?.current?.focus(); }; const handlePressDelete = () => { handleChangeText(""); // @ts-ignore inputRef?.current?.focus(); }; const handleFocus = (e: NativeSyntheticEvent) => { setIsFocused(true); onFocus?.(e); }; const handleBlur = (e: NativeSyntheticEvent) => { setIsFocused(false); onBlur?.(e); }; const handleChangeText = (text: string) => { setText(text); onChangeText?.(text); }; return ( ); } ); const DeleteButton = ({ deleteButtonAnimationProgress, deleteIconColor, handlePressDelete, }: DeleteButtonProps) => { const style = useAnimatedStyle(() => ({ marginRight: 14, opacity: interpolate(deleteButtonAnimationProgress.value, [0, 1], [0, 1]), transform: [ { translateX: interpolate( deleteButtonAnimationProgress.value, [0, 1], [8, 0] ), }, ], })); return ( ); }; const Placeholder = ({ placeholder, placeholderTextColor, placeholderTextStyle, placeholderAnimationProgress, backgroundColor, }: PlaceholderProps) => { const styles = { placeholderContainerStyle: useAnimatedStyle(() => ({ position: "absolute", backgroundColor: backgroundColor ?? "black", paddingHorizontal: interpolate( placeholderAnimationProgress.value, [0.6, 1], [0, 5], Extrapolate.CLAMP ), marginHorizontal: interpolate( placeholderAnimationProgress.value, [0.6, 1], [5, 0], Extrapolate.CLAMP ), transform: [ { translateY: interpolate( placeholderAnimationProgress.value, [0, 1], [0.765, -28] ), }, { translateX: 20 }, ], })), placeholderStyle: useAnimatedStyle(() => ({ color: placeholderTextColor, fontSize: interpolate( placeholderAnimationProgress.value, [0, 1], [15.5, 11] ), ...placeholderTextStyle, })), }; return ( <> {placeholder && placeholder !== "" ? ( {placeholder} ) : ( <> )} ); }; export default AnimatedTextInput;