import { overlayBorderRadius, positions } from './constants'; import { Theme } from '@cleartrip/ct-design-theme'; export const getTooltipBorderSize = (borderSize: `${overlayBorderRadius}`, theme: Theme) => { switch (borderSize) { case overlayBorderRadius.XS: { return { borderRadius: theme.border.radius[4], }; } case overlayBorderRadius.SM: { return { borderRadius: theme.border.radius[6], }; } case overlayBorderRadius.MD: { return { borderRadius: theme.border.radius[8], }; } case overlayBorderRadius.LG: { return { borderRadius: theme.border.radius[16], }; } case overlayBorderRadius.XL: { return { borderRadius: theme.border.radius[32], }; } default: { return { borderRadius: theme.border.radius[8], }; } } }; export const getTooltipAutoPositionStyle = ({ labelRef, tooltipRef, anchorRef, }: { labelRef: React.RefObject; tooltipRef: React.RefObject; anchorRef?: React.RefObject; }) => { const anchorElement = anchorRef?.current || document.body; if (tooltipRef?.current && labelRef?.current && anchorElement) { const anchorElementStyleData = anchorElement.getBoundingClientRect(); const tooltipElementStyleData = tooltipRef.current.getBoundingClientRect(); const { right: anchorRight, bottom: anchorBottom } = anchorElementStyleData; const { right: tooltipRight, bottom: tooltipBottom } = tooltipElementStyleData; const tooltipHeight = tooltipRef.current.offsetHeight; const tooltipWidth = tooltipRef.current.offsetWidth; const labelHeight = labelRef.current.offsetHeight; const labelWidth = labelRef.current.offsetWidth; if (tooltipRight > anchorRight && tooltipBottom > anchorBottom) { /* BOTTOM-RIGHT */ return { left: tooltipWidth - labelWidth < 0 ? tooltipWidth - labelWidth : -(tooltipWidth - labelWidth), top: -(tooltipHeight + 10), }; } else if (tooltipBottom > anchorBottom) { /* BOTTOM - CENTER */ return { top: -(tooltipHeight + 10), }; } else if (tooltipRight > anchorRight) { /* RIGHT - CENTER */ return { left: tooltipWidth - labelWidth < 0 ? tooltipWidth - labelWidth : -(tooltipWidth - labelWidth), top: labelHeight + 10, }; } else { return { top: labelHeight + 10, }; } } return {}; }; export const getTooltipBottomPosition = ({ labelRef, tooltipRef, anchorRef, }: { labelRef: React.RefObject; tooltipRef: React.RefObject; anchorRef?: React.RefObject; }) => { const anchorElement = anchorRef?.current || document.body; if (labelRef?.current && tooltipRef?.current) { const { right: anchorRight, left: anchorLeft } = anchorElement.getBoundingClientRect(); const { right: labelRight, left: labelLeft } = labelRef.current.getBoundingClientRect(); const tooltipWidth = tooltipRef.current.offsetWidth; const labelHeight = labelRef.current.offsetHeight; const labelWidth = labelRef.current.offsetWidth; let leftPosition = tooltipWidth - labelWidth < 0 ? (tooltipWidth - labelWidth) / 2 : -(tooltipWidth - labelWidth) / 2; if (tooltipWidth > labelWidth && anchorRight - labelRight < (tooltipWidth - labelWidth) / 2) { leftPosition -= (tooltipWidth - labelWidth) / 2 - (anchorRight - labelRight) + 4; } else if (tooltipWidth > labelWidth && labelLeft - anchorLeft < (tooltipWidth - labelWidth) / 2) { leftPosition += (tooltipWidth - labelWidth) / 2 - (labelLeft - anchorLeft) + 4; } return { top: labelHeight + 10, left: leftPosition, }; } return {}; }; export const getTooltipTopPosition = ({ labelRef, tooltipRef, anchorRef, }: { labelRef: React.RefObject; tooltipRef: React.RefObject; anchorRef?: React.RefObject; }) => { const anchorElement = anchorRef?.current || document.body; if (labelRef?.current && tooltipRef?.current) { const { right: anchorRight, left: anchorLeft } = anchorElement.getBoundingClientRect(); const { right: labelRight, left: labelLeft } = labelRef.current.getBoundingClientRect(); const tooltipWidth = tooltipRef.current.offsetWidth; const tooltipHeight = tooltipRef.current.offsetHeight; const labelWidth = labelRef.current.offsetWidth; let leftPosition = tooltipWidth - labelWidth < 0 ? (tooltipWidth - labelWidth) / 2 : -(tooltipWidth - labelWidth) / 2; if (tooltipWidth > labelWidth && anchorRight - labelRight < (tooltipWidth - labelWidth) / 2) { leftPosition -= (tooltipWidth - labelWidth) / 2 - (anchorRight - labelRight) + 4; } else if (tooltipWidth > labelWidth && labelLeft - anchorLeft < (tooltipWidth - labelWidth) / 2) { leftPosition += (tooltipWidth - labelWidth) / 2 - (labelLeft - anchorLeft) + 4; } return { top: -(tooltipHeight + 10), left: leftPosition, }; } return {}; }; export const getTooltipPosition = ({ position, labelRef, tooltipRef, anchorRef, }: { position: `${positions}`; labelRef: React.RefObject; tooltipRef: React.RefObject; anchorRef?: React.RefObject; theme: Theme; }) => { if (labelRef?.current && tooltipRef?.current) { const tooltipHeight = tooltipRef.current.offsetHeight; const tooltipWidth = tooltipRef.current.offsetWidth; const labelHeight = labelRef.current.offsetHeight; const labelWidth = labelRef.current.offsetWidth; switch (position) { case positions.LEFT: { return { left: -(tooltipWidth + 10), top: -(tooltipHeight / 2 - labelHeight / 2), }; } case positions.RIGHT: { return { left: labelWidth + 10, top: -(tooltipHeight / 2 - labelHeight / 2), }; } case positions.TOP: { return getTooltipTopPosition({ labelRef, tooltipRef, anchorRef, }); } case positions.BOTTOM: { return getTooltipBottomPosition({ labelRef, tooltipRef, anchorRef, }); } case positions.AUTO: { return getTooltipAutoPositionStyle({ labelRef, tooltipRef, anchorRef, }); } default: { return { left: labelWidth, top: -(tooltipHeight / 2 - labelHeight / 2), }; } } } }; export const getTooltipAutoArrowStyles = ({ labelRef, tooltipRef, anchorRef, }: { labelRef: React.RefObject; tooltipRef: React.RefObject; anchorRef?: React.RefObject; }) => { const anchorElement = anchorRef?.current || document.body; if (tooltipRef?.current && labelRef?.current && anchorElement) { const anchorElementStyleData = anchorElement.getBoundingClientRect(); const tooltipElementStyleData = tooltipRef.current.getBoundingClientRect(); const { right: anchorRight, bottom: anchorBottom } = anchorElementStyleData; const { right: tooltipRight, bottom: tooltipBottom } = tooltipElementStyleData; const labelHeight = labelRef.current.offsetHeight; const labelWidth = labelRef.current.offsetWidth; if (tooltipRight > anchorRight && tooltipBottom > anchorBottom) { /* BOTTOM-RIGHT */ return { top: -11, left: labelWidth / 2, }; } else if (tooltipBottom > anchorBottom) { /* BOTTOM - CENTER */ return { top: -11, left: labelWidth / 2, }; } else if (tooltipRight > anchorRight) { /* RIGHT - CENTER */ return { rotate: '180deg', left: labelWidth / 2, top: labelHeight + 5, }; } else { return { rotate: '180deg', left: labelWidth / 2, top: labelHeight + 5, }; } } return {}; }; export const getTooltipArrowStyles = ({ position, labelRef, tooltipRef, anchorRef, }: { position: `${positions}`; labelRef: React.RefObject; tooltipRef: React.RefObject; anchorRef?: React.RefObject; theme: Theme; }) => { if (labelRef?.current && tooltipRef?.current) { const labelWidth = labelRef.current.offsetWidth; const labelHeight = labelRef.current.offsetHeight; switch (position) { case positions.TOP: { return { top: -11, left: labelWidth / 2, }; } case positions.BOTTOM: { return { rotate: '180deg', top: labelHeight + 5, left: labelWidth / 2, }; } case positions.LEFT: { return { rotate: '-90deg', top: 0, left: -16, }; } case positions.RIGHT: { return { rotate: '90deg', top: 0, left: labelWidth - 1, }; } case positions.AUTO: { return getTooltipAutoArrowStyles({ labelRef, tooltipRef, anchorRef }); } default: { return { rotate: '90deg', top: 0, left: labelWidth, }; } } } return {}; }; export const getTooltipStyles = ({ borderSize, position, labelRef, tooltipRef, anchorRef, theme, }: { borderSize: `${overlayBorderRadius}`; position: `${positions}`; labelRef: React.RefObject; tooltipRef: React.RefObject; anchorRef?: React.RefObject; theme: Theme; }) => { const tooltipPosition = getTooltipPosition({ position, labelRef, tooltipRef, anchorRef, theme }) || {}; return { ...tooltipPosition, ...getTooltipBorderSize(borderSize, theme), }; };