// Composables
import { useDisplay } from "./display.mjs";
import { useResizeObserver } from "./resizeObserver.mjs"; // Utilities
import { computed, ref, shallowRef, watch, watchEffect } from 'vue';
import { clamp, createRange, propsFactory } from "../util/index.mjs"; // Types
const UP = -1;
const DOWN = 1;
export const makeVirtualProps = propsFactory({
  itemHeight: {
    type: [Number, String],
    default: 48
  }
}, 'virtual');
export function useVirtual(props, items, offset) {
  const first = shallowRef(0);
  const baseItemHeight = shallowRef(props.itemHeight);
  const itemHeight = computed({
    get: () => parseInt(baseItemHeight.value ?? 0, 10),
    set(val) {
      baseItemHeight.value = val;
    }
  });
  const containerRef = ref();
  const {
    resizeRef,
    contentRect
  } = useResizeObserver();
  watchEffect(() => {
    resizeRef.value = containerRef.value;
  });
  const display = useDisplay();
  const sizeMap = new Map();
  let sizes = Array.from({
    length: items.value.length
  });
  const visibleItems = computed(() => {
    const height = (!contentRect.value || containerRef.value === document.documentElement ? display.height.value : contentRect.value.height) - (offset?.value ?? 0);
    return Math.ceil(height / itemHeight.value * 1.7 + 1);
  });
  function handleItemResize(index, height) {
    itemHeight.value = Math.max(itemHeight.value, height);
    sizes[index] = height;
    sizeMap.set(items.value[index], height);
  }
  function calculateOffset(index) {
    return sizes.slice(0, index).reduce((acc, val) => acc + (val || itemHeight.value), 0);
  }
  function calculateMidPointIndex(scrollTop) {
    const end = items.value.length;
    let middle = 0;
    let middleOffset = 0;
    while (middleOffset < scrollTop && middle < end) {
      middleOffset += sizes[middle++] || itemHeight.value;
    }
    return middle - 1;
  }
  let lastScrollTop = 0;
  function handleScroll() {
    if (!containerRef.value || !contentRect.value) return;
    const height = contentRect.value.height - 56;
    const scrollTop = containerRef.value.scrollTop;
    const direction = scrollTop < lastScrollTop ? UP : DOWN;
    const midPointIndex = calculateMidPointIndex(scrollTop + height / 2);
    const buffer = Math.round(visibleItems.value / 3);
    const firstIndex = midPointIndex - buffer;
    const lastIndex = first.value + buffer * 2 - 1;
    if (direction === UP && midPointIndex <= lastIndex) {
      first.value = clamp(firstIndex, 0, items.value.length);
    } else if (direction === DOWN && midPointIndex >= lastIndex) {
      first.value = clamp(firstIndex, 0, items.value.length - visibleItems.value);
    }
    lastScrollTop = scrollTop;
  }
  function scrollToIndex(index) {
    if (!containerRef.value) return;
    const offset = calculateOffset(index);
    containerRef.value.scrollTop = offset;
  }
  const last = computed(() => Math.min(items.value.length, first.value + visibleItems.value));
  const computedItems = computed(() => {
    return items.value.slice(first.value, last.value).map((item, index) => ({
      raw: item,
      index: index + first.value
    }));
  });
  const paddingTop = computed(() => calculateOffset(first.value));
  const paddingBottom = computed(() => calculateOffset(items.value.length) - calculateOffset(last.value));
  watch(() => items.value.length, () => {
    sizes = createRange(items.value.length).map(() => itemHeight.value);
    sizeMap.forEach((height, item) => {
      const index = items.value.indexOf(item);
      if (index === -1) {
        sizeMap.delete(item);
      } else {
        sizes[index] = height;
      }
    });
  });
  return {
    containerRef,
    computedItems,
    itemHeight,
    paddingTop,
    paddingBottom,
    scrollToIndex,
    handleScroll,
    handleItemResize
  };
}
//# sourceMappingURL=virtual.mjs.map