import React, { useRef, useState, useEffect, useMemo } from 'react'; import './ListPicker.css'; /** * Kole UI ListPicker(React) * 对齐 组件7.txt ListPicker 规范:下拉列表、多选、分组、搜索、全选、标签 * * props: * - value / defaultValue : 选中 value 数组 * - options : 扁平 [{value,label}] 或 分组 [{label, children}] * - filterable * - placeholder * - onChange(keys) */ export default function ListPicker({ value, defaultValue = [], options = [], filterable = true, placeholder = '请选择', onChange }) { const rootRef = useRef(null); const [inner, setInner] = useState(defaultValue); const current = value !== undefined ? value : inner; const [open, setOpen] = useState(false); const [query, setQuery] = useState(''); const isGrouped = options.length && options[0] && options[0].children; const flat = isGrouped ? options.flatMap(g => g.children) : options; const labelMap = Object.fromEntries(flat.map(o => [o.value, o.label])); const flatVisible = useMemo(() => flat.filter(o => o.label.includes(query)), [flat, query]); const visibleGroups = useMemo(() => { if (!isGrouped) return [{ children: flatVisible }]; return options .map(g => ({ label: g.label, children: g.children.filter(o => o.label.includes(query)) })) .filter(g => g.children.length); }, [options, flatVisible, query]); const commit = (next) => { if (value === undefined) setInner(next); onChange && onChange(next); }; const toggleOpt = (o) => { if (o.disabled) return; commit(current.includes(o.value) ? current.filter(x => x !== o.value) : [...current, o.value]); }; const remove = (v) => commit(current.filter(x => x !== v)); const selectAll = () => commit([...new Set([...current, ...flatVisible.map(o => o.value)])]); useEffect(() => { const onDoc = (e) => { if (rootRef.current && !rootRef.current.contains(e.target)) { setOpen(false); setQuery(''); } }; document.addEventListener('click', onDoc, true); return () => document.removeEventListener('click', onDoc, true); }, []); return (