valtech-components 4.0.970 → 4.0.972

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/esm2022/lib/components/organisms/page-block/page-block.component.mjs +254 -0
  2. package/esm2022/lib/components/organisms/page-block/types.mjs +20 -0
  3. package/esm2022/lib/components/organisms/rich-editor/article-to-tiptap.mjs +110 -0
  4. package/esm2022/lib/components/organisms/rich-editor/rich-editor.component.mjs +287 -0
  5. package/esm2022/lib/components/organisms/rich-editor/rich-editor.i18n.mjs +27 -0
  6. package/esm2022/lib/components/organisms/rich-editor/tiptap-to-article.mjs +136 -0
  7. package/esm2022/lib/components/organisms/rich-editor/types.mjs +2 -0
  8. package/esm2022/lib/components/organisms/video-player/types.mjs +12 -0
  9. package/esm2022/lib/components/organisms/video-player/video-player.component.mjs +235 -0
  10. package/esm2022/lib/version.mjs +2 -2
  11. package/esm2022/public-api.mjs +13 -1
  12. package/fesm2022/valtech-components.mjs +1060 -3
  13. package/fesm2022/valtech-components.mjs.map +1 -1
  14. package/lib/components/organisms/page-block/page-block.component.d.ts +65 -0
  15. package/lib/components/organisms/page-block/types.d.ts +102 -0
  16. package/lib/components/organisms/rich-editor/article-to-tiptap.d.ts +3 -0
  17. package/lib/components/organisms/rich-editor/rich-editor.component.d.ts +46 -0
  18. package/lib/components/organisms/rich-editor/rich-editor.i18n.d.ts +26 -0
  19. package/lib/components/organisms/rich-editor/tiptap-to-article.d.ts +38 -0
  20. package/lib/components/organisms/rich-editor/types.d.ts +21 -0
  21. package/lib/components/organisms/video-player/types.d.ts +45 -0
  22. package/lib/components/organisms/video-player/video-player.component.d.ts +42 -0
  23. package/lib/version.d.ts +1 -1
  24. package/package.json +5 -1
  25. package/public-api.d.ts +8 -0
@@ -27,7 +27,7 @@ import * as i1$5 from '@ionic/angular/standalone';
27
27
  import { IonText, IonHeader, IonToolbar, IonButtons, IonButton, IonContent, IonFooter, IonSpinner, AlertController, IonAvatar, IonCard, IonIcon, IonModal, IonTitle, IonProgressBar, IonSkeletonText, IonFab, IonFabButton, IonFabList, IonLabel, IonCardContent, IonCardHeader, IonCardTitle, IonCardSubtitle, IonCheckbox, IonRadio, IonRadioGroup, IonTextarea, IonDatetime, IonInput, IonSelect, IonSelectOption, IonPopover, IonList, IonItem, IonChip, IonRange, IonSearchbar, IonSegment, IonSegmentButton, IonToggle, IonRippleEffect, IonAccordion, IonAccordionGroup, IonTabBar, IonTabButton, IonBadge, IonBreadcrumb, IonBreadcrumbs, IonInfiniteScroll, IonInfiniteScrollContent, IonNote, IonDatetimeButton, ToastController as ToastController$1, IonCol, IonRow, IonRefresher, IonRefresherContent, IonMenuButton, IonListHeader, IonGrid, MenuController, IonMenu, IonMenuToggle, IonSplitPane, IonAlert } from '@ionic/angular/standalone';
28
28
  import { Browser } from '@capacitor/browser';
29
29
  import { addIcons } from 'ionicons';
30
- import { addOutline, addCircleOutline, alertOutline, alertCircleOutline, arrowBackOutline, arrowForwardOutline, arrowDownOutline, settings, settingsOutline, checkmarkCircleOutline, ellipsisHorizontalOutline, notifications, notificationsOutline, openOutline, closeOutline, chatbubblesOutline, shareOutline, heart, heartOutline, home, homeOutline, eyeOffOutline, eyeOutline, scanOutline, chevronDownOutline, chevronForwardOutline, chevronBackCircleOutline, chevronForwardCircleOutline, helpCircleOutline, logInOutline, chatbubbleEllipsesOutline, listOutline, folderOutline, pricetagOutline, starOutline, gameControllerOutline, checkmarkOutline, clipboardOutline, copyOutline, filterOutline, locationOutline, calendarOutline, businessOutline, logoTwitter, logoInstagram, logoLinkedin, logoYoutube, logoTiktok, logoFacebook, logoGoogle, mailOutline, qrCodeOutline, sendOutline, createOutline, trashOutline, playOutline, peopleOutline, phonePortraitOutline, refreshOutline, documentTextOutline, lockClosedOutline, cloudUploadOutline, textOutline, informationCircleOutline, logoNpm, removeOutline, optionsOutline, personOutline, shieldCheckmarkOutline, keyOutline, desktopOutline, logOutOutline, cloudDownloadOutline, cloudOutline, gitBranchOutline, layersOutline, serverOutline, warningOutline, bulbOutline, lockOpenOutline, diamondOutline, newspaperOutline, add, close, share, create, trash, star, camera, mic, send, downloadOutline, reorderThreeOutline, chevronDown, language, globeOutline, checkmark, sunnyOutline, moonOutline, list, grid, apps, menu, search, person, helpCircle, informationCircle, documentText, mail, calendar, folder, chevronForward, ellipsisHorizontal, chevronBack, playBack, playForward, ellipse, starHalf, heartHalf, checkmarkCircle, timeOutline, flag, trendingUp, trendingDown, remove, analytics, people, cash, cart, eye, chatbubbleOutline, thumbsUpOutline, thumbsUp, happyOutline, happy, sadOutline, sad, chevronUp, pin, pencil, callOutline, logoWhatsapp, paperPlaneOutline, chevronDownCircleOutline, closeCircle, alertCircle, logoApple, logoMicrosoft, linkOutline, unlinkOutline, attachOutline, cameraOutline, closeCircleOutline, documentOutline, imageOutline, cloudOfflineOutline, printOutline, chevronBackOutline, swapVerticalOutline, chevronUpOutline, searchOutline, cartOutline, chatbubble, compass, compassOutline, gridOutline, documents, documentsOutline, statsChart, statsChartOutline, laptopOutline, swapHorizontalOutline, codeSlashOutline, appsOutline, notificationsOffOutline, alarmOutline, chatboxEllipsesOutline, checkmarkDoneOutline, cogOutline, shareSocialOutline, sparklesOutline, trophyOutline, musicalNotesOutline, volumeHighOutline, volumeMuteOutline, menuOutline, flashlight, flashlightOutline, stopCircleOutline, videocamOffOutline, keypadOutline, checkmarkDone, arrowUndoOutline, micOutline, pulseOutline, arrowUpOutline } from 'ionicons/icons';
30
+ import { addOutline, addCircleOutline, alertOutline, alertCircleOutline, arrowBackOutline, arrowForwardOutline, arrowDownOutline, settings, settingsOutline, checkmarkCircleOutline, ellipsisHorizontalOutline, notifications, notificationsOutline, openOutline, closeOutline, chatbubblesOutline, shareOutline, heart, heartOutline, home, homeOutline, eyeOffOutline, eyeOutline, scanOutline, chevronDownOutline, chevronForwardOutline, chevronBackCircleOutline, chevronForwardCircleOutline, helpCircleOutline, logInOutline, chatbubbleEllipsesOutline, listOutline, folderOutline, pricetagOutline, starOutline, gameControllerOutline, checkmarkOutline, clipboardOutline, copyOutline, filterOutline, locationOutline, calendarOutline, businessOutline, logoTwitter, logoInstagram, logoLinkedin, logoYoutube, logoTiktok, logoFacebook, logoGoogle, mailOutline, qrCodeOutline, sendOutline, createOutline, trashOutline, playOutline, peopleOutline, phonePortraitOutline, refreshOutline, documentTextOutline, lockClosedOutline, cloudUploadOutline, textOutline, informationCircleOutline, logoNpm, removeOutline, optionsOutline, personOutline, shieldCheckmarkOutline, keyOutline, desktopOutline, logOutOutline, cloudDownloadOutline, cloudOutline, gitBranchOutline, layersOutline, serverOutline, warningOutline, bulbOutline, lockOpenOutline, diamondOutline, newspaperOutline, add, close, share, create, trash, star, camera, mic, send, downloadOutline, reorderThreeOutline, chevronDown, language, globeOutline, checkmark, sunnyOutline, moonOutline, list, grid, apps, menu, search, person, helpCircle, informationCircle, documentText, mail, calendar, folder, chevronForward, ellipsisHorizontal, chevronBack, playBack, playForward, ellipse, starHalf, heartHalf, checkmarkCircle, timeOutline, flag, trendingUp, trendingDown, remove, analytics, people, cash, cart, eye, chatbubbleOutline, thumbsUpOutline, thumbsUp, happyOutline, happy, sadOutline, sad, chevronUp, pin, pencil, callOutline, logoWhatsapp, paperPlaneOutline, chevronDownCircleOutline, closeCircle, alertCircle, logoApple, logoMicrosoft, linkOutline, unlinkOutline, attachOutline, cameraOutline, closeCircleOutline, documentOutline, imageOutline, cloudOfflineOutline, chatboxOutline, printOutline, chevronBackOutline, swapVerticalOutline, chevronUpOutline, searchOutline, cartOutline, chatbubble, compass, compassOutline, gridOutline, documents, documentsOutline, statsChart, statsChartOutline, laptopOutline, swapHorizontalOutline, codeSlashOutline, appsOutline, notificationsOffOutline, alarmOutline, chatboxEllipsesOutline, checkmarkDoneOutline, cogOutline, shareSocialOutline, sparklesOutline, trophyOutline, musicalNotesOutline, volumeHighOutline, volumeMuteOutline, menuOutline, flashlight, flashlightOutline, stopCircleOutline, videocamOffOutline, keypadOutline, checkmarkDone, arrowUndoOutline, micOutline, pulseOutline, arrowUpOutline } from 'ionicons/icons';
31
31
  import * as i1$7 from '@angular/platform-browser';
32
32
  import { DomSanitizer, Meta, Title } from '@angular/platform-browser';
33
33
  import QRCodeStyling from 'qr-code-styling';
@@ -57,6 +57,9 @@ import { Capacitor } from '@capacitor/core';
57
57
  import { Calendar } from 'vanilla-calendar-pro';
58
58
  import Compressor from 'compressorjs';
59
59
  import { ImageCropperComponent } from 'ngx-image-cropper';
60
+ import { Editor } from '@tiptap/core';
61
+ import StarterKit from '@tiptap/starter-kit';
62
+ import Image$1 from '@tiptap/extension-image';
60
63
  import { trigger, transition, style, animate } from '@angular/animations';
61
64
  import 'prismjs/components/prism-scss';
62
65
  import 'prismjs/components/prism-json';
@@ -67,7 +70,7 @@ import fixWebmDuration from 'fix-webm-duration';
67
70
  * Current version of valtech-components.
68
71
  * This is automatically updated during the publish process.
69
72
  */
70
- const VERSION = '4.0.970';
73
+ const VERSION = '4.0.972';
71
74
 
72
75
  function evaluateValtechAccess(rule, context, features = {}, visitedFeatures = new Set()) {
73
76
  if (rule == null)
@@ -39403,6 +39406,553 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
39403
39406
  type: Input
39404
39407
  }] } });
39405
39408
 
39409
+ /**
39410
+ * Defaults i18n (es/en) del `val-rich-editor`. Auto-registrados en el
39411
+ * constructor (namespace `RichEditor`) — el consumer nunca los registra.
39412
+ */
39413
+ const RICH_EDITOR_I18N = {
39414
+ es: {
39415
+ bold: 'Negrita',
39416
+ italic: 'Cursiva',
39417
+ heading2: 'Título',
39418
+ heading3: 'Subtítulo',
39419
+ bulletList: 'Lista con viñetas',
39420
+ orderedList: 'Lista numerada',
39421
+ quote: 'Cita',
39422
+ separator: 'Separador',
39423
+ },
39424
+ en: {
39425
+ bold: 'Bold',
39426
+ italic: 'Italic',
39427
+ heading2: 'Heading',
39428
+ heading3: 'Subheading',
39429
+ bulletList: 'Bullet list',
39430
+ orderedList: 'Numbered list',
39431
+ quote: 'Quote',
39432
+ separator: 'Separator',
39433
+ },
39434
+ };
39435
+
39436
+ /** Texto inline con marks bold/italic convertidas a <strong>/<em> — mismo
39437
+ * criterio que `allowPartialBold` en ArticleTextElement/ArticleNoteElement:
39438
+ * el elemento consumer debe setear `allowPartialBold: true` para que se
39439
+ * rendericen (si no, val-text/val-note escapan el HTML a propósito). */
39440
+ function inlineText(node) {
39441
+ if (node.type === 'text') {
39442
+ let text = node.text ?? '';
39443
+ for (const mark of node.marks ?? []) {
39444
+ if (mark.type === 'bold')
39445
+ text = `<strong>${text}</strong>`;
39446
+ if (mark.type === 'italic')
39447
+ text = `<em>${text}</em>`;
39448
+ if (mark.type === 'code')
39449
+ text = `<code>${text}</code>`;
39450
+ }
39451
+ return text;
39452
+ }
39453
+ return (node.content ?? []).map(inlineText).join('');
39454
+ }
39455
+ function hasBold(node) {
39456
+ if (node.type === 'text') {
39457
+ return (node.marks ?? []).some(m => m.type === 'bold' || m.type === 'code');
39458
+ }
39459
+ return (node.content ?? []).some(hasBold);
39460
+ }
39461
+ function paragraphText(node) {
39462
+ return (node.content ?? []).map(inlineText).join('');
39463
+ }
39464
+ function listItemsFrom(node) {
39465
+ return (node.content ?? []).map(li => ({
39466
+ text: (li.content ?? []).map(paragraphText).join(' '),
39467
+ }));
39468
+ }
39469
+ /**
39470
+ * Convierte un doc de TipTap a `ArticleElement[]`. Función pura — sin
39471
+ * Angular DI, usable también desde Node (mismo criterio que
39472
+ * `parseMarkdownArticle`).
39473
+ */
39474
+ function tiptapDocToArticleElements(doc) {
39475
+ const elements = [];
39476
+ for (const node of doc.content ?? []) {
39477
+ switch (node.type) {
39478
+ case 'heading': {
39479
+ const level = node.attrs?.['level'] ?? 2;
39480
+ elements.push({
39481
+ type: level <= 2 ? 'title' : 'subtitle',
39482
+ props: {
39483
+ content: paragraphText(node),
39484
+ size: level === 1 ? 'large' : level === 2 ? 'medium' : 'small',
39485
+ color: 'dark',
39486
+ bold: true,
39487
+ },
39488
+ });
39489
+ break;
39490
+ }
39491
+ case 'paragraph': {
39492
+ const content = paragraphText(node);
39493
+ if (!content.trim())
39494
+ break; // párrafo vacío (Enter suelto) — no genera elemento
39495
+ elements.push({
39496
+ type: 'text',
39497
+ props: {
39498
+ content,
39499
+ size: 'medium',
39500
+ color: 'dark',
39501
+ bold: false,
39502
+ allowPartialBold: hasBold(node),
39503
+ },
39504
+ });
39505
+ break;
39506
+ }
39507
+ case 'bulletList':
39508
+ case 'orderedList': {
39509
+ elements.push({
39510
+ type: 'list',
39511
+ props: {
39512
+ items: listItemsFrom(node),
39513
+ listType: node.type === 'orderedList' ? 'ordered' : 'unordered',
39514
+ },
39515
+ });
39516
+ break;
39517
+ }
39518
+ case 'blockquote': {
39519
+ elements.push({
39520
+ type: 'quote',
39521
+ props: {
39522
+ content: (node.content ?? []).map(paragraphText).join(' '),
39523
+ size: 'medium',
39524
+ color: 'dark',
39525
+ bold: false,
39526
+ showQuoteMark: true,
39527
+ },
39528
+ });
39529
+ break;
39530
+ }
39531
+ case 'codeBlock': {
39532
+ elements.push({
39533
+ type: 'code',
39534
+ props: {
39535
+ code: (node.content ?? []).map(n => n.text ?? '').join('\n'),
39536
+ language: node.attrs?.['language'] ?? undefined,
39537
+ },
39538
+ });
39539
+ break;
39540
+ }
39541
+ case 'horizontalRule': {
39542
+ elements.push({ type: 'separator', props: { style: 'line' } });
39543
+ break;
39544
+ }
39545
+ case 'image': {
39546
+ const src = node.attrs?.['src'] ?? '';
39547
+ if (!src)
39548
+ break; // sin src no hay nada que renderizar
39549
+ elements.push({
39550
+ type: 'image',
39551
+ props: {
39552
+ src,
39553
+ alt: node.attrs?.['alt'] ?? '',
39554
+ title: node.attrs?.['title'] ?? undefined,
39555
+ },
39556
+ });
39557
+ break;
39558
+ }
39559
+ default:
39560
+ break; // nodo no soportado todavía — se omite, no rompe el resto
39561
+ }
39562
+ }
39563
+ return elements;
39564
+ }
39565
+ function tiptapDocToArticle(doc, config) {
39566
+ return {
39567
+ ...config,
39568
+ elements: tiptapDocToArticleElements(doc),
39569
+ };
39570
+ }
39571
+
39572
+ /**
39573
+ * Conversión inversa de `tiptapDocToArticle` — hidrata `val-rich-editor` con
39574
+ * contenido ya guardado como `ArticleMetadata` (ej. una receta existente que
39575
+ * se vuelve a editar). No es un round-trip perfecto: `ArticleElement` cubre
39576
+ * tipos que TipTap no tiene nodo nativo para editar (`note`, `diagram`,
39577
+ * `command`, `highlight`, `button`, `video`, `custom`) — esos se degradan a
39578
+ * un párrafo de texto plano, editable pero sin su formato especial. Es
39579
+ * intencional: mejor mostrar el contenido (aunque simplificado) que perderlo
39580
+ * o que el editor rechace abrir el documento.
39581
+ */
39582
+ const STRONG_OR_EM = /<(strong|b|em|i|code)>(.*?)<\/\1>/g;
39583
+ /** Extrae marks (bold/italic/code) de un string con <strong>/<em>/<code>
39584
+ * embebido — el inverso de `inlineText` en tiptap-to-article.ts. Simplifica
39585
+ * a un solo nivel de anidación (suficiente para lo que el editor produce). */
39586
+ function textToTiptapInline(html) {
39587
+ if (!html)
39588
+ return [];
39589
+ const nodes = [];
39590
+ let lastIndex = 0;
39591
+ STRONG_OR_EM.lastIndex = 0;
39592
+ let match;
39593
+ const markFor = (tag) => {
39594
+ if (tag === 'strong' || tag === 'b')
39595
+ return { type: 'bold' };
39596
+ if (tag === 'em' || tag === 'i')
39597
+ return { type: 'italic' };
39598
+ return { type: 'code' };
39599
+ };
39600
+ while ((match = STRONG_OR_EM.exec(html)) !== null) {
39601
+ if (match.index > lastIndex) {
39602
+ nodes.push({ type: 'text', text: html.slice(lastIndex, match.index) });
39603
+ }
39604
+ nodes.push({ type: 'text', text: match[2], marks: [markFor(match[1])] });
39605
+ lastIndex = match.index + match[0].length;
39606
+ }
39607
+ if (lastIndex < html.length) {
39608
+ nodes.push({ type: 'text', text: html.slice(lastIndex) });
39609
+ }
39610
+ return nodes.length ? nodes : [{ type: 'text', text: html }];
39611
+ }
39612
+ function paragraph(text) {
39613
+ return { type: 'paragraph', content: textToTiptapInline(text) };
39614
+ }
39615
+ function elementToTiptapNodes(el) {
39616
+ switch (el.type) {
39617
+ case 'title':
39618
+ case 'subtitle': {
39619
+ const level = el.type === 'title' ? (el.props.size === 'large' ? 1 : 2) : 3;
39620
+ return [
39621
+ {
39622
+ type: 'heading',
39623
+ attrs: { level },
39624
+ content: textToTiptapInline(el.props.content ?? ''),
39625
+ },
39626
+ ];
39627
+ }
39628
+ case 'text':
39629
+ case 'paragraph':
39630
+ return [paragraph(el.props.content ?? '')];
39631
+ case 'list':
39632
+ return [
39633
+ {
39634
+ type: el.props.listType === 'ordered' ? 'orderedList' : 'bulletList',
39635
+ content: el.props.items.map(item => ({
39636
+ type: 'listItem',
39637
+ content: [paragraph(item.text)],
39638
+ })),
39639
+ },
39640
+ ];
39641
+ case 'quote':
39642
+ return [
39643
+ {
39644
+ type: 'blockquote',
39645
+ content: [paragraph(el.props.content ?? '')],
39646
+ },
39647
+ ];
39648
+ case 'code':
39649
+ return [
39650
+ {
39651
+ type: 'codeBlock',
39652
+ attrs: el.props.language ? { language: el.props.language } : undefined,
39653
+ content: el.props.code ? [{ type: 'text', text: el.props.code }] : [],
39654
+ },
39655
+ ];
39656
+ case 'separator':
39657
+ return [{ type: 'horizontalRule' }];
39658
+ case 'image':
39659
+ return [
39660
+ {
39661
+ type: 'image',
39662
+ attrs: { src: el.props.src, alt: el.props.alt, title: el.props.title },
39663
+ },
39664
+ ];
39665
+ // Sin nodo TipTap nativo — se degrada a párrafo de texto plano (ver
39666
+ // comentario del módulo). El contenido nunca se pierde, solo su formato.
39667
+ case 'note':
39668
+ return [paragraph(el.props.text ?? '')];
39669
+ case 'highlight':
39670
+ return [paragraph(el.props.content ?? '')];
39671
+ default:
39672
+ return [];
39673
+ }
39674
+ }
39675
+ function articleToTiptapDoc(article) {
39676
+ return {
39677
+ type: 'doc',
39678
+ content: article.elements.flatMap(elementToTiptapNodes),
39679
+ };
39680
+ }
39681
+
39682
+ // Íconos de la toolbar. B/I/H2/H3 no tienen ícono Ionicons dedicado — se
39683
+ // renderizan como texto ("B", "I", "H2", "H3") directo en el botón, mismo
39684
+ // criterio que un editor de texto rico convencional (Google Docs, Notion).
39685
+ addIcons({ listOutline, chatboxOutline, removeOutline });
39686
+ /**
39687
+ * val-rich-editor
39688
+ *
39689
+ * Organism: editor WYSIWYG reusable para toda la plataforma, sobre TipTap.
39690
+ * Emite `ArticleMetadata` — el mismo contrato que `val-article` renderiza,
39691
+ * que también producen Markdown (`parseMarkdownArticle`) y (a futuro) el
39692
+ * page builder. El consumer nunca toca TipTap directamente.
39693
+ *
39694
+ * Cobertura de la toolbar v1: negrita, cursiva, título (H2), subtítulo (H3),
39695
+ * lista con/sin viñetas, cita, separador, imagen. Ver `tiptap-to-article.ts`
39696
+ * para qué tipos de `ArticleElement` puede producir el editor hoy.
39697
+ *
39698
+ * @example
39699
+ * ```html
39700
+ * <val-rich-editor
39701
+ * [props]="{ initialValue: recipe.descriptionArticle, placeholder: 'Escribe la preparación...' }"
39702
+ * (articleChange)="onArticleChange($event)"
39703
+ * />
39704
+ * ```
39705
+ */
39706
+ class RichEditorComponent {
39707
+ constructor() {
39708
+ this.props = {};
39709
+ this.articleChange = new EventEmitter();
39710
+ this.i18n = inject(I18nService);
39711
+ /** Se recalcula en cada `onUpdate`/`onSelectionUpdate` para reflejar el
39712
+ * mark/nodo activo bajo el cursor (ej. el botón "B" resaltado si el
39713
+ * cursor está dentro de texto en negrita). */
39714
+ this._toolbarTick = signal(0);
39715
+ this.toolbarButtons = signal([]);
39716
+ this.i18n.registerDefaults('RichEditor', RICH_EDITOR_I18N);
39717
+ }
39718
+ t(key) {
39719
+ return this.i18n.t(key, 'RichEditor');
39720
+ }
39721
+ ngAfterViewInit() {
39722
+ this.editor = new Editor({
39723
+ element: this.editorHost.nativeElement,
39724
+ extensions: [StarterKit, Image$1],
39725
+ editable: !this.props.disabled,
39726
+ content: this.props.initialValue
39727
+ ? articleToTiptapDoc(this.props.initialValue)
39728
+ : '',
39729
+ editorProps: {
39730
+ attributes: {
39731
+ 'data-placeholder': this.props.placeholder ?? '',
39732
+ },
39733
+ },
39734
+ onUpdate: ({ editor }) => {
39735
+ this.emitArticle(editor.getJSON());
39736
+ this.refreshToolbar();
39737
+ },
39738
+ onSelectionUpdate: () => this.refreshToolbar(),
39739
+ });
39740
+ this.refreshToolbar();
39741
+ }
39742
+ ngOnChanges(changes) {
39743
+ if (changes['props']?.currentValue && this.editor) {
39744
+ this.editor.setEditable(!this.props.disabled);
39745
+ }
39746
+ }
39747
+ emitArticle(doc) {
39748
+ this.articleChange.emit(tiptapDocToArticle(doc));
39749
+ }
39750
+ refreshToolbar() {
39751
+ const editor = this.editor;
39752
+ if (!editor)
39753
+ return;
39754
+ this.toolbarButtons.set([
39755
+ {
39756
+ token: 'bold',
39757
+ icon: '',
39758
+ label: this.t('bold'),
39759
+ isActive: editor.isActive('bold'),
39760
+ disabled: false,
39761
+ },
39762
+ {
39763
+ token: 'italic',
39764
+ icon: '',
39765
+ label: this.t('italic'),
39766
+ isActive: editor.isActive('italic'),
39767
+ disabled: false,
39768
+ },
39769
+ {
39770
+ token: 'heading2',
39771
+ icon: '',
39772
+ label: this.t('heading2'),
39773
+ isActive: editor.isActive('heading', { level: 2 }),
39774
+ disabled: false,
39775
+ },
39776
+ {
39777
+ token: 'heading3',
39778
+ icon: '',
39779
+ label: this.t('heading3'),
39780
+ isActive: editor.isActive('heading', { level: 3 }),
39781
+ disabled: false,
39782
+ },
39783
+ {
39784
+ token: 'bulletList',
39785
+ icon: 'list-outline',
39786
+ label: this.t('bulletList'),
39787
+ isActive: editor.isActive('bulletList'),
39788
+ disabled: false,
39789
+ },
39790
+ {
39791
+ token: 'orderedList',
39792
+ icon: 'list-outline',
39793
+ label: this.t('orderedList'),
39794
+ isActive: editor.isActive('orderedList'),
39795
+ disabled: false,
39796
+ },
39797
+ {
39798
+ token: 'quote',
39799
+ icon: 'chatbox-outline',
39800
+ label: this.t('quote'),
39801
+ isActive: editor.isActive('blockquote'),
39802
+ disabled: false,
39803
+ },
39804
+ {
39805
+ token: 'separator',
39806
+ icon: 'remove-outline',
39807
+ label: this.t('separator'),
39808
+ isActive: false,
39809
+ disabled: false,
39810
+ },
39811
+ ]);
39812
+ }
39813
+ runCommand(token) {
39814
+ const editor = this.editor;
39815
+ if (!editor)
39816
+ return;
39817
+ const chain = editor.chain().focus();
39818
+ switch (token) {
39819
+ case 'bold':
39820
+ chain.toggleBold().run();
39821
+ break;
39822
+ case 'italic':
39823
+ chain.toggleItalic().run();
39824
+ break;
39825
+ case 'heading2':
39826
+ chain.toggleHeading({ level: 2 }).run();
39827
+ break;
39828
+ case 'heading3':
39829
+ chain.toggleHeading({ level: 3 }).run();
39830
+ break;
39831
+ case 'bulletList':
39832
+ chain.toggleBulletList().run();
39833
+ break;
39834
+ case 'orderedList':
39835
+ chain.toggleOrderedList().run();
39836
+ break;
39837
+ case 'quote':
39838
+ chain.toggleBlockquote().run();
39839
+ break;
39840
+ case 'separator':
39841
+ chain.setHorizontalRule().run();
39842
+ break;
39843
+ }
39844
+ this.refreshToolbar();
39845
+ }
39846
+ ngOnDestroy() {
39847
+ this.editor?.destroy();
39848
+ }
39849
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RichEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
39850
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: RichEditorComponent, isStandalone: true, selector: "val-rich-editor", inputs: { props: "props" }, outputs: { articleChange: "articleChange" }, viewQueries: [{ propertyName: "editorHost", first: true, predicate: ["editorHost"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: `
39851
+ <div class="rich-editor" [class.rich-editor--disabled]="props.disabled">
39852
+ @if (!props.disabled) {
39853
+ <div class="rich-editor__toolbar" role="toolbar">
39854
+ @for (btn of toolbarButtons(); track btn.token) {
39855
+ <ion-button
39856
+ fill="clear"
39857
+ size="small"
39858
+ [class.rich-editor__btn--active]="btn.isActive"
39859
+ [disabled]="btn.disabled"
39860
+ [attr.aria-label]="btn.label"
39861
+ [attr.aria-pressed]="btn.isActive"
39862
+ (click)="runCommand(btn.token)"
39863
+ >
39864
+ @switch (btn.token) {
39865
+ @case ('bold') {
39866
+ <strong>B</strong>
39867
+ }
39868
+ @case ('italic') {
39869
+ <em>I</em>
39870
+ }
39871
+ @case ('heading2') {
39872
+ <strong>H2</strong>
39873
+ }
39874
+ @case ('heading3') {
39875
+ <strong>H3</strong>
39876
+ }
39877
+ @case ('bulletList') {
39878
+ <ion-icon name="list-outline" slot="icon-only" />
39879
+ }
39880
+ @case ('orderedList') {
39881
+ <ion-icon name="list-outline" slot="icon-only" />
39882
+ }
39883
+ @case ('quote') {
39884
+ <ion-icon name="chatbox-outline" slot="icon-only" />
39885
+ }
39886
+ @case ('separator') {
39887
+ <ion-icon name="remove-outline" slot="icon-only" />
39888
+ }
39889
+ }
39890
+ </ion-button>
39891
+ }
39892
+ </div>
39893
+ }
39894
+ <div #editorHost class="rich-editor__surface"></div>
39895
+ </div>
39896
+ `, isInline: true, styles: [".rich-editor{display:flex;flex-direction:column;gap:8px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));border-radius:8px;overflow:hidden}.rich-editor--disabled{opacity:.7}.rich-editor__toolbar{display:flex;flex-wrap:wrap;gap:2px;padding:6px 8px;border-bottom:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));background:var(--ion-color-light, #f4f5f8)}.rich-editor__toolbar ion-button{--padding-start: 8px;--padding-end: 8px;margin:0;font-size:.8125rem;min-width:32px}.rich-editor__btn--active{--background: rgba(var(--ion-color-primary-rgb), .14);--color: var(--ion-color-primary);border-radius:6px}.rich-editor__surface{padding:12px 16px;min-height:160px}.rich-editor__surface ::ng-deep .ProseMirror{outline:none;min-height:140px;color:var(--ion-text-color, #000);font-size:.9375rem;line-height:1.5}.rich-editor__surface ::ng-deep .ProseMirror p.is-editor-empty:first-child:before{content:attr(data-placeholder);color:var(--ion-color-medium, #92949c);float:left;height:0;pointer-events:none}.rich-editor__surface ::ng-deep .ProseMirror p{margin:0 0 .75em}.rich-editor__surface ::ng-deep .ProseMirror h1,.rich-editor__surface ::ng-deep .ProseMirror h2,.rich-editor__surface ::ng-deep .ProseMirror h3{margin:0 0 .5em;color:var(--ion-text-color, #000)}.rich-editor__surface ::ng-deep .ProseMirror blockquote{margin:0 0 .75em;padding-left:12px;border-left:3px solid var(--ion-color-primary, #3880ff);color:var(--ion-color-medium, #92949c)}.rich-editor__surface ::ng-deep .ProseMirror ul,.rich-editor__surface ::ng-deep .ProseMirror ol{margin:0 0 .75em;padding-left:24px}.rich-editor__surface ::ng-deep .ProseMirror img{max-width:100%;border-radius:8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }] }); }
39897
+ }
39898
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RichEditorComponent, decorators: [{
39899
+ type: Component,
39900
+ args: [{ selector: 'val-rich-editor', standalone: true, imports: [CommonModule, IonButton, IonIcon], template: `
39901
+ <div class="rich-editor" [class.rich-editor--disabled]="props.disabled">
39902
+ @if (!props.disabled) {
39903
+ <div class="rich-editor__toolbar" role="toolbar">
39904
+ @for (btn of toolbarButtons(); track btn.token) {
39905
+ <ion-button
39906
+ fill="clear"
39907
+ size="small"
39908
+ [class.rich-editor__btn--active]="btn.isActive"
39909
+ [disabled]="btn.disabled"
39910
+ [attr.aria-label]="btn.label"
39911
+ [attr.aria-pressed]="btn.isActive"
39912
+ (click)="runCommand(btn.token)"
39913
+ >
39914
+ @switch (btn.token) {
39915
+ @case ('bold') {
39916
+ <strong>B</strong>
39917
+ }
39918
+ @case ('italic') {
39919
+ <em>I</em>
39920
+ }
39921
+ @case ('heading2') {
39922
+ <strong>H2</strong>
39923
+ }
39924
+ @case ('heading3') {
39925
+ <strong>H3</strong>
39926
+ }
39927
+ @case ('bulletList') {
39928
+ <ion-icon name="list-outline" slot="icon-only" />
39929
+ }
39930
+ @case ('orderedList') {
39931
+ <ion-icon name="list-outline" slot="icon-only" />
39932
+ }
39933
+ @case ('quote') {
39934
+ <ion-icon name="chatbox-outline" slot="icon-only" />
39935
+ }
39936
+ @case ('separator') {
39937
+ <ion-icon name="remove-outline" slot="icon-only" />
39938
+ }
39939
+ }
39940
+ </ion-button>
39941
+ }
39942
+ </div>
39943
+ }
39944
+ <div #editorHost class="rich-editor__surface"></div>
39945
+ </div>
39946
+ `, styles: [".rich-editor{display:flex;flex-direction:column;gap:8px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));border-radius:8px;overflow:hidden}.rich-editor--disabled{opacity:.7}.rich-editor__toolbar{display:flex;flex-wrap:wrap;gap:2px;padding:6px 8px;border-bottom:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));background:var(--ion-color-light, #f4f5f8)}.rich-editor__toolbar ion-button{--padding-start: 8px;--padding-end: 8px;margin:0;font-size:.8125rem;min-width:32px}.rich-editor__btn--active{--background: rgba(var(--ion-color-primary-rgb), .14);--color: var(--ion-color-primary);border-radius:6px}.rich-editor__surface{padding:12px 16px;min-height:160px}.rich-editor__surface ::ng-deep .ProseMirror{outline:none;min-height:140px;color:var(--ion-text-color, #000);font-size:.9375rem;line-height:1.5}.rich-editor__surface ::ng-deep .ProseMirror p.is-editor-empty:first-child:before{content:attr(data-placeholder);color:var(--ion-color-medium, #92949c);float:left;height:0;pointer-events:none}.rich-editor__surface ::ng-deep .ProseMirror p{margin:0 0 .75em}.rich-editor__surface ::ng-deep .ProseMirror h1,.rich-editor__surface ::ng-deep .ProseMirror h2,.rich-editor__surface ::ng-deep .ProseMirror h3{margin:0 0 .5em;color:var(--ion-text-color, #000)}.rich-editor__surface ::ng-deep .ProseMirror blockquote{margin:0 0 .75em;padding-left:12px;border-left:3px solid var(--ion-color-primary, #3880ff);color:var(--ion-color-medium, #92949c)}.rich-editor__surface ::ng-deep .ProseMirror ul,.rich-editor__surface ::ng-deep .ProseMirror ol{margin:0 0 .75em;padding-left:24px}.rich-editor__surface ::ng-deep .ProseMirror img{max-width:100%;border-radius:8px}\n"] }]
39947
+ }], ctorParameters: () => [], propDecorators: { props: [{
39948
+ type: Input
39949
+ }], articleChange: [{
39950
+ type: Output
39951
+ }], editorHost: [{
39952
+ type: ViewChild,
39953
+ args: ['editorHost', { static: true }]
39954
+ }] } });
39955
+
39406
39956
  /**
39407
39957
  * val-banner
39408
39958
  *
@@ -89861,6 +90411,513 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
89861
90411
  }]
89862
90412
  }] });
89863
90413
 
90414
+ /**
90415
+ * `val-page-block`
90416
+ *
90417
+ * Organism que renderiza UN bloque del constructor de páginas de plataforma
90418
+ * (ADR-077, `services/page` en el backend). Es el punto de la lib donde el
90419
+ * catálogo de bloques (`hero`, `text`, `gallery`, `cta`, `faq`) mapea 1:1 a
90420
+ * componentes reales — la razón de ser del ADR: "un rediseño de la lib
90421
+ * mejora todas las páginas publicadas sin tocar sus datos".
90422
+ *
90423
+ * Presentacional puro: recibe `props` ya tipados (espejo exacto del `Block`
90424
+ * del backend), sin llamadas HTTP propias ni conocimiento de a qué app
90425
+ * pertenece la página. El bloque `cta` NO navega — emite `(ctaAction)` con
90426
+ * la intención cruda (`{ action }`) para que el consumidor (la app) la
90427
+ * resuelva contra su propio routing (ADR-077: "el CTA guarda una intención,
90428
+ * nunca una URL").
90429
+ *
90430
+ * El caller típico itera `Page.Blocks` y renderiza uno de estos por bloque:
90431
+ *
90432
+ * @example
90433
+ * ```html
90434
+ * @for (block of page().blocks; track $index) {
90435
+ * <val-page-block [props]="block" (ctaAction)="onCtaAction($event)" />
90436
+ * }
90437
+ * ```
90438
+ */
90439
+ class PageBlockComponent {
90440
+ constructor() {
90441
+ /**
90442
+ * Emite la intención cruda del CTA al hacer click (`{ action }`, nunca una
90443
+ * URL — ver comentario de clase). El consumidor decide qué hacer con
90444
+ * `action`; este componente no la interpreta.
90445
+ */
90446
+ this.ctaAction = new EventEmitter();
90447
+ /** Fallback de imagen rota (convención de la lib) — un slot, una signal. */
90448
+ this.heroImageFailed = signal(false);
90449
+ /** Índices de `gallery.images` cuya carga falló (`error` del `<img>`). */
90450
+ this.failedGalleryImages = signal(new Set());
90451
+ }
90452
+ get hero() {
90453
+ return this.props.props;
90454
+ }
90455
+ get text() {
90456
+ return this.props.props;
90457
+ }
90458
+ get gallery() {
90459
+ return this.props.props;
90460
+ }
90461
+ get cta() {
90462
+ return this.props.props;
90463
+ }
90464
+ get faq() {
90465
+ return this.props.props;
90466
+ }
90467
+ /**
90468
+ * `text.body` es texto plano (no markdown — a diferencia de
90469
+ * `val-article`/`val-faq`, `blocks_validate.go` solo exige un string no
90470
+ * vacío). Partido por línea para que `val-text` (un `<p>` por línea)
90471
+ * respete los saltos que el autor escribió, sin interpretar sintaxis.
90472
+ */
90473
+ textParagraphs() {
90474
+ return this.text.body.split('\n').filter(line => line.trim().length > 0);
90475
+ }
90476
+ /**
90477
+ * `val-faq` agrupa por categoría; el bloque `faq` de `services/page` no
90478
+ * tiene categorías (solo `items` planos) — se envuelve en una única
90479
+ * categoría sin label (`hideSingleCategoryLabel` default `true` la oculta).
90480
+ */
90481
+ faqCategories() {
90482
+ return [
90483
+ {
90484
+ id: 'page-faq',
90485
+ label: '',
90486
+ items: this.faq.items.map((item, index) => ({
90487
+ id: `page-faq-${index}`,
90488
+ question: item.question,
90489
+ answer: item.answer,
90490
+ })),
90491
+ },
90492
+ ];
90493
+ }
90494
+ onCtaClick() {
90495
+ this.ctaAction.emit(this.cta.intent);
90496
+ }
90497
+ onGalleryImageError(index) {
90498
+ const next = new Set(this.failedGalleryImages());
90499
+ next.add(index);
90500
+ this.failedGalleryImages.set(next);
90501
+ }
90502
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PageBlockComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
90503
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: PageBlockComponent, isStandalone: true, selector: "val-page-block", inputs: { props: "props" }, outputs: { ctaAction: "ctaAction" }, ngImport: i0, template: `
90504
+ @switch (props.type) {
90505
+ @case ('hero') {
90506
+ <section class="page-block page-block--hero" [class]="'align-' + (hero.align || 'center')">
90507
+ @if (hero.image && !heroImageFailed()) {
90508
+ <img
90509
+ class="page-block__hero-image"
90510
+ [src]="hero.image"
90511
+ [alt]="hero.title"
90512
+ loading="lazy"
90513
+ (error)="heroImageFailed.set(true)"
90514
+ />
90515
+ }
90516
+ <div class="page-block__hero-copy">
90517
+ <val-display [props]="{ size: 'large', color: 'dark', content: hero.title }" />
90518
+ @if (hero.subtitle) {
90519
+ <val-text [props]="{ size: 'large', color: 'medium', bold: false, content: hero.subtitle }" />
90520
+ }
90521
+ </div>
90522
+ </section>
90523
+ }
90524
+ @case ('text') {
90525
+ <section class="page-block page-block--text">
90526
+ @if (text.heading) {
90527
+ <val-title [props]="{ size: 'large', color: 'dark', bold: false, content: text.heading }" />
90528
+ }
90529
+ @for (paragraph of textParagraphs(); track $index) {
90530
+ <val-text [props]="{ size: 'medium', color: 'dark', bold: false, content: paragraph }" />
90531
+ }
90532
+ </section>
90533
+ }
90534
+ @case ('gallery') {
90535
+ <section class="page-block page-block--gallery">
90536
+ <div class="page-block__gallery-grid">
90537
+ @for (image of gallery.images; track $index) {
90538
+ @if (!failedGalleryImages().has($index)) {
90539
+ <!-- Sin alt descriptivo: el backend no modela texto alternativo
90540
+ por imagen (services/page/blocks_validate.go, gallery.images
90541
+ es solo un arreglo de strings). Se marca decorativa a
90542
+ propósito, no un olvido de a11y. -->
90543
+ <img
90544
+ class="page-block__gallery-image"
90545
+ [src]="image"
90546
+ alt=""
90547
+ loading="lazy"
90548
+ (error)="onGalleryImageError($index)"
90549
+ />
90550
+ }
90551
+ }
90552
+ </div>
90553
+ </section>
90554
+ }
90555
+ @case ('cta') {
90556
+ <section class="page-block page-block--cta">
90557
+ <val-button
90558
+ [props]="{
90559
+ text: cta.label,
90560
+ color: 'primary',
90561
+ fill: 'solid',
90562
+ shape: 'round',
90563
+ type: 'button',
90564
+ state: 'ENABLED',
90565
+ }"
90566
+ (onClick)="onCtaClick()"
90567
+ />
90568
+ </section>
90569
+ }
90570
+ @case ('faq') {
90571
+ <section class="page-block page-block--faq">
90572
+ <val-faq [props]="{ categories: faqCategories(), searchable: false }" />
90573
+ </section>
90574
+ }
90575
+ }
90576
+ `, isInline: true, styles: [".page-block{display:block;width:100%}.page-block--hero{display:flex;flex-direction:column;gap:16px;text-align:center;align-items:center}.page-block--hero.align-left{text-align:left;align-items:flex-start}.page-block--hero.align-right{text-align:right;align-items:flex-end}.page-block__hero-image{width:100%;max-height:360px;object-fit:cover;border-radius:12px}.page-block__hero-copy{display:flex;flex-direction:column;gap:8px;max-width:640px}.page-block--text{display:flex;flex-direction:column;gap:8px}.page-block__gallery-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:8px}@media (min-width: 768px){.page-block__gallery-grid{grid-template-columns:repeat(3,1fr);gap:12px}}@media (min-width: 992px){.page-block__gallery-grid{grid-template-columns:repeat(4,1fr)}}.page-block__gallery-image{width:100%;aspect-ratio:1/1;object-fit:cover;border-radius:8px}.page-block--cta{display:flex;justify-content:center}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }, { kind: "component", type: ButtonComponent, selector: "val-button", inputs: ["preset", "props"], outputs: ["onClick"] }, { kind: "component", type: FaqComponent, selector: "val-faq", inputs: ["props"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
90577
+ }
90578
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PageBlockComponent, decorators: [{
90579
+ type: Component,
90580
+ args: [{ selector: 'val-page-block', standalone: true, imports: [CommonModule, DisplayComponent, TitleComponent, TextComponent, ButtonComponent, FaqComponent], template: `
90581
+ @switch (props.type) {
90582
+ @case ('hero') {
90583
+ <section class="page-block page-block--hero" [class]="'align-' + (hero.align || 'center')">
90584
+ @if (hero.image && !heroImageFailed()) {
90585
+ <img
90586
+ class="page-block__hero-image"
90587
+ [src]="hero.image"
90588
+ [alt]="hero.title"
90589
+ loading="lazy"
90590
+ (error)="heroImageFailed.set(true)"
90591
+ />
90592
+ }
90593
+ <div class="page-block__hero-copy">
90594
+ <val-display [props]="{ size: 'large', color: 'dark', content: hero.title }" />
90595
+ @if (hero.subtitle) {
90596
+ <val-text [props]="{ size: 'large', color: 'medium', bold: false, content: hero.subtitle }" />
90597
+ }
90598
+ </div>
90599
+ </section>
90600
+ }
90601
+ @case ('text') {
90602
+ <section class="page-block page-block--text">
90603
+ @if (text.heading) {
90604
+ <val-title [props]="{ size: 'large', color: 'dark', bold: false, content: text.heading }" />
90605
+ }
90606
+ @for (paragraph of textParagraphs(); track $index) {
90607
+ <val-text [props]="{ size: 'medium', color: 'dark', bold: false, content: paragraph }" />
90608
+ }
90609
+ </section>
90610
+ }
90611
+ @case ('gallery') {
90612
+ <section class="page-block page-block--gallery">
90613
+ <div class="page-block__gallery-grid">
90614
+ @for (image of gallery.images; track $index) {
90615
+ @if (!failedGalleryImages().has($index)) {
90616
+ <!-- Sin alt descriptivo: el backend no modela texto alternativo
90617
+ por imagen (services/page/blocks_validate.go, gallery.images
90618
+ es solo un arreglo de strings). Se marca decorativa a
90619
+ propósito, no un olvido de a11y. -->
90620
+ <img
90621
+ class="page-block__gallery-image"
90622
+ [src]="image"
90623
+ alt=""
90624
+ loading="lazy"
90625
+ (error)="onGalleryImageError($index)"
90626
+ />
90627
+ }
90628
+ }
90629
+ </div>
90630
+ </section>
90631
+ }
90632
+ @case ('cta') {
90633
+ <section class="page-block page-block--cta">
90634
+ <val-button
90635
+ [props]="{
90636
+ text: cta.label,
90637
+ color: 'primary',
90638
+ fill: 'solid',
90639
+ shape: 'round',
90640
+ type: 'button',
90641
+ state: 'ENABLED',
90642
+ }"
90643
+ (onClick)="onCtaClick()"
90644
+ />
90645
+ </section>
90646
+ }
90647
+ @case ('faq') {
90648
+ <section class="page-block page-block--faq">
90649
+ <val-faq [props]="{ categories: faqCategories(), searchable: false }" />
90650
+ </section>
90651
+ }
90652
+ }
90653
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".page-block{display:block;width:100%}.page-block--hero{display:flex;flex-direction:column;gap:16px;text-align:center;align-items:center}.page-block--hero.align-left{text-align:left;align-items:flex-start}.page-block--hero.align-right{text-align:right;align-items:flex-end}.page-block__hero-image{width:100%;max-height:360px;object-fit:cover;border-radius:12px}.page-block__hero-copy{display:flex;flex-direction:column;gap:8px;max-width:640px}.page-block--text{display:flex;flex-direction:column;gap:8px}.page-block__gallery-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:8px}@media (min-width: 768px){.page-block__gallery-grid{grid-template-columns:repeat(3,1fr);gap:12px}}@media (min-width: 992px){.page-block__gallery-grid{grid-template-columns:repeat(4,1fr)}}.page-block__gallery-image{width:100%;aspect-ratio:1/1;object-fit:cover;border-radius:8px}.page-block--cta{display:flex;justify-content:center}\n"] }]
90654
+ }], propDecorators: { props: [{
90655
+ type: Input
90656
+ }], ctaAction: [{
90657
+ type: Output
90658
+ }] } });
90659
+
90660
+ /**
90661
+ * Tipos del organism `val-page-block` — constructor de páginas de plataforma
90662
+ * (ADR-077: `docs/adr/077-constructor-de-paginas-de-plataforma.md`, primera
90663
+ * aplicación real en Eklesee, Bloque 4:
90664
+ * `docs/eklesee/03-roadmap-y-abiertos.md`).
90665
+ *
90666
+ * Espejo 1:1 de `Block`/`Props` en `backend/go/services/page/types.go` +
90667
+ * `backend/go/services/page/blocks_validate.go` — la forma de cada `props`
90668
+ * sale de ahí, no se inventa acá. El catálogo de esta primera pasada son 5
90669
+ * tipos (`hero`, `text`, `gallery`, `cta`, `faq`); `countdown`/`sponsors`/
90670
+ * `progress` (catálogo completo del ADR) quedan fuera hasta que Bingo migre.
90671
+ *
90672
+ * El componente es i18n-agnóstico y agnóstico al dominio a propósito (mismo
90673
+ * criterio que `val-faq`/`val-article`): recibe `props` ya resueltos, sin
90674
+ * llamadas HTTP propias ni conocimiento de rutas de ninguna app. La app que
90675
+ * lo consume (Eklesee) resuelve `PageCTAIntent.action` a una ruta real —
90676
+ * este organism solo la emite via `(ctaAction)`.
90677
+ */
90678
+
90679
+ const VIDEO_PLAYER_I18N = {
90680
+ es: {
90681
+ uploadingTitle: 'Subiendo video',
90682
+ uploadingBody: 'La subida está en curso. Vuelve en un momento.',
90683
+ processingTitle: 'Video en proceso',
90684
+ processingBody: 'Estamos preparando este video. Vuelve en unos minutos.',
90685
+ rejectedTitle: 'Este video no se pudo publicar',
90686
+ failedTitle: 'No pudimos procesar este video',
90687
+ unsupportedLink: 'Este enlace no se puede reproducir aquí.',
90688
+ },
90689
+ en: {
90690
+ uploadingTitle: 'Uploading video',
90691
+ uploadingBody: 'The upload is in progress. Check back in a moment.',
90692
+ processingTitle: 'Video processing',
90693
+ processingBody: "We're getting this video ready. Check back in a few minutes.",
90694
+ rejectedTitle: "This video couldn't be published",
90695
+ failedTitle: "We couldn't process this video",
90696
+ unsupportedLink: "This link can't be played here.",
90697
+ },
90698
+ };
90699
+ /**
90700
+ * `val-video-player` — reproductor de video de plataforma (ADR-087, "Fase 1 —
90701
+ * Reproducción"). Presentacional/dumb: recibe un `VideoPlayerAsset` YA
90702
+ * RESUELTO por el backend de turno y decide el render, sin fetch propio.
90703
+ *
90704
+ * - `source:'upload'` + `status:'ready'` → `<video>` nativo (`preload="none"`
90705
+ * + portada — ADR-087 §8, cero bytes de video hasta que alguien le da play).
90706
+ * - `source:'upload'` + `uploading|processing` → portada (si hay) + mensaje.
90707
+ * - `source:'upload'` + `failed|rejected` → mensaje legible con `failReason`
90708
+ * (texto ya resuelto por el backend, se muestra tal cual).
90709
+ * - `source:'link'` → iframe **siempre** contra un patrón fijo por proveedor
90710
+ * conocido (`youtube-nocookie.com/embed/{id}` · `player.vimeo.com/video/{id}`),
90711
+ * nunca un `src` armado con datos crudos del usuario. Proveedor no
90712
+ * reconocido → mensaje, nunca un iframe "por si acaso".
90713
+ *
90714
+ * @example
90715
+ * <val-video-player [video]="doc.video" />
90716
+ */
90717
+ class VideoPlayerComponent {
90718
+ constructor() {
90719
+ this.i18n = inject(I18nService);
90720
+ this.sanitizer = inject(DomSanitizer);
90721
+ /** El `VideoAsset` ya resuelto por el backend. `null`/`undefined` → no renderiza nada. */
90722
+ this.video = input(null);
90723
+ this.asset = computed(() => this.video() ?? null);
90724
+ this.thumbFailed = signal(false);
90725
+ /**
90726
+ * Arma el embed SIEMPRE contra un patrón fijo por proveedor conocido — el
90727
+ * `externalId` es el único dato del backend que entra en el `src`. Un
90728
+ * proveedor no reconocido devuelve `null` (el template cae al mensaje, no a
90729
+ * un iframe "por si acaso"). `bypassSecurityTrustResourceUrl` es seguro acá
90730
+ * porque el string completo lo arma este componente, nunca el usuario.
90731
+ */
90732
+ this.embedUrl = computed(() => {
90733
+ const v = this.asset();
90734
+ if (!v || v.source !== 'link' || !v.externalId)
90735
+ return null;
90736
+ const raw = this.buildEmbedUrl(v.provider, v.externalId);
90737
+ return raw ? this.sanitizer.bypassSecurityTrustResourceUrl(raw) : null;
90738
+ });
90739
+ if (!this.i18n.hasNamespace('VideoPlayer')) {
90740
+ this.i18n.registerContent('VideoPlayer', VIDEO_PLAYER_I18N);
90741
+ }
90742
+ }
90743
+ buildEmbedUrl(provider, externalId) {
90744
+ switch (provider) {
90745
+ case 'youtube':
90746
+ return `https://www.youtube-nocookie.com/embed/${encodeURIComponent(externalId)}`;
90747
+ case 'vimeo':
90748
+ return `https://player.vimeo.com/video/${encodeURIComponent(externalId)}`;
90749
+ default:
90750
+ return null;
90751
+ }
90752
+ }
90753
+ t(key) {
90754
+ return this.i18n.t(key, 'VideoPlayer');
90755
+ }
90756
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: VideoPlayerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
90757
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: VideoPlayerComponent, isStandalone: true, selector: "val-video-player", inputs: { video: { classPropertyName: "video", publicName: "video", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
90758
+ @if (asset(); as v) {
90759
+ @if (v.source === 'upload') {
90760
+ @if (v.status === 'ready' && v.url) {
90761
+ <video
90762
+ class="val-video-player__video"
90763
+ [src]="v.url"
90764
+ [poster]="v.thumbnail || undefined"
90765
+ controls
90766
+ preload="none"
90767
+ playsinline
90768
+ ></video>
90769
+ } @else if (v.status === 'rejected' || v.status === 'failed') {
90770
+ <div class="val-video-player__state val-video-player__state--error">
90771
+ @if (v.thumbnail && !thumbFailed()) {
90772
+ <img
90773
+ class="val-video-player__poster"
90774
+ [src]="v.thumbnail"
90775
+ alt=""
90776
+ loading="lazy"
90777
+ (error)="thumbFailed.set(true)"
90778
+ />
90779
+ }
90780
+ <div class="val-video-player__overlay">
90781
+ <strong>{{ v.status === 'rejected' ? t('rejectedTitle') : t('failedTitle') }}</strong>
90782
+ @if (v.failReason) {
90783
+ <span>{{ v.failReason }}</span>
90784
+ }
90785
+ </div>
90786
+ </div>
90787
+ } @else {
90788
+ <!-- uploading | processing -->
90789
+ <div class="val-video-player__state" aria-live="polite">
90790
+ @if (v.thumbnail && !thumbFailed()) {
90791
+ <img
90792
+ class="val-video-player__poster"
90793
+ [src]="v.thumbnail"
90794
+ alt=""
90795
+ loading="lazy"
90796
+ (error)="thumbFailed.set(true)"
90797
+ />
90798
+ }
90799
+ <div class="val-video-player__overlay">
90800
+ <span class="val-video-player__spinner" aria-hidden="true"></span>
90801
+ <strong>{{ v.status === 'uploading' ? t('uploadingTitle') : t('processingTitle') }}</strong>
90802
+ <span>{{ v.status === 'uploading' ? t('uploadingBody') : t('processingBody') }}</span>
90803
+ </div>
90804
+ </div>
90805
+ }
90806
+ } @else {
90807
+ <!-- source: link -->
90808
+ @if (embedUrl(); as src) {
90809
+ <div class="val-video-player__embed-wrap">
90810
+ <iframe
90811
+ class="val-video-player__embed"
90812
+ [src]="src"
90813
+ title="video"
90814
+ frameborder="0"
90815
+ referrerpolicy="strict-origin-when-cross-origin"
90816
+ allow="accelerometer; encrypted-media; gyroscope; picture-in-picture"
90817
+ allowfullscreen
90818
+ loading="lazy"
90819
+ ></iframe>
90820
+ </div>
90821
+ } @else {
90822
+ <div class="val-video-player__state">
90823
+ <div class="val-video-player__overlay">
90824
+ <span>{{ t('unsupportedLink') }}</span>
90825
+ </div>
90826
+ </div>
90827
+ }
90828
+ }
90829
+ }
90830
+ `, isInline: true, styles: [":host{display:block}.val-video-player__video{display:block;width:100%;max-height:70vh;border-radius:12px;background:#000}.val-video-player__embed-wrap{position:relative;width:100%;padding-top:56.25%;border-radius:12px;overflow:hidden;background:#000}.val-video-player__embed{position:absolute;inset:0;width:100%;height:100%;border:0}.val-video-player__state{position:relative;width:100%;aspect-ratio:16 / 9;border-radius:12px;overflow:hidden;background:var(--ion-color-light, rgba(0, 0, 0, .06));display:flex;align-items:flex-end}.val-video-player__poster{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:.55}.val-video-player__overlay{position:relative;z-index:1;display:flex;flex-direction:column;gap:4px;width:100%;padding:14px 16px;color:var(--ion-text-color, #000);background:linear-gradient(to top,rgba(0,0,0,.08),transparent)}.val-video-player__state--error .val-video-player__overlay{color:var(--ion-color-danger, #eb445a)}.val-video-player__overlay strong{font-size:.9375rem}.val-video-player__overlay span{font-size:.8125rem;opacity:.85}.val-video-player__spinner{width:18px;height:18px;border-radius:50%;border:2px solid rgba(127,127,127,.35);border-top-color:var(--ion-color-dark, #313131);animation:val-video-player-spin .8s linear infinite}@keyframes val-video-player-spin{to{transform:rotate(360deg)}}\n"] }); }
90831
+ }
90832
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: VideoPlayerComponent, decorators: [{
90833
+ type: Component,
90834
+ args: [{ selector: 'val-video-player', standalone: true, imports: [], template: `
90835
+ @if (asset(); as v) {
90836
+ @if (v.source === 'upload') {
90837
+ @if (v.status === 'ready' && v.url) {
90838
+ <video
90839
+ class="val-video-player__video"
90840
+ [src]="v.url"
90841
+ [poster]="v.thumbnail || undefined"
90842
+ controls
90843
+ preload="none"
90844
+ playsinline
90845
+ ></video>
90846
+ } @else if (v.status === 'rejected' || v.status === 'failed') {
90847
+ <div class="val-video-player__state val-video-player__state--error">
90848
+ @if (v.thumbnail && !thumbFailed()) {
90849
+ <img
90850
+ class="val-video-player__poster"
90851
+ [src]="v.thumbnail"
90852
+ alt=""
90853
+ loading="lazy"
90854
+ (error)="thumbFailed.set(true)"
90855
+ />
90856
+ }
90857
+ <div class="val-video-player__overlay">
90858
+ <strong>{{ v.status === 'rejected' ? t('rejectedTitle') : t('failedTitle') }}</strong>
90859
+ @if (v.failReason) {
90860
+ <span>{{ v.failReason }}</span>
90861
+ }
90862
+ </div>
90863
+ </div>
90864
+ } @else {
90865
+ <!-- uploading | processing -->
90866
+ <div class="val-video-player__state" aria-live="polite">
90867
+ @if (v.thumbnail && !thumbFailed()) {
90868
+ <img
90869
+ class="val-video-player__poster"
90870
+ [src]="v.thumbnail"
90871
+ alt=""
90872
+ loading="lazy"
90873
+ (error)="thumbFailed.set(true)"
90874
+ />
90875
+ }
90876
+ <div class="val-video-player__overlay">
90877
+ <span class="val-video-player__spinner" aria-hidden="true"></span>
90878
+ <strong>{{ v.status === 'uploading' ? t('uploadingTitle') : t('processingTitle') }}</strong>
90879
+ <span>{{ v.status === 'uploading' ? t('uploadingBody') : t('processingBody') }}</span>
90880
+ </div>
90881
+ </div>
90882
+ }
90883
+ } @else {
90884
+ <!-- source: link -->
90885
+ @if (embedUrl(); as src) {
90886
+ <div class="val-video-player__embed-wrap">
90887
+ <iframe
90888
+ class="val-video-player__embed"
90889
+ [src]="src"
90890
+ title="video"
90891
+ frameborder="0"
90892
+ referrerpolicy="strict-origin-when-cross-origin"
90893
+ allow="accelerometer; encrypted-media; gyroscope; picture-in-picture"
90894
+ allowfullscreen
90895
+ loading="lazy"
90896
+ ></iframe>
90897
+ </div>
90898
+ } @else {
90899
+ <div class="val-video-player__state">
90900
+ <div class="val-video-player__overlay">
90901
+ <span>{{ t('unsupportedLink') }}</span>
90902
+ </div>
90903
+ </div>
90904
+ }
90905
+ }
90906
+ }
90907
+ `, styles: [":host{display:block}.val-video-player__video{display:block;width:100%;max-height:70vh;border-radius:12px;background:#000}.val-video-player__embed-wrap{position:relative;width:100%;padding-top:56.25%;border-radius:12px;overflow:hidden;background:#000}.val-video-player__embed{position:absolute;inset:0;width:100%;height:100%;border:0}.val-video-player__state{position:relative;width:100%;aspect-ratio:16 / 9;border-radius:12px;overflow:hidden;background:var(--ion-color-light, rgba(0, 0, 0, .06));display:flex;align-items:flex-end}.val-video-player__poster{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:.55}.val-video-player__overlay{position:relative;z-index:1;display:flex;flex-direction:column;gap:4px;width:100%;padding:14px 16px;color:var(--ion-text-color, #000);background:linear-gradient(to top,rgba(0,0,0,.08),transparent)}.val-video-player__state--error .val-video-player__overlay{color:var(--ion-color-danger, #eb445a)}.val-video-player__overlay strong{font-size:.9375rem}.val-video-player__overlay span{font-size:.8125rem;opacity:.85}.val-video-player__spinner{width:18px;height:18px;border-radius:50%;border:2px solid rgba(127,127,127,.35);border-top-color:var(--ion-color-dark, #313131);animation:val-video-player-spin .8s linear infinite}@keyframes val-video-player-spin{to{transform:rotate(360deg)}}\n"] }]
90908
+ }], ctorParameters: () => [] });
90909
+
90910
+ /**
90911
+ * Tipos de `val-video-player` (ADR-087 — video como servicio de plataforma).
90912
+ *
90913
+ * Espejo agnóstico al dominio de `content.VideoAsset` (backend Go,
90914
+ * `backend/go/services/content/video.go`) — mismos nombres de campo en
90915
+ * camelCase. Cualquier app pasa acá el `VideoAsset` que le devuelve SU propio
90916
+ * backend (sermón en Eklesee, receta en Okhelia, animal en Chesed, artículo de
90917
+ * ayuda) con un mapeo trivial; el componente no conoce ni le importa de qué
90918
+ * dominio viene.
90919
+ */
90920
+
89864
90921
  /*
89865
90922
  * Public API Surface of valtech-components
89866
90923
  */
@@ -89870,5 +90927,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
89870
90927
  * Generated bundle index. Do not edit.
89871
90928
  */
89872
90929
 
89873
- export { ACCESS_REQUEST_TYPE, ACCESS_REQUEST_VIEW_I18N, ACTION_CARD_DEFAULTS, AD_SIZE_MAP, API_TABLE_COLUMN_LABELS, APP_VERSION_PLATFORM_PLUGIN, APP_VERSION_REMOTE_PLUGIN, ARTICLE_CARD_DEFAULTS, ARTICLE_SPACING, ARTICLE_STRIP_DEFAULTS, AUTH_CTA_DEFAULTS, AVATAR_UPLOAD_DEFAULTS, AboutViewComponent, AccessControlService, AccessRequestViewComponent, AccordionComponent, AccountViewComponent, ActionCardComponent, ActionHeaderComponent, ActionType, AdSlotComponent, AdsLoaderService, AdsService, AlertBoxComponent, AnalyticsErrorHandler, AnalyticsRouterTracker, AnalyticsService, AnimalCardComponent, AnimatedTerminalComponent, ApiKeyCreateModalComponent, ApiKeyService, ApiKeysModalComponent, ApiKeysViewComponent, AppConfigService, AppVersionService, ArticleBuilder, ArticleCardComponent, ArticleComponent, ArticleStripComponent, AttachmentUploaderComponent, AuthBackgroundComponent, AuthCtaComponent, AuthService, AuthStateService, AuthStorageService, AuthSyncService, AvatarComponent, AvatarUploadComponent, BOTTOM_NAV_DEFAULTS, BackButtonComponent, BannerComponent, BlogPostBuilder, BottomNavComponent, BoxComponent, BreadcrumbComponent, BubbleBlitzGameComponent, ButtonComponent, ButtonGroupComponent, CALLOUT_LABELS, CHEV_KEYS, CIRCLE_KEYS, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CORNER_KEYS, CTA_CARD_DEFAULTS, CURRENCY_INFO, CanAccessDirective, CardComponent, CardSection, CardType, CardsCarouselComponent, ChangeEmailModalComponent, ChangePasswordModalComponent, ChatComposerComponent, ChatInputComponent, ChatWindowComponent, CheckInputComponent, CheckboxRadioInputComponent, ChipGroupComponent, ChipSelectComponent, ClientTelemetryService, CodeDisplayComponent, CodeValidatorComponent, CollectionsService, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CommsPreferencesService, CommsPreferencesSettingsComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ConfirmationDialogV2Component, ContainerComponent, ContentLoaderComponent, ContentReactionComponent, ContentReactionModalComponent, ContentService, ContentTransformer, ConversationListItemComponent, ConversationService, CookieBannerComponent, CookieSettingsComponent, CountdownComponent, CreateOrgModalComponent, CtaCardComponent, CurrencyInputComponent, DEFAULT_ADS_CONFIG, DEFAULT_APP_CONFIG_SERVICE_CONFIG, DEFAULT_APP_VERSION_SERVICE_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_BACK_HEADER, DEFAULT_BUTTON_PRESETS, DEFAULT_CANCEL_BUTTON, DEFAULT_CANONICAL_FIELD_ALIASES, 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_LOGIN_LOGO, DEFAULT_MODAL_CANCEL_BUTTON, DEFAULT_MODAL_CONFIRM_BUTTON, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLATFORMS, DEFAULT_POST_UPDATE_GRACE_MS, DEFAULT_PRESETS, DEFAULT_REFRESHER_METADATA, DEFAULT_SKELETON_CONFIG, DEFAULT_SPLASH_SCREEN_CONFIG, DangerSectionComponent, DataTableComponent, DatasetPaginationService, DateInputComponent, DatePickerComponent, DateRangeInputComponent, DebugConsoleComponent, DeleteAccountModalComponent, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsBuilder, DocsCalloutComponent, DocsCodeExampleComponent, DocsLayoutComponent, DocsNavLinksComponent, DocsNavigationService, DocsPageComponent, DocsSearchComponent, DocsSectionComponent, DocsShellComponent, DocsSidebarComponent, DocsTocComponent, DonationService, DownloadService, EXPECTED_NOT_FOUND, EditOrgModalComponent, EmptyStateComponent, EntityCardComponent, EntityFeedService, EntradaSerializada, ExpandableTextComponent, ExternalBrowserService, FEATURES_LIST_DEFAULTS, FUN_MODAL_DEFAULTS, FabComponent, FaqComponent, FeatureControlService, FeatureGuardDirective, FeaturesListComponent, FeedbackFormComponent, FeedbackService, FieldListComponent, FieldOptionsEditorComponent, FileInputComponent, FirebaseService, FirestoreCollectionFactory, FirestoreService, FolderTabsComponent, FontSizeOption, FontSizeSelectorComponent, FontSizeService, FooterComponent, FooterLinksComponent, FormComponent, FormFieldComponent, FormSchemaBuilderService, FormSkeletonComponent, FunHeaderComponent, FunModalComponent, GAME_AVATAR_CATALOG_SIZE_PER_STYLE, GameAvatarComponent, GameProfileService, GlassComponent, GlowCardComponent, GlowComponent, GridSkeletonComponent, GroupMembersComponent, GroupPickerComponent, GroupsService, HANDOFF_ROUTE_PARAM, HANDOFF_TOKEN_PARAM, HandleService, HandoffService, HapticsService, HasPermissionDirective, HeaderActionsService, HeaderComponent, HintComponent, HorizontalScrollComponent, HrefComponent, HtmlViewerModalComponent, I18nService, IMAGE_DEFAULTS, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, INVITATION_CARD_DEFAULTS, IONIC_COLORS$5 as IONIC_COLORS, Icon, IconComponent, IconService, ImageComponent, ImageCropComponent, ImageService, InAppBrowserService, InfiniteListComponent, InfoComponent, InputI18nHelper, InputType, InvitationCardComponent, InviteMemberModalComponent, ItemListComponent, KNOWN_ROUTES, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LEGAL_CONTENT_CONFIG, LOGGED_IN_HINT_COOKIE, LOGIN_DEFAULTS, LandingSplitComponent, LandingStepsComponent, LanguageSelectorComponent, LanguageSelectorV2Component, LayeredCardComponent, LegalContentService, LegalLinkService, LightRippleDirective, LinkComponent, LinkProcessorService, LinkedProvidersComponent, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LiveReadFallbackService, LoadMoreComponent, LoadingDirective, LocalStorageService, LocaleService, LoginAttemptModalComponent, LoginComponent, MEDIA_OBJECT_DEFAULTS, MEMBER_CARD_DEFAULTS, METADATA_LIST_DEFAULTS, META_SCHEMA_VERSION, METRIC_CARD_DEFAULTS, MINI_GAMES_I18N, MINI_GAME_PLAYER_AVATARS, MINI_GAME_PLAYER_COLORS, MODAL_SIZES, MOTIF_KEYS, MOTION, MaintenancePageComponent, MarkdownArticleParserService, MediaObjectComponent, MediaViewerModalComponent, MemberCardComponent, MemberDetailModalComponent, MemberImportModalComponent, MemoryGameComponent, MenuComponent, MessageBubbleComponent, MessagingService, MetaService, MetadataListComponent, MeteringService, MetricCardComponent, MfaModalComponent, MiniGameCalloutComponent, MiniGamePlayerProfileService, MiniGameScorePopComponent, MiniGamesMenuComponent, ModalLayoutComponent, ModalService, ModalShellComponent, MultiSelectSearchComponent, NUM_KEYS, NavigationService, NetworkBannerComponent, NetworkStatusService, NewsBuilder, NoContentComponent, NotesBoxComponent, NoticeComponent, NotificationActionService, NotificationPreferencesViewComponent, NotificationsService, NotificationsViewComponent, NumberFromToComponent, NumberPickerComponent, NumberStepperComponent, OAUTH_PROVIDERS_INFO, OAuthCallbackComponent, OAuthService, OperationReferenceComponent, OptionCardsComponent, OptionSheetComponent, OptionSheetService, OrgInfoSheetComponent, OrgService, OrgSwitchService, OrganizationViewComponent, PATTERN_MOTIFS, PATTERN_PALETTES, PATTERN_STYLE_CONFIGS, PERSONA_CONFIG, PLATFORM_CONFIGS, POST_UPDATE_TS_KEY, PageContentComponent, PageLinksComponent, PageRefreshService, PageTemplateComponent, PageWavesComponent, PageWrapperComponent, PaginationComponent, PaginationService, PasswordInputComponent, PatternComponent, PdfService, PermissionCatalogService, PermissionSelectorComponent, PermissionsModalComponent, PermissionsViewComponent, PersonaService, PhoneDisplayComponent, PhoneFormatService, PhoneInputComponent, PickerV2Component, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PreferencesService, PreferencesViewComponent, PresetService, PriceTagComponent, PricingTableComponent, ProcessLinksPipe, ProfileCardComponent, ProfileContentComponent, ProfileModalComponent, ProfileSkeletonComponent, ProfileViewComponent, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrBrandValidationError, QrCodeComponent, QrGeneratorService, QrScannerComponent, QueryBuilder, QuoteBoxComponent, REQUEST_STATUSES, RadioInputComponent, RangeInputComponent, RatingComponent, RbacService, ReactionBarComponent, ReactionsService, RefresherComponent, RequestFirestoreService, RequestFormBuilderService, RequestFormComponent, RequestModalComponent, RequestReviewPanelComponent, RequestService, RetroAudioService, RightsFooterComponent, RoleManagerComponent, RotatingTextComponent, SEARCH_HEADER_DEFAULTS, SETTINGS_SECTIONS_CATALOG, SHAPE_KEYS, SHARE_PROFILE_MODAL_DEFAULTS, SKELETON_LAYOUT_DEFAULT_ROWS, SKELETON_PRESETS, SOLID_KEYS, STATS_BAR_DEFAULTS, STROKE_KEYS, SearchHeaderComponent, SearchSelectorComponent, SearchbarComponent, SectionHeaderComponent, SecurityViewComponent, SegmentControlComponent, SelectSearchComponent, SelectSearchPickerModalComponent, SessionListModalComponent, SessionService, SessionTransitionOverlayComponent, SettingsHubComponent, ShareButtonsComponent, ShareProfileModalComponent, SimonGameComponent, SimpleComponent, SkeletonComponent, SkeletonLayoutComponent, SkeletonService, SplashComponent, SplashScreenService, StatsBarComponent, StatsCardComponent, StepperComponent, StorageService, SupportTicketCtaComponent, SwipeCarouselComponent, SwitchOrgModalComponent, TRI_KEYS, TabbedContentComponent, TableSkeletonComponent, TabsComponent, Terminal404Component, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeSelectorComponent, ThemeService, ThreadPanelComponent, TicketCardComponent, TicketCardImageService, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TransferOwnershipModalComponent, TranslatePipe, TypedCollection, TypingIndicatorComponent, UPDATE_BANNER_DEFAULT_CONTENT, UPDATE_BANNER_I18N_NAMESPACE, UpdateBannerComponent, UsageMetersComponent, UsageService, UserAvatarComponent, UsernameInputComponent, VALTECH_ACCESS_FEATURES, VALTECH_ADS_CONFIG, VALTECH_APP_CONFIG, VALTECH_APP_VERSION, VALTECH_AUTH_CONFIG, VALTECH_CHAT_CONFIG, VALTECH_COLLECTIONS_CONFIG, VALTECH_COMMS_PREFERENCES_CONFIG, VALTECH_COMPANY_LINKS, VALTECH_CONTENT_CONFIG, VALTECH_COPYRIGHT_TEMPLATE, VALTECH_DEBUG_CONSOLE, VALTECH_DEFAULT_CONTENT, VALTECH_DIAGRAMS, VALTECH_DONATION_CONFIG, VALTECH_FEEDBACK_CONFIG, VALTECH_FIREBASE_CONFIG, VALTECH_FOOTER_I18N, VALTECH_FOOTER_LOGO, VALTECH_LANGUAGE_SELECTOR, VALTECH_LEGAL_CONFIG, VALTECH_LEGAL_ENTITY, VALTECH_MENU_I18N, VALTECH_NETWORK_ERROR_KEY, VALTECH_REACTIONS_CONFIG, VALTECH_SETTINGS_MENU_LINKS, VALTECH_SITE_PATHS, VALTECH_SOCIAL_LINKS, VALTECH_SPLASH_SCREEN, VALTECH_WEB_BASE_URLS, VALTECH_WHATSAPP_CONFIG, VAL_REGISTERED_ICONS, VERSION, ValCommentThreadComponent, ValQuotaWarningComponent, ValtechErrorService, VerifyViewComponent, WhatsappFabComponent, WhatsappService, WizardComponent, WizardFooterComponent, WorkflowService, accessGuard, accessGuardFromRoute, applyDefaultValueToControl, authGuard, authInterceptor, authPasswordValidator, beautifyLegalArticle, blogPost, buildCompanyFooterProps, buildFooterLinks, buildLegalLinkResolver, buildPath, buildPlatformMenu, buildSettingsCards, buildSideNavItemsFromBottomNav, button, canSubmitRequestType, classifyChip, collections, connectPageRefresh, createErrorStateProps, createFirebaseConfig, createGameAvatarCatalog, createGameAvatarProps, createGlowCardProps, createInitialDatasetState, createInitialPaginationState, createNumberFromToField, createPageState, createPermissionLabeler, createRefreshableStream, createTitleProps, datasetPageFromLegacyCursor, defaultQrBrand, docs, errorLoggingInterceptor, evaluateValtechAccess, extractPathParams, firmaDeSesionDeCustomToken, formatClockTime, formatDateSeparator, formatRelativeTime, gameAvatarCatalogEntryToMetadata, gameAvatarDataUri, generatePatternTiles, generateRandomTile, getAppInfo, getAppVersion, getCollectionPath, getDocumentId, getTimeOfDayKey, goToTop, groupPermissionsByScope, guestGuard, hasEmulators, iconButton, interpretError, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isIonicColor, isKnownRoute, isValidPath, joinPath, maxLength, mulberry32, news, parseMarkdownArticle, permissionGuard, permissionGuardFromRoute, provideLegalContent, providePersona, provideSplashScreen, provideValtechAboutRoutes, provideValtechAccessFeatures, provideValtechAccountRoutes, provideValtechAds, provideValtechApiKeysRoutes, provideValtechAppConfig, provideValtechAppVersion, provideValtechAppVersionHttp, provideValtechAuth, provideValtechAuthInterceptor, provideValtechChat, provideValtechCollections, provideValtechCommsPreferences, provideValtechContent, provideValtechDebugConsole, provideValtechDiagrams, provideValtechDonations, provideValtechErrorHandling, provideValtechFeedback, provideValtechFirebase, provideValtechI18n, provideValtechLegal, provideValtechNotificationClickActions, provideValtechNotificationPreferencesRoutes, provideValtechNotificationsRoutes, provideValtechOrganizationRoutes, provideValtechPermissionsRoutes, provideValtechPreferencesRoutes, provideValtechPresets, provideValtechProfileRoutes, provideValtechReactions, provideValtechSecurityRoutes, provideValtechSettingsRoutes, provideValtechSite, provideValtechSkeleton, provideValtechWhatsapp, qrContrastRatio, qrErrorCorrectionFor, query, rbacGuard, renderGameAvatarSvg, renderPatternSvgInner, replaceSpecialChars, requestSubmitMode, resolveColor, resolveCopyrightTemplate, resolveInputDefaultValue, resolveIonicColor, resolveWebBaseUrl, roleGuard, roleOf, selectableRequestTypes, storagePaths, suggestEmailFix, superAdminGuard, supportedGameAvatarStyles, toArticle, validateQrBrand, validateRoutes };
90930
+ export { ACCESS_REQUEST_TYPE, ACCESS_REQUEST_VIEW_I18N, ACTION_CARD_DEFAULTS, AD_SIZE_MAP, API_TABLE_COLUMN_LABELS, APP_VERSION_PLATFORM_PLUGIN, APP_VERSION_REMOTE_PLUGIN, ARTICLE_CARD_DEFAULTS, ARTICLE_SPACING, ARTICLE_STRIP_DEFAULTS, AUTH_CTA_DEFAULTS, AVATAR_UPLOAD_DEFAULTS, AboutViewComponent, AccessControlService, AccessRequestViewComponent, AccordionComponent, AccountViewComponent, ActionCardComponent, ActionHeaderComponent, ActionType, AdSlotComponent, AdsLoaderService, AdsService, AlertBoxComponent, AnalyticsErrorHandler, AnalyticsRouterTracker, AnalyticsService, AnimalCardComponent, AnimatedTerminalComponent, ApiKeyCreateModalComponent, ApiKeyService, ApiKeysModalComponent, ApiKeysViewComponent, AppConfigService, AppVersionService, ArticleBuilder, ArticleCardComponent, ArticleComponent, ArticleStripComponent, AttachmentUploaderComponent, AuthBackgroundComponent, AuthCtaComponent, AuthService, AuthStateService, AuthStorageService, AuthSyncService, AvatarComponent, AvatarUploadComponent, BOTTOM_NAV_DEFAULTS, BackButtonComponent, BannerComponent, BlogPostBuilder, BottomNavComponent, BoxComponent, BreadcrumbComponent, BubbleBlitzGameComponent, ButtonComponent, ButtonGroupComponent, CALLOUT_LABELS, CHEV_KEYS, CIRCLE_KEYS, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CORNER_KEYS, CTA_CARD_DEFAULTS, CURRENCY_INFO, CanAccessDirective, CardComponent, CardSection, CardType, CardsCarouselComponent, ChangeEmailModalComponent, ChangePasswordModalComponent, ChatComposerComponent, ChatInputComponent, ChatWindowComponent, CheckInputComponent, CheckboxRadioInputComponent, ChipGroupComponent, ChipSelectComponent, ClientTelemetryService, CodeDisplayComponent, CodeValidatorComponent, CollectionsService, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CommsPreferencesService, CommsPreferencesSettingsComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ConfirmationDialogV2Component, ContainerComponent, ContentLoaderComponent, ContentReactionComponent, ContentReactionModalComponent, ContentService, ContentTransformer, ConversationListItemComponent, ConversationService, CookieBannerComponent, CookieSettingsComponent, CountdownComponent, CreateOrgModalComponent, CtaCardComponent, CurrencyInputComponent, DEFAULT_ADS_CONFIG, DEFAULT_APP_CONFIG_SERVICE_CONFIG, DEFAULT_APP_VERSION_SERVICE_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_BACK_HEADER, DEFAULT_BUTTON_PRESETS, DEFAULT_CANCEL_BUTTON, DEFAULT_CANONICAL_FIELD_ALIASES, 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_LOGIN_LOGO, DEFAULT_MODAL_CANCEL_BUTTON, DEFAULT_MODAL_CONFIRM_BUTTON, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLATFORMS, DEFAULT_POST_UPDATE_GRACE_MS, DEFAULT_PRESETS, DEFAULT_REFRESHER_METADATA, DEFAULT_SKELETON_CONFIG, DEFAULT_SPLASH_SCREEN_CONFIG, DangerSectionComponent, DataTableComponent, DatasetPaginationService, DateInputComponent, DatePickerComponent, DateRangeInputComponent, DebugConsoleComponent, DeleteAccountModalComponent, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsBuilder, DocsCalloutComponent, DocsCodeExampleComponent, DocsLayoutComponent, DocsNavLinksComponent, DocsNavigationService, DocsPageComponent, DocsSearchComponent, DocsSectionComponent, DocsShellComponent, DocsSidebarComponent, DocsTocComponent, DonationService, DownloadService, EXPECTED_NOT_FOUND, EditOrgModalComponent, EmptyStateComponent, EntityCardComponent, EntityFeedService, EntradaSerializada, ExpandableTextComponent, ExternalBrowserService, FEATURES_LIST_DEFAULTS, FUN_MODAL_DEFAULTS, FabComponent, FaqComponent, FeatureControlService, FeatureGuardDirective, FeaturesListComponent, FeedbackFormComponent, FeedbackService, FieldListComponent, FieldOptionsEditorComponent, FileInputComponent, FirebaseService, FirestoreCollectionFactory, FirestoreService, FolderTabsComponent, FontSizeOption, FontSizeSelectorComponent, FontSizeService, FooterComponent, FooterLinksComponent, FormComponent, FormFieldComponent, FormSchemaBuilderService, FormSkeletonComponent, FunHeaderComponent, FunModalComponent, GAME_AVATAR_CATALOG_SIZE_PER_STYLE, GameAvatarComponent, GameProfileService, GlassComponent, GlowCardComponent, GlowComponent, GridSkeletonComponent, GroupMembersComponent, GroupPickerComponent, GroupsService, HANDOFF_ROUTE_PARAM, HANDOFF_TOKEN_PARAM, HandleService, HandoffService, HapticsService, HasPermissionDirective, HeaderActionsService, HeaderComponent, HintComponent, HorizontalScrollComponent, HrefComponent, HtmlViewerModalComponent, I18nService, IMAGE_DEFAULTS, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, INVITATION_CARD_DEFAULTS, IONIC_COLORS$5 as IONIC_COLORS, Icon, IconComponent, IconService, ImageComponent, ImageCropComponent, ImageService, InAppBrowserService, InfiniteListComponent, InfoComponent, InputI18nHelper, InputType, InvitationCardComponent, InviteMemberModalComponent, ItemListComponent, KNOWN_ROUTES, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LEGAL_CONTENT_CONFIG, LOGGED_IN_HINT_COOKIE, LOGIN_DEFAULTS, LandingSplitComponent, LandingStepsComponent, LanguageSelectorComponent, LanguageSelectorV2Component, LayeredCardComponent, LegalContentService, LegalLinkService, LightRippleDirective, LinkComponent, LinkProcessorService, LinkedProvidersComponent, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LiveReadFallbackService, LoadMoreComponent, LoadingDirective, LocalStorageService, LocaleService, LoginAttemptModalComponent, LoginComponent, MEDIA_OBJECT_DEFAULTS, MEMBER_CARD_DEFAULTS, METADATA_LIST_DEFAULTS, META_SCHEMA_VERSION, METRIC_CARD_DEFAULTS, MINI_GAMES_I18N, MINI_GAME_PLAYER_AVATARS, MINI_GAME_PLAYER_COLORS, MODAL_SIZES, MOTIF_KEYS, MOTION, MaintenancePageComponent, MarkdownArticleParserService, MediaObjectComponent, MediaViewerModalComponent, MemberCardComponent, MemberDetailModalComponent, MemberImportModalComponent, MemoryGameComponent, MenuComponent, MessageBubbleComponent, MessagingService, MetaService, MetadataListComponent, MeteringService, MetricCardComponent, MfaModalComponent, MiniGameCalloutComponent, MiniGamePlayerProfileService, MiniGameScorePopComponent, MiniGamesMenuComponent, ModalLayoutComponent, ModalService, ModalShellComponent, MultiSelectSearchComponent, NUM_KEYS, NavigationService, NetworkBannerComponent, NetworkStatusService, NewsBuilder, NoContentComponent, NotesBoxComponent, NoticeComponent, NotificationActionService, NotificationPreferencesViewComponent, NotificationsService, NotificationsViewComponent, NumberFromToComponent, NumberPickerComponent, NumberStepperComponent, OAUTH_PROVIDERS_INFO, OAuthCallbackComponent, OAuthService, OperationReferenceComponent, OptionCardsComponent, OptionSheetComponent, OptionSheetService, OrgInfoSheetComponent, OrgService, OrgSwitchService, OrganizationViewComponent, PATTERN_MOTIFS, PATTERN_PALETTES, PATTERN_STYLE_CONFIGS, PERSONA_CONFIG, PLATFORM_CONFIGS, POST_UPDATE_TS_KEY, PageBlockComponent, PageContentComponent, PageLinksComponent, PageRefreshService, PageTemplateComponent, PageWavesComponent, PageWrapperComponent, PaginationComponent, PaginationService, PasswordInputComponent, PatternComponent, PdfService, PermissionCatalogService, PermissionSelectorComponent, PermissionsModalComponent, PermissionsViewComponent, PersonaService, PhoneDisplayComponent, PhoneFormatService, PhoneInputComponent, PickerV2Component, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PreferencesService, PreferencesViewComponent, PresetService, PriceTagComponent, PricingTableComponent, ProcessLinksPipe, ProfileCardComponent, ProfileContentComponent, ProfileModalComponent, ProfileSkeletonComponent, ProfileViewComponent, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrBrandValidationError, QrCodeComponent, QrGeneratorService, QrScannerComponent, QueryBuilder, QuoteBoxComponent, REQUEST_STATUSES, RadioInputComponent, RangeInputComponent, RatingComponent, RbacService, ReactionBarComponent, ReactionsService, RefresherComponent, RequestFirestoreService, RequestFormBuilderService, RequestFormComponent, RequestModalComponent, RequestReviewPanelComponent, RequestService, RetroAudioService, RichEditorComponent, RightsFooterComponent, RoleManagerComponent, RotatingTextComponent, SEARCH_HEADER_DEFAULTS, SETTINGS_SECTIONS_CATALOG, SHAPE_KEYS, SHARE_PROFILE_MODAL_DEFAULTS, SKELETON_LAYOUT_DEFAULT_ROWS, SKELETON_PRESETS, SOLID_KEYS, STATS_BAR_DEFAULTS, STROKE_KEYS, SearchHeaderComponent, SearchSelectorComponent, SearchbarComponent, SectionHeaderComponent, SecurityViewComponent, SegmentControlComponent, SelectSearchComponent, SelectSearchPickerModalComponent, SessionListModalComponent, SessionService, SessionTransitionOverlayComponent, SettingsHubComponent, ShareButtonsComponent, ShareProfileModalComponent, SimonGameComponent, SimpleComponent, SkeletonComponent, SkeletonLayoutComponent, SkeletonService, SplashComponent, SplashScreenService, StatsBarComponent, StatsCardComponent, StepperComponent, StorageService, SupportTicketCtaComponent, SwipeCarouselComponent, SwitchOrgModalComponent, TRI_KEYS, TabbedContentComponent, TableSkeletonComponent, TabsComponent, Terminal404Component, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeSelectorComponent, ThemeService, ThreadPanelComponent, TicketCardComponent, TicketCardImageService, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TransferOwnershipModalComponent, TranslatePipe, TypedCollection, TypingIndicatorComponent, UPDATE_BANNER_DEFAULT_CONTENT, UPDATE_BANNER_I18N_NAMESPACE, UpdateBannerComponent, UsageMetersComponent, UsageService, UserAvatarComponent, UsernameInputComponent, VALTECH_ACCESS_FEATURES, VALTECH_ADS_CONFIG, VALTECH_APP_CONFIG, VALTECH_APP_VERSION, VALTECH_AUTH_CONFIG, VALTECH_CHAT_CONFIG, VALTECH_COLLECTIONS_CONFIG, VALTECH_COMMS_PREFERENCES_CONFIG, VALTECH_COMPANY_LINKS, VALTECH_CONTENT_CONFIG, VALTECH_COPYRIGHT_TEMPLATE, VALTECH_DEBUG_CONSOLE, VALTECH_DEFAULT_CONTENT, VALTECH_DIAGRAMS, VALTECH_DONATION_CONFIG, VALTECH_FEEDBACK_CONFIG, VALTECH_FIREBASE_CONFIG, VALTECH_FOOTER_I18N, VALTECH_FOOTER_LOGO, VALTECH_LANGUAGE_SELECTOR, VALTECH_LEGAL_CONFIG, VALTECH_LEGAL_ENTITY, VALTECH_MENU_I18N, VALTECH_NETWORK_ERROR_KEY, VALTECH_REACTIONS_CONFIG, VALTECH_SETTINGS_MENU_LINKS, VALTECH_SITE_PATHS, VALTECH_SOCIAL_LINKS, VALTECH_SPLASH_SCREEN, VALTECH_WEB_BASE_URLS, VALTECH_WHATSAPP_CONFIG, VAL_REGISTERED_ICONS, VERSION, ValCommentThreadComponent, ValQuotaWarningComponent, ValtechErrorService, VerifyViewComponent, VideoPlayerComponent, WhatsappFabComponent, WhatsappService, WizardComponent, WizardFooterComponent, WorkflowService, accessGuard, accessGuardFromRoute, applyDefaultValueToControl, articleToTiptapDoc, authGuard, authInterceptor, authPasswordValidator, beautifyLegalArticle, blogPost, buildCompanyFooterProps, buildFooterLinks, buildLegalLinkResolver, buildPath, buildPlatformMenu, buildSettingsCards, buildSideNavItemsFromBottomNav, button, canSubmitRequestType, classifyChip, collections, connectPageRefresh, createErrorStateProps, createFirebaseConfig, createGameAvatarCatalog, createGameAvatarProps, createGlowCardProps, createInitialDatasetState, createInitialPaginationState, createNumberFromToField, createPageState, createPermissionLabeler, createRefreshableStream, createTitleProps, datasetPageFromLegacyCursor, defaultQrBrand, docs, errorLoggingInterceptor, evaluateValtechAccess, extractPathParams, firmaDeSesionDeCustomToken, formatClockTime, formatDateSeparator, formatRelativeTime, gameAvatarCatalogEntryToMetadata, gameAvatarDataUri, generatePatternTiles, generateRandomTile, getAppInfo, getAppVersion, getCollectionPath, getDocumentId, getTimeOfDayKey, goToTop, groupPermissionsByScope, guestGuard, hasEmulators, iconButton, interpretError, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isIonicColor, isKnownRoute, isValidPath, joinPath, maxLength, mulberry32, news, parseMarkdownArticle, permissionGuard, permissionGuardFromRoute, provideLegalContent, providePersona, provideSplashScreen, provideValtechAboutRoutes, provideValtechAccessFeatures, provideValtechAccountRoutes, provideValtechAds, provideValtechApiKeysRoutes, provideValtechAppConfig, provideValtechAppVersion, provideValtechAppVersionHttp, provideValtechAuth, provideValtechAuthInterceptor, provideValtechChat, provideValtechCollections, provideValtechCommsPreferences, provideValtechContent, provideValtechDebugConsole, provideValtechDiagrams, provideValtechDonations, provideValtechErrorHandling, provideValtechFeedback, provideValtechFirebase, provideValtechI18n, provideValtechLegal, provideValtechNotificationClickActions, provideValtechNotificationPreferencesRoutes, provideValtechNotificationsRoutes, provideValtechOrganizationRoutes, provideValtechPermissionsRoutes, provideValtechPreferencesRoutes, provideValtechPresets, provideValtechProfileRoutes, provideValtechReactions, provideValtechSecurityRoutes, provideValtechSettingsRoutes, provideValtechSite, provideValtechSkeleton, provideValtechWhatsapp, qrContrastRatio, qrErrorCorrectionFor, query, rbacGuard, renderGameAvatarSvg, renderPatternSvgInner, replaceSpecialChars, requestSubmitMode, resolveColor, resolveCopyrightTemplate, resolveInputDefaultValue, resolveIonicColor, resolveWebBaseUrl, roleGuard, roleOf, selectableRequestTypes, storagePaths, suggestEmailFix, superAdminGuard, supportedGameAvatarStyles, tiptapDocToArticle, tiptapDocToArticleElements, toArticle, validateQrBrand, validateRoutes };
89874
90931
  //# sourceMappingURL=valtech-components.mjs.map