@smkit/ui
Version:
UI Kit of SberMarketing
135 lines (134 loc) • 5.82 kB
JavaScript
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 };
}