mirror of
https://github.com/halo-dev/halo.git
synced 2025-12-17 16:34:02 +08:00
141 lines
3.0 KiB
Vue
141 lines
3.0 KiB
Vue
<script lang="ts" setup>
|
|
import type { LanguageSupport } from "@codemirror/language";
|
|
import { Compartment, EditorState } from "@codemirror/state";
|
|
import { EditorView } from "@codemirror/view";
|
|
import { basicSetup } from "codemirror";
|
|
import { onBeforeUnmount, onMounted, shallowRef, watch } from "vue";
|
|
import { presetLanguages, type CodemirrorProps } from "./supports";
|
|
|
|
const props = withDefaults(defineProps<CodemirrorProps>(), {
|
|
modelValue: "",
|
|
height: "auto",
|
|
language: "yaml",
|
|
extensions: () => [],
|
|
});
|
|
|
|
const emit = defineEmits<{
|
|
(e: "update:modelValue", value: string): void;
|
|
(e: "change", value: string): void;
|
|
}>();
|
|
|
|
const wrapper = shallowRef<HTMLDivElement>();
|
|
const cmState = shallowRef<EditorState>();
|
|
const cmView = shallowRef<EditorView>();
|
|
|
|
const themeCompartment = new Compartment();
|
|
const languageCompartment = new Compartment();
|
|
|
|
const createCustomTheme = (height: string) => {
|
|
return EditorView.theme({
|
|
"&": {
|
|
height,
|
|
width: "100%",
|
|
},
|
|
});
|
|
};
|
|
|
|
const createCmEditor = () => {
|
|
let extensions = [
|
|
basicSetup,
|
|
EditorView.lineWrapping,
|
|
themeCompartment.of(createCustomTheme(props.height)),
|
|
languageCompartment.of([]),
|
|
EditorView.updateListener.of((viewUpdate) => {
|
|
if (viewUpdate.docChanged) {
|
|
const doc = viewUpdate.state.doc.toString();
|
|
emit("update:modelValue", doc);
|
|
emit("change", doc);
|
|
}
|
|
}),
|
|
];
|
|
|
|
if (props.extensions) {
|
|
extensions = extensions.concat(props.extensions);
|
|
}
|
|
|
|
cmState.value = EditorState.create({
|
|
doc: props.modelValue,
|
|
extensions,
|
|
});
|
|
|
|
cmView.value = new EditorView({
|
|
state: cmState.value,
|
|
parent: wrapper.value,
|
|
});
|
|
|
|
loadLanguage();
|
|
};
|
|
|
|
async function loadLanguage() {
|
|
let language: LanguageSupport;
|
|
|
|
if (!props.language) {
|
|
return;
|
|
}
|
|
|
|
if (typeof props.language === "string") {
|
|
const loader = presetLanguages[props.language];
|
|
if (!loader) {
|
|
throw new Error(`Language ${props.language} not found`);
|
|
}
|
|
language = await loader();
|
|
} else {
|
|
language = props.language;
|
|
}
|
|
|
|
cmView.value?.dispatch({
|
|
effects: languageCompartment.reconfigure(language),
|
|
});
|
|
}
|
|
|
|
onMounted(() => {
|
|
createCmEditor();
|
|
});
|
|
|
|
// Update the codemirror editor doc when the model value changes.
|
|
watch(
|
|
() => props.modelValue,
|
|
(newValue) => {
|
|
if (!cmView.value) {
|
|
return;
|
|
}
|
|
|
|
if (newValue !== cmView.value.state.doc.toString()) {
|
|
cmView.value.dispatch({
|
|
changes: {
|
|
from: 0,
|
|
to: cmView.value.state.doc.length,
|
|
insert: newValue,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
);
|
|
|
|
watch(
|
|
() => props.height,
|
|
(newHeight) => {
|
|
if (cmView.value) {
|
|
cmView.value.dispatch({
|
|
effects: themeCompartment.reconfigure(createCustomTheme(newHeight)),
|
|
});
|
|
}
|
|
}
|
|
);
|
|
|
|
watch(
|
|
() => props.language,
|
|
() => {
|
|
loadLanguage();
|
|
}
|
|
);
|
|
|
|
// Destroy codemirror editor when component unmounts
|
|
onBeforeUnmount(() => {
|
|
cmView.value?.destroy();
|
|
});
|
|
</script>
|
|
<template>
|
|
<div ref="wrapper" class="codemirror-wrapper contents"></div>
|
|
</template>
|