diff --git a/components/tooltip/demo/index.vue b/components/tooltip/demo/index.vue new file mode 100644 index 000000000..178db3bff --- /dev/null +++ b/components/tooltip/demo/index.vue @@ -0,0 +1,97 @@ + + + + diff --git a/components/tooltip/index.js b/components/tooltip/index.js index 4b5f7d8bc..b93dd566a 100644 --- a/components/tooltip/index.js +++ b/components/tooltip/index.js @@ -1,3 +1,4 @@ import ToolTip from './tooltip.vue' +import './style' export default ToolTip diff --git a/components/tooltip/tooltip.vue b/components/tooltip/tooltip.vue index 33912bae6..b0e17ff46 100644 --- a/components/tooltip/tooltip.vue +++ b/components/tooltip/tooltip.vue @@ -33,7 +33,9 @@ export default { visible: false, left: 0, top: 0, - domNode: null, + realPlacement: this.placement, + t1: null, + t2: null, } }, computed: { @@ -44,63 +46,96 @@ export default { } }, }, - created() { - const div = document.createElement('div') - document.body.appendChild(div) - const that = this - const vnode = new Vue({ - data() { - return { - left: 0, - top: 0, - } - }, - render(h) { - return ( - -
-
-
-
- {that.title} + methods: { + checkPosition(popup, text, placement) { + let { top, left, bottom, right } = text + const reg = /(top|bottom|left|right)(.*)/ + const [, abstractPos, suffix] = placement.match(reg) + let ret = placement + // we can change the position many times + if (abstractPos === 'left' && left < popup.width) ret = 'right' + suffix + if (abstractPos === 'right' && document.documentElement.clientWidth - right < popup.width) ret = 'left' + suffix + if (abstractPos === 'top' && top < popup.height) ret = 'bottom' + suffix + if (abstractPos === 'bottom' && document.documentElement.clientHeight - bottom < popup.height) ret = 'left' + suffix + return ret + }, + mountNode(callback) { + if (this.vnode) { + callback() + return + } + const div = document.createElement('div') + document.body.appendChild(div) + const that = this + const vnode = new Vue({ + data() { + return { + left: 0, + top: 0, + } + }, + methods: { + hideSelf(e) { + if (that.t1) { + clearTimeout(that.t1) + that.t1 = null + } + if (that.mouseLeaveDelay) { + that.t2 = window.setTimeout(() => { + if (e.relatedTarget === that.$el) { + return + } + that.visible = false + }, +that.mouseLeaveDelay * 1e3) + } + } + }, + render(h) { + return ( + +
+
+
+
+ {that.title} +
-
-
- ) - } - }).$mount(div) - this.$nextTick(() => { - this.vnode = vnode - this.domNode = div - }) - }, - methods: { + + ) + } + }).$mount(div) + this.$nextTick(() => { + this.vnode = vnode + callback() + }) + }, onPopupAlign: (placement, domNode, target, align) => { if (!placement) { - return; + return } // 根据当前坐标设置动画点 const rect = domNode.getBoundingClientRect() const transformOrigin = { top: '50%', left: '50%', - }; + } if (placement.indexOf('top') >= 0 || placement.indexOf('Bottom') >= 0) { - transformOrigin.top = `${rect.height - align.offset[1]}px`; + transformOrigin.top = `${rect.height - align.offset[1]}px` } else if (placement.indexOf('Top') >= 0 || placement.indexOf('bottom') >= 0) { - transformOrigin.top = `${-align.offset[1]}px`; + transformOrigin.top = `${-align.offset[1]}px` } if (placement.indexOf('left') >= 0 || placement.indexOf('Right') >= 0) { - transformOrigin.left = `${rect.width - align.offset[0]}px`; + transformOrigin.left = `${rect.width - align.offset[0]}px` } else if (placement.indexOf('right') >= 0 || placement.indexOf('Left') >= 0) { - transformOrigin.left = `${-align.offset[0]}px`; + transformOrigin.left = `${-align.offset[0]}px` } - target.style.transformOrigin = `${transformOrigin.left} ${transformOrigin.top}`; + target.style.transformOrigin = `${transformOrigin.left} ${transformOrigin.top}` }, addEventHandle(old, fn) { if (!old) { @@ -111,68 +146,107 @@ export default { return old === fn ? old : [old, fn] } }, - computeOffset(popup, text, placement) { + computeOffset(popup, text, placement, scale) { let { width, height, top, left } = text // you cant change the properties of DOMRect top += window.scrollY left += window.scrollX + // FIXME: we can get the numbers from scale, but that's not what we really want + const p = { width: popup.width / scale, height: popup.height / scale } const ret = { left, top } - if (/top/.test(placement)) ret.top -= popup.height + if (/top/.test(placement)) ret.top -= p.height if (/bottom/.test(placement)) ret.top += height - if (/left/.test(placement)) ret.left -= popup.width + if (/left/.test(placement)) ret.left -= p.width if (/right/.test(placement)) ret.left += width + // FIXME: magic number 20 & 14 comes from the offset of triangle if (/Left/.test(placement)) { + if (this.arrowPointAtCenter) ret.left += width / 2 - 20 } else if(/Right/.test(placement)) { - ret.left += (width - popup.width) + ret.left += (width - p.width) + if (this.arrowPointAtCenter) ret.left -= width / 2 - 20 } else if(/(top)|(bottom)/.test(placement)) { - ret.left += (width - popup.width) / 2 + ret.left += (width - p.width) / 2 } if (/Top/.test(placement)) { + if (this.arrowPointAtCenter) ret.top += height / 2 - 14 } else if(/Bottom/.test(placement)) { - ret.top += (height - popup.height) + ret.top += (height - p.height) + if (this.arrowPointAtCenter) ret.top -= height / 2 - 14 } else if(/(left)|(right)/.test(placement)) { - ret.top += (height - popup.height) / 2 + ret.top += (height - p.height) / 2 } return ret }, showNode() { - this.visible = true - this.$nextTick(() => { - const popup = this.vnode.$el.getBoundingClientRect() - const content = this.$el.getBoundingClientRect() - const { left, top } = this.computeOffset(popup, content, this.placement) - this.vnode.left = left - this.vnode.top = top + this.mountNode(() => { + this.visible = true + this.$nextTick(() => { + const popup = this.vnode.$el.getBoundingClientRect() + const [, scale = 1] = window.getComputedStyle(this.vnode.$el).transform.match(/matrix\((.*?),/) || [] + const content = this.$el.getBoundingClientRect() + const place = this.autoAdjustOverflow ? this.checkPosition(popup, content, this.placement, scale) : this.placement + this.realPlacement = place + const { left, top } = this.computeOffset(popup, content, place, scale) + this.vnode.left = left + this.vnode.top = top + }) + this.onPopupAlign(this.realPlacement, this.$el, this.vnode.$el, { offset: [0,0] }) }) - this.onPopupAlign(this.placement, this.$el, this.vnode.$el, { offset: [0,0] }) }, - hideNode() { + hideNode(e) { + if (!this.vnode) return + if (e.relatedTarget === this.vnode.$el) { + return + } this.visible = false + }, + checkShow(e) { + if (this.t2) { + clearTimeout(this.t2) + this.t2 = null + } + if (this.mouseEnterDelay) { + this.t1 = window.setTimeout(() => { + this.showNode(e) + }, +this.mouseEnterDelay * 1e3) + } + }, + checkHide(e) { + if (this.t1) { + clearTimeout(this.t1) + this.t1 = null + } + if (this.mouseLeaveDelay) { + this.t2 = window.setTimeout(() => { + this.hideNode(e) + }, +this.mouseLeaveDelay * 1e3) + } } }, render(h) { - let node = this.vnode const inner = this.$slots.default[0] inner.data = inner.data || {} inner.data.on = inner.data.on || {} - inner.data.on.mouseenter = this.addEventHandle(inner.data.on.mouseenter, this.showNode) - inner.data.on.mouseleave = this.addEventHandle(inner.data.on.mouseleave, this.hideNode) -// console.info(inner) + inner.data.on.mouseenter = this.addEventHandle(inner.data.on.mouseenter, this.checkShow) + inner.data.on.mouseleave = this.addEventHandle(inner.data.on.mouseleave, this.checkHide) + return this.$slots.default[0] }, updated() { + if (!this.vnode) return const popup = this.vnode.$el.getBoundingClientRect() + const [, scale = 1] = window.getComputedStyle(this.vnode.$el).transform.match(/matrix\((.*?),/) || [] const content = this.$el.getBoundingClientRect() - const { left, top } = this.computeOffset(popup, content, this.placement) + const { left, top } = this.computeOffset(popup, content, this.realPlacement, scale) this.vnode.left = left this.vnode.top = top }, - beforeDestory() { - console.info('没有成功清除实例 ,看vue panel') - this.vnode.$destroy(); - this.domNode && this.domNode.remove() + beforeDestroy() { + if (!this.vnode) return + this.vnode.$el.remove() + this.vnode.$destroy() } } diff --git a/examples/tooltip.vue b/examples/tooltip.vue deleted file mode 100644 index 651957dbd..000000000 --- a/examples/tooltip.vue +++ /dev/null @@ -1,30 +0,0 @@ - - -