mirror of
https://github.com/bpatrik/pigallery2.git
synced 2025-02-05 13:25:08 +02:00
implementing Information panel for showing Exif info at lightbox
This commit is contained in:
parent
2c315d7bd5
commit
3f9c8a383e
@ -65,7 +65,7 @@ To configure it. Run `PiGallery2` first to create `config.json` file, then edit
|
||||
* Custom lightbox for full screen photo viewing
|
||||
* keyboard support for navigation
|
||||
* showing low-res thumbnail while full image loads
|
||||
* Information panel for showing **Exif info** - `In progress`
|
||||
* Information panel for showing **Exif info**
|
||||
* Client side caching (directories and search results)
|
||||
* Rendering **photos** with GPS coordinates **on google map**
|
||||
* .gpx file support - `future plan`
|
||||
|
@ -34,7 +34,7 @@ export class AuthenticationMWs {
|
||||
public static async authenticate(req: Request, res: Response, next: NextFunction) {
|
||||
|
||||
if (Config.Client.authenticationRequired === false) {
|
||||
req.session.user = <UserDTO>{name: "", role: UserRoles.Admin};
|
||||
req.session.user = <UserDTO>{name: "Admin", role: UserRoles.Admin};
|
||||
return next();
|
||||
}
|
||||
try {
|
||||
|
@ -1,32 +1,32 @@
|
||||
import {Entity, EmbeddableEntity, Column, Embedded, PrimaryGeneratedColumn, ManyToOne} from "typeorm";
|
||||
import {Column, EmbeddableEntity, Embedded, Entity, ManyToOne, PrimaryGeneratedColumn} from "typeorm";
|
||||
import {DirectoryDTO} from "../../../../common/entities/DirectoryDTO";
|
||||
import {
|
||||
PhotoDTO,
|
||||
PhotoMetadata,
|
||||
CameraMetadata,
|
||||
ImageSize,
|
||||
PositionMetaData
|
||||
CameraMetadata,
|
||||
ImageSize,
|
||||
PhotoDTO,
|
||||
PhotoMetadata,
|
||||
PositionMetaData
|
||||
} from "../../../../common/entities/PhotoDTO";
|
||||
import {DirectoryEntity} from "./DirectoryEntity";
|
||||
|
||||
@Entity()
|
||||
export class PhotoEntity implements PhotoDTO {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column("string")
|
||||
name: string;
|
||||
@Column("string")
|
||||
name: string;
|
||||
|
||||
@ManyToOne(type => DirectoryEntity, directory => directory.photos)
|
||||
directory: DirectoryDTO;
|
||||
@ManyToOne(type => DirectoryEntity, directory => directory.photos)
|
||||
directory: DirectoryDTO;
|
||||
|
||||
@Embedded(type => PhotoMetadataEntity)
|
||||
metadata: PhotoMetadataEntity;
|
||||
@Embedded(type => PhotoMetadataEntity)
|
||||
metadata: PhotoMetadataEntity;
|
||||
|
||||
readyThumbnails: Array<number> = [];
|
||||
readyThumbnails: Array<number> = [];
|
||||
|
||||
readyIcon: boolean = false;
|
||||
readyIcon: boolean = false;
|
||||
|
||||
}
|
||||
|
||||
@ -34,20 +34,23 @@ export class PhotoEntity implements PhotoDTO {
|
||||
@EmbeddableEntity()
|
||||
export class PhotoMetadataEntity implements PhotoMetadata {
|
||||
|
||||
@Column("string")
|
||||
keywords: Array<string>;
|
||||
@Column("string")
|
||||
keywords: Array<string>;
|
||||
|
||||
@Column("string")
|
||||
cameraData: CameraMetadata;
|
||||
@Column("string")
|
||||
cameraData: CameraMetadata;
|
||||
|
||||
@Column("string")
|
||||
positionData: PositionMetaData;
|
||||
@Column("string")
|
||||
positionData: PositionMetaData;
|
||||
|
||||
@Column("string")
|
||||
size: ImageSize;
|
||||
@Column("string")
|
||||
size: ImageSize;
|
||||
|
||||
@Column("number")
|
||||
creationDate: number;
|
||||
@Column("number")
|
||||
creationDate: number;
|
||||
|
||||
@Column("number")
|
||||
fileSize: number;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -113,4 +116,4 @@ export class PhotoMetadataEntity implements PhotoMetadata {
|
||||
|
||||
@Column("int")
|
||||
height: number;
|
||||
}*/
|
||||
}*/
|
||||
|
@ -32,7 +32,7 @@ export class DiskMangerWorker {
|
||||
|
||||
private static loadPhotoMetadata(fullPath: string): Promise<PhotoMetadata> {
|
||||
return new Promise<PhotoMetadata>((resolve, reject) => {
|
||||
fs.readFile(fullPath, function (err, data) {
|
||||
fs.readFile(fullPath, (err, data) => {
|
||||
if (err) {
|
||||
return reject({file: fullPath, error: err});
|
||||
}
|
||||
@ -41,11 +41,15 @@ export class DiskMangerWorker {
|
||||
cameraData: {},
|
||||
positionData: null,
|
||||
size: {},
|
||||
creationDate: 0
|
||||
creationDate: 0,
|
||||
fileSize: 0
|
||||
};
|
||||
|
||||
try {
|
||||
|
||||
fs.stat(fullPath, (err, data) => {
|
||||
metadata.fileSize = data.size;
|
||||
});
|
||||
try {
|
||||
const exif = exif_parser.create(data).parse();
|
||||
metadata.cameraData = <CameraMetadata> {
|
||||
|
@ -15,6 +15,7 @@ export interface PhotoMetadata {
|
||||
positionData: PositionMetaData;
|
||||
size: ImageSize;
|
||||
creationDate: number;
|
||||
fileSize: number;
|
||||
}
|
||||
|
||||
export interface ImageSize {
|
||||
|
@ -44,6 +44,7 @@ import {NotificationService} from "./model/notification.service";
|
||||
|
||||
import {ClipboardModule} from "ngx-clipboard";
|
||||
import {NavigationService} from "./model/navigation.service";
|
||||
import {InfoPanelLightboxComponent} from "./gallery/lightbox/infopanel/info-panel.lightbox.gallery.component";
|
||||
|
||||
@Injectable()
|
||||
export class GoogleMapsConfig {
|
||||
@ -86,6 +87,7 @@ export class GoogleMapsConfig {
|
||||
GalleryNavigatorComponent,
|
||||
GalleryPhotoComponent,
|
||||
AdminComponent,
|
||||
InfoPanelLightboxComponent,
|
||||
//Settings
|
||||
UserMangerSettingsComponent,
|
||||
DatabaseSettingsComponent,
|
||||
|
@ -0,0 +1,38 @@
|
||||
.content {
|
||||
background-color: #F7F7F7;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.row {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.details-icon {
|
||||
margin-top: 10px;
|
||||
font-size: x-large;
|
||||
}
|
||||
|
||||
.details-main {
|
||||
font-size: x-large;
|
||||
}
|
||||
|
||||
.details-sub {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.details-sub div {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.sebm-google-map-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#map {
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
<div class="content">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<h1>Info</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-2">
|
||||
<span class="details-icon glyphicon glyphicon-picture"></span>
|
||||
</div>
|
||||
<div class="col-sm-10">
|
||||
<div class="details-main">
|
||||
{{photo.name}}
|
||||
</div>
|
||||
<div class="details-sub">
|
||||
<div class="col-sm-4">{{photo.metadata.size.width}} x {{photo.metadata.size.height}}</div>
|
||||
<div class="col-sm-4">{{calcMpx()}}MP</div>
|
||||
<div class="col-sm-4" *ngIf="photo.metadata.fileSize">{{calcFileSize()}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-2">
|
||||
<span class="details-icon glyphicon glyphicon-calendar"></span>
|
||||
</div>
|
||||
<div class="col-sm-10">
|
||||
<div class="details-main">
|
||||
<ng-container *ngIf="getYear() !== getCurrentYear()">
|
||||
{{getYear()}}
|
||||
</ng-container>
|
||||
{{getDate()}}
|
||||
</div>
|
||||
<div class="details-sub">
|
||||
<div class="col-sm-12">{{getDay()}}, {{getTime()}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-2">
|
||||
<span class="details-icon glyphicon glyphicon-camera"></span>
|
||||
</div>
|
||||
<div class="col-sm-10">
|
||||
<div class="details-main">
|
||||
{{photo.metadata.cameraData.model || "Camera"}}
|
||||
</div>
|
||||
<div class="details-sub">
|
||||
<div class="col-sm-3" *ngIf="photo.metadata.cameraData.ISO">ISO{{photo.metadata.cameraData.ISO}}</div>
|
||||
<div class="col-sm-3" *ngIf="photo.metadata.cameraData.fStop">f/{{photo.metadata.cameraData.fStop}}</div>
|
||||
<div class="col-sm-3" *ngIf="photo.metadata.cameraData.exposure">
|
||||
{{toFraction(photo.metadata.cameraData.exposure)}}s
|
||||
</div>
|
||||
<div class="col-sm-3" *ngIf="photo.metadata.cameraData.focalLength">
|
||||
{{photo.metadata.cameraData.focalLength}}mm
|
||||
</div>
|
||||
<div class="col-sm-12" *ngIf="photo.metadata.cameraData.lens">{{photo.metadata.cameraData.lens}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-2">
|
||||
<span class="details-icon glyphicon glyphicon-map-marker"></span>
|
||||
</div>
|
||||
<div class="col-sm-10">
|
||||
<div class="details-main">
|
||||
{{getPositionText() || "Position"}}
|
||||
</div>
|
||||
<div class="details-sub">
|
||||
<div class="col-sm-12"
|
||||
*ngIf="hasGPS()">
|
||||
{{photo.metadata.positionData.GPSData.latitude.toFixed(3)}},
|
||||
{{photo.metadata.positionData.GPSData.longitude.toFixed(3)}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="map" *ngIf="hasGPS()">
|
||||
<agm-map
|
||||
[disableDefaultUI]="true"
|
||||
[zoomControl]="false"
|
||||
[streetViewControl]="false"
|
||||
[zoom]="5"
|
||||
[latitude]="photo.metadata.positionData.GPSData.latitude"
|
||||
[longitude]="photo.metadata.positionData.GPSData.longitude">
|
||||
<agm-marker
|
||||
[latitude]="photo.metadata.positionData.GPSData.latitude"
|
||||
[longitude]="photo.metadata.positionData.GPSData.longitude">
|
||||
</agm-marker>
|
||||
</agm-map>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,84 @@
|
||||
import {Component, Input} from "@angular/core";
|
||||
import {PhotoDTO} from "../../../../../common/entities/PhotoDTO";
|
||||
|
||||
@Component({
|
||||
selector: 'info-panel',
|
||||
styleUrls: ['./info-panel.lightbox.gallery.component.css'],
|
||||
templateUrl: './info-panel.lightbox.gallery.component.html',
|
||||
})
|
||||
export class InfoPanelLightboxComponent {
|
||||
@Input() photo: PhotoDTO;
|
||||
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
calcMpx() {
|
||||
return (this.photo.metadata.size.width * this.photo.metadata.size.height / 1000000).toFixed(2);
|
||||
}
|
||||
|
||||
calcFileSize() {
|
||||
let postFixes = ["B", "KB", "MB", "GB", "TB"];
|
||||
let index = 0;
|
||||
let size = this.photo.metadata.fileSize;
|
||||
while (size > 1000 && index < postFixes.length - 1) {
|
||||
size /= 1000;
|
||||
index++;
|
||||
}
|
||||
return size.toFixed(2) + postFixes[index];
|
||||
}
|
||||
|
||||
getCurrentYear() {
|
||||
return (new Date()).getFullYear();
|
||||
}
|
||||
|
||||
getYear() {
|
||||
const date = new Date(this.photo.metadata.creationDate);
|
||||
return date.getFullYear();
|
||||
}
|
||||
|
||||
getDate() {
|
||||
const date = new Date(this.photo.metadata.creationDate);
|
||||
let locale = "en-us";
|
||||
return date.toLocaleString(locale, {month: "long"}) + " " + date.getDay();
|
||||
}
|
||||
|
||||
getTime() {
|
||||
const date = new Date(this.photo.metadata.creationDate);
|
||||
return date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds();
|
||||
}
|
||||
|
||||
getDay() {
|
||||
const date = new Date(this.photo.metadata.creationDate);
|
||||
let locale = "en-us";
|
||||
return date.toLocaleString(locale, {weekday: "long"});
|
||||
}
|
||||
|
||||
toFraction(f) {
|
||||
if (f > 1) {
|
||||
return f;
|
||||
}
|
||||
return "1/" + Math.ceil(((f < 1.0) ? f : (f % Math.floor(f))) * 10000)
|
||||
}
|
||||
|
||||
hasGPS() {
|
||||
return this.photo.metadata.positionData && this.photo.metadata.positionData.GPSData &&
|
||||
this.photo.metadata.positionData.GPSData.latitude && this.photo.metadata.positionData.GPSData.longitude
|
||||
}
|
||||
|
||||
getPositionText(): string {
|
||||
if (!this.photo.metadata.positionData) {
|
||||
return "";
|
||||
}
|
||||
let str = this.photo.metadata.positionData.city ||
|
||||
this.photo.metadata.positionData.state;
|
||||
|
||||
if (str.length != 0) {
|
||||
str += ", ";
|
||||
}
|
||||
str += this.photo.metadata.positionData.country;
|
||||
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
@ -1,89 +1,96 @@
|
||||
.lightbox {
|
||||
position: fixed; /* Stay in place */
|
||||
z-index: 1100; /* Sit on top */
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%; /* Full width */
|
||||
height: 100%; /* Full height */
|
||||
overflow: hidden;
|
||||
display: flex; /* add */
|
||||
justify-content: center; /* add to align horizontal */
|
||||
align-items: center; /* add to align vertical */
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease-in-out;
|
||||
position: fixed; /* Stay in place */
|
||||
z-index: 1100; /* Sit on top */
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%; /* Full width */
|
||||
height: 100%; /* Full height */
|
||||
overflow: hidden;
|
||||
display: flex; /* add */
|
||||
justify-content: center; /* add to align horizontal */
|
||||
align-items: center; /* add to align vertical */
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
gallery-lightbox-photo {
|
||||
transition: all 0.3s ease-in-out;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease-in-out;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.blackCanvas{
|
||||
position: fixed; /* Stay in place */
|
||||
z-index: 1099; /* Sit on top */
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%; /* Full width */
|
||||
height: 100%; /* Full height */
|
||||
background-color: black;
|
||||
transition: all 0.3s ease-in-out;
|
||||
.blackCanvas {
|
||||
position: fixed; /* Stay in place */
|
||||
z-index: 1099; /* Sit on top */
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%; /* Full width */
|
||||
height: 100%; /* Full height */
|
||||
background-color: black;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.navigation-arrow {
|
||||
width: 30%;
|
||||
height: 100%;
|
||||
position: static;
|
||||
display: inline-block;
|
||||
padding: 15px;
|
||||
cursor: pointer;
|
||||
font-size: x-large;
|
||||
width: 30%;
|
||||
height: 100%;
|
||||
position: static;
|
||||
display: inline-block;
|
||||
padding: 15px;
|
||||
cursor: pointer;
|
||||
font-size: x-large;
|
||||
}
|
||||
|
||||
.navigation-arrow span {
|
||||
top: 43%;
|
||||
top: 43%;
|
||||
}
|
||||
|
||||
#controllers-container {
|
||||
z-index: 1100;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
position: fixed;;
|
||||
color: white;
|
||||
z-index: 1100;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
position: fixed;
|
||||
color: white;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
#rightArrow {
|
||||
float: right;
|
||||
text-align: right;
|
||||
float: right;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#controls {
|
||||
top: 0;
|
||||
height: initial;
|
||||
text-align: right;
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
font-size: large;
|
||||
top: 0;
|
||||
height: initial;
|
||||
text-align: right;
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
#controls span {
|
||||
margin-left: 6px;
|
||||
margin-right: 6px;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
margin-left: 6px;
|
||||
margin-right: 6px;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
opacity: 0.4;
|
||||
transition: opacity .2s ease-out;
|
||||
-moz-transition: opacity .2s ease-out;
|
||||
-webkit-transition: opacity .2s ease-out;
|
||||
-o-transition: opacity .2s ease-out;
|
||||
opacity: 0.4;
|
||||
transition: opacity .2s ease-out;
|
||||
-moz-transition: opacity .2s ease-out;
|
||||
-webkit-transition: opacity .2s ease-out;
|
||||
-o-transition: opacity .2s ease-out;
|
||||
}
|
||||
|
||||
.highlight:hover {
|
||||
opacity: 1.0;
|
||||
}
|
||||
opacity: 1.0;
|
||||
}
|
||||
|
||||
info-panel {
|
||||
z-index: 1100; /* Sit on top */
|
||||
position: fixed;
|
||||
height: 100vh;
|
||||
right: 0;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
@ -1,47 +1,54 @@
|
||||
<div [hidden]="!visible" #root>
|
||||
|
||||
<div class="blackCanvas"
|
||||
[style.opacity]="blackCanvasOpacity">
|
||||
<div class="blackCanvas"
|
||||
[style.opacity]="blackCanvasOpacity">
|
||||
</div>
|
||||
|
||||
<div class="lightbox"
|
||||
[style.width.px]="lightboxDimension.width"
|
||||
[style.height.px]="lightboxDimension.height"
|
||||
[style.top.px]="lightboxDimension.top"
|
||||
[style.left.px]="lightboxDimension.left">
|
||||
<gallery-lightbox-photo [gridPhoto]="activePhoto ? activePhoto.gridPhoto : null"
|
||||
[style.top.px]="photoDimension.top"
|
||||
[style.left.px]="photoDimension.left"
|
||||
[style.width.px]="photoDimension.width"
|
||||
[style.height.px]="photoDimension.height"
|
||||
[style.transition]="transition">
|
||||
</gallery-lightbox-photo>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="controllers-container" #controls
|
||||
[style.width.px]="contentWidth">
|
||||
<div id="controls">
|
||||
<a *ngIf="activePhoto" [href]="activePhoto.gridPhoto.getPhotoPath()"
|
||||
[download]="activePhoto.gridPhoto.photo.name"><span class="glyphicon glyphicon-download-alt highlight"
|
||||
title="download"></span></a>
|
||||
<span class="glyphicon glyphicon-info-sign highlight" (click)="toggleInfoPanel()" title="info"></span>
|
||||
|
||||
<span class=" glyphicon glyphicon-resize-small highlight"
|
||||
*ngIf="fullScreenService.isFullScreenEnabled()"
|
||||
(click)="fullScreenService.exitFullScreen()" title="toggle fullscreen"></span>
|
||||
|
||||
<span class="glyphicon glyphicon-fullscreen highlight"
|
||||
*ngIf="!fullScreenService.isFullScreenEnabled()"
|
||||
(click)="fullScreenService.showFullScreen(root)" title="toggle fullscreen"></span>
|
||||
|
||||
<span class="glyphicon glyphicon-remove highlight" (click)="hide()" title="close"></span>
|
||||
</div>
|
||||
|
||||
<div class="lightbox"
|
||||
[style.width.px]="lightboxDimension.width"
|
||||
[style.height.px]="lightboxDimension.height"
|
||||
[style.top.px]="lightboxDimension.top"
|
||||
[style.left.px]="lightboxDimension.left">
|
||||
<gallery-lightbox-photo [gridPhoto]="activePhoto ? activePhoto.gridPhoto : null"
|
||||
[style.top.px]="photoDimension.top"
|
||||
[style.left.px]="photoDimension.left"
|
||||
[style.width.px]="photoDimension.width"
|
||||
[style.height.px]="photoDimension.height"
|
||||
[style.transition]="transition">
|
||||
</gallery-lightbox-photo>
|
||||
</div>
|
||||
<div class="navigation-arrow highlight" *ngIf="navigation.hasPrev" title="key: left arrow" id="leftArrow"
|
||||
(click)="prevImage()"><span
|
||||
class="glyphicon glyphicon-chevron-left"></span></div>
|
||||
<div class="navigation-arrow highlight" *ngIf="navigation.hasNext" title="key: right arrow" id="rightArrow"
|
||||
(click)="nextImage()"><span
|
||||
class="glyphicon glyphicon-chevron-right"></span></div>
|
||||
</div>
|
||||
<info-panel *ngIf="activePhoto && infoPanelVisible"
|
||||
id="info-panel"
|
||||
[style.width.px]="infoPanelWidth"
|
||||
[photo]="activePhoto.gridPhoto.photo">
|
||||
|
||||
|
||||
<div id="controllers-container" #controls>
|
||||
<div id="controls">
|
||||
<a *ngIf="activePhoto" [href]="activePhoto.gridPhoto.getPhotoPath()"
|
||||
[download]="activePhoto.gridPhoto.photo.name"><span class="glyphicon glyphicon-download-alt highlight"
|
||||
title="download"></span></a>
|
||||
<span class="glyphicon glyphicon-info-sign highlight" title="info"></span>
|
||||
|
||||
<span class=" glyphicon glyphicon-resize-small highlight"
|
||||
*ngIf="fullScreenService.isFullScreenEnabled()"
|
||||
(click)="fullScreenService.exitFullScreen()" title="toggle fullscreen"></span>
|
||||
|
||||
<span class="glyphicon glyphicon-fullscreen highlight"
|
||||
*ngIf="!fullScreenService.isFullScreenEnabled()"
|
||||
(click)="fullScreenService.showFullScreen(root)" title="toggle fullscreen"></span>
|
||||
|
||||
<span class="glyphicon glyphicon-remove highlight" (click)="hide()" title="close"></span>
|
||||
</div>
|
||||
|
||||
<div class="navigation-arrow highlight" *ngIf="navigation.hasPrev" title="key: left arrow" id="leftArrow"
|
||||
(click)="prevImage()"><span
|
||||
class="glyphicon glyphicon-chevron-left"></span></div>
|
||||
<div class="navigation-arrow highlight" *ngIf="navigation.hasNext" title="key: right arrow" id="rightArrow"
|
||||
(click)="nextImage()"><span
|
||||
class="glyphicon glyphicon-chevron-right"></span></div>
|
||||
</div>
|
||||
</info-panel>
|
||||
</div>
|
||||
|
@ -4,6 +4,7 @@ import {
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
HostListener,
|
||||
OnDestroy,
|
||||
Output,
|
||||
QueryList,
|
||||
ViewChild
|
||||
@ -20,8 +21,9 @@ import {Subscription} from "rxjs";
|
||||
styleUrls: ['./lightbox.gallery.component.css'],
|
||||
templateUrl: './lightbox.gallery.component.html',
|
||||
})
|
||||
export class GalleryLightboxComponent {
|
||||
export class GalleryLightboxComponent implements OnDestroy {
|
||||
@Output('onLastElement') onLastElement = new EventEmitter();
|
||||
@ViewChild("root") elementRef: ElementRef;
|
||||
|
||||
public navigation = {hasPrev: true, hasNext: true};
|
||||
public photoDimension: Dimension = <Dimension>{top: 0, left: 0, width: 0, height: 0};
|
||||
@ -35,14 +37,23 @@ export class GalleryLightboxComponent {
|
||||
public visible = false;
|
||||
private changeSubscription: Subscription = null;
|
||||
|
||||
@ViewChild("root") elementRef: ElementRef;
|
||||
|
||||
public infoPanelVisible = false;
|
||||
public infoPanelWidth = 0;
|
||||
public contentWidth = 0;
|
||||
|
||||
|
||||
constructor(public fullScreenService: FullScreenService, private changeDetector: ChangeDetectorRef, private overlayService: OverlayService) {
|
||||
}
|
||||
|
||||
|
||||
//noinspection JSUnusedGlobalSymbols
|
||||
ngOnDestroy(): void {
|
||||
if (this.changeSubscription != null) {
|
||||
this.changeSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
//noinspection JSUnusedGlobalSymbols
|
||||
@HostListener('window:resize', ['$event'])
|
||||
onResize() {
|
||||
if (this.activePhoto) {
|
||||
@ -130,11 +141,13 @@ export class GalleryLightboxComponent {
|
||||
};
|
||||
this.blackCanvasOpacity = 1.0;
|
||||
this.showPhoto(this.gridPhotoQL.toArray().indexOf(selectedPhoto));
|
||||
this.contentWidth = this.getScreenWidth();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
public hide() {
|
||||
this.enableAnimation();
|
||||
this.hideInfoPanel();
|
||||
this.fullScreenService.exitFullScreen();
|
||||
|
||||
this.lightboxDimension = this.activePhoto.getDimension();
|
||||
@ -196,6 +209,52 @@ export class GalleryLightboxComponent {
|
||||
}
|
||||
}
|
||||
|
||||
iPvisibilityTimer = null;
|
||||
|
||||
public toggleInfoPanel() {
|
||||
|
||||
|
||||
if (this.infoPanelWidth != 400) {
|
||||
this.showInfoPanel();
|
||||
} else {
|
||||
this.hideInfoPanel();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
recalcPositions() {
|
||||
|
||||
this.photoDimension = this.calcLightBoxPhotoDimension(this.activePhoto.gridPhoto.photo);
|
||||
this.contentWidth = this.getScreenWidth();
|
||||
this.lightboxDimension = <Dimension>{
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: this.getScreenWidth(),
|
||||
height: this.getScreenHeight()
|
||||
};
|
||||
};
|
||||
|
||||
showInfoPanel() {
|
||||
this.infoPanelVisible = true;
|
||||
this.infoPanelWidth = 0;
|
||||
setTimeout(() => {
|
||||
this.infoPanelWidth = 400;
|
||||
this.recalcPositions();
|
||||
}, 0);
|
||||
if (this.iPvisibilityTimer != null) {
|
||||
clearTimeout(this.iPvisibilityTimer);
|
||||
}
|
||||
}
|
||||
|
||||
hideInfoPanel() {
|
||||
this.infoPanelWidth = 0;
|
||||
this.iPvisibilityTimer = setTimeout(() => {
|
||||
this.iPvisibilityTimer = null;
|
||||
this.infoPanelVisible = false;
|
||||
}, 1000);
|
||||
this.recalcPositions();
|
||||
}
|
||||
|
||||
private enableAnimation() {
|
||||
this.transition = null;
|
||||
}
|
||||
@ -214,7 +273,7 @@ export class GalleryLightboxComponent {
|
||||
}
|
||||
|
||||
private getScreenWidth() {
|
||||
return window.innerWidth;
|
||||
return Math.max(window.innerWidth - this.infoPanelWidth, 0);
|
||||
}
|
||||
|
||||
private getScreenHeight() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user