2024-10-03 09:01:44 +00:00
|
|
|
export function createTooltip(referenceElement?: Element, tooltipElement?: HTMLElement, options = {}) {
|
|
|
|
const defaultOptions = {
|
|
|
|
placement: 'top',
|
2024-10-09 15:52:30 +00:00
|
|
|
offset: [0, 8],
|
|
|
|
flip: {
|
|
|
|
fallbackPlacements: ['top', 'bottom']
|
|
|
|
}
|
2024-10-03 09:01:44 +00:00
|
|
|
}
|
|
|
|
const config = { ...defaultOptions, ...options }
|
|
|
|
|
|
|
|
function updatePosition() {
|
|
|
|
if (!(referenceElement && tooltipElement)) return
|
|
|
|
|
|
|
|
const rect = referenceElement.getBoundingClientRect()
|
|
|
|
const tooltipRect = tooltipElement.getBoundingClientRect()
|
|
|
|
const offsetX = config.offset[0]
|
|
|
|
const offsetY = config.offset[1]
|
|
|
|
|
2024-10-09 15:52:30 +00:00
|
|
|
let placement = config.placement
|
2024-10-03 09:01:44 +00:00
|
|
|
let top = 0
|
|
|
|
let left = 0
|
|
|
|
|
2024-10-09 15:52:30 +00:00
|
|
|
// Базовое позиционирование
|
|
|
|
switch (placement) {
|
|
|
|
case 'top':
|
2024-10-03 09:01:44 +00:00
|
|
|
top = rect.top - tooltipRect.height - offsetY
|
|
|
|
left = rect.left + (rect.width - tooltipRect.width) / 2 + offsetX
|
|
|
|
break
|
2024-10-09 15:52:30 +00:00
|
|
|
case 'bottom':
|
2024-10-03 09:01:44 +00:00
|
|
|
top = rect.bottom + offsetY
|
|
|
|
left = rect.left + (rect.width - tooltipRect.width) / 2 + offsetX
|
|
|
|
break
|
2024-10-09 15:52:30 +00:00
|
|
|
// Добавьте case для 'left' и 'right', если необходимо
|
|
|
|
}
|
|
|
|
|
|
|
|
// Проверка на выход за границы окна и применение flip
|
|
|
|
if (top < 0 && config.flip.fallbackPlacements.includes('bottom')) {
|
|
|
|
top = rect.bottom + offsetY
|
|
|
|
placement = 'bottom'
|
|
|
|
} else if (top + tooltipRect.height > window.innerHeight && config.flip.fallbackPlacements.includes('top')) {
|
|
|
|
top = rect.top - tooltipRect.height - offsetY
|
|
|
|
placement = 'top'
|
2024-10-03 09:01:44 +00:00
|
|
|
}
|
|
|
|
|
2024-10-09 15:52:30 +00:00
|
|
|
// Применение позиции
|
2024-10-03 09:01:44 +00:00
|
|
|
tooltipElement.style.position = 'absolute'
|
|
|
|
tooltipElement.style.top = `${top}px`
|
|
|
|
tooltipElement.style.left = `${left}px`
|
2024-10-09 15:52:30 +00:00
|
|
|
tooltipElement.style.visibility = 'visible'
|
|
|
|
|
|
|
|
// Обновление класса для стрелки
|
|
|
|
tooltipElement.setAttribute('data-popper-placement', placement)
|
|
|
|
|
|
|
|
// Позиционирование стрелки
|
|
|
|
const arrow = tooltipElement.querySelector('[data-popper-arrow]') as HTMLElement
|
|
|
|
if (arrow) {
|
|
|
|
const arrowRect = arrow.getBoundingClientRect()
|
|
|
|
if (placement === 'top') {
|
|
|
|
arrow.style.bottom = '-4px'
|
|
|
|
arrow.style.left = `${tooltipRect.width / 2 - arrowRect.width / 2}px`
|
|
|
|
} else if (placement === 'bottom') {
|
|
|
|
arrow.style.top = '-4px'
|
|
|
|
arrow.style.left = `${tooltipRect.width / 2 - arrowRect.width / 2}px`
|
|
|
|
}
|
|
|
|
}
|
2024-10-03 09:01:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function showTooltip() {
|
2024-10-09 15:52:30 +00:00
|
|
|
if (tooltipElement) {
|
|
|
|
tooltipElement.style.visibility = 'visible'
|
|
|
|
updatePosition()
|
|
|
|
}
|
2024-10-03 09:01:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function hideTooltip() {
|
|
|
|
if (tooltipElement) tooltipElement.style.visibility = 'hidden'
|
|
|
|
}
|
|
|
|
|
|
|
|
referenceElement?.addEventListener('mouseenter', showTooltip)
|
|
|
|
referenceElement?.addEventListener('mouseleave', hideTooltip)
|
|
|
|
window.addEventListener('resize', updatePosition)
|
2024-10-09 15:52:30 +00:00
|
|
|
window.addEventListener('scroll', updatePosition)
|
2024-10-03 09:01:44 +00:00
|
|
|
|
|
|
|
return {
|
|
|
|
update: updatePosition,
|
|
|
|
destroy() {
|
|
|
|
referenceElement?.removeEventListener('mouseenter', showTooltip)
|
|
|
|
referenceElement?.removeEventListener('mouseleave', hideTooltip)
|
|
|
|
window.removeEventListener('resize', updatePosition)
|
2024-10-09 15:52:30 +00:00
|
|
|
window.removeEventListener('scroll', updatePosition)
|
2024-10-03 09:01:44 +00:00
|
|
|
}
|
|
|
|
}
|
2024-10-09 15:52:30 +00:00
|
|
|
}
|