Editor Component
The Editor
component is responsible for rendering the Monaco Editor, which provides a rich text editing experience for Markdown.
Props
value: string
: The current Markdown content to be displayed in the editor.onChange: (value: string) => void
: A callback function that is called whenever the content of the editor changes.minimapEnabled: boolean
: A boolean to control the visibility of the minimap.wordWrapEnabled?: boolean
: An optional boolean to control word wrapping. It defaults totrue
.
State
theme
: Manages the theme of the Monaco Editor, switching between'vs-dark'
and'vs-light'
based on the application's theme.
Key Features
Theme Synchronization
The editor's theme is automatically synchronized with the application's theme (light/dark mode). This is achieved using a MutationObserver
that watches for changes to the class
attribute of the <html>
element. When the dark
class is added or removed, the editor's theme is updated accordingly.
Editor Options
The component exposes props to control editor features like the minimap and word wrapping. These options are dynamically updated using a useEffect
hook that listens for changes to the minimapEnabled
and wordWrapEnabled
props.
Code Breakdown
import { useRef, useEffect, useState } from 'react';
import MonacoEditor, { type OnMount } from '@monaco-editor/react';
import type { editor } from 'monaco-editor';
interface EditorProps {
value: string;
onChange: (value: string) => void;
minimapEnabled: boolean;
wordWrapEnabled?: boolean;
}
const Editor: React.FC<EditorProps> = ({
value,
onChange,
minimapEnabled,
wordWrapEnabled = true
}) => {
const editorRef = useRef<editor.IStandaloneCodeEditor | null>(null);
const [theme, setTheme] = useState<'vs-dark' | 'vs-light'>('vs-dark');
const handleEditorDidMount: OnMount = (editor) => {
editorRef.current = editor;
};
useEffect(() => {
if (editorRef.current) {
editorRef.current.updateOptions({
minimap: {
enabled: minimapEnabled,
},
wordWrap: wordWrapEnabled ? 'on' : 'off',
});
}
}, [minimapEnabled, wordWrapEnabled]);
useEffect(() => {
const updateTheme = () => {
const isDark = document.documentElement.classList.contains('dark');
setTheme(isDark ? 'vs-dark' : 'vs-light');
};
updateTheme();
const observer = new MutationObserver(() => updateTheme());
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
return () => observer.disconnect();
}, []);
return (
<div className="h-full w-full">
<MonacoEditor
height="100%"
defaultLanguage="markdown"
value={value}
onChange={(value) => onChange(value || '')}
onMount={handleEditorDidMount}
theme={theme}
options={{
wordWrap: wordWrapEnabled ? 'on' : 'off',
minimap: {
enabled: minimapEnabled,
},
// ... other options
}}
/>
</div>
);
};
export default Editor;
editorRef
: AuseRef
hook to hold a reference to the Monaco Editor instance. This allows direct interaction with the editor's API.handleEditorDidMount
: A callback function that runs when the editor is first mounted. It saves the editor instance toeditorRef
.useEffect
for Options: This effect runs wheneverminimapEnabled
orwordWrapEnabled
props change. It calls the editor'supdateOptions
method to apply the new settings.useEffect
for Theme: This effect sets up theMutationObserver
to listen for theme changes on the main document and updates the editor's theme state accordingly. It also cleans up the observer when the component unmounts.