@dataclouder/ngx-cloud-storage 0.0.22 → 0.0.23

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.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, Injectable, input, output, ViewChild, Component, ChangeDetectorRef, Input } from '@angular/core';
2
+ import { inject, Injectable, input, output, ViewChild, Component, ChangeDetectorRef, Input, signal } from '@angular/core';
3
3
  import { ImageCropperComponent } from 'ngx-image-cropper';
4
4
  import * as i1 from '@angular/forms';
5
5
  import { FormsModule } from '@angular/forms';
@@ -20,7 +20,7 @@ import { SelectModule } from 'primeng/select';
20
20
  import * as i8 from 'primeng/inputtext';
21
21
  import { InputTextModule } from 'primeng/inputtext';
22
22
  import * as i3 from 'primeng/api';
23
- import { AsyncPipe } from '@angular/common';
23
+ import { AsyncPipe, CommonModule } from '@angular/common';
24
24
 
25
25
  const AspectRatioOptions = [
26
26
  { value: '1:1', label: 'square', description: 'Square (1:1)', valueRatio: 1 / 1 },
@@ -81,6 +81,62 @@ const DEFAULT_SETTINGS = {
81
81
  resizeToWidth: 450,
82
82
  },
83
83
  };
84
+ const FIREBASE_STORAGE_URL_REGEX = /https:\/\/firebasestorage\.googleapis\.com\/v0\/b\/([^/]+)\/o\/([^?]+)/;
85
+ /**
86
+ * Extracts the bucket name from a Firebase Storage URL.
87
+ * The URL is expected to follow the pattern:
88
+ * 'https://firebasestorage.googleapis.com/v0/b/<bucket>/<path>?<query_params>'
89
+ *
90
+ * @param data The CloudStorageData object containing the 'url' property.
91
+ * @returns The bucket name (e.g., 'appingles-pro.appspot.com') or undefined if not found or URL is invalid.
92
+ */
93
+ function extractBucket(data) {
94
+ if (!data.url) {
95
+ return undefined;
96
+ }
97
+ const match = data.url.match(FIREBASE_STORAGE_URL_REGEX);
98
+ // match[0] is the full matched string
99
+ // match[1] is the first capturing group (bucket)
100
+ // match[2] is the second capturing group (path)
101
+ return match?.[1];
102
+ }
103
+ /**
104
+ * Extracts the file path from a Firebase Storage URL.
105
+ * The URL is expected to follow the pattern:
106
+ * 'https://firebasestorage.googleapis.com/v0/b/<bucket>/<path>?<query_params>'
107
+ * The extracted path is the part after the bucket and before any query parameters.
108
+ *
109
+ * @param data The CloudStorageData object containing the 'url' property.
110
+ * @returns The file path (e.g., 'scenarios/666506c3b9b5443f4bfab5e0/images/hairdresser.webp')
111
+ * or undefined if not found or URL is invalid.
112
+ * Note: This function does not perform URL decoding on the path. If URL-decoded paths
113
+ * (e.g., converting %2F to /) are needed, `decodeURIComponent` should be applied to the result.
114
+ */
115
+ function extractPath(data) {
116
+ if (data && data?.url) {
117
+ const match = data.url.match(FIREBASE_STORAGE_URL_REGEX);
118
+ // match[0] is the full matched string
119
+ // match[1] is the first capturing group (bucket)
120
+ // match[2] is the second capturing group (path after 'o/')
121
+ if (match && match[2]) {
122
+ try {
123
+ // Decode URI components, e.g., %2F to /
124
+ return decodeURIComponent(match[2]);
125
+ }
126
+ catch (e) {
127
+ // Log error if decoding fails.
128
+ // Returning the raw path component as a fallback.
129
+ // Consider if undefined or throwing an error would be more appropriate for your use case.
130
+ console.error(`Failed to decode URI component for path: '${match[2]}'`, e);
131
+ return match[2]; // Fallback to raw path component
132
+ }
133
+ }
134
+ return undefined; // URL did not match expected pattern or path component missing
135
+ }
136
+ else {
137
+ return undefined;
138
+ }
139
+ }
84
140
 
85
141
  class MultiImagesStorageService {
86
142
  constructor() {
@@ -90,9 +146,14 @@ class MultiImagesStorageService {
90
146
  try {
91
147
  const refStorage = this.storage.ref(path);
92
148
  const task = await refStorage.put(image);
93
- const { fullPath, bucket } = task.metadata;
149
+ const { bucket, name: metadataName, contentType, size } = task.metadata;
94
150
  const url = await lastValueFrom(refStorage.getDownloadURL());
95
- const imageStorage = { url, path: fullPath, bucket };
151
+ const imageStorage = {
152
+ url,
153
+ name: metadataName,
154
+ type: contentType,
155
+ size,
156
+ };
96
157
  return imageStorage;
97
158
  }
98
159
  catch (error) {
@@ -100,43 +161,6 @@ class MultiImagesStorageService {
100
161
  return null;
101
162
  }
102
163
  }
103
- uploadImage2(imageMulticrops, path) {
104
- // path is usually [products/id] will create a new folder with the name and upload the images
105
- const blobs = imageMulticrops.imagesBlobs;
106
- const tasks = [];
107
- let fileName = imageMulticrops.settings?.renameFile ?? imageMulticrops.file.name;
108
- fileName = fileName.split('.')[0];
109
- // load the default image jpeg
110
- path = `${path}/${fileName}`;
111
- const defaultImagePath = `${path}/${fileName}-400W.jpeg`;
112
- const refStorage = this.storage.ref(defaultImagePath);
113
- const task = refStorage.put(imageMulticrops.defaultImageBlob);
114
- tasks.push(this.uploadAndGetUrl(task, 'default'));
115
- // Upload webp images
116
- for (const resolutionStr of Object.keys(blobs)) {
117
- const fullName = `${fileName}-${resolutionStr}W.webp`;
118
- const filePath = `${path}/${fullName}`;
119
- const refStorage = this.storage.ref(filePath);
120
- const resolution = Number(resolutionStr);
121
- const task = refStorage.put(blobs[resolution]);
122
- tasks.push(this.uploadAndGetUrl(task, resolution + 'W'));
123
- }
124
- return Promise.all(tasks).then(async (upladedImages) => {
125
- let defaultImage = {};
126
- let resolutionsUrls = {};
127
- for (const image of upladedImages) {
128
- if (image.resolution === 'default') {
129
- defaultImage = image;
130
- }
131
- else {
132
- resolutionsUrls[image.resolution] = image.url;
133
- }
134
- }
135
- defaultImage['resolutions'] = resolutionsUrls;
136
- defaultImage['path'] = path;
137
- return defaultImage;
138
- });
139
- }
140
164
  async delete_directory(imagePath) {
141
165
  // WARNING!! user very carefully could delete whatever folder
142
166
  const storage = getStorage();
@@ -160,21 +184,41 @@ class MultiImagesStorageService {
160
184
  }
161
185
  async uploadAndGetUrl(task, resolution) {
162
186
  const snap = await task;
163
- const { fullPath, bucket, name } = snap.metadata;
187
+ const { fullPath, bucket, name, contentType, size } = snap.metadata;
164
188
  const storage = getStorage();
165
189
  const storageRef = ref(storage, fullPath);
166
190
  const url = await getDownloadURL(storageRef);
167
191
  const meta = {
168
192
  url,
169
- fullPath,
170
- bucket,
171
193
  name,
172
194
  resolution,
173
- path: '',
174
- resolutions: {},
195
+ type: contentType,
196
+ size,
197
+ resolutions: {}, // Default empty resolutions
175
198
  };
176
199
  return meta;
177
200
  }
201
+ async uploadGenericFile(file, storagePath) {
202
+ try {
203
+ // Ensure storagePath is a directory path, and append file.name
204
+ const fullFilePath = `${storagePath.replace(/\/$/, '')}/${file.name}`;
205
+ const refStorage = this.storage.ref(fullFilePath);
206
+ const task = await refStorage.put(file);
207
+ const url = await lastValueFrom(refStorage.getDownloadURL());
208
+ const fileData = {
209
+ url,
210
+ name: file.name,
211
+ type: file.type,
212
+ size: file.size,
213
+ metadata: task.metadata,
214
+ };
215
+ return fileData;
216
+ }
217
+ catch (error) {
218
+ console.error(`Error uploading file ${file.name} to ${storagePath}:`, error);
219
+ return null;
220
+ }
221
+ }
178
222
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MultiImagesStorageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
179
223
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MultiImagesStorageService, providedIn: 'root' }); }
180
224
  }
@@ -192,6 +236,7 @@ class CropperComponent {
192
236
  this.imageSettings = input({});
193
237
  this.ratioType = input(AspectType.Square);
194
238
  this.resolutions = input([ResolutionType.MediumLarge]);
239
+ // Outputs
195
240
  this.imageUploaded = output();
196
241
  this.onImageCropped = output();
197
242
  this.onFileSelected = output();
@@ -243,28 +288,6 @@ class CropperComponent {
243
288
  this.imageUploaded.emit(imgStorage);
244
289
  this.closeModal();
245
290
  }
246
- async uploadToStorage(imageMulticrops) {
247
- // TODO: Nota si algo falla aquí puede causar inconsistencias en el sistema, ver como manejar errores
248
- const path = this.imageSettings().path;
249
- // const imageUploaded = await this.multiImagesStorageService.uploadImage(imageMulticrops, path);
250
- // this.modalRef.close();
251
- // console.log(imageUploaded);
252
- // const image = { type: 'cover', ...imageUploaded };
253
- // TODO: creo que esta parte va en el componente que llama a este componente
254
- // if (!this.lesson.media) {
255
- // this.lesson.media = {};
256
- // this.lesson.media.images = [image];
257
- // } else {
258
- // // solo sustituir el cover si ya existe
259
- // const currentCover = this.lesson.media.images.find((img) => img.type === 'cover');
260
- // this.multiImagesStorageService.delete_directory(currentCover.path);
261
- // this.lesson.media.images = this.lesson.media.images.filter((img) => img.type !== 'cover');
262
- // this.lesson.media.images.push(image);
263
- // }
264
- // await this.saveLesson();
265
- // this.updateCover();
266
- // this.imageUploaded.emit(image);
267
- }
268
291
  closeModal() {
269
292
  this.showModal = false;
270
293
  }
@@ -274,7 +297,7 @@ class CropperComponent {
274
297
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: CropperComponent, decorators: [{
275
298
  type: Component,
276
299
  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: [{
300
+ }], propDecorators: { imageCropper: [{
278
301
  type: ViewChild,
279
302
  args: [ImageCropperComponent]
280
303
  }] } });
@@ -310,7 +333,8 @@ class CropperComponentModal {
310
333
  }
311
334
  reloadPath() {
312
335
  const randomCharacters = Math.random().toString(36).substring(2, 15);
313
- this.storagePath = `${this.imgStorageSettings().path}/${this.imgStorageSettings().fileName}-${randomCharacters}.webp`;
336
+ const fileName = this.imgStorageSettings().fileName || 'img';
337
+ this.storagePath = `${this.imgStorageSettings().path}/${fileName}-${randomCharacters}.webp`;
314
338
  }
315
339
  setSettingsForComponent() {
316
340
  const imgStorageSettings = this.imgStorageSettings();
@@ -372,12 +396,15 @@ class CropperComponentModal {
372
396
  }
373
397
  async simpleCropAndUpload() {
374
398
  console.log(this.fileInputId);
399
+ debugger;
375
400
  console.log('simpleCropAndUpload');
376
401
  const imageCropped = await this.imageCropper.crop();
377
402
  const imgStorage = await this.multiImagesStorageService.uploadImage(imageCropped?.blob, this.storagePath);
378
- if (this.currentStorage?.path) {
379
- console.warn('deleting current Image', this.currentStorage?.path);
380
- this.multiImagesStorageService.deleteImage(this.currentStorage.path);
403
+ const path = extractPath(this.currentStorage);
404
+ if (path) {
405
+ console.warn('deleting current Image', path);
406
+ debugger;
407
+ this.multiImagesStorageService.deleteImage(path);
381
408
  }
382
409
  console.log('imgStorage', imgStorage);
383
410
  this.imageUploaded.emit(imgStorage);
@@ -391,11 +418,11 @@ class CropperComponentModal {
391
418
  this.changeDetectorRef.detectChanges();
392
419
  }
393
420
  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"] }] }); }
421
+ 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"] }] }); }
395
422
  }
396
423
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: CropperComponentModal, decorators: [{
397
424
  type: Component,
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"] }]
425
+ 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
426
  }], ctorParameters: () => [], propDecorators: { currentStorage: [{
400
427
  type: Input
401
428
  }], imageCropper: [{
@@ -446,9 +473,8 @@ class ImageStoragePreviewComponent {
446
473
  const url = await getDownloadURL$1(itemRef);
447
474
  const image = {
448
475
  url,
449
- fullPath: itemRef.fullPath,
476
+ // fullPath: itemRef.fullPath,
450
477
  name: itemRef.name,
451
- path: this.storagePath,
452
478
  };
453
479
  return image;
454
480
  }
@@ -485,6 +511,55 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
485
511
  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
512
  }], ctorParameters: () => [] });
487
513
 
514
+ class SimpleUploaderComponent {
515
+ constructor() {
516
+ this.multiImagesStorageService = inject(MultiImagesStorageService);
517
+ this.storagePath = input.required();
518
+ this.buttonLabel = input('Upload File');
519
+ this.accept = input('*/*');
520
+ this.disabled = input(false);
521
+ this.fileUploaded = output();
522
+ this.uploadError = output();
523
+ this.isLoading = signal(false);
524
+ this.fileInputId = `file-upload-${Math.random().toString(36).substring(2, 11)}`;
525
+ }
526
+ triggerFileInputClick(fileInput) {
527
+ fileInput.click();
528
+ }
529
+ async onFileSelected(event) {
530
+ const fileInput = event.target;
531
+ const file = fileInput.files?.[0];
532
+ if (file) {
533
+ this.isLoading.set(true);
534
+ try {
535
+ const result = await this.multiImagesStorageService.uploadGenericFile(file, this.storagePath());
536
+ if (result) {
537
+ this.fileUploaded.emit(result);
538
+ }
539
+ else {
540
+ // This case might occur if the service's uploadGenericFile returns null without throwing an error
541
+ this.uploadError.emit({ error: 'Upload failed and no specific error was provided by the service.' });
542
+ }
543
+ }
544
+ catch (error) {
545
+ console.error('Error during file upload in SimpleUploaderComponent:', error);
546
+ this.uploadError.emit(error);
547
+ }
548
+ finally {
549
+ this.isLoading.set(false);
550
+ // Reset file input to allow uploading the same file again if needed
551
+ fileInput.value = '';
552
+ }
553
+ }
554
+ }
555
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SimpleUploaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
556
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.4", type: SimpleUploaderComponent, isStandalone: true, selector: "dc-simple-uploader", inputs: { storagePath: { classPropertyName: "storagePath", publicName: "storagePath", isSignal: true, isRequired: true, transformFunction: null }, buttonLabel: { classPropertyName: "buttonLabel", publicName: "buttonLabel", isSignal: true, isRequired: false, transformFunction: null }, accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { fileUploaded: "fileUploaded", uploadError: "uploadError" }, ngImport: i0, template: "<input #fileInput type=\"file\" [id]=\"fileInputId\" (change)=\"onFileSelected($event)\" [accept]=\"accept()\" style=\"display: none\" />\n<p-button\n [label]=\"buttonLabel()\"\n (click)=\"triggerFileInputClick(fileInput)\"\n [loading]=\"isLoading()\"\n [disabled]=\"disabled() || isLoading()\"\n icon=\"pi pi-upload\"\n iconPos=\"left\"></p-button>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }] }); }
557
+ }
558
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SimpleUploaderComponent, decorators: [{
559
+ type: Component,
560
+ args: [{ selector: 'dc-simple-uploader', standalone: true, imports: [CommonModule, FormsModule, ButtonModule], template: "<input #fileInput type=\"file\" [id]=\"fileInputId\" (change)=\"onFileSelected($event)\" [accept]=\"accept()\" style=\"display: none\" />\n<p-button\n [label]=\"buttonLabel()\"\n (click)=\"triggerFileInputClick(fileInput)\"\n [loading]=\"isLoading()\"\n [disabled]=\"disabled() || isLoading()\"\n icon=\"pi pi-upload\"\n iconPos=\"left\"></p-button>\n" }]
561
+ }], ctorParameters: () => [] });
562
+
488
563
  class DCFilesCacheService {
489
564
  constructor() {
490
565
  this.storage = inject(Storage);
@@ -637,5 +712,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
637
712
  * Generated bundle index. Do not edit.
638
713
  */
639
714
 
640
- export { AspectRatio, AspectRatio2, AspectRatioOptions, AspectType, CropperComponent, CropperComponentModal, DCFilesCacheService, DEFAULT_SETTINGS, ImageStoragePreviewComponent, MultiImagesStorageService, MultiObjectStorageService, ResolutionType };
715
+ export { AspectRatio, AspectRatio2, AspectRatioOptions, AspectType, CropperComponent, CropperComponentModal, DCFilesCacheService, DEFAULT_SETTINGS, ImageStoragePreviewComponent, MultiImagesStorageService, MultiObjectStorageService, ResolutionType, SimpleUploaderComponent, extractBucket, extractPath };
641
716
  //# 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/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
+ {"version":3,"file":"dataclouder-ngx-cloud-storage.mjs","sources":["../../../../projects/dataclouder/ngx-cloud-storage/src/lib/classes/storage.models.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/components/simple-uploader/simple-uploader.component.ts","../../../../projects/dataclouder/ngx-cloud-storage/src/lib/components/simple-uploader/simple-uploader.component.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 CloudStorageData {\n url: string;\n}\n\nexport interface FileStorageData extends CloudStorageData {\n name?: string; // File name in storage, e.g., 'report.pdf'\n type?: string; // MIME type, e.g., 'application/pdf'\n size?: number; // File size in bytes\n metadata?: any;\n}\n\nexport interface ImgStorageData extends FileStorageData {\n resolution?: string;\n resolutions?: { [key: string]: string };\n}\n\nexport interface ImgGeneratedStorageData extends ImgStorageData {\n model?: string;\n prompt?: string;\n}\n\n// Use as input for uploadImage\nexport 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 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\nconst FIREBASE_STORAGE_URL_REGEX = /https:\\/\\/firebasestorage\\.googleapis\\.com\\/v0\\/b\\/([^/]+)\\/o\\/([^?]+)/;\n\n/**\n * Extracts the bucket name from a Firebase Storage URL.\n * The URL is expected to follow the pattern:\n * 'https://firebasestorage.googleapis.com/v0/b/&lt;bucket&gt;/&lt;path&gt;?&lt;query_params&gt;'\n *\n * @param data The CloudStorageData object containing the 'url' property.\n * @returns The bucket name (e.g., 'appingles-pro.appspot.com') or undefined if not found or URL is invalid.\n */\nexport function extractBucket(data: CloudStorageData): string | undefined {\n if (!data.url) {\n return undefined;\n }\n const match = data.url.match(FIREBASE_STORAGE_URL_REGEX);\n // match[0] is the full matched string\n // match[1] is the first capturing group (bucket)\n // match[2] is the second capturing group (path)\n return match?.[1];\n}\n\n/**\n * Extracts the file path from a Firebase Storage URL.\n * The URL is expected to follow the pattern:\n * 'https://firebasestorage.googleapis.com/v0/b/&lt;bucket&gt;/&lt;path&gt;?&lt;query_params&gt;'\n * The extracted path is the part after the bucket and before any query parameters.\n *\n * @param data The CloudStorageData object containing the 'url' property.\n * @returns The file path (e.g., 'scenarios/666506c3b9b5443f4bfab5e0/images/hairdresser.webp')\n * or undefined if not found or URL is invalid.\n * Note: This function does not perform URL decoding on the path. If URL-decoded paths\n * (e.g., converting %2F to /) are needed, `decodeURIComponent` should be applied to the result.\n */\nexport function extractPath(data: CloudStorageData): string | undefined {\n if (data && data?.url) {\n const match = data.url.match(FIREBASE_STORAGE_URL_REGEX);\n // match[0] is the full matched string\n // match[1] is the first capturing group (bucket)\n // match[2] is the second capturing group (path after 'o/')\n if (match && match[2]) {\n try {\n // Decode URI components, e.g., %2F to /\n return decodeURIComponent(match[2]);\n } catch (e) {\n // Log error if decoding fails.\n // Returning the raw path component as a fallback.\n // Consider if undefined or throwing an error would be more appropriate for your use case.\n console.error(`Failed to decode URI component for path: '${match[2]}'`, e);\n return match[2]; // Fallback to raw path component\n }\n }\n return undefined; // URL did not match expected pattern or path component missing\n } else {\n return undefined;\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, FileStorageData } from '../classes/storage.models';\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 { bucket, name: metadataName, contentType, size } = task.metadata;\n const url = await lastValueFrom(refStorage.getDownloadURL());\n const imageStorage: ImgStorageData = {\n url,\n name: metadataName,\n type: contentType,\n size,\n };\n return imageStorage;\n } catch (error) {\n console.error('uploading image error: ', error);\n return null;\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, contentType, size } = 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 name,\n resolution,\n type: contentType,\n size,\n resolutions: {}, // Default empty resolutions\n };\n\n return meta;\n }\n\n public async uploadGenericFile(file: File, storagePath: string): Promise<FileStorageData | null> {\n try {\n // Ensure storagePath is a directory path, and append file.name\n const fullFilePath = `${storagePath.replace(/\\/$/, '')}/${file.name}`;\n const refStorage = this.storage.ref(fullFilePath);\n const task = await refStorage.put(file);\n const url = await lastValueFrom(refStorage.getDownloadURL());\n\n const fileData: FileStorageData = {\n url,\n name: file.name,\n type: file.type,\n size: file.size,\n metadata: task.metadata,\n };\n return fileData;\n } catch (error) {\n console.error(`Error uploading file ${file.name} to ${storagePath}:`, error);\n return null;\n }\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/storage.models';\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 readonly ratioType = input<AspectType | string>(AspectType.Square);\n readonly resolutions = input<Array<number>>([ResolutionType.MediumLarge]);\n // Outputs\n readonly imageUploaded = output<any>();\n readonly onImageCropped = output<ImageMultipleCrops>();\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 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 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 extractPath,\n} from '../../classes/storage.models';\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 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\n this.reloadPath();\n }\n\n public reloadPath(): void {\n const randomCharacters = Math.random().toString(36).substring(2, 15);\n const fileName = this.imgStorageSettings().fileName || 'img';\n this.storagePath = `${this.imgStorageSettings().path}/${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 debugger;\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\n const path = extractPath(this.currentStorage);\n\n if (path) {\n console.warn('deleting current Image', path);\n debugger;\n this.multiImagesStorageService.deleteImage(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/storage.models';\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 };\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 { Component, inject, input, output, signal } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { ButtonModule } from 'primeng/button';\nimport { MultiImagesStorageService } from '../../services/multi-images-storage.service';\nimport { FileStorageData } from '../../classes/storage.models';\n\n@Component({\n selector: 'dc-simple-uploader',\n templateUrl: './simple-uploader.component.html',\n styleUrls: ['./simple-uploader.component.scss'],\n standalone: true,\n imports: [CommonModule, FormsModule, ButtonModule],\n})\nexport class SimpleUploaderComponent {\n private multiImagesStorageService = inject(MultiImagesStorageService);\n\n storagePath = input.required<string>();\n buttonLabel = input<string>('Upload File');\n accept = input<string>('*/*');\n disabled = input<boolean>(false);\n\n fileUploaded = output<FileStorageData>();\n uploadError = output<any>();\n\n isLoading = signal(false);\n fileInputId: string;\n\n constructor() {\n this.fileInputId = `file-upload-${Math.random().toString(36).substring(2, 11)}`;\n }\n\n triggerFileInputClick(fileInput: HTMLInputElement): void {\n fileInput.click();\n }\n\n async onFileSelected(event: Event): Promise<void> {\n const fileInput = event.target as HTMLInputElement;\n const file = fileInput.files?.[0];\n\n if (file) {\n this.isLoading.set(true);\n try {\n const result = await this.multiImagesStorageService.uploadGenericFile(file, this.storagePath());\n if (result) {\n this.fileUploaded.emit(result);\n } else {\n // This case might occur if the service's uploadGenericFile returns null without throwing an error\n this.uploadError.emit({ error: 'Upload failed and no specific error was provided by the service.' });\n }\n } catch (error) {\n console.error('Error during file upload in SimpleUploaderComponent:', error);\n this.uploadError.emit(error);\n } finally {\n this.isLoading.set(false);\n // Reset file input to allow uploading the same file again if needed\n fileInput.value = '';\n }\n }\n }\n}\n","<input #fileInput type=\"file\" [id]=\"fileInputId\" (change)=\"onFileSelected($event)\" [accept]=\"accept()\" style=\"display: none\" />\n<p-button\n [label]=\"buttonLabel()\"\n (click)=\"triggerFileInputClick(fileInput)\"\n [loading]=\"isLoading()\"\n [disabled]=\"disabled() || isLoading()\"\n icon=\"pi pi-upload\"\n iconPos=\"left\"></p-button>\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/components/simple-uploader/simple-uploader.component';\nexport * from './lib/classes/storage.models';\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","i1","ref"],"mappings":"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"}
@@ -1,3 +1,22 @@
1
+ export interface CloudStorageData {
2
+ url: string;
3
+ }
4
+ export interface FileStorageData extends CloudStorageData {
5
+ name?: string;
6
+ type?: string;
7
+ size?: number;
8
+ metadata?: any;
9
+ }
10
+ export interface ImgStorageData extends FileStorageData {
11
+ resolution?: string;
12
+ resolutions?: {
13
+ [key: string]: string;
14
+ };
15
+ }
16
+ export interface ImgGeneratedStorageData extends ImgStorageData {
17
+ model?: string;
18
+ prompt?: string;
19
+ }
1
20
  export interface StorageImageSettings {
2
21
  path?: string;
3
22
  fileName?: string;
@@ -63,18 +82,26 @@ export declare const AspectRatio: {
63
82
  vertical_3_5: number;
64
83
  vertical_2_3: number;
65
84
  };
66
- export interface ImgStorageData extends CloudStorageData {
67
- name?: string;
68
- bucket?: string;
69
- url?: string;
70
- path?: string;
71
- fullPath?: string;
72
- resolution?: string;
73
- resolutions?: any;
74
- }
75
- export interface CloudStorageData {
76
- bucket?: string;
77
- url?: string;
78
- path?: string;
79
- }
80
85
  export declare const DEFAULT_SETTINGS: StorageImageSettings;
86
+ /**
87
+ * Extracts the bucket name from a Firebase Storage URL.
88
+ * The URL is expected to follow the pattern:
89
+ * 'https://firebasestorage.googleapis.com/v0/b/&lt;bucket&gt;/&lt;path&gt;?&lt;query_params&gt;'
90
+ *
91
+ * @param data The CloudStorageData object containing the 'url' property.
92
+ * @returns The bucket name (e.g., 'appingles-pro.appspot.com') or undefined if not found or URL is invalid.
93
+ */
94
+ export declare function extractBucket(data: CloudStorageData): string | undefined;
95
+ /**
96
+ * Extracts the file path from a Firebase Storage URL.
97
+ * The URL is expected to follow the pattern:
98
+ * 'https://firebasestorage.googleapis.com/v0/b/&lt;bucket&gt;/&lt;path&gt;?&lt;query_params&gt;'
99
+ * The extracted path is the part after the bucket and before any query parameters.
100
+ *
101
+ * @param data The CloudStorageData object containing the 'url' property.
102
+ * @returns The file path (e.g., 'scenarios/666506c3b9b5443f4bfab5e0/images/hairdresser.webp')
103
+ * or undefined if not found or URL is invalid.
104
+ * Note: This function does not perform URL decoding on the path. If URL-decoded paths
105
+ * (e.g., converting %2F to /) are needed, `decodeURIComponent` should be applied to the result.
106
+ */
107
+ export declare function extractPath(data: CloudStorageData): string | undefined;
@@ -1,7 +1,7 @@
1
1
  import { OnInit } from '@angular/core';
2
2
  import { ImageCroppedEvent, ImageCropperComponent, LoadedImage } from 'ngx-image-cropper';
3
3
  import { Observable } from 'rxjs';
4
- import { ImageMultipleCrops, CropImageSettings } from '../../classes/cropper.classes';
4
+ import { ImageMultipleCrops, CropImageSettings } from '../../classes/storage.models';
5
5
  import * as i0 from "@angular/core";
6
6
  export declare class CropperComponent implements OnInit {
7
7
  private multiImagesStorageService;
@@ -21,8 +21,6 @@ export declare class CropperComponent implements OnInit {
21
21
  renameFile: any;
22
22
  storagePath: string;
23
23
  showModal: boolean;
24
- /** Inserted by Angular inject() migration for backwards compatibility */
25
- constructor(...args: unknown[]);
26
24
  ngOnInit(): void;
27
25
  reloadPath(): void;
28
26
  fileChangeEvent(event: any): Promise<void>;
@@ -31,7 +29,6 @@ export declare class CropperComponent implements OnInit {
31
29
  loadImageFailed(): void;
32
30
  downloadURL: Observable<string>;
33
31
  simpleCropAndUpload(): Promise<void>;
34
- uploadToStorage(imageMulticrops: ImageMultipleCrops): Promise<void>;
35
32
  closeModal(): void;
36
33
  static ɵfac: i0.ɵɵFactoryDeclaration<CropperComponent, never>;
37
34
  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>;
@@ -1,7 +1,7 @@
1
1
  import { OnInit } from '@angular/core';
2
2
  import { ImageCroppedEvent, ImageCropperComponent, LoadedImage } from 'ngx-image-cropper';
3
3
  import { Observable } from 'rxjs';
4
- import { ImageMultipleCrops, StorageImageSettings, ImgStorageData, AspectRatioOption } from '../../classes/cropper.classes';
4
+ import { ImageMultipleCrops, StorageImageSettings, ImgStorageData, AspectRatioOption } from '../../classes/storage.models';
5
5
  import * as i0 from "@angular/core";
6
6
  export declare class CropperComponentModal implements OnInit {
7
7
  private multiImagesStorageService;
@@ -25,8 +25,7 @@ export declare class CropperComponentModal implements OnInit {
25
25
  resizeToWidth: number;
26
26
  ratioSelected: any;
27
27
  fileInputId: string;
28
- /** Inserted by Angular inject() migration for backwards compatibility */
29
- constructor(...args: unknown[]);
28
+ constructor();
30
29
  ngOnInit(): void;
31
30
  reloadPath(): void;
32
31
  private setSettingsForComponent;
@@ -1,6 +1,6 @@
1
1
  import { OnInit, OnDestroy } from '@angular/core';
2
2
  import { BehaviorSubject } from 'rxjs';
3
- import { ImgStorageData, StorageImageSettings } from '../../classes/cropper.classes';
3
+ import { ImgStorageData, StorageImageSettings } from '../../classes/storage.models';
4
4
  import * as i0 from "@angular/core";
5
5
  export declare class ImageStoragePreviewComponent implements OnInit, OnDestroy {
6
6
  private storage;
@@ -0,0 +1,18 @@
1
+ import { FileStorageData } from '../../classes/storage.models';
2
+ import * as i0 from "@angular/core";
3
+ export declare class SimpleUploaderComponent {
4
+ private multiImagesStorageService;
5
+ storagePath: import("@angular/core").InputSignal<string>;
6
+ buttonLabel: import("@angular/core").InputSignal<string>;
7
+ accept: import("@angular/core").InputSignal<string>;
8
+ disabled: import("@angular/core").InputSignal<boolean>;
9
+ fileUploaded: import("@angular/core").OutputEmitterRef<FileStorageData>;
10
+ uploadError: import("@angular/core").OutputEmitterRef<any>;
11
+ isLoading: import("@angular/core").WritableSignal<boolean>;
12
+ fileInputId: string;
13
+ constructor();
14
+ triggerFileInputClick(fileInput: HTMLInputElement): void;
15
+ onFileSelected(event: Event): Promise<void>;
16
+ static ɵfac: i0.ɵɵFactoryDeclaration<SimpleUploaderComponent, never>;
17
+ static ɵcmp: i0.ɵɵComponentDeclaration<SimpleUploaderComponent, "dc-simple-uploader", never, { "storagePath": { "alias": "storagePath"; "required": true; "isSignal": true; }; "buttonLabel": { "alias": "buttonLabel"; "required": false; "isSignal": true; }; "accept": { "alias": "accept"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, { "fileUploaded": "fileUploaded"; "uploadError": "uploadError"; }, never, never, true, never>;
18
+ }
@@ -1,12 +1,12 @@
1
- import { ImageMultipleCrops, ImgStorageData } from '../classes/cropper.classes';
1
+ import { ImgStorageData, FileStorageData } from '../classes/storage.models';
2
2
  import * as i0 from "@angular/core";
3
3
  export declare class MultiImagesStorageService {
4
4
  private storage;
5
5
  uploadImage(image: Blob, path: string): Promise<ImgStorageData>;
6
- uploadImage2(imageMulticrops: ImageMultipleCrops, path: string): Promise<ImgStorageData>;
7
6
  delete_directory(imagePath: string): Promise<void>;
8
7
  deleteImage(imagePath: string): Promise<void>;
9
8
  private uploadAndGetUrl;
9
+ uploadGenericFile(file: File, storagePath: string): Promise<FileStorageData | null>;
10
10
  static ɵfac: i0.ɵɵFactoryDeclaration<MultiImagesStorageService, never>;
11
11
  static ɵprov: i0.ɵɵInjectableDeclaration<MultiImagesStorageService>;
12
12
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dataclouder/ngx-cloud-storage",
3
- "version": "0.0.22",
3
+ "version": "0.0.23",
4
4
  "description": "Angular component library for handling cloud storage uploads with image cropping capabilities",
5
5
  "publishConfig": {
6
6
  "access": "public"
package/public-api.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  export * from './lib/components/cropper/cropper.component';
2
2
  export * from './lib/components/cropper-modal/cropper-modal.component';
3
3
  export * from './lib/components/image-storage-preview/image-storage-preview';
4
- export * from './lib/classes/cropper.classes';
4
+ export * from './lib/components/simple-uploader/simple-uploader.component';
5
+ export * from './lib/classes/storage.models';
5
6
  export * from './lib/services/multi-images-storage.service';
6
7
  export * from './lib/services/dc-files-cache.service';
7
8
  export * from './lib/services/multi-object-storage.service';