cloud-ide-element 1.0.98 → 1.0.99

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.
@@ -2970,9 +2970,11 @@ class CideEleFileManagerService {
2970
2970
  */
2971
2971
  getAllFilesForGroup(groupId) {
2972
2972
  const files = [];
2973
+ const fileIds = new Set(); // Track file IDs to prevent duplicates
2973
2974
  // Add active uploads for this group
2974
2975
  this._activeUploads().forEach((upload, fileId) => {
2975
- if (upload.groupId === groupId) {
2976
+ if (upload.groupId === groupId && !fileIds.has(fileId)) {
2977
+ fileIds.add(fileId);
2976
2978
  files.push({
2977
2979
  fileId,
2978
2980
  fileName: this.getFileNameFromId(fileId),
@@ -2981,14 +2983,25 @@ class CideEleFileManagerService {
2981
2983
  });
2982
2984
  }
2983
2985
  });
2984
- // Add fetched files for this group
2986
+ // Add fetched files for this group (only if not already in active uploads)
2985
2987
  const fetchedFiles = this.getFetchedFilesByGroupId(groupId);
2986
2988
  fetchedFiles.forEach(file => {
2987
- files.push({
2988
- fileId: file.cyfm_id || file.id,
2989
- fileName: file.cyfm_name || file.name,
2990
- stage: 'complete' // Fetched files are already completed
2991
- });
2989
+ const fetchedFileId = file.cyfm_id || file.id;
2990
+ if (!fileIds.has(fetchedFileId)) {
2991
+ fileIds.add(fetchedFileId);
2992
+ files.push({
2993
+ fileId: fetchedFileId,
2994
+ fileName: file.cyfm_name || file.name,
2995
+ stage: 'complete' // Fetched files are already completed
2996
+ });
2997
+ }
2998
+ });
2999
+ console.log('📊 [FileManagerService] getAllFilesForGroup result:', {
3000
+ groupId,
3001
+ totalFiles: files.length,
3002
+ activeUploads: Array.from(this._activeUploads().entries()).filter(([_, upload]) => upload.groupId === groupId).length,
3003
+ fetchedFiles: fetchedFiles.length,
3004
+ uniqueFileIds: Array.from(fileIds)
2992
3005
  });
2993
3006
  return files;
2994
3007
  }
@@ -3672,40 +3685,39 @@ class CideEleFloatingFileUploaderComponent {
3672
3685
  }
3673
3686
  /**
3674
3687
  * Manually show the floating uploader
3675
- * This can be called from file input components or other triggers
3688
+ * This should always be called with a group ID from the file input component
3676
3689
  */
3677
3690
  showUploader(groupId) {
3678
- console.log('👁️ [FloatingFileUploader] Manually showing uploader', groupId ? `for group: ${groupId}` : '');
3679
- if (groupId) {
3680
- this.currentGroupId.set(groupId);
3681
- // Use service to fetch and store files
3682
- this.fileManagerService.fetchAndStoreFilesByGroupId(groupId)
3683
- .pipe(takeUntilDestroyed(this.destroyRef))
3684
- .subscribe({
3685
- next: (files) => {
3686
- console.log('✅ [FloatingFileUploader] Files fetched and stored:', files.length);
3687
- // Force show the uploader after files are loaded
3688
- this.showWithAnimation();
3689
- // Debug: Check what files are available now
3690
- setTimeout(() => {
3691
- const allFiles = this.getAllFiles();
3692
- console.log('🔍 [FloatingFileUploader] Files available after fetch:', {
3693
- allFilesCount: allFiles.length,
3694
- files: allFiles.map(f => ({ id: f.fileId, name: f.fileName, stage: f.stage }))
3695
- });
3696
- }, 100);
3697
- },
3698
- error: (error) => {
3699
- console.error('❌ [FloatingFileUploader] Failed to fetch files:', error);
3700
- // Still show the uploader even if fetch fails
3701
- this.showWithAnimation();
3702
- }
3703
- });
3704
- }
3705
- else {
3706
- // Show immediately if no group ID
3707
- this.showWithAnimation();
3691
+ console.log('👁️ [FloatingFileUploader] Manually showing uploader', groupId ? `for group: ${groupId}` : 'without group ID');
3692
+ if (!groupId) {
3693
+ console.error('❌ [FloatingFileUploader] No group ID provided. Floating uploader should always be opened with a group ID from the file input component.');
3694
+ return;
3708
3695
  }
3696
+ this.currentGroupId.set(groupId);
3697
+ console.log('🆔 [FloatingFileUploader] Set group ID:', groupId);
3698
+ // Use service to fetch and store files for this group
3699
+ this.fileManagerService.fetchAndStoreFilesByGroupId(groupId)
3700
+ .pipe(takeUntilDestroyed(this.destroyRef))
3701
+ .subscribe({
3702
+ next: (files) => {
3703
+ console.log('✅ [FloatingFileUploader] Files fetched and stored:', files.length);
3704
+ // Force show the uploader after files are loaded
3705
+ this.showWithAnimation();
3706
+ // Debug: Check what files are available now
3707
+ setTimeout(() => {
3708
+ const allFiles = this.getAllFiles();
3709
+ console.log('🔍 [FloatingFileUploader] Files available after fetch:', {
3710
+ allFilesCount: allFiles.length,
3711
+ files: allFiles.map(f => ({ id: f.fileId, name: f.fileName, stage: f.stage }))
3712
+ });
3713
+ }, 100);
3714
+ },
3715
+ error: (error) => {
3716
+ console.error('❌ [FloatingFileUploader] Failed to fetch files:', error);
3717
+ // Still show the uploader even if fetch fails
3718
+ this.showWithAnimation();
3719
+ }
3720
+ });
3709
3721
  }
3710
3722
  /**
3711
3723
  * Check if there are any uploads for the current group
@@ -3772,10 +3784,16 @@ class CideEleFloatingFileUploaderComponent {
3772
3784
  */
3773
3785
  handleFileSelection(files) {
3774
3786
  console.log('📁 [FloatingFileUploader] Files selected:', files.map(f => f.name));
3775
- const groupId = this.currentGroupId() || this.generateGroupId();
3776
- this.currentGroupId.set(groupId);
3787
+ const groupId = this.currentGroupId();
3788
+ // Group ID must be provided by the file input component
3789
+ if (!groupId) {
3790
+ console.error('❌ [FloatingFileUploader] No group ID available. Files cannot be uploaded without a group ID from the file input component.');
3791
+ return;
3792
+ }
3793
+ console.log('🆔 [FloatingFileUploader] Using group ID from file input:', groupId);
3777
3794
  // Upload files using the file manager service
3778
- files.forEach(file => {
3795
+ files.forEach((file, index) => {
3796
+ console.log(`📤 [FloatingFileUploader] Uploading file ${index + 1}/${files.length}: ${file.name} to group: ${groupId}`);
3779
3797
  this.fileManagerService.uploadFile(file, {
3780
3798
  groupId: groupId,
3781
3799
  isMultiple: true,
@@ -3783,12 +3801,6 @@ class CideEleFloatingFileUploaderComponent {
3783
3801
  });
3784
3802
  });
3785
3803
  }
3786
- /**
3787
- * Generate a unique group ID
3788
- */
3789
- generateGroupId() {
3790
- return `group_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
3791
- }
3792
3804
  /**
3793
3805
  * Update cached dimensions (throttled for performance)
3794
3806
  */
@@ -4658,7 +4670,7 @@ class CideEleFileInputComponent {
4658
4670
  // Fallback to direct component access if available
4659
4671
  if (this.floatingUploader) {
4660
4672
  console.log('👁️ [FileInput] Using direct component access as fallback');
4661
- this.floatingUploader.showUploader(this.floatingUploaderGroupIdSignal());
4673
+ this.floatingUploader.showUploader(groupId || undefined);
4662
4674
  }
4663
4675
  }
4664
4676
  /**
@@ -4706,6 +4718,35 @@ class CideEleFileInputComponent {
4706
4718
  showFloatingUploaderDialog() {
4707
4719
  this.showUploader();
4708
4720
  }
4721
+ /**
4722
+ * Get dynamic classes for drag and drop zone
4723
+ */
4724
+ getDragDropZoneClasses() {
4725
+ const classes = [];
4726
+ if (this.isDragOver()) {
4727
+ classes.push('!border-blue-500', '!bg-blue-100', 'dark:!bg-blue-900/30', 'scale-[1.01]');
4728
+ }
4729
+ if (this.disabledSignal()) {
4730
+ classes.push('opacity-50', 'cursor-not-allowed', '!hover:border-gray-300', '!hover:bg-gray-50', 'dark:!hover:bg-gray-800');
4731
+ }
4732
+ if (this.hasFiles()) {
4733
+ classes.push('!border-emerald-500', '!bg-emerald-50', 'dark:!bg-emerald-900/20', 'hover:!border-emerald-600', 'hover:!bg-emerald-100', 'dark:hover:!bg-emerald-900/30');
4734
+ }
4735
+ return classes.join(' ');
4736
+ }
4737
+ /**
4738
+ * Get dynamic classes for icon
4739
+ */
4740
+ getIconClasses() {
4741
+ const classes = ['text-gray-500', 'dark:text-gray-400'];
4742
+ if (this.isDragOver()) {
4743
+ classes.push('!text-blue-500', 'dark:!text-blue-400');
4744
+ }
4745
+ else if (this.hasFiles()) {
4746
+ classes.push('!text-emerald-500', 'dark:!text-emerald-400');
4747
+ }
4748
+ return classes.join(' ');
4749
+ }
4709
4750
  // Drag and Drop Event Handlers
4710
4751
  onDragOver(event) {
4711
4752
  event.preventDefault();
@@ -4902,7 +4943,7 @@ class CideEleFileInputComponent {
4902
4943
  useExisting: CideEleFileInputComponent,
4903
4944
  multi: true
4904
4945
  }
4905
- ], usesOnChanges: true, ngImport: i0, template: "<div class=\"cide-file-input\">\n <!-- Label (shown when not in preview box mode or when preview box mode but no label override) -->\n @if (labelSignal() && !isPreviewBoxMode()) {\n <label class=\"cide-file-input-label\" [attr.for]=\"'cide-file-input-' + id()\">\n {{ labelSignal() }}@if (requiredSignal()) {<span class=\"cide-file-input-required\"> *</span>}\n </label>\n }\n \n <!-- Preview Box Mode -->\n @if (isPreviewBoxMode()) {\n <div class=\"cide-file-input-preview-box-container\">\n <!-- Hidden file input -->\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"cide-file-input-hidden\"\n />\n \n <!-- Preview Box -->\n <div \n class=\"cide-file-input-preview-box\"\n [class.cide-file-input-preview-box-disabled]=\"disabledSignal()\"\n [class.cide-file-input-preview-box-has-image]=\"hasImages()\"\n [class.cide-file-input-preview-box-drag-over]=\"isDragOver()\"\n [style.width]=\"previewWidthSignal()\"\n [style.height]=\"previewHeightSignal()\"\n (click)=\"triggerFileSelect()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n [attr.title]=\"disabledSignal() ? 'File selection disabled' : placeholderTextSignal()\">\n \n <!-- No Image State -->\n @if (!hasImages()) {\n <div class=\"cide-file-input-preview-box-placeholder\">\n <div class=\"cide-file-input-preview-box-icon\">\n <cide-ele-icon>{{ isDragOver() ? '\uD83D\uDCC1' : placeholderIconSignal() }}</cide-ele-icon>\n </div>\n <div class=\"cide-file-input-preview-box-text\">\n {{ isDragOver() ? 'Drop files here...' : placeholderTextSignal() }}\n </div>\n </div>\n }\n \n <!-- Image Preview State -->\n @if (hasImages()) {\n <div class=\"cide-file-input-preview-box-content\">\n <img \n [src]=\"previewUrls()[0]\" \n [alt]=\"fileNames()[0] || 'Preview image'\"\n class=\"cide-file-input-preview-box-image\">\n <div class=\"cide-file-input-preview-box-overlay\">\n <div class=\"cide-file-input-preview-box-overlay-text\">Click to change</div>\n </div>\n @if (!disabledSignal()) {\n <button \n type=\"button\" \n class=\"cide-file-input-preview-box-remove\"\n (click)=\"clearFiles(); $event.stopPropagation()\"\n title=\"Remove image\">\n \u00D7\n </button>\n }\n </div>\n }\n </div>\n \n <!-- File name display for preview box mode -->\n @if (hasImages() && fileNames().length && showFileNameSignal()) {\n <div class=\"cide-file-input-preview-box-filename\">\n {{ fileNames()[0] }}\n </div>\n }\n </div>\n }\n\n <!-- Standard Mode -->\n @if (!isPreviewBoxMode()) {\n <!-- Hidden file input -->\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"cide-file-input-hidden\"\n />\n \n <!-- Modern Drag and Drop Zone -->\n <div \n class=\"cide-file-input-drop-zone\"\n [class.cide-file-input-drag-over]=\"isDragOver()\"\n [class.cide-file-input-disabled]=\"disabledSignal()\"\n [class.cide-file-input-has-files]=\"hasFiles()\"\n (click)=\"triggerFileSelect()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\n \n <div class=\"cide-file-input-drop-content\">\n <!-- Icon and Text -->\n <div class=\"cide-file-input-drop-main\">\n <cide-ele-icon class=\"cide-file-input-drop-icon\" size=\"sm\">\n {{ isDragOver() ? 'file_download' : (hasFiles() ? 'check_circle' : 'cloud_upload') }}\n </cide-ele-icon>\n \n <div class=\"cide-file-input-drop-text\">\n @if (isDragOver()) {\n <span class=\"cide-file-input-drop-title\">Drop files here</span>\n } @else if (hasFiles()) {\n <span class=\"cide-file-input-drop-title\">\n @if (multipleSignal() && fileNames().length > 1) {\n {{ fileNames().length }} files selected\n } @else {\n {{ fileNames()[0] }}\n }\n </span>\n @if (totalFileSize() > 0) {\n <span class=\"cide-file-input-drop-subtitle\">{{ fileSizeInMB() }} MB</span>\n }\n } @else {\n <span class=\"cide-file-input-drop-title\">\n {{ multipleSignal() ? 'Choose files or drag here' : 'Choose file or drag here' }}\n </span>\n }\n </div>\n </div>\n \n <!-- Action Buttons -->\n <div class=\"cide-file-input-drop-actions\">\n @if (hasFiles()) {\n <button type=\"button\" \n class=\"cide-file-input-action-btn cide-file-input-clear-btn\" \n (click)=\"clearFiles(); $event.stopPropagation()\"\n title=\"Clear files\">\n <cide-ele-icon size=\"xs\">close</cide-ele-icon>\n </button>\n }\n </div>\n </div>\n </div>\n }\n \n <!-- Image Preview Section (only for standard mode) -->\n @if (isImagePreviewAvailable() && !isPreviewBoxMode()) {\n <div class=\"cide-file-input-preview\">\n <div class=\"cide-file-input-preview-label\">Preview:</div>\n <div class=\"cide-file-input-preview-container\">\n @for (previewUrl of previewUrls(); track previewUrl; let i = $index) {\n <div \n class=\"cide-file-input-preview-item\"\n [style.width]=\"previewWidthSignal()\"\n [style.height]=\"previewHeightSignal()\">\n <button \n type=\"button\" \n class=\"cide-file-input-preview-remove\"\n (click)=\"removePreview(i)\"\n title=\"Remove image\">\n \u00D7\n </button>\n <img \n [src]=\"previewUrl\" \n [alt]=\"fileNames()[i] || 'Preview image'\"\n class=\"cide-file-input-preview-image\"\n loading=\"lazy\">\n <div class=\"cide-file-input-preview-filename\">{{ fileNames()[i] }}</div>\n </div>\n }\n </div>\n </div>\n }\n \n <!-- Upload Status and Show Files Button (only for multiple file inputs) -->\n @if (multiple && showFloatingUploaderSignal() && (getUploadCount() > 0 || hasActiveUploads() || hasEverUploaded())) {\n <div class=\"cide-file-input-upload-status\">\n <div class=\"cide-file-input-upload-count\">\n <cide-ele-icon size=\"sm\">cloud_upload</cide-ele-icon>\n <span class=\"upload-count-text\">\n @if (hasActiveUploads()) {\n {{ getActiveUploadCount() }} uploading\n } @else if (getUploadCount() > 0) {\n {{ getUploadCount() }} completed\n } @else if (hasEverUploaded()) {\n View uploads\n }\n </span>\n </div>\n <button \n type=\"button\" \n class=\"cide-file-input-show-files-icon\"\n (click)=\"showFloatingUploaderDialog()\"\n title=\"View upload progress and history\">\n <cide-ele-icon size=\"sm\">visibility</cide-ele-icon>\n </button>\n </div>\n }\n \n @if (errorTextSignal()) {\n <div class=\"cide-file-input-error\">{{ errorTextSignal() }}</div>\n }\n @if (helperTextSignal() && !errorTextSignal()) {\n <div class=\"cide-file-input-helper\">{{ helperTextSignal() }}</div>\n }\n</div> ", styles: [".cide-file-input{display:flex;flex-direction:column;gap:.5rem}.cide-file-input-drop-zone{border:2px dashed #d1d5db;border-radius:8px;background:#f9fafb;cursor:pointer;transition:all .2s ease;min-height:60px}.cide-file-input-drop-zone:hover{border-color:#3b82f6;background:#f0f9ff}.cide-file-input-drop-zone.cide-file-input-drag-over{border-color:#3b82f6;background:#dbeafe;transform:scale(1.01)}.cide-file-input-drop-zone.cide-file-input-disabled{opacity:.5;cursor:not-allowed}.cide-file-input-drop-zone.cide-file-input-disabled:hover{border-color:#d1d5db;background:#f9fafb;transform:none}.cide-file-input-drop-zone.cide-file-input-has-files{border-color:#10b981;background:#f0fdf4}.cide-file-input-drop-zone.cide-file-input-has-files:hover{border-color:#059669;background:#ecfdf5}.cide-file-input-drop-content{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;gap:12px}.cide-file-input-drop-main{display:flex;align-items:center;gap:10px;flex:1;min-width:0}.cide-file-input-drop-icon{color:#6b7280;transition:color .2s ease;flex-shrink:0}.cide-file-input-drag-over .cide-file-input-drop-icon{color:#3b82f6}.cide-file-input-has-files .cide-file-input-drop-icon{color:#10b981}.cide-file-input-drop-text{display:flex;flex-direction:column;gap:2px;min-width:0}.cide-file-input-drop-title{font-size:14px;font-weight:500;color:#374151;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.cide-file-input-drag-over .cide-file-input-drop-title{color:#1d4ed8}.cide-file-input-has-files .cide-file-input-drop-title{color:#065f46}.cide-file-input-drop-subtitle{font-size:12px;color:#6b7280}.cide-file-input-has-files .cide-file-input-drop-subtitle{color:#047857}.cide-file-input-drop-actions{display:flex;gap:4px;flex-shrink:0}.cide-file-input-action-btn{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border:none;border-radius:4px;background:transparent;cursor:pointer;transition:all .2s ease}.cide-file-input-action-btn:hover{background:#0000000d}.cide-file-input-action-btn.cide-file-input-clear-btn{color:#dc2626}.cide-file-input-action-btn.cide-file-input-clear-btn:hover{background:#fef2f2;color:#b91c1c}.cide-file-input-label{display:block;font-size:.875rem;font-weight:500;color:#374151;margin-bottom:.375rem;line-height:1.25}.cide-file-input-required{color:#d32f2f;font-weight:700}.cide-file-input-wrapper{display:flex;align-items:center;gap:.5rem;border:2px dashed transparent;border-radius:.5rem;padding:.5rem;transition:all .2s ease-in-out}.cide-file-input-wrapper.cide-file-input-drag-over{border-color:#3b82f6;background-color:#eff6ff;transform:scale(1.02)}.cide-file-input-element{flex:1}.cide-file-input-clear{background:none;border:none;color:#d32f2f;cursor:pointer;font-size:.9rem}.cide-file-input-files{font-size:.95rem;color:#333;margin-top:.25rem}.cide-file-input-multiple-count{display:inline-flex;align-items:center;gap:.5rem;padding:.5rem 1rem;background-color:#f0f9ff;border:1px solid #0ea5e9;border-radius:.5rem;color:#0369a1;font-weight:500;font-size:.9rem}.cide-file-input-size{margin-top:.5rem;padding:.25rem .5rem;background-color:#f3f4f6;border-radius:.25rem;font-size:.75rem;color:#4b5563;font-weight:500}.cide-file-input-error{color:#d32f2f;font-size:.9rem}.cide-file-input-helper{color:#666;font-size:.9rem}.cide-file-input-preview{margin-top:.75rem;padding:.75rem;background-color:#f8f9fa;border:1px solid #e1e5e9;border-radius:.375rem}.cide-file-input-preview-label{font-weight:500;margin-bottom:.5rem;color:#374151;font-size:.875rem}.cide-file-input-preview-container{display:flex;flex-wrap:wrap;gap:.75rem}.cide-file-input-preview-item{position:relative;display:flex;flex-direction:column;border:1px solid #d1d5db;border-radius:.5rem;overflow:hidden;background-color:#fff;box-shadow:0 1px 3px #0000001a;transition:box-shadow .2s ease-in-out}.cide-file-input-preview-item:hover{box-shadow:0 4px 6px -1px #0000001a}.cide-file-input-preview-image{width:100%;height:calc(100% - 2rem);object-fit:cover;object-position:center;background-color:#f3f4f6}.cide-file-input-preview-filename{padding:.375rem .5rem;background-color:#f9fafbf2;border-top:1px solid #e5e7eb;font-size:.75rem;color:#374151;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-height:2rem;display:flex;align-items:center;justify-content:center}.cide-file-input-preview-remove{position:absolute;top:.25rem;right:.25rem;width:1.5rem;height:1.5rem;background-color:#ef4444e6;color:#fff;border:none;border-radius:50%;font-size:1rem;font-weight:700;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;z-index:10;transition:all .2s ease-in-out}.cide-file-input-preview-remove:hover{background-color:#dc2626f2;transform:scale(1.1)}.cide-file-input-preview-remove:focus{outline:2px solid #3b82f6;outline-offset:2px}.cide-file-input-hidden{display:none}.cide-file-input-preview-box-container{display:flex;flex-direction:column;gap:.5rem}.cide-file-input-preview-box{position:relative;border:2px dashed #d1d5db;border-radius:.5rem;cursor:pointer;background-color:#f9fafb;display:flex;align-items:center;justify-content:center;overflow:hidden;transition:all .2s ease-in-out}.cide-file-input-preview-box:hover{border-color:#3b82f6;background-color:#eff6ff}.cide-file-input-preview-box.cide-file-input-preview-box-disabled{cursor:not-allowed;opacity:.6;background-color:#f3f4f6}.cide-file-input-preview-box.cide-file-input-preview-box-disabled:hover{border-color:#d1d5db;background-color:#f3f4f6}.cide-file-input-preview-box.cide-file-input-preview-box-has-image{border-style:solid;border-color:#e5e7eb;padding:0}.cide-file-input-preview-box.cide-file-input-preview-box-has-image:hover{border-color:#3b82f6}.cide-file-input-preview-box.cide-file-input-preview-box-has-image:hover .cide-file-input-preview-box-overlay{opacity:1}.cide-file-input-preview-box.cide-file-input-preview-box-drag-over{border-color:#3b82f6!important;background-color:#eff6ff!important;transform:scale(1.02);box-shadow:0 0 0 4px #3b82f61a}.cide-file-input-preview-box.cide-file-input-preview-box-drag-over .cide-file-input-preview-box-placeholder .cide-file-input-preview-box-icon{color:#3b82f6;transform:scale(1.1)}.cide-file-input-preview-box.cide-file-input-preview-box-drag-over .cide-file-input-preview-box-placeholder .cide-file-input-preview-box-text{color:#3b82f6;font-weight:600}.cide-file-input-preview-box-placeholder{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;padding:1rem;text-align:center}.cide-file-input-preview-box-icon{font-size:2rem;color:#6b7280}.cide-file-input-preview-box-text{font-size:.875rem;color:#6b7280;font-weight:500}.cide-file-input-preview-box-content{position:relative;width:100%;height:100%;display:flex;align-items:center;justify-content:center}.cide-file-input-preview-box-image{width:100%;height:100%;object-fit:cover;object-position:center}.cide-file-input-preview-box-overlay{position:absolute;inset:0;background-color:#0009;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .2s ease-in-out}.cide-file-input-preview-box-overlay-text{color:#fff;font-size:.875rem;font-weight:500;text-align:center}.cide-file-input-preview-box-remove{position:absolute;top:.375rem;right:.375rem;width:1.5rem;height:1.5rem;background-color:#ef4444e6;color:#fff;border:none;border-radius:50%;font-size:1rem;font-weight:700;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;z-index:20;transition:all .2s ease-in-out}.cide-file-input-preview-box-remove:hover{background-color:#dc2626f2;transform:scale(1.1)}.cide-file-input-preview-box-remove:focus{outline:2px solid #3b82f6;outline-offset:2px}.cide-file-input-preview-box-filename{font-size:.75rem;color:#374151;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding:.25rem .5rem;background-color:#f3f4f6;border-radius:.25rem;margin-top:.25rem}@media (max-width: 640px){.cide-file-input-preview-container{justify-content:center}.cide-file-input-preview-item{min-width:120px;max-width:150px}.cide-file-input-preview-box-icon{font-size:1.5rem}.cide-file-input-preview-box-text{font-size:.75rem}}.cide-file-input-upload-status{display:flex;align-items:center;justify-content:space-between;padding:.25rem 0;margin-top:.375rem;gap:.5rem}.cide-file-input-upload-status .cide-file-input-upload-count{display:flex;align-items:center;gap:.5rem;color:#64748b;font-size:.875rem;font-weight:500}.cide-file-input-upload-status .cide-file-input-upload-count .upload-count-text{color:#1e293b}.cide-file-input-upload-status .cide-file-input-show-files-icon{display:flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;background:transparent;color:#6b7280;border:none;border-radius:.25rem;cursor:pointer;transition:all .2s ease}.cide-file-input-upload-status .cide-file-input-show-files-icon:hover{background:#f3f4f6;color:#3b82f6}.cide-file-input-upload-status .cide-file-input-show-files-icon:active{background:#e5e7eb}@media (prefers-color-scheme: dark){.cide-file-input-label{color:#f9fafb}.cide-file-input-required{color:#f87171}.cide-file-input-drop-zone{border-color:#475569;background:#334155}.cide-file-input-drop-zone:hover{border-color:#3b82f6;background:#1e3a8a}.cide-file-input-drop-zone.cide-file-input-drag-over{border-color:#60a5fa;background:#1e40af}.cide-file-input-drop-zone.cide-file-input-has-files{border-color:#10b981;background:#064e3b}.cide-file-input-drop-zone.cide-file-input-has-files:hover{border-color:#059669;background:#065f46}.cide-file-input-drop-icon{color:#94a3b8}.cide-file-input-drag-over .cide-file-input-drop-icon{color:#60a5fa}.cide-file-input-has-files .cide-file-input-drop-icon{color:#34d399}.cide-file-input-drop-title{color:#f1f5f9}.cide-file-input-drag-over .cide-file-input-drop-title{color:#93c5fd}.cide-file-input-has-files .cide-file-input-drop-title{color:#6ee7b7}.cide-file-input-drop-subtitle{color:#94a3b8}.cide-file-input-has-files .cide-file-input-drop-subtitle{color:#a7f3d0}.cide-file-input-action-btn:hover{background:#ffffff1a}.cide-file-input-action-btn.cide-file-input-clear-btn{color:#f87171}.cide-file-input-action-btn.cide-file-input-clear-btn:hover{background:#7f1d1d;color:#fca5a5}.cide-file-input-upload-status .cide-file-input-upload-count{color:#94a3b8}.cide-file-input-upload-status .cide-file-input-upload-count .upload-count-text{color:#f1f5f9}.cide-file-input-upload-status .cide-file-input-show-files-icon{color:#94a3b8}.cide-file-input-upload-status .cide-file-input-show-files-icon:hover{background:#374151;color:#60a5fa}.cide-file-input-upload-status .cide-file-input-show-files-icon:active{background:#4b5563}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }] });
4946
+ ], usesOnChanges: true, ngImport: i0, template: "<div class=\"flex flex-col gap-2\">\n <!-- Label (shown when not in preview box mode or when preview box mode but no label override) -->\n @if (labelSignal() && !isPreviewBoxMode()) {\n <label class=\"block text-sm font-medium text-gray-700 dark:text-gray-200 mb-1.5 leading-5\" [attr.for]=\"'cide-file-input-' + id()\">\n {{ labelSignal() }}@if (requiredSignal()) {<span class=\"text-red-500 dark:text-red-400\"> *</span>}\n </label>\n }\n \n <!-- Preview Box Mode -->\n @if (isPreviewBoxMode()) {\n <div class=\"cide-file-input-preview-box-container\">\n <!-- Hidden file input -->\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"hidden\"\n />\n \n <!-- Preview Box -->\n <div \n class=\"cide-file-input-preview-box\"\n [class.cide-file-input-preview-box-disabled]=\"disabledSignal()\"\n [class.cide-file-input-preview-box-has-image]=\"hasImages()\"\n [class.cide-file-input-preview-box-drag-over]=\"isDragOver()\"\n [style.width]=\"previewWidthSignal()\"\n [style.height]=\"previewHeightSignal()\"\n (click)=\"triggerFileSelect()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n [attr.title]=\"disabledSignal() ? 'File selection disabled' : placeholderTextSignal()\">\n \n <!-- No Image State -->\n @if (!hasImages()) {\n <div class=\"cide-file-input-preview-box-placeholder\">\n <div class=\"cide-file-input-preview-box-icon\">\n <cide-ele-icon>{{ isDragOver() ? '\uD83D\uDCC1' : placeholderIconSignal() }}</cide-ele-icon>\n </div>\n <div class=\"cide-file-input-preview-box-text\">\n {{ isDragOver() ? 'Drop files here...' : placeholderTextSignal() }}\n </div>\n </div>\n }\n \n <!-- Image Preview State -->\n @if (hasImages()) {\n <div class=\"cide-file-input-preview-box-content\">\n <img \n [src]=\"previewUrls()[0]\" \n [alt]=\"fileNames()[0] || 'Preview image'\"\n class=\"cide-file-input-preview-box-image\">\n <div class=\"cide-file-input-preview-box-overlay\">\n <div class=\"cide-file-input-preview-box-overlay-text\">Click to change</div>\n </div>\n @if (!disabledSignal()) {\n <button \n type=\"button\" \n class=\"cide-file-input-preview-box-remove\"\n (click)=\"clearFiles(); $event.stopPropagation()\"\n title=\"Remove image\">\n \u00D7\n </button>\n }\n </div>\n }\n </div>\n \n <!-- File name display for preview box mode -->\n @if (hasImages() && fileNames().length && showFileNameSignal()) {\n <div class=\"cide-file-input-preview-box-filename\">\n {{ fileNames()[0] }}\n </div>\n }\n </div>\n }\n\n <!-- Standard Mode -->\n @if (!isPreviewBoxMode()) {\n <!-- Hidden file input -->\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"hidden\"\n />\n \n <!-- Modern Drag and Drop Zone -->\n <div \n class=\"border-2 border-dashed border-gray-300 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-800 cursor-pointer transition-all duration-200 min-h-[60px] hover:border-blue-500 hover:bg-blue-50 dark:hover:bg-blue-900/20\"\n [class]=\"getDragDropZoneClasses()\"\n (click)=\"triggerFileSelect()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\n \n <div class=\"flex items-center justify-between p-3 gap-3\">\n <!-- Icon and Text -->\n <div class=\"flex items-center gap-2.5 flex-1 min-w-0\">\n <cide-ele-icon class=\"flex-shrink-0 transition-colors duration-200\" \n [class]=\"getIconClasses()\" \n size=\"sm\">\n {{ isDragOver() ? 'file_download' : (hasFiles() ? 'check_circle' : 'cloud_upload') }}\n </cide-ele-icon>\n \n <div class=\"flex flex-col gap-0.5 min-w-0\">\n @if (isDragOver()) {\n <span class=\"text-sm font-medium text-blue-700 dark:text-blue-300 whitespace-nowrap overflow-hidden text-ellipsis\">Drop files here</span>\n } @else if (hasFiles()) {\n <span class=\"text-sm font-medium text-emerald-700 dark:text-emerald-300 whitespace-nowrap overflow-hidden text-ellipsis\">\n @if (multipleSignal() && fileNames().length > 1) {\n {{ fileNames().length }} files selected\n } @else {\n {{ fileNames()[0] }}\n }\n </span>\n @if (totalFileSize() > 0) {\n <span class=\"text-xs text-emerald-600 dark:text-emerald-400\">{{ fileSizeInMB() }} MB</span>\n }\n } @else {\n <span class=\"text-sm font-medium text-gray-700 dark:text-gray-300 whitespace-nowrap overflow-hidden text-ellipsis\">\n {{ multipleSignal() ? 'Choose files or drag here' : 'Choose file or drag here' }}\n </span>\n }\n </div>\n </div>\n \n <!-- Action Buttons -->\n <div class=\"flex gap-1 flex-shrink-0\">\n @if (hasFiles()) {\n <button type=\"button\" \n class=\"flex items-center justify-center w-6 h-6 border-none rounded bg-transparent cursor-pointer transition-all duration-200 text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20 hover:text-red-700\" \n (click)=\"clearFiles(); $event.stopPropagation()\"\n title=\"Clear files\">\n <cide-ele-icon size=\"xs\">close</cide-ele-icon>\n </button>\n }\n </div>\n </div>\n </div>\n }\n \n <!-- Image Preview Section (only for standard mode) -->\n @if (isImagePreviewAvailable() && !isPreviewBoxMode()) {\n <div class=\"cide-file-input-preview\">\n <div class=\"cide-file-input-preview-label\">Preview:</div>\n <div class=\"cide-file-input-preview-container\">\n @for (previewUrl of previewUrls(); track previewUrl; let i = $index) {\n <div \n class=\"cide-file-input-preview-item\"\n [style.width]=\"previewWidthSignal()\"\n [style.height]=\"previewHeightSignal()\">\n <button \n type=\"button\" \n class=\"cide-file-input-preview-remove\"\n (click)=\"removePreview(i)\"\n title=\"Remove image\">\n \u00D7\n </button>\n <img \n [src]=\"previewUrl\" \n [alt]=\"fileNames()[i] || 'Preview image'\"\n class=\"cide-file-input-preview-image\"\n loading=\"lazy\">\n <div class=\"cide-file-input-preview-filename\">{{ fileNames()[i] }}</div>\n </div>\n }\n </div>\n </div>\n }\n \n <!-- Upload Status and Show Files Button (only for multiple file inputs) -->\n @if (multiple && showFloatingUploaderSignal() && (getUploadCount() > 0 || hasActiveUploads() || hasEverUploaded())) {\n <div class=\"flex items-center justify-between py-1.5 gap-2\">\n <div class=\"flex items-center gap-2\">\n <cide-ele-icon class=\"text-blue-600 dark:text-blue-400\" size=\"sm\">cloud_upload</cide-ele-icon>\n <span class=\"text-sm text-gray-700 dark:text-gray-300\">\n @if (hasActiveUploads()) {\n {{ getActiveUploadCount() }} uploading\n } @else if (getUploadCount() > 0) {\n {{ getUploadCount() }} completed\n } @else if (hasEverUploaded()) {\n View uploads\n }\n </span>\n </div>\n <button \n type=\"button\" \n class=\"flex items-center justify-center w-8 h-8 rounded-md bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 text-gray-600 dark:text-gray-300 transition-colors duration-200 border-none cursor-pointer\"\n (click)=\"showFloatingUploaderDialog()\"\n title=\"View upload progress and history\">\n <cide-ele-icon size=\"sm\">visibility</cide-ele-icon>\n </button>\n </div>\n }\n \n @if (errorTextSignal()) {\n <div class=\"text-sm text-red-600 dark:text-red-400 mt-1\">{{ errorTextSignal() }}</div>\n }\n @if (helperTextSignal() && !errorTextSignal()) {\n <div class=\"text-sm text-gray-500 dark:text-gray-400 mt-1\">{{ helperTextSignal() }}</div>\n }\n</div> ", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }] });
4906
4947
  }
4907
4948
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFileInputComponent, decorators: [{
4908
4949
  type: Component,
@@ -4917,7 +4958,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
4917
4958
  useExisting: CideEleFileInputComponent,
4918
4959
  multi: true
4919
4960
  }
4920
- ], template: "<div class=\"cide-file-input\">\n <!-- Label (shown when not in preview box mode or when preview box mode but no label override) -->\n @if (labelSignal() && !isPreviewBoxMode()) {\n <label class=\"cide-file-input-label\" [attr.for]=\"'cide-file-input-' + id()\">\n {{ labelSignal() }}@if (requiredSignal()) {<span class=\"cide-file-input-required\"> *</span>}\n </label>\n }\n \n <!-- Preview Box Mode -->\n @if (isPreviewBoxMode()) {\n <div class=\"cide-file-input-preview-box-container\">\n <!-- Hidden file input -->\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"cide-file-input-hidden\"\n />\n \n <!-- Preview Box -->\n <div \n class=\"cide-file-input-preview-box\"\n [class.cide-file-input-preview-box-disabled]=\"disabledSignal()\"\n [class.cide-file-input-preview-box-has-image]=\"hasImages()\"\n [class.cide-file-input-preview-box-drag-over]=\"isDragOver()\"\n [style.width]=\"previewWidthSignal()\"\n [style.height]=\"previewHeightSignal()\"\n (click)=\"triggerFileSelect()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n [attr.title]=\"disabledSignal() ? 'File selection disabled' : placeholderTextSignal()\">\n \n <!-- No Image State -->\n @if (!hasImages()) {\n <div class=\"cide-file-input-preview-box-placeholder\">\n <div class=\"cide-file-input-preview-box-icon\">\n <cide-ele-icon>{{ isDragOver() ? '\uD83D\uDCC1' : placeholderIconSignal() }}</cide-ele-icon>\n </div>\n <div class=\"cide-file-input-preview-box-text\">\n {{ isDragOver() ? 'Drop files here...' : placeholderTextSignal() }}\n </div>\n </div>\n }\n \n <!-- Image Preview State -->\n @if (hasImages()) {\n <div class=\"cide-file-input-preview-box-content\">\n <img \n [src]=\"previewUrls()[0]\" \n [alt]=\"fileNames()[0] || 'Preview image'\"\n class=\"cide-file-input-preview-box-image\">\n <div class=\"cide-file-input-preview-box-overlay\">\n <div class=\"cide-file-input-preview-box-overlay-text\">Click to change</div>\n </div>\n @if (!disabledSignal()) {\n <button \n type=\"button\" \n class=\"cide-file-input-preview-box-remove\"\n (click)=\"clearFiles(); $event.stopPropagation()\"\n title=\"Remove image\">\n \u00D7\n </button>\n }\n </div>\n }\n </div>\n \n <!-- File name display for preview box mode -->\n @if (hasImages() && fileNames().length && showFileNameSignal()) {\n <div class=\"cide-file-input-preview-box-filename\">\n {{ fileNames()[0] }}\n </div>\n }\n </div>\n }\n\n <!-- Standard Mode -->\n @if (!isPreviewBoxMode()) {\n <!-- Hidden file input -->\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"cide-file-input-hidden\"\n />\n \n <!-- Modern Drag and Drop Zone -->\n <div \n class=\"cide-file-input-drop-zone\"\n [class.cide-file-input-drag-over]=\"isDragOver()\"\n [class.cide-file-input-disabled]=\"disabledSignal()\"\n [class.cide-file-input-has-files]=\"hasFiles()\"\n (click)=\"triggerFileSelect()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\n \n <div class=\"cide-file-input-drop-content\">\n <!-- Icon and Text -->\n <div class=\"cide-file-input-drop-main\">\n <cide-ele-icon class=\"cide-file-input-drop-icon\" size=\"sm\">\n {{ isDragOver() ? 'file_download' : (hasFiles() ? 'check_circle' : 'cloud_upload') }}\n </cide-ele-icon>\n \n <div class=\"cide-file-input-drop-text\">\n @if (isDragOver()) {\n <span class=\"cide-file-input-drop-title\">Drop files here</span>\n } @else if (hasFiles()) {\n <span class=\"cide-file-input-drop-title\">\n @if (multipleSignal() && fileNames().length > 1) {\n {{ fileNames().length }} files selected\n } @else {\n {{ fileNames()[0] }}\n }\n </span>\n @if (totalFileSize() > 0) {\n <span class=\"cide-file-input-drop-subtitle\">{{ fileSizeInMB() }} MB</span>\n }\n } @else {\n <span class=\"cide-file-input-drop-title\">\n {{ multipleSignal() ? 'Choose files or drag here' : 'Choose file or drag here' }}\n </span>\n }\n </div>\n </div>\n \n <!-- Action Buttons -->\n <div class=\"cide-file-input-drop-actions\">\n @if (hasFiles()) {\n <button type=\"button\" \n class=\"cide-file-input-action-btn cide-file-input-clear-btn\" \n (click)=\"clearFiles(); $event.stopPropagation()\"\n title=\"Clear files\">\n <cide-ele-icon size=\"xs\">close</cide-ele-icon>\n </button>\n }\n </div>\n </div>\n </div>\n }\n \n <!-- Image Preview Section (only for standard mode) -->\n @if (isImagePreviewAvailable() && !isPreviewBoxMode()) {\n <div class=\"cide-file-input-preview\">\n <div class=\"cide-file-input-preview-label\">Preview:</div>\n <div class=\"cide-file-input-preview-container\">\n @for (previewUrl of previewUrls(); track previewUrl; let i = $index) {\n <div \n class=\"cide-file-input-preview-item\"\n [style.width]=\"previewWidthSignal()\"\n [style.height]=\"previewHeightSignal()\">\n <button \n type=\"button\" \n class=\"cide-file-input-preview-remove\"\n (click)=\"removePreview(i)\"\n title=\"Remove image\">\n \u00D7\n </button>\n <img \n [src]=\"previewUrl\" \n [alt]=\"fileNames()[i] || 'Preview image'\"\n class=\"cide-file-input-preview-image\"\n loading=\"lazy\">\n <div class=\"cide-file-input-preview-filename\">{{ fileNames()[i] }}</div>\n </div>\n }\n </div>\n </div>\n }\n \n <!-- Upload Status and Show Files Button (only for multiple file inputs) -->\n @if (multiple && showFloatingUploaderSignal() && (getUploadCount() > 0 || hasActiveUploads() || hasEverUploaded())) {\n <div class=\"cide-file-input-upload-status\">\n <div class=\"cide-file-input-upload-count\">\n <cide-ele-icon size=\"sm\">cloud_upload</cide-ele-icon>\n <span class=\"upload-count-text\">\n @if (hasActiveUploads()) {\n {{ getActiveUploadCount() }} uploading\n } @else if (getUploadCount() > 0) {\n {{ getUploadCount() }} completed\n } @else if (hasEverUploaded()) {\n View uploads\n }\n </span>\n </div>\n <button \n type=\"button\" \n class=\"cide-file-input-show-files-icon\"\n (click)=\"showFloatingUploaderDialog()\"\n title=\"View upload progress and history\">\n <cide-ele-icon size=\"sm\">visibility</cide-ele-icon>\n </button>\n </div>\n }\n \n @if (errorTextSignal()) {\n <div class=\"cide-file-input-error\">{{ errorTextSignal() }}</div>\n }\n @if (helperTextSignal() && !errorTextSignal()) {\n <div class=\"cide-file-input-helper\">{{ helperTextSignal() }}</div>\n }\n</div> ", styles: [".cide-file-input{display:flex;flex-direction:column;gap:.5rem}.cide-file-input-drop-zone{border:2px dashed #d1d5db;border-radius:8px;background:#f9fafb;cursor:pointer;transition:all .2s ease;min-height:60px}.cide-file-input-drop-zone:hover{border-color:#3b82f6;background:#f0f9ff}.cide-file-input-drop-zone.cide-file-input-drag-over{border-color:#3b82f6;background:#dbeafe;transform:scale(1.01)}.cide-file-input-drop-zone.cide-file-input-disabled{opacity:.5;cursor:not-allowed}.cide-file-input-drop-zone.cide-file-input-disabled:hover{border-color:#d1d5db;background:#f9fafb;transform:none}.cide-file-input-drop-zone.cide-file-input-has-files{border-color:#10b981;background:#f0fdf4}.cide-file-input-drop-zone.cide-file-input-has-files:hover{border-color:#059669;background:#ecfdf5}.cide-file-input-drop-content{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;gap:12px}.cide-file-input-drop-main{display:flex;align-items:center;gap:10px;flex:1;min-width:0}.cide-file-input-drop-icon{color:#6b7280;transition:color .2s ease;flex-shrink:0}.cide-file-input-drag-over .cide-file-input-drop-icon{color:#3b82f6}.cide-file-input-has-files .cide-file-input-drop-icon{color:#10b981}.cide-file-input-drop-text{display:flex;flex-direction:column;gap:2px;min-width:0}.cide-file-input-drop-title{font-size:14px;font-weight:500;color:#374151;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.cide-file-input-drag-over .cide-file-input-drop-title{color:#1d4ed8}.cide-file-input-has-files .cide-file-input-drop-title{color:#065f46}.cide-file-input-drop-subtitle{font-size:12px;color:#6b7280}.cide-file-input-has-files .cide-file-input-drop-subtitle{color:#047857}.cide-file-input-drop-actions{display:flex;gap:4px;flex-shrink:0}.cide-file-input-action-btn{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border:none;border-radius:4px;background:transparent;cursor:pointer;transition:all .2s ease}.cide-file-input-action-btn:hover{background:#0000000d}.cide-file-input-action-btn.cide-file-input-clear-btn{color:#dc2626}.cide-file-input-action-btn.cide-file-input-clear-btn:hover{background:#fef2f2;color:#b91c1c}.cide-file-input-label{display:block;font-size:.875rem;font-weight:500;color:#374151;margin-bottom:.375rem;line-height:1.25}.cide-file-input-required{color:#d32f2f;font-weight:700}.cide-file-input-wrapper{display:flex;align-items:center;gap:.5rem;border:2px dashed transparent;border-radius:.5rem;padding:.5rem;transition:all .2s ease-in-out}.cide-file-input-wrapper.cide-file-input-drag-over{border-color:#3b82f6;background-color:#eff6ff;transform:scale(1.02)}.cide-file-input-element{flex:1}.cide-file-input-clear{background:none;border:none;color:#d32f2f;cursor:pointer;font-size:.9rem}.cide-file-input-files{font-size:.95rem;color:#333;margin-top:.25rem}.cide-file-input-multiple-count{display:inline-flex;align-items:center;gap:.5rem;padding:.5rem 1rem;background-color:#f0f9ff;border:1px solid #0ea5e9;border-radius:.5rem;color:#0369a1;font-weight:500;font-size:.9rem}.cide-file-input-size{margin-top:.5rem;padding:.25rem .5rem;background-color:#f3f4f6;border-radius:.25rem;font-size:.75rem;color:#4b5563;font-weight:500}.cide-file-input-error{color:#d32f2f;font-size:.9rem}.cide-file-input-helper{color:#666;font-size:.9rem}.cide-file-input-preview{margin-top:.75rem;padding:.75rem;background-color:#f8f9fa;border:1px solid #e1e5e9;border-radius:.375rem}.cide-file-input-preview-label{font-weight:500;margin-bottom:.5rem;color:#374151;font-size:.875rem}.cide-file-input-preview-container{display:flex;flex-wrap:wrap;gap:.75rem}.cide-file-input-preview-item{position:relative;display:flex;flex-direction:column;border:1px solid #d1d5db;border-radius:.5rem;overflow:hidden;background-color:#fff;box-shadow:0 1px 3px #0000001a;transition:box-shadow .2s ease-in-out}.cide-file-input-preview-item:hover{box-shadow:0 4px 6px -1px #0000001a}.cide-file-input-preview-image{width:100%;height:calc(100% - 2rem);object-fit:cover;object-position:center;background-color:#f3f4f6}.cide-file-input-preview-filename{padding:.375rem .5rem;background-color:#f9fafbf2;border-top:1px solid #e5e7eb;font-size:.75rem;color:#374151;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-height:2rem;display:flex;align-items:center;justify-content:center}.cide-file-input-preview-remove{position:absolute;top:.25rem;right:.25rem;width:1.5rem;height:1.5rem;background-color:#ef4444e6;color:#fff;border:none;border-radius:50%;font-size:1rem;font-weight:700;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;z-index:10;transition:all .2s ease-in-out}.cide-file-input-preview-remove:hover{background-color:#dc2626f2;transform:scale(1.1)}.cide-file-input-preview-remove:focus{outline:2px solid #3b82f6;outline-offset:2px}.cide-file-input-hidden{display:none}.cide-file-input-preview-box-container{display:flex;flex-direction:column;gap:.5rem}.cide-file-input-preview-box{position:relative;border:2px dashed #d1d5db;border-radius:.5rem;cursor:pointer;background-color:#f9fafb;display:flex;align-items:center;justify-content:center;overflow:hidden;transition:all .2s ease-in-out}.cide-file-input-preview-box:hover{border-color:#3b82f6;background-color:#eff6ff}.cide-file-input-preview-box.cide-file-input-preview-box-disabled{cursor:not-allowed;opacity:.6;background-color:#f3f4f6}.cide-file-input-preview-box.cide-file-input-preview-box-disabled:hover{border-color:#d1d5db;background-color:#f3f4f6}.cide-file-input-preview-box.cide-file-input-preview-box-has-image{border-style:solid;border-color:#e5e7eb;padding:0}.cide-file-input-preview-box.cide-file-input-preview-box-has-image:hover{border-color:#3b82f6}.cide-file-input-preview-box.cide-file-input-preview-box-has-image:hover .cide-file-input-preview-box-overlay{opacity:1}.cide-file-input-preview-box.cide-file-input-preview-box-drag-over{border-color:#3b82f6!important;background-color:#eff6ff!important;transform:scale(1.02);box-shadow:0 0 0 4px #3b82f61a}.cide-file-input-preview-box.cide-file-input-preview-box-drag-over .cide-file-input-preview-box-placeholder .cide-file-input-preview-box-icon{color:#3b82f6;transform:scale(1.1)}.cide-file-input-preview-box.cide-file-input-preview-box-drag-over .cide-file-input-preview-box-placeholder .cide-file-input-preview-box-text{color:#3b82f6;font-weight:600}.cide-file-input-preview-box-placeholder{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;padding:1rem;text-align:center}.cide-file-input-preview-box-icon{font-size:2rem;color:#6b7280}.cide-file-input-preview-box-text{font-size:.875rem;color:#6b7280;font-weight:500}.cide-file-input-preview-box-content{position:relative;width:100%;height:100%;display:flex;align-items:center;justify-content:center}.cide-file-input-preview-box-image{width:100%;height:100%;object-fit:cover;object-position:center}.cide-file-input-preview-box-overlay{position:absolute;inset:0;background-color:#0009;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .2s ease-in-out}.cide-file-input-preview-box-overlay-text{color:#fff;font-size:.875rem;font-weight:500;text-align:center}.cide-file-input-preview-box-remove{position:absolute;top:.375rem;right:.375rem;width:1.5rem;height:1.5rem;background-color:#ef4444e6;color:#fff;border:none;border-radius:50%;font-size:1rem;font-weight:700;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;z-index:20;transition:all .2s ease-in-out}.cide-file-input-preview-box-remove:hover{background-color:#dc2626f2;transform:scale(1.1)}.cide-file-input-preview-box-remove:focus{outline:2px solid #3b82f6;outline-offset:2px}.cide-file-input-preview-box-filename{font-size:.75rem;color:#374151;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding:.25rem .5rem;background-color:#f3f4f6;border-radius:.25rem;margin-top:.25rem}@media (max-width: 640px){.cide-file-input-preview-container{justify-content:center}.cide-file-input-preview-item{min-width:120px;max-width:150px}.cide-file-input-preview-box-icon{font-size:1.5rem}.cide-file-input-preview-box-text{font-size:.75rem}}.cide-file-input-upload-status{display:flex;align-items:center;justify-content:space-between;padding:.25rem 0;margin-top:.375rem;gap:.5rem}.cide-file-input-upload-status .cide-file-input-upload-count{display:flex;align-items:center;gap:.5rem;color:#64748b;font-size:.875rem;font-weight:500}.cide-file-input-upload-status .cide-file-input-upload-count .upload-count-text{color:#1e293b}.cide-file-input-upload-status .cide-file-input-show-files-icon{display:flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;background:transparent;color:#6b7280;border:none;border-radius:.25rem;cursor:pointer;transition:all .2s ease}.cide-file-input-upload-status .cide-file-input-show-files-icon:hover{background:#f3f4f6;color:#3b82f6}.cide-file-input-upload-status .cide-file-input-show-files-icon:active{background:#e5e7eb}@media (prefers-color-scheme: dark){.cide-file-input-label{color:#f9fafb}.cide-file-input-required{color:#f87171}.cide-file-input-drop-zone{border-color:#475569;background:#334155}.cide-file-input-drop-zone:hover{border-color:#3b82f6;background:#1e3a8a}.cide-file-input-drop-zone.cide-file-input-drag-over{border-color:#60a5fa;background:#1e40af}.cide-file-input-drop-zone.cide-file-input-has-files{border-color:#10b981;background:#064e3b}.cide-file-input-drop-zone.cide-file-input-has-files:hover{border-color:#059669;background:#065f46}.cide-file-input-drop-icon{color:#94a3b8}.cide-file-input-drag-over .cide-file-input-drop-icon{color:#60a5fa}.cide-file-input-has-files .cide-file-input-drop-icon{color:#34d399}.cide-file-input-drop-title{color:#f1f5f9}.cide-file-input-drag-over .cide-file-input-drop-title{color:#93c5fd}.cide-file-input-has-files .cide-file-input-drop-title{color:#6ee7b7}.cide-file-input-drop-subtitle{color:#94a3b8}.cide-file-input-has-files .cide-file-input-drop-subtitle{color:#a7f3d0}.cide-file-input-action-btn:hover{background:#ffffff1a}.cide-file-input-action-btn.cide-file-input-clear-btn{color:#f87171}.cide-file-input-action-btn.cide-file-input-clear-btn:hover{background:#7f1d1d;color:#fca5a5}.cide-file-input-upload-status .cide-file-input-upload-count{color:#94a3b8}.cide-file-input-upload-status .cide-file-input-upload-count .upload-count-text{color:#f1f5f9}.cide-file-input-upload-status .cide-file-input-show-files-icon{color:#94a3b8}.cide-file-input-upload-status .cide-file-input-show-files-icon:hover{background:#374151;color:#60a5fa}.cide-file-input-upload-status .cide-file-input-show-files-icon:active{background:#4b5563}}\n"] }]
4961
+ ], template: "<div class=\"flex flex-col gap-2\">\n <!-- Label (shown when not in preview box mode or when preview box mode but no label override) -->\n @if (labelSignal() && !isPreviewBoxMode()) {\n <label class=\"block text-sm font-medium text-gray-700 dark:text-gray-200 mb-1.5 leading-5\" [attr.for]=\"'cide-file-input-' + id()\">\n {{ labelSignal() }}@if (requiredSignal()) {<span class=\"text-red-500 dark:text-red-400\"> *</span>}\n </label>\n }\n \n <!-- Preview Box Mode -->\n @if (isPreviewBoxMode()) {\n <div class=\"cide-file-input-preview-box-container\">\n <!-- Hidden file input -->\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"hidden\"\n />\n \n <!-- Preview Box -->\n <div \n class=\"cide-file-input-preview-box\"\n [class.cide-file-input-preview-box-disabled]=\"disabledSignal()\"\n [class.cide-file-input-preview-box-has-image]=\"hasImages()\"\n [class.cide-file-input-preview-box-drag-over]=\"isDragOver()\"\n [style.width]=\"previewWidthSignal()\"\n [style.height]=\"previewHeightSignal()\"\n (click)=\"triggerFileSelect()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n [attr.title]=\"disabledSignal() ? 'File selection disabled' : placeholderTextSignal()\">\n \n <!-- No Image State -->\n @if (!hasImages()) {\n <div class=\"cide-file-input-preview-box-placeholder\">\n <div class=\"cide-file-input-preview-box-icon\">\n <cide-ele-icon>{{ isDragOver() ? '\uD83D\uDCC1' : placeholderIconSignal() }}</cide-ele-icon>\n </div>\n <div class=\"cide-file-input-preview-box-text\">\n {{ isDragOver() ? 'Drop files here...' : placeholderTextSignal() }}\n </div>\n </div>\n }\n \n <!-- Image Preview State -->\n @if (hasImages()) {\n <div class=\"cide-file-input-preview-box-content\">\n <img \n [src]=\"previewUrls()[0]\" \n [alt]=\"fileNames()[0] || 'Preview image'\"\n class=\"cide-file-input-preview-box-image\">\n <div class=\"cide-file-input-preview-box-overlay\">\n <div class=\"cide-file-input-preview-box-overlay-text\">Click to change</div>\n </div>\n @if (!disabledSignal()) {\n <button \n type=\"button\" \n class=\"cide-file-input-preview-box-remove\"\n (click)=\"clearFiles(); $event.stopPropagation()\"\n title=\"Remove image\">\n \u00D7\n </button>\n }\n </div>\n }\n </div>\n \n <!-- File name display for preview box mode -->\n @if (hasImages() && fileNames().length && showFileNameSignal()) {\n <div class=\"cide-file-input-preview-box-filename\">\n {{ fileNames()[0] }}\n </div>\n }\n </div>\n }\n\n <!-- Standard Mode -->\n @if (!isPreviewBoxMode()) {\n <!-- Hidden file input -->\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"hidden\"\n />\n \n <!-- Modern Drag and Drop Zone -->\n <div \n class=\"border-2 border-dashed border-gray-300 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-800 cursor-pointer transition-all duration-200 min-h-[60px] hover:border-blue-500 hover:bg-blue-50 dark:hover:bg-blue-900/20\"\n [class]=\"getDragDropZoneClasses()\"\n (click)=\"triggerFileSelect()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\n \n <div class=\"flex items-center justify-between p-3 gap-3\">\n <!-- Icon and Text -->\n <div class=\"flex items-center gap-2.5 flex-1 min-w-0\">\n <cide-ele-icon class=\"flex-shrink-0 transition-colors duration-200\" \n [class]=\"getIconClasses()\" \n size=\"sm\">\n {{ isDragOver() ? 'file_download' : (hasFiles() ? 'check_circle' : 'cloud_upload') }}\n </cide-ele-icon>\n \n <div class=\"flex flex-col gap-0.5 min-w-0\">\n @if (isDragOver()) {\n <span class=\"text-sm font-medium text-blue-700 dark:text-blue-300 whitespace-nowrap overflow-hidden text-ellipsis\">Drop files here</span>\n } @else if (hasFiles()) {\n <span class=\"text-sm font-medium text-emerald-700 dark:text-emerald-300 whitespace-nowrap overflow-hidden text-ellipsis\">\n @if (multipleSignal() && fileNames().length > 1) {\n {{ fileNames().length }} files selected\n } @else {\n {{ fileNames()[0] }}\n }\n </span>\n @if (totalFileSize() > 0) {\n <span class=\"text-xs text-emerald-600 dark:text-emerald-400\">{{ fileSizeInMB() }} MB</span>\n }\n } @else {\n <span class=\"text-sm font-medium text-gray-700 dark:text-gray-300 whitespace-nowrap overflow-hidden text-ellipsis\">\n {{ multipleSignal() ? 'Choose files or drag here' : 'Choose file or drag here' }}\n </span>\n }\n </div>\n </div>\n \n <!-- Action Buttons -->\n <div class=\"flex gap-1 flex-shrink-0\">\n @if (hasFiles()) {\n <button type=\"button\" \n class=\"flex items-center justify-center w-6 h-6 border-none rounded bg-transparent cursor-pointer transition-all duration-200 text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20 hover:text-red-700\" \n (click)=\"clearFiles(); $event.stopPropagation()\"\n title=\"Clear files\">\n <cide-ele-icon size=\"xs\">close</cide-ele-icon>\n </button>\n }\n </div>\n </div>\n </div>\n }\n \n <!-- Image Preview Section (only for standard mode) -->\n @if (isImagePreviewAvailable() && !isPreviewBoxMode()) {\n <div class=\"cide-file-input-preview\">\n <div class=\"cide-file-input-preview-label\">Preview:</div>\n <div class=\"cide-file-input-preview-container\">\n @for (previewUrl of previewUrls(); track previewUrl; let i = $index) {\n <div \n class=\"cide-file-input-preview-item\"\n [style.width]=\"previewWidthSignal()\"\n [style.height]=\"previewHeightSignal()\">\n <button \n type=\"button\" \n class=\"cide-file-input-preview-remove\"\n (click)=\"removePreview(i)\"\n title=\"Remove image\">\n \u00D7\n </button>\n <img \n [src]=\"previewUrl\" \n [alt]=\"fileNames()[i] || 'Preview image'\"\n class=\"cide-file-input-preview-image\"\n loading=\"lazy\">\n <div class=\"cide-file-input-preview-filename\">{{ fileNames()[i] }}</div>\n </div>\n }\n </div>\n </div>\n }\n \n <!-- Upload Status and Show Files Button (only for multiple file inputs) -->\n @if (multiple && showFloatingUploaderSignal() && (getUploadCount() > 0 || hasActiveUploads() || hasEverUploaded())) {\n <div class=\"flex items-center justify-between py-1.5 gap-2\">\n <div class=\"flex items-center gap-2\">\n <cide-ele-icon class=\"text-blue-600 dark:text-blue-400\" size=\"sm\">cloud_upload</cide-ele-icon>\n <span class=\"text-sm text-gray-700 dark:text-gray-300\">\n @if (hasActiveUploads()) {\n {{ getActiveUploadCount() }} uploading\n } @else if (getUploadCount() > 0) {\n {{ getUploadCount() }} completed\n } @else if (hasEverUploaded()) {\n View uploads\n }\n </span>\n </div>\n <button \n type=\"button\" \n class=\"flex items-center justify-center w-8 h-8 rounded-md bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 text-gray-600 dark:text-gray-300 transition-colors duration-200 border-none cursor-pointer\"\n (click)=\"showFloatingUploaderDialog()\"\n title=\"View upload progress and history\">\n <cide-ele-icon size=\"sm\">visibility</cide-ele-icon>\n </button>\n </div>\n }\n \n @if (errorTextSignal()) {\n <div class=\"text-sm text-red-600 dark:text-red-400 mt-1\">{{ errorTextSignal() }}</div>\n }\n @if (helperTextSignal() && !errorTextSignal()) {\n <div class=\"text-sm text-gray-500 dark:text-gray-400 mt-1\">{{ helperTextSignal() }}</div>\n }\n</div> " }]
4921
4962
  }], ctorParameters: () => [], propDecorators: { label: [{
4922
4963
  type: Input
4923
4964
  }], accept: [{