import classNames from 'classnames'; import type { ReactNode } from 'react'; import { useMemo, useState } from 'react'; import { Icon } from './Icon'; interface Props { depth?: number; // eslint-disable-next-line @typescript-eslint/no-explicit-any attrValue: any; attrKey?: string | number; attrKeyJsonPath?: string; className?: string; } export const JsonAttributeTree = ({ depth = 0, attrKey, attrValue, attrKeyJsonPath, className, }: Props) => { attrKeyJsonPath = attrKeyJsonPath ?? `${attrKey}`; const [isExpanded, setIsExpanded] = useState(true); const toggleExpanded = () => setIsExpanded((v) => !v); const { isExpandable, children, label, labelClassName } = useMemo<{ isExpandable: boolean; children: ReactNode; label?: string; labelClassName?: string; }>(() => { const jsonType = Object.prototype.toString.call(attrValue); if (jsonType === '[object Object]') { return { children: isExpanded ? Object.keys(attrValue) .sort((a, b) => a.localeCompare(b)) .flatMap((k) => ( )) : null, isExpandable: Object.keys(attrValue).length > 0, label: isExpanded ? `{${Object.keys(attrValue).length || ' '}}` : `{⋯}`, labelClassName: 'text-text-subtlest', }; } else if (jsonType === '[object Array]') { return { children: isExpanded ? // eslint-disable-next-line @typescript-eslint/no-explicit-any attrValue.flatMap((v: any, i: number) => ( )) : null, isExpandable: attrValue.length > 0, label: isExpanded ? `[${attrValue.length || ' '}]` : `[⋯]`, labelClassName: 'text-text-subtlest', }; } else { return { children: null, isExpandable: false, label: jsonType === '[object String]' ? `"${attrValue}"` : `${attrValue}`, labelClassName: classNames( jsonType === '[object Boolean]' && 'text-primary', jsonType === '[object Number]' && 'text-info', jsonType === '[object String]' && 'text-notice', jsonType === '[object Null]' && 'text-danger', ), }; } }, [attrValue, attrKeyJsonPath, isExpanded, depth]); const labelEl = ( {label} ); return (
{isExpandable ? ( ) : ( <> {attrKey}: {labelEl} )}
{children &&
{children}
}
); }; function joinObjectKey(baseKey: string | undefined, key: string): string { const quotedKey = key.match(/^[a-z0-9_]+$/i) ? key : `\`${key}\``; if (baseKey == null) return quotedKey; else return `${baseKey}.${quotedKey}`; } function joinArrayKey(baseKey: string | undefined, index: number): string { return `${baseKey ?? ''}[${index}]`; }