49 lines
2.1 KiB
TypeScript
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' }} />;
|
|
}
|