feat: JsonEditor for inputting JSON content (#4)
* feat(editor): add JsonEditor component for JSON configuration editing * fix(provider): update API Key visibility logic in ProviderForm component * fix(editor): stabilize JsonEditor height and restore API Key logic - Revert API Key visibility to preset-based rule and injection to non-custom preset only. - Reduce JsonEditor min-height from rows*22px to rows*18px to lessen layout jitter when switching presets. - Keep fonts/size consistent with the previous textarea for visual parity. * - fix(form): remove websiteUrl auto-extraction from JSON to prevent incorrect overrides --------- Co-authored-by: Jason <farion1231@gmail.com>
This commit is contained in:
97
src/components/JsonEditor.tsx
Normal file
97
src/components/JsonEditor.tsx
Normal file
@@ -0,0 +1,97 @@
|
||||
import React, { useRef, useEffect } from "react";
|
||||
import { EditorView, basicSetup } from "codemirror";
|
||||
import { json } from "@codemirror/lang-json";
|
||||
import { oneDark } from "@codemirror/theme-one-dark";
|
||||
import { EditorState } from "@codemirror/state";
|
||||
import { placeholder } from "@codemirror/view";
|
||||
|
||||
interface JsonEditorProps {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
placeholder?: string;
|
||||
darkMode?: boolean;
|
||||
rows?: number;
|
||||
}
|
||||
|
||||
const JsonEditor: React.FC<JsonEditorProps> = ({
|
||||
value,
|
||||
onChange,
|
||||
placeholder: placeholderText = "",
|
||||
darkMode = false,
|
||||
rows = 12,
|
||||
}) => {
|
||||
const editorRef = useRef<HTMLDivElement>(null);
|
||||
const viewRef = useRef<EditorView | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!editorRef.current) return;
|
||||
|
||||
// 创建编辑器扩展
|
||||
const minHeightPx = Math.max(1, rows) * 18; // 降低最小高度以减少抖动
|
||||
const sizingTheme = EditorView.theme({
|
||||
"&": { minHeight: `${minHeightPx}px` },
|
||||
".cm-scroller": { overflow: "auto" },
|
||||
".cm-content": {
|
||||
fontFamily:
|
||||
"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace",
|
||||
fontSize: "14px",
|
||||
},
|
||||
});
|
||||
|
||||
const extensions = [
|
||||
basicSetup,
|
||||
json(),
|
||||
placeholder(placeholderText || ""),
|
||||
sizingTheme,
|
||||
EditorView.updateListener.of((update) => {
|
||||
if (update.docChanged) {
|
||||
const newValue = update.state.doc.toString();
|
||||
onChange(newValue);
|
||||
}
|
||||
}),
|
||||
];
|
||||
|
||||
// 如果启用深色模式,添加深色主题
|
||||
if (darkMode) {
|
||||
extensions.push(oneDark);
|
||||
}
|
||||
|
||||
// 创建初始状态
|
||||
const state = EditorState.create({
|
||||
doc: value,
|
||||
extensions,
|
||||
});
|
||||
|
||||
// 创建编辑器视图
|
||||
const view = new EditorView({
|
||||
state,
|
||||
parent: editorRef.current,
|
||||
});
|
||||
|
||||
viewRef.current = view;
|
||||
|
||||
// 清理函数
|
||||
return () => {
|
||||
view.destroy();
|
||||
viewRef.current = null;
|
||||
};
|
||||
}, [darkMode, rows]); // 依赖项中不包含 onChange 和 placeholder,避免不必要的重建
|
||||
|
||||
// 当 value 从外部改变时更新编辑器内容
|
||||
useEffect(() => {
|
||||
if (viewRef.current && viewRef.current.state.doc.toString() !== value) {
|
||||
const transaction = viewRef.current.state.update({
|
||||
changes: {
|
||||
from: 0,
|
||||
to: viewRef.current.state.doc.length,
|
||||
insert: value,
|
||||
},
|
||||
});
|
||||
viewRef.current.dispatch(transaction);
|
||||
}
|
||||
}, [value]);
|
||||
|
||||
return <div ref={editorRef} style={{ width: "100%" }} />;
|
||||
};
|
||||
|
||||
export default JsonEditor;
|
||||
Reference in New Issue
Block a user