@exmg/exm-upload 1.0.0
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/README.md +20 -0
- package/index.d.ts +18 -0
- package/index.js +17 -0
- package/package.json +53 -0
- package/src/exm-dialog-upload-base.d.ts +90 -0
- package/src/exm-dialog-upload-base.js +226 -0
- package/src/exm-dialog-upload.d.ts +11 -0
- package/src/exm-dialog-upload.js +15 -0
- package/src/exm-upload-base.d.ts +148 -0
- package/src/exm-upload-base.js +373 -0
- package/src/exm-upload-crop-base.d.ts +16 -0
- package/src/exm-upload-crop-base.js +76 -0
- package/src/exm-upload-crop.d.ts +9 -0
- package/src/exm-upload-crop.js +12 -0
- package/src/exm-upload-drop-area-base.d.ts +8 -0
- package/src/exm-upload-drop-area-base.js +34 -0
- package/src/exm-upload-drop-area.d.ts +9 -0
- package/src/exm-upload-drop-area.js +12 -0
- package/src/exm-upload-input.d.ts +15 -0
- package/src/exm-upload-input.js +84 -0
- package/src/exm-upload-item-base.d.ts +45 -0
- package/src/exm-upload-item-base.js +177 -0
- package/src/exm-upload-item.d.ts +9 -0
- package/src/exm-upload-item.js +12 -0
- package/src/exm-upload.d.ts +19 -0
- package/src/exm-upload.js +22 -0
- package/src/mixins/exm-upload-drag-drop-mixin.d.ts +10 -0
- package/src/mixins/exm-upload-drag-drop-mixin.js +28 -0
- package/src/styles/exm-dialog-upload-css.d.ts +2 -0
- package/src/styles/exm-dialog-upload-css.js +4 -0
- package/src/styles/exm-dialog-upload.scss +22 -0
- package/src/styles/exm-upload-crop-styles-css.d.ts +2 -0
- package/src/styles/exm-upload-crop-styles-css.js +12 -0
- package/src/styles/exm-upload-crop-styles.scss +323 -0
- package/src/styles/exm-upload-drop-area-styles-css.d.ts +2 -0
- package/src/styles/exm-upload-drop-area-styles-css.js +4 -0
- package/src/styles/exm-upload-drop-area-styles.scss +45 -0
- package/src/styles/exm-upload-input-css.d.ts +2 -0
- package/src/styles/exm-upload-input-css.js +4 -0
- package/src/styles/exm-upload-input.scss +18 -0
- package/src/styles/exm-upload-item-styles-css.d.ts +2 -0
- package/src/styles/exm-upload-item-styles-css.js +4 -0
- package/src/styles/exm-upload-item-styles.scss +119 -0
- package/src/styles/exm-upload-styles-css.d.ts +2 -0
- package/src/styles/exm-upload-styles-css.js +4 -0
- package/src/styles/exm-upload-styles.scss +147 -0
- package/src/types.d.ts +33 -0
- package/src/types.js +10 -0
- package/src/upload/adapters/local-adapter.d.ts +6 -0
- package/src/upload/adapters/local-adapter.js +23 -0
- package/src/upload/adapters/xhr-adapter.d.ts +9 -0
- package/src/upload/adapters/xhr-adapter.js +49 -0
- package/src/upload/index.d.ts +1 -0
- package/src/upload/index.js +2 -0
- package/src/upload/service.d.ts +9 -0
- package/src/upload/service.js +42 -0
- package/src/upload/types.d.ts +12 -0
- package/src/upload/types.js +2 -0
- package/src/utils.d.ts +33 -0
- package/src/utils.js +89 -0
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { html } from 'lit';
|
|
3
|
+
import { property, query, state } from 'lit/decorators.js';
|
|
4
|
+
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
5
|
+
import { repeat } from 'lit/directives/repeat.js';
|
|
6
|
+
import '@material/web/icon/icon.js';
|
|
7
|
+
import { FileUploadError } from './types.js';
|
|
8
|
+
// File upload imports
|
|
9
|
+
import './exm-upload-item.js';
|
|
10
|
+
import './exm-upload-crop.js';
|
|
11
|
+
import './exm-upload-drop-area.js';
|
|
12
|
+
import { isCorrectResolution, isImage, isSizeValid, isTypeValidExtension } from './utils.js';
|
|
13
|
+
import { classMap } from 'lit/directives/class-map.js';
|
|
14
|
+
import { ExmgElement, observer } from '@exmg/lit-base';
|
|
15
|
+
export class ExmgUploadBase extends ExmgElement {
|
|
16
|
+
constructor() {
|
|
17
|
+
super(...arguments);
|
|
18
|
+
/**
|
|
19
|
+
* Files addded to component
|
|
20
|
+
*/
|
|
21
|
+
this.files = [];
|
|
22
|
+
/**
|
|
23
|
+
* Accepted file types seperated by comma
|
|
24
|
+
*/
|
|
25
|
+
this.accept = '';
|
|
26
|
+
/**
|
|
27
|
+
* The max file size allowed to upload e.g. '20mb'
|
|
28
|
+
*/
|
|
29
|
+
this.maxSize = '100mb';
|
|
30
|
+
/**
|
|
31
|
+
* Determines if multiple files can be selected. When false maxAmount is set to 1 automatically
|
|
32
|
+
*/
|
|
33
|
+
this.multiple = false;
|
|
34
|
+
/**
|
|
35
|
+
* Disable the upload component useful for forms
|
|
36
|
+
*/
|
|
37
|
+
this.disabled = false;
|
|
38
|
+
/**
|
|
39
|
+
* The upload response type
|
|
40
|
+
*/
|
|
41
|
+
this.responseType = 'json';
|
|
42
|
+
/**
|
|
43
|
+
* The upload response type
|
|
44
|
+
*/
|
|
45
|
+
this.serverType = 'xhr';
|
|
46
|
+
/**
|
|
47
|
+
* The CropperJS config see: https://github.com/fengyuanchen/cropperjs#options
|
|
48
|
+
*/
|
|
49
|
+
this.cropperConfig = {};
|
|
50
|
+
this._cropperConfig = {
|
|
51
|
+
modal: true,
|
|
52
|
+
center: true,
|
|
53
|
+
dragMode: 'move',
|
|
54
|
+
movable: true,
|
|
55
|
+
scalable: true,
|
|
56
|
+
guides: true,
|
|
57
|
+
zoomOnWheel: true,
|
|
58
|
+
cropBoxMovable: true,
|
|
59
|
+
wheelZoomRatio: 0.1,
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Internal state to check if user is cropping or not
|
|
63
|
+
*/
|
|
64
|
+
this._isCropping = false;
|
|
65
|
+
this.isModeDialog = false;
|
|
66
|
+
/**
|
|
67
|
+
* Internal state to check if file is uploaded or not
|
|
68
|
+
*/
|
|
69
|
+
this._uploaded = false;
|
|
70
|
+
/**
|
|
71
|
+
* Allow cropping can only be used when fixedResolution is not set
|
|
72
|
+
*/
|
|
73
|
+
this.allowCropping = false;
|
|
74
|
+
}
|
|
75
|
+
getValues() {
|
|
76
|
+
return this.files.filter((file) => file.status === 'UPLOADED').map((file) => file.url);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Extract files from either file input or drop event
|
|
80
|
+
* @param event
|
|
81
|
+
* @returns FileList[] | []
|
|
82
|
+
*/
|
|
83
|
+
_getFiles(event) {
|
|
84
|
+
var _a;
|
|
85
|
+
const { files } = (_a = (event.type === 'drop' ? event.dataTransfer : event.target)) !== null && _a !== void 0 ? _a : {};
|
|
86
|
+
return Array.from(files !== null && files !== void 0 ? files : []);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Resets the upload component
|
|
90
|
+
* @public
|
|
91
|
+
*/
|
|
92
|
+
reset() {
|
|
93
|
+
this._uploaded = false;
|
|
94
|
+
this.files = [];
|
|
95
|
+
this.cancelActiveCrop();
|
|
96
|
+
}
|
|
97
|
+
prepareFiles(addedFiles) {
|
|
98
|
+
// Loop through files and assign unique id
|
|
99
|
+
const newFiles = addedFiles.map((item) => ({
|
|
100
|
+
id: (Date.now() + Math.random()).toString(36),
|
|
101
|
+
file: item,
|
|
102
|
+
status: 'UPLOADING',
|
|
103
|
+
}));
|
|
104
|
+
for (const file of newFiles) {
|
|
105
|
+
this._validateFile(file);
|
|
106
|
+
this.files = [...this.files, file];
|
|
107
|
+
}
|
|
108
|
+
this.fire('files-changed', { files: this.files }, true);
|
|
109
|
+
this._uploaded = true;
|
|
110
|
+
if (this.fileElement) {
|
|
111
|
+
this.fileElement.value = '';
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Handles file change which also validates
|
|
116
|
+
* @param event
|
|
117
|
+
* @fires files-changed
|
|
118
|
+
*/
|
|
119
|
+
async _handleChange(event) {
|
|
120
|
+
event.preventDefault();
|
|
121
|
+
if (this.disabled) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
// clear error files
|
|
125
|
+
this.files = [...this.files.filter((item) => !item.invalid)];
|
|
126
|
+
const addedFiles = this._getFiles(event);
|
|
127
|
+
this.prepareFiles(addedFiles);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Validator function to check file before upload to server
|
|
131
|
+
* @param FileData
|
|
132
|
+
*/
|
|
133
|
+
async _validateFile(item) {
|
|
134
|
+
if (!isSizeValid(item.file.size, this.maxSize)) {
|
|
135
|
+
this._handleError(FileUploadError.INVALID_SIZE, item);
|
|
136
|
+
}
|
|
137
|
+
if (!isTypeValidExtension(item.file, this.accept || '')) {
|
|
138
|
+
this._handleError(FileUploadError.INVALID_TYPE, item);
|
|
139
|
+
}
|
|
140
|
+
if (this.maxAmount && this.files.length >= this.maxAmount) {
|
|
141
|
+
this._handleError(FileUploadError.INVALID_AMOUNT, item);
|
|
142
|
+
}
|
|
143
|
+
if (!this.multiple && this.files.length >= 1) {
|
|
144
|
+
this._handleError(FileUploadError.INVALID_MULTIPLE, item);
|
|
145
|
+
}
|
|
146
|
+
const _isImage = isImage(item.file);
|
|
147
|
+
if (_isImage && this.fixedResolution) {
|
|
148
|
+
const isCorrect = await isCorrectResolution(item.file, this.fixedResolution);
|
|
149
|
+
if (!isCorrect) {
|
|
150
|
+
this._handleError(FileUploadError.INVALID_RESOLUTION, item);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Enable cropping mode
|
|
156
|
+
* @param e
|
|
157
|
+
*/
|
|
158
|
+
_handleCropping(e) {
|
|
159
|
+
const file = e.detail;
|
|
160
|
+
this._isCropping = true;
|
|
161
|
+
setTimeout(() => { var _a; return (_a = this.cropSection) === null || _a === void 0 ? void 0 : _a.crop(file); });
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Removes file from files array
|
|
165
|
+
* @param id
|
|
166
|
+
*/
|
|
167
|
+
removeFile(id) {
|
|
168
|
+
this.files = this.files.filter((item) => {
|
|
169
|
+
return item.id !== id;
|
|
170
|
+
});
|
|
171
|
+
this.fire('file-removed', id);
|
|
172
|
+
this.fire('files-changed', { files: this.files }, true);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Removes the file out of the files array.
|
|
176
|
+
* @fires file-removed
|
|
177
|
+
* @param e
|
|
178
|
+
* @param id
|
|
179
|
+
*/
|
|
180
|
+
_handleRemove(e) {
|
|
181
|
+
const idToRemove = e.detail;
|
|
182
|
+
this.removeFile(idToRemove);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Handles crop done and toggles cropping state
|
|
186
|
+
* @fires crop-done
|
|
187
|
+
* @param e
|
|
188
|
+
*/
|
|
189
|
+
async _handleCropDone(e) {
|
|
190
|
+
this._isCropping = false;
|
|
191
|
+
const item = e.detail;
|
|
192
|
+
this.removeFile(item === null || item === void 0 ? void 0 : item.id);
|
|
193
|
+
this.prepareFiles([item.file]);
|
|
194
|
+
this.fire('crop-done', e.detail, true);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Handles crop cancel and toggles cropping state
|
|
198
|
+
* @fires crop-cancel
|
|
199
|
+
*/
|
|
200
|
+
cancelActiveCrop() {
|
|
201
|
+
this._isCropping = false;
|
|
202
|
+
this.fire('crop-cancel', {}, true);
|
|
203
|
+
}
|
|
204
|
+
saveActiveCrop() {
|
|
205
|
+
var _a;
|
|
206
|
+
(_a = this.cropSection) === null || _a === void 0 ? void 0 : _a.saveCropArea();
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Error handling helper function used for all validation
|
|
210
|
+
* @param message
|
|
211
|
+
* @param item
|
|
212
|
+
*/
|
|
213
|
+
_handleError(message, item) {
|
|
214
|
+
item.invalid = true;
|
|
215
|
+
item.status = 'INVALID';
|
|
216
|
+
item.error = message;
|
|
217
|
+
}
|
|
218
|
+
openFileSelector(e) {
|
|
219
|
+
var _a;
|
|
220
|
+
e.preventDefault();
|
|
221
|
+
const fileInput = (_a = this === null || this === void 0 ? void 0 : this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('#file');
|
|
222
|
+
if (fileInput) {
|
|
223
|
+
fileInput.click();
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
renderDescription() {
|
|
227
|
+
const { accept, maxSize, fixedResolution } = this;
|
|
228
|
+
return `Only ${accept ? accept.replace(/,/g, ' ') : ''} files${fixedResolution ? ` of resolution ${fixedResolution} px` : ''} that do not exceed ${maxSize} in size`;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Renders the file items
|
|
232
|
+
*/
|
|
233
|
+
renderFileItems() {
|
|
234
|
+
return repeat(this.files || [], (file) => file.id, (item) => html `<exm-upload-item
|
|
235
|
+
class="item"
|
|
236
|
+
id=${item.id}
|
|
237
|
+
@edit-image=${this._handleCropping}
|
|
238
|
+
@remove-item=${this._handleRemove}
|
|
239
|
+
uploadUrl=${ifDefined(this.uploadUrl)}
|
|
240
|
+
customAdapterPath=${ifDefined(this.customAdapterPath)}
|
|
241
|
+
serverType=${this.serverType}
|
|
242
|
+
responseType=${this.responseType}
|
|
243
|
+
?allowCropping=${!this.fixedResolution && this.allowCropping}
|
|
244
|
+
.item=${item}
|
|
245
|
+
></exm-upload-item>`);
|
|
246
|
+
}
|
|
247
|
+
renderUploadCrop() {
|
|
248
|
+
const { _cropperConfig } = this;
|
|
249
|
+
return html `
|
|
250
|
+
<exm-upload-crop
|
|
251
|
+
id="crop-dialog"
|
|
252
|
+
?hideActions=${this.isModeDialog}
|
|
253
|
+
@crop-done=${this._handleCropDone}
|
|
254
|
+
@crop-cancel=${this.cancelActiveCrop}
|
|
255
|
+
.cropperConfig=${_cropperConfig}
|
|
256
|
+
></exm-upload-crop>
|
|
257
|
+
`;
|
|
258
|
+
}
|
|
259
|
+
renderUploadDropArea() {
|
|
260
|
+
const { disabled } = this;
|
|
261
|
+
return html `
|
|
262
|
+
<exm-upload-drop-area
|
|
263
|
+
description=${this.renderDescription()}
|
|
264
|
+
?disabled=${disabled}
|
|
265
|
+
@browse-files=${this.openFileSelector}
|
|
266
|
+
@remove-file=${this._handleRemove}
|
|
267
|
+
@drop=${this._handleChange}
|
|
268
|
+
>
|
|
269
|
+
<slot slot="drop-icon" name="drop-icon">
|
|
270
|
+
<md-icon>upload</md-icon>
|
|
271
|
+
</slot>
|
|
272
|
+
<slot slot="drop-text" name="drop-text"
|
|
273
|
+
>Drag and drop, or <a href="#" @click=${this.openFileSelector}>browse</a> your files</slot
|
|
274
|
+
>
|
|
275
|
+
</exm-upload-drop-area>
|
|
276
|
+
`;
|
|
277
|
+
}
|
|
278
|
+
render() {
|
|
279
|
+
const { _isCropping, accept, disabled } = this;
|
|
280
|
+
if (_isCropping) {
|
|
281
|
+
return this.renderUploadCrop();
|
|
282
|
+
}
|
|
283
|
+
return html `
|
|
284
|
+
<div class="image-upload-wrapper ${classMap({ disabled: !!this.disabled })}">
|
|
285
|
+
${this.renderUploadDropArea()}
|
|
286
|
+
|
|
287
|
+
<form id="form-upload" enctype="multipart/form-data">
|
|
288
|
+
<input
|
|
289
|
+
id="file"
|
|
290
|
+
type="file"
|
|
291
|
+
name="file"
|
|
292
|
+
tabindex="-1"
|
|
293
|
+
accept=${ifDefined(accept)}
|
|
294
|
+
?disabled=${disabled}
|
|
295
|
+
@change=${this._handleChange}
|
|
296
|
+
?multiple=${this.multiple}
|
|
297
|
+
hidden
|
|
298
|
+
/>
|
|
299
|
+
</form>
|
|
300
|
+
</div>
|
|
301
|
+
|
|
302
|
+
<!-- Upload Items list -->
|
|
303
|
+
<div class="item-container">${this.renderFileItems()}</div>
|
|
304
|
+
`;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
__decorate([
|
|
308
|
+
property({ type: String })
|
|
309
|
+
], ExmgUploadBase.prototype, "for", void 0);
|
|
310
|
+
__decorate([
|
|
311
|
+
property({ type: Array })
|
|
312
|
+
], ExmgUploadBase.prototype, "files", void 0);
|
|
313
|
+
__decorate([
|
|
314
|
+
property({ type: String })
|
|
315
|
+
], ExmgUploadBase.prototype, "accept", void 0);
|
|
316
|
+
__decorate([
|
|
317
|
+
property({ type: String })
|
|
318
|
+
], ExmgUploadBase.prototype, "maxSize", void 0);
|
|
319
|
+
__decorate([
|
|
320
|
+
property({ type: Boolean })
|
|
321
|
+
], ExmgUploadBase.prototype, "multiple", void 0);
|
|
322
|
+
__decorate([
|
|
323
|
+
property({ type: Number })
|
|
324
|
+
], ExmgUploadBase.prototype, "maxAmount", void 0);
|
|
325
|
+
__decorate([
|
|
326
|
+
property({ type: Boolean })
|
|
327
|
+
], ExmgUploadBase.prototype, "disabled", void 0);
|
|
328
|
+
__decorate([
|
|
329
|
+
property({ type: String })
|
|
330
|
+
], ExmgUploadBase.prototype, "customAdapterPath", void 0);
|
|
331
|
+
__decorate([
|
|
332
|
+
property({ type: String })
|
|
333
|
+
], ExmgUploadBase.prototype, "uploadUrl", void 0);
|
|
334
|
+
__decorate([
|
|
335
|
+
property({ type: String })
|
|
336
|
+
], ExmgUploadBase.prototype, "responseType", void 0);
|
|
337
|
+
__decorate([
|
|
338
|
+
property({ type: String })
|
|
339
|
+
], ExmgUploadBase.prototype, "serverType", void 0);
|
|
340
|
+
__decorate([
|
|
341
|
+
property({ type: Object }),
|
|
342
|
+
observer(function (value) {
|
|
343
|
+
this._cropperConfig = { ...this._cropperConfig, ...value };
|
|
344
|
+
})
|
|
345
|
+
], ExmgUploadBase.prototype, "cropperConfig", void 0);
|
|
346
|
+
__decorate([
|
|
347
|
+
state()
|
|
348
|
+
], ExmgUploadBase.prototype, "_cropperConfig", void 0);
|
|
349
|
+
__decorate([
|
|
350
|
+
state()
|
|
351
|
+
], ExmgUploadBase.prototype, "_isCropping", void 0);
|
|
352
|
+
__decorate([
|
|
353
|
+
property({ type: Boolean })
|
|
354
|
+
], ExmgUploadBase.prototype, "isModeDialog", void 0);
|
|
355
|
+
__decorate([
|
|
356
|
+
state()
|
|
357
|
+
], ExmgUploadBase.prototype, "_uploaded", void 0);
|
|
358
|
+
__decorate([
|
|
359
|
+
query('#crop-dialog')
|
|
360
|
+
], ExmgUploadBase.prototype, "cropSection", void 0);
|
|
361
|
+
__decorate([
|
|
362
|
+
property({ type: Boolean })
|
|
363
|
+
], ExmgUploadBase.prototype, "allowCropping", void 0);
|
|
364
|
+
__decorate([
|
|
365
|
+
property({ type: String })
|
|
366
|
+
], ExmgUploadBase.prototype, "fixedResolution", void 0);
|
|
367
|
+
__decorate([
|
|
368
|
+
query('#file')
|
|
369
|
+
], ExmgUploadBase.prototype, "fileElement", void 0);
|
|
370
|
+
__decorate([
|
|
371
|
+
query('.item-container')
|
|
372
|
+
], ExmgUploadBase.prototype, "itemContainer", void 0);
|
|
373
|
+
//# sourceMappingURL=exm-upload-base.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import '@material/web/button/filled-button.js';
|
|
2
|
+
import { FileData } from './types.js';
|
|
3
|
+
import { ExmgElement } from '@exmg/lit-base';
|
|
4
|
+
export declare class ExmgUploadCropBase extends ExmgElement {
|
|
5
|
+
cropperConfig: any;
|
|
6
|
+
cropArea?: HTMLImageElement;
|
|
7
|
+
hideActions: boolean;
|
|
8
|
+
_item?: FileData;
|
|
9
|
+
private cropper?;
|
|
10
|
+
protected firstUpdated(): void;
|
|
11
|
+
crop(item: FileData): Promise<void>;
|
|
12
|
+
_cancel(): void;
|
|
13
|
+
saveCropArea(): void;
|
|
14
|
+
renderActions(): import("lit-html").TemplateResult<1>;
|
|
15
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { html, nothing } from 'lit';
|
|
3
|
+
import { query, property, state } from 'lit/decorators.js';
|
|
4
|
+
import '@material/web/button/filled-button.js';
|
|
5
|
+
import { ExmgElement } from '@exmg/lit-base';
|
|
6
|
+
import Cropper from 'cropperjs';
|
|
7
|
+
export class ExmgUploadCropBase extends ExmgElement {
|
|
8
|
+
constructor() {
|
|
9
|
+
super(...arguments);
|
|
10
|
+
this.cropperConfig = {};
|
|
11
|
+
this.hideActions = false;
|
|
12
|
+
}
|
|
13
|
+
firstUpdated() {
|
|
14
|
+
this.fire('crop-start', {}, true);
|
|
15
|
+
}
|
|
16
|
+
async crop(item) {
|
|
17
|
+
this._item = item;
|
|
18
|
+
const reader = new FileReader();
|
|
19
|
+
reader.onload = () => {
|
|
20
|
+
if (this.cropArea) {
|
|
21
|
+
this.cropArea.src = reader.result;
|
|
22
|
+
}
|
|
23
|
+
this.cropper = new Cropper(this.cropArea, this.cropperConfig);
|
|
24
|
+
};
|
|
25
|
+
if (item.file) {
|
|
26
|
+
reader.readAsDataURL(item.file);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
_cancel() {
|
|
30
|
+
this.fire('crop-cancel', {}, true);
|
|
31
|
+
}
|
|
32
|
+
saveCropArea() {
|
|
33
|
+
var _a, _b;
|
|
34
|
+
const canvas = (_a = this.cropper) === null || _a === void 0 ? void 0 : _a.getCroppedCanvas();
|
|
35
|
+
if (!canvas) {
|
|
36
|
+
throw new Error('Cropper canvas not found');
|
|
37
|
+
}
|
|
38
|
+
canvas === null || canvas === void 0 ? void 0 : canvas.toBlob((blob) => {
|
|
39
|
+
if (!this._item || !blob) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const file = new File([blob], `cropped-${this._item.file.name}`, { type: this._item.file.type });
|
|
43
|
+
this._item.file = file;
|
|
44
|
+
this.fire('crop-done', this._item);
|
|
45
|
+
}, (_b = this._item) === null || _b === void 0 ? void 0 : _b.file.type);
|
|
46
|
+
}
|
|
47
|
+
renderActions() {
|
|
48
|
+
return html `
|
|
49
|
+
<div class="actions">
|
|
50
|
+
<md-filled-button @click=${this._cancel}>Cancel</md-filled-button>
|
|
51
|
+
<md-filled-button @click=${this.saveCropArea}>Crop</md-filled-button>
|
|
52
|
+
</div>
|
|
53
|
+
`;
|
|
54
|
+
}
|
|
55
|
+
render() {
|
|
56
|
+
return html `
|
|
57
|
+
<div class="image-container">
|
|
58
|
+
<img id="image" />
|
|
59
|
+
</div>
|
|
60
|
+
${this.hideActions ? nothing : this.renderActions()}
|
|
61
|
+
`;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
__decorate([
|
|
65
|
+
property({ type: Object })
|
|
66
|
+
], ExmgUploadCropBase.prototype, "cropperConfig", void 0);
|
|
67
|
+
__decorate([
|
|
68
|
+
query('#image')
|
|
69
|
+
], ExmgUploadCropBase.prototype, "cropArea", void 0);
|
|
70
|
+
__decorate([
|
|
71
|
+
property({ type: Boolean })
|
|
72
|
+
], ExmgUploadCropBase.prototype, "hideActions", void 0);
|
|
73
|
+
__decorate([
|
|
74
|
+
state()
|
|
75
|
+
], ExmgUploadCropBase.prototype, "_item", void 0);
|
|
76
|
+
//# sourceMappingURL=exm-upload-crop-base.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ExmgUploadCropBase } from './exm-upload-crop-base.js';
|
|
2
|
+
export declare class ExmgUploadCrop extends ExmgUploadCropBase {
|
|
3
|
+
static styles: import("lit").CSSResult;
|
|
4
|
+
}
|
|
5
|
+
declare global {
|
|
6
|
+
interface HTMLElementTagNameMap {
|
|
7
|
+
'exm-upload-crop': ExmgUploadCrop;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { customElement } from 'lit/decorators.js';
|
|
3
|
+
import { style } from './styles/exm-upload-crop-styles-css.js';
|
|
4
|
+
import { ExmgUploadCropBase } from './exm-upload-crop-base.js';
|
|
5
|
+
let ExmgUploadCrop = class ExmgUploadCrop extends ExmgUploadCropBase {
|
|
6
|
+
};
|
|
7
|
+
ExmgUploadCrop.styles = style;
|
|
8
|
+
ExmgUploadCrop = __decorate([
|
|
9
|
+
customElement('exm-upload-crop')
|
|
10
|
+
], ExmgUploadCrop);
|
|
11
|
+
export { ExmgUploadCrop };
|
|
12
|
+
//# sourceMappingURL=exm-upload-crop.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ExmgElement } from '@exmg/lit-base';
|
|
2
|
+
declare const ExmgUploadDropAreaBase_base: (new (...args: any[]) => import("./mixins/exm-upload-drag-drop-mixin.js").DragAndDropInterface) & typeof ExmgElement;
|
|
3
|
+
export declare class ExmgUploadDropAreaBase extends ExmgUploadDropAreaBase_base {
|
|
4
|
+
description?: string;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
7
|
+
}
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { html } from 'lit';
|
|
3
|
+
import { property } from 'lit/decorators.js';
|
|
4
|
+
import { classMap } from 'lit/directives/class-map.js';
|
|
5
|
+
import { DragAndDropMixin } from './mixins/exm-upload-drag-drop-mixin.js';
|
|
6
|
+
import { ExmgElement } from '@exmg/lit-base';
|
|
7
|
+
export class ExmgUploadDropAreaBase extends DragAndDropMixin(ExmgElement) {
|
|
8
|
+
render() {
|
|
9
|
+
return html ` <div
|
|
10
|
+
class="drop ${classMap({
|
|
11
|
+
disabled: !!this.disabled,
|
|
12
|
+
dropHover: this.dragOver && !this.disabled,
|
|
13
|
+
})}"
|
|
14
|
+
@dragover=${this.onDragOver}
|
|
15
|
+
@dragleave=${this.onDragLeave}
|
|
16
|
+
@drop=${this.onDrop}
|
|
17
|
+
>
|
|
18
|
+
<div class="drop-container">
|
|
19
|
+
<slot name="drop-icon"></slot>
|
|
20
|
+
<span class="drop-text">
|
|
21
|
+
<slot name="drop-text"></slot>
|
|
22
|
+
</span>
|
|
23
|
+
<span class="description">${this.description}</span>
|
|
24
|
+
</div>
|
|
25
|
+
</div>`;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
__decorate([
|
|
29
|
+
property({ type: String })
|
|
30
|
+
], ExmgUploadDropAreaBase.prototype, "description", void 0);
|
|
31
|
+
__decorate([
|
|
32
|
+
property({ type: Boolean })
|
|
33
|
+
], ExmgUploadDropAreaBase.prototype, "disabled", void 0);
|
|
34
|
+
//# sourceMappingURL=exm-upload-drop-area-base.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ExmgUploadDropAreaBase } from './exm-upload-drop-area-base.js';
|
|
2
|
+
export declare class ExmgUploadDropArea extends ExmgUploadDropAreaBase {
|
|
3
|
+
static styles: import("lit").CSSResult;
|
|
4
|
+
}
|
|
5
|
+
declare global {
|
|
6
|
+
interface HTMLElementTagNameMap {
|
|
7
|
+
'exm-upload-drop-area': ExmgUploadDropArea;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { customElement } from 'lit/decorators.js';
|
|
3
|
+
import { style } from './styles/exm-upload-drop-area-styles-css.js';
|
|
4
|
+
import { ExmgUploadDropAreaBase } from './exm-upload-drop-area-base.js';
|
|
5
|
+
let ExmgUploadDropArea = class ExmgUploadDropArea extends ExmgUploadDropAreaBase {
|
|
6
|
+
};
|
|
7
|
+
ExmgUploadDropArea.styles = style;
|
|
8
|
+
ExmgUploadDropArea = __decorate([
|
|
9
|
+
customElement('exm-upload-drop-area')
|
|
10
|
+
], ExmgUploadDropArea);
|
|
11
|
+
export { ExmgUploadDropArea };
|
|
12
|
+
//# sourceMappingURL=exm-upload-drop-area.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { LitElement } from 'lit';
|
|
2
|
+
import '@exmg/exm-collapsed';
|
|
3
|
+
import { ExmgUpload } from './exm-upload.js';
|
|
4
|
+
export declare class ExmgUploadInput extends LitElement {
|
|
5
|
+
opened: boolean;
|
|
6
|
+
closeDelay: number;
|
|
7
|
+
static styles: import("lit").CSSResult[];
|
|
8
|
+
getUploadElement(): ExmgUpload;
|
|
9
|
+
getInputElement(): HTMLInputElement;
|
|
10
|
+
protected firstUpdated(): void;
|
|
11
|
+
toggle(): void;
|
|
12
|
+
_uploadSuccess(): void;
|
|
13
|
+
handleClick(e: CustomEvent): void;
|
|
14
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { LitElement, html } from 'lit';
|
|
3
|
+
import { customElement, property } from 'lit/decorators.js';
|
|
4
|
+
import '@exmg/exm-collapsed';
|
|
5
|
+
import { style } from './styles/exm-upload-input-css.js';
|
|
6
|
+
let ExmgUploadInput = class ExmgUploadInput extends LitElement {
|
|
7
|
+
constructor() {
|
|
8
|
+
super(...arguments);
|
|
9
|
+
this.opened = false;
|
|
10
|
+
this.closeDelay = 400;
|
|
11
|
+
}
|
|
12
|
+
getUploadElement() {
|
|
13
|
+
return this.querySelector('exm-upload');
|
|
14
|
+
}
|
|
15
|
+
getInputElement() {
|
|
16
|
+
return this.querySelector('[slot="input"]');
|
|
17
|
+
}
|
|
18
|
+
firstUpdated() {
|
|
19
|
+
const upload = this.getUploadElement();
|
|
20
|
+
if (upload) {
|
|
21
|
+
if (upload.multiple) {
|
|
22
|
+
console.warn('exm-upload-input: multiple is not supported, forcing to false');
|
|
23
|
+
upload.multiple = false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
toggle() {
|
|
28
|
+
this.opened = !this.opened;
|
|
29
|
+
}
|
|
30
|
+
_uploadSuccess() {
|
|
31
|
+
const upload = this.getUploadElement();
|
|
32
|
+
const input = this.getInputElement();
|
|
33
|
+
if (!input || !upload) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const response = upload.getValues()[0];
|
|
37
|
+
let value = '';
|
|
38
|
+
try {
|
|
39
|
+
const d = JSON.parse(response);
|
|
40
|
+
// @ts-ignore
|
|
41
|
+
value = d.url ? d.url : response;
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
value = response;
|
|
45
|
+
}
|
|
46
|
+
input.value = value;
|
|
47
|
+
setTimeout(() => {
|
|
48
|
+
upload.reset();
|
|
49
|
+
this.opened = false;
|
|
50
|
+
}, this.closeDelay);
|
|
51
|
+
}
|
|
52
|
+
handleClick(e) {
|
|
53
|
+
e.preventDefault();
|
|
54
|
+
const el = e.target;
|
|
55
|
+
const slotValue = el.getAttribute('slot');
|
|
56
|
+
if (el.tagName.toLowerCase() === 'md-icon-button' && slotValue === 'trailing-icon') {
|
|
57
|
+
this.toggle();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
render() {
|
|
61
|
+
return html `
|
|
62
|
+
<div class="input-wrapper">
|
|
63
|
+
<div class="input" aria-expanded=${this.opened} aria-controls="collapsed">
|
|
64
|
+
<slot name="input" @click=${this.handleClick}></slot>
|
|
65
|
+
</div>
|
|
66
|
+
<exm-collapsed id="collapsed" ?opened=${this.opened}>
|
|
67
|
+
<slot name="upload" @upload-success=${this._uploadSuccess}></slot>
|
|
68
|
+
</exm-collapsed>
|
|
69
|
+
</div>
|
|
70
|
+
`;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
ExmgUploadInput.styles = [style];
|
|
74
|
+
__decorate([
|
|
75
|
+
property({ type: Boolean })
|
|
76
|
+
], ExmgUploadInput.prototype, "opened", void 0);
|
|
77
|
+
__decorate([
|
|
78
|
+
property({ type: Number })
|
|
79
|
+
], ExmgUploadInput.prototype, "closeDelay", void 0);
|
|
80
|
+
ExmgUploadInput = __decorate([
|
|
81
|
+
customElement('exm-upload-input')
|
|
82
|
+
], ExmgUploadInput);
|
|
83
|
+
export { ExmgUploadInput };
|
|
84
|
+
//# sourceMappingURL=exm-upload-input.js.map
|