uiv
Version:
Bootstrap 3 components implemented by Vue 2.
256 lines (251 loc) • 7.14 kB
JavaScript
import Local from '../../mixins/locale.mixin'
import Btn from './../button/Btn'
import {
EVENTS,
on,
off,
removeFromDom,
toggleBodyOverflow,
addClass,
removeClass,
getComputedStyle,
getOpenModals,
getOpenModalNum
} from '../../utils/dom.utils'
import { isFunction, isPromiseSupported } from '../../utils/object.utils'
const IN = 'in'
export default {
mixins: [Local],
components: { Btn },
props: {
value: {
type: Boolean,
default: false
},
title: String,
size: String,
backdrop: {
type: Boolean,
default: true
},
footer: {
type: Boolean,
default: true
},
header: {
type: Boolean,
default: true
},
cancelText: String,
cancelType: {
type: String,
default: 'default'
},
okText: String,
okType: {
type: String,
default: 'primary'
},
dismissBtn: {
type: Boolean,
default: true
},
transition: {
type: Number,
default: 150
},
autoFocus: {
type: Boolean,
default: false
},
keyboard: {
type: Boolean,
default: true
},
beforeClose: Function,
zOffset: {
type: Number,
default: 20
},
appendToBody: {
type: Boolean,
default: false
},
displayStyle: {
type: String,
default: 'block'
}
},
data () {
return {
msg: ''
}
},
computed: {
modalSizeClass () {
return {
[`modal-${this.size}`]: Boolean(this.size)
}
}
},
watch: {
value (v) {
this.$toggle(v)
}
},
mounted () {
removeFromDom(this.$refs.backdrop)
on(window, EVENTS.MOUSE_DOWN, this.suppressBackgroundClose)
on(window, EVENTS.KEY_UP, this.onKeyPress)
if (this.value) {
this.$toggle(true)
}
},
beforeDestroy () {
clearTimeout(this.timeoutId)
removeFromDom(this.$refs.backdrop)
removeFromDom(this.$el)
if (getOpenModalNum() === 0) {
toggleBodyOverflow(true)
}
off(window, EVENTS.MOUSE_DOWN, this.suppressBackgroundClose)
off(window, EVENTS.MOUSE_UP, this.unsuppressBackgroundClose)
off(window, EVENTS.KEY_UP, this.onKeyPress)
},
methods: {
onKeyPress (event) {
if (this.keyboard && this.value && event.keyCode === 27) {
const thisModal = this.$refs.backdrop
let thisZIndex = thisModal.style.zIndex
thisZIndex = thisZIndex && thisZIndex !== 'auto' ? parseInt(thisZIndex) : 0
// Find out if this modal is the top most one.
const modals = getOpenModals()
const modalsLength = modals.length
for (let i = 0; i < modalsLength; i++) {
if (modals[i] !== thisModal) {
let zIndex = modals[i].style.zIndex
zIndex = zIndex && zIndex !== 'auto' ? parseInt(zIndex) : 0
// if any existing modal has higher zIndex, ignore
if (zIndex > thisZIndex) {
return
}
}
}
this.toggle(false)
}
},
toggle (show, msg) {
let shouldClose = true
if (isFunction(this.beforeClose)) {
shouldClose = this.beforeClose(msg)
}
if (isPromiseSupported()) {
// Skip the hiding when beforeClose returning falsely value or returned Promise resolves to falsely value
// Use Promise.resolve to accept both Boolean values and Promises
Promise.resolve(shouldClose).then((shouldClose) => {
// Skip the hiding while show===false
if (!show && shouldClose) {
this.msg = msg
this.$emit('input', show)
}
})
} else {
// Fallback to old version if promise is not supported
// skip the hiding while show===false & beforeClose returning falsely value
if (!show && !shouldClose) {
return
}
this.msg = msg
this.$emit('input', show)
}
},
$toggle (show) {
const modal = this.$el
const backdrop = this.$refs.backdrop
clearTimeout(this.timeoutId)
if (show) {
// If two modals share the same v-if condition the calculated z-index is incorrect,
// resulting in popover misbehaviour.
// solved by adding a nextTick.
// https://github.com/uiv-lib/uiv/issues/342
this.$nextTick(() => {
const alreadyOpenModalNum = getOpenModalNum()
document.body.appendChild(backdrop)
if (this.appendToBody) {
document.body.appendChild(modal)
}
modal.style.display = this.displayStyle
modal.scrollTop = 0
backdrop.offsetHeight // force repaint
toggleBodyOverflow(false)
addClass(backdrop, IN)
addClass(modal, IN)
// fix z-index for nested modals
// no need to calculate if no modal is already open
if (alreadyOpenModalNum > 0) {
const modalBaseZ = parseInt(getComputedStyle(modal).zIndex) || 1050 // 1050 is default modal z-Index
const backdropBaseZ = parseInt(getComputedStyle(backdrop).zIndex) || 1040 // 1040 is default backdrop z-Index
const offset = alreadyOpenModalNum * this.zOffset
modal.style.zIndex = `${modalBaseZ + offset}`
backdrop.style.zIndex = `${backdropBaseZ + offset}`
}
// z-index fix end
this.timeoutId = setTimeout(() => {
if (this.autoFocus) {
const btn = this.$el.querySelector('[data-action="auto-focus"]')
if (btn) {
btn.focus()
/* START.TESTS_ONLY */
/* istanbul ignore next */
btn.setAttribute('data-focused', 'true')
/* END.TESTS_ONLY */
}
}
this.$emit('show')
this.timeoutId = 0
}, this.transition)
})
} else {
removeClass(backdrop, IN)
removeClass(modal, IN)
this.timeoutId = setTimeout(() => {
modal.style.display = 'none'
removeFromDom(backdrop)
if (this.appendToBody) {
removeFromDom(modal)
}
if (getOpenModalNum() === 0) {
toggleBodyOverflow(true)
}
this.$emit('hide', this.msg || 'dismiss')
this.msg = ''
this.timeoutId = 0
// restore z-index for nested modals
modal.style.zIndex = ''
backdrop.style.zIndex = ''
// z-index fix end
}, this.transition)
}
},
suppressBackgroundClose (event) {
if (event && event.target === this.$el) {
return
}
this.isCloseSuppressed = true
on(window, 'mouseup', this.unsuppressBackgroundClose)
},
unsuppressBackgroundClose () {
if (this.isCloseSuppressed) {
off(window, 'mouseup', this.unsuppressBackgroundClose)
setTimeout(() => {
this.isCloseSuppressed = false
}, 1)
}
},
backdropClicked (event) {
if (this.backdrop && !this.isCloseSuppressed) {
this.toggle(false)
}
}
}
}