CodeMirror v6 on Vue3 hooked to Pinia store
medilies
Posted on July 30, 2023
Scope
I'm gonna make a very basic Codepen clone for writing HTML and CSS, and integrate CodeMirror in Vue using vue-codemirror
wrapper.
Setup
After setting up Vue with Pinia, install the following packages:
npm i vue-codemirror @codemirror/lang-css @codemirror/lang-html @codemirror/theme-one-dark
Code
1. The store
import { defineStore } from "pinia";
import { computed, ref } from "vue";
export const useAppStore = defineStore("app", () => {
const css = ref("");
const cssCopy = computed(() => css.value);
const setCss = (newCss) => {
css.value = newCss;
};
const template = ref("");
const templateCopy = computed(() => template.value);
const setTemplate = (newTemplate) => {
template.value = newTemplate;
};
return {
css,
cssCopy,
setCss,
template,
templateCopy,
setTemplate,
};
});
This is the store I made to keep track of the value of HTML and CSS code.
2. Base text editor component
<template>
<codemirror
:modelValue="props.code"
placeholder="code goes here..."
:style="{ height: '100%' }"
:autofocus="true"
:indent-with-tab="true"
:tab-size="4"
:extensions="extensions"
@ready="handleReady"
/>
</template>
<script setup>
import { shallowRef, computed } from "vue";
import { Codemirror } from "vue-codemirror";
import { oneDark } from "@codemirror/theme-one-dark";
const props = defineProps({
additionalExtensions: {
type: Array,
required: true,
default: [],
},
code: {
type: String,
required: true,
},
});
const baseExtensions = [oneDark];
const extensions = computed(() => [
...baseExtensions,
...props.additionalExtensions,
]);
const view = shallowRef();
const handleReady = (payload) => {
view.value = payload.view;
};
</script>
This component defines two simple properties:
-
additionalExtensions
mainly to add language support. -
code
to access value from store.
3. Language specific text editors
<template>
<base-editor
:additional-extensions="[html()]"
:code="store.templateCopy"
@change="store.setTemplate($event)"
/>
</template>
<script setup>
import { html } from "@codemirror/lang-html";
import { useAppStore } from "@/stores/app";
import BaseEditor from "@/components/BaseEditor.vue";
const store = useAppStore();
store.setTemplate("<h1>template</h1>");
</script>
<template>
<base-editor
:additionalExtensions="[css()]"
:code="store.cssCopy"
@change="store.setCss($event)"
/>
</template>
<script setup>
import { css } from "@codemirror/lang-css";
import { useAppStore } from "@/stores/app";
import BaseEditor from "@/components/BaseEditor.vue";
const store = useAppStore();
store.setCss(* {
padding = 0;
margin = 0;
}
);
</script>
- Putting It All Together
<template>
<div class="grid h-screen grid-cols-8 overflow-hidden bg-slate-800">
<div class="h-screen col-span-4 overflow-hidden border border-gray-300">
<div v-html="compiledCode"></div>
</div>
<div
class="flex flex-col col-span-4 overflow-hidden border border-gray-300 grow-0 shrink basis-full"
>
<div
class="overflow-hidden border border-gray-300 grow-0 shrink basis-1/2"
>
<div class="h-full overflow-auto">
<HtmlEditor />
</div>
</div>
<div
class="overflow-hidden border border-gray-300 grow-0 shrink basis-1/2"
>
<div class="h-full overflow-auto">
<CssEditor />
</div>
</div>
</div>
</div>
</template>
<script setup>
import { computed } from "vue";
import HtmlEditor from "@/components/HtmlEditor.vue";
import CssEditor from "@/components/CssEditor.vue";
import { useAppStore } from "@/stores/app";
const store = useAppStore();
const compiledCode = computed(() => {
return
<style></span><span class="p">${</span><span class="nx">store</span><span class="p">.</span><span class="nx">css</span><span class="p">}</span><span class="s2"></style>
</span><span class="p">${</span><span class="nx">store</span><span class="p">.</span><span class="nx">template</span><span class="p">}</span><span class="s2">
;
});
</script>
Results
Posted on July 30, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.