1
0
mirror of https://github.com/bpatrik/pigallery2.git synced 2025-01-08 04:03:48 +02:00

Create customizable lightbox text. fixes #801

This commit is contained in:
Patrik J. Braun 2023-12-26 00:36:50 +01:00
parent 7c3bce7179
commit e68fbbbe44
5 changed files with 371 additions and 204 deletions

File diff suppressed because one or more lines are too long

View File

@ -386,6 +386,7 @@ Marker.prototype.options.icon = MarkerFactory.defIcon;
CookieService,
GPXFilesFilterPipe,
MDFilesFilterPipe,
FileSizePipe,
DatePipe
],
bootstrap: [AppComponent],

View File

@ -98,9 +98,8 @@
vertical-align: top;
}
.controls-caption {
.controls-title {
opacity: 0.7;
top: 0;
position: absolute;
height: initial;
text-align: left;
@ -110,6 +109,18 @@
border-radius: 5px !important;
}
.controls-subtitle-text{
font-size: .6em;
}
.controls-top-left-title {
top: 0;
}
.controls-bottom-left-title {
bottom: 2rem;
}
.controls-zoom {
bottom: 0;
@ -291,7 +302,7 @@ input[type="range"].zoom-progress::-moz-range-track {
opacity: 0.0;
}
.controls-caption.controls-nodim {
.controls-title.controls-nodim {
opacity: 0.7;
}

View File

@ -1,14 +1,19 @@
<div id="controllers-container" #root>
<div [class.dim-controls]="controllersDimmed" class="controls-caption"
<div [class.dim-controls]="controllersDimmed" class="controls-title controls-top-left-title"
[class.controls-nodim]="lightboxService.captionAlwaysOn"
*ngIf="Title">{{Title}}</div>
*ngIf="TopLeftTitle || TopLeftSubtitle">
<div class="controls-title-text" *ngIf="TopLeftTitle">{{ TopLeftTitle }}</div>
<div class="controls-subtitle-text" *ngIf="TopLeftSubtitle">{{ TopLeftSubtitle }}</div>
</div>
<div [class.dim-controls]="controllersDimmed"
class="controls controls-top">
<div class="controls-background rounded-start-bottom">
<div class="highlight control-button" (click)="toggleInfoPanel.emit()"
title="info key: i" i18n-title>
<ng-icon name="ionInformationOutline"></ng-icon>
<ng-icon name="ionInformationOutline"></ng-icon>
</div>
<div *ngIf="fullScreenService.isFullScreenEnabled()"
@ -23,7 +28,7 @@
class="highlight control-button"
(click)="toggleFullScreen.emit(true)"
title="toggle fullscreen, key: f" i18n-title>
<ng-icon size="0.85em"
<ng-icon size="0.85em"
name="ionExpandOutline"></ng-icon>
</div>
@ -32,7 +37,7 @@
class="border-0 highlight control-button"
data-bs-auto-close="outside"
aria-controls="dropdown-basic">
<ng-icon size="1.2em" name="ionMenuOutline"></ng-icon>
<ng-icon size="1.2em" name="ionMenuOutline"></ng-icon>
</button>
<ul id="dropdown-basic" *dropdownMenu class="dropdown-menu dropdown-menu-right"
role="menu" aria-labelledby="button-basic">
@ -141,7 +146,7 @@
<div class="face-box"
[class.controls-nodim]="lightboxService.facesAlwaysOn"></div>
<span class="face-name"
[class.controls-nodim]="lightboxService.facesAlwaysOn">{{face.name}}</span>
[class.controls-nodim]="lightboxService.facesAlwaysOn">{{ face.name }}</span>
</a>
</ng-container>
<ng-container *ngIf="!searchEnabled">
@ -157,7 +162,7 @@
</div>
<span class="face-name"
[class.controls-nodim]="lightboxService.facesAlwaysOn"
>{{face.name}}</span>
>{{ face.name }}</span>
</div>
</ng-container>
</div>
@ -179,6 +184,14 @@
#canvas width="55px" height="55px"></canvas>
</div>
<div [class.dim-controls]="controllersDimmed" class="controls-title controls-bottom-left-title"
[class.controls-nodim]="lightboxService.captionAlwaysOn"
*ngIf="BottomLeftTitle || BottomLeftSubtitle">
<div class="controls-title-text" *ngIf="BottomLeftTitle">{{ BottomLeftTitle }}</div>
<div class="controls-subtitle-text " *ngIf="BottomLeftSubtitle">{{ BottomLeftSubtitle }}</div>
</div>
<div [class.dim-controls]="controllersDimmed" class="controls controls-zoom row mb-3" *ngIf="Zoom > 1">
<div class="col-1 col-md-4">
@ -194,7 +207,6 @@
</div>
</div>
<div [class.dim-controls]="controllersDimmed" class="controls controls-playback"
*ngIf="zoom == 1 && activePhoto && activePhoto.gridMedia.isPhoto()">
<div class="controls-background rounded-start-top pe-1 pb-1">

View File

@ -11,12 +11,17 @@ import {SearchQueryTypes, TextSearch, TextSearchQueryMatchTypes,} from '../../..
import {AuthenticationService} from '../../../../model/network/authentication.service';
import {LightboxService} from '../lightbox.service';
import {GalleryCacheService} from '../../cache.gallery.service';
import {Utils} from '../../../../../../common/Utils';
import {FileSizePipe} from '../../../../pipes/FileSizePipe';
import {DatePipe} from '@angular/common';
import {LightBoxTitleTexts} from '../../../../../../common/config/public/ClientConfig';
export enum PlayBackStates {
Paused = 1,
Play = 2,
}
@Component({
selector: 'app-lightbox-controls',
styleUrls: ['./controls.lightbox.gallery.component.css', './inputrange.css'],
@ -61,10 +66,12 @@ export class ControlsLightboxComponent implements OnDestroy, OnInit, OnChanges {
private prevZoom = 1;
constructor(
public lightboxService: LightboxService,
public fullScreenService: FullScreenService,
private authService: AuthenticationService,
private cacheService: GalleryCacheService
public lightboxService: LightboxService,
public fullScreenService: FullScreenService,
private authService: AuthenticationService,
private cacheService: GalleryCacheService,
private fileSizePipe: FileSizePipe,
private datePipe: DatePipe
) {
this.searchEnabled = this.authService.canSearch();
}
@ -97,13 +104,6 @@ export class ControlsLightboxComponent implements OnDestroy, OnInit, OnChanges {
this.checkZoomAndDrag();
}
get Title(): string {
if (!this.activePhoto) {
return null;
}
return (this.activePhoto.gridMedia.media as PhotoDTO).metadata.caption;
}
public containerWidth(): void {
return this.root.nativeElement.width;
}
@ -271,14 +271,14 @@ export class ControlsLightboxComponent implements OnDestroy, OnInit, OnChanges {
private showNextMedia = () => {
if (this.mediaElement.imageLoadFinished.this === false ||
this.mediaElement.imageLoadFinished.next === false) {
this.mediaElement.imageLoadFinished.next === false) {
return;
}
// do not skip video if its playing
if (
this.activePhoto &&
this.activePhoto.gridMedia.isVideo() &&
!this.mediaElement.Paused
this.activePhoto &&
this.activePhoto.gridMedia.isVideo() &&
!this.mediaElement.Paused
) {
return;
}
@ -290,8 +290,8 @@ export class ControlsLightboxComponent implements OnDestroy, OnInit, OnChanges {
// Video is a special snowflake. It won't go to next media if a video is playing
if (!(this.activePhoto &&
this.activePhoto.gridMedia.isVideo() &&
!this.mediaElement.Paused)) {
this.activePhoto.gridMedia.isVideo() &&
!this.mediaElement.Paused)) {
p = (t % (this.selectedSlideshowSpeed * 10)) / this.selectedSlideshowSpeed / 10; // ticks every 100 ms
}
@ -322,12 +322,12 @@ export class ControlsLightboxComponent implements OnDestroy, OnInit, OnChanges {
this.pause();
this.drawSliderProgress(0);
this.timerSub = interval(100)
.pipe(filter((t) => {
this.drawSliderProgress(t);
return t % (this.selectedSlideshowSpeed * 10) === 0; // ticks every 100 ms
}))
.pipe(skip(1)) // do not skip to next photo right away
.subscribe(this.showNextMedia);
.pipe(filter((t) => {
this.drawSliderProgress(t);
return t % (this.selectedSlideshowSpeed * 10) === 0; // ticks every 100 ms
}))
.pipe(skip(1)) // do not skip to next photo right away
.subscribe(this.showNextMedia);
this.playBackState = PlayBackStates.Play;
}
@ -381,7 +381,7 @@ export class ControlsLightboxComponent implements OnDestroy, OnInit, OnChanges {
}
const photoAspect = MediaDTOUtils.calcAspectRatio(
this.activePhoto.gridMedia.media
this.activePhoto.gridMedia.media
);
const widthFilled = photoAspect > this.photoFrameDim.aspect;
const divWidth = this.photoFrameDim.width;
@ -442,7 +442,7 @@ export class ControlsLightboxComponent implements OnDestroy, OnInit, OnChanges {
}
const photoAspect = MediaDTOUtils.calcAspectRatio(
this.activePhoto.gridMedia.media
this.activePhoto.gridMedia.media
);
if (photoAspect < this.photoFrameDim.aspect) {
@ -459,5 +459,70 @@ export class ControlsLightboxComponent implements OnDestroy, OnInit, OnChanges {
this.nextPhoto.emit();
}
getText(type: LightBoxTitleTexts): string {
if (!this.activePhoto?.gridMedia?.media) {
return null;
}
const m = this.activePhoto.gridMedia.media as PhotoDTO;
switch (type) {
case LightBoxTitleTexts.file:
return Utils.concatUrls(
m.directory.path,
m.directory.name,
m.name
);
case LightBoxTitleTexts.resolution:
return `${m.metadata.size.width}x${m.metadata.size.height}`;
case LightBoxTitleTexts.size:
return this.fileSizePipe.transform(m.metadata.fileSize);
case LightBoxTitleTexts.title:
return m.metadata.title;
case LightBoxTitleTexts.caption:
return m.metadata.caption;
case LightBoxTitleTexts.keywords:
return m.metadata.keywords.join(', ');
case LightBoxTitleTexts.persons:
return m.metadata.faces?.map(f => f.name)?.join(', ');
case LightBoxTitleTexts.date:
return this.datePipe.transform(m.metadata.creationDate, 'longDate');
case LightBoxTitleTexts.location:
return (
m.metadata.positionData?.city ||
m.metadata.positionData?.state ||
m.metadata.positionData?.country || ''
).trim();
case LightBoxTitleTexts.camera:
return m.metadata.cameraData?.model;
case LightBoxTitleTexts.lens:
return m.metadata.cameraData?.lens;
case LightBoxTitleTexts.iso:
return m.metadata.cameraData?.ISO.toString();
case LightBoxTitleTexts.fstop:
if (m.metadata.cameraData?.fStop > 1) {
return m.metadata.cameraData?.fStop.toString();
}
return '1/' + Math.round(1 / m.metadata.cameraData?.fStop);
case LightBoxTitleTexts.focal_length:
return m.metadata.cameraData?.focalLength.toString();
}
return null;
}
get TopLeftTitle(): string {
return this.getText(Config.Gallery.Lightbox.Titles.topLeftTitle);
}
get TopLeftSubtitle(): string {
return this.getText(Config.Gallery.Lightbox.Titles.topLeftSubtitle);
}
get BottomLeftTitle(): string {
return this.getText(Config.Gallery.Lightbox.Titles.bottomLeftTitle);
}
get BottomLeftSubtitle(): string {
return this.getText(Config.Gallery.Lightbox.Titles.bottomLeftSubtitle);
}
}