Skip to content

🎉 feat: release v17 #67

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

Merged
merged 30 commits into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
49a71b8
:tada: feat(#57): allow arbitrary content within mat-dropzone, like i…
hackingharold Oct 13, 2023
4e4dc10
:wrench: fix: mat-dropzone exportAs name.
hackingharold Oct 13, 2023
3e2ed4c
:tada: feat(#57): add cdk file preview.
hackingharold Oct 13, 2023
c94f1fa
:tada: feat(#57): add cdk file image preview.
hackingharold Oct 13, 2023
87effb3
:sparkles: refactor: rename to image preview component.
hackingharold Oct 13, 2023
f993dd8
:sparkles: refactor: move mat-dropzone into subfolder.
hackingharold Oct 13, 2023
fc45453
:sparkles: refactor: revert file-preview components.
hackingharold Nov 20, 2023
b424d7f
:sparkles: refactor: remove extra dropzone padding.
hackingharold Nov 20, 2023
bc748db
:tada: feat(#57): add mat-chips file preview.
hackingharold Nov 20, 2023
6971ed8
:wrench: fix: add padding for filled mat-dropzone with chips.
hackingharold Nov 21, 2023
99937a7
:pencil: docs: update documentation to include new file preview chips.
hackingharold Nov 21, 2023
be2cf1b
:test_tube: test: add test for mat chips file preview.
hackingharold Nov 21, 2023
cb0646b
:building_construction: build: upgrade to angular v17.
hackingharold Nov 21, 2023
b89a522
:building_construction: build: upgrade to angular v17.
hackingharold Nov 21, 2023
e18bfd9
:building_construction: build: upgrade to angular v17.
hackingharold Nov 21, 2023
728e24b
:building_construction: build: upgrade to angular v17.
hackingharold Nov 21, 2023
8e81ec7
:building_construction: build: upgrade to angular v17.
hackingharold Nov 21, 2023
9c30506
:building_construction: build: upgrade to angular v17.
hackingharold Nov 21, 2023
18eb3b0
:tada: feat(#60): add new dropzone service which accepts dropping fol…
hackingharold Nov 22, 2023
0f52e5e
:pencil: docs: add info about folder drops to readme.
hackingharold Nov 22, 2023
e823ef7
:building_construction: build: upgrade peer deps to v17.
hackingharold Nov 22, 2023
1cc94a8
Merge branch 'feat/file-preview' into releases/ngx-dropzone-v17
hackingharold Nov 22, 2023
c5458da
Merge branch 'feat/folder-upload' into releases/ngx-dropzone-v17
hackingharold Nov 22, 2023
126d948
:pencil: docs: remove old file preview notice.
hackingharold Nov 22, 2023
20c42af
:building_construction: build: update package-lock.json.
hackingharold Nov 22, 2023
3571883
:building_construction: build: use fixed typescript version.
hackingharold Nov 22, 2023
7946d0c
:building_construction: build: use fixed typescript version.
hackingharold Nov 22, 2023
879c9e4
:building_construction: build: re-initialised package-lock.json.
hackingharold Nov 22, 2023
593238f
:building_construction: build: update browserslist before build.
hackingharold Nov 22, 2023
93e238e
:building_construction: build: re-initialised dependencies.
hackingharold Nov 22, 2023
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
6 changes: 3 additions & 3 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,18 +145,18 @@
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"browserTarget": "app:build:production"
"buildTarget": "app:build:production"
},
"development": {
"browserTarget": "app:build:development"
"buildTarget": "app:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "app:build"
"buildTarget": "app:build"
}
},
"test": {
Expand Down
Binary file modified bun.lockb
Binary file not shown.
Binary file modified example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 24 additions & 24 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,41 +13,41 @@
"lint:material": "ng lint material"
},
"dependencies": {
"@angular/animations": "^16.2.4",
"@angular/cdk": "~16.2.3",
"@angular/common": "^16.2.4",
"@angular/compiler": "^16.2.4",
"@angular/core": "^16.2.4",
"@angular/forms": "^16.2.4",
"@angular/material": "~16.2.3",
"@angular/platform-browser": "^16.2.4",
"@angular/platform-browser-dynamic": "^16.2.4",
"@angular/router": "^16.2.4",
"@angular/animations": "^17.0.4",
"@angular/cdk": "~17.0.1",
"@angular/common": "^17.0.4",
"@angular/compiler": "^17.0.4",
"@angular/core": "^17.0.4",
"@angular/forms": "^17.0.4",
"@angular/material": "~17.0.1",
"@angular/platform-browser": "^17.0.4",
"@angular/platform-browser-dynamic": "^17.0.4",
"@angular/router": "^17.0.4",
"rxjs": "~7.8.1",
"tslib": "^2.3.0",
"zone.js": "~0.13.1"
"zone.js": "^0.14.2"
},
"devDependencies": {
"@angular-devkit/build-angular": "^16.2.1",
"@angular-eslint/builder": "^16.1.2",
"@angular-eslint/eslint-plugin": "16.1.2",
"@angular-eslint/eslint-plugin-template": "16.1.2",
"@angular-eslint/schematics": "^16.1.2",
"@angular-eslint/template-parser": "16.1.2",
"@angular/cli": "^16.2.1",
"@angular/compiler-cli": "^16.2.4",
"@angular-devkit/build-angular": "^17.0.2",
"@angular-eslint/builder": "^17.1.0",
"@angular-eslint/eslint-plugin": "17.1.0",
"@angular-eslint/eslint-plugin-template": "17.1.0",
"@angular-eslint/schematics": "^17.1.0",
"@angular-eslint/template-parser": "17.1.0",
"@angular/cli": "^17.0.2",
"@angular/compiler-cli": "^17.0.4",
"@types/jasmine": "~3.8.0",
"@types/node": "^20.5.9",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"eslint": "^8.39.0",
"@typescript-eslint/eslint-plugin": "^6.10.0",
"@typescript-eslint/parser": "^6.10.0",
"eslint": "^8.53.0",
"jasmine-core": "~3.8.0",
"karma": "~6.4.2",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.0.3",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "~1.7.0",
"ng-packagr": "^16.2.3",
"typescript": "~4.9.5"
"ng-packagr": "^17.0.2",
"typescript": "5.2.2"
}
}
34 changes: 32 additions & 2 deletions projects/app/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,25 @@ import { FormControl } from '@angular/forms';
selector: 'app-root',
template: `<div class="app-container">
<mat-form-field appearance="outline">
<mat-label>Drop only .png files!</mat-label>
<ngx-mat-dropzone>
<input type="file" fileInput [formControl]="profileImg" accept="image/jpeg" />
<input type="file" fileInput multiple [formControl]="profileImg" accept="image/png" />

<mat-chip-row *ngFor="let image of images" (removed)="remove(image)">
{{ image.name }}
<button matChipRemove>
<mat-icon>cancel</mat-icon>
</button>
</mat-chip-row>
</ngx-mat-dropzone>
<mat-icon matSuffix>cloud_upload</mat-icon>
<mat-error>Only image files allowed!</mat-error>
</mat-form-field>

<div style="width: 40px"></div>

<mat-form-field appearance="fill">
<mat-label>Drop anything!</mat-label>
<ngx-mat-dropzone>
<input type="file" fileInput />
</ngx-mat-dropzone>
Expand All @@ -25,11 +35,31 @@ import { FormControl } from '@angular/forms';
display: flex;
flex-direction: column;
align-items: center;
margin-top: 25vh;
padding: 20vh;
}

mat-form-field {
width: 100%;
}
`,
],
})
export class AppComponent {
profileImg = new FormControl();

get images() {
const images = this.profileImg.value;

if (!images) return [];
return Array.isArray(images) ? images : [images];
}

remove(image: File) {
if (Array.isArray(this.profileImg.value)) {
this.profileImg.setValue(this.profileImg.value.filter((i) => i !== image));
return;
}

this.profileImg.setValue(null);
}
}
4 changes: 4 additions & 0 deletions projects/app/src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { MatChipsModule } from '@angular/material/chips';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
Expand All @@ -16,6 +18,8 @@ import { AppComponent } from './app.component';
ReactiveFormsModule,
MatFormFieldModule,
MatInputModule,
MatChipsModule,
MatIconModule,
DropzoneCdkModule,
DropzoneMaterialModule,
],
Expand Down
4 changes: 4 additions & 0 deletions projects/app/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
<link
href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet"
/>
</head>
<body>
<app-root></app-root>
Expand Down
6 changes: 3 additions & 3 deletions projects/cdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
"cdk"
],
"peerDependencies": {
"@angular/common": "^16.0.0",
"@angular/core": "^16.0.0",
"@angular/forms": "^16.0.0",
"@angular/common": "^17.0.0",
"@angular/core": "^17.0.0",
"@angular/forms": "^17.0.0",
"rxjs": "^7.4.0"
}
}
6 changes: 4 additions & 2 deletions projects/cdk/src/lib/dropzone/dropzone.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,16 @@ describe('DropzoneComponent', () => {
expect(selectors.element.nativeElement.classList).toContain('disabled');
});

it('should update form control value after file drop', () => {
it('should update form control value after file drop', async () => {
const dataTransfer = new DataTransfer();
const getFile = () => new File(['...'], `${Date.now()}.txt`);
[getFile(), getFile(), getFile()].forEach((f) => dataTransfer.items.add(f));

const drop = new DragEvent('drop', { dataTransfer });

selectors.element.nativeElement.dispatchEvent(drop);
// We directly call the internal method `_onDrop` instead of
// dispatching the event because we have to await it to complete.
await selectors.component._onDrop(drop);
selectors.fixture.detectChanges();

expect(selectors.element.nativeElement.classList).not.toContain('dragover');
Expand Down
19 changes: 4 additions & 15 deletions projects/cdk/src/lib/dropzone/dropzone.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { NgControl } from '@angular/forms';
import { BehaviorSubject, Subject, takeUntil, tap } from 'rxjs';
import { FileInputDirective, FileInputValue } from './../file-input';
import { getMissingControlError } from './dropzone-errors';
import { DropzoneService } from './dropzone.service';

@Component({
selector: 'ngx-dropzone',
Expand All @@ -36,6 +37,7 @@ import { getMissingControlError } from './dropzone-errors';
export class DropzoneComponent implements AfterContentInit, OnDestroy {
protected _destroy$ = new Subject<void>();
protected _changeDetectorRef = inject(ChangeDetectorRef);
protected _dropzoneService = inject(DropzoneService);

@ContentChild(FileInputDirective, { static: true })
readonly fileInputDirective: FileInputDirective | null = null;
Expand Down Expand Up @@ -122,23 +124,10 @@ export class DropzoneComponent implements AfterContentInit, OnDestroy {
};

@HostListener('drop', ['$event'])
_onDrop = (event: DragEvent) => {
_onDrop = async (event: DragEvent) => {
this._onDragLeave(event);

const files = this._getDroppedFiles(event);
const files = await this._dropzoneService.getFiles(event);
this.fileInputDirective?.handleFileDrop(files);
};

private _getDroppedFiles(event: DragEvent): File[] {
if (event.dataTransfer?.items) {
const files = Array.from(event.dataTransfer.items)
.map((file) => file.getAsFile())
.filter((file) => file !== null) as File[];

return files;
}

// Fallback for older specifications
return Array.from(event.dataTransfer?.files ?? []);
}
}
24 changes: 24 additions & 0 deletions projects/cdk/src/lib/dropzone/dropzone.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { TestBed } from '@angular/core/testing';
import { DropzoneService } from './dropzone.service';

describe('DropzoneService', () => {
let service: DropzoneService;

beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(DropzoneService);
});

it('returns dropped files', async () => {
const dataTransfer = new DataTransfer();
const file1 = new File(['...'], `${Date.now()}.txt`);
const file2 = new File(['...'], `${Date.now()}.txt`);

[file1, file2].forEach((f) => dataTransfer.items.add(f));

const drop = new DragEvent('drop', { dataTransfer });
const files = await service.getFiles(drop);

expect(files.length).toEqual(2);
});
});
62 changes: 62 additions & 0 deletions projects/cdk/src/lib/dropzone/dropzone.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Injectable } from '@angular/core';

@Injectable({
providedIn: 'root',
})
export class DropzoneService {
/**
* Returns a `File[]` from a `DragEvent`. Accepts a list of files or folders.
*/
async getFiles(event: DragEvent): Promise<File[]> {
if (!event.dataTransfer?.items) {
// Fallback for older specifications
return Array.from(event.dataTransfer?.files ?? []);
}

const fsEntries = Array.from(event.dataTransfer?.items ?? [])
.map((item) => this._toFileSystemEntry(item))
.filter((e): e is FileSystemEntry | File => e !== null)
.map((entry) => this._getFilesFromEntry(entry));

const files: File[][] = await Promise.all(fsEntries);
return this._flattenFiles(files);
}

private _toFileSystemEntry(item: DataTransferItem): FileSystemEntry | File | null {
// In the future, we can use the `getAsEntry` method when it becomes available.
if ('getAsEntry' in item && typeof item.getAsEntry === 'function') {
return item.getAsEntry();
}

// If supported, use the `webkitGetAsEntry` method to allow folder drops.
// As a fallback, use the well-supported `getAsFile` method.
return item.webkitGetAsEntry() || item.getAsFile();
}

private _getFilesFromEntry(entry: FileSystemEntry | File): Promise<File[]> {
return new Promise<File[]>((resolve) => {
if (entry instanceof File) {
return resolve([entry]);
}

if (this._isFile(entry)) {
return entry.file((f) => resolve([f]));
}

if (this._isDirectory(entry)) {
const reader = entry.createReader();

reader.readEntries(async (entries) => {
const children = entries.map((e) => this._getFilesFromEntry(e));
const files = await Promise.all(children);

return resolve(this._flattenFiles(files));
});
}
});
}

private _isFile = (item: FileSystemEntry): item is FileSystemFileEntry => item.isFile;
private _isDirectory = (item: FileSystemEntry): item is FileSystemDirectoryEntry => item.isDirectory;
private _flattenFiles = (files: File[][]) => ([] as File[]).concat(...files);
}
1 change: 1 addition & 0 deletions projects/cdk/src/lib/dropzone/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './dropzone.component';
export * from './dropzone.service';
8 changes: 4 additions & 4 deletions projects/material/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@
"material"
],
"peerDependencies": {
"@angular/common": "^16.0.0",
"@angular/core": "^16.0.0",
"@angular/forms": "^16.0.0",
"@ngx-dropzone/cdk": "^16.0.0",
"@angular/common": "^17.0.0",
"@angular/core": "^17.0.0",
"@angular/forms": "^17.0.0",
"@ngx-dropzone/cdk": "^17.0.0",
"rxjs": "^7.4.0"
}
}
1 change: 1 addition & 0 deletions projects/material/src/lib/mat-dropzone/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './mat-dropzone.component';
Loading