'use client'; import { ChevronRight } from 'lucide-react'; import React, { useState } from 'react'; import { cn } from '@djangocfg/ui-core/lib'; import type { FieldNode } from './types'; interface FieldRowProps { field: FieldNode; /** 0-based nesting level. Used to compute left padding so the tree * visually expresses hierarchy without relying on a separate tree * component (no deps, one file). */ depth: number; /** Parent container nodes render with an extra vertical tick so the * tree reads as a rooted graph instead of flat indented rows. */ showTreeLine?: boolean; } /** Recursive tree row. Object/array nodes are collapsible and default * to expanded at shallow depths — readers almost always want to see * the first level of nesting, and explicitly collapsing uninteresting * subtrees is cheaper than expanding every interesting one. */ export function FieldRow({ field, depth, showTreeLine = true }: FieldRowProps) { const isExpandable = (field.kind === 'object' || field.kind === 'array') && Array.isArray(field.children) && field.children.length > 0; // Expand levels 0-1 by default; beyond that the tree can explode // visually for deeply-nested objects (think Pet.category.tag.…). const [open, setOpen] = useState(depth < 2); // Inline padding via style so arbitrary depths render without a // pile of Tailwind classes. 14px per level matches the chevron gap // at smaller text sizes. const padLeft = showTreeLine ? depth * 14 : 0; const toggle = () => isExpandable && setOpen((v) => !v); return (
{ if (!isExpandable) return; if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggle(); } }} role={isExpandable ? 'button' : undefined} tabIndex={isExpandable ? 0 : undefined} aria-expanded={isExpandable ? open : undefined} >
{field.name} {field.required && ( * )} {field.type}
{field.description && (

{field.description}

)} {field.enumValues && field.enumValues.length > 0 && (
{field.enumValues.map((v) => ( {v} ))}
)}
{isExpandable && open && (
{field.children!.map((child, i) => ( ))}
)}
); }