import { useCallback, useMemo, useRef, useState } from 'react' import { IconButton, InputAdornment, TextField } from '@mui/material' import { Search as SearchIcon } from '@mui/icons-material' import { Clear as ClearIcon } from '@mui/icons-material' import { debounce } from '@carto/ps-utils' import { getWidgetStore, useWidget, useWidgetId } from '../../stores' import { setSearcherText } from './searcher-toggle' import { DEFAULT_SEARCHER_LABELS, type SearcherLabels } from './labels' import { styles } from './style' export interface SearcherProps { labels?: Partial /** Debounce delay before the input value is written to the widget store. */ debounceMs?: number } const DEFAULT_DEBOUNCE = 300 /** * Renders only when the matching `` is enabled — drives the * `transformStates['searcher'].searchText` field via a debounced setter. */ export function Searcher({ labels, debounceMs = DEFAULT_DEBOUNCE, }: SearcherProps) { const id = useWidgetId() const resolved = { ...DEFAULT_SEARCHER_LABELS, ...labels } const enabled = useWidget( id, (s) => s.transformStates.searcher?.enabled ?? false, ) const inputRef = useRef(null) // Hydrate from the store so re-toggling the input preserves any prior text. const [local, setLocal] = useState( () => (getWidgetStore(id).getState().transformStates.searcher?.searchText as | string | undefined) ?? '', ) const debouncedWrite = useMemo( () => debounce( (value: unknown) => setSearcherText(id, value as string), debounceMs, ), [id, debounceMs], ) const handleSearchTextChange = useCallback( (e: React.ChangeEvent) => { const value = e.target.value setLocal(value) debouncedWrite(value) }, [debouncedWrite], ) const handleClear = useCallback(() => { setLocal('') setSearcherText(id, '') // Re-arm the debounced timer with '' so any pending stale write resolves // to the cleared value rather than re-applying the previous text. debouncedWrite('') inputRef.current?.focus() }, [id, debouncedWrite]) if (!enabled) return null return ( ), endAdornment: local ? ( ) : null, }} /> ) }