UNPKG

vue-uiv

Version:

Bootstrap 3 components implemented by Vue 2.

247 lines (244 loc) 6.72 kB
import { PLACEMENTS, ensureElementMatchesFunction, on, off, EVENTS, TRIGGERS, removeFromDom, removeClass, hasClass, setTooltipPosition, isElement, addClass } from '../utils/domUtils' import {isString} from '../utils/objectUtils' const SHOW_CLASS = 'in' export default { props: { value: { type: Boolean, default: false }, tag: { type: String, default: 'span' }, placement: { type: String, default: PLACEMENTS.TOP }, autoPlacement: { type: Boolean, default: true }, appendTo: { type: String, default: 'body' }, transitionDuration: { type: Number, default: 150 }, enable: { type: Boolean, default: true }, enterable: { type: Boolean, default: true }, target: null }, data () { return { triggerEl: null, timeoutId: 0 } }, watch: { value (v) { v ? this.show() : this.hide() }, trigger () { this.clearListeners() this.initListeners() }, target (value) { this.clearListeners() this.initTriggerElByTarget(value) this.initListeners() }, allContent (value) { // can not use value because it can not detect slot changes if (this.isNotEmpty()) { // reset position while content changed & is shown // nextTick is required this.$nextTick(() => { if (this.isShown()) { this.resetPosition() } }) } else { this.hide() } }, enable (value) { // hide if enable changed to false if (!value) { this.hide() } } }, mounted () { ensureElementMatchesFunction() removeFromDom(this.$refs.popup) this.$nextTick(() => { this.initTriggerElByTarget(this.target) this.initListeners() if (this.value) { this.show() } }) }, beforeDestroy () { this.clearListeners() removeFromDom(this.$refs.popup) }, methods: { initTriggerElByTarget (target) { if (target) { // target exist if (isString(target)) { // is selector this.triggerEl = document.querySelector(target) } else if (isElement(target)) { // is element this.triggerEl = target } else if (isElement(target.$el)) { // is component this.triggerEl = target.$el } } else { // find special element let trigger = this.$el.querySelector('[data-role="trigger"]') if (trigger) { this.triggerEl = trigger } else { // use the first child let firstChild = this.$el.firstChild this.triggerEl = firstChild === this.$refs.popup ? null : firstChild } } }, initListeners () { if (this.triggerEl) { if (this.trigger === TRIGGERS.HOVER) { on(this.triggerEl, EVENTS.MOUSE_ENTER, this.show) on(this.triggerEl, EVENTS.MOUSE_LEAVE, this.hide) } else if (this.trigger === TRIGGERS.FOCUS) { on(this.triggerEl, EVENTS.FOCUS, this.show) on(this.triggerEl, EVENTS.BLUR, this.hide) } else if (this.trigger === TRIGGERS.HOVER_FOCUS) { on(this.triggerEl, EVENTS.MOUSE_ENTER, this.handleAuto) on(this.triggerEl, EVENTS.MOUSE_LEAVE, this.handleAuto) on(this.triggerEl, EVENTS.FOCUS, this.handleAuto) on(this.triggerEl, EVENTS.BLUR, this.handleAuto) } else if (this.trigger === TRIGGERS.CLICK || this.trigger === TRIGGERS.OUTSIDE_CLICK) { on(this.triggerEl, EVENTS.CLICK, this.toggle) } } on(window, EVENTS.CLICK, this.windowClicked) }, clearListeners () { if (this.triggerEl) { off(this.triggerEl, EVENTS.FOCUS, this.show) off(this.triggerEl, EVENTS.BLUR, this.hide) off(this.triggerEl, EVENTS.MOUSE_ENTER, this.show) off(this.triggerEl, EVENTS.MOUSE_LEAVE, this.hide) off(this.triggerEl, EVENTS.CLICK, this.toggle) off(this.triggerEl, EVENTS.MOUSE_ENTER, this.handleAuto) off(this.triggerEl, EVENTS.MOUSE_LEAVE, this.handleAuto) off(this.triggerEl, EVENTS.FOCUS, this.handleAuto) off(this.triggerEl, EVENTS.BLUR, this.handleAuto) } off(window, EVENTS.CLICK, this.windowClicked) }, resetPosition () { const popup = this.$refs.popup setTooltipPosition(popup, this.triggerEl, this.placement, this.autoPlacement, this.appendTo) popup.offsetHeight }, hideOnLeave () { if (this.trigger === TRIGGERS.HOVER || (this.trigger === TRIGGERS.HOVER_FOCUS && !this.triggerEl.matches(':focus'))) { this.$hide() } }, toggle () { if (this.isShown()) { this.hide() } else { this.show() } }, show () { if (this.enable && this.triggerEl && this.isNotEmpty() && !this.isShown()) { let popup = this.$refs.popup if (this.timeoutId > 0) { clearTimeout(this.timeoutId) this.timeoutId = 0 } else { popup.className = `${this.name} ${this.placement} fade` let container = document.querySelector(this.appendTo) container.appendChild(popup) this.resetPosition() } addClass(popup, SHOW_CLASS) this.$emit('input', true) this.$emit('show') } }, hide () { if (!this.isShown()) { return } if (this.enterable && (this.trigger === TRIGGERS.HOVER || this.trigger === TRIGGERS.HOVER_FOCUS)) { setTimeout(() => { if (!this.$refs.popup.matches(':hover')) { this.$hide() } }, 100) } else { this.$hide() } }, $hide () { if (this.isShown()) { clearTimeout(this.timeoutId) removeClass(this.$refs.popup, SHOW_CLASS) this.timeoutId = setTimeout(() => { removeFromDom(this.$refs.popup) this.timeoutId = 0 this.$emit('input', false) this.$emit('hide') }, this.transitionDuration) } }, isShown () { return hasClass(this.$refs.popup, SHOW_CLASS) }, windowClicked (event) { if (this.triggerEl && !this.triggerEl.contains(event.target) && this.trigger === TRIGGERS.OUTSIDE_CLICK && !this.$refs.popup.contains(event.target) && this.isShown()) { this.hide() } }, handleAuto () { setTimeout(() => { if (this.triggerEl.matches(':hover, :focus')) { this.show() } else { this.hide() } }, 20) // 20ms make firefox happy } } }