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'); $warn-palette: map.get($color-config, 'warn');
mat-card.selected { 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 #card (click)="clickEvent.emit(this)" (dblclick)="dblClickEvent.emit(this)" [ngClass]="{'selected': gridEntry.selected}">
<mat-card-title *ngIf="!!file?.name">{{file?.name}}</mat-card-title> <mat-card-title *ngIf="!!gridEntry.file?.name">{{gridEntry.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">
</mat-card-content> </mat-card-content>

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

@ -1,8 +1,8 @@
<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 rowEntry of partitionedGridEntries">
<div class="file-row"> <div class="file-row">
<app-file-grid-entry (clickEvent)="setSelectedFile($event)" (dblClickEvent)="fileDblClickEvent.emit($event.file)" <app-file-grid-entry (clickEvent)="setSelectedFile($event.gridEntry)" (dblClickEvent)="fileDblClickEvent.emit($event.gridEntry.file)"
*ngFor="let file of fileRow" [file]="file"></app-file-grid-entry> *ngFor="let gridEntry of rowEntry" [gridEntry]="gridEntry"></app-file-grid-entry>
</div> </div>
</div> </div>
</cdk-virtual-scroll-viewport> </cdk-virtual-scroll-viewport>

@ -2,71 +2,91 @@ import {
Component, Component,
EventEmitter, EventEmitter,
HostListener, HostListener,
Input, Input, OnChanges,
OnInit, OnInit,
Output, QueryList, ViewChildren Output, QueryList, SimpleChanges, ViewChildren
} from '@angular/core'; } 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"; import {FileGridEntryComponent} from "./file-grid-entry/file-grid-entry.component";
import {GridEntry} from "./file-grid-entry/GridEntry";
@Component({ @Component({
selector: 'app-file-grid', selector: 'app-file-grid',
templateUrl: './file-grid.component.html', templateUrl: './file-grid.component.html',
styleUrls: ['./file-grid.component.scss'] 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() fileDblClickEvent = new EventEmitter<File>();
@Output() filesSelectEvent = new EventEmitter<File[]>(); @Output() filesSelectEvent = new EventEmitter<File[]>();
@ViewChildren(FileGridEntryComponent) childQuery!: QueryList<FileGridEntryComponent>; selectedEntries: GridEntry[] = [];
selectedEntries: FileGridEntryComponent[] = [];
private shiftClicked = false; private shiftClicked = false;
private ctrlClicked = 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 * File selector logic
* @param {FileGridEntryComponent} entry * @param {FileGridEntryComponent} clickedEntry
*/ */
setSelectedFile(entry: FileGridEntryComponent) { setSelectedFile(clickedEntry: GridEntry) {
if (!(this.shiftClicked || this.ctrlClicked) && this.selectedEntries.length > 0) { 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 = []; this.selectedEntries = [];
} }
// shift selector (forwards and backwards)
if (this.shiftClicked && this.selectedEntries.length > 0) { 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]; const lastEntry = this.selectedEntries[this.selectedEntries.length - 1];
let found = false; let found = false;
if (clickedEntry == lastEntry) {
return;
}
// TODO: change to use wrapped entry files instead because of reused components for (const gridEntry of this.gridEntries) {
for (const child of this.childQuery) {
if (found) { if (found) {
child.selected = true; gridEntry.selected = true;
this.selectedEntries.push(child); this.selectedEntries.push(gridEntry);
if (child === entry || child == lastEntry) { if (gridEntry === clickedEntry || gridEntry == lastEntry) {
break; return;
} }
} else if (child === lastEntry || child === entry) { } else if (gridEntry === lastEntry || gridEntry === clickedEntry) {
found = true; found = true;
if (child === entry) { if (gridEntry === clickedEntry) {
child.selected = true; gridEntry.selected = true;
this.selectedEntries.push(child); 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"]) @HostListener("window:keydown", ["$event"])

@ -7,7 +7,7 @@
<p>Drawer</p> <p>Drawer</p>
</mat-drawer> </mat-drawer>
<mat-drawer-content> <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-content>
</mat-drawer-container> </mat-drawer-container>

@ -13,25 +13,16 @@ import {ErrorBrokerService} from "../../services/error-broker/error-broker.servi
}) })
export class HomeComponent implements OnInit { export class HomeComponent implements OnInit {
fileRows: File[][] = []; files: File[] = [];
private openingLightbox = false; private openingLightbox = false;
constructor(private errorBroker: ErrorBrokerService, 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.files = files);
await this.fileService.getFiles(); 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) { async openFile(file: File) {
if (this.openingLightbox) { if (this.openingLightbox) {
return; return;

Loading…
Cancel
Save