<script lang="ts">
import type { PropType } from 'vue'
interface Props {
  data: any[]
  dataKey: string
  showLoading: boolean
  itemHeight: number
  dataBuffer: number
  onCompelete?: () => void
}
export default defineComponent({
  props: {
    data: {
      type: Array,
      default: () => [],
    },
    dataKey: {
      type: String,
      default: 'id',
    },
    showLoading: {
      type: Boolean,
      default: true,
    },
    itemHeight: {
      type: Number,
      default: 40,
    },
    dataBuffer: {
      type: Number,
      default: 40,
    },
    onCompelete: {
      type: Function as PropType<Pick<Props, 'onCompelete'>>,
      default: null,
    },
    itemId: {
      type: String,
      default: '',
    },
  },
  setup(props: Props): any {
    const { data, dataBuffer, itemHeight, onCompelete } = toRefs(props)
    const vitrualListContainer = ref<HTMLElement | null>(null)
    const canSeeDataCount = ref(0)
    const showDataList = ref<any[]>([])
    const paddingTop = ref(0)
    const containerHeight = ref(0)
    watch(
      () => props.data,
      nv => {
        handleScroll()
      },
    )
    let dataRange = {
      startIndex: 0,
      endIndex: 0,
    }
    let isScroll = false
    let isCompelete = false

    const requestAnimationFrame = window.requestAnimationFrame
    const handleScroll = () => {
      if (isScroll) return
      setIsScrollStatus()
      requestAnimationFrame(() => setDataRange()()()())
    }

    const setDataRange = () => {
      dataRange.startIndex = vitrualListContainer.value
        ? ~~(
            vitrualListContainer?.value?.scrollTop /
            ((itemHeight.value / 1920) * window.innerWidth)
          ) -
          dataBuffer?.value / 2
        : 0
      dataRange.startIndex = Math.max(dataRange.startIndex, 0)
      dataRange.endIndex = dataRange.startIndex + canSeeDataCount.value + dataBuffer?.value

      dataRange.endIndex = Math.min(data.value.length - 1, dataRange.endIndex)
      return () => getShowDataList()
    }

    const getShowDataList = () => {
      showDataList.value = data.value
        .slice(dataRange.startIndex, dataRange.endIndex + 1)
        .map((item, index) => ({
          ...item,
          __index__: index + dataRange.startIndex,
        }))
      return () => setScrollContainerPadding()
    }

    const setIsScrollStatus = () => {
      isScroll = !isScroll
      return onPageEnd()
    }
    const setScrollContainerPadding = () => {
      paddingTop.value = dataRange.startIndex * itemHeight.value
      return () => setIsScrollStatus()
    }

    const setScrollContainerHeight = () => {
      return (containerHeight.value = data.value.length * itemHeight.value)
    }
    const onPageEnd = () => {
      if (
        dataRange.startIndex + canSeeDataCount.value + dataBuffer?.value / 2 >
          data.value.length - 1 &&
        !isCompelete
      ) {
        setIsCompelete()
        return onCompelete?.value?.()
      }
    }

    const setCanSeeDataCount = () => {
      canSeeDataCount.value = vitrualListContainer.value
        ? Math.ceil(
            vitrualListContainer.value.clientHeight /
              ((itemHeight.value / 1920) * window.innerWidth),
          ) + 1
        : 0
    }

    const setIsCompelete = () => (isCompelete = !isCompelete)

    watch(props, () => {
      setScrollContainerHeight()
      isCompelete = false
    })

    onMounted(() => {
      setScrollContainerHeight()
      setCanSeeDataCount()
      return setDataRange()()()
    })

    return {
      handleScroll,
      paddingTop,
      containerHeight,
      showDataList,
      vitrualListContainer,
    }
  },
})
</script>

<template>
  <div ref="vitrualListContainer" class="vitrual-list-container" @scroll.passive="handleScroll">
    <div
      v-if="data?.length"
      class="vitrual-list-scroll-list"
      :style="`
              padding-top: ${paddingTop / 19.2}vw;
              height: ${containerHeight / 19.2}vw;
            `"
    >
      <div
        v-for="(item, index) in showDataList"
        :key="item[dataKey]"
        :class="{ 'vitrual-list-scroll-item': true, 'self-static': item.id === itemId }"
        :style="`height: ${itemHeight / 19.2}vw`"
      >
        <slot :item="item" :index="index"></slot>
      </div>
      <div v-if="showLoading">
        <slot name="loading">
          <div class="vitrual-list-loading">
            <span>正在努力加载中～</span>
          </div>
        </slot>
      </div>
    </div>
  </div>
</template>

<style scoped>
.vitrual-list-container {
  width: 100%;
  height: 100%;
  min-width: 100px;
  min-height: 8.33vh;
  overflow-y: scroll;
}
.vitrual-list-scroll-list {
  box-sizing: border-box;
}
.vitrual-list-scroll-item {
  overflow: hidden;
}
.vitrual-list-loading {
  text-align: center;
  color: #ccc;
}
</style>
