/** @license MIT | @cube-dev/ui-kit v0.127.0 | Cube Dev Team */
import { mergeProps as mergeProps$1 } from "../../utils/react/mergeProps.js";
import { useEventBus } from "../../utils/react/useEventBus.js";
import { useEvent } from "../../_internal/hooks/use-event.js";
import { generateRandomId } from "../../utils/random.js";
import { _MenuTrigger } from "./Menu/MenuTrigger.js";
import { useEffect, useMemo, useRef, useState } from "react";
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
import { VisuallyHidden } from "react-aria";
import { Pressable } from "@react-aria/interactions";

//#region src/components/actions/use-context-menu.tsx
/**
* Generic hook to manage a context menu component that opens at pointer coordinates.
*
* @param Component - A React component that represents the menu content (Menu or CommandMenu).
* @param defaultTriggerProps - Default props to pass to the MenuTrigger.
* @param defaultMenuProps - Default props to pass to the Menu component.
* @returns An object with `targetRef` to attach to the container element, `open` function to open the menu at event coordinates, `close` function to close the menu, and `rendered` JSX element to include in your component tree.
*/
function useContextMenu(Component, defaultTriggerProps, defaultMenuProps) {
	const [isOpen, setIsOpen] = useState(false);
	const [componentProps, setComponentProps] = useState(null);
	const [triggerProps, setTriggerProps] = useState(null);
	const [anchorPosition, setAnchorPosition] = useState(null);
	const targetRef = useRef(null);
	const invisibleAnchorRef = useRef(null);
	const setupRef = useRef(false);
	const menuId = useMemo(() => generateRandomId(), []);
	const { emit, on } = useEventBus();
	useEffect(() => {
		return on("popover:open", (data) => {
			if (data.menuId !== menuId && isOpen) {
				setIsOpen(false);
				setAnchorPosition(null);
			}
		});
	}, [
		on,
		menuId,
		isOpen
	]);
	useEffect(() => {
		if (isOpen) emit("popover:open", { menuId });
	}, [
		isOpen,
		emit,
		menuId
	]);
	function setupCheck() {
		if (!setupRef.current) throw new Error("useContextMenu: MenuTrigger must be rendered. Use `rendered` property to include it in your component tree.");
	}
	const calculatePosition = (event) => {
		const container = targetRef.current;
		if (!event) {
			if (!container) return {
				x: 0,
				y: 0
			};
			container.getBoundingClientRect();
			const scrollLeft = container.scrollLeft;
			const scrollTop = container.scrollTop;
			const computed = window.getComputedStyle(container);
			parseFloat(computed.borderLeftWidth);
			parseFloat(computed.borderTopWidth);
			const x = container.clientWidth / 2 + scrollLeft;
			const y = container.clientHeight / 2 + scrollTop;
			return {
				x: Math.max(0, Math.min(x, container.scrollWidth)),
				y: Math.max(0, Math.min(y, container.scrollHeight))
			};
		}
		if (!container) {
			const { clientX = 0, clientY = 0 } = event;
			return {
				x: clientX,
				y: clientY
			};
		}
		const containerRect = container.getBoundingClientRect();
		const { clientX, clientY } = event;
		const scrollLeft = container.scrollLeft;
		const scrollTop = container.scrollTop;
		const computed = window.getComputedStyle(container);
		const borderLeft = parseFloat(computed.borderLeftWidth) || 0;
		const borderTop = parseFloat(computed.borderTopWidth) || 0;
		const x = clientX - containerRect.left - borderLeft + scrollLeft;
		const y = clientY - containerRect.top - borderTop + scrollTop;
		return {
			x: Math.max(0, Math.min(x, container.scrollWidth)),
			y: Math.max(0, Math.min(y, container.scrollHeight))
		};
	};
	const open = useEvent((props = {}, triggerProps, event) => {
		setupCheck();
		if (targetRef.current) {
			if (window.getComputedStyle(targetRef.current).position === "static") targetRef.current.style.position = "relative";
		}
		if (event && "preventDefault" in event && typeof event.preventDefault === "function") event.preventDefault();
		const { x, y } = calculatePosition(event);
		setAnchorPosition({
			x,
			y
		});
		setComponentProps(defaultMenuProps ? {
			...defaultMenuProps,
			...props
		} : props);
		setTriggerProps(triggerProps ?? null);
		setIsOpen(true);
	});
	const update = useEvent((props, triggerProps) => {
		setupCheck();
		setComponentProps(defaultMenuProps ? {
			...defaultMenuProps,
			...props
		} : props);
		setTriggerProps(triggerProps ?? null);
	});
	const close = useEvent(() => {
		setIsOpen(false);
		setAnchorPosition(null);
	});
	const onContextMenu = useEvent((event) => {
		event.preventDefault();
		if (isOpen) setAnchorPosition(calculatePosition(event));
		else open(defaultMenuProps, void 0, event);
	});
	useEffect(() => {
		const element = targetRef.current;
		if (!element) return;
		element.addEventListener("contextmenu", onContextMenu);
		return () => {
			element.removeEventListener("contextmenu", onContextMenu);
		};
	}, [onContextMenu]);
	const renderedMenu = useMemo(() => {
		if (!componentProps || !anchorPosition) return null;
		return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
			ref: invisibleAnchorRef,
			style: {
				position: "absolute",
				left: `${anchorPosition.x}px`,
				top: `${anchorPosition.y}px`,
				width: "0px",
				height: "0px",
				lineHeight: "0",
				pointerEvents: "none",
				visibility: "hidden"
			}
		}), /* @__PURE__ */ jsxs(_MenuTrigger, {
			isDummy: true,
			isOpen,
			targetRef: invisibleAnchorRef,
			offset: 0,
			crossOffset: 0,
			placement: triggerProps?.placement || defaultTriggerProps?.placement || "bottom start",
			onOpenChange: setIsOpen,
			...mergeProps$1(defaultTriggerProps, triggerProps || void 0),
			children: [/* @__PURE__ */ jsx(VisuallyHidden, { children: /* @__PURE__ */ jsx(Pressable, { children: /* @__PURE__ */ jsx("button", { "aria-label": "Open context menu" }) }) }), /* @__PURE__ */ jsx(Component, { ...componentProps })]
		})] });
	}, [
		componentProps,
		triggerProps,
		isOpen,
		defaultTriggerProps,
		anchorPosition
	]);
	return {
		targetRef,
		open,
		update,
		close,
		isOpen,
		get rendered() {
			setupRef.current = true;
			return renderedMenu;
		}
	};
}

//#endregion
export { useContextMenu };
//# sourceMappingURL=use-context-menu.js.map