@vaadin/upload 25.2.7 → 25.3.0-alpha10
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/custom-elements.json +637 -204
- package/package.json +11 -11
- package/src/vaadin-upload-button.js +4 -10
- package/src/vaadin-upload-drop-zone.js +1 -0
- package/src/vaadin-upload-file-list-mixin.d.ts +1 -0
- package/src/vaadin-upload-file-list-mixin.js +10 -103
- package/src/vaadin-upload-file-list.js +1 -0
- package/src/vaadin-upload-file-mixin.js +0 -9
- package/src/vaadin-upload-file.js +8 -5
- package/src/vaadin-upload-helpers.js +143 -0
- package/src/vaadin-upload-icon.js +1 -0
- package/src/vaadin-upload-internal-manager.js +174 -0
- package/src/vaadin-upload-manager.d.ts +1 -6
- package/src/vaadin-upload-manager.js +85 -32
- package/src/vaadin-upload-mixin.d.ts +4 -44
- package/src/vaadin-upload-mixin.js +361 -577
- package/src/vaadin-upload.js +1 -0
- package/web-types.json +6 -16
- package/web-types.lit.json +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/upload",
|
|
3
|
-
"version": "25.
|
|
3
|
+
"version": "25.3.0-alpha10",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -35,19 +35,19 @@
|
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@open-wc/dedupe-mixin": "^1.3.0",
|
|
38
|
-
"@vaadin/a11y-base": "
|
|
39
|
-
"@vaadin/button": "
|
|
40
|
-
"@vaadin/component-base": "
|
|
41
|
-
"@vaadin/progress-bar": "
|
|
42
|
-
"@vaadin/vaadin-themable-mixin": "
|
|
38
|
+
"@vaadin/a11y-base": "25.3.0-alpha10",
|
|
39
|
+
"@vaadin/button": "25.3.0-alpha10",
|
|
40
|
+
"@vaadin/component-base": "25.3.0-alpha10",
|
|
41
|
+
"@vaadin/progress-bar": "25.3.0-alpha10",
|
|
42
|
+
"@vaadin/vaadin-themable-mixin": "25.3.0-alpha10",
|
|
43
43
|
"lit": "^3.0.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@vaadin/aura": "
|
|
47
|
-
"@vaadin/chai-plugins": "
|
|
48
|
-
"@vaadin/test-runner-commands": "
|
|
46
|
+
"@vaadin/aura": "25.3.0-alpha10",
|
|
47
|
+
"@vaadin/chai-plugins": "25.3.0-alpha10",
|
|
48
|
+
"@vaadin/test-runner-commands": "25.3.0-alpha10",
|
|
49
49
|
"@vaadin/testing-helpers": "^2.0.0",
|
|
50
|
-
"@vaadin/vaadin-lumo-styles": "
|
|
50
|
+
"@vaadin/vaadin-lumo-styles": "25.3.0-alpha10",
|
|
51
51
|
"sinon": "^22.0.0"
|
|
52
52
|
},
|
|
53
53
|
"customElements": "custom-elements.json",
|
|
@@ -55,5 +55,5 @@
|
|
|
55
55
|
"web-types.json",
|
|
56
56
|
"web-types.lit.json"
|
|
57
57
|
],
|
|
58
|
-
"gitHead": "
|
|
58
|
+
"gitHead": "f2833abdf9b613fa0d0ed216830e3f4de87b7dac"
|
|
59
59
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { html, LitElement } from 'lit';
|
|
7
7
|
import { ButtonMixin } from '@vaadin/button/src/vaadin-button-mixin.js';
|
|
8
8
|
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
|
|
9
|
+
import { setOrRemoveAttribute } from '@vaadin/component-base/src/dom-utils.js';
|
|
9
10
|
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
10
11
|
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
|
|
11
12
|
import { TooltipController } from '@vaadin/component-base/src/tooltip-controller.js';
|
|
@@ -70,6 +71,7 @@ import { UploadManager } from './vaadin-upload-manager.js';
|
|
|
70
71
|
*
|
|
71
72
|
* See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
|
|
72
73
|
*
|
|
74
|
+
* @attr {string} theme - The theme variants to apply to the component.
|
|
73
75
|
* @customElement vaadin-upload-button
|
|
74
76
|
* @extends HTMLElement
|
|
75
77
|
*/
|
|
@@ -213,22 +215,14 @@ class UploadButton extends ButtonMixin(ElementMixin(ThemableMixin(PolylitMixin(L
|
|
|
213
215
|
|
|
214
216
|
// Set accept attribute from manager
|
|
215
217
|
const accept = this.manager && this.manager.accept;
|
|
216
|
-
|
|
217
|
-
fileInput.setAttribute('accept', accept);
|
|
218
|
-
} else {
|
|
219
|
-
fileInput.removeAttribute('accept');
|
|
220
|
-
}
|
|
218
|
+
setOrRemoveAttribute(fileInput, 'accept', accept);
|
|
221
219
|
|
|
222
220
|
// Set multiple attribute based on manager's maxFiles
|
|
223
221
|
const maxFiles = this.manager && this.manager.maxFiles != null ? this.manager.maxFiles : Infinity;
|
|
224
222
|
fileInput.multiple = maxFiles !== 1;
|
|
225
223
|
|
|
226
224
|
// Set capture attribute
|
|
227
|
-
|
|
228
|
-
fileInput.setAttribute('capture', this.capture);
|
|
229
|
-
} else {
|
|
230
|
-
fileInput.removeAttribute('capture');
|
|
231
|
-
}
|
|
225
|
+
setOrRemoveAttribute(fileInput, 'capture', this.capture);
|
|
232
226
|
}
|
|
233
227
|
|
|
234
228
|
/** @private */
|
|
@@ -47,6 +47,7 @@ import { UploadManager } from './vaadin-upload-manager.js';
|
|
|
47
47
|
*
|
|
48
48
|
* See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
|
|
49
49
|
*
|
|
50
|
+
* @attr {string} theme - The theme variants to apply to the component.
|
|
50
51
|
* @customElement vaadin-upload-drop-zone
|
|
51
52
|
* @extends HTMLElement
|
|
52
53
|
*/
|
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
import { html, render } from 'lit';
|
|
7
7
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
8
8
|
import { I18nMixin } from '@vaadin/component-base/src/i18n-mixin.js';
|
|
9
|
+
import { translateErrorKey, updateFileStatus } from './vaadin-upload-helpers.js';
|
|
9
10
|
import { UploadManager } from './vaadin-upload-manager.js';
|
|
10
11
|
|
|
11
|
-
const DEFAULT_I18N = {
|
|
12
|
+
export const DEFAULT_I18N = {
|
|
12
13
|
file: {
|
|
13
14
|
retry: 'Retry',
|
|
14
15
|
start: 'Start',
|
|
@@ -267,124 +268,30 @@ export const UploadFileListMixin = (superClass) =>
|
|
|
267
268
|
__applyI18nToFile(file) {
|
|
268
269
|
const i18n = this.__effectiveI18n;
|
|
269
270
|
|
|
270
|
-
//
|
|
271
|
-
|
|
272
|
-
this.__applyFileSizeStrings(file);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// Apply status messages based on file state
|
|
276
|
-
file.status = this.__getFileStatus(file, i18n);
|
|
271
|
+
// Apply size and status strings based on file state
|
|
272
|
+
updateFileStatus(file, i18n, { indeterminateFirst: true });
|
|
277
273
|
|
|
278
274
|
// Translate error codes to i18n messages
|
|
279
275
|
this.__applyFileError(file, i18n);
|
|
280
276
|
}
|
|
281
277
|
|
|
282
|
-
/** @private */
|
|
283
|
-
__applyFileSizeStrings(file) {
|
|
284
|
-
file.totalStr = this.__formatSize(file.total);
|
|
285
|
-
file.loadedStr = this.__formatSize(file.loaded || 0);
|
|
286
|
-
// TODO: Remove elapsedStr in next major version - it's not used by vaadin-upload-file
|
|
287
|
-
if (file.elapsed != null) {
|
|
288
|
-
file.elapsedStr = this.__formatTime(file.elapsed, this.__splitTimeByUnits(file.elapsed));
|
|
289
|
-
}
|
|
290
|
-
if (file.remaining != null) {
|
|
291
|
-
file.remainingStr = this.__formatTime(file.remaining, this.__splitTimeByUnits(file.remaining));
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/** @private */
|
|
296
|
-
__getFileStatus(file, i18n) {
|
|
297
|
-
if (file.held && !file.error) {
|
|
298
|
-
// File is queued and waiting
|
|
299
|
-
return i18n.uploading.status.held;
|
|
300
|
-
}
|
|
301
|
-
if (file.stalled) {
|
|
302
|
-
// File upload is stalled
|
|
303
|
-
return i18n.uploading.status.stalled;
|
|
304
|
-
}
|
|
305
|
-
if (file.uploading && file.indeterminate && !file.held) {
|
|
306
|
-
// File is uploading but progress is indeterminate (connecting or processing)
|
|
307
|
-
return file.progress === 100 ? i18n.uploading.status.processing : i18n.uploading.status.connecting;
|
|
308
|
-
}
|
|
309
|
-
if (file.uploading && file.progress < 100 && file.total) {
|
|
310
|
-
// File is uploading with known progress
|
|
311
|
-
return this.__formatFileProgress(file);
|
|
312
|
-
}
|
|
313
|
-
return file.status;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
278
|
/** @private */
|
|
317
279
|
__applyFileError(file, i18n) {
|
|
318
|
-
|
|
319
|
-
|
|
280
|
+
// An error key without an i18n message (e.g. a failure to send the
|
|
281
|
+
// request) leaves the error assigned to the file untouched
|
|
282
|
+
const error = file.errorKey && translateErrorKey(file.errorKey, i18n);
|
|
283
|
+
if (error) {
|
|
284
|
+
file.error = error;
|
|
320
285
|
} else if (!file.errorKey && this.manager instanceof UploadManager) {
|
|
321
286
|
// Clear error when errorKey is reset (e.g., on retry) only when using manager
|
|
322
287
|
file.error = '';
|
|
323
288
|
}
|
|
324
289
|
}
|
|
325
290
|
|
|
326
|
-
/** @private */
|
|
327
|
-
__formatSize(bytes) {
|
|
328
|
-
const i18n = this.__effectiveI18n;
|
|
329
|
-
if (typeof i18n.formatSize === 'function') {
|
|
330
|
-
return i18n.formatSize(bytes);
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// https://wiki.ubuntu.com/UnitsPolicy
|
|
334
|
-
const base = i18n.units.sizeBase || 1000;
|
|
335
|
-
const unit = Math.trunc(Math.log(bytes) / Math.log(base));
|
|
336
|
-
const dec = Math.max(0, Math.min(3, unit - 1));
|
|
337
|
-
const size = Number.parseFloat((bytes / base ** unit).toFixed(dec));
|
|
338
|
-
return `${size} ${i18n.units.size[unit]}`;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
/** @private */
|
|
342
|
-
__splitTimeByUnits(time) {
|
|
343
|
-
const unitSizes = [60, 60, 24, Infinity];
|
|
344
|
-
const timeValues = [0];
|
|
345
|
-
|
|
346
|
-
for (let i = 0; i < unitSizes.length && time > 0; i++) {
|
|
347
|
-
timeValues[i] = time % unitSizes[i];
|
|
348
|
-
time = Math.floor(time / unitSizes[i]);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
return timeValues;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
/** @private */
|
|
355
|
-
__formatTime(seconds, split) {
|
|
356
|
-
const i18n = this.__effectiveI18n;
|
|
357
|
-
if (typeof i18n.formatTime === 'function') {
|
|
358
|
-
return i18n.formatTime(seconds, split);
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// Fill HH:MM:SS with leading zeros
|
|
362
|
-
while (split.length < 3) {
|
|
363
|
-
split.push(0);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
return split
|
|
367
|
-
.reverse()
|
|
368
|
-
.map((number) => {
|
|
369
|
-
return (number < 10 ? '0' : '') + number;
|
|
370
|
-
})
|
|
371
|
-
.join(':');
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/** @private */
|
|
375
|
-
__formatFileProgress(file) {
|
|
376
|
-
const i18n = this.__effectiveI18n;
|
|
377
|
-
const remainingTime =
|
|
378
|
-
file.loaded > 0
|
|
379
|
-
? i18n.uploading.remainingTime.prefix + file.remainingStr
|
|
380
|
-
: i18n.uploading.remainingTime.unknown;
|
|
381
|
-
|
|
382
|
-
return `${file.totalStr}: ${file.progress}% (${remainingTime})`;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
291
|
/** @private */
|
|
386
292
|
requestContentUpdate() {
|
|
387
293
|
const { items, __effectiveI18n: i18n, disabled } = this;
|
|
294
|
+
|
|
388
295
|
const managerDisabled = this.manager instanceof UploadManager && this.manager.disabled;
|
|
389
296
|
const effectiveDisabled = disabled || managerDisabled;
|
|
390
297
|
|
|
@@ -75,6 +75,7 @@ import { UploadFileListMixin } from './vaadin-upload-file-list-mixin.js';
|
|
|
75
75
|
*
|
|
76
76
|
* See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
|
|
77
77
|
*
|
|
78
|
+
* @attr {string} theme - The theme variants to apply to the component.
|
|
78
79
|
* @customElement vaadin-upload-file-list
|
|
79
80
|
* @extends HTMLElement
|
|
80
81
|
*/
|
|
@@ -167,15 +167,6 @@ export const UploadFileMixin = (superClass) =>
|
|
|
167
167
|
return event.composedPath()[0] === this;
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
/** @private */
|
|
171
|
-
__disabledChanged(disabled) {
|
|
172
|
-
if (disabled) {
|
|
173
|
-
this.removeAttribute('tabindex');
|
|
174
|
-
} else {
|
|
175
|
-
this.setAttribute('tabindex', this.tabindex);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
170
|
/** @private */
|
|
180
171
|
_errorMessageChanged(errorMessage) {
|
|
181
172
|
this.toggleAttribute('error', Boolean(errorMessage));
|
|
@@ -81,6 +81,7 @@ import { UploadFileMixin } from './vaadin-upload-file-mixin.js';
|
|
|
81
81
|
* @fires {CustomEvent} file-retry - Fired when the retry button is pressed to request retrying the upload of this file.
|
|
82
82
|
* @fires {CustomEvent} file-start - Fired when the start button is pressed to request starting the upload of this file.
|
|
83
83
|
*
|
|
84
|
+
* @attr {string} theme - The theme variants to apply to the component.
|
|
84
85
|
* @customElement vaadin-upload-file
|
|
85
86
|
* @extends HTMLElement
|
|
86
87
|
*/
|
|
@@ -105,11 +106,13 @@ class UploadFile extends UploadFileMixin(ThemableMixin(PolylitMixin(LumoInjectio
|
|
|
105
106
|
return html`
|
|
106
107
|
<div part="loader" ?hidden="${!this.uploading}" aria-hidden="true"></div>
|
|
107
108
|
|
|
108
|
-
${
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
109
|
+
${
|
|
110
|
+
this.__thumbnail
|
|
111
|
+
? html`<div part="thumbnail">
|
|
112
|
+
<img src="${this.__thumbnail}" alt="${this.fileName}" />
|
|
113
|
+
</div>`
|
|
114
|
+
: nothing
|
|
115
|
+
}
|
|
113
116
|
|
|
114
117
|
<div part="done-icon" ?hidden="${!this.complete}" aria-hidden="true"></div>
|
|
115
118
|
<div part="warning-icon" ?hidden="${!this.errorMessage}" aria-hidden="true"></div>
|
|
@@ -4,6 +4,149 @@
|
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Format the given number of bytes with the units defined by the i18n object,
|
|
9
|
+
* or with the custom `formatSize` function if the i18n object provides one.
|
|
10
|
+
*
|
|
11
|
+
* @param {number} bytes - The size in bytes
|
|
12
|
+
* @param {Object} i18n - The effective i18n object
|
|
13
|
+
* @returns {string} - The formatted size
|
|
14
|
+
* @private
|
|
15
|
+
*/
|
|
16
|
+
function formatSize(bytes, i18n) {
|
|
17
|
+
if (typeof i18n.formatSize === 'function') {
|
|
18
|
+
return i18n.formatSize(bytes);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// https://wiki.ubuntu.com/UnitsPolicy
|
|
22
|
+
const base = i18n.units.sizeBase || 1000;
|
|
23
|
+
const unit = bytes > 0 ? Math.trunc(Math.log(bytes) / Math.log(base)) : 0;
|
|
24
|
+
const dec = Math.max(0, Math.min(3, unit - 1));
|
|
25
|
+
const size = Number.parseFloat((bytes / base ** unit).toFixed(dec));
|
|
26
|
+
return `${size} ${i18n.units.size[unit]}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** @private */
|
|
30
|
+
function splitTimeByUnits(time) {
|
|
31
|
+
const unitSizes = [60, 60, 24, Infinity];
|
|
32
|
+
const timeValues = [0];
|
|
33
|
+
|
|
34
|
+
for (let i = 0; i < unitSizes.length && time > 0; i++) {
|
|
35
|
+
timeValues[i] = time % unitSizes[i];
|
|
36
|
+
time = Math.floor(time / unitSizes[i]);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return timeValues;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Format the given time in seconds as a 'HH:MM:SS' string, or with the
|
|
44
|
+
* custom `formatTime` function if the i18n object provides one.
|
|
45
|
+
*
|
|
46
|
+
* @param {number} seconds - The time in seconds
|
|
47
|
+
* @param {Object} i18n - The effective i18n object
|
|
48
|
+
* @returns {string} - The formatted time
|
|
49
|
+
* @private
|
|
50
|
+
*/
|
|
51
|
+
function formatTime(seconds, i18n) {
|
|
52
|
+
const split = splitTimeByUnits(seconds);
|
|
53
|
+
if (typeof i18n.formatTime === 'function') {
|
|
54
|
+
return i18n.formatTime(seconds, split);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Fill HH:MM:SS with leading zeros
|
|
58
|
+
while (split.length < 3) {
|
|
59
|
+
split.push(0);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return split
|
|
63
|
+
.reverse()
|
|
64
|
+
.map((number) => {
|
|
65
|
+
return (number < 10 ? '0' : '') + number;
|
|
66
|
+
})
|
|
67
|
+
.join(':');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** @private */
|
|
71
|
+
function formatFileProgress(file, i18n) {
|
|
72
|
+
const remainingTime =
|
|
73
|
+
file.loaded > 0 ? i18n.uploading.remainingTime.prefix + file.remainingStr : i18n.uploading.remainingTime.unknown;
|
|
74
|
+
|
|
75
|
+
return `${file.totalStr}: ${file.progress}% (${remainingTime})`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** @private */
|
|
79
|
+
function getFileStatus(file, i18n, indeterminateFirst) {
|
|
80
|
+
if (file.held && !file.error) {
|
|
81
|
+
// File is queued and waiting
|
|
82
|
+
return i18n.uploading.status.held;
|
|
83
|
+
}
|
|
84
|
+
if (file.stalled) {
|
|
85
|
+
// File upload is stalled
|
|
86
|
+
return i18n.uploading.status.stalled;
|
|
87
|
+
}
|
|
88
|
+
// A progress event has arrived, show the progress even when no bytes
|
|
89
|
+
// have been transferred yet (formatted as unknown remaining time)
|
|
90
|
+
const progressStatus =
|
|
91
|
+
file.uploading && file.progress < 100 && file.total ? formatFileProgress(file, i18n) : undefined;
|
|
92
|
+
if (progressStatus && !indeterminateFirst) {
|
|
93
|
+
return progressStatus;
|
|
94
|
+
}
|
|
95
|
+
if (file.uploading && file.indeterminate && !file.held) {
|
|
96
|
+
// File is uploading but progress is indeterminate (connecting or processing)
|
|
97
|
+
return file.progress === 100 ? i18n.uploading.status.processing : i18n.uploading.status.connecting;
|
|
98
|
+
}
|
|
99
|
+
if (progressStatus) {
|
|
100
|
+
return progressStatus;
|
|
101
|
+
}
|
|
102
|
+
return file.status;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Update the human-readable status and size strings of the given file
|
|
107
|
+
* based on its upload state, using the given i18n object for formatting.
|
|
108
|
+
*
|
|
109
|
+
* @param {Object} file - The file to update
|
|
110
|
+
* @param {Object} i18n - The effective i18n object
|
|
111
|
+
* @param {Object} [options]
|
|
112
|
+
* @param {boolean} [options.indeterminateFirst=false] - Give the
|
|
113
|
+
* indeterminate statuses (connecting, processing) precedence over the
|
|
114
|
+
* transfer progress, like `<vaadin-upload-file-list>` historically does.
|
|
115
|
+
* @private
|
|
116
|
+
*/
|
|
117
|
+
export function updateFileStatus(file, i18n, { indeterminateFirst = false } = {}) {
|
|
118
|
+
// Always set size-related strings when total is available
|
|
119
|
+
if (file.total) {
|
|
120
|
+
file.totalStr = formatSize(file.total, i18n);
|
|
121
|
+
file.loadedStr = formatSize(file.loaded || 0, i18n);
|
|
122
|
+
// TODO: Remove elapsedStr in next major version - it's not used by vaadin-upload-file
|
|
123
|
+
if (file.elapsed != null) {
|
|
124
|
+
file.elapsedStr = formatTime(file.elapsed, i18n);
|
|
125
|
+
}
|
|
126
|
+
if (file.remaining != null) {
|
|
127
|
+
file.remainingStr = formatTime(file.remaining, i18n);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Apply status message based on file state
|
|
132
|
+
file.status = getFileStatus(file, i18n, indeterminateFirst);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Translate the error key of a failed upload into the matching i18n error
|
|
137
|
+
* message. A timed out request is reported as server unavailable.
|
|
138
|
+
*
|
|
139
|
+
* @param {string} errorKey - The error key of a failed upload
|
|
140
|
+
* @param {Object} i18n - The effective i18n object
|
|
141
|
+
* @returns {string | undefined} - The translated error message, or `undefined`
|
|
142
|
+
* when the i18n object has no message for the key
|
|
143
|
+
* @private
|
|
144
|
+
*/
|
|
145
|
+
export function translateErrorKey(errorKey, i18n) {
|
|
146
|
+
const key = errorKey === 'timeout' ? 'serverUnavailable' : errorKey;
|
|
147
|
+
return i18n.uploading.error[key];
|
|
148
|
+
}
|
|
149
|
+
|
|
7
150
|
/**
|
|
8
151
|
* Get the files from the drop event. The dropped items may contain a
|
|
9
152
|
* combination of files and directories. If a dropped item is a directory,
|
|
@@ -12,6 +12,7 @@ import { uploadIconStyles } from './styles/vaadin-upload-icon-base-styles.js';
|
|
|
12
12
|
/**
|
|
13
13
|
* An element used internally by `<vaadin-upload>`. Not intended to be used separately.
|
|
14
14
|
*
|
|
15
|
+
* @attr {string} theme - The theme variants to apply to the component.
|
|
15
16
|
* @customElement vaadin-upload-icon
|
|
16
17
|
* @extends HTMLElement
|
|
17
18
|
* @private
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2016 - 2026 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { UploadManager } from './vaadin-upload-manager.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The `headers` property also supports a JSON string, while the manager only
|
|
10
|
+
* accepts an object, so strings are parsed. An invalid JSON string parses to
|
|
11
|
+
* undefined, which throws when the request is configured.
|
|
12
|
+
*/
|
|
13
|
+
export function parseHeaders(headers) {
|
|
14
|
+
if (typeof headers !== 'string') {
|
|
15
|
+
return headers;
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(headers);
|
|
19
|
+
} catch (_) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// The manager configuration derived from the component properties. Each entry
|
|
25
|
+
// is defined as a getter on `InternalUploadManager`, so that the manager reads
|
|
26
|
+
// the current value whenever it needs it. This way configuration changed by an
|
|
27
|
+
// `upload-before` listener (e.g. headers with a fresh auth token) applies to
|
|
28
|
+
// the request being started, like it historically did.
|
|
29
|
+
//
|
|
30
|
+
// `formDataName` is not included: the `upload-before` listener assigns it to
|
|
31
|
+
// the file when the upload starts, like the component has historically done.
|
|
32
|
+
const MANAGER_CONFIG = {
|
|
33
|
+
// Fall back to an empty string, which means that window.location will be
|
|
34
|
+
// used, also when the property is set to null
|
|
35
|
+
target: (host) => host.target || '',
|
|
36
|
+
// Unlike the manager, the component passes an unsupported method to the
|
|
37
|
+
// request as-is instead of throwing
|
|
38
|
+
method: (host) => host.method,
|
|
39
|
+
headers: (host) => parseHeaders(host.headers),
|
|
40
|
+
timeout: (host) => host.timeout,
|
|
41
|
+
noAuto: (host) => host.noAuto,
|
|
42
|
+
withCredentials: (host) => host.withCredentials,
|
|
43
|
+
uploadFormat: (host) => host.uploadFormat,
|
|
44
|
+
// Unlike the manager, the component pauses uploads on a non-positive value
|
|
45
|
+
// instead of throwing
|
|
46
|
+
maxConcurrentUploads: (host) => host.maxConcurrentUploads,
|
|
47
|
+
// Unlike the manager, the component treats a negative value as no limit
|
|
48
|
+
maxFiles: (host) => (host.maxFiles < 0 ? Infinity : host.maxFiles),
|
|
49
|
+
maxFileSize: (host) => host.maxFileSize,
|
|
50
|
+
accept: (host) => host.accept,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* An `UploadManager` subclass used internally by `<vaadin-upload>`. It reads
|
|
55
|
+
* its configuration from the host component, and overrides the manager
|
|
56
|
+
* behavior where the component behavior that predates the manager integration
|
|
57
|
+
* differs from the standalone manager.
|
|
58
|
+
*/
|
|
59
|
+
export class InternalUploadManager extends UploadManager {
|
|
60
|
+
/**
|
|
61
|
+
* @param {HTMLElement} host The `<vaadin-upload>` element to read the
|
|
62
|
+
* configuration from
|
|
63
|
+
*/
|
|
64
|
+
constructor(host) {
|
|
65
|
+
super();
|
|
66
|
+
this.__host = host;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Override accessor from `UploadManager` to accept assigned files as-is,
|
|
71
|
+
* without validating them against the maxFiles, maxFileSize and accept
|
|
72
|
+
* constraints, e.g. to show previously uploaded files.
|
|
73
|
+
* @override
|
|
74
|
+
*/
|
|
75
|
+
get files() {
|
|
76
|
+
return super.files;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
set files(files) {
|
|
80
|
+
this._setFiles([...files]);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Start the upload of the given file, even if it is already complete, in
|
|
85
|
+
* which case it is uploaded again from scratch. A file that is already
|
|
86
|
+
* uploading or queued is ignored.
|
|
87
|
+
* @param {UploadFile} file
|
|
88
|
+
*/
|
|
89
|
+
startUpload(file) {
|
|
90
|
+
this._queueFileUpload(file);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Override method from `UploadManager` to not restart the upload of a
|
|
95
|
+
* file that is already uploading or queued, like the component
|
|
96
|
+
* historically does; only the `upload-retry` event is dispatched.
|
|
97
|
+
* @override
|
|
98
|
+
*/
|
|
99
|
+
retryUpload(file) {
|
|
100
|
+
const evt = this.dispatchEvent(
|
|
101
|
+
new CustomEvent('upload-retry', {
|
|
102
|
+
detail: { file, xhr: file.xhr },
|
|
103
|
+
cancelable: true,
|
|
104
|
+
}),
|
|
105
|
+
);
|
|
106
|
+
if (evt) {
|
|
107
|
+
this._queueFileUpload(file);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Override method from `UploadManager` to treat all files as managed:
|
|
113
|
+
* files that are not in the `files` list are uploaded without being added
|
|
114
|
+
* to it, and removing a file in an `upload-before` or `upload-request`
|
|
115
|
+
* listener does not cancel its upload.
|
|
116
|
+
* @override
|
|
117
|
+
*/
|
|
118
|
+
_isFileManaged(_file) {
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Override method from `UploadManager` to leave the state of a file whose
|
|
124
|
+
* `upload-before` or `upload-request` event was prevented untouched, so
|
|
125
|
+
* that the preventing listener can take over the request (e.g. send the
|
|
126
|
+
* xhr manually). The upload slot stays taken until the taken-over request
|
|
127
|
+
* completes.
|
|
128
|
+
* @override
|
|
129
|
+
*/
|
|
130
|
+
_holdFile(_file) {
|
|
131
|
+
// The listener that prevented the upload is expected to take it over
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Override method from `UploadManager` to keep the progress properties of
|
|
136
|
+
* the previous upload attempt until a new upload progresses.
|
|
137
|
+
* @override
|
|
138
|
+
*/
|
|
139
|
+
_resetFileProgress(_file) {
|
|
140
|
+
// Progress is only updated by upload progress events
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Override method from `UploadManager` to keep the last request available
|
|
145
|
+
* on the file after the upload has finished, e.g. in the `upload-retry`
|
|
146
|
+
* event detail.
|
|
147
|
+
* @override
|
|
148
|
+
*/
|
|
149
|
+
_clearFileXhr(_file) {
|
|
150
|
+
// The request reference is kept on the file
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Override method from `UploadManager` to also free upload capacity for
|
|
155
|
+
* other queued files when a file is removed.
|
|
156
|
+
* @override
|
|
157
|
+
*/
|
|
158
|
+
_removeFile(file) {
|
|
159
|
+
super._removeFile(file);
|
|
160
|
+
this._processUploadQueue();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Define the configuration properties as getters that read the current value
|
|
165
|
+
// from the host component. The setters are no-ops so that the assignments in
|
|
166
|
+
// the `UploadManager` constructor are ignored.
|
|
167
|
+
Object.entries(MANAGER_CONFIG).forEach(([prop, getValue]) => {
|
|
168
|
+
Object.defineProperty(InternalUploadManager.prototype, prop, {
|
|
169
|
+
get() {
|
|
170
|
+
return getValue(this.__host);
|
|
171
|
+
},
|
|
172
|
+
set(_value) {},
|
|
173
|
+
});
|
|
174
|
+
});
|
|
@@ -10,12 +10,7 @@ export type UploadFormat = 'raw' | 'multipart';
|
|
|
10
10
|
export type FileRejectError = 'tooManyFiles' | 'fileIsTooBig' | 'incorrectFileType';
|
|
11
11
|
|
|
12
12
|
export type UploadErrorKey =
|
|
13
|
-
| '
|
|
14
|
-
| 'serverUnavailable'
|
|
15
|
-
| 'unexpectedServerError'
|
|
16
|
-
| 'forbidden'
|
|
17
|
-
| 'sendFailed'
|
|
18
|
-
| 'fileTooLarge';
|
|
13
|
+
'timeout' | 'serverUnavailable' | 'unexpectedServerError' | 'forbidden' | 'sendFailed' | 'fileTooLarge';
|
|
19
14
|
|
|
20
15
|
export interface UploadFile extends File {
|
|
21
16
|
uploadTarget: string;
|