import { cn } from '@gentleduck/libs/cn' import React from 'react' import { ChevronDown, ChevronRight } from './icons' export interface IJsonTreeProps { data: unknown label?: string defaultOpen?: boolean level?: number } function typeOf(v: unknown): 'object' | 'array' | 'string' | 'number' | 'boolean' | 'null' | 'undefined' | 'function' { if (v === null) return 'null' if (Array.isArray(v)) return 'array' if (typeof v === 'object') return 'object' return typeof v as 'string' | 'number' | 'boolean' | 'undefined' | 'function' } function previewLen(v: unknown): string { if (Array.isArray(v)) return `${v.length} ${v.length === 1 ? 'item' : 'items'}` if (v && typeof v === 'object') { const n = Object.keys(v as Record).length return `${n} ${n === 1 ? 'item' : 'items'}` } return '' } function Primitive({ value }: { value: unknown }) { const t = typeOf(value) if (t === 'string') return "{String(value)}" if (t === 'number') return {String(value)} if (t === 'boolean') return {String(value)} if (t === 'null') return null if (t === 'undefined') return undefined if (t === 'function') return ƒ() return {String(value)} } export function JsonTree({ data, label, defaultOpen = false, level = 0 }: IJsonTreeProps) { const t = typeOf(data) const isContainer = t === 'object' || t === 'array' const [open, setOpen] = React.useState(defaultOpen || level === 0) if (!isContainer) { return (
{label != null && {label}:}
) } const entries = Array.isArray(data) ? (data as unknown[]).map((v, i) => [String(i), v] as const) : Object.entries(data as Record) const summary = previewLen(data) return (
{open && (
{entries.map(([k, v]) => ( ))}
{t === 'array' ? ']' : '}'}
)}
) }