/* COPYRIGHT Esri - https://js.arcgis.com/5.1/LICENSE.txt */
import { isServer } from "lit";
import { computePosition, autoUpdate, shift, hide, flip, offset, autoPlacement, arrow, platform } from "@floating-ui/dom";
import { debounce } from "es-toolkit";
import { offsetParent } from "composed-offset-position";
import { D as DEBOUNCE } from "./runtime.js";
(function setUpFloatingUiForShadowDomPositioning() {
  if (!isServer) {
    const originalGetOffsetParent = platform.getOffsetParent;
    platform.getOffsetParent = (element) => originalGetOffsetParent(element, offsetParent);
  }
})();
function roundByDPR(value) {
  const dpr = window.devicePixelRatio || 1;
  return Math.round(value * dpr) / dpr;
}
const positionFloatingUI = (
  /* we export arrow function to allow us to spy on it during testing */
  (async (component, {
    arrowEl,
    direction,
    flipDisabled,
    flipPlacements: flipPlacements2,
    floatingEl,
    offsetDistance,
    offsetSkidding,
    overlayPositioning = "absolute",
    placement,
    referenceEl,
    type
  }) => {
    if (!referenceEl || !floatingEl) {
      return;
    }
    const isRTL = direction === "rtl";
    const {
      x,
      y,
      placement: effectivePlacement,
      strategy: position,
      middlewareData
    } = await computePosition(referenceEl, floatingEl, {
      strategy: overlayPositioning,
      placement: placement === "auto" || placement === "auto-start" || placement === "auto-end" ? void 0 : getEffectivePlacement(placement, isRTL),
      middleware: getMiddleware({
        placement,
        flipDisabled,
        flipPlacements: flipPlacements2?.map((placement2) => getEffectivePlacement(placement2, isRTL)),
        offsetDistance,
        offsetSkidding,
        arrowEl,
        type
      })
    });
    if (arrowEl && middlewareData.arrow) {
      const { x: x2, y: y2 } = middlewareData.arrow;
      const side = effectivePlacement.split("-")[0];
      const alignment = x2 != null ? "left" : "top";
      const transform = ARROW_CSS_TRANSFORM[side];
      const reset = { left: "", top: "", bottom: "", right: "" };
      if ("floatingLayout" in component) {
        component.floatingLayout = side === "left" || side === "right" ? "horizontal" : "vertical";
      }
      Object.assign(arrowEl.style, {
        ...reset,
        [alignment]: `${alignment == "left" ? x2 : y2}px`,
        [side]: "100%",
        transform
      });
    }
    const referenceHidden = middlewareData.hide?.referenceHidden;
    const visibility = referenceHidden ? "hidden" : null;
    const pointerEvents = visibility ? "none" : null;
    floatingEl.setAttribute(placementDataAttribute, effectivePlacement);
    Object.assign(floatingEl.style, {
      pointerEvents,
      position,
      transform: `translate(${roundByDPR(x)}px,${roundByDPR(y)}px)`,
      visibility
    });
  })
);
const placementDataAttribute = "data-placement";
const flipPlacements = [
  "top",
  "bottom",
  "right",
  "left",
  "top-start",
  "top-end",
  "bottom-start",
  "bottom-end",
  "right-start",
  "right-end",
  "left-start",
  "left-end",
  "leading",
  "trailing",
  "leading-start",
  "leading-end",
  "trailing-start",
  "trailing-end"
];
const defaultMenuPlacement = "bottom-start";
const defaultEndMenuPlacement = "bottom-end";
const FloatingCSS = {
  animation: "calcite-floating-ui-anim",
  animationActive: "calcite-floating-ui-anim--active"
};
function getMiddleware({
  placement,
  flipDisabled,
  flipPlacements: flipPlacements2,
  offsetDistance,
  offsetSkidding,
  arrowEl,
  type
}) {
  const middleware = [shift(), hide()];
  if (type === "menu") {
    middleware.push(
      flip({
        fallbackPlacements: flipPlacements2 || ["top-start", "top", "top-end", "bottom-start", "bottom", "bottom-end"]
      })
    );
  }
  middleware.push(
    offset({
      mainAxis: typeof offsetDistance === "number" ? offsetDistance : 0,
      crossAxis: typeof offsetSkidding === "number" ? offsetSkidding : 0
    })
  );
  if (placement === "auto" || placement === "auto-start" || placement === "auto-end") {
    middleware.push(
      autoPlacement({ alignment: placement === "auto-start" ? "start" : placement === "auto-end" ? "end" : null })
    );
  } else if (!flipDisabled) {
    middleware.push(flip(flipPlacements2 ? { fallbackPlacements: flipPlacements2 } : {}));
  }
  if (arrowEl) {
    middleware.push(
      arrow({
        element: arrowEl
      })
    );
  }
  return middleware;
}
function isFlipPlacement(placement) {
  return flipPlacements.includes(placement);
}
function filterValidFlipPlacements(placements2, el) {
  const filteredPlacements = placements2.filter(isFlipPlacement);
  if (filteredPlacements.length !== placements2.length) {
    console.warn(
      `${el.tagName}: Invalid value found in: flipPlacements. Try any of these: ${flipPlacements.map((placement) => `"${placement}"`).join(", ").trim()}`,
      { el }
    );
  }
  return filteredPlacements;
}
function getEffectivePlacement(placement, isRTL = false) {
  const placements2 = ["left", "right"];
  if (isRTL) {
    placements2.reverse();
  }
  return placement.replace(/leading/gi, placements2[0]).replace(/trailing/gi, placements2[1]);
}
async function reposition(component, options, delayed = false) {
  if (!component.open || !options.floatingEl || !options.referenceEl) {
    return;
  }
  Object.assign(options.floatingEl.style, {
    display: "block",
    inset: "unset",
    // initial positioning based on https://floating-ui.com/docs/computePosition#initial-layout
    left: "0",
    position: options.overlayPositioning ?? "absolute",
    top: "0"
  });
  const trackedState = autoUpdatingComponentMap.get(component);
  if (!trackedState) {
    return runAutoUpdate(component);
  }
  const positionFunction = delayed ? getDebouncedReposition(component) : positionFloatingUI;
  await positionFunction(component, options);
}
function getDebouncedReposition(component) {
  let debounced = componentToDebouncedRepositionMap.get(component);
  if (debounced) {
    return debounced;
  }
  debounced = debounce(positionFloatingUI, DEBOUNCE.reposition, {
    edges: ["leading", "trailing"]
  });
  componentToDebouncedRepositionMap.set(component, debounced);
  return debounced;
}
const ARROW_CSS_TRANSFORM = {
  top: "",
  left: "rotate(-90deg)",
  bottom: "rotate(180deg)",
  right: "rotate(90deg)"
};
const autoUpdatingComponentMap = /* @__PURE__ */ new WeakMap();
const componentToDebouncedRepositionMap = /* @__PURE__ */ new WeakMap();
async function runAutoUpdate(component) {
  const { referenceEl, floatingEl } = component;
  if (!floatingEl?.isConnected) {
    return;
  }
  const effectiveAutoUpdate = !isServer ? autoUpdate : (_refEl, _floatingEl, updateCallback) => {
    updateCallback();
    return () => {
    };
  };
  autoUpdatingComponentMap.set(component, { state: "pending" });
  let repositionPromise;
  const cleanUp = effectiveAutoUpdate(
    referenceEl,
    floatingEl,
    // callback is invoked immediately
    () => {
      const promise = component.reposition();
      if (!repositionPromise) {
        repositionPromise = promise;
      }
    }
  );
  autoUpdatingComponentMap.set(component, { state: "active", cleanUp });
  return repositionPromise;
}
function hideFloatingUI(component) {
  const { floatingEl } = component;
  if (!floatingEl) {
    return;
  }
  Object.assign(floatingEl.style, {
    display: "",
    left: "",
    pointerEvents: "",
    position: "",
    top: "",
    transform: "",
    visibility: ""
  });
}
async function connectFloatingUI(component) {
  const { floatingEl, referenceEl } = component;
  hideFloatingUI(component);
  disconnectFloatingUI(component);
  if (!floatingEl || !referenceEl || !component.open) {
    return;
  }
  return runAutoUpdate(component);
}
function disconnectFloatingUI(component) {
  const trackedState = autoUpdatingComponentMap.get(component);
  if (trackedState?.state === "active") {
    trackedState.cleanUp();
  }
  autoUpdatingComponentMap.delete(component);
  componentToDebouncedRepositionMap.get(component)?.cancel();
  componentToDebouncedRepositionMap.delete(component);
}
const visiblePointerSize = 4;
const defaultOffsetDistance = Math.ceil(Math.hypot(visiblePointerSize, visiblePointerSize));
export {
  FloatingCSS as F,
  disconnectFloatingUI as a,
  defaultEndMenuPlacement as b,
  connectFloatingUI as c,
  defaultMenuPlacement as d,
  defaultOffsetDistance as e,
  filterValidFlipPlacements as f,
  hideFloatingUI as h,
  reposition as r
};
