-
I'm looking for something like useVirtualList except I don't have all the data loaded yet, and the data is loaded as we hit the page bounds of the list. It's similar in concept to useVirtualList + useInfiniteScroll, but I want the scrollbar to know its full height because I know the total records that will be loaded. Similar to how this Kendo Grid (source) does it. Is there a way to accomplish this with the current composables at my disposal? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
I came up with something that works for my solution... it combines useVirtualList and useScroll (fiddle). <template>
<article class="flex flex-1 flex-col overflow-hidden p-2 h-full">
<div class="mb-4 flex space-x-2">
Total: <input :value="dataTotal" readonly class="border w-12" /> Loaded:
<input :value="totalLoaded" readonly class="border w-12" /> Goto:
<input v-model="goto" @keydown.enter="onEnterGoto" class="border w-12 pl-1" />
<button @click="onClickRefreshButton" class="border bg-sky-200 hover:bg-sky-300 rounded border-sky-300 px-1">
Refresh
</button>
</div>
<section v-bind="containerProps" class="h-96">
<div v-bind="wrapperProps" class="space-y-2">
<div v-for="item in list" :key="item.index" class="h-16 border">
<span v-if="item.data">{{ item.data.index }}: {{ item.data.name }}</span>
</div>
</div>
</section>
</article>
</template>
<script setup lang="ts">
import { nextTick, reactive, ref, unref } from 'vue'
import { useScroll, useVirtualList } from '@vueuse/core'
import { faker } from '@faker-js/faker'
const data = reactive<any[]>([])
const dataTotal = ref(0)
const pageSize = ref(50)
const loadedPages = reactive<number[]>([])
const ItemHeight = 72
const RecordThreshold = 20
const totalLoaded = ref(0)
const goto = ref(0)
const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(data, {
itemHeight: ItemHeight,
})
const { y } = useScroll(containerProps.ref, {
throttle: 10,
idle: 0,
async onStop(event) {
const index = Math.ceil(y.value / ItemHeight)
// Check the next x records have loaded
loadRecords(index + RecordThreshold)
// Check the previous x records have loaded
loadRecords(index - RecordThreshold)
},
})
function getNewTotal() {
dataTotal.value = Math.floor(Math.random() * 5000)
}
async function loadRecords(take = 0) {
const $pageSize = unref(pageSize)
const page = Math.floor(take / $pageSize)
if (page < 0 || loadedPages.includes(page)) {
return
}
// Normalize the amount we want to take
take = page * $pageSize
loadedPages.push(page)
// Reset our data if we don't match
if (data.length !== dataTotal.value) {
data.splice(0)
for (let i = 0; i < dataTotal.value; i++) {
data.push(null)
}
}
return new Promise((resolve) => {
// Simulate async request
setTimeout(() => {
let count = $pageSize * (page + 1)
const $dataTotal = unref(dataTotal)
if ($dataTotal < $pageSize || $dataTotal - take < $pageSize) {
count = $dataTotal
}
for (let i = take; i < count; i++) {
data.splice(i, 1, {
index: i,
name: faker.name.fullName(),
})
totalLoaded.value++
}
resolve(true)
}, 1000)
})
}
function reloadRecords() {
scrollTo(0)
goto.value = 0
data.splice(0)
getNewTotal()
totalLoaded.value = 0
loadedPages.splice(0)
loadRecords()
}
function onClickRefreshButton() {
reloadRecords()
}
function inputAttrs() {
return {
readonly: true,
}
}
function onEnterGoto() {
scrollTo(goto.value)
}
getNewTotal()
loadRecords()
</script> |
Beta Was this translation helpful? Give feedback.
I came up with something that works for my solution... it combines useVirtualList and useScroll (fiddle).