cloud-ide-element 1.0.66 → 1.0.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/cloud-ide-element.mjs +329 -19
- package/fesm2022/cloud-ide-element.mjs.map +1 -1
- package/index.d.ts +62 -4
- package/package.json +1 -1
|
@@ -2598,6 +2598,7 @@ class ICoreCyfmSave {
|
|
|
2598
2598
|
cyfm_tags;
|
|
2599
2599
|
cyfm_version;
|
|
2600
2600
|
cyfm_file_status_sygmt;
|
|
2601
|
+
cyfm_group_id; // Group ID for multiple file uploads
|
|
2601
2602
|
cyfm_isactive;
|
|
2602
2603
|
cyfm_file_base64 = "";
|
|
2603
2604
|
cyfm_temp_unique_id = "";
|
|
@@ -2625,12 +2626,14 @@ class CideEleFileManagerService {
|
|
|
2625
2626
|
destroyRef = inject(DestroyRef);
|
|
2626
2627
|
// Angular 20: Signal-based state management
|
|
2627
2628
|
_baseUrl = signal('/api', ...(ngDevMode ? [{ debugName: "_baseUrl" }] : []));
|
|
2629
|
+
_userId = signal('', ...(ngDevMode ? [{ debugName: "_userId" }] : []));
|
|
2628
2630
|
_isUploading = signal(false, ...(ngDevMode ? [{ debugName: "_isUploading" }] : []));
|
|
2629
2631
|
_uploadQueue = signal([], ...(ngDevMode ? [{ debugName: "_uploadQueue" }] : []));
|
|
2630
2632
|
_activeUploads = signal(new Map(), ...(ngDevMode ? [{ debugName: "_activeUploads" }] : []));
|
|
2631
2633
|
_error = signal(null, ...(ngDevMode ? [{ debugName: "_error" }] : []));
|
|
2632
2634
|
// Angular 20: Computed values
|
|
2633
2635
|
baseUrl = this._baseUrl.asReadonly();
|
|
2636
|
+
userId = this._userId.asReadonly();
|
|
2634
2637
|
isUploading = this._isUploading.asReadonly();
|
|
2635
2638
|
uploadQueue = this._uploadQueue.asReadonly();
|
|
2636
2639
|
activeUploads = this._activeUploads.asReadonly();
|
|
@@ -2699,11 +2702,12 @@ class CideEleFileManagerService {
|
|
|
2699
2702
|
cyfm_size_in_byte: file.size,
|
|
2700
2703
|
cyfm_type: file.type,
|
|
2701
2704
|
cyfm_creation_dt: new Date().toISOString(),
|
|
2702
|
-
cyfm_id_user: uploadOptions?.userId || '',
|
|
2705
|
+
cyfm_id_user: uploadOptions?.userId || this._userId() || '',
|
|
2703
2706
|
cyfm_permissions: [], // Match the interface type
|
|
2704
2707
|
cyfm_tags: uploadOptions?.tags || [],
|
|
2705
2708
|
cyfm_version: 1,
|
|
2706
2709
|
cyfm_file_status_sygmt: uploadOptions?.fileStatus || 'file_manager_file_status_active',
|
|
2710
|
+
cyfm_group_id: uploadOptions?.groupId, // Group ID for multiple file uploads
|
|
2707
2711
|
cyfm_isactive: true,
|
|
2708
2712
|
cyfm_file_base64: base64Data,
|
|
2709
2713
|
cyfm_temp_unique_id: fileId
|
|
@@ -2796,6 +2800,29 @@ class CideEleFileManagerService {
|
|
|
2796
2800
|
console.log('🔍 [FileManagerService] Setting base URL:', url);
|
|
2797
2801
|
this._baseUrl.set(url);
|
|
2798
2802
|
}
|
|
2803
|
+
/**
|
|
2804
|
+
* Set the user ID for file uploads
|
|
2805
|
+
* Angular 20: Using signal-based state management
|
|
2806
|
+
* @param userId The user ID to associate with uploaded files
|
|
2807
|
+
*/
|
|
2808
|
+
setUserId(userId) {
|
|
2809
|
+
console.log('🔍 [FileManagerService] Setting user ID:', userId);
|
|
2810
|
+
this._userId.set(userId);
|
|
2811
|
+
}
|
|
2812
|
+
/**
|
|
2813
|
+
* Generate Object ID for group uploads
|
|
2814
|
+
* Calls the backend API to generate a unique ObjectId for grouping multiple files
|
|
2815
|
+
* @returns Observable with the generated ObjectId
|
|
2816
|
+
*/
|
|
2817
|
+
generateObjectId() {
|
|
2818
|
+
const url = `${this._baseUrl()}/utility/generateObjectId`;
|
|
2819
|
+
console.log('🆔 [FileManagerService] Generating ObjectId from:', url);
|
|
2820
|
+
return this.http.get(url).pipe(catchError((error) => {
|
|
2821
|
+
console.error('❌ [FileManagerService] Error generating ObjectId:', error);
|
|
2822
|
+
this._error.set(`Failed to generate ObjectId: ${error.message}`);
|
|
2823
|
+
return throwError(() => error);
|
|
2824
|
+
}), takeUntilDestroyed(this.destroyRef));
|
|
2825
|
+
}
|
|
2799
2826
|
/**
|
|
2800
2827
|
* Angular 20: Helper methods for state management
|
|
2801
2828
|
*/
|
|
@@ -3127,6 +3154,7 @@ class CideEleFileInputComponent {
|
|
|
3127
3154
|
placeholderIcon = '📷';
|
|
3128
3155
|
autoUpload = false;
|
|
3129
3156
|
uploadData = {};
|
|
3157
|
+
multipleFileUpload = false; // New property for multiple file upload mode
|
|
3130
3158
|
// Traditional @Output() decorators
|
|
3131
3159
|
fileChange = new EventEmitter();
|
|
3132
3160
|
uploadSuccess = new EventEmitter();
|
|
@@ -3149,6 +3177,7 @@ class CideEleFileInputComponent {
|
|
|
3149
3177
|
placeholderIconSignal = signal(this.placeholderIcon, ...(ngDevMode ? [{ debugName: "placeholderIconSignal" }] : []));
|
|
3150
3178
|
autoUploadSignal = signal(this.autoUpload, ...(ngDevMode ? [{ debugName: "autoUploadSignal" }] : []));
|
|
3151
3179
|
uploadDataSignal = signal(this.uploadData, ...(ngDevMode ? [{ debugName: "uploadDataSignal" }] : []));
|
|
3180
|
+
multipleFileUploadSignal = signal(this.multipleFileUpload, ...(ngDevMode ? [{ debugName: "multipleFileUploadSignal" }] : []));
|
|
3152
3181
|
// Reactive state with signals
|
|
3153
3182
|
id = signal(Math.random().toString(36).substring(2, 10), ...(ngDevMode ? [{ debugName: "id" }] : []));
|
|
3154
3183
|
isUploading = signal(false, ...(ngDevMode ? [{ debugName: "isUploading" }] : []));
|
|
@@ -3159,6 +3188,7 @@ class CideEleFileInputComponent {
|
|
|
3159
3188
|
previewUrls = signal([], ...(ngDevMode ? [{ debugName: "previewUrls" }] : []));
|
|
3160
3189
|
uploadNotificationId = signal(null, ...(ngDevMode ? [{ debugName: "uploadNotificationId" }] : []));
|
|
3161
3190
|
isDragOver = signal(false, ...(ngDevMode ? [{ debugName: "isDragOver" }] : []));
|
|
3191
|
+
groupId = signal(null, ...(ngDevMode ? [{ debugName: "groupId" }] : [])); // Group ID for multiple file uploads
|
|
3162
3192
|
// Computed signals for better relationships
|
|
3163
3193
|
hasFiles = computed(() => this.files() !== null && this.files().length > 0, ...(ngDevMode ? [{ debugName: "hasFiles" }] : []));
|
|
3164
3194
|
canUpload = computed(() => this.hasFiles() && !this.isUploading() && !this.disabledSignal(), ...(ngDevMode ? [{ debugName: "canUpload" }] : []));
|
|
@@ -3221,6 +3251,44 @@ class CideEleFileInputComponent {
|
|
|
3221
3251
|
this.placeholderIconSignal.set(this.placeholderIcon);
|
|
3222
3252
|
this.autoUploadSignal.set(this.autoUpload);
|
|
3223
3253
|
this.uploadDataSignal.set(this.uploadData);
|
|
3254
|
+
this.multipleFileUploadSignal.set(this.multipleFileUpload);
|
|
3255
|
+
}
|
|
3256
|
+
ngOnChanges(changes) {
|
|
3257
|
+
// Angular 20: Update signals when @Input() values change
|
|
3258
|
+
if (changes['label'])
|
|
3259
|
+
this.labelSignal.set(this.label);
|
|
3260
|
+
if (changes['accept'])
|
|
3261
|
+
this.acceptSignal.set(this.accept);
|
|
3262
|
+
if (changes['multiple'])
|
|
3263
|
+
this.multipleSignal.set(this.multiple);
|
|
3264
|
+
if (changes['disabled'])
|
|
3265
|
+
this.disabledSignal.set(this.disabled);
|
|
3266
|
+
if (changes['required'])
|
|
3267
|
+
this.requiredSignal.set(this.required);
|
|
3268
|
+
if (changes['helperText'])
|
|
3269
|
+
this.helperTextSignal.set(this.helperText);
|
|
3270
|
+
if (changes['errorText'])
|
|
3271
|
+
this.errorTextSignal.set(this.errorText);
|
|
3272
|
+
if (changes['showPreview'])
|
|
3273
|
+
this.showPreviewSignal.set(this.showPreview);
|
|
3274
|
+
if (changes['previewWidth'])
|
|
3275
|
+
this.previewWidthSignal.set(this.previewWidth);
|
|
3276
|
+
if (changes['previewHeight'])
|
|
3277
|
+
this.previewHeightSignal.set(this.previewHeight);
|
|
3278
|
+
if (changes['previewBoxMode'])
|
|
3279
|
+
this.previewBoxModeSignal.set(this.previewBoxMode);
|
|
3280
|
+
if (changes['showFileName'])
|
|
3281
|
+
this.showFileNameSignal.set(this.showFileName);
|
|
3282
|
+
if (changes['placeholderText'])
|
|
3283
|
+
this.placeholderTextSignal.set(this.placeholderText);
|
|
3284
|
+
if (changes['placeholderIcon'])
|
|
3285
|
+
this.placeholderIconSignal.set(this.placeholderIcon);
|
|
3286
|
+
if (changes['autoUpload'])
|
|
3287
|
+
this.autoUploadSignal.set(this.autoUpload);
|
|
3288
|
+
if (changes['uploadData'])
|
|
3289
|
+
this.uploadDataSignal.set(this.uploadData);
|
|
3290
|
+
if (changes['multipleFileUpload'])
|
|
3291
|
+
this.multipleFileUploadSignal.set(this.multipleFileUpload);
|
|
3224
3292
|
}
|
|
3225
3293
|
writeValue(value) {
|
|
3226
3294
|
console.log('📝 [FileInput] writeValue called with:', value);
|
|
@@ -3278,8 +3346,14 @@ class CideEleFileInputComponent {
|
|
|
3278
3346
|
this.onTouched();
|
|
3279
3347
|
// Auto upload if enabled
|
|
3280
3348
|
if (this.autoUploadSignal() && selectedFiles && selectedFiles.length > 0) {
|
|
3281
|
-
|
|
3282
|
-
|
|
3349
|
+
if (this.multipleFileUploadSignal() && this.multipleSignal()) {
|
|
3350
|
+
console.log('🚀 [FileInput] Auto upload enabled for multiple files:', selectedFiles.length);
|
|
3351
|
+
this.uploadMultipleFiles(Array.from(selectedFiles));
|
|
3352
|
+
}
|
|
3353
|
+
else {
|
|
3354
|
+
console.log('🚀 [FileInput] Auto upload enabled, starting upload for:', selectedFiles[0].name);
|
|
3355
|
+
this.uploadFile(selectedFiles[0]);
|
|
3356
|
+
}
|
|
3283
3357
|
}
|
|
3284
3358
|
else {
|
|
3285
3359
|
console.log('⏸️ [FileInput] Auto upload disabled or no files');
|
|
@@ -3410,6 +3484,136 @@ class CideEleFileInputComponent {
|
|
|
3410
3484
|
}
|
|
3411
3485
|
});
|
|
3412
3486
|
}
|
|
3487
|
+
/**
|
|
3488
|
+
* Upload multiple files with group ID support
|
|
3489
|
+
* Generates a group ID if not provided and uploads all files with the same group ID
|
|
3490
|
+
*/
|
|
3491
|
+
uploadMultipleFiles(files) {
|
|
3492
|
+
console.log('📤 [FileInput] uploadMultipleFiles called for:', files.length, 'files');
|
|
3493
|
+
// Set upload status to 'start' before starting upload
|
|
3494
|
+
this.uploadStatus.set('start');
|
|
3495
|
+
this.isUploading.set(true);
|
|
3496
|
+
this.uploadProgress.set(0);
|
|
3497
|
+
this.uploadProgressChange.emit(0);
|
|
3498
|
+
// Make form control invalid during upload
|
|
3499
|
+
this.onChange(null);
|
|
3500
|
+
// Show initial progress notification
|
|
3501
|
+
const notificationId = this.notificationService.showProgress('🔄 Preparing multiple file upload...', 0, { duration: 0 });
|
|
3502
|
+
this.uploadNotificationId.set(notificationId);
|
|
3503
|
+
// Check if we already have a group ID from uploadData
|
|
3504
|
+
const existingGroupId = this.uploadDataSignal().groupId;
|
|
3505
|
+
if (existingGroupId) {
|
|
3506
|
+
console.log('🆔 [FileInput] Using existing group ID:', existingGroupId);
|
|
3507
|
+
this.groupId.set(existingGroupId);
|
|
3508
|
+
this.startMultipleFileUpload(files, existingGroupId);
|
|
3509
|
+
}
|
|
3510
|
+
else {
|
|
3511
|
+
console.log('🆔 [FileInput] No group ID provided, generating new one...');
|
|
3512
|
+
// Generate group ID first
|
|
3513
|
+
this.fileManagerService.generateObjectId().subscribe({
|
|
3514
|
+
next: (response) => {
|
|
3515
|
+
const newGroupId = response.objectId;
|
|
3516
|
+
console.log('🆔 [FileInput] Generated new group ID:', newGroupId);
|
|
3517
|
+
this.groupId.set(newGroupId);
|
|
3518
|
+
this.startMultipleFileUpload(files, newGroupId);
|
|
3519
|
+
},
|
|
3520
|
+
error: (error) => {
|
|
3521
|
+
console.error('❌ [FileInput] Failed to generate group ID:', error);
|
|
3522
|
+
this.uploadError.emit('Failed to generate group ID');
|
|
3523
|
+
this.isUploading.set(false);
|
|
3524
|
+
this.uploadStatus.set('error');
|
|
3525
|
+
const notificationId = this.uploadNotificationId();
|
|
3526
|
+
if (notificationId) {
|
|
3527
|
+
this.notificationService.remove(notificationId);
|
|
3528
|
+
}
|
|
3529
|
+
this.notificationService.error('❌ Failed to generate group ID for multiple file upload', { duration: 0 });
|
|
3530
|
+
this.uploadNotificationId.set(null);
|
|
3531
|
+
}
|
|
3532
|
+
});
|
|
3533
|
+
}
|
|
3534
|
+
}
|
|
3535
|
+
/**
|
|
3536
|
+
* Start uploading multiple files with the provided group ID
|
|
3537
|
+
*/
|
|
3538
|
+
startMultipleFileUpload(files, groupId) {
|
|
3539
|
+
console.log('🚀 [FileInput] Starting upload for', files.length, 'files with group ID:', groupId);
|
|
3540
|
+
let completedUploads = 0;
|
|
3541
|
+
let failedUploads = 0;
|
|
3542
|
+
const totalFiles = files.length;
|
|
3543
|
+
// Update upload data with group ID
|
|
3544
|
+
const uploadDataWithGroupId = {
|
|
3545
|
+
...this.uploadDataSignal(),
|
|
3546
|
+
groupId: groupId
|
|
3547
|
+
};
|
|
3548
|
+
files.forEach((file, index) => {
|
|
3549
|
+
this.fileManagerService.uploadFile(file, uploadDataWithGroupId, (progress) => {
|
|
3550
|
+
// Calculate overall progress
|
|
3551
|
+
const fileProgress = progress / totalFiles;
|
|
3552
|
+
const overallProgress = ((completedUploads * 100) + fileProgress) / totalFiles;
|
|
3553
|
+
this.uploadProgress.set(overallProgress);
|
|
3554
|
+
this.uploadProgressChange.emit(overallProgress);
|
|
3555
|
+
// Update progress notification
|
|
3556
|
+
const notificationId = this.uploadNotificationId();
|
|
3557
|
+
if (notificationId) {
|
|
3558
|
+
const progressMessage = `🔄 Uploading file ${index + 1} of ${totalFiles}...`;
|
|
3559
|
+
this.notificationService.updateProgress(notificationId, overallProgress, progressMessage);
|
|
3560
|
+
}
|
|
3561
|
+
}).pipe(takeUntilDestroyed(this.destroyRef)).subscribe({
|
|
3562
|
+
next: (response) => {
|
|
3563
|
+
completedUploads++;
|
|
3564
|
+
console.log(`✅ [FileInput] File ${index + 1}/${totalFiles} uploaded successfully`);
|
|
3565
|
+
// Check if all files are completed
|
|
3566
|
+
if (completedUploads + failedUploads === totalFiles) {
|
|
3567
|
+
this.handleMultipleUploadComplete(completedUploads, failedUploads, totalFiles, groupId);
|
|
3568
|
+
}
|
|
3569
|
+
},
|
|
3570
|
+
error: (error) => {
|
|
3571
|
+
failedUploads++;
|
|
3572
|
+
console.error(`❌ [FileInput] File ${index + 1}/${totalFiles} upload failed:`, error);
|
|
3573
|
+
// Check if all files are completed
|
|
3574
|
+
if (completedUploads + failedUploads === totalFiles) {
|
|
3575
|
+
this.handleMultipleUploadComplete(completedUploads, failedUploads, totalFiles, groupId);
|
|
3576
|
+
}
|
|
3577
|
+
}
|
|
3578
|
+
});
|
|
3579
|
+
});
|
|
3580
|
+
}
|
|
3581
|
+
/**
|
|
3582
|
+
* Handle completion of multiple file upload
|
|
3583
|
+
*/
|
|
3584
|
+
handleMultipleUploadComplete(completed, failed, total, groupId) {
|
|
3585
|
+
console.log(`📊 [FileInput] Multiple upload complete: ${completed}/${total} successful, ${failed} failed`);
|
|
3586
|
+
this.isUploading.set(false);
|
|
3587
|
+
this.uploadProgress.set(100);
|
|
3588
|
+
this.uploadProgressChange.emit(100);
|
|
3589
|
+
// Remove progress notification
|
|
3590
|
+
const notificationId = this.uploadNotificationId();
|
|
3591
|
+
if (notificationId) {
|
|
3592
|
+
this.notificationService.remove(notificationId);
|
|
3593
|
+
}
|
|
3594
|
+
this.uploadNotificationId.set(null);
|
|
3595
|
+
if (failed === 0) {
|
|
3596
|
+
// All files uploaded successfully
|
|
3597
|
+
this.uploadStatus.set('success');
|
|
3598
|
+
this.notificationService.success(`✅ All ${total} files uploaded successfully!`, { duration: 0 });
|
|
3599
|
+
// For multiple file upload, return the group ID as the form value
|
|
3600
|
+
this.onChange(groupId);
|
|
3601
|
+
this.uploadSuccess.emit(groupId);
|
|
3602
|
+
console.log('📝 [FileInput] Form control value set to group ID:', groupId);
|
|
3603
|
+
}
|
|
3604
|
+
else if (completed > 0) {
|
|
3605
|
+
// Some files uploaded successfully
|
|
3606
|
+
this.uploadStatus.set('error');
|
|
3607
|
+
this.notificationService.warning(`⚠️ ${completed}/${total} files uploaded. ${failed} failed.`, { duration: 0 });
|
|
3608
|
+
this.uploadError.emit(`${failed} out of ${total} files failed to upload`);
|
|
3609
|
+
}
|
|
3610
|
+
else {
|
|
3611
|
+
// All files failed
|
|
3612
|
+
this.uploadStatus.set('error');
|
|
3613
|
+
this.notificationService.error(`❌ All ${total} files failed to upload.`, { duration: 0 });
|
|
3614
|
+
this.uploadError.emit('All files failed to upload');
|
|
3615
|
+
}
|
|
3616
|
+
}
|
|
3413
3617
|
generatePreviews() {
|
|
3414
3618
|
// Clear existing previews
|
|
3415
3619
|
this.clearPreviews();
|
|
@@ -3717,7 +3921,7 @@ class CideEleFileInputComponent {
|
|
|
3717
3921
|
return null; // No validation errors
|
|
3718
3922
|
}
|
|
3719
3923
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFileInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
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: [
|
|
3924
|
+
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", multipleFileUpload: "multipleFileUpload" }, outputs: { fileChange: "fileChange", uploadSuccess: "uploadSuccess", uploadError: "uploadError", uploadProgressChange: "uploadProgressChange" }, providers: [
|
|
3721
3925
|
{
|
|
3722
3926
|
provide: NG_VALUE_ACCESSOR,
|
|
3723
3927
|
useExisting: CideEleFileInputComponent,
|
|
@@ -3728,7 +3932,7 @@ class CideEleFileInputComponent {
|
|
|
3728
3932
|
useExisting: CideEleFileInputComponent,
|
|
3729
3933
|
multi: true
|
|
3730
3934
|
}
|
|
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"] }] });
|
|
3935
|
+
], 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 <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"] }] });
|
|
3732
3936
|
}
|
|
3733
3937
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFileInputComponent, decorators: [{
|
|
3734
3938
|
type: Component,
|
|
@@ -3776,6 +3980,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
3776
3980
|
type: Input
|
|
3777
3981
|
}], uploadData: [{
|
|
3778
3982
|
type: Input
|
|
3983
|
+
}], multipleFileUpload: [{
|
|
3984
|
+
type: Input
|
|
3779
3985
|
}], fileChange: [{
|
|
3780
3986
|
type: Output
|
|
3781
3987
|
}], uploadSuccess: [{
|
|
@@ -4714,8 +4920,14 @@ class CideEleDataGridComponent {
|
|
|
4714
4920
|
let processedData = data;
|
|
4715
4921
|
// Transform to tree structure if tree config is enabled
|
|
4716
4922
|
if (this.mergedConfig().tree?.enabled) {
|
|
4923
|
+
// Preserve expansion state from current data before transforming
|
|
4924
|
+
const currentData = this.internalData();
|
|
4925
|
+
const expansionState = this.preserveTreeExpansionState(currentData);
|
|
4717
4926
|
processedData = this.transformToTree(data);
|
|
4718
4927
|
console.log('processedData', processedData);
|
|
4928
|
+
// Apply preserved expansion state to new tree data
|
|
4929
|
+
processedData = this.applyTreeExpansionState(processedData, expansionState);
|
|
4930
|
+
console.log('processedData with preserved expansion state', processedData);
|
|
4719
4931
|
}
|
|
4720
4932
|
this.internalData.set(processedData);
|
|
4721
4933
|
console.log('processedData', processedData);
|
|
@@ -4747,7 +4959,12 @@ class CideEleDataGridComponent {
|
|
|
4747
4959
|
let data = this.config?.data || [];
|
|
4748
4960
|
// Transform to tree structure if tree config is enabled
|
|
4749
4961
|
if (this.mergedConfig().tree?.enabled) {
|
|
4962
|
+
// Preserve expansion state from current data before transforming
|
|
4963
|
+
const currentData = this.internalData();
|
|
4964
|
+
const expansionState = this.preserveTreeExpansionState(currentData);
|
|
4750
4965
|
data = this.transformToTree(data);
|
|
4966
|
+
// Apply preserved expansion state to new tree data
|
|
4967
|
+
data = this.applyTreeExpansionState(data, expansionState);
|
|
4751
4968
|
}
|
|
4752
4969
|
this.internalData.set(data);
|
|
4753
4970
|
// Set pagination values based on mode
|
|
@@ -4800,11 +5017,13 @@ class CideEleDataGridComponent {
|
|
|
4800
5017
|
if (this.isDragDropEnabled()) {
|
|
4801
5018
|
// Get the full dataset from the config (not just displayedData)
|
|
4802
5019
|
const fullData = this.mergedConfig().data || [];
|
|
5020
|
+
// Flatten the data to include all items (parents and children) for hierarchical structures
|
|
5021
|
+
const flattenedData = this.flattenAllDataForOrderTracking(fullData);
|
|
4803
5022
|
// Completely build the order tracking from current data
|
|
4804
5023
|
this.rowOrderMap.clear();
|
|
4805
5024
|
this.initialDataOrderIds = [];
|
|
4806
|
-
// Build the order tracking with
|
|
4807
|
-
|
|
5025
|
+
// Build the order tracking with flattened data
|
|
5026
|
+
flattenedData.forEach((item, index) => {
|
|
4808
5027
|
const itemId = this.getItemId(item);
|
|
4809
5028
|
if (itemId) {
|
|
4810
5029
|
const actualOrder = this.getItemOrder(item);
|
|
@@ -4817,7 +5036,7 @@ class CideEleDataGridComponent {
|
|
|
4817
5036
|
});
|
|
4818
5037
|
// Clear any existing local reordered data
|
|
4819
5038
|
this.localReorderedData = [];
|
|
4820
|
-
console.log('🔍 Initial order tracking built from
|
|
5039
|
+
console.log('🔍 Initial order tracking built from flattened dataset:', this.initialDataOrderIds.length, 'items (including children)');
|
|
4821
5040
|
console.log('🔍 Initial order map created with actual order values:', Array.from(this.rowOrderMap.entries()));
|
|
4822
5041
|
}
|
|
4823
5042
|
}
|
|
@@ -4828,11 +5047,13 @@ class CideEleDataGridComponent {
|
|
|
4828
5047
|
if (this.isDragDropEnabled()) {
|
|
4829
5048
|
// Get the current full dataset
|
|
4830
5049
|
const fullData = this.mergedConfig().data || [];
|
|
5050
|
+
// Flatten the data to include all items (parents and children) for hierarchical structures
|
|
5051
|
+
const flattenedData = this.flattenAllDataForOrderTracking(fullData);
|
|
4831
5052
|
// Completely rebuild the order tracking from current data
|
|
4832
5053
|
this.rowOrderMap.clear();
|
|
4833
5054
|
this.initialDataOrderIds = [];
|
|
4834
|
-
// Rebuild the order tracking with
|
|
4835
|
-
|
|
5055
|
+
// Rebuild the order tracking with flattened data
|
|
5056
|
+
flattenedData.forEach((item, index) => {
|
|
4836
5057
|
const itemId = this.getItemId(item);
|
|
4837
5058
|
if (itemId) {
|
|
4838
5059
|
const actualOrder = this.getItemOrder(item);
|
|
@@ -4878,6 +5099,59 @@ class CideEleDataGridComponent {
|
|
|
4878
5099
|
}
|
|
4879
5100
|
this.updatePaginationState();
|
|
4880
5101
|
}
|
|
5102
|
+
/**
|
|
5103
|
+
* Preserve tree expansion state from current data
|
|
5104
|
+
*/
|
|
5105
|
+
preserveTreeExpansionState(currentData) {
|
|
5106
|
+
const expansionState = new Map();
|
|
5107
|
+
const treeConfig = this.mergedConfig().tree;
|
|
5108
|
+
if (!treeConfig)
|
|
5109
|
+
return expansionState;
|
|
5110
|
+
const { expandedKey = 'isExpanded', primaryKey } = treeConfig;
|
|
5111
|
+
const extractExpansionState = (items) => {
|
|
5112
|
+
items.forEach(item => {
|
|
5113
|
+
const itemId = String(this.getNestedValue(item, primaryKey) || '');
|
|
5114
|
+
if (itemId) {
|
|
5115
|
+
const isExpanded = this.getNestedValue(item, expandedKey) || false;
|
|
5116
|
+
expansionState.set(itemId, isExpanded);
|
|
5117
|
+
}
|
|
5118
|
+
// Check for children recursively
|
|
5119
|
+
const children = this.getNestedValue(item, treeConfig.childrenKey || 'children') || [];
|
|
5120
|
+
if (children.length > 0) {
|
|
5121
|
+
extractExpansionState(children);
|
|
5122
|
+
}
|
|
5123
|
+
});
|
|
5124
|
+
};
|
|
5125
|
+
extractExpansionState(currentData);
|
|
5126
|
+
console.log('🔍 DataGrid: Preserved expansion state for', expansionState.size, 'items');
|
|
5127
|
+
return expansionState;
|
|
5128
|
+
}
|
|
5129
|
+
/**
|
|
5130
|
+
* Apply preserved tree expansion state to new data
|
|
5131
|
+
*/
|
|
5132
|
+
applyTreeExpansionState(data, expansionState) {
|
|
5133
|
+
const treeConfig = this.mergedConfig().tree;
|
|
5134
|
+
if (!treeConfig)
|
|
5135
|
+
return data;
|
|
5136
|
+
const { expandedKey = 'isExpanded', primaryKey, childrenKey = 'children' } = treeConfig;
|
|
5137
|
+
const applyToItems = (items) => {
|
|
5138
|
+
return items.map(item => {
|
|
5139
|
+
const itemId = String(this.getNestedValue(item, primaryKey) || '');
|
|
5140
|
+
const isExpanded = expansionState.get(itemId) || false;
|
|
5141
|
+
const updatedItem = {
|
|
5142
|
+
...item,
|
|
5143
|
+
[expandedKey]: isExpanded
|
|
5144
|
+
};
|
|
5145
|
+
// Apply to children recursively
|
|
5146
|
+
const children = this.getNestedValue(item, childrenKey) || [];
|
|
5147
|
+
if (children.length > 0) {
|
|
5148
|
+
this.setNestedValue(updatedItem, childrenKey, applyToItems(children));
|
|
5149
|
+
}
|
|
5150
|
+
return updatedItem;
|
|
5151
|
+
});
|
|
5152
|
+
};
|
|
5153
|
+
return applyToItems(data);
|
|
5154
|
+
}
|
|
4881
5155
|
/**
|
|
4882
5156
|
* Transform flat data to tree structure based on foreign key relationships
|
|
4883
5157
|
*/
|
|
@@ -5599,15 +5873,18 @@ class CideEleDataGridComponent {
|
|
|
5599
5873
|
}
|
|
5600
5874
|
/**
|
|
5601
5875
|
* Reset the row order map to original positions (for reset action)
|
|
5876
|
+
* Includes all items from hierarchical structure (parents and children)
|
|
5602
5877
|
*/
|
|
5603
5878
|
resetRowOrderMap() {
|
|
5604
5879
|
// Get the current full dataset from the config (not cached data)
|
|
5605
5880
|
const fullData = this.mergedConfig().data || [];
|
|
5881
|
+
// Flatten the data to include all items (parents and children) for hierarchical structures
|
|
5882
|
+
const flattenedData = this.flattenAllDataForOrderTracking(fullData);
|
|
5606
5883
|
// Completely rebuild the order tracking from current data
|
|
5607
5884
|
this.rowOrderMap.clear();
|
|
5608
5885
|
this.initialDataOrderIds = [];
|
|
5609
|
-
// Rebuild the order tracking with
|
|
5610
|
-
|
|
5886
|
+
// Rebuild the order tracking with flattened data
|
|
5887
|
+
flattenedData.forEach((item, index) => {
|
|
5611
5888
|
const itemId = this.getItemId(item);
|
|
5612
5889
|
if (itemId) {
|
|
5613
5890
|
const actualOrder = this.getItemOrder(item);
|
|
@@ -5621,19 +5898,22 @@ class CideEleDataGridComponent {
|
|
|
5621
5898
|
// Clear any local reordered data
|
|
5622
5899
|
this.localReorderedData = [];
|
|
5623
5900
|
this.hasOrderChanged.set(false);
|
|
5624
|
-
console.log('🔍 Row order map completely rebuilt from
|
|
5901
|
+
console.log('🔍 Row order map completely rebuilt from flattened data:', this.initialDataOrderIds.length, 'items (including children)');
|
|
5625
5902
|
}
|
|
5626
5903
|
/**
|
|
5627
5904
|
* Update the row order map baseline to current positions (for save action)
|
|
5905
|
+
* Includes all items from hierarchical structure (parents and children)
|
|
5628
5906
|
*/
|
|
5629
5907
|
updateRowOrderMapBaseline() {
|
|
5630
5908
|
// Get the current full dataset from the config
|
|
5631
5909
|
const fullData = this.mergedConfig().data || [];
|
|
5910
|
+
// Flatten the data to include all items (parents and children) for hierarchical structures
|
|
5911
|
+
const flattenedData = this.flattenAllDataForOrderTracking(fullData);
|
|
5632
5912
|
// Completely rebuild the order tracking from current data
|
|
5633
5913
|
this.rowOrderMap.clear();
|
|
5634
5914
|
this.initialDataOrderIds = [];
|
|
5635
|
-
// Rebuild the order tracking with
|
|
5636
|
-
|
|
5915
|
+
// Rebuild the order tracking with flattened data
|
|
5916
|
+
flattenedData.forEach((item, index) => {
|
|
5637
5917
|
const itemId = this.getItemId(item);
|
|
5638
5918
|
if (itemId) {
|
|
5639
5919
|
const actualOrder = this.getItemOrder(item);
|
|
@@ -5644,18 +5924,21 @@ class CideEleDataGridComponent {
|
|
|
5644
5924
|
});
|
|
5645
5925
|
}
|
|
5646
5926
|
});
|
|
5647
|
-
console.log('🔍 Row order map baseline completely rebuilt from
|
|
5927
|
+
console.log('🔍 Row order map baseline completely rebuilt from flattened data:', this.initialDataOrderIds.length, 'items (including children)');
|
|
5648
5928
|
}
|
|
5649
5929
|
/**
|
|
5650
5930
|
* Get the current order array from the row order map
|
|
5931
|
+
* Includes all items from hierarchical structure (parents and children)
|
|
5651
5932
|
*/
|
|
5652
5933
|
getCurrentOrderFromMap() {
|
|
5653
5934
|
const orderedItems = [];
|
|
5654
5935
|
// Get the full dataset from the config (not just displayedData)
|
|
5655
5936
|
const fullData = this.mergedConfig().data || [];
|
|
5656
|
-
//
|
|
5937
|
+
// Flatten the data to include all items (parents and children) for hierarchical structures
|
|
5938
|
+
const flattenedData = this.flattenAllDataForOrderTracking(fullData);
|
|
5939
|
+
// Collect all items with their new positions from the flattened dataset
|
|
5657
5940
|
this.rowOrderMap.forEach((orderInfo, itemId) => {
|
|
5658
|
-
const item =
|
|
5941
|
+
const item = flattenedData.find(dataItem => this.getItemId(dataItem) === itemId);
|
|
5659
5942
|
if (item) {
|
|
5660
5943
|
orderedItems.push({ item, newPosition: orderInfo.newPosition });
|
|
5661
5944
|
}
|
|
@@ -5667,14 +5950,17 @@ class CideEleDataGridComponent {
|
|
|
5667
5950
|
}
|
|
5668
5951
|
/**
|
|
5669
5952
|
* Get only the items that have actually changed order
|
|
5953
|
+
* Includes all items from hierarchical structure (parents and children)
|
|
5670
5954
|
*/
|
|
5671
5955
|
getChangedOrders() {
|
|
5672
5956
|
const changedItems = [];
|
|
5673
5957
|
// Get the full dataset from the config
|
|
5674
5958
|
const fullData = this.mergedConfig().data || [];
|
|
5959
|
+
// Flatten the data to include all items (parents and children) for hierarchical structures
|
|
5960
|
+
const flattenedData = this.flattenAllDataForOrderTracking(fullData);
|
|
5675
5961
|
// Check each item to see if its position has changed
|
|
5676
5962
|
this.rowOrderMap.forEach((orderInfo, itemId) => {
|
|
5677
|
-
const item =
|
|
5963
|
+
const item = flattenedData.find(dataItem => this.getItemId(dataItem) === itemId);
|
|
5678
5964
|
if (item && orderInfo.oldPosition !== orderInfo.newPosition) {
|
|
5679
5965
|
changedItems.push(item);
|
|
5680
5966
|
console.log('🔍 Item changed order:', itemId, 'from', orderInfo.oldPosition, 'to', orderInfo.newPosition);
|
|
@@ -5682,6 +5968,30 @@ class CideEleDataGridComponent {
|
|
|
5682
5968
|
});
|
|
5683
5969
|
return changedItems;
|
|
5684
5970
|
}
|
|
5971
|
+
/**
|
|
5972
|
+
* Flatten all data for order tracking (includes all hierarchical children)
|
|
5973
|
+
* This method ensures that all items (parents and children) are included in order tracking
|
|
5974
|
+
*/
|
|
5975
|
+
flattenAllDataForOrderTracking(data) {
|
|
5976
|
+
const flattenedItems = [];
|
|
5977
|
+
const flattenItems = (items) => {
|
|
5978
|
+
items.forEach(item => {
|
|
5979
|
+
flattenedItems.push(item);
|
|
5980
|
+
// Check if item has children (for hierarchical data)
|
|
5981
|
+
const treeConfig = this.mergedConfig().tree;
|
|
5982
|
+
if (treeConfig?.enabled) {
|
|
5983
|
+
const childrenKey = treeConfig.childrenKey || 'children';
|
|
5984
|
+
const children = this.getNestedValue(item, childrenKey) || [];
|
|
5985
|
+
if (children.length > 0) {
|
|
5986
|
+
flattenItems(children);
|
|
5987
|
+
}
|
|
5988
|
+
}
|
|
5989
|
+
});
|
|
5990
|
+
};
|
|
5991
|
+
flattenItems(data);
|
|
5992
|
+
console.log('🔍 Flattened data for order tracking:', flattenedItems.length, 'items (including children)');
|
|
5993
|
+
return flattenedItems;
|
|
5994
|
+
}
|
|
5685
5995
|
/**
|
|
5686
5996
|
* Update local data order for visual reordering (LOCAL ONLY)
|
|
5687
5997
|
*/
|