@vaadin/upload 24.0.0-alpha1 → 24.0.0-alpha3

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": "24.0.0-alpha1",
3
+ "version": "24.0.0-alpha3",
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": "24.0.0-alpha1",
40
- "@vaadin/component-base": "24.0.0-alpha1",
41
- "@vaadin/progress-bar": "24.0.0-alpha1",
42
- "@vaadin/vaadin-lumo-styles": "24.0.0-alpha1",
43
- "@vaadin/vaadin-material-styles": "24.0.0-alpha1",
44
- "@vaadin/vaadin-themable-mixin": "24.0.0-alpha1"
39
+ "@vaadin/button": "24.0.0-alpha3",
40
+ "@vaadin/component-base": "24.0.0-alpha3",
41
+ "@vaadin/progress-bar": "24.0.0-alpha3",
42
+ "@vaadin/vaadin-lumo-styles": "24.0.0-alpha3",
43
+ "@vaadin/vaadin-material-styles": "24.0.0-alpha3",
44
+ "@vaadin/vaadin-themable-mixin": "24.0.0-alpha3"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@esm-bundle/chai": "^4.3.4",
48
- "@vaadin/form-layout": "24.0.0-alpha1",
48
+ "@vaadin/form-layout": "24.0.0-alpha3",
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": "427527c27c4b27822d61fd41d38d7b170134770b"
56
+ "gitHead": "7a013a3c5a56abd61dd4f7773c6ec77c3541bdf2"
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
+ },
136
165
 
137
- i18n: Object,
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
+ },
196
+
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,39 @@ 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(this, 'progress', 'vaadin-progress-bar', {
240
+ initializer: (progress) => {
241
+ this._progress = progress;
242
+ },
243
+ }),
244
+ );
245
+
165
246
  // Handle moving focus to the button on Tab.
166
247
  this.shadowRoot.addEventListener('focusin', (e) => {
167
248
  const target = e.composedPath()[0];
@@ -191,26 +272,16 @@ class UploadFile extends FocusMixin(ThemableMixin(PolymerElement)) {
191
272
  }
192
273
 
193
274
  /** @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
- );
275
+ _errorMessageChanged(errorMessage) {
276
+ this.toggleAttribute('error', Boolean(errorMessage));
209
277
  }
210
278
 
211
279
  /** @private */
212
- _formatProgressValue(progress) {
213
- return progress / 100;
280
+ __updateProgress(progress, value, indeterminate) {
281
+ if (progress) {
282
+ progress.value = isNaN(value) ? 0 : value / 100;
283
+ progress.indeterminate = indeterminate;
284
+ }
214
285
  }
215
286
 
216
287
  /** @private */
@@ -225,19 +296,6 @@ class UploadFile extends FocusMixin(ThemableMixin(PolymerElement)) {
225
296
  );
226
297
  }
227
298
 
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
299
  /**
242
300
  * Fired when the retry button is pressed. It is listened by `vaadin-upload`
243
301
  * which will start a new upload process of this file.
@@ -258,23 +316,12 @@ class UploadFile extends FocusMixin(ThemableMixin(PolymerElement)) {
258
316
 
259
317
  /**
260
318
  * 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.
319
+ * will abort the upload in progress, and then remove the file from the list.
263
320
  *
264
321
  * @event file-abort
265
322
  * @param {Object} detail
266
323
  * @param {Object} detail.file file to abort upload of
267
324
  */
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
325
  }
279
326
 
280
327
  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,43 @@ 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(this, 'add-button', 'vaadin-button', {
458
+ initializer: (button) => {
459
+ button.addEventListener('touchend', (e) => {
460
+ this._onAddFilesTouchEnd(e);
461
+ });
462
+ button.addEventListener('click', (e) => {
463
+ this._onAddFilesClick(e);
464
+ });
465
+ this._addButton = button;
466
+ },
467
+ }),
468
+ );
469
+
470
+ this.addController(
471
+ new SlotController(this, 'drop-label', 'span', {
472
+ initializer: (label) => {
473
+ this._dropLabel = label;
474
+ },
475
+ }),
476
+ );
477
+
478
+ this.addController(
479
+ new SlotController(this, 'file-list', 'vaadin-upload-file-list', {
480
+ initializer: (list) => {
481
+ this._fileList = list;
482
+ },
483
+ }),
484
+ );
485
+
486
+ this.addController(new SlotController(this, 'drop-label-icon', 'vaadin-upload-icon'));
449
487
  }
450
488
 
451
489
  /** @private */
@@ -509,6 +547,29 @@ class Upload extends ElementMixin(ThemableMixin(PolymerElement)) {
509
547
  return maxFiles >= 0 && numFiles >= maxFiles;
510
548
  }
511
549
 
550
+ /** @private */
551
+ __updateAddButton(addButton, maxFiles, i18n, maxFilesReached) {
552
+ if (addButton) {
553
+ addButton.disabled = maxFilesReached;
554
+ addButton.textContent = this._i18nPlural(maxFiles, i18n.addFiles);
555
+ }
556
+ }
557
+
558
+ /** @private */
559
+ __updateDropLabel(dropLabel, maxFiles, i18n) {
560
+ if (dropLabel) {
561
+ dropLabel.textContent = this._i18nPlural(maxFiles, i18n.dropFiles);
562
+ }
563
+ }
564
+
565
+ /** @private */
566
+ __updateFileList(list, files, i18n) {
567
+ if (list) {
568
+ list.items = [...files];
569
+ list.i18n = i18n;
570
+ }
571
+ }
572
+
512
573
  /** @private */
513
574
  _onDragover(event) {
514
575
  event.preventDefault();
@@ -615,7 +676,7 @@ class Upload extends ElementMixin(ThemableMixin(PolymerElement)) {
615
676
  this._setStatus(file, total, loaded, elapsed);
616
677
  stalledId = setTimeout(() => {
617
678
  file.status = this.i18n.uploading.status.stalled;
618
- this._notifyFileChanges(file);
679
+ this._renderFileList();
619
680
  }, 2000);
620
681
  } else {
621
682
  file.loadedStr = file.totalStr;
@@ -623,7 +684,7 @@ class Upload extends ElementMixin(ThemableMixin(PolymerElement)) {
623
684
  }
624
685
  }
625
686
 
626
- this._notifyFileChanges(file);
687
+ this._renderFileList();
627
688
  this.dispatchEvent(new CustomEvent('upload-progress', { detail: { file, xhr } }));
628
689
  };
629
690
 
@@ -633,7 +694,6 @@ class Upload extends ElementMixin(ThemableMixin(PolymerElement)) {
633
694
  clearTimeout(stalledId);
634
695
  file.indeterminate = file.uploading = false;
635
696
  if (file.abort) {
636
- this._notifyFileChanges(file);
637
697
  return;
638
698
  }
639
699
  file.status = '';
@@ -663,7 +723,7 @@ class Upload extends ElementMixin(ThemableMixin(PolymerElement)) {
663
723
  detail: { file, xhr },
664
724
  }),
665
725
  );
666
- this._notifyFileChanges(file);
726
+ this._renderFileList();
667
727
  }
668
728
  };
669
729
 
@@ -697,7 +757,7 @@ class Upload extends ElementMixin(ThemableMixin(PolymerElement)) {
697
757
  detail: { file, xhr },
698
758
  }),
699
759
  );
700
- this._notifyFileChanges(file);
760
+ this._renderFileList();
701
761
  };
702
762
 
703
763
  // Custom listener could modify the xhr just before sending it
@@ -739,16 +799,15 @@ class Upload extends ElementMixin(ThemableMixin(PolymerElement)) {
739
799
  if (file.xhr) {
740
800
  file.xhr.abort();
741
801
  }
742
- this._notifyFileChanges(file);
802
+ this._removeFile(file);
743
803
  }
744
804
  }
745
805
 
746
806
  /** @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
- });
807
+ _renderFileList() {
808
+ if (this._fileList) {
809
+ this._fileList.requestContentUpdate();
810
+ }
752
811
  }
753
812
 
754
813
  /** @private */
@@ -810,6 +869,14 @@ class Upload extends ElementMixin(ThemableMixin(PolymerElement)) {
810
869
  _removeFile(file) {
811
870
  if (this.files.indexOf(file) > -1) {
812
871
  this.files = this.files.filter((i) => i !== file);
872
+
873
+ this.dispatchEvent(
874
+ new CustomEvent('file-remove', {
875
+ detail: { file },
876
+ bubbles: true,
877
+ composed: true,
878
+ }),
879
+ );
813
880
  }
814
881
  }
815
882
 
@@ -851,11 +918,6 @@ class Upload extends ElementMixin(ThemableMixin(PolymerElement)) {
851
918
  this._abortFileUpload(event.detail.file);
852
919
  }
853
920
 
854
- /** @private */
855
- _onFileRemove(event) {
856
- this._removeFile(event.detail.file);
857
- }
858
-
859
921
  /** @private */
860
922
  _onFileReject(event) {
861
923
  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": "24.0.0-alpha1",
4
+ "version": "24.0.0-alpha3",
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": "24.0.0-alpha1",
4
+ "version": "24.0.0-alpha3",
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
  {