import type { HTMLAttributes, ReactNode } from "react"; import { Children, createContext, isValidElement, useContext, useState } from "react"; import { cx } from "../../utils/cx.utils"; import { Icon } from "../icons/icon"; import "./tree.component.css"; interface TreeItemContextValue { /** Whether this item is the last child among its siblings, so its connector line stops at the branch. */ isLast: boolean; } const TreeItemContext = createContext({ isLast: true }); /** Props accepted by {@link Tree.Item}. */ export interface TreeItemProps extends Omit, "onClick"> { /** Label shown next to the branch/leaf icon. */ label: ReactNode; /** Nested {@link Tree.Item} elements. When present, this item renders as an expandable branch. */ children?: ReactNode; /** Whether the branch starts expanded. Defaults to `true`. Ignored on leaf items (no children). */ initialExpanded?: boolean; } /** * Single node in a {@link Tree}, connected to its siblings and parent by * guide lines. Renders as an expandable branch (folder) when it has nested * `Tree.Item` children, or as a leaf (file) otherwise. * * @example * ```tsx * * * * ``` */ function TreeItem({ className, label, children, initialExpanded = true, ...rest }: TreeItemProps) { const { isLast } = useContext(TreeItemContext); const [expanded, setExpanded] = useState(initialExpanded); const hasChildren = children != null && children !== false; return (
  • {hasChildren ? ( ) : ( {label} )}
    {hasChildren && expanded && {children}}
  • ); } interface TreeGroupProps { children: ReactNode; isRoot: boolean; } function TreeGroup({ children, isRoot }: TreeGroupProps) { const items = Children.toArray(children); return (
      {items.map((child, index) => ( {child} ))}
    ); } /** Props accepted by {@link Tree}. */ export interface TreeProps extends Omit, "children"> { /** {@link Tree.Item} elements forming the top level of the tree. */ children: ReactNode; } /** * A hierarchical folder/file tree with guide lines connecting each node to * its siblings and parent. Composes {@link Tree.Item}, which can itself * nest further `Tree.Item`s to form branches; branches are expandable and * collapsible. * * @example * ```tsx * * * * * * * * * ``` */ export function Tree({ className, children, ...rest }: TreeProps) { return (
    {children}
    ); } Tree.Item = TreeItem;