import React, { useState, useEffect, useRef } from 'react'; type Option = { value: string; label: string; children?: Option[]; }; type CascaderProps = { options: Option[]; defaultValue?: string[]; onChange?: (value: string[]) => void; placeholder?: string; }; const Cascader: React.FC = ({ options, defaultValue = [], onChange, placeholder = 'Select' }) => { const [visible, setVisible] = useState(false); const [selected, setSelected] = useState(defaultValue); const [menus, setMenus] = useState([options]); const containerRef = useRef(null); useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (containerRef.current && !containerRef.current.contains(event.target as Node)) { setVisible(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, []); const handleItemClick = (option: Option, level: number) => { const newSelected = selected.slice(0, level).concat(option.value); setSelected(newSelected); if (option.children) { setMenus(menus.slice(0, level + 1).concat([option.children])); } else { setMenus(menus.slice(0, level + 1)); setVisible(false); onChange?.(newSelected); } }; const getDisplayText = () => { if (selected.length === 0) return placeholder; let currentOptions = options; return selected.map(val => { const opt = currentOptions.find(o => o.value === val); currentOptions = opt?.children || []; return opt?.label || ''; }).join(' / '); }; return (
setVisible(!visible)} > {getDisplayText()}
{visible && (
{menus.map((menu, level) => (
    {menu.map(option => (
  • handleItemClick(option, level)} > {option.label}
  • ))}
))}
)}
); }; export default Cascader;