"use client"; import React, { useState, useEffect, useRef } from "react"; import { Input } from "@/components/ui/input"; import { X, Loader2, ChevronDown } from "lucide-react"; import { Button } from "@/components/ui/button"; import { useEntitySearch } from "../entity-search-input/useEntitySearch"; import { EntitySearchResult } from "../entity-search-input/types"; import { cn } from "@/lib/utils"; import { AutocompleteSearchInputConfig } from "./types"; interface AutocompleteSearchInputProps extends Partial { config?: AutocompleteSearchInputConfig; filterCondition?: Record; disabled?: boolean; value?: any; onChange?: (value: any, fullData?: any) => void; className?: string; } export function AutocompleteSearchInputComponent({ config, tableName: propTableName, displayField: propDisplayField, valueField: propValueField, searchFields: propSearchFields, filterCondition = {}, placeholder: propPlaceholder, disabled = false, value, onChange, showAdditionalInfo: propShowAdditionalInfo, additionalFields: propAdditionalFields, className, }: AutocompleteSearchInputProps) { // config prop 우선, 없으면 개별 prop 사용 const tableName = config?.tableName || propTableName || ""; const displayField = config?.displayField || propDisplayField || ""; const valueField = config?.valueField || propValueField || ""; const searchFields = config?.searchFields || propSearchFields || [displayField]; const placeholder = config?.placeholder || propPlaceholder || "검색..."; const showAdditionalInfo = config?.showAdditionalInfo ?? propShowAdditionalInfo ?? false; const additionalFields = config?.additionalFields || propAdditionalFields || []; const [inputValue, setInputValue] = useState(""); const [isOpen, setIsOpen] = useState(false); const [selectedData, setSelectedData] = useState(null); const containerRef = useRef(null); const { searchText, setSearchText, results, loading, clearSearch } = useEntitySearch({ tableName, searchFields, filterCondition, }); // value가 변경되면 표시값 업데이트 useEffect(() => { if (value && selectedData) { setInputValue(selectedData[displayField] || ""); } else if (!value) { setInputValue(""); setSelectedData(null); } }, [value, displayField]); // 외부 클릭 감지 useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (containerRef.current && !containerRef.current.contains(event.target as Node)) { setIsOpen(false); } }; document.addEventListener("mousedown", handleClickOutside); return () => document.removeEventListener("mousedown", handleClickOutside); }, []); const handleInputChange = (e: React.ChangeEvent) => { const newValue = e.target.value; setInputValue(newValue); setSearchText(newValue); setIsOpen(true); }; const handleSelect = (item: EntitySearchResult) => { setSelectedData(item); setInputValue(item[displayField] || ""); onChange?.(item[valueField], item); setIsOpen(false); }; const handleClear = () => { setInputValue(""); setSelectedData(null); onChange?.(null, null); setIsOpen(false); }; const handleInputFocus = () => { // 포커스 시 항상 검색 실행 (빈 값이면 전체 목록) if (!selectedData) { setSearchText(inputValue || ""); setIsOpen(true); } }; return (
{/* 입력 필드 */}
{loading && ( )} {inputValue && !disabled && ( )}
{/* 드롭다운 결과 */} {isOpen && (results.length > 0 || loading) && (
{loading && results.length === 0 ? (
검색 중...
) : results.length === 0 ? (
검색 결과가 없습니다
) : (
{results.map((item, index) => ( ))}
)}
)} {/* 추가 정보 표시 */} {showAdditionalInfo && selectedData && additionalFields.length > 0 && (
{additionalFields.map((field) => (
{field}: {selectedData[field] || "-"}
))}
)}
); }