/**
 * ldesign v0.2.14
 * (c) 2022/11/2 16:16 built by swimly
 * @license MIT
 */

import { reactive, computed, ref, nextTick, watch, onMounted, toRefs } from '@vue/composition-api';

const useVirtualScroll = _ref => {
  let {
    data,
    container,
    fixedHeight = false,
    lineHeight = 30,
    bufferSize = 20,
    threshold = 100
  } = _ref;
  const state = reactive({
    visibleData: [],
    cachedHeight: [],
    cachedScrollY: []
  });
  const isVirtual = computed(() => data.value.length > threshold);
  const updateId = ref(0);
  const trs = /* @__PURE__ */new Map();
  let visibleCount = 0;
  let beforeScrollTop = 0;
  let index = 0;
  let offset = 0;
  let start = 0;
  let last = 0;
  const reset = () => {
    data.value.forEach((item, i) => {
      item.$index = i;
      if (fixedHeight) state.cachedScrollY[i] = i * lineHeight;
    });
    if (!fixedHeight) state.cachedScrollY[data.value.length - 1] = void 0;
  };
  reset();
  const scrollHeight = computed(() => {
    const {
      cachedHeight
    } = state;
    const {
      length
    } = cachedHeight;
    if (length) {
      const maxScrollY = cachedHeight.reduce((sum, v) => sum + v || lineHeight, 0);
      if (cachedHeight.length === data.value.length) return maxScrollY;
      const average = maxScrollY / cachedHeight.length;
      return maxScrollY + (data.value.length - cachedHeight.length) * average;
    }
    return isVirtual.value ? data.value.length * lineHeight : 0;
  });
  const translateY = computed(() => {
    const {
      visibleData
    } = state;
    const firstRow = visibleData[0];
    if (firstRow) {
      return visibleData.length === 1 ? 0 : state.cachedScrollY[firstRow.$index];
    }
    return 0;
  });
  const updateVisibleData = () => {
    last = Math.min(start + visibleCount + bufferSize * 2, data.value.length);
    state.visibleData = data.value.slice(start, last);
  };
  const calculateScrollY = () => {
    var _anchorDom$getBoundin;
    const anchorDom = trs.get(index);
    if (!anchorDom) return;
    const anchorDomHeight = anchorDom === null || anchorDom === void 0 ? void 0 : (_anchorDom$getBoundin = anchorDom.getBoundingClientRect()) === null || _anchorDom$getBoundin === void 0 ? void 0 : _anchorDom$getBoundin.height;
    state.cachedScrollY[index] = container.value.scrollTop - offset;
    state.cachedHeight[index] = anchorDomHeight;
    for (let i = index + 1; i <= state.visibleData[state.visibleData.length - 1].$index; i++) {
      const tr = trs.get(i);
      const {
        height
      } = (tr === null || tr === void 0 ? void 0 : tr.getBoundingClientRect()) || {};
      state.cachedHeight[i] = height;
      const scrollY = state.cachedScrollY[i - 1] + state.cachedHeight[i - 1];
      state.cachedScrollY.splice(i, 1, scrollY);
    }
    for (let i = index - 1; i >= state.visibleData[0].$index; i--) {
      const tr = trs.get(i);
      const {
        height
      } = (tr === null || tr === void 0 ? void 0 : tr.getBoundingClientRect()) || {};
      state.cachedHeight[i] = height;
      const scrollY = state.cachedScrollY[i + 1] - state.cachedHeight[i];
      state.cachedScrollY.splice(i, 1, scrollY);
    }
    if (state.cachedScrollY[0] > 0) {
      const distance = state.cachedScrollY[0];
      const length = Math.min(last, data.value.length);
      for (let i = 0; i < length; i++) {
        state.cachedScrollY.splice(i, 1, state.cachedScrollY[i] - distance);
      }
      const scrollTop = state.cachedScrollY[index - 1] ? state.cachedScrollY[index - 1] + offset : offset;
      container.value.scrollTop = scrollTop;
      beforeScrollTop = scrollTop;
    }
    if (state.cachedScrollY[start] < 0) {
      const s = state.cachedHeight.slice(0, Math.max(0, index)).reduce((sum, v) => sum + v, 0) + offset;
      container.value.scrollTop = s;
      beforeScrollTop = s;
      if (s === 0) {
        index = 0;
        offset = 0;
      }
    }
    nextTick(() => {
      const {
        scrollTop,
        scrollHeight: scrollHeight2,
        clientHeight
      } = container.value;
      if (scrollTop + clientHeight === scrollHeight2) {
        for (let i = last - 1; i >= start; i--) {
          if (i === last - 1) {
            state.cachedScrollY.splice(i, 1, scrollHeight2.value - state.cachedHeight[i]);
          } else {
            state.cachedScrollY.splice(i, 1, state.cachedScrollY[i + 1] - state.cachedHeight[i]);
          }
        }
      }
    });
  };
  const handleScroll = () => {
    if (!isVirtual.value) return;
    if (!container.value) return;
    const {
      scrollTop
    } = container.value;
    let distance = scrollTop - beforeScrollTop;
    beforeScrollTop = scrollTop;
    distance += offset;
    let lastIndex = index;
    if (!distance) return;
    if (distance >= 0) {
      while (lastIndex < data.value.length && distance > (state.cachedHeight[lastIndex] || lineHeight)) {
        if (!state.cachedHeight[lastIndex]) state.cachedHeight[lastIndex] = lineHeight;
        distance -= state.cachedHeight[lastIndex];
        lastIndex++;
      }
      if (lastIndex >= data.value.length) {
        index = data.value.length - 1;
        offset = 0;
      } else {
        index = lastIndex;
        offset = distance;
      }
      const {
        clientHeight,
        scrollHeight: scrollHeight2
      } = container.value;
      if (scrollTop + clientHeight === scrollHeight2) {
        index = data.value.length - visibleCount + 1;
      }
      if (start <= index - bufferSize) {
        start = Math.min(data.value.length - visibleCount, index - bufferSize);
        if (start < 0) start = 0;
      }
    } else {
      while (distance < 0) {
        lastIndex--;
        if (!state.cachedHeight[lastIndex]) state.cachedHeight[lastIndex] = lineHeight;
        distance += state.cachedHeight[lastIndex];
      }
      if (lastIndex < 0) {
        index = 0;
        offset = 0;
      } else {
        index = lastIndex;
        offset = distance;
      }
      calculateScrollY();
      if (start > index - bufferSize) {
        start = Math.max(0, index - bufferSize);
      }
    }
    updateVisibleData();
  };
  !fixedHeight && watch(updateId, calculateScrollY, {
    flush: "post"
  });
  const handleRowMounted = () => {
    if (!isVirtual.value) return;
    updateId.value++;
  };
  watch(data, () => {
    reset();
    state.visibleData = [];
    state.cachedScrollY = [];
    state.cachedHeight = [];
    beforeScrollTop = 0;
    index = 0;
    offset = 0;
    start = 0;
    trs.clear();
    if (data.value.length <= threshold) state.visibleData = data.value;else updateVisibleData();
    container.value && (container.value.scrollTop = 0);
  });
  let mounted = false;
  const refreshContainer = () => {
    if (mounted) {
      visibleCount = Math.ceil(container.value.offsetHeight / lineHeight);
      updateVisibleData();
    }
  };
  onMounted(() => {
    if (!window || !window.IntersectionObserver) return;
    const ob = new window.IntersectionObserver(entries => {
      const entry = entries[0];
      if (entry.isIntersecting || entry.intersectionRatio) {
        mounted = true;
        isVirtual.value && refreshContainer();
        ob.unobserve(container.value);
      }
    });
    container.value && ob.observe(container.value);
  });
  return {
    trs,
    scrollHeight,
    ...toRefs(state),
    translateY,
    handleScroll,
    handleRowMounted,
    refreshContainer,
    fixedHeight,
    calculateScrollY
  };
};

export { useVirtualScroll as default };
//# sourceMappingURL=use-virtual-scroll.js.map
