|
<template> |
|
<div ref="editorContainer" class="h-full"></div> |
|
</template> |
|
|
|
<script setup lang="ts"> |
|
import { ref, onMounted, watch, onBeforeUnmount } from 'vue'; |
|
import monaco from '@monaco-editor/loader'; |
|
|
|
const props = defineProps<{ |
|
value: string, |
|
originalValue?: string, |
|
language?: string, |
|
options?: Record<string, any>, |
|
}>(); |
|
const emit = defineEmits(['update:value']); |
|
|
|
const editorContainer = ref<HTMLElement>(); |
|
let editorInstance: any = null; |
|
let monacoInstance: any = null; |
|
let editorModel: any = null; |
|
|
|
|
|
const createEditor = () => { |
|
if (!editorContainer.value || !monacoInstance) return; |
|
|
|
|
|
if (editorInstance) { |
|
editorInstance.dispose(); |
|
editorInstance = null; |
|
} |
|
|
|
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); |
|
const commonOptions = { |
|
language: props.language || 'json', |
|
minimap: { enabled: !isMobile }, |
|
fontSize: isMobile ? 16 : 14, |
|
lineNumbers: isMobile ? 'off' as const : 'on' as const, |
|
scrollBeyondLastLine: false, |
|
automaticLayout: true, |
|
...props.options |
|
}; |
|
|
|
|
|
if (editorModel) { |
|
editorModel.dispose(); |
|
} |
|
editorModel = monacoInstance.editor.createModel(props.value, props.language); |
|
|
|
if (props.originalValue !== undefined) { |
|
|
|
const originalModel = monacoInstance.editor.createModel(props.originalValue, props.language); |
|
editorInstance = monacoInstance.editor.createDiffEditor(editorContainer.value, { |
|
...commonOptions, |
|
readOnly: false, |
|
renderSideBySide: true, |
|
ignoreTrimWhitespace: false, |
|
renderOverviewRuler: true, |
|
diffWordWrap: 'on', |
|
}); |
|
|
|
editorInstance.setModel({ |
|
original: originalModel, |
|
modified: editorModel |
|
}); |
|
const modifiedEditor = editorInstance.getModifiedEditor(); |
|
modifiedEditor.onDidChangeModelContent(() => { |
|
emit('update:value', modifiedEditor.getValue()); |
|
}); |
|
} else { |
|
|
|
editorInstance = monacoInstance.editor.create(editorContainer.value, { |
|
...commonOptions, |
|
model: editorModel |
|
}); |
|
editorInstance.onDidChangeModelContent(() => { |
|
emit('update:value', editorInstance.getValue()); |
|
}); |
|
} |
|
}; |
|
|
|
onMounted(() => { |
|
monaco.init().then(instance => { |
|
monacoInstance = instance; |
|
createEditor(); |
|
}); |
|
}); |
|
|
|
|
|
watch([() => props.value, () => props.originalValue, () => props.language], ([newVal, newOriginalVal, newLanguage], [oldVal, oldOriginalVal]) => { |
|
|
|
const modeChanged = |
|
(oldOriginalVal === undefined && newOriginalVal !== undefined) || |
|
(oldOriginalVal !== undefined && newOriginalVal === undefined); |
|
|
|
if (modeChanged || oldVal === undefined) { |
|
|
|
createEditor(); |
|
} else if (props.language && editorModel && editorModel.getLanguageId() !== props.language) { |
|
|
|
monacoInstance.editor.setModelLanguage(editorModel, props.language); |
|
} else if (editorInstance) { |
|
if (props.originalValue !== undefined) { |
|
|
|
const model = editorInstance.getModel(); |
|
if (model && model.original && model.original.getValue() !== newOriginalVal) { |
|
model.original.setValue(newOriginalVal || ''); |
|
} |
|
if (model && model.modified && model.modified.getValue() !== newVal) { |
|
model.modified.setValue(newVal || ''); |
|
} |
|
} else { |
|
|
|
if (editorInstance.getValue() !== newVal) { |
|
editorInstance.setValue(newVal || ''); |
|
} |
|
} |
|
} |
|
}, { deep: true }); |
|
|
|
onBeforeUnmount(() => { |
|
if (editorInstance) { |
|
editorInstance.dispose(); |
|
} |
|
if (editorModel) { |
|
editorModel.dispose(); |
|
} |
|
editorInstance = null; |
|
editorModel = null; |
|
}); |
|
</script> |
|
|
|
<style scoped> |
|
:deep(.monaco-diff-editor), |
|
:deep(.monaco-editor) { |
|
height: 100%; |
|
} |
|
</style> |
|
|