import React, { forwardRef, useCallback, useEffect, useRef, useState } from 'react'; import { Pressable, View } from 'react-native'; import Animated, { Easing, useSharedValue, withTiming } from 'react-native-reanimated'; import { useNativeMergeStyles, useStyles } from '@cleartrip/ct-design-style-manager'; import { Container, ContainerRef } from '@cleartrip/ct-design-container'; import { Typography } from '@cleartrip/ct-design-typography'; import Conditional from '@cleartrip/ct-design-conditional-wrap'; import { TooltipChevronDown } from '@cleartrip/ct-design-icons'; import { getTooltipArrowStyles, getTooltipOverlayStyles, staticTooltipStyles } from './style'; import { IDimensionsProps, IMeasurements, ITooltipProps } from './type'; import { Theme } from '@cleartrip/ct-design-theme'; export const getDefaultTooltipStyles = (theme: Theme) => { return { backgroundColor: theme.color.tooltip.primaryBg, borderRadius: theme.border.radius[4], }; }; const initialDimensions: IDimensionsProps = { width: 0, height: 0, x: 0, y: 0, leftEdge: 0, rightEdge: 0, }; const measure = (ref: React.RefObject): Promise => { return new Promise((resolve) => { ref.current?.measure((x: number, y: number, width: number, height: number, pageX: number, pageY: number) => { if (width !== undefined && height !== undefined && pageX !== undefined && pageY !== undefined) { resolve({ width: Math.floor(width), height: Math.floor(height), x: Math.floor(x), y: Math.floor(y), leftEdge: pageX, rightEdge: pageX + Math.floor(width), }); } else { resolve(initialDimensions); } }); }); }; const Tooltip = forwardRef( ( { children, position = 'right', labelContainer, tooltipWidth = 150, tooltipBorderRadius = 'md', styleConfig, open = false, animate = true, svgPathProps, anchorRef, }, ref, ) => { const { root: customRootStyles = [], labelContainerStyles: customLabelContainerStyles = [], overlayStyles: customOverlayStyles = [], arrowStyles: customArrowStyles = [], } = styleConfig || {}; const [isHovered, setIsHovered] = useState(open); const labelContainerRef = useRef(null); const overlayRef = useRef(null); const fadeAnim = useSharedValue(0); const [measurements, setMeasurements] = useState({ labelContainer: initialDimensions, overlayContainer: initialDimensions, anchorRef: initialDimensions, }); const dynamicTooltipStyles = useStyles( (theme) => { return { overlayStyles: { opacity: isHovered ? 1 : 0, width: tooltipWidth, ...getTooltipOverlayStyles({ theme, borderRadius: tooltipBorderRadius, overlayWidth: tooltipWidth, position, measurements, }), }, }; }, [position, measurements, tooltipWidth, isHovered, tooltipBorderRadius], ); const dynamicTooltipArrowStyles = useStyles(() => { return { tooltipArrow: { opacity: isHovered ? 1 : 0, ...getTooltipArrowStyles({ position, measurements, }), }, }; }, [isHovered, position, measurements]); const mergedLabelContainerStyles = useNativeMergeStyles( [staticTooltipStyles?.labelContainerStyles, ...customLabelContainerStyles], [customLabelContainerStyles], ); const handleLayout = useCallback(() => { const calculatePosition = async () => { try { const labelContainer = await measure(labelContainerRef); const overlayContainer = await measure(overlayRef); /** * force type casting */ const wrapper = anchorRef ? await measure(anchorRef as React.RefObject) : initialDimensions; setMeasurements({ labelContainer, overlayContainer, anchorRef: wrapper, }); } catch (_error) { // Error handling without console.log } }; calculatePosition(); }, [anchorRef]); const handlePress = useCallback((isVisible: boolean) => setIsHovered(isVisible), []); useEffect(() => { if (isHovered) { if (animate) { fadeAnim.value = withTiming(1, { duration: 200, easing: Easing.inOut(Easing.ease), }); } else { fadeAnim.value = 1; } } else { if (animate) { fadeAnim.value = withTiming(0, { duration: 200, easing: Easing.inOut(Easing.ease), }); } else { fadeAnim.value = 0; } } }, [isHovered, fadeAnim, animate]); useEffect(() => { setIsHovered(open); }, [open]); return ( handlePress(true)} onPressOut={() => handlePress(false)} > {labelContainer} {!!children && ( <> ( {children} )} > {children} )} ); }, ); Tooltip.displayName = 'Tooltip'; export default Tooltip;