'use client';
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { createContext, useCallback, useContext, useEffect, useRef, useState, } from 'react';
import { Tooltip } from '@base-ui/react/tooltip';
import { useMergedRefs } from '@base-ui/utils/useMergedRefs';
import { twToPx } from '@graphprotocol/gds-css';
import { useControlled, useCSSPropsPolyfill, useGDS } from "../hooks/index.js";
import { cn, getCSSPropsAttributes, getReactNodeKey } from "../utils/index.js";
import { Portal } from "./base/Portal.js";
import { Render } from "./base/Render.js";
import { TooltipMeta } from "./Tooltip.meta.js";
const TooltipContext = createContext(null);
const OPEN_DELAY = 300;
export function TooltipRoot({ ref: passedRef, content: passedContent, side, gap, align, alignOffset, open: controlledOpen, onOpenChange, disabled, overrideDescendants, className, style, children, triggerProps: passedTriggerProps, ...passedProps }) {
    const { dirProps } = useGDS();
    const ancestorTooltip = useContext(TooltipContext);
    const popupProps = passedTriggerProps !== 'forward' ? passedProps : {};
    const popupRef = passedTriggerProps !== 'forward' ? passedRef : undefined;
    const triggerProps = passedTriggerProps === 'forward' ? passedProps : passedTriggerProps;
    const triggerDisabled = triggerProps && 'disabled' in triggerProps ? triggerProps.disabled === true : false;
    const [cssPropsPolyfillRef, cssPropsPolyfillAttributes, cssProps] = useCSSPropsPolyfill(TooltipMeta, { side, align, disabled, overrideDescendants }, { returnPropValues: { side, gap, align, alignOffset, disabled, overrideDescendants } });
    const cssPropsAttributes = getCSSPropsAttributes(TooltipMeta, { side, gap, align, alignOffset, disabled, overrideDescendants }, style);
    const [setCollectedContent, collectedContent] = useDeferredContent();
    const content = passedContent !== undefined ? passedContent : collectedContent;
    /**
     * Propagate collected content up till it reaches `Tooltip.Collector`'s closest non-disabled
     * tooltip ancestor with no passed content.
     */
    const collectContent = (newCollectedContent, priority) => {
        if (passedContent === undefined || cssProps.disabled) {
            void setCollectedContent(newCollectedContent, priority);
            ancestorTooltip?.collectContent(newCollectedContent, priority);
        }
    };
    const disabledByAncestor = ancestorTooltip?.disableDescendants ?? false;
    const disableDescendants = disabledByAncestor ||
        (content !== undefined && !cssProps.disabled && cssProps.overrideDescendants);
    const enabled = Boolean(content) &&
        !cssProps.disabled &&
        (!triggerDisabled || controlledOpen) &&
        !disabledByAncestor;
    const triggerRef = useRef(null);
    const triggerPassedRef = useMergedRefs(
    /**
     * Conditionally setting the ref to force the tooltip to refresh its position when `enabled`
     * changes, preventing it from moving to the top left corner of the viewport in some cases (e.g.
     * when `CopyButton` returns to its default tooltip).
     */
    enabled ? triggerRef : undefined, (passedTriggerProps === 'forward' ? passedRef : passedTriggerProps?.ref));
    const [open, privateSetOpen] = useControlled(controlledOpen, false, onOpenChange);
    const justOpenedRef = useRef(false);
    const setOpen = useCallback((newOpen, reason) => {
        privateSetOpen(newOpen, reason);
        if (newOpen) {
            justOpenedRef.current = true;
            window.setTimeout(() => {
                justOpenedRef.current = false;
            }, 0);
        }
    }, [privateSetOpen]);
    useEffect(() => {
        if (!enabled) {
            if (open)
                setOpen(false, 'disabled');
        }
    }, [enabled, open, setOpen]);
    return (_jsx(TooltipContext.Provider, { value: {
            collectContent,
            disableDescendants,
        }, children: _jsxs(Tooltip.Root, { disabled: !enabled, disableHoverablePopup: true, open: open, onOpenChange: (newOpen, eventDetails) => {
                if (!enabled)
                    return;
                /**
                 * Disable Base UI's default behavior of closing the tooltip when pressing the trigger.
                 * This solution is not perfect (see https://github.com/mui/base-ui/issues/4113), but
                 * trying to fix all edge cases requires some overengineering.
                 */
                if (!newOpen && eventDetails.reason === 'trigger-press')
                    return;
                // Make `overrideDescendants={false}` work as expected (see the `NestedSimultaneous` story)
                if (!newOpen &&
                    eventDetails.reason === 'none' &&
                    (justOpenedRef.current ||
                        (!cssProps.overrideDescendants && triggerRef.current?.matches(':hover')))) {
                    return;
                }
                setOpen(newOpen, eventDetails.reason);
            }, children: [_jsx(Tooltip.Trigger, { delay: OPEN_DELAY, closeDelay: 100, ...triggerProps, ref: triggerPassedRef, disabled: undefined, render: (renderProps) => (_jsx(Render, { render: children, ...renderProps, disabled: triggerDisabled })) }), _jsx(Portal, { children: _jsx("span", { ref: cssPropsPolyfillRef, className: cn('gds-tooltip invisible! block!', className), ...cssPropsAttributes, ...cssPropsPolyfillAttributes, ...dirProps, ...popupProps, hidden: undefined }) }), _jsx(Tooltip.Portal, { children: _jsx(Tooltip.Positioner, { side: cssProps.side === 'start'
                            ? 'inline-start'
                            : cssProps.side === 'end'
                                ? 'inline-end'
                                : cssProps.side, sideOffset: twToPx(cssProps.gap), align: cssProps.align, alignOffset: twToPx(cssProps.alignOffset), collisionPadding: 4, ...dirProps, className: "i:data-anchor-hidden:hidden" // Prevent moving to the top left corner when the trigger is hidden or unmounted
                        , children: _jsx(Tooltip.Popup, { ref: popupRef, className: cn(`gds-tooltip root-block u:max-w-(--available-width) u:rounded-4 u:bg-default u:px-2 u:py-1 u:text-12 u:transition
                i:origin-(--transform-origin)
                i:data-ending-style:opacity-0
                i:data-starting-style:opacity-0
                i:data-starting-style:data-[side=bottom]:-translate-y-1
                i:data-starting-style:data-[side=inline-end]:-translate-x-1
                i:data-starting-style:data-[side=inline-start]:translate-x-1
                i:data-starting-style:data-[side=top]:translate-y-1
                i:rtl:data-starting-style:data-[side=end]:translate-x-1
                i:rtl:data-starting-style:data-[side=start]:-translate-x-1`, className), ...cssPropsAttributes, ...cssPropsPolyfillAttributes, ...popupProps, children: content }) }, getReactNodeKey(content)) })] }) }));
}
function useDeferredContent() {
    const [content, setContent] = useState(undefined);
    const pendingContentRef = useRef(undefined);
    const pendingPriorityRef = useRef(undefined);
    // Store the content in a ref to avoid updating the state during render
    const setDeferredContent = (newContent, priority = 0) => {
        // If we've already stored content this render, only replace it if the new content has higher priority
        if (pendingContentRef.current === undefined ||
            priority >= (pendingPriorityRef.current ?? -Infinity)) {
            pendingContentRef.current = newContent;
            pendingPriorityRef.current = priority;
        }
        return pendingContentRef.current;
    };
    // Commit the stored content after render
    // oxlint-disable-next-line react-hooks/exhaustive-deps -- Must run after every render to sync
    useEffect(() => {
        if (getReactNodeKey(content) !== getReactNodeKey(pendingContentRef.current)) {
            setContent(pendingContentRef.current);
        }
        // Reset for next render
        pendingContentRef.current = undefined;
        pendingPriorityRef.current = undefined;
    });
    return [setDeferredContent, content];
}
/**
 * Wrap any text or `ReactNode` with this component to use it as the default `content` of the
 * closest `Tooltip`, in addition to rendering it inline. It is meant to be wrapped in a `sr-only`
 * element, so that the content rendered inline is visually hidden but accessible to screen readers,
 * unlike the content rendered in the tooltip itself, which is hidden from the accessibility tree
 * since most tooltips are decorative. This allows a tooltip to be non-decorative, in the rare case
 * where the information it provides is not otherwise available to screen readers (i.e. different
 * from the trigger's accessible name).
 */
export function TooltipContent({ children, priority, render }) {
    const ancestorTooltip = useContext(TooltipContext);
    ancestorTooltip?.collectContent(children || null, priority);
    return render ? _jsx(Render, { render: render, children: children }) : children;
}
TooltipContent.displayName = 'Tooltip.Content';
const TooltipCollectorContext = createContext(null);
export function TooltipCollector({ children }) {
    const ancestorTooltip = useContext(TooltipContext);
    return (_jsx(TooltipCollectorContext.Provider, { value: ancestorTooltip?.collectContent ?? null, children: children }));
}
TooltipCollector.displayName = 'Tooltip.Collector';
/** Hook for components to send tooltip content up to a collector. */
export function useTooltipCollector() {
    return useContext(TooltipCollectorContext);
}
//# sourceMappingURL=Tooltip.parts.js.map