/**
 * Stage 4.2 port of Base UI React's `useClick` hook.
 *
 * React source:
 *   `base-ui/packages/react/src/floating-ui-react/hooks/useClick.ts`
 *
 * Opens or closes the floating element when the reference element is
 * clicked. Supports `event: 'click' | 'mousedown' | 'mousedown-only'`,
 * `toggle`, `ignoreMouse`, `stickIfOpen`, `touchOpenDelay`, and a custom
 * `reason`.
 *
 * ### React → Solid deviations
 *
 * 1. React `useRef` for `pointerTypeRef` → a plain closure `let`. Solid
 *    component bodies don't re-run, so mutables live as variables.
 *
 * 2. React memoises `reference` via `useMemo` keyed on all option deps.
 *    Solid reads the options directly at event time off the captured
 *    props, so the returned object is a plain literal. Callers that
 *    want option reactivity can pass accessors (see `touchOpenDelay`
 *    resolution below) — but the React source also treated these as
 *    simple values, so the default Solid shape is 1:1.
 *
 * 3. `useAnimationFrame` and `useTimeout` are already ported (shared
 *    internals). They provide the same `request()` / `cancel()` and
 *    `start()` / `clear()` handles.
 *
 * 4. The reason default (`REASONS.triggerPress`) stays identical.
 */
import { untrack } from "solid-js";
import { createChangeEventDetails, REASONS } from "../../change-event-details.jsx";
import { readOption } from "../../maybe-accessor.jsx";
import { useAnimationFrame } from "../../use-animation-frame.jsx";
import { useTimeout } from "../../use-timeout.jsx";
import { getTarget, isTypeableElement } from "../utils/element.jsx";
import { isClickLikeEvent, isMouseLikePointerType } from "../utils/event.jsx";
const EMPTY_OBJECT = Object.freeze({});

/**
 * Opens or closes the floating element when clicking the reference element.
 * @see https://floating-ui.com/docs/useClick
 */
export function useClick(context, props = {}) {
  const store = "rootStore" in context ? context.rootStore : context;
  const dataRef = store.context.dataRef;

  // Untrack the body-time destructure (see use-dismiss for the full
  // reasoning). These fields are not `MaybeAccessor` shapes.
  const {
    event: eventOption = "click",
    ignoreMouse = false,
    stickIfOpen = true,
    touchOpenDelay = 0,
    reason = REASONS.triggerPress
  } = untrack(() => ({
    event: props.event,
    ignoreMouse: props.ignoreMouse,
    stickIfOpen: props.stickIfOpen,
    touchOpenDelay: props.touchOpenDelay,
    reason: props.reason
  }));

  // `enabled` + `toggle` are read lazily so reactive accessor callers
  // observe live changes without re-mounting (solidify.md P3).
  const getEnabled = () => props.enabled === undefined ? true : readOption(props.enabled);
  const getToggle = () => props.toggle === undefined ? true : readOption(props.toggle);
  let pointerType;
  const frame = useAnimationFrame();
  const touchOpenTimeout = useTimeout();
  const reference = {
    onPointerDown(event) {
      if (!getEnabled()) return;
      pointerType = event.pointerType;
    },
    onMouseDown(event) {
      if (!getEnabled()) return;
      const nativeEvent = event;
      const open = store.state.open;

      // Ignore all buttons except for the "main" button.
      // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
      if (event.button !== 0 || eventOption === "click" || isMouseLikePointerType(pointerType, true) && ignoreMouse) {
        return;
      }
      const openEvent = dataRef.current.openEvent;
      const openEventType = openEvent?.type;
      const hasClickedOnInactiveTrigger = store.state.domReferenceElement !== event.currentTarget;
      const toggle = getToggle();
      const nextOpen = open && hasClickedOnInactiveTrigger || !(open && toggle && (openEvent && stickIfOpen ? openEventType === "click" || openEventType === "mousedown" : true));

      // Animations sometimes won't run on a typeable element if using
      // a rAF. Focus is always set on these elements. For touch, we
      // may delay opening.
      const target = getTarget(nativeEvent);
      if (isTypeableElement(target)) {
        const details = createChangeEventDetails(reason, nativeEvent, target);
        if (nextOpen && pointerType === "touch" && touchOpenDelay > 0) {
          touchOpenTimeout.start(touchOpenDelay, () => {
            store.setOpen(true, details);
          });
        } else {
          store.setOpen(nextOpen, details);
        }
        return;
      }

      // Capture the currentTarget before the rAF. React sets it to
      // null after the event handler completes; happy-dom / Solid do
      // the same, so keep the snapshot.
      const eventCurrentTarget = event.currentTarget;

      // Wait until focus is set on the element. This is an
      // alternative to `event.preventDefault()` to avoid
      // `:focus-visible` from appearing when using a pointer.
      frame.request(() => {
        const details = createChangeEventDetails(reason, nativeEvent, eventCurrentTarget);
        if (nextOpen && pointerType === "touch" && touchOpenDelay > 0) {
          touchOpenTimeout.start(touchOpenDelay, () => {
            store.setOpen(true, details);
          });
        } else {
          store.setOpen(nextOpen, details);
        }
      });
    },
    onClick(event) {
      if (!getEnabled()) return;
      if (eventOption === "mousedown-only") {
        return;
      }
      if (eventOption === "mousedown" && pointerType) {
        pointerType = undefined;
        return;
      }
      if (isMouseLikePointerType(pointerType, true) && ignoreMouse) {
        return;
      }
      const open = store.state.open;
      const openEvent = dataRef.current.openEvent;
      const hasClickedOnInactiveTrigger = store.state.domReferenceElement !== event.currentTarget;
      const toggle = getToggle();
      const nextOpen = open && hasClickedOnInactiveTrigger || !(open && toggle && (openEvent && stickIfOpen ? isClickLikeEvent(openEvent) : true));
      const details = createChangeEventDetails(reason, event, event.currentTarget);
      if (nextOpen && pointerType === "touch" && touchOpenDelay > 0) {
        touchOpenTimeout.start(touchOpenDelay, () => {
          store.setOpen(true, details);
        });
      } else {
        store.setOpen(nextOpen, details);
      }
    },
    onKeyDown() {
      if (!getEnabled()) return;
      pointerType = undefined;
    }
  };

  // When `enabled` is an accessor, always return the bag so attached
  // handlers can reactively short-circuit. When it's a literal `false`,
  // preserve the React-compatible empty-bag shape. The body-time
  // `props.enabled` access is wrapped in `untrack` so the strict-read
  // scope established by `devComponent(...)` does not warn — the
  // accessor branch goes via `getEnabled()` at event time inside
  // already-tracked handlers.
  const isStaticallyDisabled = untrack(() => typeof props.enabled !== "function" && !getEnabled());
  if (isStaticallyDisabled) {
    return EMPTY_OBJECT;
  }
  return {
    reference
  };
}
//# sourceMappingURL=use-click.jsx.map
