valtech-components 2.0.681 → 2.0.683
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/templates/docs-page/docs-page.component.mjs +35 -4
- package/esm2022/lib/components/templates/docs-page/types.mjs +1 -1
- package/esm2022/lib/services/auth/auth.service.mjs +11 -2
- package/esm2022/lib/services/auth/types.mjs +1 -1
- package/esm2022/lib/services/feedback/feedback.service.mjs +24 -21
- 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 +838 -27
- 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/templates/docs-page/docs-page.component.d.ts +3 -0
- package/lib/components/templates/docs-page/types.d.ts +39 -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 +2 -1
- package/public-api.d.ts +4 -0
- package/src/lib/services/firebase/firebase-messaging-sw.js +134 -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.683';
|
|
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: [":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
|
+
|
|
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
|
/**
|
|
@@ -33501,6 +33679,607 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
33501
33679
|
type: Output
|
|
33502
33680
|
}] } });
|
|
33503
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
|
+
|
|
33504
34283
|
class LayoutComponent {
|
|
33505
34284
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
33506
34285
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: LayoutComponent, isStandalone: true, selector: "val-layout", ngImport: i0, template: `
|
|
@@ -37624,35 +38403,38 @@ class FeedbackService {
|
|
|
37624
38403
|
* ```
|
|
37625
38404
|
*/
|
|
37626
38405
|
async checkFeedback(entityType, entityId) {
|
|
38406
|
+
// Si no hay usuario autenticado, no puede haber feedback previo
|
|
38407
|
+
// Retornar inmediatamente sin llamar al API (evita 401 y redirect a login)
|
|
38408
|
+
const userId = this.auth?.user()?.userId;
|
|
38409
|
+
if (!userId) {
|
|
38410
|
+
return { operationId: '', hasFeedback: false };
|
|
38411
|
+
}
|
|
37627
38412
|
// 1. Intentar Firebase primero (si está disponible)
|
|
37628
|
-
if (this.firestore
|
|
38413
|
+
if (this.firestore) {
|
|
37629
38414
|
try {
|
|
37630
|
-
|
|
37631
|
-
|
|
37632
|
-
|
|
37633
|
-
|
|
37634
|
-
|
|
37635
|
-
|
|
37636
|
-
|
|
37637
|
-
|
|
37638
|
-
|
|
37639
|
-
|
|
37640
|
-
|
|
37641
|
-
|
|
37642
|
-
|
|
37643
|
-
createdAt: doc.createdAt?.toISOString(),
|
|
37644
|
-
};
|
|
37645
|
-
}
|
|
37646
|
-
// Doc no existe = no hay feedback
|
|
37647
|
-
return { operationId: '', hasFeedback: false };
|
|
38415
|
+
// Path: feedback/{entityType}/{entityId}/{userId}
|
|
38416
|
+
// FirestoreService agrega automáticamente el prefijo apps/{appId}/
|
|
38417
|
+
const collectionPath = `feedback/${entityType}/${entityId}`;
|
|
38418
|
+
const doc = await this.firestore.getDoc(collectionPath, userId);
|
|
38419
|
+
if (doc) {
|
|
38420
|
+
return {
|
|
38421
|
+
operationId: '',
|
|
38422
|
+
hasFeedback: true,
|
|
38423
|
+
feedbackId: doc.feedbackId,
|
|
38424
|
+
type: doc.type,
|
|
38425
|
+
reactionValue: doc.reactionValue,
|
|
38426
|
+
createdAt: doc.createdAt?.toISOString(),
|
|
38427
|
+
};
|
|
37648
38428
|
}
|
|
38429
|
+
// Doc no existe = no hay feedback
|
|
38430
|
+
return { operationId: '', hasFeedback: false };
|
|
37649
38431
|
}
|
|
37650
38432
|
catch (error) {
|
|
37651
38433
|
console.warn('[FeedbackService] Firebase check failed, falling back to API:', error);
|
|
37652
38434
|
// Fallback a API
|
|
37653
38435
|
}
|
|
37654
38436
|
}
|
|
37655
|
-
// 2. Fallback: llamar API
|
|
38437
|
+
// 2. Fallback: llamar API (solo si hay usuario autenticado)
|
|
37656
38438
|
const params = new URLSearchParams({
|
|
37657
38439
|
appId: this.config.appId,
|
|
37658
38440
|
entityType,
|
|
@@ -40446,6 +41228,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
40446
41228
|
class DocsPageComponent {
|
|
40447
41229
|
constructor() {
|
|
40448
41230
|
this.elementRef = inject(ElementRef);
|
|
41231
|
+
this.router = inject(Router);
|
|
40449
41232
|
// Use signal internally so computed properties react to changes
|
|
40450
41233
|
this._props = signal({ title: '' });
|
|
40451
41234
|
this.tocItems = signal([]);
|
|
@@ -40491,6 +41274,22 @@ class DocsPageComponent {
|
|
|
40491
41274
|
homeRoute: props.breadcrumb.homeRoute ?? ['/'],
|
|
40492
41275
|
};
|
|
40493
41276
|
});
|
|
41277
|
+
this.feedbackProps = computed(() => {
|
|
41278
|
+
const props = this._props();
|
|
41279
|
+
const feedback = props.feedback;
|
|
41280
|
+
// Derive entityId from route if not provided
|
|
41281
|
+
const entityId = feedback?.entityId || this.router.url.replace(/^\//, '').replace(/\//g, '-') || 'unknown';
|
|
41282
|
+
return {
|
|
41283
|
+
entityRef: {
|
|
41284
|
+
entityType: feedback?.entityType || 'docs-page',
|
|
41285
|
+
entityId: entityId,
|
|
41286
|
+
},
|
|
41287
|
+
question: feedback?.question,
|
|
41288
|
+
showComment: feedback?.showComment ?? true,
|
|
41289
|
+
allowAnonymous: feedback?.allowAnonymous ?? true,
|
|
41290
|
+
showThankYou: true,
|
|
41291
|
+
};
|
|
41292
|
+
});
|
|
40494
41293
|
}
|
|
40495
41294
|
set props(value) {
|
|
40496
41295
|
this._props.set(value);
|
|
@@ -40562,6 +41361,12 @@ class DocsPageComponent {
|
|
|
40562
41361
|
<ng-content></ng-content>
|
|
40563
41362
|
</div>
|
|
40564
41363
|
|
|
41364
|
+
@if (props.feedback?.enabled) {
|
|
41365
|
+
<div class="docs-page__feedback">
|
|
41366
|
+
<val-content-reaction [props]="feedbackProps()"></val-content-reaction>
|
|
41367
|
+
</div>
|
|
41368
|
+
}
|
|
41369
|
+
|
|
40565
41370
|
@if (showNavLinks()) {
|
|
40566
41371
|
<val-docs-nav-links [props]="navLinksProps()"></val-docs-nav-links>
|
|
40567
41372
|
}
|
|
@@ -40573,11 +41378,11 @@ class DocsPageComponent {
|
|
|
40573
41378
|
</aside>
|
|
40574
41379
|
}
|
|
40575
41380
|
</div>
|
|
40576
|
-
`, isInline: true, styles: [".docs-page{display:grid;grid-template-columns:1fr;gap:2rem;max-width:1400px;margin:0 auto;padding:2rem 1.5rem}@media (min-width: 1200px){.docs-page{grid-template-columns:1fr 220px;padding:2rem}}.docs-page__content{min-width:0;max-width:900px}val-docs-breadcrumb{display:block;margin-bottom:1rem}.docs-page__header{margin-bottom:2rem}.docs-page__title-row{display:flex;align-items:center;gap:.75rem;flex-wrap:wrap}.docs-page__title{margin:0;font-size:2rem;font-weight:700;color:var(--ion-text-color, #1a1a1a);line-height:1.2}@media (min-width: 768px){.docs-page__title{font-size:2.5rem}}.docs-page__badge{display:inline-flex;align-items:center;padding:.25rem .5rem;font-size:.6875rem;font-weight:500;text-transform:uppercase;letter-spacing:.03em;border-radius:4px;background:#0000000a;color:#888}.docs-page__badge--success{background:#0000000a;color:#888}.docs-page__badge--warning{background:var(--ion-color-warning-tint, #fff3e0);color:var(--ion-color-warning-shade, #e65100)}.docs-page__badge--danger{background:var(--ion-color-danger-tint, #ffebee);color:var(--ion-color-danger-shade, #c62828)}.docs-page__lead{margin:1rem 0 0;font-size:1.125rem;line-height:1.7;color:var(--ion-color-medium, #666)}.docs-page__sections>*:last-child{margin-bottom:0}.docs-page__toc{display:none}@media (min-width: 1200px){.docs-page__toc{display:block;position:sticky;top:2rem;height:fit-content;max-height:calc(100vh - 4rem);overflow-y:auto}}.docs-page__sections h2{font-size:1.5rem;font-weight:600;margin:0 0 1rem;color:var(--ion-text-color, #1a1a1a);scroll-margin-top:2rem}.docs-page__sections h3{font-size:1.125rem;font-weight:600;margin:1.5rem 0 1rem;color:var(--ion-text-color, #1a1a1a)}.docs-page__sections h4{font-size:1rem;font-weight:600;margin:1.25rem 0 .75rem;color:var(--ion-text-color, #1a1a1a)}.docs-page__sections p{line-height:1.7;color:var(--ion-text-color, #1a1a1a);margin:0 0 1rem}.docs-page__sections ul,.docs-page__sections ol{padding-left:1.5rem;margin:0 0 1rem}.docs-page__sections li{margin-bottom:.5rem;line-height:1.6;color:var(--ion-text-color, #1a1a1a)}.docs-page__sections a{color:var(--ion-color-primary, #3880ff);text-decoration:none}.docs-page__sections a:hover{text-decoration:underline}.docs-page__sections code:not([class*=language-]){background:#0000000f;padding:.125rem .375rem;border-radius:4px;font-family:SF Mono,Fira Code,Consolas,monospace;font-size:.875em}.docs-page__sections pre:not([class*=language-]){background:#0000000a;padding:1rem;border-radius:8px;overflow-x:auto;margin:0 0 1rem}.docs-page__sections pre:not([class*=language-]) code{background:none;padding:0}.docs-page__sections table{width:100%;border-collapse:collapse;margin:0 0 1rem;font-size:.875rem}.docs-page__sections th,.docs-page__sections td{padding:.75rem;text-align:left;border-bottom:1px solid rgba(0,0,0,.1)}.docs-page__sections th{font-weight:600;background:#00000005}.docs-page__sections blockquote{margin:0 0 1rem;padding:1rem 1.5rem;border-left:4px solid var(--ion-color-primary, #3880ff);background:#00000005;border-radius:0 8px 8px 0}.docs-page__sections blockquote p:last-child{margin-bottom:0}.docs-page__sections img{max-width:100%;height:auto;border-radius:8px}.docs-page__sections hr{border:none;border-top:1px solid rgba(0,0,0,.1);margin:2rem 0}.docs-page__sections strong{font-weight:600}.docs-page__sections section{margin-bottom:2rem}:host-context(.dark) .docs-page__badge,:host-context([color-scheme=\"dark\"]) .docs-page__badge{background:#ffffff0f;color:#999}:host-context(.dark) .docs-page__badge--success,:host-context([color-scheme=\"dark\"]) .docs-page__badge--success{background:#ffffff0f;color:#999}:host-context(.dark) .docs-page__badge--warning,:host-context([color-scheme=\"dark\"]) .docs-page__badge--warning{background:#e6510033;color:#ffb74d}:host-context(.dark) .docs-page__badge--danger,:host-context([color-scheme=\"dark\"]) .docs-page__badge--danger{background:#c6282833;color:#e57373}:host-context(.dark) .docs-page__sections code:not([class*=language-]),:host-context([color-scheme=\"dark\"]) .docs-page__sections code:not([class*=language-]){background:#ffffff1a}:host-context(.dark) .docs-page__sections pre:not([class*=language-]),:host-context([color-scheme=\"dark\"]) .docs-page__sections pre:not([class*=language-]){background:#ffffff0f}:host-context(.dark) .docs-page__sections th,:host-context([color-scheme=\"dark\"]) .docs-page__sections th{background:#ffffff0a}:host-context(.dark) .docs-page__sections th,:host-context(.dark) .docs-page__sections td,:host-context([color-scheme=\"dark\"]) .docs-page__sections th,:host-context([color-scheme=\"dark\"]) .docs-page__sections td{border-color:#ffffff1a}:host-context(.dark) .docs-page__sections blockquote,:host-context([color-scheme=\"dark\"]) .docs-page__sections blockquote{background:#ffffff0a}:host-context(.dark) .docs-page__sections hr,:host-context([color-scheme=\"dark\"]) .docs-page__sections hr{border-color:#ffffff1a}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DocsBreadcrumbComponent, selector: "val-docs-breadcrumb", inputs: ["props"], outputs: ["navigate"] }, { kind: "component", type: DocsNavLinksComponent, selector: "val-docs-nav-links", inputs: ["props"], outputs: ["navigate"] }, { kind: "component", type: DocsTocComponent, selector: "val-docs-toc", inputs: ["props"], outputs: ["sectionChange"] }] }); }
|
|
41381
|
+
`, isInline: true, styles: [".docs-page{display:grid;grid-template-columns:1fr;gap:2rem;max-width:1400px;margin:0 auto;padding:2rem 1.5rem}@media (min-width: 1200px){.docs-page{grid-template-columns:1fr 220px;padding:2rem}}.docs-page__content{min-width:0;max-width:900px}val-docs-breadcrumb{display:block;margin-bottom:1rem}.docs-page__header{margin-bottom:2rem}.docs-page__title-row{display:flex;align-items:center;gap:.75rem;flex-wrap:wrap}.docs-page__title{margin:0;font-size:2rem;font-weight:700;color:var(--ion-text-color, #1a1a1a);line-height:1.2}@media (min-width: 768px){.docs-page__title{font-size:2.5rem}}.docs-page__badge{display:inline-flex;align-items:center;padding:.25rem .5rem;font-size:.6875rem;font-weight:500;text-transform:uppercase;letter-spacing:.03em;border-radius:4px;background:#0000000a;color:#888}.docs-page__badge--success{background:#0000000a;color:#888}.docs-page__badge--warning{background:var(--ion-color-warning-tint, #fff3e0);color:var(--ion-color-warning-shade, #e65100)}.docs-page__badge--danger{background:var(--ion-color-danger-tint, #ffebee);color:var(--ion-color-danger-shade, #c62828)}.docs-page__lead{margin:1rem 0 0;font-size:1.125rem;line-height:1.7;color:var(--ion-color-medium, #666)}.docs-page__sections>*:last-child{margin-bottom:0}.docs-page__feedback{margin-top:3rem;padding-top:2rem;border-top:1px solid rgba(0,0,0,.08);display:flex;justify-content:center}.docs-page__toc{display:none}@media (min-width: 1200px){.docs-page__toc{display:block;position:sticky;top:2rem;height:fit-content;max-height:calc(100vh - 4rem);overflow-y:auto}}.docs-page__sections h2{font-size:1.5rem;font-weight:600;margin:0 0 1rem;color:var(--ion-text-color, #1a1a1a);scroll-margin-top:2rem}.docs-page__sections h3{font-size:1.125rem;font-weight:600;margin:1.5rem 0 1rem;color:var(--ion-text-color, #1a1a1a)}.docs-page__sections h4{font-size:1rem;font-weight:600;margin:1.25rem 0 .75rem;color:var(--ion-text-color, #1a1a1a)}.docs-page__sections p{line-height:1.7;color:var(--ion-text-color, #1a1a1a);margin:0 0 1rem}.docs-page__sections ul,.docs-page__sections ol{padding-left:1.5rem;margin:0 0 1rem}.docs-page__sections li{margin-bottom:.5rem;line-height:1.6;color:var(--ion-text-color, #1a1a1a)}.docs-page__sections a{color:var(--ion-color-primary, #3880ff);text-decoration:none}.docs-page__sections a:hover{text-decoration:underline}.docs-page__sections code:not([class*=language-]){background:#0000000f;padding:.125rem .375rem;border-radius:4px;font-family:SF Mono,Fira Code,Consolas,monospace;font-size:.875em}.docs-page__sections pre:not([class*=language-]){background:#0000000a;padding:1rem;border-radius:8px;overflow-x:auto;margin:0 0 1rem}.docs-page__sections pre:not([class*=language-]) code{background:none;padding:0}.docs-page__sections table{width:100%;border-collapse:collapse;margin:0 0 1rem;font-size:.875rem}.docs-page__sections th,.docs-page__sections td{padding:.75rem;text-align:left;border-bottom:1px solid rgba(0,0,0,.1)}.docs-page__sections th{font-weight:600;background:#00000005}.docs-page__sections blockquote{margin:0 0 1rem;padding:1rem 1.5rem;border-left:4px solid var(--ion-color-primary, #3880ff);background:#00000005;border-radius:0 8px 8px 0}.docs-page__sections blockquote p:last-child{margin-bottom:0}.docs-page__sections img{max-width:100%;height:auto;border-radius:8px}.docs-page__sections hr{border:none;border-top:1px solid rgba(0,0,0,.1);margin:2rem 0}.docs-page__sections strong{font-weight:600}.docs-page__sections section{margin-bottom:2rem}:host-context(.dark) .docs-page__badge,:host-context([color-scheme=\"dark\"]) .docs-page__badge{background:#ffffff0f;color:#999}:host-context(.dark) .docs-page__badge--success,:host-context([color-scheme=\"dark\"]) .docs-page__badge--success{background:#ffffff0f;color:#999}:host-context(.dark) .docs-page__badge--warning,:host-context([color-scheme=\"dark\"]) .docs-page__badge--warning{background:#e6510033;color:#ffb74d}:host-context(.dark) .docs-page__badge--danger,:host-context([color-scheme=\"dark\"]) .docs-page__badge--danger{background:#c6282833;color:#e57373}:host-context(.dark) .docs-page__sections code:not([class*=language-]),:host-context([color-scheme=\"dark\"]) .docs-page__sections code:not([class*=language-]){background:#ffffff1a}:host-context(.dark) .docs-page__sections pre:not([class*=language-]),:host-context([color-scheme=\"dark\"]) .docs-page__sections pre:not([class*=language-]){background:#ffffff0f}:host-context(.dark) .docs-page__sections th,:host-context([color-scheme=\"dark\"]) .docs-page__sections th{background:#ffffff0a}:host-context(.dark) .docs-page__sections th,:host-context(.dark) .docs-page__sections td,:host-context([color-scheme=\"dark\"]) .docs-page__sections th,:host-context([color-scheme=\"dark\"]) .docs-page__sections td{border-color:#ffffff1a}:host-context(.dark) .docs-page__sections blockquote,:host-context([color-scheme=\"dark\"]) .docs-page__sections blockquote{background:#ffffff0a}:host-context(.dark) .docs-page__sections hr,:host-context([color-scheme=\"dark\"]) .docs-page__sections hr{border-color:#ffffff1a}:host-context(.dark) .docs-page__feedback,:host-context([color-scheme=\"dark\"]) .docs-page__feedback{border-color:#ffffff1a}@media (max-width: 768px){.docs-page__feedback{margin-top:2rem;padding-top:1.5rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DocsBreadcrumbComponent, selector: "val-docs-breadcrumb", inputs: ["props"], outputs: ["navigate"] }, { kind: "component", type: DocsNavLinksComponent, selector: "val-docs-nav-links", inputs: ["props"], outputs: ["navigate"] }, { kind: "component", type: DocsTocComponent, selector: "val-docs-toc", inputs: ["props"], outputs: ["sectionChange"] }, { kind: "component", type: ContentReactionComponent, selector: "val-content-reaction", inputs: ["props"], outputs: ["reactionSubmit", "reactionChange"] }] }); }
|
|
40577
41382
|
}
|
|
40578
41383
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DocsPageComponent, decorators: [{
|
|
40579
41384
|
type: Component,
|
|
40580
|
-
args: [{ selector: 'val-docs-page', standalone: true, imports: [CommonModule, DocsBreadcrumbComponent, DocsNavLinksComponent, DocsTocComponent], template: `
|
|
41385
|
+
args: [{ selector: 'val-docs-page', standalone: true, imports: [CommonModule, DocsBreadcrumbComponent, DocsNavLinksComponent, DocsTocComponent, ContentReactionComponent], template: `
|
|
40581
41386
|
<div class="docs-page" [class]="props.cssClass">
|
|
40582
41387
|
<div class="docs-page__content" #content>
|
|
40583
41388
|
@if (breadcrumbProps()) {
|
|
@@ -40606,6 +41411,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
40606
41411
|
<ng-content></ng-content>
|
|
40607
41412
|
</div>
|
|
40608
41413
|
|
|
41414
|
+
@if (props.feedback?.enabled) {
|
|
41415
|
+
<div class="docs-page__feedback">
|
|
41416
|
+
<val-content-reaction [props]="feedbackProps()"></val-content-reaction>
|
|
41417
|
+
</div>
|
|
41418
|
+
}
|
|
41419
|
+
|
|
40609
41420
|
@if (showNavLinks()) {
|
|
40610
41421
|
<val-docs-nav-links [props]="navLinksProps()"></val-docs-nav-links>
|
|
40611
41422
|
}
|
|
@@ -40617,7 +41428,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
40617
41428
|
</aside>
|
|
40618
41429
|
}
|
|
40619
41430
|
</div>
|
|
40620
|
-
`, styles: [".docs-page{display:grid;grid-template-columns:1fr;gap:2rem;max-width:1400px;margin:0 auto;padding:2rem 1.5rem}@media (min-width: 1200px){.docs-page{grid-template-columns:1fr 220px;padding:2rem}}.docs-page__content{min-width:0;max-width:900px}val-docs-breadcrumb{display:block;margin-bottom:1rem}.docs-page__header{margin-bottom:2rem}.docs-page__title-row{display:flex;align-items:center;gap:.75rem;flex-wrap:wrap}.docs-page__title{margin:0;font-size:2rem;font-weight:700;color:var(--ion-text-color, #1a1a1a);line-height:1.2}@media (min-width: 768px){.docs-page__title{font-size:2.5rem}}.docs-page__badge{display:inline-flex;align-items:center;padding:.25rem .5rem;font-size:.6875rem;font-weight:500;text-transform:uppercase;letter-spacing:.03em;border-radius:4px;background:#0000000a;color:#888}.docs-page__badge--success{background:#0000000a;color:#888}.docs-page__badge--warning{background:var(--ion-color-warning-tint, #fff3e0);color:var(--ion-color-warning-shade, #e65100)}.docs-page__badge--danger{background:var(--ion-color-danger-tint, #ffebee);color:var(--ion-color-danger-shade, #c62828)}.docs-page__lead{margin:1rem 0 0;font-size:1.125rem;line-height:1.7;color:var(--ion-color-medium, #666)}.docs-page__sections>*:last-child{margin-bottom:0}.docs-page__toc{display:none}@media (min-width: 1200px){.docs-page__toc{display:block;position:sticky;top:2rem;height:fit-content;max-height:calc(100vh - 4rem);overflow-y:auto}}.docs-page__sections h2{font-size:1.5rem;font-weight:600;margin:0 0 1rem;color:var(--ion-text-color, #1a1a1a);scroll-margin-top:2rem}.docs-page__sections h3{font-size:1.125rem;font-weight:600;margin:1.5rem 0 1rem;color:var(--ion-text-color, #1a1a1a)}.docs-page__sections h4{font-size:1rem;font-weight:600;margin:1.25rem 0 .75rem;color:var(--ion-text-color, #1a1a1a)}.docs-page__sections p{line-height:1.7;color:var(--ion-text-color, #1a1a1a);margin:0 0 1rem}.docs-page__sections ul,.docs-page__sections ol{padding-left:1.5rem;margin:0 0 1rem}.docs-page__sections li{margin-bottom:.5rem;line-height:1.6;color:var(--ion-text-color, #1a1a1a)}.docs-page__sections a{color:var(--ion-color-primary, #3880ff);text-decoration:none}.docs-page__sections a:hover{text-decoration:underline}.docs-page__sections code:not([class*=language-]){background:#0000000f;padding:.125rem .375rem;border-radius:4px;font-family:SF Mono,Fira Code,Consolas,monospace;font-size:.875em}.docs-page__sections pre:not([class*=language-]){background:#0000000a;padding:1rem;border-radius:8px;overflow-x:auto;margin:0 0 1rem}.docs-page__sections pre:not([class*=language-]) code{background:none;padding:0}.docs-page__sections table{width:100%;border-collapse:collapse;margin:0 0 1rem;font-size:.875rem}.docs-page__sections th,.docs-page__sections td{padding:.75rem;text-align:left;border-bottom:1px solid rgba(0,0,0,.1)}.docs-page__sections th{font-weight:600;background:#00000005}.docs-page__sections blockquote{margin:0 0 1rem;padding:1rem 1.5rem;border-left:4px solid var(--ion-color-primary, #3880ff);background:#00000005;border-radius:0 8px 8px 0}.docs-page__sections blockquote p:last-child{margin-bottom:0}.docs-page__sections img{max-width:100%;height:auto;border-radius:8px}.docs-page__sections hr{border:none;border-top:1px solid rgba(0,0,0,.1);margin:2rem 0}.docs-page__sections strong{font-weight:600}.docs-page__sections section{margin-bottom:2rem}:host-context(.dark) .docs-page__badge,:host-context([color-scheme=\"dark\"]) .docs-page__badge{background:#ffffff0f;color:#999}:host-context(.dark) .docs-page__badge--success,:host-context([color-scheme=\"dark\"]) .docs-page__badge--success{background:#ffffff0f;color:#999}:host-context(.dark) .docs-page__badge--warning,:host-context([color-scheme=\"dark\"]) .docs-page__badge--warning{background:#e6510033;color:#ffb74d}:host-context(.dark) .docs-page__badge--danger,:host-context([color-scheme=\"dark\"]) .docs-page__badge--danger{background:#c6282833;color:#e57373}:host-context(.dark) .docs-page__sections code:not([class*=language-]),:host-context([color-scheme=\"dark\"]) .docs-page__sections code:not([class*=language-]){background:#ffffff1a}:host-context(.dark) .docs-page__sections pre:not([class*=language-]),:host-context([color-scheme=\"dark\"]) .docs-page__sections pre:not([class*=language-]){background:#ffffff0f}:host-context(.dark) .docs-page__sections th,:host-context([color-scheme=\"dark\"]) .docs-page__sections th{background:#ffffff0a}:host-context(.dark) .docs-page__sections th,:host-context(.dark) .docs-page__sections td,:host-context([color-scheme=\"dark\"]) .docs-page__sections th,:host-context([color-scheme=\"dark\"]) .docs-page__sections td{border-color:#ffffff1a}:host-context(.dark) .docs-page__sections blockquote,:host-context([color-scheme=\"dark\"]) .docs-page__sections blockquote{background:#ffffff0a}:host-context(.dark) .docs-page__sections hr,:host-context([color-scheme=\"dark\"]) .docs-page__sections hr{border-color:#ffffff1a}\n"] }]
|
|
41431
|
+
`, styles: [".docs-page{display:grid;grid-template-columns:1fr;gap:2rem;max-width:1400px;margin:0 auto;padding:2rem 1.5rem}@media (min-width: 1200px){.docs-page{grid-template-columns:1fr 220px;padding:2rem}}.docs-page__content{min-width:0;max-width:900px}val-docs-breadcrumb{display:block;margin-bottom:1rem}.docs-page__header{margin-bottom:2rem}.docs-page__title-row{display:flex;align-items:center;gap:.75rem;flex-wrap:wrap}.docs-page__title{margin:0;font-size:2rem;font-weight:700;color:var(--ion-text-color, #1a1a1a);line-height:1.2}@media (min-width: 768px){.docs-page__title{font-size:2.5rem}}.docs-page__badge{display:inline-flex;align-items:center;padding:.25rem .5rem;font-size:.6875rem;font-weight:500;text-transform:uppercase;letter-spacing:.03em;border-radius:4px;background:#0000000a;color:#888}.docs-page__badge--success{background:#0000000a;color:#888}.docs-page__badge--warning{background:var(--ion-color-warning-tint, #fff3e0);color:var(--ion-color-warning-shade, #e65100)}.docs-page__badge--danger{background:var(--ion-color-danger-tint, #ffebee);color:var(--ion-color-danger-shade, #c62828)}.docs-page__lead{margin:1rem 0 0;font-size:1.125rem;line-height:1.7;color:var(--ion-color-medium, #666)}.docs-page__sections>*:last-child{margin-bottom:0}.docs-page__feedback{margin-top:3rem;padding-top:2rem;border-top:1px solid rgba(0,0,0,.08);display:flex;justify-content:center}.docs-page__toc{display:none}@media (min-width: 1200px){.docs-page__toc{display:block;position:sticky;top:2rem;height:fit-content;max-height:calc(100vh - 4rem);overflow-y:auto}}.docs-page__sections h2{font-size:1.5rem;font-weight:600;margin:0 0 1rem;color:var(--ion-text-color, #1a1a1a);scroll-margin-top:2rem}.docs-page__sections h3{font-size:1.125rem;font-weight:600;margin:1.5rem 0 1rem;color:var(--ion-text-color, #1a1a1a)}.docs-page__sections h4{font-size:1rem;font-weight:600;margin:1.25rem 0 .75rem;color:var(--ion-text-color, #1a1a1a)}.docs-page__sections p{line-height:1.7;color:var(--ion-text-color, #1a1a1a);margin:0 0 1rem}.docs-page__sections ul,.docs-page__sections ol{padding-left:1.5rem;margin:0 0 1rem}.docs-page__sections li{margin-bottom:.5rem;line-height:1.6;color:var(--ion-text-color, #1a1a1a)}.docs-page__sections a{color:var(--ion-color-primary, #3880ff);text-decoration:none}.docs-page__sections a:hover{text-decoration:underline}.docs-page__sections code:not([class*=language-]){background:#0000000f;padding:.125rem .375rem;border-radius:4px;font-family:SF Mono,Fira Code,Consolas,monospace;font-size:.875em}.docs-page__sections pre:not([class*=language-]){background:#0000000a;padding:1rem;border-radius:8px;overflow-x:auto;margin:0 0 1rem}.docs-page__sections pre:not([class*=language-]) code{background:none;padding:0}.docs-page__sections table{width:100%;border-collapse:collapse;margin:0 0 1rem;font-size:.875rem}.docs-page__sections th,.docs-page__sections td{padding:.75rem;text-align:left;border-bottom:1px solid rgba(0,0,0,.1)}.docs-page__sections th{font-weight:600;background:#00000005}.docs-page__sections blockquote{margin:0 0 1rem;padding:1rem 1.5rem;border-left:4px solid var(--ion-color-primary, #3880ff);background:#00000005;border-radius:0 8px 8px 0}.docs-page__sections blockquote p:last-child{margin-bottom:0}.docs-page__sections img{max-width:100%;height:auto;border-radius:8px}.docs-page__sections hr{border:none;border-top:1px solid rgba(0,0,0,.1);margin:2rem 0}.docs-page__sections strong{font-weight:600}.docs-page__sections section{margin-bottom:2rem}:host-context(.dark) .docs-page__badge,:host-context([color-scheme=\"dark\"]) .docs-page__badge{background:#ffffff0f;color:#999}:host-context(.dark) .docs-page__badge--success,:host-context([color-scheme=\"dark\"]) .docs-page__badge--success{background:#ffffff0f;color:#999}:host-context(.dark) .docs-page__badge--warning,:host-context([color-scheme=\"dark\"]) .docs-page__badge--warning{background:#e6510033;color:#ffb74d}:host-context(.dark) .docs-page__badge--danger,:host-context([color-scheme=\"dark\"]) .docs-page__badge--danger{background:#c6282833;color:#e57373}:host-context(.dark) .docs-page__sections code:not([class*=language-]),:host-context([color-scheme=\"dark\"]) .docs-page__sections code:not([class*=language-]){background:#ffffff1a}:host-context(.dark) .docs-page__sections pre:not([class*=language-]),:host-context([color-scheme=\"dark\"]) .docs-page__sections pre:not([class*=language-]){background:#ffffff0f}:host-context(.dark) .docs-page__sections th,:host-context([color-scheme=\"dark\"]) .docs-page__sections th{background:#ffffff0a}:host-context(.dark) .docs-page__sections th,:host-context(.dark) .docs-page__sections td,:host-context([color-scheme=\"dark\"]) .docs-page__sections th,:host-context([color-scheme=\"dark\"]) .docs-page__sections td{border-color:#ffffff1a}:host-context(.dark) .docs-page__sections blockquote,:host-context([color-scheme=\"dark\"]) .docs-page__sections blockquote{background:#ffffff0a}:host-context(.dark) .docs-page__sections hr,:host-context([color-scheme=\"dark\"]) .docs-page__sections hr{border-color:#ffffff1a}:host-context(.dark) .docs-page__feedback,:host-context([color-scheme=\"dark\"]) .docs-page__feedback{border-color:#ffffff1a}@media (max-width: 768px){.docs-page__feedback{margin-top:2rem;padding-top:1.5rem}}\n"] }]
|
|
40621
41432
|
}], propDecorators: { props: [{
|
|
40622
41433
|
type: Input
|
|
40623
41434
|
}] } });
|
|
@@ -41109,5 +41920,5 @@ function buildFooterLinks(links, t) {
|
|
|
41109
41920
|
* Generated bundle index. Do not edit.
|
|
41110
41921
|
*/
|
|
41111
41922
|
|
|
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 };
|
|
41923
|
+
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 };
|
|
41113
41924
|
//# sourceMappingURL=valtech-components.mjs.map
|