Skip to content

Commit 833ea2c

Browse files
committed
feat: virtual scrolling update
Signed-off-by: John Molakvoæ <skjnldsv@protonmail.com>
1 parent c654c4a commit 833ea2c

File tree

10 files changed

+781
-74
lines changed

10 files changed

+781
-74
lines changed

apps/files/src/components/FileEntry.vue

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -157,24 +157,24 @@
157157
<script lang='ts'>
158158
import { debounce } from 'debounce'
159159
import { emit } from '@nextcloud/event-bus'
160+
import { extname } from 'path'
160161
import { formatFileSize, Permission } from '@nextcloud/files'
161162
import { Fragment } from 'vue-frag'
162-
import { extname } from 'path'
163+
import { generateUrl } from '@nextcloud/router'
163164
import { showError, showSuccess } from '@nextcloud/dialogs'
164165
import { translate } from '@nextcloud/l10n'
165-
import { generateUrl } from '@nextcloud/router'
166166
import { vOnClickOutside } from '@vueuse/components'
167167
import axios from '@nextcloud/axios'
168168
import CancelablePromise from 'cancelable-promise'
169169
import FileIcon from 'vue-material-design-icons/File.vue'
170170
import FolderIcon from 'vue-material-design-icons/Folder.vue'
171+
import moment from '@nextcloud/moment'
171172
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
172173
import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'
173174
import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
174175
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
175176
import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js'
176177
import Vue from 'vue'
177-
import type moment from 'moment'
178178
179179
import { ACTION_DETAILS } from '../actions/sidebarAction.ts'
180180
import { getFileActions, DefaultType } from '../services/FileAction.ts'
@@ -183,9 +183,9 @@ import { isCachedPreview } from '../services/PreviewService.ts'
183183
import { useActionsMenuStore } from '../store/actionsmenu.ts'
184184
import { useFilesStore } from '../store/files.ts'
185185
import { useKeyboardStore } from '../store/keyboard.ts'
186+
import { useRenamingStore } from '../store/renaming.ts'
186187
import { useSelectionStore } from '../store/selection.ts'
187188
import { useUserConfigStore } from '../store/userconfig.ts'
188-
import { useRenamingStore } from '../store/renaming.ts'
189189
import CustomElementRender from './CustomElementRender.vue'
190190
import CustomSvgIconRender from './CustomSvgIconRender.vue'
191191
import FavoriteIcon from './FavoriteIcon.vue'
@@ -489,21 +489,6 @@ export default Vue.extend({
489489
},
490490
491491
watch: {
492-
active(active, before) {
493-
if (active === false && before === true) {
494-
this.resetState()
495-
496-
// When the row is not active anymore
497-
// remove the display from the row to prevent
498-
// keyboard interaction with it.
499-
this.$el.parentNode.style.display = 'none'
500-
return
501-
}
502-
503-
// Restore default tabindex
504-
this.$el.parentNode.style.display = ''
505-
},
506-
507492
/**
508493
* When the source changes, reset the preview
509494
* and fetch the new one.
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
<!--
2+
- @copyright Copyright (c) 2023 John Molakvoæ <skjnldsv@protonmail.com>
3+
-
4+
- @author John Molakvoæ <skjnldsv@protonmail.com>
5+
-
6+
- @license GNU AGPL version 3 or any later version
7+
-
8+
- This program is free software: you can redistribute it and/or modify
9+
- it under the terms of the GNU Affero General Public License as
10+
- published by the Free Software Foundation, either version 3 of the
11+
- License, or (at your option) any later version.
12+
-
13+
- This program is distributed in the hope that it will be useful,
14+
- but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
- GNU Affero General Public License for more details.
17+
-
18+
- You should have received a copy of the GNU Affero General Public License
19+
- along with this program. If not, see <http://www.gnu.org/licenses/>.
20+
-
21+
-->
22+
<template>
23+
<tr>
24+
<th class="files-list__row-checkbox">
25+
<span class="hidden-visually">{{ t('files', 'Total rows summary') }}</span>
26+
</th>
27+
28+
<!-- Link to file -->
29+
<td class="files-list__row-name">
30+
<!-- Icon or preview -->
31+
<span class="files-list__row-icon" />
32+
33+
<!-- Summary -->
34+
<span>{{ summary }}</span>
35+
</td>
36+
37+
<!-- Actions -->
38+
<td class="files-list__row-actions" />
39+
40+
<!-- Size -->
41+
<td v-if="isSizeAvailable"
42+
class="files-list__column files-list__row-size">
43+
<span>{{ totalSize }}</span>
44+
</td>
45+
46+
<!-- Mtime -->
47+
<td v-if="isMtimeAvailable"
48+
class="files-list__column files-list__row-mtime" />
49+
50+
<!-- Custom views columns -->
51+
<th v-for="column in columns"
52+
:key="column.id"
53+
:class="classForColumn(column)">
54+
<span>{{ column.summary?.(nodes, currentView) }}</span>
55+
</th>
56+
</tr>
57+
</template>
58+
59+
<script lang="ts">
60+
import { formatFileSize } from '@nextcloud/files'
61+
import { translate } from '@nextcloud/l10n'
62+
import Vue from 'vue'
63+
64+
import { useFilesStore } from '../store/files.ts'
65+
import { usePathsStore } from '../store/paths.ts'
66+
67+
export default Vue.extend({
68+
name: 'FilesListFooter',
69+
70+
components: {
71+
},
72+
73+
props: {
74+
isMtimeAvailable: {
75+
type: Boolean,
76+
default: false,
77+
},
78+
isSizeAvailable: {
79+
type: Boolean,
80+
default: false,
81+
},
82+
nodes: {
83+
type: Array,
84+
required: true,
85+
},
86+
summary: {
87+
type: String,
88+
default: '',
89+
},
90+
filesListWidth: {
91+
type: Number,
92+
default: 0,
93+
},
94+
},
95+
96+
setup() {
97+
const pathsStore = usePathsStore()
98+
const filesStore = useFilesStore()
99+
return {
100+
filesStore,
101+
pathsStore,
102+
}
103+
},
104+
105+
computed: {
106+
currentView() {
107+
return this.$navigation.active
108+
},
109+
110+
dir() {
111+
// Remove any trailing slash but leave root slash
112+
return (this.$route?.query?.dir || '/').replace(/^(.+)\/$/, '$1')
113+
},
114+
115+
currentFolder() {
116+
if (!this.currentView?.id) {
117+
return
118+
}
119+
120+
if (this.dir === '/') {
121+
return this.filesStore.getRoot(this.currentView.id)
122+
}
123+
const fileId = this.pathsStore.getPath(this.currentView.id, this.dir)
124+
return this.filesStore.getNode(fileId)
125+
},
126+
127+
columns() {
128+
// Hide columns if the list is too small
129+
if (this.filesListWidth < 512) {
130+
return []
131+
}
132+
return this.currentView?.columns || []
133+
},
134+
135+
totalSize() {
136+
// If we have the size already, let's use it
137+
if (this.currentFolder?.size) {
138+
return formatFileSize(this.currentFolder.size, true)
139+
}
140+
141+
// Otherwise let's compute it
142+
return formatFileSize(this.nodes.reduce((total, node) => total + node.size || 0, 0), true)
143+
},
144+
},
145+
146+
methods: {
147+
classForColumn(column) {
148+
return {
149+
'files-list__row-column-custom': true,
150+
[`files-list__row-${this.currentView.id}-${column.id}`]: true,
151+
}
152+
},
153+
154+
t: translate,
155+
},
156+
})
157+
</script>
158+
159+
<style scoped lang="scss">
160+
// Scoped row
161+
tr {
162+
padding-bottom: 300px;
163+
border-top: 1px solid var(--color-border);
164+
// Prevent hover effect on the whole row
165+
background-color: transparent !important;
166+
border-bottom: none !important;
167+
}
168+
169+
td {
170+
user-select: none;
171+
// Make sure the cell colors don't apply to column headers
172+
color: var(--color-text-maxcontrast) !important;
173+
}
174+
175+
</style>

0 commit comments

Comments
 (0)