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;
}
export const JsonAttributeTree = ({ depth = 0, attrKey, attrValue, attrKeyJsonPath }: 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-fg-subtler',
};
} 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-subtler',
};
} else {
return {
children: null,
isExpandable: false,
label: jsonType === '[object String]' ? `"${attrValue}"` : `${attrValue}`,
labelClassName: classNames(
jsonType === '[object Boolean]' && 'text-fg-primary',
jsonType === '[object Number]' && 'text-fg-info',
jsonType === '[object String]' && 'text-fg-notice',
jsonType === '[object Null]' && 'text-fg-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}]`;
}