aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files/src/components/VirtualList.vue
diff options
context:
space:
mode:
authorJohn Molakvoæ <skjnldsv@protonmail.com>2023-10-13 16:49:54 +0200
committerJohn Molakvoæ <skjnldsv@protonmail.com>2023-10-17 11:19:02 +0200
commit16975ae45720945776155f026835cfdaf8491383 (patch)
tree6eb6db9dee1d86a7da98c46b10d0dd9ea004dcc7 /apps/files/src/components/VirtualList.vue
parent694fd51cbaa18acbaa76a100010f00b904f96f7b (diff)
downloadnextcloud-server-16975ae45720945776155f026835cfdaf8491383.tar.gz
nextcloud-server-16975ae45720945776155f026835cfdaf8491383.zip
feat(files): grid view
Signed-off-by: John Molakvoæ <skjnldsv@protonmail.com>
Diffstat (limited to 'apps/files/src/components/VirtualList.vue')
-rw-r--r--apps/files/src/components/VirtualList.vue80
1 files changed, 57 insertions, 23 deletions
diff --git a/apps/files/src/components/VirtualList.vue b/apps/files/src/components/VirtualList.vue
index ef824d7ba91..6a415799034 100644
--- a/apps/files/src/components/VirtualList.vue
+++ b/apps/files/src/components/VirtualList.vue
@@ -11,7 +11,10 @@
</thead>
<!-- Body -->
- <tbody :style="tbodyStyle" class="files-list__tbody" data-cy-files-list-tbody>
+ <tbody :style="tbodyStyle"
+ class="files-list__tbody"
+ :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"
@@ -23,7 +26,6 @@
<!-- Footer -->
<tfoot v-show="isReady"
- ref="tfoot"
class="files-list__tfoot"
data-cy-files-list-tfoot>
<slot name="footer" />
@@ -32,16 +34,18 @@
</template>
<script lang="ts">
-import { File, Folder, debounce } from 'debounce'
-import Vue from 'vue'
-import logger from '../logger.js'
+import type { File, Folder } from '@nextcloud/files'
+import { debounce } from 'debounce'
+import Vue, { PropType } from 'vue'
-// Items to render before and after the visible area
-const bufferItems = 3
+import filesListWidthMixin from '../mixins/filesListWidth.ts'
+import logger from '../logger.js'
export default Vue.extend({
name: 'VirtualList',
+ mixins: [filesListWidthMixin],
+
props: {
dataComponent: {
type: [Object, Function],
@@ -52,26 +56,25 @@ export default Vue.extend({
required: true,
},
dataSources: {
- type: Array as () => (File | Folder)[],
- required: true,
- },
- itemHeight: {
- type: Number,
+ type: Array as PropType<(File | Folder)[]>,
required: true,
},
extraProps: {
- type: Object,
+ type: Object as PropType<Record<string, unknown>>,
default: () => ({}),
},
scrollToIndex: {
type: Number,
default: 0,
},
+ gridMode: {
+ type: Boolean,
+ default: false,
+ },
},
data() {
return {
- bufferItems,
index: this.scrollToIndex,
beforeHeight: 0,
headerHeight: 0,
@@ -86,11 +89,44 @@ export default Vue.extend({
return this.tableHeight > 0
},
+ // Items to render before and after the visible area
+ bufferItems() {
+ if (this.gridMode) {
+ return this.columnCount
+ }
+ return 3
+ },
+
+ itemHeight() {
+ // 160px + 44px (name) + 15px (grid gap)
+ return this.gridMode ? (160 + 44 + 15) : 56
+ },
+ // Grid mode only
+ itemWidth() {
+ // 160px + 15px grid gap
+ return 160 + 15
+ },
+
+ rowCount() {
+ return Math.ceil((this.tableHeight - this.headerHeight) / this.itemHeight) + (this.bufferItems / this.columnCount) * 2
+ },
+ columnCount() {
+ if (!this.gridMode) {
+ return 1
+ }
+ return Math.floor(this.filesListWidth / this.itemWidth)
+ },
+
startIndex() {
- return Math.max(0, this.index - bufferItems)
+ return Math.max(0, this.index - this.bufferItems)
},
shownItems() {
- return Math.ceil((this.tableHeight - this.headerHeight) / this.itemHeight) + bufferItems * 2
+ // If in grid mode, we need to multiply the number of rows by the number of columns
+ if (this.gridMode) {
+ return this.rowCount * this.columnCount
+ }
+
+ return this.rowCount
},
renderedItems(): (File | Folder)[] {
if (!this.isReady) {
@@ -100,11 +136,11 @@ export default Vue.extend({
},
tbodyStyle() {
- const isOverScrolled = this.startIndex + this.shownItems > this.dataSources.length
+ const isOverScrolled = this.startIndex + this.rowCount > this.dataSources.length
const lastIndex = this.dataSources.length - this.startIndex - this.shownItems
- const hiddenAfterItems = Math.min(this.dataSources.length - this.startIndex, lastIndex)
+ const hiddenAfterItems = Math.floor(Math.min(this.dataSources.length - this.startIndex, lastIndex) / this.columnCount)
return {
- paddingTop: `${this.startIndex * this.itemHeight}px`,
+ paddingTop: `${Math.floor(this.startIndex / this.columnCount) * this.itemHeight}px`,
paddingBottom: isOverScrolled ? 0 : `${hiddenAfterItems * this.itemHeight}px`,
}
},
@@ -119,7 +155,6 @@ export default Vue.extend({
mounted() {
const before = this.$refs?.before as HTMLElement
const root = this.$el as HTMLElement
- const tfoot = this.$refs?.tfoot as HTMLElement
const thead = this.$refs?.thead as HTMLElement
this.resizeObserver = new ResizeObserver(debounce(() => {
@@ -132,13 +167,12 @@ export default Vue.extend({
this.resizeObserver.observe(before)
this.resizeObserver.observe(root)
- this.resizeObserver.observe(tfoot)
this.resizeObserver.observe(thead)
this.$el.addEventListener('scroll', this.onScroll)
if (this.scrollToIndex) {
- this.$el.scrollTop = this.index * this.itemHeight + this.beforeHeight
+ this.$el.scrollTop = Math.floor((this.index * this.itemHeight) / this.rowCount) + this.beforeHeight
}
},
@@ -151,7 +185,7 @@ export default Vue.extend({
methods: {
onScroll() {
// Max 0 to prevent negative index
- this.index = Math.max(0, Math.round((this.$el.scrollTop - this.beforeHeight) / this.itemHeight))
+ this.index = Math.max(0, Math.floor(Math.round((this.$el.scrollTop - this.beforeHeight) / this.itemHeight) * this.columnCount))
this.$emit('scroll')
},
},