DEV Community

medilies
medilies

Posted on

CodeMirror v6 on Vue3 hooked to Pinia store

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


Enter fullscreen mode Exit fullscreen mode

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,
    };
});


Enter fullscreen mode Exit fullscreen mode

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>


Enter fullscreen mode Exit fullscreen mode

This component defines two simple properties:

  1. additionalExtensions mainly to add language support.
  2. 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>

Enter fullscreen mode Exit fullscreen mode


<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>

Enter fullscreen mode Exit fullscreen mode



  1. 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
&lt;style&gt;</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">&lt;/style&gt;
</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>

Enter fullscreen mode Exit fullscreen mode




Results

screenshot

Top comments (0)