import { defineComponent, ref, provide, onMounted, nextTick, onUnmounted, createElementBlock, openBlock, normalizeClass, createElementVNode, normalizeStyle, renderSlot } from "vue";
import { anchorProps } from "./types.mjs";
import { anchorInjectKey } from "./provide.mjs";
import { isString, isCurrentPageLink, isUndefined, isWindow } from "../_utils/is.mjs";
import { getScroll, scrollTo } from "../_utils/dom.mjs";
const _hoisted_1 = { class: "o-anchor-line" };
const _hoisted_2 = { class: "o-anchor-items" };
const _sfc_main = /* @__PURE__ */ defineComponent({
  __name: "OAnchor",
  props: anchorProps,
  emits: ["click", "change"],
  setup(__props, { emit: __emit }) {
    const props = __props;
    const emits = __emit;
    const ANCHOR_REGX = /#([\S ]+)$/;
    const anchorRef = ref();
    const isScrolling = ref(false);
    const links = ref(/* @__PURE__ */ new Set());
    const activeLink = ref("");
    const indicatorStyle = ref({});
    const scrollContainer = ref();
    const getContainer = (container = window) => {
      if (isString(container)) {
        const dom = document.querySelector(container);
        return dom ? dom : window;
      }
      return container;
    };
    const updateIndicatorPosition = () => {
      var _a;
      const el = (_a = anchorRef.value) == null ? void 0 : _a.querySelector(".o-anchor-item-link.is-active");
      if (!el) {
        indicatorStyle.value = {};
      } else {
        const { offsetTop, offsetHeight } = el;
        const depth = el.getAttribute("data-depth");
        indicatorStyle.value.top = `${offsetTop}px`;
        indicatorStyle.value.height = `${offsetHeight}px`;
        indicatorStyle.value.opacity = depth === "0" ? 0 : 1;
      }
    };
    const setActiveLink = async (link) => {
      if (activeLink.value === link) {
        return;
      }
      activeLink.value = link;
      emits("change", activeLink.value);
      await nextTick();
      updateIndicatorPosition();
    };
    const getAnchorTarget = (link) => {
      const anchorMatches = ANCHOR_REGX.exec(link);
      if (!anchorMatches) {
        return;
      }
      const target = document.getElementById(anchorMatches[1]);
      return target;
    };
    const getOffsetTop = (el, container) => {
      const { top } = el.getBoundingClientRect();
      if (isWindow(container)) {
        return top - document.documentElement.clientTop;
      }
      return top - container.getBoundingClientRect().top;
    };
    const scrollIntoView = async (link) => {
      if (!isCurrentPageLink(link)) {
        return;
      }
      setActiveLink(link);
      const target = getAnchorTarget(link);
      if (!target) {
        return;
      }
      isScrolling.value = true;
      const { scrollTop } = getScroll(scrollContainer.value);
      const offsetTop = getOffsetTop(target, scrollContainer.value);
      const y = scrollTop + offsetTop - props.targetOffset;
      await scrollTo(y, {
        container: scrollContainer.value
      });
      isScrolling.value = false;
    };
    const activeNearest = () => {
      const distances = [];
      const { targetOffset, bounds } = props;
      let active = "";
      links.value.forEach((link) => {
        const target = getAnchorTarget(link);
        if (target) {
          const top = getOffsetTop(target, scrollContainer.value);
          if (top < targetOffset + bounds) {
            distances.push({
              link,
              top
            });
          }
        }
      });
      if (distances.length) {
        const max = distances.reduce((prev, cur) => prev.top > cur.top ? prev : cur);
        active = max.link;
      }
      setActiveLink(active);
    };
    const onScroll = () => {
      if (isScrolling.value) {
        return;
      }
      activeNearest();
    };
    const bindEvent = () => {
      if (isUndefined(scrollContainer.value)) {
        return;
      }
      scrollContainer.value.addEventListener("scroll", onScroll, { passive: true });
    };
    const unbindEvent = () => {
      if (isUndefined(scrollContainer.value)) {
        return;
      }
      scrollContainer.value.removeEventListener("scroll", onScroll);
    };
    const addLink = (link) => {
      if (!ANCHOR_REGX.test(link) || links.value.has(link)) {
        return;
      }
      links.value.add(link);
    };
    const removeLink = (link) => {
      links.value.add(link);
    };
    const onItemClick = (options) => {
      const { event, link } = options;
      emits("click", event, link);
    };
    provide(anchorInjectKey, {
      addLink,
      removeLink,
      onItemClick,
      activeLink,
      scrollIntoView,
      getChangeHash: () => props.changeHash
    });
    onMounted(() => {
      scrollContainer.value = getContainer(props.container);
      const hash = decodeURIComponent(window.location.hash);
      if (hash) {
        scrollIntoView(hash);
      } else {
        activeNearest();
      }
      nextTick(() => {
        bindEvent();
      });
    });
    onUnmounted(() => {
      unbindEvent();
    });
    return (_ctx, _cache) => {
      return openBlock(), createElementBlock(
        "div",
        {
          ref_key: "anchorRef",
          ref: anchorRef,
          class: normalizeClass(["o-anchor", `o-anchor-${props.size}`])
        },
        [
          createElementVNode("div", _hoisted_1, [
            createElementVNode(
              "div",
              {
                class: "o-anchor-indicator",
                style: normalizeStyle(indicatorStyle.value)
              },
              null,
              4
              /* STYLE */
            )
          ]),
          createElementVNode("div", _hoisted_2, [
            renderSlot(_ctx.$slots, "default")
          ])
        ],
        2
        /* CLASS */
      );
    };
  }
});
export {
  _sfc_main as default
};
