/**
 * `<Select.Positioner>` — anchors the popup to the trigger via floating-ui.
 * Mirrors `packages/react/src/select/positioner/SelectPositioner.tsx`.
 *
 * ### Port deviations
 *
 * - `alignItemWithTrigger` uses `position: fixed` on the positioner (no
 *   floating-ui transform) while Select.Popup writes viewport coordinates.
 *   Standard anchor placement uses the thin `useFloating` transform path.
 * - Uses low-level `useFloating` + `flip` / `shift` / `size` / `arrow`
 *   middleware directly, matching `<Popover.Positioner>`. The full
 *   `useAnchorPositioning` helper is available but not needed for this
 *   thin port.
 * - `InternalBackdrop` is rendered when `modal && open` to block outside
 *   pointer interaction.
 */

import { flip, offset, shift, size } from "@floating-ui/dom";
import { dynamic } from "@solidjs/web";
import { createEffect, createMemo, createSignal, omit, Show, untrack } from "solid-js";
import { composeRefs, userRef } from "../../internals/compose-refs.jsx";
import { CompositeList } from "../../internals/composite/composite-list.jsx";
import { FloatingNode, useFloatingNodeId } from "../../internals/floating-ui/components/floating-tree.jsx";
import { useFloating } from "../../internals/floating-ui/hooks/use-floating.jsx";
import { arrow } from "../../internals/floating-ui/middleware/arrow.jsx";
import { mergeRootProps } from "../../internals/merge-root-props.jsx";
import { InternalBackdrop } from "../../internals/popups/internal-backdrop.jsx";
import { resolveClassStyle } from "../../internals/resolve-class-style.jsx";
import { useSelectRootContext } from "../select-root-context.jsx";
import { SelectPositionerContext } from "./select-positioner-context.jsx";
function composePlacement(side, align) {
  const physicalSide = side === "inline-start" ? "left" : side === "inline-end" ? "right" : side;
  if (align === "center") return physicalSide;
  return `${physicalSide}-${align}`;
}
function parsePlacement(placement) {
  const [rawSide, rawAlign] = placement.split("-");
  return {
    side: rawSide,
    align: rawAlign ?? "center"
  };
}
export function SelectPositioner(props) {
  const root = useSelectRootContext();
  const nodeId = useFloatingNodeId();
  const side = () => props.side ?? "bottom";
  const align = () => props.align ?? "start";
  const sideOffset = () => props.sideOffset ?? 0;
  const alignOffset = () => props.alignOffset ?? 0;
  const arrowPadding = () => props.arrowPadding ?? 5;
  const collisionPadding = () => props.collisionPadding ?? 5;
  const strategy = () => props.positionMethod ?? "absolute";
  const arrowRef = {
    current: null
  };
  const floating = useFloating({
    rootContext: root.floatingRootContext,
    nodeId,
    get placement() {
      return composePlacement(side(), align());
    },
    get strategy() {
      return strategy();
    },
    get middleware() {
      return [offset({
        mainAxis: sideOffset(),
        alignmentAxis: alignOffset()
      }), flip({
        padding: collisionPadding()
      }), shift({
        padding: collisionPadding()
      }), size({
        padding: collisionPadding(),
        apply({
          rects,
          availableWidth,
          availableHeight,
          elements: els
        }) {
          const s = els.floating.style;
          s.setProperty("--anchor-width", `${rects.reference.width}px`);
          s.setProperty("--anchor-height", `${rects.reference.height}px`);
          s.setProperty("--available-width", `${availableWidth}px`);
          s.setProperty("--available-height", `${availableHeight}px`);
        }
      }), arrow({
        element: arrowRef.current,
        padding: arrowPadding(),
        offsetParent: "real"
      })];
    }
  });
  let positionerNode = null;
  const setPositionerElement = el => {
    positionerNode = el;
    // opacity:0 before first paint (ref runs during DOM construction); the
    // reveal effect can run after the first paint in Solid 2. See
    // popover-positioner.
    if (el) el.style.opacity = "0";
    floating.refs.setFloating(el);
  };
  const scrollUpArrowRef = {
    current: null
  };
  const scrollDownArrowRef = {
    current: null
  };

  // Trigger-aligned popup placement state. The controlled flag mirrors
  // React's `useState(alignItemWithTrigger)` — it lets the popup
  // auto-fallback to standard anchor placement when space is tight. When
  // `mounted` flips to false we resync with the prop for the next open.
  const [controlledAlignItemWithTrigger, setControlledAlignItemWithTrigger] = createSignal(props.alignItemWithTrigger ?? true, {
    ownedWrite: true
  });
  createEffect(() => ({
    mountedNow: root.mounted(),
    prop: props.alignItemWithTrigger ?? true
  }), ({
    mountedNow,
    prop
  }) => untrack(() => {
    if (!mountedNow && controlledAlignItemWithTrigger() !== prop) {
      setControlledAlignItemWithTrigger(prop);
    }
  }));
  const alignItemWithTriggerActive = createMemo(() => {
    return root.mounted() && controlledAlignItemWithTrigger() && root.openMethod() !== "touch";
  });

  // Solid 2 DOM spread misses the first `floating.x`/`y` frame —
  // imperatively mirror the transform so the popup doesn't flash at (0, 0).
  // Skip when trigger-aligned: popup geometry writes viewport coords and
  // React uses `position: fixed` only (no transform) in that mode.
  // Transform + reveal in ONE imperative write so the popup is never visible
  // at the stale (0,0) transform (the teleport). `opacity:0` until
  // `isPositioned`, matching Base UI's `useAnchorPositioning`. No transform in
  // trigger-aligned mode (popup writes viewport coords). See popover-positioner
  // for the full rationale.
  createEffect(() => {
    if (!root.mounted()) return null;
    return {
      transform: alignItemWithTriggerActive() ? null : `translate(${Math.round(floating.x)}px, ${Math.round(floating.y)}px)`,
      positioned: floating.isPositioned || !root.open()
    };
  }, data => {
    if (!data || !positionerNode) return;
    positionerNode.style.transform = data.transform ?? "";
    positionerNode.style.opacity = data.positioned ? "" : "0";
  });
  const computedSideAlign = createMemo(() => parsePlacement(floating.placement));
  const computedAlign = () => computedSideAlign().align;
  const [anchorHidden] = createSignal(false);

  // Reported `side` flips to 'none' when the popup is trigger-aligned —
  // the popup isn't anchored to one edge; it overlaps the trigger.
  const computedSide = () => alignItemWithTriggerActive() ? "none" : computedSideAlign().side;

  // `isPositioned` gates trigger-aligned geometry in Select.Popup — wait for
  // floating-ui's first successful compute while open, not the initial x/y of 0.
  const isPositioned = createMemo(() => {
    return root.mounted() && root.open() && floating.isPositioned;
  });
  const arrowStyles = createMemo(() => {
    const data = floating.middlewareData.arrow;
    if (!data) return {
      position: "absolute"
    };
    const styles = {
      position: "absolute"
    };
    if (data.x != null) styles.left = `${data.x}px`;
    if (data.y != null) styles.top = `${data.y}px`;
    return styles;
  });
  const arrowUncentered = createMemo(() => {
    const data = floating.middlewareData.arrow;
    return (data?.centerOffset ?? 0) !== 0;
  });
  const positionerContext = {
    side: computedSide,
    align: computedAlign,
    anchorHidden,
    arrowRef,
    arrowStyles,
    arrowUncentered,
    alignItemWithTriggerActive,
    setControlledAlignItemWithTrigger,
    isPositioned,
    scrollUpArrowRef,
    scrollDownArrowRef
  };
  const state = createMemo(() => ({
    open: root.open(),
    side: computedSide(),
    align: computedAlign(),
    anchorHidden: anchorHidden()
  }));
  createEffect(() => ({
    mountedNow: root.mounted(),
    openNow: root.open(),
    trigger: root.triggerElement()
  }), ({
    trigger
  }) => {
    if (trigger) {
      floating.refs.setReference(trigger);
    }
    floating.update();
  });

  // `transform` is written imperatively in the `createEffect` above
  // because Solid 2's DOM spread subscribes `get style()` late and misses
  // the first `floating.x` / `floating.y` frame.
  const positionerStyle = createMemo(() => {
    const openNow = root.open();
    const mountedNow = root.mounted();
    const base = {
      visibility: mountedNow ? undefined : "hidden",
      "pointer-events": openNow ? undefined : "none"
    };
    if (alignItemWithTriggerActive()) {
      return {
        ...base,
        position: "fixed"
      };
    }
    return {
      ...base,
      position: strategy(),
      top: "0",
      left: "0"
    };
  });
  const internal = {
    get hidden() {
      return !root.open();
    },
    // popupStateMapping projection.
    get "data-open"() {
      return state().open ? "" : undefined;
    },
    get "data-closed"() {
      return state().open ? undefined : "";
    },
    get "data-anchor-hidden"() {
      return state().anchorHidden ? "" : undefined;
    }
  };
  const internalStyle = {
    get style() {
      return positionerStyle();
    }
  };
  const userClassStyle = resolveClassStyle(props, () => state());
  const userProps = omit(props, "as", "class", "style", "attrs", "side", "align", "sideOffset", "alignOffset", "arrowPadding", "collisionPadding", "positionMethod", "alignItemWithTrigger");
  let merged = mergeRootProps(internalStyle, internal);
  merged = mergeRootProps(merged, userClassStyle);
  merged = mergeRootProps(merged, userProps);
  const Tag = dynamic(() => props.as ?? "div");
  return <FloatingNode id={nodeId}>
			<CompositeList elementsRef={root.listRef.current} labelsRef={root.labelsRef.current}>
				<SelectPositionerContext value={positionerContext}>
					<Show when={root.mounted()}>
						<Show when={root.modal() && root.open()}>
							<InternalBackdrop inert={!root.open()} cutout={root.triggerElement() ?? undefined} />
						</Show>
						<Tag {...merged} ref={composeRefs(setPositionerElement, userRef(props))} />
					</Show>
				</SelectPositionerContext>
			</CompositeList>
		</FloatingNode>;
}
//# sourceMappingURL=select-positioner.jsx.map
