Merge branch 'advertiser2' into main
This commit is contained in:
commit
48fb0845f1
12 changed files with 377 additions and 12 deletions
|
|
@ -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>
|
||||
143
src/app/advertiser/drag-and-drop/drag-and-drop.component.scss
Normal file
143
src/app/advertiser/drag-and-drop/drag-and-drop.component.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
90
src/app/advertiser/drag-and-drop/drag-and-drop.component.ts
Normal file
90
src/app/advertiser/drag-and-drop/drag-and-drop.component.ts
Normal 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];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -62,7 +62,8 @@ export class PageAdvertiserComponent implements OnInit
|
|||
onAdd(): void
|
||||
{
|
||||
const config = {
|
||||
width: '50%',
|
||||
width: '40%',
|
||||
height: '80%',
|
||||
data: { action: "add", advert: null }
|
||||
};
|
||||
this.dialog
|
||||
|
|
@ -89,7 +90,8 @@ export class PageAdvertiserComponent implements OnInit
|
|||
onUpdate(advertToUpdate: Advert): void
|
||||
{
|
||||
const config = {
|
||||
width: '50%',
|
||||
width: '40%',
|
||||
height: '80%',
|
||||
data: { action: "update", advert: advertToUpdate }
|
||||
};
|
||||
this.dialog
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
<!-- --------------------------------------------------------------------------------------------- -->
|
||||
|
||||
<mat-dialog-content>
|
||||
<div style="text-align: center; overflow-y: hidden">
|
||||
|
||||
<!-- Title -->
|
||||
<mat-form-field appearance="fill">
|
||||
|
|
@ -15,9 +15,7 @@
|
|||
|
||||
<!-- Images -->
|
||||
<br>
|
||||
<mat-form-field appearance="fill">
|
||||
<input matInput type="file" disabled>
|
||||
</mat-form-field>
|
||||
<app-drag-and-drop></app-drag-and-drop>
|
||||
|
||||
<!-- Tags -->
|
||||
<app-bar-tags [myTags]="advert.tags" (eventEmitter)="onEventBarTags($event)"></app-bar-tags>
|
||||
|
|
@ -31,7 +29,7 @@
|
|||
<!-- IsVisible -->
|
||||
<mat-checkbox [(ngModel)]="advert.isVisible"> Visible </mat-checkbox>
|
||||
|
||||
</mat-dialog-content>
|
||||
</div>
|
||||
|
||||
<!-- --------------------------------------------------------------------------------------------- -->
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
.lightTheme, .darkTheme {
|
||||
background-image: none;
|
||||
overflow-y: hidden;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
h1 {
|
||||
|
|
@ -12,15 +14,15 @@ h1 {
|
|||
// -------------------------------------------------------------------------
|
||||
|
||||
|
||||
mat-dialog-content {
|
||||
overflow-y: hidden;
|
||||
//padding: 20px 20px 20px 20px;
|
||||
}
|
||||
|
||||
.commentContainer {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.imageContainer {
|
||||
border: solid 1px grey;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// --- LightTheme ---
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ export class PopupAddOrUpdateAdComponent implements OnInit
|
|||
advert: Advert;
|
||||
urlBackend: string = "" ;
|
||||
title: string = "" ;
|
||||
tabWaitingFile: File[] = []; // fichiers selectionnés mais pas encore "validés"
|
||||
tabSelectedFile: File[] = []; // fichier "validés"
|
||||
_event;
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ import {MatAutocompleteModule} from "@angular/material/autocomplete";
|
|||
import {MatSelectModule} from "@angular/material/select";
|
||||
import { PopupVisualizeImagesComponent } from './advertiser/popup-visualize-images/popup-visualize-images.component';
|
||||
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({
|
||||
|
|
@ -75,6 +77,8 @@ import {IvyCarouselModule} from "angular-responsive-carousel";
|
|||
PopupVisualizeAdComponent,
|
||||
BarTagsComponent,
|
||||
PopupVisualizeImagesComponent,
|
||||
DragAndDropComponent,
|
||||
DragAndDropDirective,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
@ -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
BIN
src/assets/uploadFile.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.6 KiB |
Reference in a new issue