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.
modal-header
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
modal-footer
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>