Vue.js emojione in a chat application

by riyaz_ali

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.5/require.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/emojione@3.1.6/extras/css/emojione.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/emojionearea/3.4.1/emojionearea.css">
<!-- Vuejs application -->
<div id="app">

  <div class="chat">
    <message v-for="(msg, index) in messages" :key="index" :class="msg.type" :content="msg.content" />
  </div>
  
  <message-box />
</div>

<script type="x-template" id="messagebox">
<div class="message-box">
  <textarea></textarea>
</div>
</script>

SCSS

body {
  background: #20262e;
  padding: 16px;
}

#app {
  box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.12);
  background-image: url('http://backgroundcheckall.com/wp-content/uploads/2017/12/whatsapp-background-android-5.jpg')
}

.chat {
  padding: 8px;
  height: 100%;
  border-radius: 2px;
  
  .message {
    margin: 8px 4px;
    font-family: sans-serif;
    
    &.sent {
      display: flex;
      justify-content: flex-end;
      margin-left: 25%;
      color: #262626;
      
      .bubble {
        position: relative;
        background: #dcf8c6;
        border-radius: .4em;
        display: inline-block;
        padding: 8px;

        &::after {
          content: '';
          position: absolute;
          right: 0;
          top: 50%;
          width: 0;
          height: 0;
          border: 9px solid transparent;
          border-left-color: #dcf8c6;
          border-right: 0;
          margin-top: -9px;
          margin-right: -9px;
        }
      }
    }
    
    &.received {
      margin-right: 25%;
      color: #262626;
      
      .bubble {
        position: relative;
        background: #fff;
        border-radius: .4em;
        display: inline-block;
        padding: 8px;

        &::after {
          content: '';
          position: absolute;
          left: 0;
          top: 50%;
          width: 0;
          height: 0;
          border: 9px solid transparent;
          border-right-color: #fff;
          border-left: 0;
          margin-top: -9px;
          margin-left: -9px;
        }
      }
    }
  }
}

.message-box {
  position: relative;
  padding: 16px;
  
  textarea {
    display: block;
    border: none;
    border-radius: 2px;
    outline: none;
    padding: 0.5rem;
    width: 95%;
    margin: auto;
    box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.12);
  }
}

JavaScript

// global require
require.config({
  paths: {
    'jquery': 'https://code.jquery.com/jquery-3.3.1',
    'Vue': 'https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue',
    'emojione': 'https://cdn.jsdelivr.net/npm/emojione@3.1.6/lib/js/emojione',
    'jquery.textcomplete': 'https://cdnjs.cloudflare.com/ajax/libs/jquery.textcomplete/1.8.4/jquery.textcomplete',
    'emojionearea': 'https://cdnjs.cloudflare.com/ajax/libs/emojionearea/3.4.1/emojionearea'
  },
  
  shim: {
    'emojione': {
      'exports': 'emojione'
    }
  },
  
  map: {
    "*": {
      "jquery": "jquery-shield"
    },
    "jquery-shield": {
      "jquery": "jquery"
    },
    // patch plugins
    "jquery.textcomplete": {
      "jquery": "jquery"
    },
    "emojionearea": {
      "jquery": "jquery"
    }
  }
})

// define shield
define('jquery-shield', ['jquery', 'jquery.textcomplete', 'emojionearea'], function($){
  return $ //.noConflict(); uncomment if you want to hide jquery from window
})

// app entrypoint
require(['require', 'jquery', 'Vue', 'emojione', 'jquery.textcomplete', 'emojionearea'], function(require, $, Vue, emojione){

  // define the <message /> component
  Vue.component('message', {
    props: ['content'],
    render(h){
      return h('div', {
        class: {
          'message': true
        }
      }, [h('div', {
        class: {
          'bubble': true
        },
        domProps: {
          innerHTML: emojione.toImage(this.content)
        }
      })])
    }
  });
  
  // define <message-box /> component
  Vue.component('message-box', {
    template: "#messagebox",
    mounted(){
      // find the <input />
      $(this.$el).find('textarea').emojioneArea()
    }
  })
  
  
  // just some helpers
  let send = txt => ({type: 'sent', content: txt})
  let recv = txt => ({type: 'received', content: txt})
  
  // render our chat application
  let $app = new Vue({
    data(){
      return { 
        messages: [
          send('Hey :hugging:!!'),
          recv('Hello there...