@duskmoon-dev/el-file-upload 0.4.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.
@@ -0,0 +1,463 @@
1
+ // src/el-dm-file-upload.ts
2
+ import { BaseElement, css } from "@duskmoon-dev/el-core";
3
+ import { css as fileUploadCSS } from "@duskmoon-dev/core/components/file-upload";
4
+ var SIZE_CLASSES = {
5
+ sm: "file-upload-sm",
6
+ md: "",
7
+ lg: "file-upload-lg"
8
+ };
9
+ var coreStyles = fileUploadCSS.replace(/@layer\s+components\s*\{/, "").replace(/\}\s*$/, "");
10
+ var styles = css`
11
+ :host {
12
+ display: block;
13
+ }
14
+
15
+ :host([hidden]) {
16
+ display: none !important;
17
+ }
18
+
19
+ ${coreStyles}
20
+
21
+ /* Web component specific adjustments */
22
+ .file-upload {
23
+ font-family: inherit;
24
+ }
25
+
26
+ .file-upload-dropzone {
27
+ display: flex;
28
+ flex-direction: column;
29
+ align-items: center;
30
+ justify-content: center;
31
+ gap: 0.5rem;
32
+ min-height: 150px;
33
+ padding: 2rem;
34
+ border: 2px dashed var(--color-outline, #ccc);
35
+ border-radius: 0.5rem;
36
+ background-color: var(--color-surface-container, #f5f5f5);
37
+ cursor: pointer;
38
+ transition:
39
+ border-color 0.2s,
40
+ background-color 0.2s;
41
+ }
42
+
43
+ .file-upload-dropzone:hover {
44
+ border-color: var(--color-primary);
45
+ background-color: var(--color-surface-container-high, #e8e8e8);
46
+ }
47
+
48
+ .file-upload-dropzone.dragging {
49
+ border-color: var(--color-primary);
50
+ background-color: var(--color-primary);
51
+ background-color: rgba(var(--color-primary-rgb, 98, 0, 238), 0.1);
52
+ }
53
+
54
+ .file-upload-input {
55
+ display: none;
56
+ }
57
+
58
+ .file-upload-icon {
59
+ font-size: 2.5rem;
60
+ color: var(--color-on-surface);
61
+ opacity: 0.5;
62
+ }
63
+
64
+ .file-upload-text {
65
+ text-align: center;
66
+ }
67
+
68
+ .file-upload-title {
69
+ font-size: 1rem;
70
+ font-weight: 500;
71
+ color: var(--color-on-surface);
72
+ }
73
+
74
+ .file-upload-subtitle {
75
+ font-size: 0.875rem;
76
+ color: var(--color-on-surface);
77
+ opacity: 0.7;
78
+ }
79
+
80
+ .file-upload-browse {
81
+ color: var(--color-primary);
82
+ text-decoration: underline;
83
+ cursor: pointer;
84
+ }
85
+
86
+ /* File list */
87
+ .file-upload-list {
88
+ display: flex;
89
+ flex-direction: column;
90
+ gap: 0.5rem;
91
+ margin-top: 1rem;
92
+ }
93
+
94
+ .file-upload-item {
95
+ display: flex;
96
+ align-items: center;
97
+ gap: 0.75rem;
98
+ padding: 0.75rem;
99
+ background-color: var(--color-surface-container, #f5f5f5);
100
+ border-radius: 0.375rem;
101
+ }
102
+
103
+ .file-upload-item-icon {
104
+ font-size: 1.5rem;
105
+ color: var(--color-on-surface);
106
+ opacity: 0.7;
107
+ }
108
+
109
+ .file-upload-item-info {
110
+ flex: 1;
111
+ min-width: 0;
112
+ }
113
+
114
+ .file-upload-item-name {
115
+ font-size: 0.875rem;
116
+ font-weight: 500;
117
+ color: var(--color-on-surface);
118
+ white-space: nowrap;
119
+ overflow: hidden;
120
+ text-overflow: ellipsis;
121
+ }
122
+
123
+ .file-upload-item-size {
124
+ font-size: 0.75rem;
125
+ color: var(--color-on-surface);
126
+ opacity: 0.7;
127
+ }
128
+
129
+ .file-upload-item-remove {
130
+ display: flex;
131
+ align-items: center;
132
+ justify-content: center;
133
+ width: 1.5rem;
134
+ height: 1.5rem;
135
+ padding: 0;
136
+ border: none;
137
+ border-radius: 50%;
138
+ background-color: transparent;
139
+ color: var(--color-on-surface);
140
+ opacity: 0.5;
141
+ cursor: pointer;
142
+ transition:
143
+ opacity 0.2s,
144
+ background-color 0.2s;
145
+ }
146
+
147
+ .file-upload-item-remove:hover {
148
+ opacity: 1;
149
+ background-color: var(--color-error);
150
+ color: var(--color-on-error, white);
151
+ }
152
+
153
+ /* Preview grid */
154
+ .file-upload-preview {
155
+ display: grid;
156
+ grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
157
+ gap: 0.75rem;
158
+ margin-top: 1rem;
159
+ }
160
+
161
+ .file-upload-preview-item {
162
+ position: relative;
163
+ aspect-ratio: 1;
164
+ border-radius: 0.375rem;
165
+ overflow: hidden;
166
+ background-color: var(--color-surface-container, #f5f5f5);
167
+ }
168
+
169
+ .file-upload-preview-image {
170
+ width: 100%;
171
+ height: 100%;
172
+ object-fit: cover;
173
+ }
174
+
175
+ .file-upload-preview-remove {
176
+ position: absolute;
177
+ top: 0.25rem;
178
+ right: 0.25rem;
179
+ display: flex;
180
+ align-items: center;
181
+ justify-content: center;
182
+ width: 1.5rem;
183
+ height: 1.5rem;
184
+ padding: 0;
185
+ border: none;
186
+ border-radius: 50%;
187
+ background-color: rgba(0, 0, 0, 0.5);
188
+ color: white;
189
+ cursor: pointer;
190
+ opacity: 0;
191
+ transition: opacity 0.2s;
192
+ }
193
+
194
+ .file-upload-preview-item:hover .file-upload-preview-remove {
195
+ opacity: 1;
196
+ }
197
+
198
+ /* Size variants */
199
+ .file-upload-sm .file-upload-dropzone {
200
+ min-height: 100px;
201
+ padding: 1rem;
202
+ }
203
+
204
+ .file-upload-lg .file-upload-dropzone {
205
+ min-height: 200px;
206
+ padding: 3rem;
207
+ }
208
+
209
+ /* Compact variant */
210
+ .file-upload-compact .file-upload-dropzone {
211
+ flex-direction: row;
212
+ min-height: auto;
213
+ padding: 1rem;
214
+ }
215
+
216
+ /* Disabled state */
217
+ :host([disabled]) .file-upload-dropzone {
218
+ opacity: 0.5;
219
+ cursor: not-allowed;
220
+ pointer-events: none;
221
+ }
222
+ `;
223
+
224
+ class ElDmFileUpload extends BaseElement {
225
+ static properties = {
226
+ accept: { type: String, reflect: true },
227
+ multiple: { type: Boolean, reflect: true },
228
+ disabled: { type: Boolean, reflect: true },
229
+ maxSize: { type: Number, reflect: true, attribute: "max-size" },
230
+ maxFiles: { type: Number, reflect: true, attribute: "max-files" },
231
+ showPreview: { type: Boolean, reflect: true, attribute: "show-preview" },
232
+ compact: { type: Boolean, reflect: true },
233
+ size: { type: String, reflect: true }
234
+ };
235
+ _files = [];
236
+ constructor() {
237
+ super();
238
+ this.attachStyles(styles);
239
+ }
240
+ get files() {
241
+ return this._files;
242
+ }
243
+ _getClasses() {
244
+ const classes = ["file-upload"];
245
+ if (this.size && SIZE_CLASSES[this.size]) {
246
+ classes.push(SIZE_CLASSES[this.size]);
247
+ }
248
+ if (this.compact) {
249
+ classes.push("file-upload-compact");
250
+ }
251
+ return classes.join(" ");
252
+ }
253
+ _formatFileSize(bytes) {
254
+ if (bytes === 0)
255
+ return "0 Bytes";
256
+ const k = 1024;
257
+ const sizes = ["Bytes", "KB", "MB", "GB"];
258
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
259
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
260
+ }
261
+ _generateId() {
262
+ return Math.random().toString(36).substring(2, 11);
263
+ }
264
+ _handleDragOver(e) {
265
+ e.preventDefault();
266
+ if (this.disabled)
267
+ return;
268
+ const dropzone = this.shadowRoot?.querySelector(".file-upload-dropzone");
269
+ dropzone?.classList.add("dragging");
270
+ }
271
+ _handleDragLeave(e) {
272
+ e.preventDefault();
273
+ const dropzone = this.shadowRoot?.querySelector(".file-upload-dropzone");
274
+ dropzone?.classList.remove("dragging");
275
+ }
276
+ _handleDrop(e) {
277
+ e.preventDefault();
278
+ if (this.disabled)
279
+ return;
280
+ const dropzone = this.shadowRoot?.querySelector(".file-upload-dropzone");
281
+ dropzone?.classList.remove("dragging");
282
+ const files = e.dataTransfer?.files;
283
+ if (files) {
284
+ this._processFiles(files);
285
+ }
286
+ }
287
+ _handleClick() {
288
+ if (this.disabled)
289
+ return;
290
+ const input = this.shadowRoot?.querySelector(".file-upload-input");
291
+ input?.click();
292
+ }
293
+ _handleInputChange(e) {
294
+ const input = e.target;
295
+ if (input.files) {
296
+ this._processFiles(input.files);
297
+ }
298
+ input.value = "";
299
+ }
300
+ _processFiles(fileList) {
301
+ const newFiles = [];
302
+ for (let i = 0;i < fileList.length; i++) {
303
+ const file = fileList[i];
304
+ if (this.maxFiles && this._files.length + newFiles.length >= this.maxFiles) {
305
+ break;
306
+ }
307
+ if (this.maxSize && file.size > this.maxSize) {
308
+ continue;
309
+ }
310
+ const uploadedFile = {
311
+ file,
312
+ id: this._generateId()
313
+ };
314
+ if (this.showPreview && file.type.startsWith("image/")) {
315
+ uploadedFile.preview = URL.createObjectURL(file);
316
+ }
317
+ newFiles.push(uploadedFile);
318
+ }
319
+ if (this.multiple) {
320
+ this._files = [...this._files, ...newFiles];
321
+ } else {
322
+ this._files.forEach((f) => {
323
+ if (f.preview)
324
+ URL.revokeObjectURL(f.preview);
325
+ });
326
+ this._files = newFiles.slice(0, 1);
327
+ }
328
+ this._updateFileList();
329
+ this.emit("change", { files: this._files.map((f) => f.file) });
330
+ }
331
+ _removeFile(id) {
332
+ const file = this._files.find((f) => f.id === id);
333
+ if (file?.preview) {
334
+ URL.revokeObjectURL(file.preview);
335
+ }
336
+ this._files = this._files.filter((f) => f.id !== id);
337
+ this._updateFileList();
338
+ this.emit("remove", { files: this._files.map((f) => f.file) });
339
+ this.emit("change", { files: this._files.map((f) => f.file) });
340
+ }
341
+ _updateFileList() {
342
+ const listContainer = this.shadowRoot?.querySelector(".file-upload-list");
343
+ const previewContainer = this.shadowRoot?.querySelector(".file-upload-preview");
344
+ if (this.showPreview && previewContainer) {
345
+ previewContainer.innerHTML = this._files.map((f) => `
346
+ <div class="file-upload-preview-item" data-id="${f.id}">
347
+ ${f.preview ? `<img class="file-upload-preview-image" src="${f.preview}" alt="${f.file.name}">` : ""}
348
+ <button class="file-upload-preview-remove" type="button" aria-label="Remove file">
349
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
350
+ <line x1="18" y1="6" x2="6" y2="18"></line>
351
+ <line x1="6" y1="6" x2="18" y2="18"></line>
352
+ </svg>
353
+ </button>
354
+ </div>
355
+ `).join("");
356
+ previewContainer.querySelectorAll(".file-upload-preview-remove").forEach((btn, index) => {
357
+ btn.addEventListener("click", (e) => {
358
+ e.stopPropagation();
359
+ this._removeFile(this._files[index].id);
360
+ });
361
+ });
362
+ } else if (listContainer) {
363
+ listContainer.innerHTML = this._files.map((f) => `
364
+ <div class="file-upload-item" data-id="${f.id}">
365
+ <span class="file-upload-item-icon">
366
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
367
+ <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
368
+ <polyline points="14 2 14 8 20 8"></polyline>
369
+ </svg>
370
+ </span>
371
+ <div class="file-upload-item-info">
372
+ <div class="file-upload-item-name">${f.file.name}</div>
373
+ <div class="file-upload-item-size">${this._formatFileSize(f.file.size)}</div>
374
+ </div>
375
+ <button class="file-upload-item-remove" type="button" aria-label="Remove file">
376
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
377
+ <line x1="18" y1="6" x2="6" y2="18"></line>
378
+ <line x1="6" y1="6" x2="18" y2="18"></line>
379
+ </svg>
380
+ </button>
381
+ </div>
382
+ `).join("");
383
+ listContainer.querySelectorAll(".file-upload-item-remove").forEach((btn, index) => {
384
+ btn.addEventListener("click", (e) => {
385
+ e.stopPropagation();
386
+ this._removeFile(this._files[index].id);
387
+ });
388
+ });
389
+ }
390
+ }
391
+ clear() {
392
+ this._files.forEach((f) => {
393
+ if (f.preview)
394
+ URL.revokeObjectURL(f.preview);
395
+ });
396
+ this._files = [];
397
+ this._updateFileList();
398
+ this.emit("change", { files: [] });
399
+ }
400
+ render() {
401
+ const classes = this._getClasses();
402
+ return `
403
+ <div class="${classes}" part="file-upload">
404
+ <input
405
+ type="file"
406
+ class="file-upload-input"
407
+ ${this.accept ? `accept="${this.accept}"` : ""}
408
+ ${this.multiple ? "multiple" : ""}
409
+ ${this.disabled ? "disabled" : ""}
410
+ >
411
+ <div class="file-upload-dropzone" part="dropzone">
412
+ <span class="file-upload-icon">
413
+ <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
414
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
415
+ <polyline points="17 8 12 3 7 8"></polyline>
416
+ <line x1="12" y1="3" x2="12" y2="15"></line>
417
+ </svg>
418
+ </span>
419
+ <div class="file-upload-text">
420
+ <div class="file-upload-title">
421
+ Drag & drop files here or <span class="file-upload-browse">browse</span>
422
+ </div>
423
+ <div class="file-upload-subtitle">
424
+ ${this.accept ? `Accepted: ${this.accept}` : "All file types accepted"}
425
+ ${this.maxSize ? ` • Max ${this._formatFileSize(this.maxSize)}` : ""}
426
+ </div>
427
+ </div>
428
+ </div>
429
+ ${this.showPreview ? '<div class="file-upload-preview" part="preview"></div>' : '<div class="file-upload-list" part="file-list"></div>'}
430
+ </div>
431
+ `;
432
+ }
433
+ update() {
434
+ super.update();
435
+ const dropzone = this.shadowRoot?.querySelector(".file-upload-dropzone");
436
+ const input = this.shadowRoot?.querySelector(".file-upload-input");
437
+ dropzone?.addEventListener("dragover", (e) => this._handleDragOver(e));
438
+ dropzone?.addEventListener("dragleave", (e) => this._handleDragLeave(e));
439
+ dropzone?.addEventListener("drop", (e) => this._handleDrop(e));
440
+ dropzone?.addEventListener("click", () => this._handleClick());
441
+ input?.addEventListener("change", (e) => this._handleInputChange(e));
442
+ }
443
+ disconnectedCallback() {
444
+ super.disconnectedCallback?.();
445
+ this._files.forEach((f) => {
446
+ if (f.preview)
447
+ URL.revokeObjectURL(f.preview);
448
+ });
449
+ }
450
+ }
451
+
452
+ // src/index.ts
453
+ function register() {
454
+ if (!customElements.get("el-dm-file-upload")) {
455
+ customElements.define("el-dm-file-upload", ElDmFileUpload);
456
+ }
457
+ }
458
+
459
+ // src/register.ts
460
+ register();
461
+
462
+ //# debugId=36890F53CDC0524C64756E2164756E21
463
+ //# sourceMappingURL=register.js.map
@@ -0,0 +1,12 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/el-dm-file-upload.ts", "../../src/index.ts", "../../src/register.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * DuskMoon File Upload Element\n *\n * A drag-and-drop file upload component with file list and preview support.\n * Uses styles from @duskmoon-dev/core for consistent theming.\n *\n * @element el-dm-file-upload\n *\n * @attr {string} accept - File type filter (e.g., \"image/*,.pdf\")\n * @attr {boolean} multiple - Allow multiple files\n * @attr {boolean} disabled - Whether the upload is disabled\n * @attr {number} max-size - Maximum file size in bytes\n * @attr {number} max-files - Maximum number of files\n * @attr {boolean} show-preview - Show image previews\n * @attr {boolean} compact - Use compact layout\n * @attr {string} size - Size: sm, md, lg\n *\n * @slot - Default slot for custom dropzone content\n *\n * @csspart dropzone - The dropzone area\n * @csspart file-list - The file list container\n *\n * @fires change - Fired when files are selected\n * @fires remove - Fired when a file is removed\n */\n\nimport { BaseElement, css } from '@duskmoon-dev/el-core';\nimport { css as fileUploadCSS } from '@duskmoon-dev/core/components/file-upload';\n\nexport type FileUploadSize = 'sm' | 'md' | 'lg';\n\nexport interface UploadedFile {\n file: File;\n id: string;\n preview?: string;\n}\n\nconst SIZE_CLASSES: Record<string, string> = {\n sm: 'file-upload-sm',\n md: '',\n lg: 'file-upload-lg',\n};\n\n// Strip @layer wrapper for Shadow DOM compatibility\nconst coreStyles = fileUploadCSS.replace(/@layer\\s+components\\s*\\{/, '').replace(/\\}\\s*$/, '');\n\nconst styles = css`\n :host {\n display: block;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n\n ${coreStyles}\n\n /* Web component specific adjustments */\n .file-upload {\n font-family: inherit;\n }\n\n .file-upload-dropzone {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n min-height: 150px;\n padding: 2rem;\n border: 2px dashed var(--color-outline, #ccc);\n border-radius: 0.5rem;\n background-color: var(--color-surface-container, #f5f5f5);\n cursor: pointer;\n transition:\n border-color 0.2s,\n background-color 0.2s;\n }\n\n .file-upload-dropzone:hover {\n border-color: var(--color-primary);\n background-color: var(--color-surface-container-high, #e8e8e8);\n }\n\n .file-upload-dropzone.dragging {\n border-color: var(--color-primary);\n background-color: var(--color-primary);\n background-color: rgba(var(--color-primary-rgb, 98, 0, 238), 0.1);\n }\n\n .file-upload-input {\n display: none;\n }\n\n .file-upload-icon {\n font-size: 2.5rem;\n color: var(--color-on-surface);\n opacity: 0.5;\n }\n\n .file-upload-text {\n text-align: center;\n }\n\n .file-upload-title {\n font-size: 1rem;\n font-weight: 500;\n color: var(--color-on-surface);\n }\n\n .file-upload-subtitle {\n font-size: 0.875rem;\n color: var(--color-on-surface);\n opacity: 0.7;\n }\n\n .file-upload-browse {\n color: var(--color-primary);\n text-decoration: underline;\n cursor: pointer;\n }\n\n /* File list */\n .file-upload-list {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n margin-top: 1rem;\n }\n\n .file-upload-item {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.75rem;\n background-color: var(--color-surface-container, #f5f5f5);\n border-radius: 0.375rem;\n }\n\n .file-upload-item-icon {\n font-size: 1.5rem;\n color: var(--color-on-surface);\n opacity: 0.7;\n }\n\n .file-upload-item-info {\n flex: 1;\n min-width: 0;\n }\n\n .file-upload-item-name {\n font-size: 0.875rem;\n font-weight: 500;\n color: var(--color-on-surface);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .file-upload-item-size {\n font-size: 0.75rem;\n color: var(--color-on-surface);\n opacity: 0.7;\n }\n\n .file-upload-item-remove {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 1.5rem;\n height: 1.5rem;\n padding: 0;\n border: none;\n border-radius: 50%;\n background-color: transparent;\n color: var(--color-on-surface);\n opacity: 0.5;\n cursor: pointer;\n transition:\n opacity 0.2s,\n background-color 0.2s;\n }\n\n .file-upload-item-remove:hover {\n opacity: 1;\n background-color: var(--color-error);\n color: var(--color-on-error, white);\n }\n\n /* Preview grid */\n .file-upload-preview {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));\n gap: 0.75rem;\n margin-top: 1rem;\n }\n\n .file-upload-preview-item {\n position: relative;\n aspect-ratio: 1;\n border-radius: 0.375rem;\n overflow: hidden;\n background-color: var(--color-surface-container, #f5f5f5);\n }\n\n .file-upload-preview-image {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n .file-upload-preview-remove {\n position: absolute;\n top: 0.25rem;\n right: 0.25rem;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 1.5rem;\n height: 1.5rem;\n padding: 0;\n border: none;\n border-radius: 50%;\n background-color: rgba(0, 0, 0, 0.5);\n color: white;\n cursor: pointer;\n opacity: 0;\n transition: opacity 0.2s;\n }\n\n .file-upload-preview-item:hover .file-upload-preview-remove {\n opacity: 1;\n }\n\n /* Size variants */\n .file-upload-sm .file-upload-dropzone {\n min-height: 100px;\n padding: 1rem;\n }\n\n .file-upload-lg .file-upload-dropzone {\n min-height: 200px;\n padding: 3rem;\n }\n\n /* Compact variant */\n .file-upload-compact .file-upload-dropzone {\n flex-direction: row;\n min-height: auto;\n padding: 1rem;\n }\n\n /* Disabled state */\n :host([disabled]) .file-upload-dropzone {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n }\n`;\n\nexport class ElDmFileUpload extends BaseElement {\n static properties = {\n accept: { type: String, reflect: true },\n multiple: { type: Boolean, reflect: true },\n disabled: { type: Boolean, reflect: true },\n maxSize: { type: Number, reflect: true, attribute: 'max-size' },\n maxFiles: { type: Number, reflect: true, attribute: 'max-files' },\n showPreview: { type: Boolean, reflect: true, attribute: 'show-preview' },\n compact: { type: Boolean, reflect: true },\n size: { type: String, reflect: true },\n };\n\n declare accept: string;\n declare multiple: boolean;\n declare disabled: boolean;\n declare maxSize: number;\n declare maxFiles: number;\n declare showPreview: boolean;\n declare compact: boolean;\n declare size: FileUploadSize;\n\n private _files: UploadedFile[] = [];\n\n constructor() {\n super();\n this.attachStyles(styles);\n }\n\n get files(): UploadedFile[] {\n return this._files;\n }\n\n private _getClasses(): string {\n const classes = ['file-upload'];\n\n if (this.size && SIZE_CLASSES[this.size]) {\n classes.push(SIZE_CLASSES[this.size]);\n }\n\n if (this.compact) {\n classes.push('file-upload-compact');\n }\n\n return classes.join(' ');\n }\n\n private _formatFileSize(bytes: number): string {\n if (bytes === 0) return '0 Bytes';\n const k = 1024;\n const sizes = ['Bytes', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];\n }\n\n private _generateId(): string {\n return Math.random().toString(36).substring(2, 11);\n }\n\n private _handleDragOver(e: DragEvent): void {\n e.preventDefault();\n if (this.disabled) return;\n const dropzone = this.shadowRoot?.querySelector('.file-upload-dropzone');\n dropzone?.classList.add('dragging');\n }\n\n private _handleDragLeave(e: DragEvent): void {\n e.preventDefault();\n const dropzone = this.shadowRoot?.querySelector('.file-upload-dropzone');\n dropzone?.classList.remove('dragging');\n }\n\n private _handleDrop(e: DragEvent): void {\n e.preventDefault();\n if (this.disabled) return;\n\n const dropzone = this.shadowRoot?.querySelector('.file-upload-dropzone');\n dropzone?.classList.remove('dragging');\n\n const files = e.dataTransfer?.files;\n if (files) {\n this._processFiles(files);\n }\n }\n\n private _handleClick(): void {\n if (this.disabled) return;\n const input = this.shadowRoot?.querySelector('.file-upload-input') as HTMLInputElement;\n input?.click();\n }\n\n private _handleInputChange(e: Event): void {\n const input = e.target as HTMLInputElement;\n if (input.files) {\n this._processFiles(input.files);\n }\n // Reset input so the same file can be selected again\n input.value = '';\n }\n\n private _processFiles(fileList: FileList): void {\n const newFiles: UploadedFile[] = [];\n\n for (let i = 0; i < fileList.length; i++) {\n const file = fileList[i];\n\n // Check max files\n if (this.maxFiles && this._files.length + newFiles.length >= this.maxFiles) {\n break;\n }\n\n // Check file size\n if (this.maxSize && file.size > this.maxSize) {\n continue;\n }\n\n const uploadedFile: UploadedFile = {\n file,\n id: this._generateId(),\n };\n\n // Generate preview for images\n if (this.showPreview && file.type.startsWith('image/')) {\n uploadedFile.preview = URL.createObjectURL(file);\n }\n\n newFiles.push(uploadedFile);\n }\n\n if (this.multiple) {\n this._files = [...this._files, ...newFiles];\n } else {\n // Clean up old previews\n this._files.forEach((f) => {\n if (f.preview) URL.revokeObjectURL(f.preview);\n });\n this._files = newFiles.slice(0, 1);\n }\n\n this._updateFileList();\n this.emit('change', { files: this._files.map((f) => f.file) });\n }\n\n private _removeFile(id: string): void {\n const file = this._files.find((f) => f.id === id);\n if (file?.preview) {\n URL.revokeObjectURL(file.preview);\n }\n\n this._files = this._files.filter((f) => f.id !== id);\n this._updateFileList();\n this.emit('remove', { files: this._files.map((f) => f.file) });\n this.emit('change', { files: this._files.map((f) => f.file) });\n }\n\n private _updateFileList(): void {\n const listContainer = this.shadowRoot?.querySelector('.file-upload-list');\n const previewContainer = this.shadowRoot?.querySelector('.file-upload-preview');\n\n if (this.showPreview && previewContainer) {\n previewContainer.innerHTML = this._files\n .map(\n (f) => `\n <div class=\"file-upload-preview-item\" data-id=\"${f.id}\">\n ${f.preview ? `<img class=\"file-upload-preview-image\" src=\"${f.preview}\" alt=\"${f.file.name}\">` : ''}\n <button class=\"file-upload-preview-remove\" type=\"button\" aria-label=\"Remove file\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\n </svg>\n </button>\n </div>\n `,\n )\n .join('');\n\n previewContainer.querySelectorAll('.file-upload-preview-remove').forEach((btn, index) => {\n btn.addEventListener('click', (e) => {\n e.stopPropagation();\n this._removeFile(this._files[index].id);\n });\n });\n } else if (listContainer) {\n listContainer.innerHTML = this._files\n .map(\n (f) => `\n <div class=\"file-upload-item\" data-id=\"${f.id}\">\n <span class=\"file-upload-item-icon\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"></path>\n <polyline points=\"14 2 14 8 20 8\"></polyline>\n </svg>\n </span>\n <div class=\"file-upload-item-info\">\n <div class=\"file-upload-item-name\">${f.file.name}</div>\n <div class=\"file-upload-item-size\">${this._formatFileSize(f.file.size)}</div>\n </div>\n <button class=\"file-upload-item-remove\" type=\"button\" aria-label=\"Remove file\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\n </svg>\n </button>\n </div>\n `,\n )\n .join('');\n\n listContainer.querySelectorAll('.file-upload-item-remove').forEach((btn, index) => {\n btn.addEventListener('click', (e) => {\n e.stopPropagation();\n this._removeFile(this._files[index].id);\n });\n });\n }\n }\n\n /**\n * Clear all files\n */\n clear(): void {\n this._files.forEach((f) => {\n if (f.preview) URL.revokeObjectURL(f.preview);\n });\n this._files = [];\n this._updateFileList();\n this.emit('change', { files: [] });\n }\n\n render(): string {\n const classes = this._getClasses();\n\n return `\n <div class=\"${classes}\" part=\"file-upload\">\n <input\n type=\"file\"\n class=\"file-upload-input\"\n ${this.accept ? `accept=\"${this.accept}\"` : ''}\n ${this.multiple ? 'multiple' : ''}\n ${this.disabled ? 'disabled' : ''}\n >\n <div class=\"file-upload-dropzone\" part=\"dropzone\">\n <span class=\"file-upload-icon\">\n <svg width=\"40\" height=\"40\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"></path>\n <polyline points=\"17 8 12 3 7 8\"></polyline>\n <line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\"></line>\n </svg>\n </span>\n <div class=\"file-upload-text\">\n <div class=\"file-upload-title\">\n Drag & drop files here or <span class=\"file-upload-browse\">browse</span>\n </div>\n <div class=\"file-upload-subtitle\">\n ${this.accept ? `Accepted: ${this.accept}` : 'All file types accepted'}\n ${this.maxSize ? ` • Max ${this._formatFileSize(this.maxSize)}` : ''}\n </div>\n </div>\n </div>\n ${this.showPreview ? '<div class=\"file-upload-preview\" part=\"preview\"></div>' : '<div class=\"file-upload-list\" part=\"file-list\"></div>'}\n </div>\n `;\n }\n\n update(): void {\n super.update();\n\n const dropzone = this.shadowRoot?.querySelector('.file-upload-dropzone');\n const input = this.shadowRoot?.querySelector('.file-upload-input');\n\n dropzone?.addEventListener('dragover', ((e: DragEvent) =>\n this._handleDragOver(e)) as EventListener);\n dropzone?.addEventListener('dragleave', ((e: DragEvent) =>\n this._handleDragLeave(e)) as EventListener);\n dropzone?.addEventListener('drop', ((e: DragEvent) => this._handleDrop(e)) as EventListener);\n dropzone?.addEventListener('click', () => this._handleClick());\n input?.addEventListener('change', ((e: Event) => this._handleInputChange(e)) as EventListener);\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback?.();\n // Clean up object URLs\n this._files.forEach((f) => {\n if (f.preview) URL.revokeObjectURL(f.preview);\n });\n }\n}\n",
6
+ "/**\n * @duskmoon-dev/el-file-upload\n *\n * DuskMoon File Upload custom element\n */\n\nimport { ElDmFileUpload } from './el-dm-file-upload.js';\n\nexport { ElDmFileUpload };\nexport type { FileUploadSize, UploadedFile } from './el-dm-file-upload.js';\n\n/**\n * Register the el-dm-file-upload custom element\n *\n * @example\n * ```ts\n * import { register } from '@duskmoon-dev/el-file-upload';\n * register();\n * ```\n */\nexport function register(): void {\n if (!customElements.get('el-dm-file-upload')) {\n customElements.define('el-dm-file-upload', ElDmFileUpload);\n }\n}\n",
7
+ "import { register } from './index.js';\nregister();\n"
8
+ ],
9
+ "mappings": "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",
10
+ "debugId": "36890F53CDC0524C64756E2164756E21",
11
+ "names": []
12
+ }