@redocly/theme
Version:
Shared UI components lib
74 lines (58 loc) • 2.21 kB
text/typescript
import { useLayoutEffect, useRef, useState } from 'react';
import type { RefObject } from 'react';
import type { TooltipPlacement, TooltipProps } from '@redocly/theme/core/types';
type TooltipFallbackPlacementParams = {
isOpened: boolean;
placement: TooltipPlacement;
arrowPosition: TooltipProps['arrowPosition'];
fallbackPlacements: TooltipPlacement[] | undefined;
tooltipBodyRef: RefObject<HTMLElement | null>;
};
type TooltipFallbackPlacementResult = {
activePlacement: TooltipPlacement;
activeArrowPosition: TooltipProps['arrowPosition'];
};
export function useTooltipFallbackPlacement({
isOpened,
placement,
arrowPosition,
fallbackPlacements,
tooltipBodyRef,
}: TooltipFallbackPlacementParams): TooltipFallbackPlacementResult {
const [activePlacement, setActivePlacement] = useState<TooltipPlacement>(placement);
const wasOpenRef = useRef(false);
const candidateIndexRef = useRef(0);
useLayoutEffect(() => {
if (!isOpened) {
wasOpenRef.current = false;
candidateIndexRef.current = 0;
return;
}
if (!wasOpenRef.current) {
wasOpenRef.current = true;
candidateIndexRef.current = 0;
if (activePlacement !== placement) {
setActivePlacement(placement);
return;
}
}
if (!tooltipBodyRef.current || !fallbackPlacements?.length) return;
const candidates: TooltipPlacement[] = [placement, ...fallbackPlacements];
if (candidateIndexRef.current >= candidates.length) return;
const rect = tooltipBodyRef.current.getBoundingClientRect();
const overflows =
rect.left < 0 ||
rect.top < 0 ||
rect.right > window.innerWidth ||
rect.bottom > window.innerHeight;
if (!overflows) return;
candidateIndexRef.current++;
if (candidateIndexRef.current < candidates.length) {
setActivePlacement(candidates[candidateIndexRef.current]);
} else if (activePlacement !== placement) {
setActivePlacement(placement);
}
}, [isOpened, activePlacement, placement, fallbackPlacements, tooltipBodyRef]);
const activeArrowPosition = activePlacement === placement ? arrowPosition : 'center';
return { activePlacement, activeArrowPosition };
}