@vaadin/upload 25.1.0-alpha1 → 25.1.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 +11 -10
- package/src/styles/vaadin-upload-button-base-styles.d.ts +8 -0
- package/src/styles/vaadin-upload-button-base-styles.js +11 -0
- package/src/styles/vaadin-upload-drop-zone-base-styles.d.ts +8 -0
- package/src/styles/vaadin-upload-drop-zone-base-styles.js +26 -0
- package/src/styles/vaadin-upload-file-base-styles.js +19 -11
- package/src/vaadin-upload-button.d.ts +100 -0
- package/src/vaadin-upload-button.js +288 -0
- package/src/vaadin-upload-drop-zone.d.ts +72 -0
- package/src/vaadin-upload-drop-zone.js +215 -0
- package/src/vaadin-upload-file-list-mixin.d.ts +74 -0
- package/src/vaadin-upload-file-list-mixin.js +281 -15
- package/src/vaadin-upload-file-list.d.ts +83 -0
- package/src/vaadin-upload-file-list.js +62 -2
- package/src/vaadin-upload-helpers.js +48 -0
- package/src/vaadin-upload-manager.d.ts +345 -0
- package/src/vaadin-upload-manager.js +720 -0
- package/src/vaadin-upload-mixin.js +2 -49
- package/vaadin-upload-button.d.ts +1 -0
- package/vaadin-upload-button.js +3 -0
- package/vaadin-upload-drop-zone.d.ts +1 -0
- package/vaadin-upload-drop-zone.js +3 -0
- package/vaadin-upload-file-list.d.ts +1 -0
- package/vaadin-upload-file-list.js +3 -0
- package/vaadin-upload-manager.d.ts +1 -0
- package/vaadin-upload-manager.js +1 -0
- package/web-types.json +240 -1
- package/web-types.lit.json +99 -1
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2000 - 2026 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { html, LitElement } from 'lit';
|
|
7
|
+
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
|
|
8
|
+
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
9
|
+
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
|
|
10
|
+
import { LumoInjectionMixin } from '@vaadin/vaadin-themable-mixin/lumo-injection-mixin.js';
|
|
11
|
+
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
12
|
+
import { uploadDropZoneStyles } from './styles/vaadin-upload-drop-zone-base-styles.js';
|
|
13
|
+
import { getFilesFromDropEvent } from './vaadin-upload-helpers.js';
|
|
14
|
+
import { UploadManager } from './vaadin-upload-manager.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* `<vaadin-upload-drop-zone>` is a Web Component that can be used as a drop zone
|
|
18
|
+
* for file uploads. When files are dropped on the drop zone, they are added to
|
|
19
|
+
* a linked UploadManager.
|
|
20
|
+
*
|
|
21
|
+
* ```html
|
|
22
|
+
* <vaadin-upload-drop-zone>
|
|
23
|
+
* <p>Drop files here</p>
|
|
24
|
+
* </vaadin-upload-drop-zone>
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* The drop zone must be linked to an UploadManager by setting the
|
|
28
|
+
* `manager` property:
|
|
29
|
+
*
|
|
30
|
+
* ```javascript
|
|
31
|
+
* const dropZone = document.querySelector('vaadin-upload-drop-zone');
|
|
32
|
+
* dropZone.manager = uploadManager;
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* ### Styling
|
|
36
|
+
*
|
|
37
|
+
* The component has no styling by default. When files are dragged over,
|
|
38
|
+
* the `dragover` attribute is set and the component uses a hover effect.
|
|
39
|
+
* To override the hover effect, use `vaadin-upload-drop-zone[dragover]::after`
|
|
40
|
+
* selector to style the pseudo-element covering the drop zone during dragover.
|
|
41
|
+
*
|
|
42
|
+
* Attribute | Description
|
|
43
|
+
* -------------------|--------------------------------------------
|
|
44
|
+
* `dragover` | Set when files are being dragged over the element
|
|
45
|
+
* `disabled` | Set when the drop zone is explicitly disabled
|
|
46
|
+
* `max-files-reached`| Set when the manager has reached maxFiles
|
|
47
|
+
*
|
|
48
|
+
* See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
|
|
49
|
+
*
|
|
50
|
+
* @customElement
|
|
51
|
+
* @extends HTMLElement
|
|
52
|
+
* @mixes ElementMixin
|
|
53
|
+
* @mixes ThemableMixin
|
|
54
|
+
*/
|
|
55
|
+
class UploadDropZone extends ElementMixin(ThemableMixin(PolylitMixin(LumoInjectionMixin(LitElement)))) {
|
|
56
|
+
static get is() {
|
|
57
|
+
return 'vaadin-upload-drop-zone';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
static get styles() {
|
|
61
|
+
return uploadDropZoneStyles;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
static get lumoInjector() {
|
|
65
|
+
return { ...super.lumoInjector, includeBaseStyles: true };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static get properties() {
|
|
69
|
+
return {
|
|
70
|
+
/**
|
|
71
|
+
* Reference to an UploadManager.
|
|
72
|
+
* When set, dropped files will be automatically added to the manager.
|
|
73
|
+
* @type {Object | null}
|
|
74
|
+
*/
|
|
75
|
+
manager: {
|
|
76
|
+
type: Object,
|
|
77
|
+
value: null,
|
|
78
|
+
observer: '__managerChanged',
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Whether the drop zone is disabled.
|
|
83
|
+
* @type {boolean}
|
|
84
|
+
*/
|
|
85
|
+
disabled: {
|
|
86
|
+
type: Boolean,
|
|
87
|
+
value: false,
|
|
88
|
+
reflect: true,
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* True when max files has been reached on the manager.
|
|
93
|
+
* @type {boolean}
|
|
94
|
+
* @readonly
|
|
95
|
+
*/
|
|
96
|
+
maxFilesReached: {
|
|
97
|
+
type: Boolean,
|
|
98
|
+
value: false,
|
|
99
|
+
reflect: true,
|
|
100
|
+
attribute: 'max-files-reached',
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
/** @private */
|
|
104
|
+
__dragover: {
|
|
105
|
+
type: Boolean,
|
|
106
|
+
value: false,
|
|
107
|
+
reflect: true,
|
|
108
|
+
attribute: 'dragover',
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
constructor() {
|
|
114
|
+
super();
|
|
115
|
+
this.__onMaxFilesReachedChanged = this.__onMaxFilesReachedChanged.bind(this);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** @protected */
|
|
119
|
+
ready() {
|
|
120
|
+
super.ready();
|
|
121
|
+
|
|
122
|
+
this.addEventListener('dragover', this.__onDragover.bind(this));
|
|
123
|
+
this.addEventListener('dragleave', this.__onDragleave.bind(this));
|
|
124
|
+
this.addEventListener('drop', this.__onDrop.bind(this));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** @protected */
|
|
128
|
+
disconnectedCallback() {
|
|
129
|
+
super.disconnectedCallback();
|
|
130
|
+
|
|
131
|
+
// Clean up manager listener to prevent memory leaks
|
|
132
|
+
if (this.manager instanceof UploadManager) {
|
|
133
|
+
this.manager.removeEventListener('max-files-reached-changed', this.__onMaxFilesReachedChanged);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** @protected */
|
|
138
|
+
connectedCallback() {
|
|
139
|
+
super.connectedCallback();
|
|
140
|
+
|
|
141
|
+
// Re-attach manager listener when reconnected to DOM
|
|
142
|
+
if (this.manager instanceof UploadManager) {
|
|
143
|
+
this.manager.addEventListener('max-files-reached-changed', this.__onMaxFilesReachedChanged);
|
|
144
|
+
|
|
145
|
+
// Sync maxFilesReached state with current manager state
|
|
146
|
+
this.maxFilesReached = !!this.manager.maxFilesReached;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** @protected */
|
|
151
|
+
render() {
|
|
152
|
+
return html`<slot></slot>`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** @private */
|
|
156
|
+
__onDragover(event) {
|
|
157
|
+
event.preventDefault();
|
|
158
|
+
const effectiveDisabled = this.disabled || this.maxFilesReached;
|
|
159
|
+
if (!effectiveDisabled) {
|
|
160
|
+
this.__dragover = true;
|
|
161
|
+
}
|
|
162
|
+
event.dataTransfer.dropEffect = effectiveDisabled ? 'none' : 'copy';
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** @private */
|
|
166
|
+
__onDragleave(event) {
|
|
167
|
+
event.preventDefault();
|
|
168
|
+
// Only remove dragover if we're actually leaving the drop zone
|
|
169
|
+
// (not just entering a child element)
|
|
170
|
+
if (event.relatedTarget && this.contains(event.relatedTarget)) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
this.__dragover = false;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** @private */
|
|
177
|
+
async __onDrop(event) {
|
|
178
|
+
event.preventDefault();
|
|
179
|
+
this.__dragover = false;
|
|
180
|
+
|
|
181
|
+
// If we have a manager and not disabled, add the files
|
|
182
|
+
const effectiveDisabled = this.disabled || this.maxFilesReached;
|
|
183
|
+
if (!effectiveDisabled && this.manager instanceof UploadManager) {
|
|
184
|
+
const files = await getFilesFromDropEvent(event);
|
|
185
|
+
this.manager.addFiles(files);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** @private */
|
|
190
|
+
__managerChanged(manager, oldManager) {
|
|
191
|
+
// Remove listener from old manager
|
|
192
|
+
if (oldManager instanceof UploadManager) {
|
|
193
|
+
oldManager.removeEventListener('max-files-reached-changed', this.__onMaxFilesReachedChanged);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Add listener to new manager
|
|
197
|
+
if (this.isConnected && manager instanceof UploadManager) {
|
|
198
|
+
manager.addEventListener('max-files-reached-changed', this.__onMaxFilesReachedChanged);
|
|
199
|
+
|
|
200
|
+
// Sync initial state if manager has maxFilesReached property
|
|
201
|
+
this.maxFilesReached = !!manager.maxFilesReached;
|
|
202
|
+
} else {
|
|
203
|
+
this.maxFilesReached = false;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** @private */
|
|
208
|
+
__onMaxFilesReachedChanged(event) {
|
|
209
|
+
this.maxFilesReached = event.detail.value;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
defineCustomElement(UploadDropZone);
|
|
214
|
+
|
|
215
|
+
export { UploadDropZone };
|
|
@@ -0,0 +1,74 @@
|
|
|
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 type { Constructor } from '@open-wc/dedupe-mixin';
|
|
7
|
+
import type { I18nMixinClass } from '@vaadin/component-base/src/i18n-mixin.js';
|
|
8
|
+
import type { UploadManager } from './vaadin-upload-manager.js';
|
|
9
|
+
|
|
10
|
+
export interface UploadFileListI18n {
|
|
11
|
+
file?: {
|
|
12
|
+
retry?: string;
|
|
13
|
+
start?: string;
|
|
14
|
+
remove?: string;
|
|
15
|
+
};
|
|
16
|
+
error?: {
|
|
17
|
+
tooManyFiles?: string;
|
|
18
|
+
fileIsTooBig?: string;
|
|
19
|
+
incorrectFileType?: string;
|
|
20
|
+
};
|
|
21
|
+
uploading?: {
|
|
22
|
+
status?: {
|
|
23
|
+
connecting?: string;
|
|
24
|
+
stalled?: string;
|
|
25
|
+
processing?: string;
|
|
26
|
+
held?: string;
|
|
27
|
+
};
|
|
28
|
+
remainingTime?: {
|
|
29
|
+
prefix?: string;
|
|
30
|
+
unknown?: string;
|
|
31
|
+
};
|
|
32
|
+
error?: {
|
|
33
|
+
serverUnavailable?: string;
|
|
34
|
+
unexpectedServerError?: string;
|
|
35
|
+
forbidden?: string;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
units?: {
|
|
39
|
+
size?: string[];
|
|
40
|
+
sizeBase?: number;
|
|
41
|
+
};
|
|
42
|
+
formatSize?(bytes: number): string;
|
|
43
|
+
formatTime?(seconds: number, split: number[]): string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* A mixin providing file list functionality.
|
|
48
|
+
*/
|
|
49
|
+
export declare function UploadFileListMixin<T extends Constructor<HTMLElement>>(
|
|
50
|
+
base: T,
|
|
51
|
+
): Constructor<UploadFileListMixinClass> & Constructor<I18nMixinClass<UploadFileListI18n>> & T;
|
|
52
|
+
|
|
53
|
+
export declare class UploadFileListMixinClass {
|
|
54
|
+
/**
|
|
55
|
+
* If true, the user cannot interact with this element.
|
|
56
|
+
*/
|
|
57
|
+
disabled: boolean;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Reference to an UploadManager to link this file list to.
|
|
61
|
+
* When set, the file list automatically:
|
|
62
|
+
* - Syncs files from the manager
|
|
63
|
+
* - Forwards retry/abort/start/remove events back to the manager
|
|
64
|
+
*/
|
|
65
|
+
manager: UploadManager | null;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The object used to localize this component.
|
|
69
|
+
* To change the default localization, replace this with an object
|
|
70
|
+
* that provides all properties, or just the individual properties
|
|
71
|
+
* you want to change.
|
|
72
|
+
*/
|
|
73
|
+
i18n: UploadFileListI18n;
|
|
74
|
+
}
|
|
@@ -4,28 +4,58 @@
|
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
6
|
import { html, render } from 'lit';
|
|
7
|
+
import { I18nMixin } from '@vaadin/component-base/src/i18n-mixin.js';
|
|
8
|
+
import { UploadManager } from './vaadin-upload-manager.js';
|
|
9
|
+
|
|
10
|
+
const DEFAULT_I18N = {
|
|
11
|
+
file: {
|
|
12
|
+
retry: 'Retry',
|
|
13
|
+
start: 'Start',
|
|
14
|
+
remove: 'Remove',
|
|
15
|
+
},
|
|
16
|
+
error: {
|
|
17
|
+
tooManyFiles: 'Too Many Files.',
|
|
18
|
+
fileIsTooBig: 'File is Too Big.',
|
|
19
|
+
incorrectFileType: 'Incorrect File Type.',
|
|
20
|
+
},
|
|
21
|
+
uploading: {
|
|
22
|
+
status: {
|
|
23
|
+
connecting: 'Connecting...',
|
|
24
|
+
stalled: 'Stalled',
|
|
25
|
+
processing: 'Processing File...',
|
|
26
|
+
held: 'Queued',
|
|
27
|
+
},
|
|
28
|
+
remainingTime: {
|
|
29
|
+
prefix: 'remaining time: ',
|
|
30
|
+
unknown: 'unknown remaining time',
|
|
31
|
+
},
|
|
32
|
+
error: {
|
|
33
|
+
serverUnavailable: 'Upload failed, please try again later',
|
|
34
|
+
unexpectedServerError: 'Upload failed due to server error',
|
|
35
|
+
forbidden: 'Upload forbidden',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
units: {
|
|
39
|
+
size: ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
|
|
40
|
+
},
|
|
41
|
+
};
|
|
7
42
|
|
|
8
43
|
/**
|
|
9
44
|
* @polymerMixin
|
|
45
|
+
* @mixes I18nMixin
|
|
10
46
|
*/
|
|
11
47
|
export const UploadFileListMixin = (superClass) =>
|
|
12
|
-
class UploadFileListMixin extends superClass {
|
|
48
|
+
class UploadFileListMixin extends I18nMixin(DEFAULT_I18N, superClass) {
|
|
13
49
|
static get properties() {
|
|
14
50
|
return {
|
|
15
51
|
/**
|
|
16
52
|
* The array of files being processed, or already uploaded.
|
|
53
|
+
* @readonly
|
|
17
54
|
*/
|
|
18
55
|
items: {
|
|
19
56
|
type: Array,
|
|
20
57
|
},
|
|
21
58
|
|
|
22
|
-
/**
|
|
23
|
-
* The object used to localize upload files.
|
|
24
|
-
*/
|
|
25
|
-
i18n: {
|
|
26
|
-
type: Object,
|
|
27
|
-
},
|
|
28
|
-
|
|
29
59
|
/**
|
|
30
60
|
* If true, the user cannot interact with this element.
|
|
31
61
|
*/
|
|
@@ -34,27 +64,263 @@ export const UploadFileListMixin = (superClass) =>
|
|
|
34
64
|
value: false,
|
|
35
65
|
reflectToAttribute: true,
|
|
36
66
|
},
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Reference to an UploadManager to link this file list to.
|
|
70
|
+
* When set, the file list automatically:
|
|
71
|
+
* - Syncs files from the manager
|
|
72
|
+
* - Forwards retry/abort/start/remove events back to the manager
|
|
73
|
+
* @type {Object | null}
|
|
74
|
+
*/
|
|
75
|
+
manager: {
|
|
76
|
+
type: Object,
|
|
77
|
+
value: null,
|
|
78
|
+
observer: '__managerChanged',
|
|
79
|
+
},
|
|
37
80
|
};
|
|
38
81
|
}
|
|
39
82
|
|
|
40
83
|
static get observers() {
|
|
41
|
-
return ['__updateItems(items,
|
|
84
|
+
return ['__updateItems(items, __effectiveI18n, disabled)'];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
constructor() {
|
|
88
|
+
super();
|
|
89
|
+
this.__onManagerFilesChanged = this.__onManagerFilesChanged.bind(this);
|
|
90
|
+
this.__onFileRetry = this.__onFileRetry.bind(this);
|
|
91
|
+
this.__onFileAbort = this.__onFileAbort.bind(this);
|
|
92
|
+
this.__onFileStart = this.__onFileStart.bind(this);
|
|
93
|
+
this.__onFileRemove = this.__onFileRemove.bind(this);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** @protected */
|
|
97
|
+
ready() {
|
|
98
|
+
super.ready();
|
|
99
|
+
|
|
100
|
+
// Listen for file events to forward to the manager
|
|
101
|
+
this.addEventListener('file-retry', this.__onFileRetry);
|
|
102
|
+
this.addEventListener('file-abort', this.__onFileAbort);
|
|
103
|
+
this.addEventListener('file-start', this.__onFileStart);
|
|
104
|
+
this.addEventListener('file-remove', this.__onFileRemove);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** @protected */
|
|
108
|
+
disconnectedCallback() {
|
|
109
|
+
super.disconnectedCallback();
|
|
110
|
+
|
|
111
|
+
// Clean up manager listener to prevent memory leaks
|
|
112
|
+
if (this.manager instanceof UploadManager) {
|
|
113
|
+
this.manager.removeEventListener('files-changed', this.__onManagerFilesChanged);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** @protected */
|
|
118
|
+
connectedCallback() {
|
|
119
|
+
super.connectedCallback();
|
|
120
|
+
|
|
121
|
+
// Re-attach manager listener when reconnected to DOM
|
|
122
|
+
if (this.manager instanceof UploadManager) {
|
|
123
|
+
this.manager.addEventListener('files-changed', this.__onManagerFilesChanged);
|
|
124
|
+
|
|
125
|
+
// Sync state with current manager files
|
|
126
|
+
this.__syncFromManager();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** @private */
|
|
131
|
+
__managerChanged(manager, oldManager) {
|
|
132
|
+
// Remove listeners from old manager
|
|
133
|
+
if (oldManager instanceof UploadManager) {
|
|
134
|
+
oldManager.removeEventListener('files-changed', this.__onManagerFilesChanged);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Add listeners to new manager only when connected
|
|
138
|
+
if (this.isConnected && manager instanceof UploadManager) {
|
|
139
|
+
manager.addEventListener('files-changed', this.__onManagerFilesChanged);
|
|
140
|
+
|
|
141
|
+
// Sync initial state
|
|
142
|
+
this.__syncFromManager();
|
|
143
|
+
} else {
|
|
144
|
+
// Clear the list when manager is removed
|
|
145
|
+
this.items = [];
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** @private */
|
|
150
|
+
__onManagerFilesChanged() {
|
|
151
|
+
this.__syncFromManager();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** @private */
|
|
155
|
+
__syncFromManager() {
|
|
156
|
+
if (this.manager instanceof UploadManager) {
|
|
157
|
+
this.items = [...this.manager.files];
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** @private */
|
|
162
|
+
__onFileRetry(event) {
|
|
163
|
+
if (this.manager instanceof UploadManager) {
|
|
164
|
+
event.stopPropagation();
|
|
165
|
+
this.manager.retryUpload(event.detail.file);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** @private */
|
|
170
|
+
__onFileAbort(event) {
|
|
171
|
+
if (this.manager instanceof UploadManager) {
|
|
172
|
+
event.stopPropagation();
|
|
173
|
+
this.manager.abortUpload(event.detail.file);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** @private */
|
|
178
|
+
__onFileStart(event) {
|
|
179
|
+
if (this.manager instanceof UploadManager) {
|
|
180
|
+
event.stopPropagation();
|
|
181
|
+
this.manager.uploadFiles(event.detail.file);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** @private */
|
|
186
|
+
__onFileRemove(event) {
|
|
187
|
+
if (this.manager instanceof UploadManager) {
|
|
188
|
+
event.stopPropagation();
|
|
189
|
+
this.manager.removeFile(event.detail.file);
|
|
190
|
+
}
|
|
42
191
|
}
|
|
43
192
|
|
|
44
193
|
/** @private */
|
|
45
194
|
__updateItems(items, i18n) {
|
|
46
195
|
if (items && i18n) {
|
|
196
|
+
// Apply i18n formatting to each file
|
|
197
|
+
items.forEach((file) => this.__applyI18nToFile(file));
|
|
47
198
|
this.requestContentUpdate();
|
|
48
199
|
}
|
|
49
200
|
}
|
|
50
201
|
|
|
51
|
-
/**
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
202
|
+
/** @private */
|
|
203
|
+
__applyI18nToFile(file) {
|
|
204
|
+
const i18n = this.__effectiveI18n;
|
|
205
|
+
|
|
206
|
+
// Always set size-related strings when total is available
|
|
207
|
+
if (file.total) {
|
|
208
|
+
this.__applyFileSizeStrings(file);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Apply status messages based on file state
|
|
212
|
+
file.status = this.__getFileStatus(file, i18n);
|
|
213
|
+
|
|
214
|
+
// Translate error codes to i18n messages
|
|
215
|
+
this.__applyFileError(file, i18n);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** @private */
|
|
219
|
+
__applyFileSizeStrings(file) {
|
|
220
|
+
file.totalStr = this.__formatSize(file.total);
|
|
221
|
+
file.loadedStr = this.__formatSize(file.loaded || 0);
|
|
222
|
+
// TODO: Remove elapsedStr in next major version - it's not used by vaadin-upload-file
|
|
223
|
+
if (file.elapsed != null) {
|
|
224
|
+
file.elapsedStr = this.__formatTime(file.elapsed, this.__splitTimeByUnits(file.elapsed));
|
|
225
|
+
}
|
|
226
|
+
if (file.remaining != null) {
|
|
227
|
+
file.remainingStr = this.__formatTime(file.remaining, this.__splitTimeByUnits(file.remaining));
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** @private */
|
|
232
|
+
__getFileStatus(file, i18n) {
|
|
233
|
+
if (file.held && !file.error) {
|
|
234
|
+
// File is queued and waiting
|
|
235
|
+
return i18n.uploading.status.held;
|
|
236
|
+
}
|
|
237
|
+
if (file.stalled) {
|
|
238
|
+
// File upload is stalled
|
|
239
|
+
return i18n.uploading.status.stalled;
|
|
240
|
+
}
|
|
241
|
+
if (file.uploading && file.indeterminate && !file.held) {
|
|
242
|
+
// File is uploading but progress is indeterminate (connecting or processing)
|
|
243
|
+
return file.progress === 100 ? i18n.uploading.status.processing : i18n.uploading.status.connecting;
|
|
244
|
+
}
|
|
245
|
+
if (file.uploading && file.progress < 100 && file.total) {
|
|
246
|
+
// File is uploading with known progress
|
|
247
|
+
return this.__formatFileProgress(file);
|
|
248
|
+
}
|
|
249
|
+
return file.status;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** @private */
|
|
253
|
+
__applyFileError(file, i18n) {
|
|
254
|
+
if (file.errorKey && i18n.uploading.error[file.errorKey]) {
|
|
255
|
+
file.error = i18n.uploading.error[file.errorKey];
|
|
256
|
+
} else if (!file.errorKey && this.manager instanceof UploadManager) {
|
|
257
|
+
// Clear error when errorKey is reset (e.g., on retry) only when using manager
|
|
258
|
+
file.error = '';
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** @private */
|
|
263
|
+
__formatSize(bytes) {
|
|
264
|
+
const i18n = this.__effectiveI18n;
|
|
265
|
+
if (typeof i18n.formatSize === 'function') {
|
|
266
|
+
return i18n.formatSize(bytes);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// https://wiki.ubuntu.com/UnitsPolicy
|
|
270
|
+
const base = i18n.units.sizeBase || 1000;
|
|
271
|
+
const unit = Math.trunc(Math.log(bytes) / Math.log(base));
|
|
272
|
+
const dec = Math.max(0, Math.min(3, unit - 1));
|
|
273
|
+
const size = Number.parseFloat((bytes / base ** unit).toFixed(dec));
|
|
274
|
+
return `${size} ${i18n.units.size[unit]}`;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/** @private */
|
|
278
|
+
__splitTimeByUnits(time) {
|
|
279
|
+
const unitSizes = [60, 60, 24, Infinity];
|
|
280
|
+
const timeValues = [0];
|
|
281
|
+
|
|
282
|
+
for (let i = 0; i < unitSizes.length && time > 0; i++) {
|
|
283
|
+
timeValues[i] = time % unitSizes[i];
|
|
284
|
+
time = Math.floor(time / unitSizes[i]);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return timeValues;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** @private */
|
|
291
|
+
__formatTime(seconds, split) {
|
|
292
|
+
const i18n = this.__effectiveI18n;
|
|
293
|
+
if (typeof i18n.formatTime === 'function') {
|
|
294
|
+
return i18n.formatTime(seconds, split);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Fill HH:MM:SS with leading zeros
|
|
298
|
+
while (split.length < 3) {
|
|
299
|
+
split.push(0);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return split
|
|
303
|
+
.reverse()
|
|
304
|
+
.map((number) => {
|
|
305
|
+
return (number < 10 ? '0' : '') + number;
|
|
306
|
+
})
|
|
307
|
+
.join(':');
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/** @private */
|
|
311
|
+
__formatFileProgress(file) {
|
|
312
|
+
const i18n = this.__effectiveI18n;
|
|
313
|
+
const remainingTime =
|
|
314
|
+
file.loaded > 0
|
|
315
|
+
? i18n.uploading.remainingTime.prefix + file.remainingStr
|
|
316
|
+
: i18n.uploading.remainingTime.unknown;
|
|
317
|
+
|
|
318
|
+
return `${file.totalStr}: ${file.progress}% (${remainingTime})`;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/** @private */
|
|
56
322
|
requestContentUpdate() {
|
|
57
|
-
const { items, i18n, disabled } = this;
|
|
323
|
+
const { items, __effectiveI18n: i18n, disabled } = this;
|
|
58
324
|
|
|
59
325
|
render(
|
|
60
326
|
html`
|
|
@@ -0,0 +1,83 @@
|
|
|
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 { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
7
|
+
import { UploadFileListI18n, UploadFileListMixin } from './vaadin-upload-file-list-mixin.js';
|
|
8
|
+
|
|
9
|
+
export { UploadFileListI18n };
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* `<vaadin-upload-file-list>` is a Web Component that displays a list of uploaded files.
|
|
13
|
+
* It automatically syncs files from the manager and forwards file events back to it.
|
|
14
|
+
*
|
|
15
|
+
* ```html
|
|
16
|
+
* <vaadin-upload-file-list></vaadin-upload-file-list>
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* The file list must be linked to an UploadManager by setting the `manager` property:
|
|
20
|
+
*
|
|
21
|
+
* ```javascript
|
|
22
|
+
* import { UploadManager } from '@vaadin/upload/vaadin-upload-manager.js';
|
|
23
|
+
*
|
|
24
|
+
* const manager = new UploadManager({ target: '/api/upload' });
|
|
25
|
+
* const fileList = document.querySelector('vaadin-upload-file-list');
|
|
26
|
+
* fileList.manager = manager;
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* ### Styling
|
|
30
|
+
*
|
|
31
|
+
* The following shadow DOM parts are available for styling:
|
|
32
|
+
*
|
|
33
|
+
* Part name | Description
|
|
34
|
+
* ----------|-------------
|
|
35
|
+
* `list` | The `<ul>` element wrapping the file items
|
|
36
|
+
*
|
|
37
|
+
* The following state attributes are available for styling:
|
|
38
|
+
*
|
|
39
|
+
* Attribute | Description
|
|
40
|
+
* -----------|-------------
|
|
41
|
+
* `disabled` | Set when the element is disabled
|
|
42
|
+
*
|
|
43
|
+
* The following custom CSS properties are available for styling:
|
|
44
|
+
*
|
|
45
|
+
* Custom CSS property |
|
|
46
|
+
* :--------------------------------------------|
|
|
47
|
+
* `--vaadin-upload-file-list-divider-color` |
|
|
48
|
+
* `--vaadin-upload-file-list-divider-width` |
|
|
49
|
+
* `--vaadin-upload-file-border-radius` |
|
|
50
|
+
* `--vaadin-upload-file-button-background` |
|
|
51
|
+
* `--vaadin-upload-file-button-border-color` |
|
|
52
|
+
* `--vaadin-upload-file-button-border-radius` |
|
|
53
|
+
* `--vaadin-upload-file-button-border-width` |
|
|
54
|
+
* `--vaadin-upload-file-button-text-color` |
|
|
55
|
+
* `--vaadin-upload-file-button-padding` |
|
|
56
|
+
* `--vaadin-upload-file-done-color` |
|
|
57
|
+
* `--vaadin-upload-file-error-color` |
|
|
58
|
+
* `--vaadin-upload-file-error-font-size` |
|
|
59
|
+
* `--vaadin-upload-file-error-font-weight` |
|
|
60
|
+
* `--vaadin-upload-file-error-line-height` |
|
|
61
|
+
* `--vaadin-upload-file-gap` |
|
|
62
|
+
* `--vaadin-upload-file-name-color` |
|
|
63
|
+
* `--vaadin-upload-file-name-font-size` |
|
|
64
|
+
* `--vaadin-upload-file-name-font-weight` |
|
|
65
|
+
* `--vaadin-upload-file-name-line-height` |
|
|
66
|
+
* `--vaadin-upload-file-padding` |
|
|
67
|
+
* `--vaadin-upload-file-status-color` |
|
|
68
|
+
* `--vaadin-upload-file-status-font-size` |
|
|
69
|
+
* `--vaadin-upload-file-status-font-weight` |
|
|
70
|
+
* `--vaadin-upload-file-status-line-height` |
|
|
71
|
+
* `--vaadin-upload-file-warning-color` |
|
|
72
|
+
*
|
|
73
|
+
* See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
|
|
74
|
+
*/
|
|
75
|
+
declare class UploadFileList extends UploadFileListMixin(ThemableMixin(HTMLElement)) {}
|
|
76
|
+
|
|
77
|
+
declare global {
|
|
78
|
+
interface HTMLElementTagNameMap {
|
|
79
|
+
'vaadin-upload-file-list': UploadFileList;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export { UploadFileList };
|