var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                t[p[i]] = s[p[i]];
        }
    return t;
};
import { autoUpdate, flip, arrow as floatingArrow, hide, limitShift, offset, shift, size, useFloating, } from "@floating-ui/react-dom";
import React, { forwardRef, useEffect, useRef, useState, } from "react";
import { useModalContext } from "../../../modal/Modal.context.js";
import { useClientLayoutEffect } from "../../../utils-external/index.js";
import { createStrictContext } from "../../helpers/index.js";
import { useEventCallback, useMergeRefs, useOpenChangeAnimationComplete, } from "../../hooks/index.js";
import { Slot } from "../slot/Slot.js";
import { getSideAndAlignFromPlacement, transformOrigin, } from "./Floating.utils.js";
/**
 * Used for menus that prefer top/bottom placements and
 * use height-var to limit their height.
 */
const MENU_COLLISION_AVOIDANCE = {
    fallbackAxisSide: "none",
};
/**
 * Used by regular popovers that usually aren't scrollable and are allowed to
 * freely flip to any axis of placement.
 */
const POPOVER_COLLISION_AVOIDANCE = {
    fallbackAxisSide: "end",
};
export const { Provider: FloatingProvider, useContext: useFloatingContext } = createStrictContext({
    name: "FloatingContext",
});
const Floating = ({ children }) => {
    const [anchor, setAnchor] = useState(null);
    return (React.createElement(FloatingProvider, { anchor: anchor, onAnchorChange: setAnchor }, children));
};
/**
 * `FloatingAnchor` provides an anchor for a Floating instance.
 * Allows anchoring to non-DOM nodes like a cursor position when used with `virtualRef`.
 */
const FloatingAnchor = forwardRef((_a, forwardedRef) => {
    var { virtualRef, asChild } = _a, rest = __rest(_a, ["virtualRef", "asChild"]);
    const context = useFloatingContext();
    const ref = useRef(null);
    const mergedRef = useMergeRefs(forwardedRef, ref);
    useEffect(() => {
        // Allows anchoring the floating to non-DOM nodes like a cursor position.
        // We replace `anchorRef` with a virtual ref in such cases.
        context.onAnchorChange(virtualRef || ref.current);
    });
    const Comp = asChild ? Slot : "div";
    return virtualRef ? null : React.createElement(Comp, Object.assign({ ref: mergedRef }, rest));
});
/**
 * Floating Arrow
 */
const OPPOSITE_SIDE = {
    top: "bottom",
    right: "left",
    bottom: "top",
    left: "right",
};
const FloatingArrow = ({ width, height, className }) => {
    const context = useFloatingContentContext();
    const side = OPPOSITE_SIDE[context.placedSide];
    return (React.createElement("span", { ref: context.onArrowChange, style: {
            position: "absolute",
            left: context.arrowX,
            top: context.arrowY,
            [side]: 0,
            transformOrigin: {
                top: "",
                right: "0 0",
                bottom: "center 0",
                left: "100% 0",
            }[context.placedSide],
            transform: {
                top: "translateY(100%)",
                right: "translateY(50%) rotate(90deg) translateX(-50%)",
                bottom: `rotate(180deg)`,
                left: "translateY(50%) rotate(-90deg) translateX(50%)",
            }[context.placedSide],
            visibility: context.hideArrow ? "hidden" : undefined,
        }, "aria-hidden": true },
        React.createElement("svg", { className: className, width: width, height: height, viewBox: "0 0 30 10", preserveAspectRatio: "none", style: { display: "block" } },
            React.createElement("polygon", { points: "0,0 30,0 15,10" }))));
};
const { Provider: FloatingContentProvider, useContext: useFloatingContentContext, } = createStrictContext({
    name: "FloatingContentContext",
});
const FloatingContent = forwardRef((_a, forwardedRef) => {
    var _b, _c, _d, _e, _f, _g;
    var { children, side = "bottom", sideOffset = 0, align = "center", alignOffset = 0, avoidCollisions = true, collisionBoundary = [], collisionPadding: collisionPaddingProp = 0, hideWhenDetached = false, updatePositionStrategy = "optimized", onPlaced, arrow: _arrow, fallbackPlacements, enabled = true, autoUpdateWhileMounted = true, fallbackAxisSideDirection = POPOVER_COLLISION_AVOIDANCE.fallbackAxisSide } = _a, contentProps = __rest(_a, ["children", "side", "sideOffset", "align", "alignOffset", "avoidCollisions", "collisionBoundary", "collisionPadding", "hideWhenDetached", "updatePositionStrategy", "onPlaced", "arrow", "fallbackPlacements", "enabled", "autoUpdateWhileMounted", "fallbackAxisSideDirection"]);
    const context = useFloatingContext();
    const modalContext = useModalContext(false);
    const arrowDefaults = Object.assign({ padding: 5, width: 0, height: 0 }, _arrow);
    const [arrow, setArrow] = useState(null);
    const arrowWidth = arrowDefaults.width;
    const arrowHeight = arrowDefaults.height;
    const desiredPlacement = (side +
        (align !== "center" ? "-" + align : ""));
    /**
     * Create a bias to the preferred side.
     * On iOS, when the mobile software keyboard opens, the input is exactly centered
     * in the viewport, but this can cause it to flip to the top undesirably.
     */
    const bias = 1;
    const biasTop = side === "bottom" ? bias : 0;
    const biasBottom = side === "top" ? bias : 0;
    const biasLeft = side === "right" ? bias : 0;
    const biasRight = side === "left" ? bias : 0;
    const collisionPadding = typeof collisionPaddingProp === "number"
        ? {
            top: collisionPaddingProp + biasTop,
            right: collisionPaddingProp + biasRight,
            bottom: collisionPaddingProp + biasBottom,
            left: collisionPaddingProp + biasLeft,
        }
        : {
            top: (collisionPaddingProp.top || 0) + biasTop,
            right: (collisionPaddingProp.right || 0) + biasRight,
            bottom: (collisionPaddingProp.bottom || 0) + biasBottom,
            left: (collisionPaddingProp.left || 0) + biasLeft,
        };
    const boundary = Array.isArray(collisionBoundary)
        ? collisionBoundary
        : [collisionBoundary];
    const hasExplicitBoundaries = boundary.length > 0;
    /* https://floating-ui.com/docs/detectOverflow#boundary */
    const detectOverflowOptions = {
        padding: collisionPadding,
        boundary: boundary.filter(isNotNull),
        // with `strategy: 'fixed'`, this is the only way to get it to respect boundaries
        altBoundary: hasExplicitBoundaries,
        fallbackAxisSideDirection,
        fallbackPlacements,
    };
    const { refs, floatingStyles, placement, isPositioned, middlewareData, elements: floatingElements, update, } = useFloating({
        open: enabled,
        // default to `fixed` strategy so users don't have to pick and we also avoid focus scroll issues
        strategy: "fixed",
        placement: desiredPlacement,
        whileElementsMounted: autoUpdateWhileMounted
            ? (...args) => {
                const cleanup = autoUpdate(...args, {
                    animationFrame: updatePositionStrategy === "always",
                });
                return cleanup;
            }
            : undefined,
        elements: {
            reference: context.anchor,
        },
        middleware: [
            offset({
                mainAxis: sideOffset + arrowHeight,
                alignmentAxis: alignOffset,
            }),
            avoidCollisions &&
                shift({
                    mainAxis: true,
                    crossAxis: false,
                    limiter: limitShift(),
                }),
            avoidCollisions &&
                flip(Object.assign(Object.assign({}, detectOverflowOptions), { padding: collisionPadding })),
            size(Object.assign(Object.assign({}, detectOverflowOptions), { apply: ({ elements, rects, availableWidth, availableHeight }) => {
                    const { width: anchorWidth, height: anchorHeight } = rects.reference;
                    const contentStyle = elements.floating.style;
                    /**
                     * Allows styling and animations based on the available space.
                     */
                    contentStyle.setProperty("--__axc-floating-available-width", `${availableWidth}px`);
                    contentStyle.setProperty("--__axc-floating-available-height", `${availableHeight}px`);
                    contentStyle.setProperty("--__axc-floating-anchor-width", `${anchorWidth}px`);
                    contentStyle.setProperty("--__axc-floating-anchor-height", `${anchorHeight}px`);
                } })),
            arrow &&
                floatingArrow({ element: arrow, padding: arrowDefaults.padding }),
            transformOrigin({ arrowWidth, arrowHeight }),
            hideWhenDetached &&
                hide(Object.assign({ strategy: "referenceHidden" }, detectOverflowOptions)),
        ],
    });
    useEffect(() => {
        if (autoUpdateWhileMounted || !enabled) {
            return;
        }
        if (floatingElements.reference && floatingElements.floating) {
            const cleanup = autoUpdate(floatingElements.reference, floatingElements.floating, update);
            return () => {
                cleanup();
            };
        }
    }, [autoUpdateWhileMounted, enabled, floatingElements, update]);
    useOpenChangeAnimationComplete({
        enabled: !!(modalContext === null || modalContext === void 0 ? void 0 : modalContext.modalRef),
        open: enabled,
        ref: modalContext === null || modalContext === void 0 ? void 0 : modalContext.modalRef,
        onComplete: update,
    });
    const [placedSide, placedAlign] = getSideAndAlignFromPlacement(placement);
    const handlePlaced = useEventCallback(onPlaced);
    useClientLayoutEffect(() => {
        isPositioned && (handlePlaced === null || handlePlaced === void 0 ? void 0 : handlePlaced());
    }, [isPositioned, handlePlaced]);
    const arrowX = (_b = middlewareData.arrow) === null || _b === void 0 ? void 0 : _b.x;
    const arrowY = (_c = middlewareData.arrow) === null || _c === void 0 ? void 0 : _c.y;
    const cannotCenterArrow = ((_d = middlewareData.arrow) === null || _d === void 0 ? void 0 : _d.centerOffset) !== 0;
    return (React.createElement("div", { ref: refs.setFloating, "data-aksel-floating-content-wrapper": "", style: Object.assign(Object.assign({}, floatingStyles), { transform: isPositioned
                ? floatingStyles.transform
                : "translate(0, -200%)", minWidth: "max-content", zIndex: "9999999", ["--__axc-floating-transform-origin"]: [
                (_e = middlewareData.transformOrigin) === null || _e === void 0 ? void 0 : _e.x,
                (_f = middlewareData.transformOrigin) === null || _f === void 0 ? void 0 : _f.y,
            ].join(" ") }), 
        // Floating UI uses the `dir` attribute on the reference/floating node for logical alignment.
        // This attribute is necessary for both portalled and inline calculations.
        dir: "ltr" },
        React.createElement(FloatingContentProvider, { placedSide: placedSide, onArrowChange: setArrow, arrowX: arrowX, arrowY: arrowY, hideArrow: cannotCenterArrow },
            React.createElement("div", Object.assign({ ref: forwardedRef, "data-side": placedSide, "data-align": placedAlign }, contentProps, { style: Object.assign(Object.assign({}, contentProps.style), { 
                    // if the FloatingContent hasn't been placed yet (not all measurements done)
                    // we prevent animations so that users's animation don't kick in too early referring wrong sides
                    animation: !isPositioned ? "none" : undefined, 
                    // hide the content if using the hide middleware and should be hidden
                    opacity: ((_g = middlewareData.hide) === null || _g === void 0 ? void 0 : _g.referenceHidden) ? 0 : undefined }) }),
                children,
                (_arrow === null || _arrow === void 0 ? void 0 : _arrow.height) && (_arrow === null || _arrow === void 0 ? void 0 : _arrow.width) && (React.createElement(FloatingArrow, { width: _arrow.width, height: _arrow.height, className: _arrow.className }))))));
});
/**
 * .filter(x => x !== null) does not narrow the type of the array enough.
 */
function isNotNull(value) {
    return value !== null;
}
Floating.Anchor = FloatingAnchor;
Floating.Content = FloatingContent;
export { Floating, MENU_COLLISION_AVOIDANCE, POPOVER_COLLISION_AVOIDANCE };
//# sourceMappingURL=Floating.js.map