Line data Source code
1 : // @ts-check 2 : 3 : import { Compartment, EditorState } from "@codemirror/state"; 4 : import { EditorView } from "@codemirror/view"; 5 : import { basicSetup } from "./CodeMirrorMixinInit"; 6 : 7 : import { mixinMaker } from "../utils"; 8 : 9 : /** 10 : * @typedef {{ cm: EditorView, extensions: any }} Opts 11 : * @typedef {{ 12 : * extensions?: import("@codemirror/state").Extension[] 13 : * }} CodeMirrorMixinOptions 14 : * @typedef {import("@codemirror/state").Transaction} CodeMirrorStateTransaction 15 : * 16 : * @typedef {import("vue").ComponentOptionsMixin} ComponentOptionsMixin 17 : * @typedef {{ value: string }} MixinProps 18 : * @typedef {{ editValue: string }} MixinData 19 : * @typedef {{ 20 : * _changeFilter(tr: CodeMirrorStateTransaction): boolean, 21 : * setContent(data: string): void 22 : * }} MixinMethods 23 : * @typedef { Vue & MixinMethods & MixinData } MixinInstance 24 : * @typedef { MixinInstance & { $options: Opts } } MixinInstanceWithOptions 25 : */ 26 : 27 1 : const theme = EditorView.theme({ 28 : "&": { 29 : fontSize: "0.875rem" 30 : } 31 : }); 32 1 : export const ThemeCompartment = new Compartment(); 33 : 34 : const CodeMirrorMixin = 35 2 : (/** @type {CodeMirrorMixinOptions} */options) => mixinMaker({ 36 : name: "CodeMirrorMixin", 37 : model: { prop: "value", event: "edit" }, 38 : props: { 39 : value: { type: String, default: "" } 40 : }, 41 : /** @return {MixinData} */ 42 : data() { 43 8 : return { editValue: "" }; 44 : }, 45 : /** @this {MixinInstanceWithOptions} */ 46 : beforeDestroy() { 47 8 : this.$options.cm.destroy(); 48 : }, 49 : /** @this {MixinInstanceWithOptions & Readonly<MixinProps>} */ 50 : mounted() { 51 8 : const ext = [ basicSetup, EditorState.changeFilter.of(this._changeFilter), 52 : ThemeCompartment.of(theme) ]; 53 8 : if (options?.extensions) { 54 8 : ext.push(options.extensions); 55 : } 56 : 57 8 : this.$options.cm = new EditorView({ 58 : extensions: ext, 59 : parent: (/** @type {Element} */(this.$refs?.cm) ?? this.$el), 60 : doc: this.value 61 : }); 62 : }, 63 : methods: { 64 : /** 65 : * @this {MixinInstance} 66 : * @param {CodeMirrorStateTransaction} tr 67 : * @returns {boolean} 68 : */ 69 : _changeFilter(tr) { 70 29 : this.editValue = tr.state.doc.toString(); 71 29 : this.$emit("edit", this.editValue); 72 29 : return true; 73 : }, 74 : /** 75 : * @this {MixinInstance} 76 : * @returns {EditorView} 77 : */ 78 : getEditor() { 79 9 : return /** @type {MixinInstanceWithOptions} */(this).$options.cm; 80 : }, 81 : /** 82 : * @this {MixinInstance} 83 : * @param {string} data 84 : */ 85 : setContent(data) { 86 16 : const cm = /** @type {MixinInstanceWithOptions} */(this).$options.cm; 87 16 : if (!cm) { return; } 88 16 : cm.dispatch(cm.state.update({ 89 : changes: { 90 : from: 0, to: cm.state.doc.length, insert: data 91 : } 92 : })); 93 : } 94 : } 95 : }); 96 : export default CodeMirrorMixin;