Skip to content

Commit e84cfb3

Browse files
committed
Optimize quick overview with virtual list
1 parent e10a6ee commit e84cfb3

File tree

1 file changed

+59
-35
lines changed

1 file changed

+59
-35
lines changed

packages/client/internals/QuickOverview.vue

Lines changed: 59 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
<script setup lang="ts">
2-
import { useEventListener } from '@vueuse/core'
2+
import { useEventListener, useVirtualList } from '@vueuse/core'
33
import { computed, ref, watchEffect } from 'vue'
44
import { breakpoints, showOverview, windowSize } from '../state'
55
import { currentOverviewPage, overviewRowCount } from '../logic/overview'
66
import { createFixedClicks } from '../composables/useClicks'
77
import { CLICKS_MAX } from '../constants'
88
import { useNav } from '../composables/useNav'
9+
import { slideAspect } from '../env'
910
import SlideContainer from './SlideContainer.vue'
1011
import SlideWrapper from './SlideWrapper.vue'
1112
import DrawingPreview from './DrawingPreview.vue'
@@ -41,10 +42,29 @@ const cardWidth = computed(() => {
4142
return 300
4243
})
4344
45+
const cardHeight = computed(() => cardWidth.value / slideAspect.value)
46+
4447
const rowCount = computed(() => {
45-
return Math.floor((windowSize.width.value - padding) / (cardWidth.value + gap))
48+
return Math.floor((windowSize.width.value - padding) / (cardWidth.value + 2 * gap))
49+
})
50+
51+
const numOfRows = computed(() => {
52+
return Math.ceil(slides.value.length / rowCount.value)
53+
})
54+
55+
const slideRows = computed(() => {
56+
return [...Array(numOfRows.value)].map((_, i) => ({ rowIdx: i, slides: slides.value.slice(i * rowCount.value, (i + 1) * rowCount.value).map((route, j) => ({ route, idx: i * rowCount.value + j })) }))
4657
})
4758
59+
const { list: vList, containerProps, wrapperProps } = useVirtualList(
60+
slideRows,
61+
{
62+
itemHeight: cardHeight.value + gap,
63+
itemWidth: (cardWidth.value + gap) * rowCount.value,
64+
overscan: 3,
65+
},
66+
)
67+
4868
const keyboardBuffer = ref<string>('')
4969
5070
useEventListener('keypress', (e) => {
@@ -114,47 +134,51 @@ setTimeout(() => {
114134
<div
115135
v-if="showOverview || activeSlidesLoaded"
116136
v-show="showOverview"
137+
v-bind="containerProps"
117138
class="fixed left-0 right-0 top-0 h-[calc(var(--vh,1vh)*100)] z-20 bg-main !bg-opacity-75 p-16 py-20 overflow-y-auto backdrop-blur-5px"
118139
@click="close"
119140
>
120-
<div
121-
class="grid gap-y-4 gap-x-8 w-full"
122-
:style="`grid-template-columns: repeat(auto-fit,minmax(${cardWidth}px,1fr))`"
123-
>
141+
<div v-bind="wrapperProps">
124142
<div
125-
v-for="(route, idx) of slides"
126-
:key="route.no"
127-
class="relative"
143+
v-for="{ data: { rowIdx, slides: row } } of vList"
144+
:key="rowIdx"
145+
class="flex mb-8"
128146
>
129147
<div
130-
class="inline-block border rounded overflow-hidden bg-main hover:border-primary transition"
131-
:class="(focus(idx + 1) || currentOverviewPage === idx + 1) ? 'border-primary' : 'border-main'"
132-
@click="go(route.no)"
148+
v-for="{ route, idx } of row"
149+
:key="route.no"
150+
class="relative flex-1"
133151
>
134-
<SlideContainer
135-
:key="route.no"
136-
:width="cardWidth"
137-
class="pointer-events-none"
152+
<div
153+
class="inline-block border rounded overflow-hidden bg-main hover:border-primary transition"
154+
:class="(focus(idx + 1) || currentOverviewPage === idx + 1) ? 'border-primary' : 'border-main'"
155+
@click="go(route.no)"
138156
>
139-
<SlideWrapper
140-
:clicks-context="createFixedClicks(route, CLICKS_MAX)"
141-
:route="route"
142-
render-context="overview"
143-
/>
144-
<DrawingPreview :page="route.no" />
145-
</SlideContainer>
146-
</div>
147-
<div
148-
class="absolute top-0"
149-
:style="`left: ${cardWidth + 5}px`"
150-
>
151-
<template v-if="keyboardBuffer && String(idx + 1).startsWith(keyboardBuffer)">
152-
<span class="text-green font-bold">{{ keyboardBuffer }}</span>
153-
<span class="opacity-50">{{ String(idx + 1).slice(keyboardBuffer.length) }}</span>
154-
</template>
155-
<span v-else class="opacity-50">
156-
{{ idx + 1 }}
157-
</span>
157+
<SlideContainer
158+
:key="route.no"
159+
:width="cardWidth"
160+
class="pointer-events-none"
161+
>
162+
<SlideWrapper
163+
:clicks-context="createFixedClicks(route, CLICKS_MAX)"
164+
:route="route"
165+
render-context="overview"
166+
/>
167+
<DrawingPreview :page="route.no" />
168+
</SlideContainer>
169+
</div>
170+
<div
171+
class="absolute top-0"
172+
:style="`left: ${cardWidth + 5}px`"
173+
>
174+
<template v-if="keyboardBuffer && String(idx + 1).startsWith(keyboardBuffer)">
175+
<span class="text-green font-bold">{{ keyboardBuffer }}</span>
176+
<span class="opacity-50">{{ String(idx + 1).slice(keyboardBuffer.length) }}</span>
177+
</template>
178+
<span v-else class="opacity-50">
179+
{{ idx + 1 }}
180+
</span>
181+
</div>
158182
</div>
159183
</div>
160184
</div>

0 commit comments

Comments
 (0)