#app
  .modal.simple-modal(
    :style="{display: visibility ? 'block' : 'none'}",
  )
    .modal-dialog.modal-dialog-scrollable(
      ref="dialog",
      :class="dialogClass",
    )
      .modal-content(
        ref="content",
        :style="contentStyle",
      )
        .modal-header.p-2
          h4 Hello
          span.close(
            @click="doClose",
          ) ×
        .modal-body
          img(src="http://evereditor.com/1.jpg")
View Compiled
.simple-modal
  background-color: rgba(0, 0, 0, 0.5)
  .modal-content
    padding 1em

    .close
      cursor pointer

.modal-dialog-resizable
  &.ready
    max-width unset !important

  .modal-content
    resize both
    margin 0 auto
    
  .modal-body
    padding 1rem 0 0 
    img
      max-width 100%
View Compiled
const RESIZED_SIZE = 'resized_width_key';
let sharedSize;
const modal = new Vue({
  el: '#app',
  props: {
    size: {
      type: String,
      default: null,
      validator: function(value) {
        return ['sm', 'lg', 'xl'].indexOf(value) !== -1;
      },
    },
    resizable: {
      type: Boolean,
      default: true,
    },
    backdrop: {
      type: Boolean,
      default: true,
    },
  },
  computed: {
    dialogClass() {
      const classes = [];
      if (this.size) {
        classes.push(`modal-${this.size}`);
      }
      if (this.resizable) {
        classes.push('modal-dialog-resizable');
      }
      if (this.resizedSize) {
        classes.push('ready');
      }
      return classes.join(' ');
    },
    contentStyle() {
      if (!this.resizable || !this.resizedSize) {
        return null;
      }
      const {width, height} = this.resizedSize;
      return {
        width: `${width}px`,
        height: `${height}px`,
      };
    },
  },
  data() {
    return {
      visibility: false,
      resizedSize: null,
    };
  },
  methods: {
    async doOpen() {
      this.visibility = true;
      this.$emit('open');
      if (this.resizable) {
        const onResize = _.debounce(this.onEditorResize, 100);
        const observer = this.observer = new MutationObserver(onResize);
        observer.observe(this.$refs.content, {
          attributes: true,
        });

        if (!this.resizedSize) {
          if (sharedSize) {
            this.resizedSize = sharedSize;
          } else {
            await this.$nextTick();
            const width = this.$refs.dialog.clientWidth;
            this.resizedSize = {width};
          }
        }
      }
    },
    doClose() {
      this.visibility = false;
      this.$emit('close');
    },
    onEditorResize([{target}]) {
      const width = target.clientWidth;
      const height = target.clientHeight;
      if (width < 320 || height < 160) {
        return;
      }
      sharedSize = {width, height};
      localStorage.setItem(RESIZED_SIZE, JSON.stringify(sharedSize));
    },
  },
  mounted() {
    const size = localStorage.getItem(RESIZED_SIZE);
    if (size) {
      this.resizedSize = JSON.parse(size);
    }
  },
  beforeDestroy() {
    if (this.observer) {
      this.observer.disconnect();
      this.observer = null;
    }
  },
});

modal.doOpen();
Run Pen

External CSS

  1. https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js