cloud-ide-element 1.0.61 → 1.0.64

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.
@@ -1,13 +1,16 @@
1
1
  import * as i1 from '@angular/common';
2
2
  import { CommonModule, NgTemplateOutlet } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
- import { Pipe, Injectable, inject, EventEmitter, ViewContainerRef, forwardRef, ViewChild, Output, Input, Component, HostListener, ContentChildren, signal, DestroyRef, computed, input, output, linkedSignal, afterRenderEffect, afterNextRender, Directive, viewChild, effect } from '@angular/core';
4
+ import { Pipe, Injectable, inject, EventEmitter, ViewContainerRef, forwardRef, ViewChild, Output, Input, Component, HostListener, ContentChildren, signal, DestroyRef, computed, afterRenderEffect, afterNextRender, Directive, ElementRef, viewChild, effect } from '@angular/core';
5
5
  import * as i2 from '@angular/forms';
6
6
  import { FormsModule, NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
7
7
  import { BehaviorSubject, Subject, debounceTime, takeUntil, distinctUntilChanged, Observable, retry, catchError, finalize, throwError } from 'rxjs';
8
8
  import * as i2$1 from '@angular/router';
9
- import { HttpClient, HttpEventType } from '@angular/common/http';
9
+ import * as i1$1 from '@angular/common/http';
10
+ import { HttpClient, HttpEventType, HttpRequest } from '@angular/common/http';
10
11
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
12
+ import { tap, catchError as catchError$1 } from 'rxjs/operators';
13
+ import { coreRoutesUrl, generateStringFromObject, cidePath, hostManagerRoutesUrl } from 'cloud-ide-lms-model';
11
14
 
12
15
  class CapitalizePipe {
13
16
  transform(value, capitalizationMethod) {
@@ -2564,23 +2567,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
2564
2567
  type: Output
2565
2568
  }] } });
2566
2569
 
2567
- class ICoreCyfm {
2568
- _id;
2569
- cyfm_name;
2570
- cyfm_alt_text;
2571
- cyfm_path;
2572
- cyfm_size_in_byte;
2573
- cyfm_type;
2574
- cyfm_creation_dt;
2575
- cyfm_id_user;
2576
- cyfm_permissions;
2577
- cyfm_tags;
2578
- cyfm_version;
2579
- cyfm_file_status_sygmt;
2580
- cyfm_isactive;
2581
- }
2582
- /* INTERFACE END */
2583
- /* MODEL START */
2570
+ /**
2571
+ * File Manager Interfaces
2572
+ * Based on core_file_manager table schema
2573
+ */
2584
2574
  class MFileManager {
2585
2575
  cyfm_id = "";
2586
2576
  constructor(init) {
@@ -2595,11 +2585,23 @@ class MFileManager {
2595
2585
  return errorLogger;
2596
2586
  }
2597
2587
  }
2598
- class ICoreCyfmSave extends ICoreCyfm {
2588
+ class ICoreCyfmSave {
2589
+ _id;
2590
+ cyfm_name;
2591
+ cyfm_alt_text;
2592
+ cyfm_path;
2593
+ cyfm_size_in_byte;
2594
+ cyfm_type;
2595
+ cyfm_creation_dt;
2596
+ cyfm_id_user;
2597
+ cyfm_permissions;
2598
+ cyfm_tags;
2599
+ cyfm_version;
2600
+ cyfm_file_status_sygmt;
2601
+ cyfm_isactive;
2599
2602
  cyfm_file_base64 = "";
2600
2603
  cyfm_temp_unique_id = "";
2601
2604
  constructor(init) {
2602
- super();
2603
2605
  Object.assign(this, init);
2604
2606
  }
2605
2607
  }
@@ -3108,28 +3110,45 @@ class CideEleFileInputComponent {
3108
3110
  elementService = inject(CideElementsService);
3109
3111
  destroyRef = inject(DestroyRef);
3110
3112
  // private readonly pendingTasks = inject(PendingTasks); // TODO: Fix PendingTasks API usage
3111
- // Modern input signals
3112
- label = input('Choose file', ...(ngDevMode ? [{ debugName: "label" }] : []));
3113
- accept = input('', ...(ngDevMode ? [{ debugName: "accept" }] : []));
3114
- multiple = input(false, ...(ngDevMode ? [{ debugName: "multiple" }] : []));
3115
- disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
3116
- required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : []));
3117
- helperText = input('', ...(ngDevMode ? [{ debugName: "helperText" }] : []));
3118
- errorText = input('', ...(ngDevMode ? [{ debugName: "errorText" }] : []));
3119
- showPreview = input(false, ...(ngDevMode ? [{ debugName: "showPreview" }] : []));
3120
- previewWidth = input('200px', ...(ngDevMode ? [{ debugName: "previewWidth" }] : []));
3121
- previewHeight = input('200px', ...(ngDevMode ? [{ debugName: "previewHeight" }] : []));
3122
- previewBoxMode = input(false, ...(ngDevMode ? [{ debugName: "previewBoxMode" }] : []));
3123
- showFileName = input(true, ...(ngDevMode ? [{ debugName: "showFileName" }] : []));
3124
- placeholderText = input('Click to select image', ...(ngDevMode ? [{ debugName: "placeholderText" }] : []));
3125
- placeholderIcon = input('📷', ...(ngDevMode ? [{ debugName: "placeholderIcon" }] : []));
3126
- autoUpload = input(false, ...(ngDevMode ? [{ debugName: "autoUpload" }] : []));
3127
- uploadData = input({}, ...(ngDevMode ? [{ debugName: "uploadData" }] : []));
3128
- // Modern output signals
3129
- fileChange = output();
3130
- uploadSuccess = output();
3131
- uploadError = output();
3132
- uploadProgressChange = output();
3113
+ // Traditional @Input() decorators
3114
+ label = 'Choose file';
3115
+ accept = '';
3116
+ multiple = false;
3117
+ disabled = false;
3118
+ required = false;
3119
+ helperText = '';
3120
+ errorText = '';
3121
+ showPreview = false;
3122
+ previewWidth = '200px';
3123
+ previewHeight = '200px';
3124
+ previewBoxMode = false;
3125
+ showFileName = true;
3126
+ placeholderText = 'Click to select image';
3127
+ placeholderIcon = '📷';
3128
+ autoUpload = false;
3129
+ uploadData = {};
3130
+ // Traditional @Output() decorators
3131
+ fileChange = new EventEmitter();
3132
+ uploadSuccess = new EventEmitter();
3133
+ uploadError = new EventEmitter();
3134
+ uploadProgressChange = new EventEmitter();
3135
+ // Readable signals created from @Input() decorator values
3136
+ labelSignal = signal(this.label, ...(ngDevMode ? [{ debugName: "labelSignal" }] : []));
3137
+ acceptSignal = signal(this.accept, ...(ngDevMode ? [{ debugName: "acceptSignal" }] : []));
3138
+ multipleSignal = signal(this.multiple, ...(ngDevMode ? [{ debugName: "multipleSignal" }] : []));
3139
+ disabledSignal = signal(this.disabled, ...(ngDevMode ? [{ debugName: "disabledSignal" }] : []));
3140
+ requiredSignal = signal(this.required, ...(ngDevMode ? [{ debugName: "requiredSignal" }] : []));
3141
+ helperTextSignal = signal(this.helperText, ...(ngDevMode ? [{ debugName: "helperTextSignal" }] : []));
3142
+ errorTextSignal = signal(this.errorText, ...(ngDevMode ? [{ debugName: "errorTextSignal" }] : []));
3143
+ showPreviewSignal = signal(this.showPreview, ...(ngDevMode ? [{ debugName: "showPreviewSignal" }] : []));
3144
+ previewWidthSignal = signal(this.previewWidth, ...(ngDevMode ? [{ debugName: "previewWidthSignal" }] : []));
3145
+ previewHeightSignal = signal(this.previewHeight, ...(ngDevMode ? [{ debugName: "previewHeightSignal" }] : []));
3146
+ previewBoxModeSignal = signal(this.previewBoxMode, ...(ngDevMode ? [{ debugName: "previewBoxModeSignal" }] : []));
3147
+ showFileNameSignal = signal(this.showFileName, ...(ngDevMode ? [{ debugName: "showFileNameSignal" }] : []));
3148
+ placeholderTextSignal = signal(this.placeholderText, ...(ngDevMode ? [{ debugName: "placeholderTextSignal" }] : []));
3149
+ placeholderIconSignal = signal(this.placeholderIcon, ...(ngDevMode ? [{ debugName: "placeholderIconSignal" }] : []));
3150
+ autoUploadSignal = signal(this.autoUpload, ...(ngDevMode ? [{ debugName: "autoUploadSignal" }] : []));
3151
+ uploadDataSignal = signal(this.uploadData, ...(ngDevMode ? [{ debugName: "uploadDataSignal" }] : []));
3133
3152
  // Reactive state with signals
3134
3153
  id = signal(Math.random().toString(36).substring(2, 10), ...(ngDevMode ? [{ debugName: "id" }] : []));
3135
3154
  isUploading = signal(false, ...(ngDevMode ? [{ debugName: "isUploading" }] : []));
@@ -3140,11 +3159,11 @@ class CideEleFileInputComponent {
3140
3159
  previewUrls = signal([], ...(ngDevMode ? [{ debugName: "previewUrls" }] : []));
3141
3160
  uploadNotificationId = signal(null, ...(ngDevMode ? [{ debugName: "uploadNotificationId" }] : []));
3142
3161
  isDragOver = signal(false, ...(ngDevMode ? [{ debugName: "isDragOver" }] : []));
3143
- // Angular 20: Linked signals for better relationships
3144
- hasFiles = linkedSignal(() => this.files() !== null && this.files().length > 0);
3145
- canUpload = linkedSignal(() => this.hasFiles() && !this.isUploading() && !this.disabled());
3146
- isInErrorState = linkedSignal(() => this.uploadStatus() === 'error');
3147
- isInSuccessState = linkedSignal(() => this.uploadStatus() === 'success');
3162
+ // Computed signals for better relationships
3163
+ hasFiles = computed(() => this.files() !== null && this.files().length > 0, ...(ngDevMode ? [{ debugName: "hasFiles" }] : []));
3164
+ canUpload = computed(() => this.hasFiles() && !this.isUploading() && !this.disabledSignal(), ...(ngDevMode ? [{ debugName: "canUpload" }] : []));
3165
+ isInErrorState = computed(() => this.uploadStatus() === 'error', ...(ngDevMode ? [{ debugName: "isInErrorState" }] : []));
3166
+ isInSuccessState = computed(() => this.uploadStatus() === 'success', ...(ngDevMode ? [{ debugName: "isInSuccessState" }] : []));
3148
3167
  // Angular 20: Computed values using new features
3149
3168
  totalFileSize = computed(() => {
3150
3169
  if (!this.files())
@@ -3165,8 +3184,8 @@ class CideEleFileInputComponent {
3165
3184
  onValidatorChange = () => { };
3166
3185
  // Computed values
3167
3186
  hasImages = computed(() => this.previewUrls().length > 0, ...(ngDevMode ? [{ debugName: "hasImages" }] : []));
3168
- isPreviewBoxMode = computed(() => this.previewBoxMode() && this.showPreview(), ...(ngDevMode ? [{ debugName: "isPreviewBoxMode" }] : []));
3169
- isImagePreviewAvailable = computed(() => this.showPreview() && this.previewUrls().length > 0, ...(ngDevMode ? [{ debugName: "isImagePreviewAvailable" }] : []));
3187
+ isPreviewBoxMode = computed(() => this.previewBoxModeSignal() && this.showPreviewSignal(), ...(ngDevMode ? [{ debugName: "isPreviewBoxMode" }] : []));
3188
+ isImagePreviewAvailable = computed(() => this.showPreviewSignal() && this.previewUrls().length > 0, ...(ngDevMode ? [{ debugName: "isImagePreviewAvailable" }] : []));
3170
3189
  constructor() {
3171
3190
  // Angular 20: afterRenderEffect for DOM operations
3172
3191
  afterRenderEffect(() => {
@@ -3184,6 +3203,25 @@ class CideEleFileInputComponent {
3184
3203
  console.log('🎯 [FileInput] Component rendered and DOM is ready');
3185
3204
  });
3186
3205
  }
3206
+ ngOnInit() {
3207
+ // Update signals with initial @Input() values
3208
+ this.labelSignal.set(this.label);
3209
+ this.acceptSignal.set(this.accept);
3210
+ this.multipleSignal.set(this.multiple);
3211
+ this.disabledSignal.set(this.disabled);
3212
+ this.requiredSignal.set(this.required);
3213
+ this.helperTextSignal.set(this.helperText);
3214
+ this.errorTextSignal.set(this.errorText);
3215
+ this.showPreviewSignal.set(this.showPreview);
3216
+ this.previewWidthSignal.set(this.previewWidth);
3217
+ this.previewHeightSignal.set(this.previewHeight);
3218
+ this.previewBoxModeSignal.set(this.previewBoxMode);
3219
+ this.showFileNameSignal.set(this.showFileName);
3220
+ this.placeholderTextSignal.set(this.placeholderText);
3221
+ this.placeholderIconSignal.set(this.placeholderIcon);
3222
+ this.autoUploadSignal.set(this.autoUpload);
3223
+ this.uploadDataSignal.set(this.uploadData);
3224
+ }
3187
3225
  writeValue(value) {
3188
3226
  console.log('📝 [FileInput] writeValue called with:', value);
3189
3227
  if (typeof value === 'string') {
@@ -3239,7 +3277,7 @@ class CideEleFileInputComponent {
3239
3277
  this.fileChange.emit(selectedFiles);
3240
3278
  this.onTouched();
3241
3279
  // Auto upload if enabled
3242
- if (this.autoUpload() && selectedFiles && selectedFiles.length > 0) {
3280
+ if (this.autoUploadSignal() && selectedFiles && selectedFiles.length > 0) {
3243
3281
  console.log('🚀 [FileInput] Auto upload enabled, starting upload for:', selectedFiles[0].name);
3244
3282
  this.uploadFile(selectedFiles[0]);
3245
3283
  }
@@ -3276,7 +3314,7 @@ class CideEleFileInputComponent {
3276
3314
  const notificationId = this.notificationService.showProgress('🔄 Preparing file upload...', 0, { duration: 0 });
3277
3315
  this.uploadNotificationId.set(notificationId);
3278
3316
  console.log('🔔 [FileInput] Progress notification started with ID:', notificationId);
3279
- this.fileManagerService.uploadFile(file, this.uploadData(), (progress) => {
3317
+ this.fileManagerService.uploadFile(file, this.uploadDataSignal(), (progress) => {
3280
3318
  // Real progress callback from file manager service
3281
3319
  this.uploadProgress.set(progress);
3282
3320
  this.uploadProgressChange.emit(progress);
@@ -3375,7 +3413,7 @@ class CideEleFileInputComponent {
3375
3413
  generatePreviews() {
3376
3414
  // Clear existing previews
3377
3415
  this.clearPreviews();
3378
- if (!this.showPreview() || !this.files()) {
3416
+ if (!this.showPreviewSignal() || !this.files()) {
3379
3417
  return;
3380
3418
  }
3381
3419
  Array.from(this.files()).forEach(file => {
@@ -3404,6 +3442,8 @@ class CideEleFileInputComponent {
3404
3442
  }
3405
3443
  loadFileDetailsFromId(fileId) {
3406
3444
  console.log('🔍 [FileInput] Loading file details for ID:', fileId);
3445
+ if (!fileId)
3446
+ return;
3407
3447
  this.fileManagerService?.getFileDetails({ cyfm_id: fileId })?.pipe(takeUntilDestroyed(this.destroyRef)).subscribe({
3408
3448
  next: (fileDetails) => {
3409
3449
  console.log('📋 [FileInput] File details received:', fileDetails);
@@ -3416,7 +3456,7 @@ class CideEleFileInputComponent {
3416
3456
  console.log('📝 [FileInput] File name set:', fileData.cyfm_name);
3417
3457
  }
3418
3458
  // If it's an image and we have base64 data, set preview
3419
- if (this.showPreview() && fileData.cyfm_file_base64) {
3459
+ if (this.showPreviewSignal() && fileData.cyfm_file_base64) {
3420
3460
  // Check if it's an image file based on file name or type
3421
3461
  const isImage = this.isImageFileFromName(fileData.cyfm_name || '') ||
3422
3462
  this.isImageFileFromType(fileData.cyfm_type || '');
@@ -3499,7 +3539,7 @@ class CideEleFileInputComponent {
3499
3539
  }
3500
3540
  triggerFileSelect() {
3501
3541
  const fileInput = document.getElementById('cide-file-input-' + this.id());
3502
- if (fileInput && !this.disabled()) {
3542
+ if (fileInput && !this.disabledSignal()) {
3503
3543
  fileInput.click();
3504
3544
  }
3505
3545
  }
@@ -3507,7 +3547,7 @@ class CideEleFileInputComponent {
3507
3547
  onDragOver(event) {
3508
3548
  event.preventDefault();
3509
3549
  event.stopPropagation();
3510
- if (!this.disabled()) {
3550
+ if (!this.disabledSignal()) {
3511
3551
  this.isDragOver.set(true);
3512
3552
  console.log('🔄 [FileInput] Drag over detected');
3513
3553
  }
@@ -3521,7 +3561,7 @@ class CideEleFileInputComponent {
3521
3561
  onDragEnter(event) {
3522
3562
  event.preventDefault();
3523
3563
  event.stopPropagation();
3524
- if (!this.disabled()) {
3564
+ if (!this.disabledSignal()) {
3525
3565
  this.isDragOver.set(true);
3526
3566
  console.log('🔄 [FileInput] Drag enter detected');
3527
3567
  }
@@ -3530,7 +3570,7 @@ class CideEleFileInputComponent {
3530
3570
  event.preventDefault();
3531
3571
  event.stopPropagation();
3532
3572
  this.isDragOver.set(false);
3533
- if (this.disabled()) {
3573
+ if (this.disabledSignal()) {
3534
3574
  console.log('⏸️ [FileInput] Drop ignored - component is disabled');
3535
3575
  return;
3536
3576
  }
@@ -3538,13 +3578,13 @@ class CideEleFileInputComponent {
3538
3578
  if (files && files.length > 0) {
3539
3579
  console.log('📁 [FileInput] Files dropped:', Array.from(files).map(f => f.name));
3540
3580
  // Validate file types if accept is specified
3541
- if (this.accept() && !this.validateFileTypes(files)) {
3581
+ if (this.acceptSignal() && !this.validateFileTypes(files)) {
3542
3582
  console.log('❌ [FileInput] Invalid file types dropped');
3543
3583
  this.notificationService.error('❌ Invalid file type. Please select files of the correct type.', { duration: 0 });
3544
3584
  return;
3545
3585
  }
3546
3586
  // Handle single vs multiple files
3547
- if (!this.multiple() && files.length > 1) {
3587
+ if (!this.multipleSignal() && files.length > 1) {
3548
3588
  console.log('⚠️ [FileInput] Multiple files dropped but multiple is disabled');
3549
3589
  this.notificationService.warning('⚠️ Only one file is allowed. Using the first file.', { duration: 0 });
3550
3590
  // Create a new FileList with only the first file
@@ -3558,7 +3598,7 @@ class CideEleFileInputComponent {
3558
3598
  }
3559
3599
  }
3560
3600
  validateFileTypes(files) {
3561
- const acceptTypes = this.accept().split(',').map(type => type.trim());
3601
+ const acceptTypes = this.acceptSignal().split(',').map(type => type.trim());
3562
3602
  if (acceptTypes.length === 0 || acceptTypes[0] === '')
3563
3603
  return true;
3564
3604
  return Array.from(files).every(file => {
@@ -3587,7 +3627,7 @@ class CideEleFileInputComponent {
3587
3627
  this.fileChange.emit(files);
3588
3628
  this.onTouched();
3589
3629
  // Auto upload if enabled
3590
- if (this.autoUpload() && files.length > 0) {
3630
+ if (this.autoUploadSignal() && files.length > 0) {
3591
3631
  console.log('🚀 [FileInput] Auto upload enabled, starting upload for:', files[0].name);
3592
3632
  this.uploadFile(files[0]);
3593
3633
  }
@@ -3596,21 +3636,21 @@ class CideEleFileInputComponent {
3596
3636
  }
3597
3637
  }
3598
3638
  isRequired() {
3599
- return this.required();
3639
+ return this.requiredSignal();
3600
3640
  }
3601
3641
  /**
3602
3642
  * Angular 20: Utility method to get upload data with proper typing
3603
3643
  * @returns Properly typed upload data
3604
3644
  */
3605
3645
  getUploadData() {
3606
- return this.uploadData();
3646
+ return this.uploadDataSignal();
3607
3647
  }
3608
3648
  /**
3609
3649
  * Angular 20: Utility method to update upload data with type safety
3610
3650
  * @param data Partial upload data to merge with existing data
3611
3651
  */
3612
3652
  updateUploadData(data) {
3613
- const currentData = this.uploadData();
3653
+ const currentData = this.uploadDataSignal();
3614
3654
  const updatedData = { ...currentData, ...data };
3615
3655
  // Note: This would require the uploadData to be a writable signal
3616
3656
  // For now, this method serves as a type-safe way to work with upload data
@@ -3619,28 +3659,28 @@ class CideEleFileInputComponent {
3619
3659
  getCurrentState() {
3620
3660
  return {
3621
3661
  id: this.id(),
3622
- label: this.label(),
3623
- required: this.required(),
3624
- disabled: this.disabled(),
3625
- accept: this.accept(),
3626
- multiple: this.multiple(),
3627
- showPreview: this.showPreview(),
3628
- autoUpload: this.autoUpload(),
3662
+ label: this.labelSignal(),
3663
+ required: this.requiredSignal(),
3664
+ disabled: this.disabledSignal(),
3665
+ accept: this.acceptSignal(),
3666
+ multiple: this.multipleSignal(),
3667
+ showPreview: this.showPreviewSignal(),
3668
+ autoUpload: this.autoUploadSignal(),
3629
3669
  uploadStatus: this.uploadStatus(),
3630
3670
  isUploading: this.isUploading(),
3631
3671
  uploadProgress: this.uploadProgress(),
3632
3672
  files: this.files() ? Array.from(this.files()).map(f => ({ name: f.name, size: f.size, type: f.type })) : null,
3633
3673
  fileNames: this.fileNames(),
3634
3674
  previewUrls: this.previewUrls().length,
3635
- helperText: this.helperText(),
3636
- errorText: this.errorText(),
3637
- placeholderText: this.placeholderText(),
3638
- placeholderIcon: this.placeholderIcon(),
3639
- previewWidth: this.previewWidth(),
3640
- previewHeight: this.previewHeight(),
3641
- previewBoxMode: this.previewBoxMode(),
3642
- showFileName: this.showFileName(),
3643
- uploadData: this.uploadData()
3675
+ helperText: this.helperTextSignal(),
3676
+ errorText: this.errorTextSignal(),
3677
+ placeholderText: this.placeholderTextSignal(),
3678
+ placeholderIcon: this.placeholderIconSignal(),
3679
+ previewWidth: this.previewWidthSignal(),
3680
+ previewHeight: this.previewHeightSignal(),
3681
+ previewBoxMode: this.previewBoxModeSignal(),
3682
+ showFileName: this.showFileNameSignal(),
3683
+ uploadData: this.uploadDataSignal()
3644
3684
  };
3645
3685
  }
3646
3686
  async getControlData() {
@@ -3662,14 +3702,14 @@ class CideEleFileInputComponent {
3662
3702
  }
3663
3703
  // Validator implementation
3664
3704
  validate(control) {
3665
- console.log('🔍 [FileInput] validate() called - uploadStatus:', this.uploadStatus(), 'required:', this.required(), 'files:', !!this.files(), 'control.value:', control.value);
3705
+ console.log('🔍 [FileInput] validate() called - uploadStatus:', this.uploadStatus(), 'required:', this.requiredSignal(), 'files:', !!this.files(), 'control.value:', control.value);
3666
3706
  // If upload is in progress (start or uploading status), return validation error
3667
3707
  if (this.uploadStatus() === 'start' || this.uploadStatus() === 'uploading') {
3668
3708
  console.log('⚠️ [FileInput] Validation ERROR: Upload in progress');
3669
3709
  return { 'uploadInProgress': { message: 'File upload in progress. Please wait...' } };
3670
3710
  }
3671
3711
  // If required and no file is selected and no control value (uploaded file ID), return validation error
3672
- if (this.required() && !this.files() && !control.value) {
3712
+ if (this.requiredSignal() && !this.files() && !control.value) {
3673
3713
  console.log('⚠️ [FileInput] Validation ERROR: File required');
3674
3714
  return { 'required': { message: 'Please select a file to upload.' } };
3675
3715
  }
@@ -3677,7 +3717,7 @@ class CideEleFileInputComponent {
3677
3717
  return null; // No validation errors
3678
3718
  }
3679
3719
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFileInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3680
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.7", type: CideEleFileInputComponent, isStandalone: true, selector: "cide-ele-file-input", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, helperText: { classPropertyName: "helperText", publicName: "helperText", isSignal: true, isRequired: false, transformFunction: null }, errorText: { classPropertyName: "errorText", publicName: "errorText", isSignal: true, isRequired: false, transformFunction: null }, showPreview: { classPropertyName: "showPreview", publicName: "showPreview", isSignal: true, isRequired: false, transformFunction: null }, previewWidth: { classPropertyName: "previewWidth", publicName: "previewWidth", isSignal: true, isRequired: false, transformFunction: null }, previewHeight: { classPropertyName: "previewHeight", publicName: "previewHeight", isSignal: true, isRequired: false, transformFunction: null }, previewBoxMode: { classPropertyName: "previewBoxMode", publicName: "previewBoxMode", isSignal: true, isRequired: false, transformFunction: null }, showFileName: { classPropertyName: "showFileName", publicName: "showFileName", isSignal: true, isRequired: false, transformFunction: null }, placeholderText: { classPropertyName: "placeholderText", publicName: "placeholderText", isSignal: true, isRequired: false, transformFunction: null }, placeholderIcon: { classPropertyName: "placeholderIcon", publicName: "placeholderIcon", isSignal: true, isRequired: false, transformFunction: null }, autoUpload: { classPropertyName: "autoUpload", publicName: "autoUpload", isSignal: true, isRequired: false, transformFunction: null }, uploadData: { classPropertyName: "uploadData", publicName: "uploadData", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { fileChange: "fileChange", uploadSuccess: "uploadSuccess", uploadError: "uploadError", uploadProgressChange: "uploadProgressChange" }, providers: [
3720
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.7", type: CideEleFileInputComponent, isStandalone: true, selector: "cide-ele-file-input", inputs: { label: "label", accept: "accept", multiple: "multiple", disabled: "disabled", required: "required", helperText: "helperText", errorText: "errorText", showPreview: "showPreview", previewWidth: "previewWidth", previewHeight: "previewHeight", previewBoxMode: "previewBoxMode", showFileName: "showFileName", placeholderText: "placeholderText", placeholderIcon: "placeholderIcon", autoUpload: "autoUpload", uploadData: "uploadData" }, outputs: { fileChange: "fileChange", uploadSuccess: "uploadSuccess", uploadError: "uploadError", uploadProgressChange: "uploadProgressChange" }, providers: [
3681
3721
  {
3682
3722
  provide: NG_VALUE_ACCESSOR,
3683
3723
  useExisting: CideEleFileInputComponent,
@@ -3688,7 +3728,7 @@ class CideEleFileInputComponent {
3688
3728
  useExisting: CideEleFileInputComponent,
3689
3729
  multi: true
3690
3730
  }
3691
- ], 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 (label() && !isPreviewBoxMode()) {\n <label class=\"cide-file-input-label\" [attr.for]=\"'cide-file-input-' + id()\">\n {{ label() }}@if (required()) {<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]=\"accept()\"\n [attr.multiple]=\"multiple() ? true : null\"\n [disabled]=\"disabled()\"\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]=\"disabled()\"\n [class.cide-file-input-preview-box-has-image]=\"hasImages()\"\n [class.cide-file-input-preview-box-drag-over]=\"isDragOver()\"\n [style.width]=\"previewWidth()\"\n [style.height]=\"previewHeight()\"\n (click)=\"triggerFileSelect()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n [attr.title]=\"disabled() ? 'File selection disabled' : placeholderText()\">\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' : placeholderIcon() }}</cide-ele-icon>\n </div>\n <div class=\"cide-file-input-preview-box-text\">\n {{ isDragOver() ? 'Drop files here...' : placeholderText() }}\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 (!disabled()) {\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 && showFileName()) {\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 <div \n class=\"cide-file-input-wrapper\"\n [class.cide-file-input-drag-over]=\"isDragOver()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"accept()\"\n [attr.multiple]=\"multiple() ? true : null\"\n [disabled]=\"disabled()\"\n (change)=\"onFileSelected($event)\"\n class=\"cide-file-input-element\"\n />\n @if (hasFiles()) {\n <button type=\"button\" class=\"cide-file-input-clear\" (click)=\"clearFiles()\">\n Clear\n </button>\n }\n </div>\n @if (hasFiles() && !isPreviewBoxMode()) {\n <div class=\"cide-file-input-files\">\n @for (name of fileNames(); track name) {\n <span>{{ name }}</span>\n }\n <!-- Angular 20: Display file size using new computed values -->\n @if (totalFileSize() > 0) {\n <div class=\"cide-file-input-size\">\n Total size: {{ fileSizeInMB() }} MB\n </div>\n }\n </div>\n }\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]=\"previewWidth()\"\n [style.height]=\"previewHeight()\">\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 @if (errorText()) {\n <div class=\"cide-file-input-error\">{{ errorText() }}</div>\n }\n @if (helperText() && !errorText()) {\n <div class=\"cide-file-input-helper\">{{ helperText() }}</div>\n }\n</div> ", styles: [".cide-file-input{display:flex;flex-direction:column;gap:.5rem}.cide-file-input-label{font-weight:500;margin-bottom:.25rem}.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-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}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }] });
3731
+ ], 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 <div \n class=\"cide-file-input-wrapper\"\n [class.cide-file-input-drag-over]=\"isDragOver()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\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-element\"\n />\n @if (hasFiles()) {\n <button type=\"button\" class=\"cide-file-input-clear\" (click)=\"clearFiles()\">\n Clear\n </button>\n }\n </div>\n @if (hasFiles() && !isPreviewBoxMode()) {\n <div class=\"cide-file-input-files\">\n @for (name of fileNames(); track name) {\n <span>{{ name }}</span>\n }\n <!-- Angular 20: Display file size using new computed values -->\n @if (totalFileSize() > 0) {\n <div class=\"cide-file-input-size\">\n Total size: {{ fileSizeInMB() }} MB\n </div>\n }\n </div>\n }\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 @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-label{font-weight:500;margin-bottom:.25rem}.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-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}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }] });
3692
3732
  }
3693
3733
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFileInputComponent, decorators: [{
3694
3734
  type: Component,
@@ -3703,8 +3743,48 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
3703
3743
  useExisting: CideEleFileInputComponent,
3704
3744
  multi: true
3705
3745
  }
3706
- ], 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 (label() && !isPreviewBoxMode()) {\n <label class=\"cide-file-input-label\" [attr.for]=\"'cide-file-input-' + id()\">\n {{ label() }}@if (required()) {<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]=\"accept()\"\n [attr.multiple]=\"multiple() ? true : null\"\n [disabled]=\"disabled()\"\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]=\"disabled()\"\n [class.cide-file-input-preview-box-has-image]=\"hasImages()\"\n [class.cide-file-input-preview-box-drag-over]=\"isDragOver()\"\n [style.width]=\"previewWidth()\"\n [style.height]=\"previewHeight()\"\n (click)=\"triggerFileSelect()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n [attr.title]=\"disabled() ? 'File selection disabled' : placeholderText()\">\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' : placeholderIcon() }}</cide-ele-icon>\n </div>\n <div class=\"cide-file-input-preview-box-text\">\n {{ isDragOver() ? 'Drop files here...' : placeholderText() }}\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 (!disabled()) {\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 && showFileName()) {\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 <div \n class=\"cide-file-input-wrapper\"\n [class.cide-file-input-drag-over]=\"isDragOver()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"accept()\"\n [attr.multiple]=\"multiple() ? true : null\"\n [disabled]=\"disabled()\"\n (change)=\"onFileSelected($event)\"\n class=\"cide-file-input-element\"\n />\n @if (hasFiles()) {\n <button type=\"button\" class=\"cide-file-input-clear\" (click)=\"clearFiles()\">\n Clear\n </button>\n }\n </div>\n @if (hasFiles() && !isPreviewBoxMode()) {\n <div class=\"cide-file-input-files\">\n @for (name of fileNames(); track name) {\n <span>{{ name }}</span>\n }\n <!-- Angular 20: Display file size using new computed values -->\n @if (totalFileSize() > 0) {\n <div class=\"cide-file-input-size\">\n Total size: {{ fileSizeInMB() }} MB\n </div>\n }\n </div>\n }\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]=\"previewWidth()\"\n [style.height]=\"previewHeight()\">\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 @if (errorText()) {\n <div class=\"cide-file-input-error\">{{ errorText() }}</div>\n }\n @if (helperText() && !errorText()) {\n <div class=\"cide-file-input-helper\">{{ helperText() }}</div>\n }\n</div> ", styles: [".cide-file-input{display:flex;flex-direction:column;gap:.5rem}.cide-file-input-label{font-weight:500;margin-bottom:.25rem}.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-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}}\n"] }]
3707
- }], ctorParameters: () => [] });
3746
+ ], 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 <div \n class=\"cide-file-input-wrapper\"\n [class.cide-file-input-drag-over]=\"isDragOver()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\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-element\"\n />\n @if (hasFiles()) {\n <button type=\"button\" class=\"cide-file-input-clear\" (click)=\"clearFiles()\">\n Clear\n </button>\n }\n </div>\n @if (hasFiles() && !isPreviewBoxMode()) {\n <div class=\"cide-file-input-files\">\n @for (name of fileNames(); track name) {\n <span>{{ name }}</span>\n }\n <!-- Angular 20: Display file size using new computed values -->\n @if (totalFileSize() > 0) {\n <div class=\"cide-file-input-size\">\n Total size: {{ fileSizeInMB() }} MB\n </div>\n }\n </div>\n }\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 @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-label{font-weight:500;margin-bottom:.25rem}.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-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}}\n"] }]
3747
+ }], ctorParameters: () => [], propDecorators: { label: [{
3748
+ type: Input
3749
+ }], accept: [{
3750
+ type: Input
3751
+ }], multiple: [{
3752
+ type: Input
3753
+ }], disabled: [{
3754
+ type: Input
3755
+ }], required: [{
3756
+ type: Input
3757
+ }], helperText: [{
3758
+ type: Input
3759
+ }], errorText: [{
3760
+ type: Input
3761
+ }], showPreview: [{
3762
+ type: Input
3763
+ }], previewWidth: [{
3764
+ type: Input
3765
+ }], previewHeight: [{
3766
+ type: Input
3767
+ }], previewBoxMode: [{
3768
+ type: Input
3769
+ }], showFileName: [{
3770
+ type: Input
3771
+ }], placeholderText: [{
3772
+ type: Input
3773
+ }], placeholderIcon: [{
3774
+ type: Input
3775
+ }], autoUpload: [{
3776
+ type: Input
3777
+ }], uploadData: [{
3778
+ type: Input
3779
+ }], fileChange: [{
3780
+ type: Output
3781
+ }], uploadSuccess: [{
3782
+ type: Output
3783
+ }], uploadError: [{
3784
+ type: Output
3785
+ }], uploadProgressChange: [{
3786
+ type: Output
3787
+ }] } });
3708
3788
 
3709
3789
  class CideTextareaComponent {
3710
3790
  label = '';
@@ -4380,6 +4460,101 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
4380
4460
  args: ['click']
4381
4461
  }] } });
4382
4462
 
4463
+ /**
4464
+ * Directive to display images from file manager by ID
4465
+ * Usage: <img cideEleFileImage [fileId]="yourFileId" [altText]="'Image'" class="your-css-classes" />
4466
+ */
4467
+ class CideEleFileImageDirective {
4468
+ fileId = null;
4469
+ altText = 'Image';
4470
+ fileManagerService = inject(CideEleFileManagerService);
4471
+ elementRef = inject(ElementRef);
4472
+ destroyRef = inject(DestroyRef);
4473
+ ngOnInit() {
4474
+ this.loadImage();
4475
+ }
4476
+ ngOnDestroy() {
4477
+ // Cleanup handled by takeUntilDestroyed
4478
+ }
4479
+ loadImage() {
4480
+ if (!this.fileId) {
4481
+ return;
4482
+ }
4483
+ console.log('🖼️ [FileImageDirective] Loading image for ID:', this.fileId);
4484
+ this.fileManagerService.getFileDetails({ cyfm_id: this.fileId }).pipe(takeUntilDestroyed(this.destroyRef)).subscribe({
4485
+ next: (fileDetails) => {
4486
+ console.log('📋 [FileImageDirective] File details received:', fileDetails);
4487
+ if (fileDetails?.data?.length) {
4488
+ const fileData = fileDetails.data[0];
4489
+ this.displayImage(fileData);
4490
+ }
4491
+ else {
4492
+ console.warn('⚠️ [FileImageDirective] No file data found for ID:', this.fileId);
4493
+ }
4494
+ },
4495
+ error: (error) => {
4496
+ console.error('❌ [FileImageDirective] Error loading file details:', error);
4497
+ }
4498
+ });
4499
+ }
4500
+ displayImage(fileData) {
4501
+ const imgElement = this.elementRef.nativeElement;
4502
+ // Check if it's an image file
4503
+ const isImage = this.isImageFile(fileData.cyfm_name || '', fileData.cyfm_type || '');
4504
+ if (isImage) {
4505
+ // For now, we'll use a placeholder or the file path
4506
+ // In a real implementation, you might need to fetch the actual file content
4507
+ if (fileData.cyfm_path) {
4508
+ // If you have a direct URL to the file, use it
4509
+ imgElement.src = fileData.cyfm_path;
4510
+ imgElement.alt = fileData.cyfm_alt_text || this.altText;
4511
+ console.log('✅ [FileImageDirective] Image loaded from path:', fileData.cyfm_path);
4512
+ }
4513
+ else if (fileData.cyfm_file_base64) {
4514
+ // If base64 data is available, use it
4515
+ let base64Data = fileData.cyfm_file_base64;
4516
+ if (!base64Data.startsWith('data:')) {
4517
+ const mimeType = fileData.cyfm_type || 'image/jpeg';
4518
+ base64Data = `data:${mimeType};base64,${base64Data}`;
4519
+ }
4520
+ imgElement.src = base64Data;
4521
+ imgElement.alt = fileData.cyfm_alt_text || this.altText;
4522
+ console.log('✅ [FileImageDirective] Image loaded from base64 data');
4523
+ }
4524
+ else {
4525
+ console.warn('⚠️ [FileImageDirective] No image data available for file:', fileData.cyfm_name);
4526
+ }
4527
+ }
4528
+ else {
4529
+ console.warn('⚠️ [FileImageDirective] File is not an image:', fileData.cyfm_name);
4530
+ }
4531
+ }
4532
+ isImageFile(fileName, fileType) {
4533
+ if (fileType && fileType.startsWith('image/')) {
4534
+ return true;
4535
+ }
4536
+ if (fileName) {
4537
+ const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.svg'];
4538
+ const lowerFileName = fileName.toLowerCase();
4539
+ return imageExtensions.some(ext => lowerFileName.endsWith(ext));
4540
+ }
4541
+ return false;
4542
+ }
4543
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFileImageDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
4544
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.1.7", type: CideEleFileImageDirective, isStandalone: true, selector: "[cideEleFileImage]", inputs: { fileId: "fileId", altText: "altText" }, ngImport: i0 });
4545
+ }
4546
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFileImageDirective, decorators: [{
4547
+ type: Directive,
4548
+ args: [{
4549
+ selector: '[cideEleFileImage]',
4550
+ standalone: true
4551
+ }]
4552
+ }], propDecorators: { fileId: [{
4553
+ type: Input
4554
+ }], altText: [{
4555
+ type: Input
4556
+ }] } });
4557
+
4383
4558
  class CideEleSkeletonLoaderComponent {
4384
4559
  width = '100%';
4385
4560
  height = '1rem';
@@ -6569,6 +6744,223 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
6569
6744
  }]
6570
6745
  }], ctorParameters: () => [] });
6571
6746
 
6747
+ class CideCoreFileManagerService {
6748
+ http;
6749
+ apiUrl = `${coreRoutesUrl?.fileManager}`;
6750
+ fileListSubject = new BehaviorSubject([]);
6751
+ fileList$ = this.fileListSubject.asObservable();
6752
+ constructor(http) {
6753
+ this.http = http;
6754
+ console.log('CideCoreFileManagerService initialized - using real API');
6755
+ }
6756
+ /**
6757
+ * Get file list from API
6758
+ */
6759
+ getFileList(body) {
6760
+ const query = generateStringFromObject(body);
6761
+ return this.http?.get(cidePath?.join([hostManagerRoutesUrl?.cideSuiteHost, coreRoutesUrl?.module, coreRoutesUrl?.fileManager, query]))
6762
+ .pipe(tap((response) => {
6763
+ if (response?.success) {
6764
+ this.fileListSubject.next(response?.data || []);
6765
+ }
6766
+ }), catchError$1(error => {
6767
+ console.error('CideCoreFileManagerService API error:', error);
6768
+ return this.handleError(error);
6769
+ }));
6770
+ }
6771
+ /**
6772
+ * Get file list using mock data (deprecated - use getFileList instead)
6773
+ */
6774
+ getFileListWithMockData(_body) {
6775
+ console.warn('getFileListWithMockData is deprecated. Use getFileList instead.');
6776
+ return this.getFileList(_body);
6777
+ }
6778
+ /**
6779
+ * Get file list from cache (if available)
6780
+ */
6781
+ getFileListFromCache() {
6782
+ return this.fileListSubject.value;
6783
+ }
6784
+ /**
6785
+ * Upload file with progress tracking
6786
+ */
6787
+ uploadFile(request) {
6788
+ const formData = new FormData();
6789
+ formData.append('file', request.file);
6790
+ formData.append('altText', request.altText || '');
6791
+ formData.append('tags', JSON.stringify(request.tags || []));
6792
+ formData.append('permissions', JSON.stringify(request.permissions || []));
6793
+ formData.append('userId', request.userId);
6794
+ const url = cidePath?.join([hostManagerRoutesUrl?.cideSuiteHost, coreRoutesUrl?.module, coreRoutesUrl?.fileManager, 'upload']);
6795
+ const req = new HttpRequest('POST', url, formData, {
6796
+ reportProgress: true
6797
+ });
6798
+ return this.http.request(req).pipe(catchError$1(this.handleError));
6799
+ }
6800
+ /**
6801
+ * Upload file with progress tracking (mock version - deprecated)
6802
+ */
6803
+ uploadFileWithMockData(request) {
6804
+ console.warn('uploadFileWithMockData is deprecated. Use uploadFile instead.');
6805
+ return this.uploadFile(request);
6806
+ }
6807
+ /**
6808
+ * Update file metadata
6809
+ */
6810
+ updateFile(request) {
6811
+ console.log('Updating file:', request);
6812
+ const url = cidePath?.join([hostManagerRoutesUrl?.cideSuiteHost, coreRoutesUrl?.module, coreRoutesUrl?.fileManager]);
6813
+ return this.http.post(url, request)
6814
+ .pipe(tap((response) => {
6815
+ if (response.success) {
6816
+ this.refreshFileList();
6817
+ }
6818
+ }), catchError$1(this.handleError));
6819
+ }
6820
+ /**
6821
+ * Delete file
6822
+ */
6823
+ deleteFile(id) {
6824
+ const payload = { cyfm_id: id };
6825
+ const query = generateStringFromObject(payload);
6826
+ const url = cidePath?.join([hostManagerRoutesUrl?.cideSuiteHost, coreRoutesUrl?.module, coreRoutesUrl?.fileManager, query]);
6827
+ return this.http.delete(url)
6828
+ .pipe(tap((response) => {
6829
+ if (response.success) {
6830
+ this.refreshFileList();
6831
+ }
6832
+ }), catchError$1(this.handleError));
6833
+ }
6834
+ /**
6835
+ * Delete multiple files
6836
+ */
6837
+ deleteMultipleFiles(ids) {
6838
+ console.log('Deleting multiple files:', ids);
6839
+ const payload = { ids };
6840
+ const query = generateStringFromObject(payload);
6841
+ const url = cidePath?.join([hostManagerRoutesUrl?.cideSuiteHost, coreRoutesUrl?.module, coreRoutesUrl?.fileManager, query]);
6842
+ return this.http.delete(url)
6843
+ .pipe(tap((response) => {
6844
+ if (response.success) {
6845
+ this.refreshFileList();
6846
+ }
6847
+ }), catchError$1(this.handleError));
6848
+ }
6849
+ /**
6850
+ * Toggle file active status
6851
+ */
6852
+ toggleFileStatus(id) {
6853
+ console.log('Toggling file status:', id);
6854
+ const payload = { id };
6855
+ const query = generateStringFromObject(payload);
6856
+ const url = cidePath?.join([hostManagerRoutesUrl?.cideSuiteHost, coreRoutesUrl?.module, coreRoutesUrl?.fileManager, query]);
6857
+ return this.http.put(url, {})
6858
+ .pipe(tap((response) => {
6859
+ if (response.success) {
6860
+ this.refreshFileList();
6861
+ }
6862
+ }), catchError$1(this.handleError));
6863
+ }
6864
+ /**
6865
+ * Get file by ID
6866
+ */
6867
+ getFileById(id) {
6868
+ console.log('Getting file by ID:', id);
6869
+ const payload = { cyfm_id: id };
6870
+ const query = generateStringFromObject(payload);
6871
+ const url = cidePath?.join([hostManagerRoutesUrl?.cideSuiteHost, coreRoutesUrl?.module, coreRoutesUrl?.fileManager, 'byId', query]);
6872
+ return this.http.get(url)
6873
+ .pipe(catchError$1(this.handleError));
6874
+ }
6875
+ /**
6876
+ * Find file by ID
6877
+ */
6878
+ findFileById(id, items = this.fileListSubject.value) {
6879
+ for (const item of items) {
6880
+ if (item._id === id) {
6881
+ return item;
6882
+ }
6883
+ }
6884
+ return null;
6885
+ }
6886
+ /**
6887
+ * Get file size in human readable format
6888
+ */
6889
+ getFileSizeDisplay(bytes) {
6890
+ if (bytes === 0)
6891
+ return '0 Bytes';
6892
+ const k = 1024;
6893
+ const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
6894
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
6895
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
6896
+ }
6897
+ /**
6898
+ * Get file type icon
6899
+ */
6900
+ getFileTypeIcon(fileType) {
6901
+ const typeMap = {
6902
+ 'image/jpeg': 'image',
6903
+ 'image/png': 'image',
6904
+ 'image/gif': 'image',
6905
+ 'image/svg+xml': 'image',
6906
+ 'application/pdf': 'picture_as_pdf',
6907
+ 'text/plain': 'description',
6908
+ 'text/html': 'code',
6909
+ 'application/json': 'code',
6910
+ 'application/javascript': 'code',
6911
+ 'text/css': 'code',
6912
+ 'application/zip': 'folder_zip',
6913
+ 'application/x-zip-compressed': 'folder_zip',
6914
+ 'application/msword': 'description',
6915
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'description',
6916
+ 'application/vnd.ms-excel': 'table_chart',
6917
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'table_chart',
6918
+ 'application/vnd.ms-powerpoint': 'slideshow',
6919
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'slideshow'
6920
+ };
6921
+ return typeMap[fileType] || 'insert_drive_file';
6922
+ }
6923
+ /**
6924
+ * Refresh file list from server
6925
+ */
6926
+ refreshFileList() {
6927
+ const defaultBody = {
6928
+ pageIndex: 1,
6929
+ pageSize: 10
6930
+ };
6931
+ this.getFileList(defaultBody).subscribe({
6932
+ next: () => {
6933
+ console.log('File list refreshed successfully');
6934
+ },
6935
+ error: (error) => {
6936
+ console.error('Error refreshing file list:', error);
6937
+ }
6938
+ });
6939
+ }
6940
+ /**
6941
+ * Handle errors
6942
+ */
6943
+ handleError(error) {
6944
+ let errorMessage = 'An error occurred';
6945
+ if (error instanceof Error) {
6946
+ errorMessage = error.message;
6947
+ }
6948
+ else if (typeof error === 'string') {
6949
+ errorMessage = error;
6950
+ }
6951
+ console.error('CideCoreFileManagerService Error:', errorMessage);
6952
+ return throwError(() => new Error(errorMessage));
6953
+ }
6954
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideCoreFileManagerService, deps: [{ token: i1$1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
6955
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideCoreFileManagerService, providedIn: 'root' });
6956
+ }
6957
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideCoreFileManagerService, decorators: [{
6958
+ type: Injectable,
6959
+ args: [{
6960
+ providedIn: 'root'
6961
+ }]
6962
+ }], ctorParameters: () => [{ type: i1$1.HttpClient }] });
6963
+
6572
6964
  class CideEleConfirmationModalComponent {
6573
6965
  // Modern Angular 20+ dependency injection
6574
6966
  confirmationService = inject(ConfirmationService);
@@ -7469,5 +7861,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
7469
7861
  * Generated bundle index. Do not edit.
7470
7862
  */
7471
7863
 
7472
- export { CideEleButtonComponent, CideEleConfirmationModalComponent, CideEleDataGridComponent, CideEleDropdownComponent, CideEleFileInputComponent, CideEleFileManagerService, CideEleGlobalNotificationsComponent, CideEleJsonEditorComponent, CideEleResizerDirective, CideEleSkeletonLoaderComponent, CideEleTabComponent, CideEleToastNotificationComponent, CideElementsService, CideIconComponent, CideInputComponent, CideSelectComponent, CideSelectOptionComponent, CideSpinnerComponent, CideTextareaComponent, ConfirmationService, DEFAULT_GRID_CONFIG, DropdownManagerService, NotificationService, TooltipDirective };
7864
+ export { CideCoreFileManagerService, CideEleButtonComponent, CideEleConfirmationModalComponent, CideEleDataGridComponent, CideEleDropdownComponent, CideEleFileImageDirective, CideEleFileInputComponent, CideEleFileManagerService, CideEleGlobalNotificationsComponent, CideEleJsonEditorComponent, CideEleResizerDirective, CideEleSkeletonLoaderComponent, CideEleTabComponent, CideEleToastNotificationComponent, CideElementsService, CideIconComponent, CideInputComponent, CideSelectComponent, CideSelectOptionComponent, CideSpinnerComponent, CideTextareaComponent, ConfirmationService, CoreFileManagerInsertUpdatePayload, DEFAULT_GRID_CONFIG, DropdownManagerService, ICoreCyfmSave, MFileManager, NotificationService, TooltipDirective };
7473
7865
  //# sourceMappingURL=cloud-ide-element.mjs.map