shinkan-jinkendo/frontend/src/components/SkillTreeSelect.jsx
Lars 9020e5eb16
Some checks failed
Deploy Development / deploy (push) Successful in 40s
Test Suite / pytest-backend (push) Successful in 38s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 14s
Test Suite / k6 /health Baseline (push) Successful in 33s
Test Suite / playwright-tests (push) Has been cancelled
Enhance Skill Tree Components with Improved Group Labeling and Default Expansion
- Added CSS styles for skill group labels in the skill tree to improve visual hierarchy and readability.
- Updated `SkillTreePickerPanel` and `SkillTreeMultiSelect` components to utilize the new default expansion logic, ensuring main and category nodes are open by default while skill groups remain collapsed.
- Refactored state management in `SkillTreePickerPanel` to align with the new default expansion behavior.
- Enhanced utility functions to support the new default expansion logic for skill trees.
2026-05-20 11:13:34 +02:00

85 lines
2.4 KiB
JavaScript

import React, { useEffect, useMemo, useRef, useState } from 'react'
import { skillCatalogPathLabel } from '../utils/skillCatalogTree'
import SkillTreePickerPanel from './SkillTreePickerPanel'
/**
* Einzelauswahl einer Fähigkeit als hierarchische Treeview.
*/
export default function SkillTreeSelect({
value = '',
onChange,
skills = [],
excludeIds,
placeholder = 'Fähigkeit wählen…',
disabled = false,
className = '',
searchPlaceholder = 'Fähigkeit suchen…',
}) {
const [open, setOpen] = useState(false)
const [query, setQuery] = useState('')
const rootRef = useRef(null)
const selected = useMemo(() => {
const id = value ? Number(value) : NaN
if (!Number.isFinite(id)) return null
return skills.find((s) => Number(s.id) === id) || null
}, [value, skills])
const displayLabel = selected ? skillCatalogPathLabel(selected) : ''
useEffect(() => {
const onDoc = (e) => {
if (!rootRef.current?.contains(e.target)) {
setOpen(false)
setQuery('')
}
}
document.addEventListener('mousedown', onDoc)
return () => document.removeEventListener('mousedown', onDoc)
}, [])
const pick = (skillId) => {
onChange(String(skillId))
setOpen(false)
setQuery('')
}
return (
<div className={`skill-tree-select ${className}`.trim()} ref={rootRef}>
<button
type="button"
className="form-input skill-tree-select__trigger"
disabled={disabled}
aria-haspopup="listbox"
aria-expanded={open}
onClick={() => !disabled && setOpen((o) => !o)}
>
<span className={displayLabel ? '' : 'skill-tree-select__placeholder'}>
{displayLabel || placeholder}
</span>
</button>
{open ? (
<div className="skill-tree-select__panel">
<input
type="search"
className="form-input skill-tree-select__search"
placeholder={searchPlaceholder}
value={query}
onChange={(e) => setQuery(e.target.value)}
autoComplete="off"
/>
<div className="skill-tree-select__tree-wrap">
<SkillTreePickerPanel
skills={skills}
excludeIds={excludeIds}
searchQuery={query}
onPickSkill={pick}
pickMode="single"
/>
</div>
</div>
) : null}
</div>
)
}