cloud-ide-element 1.0.99 → 1.0.102
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 +111 -54
- package/fesm2022/cloud-ide-element.mjs.map +1 -1
- package/index.d.ts +13 -2
- package/package.json +1 -1
|
@@ -2676,6 +2676,8 @@ class CideEleFileManagerService {
|
|
|
2676
2676
|
this._error.set(null);
|
|
2677
2677
|
this.addToUploadQueue(fileId);
|
|
2678
2678
|
console.log('📤 [FileManagerService] Starting upload for:', file.name, 'Size:', file.size, 'bytes');
|
|
2679
|
+
console.log('📤 [FileManagerService] Upload options:', uploadOptions);
|
|
2680
|
+
console.log('📤 [FileManagerService] File ID:', fileId, 'Group ID:', uploadOptions?.groupId);
|
|
2679
2681
|
return new Observable(observer => {
|
|
2680
2682
|
const reader = new FileReader();
|
|
2681
2683
|
// Angular 20: Enhanced progress tracking with signals
|
|
@@ -2868,8 +2870,21 @@ class CideEleFileManagerService {
|
|
|
2868
2870
|
updateUploadProgress(fileId, progress, groupId) {
|
|
2869
2871
|
const currentUploads = new Map(this._activeUploads());
|
|
2870
2872
|
const progressWithGroupId = { ...progress, groupId };
|
|
2873
|
+
console.log('📊 [FileManagerService] Updating upload progress:', {
|
|
2874
|
+
fileId,
|
|
2875
|
+
stage: progress.stage,
|
|
2876
|
+
percentage: progress.percentage,
|
|
2877
|
+
groupId: groupId,
|
|
2878
|
+
progressWithGroupId
|
|
2879
|
+
});
|
|
2871
2880
|
currentUploads.set(fileId, progressWithGroupId);
|
|
2872
2881
|
this._activeUploads.set(currentUploads);
|
|
2882
|
+
// Debug: Show current active uploads with their group IDs
|
|
2883
|
+
console.log('📊 [FileManagerService] Current active uploads:', Array.from(currentUploads.entries()).map(([id, upload]) => ({
|
|
2884
|
+
fileId: id,
|
|
2885
|
+
stage: upload.stage,
|
|
2886
|
+
groupId: upload.groupId
|
|
2887
|
+
})));
|
|
2873
2888
|
}
|
|
2874
2889
|
removeActiveUpload(fileId) {
|
|
2875
2890
|
const currentUploads = new Map(this._activeUploads());
|
|
@@ -3310,21 +3325,33 @@ class CideEleFloatingFileUploaderComponent {
|
|
|
3310
3325
|
activeUploadsLocal = computed(() => Array.from(this.activeUploads().values()).filter(upload => upload.stage === 'reading' || upload.stage === 'uploading'), ...(ngDevMode ? [{ debugName: "activeUploadsLocal" }] : []));
|
|
3311
3326
|
completedUploads = computed(() => Array.from(this.activeUploads().values()).filter(upload => upload.stage === 'complete'), ...(ngDevMode ? [{ debugName: "completedUploads" }] : []));
|
|
3312
3327
|
failedUploads = computed(() => Array.from(this.activeUploads().values()).filter(upload => upload.stage === 'error'), ...(ngDevMode ? [{ debugName: "failedUploads" }] : []));
|
|
3328
|
+
// Get all files for the current group (computed property for reactivity)
|
|
3329
|
+
allFilesForGroup = computed(() => {
|
|
3330
|
+
const groupId = this.currentGroupId();
|
|
3331
|
+
if (!groupId) {
|
|
3332
|
+
console.log('🔍 [FloatingFileUploader] No group ID set, returning empty array');
|
|
3333
|
+
return [];
|
|
3334
|
+
}
|
|
3335
|
+
const files = this.fileManagerService.getAllFilesForGroup(groupId);
|
|
3336
|
+
console.log('🔍 [FloatingFileUploader] allFilesForGroup computed:', {
|
|
3337
|
+
groupId,
|
|
3338
|
+
filesCount: files.length,
|
|
3339
|
+
files: files.map(f => ({ id: f.fileId, name: f.fileName, stage: f.stage }))
|
|
3340
|
+
});
|
|
3341
|
+
return files;
|
|
3342
|
+
}, ...(ngDevMode ? [{ debugName: "allFilesForGroup" }] : []));
|
|
3313
3343
|
// Check if there are any files to display (active uploads OR fetched files for current group)
|
|
3314
3344
|
hasFilesToShow = computed(() => {
|
|
3315
3345
|
const hasActiveUploads = this.hasActiveUploads();
|
|
3316
|
-
const allFiles = this.
|
|
3346
|
+
const allFiles = this.allFilesForGroup();
|
|
3317
3347
|
const hasFiles = allFiles.length > 0;
|
|
3318
3348
|
const currentGroupId = this.currentGroupId();
|
|
3319
|
-
const fetchedFiles = currentGroupId ? this.fileManagerService.getFetchedFilesByGroupId(currentGroupId) : [];
|
|
3320
3349
|
console.log('🔍 [FloatingFileUploader] hasFilesToShow check:', {
|
|
3321
3350
|
hasActiveUploads,
|
|
3322
3351
|
allFilesCount: allFiles.length,
|
|
3323
3352
|
hasFiles,
|
|
3324
3353
|
currentGroupId,
|
|
3325
|
-
|
|
3326
|
-
fetchedFiles: fetchedFiles.map(f => ({ id: f.cyfm_id || f.id, name: f.cyfm_name || f.name })),
|
|
3327
|
-
allFiles: allFiles.map(f => ({ id: f.fileId, name: f.fileName, stage: f.stage }))
|
|
3354
|
+
result: hasActiveUploads || hasFiles
|
|
3328
3355
|
});
|
|
3329
3356
|
return hasActiveUploads || hasFiles;
|
|
3330
3357
|
}, ...(ngDevMode ? [{ debugName: "hasFilesToShow" }] : []));
|
|
@@ -3611,35 +3638,10 @@ class CideEleFloatingFileUploaderComponent {
|
|
|
3611
3638
|
}
|
|
3612
3639
|
/**
|
|
3613
3640
|
* Get all files from service state (pending + active uploads + fetched files)
|
|
3641
|
+
* This method now uses the computed property for consistency
|
|
3614
3642
|
*/
|
|
3615
3643
|
getAllFiles() {
|
|
3616
|
-
|
|
3617
|
-
if (groupId) {
|
|
3618
|
-
// Use service method to get all files for this group
|
|
3619
|
-
return this.fileManagerService.getAllFilesForGroup(groupId);
|
|
3620
|
-
}
|
|
3621
|
-
// Fallback to general files if no group ID
|
|
3622
|
-
const files = [];
|
|
3623
|
-
// Add pending files
|
|
3624
|
-
this.uploadQueue().forEach(fileId => {
|
|
3625
|
-
if (!this.activeUploads().has(fileId)) {
|
|
3626
|
-
files.push({
|
|
3627
|
-
fileId,
|
|
3628
|
-
fileName: this.getFileNameFromId(fileId),
|
|
3629
|
-
stage: 'pending'
|
|
3630
|
-
});
|
|
3631
|
-
}
|
|
3632
|
-
});
|
|
3633
|
-
// Add active uploads
|
|
3634
|
-
this.activeUploads().forEach((upload, fileId) => {
|
|
3635
|
-
files.push({
|
|
3636
|
-
fileId,
|
|
3637
|
-
fileName: this.getFileNameFromId(fileId),
|
|
3638
|
-
stage: upload.stage,
|
|
3639
|
-
percentage: upload.percentage
|
|
3640
|
-
});
|
|
3641
|
-
});
|
|
3642
|
-
return files;
|
|
3644
|
+
return this.allFilesForGroup();
|
|
3643
3645
|
}
|
|
3644
3646
|
/**
|
|
3645
3647
|
* Set current user ID
|
|
@@ -3695,7 +3697,14 @@ class CideEleFloatingFileUploaderComponent {
|
|
|
3695
3697
|
}
|
|
3696
3698
|
this.currentGroupId.set(groupId);
|
|
3697
3699
|
console.log('🆔 [FloatingFileUploader] Set group ID:', groupId);
|
|
3698
|
-
//
|
|
3700
|
+
// Check if we already have files for this group
|
|
3701
|
+
const existingFiles = this.fileManagerService.getAllFilesForGroup(groupId);
|
|
3702
|
+
console.log('🔍 [FloatingFileUploader] Existing files for group:', {
|
|
3703
|
+
groupId,
|
|
3704
|
+
existingFilesCount: existingFiles.length,
|
|
3705
|
+
files: existingFiles.map(f => ({ id: f.fileId, name: f.fileName, stage: f.stage }))
|
|
3706
|
+
});
|
|
3707
|
+
// Always fetch fresh data from API to ensure we have the latest files
|
|
3699
3708
|
this.fileManagerService.fetchAndStoreFilesByGroupId(groupId)
|
|
3700
3709
|
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
3701
3710
|
.subscribe({
|
|
@@ -3703,12 +3712,13 @@ class CideEleFloatingFileUploaderComponent {
|
|
|
3703
3712
|
console.log('✅ [FloatingFileUploader] Files fetched and stored:', files.length);
|
|
3704
3713
|
// Force show the uploader after files are loaded
|
|
3705
3714
|
this.showWithAnimation();
|
|
3706
|
-
// Debug: Check what files are available now
|
|
3715
|
+
// Debug: Check what files are available now using computed property
|
|
3707
3716
|
setTimeout(() => {
|
|
3708
|
-
const allFiles = this.
|
|
3709
|
-
console.log('🔍 [FloatingFileUploader] Files available after fetch:', {
|
|
3717
|
+
const allFiles = this.allFilesForGroup();
|
|
3718
|
+
console.log('🔍 [FloatingFileUploader] Files available after fetch (computed):', {
|
|
3710
3719
|
allFilesCount: allFiles.length,
|
|
3711
|
-
files: allFiles.map(f => ({ id: f.fileId, name: f.fileName, stage: f.stage }))
|
|
3720
|
+
files: allFiles.map(f => ({ id: f.fileId, name: f.fileName, stage: f.stage })),
|
|
3721
|
+
hasFilesToShow: this.hasFilesToShow()
|
|
3712
3722
|
});
|
|
3713
3723
|
}, 100);
|
|
3714
3724
|
},
|
|
@@ -3716,6 +3726,15 @@ class CideEleFloatingFileUploaderComponent {
|
|
|
3716
3726
|
console.error('❌ [FloatingFileUploader] Failed to fetch files:', error);
|
|
3717
3727
|
// Still show the uploader even if fetch fails
|
|
3718
3728
|
this.showWithAnimation();
|
|
3729
|
+
// Debug existing files even on error
|
|
3730
|
+
setTimeout(() => {
|
|
3731
|
+
const allFiles = this.allFilesForGroup();
|
|
3732
|
+
console.log('🔍 [FloatingFileUploader] Files available after error (computed):', {
|
|
3733
|
+
allFilesCount: allFiles.length,
|
|
3734
|
+
files: allFiles.map(f => ({ id: f.fileId, name: f.fileName, stage: f.stage })),
|
|
3735
|
+
hasFilesToShow: this.hasFilesToShow()
|
|
3736
|
+
});
|
|
3737
|
+
}, 100);
|
|
3719
3738
|
}
|
|
3720
3739
|
});
|
|
3721
3740
|
}
|
|
@@ -3930,14 +3949,14 @@ class CideEleFloatingFileUploaderComponent {
|
|
|
3930
3949
|
}, 100);
|
|
3931
3950
|
}
|
|
3932
3951
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFloatingFileUploaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3933
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.7", type: CideEleFloatingFileUploaderComponent, isStandalone: true, selector: "cide-ele-floating-file-uploader", ngImport: i0, template: "<!-- Floating File Uploader Container -->\n@if (isVisible()) {\n<div class=\"floating-uploader\" \n [class.minimized]=\"isMinimized()\" \n [class.animating]=\"isAnimating()\"\n [style.left.px]=\"position().x\"\n [style.top.px]=\"position().y\">\n\n <!-- Header (Draggable) -->\n <div class=\"uploader-header draggable-header\"\n (mousedown)=\"startDrag($event)\"\n (touchstart)=\"startDrag($event)\">\n <div class=\"header-left\">\n <div class=\"upload-icon\">\n <cide-ele-icon size=\"sm\">cloud_upload</cide-ele-icon>\n </div>\n <div class=\"upload-info\">\n <div class=\"upload-title\">File Upload</div>\n <div class=\"upload-summary\">{{ getUploadSummary() }}</div>\n </div>\n </div>\n \n <div class=\"header-actions\">\n <button class=\"action-btn minimize-btn\" (click)=\"toggleMinimize()\" [title]=\"isMinimized() ? 'Expand' : 'Minimize'\">\n <cide-ele-icon size=\"xs\">{{ isMinimized() ? 'expand_more' : 'expand_less' }}</cide-ele-icon>\n </button>\n <button class=\"action-btn close-btn\" (click)=\"close()\" title=\"Close\">\n <cide-ele-icon size=\"xs\">close</cide-ele-icon>\n </button>\n </div>\n </div>\n\n <!-- Content (hidden when minimized) -->\n @if (!isMinimized()) {\n <div class=\"uploader-content\">\n \n <!-- Drag and Drop Zone -->\n <div class=\"upload-zone\" \n [class.drag-over]=\"isDragOver()\" \n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\" \n (drop)=\"onDrop($event)\" \n (click)=\"triggerFileInput()\">\n \n <!-- Hidden file input -->\n <input #fileInput \n type=\"file\" \n [multiple]=\"true\" \n [accept]=\"'*/*'\" \n (change)=\"onFileInputChange($event)\" \n style=\"display: none;\">\n \n <div class=\"upload-zone-content\">\n <cide-ele-icon class=\"upload-icon\" size=\"sm\">cloud_upload</cide-ele-icon>\n \n <div class=\"upload-text\">\n <div class=\"upload-title\">\n {{ isDragOver() ? 'Drop files here' : 'Drag files here or click to browse' }}\n </div>\n </div>\n </div>\n </div>\n \n <!-- Upload Queue - Show files from service state -->\n @if (getAllFiles().length > 0) {\n <div class=\"upload-queue\">\n <!-- Show all files from service state -->\n @for (file of getAllFiles(); track file.fileId) {\n <div class=\"upload-item\" [class]=\"getStatusClass(file.stage)\">\n <div class=\"file-info\">\n <cide-ele-icon class=\"status-icon\" size=\"xs\">{{ getStatusIcon(file.stage) }}</cide-ele-icon>\n <div class=\"file-details\">\n <div class=\"file-name\">{{ file.fileName }}</div>\n <div class=\"file-status\">\n @switch (file.stage) {\n @case ('pending') {\n <span class=\"text-yellow-600\">Waiting...</span>\n }\n @case ('reading') {\n <span class=\"text-yellow-600\">Reading...</span>\n }\n @case ('uploading') {\n <span class=\"text-blue-600\">Uploading...</span>\n }\n @case ('complete') {\n <span class=\"text-green-600\">Completed</span>\n }\n @case ('error') {\n <span class=\"text-red-600\">Failed</span>\n }\n }\n </div>\n </div>\n </div>\n\n <!-- Progress Bar (only for uploading files) -->\n @if (file.stage === 'uploading' && file.percentage !== undefined) {\n <div class=\"file-progress\">\n <div class=\"progress-bar\">\n <div class=\"progress-fill\" [style.width.%]=\"file.percentage\"></div>\n </div>\n <span class=\"progress-text\">{{ file.percentage }}%</span>\n </div>\n }\n\n <!-- Actions -->\n <div class=\"upload-actions\">\n @switch (file.stage) {\n @case ('pending') {\n <button class=\"action-btn cancel-btn\" (click)=\"cancelUpload(file.fileId)\" title=\"Cancel\">\n <cide-ele-icon size=\"xs\">cancel</cide-ele-icon>\n </button>\n }\n @case ('reading') {\n <button class=\"action-btn cancel-btn\" (click)=\"cancelUpload(file.fileId)\" title=\"Cancel\">\n <cide-ele-icon size=\"xs\">cancel</cide-ele-icon>\n </button>\n }\n @case ('uploading') {\n <button class=\"action-btn cancel-btn\" (click)=\"cancelUpload(file.fileId)\" title=\"Cancel\">\n <cide-ele-icon size=\"xs\">cancel</cide-ele-icon>\n </button>\n }\n @case ('complete') {\n <button class=\"action-btn success-btn\" title=\"Completed\">\n <cide-ele-icon size=\"xs\">check_circle</cide-ele-icon>\n </button>\n }\n @case ('error') {\n <button class=\"action-btn retry-btn\" title=\"Retry\">\n <cide-ele-icon size=\"xs\">refresh</cide-ele-icon>\n </button>\n }\n }\n </div>\n </div>\n }\n </div>\n } @else {\n <!-- No uploads message when manually opened -->\n <div class=\"no-uploads-message\">\n <div class=\"message-content\">\n <cide-ele-icon size=\"md\" class=\"message-icon\">cloud_upload</cide-ele-icon>\n <div class=\"message-text\">\n <h4>No active uploads</h4>\n <p>Upload files to see their progress here</p>\n </div>\n </div>\n </div>\n }\n </div>\n }\n</div>\n}\n", styles: [".floating-uploader{position:fixed;width:320px;max-height:500px;background:#fff;border-radius:12px;box-shadow:0 8px 32px #0000001f;border:1px solid rgba(0,0,0,.08);z-index:1000;overflow:hidden;transition:all .3s cubic-bezier(.4,0,.2,1);transform:translateY(0);opacity:1}.floating-uploader.animating{transition:all .3s cubic-bezier(.4,0,.2,1)}.floating-uploader.minimized .uploader-content{display:none}.floating-uploader.minimized .uploader-footer{border-top:none}.floating-uploader.uploading{border-color:#3b82f6;box-shadow:0 8px 32px #3b82f626}.floating-uploader .uploader-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;background:#f8fafc;border-bottom:1px solid #e2e8f0}.floating-uploader .uploader-header.draggable-header{cursor:move;-webkit-user-select:none;user-select:none}.floating-uploader .uploader-header.draggable-header:hover{background:#f1f5f9}.floating-uploader .uploader-header.draggable-header:active{background:#e2e8f0;cursor:grabbing}.floating-uploader .uploader-header .header-left{display:flex;align-items:center;gap:8px}.floating-uploader .uploader-header .header-left .upload-icon{display:flex;align-items:center;justify-content:center;width:24px;height:24px;background:#3b82f6;border-radius:6px;color:#fff}.floating-uploader .uploader-header .header-left .upload-info .upload-title{font-size:14px;font-weight:600;color:#1e293b;margin:0}.floating-uploader .uploader-header .header-left .upload-info .upload-summary{font-size:12px;color:#64748b;margin:0}.floating-uploader .uploader-header .header-actions{display:flex;gap:4px}.floating-uploader .uploader-header .header-actions .action-btn{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border:none;background:transparent;border-radius:4px;cursor:pointer;transition:background-color .2s;color:#64748b}.floating-uploader .uploader-header .header-actions .action-btn:hover{background:#e2e8f0;color:#1e293b}.floating-uploader .uploader-header .header-actions .action-btn.close-btn:hover{background:#fef2f2;color:#dc2626}.floating-uploader .uploader-content{max-height:400px;overflow-y:auto}.floating-uploader .uploader-content .upload-zone{margin:8px 16px;padding:12px;border:2px dashed #d1d5db;border-radius:6px;background:#f9fafb;cursor:pointer;transition:all .2s ease;text-align:center}.floating-uploader .uploader-content .upload-zone:hover{border-color:#3b82f6;background:#f0f9ff}.floating-uploader .uploader-content .upload-zone.drag-over{border-color:#3b82f6;background:#dbeafe;transform:scale(1.01)}.floating-uploader .uploader-content .upload-zone .upload-zone-content{display:flex;flex-direction:column;align-items:center;gap:6px}.floating-uploader .uploader-content .upload-zone .upload-zone-content .upload-icon{color:#6b7280;transition:color .2s ease}.floating-uploader .uploader-content .upload-zone .upload-zone-content .upload-text .upload-title{font-size:13px;font-weight:500;color:#374151;margin:0;line-height:1.2}.floating-uploader .uploader-content .upload-zone:hover .upload-zone-content .upload-icon{color:#3b82f6}.floating-uploader .uploader-content .upload-zone.drag-over .upload-zone-content .upload-icon{color:#1d4ed8}.floating-uploader .uploader-content .upload-queue .upload-item{display:flex;align-items:center;padding:8px 16px;border-bottom:1px solid #f1f5f9;transition:background-color .2s}.floating-uploader .uploader-content .upload-queue .upload-item:last-child{border-bottom:none}.floating-uploader .uploader-content .upload-queue .upload-item.status-uploading{background:#f0f9ff}.floating-uploader .uploader-content .upload-queue .upload-item.status-completed{background:#f0fdf4}.floating-uploader .uploader-content .upload-queue .upload-item.status-error{background:#fef2f2}.floating-uploader .uploader-content .upload-queue .upload-item .file-info{display:flex;align-items:center;gap:8px;flex:1;min-width:0}.floating-uploader .uploader-content .upload-queue .upload-item .file-info .status-icon{flex-shrink:0}.floating-uploader .uploader-content .upload-queue .upload-item .file-info .file-details{min-width:0;flex:1}.floating-uploader .uploader-content .upload-queue .upload-item .file-info .file-details .file-name{font-size:13px;font-weight:500;color:#1e293b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin:0}.floating-uploader .uploader-content .upload-queue .upload-item .file-info .file-details .file-status{font-size:11px;margin:0}.floating-uploader .uploader-content .upload-queue .upload-item .file-info .file-details .file-status span{font-weight:500}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress{display:flex;align-items:center;gap:8px;margin:0 8px;min-width:80px}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress .progress-bar{flex:1;height:3px;background:#e2e8f0;border-radius:2px;overflow:hidden}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress .progress-bar .progress-fill{height:100%;background:#3b82f6;transition:width .3s ease}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress .progress-text{font-size:10px;color:#64748b;min-width:24px;text-align:right}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions{display:flex;gap:4px}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn{display:flex;align-items:center;justify-content:center;width:20px;height:20px;border:none;background:transparent;border-radius:4px;cursor:pointer;transition:all .2s;color:#64748b}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn:hover{background:#e2e8f0}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn.cancel-btn:hover{background:#fef2f2;color:#dc2626}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn.retry-btn:hover{background:#f0f9ff;color:#3b82f6}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn.success-btn{color:#16a34a}.floating-uploader .uploader-content .hidden-uploader{display:none}.floating-uploader .uploader-footer{padding:8px 16px;background:#f8fafc;border-top:1px solid #e2e8f0}.floating-uploader .uploader-footer .footer-stats{display:flex;gap:12px;font-size:11px}.floating-uploader .uploader-footer .footer-stats .stat{display:flex;align-items:center;gap:4px;color:#64748b}.floating-uploader .uploader-footer .footer-stats .stat.uploading{color:#3b82f6}.floating-uploader .uploader-footer .footer-stats .stat.completed{color:#16a34a}.floating-uploader .uploader-footer .footer-stats .stat.failed{color:#dc2626}@media (max-width: 640px){.floating-uploader{bottom:10px;right:10px;left:10px;width:auto;max-width:none}}@media (prefers-color-scheme: dark){.floating-uploader{background:#1e293b;border-color:#334155;box-shadow:0 8px 32px #0000004d}.floating-uploader.uploading{border-color:#3b82f6;box-shadow:0 8px 32px #3b82f633}.floating-uploader .uploader-header{background:#334155;border-bottom-color:#475569}.floating-uploader .uploader-header.draggable-header:hover{background:#475569}.floating-uploader .uploader-header.draggable-header:active{background:#64748b}.floating-uploader .uploader-header .header-left .upload-icon{background:#3b82f6}.floating-uploader .uploader-header .header-left .upload-info .upload-title{color:#f1f5f9}.floating-uploader .uploader-header .header-left .upload-info .upload-summary,.floating-uploader .uploader-header .header-actions .action-btn{color:#94a3b8}.floating-uploader .uploader-header .header-actions .action-btn:hover{background:#475569;color:#f1f5f9}.floating-uploader .uploader-header .header-actions .action-btn.close-btn:hover{background:#7f1d1d;color:#fca5a5}.floating-uploader .uploader-content .upload-zone{border-color:#475569;background:#334155}.floating-uploader .uploader-content .upload-zone:hover{border-color:#3b82f6;background:#1e3a8a}.floating-uploader .uploader-content .upload-zone.drag-over{border-color:#60a5fa;background:#1e40af}.floating-uploader .uploader-content .upload-zone .upload-zone-content .upload-icon{color:#94a3b8}.floating-uploader .uploader-content .upload-zone .upload-zone-content .upload-text .upload-title{color:#f1f5f9}.floating-uploader .uploader-content .upload-zone:hover .upload-zone-content .upload-icon{color:#60a5fa}.floating-uploader .uploader-content .upload-zone.drag-over .upload-zone-content .upload-icon{color:#93c5fd}.floating-uploader .uploader-content .upload-queue .upload-item{border-bottom-color:#334155}.floating-uploader .uploader-content .upload-queue .upload-item.status-uploading{background:#1e3a8a}.floating-uploader .uploader-content .upload-queue .upload-item.status-completed{background:#14532d}.floating-uploader .uploader-content .upload-queue .upload-item.status-error{background:#7f1d1d}.floating-uploader .uploader-content .upload-queue .upload-item .file-info .file-details .file-name{color:#f1f5f9}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress .progress-bar{background:#475569}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress .progress-bar .progress-fill{background:#3b82f6}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress .progress-text,.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn{color:#94a3b8}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn:hover{background:#475569}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn.cancel-btn:hover{background:#7f1d1d;color:#fca5a5}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn.retry-btn:hover{background:#1e3a8a;color:#60a5fa}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn.success-btn{color:#4ade80}.floating-uploader .uploader-footer{background:#334155;border-top-color:#475569}.floating-uploader .uploader-footer .footer-stats .stat{color:#94a3b8}.floating-uploader .uploader-footer .footer-stats .stat.uploading{color:#60a5fa}.floating-uploader .uploader-footer .footer-stats .stat.completed{color:#4ade80}.floating-uploader .uploader-footer .footer-stats .stat.failed{color:#fca5a5}}@keyframes slideInUp{0%{transform:translateY(100%);opacity:0}to{transform:translateY(0);opacity:1}}@keyframes slideOutDown{0%{transform:translateY(0);opacity:1}to{transform:translateY(100%);opacity:0}}.floating-uploader.animating{animation:slideInUp .3s cubic-bezier(.4,0,.2,1)}.floating-uploader.animating.hiding{animation:slideOutDown .3s cubic-bezier(.4,0,.2,1)}.no-uploads-message{padding:2rem;text-align:center;color:#6b7280}.no-uploads-message .message-content{display:flex;flex-direction:column;align-items:center;gap:1rem}.no-uploads-message .message-content .message-icon{color:#9ca3af;opacity:.7}.no-uploads-message .message-content .message-text h4{margin:0 0 .5rem;font-size:1.1rem;font-weight:600;color:#374151}.no-uploads-message .message-content .message-text p{margin:0;font-size:.9rem;color:#6b7280}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }] });
|
|
3952
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.7", type: CideEleFloatingFileUploaderComponent, isStandalone: true, selector: "cide-ele-floating-file-uploader", ngImport: i0, template: "<!-- Floating File Uploader Container -->\n@if (isVisible()) {\n<div class=\"floating-uploader\" \n [class.minimized]=\"isMinimized()\" \n [class.animating]=\"isAnimating()\"\n [style.left.px]=\"position().x\"\n [style.top.px]=\"position().y\">\n\n <!-- Header (Draggable) -->\n <div class=\"uploader-header draggable-header\"\n (mousedown)=\"startDrag($event)\"\n (touchstart)=\"startDrag($event)\">\n <div class=\"header-left\">\n <div class=\"upload-icon\">\n <cide-ele-icon size=\"sm\">cloud_upload</cide-ele-icon>\n </div>\n <div class=\"upload-info\">\n <div class=\"upload-title\">File Upload</div>\n <div class=\"upload-summary\">{{ getUploadSummary() }}</div>\n </div>\n </div>\n \n <div class=\"header-actions\">\n <button class=\"action-btn minimize-btn\" (click)=\"toggleMinimize()\" [title]=\"isMinimized() ? 'Expand' : 'Minimize'\">\n <cide-ele-icon size=\"xs\">{{ isMinimized() ? 'expand_more' : 'expand_less' }}</cide-ele-icon>\n </button>\n <button class=\"action-btn close-btn\" (click)=\"close()\" title=\"Close\">\n <cide-ele-icon size=\"xs\">close</cide-ele-icon>\n </button>\n </div>\n </div>\n\n <!-- Content (hidden when minimized) -->\n @if (!isMinimized()) {\n <div class=\"uploader-content\">\n \n <!-- Drag and Drop Zone -->\n <div class=\"upload-zone\" \n [class.drag-over]=\"isDragOver()\" \n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\" \n (drop)=\"onDrop($event)\" \n (click)=\"triggerFileInput()\">\n \n <!-- Hidden file input -->\n <input #fileInput \n type=\"file\" \n [multiple]=\"true\" \n [accept]=\"'*/*'\" \n (change)=\"onFileInputChange($event)\" \n style=\"display: none;\">\n \n <div class=\"upload-zone-content\">\n <cide-ele-icon class=\"upload-icon\" size=\"sm\">cloud_upload</cide-ele-icon>\n \n <div class=\"upload-text\">\n <div class=\"upload-title\">\n {{ isDragOver() ? 'Drop files here' : 'Drag files here or click to browse' }}\n </div>\n </div>\n </div>\n </div>\n \n <!-- Upload Queue - Show files from service state -->\n @if (allFilesForGroup().length > 0) {\n <div class=\"upload-queue\">\n <!-- Show all files from service state -->\n @for (file of allFilesForGroup(); track file.fileId) {\n <div class=\"upload-item\" [class]=\"getStatusClass(file.stage)\">\n <div class=\"file-info\">\n <cide-ele-icon class=\"status-icon\" size=\"xs\">{{ getStatusIcon(file.stage) }}</cide-ele-icon>\n <div class=\"file-details\">\n <div class=\"file-name\">{{ file.fileName }}</div>\n <div class=\"file-status\">\n @switch (file.stage) {\n @case ('pending') {\n <span class=\"text-yellow-600\">Waiting...</span>\n }\n @case ('reading') {\n <span class=\"text-yellow-600\">Reading...</span>\n }\n @case ('uploading') {\n <span class=\"text-blue-600\">Uploading...</span>\n }\n @case ('complete') {\n <span class=\"text-green-600\">Completed</span>\n }\n @case ('error') {\n <span class=\"text-red-600\">Failed</span>\n }\n }\n </div>\n </div>\n </div>\n\n <!-- Progress Bar (only for uploading files) -->\n @if (file.stage === 'uploading' && file.percentage !== undefined) {\n <div class=\"file-progress\">\n <div class=\"progress-bar\">\n <div class=\"progress-fill\" [style.width.%]=\"file.percentage\"></div>\n </div>\n <span class=\"progress-text\">{{ file.percentage }}%</span>\n </div>\n }\n\n <!-- Actions -->\n <div class=\"upload-actions\">\n @switch (file.stage) {\n @case ('pending') {\n <button class=\"action-btn cancel-btn\" (click)=\"cancelUpload(file.fileId)\" title=\"Cancel\">\n <cide-ele-icon size=\"xs\">cancel</cide-ele-icon>\n </button>\n }\n @case ('reading') {\n <button class=\"action-btn cancel-btn\" (click)=\"cancelUpload(file.fileId)\" title=\"Cancel\">\n <cide-ele-icon size=\"xs\">cancel</cide-ele-icon>\n </button>\n }\n @case ('uploading') {\n <button class=\"action-btn cancel-btn\" (click)=\"cancelUpload(file.fileId)\" title=\"Cancel\">\n <cide-ele-icon size=\"xs\">cancel</cide-ele-icon>\n </button>\n }\n @case ('complete') {\n <button class=\"action-btn success-btn\" title=\"Completed\">\n <cide-ele-icon size=\"xs\">check_circle</cide-ele-icon>\n </button>\n }\n @case ('error') {\n <button class=\"action-btn retry-btn\" title=\"Retry\">\n <cide-ele-icon size=\"xs\">refresh</cide-ele-icon>\n </button>\n }\n }\n </div>\n </div>\n }\n </div>\n } @else {\n <!-- No uploads message when manually opened -->\n <div class=\"no-uploads-message\">\n <div class=\"message-content\">\n <cide-ele-icon size=\"md\" class=\"message-icon\">cloud_upload</cide-ele-icon>\n <div class=\"message-text\">\n <h4>No active uploads</h4>\n <p>Upload files to see their progress here</p>\n </div>\n </div>\n </div>\n }\n </div>\n }\n</div>\n}\n", styles: [".floating-uploader{position:fixed;width:320px;max-height:500px;background:#fff;border-radius:12px;box-shadow:0 8px 32px #0000001f;border:1px solid rgba(0,0,0,.08);z-index:1000;overflow:hidden;transition:all .3s cubic-bezier(.4,0,.2,1);transform:translateY(0);opacity:1}.floating-uploader.animating{transition:all .3s cubic-bezier(.4,0,.2,1)}.floating-uploader.minimized .uploader-content{display:none}.floating-uploader.minimized .uploader-footer{border-top:none}.floating-uploader.uploading{border-color:#3b82f6;box-shadow:0 8px 32px #3b82f626}.floating-uploader .uploader-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;background:#f8fafc;border-bottom:1px solid #e2e8f0}.floating-uploader .uploader-header.draggable-header{cursor:move;-webkit-user-select:none;user-select:none}.floating-uploader .uploader-header.draggable-header:hover{background:#f1f5f9}.floating-uploader .uploader-header.draggable-header:active{background:#e2e8f0;cursor:grabbing}.floating-uploader .uploader-header .header-left{display:flex;align-items:center;gap:8px}.floating-uploader .uploader-header .header-left .upload-icon{display:flex;align-items:center;justify-content:center;width:24px;height:24px;background:#3b82f6;border-radius:6px;color:#fff}.floating-uploader .uploader-header .header-left .upload-info .upload-title{font-size:14px;font-weight:600;color:#1e293b;margin:0}.floating-uploader .uploader-header .header-left .upload-info .upload-summary{font-size:12px;color:#64748b;margin:0}.floating-uploader .uploader-header .header-actions{display:flex;gap:4px}.floating-uploader .uploader-header .header-actions .action-btn{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border:none;background:transparent;border-radius:4px;cursor:pointer;transition:background-color .2s;color:#64748b}.floating-uploader .uploader-header .header-actions .action-btn:hover{background:#e2e8f0;color:#1e293b}.floating-uploader .uploader-header .header-actions .action-btn.close-btn:hover{background:#fef2f2;color:#dc2626}.floating-uploader .uploader-content{max-height:400px;overflow-y:auto}.floating-uploader .uploader-content .upload-zone{margin:8px 16px;padding:12px;border:2px dashed #d1d5db;border-radius:6px;background:#f9fafb;cursor:pointer;transition:all .2s ease;text-align:center}.floating-uploader .uploader-content .upload-zone:hover{border-color:#3b82f6;background:#f0f9ff}.floating-uploader .uploader-content .upload-zone.drag-over{border-color:#3b82f6;background:#dbeafe;transform:scale(1.01)}.floating-uploader .uploader-content .upload-zone .upload-zone-content{display:flex;flex-direction:column;align-items:center;gap:6px}.floating-uploader .uploader-content .upload-zone .upload-zone-content .upload-icon{color:#6b7280;transition:color .2s ease}.floating-uploader .uploader-content .upload-zone .upload-zone-content .upload-text .upload-title{font-size:13px;font-weight:500;color:#374151;margin:0;line-height:1.2}.floating-uploader .uploader-content .upload-zone:hover .upload-zone-content .upload-icon{color:#3b82f6}.floating-uploader .uploader-content .upload-zone.drag-over .upload-zone-content .upload-icon{color:#1d4ed8}.floating-uploader .uploader-content .upload-queue .upload-item{display:flex;align-items:center;padding:8px 16px;border-bottom:1px solid #f1f5f9;transition:background-color .2s}.floating-uploader .uploader-content .upload-queue .upload-item:last-child{border-bottom:none}.floating-uploader .uploader-content .upload-queue .upload-item.status-uploading{background:#f0f9ff}.floating-uploader .uploader-content .upload-queue .upload-item.status-completed{background:#f0fdf4}.floating-uploader .uploader-content .upload-queue .upload-item.status-error{background:#fef2f2}.floating-uploader .uploader-content .upload-queue .upload-item .file-info{display:flex;align-items:center;gap:8px;flex:1;min-width:0}.floating-uploader .uploader-content .upload-queue .upload-item .file-info .status-icon{flex-shrink:0}.floating-uploader .uploader-content .upload-queue .upload-item .file-info .file-details{min-width:0;flex:1}.floating-uploader .uploader-content .upload-queue .upload-item .file-info .file-details .file-name{font-size:13px;font-weight:500;color:#1e293b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin:0}.floating-uploader .uploader-content .upload-queue .upload-item .file-info .file-details .file-status{font-size:11px;margin:0}.floating-uploader .uploader-content .upload-queue .upload-item .file-info .file-details .file-status span{font-weight:500}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress{display:flex;align-items:center;gap:8px;margin:0 8px;min-width:80px}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress .progress-bar{flex:1;height:3px;background:#e2e8f0;border-radius:2px;overflow:hidden}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress .progress-bar .progress-fill{height:100%;background:#3b82f6;transition:width .3s ease}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress .progress-text{font-size:10px;color:#64748b;min-width:24px;text-align:right}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions{display:flex;gap:4px}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn{display:flex;align-items:center;justify-content:center;width:20px;height:20px;border:none;background:transparent;border-radius:4px;cursor:pointer;transition:all .2s;color:#64748b}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn:hover{background:#e2e8f0}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn.cancel-btn:hover{background:#fef2f2;color:#dc2626}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn.retry-btn:hover{background:#f0f9ff;color:#3b82f6}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn.success-btn{color:#16a34a}.floating-uploader .uploader-content .hidden-uploader{display:none}.floating-uploader .uploader-footer{padding:8px 16px;background:#f8fafc;border-top:1px solid #e2e8f0}.floating-uploader .uploader-footer .footer-stats{display:flex;gap:12px;font-size:11px}.floating-uploader .uploader-footer .footer-stats .stat{display:flex;align-items:center;gap:4px;color:#64748b}.floating-uploader .uploader-footer .footer-stats .stat.uploading{color:#3b82f6}.floating-uploader .uploader-footer .footer-stats .stat.completed{color:#16a34a}.floating-uploader .uploader-footer .footer-stats .stat.failed{color:#dc2626}@media (max-width: 640px){.floating-uploader{bottom:10px;right:10px;left:10px;width:auto;max-width:none}}@media (prefers-color-scheme: dark){.floating-uploader{background:#1e293b;border-color:#334155;box-shadow:0 8px 32px #0000004d}.floating-uploader.uploading{border-color:#3b82f6;box-shadow:0 8px 32px #3b82f633}.floating-uploader .uploader-header{background:#334155;border-bottom-color:#475569}.floating-uploader .uploader-header.draggable-header:hover{background:#475569}.floating-uploader .uploader-header.draggable-header:active{background:#64748b}.floating-uploader .uploader-header .header-left .upload-icon{background:#3b82f6}.floating-uploader .uploader-header .header-left .upload-info .upload-title{color:#f1f5f9}.floating-uploader .uploader-header .header-left .upload-info .upload-summary,.floating-uploader .uploader-header .header-actions .action-btn{color:#94a3b8}.floating-uploader .uploader-header .header-actions .action-btn:hover{background:#475569;color:#f1f5f9}.floating-uploader .uploader-header .header-actions .action-btn.close-btn:hover{background:#7f1d1d;color:#fca5a5}.floating-uploader .uploader-content .upload-zone{border-color:#475569;background:#334155}.floating-uploader .uploader-content .upload-zone:hover{border-color:#3b82f6;background:#1e3a8a}.floating-uploader .uploader-content .upload-zone.drag-over{border-color:#60a5fa;background:#1e40af}.floating-uploader .uploader-content .upload-zone .upload-zone-content .upload-icon{color:#94a3b8}.floating-uploader .uploader-content .upload-zone .upload-zone-content .upload-text .upload-title{color:#f1f5f9}.floating-uploader .uploader-content .upload-zone:hover .upload-zone-content .upload-icon{color:#60a5fa}.floating-uploader .uploader-content .upload-zone.drag-over .upload-zone-content .upload-icon{color:#93c5fd}.floating-uploader .uploader-content .upload-queue .upload-item{border-bottom-color:#334155}.floating-uploader .uploader-content .upload-queue .upload-item.status-uploading{background:#1e3a8a}.floating-uploader .uploader-content .upload-queue .upload-item.status-completed{background:#14532d}.floating-uploader .uploader-content .upload-queue .upload-item.status-error{background:#7f1d1d}.floating-uploader .uploader-content .upload-queue .upload-item .file-info .file-details .file-name{color:#f1f5f9}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress .progress-bar{background:#475569}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress .progress-bar .progress-fill{background:#3b82f6}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress .progress-text,.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn{color:#94a3b8}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn:hover{background:#475569}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn.cancel-btn:hover{background:#7f1d1d;color:#fca5a5}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn.retry-btn:hover{background:#1e3a8a;color:#60a5fa}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn.success-btn{color:#4ade80}.floating-uploader .uploader-footer{background:#334155;border-top-color:#475569}.floating-uploader .uploader-footer .footer-stats .stat{color:#94a3b8}.floating-uploader .uploader-footer .footer-stats .stat.uploading{color:#60a5fa}.floating-uploader .uploader-footer .footer-stats .stat.completed{color:#4ade80}.floating-uploader .uploader-footer .footer-stats .stat.failed{color:#fca5a5}}@keyframes slideInUp{0%{transform:translateY(100%);opacity:0}to{transform:translateY(0);opacity:1}}@keyframes slideOutDown{0%{transform:translateY(0);opacity:1}to{transform:translateY(100%);opacity:0}}.floating-uploader.animating{animation:slideInUp .3s cubic-bezier(.4,0,.2,1)}.floating-uploader.animating.hiding{animation:slideOutDown .3s cubic-bezier(.4,0,.2,1)}.no-uploads-message{padding:2rem;text-align:center;color:#6b7280}.no-uploads-message .message-content{display:flex;flex-direction:column;align-items:center;gap:1rem}.no-uploads-message .message-content .message-icon{color:#9ca3af;opacity:.7}.no-uploads-message .message-content .message-text h4{margin:0 0 .5rem;font-size:1.1rem;font-weight:600;color:#374151}.no-uploads-message .message-content .message-text p{margin:0;font-size:.9rem;color:#6b7280}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }] });
|
|
3934
3953
|
}
|
|
3935
3954
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFloatingFileUploaderComponent, decorators: [{
|
|
3936
3955
|
type: Component,
|
|
3937
3956
|
args: [{ selector: 'cide-ele-floating-file-uploader', standalone: true, imports: [
|
|
3938
3957
|
CommonModule,
|
|
3939
3958
|
CideIconComponent
|
|
3940
|
-
], template: "<!-- Floating File Uploader Container -->\n@if (isVisible()) {\n<div class=\"floating-uploader\" \n [class.minimized]=\"isMinimized()\" \n [class.animating]=\"isAnimating()\"\n [style.left.px]=\"position().x\"\n [style.top.px]=\"position().y\">\n\n <!-- Header (Draggable) -->\n <div class=\"uploader-header draggable-header\"\n (mousedown)=\"startDrag($event)\"\n (touchstart)=\"startDrag($event)\">\n <div class=\"header-left\">\n <div class=\"upload-icon\">\n <cide-ele-icon size=\"sm\">cloud_upload</cide-ele-icon>\n </div>\n <div class=\"upload-info\">\n <div class=\"upload-title\">File Upload</div>\n <div class=\"upload-summary\">{{ getUploadSummary() }}</div>\n </div>\n </div>\n \n <div class=\"header-actions\">\n <button class=\"action-btn minimize-btn\" (click)=\"toggleMinimize()\" [title]=\"isMinimized() ? 'Expand' : 'Minimize'\">\n <cide-ele-icon size=\"xs\">{{ isMinimized() ? 'expand_more' : 'expand_less' }}</cide-ele-icon>\n </button>\n <button class=\"action-btn close-btn\" (click)=\"close()\" title=\"Close\">\n <cide-ele-icon size=\"xs\">close</cide-ele-icon>\n </button>\n </div>\n </div>\n\n <!-- Content (hidden when minimized) -->\n @if (!isMinimized()) {\n <div class=\"uploader-content\">\n \n <!-- Drag and Drop Zone -->\n <div class=\"upload-zone\" \n [class.drag-over]=\"isDragOver()\" \n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\" \n (drop)=\"onDrop($event)\" \n (click)=\"triggerFileInput()\">\n \n <!-- Hidden file input -->\n <input #fileInput \n type=\"file\" \n [multiple]=\"true\" \n [accept]=\"'*/*'\" \n (change)=\"onFileInputChange($event)\" \n style=\"display: none;\">\n \n <div class=\"upload-zone-content\">\n <cide-ele-icon class=\"upload-icon\" size=\"sm\">cloud_upload</cide-ele-icon>\n \n <div class=\"upload-text\">\n <div class=\"upload-title\">\n {{ isDragOver() ? 'Drop files here' : 'Drag files here or click to browse' }}\n </div>\n </div>\n </div>\n </div>\n \n <!-- Upload Queue - Show files from service state -->\n @if (getAllFiles().length > 0) {\n <div class=\"upload-queue\">\n <!-- Show all files from service state -->\n @for (file of getAllFiles(); track file.fileId) {\n <div class=\"upload-item\" [class]=\"getStatusClass(file.stage)\">\n <div class=\"file-info\">\n <cide-ele-icon class=\"status-icon\" size=\"xs\">{{ getStatusIcon(file.stage) }}</cide-ele-icon>\n <div class=\"file-details\">\n <div class=\"file-name\">{{ file.fileName }}</div>\n <div class=\"file-status\">\n @switch (file.stage) {\n @case ('pending') {\n <span class=\"text-yellow-600\">Waiting...</span>\n }\n @case ('reading') {\n <span class=\"text-yellow-600\">Reading...</span>\n }\n @case ('uploading') {\n <span class=\"text-blue-600\">Uploading...</span>\n }\n @case ('complete') {\n <span class=\"text-green-600\">Completed</span>\n }\n @case ('error') {\n <span class=\"text-red-600\">Failed</span>\n }\n }\n </div>\n </div>\n </div>\n\n <!-- Progress Bar (only for uploading files) -->\n @if (file.stage === 'uploading' && file.percentage !== undefined) {\n <div class=\"file-progress\">\n <div class=\"progress-bar\">\n <div class=\"progress-fill\" [style.width.%]=\"file.percentage\"></div>\n </div>\n <span class=\"progress-text\">{{ file.percentage }}%</span>\n </div>\n }\n\n <!-- Actions -->\n <div class=\"upload-actions\">\n @switch (file.stage) {\n @case ('pending') {\n <button class=\"action-btn cancel-btn\" (click)=\"cancelUpload(file.fileId)\" title=\"Cancel\">\n <cide-ele-icon size=\"xs\">cancel</cide-ele-icon>\n </button>\n }\n @case ('reading') {\n <button class=\"action-btn cancel-btn\" (click)=\"cancelUpload(file.fileId)\" title=\"Cancel\">\n <cide-ele-icon size=\"xs\">cancel</cide-ele-icon>\n </button>\n }\n @case ('uploading') {\n <button class=\"action-btn cancel-btn\" (click)=\"cancelUpload(file.fileId)\" title=\"Cancel\">\n <cide-ele-icon size=\"xs\">cancel</cide-ele-icon>\n </button>\n }\n @case ('complete') {\n <button class=\"action-btn success-btn\" title=\"Completed\">\n <cide-ele-icon size=\"xs\">check_circle</cide-ele-icon>\n </button>\n }\n @case ('error') {\n <button class=\"action-btn retry-btn\" title=\"Retry\">\n <cide-ele-icon size=\"xs\">refresh</cide-ele-icon>\n </button>\n }\n }\n </div>\n </div>\n }\n </div>\n } @else {\n <!-- No uploads message when manually opened -->\n <div class=\"no-uploads-message\">\n <div class=\"message-content\">\n <cide-ele-icon size=\"md\" class=\"message-icon\">cloud_upload</cide-ele-icon>\n <div class=\"message-text\">\n <h4>No active uploads</h4>\n <p>Upload files to see their progress here</p>\n </div>\n </div>\n </div>\n }\n </div>\n }\n</div>\n}\n", styles: [".floating-uploader{position:fixed;width:320px;max-height:500px;background:#fff;border-radius:12px;box-shadow:0 8px 32px #0000001f;border:1px solid rgba(0,0,0,.08);z-index:1000;overflow:hidden;transition:all .3s cubic-bezier(.4,0,.2,1);transform:translateY(0);opacity:1}.floating-uploader.animating{transition:all .3s cubic-bezier(.4,0,.2,1)}.floating-uploader.minimized .uploader-content{display:none}.floating-uploader.minimized .uploader-footer{border-top:none}.floating-uploader.uploading{border-color:#3b82f6;box-shadow:0 8px 32px #3b82f626}.floating-uploader .uploader-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;background:#f8fafc;border-bottom:1px solid #e2e8f0}.floating-uploader .uploader-header.draggable-header{cursor:move;-webkit-user-select:none;user-select:none}.floating-uploader .uploader-header.draggable-header:hover{background:#f1f5f9}.floating-uploader .uploader-header.draggable-header:active{background:#e2e8f0;cursor:grabbing}.floating-uploader .uploader-header .header-left{display:flex;align-items:center;gap:8px}.floating-uploader .uploader-header .header-left .upload-icon{display:flex;align-items:center;justify-content:center;width:24px;height:24px;background:#3b82f6;border-radius:6px;color:#fff}.floating-uploader .uploader-header .header-left .upload-info .upload-title{font-size:14px;font-weight:600;color:#1e293b;margin:0}.floating-uploader .uploader-header .header-left .upload-info .upload-summary{font-size:12px;color:#64748b;margin:0}.floating-uploader .uploader-header .header-actions{display:flex;gap:4px}.floating-uploader .uploader-header .header-actions .action-btn{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border:none;background:transparent;border-radius:4px;cursor:pointer;transition:background-color .2s;color:#64748b}.floating-uploader .uploader-header .header-actions .action-btn:hover{background:#e2e8f0;color:#1e293b}.floating-uploader .uploader-header .header-actions .action-btn.close-btn:hover{background:#fef2f2;color:#dc2626}.floating-uploader .uploader-content{max-height:400px;overflow-y:auto}.floating-uploader .uploader-content .upload-zone{margin:8px 16px;padding:12px;border:2px dashed #d1d5db;border-radius:6px;background:#f9fafb;cursor:pointer;transition:all .2s ease;text-align:center}.floating-uploader .uploader-content .upload-zone:hover{border-color:#3b82f6;background:#f0f9ff}.floating-uploader .uploader-content .upload-zone.drag-over{border-color:#3b82f6;background:#dbeafe;transform:scale(1.01)}.floating-uploader .uploader-content .upload-zone .upload-zone-content{display:flex;flex-direction:column;align-items:center;gap:6px}.floating-uploader .uploader-content .upload-zone .upload-zone-content .upload-icon{color:#6b7280;transition:color .2s ease}.floating-uploader .uploader-content .upload-zone .upload-zone-content .upload-text .upload-title{font-size:13px;font-weight:500;color:#374151;margin:0;line-height:1.2}.floating-uploader .uploader-content .upload-zone:hover .upload-zone-content .upload-icon{color:#3b82f6}.floating-uploader .uploader-content .upload-zone.drag-over .upload-zone-content .upload-icon{color:#1d4ed8}.floating-uploader .uploader-content .upload-queue .upload-item{display:flex;align-items:center;padding:8px 16px;border-bottom:1px solid #f1f5f9;transition:background-color .2s}.floating-uploader .uploader-content .upload-queue .upload-item:last-child{border-bottom:none}.floating-uploader .uploader-content .upload-queue .upload-item.status-uploading{background:#f0f9ff}.floating-uploader .uploader-content .upload-queue .upload-item.status-completed{background:#f0fdf4}.floating-uploader .uploader-content .upload-queue .upload-item.status-error{background:#fef2f2}.floating-uploader .uploader-content .upload-queue .upload-item .file-info{display:flex;align-items:center;gap:8px;flex:1;min-width:0}.floating-uploader .uploader-content .upload-queue .upload-item .file-info .status-icon{flex-shrink:0}.floating-uploader .uploader-content .upload-queue .upload-item .file-info .file-details{min-width:0;flex:1}.floating-uploader .uploader-content .upload-queue .upload-item .file-info .file-details .file-name{font-size:13px;font-weight:500;color:#1e293b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin:0}.floating-uploader .uploader-content .upload-queue .upload-item .file-info .file-details .file-status{font-size:11px;margin:0}.floating-uploader .uploader-content .upload-queue .upload-item .file-info .file-details .file-status span{font-weight:500}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress{display:flex;align-items:center;gap:8px;margin:0 8px;min-width:80px}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress .progress-bar{flex:1;height:3px;background:#e2e8f0;border-radius:2px;overflow:hidden}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress .progress-bar .progress-fill{height:100%;background:#3b82f6;transition:width .3s ease}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress .progress-text{font-size:10px;color:#64748b;min-width:24px;text-align:right}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions{display:flex;gap:4px}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn{display:flex;align-items:center;justify-content:center;width:20px;height:20px;border:none;background:transparent;border-radius:4px;cursor:pointer;transition:all .2s;color:#64748b}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn:hover{background:#e2e8f0}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn.cancel-btn:hover{background:#fef2f2;color:#dc2626}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn.retry-btn:hover{background:#f0f9ff;color:#3b82f6}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn.success-btn{color:#16a34a}.floating-uploader .uploader-content .hidden-uploader{display:none}.floating-uploader .uploader-footer{padding:8px 16px;background:#f8fafc;border-top:1px solid #e2e8f0}.floating-uploader .uploader-footer .footer-stats{display:flex;gap:12px;font-size:11px}.floating-uploader .uploader-footer .footer-stats .stat{display:flex;align-items:center;gap:4px;color:#64748b}.floating-uploader .uploader-footer .footer-stats .stat.uploading{color:#3b82f6}.floating-uploader .uploader-footer .footer-stats .stat.completed{color:#16a34a}.floating-uploader .uploader-footer .footer-stats .stat.failed{color:#dc2626}@media (max-width: 640px){.floating-uploader{bottom:10px;right:10px;left:10px;width:auto;max-width:none}}@media (prefers-color-scheme: dark){.floating-uploader{background:#1e293b;border-color:#334155;box-shadow:0 8px 32px #0000004d}.floating-uploader.uploading{border-color:#3b82f6;box-shadow:0 8px 32px #3b82f633}.floating-uploader .uploader-header{background:#334155;border-bottom-color:#475569}.floating-uploader .uploader-header.draggable-header:hover{background:#475569}.floating-uploader .uploader-header.draggable-header:active{background:#64748b}.floating-uploader .uploader-header .header-left .upload-icon{background:#3b82f6}.floating-uploader .uploader-header .header-left .upload-info .upload-title{color:#f1f5f9}.floating-uploader .uploader-header .header-left .upload-info .upload-summary,.floating-uploader .uploader-header .header-actions .action-btn{color:#94a3b8}.floating-uploader .uploader-header .header-actions .action-btn:hover{background:#475569;color:#f1f5f9}.floating-uploader .uploader-header .header-actions .action-btn.close-btn:hover{background:#7f1d1d;color:#fca5a5}.floating-uploader .uploader-content .upload-zone{border-color:#475569;background:#334155}.floating-uploader .uploader-content .upload-zone:hover{border-color:#3b82f6;background:#1e3a8a}.floating-uploader .uploader-content .upload-zone.drag-over{border-color:#60a5fa;background:#1e40af}.floating-uploader .uploader-content .upload-zone .upload-zone-content .upload-icon{color:#94a3b8}.floating-uploader .uploader-content .upload-zone .upload-zone-content .upload-text .upload-title{color:#f1f5f9}.floating-uploader .uploader-content .upload-zone:hover .upload-zone-content .upload-icon{color:#60a5fa}.floating-uploader .uploader-content .upload-zone.drag-over .upload-zone-content .upload-icon{color:#93c5fd}.floating-uploader .uploader-content .upload-queue .upload-item{border-bottom-color:#334155}.floating-uploader .uploader-content .upload-queue .upload-item.status-uploading{background:#1e3a8a}.floating-uploader .uploader-content .upload-queue .upload-item.status-completed{background:#14532d}.floating-uploader .uploader-content .upload-queue .upload-item.status-error{background:#7f1d1d}.floating-uploader .uploader-content .upload-queue .upload-item .file-info .file-details .file-name{color:#f1f5f9}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress .progress-bar{background:#475569}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress .progress-bar .progress-fill{background:#3b82f6}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress .progress-text,.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn{color:#94a3b8}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn:hover{background:#475569}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn.cancel-btn:hover{background:#7f1d1d;color:#fca5a5}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn.retry-btn:hover{background:#1e3a8a;color:#60a5fa}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn.success-btn{color:#4ade80}.floating-uploader .uploader-footer{background:#334155;border-top-color:#475569}.floating-uploader .uploader-footer .footer-stats .stat{color:#94a3b8}.floating-uploader .uploader-footer .footer-stats .stat.uploading{color:#60a5fa}.floating-uploader .uploader-footer .footer-stats .stat.completed{color:#4ade80}.floating-uploader .uploader-footer .footer-stats .stat.failed{color:#fca5a5}}@keyframes slideInUp{0%{transform:translateY(100%);opacity:0}to{transform:translateY(0);opacity:1}}@keyframes slideOutDown{0%{transform:translateY(0);opacity:1}to{transform:translateY(100%);opacity:0}}.floating-uploader.animating{animation:slideInUp .3s cubic-bezier(.4,0,.2,1)}.floating-uploader.animating.hiding{animation:slideOutDown .3s cubic-bezier(.4,0,.2,1)}.no-uploads-message{padding:2rem;text-align:center;color:#6b7280}.no-uploads-message .message-content{display:flex;flex-direction:column;align-items:center;gap:1rem}.no-uploads-message .message-content .message-icon{color:#9ca3af;opacity:.7}.no-uploads-message .message-content .message-text h4{margin:0 0 .5rem;font-size:1.1rem;font-weight:600;color:#374151}.no-uploads-message .message-content .message-text p{margin:0;font-size:.9rem;color:#6b7280}\n"] }]
|
|
3959
|
+
], template: "<!-- Floating File Uploader Container -->\n@if (isVisible()) {\n<div class=\"floating-uploader\" \n [class.minimized]=\"isMinimized()\" \n [class.animating]=\"isAnimating()\"\n [style.left.px]=\"position().x\"\n [style.top.px]=\"position().y\">\n\n <!-- Header (Draggable) -->\n <div class=\"uploader-header draggable-header\"\n (mousedown)=\"startDrag($event)\"\n (touchstart)=\"startDrag($event)\">\n <div class=\"header-left\">\n <div class=\"upload-icon\">\n <cide-ele-icon size=\"sm\">cloud_upload</cide-ele-icon>\n </div>\n <div class=\"upload-info\">\n <div class=\"upload-title\">File Upload</div>\n <div class=\"upload-summary\">{{ getUploadSummary() }}</div>\n </div>\n </div>\n \n <div class=\"header-actions\">\n <button class=\"action-btn minimize-btn\" (click)=\"toggleMinimize()\" [title]=\"isMinimized() ? 'Expand' : 'Minimize'\">\n <cide-ele-icon size=\"xs\">{{ isMinimized() ? 'expand_more' : 'expand_less' }}</cide-ele-icon>\n </button>\n <button class=\"action-btn close-btn\" (click)=\"close()\" title=\"Close\">\n <cide-ele-icon size=\"xs\">close</cide-ele-icon>\n </button>\n </div>\n </div>\n\n <!-- Content (hidden when minimized) -->\n @if (!isMinimized()) {\n <div class=\"uploader-content\">\n \n <!-- Drag and Drop Zone -->\n <div class=\"upload-zone\" \n [class.drag-over]=\"isDragOver()\" \n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\" \n (drop)=\"onDrop($event)\" \n (click)=\"triggerFileInput()\">\n \n <!-- Hidden file input -->\n <input #fileInput \n type=\"file\" \n [multiple]=\"true\" \n [accept]=\"'*/*'\" \n (change)=\"onFileInputChange($event)\" \n style=\"display: none;\">\n \n <div class=\"upload-zone-content\">\n <cide-ele-icon class=\"upload-icon\" size=\"sm\">cloud_upload</cide-ele-icon>\n \n <div class=\"upload-text\">\n <div class=\"upload-title\">\n {{ isDragOver() ? 'Drop files here' : 'Drag files here or click to browse' }}\n </div>\n </div>\n </div>\n </div>\n \n <!-- Upload Queue - Show files from service state -->\n @if (allFilesForGroup().length > 0) {\n <div class=\"upload-queue\">\n <!-- Show all files from service state -->\n @for (file of allFilesForGroup(); track file.fileId) {\n <div class=\"upload-item\" [class]=\"getStatusClass(file.stage)\">\n <div class=\"file-info\">\n <cide-ele-icon class=\"status-icon\" size=\"xs\">{{ getStatusIcon(file.stage) }}</cide-ele-icon>\n <div class=\"file-details\">\n <div class=\"file-name\">{{ file.fileName }}</div>\n <div class=\"file-status\">\n @switch (file.stage) {\n @case ('pending') {\n <span class=\"text-yellow-600\">Waiting...</span>\n }\n @case ('reading') {\n <span class=\"text-yellow-600\">Reading...</span>\n }\n @case ('uploading') {\n <span class=\"text-blue-600\">Uploading...</span>\n }\n @case ('complete') {\n <span class=\"text-green-600\">Completed</span>\n }\n @case ('error') {\n <span class=\"text-red-600\">Failed</span>\n }\n }\n </div>\n </div>\n </div>\n\n <!-- Progress Bar (only for uploading files) -->\n @if (file.stage === 'uploading' && file.percentage !== undefined) {\n <div class=\"file-progress\">\n <div class=\"progress-bar\">\n <div class=\"progress-fill\" [style.width.%]=\"file.percentage\"></div>\n </div>\n <span class=\"progress-text\">{{ file.percentage }}%</span>\n </div>\n }\n\n <!-- Actions -->\n <div class=\"upload-actions\">\n @switch (file.stage) {\n @case ('pending') {\n <button class=\"action-btn cancel-btn\" (click)=\"cancelUpload(file.fileId)\" title=\"Cancel\">\n <cide-ele-icon size=\"xs\">cancel</cide-ele-icon>\n </button>\n }\n @case ('reading') {\n <button class=\"action-btn cancel-btn\" (click)=\"cancelUpload(file.fileId)\" title=\"Cancel\">\n <cide-ele-icon size=\"xs\">cancel</cide-ele-icon>\n </button>\n }\n @case ('uploading') {\n <button class=\"action-btn cancel-btn\" (click)=\"cancelUpload(file.fileId)\" title=\"Cancel\">\n <cide-ele-icon size=\"xs\">cancel</cide-ele-icon>\n </button>\n }\n @case ('complete') {\n <button class=\"action-btn success-btn\" title=\"Completed\">\n <cide-ele-icon size=\"xs\">check_circle</cide-ele-icon>\n </button>\n }\n @case ('error') {\n <button class=\"action-btn retry-btn\" title=\"Retry\">\n <cide-ele-icon size=\"xs\">refresh</cide-ele-icon>\n </button>\n }\n }\n </div>\n </div>\n }\n </div>\n } @else {\n <!-- No uploads message when manually opened -->\n <div class=\"no-uploads-message\">\n <div class=\"message-content\">\n <cide-ele-icon size=\"md\" class=\"message-icon\">cloud_upload</cide-ele-icon>\n <div class=\"message-text\">\n <h4>No active uploads</h4>\n <p>Upload files to see their progress here</p>\n </div>\n </div>\n </div>\n }\n </div>\n }\n</div>\n}\n", styles: [".floating-uploader{position:fixed;width:320px;max-height:500px;background:#fff;border-radius:12px;box-shadow:0 8px 32px #0000001f;border:1px solid rgba(0,0,0,.08);z-index:1000;overflow:hidden;transition:all .3s cubic-bezier(.4,0,.2,1);transform:translateY(0);opacity:1}.floating-uploader.animating{transition:all .3s cubic-bezier(.4,0,.2,1)}.floating-uploader.minimized .uploader-content{display:none}.floating-uploader.minimized .uploader-footer{border-top:none}.floating-uploader.uploading{border-color:#3b82f6;box-shadow:0 8px 32px #3b82f626}.floating-uploader .uploader-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;background:#f8fafc;border-bottom:1px solid #e2e8f0}.floating-uploader .uploader-header.draggable-header{cursor:move;-webkit-user-select:none;user-select:none}.floating-uploader .uploader-header.draggable-header:hover{background:#f1f5f9}.floating-uploader .uploader-header.draggable-header:active{background:#e2e8f0;cursor:grabbing}.floating-uploader .uploader-header .header-left{display:flex;align-items:center;gap:8px}.floating-uploader .uploader-header .header-left .upload-icon{display:flex;align-items:center;justify-content:center;width:24px;height:24px;background:#3b82f6;border-radius:6px;color:#fff}.floating-uploader .uploader-header .header-left .upload-info .upload-title{font-size:14px;font-weight:600;color:#1e293b;margin:0}.floating-uploader .uploader-header .header-left .upload-info .upload-summary{font-size:12px;color:#64748b;margin:0}.floating-uploader .uploader-header .header-actions{display:flex;gap:4px}.floating-uploader .uploader-header .header-actions .action-btn{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border:none;background:transparent;border-radius:4px;cursor:pointer;transition:background-color .2s;color:#64748b}.floating-uploader .uploader-header .header-actions .action-btn:hover{background:#e2e8f0;color:#1e293b}.floating-uploader .uploader-header .header-actions .action-btn.close-btn:hover{background:#fef2f2;color:#dc2626}.floating-uploader .uploader-content{max-height:400px;overflow-y:auto}.floating-uploader .uploader-content .upload-zone{margin:8px 16px;padding:12px;border:2px dashed #d1d5db;border-radius:6px;background:#f9fafb;cursor:pointer;transition:all .2s ease;text-align:center}.floating-uploader .uploader-content .upload-zone:hover{border-color:#3b82f6;background:#f0f9ff}.floating-uploader .uploader-content .upload-zone.drag-over{border-color:#3b82f6;background:#dbeafe;transform:scale(1.01)}.floating-uploader .uploader-content .upload-zone .upload-zone-content{display:flex;flex-direction:column;align-items:center;gap:6px}.floating-uploader .uploader-content .upload-zone .upload-zone-content .upload-icon{color:#6b7280;transition:color .2s ease}.floating-uploader .uploader-content .upload-zone .upload-zone-content .upload-text .upload-title{font-size:13px;font-weight:500;color:#374151;margin:0;line-height:1.2}.floating-uploader .uploader-content .upload-zone:hover .upload-zone-content .upload-icon{color:#3b82f6}.floating-uploader .uploader-content .upload-zone.drag-over .upload-zone-content .upload-icon{color:#1d4ed8}.floating-uploader .uploader-content .upload-queue .upload-item{display:flex;align-items:center;padding:8px 16px;border-bottom:1px solid #f1f5f9;transition:background-color .2s}.floating-uploader .uploader-content .upload-queue .upload-item:last-child{border-bottom:none}.floating-uploader .uploader-content .upload-queue .upload-item.status-uploading{background:#f0f9ff}.floating-uploader .uploader-content .upload-queue .upload-item.status-completed{background:#f0fdf4}.floating-uploader .uploader-content .upload-queue .upload-item.status-error{background:#fef2f2}.floating-uploader .uploader-content .upload-queue .upload-item .file-info{display:flex;align-items:center;gap:8px;flex:1;min-width:0}.floating-uploader .uploader-content .upload-queue .upload-item .file-info .status-icon{flex-shrink:0}.floating-uploader .uploader-content .upload-queue .upload-item .file-info .file-details{min-width:0;flex:1}.floating-uploader .uploader-content .upload-queue .upload-item .file-info .file-details .file-name{font-size:13px;font-weight:500;color:#1e293b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin:0}.floating-uploader .uploader-content .upload-queue .upload-item .file-info .file-details .file-status{font-size:11px;margin:0}.floating-uploader .uploader-content .upload-queue .upload-item .file-info .file-details .file-status span{font-weight:500}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress{display:flex;align-items:center;gap:8px;margin:0 8px;min-width:80px}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress .progress-bar{flex:1;height:3px;background:#e2e8f0;border-radius:2px;overflow:hidden}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress .progress-bar .progress-fill{height:100%;background:#3b82f6;transition:width .3s ease}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress .progress-text{font-size:10px;color:#64748b;min-width:24px;text-align:right}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions{display:flex;gap:4px}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn{display:flex;align-items:center;justify-content:center;width:20px;height:20px;border:none;background:transparent;border-radius:4px;cursor:pointer;transition:all .2s;color:#64748b}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn:hover{background:#e2e8f0}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn.cancel-btn:hover{background:#fef2f2;color:#dc2626}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn.retry-btn:hover{background:#f0f9ff;color:#3b82f6}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn.success-btn{color:#16a34a}.floating-uploader .uploader-content .hidden-uploader{display:none}.floating-uploader .uploader-footer{padding:8px 16px;background:#f8fafc;border-top:1px solid #e2e8f0}.floating-uploader .uploader-footer .footer-stats{display:flex;gap:12px;font-size:11px}.floating-uploader .uploader-footer .footer-stats .stat{display:flex;align-items:center;gap:4px;color:#64748b}.floating-uploader .uploader-footer .footer-stats .stat.uploading{color:#3b82f6}.floating-uploader .uploader-footer .footer-stats .stat.completed{color:#16a34a}.floating-uploader .uploader-footer .footer-stats .stat.failed{color:#dc2626}@media (max-width: 640px){.floating-uploader{bottom:10px;right:10px;left:10px;width:auto;max-width:none}}@media (prefers-color-scheme: dark){.floating-uploader{background:#1e293b;border-color:#334155;box-shadow:0 8px 32px #0000004d}.floating-uploader.uploading{border-color:#3b82f6;box-shadow:0 8px 32px #3b82f633}.floating-uploader .uploader-header{background:#334155;border-bottom-color:#475569}.floating-uploader .uploader-header.draggable-header:hover{background:#475569}.floating-uploader .uploader-header.draggable-header:active{background:#64748b}.floating-uploader .uploader-header .header-left .upload-icon{background:#3b82f6}.floating-uploader .uploader-header .header-left .upload-info .upload-title{color:#f1f5f9}.floating-uploader .uploader-header .header-left .upload-info .upload-summary,.floating-uploader .uploader-header .header-actions .action-btn{color:#94a3b8}.floating-uploader .uploader-header .header-actions .action-btn:hover{background:#475569;color:#f1f5f9}.floating-uploader .uploader-header .header-actions .action-btn.close-btn:hover{background:#7f1d1d;color:#fca5a5}.floating-uploader .uploader-content .upload-zone{border-color:#475569;background:#334155}.floating-uploader .uploader-content .upload-zone:hover{border-color:#3b82f6;background:#1e3a8a}.floating-uploader .uploader-content .upload-zone.drag-over{border-color:#60a5fa;background:#1e40af}.floating-uploader .uploader-content .upload-zone .upload-zone-content .upload-icon{color:#94a3b8}.floating-uploader .uploader-content .upload-zone .upload-zone-content .upload-text .upload-title{color:#f1f5f9}.floating-uploader .uploader-content .upload-zone:hover .upload-zone-content .upload-icon{color:#60a5fa}.floating-uploader .uploader-content .upload-zone.drag-over .upload-zone-content .upload-icon{color:#93c5fd}.floating-uploader .uploader-content .upload-queue .upload-item{border-bottom-color:#334155}.floating-uploader .uploader-content .upload-queue .upload-item.status-uploading{background:#1e3a8a}.floating-uploader .uploader-content .upload-queue .upload-item.status-completed{background:#14532d}.floating-uploader .uploader-content .upload-queue .upload-item.status-error{background:#7f1d1d}.floating-uploader .uploader-content .upload-queue .upload-item .file-info .file-details .file-name{color:#f1f5f9}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress .progress-bar{background:#475569}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress .progress-bar .progress-fill{background:#3b82f6}.floating-uploader .uploader-content .upload-queue .upload-item .file-progress .progress-text,.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn{color:#94a3b8}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn:hover{background:#475569}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn.cancel-btn:hover{background:#7f1d1d;color:#fca5a5}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn.retry-btn:hover{background:#1e3a8a;color:#60a5fa}.floating-uploader .uploader-content .upload-queue .upload-item .upload-actions .action-btn.success-btn{color:#4ade80}.floating-uploader .uploader-footer{background:#334155;border-top-color:#475569}.floating-uploader .uploader-footer .footer-stats .stat{color:#94a3b8}.floating-uploader .uploader-footer .footer-stats .stat.uploading{color:#60a5fa}.floating-uploader .uploader-footer .footer-stats .stat.completed{color:#4ade80}.floating-uploader .uploader-footer .footer-stats .stat.failed{color:#fca5a5}}@keyframes slideInUp{0%{transform:translateY(100%);opacity:0}to{transform:translateY(0);opacity:1}}@keyframes slideOutDown{0%{transform:translateY(0);opacity:1}to{transform:translateY(100%);opacity:0}}.floating-uploader.animating{animation:slideInUp .3s cubic-bezier(.4,0,.2,1)}.floating-uploader.animating.hiding{animation:slideOutDown .3s cubic-bezier(.4,0,.2,1)}.no-uploads-message{padding:2rem;text-align:center;color:#6b7280}.no-uploads-message .message-content{display:flex;flex-direction:column;align-items:center;gap:1rem}.no-uploads-message .message-content .message-icon{color:#9ca3af;opacity:.7}.no-uploads-message .message-content .message-text h4{margin:0 0 .5rem;font-size:1.1rem;font-weight:600;color:#374151}.no-uploads-message .message-content .message-text p{margin:0;font-size:.9rem;color:#6b7280}\n"] }]
|
|
3941
3960
|
}], ctorParameters: () => [] });
|
|
3942
3961
|
|
|
3943
3962
|
class CideEleFileInputComponent {
|
|
@@ -4395,7 +4414,9 @@ class CideEleFileInputComponent {
|
|
|
4395
4414
|
isMultiple: true
|
|
4396
4415
|
};
|
|
4397
4416
|
files.forEach((file, index) => {
|
|
4398
|
-
|
|
4417
|
+
const componentId = this.id();
|
|
4418
|
+
console.log(`📤 [FileInput-${componentId}] Uploading file ${index + 1}/${totalFiles}: "${file.name}" with group ID: ${groupId}`);
|
|
4419
|
+
console.log(`📤 [FileInput-${componentId}] Upload data:`, uploadDataWithGroupId);
|
|
4399
4420
|
this.fileManagerService.uploadFile(file, uploadDataWithGroupId, (progress) => {
|
|
4400
4421
|
// Calculate overall progress
|
|
4401
4422
|
const fileProgress = progress / totalFiles;
|
|
@@ -4678,12 +4699,18 @@ class CideEleFileInputComponent {
|
|
|
4678
4699
|
*/
|
|
4679
4700
|
getUploadCount() {
|
|
4680
4701
|
const groupId = this.groupId();
|
|
4702
|
+
const componentId = this.id();
|
|
4681
4703
|
if (!groupId) {
|
|
4704
|
+
console.log(`📊 [FileInput-${componentId}] No group ID, returning all uploads count:`, this.fileManagerService.activeUploads().size);
|
|
4682
4705
|
// If no group ID, return all uploads
|
|
4683
4706
|
return this.fileManagerService.activeUploads().size;
|
|
4684
4707
|
}
|
|
4685
4708
|
// Use service method to get all files for this group
|
|
4686
4709
|
const allFiles = this.fileManagerService.getAllFilesForGroup(groupId);
|
|
4710
|
+
console.log(`📊 [FileInput-${componentId}] Upload count for group ${groupId}:`, {
|
|
4711
|
+
count: allFiles.length,
|
|
4712
|
+
files: allFiles.map(f => ({ id: f.fileId, name: f.fileName, stage: f.stage }))
|
|
4713
|
+
});
|
|
4687
4714
|
return allFiles.length;
|
|
4688
4715
|
}
|
|
4689
4716
|
/**
|
|
@@ -4691,26 +4718,40 @@ class CideEleFileInputComponent {
|
|
|
4691
4718
|
*/
|
|
4692
4719
|
hasActiveUploads() {
|
|
4693
4720
|
const groupId = this.groupId();
|
|
4721
|
+
const componentId = this.id();
|
|
4694
4722
|
if (!groupId) {
|
|
4695
|
-
|
|
4696
|
-
|
|
4723
|
+
const hasActive = Array.from(this.fileManagerService.activeUploads().values()).some(upload => upload.stage !== 'complete');
|
|
4724
|
+
console.log(`📊 [FileInput-${componentId}] No group ID, checking all active uploads:`, hasActive);
|
|
4725
|
+
return hasActive;
|
|
4697
4726
|
}
|
|
4698
4727
|
// Use service method to get all files and check for active ones
|
|
4699
4728
|
const allFiles = this.fileManagerService.getAllFilesForGroup(groupId);
|
|
4700
|
-
|
|
4729
|
+
const hasActive = allFiles.some(file => file.stage !== 'complete');
|
|
4730
|
+
console.log(`📊 [FileInput-${componentId}] Active uploads for group ${groupId}:`, {
|
|
4731
|
+
hasActive,
|
|
4732
|
+
activeFiles: allFiles.filter(f => f.stage !== 'complete').map(f => ({ id: f.fileId, name: f.fileName, stage: f.stage }))
|
|
4733
|
+
});
|
|
4734
|
+
return hasActive;
|
|
4701
4735
|
}
|
|
4702
4736
|
/**
|
|
4703
4737
|
* Get count of active (non-completed) uploads for this component's group ID
|
|
4704
4738
|
*/
|
|
4705
4739
|
getActiveUploadCount() {
|
|
4706
4740
|
const groupId = this.groupId();
|
|
4741
|
+
const componentId = this.id();
|
|
4707
4742
|
if (!groupId) {
|
|
4708
|
-
|
|
4709
|
-
|
|
4743
|
+
const count = Array.from(this.fileManagerService.activeUploads().values()).filter(upload => upload.stage !== 'complete').length;
|
|
4744
|
+
console.log(`📊 [FileInput-${componentId}] No group ID, active upload count:`, count);
|
|
4745
|
+
return count;
|
|
4710
4746
|
}
|
|
4711
4747
|
// Use service method to get all files and filter active ones
|
|
4712
4748
|
const allFiles = this.fileManagerService.getAllFilesForGroup(groupId);
|
|
4713
|
-
|
|
4749
|
+
const activeCount = allFiles.filter(file => file.stage !== 'complete').length;
|
|
4750
|
+
console.log(`📊 [FileInput-${componentId}] Active upload count for group ${groupId}:`, {
|
|
4751
|
+
count: activeCount,
|
|
4752
|
+
activeFiles: allFiles.filter(f => f.stage !== 'complete').map(f => ({ id: f.fileId, name: f.fileName, stage: f.stage }))
|
|
4753
|
+
});
|
|
4754
|
+
return activeCount;
|
|
4714
4755
|
}
|
|
4715
4756
|
/**
|
|
4716
4757
|
* Show floating uploader (alias for showUploader for template)
|
|
@@ -4724,13 +4765,13 @@ class CideEleFileInputComponent {
|
|
|
4724
4765
|
getDragDropZoneClasses() {
|
|
4725
4766
|
const classes = [];
|
|
4726
4767
|
if (this.isDragOver()) {
|
|
4727
|
-
classes.push('!border-blue-500', '!bg-blue-100', 'dark:!bg-blue-900/30', 'scale-[1.01]');
|
|
4768
|
+
classes.push('!tw-border-blue-500', '!tw-bg-blue-100', 'dark:!tw-bg-blue-900/30', 'tw-scale-[1.01]');
|
|
4728
4769
|
}
|
|
4729
4770
|
if (this.disabledSignal()) {
|
|
4730
|
-
classes.push('opacity-50', 'cursor-not-allowed', '!hover:border-gray-300', '!hover:bg-gray-50', 'dark:!hover:bg-gray-800');
|
|
4771
|
+
classes.push('tw-opacity-50', 'tw-cursor-not-allowed', '!hover:tw-border-gray-300', '!hover:tw-bg-gray-50', 'dark:!hover:tw-bg-gray-800');
|
|
4731
4772
|
}
|
|
4732
4773
|
if (this.hasFiles()) {
|
|
4733
|
-
classes.push('!border-emerald-500', '!bg-emerald-50', 'dark:!bg-emerald-900/20', 'hover:!border-emerald-600', 'hover:!bg-emerald-100', 'dark:hover:!bg-emerald-900/30');
|
|
4774
|
+
classes.push('!tw-border-emerald-500', '!tw-bg-emerald-50', 'dark:!tw-bg-emerald-900/20', 'hover:!tw-border-emerald-600', 'hover:!tw-bg-emerald-100', 'dark:hover:!tw-bg-emerald-900/30');
|
|
4734
4775
|
}
|
|
4735
4776
|
return classes.join(' ');
|
|
4736
4777
|
}
|
|
@@ -4738,12 +4779,28 @@ class CideEleFileInputComponent {
|
|
|
4738
4779
|
* Get dynamic classes for icon
|
|
4739
4780
|
*/
|
|
4740
4781
|
getIconClasses() {
|
|
4741
|
-
const classes = ['text-gray-500', 'dark:text-gray-400'];
|
|
4782
|
+
const classes = ['tw-text-gray-500', 'dark:tw-text-gray-400'];
|
|
4742
4783
|
if (this.isDragOver()) {
|
|
4743
|
-
classes.push('!text-blue-500', 'dark:!text-blue-400');
|
|
4784
|
+
classes.push('!tw-text-blue-500', 'dark:!tw-text-blue-400');
|
|
4744
4785
|
}
|
|
4745
4786
|
else if (this.hasFiles()) {
|
|
4746
|
-
classes.push('!text-emerald-500', 'dark:!text-emerald-400');
|
|
4787
|
+
classes.push('!tw-text-emerald-500', 'dark:!tw-text-emerald-400');
|
|
4788
|
+
}
|
|
4789
|
+
return classes.join(' ');
|
|
4790
|
+
}
|
|
4791
|
+
/**
|
|
4792
|
+
* Get dynamic classes for preview box
|
|
4793
|
+
*/
|
|
4794
|
+
getPreviewBoxClasses() {
|
|
4795
|
+
const classes = [];
|
|
4796
|
+
if (this.isDragOver()) {
|
|
4797
|
+
classes.push('!tw-border-blue-500', '!tw-bg-blue-100', 'dark:!tw-bg-blue-900/30');
|
|
4798
|
+
}
|
|
4799
|
+
if (this.disabledSignal()) {
|
|
4800
|
+
classes.push('tw-opacity-50', 'tw-cursor-not-allowed', '!hover:tw-border-gray-300', '!hover:tw-bg-gray-50', 'dark:!hover:tw-bg-gray-800');
|
|
4801
|
+
}
|
|
4802
|
+
if (this.hasImages()) {
|
|
4803
|
+
classes.push('!tw-border-emerald-500', '!tw-bg-emerald-50', 'dark:!tw-bg-emerald-900/20');
|
|
4747
4804
|
}
|
|
4748
4805
|
return classes.join(' ');
|
|
4749
4806
|
}
|
|
@@ -4943,7 +5000,7 @@ class CideEleFileInputComponent {
|
|
|
4943
5000
|
useExisting: CideEleFileInputComponent,
|
|
4944
5001
|
multi: true
|
|
4945
5002
|
}
|
|
4946
|
-
], usesOnChanges: true, ngImport: i0, template: "<div class=\"flex flex-col gap-2\">\n <!-- Label (shown when not in preview box mode or when preview box mode but no label override) -->\n @if (labelSignal() && !isPreviewBoxMode()) {\n <label class=\"block text-sm font-medium text-gray-700 dark:text-gray-200 mb-1.5 leading-5\" [attr.for]=\"'cide-file-input-' + id()\">\n {{ labelSignal() }}@if (requiredSignal()) {<span class=\"text-red-500 dark:text-red-400\"> *</span>}\n </label>\n }\n \n <!-- Preview Box Mode -->\n @if (isPreviewBoxMode()) {\n <div class=\"cide-file-input-preview-box-container\">\n <!-- Hidden file input -->\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"hidden\"\n />\n \n <!-- Preview Box -->\n <div \n class=\"cide-file-input-preview-box\"\n [class.cide-file-input-preview-box-disabled]=\"disabledSignal()\"\n [class.cide-file-input-preview-box-has-image]=\"hasImages()\"\n [class.cide-file-input-preview-box-drag-over]=\"isDragOver()\"\n [style.width]=\"previewWidthSignal()\"\n [style.height]=\"previewHeightSignal()\"\n (click)=\"triggerFileSelect()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n [attr.title]=\"disabledSignal() ? 'File selection disabled' : placeholderTextSignal()\">\n \n <!-- No Image State -->\n @if (!hasImages()) {\n <div class=\"cide-file-input-preview-box-placeholder\">\n <div class=\"cide-file-input-preview-box-icon\">\n <cide-ele-icon>{{ isDragOver() ? '\uD83D\uDCC1' : placeholderIconSignal() }}</cide-ele-icon>\n </div>\n <div class=\"cide-file-input-preview-box-text\">\n {{ isDragOver() ? 'Drop files here...' : placeholderTextSignal() }}\n </div>\n </div>\n }\n \n <!-- Image Preview State -->\n @if (hasImages()) {\n <div class=\"cide-file-input-preview-box-content\">\n <img \n [src]=\"previewUrls()[0]\" \n [alt]=\"fileNames()[0] || 'Preview image'\"\n class=\"cide-file-input-preview-box-image\">\n <div class=\"cide-file-input-preview-box-overlay\">\n <div class=\"cide-file-input-preview-box-overlay-text\">Click to change</div>\n </div>\n @if (!disabledSignal()) {\n <button \n type=\"button\" \n class=\"cide-file-input-preview-box-remove\"\n (click)=\"clearFiles(); $event.stopPropagation()\"\n title=\"Remove image\">\n \u00D7\n </button>\n }\n </div>\n }\n </div>\n \n <!-- File name display for preview box mode -->\n @if (hasImages() && fileNames().length && showFileNameSignal()) {\n <div class=\"cide-file-input-preview-box-filename\">\n {{ fileNames()[0] }}\n </div>\n }\n </div>\n }\n\n <!-- Standard Mode -->\n @if (!isPreviewBoxMode()) {\n <!-- Hidden file input -->\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"hidden\"\n />\n \n <!-- Modern Drag and Drop Zone -->\n <div \n class=\"border-2 border-dashed border-gray-300 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-800 cursor-pointer transition-all duration-200 min-h-[60px] hover:border-blue-500 hover:bg-blue-50 dark:hover:bg-blue-900/20\"\n [class]=\"getDragDropZoneClasses()\"\n (click)=\"triggerFileSelect()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\n \n <div class=\"flex items-center justify-between p-3 gap-3\">\n <!-- Icon and Text -->\n <div class=\"flex items-center gap-2.5 flex-1 min-w-0\">\n <cide-ele-icon class=\"flex-shrink-0 transition-colors duration-200\" \n [class]=\"getIconClasses()\" \n size=\"sm\">\n {{ isDragOver() ? 'file_download' : (hasFiles() ? 'check_circle' : 'cloud_upload') }}\n </cide-ele-icon>\n \n <div class=\"flex flex-col gap-0.5 min-w-0\">\n @if (isDragOver()) {\n <span class=\"text-sm font-medium text-blue-700 dark:text-blue-300 whitespace-nowrap overflow-hidden text-ellipsis\">Drop files here</span>\n } @else if (hasFiles()) {\n <span class=\"text-sm font-medium text-emerald-700 dark:text-emerald-300 whitespace-nowrap overflow-hidden text-ellipsis\">\n @if (multipleSignal() && fileNames().length > 1) {\n {{ fileNames().length }} files selected\n } @else {\n {{ fileNames()[0] }}\n }\n </span>\n @if (totalFileSize() > 0) {\n <span class=\"text-xs text-emerald-600 dark:text-emerald-400\">{{ fileSizeInMB() }} MB</span>\n }\n } @else {\n <span class=\"text-sm font-medium text-gray-700 dark:text-gray-300 whitespace-nowrap overflow-hidden text-ellipsis\">\n {{ multipleSignal() ? 'Choose files or drag here' : 'Choose file or drag here' }}\n </span>\n }\n </div>\n </div>\n \n <!-- Action Buttons -->\n <div class=\"flex gap-1 flex-shrink-0\">\n @if (hasFiles()) {\n <button type=\"button\" \n class=\"flex items-center justify-center w-6 h-6 border-none rounded bg-transparent cursor-pointer transition-all duration-200 text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20 hover:text-red-700\" \n (click)=\"clearFiles(); $event.stopPropagation()\"\n title=\"Clear files\">\n <cide-ele-icon size=\"xs\">close</cide-ele-icon>\n </button>\n }\n </div>\n </div>\n </div>\n }\n \n <!-- Image Preview Section (only for standard mode) -->\n @if (isImagePreviewAvailable() && !isPreviewBoxMode()) {\n <div class=\"cide-file-input-preview\">\n <div class=\"cide-file-input-preview-label\">Preview:</div>\n <div class=\"cide-file-input-preview-container\">\n @for (previewUrl of previewUrls(); track previewUrl; let i = $index) {\n <div \n class=\"cide-file-input-preview-item\"\n [style.width]=\"previewWidthSignal()\"\n [style.height]=\"previewHeightSignal()\">\n <button \n type=\"button\" \n class=\"cide-file-input-preview-remove\"\n (click)=\"removePreview(i)\"\n title=\"Remove image\">\n \u00D7\n </button>\n <img \n [src]=\"previewUrl\" \n [alt]=\"fileNames()[i] || 'Preview image'\"\n class=\"cide-file-input-preview-image\"\n loading=\"lazy\">\n <div class=\"cide-file-input-preview-filename\">{{ fileNames()[i] }}</div>\n </div>\n }\n </div>\n </div>\n }\n \n <!-- Upload Status and Show Files Button (only for multiple file inputs) -->\n @if (multiple && showFloatingUploaderSignal() && (getUploadCount() > 0 || hasActiveUploads() || hasEverUploaded())) {\n <div class=\"flex items-center justify-between py-1.5 gap-2\">\n <div class=\"flex items-center gap-2\">\n <cide-ele-icon class=\"text-blue-600 dark:text-blue-400\" size=\"sm\">cloud_upload</cide-ele-icon>\n <span class=\"text-sm text-gray-700 dark:text-gray-300\">\n @if (hasActiveUploads()) {\n {{ getActiveUploadCount() }} uploading\n } @else if (getUploadCount() > 0) {\n {{ getUploadCount() }} completed\n } @else if (hasEverUploaded()) {\n View uploads\n }\n </span>\n </div>\n <button \n type=\"button\" \n class=\"flex items-center justify-center w-8 h-8 rounded-md bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 text-gray-600 dark:text-gray-300 transition-colors duration-200 border-none cursor-pointer\"\n (click)=\"showFloatingUploaderDialog()\"\n title=\"View upload progress and history\">\n <cide-ele-icon size=\"sm\">visibility</cide-ele-icon>\n </button>\n </div>\n }\n \n @if (errorTextSignal()) {\n <div class=\"text-sm text-red-600 dark:text-red-400 mt-1\">{{ errorTextSignal() }}</div>\n }\n @if (helperTextSignal() && !errorTextSignal()) {\n <div class=\"text-sm text-gray-500 dark:text-gray-400 mt-1\">{{ helperTextSignal() }}</div>\n }\n</div> ", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }] });
|
|
5003
|
+
], 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\" [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\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"tw-hidden\"\n />\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()\"\n [style.width]=\"previewWidthSignal()\"\n [style.height]=\"previewHeightSignal()\"\n (click)=\"triggerFileSelect()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n [attr.title]=\"disabledSignal() ? 'File selection disabled' : placeholderTextSignal()\">\n \n <!-- No Image State -->\n @if (!hasImages()) {\n <div class=\"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' : 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 \n [src]=\"previewUrls()[0]\" \n [alt]=\"fileNames()[0] || 'Preview image'\"\n class=\"tw-w-full tw-h-full tw-object-cover tw-rounded-lg\">\n <div 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\">Click to change</div>\n </div>\n @if (!disabledSignal()) {\n <button \n 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()\"\n title=\"Remove image\">\n \u00D7\n </button>\n }\n </div>\n }\n </div>\n \n <!-- File name display for preview box mode -->\n @if (hasImages() && fileNames().length && showFileNameSignal()) {\n <div class=\"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 </div>\n }\n\n <!-- Standard Mode -->\n @if (!isPreviewBoxMode()) {\n <!-- Hidden file input -->\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"tw-hidden\"\n />\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()\"\n (click)=\"triggerFileSelect()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (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\" \n [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 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 files here</span>\n } @else if (hasFiles()) {\n <span 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 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 @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()\"\n title=\"Clear files\">\n <cide-ele-icon size=\"xs\">close</cide-ele-icon>\n </button>\n }\n </div>\n </div>\n </div>\n }\n \n <!-- Image Preview Section (only for standard mode) -->\n @if (isImagePreviewAvailable() && !isPreviewBoxMode()) {\n <div class=\"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()\"\n [style.height]=\"previewHeightSignal()\">\n <button \n 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)\"\n title=\"Remove image\">\n \u00D7\n </button>\n <img \n [src]=\"previewUrl\" \n [alt]=\"fileNames()[i] || 'Preview image'\"\n class=\"tw-w-full tw-h-full tw-object-cover\"\n loading=\"lazy\">\n <div 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\">{{ 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 \n 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()\"\n title=\"View upload progress and history\">\n <cide-ele-icon size=\"sm\">visibility</cide-ele-icon>\n </button>\n </div>\n }\n \n @if (errorTextSignal()) {\n <div class=\"tw-text-sm tw-text-red-600 dark:tw-text-red-400 tw-mt-1\">{{ errorTextSignal() }}</div>\n }\n @if (helperTextSignal() && !errorTextSignal()) {\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"] }] });
|
|
4947
5004
|
}
|
|
4948
5005
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFileInputComponent, decorators: [{
|
|
4949
5006
|
type: Component,
|
|
@@ -4958,7 +5015,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
4958
5015
|
useExisting: CideEleFileInputComponent,
|
|
4959
5016
|
multi: true
|
|
4960
5017
|
}
|
|
4961
|
-
], template: "<div class=\"flex flex-col gap-2\">\n <!-- Label (shown when not in preview box mode or when preview box mode but no label override) -->\n @if (labelSignal() && !isPreviewBoxMode()) {\n <label class=\"block text-sm font-medium text-gray-700 dark:text-gray-200 mb-1.5 leading-5\" [attr.for]=\"'cide-file-input-' + id()\">\n {{ labelSignal() }}@if (requiredSignal()) {<span class=\"text-red-500 dark:text-red-400\"> *</span>}\n </label>\n }\n \n <!-- Preview Box Mode -->\n @if (isPreviewBoxMode()) {\n <div class=\"cide-file-input-preview-box-container\">\n <!-- Hidden file input -->\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"hidden\"\n />\n \n <!-- Preview Box -->\n <div \n class=\"cide-file-input-preview-box\"\n [class.cide-file-input-preview-box-disabled]=\"disabledSignal()\"\n [class.cide-file-input-preview-box-has-image]=\"hasImages()\"\n [class.cide-file-input-preview-box-drag-over]=\"isDragOver()\"\n [style.width]=\"previewWidthSignal()\"\n [style.height]=\"previewHeightSignal()\"\n (click)=\"triggerFileSelect()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n [attr.title]=\"disabledSignal() ? 'File selection disabled' : placeholderTextSignal()\">\n \n <!-- No Image State -->\n @if (!hasImages()) {\n <div class=\"cide-file-input-preview-box-placeholder\">\n <div class=\"cide-file-input-preview-box-icon\">\n <cide-ele-icon>{{ isDragOver() ? '\uD83D\uDCC1' : placeholderIconSignal() }}</cide-ele-icon>\n </div>\n <div class=\"cide-file-input-preview-box-text\">\n {{ isDragOver() ? 'Drop files here...' : placeholderTextSignal() }}\n </div>\n </div>\n }\n \n <!-- Image Preview State -->\n @if (hasImages()) {\n <div class=\"cide-file-input-preview-box-content\">\n <img \n [src]=\"previewUrls()[0]\" \n [alt]=\"fileNames()[0] || 'Preview image'\"\n class=\"cide-file-input-preview-box-image\">\n <div class=\"cide-file-input-preview-box-overlay\">\n <div class=\"cide-file-input-preview-box-overlay-text\">Click to change</div>\n </div>\n @if (!disabledSignal()) {\n <button \n type=\"button\" \n class=\"cide-file-input-preview-box-remove\"\n (click)=\"clearFiles(); $event.stopPropagation()\"\n title=\"Remove image\">\n \u00D7\n </button>\n }\n </div>\n }\n </div>\n \n <!-- File name display for preview box mode -->\n @if (hasImages() && fileNames().length && showFileNameSignal()) {\n <div class=\"cide-file-input-preview-box-filename\">\n {{ fileNames()[0] }}\n </div>\n }\n </div>\n }\n\n <!-- Standard Mode -->\n @if (!isPreviewBoxMode()) {\n <!-- Hidden file input -->\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"hidden\"\n />\n \n <!-- Modern Drag and Drop Zone -->\n <div \n class=\"border-2 border-dashed border-gray-300 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-800 cursor-pointer transition-all duration-200 min-h-[60px] hover:border-blue-500 hover:bg-blue-50 dark:hover:bg-blue-900/20\"\n [class]=\"getDragDropZoneClasses()\"\n (click)=\"triggerFileSelect()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\n \n <div class=\"flex items-center justify-between p-3 gap-3\">\n <!-- Icon and Text -->\n <div class=\"flex items-center gap-2.5 flex-1 min-w-0\">\n <cide-ele-icon class=\"flex-shrink-0 transition-colors duration-200\" \n [class]=\"getIconClasses()\" \n size=\"sm\">\n {{ isDragOver() ? 'file_download' : (hasFiles() ? 'check_circle' : 'cloud_upload') }}\n </cide-ele-icon>\n \n <div class=\"flex flex-col gap-0.5 min-w-0\">\n @if (isDragOver()) {\n <span class=\"text-sm font-medium text-blue-700 dark:text-blue-300 whitespace-nowrap overflow-hidden text-ellipsis\">Drop files here</span>\n } @else if (hasFiles()) {\n <span class=\"text-sm font-medium text-emerald-700 dark:text-emerald-300 whitespace-nowrap overflow-hidden text-ellipsis\">\n @if (multipleSignal() && fileNames().length > 1) {\n {{ fileNames().length }} files selected\n } @else {\n {{ fileNames()[0] }}\n }\n </span>\n @if (totalFileSize() > 0) {\n <span class=\"text-xs text-emerald-600 dark:text-emerald-400\">{{ fileSizeInMB() }} MB</span>\n }\n } @else {\n <span class=\"text-sm font-medium text-gray-700 dark:text-gray-300 whitespace-nowrap overflow-hidden text-ellipsis\">\n {{ multipleSignal() ? 'Choose files or drag here' : 'Choose file or drag here' }}\n </span>\n }\n </div>\n </div>\n \n <!-- Action Buttons -->\n <div class=\"flex gap-1 flex-shrink-0\">\n @if (hasFiles()) {\n <button type=\"button\" \n class=\"flex items-center justify-center w-6 h-6 border-none rounded bg-transparent cursor-pointer transition-all duration-200 text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20 hover:text-red-700\" \n (click)=\"clearFiles(); $event.stopPropagation()\"\n title=\"Clear files\">\n <cide-ele-icon size=\"xs\">close</cide-ele-icon>\n </button>\n }\n </div>\n </div>\n </div>\n }\n \n <!-- Image Preview Section (only for standard mode) -->\n @if (isImagePreviewAvailable() && !isPreviewBoxMode()) {\n <div class=\"cide-file-input-preview\">\n <div class=\"cide-file-input-preview-label\">Preview:</div>\n <div class=\"cide-file-input-preview-container\">\n @for (previewUrl of previewUrls(); track previewUrl; let i = $index) {\n <div \n class=\"cide-file-input-preview-item\"\n [style.width]=\"previewWidthSignal()\"\n [style.height]=\"previewHeightSignal()\">\n <button \n type=\"button\" \n class=\"cide-file-input-preview-remove\"\n (click)=\"removePreview(i)\"\n title=\"Remove image\">\n \u00D7\n </button>\n <img \n [src]=\"previewUrl\" \n [alt]=\"fileNames()[i] || 'Preview image'\"\n class=\"cide-file-input-preview-image\"\n loading=\"lazy\">\n <div class=\"cide-file-input-preview-filename\">{{ fileNames()[i] }}</div>\n </div>\n }\n </div>\n </div>\n }\n \n <!-- Upload Status and Show Files Button (only for multiple file inputs) -->\n @if (multiple && showFloatingUploaderSignal() && (getUploadCount() > 0 || hasActiveUploads() || hasEverUploaded())) {\n <div class=\"flex items-center justify-between py-1.5 gap-2\">\n <div class=\"flex items-center gap-2\">\n <cide-ele-icon class=\"text-blue-600 dark:text-blue-400\" size=\"sm\">cloud_upload</cide-ele-icon>\n <span class=\"text-sm text-gray-700 dark:text-gray-300\">\n @if (hasActiveUploads()) {\n {{ getActiveUploadCount() }} uploading\n } @else if (getUploadCount() > 0) {\n {{ getUploadCount() }} completed\n } @else if (hasEverUploaded()) {\n View uploads\n }\n </span>\n </div>\n <button \n type=\"button\" \n class=\"flex items-center justify-center w-8 h-8 rounded-md bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 text-gray-600 dark:text-gray-300 transition-colors duration-200 border-none cursor-pointer\"\n (click)=\"showFloatingUploaderDialog()\"\n title=\"View upload progress and history\">\n <cide-ele-icon size=\"sm\">visibility</cide-ele-icon>\n </button>\n </div>\n }\n \n @if (errorTextSignal()) {\n <div class=\"text-sm text-red-600 dark:text-red-400 mt-1\">{{ errorTextSignal() }}</div>\n }\n @if (helperTextSignal() && !errorTextSignal()) {\n <div class=\"text-sm text-gray-500 dark:text-gray-400 mt-1\">{{ helperTextSignal() }}</div>\n }\n</div> " }]
|
|
5018
|
+
], 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\" [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\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"tw-hidden\"\n />\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()\"\n [style.width]=\"previewWidthSignal()\"\n [style.height]=\"previewHeightSignal()\"\n (click)=\"triggerFileSelect()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n [attr.title]=\"disabledSignal() ? 'File selection disabled' : placeholderTextSignal()\">\n \n <!-- No Image State -->\n @if (!hasImages()) {\n <div class=\"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' : 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 \n [src]=\"previewUrls()[0]\" \n [alt]=\"fileNames()[0] || 'Preview image'\"\n class=\"tw-w-full tw-h-full tw-object-cover tw-rounded-lg\">\n <div 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\">Click to change</div>\n </div>\n @if (!disabledSignal()) {\n <button \n 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()\"\n title=\"Remove image\">\n \u00D7\n </button>\n }\n </div>\n }\n </div>\n \n <!-- File name display for preview box mode -->\n @if (hasImages() && fileNames().length && showFileNameSignal()) {\n <div class=\"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 </div>\n }\n\n <!-- Standard Mode -->\n @if (!isPreviewBoxMode()) {\n <!-- Hidden file input -->\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"tw-hidden\"\n />\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()\"\n (click)=\"triggerFileSelect()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (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\" \n [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 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 files here</span>\n } @else if (hasFiles()) {\n <span 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 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 @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()\"\n title=\"Clear files\">\n <cide-ele-icon size=\"xs\">close</cide-ele-icon>\n </button>\n }\n </div>\n </div>\n </div>\n }\n \n <!-- Image Preview Section (only for standard mode) -->\n @if (isImagePreviewAvailable() && !isPreviewBoxMode()) {\n <div class=\"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()\"\n [style.height]=\"previewHeightSignal()\">\n <button \n 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)\"\n title=\"Remove image\">\n \u00D7\n </button>\n <img \n [src]=\"previewUrl\" \n [alt]=\"fileNames()[i] || 'Preview image'\"\n class=\"tw-w-full tw-h-full tw-object-cover\"\n loading=\"lazy\">\n <div 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\">{{ 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 \n 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()\"\n title=\"View upload progress and history\">\n <cide-ele-icon size=\"sm\">visibility</cide-ele-icon>\n </button>\n </div>\n }\n \n @if (errorTextSignal()) {\n <div class=\"tw-text-sm tw-text-red-600 dark:tw-text-red-400 tw-mt-1\">{{ errorTextSignal() }}</div>\n }\n @if (helperTextSignal() && !errorTextSignal()) {\n <div class=\"tw-text-sm tw-text-gray-500 dark:tw-text-gray-400 tw-mt-1\">{{ helperTextSignal() }}</div>\n }\n</div> " }]
|
|
4962
5019
|
}], ctorParameters: () => [], propDecorators: { label: [{
|
|
4963
5020
|
type: Input
|
|
4964
5021
|
}], accept: [{
|