diff options
Diffstat (limited to 'apps/files/src')
-rw-r--r-- | apps/files/src/components/VirtualList.vue | 41 |
1 files changed, 35 insertions, 6 deletions
diff --git a/apps/files/src/components/VirtualList.vue b/apps/files/src/components/VirtualList.vue index 6a415799034..dae3c8d3f42 100644 --- a/apps/files/src/components/VirtualList.vue +++ b/apps/files/src/components/VirtualList.vue @@ -16,8 +16,8 @@ :class="gridMode ? 'files-list__tbody--grid' : 'files-list__tbody--list'" data-cy-files-list-tbody> <component :is="dataComponent" - v-for="(item, i) in renderedItems" - :key="i" + v-for="({key, item}, i) in renderedItems" + :key="key" :visible="(i >= bufferItems || index <= bufferItems) && (i < shownItems - bufferItems)" :source="item" :index="i" @@ -34,13 +34,18 @@ </template> <script lang="ts"> -import type { File, Folder } from '@nextcloud/files' +import type { File, Folder, Node } from '@nextcloud/files' import { debounce } from 'debounce' import Vue, { PropType } from 'vue' import filesListWidthMixin from '../mixins/filesListWidth.ts' import logger from '../logger.js' +interface RecycledPoolItem { + key: string, + item: Node, +} + export default Vue.extend({ name: 'VirtualList', @@ -98,7 +103,8 @@ export default Vue.extend({ }, itemHeight() { - // 160px + 44px (name) + 15px (grid gap) + // Align with css in FilesListVirtual + // 138px + 44px (name) + 15px (grid gap) return this.gridMode ? (160 + 44 + 15) : 56 }, // Grid mode only @@ -128,11 +134,32 @@ export default Vue.extend({ return this.rowCount }, - renderedItems(): (File | Folder)[] { + renderedItems(): RecycledPoolItem[] { if (!this.isReady) { return [] } - return this.dataSources.slice(this.startIndex, this.startIndex + this.shownItems) + + const items = this.dataSources.slice(this.startIndex, this.startIndex + this.shownItems) as Node[] + + const oldItems = items.filter(item => Object.values(this.$_recycledPool).includes(item[this.dataKey])) + const oldItemsKeys = oldItems.map(item => item[this.dataKey] as string) + const unusedKeys = Object.keys(this.$_recycledPool).filter(key => !oldItemsKeys.includes(this.$_recycledPool[key])) + + return items.map(item => { + const index = Object.values(this.$_recycledPool).indexOf(item[this.dataKey]) + // If defined, let's keep the key + if (index !== -1) { + return { + key: Object.keys(this.$_recycledPool)[index], + item, + } + } + + // Get and consume reusable key or generate a new one + const key = unusedKeys.pop() || Math.random().toString(36).substr(2) + this.$_recycledPool[key] = item[this.dataKey] + return { key, item } + }) }, tbodyStyle() { @@ -174,6 +201,8 @@ export default Vue.extend({ if (this.scrollToIndex) { this.$el.scrollTop = Math.floor((this.index * this.itemHeight) / this.rowCount) + this.beforeHeight } + + this.$_recycledPool = {} as Record<string, any> }, beforeDestroy() { |