valtech-components 2.0.1006 → 2.0.1008

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.
@@ -25,7 +25,7 @@ import { getApps, getApp, initializeApp as initializeApp$1 } from 'firebase/app'
25
25
  import * as i2$1 from '@ionic/angular/standalone';
26
26
  import { AlertController, IonAvatar, IonCard, IonIcon, IonButton, IonSpinner, IonText, IonModal, IonHeader, IonToolbar, IonContent, IonButtons, IonTitle, IonProgressBar, IonSkeletonText, IonFab, IonFabButton, IonFabList, IonLabel, IonCardContent, IonCardHeader, IonCardTitle, IonCardSubtitle, IonCheckbox, IonTextarea, IonDatetime, IonInput, IonSelect, IonSelectOption, IonPopover, IonList, IonItem, IonRadioGroup, IonRadio, IonRange, IonSearchbar, IonSegment, IonSegmentButton, IonToggle, IonRippleEffect, IonAccordion, IonAccordionGroup, IonTabBar, IonTabButton, IonBadge, IonBreadcrumb, IonBreadcrumbs, IonChip, IonNote, IonDatetimeButton, ToastController as ToastController$1, IonCol, IonRow, IonRefresher, IonRefresherContent, IonMenuButton, IonFooter, IonListHeader, IonInfiniteScroll, IonInfiniteScrollContent, IonGrid, MenuController, IonMenu, IonMenuToggle, IonSplitPane } from '@ionic/angular/standalone';
27
27
  import { addIcons } from 'ionicons';
28
- import { addOutline, addCircleOutline, alertOutline, alertCircleOutline, arrowBackOutline, arrowForwardOutline, arrowDownOutline, settings, settingsOutline, checkmarkCircleOutline, ellipsisHorizontalOutline, notifications, notificationsOutline, openOutline, closeOutline, chatbubblesOutline, shareOutline, heart, heartOutline, home, homeOutline, eyeOffOutline, eyeOutline, scanOutline, chevronDownOutline, chevronForwardOutline, checkmarkOutline, clipboardOutline, copyOutline, filterOutline, locationOutline, calendarOutline, businessOutline, logoTwitter, logoInstagram, logoLinkedin, logoYoutube, logoTiktok, logoFacebook, logoGoogle, createOutline, trashOutline, playOutline, peopleOutline, phonePortraitOutline, refreshOutline, documentTextOutline, lockClosedOutline, informationCircleOutline, logoNpm, removeOutline, optionsOutline, personOutline, shieldCheckmarkOutline, keyOutline, desktopOutline, logOutOutline, cloudDownloadOutline, cloudOutline, gitBranchOutline, layersOutline, serverOutline, warningOutline, bulbOutline, lockOpenOutline, diamondOutline, add, close, share, create, trash, star, camera, mic, send, downloadOutline, chevronDown, language, globeOutline, checkmark, list, grid, apps, menu, search, person, helpCircle, informationCircle, documentText, mail, calendar, folder, chevronForward, ellipsisHorizontal, chevronBack, playBack, playForward, ellipse, starOutline, starHalf, heartHalf, checkmarkCircle, timeOutline, flag, trendingUp, trendingDown, remove, analytics, people, cash, cart, eye, chatbubbleOutline, thumbsUpOutline, thumbsUp, happyOutline, happy, sadOutline, sad, chevronUp, pin, pencil, callOutline, logoWhatsapp, paperPlaneOutline, mailOutline, chevronDownCircleOutline, closeCircle, alertCircle, logoApple, logoMicrosoft, linkOutline, unlinkOutline, cloudOfflineOutline, documentOutline, attachOutline, cameraOutline, closeCircleOutline, imageOutline, chevronBackOutline, sendOutline, chatbubbleEllipsesOutline, swapVerticalOutline, chevronUpOutline, searchOutline, cartOutline, chatbubble, compass, compassOutline, gridOutline, listOutline, folderOutline, documents, documentsOutline, statsChart, statsChartOutline, laptopOutline, swapHorizontalOutline, notificationsOffOutline, alarmOutline, chatboxEllipsesOutline, checkmarkDoneOutline, cogOutline, shareSocialOutline, helpCircleOutline, menuOutline } from 'ionicons/icons';
28
+ import { addOutline, addCircleOutline, alertOutline, alertCircleOutline, arrowBackOutline, arrowForwardOutline, arrowDownOutline, settings, settingsOutline, checkmarkCircleOutline, ellipsisHorizontalOutline, notifications, notificationsOutline, openOutline, closeOutline, chatbubblesOutline, shareOutline, heart, heartOutline, home, homeOutline, eyeOffOutline, eyeOutline, scanOutline, chevronDownOutline, chevronForwardOutline, checkmarkOutline, clipboardOutline, copyOutline, filterOutline, locationOutline, calendarOutline, businessOutline, logoTwitter, logoInstagram, logoLinkedin, logoYoutube, logoTiktok, logoFacebook, logoGoogle, createOutline, trashOutline, playOutline, peopleOutline, phonePortraitOutline, refreshOutline, documentTextOutline, lockClosedOutline, informationCircleOutline, logoNpm, removeOutline, optionsOutline, personOutline, shieldCheckmarkOutline, keyOutline, desktopOutline, logOutOutline, cloudDownloadOutline, cloudOutline, gitBranchOutline, layersOutline, serverOutline, warningOutline, bulbOutline, lockOpenOutline, diamondOutline, add, close, share, create, trash, star, camera, mic, send, downloadOutline, chevronDown, language, globeOutline, checkmark, list, grid, apps, menu, search, person, helpCircle, informationCircle, documentText, mail, calendar, folder, chevronForward, ellipsisHorizontal, chevronBack, playBack, playForward, ellipse, starOutline, starHalf, heartHalf, checkmarkCircle, timeOutline, flag, trendingUp, trendingDown, remove, analytics, people, cash, cart, eye, chatbubbleOutline, thumbsUpOutline, thumbsUp, happyOutline, happy, sadOutline, sad, chevronUp, pin, pencil, callOutline, logoWhatsapp, paperPlaneOutline, mailOutline, chevronDownCircleOutline, closeCircle, alertCircle, logoApple, logoMicrosoft, linkOutline, unlinkOutline, cloudOfflineOutline, documentOutline, attachOutline, cameraOutline, closeCircleOutline, imageOutline, chevronBackOutline, sendOutline, chatbubbleEllipsesOutline, swapVerticalOutline, chevronUpOutline, searchOutline, cartOutline, chatbubble, compass, compassOutline, gridOutline, listOutline, folderOutline, documents, documentsOutline, statsChart, statsChartOutline, laptopOutline, swapHorizontalOutline, notificationsOffOutline, alarmOutline, chatboxEllipsesOutline, checkmarkDoneOutline, cogOutline, menuOutline } from 'ionicons/icons';
29
29
  import { Browser } from '@capacitor/browser';
30
30
  import * as i1$6 from '@angular/platform-browser';
31
31
  import { DomSanitizer, Meta, Title } from '@angular/platform-browser';
@@ -54,7 +54,7 @@ import 'prismjs/components/prism-json';
54
54
  * Current version of valtech-components.
55
55
  * This is automatically updated during the publish process.
56
56
  */
57
- const VERSION = '2.0.1006';
57
+ const VERSION = '2.0.1008';
58
58
 
59
59
  // Control de estado de refresco (singleton a nivel de módulo)
60
60
  let isRefreshing = false;
@@ -21657,7 +21657,7 @@ class CommentComponent {
21657
21657
  </div>
21658
21658
  }
21659
21659
  </div>
21660
- `, isInline: true, styles: [":host{display:block}.comment{--indent-size: 24px;--max-indent: 72px;padding:12px 0}.comment.highlighted{background-color:rgba(var(--ion-color-primary-rgb),.08);border-radius:8px;padding:12px;margin:-4px}.comment.pinned{border-left:3px solid var(--ion-color-primary);padding-left:12px}.comment.collapsed{opacity:.7}.pinned-indicator{display:flex;align-items:center;gap:4px;font-size:.75rem;color:var(--ion-color-primary);margin-bottom:8px;padding-left:44px}.pinned-indicator ion-icon{font-size:.875rem}.comment-main{display:flex;gap:12px}.comment-avatar{flex-shrink:0;cursor:pointer}.comment-avatar val-avatar{--size: 36px}.comment-avatar .avatar-placeholder{width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,var(--ion-color-primary),var(--ion-color-secondary));display:flex;align-items:center;justify-content:center;color:#fff;font-size:.875rem;font-weight:600}.comment-body{flex:1;min-width:0}.comment-header{display:flex;align-items:center;flex-wrap:wrap;gap:6px;margin-bottom:4px}.author-info{display:flex;align-items:center;gap:4px;cursor:pointer}.author-info:hover .author-name{text-decoration:underline}.author-name{font-weight:600;font-size:.875rem;color:var(--ion-text-color)}.verified-badge{color:var(--ion-color-primary);font-size:1rem}.author-username{font-size:.8125rem;color:var(--ion-color-medium)}.comment-meta{display:flex;align-items:center;gap:6px;margin-left:auto}.timestamp{font-size:.75rem;color:var(--ion-color-medium)}.edited-label{font-size:.6875rem;color:var(--ion-color-medium);font-style:italic}.more-button{--padding-start: 4px;--padding-end: 4px;margin:0;height:28px}.more-button ion-icon{font-size:1.125rem;color:var(--ion-color-medium)}.menu-divider{height:1px;background-color:var(--ion-color-light);margin:4px 0}.comment-content{font-size:.875rem;line-height:1.5;color:var(--ion-text-color);word-wrap:break-word;overflow-wrap:break-word}.comment-content :global(.comment-link){color:var(--ion-color-primary);text-decoration:none}.comment-content :global(.comment-link):hover{text-decoration:underline}.comment-content :global(.comment-mention){color:var(--ion-color-primary);font-weight:500;cursor:pointer}.comment-content :global(.comment-mention):hover{text-decoration:underline}.comment-content :global(.comment-hashtag){color:var(--ion-color-primary);cursor:pointer}.comment-content :global(.comment-hashtag):hover{text-decoration:underline}.comment-reactions{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}.reaction-button{display:flex;align-items:center;gap:4px;background:none;border:none;padding:4px 8px;border-radius:16px;cursor:pointer;font-size:.8125rem;color:var(--reaction-color, var(--ion-color-medium));transition:all .2s ease;background-color:transparent}.reaction-button:hover{background-color:rgba(var(--ion-color-medium-rgb),.1)}.reaction-button.active{background-color:rgba(var(--ion-color-primary-rgb),.1)}.reaction-button.active ion-icon{transform:scale(1.1)}.reaction-button ion-icon{font-size:1.125rem;transition:transform .2s ease}.reaction-button .reaction-count{font-weight:500}.comment-actions{display:flex;flex-wrap:wrap;gap:4px;margin-top:6px;margin-left:-8px}.comment-actions ion-button{--padding-start: 8px;--padding-end: 8px;font-size:.8125rem;height:32px;text-transform:none;font-weight:500}.expand-button,.collapse-button,.load-more-button,.view-replies-button{display:flex;align-items:center;gap:6px;background:none;border:none;padding:8px 12px;cursor:pointer;font-size:.8125rem;color:var(--ion-color-primary);border-radius:8px;transition:background-color .2s}.expand-button:hover,.collapse-button:hover,.load-more-button:hover,.view-replies-button:hover{background-color:rgba(var(--ion-color-primary-rgb),.1)}.expand-button ion-icon,.collapse-button ion-icon,.load-more-button ion-icon,.view-replies-button ion-icon{font-size:1rem}.expand-button{margin-top:4px;color:var(--ion-color-medium)}.comment-children{margin-top:12px;padding-left:min(var(--indent-size),var(--max-indent));border-left:2px solid var(--ion-color-light-shade)}.comment-children val-comment{display:block}.comment-children val-comment:first-of-type .comment{padding-top:0}.load-more-button,.view-replies-button{margin-top:8px;margin-left:0}ion-popover{--width: 200px}ion-popover ion-list{padding:4px 0}ion-popover ion-item{--min-height: 40px;--padding-start: 12px;font-size:.875rem}ion-popover ion-item ion-icon{font-size:1.125rem;margin-right:8px}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 2px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 2px);height:max(28px,36px - 2px);font-size:max(11px,14px - 1px)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 4px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 4px);height:max(28px,36px - 4px);font-size:max(11px,14px - 2px)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 6px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 6px);height:max(28px,36px - 6px);font-size:max(11px,14px - 3px)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 8px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 8px);height:max(28px,36px - 8px);font-size:max(11px,14px - 4px)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 10px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 10px);height:max(28px,36px - 10px);font-size:max(11px,14px - 5px)}:host-context(.dark) .comment.highlighted,:host-context(.ion-palette-dark) .comment.highlighted,:host-context(html.ion-palette-dark) .comment.highlighted,:host-context(body.dark) .comment.highlighted,:host-context([data-theme=dark]) .comment.highlighted{background-color:rgba(var(--ion-color-primary-rgb),.12)}:host-context(.dark) .avatar-placeholder,:host-context(.ion-palette-dark) .avatar-placeholder,:host-context(html.ion-palette-dark) .avatar-placeholder,:host-context(body.dark) .avatar-placeholder,:host-context([data-theme=dark]) .avatar-placeholder{background:linear-gradient(135deg,var(--ion-color-primary-shade),var(--ion-color-secondary-shade))}\n"], dependencies: [{ kind: "component", type: CommentComponent, selector: "val-comment", inputs: ["props"], outputs: ["authorClick", "reactionClick", "actionClick", "menuItemClick", "loadMoreClick", "collapseToggle"] }, { kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: 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: IonPopover, selector: "ion-popover" }, { kind: "component", type: IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: AvatarComponent, selector: "val-avatar", inputs: ["preset", "props"], outputs: ["onClick"] }] }); }
21660
+ `, isInline: true, styles: ["@charset \"UTF-8\";:root{--val-container-sm: 540px;--val-container-md: 720px;--val-container-lg: 880px;--val-container-xl: 1100px;--val-container-padding: 16px;--ion-color-primary: #7026df;--ion-color-primary-rgb: 112, 38, 223;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #6321c4;--ion-color-primary-tint: #7e3ce2;--ion-color-secondary: #e2ccff;--ion-color-secondary-rgb: 226, 204, 255;--ion-color-secondary-contrast: #000000;--ion-color-secondary-contrast-rgb: 0, 0, 0;--ion-color-secondary-shade: #c7b4e0;--ion-color-secondary-tint: #e5d1ff;--ion-color-texti: #354c69;--ion-color-texti-rgb: 53, 76, 105;--ion-color-texti-contrast: #ffffff;--ion-color-texti-contrast-rgb: 255, 255, 255;--ion-color-texti-shade: #2f435c;--ion-color-texti-tint: #495e78;--ion-color-darki: #090f1b;--ion-color-darki-rgb: 9, 15, 27;--ion-color-darki-contrast: #ffffff;--ion-color-darki-contrast-rgb: 255, 255, 255;--ion-color-darki-shade: #080d18;--ion-color-darki-tint: #222732;--ion-color-medium: #9e9e9e;--ion-color-medium-rgb: 158, 158, 158;--ion-color-medium-contrast: #000000;--ion-color-medium-contrast-rgb: 0, 0, 0;--ion-color-medium-shade: #8b8b8b;--ion-color-medium-tint: #a8a8a8;--swiper-pagination-color: var(--ion-color-primary);--swiper-navigation-color: var(--ion-color-primary);--swiper-pagination-bullet-inactive-color: var(--ion-color-medium)}body.dark,html.ion-palette-dark,body[data-theme=dark]{--ion-color-texti: #8fc1ff;--ion-color-texti-rgb: 143, 193, 255;--ion-color-texti-contrast: #000000;--ion-color-texti-contrast-rgb: 0, 0, 0;--ion-color-texti-shade: #7eaae0;--ion-color-texti-tint: #9ac7ff;--ion-color-darki: #ffffff;--ion-color-darki-rgb: 255, 255, 255;--ion-color-darki-contrast: #000000;--ion-color-darki-contrast-rgb: 0, 0, 0;--ion-color-darki-shade: #e0e0e0;--ion-color-darki-tint: #ffffff;--ion-color-primary: #8f49f8;--ion-color-primary-rgb: 143, 73, 248;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #7e40da;--ion-color-primary-tint: #9a5bf9}.ion-color-texti{--ion-color-base: var(--ion-color-texti);--ion-color-base-rgb: var(--ion-color-texti-rgb);--ion-color-contrast: var(--ion-color-texti-contrast);--ion-color-contrast-rgb: var(--ion-color-texti-contrast-rgb);--ion-color-shade: var(--ion-color-texti-shade);--ion-color-tint: var(--ion-color-texti-tint)}.ion-color-darki{--ion-color-base: var(--ion-color-darki);--ion-color-base-rgb: var(--ion-color-darki-rgb);--ion-color-contrast: var(--ion-color-darki-contrast);--ion-color-contrast-rgb: var(--ion-color-darki-contrast-rgb);--ion-color-shade: var(--ion-color-darki-shade);--ion-color-tint: var(--ion-color-darki-tint)}:host{display:block}.comment{--indent-size: 24px;--max-indent: 72px;padding:12px 0}.comment.highlighted{background-color:rgba(var(--ion-color-primary-rgb),.08);border-radius:8px;padding:12px;margin:-4px}.comment.pinned{border-left:3px solid var(--ion-color-primary);padding-left:12px}.comment.collapsed{opacity:.7}.pinned-indicator{display:flex;align-items:center;gap:4px;font-size:.75rem;color:var(--ion-color-primary);margin-bottom:8px;padding-left:44px}.pinned-indicator ion-icon{font-size:.875rem}.comment-main{display:flex;gap:12px}.comment-avatar{flex-shrink:0;cursor:pointer}.comment-avatar val-avatar{--size: 36px}.comment-avatar .avatar-placeholder{width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,var(--ion-color-primary),var(--ion-color-secondary));display:flex;align-items:center;justify-content:center;color:#fff;font-size:.875rem;font-weight:600}.comment-body{flex:1;min-width:0}.comment-header{display:flex;align-items:center;flex-wrap:wrap;gap:6px;margin-bottom:4px}.author-info{display:flex;align-items:center;gap:4px;cursor:pointer}.author-info:hover .author-name{text-decoration:underline}.author-name{font-weight:600;font-size:.875rem;color:var(--ion-text-color)}.verified-badge{color:var(--ion-color-primary);font-size:1rem}.author-username{font-size:.8125rem;color:var(--ion-color-medium)}.comment-meta{display:flex;align-items:center;gap:6px;margin-left:auto}.timestamp{font-size:.75rem;color:var(--ion-color-medium)}.edited-label{font-size:.6875rem;color:var(--ion-color-medium);font-style:italic}.more-button{--padding-start: 4px;--padding-end: 4px;margin:0;height:28px}.more-button ion-icon{font-size:1.125rem;color:var(--ion-color-medium)}.menu-divider{height:1px;background-color:var(--ion-color-light);margin:4px 0}.comment-content{font-size:.875rem;line-height:1.5;color:var(--ion-text-color);word-wrap:break-word;overflow-wrap:break-word}.comment-content :global(.comment-link){color:var(--ion-color-primary);text-decoration:none}.comment-content :global(.comment-link):hover{text-decoration:underline}.comment-content :global(.comment-mention){color:var(--ion-color-primary);font-weight:500;cursor:pointer}.comment-content :global(.comment-mention):hover{text-decoration:underline}.comment-content :global(.comment-hashtag){color:var(--ion-color-primary);cursor:pointer}.comment-content :global(.comment-hashtag):hover{text-decoration:underline}.comment-reactions{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}.reaction-button{display:flex;align-items:center;gap:4px;background:none;border:none;padding:4px 8px;border-radius:16px;cursor:pointer;font-size:.8125rem;color:var(--reaction-color, var(--ion-color-medium));transition:all .2s ease;background-color:transparent}.reaction-button:hover{background-color:rgba(var(--ion-color-medium-rgb),.1)}.reaction-button.active{background-color:rgba(var(--ion-color-primary-rgb),.1)}.reaction-button.active ion-icon{transform:scale(1.1)}.reaction-button ion-icon{font-size:1.125rem;transition:transform .2s ease}.reaction-button .reaction-count{font-weight:500}.comment-actions{display:flex;flex-wrap:wrap;gap:4px;margin-top:6px;margin-left:-8px}.comment-actions ion-button{--padding-start: 8px;--padding-end: 8px;font-size:.8125rem;height:32px;text-transform:none;font-weight:500}.expand-button,.collapse-button,.load-more-button,.view-replies-button{display:flex;align-items:center;gap:6px;background:none;border:none;padding:8px 12px;cursor:pointer;font-size:.8125rem;color:var(--ion-color-primary);border-radius:8px;transition:background-color .2s}.expand-button:hover,.collapse-button:hover,.load-more-button:hover,.view-replies-button:hover{background-color:rgba(var(--ion-color-primary-rgb),.1)}.expand-button ion-icon,.collapse-button ion-icon,.load-more-button ion-icon,.view-replies-button ion-icon{font-size:1rem}.expand-button{margin-top:4px;color:var(--ion-color-medium)}.comment-children{margin-top:12px;padding-left:min(var(--indent-size),var(--max-indent));border-left:2px solid var(--ion-color-light-shade)}.comment-children val-comment{display:block}.comment-children val-comment:first-of-type .comment{padding-top:0}.load-more-button,.view-replies-button{margin-top:8px;margin-left:0}ion-popover{--width: 200px}ion-popover ion-list{padding:4px 0}ion-popover ion-item{--min-height: 40px;--padding-start: 12px;font-size:.875rem}ion-popover ion-item ion-icon{font-size:1.125rem;margin-right:8px}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 2px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 2px);height:max(28px,36px - 2px);font-size:max(.6875rem,.8125rem)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 4px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 4px);height:max(28px,36px - 4px);font-size:max(.6875rem,.75rem)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 6px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 6px);height:max(28px,36px - 6px);font-size:max(.6875rem,.6875rem)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 8px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 8px);height:max(28px,36px - 8px);font-size:max(.6875rem,.625rem)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 10px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 10px);height:max(28px,36px - 10px);font-size:max(.6875rem,.5625rem)}:host-context(.dark) .comment.highlighted,:host-context(.ion-palette-dark) .comment.highlighted,:host-context(html.ion-palette-dark) .comment.highlighted,:host-context(body.dark) .comment.highlighted,:host-context([data-theme=dark]) .comment.highlighted{background-color:rgba(var(--ion-color-primary-rgb),.12)}:host-context(.dark) .avatar-placeholder,:host-context(.ion-palette-dark) .avatar-placeholder,:host-context(html.ion-palette-dark) .avatar-placeholder,:host-context(body.dark) .avatar-placeholder,:host-context([data-theme=dark]) .avatar-placeholder{background:linear-gradient(135deg,var(--ion-color-primary-shade),var(--ion-color-secondary-shade))}\n"], dependencies: [{ kind: "component", type: CommentComponent, selector: "val-comment", inputs: ["props"], outputs: ["authorClick", "reactionClick", "actionClick", "menuItemClick", "loadMoreClick", "collapseToggle"] }, { kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: 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: IonPopover, selector: "ion-popover" }, { kind: "component", type: IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: AvatarComponent, selector: "val-avatar", inputs: ["preset", "props"], outputs: ["onClick"] }] }); }
21661
21661
  }
21662
21662
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CommentComponent, decorators: [{
21663
21663
  type: Component,
@@ -21847,7 +21847,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
21847
21847
  </div>
21848
21848
  }
21849
21849
  </div>
21850
- `, styles: [":host{display:block}.comment{--indent-size: 24px;--max-indent: 72px;padding:12px 0}.comment.highlighted{background-color:rgba(var(--ion-color-primary-rgb),.08);border-radius:8px;padding:12px;margin:-4px}.comment.pinned{border-left:3px solid var(--ion-color-primary);padding-left:12px}.comment.collapsed{opacity:.7}.pinned-indicator{display:flex;align-items:center;gap:4px;font-size:.75rem;color:var(--ion-color-primary);margin-bottom:8px;padding-left:44px}.pinned-indicator ion-icon{font-size:.875rem}.comment-main{display:flex;gap:12px}.comment-avatar{flex-shrink:0;cursor:pointer}.comment-avatar val-avatar{--size: 36px}.comment-avatar .avatar-placeholder{width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,var(--ion-color-primary),var(--ion-color-secondary));display:flex;align-items:center;justify-content:center;color:#fff;font-size:.875rem;font-weight:600}.comment-body{flex:1;min-width:0}.comment-header{display:flex;align-items:center;flex-wrap:wrap;gap:6px;margin-bottom:4px}.author-info{display:flex;align-items:center;gap:4px;cursor:pointer}.author-info:hover .author-name{text-decoration:underline}.author-name{font-weight:600;font-size:.875rem;color:var(--ion-text-color)}.verified-badge{color:var(--ion-color-primary);font-size:1rem}.author-username{font-size:.8125rem;color:var(--ion-color-medium)}.comment-meta{display:flex;align-items:center;gap:6px;margin-left:auto}.timestamp{font-size:.75rem;color:var(--ion-color-medium)}.edited-label{font-size:.6875rem;color:var(--ion-color-medium);font-style:italic}.more-button{--padding-start: 4px;--padding-end: 4px;margin:0;height:28px}.more-button ion-icon{font-size:1.125rem;color:var(--ion-color-medium)}.menu-divider{height:1px;background-color:var(--ion-color-light);margin:4px 0}.comment-content{font-size:.875rem;line-height:1.5;color:var(--ion-text-color);word-wrap:break-word;overflow-wrap:break-word}.comment-content :global(.comment-link){color:var(--ion-color-primary);text-decoration:none}.comment-content :global(.comment-link):hover{text-decoration:underline}.comment-content :global(.comment-mention){color:var(--ion-color-primary);font-weight:500;cursor:pointer}.comment-content :global(.comment-mention):hover{text-decoration:underline}.comment-content :global(.comment-hashtag){color:var(--ion-color-primary);cursor:pointer}.comment-content :global(.comment-hashtag):hover{text-decoration:underline}.comment-reactions{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}.reaction-button{display:flex;align-items:center;gap:4px;background:none;border:none;padding:4px 8px;border-radius:16px;cursor:pointer;font-size:.8125rem;color:var(--reaction-color, var(--ion-color-medium));transition:all .2s ease;background-color:transparent}.reaction-button:hover{background-color:rgba(var(--ion-color-medium-rgb),.1)}.reaction-button.active{background-color:rgba(var(--ion-color-primary-rgb),.1)}.reaction-button.active ion-icon{transform:scale(1.1)}.reaction-button ion-icon{font-size:1.125rem;transition:transform .2s ease}.reaction-button .reaction-count{font-weight:500}.comment-actions{display:flex;flex-wrap:wrap;gap:4px;margin-top:6px;margin-left:-8px}.comment-actions ion-button{--padding-start: 8px;--padding-end: 8px;font-size:.8125rem;height:32px;text-transform:none;font-weight:500}.expand-button,.collapse-button,.load-more-button,.view-replies-button{display:flex;align-items:center;gap:6px;background:none;border:none;padding:8px 12px;cursor:pointer;font-size:.8125rem;color:var(--ion-color-primary);border-radius:8px;transition:background-color .2s}.expand-button:hover,.collapse-button:hover,.load-more-button:hover,.view-replies-button:hover{background-color:rgba(var(--ion-color-primary-rgb),.1)}.expand-button ion-icon,.collapse-button ion-icon,.load-more-button ion-icon,.view-replies-button ion-icon{font-size:1rem}.expand-button{margin-top:4px;color:var(--ion-color-medium)}.comment-children{margin-top:12px;padding-left:min(var(--indent-size),var(--max-indent));border-left:2px solid var(--ion-color-light-shade)}.comment-children val-comment{display:block}.comment-children val-comment:first-of-type .comment{padding-top:0}.load-more-button,.view-replies-button{margin-top:8px;margin-left:0}ion-popover{--width: 200px}ion-popover ion-list{padding:4px 0}ion-popover ion-item{--min-height: 40px;--padding-start: 12px;font-size:.875rem}ion-popover ion-item ion-icon{font-size:1.125rem;margin-right:8px}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 2px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 2px);height:max(28px,36px - 2px);font-size:max(11px,14px - 1px)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 4px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 4px);height:max(28px,36px - 4px);font-size:max(11px,14px - 2px)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 6px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 6px);height:max(28px,36px - 6px);font-size:max(11px,14px - 3px)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 8px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 8px);height:max(28px,36px - 8px);font-size:max(11px,14px - 4px)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 10px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 10px);height:max(28px,36px - 10px);font-size:max(11px,14px - 5px)}:host-context(.dark) .comment.highlighted,:host-context(.ion-palette-dark) .comment.highlighted,:host-context(html.ion-palette-dark) .comment.highlighted,:host-context(body.dark) .comment.highlighted,:host-context([data-theme=dark]) .comment.highlighted{background-color:rgba(var(--ion-color-primary-rgb),.12)}:host-context(.dark) .avatar-placeholder,:host-context(.ion-palette-dark) .avatar-placeholder,:host-context(html.ion-palette-dark) .avatar-placeholder,:host-context(body.dark) .avatar-placeholder,:host-context([data-theme=dark]) .avatar-placeholder{background:linear-gradient(135deg,var(--ion-color-primary-shade),var(--ion-color-secondary-shade))}\n"] }]
21850
+ `, styles: ["@charset \"UTF-8\";:root{--val-container-sm: 540px;--val-container-md: 720px;--val-container-lg: 880px;--val-container-xl: 1100px;--val-container-padding: 16px;--ion-color-primary: #7026df;--ion-color-primary-rgb: 112, 38, 223;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #6321c4;--ion-color-primary-tint: #7e3ce2;--ion-color-secondary: #e2ccff;--ion-color-secondary-rgb: 226, 204, 255;--ion-color-secondary-contrast: #000000;--ion-color-secondary-contrast-rgb: 0, 0, 0;--ion-color-secondary-shade: #c7b4e0;--ion-color-secondary-tint: #e5d1ff;--ion-color-texti: #354c69;--ion-color-texti-rgb: 53, 76, 105;--ion-color-texti-contrast: #ffffff;--ion-color-texti-contrast-rgb: 255, 255, 255;--ion-color-texti-shade: #2f435c;--ion-color-texti-tint: #495e78;--ion-color-darki: #090f1b;--ion-color-darki-rgb: 9, 15, 27;--ion-color-darki-contrast: #ffffff;--ion-color-darki-contrast-rgb: 255, 255, 255;--ion-color-darki-shade: #080d18;--ion-color-darki-tint: #222732;--ion-color-medium: #9e9e9e;--ion-color-medium-rgb: 158, 158, 158;--ion-color-medium-contrast: #000000;--ion-color-medium-contrast-rgb: 0, 0, 0;--ion-color-medium-shade: #8b8b8b;--ion-color-medium-tint: #a8a8a8;--swiper-pagination-color: var(--ion-color-primary);--swiper-navigation-color: var(--ion-color-primary);--swiper-pagination-bullet-inactive-color: var(--ion-color-medium)}body.dark,html.ion-palette-dark,body[data-theme=dark]{--ion-color-texti: #8fc1ff;--ion-color-texti-rgb: 143, 193, 255;--ion-color-texti-contrast: #000000;--ion-color-texti-contrast-rgb: 0, 0, 0;--ion-color-texti-shade: #7eaae0;--ion-color-texti-tint: #9ac7ff;--ion-color-darki: #ffffff;--ion-color-darki-rgb: 255, 255, 255;--ion-color-darki-contrast: #000000;--ion-color-darki-contrast-rgb: 0, 0, 0;--ion-color-darki-shade: #e0e0e0;--ion-color-darki-tint: #ffffff;--ion-color-primary: #8f49f8;--ion-color-primary-rgb: 143, 73, 248;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #7e40da;--ion-color-primary-tint: #9a5bf9}.ion-color-texti{--ion-color-base: var(--ion-color-texti);--ion-color-base-rgb: var(--ion-color-texti-rgb);--ion-color-contrast: var(--ion-color-texti-contrast);--ion-color-contrast-rgb: var(--ion-color-texti-contrast-rgb);--ion-color-shade: var(--ion-color-texti-shade);--ion-color-tint: var(--ion-color-texti-tint)}.ion-color-darki{--ion-color-base: var(--ion-color-darki);--ion-color-base-rgb: var(--ion-color-darki-rgb);--ion-color-contrast: var(--ion-color-darki-contrast);--ion-color-contrast-rgb: var(--ion-color-darki-contrast-rgb);--ion-color-shade: var(--ion-color-darki-shade);--ion-color-tint: var(--ion-color-darki-tint)}:host{display:block}.comment{--indent-size: 24px;--max-indent: 72px;padding:12px 0}.comment.highlighted{background-color:rgba(var(--ion-color-primary-rgb),.08);border-radius:8px;padding:12px;margin:-4px}.comment.pinned{border-left:3px solid var(--ion-color-primary);padding-left:12px}.comment.collapsed{opacity:.7}.pinned-indicator{display:flex;align-items:center;gap:4px;font-size:.75rem;color:var(--ion-color-primary);margin-bottom:8px;padding-left:44px}.pinned-indicator ion-icon{font-size:.875rem}.comment-main{display:flex;gap:12px}.comment-avatar{flex-shrink:0;cursor:pointer}.comment-avatar val-avatar{--size: 36px}.comment-avatar .avatar-placeholder{width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,var(--ion-color-primary),var(--ion-color-secondary));display:flex;align-items:center;justify-content:center;color:#fff;font-size:.875rem;font-weight:600}.comment-body{flex:1;min-width:0}.comment-header{display:flex;align-items:center;flex-wrap:wrap;gap:6px;margin-bottom:4px}.author-info{display:flex;align-items:center;gap:4px;cursor:pointer}.author-info:hover .author-name{text-decoration:underline}.author-name{font-weight:600;font-size:.875rem;color:var(--ion-text-color)}.verified-badge{color:var(--ion-color-primary);font-size:1rem}.author-username{font-size:.8125rem;color:var(--ion-color-medium)}.comment-meta{display:flex;align-items:center;gap:6px;margin-left:auto}.timestamp{font-size:.75rem;color:var(--ion-color-medium)}.edited-label{font-size:.6875rem;color:var(--ion-color-medium);font-style:italic}.more-button{--padding-start: 4px;--padding-end: 4px;margin:0;height:28px}.more-button ion-icon{font-size:1.125rem;color:var(--ion-color-medium)}.menu-divider{height:1px;background-color:var(--ion-color-light);margin:4px 0}.comment-content{font-size:.875rem;line-height:1.5;color:var(--ion-text-color);word-wrap:break-word;overflow-wrap:break-word}.comment-content :global(.comment-link){color:var(--ion-color-primary);text-decoration:none}.comment-content :global(.comment-link):hover{text-decoration:underline}.comment-content :global(.comment-mention){color:var(--ion-color-primary);font-weight:500;cursor:pointer}.comment-content :global(.comment-mention):hover{text-decoration:underline}.comment-content :global(.comment-hashtag){color:var(--ion-color-primary);cursor:pointer}.comment-content :global(.comment-hashtag):hover{text-decoration:underline}.comment-reactions{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}.reaction-button{display:flex;align-items:center;gap:4px;background:none;border:none;padding:4px 8px;border-radius:16px;cursor:pointer;font-size:.8125rem;color:var(--reaction-color, var(--ion-color-medium));transition:all .2s ease;background-color:transparent}.reaction-button:hover{background-color:rgba(var(--ion-color-medium-rgb),.1)}.reaction-button.active{background-color:rgba(var(--ion-color-primary-rgb),.1)}.reaction-button.active ion-icon{transform:scale(1.1)}.reaction-button ion-icon{font-size:1.125rem;transition:transform .2s ease}.reaction-button .reaction-count{font-weight:500}.comment-actions{display:flex;flex-wrap:wrap;gap:4px;margin-top:6px;margin-left:-8px}.comment-actions ion-button{--padding-start: 8px;--padding-end: 8px;font-size:.8125rem;height:32px;text-transform:none;font-weight:500}.expand-button,.collapse-button,.load-more-button,.view-replies-button{display:flex;align-items:center;gap:6px;background:none;border:none;padding:8px 12px;cursor:pointer;font-size:.8125rem;color:var(--ion-color-primary);border-radius:8px;transition:background-color .2s}.expand-button:hover,.collapse-button:hover,.load-more-button:hover,.view-replies-button:hover{background-color:rgba(var(--ion-color-primary-rgb),.1)}.expand-button ion-icon,.collapse-button ion-icon,.load-more-button ion-icon,.view-replies-button ion-icon{font-size:1rem}.expand-button{margin-top:4px;color:var(--ion-color-medium)}.comment-children{margin-top:12px;padding-left:min(var(--indent-size),var(--max-indent));border-left:2px solid var(--ion-color-light-shade)}.comment-children val-comment{display:block}.comment-children val-comment:first-of-type .comment{padding-top:0}.load-more-button,.view-replies-button{margin-top:8px;margin-left:0}ion-popover{--width: 200px}ion-popover ion-list{padding:4px 0}ion-popover ion-item{--min-height: 40px;--padding-start: 12px;font-size:.875rem}ion-popover ion-item ion-icon{font-size:1.125rem;margin-right:8px}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 2px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 2px);height:max(28px,36px - 2px);font-size:max(.6875rem,.8125rem)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 4px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 4px);height:max(28px,36px - 4px);font-size:max(.6875rem,.75rem)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 6px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 6px);height:max(28px,36px - 6px);font-size:max(.6875rem,.6875rem)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 8px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 8px);height:max(28px,36px - 8px);font-size:max(.6875rem,.625rem)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 10px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 10px);height:max(28px,36px - 10px);font-size:max(.6875rem,.5625rem)}:host-context(.dark) .comment.highlighted,:host-context(.ion-palette-dark) .comment.highlighted,:host-context(html.ion-palette-dark) .comment.highlighted,:host-context(body.dark) .comment.highlighted,:host-context([data-theme=dark]) .comment.highlighted{background-color:rgba(var(--ion-color-primary-rgb),.12)}:host-context(.dark) .avatar-placeholder,:host-context(.ion-palette-dark) .avatar-placeholder,:host-context(html.ion-palette-dark) .avatar-placeholder,:host-context(body.dark) .avatar-placeholder,:host-context([data-theme=dark]) .avatar-placeholder{background:linear-gradient(135deg,var(--ion-color-primary-shade),var(--ion-color-secondary-shade))}\n"] }]
21851
21851
  }], propDecorators: { props: [{
21852
21852
  type: Input
21853
21853
  }], authorClick: [{
@@ -41737,7 +41737,7 @@ const PROFILE_VIEW_I18N = {
41737
41737
  const HANDLE_PATTERN = /^[a-z_][a-z0-9_]*$/;
41738
41738
  const HANDLE_MIN = 3;
41739
41739
  const HANDLE_MAX = 20;
41740
- const DEFAULT_NAMESPACE$g = 'Settings.Profile';
41740
+ const DEFAULT_NAMESPACE$f = 'Settings.Profile';
41741
41741
  /**
41742
41742
  * `val-profile-view` — vista Perfil full-feature autocontenida (organism).
41743
41743
  *
@@ -41787,7 +41787,7 @@ class ProfileViewComponent {
41787
41787
  showPhone: merged.showPhone ?? true,
41788
41788
  showShareCta: merged.showShareCta ?? true,
41789
41789
  profileBaseUrl: merged.profileBaseUrl ?? '',
41790
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$g,
41790
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$f,
41791
41791
  onSaved: merged.onSaved,
41792
41792
  onAvatarUploaded: merged.onAvatarUploaded,
41793
41793
  };
@@ -42263,17 +42263,12 @@ class ProfileViewComponent {
42263
42263
 
42264
42264
  <val-form #form [props]="formMeta" (onSubmit)="onSubmit($event)" />
42265
42265
 
42266
- @if (resolvedConfig().showShareCta && p.handle) {
42267
- <section class="settings-section share-cta-section">
42268
- <val-cta-card [props]="shareCta()" (onAction)="shareOpen.set(true)" />
42269
- </section>
42270
- }
42266
+ <!-- Card de compartir + modal QR removidos temporalmente (se retoman luego).
42267
+ Wiring intacto: shareCta(), shareModal(), shareOpen, showShareCta. -->
42271
42268
  }
42272
42269
  }
42273
42270
  </div>
42274
-
42275
- <val-share-profile-modal [isOpen]="shareOpen()" [props]="shareModal()" (dismissed)="shareOpen.set(false)" />
42276
- `, isInline: true, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.page-header{margin-bottom:16px}.page-header h1{font-size:22px;font-weight:700;margin:0 0 4px}.hint{font-size:13px;opacity:.7;margin:0 0 12px}.settings-section{padding:16px 0}.settings-section+.settings-section{border-top:1px solid var(--val-border-color, rgba(0, 0, 0, .08))}.avatar-section{display:flex;align-items:center;gap:16px}.avatar-meta{display:flex;flex-direction:column;gap:2px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "component", type: AvatarUploadComponent, selector: "val-avatar-upload", inputs: ["props"], outputs: ["uploaded", "error", "uploadStart"] }, { kind: "component", type: EmptyStateComponent, selector: "val-empty-state", inputs: ["props"] }, { kind: "component", type: FormComponent, selector: "val-form", inputs: ["props"], outputs: ["onSubmit", "onInvalid", "onSelectChange"] }, { kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }, { kind: "component", type: SkeletonLayoutComponent, selector: "val-skeleton-layout", inputs: ["props"] }, { kind: "component", type: CtaCardComponent, selector: "val-cta-card", inputs: ["props"], outputs: ["onAction"] }, { kind: "component", type: ShareProfileModalComponent, selector: "val-share-profile-modal", inputs: ["isOpen", "props"], outputs: ["dismissed", "shared"] }] }); }
42271
+ `, isInline: true, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.page-header{margin-bottom:16px}.page-header h1{font-size:22px;font-weight:700;margin:0 0 4px}.hint{font-size:13px;opacity:.7;margin:0 0 12px}.settings-section{padding:16px 0}.settings-section+.settings-section{border-top:1px solid var(--val-border-color, rgba(0, 0, 0, .08))}.avatar-section{display:flex;align-items:center;gap:16px}.avatar-meta{display:flex;flex-direction:column;gap:2px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "component", type: AvatarUploadComponent, selector: "val-avatar-upload", inputs: ["props"], outputs: ["uploaded", "error", "uploadStart"] }, { kind: "component", type: EmptyStateComponent, selector: "val-empty-state", inputs: ["props"] }, { kind: "component", type: FormComponent, selector: "val-form", inputs: ["props"], outputs: ["onSubmit", "onInvalid", "onSelectChange"] }, { kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }, { kind: "component", type: SkeletonLayoutComponent, selector: "val-skeleton-layout", inputs: ["props"] }] }); }
42277
42272
  }
42278
42273
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ProfileViewComponent, decorators: [{
42279
42274
  type: Component,
@@ -42349,16 +42344,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
42349
42344
 
42350
42345
  <val-form #form [props]="formMeta" (onSubmit)="onSubmit($event)" />
42351
42346
 
42352
- @if (resolvedConfig().showShareCta && p.handle) {
42353
- <section class="settings-section share-cta-section">
42354
- <val-cta-card [props]="shareCta()" (onAction)="shareOpen.set(true)" />
42355
- </section>
42356
- }
42347
+ <!-- Card de compartir + modal QR removidos temporalmente (se retoman luego).
42348
+ Wiring intacto: shareCta(), shareModal(), shareOpen, showShareCta. -->
42357
42349
  }
42358
42350
  }
42359
42351
  </div>
42360
-
42361
- <val-share-profile-modal [isOpen]="shareOpen()" [props]="shareModal()" (dismissed)="shareOpen.set(false)" />
42362
42352
  `, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.page-header{margin-bottom:16px}.page-header h1{font-size:22px;font-weight:700;margin:0 0 4px}.hint{font-size:13px;opacity:.7;margin:0 0 12px}.settings-section{padding:16px 0}.settings-section+.settings-section{border-top:1px solid var(--val-border-color, rgba(0, 0, 0, .08))}.avatar-section{display:flex;align-items:center;gap:16px}.avatar-meta{display:flex;flex-direction:column;gap:2px}\n"] }]
42363
42353
  }], ctorParameters: () => [], propDecorators: { config: [{
42364
42354
  type: Input
@@ -42489,6 +42479,21 @@ class PreferencesService {
42489
42479
  this._updateInFlight = false;
42490
42480
  const destroyRef = inject(DestroyRef);
42491
42481
  destroyRef.onDestroy(() => this.unbind());
42482
+ // Seed `_fontSize` con el valor que FontSizeService ya restauró desde
42483
+ // localStorage en su constructor (providedIn root → ya corrió al inyectarlo
42484
+ // acá). Sin esto, el signal arranca en 'medium' y el effect de abajo —que
42485
+ // dispara al marcarse `synced`— forzaría 'medium' pisando el tamaño
42486
+ // restaurado cuando el snapshot Firestore NO trae `fontSize` (user sin ese
42487
+ // campo en el mirror). Bug: cambias el tamaño, refrescas y se pierde.
42488
+ if (this.fontSizeService) {
42489
+ this._fontSize.set(this.fontSizeService.size());
42490
+ }
42491
+ // Mismo seed para theme — ThemeService aplica su valor de localStorage en
42492
+ // bootstrap (`currentOption`). Necesario para desgatear el effect de theme
42493
+ // (abajo) sin que el default 'auto' pise el tema restaurado.
42494
+ if (this.themeService) {
42495
+ this._theme.set(this.themeService.currentOption);
42496
+ }
42492
42497
  // Auto-bind al user actual. effect() corre en injection context (constructor OK).
42493
42498
  // allowSignalWrites: bindToUser/unbind escriben `_synced` (señal interna sin
42494
42499
  // dependencias del propio effect) → seguro y necesario.
@@ -42501,14 +42506,18 @@ class PreferencesService {
42501
42506
  this.unbind();
42502
42507
  }
42503
42508
  }, { allowSignalWrites: true });
42504
- // Side-effect: aplicar theme local cuando llega snapshot real.
42505
- // OJO: usar `toggleUserPreference()` y NO el setter `Theme =`. El setter
42506
- // solo persiste localStorage + emite el BehaviorSubject; no aplica las
42507
- // clases `body.dark`/`body.light` que el palette `dark.class.css` de
42508
- // Ionic necesita para conmutar el tema visible.
42509
+ // Side-effect: aplicar theme. SIN gate `_synced` (local-first, igual que
42510
+ // fontSize): ThemeService/localStorage es la verdad del dispositivo y
42511
+ // `_theme` viene seedeado arriba. Aplicar siempre que cambie da apply
42512
+ // optimista al instante; el sync cross-device también aplica vía snapshot.
42513
+ // Gatearlo en `_synced` hacía que el primer cambio de un user nuevo (sin doc
42514
+ // Firestore aún) no se aplicara: el selector mostraba "Dark" pero el tema no
42515
+ // cambiaba. (El selector de idioma no sufre esto porque val-language-selector
42516
+ // llama i18n.setLanguage directo; el de theme depende solo de este effect.)
42517
+ // OJO: usar `toggleUserPreference()` y NO el setter `Theme =`. El setter solo
42518
+ // persiste localStorage + emite el BehaviorSubject; no aplica las clases
42519
+ // `body.dark`/`html.ion-palette-dark` que el palette de Ionic necesita.
42509
42520
  effect(() => {
42510
- if (!this._synced())
42511
- return;
42512
42521
  const t = this._theme();
42513
42522
  if (this.themeService) {
42514
42523
  this.themeService.toggleUserPreference(t);
@@ -42523,11 +42532,17 @@ class PreferencesService {
42523
42532
  this.i18n.setLanguage(l);
42524
42533
  }
42525
42534
  });
42526
- // Side-effect: aplicar fontSize local cuando llega snapshot real.
42535
+ // Side-effect: aplicar fontSize.
42536
+ // SIN gate `_synced` (igual que theme): el tamaño es una
42537
+ // preferencia local-first — FontSizeService/localStorage es la verdad del
42538
+ // dispositivo y `_fontSize` ya viene seedeado de ahí arriba. Aplicar siempre
42539
+ // que cambie permite que el cambio del usuario surta efecto al instante
42540
+ // (optimista, sin esperar el round-trip a Firestore) y que el sync
42541
+ // cross-device también aplique al llegar el snapshot. Gatearlo en `_synced`
42542
+ // hacía que el primer cambio de un user nuevo (sin doc Firestore aún) no se
42543
+ // aplicara: el selector mostraba "Grande" pero el font-size no cambiaba.
42527
42544
  // allowSignalWrites: fontSizeService.setSize() escribe this._size (signal).
42528
42545
  effect(() => {
42529
- if (!this._synced())
42530
- return;
42531
42546
  const fs = this._fontSize();
42532
42547
  if (this.fontSizeService) {
42533
42548
  this.fontSizeService.setSize(fs);
@@ -42703,7 +42718,7 @@ const PREFERENCES_VIEW_I18N = {
42703
42718
  },
42704
42719
  };
42705
42720
 
42706
- const DEFAULT_NAMESPACE$f = 'Settings.General';
42721
+ const DEFAULT_NAMESPACE$e = 'Settings.General';
42707
42722
  const DEFAULT_LANGUAGES = ['es', 'en'];
42708
42723
  /**
42709
42724
  * `val-preferences-view` — vista de preferencias de UI cross-app (tema +
@@ -42740,7 +42755,7 @@ class PreferencesViewComponent {
42740
42755
  showLanguage: merged.showLanguage ?? true,
42741
42756
  showFontSize: merged.showFontSize ?? true,
42742
42757
  supportedLanguages: merged.supportedLanguages ?? DEFAULT_LANGUAGES,
42743
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$f,
42758
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$e,
42744
42759
  onThemeChanged: merged.onThemeChanged,
42745
42760
  onLanguageChanged: merged.onLanguageChanged,
42746
42761
  onFontSizeChanged: merged.onFontSizeChanged,
@@ -44496,7 +44511,7 @@ const SECURITY_VIEW_I18N = {
44496
44511
  };
44497
44512
 
44498
44513
  addIcons({ laptopOutline, phonePortraitOutline });
44499
- const DEFAULT_NAMESPACE$e = 'Settings.Security';
44514
+ const DEFAULT_NAMESPACE$d = 'Settings.Security';
44500
44515
  /**
44501
44516
  * `val-security-view` — vista Seguridad full-feature autocontenida (organism).
44502
44517
  *
@@ -44545,7 +44560,7 @@ class SecurityViewComponent {
44545
44560
  showChangeEmail: merged.showChangeEmail ?? true,
44546
44561
  showMfa: merged.showMfa ?? true,
44547
44562
  showPolicies: merged.showPolicies ?? true,
44548
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$e,
44563
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$d,
44549
44564
  handleDeepLinks: merged.handleDeepLinks ?? true,
44550
44565
  homeRoute: merged.homeRoute ?? '/app/home',
44551
44566
  onMfaCompleted: merged.onMfaCompleted,
@@ -45451,7 +45466,7 @@ const CREATE_ORG_MODAL_I18N = {
45451
45466
  },
45452
45467
  };
45453
45468
 
45454
- const DEFAULT_NAMESPACE$d = 'CreateOrgModal';
45469
+ const DEFAULT_NAMESPACE$c = 'CreateOrgModal';
45455
45470
  /**
45456
45471
  * `val-create-org-modal` — modal de creación de organización (organism
45457
45472
  * compartido). Promovido desde `showcase` bajo el proceso de ADR-021. Reusado por
@@ -45477,10 +45492,10 @@ class CreateOrgModalComponent {
45477
45492
  this.toast = inject(ToastService);
45478
45493
  this.errors = inject(ValtechErrorService);
45479
45494
  /** Namespace i18n con que la vista resuelve sus textos. */
45480
- this.i18nNamespace = DEFAULT_NAMESPACE$d;
45495
+ this.i18nNamespace = DEFAULT_NAMESPACE$c;
45481
45496
  this._busy = signal(false);
45482
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$d)) {
45483
- this.i18n.registerContent(DEFAULT_NAMESPACE$d, CREATE_ORG_MODAL_I18N);
45497
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$c)) {
45498
+ this.i18n.registerContent(DEFAULT_NAMESPACE$c, CREATE_ORG_MODAL_I18N);
45484
45499
  }
45485
45500
  this.formMeta = signal(this.buildFormMeta());
45486
45501
  effect(() => {
@@ -45716,7 +45731,7 @@ const DELETE_ACCOUNT_MODAL_I18N = {
45716
45731
  },
45717
45732
  };
45718
45733
 
45719
- const DEFAULT_NAMESPACE$c = 'Settings.DeleteAccount';
45734
+ const DEFAULT_NAMESPACE$b = 'Settings.DeleteAccount';
45720
45735
  /**
45721
45736
  * `val-delete-account-modal` — modal de eliminación de cuenta (organism,
45722
45737
  * cuenta-específico). Promovido desde `showcase` bajo el proceso de ADR-021.
@@ -45742,7 +45757,7 @@ class DeleteAccountModalComponent {
45742
45757
  this.i18n = inject(I18nService);
45743
45758
  this.toast = inject(ToastService);
45744
45759
  /** Namespace i18n con que la vista resuelve sus textos. */
45745
- this.i18nNamespace = DEFAULT_NAMESPACE$c;
45760
+ this.i18nNamespace = DEFAULT_NAMESPACE$b;
45746
45761
  this.hasPassword = signal(null);
45747
45762
  this.codeSent = signal(false);
45748
45763
  this.formState = signal(ComponentStates.ENABLED);
@@ -45809,8 +45824,8 @@ class DeleteAccountModalComponent {
45809
45824
  ],
45810
45825
  actions: this.submitBtn('delete-code-submit', 'confirmCta'),
45811
45826
  }));
45812
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$c)) {
45813
- this.i18n.registerContent(DEFAULT_NAMESPACE$c, DELETE_ACCOUNT_MODAL_I18N);
45827
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$b)) {
45828
+ this.i18n.registerContent(DEFAULT_NAMESPACE$b, DELETE_ACCOUNT_MODAL_I18N);
45814
45829
  }
45815
45830
  }
45816
45831
  ngOnInit() {
@@ -46011,7 +46026,7 @@ const ORG_INFO_SHEET_I18N = {
46011
46026
  },
46012
46027
  };
46013
46028
 
46014
- const DEFAULT_NAMESPACE$b = 'OrgInfoSheet';
46029
+ const DEFAULT_NAMESPACE$a = 'OrgInfoSheet';
46015
46030
  /**
46016
46031
  * `val-org-info-sheet` — sheet informativo "¿Qué son las organizaciones?".
46017
46032
  * Extraído del `OrgInfoSheetComponent` inline de `AccountPage` bajo el proceso de
@@ -46056,15 +46071,15 @@ class OrgInfoSheetComponent {
46056
46071
  text: this.closeProps.text || this.resolvedCloseLabel(),
46057
46072
  }));
46058
46073
  addIcons({ peopleOutline, swapHorizontalOutline, mailOutline });
46059
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$b)) {
46060
- this.i18n.registerContent(DEFAULT_NAMESPACE$b, ORG_INFO_SHEET_I18N);
46074
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$a)) {
46075
+ this.i18n.registerContent(DEFAULT_NAMESPACE$a, ORG_INFO_SHEET_I18N);
46061
46076
  }
46062
46077
  }
46063
46078
  dismiss() {
46064
46079
  this._modalRef?.dismiss(null, 'cancel');
46065
46080
  }
46066
46081
  tt(key) {
46067
- return this.i18n.t(key, DEFAULT_NAMESPACE$b);
46082
+ return this.i18n.t(key, DEFAULT_NAMESPACE$a);
46068
46083
  }
46069
46084
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OrgInfoSheetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
46070
46085
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: OrgInfoSheetComponent, isStandalone: true, selector: "val-org-info-sheet", inputs: { title: "title", subtitle: "subtitle", body: "body", closeLabel: "closeLabel", closeProps: "closeProps", _modalRef: "_modalRef" }, ngImport: i0, template: `
@@ -46231,7 +46246,7 @@ const SWITCH_ORG_MODAL_I18N = {
46231
46246
  },
46232
46247
  };
46233
46248
 
46234
- const DEFAULT_NAMESPACE$a = 'Settings.SwitchOrg';
46249
+ const DEFAULT_NAMESPACE$9 = 'Settings.SwitchOrg';
46235
46250
  addIcons({ businessOutline, checkmarkCircleOutline });
46236
46251
  /**
46237
46252
  * `val-switch-org-modal` — modal de cambio de organización activa (organism
@@ -46257,7 +46272,7 @@ class SwitchOrgModalComponent {
46257
46272
  this.orgSwitch = inject(OrgSwitchService);
46258
46273
  this.errors = inject(ValtechErrorService);
46259
46274
  /** Namespace i18n con que la vista resuelve sus textos. */
46260
- this.i18nNamespace = DEFAULT_NAMESPACE$a;
46275
+ this.i18nNamespace = DEFAULT_NAMESPACE$9;
46261
46276
  this.orgs = signal([]);
46262
46277
  this.loading = signal(false);
46263
46278
  this.query = signal('');
@@ -46273,8 +46288,8 @@ class SwitchOrgModalComponent {
46273
46288
  return list;
46274
46289
  return list.filter(o => o.name.toLowerCase().includes(q));
46275
46290
  });
46276
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$a)) {
46277
- this.i18n.registerContent(DEFAULT_NAMESPACE$a, SWITCH_ORG_MODAL_I18N);
46291
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$9)) {
46292
+ this.i18n.registerContent(DEFAULT_NAMESPACE$9, SWITCH_ORG_MODAL_I18N);
46278
46293
  }
46279
46294
  this.loadOrgs();
46280
46295
  }
@@ -46592,7 +46607,7 @@ const ACCOUNT_VIEW_I18N = {
46592
46607
  };
46593
46608
 
46594
46609
  addIcons({ peopleOutline, businessOutline, chevronForwardOutline, swapHorizontalOutline, mailOutline });
46595
- const DEFAULT_NAMESPACE$9 = 'Settings.Account';
46610
+ const DEFAULT_NAMESPACE$8 = 'Settings.Account';
46596
46611
  const DEFAULT_MANAGE_ORG_ROUTE = '/app/settings/organization';
46597
46612
  /**
46598
46613
  * `val-account-view` — vista Cuenta full-feature autocontenida (organism).
@@ -46633,7 +46648,7 @@ class AccountViewComponent {
46633
46648
  showNewOrgCta: merged.showNewOrgCta ?? true,
46634
46649
  showLogout: merged.showLogout ?? true,
46635
46650
  showDeleteAccount: merged.showDeleteAccount ?? true,
46636
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$9,
46651
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$8,
46637
46652
  manageOrgRoute: merged.manageOrgRoute ?? DEFAULT_MANAGE_ORG_ROUTE,
46638
46653
  onOrgSwitched: merged.onOrgSwitched,
46639
46654
  onOrgCreated: merged.onOrgCreated,
@@ -47358,7 +47373,7 @@ const EDIT_ORG_MODAL_I18N = {
47358
47373
  },
47359
47374
  };
47360
47375
 
47361
- const DEFAULT_NAMESPACE$8 = 'EditOrgModal';
47376
+ const DEFAULT_NAMESPACE$7 = 'EditOrgModal';
47362
47377
  /**
47363
47378
  * `val-edit-org-modal` — modal de edición de organización (nombre + descripción).
47364
47379
  * Promovido desde `showcase` bajo el proceso de ADR-021. Reusado por la vista de
@@ -47385,10 +47400,10 @@ class EditOrgModalComponent {
47385
47400
  /** Organización a editar (pasada por `componentProps`). */
47386
47401
  this.org = null;
47387
47402
  /** Namespace i18n con que la vista resuelve sus textos. */
47388
- this.i18nNamespace = DEFAULT_NAMESPACE$8;
47403
+ this.i18nNamespace = DEFAULT_NAMESPACE$7;
47389
47404
  this.saving = signal(false);
47390
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$8)) {
47391
- this.i18n.registerContent(DEFAULT_NAMESPACE$8, EDIT_ORG_MODAL_I18N);
47405
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$7)) {
47406
+ this.i18n.registerContent(DEFAULT_NAMESPACE$7, EDIT_ORG_MODAL_I18N);
47392
47407
  }
47393
47408
  this.formMeta = signal(this.buildFormMeta());
47394
47409
  effect(() => {
@@ -47626,7 +47641,7 @@ const INVITE_MEMBER_MODAL_I18N = {
47626
47641
  },
47627
47642
  };
47628
47643
 
47629
- const DEFAULT_NAMESPACE$7 = 'InviteModal';
47644
+ const DEFAULT_NAMESPACE$6 = 'InviteModal';
47630
47645
  addIcons({ personOutline, mailOutline, closeOutline, checkmarkCircleOutline });
47631
47646
  /**
47632
47647
  * `val-invite-member-modal` — invitación de miembros a una organización.
@@ -47657,7 +47672,7 @@ class InviteMemberModalComponent {
47657
47672
  /** Ids de miembros ya existentes (excluidos de los resultados). */
47658
47673
  this.existingMemberIds = [];
47659
47674
  /** Namespace i18n con que la vista resuelve sus textos. */
47660
- this.i18nNamespace = DEFAULT_NAMESPACE$7;
47675
+ this.i18nNamespace = DEFAULT_NAMESPACE$6;
47661
47676
  this.query = signal('');
47662
47677
  this.searching = signal(false);
47663
47678
  this.searchResults = signal([]);
@@ -47704,8 +47719,8 @@ class InviteMemberModalComponent {
47704
47719
  state: this.sending() ? ComponentStates.WORKING : ComponentStates.ENABLED,
47705
47720
  }));
47706
47721
  this.searchSubject = new Subject();
47707
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$7)) {
47708
- this.i18n.registerContent(DEFAULT_NAMESPACE$7, INVITE_MEMBER_MODAL_I18N);
47722
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$6)) {
47723
+ this.i18n.registerContent(DEFAULT_NAMESPACE$6, INVITE_MEMBER_MODAL_I18N);
47709
47724
  }
47710
47725
  this.loadRoles();
47711
47726
  this.roleSub = this.roleControl.valueChanges.subscribe(v => {
@@ -48194,7 +48209,7 @@ const MEMBER_DETAIL_MODAL_I18N = {
48194
48209
  },
48195
48210
  };
48196
48211
 
48197
- const DEFAULT_NAMESPACE$6 = 'Settings.MemberDetail';
48212
+ const DEFAULT_NAMESPACE$5 = 'Settings.MemberDetail';
48198
48213
  /**
48199
48214
  * `val-member-detail-modal` — detalle de miembro de organización: perfil, rol y
48200
48215
  * permisos. Promovido desde `showcase` bajo el proceso de ADR-021.
@@ -48231,7 +48246,7 @@ class MemberDetailModalComponent {
48231
48246
  { id: 'admin', name: 'admin' },
48232
48247
  ];
48233
48248
  /** Namespace i18n con que la vista resuelve sus textos. */
48234
- this.i18nNamespace = DEFAULT_NAMESPACE$6;
48249
+ this.i18nNamespace = DEFAULT_NAMESPACE$5;
48235
48250
  this.working = signal(false);
48236
48251
  this.currentRole = signal('');
48237
48252
  this.roleControl = new FormControl('');
@@ -48271,8 +48286,8 @@ class MemberDetailModalComponent {
48271
48286
  type: 'button',
48272
48287
  state: this.working() ? ComponentStates.WORKING : ComponentStates.ENABLED,
48273
48288
  }));
48274
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$6)) {
48275
- this.i18n.registerContent(DEFAULT_NAMESPACE$6, MEMBER_DETAIL_MODAL_I18N);
48289
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$5)) {
48290
+ this.i18n.registerContent(DEFAULT_NAMESPACE$5, MEMBER_DETAIL_MODAL_I18N);
48276
48291
  }
48277
48292
  }
48278
48293
  ngOnInit() {
@@ -48609,7 +48624,7 @@ const TRANSFER_OWNERSHIP_MODAL_I18N = {
48609
48624
  },
48610
48625
  };
48611
48626
 
48612
- const DEFAULT_NAMESPACE$5 = 'Settings.TransferModal';
48627
+ const DEFAULT_NAMESPACE$4 = 'Settings.TransferModal';
48613
48628
  /**
48614
48629
  * `val-transfer-ownership-modal` — selector de miembro para transferir la
48615
48630
  * propiedad de la organización. Promovido desde `showcase` bajo el proceso de
@@ -48633,7 +48648,7 @@ class TransferOwnershipModalComponent {
48633
48648
  /** Lista de miembros transferibles (todos excepto el owner actual). */
48634
48649
  this.members = [];
48635
48650
  /** Namespace i18n con que la vista resuelve sus textos. */
48636
- this.i18nNamespace = DEFAULT_NAMESPACE$5;
48651
+ this.i18nNamespace = DEFAULT_NAMESPACE$4;
48637
48652
  this.query = signal('');
48638
48653
  this.selectedId = signal('');
48639
48654
  this.filtered = computed(() => {
@@ -48645,8 +48660,8 @@ class TransferOwnershipModalComponent {
48645
48660
  m.userId.toLowerCase().includes(q));
48646
48661
  });
48647
48662
  this.noResultsLabel = computed(() => `${this.t('noResults')} "${this.query()}"`);
48648
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$5)) {
48649
- this.i18n.registerContent(DEFAULT_NAMESPACE$5, TRANSFER_OWNERSHIP_MODAL_I18N);
48663
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$4)) {
48664
+ this.i18n.registerContent(DEFAULT_NAMESPACE$4, TRANSFER_OWNERSHIP_MODAL_I18N);
48650
48665
  }
48651
48666
  }
48652
48667
  ngOnInit() {
@@ -48979,7 +48994,7 @@ const ORGANIZATION_VIEW_I18N = {
48979
48994
  },
48980
48995
  };
48981
48996
 
48982
- const DEFAULT_NAMESPACE$4 = 'Settings.Organization';
48997
+ const DEFAULT_NAMESPACE$3 = 'Settings.Organization';
48983
48998
  const DEFAULT_VIEW_PERMISSIONS_ROUTE = '/app/settings/permissions';
48984
48999
  const DEFAULT_LEAVE_REDIRECT_ROUTE = '/app/settings/account';
48985
49000
  /**
@@ -49036,7 +49051,7 @@ class OrganizationViewComponent {
49036
49051
  showInvite: merged.showInvite ?? true,
49037
49052
  showTransferOwnership: merged.showTransferOwnership ?? true,
49038
49053
  showLeave: merged.showLeave ?? true,
49039
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$4,
49054
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$3,
49040
49055
  viewPermissionsRoute: merged.viewPermissionsRoute ?? DEFAULT_VIEW_PERMISSIONS_ROUTE,
49041
49056
  leaveRedirectRoute: merged.leaveRedirectRoute ?? DEFAULT_LEAVE_REDIRECT_ROUTE,
49042
49057
  onOrgEdited: merged.onOrgEdited,
@@ -50123,7 +50138,7 @@ addIcons({
50123
50138
  notificationsOutline,
50124
50139
  refreshOutline,
50125
50140
  });
50126
- const DEFAULT_NAMESPACE$3 = 'Settings.Notifications';
50141
+ const DEFAULT_NAMESPACE$2 = 'Settings.Notifications';
50127
50142
  /**
50128
50143
  * `val-notification-preferences-view` — **preferencias de notificaciones push**
50129
50144
  * (FCM) full-feature autocontenida (organism). Promovida desde `showcase` bajo el
@@ -50166,7 +50181,7 @@ class NotificationPreferencesViewComponent {
50166
50181
  const merged = { ...fromRoute, ...(this.config ?? {}) };
50167
50182
  return {
50168
50183
  showDetails: merged.showDetails ?? true,
50169
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$3,
50184
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$2,
50170
50185
  onPushEnabled: merged.onPushEnabled,
50171
50186
  onPushDisabled: merged.onPushDisabled,
50172
50187
  };
@@ -50937,7 +50952,7 @@ const PERMISSIONS_VIEW_I18N = {
50937
50952
  },
50938
50953
  };
50939
50954
 
50940
- const DEFAULT_NAMESPACE$2 = 'Settings.Permissions';
50955
+ const DEFAULT_NAMESPACE$1 = 'Settings.Permissions';
50941
50956
  /**
50942
50957
  * `val-permissions-view` — vista Permisos full-feature autocontenida (organism).
50943
50958
  * Visor RBAC de solo lectura: muestra los roles del sistema (Visor/Editor/Admin +
@@ -50977,7 +50992,7 @@ class PermissionsViewComponent {
50977
50992
  return {
50978
50993
  showSystemRoles: merged.showSystemRoles ?? true,
50979
50994
  showCustomRoles: merged.showCustomRoles ?? true,
50980
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$2,
50995
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$1,
50981
50996
  };
50982
50997
  });
50983
50998
  this.loading = signal(false);
@@ -51754,7 +51769,7 @@ addIcons({
51754
51769
  openOutline,
51755
51770
  personOutline,
51756
51771
  });
51757
- const DEFAULT_NAMESPACE$1 = 'Notifications';
51772
+ const DEFAULT_NAMESPACE = 'Notifications';
51758
51773
  const DEFAULT_SETTINGS_PATH = '/app/settings/notifications';
51759
51774
  const DEFAULT_PAGE_SIZE = 10;
51760
51775
  const DEFAULT_MAX_LOAD = 50;
@@ -51814,7 +51829,7 @@ class NotificationsViewComponent {
51814
51829
  showPushOffCta: merged.showPushOffCta ?? true,
51815
51830
  enableMarkAll: merged.enableMarkAll ?? true,
51816
51831
  settingsNotificationsPath: merged.settingsNotificationsPath ?? DEFAULT_SETTINGS_PATH,
51817
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$1,
51832
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE,
51818
51833
  pageSize: merged.pageSize ?? DEFAULT_PAGE_SIZE,
51819
51834
  maxLoad: merged.maxLoad ?? DEFAULT_MAX_LOAD,
51820
51835
  onNotificationOpen: merged.onNotificationOpen,
@@ -52482,797 +52497,6 @@ function provideValtechNotificationsRoutes(opts) {
52482
52497
  ];
52483
52498
  }
52484
52499
 
52485
- /**
52486
- * Defaults i18n del componente (es/en). El componente los auto-registra en su
52487
- * constructor si el consumer no proveyó el namespace `ContentReaction`. Esto
52488
- * garantiza que NUNCA haya una key faltante: una key faltante se evalúa en el
52489
- * template por cada change-detection → `console.warn` por CD → con el
52490
- * `val-debug-console` activo (espeja console.* a un signal) se dispara un loop
52491
- * infinito que congela el navegador en dev. Auto-registrar lo elimina de raíz.
52492
- */
52493
- const CONTENT_REACTION_I18N = {
52494
- es: {
52495
- question: '¿Te resultó útil este contenido?',
52496
- commentPlaceholder: 'Cuéntanos más (opcional)...',
52497
- submit: 'Enviar opinión',
52498
- update: 'Actualizar opinión',
52499
- thankYou: '¡Gracias por tu opinión!',
52500
- submitted: 'Tu opinión ha sido registrada',
52501
- errorSubmitting: 'Error al enviar. Intenta de nuevo.',
52502
- positiveLabel: 'Sí, mucho',
52503
- negativeLabel: 'No tanto',
52504
- negativeFeedbackTitle: '¿Cuéntanos qué no te gustó?',
52505
- },
52506
- en: {
52507
- question: 'Was this content helpful?',
52508
- commentPlaceholder: 'Tell us more (optional)...',
52509
- submit: 'Send feedback',
52510
- update: 'Update feedback',
52511
- thankYou: 'Thanks for your feedback!',
52512
- submitted: 'Your feedback has been recorded',
52513
- errorSubmitting: 'Failed to send. Please try again.',
52514
- positiveLabel: 'Yes, a lot',
52515
- negativeLabel: 'Not really',
52516
- negativeFeedbackTitle: "Tell us what you didn't like?",
52517
- },
52518
- };
52519
- /**
52520
- * Componente para reacciones de contenido con emojis.
52521
- *
52522
- * @example
52523
- * ```html
52524
- * <val-content-reaction
52525
- * [props]="{
52526
- * entityRef: { entityType: 'article', entityId: 'art-123' },
52527
- * question: '¿Te fue útil este artículo?'
52528
- * }"
52529
- * (reactionSubmit)="onReactionSubmit($event)"
52530
- * />
52531
- * ```
52532
- */
52533
- class ContentReactionComponent {
52534
- constructor() {
52535
- this.feedbackService = inject(FeedbackService);
52536
- this.toast = inject(ToastService);
52537
- this.i18n = inject(I18nService);
52538
- this.auth = inject(AuthService, { optional: true });
52539
- this.isConfigured = computed(() => this.feedbackService.isConfigured());
52540
- this.props = {};
52541
- this.reactionSubmit = new EventEmitter();
52542
- this.reactionChange = new EventEmitter();
52543
- // Estado reactivo
52544
- this.state = signal({
52545
- selectedValue: null,
52546
- comment: '',
52547
- isLoading: false,
52548
- isSubmitted: false,
52549
- hadPreviousReaction: false,
52550
- error: null,
52551
- });
52552
- // Valores por defecto
52553
- this.defaultEmojis = ['😞', '😐', '😊'];
52554
- this.defaultLabels = ['No me ayudó', 'Regular', 'Muy útil'];
52555
- // Computed properties
52556
- this.resolvedProps = computed(() => ({
52557
- entityRef: this.props.entityRef,
52558
- question: this.props.question || this.t('question'),
52559
- showComment: this.props.showComment ?? true,
52560
- commentOnValues: this.props.commentOnValues,
52561
- commentPlaceholder: this.props.commentPlaceholder || this.t('commentPlaceholder'),
52562
- maxCommentLength: this.props.maxCommentLength ?? 500,
52563
- emojis: this.props.emojis || this.defaultEmojis,
52564
- emojiLabels: this.props.emojiLabels || this.defaultLabels,
52565
- showThankYou: this.props.showThankYou ?? true,
52566
- thankYouMessage: this.props.thankYouMessage || this.t('thankYou'),
52567
- disabled: this.props.disabled ?? false,
52568
- readonly: this.props.readonly ?? false,
52569
- // Variante: 'emoji' cuando se declara explícitamente o cuando se pasan emojis custom.
52570
- variant: this.props.variant ?? (this.props.emojis ? 'emoji' : 'buttons'),
52571
- }));
52572
- this.activeReactionValues = computed(() => this.resolvedProps().emojis.length === 2 ? ['negative', 'positive'] : ['negative', 'neutral', 'positive']);
52573
- this.showCommentField = computed(() => {
52574
- const sel = this.state().selectedValue;
52575
- if (!sel)
52576
- return false;
52577
- const commentOn = this.resolvedProps().commentOnValues;
52578
- if (commentOn !== undefined)
52579
- return commentOn.includes(sel);
52580
- return this.resolvedProps().showComment;
52581
- });
52582
- this.canSubmit = computed(() => this.state().selectedValue !== null && !this.state().isLoading);
52583
- // Auto-registro de defaults i18n (es/en) si el consumer no proveyó el
52584
- // namespace. Garantiza que `t()` nunca caiga en key faltante. Ver
52585
- // CONTENT_REACTION_I18N. Idempotente: solo la 1ª instancia registra.
52586
- if (!this.i18n.hasNamespace('ContentReaction')) {
52587
- this.i18n.registerContent('ContentReaction', CONTENT_REACTION_I18N);
52588
- }
52589
- }
52590
- ngOnInit() {
52591
- this.loadPreviousReaction();
52592
- }
52593
- ngOnChanges(changes) {
52594
- if (changes['props'] && !changes['props'].firstChange) {
52595
- this.loadPreviousReaction();
52596
- }
52597
- }
52598
- async loadPreviousReaction() {
52599
- if (!this.props.entityRef)
52600
- return;
52601
- // Si hay initialValue, usarlo directamente sin consultar
52602
- if (this.props.initialValue) {
52603
- this.state.update(s => ({
52604
- ...s,
52605
- selectedValue: this.props.initialValue,
52606
- hadPreviousReaction: true,
52607
- isLoading: false,
52608
- isSubmitted: true,
52609
- }));
52610
- return;
52611
- }
52612
- // Si skipInitialCheck está activo, no consultar
52613
- if (this.props.skipInitialCheck) {
52614
- return;
52615
- }
52616
- this.state.update(s => ({ ...s, isLoading: true, error: null }));
52617
- try {
52618
- const check = await this.feedbackService.checkFeedback(this.props.entityRef.entityType, this.props.entityRef.entityId);
52619
- if (check.hasFeedback && check.reactionValue) {
52620
- this.state.update(s => ({
52621
- ...s,
52622
- selectedValue: check.reactionValue,
52623
- hadPreviousReaction: true,
52624
- isLoading: false,
52625
- isSubmitted: true,
52626
- }));
52627
- }
52628
- else {
52629
- this.state.update(s => ({ ...s, isLoading: false }));
52630
- }
52631
- }
52632
- catch (error) {
52633
- console.error('Error loading previous reaction:', error);
52634
- this.state.update(s => ({ ...s, isLoading: false }));
52635
- }
52636
- }
52637
- selectReaction(value) {
52638
- if (this.resolvedProps().disabled || this.resolvedProps().readonly)
52639
- return;
52640
- const previousValue = this.state().selectedValue;
52641
- this.state.update(s => ({
52642
- ...s,
52643
- selectedValue: value,
52644
- isSubmitted: false,
52645
- error: null,
52646
- }));
52647
- this.reactionChange.emit({ value, previousValue });
52648
- }
52649
- async submitReaction() {
52650
- const currentState = this.state();
52651
- const props = this.resolvedProps();
52652
- if (!currentState.selectedValue || props.disabled)
52653
- return;
52654
- this.state.update(s => ({ ...s, isLoading: true, error: null }));
52655
- try {
52656
- // Determinar si usar endpoint anónimo o autenticado
52657
- const isAuthenticated = this.auth?.isAuthenticated() ?? false;
52658
- const useAnonymous = this.props.allowAnonymous && !isAuthenticated;
52659
- if (useAnonymous) {
52660
- // Usar endpoint anónimo (público)
52661
- await this.feedbackService.createAnonymousReaction(props.entityRef, currentState.selectedValue, currentState.comment || undefined);
52662
- }
52663
- else {
52664
- // Usar endpoint autenticado
52665
- await this.feedbackService.createReaction(props.entityRef, currentState.selectedValue, currentState.comment || undefined);
52666
- }
52667
- this.state.update(s => ({
52668
- ...s,
52669
- isLoading: false,
52670
- isSubmitted: true,
52671
- comment: '',
52672
- hadPreviousReaction: !useAnonymous, // Solo para autenticados
52673
- }));
52674
- this.reactionSubmit.emit({
52675
- value: currentState.selectedValue,
52676
- comment: currentState.comment || undefined,
52677
- entityRef: props.entityRef,
52678
- isUpdate: currentState.hadPreviousReaction,
52679
- });
52680
- if (props.showThankYou) {
52681
- this.toast.show({
52682
- message: props.thankYouMessage,
52683
- duration: 2000,
52684
- });
52685
- }
52686
- }
52687
- catch (error) {
52688
- console.error('Error submitting reaction:', error);
52689
- this.state.update(s => ({
52690
- ...s,
52691
- isLoading: false,
52692
- error: this.t('errorSubmitting'),
52693
- }));
52694
- this.toast.show({
52695
- message: this.t('errorSubmitting'),
52696
- duration: 3000,
52697
- });
52698
- }
52699
- }
52700
- updateComment(event) {
52701
- const value = event.detail.value || '';
52702
- this.state.update(s => ({ ...s, comment: value }));
52703
- }
52704
- getEmoji(index) {
52705
- return this.resolvedProps().emojis[index];
52706
- }
52707
- getEmojiLabel(index) {
52708
- return this.resolvedProps().emojiLabels[index];
52709
- }
52710
- isSelected(value) {
52711
- return this.state().selectedValue === value;
52712
- }
52713
- t(key) {
52714
- const translations = {
52715
- question: '¿Te resultó útil este contenido?',
52716
- commentPlaceholder: 'Cuéntanos más (opcional)...',
52717
- submit: 'Enviar opinión',
52718
- update: 'Actualizar opinión',
52719
- thankYou: '¡Gracias por tu opinión!',
52720
- submitted: 'Tu opinión ha sido registrada',
52721
- errorSubmitting: 'Error al enviar. Intenta de nuevo.',
52722
- positiveLabel: 'Sí, mucho',
52723
- negativeLabel: 'No tanto',
52724
- negativeFeedbackTitle: '¿Cuéntanos qué no te gustó?',
52725
- };
52726
- return this.i18n.t(key, 'ContentReaction') || translations[key] || key;
52727
- }
52728
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ContentReactionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
52729
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ContentReactionComponent, isStandalone: true, selector: "val-content-reaction", inputs: { props: "props" }, outputs: { reactionSubmit: "reactionSubmit", reactionChange: "reactionChange" }, usesOnChanges: true, ngImport: i0, template: "@if (isConfigured()) {\n<div class=\"content-reaction\" [class.disabled]=\"resolvedProps().disabled\" [class.readonly]=\"resolvedProps().readonly\">\n <!-- Loading inicial -->\n @if (state().isLoading && !state().selectedValue) {\n <div class=\"loading-container\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n </div>\n } @else {\n <!-- Pregunta -->\n <p class=\"question\">\n {{ state().selectedValue === 'negative' ? t('negativeFeedbackTitle') : resolvedProps().question }}\n </p>\n\n <!-- Botones de reacci\u00F3n \u2014 variante emoji (caras, estrellas, thumbs, \u2026) -->\n @if (resolvedProps().variant === 'emoji') {\n <div class=\"reaction-buttons reaction-buttons--emoji\">\n @for (value of activeReactionValues(); track value; let i = $index) {\n <ion-button\n fill=\"clear\"\n shape=\"round\"\n [class.selected]=\"isSelected(value)\"\n [attr.aria-label]=\"resolvedProps().emojiLabels[i]\"\n [attr.aria-pressed]=\"isSelected(value)\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction(value)\"\n >\n <span class=\"emoji-btn\">{{ resolvedProps().emojis[i] }}</span>\n </ion-button>\n }\n </div>\n } @else {\n <!-- Botones de reacci\u00F3n \u2014 variante texto (default) -->\n <div class=\"reaction-buttons\">\n <ion-button\n [fill]=\"isSelected('positive') ? 'solid' : 'outline'\"\n shape=\"round\"\n color=\"dark\"\n [attr.aria-pressed]=\"isSelected('positive')\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction('positive')\"\n >\n {{ t('positiveLabel') }}\n </ion-button>\n <ion-button\n [fill]=\"isSelected('negative') ? 'solid' : 'outline'\"\n shape=\"round\"\n color=\"dark\"\n [attr.aria-pressed]=\"isSelected('negative')\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction('negative')\"\n >\n {{ t('negativeLabel') }}\n </ion-button>\n </div>\n }\n\n <!-- Campo de comentario (solo si hay selecci\u00F3n) -->\n @if (showCommentField()) {\n <div class=\"comment-section\">\n <ion-textarea\n [value]=\"state().comment\"\n [placeholder]=\"resolvedProps().commentPlaceholder\"\n [maxlength]=\"resolvedProps().maxCommentLength\"\n [disabled]=\"resolvedProps().disabled\"\n [rows]=\"3\"\n class=\"comment-textarea\"\n (ionInput)=\"updateComment($event)\"\n ></ion-textarea>\n <span class=\"char-count\"> {{ state().comment.length }}/{{ resolvedProps().maxCommentLength }} </span>\n </div>\n }\n\n <!-- Bot\u00F3n de env\u00EDo -->\n @if (state().selectedValue && !state().isSubmitted) {\n <ion-button expand=\"block\" shape=\"round\" [disabled]=\"!canSubmit()\" (click)=\"submitReaction()\" class=\"submit-button\">\n @if (state().isLoading) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ state().hadPreviousReaction ? t('update') : t('submit') }} }\n </ion-button>\n }\n\n <!-- Mensaje de confirmaci\u00F3n -->\n @if (state().isSubmitted) {\n <p class=\"submitted-message\">{{ t('thankYou') }}</p>\n }\n\n <!-- Error -->\n @if (state().error) {\n <p class=\"error-message\">{{ state().error }}</p>\n } }\n</div>\n}\n", styles: [":host{display:block}.content-reaction{padding:16px;text-align:center}.content-reaction.disabled{opacity:.6;pointer-events:none}.content-reaction.readonly{pointer-events:none}.question{font-size:1.0625rem;font-weight:700;color:var(--ion-color-dark);margin:0 0 16px}.reaction-buttons{display:flex;justify-content:center;gap:12px;margin-bottom:20px}.reaction-buttons--emoji{gap:8px}.reaction-buttons--emoji ion-button{--padding-start: 4px;--padding-end: 4px;transition:transform .15s ease}.reaction-buttons--emoji ion-button.selected{transform:scale(1.25)}.reaction-buttons--emoji ion-button:not(.selected):not([disabled]){opacity:.55}.reaction-buttons--emoji ion-button:not(.selected):not([disabled]):hover{opacity:1}.emoji-btn{font-size:2rem;line-height:1;display:block}.comment-section{margin-top:16px;animation:slideIn .3s ease-out}.comment-section .comment-textarea{--background: transparent;--border-radius: 8px;--padding-start: 12px;--padding-end: 12px;width:100%}.comment-section .char-count{display:block;text-align:right;font-size:.75rem;color:var(--ion-color-medium);margin-top:4px}.submit-button{margin-top:16px}.submitted-message{margin-top:16px;color:var(--ion-color-success);font-weight:500;animation:fadeIn .3s ease-out}.error-message{margin-top:8px;color:var(--ion-color-danger);font-size:.875rem}.loading-container{display:flex;justify-content:center;padding:20px}@keyframes slideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { 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: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: IonTextarea, selector: "ion-textarea", inputs: ["autoGrow", "autocapitalize", "autofocus", "clearOnEdit", "color", "cols", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "maxlength", "minlength", "mode", "name", "placeholder", "readonly", "required", "rows", "shape", "spellcheck", "value", "wrap"] }] }); }
52730
- }
52731
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ContentReactionComponent, decorators: [{
52732
- type: Component,
52733
- args: [{ selector: 'val-content-reaction', standalone: true, imports: [CommonModule, FormsModule, IonButton, IonSpinner, IonTextarea], template: "@if (isConfigured()) {\n<div class=\"content-reaction\" [class.disabled]=\"resolvedProps().disabled\" [class.readonly]=\"resolvedProps().readonly\">\n <!-- Loading inicial -->\n @if (state().isLoading && !state().selectedValue) {\n <div class=\"loading-container\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n </div>\n } @else {\n <!-- Pregunta -->\n <p class=\"question\">\n {{ state().selectedValue === 'negative' ? t('negativeFeedbackTitle') : resolvedProps().question }}\n </p>\n\n <!-- Botones de reacci\u00F3n \u2014 variante emoji (caras, estrellas, thumbs, \u2026) -->\n @if (resolvedProps().variant === 'emoji') {\n <div class=\"reaction-buttons reaction-buttons--emoji\">\n @for (value of activeReactionValues(); track value; let i = $index) {\n <ion-button\n fill=\"clear\"\n shape=\"round\"\n [class.selected]=\"isSelected(value)\"\n [attr.aria-label]=\"resolvedProps().emojiLabels[i]\"\n [attr.aria-pressed]=\"isSelected(value)\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction(value)\"\n >\n <span class=\"emoji-btn\">{{ resolvedProps().emojis[i] }}</span>\n </ion-button>\n }\n </div>\n } @else {\n <!-- Botones de reacci\u00F3n \u2014 variante texto (default) -->\n <div class=\"reaction-buttons\">\n <ion-button\n [fill]=\"isSelected('positive') ? 'solid' : 'outline'\"\n shape=\"round\"\n color=\"dark\"\n [attr.aria-pressed]=\"isSelected('positive')\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction('positive')\"\n >\n {{ t('positiveLabel') }}\n </ion-button>\n <ion-button\n [fill]=\"isSelected('negative') ? 'solid' : 'outline'\"\n shape=\"round\"\n color=\"dark\"\n [attr.aria-pressed]=\"isSelected('negative')\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction('negative')\"\n >\n {{ t('negativeLabel') }}\n </ion-button>\n </div>\n }\n\n <!-- Campo de comentario (solo si hay selecci\u00F3n) -->\n @if (showCommentField()) {\n <div class=\"comment-section\">\n <ion-textarea\n [value]=\"state().comment\"\n [placeholder]=\"resolvedProps().commentPlaceholder\"\n [maxlength]=\"resolvedProps().maxCommentLength\"\n [disabled]=\"resolvedProps().disabled\"\n [rows]=\"3\"\n class=\"comment-textarea\"\n (ionInput)=\"updateComment($event)\"\n ></ion-textarea>\n <span class=\"char-count\"> {{ state().comment.length }}/{{ resolvedProps().maxCommentLength }} </span>\n </div>\n }\n\n <!-- Bot\u00F3n de env\u00EDo -->\n @if (state().selectedValue && !state().isSubmitted) {\n <ion-button expand=\"block\" shape=\"round\" [disabled]=\"!canSubmit()\" (click)=\"submitReaction()\" class=\"submit-button\">\n @if (state().isLoading) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ state().hadPreviousReaction ? t('update') : t('submit') }} }\n </ion-button>\n }\n\n <!-- Mensaje de confirmaci\u00F3n -->\n @if (state().isSubmitted) {\n <p class=\"submitted-message\">{{ t('thankYou') }}</p>\n }\n\n <!-- Error -->\n @if (state().error) {\n <p class=\"error-message\">{{ state().error }}</p>\n } }\n</div>\n}\n", styles: [":host{display:block}.content-reaction{padding:16px;text-align:center}.content-reaction.disabled{opacity:.6;pointer-events:none}.content-reaction.readonly{pointer-events:none}.question{font-size:1.0625rem;font-weight:700;color:var(--ion-color-dark);margin:0 0 16px}.reaction-buttons{display:flex;justify-content:center;gap:12px;margin-bottom:20px}.reaction-buttons--emoji{gap:8px}.reaction-buttons--emoji ion-button{--padding-start: 4px;--padding-end: 4px;transition:transform .15s ease}.reaction-buttons--emoji ion-button.selected{transform:scale(1.25)}.reaction-buttons--emoji ion-button:not(.selected):not([disabled]){opacity:.55}.reaction-buttons--emoji ion-button:not(.selected):not([disabled]):hover{opacity:1}.emoji-btn{font-size:2rem;line-height:1;display:block}.comment-section{margin-top:16px;animation:slideIn .3s ease-out}.comment-section .comment-textarea{--background: transparent;--border-radius: 8px;--padding-start: 12px;--padding-end: 12px;width:100%}.comment-section .char-count{display:block;text-align:right;font-size:.75rem;color:var(--ion-color-medium);margin-top:4px}.submit-button{margin-top:16px}.submitted-message{margin-top:16px;color:var(--ion-color-success);font-weight:500;animation:fadeIn .3s ease-out}.error-message{margin-top:8px;color:var(--ion-color-danger);font-size:.875rem}.loading-container{display:flex;justify-content:center;padding:20px}@keyframes slideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}\n"] }]
52734
- }], ctorParameters: () => [], propDecorators: { props: [{
52735
- type: Input
52736
- }], reactionSubmit: [{
52737
- type: Output
52738
- }], reactionChange: [{
52739
- type: Output
52740
- }] } });
52741
-
52742
- /**
52743
- * Defaults i18n (es/en) embebidos en `val-about-view`. Auto-registrados en el
52744
- * constructor del componente si el consumer no proveyó el namespace
52745
- * (`Settings.About` por default). SOLO incluye las keys que la vista usa.
52746
- *
52747
- * Textos portados EXACTOS del override `Settings.About` de showcase
52748
- * (`frontend/showcase/src/i18n/index.ts`). Las keys de las acciones
52749
- * showcase-specific (`feedback*`, `share*`, `faq*`, `donate*`) NO se incluyen:
52750
- * esas acciones se modelan ahora como `actions[]` de la config (el consumer
52751
- * aporta sus propios textos). `copyrightText` se incluye aquí (antes salía del
52752
- * namespace `Layout`) para que la vista sea autocontenida.
52753
- */
52754
- const ABOUT_VIEW_I18N = {
52755
- es: {
52756
- pageTitle: 'Acerca de',
52757
- pageDescription: 'Demo del factory Valtech',
52758
- tagline: 'App de demo — explora las funcionalidades en vivo',
52759
- descTitle: 'Acerca de Showcase',
52760
- descBody: 'Showcase es la app de referencia del factory Valtech. Aquí puedes explorar el flujo completo en vivo: autenticación, perfil, organizaciones, notificaciones y ajustes de cuenta.',
52761
- actionsTitle: 'Acciones',
52762
- rateTitle: 'Calificar la app',
52763
- rateDesc: 'Cuéntanos qué te ha parecido la experiencia.',
52764
- rateModalTitle: 'Tu opinión',
52765
- rateQuestion: '¿Qué te ha parecido la experiencia hasta ahora?',
52766
- rateComment: 'Cuéntanos más (opcional)…',
52767
- rateThanks: '¡Gracias por tu opinión!',
52768
- rateClose: 'Cerrar',
52769
- rateBad: 'No tanto',
52770
- rateMid: 'Bien',
52771
- rateGood: '¡Me encanta!',
52772
- socialTitle: 'Síguenos',
52773
- socialDesc: 'Estamos en redes sociales.',
52774
- versionLabel: 'Versión',
52775
- versionCopied: 'v{v} copiado',
52776
- copyrightText: '© {year}, Soluciones Valtech Ltda. o sus afiliados. Todos los derechos reservados.',
52777
- },
52778
- en: {
52779
- pageTitle: 'About',
52780
- pageDescription: 'Valtech factory demo',
52781
- tagline: 'Demo app — explore features live',
52782
- descTitle: 'About Showcase',
52783
- descBody: 'Showcase is the reference app for the Valtech factory. Explore the full flow live: authentication, profile, organizations, notifications and account settings.',
52784
- actionsTitle: 'Actions',
52785
- rateTitle: 'Rate the app',
52786
- rateDesc: 'Tell us how your experience has been.',
52787
- rateModalTitle: 'Your feedback',
52788
- rateQuestion: 'How has your experience been so far?',
52789
- rateComment: 'Tell us more (optional)…',
52790
- rateThanks: 'Thanks for your feedback!',
52791
- rateClose: 'Close',
52792
- rateBad: 'Not great',
52793
- rateMid: 'Good',
52794
- rateGood: 'Love it!',
52795
- socialTitle: 'Follow us',
52796
- socialDesc: "We're on social media.",
52797
- versionLabel: 'Version',
52798
- versionCopied: 'v{v} copied',
52799
- copyrightText: '© {year}, Soluciones Valtech Ltda. or its affiliates. All rights reserved.',
52800
- },
52801
- };
52802
-
52803
- addIcons({
52804
- copyOutline,
52805
- starOutline,
52806
- chatbubbleEllipsesOutline,
52807
- shareSocialOutline,
52808
- helpCircleOutline,
52809
- logoFacebook,
52810
- logoInstagram,
52811
- logoLinkedin,
52812
- logoTwitter,
52813
- logoYoutube,
52814
- logoTiktok,
52815
- });
52816
- const DEFAULT_NAMESPACE = 'Settings.About';
52817
- const DEFAULT_BRAND_NAME = 'Valtech';
52818
- const DEFAULT_LOGO_SRC = 'assets/images/main-icon.png';
52819
- const DEFAULT_RATING_ENTITY_ID = 'experience';
52820
- /**
52821
- * `val-about-view` — vista About full-feature autocontenida (organism).
52822
- * Promovida desde `showcase` bajo el proceso de ADR-021. Reúne:
52823
- * - **HERO** — logo + nombre de marca + tagline + pill de versión (copiable).
52824
- * - **Descripción** — título + cuerpo.
52825
- * - **Acciones** — action-card de rating built-in (abre `val-content-reaction`
52826
- * DIRECTAMENTE vía `ModalService`, sin wrapper local — decisión CTO) + las
52827
- * acciones genéricas que el consumer aporte por `config.actions`.
52828
- * - **Redes** — links sociales (`config.socialLinks`); sección oculta si vacía.
52829
- * - **Footer** — copyright con el año actual.
52830
- *
52831
- * Lo app-specific (logo, nombre, versión, redes, acciones share/faq) viaja como
52832
- * DATA de config — NO como slots (ADR-021). La versión la pasa el consumer
52833
- * (`config.version` = su `APP_VERSION`); la lib NO importa el `version.ts` de la
52834
- * app. Si no se pasa versión, el pill se oculta.
52835
- *
52836
- * NO renderiza ion-content — vive dentro de val-page-wrapper. `ActivatedRoute`
52837
- * se inyecta `{ optional: true }` solo para leer el route data de config.
52838
- *
52839
- * Auto-registra sus defaults i18n (es/en) en el constructor si el consumer no
52840
- * proveyó el namespace configurado (default `Settings.About`).
52841
- *
52842
- * GOTCHA i18n ContentReaction: `val-content-reaction` exige su namespace
52843
- * `ContentReaction` registrado donde se use (key faltante en binding + CD =
52844
- * navegador congelado en dev). `ContentReactionComponent` se auto-registra ese
52845
- * namespace en SU constructor, y como se abre vía `ModalService` ese constructor
52846
- * corre antes de que su template binde — queda cubierto. Igual lo importamos
52847
- * como dependencia explícita de esta vista para que la garantía sea local.
52848
- */
52849
- class AboutViewComponent {
52850
- /** Namespace i18n resuelto (capturado para llamadas no-reactivas). */
52851
- get ns() {
52852
- return this.resolvedConfig().i18nNamespace;
52853
- }
52854
- constructor() {
52855
- this.nav = inject(NavigationService);
52856
- this.i18n = inject(I18nService);
52857
- this.modalService = inject(ModalService);
52858
- this.toast = inject(ToastService);
52859
- this.route = inject(ActivatedRoute, { optional: true });
52860
- this.resolvedConfig = computed(() => {
52861
- const fromRoute = (this.route?.snapshot.data['aboutConfig'] ?? {});
52862
- const merged = { ...fromRoute, ...(this.config ?? {}) };
52863
- return {
52864
- brand: {
52865
- name: merged.brand?.name ?? DEFAULT_BRAND_NAME,
52866
- logoSrc: merged.brand?.logoSrc ?? DEFAULT_LOGO_SRC,
52867
- },
52868
- version: merged.version ?? '',
52869
- socialLinks: merged.socialLinks ?? [],
52870
- showRating: merged.showRating ?? true,
52871
- ratingEntityId: merged.ratingEntityId ?? DEFAULT_RATING_ENTITY_ID,
52872
- actions: merged.actions ?? [],
52873
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE,
52874
- onRated: merged.onRated,
52875
- };
52876
- });
52877
- this.brandName = computed(() => this.resolvedConfig().brand.name);
52878
- this.logoSrc = computed(() => this.resolvedConfig().brand.logoSrc);
52879
- this.version = computed(() => this.resolvedConfig().version);
52880
- this.socialLinks = computed(() => this.resolvedConfig().socialLinks);
52881
- this.showRating = computed(() => this.resolvedConfig().showRating);
52882
- this.actions = computed(() => this.resolvedConfig().actions);
52883
- /** Copyright del footer — año actual interpolado en `copyrightText`. */
52884
- this.copyright = computed(() => {
52885
- this.i18n.lang();
52886
- const year = String(new Date().getFullYear());
52887
- return this.tt('copyrightText').replace('{year}', year);
52888
- });
52889
- /** Title metadata para secciones — bold + size small. */
52890
- this.rateActionCard = computed(() => {
52891
- this.i18n.lang();
52892
- return {
52893
- icon: { ionicon: 'star-outline', color: 'primary' },
52894
- title: this.tt('rateTitle'),
52895
- description: this.tt('rateDesc'),
52896
- size: 'medium',
52897
- bordered: false,
52898
- shadowed: false,
52899
- };
52900
- });
52901
- const ns = this.ns;
52902
- if (!this.i18n.hasNamespace(ns)) {
52903
- this.i18n.registerContent(ns, ABOUT_VIEW_I18N);
52904
- }
52905
- this.nav.setBackHeader('pageTitle', ns, { withMenu: true });
52906
- }
52907
- /** Helper i18n bound al namespace resuelto. Reactivo vía `lang()`. */
52908
- tt(key) {
52909
- this.i18n.lang();
52910
- return this.i18n.t(key, this.ns);
52911
- }
52912
- /** Title metadata para secciones — bold + size small. */
52913
- sectionTitle(key) {
52914
- this.i18n.lang();
52915
- return {
52916
- content: this.tt(key),
52917
- size: 'small',
52918
- color: 'dark',
52919
- bold: true,
52920
- };
52921
- }
52922
- /** ActionCardMetadata para una acción genérica del consumer. */
52923
- extraActionCard(action) {
52924
- return {
52925
- icon: { ionicon: action.icon ?? 'chatbubble-ellipses-outline', color: 'primary' },
52926
- title: action.label,
52927
- description: action.description,
52928
- size: 'medium',
52929
- bordered: false,
52930
- shadowed: false,
52931
- };
52932
- }
52933
- /** Copia la versión al portapapeles + toast (color dark). */
52934
- async copyVersion() {
52935
- const v = this.version();
52936
- if (!v)
52937
- return;
52938
- try {
52939
- await navigator.clipboard.writeText(v);
52940
- this.toast.show({
52941
- message: this.tt('versionCopied').replace('{v}', v),
52942
- color: 'dark',
52943
- duration: 1500,
52944
- });
52945
- }
52946
- catch {
52947
- // clipboard no disponible — silencioso
52948
- }
52949
- }
52950
- /** Abre una URL externa en nueva tab. `mailto:` queda en la misma tab. */
52951
- openUrl(url) {
52952
- if (url.startsWith('mailto:')) {
52953
- window.location.href = url;
52954
- return;
52955
- }
52956
- window.open(url, '_blank', 'noopener,noreferrer');
52957
- }
52958
- /**
52959
- * "Calificar la app" — abre `val-content-reaction` DIRECTAMENTE como sheet
52960
- * modal (sin wrapper local — decisión CTO). El componente persiste la reacción
52961
- * contra el backend de feedback y muestra su propio estado de "gracias"; el
52962
- * sheet se cierra arrastrando (breakpoint 0). El namespace i18n
52963
- * `ContentReaction` lo auto-registra `ContentReactionComponent` en su
52964
- * constructor, que corre al crear el modal — sin riesgo de key faltante.
52965
- *
52966
- * Sheet (bottom sheet): el contenido scrollea dentro y el botón "Enviar" queda
52967
- * siempre alcanzable, también en viewports chicos.
52968
- */
52969
- async openRating() {
52970
- const reactionProps = {
52971
- // entityType "app" distingue esta reacción de las de artículos/FAQs.
52972
- entityRef: { entityType: 'app', entityId: this.resolvedConfig().ratingEntityId },
52973
- question: this.tt('rateQuestion'),
52974
- commentPlaceholder: this.tt('rateComment'),
52975
- thankYouMessage: this.tt('rateThanks'),
52976
- showComment: true,
52977
- showThankYou: true,
52978
- // Modal one-shot: NO bloquear el render con el check de rating previo.
52979
- skipInitialCheck: true,
52980
- emojis: ['😕', '🙂', '😍'],
52981
- emojiLabels: [this.tt('rateBad'), this.tt('rateMid'), this.tt('rateGood')],
52982
- };
52983
- await this.modalService.open({
52984
- component: ContentReactionComponent,
52985
- componentProps: { props: reactionProps },
52986
- breakpoints: {
52987
- initialBreakpoint: 0.9,
52988
- breakpoints: [0, 0.9, 1],
52989
- showHandle: true,
52990
- handleBehavior: 'cycle',
52991
- },
52992
- });
52993
- this.resolvedConfig().onRated?.();
52994
- }
52995
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AboutViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
52996
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: AboutViewComponent, isStandalone: true, selector: "val-about-view", inputs: { config: "config" }, ngImport: i0, template: `
52997
- <ion-grid class="about-grid">
52998
- <!-- HERO: logo + name + version -->
52999
- <div class="about-hero">
53000
- <img class="about-hero__logo" [src]="logoSrc()" [alt]="brandName()" />
53001
- <val-title
53002
- [props]="{
53003
- content: brandName(),
53004
- size: 'xlarge',
53005
- color: 'dark',
53006
- bold: true,
53007
- }"
53008
- />
53009
- <val-text
53010
- [props]="{
53011
- content: tt('tagline'),
53012
- size: 'medium',
53013
- color: 'dark',
53014
- bold: false,
53015
- }"
53016
- />
53017
- @if (version()) {
53018
- <div
53019
- class="version-row"
53020
- (click)="copyVersion()"
53021
- role="button"
53022
- [attr.aria-label]="tt('versionLabel') + ' ' + version()"
53023
- >
53024
- <val-pill
53025
- [props]="{
53026
- label: tt('versionLabel') + ' ' + version(),
53027
- color: 'dark',
53028
- weight: 'bold',
53029
- size: 'small',
53030
- }"
53031
- />
53032
- <ion-icon name="copy-outline" class="version-copy-icon" />
53033
- </div>
53034
- }
53035
- </div>
53036
-
53037
- <!-- DESCRIPTION -->
53038
- <section class="about-section">
53039
- <val-title [props]="sectionTitle('descTitle')" />
53040
- <val-text
53041
- [props]="{
53042
- content: tt('descBody'),
53043
- size: 'medium',
53044
- color: 'dark',
53045
- bold: false,
53046
- }"
53047
- />
53048
- </section>
53049
-
53050
- <!-- ACTIONS — rating built-in + acciones del consumer. -->
53051
- @if (showRating() || actions().length > 0) {
53052
- <section class="about-section about-section--reviews">
53053
- <val-title [props]="sectionTitle('actionsTitle')" />
53054
- <div class="about-actions">
53055
- @if (showRating()) {
53056
- <val-action-card [props]="rateActionCard()" (onClick)="openRating()" />
53057
- }
53058
- @for (action of actions(); track action.label) {
53059
- <val-action-card [props]="extraActionCard(action)" (onClick)="action.onClick()" />
53060
- }
53061
- </div>
53062
- </section>
53063
- }
53064
-
53065
- <!-- SOCIAL — penúltimo, justo antes del footer de derechos. -->
53066
- @if (socialLinks().length > 0) {
53067
- <section class="about-section">
53068
- <val-title [props]="sectionTitle('socialTitle')" />
53069
- <val-text
53070
- [props]="{
53071
- content: tt('socialDesc'),
53072
- size: 'medium',
53073
- color: 'dark',
53074
- bold: false,
53075
- }"
53076
- />
53077
- <div class="about-social">
53078
- @for (s of socialLinks(); track s.url) {
53079
- <ion-button
53080
- fill="clear"
53081
- size="small"
53082
- class="social-btn"
53083
- [attr.aria-label]="s.label"
53084
- (click)="openUrl(s.url)"
53085
- >
53086
- <ion-icon [name]="s.icon" slot="icon-only" />
53087
- </ion-button>
53088
- }
53089
- </div>
53090
- </section>
53091
- }
53092
-
53093
- <!-- FOOTER -->
53094
- <footer class="about-footer">
53095
- <val-text
53096
- [props]="{
53097
- content: copyright(),
53098
- size: 'small',
53099
- color: 'dark',
53100
- bold: false,
53101
- }"
53102
- />
53103
- </footer>
53104
- </ion-grid>
53105
- `, isInline: true, styles: [":host{display:block}.about-grid{padding:16px 0;max-width:720px;margin:0 auto}.about-hero{display:flex;flex-direction:column;align-items:center;gap:8px;padding:24px 0 16px;text-align:center}.about-hero__logo{height:96px;width:auto;display:block}.version-row{display:flex;align-items:center;gap:6px;cursor:pointer;opacity:.85;transition:opacity .15s ease}.version-row:hover{opacity:1}.version-copy-icon{font-size:14px;color:var(--ion-color-medium)}.about-section{padding:16px 0 8px;display:flex;flex-direction:column;gap:8px}.about-actions{display:flex;flex-direction:column;gap:8px}.about-section--reviews val-action-card{display:block}.about-section--reviews val-action-card ::ng-deep .action-card{background:var(--ion-color-light, #f4f5f8);box-shadow:none}.about-section--reviews val-action-card ::ng-deep .action-card__description{color:var(--ion-color-dark);opacity:.7}.about-social{display:flex;flex-wrap:wrap;gap:10px;padding-top:8px}.about-social .social-btn{--border-radius: 50%;--background: var(--ion-color-light, #f4f5f8);--color: var(--ion-color-dark);--padding-start: 0;--padding-end: 0;width:48px;height:48px;margin:0;transition:transform .15s ease}.about-social .social-btn:hover{--background: var(--ion-color-primary-tint, #8a4ce5);--color: #fff;transform:translateY(-2px)}.about-social .social-btn ion-icon{font-size:22px}.about-footer{display:flex;flex-direction:column;align-items:center;gap:4px;padding:24px 0;text-align:center}:host-context(body.dark) val-action-card ::ng-deep .action-card,:host-context(html.ion-palette-dark) val-action-card ::ng-deep .action-card,:host-context([data-theme=\"dark\"]) val-action-card ::ng-deep .action-card{background:#ffffff0a}:host-context(body.dark) .about-section--reviews val-action-card ::ng-deep .action-card:hover,:host-context(html.ion-palette-dark) .about-section--reviews val-action-card ::ng-deep .action-card:hover,:host-context([data-theme=\"dark\"]) .about-section--reviews val-action-card ::ng-deep .action-card:hover{background:#ffffff14}:host-context(body.dark) .about-social .social-btn,:host-context(html.ion-palette-dark) .about-social .social-btn,:host-context([data-theme=\"dark\"]) .about-social .social-btn{--background: rgba(255, 255, 255, .04)}\n"], dependencies: [{ kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonGrid, selector: "ion-grid", inputs: ["fixed"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: PillComponent, selector: "val-pill", inputs: ["preset", "props"], outputs: ["pillClick", "pillAction"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }, { kind: "component", type: ActionCardComponent, selector: "val-action-card", inputs: ["props"], outputs: ["onClick"] }] }); }
53106
- }
53107
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AboutViewComponent, decorators: [{
53108
- type: Component,
53109
- args: [{ selector: 'val-about-view', standalone: true, imports: [IonButton, IonGrid, IonIcon, PillComponent, TitleComponent, TextComponent, ActionCardComponent], template: `
53110
- <ion-grid class="about-grid">
53111
- <!-- HERO: logo + name + version -->
53112
- <div class="about-hero">
53113
- <img class="about-hero__logo" [src]="logoSrc()" [alt]="brandName()" />
53114
- <val-title
53115
- [props]="{
53116
- content: brandName(),
53117
- size: 'xlarge',
53118
- color: 'dark',
53119
- bold: true,
53120
- }"
53121
- />
53122
- <val-text
53123
- [props]="{
53124
- content: tt('tagline'),
53125
- size: 'medium',
53126
- color: 'dark',
53127
- bold: false,
53128
- }"
53129
- />
53130
- @if (version()) {
53131
- <div
53132
- class="version-row"
53133
- (click)="copyVersion()"
53134
- role="button"
53135
- [attr.aria-label]="tt('versionLabel') + ' ' + version()"
53136
- >
53137
- <val-pill
53138
- [props]="{
53139
- label: tt('versionLabel') + ' ' + version(),
53140
- color: 'dark',
53141
- weight: 'bold',
53142
- size: 'small',
53143
- }"
53144
- />
53145
- <ion-icon name="copy-outline" class="version-copy-icon" />
53146
- </div>
53147
- }
53148
- </div>
53149
-
53150
- <!-- DESCRIPTION -->
53151
- <section class="about-section">
53152
- <val-title [props]="sectionTitle('descTitle')" />
53153
- <val-text
53154
- [props]="{
53155
- content: tt('descBody'),
53156
- size: 'medium',
53157
- color: 'dark',
53158
- bold: false,
53159
- }"
53160
- />
53161
- </section>
53162
-
53163
- <!-- ACTIONS — rating built-in + acciones del consumer. -->
53164
- @if (showRating() || actions().length > 0) {
53165
- <section class="about-section about-section--reviews">
53166
- <val-title [props]="sectionTitle('actionsTitle')" />
53167
- <div class="about-actions">
53168
- @if (showRating()) {
53169
- <val-action-card [props]="rateActionCard()" (onClick)="openRating()" />
53170
- }
53171
- @for (action of actions(); track action.label) {
53172
- <val-action-card [props]="extraActionCard(action)" (onClick)="action.onClick()" />
53173
- }
53174
- </div>
53175
- </section>
53176
- }
53177
-
53178
- <!-- SOCIAL — penúltimo, justo antes del footer de derechos. -->
53179
- @if (socialLinks().length > 0) {
53180
- <section class="about-section">
53181
- <val-title [props]="sectionTitle('socialTitle')" />
53182
- <val-text
53183
- [props]="{
53184
- content: tt('socialDesc'),
53185
- size: 'medium',
53186
- color: 'dark',
53187
- bold: false,
53188
- }"
53189
- />
53190
- <div class="about-social">
53191
- @for (s of socialLinks(); track s.url) {
53192
- <ion-button
53193
- fill="clear"
53194
- size="small"
53195
- class="social-btn"
53196
- [attr.aria-label]="s.label"
53197
- (click)="openUrl(s.url)"
53198
- >
53199
- <ion-icon [name]="s.icon" slot="icon-only" />
53200
- </ion-button>
53201
- }
53202
- </div>
53203
- </section>
53204
- }
53205
-
53206
- <!-- FOOTER -->
53207
- <footer class="about-footer">
53208
- <val-text
53209
- [props]="{
53210
- content: copyright(),
53211
- size: 'small',
53212
- color: 'dark',
53213
- bold: false,
53214
- }"
53215
- />
53216
- </footer>
53217
- </ion-grid>
53218
- `, styles: [":host{display:block}.about-grid{padding:16px 0;max-width:720px;margin:0 auto}.about-hero{display:flex;flex-direction:column;align-items:center;gap:8px;padding:24px 0 16px;text-align:center}.about-hero__logo{height:96px;width:auto;display:block}.version-row{display:flex;align-items:center;gap:6px;cursor:pointer;opacity:.85;transition:opacity .15s ease}.version-row:hover{opacity:1}.version-copy-icon{font-size:14px;color:var(--ion-color-medium)}.about-section{padding:16px 0 8px;display:flex;flex-direction:column;gap:8px}.about-actions{display:flex;flex-direction:column;gap:8px}.about-section--reviews val-action-card{display:block}.about-section--reviews val-action-card ::ng-deep .action-card{background:var(--ion-color-light, #f4f5f8);box-shadow:none}.about-section--reviews val-action-card ::ng-deep .action-card__description{color:var(--ion-color-dark);opacity:.7}.about-social{display:flex;flex-wrap:wrap;gap:10px;padding-top:8px}.about-social .social-btn{--border-radius: 50%;--background: var(--ion-color-light, #f4f5f8);--color: var(--ion-color-dark);--padding-start: 0;--padding-end: 0;width:48px;height:48px;margin:0;transition:transform .15s ease}.about-social .social-btn:hover{--background: var(--ion-color-primary-tint, #8a4ce5);--color: #fff;transform:translateY(-2px)}.about-social .social-btn ion-icon{font-size:22px}.about-footer{display:flex;flex-direction:column;align-items:center;gap:4px;padding:24px 0;text-align:center}:host-context(body.dark) val-action-card ::ng-deep .action-card,:host-context(html.ion-palette-dark) val-action-card ::ng-deep .action-card,:host-context([data-theme=\"dark\"]) val-action-card ::ng-deep .action-card{background:#ffffff0a}:host-context(body.dark) .about-section--reviews val-action-card ::ng-deep .action-card:hover,:host-context(html.ion-palette-dark) .about-section--reviews val-action-card ::ng-deep .action-card:hover,:host-context([data-theme=\"dark\"]) .about-section--reviews val-action-card ::ng-deep .action-card:hover{background:#ffffff14}:host-context(body.dark) .about-social .social-btn,:host-context(html.ion-palette-dark) .about-social .social-btn,:host-context([data-theme=\"dark\"]) .about-social .social-btn{--background: rgba(255, 255, 255, .04)}\n"] }]
53219
- }], ctorParameters: () => [], propDecorators: { config: [{
53220
- type: Input
53221
- }] } });
53222
-
53223
- /**
53224
- * Configuración acotada de `val-about-view` (object-first). La vista About es
53225
- * app-specific por naturaleza (logo, nombre, versión, redes, acciones extra),
53226
- * así que lo propio de cada app se parametriza como **DATA de config** — NO como
53227
- * slots arbitrarios (ADR-021: una vista full-feature no abre slots; si falta
53228
- * algo, se promueve un nuevo punto de extensión).
53229
- *
53230
- * Ejes de variación permitidos:
53231
- * - **branding** — `brand.name` / `brand.logoSrc` (qué muestra el HERO).
53232
- * - **versión** — `version` la pasa el consumer (la lib NO puede importar el
53233
- * `version.ts` de la app). Si no se pasa, se oculta el pill de versión.
53234
- * - **redes** — `socialLinks`; si está vacío se oculta la sección social.
53235
- * - **rating** — `showRating` + `ratingEntityId` + `onRated`.
53236
- * - **acciones extra** — `actions[]` genéricas que el consumer agrega junto a
53237
- * la acción de rating built-in.
53238
- * - **branding i18n** — `i18nNamespace` (default `'Settings.About'`).
53239
- */
53240
-
53241
- /**
53242
- * Helper para montar la vista About (`val-about-view`) como ruta en una app del
53243
- * factory. El `config` se pasa por route `data` (`aboutConfig`) y el componente
53244
- * lo lee como fallback de su `@Input() config` (el `@Input` gana).
53245
- *
53246
- * @example
53247
- * ```ts
53248
- * // settings.routes.ts
53249
- * import { APP_VERSION } from '../version';
53250
- *
53251
- * export const settingsRoutes: Routes = [
53252
- * ...provideValtechAboutRoutes({ config: { version: APP_VERSION } }), // → /app/about
53253
- * { path: 'preferences', loadComponent: () => ... },
53254
- * ];
53255
- *
53256
- * // con redes y acciones extra:
53257
- * ...provideValtechAboutRoutes({
53258
- * config: {
53259
- * version: APP_VERSION,
53260
- * socialLinks: VALTECH_SOCIAL_LINKS.map(s => ({ icon: s.icon, url: s.url, label: s.name })),
53261
- * actions: [{ label: 'Compartir', icon: 'share-social-outline', onClick: () => share() }],
53262
- * },
53263
- * }),
53264
- * ```
53265
- */
53266
- function provideValtechAboutRoutes(opts) {
53267
- return [
53268
- {
53269
- path: opts?.path ?? 'about',
53270
- component: AboutViewComponent,
53271
- data: { aboutConfig: opts?.config },
53272
- },
53273
- ];
53274
- }
53275
-
53276
52500
  /** Built-in label sets for the three platform locales. */
53277
52501
  const CALLOUT_LABELS = {
53278
52502
  es: {
@@ -54829,11 +54053,15 @@ class AuthBackgroundComponent {
54829
54053
  const isThick = i % 5 === 0;
54830
54054
  const isMedium = i % 3 === 0 && !isThick;
54831
54055
  const amplitude = 10 + Math.random() * 20;
54832
- const frequency = 1 + Math.random() * 2;
54056
+ // Frecuencia ENTERA (ciclos completos sobre el ancho 1440). Imprescindible
54057
+ // para el loop seamless: con freq entero, path(x) === path(x ± 1440), así
54058
+ // el translateX de una vuelta (1440px) calza exacto y NO hay salto/corte.
54059
+ const frequency = 1 + Math.floor(Math.random() * 3); // 1 | 2 | 3
54833
54060
  const phase = Math.random() * Math.PI * 2;
54834
- // Path extendido off-canvas (-400 .. 1840) para que translateX no deje huecos
54835
- let path = `M-400,${y}`;
54836
- for (let x = -400; x <= 1840; x += 40) {
54061
+ // Path extendido a -1480 .. 2920 (≥ una vuelta de bleed a cada lado) para
54062
+ // que el desplazamiento de ±1440px nunca deje el viewBox sin onda.
54063
+ let path = `M-1480,${y}`;
54064
+ for (let x = -1480; x <= 2920; x += 40) {
54837
54065
  const waveY = y + amplitude * Math.sin((x / 1440) * Math.PI * frequency * 2 + phase);
54838
54066
  path += ` L${x},${waveY.toFixed(1)}`;
54839
54067
  }
@@ -54844,12 +54072,12 @@ class AuthBackgroundComponent {
54844
54072
  color,
54845
54073
  thickness: isThick ? 1.2 : isMedium ? 0.7 : 0.3,
54846
54074
  opacity: isThick ? 0.5 : isMedium ? 0.4 : 0.3,
54847
- // Pulse vertical (equalizer) ~1/3 de las waves son "rápidas"
54848
- pulseDuration: Math.random() < 0.33 ? 0.8 + Math.random() * 0.8 : 1.6 + Math.random() * 2.4,
54849
- pulseDelay: -Math.random() * 4,
54850
- // Traveling horizontal ~1/3 más rápidas para sensación de ecualizador
54851
- travelDuration: Math.random() < 0.33 ? 5 + Math.random() * 4 : 10 + Math.random() * 12,
54852
- travelDelay: -Math.random() * 12,
54075
+ // Drift vertical sutil (no "equalizer"): da vida sin saltos.
54076
+ bob: 4 + Math.random() * 10,
54077
+ // Flujo horizontal continuo. Duraciones variadas = parallax/profundidad.
54078
+ // delay negativo desincroniza el arranque de cada capa.
54079
+ travelDuration: 14 + Math.random() * 22,
54080
+ travelDelay: -Math.random() * 30,
54853
54081
  travelDirection: i % 2 === 0 ? 'right' : 'left',
54854
54082
  };
54855
54083
  });
@@ -54874,8 +54102,7 @@ class AuthBackgroundComponent {
54874
54102
  [class.travel-left]="wave.travelDirection === 'left'"
54875
54103
  [class.travel-right]="wave.travelDirection === 'right'"
54876
54104
  [style.opacity]="wave.opacity"
54877
- [style.--pulse-duration]="wave.pulseDuration + 's'"
54878
- [style.--pulse-delay]="wave.pulseDelay + 's'"
54105
+ [style.--bob]="wave.bob + 'px'"
54879
54106
  [style.--travel-duration]="wave.travelDuration + 's'"
54880
54107
  [style.--travel-delay]="wave.travelDelay + 's'"
54881
54108
  />
@@ -54887,7 +54114,7 @@ class AuthBackgroundComponent {
54887
54114
  <ng-content></ng-content>
54888
54115
  </div>
54889
54116
  </section>
54890
- `, isInline: true, styles: [":host{display:block;--auth-bg-start: #ffffff;--auth-bg-mid: #f8f9fa;--auth-bg-end: #f0f0f0;--auth-glass-bg: rgba(255, 255, 255, .3);--auth-grain-opacity: .035;--auth-wave-color: var(--ion-color-primary, #6366f1)}:host-context(body.dark),:host-context(.dark),:host-context(.ion-palette-dark),:host-context(html.ion-palette-dark),:host-context([data-theme=\"dark\"]){--auth-bg-start: #0f0f1a;--auth-bg-mid: #1a1a2e;--auth-bg-end: #16213e;--auth-glass-bg: rgba(0, 0, 0, .3);--auth-grain-opacity: .05;--auth-wave-color: var(--ion-color-primary, #818cf8)}.auth-background{position:relative;display:flex;align-items:center;padding:0;background:linear-gradient(135deg,var(--auth-bg-start) 0%,var(--auth-bg-mid) 50%,var(--auth-bg-end) 100%);overflow:hidden}.grain{position:absolute;inset:0;pointer-events:none;z-index:0;opacity:var(--auth-grain-opacity);background-image:url(\"data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.5' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E\")}.glass-layer{position:absolute;inset:0;pointer-events:none;z-index:1;background:var(--auth-glass-bg);backdrop-filter:blur(3px) saturate(120%);-webkit-backdrop-filter:blur(3px) saturate(120%)}.wave-bg{position:absolute;inset:0;pointer-events:none;z-index:0;transition:filter .3s ease}:host-context(body.dark) .wave-bg,:host-context(.dark) .wave-bg,:host-context(.ion-palette-dark) .wave-bg,:host-context(html.ion-palette-dark) .wave-bg,:host-context([data-theme=\"dark\"]) .wave-bg{filter:invert(1) opacity(.6)}.waves{width:100%;height:100%;overflow:visible}.waves path{fill:none;transform-box:fill-box;transform-origin:center;will-change:transform;animation:wave-pulse var(--pulse-duration, 3s) ease-in-out var(--pulse-delay, 0s) infinite alternate,wave-travel-right var(--travel-duration, 12s) linear var(--travel-delay, 0s) infinite}.waves path.travel-left{animation:wave-pulse var(--pulse-duration, 3s) ease-in-out var(--pulse-delay, 0s) infinite alternate,wave-travel-left var(--travel-duration, 12s) linear var(--travel-delay, 0s) infinite}@keyframes wave-pulse{0%{transform:scaleY(.85)}50%{transform:scaleY(1.25)}to{transform:scaleY(.85)}}@keyframes wave-travel-right{0%{transform:translate(-300px)}to{transform:translate(300px)}}@keyframes wave-travel-left{0%{transform:translate(300px)}to{transform:translate(-300px)}}@media (prefers-reduced-motion: reduce){.waves path{animation:none}}.content-layer{position:relative;z-index:2;width:100%;height:100%;display:flex;align-items:center}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] }); }
54117
+ `, isInline: true, styles: [":host{display:block;--auth-bg-start: #ffffff;--auth-bg-mid: #f8f9fa;--auth-bg-end: #f0f0f0;--auth-glass-bg: rgba(255, 255, 255, .3);--auth-grain-opacity: .035;--auth-wave-color: var(--ion-color-primary, #6366f1)}:host-context(body.dark),:host-context(.dark),:host-context(.ion-palette-dark),:host-context(html.ion-palette-dark),:host-context([data-theme=\"dark\"]){--auth-bg-start: #0f0f1a;--auth-bg-mid: #1a1a2e;--auth-bg-end: #16213e;--auth-glass-bg: rgba(0, 0, 0, .3);--auth-grain-opacity: .05;--auth-wave-color: var(--ion-color-primary, #818cf8)}.auth-background{position:relative;display:flex;align-items:center;padding:0;background:linear-gradient(135deg,var(--auth-bg-start) 0%,var(--auth-bg-mid) 50%,var(--auth-bg-end) 100%);overflow:hidden}.grain{position:absolute;inset:0;pointer-events:none;z-index:0;opacity:var(--auth-grain-opacity);background-image:url(\"data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.5' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E\")}.glass-layer{position:absolute;inset:0;pointer-events:none;z-index:1;background:var(--auth-glass-bg);backdrop-filter:blur(3px) saturate(120%);-webkit-backdrop-filter:blur(3px) saturate(120%)}.wave-bg{position:absolute;inset:0;pointer-events:none;z-index:0;transition:filter .3s ease}:host-context(body.dark) .wave-bg,:host-context(.dark) .wave-bg,:host-context(.ion-palette-dark) .wave-bg,:host-context(html.ion-palette-dark) .wave-bg,:host-context([data-theme=\"dark\"]) .wave-bg{filter:invert(1) opacity(.6)}.waves{width:100%;height:100%;overflow:visible}.waves path{fill:none;will-change:transform;animation:wave-flow-right var(--travel-duration, 18s) linear var(--travel-delay, 0s) infinite}.waves path.travel-left{animation:wave-flow-left var(--travel-duration, 18s) linear var(--travel-delay, 0s) infinite}@keyframes wave-flow-right{0%{transform:translate(0)}25%{transform:translate(-360px,calc(var(--bob, 0px) * -1))}50%{transform:translate(-720px)}75%{transform:translate(-1080px,var(--bob, 0px))}to{transform:translate(-1440px)}}@keyframes wave-flow-left{0%{transform:translate(0)}25%{transform:translate(360px,var(--bob, 0px))}50%{transform:translate(720px)}75%{transform:translate(1080px,calc(var(--bob, 0px) * -1))}to{transform:translate(1440px)}}@media (prefers-reduced-motion: reduce){.waves path{animation:none}}.content-layer{position:relative;z-index:2;width:100%;height:100%;display:flex;align-items:center}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] }); }
54891
54118
  }
54892
54119
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AuthBackgroundComponent, decorators: [{
54893
54120
  type: Component,
@@ -54909,8 +54136,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
54909
54136
  [class.travel-left]="wave.travelDirection === 'left'"
54910
54137
  [class.travel-right]="wave.travelDirection === 'right'"
54911
54138
  [style.opacity]="wave.opacity"
54912
- [style.--pulse-duration]="wave.pulseDuration + 's'"
54913
- [style.--pulse-delay]="wave.pulseDelay + 's'"
54139
+ [style.--bob]="wave.bob + 'px'"
54914
54140
  [style.--travel-duration]="wave.travelDuration + 's'"
54915
54141
  [style.--travel-delay]="wave.travelDelay + 's'"
54916
54142
  />
@@ -54922,7 +54148,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
54922
54148
  <ng-content></ng-content>
54923
54149
  </div>
54924
54150
  </section>
54925
- `, styles: [":host{display:block;--auth-bg-start: #ffffff;--auth-bg-mid: #f8f9fa;--auth-bg-end: #f0f0f0;--auth-glass-bg: rgba(255, 255, 255, .3);--auth-grain-opacity: .035;--auth-wave-color: var(--ion-color-primary, #6366f1)}:host-context(body.dark),:host-context(.dark),:host-context(.ion-palette-dark),:host-context(html.ion-palette-dark),:host-context([data-theme=\"dark\"]){--auth-bg-start: #0f0f1a;--auth-bg-mid: #1a1a2e;--auth-bg-end: #16213e;--auth-glass-bg: rgba(0, 0, 0, .3);--auth-grain-opacity: .05;--auth-wave-color: var(--ion-color-primary, #818cf8)}.auth-background{position:relative;display:flex;align-items:center;padding:0;background:linear-gradient(135deg,var(--auth-bg-start) 0%,var(--auth-bg-mid) 50%,var(--auth-bg-end) 100%);overflow:hidden}.grain{position:absolute;inset:0;pointer-events:none;z-index:0;opacity:var(--auth-grain-opacity);background-image:url(\"data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.5' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E\")}.glass-layer{position:absolute;inset:0;pointer-events:none;z-index:1;background:var(--auth-glass-bg);backdrop-filter:blur(3px) saturate(120%);-webkit-backdrop-filter:blur(3px) saturate(120%)}.wave-bg{position:absolute;inset:0;pointer-events:none;z-index:0;transition:filter .3s ease}:host-context(body.dark) .wave-bg,:host-context(.dark) .wave-bg,:host-context(.ion-palette-dark) .wave-bg,:host-context(html.ion-palette-dark) .wave-bg,:host-context([data-theme=\"dark\"]) .wave-bg{filter:invert(1) opacity(.6)}.waves{width:100%;height:100%;overflow:visible}.waves path{fill:none;transform-box:fill-box;transform-origin:center;will-change:transform;animation:wave-pulse var(--pulse-duration, 3s) ease-in-out var(--pulse-delay, 0s) infinite alternate,wave-travel-right var(--travel-duration, 12s) linear var(--travel-delay, 0s) infinite}.waves path.travel-left{animation:wave-pulse var(--pulse-duration, 3s) ease-in-out var(--pulse-delay, 0s) infinite alternate,wave-travel-left var(--travel-duration, 12s) linear var(--travel-delay, 0s) infinite}@keyframes wave-pulse{0%{transform:scaleY(.85)}50%{transform:scaleY(1.25)}to{transform:scaleY(.85)}}@keyframes wave-travel-right{0%{transform:translate(-300px)}to{transform:translate(300px)}}@keyframes wave-travel-left{0%{transform:translate(300px)}to{transform:translate(-300px)}}@media (prefers-reduced-motion: reduce){.waves path{animation:none}}.content-layer{position:relative;z-index:2;width:100%;height:100%;display:flex;align-items:center}\n"] }]
54151
+ `, styles: [":host{display:block;--auth-bg-start: #ffffff;--auth-bg-mid: #f8f9fa;--auth-bg-end: #f0f0f0;--auth-glass-bg: rgba(255, 255, 255, .3);--auth-grain-opacity: .035;--auth-wave-color: var(--ion-color-primary, #6366f1)}:host-context(body.dark),:host-context(.dark),:host-context(.ion-palette-dark),:host-context(html.ion-palette-dark),:host-context([data-theme=\"dark\"]){--auth-bg-start: #0f0f1a;--auth-bg-mid: #1a1a2e;--auth-bg-end: #16213e;--auth-glass-bg: rgba(0, 0, 0, .3);--auth-grain-opacity: .05;--auth-wave-color: var(--ion-color-primary, #818cf8)}.auth-background{position:relative;display:flex;align-items:center;padding:0;background:linear-gradient(135deg,var(--auth-bg-start) 0%,var(--auth-bg-mid) 50%,var(--auth-bg-end) 100%);overflow:hidden}.grain{position:absolute;inset:0;pointer-events:none;z-index:0;opacity:var(--auth-grain-opacity);background-image:url(\"data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.5' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E\")}.glass-layer{position:absolute;inset:0;pointer-events:none;z-index:1;background:var(--auth-glass-bg);backdrop-filter:blur(3px) saturate(120%);-webkit-backdrop-filter:blur(3px) saturate(120%)}.wave-bg{position:absolute;inset:0;pointer-events:none;z-index:0;transition:filter .3s ease}:host-context(body.dark) .wave-bg,:host-context(.dark) .wave-bg,:host-context(.ion-palette-dark) .wave-bg,:host-context(html.ion-palette-dark) .wave-bg,:host-context([data-theme=\"dark\"]) .wave-bg{filter:invert(1) opacity(.6)}.waves{width:100%;height:100%;overflow:visible}.waves path{fill:none;will-change:transform;animation:wave-flow-right var(--travel-duration, 18s) linear var(--travel-delay, 0s) infinite}.waves path.travel-left{animation:wave-flow-left var(--travel-duration, 18s) linear var(--travel-delay, 0s) infinite}@keyframes wave-flow-right{0%{transform:translate(0)}25%{transform:translate(-360px,calc(var(--bob, 0px) * -1))}50%{transform:translate(-720px)}75%{transform:translate(-1080px,var(--bob, 0px))}to{transform:translate(-1440px)}}@keyframes wave-flow-left{0%{transform:translate(0)}25%{transform:translate(360px,var(--bob, 0px))}50%{transform:translate(720px)}75%{transform:translate(1080px,calc(var(--bob, 0px) * -1))}to{transform:translate(1440px)}}@media (prefers-reduced-motion: reduce){.waves path{animation:none}}.content-layer{position:relative;z-index:2;width:100%;height:100%;display:flex;align-items:center}\n"] }]
54926
54152
  }], ctorParameters: () => [], propDecorators: { props: [{
54927
54153
  type: Input
54928
54154
  }] } });
@@ -59370,6 +58596,263 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
59370
58596
  type: Output
59371
58597
  }] } });
59372
58598
 
58599
+ /**
58600
+ * Defaults i18n del componente (es/en). El componente los auto-registra en su
58601
+ * constructor si el consumer no proveyó el namespace `ContentReaction`. Esto
58602
+ * garantiza que NUNCA haya una key faltante: una key faltante se evalúa en el
58603
+ * template por cada change-detection → `console.warn` por CD → con el
58604
+ * `val-debug-console` activo (espeja console.* a un signal) se dispara un loop
58605
+ * infinito que congela el navegador en dev. Auto-registrar lo elimina de raíz.
58606
+ */
58607
+ const CONTENT_REACTION_I18N = {
58608
+ es: {
58609
+ question: '¿Te resultó útil este contenido?',
58610
+ commentPlaceholder: 'Cuéntanos más (opcional)...',
58611
+ submit: 'Enviar opinión',
58612
+ update: 'Actualizar opinión',
58613
+ thankYou: '¡Gracias por tu opinión!',
58614
+ submitted: 'Tu opinión ha sido registrada',
58615
+ errorSubmitting: 'Error al enviar. Intenta de nuevo.',
58616
+ positiveLabel: 'Sí, mucho',
58617
+ negativeLabel: 'No tanto',
58618
+ negativeFeedbackTitle: '¿Cuéntanos qué no te gustó?',
58619
+ },
58620
+ en: {
58621
+ question: 'Was this content helpful?',
58622
+ commentPlaceholder: 'Tell us more (optional)...',
58623
+ submit: 'Send feedback',
58624
+ update: 'Update feedback',
58625
+ thankYou: 'Thanks for your feedback!',
58626
+ submitted: 'Your feedback has been recorded',
58627
+ errorSubmitting: 'Failed to send. Please try again.',
58628
+ positiveLabel: 'Yes, a lot',
58629
+ negativeLabel: 'Not really',
58630
+ negativeFeedbackTitle: "Tell us what you didn't like?",
58631
+ },
58632
+ };
58633
+ /**
58634
+ * Componente para reacciones de contenido con emojis.
58635
+ *
58636
+ * @example
58637
+ * ```html
58638
+ * <val-content-reaction
58639
+ * [props]="{
58640
+ * entityRef: { entityType: 'article', entityId: 'art-123' },
58641
+ * question: '¿Te fue útil este artículo?'
58642
+ * }"
58643
+ * (reactionSubmit)="onReactionSubmit($event)"
58644
+ * />
58645
+ * ```
58646
+ */
58647
+ class ContentReactionComponent {
58648
+ constructor() {
58649
+ this.feedbackService = inject(FeedbackService);
58650
+ this.toast = inject(ToastService);
58651
+ this.i18n = inject(I18nService);
58652
+ this.auth = inject(AuthService, { optional: true });
58653
+ this.isConfigured = computed(() => this.feedbackService.isConfigured());
58654
+ this.props = {};
58655
+ this.reactionSubmit = new EventEmitter();
58656
+ this.reactionChange = new EventEmitter();
58657
+ // Estado reactivo
58658
+ this.state = signal({
58659
+ selectedValue: null,
58660
+ comment: '',
58661
+ isLoading: false,
58662
+ isSubmitted: false,
58663
+ hadPreviousReaction: false,
58664
+ error: null,
58665
+ });
58666
+ // Valores por defecto
58667
+ this.defaultEmojis = ['😞', '😐', '😊'];
58668
+ this.defaultLabels = ['No me ayudó', 'Regular', 'Muy útil'];
58669
+ // Computed properties
58670
+ this.resolvedProps = computed(() => ({
58671
+ entityRef: this.props.entityRef,
58672
+ question: this.props.question || this.t('question'),
58673
+ showComment: this.props.showComment ?? true,
58674
+ commentOnValues: this.props.commentOnValues,
58675
+ commentPlaceholder: this.props.commentPlaceholder || this.t('commentPlaceholder'),
58676
+ maxCommentLength: this.props.maxCommentLength ?? 500,
58677
+ emojis: this.props.emojis || this.defaultEmojis,
58678
+ emojiLabels: this.props.emojiLabels || this.defaultLabels,
58679
+ showThankYou: this.props.showThankYou ?? true,
58680
+ thankYouMessage: this.props.thankYouMessage || this.t('thankYou'),
58681
+ disabled: this.props.disabled ?? false,
58682
+ readonly: this.props.readonly ?? false,
58683
+ // Variante: 'emoji' cuando se declara explícitamente o cuando se pasan emojis custom.
58684
+ variant: this.props.variant ?? (this.props.emojis ? 'emoji' : 'buttons'),
58685
+ }));
58686
+ this.activeReactionValues = computed(() => this.resolvedProps().emojis.length === 2 ? ['negative', 'positive'] : ['negative', 'neutral', 'positive']);
58687
+ this.showCommentField = computed(() => {
58688
+ const sel = this.state().selectedValue;
58689
+ if (!sel)
58690
+ return false;
58691
+ const commentOn = this.resolvedProps().commentOnValues;
58692
+ if (commentOn !== undefined)
58693
+ return commentOn.includes(sel);
58694
+ return this.resolvedProps().showComment;
58695
+ });
58696
+ this.canSubmit = computed(() => this.state().selectedValue !== null && !this.state().isLoading);
58697
+ // Auto-registro de defaults i18n (es/en) si el consumer no proveyó el
58698
+ // namespace. Garantiza que `t()` nunca caiga en key faltante. Ver
58699
+ // CONTENT_REACTION_I18N. Idempotente: solo la 1ª instancia registra.
58700
+ if (!this.i18n.hasNamespace('ContentReaction')) {
58701
+ this.i18n.registerContent('ContentReaction', CONTENT_REACTION_I18N);
58702
+ }
58703
+ }
58704
+ ngOnInit() {
58705
+ this.loadPreviousReaction();
58706
+ }
58707
+ ngOnChanges(changes) {
58708
+ if (changes['props'] && !changes['props'].firstChange) {
58709
+ this.loadPreviousReaction();
58710
+ }
58711
+ }
58712
+ async loadPreviousReaction() {
58713
+ if (!this.props.entityRef)
58714
+ return;
58715
+ // Si hay initialValue, usarlo directamente sin consultar
58716
+ if (this.props.initialValue) {
58717
+ this.state.update(s => ({
58718
+ ...s,
58719
+ selectedValue: this.props.initialValue,
58720
+ hadPreviousReaction: true,
58721
+ isLoading: false,
58722
+ isSubmitted: true,
58723
+ }));
58724
+ return;
58725
+ }
58726
+ // Si skipInitialCheck está activo, no consultar
58727
+ if (this.props.skipInitialCheck) {
58728
+ return;
58729
+ }
58730
+ this.state.update(s => ({ ...s, isLoading: true, error: null }));
58731
+ try {
58732
+ const check = await this.feedbackService.checkFeedback(this.props.entityRef.entityType, this.props.entityRef.entityId);
58733
+ if (check.hasFeedback && check.reactionValue) {
58734
+ this.state.update(s => ({
58735
+ ...s,
58736
+ selectedValue: check.reactionValue,
58737
+ hadPreviousReaction: true,
58738
+ isLoading: false,
58739
+ isSubmitted: true,
58740
+ }));
58741
+ }
58742
+ else {
58743
+ this.state.update(s => ({ ...s, isLoading: false }));
58744
+ }
58745
+ }
58746
+ catch (error) {
58747
+ console.error('Error loading previous reaction:', error);
58748
+ this.state.update(s => ({ ...s, isLoading: false }));
58749
+ }
58750
+ }
58751
+ selectReaction(value) {
58752
+ if (this.resolvedProps().disabled || this.resolvedProps().readonly)
58753
+ return;
58754
+ const previousValue = this.state().selectedValue;
58755
+ this.state.update(s => ({
58756
+ ...s,
58757
+ selectedValue: value,
58758
+ isSubmitted: false,
58759
+ error: null,
58760
+ }));
58761
+ this.reactionChange.emit({ value, previousValue });
58762
+ }
58763
+ async submitReaction() {
58764
+ const currentState = this.state();
58765
+ const props = this.resolvedProps();
58766
+ if (!currentState.selectedValue || props.disabled)
58767
+ return;
58768
+ this.state.update(s => ({ ...s, isLoading: true, error: null }));
58769
+ try {
58770
+ // Determinar si usar endpoint anónimo o autenticado
58771
+ const isAuthenticated = this.auth?.isAuthenticated() ?? false;
58772
+ const useAnonymous = this.props.allowAnonymous && !isAuthenticated;
58773
+ if (useAnonymous) {
58774
+ // Usar endpoint anónimo (público)
58775
+ await this.feedbackService.createAnonymousReaction(props.entityRef, currentState.selectedValue, currentState.comment || undefined);
58776
+ }
58777
+ else {
58778
+ // Usar endpoint autenticado
58779
+ await this.feedbackService.createReaction(props.entityRef, currentState.selectedValue, currentState.comment || undefined);
58780
+ }
58781
+ this.state.update(s => ({
58782
+ ...s,
58783
+ isLoading: false,
58784
+ isSubmitted: true,
58785
+ comment: '',
58786
+ hadPreviousReaction: !useAnonymous, // Solo para autenticados
58787
+ }));
58788
+ this.reactionSubmit.emit({
58789
+ value: currentState.selectedValue,
58790
+ comment: currentState.comment || undefined,
58791
+ entityRef: props.entityRef,
58792
+ isUpdate: currentState.hadPreviousReaction,
58793
+ });
58794
+ if (props.showThankYou) {
58795
+ this.toast.show({
58796
+ message: props.thankYouMessage,
58797
+ duration: 2000,
58798
+ });
58799
+ }
58800
+ }
58801
+ catch (error) {
58802
+ console.error('Error submitting reaction:', error);
58803
+ this.state.update(s => ({
58804
+ ...s,
58805
+ isLoading: false,
58806
+ error: this.t('errorSubmitting'),
58807
+ }));
58808
+ this.toast.show({
58809
+ message: this.t('errorSubmitting'),
58810
+ duration: 3000,
58811
+ });
58812
+ }
58813
+ }
58814
+ updateComment(event) {
58815
+ const value = event.detail.value || '';
58816
+ this.state.update(s => ({ ...s, comment: value }));
58817
+ }
58818
+ getEmoji(index) {
58819
+ return this.resolvedProps().emojis[index];
58820
+ }
58821
+ getEmojiLabel(index) {
58822
+ return this.resolvedProps().emojiLabels[index];
58823
+ }
58824
+ isSelected(value) {
58825
+ return this.state().selectedValue === value;
58826
+ }
58827
+ t(key) {
58828
+ const translations = {
58829
+ question: '¿Te resultó útil este contenido?',
58830
+ commentPlaceholder: 'Cuéntanos más (opcional)...',
58831
+ submit: 'Enviar opinión',
58832
+ update: 'Actualizar opinión',
58833
+ thankYou: '¡Gracias por tu opinión!',
58834
+ submitted: 'Tu opinión ha sido registrada',
58835
+ errorSubmitting: 'Error al enviar. Intenta de nuevo.',
58836
+ positiveLabel: 'Sí, mucho',
58837
+ negativeLabel: 'No tanto',
58838
+ negativeFeedbackTitle: '¿Cuéntanos qué no te gustó?',
58839
+ };
58840
+ return this.i18n.t(key, 'ContentReaction') || translations[key] || key;
58841
+ }
58842
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ContentReactionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
58843
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ContentReactionComponent, isStandalone: true, selector: "val-content-reaction", inputs: { props: "props" }, outputs: { reactionSubmit: "reactionSubmit", reactionChange: "reactionChange" }, usesOnChanges: true, ngImport: i0, template: "@if (isConfigured()) {\n<div class=\"content-reaction\" [class.disabled]=\"resolvedProps().disabled\" [class.readonly]=\"resolvedProps().readonly\">\n <!-- Loading inicial -->\n @if (state().isLoading && !state().selectedValue) {\n <div class=\"loading-container\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n </div>\n } @else {\n <!-- Pregunta -->\n <p class=\"question\">\n {{ state().selectedValue === 'negative' ? t('negativeFeedbackTitle') : resolvedProps().question }}\n </p>\n\n <!-- Botones de reacci\u00F3n \u2014 variante emoji (caras, estrellas, thumbs, \u2026) -->\n @if (resolvedProps().variant === 'emoji') {\n <div class=\"reaction-buttons reaction-buttons--emoji\">\n @for (value of activeReactionValues(); track value; let i = $index) {\n <ion-button\n fill=\"clear\"\n shape=\"round\"\n [class.selected]=\"isSelected(value)\"\n [attr.aria-label]=\"resolvedProps().emojiLabels[i]\"\n [attr.aria-pressed]=\"isSelected(value)\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction(value)\"\n >\n <span class=\"emoji-btn\">{{ resolvedProps().emojis[i] }}</span>\n </ion-button>\n }\n </div>\n } @else {\n <!-- Botones de reacci\u00F3n \u2014 variante texto (default) -->\n <div class=\"reaction-buttons\">\n <ion-button\n [fill]=\"isSelected('positive') ? 'solid' : 'outline'\"\n shape=\"round\"\n color=\"dark\"\n [attr.aria-pressed]=\"isSelected('positive')\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction('positive')\"\n >\n {{ t('positiveLabel') }}\n </ion-button>\n <ion-button\n [fill]=\"isSelected('negative') ? 'solid' : 'outline'\"\n shape=\"round\"\n color=\"dark\"\n [attr.aria-pressed]=\"isSelected('negative')\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction('negative')\"\n >\n {{ t('negativeLabel') }}\n </ion-button>\n </div>\n }\n\n <!-- Campo de comentario (solo si hay selecci\u00F3n) -->\n @if (showCommentField()) {\n <div class=\"comment-section\">\n <ion-textarea\n [value]=\"state().comment\"\n [placeholder]=\"resolvedProps().commentPlaceholder\"\n [maxlength]=\"resolvedProps().maxCommentLength\"\n [disabled]=\"resolvedProps().disabled\"\n [rows]=\"3\"\n class=\"comment-textarea\"\n (ionInput)=\"updateComment($event)\"\n ></ion-textarea>\n <span class=\"char-count\"> {{ state().comment.length }}/{{ resolvedProps().maxCommentLength }} </span>\n </div>\n }\n\n <!-- Bot\u00F3n de env\u00EDo -->\n @if (state().selectedValue && !state().isSubmitted) {\n <ion-button expand=\"block\" shape=\"round\" [disabled]=\"!canSubmit()\" (click)=\"submitReaction()\" class=\"submit-button\">\n @if (state().isLoading) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ state().hadPreviousReaction ? t('update') : t('submit') }} }\n </ion-button>\n }\n\n <!-- Mensaje de confirmaci\u00F3n -->\n @if (state().isSubmitted) {\n <p class=\"submitted-message\">{{ t('thankYou') }}</p>\n }\n\n <!-- Error -->\n @if (state().error) {\n <p class=\"error-message\">{{ state().error }}</p>\n } }\n</div>\n}\n", styles: [":host{display:block}.content-reaction{padding:16px;text-align:center}.content-reaction.disabled{opacity:.6;pointer-events:none}.content-reaction.readonly{pointer-events:none}.question{font-size:1.0625rem;font-weight:700;color:var(--ion-color-dark);margin:0 0 16px}.reaction-buttons{display:flex;justify-content:center;gap:12px;margin-bottom:20px}.reaction-buttons--emoji{gap:8px}.reaction-buttons--emoji ion-button{--padding-start: 4px;--padding-end: 4px;transition:transform .15s ease}.reaction-buttons--emoji ion-button.selected{transform:scale(1.25)}.reaction-buttons--emoji ion-button:not(.selected):not([disabled]){opacity:.55}.reaction-buttons--emoji ion-button:not(.selected):not([disabled]):hover{opacity:1}.emoji-btn{font-size:2rem;line-height:1;display:block}.comment-section{margin-top:16px;animation:slideIn .3s ease-out}.comment-section .comment-textarea{--background: transparent;--border-radius: 8px;--padding-start: 12px;--padding-end: 12px;width:100%}.comment-section .char-count{display:block;text-align:right;font-size:.75rem;color:var(--ion-color-medium);margin-top:4px}.submit-button{margin-top:16px}.submitted-message{margin-top:16px;color:var(--ion-color-success);font-weight:500;animation:fadeIn .3s ease-out}.error-message{margin-top:8px;color:var(--ion-color-danger);font-size:.875rem}.loading-container{display:flex;justify-content:center;padding:20px}@keyframes slideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { 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: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: IonTextarea, selector: "ion-textarea", inputs: ["autoGrow", "autocapitalize", "autofocus", "clearOnEdit", "color", "cols", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "maxlength", "minlength", "mode", "name", "placeholder", "readonly", "required", "rows", "shape", "spellcheck", "value", "wrap"] }] }); }
58844
+ }
58845
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ContentReactionComponent, decorators: [{
58846
+ type: Component,
58847
+ args: [{ selector: 'val-content-reaction', standalone: true, imports: [CommonModule, FormsModule, IonButton, IonSpinner, IonTextarea], template: "@if (isConfigured()) {\n<div class=\"content-reaction\" [class.disabled]=\"resolvedProps().disabled\" [class.readonly]=\"resolvedProps().readonly\">\n <!-- Loading inicial -->\n @if (state().isLoading && !state().selectedValue) {\n <div class=\"loading-container\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n </div>\n } @else {\n <!-- Pregunta -->\n <p class=\"question\">\n {{ state().selectedValue === 'negative' ? t('negativeFeedbackTitle') : resolvedProps().question }}\n </p>\n\n <!-- Botones de reacci\u00F3n \u2014 variante emoji (caras, estrellas, thumbs, \u2026) -->\n @if (resolvedProps().variant === 'emoji') {\n <div class=\"reaction-buttons reaction-buttons--emoji\">\n @for (value of activeReactionValues(); track value; let i = $index) {\n <ion-button\n fill=\"clear\"\n shape=\"round\"\n [class.selected]=\"isSelected(value)\"\n [attr.aria-label]=\"resolvedProps().emojiLabels[i]\"\n [attr.aria-pressed]=\"isSelected(value)\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction(value)\"\n >\n <span class=\"emoji-btn\">{{ resolvedProps().emojis[i] }}</span>\n </ion-button>\n }\n </div>\n } @else {\n <!-- Botones de reacci\u00F3n \u2014 variante texto (default) -->\n <div class=\"reaction-buttons\">\n <ion-button\n [fill]=\"isSelected('positive') ? 'solid' : 'outline'\"\n shape=\"round\"\n color=\"dark\"\n [attr.aria-pressed]=\"isSelected('positive')\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction('positive')\"\n >\n {{ t('positiveLabel') }}\n </ion-button>\n <ion-button\n [fill]=\"isSelected('negative') ? 'solid' : 'outline'\"\n shape=\"round\"\n color=\"dark\"\n [attr.aria-pressed]=\"isSelected('negative')\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction('negative')\"\n >\n {{ t('negativeLabel') }}\n </ion-button>\n </div>\n }\n\n <!-- Campo de comentario (solo si hay selecci\u00F3n) -->\n @if (showCommentField()) {\n <div class=\"comment-section\">\n <ion-textarea\n [value]=\"state().comment\"\n [placeholder]=\"resolvedProps().commentPlaceholder\"\n [maxlength]=\"resolvedProps().maxCommentLength\"\n [disabled]=\"resolvedProps().disabled\"\n [rows]=\"3\"\n class=\"comment-textarea\"\n (ionInput)=\"updateComment($event)\"\n ></ion-textarea>\n <span class=\"char-count\"> {{ state().comment.length }}/{{ resolvedProps().maxCommentLength }} </span>\n </div>\n }\n\n <!-- Bot\u00F3n de env\u00EDo -->\n @if (state().selectedValue && !state().isSubmitted) {\n <ion-button expand=\"block\" shape=\"round\" [disabled]=\"!canSubmit()\" (click)=\"submitReaction()\" class=\"submit-button\">\n @if (state().isLoading) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ state().hadPreviousReaction ? t('update') : t('submit') }} }\n </ion-button>\n }\n\n <!-- Mensaje de confirmaci\u00F3n -->\n @if (state().isSubmitted) {\n <p class=\"submitted-message\">{{ t('thankYou') }}</p>\n }\n\n <!-- Error -->\n @if (state().error) {\n <p class=\"error-message\">{{ state().error }}</p>\n } }\n</div>\n}\n", styles: [":host{display:block}.content-reaction{padding:16px;text-align:center}.content-reaction.disabled{opacity:.6;pointer-events:none}.content-reaction.readonly{pointer-events:none}.question{font-size:1.0625rem;font-weight:700;color:var(--ion-color-dark);margin:0 0 16px}.reaction-buttons{display:flex;justify-content:center;gap:12px;margin-bottom:20px}.reaction-buttons--emoji{gap:8px}.reaction-buttons--emoji ion-button{--padding-start: 4px;--padding-end: 4px;transition:transform .15s ease}.reaction-buttons--emoji ion-button.selected{transform:scale(1.25)}.reaction-buttons--emoji ion-button:not(.selected):not([disabled]){opacity:.55}.reaction-buttons--emoji ion-button:not(.selected):not([disabled]):hover{opacity:1}.emoji-btn{font-size:2rem;line-height:1;display:block}.comment-section{margin-top:16px;animation:slideIn .3s ease-out}.comment-section .comment-textarea{--background: transparent;--border-radius: 8px;--padding-start: 12px;--padding-end: 12px;width:100%}.comment-section .char-count{display:block;text-align:right;font-size:.75rem;color:var(--ion-color-medium);margin-top:4px}.submit-button{margin-top:16px}.submitted-message{margin-top:16px;color:var(--ion-color-success);font-weight:500;animation:fadeIn .3s ease-out}.error-message{margin-top:8px;color:var(--ion-color-danger);font-size:.875rem}.loading-container{display:flex;justify-content:center;padding:20px}@keyframes slideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}\n"] }]
58848
+ }], ctorParameters: () => [], propDecorators: { props: [{
58849
+ type: Input
58850
+ }], reactionSubmit: [{
58851
+ type: Output
58852
+ }], reactionChange: [{
58853
+ type: Output
58854
+ }] } });
58855
+
59373
58856
  const DEFAULT_SPLASH_SCREEN_CONFIG = {
59374
58857
  fadeOutDuration: 300,
59375
58858
  };
@@ -63222,5 +62705,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
63222
62705
  * Generated bundle index. Do not edit.
63223
62706
  */
63224
62707
 
63225
- export { ACTION_CARD_DEFAULTS, AD_SIZE_MAP, API_TABLE_COLUMN_LABELS, ARTICLE_CARD_DEFAULTS, ARTICLE_SPACING, ARTICLE_STRIP_DEFAULTS, AUTH_CTA_DEFAULTS, AVATAR_UPLOAD_DEFAULTS, AboutViewComponent, AccordionComponent, AccountViewComponent, ActionCardComponent, ActionHeaderComponent, ActionType, AdSlotComponent, AdsLoaderService, AdsService, AlertBoxComponent, AnalyticsErrorHandler, AnalyticsRouterTracker, AnalyticsService, AnimatedTerminalComponent, AppConfigService, AppVersionService, ArticleBuilder, ArticleCardComponent, ArticleComponent, ArticleStripComponent, AttachmentUploaderComponent, AuthBackgroundComponent, AuthCtaComponent, AuthService, AuthStateService, AuthStorageService, AuthSyncService, AvatarComponent, AvatarUploadComponent, BOTTOM_NAV_DEFAULTS, BannerComponent, BaseDefault, BlogPostBuilder, BottomNavComponent, BoxComponent, BreadcrumbComponent, ButtonComponent, ButtonGroupComponent, CALLOUT_LABELS, CHEV_KEYS, CIRCLE_KEYS, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CONTENT_CARD_DEFAULTS, CORNER_KEYS, CTA_CARD_DEFAULTS, CURRENCY_INFO, CardComponent, CardSection, CardType, CardsCarouselComponent, ChangeEmailModalComponent, ChangePasswordModalComponent, CheckInputComponent, CheckboxRadioInputComponent, ChipGroupComponent, ClearDefault, ClearDefaultBlock, ClearDefaultFull, ClearDefaultRound, ClearDefaultRoundBlock, ClearDefaultRoundFull, CodeDisplayComponent, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ContainerComponent, ContentCardComponent, ContentLoaderComponent, ContentReactionComponent, ContentService, ContentTransformer, CookieBannerComponent, CountdownComponent, CreateOrgModalComponent, CtaCardComponent, CurrencyInputComponent, DEFAULT_ADS_CONFIG, DEFAULT_APP_CONFIG_SERVICE_CONFIG, DEFAULT_APP_VERSION_SERVICE_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_BACK_HEADER, DEFAULT_CANCEL_BUTTON, DEFAULT_CHECK_INTERVAL_MS, DEFAULT_CONFIRM_BUTTON, DEFAULT_COUNTDOWN_LABELS, DEFAULT_COUNTDOWN_LABELS_EN, DEFAULT_DEBUG_CONSOLE_CONFIG, DEFAULT_DONATION_CONFIG, DEFAULT_EMPTY_STATE, DEFAULT_EMULATOR_CONFIG, DEFAULT_FEEDBACK_CONFIG, DEFAULT_FEEDBACK_TYPE_OPTIONS, DEFAULT_HOME_HEADER, DEFAULT_INFINITE_LIST_METADATA, DEFAULT_MODAL_CANCEL_BUTTON, DEFAULT_MODAL_CONFIRM_BUTTON, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLATFORMS, DEFAULT_REFRESHER_METADATA, DEFAULT_SKELETON_CONFIG, DEFAULT_SPLASH_SCREEN_CONFIG, DataTableComponent, DateInputComponent, DateRangeInputComponent, DebugConsoleComponent, DeleteAccountModalComponent, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsBuilder, DocsCalloutComponent, DocsCodeExampleComponent, DocsLayoutComponent, DocsNavLinksComponent, DocsNavigationService, DocsPageComponent, DocsSearchComponent, DocsSectionComponent, DocsShellComponent, DocsSidebarComponent, DocsTocComponent, DonationService, DownloadService, EditOrgModalComponent, EmailInputComponent, EmptyStateComponent, ExpandableTextComponent, FEATURES_LIST_DEFAULTS, FUN_MODAL_DEFAULTS, FabComponent, FaqComponent, FeaturesListComponent, FeedbackFormComponent, FeedbackService, FileInputComponent, FirebaseService, FirestoreCollectionFactory, FirestoreService, FontSizeOption, FontSizeService, FooterComponent, FooterLinksComponent, FormComponent, FormSkeletonComponent, FunHeaderComponent, FunModalComponent, GlassComponent, GlowCardComponent, GlowComponent, GridSkeletonComponent, HANDOFF_ROUTE_PARAM, HANDOFF_TOKEN_PARAM, HandoffService, HasPermissionDirective, HeaderComponent, HintComponent, HorizontalScrollComponent, HourInputComponent, HrefComponent, I18nService, IMAGE_DEFAULTS, INFO_CARD_DEFAULTS, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, INVITATION_CARD_DEFAULTS, Icon, IconComponent, IconService, ImageComponent, ImageCropComponent, ImageService, InAppBrowserService, InfiniteListComponent, InfoCardComponent, InfoComponent, InputI18nHelper, InputType, InvitationCardComponent, InviteMemberModalComponent, ItemListComponent, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LEGAL_CONTENT_CONFIG, LOGIN_DEFAULTS, LandingSplitComponent, LandingStepsComponent, LanguageSelectorComponent, LayeredCardComponent, LegalContentService, LegalLinkService, LinkComponent, LinkProcessorService, LinkedProvidersComponent, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LoadingDirective, LocalStorageService, LocaleService, LoginAttemptModalComponent, LoginComponent, MEDIA_OBJECT_DEFAULTS, MEMBER_CARD_DEFAULTS, METRIC_CARD_DEFAULTS, MODAL_SIZES, MOTIF_KEYS, MOTION, MaintenancePageComponent, MarkdownArticleParserService, MediaObjectComponent, MemberCardComponent, MemberDetailModalComponent, MenuComponent, MessagingService, MetaService, MetricCardComponent, MfaModalComponent, ModalService, ModalShellComponent, MultiSelectSearchComponent, NUM_KEYS, NavigationService, NetworkBannerComponent, NetworkStatusService, NewsBuilder, NoContentComponent, NotesBoxComponent, NotificationActionService, NotificationPreferencesViewComponent, NotificationsService, NotificationsViewComponent, NumberFromToComponent, NumberInputComponent, NumberStepperComponent, OAUTH_PROVIDERS_INFO, OAuthCallbackComponent, OAuthService, OrgInfoSheetComponent, OrgService, OrgSwitchService, OrganizationViewComponent, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PATTERN_MOTIFS, PATTERN_PALETTES, PATTERN_STYLE_CONFIGS, PLATFORM_CONFIGS, PageContentComponent, PageLinksComponent, PageRefreshService, PageTemplateComponent, PageWavesComponent, PageWrapperComponent, PaginationComponent, PaginationService, PasswordInputComponent, PatternComponent, PermissionCatalogService, PermissionsViewComponent, PhoneInputComponent, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PreferencesService, PreferencesViewComponent, PresetService, PriceTagComponent, PrimarySolidBlockButton, PrimarySolidBlockHrefButton, PrimarySolidBlockIconButton, PrimarySolidBlockIconHrefButton, PrimarySolidDefaultRoundButton, PrimarySolidDefaultRoundHrefButton, PrimarySolidDefaultRoundIconButton, PrimarySolidDefaultRoundIconHrefButton, PrimarySolidFullButton, PrimarySolidFullHrefButton, PrimarySolidFullIconButton, PrimarySolidFullIconHrefButton, PrimarySolidLargeRoundButton, PrimarySolidLargeRoundHrefButton, PrimarySolidLargeRoundIconButton, PrimarySolidLargeRoundIconHrefButton, PrimarySolidSmallRoundButton, PrimarySolidSmallRoundHrefButton, PrimarySolidSmallRoundIconButton, PrimarySolidSmallRoundIconHrefButton, ProcessLinksPipe, ProfileSkeletonComponent, ProfileViewComponent, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrCodeComponent, QrGeneratorService, QueryBuilder, QuoteBoxComponent, REQUEST_STATUSES, RadioInputComponent, RangeInputComponent, RatingComponent, RefresherComponent, RequestFirestoreService, RequestFormBuilderService, RequestService, RightsFooterComponent, 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, SecondarySolidBlockButton, SecondarySolidBlockHrefButton, SecondarySolidBlockIconButton, SecondarySolidBlockIconHrefButton, SecondarySolidDefaultRoundButton, SecondarySolidDefaultRoundHrefButton, SecondarySolidDefaultRoundIconButton, SecondarySolidDefaultRoundIconHrefButton, SecondarySolidFullButton, SecondarySolidFullHrefButton, SecondarySolidFullIconButton, SecondarySolidFullIconHrefButton, SecondarySolidLargeRoundButton, SecondarySolidLargeRoundHrefButton, SecondarySolidLargeRoundIconButton, SecondarySolidLargeRoundIconHrefButton, SecondarySolidSmallRoundButton, SecondarySolidSmallRoundHrefButton, SecondarySolidSmallRoundIconButton, SecondarySolidSmallRoundIconHrefButton, SecurityViewComponent, SegmentControlComponent, SelectSearchComponent, SessionListModalComponent, SessionService, SettingsHubComponent, ShareButtonsComponent, ShareProfileModalComponent, SimpleComponent, SkeletonComponent, SkeletonLayoutComponent, SkeletonService, SolidBlockButton, SolidDefault, SolidDefaultBlock, SolidDefaultButton, SolidDefaultFull, SolidDefaultRound, SolidDefaultRoundBlock, SolidDefaultRoundButton, SolidDefaultRoundFull, SolidFullButton, SolidLargeButton, SolidLargeRoundButton, SolidSmallButton, SolidSmallRoundButton, SplashScreenService, StatsBarComponent, StatsCardComponent, StepperComponent, StorageService, SwipeCarouselComponent, SwitchOrgModalComponent, TRI_KEYS, TabbedContentComponent, TableSkeletonComponent, TabsComponent, Terminal404Component, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeService, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TransferOwnershipModalComponent, TranslatePipe, TypedCollection, UPDATE_BANNER_DEFAULT_CONTENT, UPDATE_BANNER_I18N_NAMESPACE, UpdateBannerComponent, UserAvatarComponent, UsernameInputComponent, VALTECH_ADS_CONFIG, VALTECH_APP_CONFIG, VALTECH_APP_VERSION, VALTECH_AUTH_CONFIG, VALTECH_COMPANY_LINKS, VALTECH_CONTENT_CONFIG, VALTECH_DEBUG_CONSOLE, VALTECH_DEFAULT_CONTENT, VALTECH_DONATION_CONFIG, VALTECH_FEEDBACK_CONFIG, VALTECH_FIREBASE_CONFIG, VALTECH_FOOTER_I18N, VALTECH_FOOTER_LOGO, VALTECH_LANGUAGE_SELECTOR, VALTECH_LEGAL_CONFIG, VALTECH_NETWORK_ERROR_KEY, VALTECH_SITE_PATHS, VALTECH_SOCIAL_LINKS, VALTECH_SPLASH_SCREEN, VALTECH_WEB_BASE_URLS, VERSION, ValtechErrorService, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, authGuard, authInterceptor, authPasswordValidator, beautifyLegalArticle, blogPost, buildCompanyFooterProps, buildFooterLinks, buildLegalLinkResolver, buildPath, buildSettingsCards, collections, connectPageRefresh, createErrorStateProps, createFirebaseConfig, createGlowCardProps, createInitialPaginationState, createNumberFromToField, createRefreshableStream, createTitleProps, docs, errorLoggingInterceptor, extractPathParams, generatePatternTiles, generateRandomTile, getAppInfo, getAppVersion, getCollectionPath, getDocumentId, getTimeOfDayKey, goToTop, guestGuard, hasEmulators, interpretError, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isValidPath, joinPath, maxLength, mulberry32, news, parseMarkdownArticle, permissionGuard, permissionGuardFromRoute, provideLegalContent, provideSplashScreen, provideValtechAboutRoutes, provideValtechAccountRoutes, provideValtechAds, provideValtechAppConfig, provideValtechAppVersion, provideValtechAuth, provideValtechAuthInterceptor, provideValtechContent, provideValtechDebugConsole, provideValtechDonations, provideValtechErrorHandling, provideValtechFeedback, provideValtechFirebase, provideValtechI18n, provideValtechLegal, provideValtechNotificationPreferencesRoutes, provideValtechNotificationsRoutes, provideValtechOrganizationRoutes, provideValtechPermissionsRoutes, provideValtechPreferencesRoutes, provideValtechPresets, provideValtechProfileRoutes, provideValtechSecurityRoutes, provideValtechSettingsRoutes, provideValtechSite, provideValtechSkeleton, query, renderPatternSvgInner, replaceSpecialChars, resolveColor$1 as resolveColor, resolveInputDefaultValue, resolveWebBaseUrl, roleGuard, storagePaths, superAdminGuard, toArticle };
62708
+ export { ACTION_CARD_DEFAULTS, AD_SIZE_MAP, API_TABLE_COLUMN_LABELS, ARTICLE_CARD_DEFAULTS, ARTICLE_SPACING, ARTICLE_STRIP_DEFAULTS, AUTH_CTA_DEFAULTS, AVATAR_UPLOAD_DEFAULTS, AccordionComponent, AccountViewComponent, ActionCardComponent, ActionHeaderComponent, ActionType, AdSlotComponent, AdsLoaderService, AdsService, AlertBoxComponent, AnalyticsErrorHandler, AnalyticsRouterTracker, AnalyticsService, AnimatedTerminalComponent, AppConfigService, AppVersionService, ArticleBuilder, ArticleCardComponent, ArticleComponent, ArticleStripComponent, AttachmentUploaderComponent, AuthBackgroundComponent, AuthCtaComponent, AuthService, AuthStateService, AuthStorageService, AuthSyncService, AvatarComponent, AvatarUploadComponent, BOTTOM_NAV_DEFAULTS, BannerComponent, BaseDefault, BlogPostBuilder, BottomNavComponent, BoxComponent, BreadcrumbComponent, ButtonComponent, ButtonGroupComponent, CALLOUT_LABELS, CHEV_KEYS, CIRCLE_KEYS, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CONTENT_CARD_DEFAULTS, CORNER_KEYS, CTA_CARD_DEFAULTS, CURRENCY_INFO, CardComponent, CardSection, CardType, CardsCarouselComponent, ChangeEmailModalComponent, ChangePasswordModalComponent, CheckInputComponent, CheckboxRadioInputComponent, ChipGroupComponent, ClearDefault, ClearDefaultBlock, ClearDefaultFull, ClearDefaultRound, ClearDefaultRoundBlock, ClearDefaultRoundFull, CodeDisplayComponent, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ContainerComponent, ContentCardComponent, ContentLoaderComponent, ContentReactionComponent, ContentService, ContentTransformer, CookieBannerComponent, CountdownComponent, CreateOrgModalComponent, CtaCardComponent, CurrencyInputComponent, DEFAULT_ADS_CONFIG, DEFAULT_APP_CONFIG_SERVICE_CONFIG, DEFAULT_APP_VERSION_SERVICE_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_BACK_HEADER, DEFAULT_CANCEL_BUTTON, DEFAULT_CHECK_INTERVAL_MS, DEFAULT_CONFIRM_BUTTON, DEFAULT_COUNTDOWN_LABELS, DEFAULT_COUNTDOWN_LABELS_EN, DEFAULT_DEBUG_CONSOLE_CONFIG, DEFAULT_DONATION_CONFIG, DEFAULT_EMPTY_STATE, DEFAULT_EMULATOR_CONFIG, DEFAULT_FEEDBACK_CONFIG, DEFAULT_FEEDBACK_TYPE_OPTIONS, DEFAULT_HOME_HEADER, DEFAULT_INFINITE_LIST_METADATA, DEFAULT_MODAL_CANCEL_BUTTON, DEFAULT_MODAL_CONFIRM_BUTTON, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLATFORMS, DEFAULT_REFRESHER_METADATA, DEFAULT_SKELETON_CONFIG, DEFAULT_SPLASH_SCREEN_CONFIG, DataTableComponent, DateInputComponent, DateRangeInputComponent, DebugConsoleComponent, DeleteAccountModalComponent, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsBuilder, DocsCalloutComponent, DocsCodeExampleComponent, DocsLayoutComponent, DocsNavLinksComponent, DocsNavigationService, DocsPageComponent, DocsSearchComponent, DocsSectionComponent, DocsShellComponent, DocsSidebarComponent, DocsTocComponent, DonationService, DownloadService, EditOrgModalComponent, EmailInputComponent, EmptyStateComponent, ExpandableTextComponent, FEATURES_LIST_DEFAULTS, FUN_MODAL_DEFAULTS, FabComponent, FaqComponent, FeaturesListComponent, FeedbackFormComponent, FeedbackService, FileInputComponent, FirebaseService, FirestoreCollectionFactory, FirestoreService, FontSizeOption, FontSizeService, FooterComponent, FooterLinksComponent, FormComponent, FormSkeletonComponent, FunHeaderComponent, FunModalComponent, GlassComponent, GlowCardComponent, GlowComponent, GridSkeletonComponent, HANDOFF_ROUTE_PARAM, HANDOFF_TOKEN_PARAM, HandoffService, HasPermissionDirective, HeaderComponent, HintComponent, HorizontalScrollComponent, HourInputComponent, HrefComponent, I18nService, IMAGE_DEFAULTS, INFO_CARD_DEFAULTS, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, INVITATION_CARD_DEFAULTS, Icon, IconComponent, IconService, ImageComponent, ImageCropComponent, ImageService, InAppBrowserService, InfiniteListComponent, InfoCardComponent, InfoComponent, InputI18nHelper, InputType, InvitationCardComponent, InviteMemberModalComponent, ItemListComponent, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LEGAL_CONTENT_CONFIG, LOGIN_DEFAULTS, LandingSplitComponent, LandingStepsComponent, LanguageSelectorComponent, LayeredCardComponent, LegalContentService, LegalLinkService, LinkComponent, LinkProcessorService, LinkedProvidersComponent, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LoadingDirective, LocalStorageService, LocaleService, LoginAttemptModalComponent, LoginComponent, MEDIA_OBJECT_DEFAULTS, MEMBER_CARD_DEFAULTS, METRIC_CARD_DEFAULTS, MODAL_SIZES, MOTIF_KEYS, MOTION, MaintenancePageComponent, MarkdownArticleParserService, MediaObjectComponent, MemberCardComponent, MemberDetailModalComponent, MenuComponent, MessagingService, MetaService, MetricCardComponent, MfaModalComponent, ModalService, ModalShellComponent, MultiSelectSearchComponent, NUM_KEYS, NavigationService, NetworkBannerComponent, NetworkStatusService, NewsBuilder, NoContentComponent, NotesBoxComponent, NotificationActionService, NotificationPreferencesViewComponent, NotificationsService, NotificationsViewComponent, NumberFromToComponent, NumberInputComponent, NumberStepperComponent, OAUTH_PROVIDERS_INFO, OAuthCallbackComponent, OAuthService, OrgInfoSheetComponent, OrgService, OrgSwitchService, OrganizationViewComponent, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PATTERN_MOTIFS, PATTERN_PALETTES, PATTERN_STYLE_CONFIGS, PLATFORM_CONFIGS, PageContentComponent, PageLinksComponent, PageRefreshService, PageTemplateComponent, PageWavesComponent, PageWrapperComponent, PaginationComponent, PaginationService, PasswordInputComponent, PatternComponent, PermissionCatalogService, PermissionsViewComponent, PhoneInputComponent, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PreferencesService, PreferencesViewComponent, PresetService, PriceTagComponent, PrimarySolidBlockButton, PrimarySolidBlockHrefButton, PrimarySolidBlockIconButton, PrimarySolidBlockIconHrefButton, PrimarySolidDefaultRoundButton, PrimarySolidDefaultRoundHrefButton, PrimarySolidDefaultRoundIconButton, PrimarySolidDefaultRoundIconHrefButton, PrimarySolidFullButton, PrimarySolidFullHrefButton, PrimarySolidFullIconButton, PrimarySolidFullIconHrefButton, PrimarySolidLargeRoundButton, PrimarySolidLargeRoundHrefButton, PrimarySolidLargeRoundIconButton, PrimarySolidLargeRoundIconHrefButton, PrimarySolidSmallRoundButton, PrimarySolidSmallRoundHrefButton, PrimarySolidSmallRoundIconButton, PrimarySolidSmallRoundIconHrefButton, ProcessLinksPipe, ProfileSkeletonComponent, ProfileViewComponent, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrCodeComponent, QrGeneratorService, QueryBuilder, QuoteBoxComponent, REQUEST_STATUSES, RadioInputComponent, RangeInputComponent, RatingComponent, RefresherComponent, RequestFirestoreService, RequestFormBuilderService, RequestService, RightsFooterComponent, 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, SecondarySolidBlockButton, SecondarySolidBlockHrefButton, SecondarySolidBlockIconButton, SecondarySolidBlockIconHrefButton, SecondarySolidDefaultRoundButton, SecondarySolidDefaultRoundHrefButton, SecondarySolidDefaultRoundIconButton, SecondarySolidDefaultRoundIconHrefButton, SecondarySolidFullButton, SecondarySolidFullHrefButton, SecondarySolidFullIconButton, SecondarySolidFullIconHrefButton, SecondarySolidLargeRoundButton, SecondarySolidLargeRoundHrefButton, SecondarySolidLargeRoundIconButton, SecondarySolidLargeRoundIconHrefButton, SecondarySolidSmallRoundButton, SecondarySolidSmallRoundHrefButton, SecondarySolidSmallRoundIconButton, SecondarySolidSmallRoundIconHrefButton, SecurityViewComponent, SegmentControlComponent, SelectSearchComponent, SessionListModalComponent, SessionService, SettingsHubComponent, ShareButtonsComponent, ShareProfileModalComponent, SimpleComponent, SkeletonComponent, SkeletonLayoutComponent, SkeletonService, SolidBlockButton, SolidDefault, SolidDefaultBlock, SolidDefaultButton, SolidDefaultFull, SolidDefaultRound, SolidDefaultRoundBlock, SolidDefaultRoundButton, SolidDefaultRoundFull, SolidFullButton, SolidLargeButton, SolidLargeRoundButton, SolidSmallButton, SolidSmallRoundButton, SplashScreenService, StatsBarComponent, StatsCardComponent, StepperComponent, StorageService, SwipeCarouselComponent, SwitchOrgModalComponent, TRI_KEYS, TabbedContentComponent, TableSkeletonComponent, TabsComponent, Terminal404Component, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeService, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TransferOwnershipModalComponent, TranslatePipe, TypedCollection, UPDATE_BANNER_DEFAULT_CONTENT, UPDATE_BANNER_I18N_NAMESPACE, UpdateBannerComponent, UserAvatarComponent, UsernameInputComponent, VALTECH_ADS_CONFIG, VALTECH_APP_CONFIG, VALTECH_APP_VERSION, VALTECH_AUTH_CONFIG, VALTECH_COMPANY_LINKS, VALTECH_CONTENT_CONFIG, VALTECH_DEBUG_CONSOLE, VALTECH_DEFAULT_CONTENT, VALTECH_DONATION_CONFIG, VALTECH_FEEDBACK_CONFIG, VALTECH_FIREBASE_CONFIG, VALTECH_FOOTER_I18N, VALTECH_FOOTER_LOGO, VALTECH_LANGUAGE_SELECTOR, VALTECH_LEGAL_CONFIG, VALTECH_NETWORK_ERROR_KEY, VALTECH_SITE_PATHS, VALTECH_SOCIAL_LINKS, VALTECH_SPLASH_SCREEN, VALTECH_WEB_BASE_URLS, VERSION, ValtechErrorService, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, authGuard, authInterceptor, authPasswordValidator, beautifyLegalArticle, blogPost, buildCompanyFooterProps, buildFooterLinks, buildLegalLinkResolver, buildPath, buildSettingsCards, collections, connectPageRefresh, createErrorStateProps, createFirebaseConfig, createGlowCardProps, createInitialPaginationState, createNumberFromToField, createRefreshableStream, createTitleProps, docs, errorLoggingInterceptor, extractPathParams, generatePatternTiles, generateRandomTile, getAppInfo, getAppVersion, getCollectionPath, getDocumentId, getTimeOfDayKey, goToTop, guestGuard, hasEmulators, interpretError, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isValidPath, joinPath, maxLength, mulberry32, news, parseMarkdownArticle, permissionGuard, permissionGuardFromRoute, provideLegalContent, provideSplashScreen, provideValtechAccountRoutes, provideValtechAds, provideValtechAppConfig, provideValtechAppVersion, provideValtechAuth, provideValtechAuthInterceptor, provideValtechContent, provideValtechDebugConsole, provideValtechDonations, provideValtechErrorHandling, provideValtechFeedback, provideValtechFirebase, provideValtechI18n, provideValtechLegal, provideValtechNotificationPreferencesRoutes, provideValtechNotificationsRoutes, provideValtechOrganizationRoutes, provideValtechPermissionsRoutes, provideValtechPreferencesRoutes, provideValtechPresets, provideValtechProfileRoutes, provideValtechSecurityRoutes, provideValtechSettingsRoutes, provideValtechSite, provideValtechSkeleton, query, renderPatternSvgInner, replaceSpecialChars, resolveColor$1 as resolveColor, resolveInputDefaultValue, resolveWebBaseUrl, roleGuard, storagePaths, superAdminGuard, toArticle };
63226
62709
  //# sourceMappingURL=valtech-components.mjs.map