Skip to content
96 changes: 96 additions & 0 deletions apps/files/src/components/FilesListVirtual.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
:data-component="userConfig.grid_view ? FileEntryGrid : FileEntry"
data-key="source"
:data-sources="nodes"
:navigating="navigating"
:gridMode="userConfig.grid_view"
:extraProps="{
isMimeAvailable,
Expand Down Expand Up @@ -47,6 +48,24 @@
<slot name="empty" />
</template>

<!-- Loading skeleton while navigating -->
<template #navigating>
<div class="files-list__loading-skeleton">
<!-- Skeleton rows -->
<div
v-for="index in 5"
:key="`skeleton-${index}`"
class="files-list__skeleton-row">
<!-- Checkbox skeleton -->
<div class="files-list__skeleton-checkbox" />
<!-- Icon skeleton -->
<div class="files-list__skeleton-icon" />
<!-- Name skeleton -->
<div class="files-list__skeleton-name" />
</div>
</div>
</template>

<!-- Tfoot-->
<template #footer>
<FilesListTableFooter
Expand Down Expand Up @@ -118,6 +137,11 @@ export default defineComponent({
type: String,
required: true,
},

navigating: {
type: Boolean,
default: false,
},
},

setup(props) {
Expand Down Expand Up @@ -578,6 +602,78 @@ export default defineComponent({
height: 100%;
}

// Loading skeleton
.files-list__loading-skeleton {
display: flex;
flex-direction: column;
width: 100%;
}

.files-list__skeleton-row {
display: flex;
align-items: center;
width: 100%;
height: var(--row-height);
border-block-end: 1px solid var(--color-border);
box-sizing: border-box;
gap: 8px;
padding: 0 8px;
}

.files-list__skeleton-checkbox {
width: var(--checkbox-size);
height: var(--checkbox-size);
border-radius: 4px;
background: linear-gradient(
90deg,
var(--color-placeholder-light),
var(--color-placeholder-dark),
var(--color-placeholder-light)
);
background-size: 200% 100%;
animation: skeleton-loading 3.5s infinite;
flex-shrink: 0;
}

.files-list__skeleton-icon {
width: 24px;
height: 24px;
border-radius: 4px;
background: linear-gradient(
90deg,
var(--color-placeholder-light),
var(--color-placeholder-dark),
var(--color-placeholder-light)
);
background-size: 200% 100%;
animation: skeleton-loading 3.5s infinite;
flex-shrink: 0;
}

.files-list__skeleton-name {
flex: 1 1 auto;
height: 20px;
border-radius: 4px;
background: linear-gradient(
90deg,
var(--color-placeholder-light),
var(--color-placeholder-dark),
var(--color-placeholder-light)
);
background-size: 200% 100%;
animation: skeleton-loading 3.5s infinite;
min-width: 100px;
}

@keyframes skeleton-loading {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}

tr {
position: relative;
display: flex;
Expand Down
21 changes: 17 additions & 4 deletions apps/files/src/components/VirtualList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@
</div>

<div
v-if="dataSources.length === 0"
v-if="!navigating && dataSources.length === 0"
class="files-list__empty">
<slot name="empty" />
</div>

<table
:aria-hidden="dataSources.length === 0"
:inert="dataSources.length === 0"
:aria-hidden="dataSources.length === 0 && !navigating"
:inert="dataSources.length === 0 && !navigating"
class="files-list__table"
:class="{
'files-list__table--hidden': dataSources.length === 0,
'files-list__table--hidden': dataSources.length === 0 && !navigating,
}">
<!-- Accessibility table caption for screen readers -->
<caption v-if="caption" class="hidden-visually">
Expand All @@ -42,6 +42,7 @@

<!-- Body -->
<tbody
v-if="!navigating"
:style="tbodyStyle"
class="files-list__tbody"
data-cy-files-list-tbody>
Expand All @@ -56,12 +57,19 @@

<!-- Footer -->
<tfoot
v-if="!navigating"
ref="footer"
class="files-list__tfoot"
data-cy-files-list-tfoot>
<slot name="footer" />
</tfoot>
</table>

<div
v-if="navigating"
class="files-list__navigating">
<slot name="navigating" />
</div>
</div>
</template>

Expand Down Expand Up @@ -123,6 +131,11 @@ export default defineComponent({
type: String,
default: '',
},

navigating: {
type: Boolean,
default: false,
},
},

setup() {
Expand Down
4 changes: 4 additions & 0 deletions apps/files/src/views/FilesList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
<FilesListVirtual
v-else
ref="filesListVirtual"
:navigating="navigating"
:currentFolder
:currentView
:nodes="dirContentsSorted"
Expand Down Expand Up @@ -310,6 +311,7 @@ export default defineComponent({

loading: true,
loadingAction: null as string | null,
navigating: false,
error: null as string | null,
controller: new AbortController(),
promise: null as Promise<ContentsWithRoot> | null,
Expand Down Expand Up @@ -475,6 +477,7 @@ export default defineComponent({
}

logger.debug('View changed', { newView, oldView })
this.navigating = true
this.selectionStore.reset()
this.fetchContent()
},
Expand Down Expand Up @@ -607,6 +610,7 @@ export default defineComponent({
this.error = humanizeWebDAVError(error)
} finally {
this.loading = false
this.navigating = false
}
},

Expand Down
Loading