3
3
SPDX-License-Identifier: AGPL-3.0-or-later
4
4
-->
5
5
<template>
6
- <div class="log-table">
6
+ <div ref="table" class="log-table" @scroll="debouncedHandleScrollPosition ">
7
7
<LogDetailsModal v-if="currentRow"
8
8
:open.sync="isModalOpen"
9
9
:current-entry.sync="currentRow"
35
35
</td>
36
36
</tr>
37
37
38
- <LogTableRow v-for="row, rowNumber in sortedRows"
39
- :key="rowNumber"
40
- :row="row"
41
- @show-details="showDetailsForRow" />
38
+ <template v-for="(row, rowNumber) in sortedRows">
39
+ <tr v-if="row.showDummy" :key="rowNumber" class="log-table__dummy-row" />
40
+ <LogTableRow v-else
41
+ :key="rowNumber"
42
+ :row="row"
43
+ @show-details="showDetailsForRow" />
44
+ </template>
42
45
</tbody>
43
46
<tfoot>
44
47
<tr v-if="sortedByTime !== 'ascending'">
59
62
<script setup lang="ts">
60
63
import type { ILogEntry, ISortingOptions } from '../../interfaces'
61
64
62
- import { computed, nextTick, ref } from 'vue'
65
+ import { computed, nextTick, ref, watch } from 'vue'
63
66
import { translate as t } from '@nextcloud/l10n'
64
67
import { useSettingsStore } from '../../store/settings'
65
68
import { useLogStore } from '../../store/logging'
69
+ import { debounce } from '../../utils/debounce'
66
70
67
71
import IntersectionObserver from '../IntersectionObserver.vue'
68
72
import LogDetailsModal from '../LogDetailsModal.vue'
@@ -109,11 +113,27 @@ const showDetailsForRow = (row: ILogEntry) => {
109
113
isModalOpen.value = true
110
114
}
111
115
116
+ /**
117
+ * Reference to the table container, used to track current scroll position
118
+ */
119
+ const table = ref<HTMLElement>()
112
120
/**
113
121
* Reference to the table body, used for keeping scroll position on loading more entries
114
122
*/
115
123
const tableBody = ref<HTMLElement>()
116
124
125
+ /**
126
+ * Get index of the first visible row, to define visible scope around it
127
+ * Rest entries should be rendered as dummy nodes, to reduce load while render
128
+ */
129
+ const firstVisibleRowIndex = ref(0)
130
+
131
+ const handleScrollPosition = () => {
132
+ firstVisibleRowIndex.value = Math.floor((table.value?.scrollTop ?? 0) / 42)
133
+ }
134
+
135
+ const debouncedHandleScrollPosition = debounce(handleScrollPosition, 100)
136
+
117
137
/**
118
138
* Load older log entries and ensure that the view sticks at the previous top element
119
139
*/
@@ -144,7 +164,17 @@ const sortedRows = computed(() => {
144
164
const order = (fn: SortLogFunction, type: string, a: ILogEntry, b: ILogEntry) => type === 'ascending' ? fn(a, b) : (type === 'descending' ? fn(b, a) : 0)
145
165
146
166
sorted.sort((a, b) => order(byLevel, sortedByLevel.value, a, b) || order(byApp, sortedByApp.value, a, b) || order(byTime, sortedByTime.value, a, b))
147
- return sorted
167
+ return sorted.map((row, index) => ({
168
+ ...row,
169
+ showDummy: index > firstVisibleRowIndex.value + 50 || index < firstVisibleRowIndex.value - 50,
170
+ }))
171
+ })
172
+
173
+ /**
174
+ * Recalculate the visible scope of log entries on list update
175
+ */
176
+ watch(sortedRows, () => {
177
+ handleScrollPosition()
148
178
})
149
179
</script>
150
180
@@ -160,6 +190,10 @@ const sortedRows = computed(() => {
160
190
table-layout: fixed;
161
191
}
162
192
193
+ &__dummy-row {
194
+ height: 42px;
195
+ }
196
+
163
197
&__load-more {
164
198
text-align: center;
165
199
padding-block: 4px;
0 commit comments