valtech-components 2.0.679 → 2.0.681

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.
@@ -5,7 +5,7 @@ import { IonAvatar, IonCard, IonIcon, IonButton, IonSpinner, IonText, IonModal,
5
5
  import * as i1 from '@angular/common';
6
6
  import { CommonModule, NgStyle, NgFor, isPlatformBrowser, NgClass } from '@angular/common';
7
7
  import { addIcons } from 'ionicons';
8
- import { addOutline, addCircleOutline, alertOutline, alertCircleOutline, arrowBackOutline, arrowForwardOutline, arrowDownOutline, settings, settingsOutline, checkmarkCircleOutline, ellipsisHorizontalOutline, notifications, notificationsOutline, openOutline, closeOutline, chatbubblesOutline, shareOutline, heart, heartOutline, home, homeOutline, eyeOffOutline, eyeOutline, scanOutline, chevronDownOutline, chevronForwardOutline, checkmarkOutline, clipboardOutline, copyOutline, filterOutline, locationOutline, calendarOutline, businessOutline, logoTwitter, logoInstagram, logoLinkedin, logoYoutube, logoTiktok, logoFacebook, logoGoogle, createOutline, trashOutline, playOutline, refreshOutline, documentTextOutline, lockClosedOutline, informationCircleOutline, logoNpm, removeOutline, add, close, share, create, trash, star, camera, mic, send, downloadOutline, chevronDown, language, globeOutline, checkmark, list, grid, apps, menu, search, person, helpCircle, informationCircle, documentText, mail, calendar, folder, chevronForward, ellipsisHorizontal, chevronBack, playBack, playForward, ellipse, starOutline, starHalf, heartHalf, checkmarkCircle, timeOutline, flag, trendingUp, trendingDown, remove, analytics, people, cash, cart, eye, chatbubbleOutline, thumbsUpOutline, thumbsUp, happyOutline, happy, sadOutline, sad, chevronUp, pin, pencil, callOutline, shuffleOutline, logoWhatsapp, paperPlaneOutline, mailOutline, trophyOutline, ticketOutline, giftOutline, personOutline, ellipsisVertical, closeCircle, alertCircle, logoApple, logoMicrosoft, linkOutline, unlinkOutline, chevronBackOutline, sendOutline, chatbubbleEllipsesOutline, swapVerticalOutline, chevronUpOutline, documentOutline, searchOutline, cartOutline, chatbubble, compass, compassOutline, gridOutline, listOutline, folderOutline, documents, documentsOutline, statsChart, statsChartOutline, cameraOutline, bugOutline, bulbOutline, closeCircleOutline, menuOutline } from 'ionicons/icons';
8
+ import { addOutline, addCircleOutline, alertOutline, alertCircleOutline, arrowBackOutline, arrowForwardOutline, arrowDownOutline, settings, settingsOutline, checkmarkCircleOutline, ellipsisHorizontalOutline, notifications, notificationsOutline, openOutline, closeOutline, chatbubblesOutline, shareOutline, heart, heartOutline, home, homeOutline, eyeOffOutline, eyeOutline, scanOutline, chevronDownOutline, chevronForwardOutline, checkmarkOutline, clipboardOutline, copyOutline, filterOutline, locationOutline, calendarOutline, businessOutline, logoTwitter, logoInstagram, logoLinkedin, logoYoutube, logoTiktok, logoFacebook, logoGoogle, createOutline, trashOutline, playOutline, refreshOutline, documentTextOutline, lockClosedOutline, informationCircleOutline, logoNpm, removeOutline, add, close, share, create, trash, star, camera, mic, send, downloadOutline, chevronDown, language, globeOutline, checkmark, list, grid, apps, menu, search, person, helpCircle, informationCircle, documentText, mail, calendar, folder, chevronForward, ellipsisHorizontal, chevronBack, playBack, playForward, ellipse, starOutline, starHalf, heartHalf, checkmarkCircle, timeOutline, flag, trendingUp, trendingDown, remove, analytics, people, cash, cart, eye, chatbubbleOutline, thumbsUpOutline, thumbsUp, happyOutline, happy, sadOutline, sad, chevronUp, pin, pencil, callOutline, shuffleOutline, logoWhatsapp, paperPlaneOutline, mailOutline, trophyOutline, ticketOutline, giftOutline, personOutline, ellipsisVertical, closeCircle, alertCircle, logoApple, logoMicrosoft, linkOutline, unlinkOutline, chevronBackOutline, sendOutline, chatbubbleEllipsesOutline, swapVerticalOutline, chevronUpOutline, documentOutline, searchOutline, cartOutline, chatbubble, compass, compassOutline, gridOutline, listOutline, folderOutline, documents, documentsOutline, statsChart, statsChartOutline, bugOutline, bulbOutline, closeCircleOutline, menuOutline } from 'ionicons/icons';
9
9
  import * as i1$1 from '@angular/router';
10
10
  import { Router, NavigationEnd, RouterLink, RouterOutlet, RouterModule } from '@angular/router';
11
11
  import { Browser } from '@capacitor/browser';
@@ -39,7 +39,6 @@ import * as i1$7 from '@angular/fire/storage';
39
39
  import { provideStorage, getStorage, connectStorageEmulator, ref, uploadBytesResumable, getDownloadURL, getMetadata, deleteObject, listAll } from '@angular/fire/storage';
40
40
  import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
41
41
  import { filter, map as map$1, catchError as catchError$1, tap, switchMap as switchMap$1, finalize, take, debounceTime as debounceTime$1, takeUntil as takeUntil$1 } from 'rxjs/operators';
42
- import { ImageCropperComponent } from 'ngx-image-cropper';
43
42
  import * as i1$8 from '@angular/common/http';
44
43
  import { provideHttpClient, withInterceptors, HttpClient } from '@angular/common/http';
45
44
  import { Capacitor } from '@capacitor/core';
@@ -50,7 +49,7 @@ import 'prismjs/components/prism-json';
50
49
  * Current version of valtech-components.
51
50
  * This is automatically updated during the publish process.
52
51
  */
53
- const VERSION = '2.0.679';
52
+ const VERSION = '2.0.681';
54
53
 
55
54
  /**
56
55
  * Servicio para gestionar presets de componentes.
@@ -21197,174 +21196,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
21197
21196
  type: Input
21198
21197
  }] } });
21199
21198
 
21200
- /**
21201
- * ImageCropComponent
21202
- *
21203
- * A modal-ready component for cropping images with a specified aspect ratio.
21204
- * Uses ngx-image-cropper internally and provides a simple interface.
21205
- *
21206
- * @example Inside an ion-modal
21207
- * ```html
21208
- * <ion-modal [isOpen]="showCropModal">
21209
- * <ng-template>
21210
- * <val-image-crop
21211
- * [image]="selectedFile"
21212
- * [aspectRatio]="1"
21213
- * [roundCropper]="true"
21214
- * (cropComplete)="onCropComplete($event)"
21215
- * (cancel)="showCropModal = false"
21216
- * />
21217
- * </ng-template>
21218
- * </ion-modal>
21219
- * ```
21220
- */
21221
- class ImageCropComponent {
21222
- constructor() {
21223
- this.i18n = inject(I18nService);
21224
- /** Image file to crop */
21225
- this.image = input.required();
21226
- /** Aspect ratio (1 for square, 16/9 for widescreen, etc.) */
21227
- this.aspectRatio = input(1);
21228
- /** Use round cropper (for avatars) */
21229
- this.roundCropper = input(true);
21230
- /** Resize output to specific width (0 = no resize) */
21231
- this.resizeToWidth = input(0);
21232
- /** i18n namespace for labels */
21233
- this.i18nNamespace = input('ImageCrop');
21234
- /** Emitted when crop is confirmed with the cropped blob */
21235
- this.cropComplete = new EventEmitter();
21236
- /** Emitted when user cancels the crop */
21237
- this.cancel = new EventEmitter();
21238
- /** Emitted when image fails to load */
21239
- this.loadFailed = new EventEmitter();
21240
- /** Internal signal for cropped blob */
21241
- this.croppedBlob = signal(null);
21242
- /** Computed text for cancel button */
21243
- this.cancelText = computed(() => {
21244
- this.i18n.lang(); // Track language changes
21245
- return this.i18n.t('cancel', 'Common') || 'Cancelar';
21246
- });
21247
- /** Computed text for confirm button */
21248
- this.confirmText = computed(() => {
21249
- this.i18n.lang();
21250
- return this.i18n.t('confirm', 'Common') || 'Confirmar';
21251
- });
21252
- /** Computed text for title */
21253
- this.titleText = computed(() => {
21254
- this.i18n.lang();
21255
- return this.i18n.t('cropImage', this.i18nNamespace()) || 'Recortar imagen';
21256
- });
21257
- }
21258
- /** Handle crop event from ngx-image-cropper */
21259
- onImageCropped(event) {
21260
- if (event.blob) {
21261
- this.croppedBlob.set(event.blob);
21262
- }
21263
- }
21264
- /** Confirm and emit the cropped blob */
21265
- confirmCrop() {
21266
- const blob = this.croppedBlob();
21267
- if (blob) {
21268
- this.cropComplete.emit(blob);
21269
- }
21270
- }
21271
- /** Handle load failure */
21272
- onLoadFailed() {
21273
- this.loadFailed.emit();
21274
- }
21275
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ImageCropComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
21276
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.14", type: ImageCropComponent, isStandalone: true, selector: "val-image-crop", inputs: { image: { classPropertyName: "image", publicName: "image", isSignal: true, isRequired: true, transformFunction: null }, aspectRatio: { classPropertyName: "aspectRatio", publicName: "aspectRatio", isSignal: true, isRequired: false, transformFunction: null }, roundCropper: { classPropertyName: "roundCropper", publicName: "roundCropper", isSignal: true, isRequired: false, transformFunction: null }, resizeToWidth: { classPropertyName: "resizeToWidth", publicName: "resizeToWidth", isSignal: true, isRequired: false, transformFunction: null }, i18nNamespace: { classPropertyName: "i18nNamespace", publicName: "i18nNamespace", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { cropComplete: "cropComplete", cancel: "cancel", loadFailed: "loadFailed" }, ngImport: i0, template: `
21277
- <ion-header>
21278
- <ion-toolbar>
21279
- <ion-buttons slot="start">
21280
- <ion-button (click)="cancel.emit()" color="medium">
21281
- {{ cancelText() }}
21282
- </ion-button>
21283
- </ion-buttons>
21284
- <ion-title>{{ titleText() }}</ion-title>
21285
- <ion-buttons slot="end">
21286
- <ion-button
21287
- (click)="confirmCrop()"
21288
- color="primary"
21289
- [strong]="true"
21290
- [disabled]="!croppedBlob()"
21291
- >
21292
- {{ confirmText() }}
21293
- </ion-button>
21294
- </ion-buttons>
21295
- </ion-toolbar>
21296
- </ion-header>
21297
-
21298
- <ion-content class="image-crop-content">
21299
- <image-cropper
21300
- [imageFile]="image()"
21301
- [aspectRatio]="aspectRatio()"
21302
- [maintainAspectRatio]="true"
21303
- [roundCropper]="roundCropper()"
21304
- [resizeToWidth]="resizeToWidth()"
21305
- format="jpeg"
21306
- outputType="blob"
21307
- (imageCropped)="onImageCropped($event)"
21308
- (loadImageFailed)="onLoadFailed()"
21309
- />
21310
- </ion-content>
21311
- `, isInline: true, styles: [":host{display:flex;flex-direction:column;height:100%}.image-crop-content{--background: var(--ion-color-dark)}.image-crop-content::part(scroll){display:flex;flex-direction:column}image-cropper{--cropper-outline-color: rgba(255, 255, 255, .3);--cropper-background-color: var(--ion-color-dark);flex:1;height:100%;max-height:calc(100vh - 56px)}::ng-deep .ngx-ic-component{height:100%!important}::ng-deep .ngx-ic-source-image{max-height:100%!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { 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"] }] }); }
21312
- }
21313
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ImageCropComponent, decorators: [{
21314
- type: Component,
21315
- args: [{ selector: 'val-image-crop', standalone: true, imports: [
21316
- CommonModule,
21317
- IonHeader,
21318
- IonToolbar,
21319
- IonTitle,
21320
- IonButtons,
21321
- IonButton,
21322
- IonContent,
21323
- ImageCropperComponent,
21324
- ], template: `
21325
- <ion-header>
21326
- <ion-toolbar>
21327
- <ion-buttons slot="start">
21328
- <ion-button (click)="cancel.emit()" color="medium">
21329
- {{ cancelText() }}
21330
- </ion-button>
21331
- </ion-buttons>
21332
- <ion-title>{{ titleText() }}</ion-title>
21333
- <ion-buttons slot="end">
21334
- <ion-button
21335
- (click)="confirmCrop()"
21336
- color="primary"
21337
- [strong]="true"
21338
- [disabled]="!croppedBlob()"
21339
- >
21340
- {{ confirmText() }}
21341
- </ion-button>
21342
- </ion-buttons>
21343
- </ion-toolbar>
21344
- </ion-header>
21345
-
21346
- <ion-content class="image-crop-content">
21347
- <image-cropper
21348
- [imageFile]="image()"
21349
- [aspectRatio]="aspectRatio()"
21350
- [maintainAspectRatio]="true"
21351
- [roundCropper]="roundCropper()"
21352
- [resizeToWidth]="resizeToWidth()"
21353
- format="jpeg"
21354
- outputType="blob"
21355
- (imageCropped)="onImageCropped($event)"
21356
- (loadImageFailed)="onLoadFailed()"
21357
- />
21358
- </ion-content>
21359
- `, styles: [":host{display:flex;flex-direction:column;height:100%}.image-crop-content{--background: var(--ion-color-dark)}.image-crop-content::part(scroll){display:flex;flex-direction:column}image-cropper{--cropper-outline-color: rgba(255, 255, 255, .3);--cropper-background-color: var(--ion-color-dark);flex:1;height:100%;max-height:calc(100vh - 56px)}::ng-deep .ngx-ic-component{height:100%!important}::ng-deep .ngx-ic-source-image{max-height:100%!important}\n"] }]
21360
- }], propDecorators: { cropComplete: [{
21361
- type: Output
21362
- }], cancel: [{
21363
- type: Output
21364
- }], loadFailed: [{
21365
- type: Output
21366
- }] } });
21367
-
21368
21199
  /**
21369
21200
  * Configuración de espaciado predefinida
21370
21201
  */
@@ -30754,15 +30585,6 @@ class AuthService {
30754
30585
  .put(`${this.baseUrl}/profile`, request)
30755
30586
  .pipe(catchError$1(error => this.handleAuthError(error)));
30756
30587
  }
30757
- /**
30758
- * Actualiza el avatar del usuario en el backend.
30759
- * Nota: El estado local del avatar se maneja a través de getProfile().
30760
- */
30761
- updateAvatar(request) {
30762
- return this.http
30763
- .put(`${this.baseUrl}/profile/avatar`, request)
30764
- .pipe(catchError$1(error => this.handleAuthError(error)));
30765
- }
30766
30588
  // =============================================
30767
30589
  // RECUPERACIÓN DE CONTRASEÑA
30768
30590
  // =============================================
@@ -30885,7 +30707,7 @@ class AuthService {
30885
30707
  */
30886
30708
  updateHandle(handle) {
30887
30709
  return this.http
30888
- .put(`${this.baseUrl}/profile/handle`, { handle })
30710
+ .put(`${this.baseUrl}/handle`, { handle })
30889
30711
  .pipe(catchError$1(error => this.handleAuthError(error)));
30890
30712
  }
30891
30713
  /**
@@ -33679,607 +33501,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
33679
33501
  type: Output
33680
33502
  }] } });
33681
33503
 
33682
- /**
33683
- * Default values for image processing
33684
- */
33685
- const IMAGE_DEFAULTS = {
33686
- maxWidth: 800,
33687
- maxHeight: 800,
33688
- quality: 0.8,
33689
- mimeType: 'image/jpeg',
33690
- maxSize: 10 * 1024 * 1024, // 10MB
33691
- allowedTypes: ['image/jpeg', 'image/png', 'image/webp', 'image/gif'],
33692
- thumbnailSize: 150,
33693
- };
33694
-
33695
- /**
33696
- * ImageService
33697
- *
33698
- * Service for image processing including compression, thumbnails, cropping and validation.
33699
- * Uses HTML Canvas for all operations - no external dependencies.
33700
- *
33701
- * @example
33702
- * ```typescript
33703
- * const imageService = inject(ImageService);
33704
- *
33705
- * // Compress an image
33706
- * const compressed = await imageService.compress(file, { maxWidth: 800, quality: 0.8 });
33707
- *
33708
- * // Generate thumbnail
33709
- * const thumb = await imageService.thumbnail(file, 150);
33710
- *
33711
- * // Validate before processing
33712
- * const validation = imageService.validate(file, { maxSize: 5 * 1024 * 1024 });
33713
- * if (!validation.valid) {
33714
- * console.error(validation.message);
33715
- * }
33716
- * ```
33717
- */
33718
- class ImageService {
33719
- /**
33720
- * Compress an image maintaining aspect ratio
33721
- * @param file - File or Blob to compress
33722
- * @param options - Compression options
33723
- * @returns Promise with processed image data
33724
- */
33725
- async compress(file, options) {
33726
- const opts = {
33727
- maxWidth: options?.maxWidth ?? IMAGE_DEFAULTS.maxWidth,
33728
- maxHeight: options?.maxHeight ?? IMAGE_DEFAULTS.maxHeight,
33729
- quality: options?.quality ?? IMAGE_DEFAULTS.quality,
33730
- mimeType: options?.mimeType ?? IMAGE_DEFAULTS.mimeType,
33731
- };
33732
- const img = await this.loadImage(file);
33733
- const { width, height } = this.calculateDimensions(img.width, img.height, opts.maxWidth, opts.maxHeight);
33734
- const canvas = document.createElement('canvas');
33735
- canvas.width = width;
33736
- canvas.height = height;
33737
- const ctx = canvas.getContext('2d');
33738
- ctx.drawImage(img, 0, 0, width, height);
33739
- const blob = await this.canvasToBlob(canvas, opts.mimeType, opts.quality);
33740
- const dataUrl = canvas.toDataURL(opts.mimeType, opts.quality);
33741
- return {
33742
- blob,
33743
- dataUrl,
33744
- width,
33745
- height,
33746
- size: blob.size,
33747
- };
33748
- }
33749
- /**
33750
- * Generate a square thumbnail from an image
33751
- * @param file - File or Blob to process
33752
- * @param size - Thumbnail size in pixels (default: 150)
33753
- * @returns Promise with processed thumbnail
33754
- */
33755
- async thumbnail(file, size) {
33756
- const thumbSize = size ?? IMAGE_DEFAULTS.thumbnailSize;
33757
- const img = await this.loadImage(file);
33758
- // Calculate square crop from center
33759
- const minDim = Math.min(img.width, img.height);
33760
- const cropX = (img.width - minDim) / 2;
33761
- const cropY = (img.height - minDim) / 2;
33762
- const canvas = document.createElement('canvas');
33763
- canvas.width = thumbSize;
33764
- canvas.height = thumbSize;
33765
- const ctx = canvas.getContext('2d');
33766
- ctx.drawImage(img, cropX, cropY, minDim, minDim, 0, 0, thumbSize, thumbSize);
33767
- const blob = await this.canvasToBlob(canvas, IMAGE_DEFAULTS.mimeType, 0.7 // Lower quality for thumbnails
33768
- );
33769
- const dataUrl = canvas.toDataURL(IMAGE_DEFAULTS.mimeType, 0.7);
33770
- return {
33771
- blob,
33772
- dataUrl,
33773
- width: thumbSize,
33774
- height: thumbSize,
33775
- size: blob.size,
33776
- };
33777
- }
33778
- /**
33779
- * Crop an image with specific coordinates
33780
- * @param file - File or Blob to crop
33781
- * @param cropData - Crop coordinates and dimensions
33782
- * @param options - Optional compression options for output
33783
- * @returns Promise with cropped image
33784
- */
33785
- async crop(file, cropData, options) {
33786
- const img = await this.loadImage(file);
33787
- const opts = {
33788
- quality: options?.quality ?? IMAGE_DEFAULTS.quality,
33789
- mimeType: options?.mimeType ?? IMAGE_DEFAULTS.mimeType,
33790
- };
33791
- const canvas = document.createElement('canvas');
33792
- canvas.width = cropData.width;
33793
- canvas.height = cropData.height;
33794
- const ctx = canvas.getContext('2d');
33795
- ctx.drawImage(img, cropData.x, cropData.y, cropData.width, cropData.height, 0, 0, cropData.width, cropData.height);
33796
- // Apply max dimensions if specified
33797
- if (options?.maxWidth || options?.maxHeight) {
33798
- return this.compress(await this.canvasToBlob(canvas, opts.mimeType, 1), options);
33799
- }
33800
- const blob = await this.canvasToBlob(canvas, opts.mimeType, opts.quality);
33801
- const dataUrl = canvas.toDataURL(opts.mimeType, opts.quality);
33802
- return {
33803
- blob,
33804
- dataUrl,
33805
- width: cropData.width,
33806
- height: cropData.height,
33807
- size: blob.size,
33808
- };
33809
- }
33810
- /**
33811
- * Validate an image file before processing
33812
- * @param file - File to validate
33813
- * @param options - Validation options
33814
- * @returns Validation result with error details if invalid
33815
- */
33816
- validate(file, options) {
33817
- const opts = {
33818
- maxSize: options?.maxSize ?? IMAGE_DEFAULTS.maxSize,
33819
- allowedTypes: options?.allowedTypes ?? IMAGE_DEFAULTS.allowedTypes,
33820
- };
33821
- // Check file type
33822
- if (!opts.allowedTypes.includes(file.type)) {
33823
- return {
33824
- valid: false,
33825
- error: 'invalidType',
33826
- message: `Formato no válido. Usa: ${opts.allowedTypes.map(t => t.split('/')[1].toUpperCase()).join(', ')}`,
33827
- };
33828
- }
33829
- // Check file size
33830
- if (file.size > opts.maxSize) {
33831
- const maxMB = Math.round(opts.maxSize / (1024 * 1024));
33832
- return {
33833
- valid: false,
33834
- error: 'fileTooLarge',
33835
- message: `La imagen es muy grande. Máximo ${maxMB}MB`,
33836
- };
33837
- }
33838
- return { valid: true };
33839
- }
33840
- /**
33841
- * Validate image dimensions (async - requires loading image)
33842
- * @param file - File to validate
33843
- * @param options - Validation options with minWidth/minHeight
33844
- * @returns Promise with validation result
33845
- */
33846
- async validateDimensions(file, options) {
33847
- const img = await this.loadImage(file);
33848
- if (options.minWidth && img.width < options.minWidth) {
33849
- return {
33850
- valid: false,
33851
- error: 'imageTooSmall',
33852
- message: `La imagen debe tener al menos ${options.minWidth}px de ancho`,
33853
- };
33854
- }
33855
- if (options.minHeight && img.height < options.minHeight) {
33856
- return {
33857
- valid: false,
33858
- error: 'imageTooSmall',
33859
- message: `La imagen debe tener al menos ${options.minHeight}px de alto`,
33860
- };
33861
- }
33862
- return { valid: true };
33863
- }
33864
- /**
33865
- * Convert a Blob/File to a data URL
33866
- */
33867
- async toDataUrl(file) {
33868
- return new Promise((resolve, reject) => {
33869
- const reader = new FileReader();
33870
- reader.onload = () => resolve(reader.result);
33871
- reader.onerror = reject;
33872
- reader.readAsDataURL(file);
33873
- });
33874
- }
33875
- /**
33876
- * Convert a data URL to a Blob
33877
- */
33878
- dataUrlToBlob(dataUrl) {
33879
- const arr = dataUrl.split(',');
33880
- const mime = arr[0].match(/:(.*?);/)[1];
33881
- const bstr = atob(arr[1]);
33882
- let n = bstr.length;
33883
- const u8arr = new Uint8Array(n);
33884
- while (n--) {
33885
- u8arr[n] = bstr.charCodeAt(n);
33886
- }
33887
- return new Blob([u8arr], { type: mime });
33888
- }
33889
- // ============== Private Helpers ==============
33890
- loadImage(file) {
33891
- return new Promise((resolve, reject) => {
33892
- const img = new Image();
33893
- img.onload = () => {
33894
- URL.revokeObjectURL(img.src);
33895
- resolve(img);
33896
- };
33897
- img.onerror = reject;
33898
- img.src = URL.createObjectURL(file);
33899
- });
33900
- }
33901
- calculateDimensions(originalWidth, originalHeight, maxWidth, maxHeight) {
33902
- let width = originalWidth;
33903
- let height = originalHeight;
33904
- // Scale down if necessary, maintaining aspect ratio
33905
- if (width > maxWidth) {
33906
- height = (height * maxWidth) / width;
33907
- width = maxWidth;
33908
- }
33909
- if (height > maxHeight) {
33910
- width = (width * maxHeight) / height;
33911
- height = maxHeight;
33912
- }
33913
- return {
33914
- width: Math.round(width),
33915
- height: Math.round(height),
33916
- };
33917
- }
33918
- canvasToBlob(canvas, mimeType, quality) {
33919
- return new Promise((resolve, reject) => {
33920
- canvas.toBlob((blob) => {
33921
- if (blob)
33922
- resolve(blob);
33923
- else
33924
- reject(new Error('Failed to create blob from canvas'));
33925
- }, mimeType, quality);
33926
- });
33927
- }
33928
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ImageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
33929
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ImageService, providedIn: 'root' }); }
33930
- }
33931
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ImageService, decorators: [{
33932
- type: Injectable,
33933
- args: [{ providedIn: 'root' }]
33934
- }] });
33935
-
33936
- /**
33937
- * Default values
33938
- */
33939
- const AVATAR_UPLOAD_DEFAULTS = {
33940
- size: 100,
33941
- editable: true,
33942
- storagePath: 'avatars',
33943
- i18nNamespace: 'AvatarUpload',
33944
- maxFileSize: 10 * 1024 * 1024, // 10MB
33945
- compressQuality: 0.8,
33946
- maxWidth: 800,
33947
- thumbnailSize: 150,
33948
- backgroundColor: '#6366f1', // Indigo
33949
- };
33950
-
33951
- addIcons({ cameraOutline });
33952
- /**
33953
- * AvatarUploadComponent
33954
- *
33955
- * A complete avatar upload solution with:
33956
- * - Image selection from device
33957
- * - Crop modal with round preview
33958
- * - Automatic compression and thumbnail generation
33959
- * - Upload to Firebase Storage
33960
- * - Backend sync via AuthService
33961
- *
33962
- * @example Basic usage
33963
- * ```html
33964
- * <val-avatar-upload
33965
- * [props]="{
33966
- * currentUrl: user()?.avatarUrl,
33967
- * initials: 'JD',
33968
- * size: 120
33969
- * }"
33970
- * (uploaded)="onAvatarUploaded($event)"
33971
- * (error)="onError($event)"
33972
- * />
33973
- * ```
33974
- */
33975
- class AvatarUploadComponent {
33976
- constructor() {
33977
- this.imageService = inject(ImageService);
33978
- this.storageService = inject(StorageService);
33979
- this.authService = inject(AuthService);
33980
- this.i18n = inject(I18nService);
33981
- /** Component configuration */
33982
- this.props = input({});
33983
- /** Emitted after successful upload and backend sync */
33984
- this.uploaded = new EventEmitter();
33985
- /** Emitted on any error during the process */
33986
- this.error = new EventEmitter();
33987
- /** Emitted when upload starts */
33988
- this.uploadStart = new EventEmitter();
33989
- // Internal state
33990
- this.loading = signal(false);
33991
- this.showCropModal = signal(false);
33992
- this.selectedFile = signal(null);
33993
- this.previewUrl = signal(null);
33994
- this.imageLoadError = signal(false);
33995
- /** Merged config with defaults */
33996
- this.config = computed(() => ({
33997
- ...AVATAR_UPLOAD_DEFAULTS,
33998
- ...this.props(),
33999
- }));
34000
- /** URL to display (preview takes priority over current) */
34001
- this.displayUrl = computed(() => {
34002
- if (this.imageLoadError())
34003
- return null;
34004
- return this.previewUrl() || this.config().currentUrl || null;
34005
- });
34006
- /** Aria label for edit button */
34007
- this.editButtonLabel = computed(() => {
34008
- this.i18n.lang();
34009
- return this.i18n.t('changePhoto', this.config().i18nNamespace) || 'Cambiar foto';
34010
- });
34011
- }
34012
- /** Open file picker dialog */
34013
- openFilePicker() {
34014
- this.fileInput.nativeElement.click();
34015
- }
34016
- /** Handle file selection */
34017
- onFileSelected(event) {
34018
- const input = event.target;
34019
- const file = input.files?.[0];
34020
- if (!file)
34021
- return;
34022
- // Reset input for same file selection
34023
- input.value = '';
34024
- // Validate file
34025
- const validation = this.imageService.validate(file, {
34026
- maxSize: this.config().maxFileSize,
34027
- allowedTypes: ['image/jpeg', 'image/png', 'image/webp'],
34028
- });
34029
- if (!validation.valid) {
34030
- this.emitError(validation.error, validation.message);
34031
- return;
34032
- }
34033
- // Open crop modal
34034
- this.selectedFile.set(file);
34035
- this.showCropModal.set(true);
34036
- }
34037
- /** Handle crop completion */
34038
- async onCropComplete(croppedBlob) {
34039
- this.showCropModal.set(false);
34040
- this.selectedFile.set(null);
34041
- await this.processAndUpload(croppedBlob);
34042
- }
34043
- /** Handle crop cancel */
34044
- onCropCancel() {
34045
- this.showCropModal.set(false);
34046
- this.selectedFile.set(null);
34047
- }
34048
- /** Handle crop load failure */
34049
- onCropLoadFailed() {
34050
- this.showCropModal.set(false);
34051
- this.selectedFile.set(null);
34052
- this.emitError('invalidType', this.i18n.t('loadFailed', this.config().i18nNamespace) || 'No se pudo cargar la imagen');
34053
- }
34054
- /** Handle image load error */
34055
- onImageError() {
34056
- this.imageLoadError.set(true);
34057
- }
34058
- /** Process cropped image and upload */
34059
- async processAndUpload(croppedBlob) {
34060
- this.loading.set(true);
34061
- this.uploadStart.emit();
34062
- try {
34063
- const config = this.config();
34064
- // 1. Compress image
34065
- const compressed = await this.imageService.compress(croppedBlob, {
34066
- maxWidth: config.maxWidth,
34067
- maxHeight: config.maxWidth,
34068
- quality: config.compressQuality,
34069
- });
34070
- // 2. Generate thumbnail
34071
- const thumbnail = await this.imageService.thumbnail(compressed.blob, config.thumbnailSize);
34072
- // 3. Set preview immediately
34073
- this.previewUrl.set(compressed.dataUrl);
34074
- this.imageLoadError.set(false);
34075
- // 4. Get user ID for storage path
34076
- const userId = this.authService.user()?.userId;
34077
- if (!userId) {
34078
- throw new Error('User not authenticated');
34079
- }
34080
- // 5. Upload to Firebase Storage
34081
- const timestamp = Date.now();
34082
- const avatarPath = `${config.storagePath}/${userId}/avatar_${timestamp}.jpg`;
34083
- const thumbPath = `${config.storagePath}/${userId}/thumb_${timestamp}.jpg`;
34084
- const [avatarResult, thumbResult] = await Promise.all([
34085
- this.storageService.uploadAndGetUrl(avatarPath, compressed.blob),
34086
- this.storageService.uploadAndGetUrl(thumbPath, thumbnail.blob),
34087
- ]);
34088
- // 6. Update backend
34089
- await firstValueFrom(this.authService.updateAvatar({
34090
- avatarUrl: avatarResult.downloadUrl,
34091
- avatarThumbnail: thumbResult.downloadUrl,
34092
- }));
34093
- // 7. Emit success
34094
- const result = {
34095
- avatarUrl: avatarResult.downloadUrl,
34096
- thumbnailUrl: thumbResult.downloadUrl,
34097
- };
34098
- this.uploaded.emit(result);
34099
- }
34100
- catch (err) {
34101
- // Revert preview on error
34102
- this.previewUrl.set(null);
34103
- const message = err instanceof Error
34104
- ? err.message
34105
- : this.i18n.t('uploadError', this.config().i18nNamespace) || 'Error al subir la imagen';
34106
- this.emitError('uploadFailed', message, err);
34107
- }
34108
- finally {
34109
- this.loading.set(false);
34110
- }
34111
- }
34112
- /** Emit error event */
34113
- emitError(type, message, originalError) {
34114
- this.error.emit({ type, message, originalError });
34115
- }
34116
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AvatarUploadComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
34117
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: AvatarUploadComponent, isStandalone: true, selector: "val-avatar-upload", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { uploaded: "uploaded", error: "error", uploadStart: "uploadStart" }, viewQueries: [{ propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true }], ngImport: i0, template: `
34118
- <div
34119
- class="avatar-upload"
34120
- [style.--avatar-size.px]="config().size"
34121
- [class.avatar-upload--loading]="loading()"
34122
- >
34123
- <div class="avatar-container">
34124
- <!-- Avatar Image or Initials -->
34125
- @if (displayUrl()) {
34126
- <img
34127
- class="avatar-image"
34128
- [src]="displayUrl()"
34129
- alt="Avatar"
34130
- (error)="onImageError()"
34131
- />
34132
- } @else {
34133
- <div
34134
- class="avatar-initials"
34135
- [style.background-color]="config().backgroundColor"
34136
- >
34137
- {{ config().initials || '?' }}
34138
- </div>
34139
- }
34140
-
34141
- <!-- Edit Button -->
34142
- @if (config().editable && !loading()) {
34143
- <button
34144
- class="edit-button"
34145
- type="button"
34146
- (click)="openFilePicker()"
34147
- [attr.aria-label]="editButtonLabel()"
34148
- >
34149
- <ion-icon name="camera-outline"></ion-icon>
34150
- </button>
34151
- }
34152
-
34153
- <!-- Loading Overlay -->
34154
- @if (loading()) {
34155
- <div class="loading-overlay">
34156
- <ion-spinner name="crescent"></ion-spinner>
34157
- </div>
34158
- }
34159
- </div>
34160
-
34161
- <!-- Hidden File Input -->
34162
- <input
34163
- #fileInput
34164
- type="file"
34165
- accept="image/jpeg,image/png,image/webp"
34166
- (change)="onFileSelected($event)"
34167
- hidden
34168
- />
34169
-
34170
- <!-- Crop Modal -->
34171
- <ion-modal
34172
- [isOpen]="showCropModal()"
34173
- (didDismiss)="onCropCancel()"
34174
- [breakpoints]="[0, 1]"
34175
- [initialBreakpoint]="1"
34176
- >
34177
- <ng-template>
34178
- @if (selectedFile()) {
34179
- <val-image-crop
34180
- [image]="selectedFile()!"
34181
- [aspectRatio]="1"
34182
- [roundCropper]="true"
34183
- [i18nNamespace]="config().i18nNamespace"
34184
- (cropComplete)="onCropComplete($event)"
34185
- (cancel)="onCropCancel()"
34186
- (loadFailed)="onCropLoadFailed()"
34187
- />
34188
- }
34189
- </ng-template>
34190
- </ion-modal>
34191
- </div>
34192
- `, isInline: true, styles: [".avatar-upload{--avatar-size: 100px;--edit-button-size: 32px;--edit-button-offset: 4px;display:inline-block}.avatar-container{position:relative;width:var(--avatar-size);height:var(--avatar-size);border-radius:50%;overflow:visible}.avatar-image{width:100%;height:100%;border-radius:50%;object-fit:cover;background-color:var(--ion-color-light)}.avatar-initials{width:100%;height:100%;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:calc(var(--avatar-size) * .4);font-weight:600;color:#fff;text-transform:uppercase;-webkit-user-select:none;user-select:none}.edit-button{position:absolute;bottom:var(--edit-button-offset);right:var(--edit-button-offset);width:var(--edit-button-size);height:var(--edit-button-size);border-radius:50%;border:2px solid white;background:var(--ion-color-primary);color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:transform .2s ease,background-color .2s ease;box-shadow:0 2px 8px #00000026}.edit-button ion-icon{font-size:calc(var(--edit-button-size) * .5)}.edit-button:hover{transform:scale(1.1);background:var(--ion-color-primary-shade)}.edit-button:active{transform:scale(.95)}.edit-button:focus-visible{outline:2px solid var(--ion-color-primary);outline-offset:2px}.loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;border-radius:50%;background:#00000080;display:flex;align-items:center;justify-content:center}.loading-overlay ion-spinner{--color: white;width:calc(var(--avatar-size) * .4);height:calc(var(--avatar-size) * .4)}.avatar-upload--loading .edit-button{display:none}.avatar-upload--loading .avatar-image,.avatar-upload--loading .avatar-initials{filter:brightness(.7)}@container (max-width: 60px){.edit-button{--edit-button-size: 24px;--edit-button-offset: 0}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: IonModal, selector: "ion-modal" }, { kind: "component", type: ImageCropComponent, selector: "val-image-crop", inputs: ["image", "aspectRatio", "roundCropper", "resizeToWidth", "i18nNamespace"], outputs: ["cropComplete", "cancel", "loadFailed"] }] }); }
34193
- }
34194
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AvatarUploadComponent, decorators: [{
34195
- type: Component,
34196
- args: [{ selector: 'val-avatar-upload', standalone: true, imports: [CommonModule, IonIcon, IonSpinner, IonModal, ImageCropComponent], template: `
34197
- <div
34198
- class="avatar-upload"
34199
- [style.--avatar-size.px]="config().size"
34200
- [class.avatar-upload--loading]="loading()"
34201
- >
34202
- <div class="avatar-container">
34203
- <!-- Avatar Image or Initials -->
34204
- @if (displayUrl()) {
34205
- <img
34206
- class="avatar-image"
34207
- [src]="displayUrl()"
34208
- alt="Avatar"
34209
- (error)="onImageError()"
34210
- />
34211
- } @else {
34212
- <div
34213
- class="avatar-initials"
34214
- [style.background-color]="config().backgroundColor"
34215
- >
34216
- {{ config().initials || '?' }}
34217
- </div>
34218
- }
34219
-
34220
- <!-- Edit Button -->
34221
- @if (config().editable && !loading()) {
34222
- <button
34223
- class="edit-button"
34224
- type="button"
34225
- (click)="openFilePicker()"
34226
- [attr.aria-label]="editButtonLabel()"
34227
- >
34228
- <ion-icon name="camera-outline"></ion-icon>
34229
- </button>
34230
- }
34231
-
34232
- <!-- Loading Overlay -->
34233
- @if (loading()) {
34234
- <div class="loading-overlay">
34235
- <ion-spinner name="crescent"></ion-spinner>
34236
- </div>
34237
- }
34238
- </div>
34239
-
34240
- <!-- Hidden File Input -->
34241
- <input
34242
- #fileInput
34243
- type="file"
34244
- accept="image/jpeg,image/png,image/webp"
34245
- (change)="onFileSelected($event)"
34246
- hidden
34247
- />
34248
-
34249
- <!-- Crop Modal -->
34250
- <ion-modal
34251
- [isOpen]="showCropModal()"
34252
- (didDismiss)="onCropCancel()"
34253
- [breakpoints]="[0, 1]"
34254
- [initialBreakpoint]="1"
34255
- >
34256
- <ng-template>
34257
- @if (selectedFile()) {
34258
- <val-image-crop
34259
- [image]="selectedFile()!"
34260
- [aspectRatio]="1"
34261
- [roundCropper]="true"
34262
- [i18nNamespace]="config().i18nNamespace"
34263
- (cropComplete)="onCropComplete($event)"
34264
- (cancel)="onCropCancel()"
34265
- (loadFailed)="onCropLoadFailed()"
34266
- />
34267
- }
34268
- </ng-template>
34269
- </ion-modal>
34270
- </div>
34271
- `, styles: [".avatar-upload{--avatar-size: 100px;--edit-button-size: 32px;--edit-button-offset: 4px;display:inline-block}.avatar-container{position:relative;width:var(--avatar-size);height:var(--avatar-size);border-radius:50%;overflow:visible}.avatar-image{width:100%;height:100%;border-radius:50%;object-fit:cover;background-color:var(--ion-color-light)}.avatar-initials{width:100%;height:100%;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:calc(var(--avatar-size) * .4);font-weight:600;color:#fff;text-transform:uppercase;-webkit-user-select:none;user-select:none}.edit-button{position:absolute;bottom:var(--edit-button-offset);right:var(--edit-button-offset);width:var(--edit-button-size);height:var(--edit-button-size);border-radius:50%;border:2px solid white;background:var(--ion-color-primary);color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:transform .2s ease,background-color .2s ease;box-shadow:0 2px 8px #00000026}.edit-button ion-icon{font-size:calc(var(--edit-button-size) * .5)}.edit-button:hover{transform:scale(1.1);background:var(--ion-color-primary-shade)}.edit-button:active{transform:scale(.95)}.edit-button:focus-visible{outline:2px solid var(--ion-color-primary);outline-offset:2px}.loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;border-radius:50%;background:#00000080;display:flex;align-items:center;justify-content:center}.loading-overlay ion-spinner{--color: white;width:calc(var(--avatar-size) * .4);height:calc(var(--avatar-size) * .4)}.avatar-upload--loading .edit-button{display:none}.avatar-upload--loading .avatar-image,.avatar-upload--loading .avatar-initials{filter:brightness(.7)}@container (max-width: 60px){.edit-button{--edit-button-size: 24px;--edit-button-offset: 0}}\n"] }]
34272
- }], propDecorators: { fileInput: [{
34273
- type: ViewChild,
34274
- args: ['fileInput']
34275
- }], uploaded: [{
34276
- type: Output
34277
- }], error: [{
34278
- type: Output
34279
- }], uploadStart: [{
34280
- type: Output
34281
- }] } });
34282
-
34283
33504
  class LayoutComponent {
34284
33505
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
34285
33506
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: LayoutComponent, isStandalone: true, selector: "val-layout", ngImport: i0, template: `
@@ -41888,5 +41109,5 @@ function buildFooterLinks(links, t) {
41888
41109
  * Generated bundle index. Do not edit.
41889
41110
  */
41890
41111
 
41891
- export { ACTION_CARD_DEFAULTS, AD_SIZE_MAP, API_TABLE_COLUMN_LABELS, ARTICLE_SPACING, AVATAR_UPLOAD_DEFAULTS, AccordionComponent, ActionCardComponent, ActionHeaderComponent, ActionType, AdSlotComponent, AdsLoaderService, AdsService, AlertBoxComponent, AnalyticsErrorHandler, AnalyticsRouterTracker, AnalyticsService, AppConfigService, ArticleBuilder, ArticleComponent, AuthBackgroundComponent, AuthService, AuthStateService, AuthStorageService, AuthSyncService, AvatarComponent, AvatarUploadComponent, BOTTOM_NAV_DEFAULTS, BannerComponent, BaseDefault, BottomNavComponent, BoxComponent, BreadcrumbComponent, ButtonComponent, ButtonGroupComponent, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CURRENCY_INFO, CardComponent, CardSection, CardType, CardsCarouselComponent, CheckInputComponent, ChipGroupComponent, ClearDefault, ClearDefaultBlock, ClearDefaultFull, ClearDefaultRound, ClearDefaultRoundBlock, ClearDefaultRoundFull, CodeDisplayComponent, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ContentLoaderComponent, ContentReactionComponent, CountdownComponent, CurrencyInputComponent, DEFAULT_ADS_CONFIG, DEFAULT_APP_CONFIG_SERVICE_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_BACK_HEADER, DEFAULT_CANCEL_BUTTON, DEFAULT_CONFIRM_BUTTON, DEFAULT_COUNTDOWN_LABELS, DEFAULT_COUNTDOWN_LABELS_EN, DEFAULT_EMPTY_STATE, DEFAULT_EMULATOR_CONFIG, DEFAULT_FEEDBACK_CONFIG, DEFAULT_FEEDBACK_TYPE_OPTIONS, DEFAULT_HOME_HEADER, DEFAULT_INFINITE_LIST_METADATA, DEFAULT_LEGEND_LABELS, DEFAULT_MODAL_CANCEL_BUTTON, DEFAULT_MODAL_CONFIRM_BUTTON, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PAYMENT_STATUS_COLORS, DEFAULT_PAYMENT_STATUS_LABELS, DEFAULT_PLATFORMS, DEFAULT_REFRESHER_METADATA, DEFAULT_SKELETON_CONFIG, DEFAULT_STATUS_COLORS, DEFAULT_STATUS_LABELS, DEFAULT_WINNER_LABELS, DataTableComponent, DateInputComponent, DateRangeInputComponent, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsCalloutComponent, DocsCodeExampleComponent, DocsLayoutComponent, DocsNavLinksComponent, DocsNavigationService, DocsPageComponent, DocsSearchComponent, DocsSectionComponent, DocsShellComponent, DocsSidebarComponent, DocsTocComponent, DownloadService, EmailInputComponent, ExpandableTextComponent, FEATURES_LIST_DEFAULTS, FabComponent, FeaturesListComponent, FeedbackFormComponent, FeedbackService, FileInputComponent, FirebaseService, FirestoreCollectionFactory, FirestoreService, FooterComponent, FooterLinksComponent, FormComponent, FormFooterComponent, FormSkeletonComponent, FunHeaderComponent, GlowCardComponent, GridSkeletonComponent, HeaderComponent, HintComponent, HorizontalScrollComponent, HourInputComponent, HrefComponent, I18nService, IMAGE_DEFAULTS, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, Icon, IconComponent, IconService, ImageComponent, ImageCropComponent, ImageService, InAppBrowserService, InfiniteListComponent, InfoComponent, InputI18nHelper, InputType, ItemListComponent, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LOGIN_DEFAULTS, LanguageSelectorComponent, LayeredCardComponent, LayoutComponent, LinkComponent, LinkProcessorService, LinkedProvidersComponent, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LoadingDirective, LocalStorageService, LocaleService, LoginComponent, MODAL_SIZES, MOTION, MaintenancePageComponent, MenuComponent, MessagingService, MetaService, ModalService, MultiSelectSearchComponent, NavigationService, NoContentComponent, NotesBoxComponent, NotificationsService, NumberFromToComponent, NumberInputComponent, NumberStepperComponent, OAUTH_PROVIDERS_INFO, OAuthCallbackComponent, OAuthService, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PLATFORM_CONFIGS, PageContentComponent, PageTemplateComponent, PageWrapperComponent, PaginationComponent, PaginationService, ParticipantCardComponent, PasswordInputComponent, PhoneInputComponent, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PresetService, PriceTagComponent, PrimarySolidBlockButton, PrimarySolidBlockHrefButton, PrimarySolidBlockIconButton, PrimarySolidBlockIconHrefButton, PrimarySolidDefaultRoundButton, PrimarySolidDefaultRoundHrefButton, PrimarySolidDefaultRoundIconButton, PrimarySolidDefaultRoundIconHrefButton, PrimarySolidFullButton, PrimarySolidFullHrefButton, PrimarySolidFullIconButton, PrimarySolidFullIconHrefButton, PrimarySolidLargeRoundButton, PrimarySolidLargeRoundHrefButton, PrimarySolidLargeRoundIconButton, PrimarySolidLargeRoundIconHrefButton, PrimarySolidSmallRoundButton, PrimarySolidSmallRoundHrefButton, PrimarySolidSmallRoundIconButton, PrimarySolidSmallRoundIconHrefButton, ProcessLinksPipe, ProfileSkeletonComponent, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrCodeComponent, QrGeneratorService, QueryBuilder, QuoteBoxComponent, RadioInputComponent, RaffleStatusCardComponent, RangeInputComponent, RatingComponent, RecapCardComponent, RefresherComponent, RightsFooterComponent, RotatingTextComponent, SKELETON_PRESETS, SearchSelectorComponent, SearchbarComponent, SecondarySolidBlockButton, SecondarySolidBlockHrefButton, SecondarySolidBlockIconButton, SecondarySolidBlockIconHrefButton, SecondarySolidDefaultRoundButton, SecondarySolidDefaultRoundHrefButton, SecondarySolidDefaultRoundIconButton, SecondarySolidDefaultRoundIconHrefButton, SecondarySolidFullButton, SecondarySolidFullHrefButton, SecondarySolidFullIconButton, SecondarySolidFullIconHrefButton, SecondarySolidLargeRoundButton, SecondarySolidLargeRoundHrefButton, SecondarySolidLargeRoundIconButton, SecondarySolidLargeRoundIconHrefButton, SecondarySolidSmallRoundButton, SecondarySolidSmallRoundHrefButton, SecondarySolidSmallRoundIconButton, SecondarySolidSmallRoundIconHrefButton, SegmentControlComponent, SelectSearchComponent, SessionService, ShareButtonsComponent, SimpleComponent, SkeletonComponent, SkeletonService, SolidBlockButton, SolidDefault, SolidDefaultBlock, SolidDefaultButton, SolidDefaultFull, SolidDefaultRound, SolidDefaultRoundBlock, SolidDefaultRoundButton, SolidDefaultRoundFull, SolidFullButton, SolidLargeButton, SolidLargeRoundButton, SolidSmallButton, SolidSmallRoundButton, StatsCardComponent, StepperComponent, StorageService, SwipeCarouselComponent, TabbedContentComponent, TableSkeletonComponent, TabsComponent, Terminal404Component, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeService, TicketGridComponent, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TranslatePipe, TypedCollection, UpdateBannerComponent, UsernameInputComponent, VALTECH_ADS_CONFIG, VALTECH_APP_CONFIG, VALTECH_AUTH_CONFIG, VALTECH_COMPANY_LINKS, VALTECH_DEFAULT_CONTENT, VALTECH_FEEDBACK_CONFIG, VALTECH_FIREBASE_CONFIG, VALTECH_FOOTER_I18N, VALTECH_FOOTER_LOGO, VALTECH_LANGUAGE_SELECTOR, VALTECH_SOCIAL_LINKS, VERSION, WinnerDisplayComponent, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, authGuard, authInterceptor, buildFooterLinks, buildPath, collections, createFirebaseConfig, createGlowCardProps, createInitialPaginationState, createNumberFromToField, createTitleProps, extractPathParams, getAppInfo, getAppVersion, getCollectionPath, getDocumentId, goToTop, guestGuard, hasEmulators, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isValidPath, joinPath, maxLength, permissionGuard, permissionGuardFromRoute, provideValtechAds, provideValtechAppConfig, provideValtechAuth, provideValtechAuthInterceptor, provideValtechFeedback, provideValtechFirebase, provideValtechI18n, provideValtechPresets, provideValtechSkeleton, query, replaceSpecialChars, resolveColor, resolveInputDefaultValue, roleGuard, storagePaths, superAdminGuard };
41112
+ export { ACTION_CARD_DEFAULTS, AD_SIZE_MAP, API_TABLE_COLUMN_LABELS, ARTICLE_SPACING, AccordionComponent, ActionCardComponent, ActionHeaderComponent, ActionType, AdSlotComponent, AdsLoaderService, AdsService, AlertBoxComponent, AnalyticsErrorHandler, AnalyticsRouterTracker, AnalyticsService, AppConfigService, ArticleBuilder, ArticleComponent, AuthBackgroundComponent, AuthService, AuthStateService, AuthStorageService, AuthSyncService, AvatarComponent, BOTTOM_NAV_DEFAULTS, BannerComponent, BaseDefault, BottomNavComponent, BoxComponent, BreadcrumbComponent, ButtonComponent, ButtonGroupComponent, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CURRENCY_INFO, CardComponent, CardSection, CardType, CardsCarouselComponent, CheckInputComponent, ChipGroupComponent, ClearDefault, ClearDefaultBlock, ClearDefaultFull, ClearDefaultRound, ClearDefaultRoundBlock, ClearDefaultRoundFull, CodeDisplayComponent, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ContentLoaderComponent, ContentReactionComponent, CountdownComponent, CurrencyInputComponent, DEFAULT_ADS_CONFIG, DEFAULT_APP_CONFIG_SERVICE_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_BACK_HEADER, DEFAULT_CANCEL_BUTTON, DEFAULT_CONFIRM_BUTTON, DEFAULT_COUNTDOWN_LABELS, DEFAULT_COUNTDOWN_LABELS_EN, DEFAULT_EMPTY_STATE, DEFAULT_EMULATOR_CONFIG, DEFAULT_FEEDBACK_CONFIG, DEFAULT_FEEDBACK_TYPE_OPTIONS, DEFAULT_HOME_HEADER, DEFAULT_INFINITE_LIST_METADATA, DEFAULT_LEGEND_LABELS, DEFAULT_MODAL_CANCEL_BUTTON, DEFAULT_MODAL_CONFIRM_BUTTON, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PAYMENT_STATUS_COLORS, DEFAULT_PAYMENT_STATUS_LABELS, DEFAULT_PLATFORMS, DEFAULT_REFRESHER_METADATA, DEFAULT_SKELETON_CONFIG, DEFAULT_STATUS_COLORS, DEFAULT_STATUS_LABELS, DEFAULT_WINNER_LABELS, DataTableComponent, DateInputComponent, DateRangeInputComponent, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsCalloutComponent, DocsCodeExampleComponent, DocsLayoutComponent, DocsNavLinksComponent, DocsNavigationService, DocsPageComponent, DocsSearchComponent, DocsSectionComponent, DocsShellComponent, DocsSidebarComponent, DocsTocComponent, DownloadService, EmailInputComponent, ExpandableTextComponent, FEATURES_LIST_DEFAULTS, FabComponent, FeaturesListComponent, FeedbackFormComponent, FeedbackService, FileInputComponent, FirebaseService, FirestoreCollectionFactory, FirestoreService, FooterComponent, FooterLinksComponent, FormComponent, FormFooterComponent, FormSkeletonComponent, FunHeaderComponent, GlowCardComponent, GridSkeletonComponent, HeaderComponent, HintComponent, HorizontalScrollComponent, HourInputComponent, HrefComponent, I18nService, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, Icon, IconComponent, IconService, ImageComponent, InAppBrowserService, InfiniteListComponent, InfoComponent, InputI18nHelper, InputType, ItemListComponent, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LOGIN_DEFAULTS, LanguageSelectorComponent, LayeredCardComponent, LayoutComponent, LinkComponent, LinkProcessorService, LinkedProvidersComponent, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LoadingDirective, LocalStorageService, LocaleService, LoginComponent, MODAL_SIZES, MOTION, MaintenancePageComponent, MenuComponent, MessagingService, MetaService, ModalService, MultiSelectSearchComponent, NavigationService, NoContentComponent, NotesBoxComponent, NotificationsService, NumberFromToComponent, NumberInputComponent, NumberStepperComponent, OAUTH_PROVIDERS_INFO, OAuthCallbackComponent, OAuthService, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PLATFORM_CONFIGS, PageContentComponent, PageTemplateComponent, PageWrapperComponent, PaginationComponent, PaginationService, ParticipantCardComponent, PasswordInputComponent, PhoneInputComponent, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PresetService, PriceTagComponent, PrimarySolidBlockButton, PrimarySolidBlockHrefButton, PrimarySolidBlockIconButton, PrimarySolidBlockIconHrefButton, PrimarySolidDefaultRoundButton, PrimarySolidDefaultRoundHrefButton, PrimarySolidDefaultRoundIconButton, PrimarySolidDefaultRoundIconHrefButton, PrimarySolidFullButton, PrimarySolidFullHrefButton, PrimarySolidFullIconButton, PrimarySolidFullIconHrefButton, PrimarySolidLargeRoundButton, PrimarySolidLargeRoundHrefButton, PrimarySolidLargeRoundIconButton, PrimarySolidLargeRoundIconHrefButton, PrimarySolidSmallRoundButton, PrimarySolidSmallRoundHrefButton, PrimarySolidSmallRoundIconButton, PrimarySolidSmallRoundIconHrefButton, ProcessLinksPipe, ProfileSkeletonComponent, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrCodeComponent, QrGeneratorService, QueryBuilder, QuoteBoxComponent, RadioInputComponent, RaffleStatusCardComponent, RangeInputComponent, RatingComponent, RecapCardComponent, RefresherComponent, RightsFooterComponent, RotatingTextComponent, SKELETON_PRESETS, SearchSelectorComponent, SearchbarComponent, SecondarySolidBlockButton, SecondarySolidBlockHrefButton, SecondarySolidBlockIconButton, SecondarySolidBlockIconHrefButton, SecondarySolidDefaultRoundButton, SecondarySolidDefaultRoundHrefButton, SecondarySolidDefaultRoundIconButton, SecondarySolidDefaultRoundIconHrefButton, SecondarySolidFullButton, SecondarySolidFullHrefButton, SecondarySolidFullIconButton, SecondarySolidFullIconHrefButton, SecondarySolidLargeRoundButton, SecondarySolidLargeRoundHrefButton, SecondarySolidLargeRoundIconButton, SecondarySolidLargeRoundIconHrefButton, SecondarySolidSmallRoundButton, SecondarySolidSmallRoundHrefButton, SecondarySolidSmallRoundIconButton, SecondarySolidSmallRoundIconHrefButton, SegmentControlComponent, SelectSearchComponent, SessionService, ShareButtonsComponent, SimpleComponent, SkeletonComponent, SkeletonService, SolidBlockButton, SolidDefault, SolidDefaultBlock, SolidDefaultButton, SolidDefaultFull, SolidDefaultRound, SolidDefaultRoundBlock, SolidDefaultRoundButton, SolidDefaultRoundFull, SolidFullButton, SolidLargeButton, SolidLargeRoundButton, SolidSmallButton, SolidSmallRoundButton, StatsCardComponent, StepperComponent, StorageService, SwipeCarouselComponent, TabbedContentComponent, TableSkeletonComponent, TabsComponent, Terminal404Component, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeService, TicketGridComponent, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TranslatePipe, TypedCollection, UpdateBannerComponent, UsernameInputComponent, VALTECH_ADS_CONFIG, VALTECH_APP_CONFIG, VALTECH_AUTH_CONFIG, VALTECH_COMPANY_LINKS, VALTECH_DEFAULT_CONTENT, VALTECH_FEEDBACK_CONFIG, VALTECH_FIREBASE_CONFIG, VALTECH_FOOTER_I18N, VALTECH_FOOTER_LOGO, VALTECH_LANGUAGE_SELECTOR, VALTECH_SOCIAL_LINKS, VERSION, WinnerDisplayComponent, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, authGuard, authInterceptor, buildFooterLinks, buildPath, collections, createFirebaseConfig, createGlowCardProps, createInitialPaginationState, createNumberFromToField, createTitleProps, extractPathParams, getAppInfo, getAppVersion, getCollectionPath, getDocumentId, goToTop, guestGuard, hasEmulators, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isValidPath, joinPath, maxLength, permissionGuard, permissionGuardFromRoute, provideValtechAds, provideValtechAppConfig, provideValtechAuth, provideValtechAuthInterceptor, provideValtechFeedback, provideValtechFirebase, provideValtechI18n, provideValtechPresets, provideValtechSkeleton, query, replaceSpecialChars, resolveColor, resolveInputDefaultValue, roleGuard, storagePaths, superAdminGuard };
41892
41113
  //# sourceMappingURL=valtech-components.mjs.map