@dso-design-system/ui 0.0.2 → 0.1.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/esm2022/lib/alert/alert.component.mjs +54 -0
- package/esm2022/lib/badge/badge.component.mjs +22 -0
- package/esm2022/lib/breadcrumb/breadcrumb.component.mjs +29 -0
- package/esm2022/lib/checkbox/checkbox.component.mjs +82 -0
- package/esm2022/lib/datepicker/datepicker.component.mjs +161 -0
- package/esm2022/lib/dialog/dialog.component.mjs +25 -0
- package/esm2022/lib/directives/truncate.directive.mjs +71 -0
- package/esm2022/lib/dropdown-list/dropdown-list.component.mjs +89 -0
- package/esm2022/lib/file-upload-items/file-upload-items.component.mjs +65 -0
- package/esm2022/lib/file-upload-multiple/file-upload-multiple.component.mjs +232 -0
- package/esm2022/lib/file-upload-multiple/upload-item.model.mjs +2 -0
- package/esm2022/lib/file-upload-multiple/upload-simulator.service.mjs +76 -0
- package/esm2022/lib/file-upload-single/file-upload-single.component.mjs +100 -0
- package/esm2022/lib/input-text/input-text.component.mjs +93 -0
- package/esm2022/lib/pagination/pagination.component.mjs +115 -0
- package/esm2022/lib/progress-bar/progress-bar.component.mjs +25 -0
- package/esm2022/lib/radio/radio.component.mjs +41 -0
- package/esm2022/lib/select-dropdown/select-dropdown.component.mjs +228 -0
- package/esm2022/lib/service/toast.service.mjs +20 -0
- package/esm2022/lib/side-navigation-bar/side-navigation-bar.component.mjs +113 -0
- package/esm2022/lib/spinner/spinner.component.mjs +60 -0
- package/esm2022/lib/table/table.component.mjs +136 -0
- package/esm2022/lib/tabs/tab.component.mjs +20 -0
- package/esm2022/lib/tabs/tabs.component.mjs +40 -0
- package/esm2022/lib/tag/tag.component.mjs +27 -0
- package/esm2022/lib/text-area/text-area.component.mjs +74 -0
- package/esm2022/lib/toast/toast.component.mjs +36 -0
- package/esm2022/lib/tooltip/tooltip.component.mjs +38 -0
- package/esm2022/lib/tooltip/tooltip.directive.mjs +105 -0
- package/esm2022/lib/top-navigation-bar/top-navigation-bar.component.mjs +24 -0
- package/esm2022/public-api.mjs +27 -2
- package/fesm2022/dso-design-system-ui.mjs +2056 -3
- package/fesm2022/dso-design-system-ui.mjs.map +1 -1
- package/lib/alert/alert.component.d.ts +20 -0
- package/lib/badge/badge.component.d.ts +8 -0
- package/lib/breadcrumb/breadcrumb.component.d.ts +15 -0
- package/lib/checkbox/checkbox.component.d.ts +42 -0
- package/lib/datepicker/datepicker.component.d.ts +48 -0
- package/lib/dialog/dialog.component.d.ts +10 -0
- package/lib/directives/truncate.directive.d.ts +23 -0
- package/lib/dropdown-list/dropdown-list.component.d.ts +33 -0
- package/lib/file-upload-items/file-upload-items.component.d.ts +27 -0
- package/lib/file-upload-multiple/file-upload-multiple.component.d.ts +44 -0
- package/lib/file-upload-multiple/upload-item.model.d.ts +7 -0
- package/lib/file-upload-multiple/upload-simulator.service.d.ts +34 -0
- package/lib/file-upload-single/file-upload-single.component.d.ts +28 -0
- package/lib/input-text/input-text.component.d.ts +24 -0
- package/lib/pagination/pagination.component.d.ts +31 -0
- package/lib/progress-bar/progress-bar.component.d.ts +11 -0
- package/lib/radio/radio.component.d.ts +14 -0
- package/lib/select-dropdown/select-dropdown.component.d.ts +78 -0
- package/lib/service/toast.service.d.ts +16 -0
- package/lib/side-navigation-bar/side-navigation-bar.component.d.ts +74 -0
- package/lib/spinner/spinner.component.d.ts +23 -0
- package/lib/table/table.component.d.ts +43 -0
- package/lib/tabs/tab.component.d.ts +9 -0
- package/lib/tabs/tabs.component.d.ts +15 -0
- package/lib/tag/tag.component.d.ts +10 -0
- package/lib/text-area/text-area.component.d.ts +21 -0
- package/lib/toast/toast.component.d.ts +13 -0
- package/lib/tooltip/tooltip.component.d.ts +15 -0
- package/lib/tooltip/tooltip.directive.d.ts +19 -0
- package/lib/top-navigation-bar/top-navigation-bar.component.d.ts +16 -0
- package/package.json +1 -1
- package/public-api.d.ts +25 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { CommonModule } from '@angular/common';
|
|
2
|
+
import { Component, Input, Output, EventEmitter, HostListener, ViewChild } from '@angular/core';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
import * as i1 from "@angular/common";
|
|
5
|
+
export class DropdownListComponent {
|
|
6
|
+
// -------------------------------
|
|
7
|
+
// ELEMENT REFERENCE
|
|
8
|
+
// -------------------------------
|
|
9
|
+
/** Reference to the root <div> to detect clicks inside/outside dropdown */
|
|
10
|
+
root;
|
|
11
|
+
// -------------------------------
|
|
12
|
+
// INPUT PROPERTIES
|
|
13
|
+
// -------------------------------
|
|
14
|
+
/** Options to display in the dropdown. Each option has a label and a value */
|
|
15
|
+
options = [];
|
|
16
|
+
/** Placeholder text when no option is selected */
|
|
17
|
+
placeholder = 'Select';
|
|
18
|
+
/** Currently selected value */
|
|
19
|
+
value = null;
|
|
20
|
+
/** Disable dropdown interaction */
|
|
21
|
+
disabled = false;
|
|
22
|
+
// -------------------------------
|
|
23
|
+
// OUTPUT EVENTS
|
|
24
|
+
// -------------------------------
|
|
25
|
+
/** Emits the selected value when user clicks an option */
|
|
26
|
+
selectionChange = new EventEmitter();
|
|
27
|
+
// -------------------------------
|
|
28
|
+
// INTERNAL STATE
|
|
29
|
+
// -------------------------------
|
|
30
|
+
/** Tracks whether the dropdown list is open or closed */
|
|
31
|
+
isOpen = false;
|
|
32
|
+
// -------------------------------
|
|
33
|
+
// METHODS
|
|
34
|
+
// -------------------------------
|
|
35
|
+
/** Toggle dropdown open/close when trigger is clicked */
|
|
36
|
+
toggle() {
|
|
37
|
+
if (!this.disabled) {
|
|
38
|
+
this.isOpen = !this.isOpen;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** Handle option selection */
|
|
42
|
+
select(option) {
|
|
43
|
+
this.value = option.value; // Update internal state
|
|
44
|
+
this.selectionChange.emit(this.value); // Notify parent component
|
|
45
|
+
this.isOpen = false; // Close dropdown
|
|
46
|
+
}
|
|
47
|
+
// -------------------------------
|
|
48
|
+
// HANDLE CLICKS OUTSIDE DROPDOWN
|
|
49
|
+
// -------------------------------
|
|
50
|
+
handleOutsideClick(event) {
|
|
51
|
+
if (!this.isOpen)
|
|
52
|
+
return;
|
|
53
|
+
const target = event.target;
|
|
54
|
+
if (!this.root.nativeElement.contains(target)) {
|
|
55
|
+
this.isOpen = false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// -------------------------------
|
|
59
|
+
// HELPER GETTER
|
|
60
|
+
// -------------------------------
|
|
61
|
+
/** Returns label of selected value or placeholder if none selected */
|
|
62
|
+
get selectedLabel() {
|
|
63
|
+
const found = this.options.find(o => o.value === this.value);
|
|
64
|
+
return found ? found.label : this.placeholder;
|
|
65
|
+
}
|
|
66
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DropdownListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
67
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: DropdownListComponent, isStandalone: true, selector: "dso-dropdown-list", inputs: { options: "options", placeholder: "placeholder", value: "value", disabled: "disabled" }, outputs: { selectionChange: "selectionChange" }, host: { listeners: { "document:click": "handleOutsideClick($event)" } }, viewQueries: [{ propertyName: "root", first: true, predicate: ["root"], descendants: true, static: true }], ngImport: i0, template: "<div class=\"dropdown\" #root [class.disabled]=\"disabled\">\r\n <!--\r\n #root: Linked to ViewChild in TS for detecting outside clicks\r\n [class.disabled]: Adds CSS class when dropdown is disabled\r\n -->\r\n\r\n <!-- Trigger area: clicking toggles dropdown open/close -->\r\n <div class=\"dropdown-trigger\" (click)=\"toggle()\">\r\n <!-- Display selected label or placeholder -->\r\n <span>{{ selectedLabel }}</span>\r\n <!-- Arrow icon that rotates when dropdown is open -->\r\n <span class=\"icon\" [class.open]=\"isOpen\">\u25BE</span>\r\n </div>\r\n\r\n <!-- Dropdown menu: visible only when isOpen is true -->\r\n <ul class=\"dropdown-menu\" *ngIf=\"isOpen\">\r\n <!-- Render each option -->\r\n <li \r\n *ngFor=\"let option of options\"\r\n (click)=\"select(option)\"\r\n [class.selected]=\"option.value === value\"> <!-- Highlight selected option -->\r\n {{ option.label }}\r\n </li>\r\n </ul>\r\n</div>\r\n", styles: [".dropdown{position:relative;display:inline-flex;font-family:inherit}.dropdown.disabled{opacity:.5;pointer-events:none}.dropdown-trigger{border:1px solid #ccc;padding:8px 12px;background:#fff;cursor:pointer;display:inline-flex;gap:8px;align-items:center;border-radius:4px}.dropdown-trigger:hover{border-color:#888}.dropdown-menu{position:absolute;top:calc(100% + 4px);left:0;right:0;background:#fff;border:1px solid #ccc;border-radius:4px;list-style:none;padding:4px 0;margin:0;max-height:200px;overflow-y:auto;z-index:100}.dropdown-menu li{padding:8px 12px;cursor:pointer}.dropdown-menu li:hover{background:#f2f2f2}.dropdown-menu li.selected{background:#e6e6e6;font-weight:500}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
|
|
68
|
+
}
|
|
69
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DropdownListComponent, decorators: [{
|
|
70
|
+
type: Component,
|
|
71
|
+
args: [{ selector: 'dso-dropdown-list', standalone: true, imports: [CommonModule], template: "<div class=\"dropdown\" #root [class.disabled]=\"disabled\">\r\n <!--\r\n #root: Linked to ViewChild in TS for detecting outside clicks\r\n [class.disabled]: Adds CSS class when dropdown is disabled\r\n -->\r\n\r\n <!-- Trigger area: clicking toggles dropdown open/close -->\r\n <div class=\"dropdown-trigger\" (click)=\"toggle()\">\r\n <!-- Display selected label or placeholder -->\r\n <span>{{ selectedLabel }}</span>\r\n <!-- Arrow icon that rotates when dropdown is open -->\r\n <span class=\"icon\" [class.open]=\"isOpen\">\u25BE</span>\r\n </div>\r\n\r\n <!-- Dropdown menu: visible only when isOpen is true -->\r\n <ul class=\"dropdown-menu\" *ngIf=\"isOpen\">\r\n <!-- Render each option -->\r\n <li \r\n *ngFor=\"let option of options\"\r\n (click)=\"select(option)\"\r\n [class.selected]=\"option.value === value\"> <!-- Highlight selected option -->\r\n {{ option.label }}\r\n </li>\r\n </ul>\r\n</div>\r\n", styles: [".dropdown{position:relative;display:inline-flex;font-family:inherit}.dropdown.disabled{opacity:.5;pointer-events:none}.dropdown-trigger{border:1px solid #ccc;padding:8px 12px;background:#fff;cursor:pointer;display:inline-flex;gap:8px;align-items:center;border-radius:4px}.dropdown-trigger:hover{border-color:#888}.dropdown-menu{position:absolute;top:calc(100% + 4px);left:0;right:0;background:#fff;border:1px solid #ccc;border-radius:4px;list-style:none;padding:4px 0;margin:0;max-height:200px;overflow-y:auto;z-index:100}.dropdown-menu li{padding:8px 12px;cursor:pointer}.dropdown-menu li:hover{background:#f2f2f2}.dropdown-menu li.selected{background:#e6e6e6;font-weight:500}\n"] }]
|
|
72
|
+
}], propDecorators: { root: [{
|
|
73
|
+
type: ViewChild,
|
|
74
|
+
args: ['root', { static: true }]
|
|
75
|
+
}], options: [{
|
|
76
|
+
type: Input
|
|
77
|
+
}], placeholder: [{
|
|
78
|
+
type: Input
|
|
79
|
+
}], value: [{
|
|
80
|
+
type: Input
|
|
81
|
+
}], disabled: [{
|
|
82
|
+
type: Input
|
|
83
|
+
}], selectionChange: [{
|
|
84
|
+
type: Output
|
|
85
|
+
}], handleOutsideClick: [{
|
|
86
|
+
type: HostListener,
|
|
87
|
+
args: ['document:click', ['$event']]
|
|
88
|
+
}] } });
|
|
89
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHJvcGRvd24tbGlzdC5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy91aS9zcmMvbGliL2Ryb3Bkb3duLWxpc3QvZHJvcGRvd24tbGlzdC5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy91aS9zcmMvbGliL2Ryb3Bkb3duLWxpc3QvZHJvcGRvd24tbGlzdC5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDL0MsT0FBTyxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLFlBQVksRUFBRSxZQUFZLEVBQWMsU0FBUyxFQUFFLE1BQU0sZUFBZSxDQUFDOzs7QUFTNUcsTUFBTSxPQUFPLHFCQUFxQjtJQUVoQyxrQ0FBa0M7SUFDbEMsb0JBQW9CO0lBQ3BCLGtDQUFrQztJQUNsQywyRUFBMkU7SUFDdEMsSUFBSSxDQUFjO0lBRXZELGtDQUFrQztJQUNsQyxtQkFBbUI7SUFDbkIsa0NBQWtDO0lBQ2xDLDhFQUE4RTtJQUNyRSxPQUFPLEdBQW9DLEVBQUUsQ0FBQztJQUV2RCxrREFBa0Q7SUFDekMsV0FBVyxHQUFHLFFBQVEsQ0FBQztJQUVoQywrQkFBK0I7SUFDdEIsS0FBSyxHQUFRLElBQUksQ0FBQztJQUUzQixtQ0FBbUM7SUFDMUIsUUFBUSxHQUFHLEtBQUssQ0FBQztJQUUxQixrQ0FBa0M7SUFDbEMsZ0JBQWdCO0lBQ2hCLGtDQUFrQztJQUNsQywwREFBMEQ7SUFDaEQsZUFBZSxHQUFHLElBQUksWUFBWSxFQUFPLENBQUM7SUFFcEQsa0NBQWtDO0lBQ2xDLGlCQUFpQjtJQUNqQixrQ0FBa0M7SUFDbEMseURBQXlEO0lBQ3pELE1BQU0sR0FBRyxLQUFLLENBQUM7SUFFZixrQ0FBa0M7SUFDbEMsVUFBVTtJQUNWLGtDQUFrQztJQUVsQyx5REFBeUQ7SUFDekQsTUFBTTtRQUNKLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUM7WUFDbkIsSUFBSSxDQUFDLE1BQU0sR0FBRyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUM7UUFDN0IsQ0FBQztJQUNILENBQUM7SUFFRCw4QkFBOEI7SUFDOUIsTUFBTSxDQUFDLE1BQXFDO1FBQzFDLElBQUksQ0FBQyxLQUFLLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFlLHdCQUF3QjtRQUNqRSxJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBRywwQkFBMEI7UUFDbkUsSUFBSSxDQUFDLE1BQU0sR0FBRyxLQUFLLENBQUMsQ0FBcUIsaUJBQWlCO0lBQzVELENBQUM7SUFFRCxrQ0FBa0M7SUFDbEMsaUNBQWlDO0lBQ2pDLGtDQUFrQztJQUVsQyxrQkFBa0IsQ0FBQyxLQUFZO1FBQzdCLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTTtZQUFFLE9BQU87UUFFekIsTUFBTSxNQUFNLEdBQUcsS0FBSyxDQUFDLE1BQXFCLENBQUM7UUFDM0MsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO1lBQzlDLElBQUksQ0FBQyxNQUFNLEdBQUcsS0FBSyxDQUFDO1FBQ3RCLENBQUM7SUFDSCxDQUFDO0lBRUQsa0NBQWtDO0lBQ2xDLGdCQUFnQjtJQUNoQixrQ0FBa0M7SUFDbEMsc0VBQXNFO0lBQ3RFLElBQUksYUFBYTtRQUNmLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLEtBQUssS0FBSyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDN0QsT0FBTyxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUM7SUFDaEQsQ0FBQzt3R0F6RVUscUJBQXFCOzRGQUFyQixxQkFBcUIscVpDVmxDLCs4QkF5QkEsZ3VCRGpCWSxZQUFZOzs0RkFFWCxxQkFBcUI7a0JBUGpDLFNBQVM7K0JBQ0UsbUJBQW1CLGNBR2pCLElBQUksV0FDUCxDQUFDLFlBQVksQ0FBQzs4QkFRYyxJQUFJO3NCQUF4QyxTQUFTO3VCQUFDLE1BQU0sRUFBRSxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUU7Z0JBTTFCLE9BQU87c0JBQWYsS0FBSztnQkFHRyxXQUFXO3NCQUFuQixLQUFLO2dCQUdHLEtBQUs7c0JBQWIsS0FBSztnQkFHRyxRQUFRO3NCQUFoQixLQUFLO2dCQU1JLGVBQWU7c0JBQXhCLE1BQU07Z0JBOEJQLGtCQUFrQjtzQkFEakIsWUFBWTt1QkFBQyxnQkFBZ0IsRUFBRSxDQUFDLFFBQVEsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbW1vbk1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XHJcbmltcG9ydCB7IENvbXBvbmVudCwgSW5wdXQsIE91dHB1dCwgRXZlbnRFbWl0dGVyLCBIb3N0TGlzdGVuZXIsIEVsZW1lbnRSZWYsIFZpZXdDaGlsZCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xyXG5cclxuQENvbXBvbmVudCh7XHJcbiAgc2VsZWN0b3I6ICdkc28tZHJvcGRvd24tbGlzdCcsXHJcbiAgdGVtcGxhdGVVcmw6ICcuL2Ryb3Bkb3duLWxpc3QuY29tcG9uZW50Lmh0bWwnLFxyXG4gIHN0eWxlVXJsczogWycuL2Ryb3Bkb3duLWxpc3QuY29tcG9uZW50LnNjc3MnXSxcclxuICBzdGFuZGFsb25lOiB0cnVlLFxyXG4gIGltcG9ydHM6IFtDb21tb25Nb2R1bGVdXHJcbn0pXHJcbmV4cG9ydCBjbGFzcyBEcm9wZG93bkxpc3RDb21wb25lbnQge1xyXG5cclxuICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcbiAgLy8gRUxFTUVOVCBSRUZFUkVOQ0VcclxuICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcbiAgLyoqIFJlZmVyZW5jZSB0byB0aGUgcm9vdCA8ZGl2PiB0byBkZXRlY3QgY2xpY2tzIGluc2lkZS9vdXRzaWRlIGRyb3Bkb3duICovXHJcbiAgQFZpZXdDaGlsZCgncm9vdCcsIHsgc3RhdGljOiB0cnVlIH0pIHJvb3QhOiBFbGVtZW50UmVmO1xyXG5cclxuICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcbiAgLy8gSU5QVVQgUFJPUEVSVElFU1xyXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuICAvKiogT3B0aW9ucyB0byBkaXNwbGF5IGluIHRoZSBkcm9wZG93bi4gRWFjaCBvcHRpb24gaGFzIGEgbGFiZWwgYW5kIGEgdmFsdWUgKi9cclxuICBASW5wdXQoKSBvcHRpb25zOiB7IGxhYmVsOiBzdHJpbmc7IHZhbHVlOiBhbnkgfVtdID0gW107XHJcblxyXG4gIC8qKiBQbGFjZWhvbGRlciB0ZXh0IHdoZW4gbm8gb3B0aW9uIGlzIHNlbGVjdGVkICovXHJcbiAgQElucHV0KCkgcGxhY2Vob2xkZXIgPSAnU2VsZWN0JztcclxuXHJcbiAgLyoqIEN1cnJlbnRseSBzZWxlY3RlZCB2YWx1ZSAqL1xyXG4gIEBJbnB1dCgpIHZhbHVlOiBhbnkgPSBudWxsO1xyXG5cclxuICAvKiogRGlzYWJsZSBkcm9wZG93biBpbnRlcmFjdGlvbiAqL1xyXG4gIEBJbnB1dCgpIGRpc2FibGVkID0gZmFsc2U7XHJcblxyXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuICAvLyBPVVRQVVQgRVZFTlRTXHJcbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG4gIC8qKiBFbWl0cyB0aGUgc2VsZWN0ZWQgdmFsdWUgd2hlbiB1c2VyIGNsaWNrcyBhbiBvcHRpb24gKi9cclxuICBAT3V0cHV0KCkgc2VsZWN0aW9uQ2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjxhbnk+KCk7XHJcblxyXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuICAvLyBJTlRFUk5BTCBTVEFURVxyXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuICAvKiogVHJhY2tzIHdoZXRoZXIgdGhlIGRyb3Bkb3duIGxpc3QgaXMgb3BlbiBvciBjbG9zZWQgKi9cclxuICBpc09wZW4gPSBmYWxzZTtcclxuXHJcbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG4gIC8vIE1FVEhPRFNcclxuICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcblxyXG4gIC8qKiBUb2dnbGUgZHJvcGRvd24gb3Blbi9jbG9zZSB3aGVuIHRyaWdnZXIgaXMgY2xpY2tlZCAqL1xyXG4gIHRvZ2dsZSgpOiB2b2lkIHtcclxuICAgIGlmICghdGhpcy5kaXNhYmxlZCkge1xyXG4gICAgICB0aGlzLmlzT3BlbiA9ICF0aGlzLmlzT3BlbjtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8qKiBIYW5kbGUgb3B0aW9uIHNlbGVjdGlvbiAqL1xyXG4gIHNlbGVjdChvcHRpb246IHsgbGFiZWw6IHN0cmluZzsgdmFsdWU6IGFueSB9KTogdm9pZCB7XHJcbiAgICB0aGlzLnZhbHVlID0gb3B0aW9uLnZhbHVlOyAgICAgICAgICAgICAgIC8vIFVwZGF0ZSBpbnRlcm5hbCBzdGF0ZVxyXG4gICAgdGhpcy5zZWxlY3Rpb25DaGFuZ2UuZW1pdCh0aGlzLnZhbHVlKTsgICAvLyBOb3RpZnkgcGFyZW50IGNvbXBvbmVudFxyXG4gICAgdGhpcy5pc09wZW4gPSBmYWxzZTsgICAgICAgICAgICAgICAgICAgICAvLyBDbG9zZSBkcm9wZG93blxyXG4gIH1cclxuXHJcbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG4gIC8vIEhBTkRMRSBDTElDS1MgT1VUU0lERSBEUk9QRE9XTlxyXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuICBASG9zdExpc3RlbmVyKCdkb2N1bWVudDpjbGljaycsIFsnJGV2ZW50J10pXHJcbiAgaGFuZGxlT3V0c2lkZUNsaWNrKGV2ZW50OiBFdmVudCk6IHZvaWQge1xyXG4gICAgaWYgKCF0aGlzLmlzT3BlbikgcmV0dXJuO1xyXG5cclxuICAgIGNvbnN0IHRhcmdldCA9IGV2ZW50LnRhcmdldCBhcyBIVE1MRWxlbWVudDtcclxuICAgIGlmICghdGhpcy5yb290Lm5hdGl2ZUVsZW1lbnQuY29udGFpbnModGFyZ2V0KSkge1xyXG4gICAgICB0aGlzLmlzT3BlbiA9IGZhbHNlO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG4gIC8vIEhFTFBFUiBHRVRURVJcclxuICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcbiAgLyoqIFJldHVybnMgbGFiZWwgb2Ygc2VsZWN0ZWQgdmFsdWUgb3IgcGxhY2Vob2xkZXIgaWYgbm9uZSBzZWxlY3RlZCAqL1xyXG4gIGdldCBzZWxlY3RlZExhYmVsKCk6IHN0cmluZyB7XHJcbiAgICBjb25zdCBmb3VuZCA9IHRoaXMub3B0aW9ucy5maW5kKG8gPT4gby52YWx1ZSA9PT0gdGhpcy52YWx1ZSk7XHJcbiAgICByZXR1cm4gZm91bmQgPyBmb3VuZC5sYWJlbCA6IHRoaXMucGxhY2Vob2xkZXI7XHJcbiAgfVxyXG59XHJcbiIsIjxkaXYgY2xhc3M9XCJkcm9wZG93blwiICNyb290IFtjbGFzcy5kaXNhYmxlZF09XCJkaXNhYmxlZFwiPlxyXG4gIDwhLS1cclxuICAgICNyb290OiBMaW5rZWQgdG8gVmlld0NoaWxkIGluIFRTIGZvciBkZXRlY3Rpbmcgb3V0c2lkZSBjbGlja3NcclxuICAgIFtjbGFzcy5kaXNhYmxlZF06IEFkZHMgQ1NTIGNsYXNzIHdoZW4gZHJvcGRvd24gaXMgZGlzYWJsZWRcclxuICAtLT5cclxuXHJcbiAgPCEtLSBUcmlnZ2VyIGFyZWE6IGNsaWNraW5nIHRvZ2dsZXMgZHJvcGRvd24gb3Blbi9jbG9zZSAtLT5cclxuICA8ZGl2IGNsYXNzPVwiZHJvcGRvd24tdHJpZ2dlclwiIChjbGljayk9XCJ0b2dnbGUoKVwiPlxyXG4gICAgPCEtLSBEaXNwbGF5IHNlbGVjdGVkIGxhYmVsIG9yIHBsYWNlaG9sZGVyIC0tPlxyXG4gICAgPHNwYW4+e3sgc2VsZWN0ZWRMYWJlbCB9fTwvc3Bhbj5cclxuICAgIDwhLS0gQXJyb3cgaWNvbiB0aGF0IHJvdGF0ZXMgd2hlbiBkcm9wZG93biBpcyBvcGVuIC0tPlxyXG4gICAgPHNwYW4gY2xhc3M9XCJpY29uXCIgW2NsYXNzLm9wZW5dPVwiaXNPcGVuXCI+4pa+PC9zcGFuPlxyXG4gIDwvZGl2PlxyXG5cclxuICA8IS0tIERyb3Bkb3duIG1lbnU6IHZpc2libGUgb25seSB3aGVuIGlzT3BlbiBpcyB0cnVlIC0tPlxyXG4gIDx1bCBjbGFzcz1cImRyb3Bkb3duLW1lbnVcIiAqbmdJZj1cImlzT3BlblwiPlxyXG4gICAgPCEtLSBSZW5kZXIgZWFjaCBvcHRpb24gLS0+XHJcbiAgICA8bGkgXHJcbiAgICAgICpuZ0Zvcj1cImxldCBvcHRpb24gb2Ygb3B0aW9uc1wiXHJcbiAgICAgIChjbGljayk9XCJzZWxlY3Qob3B0aW9uKVwiXHJcbiAgICAgIFtjbGFzcy5zZWxlY3RlZF09XCJvcHRpb24udmFsdWUgPT09IHZhbHVlXCI+IDwhLS0gSGlnaGxpZ2h0IHNlbGVjdGVkIG9wdGlvbiAtLT5cclxuICAgICAge3sgb3B0aW9uLmxhYmVsIH19XHJcbiAgICA8L2xpPlxyXG4gIDwvdWw+XHJcbjwvZGl2PlxyXG4iXX0=
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import { ButtonComponent } from '../button/button.component';
|
|
4
|
+
import { IconComponent } from '../icon/icon.component';
|
|
5
|
+
import { ProgressBarComponent } from '../progress-bar/progress-bar.component';
|
|
6
|
+
import * as i0 from "@angular/core";
|
|
7
|
+
import * as i1 from "@angular/common";
|
|
8
|
+
export class FileUploadItemComponent {
|
|
9
|
+
/** The file upload item to display */
|
|
10
|
+
item;
|
|
11
|
+
/** Emitted when the user retries a failed upload */
|
|
12
|
+
retry = new EventEmitter();
|
|
13
|
+
/** Emitted when the user removes the file from the list */
|
|
14
|
+
remove = new EventEmitter();
|
|
15
|
+
/** Emitted when the user cancels an ongoing upload */
|
|
16
|
+
cancel = new EventEmitter();
|
|
17
|
+
/**
|
|
18
|
+
* Format the file size into a human-readable string
|
|
19
|
+
* @param size - size in bytes
|
|
20
|
+
* @returns formatted string like '12.3 KB', '1.5 MB'
|
|
21
|
+
*/
|
|
22
|
+
formatSize(size) {
|
|
23
|
+
if (size < 1024)
|
|
24
|
+
return size + ' B';
|
|
25
|
+
if (size < 1024 * 1024)
|
|
26
|
+
return (size / 1024).toFixed(1) + ' KB';
|
|
27
|
+
return (size / (1024 * 1024)).toFixed(1) + ' MB';
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Get an icon name for the file based on its type or extension
|
|
31
|
+
* @param item - the UploadItem
|
|
32
|
+
* @returns icon name string
|
|
33
|
+
*/
|
|
34
|
+
getFileIcon(item) {
|
|
35
|
+
const file = item.file;
|
|
36
|
+
const type = file.type.toLowerCase();
|
|
37
|
+
const name = file.name.toLowerCase();
|
|
38
|
+
// Image MIME types
|
|
39
|
+
if (type.startsWith('image/')) {
|
|
40
|
+
return 'icon-gallery';
|
|
41
|
+
}
|
|
42
|
+
// Image file extensions fallback
|
|
43
|
+
const imageExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp', '.svg'];
|
|
44
|
+
if (imageExtensions.some(ext => name.endsWith(ext))) {
|
|
45
|
+
return 'icon-gallery';
|
|
46
|
+
}
|
|
47
|
+
// Default icon for non-image files
|
|
48
|
+
return 'icon-van';
|
|
49
|
+
}
|
|
50
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FileUploadItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
51
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FileUploadItemComponent, isStandalone: true, selector: "dso-file-item", inputs: { item: "item" }, outputs: { retry: "retry", remove: "remove", cancel: "cancel" }, ngImport: i0, template: "<div class=\"file-row\">\r\n\r\n <!-- LEFT SECTION: Thumbnail + filename -->\r\n <div class=\"thumbnail-filename-wrapper\">\r\n <div class=\"file-thumb\">\r\n <dso-icon \r\n [iconName]=\"getFileIcon(item)\"\r\n size=\"small\"\r\n class=\"thumb-icon\">\r\n </dso-icon>\r\n </div>\r\n\r\n <div class=\"file-info\">\r\n <div class=\"file-name\">{{ item.file.name }}</div>\r\n <div class=\"file-size\">{{ formatSize(item.file.size) }}</div>\r\n </div>\r\n </div>\r\n\r\n\r\n <!-- MIDDLE SECTION: Progress or status -->\r\n <div class=\"filecontent-loader-wrapper\">\r\n \r\n <!-- Progress bar -->\r\n <div class=\"file-progress\" *ngIf=\"!item.hideProgressBar\">\r\n <dso-progress-bar\r\n [value]=\"\r\n item.status === 'uploading' ? item.progress :\r\n item.status === 'completed' ? 100 :\r\n null\r\n \"\r\n [color]=\"item.status === 'completed' ? 'success' : 'primary'\"\r\n ></dso-progress-bar>\r\n </div>\r\n\r\n <!-- Status text -->\r\n <div class=\"file-status\" *ngIf=\"item.hideProgressBar\">\r\n\r\n <!-- Success -->\r\n <div *ngIf=\"item.status === 'completed'\" class=\"status success\">\r\n <dso-icon iconName=\"icon-van\" size=\"small\"></dso-icon>\r\n <span>Uploaded</span>\r\n </div>\r\n\r\n <!-- Failed -->\r\n <div *ngIf=\"item.status === 'failed'\" class=\"status error\">\r\n <dso-icon iconName=\"icon-center-align\" size=\"small\"></dso-icon>\r\n <span>Upload failed</span>\r\n </div>\r\n\r\n </div>\r\n\r\n <!-- RIGHT SECTION: Action buttons -->\r\n <div class=\"file-actions\">\r\n\r\n <!-- Uploading -->\r\n <ng-container *ngIf=\"item.status === 'uploading'\">\r\n <dso-button\r\n btnLabel=\"Cancel\"\r\n btnType=\"text\"\r\n (onClick)=\"cancel.emit()\">\r\n </dso-button>\r\n </ng-container>\r\n\r\n <!-- Failed -->\r\n <ng-container *ngIf=\"item.status === 'failed'\">\r\n <dso-button\r\n btnLabel=\"Retry\"\r\n btnType=\"text\"\r\n (onClick)=\"retry.emit()\">\r\n </dso-button>\r\n\r\n <dso-button\r\n btnLabel=\"Remove\"\r\n btnType=\"text\"\r\n (onClick)=\"remove.emit()\">\r\n </dso-button>\r\n </ng-container>\r\n\r\n <!-- Completed -->\r\n <ng-container *ngIf=\"item.status === 'completed'\">\r\n <dso-button\r\n btnLabel=\"Remove\"\r\n btnType=\"text\"\r\n (onClick)=\"remove.emit()\">\r\n </dso-button>\r\n </ng-container>\r\n\r\n </div>\r\n\r\n </div>\r\n\r\n</div>\r\n", styles: [".file-row{display:flex;justify-content:space-between;align-items:center;padding:.75rem 1rem;background:#fafafa;border-radius:4px}.file-info{display:flex;flex-direction:column}.file-info .file-name{font-weight:300}.file-info .file-size{font-size:.85rem;color:#666}.file-progress{padding-right:1rem;flex:1 0 0;display:flex;justify-content:center;align-items:center;min-width:260px;max-width:320px}.file-actions{display:flex;width:160px;gap:.5rem;justify-content:flex-end}.file-status{display:flex;align-items:center;font-size:14px;flex:1 0 0;min-width:260px;max-width:320px}.file-status .status{display:flex;align-items:center;gap:6px;font-weight:500}.file-status .status.success{color:#16a34a}.file-status .status.error{color:#dc2626}.file-status .status .status-icon{width:16px;height:16px}.file-thumb{width:40px;height:40px;flex-shrink:0;border-radius:6px;overflow:hidden;background:#f3f4f6;display:flex;align-items:center;justify-content:center}.file-thumb img{width:100%;height:100%;object-fit:cover}.thumbnail-filename-wrapper{display:flex;gap:24px}.filecontent-loader-wrapper{display:flex;gap:20px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: ButtonComponent, selector: "dso-button", inputs: ["btnLabel", "btnType", "btnSize", "btnIconName", "isDisabled", "isActive"], outputs: ["onClick"] }, { kind: "component", type: IconComponent, selector: "dso-icon", inputs: ["iconName", "size"] }, { kind: "component", type: ProgressBarComponent, selector: "dso-progress-bar", inputs: ["value", "color", "animated"] }] });
|
|
52
|
+
}
|
|
53
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FileUploadItemComponent, decorators: [{
|
|
54
|
+
type: Component,
|
|
55
|
+
args: [{ selector: 'dso-file-item', standalone: true, imports: [CommonModule, ButtonComponent, IconComponent, ProgressBarComponent], template: "<div class=\"file-row\">\r\n\r\n <!-- LEFT SECTION: Thumbnail + filename -->\r\n <div class=\"thumbnail-filename-wrapper\">\r\n <div class=\"file-thumb\">\r\n <dso-icon \r\n [iconName]=\"getFileIcon(item)\"\r\n size=\"small\"\r\n class=\"thumb-icon\">\r\n </dso-icon>\r\n </div>\r\n\r\n <div class=\"file-info\">\r\n <div class=\"file-name\">{{ item.file.name }}</div>\r\n <div class=\"file-size\">{{ formatSize(item.file.size) }}</div>\r\n </div>\r\n </div>\r\n\r\n\r\n <!-- MIDDLE SECTION: Progress or status -->\r\n <div class=\"filecontent-loader-wrapper\">\r\n \r\n <!-- Progress bar -->\r\n <div class=\"file-progress\" *ngIf=\"!item.hideProgressBar\">\r\n <dso-progress-bar\r\n [value]=\"\r\n item.status === 'uploading' ? item.progress :\r\n item.status === 'completed' ? 100 :\r\n null\r\n \"\r\n [color]=\"item.status === 'completed' ? 'success' : 'primary'\"\r\n ></dso-progress-bar>\r\n </div>\r\n\r\n <!-- Status text -->\r\n <div class=\"file-status\" *ngIf=\"item.hideProgressBar\">\r\n\r\n <!-- Success -->\r\n <div *ngIf=\"item.status === 'completed'\" class=\"status success\">\r\n <dso-icon iconName=\"icon-van\" size=\"small\"></dso-icon>\r\n <span>Uploaded</span>\r\n </div>\r\n\r\n <!-- Failed -->\r\n <div *ngIf=\"item.status === 'failed'\" class=\"status error\">\r\n <dso-icon iconName=\"icon-center-align\" size=\"small\"></dso-icon>\r\n <span>Upload failed</span>\r\n </div>\r\n\r\n </div>\r\n\r\n <!-- RIGHT SECTION: Action buttons -->\r\n <div class=\"file-actions\">\r\n\r\n <!-- Uploading -->\r\n <ng-container *ngIf=\"item.status === 'uploading'\">\r\n <dso-button\r\n btnLabel=\"Cancel\"\r\n btnType=\"text\"\r\n (onClick)=\"cancel.emit()\">\r\n </dso-button>\r\n </ng-container>\r\n\r\n <!-- Failed -->\r\n <ng-container *ngIf=\"item.status === 'failed'\">\r\n <dso-button\r\n btnLabel=\"Retry\"\r\n btnType=\"text\"\r\n (onClick)=\"retry.emit()\">\r\n </dso-button>\r\n\r\n <dso-button\r\n btnLabel=\"Remove\"\r\n btnType=\"text\"\r\n (onClick)=\"remove.emit()\">\r\n </dso-button>\r\n </ng-container>\r\n\r\n <!-- Completed -->\r\n <ng-container *ngIf=\"item.status === 'completed'\">\r\n <dso-button\r\n btnLabel=\"Remove\"\r\n btnType=\"text\"\r\n (onClick)=\"remove.emit()\">\r\n </dso-button>\r\n </ng-container>\r\n\r\n </div>\r\n\r\n </div>\r\n\r\n</div>\r\n", styles: [".file-row{display:flex;justify-content:space-between;align-items:center;padding:.75rem 1rem;background:#fafafa;border-radius:4px}.file-info{display:flex;flex-direction:column}.file-info .file-name{font-weight:300}.file-info .file-size{font-size:.85rem;color:#666}.file-progress{padding-right:1rem;flex:1 0 0;display:flex;justify-content:center;align-items:center;min-width:260px;max-width:320px}.file-actions{display:flex;width:160px;gap:.5rem;justify-content:flex-end}.file-status{display:flex;align-items:center;font-size:14px;flex:1 0 0;min-width:260px;max-width:320px}.file-status .status{display:flex;align-items:center;gap:6px;font-weight:500}.file-status .status.success{color:#16a34a}.file-status .status.error{color:#dc2626}.file-status .status .status-icon{width:16px;height:16px}.file-thumb{width:40px;height:40px;flex-shrink:0;border-radius:6px;overflow:hidden;background:#f3f4f6;display:flex;align-items:center;justify-content:center}.file-thumb img{width:100%;height:100%;object-fit:cover}.thumbnail-filename-wrapper{display:flex;gap:24px}.filecontent-loader-wrapper{display:flex;gap:20px}\n"] }]
|
|
56
|
+
}], propDecorators: { item: [{
|
|
57
|
+
type: Input
|
|
58
|
+
}], retry: [{
|
|
59
|
+
type: Output
|
|
60
|
+
}], remove: [{
|
|
61
|
+
type: Output
|
|
62
|
+
}], cancel: [{
|
|
63
|
+
type: Output
|
|
64
|
+
}] } });
|
|
65
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmlsZS11cGxvYWQtaXRlbXMuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvdWkvc3JjL2xpYi9maWxlLXVwbG9hZC1pdGVtcy9maWxlLXVwbG9hZC1pdGVtcy5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy91aS9zcmMvbGliL2ZpbGUtdXBsb2FkLWl0ZW1zL2ZpbGUtdXBsb2FkLWl0ZW1zLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQUUsWUFBWSxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDdkUsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBRS9DLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUM3RCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDdkQsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sd0NBQXdDLENBQUM7OztBQVM5RSxNQUFNLE9BQU8sdUJBQXVCO0lBRWxDLHNDQUFzQztJQUM3QixJQUFJLENBQWM7SUFFM0Isb0RBQW9EO0lBQzFDLEtBQUssR0FBRyxJQUFJLFlBQVksRUFBUSxDQUFDO0lBRTNDLDJEQUEyRDtJQUNqRCxNQUFNLEdBQUcsSUFBSSxZQUFZLEVBQVEsQ0FBQztJQUU1QyxzREFBc0Q7SUFDNUMsTUFBTSxHQUFHLElBQUksWUFBWSxFQUFRLENBQUM7SUFFNUM7Ozs7T0FJRztJQUNILFVBQVUsQ0FBQyxJQUFZO1FBQ3JCLElBQUksSUFBSSxHQUFHLElBQUk7WUFBRSxPQUFPLElBQUksR0FBRyxJQUFJLENBQUM7UUFDcEMsSUFBSSxJQUFJLEdBQUcsSUFBSSxHQUFHLElBQUk7WUFBRSxPQUFPLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsR0FBRyxLQUFLLENBQUM7UUFDaEUsT0FBTyxDQUFDLElBQUksR0FBRyxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsR0FBRyxLQUFLLENBQUM7SUFDbkQsQ0FBQztJQUVEOzs7O09BSUc7SUFDSCxXQUFXLENBQUMsSUFBZ0I7UUFDMUIsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQztRQUN2QixNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQ3JDLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7UUFFckMsbUJBQW1CO1FBQ25CLElBQUksSUFBSSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO1lBQzlCLE9BQU8sY0FBYyxDQUFDO1FBQ3hCLENBQUM7UUFFRCxpQ0FBaUM7UUFDakMsTUFBTSxlQUFlLEdBQUcsQ0FBQyxNQUFNLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLENBQUMsQ0FBQztRQUNuRixJQUFJLGVBQWUsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUNwRCxPQUFPLGNBQWMsQ0FBQztRQUN4QixDQUFDO1FBRUQsbUNBQW1DO1FBQ25DLE9BQU8sVUFBVSxDQUFDO0lBQ3BCLENBQUM7d0dBaERVLHVCQUF1Qjs0RkFBdkIsdUJBQXVCLG9LQ2RwQyw0bEZBNEZBLDBvQ0RsRlksWUFBWSxtSUFBRSxlQUFlLGtLQUFFLGFBQWEsbUZBQUUsb0JBQW9COzs0RkFJakUsdUJBQXVCO2tCQVBuQyxTQUFTOytCQUNFLGVBQWUsY0FDYixJQUFJLFdBQ1AsQ0FBQyxZQUFZLEVBQUUsZUFBZSxFQUFFLGFBQWEsRUFBRSxvQkFBb0IsQ0FBQzs4QkFPcEUsSUFBSTtzQkFBWixLQUFLO2dCQUdJLEtBQUs7c0JBQWQsTUFBTTtnQkFHRyxNQUFNO3NCQUFmLE1BQU07Z0JBR0csTUFBTTtzQkFBZixNQUFNIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50LCBFdmVudEVtaXR0ZXIsIElucHV0LCBPdXRwdXQgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcclxuaW1wb3J0IHsgQ29tbW9uTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uJztcclxuaW1wb3J0IHsgVXBsb2FkSXRlbSB9IGZyb20gJy4uL2ZpbGUtdXBsb2FkLW11bHRpcGxlL3VwbG9hZC1pdGVtLm1vZGVsJztcclxuaW1wb3J0IHsgQnV0dG9uQ29tcG9uZW50IH0gZnJvbSAnLi4vYnV0dG9uL2J1dHRvbi5jb21wb25lbnQnO1xyXG5pbXBvcnQgeyBJY29uQ29tcG9uZW50IH0gZnJvbSAnLi4vaWNvbi9pY29uLmNvbXBvbmVudCc7XHJcbmltcG9ydCB7IFByb2dyZXNzQmFyQ29tcG9uZW50IH0gZnJvbSAnLi4vcHJvZ3Jlc3MtYmFyL3Byb2dyZXNzLWJhci5jb21wb25lbnQnO1xyXG5cclxuQENvbXBvbmVudCh7XHJcbiAgc2VsZWN0b3I6ICdkc28tZmlsZS1pdGVtJyxcclxuICBzdGFuZGFsb25lOiB0cnVlLFxyXG4gIGltcG9ydHM6IFtDb21tb25Nb2R1bGUsIEJ1dHRvbkNvbXBvbmVudCwgSWNvbkNvbXBvbmVudCwgUHJvZ3Jlc3NCYXJDb21wb25lbnRdLFxyXG4gIHRlbXBsYXRlVXJsOiAnLi9maWxlLXVwbG9hZC1pdGVtcy5jb21wb25lbnQuaHRtbCcsXHJcbiAgc3R5bGVVcmxzOiBbJy4vZmlsZS11cGxvYWQtaXRlbXMuY29tcG9uZW50LnNjc3MnXVxyXG59KVxyXG5leHBvcnQgY2xhc3MgRmlsZVVwbG9hZEl0ZW1Db21wb25lbnQge1xyXG5cclxuICAvKiogVGhlIGZpbGUgdXBsb2FkIGl0ZW0gdG8gZGlzcGxheSAqL1xyXG4gIEBJbnB1dCgpIGl0ZW0hOiBVcGxvYWRJdGVtO1xyXG5cclxuICAvKiogRW1pdHRlZCB3aGVuIHRoZSB1c2VyIHJldHJpZXMgYSBmYWlsZWQgdXBsb2FkICovXHJcbiAgQE91dHB1dCgpIHJldHJ5ID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xyXG4gIFxyXG4gIC8qKiBFbWl0dGVkIHdoZW4gdGhlIHVzZXIgcmVtb3ZlcyB0aGUgZmlsZSBmcm9tIHRoZSBsaXN0ICovXHJcbiAgQE91dHB1dCgpIHJlbW92ZSA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcclxuICBcclxuICAvKiogRW1pdHRlZCB3aGVuIHRoZSB1c2VyIGNhbmNlbHMgYW4gb25nb2luZyB1cGxvYWQgKi9cclxuICBAT3V0cHV0KCkgY2FuY2VsID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xyXG5cclxuICAvKipcclxuICAgKiBGb3JtYXQgdGhlIGZpbGUgc2l6ZSBpbnRvIGEgaHVtYW4tcmVhZGFibGUgc3RyaW5nXHJcbiAgICogQHBhcmFtIHNpemUgLSBzaXplIGluIGJ5dGVzXHJcbiAgICogQHJldHVybnMgZm9ybWF0dGVkIHN0cmluZyBsaWtlICcxMi4zIEtCJywgJzEuNSBNQidcclxuICAgKi9cclxuICBmb3JtYXRTaXplKHNpemU6IG51bWJlcik6IHN0cmluZyB7XHJcbiAgICBpZiAoc2l6ZSA8IDEwMjQpIHJldHVybiBzaXplICsgJyBCJztcclxuICAgIGlmIChzaXplIDwgMTAyNCAqIDEwMjQpIHJldHVybiAoc2l6ZSAvIDEwMjQpLnRvRml4ZWQoMSkgKyAnIEtCJztcclxuICAgIHJldHVybiAoc2l6ZSAvICgxMDI0ICogMTAyNCkpLnRvRml4ZWQoMSkgKyAnIE1CJztcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEdldCBhbiBpY29uIG5hbWUgZm9yIHRoZSBmaWxlIGJhc2VkIG9uIGl0cyB0eXBlIG9yIGV4dGVuc2lvblxyXG4gICAqIEBwYXJhbSBpdGVtIC0gdGhlIFVwbG9hZEl0ZW1cclxuICAgKiBAcmV0dXJucyBpY29uIG5hbWUgc3RyaW5nXHJcbiAgICovXHJcbiAgZ2V0RmlsZUljb24oaXRlbTogVXBsb2FkSXRlbSk6IHN0cmluZyB7XHJcbiAgICBjb25zdCBmaWxlID0gaXRlbS5maWxlO1xyXG4gICAgY29uc3QgdHlwZSA9IGZpbGUudHlwZS50b0xvd2VyQ2FzZSgpO1xyXG4gICAgY29uc3QgbmFtZSA9IGZpbGUubmFtZS50b0xvd2VyQ2FzZSgpO1xyXG5cclxuICAgIC8vIEltYWdlIE1JTUUgdHlwZXNcclxuICAgIGlmICh0eXBlLnN0YXJ0c1dpdGgoJ2ltYWdlLycpKSB7XHJcbiAgICAgIHJldHVybiAnaWNvbi1nYWxsZXJ5JztcclxuICAgIH1cclxuXHJcbiAgICAvLyBJbWFnZSBmaWxlIGV4dGVuc2lvbnMgZmFsbGJhY2tcclxuICAgIGNvbnN0IGltYWdlRXh0ZW5zaW9ucyA9IFsnLnBuZycsICcuanBnJywgJy5qcGVnJywgJy5naWYnLCAnLndlYnAnLCAnLmJtcCcsICcuc3ZnJ107XHJcbiAgICBpZiAoaW1hZ2VFeHRlbnNpb25zLnNvbWUoZXh0ID0+IG5hbWUuZW5kc1dpdGgoZXh0KSkpIHtcclxuICAgICAgcmV0dXJuICdpY29uLWdhbGxlcnknO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIERlZmF1bHQgaWNvbiBmb3Igbm9uLWltYWdlIGZpbGVzXHJcbiAgICByZXR1cm4gJ2ljb24tdmFuJztcclxuICB9XHJcbn1cclxuIiwiPGRpdiBjbGFzcz1cImZpbGUtcm93XCI+XHJcblxyXG4gIDwhLS0gTEVGVCBTRUNUSU9OOiBUaHVtYm5haWwgKyBmaWxlbmFtZSAtLT5cclxuICA8ZGl2IGNsYXNzPVwidGh1bWJuYWlsLWZpbGVuYW1lLXdyYXBwZXJcIj5cclxuICAgIDxkaXYgY2xhc3M9XCJmaWxlLXRodW1iXCI+XHJcbiAgICAgIDxkc28taWNvbiBcclxuICAgICAgICBbaWNvbk5hbWVdPVwiZ2V0RmlsZUljb24oaXRlbSlcIlxyXG4gICAgICAgIHNpemU9XCJzbWFsbFwiXHJcbiAgICAgICAgY2xhc3M9XCJ0aHVtYi1pY29uXCI+XHJcbiAgICAgIDwvZHNvLWljb24+XHJcbiAgICA8L2Rpdj5cclxuXHJcbiAgICA8ZGl2IGNsYXNzPVwiZmlsZS1pbmZvXCI+XHJcbiAgICAgIDxkaXYgY2xhc3M9XCJmaWxlLW5hbWVcIj57eyBpdGVtLmZpbGUubmFtZSB9fTwvZGl2PlxyXG4gICAgICA8ZGl2IGNsYXNzPVwiZmlsZS1zaXplXCI+e3sgZm9ybWF0U2l6ZShpdGVtLmZpbGUuc2l6ZSkgfX08L2Rpdj5cclxuICAgIDwvZGl2PlxyXG4gIDwvZGl2PlxyXG5cclxuXHJcbiAgPCEtLSBNSURETEUgU0VDVElPTjogUHJvZ3Jlc3Mgb3Igc3RhdHVzIC0tPlxyXG4gIDxkaXYgY2xhc3M9XCJmaWxlY29udGVudC1sb2FkZXItd3JhcHBlclwiPlxyXG4gICAgXHJcbiAgICA8IS0tIFByb2dyZXNzIGJhciAtLT5cclxuICAgIDxkaXYgY2xhc3M9XCJmaWxlLXByb2dyZXNzXCIgKm5nSWY9XCIhaXRlbS5oaWRlUHJvZ3Jlc3NCYXJcIj5cclxuICAgICAgPGRzby1wcm9ncmVzcy1iYXJcclxuICAgICAgICBbdmFsdWVdPVwiXHJcbiAgICAgICAgICBpdGVtLnN0YXR1cyA9PT0gJ3VwbG9hZGluZycgPyBpdGVtLnByb2dyZXNzIDpcclxuICAgICAgICAgIGl0ZW0uc3RhdHVzID09PSAnY29tcGxldGVkJyA/IDEwMCA6XHJcbiAgICAgICAgICBudWxsXHJcbiAgICAgICAgXCJcclxuICAgICAgICBbY29sb3JdPVwiaXRlbS5zdGF0dXMgPT09ICdjb21wbGV0ZWQnID8gJ3N1Y2Nlc3MnIDogJ3ByaW1hcnknXCJcclxuICAgICAgPjwvZHNvLXByb2dyZXNzLWJhcj5cclxuICAgIDwvZGl2PlxyXG5cclxuICAgIDwhLS0gU3RhdHVzIHRleHQgLS0+XHJcbiAgICA8ZGl2IGNsYXNzPVwiZmlsZS1zdGF0dXNcIiAqbmdJZj1cIml0ZW0uaGlkZVByb2dyZXNzQmFyXCI+XHJcblxyXG4gICAgICA8IS0tIFN1Y2Nlc3MgLS0+XHJcbiAgICAgIDxkaXYgKm5nSWY9XCJpdGVtLnN0YXR1cyA9PT0gJ2NvbXBsZXRlZCdcIiBjbGFzcz1cInN0YXR1cyBzdWNjZXNzXCI+XHJcbiAgICAgICAgPGRzby1pY29uIGljb25OYW1lPVwiaWNvbi12YW5cIiBzaXplPVwic21hbGxcIj48L2Rzby1pY29uPlxyXG4gICAgICAgIDxzcGFuPlVwbG9hZGVkPC9zcGFuPlxyXG4gICAgICA8L2Rpdj5cclxuXHJcbiAgICAgIDwhLS0gRmFpbGVkIC0tPlxyXG4gICAgICA8ZGl2ICpuZ0lmPVwiaXRlbS5zdGF0dXMgPT09ICdmYWlsZWQnXCIgY2xhc3M9XCJzdGF0dXMgZXJyb3JcIj5cclxuICAgICAgICA8ZHNvLWljb24gaWNvbk5hbWU9XCJpY29uLWNlbnRlci1hbGlnblwiIHNpemU9XCJzbWFsbFwiPjwvZHNvLWljb24+XHJcbiAgICAgICAgPHNwYW4+VXBsb2FkIGZhaWxlZDwvc3Bhbj5cclxuICAgICAgPC9kaXY+XHJcblxyXG4gICAgPC9kaXY+XHJcblxyXG4gIDwhLS0gUklHSFQgU0VDVElPTjogQWN0aW9uIGJ1dHRvbnMgLS0+XHJcbiAgPGRpdiBjbGFzcz1cImZpbGUtYWN0aW9uc1wiPlxyXG5cclxuICAgIDwhLS0gVXBsb2FkaW5nIC0tPlxyXG4gICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cIml0ZW0uc3RhdHVzID09PSAndXBsb2FkaW5nJ1wiPlxyXG4gICAgICA8ZHNvLWJ1dHRvblxyXG4gICAgICAgIGJ0bkxhYmVsPVwiQ2FuY2VsXCJcclxuICAgICAgICBidG5UeXBlPVwidGV4dFwiXHJcbiAgICAgICAgKG9uQ2xpY2spPVwiY2FuY2VsLmVtaXQoKVwiPlxyXG4gICAgICA8L2Rzby1idXR0b24+XHJcbiAgICA8L25nLWNvbnRhaW5lcj5cclxuXHJcbiAgICA8IS0tIEZhaWxlZCAtLT5cclxuICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJpdGVtLnN0YXR1cyA9PT0gJ2ZhaWxlZCdcIj5cclxuICAgICAgPGRzby1idXR0b25cclxuICAgICAgICBidG5MYWJlbD1cIlJldHJ5XCJcclxuICAgICAgICBidG5UeXBlPVwidGV4dFwiXHJcbiAgICAgICAgKG9uQ2xpY2spPVwicmV0cnkuZW1pdCgpXCI+XHJcbiAgICAgIDwvZHNvLWJ1dHRvbj5cclxuXHJcbiAgICAgIDxkc28tYnV0dG9uXHJcbiAgICAgICAgYnRuTGFiZWw9XCJSZW1vdmVcIlxyXG4gICAgICAgIGJ0blR5cGU9XCJ0ZXh0XCJcclxuICAgICAgICAob25DbGljayk9XCJyZW1vdmUuZW1pdCgpXCI+XHJcbiAgICAgIDwvZHNvLWJ1dHRvbj5cclxuICAgIDwvbmctY29udGFpbmVyPlxyXG5cclxuICAgIDwhLS0gQ29tcGxldGVkIC0tPlxyXG4gICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cIml0ZW0uc3RhdHVzID09PSAnY29tcGxldGVkJ1wiPlxyXG4gICAgICA8ZHNvLWJ1dHRvblxyXG4gICAgICAgIGJ0bkxhYmVsPVwiUmVtb3ZlXCJcclxuICAgICAgICBidG5UeXBlPVwidGV4dFwiXHJcbiAgICAgICAgKG9uQ2xpY2spPVwicmVtb3ZlLmVtaXQoKVwiPlxyXG4gICAgICA8L2Rzby1idXR0b24+XHJcbiAgICA8L25nLWNvbnRhaW5lcj5cclxuXHJcbiAgPC9kaXY+XHJcblxyXG4gIDwvZGl2PlxyXG5cclxuPC9kaXY+XHJcbiJdfQ==
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
// file-upload-multi.component.ts
|
|
2
|
+
import { Component } from '@angular/core';
|
|
3
|
+
import { CommonModule } from '@angular/common';
|
|
4
|
+
import { ButtonComponent } from '../button/button.component';
|
|
5
|
+
import { IconComponent } from '../icon/icon.component';
|
|
6
|
+
import { FileUploadItemComponent } from '../file-upload-items/file-upload-items.component';
|
|
7
|
+
import * as i0 from "@angular/core";
|
|
8
|
+
import * as i1 from "./upload-simulator.service";
|
|
9
|
+
import * as i2 from "@angular/common";
|
|
10
|
+
/**
|
|
11
|
+
* FileUploadMultiComponent
|
|
12
|
+
* -------------------------
|
|
13
|
+
* This component handles:
|
|
14
|
+
* - Drag & drop file uploads
|
|
15
|
+
* - Selecting multiple files through <input type="file">
|
|
16
|
+
* - Upload progress per file
|
|
17
|
+
* - Cancelling an upload
|
|
18
|
+
* - Retrying failed uploads
|
|
19
|
+
* - Removing items from the queue
|
|
20
|
+
*
|
|
21
|
+
* UploadSimulatorService is used to mimic real upload behavior.
|
|
22
|
+
* Later, you can replace it with a real HTTP upload service.
|
|
23
|
+
*/
|
|
24
|
+
export class FileUploadMultiComponent {
|
|
25
|
+
sim;
|
|
26
|
+
// ngOnInit() {
|
|
27
|
+
// // Fake completed file
|
|
28
|
+
// this.uploadQueue.push({
|
|
29
|
+
// file: new File([""], "example-image.jpg", { type: "image/jpeg" }),
|
|
30
|
+
// progress: 100,
|
|
31
|
+
// status: "completed",
|
|
32
|
+
// hideProgressBar: true
|
|
33
|
+
// });
|
|
34
|
+
// // Fake failed file
|
|
35
|
+
// this.uploadQueue.push({
|
|
36
|
+
// file: new File([""], "broken-file.pdf", { type: "application/pdf" }),
|
|
37
|
+
// progress: null,
|
|
38
|
+
// status: "failed",
|
|
39
|
+
// hideProgressBar: true
|
|
40
|
+
// });
|
|
41
|
+
// // Fake uploading file
|
|
42
|
+
// this.uploadQueue.push({
|
|
43
|
+
// file: new File([""], "uploading.png", { type: "image/png" }),
|
|
44
|
+
// progress: 40,
|
|
45
|
+
// status: "uploading",
|
|
46
|
+
// hideProgressBar: false
|
|
47
|
+
// });
|
|
48
|
+
// }
|
|
49
|
+
/** All files being managed (uploaded, uploading, failed, completed). */
|
|
50
|
+
uploadQueue = [];
|
|
51
|
+
/** True while user drags a file over the drop zone. */
|
|
52
|
+
isDragOver = false;
|
|
53
|
+
constructor(sim) {
|
|
54
|
+
this.sim = sim;
|
|
55
|
+
// console.log('%c[COMPONENT] FileUploadMultiComponent initialized', 'color: purple;');
|
|
56
|
+
}
|
|
57
|
+
/* ===========================================================
|
|
58
|
+
* DRAG & DROP EVENTS
|
|
59
|
+
* =========================================================== */
|
|
60
|
+
onDragOver(event) {
|
|
61
|
+
event.preventDefault();
|
|
62
|
+
this.isDragOver = true;
|
|
63
|
+
// console.log('%c[DRAG] Dragging over drop zone...', 'color: #00aaff;');
|
|
64
|
+
}
|
|
65
|
+
onDragLeave(event) {
|
|
66
|
+
this.isDragOver = false;
|
|
67
|
+
// console.log('%c[DRAG] Drag left drop zone', 'color: #888;');
|
|
68
|
+
}
|
|
69
|
+
onDrop(event) {
|
|
70
|
+
event.preventDefault();
|
|
71
|
+
this.isDragOver = false;
|
|
72
|
+
// console.log('%c[DRAG] Files dropped', 'color: #00cc66;');
|
|
73
|
+
if (event.dataTransfer?.files) {
|
|
74
|
+
// console.log('[DRAG] Dropped files:', event.dataTransfer.files);
|
|
75
|
+
this.handleFiles(event.dataTransfer.files);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/* ===========================================================
|
|
79
|
+
* FILE INPUT SELECT
|
|
80
|
+
* =========================================================== */
|
|
81
|
+
onFileSelected(event) {
|
|
82
|
+
const input = event.target;
|
|
83
|
+
if (input.files) {
|
|
84
|
+
// console.log('%c[INPUT] Files selected:', 'color: #0099ff;', input.files);
|
|
85
|
+
this.handleFiles(input.files);
|
|
86
|
+
// Clears <input> so user can re-select same file
|
|
87
|
+
input.value = '';
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/* ===========================================================
|
|
91
|
+
* PROCESSING NEWLY ADDED FILES
|
|
92
|
+
* =========================================================== */
|
|
93
|
+
handleFiles(fileList) {
|
|
94
|
+
// console.log('%c[FILES] Processing new files...', 'color: #ffaa00;');
|
|
95
|
+
Array.from(fileList).forEach(file => {
|
|
96
|
+
// console.log(`%c[FILES] Added: ${file.name} (${file.size} bytes)`, 'color: #ffaa00;');
|
|
97
|
+
const item = {
|
|
98
|
+
file,
|
|
99
|
+
progress: 0,
|
|
100
|
+
status: 'queued'
|
|
101
|
+
};
|
|
102
|
+
this.uploadQueue.push(item);
|
|
103
|
+
this.startUpload(item);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/* ===========================================================
|
|
107
|
+
* UPLOAD LOGIC
|
|
108
|
+
* =========================================================== */
|
|
109
|
+
startUpload(item) {
|
|
110
|
+
// console.log(`%c[UPLOAD] Starting upload for ${item.file.name}`, 'color: #00cc99;');
|
|
111
|
+
item.status = 'uploading';
|
|
112
|
+
item.progress = 0;
|
|
113
|
+
this.sim.simulateUpload(item,
|
|
114
|
+
// ---- onProgress callback ----
|
|
115
|
+
(progress) => {
|
|
116
|
+
item.progress = progress;
|
|
117
|
+
// console.log(`%c[UPLOAD] ${item.file.name}: ${progress.toFixed(1)}%`, 'color: #0099ff;');
|
|
118
|
+
},
|
|
119
|
+
// ---- onComplete callback ----
|
|
120
|
+
() => {
|
|
121
|
+
item.status = 'completed';
|
|
122
|
+
item.progress = 100;
|
|
123
|
+
// Hide progress bar after a very small delay (100–300ms)
|
|
124
|
+
setTimeout(() => item.hideProgressBar = true, 300);
|
|
125
|
+
// console.log(`%c[UPLOAD] COMPLETED: ${item.file.name}`, 'color: green; font-weight: bold;');
|
|
126
|
+
},
|
|
127
|
+
// ---- onError callback ----
|
|
128
|
+
() => {
|
|
129
|
+
item.status = 'failed';
|
|
130
|
+
item.progress = null;
|
|
131
|
+
// Hide progress bar so status text can show
|
|
132
|
+
setTimeout(() => item.hideProgressBar = true, 300);
|
|
133
|
+
// console.log(`%c[UPLOAD] FAILED: ${item.file.name}`, 'color: red; font-weight: bold;');
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
onDropZoneClick(fileInput) {
|
|
137
|
+
// Prevent click if user is dragging files over the drop-zone
|
|
138
|
+
if (this.isDragOver)
|
|
139
|
+
return;
|
|
140
|
+
console.log('%c[CLICK] Drop zone clicked → opening file dialog', 'color: #0077ff;');
|
|
141
|
+
fileInput.click();
|
|
142
|
+
}
|
|
143
|
+
onBrowseButtonClick(event, fileInput) {
|
|
144
|
+
event.stopPropagation(); // stops click from bubbling to drop-zone
|
|
145
|
+
// console.log('%c[BUTTON] Browse clicked → opening file dialog', 'color: #8844ff;');
|
|
146
|
+
fileInput.click();
|
|
147
|
+
}
|
|
148
|
+
/* ===========================================================
|
|
149
|
+
* BUTTON ACTIONS (CANCEL / RETRY / REMOVE)
|
|
150
|
+
* =========================================================== */
|
|
151
|
+
cancelUpload(item) {
|
|
152
|
+
// console.log(`%c[CANCEL] Cancelling ${item.file.name}`, 'color: orange; font-weight: bold;');
|
|
153
|
+
// item.status = 'cancelled';
|
|
154
|
+
// item.progress = null;
|
|
155
|
+
this.removeItem(item);
|
|
156
|
+
}
|
|
157
|
+
retryUpload(item) {
|
|
158
|
+
// console.log('[RETRY] Restarting upload for', item.file.name);
|
|
159
|
+
// Reset state
|
|
160
|
+
item.status = 'queued';
|
|
161
|
+
item.progress = 0;
|
|
162
|
+
item.hideProgressBar = false; // <-- IMPORTANT
|
|
163
|
+
// Start upload again
|
|
164
|
+
this.startUpload(item);
|
|
165
|
+
}
|
|
166
|
+
removeItem(item) {
|
|
167
|
+
// console.log(`%c[REMOVE] Removing ${item.file.name}`, 'color: gray;');
|
|
168
|
+
this.uploadQueue = this.uploadQueue.filter(i => i !== item);
|
|
169
|
+
}
|
|
170
|
+
/* ===========================================================
|
|
171
|
+
* UTILITY FUNCTIONS
|
|
172
|
+
* =========================================================== */
|
|
173
|
+
/**
|
|
174
|
+
* Converts bytes → human readable sizes.
|
|
175
|
+
*/
|
|
176
|
+
formatSize(size) {
|
|
177
|
+
if (size < 1024)
|
|
178
|
+
return size + ' B';
|
|
179
|
+
if (size < 1024 * 1024)
|
|
180
|
+
return (size / 1024).toFixed(1) + ' KB';
|
|
181
|
+
return (size / (1024 * 1024)).toFixed(1) + ' MB';
|
|
182
|
+
}
|
|
183
|
+
// getThumbnail(item: UploadItem): string | null {
|
|
184
|
+
// const file = item.file;
|
|
185
|
+
// // If actual image → return preview URL
|
|
186
|
+
// if (file.type.startsWith('image/')) {
|
|
187
|
+
// return URL.createObjectURL(file);
|
|
188
|
+
// }
|
|
189
|
+
// // Otherwise return an icon path depending on type
|
|
190
|
+
// if (file.type === 'application/pdf') return 'assets/icons/pdf.svg';
|
|
191
|
+
// if (file.type.includes('word')) return 'assets/icons/doc.svg';
|
|
192
|
+
// if (file.type.includes('spreadsheet')) return 'assets/icons/xlsx.svg';
|
|
193
|
+
// // default icon
|
|
194
|
+
// return 'assets/icons/file.svg';
|
|
195
|
+
// }
|
|
196
|
+
getThumbnail(item) {
|
|
197
|
+
const file = item.file;
|
|
198
|
+
// Image preview for image file types
|
|
199
|
+
if (file.type.startsWith('image/')) {
|
|
200
|
+
return URL.createObjectURL(file);
|
|
201
|
+
}
|
|
202
|
+
// Not an image → no thumbnail
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
getFileIcon(item) {
|
|
206
|
+
const file = item.file;
|
|
207
|
+
const type = file.type.toLowerCase();
|
|
208
|
+
const name = file.name.toLowerCase();
|
|
209
|
+
console.log('%c[DEBUG] File name:', 'color: blue;', file.name);
|
|
210
|
+
console.log('%c[DEBUG] File type:', 'color: green;', file.type);
|
|
211
|
+
// Check MIME type first
|
|
212
|
+
if (type.startsWith('image/')) {
|
|
213
|
+
console.log('%c[DEBUG] Detected as image by MIME type', 'color: purple;');
|
|
214
|
+
return 'icon-gallery';
|
|
215
|
+
}
|
|
216
|
+
// Fallback: check extension for image types including SVG
|
|
217
|
+
const imageExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp', '.svg'];
|
|
218
|
+
if (imageExtensions.some(ext => name.endsWith(ext))) {
|
|
219
|
+
console.log('%c[DEBUG] Detected as image by extension', 'color: orange;');
|
|
220
|
+
return 'icon-gallery';
|
|
221
|
+
}
|
|
222
|
+
console.log('%c[DEBUG] Defaulting to document icon', 'color: red;');
|
|
223
|
+
return 'icon-van';
|
|
224
|
+
}
|
|
225
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FileUploadMultiComponent, deps: [{ token: i1.UploadSimulatorService }], target: i0.ɵɵFactoryTarget.Component });
|
|
226
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: FileUploadMultiComponent, isStandalone: true, selector: "dso-file-upload-multi", ngImport: i0, template: "<div class=\"upload-container\">\r\n\r\n <!-- DRAG & DROP AREA -->\r\n <div\r\n class=\"drop-zone\"\r\n [class.drag-over]=\"isDragOver\"\r\n (dragover)=\"onDragOver($event)\"\r\n (dragleave)=\"onDragLeave($event)\"\r\n (drop)=\"onDrop($event)\"\r\n (click)=\"onDropZoneClick(fileInput)\"\r\n >\r\n\r\n <dso-button \r\n style=\"padding: 0; \"\r\n btnLabel=\"Select files\"\r\n btnType=\"link\"\r\n (onClick)=\"onBrowseButtonClick($event, fileInput)\">\r\n </dso-button>\r\n or drag & drop files \r\n <input \r\n type=\"file\" \r\n multiple \r\n hidden \r\n #fileInput \r\n (change)=\"onFileSelected($event)\"\r\n />\r\n </div>\r\n\r\n <!-- FILE LIST -->\r\n <div class=\"file-list\" *ngIf=\"uploadQueue.length > 0\">\r\n\r\n <dso-file-item\r\n *ngFor=\"let item of uploadQueue\"\r\n [item]=\"item\"\r\n (retry)=\"retryUpload(item)\"\r\n (remove)=\"removeItem(item)\"\r\n (cancel)=\"cancelUpload(item)\"\r\n ></dso-file-item>\r\n\r\n\r\n </div>\r\n</div>\r\n", styles: [".upload-container{display:flex;flex-direction:column;gap:8px}.drop-zone{border:2px dashed #ccc;padding:2rem;text-align:center;border-radius:6px;transition:border-color .2s,background-color .2s;cursor:pointer}.drop-zone.drag-over{border-color:#007bff;background-color:#f0f7ff}.file-list{display:flex;flex-direction:column;gap:.75rem;max-height:360px;overflow-y:auto;padding-right:4px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: ButtonComponent, selector: "dso-button", inputs: ["btnLabel", "btnType", "btnSize", "btnIconName", "isDisabled", "isActive"], outputs: ["onClick"] }, { kind: "component", type: FileUploadItemComponent, selector: "dso-file-item", inputs: ["item"], outputs: ["retry", "remove", "cancel"] }] });
|
|
227
|
+
}
|
|
228
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FileUploadMultiComponent, decorators: [{
|
|
229
|
+
type: Component,
|
|
230
|
+
args: [{ selector: 'dso-file-upload-multi', standalone: true, imports: [CommonModule, ButtonComponent, IconComponent, FileUploadItemComponent], template: "<div class=\"upload-container\">\r\n\r\n <!-- DRAG & DROP AREA -->\r\n <div\r\n class=\"drop-zone\"\r\n [class.drag-over]=\"isDragOver\"\r\n (dragover)=\"onDragOver($event)\"\r\n (dragleave)=\"onDragLeave($event)\"\r\n (drop)=\"onDrop($event)\"\r\n (click)=\"onDropZoneClick(fileInput)\"\r\n >\r\n\r\n <dso-button \r\n style=\"padding: 0; \"\r\n btnLabel=\"Select files\"\r\n btnType=\"link\"\r\n (onClick)=\"onBrowseButtonClick($event, fileInput)\">\r\n </dso-button>\r\n or drag & drop files \r\n <input \r\n type=\"file\" \r\n multiple \r\n hidden \r\n #fileInput \r\n (change)=\"onFileSelected($event)\"\r\n />\r\n </div>\r\n\r\n <!-- FILE LIST -->\r\n <div class=\"file-list\" *ngIf=\"uploadQueue.length > 0\">\r\n\r\n <dso-file-item\r\n *ngFor=\"let item of uploadQueue\"\r\n [item]=\"item\"\r\n (retry)=\"retryUpload(item)\"\r\n (remove)=\"removeItem(item)\"\r\n (cancel)=\"cancelUpload(item)\"\r\n ></dso-file-item>\r\n\r\n\r\n </div>\r\n</div>\r\n", styles: [".upload-container{display:flex;flex-direction:column;gap:8px}.drop-zone{border:2px dashed #ccc;padding:2rem;text-align:center;border-radius:6px;transition:border-color .2s,background-color .2s;cursor:pointer}.drop-zone.drag-over{border-color:#007bff;background-color:#f0f7ff}.file-list{display:flex;flex-direction:column;gap:.75rem;max-height:360px;overflow-y:auto;padding-right:4px}\n"] }]
|
|
231
|
+
}], ctorParameters: () => [{ type: i1.UploadSimulatorService }] });
|
|
232
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmlsZS11cGxvYWQtbXVsdGlwbGUuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvdWkvc3JjL2xpYi9maWxlLXVwbG9hZC1tdWx0aXBsZS9maWxlLXVwbG9hZC1tdWx0aXBsZS5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy91aS9zcmMvbGliL2ZpbGUtdXBsb2FkLW11bHRpcGxlL2ZpbGUtdXBsb2FkLW11bHRpcGxlLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGlDQUFpQztBQUNqQyxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQzFDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUcvQyxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDN0QsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ3ZELE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxNQUFNLGtEQUFrRCxDQUFDOzs7O0FBRTNGOzs7Ozs7Ozs7Ozs7O0dBYUc7QUFRSCxNQUFNLE9BQU8sd0JBQXdCO0lBbUNmO0lBakNwQixpQkFBaUI7SUFDakIsMkJBQTJCO0lBQzNCLDRCQUE0QjtJQUM1Qix5RUFBeUU7SUFDekUscUJBQXFCO0lBQ3JCLDJCQUEyQjtJQUMzQiw0QkFBNEI7SUFDNUIsUUFBUTtJQUVSLHdCQUF3QjtJQUN4Qiw0QkFBNEI7SUFDNUIsNEVBQTRFO0lBQzVFLHNCQUFzQjtJQUN0Qix3QkFBd0I7SUFDeEIsNEJBQTRCO0lBQzVCLFFBQVE7SUFFUiwyQkFBMkI7SUFDM0IsNEJBQTRCO0lBQzVCLG9FQUFvRTtJQUNwRSxvQkFBb0I7SUFDcEIsMkJBQTJCO0lBQzNCLDZCQUE2QjtJQUM3QixRQUFRO0lBQ1IsSUFBSTtJQUdKLHdFQUF3RTtJQUN4RSxXQUFXLEdBQWlCLEVBQUUsQ0FB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QyxDQUFDO1FBQy9ELE9BQU8sQ0FBQyxHQUFHLENBQUMsc0JBQXNCLEVBQUUsZUFBZSxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUVoRSx3QkFBd0I7UUFDeEIsSUFBSSxJQUFJLENBQUMsVUFBVSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUM7WUFDOUIsT0FBTyxDQUFDLEdBQUcsQ0FBQywwQ0FBMEMsRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDO1lBQzFFLE9BQU8sY0FBYyxDQUFDO1FBQ3hCLENBQUM7UUFFRCwwREFBMEQ7UUFDMUQsTUFBTSxlQUFlLEdBQUcsQ0FBQyxNQUFNLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLENBQUMsQ0FBQztRQUNuRixJQUFJLGVBQWUsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUNwRCxPQUFPLENBQUMsR0FBRyxDQUFDLDBDQUEwQyxFQUFFLGdCQUFnQixDQUFDLENBQUM7WUFDMUUsT0FBTyxjQUFjLENBQUM7UUFDeEIsQ0FBQztRQUVELE9BQU8sQ0FBQyxHQUFHLENBQUMsdUNBQXVDLEVBQUUsYUFBYSxDQUFDLENBQUM7UUFDcEUsT0FBTyxVQUFVLENBQUM7SUFDcEIsQ0FBQzt3R0E3UFUsd0JBQXdCOzRGQUF4Qix3QkFBd0IsaUZDOUJyQyxnaUNBMENBLDBiRGhCWSxZQUFZLGdRQUFFLGVBQWUsa0tBQWlCLHVCQUF1Qjs7NEZBSXBFLHdCQUF3QjtrQkFQcEMsU0FBUzsrQkFDRSx1QkFBdUIsY0FDckIsSUFBSSxXQUNQLENBQUMsWUFBWSxFQUFFLGVBQWUsRUFBRSxhQUFhLEVBQUUsdUJBQXVCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBmaWxlLXVwbG9hZC1tdWx0aS5jb21wb25lbnQudHNcclxuaW1wb3J0IHsgQ29tcG9uZW50IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XHJcbmltcG9ydCB7IENvbW1vbk1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XHJcbmltcG9ydCB7IFVwbG9hZEl0ZW0gfSBmcm9tICcuL3VwbG9hZC1pdGVtLm1vZGVsJztcclxuaW1wb3J0IHsgVXBsb2FkU2ltdWxhdG9yU2VydmljZSB9IGZyb20gJy4vdXBsb2FkLXNpbXVsYXRvci5zZXJ2aWNlJztcclxuaW1wb3J0IHsgQnV0dG9uQ29tcG9uZW50IH0gZnJvbSAnLi4vYnV0dG9uL2J1dHRvbi5jb21wb25lbnQnO1xyXG5pbXBvcnQgeyBJY29uQ29tcG9uZW50IH0gZnJvbSAnLi4vaWNvbi9pY29uLmNvbXBvbmVudCc7XHJcbmltcG9ydCB7IEZpbGVVcGxvYWRJdGVtQ29tcG9uZW50IH0gZnJvbSAnLi4vZmlsZS11cGxvYWQtaXRlbXMvZmlsZS11cGxvYWQtaXRlbXMuY29tcG9uZW50JztcclxuXHJcbi8qKlxyXG4gKiBGaWxlVXBsb2FkTXVsdGlDb21wb25lbnRcclxuICogLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG4gKiBUaGlzIGNvbXBvbmVudCBoYW5kbGVzOlxyXG4gKiAtIERyYWcgJiBkcm9wIGZpbGUgdXBsb2Fkc1xyXG4gKiAtIFNlbGVjdGluZyBtdWx0aXBsZSBmaWxlcyB0aHJvdWdoIDxpbnB1dCB0eXBlPVwiZmlsZVwiPlxyXG4gKiAtIFVwbG9hZCBwcm9ncmVzcyBwZXIgZmlsZVxyXG4gKiAtIENhbmNlbGxpbmcgYW4gdXBsb2FkXHJcbiAqIC0gUmV0cnlpbmcgZmFpbGVkIHVwbG9hZHNcclxuICogLSBSZW1vdmluZyBpdGVtcyBmcm9tIHRoZSBxdWV1ZVxyXG4gKlxyXG4gKiBVcGxvYWRTaW11bGF0b3JTZXJ2aWNlIGlzIHVzZWQgdG8gbWltaWMgcmVhbCB1cGxvYWQgYmVoYXZpb3IuXHJcbiAqIExhdGVyLCB5b3UgY2FuIHJlcGxhY2UgaXQgd2l0aCBhIHJlYWwgSFRUUCB1cGxvYWQgc2VydmljZS5cclxuICovXHJcbkBDb21wb25lbnQoe1xyXG4gIHNlbGVjdG9yOiAnZHNvLWZpbGUtdXBsb2FkLW11bHRpJyxcclxuICBzdGFuZGFsb25lOiB0cnVlLFxyXG4gIGltcG9ydHM6IFtDb21tb25Nb2R1bGUsIEJ1dHRvbkNvbXBvbmVudCwgSWNvbkNvbXBvbmVudCwgRmlsZVVwbG9hZEl0ZW1Db21wb25lbnRdLFxyXG4gIHRlbXBsYXRlVXJsOiAnLi9maWxlLXVwbG9hZC1tdWx0aXBsZS5jb21wb25lbnQuaHRtbCcsXHJcbiAgc3R5bGVVcmxzOiBbJy4vZmlsZS11cGxvYWQtbXVsdGlwbGUuY29tcG9uZW50LnNjc3MnXVxyXG59KVxyXG5leHBvcnQgY2xhc3MgRmlsZVVwbG9hZE11bHRpQ29tcG9uZW50IHtcclxuXHJcbiAgLy8gICBuZ09uSW5pdCgpIHtcclxuICAvLyAgIC8vIEZha2UgY29tcGxldGVkIGZpbGVcclxuICAvLyAgIHRoaXMudXBsb2FkUXVldWUucHVzaCh7XHJcbiAgLy8gICAgIGZpbGU6IG5ldyBGaWxlKFtcIlwiXSwgXCJleGFtcGxlLWltYWdlLmpwZ1wiLCB7IHR5cGU6IFwiaW1hZ2UvanBlZ1wiIH0pLFxyXG4gIC8vICAgICBwcm9ncmVzczogMTAwLFxyXG4gIC8vICAgICBzdGF0dXM6IFwiY29tcGxldGVkXCIsXHJcbiAgLy8gICAgIGhpZGVQcm9ncmVzc0JhcjogdHJ1ZVxyXG4gIC8vICAgfSk7XHJcblxyXG4gIC8vICAgLy8gRmFrZSBmYWlsZWQgZmlsZVxyXG4gIC8vICAgdGhpcy51cGxvYWRRdWV1ZS5wdXNoKHtcclxuICAvLyAgICAgZmlsZTogbmV3IEZpbGUoW1wiXCJdLCBcImJyb2tlbi1maWxlLnBkZlwiLCB7IHR5cGU6IFwiYXBwbGljYXRpb24vcGRmXCIgfSksXHJcbiAgLy8gICAgIHByb2dyZXNzOiBudWxsLFxyXG4gIC8vICAgICBzdGF0dXM6IFwiZmFpbGVkXCIsXHJcbiAgLy8gICAgIGhpZGVQcm9ncmVzc0JhcjogdHJ1ZVxyXG4gIC8vICAgfSk7XHJcblxyXG4gIC8vICAgLy8gRmFrZSB1cGxvYWRpbmcgZmlsZVxyXG4gIC8vICAgdGhpcy51cGxvYWRRdWV1ZS5wdXNoKHtcclxuICAvLyAgICAgZmlsZTogbmV3IEZpbGUoW1wiXCJdLCBcInVwbG9hZGluZy5wbmdcIiwgeyB0eXBlOiBcImltYWdlL3BuZ1wiIH0pLFxyXG4gIC8vICAgICBwcm9ncmVzczogNDAsXHJcbiAgLy8gICAgIHN0YXR1czogXCJ1cGxvYWRpbmdcIixcclxuICAvLyAgICAgaGlkZVByb2dyZXNzQmFyOiBmYWxzZVxyXG4gIC8vICAgfSk7XHJcbiAgLy8gfVxyXG5cclxuXHJcbiAgLyoqIEFsbCBmaWxlcyBiZWluZyBtYW5hZ2VkICh1cGxvYWRlZCwgdXBsb2FkaW5nLCBmYWlsZWQsIGNvbXBsZXRlZCkuICovXHJcbiAgdXBsb2FkUXVldWU6IFVwbG9hZEl0ZW1bXSA9IFtdO1xyXG5cclxuICAvKiogVHJ1ZSB3aGlsZSB1c2VyIGRyYWdzIGEgZmlsZSBvdmVyIHRoZSBkcm9wIHpvbmUuICovXHJcbiAgaXNEcmFnT3ZlciA9IGZhbHNlO1xyXG5cclxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHNpbTogVXBsb2FkU2ltdWxhdG9yU2VydmljZSkge1xyXG4gICAgLy8gY29uc29sZS5sb2coJyVjW0NPTVBPTkVOVF0gRmlsZVVwbG9hZE11bHRpQ29tcG9uZW50IGluaXRpYWxpemVkJywgJ2NvbG9yOiBwdXJwbGU7Jyk7XHJcbiAgfVxyXG5cclxuICAvKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxyXG4gICAqICAgICAgICAgICAgICAgICAgICAgRFJBRyAmIERST1AgRVZFTlRTXHJcbiAgICogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0gKi9cclxuXHJcbiAgb25EcmFnT3ZlcihldmVudDogRHJhZ0V2ZW50KSB7XHJcbiAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xyXG4gICAgdGhpcy5pc0RyYWdPdmVyID0gdHJ1ZTtcclxuICAgIC8vIGNvbnNvbGUubG9nKCclY1tEUkFHXSBEcmFnZ2luZyBvdmVyIGRyb3Agem9uZS4uLicsICdjb2xvcjogIzAwYWFmZjsnKTtcclxuICB9XHJcblxyXG4gIG9uRHJhZ0xlYXZlKGV2ZW50OiBEcmFnRXZlbnQpIHtcclxuICAgIHRoaXMuaXNEcmFnT3ZlciA9IGZhbHNlO1xyXG4gICAgLy8gY29uc29sZS5sb2coJyVjW0RSQUddIERyYWcgbGVmdCBkcm9wIHpvbmUnLCAnY29sb3I6ICM4ODg7Jyk7XHJcbiAgfVxyXG5cclxuICBvbkRyb3AoZXZlbnQ6IERyYWdFdmVudCkge1xyXG4gICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcclxuICAgIHRoaXMuaXNEcmFnT3ZlciA9IGZhbHNlO1xyXG5cclxuICAgIC8vIGNvbnNvbGUubG9nKCclY1tEUkFHXSBGaWxlcyBkcm9wcGVkJywgJ2NvbG9yOiAjMDBjYzY2OycpO1xyXG5cclxuICAgIGlmIChldmVudC5kYXRhVHJhbnNmZXI/LmZpbGVzKSB7XHJcbiAgICAgIC8vIGNvbnNvbGUubG9nKCdbRFJBR10gRHJvcHBlZCBmaWxlczonLCBldmVudC5kYXRhVHJhbnNmZXIuZmlsZXMpO1xyXG4gICAgICB0aGlzLmhhbmRsZUZpbGVzKGV2ZW50LmRhdGFUcmFuc2Zlci5maWxlcyk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxyXG4gICAqICAgICAgICAgICAgICAgICAgICAgRklMRSBJTlBVVCBTRUxFQ1RcclxuICAgKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSAqL1xyXG5cclxuICBvbkZpbGVTZWxlY3RlZChldmVudDogRXZlbnQpIHtcclxuICAgIGNvbnN0IGlucHV0ID0gZXZlbnQudGFyZ2V0IGFzIEhUTUxJbnB1dEVsZW1lbnQ7XHJcblxyXG4gICAgaWYgKGlucHV0LmZpbGVzKSB7XHJcbiAgICAgIC8vIGNvbnNvbGUubG9nKCclY1tJTlBVVF0gRmlsZXMgc2VsZWN0ZWQ6JywgJ2NvbG9yOiAjMDA5OWZmOycsIGlucHV0LmZpbGVzKTtcclxuICAgICAgdGhpcy5oYW5kbGVGaWxlcyhpbnB1dC5maWxlcyk7XHJcblxyXG4gICAgICAvLyBDbGVhcnMgPGlucHV0PiBzbyB1c2VyIGNhbiByZS1zZWxlY3Qgc2FtZSBmaWxlXHJcbiAgICAgIGlucHV0LnZhbHVlID0gJyc7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxyXG4gICAqICAgICAgICAgICAgICAgIFBST0NFU1NJTkcgTkVXTFkgQURERUQgRklMRVNcclxuICAgKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSAqL1xyXG5cclxuICBoYW5kbGVGaWxlcyhmaWxlTGlzdDogRmlsZUxpc3QpIHtcclxuICAgIC8vIGNvbnNvbGUubG9nKCclY1tGSUxFU10gUHJvY2Vzc2luZyBuZXcgZmlsZXMuLi4nLCAnY29sb3I6ICNmZmFhMDA7Jyk7XHJcblxyXG4gICAgQXJyYXkuZnJvbShmaWxlTGlzdCkuZm9yRWFjaChmaWxlID0+IHtcclxuICAgICAgLy8gY29uc29sZS5sb2coYCVjW0ZJTEVTXSBBZGRlZDogJHtmaWxlLm5hbWV9ICgke2ZpbGUuc2l6ZX0gYnl0ZXMpYCwgJ2NvbG9yOiAjZmZhYTAwOycpO1xyXG5cclxuICAgICAgY29uc3QgaXRlbTogVXBsb2FkSXRlbSA9IHtcclxuICAgICAgICBmaWxlLFxyXG4gICAgICAgIHByb2dyZXNzOiAwLFxyXG4gICAgICAgIHN0YXR1czogJ3F1ZXVlZCdcclxuICAgICAgfTtcclxuXHJcbiAgICAgIHRoaXMudXBsb2FkUXVldWUucHVzaChpdGVtKTtcclxuXHJcbiAgICAgIHRoaXMuc3RhcnRVcGxvYWQoaXRlbSk7XHJcbiAgICB9KTtcclxuICB9XHJcblxyXG4gIC8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XHJcbiAgICogICAgICAgICAgICAgICAgICAgICAgICBVUExPQUQgTE9HSUNcclxuICAgKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSAqL1xyXG5cclxuICBzdGFydFVwbG9hZChpdGVtOiBVcGxvYWRJdGVtKSB7XHJcbiAgICAvLyBjb25zb2xlLmxvZyhgJWNbVVBMT0FEXSBTdGFydGluZyB1cGxvYWQgZm9yICR7aXRlbS5maWxlLm5hbWV9YCwgJ2NvbG9yOiAjMDBjYzk5OycpO1xyXG5cclxuICAgIGl0ZW0uc3RhdHVzID0gJ3VwbG9hZGluZyc7XHJcbiAgICBpdGVtLnByb2dyZXNzID0gMDtcclxuXHJcbiAgICB0aGlzLnNpbS5zaW11bGF0ZVVwbG9hZChcclxuICAgICAgaXRlbSxcclxuXHJcbiAgICAgIC8vIC0tLS0gb25Qcm9ncmVzcyBjYWxsYmFjayAtLS0tXHJcbiAgICAgIChwcm9ncmVzcykgPT4ge1xyXG4gICAgICAgIGl0ZW0ucHJvZ3Jlc3MgPSBwcm9ncmVzcztcclxuICAgICAgICAvLyBjb25zb2xlLmxvZyhgJWNbVVBMT0FEXSAke2l0ZW0uZmlsZS5uYW1lfTogJHtwcm9ncmVzcy50b0ZpeGVkKDEpfSVgLCAnY29sb3I6ICMwMDk5ZmY7Jyk7XHJcbiAgICAgIH0sXHJcblxyXG4gICAgICAvLyAtLS0tIG9uQ29tcGxldGUgY2FsbGJhY2sgLS0tLVxyXG4gICAgICAoKSA9PiB7XHJcbiAgICAgICAgaXRlbS5zdGF0dXMgPSAnY29tcGxldGVkJztcclxuICAgICAgICBpdGVtLnByb2dyZXNzID0gMTAwO1xyXG4gICAgICAgIFxyXG4gICAgICAgIC8vIEhpZGUgcHJvZ3Jlc3MgYmFyIGFmdGVyIGEgdmVyeSBzbWFsbCBkZWxheSAoMTAw4oCTMzAwbXMpXHJcbiAgICAgICAgc2V0VGltZW91dCgoKSA9PiBpdGVtLmhpZGVQcm9ncmVzc0JhciA9IHRydWUsIDMwMCk7XHJcbiAgICAgICAgLy8gY29uc29sZS5sb2coYCVjW1VQTE9BRF0gQ09NUExFVEVEOiAke2l0ZW0uZmlsZS5uYW1lfWAsICdjb2xvcjogZ3JlZW47IGZvbnQtd2VpZ2h0OiBib2xkOycpO1xyXG4gICAgICB9LFxyXG5cclxuICAgICAgLy8gLS0tLSBvbkVycm9yIGNhbGxiYWNrIC0tLS1cclxuICAgICAgKCkgPT4ge1xyXG4gICAgICAgIGl0ZW0uc3RhdHVzID0gJ2ZhaWxlZCc7XHJcbiAgICAgICAgaXRlbS5wcm9ncmVzcyA9IG51bGw7XHJcblxyXG4gICAgICAgIC8vIEhpZGUgcHJvZ3Jlc3MgYmFyIHNvIHN0YXR1cyB0ZXh0IGNhbiBzaG93XHJcbiAgICAgICAgc2V0VGltZW91dCgoKSA9PiBpdGVtLmhpZGVQcm9ncmVzc0JhciA9IHRydWUsIDMwMCk7XHJcbiAgICAgICAgLy8gY29uc29sZS5sb2coYCVjW1VQTE9BRF0gRkFJTEVEOiAke2l0ZW0uZmlsZS5uYW1lfWAsICdjb2xvcjogcmVkOyBmb250LXdlaWdodDogYm9sZDsnKTtcclxuICAgICAgfVxyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIG9uRHJvcFpvbmVDbGljayhmaWxlSW5wdXQ6IEhUTUxJbnB1dEVsZW1lbnQpIHtcclxuICAvLyBQcmV2ZW50IGNsaWNrIGlmIHVzZXIgaXMgZHJhZ2dpbmcgZmlsZXMgb3ZlciB0aGUgZHJvcC16b25lXHJcbiAgaWYgKHRoaXMuaXNEcmFnT3ZlcikgcmV0dXJuO1xyXG5cclxuICBjb25zb2xlLmxvZygnJWNbQ0xJQ0tdIERyb3Agem9uZSBjbGlja2VkIOKGkiBvcGVuaW5nIGZpbGUgZGlhbG9nJywgJ2NvbG9yOiAjMDA3N2ZmOycpO1xyXG4gIGZpbGVJbnB1dC5jbGljaygpO1xyXG59XHJcblxyXG5vbkJyb3dzZUJ1dHRvbkNsaWNrKGV2ZW50OiBFdmVudCwgZmlsZUlucHV0OiBIVE1MSW5wdXRFbGVtZW50KSB7XHJcbiAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7IC8vIHN0b3BzIGNsaWNrIGZyb20gYnViYmxpbmcgdG8gZHJvcC16b25lXHJcbiAgLy8gY29uc29sZS5sb2coJyVjW0JVVFRPTl0gQnJvd3NlIGNsaWNrZWQg4oaSIG9wZW5pbmcgZmlsZSBkaWFsb2cnLCAnY29sb3I6ICM4ODQ0ZmY7Jyk7XHJcbiAgZmlsZUlucHV0LmNsaWNrKCk7XHJcbn1cclxuXHJcbiAgLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cclxuICAgKiAgICAgICAgICAgICAgICBCVVRUT04gQUNUSU9OUyAoQ0FOQ0VMIC8gUkVUUlkgLyBSRU1PVkUpXHJcbiAgICogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0gKi9cclxuXHJcbiAgY2FuY2VsVXBsb2FkKGl0ZW06IFVwbG9hZEl0ZW0pIHtcclxuICAgIC8vIGNvbnNvbGUubG9nKGAlY1tDQU5DRUxdIENhbmNlbGxpbmcgJHtpdGVtLmZpbGUubmFtZX1gLCAnY29sb3I6IG9yYW5nZTsgZm9udC13ZWlnaHQ6IGJvbGQ7Jyk7XHJcbiAgICAvLyBpdGVtLnN0YXR1cyA9ICdjYW5jZWxsZWQnO1xyXG4gICAgLy8gaXRlbS5wcm9ncmVzcyA9IG51bGw7XHJcbiAgICB0aGlzLnJlbW92ZUl0ZW0oaXRlbSk7XHJcblxyXG4gIH1cclxuXHJcbiAgcmV0cnlVcGxvYWQoaXRlbTogVXBsb2FkSXRlbSkge1xyXG4gICAgLy8gY29uc29sZS5sb2coJ1tSRVRSWV0gUmVzdGFydGluZyB1cGxvYWQgZm9yJywgaXRlbS5maWxlLm5hbWUpO1xyXG5cclxuICAgIC8vIFJlc2V0IHN0YXRlXHJcbiAgICBpdGVtLnN0YXR1cyA9ICdxdWV1ZWQnO1xyXG4gICAgaXRlbS5wcm9ncmVzcyA9IDA7XHJcbiAgICBpdGVtLmhpZGVQcm9ncmVzc0JhciA9IGZhbHNlOyAvLyA8LS0gSU1QT1JUQU5UXHJcblxyXG4gICAgLy8gU3RhcnQgdXBsb2FkIGFnYWluXHJcbiAgICB0aGlzLnN0YXJ0VXBsb2FkKGl0ZW0pO1xyXG4gIH1cclxuXHJcbiAgcmVtb3ZlSXRlbShpdGVtOiBVcGxvYWRJdGVtKSB7XHJcbiAgICAvLyBjb25zb2xlLmxvZyhgJWNbUkVNT1ZFXSBSZW1vdmluZyAke2l0ZW0uZmlsZS5uYW1lfWAsICdjb2xvcjogZ3JheTsnKTtcclxuICAgIHRoaXMudXBsb2FkUXVldWUgPSB0aGlzLnVwbG9hZFF1ZXVlLmZpbHRlcihpID0+IGkgIT09IGl0ZW0pO1xyXG4gIH1cclxuXHJcbiAgLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cclxuICAgKiAgICAgICAgICAgICAgICAgICAgIFVUSUxJVFkgRlVOQ1RJT05TXHJcbiAgICogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0gKi9cclxuXHJcbiAgLyoqXHJcbiAgICogQ29udmVydHMgYnl0ZXMg4oaSIGh1bWFuIHJlYWRhYmxlIHNpemVzLlxyXG4gICAqL1xyXG4gIGZvcm1hdFNpemUoc2l6ZTogbnVtYmVyKTogc3RyaW5nIHtcclxuICAgIGlmIChzaXplIDwgMTAyNCkgcmV0dXJuIHNpemUgKyAnIEInO1xyXG4gICAgaWYgKHNpemUgPCAxMDI0ICogMTAyNCkgcmV0dXJuIChzaXplIC8gMTAyNCkudG9GaXhlZCgxKSArICcgS0InO1xyXG4gICAgcmV0dXJuIChzaXplIC8gKDEwMjQgKiAxMDI0KSkudG9GaXhlZCgxKSArICcgTUInO1xyXG4gIH1cclxuXHJcbiAgLy8gZ2V0VGh1bWJuYWlsKGl0ZW06IFVwbG9hZEl0ZW0pOiBzdHJpbmcgfCBudWxsIHtcclxuICAvLyAgIGNvbnN0IGZpbGUgPSBpdGVtLmZpbGU7XHJcblxyXG4gIC8vICAgLy8gSWYgYWN0dWFsIGltYWdlIOKGkiByZXR1cm4gcHJldmlldyBVUkxcclxuICAvLyAgIGlmIChmaWxlLnR5cGUuc3RhcnRzV2l0aCgnaW1hZ2UvJykpIHtcclxuICAvLyAgICAgcmV0dXJuIFVSTC5jcmVhdGVPYmplY3RVUkwoZmlsZSk7XHJcbiAgLy8gICB9XHJcblxyXG4gIC8vICAgLy8gT3RoZXJ3aXNlIHJldHVybiBhbiBpY29uIHBhdGggZGVwZW5kaW5nIG9uIHR5cGVcclxuICAvLyAgIGlmIChmaWxlLnR5cGUgPT09ICdhcHBsaWNhdGlvbi9wZGYnKSByZXR1cm4gJ2Fzc2V0cy9pY29ucy9wZGYuc3ZnJztcclxuICAvLyAgIGlmIChmaWxlLnR5cGUuaW5jbHVkZXMoJ3dvcmQnKSkgcmV0dXJuICdhc3NldHMvaWNvbnMvZG9jLnN2Zyc7XHJcbiAgLy8gICBpZiAoZmlsZS50eXBlLmluY2x1ZGVzKCdzcHJlYWRzaGVldCcpKSByZXR1cm4gJ2Fzc2V0cy9pY29ucy94bHN4LnN2Zyc7XHJcblxyXG4gIC8vICAgLy8gZGVmYXVsdCBpY29uXHJcbiAgLy8gICByZXR1cm4gJ2Fzc2V0cy9pY29ucy9maWxlLnN2Zyc7XHJcbiAgLy8gfVxyXG5cclxuICBnZXRUaHVtYm5haWwoaXRlbTogVXBsb2FkSXRlbSk6IHN0cmluZyB8IG51bGwge1xyXG4gIGNvbnN0IGZpbGUgPSBpdGVtLmZpbGU7XHJcblxyXG4gIC8vIEltYWdlIHByZXZpZXcgZm9yIGltYWdlIGZpbGUgdHlwZXNcclxuICBpZiAoZmlsZS50eXBlLnN0YXJ0c1dpdGgoJ2ltYWdlLycpKSB7XHJcbiAgICByZXR1cm4gVVJMLmNyZWF0ZU9iamVjdFVSTChmaWxlKTtcclxuICB9XHJcblxyXG4gIC8vIE5vdCBhbiBpbWFnZSDihpIgbm8gdGh1bWJuYWlsXHJcbiAgcmV0dXJuIG51bGw7XHJcbn1cclxuXHJcbiAgZ2V0RmlsZUljb24oaXRlbTogVXBsb2FkSXRlbSk6IHN0cmluZyB7XHJcbiAgICBjb25zdCBmaWxlID0gaXRlbS5maWxlO1xyXG4gICAgY29uc3QgdHlwZSA9IGZpbGUudHlwZS50b0xvd2VyQ2FzZSgpO1xyXG4gICAgY29uc3QgbmFtZSA9IGZpbGUubmFtZS50b0xvd2VyQ2FzZSgpO1xyXG5cclxuICAgIGNvbnNvbGUubG9nKCclY1tERUJVR10gRmlsZSBuYW1lOicsICdjb2xvcjogYmx1ZTsnLCBmaWxlLm5hbWUpO1xyXG4gICAgY29uc29sZS5sb2coJyVjW0RFQlVHXSBGaWxlIHR5cGU6JywgJ2NvbG9yOiBncmVlbjsnLCBmaWxlLnR5cGUpO1xyXG5cclxuICAgIC8vIENoZWNrIE1JTUUgdHlwZSBmaXJzdFxyXG4gICAgaWYgKHR5cGUuc3RhcnRzV2l0aCgnaW1hZ2UvJykpIHtcclxuICAgICAgY29uc29sZS5sb2coJyVjW0RFQlVHXSBEZXRlY3RlZCBhcyBpbWFnZSBieSBNSU1FIHR5cGUnLCAnY29sb3I6IHB1cnBsZTsnKTtcclxuICAgICAgcmV0dXJuICdpY29uLWdhbGxlcnknO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEZhbGxiYWNrOiBjaGVjayBleHRlbnNpb24gZm9yIGltYWdlIHR5cGVzIGluY2x1ZGluZyBTVkdcclxuICAgIGNvbnN0IGltYWdlRXh0ZW5zaW9ucyA9IFsnLnBuZycsICcuanBnJywgJy5qcGVnJywgJy5naWYnLCAnLndlYnAnLCAnLmJtcCcsICcuc3ZnJ107XHJcbiAgICBpZiAoaW1hZ2VFeHRlbnNpb25zLnNvbWUoZXh0ID0+IG5hbWUuZW5kc1dpdGgoZXh0KSkpIHtcclxuICAgICAgY29uc29sZS5sb2coJyVjW0RFQlVHXSBEZXRlY3RlZCBhcyBpbWFnZSBieSBleHRlbnNpb24nLCAnY29sb3I6IG9yYW5nZTsnKTtcclxuICAgICAgcmV0dXJuICdpY29uLWdhbGxlcnknO1xyXG4gICAgfVxyXG5cclxuICAgIGNvbnNvbGUubG9nKCclY1tERUJVR10gRGVmYXVsdGluZyB0byBkb2N1bWVudCBpY29uJywgJ2NvbG9yOiByZWQ7Jyk7XHJcbiAgICByZXR1cm4gJ2ljb24tdmFuJztcclxuICB9XHJcbn1cclxuIiwiPGRpdiBjbGFzcz1cInVwbG9hZC1jb250YWluZXJcIj5cclxuXHJcbiAgPCEtLSBEUkFHICYgRFJPUCBBUkVBIC0tPlxyXG4gIDxkaXZcclxuICAgIGNsYXNzPVwiZHJvcC16b25lXCJcclxuICAgIFtjbGFzcy5kcmFnLW92ZXJdPVwiaXNEcmFnT3ZlclwiXHJcbiAgICAoZHJhZ292ZXIpPVwib25EcmFnT3ZlcigkZXZlbnQpXCJcclxuICAgIChkcmFnbGVhdmUpPVwib25EcmFnTGVhdmUoJGV2ZW50KVwiXHJcbiAgICAoZHJvcCk9XCJvbkRyb3AoJGV2ZW50KVwiXHJcbiAgICAoY2xpY2spPVwib25Ecm9wWm9uZUNsaWNrKGZpbGVJbnB1dClcIlxyXG4gID5cclxuXHJcbiAgICA8ZHNvLWJ1dHRvbiBcclxuICAgIHN0eWxlPVwicGFkZGluZzogMDsgXCJcclxuICAgIGJ0bkxhYmVsPVwiU2VsZWN0IGZpbGVzXCJcclxuICAgIGJ0blR5cGU9XCJsaW5rXCJcclxuICAgIChvbkNsaWNrKT1cIm9uQnJvd3NlQnV0dG9uQ2xpY2soJGV2ZW50LCBmaWxlSW5wdXQpXCI+XHJcbiAgICA8L2Rzby1idXR0b24+XHJcbiAgICBvciBkcmFnICYgZHJvcCBmaWxlcyBcclxuICAgIDxpbnB1dCBcclxuICAgICAgdHlwZT1cImZpbGVcIiBcclxuICAgICAgbXVsdGlwbGUgXHJcbiAgICAgIGhpZGRlbiBcclxuICAgICAgI2ZpbGVJbnB1dCBcclxuICAgICAgKGNoYW5nZSk9XCJvbkZpbGVTZWxlY3RlZCgkZXZlbnQpXCJcclxuICAgIC8+XHJcbiAgPC9kaXY+XHJcblxyXG4gIDwhLS0gRklMRSBMSVNUIC0tPlxyXG4gIDxkaXYgY2xhc3M9XCJmaWxlLWxpc3RcIiAqbmdJZj1cInVwbG9hZFF1ZXVlLmxlbmd0aCA+IDBcIj5cclxuXHJcbiAgICA8ZHNvLWZpbGUtaXRlbVxyXG4gICAgKm5nRm9yPVwibGV0IGl0ZW0gb2YgdXBsb2FkUXVldWVcIlxyXG4gICAgW2l0ZW1dPVwiaXRlbVwiXHJcbiAgICAocmV0cnkpPVwicmV0cnlVcGxvYWQoaXRlbSlcIlxyXG4gICAgKHJlbW92ZSk9XCJyZW1vdmVJdGVtKGl0ZW0pXCJcclxuICAgIChjYW5jZWwpPVwiY2FuY2VsVXBsb2FkKGl0ZW0pXCJcclxuICAgID48L2Rzby1maWxlLWl0ZW0+XHJcblxyXG5cclxuICA8L2Rpdj5cclxuPC9kaXY+XHJcbiJdfQ==
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export {};
|
|
2
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBsb2FkLWl0ZW0ubW9kZWwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy91aS9zcmMvbGliL2ZpbGUtdXBsb2FkLW11bHRpcGxlL3VwbG9hZC1pdGVtLm1vZGVsLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiIiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgaW50ZXJmYWNlIFVwbG9hZEl0ZW0ge1xyXG4gIGZpbGU6IEZpbGU7XHJcbiAgcHJvZ3Jlc3M6IG51bWJlciB8IG51bGw7ICAvLyBudWxsID0gcXVldWVkIC8gbm90IHN0YXJ0ZWQgeWV0XHJcbiAgc3RhdHVzOiAncXVldWVkJyB8ICd1cGxvYWRpbmcnIHwgJ2NvbXBsZXRlZCcgfCAnZmFpbGVkJyB8ICdjYW5jZWxsZWQnO1xyXG4gIGNvbnRyb2xsZXI/OiBBYm9ydENvbnRyb2xsZXI7IC8vIHVzZWQgbGF0ZXIgZm9yIHJlYWwgdXBsb2Fkc1xyXG4gIGhpZGVQcm9ncmVzc0Jhcj86IGJvb2xlYW47XHJcbn1cclxuIl19
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// upload-simulator.service.ts
|
|
2
|
+
import { Injectable } from '@angular/core';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
/**
|
|
5
|
+
* UploadSimulatorService
|
|
6
|
+
* -----------------------
|
|
7
|
+
* This service simulates an upload process.
|
|
8
|
+
* It is ONLY for demo and UI development. Later, it can
|
|
9
|
+
* be replaced with a real HTTP upload service without
|
|
10
|
+
* changing the UI components.
|
|
11
|
+
*
|
|
12
|
+
* How it works:
|
|
13
|
+
* - Every 300ms, a timer increases the upload progress.
|
|
14
|
+
* - Progress increases by a random amount (to look realistic).
|
|
15
|
+
* - When it reaches 100%, the upload is marked complete.
|
|
16
|
+
* - There is a 10% chance the upload "fails" to simulate errors.
|
|
17
|
+
* - If an upload is cancelled, the interval stops immediately.
|
|
18
|
+
*
|
|
19
|
+
* This pattern mimics:
|
|
20
|
+
* - progress events (like HttpClient upload events)
|
|
21
|
+
* - cancellation (AbortController / unsubscribe)
|
|
22
|
+
*/
|
|
23
|
+
export class UploadSimulatorService {
|
|
24
|
+
/**
|
|
25
|
+
* Simulates uploading a single file.
|
|
26
|
+
*
|
|
27
|
+
* @param item The UploadItem being uploaded
|
|
28
|
+
* @param onProgress Callback fired whenever progress changes
|
|
29
|
+
* @param onComplete Callback fired when upload reaches 100%
|
|
30
|
+
* @param onError Callback fired when upload "fails"
|
|
31
|
+
*/
|
|
32
|
+
simulateUpload(item, onProgress, onComplete, onError) {
|
|
33
|
+
console.log('%c[SIMULATOR] Starting upload:', 'color: green;', item.file.name);
|
|
34
|
+
let progress = 0;
|
|
35
|
+
// This interval simulates a real upload stream
|
|
36
|
+
const interval = setInterval(() => {
|
|
37
|
+
// If cancelled externally, we stop updating
|
|
38
|
+
if (item.status === 'cancelled') {
|
|
39
|
+
console.log('%c[SIMULATOR] Upload CANCELLED:', 'color: orange;', item.file.name);
|
|
40
|
+
clearInterval(interval);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
// 🔼 Simulate uploading by adding random progress increments
|
|
44
|
+
const increment = Math.random() * 15 + 5;
|
|
45
|
+
progress += increment;
|
|
46
|
+
console.log(`%c[SIMULATOR] ${item.file.name} +${increment.toFixed(1)}% → ${progress.toFixed(1)}%`, 'color: #1e90ff;');
|
|
47
|
+
// 🟦 If progress reaches 100%, finalize upload
|
|
48
|
+
if (progress >= 100) {
|
|
49
|
+
progress = 100;
|
|
50
|
+
onProgress(progress);
|
|
51
|
+
clearInterval(interval);
|
|
52
|
+
console.log('%c[SIMULATOR] Upload reached 100% for ' + item.file.name, 'color: green; font-weight: bold;');
|
|
53
|
+
// 🔥 50% chance to simulate failure
|
|
54
|
+
const randomFail = Math.random() < 0.5;
|
|
55
|
+
if (randomFail) {
|
|
56
|
+
console.log('%c[SIMULATOR] Simulated FAILURE for ' + item.file.name, 'color: red; font-weight: bold;');
|
|
57
|
+
onError();
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
console.log('%c[SIMULATOR] Upload SUCCESS for ' + item.file.name, 'color: limegreen; font-weight: bold;');
|
|
61
|
+
onComplete();
|
|
62
|
+
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
// Emit progress update
|
|
66
|
+
onProgress(progress);
|
|
67
|
+
}, 300); // runs every 300ms like a real upload tick
|
|
68
|
+
}
|
|
69
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UploadSimulatorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
70
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UploadSimulatorService, providedIn: 'root' });
|
|
71
|
+
}
|
|
72
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UploadSimulatorService, decorators: [{
|
|
73
|
+
type: Injectable,
|
|
74
|
+
args: [{ providedIn: 'root' }]
|
|
75
|
+
}] });
|
|
76
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBsb2FkLXNpbXVsYXRvci5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvdWkvc3JjL2xpYi9maWxlLXVwbG9hZC1tdWx0aXBsZS91cGxvYWQtc2ltdWxhdG9yLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsOEJBQThCO0FBQzlCLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxlQUFlLENBQUM7O0FBRzNDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FrQkc7QUFFSCxNQUFNLE9BQU8sc0JBQXNCO0lBRWpDOzs7Ozs7O09BT0c7SUFDSCxjQUFjLENBQ1osSUFBZ0IsRUFDaEIsVUFBbUMsRUFDbkMsVUFBc0IsRUFDdEIsT0FBbUI7UUFFbkIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxnQ0FBZ0MsRUFBRSxlQUFlLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUUvRSxJQUFJLFFBQVEsR0FBRyxDQUFDLENBQUM7UUFFakIsK0NBQStDO1FBQy9DLE1BQU0sUUFBUSxHQUFHLFdBQVcsQ0FBQyxHQUFHLEVBQUU7WUFFaEMsNENBQTRDO1lBQzVDLElBQUksSUFBSSxDQUFDLE1BQU0sS0FBSyxXQUFXLEVBQUUsQ0FBQztnQkFDaEMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxpQ0FBaUMsRUFBRSxnQkFBZ0IsRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO2dCQUNqRixhQUFhLENBQUMsUUFBUSxDQUFDLENBQUM7Z0JBQ3hCLE9BQU87WUFDVCxDQUFDO1lBRUQsNkRBQTZEO1lBQzdELE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxNQUFNLEVBQUUsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDO1lBQ3pDLFFBQVEsSUFBSSxTQUFTLENBQUM7WUFFdEIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEtBQUssU0FBUyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsT0FBTyxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxHQUFHLEVBQy9GLGlCQUFpQixDQUFDLENBQUM7WUFFckIsK0NBQStDO1lBQy9DLElBQUksUUFBUSxJQUFJLEdBQUcsRUFBRSxDQUFDO2dCQUNwQixRQUFRLEdBQUcsR0FBRyxDQUFDO2dCQUNmLFVBQVUsQ0FBQyxRQUFRLENBQUMsQ0FBQztnQkFDckIsYUFBYSxDQUFDLFFBQVEsQ0FBQyxDQUFDO2dCQUV4QixPQUFPLENBQUMsR0FBRyxDQUFDLHdDQUF3QyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUNuRSxrQ0FBa0MsQ0FBQyxDQUFDO2dCQUV0QyxvQ0FBb0M7Z0JBQ3BDLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxNQUFNLEVBQUUsR0FBRyxHQUFHLENBQUM7Z0JBRXZDLElBQUksVUFBVSxFQUFFLENBQUM7b0JBQ2YsT0FBTyxDQUFDLEdBQUcsQ0FBQyxzQ0FBc0MsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksRUFDakUsZ0NBQWdDLENBQUMsQ0FBQztvQkFDcEMsT0FBTyxFQUFFLENBQUM7Z0JBQ1osQ0FBQztxQkFBTSxDQUFDO29CQUNOLE9BQU8sQ0FBQyxHQUFHLENBQUMsbUNBQW1DLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQzlELHNDQUFzQyxDQUFDLENBQUM7b0JBQzFDLFVBQVUsRUFBRSxDQUFDO2dCQUNmLENBQUM7Z0JBRUQsT0FBTztZQUNULENBQUM7WUFFRCx1QkFBdUI7WUFDdkIsVUFBVSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBRXZCLENBQUMsRUFBRSxHQUFHLENBQUMsQ0FBQyxDQUFDLDJDQUEyQztJQUN0RCxDQUFDO3dHQWxFVSxzQkFBc0I7NEdBQXRCLHNCQUFzQixjQURULE1BQU07OzRGQUNuQixzQkFBc0I7a0JBRGxDLFVBQVU7bUJBQUMsRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFIiwic291cmNlc0NvbnRlbnQiOlsiLy8gdXBsb2FkLXNpbXVsYXRvci5zZXJ2aWNlLnRzXHJcbmltcG9ydCB7IEluamVjdGFibGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcclxuaW1wb3J0IHsgVXBsb2FkSXRlbSB9IGZyb20gJy4vdXBsb2FkLWl0ZW0ubW9kZWwnO1xyXG5cclxuLyoqXHJcbiAqIFVwbG9hZFNpbXVsYXRvclNlcnZpY2VcclxuICogLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuICogVGhpcyBzZXJ2aWNlIHNpbXVsYXRlcyBhbiB1cGxvYWQgcHJvY2Vzcy5cclxuICogSXQgaXMgT05MWSBmb3IgZGVtbyBhbmQgVUkgZGV2ZWxvcG1lbnQuIExhdGVyLCBpdCBjYW5cclxuICogYmUgcmVwbGFjZWQgd2l0aCBhIHJlYWwgSFRUUCB1cGxvYWQgc2VydmljZSB3aXRob3V0XHJcbiAqIGNoYW5naW5nIHRoZSBVSSBjb21wb25lbnRzLlxyXG4gKlxyXG4gKiBIb3cgaXQgd29ya3M6XHJcbiAqIC0gRXZlcnkgMzAwbXMsIGEgdGltZXIgaW5jcmVhc2VzIHRoZSB1cGxvYWQgcHJvZ3Jlc3MuXHJcbiAqIC0gUHJvZ3Jlc3MgaW5jcmVhc2VzIGJ5IGEgcmFuZG9tIGFtb3VudCAodG8gbG9vayByZWFsaXN0aWMpLlxyXG4gKiAtIFdoZW4gaXQgcmVhY2hlcyAxMDAlLCB0aGUgdXBsb2FkIGlzIG1hcmtlZCBjb21wbGV0ZS5cclxuICogLSBUaGVyZSBpcyBhIDEwJSBjaGFuY2UgdGhlIHVwbG9hZCBcImZhaWxzXCIgdG8gc2ltdWxhdGUgZXJyb3JzLlxyXG4gKiAtIElmIGFuIHVwbG9hZCBpcyBjYW5jZWxsZWQsIHRoZSBpbnRlcnZhbCBzdG9wcyBpbW1lZGlhdGVseS5cclxuICpcclxuICogVGhpcyBwYXR0ZXJuIG1pbWljczpcclxuICogLSBwcm9ncmVzcyBldmVudHMgKGxpa2UgSHR0cENsaWVudCB1cGxvYWQgZXZlbnRzKVxyXG4gKiAtIGNhbmNlbGxhdGlvbiAoQWJvcnRDb250cm9sbGVyIC8gdW5zdWJzY3JpYmUpXHJcbiAqL1xyXG5ASW5qZWN0YWJsZSh7IHByb3ZpZGVkSW46ICdyb290JyB9KVxyXG5leHBvcnQgY2xhc3MgVXBsb2FkU2ltdWxhdG9yU2VydmljZSB7XHJcblxyXG4gIC8qKlxyXG4gICAqIFNpbXVsYXRlcyB1cGxvYWRpbmcgYSBzaW5nbGUgZmlsZS5cclxuICAgKlxyXG4gICAqIEBwYXJhbSBpdGVtICAgICAgICBUaGUgVXBsb2FkSXRlbSBiZWluZyB1cGxvYWRlZFxyXG4gICAqIEBwYXJhbSBvblByb2dyZXNzICBDYWxsYmFjayBmaXJlZCB3aGVuZXZlciBwcm9ncmVzcyBjaGFuZ2VzXHJcbiAgICogQHBhcmFtIG9uQ29tcGxldGUgIENhbGxiYWNrIGZpcmVkIHdoZW4gdXBsb2FkIHJlYWNoZXMgMTAwJVxyXG4gICAqIEBwYXJhbSBvbkVycm9yICAgICBDYWxsYmFjayBmaXJlZCB3aGVuIHVwbG9hZCBcImZhaWxzXCJcclxuICAgKi9cclxuICBzaW11bGF0ZVVwbG9hZChcclxuICAgIGl0ZW06IFVwbG9hZEl0ZW0sXHJcbiAgICBvblByb2dyZXNzOiAodmFsdWU6IG51bWJlcikgPT4gdm9pZCxcclxuICAgIG9uQ29tcGxldGU6ICgpID0+IHZvaWQsXHJcbiAgICBvbkVycm9yOiAoKSA9PiB2b2lkXHJcbiAgKSB7XHJcbiAgICBjb25zb2xlLmxvZygnJWNbU0lNVUxBVE9SXSBTdGFydGluZyB1cGxvYWQ6JywgJ2NvbG9yOiBncmVlbjsnLCBpdGVtLmZpbGUubmFtZSk7XHJcblxyXG4gICAgbGV0IHByb2dyZXNzID0gMDtcclxuXHJcbiAgICAvLyBUaGlzIGludGVydmFsIHNpbXVsYXRlcyBhIHJlYWwgdXBsb2FkIHN0cmVhbVxyXG4gICAgY29uc3QgaW50ZXJ2YWwgPSBzZXRJbnRlcnZhbCgoKSA9PiB7XHJcblxyXG4gICAgICAvLyBJZiBjYW5jZWxsZWQgZXh0ZXJuYWxseSwgd2Ugc3RvcCB1cGRhdGluZ1xyXG4gICAgICBpZiAoaXRlbS5zdGF0dXMgPT09ICdjYW5jZWxsZWQnKSB7XHJcbiAgICAgICAgY29uc29sZS5sb2coJyVjW1NJTVVMQVRPUl0gVXBsb2FkIENBTkNFTExFRDonLCAnY29sb3I6IG9yYW5nZTsnLCBpdGVtLmZpbGUubmFtZSk7XHJcbiAgICAgICAgY2xlYXJJbnRlcnZhbChpbnRlcnZhbCk7XHJcbiAgICAgICAgcmV0dXJuO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyDwn5S8IFNpbXVsYXRlIHVwbG9hZGluZyBieSBhZGRpbmcgcmFuZG9tIHByb2dyZXNzIGluY3JlbWVudHNcclxuICAgICAgY29uc3QgaW5jcmVtZW50ID0gTWF0aC5yYW5kb20oKSAqIDE1ICsgNTtcclxuICAgICAgcHJvZ3Jlc3MgKz0gaW5jcmVtZW50O1xyXG5cclxuICAgICAgY29uc29sZS5sb2coYCVjW1NJTVVMQVRPUl0gJHtpdGVtLmZpbGUubmFtZX0gKyR7aW5jcmVtZW50LnRvRml4ZWQoMSl9JSDihpIgJHtwcm9ncmVzcy50b0ZpeGVkKDEpfSVgLFxyXG4gICAgICAgICdjb2xvcjogIzFlOTBmZjsnKTtcclxuXHJcbiAgICAgIC8vIPCfn6YgSWYgcHJvZ3Jlc3MgcmVhY2hlcyAxMDAlLCBmaW5hbGl6ZSB1cGxvYWRcclxuICAgICAgaWYgKHByb2dyZXNzID49IDEwMCkge1xyXG4gICAgICAgIHByb2dyZXNzID0gMTAwO1xyXG4gICAgICAgIG9uUHJvZ3Jlc3MocHJvZ3Jlc3MpO1xyXG4gICAgICAgIGNsZWFySW50ZXJ2YWwoaW50ZXJ2YWwpO1xyXG5cclxuICAgICAgICBjb25zb2xlLmxvZygnJWNbU0lNVUxBVE9SXSBVcGxvYWQgcmVhY2hlZCAxMDAlIGZvciAnICsgaXRlbS5maWxlLm5hbWUsXHJcbiAgICAgICAgICAnY29sb3I6IGdyZWVuOyBmb250LXdlaWdodDogYm9sZDsnKTtcclxuXHJcbiAgICAgICAgLy8g8J+UpSA1MCUgY2hhbmNlIHRvIHNpbXVsYXRlIGZhaWx1cmVcclxuICAgICAgICBjb25zdCByYW5kb21GYWlsID0gTWF0aC5yYW5kb20oKSA8IDAuNTtcclxuXHJcbiAgICAgICAgaWYgKHJhbmRvbUZhaWwpIHtcclxuICAgICAgICAgIGNvbnNvbGUubG9nKCclY1tTSU1VTEFUT1JdIFNpbXVsYXRlZCBGQUlMVVJFIGZvciAnICsgaXRlbS5maWxlLm5hbWUsXHJcbiAgICAgICAgICAgICdjb2xvcjogcmVkOyBmb250LXdlaWdodDogYm9sZDsnKTtcclxuICAgICAgICAgIG9uRXJyb3IoKTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgY29uc29sZS5sb2coJyVjW1NJTVVMQVRPUl0gVXBsb2FkIFNVQ0NFU1MgZm9yICcgKyBpdGVtLmZpbGUubmFtZSxcclxuICAgICAgICAgICAgJ2NvbG9yOiBsaW1lZ3JlZW47IGZvbnQtd2VpZ2h0OiBib2xkOycpO1xyXG4gICAgICAgICAgb25Db21wbGV0ZSgpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgcmV0dXJuO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBFbWl0IHByb2dyZXNzIHVwZGF0ZVxyXG4gICAgICBvblByb2dyZXNzKHByb2dyZXNzKTtcclxuXHJcbiAgICB9LCAzMDApOyAvLyBydW5zIGV2ZXJ5IDMwMG1zIGxpa2UgYSByZWFsIHVwbG9hZCB0aWNrXHJcbiAgfVxyXG59XHJcbiJdfQ==
|