├── .editorconfig ├── .github └── FUNDING.yml ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── LICENSE ├── README.md ├── angular.json ├── package-lock.json ├── package.json ├── projects └── ngx-file-helpers │ ├── LICENSE │ ├── README.md │ ├── ng-package.json │ ├── package.json │ ├── src │ ├── lib │ │ ├── file-dropzone.directive.ts │ │ ├── file-handler.ts │ │ ├── file-picker.directive.ts │ │ ├── helpers.ts │ │ ├── ngx-file-helpers.module.ts │ │ ├── read-file-impl.ts │ │ ├── read-file.ts │ │ └── read-mode.enum.ts │ └── public-api.ts │ ├── tsconfig.lib.json │ └── tsconfig.lib.prod.json ├── src ├── app │ ├── app.component.css │ ├── app.component.html │ ├── app.component.ts │ ├── file-dropzone-demo │ │ ├── file-dropzone-demo.component.css │ │ ├── file-dropzone-demo.component.html │ │ └── file-dropzone-demo.component.ts │ ├── file-picker-demo │ │ ├── file-picker-demo.component.css │ │ ├── file-picker-demo.component.html │ │ └── file-picker-demo.component.ts │ ├── file-picker-large-files-demo │ │ ├── file-picker-large-files-demo.component.css │ │ ├── file-picker-large-files-demo.component.html │ │ └── file-picker-large-files-demo.component.ts │ └── read-mode.pipe.ts ├── assets │ └── .gitkeep ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── main.ts ├── polyfills.ts └── styles.css ├── tsconfig.app.json ├── tsconfig.json ├── version.bat └── version.sh /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://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 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: fvilers 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # Compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | /bazel-out 8 | 9 | # Node 10 | /node_modules 11 | npm-debug.log 12 | yarn-error.log 13 | 14 | # IDEs and editors 15 | .idea/ 16 | .project 17 | .classpath 18 | .c9/ 19 | *.launch 20 | .settings/ 21 | *.sublime-workspace 22 | 23 | # Visual Studio Code 24 | .vscode/* 25 | !.vscode/settings.json 26 | !.vscode/tasks.json 27 | !.vscode/launch.json 28 | !.vscode/extensions.json 29 | .history/* 30 | 31 | # Miscellaneous 32 | /.angular/cache 33 | .sass-cache/ 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | testem.log 38 | /typings 39 | 40 | # System files 41 | .DS_Store 42 | Thumbs.db 43 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 3 | "recommendations": ["angular.ng-template"] 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "name": "ng serve", 7 | "type": "pwa-chrome", 8 | "request": "launch", 9 | "preLaunchTask": "npm: start", 10 | "url": "http://localhost:4200/" 11 | }, 12 | { 13 | "name": "ng test", 14 | "type": "chrome", 15 | "request": "launch", 16 | "preLaunchTask": "npm: test", 17 | "url": "http://localhost:9876/debug.html" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | ".angular": true, 4 | "dist": true, 5 | "node_modules": true, 6 | "package-lock.json": true 7 | }, 8 | "cSpell.words": ["devkit", "dropzone", "packagr"] 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558 3 | "version": "2.0.0", 4 | "tasks": [ 5 | { 6 | "type": "npm", 7 | "script": "start", 8 | "isBackground": true, 9 | "problemMatcher": { 10 | "owner": "typescript", 11 | "pattern": "$tsc", 12 | "background": { 13 | "activeOnStart": true, 14 | "beginsPattern": { 15 | "regexp": "(.*?)" 16 | }, 17 | "endsPattern": { 18 | "regexp": "bundle generation complete" 19 | } 20 | } 21 | } 22 | }, 23 | { 24 | "type": "npm", 25 | "script": "test", 26 | "isBackground": true, 27 | "problemMatcher": { 28 | "owner": "typescript", 29 | "pattern": "$tsc", 30 | "background": { 31 | "activeOnStart": true, 32 | "beginsPattern": { 33 | "regexp": "(.*?)" 34 | }, 35 | "endsPattern": { 36 | "regexp": "bundle generation complete" 37 | } 38 | } 39 | } 40 | } 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | https://github.com/fvilers/ngx-file-helpers.git 2 | MIT License 3 | 4 | Copyright (c) 2020 Fabian Vilers 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NgxFileHelpersDemo 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 13.1.3. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. 28 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "ngx-file-helpers-demo": { 7 | "projectType": "application", 8 | "schematics": { 9 | "@schematics/angular:component": { 10 | "style": "css", 11 | "skipTests": true 12 | }, 13 | "@schematics/angular:class": { 14 | "skipTests": true 15 | }, 16 | "@schematics/angular:directive": { 17 | "skipTests": true 18 | }, 19 | "@schematics/angular:guard": { 20 | "skipTests": true 21 | }, 22 | "@schematics/angular:interceptor": { 23 | "skipTests": true 24 | }, 25 | "@schematics/angular:pipe": { 26 | "skipTests": true 27 | }, 28 | "@schematics/angular:resolver": { 29 | "skipTests": true 30 | }, 31 | "@schematics/angular:service": { 32 | "skipTests": true 33 | }, 34 | "@schematics/angular:application": { 35 | "strict": true 36 | } 37 | }, 38 | "root": "", 39 | "sourceRoot": "src", 40 | "prefix": "app", 41 | "architect": { 42 | "build": { 43 | "builder": "@angular/build:application", 44 | "options": { 45 | "outputPath": { 46 | "base": "dist/ngx-file-helpers-demo" 47 | }, 48 | "index": "src/index.html", 49 | "polyfills": [ 50 | "zone.js" 51 | ], 52 | "tsConfig": "tsconfig.app.json", 53 | "inlineStyleLanguage": "css", 54 | "assets": [ 55 | "src/favicon.ico", 56 | "src/assets" 57 | ], 58 | "styles": [ 59 | "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", 60 | "src/styles.css" 61 | ], 62 | "scripts": [], 63 | "browser": "src/main.ts" 64 | }, 65 | "configurations": { 66 | "production": { 67 | "budgets": [ 68 | { 69 | "type": "initial", 70 | "maximumWarning": "500kb", 71 | "maximumError": "1mb" 72 | }, 73 | { 74 | "type": "anyComponentStyle", 75 | "maximumWarning": "2kb", 76 | "maximumError": "4kb" 77 | } 78 | ], 79 | "fileReplacements": [ 80 | { 81 | "replace": "src/environments/environment.ts", 82 | "with": "src/environments/environment.prod.ts" 83 | } 84 | ], 85 | "outputHashing": "all" 86 | }, 87 | "development": { 88 | "optimization": false, 89 | "extractLicenses": false, 90 | "sourceMap": true, 91 | "namedChunks": true 92 | } 93 | }, 94 | "defaultConfiguration": "production" 95 | }, 96 | "serve": { 97 | "builder": "@angular/build:dev-server", 98 | "configurations": { 99 | "production": { 100 | "buildTarget": "ngx-file-helpers-demo:build:production" 101 | }, 102 | "development": { 103 | "buildTarget": "ngx-file-helpers-demo:build:development" 104 | } 105 | }, 106 | "defaultConfiguration": "development" 107 | }, 108 | "extract-i18n": { 109 | "builder": "@angular/build:extract-i18n", 110 | "options": { 111 | "buildTarget": "ngx-file-helpers-demo:build" 112 | } 113 | } 114 | } 115 | }, 116 | "ngx-file-helpers": { 117 | "projectType": "library", 118 | "root": "projects/ngx-file-helpers", 119 | "sourceRoot": "projects/ngx-file-helpers/src", 120 | "prefix": "lib", 121 | "architect": { 122 | "build": { 123 | "builder": "@angular/build:ng-packagr", 124 | "options": { 125 | "project": "projects/ngx-file-helpers/ng-package.json" 126 | }, 127 | "configurations": { 128 | "production": { 129 | "tsConfig": "projects/ngx-file-helpers/tsconfig.lib.prod.json" 130 | }, 131 | "development": { 132 | "tsConfig": "projects/ngx-file-helpers/tsconfig.lib.json" 133 | } 134 | }, 135 | "defaultConfiguration": "production" 136 | } 137 | } 138 | } 139 | }, 140 | "cli": { 141 | "analytics": false 142 | }, 143 | "schematics": { 144 | "@schematics/angular:component": { 145 | "type": "component" 146 | }, 147 | "@schematics/angular:directive": { 148 | "type": "directive" 149 | }, 150 | "@schematics/angular:service": { 151 | "type": "service" 152 | }, 153 | "@schematics/angular:guard": { 154 | "typeSeparator": "." 155 | }, 156 | "@schematics/angular:interceptor": { 157 | "typeSeparator": "." 158 | }, 159 | "@schematics/angular:module": { 160 | "typeSeparator": "." 161 | }, 162 | "@schematics/angular:pipe": { 163 | "typeSeparator": "." 164 | }, 165 | "@schematics/angular:resolver": { 166 | "typeSeparator": "." 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-file-helpers-demo", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "prestart": "npm run build:lib", 7 | "start": "ng serve", 8 | "start:lib": "ng build ngx-file-helpers --watch", 9 | "build": "ng build", 10 | "build:lib": "ng build ngx-file-helpers --configuration production", 11 | "prepublish:lib": "npm run build:lib", 12 | "publish:lib": "npm publish dist/ngx-file-helpers", 13 | "watch": "ng build --watch --configuration development" 14 | }, 15 | "private": true, 16 | "dependencies": { 17 | "@angular/animations": "^20.0.0", 18 | "@angular/cdk": "^20.0.1", 19 | "@angular/common": "^20.0.0", 20 | "@angular/compiler": "^20.0.0", 21 | "@angular/core": "^20.0.0", 22 | "@angular/forms": "^20.0.0", 23 | "@angular/material": "^20.0.1", 24 | "@angular/platform-browser": "^20.0.0", 25 | "@angular/platform-browser-dynamic": "^20.0.0", 26 | "@angular/router": "^20.0.0", 27 | "rxjs": "~7.8.2", 28 | "tslib": "^2.8.1", 29 | "zone.js": "^0.15.1" 30 | }, 31 | "devDependencies": { 32 | "@angular/build": "^20.0.0", 33 | "@angular/cli": "^20.0.0", 34 | "@angular/compiler-cli": "^20.0.0", 35 | "@types/jasmine": "~5.1.8", 36 | "@types/node": "^22.15.27", 37 | "jasmine-core": "~5.7.1", 38 | "karma": "~6.4.4", 39 | "karma-chrome-launcher": "~3.2.0", 40 | "karma-coverage": "~2.2.1", 41 | "karma-jasmine": "~5.1.0", 42 | "karma-jasmine-html-reporter": "~2.1.0", 43 | "ng-packagr": "^20.0.0", 44 | "typescript": "~5.8.3" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /projects/ngx-file-helpers/LICENSE: -------------------------------------------------------------------------------- 1 | https://github.com/fvilers/ngx-file-helpers.git 2 | MIT License 3 | 4 | Copyright (c) 2020 Fabian Vilers 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /projects/ngx-file-helpers/README.md: -------------------------------------------------------------------------------- 1 | # ngx-file-helpers 2 | 3 | Angular File Helpers 4 | 5 | ## Installation 6 | 7 | Add the package to your application. 8 | 9 | ``` 10 | npm install --save ngx-file-helpers 11 | ``` 12 | 13 | ## Demo 14 | 15 | https://stackblitz.com/edit/ngx-file-helpers-demo 16 | 17 | ## Breaking changes 18 | 19 | Here's a list of the breaking changes upon the 14.0 release: 20 | 21 | - Angular (core/common) version 20.0.0 or greater is a peer dependency; 22 | 23 | Here's a list of the breaking changes upon the 13.0 release: 24 | 25 | - Angular (core/common) version 19.0.0 or greater is a peer dependency; 26 | 27 | Here's a list of the breaking changes upon the 12.0 release: 28 | 29 | - Angular (core/common) version 18.0.0 or greater is a peer dependency; 30 | 31 | Here's a list of the breaking changes upon the 11.0 release: 32 | 33 | - Angular (core/common) version 17.0.0 or greater is a peer dependency; 34 | 35 | Here's a list of the breaking changes upon the 10.0 release: 36 | 37 | - Angular (core/common) version 16.0.0 or greater is a peer dependency; 38 | 39 | Here's a list of the breaking changes upon the 9.0 release: 40 | 41 | - Angular (core/common) version 15.0.0 or greater is a peer dependency; 42 | 43 | Here's a list of the breaking changes upon the 8.0 release: 44 | 45 | - Angular (core/common) version 14.0.0 or greater is a peer dependency; 46 | 47 | Here's a list of the breaking changes upon the 7.0 release: 48 | 49 | - Angular (core/common) version 13.0.0 or greater is a peer dependency; 50 | 51 | Here's a list of the breaking changes upon the 6.0 release: 52 | 53 | - Angular (core/common) version 12.0.0 or greater is a peer dependency; 54 | 55 | Here's a list of the breaking changes upon the 5.0 release: 56 | 57 | - Angular (core/common) version 11.0.0 or greater is a peer dependency; 58 | 59 | Here's a list of the breaking changes upon the 4.0 release: 60 | 61 | - Angular (core/common) version 9.1.0 or greater is a peer dependency; 62 | 63 | Here's a list of the breaking changes upon the 3.0 release: 64 | 65 | - Angular (core/common) version 8.2.0 or greater is a peer dependency; 66 | 67 | Here's a list of the breaking changes upon the 2.0 release: 68 | 69 | - Angular (core/common) version 7.2.0 or greater is a peer dependency; 70 | - The module name has changed to `NgxFileHelpersModule`; 71 | - Read mode is not anymore bound using the directive name but the `[readMode]` property; 72 | - The `lastModifiedDate` property doesn't exist anymore on the `ReadFile` interface. 73 | 74 | ## Getting started 75 | 76 | Import the file helpers module to your application module. 77 | 78 | ``` 79 | import { BrowserModule } from '@angular/platform-browser'; 80 | import { NgModule } from '@angular/core'; 81 | import { NgxFileHelpersModule } from 'ngx-file-helpers'; 82 | 83 | import { AppComponent } from './app.component'; 84 | 85 | @NgModule({ 86 | declarations: [AppComponent], 87 | imports: [ 88 | BrowserModule, 89 | NgxFileHelpersModule 90 | ], 91 | bootstrap: [AppComponent] 92 | }) 93 | export class AppModule { } 94 | ``` 95 | 96 | ## File Picker 97 | 98 | Add the file picker directive to an element, like a button. 99 | 100 | ``` 101 | 102 | ``` 103 | 104 | Select how the file should be read; by default the mode is dataUrl. 105 | 106 | ``` 107 | 108 | ``` 109 | 110 | Bind to the `filePick` event to get the picked file from the `$event` variable. 111 | 112 | ``` 113 | 119 | ``` 120 | 121 | Use the optional `accept` attribute to indicate the types of files that the control can select. 122 | 123 | ``` 124 | 131 | ``` 132 | 133 | Use the optional `multiple` attribute to indicate whether the user can pick more than one file. 134 | 135 | ``` 136 | 144 | ``` 145 | 146 | Use the option `filter` attribute to specify a callback that you would implemented to filter if the file should be read or ignored. 147 | 148 | ``` 149 | 158 | ``` 159 | 160 | ``` 161 | export class MyComponent { 162 | ... 163 | ignoreTooBigFile(file: File): boolean { 164 | return file.size < 100000; 165 | } 166 | } 167 | ``` 168 | 169 | The directive also has a `reset()` method that unset the selected file. This is useful if you want to force the `filePick` event to trigger again even if the user has picked the same file. The directive is exported as `ngxFilePicker` so you can select is using a `ViewChild` decorator. 170 | 171 | ``` 172 | 177 | ``` 178 | 179 | ``` 180 | export class MyComponent { 181 | ... 182 | @ViewChild('myFilePicker') 183 | private filePicker: FilePickerDirective; 184 | ... 185 | 186 | onReadEnd(fileCount: number) { 187 | this.status = `Read ${fileCount} file(s) on ${new Date().toLocaleTimeString()}.`; 188 | this.filePicker.reset(); 189 | } 190 | } 191 | ``` 192 | 193 | There are three more events that can be listened to: 194 | 195 | - `readStart`: triggered when the directive start to read files. 196 | - `readError`: triggered when the directive has encountered an error reading a file; this typically occurs when dropping a folder 197 | - `readEnd`: triggered when the directive has read all the files. 198 | 199 | `readStart` emits the number of files (`$event` variable) to be read. 200 | `readError` emits an object containing the file and the error that occurred. 201 | `readEnd` emits the number of files that have been successfully read. 202 | 203 | In some cases you may want to filter files before reading them. You could use a special input argument `filter` which takes a function which should return `true` file to be read or `false` to stop reading. 204 | 205 | ``` 206 | export class MyComponent { 207 | ... 208 | 209 | filterFileBeforeReading(file) { 210 | // file is a native browser File 211 | // skip files which are >25mb 212 | return file.size < 25 * 1000 * 1000; 213 | } 214 | } 215 | ``` 216 | 217 | ``` 218 | 227 | ``` 228 | 229 | ## File Dropzone 230 | 231 | Add the file dropzone directive to an element, like a div. 232 | 233 | ``` 234 |
Drop a file in this zone.
235 | ``` 236 | 237 | Select how the file should be read; by default the mode is dataUrl. 238 | 239 | ``` 240 |
Drop a file in this zone.
241 | ``` 242 | 243 | Bind to the `fileDrop` event to get the dropped file from the `$event` variable. 244 | 245 | ``` 246 |
249 | Drop a file in this zone. 250 |
251 | ``` 252 | 253 | The directive is exported as `ngxFileDropzone` so you can select is using a `ViewChild` decorator. You can use the same `filter` attribute and `readStart`, `readEnd` events (see the `FilePicker` directive). 254 | 255 | ## ReadFile 256 | 257 | The read file implements the following interface: 258 | 259 | ``` 260 | interface ReadFile { 261 | name: string; 262 | size: number; 263 | type: string; 264 | readMode: ReadMode; 265 | content?: any; 266 | underlyingFile: File; // https://developer.mozilla.org/en-US/docs/Web/API/File 267 | } 268 | ``` 269 | 270 | ## ReadMode 271 | 272 | Available read modes are exposed through the ReadMode enum: 273 | 274 | ``` 275 | enum ReadMode { 276 | ArrayBuffer, 277 | BinaryString, 278 | DataURL, 279 | Text 280 | Skip, 281 | } 282 | ``` 283 | 284 | A new read mode has been introduced to ensure the directive skips reading the file. This is particularly important when uploading large files as the FileReader used behind the scenes cannot handle that case by default. Reading the underlying file is left to the directive consumer as the `content` property will be `undefined` for `ReadMode.Skip`. 285 | -------------------------------------------------------------------------------- /projects/ngx-file-helpers/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/ngx-file-helpers", 4 | "lib": { 5 | "entryFile": "src/public-api.ts" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /projects/ngx-file-helpers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-file-helpers", 3 | "version": "14.0.0", 4 | "description": "Angular File Helpers", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/fvilers/ngx-file-helpers.git" 8 | }, 9 | "keywords": [ 10 | "angular", 11 | "file", 12 | "helpers" 13 | ], 14 | "author": "Fabian Vilers (https://www.dev-one.com/)", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/fvilers/ngx-file-helpers/issues" 18 | }, 19 | "homepage": "https://github.com/fvilers/ngx-file-helpers/projects/ngx-file-helpers#readme", 20 | "peerDependencies": { 21 | "@angular/common": "^20.0.0", 22 | "@angular/core": "^20.0.0" 23 | }, 24 | "dependencies": { 25 | "tslib": "^2.8.1" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /projects/ngx-file-helpers/src/lib/file-dropzone.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, HostListener, output } from '@angular/core'; 2 | import { FileHandler } from './file-handler'; 3 | import { ReadFile } from './read-file'; 4 | 5 | @Directive({ 6 | selector: '[ngxFileDropzone]', 7 | exportAs: 'ngxFileDropzone', 8 | }) 9 | export class FileDropzoneDirective extends FileHandler { 10 | public readonly fileDrop = output(); 11 | 12 | @HostListener('dragenter', ['$event']) 13 | public onDragEnter(event: DragEvent) { 14 | event.stopPropagation(); 15 | event.preventDefault(); 16 | } 17 | 18 | @HostListener('dragover', ['$event']) 19 | public onDragOver(event: DragEvent) { 20 | event.stopPropagation(); 21 | event.preventDefault(); 22 | } 23 | 24 | @HostListener('drop', ['$event']) 25 | public onDrop(event: DragEvent) { 26 | event.stopPropagation(); 27 | event.preventDefault(); 28 | 29 | if (event.dataTransfer !== null) { 30 | this.readFiles(event.dataTransfer.files, (readFile) => 31 | this.fileDrop.emit(readFile) 32 | ); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /projects/ngx-file-helpers/src/lib/file-handler.ts: -------------------------------------------------------------------------------- 1 | import { Directive, input, output } from '@angular/core'; 2 | import { readFileAsync } from './helpers'; 3 | import { ReadFile } from './read-file'; 4 | import { ReadMode } from './read-mode.enum'; 5 | 6 | @Directive() 7 | export abstract class FileHandler { 8 | public readonly readMode = input(ReadMode.DataURL); 9 | 10 | public readonly filter = input<(file: File, index: number, files: Array) => boolean>(() => true); 11 | 12 | public readonly readStart = output(); 13 | 14 | public readonly readEnd = output(); 15 | 16 | public readonly readError = output<{ 17 | file: File; 18 | error: any; 19 | }>(); 20 | 21 | protected async readFiles( 22 | files: FileList, 23 | onFileRead: (fileRead: ReadFile) => void 24 | ): Promise { 25 | const filteredFiles = Array.from(files).filter((file, index, array) => 26 | this.filter()(file, index, array) 27 | ); 28 | const fileCount = filteredFiles.length; 29 | let readCount = 0; 30 | 31 | this.readStart.emit(fileCount); 32 | 33 | await Promise.all( 34 | filteredFiles.map(async (file) => { 35 | try { 36 | const readFile = await readFileAsync(file, this.readMode()); 37 | onFileRead(readFile); 38 | readCount++; 39 | } catch (err) { 40 | this.readError.emit({ file, error: err }); 41 | // do not re-throw, the promise returned by readFiles is not awaited anywhere 42 | // and re-throwing would result in "unhandled rejections" that the consumer cannot handle 43 | } 44 | }) 45 | ).finally(() => { 46 | this.readEnd.emit(readCount); 47 | }); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /projects/ngx-file-helpers/src/lib/file-picker.directive.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Directive, 3 | ElementRef, 4 | HostListener, 5 | OnInit, 6 | Renderer2, 7 | booleanAttribute, 8 | inject, 9 | input, 10 | output 11 | } from '@angular/core'; 12 | import { FileHandler } from './file-handler'; 13 | import { ReadFile } from './read-file'; 14 | 15 | @Directive({ 16 | selector: '[ngxFilePicker]', 17 | exportAs: 'ngxFilePicker', 18 | }) 19 | export class FilePickerDirective extends FileHandler implements OnInit { 20 | readonly #el = inject(ElementRef); 21 | readonly #renderer = inject(Renderer2); 22 | 23 | public readonly accept = input(''); 24 | 25 | public readonly multiple = input(false, { 26 | transform: booleanAttribute, 27 | }); 28 | 29 | public readonly filePick = output(); 30 | 31 | private _input?: HTMLInputElement; 32 | 33 | public ngOnInit() { 34 | this._input = this.#renderer.createElement('input'); 35 | this.#renderer.appendChild(this.#el.nativeElement, this._input); 36 | 37 | this.#renderer.setAttribute(this._input, 'type', 'file'); 38 | this.#renderer.setAttribute(this._input, 'accept', this.accept()); 39 | this.#renderer.setStyle(this._input, 'display', 'none'); 40 | 41 | if (this.multiple()) { 42 | this.#renderer.setAttribute(this._input, 'multiple', 'multiple'); 43 | } 44 | 45 | this.#renderer.listen(this._input, 'change', (event: Event) => 46 | this._onListen(event) 47 | ); 48 | } 49 | 50 | public reset() { 51 | if (!this._input) { 52 | console.error( 53 | 'It seems that ngOnInit() has not been executed or that the hidden _input element is null. Did you mess with the DOM?' 54 | ); 55 | return; 56 | } 57 | 58 | this._input.value = ''; 59 | } 60 | 61 | @HostListener('click') 62 | public browse() { 63 | if (!this._input) { 64 | console.error( 65 | 'It seems that ngOnInit() has not been executed or that the hidden _input element is null. Did you mess with the DOM?' 66 | ); 67 | return; 68 | } 69 | 70 | this._input.click(); 71 | } 72 | 73 | // The callback signature prevent the async/await usage 74 | private _onListen(event: Event) { 75 | const target = event.target as HTMLInputElement; 76 | 77 | if (target.files !== null) { 78 | this.readFiles(target.files, (readFile) => this.filePick.emit(readFile)) 79 | // reset value to trick change event making it changeable every time 80 | .finally(() => (target.value = '')); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /projects/ngx-file-helpers/src/lib/helpers.ts: -------------------------------------------------------------------------------- 1 | import { ReadFile } from './read-file'; 2 | import { ReadFileImpl } from './read-file-impl'; 3 | import { ReadMode } from './read-mode.enum'; 4 | 5 | export function coerceBooleanProperty(value: any): boolean { 6 | return value != null && `${value}` !== 'false'; 7 | } 8 | 9 | export async function readFileAsync( 10 | file: File, 11 | readMode: ReadMode 12 | ): Promise { 13 | return new Promise((resolve, reject) => { 14 | const reader = new FileReader(); 15 | 16 | reader.onload = (event: ProgressEvent) => { 17 | const fileReader = event.target as FileReader; 18 | const readFile = new ReadFileImpl(file, readMode, fileReader.result); 19 | 20 | resolve(readFile); 21 | }; 22 | 23 | reader.onerror = (event: ProgressEvent) => { 24 | reject(event); 25 | }; 26 | 27 | switch (readMode) { 28 | case ReadMode.ArrayBuffer: 29 | reader.readAsArrayBuffer(file); 30 | break; 31 | case ReadMode.BinaryString: 32 | reader.readAsBinaryString(file); 33 | break; 34 | case ReadMode.Text: 35 | reader.readAsText(file); 36 | break; 37 | case ReadMode.Skip: 38 | // Immediately return without reading the file 39 | // See: https://github.com/fvilers/ngx-file-helpers/issues/57 40 | resolve(new ReadFileImpl(file, readMode)); 41 | break; 42 | case ReadMode.DataURL: 43 | default: 44 | reader.readAsDataURL(file); 45 | break; 46 | } 47 | }); 48 | } 49 | -------------------------------------------------------------------------------- /projects/ngx-file-helpers/src/lib/ngx-file-helpers.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { FileDropzoneDirective } from './file-dropzone.directive'; 3 | import { FilePickerDirective } from './file-picker.directive'; 4 | 5 | @NgModule({ 6 | imports: [FileDropzoneDirective, FilePickerDirective], 7 | exports: [FileDropzoneDirective, FilePickerDirective], 8 | }) 9 | export class NgxFileHelpersModule { } 10 | -------------------------------------------------------------------------------- /projects/ngx-file-helpers/src/lib/read-file-impl.ts: -------------------------------------------------------------------------------- 1 | import { ReadFile } from './read-file'; 2 | import { ReadMode } from './read-mode.enum'; 3 | 4 | export class ReadFileImpl implements ReadFile { 5 | get name(): string { 6 | return this.underlyingFile.name; 7 | } 8 | 9 | get size(): number { 10 | return this.underlyingFile.size; 11 | } 12 | 13 | get type(): string { 14 | return this.underlyingFile.type; 15 | } 16 | 17 | constructor( 18 | public readonly underlyingFile: File, 19 | public readonly readMode: ReadMode, 20 | public readonly content?: any 21 | ) {} 22 | } 23 | -------------------------------------------------------------------------------- /projects/ngx-file-helpers/src/lib/read-file.ts: -------------------------------------------------------------------------------- 1 | import { ReadMode } from './read-mode.enum'; 2 | 3 | export interface ReadFile { 4 | name: string; 5 | size: number; 6 | type: string; 7 | readMode: ReadMode; 8 | content?: any; 9 | underlyingFile: File; 10 | } 11 | -------------------------------------------------------------------------------- /projects/ngx-file-helpers/src/lib/read-mode.enum.ts: -------------------------------------------------------------------------------- 1 | export enum ReadMode { 2 | ArrayBuffer, 3 | BinaryString, 4 | DataURL, 5 | Text, 6 | Skip, 7 | } 8 | -------------------------------------------------------------------------------- /projects/ngx-file-helpers/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of ngx-file-helpers 3 | */ 4 | 5 | export * from './lib/file-dropzone.directive'; 6 | export * from './lib/file-picker.directive'; 7 | export * from './lib/ngx-file-helpers.module'; 8 | export * from './lib/read-file'; 9 | export * from './lib/read-mode.enum'; 10 | -------------------------------------------------------------------------------- /projects/ngx-file-helpers/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": ["src/test.ts", "**/*.spec.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /projects/ngx-file-helpers/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 | } 11 | -------------------------------------------------------------------------------- /src/app/app.component.css: -------------------------------------------------------------------------------- 1 | .filler { 2 | flex: 1 1 auto; 3 | } 4 | 5 | main { 6 | padding: 20px; 7 | } 8 | 9 | .tab-container { 10 | padding: 5px; 11 | } 12 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 |

Angular File Helpers Demo

3 | 4 | GitHub 5 |
6 | 7 |
8 | 9 | 10 |
11 | 12 |
13 |
14 | 15 | 16 |
17 | 18 |
19 |
20 | 21 | 22 |
23 | 24 |
25 |
26 |
27 |
28 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { MatButtonModule } from '@angular/material/button'; 3 | import { MatTabsModule } from '@angular/material/tabs'; 4 | import { MatToolbarModule } from '@angular/material/toolbar'; 5 | import { NgxFileHelpersModule } from 'ngx-file-helpers'; 6 | import { FileDropzoneDemoComponent } from './file-dropzone-demo/file-dropzone-demo.component'; 7 | import { FilePickerDemoComponent } from './file-picker-demo/file-picker-demo.component'; 8 | import { FilePickerLargeFilesDemoComponent } from './file-picker-large-files-demo/file-picker-large-files-demo.component'; 9 | 10 | @Component({ 11 | selector: 'app-root', 12 | templateUrl: './app.component.html', 13 | styleUrl: './app.component.css', 14 | imports: [ 15 | MatButtonModule, 16 | MatTabsModule, 17 | MatToolbarModule, 18 | NgxFileHelpersModule, 19 | FileDropzoneDemoComponent, 20 | FilePickerDemoComponent, 21 | FilePickerLargeFilesDemoComponent 22 | ] 23 | }) 24 | export class AppComponent { } 25 | -------------------------------------------------------------------------------- /src/app/file-dropzone-demo/file-dropzone-demo.component.css: -------------------------------------------------------------------------------- 1 | .dropzone { 2 | align-items: center; 3 | background: lightgrey; 4 | border: dashed 1px grey; 5 | display: flex; 6 | justify-content: center; 7 | height: 350px; 8 | width: 500px; 9 | } 10 | 11 | .hover { 12 | background: grey; 13 | border: dashed 1px darkgrey; 14 | } 15 | -------------------------------------------------------------------------------- /src/app/file-dropzone-demo/file-dropzone-demo.component.html: -------------------------------------------------------------------------------- 1 |

Drop a file

2 |
8 | 9 | @if (files.length) { 10 |

Dropped files

11 | } 12 | 13 |
    14 | @for(file of files; track $index) { 15 |
  • 16 | {{ file.name }} ({{ file.size }} bytes) read using {{ readMode | readMode }} 17 |
    {{ file.content }}
    18 |
  • 19 | } 20 |
21 | -------------------------------------------------------------------------------- /src/app/file-dropzone-demo/file-dropzone-demo.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { NgxFileHelpersModule, ReadFile, ReadMode } from 'ngx-file-helpers'; 3 | import { ReadModePipe } from '../read-mode.pipe'; 4 | 5 | @Component({ 6 | selector: 'app-file-dropzone-demo', 7 | templateUrl: './file-dropzone-demo.component.html', 8 | styleUrl: './file-dropzone-demo.component.css', 9 | imports: [NgxFileHelpersModule, ReadModePipe] 10 | }) 11 | export class FileDropzoneDemoComponent { 12 | public readMode = ReadMode.DataURL; 13 | public isHover: boolean = false; 14 | public files: Array = []; 15 | 16 | addFile(file: ReadFile) { 17 | this.files.push(file); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/app/file-picker-demo/file-picker-demo.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvilers/ngx-file-helpers/998c179947bb57e4ba799722bf59573648d9836b/src/app/file-picker-demo/file-picker-demo.component.css -------------------------------------------------------------------------------- /src/app/file-picker-demo/file-picker-demo.component.html: -------------------------------------------------------------------------------- 1 |

Pick an image

2 |
3 |

{{ status }}

4 | 5 | @if (picked) { 6 |
7 | 8 | 9 |
10 | File: 11 | {{ picked.name }} 12 |
13 | {{ picked.size }} bytes
14 | Read as {{ picked.readMode | readMode }} 15 |
16 |
17 | } 18 | 19 | 34 | -------------------------------------------------------------------------------- /src/app/file-picker-demo/file-picker-demo.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, viewChild } from '@angular/core'; 2 | import { MatButtonModule } from '@angular/material/button'; 3 | import { 4 | FilePickerDirective, 5 | NgxFileHelpersModule, 6 | ReadFile, 7 | ReadMode, 8 | } from 'ngx-file-helpers'; 9 | import { ReadModePipe } from '../read-mode.pipe'; 10 | 11 | @Component({ 12 | selector: 'app-file-picker-demo', 13 | templateUrl: './file-picker-demo.component.html', 14 | styleUrl: './file-picker-demo.component.css', 15 | imports: [MatButtonModule, NgxFileHelpersModule, ReadModePipe] 16 | }) 17 | export class FilePickerDemoComponent { 18 | public readMode = ReadMode.DataURL; 19 | public picked: ReadFile | null = null; 20 | public status: string | null = null; 21 | 22 | protected readonly filePicker = viewChild(FilePickerDirective); 23 | 24 | protected ignoreTooBigFile(file: File): boolean { 25 | return file.size < 100000; 26 | } 27 | 28 | protected onReadStart(fileCount: number): void { 29 | this.status = `Reading ${fileCount} file(s)...`; 30 | this.picked = null; 31 | } 32 | 33 | protected onFilePicked(file: ReadFile): void { 34 | this.picked = file; 35 | } 36 | 37 | protected onReadEnd(fileCount: number): void { 38 | this.status = `Read ${fileCount} file(s) on ${new Date().toLocaleTimeString()}.`; 39 | this.filePicker()?.reset(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/app/file-picker-large-files-demo/file-picker-large-files-demo.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvilers/ngx-file-helpers/998c179947bb57e4ba799722bf59573648d9836b/src/app/file-picker-large-files-demo/file-picker-large-files-demo.component.css -------------------------------------------------------------------------------- /src/app/file-picker-large-files-demo/file-picker-large-files-demo.component.html: -------------------------------------------------------------------------------- 1 |

Pick a large file

2 |
3 |

{{ status }}

4 | 5 | @if (picked) { 6 |
 7 |     {{ picked.name }} ({{ picked.size }} bytes) read using {{
 8 |       readMode | readMode
 9 |     }}
10 |   
11 | } 12 | 13 | 25 | -------------------------------------------------------------------------------- /src/app/file-picker-large-files-demo/file-picker-large-files-demo.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, viewChild } from '@angular/core'; 2 | import { MatButtonModule } from '@angular/material/button'; 3 | import { 4 | FilePickerDirective, 5 | NgxFileHelpersModule, 6 | ReadFile, 7 | ReadMode, 8 | } from 'ngx-file-helpers'; 9 | import { ReadModePipe } from '../read-mode.pipe'; 10 | 11 | @Component({ 12 | selector: 'app-file-picker-large-files-demo', 13 | templateUrl: './file-picker-large-files-demo.component.html', 14 | styleUrl: './file-picker-large-files-demo.component.css', 15 | imports: [MatButtonModule, NgxFileHelpersModule, ReadModePipe] 16 | }) 17 | export class FilePickerLargeFilesDemoComponent { 18 | public readMode = ReadMode.Skip; 19 | public picked: ReadFile | null = null; 20 | public status: string | null = null; 21 | 22 | protected readonly filePicker = viewChild(FilePickerDirective); 23 | 24 | protected onReadStart(fileCount: number): void { 25 | this.status = `Reading ${fileCount} file(s)...`; 26 | this.picked = null; 27 | } 28 | 29 | protected onFilePicked(file: ReadFile): void { 30 | this.picked = file; 31 | } 32 | 33 | protected onReadEnd(fileCount: number): void { 34 | this.status = `Read ${fileCount} file(s) on ${new Date().toLocaleTimeString()}.`; 35 | this.filePicker()?.reset(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/app/read-mode.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import { ReadMode } from 'ngx-file-helpers'; 3 | 4 | @Pipe({ name: 'readMode' }) 5 | export class ReadModePipe implements PipeTransform { 6 | public transform(value: ReadMode): string { 7 | switch (value) { 8 | case ReadMode.ArrayBuffer: 9 | return 'Array Buffer'; 10 | case ReadMode.BinaryString: 11 | return 'Binary String'; 12 | case ReadMode.DataURL: 13 | return 'Data URL'; 14 | case ReadMode.Text: 15 | return 'Text'; 16 | case ReadMode.Skip: 17 | return 'Skip'; 18 | default: 19 | console.warn('Missing case for read mode', value); 20 | return ''; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvilers/ngx-file-helpers/998c179947bb57e4ba799722bf59573648d9836b/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | }; 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fvilers/ngx-file-helpers/998c179947bb57e4ba799722bf59573648d9836b/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ngx-file-helpers Demo 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { ApplicationConfig, enableProdMode } from '@angular/core'; 2 | import { bootstrapApplication } from '@angular/platform-browser'; 3 | import { provideAnimations } from '@angular/platform-browser/animations'; 4 | import { AppComponent } from './app/app.component'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | const appConfig: ApplicationConfig = { 12 | providers: [provideAnimations()] 13 | } 14 | 15 | bootstrapApplication(AppComponent, appConfig).catch((err) => { 16 | console.error(err) 17 | }); 18 | -------------------------------------------------------------------------------- /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 recent versions of Safari, Chrome (including 12 | * Opera), Edge on the desktop, and iOS 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 | * By default, zone.js will patch all possible macroTask and DomEvents 23 | * user can disable parts of macroTask/DomEvents patch by setting following flags 24 | * because those flags need to be set before `zone.js` being loaded, and webpack 25 | * will put import in the top of bundle, so user need to create a separate file 26 | * in this directory (for example: zone-flags.ts), and put the following flags 27 | * into that file, and then add the following code before importing zone.js. 28 | * import './zone-flags'; 29 | * 30 | * The flags allowed in zone-flags.ts are listed here. 31 | * 32 | * The following flags will work for all browsers. 33 | * 34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 37 | * 38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 40 | * 41 | * (window as any).__Zone_enable_cross_context_check = true; 42 | * 43 | */ 44 | 45 | /*************************************************************************************************** 46 | * Zone JS is required by default for Angular itself. 47 | */ 48 | import 'zone.js'; // Included with Angular CLI. 49 | 50 | /*************************************************************************************************** 51 | * APPLICATION IMPORTS 52 | */ 53 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | 3 | html, 4 | body { 5 | height: 100%; 6 | } 7 | body { 8 | margin: 0; 9 | font-family: Roboto, "Helvetica Neue", sans-serif; 10 | } 11 | -------------------------------------------------------------------------------- /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": ["src/main.ts"], 9 | "include": ["src/**/*.d.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /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 | "esModuleInterop": true, 9 | "strict": true, 10 | "noImplicitOverride": true, 11 | "noPropertyAccessFromIndexSignature": true, 12 | "noImplicitReturns": true, 13 | "noFallthroughCasesInSwitch": true, 14 | "sourceMap": true, 15 | "paths": { 16 | "ngx-file-helpers": [ 17 | "dist/ngx-file-helpers/ngx-file-helpers", 18 | "dist/ngx-file-helpers" 19 | ] 20 | }, 21 | "declaration": false, 22 | "experimentalDecorators": true, 23 | "moduleResolution": "bundler", 24 | "importHelpers": true, 25 | "target": "ES2022", 26 | "module": "es2020", 27 | "lib": [ 28 | "es2020", 29 | "dom" 30 | ], 31 | "useDefineForClassFields": false 32 | }, 33 | "angularCompilerOptions": { 34 | "enableI18nLegacyMessageIdFormat": false, 35 | "strictInjectionParameters": true, 36 | "strictInputAccessModifiers": true, 37 | "strictTemplates": true 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /version.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | IF "%1"=="" GOTO :usage 4 | 5 | rem Important: no space between %1 and pipe 6 | echo %1|findstr /r "^[0-9]*[0-9]\.[0-9]*[0-9]\.[0-9]*[0-9]$" >nul 2>&1 7 | IF errorlevel 1 ( 8 | GOTO :usage 9 | ) 10 | 11 | pushd projects\ngx-file-helpers 12 | CALL npm version %1 13 | popd 14 | 15 | CALL git commit -a -m "%1" && git tag v%1 16 | CALL git push && git push --tags 17 | 18 | GOTO:eof 19 | 20 | :usage 21 | echo Usage: %0 ^ ^(x.y.z^) 22 | -------------------------------------------------------------------------------- /version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | usage () { 4 | echo "Usage: $0 (x.y.z)" 5 | exit 1 6 | } 7 | 8 | if [ "$#" -ne 1 ]; then 9 | usage 10 | fi 11 | 12 | version=$1 13 | 14 | if [[ "$version" =~ ^[0-9]*[0-9]\.[0-9]*[0-9]\.[0-9]*[0-9]$ ]]; then 15 | echo $version 16 | else 17 | usage 18 | fi 19 | 20 | pushd projects/ngx-file-helpers 21 | npm version $version 22 | popd 23 | 24 | git commit -a -m "$version" && git tag $version 25 | git push && git push --tags 26 | --------------------------------------------------------------------------------