// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React, { useCallback, useEffect, useState } from 'react';
import ReactDOM from 'react-dom';
import { useLogger } from '../../../providers/LoggerProvider';
import { StyledTooltip } from './Styled';
export type ToolTipPositionType = 'top' | 'bottom' | 'right' | 'left';
export interface Tooltipable {
/* Put tooltips on any buttons */
['data-tooltip']?: boolean;
/* Tooltips location on any buttons */
['data-tooltip-position']?: ToolTipPositionType;
/* The element ID of an existing element to render tooltips into */
tooltipContainerId?: string;
/* Content for tooltip. Falls back to the label */
tooltipContent?: React.ReactNode;
}
export interface ToolTipProps {
label?: string;
tooltipContent?: React.ReactNode;
tooltipPosition?: ToolTipPositionType;
}
interface WithTooltipState {
bounds: DOMRect | null;
show: boolean;
}
const initialState: WithTooltipState = { show: false, bounds: null };
export const WithTooltip =
(
Component: React.ComponentType>,
container_id?: string
) =>
(props: P & ToolTipProps) => {
const logger = useLogger();
const [{ show, bounds }, setShow] =
useState(initialState);
const [container, setContainer] = useState(null);
const position = props.tooltipPosition ?? 'top';
const showToolTip = useCallback((e: Event) => {
e.preventDefault();
e.stopPropagation();
let component = e.target as Element;
while (!component.getAttribute('data-tooltip'))
component = component.parentElement as Element;
const bounds = component.getBoundingClientRect();
setShow({ show: true, bounds });
}, []);
const hideToolTip = useCallback((e: Event) => {
e.preventDefault();
e.stopPropagation();
setShow(initialState);
}, []);
useEffect(() => {
document.addEventListener('scroll', hideToolTip, true);
return () => document.removeEventListener('scroll', hideToolTip, true);
}, []);
useEffect(() => {
const container = document.getElementById(
container_id || 'Tooltip__container'
);
if (!container) {
logger.warn(
`Attempted to use 'WithTooltip' but could not find container element.Pass a valid element ID or add 'Tooltip__container' ID to existing element`
);
return;
}
setContainer(container);
}, [container_id]);
return (
<>
{show &&
bounds &&
container &&
ReactDOM.createPortal(
{props.tooltipContent || props.label}
,
container
)}
{
if (show) hideToolTip(e);
(props as any)?.onClick?.(e);
}}
onFocus={(e: FocusEvent) => {
if (!show) showToolTip(e);
(props as any)?.onFocus?.(e);
}}
onBlur={(e: FocusEvent) => {
if (show) hideToolTip(e);
(props as any)?.onBlur?.(e);
}}
onMouseEnter={(e: MouseEvent) => {
if (!show) showToolTip(e);
(props as any)?.onMouseEnter?.(e);
}}
onMouseLeave={(e: MouseEvent) => {
if (show) hideToolTip(e);
(props as any)?.onMouseLeave?.(e);
}}
/>
>
);
};