Files
dsh-web/web/src/ProfileEditor.tsx
T

49 lines
2.1 KiB
TypeScript

/// <reference types="vite/client" />
import { useEffect, useRef } from 'react';
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
import 'monaco-editor/esm/vs/basic-languages/yaml/yaml.contribution';
import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
// Vite emits this worker locally; no AMD loader or CDN is involved.
self.MonacoEnvironment = { getWorker: () => new EditorWorker() };
export interface ProfileEditorProps {
value: string;
onChange?: (value: string) => void;
readOnly?: boolean;
label?: string;
}
export function ProfileEditor({ value, onChange, readOnly = false, label = 'YAML 配置' }: ProfileEditorProps) {
const host = useRef<HTMLDivElement>(null);
const editor = useRef<monaco.editor.IStandaloneCodeEditor | null>(null);
const change = useRef(onChange);
change.current = onChange;
const initialValue = useRef(value);
const updating = useRef(false);
useEffect(() => {
if (!host.current) return;
const model = monaco.editor.createModel(initialValue.current, 'yaml');
const instance = monaco.editor.create(host.current, {
model, automaticLayout: true, minimap: { enabled: false },
scrollBeyondLastLine: false, tabSize: 2, insertSpaces: true,
wordWrap: 'on', ariaLabel: label, readOnly,
});
editor.current = instance;
const listener = instance.onDidChangeModelContent(() => {
if (!updating.current) change.current?.(instance.getValue());
});
return () => { listener.dispose(); instance.dispose(); model.dispose(); editor.current = null; };
}, []);
useEffect(() => {
const instance = editor.current;
if (instance && instance.getValue() !== value) {
updating.current = true;
instance.setValue(value);
updating.current = false;
}
}, [value]);
useEffect(() => { editor.current?.updateOptions({ readOnly, ariaLabel: label }); }, [readOnly, label]);
return <div role="group" aria-label={label} ref={host} style={{ height: 420, minWidth: 0, border: '1px solid var(--border, #d5dbe5)', borderRadius: 8, overflow: 'hidden' }} />;
}