1
- import { Component , Input , Output , EventEmitter , NgZone , OnDestroy , Renderer } from '@angular/core' ;
1
+ import {
2
+ Component ,
3
+ Input ,
4
+ Output ,
5
+ EventEmitter ,
6
+ NgZone ,
7
+ OnDestroy ,
8
+ Renderer2 ,
9
+ ViewChild ,
10
+ ElementRef
11
+ } from '@angular/core' ;
2
12
import { timer , Subscription } from 'rxjs' ;
3
13
4
14
import { UploadFile } from './upload-file.model' ;
5
15
import { UploadEvent } from './upload-event.model' ;
6
- import { FileSystemFileEntry , FileSystemEntryMetadata , FileSystemEntry , FileSystemDirectoryEntry } from './dom.types' ;
16
+ import { FileSystemFileEntry , FileSystemEntry , FileSystemDirectoryEntry } from './dom.types' ;
7
17
8
18
@Component ( {
9
19
selector : 'file-drop' ,
10
20
templateUrl : './file-drop.component.html' ,
11
21
styleUrls : [ './file-drop.component.scss' ]
12
22
} )
13
-
14
-
15
23
export class FileComponent implements OnDestroy {
16
24
17
25
@Input ( )
18
- headertext : string = '' ;
26
+ public headertext : string = '' ;
27
+ @Input ( )
28
+ public customstyle : string = 'drop-zone' ;
19
29
@Input ( )
20
- customstyle : string = null ;
30
+ public disableIf : boolean = false ;
21
31
@Input ( )
22
- disableIf : boolean = false ;
32
+ public showBrowseBtn : boolean = false ;
23
33
@Input ( )
24
- showBrowseBtn : boolean = false ;
34
+ public browseBtnLabel : string = 'Browse files' ;
25
35
26
36
@Output ( )
27
37
public onFileDrop : EventEmitter < UploadEvent > = new EventEmitter < UploadEvent > ( ) ;
@@ -30,37 +40,34 @@ export class FileComponent implements OnDestroy {
30
40
@Output ( )
31
41
public onFileLeave : EventEmitter < any > = new EventEmitter < any > ( ) ;
32
42
33
- stack = [ ] ;
43
+ @ViewChild ( 'fileSelector' )
44
+ public fileSelector : ElementRef ;
45
+
46
+ stack : string [ ] = [ ] ;
34
47
files : UploadFile [ ] = [ ] ;
35
- subscription : Subscription ;
48
+ subscription : Subscription | null = null ;
36
49
dragoverflag : boolean = false ;
37
50
38
51
globalDisable : boolean = false ;
39
52
globalStart : Function ;
40
53
globalEnd : Function ;
41
54
42
- numOfActiveReadEntries = 0
43
-
44
- length ;
45
- items ;
55
+ numOfActiveReadEntries = 0 ;
46
56
47
57
constructor (
48
58
private zone : NgZone ,
49
- private renderer : Renderer
59
+ private renderer : Renderer2
50
60
) {
51
- if ( ! this . customstyle ) {
52
- this . customstyle = 'drop-zone' ;
53
- }
54
- this . globalStart = this . renderer . listen ( 'document' , 'dragstart' , ( evt ) => {
61
+ this . globalStart = this . renderer . listen ( 'document' , 'dragstart' , ( evt : Event ) => {
55
62
this . globalDisable = true ;
56
63
} ) ;
57
- this . globalEnd = this . renderer . listen ( 'document' , 'dragend' , ( evt ) => {
64
+ this . globalEnd = this . renderer . listen ( 'document' , 'dragend' , ( evt : Event ) => {
58
65
this . globalDisable = false ;
59
66
} ) ;
60
67
}
61
68
62
69
public onDragOver ( event : Event ) : void {
63
- if ( ! this . globalDisable && ! this . disableIf ) {
70
+ if ( ! this . isDropzoneDisabled ( ) ) {
64
71
if ( ! this . dragoverflag ) {
65
72
this . dragoverflag = true ;
66
73
this . onFileOver . emit ( event ) ;
@@ -70,7 +77,7 @@ export class FileComponent implements OnDestroy {
70
77
}
71
78
72
79
public onDragLeave ( event : Event ) : void {
73
- if ( ! this . globalDisable && ! this . disableIf ) {
80
+ if ( ! this . isDropzoneDisabled ( ) ) {
74
81
if ( this . dragoverflag ) {
75
82
this . dragoverflag = false ;
76
83
this . onFileLeave . emit ( event ) ;
@@ -79,90 +86,97 @@ export class FileComponent implements OnDestroy {
79
86
}
80
87
}
81
88
82
- dropFiles ( event : any ) {
83
- this . dragoverflag = false ;
84
- event . dataTransfer . dropEffect = 'copy' ;
85
- if ( event . dataTransfer . items ) {
86
- this . length = event . dataTransfer . items . length ;
87
- this . items = event . dataTransfer . items ;
88
- } else {
89
- this . length = event . dataTransfer . files . length ;
90
- this . items = event . dataTransfer . files ;
89
+ public dropFiles ( event : DragEvent ) : void {
90
+ if ( ! this . isDropzoneDisabled ( ) ) {
91
+ this . dragoverflag = false ;
92
+ if ( event . dataTransfer ) {
93
+ event . dataTransfer . dropEffect = 'copy' ;
94
+ let items : FileList | DataTransferItemList ;
95
+ if ( event . dataTransfer . items ) {
96
+ items = event . dataTransfer . items ;
97
+ } else {
98
+ items = event . dataTransfer . files ;
99
+ }
100
+ this . preventAndStop ( event ) ;
101
+ this . checkFiles ( items ) ;
102
+ }
91
103
}
92
- this . checkFiles ( event ) ;
93
104
}
94
105
95
- uploadFiles ( event : any ) {
96
- if ( event . srcElement ) {
97
- this . items = event . srcElement . files ;
98
- this . length = this . items . length ;
99
- this . checkFiles ( event ) ;
106
+ public onBrowseButtonClick ( event : MouseEvent ) : void {
107
+ if ( this . fileSelector && this . fileSelector . nativeElement ) {
108
+ ( this . fileSelector . nativeElement as HTMLInputElement ) . click ( ) ;
100
109
}
101
110
}
102
111
103
- checkFiles ( event : any ) {
104
- if ( ! this . globalDisable && ! this . disableIf ) {
105
-
106
- for ( let i = 0 ; i < this . length ; i ++ ) {
107
- let entry : FileSystemEntry ;
108
- if ( this . items [ i ] . webkitGetAsEntry ) {
109
- entry = this . items [ i ] . webkitGetAsEntry ( ) ;
110
- }
111
-
112
- if ( ! entry ) {
113
- const file : File = this . items [ i ] ;
114
- if ( file ) {
115
- const fakeFileEntry : FileSystemFileEntry = {
116
- name : file . name ,
117
- isDirectory : false ,
118
- isFile : true ,
119
- file : ( callback : ( filea : File ) => void ) : void => {
120
- callback ( file )
121
- }
122
- }
123
- const toUpload : UploadFile = new UploadFile ( fakeFileEntry . name , fakeFileEntry ) ;
124
- this . addToQueue ( toUpload ) ;
125
- }
126
- } else {
127
- if ( entry . isFile ) {
128
- const toUpload : UploadFile = new UploadFile ( entry . name , entry ) ;
129
- this . addToQueue ( toUpload ) ;
130
- } else if ( entry . isDirectory ) {
131
- this . traverseFileTree ( entry , entry . name ) ;
132
- }
133
- }
112
+ public uploadFiles ( event : Event ) : void {
113
+ if ( ! this . isDropzoneDisabled ( ) ) {
114
+ if ( event . target ) {
115
+ const items = ( event . target as HTMLInputElement ) . files || ( [ ] as any ) ;
116
+ this . checkFiles ( items ) ;
134
117
}
118
+ }
119
+ }
135
120
136
- this . preventAndStop ( event ) ;
121
+ private checkFiles ( items : FileList | DataTransferItemList ) : void {
122
+ for ( let i = 0 ; i < items . length ; i ++ ) {
123
+ const item = items [ i ] ;
124
+ let entry : FileSystemEntry | null = null ;
125
+ if ( this . canGetAsEntry ( item ) ) {
126
+ entry = item . webkitGetAsEntry ( ) ;
127
+ }
137
128
138
- const timerObservable = timer ( 200 , 200 ) ;
139
- this . subscription = timerObservable . subscribe ( t => {
140
- if ( this . files . length > 0 && this . numOfActiveReadEntries === 0 ) {
141
- this . onFileDrop . emit ( new UploadEvent ( this . files ) ) ;
142
- this . files = [ ] ;
129
+ if ( ! entry ) {
130
+ if ( item ) {
131
+ const fakeFileEntry : FileSystemFileEntry = {
132
+ name : ( item as File ) . name ,
133
+ isDirectory : false ,
134
+ isFile : true ,
135
+ file : ( callback : ( filea : File ) => void ) : void => {
136
+ callback ( item as File )
137
+ }
138
+ } ;
139
+ const toUpload : UploadFile = new UploadFile ( fakeFileEntry . name , fakeFileEntry ) ;
140
+ this . addToQueue ( toUpload ) ;
143
141
}
144
- } ) ;
142
+ } else {
143
+ if ( entry . isFile ) {
144
+ const toUpload : UploadFile = new UploadFile ( entry . name , entry ) ;
145
+ this . addToQueue ( toUpload ) ;
146
+ } else if ( entry . isDirectory ) {
147
+ this . traverseFileTree ( entry , entry . name ) ;
148
+ }
149
+ }
145
150
}
146
151
152
+ const timerObservable = timer ( 200 , 200 ) ;
153
+ if ( this . subscription ) {
154
+ this . subscription . unsubscribe ( ) ;
155
+ }
156
+ this . subscription = timerObservable . subscribe ( t => {
157
+ if ( this . files . length > 0 && this . numOfActiveReadEntries === 0 ) {
158
+ this . onFileDrop . emit ( new UploadEvent ( this . files ) ) ;
159
+ this . files = [ ] ;
160
+ }
161
+ } ) ;
147
162
}
148
163
149
- private traverseFileTree ( item : FileSystemEntry , path : string ) {
150
-
164
+ private traverseFileTree ( item : FileSystemEntry , path : string ) : void {
151
165
if ( item . isFile ) {
152
166
const toUpload : UploadFile = new UploadFile ( path , item ) ;
153
167
this . files . push ( toUpload ) ;
154
168
this . zone . run ( ( ) => {
155
- this . popToStack ( ) ;
169
+ this . popFromStack ( ) ;
156
170
} ) ;
157
171
} else {
158
172
this . pushToStack ( path ) ;
159
173
path = path + '/' ;
160
174
const dirReader = ( item as FileSystemDirectoryEntry ) . createReader ( ) ;
161
- let entries = [ ] ;
175
+ let entries : FileSystemEntry [ ] = [ ] ;
162
176
const thisObj = this ;
163
177
164
178
const readEntries = function ( ) {
165
- thisObj . numOfActiveReadEntries ++
179
+ thisObj . numOfActiveReadEntries ++ ;
166
180
dirReader . readEntries ( function ( res ) {
167
181
if ( ! res . length ) {
168
182
// add empty folders
@@ -179,7 +193,7 @@ export class FileComponent implements OnDestroy {
179
193
}
180
194
}
181
195
thisObj . zone . run ( ( ) => {
182
- thisObj . popToStack ( ) ;
196
+ thisObj . popFromStack ( ) ;
183
197
} ) ;
184
198
} else {
185
199
// continue with the reading
@@ -194,30 +208,39 @@ export class FileComponent implements OnDestroy {
194
208
}
195
209
}
196
210
197
- private addToQueue ( item : UploadFile ) {
211
+ private canGetAsEntry ( item : any ) : item is DataTransferItem {
212
+ return ! ! item . webkitGetAsEntry ;
213
+ }
214
+
215
+ private isDropzoneDisabled ( ) : boolean {
216
+ return ( this . globalDisable || this . disableIf ) ;
217
+ }
218
+
219
+ private addToQueue ( item : UploadFile ) : void {
198
220
this . files . push ( item ) ;
199
221
}
200
222
201
- pushToStack ( str ) {
223
+ pushToStack ( str : string ) : void {
202
224
this . stack . push ( str ) ;
203
225
}
204
226
205
- popToStack ( ) {
206
- const value = this . stack . pop ( ) ;
227
+ popFromStack ( ) : string | undefined {
228
+ return this . stack . pop ( ) ;
207
229
}
208
230
209
- private clearQueue ( ) {
231
+ private clearQueue ( ) : void {
210
232
this . files = [ ] ;
211
233
}
212
234
213
- private preventAndStop ( event ) {
235
+ private preventAndStop ( event : Event ) : void {
214
236
event . stopPropagation ( ) ;
215
237
event . preventDefault ( ) ;
216
238
}
217
239
218
- ngOnDestroy ( ) {
240
+ ngOnDestroy ( ) : void {
219
241
if ( this . subscription ) {
220
242
this . subscription . unsubscribe ( ) ;
243
+ this . subscription = null ;
221
244
}
222
245
this . globalStart ( ) ;
223
246
this . globalEnd ( ) ;
0 commit comments