import classNames from "classnames"; import type { ReactNode } from "react"; import { useMemo, useState } from "react"; import { Icon } from "./Icon"; interface Props { depth?: number; // oxlint-disable-next-line 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", }; } if (jsonType === "[object Array]") { return { children: isExpanded ? // oxlint-disable-next-line no-explicit-any attrValue.flatMap((v: any, i: number) => ( )) : null, isExpandable: attrValue.length > 0, label: isExpanded ? `[${attrValue.length || " "}]` : "[⋯]", labelClassName: "text-text-subtlest", }; } 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; return `${baseKey}.${quotedKey}`; } function joinArrayKey(baseKey: string | undefined, index: number): string { return `${baseKey ?? ""}[${index}]`; }