import { Theme } from '@cleartrip/ct-design-theme'; import { makeStyles } from '@cleartrip/ct-design-style-manager'; import { getDeviceHeight, getDeviceWidth, isMobile } from '@cleartrip/ct-design-common-utils'; import { IMeasurements } from './type'; import { overlayBorderRadius, positions } from './constants'; import { ViewStyle } from '@cleartrip/ct-design-types'; export type positionType = `${positions}`; export type overlayBorderRadiusType = `${overlayBorderRadius}`; export const staticTooltipStyles = makeStyles((theme) => { return { root: { position: 'relative', }, labelContainerStyles: { width: (isMobile() ? 100 : 'fit-content') as unknown as number, flexDirection: 'row', }, overlayStyles: { flexDirection: 'row', position: 'absolute', width: theme?.size[50], zIndex: theme?.zIndex.tooltip, backgroundColor: theme?.color.background.defaultDarkest, paddingVertical: theme?.spacing[1], paddingHorizontal: theme?.spacing[2], borderRadius: theme?.border.radius[8], }, tooltipArrow: { display: 'flex', position: 'absolute', zIndex: theme?.zIndex.tooltip, }, }; }); const getTooltipArrowAutoPositionStyle = ({ measurements }: { measurements: IMeasurements }) => { const { labelContainer, overlayContainer } = measurements; const screenWidth = getDeviceWidth(); const screenHeight = getDeviceHeight(); if (labelContainer && overlayContainer) { const tooltipHeight = overlayContainer.height ?? 0; const labelHeight = labelContainer.height ?? 0; const labelWidth = labelContainer.width ?? 0; const labelLeftEdge = labelContainer.leftEdge ?? 0; const labelRightEdge = labelContainer.rightEdge ?? 0; // Calculate available space in each direction const spaceAbove = labelContainer.y; const spaceBelow = screenHeight - (labelContainer.y + labelHeight); const spaceLeft = labelLeftEdge; const spaceRight = screenWidth - labelRightEdge; // Determine arrow position based on tooltip position if (spaceBelow >= tooltipHeight + 10) { return { transform: `rotate(180deg)`, top: labelHeight + 5, left: labelWidth / 2, }; } else if (spaceAbove >= tooltipHeight + 10) { return { top: -11, left: labelWidth / 2, }; } else if (spaceRight >= overlayContainer.width + 10) { return { transform: `rotate(90deg)`, top: labelHeight - 15, left: labelWidth - 1, }; } else if (spaceLeft >= overlayContainer.width + 10) { return { transform: `rotate(-90deg)`, top: labelHeight - 15, left: -16, }; } // Default to bottom arrow if no space is available return { transform: `rotate(180deg)`, top: labelHeight + 5, left: labelWidth / 2, }; } return { left: 0, top: 0 }; }; export const getOverlayTopPosition = ({ measurements }: { measurements: IMeasurements }) => { const tooltipWidth = measurements?.overlayContainer.width || 0; const labelWidth = measurements?.labelContainer?.width || 0; const screenWidth = getDeviceWidth(); const leftEdge = measurements.labelContainer.leftEdge || 0; const rightEdge = measurements.labelContainer.rightEdge || 0; let leftPosition = 0; if (tooltipWidth > labelWidth) { const labelCenter = (leftEdge + rightEdge) / 2; leftPosition = (labelCenter - tooltipWidth) / 2; if (leftPosition < 0) { leftPosition = 0; } else if (leftPosition + tooltipWidth > screenWidth) { leftPosition = screenWidth - tooltipWidth; } } else { leftPosition = labelWidth / 2 - tooltipWidth / 2; if (leftPosition < 0) { leftPosition = 0; } else if (leftPosition + tooltipWidth > screenWidth) { leftPosition = screenWidth - tooltipWidth; } } return { top: -(measurements?.overlayContainer?.height + 10), left: leftPosition, }; }; export const getOverlayBottomPosition = ({ measurements }: { measurements: IMeasurements }) => { const tooltipWidth = measurements?.overlayContainer.width || 0; const labelWidth = measurements?.labelContainer?.width || 0; const screenWidth = getDeviceWidth(); const leftEdge = measurements.labelContainer.leftEdge || 0; const rightEdge = measurements.labelContainer.rightEdge || 0; let leftPosition = 0; if (tooltipWidth > labelWidth) { const labelCenter = (leftEdge + rightEdge) / 2; leftPosition = (labelCenter - tooltipWidth) / 2; if (leftPosition < 0) { leftPosition = 0; } else if (leftPosition + tooltipWidth > screenWidth) { leftPosition = screenWidth - tooltipWidth; } } else { leftPosition = labelWidth / 2 - tooltipWidth / 2; if (leftPosition < 0) { leftPosition = 0; } else if (leftPosition + tooltipWidth > screenWidth) { leftPosition = screenWidth - tooltipWidth; } } return { top: measurements.labelContainer.height + 10, left: leftPosition, }; }; export const getTooltipAutoPositionStyle = ({ measurements }: { measurements: IMeasurements }) => { const { labelContainer, overlayContainer } = measurements; const screenWidth = getDeviceWidth(); const screenHeight = getDeviceHeight(); // Ensure labelContainer and overlayContainer are defined if (labelContainer && overlayContainer) { const tooltipHeight = overlayContainer.height ?? 0; const tooltipWidth = overlayContainer.width ?? 0; const labelHeight = labelContainer.height ?? 0; const labelWidth = labelContainer.width ?? 0; const labelLeftEdge = labelContainer.leftEdge ?? 0; const labelRightEdge = labelContainer.rightEdge ?? 0; // Calculate available space in each direction const spaceAbove = labelContainer.y; const spaceBelow = screenHeight - (labelContainer.y + labelHeight); const spaceLeft = labelLeftEdge; const spaceRight = screenWidth - labelRightEdge; // Determine best position based on available space if (spaceBelow >= tooltipHeight + 10) { return getOverlayBottomPosition({ measurements }); } else if (spaceAbove >= tooltipHeight + 10) { return getOverlayTopPosition({ measurements }); } else if (spaceRight >= tooltipWidth + 10) { return { left: labelWidth + 10, top: -(tooltipHeight / 2 - labelHeight / 2), }; } else if (spaceLeft >= tooltipWidth + 10) { return { left: -(tooltipWidth + 10), top: -(tooltipHeight / 2 - labelHeight / 2), }; } // Default to bottom if no space is available return getOverlayBottomPosition({ measurements }); } return { left: 0, top: 0 }; }; export const getOverlayPositionStyles = ({ position, measurements, }: { position: positionType; measurements: IMeasurements; }) => { const labelHeight = measurements.labelContainer.height; const labelWidth = measurements.labelContainer.width; const tooltipHeight = measurements.overlayContainer.height; const tooltipWidth = measurements.overlayContainer.width; switch (position) { case positions.TOP: { return { ...getOverlayTopPosition({ measurements }), }; } case positions.RIGHT: { return { left: labelWidth + 10, top: -(tooltipHeight / 2 - labelHeight / 2), }; } case positions.BOTTOM: { return { ...getOverlayBottomPosition({ measurements }), }; } case positions.LEFT: { return { left: -(tooltipWidth + 10), top: -(tooltipHeight / 2 - labelHeight / 2), }; } case positions.AUTO: { return { ...getTooltipAutoPositionStyle({ measurements }) }; } default: { return { left: 0, }; } } }; export const getOverlayBorderStyles = ({ theme, borderRadius, }: { theme: Theme; borderRadius: `${overlayBorderRadius}`; }) => { switch (borderRadius) { 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 getTooltipOverlayStyles = ({ theme, position, borderRadius, measurements, }: { theme: Theme; overlayWidth: ViewStyle['width']; position: `${positionType}`; borderRadius: `${overlayBorderRadius}`; measurements: IMeasurements; }) => { return { ...getOverlayPositionStyles({ position, measurements }), ...getOverlayBorderStyles({ theme, borderRadius }), }; }; // ---------- ARROW STYLES ----------------- // const getTooltipArrowPosition = ({ position, measurements, }: { position: `${positionType}`; measurements: IMeasurements; }) => { const labelWidth = measurements?.labelContainer.width || 0; const labelHeight = measurements?.labelContainer.height || 0; switch (position) { case positions.TOP: { return { top: -11, left: labelWidth / 2, }; } case positions.BOTTOM: { return { transform: `rotate(180deg)`, top: labelHeight + 5, left: labelWidth / 2, }; } case positions.LEFT: { return { transform: `rotate(-90deg)`, top: labelHeight - 15, left: -16, }; } case positions.RIGHT: { return { transform: `rotate(90deg)`, top: labelHeight - 15, left: labelWidth - 1, }; } case positions.AUTO: { return { ...getTooltipArrowAutoPositionStyle({ measurements }), }; } } }; export const getTooltipArrowStyles = ({ position, measurements, }: { position: `${positionType}`; measurements: IMeasurements; }) => { return { ...getTooltipArrowPosition({ position, measurements }), }; };