/** * 재현경로 입력 — 직접 입력 + 관련 메뉴 경로 자동완성 */ import React, { useEffect, useId, useRef, useState } from 'react'; import { searchAppNavigationPaths } from '../../utils/appNavigationPaths'; interface Props { value: string; onChange: (value: string) => void; disabled?: boolean; placeholder?: string; } const ReproductionPathInput: React.FC = ({ value, onChange, disabled = false, placeholder = '예: 메뉴-실시간 차트-지표 추가 버튼', }) => { const listId = useId(); const wrapRef = useRef(null); const [open, setOpen] = useState(false); const [activeIdx, setActiveIdx] = useState(-1); const suggestions = searchAppNavigationPaths(value); useEffect(() => { setActiveIdx(-1); }, [value, suggestions.length]); useEffect(() => { const onDoc = (e: MouseEvent) => { if (wrapRef.current && !wrapRef.current.contains(e.target as Node)) { setOpen(false); } }; document.addEventListener('mousedown', onDoc); return () => document.removeEventListener('mousedown', onDoc); }, []); const pick = (path: string) => { onChange(path); setOpen(false); setActiveIdx(-1); }; const showList = open && !disabled && suggestions.length > 0; return (
{ onChange(e.target.value); setOpen(true); }} onFocus={() => setOpen(true)} onKeyDown={e => { if (!showList) { if (e.key === 'ArrowDown' && suggestions.length > 0) { setOpen(true); setActiveIdx(0); e.preventDefault(); } return; } if (e.key === 'ArrowDown') { e.preventDefault(); setActiveIdx(i => Math.min(i + 1, suggestions.length - 1)); } else if (e.key === 'ArrowUp') { e.preventDefault(); setActiveIdx(i => Math.max(i - 1, 0)); } else if (e.key === 'Enter' && activeIdx >= 0) { e.preventDefault(); pick(suggestions[activeIdx]!.path); } else if (e.key === 'Escape') { setOpen(false); setActiveIdx(-1); } }} /> {showList && (
    {suggestions.map((s, i) => (
  • ))}
)} {open && value.trim() && suggestions.length === 0 && (

일치하는 메뉴 경로가 없습니다. 직접 입력한 경로가 저장됩니다.

)}
); }; export default ReproductionPathInput;