import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { collectSkillLeavesFromTree, buildSkillCatalogTree } from '../utils/skillCatalogTree' import SkillTreePickerPanel from './SkillTreePickerPanel' function normId(id) { return String(id) } /** * Mehrfachauswahl Fähigkeiten mit hierarchischer Treeview („Alle“) und Pfad-Suche. */ export default function SkillTreeMultiSelect({ value = [], onChange, skills = [], placeholder = 'Fähigkeit suchen …', browseLabel = '▼ Katalog', emptyHint = 'Keine Treffer', className = '', }) { const [query, setQuery] = useState('') const [open, setOpen] = useState(false) const [browseTree, setBrowseTree] = useState(false) const rootRef = useRef(null) const tree = useMemo(() => buildSkillCatalogTree(skills), [skills]) const selectedSet = useMemo(() => new Set(value.map(normId)), [value]) const leaves = useMemo(() => collectSkillLeavesFromTree(tree, value), [tree, value]) const selectedLabels = useMemo(() => { return value.map((id) => { const leaf = leaves.find((l) => normId(l.id) === normId(id)) || leaves.find(() => false) const fromSkills = skills.find((s) => normId(s.id) === normId(id)) return leaf?.pathLabel || fromSkills?.name || `#${id}` }) }, [value, leaves, skills]) const addId = useCallback( (id) => { const sid = normId(id) if (selectedSet.has(sid)) return onChange([...value, id]) setQuery('') setBrowseTree(false) }, [value, onChange, selectedSet] ) const removeAt = useCallback( (idx) => { onChange(value.filter((_, i) => i !== idx)) }, [value, onChange] ) useEffect(() => { const onDoc = (e) => { if (!rootRef.current?.contains(e.target)) { setOpen(false) setBrowseTree(false) } } document.addEventListener('mousedown', onDoc) return () => document.removeEventListener('mousedown', onDoc) }, []) const showTree = browseTree || !query.trim() return (