Skip to content

Instantly share code, notes, and snippets.

@antixrist
Last active June 7, 2017 08:50
Show Gist options
  • Save antixrist/4458c76b34c183c3fa93975d39bdd12b to your computer and use it in GitHub Desktop.
Save antixrist/4458c76b34c183c3fa93975d39bdd12b to your computer and use it in GitHub Desktop.
Модалки могут быть вложены друг в друга, могут открываться одн�� поверх другой, а закрываться по `Esc` будут в правильном порядке. Модалка не диктует правила для контента по визуальному отображению. Поэтому бэкграунды, крестик для закрытия, ширину/высоту - можно настроить индивидуально для содержимого каждой модалки
<script>
export default {
data () {
return { myModalOpened: false };
}
};
</script>
<template>
<p> <button @click="myModalOpened = !myModalOpened">Модалочка</button> <p>
<modal v-model="myModalOpened">
<p><button @click="myModalOpened = false;">Закрыть</button></p>
<p>Контент модалки. Здесь может быть что угодно, в т.ч. другие компоненты</p>
</modal>
</template>
<script>
function modalsOnEscListener (e) {
if (e.keyCode !== 27) { return; }
let topOpenedModal;
[...this.$root.createdModals]
.reverse()
.some(instance => instance.isOpened && instance.closeOnEsc ? (topOpenedModal = instance, true) : false)
;
topOpenedModal && topOpenedModal.close();
}
export default {
props: {
value: {
type: Boolean,
default: false
},
position: {
type: String,
default: ''
},
overlay: {
type: Boolean,
default: true
},
closeOnOverlayClick: {
type: Boolean,
default: true
},
closeOnEsc: {
type: Boolean,
default: true
},
overlayCSS: Object,
bodyCSS: Object,
},
watch: {
value: {
immediate: true,
handler (opened, oldVal) {
document && document.documentElement.classList[opened ? 'add' : 'remove' ]('modal-opened');
},
},
},
computed: {
isOpened () {
return this.value;
}
},
methods: {
open () {
this.$emit('input', true);
},
close () {
this.$emit('input', false);
},
closeOnOverlay () {
this.overlay && this.closeOnOverlayClick && this.close();
},
},
beforeCreate () {
// beforeCreate - просто создаётся эксземпляр компонента
this.$root.createdModals = this.$root.createdModals || [];
this.$root.createdModals.push(this);
this.$root.modalsOnEscListenerAdded = this.$root.modalsOnEscListenerAdded || false;
},
beforeMount () {
// beforeMount - компонент уже монтируется в dom-дерево. поэтому и событие можно вешать
if (!this.$root.modalsOnEscListenerAdded) {
this.$root.modalsOnEscListenerAdded = true;
document && document.addEventListener('keyup', modalsOnEscListener.bind(this));
}
},
beforeDestroy () {
const { createdModals } = this.$root;
createdModals.splice(createdModals.indexOf(this), 1);
// перед уничтожением проверим - есть ли ещё инстансы какой-нибудь модалки
if (!createdModals.length) {
// если нет, то снимаем событие на `esc`
document && document.removeEventListener('keyup', modalsOnEscListener);
}
},
};
</script>
<template lang="pug">
.modal(v-if="value")
.modal__overlay(
v-if="overlay",
:style="overlayCSS",
@click="closeOnOverlay()",
)
.modal__outer
.modal__outer-scrollable
.modal__y-outer(@click.self="closeOnOverlay()")
.modal__y(
@click.self="closeOnOverlay()",
:style="{ 'vertical-align': position == 'center' ? 'middle' : position }",
)
.modal__x(@click.self="closeOnOverlay()")
.modal__body(@click.self="closeOnOverlay()", :style="bodyCSS")
.modal__content: slot
</template>
<style lang="scss">
html.modal-opened {
height: 100%;
overflow: hidden;
}
body {
height: 100%;
overflow: hidden;
}
.modal {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
overflow: hidden;
z-index: 999;
&__overlay {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: rgba(#000, .7);
}
&__outer {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 2;
overflow: hidden;
&-scrollable {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
overflow-y: auto;
}
}
&__body {
padding: 1em;
}
&__content {
box-shadow: 0 0 10px rgba(#000, .6);
position: relative;
}
&__y-outer {
display: table;
width: 100%;
height: 100%;
}
&__y {
display: table-cell;
vertical-align: middle;
text-align: center;
}
&__x {
text-align: left;
display: inline-block;
}
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment