aboutsummaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorskjnldsv <skjnldsv@protonmail.com>2024-12-13 12:04:01 +0100
committerskjnldsv <skjnldsv@protonmail.com>2024-12-17 09:59:56 +0100
commit376a7bba7b2911c53ff9524ac0b889e502250bff (patch)
tree70d988dc522973945c5d18be3b027bf168da0663 /apps
parente7001022c75b3a818356378bb53bbfe5129a10fe (diff)
downloadnextcloud-server-376a7bba7b2911c53ff9524ac0b889e502250bff.tar.gz
nextcloud-server-376a7bba7b2911c53ff9524ac0b889e502250bff.zip
fix(files): virtual scroller item size computation
Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
Diffstat (limited to 'apps')
-rw-r--r--apps/files/src/components/VirtualList.vue59
1 files changed, 44 insertions, 15 deletions
diff --git a/apps/files/src/components/VirtualList.vue b/apps/files/src/components/VirtualList.vue
index d2b436344a5..b3d1b1002f4 100644
--- a/apps/files/src/components/VirtualList.vue
+++ b/apps/files/src/components/VirtualList.vue
@@ -55,9 +55,10 @@
import type { File, Folder, Node } from '@nextcloud/files'
import type { PropType } from 'vue'
-import { useFileListWidth } from '../composables/useFileListWidth.ts'
import { defineComponent } from 'vue'
import debounce from 'debounce'
+
+import { useFileListWidth } from '../composables/useFileListWidth.ts'
import logger from '../logger.ts'
interface RecycledPoolItem {
@@ -66,7 +67,6 @@ interface RecycledPoolItem {
}
type DataSource = File | Folder
-
type DataSourceKey = keyof DataSource
export default defineComponent({
@@ -133,20 +133,22 @@ export default defineComponent({
// Items to render before and after the visible area
bufferItems() {
if (this.gridMode) {
+ // 1 row before and after in grid mode
return this.columnCount
}
+ // 3 rows before and after
return 3
},
itemHeight() {
// Align with css in FilesListVirtual
- // 166px + 32px (name) + 16px (mtime) + 16px (padding)
- return this.gridMode ? (166 + 32 + 16 + 16) : 55
+ // 166px + 32px (name) + 16px (mtime) + 16px (padding top and bottom)
+ return this.gridMode ? (166 + 32 + 16 + 16 + 16) : 55
},
// Grid mode only
itemWidth() {
- // 166px + 16px padding
- return 166 + 16
+ // 166px + 16px x 2 (padding left and right)
+ return 166 + 16 + 16
},
rowCount() {
@@ -161,9 +163,13 @@ export default defineComponent({
/**
* Index of the first item to be rendered
+ * The index can be any file, not just the first one
+ * But the start index is the first item to be rendered,
+ * which needs to align with the column count
*/
startIndex() {
- return Math.max(0, this.index - this.bufferItems)
+ const firstColumnIndex = this.index - (this.index % this.columnCount)
+ return Math.max(0, firstColumnIndex - this.bufferItems)
},
/**
@@ -260,7 +266,7 @@ export default defineComponent({
this.tableHeight = root?.clientHeight ?? 0
logger.debug('VirtualList: resizeObserver updated')
this.onScroll()
- }, 100, false))
+ }, 100, { immediate: false }))
this.resizeObserver.observe(before)
this.resizeObserver.observe(root)
@@ -284,28 +290,51 @@ export default defineComponent({
methods: {
scrollTo(index: number) {
+ if (!this.$el) {
+ return
+ }
+
+ // Check if the content is smaller than the viewport, meaning no scrollbar
const targetRow = Math.ceil(this.dataSources.length / this.columnCount)
if (targetRow < this.rowCount) {
- logger.debug('VirtualList: Skip scrolling. nothing to scroll', { index, targetRow, rowCount: this.rowCount })
+ logger.debug('VirtualList: Skip scrolling, nothing to scroll', { index, targetRow, rowCount: this.rowCount })
return
}
- this.index = index
+
// Scroll to one row and a half before the index
- const scrollTop = (Math.floor(index / this.columnCount) - 0.5) * this.itemHeight + this.beforeHeight
- logger.debug('VirtualList: scrolling to index ' + index, { scrollTop, columnCount: this.columnCount })
+ const scrollTop = this.indexToScrollPos(index)
+ logger.debug('VirtualList: scrolling to index ' + index, { scrollTop, columnCount: this.columnCount, beforeHeight: this.beforeHeight })
this.$el.scrollTop = scrollTop
},
onScroll() {
this._onScrollHandle ??= requestAnimationFrame(() => {
this._onScrollHandle = null
- const topScroll = this.$el.scrollTop - this.beforeHeight
- const index = Math.floor(topScroll / this.itemHeight) * this.columnCount
+
+ const index = this.scrollPosToIndex(this.$el.scrollTop)
+ if (index === this.index) {
+ return
+ }
+
// Max 0 to prevent negative index
- this.index = Math.max(0, index)
+ this.index = Math.max(0, Math.floor(index))
this.$emit('scroll')
})
},
+
+ // Convert scroll position to index
+ // It should be the opposite of `indexToScrollPos`
+ scrollPosToIndex(scrollPos: number): number {
+ const topScroll = scrollPos - this.beforeHeight
+ // Max 0 to prevent negative index
+ return Math.max(0, Math.floor(topScroll / this.itemHeight)) * this.columnCount
+ },
+
+ // Convert index to scroll position
+ // It should be the opposite of `scrollPosToIndex`
+ indexToScrollPos(index: number): number {
+ return (Math.floor(index / this.columnCount) - 0.5) * this.itemHeight + this.beforeHeight
+ },
},
})
</script>