Fix file grid multiselection

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

@ -0,0 +1,6 @@
import {File} from "../../../models/File";
export type GridEntry = {
file: File,
selected: boolean,
}

@ -7,7 +7,7 @@
$warn-palette: map.get($color-config, 'warn');
mat-card.selected {
background-color: mat.get-color-from-palette($primary-palette);
background-color: mat.get-color-from-palette($primary-palette, 'darker');
}
}

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

@ -11,6 +11,7 @@ import {ErrorBrokerService} from "../../../services/error-broker/error-broker.se
import {SafeResourceUrl} from "@angular/platform-browser";
import {MatCard} from "@angular/material/card";
import {Thumbnail} from "../../../models/Thumbnail";
import {GridEntry} from "./GridEntry";
@Component({
selector: 'app-file-grid-entry',
@ -20,10 +21,9 @@ import {Thumbnail} from "../../../models/Thumbnail";
export class FileGridEntryComponent implements OnInit, OnDestroy {
@ViewChild("card") card!: ElementRef;
@Input() public file!: File;
@Input() public gridEntry!: GridEntry;
@Output() clickEvent = new EventEmitter<FileGridEntryComponent>();
@Output() dblClickEvent = new EventEmitter<FileGridEntryComponent>();
public selected: boolean = false;
selectedThumbnail: Thumbnail | undefined;
contentUrl: SafeResourceUrl | undefined;
@ -43,9 +43,10 @@ export class FileGridEntryComponent implements OnInit, OnDestroy {
async loadImage() {
try {
const thumbnails = await this.fileService.getThumbnails(this.file.hash);
const thumbnails = await this.fileService.getThumbnails(this.gridEntry.file.hash);
let thumbnail = thumbnails.find(t => (t.height > 250 || t.width > 250) && (t.height < 500 && t.width < 500));
this.selectedThumbnail = thumbnail;
if (!thumbnail) {
console.log("Thumbnail is empty?!", thumbnails);
} else {

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

@ -2,71 +2,91 @@ import {
Component,
EventEmitter,
HostListener,
Input,
Input, OnChanges,
OnInit,
Output, QueryList, ViewChildren
Output, QueryList, SimpleChanges, ViewChildren
} from '@angular/core';
import {File} from "../../models/File";
import {FileService} from "../../services/file/file.service";
import {FileGridEntryComponent} from "./file-grid-entry/file-grid-entry.component";
import {GridEntry} from "./file-grid-entry/GridEntry";
@Component({
selector: 'app-file-grid',
templateUrl: './file-grid.component.html',
styleUrls: ['./file-grid.component.scss']
})
export class FileGridComponent {
export class FileGridComponent implements OnChanges {
@Input() fileRows: File[][] = [];
@Input() files: File[] = [];
@Input() columns: number = 6;
@Output() fileDblClickEvent = new EventEmitter<File>();
@Output() filesSelectEvent = new EventEmitter<File[]>();
@ViewChildren(FileGridEntryComponent) childQuery!: QueryList<FileGridEntryComponent>;
selectedEntries: FileGridEntryComponent[] = [];
selectedEntries: GridEntry[] = [];
private shiftClicked = false;
private ctrlClicked = false;
private gridEntries: GridEntry[] = []
partitionedGridEntries: GridEntry[][] = [];
constructor() {
}
ngOnChanges(changes: SimpleChanges): void {
this.gridEntries = this.files.map(file => {return {file, selected: false}});
this.setPartitionedGridEntries();
}
constructor() { }
private setPartitionedGridEntries() {
this.partitionedGridEntries = [];
for (let i = 0; i < (Math.ceil(this.gridEntries.length / this.columns)); i++) {
this.partitionedGridEntries.push(this.gridEntries.slice(i * this.columns, Math.min(this.gridEntries.length, (i + 1) * this.columns)))
}
}
/**
* File selector logic
* @param {FileGridEntryComponent} entry
* @param {FileGridEntryComponent} clickedEntry
*/
setSelectedFile(entry: FileGridEntryComponent) {
setSelectedFile(clickedEntry: GridEntry) {
if (!(this.shiftClicked || this.ctrlClicked) && this.selectedEntries.length > 0) {
this.selectedEntries.forEach(entry => entry.selected = false);
this.selectedEntries.forEach(entry => {if (entry !== clickedEntry) entry.selected = false});
this.selectedEntries = [];
}
// shift selector (forwards and backwards)
if (this.shiftClicked && this.selectedEntries.length > 0) {
this.handleShiftSelect(clickedEntry);
} else {
clickedEntry.selected = !clickedEntry.selected;
this.selectedEntries.push(clickedEntry);
}
this.filesSelectEvent.emit(this.selectedEntries.map(entry => entry.file));
}
private handleShiftSelect(clickedEntry: GridEntry): void {
const lastEntry = this.selectedEntries[this.selectedEntries.length - 1];
let found = false;
if (clickedEntry == lastEntry) {
return;
}
// TODO: change to use wrapped entry files instead because of reused components
for (const child of this.childQuery) {
for (const gridEntry of this.gridEntries) {
if (found) {
child.selected = true;
this.selectedEntries.push(child);
if (child === entry || child == lastEntry) {
break;
gridEntry.selected = true;
this.selectedEntries.push(gridEntry);
if (gridEntry === clickedEntry || gridEntry == lastEntry) {
return;
}
} else if (child === lastEntry || child === entry) {
} else if (gridEntry === lastEntry || gridEntry === clickedEntry) {
found = true;
if (child === entry) {
child.selected = true;
this.selectedEntries.push(child);
if (gridEntry === clickedEntry) {
gridEntry.selected = true;
this.selectedEntries.push(gridEntry);
}
}
}
} else {
entry.selected = true;
this.selectedEntries.push(entry);
}
this.filesSelectEvent.emit(this.selectedEntries.map(entry => entry.file));
}
@HostListener("window:keydown", ["$event"])

@ -7,7 +7,7 @@
<p>Drawer</p>
</mat-drawer>
<mat-drawer-content>
<app-file-grid (fileDblClickEvent)="openFile($event)" [fileRows]="fileRows"></app-file-grid>
<app-file-grid (fileDblClickEvent)="openFile($event)" [files]="files"></app-file-grid>
</mat-drawer-content>
</mat-drawer-container>

@ -13,25 +13,16 @@ import {ErrorBrokerService} from "../../services/error-broker/error-broker.servi
})
export class HomeComponent implements OnInit {
fileRows: File[][] = [];
files: File[] = [];
private openingLightbox = false;
constructor(private errorBroker: ErrorBrokerService, private fileService: FileService, private lightbox: Lightbox, private lightboxEvent: LightboxEvent) { }
async ngOnInit() {
this.fileService.displayedFiles.subscribe((files) => this.setFileRows(files));
this.fileService.displayedFiles.subscribe((files) => this.files = files);
await this.fileService.getFiles();
}
setFileRows(files: File[]) {
this.fileRows = [];
const filesPerRow = 6;
for (let i = 0; i < (Math.ceil(files.length /filesPerRow )); i++) {
this.fileRows.push(files.slice(i * filesPerRow, Math.min(files.length, (i + 1) * filesPerRow)))
}
console.log(this.fileRows);
}
async openFile(file: File) {
if (this.openingLightbox) {
return;

Loading…
Cancel
Save