import type { NativeSyntheticEvent, TextInputFocusEventData, } from "react-native"; import * as React from "react"; import { Pressable, TextInput, View, ViewStyle } from "react-native"; import Animated, { measure, useAnimatedRef, useAnimatedStyle, useDerivedValue, withTiming, } from "react-native-reanimated"; import { TailwindResolver } from "@/components/TailwindResolver"; import { Text } from "@/components/ui/Text"; import { cn } from "@/lib/utils"; import { useAugmentedRef, useControllableState } from "@rn-primitives/hooks"; import { SearchIcon } from "lucide-react-native"; import type { SearchInputProps } from "./types"; // Add as class when possible: https://github.com/marklawlor/nativewind/issues/522 const BORDER_CURVE: ViewStyle = { borderCurve: "continuous", }; const SearchInput = React.forwardRef< React.ElementRef, SearchInputProps >( ( { value: valueProp, onChangeText: onChangeTextProp, onFocus: onFocusProp, placeholder = "Search...", cancelText = "Cancel", containerClassName, iconContainerClassName, className, iconColor: _iconColor, onCancel, ...props }, ref, ) => { const inputRef = useAugmentedRef({ ref, methods: { focus, blur, clear } }); const [showCancel, setShowCancel] = React.useState(false); const showCancelDerivedValue = useDerivedValue( () => showCancel, [showCancel], ); const animatedRef = useAnimatedRef(); const [value = "", onChangeText] = useControllableState({ prop: valueProp, defaultProp: valueProp ?? "", onChange: onChangeTextProp, }); const rootStyle = useAnimatedStyle(() => { if (_WORKLET) { // safely use measure const measurement = measure(animatedRef); return { paddingRight: showCancelDerivedValue.value ? withTiming(measurement?.width ?? cancelText.length * 11.2) : withTiming(0), }; } return { paddingRight: showCancelDerivedValue.value ? withTiming(cancelText.length * 11.2) : withTiming(0), }; }); const buttonStyle3 = useAnimatedStyle(() => { if (_WORKLET) { // safely use measure const measurement = measure(animatedRef); return { position: "absolute", right: 0, opacity: showCancelDerivedValue.value ? withTiming(1) : withTiming(0), transform: [ { translateX: showCancelDerivedValue.value ? withTiming(0) : measurement?.width ? withTiming(measurement.width) : cancelText.length * 11.2, }, ], }; } return { position: "absolute", right: 0, opacity: showCancelDerivedValue.value ? withTiming(1) : withTiming(0), transform: [ { translateX: showCancelDerivedValue.value ? withTiming(0) : withTiming(cancelText.length * 11.2), }, ], }; }); function focus() { inputRef.current?.focus(); } function blur() { inputRef.current?.blur(); } function clear() { onChangeText(""); } function onFocus(e: NativeSyntheticEvent) { setShowCancel(true); onFocusProp?.(e); } return ( ( )} /> { onChangeText(""); inputRef.current?.blur(); setShowCancel(false); onCancel?.(); }} disabled={!showCancel} pointerEvents={!showCancel ? "none" : "auto"} className="flex-1 justify-center active:opacity-50" > {cancelText} ); }, ); SearchInput.displayName = "SearchInput"; export { SearchInput };