<template>
  <div ref="containerRef" relative>
    <DynamicScroller
      :items="virtualRows"
      :min-item-size="virtualMinRowHeight"
      key-field="id"
      page-mode
      :emitUpdate="true"
      @update="onUpdate"
      @resize="syncWidth"
      v-bind="attrs"
    >
      <template
        #default="{
          item: vRow,
          index,
          active,
        }: {
          item: { items: T[] }
          index: number
          active: boolean
        }"
      >
        <DynamicScrollerItem :item="vRow" :active="active" :data-index="index">
          <div class="flex flex-wrap" :style="{ gap: `${gap}px`, paddingBlockEnd: `${gap}px` }">
            <div v-for="item in vRow.items" :key="item[keyField]" class="w-1 flex-1">
              <slot :item="item" :index="index" :active="active" />
            </div>
          </div>
        </DynamicScrollerItem>
      </template>
    </DynamicScroller>
  </div>
</template>
 
<script setup lang="ts" generic="T extends Record<string, any>">
import { computed, ref, useAttrs } from 'vue'
import { DynamicScroller, DynamicScrollerItem } from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
 
const props = defineProps<{
  items: T[]
  minItemWidth: number
  minItemHeight: number
  keyField: keyof T
  gap: number
}>()
 
const attrs = useAttrs()
console.log(attrs)
 
const emit = defineEmits<{
  (e: 'viewItems', items: T[]): void
  (e: 'resize'): void
}>()
 
const containerWidth = ref(0)
const containerRef = ref<HTMLElement | null>(null)
function syncWidth() {
  containerWidth.value = containerRef.value?.clientWidth ?? 0
}
 
function onUpdate(startIndex: number, endIndex: number) {
  const items = virtualRows.value.slice(startIndex, endIndex + 1).flatMap((row) => row.items)
  emit('viewItems', items)
}
 
const columns = computed(() => {
  if (!containerWidth.value) return 1
  const potentialColumns = Math.floor(containerWidth.value / (props.minItemWidth + props.gap))
  return Math.max(1, potentialColumns)
})
 
const virtualRows = computed(() => {
  const rows: { id: string; items: T[] }[] = []
  for (let i = 0; i < props.items.length; i += columns.value) {
    rows.push({
      id: `row_${Math.floor(i / columns.value)}`,
      items: props.items.slice(i, i + columns.value),
    })
  }
  return rows
})
 
const virtualMinRowHeight = computed(() => props.minItemHeight + props.gap)
</script>
 
<style scoped>
/** 防止边框或阴影被遮挡 */
:deep(.vue-recycle-scroller__item-wrapper) {
  overflow: visible;
}
</style>