valtech-components 2.0.711 → 2.0.713

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.
@@ -14,7 +14,7 @@ import { Meta, Title } from '@angular/platform-browser';
14
14
  import QRCodeStyling from 'qr-code-styling';
15
15
  import * as i1$3 from '@angular/forms';
16
16
  import { ReactiveFormsModule, FormsModule, FormControl, Validators, FormBuilder } from '@angular/forms';
17
- import { BehaviorSubject, map, Subject, debounceTime, distinctUntilChanged, switchMap, of, catchError, takeUntil, isObservable, firstValueFrom, Observable, throwError, from, filter as filter$1 } from 'rxjs';
17
+ import { BehaviorSubject, map, Subject, debounceTime, distinctUntilChanged, switchMap, of, catchError, takeUntil, isObservable, firstValueFrom, EMPTY, from, Observable, throwError, filter as filter$1 } from 'rxjs';
18
18
  import * as i1$4 from 'ng-otp-input';
19
19
  import { NgOtpInputComponent, NgOtpInputModule } from 'ng-otp-input';
20
20
  import * as i2 from '@ionic/angular';
@@ -29,7 +29,7 @@ import 'prismjs/components/prism-bash';
29
29
  import Swiper from 'swiper';
30
30
  import { Navigation, Pagination, EffectFade, EffectCube, EffectCoverflow, EffectFlip, Autoplay } from 'swiper/modules';
31
31
  import * as i1$5 from '@angular/fire/firestore';
32
- import { provideFirestore, getFirestore, connectFirestoreEmulator, enableIndexedDbPersistence, doc, getDoc, collection, query as query$1, getDocs, limit, docData, collectionData, serverTimestamp, addDoc, setDoc, updateDoc, deleteDoc, writeBatch, arrayUnion, arrayRemove, increment, where, orderBy, startAfter, startAt, endBefore, endAt, Timestamp } from '@angular/fire/firestore';
32
+ import { provideFirestore, getFirestore, connectFirestoreEmulator, enableIndexedDbPersistence, doc, getDoc, collection, query as query$1, getDocs, getCountFromServer, limit, docData, collectionData, serverTimestamp, addDoc, setDoc, updateDoc, deleteDoc, writeBatch, arrayUnion, arrayRemove, increment, where, orderBy, startAfter, startAt, endBefore, endAt, Timestamp } from '@angular/fire/firestore';
33
33
  import { Analytics, logEvent, setUserId, setUserProperties, provideAnalytics, getAnalytics } from '@angular/fire/analytics';
34
34
  import { provideFirebaseApp, initializeApp } from '@angular/fire/app';
35
35
  import * as i1$6 from '@angular/fire/auth';
@@ -38,7 +38,7 @@ import { provideMessaging, getMessaging, Messaging, getToken, deleteToken, onMes
38
38
  import * as i1$7 from '@angular/fire/storage';
39
39
  import { provideStorage, getStorage, connectStorageEmulator, ref, uploadBytesResumable, getDownloadURL, getMetadata, deleteObject, listAll } from '@angular/fire/storage';
40
40
  import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
41
- import { filter, map as map$1, catchError as catchError$1, tap, switchMap as switchMap$1, finalize, take, debounceTime as debounceTime$1, takeUntil as takeUntil$1 } from 'rxjs/operators';
41
+ import { filter, switchMap as switchMap$1, catchError as catchError$1, tap, map as map$1, finalize, take, debounceTime as debounceTime$1, takeUntil as takeUntil$1 } from 'rxjs/operators';
42
42
  import { ImageCropperComponent } from 'ngx-image-cropper';
43
43
  import * as i1$8 from '@angular/common/http';
44
44
  import { provideHttpClient, withInterceptors, HttpClient } from '@angular/common/http';
@@ -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.711';
53
+ const VERSION = '2.0.713';
54
54
 
55
55
  /**
56
56
  * Servicio para gestionar presets de componentes.
@@ -4364,6 +4364,24 @@ function provideValtechI18n(config = {}) {
4364
4364
  if (Object.keys(contentToRegister).length > 0) {
4365
4365
  i18n.registerContentBulk(contentToRegister);
4366
4366
  }
4367
+ // Aplicar idioma según configuración
4368
+ if (!mergedConfig.detectBrowserLanguage) {
4369
+ // Sin detección de navegador: forzar idioma por defecto
4370
+ i18n.setLanguage(mergedConfig.defaultLanguage);
4371
+ }
4372
+ else {
4373
+ // Con detección: re-evaluar ahora que supportedLanguages está configurado
4374
+ const stored = localStorage.getItem('app_lang');
4375
+ if (!stored || !mergedConfig.supportedLanguages.includes(stored)) {
4376
+ const browserLang = navigator.language.split('-')[0];
4377
+ if (mergedConfig.supportedLanguages.includes(browserLang)) {
4378
+ i18n.setLanguage(browserLang);
4379
+ }
4380
+ else {
4381
+ i18n.setLanguage(mergedConfig.defaultLanguage);
4382
+ }
4383
+ }
4384
+ }
4367
4385
  };
4368
4386
  },
4369
4387
  deps: [I18nService],
@@ -19756,7 +19774,19 @@ class FirestoreService {
19756
19774
  const constraints = this.buildQueryConstraints(options);
19757
19775
  const q = query$1(collectionRef, ...constraints);
19758
19776
  const snapshot = await getDocs(q);
19759
- return snapshot.docs.map((doc) => this.mapDocument(doc));
19777
+ return snapshot.docs.map(doc => this.mapDocument(doc));
19778
+ }
19779
+ /**
19780
+ * Cuenta documentos usando aggregation query del servidor.
19781
+ * No descarga los documentos — mucho más eficiente para conteos y badges.
19782
+ */
19783
+ async countDocs(collectionPath, options) {
19784
+ const prefixedPath = this.prefixCollectionPath(collectionPath);
19785
+ const collectionRef = collection(this.firestore, prefixedPath);
19786
+ const constraints = this.buildQueryConstraints(options);
19787
+ const q = query$1(collectionRef, ...constraints);
19788
+ const snapshot = await getCountFromServer(q);
19789
+ return snapshot.data().count;
19760
19790
  }
19761
19791
  /**
19762
19792
  * Obtiene documentos con paginación basada en cursores.
@@ -19796,7 +19826,7 @@ class FirestoreService {
19796
19826
  const resultDocs = hasMore ? docs.slice(0, -1) : docs;
19797
19827
  const lastDoc = resultDocs.length > 0 ? resultDocs[resultDocs.length - 1] : null;
19798
19828
  return {
19799
- data: resultDocs.map((doc) => this.mapDocument(doc)),
19829
+ data: resultDocs.map(doc => this.mapDocument(doc)),
19800
19830
  hasMore,
19801
19831
  lastDoc,
19802
19832
  };
@@ -19838,7 +19868,7 @@ class FirestoreService {
19838
19868
  docChanges(collectionPath, docId) {
19839
19869
  const prefixedPath = this.prefixCollectionPath(collectionPath);
19840
19870
  const docRef = doc(this.firestore, prefixedPath, docId);
19841
- return docData(docRef, { idField: 'id' }).pipe(map((data) => {
19871
+ return docData(docRef, { idField: 'id' }).pipe(map(data => {
19842
19872
  if (!data)
19843
19873
  return null;
19844
19874
  return this.convertTimestamps(data);
@@ -19864,7 +19894,7 @@ class FirestoreService {
19864
19894
  const collectionRef = collection(this.firestore, prefixedPath);
19865
19895
  const constraints = this.buildQueryConstraints(options);
19866
19896
  const q = query$1(collectionRef, ...constraints);
19867
- return collectionData(q, { idField: 'id' }).pipe(map((docs) => docs.map((doc) => this.convertTimestamps(doc))));
19897
+ return collectionData(q, { idField: 'id' }).pipe(map(docs => docs.map(doc => this.convertTimestamps(doc))));
19868
19898
  }
19869
19899
  // ===========================================================================
19870
19900
  // ESCRITURA
@@ -19896,9 +19926,11 @@ class FirestoreService {
19896
19926
  updatedAt: timestamp,
19897
19927
  };
19898
19928
  const docRef = await addDoc(collectionRef, docData);
19899
- // Obtener el documento creado para retornarlo con timestamps resueltos
19900
- const snapshot = await getDoc(docRef);
19901
- return this.mapDocument(snapshot);
19929
+ // Retornar optimísticamente sin segundo round-trip.
19930
+ // serverTimestamp() resuelve en el servidor; usamos Date.now() localmente
19931
+ // para evitar un getDoc() extra. El valor real llega en la próxima lectura.
19932
+ const now = new Date();
19933
+ return { id: docRef.id, ...data, createdAt: now, updatedAt: now };
19902
19934
  }
19903
19935
  /**
19904
19936
  * Crea o sobrescribe un documento con ID específico.
@@ -28351,13 +28383,12 @@ class TypedCollection {
28351
28383
  return results[0] ?? null;
28352
28384
  }
28353
28385
  /**
28354
- * Cuenta los documentos que coinciden con la query.
28355
- * Nota: Esto carga todos los documentos, usar con cuidado en colecciones grandes.
28386
+ * Cuenta los documentos que coinciden con la query (server-side aggregation).
28387
+ * No descarga documentos usa getCountFromServer() de Firestore.
28356
28388
  */
28357
28389
  async count(options) {
28358
28390
  const queryOptions = this.applyDefaultFilters(options);
28359
- const results = await this.firestore.getDocs(this.collectionPath, queryOptions);
28360
- return results.length;
28391
+ return this.firestore.countDocs(this.collectionPath, queryOptions);
28361
28392
  }
28362
28393
  /**
28363
28394
  * Obtiene todos los documentos (one-time fetch sin listener).
@@ -29930,6 +29961,7 @@ class NotificationsService {
29930
29961
  this.collectionFactory = collectionFactory;
29931
29962
  this.collection = null;
29932
29963
  this.currentUserId = null;
29964
+ this.collectionReady$ = new BehaviorSubject(null);
29933
29965
  // Inyección opcional - AuthService puede no estar configurado
29934
29966
  this.authService = null;
29935
29967
  // Intentar obtener AuthService de forma lazy (puede no estar configurado)
@@ -29985,6 +30017,7 @@ class NotificationsService {
29985
30017
  this.collection = this.collectionFactory.create(basePath, {
29986
30018
  timestamps: true,
29987
30019
  });
30020
+ this.collectionReady$.next(this.collection);
29988
30021
  console.log('[Notifications] Initialized with path:', basePath);
29989
30022
  }
29990
30023
  /**
@@ -30009,12 +30042,14 @@ class NotificationsService {
30009
30042
  * @param limit - Máximo de notificaciones a cargar (default: 50)
30010
30043
  */
30011
30044
  getAll(limit = DEFAULT_LIMIT) {
30012
- if (!this.collection)
30013
- return of([]);
30014
- return this.collection.watchAll({
30015
- orderBy: [{ field: 'createdAt', direction: 'desc' }],
30016
- limit,
30017
- });
30045
+ return this.collectionReady$.pipe(switchMap$1(collection => {
30046
+ if (!collection)
30047
+ return EMPTY;
30048
+ return collection.watchAll({
30049
+ orderBy: [{ field: 'createdAt', direction: 'desc' }],
30050
+ limit,
30051
+ });
30052
+ }));
30018
30053
  }
30019
30054
  /**
30020
30055
  * Obtiene notificaciones (one-time fetch sin listener).
@@ -30045,17 +30080,17 @@ class NotificationsService {
30045
30080
  });
30046
30081
  }
30047
30082
  /**
30048
- * Cuenta notificaciones no leídas (real-time, filtrado server-side).
30049
- * Útil para badges en UI.
30083
+ * Cuenta notificaciones no leídas usando server-side aggregation query.
30084
+ * No descarga documentos — eficiente para badges en UI.
30050
30085
  */
30051
30086
  getUnreadCount() {
30052
- if (!this.collection)
30053
- return of(0);
30054
- return this.collection
30055
- .watchAll({
30056
- where: [{ field: 'isRead', operator: '==', value: false }],
30057
- })
30058
- .pipe(map$1(n => n.length));
30087
+ return this.collectionReady$.pipe(switchMap$1(collection => {
30088
+ if (!collection)
30089
+ return of(0);
30090
+ return from(collection.count({
30091
+ where: [{ field: 'isRead', operator: '==', value: false }],
30092
+ }));
30093
+ }));
30059
30094
  }
30060
30095
  /**
30061
30096
  * Obtiene una notificación por ID.
@@ -30113,6 +30148,7 @@ class NotificationsService {
30113
30148
  reset() {
30114
30149
  this.collection = null;
30115
30150
  this.currentUserId = null;
30151
+ this.collectionReady$.next(null);
30116
30152
  }
30117
30153
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: NotificationsService, deps: [{ token: i0.Injector }, { token: FirestoreCollectionFactory, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
30118
30154
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: NotificationsService, providedIn: 'root' }); }
@@ -38461,6 +38497,1167 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
38461
38497
  type: Input
38462
38498
  }] } });
38463
38499
 
38500
+ /**
38501
+ * Content Transformer
38502
+ *
38503
+ * Transforms ContentDocument instances into ArticleMetadata
38504
+ * for rendering with the val-article component.
38505
+ */
38506
+ /**
38507
+ * Maps callout variants to Ionic colors
38508
+ */
38509
+ const CALLOUT_COLOR_MAP = {
38510
+ info: 'primary',
38511
+ warning: 'warning',
38512
+ success: 'success',
38513
+ error: 'danger',
38514
+ };
38515
+ /**
38516
+ * Maps callout variants to prefix text
38517
+ */
38518
+ const CALLOUT_PREFIX_MAP = {
38519
+ info: 'Info:',
38520
+ warning: 'Aviso:',
38521
+ success: 'Listo:',
38522
+ error: 'Error:',
38523
+ };
38524
+ /**
38525
+ * ContentTransformer converts ContentDocument objects into ArticleMetadata
38526
+ * that can be rendered by the val-article component.
38527
+ *
38528
+ * @example
38529
+ * ```typescript
38530
+ * const doc: BlogPost = { ... };
38531
+ * const article = ContentTransformer.toArticle(doc);
38532
+ * // Use article with <val-article [props]="article">
38533
+ * ```
38534
+ */
38535
+ class ContentTransformer {
38536
+ /**
38537
+ * Transforms a ContentDocument into ArticleMetadata
38538
+ *
38539
+ * @param doc - The content document to transform
38540
+ * @returns ArticleMetadata ready for val-article component
38541
+ */
38542
+ static toArticle(doc) {
38543
+ const builder = new ArticleBuilder();
38544
+ // Add header based on document type and meta
38545
+ this.addHeader(builder, doc);
38546
+ // Transform each content block
38547
+ for (const block of doc.content) {
38548
+ this.addBlock(builder, block);
38549
+ }
38550
+ // Add footer based on document type
38551
+ this.addFooter(builder, doc);
38552
+ return builder.build({
38553
+ theme: doc.config?.theme ?? 'auto',
38554
+ maxWidth: doc.config?.maxWidth ?? '800px',
38555
+ centered: doc.config?.centered ?? true,
38556
+ });
38557
+ }
38558
+ /**
38559
+ * Adds header elements based on document type and metadata
38560
+ */
38561
+ static addHeader(builder, doc) {
38562
+ // Add author/date info if showMeta is enabled
38563
+ if (doc.config?.showMeta && doc.meta.author) {
38564
+ this.addAuthorBlock(builder, doc.meta.author, doc.meta.publishedAt);
38565
+ builder.separator('space', { spacing: { top: 'small', bottom: 'medium' } });
38566
+ }
38567
+ }
38568
+ /**
38569
+ * Adds footer elements based on document type
38570
+ */
38571
+ static addFooter(builder, doc) {
38572
+ // Add tags if present
38573
+ if (doc.meta.tags && doc.meta.tags.length > 0) {
38574
+ builder.separator('line', { spacing: { top: 'large', bottom: 'medium' } });
38575
+ builder.paragraph({
38576
+ content: `Tags: ${doc.meta.tags.join(', ')}`,
38577
+ size: 'small',
38578
+ color: 'medium',
38579
+ bold: false,
38580
+ });
38581
+ }
38582
+ }
38583
+ /**
38584
+ * Adds author information block
38585
+ */
38586
+ static addAuthorBlock(builder, author, publishedAt) {
38587
+ let authorText = author.name;
38588
+ if (author.role) {
38589
+ authorText += ` - ${author.role}`;
38590
+ }
38591
+ if (publishedAt) {
38592
+ const date = typeof publishedAt === 'string' ? new Date(publishedAt) : publishedAt;
38593
+ authorText += ` | ${date.toLocaleDateString()}`;
38594
+ }
38595
+ builder.paragraph({
38596
+ content: authorText,
38597
+ size: 'small',
38598
+ color: 'medium',
38599
+ bold: false,
38600
+ });
38601
+ }
38602
+ /**
38603
+ * Transforms a single content block and adds it to the builder
38604
+ */
38605
+ static addBlock(builder, block) {
38606
+ switch (block.type) {
38607
+ case 'heading':
38608
+ this.addHeading(builder, block);
38609
+ break;
38610
+ case 'paragraph':
38611
+ this.addParagraph(builder, block);
38612
+ break;
38613
+ case 'quote':
38614
+ this.addQuote(builder, block);
38615
+ break;
38616
+ case 'code':
38617
+ this.addCode(builder, block);
38618
+ break;
38619
+ case 'list':
38620
+ this.addList(builder, block);
38621
+ break;
38622
+ case 'image':
38623
+ this.addImage(builder, block);
38624
+ break;
38625
+ case 'callout':
38626
+ this.addCallout(builder, block);
38627
+ break;
38628
+ case 'divider':
38629
+ this.addDivider(builder, block);
38630
+ break;
38631
+ case 'button':
38632
+ this.addButton(builder, block);
38633
+ break;
38634
+ case 'command':
38635
+ this.addCommand(builder, block);
38636
+ break;
38637
+ }
38638
+ }
38639
+ /**
38640
+ * Adds a heading block
38641
+ */
38642
+ static addHeading(builder, block) {
38643
+ if (block.level === 1) {
38644
+ builder.title({
38645
+ content: block.text,
38646
+ size: 'large',
38647
+ color: 'dark',
38648
+ bold: true,
38649
+ });
38650
+ }
38651
+ else {
38652
+ builder.subtitle({
38653
+ content: block.text,
38654
+ size: block.level === 2 ? 'medium' : 'small',
38655
+ color: 'dark',
38656
+ bold: true,
38657
+ });
38658
+ }
38659
+ }
38660
+ /**
38661
+ * Adds a paragraph block
38662
+ */
38663
+ static addParagraph(builder, block) {
38664
+ builder.paragraph({
38665
+ content: block.text,
38666
+ size: 'medium',
38667
+ color: 'dark',
38668
+ bold: block.emphasis ?? false,
38669
+ });
38670
+ }
38671
+ /**
38672
+ * Adds a quote block
38673
+ */
38674
+ static addQuote(builder, block) {
38675
+ builder.quote({
38676
+ content: block.text,
38677
+ size: 'medium',
38678
+ color: 'dark',
38679
+ bold: false,
38680
+ author: block.author,
38681
+ source: block.source,
38682
+ });
38683
+ }
38684
+ /**
38685
+ * Adds a code block
38686
+ */
38687
+ static addCode(builder, block) {
38688
+ builder.code(block.code, block.language);
38689
+ }
38690
+ /**
38691
+ * Adds a list block
38692
+ */
38693
+ static addList(builder, block) {
38694
+ const items = block.items.map((text) => ({ text }));
38695
+ const listType = block.checklist ? 'checklist' : block.ordered ? 'ordered' : 'unordered';
38696
+ builder.list(items, listType);
38697
+ }
38698
+ /**
38699
+ * Adds an image block
38700
+ */
38701
+ static addImage(builder, block) {
38702
+ builder.image(block.src, block.alt, block.caption, {
38703
+ // Note: alignment is handled differently in ArticleBuilder
38704
+ });
38705
+ }
38706
+ /**
38707
+ * Adds a callout/note block
38708
+ */
38709
+ static addCallout(builder, block) {
38710
+ const color = CALLOUT_COLOR_MAP[block.variant] ?? 'primary';
38711
+ const prefix = block.title ?? CALLOUT_PREFIX_MAP[block.variant] ?? 'Nota:';
38712
+ builder.note(block.text, prefix, color);
38713
+ }
38714
+ /**
38715
+ * Adds a divider/separator block
38716
+ */
38717
+ static addDivider(builder, block) {
38718
+ builder.separator(block.style ?? 'line');
38719
+ }
38720
+ /**
38721
+ * Adds a button block
38722
+ */
38723
+ static addButton(builder, block) {
38724
+ const colorMap = {
38725
+ primary: 'primary',
38726
+ secondary: 'secondary',
38727
+ success: 'success',
38728
+ warning: 'warning',
38729
+ danger: 'danger',
38730
+ };
38731
+ builder.button({
38732
+ text: block.text,
38733
+ color: colorMap[block.color ?? 'primary'] ?? 'primary',
38734
+ fill: 'solid',
38735
+ type: 'button',
38736
+ state: ComponentStates.ENABLED,
38737
+ href: block.href,
38738
+ token: block.action,
38739
+ }, block.alignment ?? 'center');
38740
+ }
38741
+ /**
38742
+ * Adds a command/terminal block
38743
+ */
38744
+ static addCommand(builder, block) {
38745
+ builder.command(block.command);
38746
+ }
38747
+ }
38748
+ /**
38749
+ * Convenience function to transform a ContentDocument to ArticleMetadata
38750
+ *
38751
+ * @param doc - The content document to transform
38752
+ * @returns ArticleMetadata ready for val-article component
38753
+ *
38754
+ * @example
38755
+ * ```typescript
38756
+ * const article = toArticle(myBlogPost);
38757
+ * ```
38758
+ */
38759
+ function toArticle(doc) {
38760
+ return ContentTransformer.toArticle(doc);
38761
+ }
38762
+
38763
+ /**
38764
+ * Blog Post Content Type
38765
+ *
38766
+ * Represents a blog post with title, excerpt, cover image,
38767
+ * and structured content blocks.
38768
+ */
38769
+ /**
38770
+ * Calculates estimated reading time based on word count
38771
+ * @param content - Array of content blocks
38772
+ * @returns Estimated minutes to read
38773
+ */
38774
+ function calculateReadingTime$1(content) {
38775
+ const wordsPerMinute = 200;
38776
+ let wordCount = 0;
38777
+ for (const block of content) {
38778
+ if ('text' in block && typeof block.text === 'string') {
38779
+ wordCount += block.text.split(/\s+/).length;
38780
+ }
38781
+ if (block.type === 'list') {
38782
+ wordCount += block.items.join(' ').split(/\s+/).length;
38783
+ }
38784
+ if (block.type === 'code') {
38785
+ wordCount += block.code.split(/\s+/).length * 0.5; // Code reads faster
38786
+ }
38787
+ }
38788
+ return Math.max(1, Math.ceil(wordCount / wordsPerMinute));
38789
+ }
38790
+ /**
38791
+ * BlogPostBuilder provides a fluent API for creating blog posts.
38792
+ *
38793
+ * @example
38794
+ * ```typescript
38795
+ * const post = new BlogPostBuilder()
38796
+ * .title('My First Post')
38797
+ * .excerpt('A brief introduction...')
38798
+ * .author('John Doe', '/avatars/john.jpg')
38799
+ * .coverImage('/images/post-cover.jpg')
38800
+ * .heading('Introduction')
38801
+ * .paragraph('Welcome to my blog...')
38802
+ * .build();
38803
+ *
38804
+ * // Convert to ArticleMetadata
38805
+ * const article = post.toArticle();
38806
+ * ```
38807
+ */
38808
+ class BlogPostBuilder {
38809
+ constructor() {
38810
+ this.post = {
38811
+ type: 'blog',
38812
+ content: [],
38813
+ meta: {},
38814
+ config: { showMeta: true },
38815
+ };
38816
+ }
38817
+ /**
38818
+ * Sets the blog post title
38819
+ */
38820
+ title(title) {
38821
+ this.post.title = title;
38822
+ return this;
38823
+ }
38824
+ /**
38825
+ * Sets the blog post excerpt
38826
+ */
38827
+ excerpt(excerpt) {
38828
+ this.post.excerpt = excerpt;
38829
+ return this;
38830
+ }
38831
+ /**
38832
+ * Sets the cover image URL
38833
+ */
38834
+ coverImage(url) {
38835
+ this.post.coverImage = url;
38836
+ return this;
38837
+ }
38838
+ /**
38839
+ * Marks the post as featured
38840
+ */
38841
+ featured(value = true) {
38842
+ this.post.featured = value;
38843
+ return this;
38844
+ }
38845
+ /**
38846
+ * Sets the post author
38847
+ */
38848
+ author(name, avatar, role) {
38849
+ this.post.meta = {
38850
+ ...this.post.meta,
38851
+ author: { name, avatar, role },
38852
+ };
38853
+ return this;
38854
+ }
38855
+ /**
38856
+ * Sets the publication date
38857
+ */
38858
+ publishedAt(date) {
38859
+ this.post.meta = {
38860
+ ...this.post.meta,
38861
+ publishedAt: date,
38862
+ };
38863
+ return this;
38864
+ }
38865
+ /**
38866
+ * Sets tags for the post
38867
+ */
38868
+ tags(...tags) {
38869
+ this.post.meta = {
38870
+ ...this.post.meta,
38871
+ tags,
38872
+ };
38873
+ return this;
38874
+ }
38875
+ /**
38876
+ * Sets the category
38877
+ */
38878
+ category(category) {
38879
+ this.post.meta = {
38880
+ ...this.post.meta,
38881
+ category,
38882
+ };
38883
+ return this;
38884
+ }
38885
+ /**
38886
+ * Sets the slug
38887
+ */
38888
+ slug(slug) {
38889
+ this.post.meta = {
38890
+ ...this.post.meta,
38891
+ slug,
38892
+ };
38893
+ return this;
38894
+ }
38895
+ /**
38896
+ * Sets the ID
38897
+ */
38898
+ id(id) {
38899
+ this.post.meta = {
38900
+ ...this.post.meta,
38901
+ id,
38902
+ };
38903
+ return this;
38904
+ }
38905
+ // Content block methods
38906
+ /**
38907
+ * Adds a heading block
38908
+ */
38909
+ heading(text, level = 2) {
38910
+ this.post.content.push({ type: 'heading', level, text });
38911
+ return this;
38912
+ }
38913
+ /**
38914
+ * Adds a paragraph block
38915
+ */
38916
+ paragraph(text, emphasis) {
38917
+ this.post.content.push({ type: 'paragraph', text, emphasis });
38918
+ return this;
38919
+ }
38920
+ /**
38921
+ * Adds a quote block
38922
+ */
38923
+ quote(text, author, source) {
38924
+ this.post.content.push({ type: 'quote', text, author, source });
38925
+ return this;
38926
+ }
38927
+ /**
38928
+ * Adds a code block
38929
+ */
38930
+ code(code, language) {
38931
+ this.post.content.push({ type: 'code', code, language });
38932
+ return this;
38933
+ }
38934
+ /**
38935
+ * Adds an unordered list
38936
+ */
38937
+ list(items) {
38938
+ this.post.content.push({ type: 'list', items });
38939
+ return this;
38940
+ }
38941
+ /**
38942
+ * Adds an ordered/numbered list
38943
+ */
38944
+ orderedList(items) {
38945
+ this.post.content.push({ type: 'list', items, ordered: true });
38946
+ return this;
38947
+ }
38948
+ /**
38949
+ * Adds a checklist
38950
+ */
38951
+ checklist(items) {
38952
+ this.post.content.push({ type: 'list', items, checklist: true });
38953
+ return this;
38954
+ }
38955
+ /**
38956
+ * Adds an image block
38957
+ */
38958
+ image(src, alt, caption) {
38959
+ this.post.content.push({ type: 'image', src, alt, caption });
38960
+ return this;
38961
+ }
38962
+ /**
38963
+ * Adds a callout/note block
38964
+ */
38965
+ callout(text, variant = 'info', title) {
38966
+ this.post.content.push({ type: 'callout', text, variant, title });
38967
+ return this;
38968
+ }
38969
+ /**
38970
+ * Adds a divider
38971
+ */
38972
+ divider(style) {
38973
+ this.post.content.push({ type: 'divider', style });
38974
+ return this;
38975
+ }
38976
+ /**
38977
+ * Adds a button/CTA
38978
+ */
38979
+ button(text, href, color) {
38980
+ this.post.content.push({ type: 'button', text, href, color });
38981
+ return this;
38982
+ }
38983
+ /**
38984
+ * Configures rendering options
38985
+ */
38986
+ config(config) {
38987
+ this.post.config = { ...this.post.config, ...config };
38988
+ return this;
38989
+ }
38990
+ /**
38991
+ * Builds the final BlogPost object
38992
+ */
38993
+ build() {
38994
+ const content = this.post.content || [];
38995
+ // Auto-calculate reading time if not set
38996
+ if (!this.post.readingTime) {
38997
+ this.post.readingTime = calculateReadingTime$1(content);
38998
+ }
38999
+ return {
39000
+ type: 'blog',
39001
+ title: this.post.title || 'Untitled Post',
39002
+ excerpt: this.post.excerpt || '',
39003
+ coverImage: this.post.coverImage,
39004
+ readingTime: this.post.readingTime,
39005
+ featured: this.post.featured,
39006
+ meta: this.post.meta,
39007
+ content,
39008
+ config: this.post.config,
39009
+ };
39010
+ }
39011
+ /**
39012
+ * Builds and converts to ArticleMetadata
39013
+ */
39014
+ toArticle() {
39015
+ return ContentTransformer.toArticle(this.build());
39016
+ }
39017
+ /**
39018
+ * Resets the builder for reuse
39019
+ */
39020
+ clear() {
39021
+ this.post = {
39022
+ type: 'blog',
39023
+ content: [],
39024
+ meta: {},
39025
+ config: { showMeta: true },
39026
+ };
39027
+ return this;
39028
+ }
39029
+ }
39030
+ /**
39031
+ * Creates a new BlogPostBuilder instance
39032
+ */
39033
+ function blogPost() {
39034
+ return new BlogPostBuilder();
39035
+ }
39036
+
39037
+ /**
39038
+ * Documentation Content Type
39039
+ *
39040
+ * Represents technical documentation with navigation,
39041
+ * sections, and structured content blocks.
39042
+ */
39043
+ /**
39044
+ * Generates a URL-friendly ID from text
39045
+ */
39046
+ function slugify(text) {
39047
+ return text
39048
+ .toLowerCase()
39049
+ .replace(/[^\w\s-]/g, '')
39050
+ .replace(/\s+/g, '-')
39051
+ .trim();
39052
+ }
39053
+ /**
39054
+ * Extracts table of contents from content blocks
39055
+ */
39056
+ function extractToc(content) {
39057
+ const toc = [];
39058
+ for (const block of content) {
39059
+ if (block.type === 'heading') {
39060
+ toc.push({
39061
+ text: block.text,
39062
+ level: block.level,
39063
+ id: slugify(block.text),
39064
+ });
39065
+ }
39066
+ }
39067
+ return toc;
39068
+ }
39069
+ /**
39070
+ * DocsBuilder provides a fluent API for creating documentation pages.
39071
+ *
39072
+ * @example
39073
+ * ```typescript
39074
+ * const docs = new DocsBuilder()
39075
+ * .title('Installation Guide')
39076
+ * .section('Getting Started')
39077
+ * .version('1.0.0')
39078
+ * .heading('Prerequisites', 1)
39079
+ * .paragraph('Before you begin, ensure you have...')
39080
+ * .callout('Node.js 18+ is required', 'warning')
39081
+ * .heading('Installation', 1)
39082
+ * .code('npm install valtech-components', 'bash')
39083
+ * .prevPage('Introduction', '/docs/intro')
39084
+ * .nextPage('Configuration', '/docs/config')
39085
+ * .build();
39086
+ *
39087
+ * // Convert to ArticleMetadata
39088
+ * const article = docs.toArticle();
39089
+ * ```
39090
+ */
39091
+ class DocsBuilder {
39092
+ constructor() {
39093
+ this.doc = {
39094
+ type: 'docs',
39095
+ content: [],
39096
+ meta: {},
39097
+ config: { showMeta: false, showTableOfContents: true },
39098
+ };
39099
+ }
39100
+ /**
39101
+ * Sets the documentation page title
39102
+ */
39103
+ title(title) {
39104
+ this.doc.title = title;
39105
+ return this;
39106
+ }
39107
+ /**
39108
+ * Sets the section name
39109
+ */
39110
+ section(section) {
39111
+ this.doc.section = section;
39112
+ return this;
39113
+ }
39114
+ /**
39115
+ * Sets the documentation version
39116
+ */
39117
+ version(version) {
39118
+ this.doc.version = version;
39119
+ return this;
39120
+ }
39121
+ /**
39122
+ * Sets the previous page navigation
39123
+ */
39124
+ prevPage(title, slug) {
39125
+ this.doc.prevPage = { title, slug };
39126
+ return this;
39127
+ }
39128
+ /**
39129
+ * Sets the next page navigation
39130
+ */
39131
+ nextPage(title, slug) {
39132
+ this.doc.nextPage = { title, slug };
39133
+ return this;
39134
+ }
39135
+ /**
39136
+ * Sets the slug for the page
39137
+ */
39138
+ slug(slug) {
39139
+ this.doc.meta = {
39140
+ ...this.doc.meta,
39141
+ slug,
39142
+ };
39143
+ return this;
39144
+ }
39145
+ /**
39146
+ * Sets the ID for the page
39147
+ */
39148
+ id(id) {
39149
+ this.doc.meta = {
39150
+ ...this.doc.meta,
39151
+ id,
39152
+ };
39153
+ return this;
39154
+ }
39155
+ /**
39156
+ * Sets tags for the documentation page
39157
+ */
39158
+ tags(...tags) {
39159
+ this.doc.meta = {
39160
+ ...this.doc.meta,
39161
+ tags,
39162
+ };
39163
+ return this;
39164
+ }
39165
+ /**
39166
+ * Sets the category
39167
+ */
39168
+ category(category) {
39169
+ this.doc.meta = {
39170
+ ...this.doc.meta,
39171
+ category,
39172
+ };
39173
+ return this;
39174
+ }
39175
+ // Content block methods
39176
+ /**
39177
+ * Adds a heading block
39178
+ */
39179
+ heading(text, level = 2) {
39180
+ this.doc.content.push({ type: 'heading', level, text });
39181
+ return this;
39182
+ }
39183
+ /**
39184
+ * Adds a paragraph block
39185
+ */
39186
+ paragraph(text, emphasis) {
39187
+ this.doc.content.push({ type: 'paragraph', text, emphasis });
39188
+ return this;
39189
+ }
39190
+ /**
39191
+ * Adds a code block
39192
+ */
39193
+ code(code, language) {
39194
+ this.doc.content.push({ type: 'code', code, language });
39195
+ return this;
39196
+ }
39197
+ /**
39198
+ * Adds a command/terminal block
39199
+ */
39200
+ command(command) {
39201
+ this.doc.content.push({ type: 'command', command });
39202
+ return this;
39203
+ }
39204
+ /**
39205
+ * Adds a callout/note block
39206
+ */
39207
+ callout(text, variant = 'info', title) {
39208
+ this.doc.content.push({ type: 'callout', text, variant, title });
39209
+ return this;
39210
+ }
39211
+ /**
39212
+ * Adds an info callout (alias for callout with info variant)
39213
+ */
39214
+ info(text, title) {
39215
+ return this.callout(text, 'info', title);
39216
+ }
39217
+ /**
39218
+ * Adds a warning callout
39219
+ */
39220
+ warning(text, title) {
39221
+ return this.callout(text, 'warning', title);
39222
+ }
39223
+ /**
39224
+ * Adds an error callout
39225
+ */
39226
+ error(text, title) {
39227
+ return this.callout(text, 'error', title);
39228
+ }
39229
+ /**
39230
+ * Adds a success callout
39231
+ */
39232
+ success(text, title) {
39233
+ return this.callout(text, 'success', title);
39234
+ }
39235
+ /**
39236
+ * Adds an unordered list
39237
+ */
39238
+ list(items) {
39239
+ this.doc.content.push({ type: 'list', items });
39240
+ return this;
39241
+ }
39242
+ /**
39243
+ * Adds an ordered/numbered list
39244
+ */
39245
+ orderedList(items) {
39246
+ this.doc.content.push({ type: 'list', items, ordered: true });
39247
+ return this;
39248
+ }
39249
+ /**
39250
+ * Adds a checklist
39251
+ */
39252
+ checklist(items) {
39253
+ this.doc.content.push({ type: 'list', items, checklist: true });
39254
+ return this;
39255
+ }
39256
+ /**
39257
+ * Adds an image block
39258
+ */
39259
+ image(src, alt, caption) {
39260
+ this.doc.content.push({ type: 'image', src, alt, caption });
39261
+ return this;
39262
+ }
39263
+ /**
39264
+ * Adds a quote block
39265
+ */
39266
+ quote(text, author, source) {
39267
+ this.doc.content.push({ type: 'quote', text, author, source });
39268
+ return this;
39269
+ }
39270
+ /**
39271
+ * Adds a divider
39272
+ */
39273
+ divider(style) {
39274
+ this.doc.content.push({ type: 'divider', style });
39275
+ return this;
39276
+ }
39277
+ /**
39278
+ * Adds a button/CTA
39279
+ */
39280
+ button(text, href, color) {
39281
+ this.doc.content.push({ type: 'button', text, href, color });
39282
+ return this;
39283
+ }
39284
+ /**
39285
+ * Configures rendering options
39286
+ */
39287
+ config(config) {
39288
+ this.doc.config = { ...this.doc.config, ...config };
39289
+ return this;
39290
+ }
39291
+ /**
39292
+ * Builds the final Documentation object
39293
+ */
39294
+ build() {
39295
+ const content = this.doc.content || [];
39296
+ // Auto-generate TOC if not provided and showTableOfContents is enabled
39297
+ if (!this.doc.toc && this.doc.config?.showTableOfContents) {
39298
+ this.doc.toc = extractToc(content);
39299
+ }
39300
+ return {
39301
+ type: 'docs',
39302
+ title: this.doc.title || 'Untitled Documentation',
39303
+ section: this.doc.section,
39304
+ version: this.doc.version,
39305
+ prevPage: this.doc.prevPage,
39306
+ nextPage: this.doc.nextPage,
39307
+ toc: this.doc.toc,
39308
+ meta: this.doc.meta,
39309
+ content,
39310
+ config: this.doc.config,
39311
+ };
39312
+ }
39313
+ /**
39314
+ * Builds and converts to ArticleMetadata
39315
+ */
39316
+ toArticle() {
39317
+ return ContentTransformer.toArticle(this.build());
39318
+ }
39319
+ /**
39320
+ * Resets the builder for reuse
39321
+ */
39322
+ clear() {
39323
+ this.doc = {
39324
+ type: 'docs',
39325
+ content: [],
39326
+ meta: {},
39327
+ config: { showMeta: false, showTableOfContents: true },
39328
+ };
39329
+ return this;
39330
+ }
39331
+ }
39332
+ /**
39333
+ * Creates a new DocsBuilder instance
39334
+ */
39335
+ function docs() {
39336
+ return new DocsBuilder();
39337
+ }
39338
+
39339
+ /**
39340
+ * News Article Content Type
39341
+ *
39342
+ * Represents a news article or announcement with headline,
39343
+ * summary, featured image, and structured content blocks.
39344
+ */
39345
+ /**
39346
+ * Calculates estimated reading time based on word count
39347
+ * @param content - Array of content blocks
39348
+ * @returns Estimated minutes to read
39349
+ */
39350
+ function calculateReadingTime(content) {
39351
+ const wordsPerMinute = 200;
39352
+ let wordCount = 0;
39353
+ for (const block of content) {
39354
+ if ('text' in block && typeof block.text === 'string') {
39355
+ wordCount += block.text.split(/\s+/).length;
39356
+ }
39357
+ if (block.type === 'list') {
39358
+ wordCount += block.items.join(' ').split(/\s+/).length;
39359
+ }
39360
+ }
39361
+ return Math.max(1, Math.ceil(wordCount / wordsPerMinute));
39362
+ }
39363
+ /**
39364
+ * NewsBuilder provides a fluent API for creating news articles.
39365
+ *
39366
+ * @example
39367
+ * ```typescript
39368
+ * const news = new NewsBuilder()
39369
+ * .headline('New Product Launch Announced')
39370
+ * .summary('Company reveals groundbreaking new product...')
39371
+ * .author('Jane Smith', '/avatars/jane.jpg', 'Technology Reporter')
39372
+ * .publishedAt(new Date())
39373
+ * .breaking()
39374
+ * .featuredImage('/images/product-launch.jpg')
39375
+ * .paragraph('In a surprise announcement today...')
39376
+ * .quote('This is our most innovative product yet', 'CEO John Doe')
39377
+ * .build();
39378
+ *
39379
+ * // Convert to ArticleMetadata
39380
+ * const article = news.toArticle();
39381
+ * ```
39382
+ */
39383
+ class NewsBuilder {
39384
+ constructor() {
39385
+ this.article = {
39386
+ type: 'news',
39387
+ content: [],
39388
+ meta: {},
39389
+ config: { showMeta: true },
39390
+ };
39391
+ }
39392
+ /**
39393
+ * Sets the news headline
39394
+ */
39395
+ headline(headline) {
39396
+ this.article.headline = headline;
39397
+ return this;
39398
+ }
39399
+ /**
39400
+ * Sets the news summary
39401
+ */
39402
+ summary(summary) {
39403
+ this.article.summary = summary;
39404
+ return this;
39405
+ }
39406
+ /**
39407
+ * Sets the featured image URL
39408
+ */
39409
+ featuredImage(url) {
39410
+ this.article.featuredImage = url;
39411
+ return this;
39412
+ }
39413
+ /**
39414
+ * Marks the article as breaking news
39415
+ */
39416
+ breaking(value = true) {
39417
+ this.article.breaking = value;
39418
+ return this;
39419
+ }
39420
+ /**
39421
+ * Sets the news source/outlet
39422
+ */
39423
+ source(source) {
39424
+ this.article.source = source;
39425
+ return this;
39426
+ }
39427
+ /**
39428
+ * Sets the article author
39429
+ */
39430
+ author(name, avatar, role) {
39431
+ this.article.meta = {
39432
+ ...this.article.meta,
39433
+ author: { name, avatar, role },
39434
+ };
39435
+ return this;
39436
+ }
39437
+ /**
39438
+ * Sets the publication date
39439
+ */
39440
+ publishedAt(date) {
39441
+ this.article.meta = {
39442
+ ...this.article.meta,
39443
+ publishedAt: date,
39444
+ };
39445
+ return this;
39446
+ }
39447
+ /**
39448
+ * Sets tags for the article
39449
+ */
39450
+ tags(...tags) {
39451
+ this.article.meta = {
39452
+ ...this.article.meta,
39453
+ tags,
39454
+ };
39455
+ return this;
39456
+ }
39457
+ /**
39458
+ * Sets the category
39459
+ */
39460
+ category(category) {
39461
+ this.article.meta = {
39462
+ ...this.article.meta,
39463
+ category,
39464
+ };
39465
+ return this;
39466
+ }
39467
+ /**
39468
+ * Sets the slug
39469
+ */
39470
+ slug(slug) {
39471
+ this.article.meta = {
39472
+ ...this.article.meta,
39473
+ slug,
39474
+ };
39475
+ return this;
39476
+ }
39477
+ /**
39478
+ * Sets the ID
39479
+ */
39480
+ id(id) {
39481
+ this.article.meta = {
39482
+ ...this.article.meta,
39483
+ id,
39484
+ };
39485
+ return this;
39486
+ }
39487
+ /**
39488
+ * Adds related articles
39489
+ */
39490
+ relatedArticles(articles) {
39491
+ this.article.relatedArticles = articles;
39492
+ return this;
39493
+ }
39494
+ // Content block methods
39495
+ /**
39496
+ * Adds a heading block
39497
+ */
39498
+ heading(text, level = 2) {
39499
+ this.article.content.push({ type: 'heading', level, text });
39500
+ return this;
39501
+ }
39502
+ /**
39503
+ * Adds a paragraph block
39504
+ */
39505
+ paragraph(text, emphasis) {
39506
+ this.article.content.push({ type: 'paragraph', text, emphasis });
39507
+ return this;
39508
+ }
39509
+ /**
39510
+ * Adds a quote block
39511
+ */
39512
+ quote(text, author, source) {
39513
+ this.article.content.push({ type: 'quote', text, author, source });
39514
+ return this;
39515
+ }
39516
+ /**
39517
+ * Adds an image block
39518
+ */
39519
+ image(src, alt, caption) {
39520
+ this.article.content.push({ type: 'image', src, alt, caption });
39521
+ return this;
39522
+ }
39523
+ /**
39524
+ * Adds an unordered list
39525
+ */
39526
+ list(items) {
39527
+ this.article.content.push({ type: 'list', items });
39528
+ return this;
39529
+ }
39530
+ /**
39531
+ * Adds an ordered/numbered list
39532
+ */
39533
+ orderedList(items) {
39534
+ this.article.content.push({ type: 'list', items, ordered: true });
39535
+ return this;
39536
+ }
39537
+ /**
39538
+ * Adds a callout/note block
39539
+ */
39540
+ callout(text, variant = 'info', title) {
39541
+ this.article.content.push({ type: 'callout', text, variant, title });
39542
+ return this;
39543
+ }
39544
+ /**
39545
+ * Adds a divider
39546
+ */
39547
+ divider(style) {
39548
+ this.article.content.push({ type: 'divider', style });
39549
+ return this;
39550
+ }
39551
+ /**
39552
+ * Adds a button/CTA
39553
+ */
39554
+ button(text, href, color) {
39555
+ this.article.content.push({ type: 'button', text, href, color });
39556
+ return this;
39557
+ }
39558
+ /**
39559
+ * Configures rendering options
39560
+ */
39561
+ config(config) {
39562
+ this.article.config = { ...this.article.config, ...config };
39563
+ return this;
39564
+ }
39565
+ /**
39566
+ * Builds the final NewsArticle object
39567
+ */
39568
+ build() {
39569
+ const content = this.article.content || [];
39570
+ return {
39571
+ type: 'news',
39572
+ headline: this.article.headline || 'Untitled News',
39573
+ summary: this.article.summary || '',
39574
+ featuredImage: this.article.featuredImage,
39575
+ breaking: this.article.breaking,
39576
+ source: this.article.source,
39577
+ relatedArticles: this.article.relatedArticles,
39578
+ meta: this.article.meta,
39579
+ content,
39580
+ config: this.article.config,
39581
+ };
39582
+ }
39583
+ /**
39584
+ * Builds and converts to ArticleMetadata
39585
+ */
39586
+ toArticle() {
39587
+ return ContentTransformer.toArticle(this.build());
39588
+ }
39589
+ /**
39590
+ * Gets the estimated reading time in minutes
39591
+ */
39592
+ getReadingTime() {
39593
+ return calculateReadingTime(this.article.content || []);
39594
+ }
39595
+ /**
39596
+ * Resets the builder for reuse
39597
+ */
39598
+ clear() {
39599
+ this.article = {
39600
+ type: 'news',
39601
+ content: [],
39602
+ meta: {},
39603
+ config: { showMeta: true },
39604
+ };
39605
+ return this;
39606
+ }
39607
+ }
39608
+ /**
39609
+ * Creates a new NewsBuilder instance
39610
+ */
39611
+ function news() {
39612
+ return new NewsBuilder();
39613
+ }
39614
+
39615
+ /**
39616
+ * Content Types Module
39617
+ *
39618
+ * Provides a flexible content abstraction layer that transforms
39619
+ * structured content documents into ArticleMetadata for rendering
39620
+ * with the val-article component.
39621
+ *
39622
+ * @example
39623
+ * ```typescript
39624
+ * // Using BlogPostBuilder
39625
+ * import { blogPost } from 'valtech-components';
39626
+ *
39627
+ * const post = blogPost()
39628
+ * .title('My First Post')
39629
+ * .author('John Doe')
39630
+ * .heading('Introduction')
39631
+ * .paragraph('Welcome to my blog...')
39632
+ * .toArticle();
39633
+ *
39634
+ * // Using DocsBuilder
39635
+ * import { docs } from 'valtech-components';
39636
+ *
39637
+ * const page = docs()
39638
+ * .title('Installation')
39639
+ * .section('Getting Started')
39640
+ * .code('npm install valtech-components', 'bash')
39641
+ * .toArticle();
39642
+ *
39643
+ * // Using NewsBuilder
39644
+ * import { news } from 'valtech-components';
39645
+ *
39646
+ * const article = news()
39647
+ * .headline('Breaking News')
39648
+ * .summary('Important announcement...')
39649
+ * .breaking()
39650
+ * .toArticle();
39651
+ *
39652
+ * // From JSON/API response
39653
+ * import { ContentTransformer, BlogPost } from 'valtech-components';
39654
+ *
39655
+ * const json: BlogPost = await fetch('/api/posts/1').then(r => r.json());
39656
+ * const article = ContentTransformer.toArticle(json);
39657
+ * ```
39658
+ */
39659
+ // Transformer
39660
+
38464
39661
  /**
38465
39662
  * Token de inyección para la configuración de Feedback.
38466
39663
  */
@@ -42173,5 +43370,5 @@ function buildFooterLinks(links, t) {
42173
43370
  * Generated bundle index. Do not edit.
42174
43371
  */
42175
43372
 
42176
- export { ACTION_CARD_DEFAULTS, AD_SIZE_MAP, API_TABLE_COLUMN_LABELS, ARTICLE_SPACING, AVATAR_UPLOAD_DEFAULTS, AccordionComponent, ActionCardComponent, ActionHeaderComponent, ActionType, AdSlotComponent, AdsLoaderService, AdsService, AlertBoxComponent, AnalyticsErrorHandler, AnalyticsRouterTracker, AnalyticsService, AppConfigService, ArticleBuilder, ArticleComponent, AuthBackgroundComponent, AuthService, AuthStateService, AuthStorageService, AuthSyncService, AvatarComponent, AvatarUploadComponent, BOTTOM_NAV_DEFAULTS, BannerComponent, BaseDefault, BottomNavComponent, BoxComponent, BreadcrumbComponent, ButtonComponent, ButtonGroupComponent, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CURRENCY_INFO, CardComponent, CardSection, CardType, CardsCarouselComponent, CheckInputComponent, CheckboxRadioInputComponent, ChipGroupComponent, ClearDefault, ClearDefaultBlock, ClearDefaultFull, ClearDefaultRound, ClearDefaultRoundBlock, ClearDefaultRoundFull, CodeDisplayComponent, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ContentLoaderComponent, ContentReactionComponent, CountdownComponent, CurrencyInputComponent, DEFAULT_ADS_CONFIG, DEFAULT_APP_CONFIG_SERVICE_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_BACK_HEADER, DEFAULT_CANCEL_BUTTON, DEFAULT_CONFIRM_BUTTON, DEFAULT_COUNTDOWN_LABELS, DEFAULT_COUNTDOWN_LABELS_EN, DEFAULT_EMPTY_STATE, DEFAULT_EMULATOR_CONFIG, DEFAULT_FEEDBACK_CONFIG, DEFAULT_FEEDBACK_TYPE_OPTIONS, DEFAULT_HOME_HEADER, DEFAULT_INFINITE_LIST_METADATA, DEFAULT_LEGEND_LABELS, DEFAULT_MODAL_CANCEL_BUTTON, DEFAULT_MODAL_CONFIRM_BUTTON, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PAYMENT_STATUS_COLORS, DEFAULT_PAYMENT_STATUS_LABELS, DEFAULT_PLATFORMS, DEFAULT_REFRESHER_METADATA, DEFAULT_SKELETON_CONFIG, DEFAULT_STATUS_COLORS, DEFAULT_STATUS_LABELS, DEFAULT_WINNER_LABELS, DataTableComponent, DateInputComponent, DateRangeInputComponent, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsCalloutComponent, DocsCodeExampleComponent, DocsLayoutComponent, DocsNavLinksComponent, DocsNavigationService, DocsPageComponent, DocsSearchComponent, DocsSectionComponent, DocsShellComponent, DocsSidebarComponent, DocsTocComponent, DownloadService, EmailInputComponent, ExpandableTextComponent, FEATURES_LIST_DEFAULTS, FabComponent, FeaturesListComponent, FeedbackFormComponent, FeedbackService, FileInputComponent, FirebaseService, FirestoreCollectionFactory, FirestoreService, FooterComponent, FooterLinksComponent, FormComponent, FormFooterComponent, FormSkeletonComponent, FunHeaderComponent, GlowCardComponent, GridSkeletonComponent, HeaderComponent, HintComponent, HorizontalScrollComponent, HourInputComponent, HrefComponent, I18nService, IMAGE_DEFAULTS, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, Icon, IconComponent, IconService, ImageComponent, ImageCropComponent, ImageService, InAppBrowserService, InfiniteListComponent, InfoComponent, InputI18nHelper, InputType, ItemListComponent, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LOGIN_DEFAULTS, LanguageSelectorComponent, LayeredCardComponent, LayoutComponent, LinkComponent, LinkProcessorService, LinkedProvidersComponent, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LoadingDirective, LocalStorageService, LocaleService, LoginComponent, MODAL_SIZES, MOTION, MaintenancePageComponent, MenuComponent, MessagingService, MetaService, ModalService, MultiSelectSearchComponent, NavigationService, NoContentComponent, NotesBoxComponent, NotificationsService, NumberFromToComponent, NumberInputComponent, NumberStepperComponent, OAUTH_PROVIDERS_INFO, OAuthCallbackComponent, OAuthService, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PLATFORM_CONFIGS, PageContentComponent, PageTemplateComponent, PageWrapperComponent, PaginationComponent, PaginationService, ParticipantCardComponent, PasswordInputComponent, PhoneInputComponent, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PresetService, PriceTagComponent, PrimarySolidBlockButton, PrimarySolidBlockHrefButton, PrimarySolidBlockIconButton, PrimarySolidBlockIconHrefButton, PrimarySolidDefaultRoundButton, PrimarySolidDefaultRoundHrefButton, PrimarySolidDefaultRoundIconButton, PrimarySolidDefaultRoundIconHrefButton, PrimarySolidFullButton, PrimarySolidFullHrefButton, PrimarySolidFullIconButton, PrimarySolidFullIconHrefButton, PrimarySolidLargeRoundButton, PrimarySolidLargeRoundHrefButton, PrimarySolidLargeRoundIconButton, PrimarySolidLargeRoundIconHrefButton, PrimarySolidSmallRoundButton, PrimarySolidSmallRoundHrefButton, PrimarySolidSmallRoundIconButton, PrimarySolidSmallRoundIconHrefButton, ProcessLinksPipe, ProfileSkeletonComponent, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrCodeComponent, QrGeneratorService, QueryBuilder, QuoteBoxComponent, RadioInputComponent, RaffleStatusCardComponent, RangeInputComponent, RatingComponent, RecapCardComponent, RefresherComponent, RightsFooterComponent, RotatingTextComponent, SKELETON_PRESETS, SearchSelectorComponent, SearchbarComponent, SecondarySolidBlockButton, SecondarySolidBlockHrefButton, SecondarySolidBlockIconButton, SecondarySolidBlockIconHrefButton, SecondarySolidDefaultRoundButton, SecondarySolidDefaultRoundHrefButton, SecondarySolidDefaultRoundIconButton, SecondarySolidDefaultRoundIconHrefButton, SecondarySolidFullButton, SecondarySolidFullHrefButton, SecondarySolidFullIconButton, SecondarySolidFullIconHrefButton, SecondarySolidLargeRoundButton, SecondarySolidLargeRoundHrefButton, SecondarySolidLargeRoundIconButton, SecondarySolidLargeRoundIconHrefButton, SecondarySolidSmallRoundButton, SecondarySolidSmallRoundHrefButton, SecondarySolidSmallRoundIconButton, SecondarySolidSmallRoundIconHrefButton, SegmentControlComponent, SelectSearchComponent, SessionService, ShareButtonsComponent, SimpleComponent, SkeletonComponent, SkeletonService, SolidBlockButton, SolidDefault, SolidDefaultBlock, SolidDefaultButton, SolidDefaultFull, SolidDefaultRound, SolidDefaultRoundBlock, SolidDefaultRoundButton, SolidDefaultRoundFull, SolidFullButton, SolidLargeButton, SolidLargeRoundButton, SolidSmallButton, SolidSmallRoundButton, StatsCardComponent, StepperComponent, StorageService, SwipeCarouselComponent, TabbedContentComponent, TableSkeletonComponent, TabsComponent, Terminal404Component, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeService, TicketGridComponent, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TranslatePipe, TypedCollection, UpdateBannerComponent, UsernameInputComponent, VALTECH_ADS_CONFIG, VALTECH_APP_CONFIG, VALTECH_AUTH_CONFIG, VALTECH_COMPANY_LINKS, VALTECH_DEFAULT_CONTENT, VALTECH_FEEDBACK_CONFIG, VALTECH_FIREBASE_CONFIG, VALTECH_FOOTER_I18N, VALTECH_FOOTER_LOGO, VALTECH_LANGUAGE_SELECTOR, VALTECH_SOCIAL_LINKS, VERSION, WinnerDisplayComponent, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, authGuard, authInterceptor, buildFooterLinks, buildPath, collections, createFirebaseConfig, createGlowCardProps, createInitialPaginationState, createNumberFromToField, createTitleProps, extractPathParams, getAppInfo, getAppVersion, getCollectionPath, getDocumentId, goToTop, guestGuard, hasEmulators, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isValidPath, joinPath, maxLength, permissionGuard, permissionGuardFromRoute, provideValtechAds, provideValtechAppConfig, provideValtechAuth, provideValtechAuthInterceptor, provideValtechFeedback, provideValtechFirebase, provideValtechI18n, provideValtechPresets, provideValtechSkeleton, query, replaceSpecialChars, resolveColor, resolveInputDefaultValue, roleGuard, storagePaths, superAdminGuard };
43373
+ 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, 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_LEGEND_LABELS, DEFAULT_MODAL_CANCEL_BUTTON, DEFAULT_MODAL_CONFIRM_BUTTON, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PAYMENT_STATUS_COLORS, DEFAULT_PAYMENT_STATUS_LABELS, DEFAULT_PLATFORMS, DEFAULT_REFRESHER_METADATA, DEFAULT_SKELETON_CONFIG, DEFAULT_STATUS_COLORS, DEFAULT_STATUS_LABELS, DEFAULT_WINNER_LABELS, DataTableComponent, DateInputComponent, DateRangeInputComponent, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, 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, HeaderComponent, HintComponent, HorizontalScrollComponent, HourInputComponent, HrefComponent, I18nService, IMAGE_DEFAULTS, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, Icon, IconComponent, IconService, ImageComponent, ImageCropComponent, ImageService, InAppBrowserService, InfiniteListComponent, InfoComponent, InputI18nHelper, InputType, ItemListComponent, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LOGIN_DEFAULTS, LanguageSelectorComponent, LayeredCardComponent, LayoutComponent, LinkComponent, LinkProcessorService, LinkedProvidersComponent, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LoadingDirective, LocalStorageService, LocaleService, LoginComponent, MODAL_SIZES, MOTION, MaintenancePageComponent, MenuComponent, MessagingService, MetaService, ModalService, MultiSelectSearchComponent, NavigationService, NewsBuilder, NoContentComponent, NotesBoxComponent, NotificationsService, NumberFromToComponent, NumberInputComponent, NumberStepperComponent, OAUTH_PROVIDERS_INFO, OAuthCallbackComponent, OAuthService, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PLATFORM_CONFIGS, PageContentComponent, PageTemplateComponent, PageWrapperComponent, PaginationComponent, PaginationService, ParticipantCardComponent, PasswordInputComponent, PhoneInputComponent, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PresetService, PriceTagComponent, PrimarySolidBlockButton, PrimarySolidBlockHrefButton, PrimarySolidBlockIconButton, PrimarySolidBlockIconHrefButton, PrimarySolidDefaultRoundButton, PrimarySolidDefaultRoundHrefButton, PrimarySolidDefaultRoundIconButton, PrimarySolidDefaultRoundIconHrefButton, PrimarySolidFullButton, PrimarySolidFullHrefButton, PrimarySolidFullIconButton, PrimarySolidFullIconHrefButton, PrimarySolidLargeRoundButton, PrimarySolidLargeRoundHrefButton, PrimarySolidLargeRoundIconButton, PrimarySolidLargeRoundIconHrefButton, PrimarySolidSmallRoundButton, PrimarySolidSmallRoundHrefButton, PrimarySolidSmallRoundIconButton, PrimarySolidSmallRoundIconHrefButton, ProcessLinksPipe, ProfileSkeletonComponent, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrCodeComponent, QrGeneratorService, QueryBuilder, QuoteBoxComponent, RadioInputComponent, RaffleStatusCardComponent, RangeInputComponent, RatingComponent, RecapCardComponent, RefresherComponent, RightsFooterComponent, RotatingTextComponent, SKELETON_PRESETS, SearchSelectorComponent, SearchbarComponent, SecondarySolidBlockButton, SecondarySolidBlockHrefButton, SecondarySolidBlockIconButton, SecondarySolidBlockIconHrefButton, SecondarySolidDefaultRoundButton, SecondarySolidDefaultRoundHrefButton, SecondarySolidDefaultRoundIconButton, SecondarySolidDefaultRoundIconHrefButton, SecondarySolidFullButton, SecondarySolidFullHrefButton, SecondarySolidFullIconButton, SecondarySolidFullIconHrefButton, SecondarySolidLargeRoundButton, SecondarySolidLargeRoundHrefButton, SecondarySolidLargeRoundIconButton, SecondarySolidLargeRoundIconHrefButton, SecondarySolidSmallRoundButton, SecondarySolidSmallRoundHrefButton, SecondarySolidSmallRoundIconButton, SecondarySolidSmallRoundIconHrefButton, SegmentControlComponent, SelectSearchComponent, SessionService, ShareButtonsComponent, SimpleComponent, SkeletonComponent, SkeletonService, SolidBlockButton, SolidDefault, SolidDefaultBlock, SolidDefaultButton, SolidDefaultFull, SolidDefaultRound, SolidDefaultRoundBlock, SolidDefaultRoundButton, SolidDefaultRoundFull, SolidFullButton, SolidLargeButton, SolidLargeRoundButton, SolidSmallButton, SolidSmallRoundButton, StatsCardComponent, StepperComponent, StorageService, SwipeCarouselComponent, TabbedContentComponent, TableSkeletonComponent, TabsComponent, Terminal404Component, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeService, TicketGridComponent, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TranslatePipe, TypedCollection, UpdateBannerComponent, UsernameInputComponent, VALTECH_ADS_CONFIG, VALTECH_APP_CONFIG, VALTECH_AUTH_CONFIG, VALTECH_COMPANY_LINKS, VALTECH_DEFAULT_CONTENT, VALTECH_FEEDBACK_CONFIG, VALTECH_FIREBASE_CONFIG, VALTECH_FOOTER_I18N, VALTECH_FOOTER_LOGO, VALTECH_LANGUAGE_SELECTOR, VALTECH_SOCIAL_LINKS, VERSION, WinnerDisplayComponent, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, authGuard, authInterceptor, blogPost, buildFooterLinks, buildPath, collections, createFirebaseConfig, createGlowCardProps, createInitialPaginationState, createNumberFromToField, createTitleProps, docs, extractPathParams, getAppInfo, getAppVersion, getCollectionPath, getDocumentId, goToTop, guestGuard, hasEmulators, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isValidPath, joinPath, maxLength, news, permissionGuard, permissionGuardFromRoute, provideValtechAds, provideValtechAppConfig, provideValtechAuth, provideValtechAuthInterceptor, provideValtechFeedback, provideValtechFirebase, provideValtechI18n, provideValtechPresets, provideValtechSkeleton, query, replaceSpecialChars, resolveColor, resolveInputDefaultValue, roleGuard, storagePaths, superAdminGuard, toArticle };
42177
43374
  //# sourceMappingURL=valtech-components.mjs.map