import { forwardRef, useMemo, useState } from 'react' import { TextInput, View, type StyleProp, type TextInputProps, type TextStyle, type ViewStyle } from 'react-native' import { useControlledState, useDelay, type UseDelayOptionsResolved } from '@helpwave/hightide-utils/hooks' import { useTranslation } from '@helpwave/hightide-utils/context' import type { ColorPairToken } from '@helpwave/hightide-design/theme-tokens' import { useTheme } from '../../global-contexts/theme/ThemeContext' import { HightideIconRegistry } from '../../icons/HightideIconRegistry' import type { SearchBarContainerStyle, SearchBarIconButtonStyle, SearchBarInputStyle, SearchBarState } from '../../theme/types/components/searchBar' import type { StyleOverwrite } from '../../theme/types/resolver' import type { FormFieldDataHandling, FormFieldInteractionStates } from '../../types/formField' import { IconButton, type IconButtonProps } from './IconButton' export type SearchBarEditCompleteOptionsResolved = { onBlur: boolean, afterDelay: boolean, allowEnterComplete?: boolean, } & Omit export type SearchBarEditCompleteOptions = Partial const defaultEditCompleteOptions: SearchBarEditCompleteOptionsResolved = { allowEnterComplete: true, onBlur: true, afterDelay: false, delay: 2500, } export type SearchBarProps = Omit & Partial> & Partial & { color?: ColorPairToken, onSearch: (value: string) => void, searchButtonProps?: Omit, editCompleteOptions?: SearchBarEditCompleteOptions, initialValue?: string, style?: StyleProp, containerStyle?: StyleOverwrite, inputStyle?: StyleOverwrite, iconButtonStyle?: StyleOverwrite, textStyle?: StyleProp, } export const SearchBar = forwardRef(function SearchBar({ value: controlledValue, initialValue, color, invalid = false, disabled = false, readOnly = false, required = false, onValueChange, onEditComplete, onSearch, searchButtonProps, editCompleteOptions, style, containerStyle, inputStyle, iconButtonStyle, textStyle, placeholder, ...props }, ref) { const { theme } = useTheme() const translation = useTranslation() const [isHovered, setIsHovered] = useState(false) const [isPressed, setIsPressed] = useState(false) const [isFocused, setIsFocused] = useState(false) const [value, setValue] = useControlledState({ value: controlledValue, onValueChange, defaultValue: initialValue, }) const { onBlur: allowEditCompleteOnBlur, afterDelay, delay, allowEnterComplete, } = { ...defaultEditCompleteOptions, ...editCompleteOptions } const { restartTimer, clearTimer } = useDelay({ delay, disabled: !afterDelay || disabled || readOnly, }) const interactive = !disabled && !readOnly const state = useMemo((): SearchBarState => ({ color, isDisabled: disabled, isInvalid: invalid, isReadonly: readOnly, isHovered: interactive && isHovered, isPressed: interactive && isPressed, isFocused: interactive && isFocused, }), [color, disabled, invalid, readOnly, interactive, isHovered, isPressed, isFocused]) const searchBarTheme = theme.components.searchBar const resolvedContainerStyle = useMemo( () => searchBarTheme.container(state, containerStyle), [searchBarTheme, state, containerStyle] ) const resolvedInputStyle = useMemo( () => searchBarTheme.input(state, inputStyle), [searchBarTheme, state, inputStyle] ) const resolvedPlaceholderStyle = useMemo( () => searchBarTheme.placeholder(state), [searchBarTheme, state] ) const resolvedIconButtonStyle = useMemo( () => searchBarTheme.iconButton(state, iconButtonStyle), [searchBarTheme, state, iconButtonStyle] ) const commitSearch = (nextValue: string) => { onSearch(nextValue) onEditComplete?.(nextValue) } return ( { props.onChangeText?.(nextValue) restartTimer(() => { commitSearch(nextValue) }) setValue(nextValue) }} onFocus={(event) => { if (interactive) { setIsFocused(true) } props.onFocus?.(event) }} onBlur={(event) => { setIsFocused(false) props.onBlur?.(event) if (allowEditCompleteOnBlur) { commitSearch(value ?? '') clearTimer() } }} onPressIn={(event) => { if (interactive) { setIsPressed(true) } props.onPressIn?.(event) }} onPressOut={(event) => { setIsPressed(false) props.onPressOut?.(event) }} onPointerEnter={(event) => { if (interactive) { setIsHovered(true) } props.onPointerEnter?.(event) }} onPointerLeave={(event) => { setIsHovered(false) props.onPointerLeave?.(event) }} onSubmitEditing={(event) => { props.onSubmitEditing?.(event) if (allowEnterComplete) { commitSearch(value ?? '') clearTimer() } }} style={[resolvedInputStyle, textStyle]} accessibilityState={{ disabled, selected: required }} /> commitSearch(value ?? '')} style={[resolvedIconButtonStyle, searchButtonProps?.style]} /> ) })