UNPKG

@smkit/ui

Version:

UI Kit of SberMarketing

135 lines (134 loc) 5.82 kB
import { ARROW_HEIGHT, ARROW_WIDTH, CURSOR_TOP_MARGIN, GAP } from './constants'; import {} from './types'; export function getPositionByCursor(tooltipElement, position, evt, scroll = { x: 0, y: 0 }) { const tooltipRect = tooltipElement.getBoundingClientRect(); const x = evt.pageX - tooltipRect.width; const y = evt.pageY + CURSOR_TOP_MARGIN; return { x, y }; } function countCenteredCoordinate(centeredStartPoint, difference) { return centeredStartPoint + difference / 2; } function getCenteredCoordinate(targetElement, targetRects, scroll = 0, tooltipElement, isCenteredHorizontally) { let startPoint = (isCenteredHorizontally ? targetRects.x : targetRects.y) + scroll, targetAxisSize = isCenteredHorizontally ? targetElement.offsetWidth || targetRects.width : targetElement.offsetHeight || targetRects.height, tooltipAxisSize = isCenteredHorizontally ? tooltipElement.offsetWidth || targetRects.width : tooltipElement.offsetHeight || targetRects.height; const difference = targetAxisSize - tooltipAxisSize; return countCenteredCoordinate(startPoint, difference); } function getXCoordinate(targetElement, targetRects, scroll = 0, tooltipElement, position) { const startPoint = targetRects.x + scroll; let coeff = 0; if (position === 'left') { coeff = coeff - tooltipElement.clientWidth - GAP; } else { coeff = coeff + targetElement.clientWidth + GAP; } return startPoint + coeff; } function getYCoordinate(targetElement, targetRects, scroll = 0, tooltipElement, position) { const startPoint = targetRects.y + scroll; let coeff = 0; if (position === 'top') { coeff = coeff - tooltipElement.clientHeight - GAP; } else { coeff = coeff + targetElement.clientHeight + GAP; } return startPoint + coeff; } const FUNCTIONS4CALCULATIONS = { x: getXCoordinate, y: getYCoordinate }; export function getArrowPositionByElement(tooltipElement, position, evt, pos, scroll) { const targetElement = evt.target; const targetRects = targetElement.getBoundingClientRect(); const tooltipRects = tooltipElement.getBoundingClientRect(); const isCenteredHorizontally = position === 'top' || position === 'bottom'; let arrowX = 0, arrowY = 0; if (isCenteredHorizontally) { const halfArrowWidth = (ARROW_WIDTH / 2); const difference = Math.abs(pos.x - targetRects.x - scroll.x); const halfTargetWidth = targetRects.width / 2; arrowX = tooltipRects.width > targetRects.width ? halfTargetWidth + difference - halfArrowWidth : halfTargetWidth - difference - halfArrowWidth; arrowY = position === 'top' ? tooltipRects.height : -halfArrowWidth; } else { const halfArrowHeight = (ARROW_HEIGHT / 2); const difference = Math.abs(pos.y - targetRects.y - scroll.y); const halfTargetHeight = targetRects.height / 2; arrowY = tooltipRects.height > targetRects.height ? halfTargetHeight + difference - halfArrowHeight : halfTargetHeight - difference - halfArrowHeight; arrowX = position === 'left' ? tooltipRects.width - halfArrowHeight : -(ARROW_HEIGHT + halfArrowHeight); } return { x: arrowX, y: arrowY }; } export function getPositionByElement(tooltipElement, position, evt, scroll = { x: 0, y: 0 }) { const targetElement = evt.target; const targetRects = targetElement.getBoundingClientRect(); const isCenteredHorizontally = position === 'top' || position === 'bottom'; let centeredAxis = isCenteredHorizontally ? 'x' : 'y'; const centeredCoord = getCenteredCoordinate(targetElement, targetRects, isCenteredHorizontally ? scroll.x : scroll.y, tooltipElement, isCenteredHorizontally); const calculatedAxis = isCenteredHorizontally ? 'y' : 'x'; const calculatedCoord = FUNCTIONS4CALCULATIONS[calculatedAxis](targetElement, targetRects, isCenteredHorizontally ? scroll.y : scroll.x, tooltipElement, position); return { [centeredAxis]: centeredCoord, [calculatedAxis]: calculatedCoord }; } const positionHandlers = { cursor: getPositionByCursor, element: getPositionByElement }; function clampTooltipPosition(pos, tooltipElement, scroll, win) { const tooltipWidth = tooltipElement.offsetWidth; const tooltipHeight = tooltipElement.offsetHeight; const viewportLeft = scroll.x; const viewportTop = scroll.y; const viewportRight = scroll.x + win.innerWidth; const viewportBottom = scroll.y + win.innerHeight; let { x, y } = pos; if (x < viewportLeft) { x = viewportLeft; } if (x + tooltipWidth > viewportRight) { x = viewportRight - tooltipWidth; } if (y < viewportTop) { y = viewportTop; } if (y + tooltipHeight > viewportBottom) { y = viewportBottom - tooltipHeight; } return { x, y }; } export function getTooltipPosition(evt, tooltipElement, mode, position, win) { if (!evt || !tooltipElement || !mode || !position) return null; const _win = win ?? window; const scroll = { x: _win.scrollX || 0, y: _win.scrollY || 0 }; const handler = positionHandlers[mode]; if (!handler) return null; const pos = handler(tooltipElement, position, evt, scroll); if (!pos) return null; const tooltipPos = clampTooltipPosition(pos, tooltipElement, scroll, _win); const arrowPos = getArrowPositionByElement(tooltipElement, position, evt, tooltipPos, scroll); if (!pos || !arrowPos) return null; // Корректируем позицию, чтобы тултип не выходил за пределы экрана return { tooltip: tooltipPos, arrow: arrowPos }; }