ajout du drag and drop pour ajouter une image

This commit is contained in:
MiharyR 2021-11-09 14:10:36 +01:00
parent 54324794e9
commit 2f9c9c2b77
12 changed files with 377 additions and 12 deletions

View file

@ -0,0 +1,29 @@
<div title="bobo" class="container" appDragAndDrop (fileDropped)="onFileDropped($event)">
<input type="file" #fileDropRef id="fileDropRef" multiple (change)="fileBrowseHandler($event.target.files)" />
<h3>Images</h3>
<img src="/assets/uploadFile.png" width="50" height="50" style="margin-top: 5px">
<div style="font-style: italic; margin-top: 10px">Glisser déposer</div>
<div style="font-style: italic">ou</div>
<div style="font-style: italic" for="fileDropRef">Cliquer pour selectionner</div>
</div>
<mat-icon [title]=info_image>info</mat-icon>
<div class="files-list">
<div class="single-file" *ngFor="let file of files; let i = index">
<img src="assets/uploadFile.png" width="45px" alt="file">
<div class="info">
<h4 class="name">
{{ file?.name }}
</h4>
<p class="size">
{{ formatBytes(file?.size) }}
</p>
<div class="progress-cont">
<div class="progress" [style.width]="file?.progress + '%'">
</div>
</div>
</div>
<button mat-icon-button class="delete" width="20px" alt="file" (click)="deleteFile(i)">
<mat-icon>delete</mat-icon>
</button>
</div>
</div>

View file

@ -0,0 +1,143 @@
.container {
width: 450px;
height: 220px;
padding: 20px 0px 20px 0px;
text-align: center;
border: dashed 1px #979797;
position: relative;
margin: 0 auto;
}
input {
opacity: 0;
position: absolute;
z-index: 2;
width: 100%;
height: 100%;
top: 0;
left: 0;
}
h3 {
font-size: 20px;
font-weight: 600;
color: #38424c;
}
.fileover {
animation: shake 1s;
animation-iteration-count: infinite;
}
.files-list {
margin-top: 1.5rem;
.single-file {
display: flex;
padding: 0.5rem;
justify-content: space-between;
align-items: center;
border: dashed 1px #979797;
margin-bottom: 1rem;
img.delete {
margin-left: 0.5rem;
cursor: pointer;
align-self: flex-end;
}
display: flex;
flex-grow: 1;
.name {
font-size: 14px;
font-weight: 500;
color: #353f4a;
margin: 0;
}
.size {
font-size: 12px;
font-weight: 500;
color: #a4a4a4;
margin: 0;
margin-bottom: 0.25rem;
}
.info {
width: 100%
}
}
}
/* Shake animation */
@keyframes shake {
0% {
transform: translate(1px, 1px) rotate(0deg);
}
10% {
transform: translate(-1px, -2px) rotate(-1deg);
}
20% {
transform: translate(-3px, 0px) rotate(1deg);
}
30% {
transform: translate(3px, 2px) rotate(0deg);
}
40% {
transform: translate(1px, -1px) rotate(1deg);
}
50% {
transform: translate(-1px, 2px) rotate(-1deg);
}
60% {
transform: translate(-3px, 1px) rotate(0deg);
}
70% {
transform: translate(3px, 1px) rotate(-1deg);
}
80% {
transform: translate(-1px, -1px) rotate(1deg);
}
90% {
transform: translate(1px, 2px) rotate(0deg);
}
100% {
transform: translate(1px, -2px) rotate(-1deg);
}
}
.progress-cont {
height: 7px;
width: 100%;
border-radius: 4px;
background-color: #d0d0d0;
position: relative;
.progress {
width: 0;
height: 100%;
position: absolute;
z-index: 1;
top: 0;
left: 0;
border-radius: 4px;
background-color: #4c97cb;
transition: 0.5s all;
}
}

View file

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DragAndDropComponent } from './drag-and-drop.component';
describe('DragAndDropComponent', () => {
let component: DragAndDropComponent;
let fixture: ComponentFixture<DragAndDropComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DragAndDropComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(DragAndDropComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,90 @@
import {Component, ElementRef, OnInit, ViewChild} from '@angular/core';
@Component({
selector: 'app-drag-and-drop',
templateUrl: './drag-and-drop.component.html',
styleUrls: ['./drag-and-drop.component.scss']
})
export class DragAndDropComponent
{
@ViewChild("fileDropRef", { static: false }) fileDropEl: ElementRef;
files: any[] = [];
info_image = "Vos annonces seront affichées dans un rectangle de rapport 1/5 avec: \n • 1 la largeur durectangle \n • 5 la hauteur du rectangle" ;
/**
* on file drop handler
*/
onFileDropped($event) {
this.prepareFilesList($event);
}
/**
* handle file from browsing
*/
fileBrowseHandler(files) {
this.prepareFilesList(files);
}
/**
* Delete file from files list
* @param index (File index)
*/
deleteFile(index: number) {
if (this.files[index].progress < 100) {
console.log("Upload in progress.");
return;
}
this.files.splice(index, 1);
}
/**
* Simulate the upload process
*/
uploadFilesSimulator(index: number) {
setTimeout(() => {
if (index === this.files.length) {
return;
} else {
const progressInterval = setInterval(() => {
if (this.files[index].progress === 100) {
clearInterval(progressInterval);
this.uploadFilesSimulator(index + 1);
} else {
this.files[index].progress += 5;
}
}, 200);
}
}, 1000);
}
/**
* Convert Files list to normal array list
* @param files (Files List)
*/
prepareFilesList(files: Array<any>) {
for (const item of files) {
item.progress = 0;
this.files.push(item);
}
this.fileDropEl.nativeElement.value = "";
this.uploadFilesSimulator(0);
}
/**
* format bytes
* @param bytes (File size in bytes)
* @param decimals (Decimals point)
*/
formatBytes(bytes, decimals = 2) {
if (bytes === 0) {
return "0 Bytes";
}
const k = 1024;
const dm = decimals <= 0 ? 0 : decimals;
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
}
}

View file

@ -62,7 +62,8 @@ export class PageAdvertiserComponent implements OnInit
onAdd(): void onAdd(): void
{ {
const config = { const config = {
width: '50%', width: '40%',
height: '80%',
data: { action: "add", advert: null } data: { action: "add", advert: null }
}; };
this.dialog this.dialog
@ -89,7 +90,8 @@ export class PageAdvertiserComponent implements OnInit
onUpdate(advertToUpdate: Advert): void onUpdate(advertToUpdate: Advert): void
{ {
const config = { const config = {
width: '50%', width: '40%',
height: '80%',
data: { action: "update", advert: advertToUpdate } data: { action: "update", advert: advertToUpdate }
}; };
this.dialog this.dialog

View file

@ -5,7 +5,7 @@
<!-- --------------------------------------------------------------------------------------------- --> <!-- --------------------------------------------------------------------------------------------- -->
<mat-dialog-content> <div style="text-align: center; overflow-y: hidden">
<!-- Title --> <!-- Title -->
<mat-form-field appearance="fill"> <mat-form-field appearance="fill">
@ -15,9 +15,7 @@
<!-- Images --> <!-- Images -->
<br> <br>
<mat-form-field appearance="fill"> <app-drag-and-drop></app-drag-and-drop>
<input matInput type="file" disabled>
</mat-form-field>
<!-- Tags --> <!-- Tags -->
<app-bar-tags [myTags]="advert.tags" (eventEmitter)="onEventBarTags($event)"></app-bar-tags> <app-bar-tags [myTags]="advert.tags" (eventEmitter)="onEventBarTags($event)"></app-bar-tags>
@ -31,7 +29,7 @@
<!-- IsVisible --> <!-- IsVisible -->
<mat-checkbox [(ngModel)]="advert.isVisible"> Visible </mat-checkbox> <mat-checkbox [(ngModel)]="advert.isVisible"> Visible </mat-checkbox>
</mat-dialog-content> </div>
<!-- --------------------------------------------------------------------------------------------- --> <!-- --------------------------------------------------------------------------------------------- -->

View file

@ -1,5 +1,7 @@
.lightTheme, .darkTheme { .lightTheme, .darkTheme {
background-image: none; background-image: none;
overflow-y: hidden;
overflow-x: hidden;
} }
h1 { h1 {
@ -12,15 +14,15 @@ h1 {
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
mat-dialog-content {
overflow-y: hidden;
//padding: 20px 20px 20px 20px;
}
.commentContainer { .commentContainer {
width: 100%; width: 100%;
} }
.imageContainer {
border: solid 1px grey;
}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// --- LightTheme --- // --- LightTheme ---

View file

@ -29,6 +29,9 @@ export class PopupAddOrUpdateAdComponent implements OnInit
advert: Advert; advert: Advert;
urlBackend: string = "" ; urlBackend: string = "" ;
title: string = "" ; title: string = "" ;
tabWaitingFile: File[] = []; // fichiers selectionnés mais pas encore "validés"
tabSelectedFile: File[] = []; // fichier "validés"
_event;
constructor( public dialogRef: MatDialogRef<PopupAddOrUpdateAdComponent>, constructor( public dialogRef: MatDialogRef<PopupAddOrUpdateAdComponent>,
@ -79,9 +82,34 @@ export class PopupAddOrUpdateAdComponent implements OnInit
} }
onEventBarTags(myTags: string[]) onEventBarTags(myTags: string[]): void
{ {
this.advert.tags = myTags; this.advert.tags = myTags;
} }
// Lorsque l'annonceur selectionne des fichiers
onSelectFile(event)
{
const nbFileSelected = event.target.files.length ;
for(let i=0 ; i<nbFileSelected ; i++) this.tabWaitingFile.push(event.target.files[i]);
this._event = event;
}
// Lorsque l'annonceur "valide" sont choix de fichier selectionné
onValidateFiles(): void
{
const nbFile = this.tabWaitingFile.length;
for(let i=0 ; i<nbFile ; i++) this.tabSelectedFile.push(this.tabWaitingFile[i]);
this.tabWaitingFile = [];
this._event.target.value = "";
}
// Lorsque l'annonceur souhaite supprimer un fichier "validé"
onDeleteFile(file: File)
{
const index = this.tabSelectedFile.findIndex(x => file);
this.tabSelectedFile.splice(index, 1);
}
} }

View file

@ -46,6 +46,8 @@ import {MatAutocompleteModule} from "@angular/material/autocomplete";
import {MatSelectModule} from "@angular/material/select"; import {MatSelectModule} from "@angular/material/select";
import { PopupVisualizeImagesComponent } from './advertiser/popup-visualize-images/popup-visualize-images.component'; import { PopupVisualizeImagesComponent } from './advertiser/popup-visualize-images/popup-visualize-images.component';
import {IvyCarouselModule} from "angular-responsive-carousel"; import {IvyCarouselModule} from "angular-responsive-carousel";
import { DragAndDropComponent } from './advertiser/drag-and-drop/drag-and-drop.component';
import { DragAndDropDirective } from './utils/directives/dragAndDrop/drag-and-drop.directive';
@NgModule({ @NgModule({
@ -75,6 +77,8 @@ import {IvyCarouselModule} from "angular-responsive-carousel";
PopupVisualizeAdComponent, PopupVisualizeAdComponent,
BarTagsComponent, BarTagsComponent,
PopupVisualizeImagesComponent, PopupVisualizeImagesComponent,
DragAndDropComponent,
DragAndDropDirective,
], ],
imports: [ imports: [
BrowserModule, BrowserModule,

View file

@ -0,0 +1,8 @@
import { DragAndDropDirective } from './drag-and-drop.directive';
describe('DragAndDropDirective', () => {
it('should create an instance', () => {
const directive = new DragAndDropDirective();
expect(directive).toBeTruthy();
});
});

View file

@ -0,0 +1,36 @@
import {Directive, EventEmitter, HostBinding, HostListener, Output} from '@angular/core';
@Directive({
selector: '[appDragAndDrop]'
})
export class DragAndDropDirective
{
@HostBinding('class.fileover') fileOver: boolean;
@Output() fileDropped = new EventEmitter<any>();
// Dragover listener
@HostListener('dragover', ['$event']) onDragOver(evt) {
evt.preventDefault();
evt.stopPropagation();
this.fileOver = true;
}
// Dragleave listener
@HostListener('dragleave', ['$event']) public onDragLeave(evt) {
evt.preventDefault();
evt.stopPropagation();
this.fileOver = false;
}
// Drop listener
@HostListener('drop', ['$event']) public ondrop(evt) {
evt.preventDefault();
evt.stopPropagation();
this.fileOver = false;
let files = evt.dataTransfer.files;
if (files.length > 0) {
this.fileDropped.emit(files);
}
}
}

BIN
src/assets/uploadFile.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB