9
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Nuxt.js で Slack のようなリアクション機能を実装する

Last updated at Posted at 2020-06-19

Prologue

Slack のようなリアクション機能をNuxt.js(Vue.js) を使って実装してみました。
CSSの実装やカウント、リアクションをつけたユーザーの表示等に関しては割愛して、今回は Picker と絵文字の表示部分についてのみ説明します。
公式のドキュメントがかなりしっかり作られているので、そちらも確認してみてください。

参考:

環境

  • macOS: 10.15.4

  • terminal: iTerm

  • node.js: 10.16.0

  • エディタ: VS Code

  • 使用言語: TypeScript, Nuxt.js, CompositionAPI(v0.5.0)

  • Project概要

    • パッケージマネージャ: yarn

インストール

emoji-mart-vue をインストールします。

yarn add emoji-mart-vue

セットアップ

挙動を見たかった為、 Nuxt のプロジェクトを新しく作成し、必要最低限の code を書いていきます。

<template>
  <div class="section">
    <div>
      <Picker />
    </div>
  </div>
</template>
<script lang="ts">
import { defineComponent, ref, reactive } from '@vue/composition-api'
// @ts-ignore
import { Picker } from 'emoji-mart-vue'

export default defineComponent({
  name: 'EmojiPicker',
  components: {
    Picker,
  },
})
</script>

最低限これだけでも emoji-picker は表示されました。

プロパティをセット

emoji-mart-vue は様々なプロパティを用意してくれているので、必要なものを確認していきます。
<Picker /> component にプロパティを渡していきます。

参考: https://github.com/jm-david/emoji-mart-vue

<template>
  <div class="section">
    <div>
      <Picker
        title="pick emoji"
        emoji="grinning"
        set="twitter"
        color="pink"
      />
    </div>
  </div>
</template>
<script lang="ts">
import { defineComponent, ref, reactive } from '@vue/composition-api'
// @ts-ignore
import { Emoji, Picker } from 'emoji-mart-vue'

export default defineComponent({
  name: 'EmojiPicker',
  components: {
    Picker
  },
})
</script>

内容:

  • title: フッターに表示されるテキスト
  • emoji: フッターに表示される初期値の絵文字
  • set: pickerに使われる絵文字のデザイン
  • color: ヘッダーに使われるカテゴリ hover 時のカラー

個人的には Color を標準で簡単に切り替えられるのはすごい助かりました。CSSをいじればいいだけですが、バージョンアップ等を考えるといじらないに越したことはないので...

表示する内容(カテゴリ)を限定する

emoji-mart-vue はデフォルトでたくさんの絵文字を提供してくれています。
ただ、プロジェクトによっては不要だったり制限したりする可能性もあるので、カテゴリの制限の仕方を確認しました。
余談ですが、実はここがなかなかうまくいかず、一番てこずりました。ドキュメントをよく読まなかったのが敗因です。

include というプロパティが用意されています。
そこに l18n と同じキーをセットします。複数使用する場合には配列で書くだけです。

<template>
  <div class="section">
    <div>
      <Picker
        title="pick emoji"
        emoji="grinning"
        set="twitter"
        color="pink"
        :include="cat"
      />
    </div>
  </div>
</template>
<script lang="ts">
import { defineComponent, ref, reactive } from '@vue/composition-api'
// @ts-ignore
import { Emoji, Picker } from 'emoji-mart-vue'

export default defineComponent({
  name: 'EmojiPicker',
  components: {
    Picker
  },
  setup(){
    const cat = ['search', 'recent', 'people', 'objects']
    return { cat }
  }
})
</script>

選択した絵文字を任意の場所に表示する

  • Emoji という component が用意されているので import します。
  • Pickerselect という event が用意されているため、そこから emoji object を取得します。
  • Emoji component の emoji プロパティ に bind します。
<template>
  <div class="section">
    <div v-for="(item, index) in state.selectedItem" :key="index" class="emoji-wrapper">
      <div>
        <Emoji :emoji="item" :size="20" class="emoji-image" />
      </div>
    </div>
    <div>
      <Picker
        title="pick emoji"
        emoji="grinning"
        set="twitter"
        color="pink"
        :include="cat"
        @select="selectEmoji"
      />
    </div>
  </div>
</template>
<script lang="ts">
import { defineComponent, ref, reactive } from '@vue/composition-api'
// @ts-ignore
import { Emoji, Picker } from 'emoji-mart-vue'

export default defineComponent({
  name: 'EmojiPicker',
  components: {
    Picker,
    Emoji
  },
  setup(props, context) {
    const state = reactive({
      selectedItem: ['grinning'],
    })

    const cat = ['search', 'recent', 'people', 'objects']

    function selectEmoji(item: any) {
      if (!state.selectedItem) {
        state.selectedItem = item.unified
      }
      state.selectedItem.push(item)
    }

    return {
      selectEmoji,
      state,
      cat,
    }
  }
})
</script>

これで表示されるはずです。

Emoji component を使わないパターンを検討する

ここまでで特に問題なく実装ができました。ただ今回はこの内容をデータとして、なるべくシンプルな形で保存しておく必要がありました。
Emoji Component を使わず、 string だけでやりとりができないものかと方法を探していたら、以下の記事を見つけました。

参考: https://medium.com/@allegra9/add-emoji-picker-to-your-react-chat-app-30d8cbe8d9a6

select event で取得できる object の中にある unified に接頭辞 0x をつけて Unicode とし、それを String.fromCodePoint で絵文字として表示します。

以下の code は 抜粋になります。
state.emoji を template に置くと表示されます。

function selectEmoji(item: any) {
  let splitCode = item.unified.split('-');
  let codeArr = new Array()
    splitCode.forEach((c, index) => {
      splitCode[index] = `0x${c}`
    })
    state.emoji = String.fromCodePoint(...splitCode)
}

注意したいのは、 emoji-mart-vue は肌の色なども変えることができるため、取得した unified の中身は - 区切りの string になる場合があります。
その string 一つ一つの頭に 0x をつけないと、望む形の絵文字は表示されません。
最終的にはこの形で渡せればOKです。

String.fromCodePoint('0x1f44d', '0x1f3fd')

unified の中に - がいくつ存在しうるのか、肌の色以外にもどのパターンでそうなるのか、は一つ一つ検証していないのですが、この形で大丈夫だと思います。
何かあればフィードバックして頂けると助かります。^^

Epilogue

絵文字を表示する、という一見するとたくさんやることがありそうな内容ですが、今回見つけた emoji-martVue.js 用に作られたもの(emoji-mart-vue) があり、助かりました。
ドキュメントもわかりやすく、進めるのが楽しかったです。

最後の unified の部分だけ、実装段階まで知らず、気付かずで焦りましたが... JSが素敵な関数用意してくれていてよかった^^ というJSの勉強にもなりました。

今回言及していませんが、この他にもSlack風にするためにカウントを増やしたり tooltip を実装したりしています。また、絵文字のサイズも任意で変えられたりするので、ぜひ実装する際には公式ドキュメントを見ながら進めてみてください。

何かおかしなところや、こういう方法もある、というものがありましたらお気軽にメンションしてください。

9
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?