valtech-components 2.0.948 → 2.0.950

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.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, InjectionToken, makeEnvironmentProviders, APP_INITIALIZER, ErrorHandler, Injectable, DestroyRef, Inject, signal, computed, PLATFORM_ID, isDevMode, Optional, runInInjectionContext, effect, Pipe, TemplateRef, ViewContainerRef, input, Directive, EventEmitter, Component, Input, Output, ChangeDetectionStrategy, HostBinding, HostListener, NgZone, ViewChild, ChangeDetectorRef, ContentChild, output, Injector, isSignal, ElementRef, ViewEncapsulation } from '@angular/core';
2
+ import { inject, InjectionToken, makeEnvironmentProviders, APP_INITIALIZER, ErrorHandler, Injectable, DestroyRef, Inject, signal, computed, PLATFORM_ID, isDevMode, Optional, runInInjectionContext, effect, Pipe, TemplateRef, ViewContainerRef, input, Directive, EventEmitter, Component, Input, Output, ChangeDetectionStrategy, HostBinding, HostListener, NgZone, ViewChild, ChangeDetectorRef, output, ContentChild, ElementRef, Injector, isSignal, ViewEncapsulation } from '@angular/core';
3
3
  import { BehaviorSubject, throwError, Subject, map, distinctUntilChanged, filter as filter$1, take as take$1, firstValueFrom, of, from, EMPTY, Observable, interval, debounceTime, switchMap as switchMap$1, catchError as catchError$1, takeUntil, isObservable, shareReplay, race, timer, forkJoin } from 'rxjs';
4
4
  import { catchError, switchMap, finalize, filter, take, map as map$1, tap, retry, timeout, debounceTime as debounceTime$1, takeUntil as takeUntil$1 } from 'rxjs/operators';
5
5
  import * as i1$3 from '@angular/common/http';
@@ -54,7 +54,7 @@ import 'prismjs/components/prism-json';
54
54
  * Current version of valtech-components.
55
55
  * This is automatically updated during the publish process.
56
56
  */
57
- const VERSION = '2.0.948';
57
+ const VERSION = '2.0.950';
58
58
 
59
59
  // Control de estado de refresco (singleton a nivel de módulo)
60
60
  let isRefreshing = false;
@@ -25120,91 +25120,126 @@ const FEATURES_LIST_DEFAULTS = {
25120
25120
  mode: 'vertical',
25121
25121
  gap: 'medium',
25122
25122
  alignment: 'start',
25123
+ imageShape: 'square',
25124
+ maxVisible: 0,
25123
25125
  };
25124
25126
 
25127
+ /**
25128
+ * Defaults i18n (es/en) — auto-registrados si el consumer no proveyó el
25129
+ * namespace `FeaturesList`. Cubre los labels de "ver más"/"ver menos".
25130
+ */
25131
+ const FEATURES_LIST_I18N = {
25132
+ es: { showMore: 'Ver más', showLess: 'Ver menos' },
25133
+ en: { showMore: 'Show more', showLess: 'Show less' },
25134
+ };
25125
25135
  /**
25126
25136
  * val-features-list
25127
25137
  *
25128
- * A component to display a list of features with icons, titles, and descriptions.
25129
- * Supports i18n for all text content.
25138
+ * Lista de features con icono **o imagen**, título y descripción. Soporta i18n,
25139
+ * colapso opcional ("ver más" cuando supera `maxVisible`) y un link al pie
25140
+ * (ej. "Find out more"). Útil para listas de settings, apps, beneficios.
25130
25141
  *
25131
25142
  * @example
25132
25143
  * ```html
25133
25144
  * <val-features-list
25134
25145
  * [props]="{
25135
25146
  * features: [
25136
- * { icon: 'flash-outline', titleKey: 'feature1Title', descriptionKey: 'feature1Desc' },
25137
- * { icon: 'color-palette-outline', titleKey: 'feature2Title', descriptionKey: 'feature2Desc' }
25147
+ * { icon: 'flash-outline', titleKey: 'Rápido', descriptionKey: 'Carga al instante' },
25148
+ * { image: '/logos/app.png', titleKey: 'Mi App', descriptionKey: 'Gestión de equipos', token: 'app' }
25138
25149
  * ],
25139
- * i18nNamespace: 'Features',
25140
- * iconColor: 'primary',
25141
- * mode: 'vertical'
25142
- * }"
25143
- * />
25144
- * ```
25145
- *
25146
- * @example With custom SVG icons
25147
- * ```html
25148
- * <val-features-list
25149
- * [props]="{
25150
- * features: [
25151
- * {
25152
- * icon: 'custom',
25153
- * svgPath: 'M13 2L3 14h9l-1 8 10-12h-9l1-8z',
25154
- * titleKey: 'Fast & Secure',
25155
- * descriptionKey: 'Modern authentication with OAuth and MFA'
25156
- * }
25157
- * ]
25150
+ * maxVisible: 3,
25151
+ * footerLink: { label: 'Ver todo', routerLink: '/apps' }
25158
25152
  * }"
25153
+ * (itemClick)="open($event)"
25159
25154
  * />
25160
25155
  * ```
25161
25156
  */
25162
25157
  class FeaturesListComponent {
25163
25158
  constructor() {
25164
25159
  this.i18n = inject(I18nService);
25160
+ this.navigation = inject(NavigationService);
25165
25161
  /** Component configuration */
25166
25162
  this.props = input.required();
25163
+ /** Emitido al tocar un item (con su `token`). */
25164
+ this.itemClick = output();
25165
+ /** Emitido al tocar el footer link (con su `token`). */
25166
+ this.footerClick = output();
25167
+ this._expanded = signal(false);
25168
+ this.expanded = this._expanded.asReadonly();
25167
25169
  /** Merged configuration with defaults */
25168
25170
  this.config = computed(() => ({
25169
25171
  ...FEATURES_LIST_DEFAULTS,
25170
25172
  ...this.props(),
25171
25173
  }));
25174
+ /** Items visibles según el estado de colapso. */
25175
+ this.visibleFeatures = computed(() => {
25176
+ const cfg = this.config();
25177
+ const all = cfg.features ?? [];
25178
+ if (cfg.maxVisible > 0 && !this._expanded() && all.length > cfg.maxVisible) {
25179
+ return all.slice(0, cfg.maxVisible);
25180
+ }
25181
+ return all;
25182
+ });
25183
+ /** Si hay items de sobra para colapsar/expandir. */
25184
+ this.canCollapse = computed(() => {
25185
+ const cfg = this.config();
25186
+ return cfg.maxVisible > 0 && (cfg.features?.length ?? 0) > cfg.maxVisible;
25187
+ });
25188
+ this.showMoreText = computed(() => {
25189
+ this.i18n.lang();
25190
+ return this.config().showMoreLabel || this.i18n.t('showMore', 'FeaturesList');
25191
+ });
25192
+ this.showLessText = computed(() => {
25193
+ this.i18n.lang();
25194
+ return this.config().showLessLabel || this.i18n.t('showLess', 'FeaturesList');
25195
+ });
25172
25196
  /** Resolved icon color (handles Ionic colors and CSS colors) */
25173
25197
  this.iconColorStyle = computed(() => {
25174
25198
  const color = this.config().iconColor;
25175
- // Check if it's an Ionic color variable
25176
25199
  if (color && !color.startsWith('#') && !color.startsWith('rgb')) {
25177
25200
  return `var(--ion-color-${color})`;
25178
25201
  }
25179
25202
  return color;
25180
25203
  });
25204
+ if (!this.i18n.hasNamespace('FeaturesList')) {
25205
+ this.i18n.registerContent('FeaturesList', FEATURES_LIST_I18N);
25206
+ }
25181
25207
  }
25182
- /**
25183
- * Get translated title or return direct text
25184
- */
25208
+ toggle() {
25209
+ this._expanded.set(!this._expanded());
25210
+ }
25211
+ onItem(feature) {
25212
+ if (feature.token != null) {
25213
+ this.itemClick.emit(feature.token);
25214
+ }
25215
+ }
25216
+ onFooter(event, link) {
25217
+ this.footerClick.emit(link.token);
25218
+ if (link.href) {
25219
+ event.preventDefault();
25220
+ this.navigation.openInNewTab(link.href);
25221
+ }
25222
+ }
25223
+ /** Get translated title or return direct text */
25185
25224
  getTitle(feature) {
25186
25225
  const namespace = this.props().i18nNamespace;
25187
25226
  if (namespace) {
25188
- // Track language changes
25189
25227
  this.i18n.lang();
25190
25228
  return this.i18n.t(feature.titleKey, namespace);
25191
25229
  }
25192
25230
  return feature.titleKey;
25193
25231
  }
25194
- /**
25195
- * Get translated description or return direct text
25196
- */
25232
+ /** Get translated description or return direct text */
25197
25233
  getDescription(feature) {
25198
25234
  const namespace = this.props().i18nNamespace;
25199
25235
  if (namespace) {
25200
- // Track language changes
25201
25236
  this.i18n.lang();
25202
25237
  return this.i18n.t(feature.descriptionKey, namespace);
25203
25238
  }
25204
25239
  return feature.descriptionKey;
25205
25240
  }
25206
25241
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FeaturesListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
25207
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: FeaturesListComponent, isStandalone: true, selector: "val-features-list", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
25242
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: FeaturesListComponent, isStandalone: true, selector: "val-features-list", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { itemClick: "itemClick", footerClick: "footerClick" }, ngImport: i0, template: `
25208
25243
  <div
25209
25244
  class="features-list"
25210
25245
  [class.features-list--vertical]="config().mode === 'vertical'"
@@ -25214,15 +25249,22 @@ class FeaturesListComponent {
25214
25249
  [class.features-list--gap-large]="config().gap === 'large'"
25215
25250
  [class.features-list--center]="config().alignment === 'center'"
25216
25251
  >
25217
- @for (feature of props().features; track feature.titleKey) {
25218
- <div class="feature">
25252
+ @for (feature of visibleFeatures(); track feature.titleKey) {
25253
+ <div class="feature" [class.feature--clickable]="feature.token != null" (click)="onItem(feature)">
25219
25254
  <div
25220
25255
  class="feature__icon"
25221
25256
  [style.color]="iconColorStyle()"
25222
25257
  [style.width.px]="config().iconSize"
25223
25258
  [style.height.px]="config().iconSize"
25224
25259
  >
25225
- @if (feature.icon === 'custom' && feature.svgPath) {
25260
+ @if (feature.image) {
25261
+ <img
25262
+ class="feature__image"
25263
+ [class.feature__image--circle]="config().imageShape === 'circle'"
25264
+ [src]="feature.image"
25265
+ [alt]="getTitle(feature)"
25266
+ />
25267
+ } @else if (feature.icon === 'custom' && feature.svgPath) {
25226
25268
  <svg
25227
25269
  [attr.width]="config().iconSize"
25228
25270
  [attr.height]="config().iconSize"
@@ -25235,7 +25277,7 @@ class FeaturesListComponent {
25235
25277
  >
25236
25278
  <path [attr.d]="feature.svgPath" />
25237
25279
  </svg>
25238
- } @else {
25280
+ } @else if (feature.icon) {
25239
25281
  <ion-icon [name]="feature.icon" [style.font-size.px]="config().iconSize"></ion-icon>
25240
25282
  }
25241
25283
  </div>
@@ -25245,12 +25287,24 @@ class FeaturesListComponent {
25245
25287
  </div>
25246
25288
  </div>
25247
25289
  }
25290
+
25291
+ @if (canCollapse()) {
25292
+ <button type="button" class="features-list__more" (click)="toggle()">
25293
+ {{ expanded() ? showLessText() : showMoreText() }}
25294
+ </button>
25295
+ }
25296
+
25297
+ @if (config().footerLink; as link) {
25298
+ <a class="features-list__footer" [routerLink]="link.routerLink ?? null" (click)="onFooter($event, link)">
25299
+ {{ link.label }}
25300
+ </a>
25301
+ }
25248
25302
  </div>
25249
- `, isInline: true, styles: [":host{display:block}.features-list{display:flex}.features-list--vertical{flex-direction:column}.features-list--horizontal{flex-direction:row;flex-wrap:wrap}.features-list--horizontal .feature{flex:1;min-width:200px}.features-list--gap-small{gap:1rem}.features-list--gap-medium{gap:1.5rem}.features-list--gap-large{gap:2rem}.features-list--center{align-items:center;text-align:center}.features-list--center .feature{flex-direction:column;align-items:center}.features-list--center .feature__content{text-align:center}.feature{display:flex;align-items:flex-start;gap:1rem}.feature__icon{flex-shrink:0;display:flex;align-items:center;justify-content:center}.feature__content{flex:1}.feature__title{display:block;font-size:1.1rem;font-weight:600;color:var(--ion-text-color);margin-bottom:.25rem}.feature__description{margin:0;font-size:.95rem;color:var(--ion-color-medium);line-height:1.5}:host-context(.dark) .feature__title,:host-context(.ion-palette-dark) .feature__title,:host-context(html.ion-palette-dark) .feature__title,:host-context(body.dark) .feature__title,:host-context([data-theme=\"dark\"]) .feature__title{color:var(--ion-text-color, #fff)}:host-context(.dark) .feature__description,:host-context(.ion-palette-dark) .feature__description,:host-context(html.ion-palette-dark) .feature__description,:host-context(body.dark) .feature__description,:host-context([data-theme=\"dark\"]) .feature__description{color:var(--ion-color-medium, #a0a0a0)}:host-context(.dark) .feature__icon,:host-context(.ion-palette-dark) .feature__icon,:host-context(html.ion-palette-dark) .feature__icon,:host-context(body.dark) .feature__icon,:host-context([data-theme=\"dark\"]) .feature__icon{opacity:.9}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }] }); }
25303
+ `, isInline: true, styles: [":host{display:block}.features-list{display:flex}.features-list--vertical{flex-direction:column}.features-list--horizontal{flex-direction:row;flex-wrap:wrap}.features-list--horizontal .feature{flex:1;min-width:200px}.features-list--gap-small{gap:1rem}.features-list--gap-medium{gap:1.5rem}.features-list--gap-large{gap:2rem}.features-list--center{align-items:center;text-align:center}.features-list--center .feature{flex-direction:column;align-items:center}.features-list--center .feature__content{text-align:center}.feature{display:flex;align-items:flex-start;gap:1rem}.feature--clickable{cursor:pointer;border-radius:10px;transition:background-color .15s ease}.feature--clickable:hover{background:var(--ion-color-light, rgba(0, 0, 0, .03))}.feature__icon{flex-shrink:0;display:flex;align-items:center;justify-content:center}.feature__image{width:100%;height:100%;object-fit:contain;border-radius:8px}.feature__image--circle{object-fit:cover;border-radius:50%}.feature__content{flex:1}.feature__title{display:block;font-size:1.1rem;font-weight:600;color:var(--ion-text-color);margin-bottom:.25rem}.feature__description{margin:0;font-size:.95rem;color:var(--ion-color-medium);line-height:1.5}.features-list__more{align-self:flex-start;margin-top:.25rem;padding:0;background:none;border:none;font:inherit;font-weight:600;color:var(--ion-color-primary);cursor:pointer}.features-list--center .features-list__more,.features-list--center .features-list__footer{align-self:center}.features-list__footer{align-self:flex-start;margin-top:.25rem;font-weight:600;color:var(--ion-color-primary);text-decoration:none;cursor:pointer}.features-list__footer:hover{text-decoration:underline}:host-context(.dark) .feature__title,:host-context(.ion-palette-dark) .feature__title,:host-context(html.ion-palette-dark) .feature__title,:host-context(body.dark) .feature__title,:host-context([data-theme=\"dark\"]) .feature__title{color:var(--ion-text-color, #fff)}:host-context(.dark) .feature__description,:host-context(.ion-palette-dark) .feature__description,:host-context(html.ion-palette-dark) .feature__description,:host-context(body.dark) .feature__description,:host-context([data-theme=\"dark\"]) .feature__description{color:var(--ion-color-medium, #a0a0a0)}:host-context(.dark) .feature--clickable:hover,:host-context(.ion-palette-dark) .feature--clickable:hover,:host-context(html.ion-palette-dark) .feature--clickable:hover,:host-context(body.dark) .feature--clickable:hover,:host-context([data-theme=\"dark\"]) .feature--clickable:hover{background:#ffffff0a}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }] }); }
25250
25304
  }
25251
25305
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FeaturesListComponent, decorators: [{
25252
25306
  type: Component,
25253
- args: [{ selector: 'val-features-list', standalone: true, imports: [CommonModule, IonIcon], template: `
25307
+ args: [{ selector: 'val-features-list', standalone: true, imports: [CommonModule, RouterLink, IonIcon], template: `
25254
25308
  <div
25255
25309
  class="features-list"
25256
25310
  [class.features-list--vertical]="config().mode === 'vertical'"
@@ -25260,15 +25314,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
25260
25314
  [class.features-list--gap-large]="config().gap === 'large'"
25261
25315
  [class.features-list--center]="config().alignment === 'center'"
25262
25316
  >
25263
- @for (feature of props().features; track feature.titleKey) {
25264
- <div class="feature">
25317
+ @for (feature of visibleFeatures(); track feature.titleKey) {
25318
+ <div class="feature" [class.feature--clickable]="feature.token != null" (click)="onItem(feature)">
25265
25319
  <div
25266
25320
  class="feature__icon"
25267
25321
  [style.color]="iconColorStyle()"
25268
25322
  [style.width.px]="config().iconSize"
25269
25323
  [style.height.px]="config().iconSize"
25270
25324
  >
25271
- @if (feature.icon === 'custom' && feature.svgPath) {
25325
+ @if (feature.image) {
25326
+ <img
25327
+ class="feature__image"
25328
+ [class.feature__image--circle]="config().imageShape === 'circle'"
25329
+ [src]="feature.image"
25330
+ [alt]="getTitle(feature)"
25331
+ />
25332
+ } @else if (feature.icon === 'custom' && feature.svgPath) {
25272
25333
  <svg
25273
25334
  [attr.width]="config().iconSize"
25274
25335
  [attr.height]="config().iconSize"
@@ -25281,7 +25342,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
25281
25342
  >
25282
25343
  <path [attr.d]="feature.svgPath" />
25283
25344
  </svg>
25284
- } @else {
25345
+ } @else if (feature.icon) {
25285
25346
  <ion-icon [name]="feature.icon" [style.font-size.px]="config().iconSize"></ion-icon>
25286
25347
  }
25287
25348
  </div>
@@ -25291,9 +25352,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
25291
25352
  </div>
25292
25353
  </div>
25293
25354
  }
25355
+
25356
+ @if (canCollapse()) {
25357
+ <button type="button" class="features-list__more" (click)="toggle()">
25358
+ {{ expanded() ? showLessText() : showMoreText() }}
25359
+ </button>
25360
+ }
25361
+
25362
+ @if (config().footerLink; as link) {
25363
+ <a class="features-list__footer" [routerLink]="link.routerLink ?? null" (click)="onFooter($event, link)">
25364
+ {{ link.label }}
25365
+ </a>
25366
+ }
25294
25367
  </div>
25295
- `, styles: [":host{display:block}.features-list{display:flex}.features-list--vertical{flex-direction:column}.features-list--horizontal{flex-direction:row;flex-wrap:wrap}.features-list--horizontal .feature{flex:1;min-width:200px}.features-list--gap-small{gap:1rem}.features-list--gap-medium{gap:1.5rem}.features-list--gap-large{gap:2rem}.features-list--center{align-items:center;text-align:center}.features-list--center .feature{flex-direction:column;align-items:center}.features-list--center .feature__content{text-align:center}.feature{display:flex;align-items:flex-start;gap:1rem}.feature__icon{flex-shrink:0;display:flex;align-items:center;justify-content:center}.feature__content{flex:1}.feature__title{display:block;font-size:1.1rem;font-weight:600;color:var(--ion-text-color);margin-bottom:.25rem}.feature__description{margin:0;font-size:.95rem;color:var(--ion-color-medium);line-height:1.5}:host-context(.dark) .feature__title,:host-context(.ion-palette-dark) .feature__title,:host-context(html.ion-palette-dark) .feature__title,:host-context(body.dark) .feature__title,:host-context([data-theme=\"dark\"]) .feature__title{color:var(--ion-text-color, #fff)}:host-context(.dark) .feature__description,:host-context(.ion-palette-dark) .feature__description,:host-context(html.ion-palette-dark) .feature__description,:host-context(body.dark) .feature__description,:host-context([data-theme=\"dark\"]) .feature__description{color:var(--ion-color-medium, #a0a0a0)}:host-context(.dark) .feature__icon,:host-context(.ion-palette-dark) .feature__icon,:host-context(html.ion-palette-dark) .feature__icon,:host-context(body.dark) .feature__icon,:host-context([data-theme=\"dark\"]) .feature__icon{opacity:.9}\n"] }]
25296
- }] });
25368
+ `, styles: [":host{display:block}.features-list{display:flex}.features-list--vertical{flex-direction:column}.features-list--horizontal{flex-direction:row;flex-wrap:wrap}.features-list--horizontal .feature{flex:1;min-width:200px}.features-list--gap-small{gap:1rem}.features-list--gap-medium{gap:1.5rem}.features-list--gap-large{gap:2rem}.features-list--center{align-items:center;text-align:center}.features-list--center .feature{flex-direction:column;align-items:center}.features-list--center .feature__content{text-align:center}.feature{display:flex;align-items:flex-start;gap:1rem}.feature--clickable{cursor:pointer;border-radius:10px;transition:background-color .15s ease}.feature--clickable:hover{background:var(--ion-color-light, rgba(0, 0, 0, .03))}.feature__icon{flex-shrink:0;display:flex;align-items:center;justify-content:center}.feature__image{width:100%;height:100%;object-fit:contain;border-radius:8px}.feature__image--circle{object-fit:cover;border-radius:50%}.feature__content{flex:1}.feature__title{display:block;font-size:1.1rem;font-weight:600;color:var(--ion-text-color);margin-bottom:.25rem}.feature__description{margin:0;font-size:.95rem;color:var(--ion-color-medium);line-height:1.5}.features-list__more{align-self:flex-start;margin-top:.25rem;padding:0;background:none;border:none;font:inherit;font-weight:600;color:var(--ion-color-primary);cursor:pointer}.features-list--center .features-list__more,.features-list--center .features-list__footer{align-self:center}.features-list__footer{align-self:flex-start;margin-top:.25rem;font-weight:600;color:var(--ion-color-primary);text-decoration:none;cursor:pointer}.features-list__footer:hover{text-decoration:underline}:host-context(.dark) .feature__title,:host-context(.ion-palette-dark) .feature__title,:host-context(html.ion-palette-dark) .feature__title,:host-context(body.dark) .feature__title,:host-context([data-theme=\"dark\"]) .feature__title{color:var(--ion-text-color, #fff)}:host-context(.dark) .feature__description,:host-context(.ion-palette-dark) .feature__description,:host-context(html.ion-palette-dark) .feature__description,:host-context(body.dark) .feature__description,:host-context([data-theme=\"dark\"]) .feature__description{color:var(--ion-color-medium, #a0a0a0)}:host-context(.dark) .feature--clickable:hover,:host-context(.ion-palette-dark) .feature--clickable:hover,:host-context(html.ion-palette-dark) .feature--clickable:hover,:host-context(body.dark) .feature--clickable:hover,:host-context([data-theme=\"dark\"]) .feature--clickable:hover{background:#ffffff0a}\n"] }]
25369
+ }], ctorParameters: () => [] });
25297
25370
 
25298
25371
  /**
25299
25372
  * val-footer-links
@@ -29533,9 +29606,9 @@ class AttachmentUploaderComponent {
29533
29606
  cameraOutline,
29534
29607
  checkmarkCircleOutline,
29535
29608
  closeCircleOutline,
29609
+ closeOutline,
29536
29610
  documentOutline,
29537
29611
  imageOutline,
29538
- trashOutline,
29539
29612
  });
29540
29613
  }
29541
29614
  async onFilesSelected(event) {
@@ -29593,11 +29666,11 @@ class AttachmentUploaderComponent {
29593
29666
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
29594
29667
  }
29595
29668
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AttachmentUploaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
29596
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: AttachmentUploaderComponent, isStandalone: true, selector: "val-attachment-uploader", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { attachmentsChange: "attachmentsChange" }, ngImport: i0, template: "<div class=\"attachment-uploader\">\n <input #filePicker type=\"file\" [accept]=\"accept()\" multiple class=\"hidden-input\" (change)=\"onFilesSelected($event)\" />\n\n <div class=\"attachment-actions\">\n <ion-button fill=\"outline\" size=\"small\" [disabled]=\"isDisabled()\" (click)=\"filePicker.click()\">\n <ion-icon slot=\"start\" name=\"attach-outline\"></ion-icon>\n {{ i18n.t('attachAdd') }}\n </ion-button>\n <!-- label trick: native browser gesture forwarding \u2192 iOS/PWA no bloquea el picker -->\n <label class=\"camera-label\" [class.camera-label--disabled]=\"isDisabled()\">\n <input\n type=\"file\"\n accept=\"image/*\"\n capture=\"environment\"\n class=\"hidden-input\"\n [disabled]=\"isDisabled()\"\n (change)=\"onFilesSelected($event)\"\n />\n <ion-icon name=\"camera-outline\"></ion-icon>\n {{ i18n.t('attachCamera') }}\n </label>\n </div>\n\n @if (maxReached()) {\n <ion-note color=\"warning\" class=\"max-note\">\n {{ i18n.t('attachMaxCount', '_global', { count: maxFiles().toString() }) }}\n </ion-note>\n } @if (attachments().length > 0) {\n <div class=\"attachment-list\">\n @for (item of attachments(); track item.id) {\n <div class=\"attachment-item\" [class]=\"'status-' + item.status\">\n <ion-icon\n class=\"file-icon\"\n [name]=\"item.file.type.startsWith('image/') ? 'image-outline' : 'document-outline'\"\n ></ion-icon>\n <div class=\"file-info\">\n <span class=\"file-name\">{{ item.file.name }}</span>\n <span class=\"file-size\">{{ formatSize(item.file.size) }}</span>\n </div>\n\n @if (item.status === 'uploading') {\n <ion-spinner class=\"status-icon\" name=\"circular\"></ion-spinner>\n } @else if (item.status === 'ready') {\n <ion-icon class=\"status-icon\" name=\"checkmark-circle-outline\" color=\"success\"></ion-icon>\n } @else if (item.status === 'error') {\n <ion-icon class=\"status-icon\" name=\"close-circle-outline\" color=\"danger\"></ion-icon>\n } @if (item.status !== 'uploading') {\n <ion-button fill=\"clear\" size=\"small\" color=\"medium\" (click)=\"remove(item.id)\">\n <ion-icon slot=\"icon-only\" name=\"trash-outline\"></ion-icon>\n </ion-button>\n } @if (item.status === 'error' && item.error) {\n <ion-note color=\"danger\" class=\"error-note\">{{ item.error }}</ion-note>\n }\n </div>\n }\n </div>\n }\n</div>\n", styles: [".hidden-input{display:none}.camera-label{display:inline-flex;align-items:center;gap:6px;padding:0 12px;height:32px;font-size:14px;font-weight:500;border-radius:4px;border:1px solid currentColor;color:var(--ion-color-dark);background:transparent;cursor:pointer;-webkit-user-select:none;user-select:none;transition:opacity .15s}.camera-label ion-icon{font-size:16px;flex-shrink:0}.camera-label--disabled{opacity:.4;pointer-events:none}.attachment-uploader{display:flex;flex-direction:column;gap:8px}.attachment-actions{display:flex;gap:8px;flex-wrap:wrap}.attachment-list{display:flex;flex-direction:column;gap:4px}.attachment-item{display:flex;align-items:center;gap:8px;padding:8px 12px;border-radius:8px;background:var(--ion-color-light);flex-wrap:wrap}.attachment-item.status-error{background:var(--ion-color-danger-tint)}.attachment-item.status-ready{background:var(--ion-color-success-tint)}.file-icon{font-size:20px;flex-shrink:0}.file-info{flex:1;min-width:0;display:flex;flex-direction:column}.file-name{font-size:14px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.file-size{font-size:12px;color:var(--ion-color-medium)}.status-icon{font-size:20px;flex-shrink:0}.error-note{font-size:11px;width:100%}.max-note{font-size:12px}\n"], dependencies: [{ 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: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }] }); }
29669
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: AttachmentUploaderComponent, isStandalone: true, selector: "val-attachment-uploader", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { attachmentsChange: "attachmentsChange" }, ngImport: i0, template: "<div class=\"attachment-uploader\">\n <input #filePicker type=\"file\" [accept]=\"accept()\" multiple class=\"hidden-input\" (change)=\"onFilesSelected($event)\" />\n\n <div class=\"attachment-actions\">\n <ion-button fill=\"outline\" color=\"dark\" size=\"small\" [disabled]=\"isDisabled()\" (click)=\"filePicker.click()\">\n <ion-icon slot=\"start\" name=\"attach-outline\"></ion-icon>\n {{ i18n.t('attachAdd') }}\n </ion-button>\n <!-- label trick: native browser gesture forwarding \u2192 iOS/PWA no bloquea el picker -->\n <label class=\"camera-label\" [class.camera-label--disabled]=\"isDisabled()\">\n <input\n type=\"file\"\n accept=\"image/*\"\n capture=\"environment\"\n class=\"hidden-input\"\n [disabled]=\"isDisabled()\"\n (change)=\"onFilesSelected($event)\"\n />\n <ion-icon name=\"camera-outline\"></ion-icon>\n {{ i18n.t('attachCamera') }}\n </label>\n </div>\n\n @if (maxReached()) {\n <ion-note color=\"warning\" class=\"max-note\">\n {{ i18n.t('attachMaxCount', '_global', { count: maxFiles().toString() }) }}\n </ion-note>\n } @if (attachments().length > 0) {\n <div class=\"attachment-list\">\n @for (item of attachments(); track item.id) {\n <div class=\"attachment-item\" [class]=\"'status-' + item.status\">\n <ion-icon\n class=\"file-icon\"\n [name]=\"item.file.type.startsWith('image/') ? 'image-outline' : 'document-outline'\"\n ></ion-icon>\n <div class=\"file-info\">\n <span class=\"file-name\">{{ item.file.name }}</span>\n <span class=\"file-size\">{{ formatSize(item.file.size) }}</span>\n </div>\n\n @if (item.status === 'uploading') {\n <ion-spinner class=\"status-icon\" name=\"circular\"></ion-spinner>\n } @else if (item.status === 'ready') {\n <ion-icon class=\"status-icon\" name=\"checkmark-circle-outline\" color=\"success\"></ion-icon>\n } @else if (item.status === 'error') {\n <ion-icon class=\"status-icon\" name=\"close-circle-outline\" color=\"danger\"></ion-icon>\n } @if (item.status !== 'uploading') {\n <ion-button fill=\"clear\" size=\"small\" color=\"dark\" (click)=\"remove(item.id)\">\n <ion-icon slot=\"icon-only\" name=\"close-outline\"></ion-icon>\n </ion-button>\n } @if (item.status === 'error' && item.error) {\n <ion-note color=\"danger\" class=\"error-note\">{{ item.error }}</ion-note>\n }\n </div>\n }\n </div>\n }\n</div>\n", styles: [".hidden-input{display:none}.camera-label{display:inline-flex;align-items:center;gap:6px;padding:0 12px;height:32px;font-size:14px;font-weight:500;border-radius:4px;border:1px solid currentColor;color:var(--ion-color-dark);background:transparent;cursor:pointer;-webkit-user-select:none;user-select:none;transition:opacity .15s}.camera-label ion-icon{font-size:16px;flex-shrink:0}.camera-label--disabled{opacity:.4;pointer-events:none}.attachment-uploader{display:flex;flex-direction:column;gap:8px}.attachment-actions{display:flex;gap:8px;flex-wrap:wrap}.attachment-list{display:flex;flex-direction:column;gap:4px}.attachment-item{display:flex;align-items:center;gap:8px;padding:8px 10px;border-radius:8px;background:var(--ion-background-color, #fff);border:1px solid var(--ion-color-light-shade, rgba(0, 0, 0, .08));flex-wrap:wrap}.attachment-item.status-error{border-color:var(--ion-color-danger)}.file-icon{font-size:22px;color:var(--ion-text-color);flex-shrink:0}.file-info{flex:1;min-width:0;display:flex;flex-direction:column}.file-name{font-size:13px;color:var(--ion-text-color);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.file-size{font-size:11px;color:var(--ion-color-medium)}:host-context(.dark) .attachment-item,:host-context(.ion-palette-dark) .attachment-item,:host-context(html.ion-palette-dark) .attachment-item,:host-context(body.dark) .attachment-item,:host-context([data-theme=dark]) .attachment-item{background:#ffffff0a;border-color:#ffffff1f}:host-context(.dark) .attachment-item.status-error,:host-context(.ion-palette-dark) .attachment-item.status-error,:host-context(html.ion-palette-dark) .attachment-item.status-error,:host-context(body.dark) .attachment-item.status-error,:host-context([data-theme=dark]) .attachment-item.status-error{border-color:var(--ion-color-danger)}.status-icon{font-size:20px;flex-shrink:0}.error-note{font-size:11px;width:100%}.max-note{font-size:12px}\n"], dependencies: [{ 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: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }] }); }
29597
29670
  }
29598
29671
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AttachmentUploaderComponent, decorators: [{
29599
29672
  type: Component,
29600
- args: [{ selector: 'val-attachment-uploader', standalone: true, imports: [IonButton, IonIcon, IonNote, IonSpinner], template: "<div class=\"attachment-uploader\">\n <input #filePicker type=\"file\" [accept]=\"accept()\" multiple class=\"hidden-input\" (change)=\"onFilesSelected($event)\" />\n\n <div class=\"attachment-actions\">\n <ion-button fill=\"outline\" size=\"small\" [disabled]=\"isDisabled()\" (click)=\"filePicker.click()\">\n <ion-icon slot=\"start\" name=\"attach-outline\"></ion-icon>\n {{ i18n.t('attachAdd') }}\n </ion-button>\n <!-- label trick: native browser gesture forwarding \u2192 iOS/PWA no bloquea el picker -->\n <label class=\"camera-label\" [class.camera-label--disabled]=\"isDisabled()\">\n <input\n type=\"file\"\n accept=\"image/*\"\n capture=\"environment\"\n class=\"hidden-input\"\n [disabled]=\"isDisabled()\"\n (change)=\"onFilesSelected($event)\"\n />\n <ion-icon name=\"camera-outline\"></ion-icon>\n {{ i18n.t('attachCamera') }}\n </label>\n </div>\n\n @if (maxReached()) {\n <ion-note color=\"warning\" class=\"max-note\">\n {{ i18n.t('attachMaxCount', '_global', { count: maxFiles().toString() }) }}\n </ion-note>\n } @if (attachments().length > 0) {\n <div class=\"attachment-list\">\n @for (item of attachments(); track item.id) {\n <div class=\"attachment-item\" [class]=\"'status-' + item.status\">\n <ion-icon\n class=\"file-icon\"\n [name]=\"item.file.type.startsWith('image/') ? 'image-outline' : 'document-outline'\"\n ></ion-icon>\n <div class=\"file-info\">\n <span class=\"file-name\">{{ item.file.name }}</span>\n <span class=\"file-size\">{{ formatSize(item.file.size) }}</span>\n </div>\n\n @if (item.status === 'uploading') {\n <ion-spinner class=\"status-icon\" name=\"circular\"></ion-spinner>\n } @else if (item.status === 'ready') {\n <ion-icon class=\"status-icon\" name=\"checkmark-circle-outline\" color=\"success\"></ion-icon>\n } @else if (item.status === 'error') {\n <ion-icon class=\"status-icon\" name=\"close-circle-outline\" color=\"danger\"></ion-icon>\n } @if (item.status !== 'uploading') {\n <ion-button fill=\"clear\" size=\"small\" color=\"medium\" (click)=\"remove(item.id)\">\n <ion-icon slot=\"icon-only\" name=\"trash-outline\"></ion-icon>\n </ion-button>\n } @if (item.status === 'error' && item.error) {\n <ion-note color=\"danger\" class=\"error-note\">{{ item.error }}</ion-note>\n }\n </div>\n }\n </div>\n }\n</div>\n", styles: [".hidden-input{display:none}.camera-label{display:inline-flex;align-items:center;gap:6px;padding:0 12px;height:32px;font-size:14px;font-weight:500;border-radius:4px;border:1px solid currentColor;color:var(--ion-color-dark);background:transparent;cursor:pointer;-webkit-user-select:none;user-select:none;transition:opacity .15s}.camera-label ion-icon{font-size:16px;flex-shrink:0}.camera-label--disabled{opacity:.4;pointer-events:none}.attachment-uploader{display:flex;flex-direction:column;gap:8px}.attachment-actions{display:flex;gap:8px;flex-wrap:wrap}.attachment-list{display:flex;flex-direction:column;gap:4px}.attachment-item{display:flex;align-items:center;gap:8px;padding:8px 12px;border-radius:8px;background:var(--ion-color-light);flex-wrap:wrap}.attachment-item.status-error{background:var(--ion-color-danger-tint)}.attachment-item.status-ready{background:var(--ion-color-success-tint)}.file-icon{font-size:20px;flex-shrink:0}.file-info{flex:1;min-width:0;display:flex;flex-direction:column}.file-name{font-size:14px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.file-size{font-size:12px;color:var(--ion-color-medium)}.status-icon{font-size:20px;flex-shrink:0}.error-note{font-size:11px;width:100%}.max-note{font-size:12px}\n"] }]
29673
+ args: [{ selector: 'val-attachment-uploader', standalone: true, imports: [IonButton, IonIcon, IonNote, IonSpinner], template: "<div class=\"attachment-uploader\">\n <input #filePicker type=\"file\" [accept]=\"accept()\" multiple class=\"hidden-input\" (change)=\"onFilesSelected($event)\" />\n\n <div class=\"attachment-actions\">\n <ion-button fill=\"outline\" color=\"dark\" size=\"small\" [disabled]=\"isDisabled()\" (click)=\"filePicker.click()\">\n <ion-icon slot=\"start\" name=\"attach-outline\"></ion-icon>\n {{ i18n.t('attachAdd') }}\n </ion-button>\n <!-- label trick: native browser gesture forwarding \u2192 iOS/PWA no bloquea el picker -->\n <label class=\"camera-label\" [class.camera-label--disabled]=\"isDisabled()\">\n <input\n type=\"file\"\n accept=\"image/*\"\n capture=\"environment\"\n class=\"hidden-input\"\n [disabled]=\"isDisabled()\"\n (change)=\"onFilesSelected($event)\"\n />\n <ion-icon name=\"camera-outline\"></ion-icon>\n {{ i18n.t('attachCamera') }}\n </label>\n </div>\n\n @if (maxReached()) {\n <ion-note color=\"warning\" class=\"max-note\">\n {{ i18n.t('attachMaxCount', '_global', { count: maxFiles().toString() }) }}\n </ion-note>\n } @if (attachments().length > 0) {\n <div class=\"attachment-list\">\n @for (item of attachments(); track item.id) {\n <div class=\"attachment-item\" [class]=\"'status-' + item.status\">\n <ion-icon\n class=\"file-icon\"\n [name]=\"item.file.type.startsWith('image/') ? 'image-outline' : 'document-outline'\"\n ></ion-icon>\n <div class=\"file-info\">\n <span class=\"file-name\">{{ item.file.name }}</span>\n <span class=\"file-size\">{{ formatSize(item.file.size) }}</span>\n </div>\n\n @if (item.status === 'uploading') {\n <ion-spinner class=\"status-icon\" name=\"circular\"></ion-spinner>\n } @else if (item.status === 'ready') {\n <ion-icon class=\"status-icon\" name=\"checkmark-circle-outline\" color=\"success\"></ion-icon>\n } @else if (item.status === 'error') {\n <ion-icon class=\"status-icon\" name=\"close-circle-outline\" color=\"danger\"></ion-icon>\n } @if (item.status !== 'uploading') {\n <ion-button fill=\"clear\" size=\"small\" color=\"dark\" (click)=\"remove(item.id)\">\n <ion-icon slot=\"icon-only\" name=\"close-outline\"></ion-icon>\n </ion-button>\n } @if (item.status === 'error' && item.error) {\n <ion-note color=\"danger\" class=\"error-note\">{{ item.error }}</ion-note>\n }\n </div>\n }\n </div>\n }\n</div>\n", styles: [".hidden-input{display:none}.camera-label{display:inline-flex;align-items:center;gap:6px;padding:0 12px;height:32px;font-size:14px;font-weight:500;border-radius:4px;border:1px solid currentColor;color:var(--ion-color-dark);background:transparent;cursor:pointer;-webkit-user-select:none;user-select:none;transition:opacity .15s}.camera-label ion-icon{font-size:16px;flex-shrink:0}.camera-label--disabled{opacity:.4;pointer-events:none}.attachment-uploader{display:flex;flex-direction:column;gap:8px}.attachment-actions{display:flex;gap:8px;flex-wrap:wrap}.attachment-list{display:flex;flex-direction:column;gap:4px}.attachment-item{display:flex;align-items:center;gap:8px;padding:8px 10px;border-radius:8px;background:var(--ion-background-color, #fff);border:1px solid var(--ion-color-light-shade, rgba(0, 0, 0, .08));flex-wrap:wrap}.attachment-item.status-error{border-color:var(--ion-color-danger)}.file-icon{font-size:22px;color:var(--ion-text-color);flex-shrink:0}.file-info{flex:1;min-width:0;display:flex;flex-direction:column}.file-name{font-size:13px;color:var(--ion-text-color);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.file-size{font-size:11px;color:var(--ion-color-medium)}:host-context(.dark) .attachment-item,:host-context(.ion-palette-dark) .attachment-item,:host-context(html.ion-palette-dark) .attachment-item,:host-context(body.dark) .attachment-item,:host-context([data-theme=dark]) .attachment-item{background:#ffffff0a;border-color:#ffffff1f}:host-context(.dark) .attachment-item.status-error,:host-context(.ion-palette-dark) .attachment-item.status-error,:host-context(html.ion-palette-dark) .attachment-item.status-error,:host-context(body.dark) .attachment-item.status-error,:host-context([data-theme=dark]) .attachment-item.status-error{border-color:var(--ion-color-danger)}.status-icon{font-size:20px;flex-shrink:0}.error-note{font-size:11px;width:100%}.max-note{font-size:12px}\n"] }]
29601
29674
  }], ctorParameters: () => [] });
29602
29675
 
29603
29676
  /**
@@ -33885,6 +33958,166 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
33885
33958
  type: Output
33886
33959
  }] } });
33887
33960
 
33961
+ /**
33962
+ * Default values for SearchHeaderMetadata.
33963
+ */
33964
+ const SEARCH_HEADER_DEFAULTS = {
33965
+ searchToken: 'search',
33966
+ maxWidth: '720px',
33967
+ startOpen: false,
33968
+ showClose: true,
33969
+ };
33970
+
33971
+ addIcons({ closeOutline });
33972
+ /**
33973
+ * `val-search-header` — header/toolbar con búsqueda desplegable.
33974
+ *
33975
+ * El botón de búsqueda es una ICON action del toolbar (igual que
33976
+ * menu/avatar/notificaciones), identificada por su `token` (`searchToken`).
33977
+ * Al presionarla se despliega un `val-searchbar` debajo del toolbar: 100% del
33978
+ * ancho en mobile, un máximo centrado en desktop.
33979
+ *
33980
+ * Reusa `val-toolbar` para el header — las demás acciones se emiten por
33981
+ * `(onAction)`. La query se emite por `(search)`.
33982
+ *
33983
+ * @example
33984
+ * ```html
33985
+ * <val-search-header
33986
+ * [props]="{
33987
+ * header: { toolbar: { title: 'AWS', withActions: true, withBack: false,
33988
+ * actions: [
33989
+ * { type: 'ICON', description: 'search-outline', token: 'search', position: 'right' },
33990
+ * { type: 'ICON', description: 'notifications-outline', token: 'notif', position: 'right' }
33991
+ * ] } },
33992
+ * searchbar: { placeholder: 'Buscar...' }
33993
+ * }"
33994
+ * (search)="onSearch($event)"
33995
+ * (onAction)="onAction($event)"
33996
+ * >
33997
+ * <ion-button slot="search-actions" fill="outline">Filter: All</ion-button>
33998
+ * </val-search-header>
33999
+ * ```
34000
+ */
34001
+ class SearchHeaderComponent {
34002
+ constructor() {
34003
+ this.host = inject((ElementRef));
34004
+ /** Configuración del search header. */
34005
+ this.props = {
34006
+ header: { toolbar: { title: '', withActions: true, withBack: false, actions: [] } },
34007
+ };
34008
+ /** Emite el token de cualquier acción del toolbar que NO sea la de búsqueda. */
34009
+ this.onAction = new EventEmitter();
34010
+ /** Emite la query del searchbar. */
34011
+ this.search = new EventEmitter();
34012
+ /** Emite al abrir/cerrar la barra de búsqueda. */
34013
+ this.searchToggle = new EventEmitter();
34014
+ /** Emite cuando el searchbar pierde el foco. */
34015
+ this.blur = new EventEmitter();
34016
+ this._open = signal(false);
34017
+ this.open = this._open.asReadonly();
34018
+ this.config = computed(() => ({
34019
+ ...SEARCH_HEADER_DEFAULTS,
34020
+ ...this.props,
34021
+ }));
34022
+ // startOpen se respeta una vez, en la primera lectura de props.
34023
+ if (this.props.startOpen) {
34024
+ this._open.set(true);
34025
+ }
34026
+ }
34027
+ handleToolbar(token) {
34028
+ if (token && token === this.config().searchToken) {
34029
+ this.toggle();
34030
+ return;
34031
+ }
34032
+ this.onAction.emit(token);
34033
+ }
34034
+ toggle() {
34035
+ const next = !this._open();
34036
+ this._open.set(next);
34037
+ this.searchToggle.emit(next);
34038
+ if (next) {
34039
+ this.focusSearchbar();
34040
+ }
34041
+ }
34042
+ focusSearchbar() {
34043
+ // Enfoca el ion-searchbar nativo al desplegar (mejor UX, como AWS/Slack).
34044
+ setTimeout(() => {
34045
+ const el = this.host.nativeElement.querySelector('ion-searchbar');
34046
+ el?.setFocus?.();
34047
+ }, 50);
34048
+ }
34049
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SearchHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
34050
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: SearchHeaderComponent, isStandalone: true, selector: "val-search-header", inputs: { props: "props" }, outputs: { onAction: "onAction", search: "search", searchToggle: "searchToggle", blur: "blur" }, ngImport: i0, template: `
34051
+ <ion-header [class.ion-no-border]="!config().header.bordered" [translucent]="config().header.translucent">
34052
+ <val-toolbar [props]="config().header.toolbar" (onClick)="handleToolbar($event)"></val-toolbar>
34053
+
34054
+ @if (open()) {
34055
+ <div class="search-reveal">
34056
+ <div class="search-reveal__inner" [style.max-width]="config().maxWidth">
34057
+ <val-searchbar
34058
+ class="search-reveal__bar"
34059
+ [props]="config().searchbar || {}"
34060
+ (filterEvent)="search.emit($event)"
34061
+ (blurEvent)="blur.emit()"
34062
+ ></val-searchbar>
34063
+
34064
+ <ng-content select="[search-actions]"></ng-content>
34065
+
34066
+ @if (config().showClose) {
34067
+ <ion-buttons>
34068
+ <ion-button class="search-reveal__close" fill="clear" color="dark" (click)="toggle()">
34069
+ <ion-icon name="close-outline" slot="icon-only"></ion-icon>
34070
+ </ion-button>
34071
+ </ion-buttons>
34072
+ }
34073
+ </div>
34074
+ </div>
34075
+ }
34076
+ </ion-header>
34077
+ `, isInline: true, styles: [":host{display:block}.search-reveal{width:100%;background:var(--ion-toolbar-background, var(--ion-background-color));border-top:1px solid var(--ion-color-light-shade, rgba(0, 0, 0, .08));overflow:hidden;animation:search-reveal-in .22s ease}.search-reveal__inner{display:flex;align-items:center;gap:.5rem;width:100%;margin:0 auto;padding:.25rem .5rem}.search-reveal__bar{flex:1;min-width:0}.search-reveal__close{flex-shrink:0}@keyframes search-reveal-in{0%{opacity:0;transform:translateY(-8px);max-height:0}to{opacity:1;transform:translateY(0);max-height:120px}}:host-context(.dark) .search-reveal,:host-context(.ion-palette-dark) .search-reveal,:host-context(html.ion-palette-dark) .search-reveal,:host-context(body.dark) .search-reveal,:host-context([data-theme=dark]) .search-reveal{border-top-color:#ffffff1f}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { 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: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: ToolbarComponent, selector: "val-toolbar", inputs: ["props"], outputs: ["onClick"] }, { kind: "component", type: SearchbarComponent, selector: "val-searchbar", inputs: ["preset", "props"], outputs: ["filterEvent", "focusEvent", "blurEvent"] }] }); }
34078
+ }
34079
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SearchHeaderComponent, decorators: [{
34080
+ type: Component,
34081
+ args: [{ selector: 'val-search-header', standalone: true, imports: [CommonModule, IonHeader, IonButton, IonButtons, IonIcon, ToolbarComponent, SearchbarComponent], template: `
34082
+ <ion-header [class.ion-no-border]="!config().header.bordered" [translucent]="config().header.translucent">
34083
+ <val-toolbar [props]="config().header.toolbar" (onClick)="handleToolbar($event)"></val-toolbar>
34084
+
34085
+ @if (open()) {
34086
+ <div class="search-reveal">
34087
+ <div class="search-reveal__inner" [style.max-width]="config().maxWidth">
34088
+ <val-searchbar
34089
+ class="search-reveal__bar"
34090
+ [props]="config().searchbar || {}"
34091
+ (filterEvent)="search.emit($event)"
34092
+ (blurEvent)="blur.emit()"
34093
+ ></val-searchbar>
34094
+
34095
+ <ng-content select="[search-actions]"></ng-content>
34096
+
34097
+ @if (config().showClose) {
34098
+ <ion-buttons>
34099
+ <ion-button class="search-reveal__close" fill="clear" color="dark" (click)="toggle()">
34100
+ <ion-icon name="close-outline" slot="icon-only"></ion-icon>
34101
+ </ion-button>
34102
+ </ion-buttons>
34103
+ }
34104
+ </div>
34105
+ </div>
34106
+ }
34107
+ </ion-header>
34108
+ `, styles: [":host{display:block}.search-reveal{width:100%;background:var(--ion-toolbar-background, var(--ion-background-color));border-top:1px solid var(--ion-color-light-shade, rgba(0, 0, 0, .08));overflow:hidden;animation:search-reveal-in .22s ease}.search-reveal__inner{display:flex;align-items:center;gap:.5rem;width:100%;margin:0 auto;padding:.25rem .5rem}.search-reveal__bar{flex:1;min-width:0}.search-reveal__close{flex-shrink:0}@keyframes search-reveal-in{0%{opacity:0;transform:translateY(-8px);max-height:0}to{opacity:1;transform:translateY(0);max-height:120px}}:host-context(.dark) .search-reveal,:host-context(.ion-palette-dark) .search-reveal,:host-context(html.ion-palette-dark) .search-reveal,:host-context(body.dark) .search-reveal,:host-context([data-theme=dark]) .search-reveal{border-top-color:#ffffff1f}\n"] }]
34109
+ }], ctorParameters: () => [], propDecorators: { props: [{
34110
+ type: Input
34111
+ }], onAction: [{
34112
+ type: Output
34113
+ }], search: [{
34114
+ type: Output
34115
+ }], searchToggle: [{
34116
+ type: Output
34117
+ }], blur: [{
34118
+ type: Output
34119
+ }] } });
34120
+
33888
34121
  /** Segundos de espera antes de poder reenviar el código EMAIL/SMS. */
33889
34122
  const RESEND_COOLDOWN_SECONDS = 30;
33890
34123
  /**
@@ -43640,7 +43873,7 @@ const VALTECH_SITE_PATHS = {
43640
43873
  support: '/contact',
43641
43874
  contact: '/contact',
43642
43875
  faq: '/faq',
43643
- about: '/legal/about',
43876
+ about: '/about',
43644
43877
  terms: '/legal/terms',
43645
43878
  privacy: '/legal/privacy',
43646
43879
  cookies: '/legal/cookies',
@@ -51146,7 +51379,7 @@ const VALTECH_FOOTER_LOGO = {
51146
51379
  const VALTECH_COMPANY_LINKS = {
51147
51380
  // Links legales con `external: true` → abren en tab nueva (target=_blank).
51148
51381
  legal: [
51149
- { key: 'aboutUs', url: '/legal/about', kind: 'site', external: false },
51382
+ { key: 'aboutUs', url: '/about', kind: 'site', external: false },
51150
51383
  { key: 'privacyPolicy', url: '/legal/privacy', kind: 'legal', external: true },
51151
51384
  { key: 'termsConditions', url: '/legal/terms', kind: 'legal', external: true },
51152
51385
  { key: 'cookiesPolicy', url: '/legal/cookies', kind: 'legal', external: true },
@@ -51496,5 +51729,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
51496
51729
  * Generated bundle index. Do not edit.
51497
51730
  */
51498
51731
 
51499
- export { ACTION_CARD_DEFAULTS, AD_SIZE_MAP, API_TABLE_COLUMN_LABELS, ARTICLE_CARD_DEFAULTS, ARTICLE_SPACING, AVATAR_UPLOAD_DEFAULTS, AccordionComponent, ActionCardComponent, ActionHeaderComponent, ActionType, AdSlotComponent, AdsLoaderService, AdsService, AlertBoxComponent, AnalyticsErrorHandler, AnalyticsRouterTracker, AnalyticsService, AnimatedTerminalComponent, AppConfigService, AppVersionService, ArticleBuilder, ArticleCardComponent, ArticleComponent, AttachmentUploaderComponent, AuthBackgroundComponent, AuthService, AuthStateService, AuthStorageService, AuthSyncService, AvatarComponent, AvatarUploadComponent, BOTTOM_NAV_DEFAULTS, BannerComponent, BaseDefault, BlogPostBuilder, BottomNavComponent, BoxComponent, BreadcrumbComponent, ButtonComponent, ButtonGroupComponent, CALLOUT_LABELS, CHEV_KEYS, CIRCLE_KEYS, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CORNER_KEYS, CTA_CARD_DEFAULTS, CURRENCY_INFO, CardComponent, CardSection, CardType, CardsCarouselComponent, ChangeEmailModalComponent, ChangePasswordModalComponent, CheckInputComponent, CheckboxRadioInputComponent, ChipGroupComponent, ClearDefault, ClearDefaultBlock, ClearDefaultFull, ClearDefaultRound, ClearDefaultRoundBlock, ClearDefaultRoundFull, CodeDisplayComponent, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ContainerComponent, ContentLoaderComponent, ContentReactionComponent, ContentService, ContentTransformer, CookieBannerComponent, CountdownComponent, CtaCardComponent, CurrencyInputComponent, DEFAULT_ADS_CONFIG, DEFAULT_APP_CONFIG_SERVICE_CONFIG, DEFAULT_APP_VERSION_SERVICE_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_BACK_HEADER, DEFAULT_CANCEL_BUTTON, DEFAULT_CHECK_INTERVAL_MS, DEFAULT_CONFIRM_BUTTON, DEFAULT_COUNTDOWN_LABELS, DEFAULT_COUNTDOWN_LABELS_EN, DEFAULT_DEBUG_CONSOLE_CONFIG, DEFAULT_DONATION_CONFIG, 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, DEFAULT_SPLASH_SCREEN_CONFIG, DataTableComponent, DateInputComponent, DateRangeInputComponent, DebugConsoleComponent, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsBuilder, DocsCalloutComponent, DocsCodeExampleComponent, DocsLayoutComponent, DocsNavLinksComponent, DocsNavigationService, DocsPageComponent, DocsSearchComponent, DocsSectionComponent, DocsShellComponent, DocsSidebarComponent, DocsTocComponent, DonationService, DownloadService, EmailInputComponent, EmptyStateComponent, ExpandableTextComponent, FEATURES_LIST_DEFAULTS, FUN_MODAL_DEFAULTS, FabComponent, FaqComponent, FeaturesListComponent, FeedbackFormComponent, FeedbackService, FileInputComponent, FirebaseService, FirestoreCollectionFactory, FirestoreService, FooterComponent, FooterLinksComponent, FormComponent, FormFooterComponent, FormSkeletonComponent, FunHeaderComponent, FunModalComponent, GlassComponent, GlowCardComponent, GlowComponent, GridSkeletonComponent, HANDOFF_ROUTE_PARAM, HANDOFF_TOKEN_PARAM, HandoffService, HasPermissionDirective, HeaderComponent, HintComponent, HorizontalScrollComponent, HourInputComponent, HrefComponent, I18nService, IMAGE_DEFAULTS, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, INVITATION_CARD_DEFAULTS, Icon, IconComponent, IconService, ImageComponent, ImageCropComponent, ImageService, InAppBrowserService, InfiniteListComponent, InfoComponent, InputI18nHelper, InputType, InvitationCardComponent, ItemListComponent, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LEGAL_CONTENT_CONFIG, LOGIN_DEFAULTS, LandingSplitComponent, LandingStepsComponent, LanguageSelectorComponent, LayeredCardComponent, LegalContentService, LegalLinkService, LinkComponent, LinkProcessorService, LinkedProvidersComponent, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LoadingDirective, LocalStorageService, LocaleService, LoginComponent, MEDIA_OBJECT_DEFAULTS, MEMBER_CARD_DEFAULTS, MODAL_SIZES, MOTIF_KEYS, MOTION, MaintenancePageComponent, MarkdownArticleParserService, MediaObjectComponent, MemberCardComponent, MenuComponent, MessagingService, MetaService, MfaModalComponent, ModalService, ModalShellComponent, MultiSelectSearchComponent, NUM_KEYS, NavigationService, NetworkBannerComponent, NetworkStatusService, NewsBuilder, NoContentComponent, NotesBoxComponent, NotificationActionService, NotificationsService, NumberFromToComponent, NumberInputComponent, NumberStepperComponent, OAUTH_PROVIDERS_INFO, OAuthCallbackComponent, OAuthService, OrgService, OrgSwitchService, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PATTERN_MOTIFS, PATTERN_PALETTES, PATTERN_STYLE_CONFIGS, PLATFORM_CONFIGS, PageContentComponent, PageLinksComponent, PageRefreshService, PageTemplateComponent, PageWavesComponent, PageWrapperComponent, PaginationComponent, PaginationService, PasswordInputComponent, PatternComponent, 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, REQUEST_STATUSES, RadioInputComponent, RangeInputComponent, RatingComponent, RefresherComponent, RequestFirestoreService, RequestFormBuilderService, RequestService, RightsFooterComponent, RotatingTextComponent, SHAPE_KEYS, SHARE_PROFILE_MODAL_DEFAULTS, SKELETON_LAYOUT_DEFAULT_ROWS, SKELETON_PRESETS, SOLID_KEYS, STATS_BAR_DEFAULTS, STROKE_KEYS, 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, ShareProfileModalComponent, SimpleComponent, SkeletonComponent, SkeletonLayoutComponent, SkeletonService, SolidBlockButton, SolidDefault, SolidDefaultBlock, SolidDefaultButton, SolidDefaultFull, SolidDefaultRound, SolidDefaultRoundBlock, SolidDefaultRoundButton, SolidDefaultRoundFull, SolidFullButton, SolidLargeButton, SolidLargeRoundButton, SolidSmallButton, SolidSmallRoundButton, SplashScreenService, StatsBarComponent, StatsCardComponent, StepperComponent, StorageService, SwipeCarouselComponent, TRI_KEYS, TabbedContentComponent, TableSkeletonComponent, TabsComponent, Terminal404Component, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeService, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TranslatePipe, TypedCollection, UPDATE_BANNER_DEFAULT_CONTENT, UPDATE_BANNER_I18N_NAMESPACE, UpdateBannerComponent, UserAvatarComponent, UsernameInputComponent, VALTECH_ADS_CONFIG, VALTECH_APP_CONFIG, VALTECH_APP_VERSION, VALTECH_AUTH_CONFIG, VALTECH_COMPANY_LINKS, VALTECH_CONTENT_CONFIG, VALTECH_DEBUG_CONSOLE, VALTECH_DEFAULT_CONTENT, VALTECH_DONATION_CONFIG, VALTECH_FEEDBACK_CONFIG, VALTECH_FIREBASE_CONFIG, VALTECH_FOOTER_I18N, VALTECH_FOOTER_LOGO, VALTECH_LANGUAGE_SELECTOR, VALTECH_LEGAL_CONFIG, VALTECH_NETWORK_ERROR_KEY, VALTECH_SITE_PATHS, VALTECH_SOCIAL_LINKS, VALTECH_SPLASH_SCREEN, VALTECH_WEB_BASE_URLS, VERSION, ValtechErrorService, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, authGuard, authInterceptor, authPasswordValidator, blogPost, buildFooterLinks, buildPath, collections, connectPageRefresh, createErrorStateProps, createFirebaseConfig, createGlowCardProps, createInitialPaginationState, createNumberFromToField, createRefreshableStream, createTitleProps, docs, errorLoggingInterceptor, extractPathParams, generatePatternTiles, generateRandomTile, getAppInfo, getAppVersion, getCollectionPath, getDocumentId, getTimeOfDayKey, goToTop, guestGuard, hasEmulators, interpretError, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isValidPath, joinPath, maxLength, mulberry32, news, parseMarkdownArticle, permissionGuard, permissionGuardFromRoute, provideLegalContent, provideSplashScreen, provideValtechAds, provideValtechAppConfig, provideValtechAppVersion, provideValtechAuth, provideValtechAuthInterceptor, provideValtechContent, provideValtechDebugConsole, provideValtechDonations, provideValtechErrorHandling, provideValtechFeedback, provideValtechFirebase, provideValtechI18n, provideValtechLegal, provideValtechPresets, provideValtechSite, provideValtechSkeleton, query, renderPatternSvgInner, replaceSpecialChars, resolveColor, resolveInputDefaultValue, resolveWebBaseUrl, roleGuard, storagePaths, superAdminGuard, toArticle };
51732
+ export { ACTION_CARD_DEFAULTS, AD_SIZE_MAP, API_TABLE_COLUMN_LABELS, ARTICLE_CARD_DEFAULTS, ARTICLE_SPACING, AVATAR_UPLOAD_DEFAULTS, AccordionComponent, ActionCardComponent, ActionHeaderComponent, ActionType, AdSlotComponent, AdsLoaderService, AdsService, AlertBoxComponent, AnalyticsErrorHandler, AnalyticsRouterTracker, AnalyticsService, AnimatedTerminalComponent, AppConfigService, AppVersionService, ArticleBuilder, ArticleCardComponent, ArticleComponent, AttachmentUploaderComponent, AuthBackgroundComponent, AuthService, AuthStateService, AuthStorageService, AuthSyncService, AvatarComponent, AvatarUploadComponent, BOTTOM_NAV_DEFAULTS, BannerComponent, BaseDefault, BlogPostBuilder, BottomNavComponent, BoxComponent, BreadcrumbComponent, ButtonComponent, ButtonGroupComponent, CALLOUT_LABELS, CHEV_KEYS, CIRCLE_KEYS, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CORNER_KEYS, CTA_CARD_DEFAULTS, CURRENCY_INFO, CardComponent, CardSection, CardType, CardsCarouselComponent, ChangeEmailModalComponent, ChangePasswordModalComponent, CheckInputComponent, CheckboxRadioInputComponent, ChipGroupComponent, ClearDefault, ClearDefaultBlock, ClearDefaultFull, ClearDefaultRound, ClearDefaultRoundBlock, ClearDefaultRoundFull, CodeDisplayComponent, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ContainerComponent, ContentLoaderComponent, ContentReactionComponent, ContentService, ContentTransformer, CookieBannerComponent, CountdownComponent, CtaCardComponent, CurrencyInputComponent, DEFAULT_ADS_CONFIG, DEFAULT_APP_CONFIG_SERVICE_CONFIG, DEFAULT_APP_VERSION_SERVICE_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_BACK_HEADER, DEFAULT_CANCEL_BUTTON, DEFAULT_CHECK_INTERVAL_MS, DEFAULT_CONFIRM_BUTTON, DEFAULT_COUNTDOWN_LABELS, DEFAULT_COUNTDOWN_LABELS_EN, DEFAULT_DEBUG_CONSOLE_CONFIG, DEFAULT_DONATION_CONFIG, 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, DEFAULT_SPLASH_SCREEN_CONFIG, DataTableComponent, DateInputComponent, DateRangeInputComponent, DebugConsoleComponent, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsBuilder, DocsCalloutComponent, DocsCodeExampleComponent, DocsLayoutComponent, DocsNavLinksComponent, DocsNavigationService, DocsPageComponent, DocsSearchComponent, DocsSectionComponent, DocsShellComponent, DocsSidebarComponent, DocsTocComponent, DonationService, DownloadService, EmailInputComponent, EmptyStateComponent, ExpandableTextComponent, FEATURES_LIST_DEFAULTS, FUN_MODAL_DEFAULTS, FabComponent, FaqComponent, FeaturesListComponent, FeedbackFormComponent, FeedbackService, FileInputComponent, FirebaseService, FirestoreCollectionFactory, FirestoreService, FooterComponent, FooterLinksComponent, FormComponent, FormFooterComponent, FormSkeletonComponent, FunHeaderComponent, FunModalComponent, GlassComponent, GlowCardComponent, GlowComponent, GridSkeletonComponent, HANDOFF_ROUTE_PARAM, HANDOFF_TOKEN_PARAM, HandoffService, HasPermissionDirective, HeaderComponent, HintComponent, HorizontalScrollComponent, HourInputComponent, HrefComponent, I18nService, IMAGE_DEFAULTS, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, INVITATION_CARD_DEFAULTS, Icon, IconComponent, IconService, ImageComponent, ImageCropComponent, ImageService, InAppBrowserService, InfiniteListComponent, InfoComponent, InputI18nHelper, InputType, InvitationCardComponent, ItemListComponent, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LEGAL_CONTENT_CONFIG, LOGIN_DEFAULTS, LandingSplitComponent, LandingStepsComponent, LanguageSelectorComponent, LayeredCardComponent, LegalContentService, LegalLinkService, LinkComponent, LinkProcessorService, LinkedProvidersComponent, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LoadingDirective, LocalStorageService, LocaleService, LoginComponent, MEDIA_OBJECT_DEFAULTS, MEMBER_CARD_DEFAULTS, MODAL_SIZES, MOTIF_KEYS, MOTION, MaintenancePageComponent, MarkdownArticleParserService, MediaObjectComponent, MemberCardComponent, MenuComponent, MessagingService, MetaService, MfaModalComponent, ModalService, ModalShellComponent, MultiSelectSearchComponent, NUM_KEYS, NavigationService, NetworkBannerComponent, NetworkStatusService, NewsBuilder, NoContentComponent, NotesBoxComponent, NotificationActionService, NotificationsService, NumberFromToComponent, NumberInputComponent, NumberStepperComponent, OAUTH_PROVIDERS_INFO, OAuthCallbackComponent, OAuthService, OrgService, OrgSwitchService, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PATTERN_MOTIFS, PATTERN_PALETTES, PATTERN_STYLE_CONFIGS, PLATFORM_CONFIGS, PageContentComponent, PageLinksComponent, PageRefreshService, PageTemplateComponent, PageWavesComponent, PageWrapperComponent, PaginationComponent, PaginationService, PasswordInputComponent, PatternComponent, 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, REQUEST_STATUSES, RadioInputComponent, RangeInputComponent, RatingComponent, RefresherComponent, RequestFirestoreService, RequestFormBuilderService, RequestService, RightsFooterComponent, RotatingTextComponent, SEARCH_HEADER_DEFAULTS, SHAPE_KEYS, SHARE_PROFILE_MODAL_DEFAULTS, SKELETON_LAYOUT_DEFAULT_ROWS, SKELETON_PRESETS, SOLID_KEYS, STATS_BAR_DEFAULTS, STROKE_KEYS, SearchHeaderComponent, 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, ShareProfileModalComponent, SimpleComponent, SkeletonComponent, SkeletonLayoutComponent, SkeletonService, SolidBlockButton, SolidDefault, SolidDefaultBlock, SolidDefaultButton, SolidDefaultFull, SolidDefaultRound, SolidDefaultRoundBlock, SolidDefaultRoundButton, SolidDefaultRoundFull, SolidFullButton, SolidLargeButton, SolidLargeRoundButton, SolidSmallButton, SolidSmallRoundButton, SplashScreenService, StatsBarComponent, StatsCardComponent, StepperComponent, StorageService, SwipeCarouselComponent, TRI_KEYS, TabbedContentComponent, TableSkeletonComponent, TabsComponent, Terminal404Component, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeService, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TranslatePipe, TypedCollection, UPDATE_BANNER_DEFAULT_CONTENT, UPDATE_BANNER_I18N_NAMESPACE, UpdateBannerComponent, UserAvatarComponent, UsernameInputComponent, VALTECH_ADS_CONFIG, VALTECH_APP_CONFIG, VALTECH_APP_VERSION, VALTECH_AUTH_CONFIG, VALTECH_COMPANY_LINKS, VALTECH_CONTENT_CONFIG, VALTECH_DEBUG_CONSOLE, VALTECH_DEFAULT_CONTENT, VALTECH_DONATION_CONFIG, VALTECH_FEEDBACK_CONFIG, VALTECH_FIREBASE_CONFIG, VALTECH_FOOTER_I18N, VALTECH_FOOTER_LOGO, VALTECH_LANGUAGE_SELECTOR, VALTECH_LEGAL_CONFIG, VALTECH_NETWORK_ERROR_KEY, VALTECH_SITE_PATHS, VALTECH_SOCIAL_LINKS, VALTECH_SPLASH_SCREEN, VALTECH_WEB_BASE_URLS, VERSION, ValtechErrorService, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, authGuard, authInterceptor, authPasswordValidator, blogPost, buildFooterLinks, buildPath, collections, connectPageRefresh, createErrorStateProps, createFirebaseConfig, createGlowCardProps, createInitialPaginationState, createNumberFromToField, createRefreshableStream, createTitleProps, docs, errorLoggingInterceptor, extractPathParams, generatePatternTiles, generateRandomTile, getAppInfo, getAppVersion, getCollectionPath, getDocumentId, getTimeOfDayKey, goToTop, guestGuard, hasEmulators, interpretError, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isValidPath, joinPath, maxLength, mulberry32, news, parseMarkdownArticle, permissionGuard, permissionGuardFromRoute, provideLegalContent, provideSplashScreen, provideValtechAds, provideValtechAppConfig, provideValtechAppVersion, provideValtechAuth, provideValtechAuthInterceptor, provideValtechContent, provideValtechDebugConsole, provideValtechDonations, provideValtechErrorHandling, provideValtechFeedback, provideValtechFirebase, provideValtechI18n, provideValtechLegal, provideValtechPresets, provideValtechSite, provideValtechSkeleton, query, renderPatternSvgInner, replaceSpecialChars, resolveColor, resolveInputDefaultValue, resolveWebBaseUrl, roleGuard, storagePaths, superAdminGuard, toArticle };
51500
51733
  //# sourceMappingURL=valtech-components.mjs.map