Stackable modal

Stackable modals is a library for modal dialogs which can be infinitely stacked. The component uses bootstrap for styling the modal but DOES NOT include it by default - you need to import it yourself.

When you open a second dialog the first one is scaled to 0.9 and position behind

Installation

npm i @innologica/vue-stackable-modal --save

or

yarn add @innologica/vue-stackable-modal

import to use:

import StackModal from '@innologica/vue-stackable-modal'

Example

I have added some demo css classes in order to demonstrate some of the possibilities of the component - source here

Source code

<template>
    <div class="py-4">
        <p>I have added some demo css classes in order to demonstrate some of the possibilities of the component - <a href="#sample-css">source here</a> </p>
        <div class="d-flex">
            <div>
                <button class="btn btn-primary" @click="show=true">Open modal</button>
            </div>
            <div class="d-flex align-items-center mx-3">
                <label>Modal class</label>
            </div>
            <div class="d-flex align-items-center">
                <select v-model="modalClass" class="form-control">
                    <option value="">none</option>
                    <option value="modal-fullscreen">modal-fullscreen</option>
                    <option value="modal-xl">modal-xl</option>
                    <option value="modal-xxl">modal-xxl</option>
                    <option value="modal-90per">modal-90per</option>
                    <option value="modal-border-0">modal-border-0</option>
                </select>
            </div>
        </div>

        <stack-modal
                :show="show"
                title="Modal #1"
                @close="show=false"
                :modal-class="{ [modalClass]: true }"
                :saveButton="{ visible: false }"
                :cancelButton="{ title: 'Close', btnClass: { 'btn btn-primary': true } }"
        >
            <button class="btn btn-info" @click="show_second=true">Open modal 2</button>

            <hr/>
            <p>Additional options: </p>
            <pre class="language-vue">
  ...
  :saveButton="{ visible: false }"
  :cancelButton="{ title: 'Close' }"
  ...
            </pre>
        </stack-modal>

        <stack-modal
                :show="show_second"
                @close="show_second=false"
        >
            <p>I have no title so the modal-header is missing.</p>

            <button class="btn btn-warning" @click="show_third=true">Open modal 3</button>
        </stack-modal>

        <stack-modal
                :show="show_third"
                @close="show_third=false"
                title="Last modal"
                :close-on-escape="false"
        >
            <p>This is the last modal. This modal has closeOnEscape set to false so you cannot close it via ESC key.</p>
        </stack-modal>

    </div>
</template>

<script>
  import StackModal from '../../../src/components/StackModal'

  export default {
    name: "StackableModalDemo",
    components: {StackModal},
    data () {
      return {
        show: false,
        show_second: false,
        show_third: false,
        modalClass: ''
      }
    },
  }
</script>

<style>
    @import "https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css";
</style>

<style lang="scss">
    @import "../../../src/assets/transitions/translate-fade.scss";
    @import "../../../src/assets/modal";
</style>

Sample css

$modal-xl: 900px;
$modal-xxl: 1040px;
$modal-xxxl: 1180px;
$modal-90per: 90%;

@media (min-width: 768px) {
  .modal-xl {
    max-width: $modal-xl;
  }
  .modal-xxl {
    max-width: $modal-xxl;
  }
  .modal-xxxl{
    max-width: $modal-xxxl;
  }
  .modal-90per {
    max-width: $modal-90per;
  }
}

.modal-fullscreen {
  max-width: 100%;
  width: 100%;
  margin: 0;
  height: 100%;

  .modal-body {
    padding: 0;
  }

  .modal-content {
    height: 100%;
  }

  &.aside .modal-content {
    height: auto;
  }

  &.modal-dialog.aside.modal-stack1 .modal-content {
    transform: scale(1) translate(0, 0) !important;
  }
}

//modal without border
.modal-dialog.modal-border-0 {
  .modal-content {
    border: none;
  }
}

@media screen and (min-width: 800px){
  .modal-dialog {
    &.modal-xxl{
      width: 98%;
      max-width: 1040px;
    }
  }
}


@media screen and (min-width: 640px){
  .modal-dialog {
    &.modal-xxl{
      width: 98%;
      max-width: 1040px;
    }
  }
}

@media screen and (max-width: 420px){
  .modal-dialog,
  .modal-content{
    overflow: auto;
  }
}

Props

Prop Type Default Description
show Boolean false Shows/hides the modal
title String The title of the modal shown in .modal-header div. If empty the div is not rendered
modalClass Object {} :class object which is attached to the modal dialog element
hasBackdrop Boolean true Whether to display backdrop element for this dialog. It is added to the body with calculated z-index.
saveButton Object { title: 'Save', visible: true, btnClass: {'btn btn-primary': true}} Save button config
cancelButton Object { title: 'Cancel', visible: true, btnClass: {'btn btn-outline-secondary': true}} Cancel button config
transition String translate-fade* Transition to use when showing the modal.You need to include scss with the transition
closeOnEscape Boolean true If enabled the component attaches event handler on document body which monitors for ESC and emits the 'close' event.

*for the default transition you need to @import "~@innologica/vue-stackable-modal/src/assets/transitions/translate-fade.scss";

Events

Event Params Description
save none When the save button is pressed
close none When the cancel button is pressed, or the "x" in the title is clicked, or escape is pressed. Yoo need to close the modal via the show property
show show, index, total Called when the modal show/hides. show - open/close, index - the index in the current stack, total - total modals open

Slots

default

This slot is the content of the modal.

Default value:

<slot name="modal-header">
    <div class="modal-header" v-if="title">
        <h5 class="modal-title">{{title}}</h5>
        <a class="close" aria-label="Close" @click.stop="$emit('close')">
            <span aria-hidden="true">×</span>
        </a>
    </div>
</slot>

The modal header is rendered above the content. If title is empty then it is not rendered

Default value:

<slot name="modal-footer">
    <div class="modal-footer">
        <button
                v-if="saveButtonOptions.visible"
                type="button"
                @click="$emit('save')"
                :class="{ ...saveButtonOptions.btnClass }"
        >{{saveButtonOptions.title}}
        </button>
        <button
                type="button"
                :class="{ ...cancelButtonOptions.btnClass }"
                data-dismiss="modal"
                @click.stop="$emit('close')"
        >{{cancelButtonOptions.title}}
        </button>
    </div>
</slot>

This is the modal footer with the 2 buttons. If you just want to hide the footer you can provide empty footer slot e.g.:

<div slot="modal-footer"></div>