Skip to content

Instantly share code, notes, and snippets.

@chepetime
Last active August 9, 2024 17:45
Show Gist options
  • Select an option

  • Save chepetime/752cf94209fa487c02ec794b4f5d07cd to your computer and use it in GitHub Desktop.

Select an option

Save chepetime/752cf94209fa487c02ec794b4f5d07cd to your computer and use it in GitHub Desktop.
TS + React + Tailwind Component for debugging objects in the browser, with toggles
/* eslint-disable @typescript-eslint/no-explicit-any */
"use client";
import React, { useState } from "react";
interface JsonDebuggerProps {
data: any;
}
export const JsonDebugger: React.FC<JsonDebuggerProps> = ({ data }) => {
const [expanded, setExpanded] = useState<{ [key: string]: boolean }>({});
const expandAll = (obj: any, path: string[] = []) => {
if (typeof obj === "object" && obj !== null) {
const newExpanded: { [key: string]: boolean } = {};
const traverse = (obj: any, path: string[]) => {
Object.keys(obj).forEach((key) => {
const currentPath = [...path, key].join(".");
newExpanded[currentPath] = true;
if (typeof obj[key] === "object" && obj[key] !== null) {
traverse(obj[key], [...path, key]);
}
});
};
traverse(obj, path);
setExpanded(newExpanded);
}
};
const collapseAll = (obj: any, path: string[] = []) => {
if (typeof obj === "object" && obj !== null) {
const newCollapsed: { [key: string]: boolean } = {};
const traverse = (obj: any, path: string[]) => {
Object.keys(obj).forEach((key) => {
const currentPath = [...path, key].join(".");
newCollapsed[currentPath] = false;
if (typeof obj[key] === "object" && obj[key] !== null) {
traverse(obj[key], [...path, key]);
}
});
};
traverse(obj, path);
setExpanded(newCollapsed);
}
};
const toggleExpand = (key: string) => {
setExpanded((prev) => ({ ...prev, [key]: !prev[key] }));
};
const renderJson = (obj: any, path: string[] = []): JSX.Element | string => {
if (typeof obj !== "object" || obj === null) {
return <span>{JSON.stringify(obj)}</span>;
}
return (
<div className="ml-4">
{"{"}
{Object.entries(obj).map(([key, value], index, array) => {
const currentPath = [...path, key].join(".");
const isExpanded = expanded[currentPath];
return (
<div key={currentPath}>
<strong
onClick={() => toggleExpand(currentPath)}
className="cursor-pointer bg-gray-800 px-1 text-pink-600 hover:bg-gray-200"
>
{key}:
</strong>
{typeof value === "object" && value !== null ? (
isExpanded ? (
<div>{renderJson(value, [...path, key])}</div>
) : (
<span
onClick={() => toggleExpand(currentPath)}
className="cursor-pointer text-cyan-500 hover:bg-gray-200"
>
{" "}
{"{...}"}
</span>
)
) : (
<span> {JSON.stringify(value)}</span>
)}
{index < array.length - 1 && ","}
</div>
);
})}
{"}"}
</div>
);
};
return (
<div className="mx-auto max-w-7xl px-4 py-20">
<div className="mb-4 flex justify-between">
<button
onClick={() => expandAll(data)}
className="rounded bg-blue-500 px-4 py-2 text-white"
>
Expand All
</button>
<button
onClick={() => collapseAll(data)}
className="rounded bg-red-500 px-4 py-2 text-white"
>
Collapse All
</button>
</div>
<div className="max-h-[75vh] overflow-auto rounded-lg border bg-gray-900 p-4 text-white shadow-lg">
<pre className="whitespace-pre-wrap text-sm text-gray-100">
{renderJson(data)}
</pre>
</div>
</div>
);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment