From 8bca0bac2299995b254c66feb1e2e2eee49f89c7 Mon Sep 17 00:00:00 2001 From: Alexis Feron Date: Thu, 19 Jun 2025 23:29:58 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Enhance=20file=20upload=20validatio?= =?UTF-8?q?n=20in=20add-pin=20and=20preview=20functionality=20in=20drag-dr?= =?UTF-8?q?op=20component?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../add-pin-popup.component.html | 6 ++ .../add-pin-popup/add-pin-popup.component.ts | 6 +- .../drag-drop/drag-drop.component.html | 34 ++++++++- .../drag-drop/drag-drop.component.ts | 70 +++++++++++++++++-- .../edit-pin-popup.component.ts | 10 ++- 5 files changed, 112 insertions(+), 14 deletions(-) diff --git a/src/app/components/add-pin-popup/add-pin-popup.component.html b/src/app/components/add-pin-popup/add-pin-popup.component.html index ec7295e..49bc685 100644 --- a/src/app/components/add-pin-popup/add-pin-popup.component.html +++ b/src/app/components/add-pin-popup/add-pin-popup.component.html @@ -136,6 +136,12 @@ (fileRemoved)="removeFile($event)" [errorMessage]="uploadError" > +
+ Il est obligatoire d'ajouter au moins une image +
diff --git a/src/app/components/add-pin-popup/add-pin-popup.component.ts b/src/app/components/add-pin-popup/add-pin-popup.component.ts index 1608e60..d856075 100644 --- a/src/app/components/add-pin-popup/add-pin-popup.component.ts +++ b/src/app/components/add-pin-popup/add-pin-popup.component.ts @@ -61,7 +61,7 @@ export class AddPinPopupComponent implements OnInit { location: new FormControl('', [Validators.required]), complete_address: new FormControl('', [Validators.required]), coordinates: new FormControl([]), - files: new FormControl([]), + files: new FormControl([], [Validators.required]), date: new FormControl(''), }); } @@ -175,8 +175,8 @@ export class AddPinPopupComponent implements OnInit { } } - getFileNames(): string[] { - return this.files.map((file) => file.name); + getFileNames(): { name: string }[] { + return this.files.map((f) => ({ name: f.name })); } ngOnDestroy() { diff --git a/src/app/components/drag-drop/drag-drop.component.html b/src/app/components/drag-drop/drag-drop.component.html index 7193af6..c3cc7ed 100644 --- a/src/app/components/drag-drop/drag-drop.component.html +++ b/src/app/components/drag-drop/drag-drop.component.html @@ -50,12 +50,24 @@ class="mt-2 text-sm text-gray-500 dark:text-gray-400" >
    -
  • - {{ fileName }} +
  • + + {{ + fileObj.name.length > 24 + ? (fileObj.name | slice : 0 : 10) + + "..." + + (fileObj.name | slice : -10) + : fileObj.name + }} + +
    + Preview +
diff --git a/src/app/components/drag-drop/drag-drop.component.ts b/src/app/components/drag-drop/drag-drop.component.ts index e370fe3..85bcc17 100644 --- a/src/app/components/drag-drop/drag-drop.component.ts +++ b/src/app/components/drag-drop/drag-drop.component.ts @@ -2,24 +2,35 @@ import { CommonModule } from '@angular/common'; import { Component, EventEmitter, + HostListener, Input, OnChanges, + OnDestroy, Output, SimpleChanges, } from '@angular/core'; +import { ImageService } from '../../services/image/image.service'; @Component({ selector: 'app-drag-drop', imports: [CommonModule], templateUrl: './drag-drop.component.html', }) -export class DragDropComponent implements OnChanges { - @Input() initialFiles: string[] = []; +export class DragDropComponent implements OnChanges, OnDestroy { + @Input() initialFiles: { name: string; id?: string }[] = []; @Input() errorMessage: string = ''; - fileNames: string[] = []; + fileNames: { name: string; id?: string }[] = []; @Output() filesSelected = new EventEmitter(); @Output() fileRemoved = new EventEmitter(); + hoveredFileName: string | null = null; + previewUrl: string | null = null; + private objectUrl: string | null = null; + private imageUrlCache: { [id: string]: string } = {}; + private lastHoveredId: string | null = null; + + constructor(private imageService: ImageService) {} + ngOnChanges(changes: SimpleChanges) { if (changes['initialFiles']) { this.fileNames = [...this.initialFiles]; @@ -58,20 +69,67 @@ export class DragDropComponent implements OnChanges { } updateFileNamesFromFileList(files: FileList): void { - this.fileNames = Array.from(files).map((file) => file.name); + this.fileNames = Array.from(files).map((file) => ({ name: file.name })); } private updateFileNames(files: FileList): void { - const newFileNames = Array.from(files).map(file => file.name); + const newFileNames = Array.from(files).map((file) => ({ name: file.name })); this.fileNames = [...this.fileNames, ...newFileNames]; } removeFile(fileName: string, event: Event): void { event.stopPropagation(); // Empêcher la propagation du clic - const index = this.fileNames.indexOf(fileName); + const index = this.fileNames.findIndex((f) => f.name === fileName); if (index > -1) { this.fileNames.splice(index, 1); this.fileRemoved.emit(fileName); } } + + async onFileNameMouseEnter(fileName: string): Promise { + const fileObj = this.getFileByName(fileName); + if (!fileObj) { + this.hoveredFileName = null; + this.previewUrl = null; + this.lastHoveredId = null; + return; + } + // Ne rien faire si on survole le même fichier + if (fileObj.id && this.lastHoveredId === fileObj.id) { + return; + } + this.hoveredFileName = fileName; + this.lastHoveredId = fileObj.id || null; + if (fileObj.id) { + if (this.imageUrlCache[fileObj.id]) { + this.previewUrl = this.imageUrlCache[fileObj.id]; + } else { + this.imageService.getImage(fileObj.id).subscribe((blob) => { + const objectUrl = URL.createObjectURL(blob); + this.imageUrlCache[fileObj.id!] = objectUrl; + this.previewUrl = objectUrl; + }); + } + } else { + this.previewUrl = null; + } + } + + @HostListener('mouseleave') onFileNameMouseLeave(): void { + this.hoveredFileName = null; + this.previewUrl = null; + this.lastHoveredId = null; + } + + getFileByName(fileName: string): { name: string; id?: string } | undefined { + return this.fileNames.find((f) => f.name === fileName); + } + + ngOnDestroy(): void { + // Révoquer tous les objectURLs du cache + Object.values(this.imageUrlCache).forEach((url) => + URL.revokeObjectURL(url) + ); + this.imageUrlCache = {}; + } } diff --git a/src/app/components/edit-pin-popup/edit-pin-popup.component.ts b/src/app/components/edit-pin-popup/edit-pin-popup.component.ts index f88c7cc..c8f0627 100644 --- a/src/app/components/edit-pin-popup/edit-pin-popup.component.ts +++ b/src/app/components/edit-pin-popup/edit-pin-popup.component.ts @@ -324,7 +324,13 @@ export class EditPinPopupComponent implements OnInit, OnDestroy { } } - getFileNames(): string[] { - return this.files.map((file) => (file.name ? file.name.split('|')[0] : '')); + getFileNames(): { name: string; id?: string }[] { + return this.files.map((file) => { + if (file.name && file.name.includes('|')) { + const [name, id] = file.name.split('|'); + return { name, id }; + } + return { name: file.name || '' }; + }); } }