import React, { useEffect, useMemo, useRef, useState } from 'react' function normalizeSearchText(value) { return String(value || '') .normalize('NFD') .replace(/[\u0300-\u036f]/g, '') .toLowerCase() .trim() } function normalizeOption(option) { if (typeof option === 'string') { const text = String(option || '').trim() return text ? { value: text, label: text, secondary: '' } : null } if (!option || typeof option !== 'object') return null const rawValue = option.value ?? option.id ?? option.key ?? '' const value = String(rawValue || '').trim() if (!value) return null const label = String(option.label ?? option.nome_modelo ?? option.arquivo ?? value).trim() || value const secondary = String(option.secondary ?? option.arquivo ?? '').trim() return { value, label, secondary } } export default function SinglePillAutocomplete({ value, onChange, options = [], placeholder = 'Selecione um item', panelTitle = '', emptyMessage = 'Nenhuma sugestao encontrada.', loading = false, disabled = false, onOpenChange = null, }) { const rootRef = useRef(null) const inputRef = useRef(null) const [query, setQuery] = useState('') const [open, setOpen] = useState(false) const [activeIndex, setActiveIndex] = useState(-1) const selectedValue = String(value || '') const normalizedOptions = useMemo(() => { const unique = [] const seen = new Set() ;(options || []).forEach((item) => { const normalized = normalizeOption(item) if (!normalized) return if (seen.has(normalized.value)) return seen.add(normalized.value) unique.push(normalized) }) return unique }, [options]) const selectedOption = useMemo( () => normalizedOptions.find((item) => item.value === selectedValue) || null, [normalizedOptions, selectedValue], ) const queryNormalized = normalizeSearchText(query) const filteredOptions = useMemo(() => { if (loading) return [] if (!queryNormalized) return normalizedOptions.slice(0, 160) return normalizedOptions .filter((item) => ( normalizeSearchText(item.label).includes(queryNormalized) || normalizeSearchText(item.secondary).includes(queryNormalized) )) .slice(0, 160) }, [loading, normalizedOptions, queryNormalized]) useEffect(() => { if (!open) return undefined function onDocumentMouseDown(event) { if (!rootRef.current) return if (!rootRef.current.contains(event.target)) setOpen(false) } document.addEventListener('mousedown', onDocumentMouseDown) return () => document.removeEventListener('mousedown', onDocumentMouseDown) }, [open]) useEffect(() => { if (typeof onOpenChange !== 'function') return onOpenChange(Boolean(open && !disabled)) }, [open, disabled, onOpenChange]) useEffect(() => () => { if (typeof onOpenChange === 'function') onOpenChange(false) }, [onOpenChange]) useEffect(() => { if (!open || filteredOptions.length === 0) { setActiveIndex(-1) return } if (activeIndex >= filteredOptions.length) setActiveIndex(filteredOptions.length - 1) }, [activeIndex, filteredOptions, open]) function emitChange(nextValue) { if (typeof onChange === 'function') onChange(String(nextValue || '')) } function selectOption(option) { if (!option) return emitChange(option.value) setQuery('') setOpen(false) setActiveIndex(-1) } function clearSelection(event) { event.preventDefault() event.stopPropagation() emitChange('') setQuery('') setOpen(true) setActiveIndex(-1) window.requestAnimationFrame(() => inputRef.current?.focus()) } function onInputChange(event) { if (disabled) return setQuery(event.target.value) setOpen(true) setActiveIndex(-1) } function onInputFocus() { if (disabled) return setOpen(true) setActiveIndex(-1) } function onInputKeyDown(event) { if (disabled) return if (event.key === 'Escape') { setOpen(false) return } if (event.key === 'Backspace' && !query && selectedOption) { emitChange('') setOpen(true) return } if (!filteredOptions.length) return if (event.key === 'ArrowDown') { event.preventDefault() setOpen(true) setActiveIndex((prev) => (prev < 0 ? 0 : (prev + 1) % filteredOptions.length)) return } if (event.key === 'ArrowUp') { event.preventDefault() setOpen(true) setActiveIndex((prev) => { if (prev < 0) return filteredOptions.length - 1 return (prev - 1 + filteredOptions.length) % filteredOptions.length }) return } if (event.key === 'Enter') { event.preventDefault() if (activeIndex >= 0 && activeIndex < filteredOptions.length) { selectOption(filteredOptions[activeIndex]) return } if (filteredOptions.length === 1) { selectOption(filteredOptions[0]) } } } return (