"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PopoverNext = void 0;
const tslib_1 = require("tslib");
const jsx_runtime_1 = require("react/jsx-runtime");
/* !
 * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved.
 */
const react_1 = require("react");
const common_1 = require("../../common");
const Errors = tslib_1.__importStar(require("../../common/errors"));
const useValidateProps_1 = require("../../hooks/useValidateProps");
const popoverArrow_1 = require("../popover/popoverArrow");
const popoverProps_1 = require("../popover/popoverProps");
const floatingUtils_1 = require("./floatingUtils");
const popoverPopup_1 = require("./popoverPopup");
const popoverTarget_1 = require("./popoverTarget");
const usePopover_1 = require("./usePopover");
exports.PopoverNext = (0, react_1.forwardRef)((props, ref) => {
    const { animation = "scale", autoUpdateOptions, arrow = true, boundary = "clippingAncestors", canEscapeKeyClose = true, children, content, defaultIsOpen = false, disabled = false, hasBackdrop = false, hoverCloseDelay = 300, hoverOpenDelay = 150, interactionKind = popoverProps_1.PopoverInteractionKind.CLICK, isOpen, matchTargetWidth = false, middleware: middlewareOverrides, onClose, onInteraction, openOnTargetFocus = true, placement, positioningStrategy = "absolute", renderTarget, rootBoundary, shouldReturnFocusOnClose = true, targetProps, usePortal = true, } = props;
    (0, useValidateProps_1.useValidateProps)(() => {
        if (isOpen == null && onInteraction != null) {
            console.warn(Errors.POPOVER_WARN_UNCONTROLLED_ONINTERACTION);
        }
        if (hasBackdrop && !usePortal) {
            console.warn(Errors.POPOVER_WARN_HAS_BACKDROP_INLINE);
        }
        if (hasBackdrop && interactionKind !== popoverProps_1.PopoverInteractionKind.CLICK) {
            console.warn(Errors.POPOVER_HAS_BACKDROP_INTERACTION);
        }
        const childrenCount = react_1.Children.count(children);
        const hasRenderTargetProp = renderTarget !== undefined;
        const hasTargetPropsProp = targetProps !== undefined;
        if (childrenCount === 0 && !hasRenderTargetProp) {
            console.warn(Errors.POPOVER_REQUIRES_TARGET);
        }
        if (childrenCount > 1) {
            console.warn(Errors.POPOVER_WARN_TOO_MANY_CHILDREN);
        }
        if (childrenCount > 0 && hasRenderTargetProp) {
            console.warn(Errors.POPOVER_WARN_DOUBLE_TARGET);
        }
        if (hasRenderTargetProp && hasTargetPropsProp) {
            console.warn(Errors.POPOVER_WARN_TARGET_PROPS_WITH_RENDER_TARGET);
        }
    }, [
        arrow,
        children,
        hasBackdrop,
        interactionKind,
        isOpen,
        onInteraction,
        placement,
        renderTarget,
        targetProps,
        usePortal,
    ]);
    const [hasDarkParent, setHasDarkParent] = (0, react_1.useState)(false);
    const [isClosingViaEscapeKeypress, setIsClosingViaEscapeKeypress] = (0, react_1.useState)(false);
    const arrowRef = (0, react_1.useRef)(null);
    const cancelOpenTimeout = (0, react_1.useRef)(undefined);
    const isMouseInTargetOrPopover = (0, react_1.useRef)(false);
    const lostFocusOnSamePage = (0, react_1.useRef)(true);
    const targetRef = (0, react_1.useRef)(null);
    const timeoutIds = (0, react_1.useRef)([]);
    const isControlled = isOpen !== undefined;
    const isContentEmpty = content == null || common_1.Utils.isEmptyString(content);
    const computedIsOpen = disabled ? false : (isOpen ?? defaultIsOpen);
    const middleware = (0, react_1.useMemo)(() => {
        const defaultMiddleware = {
            ...(placement === undefined
                ? { autoPlacement: { boundary, rootBoundary } }
                : { flip: { boundary, rootBoundary } }),
            ...(arrow
                ? {
                    arrow: { element: arrowRef },
                    offset: { mainAxis: popoverArrow_1.POPOVER_ARROW_SVG_SIZE / 2 },
                }
                : {}),
            shift: { boundary, rootBoundary },
            size: matchTargetWidth
                ? {
                    apply({ rects, elements }) {
                        Object.assign(elements.floating.style, {
                            width: `${rects.reference.width}px`,
                        });
                    },
                }
                : undefined,
        };
        const mergedMiddleware = { ...defaultMiddleware, ...middlewareOverrides };
        return (0, floatingUtils_1.convertMiddlewareConfigToArray)(mergedMiddleware);
    }, [placement, boundary, rootBoundary, arrow, arrowRef, matchTargetWidth, middlewareOverrides]);
    const floatingData = (0, usePopover_1.usePopover)({
        autoUpdateOptions,
        canEscapeKeyClose,
        disabled,
        hasBackdrop,
        interactionKind,
        isControlled,
        isOpen: computedIsOpen,
        middleware,
        onOpenChange: (nextOpen, event) => {
            // Use setOpenState logic which handles both controlled and uncontrolled components
            setOpenState(nextOpen, event);
        },
        placement,
        positioningStrategy,
    });
    (0, react_1.useImperativeHandle)(ref, () => ({
        reposition: () => {
            floatingData.update();
        },
    }), [floatingData]);
    const popoverElement = floatingData.refs.floating.current;
    const isHoverInteractionKind = interactionKind === popoverProps_1.PopoverInteractionKind.HOVER ||
        interactionKind === popoverProps_1.PopoverInteractionKind.HOVER_TARGET_ONLY;
    const getPopoverElement = (0, react_1.useCallback)(() => {
        return popoverElement?.querySelector(`.${common_1.Classes.POPOVER}`);
    }, [popoverElement]);
    const isElementInPopover = (0, react_1.useCallback)((element) => {
        return getPopoverElement()?.contains(element) ?? false;
    }, [getPopoverElement]);
    const setTimeout = (0, react_1.useCallback)((callback, timeout) => {
        const handle = window.setTimeout(callback, timeout);
        timeoutIds.current.push(handle);
        return () => window.clearTimeout(handle);
    }, []);
    // a wrapper around setIsOpen that will call onInteraction instead when in controlled mode.
    // starts a timeout to delay changing the state if a non-zero duration is provided.
    const setOpenState = (0, react_1.useCallback)((isOpenState, event, timeout) => {
        // cancel any existing timeout because we have new state
        cancelOpenTimeout.current?.();
        if (timeout !== undefined && timeout > 0) {
            // Persist the react event since it will be used in a later macrotask.
            event?.persist();
            cancelOpenTimeout.current = setTimeout(() => {
                setOpenState(isOpenState, event);
            }, timeout);
        }
        else {
            if (isOpen == null) {
                // For uncontrolled popovers, update the usePopover state directly
                floatingData.setIsOpen(isOpenState);
            }
            else {
                onInteraction?.(isOpenState, event);
            }
            if (!isOpenState) {
                // non-null assertion because the only time `e` is undefined is when in controlled mode
                // or the rare special case in uncontrolled mode when the `disabled` flag is toggled true
                onClose?.(event);
                setIsClosingViaEscapeKeypress(isEscapeKeypressEvent(event?.nativeEvent));
            }
        }
    }, [floatingData, isOpen, onInteraction, onClose, setTimeout]);
    const handleTargetContextMenu = (0, react_1.useCallback)((event) => {
        // we assume that when someone prevents the default interaction on this event (a browser native context menu),
        // they are showing a custom context menu (as ContextMenu2 does); in this case, we should close this popover/tooltip
        if (event.defaultPrevented) {
            setOpenState(false, event);
        }
    }, [setOpenState]);
    const handleMouseLeave = (0, react_1.useCallback)((event) => {
        isMouseInTargetOrPopover.current = false;
        event.persist();
        setTimeout(() => {
            if (isMouseInTargetOrPopover.current) {
                return;
            }
            setOpenState(false, event, hoverCloseDelay);
        });
    }, [hoverCloseDelay, setOpenState, setTimeout]);
    const handleMouseEnter = (0, react_1.useCallback)((event) => {
        isMouseInTargetOrPopover.current = true;
        // if we're entering the popover, and the mode is set to be HOVER_TARGET_ONLY, we want to manually
        // trigger the mouse leave event, as hovering over the popover shouldn't count.
        if (!usePortal &&
            isElementInPopover(event.target) &&
            interactionKind === popoverProps_1.PopoverInteractionKind.HOVER_TARGET_ONLY &&
            !openOnTargetFocus) {
            handleMouseLeave(event);
        }
        else if (!disabled) {
            // only begin opening popover when it is enabled
            setOpenState(true, event, hoverOpenDelay);
        }
    }, [
        disabled,
        handleMouseLeave,
        hoverOpenDelay,
        interactionKind,
        isElementInPopover,
        openOnTargetFocus,
        setOpenState,
        usePortal,
    ]);
    const handleTargetFocus = (0, react_1.useCallback)((event) => {
        if (openOnTargetFocus && isHoverInteractionKind) {
            if (event.relatedTarget == null && !lostFocusOnSamePage.current) {
                // ignore this focus event -- the target was already focused but the page itself
                // lost focus (e.g. due to switching tabs).
                return;
            }
            handleMouseEnter(event);
        }
    }, [handleMouseEnter, isHoverInteractionKind, openOnTargetFocus]);
    const handleTargetBlur = (0, react_1.useCallback)((event) => {
        if (openOnTargetFocus && isHoverInteractionKind) {
            if (event.relatedTarget != null) {
                // if the next element to receive focus is within the popover, we'll want to leave the
                // popover open.
                if (event.relatedTarget !== popoverElement &&
                    !isElementInPopover(event.relatedTarget)) {
                    handleMouseLeave(event);
                }
            }
            else {
                handleMouseLeave(event);
            }
        }
        lostFocusOnSamePage.current = event.relatedTarget != null;
    }, [handleMouseLeave, isElementInPopover, isHoverInteractionKind, openOnTargetFocus, popoverElement]);
    const handlePopoverClick = (0, react_1.useCallback)((event) => {
        const eventTarget = event.target;
        const eventPopover = eventTarget.closest(`.${common_1.Classes.POPOVER}`);
        const isEventFromSelf = eventPopover === getPopoverElement();
        const isEventPopoverCapturing = eventPopover?.classList.contains(common_1.Classes.POPOVER_CAPTURING_DISMISS) ?? false;
        // an OVERRIDE inside a DISMISS does not dismiss, and a DISMISS inside an OVERRIDE will dismiss.
        const dismissElement = eventTarget.closest(`.${common_1.Classes.POPOVER_DISMISS}, .${common_1.Classes.POPOVER_DISMISS_OVERRIDE}`);
        const shouldDismiss = dismissElement?.classList.contains(common_1.Classes.POPOVER_DISMISS) ?? false;
        const isDisabled = eventTarget.closest(`:disabled, .${common_1.Classes.DISABLED}`) != null;
        if (shouldDismiss && !isDisabled && (!isEventPopoverCapturing || isEventFromSelf)) {
            setOpenState(false, event);
        }
    }, [getPopoverElement, setOpenState]);
    const handleOverlayClose = (0, react_1.useCallback)((event) => {
        if (targetRef.current == null || event === undefined) {
            return;
        }
        const nativeEvent = (event.nativeEvent ?? event);
        const eventTarget = (nativeEvent.composed ? nativeEvent.composedPath()[0] : nativeEvent.target);
        // if click was in target, target event listener will handle things, so don't close
        if (!common_1.Utils.elementIsOrContains(targetRef.current, eventTarget) ||
            event.nativeEvent instanceof KeyboardEvent) {
            setOpenState(false, event);
        }
    }, [setOpenState, targetRef]);
    const updateDarkParent = (0, react_1.useCallback)(() => {
        if (usePortal && floatingData.isOpen) {
            setHasDarkParent(targetRef.current?.closest(`.${common_1.Classes.DARK}`) != null);
        }
    }, [floatingData.isOpen, targetRef, usePortal]);
    (0, react_1.useEffect)(() => {
        updateDarkParent();
        if (isOpen != null && computedIsOpen !== floatingData.isOpen) {
            setOpenState(computedIsOpen);
        }
        else if (disabled && floatingData.isOpen && isOpen == null) {
            // special case: close an uncontrolled popover when disabled is set to true
            setOpenState(false);
        }
        // Warn about empty content when trying to open (needs to be in effect to access current state)
        if (isContentEmpty) {
            if (!disabled && computedIsOpen !== false && !common_1.Utils.isNodeEnv("production")) {
                console.warn(Errors.POPOVER_WARN_EMPTY_CONTENT);
            }
        }
    }, [computedIsOpen, disabled, floatingData, isContentEmpty, isOpen, setOpenState, updateDarkParent]);
    return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(popoverTarget_1.PopoverTarget, { floatingData: floatingData, handleMouseEnter: handleMouseEnter, handleMouseLeave: handleMouseLeave, handleTargetBlur: handleTargetBlur, handleTargetContextMenu: handleTargetContextMenu, handleTargetFocus: handleTargetFocus, isContentEmpty: isContentEmpty, isControlled: isControlled, isHoverInteractionKind: isHoverInteractionKind, openOnTargetFocus: openOnTargetFocus, ref: targetRef, ...props, children: children }), !isContentEmpty && ((0, jsx_runtime_1.jsx)(popoverPopup_1.PopoverPopup, { animation: animation, arrowRef: arrowRef, floatingData: floatingData, handleMouseEnter: handleMouseEnter, handleMouseLeave: handleMouseLeave, handleOverlayClose: handleOverlayClose, handlePopoverClick: handlePopoverClick, hasDarkParent: hasDarkParent, isClosingViaEscapeKeypress: isClosingViaEscapeKeypress, isHoverInteractionKind: isHoverInteractionKind, shouldReturnFocusOnClose: shouldReturnFocusOnClose, ...props }))] }));
});
exports.PopoverNext.displayName = `${common_1.DISPLAYNAME_PREFIX}.PopoverNext`;
function isEscapeKeypressEvent(e) {
    return e instanceof KeyboardEvent && e.key === "Escape";
}
//# sourceMappingURL=popoverNext.js.map