Skip to content

Commit fabc596

Browse files
author
FrancescoMauto
committed
[DSC-2308] add: skeleton loader on browse elements sub components
1 parent ae65834 commit fabc596

5 files changed

Lines changed: 102 additions & 22 deletions

File tree

src/app/shared/browse-most-elements/abstract-browse-elements.component.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { followLink } from '../utils/follow-link-config.model';
22
import { CollectionElementLinkType } from '../object-collection/collection-element-link.type';
33
import { Component, Input, OnChanges, OnInit, PLATFORM_ID, inject } from '@angular/core';
4-
import { isPlatformServer } from '@angular/common';
4+
import { isPlatformBrowser } from '@angular/common';
55

66
import { PaginatedSearchOptions } from '../search/models/paginated-search-options.model';
77
import { DSpaceObject } from '../../core/shared/dspace-object.model';
@@ -17,6 +17,7 @@ import {
1717
} from '../../core/shared/operators';
1818
import { APP_CONFIG } from '../../../config/app-config.interface';
1919
import { BehaviorSubject, Observable, mergeMap } from 'rxjs';
20+
import { tap } from 'rxjs/operators';
2021
import { Item } from '../../core/shared/item.model';
2122
import { getItemPageRoute } from '../../item-page/item-page-routing-paths';
2223
import { TopSection } from '../../core/layout/models/section.model';
@@ -77,12 +78,18 @@ export abstract class AbstractBrowseElementsComponent implements OnInit, OnChang
7778

7879
searchResultArray$: Observable<DSpaceObject[]>;
7980

81+
isLoading = true;
82+
83+
isBrowser: boolean;
84+
8085
ngOnChanges() {
8186
this.paginatedSearchOptions$?.next(this.paginatedSearchOptions);
8287
}
8388

8489
ngOnInit() {
85-
if (isPlatformServer(this.platformId)) {
90+
this.isBrowser = isPlatformBrowser(this.platformId);
91+
92+
if (!this.isBrowser) {
8693
return;
8794
}
8895
const followLinks = [];
@@ -110,6 +117,9 @@ export abstract class AbstractBrowseElementsComponent implements OnInit, OnChang
110117
toDSpaceObjectListRD(),
111118
getRemoteDataPayload(),
112119
getPaginatedListPayload(),
120+
tap(() => {
121+
this.isLoading = false;
122+
}),
113123
);
114124
}
115125

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,36 @@
11
<div class="m-2">
2-
<ng-container *ngIf="(searchResults$ | async) as searchResults">
3-
<ul *ngIf="searchResults?.hasSucceeded" class="list-unstyled">
4-
<li *ngFor="let object of searchResults?.payload?.page; let i = index; let last = last" class="mt-4 mb-4 d-flex" [class.border-bottom]="!last">
5-
<ds-listable-object-component-loader [context]="context"
6-
[index]="i"
7-
[listID]="paginatedSearchOptions.configuration"
8-
[object]="object"
9-
[showMetrics]="showMetrics"
10-
[showLabel]="showLabel"
11-
[showThumbnails]="showThumbnails"
12-
[viewMode]="paginatedSearchOptions.view"></ds-listable-object-component-loader>
13-
</li>
14-
</ul>
15-
<div *ngIf="searchResults?.hasFailed">
16-
{{ 'remote.error' | translate }}
17-
</div>
2+
<ng-container *ngIf="(searchResults$ | async) as searchResults; else skeleton">
3+
<ul *ngIf="searchResults?.hasSucceeded" class="list-unstyled">
4+
<li *ngFor="let object of searchResults?.payload?.page; let i = index; let last = last" class="mt-4 mb-4 d-flex" [class.border-bottom]="!last">
5+
<ds-listable-object-component-loader [context]="context"
6+
[index]="i"
7+
[listID]="paginatedSearchOptions.configuration"
8+
[object]="object"
9+
[showMetrics]="showMetrics"
10+
[showLabel]="showLabel"
11+
[showThumbnails]="showThumbnails"
12+
[viewMode]="paginatedSearchOptions.view"></ds-listable-object-component-loader>
13+
</li>
14+
</ul>
15+
<div *ngIf="searchResults?.hasFailed">
16+
{{ 'remote.error' | translate }}
17+
</div>
1818
</ng-container>
19-
<ds-loading *ngIf="!(searchResults$ | async)"></ds-loading>
19+
<ng-template #skeleton>
20+
<div *ngFor="let item of skeletonItems" class="skeleton-row row my-4">
21+
<div *ngIf="showThumbnails" class="col-3 col-md-2">
22+
<div class="thumbnail-skeleton">
23+
<ngx-skeleton-loader></ngx-skeleton-loader>
24+
</div>
25+
</div>
26+
<div [class.col-9]="showThumbnails" [class.col-md-10]="showThumbnails" [class.col-12]="!showThumbnails">
27+
<div class="badge-skeleton">
28+
<ngx-skeleton-loader></ngx-skeleton-loader>
29+
</div>
30+
<div class="text-skeleton">
31+
<ngx-skeleton-loader [count]="2"></ngx-skeleton-loader>
32+
</div>
33+
</div>
34+
</div>
35+
</ng-template>
2036
</div>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
:host ::ng-deep {
2+
.skeleton-row {
3+
border-bottom: 1px solid var(--bs-border-color, #dee2e6);
4+
padding-bottom: 1rem;
5+
}
6+
7+
.thumbnail-skeleton {
8+
max-width: var(--ds-thumbnail-max-width, 100%);
9+
height: 100%;
10+
11+
ngx-skeleton-loader .skeleton-loader {
12+
border-radius: 0;
13+
height: 80px;
14+
width: 100%;
15+
}
16+
}
17+
18+
.badge-skeleton {
19+
ngx-skeleton-loader .skeleton-loader {
20+
height: var(--ds-search-skeleton-text-height, 20px);
21+
width: var(--ds-search-skeleton-badge-width, 75px);
22+
}
23+
}
24+
25+
.text-skeleton {
26+
margin-top: 0.5rem;
27+
28+
ngx-skeleton-loader .skeleton-loader {
29+
height: var(--ds-search-skeleton-text-height, 20px);
30+
}
31+
}
32+
33+
ngx-skeleton-loader .skeleton-loader {
34+
background-color: var(--bs-light, #f8f9fa);
35+
box-shadow: none;
36+
}
37+
}
Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,34 @@
1-
import { Component, OnChanges, OnInit } from '@angular/core';
1+
import {
2+
Component,
3+
OnChanges,
4+
OnInit,
5+
} from '@angular/core';
6+
27
import { AbstractBrowseElementsComponent } from '../abstract-browse-elements.component';
38

49
@Component({
510
selector: 'ds-default-browse-elements',
611
templateUrl: './default-browse-elements.component.html',
7-
styleUrls: ['./default-browse-elements.component.scss']
12+
styleUrls: ['./default-browse-elements.component.scss'],
813
})
914
export class DefaultBrowseElementsComponent extends AbstractBrowseElementsComponent implements OnInit, OnChanges {
1015

1116
protected followMetricsLink: boolean;
1217
protected followThumbnailLink: boolean;
1318

19+
/**
20+
* Array used to render skeleton placeholder items.
21+
* Length matches the configured page size.
22+
*/
23+
skeletonItems: number[];
24+
1425
ngOnInit() {
1526
this.followMetricsLink = this.showMetrics ?? this.appConfig.browseBy.showMetrics;
1627
this.followThumbnailLink = this.showThumbnails ?? this.appConfig.browseBy.showThumbnails;
28+
this.skeletonItems = Array.from(
29+
{ length: this.paginatedSearchOptions?.pagination?.pageSize ?? 5 },
30+
(_, i) => i,
31+
);
1732
super.ngOnInit();
1833
}
1934
}

src/app/shared/shared.module.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ import {StickyPopoverDirective} from './metadata-link-view/sticky-popover.direct
366366
import { SortPipe } from './utils/sort.pipe';
367367
import { BrowserOnlyDirective } from './utils/browser-only.directive';
368368
import {EscapeHtmlPipe} from './utils/escape-html.pipe';
369+
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
369370
import { AuthorizedCommunitySelectorComponent } from './dso-selector/dso-selector/authorized-community-selector/authorized-community-selector.component';
370371
import { AuthorizedItemSelectorComponent } from './dso-selector/dso-selector/authorized-item-selector/authorized-item-selector.component';
371372

@@ -387,7 +388,8 @@ const MODULES = [
387388
MenuModule,
388389
NgxPaginationModule,
389390
MetricsModule,
390-
NgbModule
391+
NgbModule,
392+
NgxSkeletonLoaderModule,
391393
];
392394

393395
const ROOT_MODULES = [

0 commit comments

Comments
 (0)