@dataclouder/ngx-cloud-storage 0.0.21
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/README.md +24 -0
- package/fesm2022/dataclouder-ngx-cloud-storage.mjs +482 -0
- package/fesm2022/dataclouder-ngx-cloud-storage.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/lib/classes/cropper.classes.d.ts +80 -0
- package/lib/components/cropper/cropper.component.d.ts +38 -0
- package/lib/components/cropper-modal/cropper-modal.component.d.ts +42 -0
- package/lib/services/dc-files-cache.service.d.ts +14 -0
- package/lib/services/multi-images-storage.service.d.ts +14 -0
- package/package.json +30 -0
- package/public-api.d.ts +5 -0
package/README.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# StorageUploader
|
|
2
|
+
|
|
3
|
+
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.3.0.
|
|
4
|
+
|
|
5
|
+
## Code scaffolding
|
|
6
|
+
|
|
7
|
+
Run `ng generate component component-name --project storage-uploader` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project storage-uploader`.
|
|
8
|
+
> Note: Don't forget to add `--project storage-uploader` or else it will be added to the default project in your `angular.json` file.
|
|
9
|
+
|
|
10
|
+
## Build
|
|
11
|
+
|
|
12
|
+
Run `ng build storage-uploader` to build the project. The build artifacts will be stored in the `dist/` directory.
|
|
13
|
+
|
|
14
|
+
## Publishing
|
|
15
|
+
|
|
16
|
+
After building your library with `ng build storage-uploader`, go to the dist folder `cd dist/storage-uploader` and run `npm publish`.
|
|
17
|
+
|
|
18
|
+
## Running unit tests
|
|
19
|
+
|
|
20
|
+
Run `ng test storage-uploader` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
|
21
|
+
|
|
22
|
+
## Further help
|
|
23
|
+
|
|
24
|
+
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
|
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { Injectable, EventEmitter, Component, Input, Output, ViewChild } from '@angular/core';
|
|
3
|
+
import { ImageCropperComponent } from 'ngx-image-cropper';
|
|
4
|
+
import { NgIf } from '@angular/common';
|
|
5
|
+
import * as i2 from '@angular/forms';
|
|
6
|
+
import { FormsModule } from '@angular/forms';
|
|
7
|
+
import * as i1$1 from '@angular/fire/storage';
|
|
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 i1 from '@angular/fire/compat/storage';
|
|
12
|
+
import * as i5 from 'primeng/dialog';
|
|
13
|
+
import { DialogModule } from 'primeng/dialog';
|
|
14
|
+
import * as i6 from 'primeng/tooltip';
|
|
15
|
+
import { TooltipModule } from 'primeng/tooltip';
|
|
16
|
+
import * as i3 from 'primeng/button';
|
|
17
|
+
import { ButtonModule } from 'primeng/button';
|
|
18
|
+
import * as i7 from 'primeng/message';
|
|
19
|
+
import { MessageModule } from 'primeng/message';
|
|
20
|
+
import * as i8 from 'primeng/select';
|
|
21
|
+
import { SelectModule } from 'primeng/select';
|
|
22
|
+
import * as i9 from 'primeng/inputtext';
|
|
23
|
+
import { InputTextModule } from 'primeng/inputtext';
|
|
24
|
+
import * as i4 from 'primeng/api';
|
|
25
|
+
|
|
26
|
+
const AspectRatioOptions = [
|
|
27
|
+
{ value: '1:1', label: 'square', description: 'Square (1:1)', valueRatio: 1 / 1 },
|
|
28
|
+
{ value: '1:2', label: 'vertical_1_2', description: 'Vertical (1:2)', valueRatio: 1 / 2 },
|
|
29
|
+
{ value: '2:3', label: 'vertical_2_3', description: 'Vertical (2:3)', valueRatio: 2 / 3 },
|
|
30
|
+
{ value: '3:4', label: 'vertical_3_4', description: 'Vertical (3:4)', valueRatio: 3 / 4 },
|
|
31
|
+
{ value: '4:5', label: 'vertical_4_5', description: 'Vertical (4:5)', valueRatio: 4 / 5 },
|
|
32
|
+
{ value: '9:16', label: 'vertical_9_16', description: 'Vertical (9:16)', valueRatio: 9 / 16 },
|
|
33
|
+
{ value: '2:1', label: 'horizontal_2_1', description: 'Horizontal (2:1)', valueRatio: 2 / 1 },
|
|
34
|
+
{ value: '3:2', label: 'horizontal_3_2', description: 'Horizontal (3:2)', valueRatio: 3 / 2 },
|
|
35
|
+
{ value: '4:3', label: 'horizontal_4_3', description: 'Horizontal (4:3)', valueRatio: 4 / 3 },
|
|
36
|
+
{ value: '5:4', label: 'horizontal_5_4', description: 'Horizontal (5:4)', valueRatio: 5 / 4 },
|
|
37
|
+
{ value: '16:9', label: 'horizontal_16_9', description: 'Horizontal (16:9)', valueRatio: 16 / 9 },
|
|
38
|
+
];
|
|
39
|
+
var AspectType;
|
|
40
|
+
(function (AspectType) {
|
|
41
|
+
AspectType["Square"] = "square";
|
|
42
|
+
AspectType["Rectangle"] = "rectangle";
|
|
43
|
+
AspectType["Banner"] = "banner";
|
|
44
|
+
AspectType["RectangleLarge"] = "rectangleLarge";
|
|
45
|
+
AspectType["Vertical_9_16"] = "vertical_9_16";
|
|
46
|
+
AspectType["Vertical_3_5"] = "vertical_3_5";
|
|
47
|
+
AspectType["Vertical_2_3"] = "vertical_2_3";
|
|
48
|
+
})(AspectType || (AspectType = {}));
|
|
49
|
+
// Creo que seria bueno refactorizar para solo enviar aspect ratio asi
|
|
50
|
+
var AspectRatio2;
|
|
51
|
+
(function (AspectRatio2) {
|
|
52
|
+
// Vertical
|
|
53
|
+
AspectRatio2[AspectRatio2["9:16"] = 1.7777777777777777] = "9:16";
|
|
54
|
+
AspectRatio2[AspectRatio2["3:5"] = 0.6] = "3:5";
|
|
55
|
+
AspectRatio2[AspectRatio2["5:8"] = 0.625] = "5:8";
|
|
56
|
+
AspectRatio2[AspectRatio2["2:3"] = 0.6666666666666666] = "2:3";
|
|
57
|
+
AspectRatio2[AspectRatio2["1:1"] = 1] = "1:1";
|
|
58
|
+
})(AspectRatio2 || (AspectRatio2 = {}));
|
|
59
|
+
var ResolutionType;
|
|
60
|
+
(function (ResolutionType) {
|
|
61
|
+
ResolutionType[ResolutionType["Small"] = 200] = "Small";
|
|
62
|
+
ResolutionType[ResolutionType["Medium"] = 400] = "Medium";
|
|
63
|
+
ResolutionType[ResolutionType["MediumLarge"] = 800] = "MediumLarge";
|
|
64
|
+
ResolutionType[ResolutionType["Large"] = 1200] = "Large";
|
|
65
|
+
ResolutionType[ResolutionType["VeryLarge"] = 1600] = "VeryLarge";
|
|
66
|
+
})(ResolutionType || (ResolutionType = {}));
|
|
67
|
+
const AspectRatio = {
|
|
68
|
+
[AspectType.Square]: 1 / 1,
|
|
69
|
+
[AspectType.Rectangle]: 16 / 9,
|
|
70
|
+
[AspectType.Vertical_9_16]: 9 / 16,
|
|
71
|
+
[AspectType.RectangleLarge]: 16 / 8,
|
|
72
|
+
[AspectType.Banner]: 16 / 7,
|
|
73
|
+
[AspectType.Vertical_3_5]: 3 / 5,
|
|
74
|
+
[AspectType.Vertical_2_3]: 2 / 3,
|
|
75
|
+
};
|
|
76
|
+
const DEFAULT_SETTINGS = {
|
|
77
|
+
path: '/default-collection/id/please-change-this-subcollection',
|
|
78
|
+
fileName: 'image',
|
|
79
|
+
cropSettings: {
|
|
80
|
+
aspectRatio: AspectType.Square,
|
|
81
|
+
resolutions: [ResolutionType.Small, ResolutionType.MediumLarge],
|
|
82
|
+
resizeToWidth: 450,
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
class MultiImagesStorageService {
|
|
87
|
+
constructor(storage) {
|
|
88
|
+
this.storage = storage;
|
|
89
|
+
}
|
|
90
|
+
async uploadImage(image, path) {
|
|
91
|
+
try {
|
|
92
|
+
const refStorage = this.storage.ref(path);
|
|
93
|
+
const task = await refStorage.put(image);
|
|
94
|
+
const { fullPath, bucket } = task.metadata;
|
|
95
|
+
const url = await lastValueFrom(refStorage.getDownloadURL());
|
|
96
|
+
const imageStorage = { url, path: fullPath, bucket };
|
|
97
|
+
return imageStorage;
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
console.error('uploading image error: ', error);
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
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
|
+
async delete_directory(imagePath) {
|
|
142
|
+
// WARNING!! user very carefully could delete whatever folder
|
|
143
|
+
const storage = getStorage();
|
|
144
|
+
const directoryRef = ref(storage, imagePath);
|
|
145
|
+
listAll(directoryRef)
|
|
146
|
+
.then((res) => {
|
|
147
|
+
res.items.forEach((itemRef) => {
|
|
148
|
+
console.log(itemRef);
|
|
149
|
+
deleteObject(itemRef);
|
|
150
|
+
});
|
|
151
|
+
})
|
|
152
|
+
.catch((error) => {
|
|
153
|
+
console.error('error al eliminar imagenes de cloud storage', error);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
async deleteImage(imagePath) {
|
|
157
|
+
const storageRef = this.storage.ref(imagePath);
|
|
158
|
+
storageRef.delete().subscribe((res) => {
|
|
159
|
+
console.log('image deleted', res);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
async uploadAndGetUrl(task, resolution) {
|
|
163
|
+
const snap = await task;
|
|
164
|
+
const { fullPath, bucket, name } = snap.metadata;
|
|
165
|
+
const storage = getStorage();
|
|
166
|
+
const storageRef = ref(storage, fullPath);
|
|
167
|
+
const url = await getDownloadURL(storageRef);
|
|
168
|
+
const meta = {
|
|
169
|
+
url,
|
|
170
|
+
fullPath,
|
|
171
|
+
bucket,
|
|
172
|
+
name,
|
|
173
|
+
resolution,
|
|
174
|
+
path: '',
|
|
175
|
+
resolutions: {},
|
|
176
|
+
};
|
|
177
|
+
return meta;
|
|
178
|
+
}
|
|
179
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MultiImagesStorageService, deps: [{ token: i1.AngularFireStorage }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
180
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MultiImagesStorageService, providedIn: 'root' }); }
|
|
181
|
+
}
|
|
182
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MultiImagesStorageService, decorators: [{
|
|
183
|
+
type: Injectable,
|
|
184
|
+
args: [{
|
|
185
|
+
providedIn: 'root',
|
|
186
|
+
}]
|
|
187
|
+
}], ctorParameters: () => [{ type: i1.AngularFireStorage }] });
|
|
188
|
+
|
|
189
|
+
class CropperComponent {
|
|
190
|
+
constructor(multiImagesStorageService) {
|
|
191
|
+
this.multiImagesStorageService = multiImagesStorageService;
|
|
192
|
+
// overrides name, path and resizeToWidth
|
|
193
|
+
this.imageSettings = {};
|
|
194
|
+
this.ratioType = AspectType.Square;
|
|
195
|
+
this.resolutions = [ResolutionType.MediumLarge];
|
|
196
|
+
this.imageUploaded = new EventEmitter();
|
|
197
|
+
this.onImageCropped = new EventEmitter();
|
|
198
|
+
this.onFileSelected = new EventEmitter();
|
|
199
|
+
this.fileMetadata = null;
|
|
200
|
+
this.aspectRatio = 1;
|
|
201
|
+
this.croppedImage = '';
|
|
202
|
+
this.isLoading = false;
|
|
203
|
+
this.isUploaded = false;
|
|
204
|
+
this.renameFile = '';
|
|
205
|
+
this.storagePath = '';
|
|
206
|
+
this.showModal = false;
|
|
207
|
+
}
|
|
208
|
+
ngOnInit() {
|
|
209
|
+
this.aspectRatio = AspectRatio[this.ratioType];
|
|
210
|
+
if (this.imageSettings.path) {
|
|
211
|
+
this.storagePath = `${this.imageSettings.path}/${this.imageSettings.fileName}.webp`;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
reloadPath() {
|
|
215
|
+
this.storagePath = `${this.imageSettings.path}/${this.renameFile}.webp`;
|
|
216
|
+
}
|
|
217
|
+
async fileChangeEvent(event) {
|
|
218
|
+
this.imageChangedEvent = event;
|
|
219
|
+
const file = event?.target?.files[0];
|
|
220
|
+
if (file) {
|
|
221
|
+
this.fileMetadata = file;
|
|
222
|
+
this.onFileSelected.emit(file);
|
|
223
|
+
this.showModal = true; // Show modal when file is selected
|
|
224
|
+
this.renameFile = this.fileMetadata?.name?.split('.')[0];
|
|
225
|
+
console.log(this.renameFile);
|
|
226
|
+
if (!this.imageSettings.fileName) {
|
|
227
|
+
this.reloadPath();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
onInnerImageCropped(event) {
|
|
232
|
+
this.croppedImage = event.base64;
|
|
233
|
+
}
|
|
234
|
+
imageLoaded(image) {
|
|
235
|
+
// show cropper
|
|
236
|
+
}
|
|
237
|
+
loadImageFailed() {
|
|
238
|
+
console.error('fallo al cargar la imagen');
|
|
239
|
+
}
|
|
240
|
+
async simpleCropAndUpload() {
|
|
241
|
+
const imageCropped = await this.imageCropper.crop();
|
|
242
|
+
const imgStorage = await this.multiImagesStorageService.uploadImage(imageCropped?.blob, this.storagePath);
|
|
243
|
+
this.imageUploaded.emit(imgStorage);
|
|
244
|
+
this.closeModal();
|
|
245
|
+
}
|
|
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
|
+
closeModal() {
|
|
269
|
+
this.showModal = false;
|
|
270
|
+
}
|
|
271
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: CropperComponent, deps: [{ token: MultiImagesStorageService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
272
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type: CropperComponent, isStandalone: true, selector: "app-cropper", inputs: { imageSettings: "imageSettings", ratioType: "ratioType", resolutions: "resolutions" }, 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 <div *ngIf=\"!isUploaded\">\n <input type=\"file\" id=\"file-upload\" class=\"file-input\" (change)=\"fileChangeEvent($event)\" />\n <label for=\"file-upload\" class=\"btn-upload\">Seleccionar archivo</label>\n <em *ngIf=\"!fileMetadata\">Carga una imagen para comenzar</em>\n </div>\n\n <span *ngIf=\"fileMetadata\">\n <span style=\"margin: 1px 20px\"> tipo: {{ fileMetadata.type }} </span>\n\n <span style=\"margin: 1px 20px\"> tama\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</div>\n\n<div class=\"modal\" *ngIf=\"fileMetadata && !isUploaded\" [class.show-modal]=\"showModal\">\n <div class=\"modal-content\">\n <div class=\"modal-header\">\n <h3>Recortar imagen</h3>\n <button class=\"close-button\" (click)=\"closeModal()\">\u00D7</button>\n </div>\n <div class=\"modal-body\">\n <h1>Hlloa</h1>\n\n <image-cropper\n [imageChangedEvent]=\"imageChangedEvent\"\n [maintainAspectRatio]=\"true\"\n [aspectRatio]=\"aspectRatio\"\n format=\"webp\"\n [resizeToWidth]=\"450\"\n (imageCropped)=\"onInnerImageCropped($event)\"\n (loadImageFailed)=\"loadImageFailed()\"\n (imageLoaded)=\"imageLoaded($event)\"\n [autoCrop]=\"false\"></image-cropper>\n\n <div class=\"modal-footer\">\n <button class=\"btn-crop\" (click)=\"simpleCropAndUpload()\">Recortar y Subir</button>\n </div>\n </div>\n </div>\n</div>\n\n<button *ngIf=\"croppedImage && !isUploaded\" [disabled]=\"isLoading\" nbButton status=\"info\"> upload </button>\n", 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: "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"] }] }); }
|
|
273
|
+
}
|
|
274
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: CropperComponent, decorators: [{
|
|
275
|
+
type: Component,
|
|
276
|
+
args: [{ selector: 'app-cropper', standalone: true, imports: [NgIf, FormsModule, ImageCropperComponent], template: "<div> path: {{ storagePath }} </div>\n\n<div class=\"options\">\n <div *ngIf=\"!isUploaded\">\n <input type=\"file\" id=\"file-upload\" class=\"file-input\" (change)=\"fileChangeEvent($event)\" />\n <label for=\"file-upload\" class=\"btn-upload\">Seleccionar archivo</label>\n <em *ngIf=\"!fileMetadata\">Carga una imagen para comenzar</em>\n </div>\n\n <span *ngIf=\"fileMetadata\">\n <span style=\"margin: 1px 20px\"> tipo: {{ fileMetadata.type }} </span>\n\n <span style=\"margin: 1px 20px\"> tama\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</div>\n\n<div class=\"modal\" *ngIf=\"fileMetadata && !isUploaded\" [class.show-modal]=\"showModal\">\n <div class=\"modal-content\">\n <div class=\"modal-header\">\n <h3>Recortar imagen</h3>\n <button class=\"close-button\" (click)=\"closeModal()\">\u00D7</button>\n </div>\n <div class=\"modal-body\">\n <h1>Hlloa</h1>\n\n <image-cropper\n [imageChangedEvent]=\"imageChangedEvent\"\n [maintainAspectRatio]=\"true\"\n [aspectRatio]=\"aspectRatio\"\n format=\"webp\"\n [resizeToWidth]=\"450\"\n (imageCropped)=\"onInnerImageCropped($event)\"\n (loadImageFailed)=\"loadImageFailed()\"\n (imageLoaded)=\"imageLoaded($event)\"\n [autoCrop]=\"false\"></image-cropper>\n\n <div class=\"modal-footer\">\n <button class=\"btn-crop\" (click)=\"simpleCropAndUpload()\">Recortar y Subir</button>\n </div>\n </div>\n </div>\n</div>\n\n<button *ngIf=\"croppedImage && !isUploaded\" [disabled]=\"isLoading\" nbButton status=\"info\"> upload </button>\n", 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: () => [{ type: MultiImagesStorageService }], propDecorators: { imageSettings: [{
|
|
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: [{
|
|
290
|
+
type: ViewChild,
|
|
291
|
+
args: [ImageCropperComponent]
|
|
292
|
+
}] } });
|
|
293
|
+
|
|
294
|
+
class CropperComponentModal {
|
|
295
|
+
constructor(multiImagesStorageService, changeDetectorRef) {
|
|
296
|
+
this.multiImagesStorageService = multiImagesStorageService;
|
|
297
|
+
this.changeDetectorRef = changeDetectorRef;
|
|
298
|
+
// overrides name, path and resizeToWidth
|
|
299
|
+
this.imgStorageSettings = DEFAULT_SETTINGS;
|
|
300
|
+
this.buttonLabel = 'Seleccionar archivo';
|
|
301
|
+
this.currentStorage = {};
|
|
302
|
+
this.imageUploaded = new EventEmitter();
|
|
303
|
+
this.onImageCropped = new EventEmitter();
|
|
304
|
+
this.onFileSelected = new EventEmitter();
|
|
305
|
+
this.aspectRatioOptions = AspectRatioOptions;
|
|
306
|
+
this.fileMetadata = null;
|
|
307
|
+
this.displayDialog = false;
|
|
308
|
+
this.aspectRatioValue = 1;
|
|
309
|
+
this.croppedImage = '';
|
|
310
|
+
this.renameFile = '';
|
|
311
|
+
this.storagePath = '';
|
|
312
|
+
this.resizeToWidth = 450;
|
|
313
|
+
this.ratioSelected = null;
|
|
314
|
+
// Generate random ID for file input
|
|
315
|
+
this.fileInputId = `file-upload-${Math.random().toString(36).substring(2, 11)}`;
|
|
316
|
+
}
|
|
317
|
+
ngOnInit() {
|
|
318
|
+
if (!this.imgStorageSettings.path) {
|
|
319
|
+
console.warn('⚠️ Remember to set imgStorageSettings, path and fileName are required , path example: /collection/id/subcollection ');
|
|
320
|
+
}
|
|
321
|
+
this.reloadPath();
|
|
322
|
+
}
|
|
323
|
+
reloadPath() {
|
|
324
|
+
const randomCharacters = Math.random().toString(36).substring(2, 15);
|
|
325
|
+
this.storagePath = `${this.imgStorageSettings.path}/${this.imgStorageSettings.fileName}-${randomCharacters}.webp`;
|
|
326
|
+
}
|
|
327
|
+
setSettingsForComponent() {
|
|
328
|
+
console.log('setSettingsForComponent', this.imgStorageSettings);
|
|
329
|
+
// TODO: remove all the imageSettings and keep only imgStorageSettings
|
|
330
|
+
this.aspectRatioValue = this.imgStorageSettings?.cropSettings?.aspectRatio
|
|
331
|
+
? AspectRatio[this.imgStorageSettings?.cropSettings?.aspectRatio]
|
|
332
|
+
: AspectRatio[AspectType.Square];
|
|
333
|
+
if (this.imgStorageSettings?.path) {
|
|
334
|
+
this.storagePath = `${this.imgStorageSettings.path}/${this.imgStorageSettings.fileName}.webp`;
|
|
335
|
+
}
|
|
336
|
+
else if (this.imgStorageSettings.path) {
|
|
337
|
+
this.storagePath = `${this.imgStorageSettings.path}/${this.imgStorageSettings.fileName}.webp`;
|
|
338
|
+
}
|
|
339
|
+
if (this.imgStorageSettings?.cropSettings?.resizeToWidth) {
|
|
340
|
+
this.resizeToWidth = this.imgStorageSettings.cropSettings.resizeToWidth;
|
|
341
|
+
}
|
|
342
|
+
else if (this.imgStorageSettings.cropSettings.resizeToWidth) {
|
|
343
|
+
this.resizeToWidth = this.imgStorageSettings.cropSettings.resizeToWidth;
|
|
344
|
+
}
|
|
345
|
+
if (this.imgStorageSettings?.fileName) {
|
|
346
|
+
this.renameFile = this.imgStorageSettings.fileName;
|
|
347
|
+
}
|
|
348
|
+
else if (this.imgStorageSettings.fileName) {
|
|
349
|
+
this.renameFile = this.imgStorageSettings.fileName;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
async fileChangeEvent(event) {
|
|
353
|
+
this.setSettingsForComponent();
|
|
354
|
+
console.log(this.fileInputId);
|
|
355
|
+
this.imageChangedEvent = event;
|
|
356
|
+
const file = event?.target?.files[0];
|
|
357
|
+
if (file) {
|
|
358
|
+
this.fileMetadata = file;
|
|
359
|
+
this.onFileSelected.emit(file);
|
|
360
|
+
this.renameFile = this.fileMetadata?.name
|
|
361
|
+
?.split('.')[0]
|
|
362
|
+
.replace(/[^a-zA-Z0-9]/g, '')
|
|
363
|
+
.slice(0, 80);
|
|
364
|
+
console.log(this.renameFile);
|
|
365
|
+
if (!this.imgStorageSettings.fileName) {
|
|
366
|
+
this.reloadPath();
|
|
367
|
+
}
|
|
368
|
+
this.displayDialog = true;
|
|
369
|
+
this.changeDetectorRef.detectChanges();
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
onInnerImageCropped(event) {
|
|
373
|
+
this.croppedImage = event.base64;
|
|
374
|
+
}
|
|
375
|
+
imageLoaded(image) {
|
|
376
|
+
this.changeDetectorRef.detectChanges();
|
|
377
|
+
}
|
|
378
|
+
cropperReady() {
|
|
379
|
+
this.changeDetectorRef.detectChanges();
|
|
380
|
+
}
|
|
381
|
+
loadImageFailed() {
|
|
382
|
+
console.error('fallo al cargar la imagen');
|
|
383
|
+
}
|
|
384
|
+
async simpleCropAndUpload() {
|
|
385
|
+
console.log(this.fileInputId);
|
|
386
|
+
console.log('simpleCropAndUpload');
|
|
387
|
+
const imageCropped = await this.imageCropper.crop();
|
|
388
|
+
const imgStorage = await this.multiImagesStorageService.uploadImage(imageCropped?.blob, this.storagePath);
|
|
389
|
+
if (this.currentStorage?.path) {
|
|
390
|
+
console.warn('deleting current Image', this.currentStorage?.path);
|
|
391
|
+
this.multiImagesStorageService.deleteImage(this.currentStorage.path);
|
|
392
|
+
}
|
|
393
|
+
console.log('imgStorage', imgStorage);
|
|
394
|
+
this.imageUploaded.emit(imgStorage);
|
|
395
|
+
this.displayDialog = false;
|
|
396
|
+
this.changeDetectorRef.detectChanges();
|
|
397
|
+
}
|
|
398
|
+
changeRatio(event) {
|
|
399
|
+
console.log('changeRatio', event);
|
|
400
|
+
// this.imgStorageSettings.cropSettings.aspectRatio = event.valueRatio;
|
|
401
|
+
this.aspectRatioValue = event.valueRatio;
|
|
402
|
+
this.changeDetectorRef.detectChanges();
|
|
403
|
+
}
|
|
404
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: CropperComponentModal, deps: [{ token: MultiImagesStorageService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
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"] }] }); }
|
|
406
|
+
}
|
|
407
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: CropperComponentModal, decorators: [{
|
|
408
|
+
type: Component,
|
|
409
|
+
args: [{ selector: 'dc-cropper-modal', standalone: true, imports: [NgIf, 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 <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"] }]
|
|
410
|
+
}], ctorParameters: () => [{ type: MultiImagesStorageService }, { type: i0.ChangeDetectorRef }], propDecorators: { imgStorageSettings: [{
|
|
411
|
+
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
|
+
}], imageCropper: [{
|
|
423
|
+
type: ViewChild,
|
|
424
|
+
args: [ImageCropperComponent]
|
|
425
|
+
}] } });
|
|
426
|
+
|
|
427
|
+
class DCFilesCacheService {
|
|
428
|
+
constructor(storage) {
|
|
429
|
+
this.storage = storage;
|
|
430
|
+
this.files = {};
|
|
431
|
+
}
|
|
432
|
+
async getURLSrcFile(path) {
|
|
433
|
+
if (path in this.files) {
|
|
434
|
+
return this.files[path];
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
const url = await getDownloadURL(ref$1(this.storage, path));
|
|
438
|
+
const localUrl = await this.donwloadFileAndGetLocalURL(url);
|
|
439
|
+
this.files[path] = localUrl;
|
|
440
|
+
return localUrl;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
getBlob(url) {
|
|
444
|
+
return new Promise((resolve, reject) => {
|
|
445
|
+
const xhr = new XMLHttpRequest();
|
|
446
|
+
xhr.responseType = 'blob';
|
|
447
|
+
xhr.overrideMimeType('audio/mp3');
|
|
448
|
+
xhr.onload = (event) => {
|
|
449
|
+
var blob = xhr.response;
|
|
450
|
+
resolve(blob);
|
|
451
|
+
};
|
|
452
|
+
xhr.onerror = (event) => {
|
|
453
|
+
reject(event);
|
|
454
|
+
};
|
|
455
|
+
xhr.open('GET', url);
|
|
456
|
+
xhr.send();
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
async donwloadFileAndGetLocalURL(url) {
|
|
460
|
+
const blob = await this.getBlob(url);
|
|
461
|
+
return URL.createObjectURL(blob);
|
|
462
|
+
}
|
|
463
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCFilesCacheService, deps: [{ token: i1$1.Storage }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
464
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCFilesCacheService, providedIn: 'root' }); }
|
|
465
|
+
}
|
|
466
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCFilesCacheService, decorators: [{
|
|
467
|
+
type: Injectable,
|
|
468
|
+
args: [{
|
|
469
|
+
providedIn: 'root',
|
|
470
|
+
}]
|
|
471
|
+
}], ctorParameters: () => [{ type: i1$1.Storage }] });
|
|
472
|
+
|
|
473
|
+
/*
|
|
474
|
+
* Public API Surface of storage-uploader
|
|
475
|
+
*/
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Generated bundle index. Do not edit.
|
|
479
|
+
*/
|
|
480
|
+
|
|
481
|
+
export { AspectRatio, AspectRatio2, AspectRatioOptions, AspectType, CropperComponent, CropperComponentModal, DCFilesCacheService, DEFAULT_SETTINGS, MultiImagesStorageService, ResolutionType };
|
|
482
|
+
//# sourceMappingURL=dataclouder-ngx-cloud-storage.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dataclouder-ngx-cloud-storage.mjs","sources":["../../../../projects/dataclouder/ngx-cloud-storage/src/lib/classes/cropper.classes.ts","../../../../projects/dataclouder/ngx-cloud-storage/src/lib/services/multi-images-storage.service.ts","../../../../projects/dataclouder/ngx-cloud-storage/src/lib/components/cropper/cropper.component.ts","../../../../projects/dataclouder/ngx-cloud-storage/src/lib/components/cropper/cropper.component.html","../../../../projects/dataclouder/ngx-cloud-storage/src/lib/components/cropper-modal/cropper-modal.component.ts","../../../../projects/dataclouder/ngx-cloud-storage/src/lib/components/cropper-modal/cropper-modal.component.html","../../../../projects/dataclouder/ngx-cloud-storage/src/lib/services/dc-files-cache.service.ts","../../../../projects/dataclouder/ngx-cloud-storage/src/public-api.ts","../../../../projects/dataclouder/ngx-cloud-storage/src/dataclouder-ngx-cloud-storage.ts"],"sourcesContent":["export interface StorageImageSettings {\n path?: string;\n fileName?: string;\n cropSettings?: ImageCropSettings;\n}\n\nexport interface ImageCropSettings {\n resizeToWidth?: number;\n resolutions: Array<number>; // 400, 800, 1200, 1600\n aspectRatio: AspectType;\n}\n\nexport interface CropImageSettings {\n // Resolutions should be here.\n path: string;\n fileName?: string;\n resizeToWidth?: number;\n}\n\nexport interface ImageMultipleCrops {\n file: File;\n defaultImageBlob?: Blob;\n imagesBlobs: { [resolution: number]: Blob };\n settings: { renameFile: string; width?: number };\n}\n\nexport const AspectRatioOptions: AspectRatioOption[] = [\n { value: '1:1', label: 'square', description: 'Square (1:1)', valueRatio: 1 / 1 },\n { value: '1:2', label: 'vertical_1_2', description: 'Vertical (1:2)', valueRatio: 1 / 2 },\n { value: '2:3', label: 'vertical_2_3', description: 'Vertical (2:3)', valueRatio: 2 / 3 },\n { value: '3:4', label: 'vertical_3_4', description: 'Vertical (3:4)', valueRatio: 3 / 4 },\n { value: '4:5', label: 'vertical_4_5', description: 'Vertical (4:5)', valueRatio: 4 / 5 },\n { value: '9:16', label: 'vertical_9_16', description: 'Vertical (9:16)', valueRatio: 9 / 16 },\n { value: '2:1', label: 'horizontal_2_1', description: 'Horizontal (2:1)', valueRatio: 2 / 1 },\n { value: '3:2', label: 'horizontal_3_2', description: 'Horizontal (3:2)', valueRatio: 3 / 2 },\n { value: '4:3', label: 'horizontal_4_3', description: 'Horizontal (4:3)', valueRatio: 4 / 3 },\n { value: '5:4', label: 'horizontal_5_4', description: 'Horizontal (5:4)', valueRatio: 5 / 4 },\n { value: '16:9', label: 'horizontal_16_9', description: 'Horizontal (16:9)', valueRatio: 16 / 9 },\n];\n\nexport interface AspectRatioOption {\n label: string;\n description: string;\n value: string;\n valueRatio: number;\n}\n\nexport enum AspectType {\n Square = 'square',\n Rectangle = 'rectangle',\n Banner = 'banner',\n RectangleLarge = 'rectangleLarge',\n Vertical_9_16 = 'vertical_9_16',\n Vertical_3_5 = 'vertical_3_5',\n Vertical_2_3 = 'vertical_2_3',\n}\n\n// Creo que seria bueno refactorizar para solo enviar aspect ratio asi\nexport enum AspectRatio2 {\n // Vertical\n '9:16' = 16 / 9,\n '3:5' = 3 / 5,\n '5:8' = 5 / 8,\n '2:3' = 2 / 3,\n '1:1' = 1 / 1,\n}\n\nexport enum ResolutionType {\n Small = 200,\n Medium = 400,\n MediumLarge = 800,\n Large = 1200,\n VeryLarge = 1600,\n}\n\nexport const AspectRatio = {\n [AspectType.Square]: 1 / 1,\n [AspectType.Rectangle]: 16 / 9,\n [AspectType.Vertical_9_16]: 9 / 16,\n [AspectType.RectangleLarge]: 16 / 8,\n [AspectType.Banner]: 16 / 7,\n [AspectType.Vertical_3_5]: 3 / 5,\n [AspectType.Vertical_2_3]: 2 / 3,\n};\n\nexport interface ImgStorageData extends CloudStorageData {\n name?: string;\n bucket?: string;\n url?: string;\n path?: string; // path where the file is in the storage\n fullPath?: string; // path + name\n resolution?: string;\n resolutions?: any;\n}\n\nexport interface CloudStorageData {\n bucket?: string;\n url?: string;\n path?: string; // path where the file is in the storage\n}\n\nexport const DEFAULT_SETTINGS: StorageImageSettings = {\n path: '/default-collection/id/please-change-this-subcollection',\n fileName: 'image',\n cropSettings: {\n aspectRatio: AspectType.Square,\n resolutions: [ResolutionType.Small, ResolutionType.MediumLarge],\n resizeToWidth: 450,\n },\n};\n","import { Injectable } from '@angular/core';\nimport { AngularFireStorage, AngularFireUploadTask } from '@angular/fire/compat/storage';\nimport { getDownloadURL, getStorage, deleteObject, listAll } from '@angular/fire/storage';\n\nimport { ref } from 'firebase/storage';\nimport { lastValueFrom } from 'rxjs';\nimport { ImageMultipleCrops, ImgStorageData } from '../classes/cropper.classes';\n\n@Injectable({\n providedIn: 'root',\n})\nexport class MultiImagesStorageService {\n constructor(private storage: AngularFireStorage) {}\n\n public async uploadImage(image: Blob, path: string): Promise<ImgStorageData> {\n try {\n const refStorage = this.storage.ref(path);\n const task = await refStorage.put(image);\n const { fullPath, bucket } = task.metadata;\n const url = await lastValueFrom(refStorage.getDownloadURL());\n const imageStorage = { url, path: fullPath, bucket };\n return imageStorage;\n } catch (error) {\n console.error('uploading image error: ', error);\n return null;\n }\n }\n\n public uploadImage2(imageMulticrops: ImageMultipleCrops, path: string): Promise<ImgStorageData> {\n // path is usually [products/id] will create a new folder with the name and upload the images\n\n const blobs = imageMulticrops.imagesBlobs;\n\n const tasks: Array<Promise<any>> = [];\n let fileName = imageMulticrops.settings?.renameFile ?? imageMulticrops.file.name;\n fileName = fileName.split('.')[0];\n\n // load the default image jpeg\n path = `${path}/${fileName}`;\n const defaultImagePath = `${path}/${fileName}-400W.jpeg`;\n const refStorage = this.storage.ref(defaultImagePath);\n const task = refStorage.put(imageMulticrops.defaultImageBlob);\n\n tasks.push(this.uploadAndGetUrl(task, 'default'));\n\n // Upload webp images\n for (const resolutionStr of Object.keys(blobs)) {\n const fullName = `${fileName}-${resolutionStr}W.webp`;\n const filePath = `${path}/${fullName}`;\n\n const refStorage = this.storage.ref(filePath);\n\n const resolution = Number(resolutionStr);\n const task = refStorage.put(blobs[resolution]);\n tasks.push(this.uploadAndGetUrl(task, resolution + 'W'));\n }\n\n return Promise.all(tasks).then(async (upladedImages) => {\n let defaultImage: any = {};\n let resolutionsUrls: any = {};\n\n for (const image of upladedImages) {\n if (image.resolution === 'default') {\n defaultImage = image;\n } else {\n resolutionsUrls[image.resolution] = image.url;\n }\n }\n defaultImage['resolutions'] = resolutionsUrls;\n defaultImage['path'] = path;\n\n return defaultImage;\n });\n }\n\n public async delete_directory(imagePath: string): Promise<void> {\n // WARNING!! user very carefully could delete whatever folder\n const storage = getStorage();\n const directoryRef = ref(storage, imagePath);\n\n listAll(directoryRef)\n .then((res) => {\n res.items.forEach((itemRef) => {\n console.log(itemRef);\n deleteObject(itemRef);\n });\n })\n .catch((error) => {\n console.error('error al eliminar imagenes de cloud storage', error);\n });\n }\n\n public async deleteImage(imagePath: string): Promise<void> {\n const storageRef = this.storage.ref(imagePath);\n storageRef.delete().subscribe((res) => {\n console.log('image deleted', res);\n });\n }\n\n private async uploadAndGetUrl(task: AngularFireUploadTask, resolution: string): Promise<ImgStorageData> {\n const snap = await task;\n const { fullPath, bucket, name } = snap.metadata;\n const storage = getStorage();\n const storageRef = ref(storage, fullPath);\n const url = await getDownloadURL(storageRef);\n const meta: ImgStorageData = {\n url,\n fullPath,\n bucket,\n name,\n resolution,\n path: '',\n resolutions: {},\n };\n\n return meta;\n }\n}\n","import { Component, Input, OnInit, Output, EventEmitter, ViewChild } from '@angular/core';\nimport { ImageCroppedEvent, ImageCropperComponent, LoadedImage, base64ToFile } from 'ngx-image-cropper';\nimport { Observable } from 'rxjs';\nimport { AspectRatio, AspectType, ImageMultipleCrops, CropImageSettings, ResolutionType } from '../../classes/cropper.classes';\nimport { NgIf } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { MultiImagesStorageService } from '../../services/multi-images-storage.service';\n\n@Component({\n selector: 'app-cropper',\n templateUrl: './cropper.component.html',\n styleUrls: ['./cropper.component.scss'],\n standalone: true,\n imports: [NgIf, FormsModule, ImageCropperComponent],\n})\nexport class CropperComponent implements OnInit {\n // overrides name, path and resizeToWidth\n @Input() imageSettings: CropImageSettings = {} as any;\n\n @Input() ratioType: AspectType | string = AspectType.Square;\n\n @Input() resolutions: Array<number> = [ResolutionType.MediumLarge];\n\n @Output() imageUploaded = new EventEmitter<any>();\n\n @Output() onImageCropped = new EventEmitter<ImageMultipleCrops>();\n\n @Output() onFileSelected = new EventEmitter<any>();\n\n @ViewChild(ImageCropperComponent) imageCropper!: ImageCropperComponent;\n\n public fileMetadata: File | null = null;\n public imageChangedEvent!: Event;\n\n public aspectRatio: number = 1;\n\n public croppedImage: any = '';\n\n public isLoading = false;\n public isUploaded = false;\n public renameFile: any = '';\n public storagePath: string = '';\n public showModal = false;\n\n constructor(private multiImagesStorageService: MultiImagesStorageService) {}\n\n ngOnInit(): void {\n this.aspectRatio = AspectRatio[this.ratioType];\n if (this.imageSettings.path) {\n this.storagePath = `${this.imageSettings.path}/${this.imageSettings.fileName}.webp`;\n }\n }\n\n public reloadPath(): void {\n this.storagePath = `${this.imageSettings.path}/${this.renameFile}.webp`;\n }\n\n async fileChangeEvent(event: any) {\n this.imageChangedEvent = event;\n const file = event?.target?.files[0];\n if (file) {\n this.fileMetadata = file;\n this.onFileSelected.emit(file);\n this.showModal = true; // Show modal when file is selected\n this.renameFile = this.fileMetadata?.name?.split('.')[0];\n console.log(this.renameFile);\n\n if (!this.imageSettings.fileName) {\n this.reloadPath();\n }\n }\n }\n\n onInnerImageCropped(event: ImageCroppedEvent) {\n this.croppedImage = event.base64;\n }\n\n imageLoaded(image: LoadedImage) {\n // show cropper\n }\n\n loadImageFailed() {\n console.error('fallo al cargar la imagen');\n }\n\n public downloadURL!: Observable<string>;\n\n public async simpleCropAndUpload() {\n const imageCropped: any = await this.imageCropper.crop();\n const imgStorage = await this.multiImagesStorageService.uploadImage(imageCropped?.blob, this.storagePath);\n this.imageUploaded.emit(imgStorage);\n this.closeModal();\n }\n\n public async uploadToStorage(imageMulticrops: ImageMultipleCrops): Promise<void> {\n // TODO: Nota si algo falla aquí puede causar inconsistencias en el sistema, ver como manejar errores\n const path = this.imageSettings.path;\n // const imageUploaded = await this.multiImagesStorageService.uploadImage(imageMulticrops, path);\n // this.modalRef.close();\n // console.log(imageUploaded);\n // const image = { type: 'cover', ...imageUploaded };\n\n // TODO: creo que esta parte va en el componente que llama a este componente\n // if (!this.lesson.media) {\n // this.lesson.media = {};\n // this.lesson.media.images = [image];\n // } else {\n // // solo sustituir el cover si ya existe\n // const currentCover = this.lesson.media.images.find((img) => img.type === 'cover');\n // this.multiImagesStorageService.delete_directory(currentCover.path);\n\n // this.lesson.media.images = this.lesson.media.images.filter((img) => img.type !== 'cover');\n\n // this.lesson.media.images.push(image);\n // }\n\n // await this.saveLesson();\n // this.updateCover();\n // this.imageUploaded.emit(image);\n }\n\n closeModal(): void {\n this.showModal = false;\n }\n}\n","<div> path: {{ storagePath }} </div>\n\n<div class=\"options\">\n <div *ngIf=\"!isUploaded\">\n <input type=\"file\" id=\"file-upload\" class=\"file-input\" (change)=\"fileChangeEvent($event)\" />\n <label for=\"file-upload\" class=\"btn-upload\">Seleccionar archivo</label>\n <em *ngIf=\"!fileMetadata\">Carga una imagen para comenzar</em>\n </div>\n\n <span *ngIf=\"fileMetadata\">\n <span style=\"margin: 1px 20px\"> tipo: {{ fileMetadata.type }} </span>\n\n <span style=\"margin: 1px 20px\"> tamaño {{ fileMetadata.size }} </span>\n <br />\n <input\n [disabled]=\"imageSettings?.fileName\"\n style=\"margin: 1px 20px; width: 400px\"\n [(ngModel)]=\"renameFile\"\n type=\"text\"\n placeholder=\"Rename File\"\n (ngModelChange)=\"reloadPath()\" />\n <button class=\"btn-crop\" (click)=\"closeModal()\"> Recortar y Subir </button>\n </span>\n</div>\n\n<div class=\"modal\" *ngIf=\"fileMetadata && !isUploaded\" [class.show-modal]=\"showModal\">\n <div class=\"modal-content\">\n <div class=\"modal-header\">\n <h3>Recortar imagen</h3>\n <button class=\"close-button\" (click)=\"closeModal()\">×</button>\n </div>\n <div class=\"modal-body\">\n <h1>Hlloa</h1>\n\n <image-cropper\n [imageChangedEvent]=\"imageChangedEvent\"\n [maintainAspectRatio]=\"true\"\n [aspectRatio]=\"aspectRatio\"\n format=\"webp\"\n [resizeToWidth]=\"450\"\n (imageCropped)=\"onInnerImageCropped($event)\"\n (loadImageFailed)=\"loadImageFailed()\"\n (imageLoaded)=\"imageLoaded($event)\"\n [autoCrop]=\"false\"></image-cropper>\n\n <div class=\"modal-footer\">\n <button class=\"btn-crop\" (click)=\"simpleCropAndUpload()\">Recortar y Subir</button>\n </div>\n </div>\n </div>\n</div>\n\n<button *ngIf=\"croppedImage && !isUploaded\" [disabled]=\"isLoading\" nbButton status=\"info\"> upload </button>\n","import { Component, Input, OnInit, Output, EventEmitter, ViewChild, ChangeDetectorRef, Attribute } from '@angular/core';\nimport { NgIf } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\n\nimport { DialogModule } from 'primeng/dialog';\n\nimport { ImageCroppedEvent, ImageCropperComponent, LoadedImage } from 'ngx-image-cropper';\nimport { Observable } from 'rxjs';\n\nimport { MultiImagesStorageService } from '../../services/multi-images-storage.service';\nimport {\n AspectRatio,\n AspectType,\n ImageMultipleCrops,\n StorageImageSettings,\n ImgStorageData,\n AspectRatioOptions,\n AspectRatioOption,\n DEFAULT_SETTINGS,\n} from '../../classes/cropper.classes';\nimport { TooltipModule } from 'primeng/tooltip';\n\nimport { ButtonModule } from 'primeng/button';\nimport { MessageModule } from 'primeng/message';\nimport { SelectModule } from 'primeng/select';\nimport { InputTextModule } from 'primeng/inputtext';\n\n@Component({\n selector: 'dc-cropper-modal',\n templateUrl: './cropper-modal.component.html',\n styleUrls: ['./cropper-modal.component.scss'],\n standalone: true,\n imports: [NgIf, FormsModule, ImageCropperComponent, ButtonModule, DialogModule, TooltipModule, MessageModule, SelectModule, InputTextModule],\n})\nexport class CropperComponentModal implements OnInit {\n // overrides name, path and resizeToWidth\n\n @Input() imgStorageSettings: StorageImageSettings = DEFAULT_SETTINGS;\n @Input() buttonLabel: string = 'Seleccionar archivo';\n @Input() currentStorage: ImgStorageData = {} as any;\n\n @Output() imageUploaded = new EventEmitter<ImgStorageData>();\n @Output() onImageCropped = new EventEmitter<ImageMultipleCrops>();\n @Output() onFileSelected = new EventEmitter<any>();\n\n @ViewChild(ImageCropperComponent) imageCropper!: ImageCropperComponent;\n\n public aspectRatioOptions = AspectRatioOptions;\n public fileMetadata: File | null = null;\n public imageChangedEvent!: Event;\n public displayDialog = false;\n public aspectRatioValue: number = 1;\n public croppedImage: any = '';\n public renameFile: any = '';\n public storagePath: string = '';\n public downloadURL!: Observable<string>;\n public resizeToWidth: number = 450;\n public ratioSelected: any = null;\n\n // Add a unique identifier for the file input\n public fileInputId: string;\n\n constructor(private multiImagesStorageService: MultiImagesStorageService, private changeDetectorRef: ChangeDetectorRef) {\n // Generate random ID for file input\n this.fileInputId = `file-upload-${Math.random().toString(36).substring(2, 11)}`;\n }\n\n ngOnInit(): void {\n if (!this.imgStorageSettings.path) {\n console.warn('⚠️ Remember to set imgStorageSettings, path and fileName are required , path example: /collection/id/subcollection ');\n }\n this.reloadPath();\n }\n\n public reloadPath(): void {\n const randomCharacters = Math.random().toString(36).substring(2, 15);\n this.storagePath = `${this.imgStorageSettings.path}/${this.imgStorageSettings.fileName}-${randomCharacters}.webp`;\n }\n\n private setSettingsForComponent(): void {\n console.log('setSettingsForComponent', this.imgStorageSettings);\n // TODO: remove all the imageSettings and keep only imgStorageSettings\n\n this.aspectRatioValue = this.imgStorageSettings?.cropSettings?.aspectRatio\n ? AspectRatio[this.imgStorageSettings?.cropSettings?.aspectRatio]\n : AspectRatio[AspectType.Square];\n\n if (this.imgStorageSettings?.path) {\n this.storagePath = `${this.imgStorageSettings.path}/${this.imgStorageSettings.fileName}.webp`;\n } else if (this.imgStorageSettings.path) {\n this.storagePath = `${this.imgStorageSettings.path}/${this.imgStorageSettings.fileName}.webp`;\n }\n if (this.imgStorageSettings?.cropSettings?.resizeToWidth) {\n this.resizeToWidth = this.imgStorageSettings.cropSettings.resizeToWidth;\n } else if (this.imgStorageSettings.cropSettings.resizeToWidth) {\n this.resizeToWidth = this.imgStorageSettings.cropSettings.resizeToWidth;\n }\n if (this.imgStorageSettings?.fileName) {\n this.renameFile = this.imgStorageSettings.fileName;\n } else if (this.imgStorageSettings.fileName) {\n this.renameFile = this.imgStorageSettings.fileName;\n }\n }\n\n async fileChangeEvent(event: any) {\n this.setSettingsForComponent();\n\n console.log(this.fileInputId);\n\n this.imageChangedEvent = event;\n const file = event?.target?.files[0];\n if (file) {\n this.fileMetadata = file;\n this.onFileSelected.emit(file);\n this.renameFile = this.fileMetadata?.name\n ?.split('.')[0]\n .replace(/[^a-zA-Z0-9]/g, '')\n .slice(0, 80);\n\n console.log(this.renameFile);\n\n if (!this.imgStorageSettings.fileName) {\n this.reloadPath();\n }\n this.displayDialog = true;\n this.changeDetectorRef.detectChanges();\n }\n }\n\n onInnerImageCropped(event: ImageCroppedEvent) {\n this.croppedImage = event.base64;\n }\n\n imageLoaded(image: LoadedImage) {\n this.changeDetectorRef.detectChanges();\n }\n\n cropperReady() {\n this.changeDetectorRef.detectChanges();\n }\n\n loadImageFailed() {\n console.error('fallo al cargar la imagen');\n }\n\n public async simpleCropAndUpload() {\n console.log(this.fileInputId);\n\n console.log('simpleCropAndUpload');\n const imageCropped: any = await this.imageCropper.crop();\n const imgStorage = await this.multiImagesStorageService.uploadImage(imageCropped?.blob, this.storagePath);\n if (this.currentStorage?.path) {\n console.warn('deleting current Image', this.currentStorage?.path);\n this.multiImagesStorageService.deleteImage(this.currentStorage.path);\n }\n console.log('imgStorage', imgStorage);\n this.imageUploaded.emit(imgStorage);\n this.displayDialog = false;\n this.changeDetectorRef.detectChanges();\n }\n\n public changeRatio(event: AspectRatioOption) {\n console.log('changeRatio', event);\n // this.imgStorageSettings.cropSettings.aspectRatio = event.valueRatio;\n this.aspectRatioValue = event.valueRatio;\n this.changeDetectorRef.detectChanges();\n }\n}\n","<div class=\"upload-section\">\n <input type=\"file\" [id]=\"fileInputId\" class=\"file-input\" (change)=\"fileChangeEvent($event)\" />\n <label pButton [for]=\"fileInputId\" [pTooltip]=\"storagePath\" class=\"upload-button\">\n {{ buttonLabel }}\n </label>\n</div>\n@if(displayDialog) {\n<!-- Cropper Dialog -->\n\n<p-dialog header=\"Recortar imagen\" [(visible)]=\"displayDialog\" [modal]=\"true\" [draggable]=\"false\" [resizable]=\"false\" styleClass=\"cropper-dialog\">\n <!-- Image Settings Section -->\n <div class=\"settings-section\">\n @if(!imgStorageSettings.path) {\n <p-message severity=\"warn\">Developer Note: make sure you have a path to save the image pass object imgStorageSettings</p-message>\n } @if(currentStorage.url) {\n\n <p-message severity=\"warn\" variant=\"outlined\">\n <div>\n <span class=\"setting-label\">Image will be replaced:</span>\n <img width=\"100\" height=\"Auto\" [src]=\"currentStorage?.url\" />\n </div>\n </p-message>\n\n }\n\n <p-message>\n <b>Estas opciones estan preconfiguradas y no se pueden cambiar</b>\n\n <ul>\n <li> <b>Path to save:</b> {{ storagePath }}< </li>\n <li>\n <b>Resoluciones:</b>\n <span>{{ imgStorageSettings?.cropSettings?.resolutions }}</span>\n </li>\n </ul>\n </p-message>\n\n <div class=\"setting-item\">\n <span class=\"setting-label\">Aspecto:</span>\n <p class=\"setting-value\">{{ imgStorageSettings?.cropSettings?.aspectRatio }}</p>\n </div>\n\n <p-select\n [options]=\"aspectRatioOptions\"\n [ngModel]=\"ratioSelected\"\n (ngModelChange)=\"changeRatio($event)\"\n optionLabel=\"description\"\n placeholder=\"Select a ratio\" />\n\n <!-- File Metadata Section -->\n <div class=\"metadata-section\" *ngIf=\"fileMetadata\">\n <span class=\"metadata-item\">tipo: {{ fileMetadata.type }}</span>\n <span class=\"metadata-item\">tamaño: {{ fileMetadata.size }}</span>\n </div>\n\n <!-- Rename Input -->\n <input\n pInputText\n [disabled]=\"imgStorageSettings?.fileName\"\n [(ngModel)]=\"renameFile\"\n type=\"text\"\n placeholder=\"Rename File\"\n (ngModelChange)=\"reloadPath()\"\n class=\"rename-input\" />\n </div>\n\n <!-- Image Cropper -->\n\n <div class=\"cropper-container-father\">\n <image-cropper\n [imageChangedEvent]=\"imageChangedEvent\"\n [maintainAspectRatio]=\"true\"\n [aspectRatio]=\"aspectRatioValue\"\n format=\"webp\"\n [resizeToWidth]=\"resizeToWidth\"\n (imageCropped)=\"onInnerImageCropped($event)\"\n (loadImageFailed)=\"loadImageFailed()\"\n (cropperReady)=\"cropperReady()\"\n [autoCrop]=\"false\">\n </image-cropper>\n </div>\n <!-- Dialog Footer -->\n <ng-template pTemplate=\"footer\">\n <div class=\"dialog-footer\">\n <button pButton class=\"p-button-primary\" (click)=\"simpleCropAndUpload()\"> Recortar y Subir </button>\n </div>\n </ng-template>\n</p-dialog>\n}\n","import { Injectable } from '@angular/core';\nimport { getDownloadURL, ref, Storage } from '@angular/fire/storage';\n\n@Injectable({\n providedIn: 'root',\n})\nexport class DCFilesCacheService {\n constructor(private storage: Storage) {}\n\n public files: { [key: string]: string } = {};\n\n public async getURLSrcFile(path: string): Promise<string> {\n if (path in this.files) {\n return this.files[path];\n } else {\n const url = await getDownloadURL(ref(this.storage, path));\n const localUrl = await this.donwloadFileAndGetLocalURL(url);\n this.files[path] = localUrl;\n return localUrl;\n }\n }\n\n public getBlob(url: string): Promise<Blob> {\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.responseType = 'blob';\n xhr.overrideMimeType('audio/mp3');\n\n xhr.onload = (event) => {\n var blob = xhr.response;\n resolve(blob);\n };\n xhr.onerror = (event) => {\n reject(event);\n };\n\n xhr.open('GET', url);\n xhr.send();\n });\n }\n\n public async donwloadFileAndGetLocalURL(url: string) {\n const blob = await this.getBlob(url);\n\n return URL.createObjectURL(blob);\n }\n}\n","/*\n * Public API Surface of storage-uploader\n */\n\nexport * from './lib/components/cropper/cropper.component';\nexport * from './lib/components/cropper-modal/cropper-modal.component';\nexport * from './lib/classes/cropper.classes';\n\nexport * from './lib/services/multi-images-storage.service';\nexport * from './lib/services/dc-files-cache.service';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["i1.MultiImagesStorageService","ref","i1"],"mappings":"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"}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export interface StorageImageSettings {
|
|
2
|
+
path?: string;
|
|
3
|
+
fileName?: string;
|
|
4
|
+
cropSettings?: ImageCropSettings;
|
|
5
|
+
}
|
|
6
|
+
export interface ImageCropSettings {
|
|
7
|
+
resizeToWidth?: number;
|
|
8
|
+
resolutions: Array<number>;
|
|
9
|
+
aspectRatio: AspectType;
|
|
10
|
+
}
|
|
11
|
+
export interface CropImageSettings {
|
|
12
|
+
path: string;
|
|
13
|
+
fileName?: string;
|
|
14
|
+
resizeToWidth?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface ImageMultipleCrops {
|
|
17
|
+
file: File;
|
|
18
|
+
defaultImageBlob?: Blob;
|
|
19
|
+
imagesBlobs: {
|
|
20
|
+
[resolution: number]: Blob;
|
|
21
|
+
};
|
|
22
|
+
settings: {
|
|
23
|
+
renameFile: string;
|
|
24
|
+
width?: number;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export declare const AspectRatioOptions: AspectRatioOption[];
|
|
28
|
+
export interface AspectRatioOption {
|
|
29
|
+
label: string;
|
|
30
|
+
description: string;
|
|
31
|
+
value: string;
|
|
32
|
+
valueRatio: number;
|
|
33
|
+
}
|
|
34
|
+
export declare enum AspectType {
|
|
35
|
+
Square = "square",
|
|
36
|
+
Rectangle = "rectangle",
|
|
37
|
+
Banner = "banner",
|
|
38
|
+
RectangleLarge = "rectangleLarge",
|
|
39
|
+
Vertical_9_16 = "vertical_9_16",
|
|
40
|
+
Vertical_3_5 = "vertical_3_5",
|
|
41
|
+
Vertical_2_3 = "vertical_2_3"
|
|
42
|
+
}
|
|
43
|
+
export declare enum AspectRatio2 {
|
|
44
|
+
'9:16' = 1.7777777777777777,
|
|
45
|
+
'3:5' = 0.6,
|
|
46
|
+
'5:8' = 0.625,
|
|
47
|
+
'2:3' = 0.6666666666666666,
|
|
48
|
+
'1:1' = 1
|
|
49
|
+
}
|
|
50
|
+
export declare enum ResolutionType {
|
|
51
|
+
Small = 200,
|
|
52
|
+
Medium = 400,
|
|
53
|
+
MediumLarge = 800,
|
|
54
|
+
Large = 1200,
|
|
55
|
+
VeryLarge = 1600
|
|
56
|
+
}
|
|
57
|
+
export declare const AspectRatio: {
|
|
58
|
+
square: number;
|
|
59
|
+
rectangle: number;
|
|
60
|
+
vertical_9_16: number;
|
|
61
|
+
rectangleLarge: number;
|
|
62
|
+
banner: number;
|
|
63
|
+
vertical_3_5: number;
|
|
64
|
+
vertical_2_3: number;
|
|
65
|
+
};
|
|
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
|
+
export declare const DEFAULT_SETTINGS: StorageImageSettings;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { OnInit, EventEmitter } from '@angular/core';
|
|
2
|
+
import { ImageCroppedEvent, ImageCropperComponent, LoadedImage } from 'ngx-image-cropper';
|
|
3
|
+
import { Observable } from 'rxjs';
|
|
4
|
+
import { AspectType, ImageMultipleCrops, CropImageSettings } from '../../classes/cropper.classes';
|
|
5
|
+
import { MultiImagesStorageService } from '../../services/multi-images-storage.service';
|
|
6
|
+
import * as i0 from "@angular/core";
|
|
7
|
+
export declare class CropperComponent implements OnInit {
|
|
8
|
+
private multiImagesStorageService;
|
|
9
|
+
imageSettings: CropImageSettings;
|
|
10
|
+
ratioType: AspectType | string;
|
|
11
|
+
resolutions: Array<number>;
|
|
12
|
+
imageUploaded: EventEmitter<any>;
|
|
13
|
+
onImageCropped: EventEmitter<ImageMultipleCrops>;
|
|
14
|
+
onFileSelected: EventEmitter<any>;
|
|
15
|
+
imageCropper: ImageCropperComponent;
|
|
16
|
+
fileMetadata: File | null;
|
|
17
|
+
imageChangedEvent: Event;
|
|
18
|
+
aspectRatio: number;
|
|
19
|
+
croppedImage: any;
|
|
20
|
+
isLoading: boolean;
|
|
21
|
+
isUploaded: boolean;
|
|
22
|
+
renameFile: any;
|
|
23
|
+
storagePath: string;
|
|
24
|
+
showModal: boolean;
|
|
25
|
+
constructor(multiImagesStorageService: MultiImagesStorageService);
|
|
26
|
+
ngOnInit(): void;
|
|
27
|
+
reloadPath(): void;
|
|
28
|
+
fileChangeEvent(event: any): Promise<void>;
|
|
29
|
+
onInnerImageCropped(event: ImageCroppedEvent): void;
|
|
30
|
+
imageLoaded(image: LoadedImage): void;
|
|
31
|
+
loadImageFailed(): void;
|
|
32
|
+
downloadURL: Observable<string>;
|
|
33
|
+
simpleCropAndUpload(): Promise<void>;
|
|
34
|
+
uploadToStorage(imageMulticrops: ImageMultipleCrops): Promise<void>;
|
|
35
|
+
closeModal(): void;
|
|
36
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<CropperComponent, never>;
|
|
37
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<CropperComponent, "app-cropper", never, { "imageSettings": { "alias": "imageSettings"; "required": false; }; "ratioType": { "alias": "ratioType"; "required": false; }; "resolutions": { "alias": "resolutions"; "required": false; }; }, { "imageUploaded": "imageUploaded"; "onImageCropped": "onImageCropped"; "onFileSelected": "onFileSelected"; }, never, never, true, never>;
|
|
38
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { OnInit, EventEmitter, ChangeDetectorRef } from '@angular/core';
|
|
2
|
+
import { ImageCroppedEvent, ImageCropperComponent, LoadedImage } from 'ngx-image-cropper';
|
|
3
|
+
import { Observable } from 'rxjs';
|
|
4
|
+
import { MultiImagesStorageService } from '../../services/multi-images-storage.service';
|
|
5
|
+
import { ImageMultipleCrops, StorageImageSettings, ImgStorageData, AspectRatioOption } from '../../classes/cropper.classes';
|
|
6
|
+
import * as i0 from "@angular/core";
|
|
7
|
+
export declare class CropperComponentModal implements OnInit {
|
|
8
|
+
private multiImagesStorageService;
|
|
9
|
+
private changeDetectorRef;
|
|
10
|
+
imgStorageSettings: StorageImageSettings;
|
|
11
|
+
buttonLabel: string;
|
|
12
|
+
currentStorage: ImgStorageData;
|
|
13
|
+
imageUploaded: EventEmitter<ImgStorageData>;
|
|
14
|
+
onImageCropped: EventEmitter<ImageMultipleCrops>;
|
|
15
|
+
onFileSelected: EventEmitter<any>;
|
|
16
|
+
imageCropper: ImageCropperComponent;
|
|
17
|
+
aspectRatioOptions: AspectRatioOption[];
|
|
18
|
+
fileMetadata: File | null;
|
|
19
|
+
imageChangedEvent: Event;
|
|
20
|
+
displayDialog: boolean;
|
|
21
|
+
aspectRatioValue: number;
|
|
22
|
+
croppedImage: any;
|
|
23
|
+
renameFile: any;
|
|
24
|
+
storagePath: string;
|
|
25
|
+
downloadURL: Observable<string>;
|
|
26
|
+
resizeToWidth: number;
|
|
27
|
+
ratioSelected: any;
|
|
28
|
+
fileInputId: string;
|
|
29
|
+
constructor(multiImagesStorageService: MultiImagesStorageService, changeDetectorRef: ChangeDetectorRef);
|
|
30
|
+
ngOnInit(): void;
|
|
31
|
+
reloadPath(): void;
|
|
32
|
+
private setSettingsForComponent;
|
|
33
|
+
fileChangeEvent(event: any): Promise<void>;
|
|
34
|
+
onInnerImageCropped(event: ImageCroppedEvent): void;
|
|
35
|
+
imageLoaded(image: LoadedImage): void;
|
|
36
|
+
cropperReady(): void;
|
|
37
|
+
loadImageFailed(): void;
|
|
38
|
+
simpleCropAndUpload(): Promise<void>;
|
|
39
|
+
changeRatio(event: AspectRatioOption): void;
|
|
40
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<CropperComponentModal, never>;
|
|
41
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<CropperComponentModal, "dc-cropper-modal", never, { "imgStorageSettings": { "alias": "imgStorageSettings"; "required": false; }; "buttonLabel": { "alias": "buttonLabel"; "required": false; }; "currentStorage": { "alias": "currentStorage"; "required": false; }; }, { "imageUploaded": "imageUploaded"; "onImageCropped": "onImageCropped"; "onFileSelected": "onFileSelected"; }, never, never, true, never>;
|
|
42
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Storage } from '@angular/fire/storage';
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
export declare class DCFilesCacheService {
|
|
4
|
+
private storage;
|
|
5
|
+
constructor(storage: Storage);
|
|
6
|
+
files: {
|
|
7
|
+
[key: string]: string;
|
|
8
|
+
};
|
|
9
|
+
getURLSrcFile(path: string): Promise<string>;
|
|
10
|
+
getBlob(url: string): Promise<Blob>;
|
|
11
|
+
donwloadFileAndGetLocalURL(url: string): Promise<string>;
|
|
12
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<DCFilesCacheService, never>;
|
|
13
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<DCFilesCacheService>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AngularFireStorage } from '@angular/fire/compat/storage';
|
|
2
|
+
import { ImageMultipleCrops, ImgStorageData } from '../classes/cropper.classes';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
export declare class MultiImagesStorageService {
|
|
5
|
+
private storage;
|
|
6
|
+
constructor(storage: AngularFireStorage);
|
|
7
|
+
uploadImage(image: Blob, path: string): Promise<ImgStorageData>;
|
|
8
|
+
uploadImage2(imageMulticrops: ImageMultipleCrops, path: string): Promise<ImgStorageData>;
|
|
9
|
+
delete_directory(imagePath: string): Promise<void>;
|
|
10
|
+
deleteImage(imagePath: string): Promise<void>;
|
|
11
|
+
private uploadAndGetUrl;
|
|
12
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<MultiImagesStorageService, never>;
|
|
13
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<MultiImagesStorageService>;
|
|
14
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dataclouder/ngx-cloud-storage",
|
|
3
|
+
"version": "0.0.21",
|
|
4
|
+
"description": "Angular component library for handling cloud storage uploads with image cropping capabilities",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"peerDependencies": {
|
|
9
|
+
"@angular/common": ">=18.0.0",
|
|
10
|
+
"@angular/core": ">=18.0.0",
|
|
11
|
+
"@angular/fire": ">=18.0.0",
|
|
12
|
+
"ngx-image-cropper": "^9.0.0",
|
|
13
|
+
"primeng": ">=19.0.0"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"tslib": "^2.3.0"
|
|
17
|
+
},
|
|
18
|
+
"sideEffects": false,
|
|
19
|
+
"module": "fesm2022/dataclouder-ngx-cloud-storage.mjs",
|
|
20
|
+
"typings": "index.d.ts",
|
|
21
|
+
"exports": {
|
|
22
|
+
"./package.json": {
|
|
23
|
+
"default": "./package.json"
|
|
24
|
+
},
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./index.d.ts",
|
|
27
|
+
"default": "./fesm2022/dataclouder-ngx-cloud-storage.mjs"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
package/public-api.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export * from './lib/components/cropper/cropper.component';
|
|
2
|
+
export * from './lib/components/cropper-modal/cropper-modal.component';
|
|
3
|
+
export * from './lib/classes/cropper.classes';
|
|
4
|
+
export * from './lib/services/multi-images-storage.service';
|
|
5
|
+
export * from './lib/services/dc-files-cache.service';
|