UNPKG

uiv

Version:

Bootstrap 3 components implemented by Vue 2.

256 lines (251 loc) 7.14 kB
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) } } } }