import clsx from 'clsx'; import { useEffect, useState } from 'react'; import { globals, PARAMETER } from '@/utils/constants'; import { Overlay } from '../Container'; import { MiniButton } from '../Control'; import { IconDropArrow, IconPageRight } from '../Icons'; import { CProps } from '../props'; interface SelectTreeProps extends CProps.Styling { /** Current value. */ value: ItemType; /** List of available items. */ items: ItemType[]; /** Prefix for the ids of the elements. */ prefix: string; /** Callback to be called when the value changes. */ onChange: (newItem: ItemType) => void; /** Callback providing the parent of the item. */ getParent: (item: ItemType) => ItemType; /** Callback providing the label of the item. */ getLabel: (item: ItemType) => string; /** Callback providing the description of the item. */ getDescription: (item: ItemType) => string; } /** * Displays a tree of items and allows user to select one. */ export function SelectTree({ items, value, getParent, getLabel, getDescription, onChange, prefix, ...restProps }: SelectTreeProps) { const foldable = new Set(items.filter(item => getParent(item) !== item).map(item => getParent(item))); const [folded, setFolded] = useState(items); useEffect(() => { setFolded(items.filter(item => getParent(value) !== item && getParent(getParent(value)) !== item)); }, [value, getParent, items]); function onFoldItem(target: ItemType, showChildren: boolean) { setFolded(prev => items.filter(item => { if (item === target) { return !showChildren; } if (!showChildren && (getParent(item) === target || getParent(getParent(item)) === target)) { return true; } else { return prev.includes(item); } }) ); } function handleClickFold(event: CProps.EventMouse, target: ItemType, showChildren: boolean) { event.preventDefault(); event.stopPropagation(); onFoldItem(target, showChildren); } function handleSetValue(event: CProps.EventMouse, target: ItemType) { event.preventDefault(); event.stopPropagation(); onChange(target); } return (
{items.map((item, index) => { const isActive = getParent(item) === item || !folded.includes(getParent(item)); return (
handleSetValue(event, item)} style={{ borderBottomWidth: isActive ? '1px' : '0px', transitionProperty: 'height, opacity, padding', transitionDuration: `${PARAMETER.moveDuration}ms`, paddingTop: isActive ? '0.25rem' : '0', paddingBottom: isActive ? '0.25rem' : '0', height: isActive ? 'min-content' : '0', opacity: isActive ? '1' : '0' }} > {foldable.has(item) ? ( : } onClick={event => handleClickFold(event, item, folded.includes(item))} /> ) : null} {getParent(item) === item ? getLabel(item) : `- ${getLabel(item).toLowerCase()}`}
); })}
); }