Last active
March 4, 2026 16:32
-
-
Save hannoeru/c675b2f41c1089021fcaf1fed4f3c3aa to your computer and use it in GitHub Desktop.
Vue MonacoEditor Component
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { defineComponent, h, nextTick, onMounted, ref, watch, onUnmounted } from 'vue' | |
| import * as monaco from 'monaco-editor' | |
| // Emmet Plugin | |
| import { emmetHTML, emmetCSS } from 'emmet-monaco-es' | |
| // Import language | |
| // import 'monaco-editor/esm/vs/basic-languages/scss/scss.contribution.js' | |
| // Import language service workers | |
| // @ts-ignore | |
| import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker.js?worker' | |
| // @ts-ignore | |
| import CssWorker from 'monaco-editor/esm/vs/language/css/css.worker.js?worker' | |
| // @ts-ignore | |
| import HTMLWorker from 'monaco-editor/esm/vs/language/html/html.worker.js?worker' | |
| import { isDark } from '../composables/dark' | |
| const isClient = typeof window !== 'undefined' | |
| if (isClient) { | |
| // @ts-ignore | |
| window.MonacoEnvironment = { | |
| // @ts-ignore | |
| getWorker(workerId, label) { | |
| if (label === 'css' || label === 'scss' || label === 'less') | |
| return new CssWorker() | |
| if (label === 'html' || label === 'handlebars' || label === 'razor') | |
| return new HTMLWorker() | |
| return new EditorWorker() | |
| }, | |
| } | |
| } | |
| export default defineComponent({ | |
| name: 'MonacoEditor', | |
| props: { | |
| modelValue: { | |
| type: String, | |
| required: true, | |
| }, | |
| theme: { | |
| type: String, | |
| default: isDark.value ? 'vs-dark' : 'vs', | |
| }, | |
| language: { | |
| type: String, | |
| default: 'html', | |
| }, | |
| // eslint-disable-next-line vue/require-default-prop | |
| options: Object, | |
| }, | |
| emits: ['update:modelValue', 'editorDidMount'], | |
| setup(props, { emit }) { | |
| const root = ref<HTMLElement | null>(null) | |
| let editor: monaco.editor.IStandaloneCodeEditor | null = null | |
| // let monacoEditor: typeof import('monaco-editor') | |
| // auto adjust content height | |
| const resizeObserver = new ResizeObserver((entries) => { | |
| editor?.layout() | |
| }) | |
| const options = ref<monaco.editor.IStandaloneEditorConstructionOptions>(Object.assign( | |
| { | |
| value: props.modelValue, | |
| theme: props.theme, | |
| language: props.language, | |
| }, | |
| props.options, | |
| )) | |
| async function initEditor() { | |
| if (!isClient) return | |
| // monacoEditor = await import('monaco-editor') | |
| editor = monaco.editor.create(root.value!, options.value) | |
| editor.onDidChangeModelContent(() => { | |
| const value = editor!.getValue() | |
| if (props.modelValue !== value) | |
| emit('update:modelValue', value) | |
| emit('editorDidMount', editor) | |
| }) | |
| // Add emmet to editor | |
| const lang = options.value.language | |
| if (lang === 'html') | |
| emmetHTML(monaco) | |
| if (lang === 'css' || lang === 'scss' || lang === 'less') | |
| emmetCSS(monaco) | |
| } | |
| onMounted(async() => { | |
| await nextTick(async() => { | |
| await initEditor() | |
| }) | |
| resizeObserver.observe(root.value!) | |
| }) | |
| onUnmounted(() => { | |
| editor?.dispose() | |
| }) | |
| watch(isDark, (isDark) => { | |
| monaco.editor.setTheme(isDark ? 'vs-dark' : 'vs') | |
| }) | |
| watch(options, (v) => { | |
| editor?.updateOptions(v) | |
| }) | |
| return () => h('div', { | |
| ref: root, | |
| }) | |
| }, | |
| }) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment