/* * Copyright (c) 2021 - 2024, Ludvig Lundgren and the autobrr contributors. * SPDX-License-Identifier: GPL-2.0-or-later */ import React, { useState, useCallback, useEffect } from 'react'; import type { ReactNode } from 'react'; import { Transition } from "@headlessui/react"; import { usePopperTooltip } from "react-popper-tooltip"; import { Placement } from '@popperjs/core'; import { classNames } from "@utils"; interface TooltipProps { label: ReactNode; title?: ReactNode; maxWidth?: string; requiresClick?: boolean; children: ReactNode; } // NOTE(stacksmash76): onClick is not propagated // to the label (always-visible) component, so you will have // to use the `onLabelClick` prop in this case. export const Tooltip = ({ label, title, children, requiresClick, maxWidth = "max-w-sm" }: TooltipProps) => { const [isTooltipVisible, setIsTooltipVisible] = useState(false); const [tooltipNode, setTooltipNode] = useState(null); const [triggerNode, setTriggerNode] = useState(null); // default tooltip placement to right const [placement, setPlacement] = useState('right'); // check screen size and update placement if needed useEffect(() => { const updatePlacementForScreenSize = () => { const screenWidth = window.innerWidth; if (screenWidth < 640) { // tailwind's sm breakpoint setPlacement('top'); } else { setPlacement('right'); } }; updatePlacementForScreenSize(); window.addEventListener('resize', updatePlacementForScreenSize); return () => { window.removeEventListener('resize', updatePlacementForScreenSize); }; }, []); const { getTooltipProps, setTooltipRef: popperSetTooltipRef, setTriggerRef: popperSetTriggerRef, visible } = usePopperTooltip({ trigger: requiresClick ? 'click' : ['click', 'hover'], interactive: true, delayHide: 200, placement, followCursor: placement === "right" }); const handleClick = (e: React.MouseEvent) => { e.preventDefault(); setIsTooltipVisible(!isTooltipVisible); }; const handleTouch = (e: React.TouchEvent) => { e.preventDefault(); setIsTooltipVisible(!isTooltipVisible); }; const setTooltipRef = (node: HTMLDivElement | null) => { popperSetTooltipRef(node); setTooltipNode(node); }; const setTriggerRef = (node: HTMLDivElement | null) => { popperSetTriggerRef(node); setTriggerNode(node); }; const handleClickOutside = useCallback((event: MouseEvent | TouchEvent) => { if (tooltipNode && !tooltipNode.contains(event.target as Node) && triggerNode && !triggerNode.contains(event.target as Node)) { setIsTooltipVisible(false); } }, [tooltipNode, triggerNode]); useEffect(() => { document.addEventListener('touchstart', handleClickOutside as EventListener, true); document.addEventListener('mousedown', handleClickOutside as EventListener, true); return () => { document.removeEventListener('touchstart', handleClickOutside as EventListener, true); document.removeEventListener('mousedown', handleClickOutside as EventListener, true); }; }, [handleClickOutside]); return ( <>
{label}
e.stopPropagation() })} > {title ? (
{title}
) : null}
{children}
); };