Nuxt3でcodemirrorに絵文字ピッカーを追加する

code on a screen NuxtJS
Photo by Markus Spiske on Pexels.com
この記事は約6分で読めます。

前回は以下の記事でNuxt3にマークダウンエディタのeasymdeを導入してみました。

Nuxt3でマークダウンエディタを使う
最近は個人的にnuxt3をよく触っています。練習としてちょっとしたアプリを作っているのですが、何らかの文書をマークダウンで扱えると文書構造を表現できて便利です。そこで、今回はnuxt3でマークダウンエディタを扱ってみます。nuxt3(vue...

マークダウンで文字入力できるようになったら絵文字も入力したくなります。今回はマークダウンエディタに絵文字ピッカーを組み込んでみます。

環境

  • Node.js v16.18.1
  • Nuxt 3.2.3
  • EasyMDE 2.18.0
  • vue3-emoji-picker 1.1.7

インストール

vue3-emoji-pickerをインストールします。

$ npx nuxi init md-sample
$ cd md-sample/
$ yarn
$ yarn add easymde vue3-emoji-picker

絵文字ピッカーを追加したイメージ

easymdeではツールバーに任意のボタンを追加することができます。ツールバーに顔文字のボタンがあるとわかりやすそうです。最終的な形は以下のようになります。顔文字アイコンを押すと絵文字ピッカーを表示するようにしてみます。

 

codemirrorの導入

前回の記事のとおりeasymdeを導入します。

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
  css: ["easymde/dist/easymde.min.css"],
});
<script lang="ts" setup>
import EasyMDE from "easymde";
let mde: InstanceType<typeof EasyMDE> | null = null;
const content = ref("");
const contentArea = ref();

onMounted(async () => {
  const EasyMDE = (await import("easymde")).default;
  mde = new EasyMDE({
    element: contentArea.value!.$el,
  });
  mde.codemirror.on("change", () => {
    if (!mde) return;
    content.value = mde.value();
  });
});
</script>
<template>
  <div>
    <textarea
      ref="contentArea"
      v-model="content"
      rows="5"
      placeholder="markdown形式で説明を記述できます。"
      maxlength="300"
    />
  </div>
</template>

これで以下のようなビューが表示されます。適当に文字を入力しています。スタイルも適用されてそうです。

絵文字ピッカーの追加

続いてツールバーの一番右に顔文字アイコンを追加し、アイコンをクリックしたら絵文字ピッカーが画面右下に表示されるようにしてみます。

コンポーネント内で絵文字ピッカーコンポーネントを使えるようにプラグインを追加します。

import EmojiPicker from "vue3-emoji-picker";
import "vue3-emoji-picker/css";

export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.component("EmojiPicker", EmojiPicker);
});

vue3-emoji-pickerの型宣言が見つからないエラーが出るため、以下のようにアンビエント宣言を追加します。

declare module "vue3-emoji-picker";

最終的なコードは以下のようになります。

<script lang="ts" setup>
import EasyMDE from "easymde";

let mde: InstanceType<typeof EasyMDE> | null = null;
const content = ref("");
const contentArea = ref();
const displayEmojiPicker = ref(false);

onMounted(async () => {
  const EasyMDE = (await import("easymde")).default;
  mde = new EasyMDE({
    toolbar: [
      "bold",
      "italic",
      "heading",
      "|",
      "quote",
      "|",
      "preview",
      "side-by-side",
      "fullscreen",
      {
        name: "Emoji",
        title: "Emoji",
        className: "fa fa-smile-o",
        action: (mde: EasyMDE) => {
          displayEmojiPicker.value = !displayEmojiPicker.value;
        },
      },
    ],
    element: contentArea.value!.$el,
  });

  mde.codemirror.on("change", () => {
    if (!mde) return;
    content.value = mde.value();
  });
});

function onSelectEmoji(emoji: any) {
  if (!mde) {
    return;
  }

  // カーソル位置に絵文字追加
  // See https://codemirror.net/5/doc/manual.html
  const { line, ch } = mde.codemirror.getCursor();
  mde.codemirror.replaceRange(emoji.i, { line, ch }, { line, ch });
}
</script>

<template>
  <div>
    <textarea
      ref="contentArea"
      v-model="content"
      rows="5"
      placeholder="markdown形式で説明を記述できます。"
      maxlength="300"
    />

    <EmojiPicker
      :native="true"
      class="emoji-picker"
      v-if="displayEmojiPicker"
      @select="onSelectEmoji"
    />
  </div>
</template>

<style scoped>
.emoji-picker {
  position: absolute;
  bottom: 20px;
  right: 20px;
}
</style>

以下のような動きになります。

参考リンク

コメント

タイトルとURLをコピーしました