cloud-ide-element 1.0.93 → 1.0.96
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 +218 -491
- package/fesm2022/cloud-ide-element.mjs.map +1 -1
- package/index.d.ts +256 -328
- package/package.json +1 -1
|
@@ -2600,6 +2600,7 @@ class ICoreCyfmSave {
|
|
|
2600
2600
|
cyfm_file_status_sygmt;
|
|
2601
2601
|
cyfm_group_id; // Group ID for multiple file uploads
|
|
2602
2602
|
cyfm_isactive;
|
|
2603
|
+
cyfm_ismultiple;
|
|
2603
2604
|
cyfm_file_base64 = "";
|
|
2604
2605
|
cyfm_temp_unique_id = "";
|
|
2605
2606
|
constructor(init) {
|
|
@@ -2719,6 +2720,7 @@ class CideEleFileManagerService {
|
|
|
2719
2720
|
cyfm_file_status_sygmt: uploadOptions?.fileStatus || 'file_manager_file_status_active',
|
|
2720
2721
|
cyfm_group_id: uploadOptions?.groupId, // Group ID for multiple file uploads
|
|
2721
2722
|
cyfm_isactive: true,
|
|
2723
|
+
cyfm_ismultiple: uploadOptions?.isMultiple || false,
|
|
2722
2724
|
cyfm_file_base64: base64Data,
|
|
2723
2725
|
cyfm_temp_unique_id: fileId
|
|
2724
2726
|
};
|
|
@@ -2945,6 +2947,22 @@ class CideEleFileManagerService {
|
|
|
2945
2947
|
getFetchedFilesByGroupId(groupId) {
|
|
2946
2948
|
return this._fetchedFiles().get(groupId) || [];
|
|
2947
2949
|
}
|
|
2950
|
+
/**
|
|
2951
|
+
* Signal to trigger floating uploader visibility
|
|
2952
|
+
*/
|
|
2953
|
+
_showFloatingUploader = signal(false, ...(ngDevMode ? [{ debugName: "_showFloatingUploader" }] : []));
|
|
2954
|
+
showFloatingUploader = this._showFloatingUploader.asReadonly();
|
|
2955
|
+
/**
|
|
2956
|
+
* Trigger floating uploader to show
|
|
2957
|
+
*/
|
|
2958
|
+
triggerFloatingUploaderShow() {
|
|
2959
|
+
console.log('🎬 [FileManagerService] Triggering floating uploader to show');
|
|
2960
|
+
this._showFloatingUploader.set(true);
|
|
2961
|
+
// Reset after a short delay to allow components to react
|
|
2962
|
+
setTimeout(() => {
|
|
2963
|
+
this._showFloatingUploader.set(false);
|
|
2964
|
+
}, 100);
|
|
2965
|
+
}
|
|
2948
2966
|
/**
|
|
2949
2967
|
* Get all files (active uploads + fetched files) for a group ID
|
|
2950
2968
|
* @param groupId The group ID to get files for
|
|
@@ -3281,8 +3299,16 @@ class CideEleFloatingFileUploaderComponent {
|
|
|
3281
3299
|
failedUploads = computed(() => Array.from(this.activeUploads().values()).filter(upload => upload.stage === 'error'), ...(ngDevMode ? [{ debugName: "failedUploads" }] : []));
|
|
3282
3300
|
// Animation states
|
|
3283
3301
|
isAnimating = signal(false, ...(ngDevMode ? [{ debugName: "isAnimating" }] : []));
|
|
3302
|
+
// Drag functionality
|
|
3303
|
+
isDragging = signal(false, ...(ngDevMode ? [{ debugName: "isDragging" }] : []));
|
|
3304
|
+
position = signal({ x: 0, y: 0 }, ...(ngDevMode ? [{ debugName: "position" }] : []));
|
|
3305
|
+
dragOffset = { x: 0, y: 0 };
|
|
3306
|
+
// File drag and drop functionality
|
|
3307
|
+
isDragOver = signal(false, ...(ngDevMode ? [{ debugName: "isDragOver" }] : []));
|
|
3284
3308
|
constructor() {
|
|
3285
3309
|
console.log('🚀 [FloatingFileUploader] Component initialized');
|
|
3310
|
+
// Initialize default position
|
|
3311
|
+
this.initializePosition();
|
|
3286
3312
|
// Set up effect to show/hide floating uploader based on service state
|
|
3287
3313
|
effect(() => {
|
|
3288
3314
|
const hasActiveUploads = this.hasActiveUploads();
|
|
@@ -3300,6 +3326,14 @@ class CideEleFloatingFileUploaderComponent {
|
|
|
3300
3326
|
this.showWithAnimation();
|
|
3301
3327
|
}
|
|
3302
3328
|
});
|
|
3329
|
+
// Set up effect to listen for manual show trigger from service
|
|
3330
|
+
effect(() => {
|
|
3331
|
+
const shouldShow = this.fileManagerService.showFloatingUploader();
|
|
3332
|
+
if (shouldShow && !this.isVisible()) {
|
|
3333
|
+
console.log('👁️ [FloatingFileUploader] Showing floating uploader due to service trigger');
|
|
3334
|
+
this.showWithAnimation();
|
|
3335
|
+
}
|
|
3336
|
+
});
|
|
3303
3337
|
}
|
|
3304
3338
|
ngOnInit() {
|
|
3305
3339
|
// Set up drag and drop listeners
|
|
@@ -3614,13 +3648,20 @@ class CideEleFloatingFileUploaderComponent {
|
|
|
3614
3648
|
.subscribe({
|
|
3615
3649
|
next: (files) => {
|
|
3616
3650
|
console.log('✅ [FloatingFileUploader] Files fetched and stored:', files.length);
|
|
3651
|
+
// Force show the uploader after files are loaded
|
|
3652
|
+
this.showWithAnimation();
|
|
3617
3653
|
},
|
|
3618
3654
|
error: (error) => {
|
|
3619
3655
|
console.error('❌ [FloatingFileUploader] Failed to fetch files:', error);
|
|
3656
|
+
// Still show the uploader even if fetch fails
|
|
3657
|
+
this.showWithAnimation();
|
|
3620
3658
|
}
|
|
3621
3659
|
});
|
|
3622
3660
|
}
|
|
3623
|
-
|
|
3661
|
+
else {
|
|
3662
|
+
// Show immediately if no group ID
|
|
3663
|
+
this.showWithAnimation();
|
|
3664
|
+
}
|
|
3624
3665
|
}
|
|
3625
3666
|
/**
|
|
3626
3667
|
* Check if there are any uploads for the current group
|
|
@@ -3635,15 +3676,154 @@ class CideEleFloatingFileUploaderComponent {
|
|
|
3635
3676
|
// Note: This would need to be enhanced based on how group IDs are stored in the file manager service
|
|
3636
3677
|
return this.hasUploads();
|
|
3637
3678
|
}
|
|
3679
|
+
/**
|
|
3680
|
+
* Handle drag over event for file drop
|
|
3681
|
+
*/
|
|
3682
|
+
onDragOver(event) {
|
|
3683
|
+
event.preventDefault();
|
|
3684
|
+
event.stopPropagation();
|
|
3685
|
+
this.isDragOver.set(true);
|
|
3686
|
+
}
|
|
3687
|
+
/**
|
|
3688
|
+
* Handle drag leave event for file drop
|
|
3689
|
+
*/
|
|
3690
|
+
onDragLeave(event) {
|
|
3691
|
+
event.preventDefault();
|
|
3692
|
+
event.stopPropagation();
|
|
3693
|
+
this.isDragOver.set(false);
|
|
3694
|
+
}
|
|
3695
|
+
/**
|
|
3696
|
+
* Handle drop event for file drop
|
|
3697
|
+
*/
|
|
3698
|
+
onDrop(event) {
|
|
3699
|
+
event.preventDefault();
|
|
3700
|
+
event.stopPropagation();
|
|
3701
|
+
this.isDragOver.set(false);
|
|
3702
|
+
const files = event.dataTransfer?.files;
|
|
3703
|
+
if (files && files.length > 0) {
|
|
3704
|
+
this.handleFileSelection(Array.from(files));
|
|
3705
|
+
}
|
|
3706
|
+
}
|
|
3707
|
+
/**
|
|
3708
|
+
* Trigger file input click
|
|
3709
|
+
*/
|
|
3710
|
+
triggerFileInput() {
|
|
3711
|
+
const fileInput = document.querySelector('input[type="file"]');
|
|
3712
|
+
if (fileInput) {
|
|
3713
|
+
fileInput.click();
|
|
3714
|
+
}
|
|
3715
|
+
}
|
|
3716
|
+
/**
|
|
3717
|
+
* Handle file input change
|
|
3718
|
+
*/
|
|
3719
|
+
onFileInputChange(event) {
|
|
3720
|
+
const input = event.target;
|
|
3721
|
+
if (input.files && input.files.length > 0) {
|
|
3722
|
+
this.handleFileSelection(Array.from(input.files));
|
|
3723
|
+
input.value = ''; // Reset input
|
|
3724
|
+
}
|
|
3725
|
+
}
|
|
3726
|
+
/**
|
|
3727
|
+
* Handle file selection from drag/drop or file input
|
|
3728
|
+
*/
|
|
3729
|
+
handleFileSelection(files) {
|
|
3730
|
+
console.log('📁 [FloatingFileUploader] Files selected:', files.map(f => f.name));
|
|
3731
|
+
const groupId = this.currentGroupId() || this.generateGroupId();
|
|
3732
|
+
this.currentGroupId.set(groupId);
|
|
3733
|
+
// Upload files using the file manager service
|
|
3734
|
+
files.forEach(file => {
|
|
3735
|
+
this.fileManagerService.uploadFile(file, {
|
|
3736
|
+
groupId: groupId,
|
|
3737
|
+
isMultiple: true,
|
|
3738
|
+
userId: this.currentUserId()
|
|
3739
|
+
});
|
|
3740
|
+
});
|
|
3741
|
+
}
|
|
3742
|
+
/**
|
|
3743
|
+
* Generate a unique group ID
|
|
3744
|
+
*/
|
|
3745
|
+
generateGroupId() {
|
|
3746
|
+
return `group_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
3747
|
+
}
|
|
3748
|
+
/**
|
|
3749
|
+
* Start dragging the uploader
|
|
3750
|
+
*/
|
|
3751
|
+
startDrag(event) {
|
|
3752
|
+
event.preventDefault();
|
|
3753
|
+
const clientX = event instanceof MouseEvent ? event.clientX : event.touches[0].clientX;
|
|
3754
|
+
const clientY = event instanceof MouseEvent ? event.clientY : event.touches[0].clientY;
|
|
3755
|
+
const currentPos = this.position();
|
|
3756
|
+
this.dragOffset = {
|
|
3757
|
+
x: clientX - currentPos.x,
|
|
3758
|
+
y: clientY - currentPos.y
|
|
3759
|
+
};
|
|
3760
|
+
this.isDragging.set(true);
|
|
3761
|
+
// Add event listeners for drag and end
|
|
3762
|
+
const moveHandler = (e) => this.onDrag(e);
|
|
3763
|
+
const endHandler = () => this.endDrag(moveHandler, endHandler);
|
|
3764
|
+
document.addEventListener('mousemove', moveHandler);
|
|
3765
|
+
document.addEventListener('mouseup', endHandler);
|
|
3766
|
+
document.addEventListener('touchmove', moveHandler);
|
|
3767
|
+
document.addEventListener('touchend', endHandler);
|
|
3768
|
+
// Prevent text selection during drag
|
|
3769
|
+
document.body.style.userSelect = 'none';
|
|
3770
|
+
}
|
|
3771
|
+
/**
|
|
3772
|
+
* Handle dragging movement
|
|
3773
|
+
*/
|
|
3774
|
+
onDrag(event) {
|
|
3775
|
+
if (!this.isDragging())
|
|
3776
|
+
return;
|
|
3777
|
+
event.preventDefault();
|
|
3778
|
+
const clientX = event instanceof MouseEvent ? event.clientX : event.touches[0].clientX;
|
|
3779
|
+
const clientY = event instanceof MouseEvent ? event.clientY : event.touches[0].clientY;
|
|
3780
|
+
const newX = clientX - this.dragOffset.x;
|
|
3781
|
+
const newY = clientY - this.dragOffset.y;
|
|
3782
|
+
// Constrain to viewport bounds
|
|
3783
|
+
const viewportWidth = window.innerWidth;
|
|
3784
|
+
const viewportHeight = window.innerHeight;
|
|
3785
|
+
const uploaderWidth = 320; // From CSS
|
|
3786
|
+
const uploaderHeight = 500; // Max height from CSS
|
|
3787
|
+
const constrainedX = Math.max(0, Math.min(newX, viewportWidth - uploaderWidth));
|
|
3788
|
+
const constrainedY = Math.max(0, Math.min(newY, viewportHeight - uploaderHeight));
|
|
3789
|
+
this.position.set({ x: constrainedX, y: constrainedY });
|
|
3790
|
+
}
|
|
3791
|
+
/**
|
|
3792
|
+
* End dragging
|
|
3793
|
+
*/
|
|
3794
|
+
endDrag(moveHandler, endHandler) {
|
|
3795
|
+
this.isDragging.set(false);
|
|
3796
|
+
// Remove event listeners
|
|
3797
|
+
document.removeEventListener('mousemove', moveHandler);
|
|
3798
|
+
document.removeEventListener('mouseup', endHandler);
|
|
3799
|
+
document.removeEventListener('touchmove', moveHandler);
|
|
3800
|
+
document.removeEventListener('touchend', endHandler);
|
|
3801
|
+
// Restore text selection
|
|
3802
|
+
document.body.style.userSelect = '';
|
|
3803
|
+
}
|
|
3804
|
+
/**
|
|
3805
|
+
* Initialize default position
|
|
3806
|
+
*/
|
|
3807
|
+
initializePosition() {
|
|
3808
|
+
// Set initial position to bottom-right corner
|
|
3809
|
+
const viewportWidth = window.innerWidth;
|
|
3810
|
+
const viewportHeight = window.innerHeight;
|
|
3811
|
+
const uploaderWidth = 320;
|
|
3812
|
+
const uploaderHeight = 500;
|
|
3813
|
+
this.position.set({
|
|
3814
|
+
x: viewportWidth - uploaderWidth - 20,
|
|
3815
|
+
y: viewportHeight - uploaderHeight - 20
|
|
3816
|
+
});
|
|
3817
|
+
}
|
|
3638
3818
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFloatingFileUploaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3639
|
-
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\n <!-- Header -->\n <div class=\"uploader-header\">\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 <!-- Overall Progress Bar - Fixed at top -->\n @if (uploadQueue().length > 0 || activeUploads().size > 0) {\n <div class=\"overall-progress sticky-progress\">\n <div class=\"progress-bar\">\n <div class=\"progress-fill\" [style.width.%]=\"getOverallProgress()\"></div>\n </div>\n <div class=\"progress-text\">{{ getOverallProgress() }}%</div>\n </div>\n }\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;bottom:20px;right:20px;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 .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 .overall-progress{padding:12px 16px;border-bottom:1px solid #e2e8f0}.floating-uploader .uploader-content .overall-progress.sticky-progress{position:sticky;top:0;background:#fff;z-index:10;box-shadow:0 2px 4px #0000000d}.floating-uploader .uploader-content .overall-progress .progress-bar{width:100%;height:4px;background:#e2e8f0;border-radius:2px;overflow:hidden;margin-bottom:4px}.floating-uploader .uploader-content .overall-progress .progress-bar .progress-fill{height:100%;background:#3b82f6;transition:width .3s ease}.floating-uploader .uploader-content .overall-progress .progress-text{font-size:12px;color:#64748b;text-align:center;display:block}.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 .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 .overall-progress{border-bottom-color:#475569}.floating-uploader .uploader-content .overall-progress.sticky-progress{background:#1e293b;box-shadow:0 2px 4px #0003}.floating-uploader .uploader-content .overall-progress .progress-bar{background:#475569}.floating-uploader .uploader-content .overall-progress .progress-bar .progress-fill{background:#3b82f6}.floating-uploader .uploader-content .overall-progress .progress-text{color:#94a3b8}.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"] }] });
|
|
3819
|
+
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=\"md\">cloud_upload</cide-ele-icon>\n \n <div class=\"upload-text\">\n <div class=\"upload-title\">\n {{ isDragOver() ? 'Drop files here' : 'Add More Files' }}\n </div>\n <div class=\"upload-subtitle\">\n {{ isDragOver() ? 'Release to upload' : 'Drag and drop files or click to browse' }}\n </div>\n </div>\n \n <button type=\"button\" \n class=\"upload-browse-btn\"\n (click)=\"triggerFileInput(); $event.stopPropagation()\">\n <cide-ele-icon size=\"xs\">add</cide-ele-icon>\n Choose Files\n </button>\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:12px 16px;padding:20px;border:2px dashed #d1d5db;border-radius:8px;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.02)}.floating-uploader .uploader-content .upload-zone .upload-zone-content{display:flex;flex-direction:column;align-items:center;gap:8px}.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:14px;font-weight:600;color:#374151;margin:0}.floating-uploader .uploader-content .upload-zone .upload-zone-content .upload-text .upload-subtitle{font-size:12px;color:#6b7280;margin:0}.floating-uploader .uploader-content .upload-zone .upload-zone-content .upload-browse-btn{display:flex;align-items:center;gap:4px;padding:6px 12px;background:#3b82f6;color:#fff;border:none;border-radius:6px;font-size:12px;font-weight:500;cursor:pointer;transition:background-color .2s ease}.floating-uploader .uploader-content .upload-zone .upload-zone-content .upload-browse-btn:hover{background:#2563eb}.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 .upload-zone-content .upload-text .upload-subtitle{color:#94a3b8}.floating-uploader .uploader-content .upload-zone .upload-zone-content .upload-browse-btn{background:#3b82f6;color:#fff}.floating-uploader .uploader-content .upload-zone .upload-zone-content .upload-browse-btn:hover{background:#2563eb}.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"] }] });
|
|
3640
3820
|
}
|
|
3641
3821
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFloatingFileUploaderComponent, decorators: [{
|
|
3642
3822
|
type: Component,
|
|
3643
3823
|
args: [{ selector: 'cide-ele-floating-file-uploader', standalone: true, imports: [
|
|
3644
3824
|
CommonModule,
|
|
3645
3825
|
CideIconComponent
|
|
3646
|
-
], template: "<!-- Floating File Uploader Container -->\n@if (isVisible()) {\n<div class=\"floating-uploader\" \n [class.minimized]=\"isMinimized()\" \n [class.animating]=\"isAnimating()\">\n\n <!-- Header -->\n <div class=\"uploader-header\">\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 <!-- Overall Progress Bar - Fixed at top -->\n @if (uploadQueue().length > 0 || activeUploads().size > 0) {\n <div class=\"overall-progress sticky-progress\">\n <div class=\"progress-bar\">\n <div class=\"progress-fill\" [style.width.%]=\"getOverallProgress()\"></div>\n </div>\n <div class=\"progress-text\">{{ getOverallProgress() }}%</div>\n </div>\n }\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;bottom:20px;right:20px;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 .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 .overall-progress{padding:12px 16px;border-bottom:1px solid #e2e8f0}.floating-uploader .uploader-content .overall-progress.sticky-progress{position:sticky;top:0;background:#fff;z-index:10;box-shadow:0 2px 4px #0000000d}.floating-uploader .uploader-content .overall-progress .progress-bar{width:100%;height:4px;background:#e2e8f0;border-radius:2px;overflow:hidden;margin-bottom:4px}.floating-uploader .uploader-content .overall-progress .progress-bar .progress-fill{height:100%;background:#3b82f6;transition:width .3s ease}.floating-uploader .uploader-content .overall-progress .progress-text{font-size:12px;color:#64748b;text-align:center;display:block}.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 .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 .overall-progress{border-bottom-color:#475569}.floating-uploader .uploader-content .overall-progress.sticky-progress{background:#1e293b;box-shadow:0 2px 4px #0003}.floating-uploader .uploader-content .overall-progress .progress-bar{background:#475569}.floating-uploader .uploader-content .overall-progress .progress-bar .progress-fill{background:#3b82f6}.floating-uploader .uploader-content .overall-progress .progress-text{color:#94a3b8}.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"] }]
|
|
3826
|
+
], 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=\"md\">cloud_upload</cide-ele-icon>\n \n <div class=\"upload-text\">\n <div class=\"upload-title\">\n {{ isDragOver() ? 'Drop files here' : 'Add More Files' }}\n </div>\n <div class=\"upload-subtitle\">\n {{ isDragOver() ? 'Release to upload' : 'Drag and drop files or click to browse' }}\n </div>\n </div>\n \n <button type=\"button\" \n class=\"upload-browse-btn\"\n (click)=\"triggerFileInput(); $event.stopPropagation()\">\n <cide-ele-icon size=\"xs\">add</cide-ele-icon>\n Choose Files\n </button>\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:12px 16px;padding:20px;border:2px dashed #d1d5db;border-radius:8px;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.02)}.floating-uploader .uploader-content .upload-zone .upload-zone-content{display:flex;flex-direction:column;align-items:center;gap:8px}.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:14px;font-weight:600;color:#374151;margin:0}.floating-uploader .uploader-content .upload-zone .upload-zone-content .upload-text .upload-subtitle{font-size:12px;color:#6b7280;margin:0}.floating-uploader .uploader-content .upload-zone .upload-zone-content .upload-browse-btn{display:flex;align-items:center;gap:4px;padding:6px 12px;background:#3b82f6;color:#fff;border:none;border-radius:6px;font-size:12px;font-weight:500;cursor:pointer;transition:background-color .2s ease}.floating-uploader .uploader-content .upload-zone .upload-zone-content .upload-browse-btn:hover{background:#2563eb}.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 .upload-zone-content .upload-text .upload-subtitle{color:#94a3b8}.floating-uploader .uploader-content .upload-zone .upload-zone-content .upload-browse-btn{background:#3b82f6;color:#fff}.floating-uploader .uploader-content .upload-zone .upload-zone-content .upload-browse-btn:hover{background:#2563eb}.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"] }]
|
|
3647
3827
|
}], ctorParameters: () => [] });
|
|
3648
3828
|
|
|
3649
3829
|
class CideEleFileInputComponent {
|
|
@@ -4097,7 +4277,8 @@ class CideEleFileInputComponent {
|
|
|
4097
4277
|
// IMPORTANT: All files use the SAME group ID that was generated before starting uploads
|
|
4098
4278
|
const uploadDataWithGroupId = {
|
|
4099
4279
|
...this.uploadDataSignal(),
|
|
4100
|
-
groupId: groupId
|
|
4280
|
+
groupId: groupId,
|
|
4281
|
+
isMultiple: true
|
|
4101
4282
|
};
|
|
4102
4283
|
files.forEach((file, index) => {
|
|
4103
4284
|
console.log(`📤 [FileInput] Uploading file ${index + 1}/${totalFiles}: "${file.name}" with group ID: ${groupId}`);
|
|
@@ -4345,8 +4526,36 @@ class CideEleFileInputComponent {
|
|
|
4345
4526
|
* This can be called to show the floating uploader even when no files are selected
|
|
4346
4527
|
*/
|
|
4347
4528
|
showUploader() {
|
|
4348
|
-
|
|
4349
|
-
|
|
4529
|
+
console.log('👁️ [FileInput] Manually showing floating uploader');
|
|
4530
|
+
if (!this.showFloatingUploaderSignal()) {
|
|
4531
|
+
console.log('⚠️ [FileInput] Floating uploader is disabled');
|
|
4532
|
+
return;
|
|
4533
|
+
}
|
|
4534
|
+
const groupId = this.groupId();
|
|
4535
|
+
if (groupId) {
|
|
4536
|
+
// Fetch files for the group and trigger floating uploader to show
|
|
4537
|
+
this.fileManagerService.fetchAndStoreFilesByGroupId(groupId)
|
|
4538
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
4539
|
+
.subscribe({
|
|
4540
|
+
next: (files) => {
|
|
4541
|
+
console.log('✅ [FileInput] Files fetched for floating uploader:', files.length);
|
|
4542
|
+
// Trigger the floating uploader to show via service
|
|
4543
|
+
this.fileManagerService.triggerFloatingUploaderShow();
|
|
4544
|
+
},
|
|
4545
|
+
error: (error) => {
|
|
4546
|
+
console.error('❌ [FileInput] Failed to fetch files for floating uploader:', error);
|
|
4547
|
+
// Still trigger show even if fetch fails
|
|
4548
|
+
this.fileManagerService.triggerFloatingUploaderShow();
|
|
4549
|
+
}
|
|
4550
|
+
});
|
|
4551
|
+
}
|
|
4552
|
+
else {
|
|
4553
|
+
// No group ID, just trigger show
|
|
4554
|
+
this.fileManagerService.triggerFloatingUploaderShow();
|
|
4555
|
+
}
|
|
4556
|
+
// Fallback to direct component access if available
|
|
4557
|
+
if (this.floatingUploader) {
|
|
4558
|
+
console.log('👁️ [FileInput] Using direct component access as fallback');
|
|
4350
4559
|
this.floatingUploader.showUploader(this.floatingUploaderGroupIdSignal());
|
|
4351
4560
|
}
|
|
4352
4561
|
}
|
|
@@ -4591,7 +4800,7 @@ class CideEleFileInputComponent {
|
|
|
4591
4800
|
useExisting: CideEleFileInputComponent,
|
|
4592
4801
|
multi: true
|
|
4593
4802
|
}
|
|
4594
|
-
], usesOnChanges: true, ngImport: i0, template: "<div class=\"cide-file-input\">\n <!-- Label (shown when not in preview box mode or when preview box mode but no label override) -->\n @if (labelSignal() && !isPreviewBoxMode()) {\n <label class=\"cide-file-input-label\" [attr.for]=\"'cide-file-input-' + id()\">\n {{ labelSignal() }}@if (requiredSignal()) {<span class=\"cide-file-input-required\"> *</span>}\n </label>\n }\n \n <!-- Preview Box Mode -->\n @if (isPreviewBoxMode()) {\n <div class=\"cide-file-input-preview-box-container\">\n <!-- Hidden file input -->\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"cide-file-input-hidden\"\n />\n \n <!-- Preview Box -->\n <div \n class=\"cide-file-input-preview-box\"\n [class.cide-file-input-preview-box-disabled]=\"disabledSignal()\"\n [class.cide-file-input-preview-box-has-image]=\"hasImages()\"\n [class.cide-file-input-preview-box-drag-over]=\"isDragOver()\"\n [style.width]=\"previewWidthSignal()\"\n [style.height]=\"previewHeightSignal()\"\n (click)=\"triggerFileSelect()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n [attr.title]=\"disabledSignal() ? 'File selection disabled' : placeholderTextSignal()\">\n \n <!-- No Image State -->\n @if (!hasImages()) {\n <div class=\"cide-file-input-preview-box-placeholder\">\n <div class=\"cide-file-input-preview-box-icon\">\n <cide-ele-icon>{{ isDragOver() ? '\uD83D\uDCC1' : placeholderIconSignal() }}</cide-ele-icon>\n </div>\n <div class=\"cide-file-input-preview-box-text\">\n {{ isDragOver() ? 'Drop files here...' : placeholderTextSignal() }}\n </div>\n </div>\n }\n \n <!-- Image Preview State -->\n @if (hasImages()) {\n <div class=\"cide-file-input-preview-box-content\">\n <img \n [src]=\"previewUrls()[0]\" \n [alt]=\"fileNames()[0] || 'Preview image'\"\n class=\"cide-file-input-preview-box-image\">\n <div class=\"cide-file-input-preview-box-overlay\">\n <div class=\"cide-file-input-preview-box-overlay-text\">Click to change</div>\n </div>\n @if (!disabledSignal()) {\n <button \n type=\"button\" \n class=\"cide-file-input-preview-box-remove\"\n (click)=\"clearFiles(); $event.stopPropagation()\"\n title=\"Remove image\">\n \u00D7\n </button>\n }\n </div>\n }\n </div>\n \n <!-- File name display for preview box mode -->\n @if (hasImages() && fileNames().length && showFileNameSignal()) {\n <div class=\"cide-file-input-preview-box-filename\">\n {{ fileNames()[0] }}\n </div>\n }\n </div>\n }\n\n <!-- Standard Mode -->\n @if (!isPreviewBoxMode()) {\n <div \n class=\"cide-file-input-wrapper\"\n [class.cide-file-input-drag-over]=\"isDragOver()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"cide-file-input-element\"\n />\n @if (hasFiles()) {\n <button type=\"button\" class=\"cide-file-input-clear\" (click)=\"clearFiles()\">\n Clear\n </button>\n }\n </div>\n @if (hasFiles() && !isPreviewBoxMode()) {\n <div class=\"cide-file-input-files\">\n @if (multipleSignal() && fileNames().length > 1) {\n <!-- Show file count for multiple files -->\n <div class=\"cide-file-input-multiple-count\">\n {{ fileNames().length }} files selected\n </div>\n } @else {\n <!-- Show individual file names for single file or when only one file selected -->\n @for (name of fileNames(); track name) {\n <span>{{ name }}</span>\n }\n }\n <!-- Angular 20: Display file size using new computed values -->\n @if (totalFileSize() > 0) {\n <div class=\"cide-file-input-size\">\n Total size: {{ fileSizeInMB() }} MB\n </div>\n }\n </div>\n }\n }\n \n <!-- Image Preview Section (only for standard mode) -->\n @if (isImagePreviewAvailable() && !isPreviewBoxMode()) {\n <div class=\"cide-file-input-preview\">\n <div class=\"cide-file-input-preview-label\">Preview:</div>\n <div class=\"cide-file-input-preview-container\">\n @for (previewUrl of previewUrls(); track previewUrl; let i = $index) {\n <div \n class=\"cide-file-input-preview-item\"\n [style.width]=\"previewWidthSignal()\"\n [style.height]=\"previewHeightSignal()\">\n <button \n type=\"button\" \n class=\"cide-file-input-preview-remove\"\n (click)=\"removePreview(i)\"\n title=\"Remove image\">\n \u00D7\n </button>\n <img \n [src]=\"previewUrl\" \n [alt]=\"fileNames()[i] || 'Preview image'\"\n class=\"cide-file-input-preview-image\"\n loading=\"lazy\">\n <div class=\"cide-file-input-preview-filename\">{{ fileNames()[i] }}</div>\n </div>\n }\n </div>\n </div>\n }\n \n <!-- Upload Status and Show Files Button -->\n @if (showFloatingUploaderSignal() && (getUploadCount() > 0 || hasActiveUploads() || hasEverUploaded())) {\n <div class=\"cide-file-input-upload-status\">\n <div class=\"cide-file-input-upload-count\">\n <cide-ele-icon size=\"sm\">cloud_upload</cide-ele-icon>\n <span class=\"upload-count-text\">\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=\"cide-file-input-show-files-btn\"\n (click)=\"showFloatingUploaderDialog()\"\n title=\"View upload progress and history\">\n <cide-ele-icon size=\"sm\">visibility</cide-ele-icon>\n Show Files\n </button>\n </div>\n }\n \n @if (errorTextSignal()) {\n <div class=\"cide-file-input-error\">{{ errorTextSignal() }}</div>\n }\n @if (helperTextSignal() && !errorTextSignal()) {\n <div class=\"cide-file-input-helper\">{{ helperTextSignal() }}</div>\n }\n</div> ", styles: [".cide-file-input{display:flex;flex-direction:column;gap:.5rem}.cide-file-input-label{font-weight:500;margin-bottom:.25rem}.cide-file-input-required{color:#d32f2f;font-weight:700}.cide-file-input-wrapper{display:flex;align-items:center;gap:.5rem;border:2px dashed transparent;border-radius:.5rem;padding:.5rem;transition:all .2s ease-in-out}.cide-file-input-wrapper.cide-file-input-drag-over{border-color:#3b82f6;background-color:#eff6ff;transform:scale(1.02)}.cide-file-input-element{flex:1}.cide-file-input-clear{background:none;border:none;color:#d32f2f;cursor:pointer;font-size:.9rem}.cide-file-input-files{font-size:.95rem;color:#333;margin-top:.25rem}.cide-file-input-multiple-count{display:inline-flex;align-items:center;gap:.5rem;padding:.5rem 1rem;background-color:#f0f9ff;border:1px solid #0ea5e9;border-radius:.5rem;color:#0369a1;font-weight:500;font-size:.9rem}.cide-file-input-size{margin-top:.5rem;padding:.25rem .5rem;background-color:#f3f4f6;border-radius:.25rem;font-size:.75rem;color:#4b5563;font-weight:500}.cide-file-input-error{color:#d32f2f;font-size:.9rem}.cide-file-input-helper{color:#666;font-size:.9rem}.cide-file-input-preview{margin-top:.75rem;padding:.75rem;background-color:#f8f9fa;border:1px solid #e1e5e9;border-radius:.375rem}.cide-file-input-preview-label{font-weight:500;margin-bottom:.5rem;color:#374151;font-size:.875rem}.cide-file-input-preview-container{display:flex;flex-wrap:wrap;gap:.75rem}.cide-file-input-preview-item{position:relative;display:flex;flex-direction:column;border:1px solid #d1d5db;border-radius:.5rem;overflow:hidden;background-color:#fff;box-shadow:0 1px 3px #0000001a;transition:box-shadow .2s ease-in-out}.cide-file-input-preview-item:hover{box-shadow:0 4px 6px -1px #0000001a}.cide-file-input-preview-image{width:100%;height:calc(100% - 2rem);object-fit:cover;object-position:center;background-color:#f3f4f6}.cide-file-input-preview-filename{padding:.375rem .5rem;background-color:#f9fafbf2;border-top:1px solid #e5e7eb;font-size:.75rem;color:#374151;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-height:2rem;display:flex;align-items:center;justify-content:center}.cide-file-input-preview-remove{position:absolute;top:.25rem;right:.25rem;width:1.5rem;height:1.5rem;background-color:#ef4444e6;color:#fff;border:none;border-radius:50%;font-size:1rem;font-weight:700;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;z-index:10;transition:all .2s ease-in-out}.cide-file-input-preview-remove:hover{background-color:#dc2626f2;transform:scale(1.1)}.cide-file-input-preview-remove:focus{outline:2px solid #3b82f6;outline-offset:2px}.cide-file-input-hidden{display:none}.cide-file-input-preview-box-container{display:flex;flex-direction:column;gap:.5rem}.cide-file-input-preview-box{position:relative;border:2px dashed #d1d5db;border-radius:.5rem;cursor:pointer;background-color:#f9fafb;display:flex;align-items:center;justify-content:center;overflow:hidden;transition:all .2s ease-in-out}.cide-file-input-preview-box:hover{border-color:#3b82f6;background-color:#eff6ff}.cide-file-input-preview-box.cide-file-input-preview-box-disabled{cursor:not-allowed;opacity:.6;background-color:#f3f4f6}.cide-file-input-preview-box.cide-file-input-preview-box-disabled:hover{border-color:#d1d5db;background-color:#f3f4f6}.cide-file-input-preview-box.cide-file-input-preview-box-has-image{border-style:solid;border-color:#e5e7eb;padding:0}.cide-file-input-preview-box.cide-file-input-preview-box-has-image:hover{border-color:#3b82f6}.cide-file-input-preview-box.cide-file-input-preview-box-has-image:hover .cide-file-input-preview-box-overlay{opacity:1}.cide-file-input-preview-box.cide-file-input-preview-box-drag-over{border-color:#3b82f6!important;background-color:#eff6ff!important;transform:scale(1.02);box-shadow:0 0 0 4px #3b82f61a}.cide-file-input-preview-box.cide-file-input-preview-box-drag-over .cide-file-input-preview-box-placeholder .cide-file-input-preview-box-icon{color:#3b82f6;transform:scale(1.1)}.cide-file-input-preview-box.cide-file-input-preview-box-drag-over .cide-file-input-preview-box-placeholder .cide-file-input-preview-box-text{color:#3b82f6;font-weight:600}.cide-file-input-preview-box-placeholder{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;padding:1rem;text-align:center}.cide-file-input-preview-box-icon{font-size:2rem;color:#6b7280}.cide-file-input-preview-box-text{font-size:.875rem;color:#6b7280;font-weight:500}.cide-file-input-preview-box-content{position:relative;width:100%;height:100%;display:flex;align-items:center;justify-content:center}.cide-file-input-preview-box-image{width:100%;height:100%;object-fit:cover;object-position:center}.cide-file-input-preview-box-overlay{position:absolute;inset:0;background-color:#0009;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .2s ease-in-out}.cide-file-input-preview-box-overlay-text{color:#fff;font-size:.875rem;font-weight:500;text-align:center}.cide-file-input-preview-box-remove{position:absolute;top:.375rem;right:.375rem;width:1.5rem;height:1.5rem;background-color:#ef4444e6;color:#fff;border:none;border-radius:50%;font-size:1rem;font-weight:700;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;z-index:20;transition:all .2s ease-in-out}.cide-file-input-preview-box-remove:hover{background-color:#dc2626f2;transform:scale(1.1)}.cide-file-input-preview-box-remove:focus{outline:2px solid #3b82f6;outline-offset:2px}.cide-file-input-preview-box-filename{font-size:.75rem;color:#374151;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding:.25rem .5rem;background-color:#f3f4f6;border-radius:.25rem;margin-top:.25rem}@media (max-width: 640px){.cide-file-input-preview-container{justify-content:center}.cide-file-input-preview-item{min-width:120px;max-width:150px}.cide-file-input-preview-box-icon{font-size:1.5rem}.cide-file-input-preview-box-text{font-size:.75rem}}.cide-file-input-upload-status{display:flex;align-items:center;justify-content:space-between;padding:.5rem .75rem;background:#f8fafc;border:1px solid #e2e8f0;border-radius:.375rem;margin-top:.5rem;gap:.75rem}.cide-file-input-upload-status .cide-file-input-upload-count{display:flex;align-items:center;gap:.5rem;color:#64748b;font-size:.875rem;font-weight:500}.cide-file-input-upload-status .cide-file-input-upload-count .upload-count-text{color:#1e293b}.cide-file-input-upload-status .cide-file-input-show-files-btn{display:flex;align-items:center;gap:.25rem;padding:.375rem .5rem;background:#3b82f6;color:#fff;border:none;border-radius:.25rem;font-size:.75rem;font-weight:500;cursor:pointer;transition:all .2s ease;min-width:auto;white-space:nowrap}.cide-file-input-upload-status .cide-file-input-show-files-btn:hover{background:#2563eb;transform:translateY(-1px);box-shadow:0 2px 4px #3b82f633}.cide-file-input-upload-status .cide-file-input-show-files-btn:active{transform:translateY(0);box-shadow:0 1px 2px #3b82f61a}@media (prefers-color-scheme: dark){.cide-file-input-upload-status{background:#1e293b;border-color:#334155}.cide-file-input-upload-status .cide-file-input-upload-count{color:#94a3b8}.cide-file-input-upload-status .cide-file-input-upload-count .upload-count-text{color:#f1f5f9}.cide-file-input-upload-status .cide-file-input-show-files-btn{background:#3b82f6}.cide-file-input-upload-status .cide-file-input-show-files-btn:hover{background:#2563eb}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }] });
|
|
4803
|
+
], usesOnChanges: true, ngImport: i0, template: "<div class=\"cide-file-input\">\n <!-- Label (shown when not in preview box mode or when preview box mode but no label override) -->\n @if (labelSignal() && !isPreviewBoxMode()) {\n <label class=\"cide-file-input-label\" [attr.for]=\"'cide-file-input-' + id()\">\n {{ labelSignal() }}@if (requiredSignal()) {<span class=\"cide-file-input-required\"> *</span>}\n </label>\n }\n \n <!-- Preview Box Mode -->\n @if (isPreviewBoxMode()) {\n <div class=\"cide-file-input-preview-box-container\">\n <!-- Hidden file input -->\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"cide-file-input-hidden\"\n />\n \n <!-- Preview Box -->\n <div \n class=\"cide-file-input-preview-box\"\n [class.cide-file-input-preview-box-disabled]=\"disabledSignal()\"\n [class.cide-file-input-preview-box-has-image]=\"hasImages()\"\n [class.cide-file-input-preview-box-drag-over]=\"isDragOver()\"\n [style.width]=\"previewWidthSignal()\"\n [style.height]=\"previewHeightSignal()\"\n (click)=\"triggerFileSelect()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n [attr.title]=\"disabledSignal() ? 'File selection disabled' : placeholderTextSignal()\">\n \n <!-- No Image State -->\n @if (!hasImages()) {\n <div class=\"cide-file-input-preview-box-placeholder\">\n <div class=\"cide-file-input-preview-box-icon\">\n <cide-ele-icon>{{ isDragOver() ? '\uD83D\uDCC1' : placeholderIconSignal() }}</cide-ele-icon>\n </div>\n <div class=\"cide-file-input-preview-box-text\">\n {{ isDragOver() ? 'Drop files here...' : placeholderTextSignal() }}\n </div>\n </div>\n }\n \n <!-- Image Preview State -->\n @if (hasImages()) {\n <div class=\"cide-file-input-preview-box-content\">\n <img \n [src]=\"previewUrls()[0]\" \n [alt]=\"fileNames()[0] || 'Preview image'\"\n class=\"cide-file-input-preview-box-image\">\n <div class=\"cide-file-input-preview-box-overlay\">\n <div class=\"cide-file-input-preview-box-overlay-text\">Click to change</div>\n </div>\n @if (!disabledSignal()) {\n <button \n type=\"button\" \n class=\"cide-file-input-preview-box-remove\"\n (click)=\"clearFiles(); $event.stopPropagation()\"\n title=\"Remove image\">\n \u00D7\n </button>\n }\n </div>\n }\n </div>\n \n <!-- File name display for preview box mode -->\n @if (hasImages() && fileNames().length && showFileNameSignal()) {\n <div class=\"cide-file-input-preview-box-filename\">\n {{ fileNames()[0] }}\n </div>\n }\n </div>\n }\n\n <!-- Standard Mode -->\n @if (!isPreviewBoxMode()) {\n <div \n class=\"cide-file-input-wrapper\"\n [class.cide-file-input-drag-over]=\"isDragOver()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"cide-file-input-element\"\n />\n @if (hasFiles()) {\n <button type=\"button\" class=\"cide-file-input-clear\" (click)=\"clearFiles()\">\n Clear\n </button>\n }\n </div>\n @if (hasFiles() && !isPreviewBoxMode()) {\n <div class=\"cide-file-input-files\">\n @if (multipleSignal() && fileNames().length > 1) {\n <!-- Show file count for multiple files -->\n <div class=\"cide-file-input-multiple-count\">\n {{ fileNames().length }} files selected\n </div>\n } @else {\n <!-- Show individual file names for single file or when only one file selected -->\n @for (name of fileNames(); track name) {\n <span>{{ name }}</span>\n }\n }\n <!-- Angular 20: Display file size using new computed values -->\n @if (totalFileSize() > 0) {\n <div class=\"cide-file-input-size\">\n Total size: {{ fileSizeInMB() }} MB\n </div>\n }\n </div>\n }\n }\n \n <!-- Image Preview Section (only for standard mode) -->\n @if (isImagePreviewAvailable() && !isPreviewBoxMode()) {\n <div class=\"cide-file-input-preview\">\n <div class=\"cide-file-input-preview-label\">Preview:</div>\n <div class=\"cide-file-input-preview-container\">\n @for (previewUrl of previewUrls(); track previewUrl; let i = $index) {\n <div \n class=\"cide-file-input-preview-item\"\n [style.width]=\"previewWidthSignal()\"\n [style.height]=\"previewHeightSignal()\">\n <button \n type=\"button\" \n class=\"cide-file-input-preview-remove\"\n (click)=\"removePreview(i)\"\n title=\"Remove image\">\n \u00D7\n </button>\n <img \n [src]=\"previewUrl\" \n [alt]=\"fileNames()[i] || 'Preview image'\"\n class=\"cide-file-input-preview-image\"\n loading=\"lazy\">\n <div class=\"cide-file-input-preview-filename\">{{ fileNames()[i] }}</div>\n </div>\n }\n </div>\n </div>\n }\n \n <!-- Upload Status and Show Files Button (only for multiple file inputs) -->\n @if (multiple && showFloatingUploaderSignal() && (getUploadCount() > 0 || hasActiveUploads() || hasEverUploaded())) {\n <div class=\"cide-file-input-upload-status\">\n <div class=\"cide-file-input-upload-count\">\n <cide-ele-icon size=\"sm\">cloud_upload</cide-ele-icon>\n <span class=\"upload-count-text\">\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=\"cide-file-input-show-files-icon\"\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=\"cide-file-input-error\">{{ errorTextSignal() }}</div>\n }\n @if (helperTextSignal() && !errorTextSignal()) {\n <div class=\"cide-file-input-helper\">{{ helperTextSignal() }}</div>\n }\n</div> ", styles: [".cide-file-input{display:flex;flex-direction:column;gap:.5rem}.cide-file-input-label{font-weight:500;margin-bottom:.25rem}.cide-file-input-required{color:#d32f2f;font-weight:700}.cide-file-input-wrapper{display:flex;align-items:center;gap:.5rem;border:2px dashed transparent;border-radius:.5rem;padding:.5rem;transition:all .2s ease-in-out}.cide-file-input-wrapper.cide-file-input-drag-over{border-color:#3b82f6;background-color:#eff6ff;transform:scale(1.02)}.cide-file-input-element{flex:1}.cide-file-input-clear{background:none;border:none;color:#d32f2f;cursor:pointer;font-size:.9rem}.cide-file-input-files{font-size:.95rem;color:#333;margin-top:.25rem}.cide-file-input-multiple-count{display:inline-flex;align-items:center;gap:.5rem;padding:.5rem 1rem;background-color:#f0f9ff;border:1px solid #0ea5e9;border-radius:.5rem;color:#0369a1;font-weight:500;font-size:.9rem}.cide-file-input-size{margin-top:.5rem;padding:.25rem .5rem;background-color:#f3f4f6;border-radius:.25rem;font-size:.75rem;color:#4b5563;font-weight:500}.cide-file-input-error{color:#d32f2f;font-size:.9rem}.cide-file-input-helper{color:#666;font-size:.9rem}.cide-file-input-preview{margin-top:.75rem;padding:.75rem;background-color:#f8f9fa;border:1px solid #e1e5e9;border-radius:.375rem}.cide-file-input-preview-label{font-weight:500;margin-bottom:.5rem;color:#374151;font-size:.875rem}.cide-file-input-preview-container{display:flex;flex-wrap:wrap;gap:.75rem}.cide-file-input-preview-item{position:relative;display:flex;flex-direction:column;border:1px solid #d1d5db;border-radius:.5rem;overflow:hidden;background-color:#fff;box-shadow:0 1px 3px #0000001a;transition:box-shadow .2s ease-in-out}.cide-file-input-preview-item:hover{box-shadow:0 4px 6px -1px #0000001a}.cide-file-input-preview-image{width:100%;height:calc(100% - 2rem);object-fit:cover;object-position:center;background-color:#f3f4f6}.cide-file-input-preview-filename{padding:.375rem .5rem;background-color:#f9fafbf2;border-top:1px solid #e5e7eb;font-size:.75rem;color:#374151;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-height:2rem;display:flex;align-items:center;justify-content:center}.cide-file-input-preview-remove{position:absolute;top:.25rem;right:.25rem;width:1.5rem;height:1.5rem;background-color:#ef4444e6;color:#fff;border:none;border-radius:50%;font-size:1rem;font-weight:700;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;z-index:10;transition:all .2s ease-in-out}.cide-file-input-preview-remove:hover{background-color:#dc2626f2;transform:scale(1.1)}.cide-file-input-preview-remove:focus{outline:2px solid #3b82f6;outline-offset:2px}.cide-file-input-hidden{display:none}.cide-file-input-preview-box-container{display:flex;flex-direction:column;gap:.5rem}.cide-file-input-preview-box{position:relative;border:2px dashed #d1d5db;border-radius:.5rem;cursor:pointer;background-color:#f9fafb;display:flex;align-items:center;justify-content:center;overflow:hidden;transition:all .2s ease-in-out}.cide-file-input-preview-box:hover{border-color:#3b82f6;background-color:#eff6ff}.cide-file-input-preview-box.cide-file-input-preview-box-disabled{cursor:not-allowed;opacity:.6;background-color:#f3f4f6}.cide-file-input-preview-box.cide-file-input-preview-box-disabled:hover{border-color:#d1d5db;background-color:#f3f4f6}.cide-file-input-preview-box.cide-file-input-preview-box-has-image{border-style:solid;border-color:#e5e7eb;padding:0}.cide-file-input-preview-box.cide-file-input-preview-box-has-image:hover{border-color:#3b82f6}.cide-file-input-preview-box.cide-file-input-preview-box-has-image:hover .cide-file-input-preview-box-overlay{opacity:1}.cide-file-input-preview-box.cide-file-input-preview-box-drag-over{border-color:#3b82f6!important;background-color:#eff6ff!important;transform:scale(1.02);box-shadow:0 0 0 4px #3b82f61a}.cide-file-input-preview-box.cide-file-input-preview-box-drag-over .cide-file-input-preview-box-placeholder .cide-file-input-preview-box-icon{color:#3b82f6;transform:scale(1.1)}.cide-file-input-preview-box.cide-file-input-preview-box-drag-over .cide-file-input-preview-box-placeholder .cide-file-input-preview-box-text{color:#3b82f6;font-weight:600}.cide-file-input-preview-box-placeholder{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;padding:1rem;text-align:center}.cide-file-input-preview-box-icon{font-size:2rem;color:#6b7280}.cide-file-input-preview-box-text{font-size:.875rem;color:#6b7280;font-weight:500}.cide-file-input-preview-box-content{position:relative;width:100%;height:100%;display:flex;align-items:center;justify-content:center}.cide-file-input-preview-box-image{width:100%;height:100%;object-fit:cover;object-position:center}.cide-file-input-preview-box-overlay{position:absolute;inset:0;background-color:#0009;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .2s ease-in-out}.cide-file-input-preview-box-overlay-text{color:#fff;font-size:.875rem;font-weight:500;text-align:center}.cide-file-input-preview-box-remove{position:absolute;top:.375rem;right:.375rem;width:1.5rem;height:1.5rem;background-color:#ef4444e6;color:#fff;border:none;border-radius:50%;font-size:1rem;font-weight:700;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;z-index:20;transition:all .2s ease-in-out}.cide-file-input-preview-box-remove:hover{background-color:#dc2626f2;transform:scale(1.1)}.cide-file-input-preview-box-remove:focus{outline:2px solid #3b82f6;outline-offset:2px}.cide-file-input-preview-box-filename{font-size:.75rem;color:#374151;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding:.25rem .5rem;background-color:#f3f4f6;border-radius:.25rem;margin-top:.25rem}@media (max-width: 640px){.cide-file-input-preview-container{justify-content:center}.cide-file-input-preview-item{min-width:120px;max-width:150px}.cide-file-input-preview-box-icon{font-size:1.5rem}.cide-file-input-preview-box-text{font-size:.75rem}}.cide-file-input-upload-status{display:flex;align-items:center;justify-content:space-between;padding:.5rem .75rem;background:#f8fafc;border:1px solid #e2e8f0;border-radius:.375rem;margin-top:.5rem;gap:.75rem}.cide-file-input-upload-status .cide-file-input-upload-count{display:flex;align-items:center;gap:.5rem;color:#64748b;font-size:.875rem;font-weight:500}.cide-file-input-upload-status .cide-file-input-upload-count .upload-count-text{color:#1e293b}.cide-file-input-upload-status .cide-file-input-show-files-icon{display:flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;background:transparent;color:#6b7280;border:none;border-radius:.25rem;cursor:pointer;transition:all .2s ease}.cide-file-input-upload-status .cide-file-input-show-files-icon:hover{background:#f3f4f6;color:#3b82f6}.cide-file-input-upload-status .cide-file-input-show-files-icon:active{background:#e5e7eb}@media (prefers-color-scheme: dark){.cide-file-input-upload-status{background:#1e293b;border-color:#334155}.cide-file-input-upload-status .cide-file-input-upload-count{color:#94a3b8}.cide-file-input-upload-status .cide-file-input-upload-count .upload-count-text{color:#f1f5f9}.cide-file-input-upload-status .cide-file-input-show-files-icon{color:#94a3b8}.cide-file-input-upload-status .cide-file-input-show-files-icon:hover{background:#374151;color:#60a5fa}.cide-file-input-upload-status .cide-file-input-show-files-icon:active{background:#4b5563}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }] });
|
|
4595
4804
|
}
|
|
4596
4805
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleFileInputComponent, decorators: [{
|
|
4597
4806
|
type: Component,
|
|
@@ -4606,7 +4815,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
4606
4815
|
useExisting: CideEleFileInputComponent,
|
|
4607
4816
|
multi: true
|
|
4608
4817
|
}
|
|
4609
|
-
], template: "<div class=\"cide-file-input\">\n <!-- Label (shown when not in preview box mode or when preview box mode but no label override) -->\n @if (labelSignal() && !isPreviewBoxMode()) {\n <label class=\"cide-file-input-label\" [attr.for]=\"'cide-file-input-' + id()\">\n {{ labelSignal() }}@if (requiredSignal()) {<span class=\"cide-file-input-required\"> *</span>}\n </label>\n }\n \n <!-- Preview Box Mode -->\n @if (isPreviewBoxMode()) {\n <div class=\"cide-file-input-preview-box-container\">\n <!-- Hidden file input -->\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"cide-file-input-hidden\"\n />\n \n <!-- Preview Box -->\n <div \n class=\"cide-file-input-preview-box\"\n [class.cide-file-input-preview-box-disabled]=\"disabledSignal()\"\n [class.cide-file-input-preview-box-has-image]=\"hasImages()\"\n [class.cide-file-input-preview-box-drag-over]=\"isDragOver()\"\n [style.width]=\"previewWidthSignal()\"\n [style.height]=\"previewHeightSignal()\"\n (click)=\"triggerFileSelect()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n [attr.title]=\"disabledSignal() ? 'File selection disabled' : placeholderTextSignal()\">\n \n <!-- No Image State -->\n @if (!hasImages()) {\n <div class=\"cide-file-input-preview-box-placeholder\">\n <div class=\"cide-file-input-preview-box-icon\">\n <cide-ele-icon>{{ isDragOver() ? '\uD83D\uDCC1' : placeholderIconSignal() }}</cide-ele-icon>\n </div>\n <div class=\"cide-file-input-preview-box-text\">\n {{ isDragOver() ? 'Drop files here...' : placeholderTextSignal() }}\n </div>\n </div>\n }\n \n <!-- Image Preview State -->\n @if (hasImages()) {\n <div class=\"cide-file-input-preview-box-content\">\n <img \n [src]=\"previewUrls()[0]\" \n [alt]=\"fileNames()[0] || 'Preview image'\"\n class=\"cide-file-input-preview-box-image\">\n <div class=\"cide-file-input-preview-box-overlay\">\n <div class=\"cide-file-input-preview-box-overlay-text\">Click to change</div>\n </div>\n @if (!disabledSignal()) {\n <button \n type=\"button\" \n class=\"cide-file-input-preview-box-remove\"\n (click)=\"clearFiles(); $event.stopPropagation()\"\n title=\"Remove image\">\n \u00D7\n </button>\n }\n </div>\n }\n </div>\n \n <!-- File name display for preview box mode -->\n @if (hasImages() && fileNames().length && showFileNameSignal()) {\n <div class=\"cide-file-input-preview-box-filename\">\n {{ fileNames()[0] }}\n </div>\n }\n </div>\n }\n\n <!-- Standard Mode -->\n @if (!isPreviewBoxMode()) {\n <div \n class=\"cide-file-input-wrapper\"\n [class.cide-file-input-drag-over]=\"isDragOver()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"cide-file-input-element\"\n />\n @if (hasFiles()) {\n <button type=\"button\" class=\"cide-file-input-clear\" (click)=\"clearFiles()\">\n Clear\n </button>\n }\n </div>\n @if (hasFiles() && !isPreviewBoxMode()) {\n <div class=\"cide-file-input-files\">\n @if (multipleSignal() && fileNames().length > 1) {\n <!-- Show file count for multiple files -->\n <div class=\"cide-file-input-multiple-count\">\n {{ fileNames().length }} files selected\n </div>\n } @else {\n <!-- Show individual file names for single file or when only one file selected -->\n @for (name of fileNames(); track name) {\n <span>{{ name }}</span>\n }\n }\n <!-- Angular 20: Display file size using new computed values -->\n @if (totalFileSize() > 0) {\n <div class=\"cide-file-input-size\">\n Total size: {{ fileSizeInMB() }} MB\n </div>\n }\n </div>\n }\n }\n \n <!-- Image Preview Section (only for standard mode) -->\n @if (isImagePreviewAvailable() && !isPreviewBoxMode()) {\n <div class=\"cide-file-input-preview\">\n <div class=\"cide-file-input-preview-label\">Preview:</div>\n <div class=\"cide-file-input-preview-container\">\n @for (previewUrl of previewUrls(); track previewUrl; let i = $index) {\n <div \n class=\"cide-file-input-preview-item\"\n [style.width]=\"previewWidthSignal()\"\n [style.height]=\"previewHeightSignal()\">\n <button \n type=\"button\" \n class=\"cide-file-input-preview-remove\"\n (click)=\"removePreview(i)\"\n title=\"Remove image\">\n \u00D7\n </button>\n <img \n [src]=\"previewUrl\" \n [alt]=\"fileNames()[i] || 'Preview image'\"\n class=\"cide-file-input-preview-image\"\n loading=\"lazy\">\n <div class=\"cide-file-input-preview-filename\">{{ fileNames()[i] }}</div>\n </div>\n }\n </div>\n </div>\n }\n \n <!-- Upload Status and Show Files Button -->\n @if (showFloatingUploaderSignal() && (getUploadCount() > 0 || hasActiveUploads() || hasEverUploaded())) {\n <div class=\"cide-file-input-upload-status\">\n <div class=\"cide-file-input-upload-count\">\n <cide-ele-icon size=\"sm\">cloud_upload</cide-ele-icon>\n <span class=\"upload-count-text\">\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=\"cide-file-input-show-files-btn\"\n (click)=\"showFloatingUploaderDialog()\"\n title=\"View upload progress and history\">\n <cide-ele-icon size=\"sm\">visibility</cide-ele-icon>\n Show Files\n </button>\n </div>\n }\n \n @if (errorTextSignal()) {\n <div class=\"cide-file-input-error\">{{ errorTextSignal() }}</div>\n }\n @if (helperTextSignal() && !errorTextSignal()) {\n <div class=\"cide-file-input-helper\">{{ helperTextSignal() }}</div>\n }\n</div> ", styles: [".cide-file-input{display:flex;flex-direction:column;gap:.5rem}.cide-file-input-label{font-weight:500;margin-bottom:.25rem}.cide-file-input-required{color:#d32f2f;font-weight:700}.cide-file-input-wrapper{display:flex;align-items:center;gap:.5rem;border:2px dashed transparent;border-radius:.5rem;padding:.5rem;transition:all .2s ease-in-out}.cide-file-input-wrapper.cide-file-input-drag-over{border-color:#3b82f6;background-color:#eff6ff;transform:scale(1.02)}.cide-file-input-element{flex:1}.cide-file-input-clear{background:none;border:none;color:#d32f2f;cursor:pointer;font-size:.9rem}.cide-file-input-files{font-size:.95rem;color:#333;margin-top:.25rem}.cide-file-input-multiple-count{display:inline-flex;align-items:center;gap:.5rem;padding:.5rem 1rem;background-color:#f0f9ff;border:1px solid #0ea5e9;border-radius:.5rem;color:#0369a1;font-weight:500;font-size:.9rem}.cide-file-input-size{margin-top:.5rem;padding:.25rem .5rem;background-color:#f3f4f6;border-radius:.25rem;font-size:.75rem;color:#4b5563;font-weight:500}.cide-file-input-error{color:#d32f2f;font-size:.9rem}.cide-file-input-helper{color:#666;font-size:.9rem}.cide-file-input-preview{margin-top:.75rem;padding:.75rem;background-color:#f8f9fa;border:1px solid #e1e5e9;border-radius:.375rem}.cide-file-input-preview-label{font-weight:500;margin-bottom:.5rem;color:#374151;font-size:.875rem}.cide-file-input-preview-container{display:flex;flex-wrap:wrap;gap:.75rem}.cide-file-input-preview-item{position:relative;display:flex;flex-direction:column;border:1px solid #d1d5db;border-radius:.5rem;overflow:hidden;background-color:#fff;box-shadow:0 1px 3px #0000001a;transition:box-shadow .2s ease-in-out}.cide-file-input-preview-item:hover{box-shadow:0 4px 6px -1px #0000001a}.cide-file-input-preview-image{width:100%;height:calc(100% - 2rem);object-fit:cover;object-position:center;background-color:#f3f4f6}.cide-file-input-preview-filename{padding:.375rem .5rem;background-color:#f9fafbf2;border-top:1px solid #e5e7eb;font-size:.75rem;color:#374151;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-height:2rem;display:flex;align-items:center;justify-content:center}.cide-file-input-preview-remove{position:absolute;top:.25rem;right:.25rem;width:1.5rem;height:1.5rem;background-color:#ef4444e6;color:#fff;border:none;border-radius:50%;font-size:1rem;font-weight:700;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;z-index:10;transition:all .2s ease-in-out}.cide-file-input-preview-remove:hover{background-color:#dc2626f2;transform:scale(1.1)}.cide-file-input-preview-remove:focus{outline:2px solid #3b82f6;outline-offset:2px}.cide-file-input-hidden{display:none}.cide-file-input-preview-box-container{display:flex;flex-direction:column;gap:.5rem}.cide-file-input-preview-box{position:relative;border:2px dashed #d1d5db;border-radius:.5rem;cursor:pointer;background-color:#f9fafb;display:flex;align-items:center;justify-content:center;overflow:hidden;transition:all .2s ease-in-out}.cide-file-input-preview-box:hover{border-color:#3b82f6;background-color:#eff6ff}.cide-file-input-preview-box.cide-file-input-preview-box-disabled{cursor:not-allowed;opacity:.6;background-color:#f3f4f6}.cide-file-input-preview-box.cide-file-input-preview-box-disabled:hover{border-color:#d1d5db;background-color:#f3f4f6}.cide-file-input-preview-box.cide-file-input-preview-box-has-image{border-style:solid;border-color:#e5e7eb;padding:0}.cide-file-input-preview-box.cide-file-input-preview-box-has-image:hover{border-color:#3b82f6}.cide-file-input-preview-box.cide-file-input-preview-box-has-image:hover .cide-file-input-preview-box-overlay{opacity:1}.cide-file-input-preview-box.cide-file-input-preview-box-drag-over{border-color:#3b82f6!important;background-color:#eff6ff!important;transform:scale(1.02);box-shadow:0 0 0 4px #3b82f61a}.cide-file-input-preview-box.cide-file-input-preview-box-drag-over .cide-file-input-preview-box-placeholder .cide-file-input-preview-box-icon{color:#3b82f6;transform:scale(1.1)}.cide-file-input-preview-box.cide-file-input-preview-box-drag-over .cide-file-input-preview-box-placeholder .cide-file-input-preview-box-text{color:#3b82f6;font-weight:600}.cide-file-input-preview-box-placeholder{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;padding:1rem;text-align:center}.cide-file-input-preview-box-icon{font-size:2rem;color:#6b7280}.cide-file-input-preview-box-text{font-size:.875rem;color:#6b7280;font-weight:500}.cide-file-input-preview-box-content{position:relative;width:100%;height:100%;display:flex;align-items:center;justify-content:center}.cide-file-input-preview-box-image{width:100%;height:100%;object-fit:cover;object-position:center}.cide-file-input-preview-box-overlay{position:absolute;inset:0;background-color:#0009;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .2s ease-in-out}.cide-file-input-preview-box-overlay-text{color:#fff;font-size:.875rem;font-weight:500;text-align:center}.cide-file-input-preview-box-remove{position:absolute;top:.375rem;right:.375rem;width:1.5rem;height:1.5rem;background-color:#ef4444e6;color:#fff;border:none;border-radius:50%;font-size:1rem;font-weight:700;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;z-index:20;transition:all .2s ease-in-out}.cide-file-input-preview-box-remove:hover{background-color:#dc2626f2;transform:scale(1.1)}.cide-file-input-preview-box-remove:focus{outline:2px solid #3b82f6;outline-offset:2px}.cide-file-input-preview-box-filename{font-size:.75rem;color:#374151;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding:.25rem .5rem;background-color:#f3f4f6;border-radius:.25rem;margin-top:.25rem}@media (max-width: 640px){.cide-file-input-preview-container{justify-content:center}.cide-file-input-preview-item{min-width:120px;max-width:150px}.cide-file-input-preview-box-icon{font-size:1.5rem}.cide-file-input-preview-box-text{font-size:.75rem}}.cide-file-input-upload-status{display:flex;align-items:center;justify-content:space-between;padding:.5rem .75rem;background:#f8fafc;border:1px solid #e2e8f0;border-radius:.375rem;margin-top:.5rem;gap:.75rem}.cide-file-input-upload-status .cide-file-input-upload-count{display:flex;align-items:center;gap:.5rem;color:#64748b;font-size:.875rem;font-weight:500}.cide-file-input-upload-status .cide-file-input-upload-count .upload-count-text{color:#1e293b}.cide-file-input-upload-status .cide-file-input-show-files-btn{display:flex;align-items:center;gap:.25rem;padding:.375rem .5rem;background:#3b82f6;color:#fff;border:none;border-radius:.25rem;font-size:.75rem;font-weight:500;cursor:pointer;transition:all .2s ease;min-width:auto;white-space:nowrap}.cide-file-input-upload-status .cide-file-input-show-files-btn:hover{background:#2563eb;transform:translateY(-1px);box-shadow:0 2px 4px #3b82f633}.cide-file-input-upload-status .cide-file-input-show-files-btn:active{transform:translateY(0);box-shadow:0 1px 2px #3b82f61a}@media (prefers-color-scheme: dark){.cide-file-input-upload-status{background:#1e293b;border-color:#334155}.cide-file-input-upload-status .cide-file-input-upload-count{color:#94a3b8}.cide-file-input-upload-status .cide-file-input-upload-count .upload-count-text{color:#f1f5f9}.cide-file-input-upload-status .cide-file-input-show-files-btn{background:#3b82f6}.cide-file-input-upload-status .cide-file-input-show-files-btn:hover{background:#2563eb}}\n"] }]
|
|
4818
|
+
], template: "<div class=\"cide-file-input\">\n <!-- Label (shown when not in preview box mode or when preview box mode but no label override) -->\n @if (labelSignal() && !isPreviewBoxMode()) {\n <label class=\"cide-file-input-label\" [attr.for]=\"'cide-file-input-' + id()\">\n {{ labelSignal() }}@if (requiredSignal()) {<span class=\"cide-file-input-required\"> *</span>}\n </label>\n }\n \n <!-- Preview Box Mode -->\n @if (isPreviewBoxMode()) {\n <div class=\"cide-file-input-preview-box-container\">\n <!-- Hidden file input -->\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"cide-file-input-hidden\"\n />\n \n <!-- Preview Box -->\n <div \n class=\"cide-file-input-preview-box\"\n [class.cide-file-input-preview-box-disabled]=\"disabledSignal()\"\n [class.cide-file-input-preview-box-has-image]=\"hasImages()\"\n [class.cide-file-input-preview-box-drag-over]=\"isDragOver()\"\n [style.width]=\"previewWidthSignal()\"\n [style.height]=\"previewHeightSignal()\"\n (click)=\"triggerFileSelect()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n [attr.title]=\"disabledSignal() ? 'File selection disabled' : placeholderTextSignal()\">\n \n <!-- No Image State -->\n @if (!hasImages()) {\n <div class=\"cide-file-input-preview-box-placeholder\">\n <div class=\"cide-file-input-preview-box-icon\">\n <cide-ele-icon>{{ isDragOver() ? '\uD83D\uDCC1' : placeholderIconSignal() }}</cide-ele-icon>\n </div>\n <div class=\"cide-file-input-preview-box-text\">\n {{ isDragOver() ? 'Drop files here...' : placeholderTextSignal() }}\n </div>\n </div>\n }\n \n <!-- Image Preview State -->\n @if (hasImages()) {\n <div class=\"cide-file-input-preview-box-content\">\n <img \n [src]=\"previewUrls()[0]\" \n [alt]=\"fileNames()[0] || 'Preview image'\"\n class=\"cide-file-input-preview-box-image\">\n <div class=\"cide-file-input-preview-box-overlay\">\n <div class=\"cide-file-input-preview-box-overlay-text\">Click to change</div>\n </div>\n @if (!disabledSignal()) {\n <button \n type=\"button\" \n class=\"cide-file-input-preview-box-remove\"\n (click)=\"clearFiles(); $event.stopPropagation()\"\n title=\"Remove image\">\n \u00D7\n </button>\n }\n </div>\n }\n </div>\n \n <!-- File name display for preview box mode -->\n @if (hasImages() && fileNames().length && showFileNameSignal()) {\n <div class=\"cide-file-input-preview-box-filename\">\n {{ fileNames()[0] }}\n </div>\n }\n </div>\n }\n\n <!-- Standard Mode -->\n @if (!isPreviewBoxMode()) {\n <div \n class=\"cide-file-input-wrapper\"\n [class.cide-file-input-drag-over]=\"isDragOver()\"\n (dragover)=\"onDragOver($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\n <input\n type=\"file\"\n [attr.id]=\"'cide-file-input-' + id()\"\n [attr.accept]=\"acceptSignal()\"\n [attr.multiple]=\"multipleSignal() ? true : null\"\n [disabled]=\"disabledSignal()\"\n (change)=\"onFileSelected($event)\"\n class=\"cide-file-input-element\"\n />\n @if (hasFiles()) {\n <button type=\"button\" class=\"cide-file-input-clear\" (click)=\"clearFiles()\">\n Clear\n </button>\n }\n </div>\n @if (hasFiles() && !isPreviewBoxMode()) {\n <div class=\"cide-file-input-files\">\n @if (multipleSignal() && fileNames().length > 1) {\n <!-- Show file count for multiple files -->\n <div class=\"cide-file-input-multiple-count\">\n {{ fileNames().length }} files selected\n </div>\n } @else {\n <!-- Show individual file names for single file or when only one file selected -->\n @for (name of fileNames(); track name) {\n <span>{{ name }}</span>\n }\n }\n <!-- Angular 20: Display file size using new computed values -->\n @if (totalFileSize() > 0) {\n <div class=\"cide-file-input-size\">\n Total size: {{ fileSizeInMB() }} MB\n </div>\n }\n </div>\n }\n }\n \n <!-- Image Preview Section (only for standard mode) -->\n @if (isImagePreviewAvailable() && !isPreviewBoxMode()) {\n <div class=\"cide-file-input-preview\">\n <div class=\"cide-file-input-preview-label\">Preview:</div>\n <div class=\"cide-file-input-preview-container\">\n @for (previewUrl of previewUrls(); track previewUrl; let i = $index) {\n <div \n class=\"cide-file-input-preview-item\"\n [style.width]=\"previewWidthSignal()\"\n [style.height]=\"previewHeightSignal()\">\n <button \n type=\"button\" \n class=\"cide-file-input-preview-remove\"\n (click)=\"removePreview(i)\"\n title=\"Remove image\">\n \u00D7\n </button>\n <img \n [src]=\"previewUrl\" \n [alt]=\"fileNames()[i] || 'Preview image'\"\n class=\"cide-file-input-preview-image\"\n loading=\"lazy\">\n <div class=\"cide-file-input-preview-filename\">{{ fileNames()[i] }}</div>\n </div>\n }\n </div>\n </div>\n }\n \n <!-- Upload Status and Show Files Button (only for multiple file inputs) -->\n @if (multiple && showFloatingUploaderSignal() && (getUploadCount() > 0 || hasActiveUploads() || hasEverUploaded())) {\n <div class=\"cide-file-input-upload-status\">\n <div class=\"cide-file-input-upload-count\">\n <cide-ele-icon size=\"sm\">cloud_upload</cide-ele-icon>\n <span class=\"upload-count-text\">\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=\"cide-file-input-show-files-icon\"\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=\"cide-file-input-error\">{{ errorTextSignal() }}</div>\n }\n @if (helperTextSignal() && !errorTextSignal()) {\n <div class=\"cide-file-input-helper\">{{ helperTextSignal() }}</div>\n }\n</div> ", styles: [".cide-file-input{display:flex;flex-direction:column;gap:.5rem}.cide-file-input-label{font-weight:500;margin-bottom:.25rem}.cide-file-input-required{color:#d32f2f;font-weight:700}.cide-file-input-wrapper{display:flex;align-items:center;gap:.5rem;border:2px dashed transparent;border-radius:.5rem;padding:.5rem;transition:all .2s ease-in-out}.cide-file-input-wrapper.cide-file-input-drag-over{border-color:#3b82f6;background-color:#eff6ff;transform:scale(1.02)}.cide-file-input-element{flex:1}.cide-file-input-clear{background:none;border:none;color:#d32f2f;cursor:pointer;font-size:.9rem}.cide-file-input-files{font-size:.95rem;color:#333;margin-top:.25rem}.cide-file-input-multiple-count{display:inline-flex;align-items:center;gap:.5rem;padding:.5rem 1rem;background-color:#f0f9ff;border:1px solid #0ea5e9;border-radius:.5rem;color:#0369a1;font-weight:500;font-size:.9rem}.cide-file-input-size{margin-top:.5rem;padding:.25rem .5rem;background-color:#f3f4f6;border-radius:.25rem;font-size:.75rem;color:#4b5563;font-weight:500}.cide-file-input-error{color:#d32f2f;font-size:.9rem}.cide-file-input-helper{color:#666;font-size:.9rem}.cide-file-input-preview{margin-top:.75rem;padding:.75rem;background-color:#f8f9fa;border:1px solid #e1e5e9;border-radius:.375rem}.cide-file-input-preview-label{font-weight:500;margin-bottom:.5rem;color:#374151;font-size:.875rem}.cide-file-input-preview-container{display:flex;flex-wrap:wrap;gap:.75rem}.cide-file-input-preview-item{position:relative;display:flex;flex-direction:column;border:1px solid #d1d5db;border-radius:.5rem;overflow:hidden;background-color:#fff;box-shadow:0 1px 3px #0000001a;transition:box-shadow .2s ease-in-out}.cide-file-input-preview-item:hover{box-shadow:0 4px 6px -1px #0000001a}.cide-file-input-preview-image{width:100%;height:calc(100% - 2rem);object-fit:cover;object-position:center;background-color:#f3f4f6}.cide-file-input-preview-filename{padding:.375rem .5rem;background-color:#f9fafbf2;border-top:1px solid #e5e7eb;font-size:.75rem;color:#374151;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-height:2rem;display:flex;align-items:center;justify-content:center}.cide-file-input-preview-remove{position:absolute;top:.25rem;right:.25rem;width:1.5rem;height:1.5rem;background-color:#ef4444e6;color:#fff;border:none;border-radius:50%;font-size:1rem;font-weight:700;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;z-index:10;transition:all .2s ease-in-out}.cide-file-input-preview-remove:hover{background-color:#dc2626f2;transform:scale(1.1)}.cide-file-input-preview-remove:focus{outline:2px solid #3b82f6;outline-offset:2px}.cide-file-input-hidden{display:none}.cide-file-input-preview-box-container{display:flex;flex-direction:column;gap:.5rem}.cide-file-input-preview-box{position:relative;border:2px dashed #d1d5db;border-radius:.5rem;cursor:pointer;background-color:#f9fafb;display:flex;align-items:center;justify-content:center;overflow:hidden;transition:all .2s ease-in-out}.cide-file-input-preview-box:hover{border-color:#3b82f6;background-color:#eff6ff}.cide-file-input-preview-box.cide-file-input-preview-box-disabled{cursor:not-allowed;opacity:.6;background-color:#f3f4f6}.cide-file-input-preview-box.cide-file-input-preview-box-disabled:hover{border-color:#d1d5db;background-color:#f3f4f6}.cide-file-input-preview-box.cide-file-input-preview-box-has-image{border-style:solid;border-color:#e5e7eb;padding:0}.cide-file-input-preview-box.cide-file-input-preview-box-has-image:hover{border-color:#3b82f6}.cide-file-input-preview-box.cide-file-input-preview-box-has-image:hover .cide-file-input-preview-box-overlay{opacity:1}.cide-file-input-preview-box.cide-file-input-preview-box-drag-over{border-color:#3b82f6!important;background-color:#eff6ff!important;transform:scale(1.02);box-shadow:0 0 0 4px #3b82f61a}.cide-file-input-preview-box.cide-file-input-preview-box-drag-over .cide-file-input-preview-box-placeholder .cide-file-input-preview-box-icon{color:#3b82f6;transform:scale(1.1)}.cide-file-input-preview-box.cide-file-input-preview-box-drag-over .cide-file-input-preview-box-placeholder .cide-file-input-preview-box-text{color:#3b82f6;font-weight:600}.cide-file-input-preview-box-placeholder{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;padding:1rem;text-align:center}.cide-file-input-preview-box-icon{font-size:2rem;color:#6b7280}.cide-file-input-preview-box-text{font-size:.875rem;color:#6b7280;font-weight:500}.cide-file-input-preview-box-content{position:relative;width:100%;height:100%;display:flex;align-items:center;justify-content:center}.cide-file-input-preview-box-image{width:100%;height:100%;object-fit:cover;object-position:center}.cide-file-input-preview-box-overlay{position:absolute;inset:0;background-color:#0009;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .2s ease-in-out}.cide-file-input-preview-box-overlay-text{color:#fff;font-size:.875rem;font-weight:500;text-align:center}.cide-file-input-preview-box-remove{position:absolute;top:.375rem;right:.375rem;width:1.5rem;height:1.5rem;background-color:#ef4444e6;color:#fff;border:none;border-radius:50%;font-size:1rem;font-weight:700;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;z-index:20;transition:all .2s ease-in-out}.cide-file-input-preview-box-remove:hover{background-color:#dc2626f2;transform:scale(1.1)}.cide-file-input-preview-box-remove:focus{outline:2px solid #3b82f6;outline-offset:2px}.cide-file-input-preview-box-filename{font-size:.75rem;color:#374151;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding:.25rem .5rem;background-color:#f3f4f6;border-radius:.25rem;margin-top:.25rem}@media (max-width: 640px){.cide-file-input-preview-container{justify-content:center}.cide-file-input-preview-item{min-width:120px;max-width:150px}.cide-file-input-preview-box-icon{font-size:1.5rem}.cide-file-input-preview-box-text{font-size:.75rem}}.cide-file-input-upload-status{display:flex;align-items:center;justify-content:space-between;padding:.5rem .75rem;background:#f8fafc;border:1px solid #e2e8f0;border-radius:.375rem;margin-top:.5rem;gap:.75rem}.cide-file-input-upload-status .cide-file-input-upload-count{display:flex;align-items:center;gap:.5rem;color:#64748b;font-size:.875rem;font-weight:500}.cide-file-input-upload-status .cide-file-input-upload-count .upload-count-text{color:#1e293b}.cide-file-input-upload-status .cide-file-input-show-files-icon{display:flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;background:transparent;color:#6b7280;border:none;border-radius:.25rem;cursor:pointer;transition:all .2s ease}.cide-file-input-upload-status .cide-file-input-show-files-icon:hover{background:#f3f4f6;color:#3b82f6}.cide-file-input-upload-status .cide-file-input-show-files-icon:active{background:#e5e7eb}@media (prefers-color-scheme: dark){.cide-file-input-upload-status{background:#1e293b;border-color:#334155}.cide-file-input-upload-status .cide-file-input-upload-count{color:#94a3b8}.cide-file-input-upload-status .cide-file-input-upload-count .upload-count-text{color:#f1f5f9}.cide-file-input-upload-status .cide-file-input-show-files-icon{color:#94a3b8}.cide-file-input-upload-status .cide-file-input-show-files-icon:hover{background:#374151;color:#60a5fa}.cide-file-input-upload-status .cide-file-input-show-files-icon:active{background:#4b5563}}\n"] }]
|
|
4610
4819
|
}], ctorParameters: () => [], propDecorators: { label: [{
|
|
4611
4820
|
type: Input
|
|
4612
4821
|
}], accept: [{
|
|
@@ -4653,488 +4862,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
4653
4862
|
type: Output
|
|
4654
4863
|
}] } });
|
|
4655
4864
|
|
|
4656
|
-
class CideEleGlobalFileUploaderComponent {
|
|
4657
|
-
// Dependency injection
|
|
4658
|
-
destroyRef = inject(DestroyRef);
|
|
4659
|
-
fileService = inject(CideEleFileManagerService);
|
|
4660
|
-
// Input properties
|
|
4661
|
-
userId = '';
|
|
4662
|
-
multiple = true;
|
|
4663
|
-
accept = '';
|
|
4664
|
-
maxFileSize = 10; // MB
|
|
4665
|
-
allowedTypes = [];
|
|
4666
|
-
// Output events
|
|
4667
|
-
uploadComplete = new EventEmitter();
|
|
4668
|
-
uploadError = new EventEmitter();
|
|
4669
|
-
uploadCancelled = new EventEmitter();
|
|
4670
|
-
allUploadsComplete = new EventEmitter();
|
|
4671
|
-
// Signals for reactive state management
|
|
4672
|
-
isDragOver = signal(false, ...(ngDevMode ? [{ debugName: "isDragOver" }] : []));
|
|
4673
|
-
isUploading = signal(false, ...(ngDevMode ? [{ debugName: "isUploading" }] : []));
|
|
4674
|
-
uploadQueue = signal([], ...(ngDevMode ? [{ debugName: "uploadQueue" }] : []));
|
|
4675
|
-
currentGroupId = signal(null, ...(ngDevMode ? [{ debugName: "currentGroupId" }] : []));
|
|
4676
|
-
// Computed values
|
|
4677
|
-
hasUploads = computed(() => this.uploadQueue().length > 0, ...(ngDevMode ? [{ debugName: "hasUploads" }] : []));
|
|
4678
|
-
pendingUploads = computed(() => this.uploadQueue().filter(upload => upload.status === 'pending'), ...(ngDevMode ? [{ debugName: "pendingUploads" }] : []));
|
|
4679
|
-
activeUploads = computed(() => this.uploadQueue().filter(upload => upload.status === 'uploading'), ...(ngDevMode ? [{ debugName: "activeUploads" }] : []));
|
|
4680
|
-
completedUploads = computed(() => this.uploadQueue().filter(upload => upload.status === 'completed'), ...(ngDevMode ? [{ debugName: "completedUploads" }] : []));
|
|
4681
|
-
failedUploads = computed(() => this.uploadQueue().filter(upload => upload.status === 'error'), ...(ngDevMode ? [{ debugName: "failedUploads" }] : []));
|
|
4682
|
-
constructor() {
|
|
4683
|
-
console.log('🚀 [GlobalFileUploader] Component initialized');
|
|
4684
|
-
}
|
|
4685
|
-
/**
|
|
4686
|
-
* Handle drag over event
|
|
4687
|
-
*/
|
|
4688
|
-
onDragOver(event) {
|
|
4689
|
-
event.preventDefault();
|
|
4690
|
-
event.stopPropagation();
|
|
4691
|
-
this.isDragOver.set(true);
|
|
4692
|
-
}
|
|
4693
|
-
/**
|
|
4694
|
-
* Handle drag leave event
|
|
4695
|
-
*/
|
|
4696
|
-
onDragLeave(event) {
|
|
4697
|
-
event.preventDefault();
|
|
4698
|
-
event.stopPropagation();
|
|
4699
|
-
this.isDragOver.set(false);
|
|
4700
|
-
}
|
|
4701
|
-
/**
|
|
4702
|
-
* Handle drop event
|
|
4703
|
-
*/
|
|
4704
|
-
onDrop(event) {
|
|
4705
|
-
event.preventDefault();
|
|
4706
|
-
event.stopPropagation();
|
|
4707
|
-
this.isDragOver.set(false);
|
|
4708
|
-
const files = event.dataTransfer?.files;
|
|
4709
|
-
if (files && files.length > 0) {
|
|
4710
|
-
this.addFilesToQueue(Array.from(files));
|
|
4711
|
-
}
|
|
4712
|
-
}
|
|
4713
|
-
/**
|
|
4714
|
-
* Handle file input change
|
|
4715
|
-
*/
|
|
4716
|
-
onFileInputChange(event) {
|
|
4717
|
-
const target = event.target;
|
|
4718
|
-
const files = target.files;
|
|
4719
|
-
if (files && files.length > 0) {
|
|
4720
|
-
this.addFilesToQueue(Array.from(files));
|
|
4721
|
-
}
|
|
4722
|
-
// Reset input value to allow selecting the same file again
|
|
4723
|
-
target.value = '';
|
|
4724
|
-
}
|
|
4725
|
-
/**
|
|
4726
|
-
* Add files to upload queue
|
|
4727
|
-
*/
|
|
4728
|
-
addFilesToQueue(files) {
|
|
4729
|
-
// Validate files
|
|
4730
|
-
const validFiles = files.filter(file => this.validateFile(file));
|
|
4731
|
-
if (validFiles.length === 0) {
|
|
4732
|
-
console.warn('⚠️ [GlobalFileUploader] No valid files to upload');
|
|
4733
|
-
return;
|
|
4734
|
-
}
|
|
4735
|
-
const newUploads = validFiles.map(file => ({
|
|
4736
|
-
fileId: this.generateFileId(file),
|
|
4737
|
-
fileName: file.name,
|
|
4738
|
-
progress: 0,
|
|
4739
|
-
status: 'pending',
|
|
4740
|
-
file: file
|
|
4741
|
-
}));
|
|
4742
|
-
this.uploadQueue.update(queue => [...queue, ...newUploads]);
|
|
4743
|
-
this.processUploadQueue();
|
|
4744
|
-
}
|
|
4745
|
-
/**
|
|
4746
|
-
* Validate file
|
|
4747
|
-
*/
|
|
4748
|
-
validateFile(file) {
|
|
4749
|
-
// Check file size
|
|
4750
|
-
const maxSizeBytes = this.maxFileSize * 1024 * 1024;
|
|
4751
|
-
if (file.size > maxSizeBytes) {
|
|
4752
|
-
console.warn(`⚠️ [GlobalFileUploader] File ${file.name} exceeds size limit`);
|
|
4753
|
-
return false;
|
|
4754
|
-
}
|
|
4755
|
-
// Check file type
|
|
4756
|
-
if (this.allowedTypes.length > 0 && !this.allowedTypes.includes(file.type)) {
|
|
4757
|
-
console.warn(`⚠️ [GlobalFileUploader] File type ${file.type} not allowed`);
|
|
4758
|
-
return false;
|
|
4759
|
-
}
|
|
4760
|
-
return true;
|
|
4761
|
-
}
|
|
4762
|
-
/**
|
|
4763
|
-
* Process upload queue
|
|
4764
|
-
*/
|
|
4765
|
-
processUploadQueue() {
|
|
4766
|
-
const pendingUploads = this.uploadQueue().filter(upload => upload.status === 'pending');
|
|
4767
|
-
if (pendingUploads.length > 0 && !this.isUploading()) {
|
|
4768
|
-
this.isUploading.set(true);
|
|
4769
|
-
if (this.multiple && pendingUploads.length > 1) {
|
|
4770
|
-
// Multiple file upload with group ID
|
|
4771
|
-
this.uploadMultipleFiles(pendingUploads);
|
|
4772
|
-
}
|
|
4773
|
-
else {
|
|
4774
|
-
// Single file upload
|
|
4775
|
-
this.uploadNextFile();
|
|
4776
|
-
}
|
|
4777
|
-
}
|
|
4778
|
-
}
|
|
4779
|
-
/**
|
|
4780
|
-
* Upload multiple files with group ID
|
|
4781
|
-
*/
|
|
4782
|
-
uploadMultipleFiles(uploads) {
|
|
4783
|
-
console.log('📤 [GlobalFileUploader] Starting multiple file upload for:', uploads.length, 'files');
|
|
4784
|
-
// Generate group ID first
|
|
4785
|
-
this.fileService.generateObjectId()
|
|
4786
|
-
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
4787
|
-
.subscribe({
|
|
4788
|
-
next: (response) => {
|
|
4789
|
-
const groupId = response.data?.objectId;
|
|
4790
|
-
console.log('🆔 [GlobalFileUploader] Generated group ID:', groupId);
|
|
4791
|
-
this.currentGroupId.set(groupId || null);
|
|
4792
|
-
if (groupId) {
|
|
4793
|
-
// Upload all files with the same group ID
|
|
4794
|
-
this.uploadFilesWithGroupId(uploads, groupId);
|
|
4795
|
-
}
|
|
4796
|
-
else {
|
|
4797
|
-
console.error('❌ [GlobalFileUploader] No group ID received');
|
|
4798
|
-
this.handleUploadError('Failed to generate group ID', uploads[0]?.fileId);
|
|
4799
|
-
}
|
|
4800
|
-
},
|
|
4801
|
-
error: (error) => {
|
|
4802
|
-
console.error('❌ [GlobalFileUploader] Failed to generate group ID:', error);
|
|
4803
|
-
this.handleUploadError('Failed to generate group ID', uploads[0]?.fileId);
|
|
4804
|
-
}
|
|
4805
|
-
});
|
|
4806
|
-
}
|
|
4807
|
-
/**
|
|
4808
|
-
* Upload files with group ID
|
|
4809
|
-
*/
|
|
4810
|
-
uploadFilesWithGroupId(uploads, groupId) {
|
|
4811
|
-
let completedCount = 0;
|
|
4812
|
-
let failedCount = 0;
|
|
4813
|
-
const totalFiles = uploads.length;
|
|
4814
|
-
const uploadedFiles = [];
|
|
4815
|
-
uploads.forEach((upload, index) => {
|
|
4816
|
-
// Update status to uploading
|
|
4817
|
-
this.updateUploadStatus(upload.fileId, 'uploading', 0);
|
|
4818
|
-
const uploadRequest = {
|
|
4819
|
-
file: upload.file,
|
|
4820
|
-
userId: this.userId,
|
|
4821
|
-
groupId: groupId,
|
|
4822
|
-
tags: [],
|
|
4823
|
-
permissions: ['read', 'write']
|
|
4824
|
-
};
|
|
4825
|
-
this.fileService.uploadFile(upload.file, this.convertToFileUploadOptions(uploadRequest), (progress) => {
|
|
4826
|
-
// Use the original fileId for progress updates during upload
|
|
4827
|
-
this.updateUploadProgress(upload.fileId, progress);
|
|
4828
|
-
})
|
|
4829
|
-
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
4830
|
-
.subscribe({
|
|
4831
|
-
next: (response) => {
|
|
4832
|
-
completedCount++;
|
|
4833
|
-
console.log(`✅ [GlobalFileUploader] File ${index + 1}/${totalFiles} uploaded successfully`);
|
|
4834
|
-
// Find the upload item by cyfm_temp_unique_id from response
|
|
4835
|
-
const responseFile = response?.data?.core_file_manager?.[0];
|
|
4836
|
-
const tempUniqueId = responseFile?.cyfm_temp_unique_id;
|
|
4837
|
-
console.log('🔍 [GlobalFileUploader] Response tempUniqueId:', tempUniqueId);
|
|
4838
|
-
console.log('🔍 [GlobalFileUploader] Original upload fileId:', upload.fileId);
|
|
4839
|
-
// Use the tempUniqueId to find and update the correct upload item
|
|
4840
|
-
if (tempUniqueId) {
|
|
4841
|
-
this.updateUploadStatusByTempId(tempUniqueId, 'completed', 100, undefined, response);
|
|
4842
|
-
}
|
|
4843
|
-
else {
|
|
4844
|
-
// Fallback to original fileId
|
|
4845
|
-
this.updateUploadStatus(upload.fileId, 'completed', 100, undefined, response);
|
|
4846
|
-
}
|
|
4847
|
-
this.uploadComplete.emit({
|
|
4848
|
-
fileId: tempUniqueId || upload.fileId,
|
|
4849
|
-
fileName: upload.fileName,
|
|
4850
|
-
progress: 100,
|
|
4851
|
-
status: 'completed',
|
|
4852
|
-
uploadedFile: response
|
|
4853
|
-
});
|
|
4854
|
-
if (responseFile) {
|
|
4855
|
-
uploadedFiles.push(responseFile);
|
|
4856
|
-
}
|
|
4857
|
-
console.log('🔍 [GlobalFileUploader] Upload response received:', response);
|
|
4858
|
-
console.log('🔍 [GlobalFileUploader] Response data structure:', response?.data);
|
|
4859
|
-
// Check if all files are completed
|
|
4860
|
-
if (completedCount + failedCount === totalFiles) {
|
|
4861
|
-
this.handleMultipleUploadComplete(completedCount, failedCount, groupId, uploadedFiles);
|
|
4862
|
-
}
|
|
4863
|
-
},
|
|
4864
|
-
error: (error) => {
|
|
4865
|
-
failedCount++;
|
|
4866
|
-
console.error(`❌ [GlobalFileUploader] File ${index + 1}/${totalFiles} upload failed:`, error);
|
|
4867
|
-
this.updateUploadStatus(upload.fileId, 'error', 0, error.message || 'Upload failed');
|
|
4868
|
-
this.uploadError.emit({
|
|
4869
|
-
fileId: upload.fileId,
|
|
4870
|
-
error: error.message || 'Upload failed'
|
|
4871
|
-
});
|
|
4872
|
-
// Check if all files are completed
|
|
4873
|
-
if (completedCount + failedCount === totalFiles) {
|
|
4874
|
-
this.handleMultipleUploadComplete(completedCount, failedCount, groupId, uploadedFiles);
|
|
4875
|
-
}
|
|
4876
|
-
}
|
|
4877
|
-
});
|
|
4878
|
-
});
|
|
4879
|
-
}
|
|
4880
|
-
/**
|
|
4881
|
-
* Handle multiple upload completion
|
|
4882
|
-
*/
|
|
4883
|
-
handleMultipleUploadComplete(completed, failed, groupId, uploadedFiles) {
|
|
4884
|
-
console.log(`📊 [GlobalFileUploader] Multiple upload complete: ${completed}/${completed + failed} successful`);
|
|
4885
|
-
this.isUploading.set(false);
|
|
4886
|
-
if (completed > 0) {
|
|
4887
|
-
this.allUploadsComplete.emit({
|
|
4888
|
-
groupId: groupId,
|
|
4889
|
-
uploadedFiles: uploadedFiles
|
|
4890
|
-
});
|
|
4891
|
-
}
|
|
4892
|
-
}
|
|
4893
|
-
/**
|
|
4894
|
-
* Upload next file in queue (single file mode)
|
|
4895
|
-
*/
|
|
4896
|
-
uploadNextFile() {
|
|
4897
|
-
const pendingUpload = this.uploadQueue().find(upload => upload.status === 'pending');
|
|
4898
|
-
if (!pendingUpload) {
|
|
4899
|
-
this.isUploading.set(false);
|
|
4900
|
-
return;
|
|
4901
|
-
}
|
|
4902
|
-
// Update status to uploading
|
|
4903
|
-
this.updateUploadStatus(pendingUpload.fileId, 'uploading', 0);
|
|
4904
|
-
const uploadRequest = {
|
|
4905
|
-
file: pendingUpload.file,
|
|
4906
|
-
userId: this.userId,
|
|
4907
|
-
tags: [],
|
|
4908
|
-
permissions: ['read', 'write']
|
|
4909
|
-
};
|
|
4910
|
-
this.fileService.uploadFile(pendingUpload.file, this.convertToFileUploadOptions(uploadRequest), (progress) => {
|
|
4911
|
-
this.updateUploadProgress(pendingUpload.fileId, progress);
|
|
4912
|
-
})
|
|
4913
|
-
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
4914
|
-
.subscribe({
|
|
4915
|
-
next: (response) => {
|
|
4916
|
-
console.log('✅ [GlobalFileUploader] Single file upload successful:', response);
|
|
4917
|
-
// Find the upload item by cyfm_temp_unique_id from response
|
|
4918
|
-
const responseFile = response?.data?.core_file_manager?.[0];
|
|
4919
|
-
const tempUniqueId = responseFile?.cyfm_temp_unique_id;
|
|
4920
|
-
console.log('🔍 [GlobalFileUploader] Single upload - Response tempUniqueId:', tempUniqueId);
|
|
4921
|
-
console.log('🔍 [GlobalFileUploader] Single upload - Original upload fileId:', pendingUpload.fileId);
|
|
4922
|
-
// Use the tempUniqueId to find and update the correct upload item
|
|
4923
|
-
if (tempUniqueId) {
|
|
4924
|
-
this.updateUploadStatusByTempId(tempUniqueId, 'completed', 100, undefined, response);
|
|
4925
|
-
}
|
|
4926
|
-
else {
|
|
4927
|
-
// Fallback to original fileId
|
|
4928
|
-
this.updateUploadStatus(pendingUpload.fileId, 'completed', 100, undefined, response);
|
|
4929
|
-
}
|
|
4930
|
-
this.uploadComplete.emit({
|
|
4931
|
-
fileId: tempUniqueId || pendingUpload.fileId,
|
|
4932
|
-
fileName: pendingUpload.fileName,
|
|
4933
|
-
progress: 100,
|
|
4934
|
-
status: 'completed',
|
|
4935
|
-
uploadedFile: response
|
|
4936
|
-
});
|
|
4937
|
-
// Process next file
|
|
4938
|
-
setTimeout(() => this.uploadNextFile(), 500);
|
|
4939
|
-
},
|
|
4940
|
-
error: (error) => {
|
|
4941
|
-
console.error('❌ [GlobalFileUploader] Upload error:', error);
|
|
4942
|
-
this.updateUploadStatus(pendingUpload.fileId, 'error', 0, error.message || 'Upload failed');
|
|
4943
|
-
this.uploadError.emit({
|
|
4944
|
-
fileId: pendingUpload.fileId,
|
|
4945
|
-
error: error.message || 'Upload failed'
|
|
4946
|
-
});
|
|
4947
|
-
// Process next file
|
|
4948
|
-
setTimeout(() => this.uploadNextFile(), 500);
|
|
4949
|
-
}
|
|
4950
|
-
});
|
|
4951
|
-
}
|
|
4952
|
-
/**
|
|
4953
|
-
* Update upload progress
|
|
4954
|
-
*/
|
|
4955
|
-
updateUploadProgress(fileId, progress) {
|
|
4956
|
-
this.uploadQueue.update(queue => queue.map(upload => upload.fileId === fileId
|
|
4957
|
-
? { ...upload, progress }
|
|
4958
|
-
: upload));
|
|
4959
|
-
}
|
|
4960
|
-
/**
|
|
4961
|
-
* Update upload status
|
|
4962
|
-
*/
|
|
4963
|
-
updateUploadStatus(fileId, status, progress, error, uploadedFile) {
|
|
4964
|
-
console.log('🔄 [GlobalFileUploader] Updating upload status:', { fileId, status, progress, error: !!error, uploadedFile: !!uploadedFile });
|
|
4965
|
-
this.uploadQueue.update(queue => {
|
|
4966
|
-
const updatedQueue = queue.map(upload => {
|
|
4967
|
-
if (upload.fileId === fileId) {
|
|
4968
|
-
console.log('🔄 [GlobalFileUploader] Found upload to update:', upload.fileName, 'from', upload.status, 'to', status);
|
|
4969
|
-
return { ...upload, status, progress, error, uploadedFile };
|
|
4970
|
-
}
|
|
4971
|
-
return upload;
|
|
4972
|
-
});
|
|
4973
|
-
console.log('🔄 [GlobalFileUploader] Updated queue:', updatedQueue.map(u => ({ fileName: u.fileName, status: u.status })));
|
|
4974
|
-
return updatedQueue;
|
|
4975
|
-
});
|
|
4976
|
-
}
|
|
4977
|
-
/**
|
|
4978
|
-
* Update upload status by cyfm_temp_unique_id
|
|
4979
|
-
*/
|
|
4980
|
-
updateUploadStatusByTempId(tempUniqueId, status, progress, error, uploadedFile) {
|
|
4981
|
-
console.log('🔄 [GlobalFileUploader] Updating upload status by tempId:', { tempUniqueId, status, progress, error: !!error, uploadedFile: !!uploadedFile });
|
|
4982
|
-
this.uploadQueue.update(queue => {
|
|
4983
|
-
const updatedQueue = queue.map(upload => {
|
|
4984
|
-
// Check if this upload's fileId matches the tempUniqueId
|
|
4985
|
-
if (upload.fileId === tempUniqueId) {
|
|
4986
|
-
console.log('🔄 [GlobalFileUploader] Found upload by tempId to update:', upload.fileName, 'from', upload.status, 'to', status);
|
|
4987
|
-
return { ...upload, status, progress, error, uploadedFile };
|
|
4988
|
-
}
|
|
4989
|
-
return upload;
|
|
4990
|
-
});
|
|
4991
|
-
console.log('🔄 [GlobalFileUploader] Updated queue by tempId:', updatedQueue.map(u => ({ fileName: u.fileName, status: u.status, fileId: u.fileId })));
|
|
4992
|
-
return updatedQueue;
|
|
4993
|
-
});
|
|
4994
|
-
}
|
|
4995
|
-
/**
|
|
4996
|
-
* Handle upload error
|
|
4997
|
-
*/
|
|
4998
|
-
handleUploadError(error, fileId) {
|
|
4999
|
-
if (fileId) {
|
|
5000
|
-
this.updateUploadStatus(fileId, 'error', 0, error);
|
|
5001
|
-
this.uploadError.emit({ fileId, error });
|
|
5002
|
-
}
|
|
5003
|
-
this.isUploading.set(false);
|
|
5004
|
-
}
|
|
5005
|
-
/**
|
|
5006
|
-
* Cancel upload
|
|
5007
|
-
*/
|
|
5008
|
-
cancelUpload(fileId) {
|
|
5009
|
-
this.updateUploadStatus(fileId, 'cancelled', 0);
|
|
5010
|
-
this.uploadCancelled.emit(fileId);
|
|
5011
|
-
// Remove from queue after a delay
|
|
5012
|
-
setTimeout(() => {
|
|
5013
|
-
this.uploadQueue.update(queue => queue.filter(upload => upload.fileId !== fileId));
|
|
5014
|
-
}, 1000);
|
|
5015
|
-
}
|
|
5016
|
-
/**
|
|
5017
|
-
* Retry upload
|
|
5018
|
-
*/
|
|
5019
|
-
retryUpload(fileId) {
|
|
5020
|
-
this.updateUploadStatus(fileId, 'pending', 0);
|
|
5021
|
-
this.processUploadQueue();
|
|
5022
|
-
}
|
|
5023
|
-
/**
|
|
5024
|
-
* Remove upload from queue
|
|
5025
|
-
*/
|
|
5026
|
-
removeUpload(fileId) {
|
|
5027
|
-
this.uploadQueue.update(queue => queue.filter(upload => upload.fileId !== fileId));
|
|
5028
|
-
}
|
|
5029
|
-
/**
|
|
5030
|
-
* Clear completed uploads
|
|
5031
|
-
*/
|
|
5032
|
-
clearCompleted() {
|
|
5033
|
-
this.uploadQueue.update(queue => queue.filter(upload => upload.status !== 'completed'));
|
|
5034
|
-
}
|
|
5035
|
-
/**
|
|
5036
|
-
* Clear all uploads
|
|
5037
|
-
*/
|
|
5038
|
-
clearAll() {
|
|
5039
|
-
this.uploadQueue.set([]);
|
|
5040
|
-
this.currentGroupId.set(null);
|
|
5041
|
-
}
|
|
5042
|
-
/**
|
|
5043
|
-
* Get status icon
|
|
5044
|
-
*/
|
|
5045
|
-
getStatusIcon(status) {
|
|
5046
|
-
switch (status) {
|
|
5047
|
-
case 'pending': return 'schedule';
|
|
5048
|
-
case 'uploading': return 'cloud_upload';
|
|
5049
|
-
case 'completed': return 'check_circle';
|
|
5050
|
-
case 'error': return 'error';
|
|
5051
|
-
case 'cancelled': return 'cancel';
|
|
5052
|
-
default: return 'help';
|
|
5053
|
-
}
|
|
5054
|
-
}
|
|
5055
|
-
/**
|
|
5056
|
-
* Get status class
|
|
5057
|
-
*/
|
|
5058
|
-
getStatusClass(status) {
|
|
5059
|
-
switch (status) {
|
|
5060
|
-
case 'pending': return 'status-pending';
|
|
5061
|
-
case 'uploading': return 'status-uploading';
|
|
5062
|
-
case 'completed': return 'status-completed';
|
|
5063
|
-
case 'error': return 'status-error';
|
|
5064
|
-
case 'cancelled': return 'status-cancelled';
|
|
5065
|
-
default: return 'status-unknown';
|
|
5066
|
-
}
|
|
5067
|
-
}
|
|
5068
|
-
/**
|
|
5069
|
-
* Get file size display
|
|
5070
|
-
*/
|
|
5071
|
-
getFileSizeDisplay(file) {
|
|
5072
|
-
const bytes = file.size;
|
|
5073
|
-
if (bytes === 0)
|
|
5074
|
-
return '0 Bytes';
|
|
5075
|
-
const k = 1024;
|
|
5076
|
-
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
5077
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
5078
|
-
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
5079
|
-
}
|
|
5080
|
-
/**
|
|
5081
|
-
* Generate unique file ID - this should match the service's generateFileId method
|
|
5082
|
-
* The service uses: ${file.name}_${file.size}_${Date.now()}
|
|
5083
|
-
*/
|
|
5084
|
-
generateFileId(file) {
|
|
5085
|
-
return `${file.name}_${file.size}_${Date.now()}`;
|
|
5086
|
-
}
|
|
5087
|
-
/**
|
|
5088
|
-
* Convert IFileUploadRequest to FileUploadOptions
|
|
5089
|
-
*/
|
|
5090
|
-
convertToFileUploadOptions(request) {
|
|
5091
|
-
return {
|
|
5092
|
-
altText: request.altText,
|
|
5093
|
-
userId: request.userId,
|
|
5094
|
-
permissions: request.permissions,
|
|
5095
|
-
tags: request.tags,
|
|
5096
|
-
groupId: request.groupId
|
|
5097
|
-
};
|
|
5098
|
-
}
|
|
5099
|
-
/**
|
|
5100
|
-
* Trigger file input click
|
|
5101
|
-
*/
|
|
5102
|
-
triggerFileInput() {
|
|
5103
|
-
const fileInput = document.getElementById('global-file-input');
|
|
5104
|
-
if (fileInput) {
|
|
5105
|
-
fileInput.click();
|
|
5106
|
-
}
|
|
5107
|
-
}
|
|
5108
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleGlobalFileUploaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5109
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.7", type: CideEleGlobalFileUploaderComponent, isStandalone: true, selector: "cide-ele-global-file-uploader", inputs: { userId: "userId", multiple: "multiple", accept: "accept", maxFileSize: "maxFileSize", allowedTypes: "allowedTypes" }, outputs: { uploadComplete: "uploadComplete", uploadError: "uploadError", uploadCancelled: "uploadCancelled", allUploadsComplete: "allUploadsComplete" }, ngImport: i0, template: "<!-- Global File Uploader Component -->\r\n<div class=\"global-file-uploader\">\r\n\r\n <!-- Hidden file input -->\r\n <input id=\"global-file-input\" type=\"file\" [multiple]=\"multiple\" [accept]=\"accept\" \r\n (change)=\"onFileInputChange($event)\" style=\"display: none;\">\r\n\r\n <!-- Drag and Drop Zone -->\r\n <div class=\"upload-zone\" [class.drag-over]=\"isDragOver()\" (dragover)=\"onDragOver($event)\"\r\n (dragleave)=\"onDragLeave($event)\" (drop)=\"onDrop($event)\" (click)=\"triggerFileInput()\">\r\n\r\n <div class=\"upload-zone-content\">\r\n <cide-ele-icon name=\"cloud_upload\" class=\"upload-icon\" size=\"lg\">\r\n </cide-ele-icon>\r\n\r\n <h3 class=\"upload-title\">\r\n {{ isDragOver() ? 'Drop files here' : 'Upload Files' }}\r\n </h3>\r\n\r\n <p class=\"upload-subtitle\">\r\n {{ isDragOver() ? 'Release to upload' : 'Drag and drop files here or click to browse' }}\r\n </p>\r\n\r\n <button cideEleButton cideEleButton variant=\"outline\" size=\"sm\"\r\n (click)=\"triggerFileInput(); $event.stopPropagation()\">\r\n <cide-ele-icon name=\"add\" size=\"sm\"></cide-ele-icon>\r\n Choose Files\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Upload Queue -->\r\n @if (hasUploads()) {\r\n <div class=\"upload-queue\">\r\n <div class=\"queue-header\">\r\n <h4 class=\"queue-title\">Upload Queue</h4>\r\n <div class=\"queue-actions\">\r\n @if (completedUploads().length > 0) {\r\n <button cideEleButton variant=\"text\" size=\"sm\" (click)=\"clearCompleted()\">\r\n Clear Completed\r\n </button>\r\n }\r\n <button cideEleButton variant=\"text\" size=\"sm\" (click)=\"clearAll()\">\r\n Clear All\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <div class=\"queue-items\">\r\n @for (upload of uploadQueue(); track upload.fileId) {\r\n <div class=\"upload-item\" [class]=\"getStatusClass(upload.status)\">\r\n\r\n <!-- File Info -->\r\n <div class=\"file-info\">\r\n <cide-ele-icon class=\"status-icon\" size=\"sm\">\r\n {{getStatusIcon(upload.status)}}\r\n </cide-ele-icon>\r\n\r\n <div class=\"file-details\">\r\n <div class=\"file-name\">{{ upload.fileName }}</div>\r\n @if (upload.file) {\r\n <div class=\"file-size\">{{ getFileSizeDisplay(upload.file) }}</div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- Progress Bar -->\r\n @if (upload.status === 'uploading') {\r\n <div class=\"progress-container\">\r\n <div class=\"progress-bar\">\r\n <div class=\"progress-fill\" [style.width.%]=\"upload.progress\">\r\n </div>\r\n </div>\r\n <span class=\"progress-text\">{{ upload.progress }}%</span>\r\n </div>\r\n }\r\n\r\n <!-- Status Text -->\r\n @if (upload.status !== 'uploading') {\r\n <div class=\"status-text\">\r\n @switch (upload.status) {\r\n @case ('pending') {\r\n <span class=\"text-yellow-600\">Waiting...</span>\r\n }\r\n @case ('completed') {\r\n <span class=\"text-green-600\">Uploaded successfully</span>\r\n }\r\n @case ('error') {\r\n <span class=\"text-red-600\">{{ upload.error || 'Upload failed' }}</span>\r\n }\r\n @case ('cancelled') {\r\n <span class=\"text-gray-600\">Cancelled</span>\r\n }\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Actions -->\r\n <div class=\"upload-actions\">\r\n @switch (upload.status) {\r\n @case ('pending') {\r\n <button cideEleButton variant=\"text\" size=\"sm\" (click)=\"cancelUpload(upload.fileId)\">\r\n <cide-ele-icon name=\"cancel\" size=\"xs\"></cide-ele-icon>\r\n </button>\r\n }\r\n @case ('uploading') {\r\n <button cideEleButton variant=\"text\" size=\"sm\" (click)=\"cancelUpload(upload.fileId)\">\r\n <cide-ele-icon name=\"cancel\" size=\"xs\"></cide-ele-icon>\r\n </button>\r\n }\r\n @case ('completed') {\r\n <button cideEleButton variant=\"text\" size=\"sm\" (click)=\"removeUpload(upload.fileId)\">\r\n <cide-ele-icon name=\"close\" size=\"xs\"></cide-ele-icon>\r\n </button>\r\n }\r\n @case ('error') {\r\n <button cideEleButton variant=\"text\" size=\"sm\" (click)=\"retryUpload(upload.fileId)\">\r\n <cide-ele-icon name=\"refresh\" size=\"xs\"></cide-ele-icon>\r\n </button>\r\n <button cideEleButton variant=\"text\" size=\"sm\" (click)=\"removeUpload(upload.fileId)\">\r\n <cide-ele-icon name=\"close\" size=\"xs\"></cide-ele-icon>\r\n </button>\r\n }\r\n @case ('cancelled') {\r\n <button cideEleButton variant=\"text\" size=\"sm\" (click)=\"removeUpload(upload.fileId)\">\r\n <cide-ele-icon name=\"close\" size=\"xs\"></cide-ele-icon>\r\n </button>\r\n }\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Queue Summary -->\r\n <div class=\"queue-summary\">\r\n <div class=\"summary-stats\">\r\n <span class=\"stat\">\r\n <cide-ele-icon name=\"schedule\" size=\"xs\"></cide-ele-icon>\r\n {{ pendingUploads().length }} pending\r\n </span>\r\n <span class=\"stat\">\r\n <cide-ele-icon name=\"cloud_upload\" size=\"xs\"></cide-ele-icon>\r\n {{ activeUploads().length }} uploading\r\n </span>\r\n <span class=\"stat\">\r\n <cide-ele-icon name=\"check_circle\" size=\"xs\"></cide-ele-icon>\r\n {{ completedUploads().length }} completed\r\n </span>\r\n @if (failedUploads().length > 0) {\r\n <span class=\"stat error\">\r\n <cide-ele-icon name=\"error\" size=\"xs\"></cide-ele-icon>\r\n {{ failedUploads().length }} failed\r\n </span>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n\r\n", styles: [".global-file-uploader{@apply tw-w-full tw-max-w-2xl tw-mx-auto tw-p-4;}.global-file-uploader .upload-zone{@apply tw-border-2 tw-border-dashed tw-border-gray-300 tw-rounded-lg tw-p-8 tw-text-center tw-cursor-pointer tw-transition-all tw-duration-200 tw-ease-in-out;@apply hover:tw-border-blue-400 hover:tw-bg-blue-50;}.global-file-uploader .upload-zone.drag-over{@apply tw-border-blue-500 tw-bg-blue-100 tw-scale-105;}.global-file-uploader .upload-zone .upload-zone-content{@apply tw-space-y-4;}.global-file-uploader .upload-zone .upload-zone-content .upload-icon{@apply tw-text-gray-400 tw-mx-auto;}.global-file-uploader .upload-zone .upload-zone-content .upload-title{@apply tw-text-xl tw-font-semibold tw-text-gray-700 tw-m-0;}.global-file-uploader .upload-zone .upload-zone-content .upload-subtitle{@apply tw-text-sm tw-text-gray-500 tw-m-0;}.global-file-uploader .upload-queue{@apply tw-mt-6 tw-bg-white tw-border tw-border-gray-200 tw-rounded-lg tw-shadow-sm;}.global-file-uploader .upload-queue .queue-header{@apply tw-flex tw-justify-between tw-items-center tw-p-4 tw-border-b tw-border-gray-200;}.global-file-uploader .upload-queue .queue-header .queue-title{@apply tw-text-lg tw-font-medium tw-text-gray-700 tw-m-0;}.global-file-uploader .upload-queue .queue-header .queue-actions{@apply tw-flex tw-space-x-2;}.global-file-uploader .upload-queue .queue-items{@apply tw-max-h-96 tw-overflow-y-auto;}.global-file-uploader .upload-queue .queue-items .upload-item{@apply tw-flex tw-items-center tw-justify-between tw-p-4 tw-border-b tw-border-gray-100 tw-transition-colors tw-duration-200;}.global-file-uploader .upload-queue .queue-items .upload-item:last-child{@apply tw-border-b-0;}.global-file-uploader .upload-queue .queue-items .upload-item.status-pending{@apply tw-bg-yellow-50;}.global-file-uploader .upload-queue .queue-items .upload-item.status-uploading{@apply tw-bg-blue-50;}.global-file-uploader .upload-queue .queue-items .upload-item.status-completed{@apply tw-bg-green-50;}.global-file-uploader .upload-queue .queue-items .upload-item.status-error{@apply tw-bg-red-50;}.global-file-uploader .upload-queue .queue-items .upload-item.status-cancelled{@apply tw-bg-gray-50;}.global-file-uploader .upload-queue .queue-items .upload-item .file-info{@apply tw-flex tw-items-center tw-flex-1 tw-min-w-0;}.global-file-uploader .upload-queue .queue-items .upload-item .file-info .status-icon{@apply tw-mr-3 tw-flex-shrink-0;}.global-file-uploader .upload-queue .queue-items .upload-item .file-info .file-details{@apply tw-min-w-0 tw-flex-1;}.global-file-uploader .upload-queue .queue-items .upload-item .file-info .file-details .file-name{@apply tw-text-sm tw-font-medium tw-text-gray-700 tw-truncate;}.global-file-uploader .upload-queue .queue-items .upload-item .file-info .file-details .file-size{@apply tw-text-xs tw-text-gray-500 tw-mt-1;}.global-file-uploader .upload-queue .queue-items .upload-item .progress-container{@apply tw-flex tw-items-center tw-space-x-3 tw-flex-1 tw-max-w-xs tw-mx-4;}.global-file-uploader .upload-queue .queue-items .upload-item .progress-container .progress-bar{@apply tw-flex-1 tw-h-2 tw-bg-gray-200 tw-rounded-full tw-overflow-hidden;}.global-file-uploader .upload-queue .queue-items .upload-item .progress-container .progress-bar .progress-fill{@apply tw-h-full tw-bg-blue-500 tw-transition-all tw-duration-300 tw-ease-out;}.global-file-uploader .upload-queue .queue-items .upload-item .progress-container .progress-text{@apply tw-text-xs tw-font-medium tw-text-gray-600 tw-min-w-12 tw-text-right;}.global-file-uploader .upload-queue .queue-items .upload-item .status-text{@apply tw-flex-1 tw-text-sm tw-text-center;}.global-file-uploader .upload-queue .queue-items .upload-item .status-text span{@apply tw-font-medium;}.global-file-uploader .upload-queue .queue-items .upload-item .upload-actions{@apply tw-flex tw-space-x-1 tw-ml-4;}.global-file-uploader .upload-queue .queue-summary{@apply tw-p-4 tw-bg-gray-50 tw-border-t tw-border-gray-200;}.global-file-uploader .upload-queue .queue-summary .summary-stats{@apply tw-flex tw-flex-wrap tw-gap-4 tw-text-sm tw-text-gray-600;}.global-file-uploader .upload-queue .queue-summary .summary-stats .stat{@apply tw-flex tw-items-center tw-space-x-1;}.global-file-uploader .upload-queue .queue-summary .summary-stats .stat.error{@apply tw-text-red-600;}@media (max-width: 640px){.global-file-uploader{@apply tw-p-2;}.global-file-uploader .upload-zone{@apply tw-p-6;}.global-file-uploader .upload-zone .upload-zone-content .upload-title{@apply tw-text-lg;}.global-file-uploader .upload-zone .upload-zone-content .upload-subtitle{@apply tw-text-xs;}.global-file-uploader .upload-queue .queue-header{@apply tw-p-3 tw-flex-col tw-space-y-2 tw-items-start;}.global-file-uploader .upload-queue .queue-header .queue-actions{@apply tw-w-full tw-justify-end;}.global-file-uploader .upload-queue .queue-items .upload-item{@apply tw-p-3 tw-flex-col tw-space-y-3 tw-items-start;}.global-file-uploader .upload-queue .queue-items .upload-item .file-info{@apply tw-w-full;}.global-file-uploader .upload-queue .queue-items .upload-item .progress-container{@apply tw-w-full tw-mx-0;}.global-file-uploader .upload-queue .queue-items .upload-item .status-text{@apply tw-w-full tw-text-left;}.global-file-uploader .upload-queue .queue-items .upload-item .upload-actions{@apply tw-w-full tw-justify-end tw-ml-0;}}@media (prefers-color-scheme: dark){.global-file-uploader .upload-zone{@apply tw-border-gray-600 hover:tw-border-blue-400 hover:tw-bg-gray-800;}.global-file-uploader .upload-zone.drag-over{@apply tw-bg-blue-900;}.global-file-uploader .upload-zone .upload-zone-content .upload-icon{@apply tw-text-gray-500;}.global-file-uploader .upload-zone .upload-zone-content .upload-title{@apply tw-text-gray-200;}.global-file-uploader .upload-zone .upload-zone-content .upload-subtitle{@apply tw-text-gray-400;}.global-file-uploader .upload-queue{@apply tw-bg-gray-800 tw-border-gray-700;}.global-file-uploader .upload-queue .queue-header{@apply tw-border-gray-700;}.global-file-uploader .upload-queue .queue-header .queue-title{@apply tw-text-gray-200;}.global-file-uploader .upload-queue .queue-items .upload-item{@apply tw-border-gray-700;}.global-file-uploader .upload-queue .queue-items .upload-item.status-pending{@apply tw-bg-yellow-900;}.global-file-uploader .upload-queue .queue-items .upload-item.status-uploading{@apply tw-bg-blue-900;}.global-file-uploader .upload-queue .queue-items .upload-item.status-completed{@apply tw-bg-green-900;}.global-file-uploader .upload-queue .queue-items .upload-item.status-error{@apply tw-bg-red-900;}.global-file-uploader .upload-queue .queue-items .upload-item.status-cancelled{@apply tw-bg-gray-700;}.global-file-uploader .upload-queue .queue-items .upload-item .file-info .file-details .file-name{@apply tw-text-gray-200;}.global-file-uploader .upload-queue .queue-items .upload-item .file-info .file-details .file-size{@apply tw-text-gray-400;}.global-file-uploader .upload-queue .queue-items .upload-item .progress-container .progress-bar{@apply tw-bg-gray-600;}.global-file-uploader .upload-queue .queue-items .upload-item .progress-container .progress-text{@apply tw-text-gray-300;}.global-file-uploader .upload-queue .queue-summary{@apply tw-bg-gray-700 tw-border-gray-600;}.global-file-uploader .upload-queue .queue-summary .summary-stats{@apply tw-text-gray-300;}.global-file-uploader .upload-queue .queue-summary .summary-stats .stat.error{@apply tw-text-red-400;}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: CideEleButtonComponent, selector: "button[cideEleButton], a[cideEleButton]", inputs: ["label", "variant", "size", "type", "shape", "elevation", "disabled", "id", "loading", "fullWidth", "leftIcon", "rightIcon", "customClass", "tooltip", "ariaLabel", "testId", "routerLink", "routerExtras", "preventDoubleClick", "animated"], outputs: ["btnClick", "doubleClick"] }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }] });
|
|
5110
|
-
}
|
|
5111
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideEleGlobalFileUploaderComponent, decorators: [{
|
|
5112
|
-
type: Component,
|
|
5113
|
-
args: [{ selector: 'cide-ele-global-file-uploader', standalone: true, imports: [
|
|
5114
|
-
CommonModule,
|
|
5115
|
-
CideEleButtonComponent,
|
|
5116
|
-
CideIconComponent
|
|
5117
|
-
], template: "<!-- Global File Uploader Component -->\r\n<div class=\"global-file-uploader\">\r\n\r\n <!-- Hidden file input -->\r\n <input id=\"global-file-input\" type=\"file\" [multiple]=\"multiple\" [accept]=\"accept\" \r\n (change)=\"onFileInputChange($event)\" style=\"display: none;\">\r\n\r\n <!-- Drag and Drop Zone -->\r\n <div class=\"upload-zone\" [class.drag-over]=\"isDragOver()\" (dragover)=\"onDragOver($event)\"\r\n (dragleave)=\"onDragLeave($event)\" (drop)=\"onDrop($event)\" (click)=\"triggerFileInput()\">\r\n\r\n <div class=\"upload-zone-content\">\r\n <cide-ele-icon name=\"cloud_upload\" class=\"upload-icon\" size=\"lg\">\r\n </cide-ele-icon>\r\n\r\n <h3 class=\"upload-title\">\r\n {{ isDragOver() ? 'Drop files here' : 'Upload Files' }}\r\n </h3>\r\n\r\n <p class=\"upload-subtitle\">\r\n {{ isDragOver() ? 'Release to upload' : 'Drag and drop files here or click to browse' }}\r\n </p>\r\n\r\n <button cideEleButton cideEleButton variant=\"outline\" size=\"sm\"\r\n (click)=\"triggerFileInput(); $event.stopPropagation()\">\r\n <cide-ele-icon name=\"add\" size=\"sm\"></cide-ele-icon>\r\n Choose Files\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Upload Queue -->\r\n @if (hasUploads()) {\r\n <div class=\"upload-queue\">\r\n <div class=\"queue-header\">\r\n <h4 class=\"queue-title\">Upload Queue</h4>\r\n <div class=\"queue-actions\">\r\n @if (completedUploads().length > 0) {\r\n <button cideEleButton variant=\"text\" size=\"sm\" (click)=\"clearCompleted()\">\r\n Clear Completed\r\n </button>\r\n }\r\n <button cideEleButton variant=\"text\" size=\"sm\" (click)=\"clearAll()\">\r\n Clear All\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <div class=\"queue-items\">\r\n @for (upload of uploadQueue(); track upload.fileId) {\r\n <div class=\"upload-item\" [class]=\"getStatusClass(upload.status)\">\r\n\r\n <!-- File Info -->\r\n <div class=\"file-info\">\r\n <cide-ele-icon class=\"status-icon\" size=\"sm\">\r\n {{getStatusIcon(upload.status)}}\r\n </cide-ele-icon>\r\n\r\n <div class=\"file-details\">\r\n <div class=\"file-name\">{{ upload.fileName }}</div>\r\n @if (upload.file) {\r\n <div class=\"file-size\">{{ getFileSizeDisplay(upload.file) }}</div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- Progress Bar -->\r\n @if (upload.status === 'uploading') {\r\n <div class=\"progress-container\">\r\n <div class=\"progress-bar\">\r\n <div class=\"progress-fill\" [style.width.%]=\"upload.progress\">\r\n </div>\r\n </div>\r\n <span class=\"progress-text\">{{ upload.progress }}%</span>\r\n </div>\r\n }\r\n\r\n <!-- Status Text -->\r\n @if (upload.status !== 'uploading') {\r\n <div class=\"status-text\">\r\n @switch (upload.status) {\r\n @case ('pending') {\r\n <span class=\"text-yellow-600\">Waiting...</span>\r\n }\r\n @case ('completed') {\r\n <span class=\"text-green-600\">Uploaded successfully</span>\r\n }\r\n @case ('error') {\r\n <span class=\"text-red-600\">{{ upload.error || 'Upload failed' }}</span>\r\n }\r\n @case ('cancelled') {\r\n <span class=\"text-gray-600\">Cancelled</span>\r\n }\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Actions -->\r\n <div class=\"upload-actions\">\r\n @switch (upload.status) {\r\n @case ('pending') {\r\n <button cideEleButton variant=\"text\" size=\"sm\" (click)=\"cancelUpload(upload.fileId)\">\r\n <cide-ele-icon name=\"cancel\" size=\"xs\"></cide-ele-icon>\r\n </button>\r\n }\r\n @case ('uploading') {\r\n <button cideEleButton variant=\"text\" size=\"sm\" (click)=\"cancelUpload(upload.fileId)\">\r\n <cide-ele-icon name=\"cancel\" size=\"xs\"></cide-ele-icon>\r\n </button>\r\n }\r\n @case ('completed') {\r\n <button cideEleButton variant=\"text\" size=\"sm\" (click)=\"removeUpload(upload.fileId)\">\r\n <cide-ele-icon name=\"close\" size=\"xs\"></cide-ele-icon>\r\n </button>\r\n }\r\n @case ('error') {\r\n <button cideEleButton variant=\"text\" size=\"sm\" (click)=\"retryUpload(upload.fileId)\">\r\n <cide-ele-icon name=\"refresh\" size=\"xs\"></cide-ele-icon>\r\n </button>\r\n <button cideEleButton variant=\"text\" size=\"sm\" (click)=\"removeUpload(upload.fileId)\">\r\n <cide-ele-icon name=\"close\" size=\"xs\"></cide-ele-icon>\r\n </button>\r\n }\r\n @case ('cancelled') {\r\n <button cideEleButton variant=\"text\" size=\"sm\" (click)=\"removeUpload(upload.fileId)\">\r\n <cide-ele-icon name=\"close\" size=\"xs\"></cide-ele-icon>\r\n </button>\r\n }\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Queue Summary -->\r\n <div class=\"queue-summary\">\r\n <div class=\"summary-stats\">\r\n <span class=\"stat\">\r\n <cide-ele-icon name=\"schedule\" size=\"xs\"></cide-ele-icon>\r\n {{ pendingUploads().length }} pending\r\n </span>\r\n <span class=\"stat\">\r\n <cide-ele-icon name=\"cloud_upload\" size=\"xs\"></cide-ele-icon>\r\n {{ activeUploads().length }} uploading\r\n </span>\r\n <span class=\"stat\">\r\n <cide-ele-icon name=\"check_circle\" size=\"xs\"></cide-ele-icon>\r\n {{ completedUploads().length }} completed\r\n </span>\r\n @if (failedUploads().length > 0) {\r\n <span class=\"stat error\">\r\n <cide-ele-icon name=\"error\" size=\"xs\"></cide-ele-icon>\r\n {{ failedUploads().length }} failed\r\n </span>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n\r\n", styles: [".global-file-uploader{@apply tw-w-full tw-max-w-2xl tw-mx-auto tw-p-4;}.global-file-uploader .upload-zone{@apply tw-border-2 tw-border-dashed tw-border-gray-300 tw-rounded-lg tw-p-8 tw-text-center tw-cursor-pointer tw-transition-all tw-duration-200 tw-ease-in-out;@apply hover:tw-border-blue-400 hover:tw-bg-blue-50;}.global-file-uploader .upload-zone.drag-over{@apply tw-border-blue-500 tw-bg-blue-100 tw-scale-105;}.global-file-uploader .upload-zone .upload-zone-content{@apply tw-space-y-4;}.global-file-uploader .upload-zone .upload-zone-content .upload-icon{@apply tw-text-gray-400 tw-mx-auto;}.global-file-uploader .upload-zone .upload-zone-content .upload-title{@apply tw-text-xl tw-font-semibold tw-text-gray-700 tw-m-0;}.global-file-uploader .upload-zone .upload-zone-content .upload-subtitle{@apply tw-text-sm tw-text-gray-500 tw-m-0;}.global-file-uploader .upload-queue{@apply tw-mt-6 tw-bg-white tw-border tw-border-gray-200 tw-rounded-lg tw-shadow-sm;}.global-file-uploader .upload-queue .queue-header{@apply tw-flex tw-justify-between tw-items-center tw-p-4 tw-border-b tw-border-gray-200;}.global-file-uploader .upload-queue .queue-header .queue-title{@apply tw-text-lg tw-font-medium tw-text-gray-700 tw-m-0;}.global-file-uploader .upload-queue .queue-header .queue-actions{@apply tw-flex tw-space-x-2;}.global-file-uploader .upload-queue .queue-items{@apply tw-max-h-96 tw-overflow-y-auto;}.global-file-uploader .upload-queue .queue-items .upload-item{@apply tw-flex tw-items-center tw-justify-between tw-p-4 tw-border-b tw-border-gray-100 tw-transition-colors tw-duration-200;}.global-file-uploader .upload-queue .queue-items .upload-item:last-child{@apply tw-border-b-0;}.global-file-uploader .upload-queue .queue-items .upload-item.status-pending{@apply tw-bg-yellow-50;}.global-file-uploader .upload-queue .queue-items .upload-item.status-uploading{@apply tw-bg-blue-50;}.global-file-uploader .upload-queue .queue-items .upload-item.status-completed{@apply tw-bg-green-50;}.global-file-uploader .upload-queue .queue-items .upload-item.status-error{@apply tw-bg-red-50;}.global-file-uploader .upload-queue .queue-items .upload-item.status-cancelled{@apply tw-bg-gray-50;}.global-file-uploader .upload-queue .queue-items .upload-item .file-info{@apply tw-flex tw-items-center tw-flex-1 tw-min-w-0;}.global-file-uploader .upload-queue .queue-items .upload-item .file-info .status-icon{@apply tw-mr-3 tw-flex-shrink-0;}.global-file-uploader .upload-queue .queue-items .upload-item .file-info .file-details{@apply tw-min-w-0 tw-flex-1;}.global-file-uploader .upload-queue .queue-items .upload-item .file-info .file-details .file-name{@apply tw-text-sm tw-font-medium tw-text-gray-700 tw-truncate;}.global-file-uploader .upload-queue .queue-items .upload-item .file-info .file-details .file-size{@apply tw-text-xs tw-text-gray-500 tw-mt-1;}.global-file-uploader .upload-queue .queue-items .upload-item .progress-container{@apply tw-flex tw-items-center tw-space-x-3 tw-flex-1 tw-max-w-xs tw-mx-4;}.global-file-uploader .upload-queue .queue-items .upload-item .progress-container .progress-bar{@apply tw-flex-1 tw-h-2 tw-bg-gray-200 tw-rounded-full tw-overflow-hidden;}.global-file-uploader .upload-queue .queue-items .upload-item .progress-container .progress-bar .progress-fill{@apply tw-h-full tw-bg-blue-500 tw-transition-all tw-duration-300 tw-ease-out;}.global-file-uploader .upload-queue .queue-items .upload-item .progress-container .progress-text{@apply tw-text-xs tw-font-medium tw-text-gray-600 tw-min-w-12 tw-text-right;}.global-file-uploader .upload-queue .queue-items .upload-item .status-text{@apply tw-flex-1 tw-text-sm tw-text-center;}.global-file-uploader .upload-queue .queue-items .upload-item .status-text span{@apply tw-font-medium;}.global-file-uploader .upload-queue .queue-items .upload-item .upload-actions{@apply tw-flex tw-space-x-1 tw-ml-4;}.global-file-uploader .upload-queue .queue-summary{@apply tw-p-4 tw-bg-gray-50 tw-border-t tw-border-gray-200;}.global-file-uploader .upload-queue .queue-summary .summary-stats{@apply tw-flex tw-flex-wrap tw-gap-4 tw-text-sm tw-text-gray-600;}.global-file-uploader .upload-queue .queue-summary .summary-stats .stat{@apply tw-flex tw-items-center tw-space-x-1;}.global-file-uploader .upload-queue .queue-summary .summary-stats .stat.error{@apply tw-text-red-600;}@media (max-width: 640px){.global-file-uploader{@apply tw-p-2;}.global-file-uploader .upload-zone{@apply tw-p-6;}.global-file-uploader .upload-zone .upload-zone-content .upload-title{@apply tw-text-lg;}.global-file-uploader .upload-zone .upload-zone-content .upload-subtitle{@apply tw-text-xs;}.global-file-uploader .upload-queue .queue-header{@apply tw-p-3 tw-flex-col tw-space-y-2 tw-items-start;}.global-file-uploader .upload-queue .queue-header .queue-actions{@apply tw-w-full tw-justify-end;}.global-file-uploader .upload-queue .queue-items .upload-item{@apply tw-p-3 tw-flex-col tw-space-y-3 tw-items-start;}.global-file-uploader .upload-queue .queue-items .upload-item .file-info{@apply tw-w-full;}.global-file-uploader .upload-queue .queue-items .upload-item .progress-container{@apply tw-w-full tw-mx-0;}.global-file-uploader .upload-queue .queue-items .upload-item .status-text{@apply tw-w-full tw-text-left;}.global-file-uploader .upload-queue .queue-items .upload-item .upload-actions{@apply tw-w-full tw-justify-end tw-ml-0;}}@media (prefers-color-scheme: dark){.global-file-uploader .upload-zone{@apply tw-border-gray-600 hover:tw-border-blue-400 hover:tw-bg-gray-800;}.global-file-uploader .upload-zone.drag-over{@apply tw-bg-blue-900;}.global-file-uploader .upload-zone .upload-zone-content .upload-icon{@apply tw-text-gray-500;}.global-file-uploader .upload-zone .upload-zone-content .upload-title{@apply tw-text-gray-200;}.global-file-uploader .upload-zone .upload-zone-content .upload-subtitle{@apply tw-text-gray-400;}.global-file-uploader .upload-queue{@apply tw-bg-gray-800 tw-border-gray-700;}.global-file-uploader .upload-queue .queue-header{@apply tw-border-gray-700;}.global-file-uploader .upload-queue .queue-header .queue-title{@apply tw-text-gray-200;}.global-file-uploader .upload-queue .queue-items .upload-item{@apply tw-border-gray-700;}.global-file-uploader .upload-queue .queue-items .upload-item.status-pending{@apply tw-bg-yellow-900;}.global-file-uploader .upload-queue .queue-items .upload-item.status-uploading{@apply tw-bg-blue-900;}.global-file-uploader .upload-queue .queue-items .upload-item.status-completed{@apply tw-bg-green-900;}.global-file-uploader .upload-queue .queue-items .upload-item.status-error{@apply tw-bg-red-900;}.global-file-uploader .upload-queue .queue-items .upload-item.status-cancelled{@apply tw-bg-gray-700;}.global-file-uploader .upload-queue .queue-items .upload-item .file-info .file-details .file-name{@apply tw-text-gray-200;}.global-file-uploader .upload-queue .queue-items .upload-item .file-info .file-details .file-size{@apply tw-text-gray-400;}.global-file-uploader .upload-queue .queue-items .upload-item .progress-container .progress-bar{@apply tw-bg-gray-600;}.global-file-uploader .upload-queue .queue-items .upload-item .progress-container .progress-text{@apply tw-text-gray-300;}.global-file-uploader .upload-queue .queue-summary{@apply tw-bg-gray-700 tw-border-gray-600;}.global-file-uploader .upload-queue .queue-summary .summary-stats{@apply tw-text-gray-300;}.global-file-uploader .upload-queue .queue-summary .summary-stats .stat.error{@apply tw-text-red-400;}}\n"] }]
|
|
5118
|
-
}], ctorParameters: () => [], propDecorators: { userId: [{
|
|
5119
|
-
type: Input
|
|
5120
|
-
}], multiple: [{
|
|
5121
|
-
type: Input
|
|
5122
|
-
}], accept: [{
|
|
5123
|
-
type: Input
|
|
5124
|
-
}], maxFileSize: [{
|
|
5125
|
-
type: Input
|
|
5126
|
-
}], allowedTypes: [{
|
|
5127
|
-
type: Input
|
|
5128
|
-
}], uploadComplete: [{
|
|
5129
|
-
type: Output
|
|
5130
|
-
}], uploadError: [{
|
|
5131
|
-
type: Output
|
|
5132
|
-
}], uploadCancelled: [{
|
|
5133
|
-
type: Output
|
|
5134
|
-
}], allUploadsComplete: [{
|
|
5135
|
-
type: Output
|
|
5136
|
-
}] } });
|
|
5137
|
-
|
|
5138
4865
|
class CideFloatingUploadTriggerDirective {
|
|
5139
4866
|
elementRef = inject(ElementRef);
|
|
5140
4867
|
floatingUploader = inject(CideEleFloatingFileUploaderComponent);
|
|
@@ -9481,5 +9208,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
|
|
|
9481
9208
|
* Generated bundle index. Do not edit.
|
|
9482
9209
|
*/
|
|
9483
9210
|
|
|
9484
|
-
export { CideCoreFileManagerService, CideEleButtonComponent, CideEleConfirmationModalComponent, CideEleDataGridComponent, CideEleDropdownComponent, CideEleFileImageDirective, CideEleFileInputComponent, CideEleFileManagerService, CideEleFloatingFileUploaderComponent,
|
|
9211
|
+
export { CideCoreFileManagerService, CideEleButtonComponent, CideEleConfirmationModalComponent, CideEleDataGridComponent, CideEleDropdownComponent, CideEleFileImageDirective, CideEleFileInputComponent, CideEleFileManagerService, CideEleFloatingFileUploaderComponent, CideEleGlobalNotificationsComponent, CideEleJsonEditorComponent, CideEleResizerDirective, CideEleSkeletonLoaderComponent, CideEleTabComponent, CideEleToastNotificationComponent, CideElementsService, CideFloatingUploadTriggerDirective, CideIconComponent, CideInputComponent, CideSelectComponent, CideSelectOptionComponent, CideSpinnerComponent, CideTextareaComponent, ConfirmationService, CoreFileManagerInsertUpdatePayload, DEFAULT_GRID_CONFIG, DropdownManagerService, ICoreCyfmSave, MFileManager, NotificationService, TooltipDirective };
|
|
9485
9212
|
//# sourceMappingURL=cloud-ide-element.mjs.map
|