* Copyright (c) 2022 - present TinyVue Authors.
* Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd.
*
* Use of this source code is governed by an MIT-style license.
*
* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
*
*/
import Tooltip from '@opentiny/vue-tooltip'
import { createComponent, h, hooks } from '@opentiny/vue-common'
interface TooltipDirectiveConfig {
always?: boolean
content?: string
effect?: 'dark' | 'light'
placement?: string
popperClass?: string
}
const MISTAKE_VALUE = 2
type BoundingValueType = undefined | false | TooltipDirectiveConfig
const globalTooltip = {
value: null
}
const tooltipContent = hooks.ref('')
const isEllipsis = (currentTarget) => {
const content = getRealContent(currentTarget)
if (!content) return false
return (
currentTarget.scrollWidth > currentTarget.clientWidth ||
currentTarget.scrollHeight - currentTarget.clientHeight > MISTAKE_VALUE
)
}
const getRealContent = (currentTarget: HTMLElement) =>
'content' in currentTarget.boundingValue
? (currentTarget.boundingValue.content as string)
: currentTarget.textContent
const isAlwaysShowTip = (currentTarget) => Boolean(currentTarget?.boundingValue?.always)
const isDarkTheme = (currentTarget) => Boolean(currentTarget?.boundingValue?.effect === 'dark')
const getPlacement = (currentTarget) => currentTarget.boundingValue?.placement || 'top'
let oldPopperClass: string[] = []
const getPopperClass = (currentTarget) => {
const cls: string = currentTarget.boundingValue?.popperClass || ''
return cls.split(' ').filter((c) => c)
}
const mouseenterHandler = (e) => {
const currentTarget = e.currentTarget
if (!currentTarget.boundingValue) {
return
}
if (isAlwaysShowTip(currentTarget) || isEllipsis(currentTarget)) {
if (!globalTooltip.value) {
tooltipContent.value = getRealContent(currentTarget)
globalTooltip.value = createComponent({
el: document.createElement('div'),
propsData: {
renderContent: () => h('span', { class: 'tiny-directive-tip__content' }, tooltipContent.value),
placement: getPlacement(currentTarget),
effect: isDarkTheme(currentTarget) ? 'dark' : 'light',
popperClass: getPopperClass(currentTarget).join(' ')
},
component: Tooltip
})
oldPopperClass = getPopperClass(currentTarget)
}
const tooltip = globalTooltip.value
const popperElm = tooltip.state.popperElm
tooltipContent.value = getRealContent(currentTarget)
tooltip.state.referenceElm = currentTarget
tooltip.state.currentPlacement = getPlacement(currentTarget)
if (popperElm) {
popperElm.classList.replace(
`is-${isDarkTheme(currentTarget) ? 'light' : 'dark'}`,
`is-${isDarkTheme(currentTarget) ? 'dark' : 'light'}`
)
popperElm.classList.remove(...oldPopperClass)
oldPopperClass = getPopperClass(currentTarget)
popperElm.classList.add(...oldPopperClass)
}
tooltip.show()
if (tooltip.state.popperJS?._options) {
tooltip.state.popperJS._options.placement = getPlacement(currentTarget)
}
tooltip.updatePopper()
}
}
const mouseleaveHandler = () => {
globalTooltip.value && globalTooltip.value.hide()
}
const bind = (el, { value }: { value: BoundingValueType }) => {
let resultValue = value === undefined ? {} : value
if (typeof resultValue === 'boolean' && resultValue) {
resultValue = {}
}
el.boundingValue = resultValue
if (resultValue && !el.boundingValue?.listened) {
el.addEventListener('mouseenter', mouseenterHandler)
el.addEventListener('mouseleave', mouseleaveHandler)
el.boundingValue.listened = true
}
}
const unbind = (el) => {
if (el.boundingValue?.listened) {
const tooltip = globalTooltip.value
if (tooltip && el === tooltip.state.referenceElm && tooltip.state.showPopper) {
tooltip.hide()
}
el.removeEventListener('mouseenter', mouseenterHandler)
el.removeEventListener('mouseleave', mouseleaveHandler)
}
}
1、基本使用
v-auto-tip
v-auto-tip="false"
v-auto-tip="{always:true, content:'123', effect:'dark'}"
v-auto-tip="{always:true, content:'123', effect:'dark',placement:'right'}"
2、动态切换
v-auto-tip="enabled? {always:true, content:'123', effect:'dark'} : false "
** 修改enabled, 可以让tooltip动态提示
3、自定义content
v-auto-tip="{content:h('span','1234' )}" // 单个vnode
v-auto-tip="{content:[h('span','1234' ), h('span','5678' ) ]}" // 多vnode
v-auto-tip="{content:h('span',{innerHTML:'1234<a>5678</a>'})}" // vue3写法:支持动态 html
v-auto-tip="{content:h('span',{domProps:{ innerHTML:'12345678'}})}" // 如果使用common里的h:支持动态 html
** content 支持一个有效的字符串或vnode|vnode[]
*/
export default {
bind,
unbind,
update: bind,
beforeMount: bind,
unmounted: unbind,
updated: bind
}