@dso-design-system/ui 0.0.1 → 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.
Files changed (65) hide show
  1. package/esm2022/lib/alert/alert.component.mjs +54 -0
  2. package/esm2022/lib/badge/badge.component.mjs +22 -0
  3. package/esm2022/lib/breadcrumb/breadcrumb.component.mjs +29 -0
  4. package/esm2022/lib/checkbox/checkbox.component.mjs +82 -0
  5. package/esm2022/lib/datepicker/datepicker.component.mjs +161 -0
  6. package/esm2022/lib/dialog/dialog.component.mjs +25 -0
  7. package/esm2022/lib/directives/truncate.directive.mjs +71 -0
  8. package/esm2022/lib/dropdown-list/dropdown-list.component.mjs +89 -0
  9. package/esm2022/lib/file-upload-items/file-upload-items.component.mjs +65 -0
  10. package/esm2022/lib/file-upload-multiple/file-upload-multiple.component.mjs +232 -0
  11. package/esm2022/lib/file-upload-multiple/upload-item.model.mjs +2 -0
  12. package/esm2022/lib/file-upload-multiple/upload-simulator.service.mjs +76 -0
  13. package/esm2022/lib/file-upload-single/file-upload-single.component.mjs +100 -0
  14. package/esm2022/lib/input-text/input-text.component.mjs +93 -0
  15. package/esm2022/lib/pagination/pagination.component.mjs +115 -0
  16. package/esm2022/lib/progress-bar/progress-bar.component.mjs +25 -0
  17. package/esm2022/lib/radio/radio.component.mjs +41 -0
  18. package/esm2022/lib/select-dropdown/select-dropdown.component.mjs +228 -0
  19. package/esm2022/lib/service/toast.service.mjs +20 -0
  20. package/esm2022/lib/side-navigation-bar/side-navigation-bar.component.mjs +113 -0
  21. package/esm2022/lib/spinner/spinner.component.mjs +60 -0
  22. package/esm2022/lib/table/table.component.mjs +136 -0
  23. package/esm2022/lib/tabs/tab.component.mjs +20 -0
  24. package/esm2022/lib/tabs/tabs.component.mjs +40 -0
  25. package/esm2022/lib/tag/tag.component.mjs +27 -0
  26. package/esm2022/lib/text-area/text-area.component.mjs +74 -0
  27. package/esm2022/lib/toast/toast.component.mjs +36 -0
  28. package/esm2022/lib/tooltip/tooltip.component.mjs +38 -0
  29. package/esm2022/lib/tooltip/tooltip.directive.mjs +105 -0
  30. package/esm2022/lib/top-navigation-bar/top-navigation-bar.component.mjs +24 -0
  31. package/esm2022/public-api.mjs +27 -2
  32. package/fesm2022/dso-design-system-ui.mjs +2056 -3
  33. package/fesm2022/dso-design-system-ui.mjs.map +1 -1
  34. package/lib/alert/alert.component.d.ts +20 -0
  35. package/lib/badge/badge.component.d.ts +8 -0
  36. package/lib/breadcrumb/breadcrumb.component.d.ts +15 -0
  37. package/lib/checkbox/checkbox.component.d.ts +42 -0
  38. package/lib/datepicker/datepicker.component.d.ts +48 -0
  39. package/lib/dialog/dialog.component.d.ts +10 -0
  40. package/lib/directives/truncate.directive.d.ts +23 -0
  41. package/lib/dropdown-list/dropdown-list.component.d.ts +33 -0
  42. package/lib/file-upload-items/file-upload-items.component.d.ts +27 -0
  43. package/lib/file-upload-multiple/file-upload-multiple.component.d.ts +44 -0
  44. package/lib/file-upload-multiple/upload-item.model.d.ts +7 -0
  45. package/lib/file-upload-multiple/upload-simulator.service.d.ts +34 -0
  46. package/lib/file-upload-single/file-upload-single.component.d.ts +28 -0
  47. package/lib/input-text/input-text.component.d.ts +24 -0
  48. package/lib/pagination/pagination.component.d.ts +31 -0
  49. package/lib/progress-bar/progress-bar.component.d.ts +11 -0
  50. package/lib/radio/radio.component.d.ts +14 -0
  51. package/lib/select-dropdown/select-dropdown.component.d.ts +78 -0
  52. package/lib/service/toast.service.d.ts +16 -0
  53. package/lib/side-navigation-bar/side-navigation-bar.component.d.ts +74 -0
  54. package/lib/spinner/spinner.component.d.ts +23 -0
  55. package/lib/table/table.component.d.ts +43 -0
  56. package/lib/tabs/tab.component.d.ts +9 -0
  57. package/lib/tabs/tabs.component.d.ts +15 -0
  58. package/lib/tag/tag.component.d.ts +10 -0
  59. package/lib/text-area/text-area.component.d.ts +21 -0
  60. package/lib/toast/toast.component.d.ts +13 -0
  61. package/lib/tooltip/tooltip.component.d.ts +15 -0
  62. package/lib/tooltip/tooltip.directive.d.ts +19 -0
  63. package/lib/top-navigation-bar/top-navigation-bar.component.d.ts +16 -0
  64. package/package.json +13 -7
  65. 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==