pull/9/head
tangjinzhou 2017-11-06 17:55:27 +08:00
commit f2ea0ccc0f
4 changed files with 239 additions and 97 deletions

View File

@ -0,0 +1,97 @@
<template>
<div>
<tool-tip
placement="top"
:title="showText"
:autoAdjustOverflow="autoAdjustOverflow"
>
<h1 @click="boom" class="test">撞到边缘翻转位置 & 点击更新</h1>
</tool-tip>
<ant-button @click="reverse" type="primary">{{autoAdjustOverflow ? '启用' : '关闭'}}自动调整中</ant-button>
<div class="box">
<h2>切换arrowPointAtCenter模式</h2>
<ant-button @click="change">{{arrowPointAtCenter}}</ant-button>
<table>
<tr v-for="(tr, index) in table" :key="index">
<td v-for="(td, i) in tr" :key="i">
<tool-tip
v-if="td"
:placement="td"
:title="td"
:arrowPointAtCenter="arrowPointAtCenter"
>
<AntButton type="primary">{{td}}</AntButton>
</tool-tip>
</td>
</tr>
</table>
</div>
<div>
<p>
<tool-tip :arrowPointAtCenter="true" title="Consider using the NamedModulesPlugin for module names." placement="topLeft">
<ant-button>arrowPointAtCenter arrowPointAtCenter arrowPointAtCenter</ant-button>
</tool-tip>
</p>
</div>
</div>
</template>
<script>
import { ToolTip, Button } from 'antd'
import 'antd/button/style'
export default {
name: 'tooltip-basic',
data() {
return {
show: true,
showText: '你好啊233',
table: [
['', 'topLeft', 'top', 'topRight', ''],
['leftTop', '', '', '', 'rightTop'],
['left', '', '', '', 'right'],
['leftBottom', '', '', '', 'rightBottom'],
['', 'bottomLeft', 'bottom', 'bottomRight', ''],
],
arrowPointAtCenter: false,
autoAdjustOverflow: true,
}
},
methods: {
boom() {
if (this.showText.length % 20) {
this.showText += '3'
} else {
this.showText += ' '
}
},
change() {
this.arrowPointAtCenter = !this.arrowPointAtCenter
},
reverse() {
this.autoAdjustOverflow = !this.autoAdjustOverflow
}
},
components: {
ToolTip,
AntButton: Button,
}
}
</script>
<style scoped lang="less">
.test {
margin: 20px;
display: inline-block;
}
.box {
margin: 100px;
}
table {
td {
padding: 20px;
}
p {
text-align: center;
vertical-align: middle;
}
}
</style>

View File

@ -1,3 +1,4 @@
import ToolTip from './tooltip.vue' import ToolTip from './tooltip.vue'
import './style'
export default ToolTip export default ToolTip

View File

@ -33,7 +33,9 @@ export default {
visible: false, visible: false,
left: 0, left: 0,
top: 0, top: 0,
domNode: null, realPlacement: this.placement,
t1: null,
t2: null,
} }
}, },
computed: { computed: {
@ -44,63 +46,96 @@ export default {
} }
}, },
}, },
created() { methods: {
const div = document.createElement('div') checkPosition(popup, text, placement) {
document.body.appendChild(div) let { top, left, bottom, right } = text
const that = this const reg = /(top|bottom|left|right)(.*)/
const vnode = new Vue({ const [, abstractPos, suffix] = placement.match(reg)
data() { let ret = placement
return { // we can change the position many times
left: 0, if (abstractPos === 'left' && left < popup.width) ret = 'right' + suffix
top: 0, 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
render(h) { return ret
return ( },
<transition name="zoom-big"> mountNode(callback) {
<div if (this.vnode) {
v-show={that.visible} callback()
class={`ant-tooltip ant-tooltip-placement-${that.placement}`} return
style={{ left: this.left + 'px', top: this.top + 'px' }} }
> const div = document.createElement('div')
<div class="ant-tooltip-content"> document.body.appendChild(div)
<div class="ant-tooltip-arrow"/> const that = this
<div class="ant-tooltip-inner"> const vnode = new Vue({
<span>{that.title}</span> 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 (
<transition name={that.transitionName}>
<div
v-show={that.visible}
class={`ant-tooltip ant-tooltip-placement-${that.realPlacement}`}
style={{ left: this.left + 'px', top: this.top + 'px' }}
onMouseleave={this.hideSelf}
>
<div class="ant-tooltip-content">
<div class="ant-tooltip-arrow"/>
<div class="ant-tooltip-inner">
<span>{that.title}</span>
</div>
</div> </div>
</div> </div>
</div> </transition>
</transition> )
) }
} }).$mount(div)
}).$mount(div) this.$nextTick(() => {
this.$nextTick(() => { this.vnode = vnode
this.vnode = vnode callback()
this.domNode = div })
}) },
},
methods: {
onPopupAlign: (placement, domNode, target, align) => { onPopupAlign: (placement, domNode, target, align) => {
if (!placement) { if (!placement) {
return; return
} }
// //
const rect = domNode.getBoundingClientRect() const rect = domNode.getBoundingClientRect()
const transformOrigin = { const transformOrigin = {
top: '50%', top: '50%',
left: '50%', left: '50%',
}; }
if (placement.indexOf('top') >= 0 || placement.indexOf('Bottom') >= 0) { 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) { } 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) { 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) { } 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) { addEventHandle(old, fn) {
if (!old) { if (!old) {
@ -111,68 +146,107 @@ export default {
return old === fn ? old : [old, fn] return old === fn ? old : [old, fn]
} }
}, },
computeOffset(popup, text, placement) { computeOffset(popup, text, placement, scale) {
let { width, height, top, left } = text let { width, height, top, left } = text
// you cant change the properties of DOMRect // you cant change the properties of DOMRect
top += window.scrollY top += window.scrollY
left += window.scrollX 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 } 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 (/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 if (/right/.test(placement)) ret.left += width
// FIXME: magic number 20 & 14 comes from the offset of triangle
if (/Left/.test(placement)) { if (/Left/.test(placement)) {
if (this.arrowPointAtCenter) ret.left += width / 2 - 20
} else if(/Right/.test(placement)) { } 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)) { } else if(/(top)|(bottom)/.test(placement)) {
ret.left += (width - popup.width) / 2 ret.left += (width - p.width) / 2
} }
if (/Top/.test(placement)) { if (/Top/.test(placement)) {
if (this.arrowPointAtCenter) ret.top += height / 2 - 14
} else if(/Bottom/.test(placement)) { } 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)) { } else if(/(left)|(right)/.test(placement)) {
ret.top += (height - popup.height) / 2 ret.top += (height - p.height) / 2
} }
return ret return ret
}, },
showNode() { showNode() {
this.visible = true this.mountNode(() => {
this.$nextTick(() => { this.visible = true
const popup = this.vnode.$el.getBoundingClientRect() this.$nextTick(() => {
const content = this.$el.getBoundingClientRect() const popup = this.vnode.$el.getBoundingClientRect()
const { left, top } = this.computeOffset(popup, content, this.placement) const [, scale = 1] = window.getComputedStyle(this.vnode.$el).transform.match(/matrix\((.*?),/) || []
this.vnode.left = left const content = this.$el.getBoundingClientRect()
this.vnode.top = top 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 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) { render(h) {
let node = this.vnode
const inner = this.$slots.default[0] const inner = this.$slots.default[0]
inner.data = inner.data || {} inner.data = inner.data || {}
inner.data.on = inner.data.on || {} inner.data.on = inner.data.on || {}
inner.data.on.mouseenter = this.addEventHandle(inner.data.on.mouseenter, this.showNode) inner.data.on.mouseenter = this.addEventHandle(inner.data.on.mouseenter, this.checkShow)
inner.data.on.mouseleave = this.addEventHandle(inner.data.on.mouseleave, this.hideNode) inner.data.on.mouseleave = this.addEventHandle(inner.data.on.mouseleave, this.checkHide)
// console.info(inner)
return this.$slots.default[0] return this.$slots.default[0]
}, },
updated() { updated() {
if (!this.vnode) return
const popup = this.vnode.$el.getBoundingClientRect() const popup = this.vnode.$el.getBoundingClientRect()
const [, scale = 1] = window.getComputedStyle(this.vnode.$el).transform.match(/matrix\((.*?),/) || []
const content = this.$el.getBoundingClientRect() 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.left = left
this.vnode.top = top this.vnode.top = top
}, },
beforeDestory() { beforeDestroy() {
console.info('没有成功清除实例 看vue panel') if (!this.vnode) return
this.vnode.$destroy(); this.vnode.$el.remove()
this.domNode && this.domNode.remove() this.vnode.$destroy()
} }
} }
</script> </script>

View File

@ -1,30 +0,0 @@
<template>
<div>
<tool-tip
placement="top"
:title="showText">
<h1 @click="boom" style="display: inline-block">This is just a test, put your cursor here</h1>
</tool-tip>
</div>
</template>
<script>
import { ToolTip } from '../components'
export default {
name: '',
data() {
return {
show: true,
showText: '你好啊23'
}
},
methods: {
boom() {
this.showText += '3'
}
},
components: {
ToolTip
}
}
</script>