<script>

</script>

<script setup>
import { computed, inject, shallowRef, useTemplateRef, watch } from "vue";
import { MARKER_CLUSTERER_INJECTION_KEY } from "./types";
import { defineDeprecatedAlias, MARKER_INJECTION_KEY, normalizeLatLng, useGoogleMapsResource } from "./useGoogleMapsResource";
defineOptions({
  inheritAttrs: false
});
const {
  position,
  defaultOpen = true,
  anchor = "bottom-center",
  offset,
  pane = "floatPane",
  zIndex,
  blockMapInteraction = true,
  panOnOpen = true,
  hideWhenClustered = true
} = defineProps({
  position: { type: null, required: false },
  defaultOpen: { type: Boolean, required: false },
  anchor: { type: String, required: false },
  offset: { type: Object, required: false },
  pane: { type: String, required: false },
  zIndex: { type: Number, required: false },
  blockMapInteraction: { type: Boolean, required: false },
  panOnOpen: { type: [Boolean, Number], required: false },
  hideWhenClustered: { type: Boolean, required: false }
});
defineSlots();
const open = defineModel("open", { type: Boolean, ...{ default: void 0 } });
if (open.value === void 0)
  open.value = defaultOpen ?? true;
const markerContext = inject(MARKER_INJECTION_KEY, void 0);
const markerClustererContext = inject(MARKER_CLUSTERER_INJECTION_KEY, void 0);
function getResolvedPosition() {
  if (position)
    return normalizeLatLng(position);
  const markerPosition = markerContext?.advancedMarkerElement.value?.position;
  if (markerPosition)
    return normalizeLatLng(markerPosition);
  return void 0;
}
const ANCHOR_TRANSFORMS = {
  "center": "translate(-50%, -50%)",
  "top-left": "translate(0, 0)",
  "top-center": "translate(-50%, 0)",
  "top-right": "translate(-100%, 0)",
  "bottom-left": "translate(0, -100%)",
  "bottom-center": "translate(-50%, -100%)",
  "bottom-right": "translate(-100%, -100%)",
  "left-center": "translate(0, -50%)",
  "right-center": "translate(-100%, -50%)"
};
const overlayAnchor = useTemplateRef("overlay-anchor");
const overlayPosition = shallowRef(void 0);
const dataState = computed(
  () => open.value !== false && overlayPosition.value !== void 0 ? "open" : "closed"
);
const overlayStyle = computed(() => {
  const visible = open.value !== false && overlayPosition.value !== void 0;
  if (!visible) {
    return {
      position: "absolute",
      visibility: "hidden",
      pointerEvents: "none"
    };
  }
  const { x, y } = overlayPosition.value;
  return {
    position: "absolute",
    left: `${x + (offset?.x ?? 0)}px`,
    top: `${y + (offset?.y ?? 0)}px`,
    transform: ANCHOR_TRANSFORMS[anchor],
    zIndex: zIndex !== void 0 ? String(zIndex) : void 0,
    visibility: "visible",
    pointerEvents: "auto"
  };
});
const listeners = [];
function panMapToFitOverlay(el, map, padding) {
  const child = el.firstElementChild;
  if (!child)
    return;
  const overlayRect = child.getBoundingClientRect();
  const mapRect = map.getDiv().getBoundingClientRect();
  let panX = 0;
  let panY = 0;
  if (overlayRect.top - padding < mapRect.top)
    panY = overlayRect.top - mapRect.top - padding;
  if (overlayRect.bottom + padding > mapRect.bottom)
    panY = overlayRect.bottom - mapRect.bottom + padding;
  if (overlayRect.left - padding < mapRect.left)
    panX = overlayRect.left - mapRect.left - padding;
  else if (overlayRect.right + padding > mapRect.right)
    panX = overlayRect.right - mapRect.right + padding;
  if (panX !== 0 || panY !== 0)
    map.panBy(panX, panY);
}
function makeOverlayClass(mapsApi, map) {
  let attachedEl = null;
  return class CustomOverlay extends mapsApi.OverlayView {
    onAdd() {
      const panes = this.getPanes();
      const el = overlayAnchor.value;
      if (panes && el) {
        attachedEl = el;
        panes[pane].appendChild(el);
        if (blockMapInteraction)
          mapsApi.OverlayView.preventMapHitsAndGesturesFrom(el);
      }
      if (panOnOpen && open.value !== false) {
        const padding = typeof panOnOpen === "number" ? panOnOpen : 40;
        requestAnimationFrame(() => {
          if (open.value !== false && overlayAnchor.value && overlayPosition.value)
            panMapToFitOverlay(overlayAnchor.value, map, padding);
        });
      }
    }
    draw() {
      if (open.value === false) {
        overlayPosition.value = void 0;
        return;
      }
      const resolvedPosition = getResolvedPosition();
      if (!resolvedPosition) {
        overlayPosition.value = void 0;
        return;
      }
      const projection = this.getProjection();
      if (!projection) {
        overlayPosition.value = void 0;
        return;
      }
      const pos = projection.fromLatLngToDivPixel(
        new mapsApi.LatLng(resolvedPosition.lat, resolvedPosition.lng)
      );
      if (!pos) {
        overlayPosition.value = void 0;
        return;
      }
      overlayPosition.value = { x: pos.x, y: pos.y };
    }
    onRemove() {
      attachedEl?.parentNode?.removeChild(attachedEl);
      attachedEl = null;
    }
  };
}
const overlay = useGoogleMapsResource({
  // ready condition accesses .value on ShallowRefs — tracked by whenever() in useGoogleMapsResource
  ready: () => !!overlayAnchor.value && !!(position || markerContext?.advancedMarkerElement.value),
  create({ mapsApi, map }) {
    const CustomOverlay = makeOverlayClass(mapsApi, map);
    const ov = new CustomOverlay();
    ov.setMap(map);
    if (markerContext?.advancedMarkerElement.value) {
      const ame = markerContext.advancedMarkerElement.value;
      listeners.push(
        ame.addListener("drag", () => ov.draw()),
        ame.addListener("dragend", () => ov.draw())
      );
    }
    return ov;
  },
  cleanup(ov) {
    listeners.forEach((l) => l.remove());
    listeners.length = 0;
    ov.setMap(null);
  }
});
if (markerContext) {
  watch(
    () => {
      const markerPosition = markerContext.advancedMarkerElement.value?.position;
      return markerPosition ? normalizeLatLng(markerPosition) : void 0;
    },
    () => {
      overlay.value?.draw();
    }
  );
}
watch(
  () => {
    const p = position ? normalizeLatLng(position) : void 0;
    return [p?.lat, p?.lng, offset?.x, offset?.y, zIndex, anchor];
  },
  () => {
    overlay.value?.draw();
  }
);
watch(() => open.value, () => {
  if (!overlay.value)
    return;
  overlay.value.draw();
});
watch([() => pane, () => blockMapInteraction], () => {
  if (overlay.value) {
    const map = overlay.value.getMap();
    overlay.value.setMap(null);
    if (map)
      overlay.value.setMap(map);
  }
});
if (markerClustererContext && markerContext) {
  watch(
    () => markerClustererContext.clusteringVersion.value,
    () => {
      if (!hideWhenClustered || open.value === false)
        return;
      const clusterer = markerClustererContext.markerClusterer.value;
      if (!clusterer?.clusters)
        return;
      const parentMarker = markerContext.advancedMarkerElement.value;
      if (!parentMarker)
        return;
      const isClustered = clusterer.clusters.some(
        (cluster) => cluster.count > 1 && cluster.markers?.includes(parentMarker)
      );
      if (isClustered)
        open.value = false;
    }
  );
}
const exposed = {
  overlayView: overlay,
  // Plain alias for production. In dev, replaced below with a getter that
  // emits a one-shot deprecation warning. Both forms return the same
  // shallow ref as `overlayView`.
  overlay,
  dataState
};
if (import.meta.dev) {
  defineDeprecatedAlias(
    exposed,
    "overlay",
    "overlayView",
    '[nuxt-scripts] <ScriptGoogleMapsOverlayView> expose key "overlay" is deprecated; use "overlayView" instead. See https://scripts.nuxt.com/docs/migration-guide/v0-to-v1'
  );
}
defineExpose(exposed);
</script>

<template>
  <div style="display: none;">
    <!--
      Two-element structure:
        - `overlay-anchor` is moved into a Google Maps pane on `onAdd()`. Its
          inline style is reactively bound to `overlayStyle`, so position
          updates from `draw()` flow through Vue's patcher even after the node
          has been reparented out of the component tree.
        - `overlay-content` carries `data-state`, attribute-based animations,
          and forwards parent attrs (e.g. `class`) so consumers can target it
          directly with `[data-state]` selectors.
    -->
    <div ref="overlay-anchor" :style="overlayStyle">
      <div :data-state="dataState" v-bind="$attrs">
        <slot />
      </div>
    </div>
  </div>
</template>
