0

I have been trying to create a dynamic grid using tanstack virtualized rows (the number of columns being tied to window width)

The issue is that my rows expand in height due to window resize, but my visualizer uses a fixed estimate height, making the scroll 'jump' and not smooth.

I have a minimal working example setup here if anyone could help me figure out how to measure the elements. https://codesandbox.io/p/devbox/txkp7k

I think I have tried a few dozen versions and ways to measure the height of the elements but nothing has worked. The fix would be to measure the height of the elements on resize or update, but I couldn't get it working with tanstack at all.

This is my virtual grid:

<script setup lang="ts">
import { ref, computed } from 'vue';
import { useBreakpoints } from '@vueuse/core';
import { useVirtualizer } from '@tanstack/vue-virtual';
import { totalCardCount, cardIDs, cardMap } from '../data/generate';
import CardComponent from './CardComponent.vue';

defineProps<{
  isVaultLoaded: boolean;
}>();

const containerRef = ref<HTMLElement | null>(null);

const breakpoints = useBreakpoints({ sm: 740, md: 1020, lg: 1340, xl: 1640 });
const columnCount = computed(() => {
  if (breakpoints.smaller('sm').value) return 1;
  if (breakpoints.between('sm', 'md').value) return 2;
  if (breakpoints.between('md', 'lg').value) return 3;
  if (breakpoints.between('lg', 'xl').value) return 4;
  return 5;
});

const rowCount = computed(() => Math.ceil(totalCardCount / columnCount.value));

const virtualizerOptions = computed(() => ({
  count: rowCount.value,
  getScrollElement: () => containerRef.value,
  estimateSize: () => 480, // An estimate of a single row's height
  overscan: 5,
}));
const rowVirtualizer = useVirtualizer(virtualizerOptions);

const virtualRows = computed(() => rowVirtualizer.value.getVirtualItems());
</script>

<template>
  <div v-if="isVaultLoaded" ref="containerRef" class="grid-container hide-scrollbar">
    <div :style="{ height: `${rowVirtualizer.getTotalSize()}px`, position: 'relative' }">
      <div
          class="grid-window @container"
          :style="{
          '--column-count': columnCount,
          transform: `translateY(${virtualRows[0]?.start ?? 0}px)`,
        }"
      >
        <template v-for="row in virtualRows" :key="row.key">
          <template v-for="colIndex in columnCount" :key="colIndex">
            <template v-if="(row.index * columnCount + colIndex - 1) < totalCardCount">
              <CardComponent
                  :key="cardIDs[row.index * columnCount + colIndex - 1]"
                  v-if="cardMap.has(cardIDs[row.index * columnCount + colIndex - 1])"
                  :item="cardMap.get(cardIDs[row.index * columnCount + colIndex - 1])!"
              />
            </template>
          </template>
        </template>
      </div>
    </div>
  </div>
  <div v-else class="placeholder-text">
    <p>Please select and load a vault to view cards.</p>
  </div>
</template>

<style scoped>
.grid-container {
  width: 100%;
  height: 100%;
  overflow-y: auto;
  contain: strict;
}

.grid-window {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  display: grid;
  grid-template-columns: repeat(var(--column-count), 1fr);
  gap: 1rem;
  padding: 1rem;

  grid-template-rows: auto auto auto auto;
}

.placeholder-text {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  color: #888;
  font-style: italic;
}

.hide-scrollbar {
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.hide-scrollbar::-webkit-scrollbar {
  display: none;
}
</style>

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.