@vaadin/upload 23.3.0-alpha5 → 24.0.0-alpha2

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/upload",
3
- "version": "23.3.0-alpha5",
3
+ "version": "24.0.0-alpha2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -36,16 +36,16 @@
36
36
  ],
37
37
  "dependencies": {
38
38
  "@polymer/polymer": "^3.0.0",
39
- "@vaadin/button": "23.3.0-alpha5",
40
- "@vaadin/component-base": "23.3.0-alpha5",
41
- "@vaadin/progress-bar": "23.3.0-alpha5",
42
- "@vaadin/vaadin-lumo-styles": "23.3.0-alpha5",
43
- "@vaadin/vaadin-material-styles": "23.3.0-alpha5",
44
- "@vaadin/vaadin-themable-mixin": "23.3.0-alpha5"
39
+ "@vaadin/button": "24.0.0-alpha2",
40
+ "@vaadin/component-base": "24.0.0-alpha2",
41
+ "@vaadin/progress-bar": "24.0.0-alpha2",
42
+ "@vaadin/vaadin-lumo-styles": "24.0.0-alpha2",
43
+ "@vaadin/vaadin-material-styles": "24.0.0-alpha2",
44
+ "@vaadin/vaadin-themable-mixin": "24.0.0-alpha2"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@esm-bundle/chai": "^4.3.4",
48
- "@vaadin/form-layout": "23.3.0-alpha5",
48
+ "@vaadin/form-layout": "24.0.0-alpha2",
49
49
  "@vaadin/testing-helpers": "^0.3.2",
50
50
  "sinon": "^13.0.2"
51
51
  },
@@ -53,5 +53,5 @@
53
53
  "web-types.json",
54
54
  "web-types.lit.json"
55
55
  ],
56
- "gitHead": "0b6fdcf444683e97e3efb433d603e1274d5bcd66"
56
+ "gitHead": "0c16c01a6807e629a84f5a982793afecc1a7ced0"
57
57
  }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2016 - 2022 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import './vaadin-upload-file.js';
7
+ import { html as legacyHtml, PolymerElement } from '@polymer/polymer/polymer-element.js';
8
+ import { html, render } from 'lit';
9
+ import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
10
+
11
+ /**
12
+ * An element used internally by `<vaadin-upload>`. Not intended to be used separately.
13
+ *
14
+ * @extends HTMLElement
15
+ * @mixes FocusMixin
16
+ * @private
17
+ */
18
+ class UploadFileList extends ThemableMixin(PolymerElement) {
19
+ static get is() {
20
+ return 'vaadin-upload-file-list';
21
+ }
22
+
23
+ static get template() {
24
+ return legacyHtml`
25
+ <style>
26
+ :host {
27
+ display: block;
28
+ }
29
+
30
+ :host([hidden]) {
31
+ display: none !important;
32
+ }
33
+
34
+ [part='list'] {
35
+ padding: 0;
36
+ margin: 0;
37
+ list-style-type: none;
38
+ }
39
+ </style>
40
+ <ul part="list">
41
+ <slot></slot>
42
+ </ul>
43
+ `;
44
+ }
45
+
46
+ static get properties() {
47
+ return {
48
+ /**
49
+ * The array of files being processed, or already uploaded.
50
+ */
51
+ items: {
52
+ type: Array,
53
+ },
54
+
55
+ /**
56
+ * The object used to localize upload files.
57
+ */
58
+ i18n: {
59
+ type: Object,
60
+ },
61
+ };
62
+ }
63
+
64
+ static get observers() {
65
+ return ['__updateItems(items, i18n)'];
66
+ }
67
+
68
+ /** @private */
69
+ __updateItems(items, i18n) {
70
+ if (items && i18n) {
71
+ this.requestContentUpdate();
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Requests an update for the `vaadin-upload-file` elements.
77
+ *
78
+ * It is not guaranteed that the update happens immediately (synchronously) after it is requested.
79
+ */
80
+ requestContentUpdate() {
81
+ const { items, i18n } = this;
82
+
83
+ render(
84
+ html`
85
+ ${items.map(
86
+ (file) => html`
87
+ <li>
88
+ <vaadin-upload-file
89
+ .file="${file}"
90
+ .complete="${file.complete}"
91
+ .errorMessage="${file.error}"
92
+ .fileName="${file.name}"
93
+ .held="${file.held}"
94
+ .indeterminate="${file.indeterminate}"
95
+ .progress="${file.progress}"
96
+ .status="${file.status}"
97
+ .uploading="${file.uploading}"
98
+ .i18n="${i18n}"
99
+ ></vaadin-upload-file>
100
+ </li>
101
+ `,
102
+ )}
103
+ `,
104
+ this,
105
+ );
106
+ }
107
+ }
108
+
109
+ customElements.define(UploadFileList.is, UploadFileList);
110
+
111
+ export { UploadFileList };
@@ -6,7 +6,9 @@
6
6
  import '@vaadin/progress-bar/src/vaadin-progress-bar.js';
7
7
  import './vaadin-upload-icons.js';
8
8
  import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
9
+ import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
9
10
  import { FocusMixin } from '@vaadin/component-base/src/focus-mixin.js';
11
+ import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
10
12
  import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
11
13
 
12
14
  /**
@@ -30,7 +32,6 @@ import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mix
30
32
  * `start-button` | Start file upload button
31
33
  * `retry-button` | Retry file upload button
32
34
  * `remove-button` | Remove file button
33
- * `progress` | Progress bar
34
35
  *
35
36
  * The following state attributes are available for styling:
36
37
  *
@@ -46,10 +47,11 @@ import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mix
46
47
  * See [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.
47
48
  *
48
49
  * @extends HTMLElement
50
+ * @mixes ControllerMixin
49
51
  * @mixes FocusMixin
50
52
  * @mixes ThemableMixin
51
53
  */
52
- class UploadFile extends FocusMixin(ThemableMixin(PolymerElement)) {
54
+ class UploadFile extends FocusMixin(ThemableMixin(ControllerMixin(PolymerElement))) {
53
55
  static get template() {
54
56
  return html`
55
57
  <style>
@@ -71,17 +73,22 @@ class UploadFile extends FocusMixin(ThemableMixin(PolymerElement)) {
71
73
  border: none;
72
74
  box-shadow: none;
73
75
  }
76
+
77
+ :host([complete]) ::slotted([slot='progress']),
78
+ :host([error]) ::slotted([slot='progress']) {
79
+ display: none !important;
80
+ }
74
81
  </style>
75
82
 
76
83
  <div part="row">
77
84
  <div part="info">
78
- <div part="done-icon" hidden$="[[!file.complete]]" aria-hidden="true"></div>
79
- <div part="warning-icon" hidden$="[[!file.error]]" aria-hidden="true"></div>
85
+ <div part="done-icon" hidden$="[[!complete]]" aria-hidden="true"></div>
86
+ <div part="warning-icon" hidden$="[[!errorMessage]]" aria-hidden="true"></div>
80
87
 
81
88
  <div part="meta">
82
- <div part="name" id="name">[[file.name]]</div>
83
- <div part="status" hidden$="[[!file.status]]" id="status">[[file.status]]</div>
84
- <div part="error" id="error" hidden$="[[!file.error]]">[[file.error]]</div>
89
+ <div part="name" id="name">[[fileName]]</div>
90
+ <div part="status" hidden$="[[!status]]" id="status">[[status]]</div>
91
+ <div part="error" id="error" hidden$="[[!errorMessage]]">[[errorMessage]]</div>
85
92
  </div>
86
93
  </div>
87
94
  <div part="commands">
@@ -90,7 +97,7 @@ class UploadFile extends FocusMixin(ThemableMixin(PolymerElement)) {
90
97
  part="start-button"
91
98
  file-event="file-start"
92
99
  on-click="_fireFileEvent"
93
- hidden$="[[!file.held]]"
100
+ hidden$="[[!held]]"
94
101
  aria-label$="[[i18n.file.start]]"
95
102
  aria-describedby="name"
96
103
  ></button>
@@ -99,7 +106,7 @@ class UploadFile extends FocusMixin(ThemableMixin(PolymerElement)) {
99
106
  part="retry-button"
100
107
  file-event="file-retry"
101
108
  on-click="_fireFileEvent"
102
- hidden$="[[!file.error]]"
109
+ hidden$="[[!errorMessage]]"
103
110
  aria-label$="[[i18n.file.retry]]"
104
111
  aria-describedby="name"
105
112
  ></button>
@@ -114,15 +121,7 @@ class UploadFile extends FocusMixin(ThemableMixin(PolymerElement)) {
114
121
  </div>
115
122
  </div>
116
123
 
117
- <vaadin-progress-bar
118
- part="progress"
119
- id="progress"
120
- value$="[[_formatProgressValue(file.progress)]]"
121
- error$="[[file.error]]"
122
- indeterminate$="[[file.indeterminate]]"
123
- uploading$="[[file.uploading]]"
124
- complete$="[[file.complete]]"
125
- ></vaadin-progress-bar>
124
+ <slot name="progress"></slot>
126
125
  `;
127
126
  }
128
127
 
@@ -132,9 +131,75 @@ class UploadFile extends FocusMixin(ThemableMixin(PolymerElement)) {
132
131
 
133
132
  static get properties() {
134
133
  return {
135
- file: Object,
134
+ /**
135
+ * True if uploading is completed, false otherwise.
136
+ */
137
+ complete: {
138
+ type: Boolean,
139
+ value: false,
140
+ reflectToAttribute: true,
141
+ },
142
+
143
+ /**
144
+ * Error message returned by the server, if any.
145
+ */
146
+ errorMessage: {
147
+ type: String,
148
+ value: '',
149
+ observer: '_errorMessageChanged',
150
+ },
151
+
152
+ /**
153
+ * The object representing a file.
154
+ */
155
+ file: {
156
+ type: Object,
157
+ },
158
+
159
+ /**
160
+ * Name of the uploading file.
161
+ */
162
+ fileName: {
163
+ type: String,
164
+ },
165
+
166
+ /**
167
+ * True if uploading is not started, false otherwise.
168
+ */
169
+ held: {
170
+ type: Boolean,
171
+ value: false,
172
+ },
173
+
174
+ /**
175
+ * True if remaining time is unknown, false otherwise.
176
+ */
177
+ indeterminate: {
178
+ type: Boolean,
179
+ value: false,
180
+ reflectToAttribute: true,
181
+ },
182
+
183
+ /**
184
+ * The object used to localize this component.
185
+ */
186
+ i18n: {
187
+ type: Object,
188
+ },
189
+
190
+ /**
191
+ * Number representing the uploading progress.
192
+ */
193
+ progress: {
194
+ type: Number,
195
+ },
136
196
 
137
- i18n: Object,
197
+ /**
198
+ * Uploading status.
199
+ */
200
+ status: {
201
+ type: String,
202
+ },
138
203
 
139
204
  /**
140
205
  * Indicates whether the element can be focused and where it participates in sequential keyboard navigation.
@@ -145,23 +210,42 @@ class UploadFile extends FocusMixin(ThemableMixin(PolymerElement)) {
145
210
  value: 0,
146
211
  reflectToAttribute: true,
147
212
  },
213
+
214
+ /**
215
+ * True if uploading is in progress, false otherwise.
216
+ */
217
+ uploading: {
218
+ type: Boolean,
219
+ value: false,
220
+ reflectToAttribute: true,
221
+ },
222
+
223
+ /** @private */
224
+ _progress: {
225
+ type: Object,
226
+ },
148
227
  };
149
228
  }
150
229
 
151
230
  static get observers() {
152
- return [
153
- '_fileAborted(file.abort)',
154
- '_toggleHostAttribute(file.error, "error")',
155
- '_toggleHostAttribute(file.indeterminate, "indeterminate")',
156
- '_toggleHostAttribute(file.uploading, "uploading")',
157
- '_toggleHostAttribute(file.complete, "complete")',
158
- ];
231
+ return ['__updateProgress(_progress, progress, indeterminate)'];
159
232
  }
160
233
 
161
234
  /** @protected */
162
235
  ready() {
163
236
  super.ready();
164
237
 
238
+ this.addController(
239
+ new SlotController(
240
+ this,
241
+ 'progress',
242
+ () => document.createElement('vaadin-progress-bar'),
243
+ (_, progress) => {
244
+ this._progress = progress;
245
+ },
246
+ ),
247
+ );
248
+
165
249
  // Handle moving focus to the button on Tab.
166
250
  this.shadowRoot.addEventListener('focusin', (e) => {
167
251
  const target = e.composedPath()[0];
@@ -191,26 +275,16 @@ class UploadFile extends FocusMixin(ThemableMixin(PolymerElement)) {
191
275
  }
192
276
 
193
277
  /** @private */
194
- _fileAborted(abort) {
195
- if (abort) {
196
- this._remove();
197
- }
198
- }
199
-
200
- /** @private */
201
- _remove() {
202
- this.dispatchEvent(
203
- new CustomEvent('file-remove', {
204
- detail: { file: this.file },
205
- bubbles: true,
206
- composed: true,
207
- }),
208
- );
278
+ _errorMessageChanged(errorMessage) {
279
+ this.toggleAttribute('error', Boolean(errorMessage));
209
280
  }
210
281
 
211
282
  /** @private */
212
- _formatProgressValue(progress) {
213
- return progress / 100;
283
+ __updateProgress(progress, value, indeterminate) {
284
+ if (progress) {
285
+ progress.value = isNaN(value) ? 0 : value / 100;
286
+ progress.indeterminate = indeterminate;
287
+ }
214
288
  }
215
289
 
216
290
  /** @private */
@@ -225,19 +299,6 @@ class UploadFile extends FocusMixin(ThemableMixin(PolymerElement)) {
225
299
  );
226
300
  }
227
301
 
228
- /** @private */
229
- _toggleHostAttribute(value, attributeName) {
230
- const shouldHave = Boolean(value);
231
- const has = this.hasAttribute(attributeName);
232
- if (has !== shouldHave) {
233
- if (shouldHave) {
234
- this.setAttribute(attributeName, '');
235
- } else {
236
- this.removeAttribute(attributeName);
237
- }
238
- }
239
- }
240
-
241
302
  /**
242
303
  * Fired when the retry button is pressed. It is listened by `vaadin-upload`
243
304
  * which will start a new upload process of this file.
@@ -258,23 +319,12 @@ class UploadFile extends FocusMixin(ThemableMixin(PolymerElement)) {
258
319
 
259
320
  /**
260
321
  * Fired when abort button is pressed. It is listened by `vaadin-upload` which
261
- * will abort the upload in progress, but will not remove the file from the list
262
- * to allow the animation to hide the element to be run.
322
+ * will abort the upload in progress, and then remove the file from the list.
263
323
  *
264
324
  * @event file-abort
265
325
  * @param {Object} detail
266
326
  * @param {Object} detail.file file to abort upload of
267
327
  */
268
-
269
- /**
270
- * Fired after the animation to hide the element has finished. It is listened
271
- * by `vaadin-upload` which will actually remove the file from the upload
272
- * file list.
273
- *
274
- * @event file-remove
275
- * @param {Object} detail
276
- * @param {Object} detail.file file to remove from the upload of
277
- */
278
328
  }
279
329
 
280
330
  customElements.define(UploadFile.is, UploadFile);
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2016 - 2022 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
7
+ import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
8
+
9
+ /**
10
+ * An element used internally by `<vaadin-upload>`. Not intended to be used separately.
11
+ *
12
+ * @extends HTMLElement
13
+ * @private
14
+ */
15
+ class UploadIcon extends ThemableMixin(PolymerElement) {
16
+ static get is() {
17
+ return 'vaadin-upload-icon';
18
+ }
19
+
20
+ static get template() {
21
+ return html`
22
+ <style>
23
+ :host {
24
+ display: inline-block;
25
+ }
26
+
27
+ :host([hidden]) {
28
+ display: none !important;
29
+ }
30
+ </style>
31
+ `;
32
+ }
33
+ }
34
+
35
+ customElements.define(UploadIcon.is, UploadIcon);
36
+
37
+ export { UploadIcon };
@@ -3,6 +3,7 @@
3
3
  * Copyright (c) 2016 - 2022 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
+ import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
6
7
  import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
7
8
  import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
8
9
 
@@ -180,13 +181,10 @@ export interface UploadEventMap extends HTMLElementEventMap, UploadCustomEventMa
180
181
  *
181
182
  * The following shadow DOM parts are available for styling:
182
183
  *
183
- * Part name | Description
184
- * ---|---
185
- * `primary-buttons` | Upload container
186
- * `upload-button` | Upload button
187
- * `drop-label` | Label for drop indicator
188
- * `drop-label-icon` | Icon for drop indicator
189
- * `file-list` | File list container
184
+ * Part name | Description
185
+ * -------------------|-------------------------------------
186
+ * `primary-buttons` | Upload container
187
+ * `drop-label` | Element wrapping drop label and icon
190
188
  *
191
189
  * The following state attributes are available for styling:
192
190
  *
@@ -212,7 +210,7 @@ export interface UploadEventMap extends HTMLElementEventMap, UploadCustomEventMa
212
210
  * @fires {CustomEvent} upload-retry - Fired when retry upload is requested.
213
211
  * @fires {CustomEvent} upload-abort - Fired when upload abort is requested.
214
212
  */
215
- declare class Upload extends ThemableMixin(ElementMixin(HTMLElement)) {
213
+ declare class Upload extends ThemableMixin(ElementMixin(ControllerMixin(HTMLElement))) {
216
214
  /**
217
215
  * Define whether the element supports dropping files on it for uploading.
218
216
  * By default it's enabled in desktop and disabled in touch devices
@@ -5,12 +5,15 @@
5
5
  */
6
6
  import '@polymer/polymer/lib/elements/dom-repeat.js';
7
7
  import '@vaadin/button/src/vaadin-button.js';
8
+ import './vaadin-upload-icon.js';
8
9
  import './vaadin-upload-icons.js';
9
- import './vaadin-upload-file.js';
10
+ import './vaadin-upload-file-list.js';
10
11
  import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
11
12
  import { announce } from '@vaadin/component-base/src/a11y-announcer.js';
12
13
  import { isTouch } from '@vaadin/component-base/src/browser-utils.js';
14
+ import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
13
15
  import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
16
+ import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
14
17
  import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
15
18
 
16
19
  /**
@@ -26,13 +29,10 @@ import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mix
26
29
  *
27
30
  * The following shadow DOM parts are available for styling:
28
31
  *
29
- * Part name | Description
30
- * ---|---
31
- * `primary-buttons` | Upload container
32
- * `upload-button` | Upload button
33
- * `drop-label` | Label for drop indicator
34
- * `drop-label-icon` | Icon for drop indicator
35
- * `file-list` | File list container
32
+ * Part name | Description
33
+ * -------------------|-------------------------------------
34
+ * `primary-buttons` | Upload container
35
+ * `drop-label` | Element wrapping drop label and icon
36
36
  *
37
37
  * The following state attributes are available for styling:
38
38
  *
@@ -59,10 +59,11 @@ import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mix
59
59
  * @fires {CustomEvent} upload-abort - Fired when upload abort is requested.
60
60
  *
61
61
  * @extends HTMLElement
62
+ * @mixes ControllerMixin
62
63
  * @mixes ThemableMixin
63
64
  * @mixes ElementMixin
64
65
  */
65
- class Upload extends ElementMixin(ThemableMixin(PolymerElement)) {
66
+ class Upload extends ElementMixin(ThemableMixin(ControllerMixin(PolymerElement))) {
66
67
  static get template() {
67
68
  return html`
68
69
  <style>
@@ -79,38 +80,16 @@ class Upload extends ElementMixin(ThemableMixin(PolymerElement)) {
79
80
  [hidden] {
80
81
  display: none !important;
81
82
  }
82
-
83
- [part='file-list'] {
84
- padding: 0;
85
- margin: 0;
86
- list-style-type: none;
87
- }
88
83
  </style>
89
84
 
90
85
  <div part="primary-buttons">
91
- <div id="addFiles" on-touchend="_onAddFilesTouchEnd" on-click="_onAddFilesClick">
92
- <slot name="add-button">
93
- <vaadin-button part="upload-button" id="addButton" disabled="[[maxFilesReached]]">
94
- [[_i18nPlural(maxFiles, i18n.addFiles, i18n.addFiles.*)]]
95
- </vaadin-button>
96
- </slot>
97
- </div>
86
+ <slot name="add-button"></slot>
98
87
  <div part="drop-label" hidden$="[[nodrop]]" id="dropLabelContainer" aria-hidden="true">
99
- <slot name="drop-label-icon">
100
- <div part="drop-label-icon"></div>
101
- </slot>
102
- <slot name="drop-label" id="dropLabel"> [[_i18nPlural(maxFiles, i18n.dropFiles, i18n.dropFiles.*)]]</slot>
88
+ <slot name="drop-label-icon"></slot>
89
+ <slot name="drop-label"></slot>
103
90
  </div>
104
91
  </div>
105
- <slot name="file-list">
106
- <ul id="fileList" part="file-list">
107
- <template is="dom-repeat" items="[[files]]" as="file">
108
- <li>
109
- <vaadin-upload-file file="[[file]]" i18n="[[i18n]]"></vaadin-upload-file>
110
- </li>
111
- </template>
112
- </ul>
113
- </slot>
92
+ <slot name="file-list"></slot>
114
93
  <slot></slot>
115
94
  <input
116
95
  type="file"
@@ -429,9 +408,37 @@ class Upload extends ElementMixin(ThemableMixin(PolymerElement)) {
429
408
  };
430
409
  },
431
410
  },
411
+
412
+ /** @private */
413
+ _addButton: {
414
+ type: Object,
415
+ },
416
+
417
+ /** @private */
418
+ _dropLabel: {
419
+ type: Object,
420
+ },
421
+
422
+ /** @private */
423
+ _fileList: {
424
+ type: Object,
425
+ },
426
+
427
+ /** @private */
428
+ _files: {
429
+ type: Array,
430
+ },
432
431
  };
433
432
  }
434
433
 
434
+ static get observers() {
435
+ return [
436
+ '__updateAddButton(_addButton, maxFiles, i18n, maxFilesReached)',
437
+ '__updateDropLabel(_dropLabel, maxFiles, i18n)',
438
+ '__updateFileList(_fileList, files, i18n)',
439
+ ];
440
+ }
441
+
435
442
  /** @protected */
436
443
  ready() {
437
444
  super.ready();
@@ -440,12 +447,52 @@ class Upload extends ElementMixin(ThemableMixin(PolymerElement)) {
440
447
  this.addEventListener('drop', this._onDrop.bind(this));
441
448
  this.addEventListener('file-retry', this._onFileRetry.bind(this));
442
449
  this.addEventListener('file-abort', this._onFileAbort.bind(this));
443
- this.addEventListener('file-remove', this._onFileRemove.bind(this));
444
450
  this.addEventListener('file-start', this._onFileStart.bind(this));
445
451
  this.addEventListener('file-reject', this._onFileReject.bind(this));
446
452
  this.addEventListener('upload-start', this._onUploadStart.bind(this));
447
453
  this.addEventListener('upload-success', this._onUploadSuccess.bind(this));
448
454
  this.addEventListener('upload-error', this._onUploadError.bind(this));
455
+
456
+ this.addController(
457
+ new SlotController(
458
+ this,
459
+ 'add-button',
460
+ () => document.createElement('vaadin-button'),
461
+ (_, button) => {
462
+ button.addEventListener('touchend', (e) => {
463
+ this._onAddFilesTouchEnd(e);
464
+ });
465
+ button.addEventListener('click', (e) => {
466
+ this._onAddFilesClick(e);
467
+ });
468
+ this._addButton = button;
469
+ },
470
+ ),
471
+ );
472
+
473
+ this.addController(
474
+ new SlotController(
475
+ this,
476
+ 'drop-label',
477
+ () => document.createElement('span'),
478
+ (_, label) => {
479
+ this._dropLabel = label;
480
+ },
481
+ ),
482
+ );
483
+
484
+ this.addController(
485
+ new SlotController(
486
+ this,
487
+ 'file-list',
488
+ () => document.createElement('vaadin-upload-file-list'),
489
+ (_, list) => {
490
+ this._fileList = list;
491
+ },
492
+ ),
493
+ );
494
+
495
+ this.addController(new SlotController(this, 'drop-label-icon', () => document.createElement('vaadin-upload-icon')));
449
496
  }
450
497
 
451
498
  /** @private */
@@ -509,6 +556,29 @@ class Upload extends ElementMixin(ThemableMixin(PolymerElement)) {
509
556
  return maxFiles >= 0 && numFiles >= maxFiles;
510
557
  }
511
558
 
559
+ /** @private */
560
+ __updateAddButton(addButton, maxFiles, i18n, maxFilesReached) {
561
+ if (addButton) {
562
+ addButton.disabled = maxFilesReached;
563
+ addButton.textContent = this._i18nPlural(maxFiles, i18n.addFiles);
564
+ }
565
+ }
566
+
567
+ /** @private */
568
+ __updateDropLabel(dropLabel, maxFiles, i18n) {
569
+ if (dropLabel) {
570
+ dropLabel.textContent = this._i18nPlural(maxFiles, i18n.dropFiles);
571
+ }
572
+ }
573
+
574
+ /** @private */
575
+ __updateFileList(list, files, i18n) {
576
+ if (list) {
577
+ list.items = [...files];
578
+ list.i18n = i18n;
579
+ }
580
+ }
581
+
512
582
  /** @private */
513
583
  _onDragover(event) {
514
584
  event.preventDefault();
@@ -615,7 +685,7 @@ class Upload extends ElementMixin(ThemableMixin(PolymerElement)) {
615
685
  this._setStatus(file, total, loaded, elapsed);
616
686
  stalledId = setTimeout(() => {
617
687
  file.status = this.i18n.uploading.status.stalled;
618
- this._notifyFileChanges(file);
688
+ this._renderFileList();
619
689
  }, 2000);
620
690
  } else {
621
691
  file.loadedStr = file.totalStr;
@@ -623,7 +693,7 @@ class Upload extends ElementMixin(ThemableMixin(PolymerElement)) {
623
693
  }
624
694
  }
625
695
 
626
- this._notifyFileChanges(file);
696
+ this._renderFileList();
627
697
  this.dispatchEvent(new CustomEvent('upload-progress', { detail: { file, xhr } }));
628
698
  };
629
699
 
@@ -633,7 +703,6 @@ class Upload extends ElementMixin(ThemableMixin(PolymerElement)) {
633
703
  clearTimeout(stalledId);
634
704
  file.indeterminate = file.uploading = false;
635
705
  if (file.abort) {
636
- this._notifyFileChanges(file);
637
706
  return;
638
707
  }
639
708
  file.status = '';
@@ -663,7 +732,7 @@ class Upload extends ElementMixin(ThemableMixin(PolymerElement)) {
663
732
  detail: { file, xhr },
664
733
  }),
665
734
  );
666
- this._notifyFileChanges(file);
735
+ this._renderFileList();
667
736
  }
668
737
  };
669
738
 
@@ -697,7 +766,7 @@ class Upload extends ElementMixin(ThemableMixin(PolymerElement)) {
697
766
  detail: { file, xhr },
698
767
  }),
699
768
  );
700
- this._notifyFileChanges(file);
769
+ this._renderFileList();
701
770
  };
702
771
 
703
772
  // Custom listener could modify the xhr just before sending it
@@ -739,16 +808,15 @@ class Upload extends ElementMixin(ThemableMixin(PolymerElement)) {
739
808
  if (file.xhr) {
740
809
  file.xhr.abort();
741
810
  }
742
- this._notifyFileChanges(file);
811
+ this._removeFile(file);
743
812
  }
744
813
  }
745
814
 
746
815
  /** @private */
747
- _notifyFileChanges(file) {
748
- const p = `files.${this.files.indexOf(file)}.`;
749
- Object.keys(file).forEach((i) => {
750
- this.notifyPath(p + i, file[i]);
751
- });
816
+ _renderFileList() {
817
+ if (this._fileList) {
818
+ this._fileList.requestContentUpdate();
819
+ }
752
820
  }
753
821
 
754
822
  /** @private */
@@ -810,6 +878,14 @@ class Upload extends ElementMixin(ThemableMixin(PolymerElement)) {
810
878
  _removeFile(file) {
811
879
  if (this.files.indexOf(file) > -1) {
812
880
  this.files = this.files.filter((i) => i !== file);
881
+
882
+ this.dispatchEvent(
883
+ new CustomEvent('file-remove', {
884
+ detail: { file },
885
+ bubbles: true,
886
+ composed: true,
887
+ }),
888
+ );
813
889
  }
814
890
  }
815
891
 
@@ -851,11 +927,6 @@ class Upload extends ElementMixin(ThemableMixin(PolymerElement)) {
851
927
  this._abortFileUpload(event.detail.file);
852
928
  }
853
929
 
854
- /** @private */
855
- _onFileRemove(event) {
856
- this._removeFile(event.detail.file);
857
- }
858
-
859
930
  /** @private */
860
931
  _onFileReject(event) {
861
932
  announce(`${event.detail.file.name}: ${event.detail.file.error}`, { mode: 'alert' });
@@ -24,11 +24,6 @@ registerStyles(
24
24
  transition: background-color 0.6s, border-color 0.6s;
25
25
  }
26
26
 
27
- [part='primary-buttons'] > * {
28
- display: inline-block;
29
- white-space: nowrap;
30
- }
31
-
32
27
  [part='drop-label'] {
33
28
  display: inline-block;
34
29
  white-space: normal;
@@ -50,24 +45,32 @@ registerStyles(
50
45
  :host([max-files-reached]) [part='drop-label'] {
51
46
  color: var(--lumo-disabled-text-color);
52
47
  }
48
+ `,
49
+ { moduleId: 'lumo-upload' },
50
+ );
53
51
 
54
- [part='drop-label-icon'] {
55
- display: inline-block;
56
- }
57
-
58
- [part='drop-label-icon']::before {
52
+ registerStyles(
53
+ 'vaadin-upload-icon',
54
+ css`
55
+ :host::before {
59
56
  content: var(--lumo-icons-upload);
60
57
  font-family: lumo-icons;
61
58
  font-size: var(--lumo-icon-size-m);
62
59
  line-height: 1;
63
60
  vertical-align: -0.25em;
64
61
  }
62
+ `,
63
+ { moduleId: 'lumo-upload-icon' },
64
+ );
65
65
 
66
- [part='file-list'] > *:not(:first-child) > * {
66
+ registerStyles(
67
+ 'vaadin-upload-file-list',
68
+ css`
69
+ ::slotted(li:not(:first-of-type)) {
67
70
  border-top: 1px solid var(--lumo-contrast-10pct);
68
71
  }
69
72
  `,
70
- { moduleId: 'lumo-upload' },
73
+ { moduleId: 'lumo-upload-file-list' },
71
74
  );
72
75
 
73
76
  const uploadFile = css`
@@ -172,16 +175,11 @@ const uploadFile = css`
172
175
  color: var(--lumo-error-text-color);
173
176
  }
174
177
 
175
- [part='progress'] {
178
+ ::slotted([slot='progress']) {
176
179
  width: auto;
177
180
  margin-left: calc(var(--lumo-icon-size-m) + var(--lumo-space-xs));
178
181
  margin-right: calc(var(--lumo-icon-size-m) + var(--lumo-space-xs));
179
182
  }
180
-
181
- [part='progress'][complete],
182
- [part='progress'][error] {
183
- display: none;
184
- }
185
183
  `;
186
184
 
187
185
  registerStyles('vaadin-upload-file', [fieldButton, uploadFile], { moduleId: 'lumo-upload-file' });
@@ -23,14 +23,7 @@ registerStyles(
23
23
  align-items: baseline;
24
24
  }
25
25
 
26
- /* TODO(jouni): unsupported selector (not sure why there's #addFiles element wrapping the upload button) */
27
- [part='primary-buttons'] > * {
28
- display: block;
29
- flex-grow: 1;
30
- }
31
-
32
- [part='upload-button'] {
33
- display: block;
26
+ ::slotted([slot='add-button']) {
34
27
  margin: 0 -8px;
35
28
  }
36
29
 
@@ -56,18 +49,6 @@ registerStyles(
56
49
  color: var(--material-disabled-text-color);
57
50
  }
58
51
 
59
- [part='drop-label-icon'] {
60
- display: inline-block;
61
- margin-right: 8px;
62
- }
63
-
64
- [part='drop-label-icon']::before {
65
- content: var(--material-icons-upload);
66
- font-family: material-icons;
67
- font-size: var(--material-icon-font-size);
68
- line-height: 1;
69
- }
70
-
71
52
  /* Ripple */
72
53
 
73
54
  :host::before {
@@ -96,6 +77,23 @@ registerStyles(
96
77
  { moduleId: 'material-upload' },
97
78
  );
98
79
 
80
+ registerStyles(
81
+ 'vaadin-upload-icon',
82
+ css`
83
+ :host {
84
+ margin-right: 8px;
85
+ }
86
+
87
+ :host::before {
88
+ content: var(--material-icons-upload);
89
+ font-family: material-icons;
90
+ font-size: var(--material-icon-font-size);
91
+ line-height: 1;
92
+ }
93
+ `,
94
+ { moduleId: 'material-upload-icon' },
95
+ );
96
+
99
97
  registerStyles(
100
98
  'vaadin-upload-file',
101
99
  css`
@@ -237,15 +235,10 @@ registerStyles(
237
235
  color: var(--material-error-text-color);
238
236
  }
239
237
 
240
- [part='progress'] {
238
+ ::slotted([slot='progress']) {
241
239
  width: auto;
242
240
  margin-left: 28px;
243
241
  }
244
-
245
- [part='progress'][complete],
246
- [part='progress'][error] {
247
- display: none;
248
- }
249
242
  `,
250
243
  { moduleId: 'material-upload-file' },
251
244
  );
package/web-types.json CHANGED
@@ -1,15 +1,103 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@vaadin/upload",
4
- "version": "23.3.0-alpha5",
4
+ "version": "24.0.0-alpha2",
5
5
  "description-markup": "markdown",
6
6
  "contributions": {
7
7
  "html": {
8
8
  "elements": [
9
9
  {
10
10
  "name": "vaadin-upload-file",
11
- "description": "`<vaadin-upload-file>` element represents a file in the file list of `<vaadin-upload>`.\n\n### Styling\n\nThe following shadow DOM parts are available for styling:\n\nPart name | Description\n-----------------|-------------\n`row` | File container\n`info` | Container for file status icon, file name, status and error messages\n`done-icon` | File done status icon\n`warning-icon` | File warning status icon\n`meta` | Container for file name, status and error messages\n`name` | File name\n`error` | Error message, shown when error happens\n`status` | Status message\n`commands` | Container for file command buttons\n`start-button` | Start file upload button\n`retry-button` | Retry file upload button\n`remove-button` | Remove file button\n`progress` | Progress bar\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n-----------------|-------------\n`focus-ring` | Set when the element is focused using the keyboard.\n`focused` | Set when the element is focused.\n`error` | An error has happened during uploading.\n`indeterminate` | Uploading is in progress, but the progress value is unknown.\n`uploading` | Uploading is in progress.\n`complete` | Uploading has finished successfully.\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.",
11
+ "description": "`<vaadin-upload-file>` element represents a file in the file list of `<vaadin-upload>`.\n\n### Styling\n\nThe following shadow DOM parts are available for styling:\n\nPart name | Description\n-----------------|-------------\n`row` | File container\n`info` | Container for file status icon, file name, status and error messages\n`done-icon` | File done status icon\n`warning-icon` | File warning status icon\n`meta` | Container for file name, status and error messages\n`name` | File name\n`error` | Error message, shown when error happens\n`status` | Status message\n`commands` | Container for file command buttons\n`start-button` | Start file upload button\n`retry-button` | Retry file upload button\n`remove-button` | Remove file button\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n-----------------|-------------\n`focus-ring` | Set when the element is focused using the keyboard.\n`focused` | Set when the element is focused.\n`error` | An error has happened during uploading.\n`indeterminate` | Uploading is in progress, but the progress value is unknown.\n`uploading` | Uploading is in progress.\n`complete` | Uploading has finished successfully.\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.",
12
12
  "attributes": [
13
+ {
14
+ "name": "complete",
15
+ "description": "True if uploading is completed, false otherwise.",
16
+ "value": {
17
+ "type": [
18
+ "boolean",
19
+ "null",
20
+ "undefined"
21
+ ]
22
+ }
23
+ },
24
+ {
25
+ "name": "error-message",
26
+ "description": "Error message returned by the server, if any.",
27
+ "value": {
28
+ "type": [
29
+ "string",
30
+ "null",
31
+ "undefined"
32
+ ]
33
+ }
34
+ },
35
+ {
36
+ "name": "file-name",
37
+ "description": "Name of the uploading file.",
38
+ "value": {
39
+ "type": [
40
+ "string",
41
+ "null",
42
+ "undefined"
43
+ ]
44
+ }
45
+ },
46
+ {
47
+ "name": "held",
48
+ "description": "True if uploading is not started, false otherwise.",
49
+ "value": {
50
+ "type": [
51
+ "boolean",
52
+ "null",
53
+ "undefined"
54
+ ]
55
+ }
56
+ },
57
+ {
58
+ "name": "indeterminate",
59
+ "description": "True if remaining time is unknown, false otherwise.",
60
+ "value": {
61
+ "type": [
62
+ "boolean",
63
+ "null",
64
+ "undefined"
65
+ ]
66
+ }
67
+ },
68
+ {
69
+ "name": "progress",
70
+ "description": "Number representing the uploading progress.",
71
+ "value": {
72
+ "type": [
73
+ "number",
74
+ "null",
75
+ "undefined"
76
+ ]
77
+ }
78
+ },
79
+ {
80
+ "name": "status",
81
+ "description": "Uploading status.",
82
+ "value": {
83
+ "type": [
84
+ "string",
85
+ "null",
86
+ "undefined"
87
+ ]
88
+ }
89
+ },
90
+ {
91
+ "name": "uploading",
92
+ "description": "True if uploading is in progress, false otherwise.",
93
+ "value": {
94
+ "type": [
95
+ "boolean",
96
+ "null",
97
+ "undefined"
98
+ ]
99
+ }
100
+ },
13
101
  {
14
102
  "name": "theme",
15
103
  "description": "The theme variants to apply to the component.",
@@ -24,9 +112,31 @@
24
112
  ],
25
113
  "js": {
26
114
  "properties": [
115
+ {
116
+ "name": "complete",
117
+ "description": "True if uploading is completed, false otherwise.",
118
+ "value": {
119
+ "type": [
120
+ "boolean",
121
+ "null",
122
+ "undefined"
123
+ ]
124
+ }
125
+ },
126
+ {
127
+ "name": "errorMessage",
128
+ "description": "Error message returned by the server, if any.",
129
+ "value": {
130
+ "type": [
131
+ "string",
132
+ "null",
133
+ "undefined"
134
+ ]
135
+ }
136
+ },
27
137
  {
28
138
  "name": "file",
29
- "description": "",
139
+ "description": "The object representing a file.",
30
140
  "value": {
31
141
  "type": [
32
142
  "Object",
@@ -35,9 +145,42 @@
35
145
  ]
36
146
  }
37
147
  },
148
+ {
149
+ "name": "fileName",
150
+ "description": "Name of the uploading file.",
151
+ "value": {
152
+ "type": [
153
+ "string",
154
+ "null",
155
+ "undefined"
156
+ ]
157
+ }
158
+ },
159
+ {
160
+ "name": "held",
161
+ "description": "True if uploading is not started, false otherwise.",
162
+ "value": {
163
+ "type": [
164
+ "boolean",
165
+ "null",
166
+ "undefined"
167
+ ]
168
+ }
169
+ },
170
+ {
171
+ "name": "indeterminate",
172
+ "description": "True if remaining time is unknown, false otherwise.",
173
+ "value": {
174
+ "type": [
175
+ "boolean",
176
+ "null",
177
+ "undefined"
178
+ ]
179
+ }
180
+ },
38
181
  {
39
182
  "name": "i18n",
40
- "description": "",
183
+ "description": "The object used to localize this component.",
41
184
  "value": {
42
185
  "type": [
43
186
  "Object",
@@ -45,16 +188,45 @@
45
188
  "undefined"
46
189
  ]
47
190
  }
191
+ },
192
+ {
193
+ "name": "progress",
194
+ "description": "Number representing the uploading progress.",
195
+ "value": {
196
+ "type": [
197
+ "number",
198
+ "null",
199
+ "undefined"
200
+ ]
201
+ }
202
+ },
203
+ {
204
+ "name": "status",
205
+ "description": "Uploading status.",
206
+ "value": {
207
+ "type": [
208
+ "string",
209
+ "null",
210
+ "undefined"
211
+ ]
212
+ }
213
+ },
214
+ {
215
+ "name": "uploading",
216
+ "description": "True if uploading is in progress, false otherwise.",
217
+ "value": {
218
+ "type": [
219
+ "boolean",
220
+ "null",
221
+ "undefined"
222
+ ]
223
+ }
48
224
  }
49
225
  ],
50
226
  "events": [
51
227
  {
52
228
  "name": "file-abort",
53
- "description": "Fired when abort button is pressed. It is listened by `vaadin-upload` which\nwill abort the upload in progress, but will not remove the file from the list\nto allow the animation to hide the element to be run."
54
- },
55
- {
56
- "name": "file-remove",
57
- "description": "Fired after the animation to hide the element has finished. It is listened\nby `vaadin-upload` which will actually remove the file from the upload\nfile list."
229
+ "description": "Fired when abort button is pressed. It is listened by `vaadin-upload` which\nwill abort the upload in progress, and then remove the file from the list."
58
230
  },
59
231
  {
60
232
  "name": "file-retry",
@@ -69,7 +241,7 @@
69
241
  },
70
242
  {
71
243
  "name": "vaadin-upload",
72
- "description": "`<vaadin-upload>` is a Web Component for uploading multiple files with drag and drop support.\n\nExample:\n\n```\n<vaadin-upload></vaadin-upload>\n```\n\n### Styling\n\nThe following shadow DOM parts are available for styling:\n\nPart name | Description\n---|---\n`primary-buttons` | Upload container\n`upload-button` | Upload button\n`drop-label` | Label for drop indicator\n`drop-label-icon` | Icon for drop indicator\n`file-list` | File list container\n\nThe following state attributes are available for styling:\n\nAttribute | Description | Part name\n---|---|---\n`nodrop` | Set when drag and drop is disabled (e. g., on touch devices) | `:host`\n`dragover` | A file is being dragged over the element | `:host`\n`dragover-valid` | A dragged file is valid with `maxFiles` and `accept` criteria | `:host`\n`max-files-reached` | The maximum number of files that the user is allowed to add to the upload has been reached | `:host`\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.",
244
+ "description": "`<vaadin-upload>` is a Web Component for uploading multiple files with drag and drop support.\n\nExample:\n\n```\n<vaadin-upload></vaadin-upload>\n```\n\n### Styling\n\nThe following shadow DOM parts are available for styling:\n\nPart name | Description\n-------------------|-------------------------------------\n`primary-buttons` | Upload container\n`drop-label` | Element wrapping drop label and icon\n\nThe following state attributes are available for styling:\n\nAttribute | Description | Part name\n---|---|---\n`nodrop` | Set when drag and drop is disabled (e. g., on touch devices) | `:host`\n`dragover` | A file is being dragged over the element | `:host`\n`dragover-valid` | A dragged file is valid with `maxFiles` and `accept` criteria | `:host`\n`max-files-reached` | The maximum number of files that the user is allowed to add to the upload has been reached | `:host`\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.",
73
245
  "attributes": [
74
246
  {
75
247
  "name": "nodrop",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@vaadin/upload",
4
- "version": "23.3.0-alpha5",
4
+ "version": "24.0.0-alpha2",
5
5
  "description-markup": "markdown",
6
6
  "framework": "lit",
7
7
  "framework-config": {
@@ -16,33 +16,82 @@
16
16
  "elements": [
17
17
  {
18
18
  "name": "vaadin-upload-file",
19
- "description": "`<vaadin-upload-file>` element represents a file in the file list of `<vaadin-upload>`.\n\n### Styling\n\nThe following shadow DOM parts are available for styling:\n\nPart name | Description\n-----------------|-------------\n`row` | File container\n`info` | Container for file status icon, file name, status and error messages\n`done-icon` | File done status icon\n`warning-icon` | File warning status icon\n`meta` | Container for file name, status and error messages\n`name` | File name\n`error` | Error message, shown when error happens\n`status` | Status message\n`commands` | Container for file command buttons\n`start-button` | Start file upload button\n`retry-button` | Retry file upload button\n`remove-button` | Remove file button\n`progress` | Progress bar\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n-----------------|-------------\n`focus-ring` | Set when the element is focused using the keyboard.\n`focused` | Set when the element is focused.\n`error` | An error has happened during uploading.\n`indeterminate` | Uploading is in progress, but the progress value is unknown.\n`uploading` | Uploading is in progress.\n`complete` | Uploading has finished successfully.\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.",
19
+ "description": "`<vaadin-upload-file>` element represents a file in the file list of `<vaadin-upload>`.\n\n### Styling\n\nThe following shadow DOM parts are available for styling:\n\nPart name | Description\n-----------------|-------------\n`row` | File container\n`info` | Container for file status icon, file name, status and error messages\n`done-icon` | File done status icon\n`warning-icon` | File warning status icon\n`meta` | Container for file name, status and error messages\n`name` | File name\n`error` | Error message, shown when error happens\n`status` | Status message\n`commands` | Container for file command buttons\n`start-button` | Start file upload button\n`retry-button` | Retry file upload button\n`remove-button` | Remove file button\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n-----------------|-------------\n`focus-ring` | Set when the element is focused using the keyboard.\n`focused` | Set when the element is focused.\n`error` | An error has happened during uploading.\n`indeterminate` | Uploading is in progress, but the progress value is unknown.\n`uploading` | Uploading is in progress.\n`complete` | Uploading has finished successfully.\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.",
20
20
  "extension": true,
21
21
  "attributes": [
22
+ {
23
+ "name": "?complete",
24
+ "description": "True if uploading is completed, false otherwise.",
25
+ "value": {
26
+ "kind": "expression"
27
+ }
28
+ },
29
+ {
30
+ "name": "?held",
31
+ "description": "True if uploading is not started, false otherwise.",
32
+ "value": {
33
+ "kind": "expression"
34
+ }
35
+ },
36
+ {
37
+ "name": "?indeterminate",
38
+ "description": "True if remaining time is unknown, false otherwise.",
39
+ "value": {
40
+ "kind": "expression"
41
+ }
42
+ },
43
+ {
44
+ "name": "?uploading",
45
+ "description": "True if uploading is in progress, false otherwise.",
46
+ "value": {
47
+ "kind": "expression"
48
+ }
49
+ },
50
+ {
51
+ "name": ".errorMessage",
52
+ "description": "Error message returned by the server, if any.",
53
+ "value": {
54
+ "kind": "expression"
55
+ }
56
+ },
22
57
  {
23
58
  "name": ".file",
24
- "description": "",
59
+ "description": "The object representing a file.",
60
+ "value": {
61
+ "kind": "expression"
62
+ }
63
+ },
64
+ {
65
+ "name": ".fileName",
66
+ "description": "Name of the uploading file.",
25
67
  "value": {
26
68
  "kind": "expression"
27
69
  }
28
70
  },
29
71
  {
30
72
  "name": ".i18n",
31
- "description": "",
73
+ "description": "The object used to localize this component.",
32
74
  "value": {
33
75
  "kind": "expression"
34
76
  }
35
77
  },
36
78
  {
37
- "name": "@file-abort",
38
- "description": "Fired when abort button is pressed. It is listened by `vaadin-upload` which\nwill abort the upload in progress, but will not remove the file from the list\nto allow the animation to hide the element to be run.",
79
+ "name": ".progress",
80
+ "description": "Number representing the uploading progress.",
39
81
  "value": {
40
82
  "kind": "expression"
41
83
  }
42
84
  },
43
85
  {
44
- "name": "@file-remove",
45
- "description": "Fired after the animation to hide the element has finished. It is listened\nby `vaadin-upload` which will actually remove the file from the upload\nfile list.",
86
+ "name": ".status",
87
+ "description": "Uploading status.",
88
+ "value": {
89
+ "kind": "expression"
90
+ }
91
+ },
92
+ {
93
+ "name": "@file-abort",
94
+ "description": "Fired when abort button is pressed. It is listened by `vaadin-upload` which\nwill abort the upload in progress, and then remove the file from the list.",
46
95
  "value": {
47
96
  "kind": "expression"
48
97
  }
@@ -65,7 +114,7 @@
65
114
  },
66
115
  {
67
116
  "name": "vaadin-upload",
68
- "description": "`<vaadin-upload>` is a Web Component for uploading multiple files with drag and drop support.\n\nExample:\n\n```\n<vaadin-upload></vaadin-upload>\n```\n\n### Styling\n\nThe following shadow DOM parts are available for styling:\n\nPart name | Description\n---|---\n`primary-buttons` | Upload container\n`upload-button` | Upload button\n`drop-label` | Label for drop indicator\n`drop-label-icon` | Icon for drop indicator\n`file-list` | File list container\n\nThe following state attributes are available for styling:\n\nAttribute | Description | Part name\n---|---|---\n`nodrop` | Set when drag and drop is disabled (e. g., on touch devices) | `:host`\n`dragover` | A file is being dragged over the element | `:host`\n`dragover-valid` | A dragged file is valid with `maxFiles` and `accept` criteria | `:host`\n`max-files-reached` | The maximum number of files that the user is allowed to add to the upload has been reached | `:host`\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.",
117
+ "description": "`<vaadin-upload>` is a Web Component for uploading multiple files with drag and drop support.\n\nExample:\n\n```\n<vaadin-upload></vaadin-upload>\n```\n\n### Styling\n\nThe following shadow DOM parts are available for styling:\n\nPart name | Description\n-------------------|-------------------------------------\n`primary-buttons` | Upload container\n`drop-label` | Element wrapping drop label and icon\n\nThe following state attributes are available for styling:\n\nAttribute | Description | Part name\n---|---|---\n`nodrop` | Set when drag and drop is disabled (e. g., on touch devices) | `:host`\n`dragover` | A file is being dragged over the element | `:host`\n`dragover-valid` | A dragged file is valid with `maxFiles` and `accept` criteria | `:host`\n`max-files-reached` | The maximum number of files that the user is allowed to add to the upload has been reached | `:host`\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.",
69
118
  "extension": true,
70
119
  "attributes": [
71
120
  {