valtech-components 2.0.730 → 2.0.732

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.
Files changed (29) hide show
  1. package/esm2022/lib/components/atoms/glow/glow.component.mjs +95 -0
  2. package/esm2022/lib/components/atoms/glow/types.mjs +2 -0
  3. package/esm2022/lib/components/organisms/bottom-nav/bottom-nav.component.mjs +4 -4
  4. package/esm2022/lib/components/organisms/bottom-nav/types.mjs +2 -2
  5. package/esm2022/lib/components/organisms/login/login.component.mjs +3 -3
  6. package/esm2022/lib/config/company-footer.config.mjs +34 -20
  7. package/esm2022/lib/services/devices/devices.service.mjs +259 -0
  8. package/esm2022/lib/services/devices/index.mjs +3 -0
  9. package/esm2022/lib/services/devices/types.mjs +8 -0
  10. package/esm2022/lib/services/legal-link/legal-link.service.mjs +68 -0
  11. package/esm2022/lib/version.mjs +2 -2
  12. package/esm2022/public-api.mjs +10 -1
  13. package/fesm2022/valtech-components.mjs +458 -28
  14. package/fesm2022/valtech-components.mjs.map +1 -1
  15. package/lib/components/atoms/glow/glow.component.d.ts +42 -0
  16. package/lib/components/atoms/glow/types.d.ts +40 -0
  17. package/lib/components/atoms/rights-footer/rights-footer.component.d.ts +1 -1
  18. package/lib/components/molecules/features-list/features-list.component.d.ts +1 -1
  19. package/lib/components/organisms/article/article.component.d.ts +3 -3
  20. package/lib/components/organisms/bottom-nav/types.d.ts +1 -1
  21. package/lib/components/organisms/toolbar/toolbar.component.d.ts +1 -1
  22. package/lib/config/company-footer.config.d.ts +24 -4
  23. package/lib/services/devices/devices.service.d.ts +73 -0
  24. package/lib/services/devices/index.d.ts +2 -0
  25. package/lib/services/devices/types.d.ts +11 -0
  26. package/lib/services/legal-link/legal-link.service.d.ts +59 -0
  27. package/lib/version.d.ts +1 -1
  28. package/package.json +1 -1
  29. package/public-api.d.ts +4 -0
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { signal, Injectable, makeEnvironmentProviders, APP_INITIALIZER, inject, EventEmitter, Component, Input, Output, input, computed, HostBinding, HostListener, Pipe, ChangeDetectionStrategy, ViewChild, ChangeDetectorRef, ContentChild, PLATFORM_ID, Inject, ErrorHandler, DestroyRef, InjectionToken, runInInjectionContext, effect, Optional, TemplateRef, ViewContainerRef, isSignal, Directive, ElementRef, ViewEncapsulation } from '@angular/core';
2
+ import { signal, Injectable, makeEnvironmentProviders, APP_INITIALIZER, inject, EventEmitter, Component, Input, Output, input, computed, ChangeDetectionStrategy, HostBinding, HostListener, Pipe, ViewChild, ChangeDetectorRef, ContentChild, PLATFORM_ID, Inject, ErrorHandler, DestroyRef, InjectionToken, runInInjectionContext, effect, Optional, TemplateRef, ViewContainerRef, isSignal, Directive, ElementRef, ViewEncapsulation } from '@angular/core';
3
3
  import * as i2$1 from '@ionic/angular/standalone';
4
4
  import { IonAvatar, IonCard, IonIcon, IonButton, IonSpinner, IonText, IonModal, IonHeader, IonToolbar, IonContent, IonButtons, IonTitle, IonProgressBar, IonSkeletonText, IonFab, IonFabButton, IonFabList, IonLabel, IonCardContent, IonCardHeader, IonCardTitle, IonCardSubtitle, IonCheckbox, IonTextarea, IonDatetime, IonDatetimeButton, IonInput, IonSelect, IonSelectOption, IonPopover, IonList, IonItem, IonRadioGroup, IonRadio, IonRange, IonSearchbar, IonSegment, IonSegmentButton, IonToggle, IonAccordion, IonAccordionGroup, IonTabBar, IonTabButton, IonBadge, IonBreadcrumb, IonBreadcrumbs, IonChip, IonNote, ToastController as ToastController$1, IonCol, IonRow, IonRefresher, IonRefresherContent, IonRippleEffect, AlertController, IonMenuButton, IonFooter, IonListHeader, IonInfiniteScroll, IonInfiniteScrollContent, IonGrid, MenuController, IonMenu, IonMenuToggle, IonSplitPane } from '@ionic/angular/standalone';
5
5
  import * as i1 from '@angular/common';
@@ -50,7 +50,7 @@ import 'prismjs/components/prism-json';
50
50
  * Current version of valtech-components.
51
51
  * This is automatically updated during the publish process.
52
52
  */
53
- const VERSION = '2.0.730';
53
+ const VERSION = '2.0.732';
54
54
 
55
55
  /**
56
56
  * Servicio para gestionar presets de componentes.
@@ -445,6 +445,99 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
445
445
  type: Output
446
446
  }] } });
447
447
 
448
+ const POSITION_MAP = {
449
+ 'top-left': '0% 0%',
450
+ top: '50% 0%',
451
+ 'top-right': '100% 0%',
452
+ left: '0% 50%',
453
+ center: '50% 50%',
454
+ right: '100% 50%',
455
+ 'bottom-left': '0% 100%',
456
+ bottom: '50% 100%',
457
+ 'bottom-right': '100% 100%',
458
+ };
459
+ const SIZE_MAP = {
460
+ small: '300px',
461
+ medium: '500px',
462
+ large: '800px',
463
+ xlarge: '1200px',
464
+ };
465
+ const INTENSITY_MAP = {
466
+ subtle: 0.08,
467
+ medium: 0.18,
468
+ strong: 0.35,
469
+ };
470
+ const LINEAR_ANGLE_MAP = {
471
+ 'top-left': '135deg',
472
+ top: '180deg',
473
+ 'top-right': '225deg',
474
+ left: '90deg',
475
+ center: '180deg',
476
+ right: '270deg',
477
+ 'bottom-left': '45deg',
478
+ bottom: '0deg',
479
+ 'bottom-right': '315deg',
480
+ };
481
+ /**
482
+ * `val-glow` — wrapper that paints a colored radial or linear gradient behind its
483
+ * projected content. Uses Ionic color tokens, so it respects light/dark theme.
484
+ *
485
+ * @example
486
+ * <val-glow [props]="{ color: 'primary', position: 'top', size: 'large' }">
487
+ * <h1>Hero title</h1>
488
+ * <p>Body copy...</p>
489
+ * </val-glow>
490
+ *
491
+ * @example Two-tone radial
492
+ * <val-glow [props]="{ color: 'primary', secondaryColor: 'tertiary', position: 'center' }">
493
+ * ...
494
+ * </val-glow>
495
+ *
496
+ * @example Linear sweep from top
497
+ * <val-glow [props]="{ color: 'success', shape: 'linear', position: 'top', intensity: 'strong' }">
498
+ * ...
499
+ * </val-glow>
500
+ */
501
+ class GlowComponent {
502
+ constructor() {
503
+ this.props = input.required();
504
+ this.color = computed(() => this.props().color ?? 'primary');
505
+ this.secondaryColor = computed(() => this.props().secondaryColor);
506
+ this.hasSecondary = computed(() => Boolean(this.secondaryColor()));
507
+ this.position = computed(() => this.props().position ?? 'center');
508
+ this.size = computed(() => this.props().size ?? 'large');
509
+ this.intensity = computed(() => this.props().intensity ?? 'medium');
510
+ this.shape = computed(() => this.props().shape ?? 'radial');
511
+ this.animated = computed(() => this.props().animated ?? false);
512
+ this.blendMode = computed(() => this.props().blendMode ?? 'normal');
513
+ this.colorRgbVar = computed(() => `var(--ion-color-${this.color()}-rgb)`);
514
+ this.secondaryColorRgbVar = computed(() => this.secondaryColor() ? `var(--ion-color-${this.secondaryColor()}-rgb)` : null);
515
+ this.positionCss = computed(() => POSITION_MAP[this.position()]);
516
+ this.sizeCss = computed(() => SIZE_MAP[this.size()]);
517
+ this.intensityValue = computed(() => INTENSITY_MAP[this.intensity()].toString());
518
+ this.angleCss = computed(() => LINEAR_ANGLE_MAP[this.position()]);
519
+ }
520
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: GlowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
521
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.14", type: GlowComponent, isStandalone: true, selector: "val-glow", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "class.val-glow--radial": "shape() === 'radial'", "class.val-glow--linear": "shape() === 'linear'", "class.val-glow--animated": "animated()", "class.val-glow--dual": "hasSecondary()", "style.--val-glow-color-rgb": "colorRgbVar()", "style.--val-glow-color-secondary-rgb": "secondaryColorRgbVar()", "style.--val-glow-position": "positionCss()", "style.--val-glow-size": "sizeCss()", "style.--val-glow-intensity": "intensityValue()", "style.--val-glow-angle": "angleCss()", "style.--val-glow-blend-mode": "blendMode()" }, classAttribute: "val-glow" }, ngImport: i0, template: `<ng-content />`, isInline: true, styles: [":host{position:relative;display:block;isolation:isolate;overflow:hidden;--val-glow-color-rgb: var(--ion-color-primary-rgb);--val-glow-color-secondary-rgb: var(--val-glow-color-rgb);--val-glow-position: 50% 50%;--val-glow-size: 800px;--val-glow-intensity: .18;--val-glow-angle: 180deg;--val-glow-blend-mode: normal}:host:before{content:\"\";position:absolute;inset:0;z-index:-1;pointer-events:none;background-blend-mode:var(--val-glow-blend-mode);background-repeat:no-repeat}:host(.val-glow--radial):before{background-image:radial-gradient(circle var(--val-glow-size) at var(--val-glow-position),rgba(var(--val-glow-color-rgb),var(--val-glow-intensity)) 0%,rgba(var(--val-glow-color-rgb),0) 70%)}:host(.val-glow--radial.val-glow--dual):before{background-image:radial-gradient(circle var(--val-glow-size) at var(--val-glow-position),rgba(var(--val-glow-color-rgb),var(--val-glow-intensity)) 0%,rgba(var(--val-glow-color-secondary-rgb),calc(var(--val-glow-intensity) * .7)) 35%,rgba(var(--val-glow-color-secondary-rgb),0) 70%)}:host(.val-glow--linear):before{background-image:linear-gradient(var(--val-glow-angle),rgba(var(--val-glow-color-rgb),var(--val-glow-intensity)) 0%,rgba(var(--val-glow-color-rgb),0) 100%)}:host(.val-glow--linear.val-glow--dual):before{background-image:linear-gradient(var(--val-glow-angle),rgba(var(--val-glow-color-rgb),var(--val-glow-intensity)) 0%,rgba(var(--val-glow-color-secondary-rgb),calc(var(--val-glow-intensity) * .8)) 50%,rgba(var(--val-glow-color-secondary-rgb),0) 100%)}:host(.val-glow--animated):before{animation:val-glow-drift 16s ease-in-out infinite alternate}@keyframes val-glow-drift{0%{transform:translateZ(0) scale(1);opacity:1}50%{transform:translate3d(-2%,1.5%,0) scale(1.05);opacity:.95}to{transform:translate3d(2%,-1.5%,0) scale(1.02);opacity:1}}@media (prefers-reduced-motion: reduce){:host(.val-glow--animated):before{animation:none}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
522
+ }
523
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: GlowComponent, decorators: [{
524
+ type: Component,
525
+ args: [{ selector: 'val-glow', standalone: true, template: `<ng-content />`, changeDetection: ChangeDetectionStrategy.OnPush, host: {
526
+ class: 'val-glow',
527
+ '[class.val-glow--radial]': "shape() === 'radial'",
528
+ '[class.val-glow--linear]': "shape() === 'linear'",
529
+ '[class.val-glow--animated]': 'animated()',
530
+ '[class.val-glow--dual]': 'hasSecondary()',
531
+ '[style.--val-glow-color-rgb]': 'colorRgbVar()',
532
+ '[style.--val-glow-color-secondary-rgb]': 'secondaryColorRgbVar()',
533
+ '[style.--val-glow-position]': 'positionCss()',
534
+ '[style.--val-glow-size]': 'sizeCss()',
535
+ '[style.--val-glow-intensity]': 'intensityValue()',
536
+ '[style.--val-glow-angle]': 'angleCss()',
537
+ '[style.--val-glow-blend-mode]': 'blendMode()',
538
+ }, styles: [":host{position:relative;display:block;isolation:isolate;overflow:hidden;--val-glow-color-rgb: var(--ion-color-primary-rgb);--val-glow-color-secondary-rgb: var(--val-glow-color-rgb);--val-glow-position: 50% 50%;--val-glow-size: 800px;--val-glow-intensity: .18;--val-glow-angle: 180deg;--val-glow-blend-mode: normal}:host:before{content:\"\";position:absolute;inset:0;z-index:-1;pointer-events:none;background-blend-mode:var(--val-glow-blend-mode);background-repeat:no-repeat}:host(.val-glow--radial):before{background-image:radial-gradient(circle var(--val-glow-size) at var(--val-glow-position),rgba(var(--val-glow-color-rgb),var(--val-glow-intensity)) 0%,rgba(var(--val-glow-color-rgb),0) 70%)}:host(.val-glow--radial.val-glow--dual):before{background-image:radial-gradient(circle var(--val-glow-size) at var(--val-glow-position),rgba(var(--val-glow-color-rgb),var(--val-glow-intensity)) 0%,rgba(var(--val-glow-color-secondary-rgb),calc(var(--val-glow-intensity) * .7)) 35%,rgba(var(--val-glow-color-secondary-rgb),0) 70%)}:host(.val-glow--linear):before{background-image:linear-gradient(var(--val-glow-angle),rgba(var(--val-glow-color-rgb),var(--val-glow-intensity)) 0%,rgba(var(--val-glow-color-rgb),0) 100%)}:host(.val-glow--linear.val-glow--dual):before{background-image:linear-gradient(var(--val-glow-angle),rgba(var(--val-glow-color-rgb),var(--val-glow-intensity)) 0%,rgba(var(--val-glow-color-secondary-rgb),calc(var(--val-glow-intensity) * .8)) 50%,rgba(var(--val-glow-color-secondary-rgb),0) 100%)}:host(.val-glow--animated):before{animation:val-glow-drift 16s ease-in-out infinite alternate}@keyframes val-glow-drift{0%{transform:translateZ(0) scale(1);opacity:1}50%{transform:translate3d(-2%,1.5%,0) scale(1.05);opacity:.95}to{transform:translate3d(2%,-1.5%,0) scale(1.02);opacity:1}}@media (prefers-reduced-motion: reduce){:host(.val-glow--animated):before{animation:none}}\n"] }]
539
+ }] });
540
+
448
541
  /**
449
542
  * val-container
450
543
  *
@@ -32055,7 +32148,7 @@ class LoginComponent {
32055
32148
  this.stopResetResendCooldown();
32056
32149
  }
32057
32150
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LoginComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
32058
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: LoginComponent, isStandalone: true, selector: "val-login", inputs: { props: "props" }, outputs: { onSuccess: "onSuccess", onError: "onError", onMFARequired: "onMFARequired" }, ngImport: i0, template: "<div class=\"val-login\" [class.val-login--card]=\"config.showCard\">\n <!-- Logo -->\n @if (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n\n <!-- Login Form -->\n <val-form [props]=\"loginFormProps()\" (onSubmit)=\"loginHandler($event)\" />\n\n <!-- OAuth Section -->\n @if (config.showOAuth && config.oauthProviders.length > 0) {\n <div class=\"oauth-separator\">\n <span>{{ t('orContinueWith') }}</span>\n </div>\n\n <div class=\"oauth-buttons\">\n @for (provider of config.oauthProviders; track provider) {\n <ion-button\n expand=\"block\"\n fill=\"outline\"\n color=\"dark\"\n (click)=\"loginWithOAuth(provider)\"\n [disabled]=\"isOAuthLoading\"\n >\n @switch (provider) {\n @case ('google') {\n <ion-icon slot=\"start\" name=\"logo-google\"></ion-icon>\n {{ isOAuthLoading ? t('connecting') : t('continueWithGoogle') }}\n }\n @case ('apple') {\n <ion-icon slot=\"start\" name=\"logo-apple\"></ion-icon>\n {{ isOAuthLoading ? t('connecting') : t('continueWithApple') }}\n }\n @case ('microsoft') {\n <ion-icon slot=\"start\" name=\"logo-microsoft\"></ion-icon>\n {{ isOAuthLoading ? t('connecting') : t('continueWithMicrosoft') }}\n }\n }\n </ion-button>\n }\n </div>\n }\n\n <!-- Register Link -->\n @if (config.showRegister) {\n <div class=\"auth-link\">\n <ion-text color=\"dark\">\n {{ t('noAccount') }}\n <a (click)=\"openRegisterModal()\">{{ t('register') }}</a>\n </ion-text>\n </div>\n }\n\n <!-- Forgot Password Link -->\n @if (config.showForgotPassword) {\n <div class=\"auth-link forgot-password\">\n <ion-text color=\"dark\">\n {{ t('forgotLink') }}\n <a (click)=\"openForgotPasswordModal()\">{{ t('recoverPassword') }}</a>\n </ion-text>\n </div>\n }\n\n <!-- Legal Notice -->\n @if (props.legal) {\n <div class=\"legal-notice\">\n <ion-text color=\"medium\">\n <p>\n {{ t('legalPrefix') }}\n @if (props.legal.companyLink) {\n <a [href]=\"props.legal.companyLink\"><strong>{{ props.legal.companyName }}</strong></a>\n } @else {\n <strong>{{ props.legal.companyName }}</strong>\n }\n {{ t('legalSuffix') }}\n @if (props.legal.termsLink) {\n <a [href]=\"props.legal.termsLink\">{{ t('termsAndConditions') }}</a>\n } @else {\n <span>{{ t('termsAndConditions') }}</span>\n }\n {{ t('and') }}\n @if (props.legal.privacyLink) {\n <a [href]=\"props.legal.privacyLink\">{{ t('privacyPolicy') }}</a>\n } @else {\n <span>{{ t('privacyPolicy') }}</span>\n }.\n </p>\n </ion-text>\n </div>\n }\n</div>\n\n<!-- Register Modal -->\n<ion-modal [isOpen]=\"isRegisterModalOpen\" (didDismiss)=\"closeRegisterModal()\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" (click)=\"closeRegisterModal()\">\n <ion-icon name=\"close-outline\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class=\"ion-padding\">\n <section class=\"modal-form-section\">\n @if (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n <val-form [props]=\"registerFormProps()\" (onSubmit)=\"registerHandler($event)\" />\n <div class=\"auth-link\">\n <ion-text color=\"dark\">\n {{ t('hasAccount') }}\n <a (click)=\"closeRegisterModal()\">{{ t('signIn') }}</a>\n </ion-text>\n </div>\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- Verify Email Modal -->\n<ion-modal [isOpen]=\"isVerifyModalOpen\" [backdropDismiss]=\"false\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" (click)=\"closeVerifyModal()\">\n <ion-icon name=\"close-outline\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class=\"ion-padding\">\n <section class=\"modal-form-section\">\n @if (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n <val-form [props]=\"verifyFormProps()\" (onSubmit)=\"verifyHandler($event)\" />\n <div class=\"resend-link\">\n <ion-text color=\"dark\">\n {{ t('noCodeReceived') }}\n @if (resendCooldown > 0) {\n <span class=\"cooldown\">{{ t('resendIn', { seconds: resendCooldown.toString() }) }}</span>\n } @else {\n <a (click)=\"resendCode()\">{{ t('resend') }}</a>\n }\n </ion-text>\n </div>\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- Forgot Password Modal -->\n<ion-modal [isOpen]=\"isForgotPasswordModalOpen\" (didDismiss)=\"closeForgotPasswordModal()\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" (click)=\"closeForgotPasswordModal()\">\n <ion-icon name=\"close-outline\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class=\"ion-padding\">\n <section class=\"modal-form-section\">\n @if (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n <val-form [props]=\"forgotPasswordFormProps()\" (onSubmit)=\"forgotPasswordHandler($event)\" />\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- Reset Password Modal -->\n<ion-modal [isOpen]=\"isResetPasswordModalOpen\" [backdropDismiss]=\"false\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" (click)=\"closeResetPasswordModal()\">\n <ion-icon name=\"close-outline\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class=\"ion-padding\">\n <section class=\"modal-form-section\">\n @if (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n <val-form [props]=\"resetPasswordFormProps()\" (onSubmit)=\"resetPasswordHandler($event)\" />\n <div class=\"resend-link\">\n <ion-text color=\"dark\">\n {{ t('noCodeReceived') }}\n @if (resetResendCooldown > 0) {\n <span class=\"cooldown\">{{ t('resendIn', { seconds: resetResendCooldown.toString() }) }}</span>\n } @else {\n <a (click)=\"resendResetCode()\">{{ t('resend') }}</a>\n }\n </ion-text>\n </div>\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- MFA Verify Modal -->\n<ion-modal [isOpen]=\"isMFAVerifyModalOpen\" [backdropDismiss]=\"false\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" (click)=\"closeMFAVerifyModal()\">\n <ion-icon name=\"close-outline\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class=\"ion-padding\">\n <section class=\"modal-form-section\">\n @if (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n <val-form [props]=\"mfaVerifyFormProps()\" (onSubmit)=\"verifyMFAHandler($event)\" />\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [".val-login{width:100%}.val-login--card{padding:1rem;background:var(--ion-color-light);border-radius:16px;box-shadow:0 2px 8px #00000014}.logo-container{max-width:130px;margin-bottom:1.5rem}.auth-link{text-align:center;margin-top:1rem;font-size:.9rem}.auth-link a{color:var(--ion-color-primary);text-decoration:none;font-weight:500;cursor:pointer}.auth-link a:hover{text-decoration:underline}.oauth-separator{display:flex;align-items:center;margin:1.5rem 0}.oauth-separator:before,.oauth-separator:after{content:\"\";flex:1;height:1px;background:var(--ion-color-medium-tint)}.oauth-separator span{padding:0 1rem;color:var(--ion-color-dark);font-size:.85rem}.oauth-buttons{margin-bottom:1rem}.oauth-buttons ion-button{--border-radius: 8px;--border-width: 1px}.oauth-buttons ion-icon{font-size:1.2rem;margin-right:.5rem}.legal-notice{margin-top:1.5rem;padding-top:1rem;border-top:1px solid var(--ion-color-medium-tint)}.legal-notice p{font-size:.75rem;line-height:1.5;text-align:center;margin:0}.legal-notice a{color:var(--ion-color-primary);text-decoration:none}.legal-notice a:hover{text-decoration:underline}.resend-link{text-align:center;margin-top:1rem;font-size:.9rem}.resend-link a{color:var(--ion-color-primary);cursor:pointer;font-weight:500}.resend-link a:hover{text-decoration:underline}.resend-link .cooldown{color:var(--ion-color-medium)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonModal, selector: "ion-modal" }, { kind: "component", type: IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: FormComponent, selector: "val-form", inputs: ["props"], outputs: ["onSubmit", "onInvalid", "onSelectChange"] }, { kind: "component", type: ImageComponent, selector: "val-image", inputs: ["props"] }] }); }
32151
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: LoginComponent, isStandalone: true, selector: "val-login", inputs: { props: "props" }, outputs: { onSuccess: "onSuccess", onError: "onError", onMFARequired: "onMFARequired" }, ngImport: i0, template: "<div class=\"val-login\" [class.val-login--card]=\"config.showCard\">\n <!-- Logo -->\n @if (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n\n <!-- Login Form -->\n <val-form [props]=\"loginFormProps()\" (onSubmit)=\"loginHandler($event)\" />\n\n <!-- OAuth Section -->\n @if (config.showOAuth && config.oauthProviders.length > 0) {\n <div class=\"oauth-separator\">\n <span>{{ t('orContinueWith') }}</span>\n </div>\n\n <div class=\"oauth-buttons\">\n @for (provider of config.oauthProviders; track provider) {\n <ion-button\n expand=\"block\"\n fill=\"outline\"\n color=\"dark\"\n (click)=\"loginWithOAuth(provider)\"\n [disabled]=\"isOAuthLoading\"\n >\n @switch (provider) { @case ('google') {\n <ion-icon slot=\"start\" name=\"logo-google\"></ion-icon>\n {{ isOAuthLoading ? t('connecting') : t('continueWithGoogle') }} } @case ('apple') {\n <ion-icon slot=\"start\" name=\"logo-apple\"></ion-icon>\n {{ isOAuthLoading ? t('connecting') : t('continueWithApple') }} } @case ('microsoft') {\n <ion-icon slot=\"start\" name=\"logo-microsoft\"></ion-icon>\n {{ isOAuthLoading ? t('connecting') : t('continueWithMicrosoft') }} } }\n </ion-button>\n }\n </div>\n }\n\n <!-- Register Link -->\n @if (config.showRegister) {\n <div class=\"auth-link\">\n <ion-text color=\"dark\">\n {{ t('noAccount') }}\n <a (click)=\"openRegisterModal()\">{{ t('register') }}</a>\n </ion-text>\n </div>\n }\n\n <!-- Forgot Password Link -->\n @if (config.showForgotPassword) {\n <div class=\"auth-link forgot-password\">\n <ion-text color=\"dark\">\n {{ t('forgotLink') }}\n <a (click)=\"openForgotPasswordModal()\">{{ t('recoverPassword') }}</a>\n </ion-text>\n </div>\n }\n\n <!-- Legal Notice -->\n @if (props.legal) {\n <div class=\"legal-notice\">\n <ion-text color=\"dark\">\n <p>\n {{ t('legalPrefix') }} @if (props.legal.companyLink) {\n <a [href]=\"props.legal.companyLink\"><strong>{{ props.legal.companyName }}</strong></a>\n } @else {\n <strong>{{ props.legal.companyName }}</strong>\n } {{ t('legalSuffix') }} @if (props.legal.termsLink) {\n <a [href]=\"props.legal.termsLink\">{{ t('termsAndConditions') }}</a>\n } @else {\n <span>{{ t('termsAndConditions') }}</span>\n } {{ t('and') }} @if (props.legal.privacyLink) {\n <a [href]=\"props.legal.privacyLink\">{{ t('privacyPolicy') }}</a>\n } @else {\n <span>{{ t('privacyPolicy') }}</span>\n }.\n </p>\n </ion-text>\n </div>\n }\n</div>\n\n<!-- Register Modal -->\n<ion-modal [isOpen]=\"isRegisterModalOpen\" (didDismiss)=\"closeRegisterModal()\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" (click)=\"closeRegisterModal()\">\n <ion-icon name=\"close-outline\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class=\"ion-padding\">\n <section class=\"modal-form-section\">\n @if (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n <val-form [props]=\"registerFormProps()\" (onSubmit)=\"registerHandler($event)\" />\n <div class=\"auth-link\">\n <ion-text color=\"dark\">\n {{ t('hasAccount') }}\n <a (click)=\"closeRegisterModal()\">{{ t('signIn') }}</a>\n </ion-text>\n </div>\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- Verify Email Modal -->\n<ion-modal [isOpen]=\"isVerifyModalOpen\" [backdropDismiss]=\"false\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" (click)=\"closeVerifyModal()\">\n <ion-icon name=\"close-outline\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class=\"ion-padding\">\n <section class=\"modal-form-section\">\n @if (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n <val-form [props]=\"verifyFormProps()\" (onSubmit)=\"verifyHandler($event)\" />\n <div class=\"resend-link\">\n <ion-text color=\"dark\">\n {{ t('noCodeReceived') }} @if (resendCooldown > 0) {\n <span class=\"cooldown\">{{ t('resendIn', { seconds: resendCooldown.toString() }) }}</span>\n } @else {\n <a (click)=\"resendCode()\">{{ t('resend') }}</a>\n }\n </ion-text>\n </div>\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- Forgot Password Modal -->\n<ion-modal [isOpen]=\"isForgotPasswordModalOpen\" (didDismiss)=\"closeForgotPasswordModal()\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" (click)=\"closeForgotPasswordModal()\">\n <ion-icon name=\"close-outline\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class=\"ion-padding\">\n <section class=\"modal-form-section\">\n @if (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n <val-form [props]=\"forgotPasswordFormProps()\" (onSubmit)=\"forgotPasswordHandler($event)\" />\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- Reset Password Modal -->\n<ion-modal [isOpen]=\"isResetPasswordModalOpen\" [backdropDismiss]=\"false\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" (click)=\"closeResetPasswordModal()\">\n <ion-icon name=\"close-outline\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class=\"ion-padding\">\n <section class=\"modal-form-section\">\n @if (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n <val-form [props]=\"resetPasswordFormProps()\" (onSubmit)=\"resetPasswordHandler($event)\" />\n <div class=\"resend-link\">\n <ion-text color=\"dark\">\n {{ t('noCodeReceived') }} @if (resetResendCooldown > 0) {\n <span class=\"cooldown\">{{ t('resendIn', { seconds: resetResendCooldown.toString() }) }}</span>\n } @else {\n <a (click)=\"resendResetCode()\">{{ t('resend') }}</a>\n }\n </ion-text>\n </div>\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- MFA Verify Modal -->\n<ion-modal [isOpen]=\"isMFAVerifyModalOpen\" [backdropDismiss]=\"false\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" (click)=\"closeMFAVerifyModal()\">\n <ion-icon name=\"close-outline\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class=\"ion-padding\">\n <section class=\"modal-form-section\">\n @if (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n <val-form [props]=\"mfaVerifyFormProps()\" (onSubmit)=\"verifyMFAHandler($event)\" />\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [".val-login{width:100%}.val-login--card{padding:1rem;background:var(--ion-color-light);border-radius:16px;box-shadow:0 2px 8px #00000014}.logo-container{max-width:130px;margin-bottom:1.5rem}.auth-link{text-align:center;margin-top:1rem;font-size:.9rem}.auth-link a{color:var(--ion-color-primary);text-decoration:none;font-weight:500;cursor:pointer}.auth-link a:hover{text-decoration:underline}.oauth-separator{display:flex;align-items:center;margin:1.5rem 0}.oauth-separator:before,.oauth-separator:after{content:\"\";flex:1;height:1px;background:var(--ion-color-medium-tint)}.oauth-separator span{padding:0 1rem;color:var(--ion-color-dark);font-size:.85rem}.oauth-buttons{margin-bottom:1rem}.oauth-buttons ion-button{--border-radius: 8px;--border-width: 1px}.oauth-buttons ion-icon{font-size:1.2rem;margin-right:.5rem}.legal-notice{margin-top:1.5rem;padding-top:1rem;border-top:1px solid var(--ion-color-medium-tint)}.legal-notice p{font-size:.75rem;line-height:1.5;text-align:center;margin:0}.legal-notice a{color:var(--ion-color-primary);text-decoration:none}.legal-notice a:hover{text-decoration:underline}.resend-link{text-align:center;margin-top:1rem;font-size:.9rem}.resend-link a{color:var(--ion-color-primary);cursor:pointer;font-weight:500}.resend-link a:hover{text-decoration:underline}.resend-link .cooldown{color:var(--ion-color-medium)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonModal, selector: "ion-modal" }, { kind: "component", type: IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: FormComponent, selector: "val-form", inputs: ["props"], outputs: ["onSubmit", "onInvalid", "onSelectChange"] }, { kind: "component", type: ImageComponent, selector: "val-image", inputs: ["props"] }] }); }
32059
32152
  }
32060
32153
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LoginComponent, decorators: [{
32061
32154
  type: Component,
@@ -32071,7 +32164,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
32071
32164
  IonToolbar,
32072
32165
  FormComponent,
32073
32166
  ImageComponent,
32074
- ], template: "<div class=\"val-login\" [class.val-login--card]=\"config.showCard\">\n <!-- Logo -->\n @if (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n\n <!-- Login Form -->\n <val-form [props]=\"loginFormProps()\" (onSubmit)=\"loginHandler($event)\" />\n\n <!-- OAuth Section -->\n @if (config.showOAuth && config.oauthProviders.length > 0) {\n <div class=\"oauth-separator\">\n <span>{{ t('orContinueWith') }}</span>\n </div>\n\n <div class=\"oauth-buttons\">\n @for (provider of config.oauthProviders; track provider) {\n <ion-button\n expand=\"block\"\n fill=\"outline\"\n color=\"dark\"\n (click)=\"loginWithOAuth(provider)\"\n [disabled]=\"isOAuthLoading\"\n >\n @switch (provider) {\n @case ('google') {\n <ion-icon slot=\"start\" name=\"logo-google\"></ion-icon>\n {{ isOAuthLoading ? t('connecting') : t('continueWithGoogle') }}\n }\n @case ('apple') {\n <ion-icon slot=\"start\" name=\"logo-apple\"></ion-icon>\n {{ isOAuthLoading ? t('connecting') : t('continueWithApple') }}\n }\n @case ('microsoft') {\n <ion-icon slot=\"start\" name=\"logo-microsoft\"></ion-icon>\n {{ isOAuthLoading ? t('connecting') : t('continueWithMicrosoft') }}\n }\n }\n </ion-button>\n }\n </div>\n }\n\n <!-- Register Link -->\n @if (config.showRegister) {\n <div class=\"auth-link\">\n <ion-text color=\"dark\">\n {{ t('noAccount') }}\n <a (click)=\"openRegisterModal()\">{{ t('register') }}</a>\n </ion-text>\n </div>\n }\n\n <!-- Forgot Password Link -->\n @if (config.showForgotPassword) {\n <div class=\"auth-link forgot-password\">\n <ion-text color=\"dark\">\n {{ t('forgotLink') }}\n <a (click)=\"openForgotPasswordModal()\">{{ t('recoverPassword') }}</a>\n </ion-text>\n </div>\n }\n\n <!-- Legal Notice -->\n @if (props.legal) {\n <div class=\"legal-notice\">\n <ion-text color=\"medium\">\n <p>\n {{ t('legalPrefix') }}\n @if (props.legal.companyLink) {\n <a [href]=\"props.legal.companyLink\"><strong>{{ props.legal.companyName }}</strong></a>\n } @else {\n <strong>{{ props.legal.companyName }}</strong>\n }\n {{ t('legalSuffix') }}\n @if (props.legal.termsLink) {\n <a [href]=\"props.legal.termsLink\">{{ t('termsAndConditions') }}</a>\n } @else {\n <span>{{ t('termsAndConditions') }}</span>\n }\n {{ t('and') }}\n @if (props.legal.privacyLink) {\n <a [href]=\"props.legal.privacyLink\">{{ t('privacyPolicy') }}</a>\n } @else {\n <span>{{ t('privacyPolicy') }}</span>\n }.\n </p>\n </ion-text>\n </div>\n }\n</div>\n\n<!-- Register Modal -->\n<ion-modal [isOpen]=\"isRegisterModalOpen\" (didDismiss)=\"closeRegisterModal()\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" (click)=\"closeRegisterModal()\">\n <ion-icon name=\"close-outline\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class=\"ion-padding\">\n <section class=\"modal-form-section\">\n @if (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n <val-form [props]=\"registerFormProps()\" (onSubmit)=\"registerHandler($event)\" />\n <div class=\"auth-link\">\n <ion-text color=\"dark\">\n {{ t('hasAccount') }}\n <a (click)=\"closeRegisterModal()\">{{ t('signIn') }}</a>\n </ion-text>\n </div>\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- Verify Email Modal -->\n<ion-modal [isOpen]=\"isVerifyModalOpen\" [backdropDismiss]=\"false\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" (click)=\"closeVerifyModal()\">\n <ion-icon name=\"close-outline\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class=\"ion-padding\">\n <section class=\"modal-form-section\">\n @if (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n <val-form [props]=\"verifyFormProps()\" (onSubmit)=\"verifyHandler($event)\" />\n <div class=\"resend-link\">\n <ion-text color=\"dark\">\n {{ t('noCodeReceived') }}\n @if (resendCooldown > 0) {\n <span class=\"cooldown\">{{ t('resendIn', { seconds: resendCooldown.toString() }) }}</span>\n } @else {\n <a (click)=\"resendCode()\">{{ t('resend') }}</a>\n }\n </ion-text>\n </div>\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- Forgot Password Modal -->\n<ion-modal [isOpen]=\"isForgotPasswordModalOpen\" (didDismiss)=\"closeForgotPasswordModal()\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" (click)=\"closeForgotPasswordModal()\">\n <ion-icon name=\"close-outline\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class=\"ion-padding\">\n <section class=\"modal-form-section\">\n @if (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n <val-form [props]=\"forgotPasswordFormProps()\" (onSubmit)=\"forgotPasswordHandler($event)\" />\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- Reset Password Modal -->\n<ion-modal [isOpen]=\"isResetPasswordModalOpen\" [backdropDismiss]=\"false\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" (click)=\"closeResetPasswordModal()\">\n <ion-icon name=\"close-outline\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class=\"ion-padding\">\n <section class=\"modal-form-section\">\n @if (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n <val-form [props]=\"resetPasswordFormProps()\" (onSubmit)=\"resetPasswordHandler($event)\" />\n <div class=\"resend-link\">\n <ion-text color=\"dark\">\n {{ t('noCodeReceived') }}\n @if (resetResendCooldown > 0) {\n <span class=\"cooldown\">{{ t('resendIn', { seconds: resetResendCooldown.toString() }) }}</span>\n } @else {\n <a (click)=\"resendResetCode()\">{{ t('resend') }}</a>\n }\n </ion-text>\n </div>\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- MFA Verify Modal -->\n<ion-modal [isOpen]=\"isMFAVerifyModalOpen\" [backdropDismiss]=\"false\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" (click)=\"closeMFAVerifyModal()\">\n <ion-icon name=\"close-outline\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class=\"ion-padding\">\n <section class=\"modal-form-section\">\n @if (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n <val-form [props]=\"mfaVerifyFormProps()\" (onSubmit)=\"verifyMFAHandler($event)\" />\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [".val-login{width:100%}.val-login--card{padding:1rem;background:var(--ion-color-light);border-radius:16px;box-shadow:0 2px 8px #00000014}.logo-container{max-width:130px;margin-bottom:1.5rem}.auth-link{text-align:center;margin-top:1rem;font-size:.9rem}.auth-link a{color:var(--ion-color-primary);text-decoration:none;font-weight:500;cursor:pointer}.auth-link a:hover{text-decoration:underline}.oauth-separator{display:flex;align-items:center;margin:1.5rem 0}.oauth-separator:before,.oauth-separator:after{content:\"\";flex:1;height:1px;background:var(--ion-color-medium-tint)}.oauth-separator span{padding:0 1rem;color:var(--ion-color-dark);font-size:.85rem}.oauth-buttons{margin-bottom:1rem}.oauth-buttons ion-button{--border-radius: 8px;--border-width: 1px}.oauth-buttons ion-icon{font-size:1.2rem;margin-right:.5rem}.legal-notice{margin-top:1.5rem;padding-top:1rem;border-top:1px solid var(--ion-color-medium-tint)}.legal-notice p{font-size:.75rem;line-height:1.5;text-align:center;margin:0}.legal-notice a{color:var(--ion-color-primary);text-decoration:none}.legal-notice a:hover{text-decoration:underline}.resend-link{text-align:center;margin-top:1rem;font-size:.9rem}.resend-link a{color:var(--ion-color-primary);cursor:pointer;font-weight:500}.resend-link a:hover{text-decoration:underline}.resend-link .cooldown{color:var(--ion-color-medium)}\n"] }]
32167
+ ], template: "<div class=\"val-login\" [class.val-login--card]=\"config.showCard\">\n <!-- Logo -->\n @if (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n\n <!-- Login Form -->\n <val-form [props]=\"loginFormProps()\" (onSubmit)=\"loginHandler($event)\" />\n\n <!-- OAuth Section -->\n @if (config.showOAuth && config.oauthProviders.length > 0) {\n <div class=\"oauth-separator\">\n <span>{{ t('orContinueWith') }}</span>\n </div>\n\n <div class=\"oauth-buttons\">\n @for (provider of config.oauthProviders; track provider) {\n <ion-button\n expand=\"block\"\n fill=\"outline\"\n color=\"dark\"\n (click)=\"loginWithOAuth(provider)\"\n [disabled]=\"isOAuthLoading\"\n >\n @switch (provider) { @case ('google') {\n <ion-icon slot=\"start\" name=\"logo-google\"></ion-icon>\n {{ isOAuthLoading ? t('connecting') : t('continueWithGoogle') }} } @case ('apple') {\n <ion-icon slot=\"start\" name=\"logo-apple\"></ion-icon>\n {{ isOAuthLoading ? t('connecting') : t('continueWithApple') }} } @case ('microsoft') {\n <ion-icon slot=\"start\" name=\"logo-microsoft\"></ion-icon>\n {{ isOAuthLoading ? t('connecting') : t('continueWithMicrosoft') }} } }\n </ion-button>\n }\n </div>\n }\n\n <!-- Register Link -->\n @if (config.showRegister) {\n <div class=\"auth-link\">\n <ion-text color=\"dark\">\n {{ t('noAccount') }}\n <a (click)=\"openRegisterModal()\">{{ t('register') }}</a>\n </ion-text>\n </div>\n }\n\n <!-- Forgot Password Link -->\n @if (config.showForgotPassword) {\n <div class=\"auth-link forgot-password\">\n <ion-text color=\"dark\">\n {{ t('forgotLink') }}\n <a (click)=\"openForgotPasswordModal()\">{{ t('recoverPassword') }}</a>\n </ion-text>\n </div>\n }\n\n <!-- Legal Notice -->\n @if (props.legal) {\n <div class=\"legal-notice\">\n <ion-text color=\"dark\">\n <p>\n {{ t('legalPrefix') }} @if (props.legal.companyLink) {\n <a [href]=\"props.legal.companyLink\"><strong>{{ props.legal.companyName }}</strong></a>\n } @else {\n <strong>{{ props.legal.companyName }}</strong>\n } {{ t('legalSuffix') }} @if (props.legal.termsLink) {\n <a [href]=\"props.legal.termsLink\">{{ t('termsAndConditions') }}</a>\n } @else {\n <span>{{ t('termsAndConditions') }}</span>\n } {{ t('and') }} @if (props.legal.privacyLink) {\n <a [href]=\"props.legal.privacyLink\">{{ t('privacyPolicy') }}</a>\n } @else {\n <span>{{ t('privacyPolicy') }}</span>\n }.\n </p>\n </ion-text>\n </div>\n }\n</div>\n\n<!-- Register Modal -->\n<ion-modal [isOpen]=\"isRegisterModalOpen\" (didDismiss)=\"closeRegisterModal()\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" (click)=\"closeRegisterModal()\">\n <ion-icon name=\"close-outline\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class=\"ion-padding\">\n <section class=\"modal-form-section\">\n @if (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n <val-form [props]=\"registerFormProps()\" (onSubmit)=\"registerHandler($event)\" />\n <div class=\"auth-link\">\n <ion-text color=\"dark\">\n {{ t('hasAccount') }}\n <a (click)=\"closeRegisterModal()\">{{ t('signIn') }}</a>\n </ion-text>\n </div>\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- Verify Email Modal -->\n<ion-modal [isOpen]=\"isVerifyModalOpen\" [backdropDismiss]=\"false\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" (click)=\"closeVerifyModal()\">\n <ion-icon name=\"close-outline\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class=\"ion-padding\">\n <section class=\"modal-form-section\">\n @if (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n <val-form [props]=\"verifyFormProps()\" (onSubmit)=\"verifyHandler($event)\" />\n <div class=\"resend-link\">\n <ion-text color=\"dark\">\n {{ t('noCodeReceived') }} @if (resendCooldown > 0) {\n <span class=\"cooldown\">{{ t('resendIn', { seconds: resendCooldown.toString() }) }}</span>\n } @else {\n <a (click)=\"resendCode()\">{{ t('resend') }}</a>\n }\n </ion-text>\n </div>\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- Forgot Password Modal -->\n<ion-modal [isOpen]=\"isForgotPasswordModalOpen\" (didDismiss)=\"closeForgotPasswordModal()\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" (click)=\"closeForgotPasswordModal()\">\n <ion-icon name=\"close-outline\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class=\"ion-padding\">\n <section class=\"modal-form-section\">\n @if (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n <val-form [props]=\"forgotPasswordFormProps()\" (onSubmit)=\"forgotPasswordHandler($event)\" />\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- Reset Password Modal -->\n<ion-modal [isOpen]=\"isResetPasswordModalOpen\" [backdropDismiss]=\"false\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" (click)=\"closeResetPasswordModal()\">\n <ion-icon name=\"close-outline\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class=\"ion-padding\">\n <section class=\"modal-form-section\">\n @if (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n <val-form [props]=\"resetPasswordFormProps()\" (onSubmit)=\"resetPasswordHandler($event)\" />\n <div class=\"resend-link\">\n <ion-text color=\"dark\">\n {{ t('noCodeReceived') }} @if (resetResendCooldown > 0) {\n <span class=\"cooldown\">{{ t('resendIn', { seconds: resetResendCooldown.toString() }) }}</span>\n } @else {\n <a (click)=\"resendResetCode()\">{{ t('resend') }}</a>\n }\n </ion-text>\n </div>\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- MFA Verify Modal -->\n<ion-modal [isOpen]=\"isMFAVerifyModalOpen\" [backdropDismiss]=\"false\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" (click)=\"closeMFAVerifyModal()\">\n <ion-icon name=\"close-outline\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class=\"ion-padding\">\n <section class=\"modal-form-section\">\n @if (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n <val-form [props]=\"mfaVerifyFormProps()\" (onSubmit)=\"verifyMFAHandler($event)\" />\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [".val-login{width:100%}.val-login--card{padding:1rem;background:var(--ion-color-light);border-radius:16px;box-shadow:0 2px 8px #00000014}.logo-container{max-width:130px;margin-bottom:1.5rem}.auth-link{text-align:center;margin-top:1rem;font-size:.9rem}.auth-link a{color:var(--ion-color-primary);text-decoration:none;font-weight:500;cursor:pointer}.auth-link a:hover{text-decoration:underline}.oauth-separator{display:flex;align-items:center;margin:1.5rem 0}.oauth-separator:before,.oauth-separator:after{content:\"\";flex:1;height:1px;background:var(--ion-color-medium-tint)}.oauth-separator span{padding:0 1rem;color:var(--ion-color-dark);font-size:.85rem}.oauth-buttons{margin-bottom:1rem}.oauth-buttons ion-button{--border-radius: 8px;--border-width: 1px}.oauth-buttons ion-icon{font-size:1.2rem;margin-right:.5rem}.legal-notice{margin-top:1.5rem;padding-top:1rem;border-top:1px solid var(--ion-color-medium-tint)}.legal-notice p{font-size:.75rem;line-height:1.5;text-align:center;margin:0}.legal-notice a{color:var(--ion-color-primary);text-decoration:none}.legal-notice a:hover{text-decoration:underline}.resend-link{text-align:center;margin-top:1rem;font-size:.9rem}.resend-link a{color:var(--ion-color-primary);cursor:pointer;font-weight:500}.resend-link a:hover{text-decoration:underline}.resend-link .cooldown{color:var(--ion-color-medium)}\n"] }]
32075
32168
  }], propDecorators: { props: [{
32076
32169
  type: Input
32077
32170
  }], onSuccess: [{
@@ -32472,7 +32565,7 @@ const BOTTOM_NAV_DEFAULTS = {
32472
32565
  hideLabels: false,
32473
32566
  safeArea: true,
32474
32567
  animation: 'scale',
32475
- maxWidth: 'xl',
32568
+ maxWidth: 'md',
32476
32569
  theme: {
32477
32570
  background: 'var(--ion-background-color)',
32478
32571
  activeColor: 'primary',
@@ -32647,7 +32740,7 @@ class BottomNavComponent {
32647
32740
  return 'none';
32648
32741
  case undefined:
32649
32742
  case '':
32650
- return 'var(--val-container-xl, 1100px)';
32743
+ return 'var(--val-container-md, 720px)';
32651
32744
  default:
32652
32745
  return v;
32653
32746
  }
@@ -32773,7 +32866,7 @@ class BottomNavComponent {
32773
32866
  }
32774
32867
  </div>
32775
32868
  </nav>
32776
- `, isInline: true, styles: [":host{display:block;position:fixed;bottom:0;left:0;right:0;z-index:100;pointer-events:none}.bottom-nav{--bottom-nav-bg: var(--ion-background-color);--bottom-nav-active: var(--ion-color-primary);--bottom-nav-inactive: var(--ion-color-medium);--bottom-nav-radius: 16px 16px 0 0;--bottom-nav-height: 64px;--bottom-nav-fab-size: 56px;--bottom-nav-max-width: var(--val-container-xl, 1100px);--fab-color: var(--ion-color-primary);pointer-events:auto;background:var(--bottom-nav-bg);border-radius:var(--bottom-nav-radius);height:var(--bottom-nav-height);padding:0 8px;max-width:var(--bottom-nav-max-width);margin:0 auto}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000014}.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.75);backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);border-top:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.06)}.bottom-nav--floating{margin:0 auto 8px;width:calc(100% - 32px);border-radius:32px;border:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.08);box-shadow:0 4px 24px #00000014,0 8px 32px #0000000a;--bottom-nav-radius: 32px}.bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.82);backdrop-filter:blur(28px) saturate(200%);-webkit-backdrop-filter:blur(28px) saturate(200%);border:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.08)}.bottom-nav--floating.bottom-nav--elevated{box-shadow:0 4px 24px #0000001a,0 12px 48px #00000014}.bottom-nav--floating.bottom-nav--safe-area{margin-bottom:calc(8px + env(safe-area-inset-bottom,0))}.bottom-nav--safe-area{padding-bottom:env(safe-area-inset-bottom,0);height:calc(var(--bottom-nav-height) + env(safe-area-inset-bottom,0))}.bottom-nav__container{display:flex;align-items:center;justify-content:space-around;height:var(--bottom-nav-height);max-width:500px;margin:0 auto}.bottom-nav__tab{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;height:100%;padding:8px 4px;background:transparent;border:none;cursor:pointer;position:relative;overflow:hidden;color:var(--bottom-nav-inactive);transition:color .2s ease;max-width:80px;--ripple-color: var(--bottom-nav-active)}.bottom-nav__tab:focus-visible{outline:2px solid var(--bottom-nav-active);outline-offset:-2px;border-radius:8px}.bottom-nav__tab--active{color:var(--bottom-nav-active)}.bottom-nav__tab--disabled{opacity:.4;cursor:not-allowed;pointer-events:none}.bottom-nav__tab[data-animation=scale] .bottom-nav__tab-icon{transition:transform .2s cubic-bezier(.4,0,.2,1)}.bottom-nav__tab[data-animation=scale].bottom-nav__tab--active .bottom-nav__tab-icon{transform:scale(1.15)}.bottom-nav__tab[data-animation=fade]{transition:opacity .2s ease,color .2s ease}.bottom-nav__tab[data-animation=fade]:not(.bottom-nav__tab--active){opacity:.6}.bottom-nav__tab[data-animation=slide] .bottom-nav__tab-label{transition:transform .2s ease,opacity .2s ease;transform:translateY(6px);opacity:0}.bottom-nav__tab[data-animation=slide].bottom-nav__tab--active .bottom-nav__tab-label{transform:translateY(0);opacity:1}.bottom-nav__tab-icon{position:relative;font-size:24px;line-height:1}.bottom-nav__tab-icon ion-icon{display:block}.bottom-nav__tab-label{font-size:11px;font-weight:500;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;letter-spacing:.01em}.bottom-nav__badge{position:absolute;top:-4px;right:-10px;min-width:18px;height:18px;padding:0 5px;font-size:10px;font-weight:600;line-height:18px;text-align:center;color:#fff;background-color:var(--ion-color-danger);border-radius:9px;box-shadow:0 1px 3px #0003}.bottom-nav__badge--dot{min-width:10px;width:10px;height:10px;padding:0;top:-2px;right:-4px;border-radius:50%}.bottom-nav__fab{position:relative;flex-shrink:0;width:var(--bottom-nav-fab-size);height:var(--bottom-nav-fab-size);margin:0 12px;margin-top:calc(var(--bottom-nav-fab-size) * -.35);background:var(--fab-color);border:none;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#fff;font-size:28px;box-shadow:0 4px 14px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.4);transition:transform .2s cubic-bezier(.4,0,.2,1),box-shadow .2s ease;overflow:hidden;--ripple-color: rgba(255, 255, 255, .3)}.bottom-nav__fab:hover{transform:scale(1.08);box-shadow:0 6px 20px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.5)}.bottom-nav__fab:active{transform:scale(.95)}.bottom-nav__fab:focus-visible{outline:3px solid white;outline-offset:2px}.bottom-nav__fab--small{--bottom-nav-fab-size: 48px;font-size:24px;margin-top:-14.4px}.bottom-nav__fab ion-icon{display:block}.bottom-nav--hide-labels{--bottom-nav-height: 56px}.bottom-nav--hide-labels .bottom-nav__tab-label{display:none}.bottom-nav--hide-labels .bottom-nav__tab-icon{font-size:26px}@media (prefers-color-scheme: dark){.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 0, 0, 0),.8);border-top-color:#ffffff14}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000040}.bottom-nav--floating{border-color:#ffffff1a;box-shadow:0 4px 24px #0003,0 8px 32px #00000026}.bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.88);border-color:#ffffff1a}.bottom-nav__fab{box-shadow:0 4px 14px #0006}.bottom-nav__fab:hover{box-shadow:0 6px 20px #00000080}}:host-context(.dark) .bottom-nav--translucent,:host-context(body.dark) .bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.85);border-top-color:#ffffff14}:host-context(.dark) .bottom-nav--elevated,:host-context(body.dark) .bottom-nav--elevated,:host-context([data-theme=dark]) .bottom-nav--elevated{box-shadow:0 -4px 20px #0000004d}:host-context(.dark) .bottom-nav--floating,:host-context(body.dark) .bottom-nav--floating,:host-context([data-theme=dark]) .bottom-nav--floating{border-color:#ffffff1a;box-shadow:0 4px 24px #0003,0 8px 32px #00000026}:host-context(.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context(body.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.88);border-color:#ffffff1a}:host-context(.dark) .bottom-nav__fab,:host-context(body.dark) .bottom-nav__fab,:host-context([data-theme=dark]) .bottom-nav__fab{box-shadow:0 4px 14px #00000080}:host-context(.dark) .bottom-nav__fab:hover,:host-context(body.dark) .bottom-nav__fab:hover,:host-context([data-theme=dark]) .bottom-nav__fab:hover{box-shadow:0 6px 20px #0009}@supports (padding-bottom: env(safe-area-inset-bottom)){.bottom-nav--safe-area .bottom-nav__container{height:var(--bottom-nav-height)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonRippleEffect, selector: "ion-ripple-effect", inputs: ["type"] }] }); }
32869
+ `, isInline: true, styles: [":host{display:block;position:fixed;bottom:0;left:0;right:0;z-index:100;pointer-events:none}.bottom-nav{--bottom-nav-bg: var(--ion-background-color);--bottom-nav-active: var(--ion-color-primary);--bottom-nav-inactive: var(--ion-color-medium);--bottom-nav-radius: 16px 16px 0 0;--bottom-nav-height: 64px;--bottom-nav-fab-size: 56px;--bottom-nav-max-width: var(--val-container-md, 720px);--fab-color: var(--ion-color-primary);pointer-events:auto;background:var(--bottom-nav-bg);border-radius:var(--bottom-nav-radius);height:var(--bottom-nav-height);padding:0 8px;max-width:var(--bottom-nav-max-width);margin:0 auto}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000014}.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.75);backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);border-top:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.06)}.bottom-nav--floating{margin:0 auto 8px;width:calc(100% - 32px);border-radius:32px;border:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.08);box-shadow:0 4px 24px #00000014,0 8px 32px #0000000a;--bottom-nav-radius: 32px}.bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.82);backdrop-filter:blur(28px) saturate(200%);-webkit-backdrop-filter:blur(28px) saturate(200%);border:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.08)}.bottom-nav--floating.bottom-nav--elevated{box-shadow:0 4px 24px #0000001a,0 12px 48px #00000014}.bottom-nav--floating.bottom-nav--safe-area{margin-bottom:calc(8px + env(safe-area-inset-bottom,0))}.bottom-nav--safe-area{padding-bottom:env(safe-area-inset-bottom,0);height:calc(var(--bottom-nav-height) + env(safe-area-inset-bottom,0))}.bottom-nav__container{display:flex;align-items:center;justify-content:space-around;height:var(--bottom-nav-height);max-width:500px;margin:0 auto}.bottom-nav__tab{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;height:100%;padding:8px 4px;background:transparent;border:none;cursor:pointer;position:relative;overflow:hidden;color:var(--bottom-nav-inactive);transition:color .2s ease;max-width:80px;--ripple-color: var(--bottom-nav-active)}.bottom-nav__tab:focus-visible{outline:2px solid var(--bottom-nav-active);outline-offset:-2px;border-radius:8px}.bottom-nav__tab--active{color:var(--bottom-nav-active)}.bottom-nav__tab--disabled{opacity:.4;cursor:not-allowed;pointer-events:none}.bottom-nav__tab[data-animation=scale] .bottom-nav__tab-icon{transition:transform .2s cubic-bezier(.4,0,.2,1)}.bottom-nav__tab[data-animation=scale].bottom-nav__tab--active .bottom-nav__tab-icon{transform:scale(1.15)}.bottom-nav__tab[data-animation=fade]{transition:opacity .2s ease,color .2s ease}.bottom-nav__tab[data-animation=fade]:not(.bottom-nav__tab--active){opacity:.6}.bottom-nav__tab[data-animation=slide] .bottom-nav__tab-label{transition:transform .2s ease,opacity .2s ease;transform:translateY(6px);opacity:0}.bottom-nav__tab[data-animation=slide].bottom-nav__tab--active .bottom-nav__tab-label{transform:translateY(0);opacity:1}.bottom-nav__tab-icon{position:relative;font-size:24px;line-height:1}.bottom-nav__tab-icon ion-icon{display:block}.bottom-nav__tab-label{font-size:11px;font-weight:500;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;letter-spacing:.01em}.bottom-nav__badge{position:absolute;top:-4px;right:-10px;min-width:18px;height:18px;padding:0 5px;font-size:10px;font-weight:600;line-height:18px;text-align:center;color:#fff;background-color:var(--ion-color-danger);border-radius:9px;box-shadow:0 1px 3px #0003}.bottom-nav__badge--dot{min-width:10px;width:10px;height:10px;padding:0;top:-2px;right:-4px;border-radius:50%}.bottom-nav__fab{position:relative;flex-shrink:0;width:var(--bottom-nav-fab-size);height:var(--bottom-nav-fab-size);margin:0 12px;margin-top:calc(var(--bottom-nav-fab-size) * -.35);background:var(--fab-color);border:none;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#fff;font-size:28px;box-shadow:0 4px 14px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.4);transition:transform .2s cubic-bezier(.4,0,.2,1),box-shadow .2s ease;overflow:hidden;--ripple-color: rgba(255, 255, 255, .3)}.bottom-nav__fab:hover{transform:scale(1.08);box-shadow:0 6px 20px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.5)}.bottom-nav__fab:active{transform:scale(.95)}.bottom-nav__fab:focus-visible{outline:3px solid white;outline-offset:2px}.bottom-nav__fab--small{--bottom-nav-fab-size: 48px;font-size:24px;margin-top:-14.4px}.bottom-nav__fab ion-icon{display:block}.bottom-nav--hide-labels{--bottom-nav-height: 56px}.bottom-nav--hide-labels .bottom-nav__tab-label{display:none}.bottom-nav--hide-labels .bottom-nav__tab-icon{font-size:26px}@media (prefers-color-scheme: dark){.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 0, 0, 0),.8);border-top-color:#ffffff14}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000040}.bottom-nav--floating{border-color:#ffffff1a;box-shadow:0 4px 24px #0003,0 8px 32px #00000026}.bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.88);border-color:#ffffff1a}.bottom-nav__fab{box-shadow:0 4px 14px #0006}.bottom-nav__fab:hover{box-shadow:0 6px 20px #00000080}}:host-context(.dark) .bottom-nav--translucent,:host-context(body.dark) .bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.85);border-top-color:#ffffff14}:host-context(.dark) .bottom-nav--elevated,:host-context(body.dark) .bottom-nav--elevated,:host-context([data-theme=dark]) .bottom-nav--elevated{box-shadow:0 -4px 20px #0000004d}:host-context(.dark) .bottom-nav--floating,:host-context(body.dark) .bottom-nav--floating,:host-context([data-theme=dark]) .bottom-nav--floating{border-color:#ffffff1a;box-shadow:0 4px 24px #0003,0 8px 32px #00000026}:host-context(.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context(body.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.88);border-color:#ffffff1a}:host-context(.dark) .bottom-nav__fab,:host-context(body.dark) .bottom-nav__fab,:host-context([data-theme=dark]) .bottom-nav__fab{box-shadow:0 4px 14px #00000080}:host-context(.dark) .bottom-nav__fab:hover,:host-context(body.dark) .bottom-nav__fab:hover,:host-context([data-theme=dark]) .bottom-nav__fab:hover{box-shadow:0 6px 20px #0009}@supports (padding-bottom: env(safe-area-inset-bottom)){.bottom-nav--safe-area .bottom-nav__container{height:var(--bottom-nav-height)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonRippleEffect, selector: "ion-ripple-effect", inputs: ["type"] }] }); }
32777
32870
  }
32778
32871
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: BottomNavComponent, decorators: [{
32779
32872
  type: Component,
@@ -32847,7 +32940,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
32847
32940
  }
32848
32941
  </div>
32849
32942
  </nav>
32850
- `, styles: [":host{display:block;position:fixed;bottom:0;left:0;right:0;z-index:100;pointer-events:none}.bottom-nav{--bottom-nav-bg: var(--ion-background-color);--bottom-nav-active: var(--ion-color-primary);--bottom-nav-inactive: var(--ion-color-medium);--bottom-nav-radius: 16px 16px 0 0;--bottom-nav-height: 64px;--bottom-nav-fab-size: 56px;--bottom-nav-max-width: var(--val-container-xl, 1100px);--fab-color: var(--ion-color-primary);pointer-events:auto;background:var(--bottom-nav-bg);border-radius:var(--bottom-nav-radius);height:var(--bottom-nav-height);padding:0 8px;max-width:var(--bottom-nav-max-width);margin:0 auto}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000014}.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.75);backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);border-top:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.06)}.bottom-nav--floating{margin:0 auto 8px;width:calc(100% - 32px);border-radius:32px;border:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.08);box-shadow:0 4px 24px #00000014,0 8px 32px #0000000a;--bottom-nav-radius: 32px}.bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.82);backdrop-filter:blur(28px) saturate(200%);-webkit-backdrop-filter:blur(28px) saturate(200%);border:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.08)}.bottom-nav--floating.bottom-nav--elevated{box-shadow:0 4px 24px #0000001a,0 12px 48px #00000014}.bottom-nav--floating.bottom-nav--safe-area{margin-bottom:calc(8px + env(safe-area-inset-bottom,0))}.bottom-nav--safe-area{padding-bottom:env(safe-area-inset-bottom,0);height:calc(var(--bottom-nav-height) + env(safe-area-inset-bottom,0))}.bottom-nav__container{display:flex;align-items:center;justify-content:space-around;height:var(--bottom-nav-height);max-width:500px;margin:0 auto}.bottom-nav__tab{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;height:100%;padding:8px 4px;background:transparent;border:none;cursor:pointer;position:relative;overflow:hidden;color:var(--bottom-nav-inactive);transition:color .2s ease;max-width:80px;--ripple-color: var(--bottom-nav-active)}.bottom-nav__tab:focus-visible{outline:2px solid var(--bottom-nav-active);outline-offset:-2px;border-radius:8px}.bottom-nav__tab--active{color:var(--bottom-nav-active)}.bottom-nav__tab--disabled{opacity:.4;cursor:not-allowed;pointer-events:none}.bottom-nav__tab[data-animation=scale] .bottom-nav__tab-icon{transition:transform .2s cubic-bezier(.4,0,.2,1)}.bottom-nav__tab[data-animation=scale].bottom-nav__tab--active .bottom-nav__tab-icon{transform:scale(1.15)}.bottom-nav__tab[data-animation=fade]{transition:opacity .2s ease,color .2s ease}.bottom-nav__tab[data-animation=fade]:not(.bottom-nav__tab--active){opacity:.6}.bottom-nav__tab[data-animation=slide] .bottom-nav__tab-label{transition:transform .2s ease,opacity .2s ease;transform:translateY(6px);opacity:0}.bottom-nav__tab[data-animation=slide].bottom-nav__tab--active .bottom-nav__tab-label{transform:translateY(0);opacity:1}.bottom-nav__tab-icon{position:relative;font-size:24px;line-height:1}.bottom-nav__tab-icon ion-icon{display:block}.bottom-nav__tab-label{font-size:11px;font-weight:500;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;letter-spacing:.01em}.bottom-nav__badge{position:absolute;top:-4px;right:-10px;min-width:18px;height:18px;padding:0 5px;font-size:10px;font-weight:600;line-height:18px;text-align:center;color:#fff;background-color:var(--ion-color-danger);border-radius:9px;box-shadow:0 1px 3px #0003}.bottom-nav__badge--dot{min-width:10px;width:10px;height:10px;padding:0;top:-2px;right:-4px;border-radius:50%}.bottom-nav__fab{position:relative;flex-shrink:0;width:var(--bottom-nav-fab-size);height:var(--bottom-nav-fab-size);margin:0 12px;margin-top:calc(var(--bottom-nav-fab-size) * -.35);background:var(--fab-color);border:none;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#fff;font-size:28px;box-shadow:0 4px 14px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.4);transition:transform .2s cubic-bezier(.4,0,.2,1),box-shadow .2s ease;overflow:hidden;--ripple-color: rgba(255, 255, 255, .3)}.bottom-nav__fab:hover{transform:scale(1.08);box-shadow:0 6px 20px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.5)}.bottom-nav__fab:active{transform:scale(.95)}.bottom-nav__fab:focus-visible{outline:3px solid white;outline-offset:2px}.bottom-nav__fab--small{--bottom-nav-fab-size: 48px;font-size:24px;margin-top:-14.4px}.bottom-nav__fab ion-icon{display:block}.bottom-nav--hide-labels{--bottom-nav-height: 56px}.bottom-nav--hide-labels .bottom-nav__tab-label{display:none}.bottom-nav--hide-labels .bottom-nav__tab-icon{font-size:26px}@media (prefers-color-scheme: dark){.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 0, 0, 0),.8);border-top-color:#ffffff14}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000040}.bottom-nav--floating{border-color:#ffffff1a;box-shadow:0 4px 24px #0003,0 8px 32px #00000026}.bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.88);border-color:#ffffff1a}.bottom-nav__fab{box-shadow:0 4px 14px #0006}.bottom-nav__fab:hover{box-shadow:0 6px 20px #00000080}}:host-context(.dark) .bottom-nav--translucent,:host-context(body.dark) .bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.85);border-top-color:#ffffff14}:host-context(.dark) .bottom-nav--elevated,:host-context(body.dark) .bottom-nav--elevated,:host-context([data-theme=dark]) .bottom-nav--elevated{box-shadow:0 -4px 20px #0000004d}:host-context(.dark) .bottom-nav--floating,:host-context(body.dark) .bottom-nav--floating,:host-context([data-theme=dark]) .bottom-nav--floating{border-color:#ffffff1a;box-shadow:0 4px 24px #0003,0 8px 32px #00000026}:host-context(.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context(body.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.88);border-color:#ffffff1a}:host-context(.dark) .bottom-nav__fab,:host-context(body.dark) .bottom-nav__fab,:host-context([data-theme=dark]) .bottom-nav__fab{box-shadow:0 4px 14px #00000080}:host-context(.dark) .bottom-nav__fab:hover,:host-context(body.dark) .bottom-nav__fab:hover,:host-context([data-theme=dark]) .bottom-nav__fab:hover{box-shadow:0 6px 20px #0009}@supports (padding-bottom: env(safe-area-inset-bottom)){.bottom-nav--safe-area .bottom-nav__container{height:var(--bottom-nav-height)}}\n"] }]
32943
+ `, styles: [":host{display:block;position:fixed;bottom:0;left:0;right:0;z-index:100;pointer-events:none}.bottom-nav{--bottom-nav-bg: var(--ion-background-color);--bottom-nav-active: var(--ion-color-primary);--bottom-nav-inactive: var(--ion-color-medium);--bottom-nav-radius: 16px 16px 0 0;--bottom-nav-height: 64px;--bottom-nav-fab-size: 56px;--bottom-nav-max-width: var(--val-container-md, 720px);--fab-color: var(--ion-color-primary);pointer-events:auto;background:var(--bottom-nav-bg);border-radius:var(--bottom-nav-radius);height:var(--bottom-nav-height);padding:0 8px;max-width:var(--bottom-nav-max-width);margin:0 auto}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000014}.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.75);backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);border-top:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.06)}.bottom-nav--floating{margin:0 auto 8px;width:calc(100% - 32px);border-radius:32px;border:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.08);box-shadow:0 4px 24px #00000014,0 8px 32px #0000000a;--bottom-nav-radius: 32px}.bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.82);backdrop-filter:blur(28px) saturate(200%);-webkit-backdrop-filter:blur(28px) saturate(200%);border:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.08)}.bottom-nav--floating.bottom-nav--elevated{box-shadow:0 4px 24px #0000001a,0 12px 48px #00000014}.bottom-nav--floating.bottom-nav--safe-area{margin-bottom:calc(8px + env(safe-area-inset-bottom,0))}.bottom-nav--safe-area{padding-bottom:env(safe-area-inset-bottom,0);height:calc(var(--bottom-nav-height) + env(safe-area-inset-bottom,0))}.bottom-nav__container{display:flex;align-items:center;justify-content:space-around;height:var(--bottom-nav-height);max-width:500px;margin:0 auto}.bottom-nav__tab{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;height:100%;padding:8px 4px;background:transparent;border:none;cursor:pointer;position:relative;overflow:hidden;color:var(--bottom-nav-inactive);transition:color .2s ease;max-width:80px;--ripple-color: var(--bottom-nav-active)}.bottom-nav__tab:focus-visible{outline:2px solid var(--bottom-nav-active);outline-offset:-2px;border-radius:8px}.bottom-nav__tab--active{color:var(--bottom-nav-active)}.bottom-nav__tab--disabled{opacity:.4;cursor:not-allowed;pointer-events:none}.bottom-nav__tab[data-animation=scale] .bottom-nav__tab-icon{transition:transform .2s cubic-bezier(.4,0,.2,1)}.bottom-nav__tab[data-animation=scale].bottom-nav__tab--active .bottom-nav__tab-icon{transform:scale(1.15)}.bottom-nav__tab[data-animation=fade]{transition:opacity .2s ease,color .2s ease}.bottom-nav__tab[data-animation=fade]:not(.bottom-nav__tab--active){opacity:.6}.bottom-nav__tab[data-animation=slide] .bottom-nav__tab-label{transition:transform .2s ease,opacity .2s ease;transform:translateY(6px);opacity:0}.bottom-nav__tab[data-animation=slide].bottom-nav__tab--active .bottom-nav__tab-label{transform:translateY(0);opacity:1}.bottom-nav__tab-icon{position:relative;font-size:24px;line-height:1}.bottom-nav__tab-icon ion-icon{display:block}.bottom-nav__tab-label{font-size:11px;font-weight:500;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;letter-spacing:.01em}.bottom-nav__badge{position:absolute;top:-4px;right:-10px;min-width:18px;height:18px;padding:0 5px;font-size:10px;font-weight:600;line-height:18px;text-align:center;color:#fff;background-color:var(--ion-color-danger);border-radius:9px;box-shadow:0 1px 3px #0003}.bottom-nav__badge--dot{min-width:10px;width:10px;height:10px;padding:0;top:-2px;right:-4px;border-radius:50%}.bottom-nav__fab{position:relative;flex-shrink:0;width:var(--bottom-nav-fab-size);height:var(--bottom-nav-fab-size);margin:0 12px;margin-top:calc(var(--bottom-nav-fab-size) * -.35);background:var(--fab-color);border:none;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#fff;font-size:28px;box-shadow:0 4px 14px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.4);transition:transform .2s cubic-bezier(.4,0,.2,1),box-shadow .2s ease;overflow:hidden;--ripple-color: rgba(255, 255, 255, .3)}.bottom-nav__fab:hover{transform:scale(1.08);box-shadow:0 6px 20px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.5)}.bottom-nav__fab:active{transform:scale(.95)}.bottom-nav__fab:focus-visible{outline:3px solid white;outline-offset:2px}.bottom-nav__fab--small{--bottom-nav-fab-size: 48px;font-size:24px;margin-top:-14.4px}.bottom-nav__fab ion-icon{display:block}.bottom-nav--hide-labels{--bottom-nav-height: 56px}.bottom-nav--hide-labels .bottom-nav__tab-label{display:none}.bottom-nav--hide-labels .bottom-nav__tab-icon{font-size:26px}@media (prefers-color-scheme: dark){.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 0, 0, 0),.8);border-top-color:#ffffff14}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000040}.bottom-nav--floating{border-color:#ffffff1a;box-shadow:0 4px 24px #0003,0 8px 32px #00000026}.bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.88);border-color:#ffffff1a}.bottom-nav__fab{box-shadow:0 4px 14px #0006}.bottom-nav__fab:hover{box-shadow:0 6px 20px #00000080}}:host-context(.dark) .bottom-nav--translucent,:host-context(body.dark) .bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.85);border-top-color:#ffffff14}:host-context(.dark) .bottom-nav--elevated,:host-context(body.dark) .bottom-nav--elevated,:host-context([data-theme=dark]) .bottom-nav--elevated{box-shadow:0 -4px 20px #0000004d}:host-context(.dark) .bottom-nav--floating,:host-context(body.dark) .bottom-nav--floating,:host-context([data-theme=dark]) .bottom-nav--floating{border-color:#ffffff1a;box-shadow:0 4px 24px #0003,0 8px 32px #00000026}:host-context(.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context(body.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.88);border-color:#ffffff1a}:host-context(.dark) .bottom-nav__fab,:host-context(body.dark) .bottom-nav__fab,:host-context([data-theme=dark]) .bottom-nav__fab{box-shadow:0 4px 14px #00000080}:host-context(.dark) .bottom-nav__fab:hover,:host-context(body.dark) .bottom-nav__fab:hover,:host-context([data-theme=dark]) .bottom-nav__fab:hover{box-shadow:0 6px 20px #0009}@supports (padding-bottom: env(safe-area-inset-bottom)){.bottom-nav--safe-area .bottom-nav__container{height:var(--bottom-nav-height)}}\n"] }]
32851
32944
  }], propDecorators: { tabClick: [{
32852
32945
  type: Output
32853
32946
  }], fabClick: [{
@@ -35327,6 +35420,72 @@ function provideLegalContent(config) {
35327
35420
  return { provide: LEGAL_CONTENT_CONFIG, useValue: config };
35328
35421
  }
35329
35422
 
35423
+ const VALTECH_LEGAL_CONFIG = new InjectionToken('VALTECH_LEGAL_CONFIG');
35424
+ /**
35425
+ * Resolves legal/site paths against a configurable main-site base URL.
35426
+ *
35427
+ * - **Main site mode**: no `provideValtechLegal` called → `resolve('/legal/terms')` returns `/legal/terms` (relative).
35428
+ * - **Satellite mode**: `provideValtechLegal({ baseUrl: 'https://myvaltech.com' })` → `resolve('/legal/terms')` returns `'https://myvaltech.com/legal/terms'` and `isExternal('/legal/terms')` returns `true`.
35429
+ *
35430
+ * Absolute URLs passed in are returned unchanged (no double-prefix).
35431
+ */
35432
+ class LegalLinkService {
35433
+ constructor() {
35434
+ this.config = inject(VALTECH_LEGAL_CONFIG, { optional: true }) ?? {};
35435
+ }
35436
+ /** Effective base URL (null if running as main site). */
35437
+ get baseUrl() {
35438
+ return this.config.baseUrl?.replace(/\/$/, '') ?? null;
35439
+ }
35440
+ /** Whether resolved external links should open in a new tab. */
35441
+ get openInNewTab() {
35442
+ return this.config.openInNewTab ?? Boolean(this.config.baseUrl);
35443
+ }
35444
+ /**
35445
+ * Returns the URL to use for a given internal path. Absolute URLs pass through.
35446
+ * @example
35447
+ * resolve('/legal/terms') // main site: '/legal/terms'
35448
+ * // satellite: 'https://myvaltech.com/legal/terms'
35449
+ * resolve('https://x.com/y') // unchanged
35450
+ */
35451
+ resolve(path) {
35452
+ if (!path)
35453
+ return path;
35454
+ if (/^https?:\/\//i.test(path))
35455
+ return path;
35456
+ const base = this.baseUrl;
35457
+ if (!base)
35458
+ return path;
35459
+ return `${base}${path.startsWith('/') ? '' : '/'}${path}`;
35460
+ }
35461
+ /** `true` if the path would be resolved to a cross-origin URL. */
35462
+ isExternal(path) {
35463
+ if (/^https?:\/\//i.test(path))
35464
+ return true;
35465
+ return this.baseUrl !== null;
35466
+ }
35467
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LegalLinkService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
35468
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LegalLinkService, providedIn: 'root' }); }
35469
+ }
35470
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LegalLinkService, decorators: [{
35471
+ type: Injectable,
35472
+ args: [{ providedIn: 'root' }]
35473
+ }] });
35474
+ /**
35475
+ * Wires `LegalLinkService` for satellite apps. Omit in the main site (the one
35476
+ * that hosts the canonical /legal/* routes) so links stay relative.
35477
+ *
35478
+ * @example
35479
+ * // main.ts of showcase / sigify / etc.
35480
+ * provideValtechLegal({
35481
+ * baseUrl: 'https://myvaltech.com',
35482
+ * openInNewTab: true,
35483
+ * }),
35484
+ */
35485
+ function provideValtechLegal(config) {
35486
+ return { provide: VALTECH_LEGAL_CONFIG, useValue: config };
35487
+ }
35488
+
35330
35489
  /**
35331
35490
  * PreferencesService — preferencias del user en el doc canónico Firestore
35332
35491
  * `/apps/{appId}/users/{uid}/preferences/main`.
@@ -35488,6 +35647,263 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
35488
35647
  * Cliente NUNCA escribe directo — todas las mutaciones via PUT /v2/auth/preferences.
35489
35648
  */
35490
35649
 
35650
+ /**
35651
+ * DeviceRegistrationService — orquesta el flow combinado de:
35652
+ * - `PreferencesService.notificationsMaster` (flag user)
35653
+ * - `Notification.permission` del browser
35654
+ * - `MessagingService` (FCM: requestPermission, getToken, deleteToken)
35655
+ * - Backend `/v2/users/me/devices` (POST/GET/DELETE)
35656
+ *
35657
+ * Auto-sync:
35658
+ * - master=true & permission=granted & sin token → auto-register (silent, sin prompt).
35659
+ * - master=false & device registrado → auto-unregister (FCM deleteToken + DELETE backend).
35660
+ * - permission=denied → status='error', requiere acción manual del user.
35661
+ *
35662
+ * Cliente:
35663
+ * - `requestAndRegister()` para CTA "permitir notificaciones" (dispara browser prompt).
35664
+ * - `unregister()` para "olvidar este dispositivo".
35665
+ * - `refreshPermission()` tras volver al tab (focus) — el browser puede haber cambiado el estado.
35666
+ */
35667
+ class DeviceRegistrationService {
35668
+ constructor(config, platformId, http, messaging, prefs, auth) {
35669
+ this.config = config;
35670
+ this.platformId = platformId;
35671
+ this.http = http;
35672
+ this.messaging = messaging;
35673
+ this.prefs = prefs;
35674
+ this.auth = auth;
35675
+ this._permission = signal('default');
35676
+ this._status = signal('idle');
35677
+ this._currentToken = signal(null);
35678
+ this._currentDeviceId = signal(null);
35679
+ this._lastError = signal(null);
35680
+ this._isSupported = signal(false);
35681
+ this.permission = this._permission.asReadonly();
35682
+ this.status = this._status.asReadonly();
35683
+ this.currentToken = this._currentToken.asReadonly();
35684
+ this.currentDeviceId = this._currentDeviceId.asReadonly();
35685
+ this.lastError = this._lastError.asReadonly();
35686
+ this.isSupported = this._isSupported.asReadonly();
35687
+ /** true si el browser puede recibir push y el user ya autorizó */
35688
+ this.canReceive = computed(() => this._isSupported() && this._permission() === 'granted' && this._status() === 'registered');
35689
+ this.autoSyncInFlight = false;
35690
+ if (!isPlatformBrowser(this.platformId))
35691
+ return;
35692
+ this._isSupported.set(this.detectSupport());
35693
+ this.refreshPermission();
35694
+ // Refrescar permission al volver al tab — el user pudo cambiarla en browser settings.
35695
+ window.addEventListener('focus', () => this.refreshPermission());
35696
+ const destroyRef = inject(DestroyRef);
35697
+ destroyRef.onDestroy(() => {
35698
+ window.removeEventListener('focus', () => this.refreshPermission());
35699
+ });
35700
+ // Auto-sync: reaccionar a cambios de master / permission / user.
35701
+ effect(() => {
35702
+ const user = this.auth.user();
35703
+ const master = this.prefs.notificationsMaster();
35704
+ const perm = this._permission();
35705
+ const hasDevice = !!this._currentDeviceId();
35706
+ if (!user)
35707
+ return;
35708
+ if (!this.prefs.synced())
35709
+ return;
35710
+ if (this.autoSyncInFlight)
35711
+ return;
35712
+ // Master OFF + device registrado → unregister.
35713
+ if (!master && hasDevice) {
35714
+ void this.unregister();
35715
+ return;
35716
+ }
35717
+ // Master ON + granted + sin device → register silent (no prompt, ya granted).
35718
+ if (master && perm === 'granted' && !hasDevice && this._status() === 'idle') {
35719
+ void this.registerSilent();
35720
+ }
35721
+ });
35722
+ }
35723
+ /**
35724
+ * CTA explícito del user: dispara `Notification.requestPermission()` + registro.
35725
+ * Único método que puede generar el browser prompt.
35726
+ */
35727
+ async requestAndRegister() {
35728
+ if (!this._isSupported()) {
35729
+ this._lastError.set('not-supported');
35730
+ this._status.set('error');
35731
+ return;
35732
+ }
35733
+ this.autoSyncInFlight = true;
35734
+ this._status.set('registering');
35735
+ this._lastError.set(null);
35736
+ try {
35737
+ const token = await this.messaging.requestPermission();
35738
+ this.refreshPermission();
35739
+ if (!token) {
35740
+ this._status.set('error');
35741
+ this._lastError.set(this._permission() === 'denied' ? 'permission-denied' : 'no-token');
35742
+ return;
35743
+ }
35744
+ await this.registerDeviceBackend(token);
35745
+ }
35746
+ catch (err) {
35747
+ this._status.set('error');
35748
+ this._lastError.set(this.errorMessage(err));
35749
+ }
35750
+ finally {
35751
+ this.autoSyncInFlight = false;
35752
+ }
35753
+ }
35754
+ /**
35755
+ * Unregister: borra token FCM + DELETE backend del device actual.
35756
+ * Idempotente: si no hay device, no hace nada.
35757
+ */
35758
+ async unregister() {
35759
+ const deviceId = this._currentDeviceId();
35760
+ if (!deviceId && !this._currentToken()) {
35761
+ this._status.set('idle');
35762
+ return;
35763
+ }
35764
+ this.autoSyncInFlight = true;
35765
+ this._status.set('unregistering');
35766
+ this._lastError.set(null);
35767
+ try {
35768
+ if (deviceId) {
35769
+ await firstValueFrom(this.http.delete(`${this.config.apiUrl}/v2/users/me/devices/${deviceId}`));
35770
+ }
35771
+ try {
35772
+ await this.messaging.deleteToken();
35773
+ }
35774
+ catch {
35775
+ // deleteToken puede fallar si el token ya no existe; aceptable.
35776
+ }
35777
+ this._currentToken.set(null);
35778
+ this._currentDeviceId.set(null);
35779
+ this._status.set('idle');
35780
+ }
35781
+ catch (err) {
35782
+ this._status.set('error');
35783
+ this._lastError.set(this.errorMessage(err));
35784
+ }
35785
+ finally {
35786
+ this.autoSyncInFlight = false;
35787
+ }
35788
+ }
35789
+ /** Refresca `permission` desde `Notification.permission` del browser. */
35790
+ refreshPermission() {
35791
+ if (!isPlatformBrowser(this.platformId) || !('Notification' in window)) {
35792
+ this._permission.set('default');
35793
+ return;
35794
+ }
35795
+ this._permission.set(Notification.permission);
35796
+ }
35797
+ /** Lista devices del user actual desde backend. Útil para vista Seguridad. */
35798
+ async listDevices() {
35799
+ const res = await firstValueFrom(this.http.get(`${this.config.apiUrl}/v2/users/me/devices`));
35800
+ return res.devices;
35801
+ }
35802
+ /** Reintenta el último flow fallido. */
35803
+ async retry() {
35804
+ if (this._permission() === 'granted') {
35805
+ await this.registerSilent();
35806
+ }
35807
+ else {
35808
+ await this.requestAndRegister();
35809
+ }
35810
+ }
35811
+ // ---------------------------------------------------------------------------
35812
+ // Privados
35813
+ // ---------------------------------------------------------------------------
35814
+ /** Register sin prompt — asume permission ya granted. */
35815
+ async registerSilent() {
35816
+ this.autoSyncInFlight = true;
35817
+ this._status.set('registering');
35818
+ this._lastError.set(null);
35819
+ try {
35820
+ const token = await this.messaging.getToken();
35821
+ if (!token) {
35822
+ this._status.set('error');
35823
+ this._lastError.set('no-token');
35824
+ return;
35825
+ }
35826
+ await this.registerDeviceBackend(token);
35827
+ }
35828
+ catch (err) {
35829
+ this._status.set('error');
35830
+ this._lastError.set(this.errorMessage(err));
35831
+ }
35832
+ finally {
35833
+ this.autoSyncInFlight = false;
35834
+ }
35835
+ }
35836
+ async registerDeviceBackend(token) {
35837
+ const env = this.detectEnv();
35838
+ const body = {
35839
+ token,
35840
+ platform: 'web',
35841
+ browser: env.browser,
35842
+ os: env.os,
35843
+ appVersion: env.appVersion,
35844
+ };
35845
+ const res = await firstValueFrom(this.http.post(`${this.config.apiUrl}/v2/users/me/devices`, body));
35846
+ this._currentToken.set(token);
35847
+ this._currentDeviceId.set(res.device.deviceId);
35848
+ this._status.set('registered');
35849
+ }
35850
+ detectSupport() {
35851
+ if (!isPlatformBrowser(this.platformId))
35852
+ return false;
35853
+ return 'Notification' in window && 'serviceWorker' in navigator;
35854
+ }
35855
+ detectEnv() {
35856
+ const ua = navigator.userAgent;
35857
+ let browser = 'Unknown';
35858
+ if (/edg/i.test(ua))
35859
+ browser = 'Edge';
35860
+ else if (/chrome|chromium|crios/i.test(ua))
35861
+ browser = 'Chrome';
35862
+ else if (/firefox|fxios/i.test(ua))
35863
+ browser = 'Firefox';
35864
+ else if (/safari/i.test(ua))
35865
+ browser = 'Safari';
35866
+ let os = 'Unknown';
35867
+ if (/Windows/i.test(ua))
35868
+ os = 'Windows';
35869
+ else if (/Android/i.test(ua))
35870
+ os = 'Android';
35871
+ else if (/iPhone|iPad|iPod/i.test(ua))
35872
+ os = 'iOS';
35873
+ else if (/Mac OS X/i.test(ua))
35874
+ os = 'macOS';
35875
+ else if (/Linux/i.test(ua))
35876
+ os = 'Linux';
35877
+ return { browser, os, appVersion: 'web-1.0' };
35878
+ }
35879
+ errorMessage(err) {
35880
+ if (err instanceof Error)
35881
+ return err.message;
35882
+ if (typeof err === 'string')
35883
+ return err;
35884
+ return 'unknown-error';
35885
+ }
35886
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DeviceRegistrationService, deps: [{ token: VALTECH_AUTH_CONFIG }, { token: PLATFORM_ID }, { token: i1$8.HttpClient }, { token: MessagingService }, { token: PreferencesService }, { token: AuthService }], target: i0.ɵɵFactoryTarget.Injectable }); }
35887
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DeviceRegistrationService, providedIn: 'root' }); }
35888
+ }
35889
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DeviceRegistrationService, decorators: [{
35890
+ type: Injectable,
35891
+ args: [{ providedIn: 'root' }]
35892
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
35893
+ type: Inject,
35894
+ args: [VALTECH_AUTH_CONFIG]
35895
+ }] }, { type: Object, decorators: [{
35896
+ type: Inject,
35897
+ args: [PLATFORM_ID]
35898
+ }] }, { type: i1$8.HttpClient }, { type: MessagingService }, { type: PreferencesService }, { type: AuthService }] });
35899
+
35900
+ /**
35901
+ * Devices types específicos del `DeviceRegistrationService`.
35902
+ * Los contratos base (`RegisterDeviceRequest`, `RegisterDeviceResponse`,
35903
+ * `ListDevicesResponse`, `DeviceInfo`, `DevicePlatform`) viven en `auth/types.ts`
35904
+ * — este archivo solo agrega lo que falta.
35905
+ */
35906
+
35491
35907
  /**
35492
35908
  * Cross-Platform Version Helpers
35493
35909
  *
@@ -42527,11 +42943,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
42527
42943
  * Social media links for Valtech
42528
42944
  */
42529
42945
  const VALTECH_SOCIAL_LINKS = [
42530
- { icon: 'logo-facebook', url: 'https://m.facebook.com/profile.php?id=61557610734470', name: 'Facebook' },
42946
+ {
42947
+ icon: 'logo-facebook',
42948
+ url: 'https://m.facebook.com/profile.php?id=61557610734470',
42949
+ name: 'Facebook',
42950
+ },
42531
42951
  { icon: 'logo-instagram', url: 'https://www.instagram.com/valtechltda/', name: 'Instagram' },
42532
42952
  { icon: 'logo-linkedin', url: 'https://www.linkedin.com/company/valtechltda/', name: 'LinkedIn' },
42533
42953
  { icon: 'logo-twitter', url: 'https://twitter.com/valtechltda', name: 'Twitter' },
42534
- { icon: 'logo-youtube', url: 'https://www.youtube.com/channel/UCuF4FGdTiUXxANx1HS4Wi5Q', name: 'YouTube' },
42954
+ {
42955
+ icon: 'logo-youtube',
42956
+ url: 'https://www.youtube.com/channel/UCuF4FGdTiUXxANx1HS4Wi5Q',
42957
+ name: 'YouTube',
42958
+ },
42535
42959
  { icon: 'logo-tiktok', url: 'https://www.tiktok.com/@valtechltda', name: 'TikTok' },
42536
42960
  ];
42537
42961
  /**
@@ -42548,14 +42972,14 @@ const VALTECH_FOOTER_LOGO = {
42548
42972
  */
42549
42973
  const VALTECH_COMPANY_LINKS = {
42550
42974
  legal: [
42551
- { key: 'aboutUs', url: '/about', external: false },
42552
- { key: 'privacyPolicy', url: '/legal/privacy', external: false },
42553
- { key: 'termsConditions', url: '/legal/terms', external: false },
42975
+ { key: 'aboutUs', url: '/about', kind: 'site', external: false },
42976
+ { key: 'privacyPolicy', url: '/legal/privacy', kind: 'legal', external: false },
42977
+ { key: 'termsConditions', url: '/legal/terms', kind: 'legal', external: false },
42554
42978
  ],
42555
42979
  support: [
42556
- { key: 'contactSupport', url: '/contact', external: false },
42557
- { key: 'faq', url: '/help/faq', external: false },
42558
- { key: 'feedback', url: '/feedback', external: false },
42980
+ { key: 'contactSupport', url: '/contact', kind: 'support', external: false },
42981
+ { key: 'faq', url: '/help/faq', kind: 'support', external: false },
42982
+ { key: 'feedback', url: '/feedback', kind: 'support', external: false },
42559
42983
  ],
42560
42984
  };
42561
42985
  /**
@@ -42605,21 +43029,27 @@ const VALTECH_LANGUAGE_SELECTOR = {
42605
43029
  size: 'default',
42606
43030
  };
42607
43031
  /**
42608
- * Helper to build footer links from company links config
43032
+ * Helper to build footer links from company links config.
43033
+ *
42609
43034
  * @param links - Array of company links
42610
43035
  * @param t - Translation function (key: string) => string
43036
+ * @param resolver - Optional resolver — typically `(link) => ({ url: legalLink.resolve(link.url), external: legalLink.isExternal(link.url) && (link.kind === 'legal' || link.kind === 'site') })` to point cross-app legal/site links to the main site.
42611
43037
  * @returns Array of link objects ready for FooterLinksMetadata
42612
43038
  */
42613
- function buildFooterLinks(links, t) {
42614
- return links.map(link => ({
42615
- url: link.url,
42616
- text: t(link.key),
42617
- color: 'dark',
42618
- token: '',
42619
- download: false,
42620
- hoverable: true,
42621
- ...(link.external ? { target: '_blank' } : {}),
42622
- }));
43039
+ function buildFooterLinks(links, t, resolver) {
43040
+ return links.map(link => {
43041
+ const resolved = resolver ? resolver(link) : { url: link.url, external: link.external };
43042
+ const external = resolved.external ?? link.external;
43043
+ return {
43044
+ url: resolved.url,
43045
+ text: t(link.key),
43046
+ color: 'dark',
43047
+ token: '',
43048
+ download: false,
43049
+ hoverable: true,
43050
+ ...(external ? { target: '_blank' } : {}),
43051
+ };
43052
+ });
42623
43053
  }
42624
43054
 
42625
43055
  /**
@@ -42635,5 +43065,5 @@ function buildFooterLinks(links, t) {
42635
43065
  * Generated bundle index. Do not edit.
42636
43066
  */
42637
43067
 
42638
- 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, BlogPostBuilder, BottomNavComponent, BoxComponent, BreadcrumbComponent, ButtonComponent, ButtonGroupComponent, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CURRENCY_INFO, CardComponent, CardSection, CardType, CardsCarouselComponent, CheckInputComponent, CheckboxRadioInputComponent, ChipGroupComponent, ClearDefault, ClearDefaultBlock, ClearDefaultFull, ClearDefaultRound, ClearDefaultRoundBlock, ClearDefaultRoundFull, CodeDisplayComponent, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ContainerComponent, ContentLoaderComponent, ContentReactionComponent, ContentTransformer, 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_MODAL_CANCEL_BUTTON, DEFAULT_MODAL_CONFIRM_BUTTON, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLATFORMS, DEFAULT_REFRESHER_METADATA, DEFAULT_SKELETON_CONFIG, DataTableComponent, DateInputComponent, DateRangeInputComponent, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsBuilder, 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, HANDOFF_ROUTE_PARAM, HANDOFF_TOKEN_PARAM, HandoffService, 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, LEGAL_CONTENT_CONFIG, LOGIN_DEFAULTS, LanguageSelectorComponent, LayeredCardComponent, LegalContentService, LinkComponent, LinkProcessorService, LinkedProvidersComponent, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LoadingDirective, LocalStorageService, LocaleService, LoginComponent, MODAL_SIZES, MOTION, MaintenancePageComponent, MarkdownArticleParserService, MenuComponent, MessagingService, MetaService, ModalService, MultiSelectSearchComponent, NavigationService, NewsBuilder, NoContentComponent, NotesBoxComponent, NotificationActionService, NotificationsService, NumberFromToComponent, NumberInputComponent, NumberStepperComponent, OAUTH_PROVIDERS_INFO, OAuthCallbackComponent, OAuthService, OrgSwitchService, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PLATFORM_CONFIGS, PageContentComponent, PageTemplateComponent, PageWrapperComponent, PaginationComponent, PaginationService, PasswordInputComponent, PhoneInputComponent, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PreferencesService, 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, RangeInputComponent, RatingComponent, 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, 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, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, authGuard, authInterceptor, blogPost, buildFooterLinks, buildPath, collections, createFirebaseConfig, createGlowCardProps, createInitialPaginationState, createNumberFromToField, createTitleProps, docs, extractPathParams, getAppInfo, getAppVersion, getCollectionPath, getDocumentId, getTimeOfDayKey, goToTop, guestGuard, hasEmulators, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isValidPath, joinPath, maxLength, news, parseMarkdownArticle, permissionGuard, permissionGuardFromRoute, provideLegalContent, provideValtechAds, provideValtechAppConfig, provideValtechAuth, provideValtechAuthInterceptor, provideValtechFeedback, provideValtechFirebase, provideValtechI18n, provideValtechPresets, provideValtechSkeleton, query, replaceSpecialChars, resolveColor, resolveInputDefaultValue, roleGuard, storagePaths, superAdminGuard, toArticle };
43068
+ 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, BlogPostBuilder, BottomNavComponent, BoxComponent, BreadcrumbComponent, ButtonComponent, ButtonGroupComponent, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CURRENCY_INFO, CardComponent, CardSection, CardType, CardsCarouselComponent, CheckInputComponent, CheckboxRadioInputComponent, ChipGroupComponent, ClearDefault, ClearDefaultBlock, ClearDefaultFull, ClearDefaultRound, ClearDefaultRoundBlock, ClearDefaultRoundFull, CodeDisplayComponent, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ContainerComponent, ContentLoaderComponent, ContentReactionComponent, ContentTransformer, 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_MODAL_CANCEL_BUTTON, DEFAULT_MODAL_CONFIRM_BUTTON, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLATFORMS, DEFAULT_REFRESHER_METADATA, DEFAULT_SKELETON_CONFIG, DataTableComponent, DateInputComponent, DateRangeInputComponent, DetailSkeletonComponent, DeviceRegistrationService, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsBuilder, 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, GlowComponent, GridSkeletonComponent, HANDOFF_ROUTE_PARAM, HANDOFF_TOKEN_PARAM, HandoffService, 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, LEGAL_CONTENT_CONFIG, LOGIN_DEFAULTS, LanguageSelectorComponent, LayeredCardComponent, LegalContentService, LegalLinkService, LinkComponent, LinkProcessorService, LinkedProvidersComponent, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LoadingDirective, LocalStorageService, LocaleService, LoginComponent, MODAL_SIZES, MOTION, MaintenancePageComponent, MarkdownArticleParserService, MenuComponent, MessagingService, MetaService, ModalService, MultiSelectSearchComponent, NavigationService, NewsBuilder, NoContentComponent, NotesBoxComponent, NotificationActionService, NotificationsService, NumberFromToComponent, NumberInputComponent, NumberStepperComponent, OAUTH_PROVIDERS_INFO, OAuthCallbackComponent, OAuthService, OrgSwitchService, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PLATFORM_CONFIGS, PageContentComponent, PageTemplateComponent, PageWrapperComponent, PaginationComponent, PaginationService, PasswordInputComponent, PhoneInputComponent, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PreferencesService, 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, RangeInputComponent, RatingComponent, 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, 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_LEGAL_CONFIG, VALTECH_SOCIAL_LINKS, VERSION, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, authGuard, authInterceptor, blogPost, buildFooterLinks, buildPath, collections, createFirebaseConfig, createGlowCardProps, createInitialPaginationState, createNumberFromToField, createTitleProps, docs, extractPathParams, getAppInfo, getAppVersion, getCollectionPath, getDocumentId, getTimeOfDayKey, goToTop, guestGuard, hasEmulators, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isValidPath, joinPath, maxLength, news, parseMarkdownArticle, permissionGuard, permissionGuardFromRoute, provideLegalContent, provideValtechAds, provideValtechAppConfig, provideValtechAuth, provideValtechAuthInterceptor, provideValtechFeedback, provideValtechFirebase, provideValtechI18n, provideValtechLegal, provideValtechPresets, provideValtechSkeleton, query, replaceSpecialChars, resolveColor, resolveInputDefaultValue, roleGuard, storagePaths, superAdminGuard, toArticle };
42639
43069
  //# sourceMappingURL=valtech-components.mjs.map