drag & drop renewed, imageCropper revisited, imageOrder persisted, css quirks

This commit is contained in:
2024-03-31 19:44:08 +02:00
parent 89bb85a512
commit a437851f6d
28 changed files with 767 additions and 203 deletions

View File

@@ -133,27 +133,24 @@ export class AccountComponent {
'640px': '90vw'
},
});
this.dialogRef.onClose.subscribe(blob => {
if (blob) {
this.imageUploadService.uploadImage(blob, type==='company'?'uploadCompanyLogo':'uploadProfile',this.user.id).subscribe(async(event) => {
if (event.type === HttpEventType.UploadProgress) {
const progress = event.total ? event.loaded / event.total : 0;
console.log(`Upload-Fortschritt: ${progress * 100}%`);
} else if (event.type === HttpEventType.Response) {
console.log('Upload abgeschlossen', event.body);
this.loadingService.stopLoading('uploadImage');
if (this.type==='company'){
this.user.hasCompanyLogo=true;
this.companyLogoUrl=`${environment.apiBaseUrl}/logo/${this.user.id}.avif?_ts=${new Date().getTime()}`
} else {
this.user.hasProfile=true;
this.profileUrl=`${environment.apiBaseUrl}/profile/${this.user.id}.avif?_ts=${new Date().getTime()}`
this.dialogRef.onClose.subscribe(cropper => {
if (cropper){
this.loadingService.startLoading('uploadImage');
cropper.getCroppedCanvas().toBlob(async (blob) => {
this.imageUploadService.uploadImage(blob, type==='company'?'uploadCompanyLogo':'uploadProfile',this.user.id).subscribe(async(event) => {
if (event.type === HttpEventType.Response) {
this.loadingService.stopLoading('uploadImage');
if (this.type==='company'){
this.user.hasCompanyLogo=true;
this.companyLogoUrl=`${environment.apiBaseUrl}/logo/${this.user.id}.avif?_ts=${new Date().getTime()}`
} else {
this.user.hasProfile=true;
this.profileUrl=`${environment.apiBaseUrl}/profile/${this.user.id}.avif?_ts=${new Date().getTime()}`
}
}
}
}, error => console.error('Fehler beim Upload:', error));
}, error => console.error('Fehler beim Upload:', error));
})
}
});
})
}
}

View File

@@ -94,30 +94,19 @@
</div>
</div>
</div>
<!-- <p-carousel [value]="propertyImages" [numVisible]="3" [numScroll]="3" [circular]="false" [responsiveOptions]="responsiveOptions">
<ng-template let-image pTemplate="item">
<div class="border-1 surface-border border-round m-2 text-center py-5 px-3">
<div class="mb-3">
<img src="{{environment.apiBaseUrl}}/property/{{listing.id}}/{{image.name}}" [alt]="image.name" class="w-11 shadow-2" />
</div>
<div>
<div class="mt-5 flex align-items-center justify-content-center gap-2">
<p-button icon="pi pi-file-edit" [rounded]="true" />
<p-button icon="fa-solid fa-trash-can" [rounded]="true" severity="danger" (click)="deleteConfirm(image.name)"></p-button>
</div>
</div>
</div>
</ng-template>
</p-carousel> -->
<div class="p-2 border-1 surface-border border-round mb-4 image-container" cdkDropList (cdkDropListDropped)="onDrop($event)"
<div class="p-2 border-1 surface-border border-round mb-4 image-container" cdkDropListGroup mixedCdkDragDrop
(dropped)="onDrop($event)"
cdkDropListOrientation="horizontal">
<ul >
<li *ngFor="let image of propertyImages" class="p-2 border-round shadow-1" >
<!-- <div class="image-container"> -->
<img src="{{environment.apiBaseUrl}}/property/{{listing.id}}/{{image.name}}" [alt]="image.name" class="shadow-2" cdkDrag/>
<!-- </div> -->
</li>
</ul>
@for (image of propertyImages; track image) {
<span cdkDropList mixedCdkDropList>
<div cdkDrag mixedCdkDragSizeHelper class="image-wrap">
<img src="{{environment.apiBaseUrl}}/property/{{listing.id}}/{{image.name}}"
[alt]="image.name" class="shadow-2" cdkDrag>
<fa-icon [icon]="faTrash" (click)="deleteConfirm(image.name)"></fa-icon>
</div>
</span>
}
</div>
}
@if (listing.listingsCategory==='business'){

View File

@@ -2,86 +2,56 @@
transform: translateY(5px);
}
// .image {
// width: 120px;
// height: 30px;
// border: 1px solid #6b7280;
// padding: 1px 1px;
// object-fit: contain;
// }
// .image-container img {
// width: 200px;
// box-shadow: 0 3px 6px #00000029, 0 3px 6px #0000003b;
// margin-right: 1rem;
// }
// .container {
// width: 100%;
// min-height: 200px;
// border: 1px solid #ccc;
// display: flex;
// flex-wrap: wrap;
// }
.image-container {
width: 100%;
/* oder eine spezifische Breite */
overflow-x: auto;
/* Ermöglicht das Scrollen, wenn die Bilder zu breit sind */
}
display: flex; /* Erlaubt ein flexibles Box-Layout */
flex-wrap: wrap; /* Erlaubt das Umfließen der Elemente auf die nächste Zeile */
justify-content: flex-start; /* Startet die Anordnung der Elemente am Anfang des Containers */
align-items: flex-start; /* Ausrichtung der Elemente am Anfang der Querachse */
padding: 10px; /* Abstand zwischen den Inhalten des Containers und dessen Rand */
}
.image-container ul {
.image-container span {
flex-flow: row;
display: flex;
padding: 0;
/* Entfernt den Standard-Abstand des ul-Elements */
margin: 0;
/* Entfernt den Standard-Außenabstand des ul-Elements */
list-style-type: none;
/* Entfernt die Listenpunkte */
width: fit-content;
height: fit-content;
}
.image-container li {
flex: 1 1 auto;
/* Erlaubt den li-Elementen, zu wachsen und zu schrumpfen, aber füllt den Raum gleichmäßig */
/* Optional: Füge hier Abstände zwischen den li-Elementen hinzu */
}
.image-container img {
max-width: 100%;
/* Stellt sicher, dass die Bilder nicht über ihre natürliche Größe hinaus wachsen */
height: auto;
/* Behält das Seitenverhältnis bei */
.image-container span img {
max-height: 150px; /* Maximale Höhe der Bilder */
width: auto; /* Die Breite der Bilder passt sich automatisch an die Höhe an */
cursor: pointer;
margin: 10px;
}
// .image-container fa-icon {
// top: 0; /* Positioniert das Icon am oberen Rand des Bildes */
// right: 0; /* Positioniert das Icon am rechten Rand des Bildes */
// color: #fff; /* Weiße Farbe für das Icon */
// background-color: rgba(0,0,0,0.5); /* Halbtransparenter Hintergrund für bessere Sichtbarkeit */
// padding: 5px; /* Ein wenig Platz um das Icon */
// cursor: pointer; /* Verwandelt den Cursor in eine Hand, um Interaktivität anzudeuten */
// }
.draggable-image {
margin: 8px;
cursor: grab;
}
.draggable-image:active {
box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),
0 8px 10px 1px rgba(0, 0, 0, 0.14),
0 3px 14px 2px rgba(0, 0, 0, 0.12);
}
// .cdk-drag-preview {
// box-shadow: 0 5px 5px rgba(0, 0, 0, 0.2);
// }
// .cdk-drag-placeholder {
// background-color: #ccc;
// }
.drop-area {
border: 2px dashed #ccc;
padding: 20px;
text-align: center;
transition: all 0.3s;
}
/* CSS-Klasse für den Drop-Bereich, wenn ein Element darüber gezogen wird */
.drop-area-active {
border-color: #2196F3;
background-color: #E3F2FD;
}
.image-wrap {
position: relative; /* Ermöglicht die absolute Positionierung des Icons bezogen auf diesen Container */
display: inline-block; /* Erlaubt die Inline-Anordnung, falls mehrere Bilder vorhanden sind */
}
/* Stil für das Bild */
.image-wrap img {
max-height: 150px;
width: auto;
display: block; /* Verhindert unerwünschten Abstand unter dem Bild */
}
/* Stil für das FontAwesome Icon */
.image-wrap fa-icon {
position: absolute;
top: 15px; /* Positioniert das Icon am oberen Rand des Bildes */
right: 15px; /* Positioniert das Icon am rechten Rand des Bildes */
color: #fff; /* Weiße Farbe für das Icon */
background-color: rgba(0,0,0,0.5); /* Halbtransparenter Hintergrund für bessere Sichtbarkeit */
padding: 5px; /* Ein wenig Platz um das Icon */
cursor: pointer; /* Verwandelt den Cursor in eine Hand, um Interaktivität anzudeuten */
border-radius: 8px; /* Optional: Abrunden der linken unteren Ecke für ästhetische Zwecke */
}

View File

@@ -43,10 +43,14 @@ import { DialogService, DynamicDialogModule, DynamicDialogRef } from 'primeng/dy
import { ImageCropperComponent } from '../../../components/image-cropper/image-cropper.component';
import { ConfirmDialogModule } from 'primeng/confirmdialog';
import { CdkDragDrop, CdkDragEnter, CdkDragExit, DragDropModule, moveItemInArray } from '@angular/cdk/drag-drop';
import { MixedCdkDragDropModule } from 'angular-mixed-cdk-drag-drop';
import { faTrash } from '@fortawesome/free-solid-svg-icons';
@Component({
selector: 'create-listing',
standalone: true,
imports: [SharedModule, ArrayToStringPipe, InputNumberModule, CarouselModule, DialogModule, AngularCropperjsModule, FileUploadModule, EditorModule, DynamicDialogModule, ConfirmDialogModule,DragDropModule],
imports: [SharedModule, ArrayToStringPipe, InputNumberModule, CarouselModule,
DialogModule, AngularCropperjsModule, FileUploadModule, EditorModule, DynamicDialogModule, DragDropModule,
ConfirmDialogModule, MixedCdkDragDropModule],
providers: [MessageService, DialogService, ConfirmationService],
templateUrl: './edit-listing.component.html',
styleUrl: './edit-listing.component.scss'
@@ -85,8 +89,8 @@ export class EditListingComponent {
config = { aspectRatio: 16 / 9 }
editorModules = TOOLBAR_OPTIONS
dialogRef: DynamicDialogRef | undefined;
draggedImage:ImageProperty
dropAreaActive = false;
draggedImage: ImageProperty
faTrash = faTrash;
constructor(public selectOptions: SelectOptionsService,
private router: Router,
private activatedRoute: ActivatedRoute,
@@ -155,18 +159,35 @@ export class EditListingComponent {
'640px': '90vw'
},
});
this.dialogRef.onClose.subscribe(blob => {
if (blob) {
this.imageService.uploadImage(blob, 'uploadPropertyPicture', this.listing.id).subscribe(async (event) => {
if (event.type === HttpEventType.Response) {
console.log('Upload abgeschlossen', event.body);
this.loadingService.stopLoading('uploadImage');
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id)
}
}, error => console.error('Fehler beim Upload:', error));
this.dialogRef.onClose.subscribe(cropper => {
if (cropper){
this.loadingService.startLoading('uploadImage');
cropper.getCroppedCanvas().toBlob(async (blob) => {
this.imageService.uploadImage(blob, 'uploadPropertyPicture', this.listing.id).subscribe(async (event) => {
if (event.type === HttpEventType.Response) {
console.log('Upload abgeschlossen', event.body);
this.loadingService.stopLoading('uploadImage');
this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id)
}
}, error => console.error('Fehler beim Upload:', error));
}, 'image/jpg');
cropper.destroy();
}
});
})
// this.dialogRef.onClose.subscribe(blob => {
// if (blob) {
// // this.loadingService.startLoading('uploadImage');
// setTimeout(()=>{
// this.imageService.uploadImage(blob, 'uploadPropertyPicture', this.listing.id).subscribe(async (event) => {
// if (event.type === HttpEventType.Response) {
// console.log('Upload abgeschlossen', event.body);
// // this.loadingService.stopLoading('uploadImage');
// this.propertyImages = await this.listingsService.getPropertyImages(this.listing.id)
// }
// }, error => console.error('Fehler beim Upload:', error));
// },10)
// }
// });
}
deleteConfirm(imageName: string) {
@@ -193,19 +214,9 @@ export class EditListingComponent {
});
}
onDrop(event: CdkDragDrop<string[]>) {
this.dropAreaActive = false;
onDrop(event: { previousIndex: number; currentIndex: number }) {
moveItemInArray(this.propertyImages, event.previousIndex, event.currentIndex);
//console.log(event.previousIndex, event.currentIndex);
}
onDragEnter(event: CdkDragEnter<any,any>) {
this.dropAreaActive = true;
}
onDragExit(event: CdkDragExit<any,any>) {
this.dropAreaActive = false;
this.listingsService.changeImageOrder(this.listing.id, this.propertyImages)
}
}