Fix layout and add selection logic

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/4/head
trivernis 3 years ago
parent 1b1451d910
commit 851983c164

@ -54,8 +54,8 @@
"windows": [ "windows": [
{ {
"title": "Mediarepo", "title": "Mediarepo",
"width": 800, "width": 1920,
"height": 600, "height": 1080,
"resizable": true, "resizable": true,
"fullscreen": false "fullscreen": false
} }

@ -0,0 +1,27 @@
@use 'sass:map';
@use '~@angular/material' as mat;
@mixin color($theme) {
$color-config: mat.get-color-config($theme);
$primary-palette: map.get($color-config, 'primary');
$warn-palette: map.get($color-config, 'warn');
mat-card.selected {
background-color: mat.get-color-from-palette($primary-palette);
}
}
@mixin typography($theme) {
}
@mixin theme($theme) {
$color-config: mat.get-color-config($theme);
@if $color-config != null {
@include color($theme);
}
$typography-config: mat.get-typography-config($theme);
@if $typography-config != null {
@include typography($theme);
}
}

@ -1,4 +1,4 @@
<mat-card #card (click)="clickEvent.emit(file)" (dblclick)="dblClickEvent.emit(file)"> <mat-card #card (click)="clickEvent.emit(this)" (dblclick)="dblClickEvent.emit(this)" [ngClass]="{'selected': selected}">
<mat-card-title *ngIf="!!file?.name">{{file?.name}}</mat-card-title> <mat-card-title *ngIf="!!file?.name">{{file?.name}}</mat-card-title>
<mat-card-content *ngIf="contentUrl !== undefined"> <mat-card-content *ngIf="contentUrl !== undefined">
<img *ngIf="contentUrl !== undefined" class="entry-image" loading="lazy" decoding="async" [src]="contentUrl" alt="File Image"> <img *ngIf="contentUrl !== undefined" class="entry-image" loading="lazy" decoding="async" [src]="contentUrl" alt="File Image">

@ -1,6 +1,8 @@
mat-card { mat-card {
height: calc(100% - 32px); height: calc(100% - 32px);
width: calc(100% - 32px); width: calc(100% - 32px);
user-select: none;
cursor: pointer;
} }
mat-card-content { mat-card-content {

@ -20,9 +20,10 @@ import {Thumbnail} from "../../../models/Thumbnail";
export class FileGridEntryComponent implements OnInit, OnDestroy { export class FileGridEntryComponent implements OnInit, OnDestroy {
@ViewChild("card") card!: ElementRef; @ViewChild("card") card!: ElementRef;
@Input() file!: File; @Input() public file!: File;
@Output() clickEvent = new EventEmitter<File>(); @Output() clickEvent = new EventEmitter<FileGridEntryComponent>();
@Output() dblClickEvent = new EventEmitter<File>(); @Output() dblClickEvent = new EventEmitter<FileGridEntryComponent>();
public selected: boolean = false;
selectedThumbnail: Thumbnail | undefined; selectedThumbnail: Thumbnail | undefined;
contentUrl: SafeResourceUrl | undefined; contentUrl: SafeResourceUrl | undefined;

@ -1,7 +1,7 @@
<cdk-virtual-scroll-viewport class="file-scroll" maxBufferPx="500" minBufferPx="500" itemSize="250"> <cdk-virtual-scroll-viewport class="file-scroll" maxBufferPx="500" minBufferPx="500" itemSize="250">
<div *cdkVirtualFor="let fileRow of fileRows"> <div *cdkVirtualFor="let fileRow of fileRows">
<div class="file-row"> <div class="file-row">
<app-file-grid-entry (clickEvent)="fileClickEvent.emit($event)" (dblClickEvent)="fileDblClickEvent.emit($event)" <app-file-grid-entry (clickEvent)="setSelectedFile($event)" (dblClickEvent)="fileDblClickEvent.emit($event.file)"
*ngFor="let file of fileRow" [file]="file"></app-file-grid-entry> *ngFor="let file of fileRow" [file]="file"></app-file-grid-entry>
</div> </div>
</div> </div>

@ -1,6 +1,14 @@
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; import {
Component,
EventEmitter,
HostListener,
Input,
OnInit,
Output, QueryList, ViewChildren
} from '@angular/core';
import {File} from "../../models/File"; import {File} from "../../models/File";
import {FileService} from "../../services/file/file.service"; import {FileService} from "../../services/file/file.service";
import {FileGridEntryComponent} from "./file-grid-entry/file-grid-entry.component";
@Component({ @Component({
selector: 'app-file-grid', selector: 'app-file-grid',
@ -11,7 +19,69 @@ export class FileGridComponent {
@Input() fileRows: File[][] = []; @Input() fileRows: File[][] = [];
@Output() fileDblClickEvent = new EventEmitter<File>(); @Output() fileDblClickEvent = new EventEmitter<File>();
@Output() fileClickEvent = new EventEmitter<File>(); @Output() filesSelectEvent = new EventEmitter<File[]>();
@ViewChildren(FileGridEntryComponent) childQuery!: QueryList<FileGridEntryComponent>;
selectedEntries: FileGridEntryComponent[] = [];
private shiftClicked = false;
private ctrlClicked = false;
constructor() { } constructor() { }
/**
* File selector logic
* @param {FileGridEntryComponent} entry
*/
setSelectedFile(entry: FileGridEntryComponent) {
if (!(this.shiftClicked || this.ctrlClicked) && this.selectedEntries.length > 0) {
this.selectedEntries.forEach(entry => entry.selected = false);
this.selectedEntries = [];
}
// shift selector (forwards and backwards)
if (this.shiftClicked && this.selectedEntries.length > 0) {
const lastEntry = this.selectedEntries[this.selectedEntries.length - 1];
let found = false;
// TODO: change to use wrapped entry files instead because of reused components
for (const child of this.childQuery) {
if (found) {
child.selected = true;
this.selectedEntries.push(child);
if (child === entry || child == lastEntry) {
break;
}
} else if (child === lastEntry || child === entry) {
found = true;
if (child === entry) {
child.selected = true;
this.selectedEntries.push(child);
}
}
}
} else {
entry.selected = true;
this.selectedEntries.push(entry);
}
this.filesSelectEvent.emit(this.selectedEntries.map(entry => entry.file));
}
@HostListener("window:keydown", ["$event"])
private handleKeydownEvent(event: KeyboardEvent) {
switch (event.key) {
case "Shift": this.shiftClicked = true; break;
case "Control": this.ctrlClicked = true; break;
}
}
@HostListener("window:keyup", ["$event"])
private handleKeyupEvent(event: KeyboardEvent) {
switch (event.key) {
case "Shift": this.shiftClicked = false; break;
case "Control": this.ctrlClicked = false; break;
}
}
} }

@ -20,7 +20,7 @@ mat-drawer-content {
} }
mat-drawer-container { mat-drawer-container {
height: 100%; height: calc(100% - 64px);
} }
app-file-grid { app-file-grid {

@ -4,6 +4,7 @@ import {File} from "../../models/File";
import {PageEvent} from "@angular/material/paginator"; import {PageEvent} from "@angular/material/paginator";
import {Lightbox, LIGHTBOX_EVENT, LightboxEvent} from "ngx-lightbox"; import {Lightbox, LIGHTBOX_EVENT, LightboxEvent} from "ngx-lightbox";
import {SafeResourceUrl} from "@angular/platform-browser"; import {SafeResourceUrl} from "@angular/platform-browser";
import {ErrorBrokerService} from "../../services/error-broker/error-broker.service";
@Component({ @Component({
selector: 'app-home', selector: 'app-home',
@ -13,10 +14,9 @@ import {SafeResourceUrl} from "@angular/platform-browser";
export class HomeComponent implements OnInit { export class HomeComponent implements OnInit {
fileRows: File[][] = []; fileRows: File[][] = [];
page: number = 0; private openingLightbox = false;
pageSize: number = 25;
constructor(private fileService: FileService, private lightbox: Lightbox, private lightboxEvent: LightboxEvent) { } constructor(private errorBroker: ErrorBrokerService, private fileService: FileService, private lightbox: Lightbox, private lightboxEvent: LightboxEvent) { }
async ngOnInit() { async ngOnInit() {
this.fileService.displayedFiles.subscribe((files) => this.setFileRows(files)); this.fileService.displayedFiles.subscribe((files) => this.setFileRows(files));
@ -33,7 +33,21 @@ export class HomeComponent implements OnInit {
} }
async openFile(file: File) { async openFile(file: File) {
let url = await this.fileService.readFile(file.hash, file.mime_type ?? "image/png"); if (this.openingLightbox) {
return;
}
this.openingLightbox = true;
try {
await this.openLightbox(file);
} catch(err) {
this.errorBroker.showError(err);
}
this.openingLightbox = false;
}
private async openLightbox(file: File): Promise<void> {
let url = await this.fileService.readFile(file.hash,
file.mime_type ?? "image/png");
let albums = [ let albums = [
{ {
@ -48,11 +62,12 @@ export class HomeComponent implements OnInit {
showDownloadButton: true, showDownloadButton: true,
centerVertically: true, centerVertically: true,
}); });
const lighboxSubscription = this.lightboxEvent.lightboxEvent$.subscribe((event: any) => { const lighboxSubscription = this.lightboxEvent.lightboxEvent$.subscribe(
if (event?.id == LIGHTBOX_EVENT.CLOSE) { (event: any) => {
lighboxSubscription.unsubscribe(); if (event?.id == LIGHTBOX_EVENT.CLOSE) {
URL?.revokeObjectURL(url as string); lighboxSubscription.unsubscribe();
} URL?.revokeObjectURL(url as string);
}) }
})
} }
} }

@ -1,6 +1,7 @@
@use 'sass:map'; @use 'sass:map';
@use "~@angular/material" as mat; @use "~@angular/material" as mat;
@use 'src/app/app.component-theme' as app; @use 'src/app/app.component-theme' as app;
@use 'src/app/components/file-grid/file-grid-entry/file-grid-entry.component-theme' as file-grid-entry;
@include mat.core(); @include mat.core();
$theme: mat.define-dark-theme(( $theme: mat.define-dark-theme((
@ -17,4 +18,4 @@ $theme: mat.define-dark-theme((
@include mat.all-component-themes($theme); @include mat.all-component-themes($theme);
@include app.theme($theme); @include app.theme($theme);
@include file-grid-entry.theme($theme);

Loading…
Cancel
Save