<commit_before><template>
  <div ref="scroller" class="virtual-scroller" @scroll="onScroll">
    <div :style="{ height: `${totalHeight}px` }">
      <div :style="{ transform: `translateY(${offsetY}px)`}">
        <template v-for="item in renderedItems">
          <slot :item="item"></slot>
        </template>
      </div>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { computed, onMounted, ref, toRefs } from 'vue'

const props = defineProps<{ items: any[], itemHeight: number }>()
const { items, itemHeight } = toRefs(props)

const scroller = ref<HTMLElement>()
const scrollerHeight = ref(0)
const renderAhead = 5
const scrollTop = ref(0)

const totalHeight = computed(() => items.value.length * itemHeight.value)
const startPosition = computed(() => Math.max(0, Math.floor(scrollTop.value / itemHeight.value) - renderAhead))
const offsetY = computed(() => startPosition.value * itemHeight.value)

const renderedItems = computed(() => {
  let count = Math.ceil(scrollerHeight.value / itemHeight.value) + 2 * renderAhead
  count = Math.min(items.value.length - startPosition.value, count)
  return items.value.slice(startPosition.value, startPosition.value + count)
})

const onScroll = e => requestAnimationFrame(() => scrollTop.value = (e.target as HTMLElement).scrollTop)

const observer = new ResizeObserver(entries => entries.forEach(el => scrollerHeight.value = el.contentRect.height))

onMounted(() => {
  observer.observe(scroller.value!)
  scrollerHeight.value = scroller.value!.offsetHeight
})
</script>

<style lang="scss" scoped>
.virtual-scroller {
  overflow: auto;
  will-change: transform;

  > div {
    overflow: hidden;
    will-change: transform;

    > div {
      will-change: transform;<commit_msg>feat: improve Virtual Scroller with .passive<commit_after><template>
  <div ref="scroller" class="virtual-scroller" @scroll.passive="onScroll">
    <div :style="{ height: `${totalHeight}px` }">
      <div :style="{ transform: `translateY(${offsetY}px)`}">
        <template v-for="item in renderedItems">
          <slot :item="item"></slot>
        </template>
      </div>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { computed, onMounted, onUnmounted, ref, toRefs } from 'vue'

const props = defineProps<{ items: any[], itemHeight: number }>()
const { items, itemHeight } = toRefs(props)

const scroller = ref<HTMLElement>()
const scrollerHeight = ref(0)
const renderAhead = 5
const scrollTop = ref(0)

const totalHeight = computed(() => items.value.length * itemHeight.value)
const startPosition = computed(() => Math.max(0, Math.floor(scrollTop.value / itemHeight.value) - renderAhead))
const offsetY = computed(() => startPosition.value * itemHeight.value)

const renderedItems = computed(() => {
  let count = Math.ceil(scrollerHeight.value / itemHeight.value) + 2 * renderAhead
  count = Math.min(items.value.length - startPosition.value, count)
  return items.value.slice(startPosition.value, startPosition.value + count)
})

const onScroll = e => requestAnimationFrame(() => (scrollTop.value = (e.target as HTMLElement).scrollTop))

const observer = new ResizeObserver(entries => entries.forEach(el => scrollerHeight.value = el.contentRect.height))

onMounted(() => {
  observer.observe(scroller.value!)
  scrollerHeight.value = scroller.value!.offsetHeight
})

onUnmounted(() => observer.unobserve(scroller.value!))
</script>

<style lang="scss" scoped>
.virtual-scroller {
  overflow: auto;
  will-change: transform;

  > div {
    overflow: hidden;
    will-change: transform;

    > div {
      will-change: transform;
{'fraction_non_alphanumeric': 0.1543381535038932, 'fraction_numerical': 0.0027808676307007787, 'mean_word_length': 4.474885844748859, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 60, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6341183', 'n_tokens_mistral': 1129, 'n_tokens_neox': 1119, 'n_words': 261}