cloud-ide-element 1.0.51 → 1.0.53
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 +198 -34
- package/fesm2022/cloud-ide-element.mjs.map +1 -1
- package/index.d.ts +42 -5
- package/package.json +1 -1
|
@@ -2570,18 +2570,30 @@ class CideEleFileManagerService {
|
|
|
2570
2570
|
this.http = http;
|
|
2571
2571
|
}
|
|
2572
2572
|
/**
|
|
2573
|
-
* Upload a file with base64 encoding
|
|
2573
|
+
* Upload a file with base64 encoding and progress tracking
|
|
2574
2574
|
* @param file The file to upload
|
|
2575
2575
|
* @param additionalData Additional data to include in the payload
|
|
2576
|
+
* @param progressCallback Optional callback for progress updates
|
|
2576
2577
|
* @returns Observable with the upload response
|
|
2577
2578
|
*/
|
|
2578
|
-
uploadFile(file, additionalData) {
|
|
2579
|
-
// Convert file to base64
|
|
2579
|
+
uploadFile(file, additionalData, progressCallback) {
|
|
2580
|
+
// Convert file to base64 with progress tracking
|
|
2580
2581
|
return new Observable(observer => {
|
|
2581
2582
|
const reader = new FileReader();
|
|
2583
|
+
// Track file reading progress
|
|
2584
|
+
reader.onprogress = (event) => {
|
|
2585
|
+
if (event.lengthComputable && progressCallback) {
|
|
2586
|
+
const progress = (event.loaded / event.total) * 50; // File reading is 50% of total progress
|
|
2587
|
+
progressCallback(progress);
|
|
2588
|
+
}
|
|
2589
|
+
};
|
|
2582
2590
|
reader.onload = () => {
|
|
2583
2591
|
const base64String = reader.result;
|
|
2584
2592
|
const base64Data = base64String.split(',')[1]; // Remove data:image/jpeg;base64, prefix
|
|
2593
|
+
// Update progress to 50% (file reading complete)
|
|
2594
|
+
if (progressCallback) {
|
|
2595
|
+
progressCallback(50);
|
|
2596
|
+
}
|
|
2585
2597
|
const payload = {
|
|
2586
2598
|
cyfm_name: file.name,
|
|
2587
2599
|
cyfm_alt_text: additionalData?.altText || file.name,
|
|
@@ -2598,10 +2610,24 @@ class CideEleFileManagerService {
|
|
|
2598
2610
|
fileData: base64Data, // Base64 file data
|
|
2599
2611
|
...additionalData
|
|
2600
2612
|
};
|
|
2601
|
-
// Make the HTTP request
|
|
2602
|
-
this.http.post(`${this.baseUrl}
|
|
2603
|
-
|
|
2604
|
-
|
|
2613
|
+
// Make the HTTP request with progress tracking
|
|
2614
|
+
this.http.post(`${this.baseUrl}`, payload, {
|
|
2615
|
+
reportProgress: true,
|
|
2616
|
+
observe: 'events'
|
|
2617
|
+
}).subscribe({
|
|
2618
|
+
next: (event) => {
|
|
2619
|
+
if (event.type === 1 && progressCallback) { // HttpEventType.UploadProgress
|
|
2620
|
+
const uploadProgress = (event.loaded / event.total) * 50; // Upload is remaining 50%
|
|
2621
|
+
const totalProgress = 50 + uploadProgress; // Total progress (50% file reading + upload progress)
|
|
2622
|
+
progressCallback(totalProgress);
|
|
2623
|
+
}
|
|
2624
|
+
else if (event.type === 4) { // HttpEventType.Response
|
|
2625
|
+
if (progressCallback) {
|
|
2626
|
+
progressCallback(100);
|
|
2627
|
+
}
|
|
2628
|
+
observer.next(event.body);
|
|
2629
|
+
}
|
|
2630
|
+
},
|
|
2605
2631
|
error: (error) => observer.error(error)
|
|
2606
2632
|
});
|
|
2607
2633
|
};
|
|
@@ -2849,10 +2875,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
2849
2875
|
class CideEleFileInputComponent {
|
|
2850
2876
|
fileManagerService = inject(CideEleFileManagerService);
|
|
2851
2877
|
notificationService = inject(NotificationService);
|
|
2878
|
+
elementService = inject(CideElementsService);
|
|
2852
2879
|
label = 'Choose file';
|
|
2853
2880
|
accept = '';
|
|
2854
2881
|
multiple = false;
|
|
2855
2882
|
disabled = false;
|
|
2883
|
+
required = false;
|
|
2856
2884
|
helperText = '';
|
|
2857
2885
|
errorText = '';
|
|
2858
2886
|
showPreview = false;
|
|
@@ -2867,6 +2895,7 @@ class CideEleFileInputComponent {
|
|
|
2867
2895
|
id = Math.random().toString(36).substring(2, 10);
|
|
2868
2896
|
isUploading = false;
|
|
2869
2897
|
uploadProgress = 0;
|
|
2898
|
+
uploadStatus = 'idle';
|
|
2870
2899
|
uploadNotificationId = null;
|
|
2871
2900
|
fileChange = new EventEmitter();
|
|
2872
2901
|
uploadSuccess = new EventEmitter();
|
|
@@ -2879,7 +2908,9 @@ class CideEleFileInputComponent {
|
|
|
2879
2908
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2880
2909
|
onChange = (files) => { };
|
|
2881
2910
|
onTouched = () => { };
|
|
2911
|
+
onValidatorChange = () => { };
|
|
2882
2912
|
writeValue(files) {
|
|
2913
|
+
console.log('📝 [FileInput] writeValue called with:', files ? Array.from(files).map(f => f.name) : 'null');
|
|
2883
2914
|
this.files = files;
|
|
2884
2915
|
this.fileNames = files ? Array.from(files).map(f => f.name) : [];
|
|
2885
2916
|
this.generatePreviews();
|
|
@@ -2890,70 +2921,125 @@ class CideEleFileInputComponent {
|
|
|
2890
2921
|
registerOnTouched(fn) {
|
|
2891
2922
|
this.onTouched = fn;
|
|
2892
2923
|
}
|
|
2924
|
+
registerOnValidatorChange(fn) {
|
|
2925
|
+
this.onValidatorChange = fn;
|
|
2926
|
+
}
|
|
2893
2927
|
setDisabledState(isDisabled) {
|
|
2894
2928
|
this.disabled = isDisabled;
|
|
2895
2929
|
}
|
|
2896
2930
|
onFileSelected(event) {
|
|
2931
|
+
console.log('🔍 [FileInput] onFileSelected called');
|
|
2897
2932
|
const input = event.target;
|
|
2898
2933
|
this.files = input.files;
|
|
2899
2934
|
this.fileNames = this.files ? Array.from(this.files).map(f => f.name) : [];
|
|
2935
|
+
console.log('📁 [FileInput] Files selected:', this.fileNames);
|
|
2900
2936
|
this.generatePreviews();
|
|
2937
|
+
// Reset upload status when new file is selected
|
|
2938
|
+
this.uploadStatus = 'idle';
|
|
2939
|
+
console.log('🔄 [FileInput] Upload status reset to:', this.uploadStatus);
|
|
2901
2940
|
this.onChange(this.files);
|
|
2902
2941
|
this.fileChange.emit(this.files);
|
|
2903
2942
|
this.onTouched();
|
|
2943
|
+
this.onValidatorChange();
|
|
2904
2944
|
// Auto upload if enabled
|
|
2905
2945
|
if (this.autoUpload && this.files && this.files.length > 0) {
|
|
2946
|
+
console.log('🚀 [FileInput] Auto upload enabled, starting upload for:', this.files[0].name);
|
|
2906
2947
|
this.uploadFile(this.files[0]);
|
|
2907
2948
|
}
|
|
2949
|
+
else {
|
|
2950
|
+
console.log('⏸️ [FileInput] Auto upload disabled or no files');
|
|
2951
|
+
}
|
|
2908
2952
|
}
|
|
2909
2953
|
clearFiles() {
|
|
2954
|
+
console.log('🗑️ [FileInput] clearFiles called');
|
|
2910
2955
|
this.files = null;
|
|
2911
2956
|
this.fileNames = [];
|
|
2912
2957
|
this.clearPreviews();
|
|
2958
|
+
this.uploadStatus = 'idle';
|
|
2959
|
+
console.log('🔄 [FileInput] Upload status reset to:', this.uploadStatus);
|
|
2913
2960
|
this.onChange(null);
|
|
2914
2961
|
this.fileChange.emit(null);
|
|
2962
|
+
this.onValidatorChange();
|
|
2915
2963
|
}
|
|
2916
2964
|
uploadFile(file) {
|
|
2965
|
+
console.log('📤 [FileInput] uploadFile called for:', file.name, 'Size:', file.size, 'bytes');
|
|
2966
|
+
// Set upload status to 'start' before starting upload
|
|
2967
|
+
this.uploadStatus = 'start';
|
|
2968
|
+
console.log('🔄 [FileInput] Upload status set to:', this.uploadStatus);
|
|
2969
|
+
// Trigger validation update to show upload in progress error
|
|
2970
|
+
this.onValidatorChange();
|
|
2971
|
+
console.log('✅ [FileInput] Validation triggered - should show upload in progress error');
|
|
2917
2972
|
this.isUploading = true;
|
|
2918
2973
|
this.uploadProgress = 0;
|
|
2919
2974
|
this.uploadProgressChange.emit(0);
|
|
2920
|
-
|
|
2975
|
+
console.log('📊 [FileInput] Upload progress initialized to 0%');
|
|
2976
|
+
// Make form control invalid during upload - this prevents form submission
|
|
2921
2977
|
this.onChange(null);
|
|
2922
|
-
|
|
2923
|
-
// this.fileManagerService.setBaseUrl(this.baseUrl);
|
|
2978
|
+
console.log('🚫 [FileInput] Form control value set to null to prevent submission');
|
|
2924
2979
|
// Show initial progress notification
|
|
2925
2980
|
this.uploadNotificationId = this.notificationService.showProgress('Starting file upload...', 0);
|
|
2926
|
-
|
|
2927
|
-
this.
|
|
2928
|
-
|
|
2981
|
+
console.log('🔔 [FileInput] Progress notification started with ID:', this.uploadNotificationId);
|
|
2982
|
+
this.fileManagerService.uploadFile(file, this.uploadData, (progress) => {
|
|
2983
|
+
// Real progress callback from file manager service
|
|
2984
|
+
this.uploadProgress = progress;
|
|
2985
|
+
this.uploadProgressChange.emit(progress);
|
|
2986
|
+
console.log('📈 [FileInput] Upload progress:', Math.round(progress) + '%');
|
|
2987
|
+
// Set upload status to 'uploading' when progress starts
|
|
2988
|
+
if (this.uploadStatus === 'start') {
|
|
2989
|
+
this.uploadStatus = 'uploading';
|
|
2990
|
+
console.log('🔄 [FileInput] Upload status changed to:', this.uploadStatus);
|
|
2991
|
+
this.onValidatorChange();
|
|
2992
|
+
console.log('✅ [FileInput] Validation triggered after status change to uploading');
|
|
2993
|
+
}
|
|
2994
|
+
// Update progress notification
|
|
2995
|
+
if (this.uploadNotificationId) {
|
|
2996
|
+
this.notificationService.updateProgress(this.uploadNotificationId, progress, `Uploading file... ${Math.round(progress)}%`);
|
|
2997
|
+
}
|
|
2998
|
+
}).subscribe({
|
|
2929
2999
|
next: (response) => {
|
|
3000
|
+
console.log('🎉 [FileInput] Upload SUCCESS - Response received:', response);
|
|
3001
|
+
// Set upload status to 'success'
|
|
3002
|
+
this.uploadStatus = 'success';
|
|
3003
|
+
console.log('🔄 [FileInput] Upload status set to:', this.uploadStatus);
|
|
3004
|
+
// Complete the progress
|
|
2930
3005
|
this.uploadProgress = 100;
|
|
2931
3006
|
this.uploadProgressChange.emit(100);
|
|
3007
|
+
console.log('📊 [FileInput] Upload progress completed: 100%');
|
|
2932
3008
|
// Update progress notification to complete
|
|
2933
3009
|
if (this.uploadNotificationId) {
|
|
2934
3010
|
this.notificationService.remove(this.uploadNotificationId);
|
|
3011
|
+
console.log('🔔 [FileInput] Progress notification removed');
|
|
2935
3012
|
}
|
|
2936
3013
|
this.notificationService.success('File upload completed!');
|
|
2937
3014
|
this.uploadNotificationId = null;
|
|
2938
3015
|
// Extract ID from response - the file manager returns the _id of the created record
|
|
2939
3016
|
const uploadedId = response?._id || response?.data?._id || response?.id || response?.fileId;
|
|
2940
3017
|
if (uploadedId) {
|
|
2941
|
-
console.log('✅ File uploaded successfully with ID:', uploadedId);
|
|
3018
|
+
console.log('✅ [FileInput] File uploaded successfully with ID:', uploadedId);
|
|
2942
3019
|
this.uploadSuccess.emit(uploadedId);
|
|
2943
3020
|
// Update the form control value with the uploaded file ID
|
|
2944
3021
|
this.onChange(uploadedId);
|
|
3022
|
+
console.log('📝 [FileInput] Form control value updated with uploaded file ID');
|
|
2945
3023
|
}
|
|
2946
3024
|
else {
|
|
2947
|
-
console.error('❌ Upload successful but no ID returned:', response);
|
|
3025
|
+
console.error('❌ [FileInput] Upload successful but no ID returned:', response);
|
|
2948
3026
|
this.uploadError.emit('Upload successful but no ID returned');
|
|
2949
3027
|
}
|
|
2950
3028
|
this.isUploading = false;
|
|
3029
|
+
console.log('🔄 [FileInput] isUploading set to false');
|
|
3030
|
+
// Trigger validation update to clear upload in progress error
|
|
3031
|
+
this.onValidatorChange();
|
|
3032
|
+
console.log('✅ [FileInput] Validation triggered - should clear upload in progress error');
|
|
2951
3033
|
},
|
|
2952
3034
|
error: (error) => {
|
|
2953
|
-
console.error('
|
|
3035
|
+
console.error('💥 [FileInput] Upload FAILED:', error);
|
|
3036
|
+
// Set upload status to 'error' and remove upload validation error
|
|
3037
|
+
this.uploadStatus = 'error';
|
|
3038
|
+
console.log('🔄 [FileInput] Upload status set to:', this.uploadStatus);
|
|
2954
3039
|
// Remove progress notification and show error
|
|
2955
3040
|
if (this.uploadNotificationId) {
|
|
2956
3041
|
this.notificationService.remove(this.uploadNotificationId);
|
|
3042
|
+
console.log('🔔 [FileInput] Progress notification removed due to error');
|
|
2957
3043
|
}
|
|
2958
3044
|
this.notificationService.error(`Upload failed: ${error.message || error.error?.message || 'Upload failed'}`);
|
|
2959
3045
|
this.uploadNotificationId = null;
|
|
@@ -2961,24 +3047,13 @@ class CideEleFileInputComponent {
|
|
|
2961
3047
|
this.isUploading = false;
|
|
2962
3048
|
this.uploadProgress = 0;
|
|
2963
3049
|
this.uploadProgressChange.emit(0);
|
|
3050
|
+
console.log('🔄 [FileInput] Upload state reset - isUploading: false, progress: 0%');
|
|
3051
|
+
// Trigger validation update to clear upload in progress error
|
|
3052
|
+
this.onValidatorChange();
|
|
3053
|
+
console.log('✅ [FileInput] Validation triggered - should clear upload in progress error');
|
|
2964
3054
|
}
|
|
2965
3055
|
});
|
|
2966
3056
|
}
|
|
2967
|
-
simulateProgress() {
|
|
2968
|
-
const interval = setInterval(() => {
|
|
2969
|
-
if (this.uploadProgress < 90) {
|
|
2970
|
-
this.uploadProgress += Math.random() * 8; // Smaller increments for smoother progress
|
|
2971
|
-
this.uploadProgressChange.emit(this.uploadProgress);
|
|
2972
|
-
// Update progress notification
|
|
2973
|
-
if (this.uploadNotificationId) {
|
|
2974
|
-
this.notificationService.updateProgress(this.uploadNotificationId, this.uploadProgress, `Uploading file... ${Math.round(this.uploadProgress)}%`);
|
|
2975
|
-
}
|
|
2976
|
-
}
|
|
2977
|
-
else {
|
|
2978
|
-
clearInterval(interval);
|
|
2979
|
-
}
|
|
2980
|
-
}, 300); // More frequent updates for smoother progress bar
|
|
2981
|
-
}
|
|
2982
3057
|
generatePreviews() {
|
|
2983
3058
|
// Clear existing previews
|
|
2984
3059
|
this.clearPreviews();
|
|
@@ -3053,14 +3128,96 @@ class CideEleFileInputComponent {
|
|
|
3053
3128
|
hasImages() {
|
|
3054
3129
|
return this.previewUrls.length > 0;
|
|
3055
3130
|
}
|
|
3131
|
+
isRequired() {
|
|
3132
|
+
return this.required;
|
|
3133
|
+
}
|
|
3134
|
+
getCurrentState() {
|
|
3135
|
+
return {
|
|
3136
|
+
id: this.id,
|
|
3137
|
+
label: this.label,
|
|
3138
|
+
required: this.required,
|
|
3139
|
+
disabled: this.disabled,
|
|
3140
|
+
accept: this.accept,
|
|
3141
|
+
multiple: this.multiple,
|
|
3142
|
+
showPreview: this.showPreview,
|
|
3143
|
+
autoUpload: this.autoUpload,
|
|
3144
|
+
uploadStatus: this.uploadStatus,
|
|
3145
|
+
isUploading: this.isUploading,
|
|
3146
|
+
uploadProgress: this.uploadProgress,
|
|
3147
|
+
files: this.files ? Array.from(this.files).map(f => ({ name: f.name, size: f.size, type: f.type })) : null,
|
|
3148
|
+
fileNames: this.fileNames,
|
|
3149
|
+
previewUrls: this.previewUrls.length,
|
|
3150
|
+
helperText: this.helperText,
|
|
3151
|
+
errorText: this.errorText,
|
|
3152
|
+
placeholderText: this.placeholderText,
|
|
3153
|
+
placeholderIcon: this.placeholderIcon,
|
|
3154
|
+
previewWidth: this.previewWidth,
|
|
3155
|
+
previewHeight: this.previewHeight,
|
|
3156
|
+
previewBoxMode: this.previewBoxMode,
|
|
3157
|
+
showFileName: this.showFileName,
|
|
3158
|
+
uploadData: this.uploadData
|
|
3159
|
+
};
|
|
3160
|
+
}
|
|
3161
|
+
async getControlData() {
|
|
3162
|
+
console.log('🔍 [FileInput] getControlData called');
|
|
3163
|
+
const cide_element_data = await this.elementService?.getElementData({ sype_key: this.id });
|
|
3164
|
+
if (cide_element_data) {
|
|
3165
|
+
console.log('📋 [FileInput] Element data loaded:', cide_element_data);
|
|
3166
|
+
// Update basic properties that exist in ICoreSype
|
|
3167
|
+
this.label = cide_element_data?.sype_label;
|
|
3168
|
+
this.required = (cide_element_data?.sype_required || false);
|
|
3169
|
+
this.placeholderText = (cide_element_data?.sype_placeholder || 'Click to select image');
|
|
3170
|
+
// Update file-input specific properties from data object if available
|
|
3171
|
+
const data = cide_element_data?.data;
|
|
3172
|
+
if (data) {
|
|
3173
|
+
this.accept = data.accept || '';
|
|
3174
|
+
this.multiple = data.multiple || false;
|
|
3175
|
+
this.showPreview = data.showPreview || false;
|
|
3176
|
+
this.autoUpload = data.autoUpload || false;
|
|
3177
|
+
this.placeholderIcon = data.placeholderIcon || '📷';
|
|
3178
|
+
this.previewWidth = data.previewWidth || '200px';
|
|
3179
|
+
this.previewHeight = data.previewHeight || '200px';
|
|
3180
|
+
this.previewBoxMode = data.previewBoxMode || false;
|
|
3181
|
+
this.showFileName = data.showFileName || true;
|
|
3182
|
+
this.uploadData = data.uploadData || {};
|
|
3183
|
+
}
|
|
3184
|
+
console.log('✅ [FileInput] Control data updated from element service');
|
|
3185
|
+
// Trigger validation update
|
|
3186
|
+
this.onValidatorChange();
|
|
3187
|
+
}
|
|
3188
|
+
else {
|
|
3189
|
+
console.log('⚠️ [FileInput] No element data found for key:', this.id);
|
|
3190
|
+
}
|
|
3191
|
+
}
|
|
3192
|
+
// Validator implementation
|
|
3193
|
+
validate(control) {
|
|
3194
|
+
console.log('🔍 [FileInput] validate() called - uploadStatus:', this.uploadStatus, 'required:', this.required, 'files:', !!this.files, 'control.value:', control.value);
|
|
3195
|
+
// If upload is in progress (start or uploading status), return validation error
|
|
3196
|
+
if (this.uploadStatus === 'start' || this.uploadStatus === 'uploading') {
|
|
3197
|
+
console.log('⚠️ [FileInput] Validation ERROR: Upload in progress');
|
|
3198
|
+
return { 'uploadInProgress': { message: 'File upload in progress. Please wait...' } };
|
|
3199
|
+
}
|
|
3200
|
+
// If required and no file is selected and no control value (uploaded file ID), return validation error
|
|
3201
|
+
if (this.required && !this.files && !control.value) {
|
|
3202
|
+
console.log('⚠️ [FileInput] Validation ERROR: File required');
|
|
3203
|
+
return { 'required': { message: 'Please select a file to upload.' } };
|
|
3204
|
+
}
|
|
3205
|
+
console.log('✅ [FileInput] Validation PASSED: No errors');
|
|
3206
|
+
return null; // No validation errors
|
|
3207
|
+
}
|
|
3056
3208
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFileInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3057
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.7", type: CideEleFileInputComponent, isStandalone: true, selector: "cide-ele-file-input", inputs: { label: "label", accept: "accept", multiple: "multiple", disabled: "disabled", 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: [
|
|
3209
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.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: [
|
|
3058
3210
|
{
|
|
3059
3211
|
provide: NG_VALUE_ACCESSOR,
|
|
3060
3212
|
useExisting: forwardRef(() => CideEleFileInputComponent),
|
|
3061
3213
|
multi: true
|
|
3214
|
+
},
|
|
3215
|
+
{
|
|
3216
|
+
provide: NG_VALIDATORS,
|
|
3217
|
+
useExisting: forwardRef(() => CideEleFileInputComponent),
|
|
3218
|
+
multi: true
|
|
3062
3219
|
}
|
|
3063
|
-
], 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 <label *ngIf=\"label && !isPreviewBoxMode()\" class=\"cide-file-input-label\" [attr.for]=\"'cide-file-input-' + id\"
|
|
3220
|
+
], 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 <label *ngIf=\"label && !isPreviewBoxMode()\" class=\"cide-file-input-label\" [attr.for]=\"'cide-file-input-' + id\">\n {{ label }}<span *ngIf=\"required\" class=\"cide-file-input-required\"> *</span>\n </label>\n \n <!-- Preview Box Mode -->\n <div *ngIf=\"isPreviewBoxMode()\" 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 [style.width]=\"previewWidth\"\n [style.height]=\"previewHeight\"\n (click)=\"triggerFileSelect()\"\n [attr.title]=\"disabled ? 'File selection disabled' : placeholderText\">\n \n <!-- No Image State -->\n <div *ngIf=\"!hasImages()\" class=\"cide-file-input-preview-box-placeholder\">\n <div class=\"cide-file-input-preview-box-icon\">\n <cide-ele-icon>{{ placeholderIcon }}</cide-ele-icon>\n </div>\n <div class=\"cide-file-input-preview-box-text\">{{ placeholderText }}</div>\n </div>\n \n <!-- Image Preview State -->\n <div *ngIf=\"hasImages()\" 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 <button \n *ngIf=\"!disabled\"\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 </div>\n </div>\n \n <!-- File name display for preview box mode -->\n <div *ngIf=\"hasImages() && fileNames.length && showFileName\" class=\"cide-file-input-preview-box-filename\">\n {{ fileNames[0] }}\n </div>\n </div>\n\n <!-- Standard Mode -->\n <div *ngIf=\"!isPreviewBoxMode()\" class=\"cide-file-input-wrapper\">\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 <button *ngIf=\"fileNames.length\" type=\"button\" class=\"cide-file-input-clear\" (click)=\"clearFiles()\">\n Clear\n </button>\n </div>\n <div *ngIf=\"fileNames.length && !isPreviewBoxMode()\" class=\"cide-file-input-files\">\n <span *ngFor=\"let name of fileNames\">{{ name }}</span>\n </div>\n \n <!-- Image Preview Section (only for standard mode) -->\n <div *ngIf=\"isImagePreviewAvailable() && !isPreviewBoxMode()\" class=\"cide-file-input-preview\">\n <div class=\"cide-file-input-preview-label\">Preview:</div>\n <div class=\"cide-file-input-preview-container\">\n <div \n *ngFor=\"let previewUrl of previewUrls; let i = index\" \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 </div>\n </div>\n \n <div *ngIf=\"errorText\" class=\"cide-file-input-error\">{{ errorText }}</div>\n <div *ngIf=\"helperText && !errorText\" class=\"cide-file-input-helper\">{{ helperText }}</div>\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}.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-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-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: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }] });
|
|
3064
3221
|
}
|
|
3065
3222
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFileInputComponent, decorators: [{
|
|
3066
3223
|
type: Component,
|
|
@@ -3069,8 +3226,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
3069
3226
|
provide: NG_VALUE_ACCESSOR,
|
|
3070
3227
|
useExisting: forwardRef(() => CideEleFileInputComponent),
|
|
3071
3228
|
multi: true
|
|
3229
|
+
},
|
|
3230
|
+
{
|
|
3231
|
+
provide: NG_VALIDATORS,
|
|
3232
|
+
useExisting: forwardRef(() => CideEleFileInputComponent),
|
|
3233
|
+
multi: true
|
|
3072
3234
|
}
|
|
3073
|
-
], 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 <label *ngIf=\"label && !isPreviewBoxMode()\" class=\"cide-file-input-label\" [attr.for]=\"'cide-file-input-' + id\"
|
|
3235
|
+
], 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 <label *ngIf=\"label && !isPreviewBoxMode()\" class=\"cide-file-input-label\" [attr.for]=\"'cide-file-input-' + id\">\n {{ label }}<span *ngIf=\"required\" class=\"cide-file-input-required\"> *</span>\n </label>\n \n <!-- Preview Box Mode -->\n <div *ngIf=\"isPreviewBoxMode()\" 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 [style.width]=\"previewWidth\"\n [style.height]=\"previewHeight\"\n (click)=\"triggerFileSelect()\"\n [attr.title]=\"disabled ? 'File selection disabled' : placeholderText\">\n \n <!-- No Image State -->\n <div *ngIf=\"!hasImages()\" class=\"cide-file-input-preview-box-placeholder\">\n <div class=\"cide-file-input-preview-box-icon\">\n <cide-ele-icon>{{ placeholderIcon }}</cide-ele-icon>\n </div>\n <div class=\"cide-file-input-preview-box-text\">{{ placeholderText }}</div>\n </div>\n \n <!-- Image Preview State -->\n <div *ngIf=\"hasImages()\" 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 <button \n *ngIf=\"!disabled\"\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 </div>\n </div>\n \n <!-- File name display for preview box mode -->\n <div *ngIf=\"hasImages() && fileNames.length && showFileName\" class=\"cide-file-input-preview-box-filename\">\n {{ fileNames[0] }}\n </div>\n </div>\n\n <!-- Standard Mode -->\n <div *ngIf=\"!isPreviewBoxMode()\" class=\"cide-file-input-wrapper\">\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 <button *ngIf=\"fileNames.length\" type=\"button\" class=\"cide-file-input-clear\" (click)=\"clearFiles()\">\n Clear\n </button>\n </div>\n <div *ngIf=\"fileNames.length && !isPreviewBoxMode()\" class=\"cide-file-input-files\">\n <span *ngFor=\"let name of fileNames\">{{ name }}</span>\n </div>\n \n <!-- Image Preview Section (only for standard mode) -->\n <div *ngIf=\"isImagePreviewAvailable() && !isPreviewBoxMode()\" class=\"cide-file-input-preview\">\n <div class=\"cide-file-input-preview-label\">Preview:</div>\n <div class=\"cide-file-input-preview-container\">\n <div \n *ngFor=\"let previewUrl of previewUrls; let i = index\" \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 </div>\n </div>\n \n <div *ngIf=\"errorText\" class=\"cide-file-input-error\">{{ errorText }}</div>\n <div *ngIf=\"helperText && !errorText\" class=\"cide-file-input-helper\">{{ helperText }}</div>\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}.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-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-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"] }]
|
|
3074
3236
|
}], propDecorators: { label: [{
|
|
3075
3237
|
type: Input
|
|
3076
3238
|
}], accept: [{
|
|
@@ -3079,6 +3241,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
3079
3241
|
type: Input
|
|
3080
3242
|
}], disabled: [{
|
|
3081
3243
|
type: Input
|
|
3244
|
+
}], required: [{
|
|
3245
|
+
type: Input
|
|
3082
3246
|
}], helperText: [{
|
|
3083
3247
|
type: Input
|
|
3084
3248
|
}], errorText: [{
|