import { useState, memo } from 'react';
import { CopyButton } from './CopyButton';
interface JsonViewerProps {
value: unknown;
/** Default-open nesting depth */
defaultOpenDepth?: number;
}
/** Strings longer than this get an inline expand affordance instead of a
* truncated preview-only display. */
const LONG_STRING_THRESHOLD = 200;
export function JsonViewer({ value, defaultOpenDepth = 2 }: JsonViewerProps) {
return (
);
}
interface NodeProps {
value: unknown;
depth: number;
defaultOpenDepth: number;
keyPath: string;
keyLabel?: string | number;
isLast?: boolean;
}
const Node = memo(function Node({
value,
depth,
defaultOpenDepth,
keyPath,
keyLabel,
isLast,
}: NodeProps) {
const [open, setOpen] = useState(depth < defaultOpenDepth);
if (value === null)
return ;
if (value === undefined)
return ;
if (typeof value === 'boolean')
return ;
if (typeof value === 'number')
return ;
if (typeof value === 'string') {
if (value.length <= LONG_STRING_THRESHOLD) {
return (
);
}
return (
{open ? (
) : null}
);
}
if (Array.isArray(value)) {
if (value.length === 0)
return ;
return (
{open ? (
{value.map((v, i) => (
))}
) : null}
);
}
if (typeof value === 'object') {
const entries = Object.entries(value as Record);
if (entries.length === 0)
return ;
return (
{open ? (
{entries.map(([k, v], i) => (
))}
) : null}
);
}
return ;
});
function Leaf({
keyLabel,
repr,
color,
}: {
keyLabel?: string | number;
repr: string;
color: string;
isLast?: boolean;
}) {
return (
{keyLabel !== undefined ? (
<>
{keyLabel}
:
>
) : null}
{repr}
);
}