Vue.js reusable modal

by Michel Beloshitsky

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.17/vue.js "></script>
<modal-comment :show.sync="showModalComment"></modal-comment>
<button @click="showModalComment = true">Comment modal</button>

<template id="modal">
  <div class="modal-mask" v-if="show" transition="modal" @click="close">
    <div class="modal-container" @click.stop>
      <slot></slot>
    </div>
  </div>
</template>

<template id="modal-comment">
  <modal :show.sync="show" v-ref:modal-container>
    <div class="modal-header">
      <h3>Comment</h3>
    </div>
    <div class="modal-body">
      <form>
        <p>
          <label>Comment</label>
          <textarea rows="5"></textarea>
        </p>
      </form>
    </div>
    <div class="modal-footer">
      <button @click="save()">Save comment</button>
    </div>

  </modal>
</template>

CSS

.modal-mask {
  position: fixed;
  z-index: 9998;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background-color: rgba(0, 0, 0, .7);
  transition: opacity .3s ease;
  display: flex;
  align-items: center;
  justify-content: center;
}

.modal-container {
  min-width: 50vw;
  max-width: 80vw;
  max-height: 80vh;
  padding: 20px 30px;
  background: #fff;
  box-shadow: 0 2px 8px rgba(0, 0, 0, .33);
  transition: all .3s ease;
  overflow: auto;
}

.modal-body {
  margin: 20px 0;
}

.modal-enter,
.modal-leave {
  opacity: 0;
}

.modal-enter .modal-container,
.modal-leave .modal-container {
  transform: scale(1.1);
}

JavaScript

const Modal = Vue.extend({
  props: {
    'show': {
      type: Boolean,
      required: true,
    },
  },
  methods: {
    close: function() {
      this.show = false
    },
  },
  ready: function() {
    const self = this
    document.addEventListener('keydown', function() {
      if (self.show && event.keyCode === 27) { // Close the modal when the escape key is pressed.
        self.close()
      }
    })
  },
  template: "#modal",
})

const ModalComment = Vue.extend({
  props: ['show'],
  components: {
    'modal': Modal,
  },
  methods: {
    close: function() {
      this.$refs.modalContainer.close()
    },
    save: function() {
      // TODO: implement the form logic and an XHR call as soon as the backend is operational.
      this.close()
    },
  },
  template: "#modal-comment",
})

new Vue({
  el: 'body',
  data: {
    showModalComment: false,
  },
  components: {
    'ModalComment': ModalComment,
  },
})