feat: add functionality to copy selected raw JSON in JsonViewer and update related state management

This commit is contained in:
2026-02-24 10:58:54 +08:00
parent 6953c7a595
commit a195299e91
3 changed files with 26 additions and 4 deletions
@@ -73,6 +73,7 @@
"visualisedResult": "Visualised Result", "visualisedResult": "Visualised Result",
"matches": "matches", "matches": "matches",
"copied": "Copied!", "copied": "Copied!",
"copyRawJson": "Copy selected JSON",
"copyAsCsv": "Copy as CSV", "copyAsCsv": "Copy as CSV",
"error": "Error:" "error": "Error:"
}, },
@@ -73,6 +73,7 @@
"visualisedResult": "可视化结果", "visualisedResult": "可视化结果",
"matches": "个匹配", "matches": "个匹配",
"copied": "已复制!", "copied": "已复制!",
"copyRawJson": "复制选中 JSON",
"copyAsCsv": "复制为 CSV", "copyAsCsv": "复制为 CSV",
"error": "错误:" "error": "错误:"
}, },
+24 -4
View File
@@ -26,7 +26,8 @@ export default function JsonViewer() {
const [jsonInput, setJsonInput] = useState<string>(JSON.stringify(initialData, null, 2)) const [jsonInput, setJsonInput] = useState<string>(JSON.stringify(initialData, null, 2))
const [query, setQuery] = useState<string>("$.staff_members[*].name") const [query, setQuery] = useState<string>("$.staff_members[*].name")
const [copied, setCopied] = useState(false) const [copiedCsv, setCopiedCsv] = useState(false)
const [copiedRawJson, setCopiedRawJson] = useState(false)
const isPlainObject = (value: unknown): value is Record<string, unknown> => { const isPlainObject = (value: unknown): value is Record<string, unknown> => {
return value !== null && typeof value === "object" && !Array.isArray(value) return value !== null && typeof value === "object" && !Array.isArray(value)
@@ -85,11 +86,23 @@ export default function JsonViewer() {
})() })()
navigator.clipboard.writeText(csv).then(() => { navigator.clipboard.writeText(csv).then(() => {
setCopied(true) setCopiedCsv(true)
setTimeout(() => setCopied(false), 2000) setTimeout(() => setCopiedCsv(false), 2000)
}) })
}, [query, result.matchedValues]) }, [query, result.matchedValues])
const copySelectedRawJson = useCallback(() => {
if (result.matchedValues.length === 0) return
const payload = result.matchedValues.length === 1 ? result.matchedValues[0] : result.matchedValues
const json = JSON.stringify(payload, null, 2)
navigator.clipboard.writeText(json).then(() => {
setCopiedRawJson(true)
setTimeout(() => setCopiedRawJson(false), 2000)
})
}, [result.matchedValues])
return ( return (
<div className="h-full flex gap-4 overflow-hidden"> <div className="h-full flex gap-4 overflow-hidden">
<Seo <Seo
@@ -141,12 +154,19 @@ export default function JsonViewer() {
<span className="text-xs font-medium px-2 py-0.5 bg-indigo-100 text-indigo-700 rounded-full"> <span className="text-xs font-medium px-2 py-0.5 bg-indigo-100 text-indigo-700 rounded-full">
{result.matchedPaths.length} {t("jsonViewer.matches")} {result.matchedPaths.length} {t("jsonViewer.matches")}
</span> </span>
<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"
>
{copiedRawJson ? t("jsonViewer.copied") : t("jsonViewer.copyRawJson")}
</button>
<button <button
onClick={copyAsCsv} onClick={copyAsCsv}
disabled={result.matchedValues.length === 0 || !!result.error} 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"
> >
{copied ? t("jsonViewer.copied") : t("jsonViewer.copyAsCsv")} {copiedCsv ? t("jsonViewer.copied") : t("jsonViewer.copyAsCsv")}
</button> </button>
</div> </div>
</div> </div>