Amélioration de l'UI : meilleur affichage des images que ce soit dans la partie lore ou la partie campagne (partie campagne : visualisation scrapbooking). Possibilité de réordonner les champs dans les templates...

Passage v0.3.0
This commit is contained in:
2026-04-21 16:56:27 +02:00
parent 1e34f7f954
commit 71449bee1b
45 changed files with 1045 additions and 90 deletions

View File

@@ -1,8 +1,9 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core';
import { CommonModule } from '@angular/common';
import { LucideAngularModule, X, Image as ImageIcon } from 'lucide-angular';
import { LucideAngularModule, X, Image as ImageIcon, ChevronLeft, ChevronRight } from 'lucide-angular';
import { ImageService } from '../../services/image.service';
import { Image } from '../../services/image.model';
import { ImageLayout } from '../../services/template.model';
import { ImageUploaderComponent } from '../image-uploader/image-uploader.component';
/**
@@ -34,6 +35,8 @@ import { ImageUploaderComponent } from '../image-uploader/image-uploader.compone
export class ImageGalleryComponent {
readonly X = X;
readonly ImageIcon = ImageIcon;
readonly ChevronLeft = ChevronLeft;
readonly ChevronRight = ChevronRight;
/** IDs d'images a afficher. */
@Input() imageIds: string[] = [];
@@ -41,14 +44,45 @@ export class ImageGalleryComponent {
/** Mode edition : afficher le bouton d'ajout + les boutons de suppression. */
@Input() editable = false;
/**
* Variante de mise en page. Null/undefined = GALLERY (rendu historique).
* HERO : premiere image en banniere pleine largeur, suivantes en petit dessous.
* MASONRY : mosaique a hauteurs variables.
* CAROUSEL : defilement horizontal avec fleches.
*/
@Input() layout: ImageLayout | null | undefined = 'GALLERY';
/** Emet la nouvelle liste quand l'utilisateur ajoute/retire une image. */
@Output() imageIdsChange = new EventEmitter<string[]>();
/** ID de l'image actuellement ouverte en lightbox (null = ferme). */
lightboxId: string | null = null;
@ViewChild('carouselTrack') carouselTrack?: ElementRef<HTMLDivElement>;
constructor(private imageService: ImageService) {}
/** Layout effectif (null → GALLERY). */
get effectiveLayout(): ImageLayout {
return this.layout ?? 'GALLERY';
}
/** Premiere image (pour le layout HERO). */
get heroId(): string | null {
return this.imageIds[0] ?? null;
}
/** Images restantes apres la hero (pour le layout HERO). */
get restIds(): string[] {
return this.imageIds.slice(1);
}
scrollCarousel(direction: -1 | 1): void {
const el = this.carouselTrack?.nativeElement;
if (!el) return;
el.scrollBy({ left: direction * Math.max(240, el.clientWidth * 0.8), behavior: 'smooth' });
}
/** URL absolue du binaire d'une image. */
urlFor(id: string): string {
return this.imageService.contentUrl(id);