DEV Community

medilies
medilies

Posted on

2 1

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

Image of Docusign

Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Immerse yourself in a wealth of knowledge with this piece, supported by the inclusive DEV Community—every developer, no matter where they are in their journey, is invited to contribute to our collective wisdom.

A simple “thank you” goes a long way—express your gratitude below in the comments!

Gathering insights enriches our journey on DEV and fortifies our community ties. Did you find this article valuable? Taking a moment to thank the author can have a significant impact.

Okay