import React, { useEffect, useRef } from 'react'; import { closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete'; import { defaultKeymap, history, historyKeymap } from '@codemirror/commands'; import { json } from '@codemirror/lang-json'; import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from '@codemirror/language'; import { EditorState } from '@codemirror/state'; import { oneDark } from '@codemirror/theme-one-dark'; import { EditorView, keymap, lineNumbers } from '@codemirror/view'; interface MembersJsonEditorProps { value: string; onChange: (json: string) => void; error: string | null; } export const MembersJsonEditor = ({ value, onChange, error, }: MembersJsonEditorProps): React.JSX.Element => { const containerRef = useRef(null); const viewRef = useRef(null); const onChangeRef = useRef(onChange); onChangeRef.current = onChange; useEffect(() => { if (!containerRef.current) return; const state = EditorState.create({ doc: value, extensions: [ json(), oneDark, lineNumbers(), history(), bracketMatching(), closeBrackets(), syntaxHighlighting(defaultHighlightStyle, { fallback: true }), keymap.of([...defaultKeymap, ...historyKeymap, ...closeBracketsKeymap]), EditorView.updateListener.of((update) => { if (update.docChanged) { onChangeRef.current(update.state.doc.toString()); } }), EditorView.theme({ '&': { fontSize: '12px', maxHeight: '300px', }, '.cm-scroller': { overflow: 'auto', }, '.cm-content': { fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace', }, }), ], }); const view = new EditorView({ state, parent: containerRef.current, }); viewRef.current = view; return () => { view.destroy(); viewRef.current = null; }; // eslint-disable-next-line react-hooks/exhaustive-deps -- EditorView created once on mount }, []); useEffect(() => { const view = viewRef.current; if (!view) return; const currentDoc = view.state.doc.toString(); if (currentDoc !== value) { view.dispatch({ changes: { from: 0, to: currentDoc.length, insert: value }, }); } }, [value]); return (
{error ?

{error}

: null}
); };