123 lines
3.3 KiB
TypeScript
123 lines
3.3 KiB
TypeScript
// SearchInputText.tsx
|
|
"use client";
|
|
import { Input } from "@common/components/ui";
|
|
import { useEffect, useRef, useState } from "react";
|
|
|
|
interface SearchInputTextProps {
|
|
initValue?: string;
|
|
setValue: (val: string) => void;
|
|
setRtnData: (val: { fnm: string; fid: string }) => void;
|
|
searchList: { fnm: string; fid: string }[];
|
|
onEnter?: () => void;
|
|
className?: string;
|
|
}
|
|
|
|
export default function SearchInputText({
|
|
initValue,
|
|
setValue,
|
|
setRtnData,
|
|
searchList,
|
|
onEnter,
|
|
className,
|
|
}: SearchInputTextProps) {
|
|
const [showResults, setShowResults] = useState(false);
|
|
const [text, setText] = useState(initValue ?? "");
|
|
const [highlightIndex, setHighlightIndex] = useState(-1);
|
|
const wrapperRef = useRef(null);
|
|
|
|
useEffect(() => {
|
|
setText(initValue ?? "");
|
|
}, [initValue]);
|
|
|
|
useEffect(() => {
|
|
if (searchList.length > 0) {
|
|
setShowResults(true);
|
|
} else {
|
|
setShowResults(false);
|
|
}
|
|
setHighlightIndex(-1);
|
|
}, [searchList]);
|
|
|
|
useEffect(() => {
|
|
function handleClickOutside(event: MouseEvent) {
|
|
if (
|
|
wrapperRef.current &&
|
|
!(wrapperRef.current as any).contains(event.target)
|
|
) {
|
|
setShowResults(false);
|
|
}
|
|
}
|
|
document.addEventListener("mousedown", handleClickOutside);
|
|
return () => {
|
|
document.removeEventListener("mousedown", handleClickOutside);
|
|
};
|
|
}, [wrapperRef]);
|
|
|
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
setText(e.target.value);
|
|
setValue(e.target.value);
|
|
setHighlightIndex(-1);
|
|
};
|
|
|
|
const handleSelect = (item: { fnm: string; fid: string }) => {
|
|
setText(item.fnm);
|
|
setValue(item.fnm);
|
|
setRtnData(item);
|
|
setShowResults(false);
|
|
};
|
|
|
|
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
if (e.key === "Enter") {
|
|
if (highlightIndex >= 0 && highlightIndex < searchList.length) {
|
|
const selected = searchList[highlightIndex];
|
|
if (selected) handleSelect(selected);
|
|
} else if (onEnter) {
|
|
onEnter();
|
|
}
|
|
} else if (e.key === "ArrowDown") {
|
|
e.preventDefault();
|
|
setHighlightIndex((prev) =>
|
|
prev < searchList.length - 1 ? prev + 1 : prev
|
|
);
|
|
} else if (e.key === "ArrowUp") {
|
|
e.preventDefault();
|
|
setHighlightIndex((prev) => (prev > 0 ? prev - 1 : -1));
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="relative" ref={wrapperRef}>
|
|
<Input
|
|
value={text}
|
|
onChange={handleChange}
|
|
onKeyDown={handleKeyDown}
|
|
placeholder="시설명을 입력하세요"
|
|
className={
|
|
className + " rounded-lg border px-2 py-1 text-sm h-[29.5px]"
|
|
}
|
|
/>
|
|
{showResults && (
|
|
<div className="absolute z-10 mt-1 max-h-200 w-full overflow-auto rounded border bg-white shadow">
|
|
{searchList.length > 0 ? (
|
|
searchList.map((item, index) => (
|
|
<div
|
|
key={index}
|
|
className={`cursor-pointer px-3 py-1 hover:bg-gray-100 ${
|
|
highlightIndex === index ? "bg-blue-100" : ""
|
|
}`}
|
|
onClick={() => handleSelect(item)}
|
|
>
|
|
{item.fnm}
|
|
</div>
|
|
))
|
|
) : (
|
|
<div className="px-3 py-2 text-sm text-gray-500">
|
|
검색 결과가 없습니다.
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|