@dataclouder/ngx-cloud-storage 0.0.21 → 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.
- package/fesm2022/dataclouder-ngx-cloud-storage.mjs +402 -168
- package/fesm2022/dataclouder-ngx-cloud-storage.mjs.map +1 -1
- package/lib/classes/{cropper.classes.d.ts → storage.models.d.ts} +41 -14
- package/lib/components/cropper/cropper.component.d.ts +9 -12
- package/lib/components/cropper-modal/cropper-modal.component.d.ts +9 -10
- package/lib/components/image-storage-preview/image-storage-preview.d.ts +29 -0
- package/lib/components/simple-uploader/simple-uploader.component.d.ts +18 -0
- package/lib/services/dc-files-cache.service.d.ts +2 -2
- package/lib/services/multi-images-storage.service.d.ts +2 -4
- package/lib/services/multi-object-storage.service.d.ts +46 -0
- package/package.json +1 -1
- package/public-api.d.ts +4 -1
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Injectable,
|
|
2
|
+
import { inject, Injectable, input, output, ViewChild, Component, ChangeDetectorRef, Input, signal } from '@angular/core';
|
|
3
3
|
import { ImageCropperComponent } from 'ngx-image-cropper';
|
|
4
|
-
import
|
|
5
|
-
import * as i2 from '@angular/forms';
|
|
4
|
+
import * as i1 from '@angular/forms';
|
|
6
5
|
import { FormsModule } from '@angular/forms';
|
|
7
|
-
import
|
|
8
|
-
import { getStorage, listAll, deleteObject, getDownloadURL, ref as ref$1 } from '@angular/fire/storage';
|
|
9
|
-
import { ref } from 'firebase/storage';
|
|
10
|
-
import { lastValueFrom } from 'rxjs';
|
|
11
|
-
import * as
|
|
12
|
-
import * as i5 from 'primeng/dialog';
|
|
6
|
+
import { AngularFireStorage } from '@angular/fire/compat/storage';
|
|
7
|
+
import { getStorage, listAll, deleteObject, getDownloadURL, Storage, ref as ref$1 } from '@angular/fire/storage';
|
|
8
|
+
import { ref, getStorage as getStorage$1, listAll as listAll$1, getDownloadURL as getDownloadURL$1 } from 'firebase/storage';
|
|
9
|
+
import { lastValueFrom, BehaviorSubject } from 'rxjs';
|
|
10
|
+
import * as i4 from 'primeng/dialog';
|
|
13
11
|
import { DialogModule } from 'primeng/dialog';
|
|
14
|
-
import * as
|
|
12
|
+
import * as i5 from 'primeng/tooltip';
|
|
15
13
|
import { TooltipModule } from 'primeng/tooltip';
|
|
16
|
-
import * as
|
|
14
|
+
import * as i2 from 'primeng/button';
|
|
17
15
|
import { ButtonModule } from 'primeng/button';
|
|
18
|
-
import * as
|
|
16
|
+
import * as i6 from 'primeng/message';
|
|
19
17
|
import { MessageModule } from 'primeng/message';
|
|
20
|
-
import * as
|
|
18
|
+
import * as i7 from 'primeng/select';
|
|
21
19
|
import { SelectModule } from 'primeng/select';
|
|
22
|
-
import * as
|
|
20
|
+
import * as i8 from 'primeng/inputtext';
|
|
23
21
|
import { InputTextModule } from 'primeng/inputtext';
|
|
24
|
-
import * as
|
|
22
|
+
import * as i3 from 'primeng/api';
|
|
23
|
+
import { AsyncPipe, CommonModule } from '@angular/common';
|
|
25
24
|
|
|
26
25
|
const AspectRatioOptions = [
|
|
27
26
|
{ value: '1:1', label: 'square', description: 'Square (1:1)', valueRatio: 1 / 1 },
|
|
@@ -82,18 +81,79 @@ const DEFAULT_SETTINGS = {
|
|
|
82
81
|
resizeToWidth: 450,
|
|
83
82
|
},
|
|
84
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
|
+
}
|
|
85
140
|
|
|
86
141
|
class MultiImagesStorageService {
|
|
87
|
-
constructor(
|
|
88
|
-
this.storage =
|
|
142
|
+
constructor() {
|
|
143
|
+
this.storage = inject(AngularFireStorage);
|
|
89
144
|
}
|
|
90
145
|
async uploadImage(image, path) {
|
|
91
146
|
try {
|
|
92
147
|
const refStorage = this.storage.ref(path);
|
|
93
148
|
const task = await refStorage.put(image);
|
|
94
|
-
const {
|
|
149
|
+
const { bucket, name: metadataName, contentType, size } = task.metadata;
|
|
95
150
|
const url = await lastValueFrom(refStorage.getDownloadURL());
|
|
96
|
-
const imageStorage = {
|
|
151
|
+
const imageStorage = {
|
|
152
|
+
url,
|
|
153
|
+
name: metadataName,
|
|
154
|
+
type: contentType,
|
|
155
|
+
size,
|
|
156
|
+
};
|
|
97
157
|
return imageStorage;
|
|
98
158
|
}
|
|
99
159
|
catch (error) {
|
|
@@ -101,43 +161,6 @@ class MultiImagesStorageService {
|
|
|
101
161
|
return null;
|
|
102
162
|
}
|
|
103
163
|
}
|
|
104
|
-
uploadImage2(imageMulticrops, path) {
|
|
105
|
-
// path is usually [products/id] will create a new folder with the name and upload the images
|
|
106
|
-
const blobs = imageMulticrops.imagesBlobs;
|
|
107
|
-
const tasks = [];
|
|
108
|
-
let fileName = imageMulticrops.settings?.renameFile ?? imageMulticrops.file.name;
|
|
109
|
-
fileName = fileName.split('.')[0];
|
|
110
|
-
// load the default image jpeg
|
|
111
|
-
path = `${path}/${fileName}`;
|
|
112
|
-
const defaultImagePath = `${path}/${fileName}-400W.jpeg`;
|
|
113
|
-
const refStorage = this.storage.ref(defaultImagePath);
|
|
114
|
-
const task = refStorage.put(imageMulticrops.defaultImageBlob);
|
|
115
|
-
tasks.push(this.uploadAndGetUrl(task, 'default'));
|
|
116
|
-
// Upload webp images
|
|
117
|
-
for (const resolutionStr of Object.keys(blobs)) {
|
|
118
|
-
const fullName = `${fileName}-${resolutionStr}W.webp`;
|
|
119
|
-
const filePath = `${path}/${fullName}`;
|
|
120
|
-
const refStorage = this.storage.ref(filePath);
|
|
121
|
-
const resolution = Number(resolutionStr);
|
|
122
|
-
const task = refStorage.put(blobs[resolution]);
|
|
123
|
-
tasks.push(this.uploadAndGetUrl(task, resolution + 'W'));
|
|
124
|
-
}
|
|
125
|
-
return Promise.all(tasks).then(async (upladedImages) => {
|
|
126
|
-
let defaultImage = {};
|
|
127
|
-
let resolutionsUrls = {};
|
|
128
|
-
for (const image of upladedImages) {
|
|
129
|
-
if (image.resolution === 'default') {
|
|
130
|
-
defaultImage = image;
|
|
131
|
-
}
|
|
132
|
-
else {
|
|
133
|
-
resolutionsUrls[image.resolution] = image.url;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
defaultImage['resolutions'] = resolutionsUrls;
|
|
137
|
-
defaultImage['path'] = path;
|
|
138
|
-
return defaultImage;
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
164
|
async delete_directory(imagePath) {
|
|
142
165
|
// WARNING!! user very carefully could delete whatever folder
|
|
143
166
|
const storage = getStorage();
|
|
@@ -161,41 +184,62 @@ class MultiImagesStorageService {
|
|
|
161
184
|
}
|
|
162
185
|
async uploadAndGetUrl(task, resolution) {
|
|
163
186
|
const snap = await task;
|
|
164
|
-
const { fullPath, bucket, name } = snap.metadata;
|
|
187
|
+
const { fullPath, bucket, name, contentType, size } = snap.metadata;
|
|
165
188
|
const storage = getStorage();
|
|
166
189
|
const storageRef = ref(storage, fullPath);
|
|
167
190
|
const url = await getDownloadURL(storageRef);
|
|
168
191
|
const meta = {
|
|
169
192
|
url,
|
|
170
|
-
fullPath,
|
|
171
|
-
bucket,
|
|
172
193
|
name,
|
|
173
194
|
resolution,
|
|
174
|
-
|
|
175
|
-
|
|
195
|
+
type: contentType,
|
|
196
|
+
size,
|
|
197
|
+
resolutions: {}, // Default empty resolutions
|
|
176
198
|
};
|
|
177
199
|
return meta;
|
|
178
200
|
}
|
|
179
|
-
|
|
180
|
-
|
|
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
|
+
}
|
|
222
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MultiImagesStorageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
223
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MultiImagesStorageService, providedIn: 'root' }); }
|
|
181
224
|
}
|
|
182
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
225
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MultiImagesStorageService, decorators: [{
|
|
183
226
|
type: Injectable,
|
|
184
227
|
args: [{
|
|
185
228
|
providedIn: 'root',
|
|
186
229
|
}]
|
|
187
|
-
}]
|
|
230
|
+
}] });
|
|
188
231
|
|
|
189
232
|
class CropperComponent {
|
|
190
|
-
constructor(
|
|
191
|
-
this.multiImagesStorageService =
|
|
233
|
+
constructor() {
|
|
234
|
+
this.multiImagesStorageService = inject(MultiImagesStorageService);
|
|
192
235
|
// overrides name, path and resizeToWidth
|
|
193
|
-
this.imageSettings = {};
|
|
194
|
-
this.ratioType = AspectType.Square;
|
|
195
|
-
this.resolutions = [ResolutionType.MediumLarge];
|
|
196
|
-
|
|
197
|
-
this.
|
|
198
|
-
this.
|
|
236
|
+
this.imageSettings = input({});
|
|
237
|
+
this.ratioType = input(AspectType.Square);
|
|
238
|
+
this.resolutions = input([ResolutionType.MediumLarge]);
|
|
239
|
+
// Outputs
|
|
240
|
+
this.imageUploaded = output();
|
|
241
|
+
this.onImageCropped = output();
|
|
242
|
+
this.onFileSelected = output();
|
|
199
243
|
this.fileMetadata = null;
|
|
200
244
|
this.aspectRatio = 1;
|
|
201
245
|
this.croppedImage = '';
|
|
@@ -206,13 +250,14 @@ class CropperComponent {
|
|
|
206
250
|
this.showModal = false;
|
|
207
251
|
}
|
|
208
252
|
ngOnInit() {
|
|
209
|
-
this.aspectRatio = AspectRatio[this.ratioType];
|
|
210
|
-
|
|
211
|
-
|
|
253
|
+
this.aspectRatio = AspectRatio[this.ratioType()];
|
|
254
|
+
const imageSettings = this.imageSettings();
|
|
255
|
+
if (imageSettings.path) {
|
|
256
|
+
this.storagePath = `${imageSettings.path}/${imageSettings.fileName}.webp`;
|
|
212
257
|
}
|
|
213
258
|
}
|
|
214
259
|
reloadPath() {
|
|
215
|
-
this.storagePath = `${this.imageSettings.path}/${this.renameFile}.webp`;
|
|
260
|
+
this.storagePath = `${this.imageSettings().path}/${this.renameFile}.webp`;
|
|
216
261
|
}
|
|
217
262
|
async fileChangeEvent(event) {
|
|
218
263
|
this.imageChangedEvent = event;
|
|
@@ -223,7 +268,7 @@ class CropperComponent {
|
|
|
223
268
|
this.showModal = true; // Show modal when file is selected
|
|
224
269
|
this.renameFile = this.fileMetadata?.name?.split('.')[0];
|
|
225
270
|
console.log(this.renameFile);
|
|
226
|
-
if (!this.imageSettings.fileName) {
|
|
271
|
+
if (!this.imageSettings().fileName) {
|
|
227
272
|
this.reloadPath();
|
|
228
273
|
}
|
|
229
274
|
}
|
|
@@ -243,65 +288,31 @@ 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
|
}
|
|
271
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
272
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
294
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: CropperComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
295
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: CropperComponent, isStandalone: true, selector: "app-cropper", inputs: { imageSettings: { classPropertyName: "imageSettings", publicName: "imageSettings", isSignal: true, isRequired: false, transformFunction: null }, ratioType: { classPropertyName: "ratioType", publicName: "ratioType", isSignal: true, isRequired: false, transformFunction: null }, resolutions: { classPropertyName: "resolutions", publicName: "resolutions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { imageUploaded: "imageUploaded", onImageCropped: "onImageCropped", onFileSelected: "onFileSelected" }, viewQueries: [{ propertyName: "imageCropper", first: true, predicate: ImageCropperComponent, descendants: true }], ngImport: i0, template: "<div> path: {{ storagePath }} </div>\n\n<div class=\"options\">\n @if (!isUploaded) {\n <div>\n <input type=\"file\" id=\"file-upload\" class=\"file-input\" (change)=\"fileChangeEvent($event)\" />\n <label for=\"file-upload\" class=\"btn-upload\">Seleccionar archivo</label>\n @if (!fileMetadata) {\n <em>Carga una imagen para comenzar</em>\n }\n </div>\n }\n\n @if (fileMetadata) {\n <span>\n <span style=\"margin: 1px 20px\"> tipo: {{ fileMetadata.type }} </span>\n <span style=\"margin: 1px 20px\"> tama\u00F1o {{ fileMetadata.size }} </span>\n <br />\n <input\n [disabled]=\"imageSettings()?.fileName\"\n style=\"margin: 1px 20px; width: 400px\"\n [(ngModel)]=\"renameFile\"\n type=\"text\"\n placeholder=\"Rename File\"\n (ngModelChange)=\"reloadPath()\" />\n <button class=\"btn-crop\" (click)=\"closeModal()\"> Recortar y Subir </button>\n </span>\n }\n</div>\n\n@if (fileMetadata && !isUploaded) {\n <div class=\"modal\" [class.show-modal]=\"showModal\">\n <div class=\"modal-content\">\n <div class=\"modal-header\">\n <h3>Recortar imagen</h3>\n <button class=\"close-button\" (click)=\"closeModal()\">\u00D7</button>\n </div>\n <div class=\"modal-body\">\n <h1>Hlloa</h1>\n <image-cropper\n [imageChangedEvent]=\"imageChangedEvent\"\n [maintainAspectRatio]=\"true\"\n [aspectRatio]=\"aspectRatio\"\n format=\"webp\"\n [resizeToWidth]=\"450\"\n (imageCropped)=\"onInnerImageCropped($event)\"\n (loadImageFailed)=\"loadImageFailed()\"\n (imageLoaded)=\"imageLoaded($event)\"\n [autoCrop]=\"false\"></image-cropper>\n <div class=\"modal-footer\">\n <button class=\"btn-crop\" (click)=\"simpleCropAndUpload()\">Recortar y Subir</button>\n </div>\n </div>\n </div>\n </div>\n}\n\n@if (croppedImage && !isUploaded) {\n <button [disabled]=\"isLoading\" nbButton status=\"info\"> upload </button>\n}\n", styles: [".options{display:flex}.btn-crop{cursor:pointer;outline:0;display:inline-block;font-weight:400;line-height:1.5;text-align:center;background-color:transparent;border:1px solid transparent;padding:6px 12px;font-size:1rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;color:#0d6efd;border-color:#0d6efd}.btn-crop :hover{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.file-input{display:none}.btn-upload{cursor:pointer;outline:0;display:inline-block;font-weight:400;line-height:1.5;text-align:center;background-color:transparent;border:1px solid #0d6efd;padding:6px 12px;font-size:1rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;color:#0d6efd}.btn-upload:hover{color:#fff;background-color:#0d6efd}.modal{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background-color:#00000080;z-index:1000}.modal.show-modal{display:flex;align-items:center;justify-content:center}.modal .modal-content{background-color:#fff;border-radius:8px;width:90%;max-width:800px;max-height:90vh;overflow-y:auto;position:relative}.modal .modal-header{padding:1rem;border-bottom:1px solid #dee2e6;display:flex;justify-content:space-between;align-items:center}.modal .modal-header h3{margin:0}.modal .modal-header .close-button{background:none;border:none;font-size:1.5rem;cursor:pointer;padding:0;color:#6c757d}.modal .modal-header .close-button:hover{color:#343a40}.modal .modal-body{padding:1rem}.modal .modal-footer{padding:1rem;border-top:1px solid #dee2e6;display:flex;justify-content:flex-end}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ImageCropperComponent, selector: "image-cropper", inputs: ["imageChangedEvent", "imageURL", "imageBase64", "imageFile", "imageAltText", "options", "cropperFrameAriaLabel", "output", "format", "autoCrop", "cropper", "transform", "maintainAspectRatio", "aspectRatio", "resetCropOnAspectRatioChange", "resizeToWidth", "resizeToHeight", "cropperMinWidth", "cropperMinHeight", "cropperMaxHeight", "cropperMaxWidth", "cropperStaticWidth", "cropperStaticHeight", "canvasRotation", "initialStepSize", "roundCropper", "onlyScaleDown", "imageQuality", "backgroundColor", "containWithinAspectRatio", "hideResizeSquares", "allowMoveImage", "checkImageType", "alignImage", "disabled", "hidden"], outputs: ["imageCropped", "startCropImage", "imageLoaded", "cropperReady", "loadImageFailed", "transformChange", "cropperChange"] }] }); }
|
|
273
296
|
}
|
|
274
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
297
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: CropperComponent, decorators: [{
|
|
275
298
|
type: Component,
|
|
276
|
-
args: [{ selector: 'app-cropper', standalone: true, imports: [
|
|
277
|
-
}],
|
|
278
|
-
type: Input
|
|
279
|
-
}], ratioType: [{
|
|
280
|
-
type: Input
|
|
281
|
-
}], resolutions: [{
|
|
282
|
-
type: Input
|
|
283
|
-
}], imageUploaded: [{
|
|
284
|
-
type: Output
|
|
285
|
-
}], onImageCropped: [{
|
|
286
|
-
type: Output
|
|
287
|
-
}], onFileSelected: [{
|
|
288
|
-
type: Output
|
|
289
|
-
}], imageCropper: [{
|
|
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"] }]
|
|
300
|
+
}], propDecorators: { imageCropper: [{
|
|
290
301
|
type: ViewChild,
|
|
291
302
|
args: [ImageCropperComponent]
|
|
292
303
|
}] } });
|
|
293
304
|
|
|
294
305
|
class CropperComponentModal {
|
|
295
|
-
constructor(
|
|
296
|
-
this.multiImagesStorageService =
|
|
297
|
-
this.changeDetectorRef =
|
|
306
|
+
constructor() {
|
|
307
|
+
this.multiImagesStorageService = inject(MultiImagesStorageService);
|
|
308
|
+
this.changeDetectorRef = inject(ChangeDetectorRef);
|
|
298
309
|
// overrides name, path and resizeToWidth
|
|
299
|
-
this.imgStorageSettings = DEFAULT_SETTINGS;
|
|
300
|
-
this.buttonLabel = 'Seleccionar archivo';
|
|
310
|
+
this.imgStorageSettings = input(DEFAULT_SETTINGS);
|
|
311
|
+
this.buttonLabel = input('Seleccionar archivo');
|
|
301
312
|
this.currentStorage = {};
|
|
302
|
-
this.imageUploaded =
|
|
303
|
-
this.onImageCropped =
|
|
304
|
-
this.onFileSelected =
|
|
313
|
+
this.imageUploaded = output();
|
|
314
|
+
this.onImageCropped = output();
|
|
315
|
+
this.onFileSelected = output();
|
|
305
316
|
this.aspectRatioOptions = AspectRatioOptions;
|
|
306
317
|
this.fileMetadata = null;
|
|
307
318
|
this.displayDialog = false;
|
|
@@ -315,38 +326,40 @@ class CropperComponentModal {
|
|
|
315
326
|
this.fileInputId = `file-upload-${Math.random().toString(36).substring(2, 11)}`;
|
|
316
327
|
}
|
|
317
328
|
ngOnInit() {
|
|
318
|
-
if (!this.imgStorageSettings.path) {
|
|
329
|
+
if (!this.imgStorageSettings().path) {
|
|
319
330
|
console.warn('⚠️ Remember to set imgStorageSettings, path and fileName are required , path example: /collection/id/subcollection ');
|
|
320
331
|
}
|
|
321
332
|
this.reloadPath();
|
|
322
333
|
}
|
|
323
334
|
reloadPath() {
|
|
324
335
|
const randomCharacters = Math.random().toString(36).substring(2, 15);
|
|
325
|
-
|
|
336
|
+
const fileName = this.imgStorageSettings().fileName || 'img';
|
|
337
|
+
this.storagePath = `${this.imgStorageSettings().path}/${fileName}-${randomCharacters}.webp`;
|
|
326
338
|
}
|
|
327
339
|
setSettingsForComponent() {
|
|
328
|
-
|
|
340
|
+
const imgStorageSettings = this.imgStorageSettings();
|
|
341
|
+
console.log('setSettingsForComponent', imgStorageSettings);
|
|
329
342
|
// TODO: remove all the imageSettings and keep only imgStorageSettings
|
|
330
|
-
this.aspectRatioValue =
|
|
331
|
-
? AspectRatio[
|
|
343
|
+
this.aspectRatioValue = imgStorageSettings?.cropSettings?.aspectRatio
|
|
344
|
+
? AspectRatio[imgStorageSettings?.cropSettings?.aspectRatio]
|
|
332
345
|
: AspectRatio[AspectType.Square];
|
|
333
|
-
if (
|
|
334
|
-
this.storagePath = `${
|
|
346
|
+
if (imgStorageSettings?.path) {
|
|
347
|
+
this.storagePath = `${imgStorageSettings.path}/${imgStorageSettings.fileName}.webp`;
|
|
335
348
|
}
|
|
336
|
-
else if (
|
|
337
|
-
this.storagePath = `${
|
|
349
|
+
else if (imgStorageSettings.path) {
|
|
350
|
+
this.storagePath = `${imgStorageSettings.path}/${imgStorageSettings.fileName}.webp`;
|
|
338
351
|
}
|
|
339
|
-
if (
|
|
340
|
-
this.resizeToWidth =
|
|
352
|
+
if (imgStorageSettings?.cropSettings?.resizeToWidth) {
|
|
353
|
+
this.resizeToWidth = imgStorageSettings.cropSettings.resizeToWidth;
|
|
341
354
|
}
|
|
342
|
-
else if (
|
|
343
|
-
this.resizeToWidth =
|
|
355
|
+
else if (imgStorageSettings.cropSettings.resizeToWidth) {
|
|
356
|
+
this.resizeToWidth = imgStorageSettings.cropSettings.resizeToWidth;
|
|
344
357
|
}
|
|
345
|
-
if (
|
|
346
|
-
this.renameFile =
|
|
358
|
+
if (imgStorageSettings?.fileName) {
|
|
359
|
+
this.renameFile = imgStorageSettings.fileName;
|
|
347
360
|
}
|
|
348
|
-
else if (
|
|
349
|
-
this.renameFile =
|
|
361
|
+
else if (imgStorageSettings.fileName) {
|
|
362
|
+
this.renameFile = imgStorageSettings.fileName;
|
|
350
363
|
}
|
|
351
364
|
}
|
|
352
365
|
async fileChangeEvent(event) {
|
|
@@ -362,7 +375,7 @@ class CropperComponentModal {
|
|
|
362
375
|
.replace(/[^a-zA-Z0-9]/g, '')
|
|
363
376
|
.slice(0, 80);
|
|
364
377
|
console.log(this.renameFile);
|
|
365
|
-
if (!this.imgStorageSettings.fileName) {
|
|
378
|
+
if (!this.imgStorageSettings().fileName) {
|
|
366
379
|
this.reloadPath();
|
|
367
380
|
}
|
|
368
381
|
this.displayDialog = true;
|
|
@@ -383,12 +396,15 @@ class CropperComponentModal {
|
|
|
383
396
|
}
|
|
384
397
|
async simpleCropAndUpload() {
|
|
385
398
|
console.log(this.fileInputId);
|
|
399
|
+
debugger;
|
|
386
400
|
console.log('simpleCropAndUpload');
|
|
387
401
|
const imageCropped = await this.imageCropper.crop();
|
|
388
402
|
const imgStorage = await this.multiImagesStorageService.uploadImage(imageCropped?.blob, this.storagePath);
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
403
|
+
const path = extractPath(this.currentStorage);
|
|
404
|
+
if (path) {
|
|
405
|
+
console.warn('deleting current Image', path);
|
|
406
|
+
debugger;
|
|
407
|
+
this.multiImagesStorageService.deleteImage(path);
|
|
392
408
|
}
|
|
393
409
|
console.log('imgStorage', imgStorage);
|
|
394
410
|
this.imageUploaded.emit(imgStorage);
|
|
@@ -401,32 +417,152 @@ class CropperComponentModal {
|
|
|
401
417
|
this.aspectRatioValue = event.valueRatio;
|
|
402
418
|
this.changeDetectorRef.detectChanges();
|
|
403
419
|
}
|
|
404
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
405
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: CropperComponentModal, isStandalone: true, selector: "dc-cropper-modal", inputs: { imgStorageSettings: "imgStorageSettings", buttonLabel: "buttonLabel", currentStorage: "currentStorage" }, outputs: { imageUploaded: "imageUploaded", onImageCropped: "onImageCropped", onFileSelected: "onFileSelected" }, viewQueries: [{ propertyName: "imageCropper", first: true, predicate: ImageCropperComponent, descendants: true }], ngImport: i0, template: "<div class=\"upload-section\">\n <input type=\"file\" [id]=\"fileInputId\" class=\"file-input\" (change)=\"fileChangeEvent($event)\" />\n <label pButton [for]=\"fileInputId\" [pTooltip]=\"storagePath\" class=\"upload-button\">\n {{ buttonLabel }}\n </label>\n</div>\n@if(displayDialog) {\n<!-- Cropper Dialog -->\n\n<p-dialog header=\"Recortar imagen\" [(visible)]=\"displayDialog\" [modal]=\"true\" [draggable]=\"false\" [resizable]=\"false\" styleClass=\"cropper-dialog\">\n <!-- Image Settings Section -->\n <div class=\"settings-section\">\n @if(!imgStorageSettings.path) {\n <p-message severity=\"warn\">Developer Note: make sure you have a path to save the image pass object imgStorageSettings</p-message>\n } @if(currentStorage.url) {\n\n <p-message severity=\"warn\" variant=\"outlined\">\n <div>\n <span class=\"setting-label\">Image will be replaced:</span>\n <img width=\"100\" height=\"Auto\" [src]=\"currentStorage?.url\" />\n </div>\n </p-message>\n\n }\n\n <p-message>\n <b>Estas opciones estan preconfiguradas y no se pueden cambiar</b>\n\n <ul>\n <li> <b>Path to save:</b> {{ storagePath }}< </li>\n <li>\n <b>Resoluciones:</b>\n <span>{{ imgStorageSettings?.cropSettings?.resolutions }}</span>\n </li>\n </ul>\n </p-message>\n\n <div class=\"setting-item\">\n <span class=\"setting-label\">Aspecto:</span>\n <p class=\"setting-value\">{{ imgStorageSettings?.cropSettings?.aspectRatio }}</p>\n </div>\n\n <p-select\n [options]=\"aspectRatioOptions\"\n [ngModel]=\"ratioSelected\"\n (ngModelChange)=\"changeRatio($event)\"\n optionLabel=\"description\"\n placeholder=\"Select a ratio\" />\n\n <!-- File Metadata Section -->\n <div class=\"metadata-section\" *ngIf=\"fileMetadata\">\n <span class=\"metadata-item\">tipo: {{ fileMetadata.type }}</span>\n <span class=\"metadata-item\">tama\u00F1o: {{ fileMetadata.size }}</span>\n </div>\n\n <!-- Rename Input -->\n <input\n pInputText\n [disabled]=\"imgStorageSettings?.fileName\"\n [(ngModel)]=\"renameFile\"\n type=\"text\"\n placeholder=\"Rename File\"\n (ngModelChange)=\"reloadPath()\"\n class=\"rename-input\" />\n </div>\n\n <!-- Image Cropper -->\n\n <div class=\"cropper-container-father\">\n <image-cropper\n [imageChangedEvent]=\"imageChangedEvent\"\n [maintainAspectRatio]=\"true\"\n [aspectRatio]=\"aspectRatioValue\"\n format=\"webp\"\n [resizeToWidth]=\"resizeToWidth\"\n (imageCropped)=\"onInnerImageCropped($event)\"\n (loadImageFailed)=\"loadImageFailed()\"\n (cropperReady)=\"cropperReady()\"\n [autoCrop]=\"false\">\n </image-cropper>\n </div>\n <!-- Dialog Footer -->\n <ng-template pTemplate=\"footer\">\n <div class=\"dialog-footer\">\n <button pButton class=\"p-button-primary\" (click)=\"simpleCropAndUpload()\"> Recortar y Subir </button>\n </div>\n </ng-template>\n</p-dialog>\n}\n", styles: [":host{display:block}:host ::ng-deep .ngx-ic-overlay{width:100%!important}.upload-section{margin-bottom:1rem}.upload-section .file-input{display:none}.upload-section .upload-button{cursor:pointer}::ng-deep .cropper-dialog{max-width:90vw;width:800px}::ng-deep .cropper-dialog .p-dialog-content{padding:1.5rem}.settings-section{margin-bottom:1.5rem}.settings-section .settings-header{color:var(--text-color-secondary);margin-bottom:1rem}.settings-section .settings-grid{display:grid;gap:1rem;margin-bottom:1.5rem}.settings-section .settings-grid .setting-item .setting-label{font-weight:600;color:var(--text-color);display:block;margin-bottom:.5rem}.settings-section .settings-grid .setting-item .setting-value{color:var(--text-color-secondary);margin:0}.metadata-section{display:flex;gap:1.5rem;margin-bottom:1rem}.metadata-section .metadata-item{color:var(--text-color-secondary)}.rename-section{margin-bottom:5px}.rename-section .rename-input{width:100%;padding:.5rem;border:1px solid var(--surface-border);border-radius:4px}.rename-section .rename-input:disabled{background-color:var(--surface-200);cursor:not-allowed}.cropper-container-father{display:flex;justify-content:center;align-items:center;height:65vh}.dialog-footer{display:flex;justify-content:flex-end;gap:1rem}.btn-crop{cursor:pointer;outline:0;display:inline-block;font-weight:400;line-height:1.5;text-align:center;background-color:transparent;border:1px solid transparent;padding:6px 12px;font-size:1rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;color:#0d6efd;border-color:#0d6efd}.btn-crop :hover{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.file-input{display:none}.btn-upload{cursor:pointer;outline:0;display:inline-block;font-weight:400;line-height:1.5;text-align:center;background-color:transparent;border:1px solid #0d6efd;padding:6px 12px;font-size:1rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;color:#0d6efd}.btn-upload:hover{color:#fff;background-color:#0d6efd}.modal{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background-color:#00000080;z-index:1000}.modal.show-modal{display:flex;align-items:center;justify-content:center}.modal .modal-content{background-color:#fff;border-radius:8px;width:90%;max-width:800px;max-height:90vh;overflow-y:auto;position:relative}.modal .modal-header{padding:1rem;border-bottom:1px solid #dee2e6;display:flex;justify-content:space-between;align-items:center}.modal .modal-header h3{margin:0}.modal .modal-header .close-button{background:none;border:none;font-size:1.5rem;cursor:pointer;padding:0;color:#6c757d}.modal .modal-header .close-button:hover{color:#343a40}.modal .modal-body{padding:1rem;height:100vh;display:flex;flex-direction:column}.modal .modal-footer{padding:1rem;border-top:1px solid #dee2e6;display:flex;justify-content:flex-end}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ImageCropperComponent, selector: "image-cropper", inputs: ["imageChangedEvent", "imageURL", "imageBase64", "imageFile", "imageAltText", "options", "cropperFrameAriaLabel", "output", "format", "autoCrop", "cropper", "transform", "maintainAspectRatio", "aspectRatio", "resetCropOnAspectRatioChange", "resizeToWidth", "resizeToHeight", "cropperMinWidth", "cropperMinHeight", "cropperMaxHeight", "cropperMaxWidth", "cropperStaticWidth", "cropperStaticHeight", "canvasRotation", "initialStepSize", "roundCropper", "onlyScaleDown", "imageQuality", "backgroundColor", "containWithinAspectRatio", "hideResizeSquares", "allowMoveImage", "checkImageType", "alignImage", "disabled", "hidden"], outputs: ["imageCropped", "startCropImage", "imageLoaded", "cropperReady", "loadImageFailed", "transformChange", "cropperChange"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i3.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }, { kind: "directive", type: i4.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i5.Dialog, selector: "p-dialog", inputs: ["header", "draggable", "resizable", "positionLeft", "positionTop", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "responsive", "appendTo", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "breakpoint", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "ngmodule", type: MessageModule }, { kind: "component", type: i7.Message, selector: "p-message", inputs: ["severity", "text", "escape", "style", "styleClass", "closable", "icon", "closeIcon", "life", "showTransitionOptions", "hideTransitionOptions", "size", "variant"], outputs: ["onClose"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i8.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "size", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "fluid", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i9.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }] }); }
|
|
420
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: CropperComponentModal, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
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"] }] }); }
|
|
406
422
|
}
|
|
407
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
423
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: CropperComponentModal, decorators: [{
|
|
408
424
|
type: Component,
|
|
409
|
-
args: [{ selector: 'dc-cropper-modal', standalone: true, imports: [
|
|
410
|
-
}], ctorParameters: () => [
|
|
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"] }]
|
|
426
|
+
}], ctorParameters: () => [], propDecorators: { currentStorage: [{
|
|
411
427
|
type: Input
|
|
412
|
-
}], buttonLabel: [{
|
|
413
|
-
type: Input
|
|
414
|
-
}], currentStorage: [{
|
|
415
|
-
type: Input
|
|
416
|
-
}], imageUploaded: [{
|
|
417
|
-
type: Output
|
|
418
|
-
}], onImageCropped: [{
|
|
419
|
-
type: Output
|
|
420
|
-
}], onFileSelected: [{
|
|
421
|
-
type: Output
|
|
422
428
|
}], imageCropper: [{
|
|
423
429
|
type: ViewChild,
|
|
424
430
|
args: [ImageCropperComponent]
|
|
425
431
|
}] } });
|
|
426
432
|
|
|
433
|
+
class ImageStoragePreviewComponent {
|
|
434
|
+
constructor() {
|
|
435
|
+
this.storage = inject(AngularFireStorage);
|
|
436
|
+
this.imageSelected = output();
|
|
437
|
+
this.images$ = new BehaviorSubject([]);
|
|
438
|
+
this.loading$ = new BehaviorSubject(false);
|
|
439
|
+
this.error$ = new BehaviorSubject(null);
|
|
440
|
+
this.storagePath = '/images/resources';
|
|
441
|
+
this.subscriptions = [];
|
|
442
|
+
this.imgStorageSettings = {
|
|
443
|
+
path: this.storagePath,
|
|
444
|
+
cropSettings: {
|
|
445
|
+
aspectRatio: AspectType.Square,
|
|
446
|
+
resolutions: [ResolutionType.Small],
|
|
447
|
+
},
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
ngOnInit() {
|
|
451
|
+
this.loadImagesFromStorage();
|
|
452
|
+
}
|
|
453
|
+
ngOnDestroy() {
|
|
454
|
+
this.subscriptions.forEach((sub) => sub.unsubscribe());
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Loads images from Firebase Storage at the specified path
|
|
458
|
+
*/
|
|
459
|
+
async loadImagesFromStorage() {
|
|
460
|
+
try {
|
|
461
|
+
this.loading$.next(true);
|
|
462
|
+
this.error$.next(null);
|
|
463
|
+
const storage = getStorage$1();
|
|
464
|
+
const storageRef = ref(storage, this.storagePath);
|
|
465
|
+
const result = await listAll$1(storageRef);
|
|
466
|
+
if (result.items.length === 0) {
|
|
467
|
+
this.images$.next([]);
|
|
468
|
+
this.loading$.next(false);
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
const imagePromises = result.items.map(async (itemRef) => {
|
|
472
|
+
try {
|
|
473
|
+
const url = await getDownloadURL$1(itemRef);
|
|
474
|
+
const image = {
|
|
475
|
+
url,
|
|
476
|
+
// fullPath: itemRef.fullPath,
|
|
477
|
+
name: itemRef.name,
|
|
478
|
+
};
|
|
479
|
+
return image;
|
|
480
|
+
}
|
|
481
|
+
catch (error) {
|
|
482
|
+
console.error(`Error getting download URL for ${itemRef.fullPath}:`, error);
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
const images = (await Promise.all(imagePromises)).filter((img) => img !== null);
|
|
487
|
+
this.images$.next(images);
|
|
488
|
+
}
|
|
489
|
+
catch (error) {
|
|
490
|
+
console.error('Error loading images from storage:', error);
|
|
491
|
+
this.error$.next('Failed to load images from storage. Please try again later.');
|
|
492
|
+
}
|
|
493
|
+
finally {
|
|
494
|
+
this.loading$.next(false);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Refreshes the image list
|
|
499
|
+
*/
|
|
500
|
+
refreshImages() {
|
|
501
|
+
this.loadImagesFromStorage();
|
|
502
|
+
}
|
|
503
|
+
selectImage(image) {
|
|
504
|
+
this.imageSelected.emit(image);
|
|
505
|
+
}
|
|
506
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ImageStoragePreviewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
507
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: ImageStoragePreviewComponent, isStandalone: true, selector: "dc-image-storage-preview", outputs: { imageSelected: "imageSelected" }, ngImport: i0, template: "<div class=\"image-storage-preview-container\">\n <div class=\"header\">\n <h2>Storage Images</h2>\n <button class=\"refresh-btn\" (click)=\"refreshImages()\" [disabled]=\"loading$ | async\">\n @if (!(loading$ | async)) {\n <span>Refresh</span>\n }\n @if (loading$ | async) {\n <span>Loading...</span>\n }\n </button>\n </div>\n\n @if (loading$ | async) {\n <div class=\"loading-container\">\n <div class=\"spinner\"></div>\n <p>Loading images...</p>\n </div>\n }\n\n @if (error$ | async) {\n <div class=\"error-container\">\n <p class=\"error-message\">{{ error$ | async }}</p>\n <button (click)=\"refreshImages()\">Try Again</button>\n </div>\n }\n\n @if (!(loading$ | async) && !(error$ | async)) {\n <div class=\"images-grid\">\n @if ((images$ | async)?.length) {\n @for (image of images$ | async; track image) {\n <div class=\"image-card\">\n <div class=\"image-container\">\n <img [src]=\"image.url\" [alt]=\"image.name || 'Storage image'\" loading=\"lazy\" />\n </div>\n <div class=\"image-info\">\n <p class=\"image-name\" [title]=\"image.name\">{{ image.name }}</p>\n <div class=\"image-actions\">\n <a [href]=\"image.url\" target=\"_blank\" class=\"action-btn\">View</a>\n <button (click)=\"selectImage(image)\" class=\"action-btn\">Select</button>\n </div>\n </div>\n </div>\n }\n } @else {\n <div class=\"no-images\">\n <p>No images found in the storage path: {{ storagePath }}</p>\n </div>\n }\n <dc-cropper-modal [imgStorageSettings]=\"imgStorageSettings\"></dc-cropper-modal>\n </div>\n }\n</div>\n", styles: [".image-storage-preview-container{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Open Sans,Helvetica Neue,sans-serif;padding:20px;max-width:1200px;margin:0 auto}.header{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px}.header h2{margin:0;color:#333}.refresh-btn{background-color:#4285f4;color:#fff;border:none;border-radius:4px;padding:8px 16px;cursor:pointer;font-size:14px;transition:background-color .2s}.refresh-btn:hover{background-color:#3367d6}.refresh-btn:disabled{background-color:#a9a9a9;cursor:not-allowed}.loading-container{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px 0}.spinner{border:4px solid rgba(0,0,0,.1);border-radius:50%;border-top:4px solid #4285f4;width:30px;height:30px;animation:spin 1s linear infinite;margin-bottom:15px}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.error-container{background-color:#ffebee;border:1px solid #ffcdd2;border-radius:4px;padding:16px;margin:20px 0;text-align:center}.error-message{color:#d32f2f;margin-bottom:10px}.error-container button{background-color:#d32f2f;color:#fff;border:none;border-radius:4px;padding:8px 16px;cursor:pointer;font-size:14px}.images-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:20px}.image-card{border-radius:8px;overflow:hidden;box-shadow:0 2px 10px #0000001a;transition:transform .2s,box-shadow .2s;background-color:#fff}.image-card:hover{transform:translateY(-5px);box-shadow:0 5px 15px #00000026}.image-container{height:200px;overflow:hidden;position:relative;background-color:#f5f5f5}.image-container img{width:100%;height:100%;object-fit:cover;transition:transform .3s}.image-card:hover .image-container img{transform:scale(1.05)}.image-info{padding:15px}.image-name{margin:0 0 10px;font-size:14px;font-weight:500;color:#333;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.image-actions{display:flex;justify-content:flex-end}.action-btn{background-color:#4285f4;color:#fff;text-decoration:none;border-radius:4px;padding:6px 12px;font-size:12px;transition:background-color .2s}.action-btn:hover{background-color:#3367d6}.no-images{grid-column:1 / -1;text-align:center;padding:40px 0;color:#757575;background-color:#f5f5f5;border-radius:8px}\n"], dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "component", type: CropperComponentModal, selector: "dc-cropper-modal", inputs: ["imgStorageSettings", "buttonLabel", "currentStorage"], outputs: ["imageUploaded", "onImageCropped", "onFileSelected"] }] }); }
|
|
508
|
+
}
|
|
509
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ImageStoragePreviewComponent, decorators: [{
|
|
510
|
+
type: Component,
|
|
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"] }]
|
|
512
|
+
}], ctorParameters: () => [] });
|
|
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
|
+
|
|
427
563
|
class DCFilesCacheService {
|
|
428
|
-
constructor(
|
|
429
|
-
this.storage =
|
|
564
|
+
constructor() {
|
|
565
|
+
this.storage = inject(Storage);
|
|
430
566
|
this.files = {};
|
|
431
567
|
}
|
|
432
568
|
async getURLSrcFile(path) {
|
|
@@ -460,15 +596,113 @@ class DCFilesCacheService {
|
|
|
460
596
|
const blob = await this.getBlob(url);
|
|
461
597
|
return URL.createObjectURL(blob);
|
|
462
598
|
}
|
|
463
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
464
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.
|
|
599
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCFilesCacheService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
600
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCFilesCacheService, providedIn: 'root' }); }
|
|
601
|
+
}
|
|
602
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCFilesCacheService, decorators: [{
|
|
603
|
+
type: Injectable,
|
|
604
|
+
args: [{
|
|
605
|
+
providedIn: 'root',
|
|
606
|
+
}]
|
|
607
|
+
}], ctorParameters: () => [] });
|
|
608
|
+
|
|
609
|
+
class MultiObjectStorageService {
|
|
610
|
+
constructor() {
|
|
611
|
+
this.storage = inject(AngularFireStorage);
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* Uploads a Blob or File object to a specified path in Firebase Storage.
|
|
615
|
+
* @param objectToUpload The Blob or File to upload.
|
|
616
|
+
* @param path The desired storage path (e.g., 'documents/report.pdf').
|
|
617
|
+
* @returns A promise that resolves with the storage metadata of the uploaded object.
|
|
618
|
+
* @throws Throws an error if the upload fails.
|
|
619
|
+
*/
|
|
620
|
+
async uploadObject(objectToUpload, path) {
|
|
621
|
+
try {
|
|
622
|
+
const refStorage = this.storage.ref(path);
|
|
623
|
+
const task = await refStorage.put(objectToUpload);
|
|
624
|
+
const { fullPath, bucket, name } = task.metadata;
|
|
625
|
+
const url = await lastValueFrom(refStorage.getDownloadURL());
|
|
626
|
+
const storageData = { url, path: fullPath, bucket, name };
|
|
627
|
+
return storageData;
|
|
628
|
+
}
|
|
629
|
+
catch (error) {
|
|
630
|
+
console.error(`Error uploading object to path "${path}": `, error);
|
|
631
|
+
// Re-throw the error to allow calling code to handle it
|
|
632
|
+
throw error;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Deletes all objects within a specified directory path in Firebase Storage.
|
|
637
|
+
* WARNING: Use with extreme caution as this will permanently delete all files in the directory.
|
|
638
|
+
* @param directoryPath The path to the directory to delete (e.g., 'users/userId/files/').
|
|
639
|
+
* @returns A promise that resolves when the deletion attempt is complete.
|
|
640
|
+
*/
|
|
641
|
+
async deleteDirectory(directoryPath) {
|
|
642
|
+
const storage = getStorage();
|
|
643
|
+
const directoryRef = ref(storage, directoryPath);
|
|
644
|
+
try {
|
|
645
|
+
const res = await listAll(directoryRef);
|
|
646
|
+
const deletePromises = [];
|
|
647
|
+
res.items.forEach((itemRef) => {
|
|
648
|
+
console.log(`Deleting object: ${itemRef.fullPath}`);
|
|
649
|
+
deletePromises.push(deleteObject(itemRef));
|
|
650
|
+
});
|
|
651
|
+
// You might want to handle potential errors during individual deletions if needed
|
|
652
|
+
await Promise.all(deletePromises);
|
|
653
|
+
console.log(`Successfully deleted objects in directory: ${directoryPath}`);
|
|
654
|
+
}
|
|
655
|
+
catch (error) {
|
|
656
|
+
console.error(`Error deleting objects in directory "${directoryPath}":`, error);
|
|
657
|
+
// Optionally re-throw or handle the error appropriately
|
|
658
|
+
throw error;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Deletes a single object from Firebase Storage based on its full path.
|
|
663
|
+
* @param objectPath The full path of the object to delete (e.g., 'images/profile.jpg').
|
|
664
|
+
* @returns A promise that resolves when the deletion is complete.
|
|
665
|
+
*/
|
|
666
|
+
async deleteObjectByPath(objectPath) {
|
|
667
|
+
const storageRef = this.storage.ref(objectPath);
|
|
668
|
+
try {
|
|
669
|
+
await lastValueFrom(storageRef.delete());
|
|
670
|
+
console.log(`Object deleted successfully: ${objectPath}`);
|
|
671
|
+
}
|
|
672
|
+
catch (error) {
|
|
673
|
+
console.error(`Error deleting object at path "${objectPath}":`, error);
|
|
674
|
+
// Optionally re-throw or handle the error appropriately
|
|
675
|
+
throw error;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Private helper to get metadata after an upload task completes.
|
|
680
|
+
* @param task The AngularFireUploadTask.
|
|
681
|
+
* @returns A promise resolving with the object's storage metadata.
|
|
682
|
+
*/
|
|
683
|
+
async uploadAndGetObjectMetadata(task) {
|
|
684
|
+
const snap = await task;
|
|
685
|
+
const { fullPath, bucket, name } = snap.metadata;
|
|
686
|
+
const storage = getStorage();
|
|
687
|
+
const storageRef = ref(storage, fullPath);
|
|
688
|
+
const url = await getDownloadURL(storageRef);
|
|
689
|
+
const meta = {
|
|
690
|
+
url,
|
|
691
|
+
path: fullPath, // Use fullPath for the 'path' property
|
|
692
|
+
bucket,
|
|
693
|
+
name,
|
|
694
|
+
};
|
|
695
|
+
return meta;
|
|
696
|
+
}
|
|
697
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MultiObjectStorageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
698
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MultiObjectStorageService, providedIn: 'root' }); }
|
|
465
699
|
}
|
|
466
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
700
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MultiObjectStorageService, decorators: [{
|
|
467
701
|
type: Injectable,
|
|
468
702
|
args: [{
|
|
469
703
|
providedIn: 'root',
|
|
470
704
|
}]
|
|
471
|
-
}]
|
|
705
|
+
}] });
|
|
472
706
|
|
|
473
707
|
/*
|
|
474
708
|
* Public API Surface of storage-uploader
|
|
@@ -478,5 +712,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
|
|
|
478
712
|
* Generated bundle index. Do not edit.
|
|
479
713
|
*/
|
|
480
714
|
|
|
481
|
-
export { AspectRatio, AspectRatio2, AspectRatioOptions, AspectType, CropperComponent, CropperComponentModal, DCFilesCacheService, DEFAULT_SETTINGS, MultiImagesStorageService, ResolutionType };
|
|
715
|
+
export { AspectRatio, AspectRatio2, AspectRatioOptions, AspectType, CropperComponent, CropperComponentModal, DCFilesCacheService, DEFAULT_SETTINGS, ImageStoragePreviewComponent, MultiImagesStorageService, MultiObjectStorageService, ResolutionType, SimpleUploaderComponent, extractBucket, extractPath };
|
|
482
716
|
//# sourceMappingURL=dataclouder-ngx-cloud-storage.mjs.map
|