UNPKG

@redocly/theme

Version:

Shared UI components lib

92 lines 3.63 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getDefaultFallbackPlacements = getDefaultFallbackPlacements; exports.calcAnchorPoint = calcAnchorPoint; exports.fitsInViewport = fitsInViewport; exports.resolvePlacement = resolvePlacement; const PLACEMENT_MARGIN = 10; const COUNTER_CLOCKWISE = ['top', 'left', 'bottom', 'right']; function getDefaultFallbackPlacements(placement) { const index = COUNTER_CLOCKWISE.indexOf(placement); const result = []; for (let i = 1; i < COUNTER_CLOCKWISE.length; i++) { result.push(COUNTER_CLOCKWISE[(index + i) % COUNTER_CLOCKWISE.length]); } return result; } function calcAnchorPoint(triggerRect, placement, arrowPosition) { const horizontalLeft = () => arrowPosition === 'left' ? triggerRect.left - 24 : arrowPosition === 'right' ? triggerRect.right + 24 : triggerRect.left + triggerRect.width / 2; const verticalTop = () => triggerRect.top + triggerRect.height / 2; switch (placement) { case 'top': return { top: triggerRect.top, left: horizontalLeft() }; case 'bottom': return { top: triggerRect.bottom, left: horizontalLeft() }; case 'left': return { top: verticalTop(), left: triggerRect.left }; case 'right': return { top: verticalTop(), left: triggerRect.right }; } } function fitsInViewport({ anchor, tooltipWidth, tooltipHeight, placement, arrowPosition, }) { const horizontalLeft = () => arrowPosition === 'left' ? anchor.left : arrowPosition === 'right' ? anchor.left - tooltipWidth : anchor.left - tooltipWidth / 2; const verticalTop = () => anchor.top - tooltipHeight / 2; let top; let left; switch (placement) { case 'top': top = anchor.top - tooltipHeight - PLACEMENT_MARGIN; left = horizontalLeft(); break; case 'bottom': top = anchor.top + PLACEMENT_MARGIN; left = horizontalLeft(); break; case 'left': top = verticalTop(); left = anchor.left - tooltipWidth - PLACEMENT_MARGIN; break; case 'right': top = verticalTop(); left = anchor.left + PLACEMENT_MARGIN; break; } return (top >= 0 && left >= 0 && left + tooltipWidth <= window.innerWidth && top + tooltipHeight <= window.innerHeight); } /** * Given the trigger rect, tooltip dimensions, primary placement/arrow, and * fallback list, returns the first placement that keeps the tooltip fully * inside the viewport. Falls back to the primary when nothing fits. */ function resolvePlacement({ triggerRect, tooltipWidth, tooltipHeight, placement, arrowPosition, fallbackPlacements, }) { if (!(fallbackPlacements === null || fallbackPlacements === void 0 ? void 0 : fallbackPlacements.length) || tooltipWidth === 0 || tooltipHeight === 0) { return placement; } const candidates = [placement, ...fallbackPlacements]; for (const candidate of candidates) { const candidateArrow = candidate === placement ? arrowPosition : 'center'; const pos = calcAnchorPoint(triggerRect, candidate, candidateArrow); if (fitsInViewport({ anchor: pos, tooltipWidth, tooltipHeight, placement: candidate, arrowPosition: candidateArrow, })) { return candidate; } } return placement; } //# sourceMappingURL=tooltip-placement.js.map