programmatic modal with store

HTML

<script src="https://unpkg.com/[email protected]"></script>

<div id="app">
  <button class="button" @click="openModal()">Open Modal</button>
  <button class="button" @click="openStoreModal()">Open Store Modal</button>
  <button class="button" @click="openParentModal()">Open Parent Modal</button>
</div>

<template id="sampleModal">
  <div class="modal-card">
    <div class="modal-card-head">
      <p class="modal-card-title">Title</p>
    </div>
    <div class="modal-card-foot">
      <button class="button" :class="{'is-loading': isLoading}" @click="$emit('close')">Close</button>
    </div>
  </div>
</template>

<template id="modal">
  <div class="modal is-active">
    <div class="modal-background" @click="close()"></div>
    <div class="modal-content animation-content">
      <component :is="component" @close="close()"></component>
    </div>
  </div>
</template>

CSS

@import url("//unpkg.com/buefy/lib/buefy.css");

JavaScript

Vue.use(Vuex)
const store = new Vuex.Store({
	state: {
  	loading: 0
  },
  getters: {
  	isLoading({loading}) {
    	return !!loading
    }
  },
  mutations: {
    startLoading(state) {
    	state.loading++
    },
    stopLoading(state) {
    	state.loading--
    }
  }
})

class Modal {
	constructor() {
  	this.modal = {
    	template: '#modal',
      props: {
         component: Object
      },
      methods: {
        close() {
          setTimeout(() => {
            this.$destroy()
            this.$el.remove()
          }, 150)
        }
      },
      beforeMount() {
        document.body.appendChild(this.$el)
      }
    }
  }

	open(params) {
    const propsData = Object.assign({}, params)
    const ModalComponent = Vue.extend(this.modal)
    return new ModalComponent({
      el: document.createElement('div'),
      propsData
    })
  }
  
  openWithStore(params) {
    const propsData = Object.assign({}, params)
    const ModalComponent = Vue.extend(this.modal)
    return new ModalComponent({
      el: document.createElement('div'),
      propsData,
      store
    })
  }
  
  openWithParent(params) {
  	const propsData = Object.assign({}, params)
    const ModalComponent = Vue.extend(this.modal)
    return new ModalComponent({
      el: document.createElement('div'),
      propsData,
      parent
    })
  }
}

Vue.use({
	install(Vue) {
  	const modal = new Modal
    Object.defineProperties(Vue.prototype, {
      $modal: {
        get() {
          return modal
        }
      }
    })
  }
})

const sampleModal = {
	template: '#sampleModal',
  created() {
  	this.$store.commit('startLoading')
  },
  mounted() {
  	setTimeout(() => {
    	this.$store.commit('stopLoading')
    }, 1000)
  },
  computed: {
  	isLoading() {
    	return this.$store.getters.isLoading
    }
  }
}

const parent = new Vue({
	el: '#app',
  store,
  methods: {
  	openModal() {
    	this.$modal.open({
      	component: sampleModal
      })
    },
    openStoreModal() {
   ...