@dataclouder/ngx-cloud-storage 0.0.21 → 0.0.22
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/dataclouder-ngx-cloud-storage.mjs +259 -100
- package/fesm2022/dataclouder-ngx-cloud-storage.mjs.map +1 -1
- package/lib/components/cropper/cropper.component.d.ts +11 -11
- package/lib/components/cropper-modal/cropper-modal.component.d.ts +9 -9
- package/lib/components/image-storage-preview/image-storage-preview.d.ts +29 -0
- package/lib/services/dc-files-cache.service.d.ts +2 -2
- package/lib/services/multi-images-storage.service.d.ts +0 -2
- package/lib/services/multi-object-storage.service.d.ts +46 -0
- package/package.json +1 -1
- package/public-api.d.ts +2 -0
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Injectable,
|
|
2
|
+
import { inject, Injectable, input, output, ViewChild, Component, ChangeDetectorRef, Input } from '@angular/core';
|
|
3
3
|
import { ImageCropperComponent } from 'ngx-image-cropper';
|
|
4
|
-
import
|
|
5
|
-
import * as i2 from '@angular/forms';
|
|
4
|
+
import * as i1 from '@angular/forms';
|
|
6
5
|
import { FormsModule } from '@angular/forms';
|
|
7
|
-
import
|
|
8
|
-
import { getStorage, listAll, deleteObject, getDownloadURL, ref as ref$1 } from '@angular/fire/storage';
|
|
9
|
-
import { ref } from 'firebase/storage';
|
|
10
|
-
import { lastValueFrom } from 'rxjs';
|
|
11
|
-
import * as
|
|
12
|
-
import * as i5 from 'primeng/dialog';
|
|
6
|
+
import { AngularFireStorage } from '@angular/fire/compat/storage';
|
|
7
|
+
import { getStorage, listAll, deleteObject, getDownloadURL, Storage, ref as ref$1 } from '@angular/fire/storage';
|
|
8
|
+
import { ref, getStorage as getStorage$1, listAll as listAll$1, getDownloadURL as getDownloadURL$1 } from 'firebase/storage';
|
|
9
|
+
import { lastValueFrom, BehaviorSubject } from 'rxjs';
|
|
10
|
+
import * as i4 from 'primeng/dialog';
|
|
13
11
|
import { DialogModule } from 'primeng/dialog';
|
|
14
|
-
import * as
|
|
12
|
+
import * as i5 from 'primeng/tooltip';
|
|
15
13
|
import { TooltipModule } from 'primeng/tooltip';
|
|
16
|
-
import * as
|
|
14
|
+
import * as i2 from 'primeng/button';
|
|
17
15
|
import { ButtonModule } from 'primeng/button';
|
|
18
|
-
import * as
|
|
16
|
+
import * as i6 from 'primeng/message';
|
|
19
17
|
import { MessageModule } from 'primeng/message';
|
|
20
|
-
import * as
|
|
18
|
+
import * as i7 from 'primeng/select';
|
|
21
19
|
import { SelectModule } from 'primeng/select';
|
|
22
|
-
import * as
|
|
20
|
+
import * as i8 from 'primeng/inputtext';
|
|
23
21
|
import { InputTextModule } from 'primeng/inputtext';
|
|
24
|
-
import * as
|
|
22
|
+
import * as i3 from 'primeng/api';
|
|
23
|
+
import { AsyncPipe } from '@angular/common';
|
|
25
24
|
|
|
26
25
|
const AspectRatioOptions = [
|
|
27
26
|
{ value: '1:1', label: 'square', description: 'Square (1:1)', valueRatio: 1 / 1 },
|
|
@@ -84,8 +83,8 @@ const DEFAULT_SETTINGS = {
|
|
|
84
83
|
};
|
|
85
84
|
|
|
86
85
|
class MultiImagesStorageService {
|
|
87
|
-
constructor(
|
|
88
|
-
this.storage =
|
|
86
|
+
constructor() {
|
|
87
|
+
this.storage = inject(AngularFireStorage);
|
|
89
88
|
}
|
|
90
89
|
async uploadImage(image, path) {
|
|
91
90
|
try {
|
|
@@ -176,26 +175,26 @@ class MultiImagesStorageService {
|
|
|
176
175
|
};
|
|
177
176
|
return meta;
|
|
178
177
|
}
|
|
179
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
180
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.
|
|
178
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MultiImagesStorageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
179
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MultiImagesStorageService, providedIn: 'root' }); }
|
|
181
180
|
}
|
|
182
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
181
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MultiImagesStorageService, decorators: [{
|
|
183
182
|
type: Injectable,
|
|
184
183
|
args: [{
|
|
185
184
|
providedIn: 'root',
|
|
186
185
|
}]
|
|
187
|
-
}]
|
|
186
|
+
}] });
|
|
188
187
|
|
|
189
188
|
class CropperComponent {
|
|
190
|
-
constructor(
|
|
191
|
-
this.multiImagesStorageService =
|
|
189
|
+
constructor() {
|
|
190
|
+
this.multiImagesStorageService = inject(MultiImagesStorageService);
|
|
192
191
|
// overrides name, path and resizeToWidth
|
|
193
|
-
this.imageSettings = {};
|
|
194
|
-
this.ratioType = AspectType.Square;
|
|
195
|
-
this.resolutions = [ResolutionType.MediumLarge];
|
|
196
|
-
this.imageUploaded =
|
|
197
|
-
this.onImageCropped =
|
|
198
|
-
this.onFileSelected =
|
|
192
|
+
this.imageSettings = input({});
|
|
193
|
+
this.ratioType = input(AspectType.Square);
|
|
194
|
+
this.resolutions = input([ResolutionType.MediumLarge]);
|
|
195
|
+
this.imageUploaded = output();
|
|
196
|
+
this.onImageCropped = output();
|
|
197
|
+
this.onFileSelected = output();
|
|
199
198
|
this.fileMetadata = null;
|
|
200
199
|
this.aspectRatio = 1;
|
|
201
200
|
this.croppedImage = '';
|
|
@@ -206,13 +205,14 @@ class CropperComponent {
|
|
|
206
205
|
this.showModal = false;
|
|
207
206
|
}
|
|
208
207
|
ngOnInit() {
|
|
209
|
-
this.aspectRatio = AspectRatio[this.ratioType];
|
|
210
|
-
|
|
211
|
-
|
|
208
|
+
this.aspectRatio = AspectRatio[this.ratioType()];
|
|
209
|
+
const imageSettings = this.imageSettings();
|
|
210
|
+
if (imageSettings.path) {
|
|
211
|
+
this.storagePath = `${imageSettings.path}/${imageSettings.fileName}.webp`;
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
214
|
reloadPath() {
|
|
215
|
-
this.storagePath = `${this.imageSettings.path}/${this.renameFile}.webp`;
|
|
215
|
+
this.storagePath = `${this.imageSettings().path}/${this.renameFile}.webp`;
|
|
216
216
|
}
|
|
217
217
|
async fileChangeEvent(event) {
|
|
218
218
|
this.imageChangedEvent = event;
|
|
@@ -223,7 +223,7 @@ class CropperComponent {
|
|
|
223
223
|
this.showModal = true; // Show modal when file is selected
|
|
224
224
|
this.renameFile = this.fileMetadata?.name?.split('.')[0];
|
|
225
225
|
console.log(this.renameFile);
|
|
226
|
-
if (!this.imageSettings.fileName) {
|
|
226
|
+
if (!this.imageSettings().fileName) {
|
|
227
227
|
this.reloadPath();
|
|
228
228
|
}
|
|
229
229
|
}
|
|
@@ -245,7 +245,7 @@ class CropperComponent {
|
|
|
245
245
|
}
|
|
246
246
|
async uploadToStorage(imageMulticrops) {
|
|
247
247
|
// TODO: Nota si algo falla aquí puede causar inconsistencias en el sistema, ver como manejar errores
|
|
248
|
-
const path = this.imageSettings.path;
|
|
248
|
+
const path = this.imageSettings().path;
|
|
249
249
|
// const imageUploaded = await this.multiImagesStorageService.uploadImage(imageMulticrops, path);
|
|
250
250
|
// this.modalRef.close();
|
|
251
251
|
// console.log(imageUploaded);
|
|
@@ -268,40 +268,28 @@ class CropperComponent {
|
|
|
268
268
|
closeModal() {
|
|
269
269
|
this.showModal = false;
|
|
270
270
|
}
|
|
271
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
272
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
271
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: CropperComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
272
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: CropperComponent, isStandalone: true, selector: "app-cropper", inputs: { imageSettings: { classPropertyName: "imageSettings", publicName: "imageSettings", isSignal: true, isRequired: false, transformFunction: null }, ratioType: { classPropertyName: "ratioType", publicName: "ratioType", isSignal: true, isRequired: false, transformFunction: null }, resolutions: { classPropertyName: "resolutions", publicName: "resolutions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { imageUploaded: "imageUploaded", onImageCropped: "onImageCropped", onFileSelected: "onFileSelected" }, viewQueries: [{ propertyName: "imageCropper", first: true, predicate: ImageCropperComponent, descendants: true }], ngImport: i0, template: "<div> path: {{ storagePath }} </div>\n\n<div class=\"options\">\n @if (!isUploaded) {\n <div>\n <input type=\"file\" id=\"file-upload\" class=\"file-input\" (change)=\"fileChangeEvent($event)\" />\n <label for=\"file-upload\" class=\"btn-upload\">Seleccionar archivo</label>\n @if (!fileMetadata) {\n <em>Carga una imagen para comenzar</em>\n }\n </div>\n }\n\n @if (fileMetadata) {\n <span>\n <span style=\"margin: 1px 20px\"> tipo: {{ fileMetadata.type }} </span>\n <span style=\"margin: 1px 20px\"> tama\u00F1o {{ fileMetadata.size }} </span>\n <br />\n <input\n [disabled]=\"imageSettings()?.fileName\"\n style=\"margin: 1px 20px; width: 400px\"\n [(ngModel)]=\"renameFile\"\n type=\"text\"\n placeholder=\"Rename File\"\n (ngModelChange)=\"reloadPath()\" />\n <button class=\"btn-crop\" (click)=\"closeModal()\"> Recortar y Subir </button>\n </span>\n }\n</div>\n\n@if (fileMetadata && !isUploaded) {\n <div class=\"modal\" [class.show-modal]=\"showModal\">\n <div class=\"modal-content\">\n <div class=\"modal-header\">\n <h3>Recortar imagen</h3>\n <button class=\"close-button\" (click)=\"closeModal()\">\u00D7</button>\n </div>\n <div class=\"modal-body\">\n <h1>Hlloa</h1>\n <image-cropper\n [imageChangedEvent]=\"imageChangedEvent\"\n [maintainAspectRatio]=\"true\"\n [aspectRatio]=\"aspectRatio\"\n format=\"webp\"\n [resizeToWidth]=\"450\"\n (imageCropped)=\"onInnerImageCropped($event)\"\n (loadImageFailed)=\"loadImageFailed()\"\n (imageLoaded)=\"imageLoaded($event)\"\n [autoCrop]=\"false\"></image-cropper>\n <div class=\"modal-footer\">\n <button class=\"btn-crop\" (click)=\"simpleCropAndUpload()\">Recortar y Subir</button>\n </div>\n </div>\n </div>\n </div>\n}\n\n@if (croppedImage && !isUploaded) {\n <button [disabled]=\"isLoading\" nbButton status=\"info\"> upload </button>\n}\n", styles: [".options{display:flex}.btn-crop{cursor:pointer;outline:0;display:inline-block;font-weight:400;line-height:1.5;text-align:center;background-color:transparent;border:1px solid transparent;padding:6px 12px;font-size:1rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;color:#0d6efd;border-color:#0d6efd}.btn-crop :hover{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.file-input{display:none}.btn-upload{cursor:pointer;outline:0;display:inline-block;font-weight:400;line-height:1.5;text-align:center;background-color:transparent;border:1px solid #0d6efd;padding:6px 12px;font-size:1rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;color:#0d6efd}.btn-upload:hover{color:#fff;background-color:#0d6efd}.modal{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background-color:#00000080;z-index:1000}.modal.show-modal{display:flex;align-items:center;justify-content:center}.modal .modal-content{background-color:#fff;border-radius:8px;width:90%;max-width:800px;max-height:90vh;overflow-y:auto;position:relative}.modal .modal-header{padding:1rem;border-bottom:1px solid #dee2e6;display:flex;justify-content:space-between;align-items:center}.modal .modal-header h3{margin:0}.modal .modal-header .close-button{background:none;border:none;font-size:1.5rem;cursor:pointer;padding:0;color:#6c757d}.modal .modal-header .close-button:hover{color:#343a40}.modal .modal-body{padding:1rem}.modal .modal-footer{padding:1rem;border-top:1px solid #dee2e6;display:flex;justify-content:flex-end}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ImageCropperComponent, selector: "image-cropper", inputs: ["imageChangedEvent", "imageURL", "imageBase64", "imageFile", "imageAltText", "options", "cropperFrameAriaLabel", "output", "format", "autoCrop", "cropper", "transform", "maintainAspectRatio", "aspectRatio", "resetCropOnAspectRatioChange", "resizeToWidth", "resizeToHeight", "cropperMinWidth", "cropperMinHeight", "cropperMaxHeight", "cropperMaxWidth", "cropperStaticWidth", "cropperStaticHeight", "canvasRotation", "initialStepSize", "roundCropper", "onlyScaleDown", "imageQuality", "backgroundColor", "containWithinAspectRatio", "hideResizeSquares", "allowMoveImage", "checkImageType", "alignImage", "disabled", "hidden"], outputs: ["imageCropped", "startCropImage", "imageLoaded", "cropperReady", "loadImageFailed", "transformChange", "cropperChange"] }] }); }
|
|
273
273
|
}
|
|
274
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
274
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: CropperComponent, decorators: [{
|
|
275
275
|
type: Component,
|
|
276
|
-
args: [{ selector: 'app-cropper', standalone: true, imports: [
|
|
277
|
-
}], ctorParameters: () => [
|
|
278
|
-
type: Input
|
|
279
|
-
}], ratioType: [{
|
|
280
|
-
type: Input
|
|
281
|
-
}], resolutions: [{
|
|
282
|
-
type: Input
|
|
283
|
-
}], imageUploaded: [{
|
|
284
|
-
type: Output
|
|
285
|
-
}], onImageCropped: [{
|
|
286
|
-
type: Output
|
|
287
|
-
}], onFileSelected: [{
|
|
288
|
-
type: Output
|
|
289
|
-
}], imageCropper: [{
|
|
276
|
+
args: [{ selector: 'app-cropper', standalone: true, imports: [FormsModule, ImageCropperComponent], template: "<div> path: {{ storagePath }} </div>\n\n<div class=\"options\">\n @if (!isUploaded) {\n <div>\n <input type=\"file\" id=\"file-upload\" class=\"file-input\" (change)=\"fileChangeEvent($event)\" />\n <label for=\"file-upload\" class=\"btn-upload\">Seleccionar archivo</label>\n @if (!fileMetadata) {\n <em>Carga una imagen para comenzar</em>\n }\n </div>\n }\n\n @if (fileMetadata) {\n <span>\n <span style=\"margin: 1px 20px\"> tipo: {{ fileMetadata.type }} </span>\n <span style=\"margin: 1px 20px\"> tama\u00F1o {{ fileMetadata.size }} </span>\n <br />\n <input\n [disabled]=\"imageSettings()?.fileName\"\n style=\"margin: 1px 20px; width: 400px\"\n [(ngModel)]=\"renameFile\"\n type=\"text\"\n placeholder=\"Rename File\"\n (ngModelChange)=\"reloadPath()\" />\n <button class=\"btn-crop\" (click)=\"closeModal()\"> Recortar y Subir </button>\n </span>\n }\n</div>\n\n@if (fileMetadata && !isUploaded) {\n <div class=\"modal\" [class.show-modal]=\"showModal\">\n <div class=\"modal-content\">\n <div class=\"modal-header\">\n <h3>Recortar imagen</h3>\n <button class=\"close-button\" (click)=\"closeModal()\">\u00D7</button>\n </div>\n <div class=\"modal-body\">\n <h1>Hlloa</h1>\n <image-cropper\n [imageChangedEvent]=\"imageChangedEvent\"\n [maintainAspectRatio]=\"true\"\n [aspectRatio]=\"aspectRatio\"\n format=\"webp\"\n [resizeToWidth]=\"450\"\n (imageCropped)=\"onInnerImageCropped($event)\"\n (loadImageFailed)=\"loadImageFailed()\"\n (imageLoaded)=\"imageLoaded($event)\"\n [autoCrop]=\"false\"></image-cropper>\n <div class=\"modal-footer\">\n <button class=\"btn-crop\" (click)=\"simpleCropAndUpload()\">Recortar y Subir</button>\n </div>\n </div>\n </div>\n </div>\n}\n\n@if (croppedImage && !isUploaded) {\n <button [disabled]=\"isLoading\" nbButton status=\"info\"> upload </button>\n}\n", styles: [".options{display:flex}.btn-crop{cursor:pointer;outline:0;display:inline-block;font-weight:400;line-height:1.5;text-align:center;background-color:transparent;border:1px solid transparent;padding:6px 12px;font-size:1rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;color:#0d6efd;border-color:#0d6efd}.btn-crop :hover{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.file-input{display:none}.btn-upload{cursor:pointer;outline:0;display:inline-block;font-weight:400;line-height:1.5;text-align:center;background-color:transparent;border:1px solid #0d6efd;padding:6px 12px;font-size:1rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;color:#0d6efd}.btn-upload:hover{color:#fff;background-color:#0d6efd}.modal{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background-color:#00000080;z-index:1000}.modal.show-modal{display:flex;align-items:center;justify-content:center}.modal .modal-content{background-color:#fff;border-radius:8px;width:90%;max-width:800px;max-height:90vh;overflow-y:auto;position:relative}.modal .modal-header{padding:1rem;border-bottom:1px solid #dee2e6;display:flex;justify-content:space-between;align-items:center}.modal .modal-header h3{margin:0}.modal .modal-header .close-button{background:none;border:none;font-size:1.5rem;cursor:pointer;padding:0;color:#6c757d}.modal .modal-header .close-button:hover{color:#343a40}.modal .modal-body{padding:1rem}.modal .modal-footer{padding:1rem;border-top:1px solid #dee2e6;display:flex;justify-content:flex-end}\n"] }]
|
|
277
|
+
}], ctorParameters: () => [], propDecorators: { imageCropper: [{
|
|
290
278
|
type: ViewChild,
|
|
291
279
|
args: [ImageCropperComponent]
|
|
292
280
|
}] } });
|
|
293
281
|
|
|
294
282
|
class CropperComponentModal {
|
|
295
|
-
constructor(
|
|
296
|
-
this.multiImagesStorageService =
|
|
297
|
-
this.changeDetectorRef =
|
|
283
|
+
constructor() {
|
|
284
|
+
this.multiImagesStorageService = inject(MultiImagesStorageService);
|
|
285
|
+
this.changeDetectorRef = inject(ChangeDetectorRef);
|
|
298
286
|
// overrides name, path and resizeToWidth
|
|
299
|
-
this.imgStorageSettings = DEFAULT_SETTINGS;
|
|
300
|
-
this.buttonLabel = 'Seleccionar archivo';
|
|
287
|
+
this.imgStorageSettings = input(DEFAULT_SETTINGS);
|
|
288
|
+
this.buttonLabel = input('Seleccionar archivo');
|
|
301
289
|
this.currentStorage = {};
|
|
302
|
-
this.imageUploaded =
|
|
303
|
-
this.onImageCropped =
|
|
304
|
-
this.onFileSelected =
|
|
290
|
+
this.imageUploaded = output();
|
|
291
|
+
this.onImageCropped = output();
|
|
292
|
+
this.onFileSelected = output();
|
|
305
293
|
this.aspectRatioOptions = AspectRatioOptions;
|
|
306
294
|
this.fileMetadata = null;
|
|
307
295
|
this.displayDialog = false;
|
|
@@ -315,38 +303,39 @@ class CropperComponentModal {
|
|
|
315
303
|
this.fileInputId = `file-upload-${Math.random().toString(36).substring(2, 11)}`;
|
|
316
304
|
}
|
|
317
305
|
ngOnInit() {
|
|
318
|
-
if (!this.imgStorageSettings.path) {
|
|
306
|
+
if (!this.imgStorageSettings().path) {
|
|
319
307
|
console.warn('⚠️ Remember to set imgStorageSettings, path and fileName are required , path example: /collection/id/subcollection ');
|
|
320
308
|
}
|
|
321
309
|
this.reloadPath();
|
|
322
310
|
}
|
|
323
311
|
reloadPath() {
|
|
324
312
|
const randomCharacters = Math.random().toString(36).substring(2, 15);
|
|
325
|
-
this.storagePath = `${this.imgStorageSettings.path}/${this.imgStorageSettings.fileName}-${randomCharacters}.webp`;
|
|
313
|
+
this.storagePath = `${this.imgStorageSettings().path}/${this.imgStorageSettings().fileName}-${randomCharacters}.webp`;
|
|
326
314
|
}
|
|
327
315
|
setSettingsForComponent() {
|
|
328
|
-
|
|
316
|
+
const imgStorageSettings = this.imgStorageSettings();
|
|
317
|
+
console.log('setSettingsForComponent', imgStorageSettings);
|
|
329
318
|
// TODO: remove all the imageSettings and keep only imgStorageSettings
|
|
330
|
-
this.aspectRatioValue =
|
|
331
|
-
? AspectRatio[
|
|
319
|
+
this.aspectRatioValue = imgStorageSettings?.cropSettings?.aspectRatio
|
|
320
|
+
? AspectRatio[imgStorageSettings?.cropSettings?.aspectRatio]
|
|
332
321
|
: AspectRatio[AspectType.Square];
|
|
333
|
-
if (
|
|
334
|
-
this.storagePath = `${
|
|
322
|
+
if (imgStorageSettings?.path) {
|
|
323
|
+
this.storagePath = `${imgStorageSettings.path}/${imgStorageSettings.fileName}.webp`;
|
|
335
324
|
}
|
|
336
|
-
else if (
|
|
337
|
-
this.storagePath = `${
|
|
325
|
+
else if (imgStorageSettings.path) {
|
|
326
|
+
this.storagePath = `${imgStorageSettings.path}/${imgStorageSettings.fileName}.webp`;
|
|
338
327
|
}
|
|
339
|
-
if (
|
|
340
|
-
this.resizeToWidth =
|
|
328
|
+
if (imgStorageSettings?.cropSettings?.resizeToWidth) {
|
|
329
|
+
this.resizeToWidth = imgStorageSettings.cropSettings.resizeToWidth;
|
|
341
330
|
}
|
|
342
|
-
else if (
|
|
343
|
-
this.resizeToWidth =
|
|
331
|
+
else if (imgStorageSettings.cropSettings.resizeToWidth) {
|
|
332
|
+
this.resizeToWidth = imgStorageSettings.cropSettings.resizeToWidth;
|
|
344
333
|
}
|
|
345
|
-
if (
|
|
346
|
-
this.renameFile =
|
|
334
|
+
if (imgStorageSettings?.fileName) {
|
|
335
|
+
this.renameFile = imgStorageSettings.fileName;
|
|
347
336
|
}
|
|
348
|
-
else if (
|
|
349
|
-
this.renameFile =
|
|
337
|
+
else if (imgStorageSettings.fileName) {
|
|
338
|
+
this.renameFile = imgStorageSettings.fileName;
|
|
350
339
|
}
|
|
351
340
|
}
|
|
352
341
|
async fileChangeEvent(event) {
|
|
@@ -362,7 +351,7 @@ class CropperComponentModal {
|
|
|
362
351
|
.replace(/[^a-zA-Z0-9]/g, '')
|
|
363
352
|
.slice(0, 80);
|
|
364
353
|
console.log(this.renameFile);
|
|
365
|
-
if (!this.imgStorageSettings.fileName) {
|
|
354
|
+
if (!this.imgStorageSettings().fileName) {
|
|
366
355
|
this.reloadPath();
|
|
367
356
|
}
|
|
368
357
|
this.displayDialog = true;
|
|
@@ -401,32 +390,104 @@ class CropperComponentModal {
|
|
|
401
390
|
this.aspectRatioValue = event.valueRatio;
|
|
402
391
|
this.changeDetectorRef.detectChanges();
|
|
403
392
|
}
|
|
404
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
405
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: CropperComponentModal, isStandalone: true, selector: "dc-cropper-modal", inputs: { imgStorageSettings: "imgStorageSettings", buttonLabel: "buttonLabel", currentStorage: "currentStorage" }, outputs: { imageUploaded: "imageUploaded", onImageCropped: "onImageCropped", onFileSelected: "onFileSelected" }, viewQueries: [{ propertyName: "imageCropper", first: true, predicate: ImageCropperComponent, descendants: true }], ngImport: i0, template: "<div class=\"upload-section\">\n <input type=\"file\" [id]=\"fileInputId\" class=\"file-input\" (change)=\"fileChangeEvent($event)\" />\n <label pButton [for]=\"fileInputId\" [pTooltip]=\"storagePath\" class=\"upload-button\">\n {{ buttonLabel }}\n </label>\n</div>\n@if(displayDialog) {\n<!-- Cropper Dialog -->\n\n<p-dialog header=\"Recortar imagen\" [(visible)]=\"displayDialog\" [modal]=\"true\" [draggable]=\"false\" [resizable]=\"false\" styleClass=\"cropper-dialog\">\n <!-- Image Settings Section -->\n <div class=\"settings-section\">\n @if(!imgStorageSettings.path) {\n <p-message severity=\"warn\">Developer Note: make sure you have a path to save the image pass object imgStorageSettings</p-message>\n } @if(currentStorage.url) {\n\n <p-message severity=\"warn\" variant=\"outlined\">\n <div>\n <span class=\"setting-label\">Image will be replaced:</span>\n <img width=\"100\" height=\"Auto\" [src]=\"currentStorage?.url\" />\n </div>\n </p-message>\n\n }\n\n <p-message>\n <b>Estas opciones estan preconfiguradas y no se pueden cambiar</b>\n\n <ul>\n <li> <b>Path to save:</b> {{ storagePath }}< </li>\n <li>\n <b>Resoluciones:</b>\n <span>{{ imgStorageSettings?.cropSettings?.resolutions }}</span>\n </li>\n </ul>\n </p-message>\n\n <div class=\"setting-item\">\n <span class=\"setting-label\">Aspecto:</span>\n <p class=\"setting-value\">{{ imgStorageSettings?.cropSettings?.aspectRatio }}</p>\n </div>\n\n <p-select\n [options]=\"aspectRatioOptions\"\n [ngModel]=\"ratioSelected\"\n (ngModelChange)=\"changeRatio($event)\"\n optionLabel=\"description\"\n placeholder=\"Select a ratio\" />\n\n <!-- File Metadata Section -->\n <div class=\"metadata-section\" *ngIf=\"fileMetadata\">\n <span class=\"metadata-item\">tipo: {{ fileMetadata.type }}</span>\n <span class=\"metadata-item\">tama\u00F1o: {{ fileMetadata.size }}</span>\n </div>\n\n <!-- Rename Input -->\n <input\n pInputText\n [disabled]=\"imgStorageSettings?.fileName\"\n [(ngModel)]=\"renameFile\"\n type=\"text\"\n placeholder=\"Rename File\"\n (ngModelChange)=\"reloadPath()\"\n class=\"rename-input\" />\n </div>\n\n <!-- Image Cropper -->\n\n <div class=\"cropper-container-father\">\n <image-cropper\n [imageChangedEvent]=\"imageChangedEvent\"\n [maintainAspectRatio]=\"true\"\n [aspectRatio]=\"aspectRatioValue\"\n format=\"webp\"\n [resizeToWidth]=\"resizeToWidth\"\n (imageCropped)=\"onInnerImageCropped($event)\"\n (loadImageFailed)=\"loadImageFailed()\"\n (cropperReady)=\"cropperReady()\"\n [autoCrop]=\"false\">\n </image-cropper>\n </div>\n <!-- Dialog Footer -->\n <ng-template pTemplate=\"footer\">\n <div class=\"dialog-footer\">\n <button pButton class=\"p-button-primary\" (click)=\"simpleCropAndUpload()\"> Recortar y Subir </button>\n </div>\n </ng-template>\n</p-dialog>\n}\n", styles: [":host{display:block}:host ::ng-deep .ngx-ic-overlay{width:100%!important}.upload-section{margin-bottom:1rem}.upload-section .file-input{display:none}.upload-section .upload-button{cursor:pointer}::ng-deep .cropper-dialog{max-width:90vw;width:800px}::ng-deep .cropper-dialog .p-dialog-content{padding:1.5rem}.settings-section{margin-bottom:1.5rem}.settings-section .settings-header{color:var(--text-color-secondary);margin-bottom:1rem}.settings-section .settings-grid{display:grid;gap:1rem;margin-bottom:1.5rem}.settings-section .settings-grid .setting-item .setting-label{font-weight:600;color:var(--text-color);display:block;margin-bottom:.5rem}.settings-section .settings-grid .setting-item .setting-value{color:var(--text-color-secondary);margin:0}.metadata-section{display:flex;gap:1.5rem;margin-bottom:1rem}.metadata-section .metadata-item{color:var(--text-color-secondary)}.rename-section{margin-bottom:5px}.rename-section .rename-input{width:100%;padding:.5rem;border:1px solid var(--surface-border);border-radius:4px}.rename-section .rename-input:disabled{background-color:var(--surface-200);cursor:not-allowed}.cropper-container-father{display:flex;justify-content:center;align-items:center;height:65vh}.dialog-footer{display:flex;justify-content:flex-end;gap:1rem}.btn-crop{cursor:pointer;outline:0;display:inline-block;font-weight:400;line-height:1.5;text-align:center;background-color:transparent;border:1px solid transparent;padding:6px 12px;font-size:1rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;color:#0d6efd;border-color:#0d6efd}.btn-crop :hover{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.file-input{display:none}.btn-upload{cursor:pointer;outline:0;display:inline-block;font-weight:400;line-height:1.5;text-align:center;background-color:transparent;border:1px solid #0d6efd;padding:6px 12px;font-size:1rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;color:#0d6efd}.btn-upload:hover{color:#fff;background-color:#0d6efd}.modal{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background-color:#00000080;z-index:1000}.modal.show-modal{display:flex;align-items:center;justify-content:center}.modal .modal-content{background-color:#fff;border-radius:8px;width:90%;max-width:800px;max-height:90vh;overflow-y:auto;position:relative}.modal .modal-header{padding:1rem;border-bottom:1px solid #dee2e6;display:flex;justify-content:space-between;align-items:center}.modal .modal-header h3{margin:0}.modal .modal-header .close-button{background:none;border:none;font-size:1.5rem;cursor:pointer;padding:0;color:#6c757d}.modal .modal-header .close-button:hover{color:#343a40}.modal .modal-body{padding:1rem;height:100vh;display:flex;flex-direction:column}.modal .modal-footer{padding:1rem;border-top:1px solid #dee2e6;display:flex;justify-content:flex-end}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ImageCropperComponent, selector: "image-cropper", inputs: ["imageChangedEvent", "imageURL", "imageBase64", "imageFile", "imageAltText", "options", "cropperFrameAriaLabel", "output", "format", "autoCrop", "cropper", "transform", "maintainAspectRatio", "aspectRatio", "resetCropOnAspectRatioChange", "resizeToWidth", "resizeToHeight", "cropperMinWidth", "cropperMinHeight", "cropperMaxHeight", "cropperMaxWidth", "cropperStaticWidth", "cropperStaticHeight", "canvasRotation", "initialStepSize", "roundCropper", "onlyScaleDown", "imageQuality", "backgroundColor", "containWithinAspectRatio", "hideResizeSquares", "allowMoveImage", "checkImageType", "alignImage", "disabled", "hidden"], outputs: ["imageCropped", "startCropImage", "imageLoaded", "cropperReady", "loadImageFailed", "transformChange", "cropperChange"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i3.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }, { kind: "directive", type: i4.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i5.Dialog, selector: "p-dialog", inputs: ["header", "draggable", "resizable", "positionLeft", "positionTop", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "responsive", "appendTo", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "breakpoint", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "ngmodule", type: MessageModule }, { kind: "component", type: i7.Message, selector: "p-message", inputs: ["severity", "text", "escape", "style", "styleClass", "closable", "icon", "closeIcon", "life", "showTransitionOptions", "hideTransitionOptions", "size", "variant"], outputs: ["onClose"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i8.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "size", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "fluid", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i9.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }] }); }
|
|
393
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: CropperComponentModal, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
394
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: CropperComponentModal, isStandalone: true, selector: "dc-cropper-modal", inputs: { imgStorageSettings: { classPropertyName: "imgStorageSettings", publicName: "imgStorageSettings", isSignal: true, isRequired: false, transformFunction: null }, buttonLabel: { classPropertyName: "buttonLabel", publicName: "buttonLabel", isSignal: true, isRequired: false, transformFunction: null }, currentStorage: { classPropertyName: "currentStorage", publicName: "currentStorage", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { imageUploaded: "imageUploaded", onImageCropped: "onImageCropped", onFileSelected: "onFileSelected" }, viewQueries: [{ propertyName: "imageCropper", first: true, predicate: ImageCropperComponent, descendants: true }], ngImport: i0, template: "<div class=\"upload-section\">\n <input type=\"file\" [id]=\"fileInputId\" class=\"file-input\" (change)=\"fileChangeEvent($event)\" />\n <label pButton [for]=\"fileInputId\" [pTooltip]=\"storagePath\" class=\"upload-button\">\n {{ buttonLabel() }}\n </label>\n</div>\n@if(displayDialog) {\n <!-- Cropper Dialog -->\n\n <p-dialog header=\"Recortar imagen\" [(visible)]=\"displayDialog\" [modal]=\"true\" [draggable]=\"false\" [resizable]=\"false\" styleClass=\"cropper-dialog\">\n <!-- Image Settings Section -->\n <div class=\"settings-section\">\n @if(!imgStorageSettings().path) {\n <p-message severity=\"warn\">Developer Note: make sure you have a path to save the image pass object imgStorageSettings</p-message>\n } @if(currentStorage.url) {\n\n <p-message severity=\"warn\" variant=\"outlined\">\n <div>\n <span class=\"setting-label\">Image will be replaced:</span>\n <img width=\"100\" height=\"Auto\" [src]=\"currentStorage?.url\" />\n </div>\n </p-message>\n\n }\n\n <p-message>\n <b>Estas opciones estan preconfiguradas y no se pueden cambiar</b>\n\n <ul>\n <li> <b>Path to save:</b> {{ storagePath }}< </li>\n <li>\n <b>Resoluciones:</b>\n <span>{{ imgStorageSettings()?.cropSettings?.resolutions }}</span>\n </li>\n </ul>\n </p-message>\n\n <div class=\"setting-item\">\n <span class=\"setting-label\">Aspecto:</span>\n <p class=\"setting-value\">{{ imgStorageSettings()?.cropSettings?.aspectRatio }}</p>\n </div>\n\n <p-select\n [options]=\"aspectRatioOptions\"\n [ngModel]=\"ratioSelected\"\n (ngModelChange)=\"changeRatio($event)\"\n optionLabel=\"description\"\n placeholder=\"Select a ratio\" />\n\n <!-- File Metadata Section -->\n @if (fileMetadata) {\n <div class=\"metadata-section\">\n <span class=\"metadata-item\">tipo: {{ fileMetadata.type }}</span>\n <span class=\"metadata-item\">tama\u00F1o: {{ fileMetadata.size }}</span>\n </div>\n }\n\n <!-- Rename Input -->\n <input\n pInputText\n [disabled]=\"imgStorageSettings()?.fileName\"\n [(ngModel)]=\"renameFile\"\n type=\"text\"\n placeholder=\"Rename File\"\n (ngModelChange)=\"reloadPath()\"\n class=\"rename-input\" />\n </div>\n\n <!-- Image Cropper -->\n\n <div class=\"cropper-container-father\">\n <image-cropper\n [imageChangedEvent]=\"imageChangedEvent\"\n [maintainAspectRatio]=\"true\"\n [aspectRatio]=\"aspectRatioValue\"\n format=\"webp\"\n [resizeToWidth]=\"resizeToWidth\"\n (imageCropped)=\"onInnerImageCropped($event)\"\n (loadImageFailed)=\"loadImageFailed()\"\n (cropperReady)=\"cropperReady()\"\n [autoCrop]=\"false\">\n </image-cropper>\n </div>\n <!-- Dialog Footer -->\n <ng-template pTemplate=\"footer\">\n <div class=\"dialog-footer\">\n <button pButton class=\"p-button-primary\" (click)=\"simpleCropAndUpload()\"> Recortar y Subir </button>\n </div>\n </ng-template>\n </p-dialog>\n}\n", styles: [":host{display:block}:host ::ng-deep .ngx-ic-overlay{width:100%!important}.upload-section{margin-bottom:1rem}.upload-section .file-input{display:none}.upload-section .upload-button{cursor:pointer}::ng-deep .cropper-dialog{max-width:90vw;width:800px}::ng-deep .cropper-dialog .p-dialog-content{padding:1.5rem}.settings-section{margin-bottom:1.5rem}.settings-section .settings-header{color:var(--text-color-secondary);margin-bottom:1rem}.settings-section .settings-grid{display:grid;gap:1rem;margin-bottom:1.5rem}.settings-section .settings-grid .setting-item .setting-label{font-weight:600;color:var(--text-color);display:block;margin-bottom:.5rem}.settings-section .settings-grid .setting-item .setting-value{color:var(--text-color-secondary);margin:0}.metadata-section{display:flex;gap:1.5rem;margin-bottom:1rem}.metadata-section .metadata-item{color:var(--text-color-secondary)}.rename-section{margin-bottom:5px}.rename-section .rename-input{width:100%;padding:.5rem;border:1px solid var(--surface-border);border-radius:4px}.rename-section .rename-input:disabled{background-color:var(--surface-200);cursor:not-allowed}.cropper-container-father{display:flex;justify-content:center;align-items:center;height:65vh}.dialog-footer{display:flex;justify-content:flex-end;gap:1rem}.btn-crop{cursor:pointer;outline:0;display:inline-block;font-weight:400;line-height:1.5;text-align:center;background-color:transparent;border:1px solid transparent;padding:6px 12px;font-size:1rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;color:#0d6efd;border-color:#0d6efd}.btn-crop :hover{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.file-input{display:none}.btn-upload{cursor:pointer;outline:0;display:inline-block;font-weight:400;line-height:1.5;text-align:center;background-color:transparent;border:1px solid #0d6efd;padding:6px 12px;font-size:1rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;color:#0d6efd}.btn-upload:hover{color:#fff;background-color:#0d6efd}.modal{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background-color:#00000080;z-index:1000}.modal.show-modal{display:flex;align-items:center;justify-content:center}.modal .modal-content{background-color:#fff;border-radius:8px;width:90%;max-width:800px;max-height:90vh;overflow-y:auto;position:relative}.modal .modal-header{padding:1rem;border-bottom:1px solid #dee2e6;display:flex;justify-content:space-between;align-items:center}.modal .modal-header h3{margin:0}.modal .modal-header .close-button{background:none;border:none;font-size:1.5rem;cursor:pointer;padding:0;color:#6c757d}.modal .modal-header .close-button:hover{color:#343a40}.modal .modal-body{padding:1rem;height:100vh;display:flex;flex-direction:column}.modal .modal-footer{padding:1rem;border-top:1px solid #dee2e6;display:flex;justify-content:flex-end}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ImageCropperComponent, selector: "image-cropper", inputs: ["imageChangedEvent", "imageURL", "imageBase64", "imageFile", "imageAltText", "options", "cropperFrameAriaLabel", "output", "format", "autoCrop", "cropper", "transform", "maintainAspectRatio", "aspectRatio", "resetCropOnAspectRatioChange", "resizeToWidth", "resizeToHeight", "cropperMinWidth", "cropperMinHeight", "cropperMaxHeight", "cropperMaxWidth", "cropperStaticWidth", "cropperStaticHeight", "canvasRotation", "initialStepSize", "roundCropper", "onlyScaleDown", "imageQuality", "backgroundColor", "containWithinAspectRatio", "hideResizeSquares", "allowMoveImage", "checkImageType", "alignImage", "disabled", "hidden"], outputs: ["imageCropped", "startCropImage", "imageLoaded", "cropperReady", "loadImageFailed", "transformChange", "cropperChange"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }, { kind: "directive", type: i3.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i4.Dialog, selector: "p-dialog", inputs: ["header", "draggable", "resizable", "positionLeft", "positionTop", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "responsive", "appendTo", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "breakpoint", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i5.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "ngmodule", type: MessageModule }, { kind: "component", type: i6.Message, selector: "p-message", inputs: ["severity", "text", "escape", "style", "styleClass", "closable", "icon", "closeIcon", "life", "showTransitionOptions", "hideTransitionOptions", "size", "variant"], outputs: ["onClose"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i7.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "size", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "fluid", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i8.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }] }); }
|
|
406
395
|
}
|
|
407
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
396
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: CropperComponentModal, decorators: [{
|
|
408
397
|
type: Component,
|
|
409
|
-
args: [{ selector: 'dc-cropper-modal', standalone: true, imports: [
|
|
410
|
-
}], ctorParameters: () => [
|
|
411
|
-
type: Input
|
|
412
|
-
}], buttonLabel: [{
|
|
398
|
+
args: [{ selector: 'dc-cropper-modal', standalone: true, imports: [FormsModule, ImageCropperComponent, ButtonModule, DialogModule, TooltipModule, MessageModule, SelectModule, InputTextModule], template: "<div class=\"upload-section\">\n <input type=\"file\" [id]=\"fileInputId\" class=\"file-input\" (change)=\"fileChangeEvent($event)\" />\n <label pButton [for]=\"fileInputId\" [pTooltip]=\"storagePath\" class=\"upload-button\">\n {{ buttonLabel() }}\n </label>\n</div>\n@if(displayDialog) {\n <!-- Cropper Dialog -->\n\n <p-dialog header=\"Recortar imagen\" [(visible)]=\"displayDialog\" [modal]=\"true\" [draggable]=\"false\" [resizable]=\"false\" styleClass=\"cropper-dialog\">\n <!-- Image Settings Section -->\n <div class=\"settings-section\">\n @if(!imgStorageSettings().path) {\n <p-message severity=\"warn\">Developer Note: make sure you have a path to save the image pass object imgStorageSettings</p-message>\n } @if(currentStorage.url) {\n\n <p-message severity=\"warn\" variant=\"outlined\">\n <div>\n <span class=\"setting-label\">Image will be replaced:</span>\n <img width=\"100\" height=\"Auto\" [src]=\"currentStorage?.url\" />\n </div>\n </p-message>\n\n }\n\n <p-message>\n <b>Estas opciones estan preconfiguradas y no se pueden cambiar</b>\n\n <ul>\n <li> <b>Path to save:</b> {{ storagePath }}< </li>\n <li>\n <b>Resoluciones:</b>\n <span>{{ imgStorageSettings()?.cropSettings?.resolutions }}</span>\n </li>\n </ul>\n </p-message>\n\n <div class=\"setting-item\">\n <span class=\"setting-label\">Aspecto:</span>\n <p class=\"setting-value\">{{ imgStorageSettings()?.cropSettings?.aspectRatio }}</p>\n </div>\n\n <p-select\n [options]=\"aspectRatioOptions\"\n [ngModel]=\"ratioSelected\"\n (ngModelChange)=\"changeRatio($event)\"\n optionLabel=\"description\"\n placeholder=\"Select a ratio\" />\n\n <!-- File Metadata Section -->\n @if (fileMetadata) {\n <div class=\"metadata-section\">\n <span class=\"metadata-item\">tipo: {{ fileMetadata.type }}</span>\n <span class=\"metadata-item\">tama\u00F1o: {{ fileMetadata.size }}</span>\n </div>\n }\n\n <!-- Rename Input -->\n <input\n pInputText\n [disabled]=\"imgStorageSettings()?.fileName\"\n [(ngModel)]=\"renameFile\"\n type=\"text\"\n placeholder=\"Rename File\"\n (ngModelChange)=\"reloadPath()\"\n class=\"rename-input\" />\n </div>\n\n <!-- Image Cropper -->\n\n <div class=\"cropper-container-father\">\n <image-cropper\n [imageChangedEvent]=\"imageChangedEvent\"\n [maintainAspectRatio]=\"true\"\n [aspectRatio]=\"aspectRatioValue\"\n format=\"webp\"\n [resizeToWidth]=\"resizeToWidth\"\n (imageCropped)=\"onInnerImageCropped($event)\"\n (loadImageFailed)=\"loadImageFailed()\"\n (cropperReady)=\"cropperReady()\"\n [autoCrop]=\"false\">\n </image-cropper>\n </div>\n <!-- Dialog Footer -->\n <ng-template pTemplate=\"footer\">\n <div class=\"dialog-footer\">\n <button pButton class=\"p-button-primary\" (click)=\"simpleCropAndUpload()\"> Recortar y Subir </button>\n </div>\n </ng-template>\n </p-dialog>\n}\n", styles: [":host{display:block}:host ::ng-deep .ngx-ic-overlay{width:100%!important}.upload-section{margin-bottom:1rem}.upload-section .file-input{display:none}.upload-section .upload-button{cursor:pointer}::ng-deep .cropper-dialog{max-width:90vw;width:800px}::ng-deep .cropper-dialog .p-dialog-content{padding:1.5rem}.settings-section{margin-bottom:1.5rem}.settings-section .settings-header{color:var(--text-color-secondary);margin-bottom:1rem}.settings-section .settings-grid{display:grid;gap:1rem;margin-bottom:1.5rem}.settings-section .settings-grid .setting-item .setting-label{font-weight:600;color:var(--text-color);display:block;margin-bottom:.5rem}.settings-section .settings-grid .setting-item .setting-value{color:var(--text-color-secondary);margin:0}.metadata-section{display:flex;gap:1.5rem;margin-bottom:1rem}.metadata-section .metadata-item{color:var(--text-color-secondary)}.rename-section{margin-bottom:5px}.rename-section .rename-input{width:100%;padding:.5rem;border:1px solid var(--surface-border);border-radius:4px}.rename-section .rename-input:disabled{background-color:var(--surface-200);cursor:not-allowed}.cropper-container-father{display:flex;justify-content:center;align-items:center;height:65vh}.dialog-footer{display:flex;justify-content:flex-end;gap:1rem}.btn-crop{cursor:pointer;outline:0;display:inline-block;font-weight:400;line-height:1.5;text-align:center;background-color:transparent;border:1px solid transparent;padding:6px 12px;font-size:1rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;color:#0d6efd;border-color:#0d6efd}.btn-crop :hover{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.file-input{display:none}.btn-upload{cursor:pointer;outline:0;display:inline-block;font-weight:400;line-height:1.5;text-align:center;background-color:transparent;border:1px solid #0d6efd;padding:6px 12px;font-size:1rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;color:#0d6efd}.btn-upload:hover{color:#fff;background-color:#0d6efd}.modal{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background-color:#00000080;z-index:1000}.modal.show-modal{display:flex;align-items:center;justify-content:center}.modal .modal-content{background-color:#fff;border-radius:8px;width:90%;max-width:800px;max-height:90vh;overflow-y:auto;position:relative}.modal .modal-header{padding:1rem;border-bottom:1px solid #dee2e6;display:flex;justify-content:space-between;align-items:center}.modal .modal-header h3{margin:0}.modal .modal-header .close-button{background:none;border:none;font-size:1.5rem;cursor:pointer;padding:0;color:#6c757d}.modal .modal-header .close-button:hover{color:#343a40}.modal .modal-body{padding:1rem;height:100vh;display:flex;flex-direction:column}.modal .modal-footer{padding:1rem;border-top:1px solid #dee2e6;display:flex;justify-content:flex-end}\n"] }]
|
|
399
|
+
}], ctorParameters: () => [], propDecorators: { currentStorage: [{
|
|
413
400
|
type: Input
|
|
414
|
-
}], currentStorage: [{
|
|
415
|
-
type: Input
|
|
416
|
-
}], imageUploaded: [{
|
|
417
|
-
type: Output
|
|
418
|
-
}], onImageCropped: [{
|
|
419
|
-
type: Output
|
|
420
|
-
}], onFileSelected: [{
|
|
421
|
-
type: Output
|
|
422
401
|
}], imageCropper: [{
|
|
423
402
|
type: ViewChild,
|
|
424
403
|
args: [ImageCropperComponent]
|
|
425
404
|
}] } });
|
|
426
405
|
|
|
406
|
+
class ImageStoragePreviewComponent {
|
|
407
|
+
constructor() {
|
|
408
|
+
this.storage = inject(AngularFireStorage);
|
|
409
|
+
this.imageSelected = output();
|
|
410
|
+
this.images$ = new BehaviorSubject([]);
|
|
411
|
+
this.loading$ = new BehaviorSubject(false);
|
|
412
|
+
this.error$ = new BehaviorSubject(null);
|
|
413
|
+
this.storagePath = '/images/resources';
|
|
414
|
+
this.subscriptions = [];
|
|
415
|
+
this.imgStorageSettings = {
|
|
416
|
+
path: this.storagePath,
|
|
417
|
+
cropSettings: {
|
|
418
|
+
aspectRatio: AspectType.Square,
|
|
419
|
+
resolutions: [ResolutionType.Small],
|
|
420
|
+
},
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
ngOnInit() {
|
|
424
|
+
this.loadImagesFromStorage();
|
|
425
|
+
}
|
|
426
|
+
ngOnDestroy() {
|
|
427
|
+
this.subscriptions.forEach((sub) => sub.unsubscribe());
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Loads images from Firebase Storage at the specified path
|
|
431
|
+
*/
|
|
432
|
+
async loadImagesFromStorage() {
|
|
433
|
+
try {
|
|
434
|
+
this.loading$.next(true);
|
|
435
|
+
this.error$.next(null);
|
|
436
|
+
const storage = getStorage$1();
|
|
437
|
+
const storageRef = ref(storage, this.storagePath);
|
|
438
|
+
const result = await listAll$1(storageRef);
|
|
439
|
+
if (result.items.length === 0) {
|
|
440
|
+
this.images$.next([]);
|
|
441
|
+
this.loading$.next(false);
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
const imagePromises = result.items.map(async (itemRef) => {
|
|
445
|
+
try {
|
|
446
|
+
const url = await getDownloadURL$1(itemRef);
|
|
447
|
+
const image = {
|
|
448
|
+
url,
|
|
449
|
+
fullPath: itemRef.fullPath,
|
|
450
|
+
name: itemRef.name,
|
|
451
|
+
path: this.storagePath,
|
|
452
|
+
};
|
|
453
|
+
return image;
|
|
454
|
+
}
|
|
455
|
+
catch (error) {
|
|
456
|
+
console.error(`Error getting download URL for ${itemRef.fullPath}:`, error);
|
|
457
|
+
return null;
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
const images = (await Promise.all(imagePromises)).filter((img) => img !== null);
|
|
461
|
+
this.images$.next(images);
|
|
462
|
+
}
|
|
463
|
+
catch (error) {
|
|
464
|
+
console.error('Error loading images from storage:', error);
|
|
465
|
+
this.error$.next('Failed to load images from storage. Please try again later.');
|
|
466
|
+
}
|
|
467
|
+
finally {
|
|
468
|
+
this.loading$.next(false);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Refreshes the image list
|
|
473
|
+
*/
|
|
474
|
+
refreshImages() {
|
|
475
|
+
this.loadImagesFromStorage();
|
|
476
|
+
}
|
|
477
|
+
selectImage(image) {
|
|
478
|
+
this.imageSelected.emit(image);
|
|
479
|
+
}
|
|
480
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ImageStoragePreviewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
481
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: ImageStoragePreviewComponent, isStandalone: true, selector: "dc-image-storage-preview", outputs: { imageSelected: "imageSelected" }, ngImport: i0, template: "<div class=\"image-storage-preview-container\">\n <div class=\"header\">\n <h2>Storage Images</h2>\n <button class=\"refresh-btn\" (click)=\"refreshImages()\" [disabled]=\"loading$ | async\">\n @if (!(loading$ | async)) {\n <span>Refresh</span>\n }\n @if (loading$ | async) {\n <span>Loading...</span>\n }\n </button>\n </div>\n\n @if (loading$ | async) {\n <div class=\"loading-container\">\n <div class=\"spinner\"></div>\n <p>Loading images...</p>\n </div>\n }\n\n @if (error$ | async) {\n <div class=\"error-container\">\n <p class=\"error-message\">{{ error$ | async }}</p>\n <button (click)=\"refreshImages()\">Try Again</button>\n </div>\n }\n\n @if (!(loading$ | async) && !(error$ | async)) {\n <div class=\"images-grid\">\n @if ((images$ | async)?.length) {\n @for (image of images$ | async; track image) {\n <div class=\"image-card\">\n <div class=\"image-container\">\n <img [src]=\"image.url\" [alt]=\"image.name || 'Storage image'\" loading=\"lazy\" />\n </div>\n <div class=\"image-info\">\n <p class=\"image-name\" [title]=\"image.name\">{{ image.name }}</p>\n <div class=\"image-actions\">\n <a [href]=\"image.url\" target=\"_blank\" class=\"action-btn\">View</a>\n <button (click)=\"selectImage(image)\" class=\"action-btn\">Select</button>\n </div>\n </div>\n </div>\n }\n } @else {\n <div class=\"no-images\">\n <p>No images found in the storage path: {{ storagePath }}</p>\n </div>\n }\n <dc-cropper-modal [imgStorageSettings]=\"imgStorageSettings\"></dc-cropper-modal>\n </div>\n }\n</div>\n", styles: [".image-storage-preview-container{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Open Sans,Helvetica Neue,sans-serif;padding:20px;max-width:1200px;margin:0 auto}.header{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px}.header h2{margin:0;color:#333}.refresh-btn{background-color:#4285f4;color:#fff;border:none;border-radius:4px;padding:8px 16px;cursor:pointer;font-size:14px;transition:background-color .2s}.refresh-btn:hover{background-color:#3367d6}.refresh-btn:disabled{background-color:#a9a9a9;cursor:not-allowed}.loading-container{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px 0}.spinner{border:4px solid rgba(0,0,0,.1);border-radius:50%;border-top:4px solid #4285f4;width:30px;height:30px;animation:spin 1s linear infinite;margin-bottom:15px}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.error-container{background-color:#ffebee;border:1px solid #ffcdd2;border-radius:4px;padding:16px;margin:20px 0;text-align:center}.error-message{color:#d32f2f;margin-bottom:10px}.error-container button{background-color:#d32f2f;color:#fff;border:none;border-radius:4px;padding:8px 16px;cursor:pointer;font-size:14px}.images-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:20px}.image-card{border-radius:8px;overflow:hidden;box-shadow:0 2px 10px #0000001a;transition:transform .2s,box-shadow .2s;background-color:#fff}.image-card:hover{transform:translateY(-5px);box-shadow:0 5px 15px #00000026}.image-container{height:200px;overflow:hidden;position:relative;background-color:#f5f5f5}.image-container img{width:100%;height:100%;object-fit:cover;transition:transform .3s}.image-card:hover .image-container img{transform:scale(1.05)}.image-info{padding:15px}.image-name{margin:0 0 10px;font-size:14px;font-weight:500;color:#333;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.image-actions{display:flex;justify-content:flex-end}.action-btn{background-color:#4285f4;color:#fff;text-decoration:none;border-radius:4px;padding:6px 12px;font-size:12px;transition:background-color .2s}.action-btn:hover{background-color:#3367d6}.no-images{grid-column:1 / -1;text-align:center;padding:40px 0;color:#757575;background-color:#f5f5f5;border-radius:8px}\n"], dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "component", type: CropperComponentModal, selector: "dc-cropper-modal", inputs: ["imgStorageSettings", "buttonLabel", "currentStorage"], outputs: ["imageUploaded", "onImageCropped", "onFileSelected"] }] }); }
|
|
482
|
+
}
|
|
483
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ImageStoragePreviewComponent, decorators: [{
|
|
484
|
+
type: Component,
|
|
485
|
+
args: [{ selector: 'dc-image-storage-preview', standalone: true, imports: [AsyncPipe, CropperComponentModal], template: "<div class=\"image-storage-preview-container\">\n <div class=\"header\">\n <h2>Storage Images</h2>\n <button class=\"refresh-btn\" (click)=\"refreshImages()\" [disabled]=\"loading$ | async\">\n @if (!(loading$ | async)) {\n <span>Refresh</span>\n }\n @if (loading$ | async) {\n <span>Loading...</span>\n }\n </button>\n </div>\n\n @if (loading$ | async) {\n <div class=\"loading-container\">\n <div class=\"spinner\"></div>\n <p>Loading images...</p>\n </div>\n }\n\n @if (error$ | async) {\n <div class=\"error-container\">\n <p class=\"error-message\">{{ error$ | async }}</p>\n <button (click)=\"refreshImages()\">Try Again</button>\n </div>\n }\n\n @if (!(loading$ | async) && !(error$ | async)) {\n <div class=\"images-grid\">\n @if ((images$ | async)?.length) {\n @for (image of images$ | async; track image) {\n <div class=\"image-card\">\n <div class=\"image-container\">\n <img [src]=\"image.url\" [alt]=\"image.name || 'Storage image'\" loading=\"lazy\" />\n </div>\n <div class=\"image-info\">\n <p class=\"image-name\" [title]=\"image.name\">{{ image.name }}</p>\n <div class=\"image-actions\">\n <a [href]=\"image.url\" target=\"_blank\" class=\"action-btn\">View</a>\n <button (click)=\"selectImage(image)\" class=\"action-btn\">Select</button>\n </div>\n </div>\n </div>\n }\n } @else {\n <div class=\"no-images\">\n <p>No images found in the storage path: {{ storagePath }}</p>\n </div>\n }\n <dc-cropper-modal [imgStorageSettings]=\"imgStorageSettings\"></dc-cropper-modal>\n </div>\n }\n</div>\n", styles: [".image-storage-preview-container{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Open Sans,Helvetica Neue,sans-serif;padding:20px;max-width:1200px;margin:0 auto}.header{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px}.header h2{margin:0;color:#333}.refresh-btn{background-color:#4285f4;color:#fff;border:none;border-radius:4px;padding:8px 16px;cursor:pointer;font-size:14px;transition:background-color .2s}.refresh-btn:hover{background-color:#3367d6}.refresh-btn:disabled{background-color:#a9a9a9;cursor:not-allowed}.loading-container{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px 0}.spinner{border:4px solid rgba(0,0,0,.1);border-radius:50%;border-top:4px solid #4285f4;width:30px;height:30px;animation:spin 1s linear infinite;margin-bottom:15px}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.error-container{background-color:#ffebee;border:1px solid #ffcdd2;border-radius:4px;padding:16px;margin:20px 0;text-align:center}.error-message{color:#d32f2f;margin-bottom:10px}.error-container button{background-color:#d32f2f;color:#fff;border:none;border-radius:4px;padding:8px 16px;cursor:pointer;font-size:14px}.images-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:20px}.image-card{border-radius:8px;overflow:hidden;box-shadow:0 2px 10px #0000001a;transition:transform .2s,box-shadow .2s;background-color:#fff}.image-card:hover{transform:translateY(-5px);box-shadow:0 5px 15px #00000026}.image-container{height:200px;overflow:hidden;position:relative;background-color:#f5f5f5}.image-container img{width:100%;height:100%;object-fit:cover;transition:transform .3s}.image-card:hover .image-container img{transform:scale(1.05)}.image-info{padding:15px}.image-name{margin:0 0 10px;font-size:14px;font-weight:500;color:#333;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.image-actions{display:flex;justify-content:flex-end}.action-btn{background-color:#4285f4;color:#fff;text-decoration:none;border-radius:4px;padding:6px 12px;font-size:12px;transition:background-color .2s}.action-btn:hover{background-color:#3367d6}.no-images{grid-column:1 / -1;text-align:center;padding:40px 0;color:#757575;background-color:#f5f5f5;border-radius:8px}\n"] }]
|
|
486
|
+
}], ctorParameters: () => [] });
|
|
487
|
+
|
|
427
488
|
class DCFilesCacheService {
|
|
428
|
-
constructor(
|
|
429
|
-
this.storage =
|
|
489
|
+
constructor() {
|
|
490
|
+
this.storage = inject(Storage);
|
|
430
491
|
this.files = {};
|
|
431
492
|
}
|
|
432
493
|
async getURLSrcFile(path) {
|
|
@@ -460,15 +521,113 @@ class DCFilesCacheService {
|
|
|
460
521
|
const blob = await this.getBlob(url);
|
|
461
522
|
return URL.createObjectURL(blob);
|
|
462
523
|
}
|
|
463
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
464
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.
|
|
524
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCFilesCacheService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
525
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCFilesCacheService, providedIn: 'root' }); }
|
|
526
|
+
}
|
|
527
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCFilesCacheService, decorators: [{
|
|
528
|
+
type: Injectable,
|
|
529
|
+
args: [{
|
|
530
|
+
providedIn: 'root',
|
|
531
|
+
}]
|
|
532
|
+
}], ctorParameters: () => [] });
|
|
533
|
+
|
|
534
|
+
class MultiObjectStorageService {
|
|
535
|
+
constructor() {
|
|
536
|
+
this.storage = inject(AngularFireStorage);
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Uploads a Blob or File object to a specified path in Firebase Storage.
|
|
540
|
+
* @param objectToUpload The Blob or File to upload.
|
|
541
|
+
* @param path The desired storage path (e.g., 'documents/report.pdf').
|
|
542
|
+
* @returns A promise that resolves with the storage metadata of the uploaded object.
|
|
543
|
+
* @throws Throws an error if the upload fails.
|
|
544
|
+
*/
|
|
545
|
+
async uploadObject(objectToUpload, path) {
|
|
546
|
+
try {
|
|
547
|
+
const refStorage = this.storage.ref(path);
|
|
548
|
+
const task = await refStorage.put(objectToUpload);
|
|
549
|
+
const { fullPath, bucket, name } = task.metadata;
|
|
550
|
+
const url = await lastValueFrom(refStorage.getDownloadURL());
|
|
551
|
+
const storageData = { url, path: fullPath, bucket, name };
|
|
552
|
+
return storageData;
|
|
553
|
+
}
|
|
554
|
+
catch (error) {
|
|
555
|
+
console.error(`Error uploading object to path "${path}": `, error);
|
|
556
|
+
// Re-throw the error to allow calling code to handle it
|
|
557
|
+
throw error;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Deletes all objects within a specified directory path in Firebase Storage.
|
|
562
|
+
* WARNING: Use with extreme caution as this will permanently delete all files in the directory.
|
|
563
|
+
* @param directoryPath The path to the directory to delete (e.g., 'users/userId/files/').
|
|
564
|
+
* @returns A promise that resolves when the deletion attempt is complete.
|
|
565
|
+
*/
|
|
566
|
+
async deleteDirectory(directoryPath) {
|
|
567
|
+
const storage = getStorage();
|
|
568
|
+
const directoryRef = ref(storage, directoryPath);
|
|
569
|
+
try {
|
|
570
|
+
const res = await listAll(directoryRef);
|
|
571
|
+
const deletePromises = [];
|
|
572
|
+
res.items.forEach((itemRef) => {
|
|
573
|
+
console.log(`Deleting object: ${itemRef.fullPath}`);
|
|
574
|
+
deletePromises.push(deleteObject(itemRef));
|
|
575
|
+
});
|
|
576
|
+
// You might want to handle potential errors during individual deletions if needed
|
|
577
|
+
await Promise.all(deletePromises);
|
|
578
|
+
console.log(`Successfully deleted objects in directory: ${directoryPath}`);
|
|
579
|
+
}
|
|
580
|
+
catch (error) {
|
|
581
|
+
console.error(`Error deleting objects in directory "${directoryPath}":`, error);
|
|
582
|
+
// Optionally re-throw or handle the error appropriately
|
|
583
|
+
throw error;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Deletes a single object from Firebase Storage based on its full path.
|
|
588
|
+
* @param objectPath The full path of the object to delete (e.g., 'images/profile.jpg').
|
|
589
|
+
* @returns A promise that resolves when the deletion is complete.
|
|
590
|
+
*/
|
|
591
|
+
async deleteObjectByPath(objectPath) {
|
|
592
|
+
const storageRef = this.storage.ref(objectPath);
|
|
593
|
+
try {
|
|
594
|
+
await lastValueFrom(storageRef.delete());
|
|
595
|
+
console.log(`Object deleted successfully: ${objectPath}`);
|
|
596
|
+
}
|
|
597
|
+
catch (error) {
|
|
598
|
+
console.error(`Error deleting object at path "${objectPath}":`, error);
|
|
599
|
+
// Optionally re-throw or handle the error appropriately
|
|
600
|
+
throw error;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Private helper to get metadata after an upload task completes.
|
|
605
|
+
* @param task The AngularFireUploadTask.
|
|
606
|
+
* @returns A promise resolving with the object's storage metadata.
|
|
607
|
+
*/
|
|
608
|
+
async uploadAndGetObjectMetadata(task) {
|
|
609
|
+
const snap = await task;
|
|
610
|
+
const { fullPath, bucket, name } = snap.metadata;
|
|
611
|
+
const storage = getStorage();
|
|
612
|
+
const storageRef = ref(storage, fullPath);
|
|
613
|
+
const url = await getDownloadURL(storageRef);
|
|
614
|
+
const meta = {
|
|
615
|
+
url,
|
|
616
|
+
path: fullPath, // Use fullPath for the 'path' property
|
|
617
|
+
bucket,
|
|
618
|
+
name,
|
|
619
|
+
};
|
|
620
|
+
return meta;
|
|
621
|
+
}
|
|
622
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MultiObjectStorageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
623
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MultiObjectStorageService, providedIn: 'root' }); }
|
|
465
624
|
}
|
|
466
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
625
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MultiObjectStorageService, decorators: [{
|
|
467
626
|
type: Injectable,
|
|
468
627
|
args: [{
|
|
469
628
|
providedIn: 'root',
|
|
470
629
|
}]
|
|
471
|
-
}]
|
|
630
|
+
}] });
|
|
472
631
|
|
|
473
632
|
/*
|
|
474
633
|
* Public API Surface of storage-uploader
|
|
@@ -478,5 +637,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
|
|
|
478
637
|
* Generated bundle index. Do not edit.
|
|
479
638
|
*/
|
|
480
639
|
|
|
481
|
-
export { AspectRatio, AspectRatio2, AspectRatioOptions, AspectType, CropperComponent, CropperComponentModal, DCFilesCacheService, DEFAULT_SETTINGS, MultiImagesStorageService, ResolutionType };
|
|
640
|
+
export { AspectRatio, AspectRatio2, AspectRatioOptions, AspectType, CropperComponent, CropperComponentModal, DCFilesCacheService, DEFAULT_SETTINGS, ImageStoragePreviewComponent, MultiImagesStorageService, MultiObjectStorageService, ResolutionType };
|
|
482
641
|
//# sourceMappingURL=dataclouder-ngx-cloud-storage.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dataclouder-ngx-cloud-storage.mjs","sources":["../../../../projects/dataclouder/ngx-cloud-storage/src/lib/classes/cropper.classes.ts","../../../../projects/dataclouder/ngx-cloud-storage/src/lib/services/multi-images-storage.service.ts","../../../../projects/dataclouder/ngx-cloud-storage/src/lib/components/cropper/cropper.component.ts","../../../../projects/dataclouder/ngx-cloud-storage/src/lib/components/cropper/cropper.component.html","../../../../projects/dataclouder/ngx-cloud-storage/src/lib/components/cropper-modal/cropper-modal.component.ts","../../../../projects/dataclouder/ngx-cloud-storage/src/lib/components/cropper-modal/cropper-modal.component.html","../../../../projects/dataclouder/ngx-cloud-storage/src/lib/services/dc-files-cache.service.ts","../../../../projects/dataclouder/ngx-cloud-storage/src/public-api.ts","../../../../projects/dataclouder/ngx-cloud-storage/src/dataclouder-ngx-cloud-storage.ts"],"sourcesContent":["export interface StorageImageSettings {\n path?: string;\n fileName?: string;\n cropSettings?: ImageCropSettings;\n}\n\nexport interface ImageCropSettings {\n resizeToWidth?: number;\n resolutions: Array<number>; // 400, 800, 1200, 1600\n aspectRatio: AspectType;\n}\n\nexport interface CropImageSettings {\n // Resolutions should be here.\n path: string;\n fileName?: string;\n resizeToWidth?: number;\n}\n\nexport interface ImageMultipleCrops {\n file: File;\n defaultImageBlob?: Blob;\n imagesBlobs: { [resolution: number]: Blob };\n settings: { renameFile: string; width?: number };\n}\n\nexport const AspectRatioOptions: AspectRatioOption[] = [\n { value: '1:1', label: 'square', description: 'Square (1:1)', valueRatio: 1 / 1 },\n { value: '1:2', label: 'vertical_1_2', description: 'Vertical (1:2)', valueRatio: 1 / 2 },\n { value: '2:3', label: 'vertical_2_3', description: 'Vertical (2:3)', valueRatio: 2 / 3 },\n { value: '3:4', label: 'vertical_3_4', description: 'Vertical (3:4)', valueRatio: 3 / 4 },\n { value: '4:5', label: 'vertical_4_5', description: 'Vertical (4:5)', valueRatio: 4 / 5 },\n { value: '9:16', label: 'vertical_9_16', description: 'Vertical (9:16)', valueRatio: 9 / 16 },\n { value: '2:1', label: 'horizontal_2_1', description: 'Horizontal (2:1)', valueRatio: 2 / 1 },\n { value: '3:2', label: 'horizontal_3_2', description: 'Horizontal (3:2)', valueRatio: 3 / 2 },\n { value: '4:3', label: 'horizontal_4_3', description: 'Horizontal (4:3)', valueRatio: 4 / 3 },\n { value: '5:4', label: 'horizontal_5_4', description: 'Horizontal (5:4)', valueRatio: 5 / 4 },\n { value: '16:9', label: 'horizontal_16_9', description: 'Horizontal (16:9)', valueRatio: 16 / 9 },\n];\n\nexport interface AspectRatioOption {\n label: string;\n description: string;\n value: string;\n valueRatio: number;\n}\n\nexport enum AspectType {\n Square = 'square',\n Rectangle = 'rectangle',\n Banner = 'banner',\n RectangleLarge = 'rectangleLarge',\n Vertical_9_16 = 'vertical_9_16',\n Vertical_3_5 = 'vertical_3_5',\n Vertical_2_3 = 'vertical_2_3',\n}\n\n// Creo que seria bueno refactorizar para solo enviar aspect ratio asi\nexport enum AspectRatio2 {\n // Vertical\n '9:16' = 16 / 9,\n '3:5' = 3 / 5,\n '5:8' = 5 / 8,\n '2:3' = 2 / 3,\n '1:1' = 1 / 1,\n}\n\nexport enum ResolutionType {\n Small = 200,\n Medium = 400,\n MediumLarge = 800,\n Large = 1200,\n VeryLarge = 1600,\n}\n\nexport const AspectRatio = {\n [AspectType.Square]: 1 / 1,\n [AspectType.Rectangle]: 16 / 9,\n [AspectType.Vertical_9_16]: 9 / 16,\n [AspectType.RectangleLarge]: 16 / 8,\n [AspectType.Banner]: 16 / 7,\n [AspectType.Vertical_3_5]: 3 / 5,\n [AspectType.Vertical_2_3]: 2 / 3,\n};\n\nexport interface ImgStorageData extends CloudStorageData {\n name?: string;\n bucket?: string;\n url?: string;\n path?: string; // path where the file is in the storage\n fullPath?: string; // path + name\n resolution?: string;\n resolutions?: any;\n}\n\nexport interface CloudStorageData {\n bucket?: string;\n url?: string;\n path?: string; // path where the file is in the storage\n}\n\nexport const DEFAULT_SETTINGS: StorageImageSettings = {\n path: '/default-collection/id/please-change-this-subcollection',\n fileName: 'image',\n cropSettings: {\n aspectRatio: AspectType.Square,\n resolutions: [ResolutionType.Small, ResolutionType.MediumLarge],\n resizeToWidth: 450,\n },\n};\n","import { Injectable } from '@angular/core';\nimport { AngularFireStorage, AngularFireUploadTask } from '@angular/fire/compat/storage';\nimport { getDownloadURL, getStorage, deleteObject, listAll } from '@angular/fire/storage';\n\nimport { ref } from 'firebase/storage';\nimport { lastValueFrom } from 'rxjs';\nimport { ImageMultipleCrops, ImgStorageData } from '../classes/cropper.classes';\n\n@Injectable({\n providedIn: 'root',\n})\nexport class MultiImagesStorageService {\n constructor(private storage: AngularFireStorage) {}\n\n public async uploadImage(image: Blob, path: string): Promise<ImgStorageData> {\n try {\n const refStorage = this.storage.ref(path);\n const task = await refStorage.put(image);\n const { fullPath, bucket } = task.metadata;\n const url = await lastValueFrom(refStorage.getDownloadURL());\n const imageStorage = { url, path: fullPath, bucket };\n return imageStorage;\n } catch (error) {\n console.error('uploading image error: ', error);\n return null;\n }\n }\n\n public uploadImage2(imageMulticrops: ImageMultipleCrops, path: string): Promise<ImgStorageData> {\n // path is usually [products/id] will create a new folder with the name and upload the images\n\n const blobs = imageMulticrops.imagesBlobs;\n\n const tasks: Array<Promise<any>> = [];\n let fileName = imageMulticrops.settings?.renameFile ?? imageMulticrops.file.name;\n fileName = fileName.split('.')[0];\n\n // load the default image jpeg\n path = `${path}/${fileName}`;\n const defaultImagePath = `${path}/${fileName}-400W.jpeg`;\n const refStorage = this.storage.ref(defaultImagePath);\n const task = refStorage.put(imageMulticrops.defaultImageBlob);\n\n tasks.push(this.uploadAndGetUrl(task, 'default'));\n\n // Upload webp images\n for (const resolutionStr of Object.keys(blobs)) {\n const fullName = `${fileName}-${resolutionStr}W.webp`;\n const filePath = `${path}/${fullName}`;\n\n const refStorage = this.storage.ref(filePath);\n\n const resolution = Number(resolutionStr);\n const task = refStorage.put(blobs[resolution]);\n tasks.push(this.uploadAndGetUrl(task, resolution + 'W'));\n }\n\n return Promise.all(tasks).then(async (upladedImages) => {\n let defaultImage: any = {};\n let resolutionsUrls: any = {};\n\n for (const image of upladedImages) {\n if (image.resolution === 'default') {\n defaultImage = image;\n } else {\n resolutionsUrls[image.resolution] = image.url;\n }\n }\n defaultImage['resolutions'] = resolutionsUrls;\n defaultImage['path'] = path;\n\n return defaultImage;\n });\n }\n\n public async delete_directory(imagePath: string): Promise<void> {\n // WARNING!! user very carefully could delete whatever folder\n const storage = getStorage();\n const directoryRef = ref(storage, imagePath);\n\n listAll(directoryRef)\n .then((res) => {\n res.items.forEach((itemRef) => {\n console.log(itemRef);\n deleteObject(itemRef);\n });\n })\n .catch((error) => {\n console.error('error al eliminar imagenes de cloud storage', error);\n });\n }\n\n public async deleteImage(imagePath: string): Promise<void> {\n const storageRef = this.storage.ref(imagePath);\n storageRef.delete().subscribe((res) => {\n console.log('image deleted', res);\n });\n }\n\n private async uploadAndGetUrl(task: AngularFireUploadTask, resolution: string): Promise<ImgStorageData> {\n const snap = await task;\n const { fullPath, bucket, name } = snap.metadata;\n const storage = getStorage();\n const storageRef = ref(storage, fullPath);\n const url = await getDownloadURL(storageRef);\n const meta: ImgStorageData = {\n url,\n fullPath,\n bucket,\n name,\n resolution,\n path: '',\n resolutions: {},\n };\n\n return meta;\n }\n}\n","import { Component, Input, OnInit, Output, EventEmitter, ViewChild } from '@angular/core';\nimport { ImageCroppedEvent, ImageCropperComponent, LoadedImage, base64ToFile } from 'ngx-image-cropper';\nimport { Observable } from 'rxjs';\nimport { AspectRatio, AspectType, ImageMultipleCrops, CropImageSettings, ResolutionType } from '../../classes/cropper.classes';\nimport { NgIf } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { MultiImagesStorageService } from '../../services/multi-images-storage.service';\n\n@Component({\n selector: 'app-cropper',\n templateUrl: './cropper.component.html',\n styleUrls: ['./cropper.component.scss'],\n standalone: true,\n imports: [NgIf, FormsModule, ImageCropperComponent],\n})\nexport class CropperComponent implements OnInit {\n // overrides name, path and resizeToWidth\n @Input() imageSettings: CropImageSettings = {} as any;\n\n @Input() ratioType: AspectType | string = AspectType.Square;\n\n @Input() resolutions: Array<number> = [ResolutionType.MediumLarge];\n\n @Output() imageUploaded = new EventEmitter<any>();\n\n @Output() onImageCropped = new EventEmitter<ImageMultipleCrops>();\n\n @Output() onFileSelected = new EventEmitter<any>();\n\n @ViewChild(ImageCropperComponent) imageCropper!: ImageCropperComponent;\n\n public fileMetadata: File | null = null;\n public imageChangedEvent!: Event;\n\n public aspectRatio: number = 1;\n\n public croppedImage: any = '';\n\n public isLoading = false;\n public isUploaded = false;\n public renameFile: any = '';\n public storagePath: string = '';\n public showModal = false;\n\n constructor(private multiImagesStorageService: MultiImagesStorageService) {}\n\n ngOnInit(): void {\n this.aspectRatio = AspectRatio[this.ratioType];\n if (this.imageSettings.path) {\n this.storagePath = `${this.imageSettings.path}/${this.imageSettings.fileName}.webp`;\n }\n }\n\n public reloadPath(): void {\n this.storagePath = `${this.imageSettings.path}/${this.renameFile}.webp`;\n }\n\n async fileChangeEvent(event: any) {\n this.imageChangedEvent = event;\n const file = event?.target?.files[0];\n if (file) {\n this.fileMetadata = file;\n this.onFileSelected.emit(file);\n this.showModal = true; // Show modal when file is selected\n this.renameFile = this.fileMetadata?.name?.split('.')[0];\n console.log(this.renameFile);\n\n if (!this.imageSettings.fileName) {\n this.reloadPath();\n }\n }\n }\n\n onInnerImageCropped(event: ImageCroppedEvent) {\n this.croppedImage = event.base64;\n }\n\n imageLoaded(image: LoadedImage) {\n // show cropper\n }\n\n loadImageFailed() {\n console.error('fallo al cargar la imagen');\n }\n\n public downloadURL!: Observable<string>;\n\n public async simpleCropAndUpload() {\n const imageCropped: any = await this.imageCropper.crop();\n const imgStorage = await this.multiImagesStorageService.uploadImage(imageCropped?.blob, this.storagePath);\n this.imageUploaded.emit(imgStorage);\n this.closeModal();\n }\n\n public async uploadToStorage(imageMulticrops: ImageMultipleCrops): Promise<void> {\n // TODO: Nota si algo falla aquí puede causar inconsistencias en el sistema, ver como manejar errores\n const path = this.imageSettings.path;\n // const imageUploaded = await this.multiImagesStorageService.uploadImage(imageMulticrops, path);\n // this.modalRef.close();\n // console.log(imageUploaded);\n // const image = { type: 'cover', ...imageUploaded };\n\n // TODO: creo que esta parte va en el componente que llama a este componente\n // if (!this.lesson.media) {\n // this.lesson.media = {};\n // this.lesson.media.images = [image];\n // } else {\n // // solo sustituir el cover si ya existe\n // const currentCover = this.lesson.media.images.find((img) => img.type === 'cover');\n // this.multiImagesStorageService.delete_directory(currentCover.path);\n\n // this.lesson.media.images = this.lesson.media.images.filter((img) => img.type !== 'cover');\n\n // this.lesson.media.images.push(image);\n // }\n\n // await this.saveLesson();\n // this.updateCover();\n // this.imageUploaded.emit(image);\n }\n\n closeModal(): void {\n this.showModal = false;\n }\n}\n","<div> path: {{ storagePath }} </div>\n\n<div class=\"options\">\n <div *ngIf=\"!isUploaded\">\n <input type=\"file\" id=\"file-upload\" class=\"file-input\" (change)=\"fileChangeEvent($event)\" />\n <label for=\"file-upload\" class=\"btn-upload\">Seleccionar archivo</label>\n <em *ngIf=\"!fileMetadata\">Carga una imagen para comenzar</em>\n </div>\n\n <span *ngIf=\"fileMetadata\">\n <span style=\"margin: 1px 20px\"> tipo: {{ fileMetadata.type }} </span>\n\n <span style=\"margin: 1px 20px\"> tamaño {{ fileMetadata.size }} </span>\n <br />\n <input\n [disabled]=\"imageSettings?.fileName\"\n style=\"margin: 1px 20px; width: 400px\"\n [(ngModel)]=\"renameFile\"\n type=\"text\"\n placeholder=\"Rename File\"\n (ngModelChange)=\"reloadPath()\" />\n <button class=\"btn-crop\" (click)=\"closeModal()\"> Recortar y Subir </button>\n </span>\n</div>\n\n<div class=\"modal\" *ngIf=\"fileMetadata && !isUploaded\" [class.show-modal]=\"showModal\">\n <div class=\"modal-content\">\n <div class=\"modal-header\">\n <h3>Recortar imagen</h3>\n <button class=\"close-button\" (click)=\"closeModal()\">×</button>\n </div>\n <div class=\"modal-body\">\n <h1>Hlloa</h1>\n\n <image-cropper\n [imageChangedEvent]=\"imageChangedEvent\"\n [maintainAspectRatio]=\"true\"\n [aspectRatio]=\"aspectRatio\"\n format=\"webp\"\n [resizeToWidth]=\"450\"\n (imageCropped)=\"onInnerImageCropped($event)\"\n (loadImageFailed)=\"loadImageFailed()\"\n (imageLoaded)=\"imageLoaded($event)\"\n [autoCrop]=\"false\"></image-cropper>\n\n <div class=\"modal-footer\">\n <button class=\"btn-crop\" (click)=\"simpleCropAndUpload()\">Recortar y Subir</button>\n </div>\n </div>\n </div>\n</div>\n\n<button *ngIf=\"croppedImage && !isUploaded\" [disabled]=\"isLoading\" nbButton status=\"info\"> upload </button>\n","import { Component, Input, OnInit, Output, EventEmitter, ViewChild, ChangeDetectorRef, Attribute } from '@angular/core';\nimport { NgIf } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\n\nimport { DialogModule } from 'primeng/dialog';\n\nimport { ImageCroppedEvent, ImageCropperComponent, LoadedImage } from 'ngx-image-cropper';\nimport { Observable } from 'rxjs';\n\nimport { MultiImagesStorageService } from '../../services/multi-images-storage.service';\nimport {\n AspectRatio,\n AspectType,\n ImageMultipleCrops,\n StorageImageSettings,\n ImgStorageData,\n AspectRatioOptions,\n AspectRatioOption,\n DEFAULT_SETTINGS,\n} from '../../classes/cropper.classes';\nimport { TooltipModule } from 'primeng/tooltip';\n\nimport { ButtonModule } from 'primeng/button';\nimport { MessageModule } from 'primeng/message';\nimport { SelectModule } from 'primeng/select';\nimport { InputTextModule } from 'primeng/inputtext';\n\n@Component({\n selector: 'dc-cropper-modal',\n templateUrl: './cropper-modal.component.html',\n styleUrls: ['./cropper-modal.component.scss'],\n standalone: true,\n imports: [NgIf, FormsModule, ImageCropperComponent, ButtonModule, DialogModule, TooltipModule, MessageModule, SelectModule, InputTextModule],\n})\nexport class CropperComponentModal implements OnInit {\n // overrides name, path and resizeToWidth\n\n @Input() imgStorageSettings: StorageImageSettings = DEFAULT_SETTINGS;\n @Input() buttonLabel: string = 'Seleccionar archivo';\n @Input() currentStorage: ImgStorageData = {} as any;\n\n @Output() imageUploaded = new EventEmitter<ImgStorageData>();\n @Output() onImageCropped = new EventEmitter<ImageMultipleCrops>();\n @Output() onFileSelected = new EventEmitter<any>();\n\n @ViewChild(ImageCropperComponent) imageCropper!: ImageCropperComponent;\n\n public aspectRatioOptions = AspectRatioOptions;\n public fileMetadata: File | null = null;\n public imageChangedEvent!: Event;\n public displayDialog = false;\n public aspectRatioValue: number = 1;\n public croppedImage: any = '';\n public renameFile: any = '';\n public storagePath: string = '';\n public downloadURL!: Observable<string>;\n public resizeToWidth: number = 450;\n public ratioSelected: any = null;\n\n // Add a unique identifier for the file input\n public fileInputId: string;\n\n constructor(private multiImagesStorageService: MultiImagesStorageService, private changeDetectorRef: ChangeDetectorRef) {\n // Generate random ID for file input\n this.fileInputId = `file-upload-${Math.random().toString(36).substring(2, 11)}`;\n }\n\n ngOnInit(): void {\n if (!this.imgStorageSettings.path) {\n console.warn('⚠️ Remember to set imgStorageSettings, path and fileName are required , path example: /collection/id/subcollection ');\n }\n this.reloadPath();\n }\n\n public reloadPath(): void {\n const randomCharacters = Math.random().toString(36).substring(2, 15);\n this.storagePath = `${this.imgStorageSettings.path}/${this.imgStorageSettings.fileName}-${randomCharacters}.webp`;\n }\n\n private setSettingsForComponent(): void {\n console.log('setSettingsForComponent', this.imgStorageSettings);\n // TODO: remove all the imageSettings and keep only imgStorageSettings\n\n this.aspectRatioValue = this.imgStorageSettings?.cropSettings?.aspectRatio\n ? AspectRatio[this.imgStorageSettings?.cropSettings?.aspectRatio]\n : AspectRatio[AspectType.Square];\n\n if (this.imgStorageSettings?.path) {\n this.storagePath = `${this.imgStorageSettings.path}/${this.imgStorageSettings.fileName}.webp`;\n } else if (this.imgStorageSettings.path) {\n this.storagePath = `${this.imgStorageSettings.path}/${this.imgStorageSettings.fileName}.webp`;\n }\n if (this.imgStorageSettings?.cropSettings?.resizeToWidth) {\n this.resizeToWidth = this.imgStorageSettings.cropSettings.resizeToWidth;\n } else if (this.imgStorageSettings.cropSettings.resizeToWidth) {\n this.resizeToWidth = this.imgStorageSettings.cropSettings.resizeToWidth;\n }\n if (this.imgStorageSettings?.fileName) {\n this.renameFile = this.imgStorageSettings.fileName;\n } else if (this.imgStorageSettings.fileName) {\n this.renameFile = this.imgStorageSettings.fileName;\n }\n }\n\n async fileChangeEvent(event: any) {\n this.setSettingsForComponent();\n\n console.log(this.fileInputId);\n\n this.imageChangedEvent = event;\n const file = event?.target?.files[0];\n if (file) {\n this.fileMetadata = file;\n this.onFileSelected.emit(file);\n this.renameFile = this.fileMetadata?.name\n ?.split('.')[0]\n .replace(/[^a-zA-Z0-9]/g, '')\n .slice(0, 80);\n\n console.log(this.renameFile);\n\n if (!this.imgStorageSettings.fileName) {\n this.reloadPath();\n }\n this.displayDialog = true;\n this.changeDetectorRef.detectChanges();\n }\n }\n\n onInnerImageCropped(event: ImageCroppedEvent) {\n this.croppedImage = event.base64;\n }\n\n imageLoaded(image: LoadedImage) {\n this.changeDetectorRef.detectChanges();\n }\n\n cropperReady() {\n this.changeDetectorRef.detectChanges();\n }\n\n loadImageFailed() {\n console.error('fallo al cargar la imagen');\n }\n\n public async simpleCropAndUpload() {\n console.log(this.fileInputId);\n\n console.log('simpleCropAndUpload');\n const imageCropped: any = await this.imageCropper.crop();\n const imgStorage = await this.multiImagesStorageService.uploadImage(imageCropped?.blob, this.storagePath);\n if (this.currentStorage?.path) {\n console.warn('deleting current Image', this.currentStorage?.path);\n this.multiImagesStorageService.deleteImage(this.currentStorage.path);\n }\n console.log('imgStorage', imgStorage);\n this.imageUploaded.emit(imgStorage);\n this.displayDialog = false;\n this.changeDetectorRef.detectChanges();\n }\n\n public changeRatio(event: AspectRatioOption) {\n console.log('changeRatio', event);\n // this.imgStorageSettings.cropSettings.aspectRatio = event.valueRatio;\n this.aspectRatioValue = event.valueRatio;\n this.changeDetectorRef.detectChanges();\n }\n}\n","<div class=\"upload-section\">\n <input type=\"file\" [id]=\"fileInputId\" class=\"file-input\" (change)=\"fileChangeEvent($event)\" />\n <label pButton [for]=\"fileInputId\" [pTooltip]=\"storagePath\" class=\"upload-button\">\n {{ buttonLabel }}\n </label>\n</div>\n@if(displayDialog) {\n<!-- Cropper Dialog -->\n\n<p-dialog header=\"Recortar imagen\" [(visible)]=\"displayDialog\" [modal]=\"true\" [draggable]=\"false\" [resizable]=\"false\" styleClass=\"cropper-dialog\">\n <!-- Image Settings Section -->\n <div class=\"settings-section\">\n @if(!imgStorageSettings.path) {\n <p-message severity=\"warn\">Developer Note: make sure you have a path to save the image pass object imgStorageSettings</p-message>\n } @if(currentStorage.url) {\n\n <p-message severity=\"warn\" variant=\"outlined\">\n <div>\n <span class=\"setting-label\">Image will be replaced:</span>\n <img width=\"100\" height=\"Auto\" [src]=\"currentStorage?.url\" />\n </div>\n </p-message>\n\n }\n\n <p-message>\n <b>Estas opciones estan preconfiguradas y no se pueden cambiar</b>\n\n <ul>\n <li> <b>Path to save:</b> {{ storagePath }}< </li>\n <li>\n <b>Resoluciones:</b>\n <span>{{ imgStorageSettings?.cropSettings?.resolutions }}</span>\n </li>\n </ul>\n </p-message>\n\n <div class=\"setting-item\">\n <span class=\"setting-label\">Aspecto:</span>\n <p class=\"setting-value\">{{ imgStorageSettings?.cropSettings?.aspectRatio }}</p>\n </div>\n\n <p-select\n [options]=\"aspectRatioOptions\"\n [ngModel]=\"ratioSelected\"\n (ngModelChange)=\"changeRatio($event)\"\n optionLabel=\"description\"\n placeholder=\"Select a ratio\" />\n\n <!-- File Metadata Section -->\n <div class=\"metadata-section\" *ngIf=\"fileMetadata\">\n <span class=\"metadata-item\">tipo: {{ fileMetadata.type }}</span>\n <span class=\"metadata-item\">tamaño: {{ fileMetadata.size }}</span>\n </div>\n\n <!-- Rename Input -->\n <input\n pInputText\n [disabled]=\"imgStorageSettings?.fileName\"\n [(ngModel)]=\"renameFile\"\n type=\"text\"\n placeholder=\"Rename File\"\n (ngModelChange)=\"reloadPath()\"\n class=\"rename-input\" />\n </div>\n\n <!-- Image Cropper -->\n\n <div class=\"cropper-container-father\">\n <image-cropper\n [imageChangedEvent]=\"imageChangedEvent\"\n [maintainAspectRatio]=\"true\"\n [aspectRatio]=\"aspectRatioValue\"\n format=\"webp\"\n [resizeToWidth]=\"resizeToWidth\"\n (imageCropped)=\"onInnerImageCropped($event)\"\n (loadImageFailed)=\"loadImageFailed()\"\n (cropperReady)=\"cropperReady()\"\n [autoCrop]=\"false\">\n </image-cropper>\n </div>\n <!-- Dialog Footer -->\n <ng-template pTemplate=\"footer\">\n <div class=\"dialog-footer\">\n <button pButton class=\"p-button-primary\" (click)=\"simpleCropAndUpload()\"> Recortar y Subir </button>\n </div>\n </ng-template>\n</p-dialog>\n}\n","import { Injectable } from '@angular/core';\nimport { getDownloadURL, ref, Storage } from '@angular/fire/storage';\n\n@Injectable({\n providedIn: 'root',\n})\nexport class DCFilesCacheService {\n constructor(private storage: Storage) {}\n\n public files: { [key: string]: string } = {};\n\n public async getURLSrcFile(path: string): Promise<string> {\n if (path in this.files) {\n return this.files[path];\n } else {\n const url = await getDownloadURL(ref(this.storage, path));\n const localUrl = await this.donwloadFileAndGetLocalURL(url);\n this.files[path] = localUrl;\n return localUrl;\n }\n }\n\n public getBlob(url: string): Promise<Blob> {\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.responseType = 'blob';\n xhr.overrideMimeType('audio/mp3');\n\n xhr.onload = (event) => {\n var blob = xhr.response;\n resolve(blob);\n };\n xhr.onerror = (event) => {\n reject(event);\n };\n\n xhr.open('GET', url);\n xhr.send();\n });\n }\n\n public async donwloadFileAndGetLocalURL(url: string) {\n const blob = await this.getBlob(url);\n\n return URL.createObjectURL(blob);\n }\n}\n","/*\n * Public API Surface of storage-uploader\n */\n\nexport * from './lib/components/cropper/cropper.component';\nexport * from './lib/components/cropper-modal/cropper-modal.component';\nexport * from './lib/classes/cropper.classes';\n\nexport * from './lib/services/multi-images-storage.service';\nexport * from './lib/services/dc-files-cache.service';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["i1.MultiImagesStorageService","ref","i1"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dataclouder-ngx-cloud-storage.mjs","sources":["../../../../projects/dataclouder/ngx-cloud-storage/src/lib/classes/cropper.classes.ts","../../../../projects/dataclouder/ngx-cloud-storage/src/lib/services/multi-images-storage.service.ts","../../../../projects/dataclouder/ngx-cloud-storage/src/lib/components/cropper/cropper.component.ts","../../../../projects/dataclouder/ngx-cloud-storage/src/lib/components/cropper/cropper.component.html","../../../../projects/dataclouder/ngx-cloud-storage/src/lib/components/cropper-modal/cropper-modal.component.ts","../../../../projects/dataclouder/ngx-cloud-storage/src/lib/components/cropper-modal/cropper-modal.component.html","../../../../projects/dataclouder/ngx-cloud-storage/src/lib/components/image-storage-preview/image-storage-preview.ts","../../../../projects/dataclouder/ngx-cloud-storage/src/lib/components/image-storage-preview/image-storage-preview.html","../../../../projects/dataclouder/ngx-cloud-storage/src/lib/services/dc-files-cache.service.ts","../../../../projects/dataclouder/ngx-cloud-storage/src/lib/services/multi-object-storage.service.ts","../../../../projects/dataclouder/ngx-cloud-storage/src/public-api.ts","../../../../projects/dataclouder/ngx-cloud-storage/src/dataclouder-ngx-cloud-storage.ts"],"sourcesContent":["export interface StorageImageSettings {\n path?: string;\n fileName?: string;\n cropSettings?: ImageCropSettings;\n}\n\nexport interface ImageCropSettings {\n resizeToWidth?: number;\n resolutions: Array<number>; // 400, 800, 1200, 1600\n aspectRatio: AspectType;\n}\n\nexport interface CropImageSettings {\n // Resolutions should be here.\n path: string;\n fileName?: string;\n resizeToWidth?: number;\n}\n\nexport interface ImageMultipleCrops {\n file: File;\n defaultImageBlob?: Blob;\n imagesBlobs: { [resolution: number]: Blob };\n settings: { renameFile: string; width?: number };\n}\n\nexport const AspectRatioOptions: AspectRatioOption[] = [\n { value: '1:1', label: 'square', description: 'Square (1:1)', valueRatio: 1 / 1 },\n { value: '1:2', label: 'vertical_1_2', description: 'Vertical (1:2)', valueRatio: 1 / 2 },\n { value: '2:3', label: 'vertical_2_3', description: 'Vertical (2:3)', valueRatio: 2 / 3 },\n { value: '3:4', label: 'vertical_3_4', description: 'Vertical (3:4)', valueRatio: 3 / 4 },\n { value: '4:5', label: 'vertical_4_5', description: 'Vertical (4:5)', valueRatio: 4 / 5 },\n { value: '9:16', label: 'vertical_9_16', description: 'Vertical (9:16)', valueRatio: 9 / 16 },\n { value: '2:1', label: 'horizontal_2_1', description: 'Horizontal (2:1)', valueRatio: 2 / 1 },\n { value: '3:2', label: 'horizontal_3_2', description: 'Horizontal (3:2)', valueRatio: 3 / 2 },\n { value: '4:3', label: 'horizontal_4_3', description: 'Horizontal (4:3)', valueRatio: 4 / 3 },\n { value: '5:4', label: 'horizontal_5_4', description: 'Horizontal (5:4)', valueRatio: 5 / 4 },\n { value: '16:9', label: 'horizontal_16_9', description: 'Horizontal (16:9)', valueRatio: 16 / 9 },\n];\n\nexport interface AspectRatioOption {\n label: string;\n description: string;\n value: string;\n valueRatio: number;\n}\n\nexport enum AspectType {\n Square = 'square',\n Rectangle = 'rectangle',\n Banner = 'banner',\n RectangleLarge = 'rectangleLarge',\n Vertical_9_16 = 'vertical_9_16',\n Vertical_3_5 = 'vertical_3_5',\n Vertical_2_3 = 'vertical_2_3',\n}\n\n// Creo que seria bueno refactorizar para solo enviar aspect ratio asi\nexport enum AspectRatio2 {\n // Vertical\n '9:16' = 16 / 9,\n '3:5' = 3 / 5,\n '5:8' = 5 / 8,\n '2:3' = 2 / 3,\n '1:1' = 1 / 1,\n}\n\nexport enum ResolutionType {\n Small = 200,\n Medium = 400,\n MediumLarge = 800,\n Large = 1200,\n VeryLarge = 1600,\n}\n\nexport const AspectRatio = {\n [AspectType.Square]: 1 / 1,\n [AspectType.Rectangle]: 16 / 9,\n [AspectType.Vertical_9_16]: 9 / 16,\n [AspectType.RectangleLarge]: 16 / 8,\n [AspectType.Banner]: 16 / 7,\n [AspectType.Vertical_3_5]: 3 / 5,\n [AspectType.Vertical_2_3]: 2 / 3,\n};\n\nexport interface ImgStorageData extends CloudStorageData {\n name?: string;\n bucket?: string;\n url?: string;\n path?: string; // path where the file is in the storage\n fullPath?: string; // path + name\n resolution?: string;\n resolutions?: any;\n}\n\nexport interface CloudStorageData {\n bucket?: string;\n url?: string;\n path?: string; // path where the file is in the storage\n}\n\nexport const DEFAULT_SETTINGS: StorageImageSettings = {\n path: '/default-collection/id/please-change-this-subcollection',\n fileName: 'image',\n cropSettings: {\n aspectRatio: AspectType.Square,\n resolutions: [ResolutionType.Small, ResolutionType.MediumLarge],\n resizeToWidth: 450,\n },\n};\n","import { Injectable, inject } from '@angular/core';\nimport { AngularFireStorage, AngularFireUploadTask } from '@angular/fire/compat/storage';\nimport { getDownloadURL, getStorage, deleteObject, listAll } from '@angular/fire/storage';\n\nimport { ref } from 'firebase/storage';\nimport { lastValueFrom } from 'rxjs';\nimport { ImageMultipleCrops, ImgStorageData } from '../classes/cropper.classes';\n\n@Injectable({\n providedIn: 'root',\n})\nexport class MultiImagesStorageService {\n private storage = inject(AngularFireStorage);\n\n public async uploadImage(image: Blob, path: string): Promise<ImgStorageData> {\n try {\n const refStorage = this.storage.ref(path);\n const task = await refStorage.put(image);\n const { fullPath, bucket } = task.metadata;\n const url = await lastValueFrom(refStorage.getDownloadURL());\n const imageStorage = { url, path: fullPath, bucket };\n return imageStorage;\n } catch (error) {\n console.error('uploading image error: ', error);\n return null;\n }\n }\n\n public uploadImage2(imageMulticrops: ImageMultipleCrops, path: string): Promise<ImgStorageData> {\n // path is usually [products/id] will create a new folder with the name and upload the images\n\n const blobs = imageMulticrops.imagesBlobs;\n\n const tasks: Array<Promise<any>> = [];\n let fileName = imageMulticrops.settings?.renameFile ?? imageMulticrops.file.name;\n fileName = fileName.split('.')[0];\n\n // load the default image jpeg\n path = `${path}/${fileName}`;\n const defaultImagePath = `${path}/${fileName}-400W.jpeg`;\n const refStorage = this.storage.ref(defaultImagePath);\n const task = refStorage.put(imageMulticrops.defaultImageBlob);\n\n tasks.push(this.uploadAndGetUrl(task, 'default'));\n\n // Upload webp images\n for (const resolutionStr of Object.keys(blobs)) {\n const fullName = `${fileName}-${resolutionStr}W.webp`;\n const filePath = `${path}/${fullName}`;\n\n const refStorage = this.storage.ref(filePath);\n\n const resolution = Number(resolutionStr);\n const task = refStorage.put(blobs[resolution]);\n tasks.push(this.uploadAndGetUrl(task, resolution + 'W'));\n }\n\n return Promise.all(tasks).then(async (upladedImages) => {\n let defaultImage: any = {};\n let resolutionsUrls: any = {};\n\n for (const image of upladedImages) {\n if (image.resolution === 'default') {\n defaultImage = image;\n } else {\n resolutionsUrls[image.resolution] = image.url;\n }\n }\n defaultImage['resolutions'] = resolutionsUrls;\n defaultImage['path'] = path;\n\n return defaultImage;\n });\n }\n\n public async delete_directory(imagePath: string): Promise<void> {\n // WARNING!! user very carefully could delete whatever folder\n const storage = getStorage();\n const directoryRef = ref(storage, imagePath);\n\n listAll(directoryRef)\n .then((res) => {\n res.items.forEach((itemRef) => {\n console.log(itemRef);\n deleteObject(itemRef);\n });\n })\n .catch((error) => {\n console.error('error al eliminar imagenes de cloud storage', error);\n });\n }\n\n public async deleteImage(imagePath: string): Promise<void> {\n const storageRef = this.storage.ref(imagePath);\n storageRef.delete().subscribe((res) => {\n console.log('image deleted', res);\n });\n }\n\n private async uploadAndGetUrl(task: AngularFireUploadTask, resolution: string): Promise<ImgStorageData> {\n const snap = await task;\n const { fullPath, bucket, name } = snap.metadata;\n const storage = getStorage();\n const storageRef = ref(storage, fullPath);\n const url = await getDownloadURL(storageRef);\n const meta: ImgStorageData = {\n url,\n fullPath,\n bucket,\n name,\n resolution,\n path: '',\n resolutions: {},\n };\n\n return meta;\n }\n}\n","import { Component, OnInit, ViewChild, inject, output, input } from '@angular/core';\nimport { ImageCroppedEvent, ImageCropperComponent, LoadedImage, base64ToFile } from 'ngx-image-cropper';\nimport { Observable } from 'rxjs';\nimport { AspectRatio, AspectType, ImageMultipleCrops, CropImageSettings, ResolutionType } from '../../classes/cropper.classes';\n\nimport { FormsModule } from '@angular/forms';\nimport { MultiImagesStorageService } from '../../services/multi-images-storage.service';\n\n@Component({\n selector: 'app-cropper',\n templateUrl: './cropper.component.html',\n styleUrls: ['./cropper.component.scss'],\n standalone: true,\n imports: [FormsModule, ImageCropperComponent],\n})\nexport class CropperComponent implements OnInit {\n private multiImagesStorageService = inject(MultiImagesStorageService);\n\n // overrides name, path and resizeToWidth\n readonly imageSettings = input<CropImageSettings>({} as any);\n\n readonly ratioType = input<AspectType | string>(AspectType.Square);\n\n readonly resolutions = input<Array<number>>([ResolutionType.MediumLarge]);\n\n readonly imageUploaded = output<any>();\n\n readonly onImageCropped = output<ImageMultipleCrops>();\n\n readonly onFileSelected = output<any>();\n\n @ViewChild(ImageCropperComponent) imageCropper!: ImageCropperComponent;\n\n public fileMetadata: File | null = null;\n public imageChangedEvent!: Event;\n\n public aspectRatio: number = 1;\n\n public croppedImage: any = '';\n\n public isLoading = false;\n public isUploaded = false;\n public renameFile: any = '';\n public storagePath: string = '';\n public showModal = false;\n\n /** Inserted by Angular inject() migration for backwards compatibility */\n constructor(...args: unknown[]);\n\n constructor() {}\n\n ngOnInit(): void {\n this.aspectRatio = AspectRatio[this.ratioType()];\n const imageSettings = this.imageSettings();\n if (imageSettings.path) {\n this.storagePath = `${imageSettings.path}/${imageSettings.fileName}.webp`;\n }\n }\n\n public reloadPath(): void {\n this.storagePath = `${this.imageSettings().path}/${this.renameFile}.webp`;\n }\n\n async fileChangeEvent(event: any) {\n this.imageChangedEvent = event;\n const file = event?.target?.files[0];\n if (file) {\n this.fileMetadata = file;\n this.onFileSelected.emit(file);\n this.showModal = true; // Show modal when file is selected\n this.renameFile = this.fileMetadata?.name?.split('.')[0];\n console.log(this.renameFile);\n\n if (!this.imageSettings().fileName) {\n this.reloadPath();\n }\n }\n }\n\n onInnerImageCropped(event: ImageCroppedEvent) {\n this.croppedImage = event.base64;\n }\n\n imageLoaded(image: LoadedImage) {\n // show cropper\n }\n\n loadImageFailed() {\n console.error('fallo al cargar la imagen');\n }\n\n public downloadURL!: Observable<string>;\n\n public async simpleCropAndUpload() {\n const imageCropped: any = await this.imageCropper.crop();\n const imgStorage = await this.multiImagesStorageService.uploadImage(imageCropped?.blob, this.storagePath);\n this.imageUploaded.emit(imgStorage);\n this.closeModal();\n }\n\n public async uploadToStorage(imageMulticrops: ImageMultipleCrops): Promise<void> {\n // TODO: Nota si algo falla aquí puede causar inconsistencias en el sistema, ver como manejar errores\n const path = this.imageSettings().path;\n // const imageUploaded = await this.multiImagesStorageService.uploadImage(imageMulticrops, path);\n // this.modalRef.close();\n // console.log(imageUploaded);\n // const image = { type: 'cover', ...imageUploaded };\n\n // TODO: creo que esta parte va en el componente que llama a este componente\n // if (!this.lesson.media) {\n // this.lesson.media = {};\n // this.lesson.media.images = [image];\n // } else {\n // // solo sustituir el cover si ya existe\n // const currentCover = this.lesson.media.images.find((img) => img.type === 'cover');\n // this.multiImagesStorageService.delete_directory(currentCover.path);\n\n // this.lesson.media.images = this.lesson.media.images.filter((img) => img.type !== 'cover');\n\n // this.lesson.media.images.push(image);\n // }\n\n // await this.saveLesson();\n // this.updateCover();\n // this.imageUploaded.emit(image);\n }\n\n closeModal(): void {\n this.showModal = false;\n }\n}\n","<div> path: {{ storagePath }} </div>\n\n<div class=\"options\">\n @if (!isUploaded) {\n <div>\n <input type=\"file\" id=\"file-upload\" class=\"file-input\" (change)=\"fileChangeEvent($event)\" />\n <label for=\"file-upload\" class=\"btn-upload\">Seleccionar archivo</label>\n @if (!fileMetadata) {\n <em>Carga una imagen para comenzar</em>\n }\n </div>\n }\n\n @if (fileMetadata) {\n <span>\n <span style=\"margin: 1px 20px\"> tipo: {{ fileMetadata.type }} </span>\n <span style=\"margin: 1px 20px\"> tamaño {{ fileMetadata.size }} </span>\n <br />\n <input\n [disabled]=\"imageSettings()?.fileName\"\n style=\"margin: 1px 20px; width: 400px\"\n [(ngModel)]=\"renameFile\"\n type=\"text\"\n placeholder=\"Rename File\"\n (ngModelChange)=\"reloadPath()\" />\n <button class=\"btn-crop\" (click)=\"closeModal()\"> Recortar y Subir </button>\n </span>\n }\n</div>\n\n@if (fileMetadata && !isUploaded) {\n <div class=\"modal\" [class.show-modal]=\"showModal\">\n <div class=\"modal-content\">\n <div class=\"modal-header\">\n <h3>Recortar imagen</h3>\n <button class=\"close-button\" (click)=\"closeModal()\">×</button>\n </div>\n <div class=\"modal-body\">\n <h1>Hlloa</h1>\n <image-cropper\n [imageChangedEvent]=\"imageChangedEvent\"\n [maintainAspectRatio]=\"true\"\n [aspectRatio]=\"aspectRatio\"\n format=\"webp\"\n [resizeToWidth]=\"450\"\n (imageCropped)=\"onInnerImageCropped($event)\"\n (loadImageFailed)=\"loadImageFailed()\"\n (imageLoaded)=\"imageLoaded($event)\"\n [autoCrop]=\"false\"></image-cropper>\n <div class=\"modal-footer\">\n <button class=\"btn-crop\" (click)=\"simpleCropAndUpload()\">Recortar y Subir</button>\n </div>\n </div>\n </div>\n </div>\n}\n\n@if (croppedImage && !isUploaded) {\n <button [disabled]=\"isLoading\" nbButton status=\"info\"> upload </button>\n}\n","import { Component, Input, OnInit, ViewChild, ChangeDetectorRef, inject, output, input } from '@angular/core';\n\nimport { FormsModule } from '@angular/forms';\n\nimport { DialogModule } from 'primeng/dialog';\n\nimport { ImageCroppedEvent, ImageCropperComponent, LoadedImage } from 'ngx-image-cropper';\nimport { Observable } from 'rxjs';\n\nimport { MultiImagesStorageService } from '../../services/multi-images-storage.service';\nimport {\n AspectRatio,\n AspectType,\n ImageMultipleCrops,\n StorageImageSettings,\n ImgStorageData,\n AspectRatioOptions,\n AspectRatioOption,\n DEFAULT_SETTINGS,\n} from '../../classes/cropper.classes';\nimport { TooltipModule } from 'primeng/tooltip';\n\nimport { ButtonModule } from 'primeng/button';\nimport { MessageModule } from 'primeng/message';\nimport { SelectModule } from 'primeng/select';\nimport { InputTextModule } from 'primeng/inputtext';\n\n@Component({\n selector: 'dc-cropper-modal',\n templateUrl: './cropper-modal.component.html',\n styleUrls: ['./cropper-modal.component.scss'],\n standalone: true,\n imports: [FormsModule, ImageCropperComponent, ButtonModule, DialogModule, TooltipModule, MessageModule, SelectModule, InputTextModule],\n})\nexport class CropperComponentModal implements OnInit {\n private multiImagesStorageService = inject(MultiImagesStorageService);\n private changeDetectorRef = inject(ChangeDetectorRef);\n\n // overrides name, path and resizeToWidth\n\n readonly imgStorageSettings = input<StorageImageSettings>(DEFAULT_SETTINGS);\n readonly buttonLabel = input<string>('Seleccionar archivo');\n @Input() currentStorage: ImgStorageData = {} as any;\n\n readonly imageUploaded = output<ImgStorageData>();\n readonly onImageCropped = output<ImageMultipleCrops>();\n readonly onFileSelected = output<any>();\n\n @ViewChild(ImageCropperComponent) imageCropper!: ImageCropperComponent;\n\n public aspectRatioOptions = AspectRatioOptions;\n public fileMetadata: File | null = null;\n public imageChangedEvent!: Event;\n public displayDialog = false;\n public aspectRatioValue: number = 1;\n public croppedImage: any = '';\n public renameFile: any = '';\n public storagePath: string = '';\n public downloadURL!: Observable<string>;\n public resizeToWidth: number = 450;\n public ratioSelected: any = null;\n\n // Add a unique identifier for the file input\n public fileInputId: string;\n\n /** Inserted by Angular inject() migration for backwards compatibility */\n constructor(...args: unknown[]);\n\n constructor() {\n // Generate random ID for file input\n this.fileInputId = `file-upload-${Math.random().toString(36).substring(2, 11)}`;\n }\n\n ngOnInit(): void {\n if (!this.imgStorageSettings().path) {\n console.warn('⚠️ Remember to set imgStorageSettings, path and fileName are required , path example: /collection/id/subcollection ');\n }\n this.reloadPath();\n }\n\n public reloadPath(): void {\n const randomCharacters = Math.random().toString(36).substring(2, 15);\n this.storagePath = `${this.imgStorageSettings().path}/${this.imgStorageSettings().fileName}-${randomCharacters}.webp`;\n }\n\n private setSettingsForComponent(): void {\n const imgStorageSettings = this.imgStorageSettings();\n console.log('setSettingsForComponent', imgStorageSettings);\n // TODO: remove all the imageSettings and keep only imgStorageSettings\n\n this.aspectRatioValue = imgStorageSettings?.cropSettings?.aspectRatio\n ? AspectRatio[imgStorageSettings?.cropSettings?.aspectRatio]\n : AspectRatio[AspectType.Square];\n\n if (imgStorageSettings?.path) {\n this.storagePath = `${imgStorageSettings.path}/${imgStorageSettings.fileName}.webp`;\n } else if (imgStorageSettings.path) {\n this.storagePath = `${imgStorageSettings.path}/${imgStorageSettings.fileName}.webp`;\n }\n if (imgStorageSettings?.cropSettings?.resizeToWidth) {\n this.resizeToWidth = imgStorageSettings.cropSettings.resizeToWidth;\n } else if (imgStorageSettings.cropSettings.resizeToWidth) {\n this.resizeToWidth = imgStorageSettings.cropSettings.resizeToWidth;\n }\n if (imgStorageSettings?.fileName) {\n this.renameFile = imgStorageSettings.fileName;\n } else if (imgStorageSettings.fileName) {\n this.renameFile = imgStorageSettings.fileName;\n }\n }\n\n async fileChangeEvent(event: any) {\n this.setSettingsForComponent();\n\n console.log(this.fileInputId);\n\n this.imageChangedEvent = event;\n const file = event?.target?.files[0];\n if (file) {\n this.fileMetadata = file;\n this.onFileSelected.emit(file);\n this.renameFile = this.fileMetadata?.name\n ?.split('.')[0]\n .replace(/[^a-zA-Z0-9]/g, '')\n .slice(0, 80);\n\n console.log(this.renameFile);\n\n if (!this.imgStorageSettings().fileName) {\n this.reloadPath();\n }\n this.displayDialog = true;\n this.changeDetectorRef.detectChanges();\n }\n }\n\n onInnerImageCropped(event: ImageCroppedEvent) {\n this.croppedImage = event.base64;\n }\n\n imageLoaded(image: LoadedImage) {\n this.changeDetectorRef.detectChanges();\n }\n\n cropperReady() {\n this.changeDetectorRef.detectChanges();\n }\n\n loadImageFailed() {\n console.error('fallo al cargar la imagen');\n }\n\n public async simpleCropAndUpload() {\n console.log(this.fileInputId);\n\n console.log('simpleCropAndUpload');\n const imageCropped: any = await this.imageCropper.crop();\n const imgStorage = await this.multiImagesStorageService.uploadImage(imageCropped?.blob, this.storagePath);\n if (this.currentStorage?.path) {\n console.warn('deleting current Image', this.currentStorage?.path);\n this.multiImagesStorageService.deleteImage(this.currentStorage.path);\n }\n console.log('imgStorage', imgStorage);\n this.imageUploaded.emit(imgStorage);\n this.displayDialog = false;\n this.changeDetectorRef.detectChanges();\n }\n\n public changeRatio(event: AspectRatioOption) {\n console.log('changeRatio', event);\n // this.imgStorageSettings.cropSettings.aspectRatio = event.valueRatio;\n this.aspectRatioValue = event.valueRatio;\n this.changeDetectorRef.detectChanges();\n }\n}\n","<div class=\"upload-section\">\n <input type=\"file\" [id]=\"fileInputId\" class=\"file-input\" (change)=\"fileChangeEvent($event)\" />\n <label pButton [for]=\"fileInputId\" [pTooltip]=\"storagePath\" class=\"upload-button\">\n {{ buttonLabel() }}\n </label>\n</div>\n@if(displayDialog) {\n <!-- Cropper Dialog -->\n\n <p-dialog header=\"Recortar imagen\" [(visible)]=\"displayDialog\" [modal]=\"true\" [draggable]=\"false\" [resizable]=\"false\" styleClass=\"cropper-dialog\">\n <!-- Image Settings Section -->\n <div class=\"settings-section\">\n @if(!imgStorageSettings().path) {\n <p-message severity=\"warn\">Developer Note: make sure you have a path to save the image pass object imgStorageSettings</p-message>\n } @if(currentStorage.url) {\n\n <p-message severity=\"warn\" variant=\"outlined\">\n <div>\n <span class=\"setting-label\">Image will be replaced:</span>\n <img width=\"100\" height=\"Auto\" [src]=\"currentStorage?.url\" />\n </div>\n </p-message>\n\n }\n\n <p-message>\n <b>Estas opciones estan preconfiguradas y no se pueden cambiar</b>\n\n <ul>\n <li> <b>Path to save:</b> {{ storagePath }}< </li>\n <li>\n <b>Resoluciones:</b>\n <span>{{ imgStorageSettings()?.cropSettings?.resolutions }}</span>\n </li>\n </ul>\n </p-message>\n\n <div class=\"setting-item\">\n <span class=\"setting-label\">Aspecto:</span>\n <p class=\"setting-value\">{{ imgStorageSettings()?.cropSettings?.aspectRatio }}</p>\n </div>\n\n <p-select\n [options]=\"aspectRatioOptions\"\n [ngModel]=\"ratioSelected\"\n (ngModelChange)=\"changeRatio($event)\"\n optionLabel=\"description\"\n placeholder=\"Select a ratio\" />\n\n <!-- File Metadata Section -->\n @if (fileMetadata) {\n <div class=\"metadata-section\">\n <span class=\"metadata-item\">tipo: {{ fileMetadata.type }}</span>\n <span class=\"metadata-item\">tamaño: {{ fileMetadata.size }}</span>\n </div>\n }\n\n <!-- Rename Input -->\n <input\n pInputText\n [disabled]=\"imgStorageSettings()?.fileName\"\n [(ngModel)]=\"renameFile\"\n type=\"text\"\n placeholder=\"Rename File\"\n (ngModelChange)=\"reloadPath()\"\n class=\"rename-input\" />\n </div>\n\n <!-- Image Cropper -->\n\n <div class=\"cropper-container-father\">\n <image-cropper\n [imageChangedEvent]=\"imageChangedEvent\"\n [maintainAspectRatio]=\"true\"\n [aspectRatio]=\"aspectRatioValue\"\n format=\"webp\"\n [resizeToWidth]=\"resizeToWidth\"\n (imageCropped)=\"onInnerImageCropped($event)\"\n (loadImageFailed)=\"loadImageFailed()\"\n (cropperReady)=\"cropperReady()\"\n [autoCrop]=\"false\">\n </image-cropper>\n </div>\n <!-- Dialog Footer -->\n <ng-template pTemplate=\"footer\">\n <div class=\"dialog-footer\">\n <button pButton class=\"p-button-primary\" (click)=\"simpleCropAndUpload()\"> Recortar y Subir </button>\n </div>\n </ng-template>\n </p-dialog>\n}\n","import { Component, OnInit, OnDestroy, inject, output } from '@angular/core';\nimport { AngularFireStorage } from '@angular/fire/compat/storage';\nimport { getStorage, ref, listAll, getDownloadURL } from 'firebase/storage';\nimport { BehaviorSubject, Subscription } from 'rxjs';\nimport { AspectType, ImgStorageData, ResolutionType, StorageImageSettings } from '../../classes/cropper.classes';\nimport { AsyncPipe } from '@angular/common';\nimport { CropperComponentModal } from '../cropper-modal/cropper-modal.component';\n\n@Component({\n selector: 'dc-image-storage-preview',\n templateUrl: './image-storage-preview.html',\n styleUrls: ['./image-storage-preview.css'],\n standalone: true,\n imports: [AsyncPipe, CropperComponentModal],\n})\nexport class ImageStoragePreviewComponent implements OnInit, OnDestroy {\n private storage = inject(AngularFireStorage);\n\n readonly imageSelected = output<ImgStorageData>();\n public images$ = new BehaviorSubject<ImgStorageData[]>([]);\n public loading$ = new BehaviorSubject<boolean>(false);\n public error$ = new BehaviorSubject<string | null>(null);\n\n public readonly storagePath = '/images/resources';\n private subscriptions: Subscription[] = [];\n\n /** Inserted by Angular inject() migration for backwards compatibility */\n constructor(...args: unknown[]);\n\n constructor() {}\n\n ngOnInit(): void {\n this.loadImagesFromStorage();\n }\n\n public imgStorageSettings: StorageImageSettings = {\n path: this.storagePath,\n cropSettings: {\n aspectRatio: AspectType.Square,\n resolutions: [ResolutionType.Small],\n },\n };\n\n ngOnDestroy(): void {\n this.subscriptions.forEach((sub) => sub.unsubscribe());\n }\n\n /**\n * Loads images from Firebase Storage at the specified path\n */\n private async loadImagesFromStorage(): Promise<void> {\n try {\n this.loading$.next(true);\n this.error$.next(null);\n\n const storage = getStorage();\n const storageRef = ref(storage, this.storagePath);\n\n const result = await listAll(storageRef);\n\n if (result.items.length === 0) {\n this.images$.next([]);\n this.loading$.next(false);\n return;\n }\n\n const imagePromises = result.items.map(async (itemRef) => {\n try {\n const url = await getDownloadURL(itemRef);\n const image: ImgStorageData = {\n url,\n fullPath: itemRef.fullPath,\n name: itemRef.name,\n path: this.storagePath,\n };\n return image;\n } catch (error) {\n console.error(`Error getting download URL for ${itemRef.fullPath}:`, error);\n return null;\n }\n });\n\n const images = (await Promise.all(imagePromises)).filter((img) => img !== null) as ImgStorageData[];\n this.images$.next(images);\n } catch (error) {\n console.error('Error loading images from storage:', error);\n this.error$.next('Failed to load images from storage. Please try again later.');\n } finally {\n this.loading$.next(false);\n }\n }\n\n /**\n * Refreshes the image list\n */\n public refreshImages(): void {\n this.loadImagesFromStorage();\n }\n\n public selectImage(image: ImgStorageData): void {\n this.imageSelected.emit(image);\n }\n}\n","<div class=\"image-storage-preview-container\">\n <div class=\"header\">\n <h2>Storage Images</h2>\n <button class=\"refresh-btn\" (click)=\"refreshImages()\" [disabled]=\"loading$ | async\">\n @if (!(loading$ | async)) {\n <span>Refresh</span>\n }\n @if (loading$ | async) {\n <span>Loading...</span>\n }\n </button>\n </div>\n\n @if (loading$ | async) {\n <div class=\"loading-container\">\n <div class=\"spinner\"></div>\n <p>Loading images...</p>\n </div>\n }\n\n @if (error$ | async) {\n <div class=\"error-container\">\n <p class=\"error-message\">{{ error$ | async }}</p>\n <button (click)=\"refreshImages()\">Try Again</button>\n </div>\n }\n\n @if (!(loading$ | async) && !(error$ | async)) {\n <div class=\"images-grid\">\n @if ((images$ | async)?.length) {\n @for (image of images$ | async; track image) {\n <div class=\"image-card\">\n <div class=\"image-container\">\n <img [src]=\"image.url\" [alt]=\"image.name || 'Storage image'\" loading=\"lazy\" />\n </div>\n <div class=\"image-info\">\n <p class=\"image-name\" [title]=\"image.name\">{{ image.name }}</p>\n <div class=\"image-actions\">\n <a [href]=\"image.url\" target=\"_blank\" class=\"action-btn\">View</a>\n <button (click)=\"selectImage(image)\" class=\"action-btn\">Select</button>\n </div>\n </div>\n </div>\n }\n } @else {\n <div class=\"no-images\">\n <p>No images found in the storage path: {{ storagePath }}</p>\n </div>\n }\n <dc-cropper-modal [imgStorageSettings]=\"imgStorageSettings\"></dc-cropper-modal>\n </div>\n }\n</div>\n","import { Injectable, inject } from '@angular/core';\nimport { getDownloadURL, ref, Storage } from '@angular/fire/storage';\n\n@Injectable({\n providedIn: 'root',\n})\nexport class DCFilesCacheService {\n private storage = inject(Storage);\n\n /** Inserted by Angular inject() migration for backwards compatibility */\n constructor(...args: unknown[]);\n\n constructor() {}\n\n public files: { [key: string]: string } = {};\n\n public async getURLSrcFile(path: string): Promise<string> {\n if (path in this.files) {\n return this.files[path];\n } else {\n const url = await getDownloadURL(ref(this.storage, path));\n const localUrl = await this.donwloadFileAndGetLocalURL(url);\n this.files[path] = localUrl;\n return localUrl;\n }\n }\n\n public getBlob(url: string): Promise<Blob> {\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.responseType = 'blob';\n xhr.overrideMimeType('audio/mp3');\n\n xhr.onload = (event) => {\n var blob = xhr.response;\n resolve(blob);\n };\n xhr.onerror = (event) => {\n reject(event);\n };\n\n xhr.open('GET', url);\n xhr.send();\n });\n }\n\n public async donwloadFileAndGetLocalURL(url: string) {\n const blob = await this.getBlob(url);\n\n return URL.createObjectURL(blob);\n }\n}\n","import { Injectable, inject } from '@angular/core';\nimport { AngularFireStorage, AngularFireUploadTask } from '@angular/fire/compat/storage';\nimport { getDownloadURL, getStorage, deleteObject, listAll, StorageReference } from '@angular/fire/storage';\nimport { ref } from 'firebase/storage';\nimport { lastValueFrom } from 'rxjs';\n\n/**\n * Represents the metadata of an object stored in Firebase Storage.\n */\nexport interface ObjectStorageData {\n /** The download URL of the stored object. */\n url: string;\n /** The full path of the object in the storage bucket (e.g., 'folder/subfolder/file.txt'). */\n path: string;\n /** The name of the Firebase Storage bucket where the object is stored. */\n bucket: string;\n /** The name of the object file (e.g., 'file.txt'). */\n name?: string;\n}\n\n@Injectable({\n providedIn: 'root',\n})\nexport class MultiObjectStorageService {\n private storage = inject(AngularFireStorage);\n\n /**\n * Uploads a Blob or File object to a specified path in Firebase Storage.\n * @param objectToUpload The Blob or File to upload.\n * @param path The desired storage path (e.g., 'documents/report.pdf').\n * @returns A promise that resolves with the storage metadata of the uploaded object.\n * @throws Throws an error if the upload fails.\n */\n public async uploadObject(objectToUpload: Blob | File, path: string): Promise<ObjectStorageData> {\n try {\n const refStorage = this.storage.ref(path);\n const task = await refStorage.put(objectToUpload);\n const { fullPath, bucket, name } = task.metadata;\n const url = await lastValueFrom(refStorage.getDownloadURL());\n const storageData: ObjectStorageData = { url, path: fullPath, bucket, name };\n return storageData;\n } catch (error) {\n console.error(`Error uploading object to path \"${path}\": `, error);\n // Re-throw the error to allow calling code to handle it\n throw error;\n }\n }\n\n /**\n * Deletes all objects within a specified directory path in Firebase Storage.\n * WARNING: Use with extreme caution as this will permanently delete all files in the directory.\n * @param directoryPath The path to the directory to delete (e.g., 'users/userId/files/').\n * @returns A promise that resolves when the deletion attempt is complete.\n */\n public async deleteDirectory(directoryPath: string): Promise<void> {\n const storage = getStorage();\n const directoryRef = ref(storage, directoryPath);\n\n try {\n const res = await listAll(directoryRef);\n const deletePromises: Promise<void>[] = [];\n res.items.forEach((itemRef: StorageReference) => {\n console.log(`Deleting object: ${itemRef.fullPath}`);\n deletePromises.push(deleteObject(itemRef));\n });\n // You might want to handle potential errors during individual deletions if needed\n await Promise.all(deletePromises);\n console.log(`Successfully deleted objects in directory: ${directoryPath}`);\n } catch (error) {\n console.error(`Error deleting objects in directory \"${directoryPath}\":`, error);\n // Optionally re-throw or handle the error appropriately\n throw error;\n }\n }\n\n /**\n * Deletes a single object from Firebase Storage based on its full path.\n * @param objectPath The full path of the object to delete (e.g., 'images/profile.jpg').\n * @returns A promise that resolves when the deletion is complete.\n */\n public async deleteObjectByPath(objectPath: string): Promise<void> {\n const storageRef = this.storage.ref(objectPath);\n try {\n await lastValueFrom(storageRef.delete());\n console.log(`Object deleted successfully: ${objectPath}`);\n } catch (error) {\n console.error(`Error deleting object at path \"${objectPath}\":`, error);\n // Optionally re-throw or handle the error appropriately\n throw error;\n }\n }\n\n /**\n * Private helper to get metadata after an upload task completes.\n * @param task The AngularFireUploadTask.\n * @returns A promise resolving with the object's storage metadata.\n */\n private async uploadAndGetObjectMetadata(task: AngularFireUploadTask): Promise<ObjectStorageData> {\n const snap = await task;\n const { fullPath, bucket, name } = snap.metadata;\n const storage = getStorage();\n const storageRef = ref(storage, fullPath);\n const url = await getDownloadURL(storageRef);\n const meta: ObjectStorageData = {\n url,\n path: fullPath, // Use fullPath for the 'path' property\n bucket,\n name,\n };\n return meta;\n }\n}\n","/*\n * Public API Surface of storage-uploader\n */\n\nexport * from './lib/components/cropper/cropper.component';\nexport * from './lib/components/cropper-modal/cropper-modal.component';\nexport * from './lib/components/image-storage-preview/image-storage-preview';\nexport * from './lib/classes/cropper.classes';\n\nexport * from './lib/services/multi-images-storage.service';\nexport * from './lib/services/dc-files-cache.service';\nexport * from './lib/services/multi-object-storage.service';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["getStorage","listAll","getDownloadURL","ref"],"mappings":"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"}
|
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
import { OnInit
|
|
1
|
+
import { OnInit } from '@angular/core';
|
|
2
2
|
import { ImageCroppedEvent, ImageCropperComponent, LoadedImage } from 'ngx-image-cropper';
|
|
3
3
|
import { Observable } from 'rxjs';
|
|
4
|
-
import {
|
|
5
|
-
import { MultiImagesStorageService } from '../../services/multi-images-storage.service';
|
|
4
|
+
import { ImageMultipleCrops, CropImageSettings } from '../../classes/cropper.classes';
|
|
6
5
|
import * as i0 from "@angular/core";
|
|
7
6
|
export declare class CropperComponent implements OnInit {
|
|
8
7
|
private multiImagesStorageService;
|
|
9
|
-
imageSettings: CropImageSettings
|
|
10
|
-
ratioType:
|
|
11
|
-
resolutions:
|
|
12
|
-
imageUploaded:
|
|
13
|
-
onImageCropped:
|
|
14
|
-
onFileSelected:
|
|
8
|
+
readonly imageSettings: import("@angular/core").InputSignal<CropImageSettings>;
|
|
9
|
+
readonly ratioType: import("@angular/core").InputSignal<string>;
|
|
10
|
+
readonly resolutions: import("@angular/core").InputSignal<number[]>;
|
|
11
|
+
readonly imageUploaded: import("@angular/core").OutputEmitterRef<any>;
|
|
12
|
+
readonly onImageCropped: import("@angular/core").OutputEmitterRef<ImageMultipleCrops>;
|
|
13
|
+
readonly onFileSelected: import("@angular/core").OutputEmitterRef<any>;
|
|
15
14
|
imageCropper: ImageCropperComponent;
|
|
16
15
|
fileMetadata: File | null;
|
|
17
16
|
imageChangedEvent: Event;
|
|
@@ -22,7 +21,8 @@ export declare class CropperComponent implements OnInit {
|
|
|
22
21
|
renameFile: any;
|
|
23
22
|
storagePath: string;
|
|
24
23
|
showModal: boolean;
|
|
25
|
-
|
|
24
|
+
/** Inserted by Angular inject() migration for backwards compatibility */
|
|
25
|
+
constructor(...args: unknown[]);
|
|
26
26
|
ngOnInit(): void;
|
|
27
27
|
reloadPath(): void;
|
|
28
28
|
fileChangeEvent(event: any): Promise<void>;
|
|
@@ -34,5 +34,5 @@ export declare class CropperComponent implements OnInit {
|
|
|
34
34
|
uploadToStorage(imageMulticrops: ImageMultipleCrops): Promise<void>;
|
|
35
35
|
closeModal(): void;
|
|
36
36
|
static ɵfac: i0.ɵɵFactoryDeclaration<CropperComponent, never>;
|
|
37
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<CropperComponent, "app-cropper", never, { "imageSettings": { "alias": "imageSettings"; "required": false; }; "ratioType": { "alias": "ratioType"; "required": false; }; "resolutions": { "alias": "resolutions"; "required": false; }; }, { "imageUploaded": "imageUploaded"; "onImageCropped": "onImageCropped"; "onFileSelected": "onFileSelected"; }, never, never, true, never>;
|
|
37
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<CropperComponent, "app-cropper", never, { "imageSettings": { "alias": "imageSettings"; "required": false; "isSignal": true; }; "ratioType": { "alias": "ratioType"; "required": false; "isSignal": true; }; "resolutions": { "alias": "resolutions"; "required": false; "isSignal": true; }; }, { "imageUploaded": "imageUploaded"; "onImageCropped": "onImageCropped"; "onFileSelected": "onFileSelected"; }, never, never, true, never>;
|
|
38
38
|
}
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
import { OnInit
|
|
1
|
+
import { OnInit } from '@angular/core';
|
|
2
2
|
import { ImageCroppedEvent, ImageCropperComponent, LoadedImage } from 'ngx-image-cropper';
|
|
3
3
|
import { Observable } from 'rxjs';
|
|
4
|
-
import { MultiImagesStorageService } from '../../services/multi-images-storage.service';
|
|
5
4
|
import { ImageMultipleCrops, StorageImageSettings, ImgStorageData, AspectRatioOption } from '../../classes/cropper.classes';
|
|
6
5
|
import * as i0 from "@angular/core";
|
|
7
6
|
export declare class CropperComponentModal implements OnInit {
|
|
8
7
|
private multiImagesStorageService;
|
|
9
8
|
private changeDetectorRef;
|
|
10
|
-
imgStorageSettings: StorageImageSettings
|
|
11
|
-
buttonLabel: string
|
|
9
|
+
readonly imgStorageSettings: import("@angular/core").InputSignal<StorageImageSettings>;
|
|
10
|
+
readonly buttonLabel: import("@angular/core").InputSignal<string>;
|
|
12
11
|
currentStorage: ImgStorageData;
|
|
13
|
-
imageUploaded:
|
|
14
|
-
onImageCropped:
|
|
15
|
-
onFileSelected:
|
|
12
|
+
readonly imageUploaded: import("@angular/core").OutputEmitterRef<ImgStorageData>;
|
|
13
|
+
readonly onImageCropped: import("@angular/core").OutputEmitterRef<ImageMultipleCrops>;
|
|
14
|
+
readonly onFileSelected: import("@angular/core").OutputEmitterRef<any>;
|
|
16
15
|
imageCropper: ImageCropperComponent;
|
|
17
16
|
aspectRatioOptions: AspectRatioOption[];
|
|
18
17
|
fileMetadata: File | null;
|
|
@@ -26,7 +25,8 @@ export declare class CropperComponentModal implements OnInit {
|
|
|
26
25
|
resizeToWidth: number;
|
|
27
26
|
ratioSelected: any;
|
|
28
27
|
fileInputId: string;
|
|
29
|
-
|
|
28
|
+
/** Inserted by Angular inject() migration for backwards compatibility */
|
|
29
|
+
constructor(...args: unknown[]);
|
|
30
30
|
ngOnInit(): void;
|
|
31
31
|
reloadPath(): void;
|
|
32
32
|
private setSettingsForComponent;
|
|
@@ -38,5 +38,5 @@ export declare class CropperComponentModal implements OnInit {
|
|
|
38
38
|
simpleCropAndUpload(): Promise<void>;
|
|
39
39
|
changeRatio(event: AspectRatioOption): void;
|
|
40
40
|
static ɵfac: i0.ɵɵFactoryDeclaration<CropperComponentModal, never>;
|
|
41
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<CropperComponentModal, "dc-cropper-modal", never, { "imgStorageSettings": { "alias": "imgStorageSettings"; "required": false; }; "buttonLabel": { "alias": "buttonLabel"; "required": false; }; "currentStorage": { "alias": "currentStorage"; "required": false; }; }, { "imageUploaded": "imageUploaded"; "onImageCropped": "onImageCropped"; "onFileSelected": "onFileSelected"; }, never, never, true, never>;
|
|
41
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<CropperComponentModal, "dc-cropper-modal", never, { "imgStorageSettings": { "alias": "imgStorageSettings"; "required": false; "isSignal": true; }; "buttonLabel": { "alias": "buttonLabel"; "required": false; "isSignal": true; }; "currentStorage": { "alias": "currentStorage"; "required": false; }; }, { "imageUploaded": "imageUploaded"; "onImageCropped": "onImageCropped"; "onFileSelected": "onFileSelected"; }, never, never, true, never>;
|
|
42
42
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { OnInit, OnDestroy } from '@angular/core';
|
|
2
|
+
import { BehaviorSubject } from 'rxjs';
|
|
3
|
+
import { ImgStorageData, StorageImageSettings } from '../../classes/cropper.classes';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
export declare class ImageStoragePreviewComponent implements OnInit, OnDestroy {
|
|
6
|
+
private storage;
|
|
7
|
+
readonly imageSelected: import("@angular/core").OutputEmitterRef<ImgStorageData>;
|
|
8
|
+
images$: BehaviorSubject<ImgStorageData[]>;
|
|
9
|
+
loading$: BehaviorSubject<boolean>;
|
|
10
|
+
error$: BehaviorSubject<string>;
|
|
11
|
+
readonly storagePath = "/images/resources";
|
|
12
|
+
private subscriptions;
|
|
13
|
+
/** Inserted by Angular inject() migration for backwards compatibility */
|
|
14
|
+
constructor(...args: unknown[]);
|
|
15
|
+
ngOnInit(): void;
|
|
16
|
+
imgStorageSettings: StorageImageSettings;
|
|
17
|
+
ngOnDestroy(): void;
|
|
18
|
+
/**
|
|
19
|
+
* Loads images from Firebase Storage at the specified path
|
|
20
|
+
*/
|
|
21
|
+
private loadImagesFromStorage;
|
|
22
|
+
/**
|
|
23
|
+
* Refreshes the image list
|
|
24
|
+
*/
|
|
25
|
+
refreshImages(): void;
|
|
26
|
+
selectImage(image: ImgStorageData): void;
|
|
27
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<ImageStoragePreviewComponent, never>;
|
|
28
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<ImageStoragePreviewComponent, "dc-image-storage-preview", never, {}, { "imageSelected": "imageSelected"; }, never, never, true, never>;
|
|
29
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Storage } from '@angular/fire/storage';
|
|
2
1
|
import * as i0 from "@angular/core";
|
|
3
2
|
export declare class DCFilesCacheService {
|
|
4
3
|
private storage;
|
|
5
|
-
|
|
4
|
+
/** Inserted by Angular inject() migration for backwards compatibility */
|
|
5
|
+
constructor(...args: unknown[]);
|
|
6
6
|
files: {
|
|
7
7
|
[key: string]: string;
|
|
8
8
|
};
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { AngularFireStorage } from '@angular/fire/compat/storage';
|
|
2
1
|
import { ImageMultipleCrops, ImgStorageData } from '../classes/cropper.classes';
|
|
3
2
|
import * as i0 from "@angular/core";
|
|
4
3
|
export declare class MultiImagesStorageService {
|
|
5
4
|
private storage;
|
|
6
|
-
constructor(storage: AngularFireStorage);
|
|
7
5
|
uploadImage(image: Blob, path: string): Promise<ImgStorageData>;
|
|
8
6
|
uploadImage2(imageMulticrops: ImageMultipleCrops, path: string): Promise<ImgStorageData>;
|
|
9
7
|
delete_directory(imagePath: string): Promise<void>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
/**
|
|
3
|
+
* Represents the metadata of an object stored in Firebase Storage.
|
|
4
|
+
*/
|
|
5
|
+
export interface ObjectStorageData {
|
|
6
|
+
/** The download URL of the stored object. */
|
|
7
|
+
url: string;
|
|
8
|
+
/** The full path of the object in the storage bucket (e.g., 'folder/subfolder/file.txt'). */
|
|
9
|
+
path: string;
|
|
10
|
+
/** The name of the Firebase Storage bucket where the object is stored. */
|
|
11
|
+
bucket: string;
|
|
12
|
+
/** The name of the object file (e.g., 'file.txt'). */
|
|
13
|
+
name?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare class MultiObjectStorageService {
|
|
16
|
+
private storage;
|
|
17
|
+
/**
|
|
18
|
+
* Uploads a Blob or File object to a specified path in Firebase Storage.
|
|
19
|
+
* @param objectToUpload The Blob or File to upload.
|
|
20
|
+
* @param path The desired storage path (e.g., 'documents/report.pdf').
|
|
21
|
+
* @returns A promise that resolves with the storage metadata of the uploaded object.
|
|
22
|
+
* @throws Throws an error if the upload fails.
|
|
23
|
+
*/
|
|
24
|
+
uploadObject(objectToUpload: Blob | File, path: string): Promise<ObjectStorageData>;
|
|
25
|
+
/**
|
|
26
|
+
* Deletes all objects within a specified directory path in Firebase Storage.
|
|
27
|
+
* WARNING: Use with extreme caution as this will permanently delete all files in the directory.
|
|
28
|
+
* @param directoryPath The path to the directory to delete (e.g., 'users/userId/files/').
|
|
29
|
+
* @returns A promise that resolves when the deletion attempt is complete.
|
|
30
|
+
*/
|
|
31
|
+
deleteDirectory(directoryPath: string): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Deletes a single object from Firebase Storage based on its full path.
|
|
34
|
+
* @param objectPath The full path of the object to delete (e.g., 'images/profile.jpg').
|
|
35
|
+
* @returns A promise that resolves when the deletion is complete.
|
|
36
|
+
*/
|
|
37
|
+
deleteObjectByPath(objectPath: string): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Private helper to get metadata after an upload task completes.
|
|
40
|
+
* @param task The AngularFireUploadTask.
|
|
41
|
+
* @returns A promise resolving with the object's storage metadata.
|
|
42
|
+
*/
|
|
43
|
+
private uploadAndGetObjectMetadata;
|
|
44
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<MultiObjectStorageService, never>;
|
|
45
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<MultiObjectStorageService>;
|
|
46
|
+
}
|
package/package.json
CHANGED
package/public-api.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export * from './lib/components/cropper/cropper.component';
|
|
2
2
|
export * from './lib/components/cropper-modal/cropper-modal.component';
|
|
3
|
+
export * from './lib/components/image-storage-preview/image-storage-preview';
|
|
3
4
|
export * from './lib/classes/cropper.classes';
|
|
4
5
|
export * from './lib/services/multi-images-storage.service';
|
|
5
6
|
export * from './lib/services/dc-files-cache.service';
|
|
7
|
+
export * from './lib/services/multi-object-storage.service';
|