import type { CSSProperties, ReactNode } from "react"; export type ConnectionTreeColumn = { id: string; header: ReactNode; width?: string; align?: "left" | "center" | "right"; render: (row: T, depth: number) => ReactNode; }; export type ConnectionTreeProps = { rows: T[]; columns: ConnectionTreeColumn[]; getRowKey: (row: T) => string; getChildren?: (row: T) => T[]; renderActions?: (row: T, depth: number) => ReactNode; emptyText?: ReactNode; className?: string; rowClassName?: (row: T, depth: number) => string; }; type FlatRow = { row: T; depth: number; }; export default function ConnectionTree({ rows, columns, getRowKey, getChildren, renderActions, emptyText = "i18n:govoplan-core.no_entries_configured.48e7b97c", className = "", rowClassName }: ConnectionTreeProps) { const flatRows = flattenRows(rows, getChildren); const gridTemplateColumns = `${columns.map((column) => column.width || "minmax(0, 1fr)").join(" ")} ${renderActions ? "var(--connection-tree-actions-column, 196px)" : ""}`.trim(); return (
{columns.map((column) =>
{column.header}
)} {renderActions &&
i18n:govoplan-core.actions.c3cd636a
}
{flatRows.length === 0 &&
{emptyText}
} {flatRows.map(({ row, depth }) => { const className = ["connection-tree-row", depth > 0 ? "is-child" : "is-parent", rowClassName?.(row, depth) || ""].filter(Boolean).join(" "); return (
{columns.map((column, index) =>
{index === 0 ?
{column.render(row, depth)}
: column.render(row, depth) }
)} {renderActions &&
{renderActions(row, depth)}
}
); })}
); } function flattenRows(rows: T[], getChildren?: (row: T) => T[]): FlatRow[] { const result: FlatRow[] = []; for (const row of rows) { result.push({ row, depth: 0 }); for (const child of getChildren?.(row) ?? []) { result.push({ row: child, depth: 1 }); } } return result; }