@acpaas-ui/ngx-forms 6.1.8 → 6.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/.eslintrc.json +42 -0
  2. package/LICENSE.md +21 -0
  3. package/dist/README.md +23 -0
  4. package/dist/esm2020/lib/auto-complete/auto-complete.module.mjs +23 -0
  5. package/dist/esm2020/lib/auto-complete/components/auto-complete/auto-complete.component.mjs +265 -0
  6. package/dist/esm2020/lib/datepicker/components/datepicker/datepicker.component.mjs +232 -0
  7. package/dist/esm2020/lib/datepicker/datepicker.module.mjs +54 -0
  8. package/dist/esm2020/lib/range-slider/components/range-slider/range-slider.component.mjs +297 -0
  9. package/dist/esm2020/lib/range-slider/range-slider.module.mjs +19 -0
  10. package/dist/esm2020/lib/search-filter/components/search-filter/search-filter.component.mjs +149 -0
  11. package/dist/esm2020/lib/search-filter/search-filter.module.mjs +21 -0
  12. package/{esm2020 → dist/esm2020}/lib/shared/services/search.service.mjs +6 -6
  13. package/dist/esm2020/lib/timepicker/components/timepicker/timepicker.component.mjs +130 -0
  14. package/dist/esm2020/lib/timepicker/timepicker.module.mjs +21 -0
  15. package/dist/esm2020/lib/upload/components/upload/upload.component.mjs +69 -0
  16. package/dist/esm2020/lib/upload/components/upload-input/upload-input.component.mjs +39 -0
  17. package/dist/esm2020/lib/upload/components/upload-queue/upload-queue.component.mjs +42 -0
  18. package/dist/esm2020/lib/upload/components/upload-zone/upload-zone.component.mjs +143 -0
  19. package/dist/esm2020/lib/upload/components/validation-list/validation-list.component.mjs +33 -0
  20. package/dist/esm2020/lib/upload/services/validation-messages.service.mjs +29 -0
  21. package/dist/esm2020/lib/upload/upload.module.mjs +44 -0
  22. package/{fesm2015 → dist/fesm2015}/acpaas-ui-ngx-forms.mjs +141 -994
  23. package/dist/fesm2015/acpaas-ui-ngx-forms.mjs.map +1 -0
  24. package/dist/fesm2020/acpaas-ui-ngx-forms.mjs +1727 -0
  25. package/dist/fesm2020/acpaas-ui-ngx-forms.mjs.map +1 -0
  26. package/dist/package.json +44 -0
  27. package/karma.conf.js +38 -0
  28. package/ng-package.json +8 -0
  29. package/package.json +6 -30
  30. package/src/lib/auto-complete/README.md +148 -0
  31. package/src/lib/auto-complete/auto-complete.module.ts +15 -0
  32. package/src/lib/auto-complete/components/auto-complete/auto-complete.component.html +51 -0
  33. package/src/lib/auto-complete/components/auto-complete/auto-complete.component.scss +4 -0
  34. package/src/lib/auto-complete/components/auto-complete/auto-complete.component.spec.ts +378 -0
  35. package/src/lib/auto-complete/components/auto-complete/auto-complete.component.ts +281 -0
  36. package/src/lib/auto-complete/public-api.ts +2 -0
  37. package/src/lib/datepicker/README.md +110 -0
  38. package/src/lib/datepicker/components/datepicker/datepicker.component.html +47 -0
  39. package/src/lib/datepicker/components/datepicker/datepicker.component.scss +13 -0
  40. package/src/lib/datepicker/components/datepicker/datepicker.component.spec.ts +204 -0
  41. package/src/lib/datepicker/components/datepicker/datepicker.component.ts +251 -0
  42. package/src/lib/datepicker/datepicker.conf.ts +11 -0
  43. package/src/lib/datepicker/datepicker.module.ts +50 -0
  44. package/src/lib/datepicker/public-api.ts +8 -0
  45. package/src/lib/datepicker/types/datepicker.types.ts +8 -0
  46. package/src/lib/range-slider/README.md +56 -0
  47. package/src/lib/range-slider/components/range-slider/range-slider.component.html +46 -0
  48. package/src/lib/range-slider/components/range-slider/range-slider.component.scss +12 -0
  49. package/src/lib/range-slider/components/range-slider/range-slider.component.spec.ts +216 -0
  50. package/src/lib/range-slider/components/range-slider/range-slider.component.ts +301 -0
  51. package/src/lib/range-slider/public-api.ts +3 -0
  52. package/src/lib/range-slider/range-slider.module.ts +11 -0
  53. package/src/lib/range-slider/types/range-slider.types.ts +4 -0
  54. package/src/lib/search-filter/README.md +86 -0
  55. package/src/lib/search-filter/components/search-filter/search-filter.component.html +66 -0
  56. package/src/lib/search-filter/components/search-filter/search-filter.component.scss +23 -0
  57. package/src/lib/search-filter/components/search-filter/search-filter.component.spec.ts +264 -0
  58. package/src/lib/search-filter/components/search-filter/search-filter.component.ts +140 -0
  59. package/src/lib/search-filter/public-api.ts +3 -0
  60. package/src/lib/search-filter/search-filter.module.ts +13 -0
  61. package/src/lib/search-filter/types/search-filter.types.ts +4 -0
  62. package/src/lib/shared/services/search.service.spec.ts +78 -0
  63. package/src/lib/shared/services/search.service.ts +32 -0
  64. package/src/lib/shared/types/search.types.ts +6 -0
  65. package/src/lib/timepicker/README.md +84 -0
  66. package/src/lib/timepicker/classes/timepicker.validators.spec.ts +54 -0
  67. package/src/lib/timepicker/classes/timepicker.validators.ts +61 -0
  68. package/src/lib/timepicker/components/timepicker/timepicker.component.html +37 -0
  69. package/src/lib/timepicker/components/timepicker/timepicker.component.scss +3 -0
  70. package/src/lib/timepicker/components/timepicker/timepicker.component.spec.ts +161 -0
  71. package/src/lib/timepicker/components/timepicker/timepicker.component.ts +128 -0
  72. package/src/lib/timepicker/public-api.ts +4 -0
  73. package/src/lib/timepicker/timepicker.module.ts +13 -0
  74. package/src/lib/timepicker/types/timepicker.types.ts +5 -0
  75. package/src/lib/upload/README.md +283 -0
  76. package/src/lib/upload/classes/uploader.class.spec.ts +100 -0
  77. package/src/lib/upload/classes/uploader.class.ts +144 -0
  78. package/src/lib/upload/components/upload/upload.component.html +28 -0
  79. package/src/lib/upload/components/upload/upload.component.scss +3 -0
  80. package/src/lib/upload/components/upload/upload.component.spec.ts +117 -0
  81. package/src/lib/upload/components/upload/upload.component.ts +50 -0
  82. package/src/lib/upload/components/upload-input/upload-input.component.html +11 -0
  83. package/src/lib/upload/components/upload-input/upload-input.component.spec.ts +55 -0
  84. package/src/lib/upload/components/upload-input/upload-input.component.ts +35 -0
  85. package/src/lib/upload/components/upload-queue/upload-queue.component.html +16 -0
  86. package/src/lib/upload/components/upload-queue/upload-queue.component.spec.ts +99 -0
  87. package/src/lib/upload/components/upload-queue/upload-queue.component.ts +36 -0
  88. package/src/lib/upload/components/upload-zone/upload-zone.component.html +55 -0
  89. package/src/lib/upload/components/upload-zone/upload-zone.component.scss +3 -0
  90. package/src/lib/upload/components/upload-zone/upload-zone.component.spec.ts +144 -0
  91. package/src/lib/upload/components/upload-zone/upload-zone.component.ts +142 -0
  92. package/src/lib/upload/components/validation-list/validation-list.component.html +15 -0
  93. package/src/lib/upload/components/validation-list/validation-list.component.spec.ts +57 -0
  94. package/src/lib/upload/components/validation-list/validation-list.component.ts +29 -0
  95. package/src/lib/upload/public-api.ts +10 -0
  96. package/src/lib/upload/services/validation-messages.service.spec.ts +66 -0
  97. package/src/lib/upload/services/validation-messages.service.ts +27 -0
  98. package/src/lib/upload/types/upload.types.ts +20 -0
  99. package/src/lib/upload/upload.conf.ts +15 -0
  100. package/src/lib/upload/upload.module.ts +34 -0
  101. package/src/public-api.ts +6 -0
  102. package/src/test.ts +9 -0
  103. package/tsconfig.lib.json +26 -0
  104. package/tsconfig.spec.json +17 -0
  105. package/esm2020/lib/auto-complete/auto-complete.module.mjs +0 -24
  106. package/esm2020/lib/auto-complete/components/auto-complete/auto-complete.component.mjs +0 -347
  107. package/esm2020/lib/datepicker/components/datepicker/datepicker.component.mjs +0 -308
  108. package/esm2020/lib/datepicker/datepicker.module.mjs +0 -55
  109. package/esm2020/lib/range-slider/components/range-slider/range-slider.component.mjs +0 -349
  110. package/esm2020/lib/range-slider/range-slider.module.mjs +0 -20
  111. package/esm2020/lib/search-filter/components/search-filter/search-filter.component.mjs +0 -266
  112. package/esm2020/lib/search-filter/search-filter.module.mjs +0 -22
  113. package/esm2020/lib/timepicker/components/timepicker/timepicker.component.mjs +0 -243
  114. package/esm2020/lib/timepicker/timepicker.module.mjs +0 -22
  115. package/esm2020/lib/upload/components/upload/upload.component.mjs +0 -97
  116. package/esm2020/lib/upload/components/upload-input/upload-input.component.mjs +0 -56
  117. package/esm2020/lib/upload/components/upload-queue/upload-queue.component.mjs +0 -78
  118. package/esm2020/lib/upload/components/upload-zone/upload-zone.component.mjs +0 -252
  119. package/esm2020/lib/upload/components/validation-list/validation-list.component.mjs +0 -64
  120. package/esm2020/lib/upload/services/validation-messages.service.mjs +0 -29
  121. package/esm2020/lib/upload/upload.module.mjs +0 -45
  122. package/fesm2015/acpaas-ui-ngx-forms.mjs.map +0 -1
  123. package/fesm2020/acpaas-ui-ngx-forms.mjs +0 -2394
  124. package/fesm2020/acpaas-ui-ngx-forms.mjs.map +0 -1
  125. /package/{esm2020 → dist/esm2020}/acpaas-ui-ngx-forms.mjs +0 -0
  126. /package/{esm2020 → dist/esm2020}/lib/auto-complete/public-api.mjs +0 -0
  127. /package/{esm2020 → dist/esm2020}/lib/datepicker/datepicker.conf.mjs +0 -0
  128. /package/{esm2020 → dist/esm2020}/lib/datepicker/public-api.mjs +0 -0
  129. /package/{esm2020 → dist/esm2020}/lib/datepicker/types/datepicker.types.mjs +0 -0
  130. /package/{esm2020 → dist/esm2020}/lib/range-slider/public-api.mjs +0 -0
  131. /package/{esm2020 → dist/esm2020}/lib/range-slider/types/range-slider.types.mjs +0 -0
  132. /package/{esm2020 → dist/esm2020}/lib/search-filter/public-api.mjs +0 -0
  133. /package/{esm2020 → dist/esm2020}/lib/search-filter/types/search-filter.types.mjs +0 -0
  134. /package/{esm2020 → dist/esm2020}/lib/shared/types/search.types.mjs +0 -0
  135. /package/{esm2020 → dist/esm2020}/lib/timepicker/classes/timepicker.validators.mjs +0 -0
  136. /package/{esm2020 → dist/esm2020}/lib/timepicker/public-api.mjs +0 -0
  137. /package/{esm2020 → dist/esm2020}/lib/timepicker/types/timepicker.types.mjs +0 -0
  138. /package/{esm2020 → dist/esm2020}/lib/upload/classes/uploader.class.mjs +0 -0
  139. /package/{esm2020 → dist/esm2020}/lib/upload/public-api.mjs +0 -0
  140. /package/{esm2020 → dist/esm2020}/lib/upload/types/upload.types.mjs +0 -0
  141. /package/{esm2020 → dist/esm2020}/lib/upload/upload.conf.mjs +0 -0
  142. /package/{esm2020 → dist/esm2020}/public-api.mjs +0 -0
  143. /package/{index.d.ts → dist/index.d.ts} +0 -0
  144. /package/{lib → dist/lib}/auto-complete/auto-complete.module.d.ts +0 -0
  145. /package/{lib → dist/lib}/auto-complete/components/auto-complete/auto-complete.component.d.ts +0 -0
  146. /package/{lib → dist/lib}/auto-complete/public-api.d.ts +0 -0
  147. /package/{lib → dist/lib}/datepicker/components/datepicker/datepicker.component.d.ts +0 -0
  148. /package/{lib → dist/lib}/datepicker/datepicker.conf.d.ts +0 -0
  149. /package/{lib → dist/lib}/datepicker/datepicker.module.d.ts +0 -0
  150. /package/{lib → dist/lib}/datepicker/public-api.d.ts +0 -0
  151. /package/{lib → dist/lib}/datepicker/types/datepicker.types.d.ts +0 -0
  152. /package/{lib → dist/lib}/range-slider/components/range-slider/range-slider.component.d.ts +0 -0
  153. /package/{lib → dist/lib}/range-slider/public-api.d.ts +0 -0
  154. /package/{lib → dist/lib}/range-slider/range-slider.module.d.ts +0 -0
  155. /package/{lib → dist/lib}/range-slider/types/range-slider.types.d.ts +0 -0
  156. /package/{lib → dist/lib}/search-filter/components/search-filter/search-filter.component.d.ts +0 -0
  157. /package/{lib → dist/lib}/search-filter/public-api.d.ts +0 -0
  158. /package/{lib → dist/lib}/search-filter/search-filter.module.d.ts +0 -0
  159. /package/{lib → dist/lib}/search-filter/types/search-filter.types.d.ts +0 -0
  160. /package/{lib → dist/lib}/shared/services/search.service.d.ts +0 -0
  161. /package/{lib → dist/lib}/shared/types/search.types.d.ts +0 -0
  162. /package/{lib → dist/lib}/timepicker/classes/timepicker.validators.d.ts +0 -0
  163. /package/{lib → dist/lib}/timepicker/components/timepicker/timepicker.component.d.ts +0 -0
  164. /package/{lib → dist/lib}/timepicker/public-api.d.ts +0 -0
  165. /package/{lib → dist/lib}/timepicker/timepicker.module.d.ts +0 -0
  166. /package/{lib → dist/lib}/timepicker/types/timepicker.types.d.ts +0 -0
  167. /package/{lib → dist/lib}/upload/classes/uploader.class.d.ts +0 -0
  168. /package/{lib → dist/lib}/upload/components/upload/upload.component.d.ts +0 -0
  169. /package/{lib → dist/lib}/upload/components/upload-input/upload-input.component.d.ts +0 -0
  170. /package/{lib → dist/lib}/upload/components/upload-queue/upload-queue.component.d.ts +0 -0
  171. /package/{lib → dist/lib}/upload/components/upload-zone/upload-zone.component.d.ts +0 -0
  172. /package/{lib → dist/lib}/upload/components/validation-list/validation-list.component.d.ts +0 -0
  173. /package/{lib → dist/lib}/upload/public-api.d.ts +0 -0
  174. /package/{lib → dist/lib}/upload/services/validation-messages.service.d.ts +0 -0
  175. /package/{lib → dist/lib}/upload/types/upload.types.d.ts +0 -0
  176. /package/{lib → dist/lib}/upload/upload.conf.d.ts +0 -0
  177. /package/{lib → dist/lib}/upload/upload.module.d.ts +0 -0
  178. /package/{public-api.d.ts → dist/public-api.d.ts} +0 -0
@@ -0,0 +1,283 @@
1
+ # @acpaas-ui/ngx-forms
2
+
3
+ The upload module provides a couple of upload components and an `Uploader` class.
4
+
5
+ - `<aui-upload>` component
6
+ - `<aui-upload-zone>` component
7
+ - `<aui-upload-queue>` component
8
+ - `<aui-validation-list>` component
9
+ - `Uploader` class (non-angular service)
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { UploadModule } from '@acpaas-ui/ngx-forms';
15
+ ```
16
+
17
+ Add a map with error messages for the validation
18
+
19
+ ```typescript
20
+ UploadModule.forChild({
21
+ INVALID_FILE_TYPE: 'Ongeldig bestandstype',
22
+ INVALID_FILE_SIZE: 'Ongeldige bestandsgrootte',
23
+ INVALID_MIME_TYPE: 'Ongeldig mime type',
24
+ });
25
+ ```
26
+
27
+ ## Documentation
28
+
29
+ Visit our [documentation site](https://antwerp-ui.digipolis.be/) for full how-to docs and guidelines
30
+
31
+ ### API
32
+
33
+ #### `<aui-upload>`
34
+
35
+ This component wraps the `upload-zone`, `upload-queue` and `validation-list`, it is recommended to use this component instead of the individual `upload-zone` and `upload-queue` but it's not required.
36
+
37
+ | Name | Default value | Description |
38
+ | ------------------------------------------------------- | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
39
+ | `@Input() options: UploadOptions;` | `UPLOAD_OPTIONS_DEFAULT` | An instance of the UploadOptions interface that is part of the Uploader class. Its different options and there default values that are defined in `UPLOAD_OPTIONS_DEFAULT` are explained in detail below. |
40
+ | `@Input() data-id: string;` | `''` | An id that can be used in combination with a label's for attribute. |
41
+ | `@Input() ariaLabelRemove: string;` | `'Verwijder'` | The 'delete' text that is used for screenreaders. |
42
+ | `@Input() disabled: boolean;` | `false` | Optional attribute to set the upload and the upload zone disabled. |
43
+ | `@Input() multiple: boolean;` | `true` | Optional attribute to restrict the number of uploaded files to be selected. |
44
+ | `@Input() accept: boolean;` | `true` | Optional accept attribute to define the type of files that can be selected. |
45
+ | `@Output() selectUploadedFiles: EventEmitter<Object[]>` | - | On a successful upload, this will emit the response of the backend. This should be an array of objects with the uploaded filename and url. |
46
+
47
+ #### `<aui-upload-zone>`
48
+
49
+ This component provides the upload functionality.
50
+
51
+ | Name | Default value | Description |
52
+ | ------------------------------------------------------ | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
53
+ | `@Input() uploader: Uploader;` | - | An instance of the Uploader class. This class handles the validation and upload of a file. Its different options and there default values that are defined in `UPLOAD_OPTIONS_DEFAULT` are explained in detail below. |
54
+ | `@Input() multiple: boolean;` | `true` | Enable the HTML `multiple` attribute so the user can select multiple files to upload. |
55
+ | `@Input() id: string;` | `''` | An id that can be used in combination with a label's for attribute. |
56
+ | `@Input() ariaId: string;` | `[random id when no id was given]` | An id that can be used when you don't want a visible label. |
57
+ | `@Input() disabled: boolean;` | `false` | Optional attribute to set the upload zone disabled. |
58
+ | `@Input() multiple: boolean;` | `true` | Optional attribute to restrict the number of uploaded files to be selected. |
59
+ | `@Input() accept: boolean;` | `true` | Optional accept attribute to define the type of files that can be selected. |
60
+ | `@Output() uploadedFiles: EventEmitter<Object[]>;` | - | Emits an array of files that were uploaded. |
61
+ | `@Output() queuedFiles: EventEmitter<File[]>;` | - | Emits an array of file objects that have past the validation and are ready to be uploaded. |
62
+ | `@Output() invalidFiles: EventEmitter<InvalidFile[]>;` | - | Emits an array of invalid files and the reason why they are invalid. |
63
+
64
+ #### `<aui-upload-queue>`
65
+
66
+ This component provides a list of queued files.
67
+
68
+ | Name | Default value | Description |
69
+ | ------------------------------------------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
70
+ | `@Input() files: File[];` | - | An array of files. |
71
+ | `@Input() uploader: Uploader;` | - | An instance of the Uploader class. This class handles the validation and upload of a file. Its different options and there default values that are defined in `UPLOAD_OPTIONS_DEFAULT` are explained in detail below. |
72
+ | `@Input() ariaLabelRemove: string;` | `'Verwijder'` | The 'delete' text that is used for screenreaders. |
73
+ | `@Output() uploadedFiles: EventEmitter<Object[]>` | - | Emits an array of files that were uploaded. |
74
+
75
+ #### `<aui-validation-list>`
76
+
77
+ This component displays possible error messages.
78
+
79
+ | Name | Default value | Description |
80
+ | --------------------------------------- | ------------- | -------------------------------------------------------------- |
81
+ | `@Input() invalidFiles: InvalidFile[];` | `[]` | An array of invalid files and the reason why they are invalid. |
82
+ | `@Input() ariaLabelRemove: string;` | `'Verwijder'` | The 'delete' text that is used for screenreaders. |
83
+
84
+ #### `Uploader`
85
+
86
+ This class handles the validation and upload of a file.
87
+
88
+ ##### `UploadOptions`
89
+
90
+ | Name | Default value | Description |
91
+ | ----------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------ |
92
+ | `allowedMimeTypes: string[];` | `[]` | The MIME types that are allowed to be uploaded. |
93
+ | `allowedFileTypes: string[];` | `[]` | The file extensions that are allowed to be uploaded. |
94
+ | `autoUpload: boolean;` | `false` | Upload the file automatically without confirmation. |
95
+ | `maxFileSize: number;` | `0` | The maximum file size that is allowed. The value is expressed in Bytes. The value 0 equals infinite. |
96
+ | `queueLimit: number;` | `0` | The maximum number of files that can be selected at the same time. The value 0 equals infinite. |
97
+ | `type: string;` | `'drop'` | By default an upload has an `upload-zone` but with the option `'button'`, you can set it as an uploadbutton. |
98
+ | `url: string;` | `''` | The upload url. |
99
+
100
+ #### Example
101
+
102
+ ```typescript
103
+ import { UploadModule } from '@acpaas-ui/ngx-forms';
104
+
105
+ @NgModule({
106
+ imports: [
107
+ UploadModule
108
+ ]
109
+ });
110
+
111
+ export class AppModule {};
112
+ ```
113
+
114
+ **Basic upload**
115
+
116
+ ```typescript
117
+ public dropzone1: UploadOptions = {
118
+ allowedMimeTypes: ['image/jpeg'],
119
+ maxFileSize: 10000000,
120
+ queueLimit: 2,
121
+ type: 'drop',
122
+ url: 'api/upload',
123
+ };
124
+ ```
125
+
126
+ ```html
127
+ <aui-upload [options]="dropzone1" (selectUploadedFiles)="onUpload($event)">
128
+ <div class="m-upload__message">Drag your files here or click to upload</div>
129
+ <div class="m-upload__description">Optional description message</div>
130
+ </aui-upload>
131
+ ```
132
+
133
+ **Upload button**
134
+
135
+ ```typescript
136
+ public dropzone2: UploadOptions = {
137
+ type: 'button',
138
+ allowedFileTypes: ['.jpg', 'jpeg', 'png'],
139
+ autoUpload: true,
140
+ maxFileSize: 2000000,
141
+ url: 'api/upload',
142
+ };
143
+ ```
144
+
145
+ ```html
146
+ <div class="u-margin-bottom">
147
+ <aui-upload [options]="dropzone2" (selectUploadedFiles)="onUpload($event)">
148
+ <div class="m-upload__button">Select your files to upload</div>
149
+ </aui-upload>
150
+ </div>
151
+ ```
152
+
153
+ **Custom upload**
154
+
155
+ ```typescript
156
+ import { Uploader } from '@acpaas-ui/ngx-forms';
157
+ ```
158
+
159
+ ```typescript
160
+ public files = [];
161
+ public invalidFiles: InvalidFile[] = [];
162
+ public queuedFiles: File[] = [];
163
+ public uploadedFiles: File[] = [];
164
+ // Pass created options into new instance of Uploader
165
+ public uploader = new Uploader({
166
+ allowedFileTypes: ['jpg', 'jpeg', 'png'],
167
+ type: 'drop',
168
+ url: 'api/upload',
169
+ });
170
+ public showError = false;
171
+ public fileName = '';
172
+ });
173
+ ```
174
+
175
+ ```typescript
176
+ public onQueuedFiles(files: File[]) {
177
+ if (!files.length) {
178
+ return;
179
+ }
180
+ this.queuedFiles = this.queuedFiles.concat(files);
181
+ }
182
+
183
+ public onUploadedFiles(files) {
184
+ this.uploadedFiles = this.uploadedFiles.concat(files);
185
+ }
186
+
187
+ public onInvalidFiles(errorFiles: InvalidFile[]) {
188
+ this.invalidFiles = errorFiles;
189
+ if (errorFiles.length > 0) {
190
+ this.fileName = this.invalidFiles[0]['file'].name;
191
+ this.showError = true;
192
+ this.invalidFiles = [];
193
+ } else {
194
+ this.showError = false;
195
+ }
196
+ }
197
+
198
+ public reloadErrors() {
199
+ this.showError = false;
200
+ if (!this.queuedFiles.length) {
201
+ return;
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Using the uploader url option
207
+ */
208
+
209
+ public uploadFiles(): void {
210
+ // Upload files returns an obervable
211
+ this.uploader.uploadFiles(this.queuedFiles).subscribe(
212
+ (response) => {
213
+ // Response has a progress property to use with a progress bar
214
+ if (response.progress) {
215
+ console.log('response.progress = ', response.progress);
216
+ }
217
+ // Response had a data property with an array of uploaded files: UploadedFile[]
218
+ if (response.data) {
219
+ console.log('response.data = ', response.data);
220
+ }
221
+ this.uploadedFiles = response.data;
222
+ },
223
+ (err) => {
224
+ console.log('HTTP Error', err);
225
+ });
226
+ }
227
+
228
+ /**
229
+ * Using a custom upload service
230
+ */
231
+
232
+ // public uploadFiles(): void {
233
+ // if (!this.queuedFiles.length) {
234
+ // return;
235
+ // }
236
+ // this.customService.postFile(this.queuedFiles).subscribe(res => {
237
+ // this.result = res;
238
+ // }, (error) => {
239
+ // console.log(error);
240
+ // });
241
+ // }
242
+ ```
243
+
244
+ ```scss
245
+ ::ng-deep .a-upload-queue__wrapper {
246
+ .a-button {
247
+ display: none;
248
+ }
249
+ }
250
+ ```
251
+
252
+ ```html
253
+ <aui-upload-zone
254
+ [uploader]="uploader"
255
+ (queuedFiles)="onQueuedFiles($event)"
256
+ (uploadedFiles)="onUploadedFiles($event)"
257
+ (invalidFiles)="onInvalidFiles($event)"
258
+ >
259
+ <div class="m-upload__message">Drag your files here or click to upload</div>
260
+ <div class="m-upload__description">Maximum filesize: 10 MB, File extension: jpg, jpeg, png</div>
261
+ </aui-upload-zone>
262
+ <aui-upload-queue [files]="queuedFiles"></aui-upload-queue>
263
+ <div *ngIf="showError" class="u-margin-bottom">
264
+ <ul class="m-upload__files">
265
+ <li class="is-error">
266
+ <aui-icon name="ai-alert-triangle"></aui-icon>
267
+ <span class="m-upload__filename">{{ fileName }}</span>
268
+ <span class="m-upload__error">This file extension is not allowed.</span>
269
+ <button
270
+ type="button"
271
+ (click)="reloadErrors()"
272
+ class="m-upload__delete a-button a-button--text a-button--danger a-button--s has-icon"
273
+ >
274
+ <aui-icon name="ai-remove"></aui-icon>
275
+ </button>
276
+ </li>
277
+ </ul>
278
+ </div>
279
+ ```
280
+
281
+ ## Contributing
282
+
283
+ Visit our [Contribution Guidelines](../../../../../CONTRIBUTING.md) for more information on how to contribute.
@@ -0,0 +1,100 @@
1
+ import { UPLOAD_OPTIONS_DEFAULT } from '../upload.conf';
2
+ import { UploadOptions } from '../types/upload.types';
3
+
4
+ import { Uploader } from './uploader.class';
5
+
6
+ describe('Uploader', () => {
7
+ const uploadResponse = [
8
+ {
9
+ url: 'http://localhost/myfile',
10
+ name: 'myFile',
11
+ },
12
+ ];
13
+
14
+ beforeEach(() => {});
15
+
16
+ it('should have default options', () => {
17
+ const uploader = new Uploader();
18
+ expect(uploader.options).toEqual(UPLOAD_OPTIONS_DEFAULT);
19
+ });
20
+
21
+ it('should merge custom options with default options', () => {
22
+ const options: UploadOptions = {
23
+ type: 'button',
24
+ maxFileSize: 20000,
25
+ };
26
+
27
+ const uploader = new Uploader(options);
28
+ const result = Object.assign({}, UPLOAD_OPTIONS_DEFAULT, options);
29
+ expect(uploader.options).toEqual(result);
30
+ });
31
+
32
+ it('should validate file type', () => {
33
+ let result;
34
+ let f;
35
+ const date = new Date();
36
+ const uploader = new Uploader({
37
+ allowedFileTypes: ['jpg'],
38
+ });
39
+
40
+ f = new File([''], 'filename.txt', { type: 'text/plain', lastModified: date.getTime() });
41
+ result = uploader.validateFiles([f]);
42
+ expect(result.validFiles.length).toEqual(0);
43
+ expect(result.invalidFiles.length).toEqual(1);
44
+
45
+ f = new File([''], 'filename.jpg', { type: 'image/jpeg', lastModified: date.getTime() });
46
+ result = uploader.validateFiles([f]);
47
+ expect(result.validFiles.length).toEqual(1);
48
+ expect(result.invalidFiles.length).toEqual(0);
49
+ });
50
+
51
+ it('should validate mime type', () => {
52
+ let result;
53
+ let f;
54
+ const date = new Date();
55
+ const uploader = new Uploader({
56
+ allowedMimeTypes: ['image/jpeg'],
57
+ });
58
+
59
+ f = new File([''], 'filename.txt', { type: 'text/plain', lastModified: date.getTime() });
60
+ result = uploader.validateFiles([f]);
61
+ expect(result.validFiles.length).toEqual(0);
62
+ expect(result.invalidFiles.length).toEqual(1);
63
+
64
+ f = new File([''], 'filename.jpg', { type: 'image/jpeg', lastModified: date.getTime() });
65
+ result = uploader.validateFiles([f]);
66
+ expect(result.validFiles.length).toEqual(1);
67
+ expect(result.invalidFiles.length).toEqual(0);
68
+ });
69
+
70
+ it('should validate max file size', () => {
71
+ let result;
72
+ let f;
73
+ const date = new Date();
74
+ const uploader = new Uploader({
75
+ maxFileSize: 2,
76
+ });
77
+
78
+ f = new File(['takesomespace'], 'filename.txt', { type: 'text/plain', lastModified: date.getTime() });
79
+ result = uploader.validateFiles([f]);
80
+ expect(result.validFiles.length).toEqual(0);
81
+ expect(result.invalidFiles.length).toEqual(1);
82
+
83
+ f = new File(['a'], 'filename.jpg', { type: 'image/jpeg', lastModified: date.getTime() });
84
+ result = uploader.validateFiles([f]);
85
+ expect(result.validFiles.length).toEqual(1);
86
+ expect(result.invalidFiles.length).toEqual(0);
87
+ });
88
+
89
+ it('should throw an error if no upload url is defined', () => {
90
+ const date = new Date();
91
+ const uploader = new Uploader({
92
+ maxFileSize: 2,
93
+ });
94
+
95
+ const f = new File(['takesomespace'], 'filename.txt', { type: 'text/plain', lastModified: date.getTime() });
96
+ expect(() => {
97
+ uploader.uploadFiles([f]);
98
+ }).toThrow(Error('Define the upload url.'));
99
+ });
100
+ });
@@ -0,0 +1,144 @@
1
+ import { Observable } from 'rxjs';
2
+
3
+ import { UPLOAD_OPTIONS_DEFAULT } from '../upload.conf';
4
+ import { InvalidFile, UploadOptions } from '../types/upload.types';
5
+
6
+ export class Uploader {
7
+ public options: UploadOptions = UPLOAD_OPTIONS_DEFAULT;
8
+
9
+ constructor(options?: UploadOptions) {
10
+ this.setOptions(options);
11
+ }
12
+
13
+ public setOptions(options) {
14
+ this.options = Object.assign({}, this.options, options);
15
+ }
16
+
17
+ public uploadFiles(files: File[]): Observable<{ progress: number, data: object[] }> {
18
+ const formData: FormData = this.filesToFormData(files);
19
+
20
+ return new Observable(observer => {
21
+ const xhr = new XMLHttpRequest();
22
+
23
+ // Progress callback
24
+ xhr.upload.addEventListener('progress', (e) => {
25
+ if (e.lengthComputable) {
26
+ const percentComplete = e.loaded / e.total;
27
+
28
+ observer.next({
29
+ progress: percentComplete,
30
+ data: null,
31
+ });
32
+ }
33
+ });
34
+
35
+ // Complete callback
36
+ xhr.onload = () => {
37
+ observer.next({
38
+ progress: 1,
39
+ data: xhr.response,
40
+ });
41
+ // observer.complete();
42
+ };
43
+
44
+ // Do request
45
+ xhr.responseType = 'json';
46
+ xhr.open('post', this.options.url);
47
+ xhr.send(formData);
48
+ });
49
+ }
50
+
51
+ public validateFiles(files) {
52
+ const validFiles: File[] = [];
53
+ const invalidFiles: InvalidFile[] = [];
54
+
55
+ if (files.length > 0) {
56
+
57
+ for (const file of files) {
58
+ const errors = [];
59
+
60
+ if (!this.validateFileType(file)) {
61
+ errors.push('INVALID_FILE_TYPE');
62
+ }
63
+
64
+ if (!this.validateFileSize(file)) {
65
+ errors.push('INVALID_FILE_SIZE');
66
+ }
67
+
68
+ if (!this.validateMimeType(file)) {
69
+ errors.push('INVALID_MIME_TYPE');
70
+ }
71
+
72
+ if (errors.length === 0) {
73
+ validFiles.push(file);
74
+ } else {
75
+ invalidFiles.push({
76
+ reasons: errors,
77
+ file,
78
+ });
79
+ }
80
+ }
81
+ }
82
+
83
+ return {
84
+ validFiles,
85
+ invalidFiles,
86
+ };
87
+ }
88
+
89
+ protected filesToFormData(files: File[]): FormData {
90
+ const formData = new FormData();
91
+
92
+ if (!this.options.url || this.options.url === '') {
93
+ throw new Error('Define the upload url.');
94
+ }
95
+
96
+ for (const file of files) {
97
+ formData.append('file', file);
98
+ }
99
+
100
+ return formData;
101
+ }
102
+
103
+ protected getFileExtension(file: File): string {
104
+ return file.name.split('.')[file.name.split('.').length - 1];
105
+ }
106
+
107
+ protected validateFileType(file: File): boolean {
108
+ const allowedFileTypes = this.options.allowedFileTypes;
109
+ const ext = this.getFileExtension(file);
110
+
111
+ // Filter defined?
112
+ if (!Array.isArray(allowedFileTypes) || allowedFileTypes.length === 0) {
113
+ return true;
114
+ }
115
+
116
+ // Make allowedFileTypes case insensitive
117
+ const toUpper = (x) => x.toUpperCase();
118
+ const allowedFileTypesToUpper = allowedFileTypes.map(toUpper);
119
+
120
+ return allowedFileTypesToUpper.lastIndexOf(ext.toUpperCase()) !== -1;
121
+ }
122
+
123
+ protected validateFileSize(file: File): boolean {
124
+ const maxFileSize = this.options.maxFileSize;
125
+
126
+ // Filter defined?
127
+ if (!maxFileSize || maxFileSize === 0) {
128
+ return true;
129
+ }
130
+
131
+ return maxFileSize > file.size;
132
+ }
133
+
134
+ protected validateMimeType(file: File): boolean {
135
+ const allowedMimeTypes = this.options.allowedMimeTypes;
136
+
137
+ // Filter defined?
138
+ if (!Array.isArray(allowedMimeTypes) || allowedMimeTypes.length === 0) {
139
+ return true;
140
+ }
141
+
142
+ return allowedMimeTypes.lastIndexOf(file.type) !== -1;
143
+ }
144
+ }
@@ -0,0 +1,28 @@
1
+ <div class="m-upload aui-upload">
2
+ <aui-upload-zone
3
+ [id]="id"
4
+ [accept]="accept"
5
+ [capture]="capture"
6
+ [ariaId]="ariaId"
7
+ [disabled]="disabled"
8
+ [multiple]="multiple"
9
+ [uploader]="uploader"
10
+ (invalidFiles)="onInvalidFiles($event)"
11
+ (queuedFiles)="onQueuedFiles($event)"
12
+ (uploadedFiles)="onUploadedFiles($event)"
13
+ [label]="label"
14
+ [description]="description"
15
+ >
16
+ <div class="m-upload__button">
17
+ <ng-content select=".m-upload__button"></ng-content>
18
+ </div>
19
+ </aui-upload-zone>
20
+ <aui-validation-list [ariaLabelRemove]="ariaLabelRemove" [invalidFiles]="invalidFiles"></aui-validation-list>
21
+ <aui-upload-queue
22
+ (uploadedFiles)="onUploadedFiles($event)"
23
+ *ngIf="!options?.autoUpload"
24
+ [ariaLabelRemove]="ariaLabelRemove"
25
+ [files]="queuedFiles"
26
+ [uploader]="uploader"
27
+ ></aui-upload-queue>
28
+ </div>
@@ -0,0 +1,3 @@
1
+ .m-upload__button {
2
+ position: relative;
3
+ }
@@ -0,0 +1,117 @@
1
+ import { Component, DebugElement, Input } from '@angular/core';
2
+ import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
3
+ import { By } from '@angular/platform-browser';
4
+
5
+ import { UploadComponent } from './upload.component';
6
+ import { InvalidFile } from '../../types/upload.types';
7
+
8
+ const mockUploadFiles = [
9
+ {
10
+ url: 'http://localhost/myfile.jpg',
11
+ name: 'myfile',
12
+ },
13
+ ];
14
+
15
+ const mockFile1 = new File(['file1'], 'filename1.txt', {
16
+ type: 'text/plain',
17
+ lastModified: new Date().getTime(),
18
+ });
19
+ const mockFile2 = new File(['file2'], 'filename2.txt', {
20
+ type: 'text/plain',
21
+ lastModified: new Date().getTime(),
22
+ });
23
+ const mockFileList = [mockFile1, mockFile2];
24
+
25
+ const mockInvalidFiles: InvalidFile[] = [
26
+ {
27
+ reasons: ['INVALID_FILE_TYPE'],
28
+ file: mockFile1,
29
+ },
30
+ ];
31
+
32
+ @Component({
33
+ selector: 'aui-upload-zone',
34
+ template: '<div>aui-upload-zone</div>',
35
+ })
36
+ export class UploadZoneComponent {
37
+ @Input() uploader;
38
+ @Input() accept;
39
+ @Input() capture;
40
+ @Input() ariaId;
41
+ @Input() disabled = false;
42
+ @Input() multiple;
43
+ @Input() label;
44
+ @Input() description;
45
+ }
46
+
47
+ @Component({
48
+ selector: 'aui-upload-queue',
49
+ template: '<div>aui-upload-queue</div>',
50
+ })
51
+ export class UploadQueueComponent {
52
+ @Input() uploader;
53
+ @Input() files;
54
+ @Input() ariaLabelRemove;
55
+ }
56
+
57
+ @Component({
58
+ selector: 'aui-validation-list',
59
+ template: '<div>aui-validation-list</div>',
60
+ })
61
+ export class ValidationListComponent {
62
+ @Input() invalidFiles;
63
+ @Input() ariaLabelRemove;
64
+ }
65
+
66
+ describe('The Upload Component', () => {
67
+ let comp: UploadComponent;
68
+ let fixture: ComponentFixture<UploadComponent>;
69
+ let de: DebugElement;
70
+ let el: HTMLElement;
71
+
72
+ // waitForAsync beforeEach
73
+ beforeEach(waitForAsync(() => {
74
+ TestBed.configureTestingModule({
75
+ declarations: [
76
+ UploadComponent,
77
+ UploadZoneComponent,
78
+ UploadQueueComponent,
79
+ ValidationListComponent,
80
+ ],
81
+ }).compileComponents(); // compile template and css
82
+ }));
83
+
84
+ // synchronous beforeEach
85
+ beforeEach(() => {
86
+ fixture = TestBed.createComponent(UploadComponent);
87
+
88
+ comp = fixture.componentInstance; // BannerComponent test instance
89
+
90
+ // query for the title <h1> by CSS element selector
91
+ de = fixture.debugElement.query(By.css('.m-upload'));
92
+ el = de.nativeElement;
93
+ });
94
+
95
+ it('should exist', () => {
96
+ fixture.detectChanges();
97
+ expect(el).not.toBeUndefined();
98
+ });
99
+
100
+ it('should emit uploaded files', () => {
101
+ spyOn(comp.selectUploadedFiles, 'emit');
102
+ comp.onUploadedFiles(mockUploadFiles);
103
+ expect(comp.selectUploadedFiles.emit).toHaveBeenCalledWith(mockUploadFiles);
104
+ });
105
+
106
+ it('should concat queued files', () => {
107
+ comp.queuedFiles = [mockFile1];
108
+ comp.onQueuedFiles(mockFileList);
109
+ const result = [mockFile1].concat(mockFileList);
110
+ expect(comp.queuedFiles).toEqual(result);
111
+ });
112
+
113
+ it('should set invalid files', () => {
114
+ comp.onInvalidFiles(mockInvalidFiles);
115
+ expect(comp.invalidFiles).toEqual(mockInvalidFiles);
116
+ });
117
+ });