valtech-components 2.0.676 → 2.0.678
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/esm2022/lib/components/molecules/image-crop/image-crop.component.mjs +174 -0
- package/esm2022/lib/components/molecules/image-crop/index.mjs +2 -0
- package/esm2022/lib/components/organisms/avatar-upload/avatar-upload.component.mjs +345 -0
- package/esm2022/lib/components/organisms/avatar-upload/types.mjs +15 -0
- package/esm2022/lib/components/organisms/bottom-nav/bottom-nav.component.mjs +5 -3
- package/esm2022/lib/components/organisms/bottom-nav/types.mjs +2 -1
- package/esm2022/lib/services/auth/auth.service.mjs +11 -2
- package/esm2022/lib/services/auth/types.mjs +1 -1
- package/esm2022/lib/services/image/image.service.mjs +244 -0
- package/esm2022/lib/services/image/index.mjs +3 -0
- package/esm2022/lib/services/image/types.mjs +13 -0
- package/esm2022/lib/version.mjs +2 -2
- package/esm2022/public-api.mjs +7 -1
- package/fesm2022/valtech-components.mjs +788 -6
- package/fesm2022/valtech-components.mjs.map +1 -1
- package/lib/components/molecules/image-crop/image-crop.component.d.ts +59 -0
- package/lib/components/molecules/image-crop/index.d.ts +1 -0
- package/lib/components/organisms/avatar-upload/avatar-upload.component.d.ts +82 -0
- package/lib/components/organisms/avatar-upload/types.d.ts +62 -0
- package/lib/components/organisms/bottom-nav/bottom-nav.component.d.ts +1 -0
- package/lib/components/organisms/bottom-nav/types.d.ts +2 -0
- package/lib/services/auth/auth.service.d.ts +6 -1
- package/lib/services/auth/types.d.ts +18 -0
- package/lib/services/image/image.service.d.ts +76 -0
- package/lib/services/image/index.d.ts +2 -0
- package/lib/services/image/types.d.ts +74 -0
- package/lib/version.d.ts +1 -1
- package/package.json +6 -2
- package/public-api.d.ts +4 -0
|
@@ -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, 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, cameraOutline, 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,6 +39,7 @@ 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';
|
|
42
43
|
import * as i1$8 from '@angular/common/http';
|
|
43
44
|
import { provideHttpClient, withInterceptors, HttpClient } from '@angular/common/http';
|
|
44
45
|
import { Capacitor } from '@capacitor/core';
|
|
@@ -49,7 +50,7 @@ import 'prismjs/components/prism-json';
|
|
|
49
50
|
* Current version of valtech-components.
|
|
50
51
|
* This is automatically updated during the publish process.
|
|
51
52
|
*/
|
|
52
|
-
const VERSION = '2.0.
|
|
53
|
+
const VERSION = '2.0.678';
|
|
53
54
|
|
|
54
55
|
/**
|
|
55
56
|
* Servicio para gestionar presets de componentes.
|
|
@@ -21196,6 +21197,174 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
21196
21197
|
type: Input
|
|
21197
21198
|
}] } });
|
|
21198
21199
|
|
|
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: [".image-crop-content{--background: var(--ion-color-dark)}image-cropper{--cropper-outline-color: rgba(255, 255, 255, .3);--cropper-background-color: var(--ion-color-dark)}\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: [".image-crop-content{--background: var(--ion-color-dark)}image-cropper{--cropper-outline-color: rgba(255, 255, 255, .3);--cropper-background-color: var(--ion-color-dark)}\n"] }]
|
|
21360
|
+
}], propDecorators: { cropComplete: [{
|
|
21361
|
+
type: Output
|
|
21362
|
+
}], cancel: [{
|
|
21363
|
+
type: Output
|
|
21364
|
+
}], loadFailed: [{
|
|
21365
|
+
type: Output
|
|
21366
|
+
}] } });
|
|
21367
|
+
|
|
21199
21368
|
/**
|
|
21200
21369
|
* Configuración de espaciado predefinida
|
|
21201
21370
|
*/
|
|
@@ -30585,6 +30754,15 @@ class AuthService {
|
|
|
30585
30754
|
.put(`${this.baseUrl}/profile`, request)
|
|
30586
30755
|
.pipe(catchError$1(error => this.handleAuthError(error)));
|
|
30587
30756
|
}
|
|
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
|
+
}
|
|
30588
30766
|
// =============================================
|
|
30589
30767
|
// RECUPERACIÓN DE CONTRASEÑA
|
|
30590
30768
|
// =============================================
|
|
@@ -30707,7 +30885,7 @@ class AuthService {
|
|
|
30707
30885
|
*/
|
|
30708
30886
|
updateHandle(handle) {
|
|
30709
30887
|
return this.http
|
|
30710
|
-
.put(`${this.baseUrl}/handle`, { handle })
|
|
30888
|
+
.put(`${this.baseUrl}/profile/handle`, { handle })
|
|
30711
30889
|
.pipe(catchError$1(error => this.handleAuthError(error)));
|
|
30712
30890
|
}
|
|
30713
30891
|
/**
|
|
@@ -33142,6 +33320,7 @@ const BOTTOM_NAV_DEFAULTS = {
|
|
|
33142
33320
|
borderRadius: '16px 16px 0 0',
|
|
33143
33321
|
elevated: false,
|
|
33144
33322
|
translucent: true,
|
|
33323
|
+
floating: false,
|
|
33145
33324
|
},
|
|
33146
33325
|
};
|
|
33147
33326
|
|
|
@@ -33358,6 +33537,7 @@ class BottomNavComponent {
|
|
|
33358
33537
|
class="bottom-nav"
|
|
33359
33538
|
[class.bottom-nav--elevated]="config().theme.elevated"
|
|
33360
33539
|
[class.bottom-nav--translucent]="config().theme.translucent"
|
|
33540
|
+
[class.bottom-nav--floating]="config().theme.floating"
|
|
33361
33541
|
[class.bottom-nav--safe-area]="config().safeArea"
|
|
33362
33542
|
[class.bottom-nav--hide-labels]="config().hideLabels"
|
|
33363
33543
|
[ngStyle]="getThemeStyles()"
|
|
@@ -33420,7 +33600,7 @@ class BottomNavComponent {
|
|
|
33420
33600
|
}
|
|
33421
33601
|
</div>
|
|
33422
33602
|
</nav>
|
|
33423
|
-
`, isInline: true, styles: [":host{display:block;position:fixed;bottom:0;left:0;right:0;z-index:100;pointer-events:none}.bottom-nav{--bottom-nav-bg: var(--ion-background-color);--bottom-nav-active: var(--ion-color-primary);--bottom-nav-inactive: var(--ion-color-medium);--bottom-nav-radius: 16px 16px 0 0;--bottom-nav-height: 64px;--bottom-nav-fab-size: 56px;--fab-color: var(--ion-color-primary);pointer-events:auto;background:var(--bottom-nav-bg);border-radius:var(--bottom-nav-radius);height:var(--bottom-nav-height);padding:0 8px}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000014}.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.75);backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);border-top:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.06)}.bottom-nav--safe-area{padding-bottom:env(safe-area-inset-bottom,0);height:calc(var(--bottom-nav-height) + env(safe-area-inset-bottom,0))}.bottom-nav__container{display:flex;align-items:center;justify-content:space-around;height:var(--bottom-nav-height);max-width:500px;margin:0 auto}.bottom-nav__tab{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;height:100%;padding:8px 4px;background:transparent;border:none;cursor:pointer;position:relative;overflow:hidden;color:var(--bottom-nav-inactive);transition:color .2s ease;max-width:80px;--ripple-color: var(--bottom-nav-active)}.bottom-nav__tab:focus-visible{outline:2px solid var(--bottom-nav-active);outline-offset:-2px;border-radius:8px}.bottom-nav__tab--active{color:var(--bottom-nav-active)}.bottom-nav__tab--disabled{opacity:.4;cursor:not-allowed;pointer-events:none}.bottom-nav__tab[data-animation=scale] .bottom-nav__tab-icon{transition:transform .2s cubic-bezier(.4,0,.2,1)}.bottom-nav__tab[data-animation=scale].bottom-nav__tab--active .bottom-nav__tab-icon{transform:scale(1.15)}.bottom-nav__tab[data-animation=fade]{transition:opacity .2s ease,color .2s ease}.bottom-nav__tab[data-animation=fade]:not(.bottom-nav__tab--active){opacity:.6}.bottom-nav__tab[data-animation=slide] .bottom-nav__tab-label{transition:transform .2s ease,opacity .2s ease;transform:translateY(6px);opacity:0}.bottom-nav__tab[data-animation=slide].bottom-nav__tab--active .bottom-nav__tab-label{transform:translateY(0);opacity:1}.bottom-nav__tab-icon{position:relative;font-size:24px;line-height:1}.bottom-nav__tab-icon ion-icon{display:block}.bottom-nav__tab-label{font-size:11px;font-weight:500;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;letter-spacing:.01em}.bottom-nav__badge{position:absolute;top:-4px;right:-10px;min-width:18px;height:18px;padding:0 5px;font-size:10px;font-weight:600;line-height:18px;text-align:center;color:#fff;background-color:var(--ion-color-danger);border-radius:9px;box-shadow:0 1px 3px #0003}.bottom-nav__badge--dot{min-width:10px;width:10px;height:10px;padding:0;top:-2px;right:-4px;border-radius:50%}.bottom-nav__fab{position:relative;flex-shrink:0;width:var(--bottom-nav-fab-size);height:var(--bottom-nav-fab-size);margin:0 12px;margin-top:calc(var(--bottom-nav-fab-size) * -.35);background:var(--fab-color);border:none;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#fff;font-size:28px;box-shadow:0 4px 14px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.4);transition:transform .2s cubic-bezier(.4,0,.2,1),box-shadow .2s ease;overflow:hidden;--ripple-color: rgba(255, 255, 255, .3)}.bottom-nav__fab:hover{transform:scale(1.08);box-shadow:0 6px 20px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.5)}.bottom-nav__fab:active{transform:scale(.95)}.bottom-nav__fab:focus-visible{outline:3px solid white;outline-offset:2px}.bottom-nav__fab--small{--bottom-nav-fab-size: 48px;font-size:24px;margin-top:-14.4px}.bottom-nav__fab ion-icon{display:block}.bottom-nav--hide-labels{--bottom-nav-height: 56px}.bottom-nav--hide-labels .bottom-nav__tab-label{display:none}.bottom-nav--hide-labels .bottom-nav__tab-icon{font-size:26px}@media (prefers-color-scheme: dark){.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 0, 0, 0),.8);border-top-color:#ffffff14}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000040}.bottom-nav__fab{box-shadow:0 4px 14px #0006}.bottom-nav__fab:hover{box-shadow:0 6px 20px #00000080}}:host-context(.dark) .bottom-nav--translucent,:host-context(body.dark) .bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.85);border-top-color:#ffffff14}:host-context(.dark) .bottom-nav--elevated,:host-context(body.dark) .bottom-nav--elevated,:host-context([data-theme=dark]) .bottom-nav--elevated{box-shadow:0 -4px 20px #0000004d}:host-context(.dark) .bottom-nav__fab,:host-context(body.dark) .bottom-nav__fab,:host-context([data-theme=dark]) .bottom-nav__fab{box-shadow:0 4px 14px #00000080}:host-context(.dark) .bottom-nav__fab:hover,:host-context(body.dark) .bottom-nav__fab:hover,:host-context([data-theme=dark]) .bottom-nav__fab:hover{box-shadow:0 6px 20px #0009}@supports (padding-bottom: env(safe-area-inset-bottom)){.bottom-nav--safe-area .bottom-nav__container{height:var(--bottom-nav-height)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonRippleEffect, selector: "ion-ripple-effect", inputs: ["type"] }] }); }
|
|
33603
|
+
`, isInline: true, styles: [":host{display:block;position:fixed;bottom:0;left:0;right:0;z-index:100;pointer-events:none}.bottom-nav{--bottom-nav-bg: var(--ion-background-color);--bottom-nav-active: var(--ion-color-primary);--bottom-nav-inactive: var(--ion-color-medium);--bottom-nav-radius: 16px 16px 0 0;--bottom-nav-height: 64px;--bottom-nav-fab-size: 56px;--fab-color: var(--ion-color-primary);pointer-events:auto;background:var(--bottom-nav-bg);border-radius:var(--bottom-nav-radius);height:var(--bottom-nav-height);padding:0 8px}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000014}.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.75);backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);border-top:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.06)}.bottom-nav--floating{margin:0 16px 16px;border-radius:28px;border-top:none;box-shadow:0 4px 24px #00000014,0 8px 32px #0000000a;--bottom-nav-radius: 28px}.bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.82);backdrop-filter:blur(28px) saturate(200%);-webkit-backdrop-filter:blur(28px) saturate(200%);border:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.04)}.bottom-nav--floating.bottom-nav--elevated{box-shadow:0 4px 24px #0000001a,0 12px 48px #00000014}.bottom-nav--floating.bottom-nav--safe-area{margin-bottom:calc(16px + env(safe-area-inset-bottom,0))}.bottom-nav--safe-area{padding-bottom:env(safe-area-inset-bottom,0);height:calc(var(--bottom-nav-height) + env(safe-area-inset-bottom,0))}.bottom-nav__container{display:flex;align-items:center;justify-content:space-around;height:var(--bottom-nav-height);max-width:500px;margin:0 auto}.bottom-nav__tab{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;height:100%;padding:8px 4px;background:transparent;border:none;cursor:pointer;position:relative;overflow:hidden;color:var(--bottom-nav-inactive);transition:color .2s ease;max-width:80px;--ripple-color: var(--bottom-nav-active)}.bottom-nav__tab:focus-visible{outline:2px solid var(--bottom-nav-active);outline-offset:-2px;border-radius:8px}.bottom-nav__tab--active{color:var(--bottom-nav-active)}.bottom-nav__tab--disabled{opacity:.4;cursor:not-allowed;pointer-events:none}.bottom-nav__tab[data-animation=scale] .bottom-nav__tab-icon{transition:transform .2s cubic-bezier(.4,0,.2,1)}.bottom-nav__tab[data-animation=scale].bottom-nav__tab--active .bottom-nav__tab-icon{transform:scale(1.15)}.bottom-nav__tab[data-animation=fade]{transition:opacity .2s ease,color .2s ease}.bottom-nav__tab[data-animation=fade]:not(.bottom-nav__tab--active){opacity:.6}.bottom-nav__tab[data-animation=slide] .bottom-nav__tab-label{transition:transform .2s ease,opacity .2s ease;transform:translateY(6px);opacity:0}.bottom-nav__tab[data-animation=slide].bottom-nav__tab--active .bottom-nav__tab-label{transform:translateY(0);opacity:1}.bottom-nav__tab-icon{position:relative;font-size:24px;line-height:1}.bottom-nav__tab-icon ion-icon{display:block}.bottom-nav__tab-label{font-size:11px;font-weight:500;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;letter-spacing:.01em}.bottom-nav__badge{position:absolute;top:-4px;right:-10px;min-width:18px;height:18px;padding:0 5px;font-size:10px;font-weight:600;line-height:18px;text-align:center;color:#fff;background-color:var(--ion-color-danger);border-radius:9px;box-shadow:0 1px 3px #0003}.bottom-nav__badge--dot{min-width:10px;width:10px;height:10px;padding:0;top:-2px;right:-4px;border-radius:50%}.bottom-nav__fab{position:relative;flex-shrink:0;width:var(--bottom-nav-fab-size);height:var(--bottom-nav-fab-size);margin:0 12px;margin-top:calc(var(--bottom-nav-fab-size) * -.35);background:var(--fab-color);border:none;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#fff;font-size:28px;box-shadow:0 4px 14px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.4);transition:transform .2s cubic-bezier(.4,0,.2,1),box-shadow .2s ease;overflow:hidden;--ripple-color: rgba(255, 255, 255, .3)}.bottom-nav__fab:hover{transform:scale(1.08);box-shadow:0 6px 20px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.5)}.bottom-nav__fab:active{transform:scale(.95)}.bottom-nav__fab:focus-visible{outline:3px solid white;outline-offset:2px}.bottom-nav__fab--small{--bottom-nav-fab-size: 48px;font-size:24px;margin-top:-14.4px}.bottom-nav__fab ion-icon{display:block}.bottom-nav--hide-labels{--bottom-nav-height: 56px}.bottom-nav--hide-labels .bottom-nav__tab-label{display:none}.bottom-nav--hide-labels .bottom-nav__tab-icon{font-size:26px}@media (prefers-color-scheme: dark){.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 0, 0, 0),.8);border-top-color:#ffffff14}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000040}.bottom-nav--floating{box-shadow:0 4px 24px #0003,0 8px 32px #00000026}.bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.88);border-color:#ffffff0f}.bottom-nav__fab{box-shadow:0 4px 14px #0006}.bottom-nav__fab:hover{box-shadow:0 6px 20px #00000080}}:host-context(.dark) .bottom-nav--translucent,:host-context(body.dark) .bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.85);border-top-color:#ffffff14}:host-context(.dark) .bottom-nav--elevated,:host-context(body.dark) .bottom-nav--elevated,:host-context([data-theme=dark]) .bottom-nav--elevated{box-shadow:0 -4px 20px #0000004d}:host-context(.dark) .bottom-nav--floating,:host-context(body.dark) .bottom-nav--floating,:host-context([data-theme=dark]) .bottom-nav--floating{box-shadow:0 4px 24px #0003,0 8px 32px #00000026}:host-context(.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context(body.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.88);border-color:#ffffff0f}:host-context(.dark) .bottom-nav__fab,:host-context(body.dark) .bottom-nav__fab,:host-context([data-theme=dark]) .bottom-nav__fab{box-shadow:0 4px 14px #00000080}:host-context(.dark) .bottom-nav__fab:hover,:host-context(body.dark) .bottom-nav__fab:hover,:host-context([data-theme=dark]) .bottom-nav__fab:hover{box-shadow:0 6px 20px #0009}@supports (padding-bottom: env(safe-area-inset-bottom)){.bottom-nav--safe-area .bottom-nav__container{height:var(--bottom-nav-height)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonRippleEffect, selector: "ion-ripple-effect", inputs: ["type"] }] }); }
|
|
33424
33604
|
}
|
|
33425
33605
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: BottomNavComponent, decorators: [{
|
|
33426
33606
|
type: Component,
|
|
@@ -33429,6 +33609,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
33429
33609
|
class="bottom-nav"
|
|
33430
33610
|
[class.bottom-nav--elevated]="config().theme.elevated"
|
|
33431
33611
|
[class.bottom-nav--translucent]="config().theme.translucent"
|
|
33612
|
+
[class.bottom-nav--floating]="config().theme.floating"
|
|
33432
33613
|
[class.bottom-nav--safe-area]="config().safeArea"
|
|
33433
33614
|
[class.bottom-nav--hide-labels]="config().hideLabels"
|
|
33434
33615
|
[ngStyle]="getThemeStyles()"
|
|
@@ -33491,13 +33672,614 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
33491
33672
|
}
|
|
33492
33673
|
</div>
|
|
33493
33674
|
</nav>
|
|
33494
|
-
`, styles: [":host{display:block;position:fixed;bottom:0;left:0;right:0;z-index:100;pointer-events:none}.bottom-nav{--bottom-nav-bg: var(--ion-background-color);--bottom-nav-active: var(--ion-color-primary);--bottom-nav-inactive: var(--ion-color-medium);--bottom-nav-radius: 16px 16px 0 0;--bottom-nav-height: 64px;--bottom-nav-fab-size: 56px;--fab-color: var(--ion-color-primary);pointer-events:auto;background:var(--bottom-nav-bg);border-radius:var(--bottom-nav-radius);height:var(--bottom-nav-height);padding:0 8px}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000014}.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.75);backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);border-top:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.06)}.bottom-nav--safe-area{padding-bottom:env(safe-area-inset-bottom,0);height:calc(var(--bottom-nav-height) + env(safe-area-inset-bottom,0))}.bottom-nav__container{display:flex;align-items:center;justify-content:space-around;height:var(--bottom-nav-height);max-width:500px;margin:0 auto}.bottom-nav__tab{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;height:100%;padding:8px 4px;background:transparent;border:none;cursor:pointer;position:relative;overflow:hidden;color:var(--bottom-nav-inactive);transition:color .2s ease;max-width:80px;--ripple-color: var(--bottom-nav-active)}.bottom-nav__tab:focus-visible{outline:2px solid var(--bottom-nav-active);outline-offset:-2px;border-radius:8px}.bottom-nav__tab--active{color:var(--bottom-nav-active)}.bottom-nav__tab--disabled{opacity:.4;cursor:not-allowed;pointer-events:none}.bottom-nav__tab[data-animation=scale] .bottom-nav__tab-icon{transition:transform .2s cubic-bezier(.4,0,.2,1)}.bottom-nav__tab[data-animation=scale].bottom-nav__tab--active .bottom-nav__tab-icon{transform:scale(1.15)}.bottom-nav__tab[data-animation=fade]{transition:opacity .2s ease,color .2s ease}.bottom-nav__tab[data-animation=fade]:not(.bottom-nav__tab--active){opacity:.6}.bottom-nav__tab[data-animation=slide] .bottom-nav__tab-label{transition:transform .2s ease,opacity .2s ease;transform:translateY(6px);opacity:0}.bottom-nav__tab[data-animation=slide].bottom-nav__tab--active .bottom-nav__tab-label{transform:translateY(0);opacity:1}.bottom-nav__tab-icon{position:relative;font-size:24px;line-height:1}.bottom-nav__tab-icon ion-icon{display:block}.bottom-nav__tab-label{font-size:11px;font-weight:500;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;letter-spacing:.01em}.bottom-nav__badge{position:absolute;top:-4px;right:-10px;min-width:18px;height:18px;padding:0 5px;font-size:10px;font-weight:600;line-height:18px;text-align:center;color:#fff;background-color:var(--ion-color-danger);border-radius:9px;box-shadow:0 1px 3px #0003}.bottom-nav__badge--dot{min-width:10px;width:10px;height:10px;padding:0;top:-2px;right:-4px;border-radius:50%}.bottom-nav__fab{position:relative;flex-shrink:0;width:var(--bottom-nav-fab-size);height:var(--bottom-nav-fab-size);margin:0 12px;margin-top:calc(var(--bottom-nav-fab-size) * -.35);background:var(--fab-color);border:none;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#fff;font-size:28px;box-shadow:0 4px 14px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.4);transition:transform .2s cubic-bezier(.4,0,.2,1),box-shadow .2s ease;overflow:hidden;--ripple-color: rgba(255, 255, 255, .3)}.bottom-nav__fab:hover{transform:scale(1.08);box-shadow:0 6px 20px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.5)}.bottom-nav__fab:active{transform:scale(.95)}.bottom-nav__fab:focus-visible{outline:3px solid white;outline-offset:2px}.bottom-nav__fab--small{--bottom-nav-fab-size: 48px;font-size:24px;margin-top:-14.4px}.bottom-nav__fab ion-icon{display:block}.bottom-nav--hide-labels{--bottom-nav-height: 56px}.bottom-nav--hide-labels .bottom-nav__tab-label{display:none}.bottom-nav--hide-labels .bottom-nav__tab-icon{font-size:26px}@media (prefers-color-scheme: dark){.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 0, 0, 0),.8);border-top-color:#ffffff14}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000040}.bottom-nav__fab{box-shadow:0 4px 14px #0006}.bottom-nav__fab:hover{box-shadow:0 6px 20px #00000080}}:host-context(.dark) .bottom-nav--translucent,:host-context(body.dark) .bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.85);border-top-color:#ffffff14}:host-context(.dark) .bottom-nav--elevated,:host-context(body.dark) .bottom-nav--elevated,:host-context([data-theme=dark]) .bottom-nav--elevated{box-shadow:0 -4px 20px #0000004d}:host-context(.dark) .bottom-nav__fab,:host-context(body.dark) .bottom-nav__fab,:host-context([data-theme=dark]) .bottom-nav__fab{box-shadow:0 4px 14px #00000080}:host-context(.dark) .bottom-nav__fab:hover,:host-context(body.dark) .bottom-nav__fab:hover,:host-context([data-theme=dark]) .bottom-nav__fab:hover{box-shadow:0 6px 20px #0009}@supports (padding-bottom: env(safe-area-inset-bottom)){.bottom-nav--safe-area .bottom-nav__container{height:var(--bottom-nav-height)}}\n"] }]
|
|
33675
|
+
`, styles: [":host{display:block;position:fixed;bottom:0;left:0;right:0;z-index:100;pointer-events:none}.bottom-nav{--bottom-nav-bg: var(--ion-background-color);--bottom-nav-active: var(--ion-color-primary);--bottom-nav-inactive: var(--ion-color-medium);--bottom-nav-radius: 16px 16px 0 0;--bottom-nav-height: 64px;--bottom-nav-fab-size: 56px;--fab-color: var(--ion-color-primary);pointer-events:auto;background:var(--bottom-nav-bg);border-radius:var(--bottom-nav-radius);height:var(--bottom-nav-height);padding:0 8px}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000014}.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.75);backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);border-top:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.06)}.bottom-nav--floating{margin:0 16px 16px;border-radius:28px;border-top:none;box-shadow:0 4px 24px #00000014,0 8px 32px #0000000a;--bottom-nav-radius: 28px}.bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.82);backdrop-filter:blur(28px) saturate(200%);-webkit-backdrop-filter:blur(28px) saturate(200%);border:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.04)}.bottom-nav--floating.bottom-nav--elevated{box-shadow:0 4px 24px #0000001a,0 12px 48px #00000014}.bottom-nav--floating.bottom-nav--safe-area{margin-bottom:calc(16px + env(safe-area-inset-bottom,0))}.bottom-nav--safe-area{padding-bottom:env(safe-area-inset-bottom,0);height:calc(var(--bottom-nav-height) + env(safe-area-inset-bottom,0))}.bottom-nav__container{display:flex;align-items:center;justify-content:space-around;height:var(--bottom-nav-height);max-width:500px;margin:0 auto}.bottom-nav__tab{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;height:100%;padding:8px 4px;background:transparent;border:none;cursor:pointer;position:relative;overflow:hidden;color:var(--bottom-nav-inactive);transition:color .2s ease;max-width:80px;--ripple-color: var(--bottom-nav-active)}.bottom-nav__tab:focus-visible{outline:2px solid var(--bottom-nav-active);outline-offset:-2px;border-radius:8px}.bottom-nav__tab--active{color:var(--bottom-nav-active)}.bottom-nav__tab--disabled{opacity:.4;cursor:not-allowed;pointer-events:none}.bottom-nav__tab[data-animation=scale] .bottom-nav__tab-icon{transition:transform .2s cubic-bezier(.4,0,.2,1)}.bottom-nav__tab[data-animation=scale].bottom-nav__tab--active .bottom-nav__tab-icon{transform:scale(1.15)}.bottom-nav__tab[data-animation=fade]{transition:opacity .2s ease,color .2s ease}.bottom-nav__tab[data-animation=fade]:not(.bottom-nav__tab--active){opacity:.6}.bottom-nav__tab[data-animation=slide] .bottom-nav__tab-label{transition:transform .2s ease,opacity .2s ease;transform:translateY(6px);opacity:0}.bottom-nav__tab[data-animation=slide].bottom-nav__tab--active .bottom-nav__tab-label{transform:translateY(0);opacity:1}.bottom-nav__tab-icon{position:relative;font-size:24px;line-height:1}.bottom-nav__tab-icon ion-icon{display:block}.bottom-nav__tab-label{font-size:11px;font-weight:500;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;letter-spacing:.01em}.bottom-nav__badge{position:absolute;top:-4px;right:-10px;min-width:18px;height:18px;padding:0 5px;font-size:10px;font-weight:600;line-height:18px;text-align:center;color:#fff;background-color:var(--ion-color-danger);border-radius:9px;box-shadow:0 1px 3px #0003}.bottom-nav__badge--dot{min-width:10px;width:10px;height:10px;padding:0;top:-2px;right:-4px;border-radius:50%}.bottom-nav__fab{position:relative;flex-shrink:0;width:var(--bottom-nav-fab-size);height:var(--bottom-nav-fab-size);margin:0 12px;margin-top:calc(var(--bottom-nav-fab-size) * -.35);background:var(--fab-color);border:none;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#fff;font-size:28px;box-shadow:0 4px 14px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.4);transition:transform .2s cubic-bezier(.4,0,.2,1),box-shadow .2s ease;overflow:hidden;--ripple-color: rgba(255, 255, 255, .3)}.bottom-nav__fab:hover{transform:scale(1.08);box-shadow:0 6px 20px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.5)}.bottom-nav__fab:active{transform:scale(.95)}.bottom-nav__fab:focus-visible{outline:3px solid white;outline-offset:2px}.bottom-nav__fab--small{--bottom-nav-fab-size: 48px;font-size:24px;margin-top:-14.4px}.bottom-nav__fab ion-icon{display:block}.bottom-nav--hide-labels{--bottom-nav-height: 56px}.bottom-nav--hide-labels .bottom-nav__tab-label{display:none}.bottom-nav--hide-labels .bottom-nav__tab-icon{font-size:26px}@media (prefers-color-scheme: dark){.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 0, 0, 0),.8);border-top-color:#ffffff14}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000040}.bottom-nav--floating{box-shadow:0 4px 24px #0003,0 8px 32px #00000026}.bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.88);border-color:#ffffff0f}.bottom-nav__fab{box-shadow:0 4px 14px #0006}.bottom-nav__fab:hover{box-shadow:0 6px 20px #00000080}}:host-context(.dark) .bottom-nav--translucent,:host-context(body.dark) .bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.85);border-top-color:#ffffff14}:host-context(.dark) .bottom-nav--elevated,:host-context(body.dark) .bottom-nav--elevated,:host-context([data-theme=dark]) .bottom-nav--elevated{box-shadow:0 -4px 20px #0000004d}:host-context(.dark) .bottom-nav--floating,:host-context(body.dark) .bottom-nav--floating,:host-context([data-theme=dark]) .bottom-nav--floating{box-shadow:0 4px 24px #0003,0 8px 32px #00000026}:host-context(.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context(body.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.88);border-color:#ffffff0f}:host-context(.dark) .bottom-nav__fab,:host-context(body.dark) .bottom-nav__fab,:host-context([data-theme=dark]) .bottom-nav__fab{box-shadow:0 4px 14px #00000080}:host-context(.dark) .bottom-nav__fab:hover,:host-context(body.dark) .bottom-nav__fab:hover,:host-context([data-theme=dark]) .bottom-nav__fab:hover{box-shadow:0 6px 20px #0009}@supports (padding-bottom: env(safe-area-inset-bottom)){.bottom-nav--safe-area .bottom-nav__container{height:var(--bottom-nav-height)}}\n"] }]
|
|
33495
33676
|
}], propDecorators: { tabClick: [{
|
|
33496
33677
|
type: Output
|
|
33497
33678
|
}], fabClick: [{
|
|
33498
33679
|
type: Output
|
|
33499
33680
|
}] } });
|
|
33500
33681
|
|
|
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
|
+
|
|
33501
34283
|
class LayoutComponent {
|
|
33502
34284
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
33503
34285
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: LayoutComponent, isStandalone: true, selector: "val-layout", ngImport: i0, template: `
|
|
@@ -41106,5 +41888,5 @@ function buildFooterLinks(links, t) {
|
|
|
41106
41888
|
* Generated bundle index. Do not edit.
|
|
41107
41889
|
*/
|
|
41108
41890
|
|
|
41109
|
-
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 };
|
|
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 };
|
|
41110
41892
|
//# sourceMappingURL=valtech-components.mjs.map
|