cloud-ide-element 1.1.166 → 1.1.172
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.
|
@@ -4472,7 +4472,7 @@ class CideEleFileInputComponent {
|
|
|
4472
4472
|
hasEverUploaded = signal(false, ...(ngDevMode ? [{ debugName: "hasEverUploaded" }] : [])); // Track if this component has ever uploaded files
|
|
4473
4473
|
failedFile = signal(null, ...(ngDevMode ? [{ debugName: "failedFile" }] : [])); // Store failed file for retry
|
|
4474
4474
|
// Computed signals for better relationships
|
|
4475
|
-
hasFiles = computed(() => this.files() !== null && this.files().length > 0, ...(ngDevMode ? [{ debugName: "hasFiles" }] : []));
|
|
4475
|
+
hasFiles = computed(() => (this.files() !== null && this.files().length > 0) || this.fileNames().length > 0, ...(ngDevMode ? [{ debugName: "hasFiles" }] : []));
|
|
4476
4476
|
canUpload = computed(() => this.hasFiles() && !this.isUploading() && !this.disabledSignal(), ...(ngDevMode ? [{ debugName: "canUpload" }] : []));
|
|
4477
4477
|
isInErrorState = computed(() => this.uploadStatus() === 'error', ...(ngDevMode ? [{ debugName: "isInErrorState" }] : []));
|
|
4478
4478
|
isInSuccessState = computed(() => this.uploadStatus() === 'success', ...(ngDevMode ? [{ debugName: "isInSuccessState" }] : []));
|
|
@@ -4958,20 +4958,46 @@ class CideEleFileInputComponent {
|
|
|
4958
4958
|
}
|
|
4959
4959
|
});
|
|
4960
4960
|
this.previewUrls.set([]);
|
|
4961
|
+
// Revoke download URLs as well
|
|
4962
|
+
this.downloadUrls().forEach(url => {
|
|
4963
|
+
if (url.startsWith('blob:')) {
|
|
4964
|
+
URL.revokeObjectURL(url);
|
|
4965
|
+
}
|
|
4966
|
+
});
|
|
4967
|
+
this.downloadUrls.set([]);
|
|
4968
|
+
}
|
|
4969
|
+
generateDownloadUrls() {
|
|
4970
|
+
// Clear existing download URLs
|
|
4971
|
+
this.downloadUrls().forEach(url => {
|
|
4972
|
+
if (url.startsWith('blob:')) {
|
|
4973
|
+
URL.revokeObjectURL(url);
|
|
4974
|
+
}
|
|
4975
|
+
});
|
|
4976
|
+
this.downloadUrls.set([]);
|
|
4977
|
+
if (!this.files()) {
|
|
4978
|
+
return;
|
|
4979
|
+
}
|
|
4980
|
+
const urls = [];
|
|
4981
|
+
Array.from(this.files()).forEach(file => {
|
|
4982
|
+
const url = URL.createObjectURL(file);
|
|
4983
|
+
urls.push(url);
|
|
4984
|
+
});
|
|
4985
|
+
this.downloadUrls.set(urls);
|
|
4986
|
+
console.log('🔗 [FileInput] Generated local download URLs for selected files');
|
|
4961
4987
|
}
|
|
4962
4988
|
isImageFile(file) {
|
|
4963
4989
|
return file.type.startsWith('image/');
|
|
4964
4990
|
}
|
|
4965
4991
|
loadFileDetailsFromId(fileId) {
|
|
4966
|
-
console.log('🔍 [FileInput] Loading file details for ID:', fileId);
|
|
4992
|
+
console.log('🔍 [FileInput] Loading file details for ID:', fileId, 'Component ID:', this.id());
|
|
4967
4993
|
if (!fileId)
|
|
4968
4994
|
return;
|
|
4969
4995
|
this.fileManagerService?.getFileDetails({ cyfm_id: fileId })?.pipe(takeUntilDestroyed(this.destroyRef)).subscribe({
|
|
4970
4996
|
next: (fileDetails) => {
|
|
4971
|
-
console.log('📋 [FileInput] File details received:',
|
|
4997
|
+
console.log('📋 [FileInput] File details received for ID:', fileId);
|
|
4972
4998
|
if (fileDetails?.data?.length) {
|
|
4973
4999
|
const fileData = fileDetails.data[0];
|
|
4974
|
-
console.log('📁 [FileInput] File
|
|
5000
|
+
console.log('📁 [FileInput] File name:', fileData.cyfm_name);
|
|
4975
5001
|
// Set file name from the details
|
|
4976
5002
|
if (fileData.cyfm_name) {
|
|
4977
5003
|
this.fileNames.set([fileData.cyfm_name]);
|
|
@@ -4984,6 +5010,7 @@ class CideEleFileInputComponent {
|
|
|
4984
5010
|
if (!base64Data.startsWith('data:')) {
|
|
4985
5011
|
base64Data = `data:${mimeType};base64,${base64Data}`;
|
|
4986
5012
|
}
|
|
5013
|
+
console.log(`💾 [FileInput] Setting download URL for ${fileData.cyfm_name}, length: ${base64Data.length}`);
|
|
4987
5014
|
// Set download URL for all files
|
|
4988
5015
|
this.downloadUrls.set([base64Data]);
|
|
4989
5016
|
// If it's an image, set preview
|
|
@@ -5069,6 +5096,29 @@ class CideEleFileInputComponent {
|
|
|
5069
5096
|
document.body.removeChild(link);
|
|
5070
5097
|
}
|
|
5071
5098
|
}
|
|
5099
|
+
viewFile(index = 0) {
|
|
5100
|
+
const urls = this.downloadUrls();
|
|
5101
|
+
const names = this.fileNames();
|
|
5102
|
+
console.log(`👁️ [FileInput] viewFile called for index ${index}`);
|
|
5103
|
+
console.log(`👁️ [FileInput] Component ID: ${this.id()}`);
|
|
5104
|
+
console.log(`👁️ [FileInput] URLs available: ${urls.length}`);
|
|
5105
|
+
if (urls.length > index) {
|
|
5106
|
+
const base64 = urls[index];
|
|
5107
|
+
console.log(`👁️ [FileInput] Opening file: ${names[index] || 'unknown'}`);
|
|
5108
|
+
console.log(`👁️ [FileInput] Content length: ${base64.length}`);
|
|
5109
|
+
const win = window.open('about:blank');
|
|
5110
|
+
if (win) {
|
|
5111
|
+
win.document.write(`<iframe src="${base64}" frameborder="0" style="border:0; top:0px; left:0px; bottom:0px; right:0px; width:100%; height:100%;" allowfullscreen></iframe>`);
|
|
5112
|
+
win.document.close();
|
|
5113
|
+
}
|
|
5114
|
+
else {
|
|
5115
|
+
console.error('❌ [FileInput] Failed to open new window');
|
|
5116
|
+
}
|
|
5117
|
+
}
|
|
5118
|
+
else {
|
|
5119
|
+
console.error('❌ [FileInput] No URL found for index', index);
|
|
5120
|
+
}
|
|
5121
|
+
}
|
|
5072
5122
|
removePreview(index) {
|
|
5073
5123
|
const currentFiles = this.files();
|
|
5074
5124
|
const currentUrls = this.previewUrls();
|
|
@@ -5374,6 +5424,7 @@ class CideEleFileInputComponent {
|
|
|
5374
5424
|
this.files.set(files);
|
|
5375
5425
|
this.fileNames.set(Array.from(files).map(f => f.name));
|
|
5376
5426
|
this.generatePreviews();
|
|
5427
|
+
this.generateDownloadUrls();
|
|
5377
5428
|
// Reset upload status when new file is selected
|
|
5378
5429
|
this.uploadStatus.set('idle');
|
|
5379
5430
|
this.onChange(files);
|
|
@@ -5486,7 +5537,7 @@ class CideEleFileInputComponent {
|
|
|
5486
5537
|
useExisting: CideEleFileInputComponent,
|
|
5487
5538
|
multi: true
|
|
5488
5539
|
}
|
|
5489
|
-
], usesOnChanges: true, ngImport: i0, template: "<div class=\"tw-flex tw-flex-col tw-gap-2\">\n <!-- Label (shown when not in preview box mode or when preview box mode but no label override) -->\n @if (labelSignal() && !isPreviewBoxMode()) {\n <label class=\"tw-block tw-text-sm tw-font-medium tw-text-gray-700 dark:tw-text-gray-200 tw-mb-1.5 tw-leading-5\"\n [attr.for]=\"'cide-file-input-' + id()\">\n {{ labelSignal() }}@if (requiredSignal()) {<span class=\"tw-text-red-500 dark:tw-text-red-400\"> *</span>}\n </label>\n }\n\n <!-- Preview Box Mode -->\n @if (isPreviewBoxMode()) {\n <div class=\"tw-relative\">\n <!-- Hidden file input -->\n <input type=\"file\" [attr.id]=\"'cide-file-input-' + id()\" [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\" [disabled]=\"disabledSignal()\" (change)=\"onFileSelected($event)\"\n class=\"tw-hidden\" />\n\n <!-- Preview Box -->\n <div\n class=\"tw-border-2 tw-border-dashed tw-border-gray-300 dark:tw-border-gray-600 tw-rounded-lg tw-bg-gray-50 dark:tw-bg-gray-800 tw-cursor-pointer tw-transition-all tw-duration-200 tw-relative tw-overflow-hidden hover:tw-border-blue-500 hover:tw-bg-blue-50 dark:hover:tw-bg-blue-900/20\"\n [class]=\"getPreviewBoxClasses()\" [style.width]=\"previewWidthSignal()\" [style.height]=\"previewHeightSignal()\"\n (click)=\"triggerFileSelect()\" (dragover)=\"onDragOver($event)\" (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\" (drop)=\"onDrop($event)\"\n [attr.title]=\"disabledSignal() ? 'File selection disabled' : placeholderTextSignal()\">\n\n <!-- No Image State -->\n @if (!hasImages()) {\n <div class=\"tw-flex tw-flex-col tw-items-center tw-justify-center tw-h-full tw-p-4\">\n <div class=\"tw-mb-2\">\n <cide-ele-icon class=\"tw-text-gray-400 dark:tw-text-gray-500\" size=\"lg\">{{ isDragOver() ? '\uD83D\uDCC1' :\n placeholderIconSignal() }}</cide-ele-icon>\n </div>\n <div class=\"tw-text-sm tw-text-gray-600 dark:tw-text-gray-400 tw-text-center\">\n {{ isDragOver() ? 'Drop files here...' : placeholderTextSignal() }}\n </div>\n </div>\n }\n\n <!-- Image Preview State -->\n @if (hasImages()) {\n <div class=\"tw-relative tw-w-full tw-h-full\">\n <img [src]=\"previewUrls()[0]\" [alt]=\"fileNames()[0] || 'Preview image'\"\n class=\"tw-w-full tw-h-full tw-object-cover tw-rounded-lg\">\n <div\n class=\"tw-absolute tw-inset-0 tw-bg-black tw-bg-opacity-0 hover:tw-bg-opacity-30 tw-transition-all tw-duration-200 tw-flex tw-items-center tw-justify-center tw-rounded-lg\">\n <div class=\"tw-text-white tw-text-sm tw-opacity-0 hover:tw-opacity-100 tw-transition-opacity tw-duration-200\">\n Click to change</div>\n </div>\n @if (!disabledSignal()) {\n <button type=\"button\"\n class=\"tw-absolute tw-top-2 tw-right-2 tw-w-6 tw-h-6 tw-bg-red-500 hover:tw-bg-red-600 tw-text-white tw-rounded-full tw-flex tw-items-center tw-justify-center tw-text-sm tw-font-bold tw-transition-colors tw-duration-200 tw-border-none tw-cursor-pointer\"\n (click)=\"clearFiles(); $event.stopPropagation()\" title=\"Remove image\">\n \u00D7\n </button>\n }\n @if (isInErrorState() && failedFile()) {\n <button type=\"button\"\n class=\"tw-absolute tw-bottom-2 tw-left-1/2 tw-transform tw--translate-x-1/2 tw-px-3 tw-py-1.5 tw-text-xs tw-font-medium tw-border-none tw-rounded tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white tw-cursor-pointer tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed tw-shadow-lg tw-z-10\"\n (click)=\"retryUpload(); $event.stopPropagation()\" [disabled]=\"isUploading() || disabledSignal()\"\n title=\"Retry upload\">\n <cide-ele-icon size=\"xs\" class=\"tw-mr-1\">refresh</cide-ele-icon>\n Retry Upload\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=\"tw-mt-2 tw-text-sm tw-text-gray-600 dark:tw-text-gray-400 tw-text-center tw-truncate\">\n {{ fileNames()[0] }}\n </div>\n }\n <!-- Error message and retry button for preview box mode -->\n @if (isInErrorState() && failedFile() && !hasImages()) {\n <div class=\"tw-mt-2 tw-flex tw-flex-col tw-items-center tw-gap-2\">\n <span class=\"tw-text-sm tw-text-red-600 dark:tw-text-red-400\">Upload failed. Please retry.</span>\n <button type=\"button\"\n class=\"tw-flex tw-items-center tw-gap-1 tw-px-3 tw-py-1.5 tw-text-xs tw-font-medium tw-border-none tw-rounded tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white tw-cursor-pointer tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\"\n (click)=\"retryUpload()\" [disabled]=\"isUploading() || disabledSignal()\">\n <cide-ele-icon size=\"xs\">refresh</cide-ele-icon>\n Retry Upload\n </button>\n </div>\n }\n </div>\n }\n\n <!-- Standard Mode -->\n @if (!isPreviewBoxMode()) {\n <!-- Hidden file input -->\n <input type=\"file\" [attr.id]=\"'cide-file-input-' + id()\" [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\" [disabled]=\"disabledSignal()\" (change)=\"onFileSelected($event)\"\n class=\"tw-hidden\" />\n\n <!-- Modern Drag and Drop Zone -->\n <div\n class=\"tw-border-2 tw-border-dashed tw-border-gray-300 dark:tw-border-gray-600 tw-rounded-lg tw-bg-gray-50 dark:tw-bg-gray-800 tw-cursor-pointer tw-transition-all tw-duration-200 tw-min-h-[60px] hover:tw-border-blue-500 hover:tw-bg-blue-50 dark:hover:tw-bg-blue-900/20\"\n [class]=\"getDragDropZoneClasses()\" (click)=\"triggerFileSelect()\" (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\" (dragleave)=\"onDragLeave($event)\" (drop)=\"onDrop($event)\">\n\n <div class=\"tw-flex tw-items-center tw-justify-between tw-p-3 tw-gap-3\">\n <!-- Icon and Text -->\n <div class=\"tw-flex tw-items-center tw-gap-2.5 tw-flex-1 tw-min-w-0\">\n <cide-ele-icon class=\"tw-flex-shrink-0 tw-transition-colors tw-duration-200\" [class]=\"getIconClasses()\"\n size=\"sm\">\n {{ isDragOver() ? 'file_download' : (hasFiles() ? 'check_circle' : 'cloud_upload') }}\n </cide-ele-icon>\n\n <div class=\"tw-flex tw-flex-col tw-gap-0.5 tw-min-w-0\">\n @if (isDragOver()) {\n <span\n class=\"tw-text-sm tw-font-medium tw-text-blue-700 dark:tw-text-blue-300 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\">Drop\n files here</span>\n } @else if (hasFiles()) {\n <span\n class=\"tw-text-sm tw-font-medium tw-text-emerald-700 dark:tw-text-emerald-300 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\">\n @if (multipleSignal() && fileNames().length > 1) {\n {{ fileNames().length }} files selected\n } @else {\n {{ fileNames()[0] }}\n }\n </span>\n @if (totalFileSize() > 0) {\n <span class=\"tw-text-xs tw-text-emerald-600 dark:tw-text-emerald-400\">{{ fileSizeInMB() }} MB</span>\n }\n } @else {\n <span\n class=\"tw-text-sm tw-font-medium tw-text-gray-700 dark:tw-text-gray-300 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\">\n {{ multipleSignal() ? 'Choose files or drag here' : 'Choose file or drag here' }}\n </span>\n }\n </div>\n </div>\n\n <!-- Action Buttons -->\n <div class=\"tw-flex tw-gap-1 tw-flex-shrink-0\">\n <!-- Download Button -->\n @if (hasFiles() && downloadUrls().length > 0) {\n <button type=\"button\"\n class=\"tw-flex tw-items-center tw-justify-center tw-w-6 tw-h-6 tw-border-none tw-rounded tw-bg-transparent tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-blue-600 hover:tw-bg-blue-50 dark:hover:tw-bg-blue-900/20 hover:tw-text-blue-700\"\n (click)=\"downloadFile(0); $event.stopPropagation()\" title=\"Download file\">\n <cide-ele-icon size=\"xs\">download</cide-ele-icon>\n </button>\n }\n\n @if (isInErrorState() && failedFile()) {\n <button type=\"button\"\n class=\"tw-flex tw-items-center tw-justify-center tw-px-2 tw-py-1 tw-text-xs tw-font-medium tw-border-none tw-rounded tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white tw-cursor-pointer tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\"\n (click)=\"retryUpload(); $event.stopPropagation()\" [disabled]=\"isUploading() || disabledSignal()\"\n title=\"Retry upload\">\n <cide-ele-icon size=\"xs\" class=\"tw-mr-1\">refresh</cide-ele-icon>\n Retry\n </button>\n }\n @if (hasFiles()) {\n <button type=\"button\"\n class=\"tw-flex tw-items-center tw-justify-center tw-w-6 tw-h-6 tw-border-none tw-rounded tw-bg-transparent tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-red-600 hover:tw-bg-red-50 dark:hover:tw-bg-red-900/20 hover:tw-text-red-700\"\n (click)=\"clearFiles(); $event.stopPropagation()\" title=\"Clear files\">\n <cide-ele-icon size=\"xs\">close</cide-ele-icon>\n </button>\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Image Preview Section (only for standard mode) -->\n @if (isImagePreviewAvailable() && !isPreviewBoxMode()) {\n <div class=\"tw-mt-3\">\n <div class=\"tw-text-sm tw-font-medium tw-text-gray-700 dark:tw-text-gray-200 tw-mb-2\">Preview:</div>\n <div class=\"tw-flex tw-flex-wrap tw-gap-3\">\n @for (previewUrl of previewUrls(); track previewUrl; let i = $index) {\n <div\n class=\"tw-relative tw-border tw-border-gray-200 dark:tw-border-gray-600 tw-rounded-lg tw-overflow-hidden tw-bg-white dark:tw-bg-gray-800\"\n [style.width]=\"previewWidthSignal()\" [style.height]=\"previewHeightSignal()\">\n <button type=\"button\"\n class=\"tw-absolute tw-top-1 tw-right-1 tw-w-5 tw-h-5 tw-bg-red-500 hover:tw-bg-red-600 tw-text-white tw-rounded-full tw-flex tw-items-center tw-justify-center tw-text-xs tw-font-bold tw-transition-colors tw-duration-200 tw-border-none tw-cursor-pointer tw-z-10\"\n (click)=\"removePreview(i)\" title=\"Remove image\">\n \u00D7\n </button>\n <img [src]=\"previewUrl\" [alt]=\"fileNames()[i] || 'Preview image'\" class=\"tw-w-full tw-h-full tw-object-cover\"\n loading=\"lazy\">\n <div\n class=\"tw-absolute tw-bottom-0 tw-left-0 tw-right-0 tw-bg-black tw-bg-opacity-75 tw-text-white tw-text-xs tw-p-1 tw-truncate\">\n {{ fileNames()[i] }}</div>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Upload Status and Show Files Button (only for multiple file inputs) -->\n @if (multiple && showFloatingUploaderSignal() && (getUploadCount() > 0 || hasActiveUploads() || hasEverUploaded())) {\n <div class=\"tw-flex tw-items-center tw-justify-between tw-py-1.5 tw-gap-2\">\n <div class=\"tw-flex tw-items-center tw-gap-2\">\n <cide-ele-icon class=\"tw-text-blue-600 dark:tw-text-blue-400\" size=\"sm\">cloud_upload</cide-ele-icon>\n <span class=\"tw-text-sm tw-text-gray-700 dark:tw-text-gray-300\">\n @if (hasActiveUploads()) {\n {{ getActiveUploadCount() }} uploading\n } @else if (getUploadCount() > 0) {\n {{ getUploadCount() }} completed\n } @else if (hasEverUploaded()) {\n View uploads\n }\n </span>\n </div>\n <button type=\"button\"\n class=\"tw-flex tw-items-center tw-justify-center tw-w-8 tw-h-8 tw-rounded-md tw-bg-gray-100 dark:tw-bg-gray-700 hover:tw-bg-gray-200 dark:hover:tw-bg-gray-600 tw-text-gray-600 dark:tw-text-gray-300 tw-transition-colors tw-duration-200 tw-border-none tw-cursor-pointer\"\n (click)=\"showFloatingUploaderDialog()\" title=\"View upload progress and history\">\n <cide-ele-icon size=\"sm\">visibility</cide-ele-icon>\n </button>\n </div>\n }\n\n @if (errorTextSignal()) {\n <div class=\"tw-text-sm tw-text-red-600 dark:tw-text-red-400 tw-mt-1\">{{ errorTextSignal() }}</div>\n }\n @if (isInErrorState() && failedFile() && !errorTextSignal()) {\n <div class=\"tw-flex tw-items-center tw-gap-2 tw-mt-1\">\n <span class=\"tw-text-sm tw-text-red-600 dark:tw-text-red-400\">Upload failed. Please retry.</span>\n <button type=\"button\"\n class=\"tw-flex tw-items-center tw-gap-1 tw-px-2 tw-py-1 tw-text-xs tw-font-medium tw-border-none tw-rounded tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white tw-cursor-pointer tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\"\n (click)=\"retryUpload()\" [disabled]=\"isUploading() || disabledSignal()\">\n <cide-ele-icon size=\"xs\">refresh</cide-ele-icon>\n Retry Upload\n </button>\n </div>\n }\n @if (helperTextSignal() && !errorTextSignal() && !isInErrorState()) {\n <div class=\"tw-text-sm tw-text-gray-500 dark:tw-text-gray-400 tw-mt-1\">{{ helperTextSignal() }}</div>\n }\n</div>", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }] });
|
|
5540
|
+
], usesOnChanges: true, ngImport: i0, template: "<div class=\"tw-flex tw-flex-col tw-gap-2\">\n <!-- Label (shown when not in preview box mode or when preview box mode but no label override) -->\n @if (labelSignal() && !isPreviewBoxMode()) {\n <label class=\"tw-block tw-text-sm tw-font-medium tw-text-gray-700 dark:tw-text-gray-200 tw-mb-1.5 tw-leading-5\"\n [attr.for]=\"'cide-file-input-' + id()\">\n {{ labelSignal() }}@if (requiredSignal()) {<span class=\"tw-text-red-500 dark:tw-text-red-400\"> *</span>}\n </label>\n }\n\n <!-- Preview Box Mode -->\n @if (isPreviewBoxMode()) {\n <div class=\"tw-relative\">\n <!-- Hidden file input -->\n <input type=\"file\" [attr.id]=\"'cide-file-input-' + id()\" [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\" [disabled]=\"disabledSignal()\" (change)=\"onFileSelected($event)\"\n class=\"tw-hidden\" />\n\n <!-- Preview Box -->\n <div\n class=\"tw-border-2 tw-border-dashed tw-border-gray-300 dark:tw-border-gray-600 tw-rounded-lg tw-bg-gray-50 dark:tw-bg-gray-800 tw-cursor-pointer tw-transition-all tw-duration-200 tw-relative tw-overflow-hidden hover:tw-border-blue-500 hover:tw-bg-blue-50 dark:hover:tw-bg-blue-900/20\"\n [class]=\"getPreviewBoxClasses()\" [style.width]=\"previewWidthSignal()\" [style.height]=\"previewHeightSignal()\"\n (click)=\"triggerFileSelect()\" (dragover)=\"onDragOver($event)\" (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\" (drop)=\"onDrop($event)\"\n [attr.title]=\"disabledSignal() ? 'File selection disabled' : placeholderTextSignal()\">\n\n <!-- No Image State -->\n @if (!hasImages()) {\n <div class=\"tw-flex tw-flex-col tw-items-center tw-justify-center tw-h-full tw-p-4\">\n <div class=\"tw-mb-2\">\n <cide-ele-icon class=\"tw-text-gray-400 dark:tw-text-gray-500\" size=\"lg\">{{ isDragOver() ? '\uD83D\uDCC1' :\n placeholderIconSignal() }}</cide-ele-icon>\n </div>\n <div class=\"tw-text-sm tw-text-gray-600 dark:tw-text-gray-400 tw-text-center\">\n {{ isDragOver() ? 'Drop files here...' : placeholderTextSignal() }}\n </div>\n </div>\n }\n\n <!-- Image Preview State -->\n @if (hasImages()) {\n <div class=\"tw-relative tw-w-full tw-h-full\">\n <img [src]=\"previewUrls()[0]\" [alt]=\"fileNames()[0] || 'Preview image'\"\n class=\"tw-w-full tw-h-full tw-object-cover tw-rounded-lg\">\n <div\n class=\"tw-absolute tw-inset-0 tw-bg-black tw-bg-opacity-0 hover:tw-bg-opacity-30 tw-transition-all tw-duration-200 tw-flex tw-items-center tw-justify-center tw-rounded-lg\">\n <div class=\"tw-text-white tw-text-sm tw-opacity-0 hover:tw-opacity-100 tw-transition-opacity tw-duration-200\">\n Click to change</div>\n </div>\n @if (!disabledSignal()) {\n <button type=\"button\"\n class=\"tw-absolute tw-top-2 tw-right-2 tw-w-6 tw-h-6 tw-bg-red-500 hover:tw-bg-red-600 tw-text-white tw-rounded-full tw-flex tw-items-center tw-justify-center tw-text-sm tw-font-bold tw-transition-colors tw-duration-200 tw-border-none tw-cursor-pointer\"\n (click)=\"clearFiles(); $event.stopPropagation()\" title=\"Remove image\">\n \u00D7\n </button>\n }\n @if (isInErrorState() && failedFile()) {\n <button type=\"button\"\n class=\"tw-absolute tw-bottom-2 tw-left-1/2 tw-transform tw--translate-x-1/2 tw-px-3 tw-py-1.5 tw-text-xs tw-font-medium tw-border-none tw-rounded tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white tw-cursor-pointer tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed tw-shadow-lg tw-z-10\"\n (click)=\"retryUpload(); $event.stopPropagation()\" [disabled]=\"isUploading() || disabledSignal()\"\n title=\"Retry upload\">\n <cide-ele-icon size=\"xs\" class=\"tw-mr-1\">refresh</cide-ele-icon>\n Retry Upload\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=\"tw-mt-2 tw-text-sm tw-text-gray-600 dark:tw-text-gray-400 tw-text-center tw-truncate\">\n {{ fileNames()[0] }}\n </div>\n }\n <!-- Error message and retry button for preview box mode -->\n @if (isInErrorState() && failedFile() && !hasImages()) {\n <div class=\"tw-mt-2 tw-flex tw-flex-col tw-items-center tw-gap-2\">\n <span class=\"tw-text-sm tw-text-red-600 dark:tw-text-red-400\">Upload failed. Please retry.</span>\n <button type=\"button\"\n class=\"tw-flex tw-items-center tw-gap-1 tw-px-3 tw-py-1.5 tw-text-xs tw-font-medium tw-border-none tw-rounded tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white tw-cursor-pointer tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\"\n (click)=\"retryUpload()\" [disabled]=\"isUploading() || disabledSignal()\">\n <cide-ele-icon size=\"xs\">refresh</cide-ele-icon>\n Retry Upload\n </button>\n </div>\n }\n </div>\n }\n\n <!-- Standard Mode -->\n @if (!isPreviewBoxMode()) {\n <!-- Hidden file input -->\n <input type=\"file\" [attr.id]=\"'cide-file-input-' + id()\" [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\" [disabled]=\"disabledSignal()\" (change)=\"onFileSelected($event)\"\n class=\"tw-hidden\" />\n\n <!-- Modern Drag and Drop Zone -->\n <div\n class=\"tw-border-2 tw-border-dashed tw-border-gray-300 dark:tw-border-gray-600 tw-rounded-lg tw-bg-gray-50 dark:tw-bg-gray-800 tw-cursor-pointer tw-transition-all tw-duration-200 tw-min-h-[60px] hover:tw-border-blue-500 hover:tw-bg-blue-50 dark:hover:tw-bg-blue-900/20\"\n [class]=\"getDragDropZoneClasses()\" (click)=\"triggerFileSelect()\" (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\" (dragleave)=\"onDragLeave($event)\" (drop)=\"onDrop($event)\">\n\n <div class=\"tw-flex tw-items-center tw-justify-between tw-p-3 tw-gap-3\">\n <!-- Icon and Text -->\n <div class=\"tw-flex tw-items-center tw-gap-2.5 tw-flex-1 tw-min-w-0\">\n <cide-ele-icon class=\"tw-flex-shrink-0 tw-transition-colors tw-duration-200\" [class]=\"getIconClasses()\"\n size=\"sm\">\n {{ isDragOver() ? 'file_download' : (hasFiles() ? 'check_circle' : 'cloud_upload') }}\n </cide-ele-icon>\n\n <div class=\"tw-flex tw-flex-col tw-gap-0.5 tw-min-w-0\">\n @if (isDragOver()) {\n <span\n class=\"tw-text-sm tw-font-medium tw-text-blue-700 dark:tw-text-blue-300 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\">Drop\n files here</span>\n } @else if (hasFiles()) {\n <span\n class=\"tw-text-sm tw-font-medium tw-text-emerald-700 dark:tw-text-emerald-300 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\">\n @if (multipleSignal() && fileNames().length > 1) {\n {{ fileNames().length }} files selected\n } @else {\n {{ fileNames()[0] }}\n }\n </span>\n @if (totalFileSize() > 0) {\n <span class=\"tw-text-xs tw-text-emerald-600 dark:tw-text-emerald-400\">{{ fileSizeInMB() }} MB</span>\n }\n } @else {\n <span\n class=\"tw-text-sm tw-font-medium tw-text-gray-700 dark:tw-text-gray-300 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\">\n {{ multipleSignal() ? 'Choose files or drag here' : 'Choose file or drag here' }}\n </span>\n }\n </div>\n </div>\n\n <!-- Action Buttons -->\n <div class=\"tw-flex tw-gap-1 tw-flex-shrink-0\">\n <!-- Download Button -->\n @if (hasFiles() && downloadUrls().length > 0) {\n <button type=\"button\"\n class=\"tw-flex tw-items-center tw-justify-center tw-w-6 tw-h-6 tw-border-none tw-rounded tw-bg-transparent tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-purple-600 hover:tw-bg-purple-50 dark:hover:tw-bg-purple-900/20 hover:tw-text-purple-700 tw-mr-1\"\n (click)=\"viewFile(0); $event.stopPropagation()\" title=\"View file\">\n <cide-ele-icon size=\"xs\">visibility</cide-ele-icon>\n </button>\n <button type=\"button\"\n class=\"tw-flex tw-items-center tw-justify-center tw-w-6 tw-h-6 tw-border-none tw-rounded tw-bg-transparent tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-blue-600 hover:tw-bg-blue-50 dark:hover:tw-bg-blue-900/20 hover:tw-text-blue-700\"\n (click)=\"downloadFile(0); $event.stopPropagation()\" title=\"Download file\">\n <cide-ele-icon size=\"xs\">download</cide-ele-icon>\n </button>\n }\n\n @if (isInErrorState() && failedFile()) {\n <button type=\"button\"\n class=\"tw-flex tw-items-center tw-justify-center tw-px-2 tw-py-1 tw-text-xs tw-font-medium tw-border-none tw-rounded tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white tw-cursor-pointer tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\"\n (click)=\"retryUpload(); $event.stopPropagation()\" [disabled]=\"isUploading() || disabledSignal()\"\n title=\"Retry upload\">\n <cide-ele-icon size=\"xs\" class=\"tw-mr-1\">refresh</cide-ele-icon>\n Retry\n </button>\n }\n @if (hasFiles()) {\n <button type=\"button\"\n class=\"tw-flex tw-items-center tw-justify-center tw-w-6 tw-h-6 tw-border-none tw-rounded tw-bg-transparent tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-red-600 hover:tw-bg-red-50 dark:hover:tw-bg-red-900/20 hover:tw-text-red-700\"\n (click)=\"clearFiles(); $event.stopPropagation()\" title=\"Clear files\">\n <cide-ele-icon size=\"xs\">close</cide-ele-icon>\n </button>\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Image Preview Section (only for standard mode) -->\n @if (isImagePreviewAvailable() && !isPreviewBoxMode()) {\n <div class=\"tw-mt-3\">\n <div class=\"tw-text-sm tw-font-medium tw-text-gray-700 dark:tw-text-gray-200 tw-mb-2\">Preview:</div>\n <div class=\"tw-flex tw-flex-wrap tw-gap-3\">\n @for (previewUrl of previewUrls(); track previewUrl; let i = $index) {\n <div\n class=\"tw-relative tw-border tw-border-gray-200 dark:tw-border-gray-600 tw-rounded-lg tw-overflow-hidden tw-bg-white dark:tw-bg-gray-800\"\n [style.width]=\"previewWidthSignal()\" [style.height]=\"previewHeightSignal()\">\n <button type=\"button\"\n class=\"tw-absolute tw-top-1 tw-right-1 tw-w-5 tw-h-5 tw-bg-red-500 hover:tw-bg-red-600 tw-text-white tw-rounded-full tw-flex tw-items-center tw-justify-center tw-text-xs tw-font-bold tw-transition-colors tw-duration-200 tw-border-none tw-cursor-pointer tw-z-10\"\n (click)=\"removePreview(i)\" title=\"Remove image\">\n \u00D7\n </button>\n <img [src]=\"previewUrl\" [alt]=\"fileNames()[i] || 'Preview image'\" class=\"tw-w-full tw-h-full tw-object-cover\"\n loading=\"lazy\">\n <div\n class=\"tw-absolute tw-bottom-0 tw-left-0 tw-right-0 tw-bg-black tw-bg-opacity-75 tw-text-white tw-text-xs tw-p-1 tw-truncate\">\n {{ fileNames()[i] }}</div>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Upload Status and Show Files Button (only for multiple file inputs) -->\n @if (multiple && showFloatingUploaderSignal() && (getUploadCount() > 0 || hasActiveUploads() || hasEverUploaded())) {\n <div class=\"tw-flex tw-items-center tw-justify-between tw-py-1.5 tw-gap-2\">\n <div class=\"tw-flex tw-items-center tw-gap-2\">\n <cide-ele-icon class=\"tw-text-blue-600 dark:tw-text-blue-400\" size=\"sm\">cloud_upload</cide-ele-icon>\n <span class=\"tw-text-sm tw-text-gray-700 dark:tw-text-gray-300\">\n @if (hasActiveUploads()) {\n {{ getActiveUploadCount() }} uploading\n } @else if (getUploadCount() > 0) {\n {{ getUploadCount() }} completed\n } @else if (hasEverUploaded()) {\n View uploads\n }\n </span>\n </div>\n <button type=\"button\"\n class=\"tw-flex tw-items-center tw-justify-center tw-w-8 tw-h-8 tw-rounded-md tw-bg-gray-100 dark:tw-bg-gray-700 hover:tw-bg-gray-200 dark:hover:tw-bg-gray-600 tw-text-gray-600 dark:tw-text-gray-300 tw-transition-colors tw-duration-200 tw-border-none tw-cursor-pointer\"\n (click)=\"showFloatingUploaderDialog()\" title=\"View upload progress and history\">\n <cide-ele-icon size=\"sm\">visibility</cide-ele-icon>\n </button>\n </div>\n }\n\n @if (errorTextSignal()) {\n <div class=\"tw-text-sm tw-text-red-600 dark:tw-text-red-400 tw-mt-1\">{{ errorTextSignal() }}</div>\n }\n @if (isInErrorState() && failedFile() && !errorTextSignal()) {\n <div class=\"tw-flex tw-items-center tw-gap-2 tw-mt-1\">\n <span class=\"tw-text-sm tw-text-red-600 dark:tw-text-red-400\">Upload failed. Please retry.</span>\n <button type=\"button\"\n class=\"tw-flex tw-items-center tw-gap-1 tw-px-2 tw-py-1 tw-text-xs tw-font-medium tw-border-none tw-rounded tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white tw-cursor-pointer tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\"\n (click)=\"retryUpload()\" [disabled]=\"isUploading() || disabledSignal()\">\n <cide-ele-icon size=\"xs\">refresh</cide-ele-icon>\n Retry Upload\n </button>\n </div>\n }\n @if (helperTextSignal() && !errorTextSignal() && !isInErrorState()) {\n <div class=\"tw-text-sm tw-text-gray-500 dark:tw-text-gray-400 tw-mt-1\">{{ helperTextSignal() }}</div>\n }\n</div>", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }] });
|
|
5490
5541
|
}
|
|
5491
5542
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: CideEleFileInputComponent, decorators: [{
|
|
5492
5543
|
type: Component,
|
|
@@ -5501,7 +5552,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
5501
5552
|
useExisting: CideEleFileInputComponent,
|
|
5502
5553
|
multi: true
|
|
5503
5554
|
}
|
|
5504
|
-
], template: "<div class=\"tw-flex tw-flex-col tw-gap-2\">\n <!-- Label (shown when not in preview box mode or when preview box mode but no label override) -->\n @if (labelSignal() && !isPreviewBoxMode()) {\n <label class=\"tw-block tw-text-sm tw-font-medium tw-text-gray-700 dark:tw-text-gray-200 tw-mb-1.5 tw-leading-5\"\n [attr.for]=\"'cide-file-input-' + id()\">\n {{ labelSignal() }}@if (requiredSignal()) {<span class=\"tw-text-red-500 dark:tw-text-red-400\"> *</span>}\n </label>\n }\n\n <!-- Preview Box Mode -->\n @if (isPreviewBoxMode()) {\n <div class=\"tw-relative\">\n <!-- Hidden file input -->\n <input type=\"file\" [attr.id]=\"'cide-file-input-' + id()\" [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\" [disabled]=\"disabledSignal()\" (change)=\"onFileSelected($event)\"\n class=\"tw-hidden\" />\n\n <!-- Preview Box -->\n <div\n class=\"tw-border-2 tw-border-dashed tw-border-gray-300 dark:tw-border-gray-600 tw-rounded-lg tw-bg-gray-50 dark:tw-bg-gray-800 tw-cursor-pointer tw-transition-all tw-duration-200 tw-relative tw-overflow-hidden hover:tw-border-blue-500 hover:tw-bg-blue-50 dark:hover:tw-bg-blue-900/20\"\n [class]=\"getPreviewBoxClasses()\" [style.width]=\"previewWidthSignal()\" [style.height]=\"previewHeightSignal()\"\n (click)=\"triggerFileSelect()\" (dragover)=\"onDragOver($event)\" (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\" (drop)=\"onDrop($event)\"\n [attr.title]=\"disabledSignal() ? 'File selection disabled' : placeholderTextSignal()\">\n\n <!-- No Image State -->\n @if (!hasImages()) {\n <div class=\"tw-flex tw-flex-col tw-items-center tw-justify-center tw-h-full tw-p-4\">\n <div class=\"tw-mb-2\">\n <cide-ele-icon class=\"tw-text-gray-400 dark:tw-text-gray-500\" size=\"lg\">{{ isDragOver() ? '\uD83D\uDCC1' :\n placeholderIconSignal() }}</cide-ele-icon>\n </div>\n <div class=\"tw-text-sm tw-text-gray-600 dark:tw-text-gray-400 tw-text-center\">\n {{ isDragOver() ? 'Drop files here...' : placeholderTextSignal() }}\n </div>\n </div>\n }\n\n <!-- Image Preview State -->\n @if (hasImages()) {\n <div class=\"tw-relative tw-w-full tw-h-full\">\n <img [src]=\"previewUrls()[0]\" [alt]=\"fileNames()[0] || 'Preview image'\"\n class=\"tw-w-full tw-h-full tw-object-cover tw-rounded-lg\">\n <div\n class=\"tw-absolute tw-inset-0 tw-bg-black tw-bg-opacity-0 hover:tw-bg-opacity-30 tw-transition-all tw-duration-200 tw-flex tw-items-center tw-justify-center tw-rounded-lg\">\n <div class=\"tw-text-white tw-text-sm tw-opacity-0 hover:tw-opacity-100 tw-transition-opacity tw-duration-200\">\n Click to change</div>\n </div>\n @if (!disabledSignal()) {\n <button type=\"button\"\n class=\"tw-absolute tw-top-2 tw-right-2 tw-w-6 tw-h-6 tw-bg-red-500 hover:tw-bg-red-600 tw-text-white tw-rounded-full tw-flex tw-items-center tw-justify-center tw-text-sm tw-font-bold tw-transition-colors tw-duration-200 tw-border-none tw-cursor-pointer\"\n (click)=\"clearFiles(); $event.stopPropagation()\" title=\"Remove image\">\n \u00D7\n </button>\n }\n @if (isInErrorState() && failedFile()) {\n <button type=\"button\"\n class=\"tw-absolute tw-bottom-2 tw-left-1/2 tw-transform tw--translate-x-1/2 tw-px-3 tw-py-1.5 tw-text-xs tw-font-medium tw-border-none tw-rounded tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white tw-cursor-pointer tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed tw-shadow-lg tw-z-10\"\n (click)=\"retryUpload(); $event.stopPropagation()\" [disabled]=\"isUploading() || disabledSignal()\"\n title=\"Retry upload\">\n <cide-ele-icon size=\"xs\" class=\"tw-mr-1\">refresh</cide-ele-icon>\n Retry Upload\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=\"tw-mt-2 tw-text-sm tw-text-gray-600 dark:tw-text-gray-400 tw-text-center tw-truncate\">\n {{ fileNames()[0] }}\n </div>\n }\n <!-- Error message and retry button for preview box mode -->\n @if (isInErrorState() && failedFile() && !hasImages()) {\n <div class=\"tw-mt-2 tw-flex tw-flex-col tw-items-center tw-gap-2\">\n <span class=\"tw-text-sm tw-text-red-600 dark:tw-text-red-400\">Upload failed. Please retry.</span>\n <button type=\"button\"\n class=\"tw-flex tw-items-center tw-gap-1 tw-px-3 tw-py-1.5 tw-text-xs tw-font-medium tw-border-none tw-rounded tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white tw-cursor-pointer tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\"\n (click)=\"retryUpload()\" [disabled]=\"isUploading() || disabledSignal()\">\n <cide-ele-icon size=\"xs\">refresh</cide-ele-icon>\n Retry Upload\n </button>\n </div>\n }\n </div>\n }\n\n <!-- Standard Mode -->\n @if (!isPreviewBoxMode()) {\n <!-- Hidden file input -->\n <input type=\"file\" [attr.id]=\"'cide-file-input-' + id()\" [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\" [disabled]=\"disabledSignal()\" (change)=\"onFileSelected($event)\"\n class=\"tw-hidden\" />\n\n <!-- Modern Drag and Drop Zone -->\n <div\n class=\"tw-border-2 tw-border-dashed tw-border-gray-300 dark:tw-border-gray-600 tw-rounded-lg tw-bg-gray-50 dark:tw-bg-gray-800 tw-cursor-pointer tw-transition-all tw-duration-200 tw-min-h-[60px] hover:tw-border-blue-500 hover:tw-bg-blue-50 dark:hover:tw-bg-blue-900/20\"\n [class]=\"getDragDropZoneClasses()\" (click)=\"triggerFileSelect()\" (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\" (dragleave)=\"onDragLeave($event)\" (drop)=\"onDrop($event)\">\n\n <div class=\"tw-flex tw-items-center tw-justify-between tw-p-3 tw-gap-3\">\n <!-- Icon and Text -->\n <div class=\"tw-flex tw-items-center tw-gap-2.5 tw-flex-1 tw-min-w-0\">\n <cide-ele-icon class=\"tw-flex-shrink-0 tw-transition-colors tw-duration-200\" [class]=\"getIconClasses()\"\n size=\"sm\">\n {{ isDragOver() ? 'file_download' : (hasFiles() ? 'check_circle' : 'cloud_upload') }}\n </cide-ele-icon>\n\n <div class=\"tw-flex tw-flex-col tw-gap-0.5 tw-min-w-0\">\n @if (isDragOver()) {\n <span\n class=\"tw-text-sm tw-font-medium tw-text-blue-700 dark:tw-text-blue-300 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\">Drop\n files here</span>\n } @else if (hasFiles()) {\n <span\n class=\"tw-text-sm tw-font-medium tw-text-emerald-700 dark:tw-text-emerald-300 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\">\n @if (multipleSignal() && fileNames().length > 1) {\n {{ fileNames().length }} files selected\n } @else {\n {{ fileNames()[0] }}\n }\n </span>\n @if (totalFileSize() > 0) {\n <span class=\"tw-text-xs tw-text-emerald-600 dark:tw-text-emerald-400\">{{ fileSizeInMB() }} MB</span>\n }\n } @else {\n <span\n class=\"tw-text-sm tw-font-medium tw-text-gray-700 dark:tw-text-gray-300 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\">\n {{ multipleSignal() ? 'Choose files or drag here' : 'Choose file or drag here' }}\n </span>\n }\n </div>\n </div>\n\n <!-- Action Buttons -->\n <div class=\"tw-flex tw-gap-1 tw-flex-shrink-0\">\n <!-- Download Button -->\n @if (hasFiles() && downloadUrls().length > 0) {\n <button type=\"button\"\n class=\"tw-flex tw-items-center tw-justify-center tw-w-6 tw-h-6 tw-border-none tw-rounded tw-bg-transparent tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-blue-600 hover:tw-bg-blue-50 dark:hover:tw-bg-blue-900/20 hover:tw-text-blue-700\"\n (click)=\"downloadFile(0); $event.stopPropagation()\" title=\"Download file\">\n <cide-ele-icon size=\"xs\">download</cide-ele-icon>\n </button>\n }\n\n @if (isInErrorState() && failedFile()) {\n <button type=\"button\"\n class=\"tw-flex tw-items-center tw-justify-center tw-px-2 tw-py-1 tw-text-xs tw-font-medium tw-border-none tw-rounded tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white tw-cursor-pointer tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\"\n (click)=\"retryUpload(); $event.stopPropagation()\" [disabled]=\"isUploading() || disabledSignal()\"\n title=\"Retry upload\">\n <cide-ele-icon size=\"xs\" class=\"tw-mr-1\">refresh</cide-ele-icon>\n Retry\n </button>\n }\n @if (hasFiles()) {\n <button type=\"button\"\n class=\"tw-flex tw-items-center tw-justify-center tw-w-6 tw-h-6 tw-border-none tw-rounded tw-bg-transparent tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-red-600 hover:tw-bg-red-50 dark:hover:tw-bg-red-900/20 hover:tw-text-red-700\"\n (click)=\"clearFiles(); $event.stopPropagation()\" title=\"Clear files\">\n <cide-ele-icon size=\"xs\">close</cide-ele-icon>\n </button>\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Image Preview Section (only for standard mode) -->\n @if (isImagePreviewAvailable() && !isPreviewBoxMode()) {\n <div class=\"tw-mt-3\">\n <div class=\"tw-text-sm tw-font-medium tw-text-gray-700 dark:tw-text-gray-200 tw-mb-2\">Preview:</div>\n <div class=\"tw-flex tw-flex-wrap tw-gap-3\">\n @for (previewUrl of previewUrls(); track previewUrl; let i = $index) {\n <div\n class=\"tw-relative tw-border tw-border-gray-200 dark:tw-border-gray-600 tw-rounded-lg tw-overflow-hidden tw-bg-white dark:tw-bg-gray-800\"\n [style.width]=\"previewWidthSignal()\" [style.height]=\"previewHeightSignal()\">\n <button type=\"button\"\n class=\"tw-absolute tw-top-1 tw-right-1 tw-w-5 tw-h-5 tw-bg-red-500 hover:tw-bg-red-600 tw-text-white tw-rounded-full tw-flex tw-items-center tw-justify-center tw-text-xs tw-font-bold tw-transition-colors tw-duration-200 tw-border-none tw-cursor-pointer tw-z-10\"\n (click)=\"removePreview(i)\" title=\"Remove image\">\n \u00D7\n </button>\n <img [src]=\"previewUrl\" [alt]=\"fileNames()[i] || 'Preview image'\" class=\"tw-w-full tw-h-full tw-object-cover\"\n loading=\"lazy\">\n <div\n class=\"tw-absolute tw-bottom-0 tw-left-0 tw-right-0 tw-bg-black tw-bg-opacity-75 tw-text-white tw-text-xs tw-p-1 tw-truncate\">\n {{ fileNames()[i] }}</div>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Upload Status and Show Files Button (only for multiple file inputs) -->\n @if (multiple && showFloatingUploaderSignal() && (getUploadCount() > 0 || hasActiveUploads() || hasEverUploaded())) {\n <div class=\"tw-flex tw-items-center tw-justify-between tw-py-1.5 tw-gap-2\">\n <div class=\"tw-flex tw-items-center tw-gap-2\">\n <cide-ele-icon class=\"tw-text-blue-600 dark:tw-text-blue-400\" size=\"sm\">cloud_upload</cide-ele-icon>\n <span class=\"tw-text-sm tw-text-gray-700 dark:tw-text-gray-300\">\n @if (hasActiveUploads()) {\n {{ getActiveUploadCount() }} uploading\n } @else if (getUploadCount() > 0) {\n {{ getUploadCount() }} completed\n } @else if (hasEverUploaded()) {\n View uploads\n }\n </span>\n </div>\n <button type=\"button\"\n class=\"tw-flex tw-items-center tw-justify-center tw-w-8 tw-h-8 tw-rounded-md tw-bg-gray-100 dark:tw-bg-gray-700 hover:tw-bg-gray-200 dark:hover:tw-bg-gray-600 tw-text-gray-600 dark:tw-text-gray-300 tw-transition-colors tw-duration-200 tw-border-none tw-cursor-pointer\"\n (click)=\"showFloatingUploaderDialog()\" title=\"View upload progress and history\">\n <cide-ele-icon size=\"sm\">visibility</cide-ele-icon>\n </button>\n </div>\n }\n\n @if (errorTextSignal()) {\n <div class=\"tw-text-sm tw-text-red-600 dark:tw-text-red-400 tw-mt-1\">{{ errorTextSignal() }}</div>\n }\n @if (isInErrorState() && failedFile() && !errorTextSignal()) {\n <div class=\"tw-flex tw-items-center tw-gap-2 tw-mt-1\">\n <span class=\"tw-text-sm tw-text-red-600 dark:tw-text-red-400\">Upload failed. Please retry.</span>\n <button type=\"button\"\n class=\"tw-flex tw-items-center tw-gap-1 tw-px-2 tw-py-1 tw-text-xs tw-font-medium tw-border-none tw-rounded tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white tw-cursor-pointer tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\"\n (click)=\"retryUpload()\" [disabled]=\"isUploading() || disabledSignal()\">\n <cide-ele-icon size=\"xs\">refresh</cide-ele-icon>\n Retry Upload\n </button>\n </div>\n }\n @if (helperTextSignal() && !errorTextSignal() && !isInErrorState()) {\n <div class=\"tw-text-sm tw-text-gray-500 dark:tw-text-gray-400 tw-mt-1\">{{ helperTextSignal() }}</div>\n }\n</div>" }]
|
|
5555
|
+
], template: "<div class=\"tw-flex tw-flex-col tw-gap-2\">\n <!-- Label (shown when not in preview box mode or when preview box mode but no label override) -->\n @if (labelSignal() && !isPreviewBoxMode()) {\n <label class=\"tw-block tw-text-sm tw-font-medium tw-text-gray-700 dark:tw-text-gray-200 tw-mb-1.5 tw-leading-5\"\n [attr.for]=\"'cide-file-input-' + id()\">\n {{ labelSignal() }}@if (requiredSignal()) {<span class=\"tw-text-red-500 dark:tw-text-red-400\"> *</span>}\n </label>\n }\n\n <!-- Preview Box Mode -->\n @if (isPreviewBoxMode()) {\n <div class=\"tw-relative\">\n <!-- Hidden file input -->\n <input type=\"file\" [attr.id]=\"'cide-file-input-' + id()\" [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\" [disabled]=\"disabledSignal()\" (change)=\"onFileSelected($event)\"\n class=\"tw-hidden\" />\n\n <!-- Preview Box -->\n <div\n class=\"tw-border-2 tw-border-dashed tw-border-gray-300 dark:tw-border-gray-600 tw-rounded-lg tw-bg-gray-50 dark:tw-bg-gray-800 tw-cursor-pointer tw-transition-all tw-duration-200 tw-relative tw-overflow-hidden hover:tw-border-blue-500 hover:tw-bg-blue-50 dark:hover:tw-bg-blue-900/20\"\n [class]=\"getPreviewBoxClasses()\" [style.width]=\"previewWidthSignal()\" [style.height]=\"previewHeightSignal()\"\n (click)=\"triggerFileSelect()\" (dragover)=\"onDragOver($event)\" (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\" (drop)=\"onDrop($event)\"\n [attr.title]=\"disabledSignal() ? 'File selection disabled' : placeholderTextSignal()\">\n\n <!-- No Image State -->\n @if (!hasImages()) {\n <div class=\"tw-flex tw-flex-col tw-items-center tw-justify-center tw-h-full tw-p-4\">\n <div class=\"tw-mb-2\">\n <cide-ele-icon class=\"tw-text-gray-400 dark:tw-text-gray-500\" size=\"lg\">{{ isDragOver() ? '\uD83D\uDCC1' :\n placeholderIconSignal() }}</cide-ele-icon>\n </div>\n <div class=\"tw-text-sm tw-text-gray-600 dark:tw-text-gray-400 tw-text-center\">\n {{ isDragOver() ? 'Drop files here...' : placeholderTextSignal() }}\n </div>\n </div>\n }\n\n <!-- Image Preview State -->\n @if (hasImages()) {\n <div class=\"tw-relative tw-w-full tw-h-full\">\n <img [src]=\"previewUrls()[0]\" [alt]=\"fileNames()[0] || 'Preview image'\"\n class=\"tw-w-full tw-h-full tw-object-cover tw-rounded-lg\">\n <div\n class=\"tw-absolute tw-inset-0 tw-bg-black tw-bg-opacity-0 hover:tw-bg-opacity-30 tw-transition-all tw-duration-200 tw-flex tw-items-center tw-justify-center tw-rounded-lg\">\n <div class=\"tw-text-white tw-text-sm tw-opacity-0 hover:tw-opacity-100 tw-transition-opacity tw-duration-200\">\n Click to change</div>\n </div>\n @if (!disabledSignal()) {\n <button type=\"button\"\n class=\"tw-absolute tw-top-2 tw-right-2 tw-w-6 tw-h-6 tw-bg-red-500 hover:tw-bg-red-600 tw-text-white tw-rounded-full tw-flex tw-items-center tw-justify-center tw-text-sm tw-font-bold tw-transition-colors tw-duration-200 tw-border-none tw-cursor-pointer\"\n (click)=\"clearFiles(); $event.stopPropagation()\" title=\"Remove image\">\n \u00D7\n </button>\n }\n @if (isInErrorState() && failedFile()) {\n <button type=\"button\"\n class=\"tw-absolute tw-bottom-2 tw-left-1/2 tw-transform tw--translate-x-1/2 tw-px-3 tw-py-1.5 tw-text-xs tw-font-medium tw-border-none tw-rounded tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white tw-cursor-pointer tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed tw-shadow-lg tw-z-10\"\n (click)=\"retryUpload(); $event.stopPropagation()\" [disabled]=\"isUploading() || disabledSignal()\"\n title=\"Retry upload\">\n <cide-ele-icon size=\"xs\" class=\"tw-mr-1\">refresh</cide-ele-icon>\n Retry Upload\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=\"tw-mt-2 tw-text-sm tw-text-gray-600 dark:tw-text-gray-400 tw-text-center tw-truncate\">\n {{ fileNames()[0] }}\n </div>\n }\n <!-- Error message and retry button for preview box mode -->\n @if (isInErrorState() && failedFile() && !hasImages()) {\n <div class=\"tw-mt-2 tw-flex tw-flex-col tw-items-center tw-gap-2\">\n <span class=\"tw-text-sm tw-text-red-600 dark:tw-text-red-400\">Upload failed. Please retry.</span>\n <button type=\"button\"\n class=\"tw-flex tw-items-center tw-gap-1 tw-px-3 tw-py-1.5 tw-text-xs tw-font-medium tw-border-none tw-rounded tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white tw-cursor-pointer tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\"\n (click)=\"retryUpload()\" [disabled]=\"isUploading() || disabledSignal()\">\n <cide-ele-icon size=\"xs\">refresh</cide-ele-icon>\n Retry Upload\n </button>\n </div>\n }\n </div>\n }\n\n <!-- Standard Mode -->\n @if (!isPreviewBoxMode()) {\n <!-- Hidden file input -->\n <input type=\"file\" [attr.id]=\"'cide-file-input-' + id()\" [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\" [disabled]=\"disabledSignal()\" (change)=\"onFileSelected($event)\"\n class=\"tw-hidden\" />\n\n <!-- Modern Drag and Drop Zone -->\n <div\n class=\"tw-border-2 tw-border-dashed tw-border-gray-300 dark:tw-border-gray-600 tw-rounded-lg tw-bg-gray-50 dark:tw-bg-gray-800 tw-cursor-pointer tw-transition-all tw-duration-200 tw-min-h-[60px] hover:tw-border-blue-500 hover:tw-bg-blue-50 dark:hover:tw-bg-blue-900/20\"\n [class]=\"getDragDropZoneClasses()\" (click)=\"triggerFileSelect()\" (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\" (dragleave)=\"onDragLeave($event)\" (drop)=\"onDrop($event)\">\n\n <div class=\"tw-flex tw-items-center tw-justify-between tw-p-3 tw-gap-3\">\n <!-- Icon and Text -->\n <div class=\"tw-flex tw-items-center tw-gap-2.5 tw-flex-1 tw-min-w-0\">\n <cide-ele-icon class=\"tw-flex-shrink-0 tw-transition-colors tw-duration-200\" [class]=\"getIconClasses()\"\n size=\"sm\">\n {{ isDragOver() ? 'file_download' : (hasFiles() ? 'check_circle' : 'cloud_upload') }}\n </cide-ele-icon>\n\n <div class=\"tw-flex tw-flex-col tw-gap-0.5 tw-min-w-0\">\n @if (isDragOver()) {\n <span\n class=\"tw-text-sm tw-font-medium tw-text-blue-700 dark:tw-text-blue-300 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\">Drop\n files here</span>\n } @else if (hasFiles()) {\n <span\n class=\"tw-text-sm tw-font-medium tw-text-emerald-700 dark:tw-text-emerald-300 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\">\n @if (multipleSignal() && fileNames().length > 1) {\n {{ fileNames().length }} files selected\n } @else {\n {{ fileNames()[0] }}\n }\n </span>\n @if (totalFileSize() > 0) {\n <span class=\"tw-text-xs tw-text-emerald-600 dark:tw-text-emerald-400\">{{ fileSizeInMB() }} MB</span>\n }\n } @else {\n <span\n class=\"tw-text-sm tw-font-medium tw-text-gray-700 dark:tw-text-gray-300 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\">\n {{ multipleSignal() ? 'Choose files or drag here' : 'Choose file or drag here' }}\n </span>\n }\n </div>\n </div>\n\n <!-- Action Buttons -->\n <div class=\"tw-flex tw-gap-1 tw-flex-shrink-0\">\n <!-- Download Button -->\n @if (hasFiles() && downloadUrls().length > 0) {\n <button type=\"button\"\n class=\"tw-flex tw-items-center tw-justify-center tw-w-6 tw-h-6 tw-border-none tw-rounded tw-bg-transparent tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-purple-600 hover:tw-bg-purple-50 dark:hover:tw-bg-purple-900/20 hover:tw-text-purple-700 tw-mr-1\"\n (click)=\"viewFile(0); $event.stopPropagation()\" title=\"View file\">\n <cide-ele-icon size=\"xs\">visibility</cide-ele-icon>\n </button>\n <button type=\"button\"\n class=\"tw-flex tw-items-center tw-justify-center tw-w-6 tw-h-6 tw-border-none tw-rounded tw-bg-transparent tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-blue-600 hover:tw-bg-blue-50 dark:hover:tw-bg-blue-900/20 hover:tw-text-blue-700\"\n (click)=\"downloadFile(0); $event.stopPropagation()\" title=\"Download file\">\n <cide-ele-icon size=\"xs\">download</cide-ele-icon>\n </button>\n }\n\n @if (isInErrorState() && failedFile()) {\n <button type=\"button\"\n class=\"tw-flex tw-items-center tw-justify-center tw-px-2 tw-py-1 tw-text-xs tw-font-medium tw-border-none tw-rounded tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white tw-cursor-pointer tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\"\n (click)=\"retryUpload(); $event.stopPropagation()\" [disabled]=\"isUploading() || disabledSignal()\"\n title=\"Retry upload\">\n <cide-ele-icon size=\"xs\" class=\"tw-mr-1\">refresh</cide-ele-icon>\n Retry\n </button>\n }\n @if (hasFiles()) {\n <button type=\"button\"\n class=\"tw-flex tw-items-center tw-justify-center tw-w-6 tw-h-6 tw-border-none tw-rounded tw-bg-transparent tw-cursor-pointer tw-transition-all tw-duration-200 tw-text-red-600 hover:tw-bg-red-50 dark:hover:tw-bg-red-900/20 hover:tw-text-red-700\"\n (click)=\"clearFiles(); $event.stopPropagation()\" title=\"Clear files\">\n <cide-ele-icon size=\"xs\">close</cide-ele-icon>\n </button>\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Image Preview Section (only for standard mode) -->\n @if (isImagePreviewAvailable() && !isPreviewBoxMode()) {\n <div class=\"tw-mt-3\">\n <div class=\"tw-text-sm tw-font-medium tw-text-gray-700 dark:tw-text-gray-200 tw-mb-2\">Preview:</div>\n <div class=\"tw-flex tw-flex-wrap tw-gap-3\">\n @for (previewUrl of previewUrls(); track previewUrl; let i = $index) {\n <div\n class=\"tw-relative tw-border tw-border-gray-200 dark:tw-border-gray-600 tw-rounded-lg tw-overflow-hidden tw-bg-white dark:tw-bg-gray-800\"\n [style.width]=\"previewWidthSignal()\" [style.height]=\"previewHeightSignal()\">\n <button type=\"button\"\n class=\"tw-absolute tw-top-1 tw-right-1 tw-w-5 tw-h-5 tw-bg-red-500 hover:tw-bg-red-600 tw-text-white tw-rounded-full tw-flex tw-items-center tw-justify-center tw-text-xs tw-font-bold tw-transition-colors tw-duration-200 tw-border-none tw-cursor-pointer tw-z-10\"\n (click)=\"removePreview(i)\" title=\"Remove image\">\n \u00D7\n </button>\n <img [src]=\"previewUrl\" [alt]=\"fileNames()[i] || 'Preview image'\" class=\"tw-w-full tw-h-full tw-object-cover\"\n loading=\"lazy\">\n <div\n class=\"tw-absolute tw-bottom-0 tw-left-0 tw-right-0 tw-bg-black tw-bg-opacity-75 tw-text-white tw-text-xs tw-p-1 tw-truncate\">\n {{ fileNames()[i] }}</div>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Upload Status and Show Files Button (only for multiple file inputs) -->\n @if (multiple && showFloatingUploaderSignal() && (getUploadCount() > 0 || hasActiveUploads() || hasEverUploaded())) {\n <div class=\"tw-flex tw-items-center tw-justify-between tw-py-1.5 tw-gap-2\">\n <div class=\"tw-flex tw-items-center tw-gap-2\">\n <cide-ele-icon class=\"tw-text-blue-600 dark:tw-text-blue-400\" size=\"sm\">cloud_upload</cide-ele-icon>\n <span class=\"tw-text-sm tw-text-gray-700 dark:tw-text-gray-300\">\n @if (hasActiveUploads()) {\n {{ getActiveUploadCount() }} uploading\n } @else if (getUploadCount() > 0) {\n {{ getUploadCount() }} completed\n } @else if (hasEverUploaded()) {\n View uploads\n }\n </span>\n </div>\n <button type=\"button\"\n class=\"tw-flex tw-items-center tw-justify-center tw-w-8 tw-h-8 tw-rounded-md tw-bg-gray-100 dark:tw-bg-gray-700 hover:tw-bg-gray-200 dark:hover:tw-bg-gray-600 tw-text-gray-600 dark:tw-text-gray-300 tw-transition-colors tw-duration-200 tw-border-none tw-cursor-pointer\"\n (click)=\"showFloatingUploaderDialog()\" title=\"View upload progress and history\">\n <cide-ele-icon size=\"sm\">visibility</cide-ele-icon>\n </button>\n </div>\n }\n\n @if (errorTextSignal()) {\n <div class=\"tw-text-sm tw-text-red-600 dark:tw-text-red-400 tw-mt-1\">{{ errorTextSignal() }}</div>\n }\n @if (isInErrorState() && failedFile() && !errorTextSignal()) {\n <div class=\"tw-flex tw-items-center tw-gap-2 tw-mt-1\">\n <span class=\"tw-text-sm tw-text-red-600 dark:tw-text-red-400\">Upload failed. Please retry.</span>\n <button type=\"button\"\n class=\"tw-flex tw-items-center tw-gap-1 tw-px-2 tw-py-1 tw-text-xs tw-font-medium tw-border-none tw-rounded tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white tw-cursor-pointer tw-transition-all tw-duration-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\"\n (click)=\"retryUpload()\" [disabled]=\"isUploading() || disabledSignal()\">\n <cide-ele-icon size=\"xs\">refresh</cide-ele-icon>\n Retry Upload\n </button>\n </div>\n }\n @if (helperTextSignal() && !errorTextSignal() && !isInErrorState()) {\n <div class=\"tw-text-sm tw-text-gray-500 dark:tw-text-gray-400 tw-mt-1\">{{ helperTextSignal() }}</div>\n }\n</div>" }]
|
|
5505
5556
|
}], ctorParameters: () => [], propDecorators: { label: [{
|
|
5506
5557
|
type: Input
|
|
5507
5558
|
}], accept: [{
|