UNPKG

@ithinkdt/naive

Version:

iThinkDT Naive UI

168 lines (150 loc) 5.22 kB
import { defineComponent, h, ref, shallowRef, isVNode, computed, nextTick, mergeProps } from 'vue' import { NTooltip } from 'ithinkdt-ui' import { ThemeProvider } from '../frame/naive' const show = ref(false) const current = shallowRef() const props = shallowRef({}) const tip = shallowRef() const pos = shallowRef([0, 0]) const _tip = computed(() => typeof tip.value === 'function' ? tip.value() : isVNode(tip.value) ? tip.value : h('span', { innerHTML: tip.value || current.value?.innerHTML }), ) export const TooltipProvider = defineComponent({ name: 'DtTooltipDirectiveProvider', setup() { const hoverd = ref(false) return () => ( <NTooltip {...props.value} trigger="manual" show={hoverd.value || show.value} x={pos.value[0]} y={pos.value[1]} onMouseenter={() => { if (props.value.keepAliveOnHover !== false) { hoverd.value = true } }} onMouseleave={() => { hoverd.value = false }} onClickoutside={() => { hoverd.value = false show.value = false }} > <ThemeProvider theme="dark">{_tip.value}</ThemeProvider> </NTooltip> ) }, }) function getPlace(modifiers) { return Object.keys(modifiers).find((m) => ['top', 'right', 'left', 'bottom'].find((k) => m.startsWith(k))) } const handle = function (el, binding) { if (!el.__tooltip) { let enter, leave const onEnter = () => { clearTimer(el) props.value = mergeProps({ keepAliveOnHover: false }, el.__tooltip.props) tip.value = el.__tooltip.tip current.value = el show.value = true const rect = el.getBoundingClientRect() switch ((props.value?.placement || getPlace(el.__tooltip.binding.modifiers) || 'top').split('-')[0]) { case 'top': { pos.value = [rect.left + rect.width / 2, rect.top] break } case 'right': { pos.value = [rect.left + rect.width, rect.top + rect.height / 2] break } case 'left': { pos.value = [rect.left, rect.top + rect.height / 2] break } case 'bottom': { pos.value = [rect.left + rect.width / 2, rect.bottom] break } } } enter = () => { if (!el.__tooltip.binding.modifiers.auto) { onEnter() } else if ( el.offsetWidth > el.parentElement.clientWidth || el.offsetHeight > el.parentElement.clientHeight ) { el.__tooltip.timer = setTimeout(onEnter, 160) } } leave = async () => { clearTimer(el) await nextTick() return new Promise((resolve) => { if (el.__tooltip.timer === undefined) { el.__tooltip.timer2 = setTimeout(() => { el.__tooltip.timer2 = undefined if (current.value !== el) return resolve() show.value = false el.__tooltip.timer3 = setTimeout(() => { current.value = undefined el.__tooltip.timer3 = undefined resolve() }, 300) }, 100) } else { resolve() } }) } el.addEventListener('mouseenter', enter, { passive: true }) el.addEventListener('mouseleave', leave, { passive: false }) Object.defineProperty(el, '__tooltip', { value: { binding, tip: undefined, props: {}, enter, leave, timer: undefined, }, enumerable: false, }) } el.__tooltip.binding = binding if (typeof binding.value === 'string' || typeof binding.value === 'function') { el.__tooltip.tip = binding.value } else { const { tip, ...props } = binding.value || {} el.__tooltip.tip = tip el.__tooltip.props = props } } export const vTooltip = { mounted: handle, updated: handle, beforeUnmount(el) { el?.removeEventListener('mouseenter', el.__tooltip?.enter) el?.removeEventListener('mouseleave', el.__tooltip?.leave) clearTimer(el) if (current.value == el) { show.value = false current.value = undefined } }, } const clearTimer = (el) => { for (let t of ['timer', 'timer2', 'timer3']) { if (el.__tooltip?.[t]) { clearTimeout(el.__tooltip[t]) el.__tooltip[t] = undefined } } }