refactor: add TTY

This commit is contained in:
2026-02-24 11:54:18 +08:00
parent 0bbc21bfa0
commit ff7948726f
2 changed files with 34 additions and 22 deletions
+1
View File
@@ -9,6 +9,7 @@ export default function JsonGrid() {
const { t } = useTranslation()
const initialData = [
{ id: 0, name: "TTY", roles: "CEO", active: true },
{ id: 1, name: "Alice", role: "Developer", active: true },
{ id: 2, name: "Bob", role: "Designer", active: false },
{ id: 3, name: "Charlie", role: "Product Manager", active: true },
+25 -14
View File
@@ -15,6 +15,7 @@ export default function JsonViewer() {
location: "London",
is_active: true,
staff_members: [
{ id: 100, name: "TTY", roles: ["CEO"] },
{ id: 101, name: "Alice", roles: ["Admin", "Manager"] },
{ id: 102, name: "Bob", roles: ["Developer"] },
],
@@ -39,7 +40,13 @@ export default function JsonViewer() {
try {
parsed = JSON.parse(jsonInput)
} catch (e) {
return { parsed: null, matchedPaths: [], matchedValues: [], error: (e as Error).message, queryError: null }
return {
parsed: null,
matchedPaths: [],
matchedValues: [],
error: (e as Error).message,
queryError: null,
}
}
try {
@@ -53,7 +60,13 @@ export default function JsonViewer() {
}
} catch (e) {
// When JSONPath expression is invalid, still display the JSON tree but with no matches
return { parsed, matchedPaths: [], matchedValues: [], error: null, queryError: (e as Error).message }
return {
parsed,
matchedPaths: [],
matchedValues: [],
error: null,
queryError: (e as Error).message,
}
}
}, [jsonInput, query])
@@ -70,13 +83,16 @@ export default function JsonViewer() {
}
const objectMatches = result.matchedValues.filter(isPlainObject)
const isObjectTable = objectMatches.length > 0 && objectMatches.length === result.matchedValues.length
const isObjectTable =
objectMatches.length > 0 && objectMatches.length === result.matchedValues.length
const csv = isObjectTable
? (() => {
const columns = Array.from(new Set(objectMatches.flatMap((item) => Object.keys(item))))
const headerRow = columns.map(escapeCsvValue).join(",")
const valueRows = objectMatches.map((item) => columns.map((column) => escapeCsvValue(item[column])).join(","))
const valueRows = objectMatches.map((item) =>
columns.map((column) => escapeCsvValue(item[column])).join(",")
)
return [headerRow, ...valueRows].join("\n")
})()
: (() => {
@@ -94,7 +110,8 @@ export default function JsonViewer() {
const copySelectedRawJson = useCallback(() => {
if (result.matchedValues.length === 0) return
const payload = result.matchedValues.length === 1 ? result.matchedValues[0] : result.matchedValues
const payload =
result.matchedValues.length === 1 ? result.matchedValues[0] : result.matchedValues
const json = JSON.stringify(payload, null, 2)
navigator.clipboard.writeText(json).then(() => {
@@ -157,15 +174,13 @@ export default function JsonViewer() {
<button
onClick={copySelectedRawJson}
disabled={result.matchedValues.length === 0 || !!result.error}
className="text-xs font-medium px-3 py-1 bg-indigo-500 text-white rounded-lg hover:bg-indigo-600 disabled:bg-slate-300 disabled:cursor-not-allowed transition-colours"
>
className="text-xs font-medium px-3 py-1 bg-indigo-500 text-white rounded-lg hover:bg-indigo-600 disabled:bg-slate-300 disabled:cursor-not-allowed transition-colours">
{copiedRawJson ? t("jsonViewer.copied") : t("jsonViewer.copyRawJson")}
</button>
<button
onClick={copyAsCsv}
disabled={result.matchedValues.length === 0 || !!result.error}
className="text-xs font-medium px-3 py-1 bg-emerald-500 text-white rounded-lg hover:bg-emerald-600 disabled:bg-slate-300 disabled:cursor-not-allowed transition-colours"
>
className="text-xs font-medium px-3 py-1 bg-emerald-500 text-white rounded-lg hover:bg-emerald-600 disabled:bg-slate-300 disabled:cursor-not-allowed transition-colours">
{copiedCsv ? t("jsonViewer.copied") : t("jsonViewer.copyAsCsv")}
</button>
</div>
@@ -178,11 +193,7 @@ export default function JsonViewer() {
</div>
)}
{result.parsed && (
<JsonTreeNode
data={result.parsed}
path={["$"]}
matchedPaths={result.matchedPaths}
/>
<JsonTreeNode data={result.parsed} path={["$"]} matchedPaths={result.matchedPaths} />
)}
</div>
</div>