UNPKG

quasar

Version:

Build high-performance VueJS user interfaces (SPA, PWA, SSR, Mobile and Desktop) in record time

348 lines (285 loc) 8.41 kB
import Vue from 'vue' import ModelToggleMixin from '../../mixins/model-toggle.js' import PortalMixin from '../../mixins/portal.js' import PreventScrollMixin from '../../mixins/prevent-scroll.js' import EscapeKey from '../../utils/escape-key.js' import slot from '../../utils/slot.js' import { create, stop, stopAndPrevent } from '../../utils/event.js' let maximizedModals = 0 const positionClass = { standard: 'fixed-full flex-center', top: 'fixed-top justify-center', bottom: 'fixed-bottom justify-center', right: 'fixed-right items-center', left: 'fixed-left items-center' } const transitions = { top: ['down', 'up'], bottom: ['up', 'down'], right: ['left', 'right'], left: ['right', 'left'] } export default Vue.extend({ name: 'QDialog', mixins: [ ModelToggleMixin, PortalMixin, PreventScrollMixin ], modelToggle: { history: true }, props: { persistent: Boolean, autoClose: Boolean, noEscDismiss: Boolean, noBackdropDismiss: Boolean, noRouteDismiss: Boolean, noRefocus: Boolean, noFocus: Boolean, seamless: Boolean, maximized: Boolean, fullWidth: Boolean, fullHeight: Boolean, square: Boolean, position: { type: String, default: 'standard', validator (val) { return val === 'standard' || ['top', 'bottom', 'left', 'right'].includes(val) } }, transitionShow: { type: String, default: 'scale' }, transitionHide: { type: String, default: 'scale' } }, data () { return { transitionState: this.showing } }, watch: { $route () { this.persistent !== true && this.noRouteDismiss !== true && this.seamless !== true && this.hide() }, showing (val) { if (this.position !== 'standard' || this.transitionShow !== this.transitionHide) { this.$nextTick(() => { this.transitionState = val }) } }, maximized (newV, oldV) { if (this.showing === true) { this.__updateState(false, oldV) this.__updateState(true, newV) } }, seamless (v) { this.showing === true && this.__preventScroll(!v) }, useBackdrop (v) { if (this.$q.platform.is.desktop === true) { const action = `${v === true ? 'add' : 'remove'}EventListener` document.body[action]('focusin', this.__onFocusChange) } } }, computed: { classes () { return `q-dialog__inner--${this.maximized === true ? 'maximized' : 'minimized'} ` + `q-dialog__inner--${this.position} ${positionClass[this.position]}` + (this.fullWidth === true ? ' q-dialog__inner--fullwidth' : '') + (this.fullHeight === true ? ' q-dialog__inner--fullheight' : '') + (this.square === true ? ' q-dialog__inner--square' : '') }, transition () { return 'q-transition--' + ( this.position === 'standard' ? (this.transitionState === true ? this.transitionHide : this.transitionShow) : 'slide-' + transitions[this.position][this.transitionState === true ? 1 : 0] ) }, useBackdrop () { return this.showing === true && this.seamless !== true } }, methods: { focus () { let node = this.__getInnerNode() if (node === void 0 || node.contains(document.activeElement) === true) { return } if (this.$q.platform.is.ios) { // workaround the iOS hover/touch issue this.avoidAutoClose = true node.click() this.avoidAutoClose = false } node = node.querySelector('[autofocus]') || node node.focus() }, shake () { this.focus() const node = this.__getInnerNode() if (node !== void 0) { node.classList.remove('q-animate--scale') node.classList.add('q-animate--scale') clearTimeout(this.shakeTimeout) this.shakeTimeout = setTimeout(() => { node.classList.remove('q-animate--scale') }, 170) } }, __getInnerNode () { return this.__portal !== void 0 && this.__portal.$refs !== void 0 ? this.__portal.$refs.inner : void 0 }, __show (evt) { clearTimeout(this.timer) this.__refocusTarget = this.noRefocus === false ? document.activeElement : void 0 this.$el.dispatchEvent(create('popup-show', { bubbles: true })) this.__updateState(true, this.maximized) EscapeKey.register(this, () => { if (this.seamless !== true) { if (this.persistent === true || this.noEscDismiss === true) { this.maximized !== true && this.shake() } else { this.$emit('escape-key') this.hide() } } }) this.__showPortal() if (this.noFocus !== true) { document.activeElement.blur() this.$nextTick(() => { this.focus() }) } if (this.$q.platform.is.desktop === true && this.useBackdrop === true) { document.body.addEventListener('focusin', this.__onFocusChange) } this.timer = setTimeout(() => { this.$emit('show', evt) }, 300) }, __hide (evt) { this.__cleanup(true) if (this.__refocusTarget !== void 0) { this.__refocusTarget.focus() } this.$el.dispatchEvent(create('popup-hide', { bubbles: true })) this.timer = setTimeout(() => { this.__hidePortal() this.$emit('hide', evt) }, 300) }, __cleanup (hiding) { clearTimeout(this.timer) clearTimeout(this.shakeTimeout) if (this.$q.platform.is.desktop === true && this.seamless !== true) { document.body.removeEventListener('focusin', this.__onFocusChange) } if (hiding === true || this.showing === true) { EscapeKey.pop(this) this.__updateState(false, this.maximized) } }, __updateState (opening, maximized) { if (this.seamless !== true) { this.__preventScroll(opening) } if (maximized === true) { if (opening === true) { maximizedModals < 1 && document.body.classList.add('q-body--dialog') } else if (maximizedModals < 2) { document.body.classList.remove('q-body--dialog') } maximizedModals += opening === true ? 1 : -1 } }, __onAutoClose (e) { if (this.avoidAutoClose !== true) { this.hide(e) this.$listeners.click !== void 0 && this.$emit('click', e) } }, __onBackdropClick (e) { if (this.persistent !== true && this.noBackdropDismiss !== true) { this.hide(e) } else { this.shake() } }, __onFocusChange (e) { const node = this.__getInnerNode() if ( node !== void 0 && this.__portal.$el !== void 0 && // we don't have another portal opened: this.__portal.$el.nextElementSibling === null && this.__portal.$el.contains(e.target) !== true ) { node.focus() } }, __render (h) { const on = { ...this.$listeners, input: stop } if (this.autoClose === true) { on.click = this.__onAutoClose } return h('div', { staticClass: 'q-dialog fullscreen no-pointer-events', class: this.contentClass, style: this.contentStyle, attrs: this.$attrs }, [ h('transition', { props: { name: 'q-transition--fade' } }, this.useBackdrop === true ? [ h('div', { staticClass: 'q-dialog__backdrop fixed-full', on: { touchmove: stopAndPrevent, // prevent iOS page scroll click: this.__onBackdropClick } }) ] : null), h('transition', { props: { name: this.transition } }, [ this.showing === true ? h('div', { ref: 'inner', staticClass: 'q-dialog__inner flex no-pointer-events', class: this.classes, attrs: { tabindex: -1 }, on }, slot(this, 'default')) : null ]) ]) }, __onPortalClose (evt) { this.hide(evt) } }, mounted () { this.value === true && this.show() }, beforeDestroy () { this.__cleanup() } })