mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
[DURACOM-303] add grid layout, make SSR enabling configurable, minor restyle of skeletons
This commit is contained in:
@@ -25,6 +25,14 @@ ssr:
|
|||||||
inlineCriticalCss: false
|
inlineCriticalCss: false
|
||||||
# Path prefixes to enable SSR for. By default these are limited to paths of primary DSpace objects.
|
# Path prefixes to enable SSR for. By default these are limited to paths of primary DSpace objects.
|
||||||
paths: [ '/home', '/items/', '/entities/', '/collections/', '/communities/', '/bitstream/', '/bitstreams/', '/handle/' ]
|
paths: [ '/home', '/items/', '/entities/', '/collections/', '/communities/', '/bitstream/', '/bitstreams/', '/handle/' ]
|
||||||
|
# Whether to enable rendering of Search component on SSR.
|
||||||
|
# If set to true the component will be included in the HTML returned from the server side rendering.
|
||||||
|
# If set to false the component will not be included in the HTML returned from the server side rendering.
|
||||||
|
enableSearchComponent: false,
|
||||||
|
# Whether to enable rendering of Browse component on SSR.
|
||||||
|
# If set to true the component will be included in the HTML returned from the server side rendering.
|
||||||
|
# If set to false the component will not be included in the HTML returned from the server side rendering.
|
||||||
|
enableBrowseComponent: false,
|
||||||
|
|
||||||
# The REST API server settings
|
# The REST API server settings
|
||||||
# NOTE: these settings define which (publicly available) REST API to use. They are usually
|
# NOTE: these settings define which (publicly available) REST API to use. They are usually
|
||||||
|
@@ -23,6 +23,7 @@ import { Community } from '../../core/shared/community.model';
|
|||||||
import { APP_CONFIG, AppConfig } from '../../../config/app-config.interface';
|
import { APP_CONFIG, AppConfig } from '../../../config/app-config.interface';
|
||||||
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
|
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
|
||||||
import { isPlatformServer } from "@angular/common";
|
import { isPlatformServer } from "@angular/common";
|
||||||
|
import { environment } from "../../../environments/environment";
|
||||||
|
|
||||||
export const BBM_PAGINATION_ID = 'bbm';
|
export const BBM_PAGINATION_ID = 'bbm';
|
||||||
|
|
||||||
@@ -147,6 +148,7 @@ export class BrowseByMetadataPageComponent implements OnInit, OnDestroy {
|
|||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
pageSize: this.appConfig.browseBy.pageSize,
|
pageSize: this.appConfig.browseBy.pageSize,
|
||||||
});
|
});
|
||||||
|
this.renderOnServerSide = environment.ssr.enableBrowseComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Component, Inject, Input, OnInit } from '@angular/core';
|
import { Component, EventEmitter, Inject, Input, OnInit, Output } from '@angular/core';
|
||||||
|
|
||||||
import { BehaviorSubject, Observable, of as observableOf } from 'rxjs';
|
import { BehaviorSubject, Observable, of as observableOf } from 'rxjs';
|
||||||
import { filter, map, startWith, switchMap, take } from 'rxjs/operators';
|
import { filter, map, startWith, switchMap, take } from 'rxjs/operators';
|
||||||
@@ -43,6 +43,8 @@ export class SearchFilterComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
@Input() scope: string;
|
@Input() scope: string;
|
||||||
|
|
||||||
|
@Output() isVisibilityComputed = new EventEmitter<boolean>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True when the filter is 100% collapsed in the UI
|
* True when the filter is 100% collapsed in the UI
|
||||||
*/
|
*/
|
||||||
@@ -99,6 +101,9 @@ export class SearchFilterComponent implements OnInit {
|
|||||||
this.filterService.expand(this.filter.name);
|
this.filterService.expand(this.filter.name);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.active$.pipe(take(1)).subscribe(() => {
|
||||||
|
this.isVisibilityComputed.emit(true);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,11 +1,12 @@
|
|||||||
<h3>{{"search.filters.head" | translate}}</h3>
|
<h3>{{"search.filters.head" | translate}}</h3>
|
||||||
<div *ngIf="(filters | async)?.hasSucceeded; else skeleton;">
|
<div *ngIf="(filters | async)?.hasSucceeded">
|
||||||
<div *ngFor="let filter of (filters | async)?.payload; trackBy: trackUpdate">
|
<div *ngFor="let filter of (filters | async)?.payload; trackBy: trackUpdate">
|
||||||
<ds-search-filter [scope]="currentScope" [filter]="filter" [inPlaceSearch]="inPlaceSearch" [refreshFilters]="refreshFilters"></ds-search-filter>
|
<ds-search-filter (isVisibilityComputed)="countFiltersWithComputedVisibility($event)" [scope]="currentScope" [filter]="filter" [inPlaceSearch]="inPlaceSearch" [refreshFilters]="refreshFilters"></ds-search-filter>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ng-template #skeleton>
|
|
||||||
|
<ng-container *ngIf="filtersWithComputedVisibility !== (filters | async)?.payload?.length">
|
||||||
<ngx-skeleton-loader [count]="defaultFilterCount"/>
|
<ngx-skeleton-loader [count]="defaultFilterCount"/>
|
||||||
</ng-template>
|
</ng-container>
|
||||||
<a class="btn btn-primary" [routerLink]="[searchLink]" [queryParams]="clearParams | async" queryParamsHandling="merge" role="button"><i class="fas fa-undo"></i> {{"search.filters.reset" | translate}}</a>
|
<a class="btn btn-primary" [routerLink]="[searchLink]" [queryParams]="clearParams | async" queryParamsHandling="merge" role="button"><i class="fas fa-undo"></i> {{"search.filters.reset" | translate}}</a>
|
||||||
|
@@ -61,6 +61,11 @@ export class SearchFiltersComponent implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
searchLink: string;
|
searchLink: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters for which visibility has been computed
|
||||||
|
*/
|
||||||
|
filtersWithComputedVisibility = 0;
|
||||||
|
|
||||||
subs = [];
|
subs = [];
|
||||||
defaultFilterCount: number;
|
defaultFilterCount: number;
|
||||||
|
|
||||||
@@ -114,4 +119,10 @@ export class SearchFiltersComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
countFiltersWithComputedVisibility(computed: boolean) {
|
||||||
|
if (computed) {
|
||||||
|
this.filtersWithComputedVisibility += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,17 +1,17 @@
|
|||||||
<div class="row flex-nowrap">
|
<div class="row flex-nowrap">
|
||||||
<div class="info-skeleton mb-2 col-12">
|
<div [class.mb-2]="(viewMode$ | async) === ViewMode.ListElement" class="info-skeleton col-12">
|
||||||
<ngx-skeleton-loader/>
|
<ngx-skeleton-loader/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@for (result of loadingResults; track result) {
|
@if((viewMode$ | async) === ViewMode.ListElement) {
|
||||||
|
@for (result of loadingResults; track result) {
|
||||||
<div class="row flex-nowrap result-row">
|
<div class="row flex-nowrap result-row">
|
||||||
@if(showThumbnails) {
|
@if(showThumbnails) {
|
||||||
<div class="thumbnail-skeleton">
|
<div class="thumbnail-skeleton">
|
||||||
<ngx-skeleton-loader/>
|
<ngx-skeleton-loader/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<div [class.col-9]="showThumbnails" [class.col-md-12]="!showThumbnails">
|
<div [class.col-9]="showThumbnails" [class.col-md-12]="!showThumbnails">
|
||||||
<div class="badge-skeleton">
|
<div class="badge-skeleton">
|
||||||
<ngx-skeleton-loader/>
|
<ngx-skeleton-loader/>
|
||||||
@@ -21,4 +21,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
} @else if ((viewMode$ | async) === ViewMode.GridElement) {
|
||||||
|
<div class="card-columns row">
|
||||||
|
@for (result of loadingResults; track result) {
|
||||||
|
<div class="card-column col col-sm-6 col-lg-4">
|
||||||
|
<div class="card-skeleton">
|
||||||
|
<ngx-skeleton-loader/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
:host ::ng-deep {
|
:host ::ng-deep {
|
||||||
|
--ds-wrapper-grid-spacing: calc(var(--bs-spacer) / 2);
|
||||||
|
|
||||||
.info-skeleton, .badge-skeleton, .text-skeleton{
|
.info-skeleton, .badge-skeleton, .text-skeleton{
|
||||||
ngx-skeleton-loader .skeleton-loader {
|
ngx-skeleton-loader .skeleton-loader {
|
||||||
height: var(--ds-search-skeleton-text-height);
|
height: var(--ds-search-skeleton-text-height);
|
||||||
@@ -27,13 +29,31 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-skeleton {
|
||||||
|
ngx-skeleton-loader .skeleton-loader {
|
||||||
|
height: var(--ds-search-skeleton-card-height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ngx-skeleton-loader .skeleton-loader {
|
ngx-skeleton-loader .skeleton-loader {
|
||||||
background-color: var(--bs-light);
|
background-color: var(--bs-light);
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-columns {
|
||||||
|
margin-left: calc(-1 * var(--ds-wrapper-grid-spacing));
|
||||||
|
margin-right: calc(-1 * var(--ds-wrapper-grid-spacing));
|
||||||
|
column-gap: 0;
|
||||||
|
|
||||||
|
.card-column {
|
||||||
|
padding-left: var(--ds-wrapper-grid-spacing);
|
||||||
|
padding-right: var(--ds-wrapper-grid-spacing);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.result-row {
|
.result-row {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,10 +1,17 @@
|
|||||||
|
import {
|
||||||
|
AsyncPipe,
|
||||||
|
NgForOf,
|
||||||
|
} from '@angular/common';
|
||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
Input,
|
Input,
|
||||||
OnInit,
|
OnInit,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
import { SearchService } from '../../../../core/shared/search/search.service';
|
||||||
|
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||||
import { hasValue } from '../../../empty.util';
|
import { hasValue } from '../../../empty.util';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -12,6 +19,8 @@ import { hasValue } from '../../../empty.util';
|
|||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [
|
imports: [
|
||||||
NgxSkeletonLoaderModule,
|
NgxSkeletonLoaderModule,
|
||||||
|
AsyncPipe,
|
||||||
|
NgForOf,
|
||||||
],
|
],
|
||||||
templateUrl: './search-results-skeleton.component.html',
|
templateUrl: './search-results-skeleton.component.html',
|
||||||
styleUrl: './search-results-skeleton.component.scss',
|
styleUrl: './search-results-skeleton.component.scss',
|
||||||
@@ -26,8 +35,16 @@ export class SearchResultsSkeletonComponent implements OnInit {
|
|||||||
@Input()
|
@Input()
|
||||||
textLineCount = 2;
|
textLineCount = 2;
|
||||||
|
|
||||||
|
public viewMode$: Observable<ViewMode>;
|
||||||
|
|
||||||
public loadingResults: number[];
|
public loadingResults: number[];
|
||||||
|
|
||||||
|
protected readonly ViewMode = ViewMode;
|
||||||
|
|
||||||
|
constructor(private searchService: SearchService) {
|
||||||
|
this.viewMode$ = searchService.getViewMode();
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.loadingResults = Array.from({ length: this.numberOfResults }, (_, i) => i + 1);
|
this.loadingResults = Array.from({ length: this.numberOfResults }, (_, i) => i + 1);
|
||||||
|
|
||||||
|
@@ -1,12 +1,10 @@
|
|||||||
@if ((filters$ | async).length > 0 && isLoading()) {
|
@if ((activeFilters$ | async).length > 0 && (appliedFilters$ | async).length === 0) {
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="d-flex">
|
<div class="filters-badge-skeleton-container">
|
||||||
@for (filter of (filters$| async); track filter.key) {
|
<div class="filter-badge-skeleton">
|
||||||
<div class="filter-badge-skeleton mr-2">
|
<ngx-skeleton-loader [count]="(activeFilters$ | async).length" />
|
||||||
<ngx-skeleton-loader/>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -4,7 +4,14 @@
|
|||||||
background-color: var(--bs-light);
|
background-color: var(--bs-light);
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
width: var(--ds-search-skeleton-filter-badge-width);
|
width: var(--ds-search-skeleton-filter-badge-width);
|
||||||
height: var(--ds-search-skeleton-text-height);
|
height: var(--ds-search-skeleton-badge-height);
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-right: calc(var(--bs-spacer) / 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.filters-badge-skeleton-container {
|
||||||
|
display: flex;
|
||||||
|
max-height: var(--ds-search-skeleton-badge-height);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -14,6 +14,7 @@ import { PaginatedSearchOptions } from '../models/paginated-search-options.model
|
|||||||
import { SearchFilter } from "../models/search-filter.model";
|
import { SearchFilter } from "../models/search-filter.model";
|
||||||
import { Observable } from "rxjs";
|
import { Observable } from "rxjs";
|
||||||
import { SearchConfigurationService } from "../../../core/shared/search/search-configuration.service";
|
import { SearchConfigurationService } from "../../../core/shared/search/search-configuration.service";
|
||||||
|
import { SearchService } from "../../../core/shared/search/search.service";
|
||||||
|
|
||||||
export interface SelectionConfig {
|
export interface SelectionConfig {
|
||||||
repeatable: boolean;
|
repeatable: boolean;
|
||||||
@@ -110,7 +111,10 @@ export class SearchResultsComponent {
|
|||||||
|
|
||||||
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||||
|
|
||||||
constructor(private searchConfigService: SearchConfigurationService) {
|
constructor(
|
||||||
|
protected searchConfigService: SearchConfigurationService,
|
||||||
|
protected searchService: SearchService,
|
||||||
|
) {
|
||||||
this.filters$ = this.searchConfigService.getCurrentFilters();
|
this.filters$ = this.searchConfigService.getCurrentFilters();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -49,6 +49,7 @@ import { COLLECTION_MODULE_PATH } from '../../collection-page/collection-page-ro
|
|||||||
import { COMMUNITY_MODULE_PATH } from '../../community-page/community-page-routing-paths';
|
import { COMMUNITY_MODULE_PATH } from '../../community-page/community-page-routing-paths';
|
||||||
import { AppConfig, APP_CONFIG } from '../../../config/app-config.interface';
|
import { AppConfig, APP_CONFIG } from '../../../config/app-config.interface';
|
||||||
import { isPlatformServer } from "@angular/common";
|
import { isPlatformServer } from "@angular/common";
|
||||||
|
import { environment } from 'src/environments/environment';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-search',
|
selector: 'ds-search',
|
||||||
@@ -304,6 +305,7 @@ export class SearchComponent implements OnDestroy, OnInit {
|
|||||||
@Inject(PLATFORM_ID) public platformId: any,
|
@Inject(PLATFORM_ID) public platformId: any,
|
||||||
) {
|
) {
|
||||||
this.isXsOrSm$ = this.windowService.isXsOrSm();
|
this.isXsOrSm$ = this.windowService.isXsOrSm();
|
||||||
|
this.renderOnServerSide = environment.universal.enableSearchComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -10,5 +10,7 @@ export const environment: Partial<BuildConfig> = {
|
|||||||
time: false,
|
time: false,
|
||||||
inlineCriticalCss: false,
|
inlineCriticalCss: false,
|
||||||
paths: [ '/home', '/items/', '/entities/', '/collections/', '/communities/', '/bitstream/', '/bitstreams/', '/handle/' ],
|
paths: [ '/home', '/items/', '/entities/', '/collections/', '/communities/', '/bitstream/', '/bitstreams/', '/handle/' ],
|
||||||
}
|
enableSearchComponent: false,
|
||||||
|
enableBrowseComponent: false,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
@@ -13,6 +13,8 @@ export const environment: BuildConfig = {
|
|||||||
time: false,
|
time: false,
|
||||||
inlineCriticalCss: false,
|
inlineCriticalCss: false,
|
||||||
paths: [ '/home', '/items/', '/entities/', '/collections/', '/communities/', '/bitstream/', '/bitstreams/', '/handle/' ],
|
paths: [ '/home', '/items/', '/entities/', '/collections/', '/communities/', '/bitstream/', '/bitstreams/', '/handle/' ],
|
||||||
|
enableSearchComponent: false,
|
||||||
|
enableBrowseComponent: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Angular Universal server settings.
|
// Angular Universal server settings.
|
||||||
|
@@ -15,7 +15,9 @@ export const environment: Partial<BuildConfig> = {
|
|||||||
time: false,
|
time: false,
|
||||||
inlineCriticalCss: false,
|
inlineCriticalCss: false,
|
||||||
paths: [ '/home', '/items/', '/entities/', '/collections/', '/communities/', '/bitstream/', '/bitstreams/', '/handle/' ],
|
paths: [ '/home', '/items/', '/entities/', '/collections/', '/communities/', '/bitstream/', '/bitstreams/', '/handle/' ],
|
||||||
}
|
enableSearchComponent: false,
|
||||||
|
enableBrowseComponent: false,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@@ -140,6 +140,7 @@
|
|||||||
--very-dark-cyan: #215E50; // This variable represents the background color of the save cookies button
|
--very-dark-cyan: #215E50; // This variable represents the background color of the save cookies button
|
||||||
|
|
||||||
--ds-search-skeleton-text-height: 20px;
|
--ds-search-skeleton-text-height: 20px;
|
||||||
|
--ds-search-skeleton-badge-height: 18px;
|
||||||
--ds-search-skeleton-thumbnail-height: 125px;
|
--ds-search-skeleton-thumbnail-height: 125px;
|
||||||
--ds-search-skeleton-thumbnail-width: 90px;
|
--ds-search-skeleton-thumbnail-width: 90px;
|
||||||
--ds-search-skeleton-thumbnail-padding: 1em;
|
--ds-search-skeleton-thumbnail-padding: 1em;
|
||||||
@@ -148,6 +149,7 @@
|
|||||||
--ds-search-skeleton-badge-width: 75px;
|
--ds-search-skeleton-badge-width: 75px;
|
||||||
--ds-search-skeleton-filter-badge-width: 200px;
|
--ds-search-skeleton-filter-badge-width: 200px;
|
||||||
--ds-search-skeleton-info-width: 200px;
|
--ds-search-skeleton-info-width: 200px;
|
||||||
|
--ds-search-skeleton-card-height: 435px;
|
||||||
|
|
||||||
--ds-filters-skeleton-height: 40px;
|
--ds-filters-skeleton-height: 40px;
|
||||||
--ds-filters-skeleton-spacing: 12px;
|
--ds-filters-skeleton-spacing: 12px;
|
||||||
|
Reference in New Issue
Block a user