osl-base-extended 2.0.37 → 3.0.1

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.
@@ -403,12 +403,13 @@ class Httpbase {
403
403
  }
404
404
  }
405
405
  /** Multipart file upload — do NOT pass Content-Type; browser sets the boundary */
406
- async upload(methodName, formData) {
406
+ async upload(methodName, formData, params) {
407
407
  try {
408
408
  const res = await firstValueFrom(this.http
409
409
  .post(this.getEndPoint(methodName), formData, {
410
410
  observe: 'response',
411
411
  headers: this.getUploadHeaders(),
412
+ params: this.buildParams(params || [])
412
413
  })
413
414
  .pipe(timeout(60000)));
414
415
  return this.handleSuccess(res.status, res.body, res.headers);
@@ -5371,6 +5372,217 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
5371
5372
  args: ['click', ['$event']]
5372
5373
  }] } });
5373
5374
 
5375
+ class OslDocumentUploader {
5376
+ label = '';
5377
+ required = false;
5378
+ disabled = false;
5379
+ multiple = false;
5380
+ accept = '';
5381
+ maxSize = 0;
5382
+ minFiles = 0;
5383
+ maxFiles = 0;
5384
+ showUploadButton = false;
5385
+ uploadButtonLabel = 'Upload Files';
5386
+ savedDocuments = [];
5387
+ skeletonLoading = false;
5388
+ skeletonTheme = 'light';
5389
+ uploadCallback = new EventEmitter();
5390
+ viewCallback = new EventEmitter();
5391
+ deleteCallback = new EventEmitter();
5392
+ downloadCallback = new EventEmitter();
5393
+ filesChanged = new EventEmitter();
5394
+ pendingFiles = [];
5395
+ isDragOver = false;
5396
+ sizeErrors = [];
5397
+ touched = false;
5398
+ get maxFilesReached() {
5399
+ return this.multiple && this.maxFiles > 0 && this.pendingFiles.length >= this.maxFiles;
5400
+ }
5401
+ get isBelowMin() {
5402
+ return this.minFiles > 0 && this.pendingFiles.length > 0 && this.pendingFiles.length < this.minFiles;
5403
+ }
5404
+ get isInvalid() {
5405
+ if (!this.touched)
5406
+ return false;
5407
+ if (this.required && this.pendingFiles.length === 0)
5408
+ return true;
5409
+ return this.isBelowMin;
5410
+ }
5411
+ get requiredError() {
5412
+ return this.touched && this.required && this.pendingFiles.length === 0;
5413
+ }
5414
+ get minFilesError() {
5415
+ return this.touched && this.isBelowMin;
5416
+ }
5417
+ get maxSizeLabel() {
5418
+ if (!this.maxSize)
5419
+ return '';
5420
+ if (this.maxSize >= 1024 * 1024)
5421
+ return `Max ${(this.maxSize / 1024 / 1024).toFixed(1)} MB`;
5422
+ if (this.maxSize >= 1024)
5423
+ return `Max ${(this.maxSize / 1024).toFixed(0)} KB`;
5424
+ return `Max ${this.maxSize} B`;
5425
+ }
5426
+ getFileSize(bytes) {
5427
+ if (bytes >= 1024 * 1024)
5428
+ return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
5429
+ if (bytes >= 1024)
5430
+ return `${(bytes / 1024).toFixed(0)} KB`;
5431
+ return `${bytes} B`;
5432
+ }
5433
+ getFileType(name) {
5434
+ const ext = name.split('.').pop()?.toLowerCase() || '';
5435
+ if (['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp', 'bmp', 'ico'].includes(ext))
5436
+ return 'image';
5437
+ if (ext === 'pdf')
5438
+ return 'pdf';
5439
+ if (['doc', 'docx'].includes(ext))
5440
+ return 'word';
5441
+ if (['xls', 'xlsx', 'csv'].includes(ext))
5442
+ return 'excel';
5443
+ if (['zip', 'rar', '7z', 'tar', 'gz'].includes(ext))
5444
+ return 'archive';
5445
+ return 'generic';
5446
+ }
5447
+ formatDate(value) {
5448
+ if (!value)
5449
+ return '';
5450
+ const d = new Date(value);
5451
+ if (isNaN(d.getTime()))
5452
+ return String(value);
5453
+ return d.toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' });
5454
+ }
5455
+ onFileChange(event) {
5456
+ this.touched = true;
5457
+ const input = event.target;
5458
+ this.processFiles(input.files);
5459
+ input.value = '';
5460
+ }
5461
+ onDragOver(event) {
5462
+ event.preventDefault();
5463
+ if (!this.disabled && !this.maxFilesReached)
5464
+ this.isDragOver = true;
5465
+ }
5466
+ onDragLeave(event) {
5467
+ const target = event.currentTarget;
5468
+ const related = event.relatedTarget;
5469
+ if (!related || !target.contains(related))
5470
+ this.isDragOver = false;
5471
+ }
5472
+ onDrop(event) {
5473
+ event.preventDefault();
5474
+ this.isDragOver = false;
5475
+ if (this.disabled || this.maxFilesReached)
5476
+ return;
5477
+ this.touched = true;
5478
+ this.processFiles(event.dataTransfer?.files ?? null);
5479
+ }
5480
+ processFiles(files) {
5481
+ if (!files || files.length === 0)
5482
+ return;
5483
+ this.sizeErrors = [];
5484
+ const newFiles = [];
5485
+ for (let i = 0; i < files.length; i++) {
5486
+ const file = files[i];
5487
+ if (this.maxSize > 0 && file.size > this.maxSize) {
5488
+ this.sizeErrors.push(`"${file.name}" exceeds the ${this.maxSizeLabel} limit.`);
5489
+ continue;
5490
+ }
5491
+ newFiles.push(file);
5492
+ }
5493
+ if (this.multiple) {
5494
+ const remaining = this.maxFiles > 0 ? this.maxFiles - this.pendingFiles.length : newFiles.length;
5495
+ const toAdd = newFiles.slice(0, remaining);
5496
+ if (newFiles.length > remaining) {
5497
+ this.sizeErrors.push(`Only ${remaining} more file(s) can be added. ${newFiles.length - remaining} skipped.`);
5498
+ }
5499
+ this.pendingFiles = [...this.pendingFiles, ...toAdd];
5500
+ }
5501
+ else {
5502
+ this.pendingFiles = newFiles.slice(0, 1);
5503
+ }
5504
+ this.filesChanged.emit(this.pendingFiles);
5505
+ }
5506
+ removeFile(index) {
5507
+ this.pendingFiles = this.pendingFiles.filter((_, i) => i !== index);
5508
+ this.filesChanged.emit(this.pendingFiles);
5509
+ }
5510
+ triggerInput(fileInput) {
5511
+ if (!this.disabled && !this.maxFilesReached)
5512
+ fileInput.click();
5513
+ }
5514
+ upload() {
5515
+ if (this.pendingFiles.length === 0)
5516
+ return;
5517
+ const formData = new FormData();
5518
+ if (this.multiple) {
5519
+ this.pendingFiles.forEach(file => formData.append('files', file, file.name));
5520
+ }
5521
+ else {
5522
+ formData.append('file', this.pendingFiles[0], this.pendingFiles[0].name);
5523
+ }
5524
+ this.uploadCallback.emit(formData);
5525
+ }
5526
+ onView(doc) { this.viewCallback.emit(doc); }
5527
+ onDelete(doc) { this.deleteCallback.emit(doc); }
5528
+ onDownload(doc) { this.downloadCallback.emit(doc); }
5529
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OslDocumentUploader, deps: [], target: i0.ɵɵFactoryTarget.Component });
5530
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: OslDocumentUploader, isStandalone: false, selector: "osl-document-uploader", inputs: { label: "label", required: "required", disabled: "disabled", multiple: "multiple", accept: "accept", maxSize: "maxSize", minFiles: "minFiles", maxFiles: "maxFiles", showUploadButton: "showUploadButton", uploadButtonLabel: "uploadButtonLabel", savedDocuments: "savedDocuments", skeletonLoading: "skeletonLoading", skeletonTheme: "skeletonTheme" }, outputs: { uploadCallback: "uploadCallback", viewCallback: "viewCallback", deleteCallback: "deleteCallback", downloadCallback: "downloadCallback", filesChanged: "filesChanged" }, ngImport: i0, template: "<div class=\"du-container\" [class.du--disabled]=\"disabled\" [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\n\n @if (label) {\n <label class=\"du-label\" [class.du-label--error]=\"isInvalid\">\n <span class=\"du-label__text\" [title]=\"label\">{{ label }}</span>\n @if (required) { <span class=\"du-label__req\">*</span> }\n </label>\n }\n\n <input type=\"file\" [accept]=\"accept\" [multiple]=\"multiple\" [disabled]=\"disabled || maxFilesReached\"\n (change)=\"onFileChange($event)\" #fileInput class=\"du-file-input\">\n\n <!-- \u2500\u2500 Drop Zone \u2500\u2500 -->\n <div class=\"du-dropzone\"\n [class.du-dropzone--over]=\"isDragOver\"\n [class.du-dropzone--error]=\"isInvalid\"\n [class.du-dropzone--disabled]=\"disabled\"\n [class.du-dropzone--maxed]=\"maxFilesReached\"\n (click)=\"triggerInput(fileInput)\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\n\n <div class=\"du-particles\">\n <span class=\"du-p du-p--1\"></span>\n <span class=\"du-p du-p--2\"></span>\n <span class=\"du-p du-p--3\"></span>\n <span class=\"du-p du-p--4\"></span>\n <span class=\"du-p du-p--5\"></span>\n <span class=\"du-p du-p--6\"></span>\n </div>\n\n <div class=\"du-dropzone__inner\">\n\n @if (maxFilesReached) {\n <!-- Max files reached state -->\n <div class=\"du-maxed-icon\">\n <svg viewBox=\"0 0 64 64\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"32\" cy=\"32\" r=\"28\" fill=\"#f0fdf4\" stroke=\"#86efac\" stroke-width=\"2\"/>\n <path d=\"M20 32l8 8 16-16\" stroke=\"#22c55e\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </div>\n <p class=\"du-dropzone__headline\">\n <span class=\"du-gradient-text du-gradient-text--green\">Limit reached</span>\n </p>\n <p class=\"du-maxed-sub\">{{ maxFiles }} / {{ maxFiles }} files added</p>\n } @else {\n\n <!-- Cloud SVG -->\n <div class=\"du-cloud-wrap\" [class.du-cloud-wrap--over]=\"isDragOver\">\n <svg class=\"du-cloud-svg\" viewBox=\"0 0 90 72\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path class=\"du-cloud-body\"\n d=\"M63 42H27C19.8 42 14 36.2 14 29C14 22.2 19.2 16.7 25.8 16.1C25.9 9.4 31.3 4 38 4C42.8 4 47 6.7 49.2 10.7C50.8 9.6 52.8 9 55 9C61 9 65.9 13.9 65.9 19.9V20.1C72.1 20.9 77 26.3 77 33C77 38.1 70.7 42 63 42Z\"\n stroke=\"#667eea\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <line class=\"du-arrow\" x1=\"45\" y1=\"64\" x2=\"45\" y2=\"46\" stroke=\"#764ba2\" stroke-width=\"2.5\" stroke-linecap=\"round\"/>\n <polyline class=\"du-arrow\" points=\"37,55 45,46 53,55\"\n stroke=\"#764ba2\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" fill=\"none\"/>\n <circle class=\"du-cloud-glint\" cx=\"30\" cy=\"26\" r=\"3\" fill=\"#a5b4fc\" opacity=\"0.5\"/>\n </svg>\n </div>\n\n @if (!isDragOver) {\n <p class=\"du-dropzone__headline\">\n <span class=\"du-gradient-text\">Click to browse</span><span class=\"du-or\"> or drag &amp; drop</span>\n </p>\n <div class=\"du-dropzone__meta\">\n @if (multiple) {\n @if (maxFiles > 0 && minFiles > 0) {\n <span class=\"du-chip du-chip--info\">{{ minFiles }}\u2013{{ maxFiles }} files</span>\n } @else if (maxFiles > 0) {\n <span class=\"du-chip du-chip--info\">Max {{ maxFiles }} files</span>\n } @else if (minFiles > 0) {\n <span class=\"du-chip du-chip--info\">Min {{ minFiles }} files</span>\n } @else {\n <span class=\"du-chip du-chip--info\">Multiple files</span>\n }\n } @else {\n <span class=\"du-chip du-chip--info\">Single file</span>\n }\n @if (accept) { <span class=\"du-chip\">{{ accept }}</span> }\n @if (maxSize) { <span class=\"du-chip\">{{ maxSizeLabel }}</span> }\n </div>\n } @else {\n <p class=\"du-dropzone__headline du-dropzone__headline--drop\">\n <span class=\"du-gradient-text\">Release to drop!</span>\n </p>\n }\n\n }\n </div>\n </div>\n\n <!-- \u2500\u2500 Errors \u2500\u2500 -->\n @for (err of sizeErrors; track err) {\n <div class=\"du-msg du-msg--error\">\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"13\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <circle cx=\"12\" cy=\"17\" r=\"1.2\" fill=\"currentColor\"/>\n </svg>\n {{ err }}\n </div>\n }\n @if (requiredError) {\n <div class=\"du-msg du-msg--error\">\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"13\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <circle cx=\"12\" cy=\"17\" r=\"1.2\" fill=\"currentColor\"/>\n </svg>\n {{ label || 'File' }} is required!\n </div>\n }\n @if (minFilesError) {\n <div class=\"du-msg du-msg--error\">\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"13\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <circle cx=\"12\" cy=\"17\" r=\"1.2\" fill=\"currentColor\"/>\n </svg>\n At least {{ minFiles }} files are required ({{ pendingFiles.length }} added).\n </div>\n }\n\n <!-- \u2500\u2500 Pending Files \u2500\u2500 -->\n @if (pendingFiles.length > 0) {\n <div class=\"du-section\">\n <div class=\"du-section__hdr\">\n <span class=\"du-section__title\">Queued Files</span>\n @if (multiple && maxFiles > 0) {\n <span class=\"du-count-label\" [class.du-count-label--full]=\"maxFilesReached\">\n {{ pendingFiles.length }} / {{ maxFiles }}\n </span>\n } @else {\n <span class=\"du-count du-count--purple\">{{ pendingFiles.length }}</span>\n }\n </div>\n <div class=\"du-file-list\">\n @for (file of pendingFiles; track file.name; let i = $index) {\n <div class=\"du-file-card\" [attr.data-ftype]=\"getFileType(file.name)\" [style.animation-delay]=\"i * 40 + 'ms'\">\n\n <div class=\"du-file-icon\">\n @switch (getFileType(file.name)) {\n @case ('pdf') {\n <svg viewBox=\"0 0 40 48\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M6 4h20l10 10v30a2 2 0 01-2 2H6a2 2 0 01-2-2V6a2 2 0 012-2z\" fill=\"#FEE2E2\"/>\n <path d=\"M26 4v10h10\" fill=\"#FECACA\"/>\n <rect x=\"4\" y=\"28\" width=\"32\" height=\"12\" rx=\"2\" fill=\"#EF4444\"/>\n <text x=\"20\" y=\"38\" text-anchor=\"middle\" fill=\"white\" font-size=\"7\" font-weight=\"800\" font-family=\"sans-serif\">PDF</text>\n </svg>\n }\n @case ('word') {\n <svg viewBox=\"0 0 40 48\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M6 4h20l10 10v30a2 2 0 01-2 2H6a2 2 0 01-2-2V6a2 2 0 012-2z\" fill=\"#DBEAFE\"/>\n <path d=\"M26 4v10h10\" fill=\"#BFDBFE\"/>\n <rect x=\"4\" y=\"28\" width=\"32\" height=\"12\" rx=\"2\" fill=\"#3B82F6\"/>\n <text x=\"20\" y=\"38\" text-anchor=\"middle\" fill=\"white\" font-size=\"7\" font-weight=\"800\" font-family=\"sans-serif\">DOC</text>\n </svg>\n }\n @case ('excel') {\n <svg viewBox=\"0 0 40 48\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M6 4h20l10 10v30a2 2 0 01-2 2H6a2 2 0 01-2-2V6a2 2 0 012-2z\" fill=\"#DCFCE7\"/>\n <path d=\"M26 4v10h10\" fill=\"#BBF7D0\"/>\n <rect x=\"4\" y=\"28\" width=\"32\" height=\"12\" rx=\"2\" fill=\"#22C55E\"/>\n <text x=\"20\" y=\"38\" text-anchor=\"middle\" fill=\"white\" font-size=\"7\" font-weight=\"800\" font-family=\"sans-serif\">XLS</text>\n </svg>\n }\n @case ('image') {\n <svg viewBox=\"0 0 40 48\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M6 4h20l10 10v30a2 2 0 01-2 2H6a2 2 0 01-2-2V6a2 2 0 012-2z\" fill=\"#FEF9C3\"/>\n <path d=\"M26 4v10h10\" fill=\"#FEF08A\"/>\n <rect x=\"6\" y=\"18\" width=\"28\" height=\"18\" rx=\"2\" fill=\"#FDE047\"/>\n <circle cx=\"13\" cy=\"24\" r=\"3\" fill=\"#CA8A04\"/>\n <path d=\"M6 36l9-9 5 5 4-4 10 8H6Z\" fill=\"#EAB308\" opacity=\"0.8\"/>\n </svg>\n }\n @case ('archive') {\n <svg viewBox=\"0 0 40 48\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M6 4h20l10 10v30a2 2 0 01-2 2H6a2 2 0 01-2-2V6a2 2 0 012-2z\" fill=\"#EDE9FE\"/>\n <path d=\"M26 4v10h10\" fill=\"#DDD6FE\"/>\n <rect x=\"14\" y=\"12\" width=\"12\" height=\"4\" rx=\"1\" fill=\"#A78BFA\"/>\n <rect x=\"14\" y=\"18\" width=\"12\" height=\"4\" rx=\"1\" fill=\"#C4B5FD\"/>\n <rect x=\"14\" y=\"24\" width=\"12\" height=\"4\" rx=\"1\" fill=\"#A78BFA\"/>\n <rect x=\"14\" y=\"30\" width=\"12\" height=\"4\" rx=\"1\" fill=\"#C4B5FD\"/>\n <rect x=\"16\" y=\"26\" width=\"8\" height=\"6\" rx=\"1\" fill=\"#7C3AED\"/>\n </svg>\n }\n @default {\n <svg viewBox=\"0 0 40 48\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M6 4h20l10 10v30a2 2 0 01-2 2H6a2 2 0 01-2-2V6a2 2 0 012-2z\" fill=\"#F1F5F9\"/>\n <path d=\"M26 4v10h10\" fill=\"#E2E8F0\"/>\n <line x1=\"10\" y1=\"24\" x2=\"30\" y2=\"24\" stroke=\"#94A3B8\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <line x1=\"10\" y1=\"30\" x2=\"26\" y2=\"30\" stroke=\"#94A3B8\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <line x1=\"10\" y1=\"36\" x2=\"22\" y2=\"36\" stroke=\"#94A3B8\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n </svg>\n }\n }\n </div>\n\n <div class=\"du-file-info\">\n <span class=\"du-file-name\" [title]=\"file.name\">{{ file.name }}</span>\n <span class=\"du-file-size\">{{ getFileSize(file.size) }}</span>\n </div>\n\n <button type=\"button\" class=\"du-remove-btn\" (click)=\"removeFile(i)\" title=\"Remove\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <path d=\"M15 9L9 15M9 9l6 6\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n </svg>\n </button>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- \u2500\u2500 Upload Button \u2500\u2500 -->\n @if (showUploadButton && pendingFiles.length > 0) {\n <button type=\"button\" class=\"du-upload-btn\" (click)=\"upload()\" [disabled]=\"disabled\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" width=\"17\" height=\"17\">\n <path d=\"M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\"/>\n <polyline points=\"17,8 12,3 7,8\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" fill=\"none\"/>\n <line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\"/>\n </svg>\n {{ uploadButtonLabel }}\n <span class=\"du-upload-btn__badge\">{{ pendingFiles.length }}</span>\n </button>\n }\n\n <!-- \u2500\u2500 Saved Documents \u2500\u2500 -->\n @if (savedDocuments.length > 0) {\n <div class=\"du-section\">\n <div class=\"du-section__hdr\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" class=\"du-section__icon\">\n <path d=\"M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z\" stroke=\"#10b981\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <polyline points=\"14,2 14,8 20,8\" stroke=\"#10b981\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n </svg>\n <span class=\"du-section__title\">Saved Documents</span>\n <span class=\"du-count du-count--green\">{{ savedDocuments.length }}</span>\n </div>\n <div class=\"du-saved-list\">\n @for (doc of savedDocuments; track doc.id; let i = $index) {\n <div class=\"du-saved-card\" [style.animation-delay]=\"i * 50 + 'ms'\">\n\n <div class=\"du-saved-icon\">\n @switch (getFileType(doc.name)) {\n @case ('pdf') {\n <svg viewBox=\"0 0 32 38\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M4 3h16l8 8v24a1.5 1.5 0 01-1.5 1.5H4A1.5 1.5 0 012.5 35V4.5A1.5 1.5 0 014 3z\" fill=\"#FEE2E2\"/>\n <path d=\"M20 3v8h8\" fill=\"#FECACA\"/>\n <rect x=\"2\" y=\"22\" width=\"28\" height=\"10\" rx=\"2\" fill=\"#EF4444\"/>\n <text x=\"16\" y=\"30\" text-anchor=\"middle\" fill=\"white\" font-size=\"6\" font-weight=\"800\" font-family=\"sans-serif\">PDF</text>\n </svg>\n }\n @case ('word') {\n <svg viewBox=\"0 0 32 38\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M4 3h16l8 8v24a1.5 1.5 0 01-1.5 1.5H4A1.5 1.5 0 012.5 35V4.5A1.5 1.5 0 014 3z\" fill=\"#DBEAFE\"/>\n <path d=\"M20 3v8h8\" fill=\"#BFDBFE\"/>\n <rect x=\"2\" y=\"22\" width=\"28\" height=\"10\" rx=\"2\" fill=\"#3B82F6\"/>\n <text x=\"16\" y=\"30\" text-anchor=\"middle\" fill=\"white\" font-size=\"6\" font-weight=\"800\" font-family=\"sans-serif\">DOC</text>\n </svg>\n }\n @case ('excel') {\n <svg viewBox=\"0 0 32 38\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M4 3h16l8 8v24a1.5 1.5 0 01-1.5 1.5H4A1.5 1.5 0 012.5 35V4.5A1.5 1.5 0 014 3z\" fill=\"#DCFCE7\"/>\n <path d=\"M20 3v8h8\" fill=\"#BBF7D0\"/>\n <rect x=\"2\" y=\"22\" width=\"28\" height=\"10\" rx=\"2\" fill=\"#22C55E\"/>\n <text x=\"16\" y=\"30\" text-anchor=\"middle\" fill=\"white\" font-size=\"6\" font-weight=\"800\" font-family=\"sans-serif\">XLS</text>\n </svg>\n }\n @case ('image') {\n <svg viewBox=\"0 0 32 32\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect width=\"32\" height=\"32\" rx=\"6\" fill=\"#FEF9C3\"/>\n <rect x=\"4\" y=\"7\" width=\"24\" height=\"18\" rx=\"2\" fill=\"#FDE047\"/>\n <circle cx=\"10\" cy=\"13\" r=\"2.5\" fill=\"#CA8A04\"/>\n <path d=\"M4 25l8-8 5 5 4-4 11 7H4Z\" fill=\"#EAB308\" opacity=\"0.7\"/>\n </svg>\n }\n @case ('archive') {\n <svg viewBox=\"0 0 32 32\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect width=\"32\" height=\"32\" rx=\"6\" fill=\"#EDE9FE\"/>\n <rect x=\"4\" y=\"8\" width=\"24\" height=\"5\" rx=\"1.5\" fill=\"#A78BFA\"/>\n <rect x=\"4\" y=\"14\" width=\"24\" height=\"14\" rx=\"2\" fill=\"#C4B5FD\"/>\n <rect x=\"11\" y=\"18\" width=\"10\" height=\"6\" rx=\"1.5\" fill=\"#7C3AED\"/>\n <line x1=\"16\" y1=\"18\" x2=\"16\" y2=\"24\" stroke=\"white\" stroke-width=\"1.5\"/>\n </svg>\n }\n @default {\n <svg viewBox=\"0 0 32 38\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M4 3h16l8 8v24a1.5 1.5 0 01-1.5 1.5H4A1.5 1.5 0 012.5 35V4.5A1.5 1.5 0 014 3z\" fill=\"#F1F5F9\"/>\n <path d=\"M20 3v8h8\" fill=\"#E2E8F0\"/>\n <line x1=\"7\" y1=\"20\" x2=\"25\" y2=\"20\" stroke=\"#94A3B8\" stroke-width=\"1.8\" stroke-linecap=\"round\"/>\n <line x1=\"7\" y1=\"25\" x2=\"21\" y2=\"25\" stroke=\"#94A3B8\" stroke-width=\"1.8\" stroke-linecap=\"round\"/>\n <line x1=\"7\" y1=\"30\" x2=\"17\" y2=\"30\" stroke=\"#94A3B8\" stroke-width=\"1.8\" stroke-linecap=\"round\"/>\n </svg>\n }\n }\n </div>\n\n <div class=\"du-saved-info\">\n <span class=\"du-saved-name\" [title]=\"doc.name\">{{ doc.name }}</span>\n @if (doc.addBy || doc.addOn) {\n <span class=\"du-saved-meta\">\n @if (doc.addBy) { <span class=\"du-saved-by\">{{ doc.addBy }}</span> }\n @if (doc.addBy && doc.addOn) { <span class=\"du-saved-sep\">\u00B7</span> }\n @if (doc.addOn) { <span class=\"du-saved-on\">{{ formatDate(doc.addOn) }}</span> }\n </span>\n }\n </div>\n\n <div class=\"du-saved-actions\">\n <button type=\"button\" class=\"du-act du-act--view\" (click)=\"onView(doc)\" title=\"View\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <circle cx=\"12\" cy=\"12\" r=\"3\" stroke=\"currentColor\" stroke-width=\"2\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"du-act du-act--download\" (click)=\"onDownload(doc)\" title=\"Download\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <polyline points=\"7,10 12,15 17,10\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" fill=\"none\"/>\n <line x1=\"12\" y1=\"15\" x2=\"12\" y2=\"3\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"du-act du-act--delete\" (click)=\"onDelete(doc)\" title=\"Delete\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <polyline points=\"3,6 5,6 21,6\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <path d=\"M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <path d=\"M10 11v6M14 11v6\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <path d=\"M9 6V4a1 1 0 011-1h4a1 1 0 011 1v2\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n </svg>\n </button>\n </div>\n\n </div>\n }\n </div>\n </div>\n }\n\n</div>\n", styles: ["@keyframes du-float{0%,to{transform:translateY(0)}50%{transform:translateY(-9px)}}@keyframes du-arrow-bounce{0%,to{transform:translateY(0)}50%{transform:translateY(-6px)}}@keyframes du-particle{0%,to{transform:translate(0) scale(1);opacity:.25}33%{transform:translate(4px,-14px) scale(1.15);opacity:.5}66%{transform:translate(-4px,-7px) scale(.88);opacity:.2}}@keyframes du-slide-in{0%{opacity:0;transform:translate(18px)}to{opacity:1;transform:translate(0)}}@keyframes du-fade-up{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@keyframes du-pop-in{0%{opacity:0;transform:scale(.8)}70%{transform:scale(1.06)}to{opacity:1;transform:scale(1)}}@keyframes du-glow-pulse{0%,to{box-shadow:0 0 #667eea59}50%{box-shadow:0 0 0 8px #667eea00}}@keyframes du-btn-pulse{0%,to{box-shadow:0 4px 20px #667eea66}50%{box-shadow:0 6px 30px #764ba299}}@keyframes du-gradient-shift{0%{background-position:0% 50%}50%{background-position:100% 50%}to{background-position:0% 50%}}@keyframes du-ripple{0%{transform:scale(.6);opacity:.7}to{transform:scale(2.2);opacity:0}}@keyframes du-glint{0%,80%,to{opacity:0;transform:scale(.5)}40%{opacity:.7;transform:scale(1.4)}}.du-container{display:flex;flex-direction:column;gap:10px;width:100%}.du-container.du--disabled{opacity:.55;pointer-events:none}.du-label{display:flex;align-items:center;gap:2px;font-size:var(--osl-label-font-size, 13px);font-weight:500;color:#374151;overflow:hidden}.du-label--error{color:var(--osl-error-color, #ef4444)}.du-label__text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.du-label__req{color:var(--osl-error-color, #ef4444);flex-shrink:0;font-weight:700}.du-file-input{display:none}.du-dropzone{position:relative;width:100%;min-height:158px;border-radius:18px;border:2px dashed rgba(102,126,234,.35);background:linear-gradient(145deg,#fafbff,#f5f3ff);cursor:pointer;overflow:hidden;transition:border-color .3s ease,background .3s ease,box-shadow .3s ease,transform .25s ease}.du-dropzone:hover:not(.du-dropzone--disabled){border-color:#667eeab3;background:linear-gradient(145deg,#f5f3ff,#ede9fe);transform:translateY(-2px);box-shadow:0 10px 30px #667eea1f}.du-dropzone--over{border:2px solid #667eea!important;background:linear-gradient(145deg,#ede9fe,#ddd6fe)!important;transform:scale(1.012) translateY(-3px)!important;box-shadow:0 18px 40px #667eea38,inset 0 0 0 1px #667eea4d!important}.du-dropzone--over .du-cloud-body{stroke:#764ba2}.du-dropzone--over .du-cloud-glint{animation:du-glint .6s ease infinite}.du-dropzone--error{border-color:#ef444480;background:linear-gradient(145deg,#fff5f5,#fee2e2);animation:du-glow-pulse 1.8s ease infinite}.du-dropzone--disabled{cursor:not-allowed;background:#f9fafb!important;border-color:#e5e7eb!important;box-shadow:none!important;transform:none!important}.du-particles{position:absolute;inset:0;pointer-events:none;overflow:hidden}.du-p{position:absolute;border-radius:50%;background:radial-gradient(circle,#667eea38,#764ba21f);animation:du-particle var(--dur, 4s) ease-in-out infinite}.du-p--1{width:22px;height:22px;top:12%;left:6%;--dur: 4.2s;animation-delay:0s}.du-p--2{width:14px;height:14px;top:65%;left:88%;--dur: 5.1s;animation-delay:.9s}.du-p--3{width:18px;height:18px;top:18%;left:82%;--dur: 3.8s;animation-delay:1.6s}.du-p--4{width:10px;height:10px;top:75%;left:12%;--dur: 6.3s;animation-delay:.4s}.du-p--5{width:16px;height:16px;top:50%;left:48%;--dur: 4.7s;animation-delay:2.1s}.du-p--6{width:8px;height:8px;top:30%;left:38%;--dur: 5.5s;animation-delay:1.1s}.du-dropzone__inner{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;padding:26px 20px;min-height:158px}.du-cloud-wrap{width:80px;height:64px;animation:du-float 3.2s ease-in-out infinite;filter:drop-shadow(0 4px 10px rgba(102,126,234,.2));transition:filter .3s ease;flex-shrink:0}.du-cloud-wrap--over{animation-duration:1.2s;filter:drop-shadow(0 6px 18px rgba(102,126,234,.45))}.du-cloud-svg{width:100%;height:100%}.du-cloud-body{transition:stroke .3s ease}.du-cloud-glint{transform-origin:center}.du-arrow{animation:du-arrow-bounce 1.6s ease-in-out infinite}.du-dropzone__headline{margin:0;font-size:14px;color:#6b7280;line-height:1.5;text-align:center}.du-dropzone__headline--drop .du-gradient-text{animation:du-glow-pulse .7s ease infinite}.du-gradient-text{background:linear-gradient(135deg,#667eea,#764ba2);background-size:200% 200%;-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;font-weight:700;animation:du-gradient-shift 3s ease infinite}.du-or{color:#9ca3af;font-size:13px}.du-dropzone__meta{display:flex;flex-wrap:wrap;justify-content:center;gap:6px}.du-chip{border-radius:20px;padding:2px 10px;font-size:11px;font-weight:500;background:#667eea14;color:#7c6fcd;border:1px solid rgba(102,126,234,.18);transition:background .2s}.du-chip--info{background:#10b98114;color:#059669;border-color:#10b98133}.du-msg{display:flex;align-items:center;gap:6px;font-size:var(--osl-hint-font-size, 11px);animation:du-fade-up .2s ease}.du-msg svg{flex-shrink:0}.du-msg--error{color:var(--osl-error-color, #ef4444)}.du-section{display:flex;flex-direction:column;gap:8px;animation:du-fade-up .3s ease}.du-section__hdr{display:flex;align-items:center;gap:7px}.du-section__icon{flex-shrink:0}.du-section__title{font-size:11px;font-weight:700;color:#9ca3af;text-transform:uppercase;letter-spacing:.07em}.du-count{width:19px;height:19px;border-radius:50%;font-size:10px;font-weight:800;color:#fff;display:flex;align-items:center;justify-content:center;flex-shrink:0;animation:du-pop-in .3s ease}.du-count--purple{background:linear-gradient(135deg,#667eea,#764ba2)}.du-count--green{background:linear-gradient(135deg,#10b981,#059669)}.du-file-list{display:flex;flex-direction:column;gap:7px}.du-file-card{display:flex;align-items:center;gap:11px;padding:10px 14px 10px 16px;background:#fff;border-radius:13px;border:1px solid #e9ecf1;animation:du-slide-in .28s ease both;transition:border-color .2s ease,box-shadow .2s ease,transform .2s ease;position:relative}.du-file-card:before{content:\"\";position:absolute;left:0;top:50%;transform:translateY(-50%);width:4px;height:60%;border-radius:0 4px 4px 0;transition:height .2s ease}.du-file-card[data-ftype=pdf]:before{background:#ef4444}.du-file-card[data-ftype=word]:before{background:#3b82f6}.du-file-card[data-ftype=excel]:before{background:#22c55e}.du-file-card[data-ftype=image]:before{background:#f59e0b}.du-file-card[data-ftype=archive]:before{background:#8b5cf6}.du-file-card[data-ftype=generic]:before{background:#94a3b8}.du-file-card:hover{border-color:#c4b5fd;box-shadow:0 3px 14px #667eea1a;transform:translate(3px)}.du-file-card:hover:before{height:75%}.du-file-icon{width:38px;height:46px;flex-shrink:0}.du-file-icon svg{width:100%;height:100%}.du-file-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}.du-file-name{font-size:13px;font-weight:600;color:#1e293b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.du-file-size{font-size:11px;color:#94a3b8;font-weight:500}.du-remove-btn{width:30px;height:30px;flex-shrink:0;border-radius:50%;border:1.5px solid #fecaca;background:#fef2f2;color:#fca5a5;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:6px;transition:all .22s ease}.du-remove-btn svg{width:100%;height:100%}.du-remove-btn:hover{background:#fee2e2;border-color:#ef4444;color:#ef4444;transform:rotate(90deg) scale(1.1);box-shadow:0 3px 10px #ef444440}.du-upload-btn{display:flex;align-items:center;justify-content:center;gap:8px;padding:12px 24px;width:100%;border:none;border-radius:13px;background:linear-gradient(135deg,#667eea,#764ba2);background-size:200% 200%;color:#fff;font-size:14px;font-weight:700;letter-spacing:.03em;cursor:pointer;transition:transform .25s ease,box-shadow .25s ease;animation:du-btn-pulse 2.5s ease-in-out infinite,du-gradient-shift 4s ease infinite}.du-upload-btn svg{flex-shrink:0;transition:transform .25s ease}.du-upload-btn:hover:not([disabled]){transform:translateY(-3px);box-shadow:0 10px 28px #667eea80;animation:du-gradient-shift 1.5s ease infinite}.du-upload-btn:hover:not([disabled]) svg{transform:translateY(-3px)}.du-upload-btn:active:not([disabled]){transform:translateY(-1px)}.du-upload-btn[disabled]{opacity:.45;cursor:not-allowed;animation:none}.du-upload-btn__badge{min-width:22px;height:22px;padding:0 6px;border-radius:11px;background:#ffffff38;font-size:11px;font-weight:800;display:flex;align-items:center;justify-content:center;animation:du-pop-in .3s ease}.du-saved-list{display:flex;flex-direction:column;gap:7px}.du-saved-card{display:flex;align-items:center;gap:11px;padding:10px 12px;background:linear-gradient(135deg,#fff,#f8faff);border-radius:13px;border:1px solid #e9ecf1;opacity:0;animation:du-fade-up .32s ease forwards;transition:border-color .2s ease,box-shadow .2s ease,transform .2s ease}.du-saved-card:hover{border-color:#a5b4fc;box-shadow:0 4px 18px #667eea1a;transform:translateY(-1px)}.du-saved-icon{width:32px;height:38px;flex-shrink:0}.du-saved-icon svg{width:100%;height:100%}.du-saved-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}.du-saved-name{font-size:13px;font-weight:600;color:#1e293b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.du-saved-size{font-size:11px;color:#94a3b8;font-weight:500}.du-saved-actions{display:flex;align-items:center;gap:5px;flex-shrink:0}.du-act{width:32px;height:32px;border-radius:9px;border:1.5px solid transparent;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:7px;transition:all .22s ease;position:relative;overflow:hidden}.du-act svg{width:100%;height:100%;transition:transform .22s ease}.du-act:after{content:\"\";position:absolute;inset:50%;border-radius:50%;transition:all 0s;pointer-events:none}.du-act:active:after{inset:0;border-radius:9px;background:#ffffff59;animation:du-ripple .4s ease}.du-act--view{background:#eff6ff;color:#3b82f6;border-color:#bfdbfe}.du-act--view:hover{background:#dbeafe;border-color:#93c5fd;box-shadow:0 3px 12px #3b82f64d}.du-act--view:hover svg{transform:scale(1.12)}.du-act--download{background:#f0fdf4;color:#22c55e;border-color:#bbf7d0}.du-act--download:hover{background:#dcfce7;border-color:#86efac;box-shadow:0 3px 12px #22c55e4d}.du-act--download:hover svg{transform:translateY(2px) scale(1.08)}.du-act--delete{background:#fef2f2;color:#ef4444;border-color:#fecaca}.du-act--delete:hover{background:#fee2e2;border-color:#fca5a5;box-shadow:0 3px 12px #ef44444d}.du-act--delete:hover svg{transform:scale(1.1) rotate(-5deg)}.du-dropzone--maxed{cursor:default;border-color:#22c55e66!important;background:linear-gradient(145deg,#f0fdf4,#dcfce7)!important;pointer-events:none}.du-dropzone--maxed .du-particles .du-p{background:radial-gradient(circle,#22c55e2e,#10b9811a)}.du-maxed-icon{width:56px;height:56px;animation:du-pop-in .4s cubic-bezier(.34,1.56,.64,1)}.du-maxed-icon svg{width:100%;height:100%}.du-gradient-text--green{background:linear-gradient(135deg,#22c55e,#059669)!important;background-size:200% 200%!important;-webkit-background-clip:text!important;-webkit-text-fill-color:transparent!important;background-clip:text!important}.du-maxed-sub{margin:0;font-size:12px;color:#6b7280;font-weight:500}.du-count-label{font-size:11px;font-weight:700;padding:2px 9px;border-radius:20px;background:#667eea1a;color:#667eea;border:1px solid rgba(102,126,234,.2);transition:all .25s ease;animation:du-pop-in .3s ease}.du-count-label--full{background:#22c55e1a;color:#16a34a;border-color:#22c55e40}.du-saved-meta{display:flex;align-items:center;gap:4px;flex-wrap:wrap}.du-saved-by{font-size:11px;font-weight:600;color:#667eea}.du-saved-sep{font-size:11px;color:#d1d5db}.du-saved-on{font-size:11px;color:#94a3b8}\n"], dependencies: [{ kind: "directive", type: OslSkeletonDirective, selector: "[oslSkeleton]", inputs: ["oslSkeleton", "oslSkeletonType", "oslSkeletonAnimation", "oslSkeletonTheme", "oslSkeletonColor", "oslSkeletonHighlight", "oslSkeletonRadius", "oslSkeletonRows", "oslSkeletonRowGap", "oslSkeletonZIndex", "oslSkeletonDelay", "oslSkeletonDuration", "oslSkeletonMinHeight", "oslSkeletonForceReread", "oslSkeletonCircleSize", "oslSkeletonListItems", "oslSkeletonTableRows", "oslSkeletonTableCols", "oslSkeletonCardLines", "oslSkeletonBgColor"] }] });
5531
+ }
5532
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OslDocumentUploader, decorators: [{
5533
+ type: Component,
5534
+ args: [{ selector: 'osl-document-uploader', standalone: false, template: "<div class=\"du-container\" [class.du--disabled]=\"disabled\" [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\n\n @if (label) {\n <label class=\"du-label\" [class.du-label--error]=\"isInvalid\">\n <span class=\"du-label__text\" [title]=\"label\">{{ label }}</span>\n @if (required) { <span class=\"du-label__req\">*</span> }\n </label>\n }\n\n <input type=\"file\" [accept]=\"accept\" [multiple]=\"multiple\" [disabled]=\"disabled || maxFilesReached\"\n (change)=\"onFileChange($event)\" #fileInput class=\"du-file-input\">\n\n <!-- \u2500\u2500 Drop Zone \u2500\u2500 -->\n <div class=\"du-dropzone\"\n [class.du-dropzone--over]=\"isDragOver\"\n [class.du-dropzone--error]=\"isInvalid\"\n [class.du-dropzone--disabled]=\"disabled\"\n [class.du-dropzone--maxed]=\"maxFilesReached\"\n (click)=\"triggerInput(fileInput)\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\n\n <div class=\"du-particles\">\n <span class=\"du-p du-p--1\"></span>\n <span class=\"du-p du-p--2\"></span>\n <span class=\"du-p du-p--3\"></span>\n <span class=\"du-p du-p--4\"></span>\n <span class=\"du-p du-p--5\"></span>\n <span class=\"du-p du-p--6\"></span>\n </div>\n\n <div class=\"du-dropzone__inner\">\n\n @if (maxFilesReached) {\n <!-- Max files reached state -->\n <div class=\"du-maxed-icon\">\n <svg viewBox=\"0 0 64 64\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"32\" cy=\"32\" r=\"28\" fill=\"#f0fdf4\" stroke=\"#86efac\" stroke-width=\"2\"/>\n <path d=\"M20 32l8 8 16-16\" stroke=\"#22c55e\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </div>\n <p class=\"du-dropzone__headline\">\n <span class=\"du-gradient-text du-gradient-text--green\">Limit reached</span>\n </p>\n <p class=\"du-maxed-sub\">{{ maxFiles }} / {{ maxFiles }} files added</p>\n } @else {\n\n <!-- Cloud SVG -->\n <div class=\"du-cloud-wrap\" [class.du-cloud-wrap--over]=\"isDragOver\">\n <svg class=\"du-cloud-svg\" viewBox=\"0 0 90 72\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path class=\"du-cloud-body\"\n d=\"M63 42H27C19.8 42 14 36.2 14 29C14 22.2 19.2 16.7 25.8 16.1C25.9 9.4 31.3 4 38 4C42.8 4 47 6.7 49.2 10.7C50.8 9.6 52.8 9 55 9C61 9 65.9 13.9 65.9 19.9V20.1C72.1 20.9 77 26.3 77 33C77 38.1 70.7 42 63 42Z\"\n stroke=\"#667eea\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <line class=\"du-arrow\" x1=\"45\" y1=\"64\" x2=\"45\" y2=\"46\" stroke=\"#764ba2\" stroke-width=\"2.5\" stroke-linecap=\"round\"/>\n <polyline class=\"du-arrow\" points=\"37,55 45,46 53,55\"\n stroke=\"#764ba2\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" fill=\"none\"/>\n <circle class=\"du-cloud-glint\" cx=\"30\" cy=\"26\" r=\"3\" fill=\"#a5b4fc\" opacity=\"0.5\"/>\n </svg>\n </div>\n\n @if (!isDragOver) {\n <p class=\"du-dropzone__headline\">\n <span class=\"du-gradient-text\">Click to browse</span><span class=\"du-or\"> or drag &amp; drop</span>\n </p>\n <div class=\"du-dropzone__meta\">\n @if (multiple) {\n @if (maxFiles > 0 && minFiles > 0) {\n <span class=\"du-chip du-chip--info\">{{ minFiles }}\u2013{{ maxFiles }} files</span>\n } @else if (maxFiles > 0) {\n <span class=\"du-chip du-chip--info\">Max {{ maxFiles }} files</span>\n } @else if (minFiles > 0) {\n <span class=\"du-chip du-chip--info\">Min {{ minFiles }} files</span>\n } @else {\n <span class=\"du-chip du-chip--info\">Multiple files</span>\n }\n } @else {\n <span class=\"du-chip du-chip--info\">Single file</span>\n }\n @if (accept) { <span class=\"du-chip\">{{ accept }}</span> }\n @if (maxSize) { <span class=\"du-chip\">{{ maxSizeLabel }}</span> }\n </div>\n } @else {\n <p class=\"du-dropzone__headline du-dropzone__headline--drop\">\n <span class=\"du-gradient-text\">Release to drop!</span>\n </p>\n }\n\n }\n </div>\n </div>\n\n <!-- \u2500\u2500 Errors \u2500\u2500 -->\n @for (err of sizeErrors; track err) {\n <div class=\"du-msg du-msg--error\">\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"13\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <circle cx=\"12\" cy=\"17\" r=\"1.2\" fill=\"currentColor\"/>\n </svg>\n {{ err }}\n </div>\n }\n @if (requiredError) {\n <div class=\"du-msg du-msg--error\">\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"13\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <circle cx=\"12\" cy=\"17\" r=\"1.2\" fill=\"currentColor\"/>\n </svg>\n {{ label || 'File' }} is required!\n </div>\n }\n @if (minFilesError) {\n <div class=\"du-msg du-msg--error\">\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"13\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <circle cx=\"12\" cy=\"17\" r=\"1.2\" fill=\"currentColor\"/>\n </svg>\n At least {{ minFiles }} files are required ({{ pendingFiles.length }} added).\n </div>\n }\n\n <!-- \u2500\u2500 Pending Files \u2500\u2500 -->\n @if (pendingFiles.length > 0) {\n <div class=\"du-section\">\n <div class=\"du-section__hdr\">\n <span class=\"du-section__title\">Queued Files</span>\n @if (multiple && maxFiles > 0) {\n <span class=\"du-count-label\" [class.du-count-label--full]=\"maxFilesReached\">\n {{ pendingFiles.length }} / {{ maxFiles }}\n </span>\n } @else {\n <span class=\"du-count du-count--purple\">{{ pendingFiles.length }}</span>\n }\n </div>\n <div class=\"du-file-list\">\n @for (file of pendingFiles; track file.name; let i = $index) {\n <div class=\"du-file-card\" [attr.data-ftype]=\"getFileType(file.name)\" [style.animation-delay]=\"i * 40 + 'ms'\">\n\n <div class=\"du-file-icon\">\n @switch (getFileType(file.name)) {\n @case ('pdf') {\n <svg viewBox=\"0 0 40 48\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M6 4h20l10 10v30a2 2 0 01-2 2H6a2 2 0 01-2-2V6a2 2 0 012-2z\" fill=\"#FEE2E2\"/>\n <path d=\"M26 4v10h10\" fill=\"#FECACA\"/>\n <rect x=\"4\" y=\"28\" width=\"32\" height=\"12\" rx=\"2\" fill=\"#EF4444\"/>\n <text x=\"20\" y=\"38\" text-anchor=\"middle\" fill=\"white\" font-size=\"7\" font-weight=\"800\" font-family=\"sans-serif\">PDF</text>\n </svg>\n }\n @case ('word') {\n <svg viewBox=\"0 0 40 48\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M6 4h20l10 10v30a2 2 0 01-2 2H6a2 2 0 01-2-2V6a2 2 0 012-2z\" fill=\"#DBEAFE\"/>\n <path d=\"M26 4v10h10\" fill=\"#BFDBFE\"/>\n <rect x=\"4\" y=\"28\" width=\"32\" height=\"12\" rx=\"2\" fill=\"#3B82F6\"/>\n <text x=\"20\" y=\"38\" text-anchor=\"middle\" fill=\"white\" font-size=\"7\" font-weight=\"800\" font-family=\"sans-serif\">DOC</text>\n </svg>\n }\n @case ('excel') {\n <svg viewBox=\"0 0 40 48\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M6 4h20l10 10v30a2 2 0 01-2 2H6a2 2 0 01-2-2V6a2 2 0 012-2z\" fill=\"#DCFCE7\"/>\n <path d=\"M26 4v10h10\" fill=\"#BBF7D0\"/>\n <rect x=\"4\" y=\"28\" width=\"32\" height=\"12\" rx=\"2\" fill=\"#22C55E\"/>\n <text x=\"20\" y=\"38\" text-anchor=\"middle\" fill=\"white\" font-size=\"7\" font-weight=\"800\" font-family=\"sans-serif\">XLS</text>\n </svg>\n }\n @case ('image') {\n <svg viewBox=\"0 0 40 48\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M6 4h20l10 10v30a2 2 0 01-2 2H6a2 2 0 01-2-2V6a2 2 0 012-2z\" fill=\"#FEF9C3\"/>\n <path d=\"M26 4v10h10\" fill=\"#FEF08A\"/>\n <rect x=\"6\" y=\"18\" width=\"28\" height=\"18\" rx=\"2\" fill=\"#FDE047\"/>\n <circle cx=\"13\" cy=\"24\" r=\"3\" fill=\"#CA8A04\"/>\n <path d=\"M6 36l9-9 5 5 4-4 10 8H6Z\" fill=\"#EAB308\" opacity=\"0.8\"/>\n </svg>\n }\n @case ('archive') {\n <svg viewBox=\"0 0 40 48\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M6 4h20l10 10v30a2 2 0 01-2 2H6a2 2 0 01-2-2V6a2 2 0 012-2z\" fill=\"#EDE9FE\"/>\n <path d=\"M26 4v10h10\" fill=\"#DDD6FE\"/>\n <rect x=\"14\" y=\"12\" width=\"12\" height=\"4\" rx=\"1\" fill=\"#A78BFA\"/>\n <rect x=\"14\" y=\"18\" width=\"12\" height=\"4\" rx=\"1\" fill=\"#C4B5FD\"/>\n <rect x=\"14\" y=\"24\" width=\"12\" height=\"4\" rx=\"1\" fill=\"#A78BFA\"/>\n <rect x=\"14\" y=\"30\" width=\"12\" height=\"4\" rx=\"1\" fill=\"#C4B5FD\"/>\n <rect x=\"16\" y=\"26\" width=\"8\" height=\"6\" rx=\"1\" fill=\"#7C3AED\"/>\n </svg>\n }\n @default {\n <svg viewBox=\"0 0 40 48\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M6 4h20l10 10v30a2 2 0 01-2 2H6a2 2 0 01-2-2V6a2 2 0 012-2z\" fill=\"#F1F5F9\"/>\n <path d=\"M26 4v10h10\" fill=\"#E2E8F0\"/>\n <line x1=\"10\" y1=\"24\" x2=\"30\" y2=\"24\" stroke=\"#94A3B8\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <line x1=\"10\" y1=\"30\" x2=\"26\" y2=\"30\" stroke=\"#94A3B8\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <line x1=\"10\" y1=\"36\" x2=\"22\" y2=\"36\" stroke=\"#94A3B8\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n </svg>\n }\n }\n </div>\n\n <div class=\"du-file-info\">\n <span class=\"du-file-name\" [title]=\"file.name\">{{ file.name }}</span>\n <span class=\"du-file-size\">{{ getFileSize(file.size) }}</span>\n </div>\n\n <button type=\"button\" class=\"du-remove-btn\" (click)=\"removeFile(i)\" title=\"Remove\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <path d=\"M15 9L9 15M9 9l6 6\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n </svg>\n </button>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- \u2500\u2500 Upload Button \u2500\u2500 -->\n @if (showUploadButton && pendingFiles.length > 0) {\n <button type=\"button\" class=\"du-upload-btn\" (click)=\"upload()\" [disabled]=\"disabled\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" width=\"17\" height=\"17\">\n <path d=\"M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\"/>\n <polyline points=\"17,8 12,3 7,8\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" fill=\"none\"/>\n <line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\"/>\n </svg>\n {{ uploadButtonLabel }}\n <span class=\"du-upload-btn__badge\">{{ pendingFiles.length }}</span>\n </button>\n }\n\n <!-- \u2500\u2500 Saved Documents \u2500\u2500 -->\n @if (savedDocuments.length > 0) {\n <div class=\"du-section\">\n <div class=\"du-section__hdr\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" class=\"du-section__icon\">\n <path d=\"M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z\" stroke=\"#10b981\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <polyline points=\"14,2 14,8 20,8\" stroke=\"#10b981\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n </svg>\n <span class=\"du-section__title\">Saved Documents</span>\n <span class=\"du-count du-count--green\">{{ savedDocuments.length }}</span>\n </div>\n <div class=\"du-saved-list\">\n @for (doc of savedDocuments; track doc.id; let i = $index) {\n <div class=\"du-saved-card\" [style.animation-delay]=\"i * 50 + 'ms'\">\n\n <div class=\"du-saved-icon\">\n @switch (getFileType(doc.name)) {\n @case ('pdf') {\n <svg viewBox=\"0 0 32 38\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M4 3h16l8 8v24a1.5 1.5 0 01-1.5 1.5H4A1.5 1.5 0 012.5 35V4.5A1.5 1.5 0 014 3z\" fill=\"#FEE2E2\"/>\n <path d=\"M20 3v8h8\" fill=\"#FECACA\"/>\n <rect x=\"2\" y=\"22\" width=\"28\" height=\"10\" rx=\"2\" fill=\"#EF4444\"/>\n <text x=\"16\" y=\"30\" text-anchor=\"middle\" fill=\"white\" font-size=\"6\" font-weight=\"800\" font-family=\"sans-serif\">PDF</text>\n </svg>\n }\n @case ('word') {\n <svg viewBox=\"0 0 32 38\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M4 3h16l8 8v24a1.5 1.5 0 01-1.5 1.5H4A1.5 1.5 0 012.5 35V4.5A1.5 1.5 0 014 3z\" fill=\"#DBEAFE\"/>\n <path d=\"M20 3v8h8\" fill=\"#BFDBFE\"/>\n <rect x=\"2\" y=\"22\" width=\"28\" height=\"10\" rx=\"2\" fill=\"#3B82F6\"/>\n <text x=\"16\" y=\"30\" text-anchor=\"middle\" fill=\"white\" font-size=\"6\" font-weight=\"800\" font-family=\"sans-serif\">DOC</text>\n </svg>\n }\n @case ('excel') {\n <svg viewBox=\"0 0 32 38\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M4 3h16l8 8v24a1.5 1.5 0 01-1.5 1.5H4A1.5 1.5 0 012.5 35V4.5A1.5 1.5 0 014 3z\" fill=\"#DCFCE7\"/>\n <path d=\"M20 3v8h8\" fill=\"#BBF7D0\"/>\n <rect x=\"2\" y=\"22\" width=\"28\" height=\"10\" rx=\"2\" fill=\"#22C55E\"/>\n <text x=\"16\" y=\"30\" text-anchor=\"middle\" fill=\"white\" font-size=\"6\" font-weight=\"800\" font-family=\"sans-serif\">XLS</text>\n </svg>\n }\n @case ('image') {\n <svg viewBox=\"0 0 32 32\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect width=\"32\" height=\"32\" rx=\"6\" fill=\"#FEF9C3\"/>\n <rect x=\"4\" y=\"7\" width=\"24\" height=\"18\" rx=\"2\" fill=\"#FDE047\"/>\n <circle cx=\"10\" cy=\"13\" r=\"2.5\" fill=\"#CA8A04\"/>\n <path d=\"M4 25l8-8 5 5 4-4 11 7H4Z\" fill=\"#EAB308\" opacity=\"0.7\"/>\n </svg>\n }\n @case ('archive') {\n <svg viewBox=\"0 0 32 32\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect width=\"32\" height=\"32\" rx=\"6\" fill=\"#EDE9FE\"/>\n <rect x=\"4\" y=\"8\" width=\"24\" height=\"5\" rx=\"1.5\" fill=\"#A78BFA\"/>\n <rect x=\"4\" y=\"14\" width=\"24\" height=\"14\" rx=\"2\" fill=\"#C4B5FD\"/>\n <rect x=\"11\" y=\"18\" width=\"10\" height=\"6\" rx=\"1.5\" fill=\"#7C3AED\"/>\n <line x1=\"16\" y1=\"18\" x2=\"16\" y2=\"24\" stroke=\"white\" stroke-width=\"1.5\"/>\n </svg>\n }\n @default {\n <svg viewBox=\"0 0 32 38\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M4 3h16l8 8v24a1.5 1.5 0 01-1.5 1.5H4A1.5 1.5 0 012.5 35V4.5A1.5 1.5 0 014 3z\" fill=\"#F1F5F9\"/>\n <path d=\"M20 3v8h8\" fill=\"#E2E8F0\"/>\n <line x1=\"7\" y1=\"20\" x2=\"25\" y2=\"20\" stroke=\"#94A3B8\" stroke-width=\"1.8\" stroke-linecap=\"round\"/>\n <line x1=\"7\" y1=\"25\" x2=\"21\" y2=\"25\" stroke=\"#94A3B8\" stroke-width=\"1.8\" stroke-linecap=\"round\"/>\n <line x1=\"7\" y1=\"30\" x2=\"17\" y2=\"30\" stroke=\"#94A3B8\" stroke-width=\"1.8\" stroke-linecap=\"round\"/>\n </svg>\n }\n }\n </div>\n\n <div class=\"du-saved-info\">\n <span class=\"du-saved-name\" [title]=\"doc.name\">{{ doc.name }}</span>\n @if (doc.addBy || doc.addOn) {\n <span class=\"du-saved-meta\">\n @if (doc.addBy) { <span class=\"du-saved-by\">{{ doc.addBy }}</span> }\n @if (doc.addBy && doc.addOn) { <span class=\"du-saved-sep\">\u00B7</span> }\n @if (doc.addOn) { <span class=\"du-saved-on\">{{ formatDate(doc.addOn) }}</span> }\n </span>\n }\n </div>\n\n <div class=\"du-saved-actions\">\n <button type=\"button\" class=\"du-act du-act--view\" (click)=\"onView(doc)\" title=\"View\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <circle cx=\"12\" cy=\"12\" r=\"3\" stroke=\"currentColor\" stroke-width=\"2\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"du-act du-act--download\" (click)=\"onDownload(doc)\" title=\"Download\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <polyline points=\"7,10 12,15 17,10\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" fill=\"none\"/>\n <line x1=\"12\" y1=\"15\" x2=\"12\" y2=\"3\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n </svg>\n </button>\n <button type=\"button\" class=\"du-act du-act--delete\" (click)=\"onDelete(doc)\" title=\"Delete\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <polyline points=\"3,6 5,6 21,6\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <path d=\"M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <path d=\"M10 11v6M14 11v6\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <path d=\"M9 6V4a1 1 0 011-1h4a1 1 0 011 1v2\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n </svg>\n </button>\n </div>\n\n </div>\n }\n </div>\n </div>\n }\n\n</div>\n", styles: ["@keyframes du-float{0%,to{transform:translateY(0)}50%{transform:translateY(-9px)}}@keyframes du-arrow-bounce{0%,to{transform:translateY(0)}50%{transform:translateY(-6px)}}@keyframes du-particle{0%,to{transform:translate(0) scale(1);opacity:.25}33%{transform:translate(4px,-14px) scale(1.15);opacity:.5}66%{transform:translate(-4px,-7px) scale(.88);opacity:.2}}@keyframes du-slide-in{0%{opacity:0;transform:translate(18px)}to{opacity:1;transform:translate(0)}}@keyframes du-fade-up{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@keyframes du-pop-in{0%{opacity:0;transform:scale(.8)}70%{transform:scale(1.06)}to{opacity:1;transform:scale(1)}}@keyframes du-glow-pulse{0%,to{box-shadow:0 0 #667eea59}50%{box-shadow:0 0 0 8px #667eea00}}@keyframes du-btn-pulse{0%,to{box-shadow:0 4px 20px #667eea66}50%{box-shadow:0 6px 30px #764ba299}}@keyframes du-gradient-shift{0%{background-position:0% 50%}50%{background-position:100% 50%}to{background-position:0% 50%}}@keyframes du-ripple{0%{transform:scale(.6);opacity:.7}to{transform:scale(2.2);opacity:0}}@keyframes du-glint{0%,80%,to{opacity:0;transform:scale(.5)}40%{opacity:.7;transform:scale(1.4)}}.du-container{display:flex;flex-direction:column;gap:10px;width:100%}.du-container.du--disabled{opacity:.55;pointer-events:none}.du-label{display:flex;align-items:center;gap:2px;font-size:var(--osl-label-font-size, 13px);font-weight:500;color:#374151;overflow:hidden}.du-label--error{color:var(--osl-error-color, #ef4444)}.du-label__text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.du-label__req{color:var(--osl-error-color, #ef4444);flex-shrink:0;font-weight:700}.du-file-input{display:none}.du-dropzone{position:relative;width:100%;min-height:158px;border-radius:18px;border:2px dashed rgba(102,126,234,.35);background:linear-gradient(145deg,#fafbff,#f5f3ff);cursor:pointer;overflow:hidden;transition:border-color .3s ease,background .3s ease,box-shadow .3s ease,transform .25s ease}.du-dropzone:hover:not(.du-dropzone--disabled){border-color:#667eeab3;background:linear-gradient(145deg,#f5f3ff,#ede9fe);transform:translateY(-2px);box-shadow:0 10px 30px #667eea1f}.du-dropzone--over{border:2px solid #667eea!important;background:linear-gradient(145deg,#ede9fe,#ddd6fe)!important;transform:scale(1.012) translateY(-3px)!important;box-shadow:0 18px 40px #667eea38,inset 0 0 0 1px #667eea4d!important}.du-dropzone--over .du-cloud-body{stroke:#764ba2}.du-dropzone--over .du-cloud-glint{animation:du-glint .6s ease infinite}.du-dropzone--error{border-color:#ef444480;background:linear-gradient(145deg,#fff5f5,#fee2e2);animation:du-glow-pulse 1.8s ease infinite}.du-dropzone--disabled{cursor:not-allowed;background:#f9fafb!important;border-color:#e5e7eb!important;box-shadow:none!important;transform:none!important}.du-particles{position:absolute;inset:0;pointer-events:none;overflow:hidden}.du-p{position:absolute;border-radius:50%;background:radial-gradient(circle,#667eea38,#764ba21f);animation:du-particle var(--dur, 4s) ease-in-out infinite}.du-p--1{width:22px;height:22px;top:12%;left:6%;--dur: 4.2s;animation-delay:0s}.du-p--2{width:14px;height:14px;top:65%;left:88%;--dur: 5.1s;animation-delay:.9s}.du-p--3{width:18px;height:18px;top:18%;left:82%;--dur: 3.8s;animation-delay:1.6s}.du-p--4{width:10px;height:10px;top:75%;left:12%;--dur: 6.3s;animation-delay:.4s}.du-p--5{width:16px;height:16px;top:50%;left:48%;--dur: 4.7s;animation-delay:2.1s}.du-p--6{width:8px;height:8px;top:30%;left:38%;--dur: 5.5s;animation-delay:1.1s}.du-dropzone__inner{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;padding:26px 20px;min-height:158px}.du-cloud-wrap{width:80px;height:64px;animation:du-float 3.2s ease-in-out infinite;filter:drop-shadow(0 4px 10px rgba(102,126,234,.2));transition:filter .3s ease;flex-shrink:0}.du-cloud-wrap--over{animation-duration:1.2s;filter:drop-shadow(0 6px 18px rgba(102,126,234,.45))}.du-cloud-svg{width:100%;height:100%}.du-cloud-body{transition:stroke .3s ease}.du-cloud-glint{transform-origin:center}.du-arrow{animation:du-arrow-bounce 1.6s ease-in-out infinite}.du-dropzone__headline{margin:0;font-size:14px;color:#6b7280;line-height:1.5;text-align:center}.du-dropzone__headline--drop .du-gradient-text{animation:du-glow-pulse .7s ease infinite}.du-gradient-text{background:linear-gradient(135deg,#667eea,#764ba2);background-size:200% 200%;-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;font-weight:700;animation:du-gradient-shift 3s ease infinite}.du-or{color:#9ca3af;font-size:13px}.du-dropzone__meta{display:flex;flex-wrap:wrap;justify-content:center;gap:6px}.du-chip{border-radius:20px;padding:2px 10px;font-size:11px;font-weight:500;background:#667eea14;color:#7c6fcd;border:1px solid rgba(102,126,234,.18);transition:background .2s}.du-chip--info{background:#10b98114;color:#059669;border-color:#10b98133}.du-msg{display:flex;align-items:center;gap:6px;font-size:var(--osl-hint-font-size, 11px);animation:du-fade-up .2s ease}.du-msg svg{flex-shrink:0}.du-msg--error{color:var(--osl-error-color, #ef4444)}.du-section{display:flex;flex-direction:column;gap:8px;animation:du-fade-up .3s ease}.du-section__hdr{display:flex;align-items:center;gap:7px}.du-section__icon{flex-shrink:0}.du-section__title{font-size:11px;font-weight:700;color:#9ca3af;text-transform:uppercase;letter-spacing:.07em}.du-count{width:19px;height:19px;border-radius:50%;font-size:10px;font-weight:800;color:#fff;display:flex;align-items:center;justify-content:center;flex-shrink:0;animation:du-pop-in .3s ease}.du-count--purple{background:linear-gradient(135deg,#667eea,#764ba2)}.du-count--green{background:linear-gradient(135deg,#10b981,#059669)}.du-file-list{display:flex;flex-direction:column;gap:7px}.du-file-card{display:flex;align-items:center;gap:11px;padding:10px 14px 10px 16px;background:#fff;border-radius:13px;border:1px solid #e9ecf1;animation:du-slide-in .28s ease both;transition:border-color .2s ease,box-shadow .2s ease,transform .2s ease;position:relative}.du-file-card:before{content:\"\";position:absolute;left:0;top:50%;transform:translateY(-50%);width:4px;height:60%;border-radius:0 4px 4px 0;transition:height .2s ease}.du-file-card[data-ftype=pdf]:before{background:#ef4444}.du-file-card[data-ftype=word]:before{background:#3b82f6}.du-file-card[data-ftype=excel]:before{background:#22c55e}.du-file-card[data-ftype=image]:before{background:#f59e0b}.du-file-card[data-ftype=archive]:before{background:#8b5cf6}.du-file-card[data-ftype=generic]:before{background:#94a3b8}.du-file-card:hover{border-color:#c4b5fd;box-shadow:0 3px 14px #667eea1a;transform:translate(3px)}.du-file-card:hover:before{height:75%}.du-file-icon{width:38px;height:46px;flex-shrink:0}.du-file-icon svg{width:100%;height:100%}.du-file-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}.du-file-name{font-size:13px;font-weight:600;color:#1e293b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.du-file-size{font-size:11px;color:#94a3b8;font-weight:500}.du-remove-btn{width:30px;height:30px;flex-shrink:0;border-radius:50%;border:1.5px solid #fecaca;background:#fef2f2;color:#fca5a5;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:6px;transition:all .22s ease}.du-remove-btn svg{width:100%;height:100%}.du-remove-btn:hover{background:#fee2e2;border-color:#ef4444;color:#ef4444;transform:rotate(90deg) scale(1.1);box-shadow:0 3px 10px #ef444440}.du-upload-btn{display:flex;align-items:center;justify-content:center;gap:8px;padding:12px 24px;width:100%;border:none;border-radius:13px;background:linear-gradient(135deg,#667eea,#764ba2);background-size:200% 200%;color:#fff;font-size:14px;font-weight:700;letter-spacing:.03em;cursor:pointer;transition:transform .25s ease,box-shadow .25s ease;animation:du-btn-pulse 2.5s ease-in-out infinite,du-gradient-shift 4s ease infinite}.du-upload-btn svg{flex-shrink:0;transition:transform .25s ease}.du-upload-btn:hover:not([disabled]){transform:translateY(-3px);box-shadow:0 10px 28px #667eea80;animation:du-gradient-shift 1.5s ease infinite}.du-upload-btn:hover:not([disabled]) svg{transform:translateY(-3px)}.du-upload-btn:active:not([disabled]){transform:translateY(-1px)}.du-upload-btn[disabled]{opacity:.45;cursor:not-allowed;animation:none}.du-upload-btn__badge{min-width:22px;height:22px;padding:0 6px;border-radius:11px;background:#ffffff38;font-size:11px;font-weight:800;display:flex;align-items:center;justify-content:center;animation:du-pop-in .3s ease}.du-saved-list{display:flex;flex-direction:column;gap:7px}.du-saved-card{display:flex;align-items:center;gap:11px;padding:10px 12px;background:linear-gradient(135deg,#fff,#f8faff);border-radius:13px;border:1px solid #e9ecf1;opacity:0;animation:du-fade-up .32s ease forwards;transition:border-color .2s ease,box-shadow .2s ease,transform .2s ease}.du-saved-card:hover{border-color:#a5b4fc;box-shadow:0 4px 18px #667eea1a;transform:translateY(-1px)}.du-saved-icon{width:32px;height:38px;flex-shrink:0}.du-saved-icon svg{width:100%;height:100%}.du-saved-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}.du-saved-name{font-size:13px;font-weight:600;color:#1e293b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.du-saved-size{font-size:11px;color:#94a3b8;font-weight:500}.du-saved-actions{display:flex;align-items:center;gap:5px;flex-shrink:0}.du-act{width:32px;height:32px;border-radius:9px;border:1.5px solid transparent;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:7px;transition:all .22s ease;position:relative;overflow:hidden}.du-act svg{width:100%;height:100%;transition:transform .22s ease}.du-act:after{content:\"\";position:absolute;inset:50%;border-radius:50%;transition:all 0s;pointer-events:none}.du-act:active:after{inset:0;border-radius:9px;background:#ffffff59;animation:du-ripple .4s ease}.du-act--view{background:#eff6ff;color:#3b82f6;border-color:#bfdbfe}.du-act--view:hover{background:#dbeafe;border-color:#93c5fd;box-shadow:0 3px 12px #3b82f64d}.du-act--view:hover svg{transform:scale(1.12)}.du-act--download{background:#f0fdf4;color:#22c55e;border-color:#bbf7d0}.du-act--download:hover{background:#dcfce7;border-color:#86efac;box-shadow:0 3px 12px #22c55e4d}.du-act--download:hover svg{transform:translateY(2px) scale(1.08)}.du-act--delete{background:#fef2f2;color:#ef4444;border-color:#fecaca}.du-act--delete:hover{background:#fee2e2;border-color:#fca5a5;box-shadow:0 3px 12px #ef44444d}.du-act--delete:hover svg{transform:scale(1.1) rotate(-5deg)}.du-dropzone--maxed{cursor:default;border-color:#22c55e66!important;background:linear-gradient(145deg,#f0fdf4,#dcfce7)!important;pointer-events:none}.du-dropzone--maxed .du-particles .du-p{background:radial-gradient(circle,#22c55e2e,#10b9811a)}.du-maxed-icon{width:56px;height:56px;animation:du-pop-in .4s cubic-bezier(.34,1.56,.64,1)}.du-maxed-icon svg{width:100%;height:100%}.du-gradient-text--green{background:linear-gradient(135deg,#22c55e,#059669)!important;background-size:200% 200%!important;-webkit-background-clip:text!important;-webkit-text-fill-color:transparent!important;background-clip:text!important}.du-maxed-sub{margin:0;font-size:12px;color:#6b7280;font-weight:500}.du-count-label{font-size:11px;font-weight:700;padding:2px 9px;border-radius:20px;background:#667eea1a;color:#667eea;border:1px solid rgba(102,126,234,.2);transition:all .25s ease;animation:du-pop-in .3s ease}.du-count-label--full{background:#22c55e1a;color:#16a34a;border-color:#22c55e40}.du-saved-meta{display:flex;align-items:center;gap:4px;flex-wrap:wrap}.du-saved-by{font-size:11px;font-weight:600;color:#667eea}.du-saved-sep{font-size:11px;color:#d1d5db}.du-saved-on{font-size:11px;color:#94a3b8}\n"] }]
5535
+ }], propDecorators: { label: [{
5536
+ type: Input,
5537
+ args: ['label']
5538
+ }], required: [{
5539
+ type: Input,
5540
+ args: ['required']
5541
+ }], disabled: [{
5542
+ type: Input,
5543
+ args: ['disabled']
5544
+ }], multiple: [{
5545
+ type: Input,
5546
+ args: ['multiple']
5547
+ }], accept: [{
5548
+ type: Input,
5549
+ args: ['accept']
5550
+ }], maxSize: [{
5551
+ type: Input,
5552
+ args: ['maxSize']
5553
+ }], minFiles: [{
5554
+ type: Input,
5555
+ args: ['minFiles']
5556
+ }], maxFiles: [{
5557
+ type: Input,
5558
+ args: ['maxFiles']
5559
+ }], showUploadButton: [{
5560
+ type: Input,
5561
+ args: ['showUploadButton']
5562
+ }], uploadButtonLabel: [{
5563
+ type: Input,
5564
+ args: ['uploadButtonLabel']
5565
+ }], savedDocuments: [{
5566
+ type: Input,
5567
+ args: ['savedDocuments']
5568
+ }], skeletonLoading: [{
5569
+ type: Input,
5570
+ args: ['skeletonLoading']
5571
+ }], skeletonTheme: [{
5572
+ type: Input,
5573
+ args: ['skeletonTheme']
5574
+ }], uploadCallback: [{
5575
+ type: Output
5576
+ }], viewCallback: [{
5577
+ type: Output
5578
+ }], deleteCallback: [{
5579
+ type: Output
5580
+ }], downloadCallback: [{
5581
+ type: Output
5582
+ }], filesChanged: [{
5583
+ type: Output
5584
+ }] } });
5585
+
5374
5586
  class FormStructureModule {
5375
5587
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FormStructureModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
5376
5588
  static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: FormStructureModule, declarations: [DynamicForm,
@@ -5394,7 +5606,8 @@ class FormStructureModule {
5394
5606
  OslReportForm,
5395
5607
  OslUserLog,
5396
5608
  OslMenu,
5397
- OslMenuTriggerFor], imports: [NgTemplateOutlet,
5609
+ OslMenuTriggerFor,
5610
+ OslDocumentUploader], imports: [NgTemplateOutlet,
5398
5611
  NgStyle,
5399
5612
  NgClass,
5400
5613
  DatePipe,
@@ -5430,7 +5643,7 @@ class FormStructureModule {
5430
5643
  OslButton,
5431
5644
  OslSetup,
5432
5645
  OslSearchbar, OslAutocompleteLister, OslReportGrid, OslReportForm,
5433
- OslMenu, OslMenuTriggerFor] });
5646
+ OslMenu, OslMenuTriggerFor, OslDocumentUploader] });
5434
5647
  static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FormStructureModule, providers: [
5435
5648
  { provide: AUTOCOMPLETE_LISTER_COMPONENT, useValue: OslAutocompleteLister },
5436
5649
  ], imports: [FormsModule,
@@ -5476,6 +5689,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
5476
5689
  OslUserLog,
5477
5690
  OslMenu,
5478
5691
  OslMenuTriggerFor,
5692
+ OslDocumentUploader,
5479
5693
  ],
5480
5694
  imports: [
5481
5695
  NgTemplateOutlet,
@@ -5516,7 +5730,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
5516
5730
  OslButton,
5517
5731
  OslSetup,
5518
5732
  OslSearchbar, OslAutocompleteLister, OslReportGrid, OslReportForm,
5519
- OslMenu, OslMenuTriggerFor],
5733
+ OslMenu, OslMenuTriggerFor, OslDocumentUploader],
5520
5734
  providers: [
5521
5735
  { provide: AUTOCOMPLETE_LISTER_COMPONENT, useValue: OslAutocompleteLister },
5522
5736
  ],
@@ -6781,5 +6995,5 @@ var validation_util = /*#__PURE__*/Object.freeze({
6781
6995
  * Generated bundle index. Do not edit.
6782
6996
  */
6783
6997
 
6784
- export { array_util as ArrayUtil, date_util as DateUtil, DeleteConfirmation, DeleteConfirmationData, Dialog, DialogWrapper, DynamicForm, FormStructureModule, Httpbase, number_util as NumberUtil, object_util as ObjectUtil, OslAutocomplete, OslAutocompleteLister, OslBaseExtended, OslButton, OslCheckbox, OslDatepicker, OslDatetimepicker, OslFileUpload, OslFormGrid, OslGrid, OslMenu, OslMenuTriggerFor, OslRadio, OslReportForm, OslReportGrid, OslSearchbar, OslSelect, OslSetup, OslSkeletonDirective, OslSkeletonModule, OslSkeletonThemeService, OslSlideToggle, OslUserLog, Oslinput, Osltextarea, storage_util as StorageUtil, string_util as StringUtil, validation_util as ValidationUtil, baseComponent };
6998
+ export { array_util as ArrayUtil, date_util as DateUtil, DeleteConfirmation, DeleteConfirmationData, Dialog, DialogWrapper, DynamicForm, FormStructureModule, Httpbase, number_util as NumberUtil, object_util as ObjectUtil, OslAutocomplete, OslAutocompleteLister, OslBaseExtended, OslButton, OslCheckbox, OslDatepicker, OslDatetimepicker, OslDocumentUploader, OslFileUpload, OslFormGrid, OslGrid, OslMenu, OslMenuTriggerFor, OslRadio, OslReportForm, OslReportGrid, OslSearchbar, OslSelect, OslSetup, OslSkeletonDirective, OslSkeletonModule, OslSkeletonThemeService, OslSlideToggle, OslUserLog, Oslinput, Osltextarea, storage_util as StorageUtil, string_util as StringUtil, validation_util as ValidationUtil, baseComponent };
6785
6999
  //# sourceMappingURL=osl-base-extended.mjs.map