This commit is contained in:
oneao committed 2025-09-05 17:29:10 +08:00
1 parent 48dac36685
commit d17b49cdd9
18 files changed
+532 -340

No files matched your search

@@ -4,5 +4,5 @@
@use './input/index.scss' as *;
@use './textarea/index.scss' as *;
@use './group/index.scss' as *;
@use "./popup/tooltip.scss" as *;
@use "./popup/index.scss" as *;
@@ -0,0 +1,10 @@
@use '../_styles/_variables.scss' as *;
$popup-prefix: #{$prefix}-popup;
.#{$popup-prefix} {
position: absolute;
top: 0px;
left: 0px;
width: 100%;
}
@@ -1,158 +1,15 @@
// Tooltip.tsx
import type { PropType } from 'vue'
import { cloneVNode, defineComponent, nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
interface Position {
top: number
left: number
}
import { defineComponent } from 'vue'
import { getPrefixCls } from '../_utils/getPrefixCls'
export default defineComponent({
name: 'Tooltip',
props: {
content: { type: String, required: true },
placement: { type: String as PropType<'top' | 'bottom' | 'left' | 'right'>, default: 'top' },
trigger: { type: String as PropType<'hover' | 'click'>, default: 'hover' },
offset: { type: Number, default: 6 },
},
name: 'Popup',
setup(props, { slots }) {
const visible = ref(false)
const targetEl = ref<HTMLElement | null>(null)
const tooltipEl = ref<HTMLDivElement | null>(null)
const prefixCls = getPrefixCls('popup')
const createTooltip = () => {
const el = document.createElement('div')
el.className = `tooltip tooltip-${props.placement}`
const contentEl = document.createElement('div')
contentEl.className = 'tooltip-content'
contentEl.textContent = props.content
const arrowEl = document.createElement('div')
arrowEl.className = 'tooltip-arrow'
el.appendChild(contentEl)
el.appendChild(arrowEl)
// 初始状态
el.style.position = 'absolute'
el.style.top = '0'
el.style.left = '0'
el.style.opacity = '0'
el.style.visibility = 'hidden'
el.style.pointerEvents = 'none'
document.body.appendChild(el)
tooltipEl.value = el
}
const getTargetDom = (): HTMLElement | null => {
if (!targetEl.value)
return null
let dom = targetEl.value
if ('$el' in dom)
dom = (dom as any).$el
return dom instanceof HTMLElement ? dom : null
}
const updatePosition = () => {
const tooltip = tooltipEl.value
const target = getTargetDom()
if (!tooltip || !target)
return
const rect = target.getBoundingClientRect()
const tooltipRect = tooltip.getBoundingClientRect()
const scrollX = window.scrollX || window.pageXOffset
const scrollY = window.scrollY || window.pageYOffset
let top = 0
let left = 0
const offset = props.offset
switch (props.placement) {
case 'top':
top = rect.top + scrollY - tooltipRect.height - offset
left = rect.left + scrollX + rect.width / 2 - tooltipRect.width / 2
break
case 'bottom':
top = rect.bottom + scrollY + offset
left = rect.left + scrollX + rect.width / 2 - tooltipRect.width / 2
break
case 'left':
top = rect.top + scrollY + rect.height / 2 - tooltipRect.height / 2
left = rect.left + scrollX - tooltipRect.width - offset
break
case 'right':
top = rect.top + scrollY + rect.height / 2 - tooltipRect.height / 2
left = rect.right + scrollX + offset
break
}
tooltip.style.top = `${top}px`
tooltip.style.left = `${left}px`
}
const show = () => {
const tooltip = tooltipEl.value
if (!tooltip || !getTargetDom())
return
tooltip.style.visibility = 'visible'
tooltip.style.pointerEvents = 'auto'
nextTick(() => {
updatePosition()
tooltip.style.opacity = '1'
tooltip.style.transform = 'scale(1)'
})
visible.value = true
}
const hide = () => {
const tooltip = tooltipEl.value
if (!tooltip)
return
tooltip.style.opacity = '0'
tooltip.style.transform = 'scale(0.95)'
tooltip.style.pointerEvents = 'none'
tooltip.style.visibility = 'hidden'
visible.value = false
}
const toggle = () => (visible.value ? hide() : show())
onMounted(() => {
createTooltip()
const onUpdate = () => {
if (visible.value)
updatePosition()
}
window.addEventListener('scroll', onUpdate)
window.addEventListener('resize', onUpdate)
onBeforeUnmount(() => {
window.removeEventListener('scroll', onUpdate)
window.removeEventListener('resize', onUpdate)
if (tooltipEl.value && tooltipEl.value.parentNode) {
tooltipEl.value.parentNode.removeChild(tooltipEl.value)
}
})
})
return () => {
const child = slots.default?.()[0]
if (!child)
return null
return cloneVNode(child, {
ref: targetEl,
onMouseenter: props.trigger === 'hover' ? show : undefined,
onMouseleave: props.trigger === 'hover' ? hide : undefined,
onClick: props.trigger === 'click' ? toggle : undefined,
})
}
return () => (
<div class={prefixCls}>
{/* {slots?.default()} */}
</div>
)
},
})
@@ -1,58 +0,0 @@
@use '../_styles/_variables.scss' as *;
.tooltip {
z-index: 9999;
padding: 6px 10px;
background: #fff;
border-radius: 4px;
color: #333;
font-size: 12px;
white-space: nowrap;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
transition:
opacity 0.2s ease,
transform 0.2s ease;
pointer-events: none;
opacity: 0;
transform: scale(0.95);
visibility: hidden;
&.visible {
opacity: 1;
pointer-events: auto;
}
.tooltip-arrow {
position: absolute;
width: 8px;
height: 8px;
background: #fff;
transform: rotate(45deg);
}
&.tooltip-top .tooltip-arrow,
&.tooltip-bottom .tooltip-arrow {
left: 50%;
transform: translateX(-50%) rotate(45deg);
}
&.tooltip-left .tooltip-arrow,
&.tooltip-right .tooltip-arrow {
top: 50%;
transform: translateY(-50%) rotate(45deg);
}
&.tooltip-top .tooltip-arrow {
bottom: -4px;
}
&.tooltip-bottom .tooltip-arrow {
top: -4px;
}
&.tooltip-left .tooltip-arrow {
right: -4px;
}
&.tooltip-right .tooltip-arrow {
left: -4px;
}
}
@@ -38,7 +38,6 @@ export default defineComponent({
icon={props.icon}
size={props.size}
spin
rotate={props.rotate}
/>
{props.tip && <span class={`${prefixCls}-tip`}>{props.tip}</span>}
</div>