Fix: Improve footer responsiveness and download button on the full item page (#4218)

* fix: footer and download button responsiveness

* Revert styling changes to the download button on the simple item page

* fix: for restrict bitstrream show only lock icon

* ix: display download icon consistently across all item pages

* fix embargo label responsiveness

* Recommit without json5 changes

* Recommit without json5 changes

* Recommit without json5 changes
This commit is contained in:
Jesiel Viana
2025-05-06 14:21:18 -03:00
committed by GitHub
parent b15e9d74d6
commit 8757712905
11 changed files with 170 additions and 101 deletions

View File

@@ -49,8 +49,8 @@
<!-- Grid container -->
<!-- Copyright -->
<div class="bottom-footer p-1 d-flex justify-content-center align-items-center text-white">
<div class="content-container">
<div class="bottom-footer p-1 d-flex flex-column flex-md-row justify-content-center align-items-center text-white">
<div class="content-container align-self-center">
<p class="m-0">
<a class="text-white"
href="http://www.dspace.org/" role="link" tabindex="0">{{ 'footer.link.dspace' | translate}}</a>
@@ -85,7 +85,7 @@
</ul>
</div>
@if (coarLdnEnabled$ | async) {
<div class="notify-enabled text-white">
<div class="notify-enabled text-white align-self-end">
<a class="coar-notify-support-route" routerLink="info/coar-notify-support" role="link" tabindex="0">
<img class="n-coar" src="assets/images/n-coar.svg" [attr.alt]="'menu.header.image.logo' | translate" />
{{ 'footer.link.coar-notify-support' | translate }}
@@ -94,4 +94,4 @@
}
</div>
<!-- Copyright -->
</footer>
</footer>

View File

@@ -23,9 +23,8 @@
.bottom-footer {
.notify-enabled {
position: absolute;
bottom: 4px;
right: 0;
position: relative;
margin-top: 4px;
.coar-notify-support-route {
padding: 0 calc(var(--bs-spacer) / 2);
@@ -37,7 +36,11 @@
margin-bottom: 8.5px;
}
margin-top: 20px;
@media screen and (min-width: map-get($grid-breakpoints, md)) {
position: absolute;
bottom: 4px;
right: 0;
}
}
ul {
li {

View File

@@ -1,84 +1,108 @@
<ds-metadata-field-wrapper [label]="label | translate">
<div *ngVar="(originals$ | async)?.payload as originals">
@if (hasValuesInBundle(originals)) {
<div>
<h3 class="h5 simple-view-element-header">{{"item.page.filesection.original.bundle" | translate}}</h3>
@if (originals?.page?.length > 0) {
<ds-pagination
[hideGear]="true"
[hidePagerWhenSinglePage]="true"
[paginationOptions]="originalOptions"
[collectionSize]="originals?.totalElements"
[retainScrollPosition]="true">
@for (file of originals?.page; track file) {
<div class="file-section row mb-3">
<div class="col-3">
<ds-thumbnail [thumbnail]="(file.thumbnail | async)?.payload"></ds-thumbnail>
</div>
<div class="col-7">
<dl class="row">
<dt class="col-md-4">{{"item.page.filesection.name" | translate}}</dt>
<dd class="col-md-8">{{ dsoNameService.getName(file) }}</dd>
<dt class="col-md-4">{{"item.page.filesection.size" | translate}}</dt>
<dd class="col-md-8">{{(file.sizeBytes) | dsFileSize }}</dd>
<dt class="col-md-4">{{"item.page.filesection.format" | translate}}</dt>
<dd class="col-md-8">{{(file.format | async)?.payload?.description}}</dd>
@if (file.hasMetadata('dc.description')) {
<dt class="col-md-4">{{"item.page.filesection.description" | translate}}</dt>
<dd class="col-md-8">{{file.firstMetadataValue("dc.description")}}</dd>
}
</dl>
</div>
<div class="col-2">
<ds-file-download-link [bitstream]="file" [item]="item">
{{"item.page.filesection.download" | translate}}
</ds-file-download-link>
</div>
</div>
}
</ds-pagination>
<div>
<h3 class="h5 simple-view-element-header">
{{ "item.page.filesection.original.bundle" | translate }}
</h3>
@if (originals?.page?.length > 0) {
<ds-pagination [hideGear]="true" [hidePagerWhenSinglePage]="true" [paginationOptions]="originalOptions"
[collectionSize]="originals?.totalElements" [retainScrollPosition]="true">
@for (file of originals?.page; track file) {
<div class="file-section row mb-3">
<div class="col-3">
<ds-thumbnail [thumbnail]="(file.thumbnail | async)?.payload"></ds-thumbnail>
</div>
<div class="col-7">
<dl class="row">
<dt class="col-md-4">
{{ "item.page.filesection.name" | translate }}
</dt>
<dd class="col-md-8">{{ dsoNameService.getName(file) }}</dd>
<dt class="col-md-4">
{{ "item.page.filesection.size" | translate }}
</dt>
<dd class="col-md-8">{{ file.sizeBytes | dsFileSize }}</dd>
<dt class="col-md-4">
{{ "item.page.filesection.format" | translate }}
</dt>
<dd class="col-md-8">
{{ (file.format | async)?.payload?.description }}
</dd>
@if (file.hasMetadata('dc.description')) {
<dt class="col-md-4">
{{ "item.page.filesection.description" | translate }}
</dt>
<dd class="col-md-8">
{{ file.firstMetadataValue("dc.description") }}
</dd>
}
</dl>
</div>
<div class="col-2">
<ds-file-download-link [showIcon]="true" [bitstream]="file" [item]="item" cssClasses="btn btn-outline-primary btn-download">
<span class="d-none d-md-inline">
{{ "item.page.filesection.download" | translate }}
</span>
</ds-file-download-link>
</div>
</div>
}
</div>
</ds-pagination>
}
</div>
}
</div>
<div *ngVar="(licenses$ | async)?.payload as licenses">
@if (hasValuesInBundle(licenses)) {
<div>
<h3 class="h5 simple-view-element-header">{{"item.page.filesection.license.bundle" | translate}}</h3>
@if (licenses?.page?.length > 0) {
<ds-pagination
[hideGear]="true"
[hidePagerWhenSinglePage]="true"
[paginationOptions]="licenseOptions"
[collectionSize]="licenses?.totalElements"
[retainScrollPosition]="true">
@for (file of licenses?.page; track file) {
<div class="file-section row">
<div class="col-3">
<ds-thumbnail [thumbnail]="(file.thumbnail | async)?.payload"></ds-thumbnail>
</div>
<div class="col-7">
<dl class="row">
<dt class="col-md-4">{{"item.page.filesection.name" | translate}}</dt>
<dd class="col-md-8">{{ dsoNameService.getName(file) }}</dd>
<dt class="col-md-4">{{"item.page.filesection.size" | translate}}</dt>
<dd class="col-md-8">{{(file.sizeBytes) | dsFileSize }}</dd>
<dt class="col-md-4">{{"item.page.filesection.format" | translate}}</dt>
<dd class="col-md-8">{{(file.format | async)?.payload?.description}}</dd>
<dt class="col-md-4">{{"item.page.filesection.description" | translate}}</dt>
<dd class="col-md-8">{{file.firstMetadataValue("dc.description")}}</dd>
</dl>
</div>
<div class="col-2">
<ds-file-download-link [bitstream]="file" [item]="item">
{{"item.page.filesection.download" | translate}}
</ds-file-download-link>
</div>
</div>
}
</ds-pagination>
<div>
<h3 class="h5 simple-view-element-header">
{{ "item.page.filesection.license.bundle" | translate }}
</h3>
@if (licenses?.page?.length > 0) {
<ds-pagination [hideGear]="true" [hidePagerWhenSinglePage]="true" [paginationOptions]="licenseOptions"
[collectionSize]="licenses?.totalElements" [retainScrollPosition]="true">
@for (file of licenses?.page; track file) {
<div class="file-section row">
<div class="col-3">
<ds-thumbnail [thumbnail]="(file.thumbnail | async)?.payload"></ds-thumbnail>
</div>
<div class="col-7">
<dl class="row">
<dt class="col-md-4">
{{ "item.page.filesection.name" | translate }}
</dt>
<dd class="col-md-8">{{ dsoNameService.getName(file) }}</dd>
<dt class="col-md-4">
{{ "item.page.filesection.size" | translate }}
</dt>
<dd class="col-md-8">{{ file.sizeBytes | dsFileSize }}</dd>
<dt class="col-md-4">
{{ "item.page.filesection.format" | translate }}
</dt>
<dd class="col-md-8">
{{ (file.format | async)?.payload?.description }}
</dd>
<dt class="col-md-4">
{{ "item.page.filesection.description" | translate }}
</dt>
<dd class="col-md-8">
{{ file.firstMetadataValue("dc.description") }}
</dd>
</dl>
</div>
<div class="col-2">
<ds-file-download-link [showIcon]="true" [bitstream]="file" [item]="item" cssClasses="btn btn-outline-primary btn-download">
<span class="d-none d-md-inline">
{{ "item.page.filesection.download" | translate }}
</span>
</ds-file-download-link>
</div>
</div>
}
</div>
</ds-pagination>
}
</div>
}
</div>
</ds-metadata-field-wrapper>

View File

@@ -5,17 +5,29 @@
[queryParams]="(bitstreamPath$| async)?.queryParams"
[target]="isBlank ? '_blank': '_self'"
[ngClass]="cssClasses"
[attr.aria-label]="('file-download-link.download' | translate) + dsoNameService.getName(bitstream)"
[attr.aria-label]="getDownloadLinkTitle(canDownload$ | async, canDownloadWithToken$ | async, dsoNameService.getName(bitstream))"
[title]="getDownloadLinkTitle(canDownload$ | async, canDownloadWithToken$ | async, dsoNameService.getName(bitstream))"
role="link"
tabindex="0">
@if ((canDownload$ | async) === false && (canDownloadWithToken$ | async) === false) {
<!-- If the user cannot download the file by auth or token, show a lock icon -->
<span role="img" [attr.aria-label]="'file-download-link.restricted' | translate" class="pr-1"><i class="fas fa-lock"></i></span>
<span role="img"
[attr.aria-label]="'file-download-link.restricted' | translate"
[title]="'file-download-link.restricted' | translate"
class="pr-1">
<i class="fas fa-lock"></i>
</span>
} @else if ((canDownloadWithToken$ | async) && (canDownload$ | async) === false) {
<!-- If the user can download the file by token, and NOT normally show a lock open icon -->
<span role="img" [attr.aria-label]="'file-download-link.secure-access' | translate" class="pr-1 request-a-copy-access-icon"><i class="fa-solid fa-lock-open" style=""></i></span>
<span role="img"
[attr.aria-label]="'file-download-link.secure-access' | translate"
[title]="'file-download-link.secure-access' | translate"
class="pr-1 request-a-copy-access-icon">
<i class="fa-solid fa-lock-open"></i>
</span>
} @else if (showIcon) {
<i class="fas fa-download d-inline"></i>
}
<!-- Otherwise, show no icon (normal download by authorized user), public access etc. -->
<ng-container *ngTemplateOutlet="content"></ng-container>
</a>

View File

@@ -1,3 +1,7 @@
.request-a-copy-access-icon {
color: var(--bs-success);
}
.btn-download{
width: fit-content;
}

View File

@@ -12,7 +12,10 @@ import {
ActivatedRoute,
RouterLink,
} from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';
import {
TranslateModule,
TranslateService,
} from '@ngx-translate/core';
import {
combineLatest as observableCombineLatest,
Observable,
@@ -75,6 +78,11 @@ export class FileDownloadLinkComponent implements OnInit {
*/
@Input() showAccessStatusBadge = true;
/**
* A boolean indicating whether the download icon should be displayed.
*/
@Input() showIcon = false;
itemRequest: ItemRequest;
bitstreamPath$: Observable<{
@@ -90,6 +98,7 @@ export class FileDownloadLinkComponent implements OnInit {
private authorizationService: AuthorizationDataService,
public dsoNameService: DSONameService,
private route: ActivatedRoute,
private translateService: TranslateService,
) {
}
@@ -153,4 +162,9 @@ export class FileDownloadLinkComponent implements OnInit {
queryParams: {},
};
}
getDownloadLinkTitle(canDownload: boolean,canDownloadWithToken: boolean, bitstreamName: string): string {
return (canDownload || canDownloadWithToken ? this.translateService.instant('file-download-link.download') :
this.translateService.instant('file-download-link.request-copy')) + bitstreamName;
}
}

View File

@@ -29,6 +29,8 @@ export class ThemedFileDownloadLinkComponent extends ThemedComponent<FileDownloa
@Input() showAccessStatusBadge: boolean;
@Input() showIcon = false;
protected inAndOutputNames: (keyof FileDownloadLinkComponent & keyof this)[] = [
'bitstream',
'item',
@@ -36,6 +38,7 @@ export class ThemedFileDownloadLinkComponent extends ThemedComponent<FileDownloa
'isBlank',
'enableRequestACopy',
'showAccessStatusBadge',
'showIcon',
];
protected getComponentName(): string {

View File

@@ -1,5 +1,5 @@
@if (showAccessStatus) {
@if ({ status: accessStatus$ | async, date: embargoDate$ | async }; as accessStatus) {
<span [class]="'badge bg-secondary access-status-list-element-badge ' + accessStatusClass">{{ accessStatus.status | translate: {date: accessStatus.date} }}</span>
<span [class]="'badge bg-secondary dont-break-out access-status-list-element-badge ' + accessStatusClass">{{ accessStatus.status | translate: {date: accessStatus.date} }}</span>
}
}

View File

@@ -1,25 +1,30 @@
<div class="thumbnail" [class.limit-width]="limitWidth">
@if (isLoading) {
<div class="thumbnail-content outer">
<div class="inner">
<div class="centered">
<ds-loading [spinner]="true"></ds-loading>
</div>
<div class="thumbnail-content outer">
<div class="inner">
<div class="centered">
<ds-loading [spinner]="true"></ds-loading>
</div>
</div>
</div>
}
<!-- don't use *ngIf="!isLoading" so the thumbnail can load in while the animation is playing -->
@if (src !== null) {
<img class="thumbnail-content img-fluid" [ngClass]="{'d-none': isLoading}"
[src]="src | dsSafeUrl" [alt]="alt | translate" (error)="errorHandler()" (load)="successHandler()">
}
@if (src === null && !isLoading) {
<div class="thumbnail-content outer">
<div class="inner">
<div class="thumbnail-placeholder centered lead">
{{ placeholder | translate }}
</div>
<img
class="thumbnail-content img-fluid"
[ngClass]="{ 'd-none': isLoading }"
[src]="src | dsSafeUrl"
[alt]="alt | translate"
(error)="errorHandler()"
(load)="successHandler()"
/>
} @if (src === null && !isLoading) {
<div class="thumbnail-content outer">
<div class="inner">
<div class="thumbnail-placeholder centered">
{{ placeholder | translate }}
</div>
</div>
</div>
}
</div>

View File

@@ -7061,4 +7061,6 @@
"embargo.listelement.badge": "Embargo until {{ date }}",
"metadata-export-search.submit.error.limit-exceeded": "Only the first {{limit}} items will be exported",
"file-download-link.request-copy": "Request a copy of ",
}