From fa004a639ede01e4fdef9bbc750f29b341658daa Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 27 Oct 2024 16:11:41 +0200 Subject: [PATCH] web/viewsource: add fancy JSON rendering --- web/src/ui/JSONView.css | 36 ++++++ web/src/ui/JSONView.tsx | 120 +++++++++++++++++++ web/src/ui/timeline/menu/ViewSourceModal.tsx | 5 +- web/src/ui/timeline/menu/index.css | 6 - 4 files changed, 158 insertions(+), 9 deletions(-) create mode 100644 web/src/ui/JSONView.css create mode 100644 web/src/ui/JSONView.tsx diff --git a/web/src/ui/JSONView.css b/web/src/ui/JSONView.css new file mode 100644 index 0000000..0cb4724 --- /dev/null +++ b/web/src/ui/JSONView.css @@ -0,0 +1,36 @@ +pre.json-view { + white-space: wrap; + overflow-wrap: anywhere; + margin: 0; + + ul, ol { + margin: 0; + + > li { + list-style-type: none; + } + } + + span.json-collapsed { + user-select: none; + } + + button { + padding: 0 .25rem; + } + + /* If the screen is wide enough, make line-wrapped strings aligned after the object key */ + @media screen and (min-width: 800px) { + li.json-object-entry:has(> span.json-comma-container > span.json-string) { + display: flex; + + span.json-object-key { + white-space: nowrap; + } + + span.json-object-entry-colon { + white-space: pre; + } + } + } +} diff --git a/web/src/ui/JSONView.tsx b/web/src/ui/JSONView.tsx new file mode 100644 index 0000000..8bd165a --- /dev/null +++ b/web/src/ui/JSONView.tsx @@ -0,0 +1,120 @@ +// gomuks - A Matrix client written in Go. +// Copyright (C) 2024 Tulir Asokan +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +import { useReducer } from "react" +import "./JSONView.css" + +interface JSONViewProps { + data: unknown +} + +interface JSONViewPropsWithKey extends JSONViewProps { + objectKey?: string + trailingComma?: boolean + noCollapse?: boolean +} + +function renderJSONString(data: string, styleClass: string = "s2") { + return {JSON.stringify(data)} +} + +function renderJSONValue(data: unknown, collapsed: boolean) { + switch (typeof data) { + case "object": + if (data === null) { + return null + } else if (Array.isArray(data)) { + if (data.length === 0) { + return null + } else if (collapsed) { + return + } + return
    + {data.map((item, i) => +
  1. )} +
+ } else { + const entries = Object.entries(data) + if (entries.length === 0) { + return null + } else if (collapsed) { + return + } + return + } + case "string": + return renderJSONString(data) + case "number": + return {data} + case "boolean": + return {data ? "true" : "false"} + default: + return undefined + } +} + +function JSONValueWithKey({ data, objectKey, trailingComma, noCollapse }: JSONViewPropsWithKey) { + const [collapsed, toggleCollapsed] = useReducer(collapsed => !collapsed, false) + const renderedKey = objectKey + ? + {renderJSONString(objectKey, "nt")} + : + + : null + const renderedSuffix = trailingComma + ? , + : null + const collapseButton = noCollapse ? null : + + if (Array.isArray(data)) { + return <> + {renderedKey} + {collapseButton} + [ + {renderJSONValue(data, collapsed)} + ] + {renderedSuffix} + + } else if (data !== null && typeof data === "object") { + return <> + {renderedKey} + {collapseButton} + {"{"} + {renderJSONValue(data, collapsed)} + {"}"} + {renderedSuffix} + + } + return <> + {renderedKey} + + {renderJSONValue(data, collapsed)} + {renderedSuffix} + + +} + +export default function JSONView({ data }: JSONViewProps) { + return
+		
+	
+} diff --git a/web/src/ui/timeline/menu/ViewSourceModal.tsx b/web/src/ui/timeline/menu/ViewSourceModal.tsx index fd20fbc..69da420 100644 --- a/web/src/ui/timeline/menu/ViewSourceModal.tsx +++ b/web/src/ui/timeline/menu/ViewSourceModal.tsx @@ -14,6 +14,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . import { MemDBEvent } from "@/api/types" +import JSONView from "../../JSONView.tsx" interface ViewSourceModalProps { evt: MemDBEvent @@ -21,9 +22,7 @@ interface ViewSourceModalProps { const ViewSourceModal = ({ evt }: ViewSourceModalProps) => { return
-
-			{JSON.stringify(evt, null, "  ")}
-		
+
} diff --git a/web/src/ui/timeline/menu/index.css b/web/src/ui/timeline/menu/index.css index 82ff616..ce727f3 100644 --- a/web/src/ui/timeline/menu/index.css +++ b/web/src/ui/timeline/menu/index.css @@ -56,12 +56,6 @@ div.view-source-modal { background-color: white; border-radius: 1rem; padding: 1rem; - - > pre { - margin: 0; - white-space: pre-wrap; - overflow-wrap: anywhere; - } } div.confirm-message-modal {