webapp/src/lib/createTooltip.ts

93 lines
3.2 KiB
TypeScript
Raw Normal View History

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
}