├── .editorconfig ├── .eslintrc.json ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── angular.json ├── browserslist ├── package-lock.json ├── package.json ├── projects ├── ngx-file-drop-example │ ├── karma.conf.js │ ├── src │ │ ├── app │ │ │ ├── app.component.html │ │ │ ├── app.component.scss │ │ │ ├── app.component.spec.ts │ │ │ ├── app.component.ts │ │ │ ├── app.module.ts │ │ │ └── variables.scss │ │ ├── assets │ │ │ └── .gitkeep │ │ ├── environments │ │ │ ├── environment.prod.ts │ │ │ └── environment.ts │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.ts │ │ ├── polyfills.ts │ │ ├── styles.scss │ │ └── test.ts │ ├── tsconfig.app.json │ └── tsconfig.spec.json └── ngx-file-drop │ ├── ng-package.json │ ├── package.json │ ├── src │ ├── lib │ │ ├── dom.types.ts │ │ ├── ngx-file-drop-entry.ts │ │ ├── ngx-file-drop.component.html │ │ ├── ngx-file-drop.component.scss │ │ ├── ngx-file-drop.component.ts │ │ ├── ngx-file-drop.module.ts │ │ └── ngx-templates.directive.ts │ └── public-api.ts │ ├── tsconfig.lib.json │ └── tsconfig.lib.prod.json └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "ignorePatterns": [ 4 | "projects/**/*" 5 | ], 6 | "overrides": [ 7 | { 8 | "files": [ 9 | "*.ts" 10 | ], 11 | "parserOptions": { 12 | "project": [ 13 | "tsconfig.json" 14 | ], 15 | "createDefaultProgram": true 16 | }, 17 | "extends": [ 18 | "plugin:@angular-eslint/recommended", 19 | "plugin:@angular-eslint/template/process-inline-templates" 20 | ], 21 | "rules": { 22 | "@angular-eslint/no-output-on-prefix": "off" 23 | } 24 | }, 25 | { 26 | "files": [ 27 | "*.html" 28 | ], 29 | "extends": [ 30 | "plugin:@angular-eslint/template/recommended" 31 | ], 32 | "rules": {} 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.angular/cache 2 | # Visual Studio 3 | .vs/ 4 | .vscode 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.sln.docstates 10 | 11 | # Node Files 12 | /node_modules 13 | /bower_components 14 | npm-debug.log 15 | yarn-error.log 16 | 17 | # IDE configuration 18 | **/.idea/ 19 | pre-commit-hook.sh 20 | 21 | # OS generated files 22 | .DS_Store 23 | .DS_Store? 24 | Icon[\r] 25 | ._* 26 | .Spotlight-V100 27 | .Trashes 28 | ehthumbs.db 29 | Thumbs.db 30 | 31 | # Build files 32 | /dist/ 33 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Language 2 | language: node_js 3 | node_js: 4 | - "node" 5 | 6 | #Disable google analytics promp 7 | before_install: 8 | - export NG_CLI_ANALYTICS=ci 9 | 10 | # Branches to build 11 | branches: 12 | only: 13 | - master 14 | 15 | install: npm ci 16 | 17 | before_script: npm run lint 18 | 19 | script: npm run build 20 | 21 | # Notifications 22 | notifications: 23 | email: 24 | recipients: 25 | - georgi.peltekov@accedia.com 26 | on_success: change 27 | on_failure: change 28 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Change Log 2 | ### [16.0.0](https://github.com/georgipeltekov/ngx-file-drop/compare/v15.0.0...v16.0.0) (2023-06-19) 3 | * Update to Angular 16 4 | 5 | ### [15.0.0](https://github.com/georgipeltekov/ngx-file-drop/compare/v14.0.2...v15.0.0) (2023-03-07) 6 | * Update to Angular 15 7 | 8 | ### [14.0.2](https://github.com/georgipeltekov/ngx-file-drop/compare/v14.0.1...v14.0.2) (2022-11-23) 9 | * Regression fix due to getAsFile 10 | 11 | ### [14.0.1](https://github.com/georgipeltekov/ngx-file-drop/compare/v14.0.0...v14.0.1) (2022-06-27) 12 | * Use getAsFile when possible 13 | 14 | ### [14.0.0](https://github.com/georgipeltekov/ngx-file-drop/compare/v13.0.0...v14.0.0) (2022-06-08) 15 | * Update to Angular 14 16 | 17 | ### [13.0.0](https://github.com/georgipeltekov/ngx-file-drop/compare/v12.0.0...v13.0.0) (2021-12-01) 18 | * Update to Angular 13 19 | 20 | ### [12.0.0](https://github.com/georgipeltekov/ngx-file-drop/compare/v11.3.0...v12.0.0) (2021-12-01) 21 | * Update to Angular 12 22 | 23 | ### [11.3.0](https://github.com/georgipeltekov/ngx-file-drop/compare/v11.2.0...v11.3.0) (2021-11-04) 24 | * Revert 11.2.0 25 | * Remove unused interfaces from dom.types 26 | 27 | ### [11.2.0](https://github.com/georgipeltekov/ngx-file-drop/compare/v11.1.0...v11.2.0) (2021-09-09) 28 | * On folder/files drop validate accepted file extensions 29 | 30 | ### [11.1.0](https://github.com/georgipeltekov/ngx-file-drop/compare/v11.0.1...v11.1.0) (2021-04-21) 31 | * Make FileSystemFileEntry.file() chainable 32 | 33 | ### [11.0.1](https://github.com/georgipeltekov/ngx-file-drop/compare/v11.0.0...v11.0.1) (2021-02-16) 34 | * Fix bug with dropEffect not taking into consideration and Outlook problems 35 | 36 | ### [11.0.0](https://github.com/georgipeltekov/ngx-file-drop/compare/v10.1.1...v11.0.0) (2021-01-29) 37 | * Update to Angular 11 38 | 39 | ### [10.1.1](https://github.com/georgipeltekov/ngx-file-drop/compare/v10.0.0...v10.1.1) (2020-11-11) 40 | * Compile with View Engine 41 | 42 | ### [10.0.0](https://github.com/georgipeltekov/ngx-file-drop/compare/v9.0.1...v10.0.0) (2020-10-02) 43 | * Update to Angular 10 44 | 45 | ### [9.0.1](https://github.com/georgipeltekov/ngx-file-drop/compare/v9.0.0...v9.0.1) (2020-04-15) 46 | * Add dragenter option 47 | 48 | ### [9.0.0](https://github.com/georgipeltekov/ngx-file-drop/compare/v8.0.8...v9.0.0) (2020-03-31) 49 | * Update to Angular 9 50 | 51 | ### [8.0.8](https://github.com/georgipeltekov/ngx-file-drop/compare/v8.0.7...v8.0.8) (2019-09-26) 52 | * Add directory parameter 53 | 54 | ### [8.0.7](https://github.com/georgipeltekov/ngx-file-drop/compare/v8.0.6...v8.0.7) (2019-08-08) 55 | * Revert previous version due to regression 56 | 57 | ### [8.0.6](https://github.com/georgipeltekov/ngx-file-drop/compare/v8.0.5...v8.0.6) (2019-08-06) 58 | * Add readme to the npm package 59 | 60 | ### [8.0.5](https://github.com/georgipeltekov/ngx-file-drop/compare/v8.0.4...v8.0.5) (2019-08-06) 61 | * Add directory parameter 62 | 63 | ### [8.0.4](https://github.com/georgipeltekov/ngx-file-drop/compare/v8.0.3...v8.0.4) (2019-07-17) 64 | * Lowering node version requirement to LTS version 10 ( again) 65 | 66 | ### [8.0.3](https://github.com/georgipeltekov/ngx-file-drop/compare/v8.0.2...v8.0.3) (2019-06-28) 67 | * Lowering node version requirement to LTS version 10 68 | 69 | ### [8.0.2](https://github.com/georgipeltekov/ngx-file-drop/compare/v8.0.0...v8.0.2) (2019-06-05) 70 | * Update README 71 | 72 | ### [8.0.0](https://github.com/georgipeltekov/ngx-file-drop/compare/v6.0.1...v8.0.0) (2019-06-04) 73 | * Update to Angular 8 74 | 75 | ### [6.0.1](https://github.com/georgipeltekov/ngx-file-drop/compare/v6.0.0...v6.0.1) (2019-05-08) 76 | * Add support for multiple attribute changing 77 | 78 | ### [6.0.0](https://github.com/georgipeltekov/ngx-file-drop/compare/v5.1.0...v6.0.0) (2019-03-11) 79 | * Code cleanup and consistent naming scheme, removing deprecated code 80 | 81 | ### [5.1.0](https://github.com/georgipeltekov/ngx-file-drop/compare/v5.0.6...v5.1.0) (2019-03-08) 82 | * Code cleanup and consistent naming scheme 83 | 84 | ### [5.0.6](https://github.com/georgipeltekov/ngx-file-drop/compare/v5.0.5...v5.0.6) (2019-02-27) 85 | * Add accept field 86 | 87 | ### [5.0.5](https://github.com/georgipeltekov/ngx-file-drop/compare/v5.0.4...v5.0.5) (2019-02-13) 88 | * Add custom styling properties 89 | 90 | ### [5.0.4](https://github.com/georgipeltekov/ngx-file-drop/compare/v5.0.3...v5.0.4) (2019-02-04) 91 | * Add property for browse button label 92 | * Code optimization and refactoring 93 | 94 | ### [5.0.3](https://github.com/georgipeltekov/ngx-file-drop/compare/v5.0.2...v5.0.3) (2019-02-01) 95 | * Fix bug introduced with previous version 96 | 97 | ### [5.0.2](https://github.com/georgipeltekov/ngx-file-drop/compare/v5.0.1...v5.0.2) (2019-01-23) 98 | * Add browse file button functionality 99 | 100 | ### [5.0.1](https://github.com/georgipeltekov/ngx-file-drop/compare/v5.0.0...v5.0.1) (2019-01-18) 101 | * Support patch-level differences for zone.js 102 | * Update Readme 103 | 104 | ### [5.0.0](https://github.com/georgipeltekov/ngx-file-drop/compare/v4.0.6...v5.0.0) (2018-10-22) 105 | * Update to Angular 7.0.0 106 | 107 | ### [4.0.6](https://github.com/georgipeltekov/ngx-file-drop/compare/v4.0.5...v4.0.6) (2018-06-19) 108 | * Bug fixing 109 | 110 | ### [4.0.5](https://github.com/georgipeltekov/ngx-file-drop/compare/v4.0.4...v4.0.5) (2018-06-19) 111 | * Fix for big, nested folder structure 112 | 113 | ### [4.0.4](https://github.com/georgipeltekov/ngx-file-drop/compare/v4.0.3...v4.0.4) (2018-05-16) 114 | * Clear unused dependencies 115 | 116 | ### [4.0.3](https://github.com/georgipeltekov/ngx-file-drop/compare/v4.0.2...v4.0.3) (2018-05-15) 117 | * Loosen Angular-Dependencies 118 | 119 | ### [4.0.2](https://github.com/georgipeltekov/ngx-file-drop/compare/v4.0.0...v4.0.2) (2018-05-15) 120 | * Small changes in gitignore 121 | 122 | ### [4.0.0](https://github.com/georgipeltekov/ngx-file-drop/compare/v3.0.2...v4.0.0) (2018-05-07) 123 | * Update to Angular 6.0.0 124 | 125 | ### [3.0.2](https://github.com/georgipeltekov/ngx-file-drop/compare/v3.0.1...v3.0.2) (2018-04-29) 126 | * Allow drag events to be ignored that originate from document (introduced in 3.0.1) even if using custom styles 127 | 128 | ### [3.0.1](https://github.com/georgipeltekov/ngx-file-drop/compare/v3.0.0...v3.0.1) (2018-04-25) 129 | * Conditionally Disable Dropzone and Ignore DragEvents that initiate from within the browser document 130 | 131 | ### [3.0.0](https://github.com/georgipeltekov/ngx-file-drop/compare/v2.0.5...v3.0.0) (2018-03-15) 132 | * Added better typescript types 133 | 134 | ### [2.0.5](https://github.com/georgipeltekov/ngx-file-drop/compare/v2.0.4...v2.0.5) (2018-02-18) 135 | * Some cleanups 136 | 137 | ### [2.0.4](https://github.com/georgipeltekov/ngx-file-drop/compare/v2.0.3...v2.0.4) (2018-01-19) 138 | * Update angular-cli version due to vulnerable dependencies 139 | 140 | ### [2.0.3](https://github.com/georgipeltekov/ngx-file-drop/compare/v2.0.2...v2.0.3) (2018-01-18) 141 | * Multiple File-Drops 142 | 143 | ### [2.0.2](https://github.com/georgipeltekov/ngx-file-drop/compare/v2.0.1...v2.0.2) (2017-12-20) 144 | * Using a workaround to enable getting files when no webkitGetAsEntry is supported 145 | 146 | ### [2.0.1](https://github.com/georgipeltekov/ngx-file-drop/compare/v2.0.0...v2.0.1) (2017-11-20) 147 | * Fix packaging 148 | 149 | ### [2.0.0](https://github.com/georgipeltekov/ngx-file-drop/compare/v1.0.18...v2.0.0) (2017-11-20) 150 | * Allow angular 4 & 5 and change the build and packaging process 151 | 152 | ### [1.0.18](https://github.com/georgipeltekov/ngx-file-drop/compare/v1.0.17...v1.0.18) (2017-10-30) 153 | * Allow angular 4 & 5 154 | 155 | ### [1.0.17](https://github.com/georgipeltekov/ngx-file-drop/compare/v1.0.16...v1.0.17) (2017-10-27) 156 | * Revert Angular 5 changes 157 | 158 | ### [1.0.16](https://github.com/georgipeltekov/ngx-file-drop/compare/v1.0.15...v1.0.16) (2017-10-27) 159 | * Prepare upgrade to Angular 5 160 | * Changes to confirm to the tslint rules 161 | 162 | ### [1.0.15](https://github.com/georgipeltekov/ngx-file-drop/compare/v1.0.14...v1.0.15) (2017-10-19) 163 | * Read entries should be called until all dirs are read 164 | 165 | ### [1.0.14](https://github.com/georgipeltekov/ngx-file-drop/compare/v1.0.13...v1.0.14) (2017-10-19) 166 | * Add dist folder with .js and .js.map files 167 | 168 | ### [1.0.13](https://github.com/georgipeltekov/ngx-file-drop/compare/v1.0.12...v1.0.13) (2017-10-13) 169 | * Fix typo in Readme 170 | * Add ng-content tag to support nested components within drag/drop region 171 | 172 | ### [1.0.12](https://github.com/georgipeltekov/ngx-file-drop/compare/v1.0.11...v1.0.12) (2017-09-13) 173 | * Fix bugs in Safari 174 | 175 | ### [1.0.11](https://github.com/georgipeltekov/ngx-file-drop/compare/v1.0.10...v1.0.11) (2017-09-13) 176 | * Fix bug in Safari 177 | 178 | ### [1.0.10](https://github.com/georgipeltekov/ngx-file-drop/compare/v1.0.9...v1.0.10) (2017-06-21) 179 | * Check subscription exists before unsubscribing 180 | 181 | ### [1.0.9](https://github.com/georgipeltekov/ngx-file-drop/compare/v1.0.4...v1.0.9) (2017-06-06) 182 | * Add onFileOver and onFileLeave functions 183 | 184 | ### 1.0.8 (2017-06-03) 185 | * Remove some not needed dependencies 186 | 187 | ### 1.0.7 (2017-06-01) 188 | * Fix typos in Readme 189 | 190 | ### 1.0.5 (2017-05-01) 191 | * Add Travis CI 192 | * Update Readme.md 193 | 194 | ### [1.0.4](https://github.com/georgipeltekov/ngx-file-drop/tree/v1.0.4) (2017-05-28) 195 | * Add LICENSE 196 | * Add DEMO 197 | 198 | ### 1.0.3 (2017-05-28) 199 | * Update npm support 200 | 201 | ### 1.0.1 (2017-05-27) 202 | * Doc update 203 | * Style changes 204 | 205 | ### 1.0.0 (2017-05-25) 206 | * Initial release 207 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 georgipeltekov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm](https://img.shields.io/npm/v/ngx-file-drop.svg?style=flat-square)](https://www.npmjs.com/package/ngx-file-drop) [![npm downloads](https://img.shields.io/npm/dm/ngx-file-drop.svg)](https://www.npmjs.com/package/ngx-file-drop) [![Travis](https://img.shields.io/travis/georgipeltekov/ngx-file-drop.svg?style=flat-square)](https://travis-ci.org/georgipeltekov/ngx-file-drop) [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](https://github.com/georgipeltekov/ngx-file-drop/blob/master/LICENSE) 2 | 3 | ## Overview 4 | 5 | An Angular module for simple desktop file and folder drag and drop. This library does not need rxjs-compat. 6 | 7 | For previous Angular support please use older versions. 8 | 9 | This library relies on HTML 5 File API thus IE is not supported 10 | 11 | ## DEMO 12 | You can check the [DEMO](https://georgipeltekov.github.io/) of the library 13 | 14 | ## Installation 15 | 16 | ```bash 17 | npm install ngx-file-drop --save 18 | ``` 19 | 20 | ## Usage 21 | 22 | 23 | ### Importing The 'ngx-file-drop' Module 24 | 25 | ```TypeScript 26 | import { BrowserModule } from '@angular/platform-browser'; 27 | import { NgModule } from '@angular/core'; 28 | import { FormsModule } from '@angular/forms'; 29 | import { HttpClientModule } from '@angular/common/http'; 30 | 31 | import { AppComponent } from './app.component'; 32 | import { NgxFileDropModule } from 'ngx-file-drop'; 33 | 34 | 35 | @NgModule({ 36 | declarations: [ 37 | AppComponent 38 | ], 39 | imports: [ 40 | BrowserModule, 41 | FormsModule, 42 | HttpClientModule, 43 | NgxFileDropModule 44 | ], 45 | providers: [], 46 | bootstrap: [AppComponent] 47 | }) 48 | export class AppModule { } 49 | 50 | ``` 51 | 52 | ### Enabling File Drag 53 | 54 | 55 | ```TypeScript 56 | import { Component } from '@angular/core'; 57 | import { NgxFileDropEntry, FileSystemFileEntry, FileSystemDirectoryEntry } from 'ngx-file-drop'; 58 | 59 | @Component({ 60 | selector: 'demo-root', 61 | templateUrl: './app.component.html', 62 | styleUrls: ['./app.component.scss'] 63 | }) 64 | export class AppComponent { 65 | 66 | public files: NgxFileDropEntry[] = []; 67 | 68 | public dropped(files: NgxFileDropEntry[]) { 69 | this.files = files; 70 | for (const droppedFile of files) { 71 | 72 | // Is it a file? 73 | if (droppedFile.fileEntry.isFile) { 74 | const fileEntry = droppedFile.fileEntry as FileSystemFileEntry; 75 | fileEntry.file((file: File) => { 76 | 77 | // Here you can access the real file 78 | console.log(droppedFile.relativePath, file); 79 | 80 | /** 81 | // You could upload it like this: 82 | const formData = new FormData() 83 | formData.append('logo', file, relativePath) 84 | 85 | // Headers 86 | const headers = new HttpHeaders({ 87 | 'security-token': 'mytoken' 88 | }) 89 | 90 | this.http.post('https://mybackend.com/api/upload/sanitize-and-save-logo', formData, { headers: headers, responseType: 'blob' }) 91 | .subscribe(data => { 92 | // Sanitized logo returned from backend 93 | }) 94 | **/ 95 | 96 | }); 97 | } else { 98 | // It was a directory (empty directories are added, otherwise only files) 99 | const fileEntry = droppedFile.fileEntry as FileSystemDirectoryEntry; 100 | console.log(droppedFile.relativePath, fileEntry); 101 | } 102 | } 103 | } 104 | 105 | public fileOver(event){ 106 | console.log(event); 107 | } 108 | 109 | public fileLeave(event){ 110 | console.log(event); 111 | } 112 | } 113 | 114 | 115 | ``` 116 | ```HTML 117 |
118 | 120 | 121 | Optional custom content that replaces the the entire default content. 122 | 123 | 124 | 125 |
126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 |
Name
{{ item.relativePath }}
138 |
139 |
140 | ``` 141 | 142 | ## Parameters 143 | 144 | Name | Description | Example | 145 | ------------- | ------------- | ------------- 146 | (onFileDrop) | On drop function called after the files are read | (onFileDrop)="dropped($event)" 147 | (onFileOver) | On drop over function| (onFileOver)="fileOver($event)" 148 | (onFileLeave) | On drop leave function| (onFileLeave)="fileLeave($event)" 149 | accept | String of accepted formats | accept=".png" 150 | directory | Whether directories are accepted | directory="true" 151 | dropZoneLabel | Text to be displayed inside the drop box | dropZoneLabel="Drop files here" 152 | dropZoneClassName | Custom style class name(s) to be used on the "drop-zone" area | dropZoneClassName="my-style" 153 | contentClassName | Custom style class name(s) to be used for the content area | contentClassName="my-style" 154 | \[disabled\] | Conditionally disable the dropzone | \[disabled\]="condition" 155 | \[showBrowseBtn\] | Whether browse file button should be shown | \[showBrowseBtn\]="true" 156 | browseBtnClassName | Custom style class name(s) to be used for the button | browseBtnClassName="my-style" 157 | browseBtnLabel | The label of the browse file button | browseBtnLabel="Browse files" 158 | multiple | Whether multiple or single files are accepted | multiple="true" 159 | useDragEnter | Use dragenter event instead of dragover | useDragEnter="true" 160 | 161 | ## License 162 | 163 | [MIT](/LICENSE) 164 | 165 | ## Change Log 166 | 167 | [CHANGELOG](/CHANGELOG.md) 168 | 169 | ## Donate Crypto 170 | * Bitcoin: 18yJcRSyY7J9K7kHrkNQ2JspLfSgLKWUnh 171 | * Ethereum: 0xdF1E80c91599CA6d4a8745888e658f45B86b0FEd 172 | 173 | 174 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "ngx-file-drop": { 7 | "projectType": "library", 8 | "root": "projects/ngx-file-drop", 9 | "sourceRoot": "projects/ngx-file-drop/src", 10 | "prefix": "lib", 11 | "architect": { 12 | "build": { 13 | "builder": "@angular-devkit/build-angular:ng-packagr", 14 | "options": { 15 | "project": "projects/ngx-file-drop/ng-package.json" 16 | }, 17 | "configurations": { 18 | "production": { 19 | "tsConfig": "projects/ngx-file-drop/tsconfig.lib.prod.json" 20 | }, 21 | "development": { 22 | "tsConfig": "projects/ngx-file-drop/tsconfig.lib.json" 23 | } 24 | }, 25 | "defaultConfiguration": "production" 26 | }, 27 | "lint": { 28 | "builder": "@angular-devkit/build-angular:tslint", 29 | "options": { 30 | "eslintConfig": ".eslintrc.json", 31 | "lintFilePatterns": ["**/*.spec.ts", "**/*.ts"] 32 | } 33 | } 34 | } 35 | }, 36 | "ngx-file-drop-example": { 37 | "projectType": "application", 38 | "schematics": { 39 | "@schematics/angular:component": { 40 | "style": "scss" 41 | }, 42 | "@schematics/angular:application": { 43 | "strict": true 44 | } 45 | }, 46 | "root": "projects/ngx-file-drop-example", 47 | "sourceRoot": "projects/ngx-file-drop-example/src", 48 | "prefix": "app", 49 | "architect": { 50 | "build": { 51 | "builder": "@angular-devkit/build-angular:browser", 52 | "options": { 53 | "outputPath": "dist/ngx-file-drop-example", 54 | "index": "projects/ngx-file-drop-example/src/index.html", 55 | "main": "projects/ngx-file-drop-example/src/main.ts", 56 | "polyfills": "projects/ngx-file-drop-example/src/polyfills.ts", 57 | "tsConfig": "projects/ngx-file-drop-example/tsconfig.app.json", 58 | "inlineStyleLanguage": "scss", 59 | "assets": [ 60 | "projects/ngx-file-drop-example/src/favicon.ico", 61 | "projects/ngx-file-drop-example/src/assets" 62 | ], 63 | "styles": [ 64 | "projects/ngx-file-drop-example/src/styles.scss" 65 | ], 66 | "scripts": [] 67 | }, 68 | "configurations": { 69 | "production": { 70 | "budgets": [ 71 | { 72 | "type": "initial", 73 | "maximumWarning": "500kb", 74 | "maximumError": "1mb" 75 | }, 76 | { 77 | "type": "anyComponentStyle", 78 | "maximumWarning": "2kb", 79 | "maximumError": "4kb" 80 | } 81 | ], 82 | "fileReplacements": [ 83 | { 84 | "replace": "projects/ngx-file-drop-example/src/environments/environment.ts", 85 | "with": "projects/ngx-file-drop-example/src/environments/environment.prod.ts" 86 | } 87 | ], 88 | "outputHashing": "all" 89 | }, 90 | "development": { 91 | "buildOptimizer": false, 92 | "optimization": false, 93 | "vendorChunk": true, 94 | "extractLicenses": false, 95 | "sourceMap": true, 96 | "namedChunks": true 97 | } 98 | }, 99 | "defaultConfiguration": "production" 100 | }, 101 | "serve": { 102 | "builder": "@angular-devkit/build-angular:dev-server", 103 | "configurations": { 104 | "production": { 105 | "browserTarget": "ngx-file-drop-example:build:production" 106 | }, 107 | "development": { 108 | "browserTarget": "ngx-file-drop-example:build:development" 109 | } 110 | }, 111 | "defaultConfiguration": "development" 112 | }, 113 | "extract-i18n": { 114 | "builder": "@angular-devkit/build-angular:extract-i18n", 115 | "options": { 116 | "browserTarget": "ngx-file-drop-example:build" 117 | } 118 | }, 119 | "test": { 120 | "builder": "@angular-devkit/build-angular:karma", 121 | "options": { 122 | "main": "projects/ngx-file-drop-example/src/test.ts", 123 | "polyfills": "projects/ngx-file-drop-example/src/polyfills.ts", 124 | "tsConfig": "projects/ngx-file-drop-example/tsconfig.spec.json", 125 | "karmaConfig": "projects/ngx-file-drop-example/karma.conf.js", 126 | "inlineStyleLanguage": "scss", 127 | "assets": [ 128 | "projects/ngx-file-drop-example/src/favicon.ico", 129 | "projects/ngx-file-drop-example/src/assets" 130 | ], 131 | "styles": [ 132 | "projects/ngx-file-drop-example/src/styles.scss" 133 | ], 134 | "scripts": [] 135 | } 136 | } 137 | } 138 | }}, 139 | "cli": { 140 | "analytics": false 141 | }, 142 | "schematics": { 143 | "@schematics/angular:component": { 144 | "prefix": "ngx", 145 | "style": "scss" 146 | }, 147 | "@schematics/angular:directive": { 148 | "prefix": "ngx" 149 | } 150 | } 151 | } -------------------------------------------------------------------------------- /browserslist: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "repository": "georgipeltekov/ngx-file-drop", 3 | "engines": { 4 | "node": ">= 16.13.0", 5 | "npm": ">= 8.1.0" 6 | }, 7 | "scripts": { 8 | "prestart": "npm run build", 9 | "start": "ng serve", 10 | "clean": "rimraf out-tsc dist/*", 11 | "prebuild": "npm run clean", 12 | "build": "ng build ngx-file-drop --configuration=production", 13 | "lint": "ng lint" 14 | }, 15 | "devDependencies": { 16 | "@angular-devkit/build-angular": "^19.1.3", 17 | "@angular-devkit/core": "^16.1.0", 18 | "@angular-eslint/builder": "16.0.3", 19 | "@angular-eslint/eslint-plugin": "16.0.3", 20 | "@angular-eslint/eslint-plugin-template": "16.0.3", 21 | "@angular-eslint/schematics": "16.0.3", 22 | "@angular-eslint/template-parser": "16.0.3", 23 | "@angular/cli": "^16.1.0", 24 | "@angular/common": "^16.1.1", 25 | "@angular/compiler": "^16.1.1", 26 | "@angular/compiler-cli": "^16.1.1", 27 | "@angular/core": "^16.1.1", 28 | "@angular/platform-browser": "^16.1.1", 29 | "@angular/platform-browser-dynamic": "^16.1.1", 30 | "@typescript-eslint/eslint-plugin": "^5.59.2", 31 | "@typescript-eslint/parser": "^5.59.2", 32 | "eslint": "^8.39.0", 33 | "ng-packagr": "^16.1.0", 34 | "rxjs": "^6.5.4", 35 | "typescript": "^4.9.5", 36 | "zone.js": "~0.13.1" 37 | }, 38 | "dependencies": { 39 | "tslib": "^2.3.1" 40 | } 41 | } -------------------------------------------------------------------------------- /projects/ngx-file-drop-example/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | jasmine: { 17 | // you can add configuration options for Jasmine here 18 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html 19 | // for example, you can disable the random execution with `random: false` 20 | // or set a specific seed with `seed: 4321` 21 | }, 22 | clearContext: false // leave Jasmine Spec Runner output visible in browser 23 | }, 24 | jasmineHtmlReporter: { 25 | suppressAll: true // removes the duplicated traces 26 | }, 27 | coverageReporter: { 28 | dir: require('path').join(__dirname, '../../coverage/ngx-file-drop-example'), 29 | subdir: '.', 30 | reporters: [ 31 | { type: 'html' }, 32 | { type: 'text-summary' } 33 | ] 34 | }, 35 | reporters: ['progress', 'kjhtml'], 36 | port: 9876, 37 | colors: true, 38 | logLevel: config.LOG_INFO, 39 | autoWatch: true, 40 | browsers: ['Chrome'], 41 | singleRun: false, 42 | restartOnFileChange: true 43 | }); 44 | }; 45 | -------------------------------------------------------------------------------- /projects/ngx-file-drop-example/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |

2 | ngx-file-drop-example 3 |

4 |
5 |
6 | Enabled? 7 |
8 |
9 | 11 |
12 |
13 | {{entries | json}} 14 |
15 |
-------------------------------------------------------------------------------- /projects/ngx-file-drop-example/src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | @import './variables.scss'; 2 | 3 | .w-50 { 4 | width: 50%; 5 | } 6 | 7 | .pb-2 { 8 | padding-bottom: 15px; 9 | } 10 | 11 | :host ::ng-deep { 12 | 13 | ngx-file-drop { 14 | div.ngx-file-drop__content, 15 | div.ngx-file-drop__drop-zone--enabled, 16 | div.ngx-file-drop__drop-zone--disabled { 17 | height: 200px; 18 | border-radius: 30px; 19 | color: $colors-text-primary; 20 | font-size: 18px; 21 | font-weight: 400; 22 | font-family: $font-family-standard; 23 | flex-direction: column; 24 | transition: all .2s ease-in-out; 25 | 26 | a { 27 | color: $colors-blue-primary; 28 | } 29 | } 30 | 31 | div.ngx-file-drop__drop-zone--enabled, 32 | div.ngx-file-drop__drop-zone--disabled { 33 | border: 3px dashed $colors-border 34 | } 35 | 36 | div.ngx-file-drop__drop-zone--enabled:hover { 37 | border-color: $colors-text-primary; 38 | } 39 | 40 | div.ngx-file-drop__drop-zone--disabled:hover { 41 | border: 3px dashed $colors-border; 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /projects/ngx-file-drop-example/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | 4 | describe('AppComponent', () => { 5 | beforeEach(async () => { 6 | await TestBed.configureTestingModule({ 7 | declarations: [ 8 | AppComponent 9 | ], 10 | }).compileComponents(); 11 | }); 12 | 13 | it('should create the app', () => { 14 | const fixture = TestBed.createComponent(AppComponent); 15 | const app = fixture.componentInstance; 16 | expect(app).toBeTruthy(); 17 | }); 18 | 19 | it(`should have as title 'ngx-file-drop-example'`, () => { 20 | const fixture = TestBed.createComponent(AppComponent); 21 | const app = fixture.componentInstance; 22 | expect(app.title).toEqual('ngx-file-drop-example'); 23 | }); 24 | 25 | it('should render title', () => { 26 | const fixture = TestBed.createComponent(AppComponent); 27 | fixture.detectChanges(); 28 | const compiled = fixture.nativeElement as HTMLElement; 29 | expect(compiled.querySelector('.content span')?.textContent).toContain('ngx-file-drop-example app is running!'); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /projects/ngx-file-drop-example/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { NgxFileDropEntry } from 'ngx-file-drop'; 3 | 4 | @Component({ 5 | selector: 'app-root', 6 | templateUrl: './app.component.html', 7 | styleUrls: ['./app.component.scss'] 8 | }) 9 | export class AppComponent { 10 | title = 'ngx-file-drop-example'; 11 | 12 | checked = true; 13 | entries: string[] = []; 14 | 15 | get className(): string { 16 | return !this.checked ? 'ngx-file-drop__drop-zone--disabled' : 'ngx-file-drop__drop-zone--enabled'; 17 | } 18 | 19 | dropped(files: NgxFileDropEntry[]): void { 20 | this.entries = files.map(file => file.relativePath); 21 | } 22 | 23 | onChange(event: any): void { 24 | this.checked = event.target.checked; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /projects/ngx-file-drop-example/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { BrowserModule } from '@angular/platform-browser'; 4 | import { NgxFileDropModule } from 'ngx-file-drop'; 5 | import { AppComponent } from './app.component'; 6 | 7 | @NgModule({ 8 | declarations: [ 9 | AppComponent 10 | ], 11 | imports: [ 12 | BrowserModule, 13 | CommonModule, 14 | NgxFileDropModule, 15 | ], 16 | providers: [], 17 | bootstrap: [AppComponent] 18 | }) 19 | export class AppModule { } 20 | -------------------------------------------------------------------------------- /projects/ngx-file-drop-example/src/app/variables.scss: -------------------------------------------------------------------------------- 1 | 2 | // Grid Breakpoints 3 | $media-breakpoint-up-xl: 1200px; 4 | $media-breakpoint-up-lg: 992px; 5 | $media-breakpoint-up-md: 768px; 6 | $media-breakpoint-up-sm: 575px; 7 | 8 | // Shoe-Stomp Update Began October 2020 9 | 10 | // Font Family 11 | $font-family-standard: 'Inter', sans-serif; 12 | $font-family-display: 'Inter', sans-serif; 13 | $font-family-code: 'Source Code Pro', monospace; 14 | $font-size-standard: 15px; 15 | 16 | // Color System 17 | $colors-brand-blue: #007bff; 18 | $colors-brand-green: #06d6a0; 19 | $colors-white-primary: #ffffff; 20 | $colors-background-primary: #f8f9fa; 21 | $colors-background-secondary: #02284c; 22 | $colors-text-primary: #001428; 23 | $colors-text-secondary: #b6c2cc; 24 | $colors-text-muted: #6c757d; 25 | $colors-border: #dfe2e5; 26 | $colors-blue-iceberg: #80a9d0; 27 | $colors-blue-primary: #007bfd; 28 | $colors-blue-100: #ecf4ff; 29 | $colors-blue-200: #b0d6ff; 30 | $colors-blue-300: #77b7fe; 31 | $colors-blue-400: #3a9afe; 32 | $colors-blue-500: #027afd; 33 | $colors-blue-600: #0269d8; 34 | $colors-blue-700: #0256b2; 35 | $colors-blue-800: #00448c; 36 | $colors-blue-900: #013166; 37 | $colors-green-primary: #0cd7a0; 38 | $colors-green-100: #d8fdf3; 39 | $colors-green-200: #a5f4df; 40 | $colors-green-300: #72eac9; 41 | $colors-green-400: #3ee1b5; 42 | $colors-green-500: #0dd7a0; 43 | $colors-green-600: #0abe8e; 44 | $colors-green-700: #06a67c; 45 | $colors-green-800: #0a8e6a; 46 | $colors-green-900: #0a7457; 47 | $colors-grey-charcoal: #434655; 48 | $colors-grey-ash: #a8aabc; 49 | $colors-grey-200: #f4f5f6; 50 | $colors-grey-300: #e0e2e5; 51 | $colors-grey-400: #d2d6da; 52 | $colors-grey-500: #afb8bf; 53 | $colors-grey-600: #9ba3ac; 54 | $colors-grey-700: #6e7a87; 55 | $colors-grey-800: #40474f; 56 | $colors-grey-900: #121417; 57 | $colors-pink-primary: #f0476f; 58 | $colors-pink-100: #fdecf0; 59 | $colors-pink-200: #f9c3d0; 60 | $colors-pink-300: #f799b0; 61 | $colors-pink-400: #f47190; 62 | $colors-pink-500: #f1476f; 63 | $colors-pink-600: #df3960; 64 | $colors-pink-700: #cd2b51; 65 | $colors-pink-800: #bc1c42; 66 | $colors-pink-900: #aa0e31; 67 | $colors-purple-200: #e0dbfa; 68 | $colors-purple-300: #d1c9f9; 69 | $colors-purple-400: #c1b6f7; 70 | $colors-purple-600: #9282e9; 71 | $colors-purple-700: #7460de; 72 | $colors-purple-800: #543cd3; 73 | $colors-purple-900: #3619c8; 74 | $colors-yellow-300: #fce8af; 75 | $colors-yellow-200: #f7ebc9; 76 | $colors-yellow-400: #fbe093; 77 | $colors-yellow-gold: #fad775; 78 | $colors-yellow-600: #f7cf59; 79 | $colors-yellow-700: #f4c43e; 80 | $colors-yellow-800: #f1ba24; 81 | $colors-yellow-900: #edb009; 82 | 83 | -------------------------------------------------------------------------------- /projects/ngx-file-drop-example/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/georgipeltekov/ngx-file-drop/98b39452eeb67805ad0a38f2e7dd3cd3c2c4ae59/projects/ngx-file-drop-example/src/assets/.gitkeep -------------------------------------------------------------------------------- /projects/ngx-file-drop-example/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /projects/ngx-file-drop-example/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/plugins/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /projects/ngx-file-drop-example/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/georgipeltekov/ngx-file-drop/98b39452eeb67805ad0a38f2e7dd3cd3c2c4ae59/projects/ngx-file-drop-example/src/favicon.ico -------------------------------------------------------------------------------- /projects/ngx-file-drop-example/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NgxFileDropExample 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /projects/ngx-file-drop-example/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /projects/ngx-file-drop-example/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** 22 | * IE11 requires the following for NgClass support on SVG elements 23 | */ 24 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 25 | 26 | /** 27 | * Web Animations `@angular/platform-browser/animations` 28 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 29 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 30 | */ 31 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 32 | 33 | /** 34 | * By default, zone.js will patch all possible macroTask and DomEvents 35 | * user can disable parts of macroTask/DomEvents patch by setting following flags 36 | * because those flags need to be set before `zone.js` being loaded, and webpack 37 | * will put import in the top of bundle, so user need to create a separate file 38 | * in this directory (for example: zone-flags.ts), and put the following flags 39 | * into that file, and then add the following code before importing zone.js. 40 | * import './zone-flags'; 41 | * 42 | * The flags allowed in zone-flags.ts are listed here. 43 | * 44 | * The following flags will work for all browsers. 45 | * 46 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 47 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 48 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 49 | * 50 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 51 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 52 | * 53 | * (window as any).__Zone_enable_cross_context_check = true; 54 | * 55 | */ 56 | 57 | /*************************************************************************************************** 58 | * Zone JS is required by default for Angular itself. 59 | */ 60 | import 'zone.js'; // Included with Angular CLI. 61 | 62 | 63 | /*************************************************************************************************** 64 | * APPLICATION IMPORTS 65 | */ 66 | -------------------------------------------------------------------------------- /projects/ngx-file-drop-example/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /projects/ngx-file-drop-example/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | // First, initialize the Angular testing environment. 11 | getTestBed().initTestEnvironment( 12 | BrowserDynamicTestingModule, 13 | platformBrowserDynamicTesting(), 14 | { teardown: { destroyAfterEach: true }}, 15 | ); 16 | -------------------------------------------------------------------------------- /projects/ngx-file-drop-example/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts", 10 | "src/polyfills.ts" 11 | ], 12 | "include": [ 13 | "src/**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /projects/ngx-file-drop-example/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../out-tsc/spec", 6 | "types": [ 7 | "jasmine" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /projects/ngx-file-drop/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/ngx-file-drop", 4 | "lib": { 5 | "entryFile": "src/public-api.ts" 6 | } 7 | } -------------------------------------------------------------------------------- /projects/ngx-file-drop/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-file-drop", 3 | "version": "16.0.0", 4 | "description": "Angular ngx-file-drop - Simple desktop file and folder drag and drop", 5 | "author": "Georgi Peltekov", 6 | "license": "MIT", 7 | "repository": "georgipeltekov/ngx-file-drop", 8 | "engines": { 9 | "node": ">= 14.5.0", 10 | "npm": ">= 6.9.0" 11 | }, 12 | "keywords": [ 13 | "angular2", 14 | "angular 2", 15 | "angular4", 16 | "angular 4", 17 | "angular5", 18 | "angular 5", 19 | "angular6", 20 | "angular 6", 21 | "angular7", 22 | "angular 7", 23 | "angular8", 24 | "angular 8", 25 | "angular9", 26 | "angular 9", 27 | "angular10", 28 | "angular 10", 29 | "angular11", 30 | "angular 11", 31 | "angular12", 32 | "angular 12", 33 | "angular13", 34 | "angular 13", 35 | "angular14", 36 | "angular 14", 37 | "file drop", 38 | "folder drop", 39 | "file upload", 40 | "folder upload", 41 | "typescript", 42 | "drag and drop" 43 | ], 44 | "peerDependencies": { 45 | "@angular/common": ">=14.0.0", 46 | "@angular/core": ">=14.0.0" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /projects/ngx-file-drop/src/lib/dom.types.ts: -------------------------------------------------------------------------------- 1 | 2 | export interface FileSystemEntry { 3 | name: string, 4 | isDirectory: boolean 5 | isFile: boolean 6 | } 7 | 8 | export interface FileSystemEntryMetadata { 9 | modificationTime?: Date, 10 | size?: number 11 | } 12 | 13 | export interface FileSystemDirectoryReader { 14 | readEntries( 15 | successCallback: (result: FileSystemEntry[]) => void, 16 | ): void 17 | } 18 | 19 | export interface FileSystemFlags { 20 | create?: boolean 21 | exclusive?: boolean 22 | } 23 | 24 | export interface FileSystemDirectoryEntry extends FileSystemEntry { 25 | isDirectory: true 26 | isFile: false 27 | createReader(): FileSystemDirectoryReader 28 | } 29 | 30 | export interface FileSystemFileEntry extends FileSystemEntry { 31 | isDirectory: false 32 | isFile: true 33 | file(callback: (file: File) => T): T 34 | } 35 | -------------------------------------------------------------------------------- /projects/ngx-file-drop/src/lib/ngx-file-drop-entry.ts: -------------------------------------------------------------------------------- 1 | import { FileSystemEntry, FileSystemFileEntry, FileSystemDirectoryEntry } from './dom.types'; 2 | 3 | /** 4 | * fileEntry is an instance of {@link FileSystemFileEntry} or {@link FileSystemDirectoryEntry}. 5 | * Which one is it can be checked using {@link FileSystemEntry.isFile} or {@link FileSystemEntry.isDirectory} 6 | * properties of the given {@link FileSystemEntry}. 7 | */ 8 | export class NgxFileDropEntry { 9 | constructor( 10 | public relativePath: string, 11 | public fileEntry: FileSystemEntry 12 | ) { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /projects/ngx-file-drop/src/lib/ngx-file-drop.component.html: -------------------------------------------------------------------------------- 1 |
7 |
8 | 21 | 22 | 23 |
{{dropZoneLabel}}
24 |
25 | 26 |
27 |
28 | 29 | 32 | 33 |
34 |
35 | -------------------------------------------------------------------------------- /projects/ngx-file-drop/src/lib/ngx-file-drop.component.scss: -------------------------------------------------------------------------------- 1 | .ngx-file-drop__drop-zone { 2 | height: 100px; 3 | margin: auto; 4 | border: 2px dotted #0782d0; 5 | border-radius: 30px; 6 | } 7 | 8 | .ngx-file-drop__drop-zone--over { 9 | background-color: rgba(147, 147, 147, 0.5); 10 | } 11 | 12 | .ngx-file-drop__content { 13 | display: flex; 14 | align-items: center; 15 | justify-content: center; 16 | height: 100px; 17 | color: #0782d0; 18 | } 19 | 20 | .ngx-file-drop__drop-zone-label { 21 | text-align: center; 22 | } 23 | 24 | .ngx-file-drop__file-input { 25 | display: none; 26 | } 27 | -------------------------------------------------------------------------------- /projects/ngx-file-drop/src/lib/ngx-file-drop.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | ContentChild, 4 | ElementRef, 5 | EventEmitter, 6 | Input, 7 | NgZone, 8 | OnDestroy, 9 | Output, 10 | Renderer2, 11 | TemplateRef, 12 | ViewChild 13 | } from '@angular/core'; 14 | import { Subscription, timer } from 'rxjs'; 15 | 16 | import { NgxFileDropEntry } from './ngx-file-drop-entry'; 17 | import { FileSystemDirectoryEntry, FileSystemEntry, FileSystemFileEntry } from './dom.types'; 18 | import { NgxFileDropContentTemplateDirective } from './ngx-templates.directive'; 19 | 20 | @Component({ 21 | selector: 'ngx-file-drop', 22 | templateUrl: './ngx-file-drop.component.html', 23 | styleUrls: ['./ngx-file-drop.component.scss'], 24 | }) 25 | export class NgxFileDropComponent implements OnDestroy { 26 | 27 | @Input() 28 | public accept: string = '*'; 29 | 30 | @Input() 31 | public directory: boolean = false; 32 | 33 | @Input() 34 | public multiple: boolean = true; 35 | 36 | @Input() 37 | public dropZoneLabel: string = ''; 38 | 39 | @Input() 40 | public dropZoneClassName: string = 'ngx-file-drop__drop-zone'; 41 | 42 | @Input() 43 | public useDragEnter: boolean = false; 44 | 45 | @Input() 46 | public contentClassName: string = 'ngx-file-drop__content'; 47 | 48 | @Input() 49 | public showBrowseBtn: boolean = false; 50 | 51 | @Input() 52 | public browseBtnClassName: string = 'btn btn-primary btn-xs ngx-file-drop__browse-btn'; 53 | 54 | @Input() 55 | public browseBtnLabel: string = 'Browse files'; 56 | 57 | @Output() 58 | public onFileDrop: EventEmitter = new EventEmitter(); 59 | 60 | @Output() 61 | public onFileOver: EventEmitter = new EventEmitter(); 62 | 63 | @Output() 64 | public onFileLeave: EventEmitter = new EventEmitter(); 65 | 66 | // custom templates 67 | @ContentChild(NgxFileDropContentTemplateDirective, { read: TemplateRef }) contentTemplate?: TemplateRef; 68 | 69 | @ViewChild('fileSelector', { static: true }) 70 | public fileSelector?: ElementRef; 71 | 72 | public isDraggingOverDropZone: boolean = false; 73 | 74 | private globalDraggingInProgress: boolean = false; 75 | private readonly globalDragStartListener: () => void; 76 | private readonly globalDragEndListener: () => void; 77 | 78 | private files: NgxFileDropEntry[] = []; 79 | private numOfActiveReadEntries: number = 0; 80 | 81 | private helperFormEl: HTMLFormElement | null = null; 82 | private fileInputPlaceholderEl: HTMLDivElement | null = null; 83 | 84 | private dropEventTimerSubscription: Subscription | null = null; 85 | 86 | private _disabled: boolean = false; 87 | 88 | public get disabled(): boolean { return this._disabled; } 89 | 90 | @Input() 91 | public set disabled(value: boolean) { 92 | this._disabled = (value != null && `${value}` !== 'false'); 93 | } 94 | 95 | constructor( 96 | private zone: NgZone, 97 | private renderer: Renderer2 98 | ) { 99 | this.globalDragStartListener = this.renderer.listen('document', 'dragstart', (evt: Event) => { 100 | this.globalDraggingInProgress = true; 101 | }); 102 | this.globalDragEndListener = this.renderer.listen('document', 'dragend', (evt: Event) => { 103 | this.globalDraggingInProgress = false; 104 | }); 105 | } 106 | 107 | public ngOnDestroy(): void { 108 | if (this.dropEventTimerSubscription) { 109 | this.dropEventTimerSubscription.unsubscribe(); 110 | this.dropEventTimerSubscription = null; 111 | } 112 | this.globalDragStartListener(); 113 | this.globalDragEndListener(); 114 | this.files = []; 115 | this.helperFormEl = null; 116 | this.fileInputPlaceholderEl = null; 117 | } 118 | 119 | public onDragOver(event: DragEvent): void { 120 | if (this.useDragEnter) { 121 | this.preventAndStop(event); 122 | if (event.dataTransfer) { 123 | event.dataTransfer.dropEffect = 'copy'; 124 | } 125 | } else if (!this.isDropzoneDisabled() && !this.useDragEnter && event.dataTransfer) { 126 | if (!this.isDraggingOverDropZone) { 127 | this.isDraggingOverDropZone = true; 128 | this.onFileOver.emit(event); 129 | } 130 | this.preventAndStop(event); 131 | event.dataTransfer.dropEffect = 'copy'; 132 | } 133 | } 134 | 135 | public onDragEnter(event: Event): void { 136 | if (!this.isDropzoneDisabled() && this.useDragEnter) { 137 | if (!this.isDraggingOverDropZone) { 138 | this.isDraggingOverDropZone = true; 139 | this.onFileOver.emit(event); 140 | } 141 | this.preventAndStop(event); 142 | } 143 | } 144 | 145 | public onDragLeave(event: Event): void { 146 | if (!this.isDropzoneDisabled()) { 147 | if (this.isDraggingOverDropZone) { 148 | this.isDraggingOverDropZone = false; 149 | this.onFileLeave.emit(event); 150 | } 151 | this.preventAndStop(event); 152 | } 153 | } 154 | 155 | public dropFiles(event: DragEvent): void { 156 | if (this.isDropzoneDisabled()) { 157 | return; 158 | } 159 | this.isDraggingOverDropZone = false; 160 | if (event.dataTransfer) { 161 | let items: FileList | DataTransferItemList; 162 | if (event.dataTransfer.items) { 163 | items = event.dataTransfer.items; 164 | } else { 165 | items = event.dataTransfer.files; 166 | } 167 | this.preventAndStop(event); 168 | this.checkFiles(items); 169 | } 170 | } 171 | 172 | public openFileSelector = (event?: MouseEvent): void => { 173 | if (this.fileSelector && this.fileSelector.nativeElement) { 174 | (this.fileSelector.nativeElement as HTMLInputElement).click(); 175 | } 176 | }; 177 | 178 | /** 179 | * Processes the change event of the file input and adds the given files. 180 | * @param Event event 181 | */ 182 | public uploadFiles(event: Event): void { 183 | if (this.isDropzoneDisabled()) { 184 | return; 185 | } 186 | if (event.target) { 187 | const items = (event.target as HTMLInputElement).files || ([] as any); 188 | this.checkFiles(items); 189 | this.resetFileInput(); 190 | } 191 | } 192 | 193 | private getFakeDropEntry(file: File): NgxFileDropEntry { 194 | const fakeFileEntry: FileSystemFileEntry = { 195 | name: file.name, 196 | isDirectory: false, 197 | isFile: true, 198 | file: (callback: (filea: File) => T) => callback(file), 199 | }; 200 | return new NgxFileDropEntry(fakeFileEntry.name, fakeFileEntry); 201 | } 202 | 203 | private checkFile(item: DataTransferItem | File): void { 204 | if (!item) { 205 | return; 206 | } 207 | // if ("getAsFile" in item) { 208 | // const file = item.getAsFile(); 209 | // if (file) { 210 | // this.addToQueue( 211 | // this.getFakeDropEntry(file) 212 | // ); 213 | // return; 214 | // } 215 | // } 216 | if ("webkitGetAsEntry" in item) { 217 | let entry = item.webkitGetAsEntry(); 218 | if (entry) { 219 | if (entry.isFile) { 220 | const toUpload: NgxFileDropEntry = new NgxFileDropEntry(entry.name, entry); 221 | this.addToQueue(toUpload); 222 | 223 | } else if (entry.isDirectory) { 224 | this.traverseFileTree(entry, entry.name); 225 | } 226 | return; 227 | } 228 | } 229 | this.addToQueue(this.getFakeDropEntry((item as File))); 230 | } 231 | 232 | private checkFiles(items: FileList | DataTransferItemList): void { 233 | for (let i = 0; i < items.length; i++) { 234 | this.checkFile(items[i]); 235 | } 236 | 237 | if (this.dropEventTimerSubscription) { 238 | this.dropEventTimerSubscription.unsubscribe(); 239 | } 240 | this.dropEventTimerSubscription = timer(200, 200) 241 | .subscribe(() => { 242 | if (this.files.length > 0 && this.numOfActiveReadEntries === 0) { 243 | const files = this.files; 244 | this.files = []; 245 | this.onFileDrop.emit(files); 246 | } 247 | }); 248 | } 249 | 250 | private traverseFileTree(item: FileSystemEntry, path: string): void { 251 | if (item.isFile) { 252 | const toUpload: NgxFileDropEntry = new NgxFileDropEntry(path, item); 253 | this.files.push(toUpload); 254 | 255 | } else { 256 | path = path + '/'; 257 | const dirReader = (item as FileSystemDirectoryEntry).createReader(); 258 | let entries: FileSystemEntry[] = []; 259 | 260 | const readEntries = () => { 261 | this.numOfActiveReadEntries++; 262 | dirReader.readEntries((result) => { 263 | if (!result.length) { 264 | // add empty folders 265 | if (entries.length === 0) { 266 | const toUpload: NgxFileDropEntry = new NgxFileDropEntry(path, item); 267 | this.zone.run(() => { 268 | this.addToQueue(toUpload); 269 | }); 270 | 271 | } else { 272 | for (let i = 0; i < entries.length; i++) { 273 | this.zone.run(() => { 274 | this.traverseFileTree(entries[i], path + entries[i].name); 275 | }); 276 | } 277 | } 278 | 279 | } else { 280 | // continue with the reading 281 | entries = entries.concat(result); 282 | readEntries(); 283 | } 284 | 285 | this.numOfActiveReadEntries--; 286 | }); 287 | }; 288 | 289 | readEntries(); 290 | } 291 | } 292 | 293 | /** 294 | * Clears any added files from the file input element so the same file can subsequently be added multiple times. 295 | */ 296 | private resetFileInput(): void { 297 | if (this.fileSelector && this.fileSelector.nativeElement) { 298 | const fileInputEl = this.fileSelector.nativeElement as HTMLInputElement; 299 | const fileInputContainerEl = fileInputEl.parentElement; 300 | const helperFormEl = this.getHelperFormElement(); 301 | const fileInputPlaceholderEl = this.getFileInputPlaceholderElement(); 302 | 303 | // Just a quick check so we do not mess up the DOM (will never happen though). 304 | if (fileInputContainerEl !== helperFormEl) { 305 | // Insert the form input placeholder in the DOM before the form input element. 306 | this.renderer.insertBefore(fileInputContainerEl, fileInputPlaceholderEl, fileInputEl); 307 | // Add the form input as child of the temporary form element, removing the form input from the DOM. 308 | this.renderer.appendChild(helperFormEl, fileInputEl); 309 | // Reset the form, thus clearing the input element of any files. 310 | helperFormEl.reset(); 311 | // Add the file input back to the DOM in place of the file input placeholder element. 312 | this.renderer.insertBefore(fileInputContainerEl, fileInputEl, fileInputPlaceholderEl); 313 | // Remove the input placeholder from the DOM 314 | this.renderer.removeChild(fileInputContainerEl, fileInputPlaceholderEl); 315 | } 316 | } 317 | } 318 | 319 | /** 320 | * Get a cached HTML form element as a helper element to clear the file input element. 321 | */ 322 | private getHelperFormElement(): HTMLFormElement { 323 | if (!this.helperFormEl) { 324 | this.helperFormEl = this.renderer.createElement('form') as HTMLFormElement; 325 | } 326 | 327 | return this.helperFormEl; 328 | } 329 | 330 | /** 331 | * Get a cached HTML div element to be used as placeholder for the file input element when clearing said element. 332 | */ 333 | private getFileInputPlaceholderElement(): HTMLDivElement { 334 | if (!this.fileInputPlaceholderEl) { 335 | this.fileInputPlaceholderEl = this.renderer.createElement('div') as HTMLDivElement; 336 | } 337 | 338 | return this.fileInputPlaceholderEl; 339 | } 340 | 341 | private isDropzoneDisabled(): boolean { 342 | return (this.globalDraggingInProgress || this.disabled); 343 | } 344 | 345 | private addToQueue(item: NgxFileDropEntry): void { 346 | this.files.push(item); 347 | } 348 | 349 | private preventAndStop(event: Event): void { 350 | event.stopPropagation(); 351 | event.preventDefault(); 352 | } 353 | } 354 | -------------------------------------------------------------------------------- /projects/ngx-file-drop/src/lib/ngx-file-drop.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { NgxFileDropComponent } from './ngx-file-drop.component'; 4 | import { NgxFileDropContentTemplateDirective } from './ngx-templates.directive'; 5 | 6 | @NgModule({ 7 | declarations: [ 8 | NgxFileDropComponent, 9 | NgxFileDropContentTemplateDirective, 10 | ], 11 | imports: [ 12 | CommonModule 13 | ], 14 | exports: [ 15 | NgxFileDropComponent, 16 | NgxFileDropContentTemplateDirective, 17 | ], 18 | providers: [], 19 | bootstrap: [ 20 | NgxFileDropComponent 21 | ], 22 | }) 23 | export class NgxFileDropModule {} 24 | -------------------------------------------------------------------------------- /projects/ngx-file-drop/src/lib/ngx-templates.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, TemplateRef } from '@angular/core'; 2 | 3 | @Directive({ selector: '[ngx-file-drop-content-tmp]' }) 4 | export class NgxFileDropContentTemplateDirective { 5 | constructor(public template: TemplateRef) { } 6 | } 7 | -------------------------------------------------------------------------------- /projects/ngx-file-drop/src/public-api.ts: -------------------------------------------------------------------------------- 1 | export { NgxFileDropComponent } from './lib/ngx-file-drop.component'; 2 | export { NgxFileDropModule } from './lib/ngx-file-drop.module'; 3 | export { NgxFileDropEntry } from './lib/ngx-file-drop-entry'; 4 | export { FileSystemEntry, FileSystemDirectoryEntry, FileSystemFileEntry } from './lib/dom.types'; 5 | export { NgxFileDropContentTemplateDirective } from './lib/ngx-templates.directive'; 6 | -------------------------------------------------------------------------------- /projects/ngx-file-drop/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../out-tsc/lib", 6 | "declaration": true, 7 | "declarationMap": true, 8 | "inlineSources": true, 9 | "types": [] 10 | }, 11 | "exclude": [ 12 | "src/test.ts", 13 | "**/*.spec.ts" 14 | ] 15 | } -------------------------------------------------------------------------------- /projects/ngx-file-drop/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.lib.json", 4 | "compilerOptions": { 5 | "declarationMap": false 6 | }, 7 | "angularCompilerOptions": { 8 | "compilationMode": "partial" 9 | } 10 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "forceConsistentCasingInFileNames": true, 8 | "strict": true, 9 | "noImplicitOverride": true, 10 | "noPropertyAccessFromIndexSignature": true, 11 | "noImplicitReturns": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "sourceMap": true, 14 | "paths": { 15 | "ngx-file-drop": [ 16 | "dist/ngx-file-drop" 17 | ] 18 | }, 19 | "declaration": false, 20 | "downlevelIteration": true, 21 | "experimentalDecorators": true, 22 | "moduleResolution": "node", 23 | "importHelpers": true, 24 | "target": "ES2022", 25 | "module": "es2020", 26 | "lib": [ 27 | "es2020", 28 | "dom" 29 | ], 30 | "useDefineForClassFields": false 31 | }, 32 | "angularCompilerOptions": { 33 | "enableI18nLegacyMessageIdFormat": false, 34 | "strictInjectionParameters": true, 35 | "strictInputAccessModifiers": true, 36 | "strictTemplates": true 37 | } 38 | } 39 | --------------------------------------------------------------------------------