Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update upload and add remove to mad-upload-file component #212

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,29 @@
#fileInput
(change)="uploadFile($event.target.files)"
(click)="fileInput.value = null"
[accept]="acceptForInput"
[accept]="acceptedFileTypes"
[id]="id"
[multiple]="multiple"
hidden
type="file"
/>
<mat-card-content>
<mat-icon>vertical_align_top</mat-icon>
{{ text ? text : 'Upload' }}
<ng-container *ngIf="hasSingleFile(); else uploadDefault">
{{ fileList[0].name }}
</ng-container>

<ng-template #uploadDefault>
<mat-icon>vertical_align_top</mat-icon>
{{ text ? text : 'Upload' }}
</ng-template>
</mat-card-content>
</mat-card>

<ng-container *ngIf="showFileList">
<mat-chip-listbox>
<mat-chip *ngFor="let file of fileList" (click)="openFile(file)" class="download">{{ file.name }}</mat-chip>
<mat-chip *ngFor="let file of fileList" (click)="openFile(file)" class="download" [removable]="removable" (removed)="remove(file)">
{{ file.name }}
<mat-icon matChipRemove *ngIf="removable">delete_forever</mat-icon>
</mat-chip>
</mat-chip-listbox>
</ng-container>
Original file line number Diff line number Diff line change
Expand Up @@ -22,48 +22,82 @@ export class FileUploadComponent implements OnInit {
@Input() accept: string[];
@Input() text: string;
@Input() showFileList: boolean = false;
@Input() removable: boolean = true;
@Output() fileEmitter = new EventEmitter<FileList>();
@Output() errorEmitter = new EventEmitter<UploadError>();

fileList: File[] = [];
acceptForInput: string[] = [];
private uploadError: boolean = false;
acceptedFileTypes: string[] = [];

ngOnInit(): void {
this.setAcceptedFileTypes();
}

private setAcceptedFileTypes(): void {
if (this.accept?.length) {
this.accept.forEach((accepted) => this.acceptForInput.push(`.${accepted}`));
this.acceptedFileTypes = this.accept.map((ext) => `.${ext.toLowerCase()}`);
}
}

uploadFile(fileList: FileList): void {
if (!this.multiple && (fileList.length > 1 || this.fileList.length === 1)) {
this.errorEmitter.emit('ONLY_SINGLE_FILE');
this.uploadError = false;
uploadFile(files: FileList): void {
const fileArray = Array.from(files);

if (!this.validateFileList(fileArray)) {
return;
}
if (this.accept && this.accept.length > 0) {
for (let i = 0; i < fileList.length; i++) {
this.getFileEnding(fileList.item(i).name);
}

this.addFiles(fileArray);
this.fileEmitter.emit(this.createFileListFromArray(this.fileList));
}

private validateFileList(fileArray: File[]): boolean {
if (!this.multiple && fileArray.length > 1) {
this.emitError('ONLY_SINGLE_FILE');
return false;
}
if (!this.uploadError) {
for (let i = 0; i < fileList.length; i++) {
this.fileList.push(fileList.item(i));

for (const file of fileArray) {
if (!this.isAcceptedFileType(file.name)) {
this.emitError('FILETYPE_NOT_SUPPORTED');
return false;
}
this.fileEmitter.emit(fileList);
}
this.uploadError = false;

return true;
}

getFileEnding(name: string): void {
const ending = name.substring(name.lastIndexOf('.') + 1);
if (this.accept.filter((a) => a.toLowerCase() === ending.toLowerCase()).length === 0) {
this.errorEmitter.emit('FILETYPE_NOT_SUPPORTED');
this.uploadError = true;
private emitError(errorType: UploadError): void {
this.errorEmitter.emit(errorType);
}

private isAcceptedFileType(fileName: string): boolean {
const fileExtension = fileName.split('.').pop()?.toLowerCase();
return this.acceptedFileTypes.includes(`.${fileExtension}`);
}

private addFiles(fileArray: File[]): void {
if (!this.multiple) {
this.fileList = [];
}
this.fileList.push(...fileArray);
}

openFile(file: File) {
openFile(file: File): void {
window.open(window.URL.createObjectURL(file));
}

remove(file: File): void {
this.fileList = this.fileList.filter((f) => f !== file);
this.fileEmitter.emit(this.createFileListFromArray(this.fileList));
}

private createFileListFromArray(files: File[]): FileList {
const dataTransfer = new DataTransfer();
files.forEach((file) => dataTransfer.items.add(file));
return dataTransfer.files;
}

hasSingleFile(): boolean {
return !this.multiple && this.fileList.length === 1;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<div class="api-specification">
<h2>Properties</h2>
<table>
<thead>
<tr>
<th>Property</th>
<th>Type</th>
<th>Default Value</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>id</code></td>
<td><code>string</code></td>
<td><code>''</code></td>
<td>HTML id of the element.</td>
</tr>
<tr>
<td><code>multiple</code></td>
<td><code>boolean</code></td>
<td><code></code></td>
<td>if <code>false</code> only 1 document can be added, otherwise you can add as much documents as you want.</td>
</tr>
<tr>
<td><code>accept</code></td>
<td><code>string[]</code></td>
<td><code>'[]'</code></td>
<td>is a string array which contains all valid document types.</td>
</tr>
<tr>
<td><code>text</code></td>
<td><code>string</code></td>
<td><code>'Upload'</code></td>
<td>Text in the Upload Field.</td>
</tr>
<tr>
<td><code>showFileList</code></td>
<td><code>boolean</code></td>
<td><code>false</code></td>
<td>Shows the list of added files.</td>
</tr>
<tr>
<td><code>removable</code></td>
<td><code>boolean</code></td>
<td><code>true</code></td>
<td>Uploaded file can be deleted using icon in chip.</td>
</tr>
</tbody>
</table>

<h2>Events</h2>
<table>
<thead>
<tr>
<th>Event</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>fileEmitter</code></td>
<td>Emits when file is uploaded/removed. Contains file list</td>
</tr>
<tr>
<td><code>errorEmitter</code></td>
<td>(ErrorType) ONLY_SINGLE_FILE: if only 1 File is allowed and user tries to add more than 1 File in 1 dialog.</td>
</tr>
<tr>
<td><code>errorEmitter</code></td>
<td>(ErrorType) FILETYPE_NOT_SUPPORTED: if user tries to add unsupported file type</td>
</tr>
</tbody>
</table>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
.api-specification {
font-family: Arial, sans-serif;
}

.api-specification h2 {
color: #333;
}

.api-specification table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}

.api-specification th,
.api-specification td {
border: 1px solid #ccc;
padding: 8px;
text-align: left;
}

.api-specification th {
background-color: #f2f2f2;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Component } from '@angular/core';

@Component({
selector: 'app-upload-demo-api-spec',
standalone: true,
imports: [],
templateUrl: './upload-demo-api-spec.component.html',
styleUrl: './upload-demo-api-spec.component.scss',
})
export class UploadDemoApiSpecComponent {}
15 changes: 4 additions & 11 deletions src/app/component-demos/upload-demo/upload-demo.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,9 @@
<br />
<h2>Behavior</h2>
<ul>
<li>clickable with dialog to select files</li>
<li>Clickable with dialog to select files</li>
<li>Drag And Drop Functionality</li>
</ul>

<br />
<h2>Inputs</h2>
<ul>
<li>accept: is a string array which contains all valid document types</li>
<li>multiple: if false only 1 document can be added, otherwise you can add as much documents as you want</li>
<li>text: Text in the Upload Field</li>
<li>id: HTML id of the element</li>
<li>showFileList: Shows the list of added files</li>
<li>Replace file with a new one in single upload mode</li>
</ul>

<br />
Expand All @@ -37,3 +28,5 @@ <h2>Output</h2>
</p>

<example-viewer [example]="uploadComponent"></example-viewer>
<h2>API Specification</h2>
<app-upload-demo-api-spec></app-upload-demo-api-spec>
3 changes: 2 additions & 1 deletion src/app/component-demos/upload-demo/upload-demo.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import { Example } from '../../components/example-viewer/example.class';
import { UploadFileComponent } from '../../example-components/upload-file/upload-file.component';
import { ExampleViewerComponent } from '../../components/example-viewer/example-viewer.component';
import { TextCodeComponent } from '../../components/text-code/text-code.component';
import { UploadDemoApiSpecComponent } from './upload-demo-api-spec/upload-demo-api-spec.component';

@Component({
selector: 'app-upload-demo',
templateUrl: './upload-demo.component.html',
styleUrl: './upload-demo.component.scss',
standalone: true,
imports: [TextCodeComponent, ExampleViewerComponent],
imports: [TextCodeComponent, ExampleViewerComponent, UploadDemoApiSpecComponent],
})
export class UploadDemoComponent {
uploadComponent = new Example(UploadFileComponent, 'upload-file', 'upload');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@

<mat-checkbox [(ngModel)]="multiple">{{ 'Allow Multiple Uploads' }}</mat-checkbox>
<mat-checkbox [(ngModel)]="showFileList">{{ 'Show File List' }}</mat-checkbox>
<mat-checkbox [(ngModel)]="removable">{{ 'Removable File' }}</mat-checkbox>

<mad-file-upload
[accept]="accept"
[multiple]="multiple"
[text]="'Upload File'"
[showFileList]="showFileList"
[removable]="removable"
[id]="'upload'"
(fileEmitter)="filesEmitted($event)"
(errorEmitter)="errorReceived($event)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export class UploadFileComponent {

possibleFileEndings = ['pdf', 'doc', 'jpg', 'xls', 'xlsx', 'docx', 'doc', 'gif', 'png'];
showFileList: boolean = false;
removable: boolean = false;

filesEmitted(fileList: FileList) {
for (let i = 0; i < fileList.length; i++) {
Expand Down
Loading