Files
mycode/jsonhero-web/app/components/JsonEditor.tsx
2026-02-04 12:18:35 +08:00

92 lines
2.4 KiB
TypeScript

import { CodeEditor } from "./CodeEditor";
import { useJson } from "~/hooks/useJson";
import { useCallback, useMemo, useRef } from "react";
import {
useJsonColumnViewAPI,
useJsonColumnViewState,
} from "~/hooks/useJsonColumnView";
import { ViewUpdate } from "@uiw/react-codemirror";
import jsonMap from "json-source-map";
import { JSONHeroPath } from "@jsonhero/path";
import {usePreferences} from '~/components/PreferencesProvider'
export function JsonEditor() {
const [json] = useJson();
const { selectedNodeId } = useJsonColumnViewState();
const { goToNodeId } = useJsonColumnViewAPI();
const [preferences] = usePreferences();
const jsonMapped = useMemo(() => {
return jsonMap.stringify(json, null, preferences?.indent || 2);
}, [json, preferences]);
const selection = useMemo<{ start: number; end: number } | undefined>(() => {
if (!selectedNodeId) {
return;
}
const path = new JSONHeroPath(selectedNodeId);
const pointer = path.jsonPointer();
const location = jsonMapped.pointers[pointer];
if (location) {
if (location.key) {
return { start: location.key.pos, end: location.valueEnd.pos };
}
return { start: location.value.pos, end: location.valueEnd.pos };
}
}, [selectedNodeId, jsonMapped]);
const currentSelectedLine = useRef<number | undefined>(undefined);
const onUpdate = useCallback(
(update: ViewUpdate) => {
if (!update.selectionSet) {
return;
}
const range = update.state.selection.ranges[0];
const line = update.state.doc.lineAt(range.anchor);
if (
currentSelectedLine.current &&
currentSelectedLine.current === line.number
) {
return;
}
currentSelectedLine.current = line.number;
// Find the key if the selected line using jsonMapped.pointers
const pointerEntry = Object.entries(jsonMapped.pointers).find(
([pointer, info]) => {
return info.value.line === line.number - 1;
}
);
if (!pointerEntry) {
return;
}
const [pointer] = pointerEntry;
const path = JSONHeroPath.fromPointer(pointer);
goToNodeId(path.toString(), "editor");
},
[goToNodeId]
);
return (
<CodeEditor
language="json"
content={jsonMapped.json}
readOnly={true}
onUpdate={onUpdate}
selection={selection}
/>
);
}