/**
 * `<ContextMenu.Trigger>` — an area that opens the menu on right-click
 * (desktop) or long-press (touch). Solid 2 port of Base UI React's
 * `packages/react/src/context-menu/trigger/ContextMenuTrigger.tsx`.
 *
 * Renders a `<div>` (not a button) — the interaction is delegated via
 * native `contextmenu` and `touchstart`/`touchmove`/`touchend`/`touchcancel`
 * events. On contextmenu (or after a 500ms long-press on touch), writes
 * a virtual anchor into the `<ContextMenu.Root>` context and calls
 * `setOpen(true, …)` via the actionsRef supplied by `<Menu.Root>`.
 *
 * ### Port deviations
 *
 *  - React wraps the mouseup late-dismissal listener via
 *    `addEventListener` from `@base-ui/utils`. The Solid port uses
 *    `document.addEventListener` + manual `removeEventListener`, which
 *    is semantically identical (both pass options). The `{ once: true }`
 *    option is preserved so the listener auto-removes after the first
 *    mouseup.
 *  - React calls `stopEvent(event)` from `floating-ui-react/utils` on
 *    contextmenu. In Solid we inline the two calls (`preventDefault` +
 *    `stopPropagation`) for parity.
 *  - `findRootOwnerId` traversal on mouseup —
 *    detects mouseup events that originated inside a nested menu owned
 *    by this context-menu root so the outer open isn't cancelled.
 */

import { dynamic } from "@solidjs/web";
import { omit, onCleanup } from "solid-js";
import { createChangeEventDetails, REASONS } from "../../internals/change-event-details.jsx";
import { contains } from "../../internals/dom/contains.jsx";
import { getTarget } from "../../internals/dom/get-target.jsx";
import { ownerDocument } from "../../internals/dom/owner-document.jsx";
import { mergeRootProps } from "../../internals/merge-root-props.jsx";
import { resolveClassStyle } from "../../internals/resolve-class-style.jsx";
import { useTimeout } from "../../internals/use-timeout.jsx";
import { useMenuRootContext } from "../../menu/root/menu-root-context.jsx";
import { findRootOwnerId } from "../../menu/utils/find-root-owner-id.jsx";
import { useContextMenuRootContext } from "../root/context-menu-root-context.jsx";
const LONG_PRESS_DELAY = 500;
const LONG_PRESS_MOVE_THRESHOLD = 10;
export function ContextMenuTrigger(props) {
  const ctxMenu = useContextMenuRootContext();
  const menu = useMenuRootContext();
  let triggerElement = null;
  let touchPosition = null;
  let allowMouseUp = false;
  const longPressTimeout = useTimeout();
  const allowMouseUpTimeout = useTimeout();
  function openAtPoint(x, y, event) {
    const isTouchEvent = event.type.startsWith("touch");
    ctxMenu.initialCursorPointRef.current = {
      x,
      y
    };
    const anchor = {
      getBoundingClientRect() {
        const width = isTouchEvent ? 10 : 0;
        const height = isTouchEvent ? 10 : 0;
        if (typeof DOMRect !== "undefined") {
          return DOMRect.fromRect({
            width,
            height,
            x,
            y
          });
        }
        return {
          width,
          height,
          x,
          y,
          top: y,
          right: x + width,
          bottom: y + height,
          left: x,
          toJSON: () => ({})
        };
      }
    };
    ctxMenu.setAnchor(anchor);
    allowMouseUp = false;
    ctxMenu.actionsRef.current?.setOpen(true, createChangeEventDetails(REASONS.triggerPress, event));
    allowMouseUpTimeout.start(LONG_PRESS_DELAY, () => {
      allowMouseUp = true;
    });
  }
  function handleContextMenu(event) {
    if (menu.disabled()) {
      return;
    }
    ctxMenu.allowMouseUpTriggerRef.current = true;
    event.preventDefault();
    event.stopPropagation();
    openAtPoint(event.clientX, event.clientY, event);
    const doc = ownerDocument(triggerElement);
    const onMouseUp = mouseEvent => {
      // The `{ once: true }` listener auto-removes after firing; clear the
      // tracking refs so the component-level cleanup doesn't double-remove.
      activeMouseUpDoc = null;
      activeMouseUpHandler = null;
      ctxMenu.allowMouseUpTriggerRef.current = false;
      if (!allowMouseUp) {
        return;
      }
      allowMouseUpTimeout.clear();
      allowMouseUp = false;
      const mouseUpTarget = getTarget(mouseEvent);
      if (contains(ctxMenu.positionerRef.current, mouseUpTarget)) {
        return;
      }

      // Nested-menu mouseup detection. If the mouseup
      // landed inside a nested menu popup whose `data-rootownerid`
      // chain traces back to this context-menu root, treat it as
      // "inside" — don't cancel the open.
      if (ctxMenu.rootId && mouseUpTarget && findRootOwnerId(mouseUpTarget) === ctxMenu.rootId) {
        return;
      }
      ctxMenu.actionsRef.current?.setOpen(false, createChangeEventDetails(REASONS.cancelOpen, mouseEvent));
    };
    // Remove any prior pending listener before registering a new one (e.g.
    // a second right-click before the first mouseup fired).
    if (activeMouseUpDoc && activeMouseUpHandler) {
      activeMouseUpDoc.removeEventListener("mouseup", activeMouseUpHandler);
    }
    activeMouseUpDoc = doc;
    activeMouseUpHandler = onMouseUp;
    doc.addEventListener("mouseup", onMouseUp, {
      once: true
    });
  }
  function handleTouchStart(event) {
    if (menu.disabled()) {
      return;
    }
    ctxMenu.allowMouseUpTriggerRef.current = false;
    if (event.touches.length === 1) {
      event.stopPropagation();
      const touch = event.touches[0];
      if (!touch) return;
      touchPosition = {
        x: touch.clientX,
        y: touch.clientY
      };
      longPressTimeout.start(LONG_PRESS_DELAY, () => {
        if (touchPosition) {
          openAtPoint(touchPosition.x, touchPosition.y, event);
        }
      });
    }
  }
  function handleTouchMove(event) {
    if (longPressTimeout.isStarted() && touchPosition && event.touches.length === 1) {
      const touch = event.touches[0];
      if (!touch) return;
      const deltaX = Math.abs(touch.clientX - touchPosition.x);
      const deltaY = Math.abs(touch.clientY - touchPosition.y);
      if (deltaX > LONG_PRESS_MOVE_THRESHOLD || deltaY > LONG_PRESS_MOVE_THRESHOLD) {
        longPressTimeout.clear();
      }
    }
  }
  function handleTouchEnd() {
    longPressTimeout.clear();
    touchPosition = null;
  }

  // Pending mouseup late-dismissal listener (registered in `handleContextMenu`).
  // Tracked at component scope so an unmount between right-click and mouseup
  // removes it — otherwise the `{ once: true }` listener leaks, pinning the
  // handler's closures.
  let activeMouseUpDoc = null;
  let activeMouseUpHandler = null;
  onCleanup(() => {
    if (activeMouseUpDoc && activeMouseUpHandler) {
      activeMouseUpDoc.removeEventListener("mouseup", activeMouseUpHandler);
      activeMouseUpDoc = null;
      activeMouseUpHandler = null;
    }
  });
  const setRef = el => {
    triggerElement = el;
  };
  const internalProps = {
    ref: setRef,
    onContextMenu: handleContextMenu,
    onTouchStart: handleTouchStart,
    onTouchMove: handleTouchMove,
    onTouchEnd: handleTouchEnd,
    onTouchCancel: handleTouchEnd,
    style: {
      "-webkit-touch-callout": "none"
    },
    get "data-popup-open"() {
      return menu.open() ? "" : undefined;
    },
    get "data-pressed"() {
      return menu.open() ? "" : undefined;
    }
  };
  const userStyling = resolveClassStyle(props, () => ({
    open: menu.open()
  }));
  const userProps = omit(props, "class", "style", "attrs", "as");
  const styledUserProps = mergeRootProps(userStyling, userProps);
  const merged = mergeRootProps(internalProps, styledUserProps);
  const Tag = dynamic(() => props.as ?? "div");
  return <Tag {...merged} />;
}
//# sourceMappingURL=context-menu-trigger.jsx.map
