layout-tsi 0.0.7 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  import * as i1 from '@angular/common';
2
2
  import { NgClass, CommonModule, Location, NgStyle, KeyValuePipe, DecimalPipe } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
- import { inject, signal, computed, Injectable, InjectionToken, DestroyRef, Injector, effect, input, ChangeDetectionStrategy, Component, output, HostListener, NgZone, ViewChild, untracked, reflectComponentType, ContentChild, viewChild } from '@angular/core';
4
+ import { inject, signal, computed, Injectable, InjectionToken, DestroyRef, Injector, effect, input, ChangeDetectionStrategy, Component, output, HostListener, NgZone, afterNextRender, ViewChild, untracked, reflectComponentType, ContentChild, viewChild } from '@angular/core';
5
5
  import * as i2 from '@angular/router';
6
6
  import { Router, NavigationEnd, RouterLink, ChildrenOutletContexts, RouterModule } from '@angular/router';
7
7
  import { toSignal, takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
@@ -585,6 +585,7 @@ class VerticalHeaderComponent {
585
585
  headerSideStartRef;
586
586
  headerSideEndRef;
587
587
  compactSearchWrapperRef;
588
+ accesoTooltipRef;
588
589
  // Buscador centrado: se contrae a "F2" apenas su hueco (280px, centrado)
589
590
  // choca con el título/módulo de la izquierda o con los íconos de la derecha,
590
591
  // en vez de depender de un breakpoint fijo.
@@ -593,6 +594,7 @@ class VerticalHeaderComponent {
593
594
  searchCollapsed = signal(false);
594
595
  resizeObserver;
595
596
  ngZone = inject(NgZone);
597
+ injector = inject(Injector);
596
598
  router = inject(Router);
597
599
  tabsService = inject(TabsService);
598
600
  actions = input([]);
@@ -681,6 +683,8 @@ class VerticalHeaderComponent {
681
683
  userAvatar = input(null);
682
684
  avatarFallido = signal(null);
683
685
  avatarSrc = computed(() => (this.userAvatar() !== this.avatarFallido() && this.userAvatar()) || 'assets/images/admin-avatar.png');
686
+ static TOOLTIP_GAP = 4;
687
+ static TOOLTIP_MARGIN = 8;
684
688
  tooltip = signal(null);
685
689
  // Reemplazan a ngbDropdown (sin @ng-bootstrap acá): un dropdown nativo por
686
690
  // click, reutilizando las clases de Bootstrap (dropdown/dropdown-menu/show)
@@ -727,7 +731,31 @@ class VerticalHeaderComponent {
727
731
  if (!text)
728
732
  return;
729
733
  const rect = event.currentTarget.getBoundingClientRect();
730
- this.tooltip.set({ text, x: rect.right + 4, y: rect.top + rect.height / 2 });
734
+ const gap = VerticalHeaderComponent.TOOLTIP_GAP;
735
+ const margin = VerticalHeaderComponent.TOOLTIP_MARGIN;
736
+ const y = rect.top + rect.height / 2;
737
+ this.tooltip.set({ text, y, side: 'right', left: rect.right + gap });
738
+ afterNextRender(() => {
739
+ const el = this.accesoTooltipRef?.nativeElement;
740
+ const current = this.tooltip();
741
+ if (!el || current?.text !== text)
742
+ return;
743
+ const width = el.getBoundingClientRect().width;
744
+ const viewport = document.documentElement.clientWidth;
745
+ const spaceRight = viewport - rect.right - gap - margin;
746
+ const spaceLeft = rect.left - gap - margin;
747
+ if (width <= spaceRight)
748
+ return;
749
+ if (width <= spaceLeft) {
750
+ this.tooltip.set({ text, y, side: 'left', right: viewport - rect.left + gap });
751
+ }
752
+ else if (spaceLeft > spaceRight) {
753
+ this.tooltip.set({ text, y, side: 'left', right: viewport - rect.left + gap, maxWidth: spaceLeft });
754
+ }
755
+ else {
756
+ this.tooltip.set({ text, y, side: 'right', left: rect.right + gap, maxWidth: spaceRight });
757
+ }
758
+ }, { injector: this.injector });
731
759
  }
732
760
  hideTooltip() { this.tooltip.set(null); }
733
761
  // Ningún atajo de teclado se define acá (ni F2 ni ninguno con `key`): todos
@@ -770,11 +798,11 @@ class VerticalHeaderComponent {
770
798
  this.searchCollapsed.set(collides);
771
799
  }
772
800
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: VerticalHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
773
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.25", type: VerticalHeaderComponent, isStandalone: true, selector: "app-vertical-header", inputs: { actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, showSearch: { classPropertyName: "showSearch", publicName: "showSearch", isSignal: true, isRequired: false, transformFunction: null }, themes: { classPropertyName: "themes", publicName: "themes", isSignal: true, isRequired: false, transformFunction: null }, showThemeSwitcher: { classPropertyName: "showThemeSwitcher", publicName: "showThemeSwitcher", isSignal: true, isRequired: false, transformFunction: null }, showTabs: { classPropertyName: "showTabs", publicName: "showTabs", isSignal: true, isRequired: false, transformFunction: null }, showNotifications: { classPropertyName: "showNotifications", publicName: "showNotifications", isSignal: true, isRequired: false, transformFunction: null }, notificationsCount: { classPropertyName: "notificationsCount", publicName: "notificationsCount", isSignal: true, isRequired: false, transformFunction: null }, notificationsInBottomNav: { classPropertyName: "notificationsInBottomNav", publicName: "notificationsInBottomNav", isSignal: true, isRequired: false, transformFunction: null }, showChatbotToggle: { classPropertyName: "showChatbotToggle", publicName: "showChatbotToggle", isSignal: true, isRequired: false, transformFunction: null }, chatbotVisible: { classPropertyName: "chatbotVisible", publicName: "chatbotVisible", isSignal: true, isRequired: false, transformFunction: null }, chatbotInBottomNav: { classPropertyName: "chatbotInBottomNav", publicName: "chatbotInBottomNav", isSignal: true, isRequired: false, transformFunction: null }, currentTheme: { classPropertyName: "currentTheme", publicName: "currentTheme", isSignal: true, isRequired: false, transformFunction: null }, versionApp: { classPropertyName: "versionApp", publicName: "versionApp", isSignal: true, isRequired: false, transformFunction: null }, accentColor: { classPropertyName: "accentColor", publicName: "accentColor", isSignal: true, isRequired: false, transformFunction: null }, envClass: { classPropertyName: "envClass", publicName: "envClass", isSignal: true, isRequired: false, transformFunction: null }, produccion: { classPropertyName: "produccion", publicName: "produccion", isSignal: true, isRequired: false, transformFunction: null }, isScrolled: { classPropertyName: "isScrolled", publicName: "isScrolled", isSignal: true, isRequired: false, transformFunction: null }, isSidebarOpen: { classPropertyName: "isSidebarOpen", publicName: "isSidebarOpen", isSignal: true, isRequired: false, transformFunction: null }, nameProyecto: { classPropertyName: "nameProyecto", publicName: "nameProyecto", isSignal: true, isRequired: false, transformFunction: null }, showPanelControl: { classPropertyName: "showPanelControl", publicName: "showPanelControl", isSignal: true, isRequired: false, transformFunction: null }, userName: { classPropertyName: "userName", publicName: "userName", isSignal: true, isRequired: false, transformFunction: null }, empresaName: { classPropertyName: "empresaName", publicName: "empresaName", isSignal: true, isRequired: false, transformFunction: null }, userAvatar: { classPropertyName: "userAvatar", publicName: "userAvatar", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { notificationsClick: "notificationsClick", chatbotVisibleChange: "chatbotVisibleChange", themeChange: "themeChange", emitirToogle: "emitirToogle", searchComponents: "searchComponents", panelControlClick: "panelControlClick", logout: "logout" }, host: { listeners: { "document:click": "onDocumentClick()" } }, viewQueries: [{ propertyName: "headerContainerRef", first: true, predicate: ["headerContainer"], descendants: true }, { propertyName: "headerSideStartRef", first: true, predicate: ["headerSideStart"], descendants: true }, { propertyName: "headerSideEndRef", first: true, predicate: ["headerSideEnd"], descendants: true }, { propertyName: "compactSearchWrapperRef", first: true, predicate: ["compactSearchWrapper"], descendants: true }], ngImport: i0, template: "<div #headerContainer class=\"d-flex header-container\" [ngClass]=\"envClass()\">\r\n <div class=\"first-header d-none d-md-flex\">\r\n <img src=\"assets/images/logo-tsi-tres.png\" width=\"40px\" height=\"40px\" style=\"cursor:pointer\"\r\n (click)=\"goToHome()\" title=\"Ir a Home\">\r\n <span class=\"text-white empresa-nombre ps-2\" [title]=\"versionApp()\">{{empresaName()}}</span>\r\n </div>\r\n\r\n <div class=\"d-flex w-100 h-100 justify-content-between align-items-center pe-3 ps-0\">\r\n <div #headerSideStart class=\"d-flex align-items-center header-side\">\r\n <button class=\"btn text-white none-style sidebar-toggle-btn\" (click)=\"emitirToogle.emit()\">\r\n <i class=\"fa-solid fa-table-columns\" [class.flipped]=\"isSidebarOpen()\"></i>\r\n <i class=\"fa-solid fa-chevron-left toggle-arrow\" [class.flipped]=\"isSidebarOpen()\"></i>\r\n </button>\r\n <span class=\"text-white empresa-nombre d-inline d-md-none ps-2\">{{empresaName()}}</span>\r\n <span class=\"text-white fs-5 fw-normal d-none d-md-inline ps-2 text-nowrap\">{{nameProyecto()}}</span>\r\n @if (showTabs() && tabsService.tabs().length) {\r\n <button class=\"tabs-header-btn mx-2 d-none d-md-flex\"\r\n [class.tabs-header-btn--active]=\"tabsService.visible()\" (click)=\"tabsService.toggleVisible()\"\r\n [title]=\"tabsService.visible() ? 'Ocultar acceso r\u00E1pido' : 'Mostrar acceso r\u00E1pido'\">\r\n <i class=\"fas fa-layer-group\"></i>\r\n @if (!tabsService.visible()) {\r\n <span class=\"tabs-header-count\">{{ tabsService.tabs().length }}</span>\r\n }\r\n </button>\r\n }\r\n </div>\r\n\r\n @if (showSearch()) {\r\n <button class=\"global-search-trigger d-none d-md-flex align-items-center\"\r\n [class.global-search-trigger--collapsed]=\"searchCollapsed()\" title=\"B\u00FAsqueda de p\u00E1ginas (F2)\"\r\n (click)=\"searchComponents.emit()\">\r\n <i class=\"fas fa-search global-search-icon\"></i>\r\n <span class=\"global-search-placeholder\">Buscar p\u00E1ginas...</span>\r\n <kbd class=\"global-search-kbd\">F2</kbd>\r\n </button>\r\n }\r\n\r\n <div #headerSideEnd class=\"d-flex align-items-center header-side header-side--end\">\r\n\r\n <!-- Fallback del buscador centrado cuando no entra (colisi\u00F3n) o en mobile\r\n (el buscador centrado se oculta por completo por debajo de md): la\r\n p\u00EDldora de texto \"F2\", igual que antes. -->\r\n <div #compactSearchWrapper class=\"h-100 d-flex align-items-center\">\r\n @if (showSearch() && searchCollapsed()) {\r\n <button class=\"global-search-compact-pill\" title=\"Buscar p\u00E1ginas (F2)\"\r\n (click)=\"searchComponents.emit()\">F2</button>\r\n }\r\n </div>\r\n\r\n <div class=\"d-flex align-items-center\">\r\n <ng-content select=\"[extra-actions]\" />\r\n\r\n <!-- Acciones adicionales, definidas por el consumidor: siempre a la izquierda\r\n de los tres fijos (tema, notificaciones, soporte). Solo se ocultan en mobile\r\n las que adem\u00E1s se reflejan en el bottom-nav (showInBottomNav) \u2014 si una acci\u00F3n\r\n no baja al bottom-nav, se queda visible ac\u00E1 para no desaparecer del todo. -->\r\n @for (action of actions(); track action.label) {\r\n @if (!action.hidden) {\r\n @if (action.items?.length) {\r\n <div class=\"dropdown d-inline-block z-index\" [class.show]=\"openActionKey() === action.label\" [class.desktop-only]=\"action.showInBottomNav\">\r\n <div class=\"h-100 d-flex align-items-center\">\r\n <button class=\"notification-btn option p-0 m-0 me-2\" [title]=\"action.label\" (click)=\"action.accion?.(); toggleActionMenu($event, action)\">\r\n <i class=\"fas text-white\" [ngClass]=\"action.icon\"></i>\r\n </button>\r\n </div>\r\n\r\n <div class=\"dropdown-menu dropdown-menu-end mtp-9 mb-0 py-0 custom-dropdown\" [class.show]=\"openActionKey() === action.label\">\r\n @for (opcion of action.items; track opcion.label; let last = $last) {\r\n <button class=\"dropdown-item py-2 fs-7 fw-4 d-flex align-items-center\"\r\n [ngClass]=\"{'divider-border': !last,'border-top-radius': $index == 0,'border-bottom-radius': last}\"\r\n (click)=\"runAction(opcion)\"\r\n (mouseenter)=\"showTooltip($event, opcion.title ?? '')\" (mouseleave)=\"hideTooltip()\">\r\n <i [class]=\"opcion.icon\" class=\"me-1\"></i> {{ opcion.label }}\r\n @if (opcion.value) {\r\n <span class=\"ms-auto ps-3 fw-6\">{{ opcion.value }}</span>\r\n }\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"h-100 d-flex align-items-center\" [class.desktop-only]=\"action.showInBottomNav\">\r\n <button class=\"notification-btn option p-0 m-0 me-2\" [title]=\"action.label\" (click)=\"action.accion?.()\">\r\n <i class=\"fas text-white\" [ngClass]=\"action.icon\"></i>\r\n @if (action.badge) {\r\n <span class=\"notification-badge\" [ngClass]=\"produccion() ? 'bg-danger' : 'bg-info'\">\r\n {{ action.badge }}\r\n </span>\r\n }\r\n </button>\r\n </div>\r\n }\r\n }\r\n }\r\n\r\n @if (showThemeSwitcher()) {\r\n <div class=\"dropdown d-inline-block z-index\" [class.show]=\"themeMenuOpen()\">\r\n <div class=\"h-100 d-flex align-items-center\">\r\n <button class=\"notification-btn option p-0 m-0 me-2\" (click)=\"toggleThemeMenu($event)\"\r\n [title]=\"'Tema: ' + themeLabel()\">\r\n <i class=\"fas text-white\" [ngClass]=\"themeIcon()\"></i>\r\n </button>\r\n </div>\r\n\r\n <div class=\"dropdown-menu dropdown-menu-end mtp-9 mb-0 py-0 custom-dropdown\" [class.show]=\"themeMenuOpen()\">\r\n @for (item of themes(); track item.id) {\r\n <button class=\"dropdown-item py-2 fs-7 fw-4 border-bottom-radius divider-border\"\r\n (click)=\"selectTheme(item.id)\">\r\n <i class=\"fas me-1\" [ngClass]=\"item.icon\"></i> {{ item.label }}\r\n @if (currentTheme() === item.id) {\r\n <i class=\"fas fa-check ms-auto text-success\"></i>\r\n }\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Notificaciones y soporte: \u00EDcono/estilo est\u00E1ndar, siempre visibles tambi\u00E9n en mobile -->\r\n @if (showNotifications()) {\r\n <div class=\"h-100 d-flex align-items-center\">\r\n <button class=\"notification-btn option p-0 m-0 me-2\" title=\"Notificaciones\"\r\n (click)=\"notificationsClick.emit()\">\r\n <i class=\"fas fa-bell text-white\"></i>\r\n @if (notificationsCount() > 0) {\r\n <span class=\"notification-badge\" [ngClass]=\"produccion() ? 'bg-danger' : 'bg-info'\">\r\n {{ notificationsCount() }}\r\n </span>\r\n }\r\n </button>\r\n </div>\r\n }\r\n\r\n @if (showChatbotToggle()) {\r\n <div class=\"h-100 d-flex align-items-center\">\r\n <button class=\"notification-btn option p-0 m-0 me-2\"\r\n [title]=\"chatbotVisible() ? 'Ocultar soporte' : 'Mostrar soporte'\"\r\n (click)=\"chatbotVisibleChange.emit(!chatbotVisible())\">\r\n <i class=\"fas fa-robot text-white\"></i>\r\n </button>\r\n </div>\r\n }\r\n\r\n <div class=\"dropdown d-inline-block z-index\" [class.show]=\"userMenuOpen()\">\r\n <div class=\"h-100 d-flex align-items-center\">\r\n <button class=\"border-0 bg-none\" (click)=\"toggleUserMenu($event)\">\r\n <img class=\"circle-icon shadow-sm\" [src]=\"avatarSrc()\" (error)=\"avatarFallido.set(userAvatar())\" alt=\"\">\r\n </button>\r\n </div>\r\n <div class=\"dropdown-menu dropdown-menu-end mtp-9 mb-0 py-0\" [class.show]=\"userMenuOpen()\">\r\n <div\r\n class=\"d-flex no-block align-items-center p-3 divider-border bg-gray-user-data border-top-radius\">\r\n <div class=\"d-flex align-items-center\">\r\n <img class=\"circle-icon shadow-sm\" [src]=\"avatarSrc()\" (error)=\"avatarFallido.set(userAvatar())\" alt=\"\">\r\n <div class=\"ms-2\">\r\n <p class=\"fs-7 my-0 py-0 fw-6\">{{userName()}}</p>\r\n @if (userName().startsWith('DESARROLLADOR')) {\r\n <span class=\"fs-7 fw-bold\">Desarrollador</span>\r\n } @else if (userName().startsWith('SOPORTE')) {\r\n <span class=\"fs-7 fw-bold\">Soporte</span>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n @if (showPanelControl()) {\r\n <button class=\"dropdown-item py-3 fs-7 fw-4\" (click)=\"userMenuOpen.set(false); panelControlClick.emit()\">\r\n Panel de control\r\n </button>\r\n }\r\n <button class=\"dropdown-item py-3 fs-7 fw-4 border-bottom-radius\" (click)=\"userMenuOpen.set(false); logout.emit()\"><i\r\n class=\"fas fa-power-off\"></i> Cerrar sesi\u00F3n</button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n@if (tooltip()?.text) {\r\n<div class=\"acceso-tooltip-fixed\" [style.left.px]=\"tooltip()!.x\" [style.top.px]=\"tooltip()!.y\"\r\n [style.borderLeftColor]=\"accentColor()\" [style.--tooltip-accent]=\"accentColor()\">\r\n <span class=\"tooltip-label\">{{ tooltip()!.text.split(': ')[0] }}:</span>\r\n <span class=\"tooltip-value\" [style.color]=\"accentColor()\">{{ tooltip()!.text.split(': ')[1] }}</span>\r\n</div>\r\n}\r\n", styles: [".header-container{height:50px;display:flex;align-items:center;transition:position .5s ease-in-out,background-color .2s ease;padding:0;margin:0;position:relative;--header-accent: #398bf7;background:var(--header-accent);border-bottom:2px solid transparent}.header-container.dev{--header-accent: rgb(197, 0, 72)}.header-container.prod{--header-accent: #398bf7}.header-container.qa{--header-accent: #20c997}:host-context(.dark-mode) .header-container,:host-context(.sky-blue-mode) .header-container,:host-context(.high-contrast-green-mode) .header-container,:host-context(.purple-mode) .header-container{background:var(--header-bg);border-bottom-color:var(--header-accent)}.first-header{display:flex;align-items:center;gap:6px}.empresa-nombre{max-width:350px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis;white-space:normal;line-height:1.25;max-height:2.5em;max-width:100%}@media(max-width:575px){.empresa-nombre{display:none!important}.first-header{min-width:auto!important;padding:10px!important}}@media(min-width:576px)and (max-width:1180px){.first-header{padding:10px!important}}@media(min-width:1181px){.first-header{min-width:260px;width:260px;padding:15px}}.bg-gray-user-data{background:var(--background-btn-light)}.dropdown-menu{transform-origin:top;background-color:var(--background-card);border-color:var(--border-color-modal);color:var(--text-color)}.dropdown-menu-end{right:0!important;left:auto!important}.dropdown-item{color:var(--text-color)}.dropdown-item:hover,.dropdown-item:focus{background-color:var(--bs-table-hover-bg);color:var(--text-color)}.dropdown-menu.show{animation:dropdownMenuIn .24s cubic-bezier(.2,0,.2,1) both}@keyframes dropdownMenuIn{0%{opacity:0;transform:translateY(10px) scale(.9)}to{opacity:1;transform:translateY(0) scale(1)}}.dropdown-menu{width:300px!important;min-width:300px!important}.circle-icon{min-height:30px;min-width:30px;height:30px;width:30px;border-radius:100%;border:2px solid white}.none-style{background:none;border:none}.sidebar-toggle-btn{display:flex;align-items:center;gap:4px}.sidebar-toggle-btn i{transition:transform .25s cubic-bezier(.4,0,.2,1)}.sidebar-toggle-btn i.flipped{transform:scaleX(-1)}.sidebar-toggle-btn .toggle-arrow{font-size:9px;color:#fff}.z-index{z-index:1001}.option{position:relative;border-radius:50px;width:33px;height:100%;transition:background-color .3s ease}.option:hover,.dropdown.show .option{background-color:var(--bs-table-hover-bg)!important}.custom-dropdown.dropdown-menu{min-width:unset!important;width:auto!important}.acceso-tooltip-fixed{position:fixed;z-index:99999;transform:translateY(-50%);pointer-events:none;padding:4px 10px 4px 8px;border-radius:6px;font-family:monospace;white-space:nowrap;background:#0f0f14;border:1px solid rgba(255,255,255,.07);border-left:2px solid;display:flex;align-items:center;gap:5px;animation:tooltipFadeIn .14s cubic-bezier(.25,.46,.45,.94)}.acceso-tooltip-fixed:before{content:\"\";position:absolute;right:100%;top:50%;transform:translateY(-50%);border:4px solid transparent;border-right-color:var(--tooltip-accent, #c50048)}.acceso-tooltip-fixed .tooltip-label{font-size:10px;font-weight:400;color:#fff;letter-spacing:.3px}.acceso-tooltip-fixed .tooltip-value{font-size:11px;font-weight:700}@keyframes tooltipFadeIn{0%{opacity:0;transform:translateY(-50%) translate(-4px)}to{opacity:1;transform:translateY(-50%) translate(0)}}.btn-print{border:1px solid rgba(0,0,0,.5764705882)!important;width:33px;height:30px!important;border-radius:6px}.btn-print:hover{border:1px solid rgba(61,61,61,.5254901961)!important;background-color:#dadadaf8!important}.btn-search{width:40px;height:31px!important;border-radius:6px}.logo-btn{width:25px;height:25px;object-fit:contain}.btn-deudas{background:#fff;border-radius:20px;border:none;white-space:nowrap;flex-shrink:0}.btn-deudas.titilando{animation:bordeTitilante 1s infinite alternate}@keyframes bordeTitilante{0%{box-shadow:0 0 2px red}50%{box-shadow:0 0 5px red,0 0 5px #ff4d4d}to{box-shadow:0 0 2px red}}@media(max-width:1100px){.btn-deudas{width:31px;height:31px!important;border-radius:50px;padding:0;display:flex;align-items:center;justify-content:center;aspect-ratio:1/1}.btn-deudas .texto-deudas{display:none}.btn-deudas .logo-btn{width:20px;height:20px;margin:0}}@keyframes icon-rotate-pop{0%{transform:rotate(0) scale(1)}25%{transform:rotate(10deg) scale(1.05)}50%{transform:rotate(-10deg) scale(1.1)}75%{transform:rotate(10deg) scale(1.05)}to{transform:rotate(0) scale(1)}}.button-icon-hover{display:inline-block}.button-tilt-hover:hover .button-icon-hover{animation:icon-rotate-pop 1s ease-in-out infinite}.desktop-only{display:flex}@media(max-width:667px){.desktop-only{display:none!important}}.notification-btn{position:relative;border:none;background:none;padding:0;margin-right:8px;cursor:pointer;width:33px;height:32px;display:flex;align-items:center;justify-content:center}.notification-btn i{color:#fff}.notification-badge{position:absolute;top:-5px;right:-3px;color:#fff;font-size:11px;font-weight:600;border-radius:4px;min-width:18px;height:18px;display:flex;align-items:center;justify-content:center;padding:0 4px}.header-side{flex:0 1 auto;min-width:0}.header-side--end{justify-content:flex-end}.global-search-trigger{position:absolute;left:50%;transform:translate(-50%);z-index:2;align-self:center;flex-shrink:0;margin:0;gap:8px;height:31px;width:280px;padding:0 6px 0 12px;background:#ffffff26;border:1px solid rgba(255,255,255,.3);border-radius:6px;color:#ffffffe6;font-size:12px;cursor:pointer;white-space:nowrap;transition:background .15s,border-color .15s,color .15s}.global-search-trigger:hover,.global-search-trigger:focus-visible{background:#ffffff47;border-color:#ffffff80;color:#fff}.global-search-trigger .global-search-icon{font-size:12px;color:#ffffffbf}.global-search-trigger .global-search-placeholder{flex-grow:1;text-align:start}.global-search-trigger .global-search-kbd{font-size:10px;font-weight:600;line-height:1;padding:3px 5px;border-radius:4px;background:#fff3;border:1px solid rgba(255,255,255,.35);color:#fff}.global-search-trigger--collapsed{display:none!important}.global-search-compact-pill{display:flex;align-items:center;justify-content:center;height:31px;margin-right:8px;padding:0 9px;background:#ffffff26;border:1px solid rgba(255,255,255,.3);border-radius:6px;color:#ffffffe6;font-size:11px;font-weight:700;cursor:pointer;transition:background .15s,border-color .15s,color .15s}.global-search-compact-pill:hover,.global-search-compact-pill:focus-visible{background:#ffffff47;border-color:#ffffff80;color:#fff}.tabs-header-btn{display:flex;align-items:center;gap:4px;background:#ffffff1a;border:1px solid rgba(255,255,255,.15);color:#fff9;border-radius:20px;padding:3px 8px;font-size:11px;cursor:pointer;transition:background .15s,color .15s;white-space:nowrap}.tabs-header-btn:hover{background:#fff3;color:#fff}.tabs-header-btn--active{background:#ffffff2e;color:#fff;border-color:#ffffff4d}.tabs-header-btn i{font-size:12px}.tabs-header-count{font-weight:700;font-size:11px;background:#fff3;border-radius:10px;padding:0 5px;min-width:16px;text-align:center}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
801
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.25", type: VerticalHeaderComponent, isStandalone: true, selector: "app-vertical-header", inputs: { actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, showSearch: { classPropertyName: "showSearch", publicName: "showSearch", isSignal: true, isRequired: false, transformFunction: null }, themes: { classPropertyName: "themes", publicName: "themes", isSignal: true, isRequired: false, transformFunction: null }, showThemeSwitcher: { classPropertyName: "showThemeSwitcher", publicName: "showThemeSwitcher", isSignal: true, isRequired: false, transformFunction: null }, showTabs: { classPropertyName: "showTabs", publicName: "showTabs", isSignal: true, isRequired: false, transformFunction: null }, showNotifications: { classPropertyName: "showNotifications", publicName: "showNotifications", isSignal: true, isRequired: false, transformFunction: null }, notificationsCount: { classPropertyName: "notificationsCount", publicName: "notificationsCount", isSignal: true, isRequired: false, transformFunction: null }, notificationsInBottomNav: { classPropertyName: "notificationsInBottomNav", publicName: "notificationsInBottomNav", isSignal: true, isRequired: false, transformFunction: null }, showChatbotToggle: { classPropertyName: "showChatbotToggle", publicName: "showChatbotToggle", isSignal: true, isRequired: false, transformFunction: null }, chatbotVisible: { classPropertyName: "chatbotVisible", publicName: "chatbotVisible", isSignal: true, isRequired: false, transformFunction: null }, chatbotInBottomNav: { classPropertyName: "chatbotInBottomNav", publicName: "chatbotInBottomNav", isSignal: true, isRequired: false, transformFunction: null }, currentTheme: { classPropertyName: "currentTheme", publicName: "currentTheme", isSignal: true, isRequired: false, transformFunction: null }, versionApp: { classPropertyName: "versionApp", publicName: "versionApp", isSignal: true, isRequired: false, transformFunction: null }, accentColor: { classPropertyName: "accentColor", publicName: "accentColor", isSignal: true, isRequired: false, transformFunction: null }, envClass: { classPropertyName: "envClass", publicName: "envClass", isSignal: true, isRequired: false, transformFunction: null }, produccion: { classPropertyName: "produccion", publicName: "produccion", isSignal: true, isRequired: false, transformFunction: null }, isScrolled: { classPropertyName: "isScrolled", publicName: "isScrolled", isSignal: true, isRequired: false, transformFunction: null }, isSidebarOpen: { classPropertyName: "isSidebarOpen", publicName: "isSidebarOpen", isSignal: true, isRequired: false, transformFunction: null }, nameProyecto: { classPropertyName: "nameProyecto", publicName: "nameProyecto", isSignal: true, isRequired: false, transformFunction: null }, showPanelControl: { classPropertyName: "showPanelControl", publicName: "showPanelControl", isSignal: true, isRequired: false, transformFunction: null }, userName: { classPropertyName: "userName", publicName: "userName", isSignal: true, isRequired: false, transformFunction: null }, empresaName: { classPropertyName: "empresaName", publicName: "empresaName", isSignal: true, isRequired: false, transformFunction: null }, userAvatar: { classPropertyName: "userAvatar", publicName: "userAvatar", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { notificationsClick: "notificationsClick", chatbotVisibleChange: "chatbotVisibleChange", themeChange: "themeChange", emitirToogle: "emitirToogle", searchComponents: "searchComponents", panelControlClick: "panelControlClick", logout: "logout" }, host: { listeners: { "document:click": "onDocumentClick()" } }, viewQueries: [{ propertyName: "headerContainerRef", first: true, predicate: ["headerContainer"], descendants: true }, { propertyName: "headerSideStartRef", first: true, predicate: ["headerSideStart"], descendants: true }, { propertyName: "headerSideEndRef", first: true, predicate: ["headerSideEnd"], descendants: true }, { propertyName: "compactSearchWrapperRef", first: true, predicate: ["compactSearchWrapper"], descendants: true }, { propertyName: "accesoTooltipRef", first: true, predicate: ["accesoTooltip"], descendants: true }], ngImport: i0, template: "<div #headerContainer class=\"d-flex header-container\" [ngClass]=\"envClass()\">\r\n <div class=\"first-header d-none d-md-flex\">\r\n <img src=\"assets/images/logo-tsi-tres.png\" width=\"40px\" height=\"40px\" style=\"cursor:pointer\"\r\n (click)=\"goToHome()\" title=\"Ir a Home\">\r\n <span class=\"text-white empresa-nombre ps-2\" [title]=\"versionApp()\">{{empresaName()}}</span>\r\n </div>\r\n\r\n <div class=\"d-flex w-100 h-100 justify-content-between align-items-center pe-3 ps-0\">\r\n <div #headerSideStart class=\"d-flex align-items-center header-side\">\r\n <button class=\"btn text-white none-style sidebar-toggle-btn\" (click)=\"emitirToogle.emit()\">\r\n <i class=\"fa-solid fa-table-columns\" [class.flipped]=\"isSidebarOpen()\"></i>\r\n <i class=\"fa-solid fa-chevron-left toggle-arrow\" [class.flipped]=\"isSidebarOpen()\"></i>\r\n </button>\r\n <span class=\"text-white empresa-nombre d-inline d-md-none ps-2\">{{empresaName()}}</span>\r\n <span class=\"text-white fs-5 fw-normal d-none d-md-inline ps-2 text-nowrap\">{{nameProyecto()}}</span>\r\n @if (showTabs() && tabsService.tabs().length) {\r\n <button class=\"tabs-header-btn mx-2 d-none d-md-flex\"\r\n [class.tabs-header-btn--active]=\"tabsService.visible()\" (click)=\"tabsService.toggleVisible()\"\r\n [title]=\"tabsService.visible() ? 'Ocultar acceso r\u00E1pido' : 'Mostrar acceso r\u00E1pido'\">\r\n <i class=\"fas fa-layer-group\"></i>\r\n @if (!tabsService.visible()) {\r\n <span class=\"tabs-header-count\">{{ tabsService.tabs().length }}</span>\r\n }\r\n </button>\r\n }\r\n </div>\r\n\r\n @if (showSearch()) {\r\n <button class=\"global-search-trigger d-none d-md-flex align-items-center\"\r\n [class.global-search-trigger--collapsed]=\"searchCollapsed()\" title=\"B\u00FAsqueda de p\u00E1ginas (F2)\"\r\n (click)=\"searchComponents.emit()\">\r\n <i class=\"fas fa-search global-search-icon\"></i>\r\n <span class=\"global-search-placeholder\">Buscar p\u00E1ginas...</span>\r\n <kbd class=\"global-search-kbd\">F2</kbd>\r\n </button>\r\n }\r\n\r\n <div #headerSideEnd class=\"d-flex align-items-center header-side header-side--end\">\r\n\r\n <!-- Fallback del buscador centrado cuando no entra (colisi\u00F3n) o en mobile\r\n (el buscador centrado se oculta por completo por debajo de md): la\r\n p\u00EDldora de texto \"F2\", igual que antes. -->\r\n <div #compactSearchWrapper class=\"h-100 d-flex align-items-center\">\r\n @if (showSearch() && searchCollapsed()) {\r\n <button class=\"global-search-compact-pill\" title=\"Buscar p\u00E1ginas (F2)\"\r\n (click)=\"searchComponents.emit()\">F2</button>\r\n }\r\n </div>\r\n\r\n <div class=\"d-flex align-items-center\">\r\n <ng-content select=\"[extra-actions]\" />\r\n\r\n <!-- Acciones adicionales, definidas por el consumidor: siempre a la izquierda\r\n de los tres fijos (tema, notificaciones, soporte). Solo se ocultan en mobile\r\n las que adem\u00E1s se reflejan en el bottom-nav (showInBottomNav) \u2014 si una acci\u00F3n\r\n no baja al bottom-nav, se queda visible ac\u00E1 para no desaparecer del todo. -->\r\n @for (action of actions(); track action.label) {\r\n @if (!action.hidden) {\r\n @if (action.items?.length) {\r\n <div class=\"dropdown d-inline-block z-index\" [class.show]=\"openActionKey() === action.label\" [class.desktop-only]=\"action.showInBottomNav\">\r\n <div class=\"h-100 d-flex align-items-center\">\r\n <button class=\"notification-btn option p-0 m-0 me-2\" [title]=\"action.label\" (click)=\"action.accion?.(); toggleActionMenu($event, action)\">\r\n <i class=\"fas text-white\" [ngClass]=\"action.icon\"></i>\r\n </button>\r\n </div>\r\n\r\n <div class=\"dropdown-menu dropdown-menu-end mtp-9 mb-0 py-0 custom-dropdown\" [class.show]=\"openActionKey() === action.label\">\r\n @for (opcion of action.items; track opcion.label; let last = $last) {\r\n <button class=\"dropdown-item py-2 fs-7 fw-4 d-flex align-items-center\"\r\n [ngClass]=\"{'divider-border': !last,'border-top-radius': $index == 0,'border-bottom-radius': last}\"\r\n (click)=\"runAction(opcion)\"\r\n (mouseenter)=\"showTooltip($event, opcion.title ?? '')\" (mouseleave)=\"hideTooltip()\">\r\n <i [class]=\"opcion.icon\" class=\"me-1\"></i> {{ opcion.label }}\r\n @if (opcion.value) {\r\n <span class=\"ms-auto ps-3 fw-6\">{{ opcion.value }}</span>\r\n }\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"h-100 d-flex align-items-center\" [class.desktop-only]=\"action.showInBottomNav\">\r\n <button class=\"notification-btn option p-0 m-0 me-2\" [title]=\"action.label\" (click)=\"action.accion?.()\">\r\n <i class=\"fas text-white\" [ngClass]=\"action.icon\"></i>\r\n @if (action.badge) {\r\n <span class=\"notification-badge\" [ngClass]=\"produccion() ? 'bg-danger' : 'bg-info'\">\r\n {{ action.badge }}\r\n </span>\r\n }\r\n </button>\r\n </div>\r\n }\r\n }\r\n }\r\n\r\n @if (showThemeSwitcher()) {\r\n <div class=\"dropdown d-inline-block z-index\" [class.show]=\"themeMenuOpen()\">\r\n <div class=\"h-100 d-flex align-items-center\">\r\n <button class=\"notification-btn option p-0 m-0 me-2\" (click)=\"toggleThemeMenu($event)\"\r\n [title]=\"'Tema: ' + themeLabel()\">\r\n <i class=\"fas text-white\" [ngClass]=\"themeIcon()\"></i>\r\n </button>\r\n </div>\r\n\r\n <div class=\"dropdown-menu dropdown-menu-end mtp-9 mb-0 py-0 custom-dropdown\" [class.show]=\"themeMenuOpen()\">\r\n @for (item of themes(); track item.id) {\r\n <button class=\"dropdown-item py-2 fs-7 fw-4 border-bottom-radius divider-border\"\r\n (click)=\"selectTheme(item.id)\">\r\n <i class=\"fas me-1\" [ngClass]=\"item.icon\"></i> {{ item.label }}\r\n @if (currentTheme() === item.id) {\r\n <i class=\"fas fa-check ms-auto text-success\"></i>\r\n }\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Notificaciones y soporte: \u00EDcono/estilo est\u00E1ndar, siempre visibles tambi\u00E9n en mobile -->\r\n @if (showNotifications()) {\r\n <div class=\"h-100 d-flex align-items-center\">\r\n <button class=\"notification-btn option p-0 m-0 me-2\" title=\"Notificaciones\"\r\n (click)=\"notificationsClick.emit()\">\r\n <i class=\"fas fa-bell text-white\"></i>\r\n @if (notificationsCount() > 0) {\r\n <span class=\"notification-badge\" [ngClass]=\"produccion() ? 'bg-danger' : 'bg-info'\">\r\n {{ notificationsCount() }}\r\n </span>\r\n }\r\n </button>\r\n </div>\r\n }\r\n\r\n @if (showChatbotToggle()) {\r\n <div class=\"h-100 d-flex align-items-center\">\r\n <button class=\"notification-btn option p-0 m-0 me-2\"\r\n [title]=\"chatbotVisible() ? 'Ocultar soporte' : 'Mostrar soporte'\"\r\n (click)=\"chatbotVisibleChange.emit(!chatbotVisible())\">\r\n <i class=\"fas fa-robot text-white\"></i>\r\n </button>\r\n </div>\r\n }\r\n\r\n <div class=\"dropdown d-inline-block z-index\" [class.show]=\"userMenuOpen()\">\r\n <div class=\"h-100 d-flex align-items-center\">\r\n <button class=\"border-0 bg-none\" (click)=\"toggleUserMenu($event)\">\r\n <img class=\"circle-icon shadow-sm\" [src]=\"avatarSrc()\" (error)=\"avatarFallido.set(userAvatar())\" alt=\"\">\r\n </button>\r\n </div>\r\n <div class=\"dropdown-menu dropdown-menu-end mtp-9 mb-0 py-0\" [class.show]=\"userMenuOpen()\">\r\n <div\r\n class=\"d-flex no-block align-items-center p-3 divider-border bg-gray-user-data border-top-radius\">\r\n <div class=\"d-flex align-items-center\">\r\n <img class=\"circle-icon shadow-sm\" [src]=\"avatarSrc()\" (error)=\"avatarFallido.set(userAvatar())\" alt=\"\">\r\n <div class=\"ms-2\">\r\n <p class=\"fs-7 my-0 py-0 fw-6\">{{userName()}}</p>\r\n @if (userName().startsWith('DESARROLLADOR')) {\r\n <span class=\"fs-7 fw-bold\">Desarrollador</span>\r\n } @else if (userName().startsWith('SOPORTE')) {\r\n <span class=\"fs-7 fw-bold\">Soporte</span>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n @if (showPanelControl()) {\r\n <button class=\"dropdown-item py-3 fs-7 fw-4\" (click)=\"userMenuOpen.set(false); panelControlClick.emit()\">\r\n Panel de control\r\n </button>\r\n }\r\n <button class=\"dropdown-item py-3 fs-7 fw-4 border-bottom-radius\" (click)=\"userMenuOpen.set(false); logout.emit()\"><i\r\n class=\"fas fa-power-off\"></i> Cerrar sesi\u00F3n</button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n@if (tooltip()?.text) {\r\n<div #accesoTooltip class=\"acceso-tooltip-fixed\" [class.acceso-tooltip-fixed--left]=\"tooltip()!.side === 'left'\"\r\n [class.acceso-tooltip-fixed--wrap]=\"tooltip()!.maxWidth != null\"\r\n [style.left.px]=\"tooltip()!.left\" [style.right.px]=\"tooltip()!.right\" [style.top.px]=\"tooltip()!.y\"\r\n [style.maxWidth.px]=\"tooltip()!.maxWidth\" [style.--tooltip-accent]=\"accentColor()\">\r\n <span class=\"tooltip-label\">{{ tooltip()!.text.split(': ')[0] }}:</span>\r\n <span class=\"tooltip-value\" [style.color]=\"accentColor()\">{{ tooltip()!.text.split(': ')[1] }}</span>\r\n</div>\r\n}\r\n", styles: [".header-container{height:50px;display:flex;align-items:center;transition:position .5s ease-in-out,background-color .2s ease;padding:0;margin:0;position:relative;--header-accent: #398bf7;background:var(--header-accent);border-bottom:2px solid transparent}.header-container.dev{--header-accent: rgb(197, 0, 72)}.header-container.prod{--header-accent: #398bf7}.header-container.qa{--header-accent: #20c997}:host-context(.dark-mode) .header-container,:host-context(.sky-blue-mode) .header-container,:host-context(.high-contrast-green-mode) .header-container,:host-context(.purple-mode) .header-container{background:var(--header-bg);border-bottom-color:var(--header-accent)}.first-header{display:flex;align-items:center;gap:6px}.empresa-nombre{max-width:350px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis;white-space:normal;line-height:1.25;max-height:2.5em;max-width:100%}@media(max-width:575px){.empresa-nombre{display:none!important}.first-header{min-width:auto!important;padding:10px!important}}@media(min-width:576px)and (max-width:1180px){.first-header{padding:10px!important}}@media(min-width:1181px){.first-header{min-width:260px;width:260px;padding:15px}}.bg-gray-user-data{background:var(--background-btn-light)}.dropdown-menu{transform-origin:top;background-color:var(--background-card);border-color:var(--border-color-modal);color:var(--text-color)}.dropdown-menu-end{right:0!important;left:auto!important}.dropdown-item{color:var(--text-color)}.dropdown-item:hover,.dropdown-item:focus{background-color:var(--bs-table-hover-bg);color:var(--text-color)}.dropdown-menu.show{animation:dropdownMenuIn .24s cubic-bezier(.2,0,.2,1) both}@keyframes dropdownMenuIn{0%{opacity:0;transform:translateY(10px) scale(.9)}to{opacity:1;transform:translateY(0) scale(1)}}.dropdown-menu{width:300px!important;min-width:300px!important}.circle-icon{min-height:30px;min-width:30px;height:30px;width:30px;border-radius:100%;border:2px solid white}.none-style{background:none;border:none}.sidebar-toggle-btn{display:flex;align-items:center;gap:4px}.sidebar-toggle-btn i{transition:transform .25s cubic-bezier(.4,0,.2,1)}.sidebar-toggle-btn i.flipped{transform:scaleX(-1)}.sidebar-toggle-btn .toggle-arrow{font-size:9px;color:#fff}.z-index{z-index:1001}.option{position:relative;border-radius:50px;width:33px;height:100%;transition:background-color .3s ease}.option:hover,.dropdown.show .option{background-color:var(--bs-table-hover-bg)!important}.custom-dropdown.dropdown-menu{min-width:unset!important;width:auto!important}.acceso-tooltip-fixed{position:fixed;z-index:99999;transform:translateY(-50%);pointer-events:none;padding:4px 10px 4px 8px;border-radius:6px;font-family:monospace;white-space:nowrap;background:#0f0f14;border:1px solid rgba(255,255,255,.07);border-left:2px solid var(--tooltip-accent, #c50048);display:flex;align-items:center;gap:5px;animation:tooltipFadeIn .14s cubic-bezier(.25,.46,.45,.94)}.acceso-tooltip-fixed:before{content:\"\";position:absolute;right:100%;top:50%;transform:translateY(-50%);border:4px solid transparent;border-right-color:var(--tooltip-accent, #c50048)}.acceso-tooltip-fixed .tooltip-label{font-size:10px;font-weight:400;color:#fff;letter-spacing:.3px}.acceso-tooltip-fixed .tooltip-value{font-size:11px;font-weight:700}.acceso-tooltip-fixed--left{border-left:1px solid rgba(255,255,255,.07);border-right:2px solid var(--tooltip-accent, #c50048);padding:4px 8px 4px 10px;animation-name:tooltipFadeInLeft}.acceso-tooltip-fixed--left:before{right:auto;left:100%;border-right-color:transparent;border-left-color:var(--tooltip-accent, #c50048)}.acceso-tooltip-fixed--wrap{white-space:normal;flex-wrap:wrap}.acceso-tooltip-fixed--wrap .tooltip-value{min-width:0;overflow-wrap:anywhere}@keyframes tooltipFadeInLeft{0%{opacity:0;transform:translateY(-50%) translate(4px)}to{opacity:1;transform:translateY(-50%) translate(0)}}@keyframes tooltipFadeIn{0%{opacity:0;transform:translateY(-50%) translate(-4px)}to{opacity:1;transform:translateY(-50%) translate(0)}}.btn-print{border:1px solid rgba(0,0,0,.5764705882)!important;width:33px;height:30px!important;border-radius:6px}.btn-print:hover{border:1px solid rgba(61,61,61,.5254901961)!important;background-color:#dadadaf8!important}.btn-search{width:40px;height:31px!important;border-radius:6px}.logo-btn{width:25px;height:25px;object-fit:contain}.btn-deudas{background:#fff;border-radius:20px;border:none;white-space:nowrap;flex-shrink:0}.btn-deudas.titilando{animation:bordeTitilante 1s infinite alternate}@keyframes bordeTitilante{0%{box-shadow:0 0 2px red}50%{box-shadow:0 0 5px red,0 0 5px #ff4d4d}to{box-shadow:0 0 2px red}}@media(max-width:1100px){.btn-deudas{width:31px;height:31px!important;border-radius:50px;padding:0;display:flex;align-items:center;justify-content:center;aspect-ratio:1/1}.btn-deudas .texto-deudas{display:none}.btn-deudas .logo-btn{width:20px;height:20px;margin:0}}@keyframes icon-rotate-pop{0%{transform:rotate(0) scale(1)}25%{transform:rotate(10deg) scale(1.05)}50%{transform:rotate(-10deg) scale(1.1)}75%{transform:rotate(10deg) scale(1.05)}to{transform:rotate(0) scale(1)}}.button-icon-hover{display:inline-block}.button-tilt-hover:hover .button-icon-hover{animation:icon-rotate-pop 1s ease-in-out infinite}.desktop-only{display:flex}@media(max-width:667px){.desktop-only{display:none!important}}.notification-btn{position:relative;border:none;background:none;padding:0;margin-right:8px;cursor:pointer;width:33px;height:32px;display:flex;align-items:center;justify-content:center}.notification-btn i{color:#fff}.notification-badge{position:absolute;top:-5px;right:-3px;color:#fff;font-size:11px;font-weight:600;border-radius:4px;min-width:18px;height:18px;display:flex;align-items:center;justify-content:center;padding:0 4px}.header-side{flex:0 1 auto;min-width:0}.header-side--end{justify-content:flex-end}.global-search-trigger{position:absolute;left:50%;transform:translate(-50%);z-index:2;align-self:center;flex-shrink:0;margin:0;gap:8px;height:31px;width:280px;padding:0 6px 0 12px;background:#ffffff26;border:1px solid rgba(255,255,255,.3);border-radius:6px;color:#ffffffe6;font-size:12px;cursor:pointer;white-space:nowrap;transition:background .15s,border-color .15s,color .15s}.global-search-trigger:hover,.global-search-trigger:focus-visible{background:#ffffff47;border-color:#ffffff80;color:#fff}.global-search-trigger .global-search-icon{font-size:12px;color:#ffffffbf}.global-search-trigger .global-search-placeholder{flex-grow:1;text-align:start}.global-search-trigger .global-search-kbd{font-size:10px;font-weight:600;line-height:1;padding:3px 5px;border-radius:4px;background:#fff3;border:1px solid rgba(255,255,255,.35);color:#fff}.global-search-trigger--collapsed{display:none!important}.global-search-compact-pill{display:flex;align-items:center;justify-content:center;height:31px;margin-right:8px;padding:0 9px;background:#ffffff26;border:1px solid rgba(255,255,255,.3);border-radius:6px;color:#ffffffe6;font-size:11px;font-weight:700;cursor:pointer;transition:background .15s,border-color .15s,color .15s}.global-search-compact-pill:hover,.global-search-compact-pill:focus-visible{background:#ffffff47;border-color:#ffffff80;color:#fff}.tabs-header-btn{display:flex;align-items:center;gap:4px;background:#ffffff1a;border:1px solid rgba(255,255,255,.15);color:#fff9;border-radius:20px;padding:3px 8px;font-size:11px;cursor:pointer;transition:background .15s,color .15s;white-space:nowrap}.tabs-header-btn:hover{background:#fff3;color:#fff}.tabs-header-btn--active{background:#ffffff2e;color:#fff;border-color:#ffffff4d}.tabs-header-btn i{font-size:12px}.tabs-header-count{font-weight:700;font-size:11px;background:#fff3;border-radius:10px;padding:0 5px;min-width:16px;text-align:center}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
774
802
  }
775
803
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: VerticalHeaderComponent, decorators: [{
776
804
  type: Component,
777
- args: [{ selector: 'app-vertical-header', imports: [CommonModule], template: "<div #headerContainer class=\"d-flex header-container\" [ngClass]=\"envClass()\">\r\n <div class=\"first-header d-none d-md-flex\">\r\n <img src=\"assets/images/logo-tsi-tres.png\" width=\"40px\" height=\"40px\" style=\"cursor:pointer\"\r\n (click)=\"goToHome()\" title=\"Ir a Home\">\r\n <span class=\"text-white empresa-nombre ps-2\" [title]=\"versionApp()\">{{empresaName()}}</span>\r\n </div>\r\n\r\n <div class=\"d-flex w-100 h-100 justify-content-between align-items-center pe-3 ps-0\">\r\n <div #headerSideStart class=\"d-flex align-items-center header-side\">\r\n <button class=\"btn text-white none-style sidebar-toggle-btn\" (click)=\"emitirToogle.emit()\">\r\n <i class=\"fa-solid fa-table-columns\" [class.flipped]=\"isSidebarOpen()\"></i>\r\n <i class=\"fa-solid fa-chevron-left toggle-arrow\" [class.flipped]=\"isSidebarOpen()\"></i>\r\n </button>\r\n <span class=\"text-white empresa-nombre d-inline d-md-none ps-2\">{{empresaName()}}</span>\r\n <span class=\"text-white fs-5 fw-normal d-none d-md-inline ps-2 text-nowrap\">{{nameProyecto()}}</span>\r\n @if (showTabs() && tabsService.tabs().length) {\r\n <button class=\"tabs-header-btn mx-2 d-none d-md-flex\"\r\n [class.tabs-header-btn--active]=\"tabsService.visible()\" (click)=\"tabsService.toggleVisible()\"\r\n [title]=\"tabsService.visible() ? 'Ocultar acceso r\u00E1pido' : 'Mostrar acceso r\u00E1pido'\">\r\n <i class=\"fas fa-layer-group\"></i>\r\n @if (!tabsService.visible()) {\r\n <span class=\"tabs-header-count\">{{ tabsService.tabs().length }}</span>\r\n }\r\n </button>\r\n }\r\n </div>\r\n\r\n @if (showSearch()) {\r\n <button class=\"global-search-trigger d-none d-md-flex align-items-center\"\r\n [class.global-search-trigger--collapsed]=\"searchCollapsed()\" title=\"B\u00FAsqueda de p\u00E1ginas (F2)\"\r\n (click)=\"searchComponents.emit()\">\r\n <i class=\"fas fa-search global-search-icon\"></i>\r\n <span class=\"global-search-placeholder\">Buscar p\u00E1ginas...</span>\r\n <kbd class=\"global-search-kbd\">F2</kbd>\r\n </button>\r\n }\r\n\r\n <div #headerSideEnd class=\"d-flex align-items-center header-side header-side--end\">\r\n\r\n <!-- Fallback del buscador centrado cuando no entra (colisi\u00F3n) o en mobile\r\n (el buscador centrado se oculta por completo por debajo de md): la\r\n p\u00EDldora de texto \"F2\", igual que antes. -->\r\n <div #compactSearchWrapper class=\"h-100 d-flex align-items-center\">\r\n @if (showSearch() && searchCollapsed()) {\r\n <button class=\"global-search-compact-pill\" title=\"Buscar p\u00E1ginas (F2)\"\r\n (click)=\"searchComponents.emit()\">F2</button>\r\n }\r\n </div>\r\n\r\n <div class=\"d-flex align-items-center\">\r\n <ng-content select=\"[extra-actions]\" />\r\n\r\n <!-- Acciones adicionales, definidas por el consumidor: siempre a la izquierda\r\n de los tres fijos (tema, notificaciones, soporte). Solo se ocultan en mobile\r\n las que adem\u00E1s se reflejan en el bottom-nav (showInBottomNav) \u2014 si una acci\u00F3n\r\n no baja al bottom-nav, se queda visible ac\u00E1 para no desaparecer del todo. -->\r\n @for (action of actions(); track action.label) {\r\n @if (!action.hidden) {\r\n @if (action.items?.length) {\r\n <div class=\"dropdown d-inline-block z-index\" [class.show]=\"openActionKey() === action.label\" [class.desktop-only]=\"action.showInBottomNav\">\r\n <div class=\"h-100 d-flex align-items-center\">\r\n <button class=\"notification-btn option p-0 m-0 me-2\" [title]=\"action.label\" (click)=\"action.accion?.(); toggleActionMenu($event, action)\">\r\n <i class=\"fas text-white\" [ngClass]=\"action.icon\"></i>\r\n </button>\r\n </div>\r\n\r\n <div class=\"dropdown-menu dropdown-menu-end mtp-9 mb-0 py-0 custom-dropdown\" [class.show]=\"openActionKey() === action.label\">\r\n @for (opcion of action.items; track opcion.label; let last = $last) {\r\n <button class=\"dropdown-item py-2 fs-7 fw-4 d-flex align-items-center\"\r\n [ngClass]=\"{'divider-border': !last,'border-top-radius': $index == 0,'border-bottom-radius': last}\"\r\n (click)=\"runAction(opcion)\"\r\n (mouseenter)=\"showTooltip($event, opcion.title ?? '')\" (mouseleave)=\"hideTooltip()\">\r\n <i [class]=\"opcion.icon\" class=\"me-1\"></i> {{ opcion.label }}\r\n @if (opcion.value) {\r\n <span class=\"ms-auto ps-3 fw-6\">{{ opcion.value }}</span>\r\n }\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"h-100 d-flex align-items-center\" [class.desktop-only]=\"action.showInBottomNav\">\r\n <button class=\"notification-btn option p-0 m-0 me-2\" [title]=\"action.label\" (click)=\"action.accion?.()\">\r\n <i class=\"fas text-white\" [ngClass]=\"action.icon\"></i>\r\n @if (action.badge) {\r\n <span class=\"notification-badge\" [ngClass]=\"produccion() ? 'bg-danger' : 'bg-info'\">\r\n {{ action.badge }}\r\n </span>\r\n }\r\n </button>\r\n </div>\r\n }\r\n }\r\n }\r\n\r\n @if (showThemeSwitcher()) {\r\n <div class=\"dropdown d-inline-block z-index\" [class.show]=\"themeMenuOpen()\">\r\n <div class=\"h-100 d-flex align-items-center\">\r\n <button class=\"notification-btn option p-0 m-0 me-2\" (click)=\"toggleThemeMenu($event)\"\r\n [title]=\"'Tema: ' + themeLabel()\">\r\n <i class=\"fas text-white\" [ngClass]=\"themeIcon()\"></i>\r\n </button>\r\n </div>\r\n\r\n <div class=\"dropdown-menu dropdown-menu-end mtp-9 mb-0 py-0 custom-dropdown\" [class.show]=\"themeMenuOpen()\">\r\n @for (item of themes(); track item.id) {\r\n <button class=\"dropdown-item py-2 fs-7 fw-4 border-bottom-radius divider-border\"\r\n (click)=\"selectTheme(item.id)\">\r\n <i class=\"fas me-1\" [ngClass]=\"item.icon\"></i> {{ item.label }}\r\n @if (currentTheme() === item.id) {\r\n <i class=\"fas fa-check ms-auto text-success\"></i>\r\n }\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Notificaciones y soporte: \u00EDcono/estilo est\u00E1ndar, siempre visibles tambi\u00E9n en mobile -->\r\n @if (showNotifications()) {\r\n <div class=\"h-100 d-flex align-items-center\">\r\n <button class=\"notification-btn option p-0 m-0 me-2\" title=\"Notificaciones\"\r\n (click)=\"notificationsClick.emit()\">\r\n <i class=\"fas fa-bell text-white\"></i>\r\n @if (notificationsCount() > 0) {\r\n <span class=\"notification-badge\" [ngClass]=\"produccion() ? 'bg-danger' : 'bg-info'\">\r\n {{ notificationsCount() }}\r\n </span>\r\n }\r\n </button>\r\n </div>\r\n }\r\n\r\n @if (showChatbotToggle()) {\r\n <div class=\"h-100 d-flex align-items-center\">\r\n <button class=\"notification-btn option p-0 m-0 me-2\"\r\n [title]=\"chatbotVisible() ? 'Ocultar soporte' : 'Mostrar soporte'\"\r\n (click)=\"chatbotVisibleChange.emit(!chatbotVisible())\">\r\n <i class=\"fas fa-robot text-white\"></i>\r\n </button>\r\n </div>\r\n }\r\n\r\n <div class=\"dropdown d-inline-block z-index\" [class.show]=\"userMenuOpen()\">\r\n <div class=\"h-100 d-flex align-items-center\">\r\n <button class=\"border-0 bg-none\" (click)=\"toggleUserMenu($event)\">\r\n <img class=\"circle-icon shadow-sm\" [src]=\"avatarSrc()\" (error)=\"avatarFallido.set(userAvatar())\" alt=\"\">\r\n </button>\r\n </div>\r\n <div class=\"dropdown-menu dropdown-menu-end mtp-9 mb-0 py-0\" [class.show]=\"userMenuOpen()\">\r\n <div\r\n class=\"d-flex no-block align-items-center p-3 divider-border bg-gray-user-data border-top-radius\">\r\n <div class=\"d-flex align-items-center\">\r\n <img class=\"circle-icon shadow-sm\" [src]=\"avatarSrc()\" (error)=\"avatarFallido.set(userAvatar())\" alt=\"\">\r\n <div class=\"ms-2\">\r\n <p class=\"fs-7 my-0 py-0 fw-6\">{{userName()}}</p>\r\n @if (userName().startsWith('DESARROLLADOR')) {\r\n <span class=\"fs-7 fw-bold\">Desarrollador</span>\r\n } @else if (userName().startsWith('SOPORTE')) {\r\n <span class=\"fs-7 fw-bold\">Soporte</span>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n @if (showPanelControl()) {\r\n <button class=\"dropdown-item py-3 fs-7 fw-4\" (click)=\"userMenuOpen.set(false); panelControlClick.emit()\">\r\n Panel de control\r\n </button>\r\n }\r\n <button class=\"dropdown-item py-3 fs-7 fw-4 border-bottom-radius\" (click)=\"userMenuOpen.set(false); logout.emit()\"><i\r\n class=\"fas fa-power-off\"></i> Cerrar sesi\u00F3n</button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n@if (tooltip()?.text) {\r\n<div class=\"acceso-tooltip-fixed\" [style.left.px]=\"tooltip()!.x\" [style.top.px]=\"tooltip()!.y\"\r\n [style.borderLeftColor]=\"accentColor()\" [style.--tooltip-accent]=\"accentColor()\">\r\n <span class=\"tooltip-label\">{{ tooltip()!.text.split(': ')[0] }}:</span>\r\n <span class=\"tooltip-value\" [style.color]=\"accentColor()\">{{ tooltip()!.text.split(': ')[1] }}</span>\r\n</div>\r\n}\r\n", styles: [".header-container{height:50px;display:flex;align-items:center;transition:position .5s ease-in-out,background-color .2s ease;padding:0;margin:0;position:relative;--header-accent: #398bf7;background:var(--header-accent);border-bottom:2px solid transparent}.header-container.dev{--header-accent: rgb(197, 0, 72)}.header-container.prod{--header-accent: #398bf7}.header-container.qa{--header-accent: #20c997}:host-context(.dark-mode) .header-container,:host-context(.sky-blue-mode) .header-container,:host-context(.high-contrast-green-mode) .header-container,:host-context(.purple-mode) .header-container{background:var(--header-bg);border-bottom-color:var(--header-accent)}.first-header{display:flex;align-items:center;gap:6px}.empresa-nombre{max-width:350px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis;white-space:normal;line-height:1.25;max-height:2.5em;max-width:100%}@media(max-width:575px){.empresa-nombre{display:none!important}.first-header{min-width:auto!important;padding:10px!important}}@media(min-width:576px)and (max-width:1180px){.first-header{padding:10px!important}}@media(min-width:1181px){.first-header{min-width:260px;width:260px;padding:15px}}.bg-gray-user-data{background:var(--background-btn-light)}.dropdown-menu{transform-origin:top;background-color:var(--background-card);border-color:var(--border-color-modal);color:var(--text-color)}.dropdown-menu-end{right:0!important;left:auto!important}.dropdown-item{color:var(--text-color)}.dropdown-item:hover,.dropdown-item:focus{background-color:var(--bs-table-hover-bg);color:var(--text-color)}.dropdown-menu.show{animation:dropdownMenuIn .24s cubic-bezier(.2,0,.2,1) both}@keyframes dropdownMenuIn{0%{opacity:0;transform:translateY(10px) scale(.9)}to{opacity:1;transform:translateY(0) scale(1)}}.dropdown-menu{width:300px!important;min-width:300px!important}.circle-icon{min-height:30px;min-width:30px;height:30px;width:30px;border-radius:100%;border:2px solid white}.none-style{background:none;border:none}.sidebar-toggle-btn{display:flex;align-items:center;gap:4px}.sidebar-toggle-btn i{transition:transform .25s cubic-bezier(.4,0,.2,1)}.sidebar-toggle-btn i.flipped{transform:scaleX(-1)}.sidebar-toggle-btn .toggle-arrow{font-size:9px;color:#fff}.z-index{z-index:1001}.option{position:relative;border-radius:50px;width:33px;height:100%;transition:background-color .3s ease}.option:hover,.dropdown.show .option{background-color:var(--bs-table-hover-bg)!important}.custom-dropdown.dropdown-menu{min-width:unset!important;width:auto!important}.acceso-tooltip-fixed{position:fixed;z-index:99999;transform:translateY(-50%);pointer-events:none;padding:4px 10px 4px 8px;border-radius:6px;font-family:monospace;white-space:nowrap;background:#0f0f14;border:1px solid rgba(255,255,255,.07);border-left:2px solid;display:flex;align-items:center;gap:5px;animation:tooltipFadeIn .14s cubic-bezier(.25,.46,.45,.94)}.acceso-tooltip-fixed:before{content:\"\";position:absolute;right:100%;top:50%;transform:translateY(-50%);border:4px solid transparent;border-right-color:var(--tooltip-accent, #c50048)}.acceso-tooltip-fixed .tooltip-label{font-size:10px;font-weight:400;color:#fff;letter-spacing:.3px}.acceso-tooltip-fixed .tooltip-value{font-size:11px;font-weight:700}@keyframes tooltipFadeIn{0%{opacity:0;transform:translateY(-50%) translate(-4px)}to{opacity:1;transform:translateY(-50%) translate(0)}}.btn-print{border:1px solid rgba(0,0,0,.5764705882)!important;width:33px;height:30px!important;border-radius:6px}.btn-print:hover{border:1px solid rgba(61,61,61,.5254901961)!important;background-color:#dadadaf8!important}.btn-search{width:40px;height:31px!important;border-radius:6px}.logo-btn{width:25px;height:25px;object-fit:contain}.btn-deudas{background:#fff;border-radius:20px;border:none;white-space:nowrap;flex-shrink:0}.btn-deudas.titilando{animation:bordeTitilante 1s infinite alternate}@keyframes bordeTitilante{0%{box-shadow:0 0 2px red}50%{box-shadow:0 0 5px red,0 0 5px #ff4d4d}to{box-shadow:0 0 2px red}}@media(max-width:1100px){.btn-deudas{width:31px;height:31px!important;border-radius:50px;padding:0;display:flex;align-items:center;justify-content:center;aspect-ratio:1/1}.btn-deudas .texto-deudas{display:none}.btn-deudas .logo-btn{width:20px;height:20px;margin:0}}@keyframes icon-rotate-pop{0%{transform:rotate(0) scale(1)}25%{transform:rotate(10deg) scale(1.05)}50%{transform:rotate(-10deg) scale(1.1)}75%{transform:rotate(10deg) scale(1.05)}to{transform:rotate(0) scale(1)}}.button-icon-hover{display:inline-block}.button-tilt-hover:hover .button-icon-hover{animation:icon-rotate-pop 1s ease-in-out infinite}.desktop-only{display:flex}@media(max-width:667px){.desktop-only{display:none!important}}.notification-btn{position:relative;border:none;background:none;padding:0;margin-right:8px;cursor:pointer;width:33px;height:32px;display:flex;align-items:center;justify-content:center}.notification-btn i{color:#fff}.notification-badge{position:absolute;top:-5px;right:-3px;color:#fff;font-size:11px;font-weight:600;border-radius:4px;min-width:18px;height:18px;display:flex;align-items:center;justify-content:center;padding:0 4px}.header-side{flex:0 1 auto;min-width:0}.header-side--end{justify-content:flex-end}.global-search-trigger{position:absolute;left:50%;transform:translate(-50%);z-index:2;align-self:center;flex-shrink:0;margin:0;gap:8px;height:31px;width:280px;padding:0 6px 0 12px;background:#ffffff26;border:1px solid rgba(255,255,255,.3);border-radius:6px;color:#ffffffe6;font-size:12px;cursor:pointer;white-space:nowrap;transition:background .15s,border-color .15s,color .15s}.global-search-trigger:hover,.global-search-trigger:focus-visible{background:#ffffff47;border-color:#ffffff80;color:#fff}.global-search-trigger .global-search-icon{font-size:12px;color:#ffffffbf}.global-search-trigger .global-search-placeholder{flex-grow:1;text-align:start}.global-search-trigger .global-search-kbd{font-size:10px;font-weight:600;line-height:1;padding:3px 5px;border-radius:4px;background:#fff3;border:1px solid rgba(255,255,255,.35);color:#fff}.global-search-trigger--collapsed{display:none!important}.global-search-compact-pill{display:flex;align-items:center;justify-content:center;height:31px;margin-right:8px;padding:0 9px;background:#ffffff26;border:1px solid rgba(255,255,255,.3);border-radius:6px;color:#ffffffe6;font-size:11px;font-weight:700;cursor:pointer;transition:background .15s,border-color .15s,color .15s}.global-search-compact-pill:hover,.global-search-compact-pill:focus-visible{background:#ffffff47;border-color:#ffffff80;color:#fff}.tabs-header-btn{display:flex;align-items:center;gap:4px;background:#ffffff1a;border:1px solid rgba(255,255,255,.15);color:#fff9;border-radius:20px;padding:3px 8px;font-size:11px;cursor:pointer;transition:background .15s,color .15s;white-space:nowrap}.tabs-header-btn:hover{background:#fff3;color:#fff}.tabs-header-btn--active{background:#ffffff2e;color:#fff;border-color:#ffffff4d}.tabs-header-btn i{font-size:12px}.tabs-header-count{font-weight:700;font-size:11px;background:#fff3;border-radius:10px;padding:0 5px;min-width:16px;text-align:center}\n"] }]
805
+ args: [{ selector: 'app-vertical-header', imports: [CommonModule], template: "<div #headerContainer class=\"d-flex header-container\" [ngClass]=\"envClass()\">\r\n <div class=\"first-header d-none d-md-flex\">\r\n <img src=\"assets/images/logo-tsi-tres.png\" width=\"40px\" height=\"40px\" style=\"cursor:pointer\"\r\n (click)=\"goToHome()\" title=\"Ir a Home\">\r\n <span class=\"text-white empresa-nombre ps-2\" [title]=\"versionApp()\">{{empresaName()}}</span>\r\n </div>\r\n\r\n <div class=\"d-flex w-100 h-100 justify-content-between align-items-center pe-3 ps-0\">\r\n <div #headerSideStart class=\"d-flex align-items-center header-side\">\r\n <button class=\"btn text-white none-style sidebar-toggle-btn\" (click)=\"emitirToogle.emit()\">\r\n <i class=\"fa-solid fa-table-columns\" [class.flipped]=\"isSidebarOpen()\"></i>\r\n <i class=\"fa-solid fa-chevron-left toggle-arrow\" [class.flipped]=\"isSidebarOpen()\"></i>\r\n </button>\r\n <span class=\"text-white empresa-nombre d-inline d-md-none ps-2\">{{empresaName()}}</span>\r\n <span class=\"text-white fs-5 fw-normal d-none d-md-inline ps-2 text-nowrap\">{{nameProyecto()}}</span>\r\n @if (showTabs() && tabsService.tabs().length) {\r\n <button class=\"tabs-header-btn mx-2 d-none d-md-flex\"\r\n [class.tabs-header-btn--active]=\"tabsService.visible()\" (click)=\"tabsService.toggleVisible()\"\r\n [title]=\"tabsService.visible() ? 'Ocultar acceso r\u00E1pido' : 'Mostrar acceso r\u00E1pido'\">\r\n <i class=\"fas fa-layer-group\"></i>\r\n @if (!tabsService.visible()) {\r\n <span class=\"tabs-header-count\">{{ tabsService.tabs().length }}</span>\r\n }\r\n </button>\r\n }\r\n </div>\r\n\r\n @if (showSearch()) {\r\n <button class=\"global-search-trigger d-none d-md-flex align-items-center\"\r\n [class.global-search-trigger--collapsed]=\"searchCollapsed()\" title=\"B\u00FAsqueda de p\u00E1ginas (F2)\"\r\n (click)=\"searchComponents.emit()\">\r\n <i class=\"fas fa-search global-search-icon\"></i>\r\n <span class=\"global-search-placeholder\">Buscar p\u00E1ginas...</span>\r\n <kbd class=\"global-search-kbd\">F2</kbd>\r\n </button>\r\n }\r\n\r\n <div #headerSideEnd class=\"d-flex align-items-center header-side header-side--end\">\r\n\r\n <!-- Fallback del buscador centrado cuando no entra (colisi\u00F3n) o en mobile\r\n (el buscador centrado se oculta por completo por debajo de md): la\r\n p\u00EDldora de texto \"F2\", igual que antes. -->\r\n <div #compactSearchWrapper class=\"h-100 d-flex align-items-center\">\r\n @if (showSearch() && searchCollapsed()) {\r\n <button class=\"global-search-compact-pill\" title=\"Buscar p\u00E1ginas (F2)\"\r\n (click)=\"searchComponents.emit()\">F2</button>\r\n }\r\n </div>\r\n\r\n <div class=\"d-flex align-items-center\">\r\n <ng-content select=\"[extra-actions]\" />\r\n\r\n <!-- Acciones adicionales, definidas por el consumidor: siempre a la izquierda\r\n de los tres fijos (tema, notificaciones, soporte). Solo se ocultan en mobile\r\n las que adem\u00E1s se reflejan en el bottom-nav (showInBottomNav) \u2014 si una acci\u00F3n\r\n no baja al bottom-nav, se queda visible ac\u00E1 para no desaparecer del todo. -->\r\n @for (action of actions(); track action.label) {\r\n @if (!action.hidden) {\r\n @if (action.items?.length) {\r\n <div class=\"dropdown d-inline-block z-index\" [class.show]=\"openActionKey() === action.label\" [class.desktop-only]=\"action.showInBottomNav\">\r\n <div class=\"h-100 d-flex align-items-center\">\r\n <button class=\"notification-btn option p-0 m-0 me-2\" [title]=\"action.label\" (click)=\"action.accion?.(); toggleActionMenu($event, action)\">\r\n <i class=\"fas text-white\" [ngClass]=\"action.icon\"></i>\r\n </button>\r\n </div>\r\n\r\n <div class=\"dropdown-menu dropdown-menu-end mtp-9 mb-0 py-0 custom-dropdown\" [class.show]=\"openActionKey() === action.label\">\r\n @for (opcion of action.items; track opcion.label; let last = $last) {\r\n <button class=\"dropdown-item py-2 fs-7 fw-4 d-flex align-items-center\"\r\n [ngClass]=\"{'divider-border': !last,'border-top-radius': $index == 0,'border-bottom-radius': last}\"\r\n (click)=\"runAction(opcion)\"\r\n (mouseenter)=\"showTooltip($event, opcion.title ?? '')\" (mouseleave)=\"hideTooltip()\">\r\n <i [class]=\"opcion.icon\" class=\"me-1\"></i> {{ opcion.label }}\r\n @if (opcion.value) {\r\n <span class=\"ms-auto ps-3 fw-6\">{{ opcion.value }}</span>\r\n }\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"h-100 d-flex align-items-center\" [class.desktop-only]=\"action.showInBottomNav\">\r\n <button class=\"notification-btn option p-0 m-0 me-2\" [title]=\"action.label\" (click)=\"action.accion?.()\">\r\n <i class=\"fas text-white\" [ngClass]=\"action.icon\"></i>\r\n @if (action.badge) {\r\n <span class=\"notification-badge\" [ngClass]=\"produccion() ? 'bg-danger' : 'bg-info'\">\r\n {{ action.badge }}\r\n </span>\r\n }\r\n </button>\r\n </div>\r\n }\r\n }\r\n }\r\n\r\n @if (showThemeSwitcher()) {\r\n <div class=\"dropdown d-inline-block z-index\" [class.show]=\"themeMenuOpen()\">\r\n <div class=\"h-100 d-flex align-items-center\">\r\n <button class=\"notification-btn option p-0 m-0 me-2\" (click)=\"toggleThemeMenu($event)\"\r\n [title]=\"'Tema: ' + themeLabel()\">\r\n <i class=\"fas text-white\" [ngClass]=\"themeIcon()\"></i>\r\n </button>\r\n </div>\r\n\r\n <div class=\"dropdown-menu dropdown-menu-end mtp-9 mb-0 py-0 custom-dropdown\" [class.show]=\"themeMenuOpen()\">\r\n @for (item of themes(); track item.id) {\r\n <button class=\"dropdown-item py-2 fs-7 fw-4 border-bottom-radius divider-border\"\r\n (click)=\"selectTheme(item.id)\">\r\n <i class=\"fas me-1\" [ngClass]=\"item.icon\"></i> {{ item.label }}\r\n @if (currentTheme() === item.id) {\r\n <i class=\"fas fa-check ms-auto text-success\"></i>\r\n }\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Notificaciones y soporte: \u00EDcono/estilo est\u00E1ndar, siempre visibles tambi\u00E9n en mobile -->\r\n @if (showNotifications()) {\r\n <div class=\"h-100 d-flex align-items-center\">\r\n <button class=\"notification-btn option p-0 m-0 me-2\" title=\"Notificaciones\"\r\n (click)=\"notificationsClick.emit()\">\r\n <i class=\"fas fa-bell text-white\"></i>\r\n @if (notificationsCount() > 0) {\r\n <span class=\"notification-badge\" [ngClass]=\"produccion() ? 'bg-danger' : 'bg-info'\">\r\n {{ notificationsCount() }}\r\n </span>\r\n }\r\n </button>\r\n </div>\r\n }\r\n\r\n @if (showChatbotToggle()) {\r\n <div class=\"h-100 d-flex align-items-center\">\r\n <button class=\"notification-btn option p-0 m-0 me-2\"\r\n [title]=\"chatbotVisible() ? 'Ocultar soporte' : 'Mostrar soporte'\"\r\n (click)=\"chatbotVisibleChange.emit(!chatbotVisible())\">\r\n <i class=\"fas fa-robot text-white\"></i>\r\n </button>\r\n </div>\r\n }\r\n\r\n <div class=\"dropdown d-inline-block z-index\" [class.show]=\"userMenuOpen()\">\r\n <div class=\"h-100 d-flex align-items-center\">\r\n <button class=\"border-0 bg-none\" (click)=\"toggleUserMenu($event)\">\r\n <img class=\"circle-icon shadow-sm\" [src]=\"avatarSrc()\" (error)=\"avatarFallido.set(userAvatar())\" alt=\"\">\r\n </button>\r\n </div>\r\n <div class=\"dropdown-menu dropdown-menu-end mtp-9 mb-0 py-0\" [class.show]=\"userMenuOpen()\">\r\n <div\r\n class=\"d-flex no-block align-items-center p-3 divider-border bg-gray-user-data border-top-radius\">\r\n <div class=\"d-flex align-items-center\">\r\n <img class=\"circle-icon shadow-sm\" [src]=\"avatarSrc()\" (error)=\"avatarFallido.set(userAvatar())\" alt=\"\">\r\n <div class=\"ms-2\">\r\n <p class=\"fs-7 my-0 py-0 fw-6\">{{userName()}}</p>\r\n @if (userName().startsWith('DESARROLLADOR')) {\r\n <span class=\"fs-7 fw-bold\">Desarrollador</span>\r\n } @else if (userName().startsWith('SOPORTE')) {\r\n <span class=\"fs-7 fw-bold\">Soporte</span>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n @if (showPanelControl()) {\r\n <button class=\"dropdown-item py-3 fs-7 fw-4\" (click)=\"userMenuOpen.set(false); panelControlClick.emit()\">\r\n Panel de control\r\n </button>\r\n }\r\n <button class=\"dropdown-item py-3 fs-7 fw-4 border-bottom-radius\" (click)=\"userMenuOpen.set(false); logout.emit()\"><i\r\n class=\"fas fa-power-off\"></i> Cerrar sesi\u00F3n</button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n@if (tooltip()?.text) {\r\n<div #accesoTooltip class=\"acceso-tooltip-fixed\" [class.acceso-tooltip-fixed--left]=\"tooltip()!.side === 'left'\"\r\n [class.acceso-tooltip-fixed--wrap]=\"tooltip()!.maxWidth != null\"\r\n [style.left.px]=\"tooltip()!.left\" [style.right.px]=\"tooltip()!.right\" [style.top.px]=\"tooltip()!.y\"\r\n [style.maxWidth.px]=\"tooltip()!.maxWidth\" [style.--tooltip-accent]=\"accentColor()\">\r\n <span class=\"tooltip-label\">{{ tooltip()!.text.split(': ')[0] }}:</span>\r\n <span class=\"tooltip-value\" [style.color]=\"accentColor()\">{{ tooltip()!.text.split(': ')[1] }}</span>\r\n</div>\r\n}\r\n", styles: [".header-container{height:50px;display:flex;align-items:center;transition:position .5s ease-in-out,background-color .2s ease;padding:0;margin:0;position:relative;--header-accent: #398bf7;background:var(--header-accent);border-bottom:2px solid transparent}.header-container.dev{--header-accent: rgb(197, 0, 72)}.header-container.prod{--header-accent: #398bf7}.header-container.qa{--header-accent: #20c997}:host-context(.dark-mode) .header-container,:host-context(.sky-blue-mode) .header-container,:host-context(.high-contrast-green-mode) .header-container,:host-context(.purple-mode) .header-container{background:var(--header-bg);border-bottom-color:var(--header-accent)}.first-header{display:flex;align-items:center;gap:6px}.empresa-nombre{max-width:350px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis;white-space:normal;line-height:1.25;max-height:2.5em;max-width:100%}@media(max-width:575px){.empresa-nombre{display:none!important}.first-header{min-width:auto!important;padding:10px!important}}@media(min-width:576px)and (max-width:1180px){.first-header{padding:10px!important}}@media(min-width:1181px){.first-header{min-width:260px;width:260px;padding:15px}}.bg-gray-user-data{background:var(--background-btn-light)}.dropdown-menu{transform-origin:top;background-color:var(--background-card);border-color:var(--border-color-modal);color:var(--text-color)}.dropdown-menu-end{right:0!important;left:auto!important}.dropdown-item{color:var(--text-color)}.dropdown-item:hover,.dropdown-item:focus{background-color:var(--bs-table-hover-bg);color:var(--text-color)}.dropdown-menu.show{animation:dropdownMenuIn .24s cubic-bezier(.2,0,.2,1) both}@keyframes dropdownMenuIn{0%{opacity:0;transform:translateY(10px) scale(.9)}to{opacity:1;transform:translateY(0) scale(1)}}.dropdown-menu{width:300px!important;min-width:300px!important}.circle-icon{min-height:30px;min-width:30px;height:30px;width:30px;border-radius:100%;border:2px solid white}.none-style{background:none;border:none}.sidebar-toggle-btn{display:flex;align-items:center;gap:4px}.sidebar-toggle-btn i{transition:transform .25s cubic-bezier(.4,0,.2,1)}.sidebar-toggle-btn i.flipped{transform:scaleX(-1)}.sidebar-toggle-btn .toggle-arrow{font-size:9px;color:#fff}.z-index{z-index:1001}.option{position:relative;border-radius:50px;width:33px;height:100%;transition:background-color .3s ease}.option:hover,.dropdown.show .option{background-color:var(--bs-table-hover-bg)!important}.custom-dropdown.dropdown-menu{min-width:unset!important;width:auto!important}.acceso-tooltip-fixed{position:fixed;z-index:99999;transform:translateY(-50%);pointer-events:none;padding:4px 10px 4px 8px;border-radius:6px;font-family:monospace;white-space:nowrap;background:#0f0f14;border:1px solid rgba(255,255,255,.07);border-left:2px solid var(--tooltip-accent, #c50048);display:flex;align-items:center;gap:5px;animation:tooltipFadeIn .14s cubic-bezier(.25,.46,.45,.94)}.acceso-tooltip-fixed:before{content:\"\";position:absolute;right:100%;top:50%;transform:translateY(-50%);border:4px solid transparent;border-right-color:var(--tooltip-accent, #c50048)}.acceso-tooltip-fixed .tooltip-label{font-size:10px;font-weight:400;color:#fff;letter-spacing:.3px}.acceso-tooltip-fixed .tooltip-value{font-size:11px;font-weight:700}.acceso-tooltip-fixed--left{border-left:1px solid rgba(255,255,255,.07);border-right:2px solid var(--tooltip-accent, #c50048);padding:4px 8px 4px 10px;animation-name:tooltipFadeInLeft}.acceso-tooltip-fixed--left:before{right:auto;left:100%;border-right-color:transparent;border-left-color:var(--tooltip-accent, #c50048)}.acceso-tooltip-fixed--wrap{white-space:normal;flex-wrap:wrap}.acceso-tooltip-fixed--wrap .tooltip-value{min-width:0;overflow-wrap:anywhere}@keyframes tooltipFadeInLeft{0%{opacity:0;transform:translateY(-50%) translate(4px)}to{opacity:1;transform:translateY(-50%) translate(0)}}@keyframes tooltipFadeIn{0%{opacity:0;transform:translateY(-50%) translate(-4px)}to{opacity:1;transform:translateY(-50%) translate(0)}}.btn-print{border:1px solid rgba(0,0,0,.5764705882)!important;width:33px;height:30px!important;border-radius:6px}.btn-print:hover{border:1px solid rgba(61,61,61,.5254901961)!important;background-color:#dadadaf8!important}.btn-search{width:40px;height:31px!important;border-radius:6px}.logo-btn{width:25px;height:25px;object-fit:contain}.btn-deudas{background:#fff;border-radius:20px;border:none;white-space:nowrap;flex-shrink:0}.btn-deudas.titilando{animation:bordeTitilante 1s infinite alternate}@keyframes bordeTitilante{0%{box-shadow:0 0 2px red}50%{box-shadow:0 0 5px red,0 0 5px #ff4d4d}to{box-shadow:0 0 2px red}}@media(max-width:1100px){.btn-deudas{width:31px;height:31px!important;border-radius:50px;padding:0;display:flex;align-items:center;justify-content:center;aspect-ratio:1/1}.btn-deudas .texto-deudas{display:none}.btn-deudas .logo-btn{width:20px;height:20px;margin:0}}@keyframes icon-rotate-pop{0%{transform:rotate(0) scale(1)}25%{transform:rotate(10deg) scale(1.05)}50%{transform:rotate(-10deg) scale(1.1)}75%{transform:rotate(10deg) scale(1.05)}to{transform:rotate(0) scale(1)}}.button-icon-hover{display:inline-block}.button-tilt-hover:hover .button-icon-hover{animation:icon-rotate-pop 1s ease-in-out infinite}.desktop-only{display:flex}@media(max-width:667px){.desktop-only{display:none!important}}.notification-btn{position:relative;border:none;background:none;padding:0;margin-right:8px;cursor:pointer;width:33px;height:32px;display:flex;align-items:center;justify-content:center}.notification-btn i{color:#fff}.notification-badge{position:absolute;top:-5px;right:-3px;color:#fff;font-size:11px;font-weight:600;border-radius:4px;min-width:18px;height:18px;display:flex;align-items:center;justify-content:center;padding:0 4px}.header-side{flex:0 1 auto;min-width:0}.header-side--end{justify-content:flex-end}.global-search-trigger{position:absolute;left:50%;transform:translate(-50%);z-index:2;align-self:center;flex-shrink:0;margin:0;gap:8px;height:31px;width:280px;padding:0 6px 0 12px;background:#ffffff26;border:1px solid rgba(255,255,255,.3);border-radius:6px;color:#ffffffe6;font-size:12px;cursor:pointer;white-space:nowrap;transition:background .15s,border-color .15s,color .15s}.global-search-trigger:hover,.global-search-trigger:focus-visible{background:#ffffff47;border-color:#ffffff80;color:#fff}.global-search-trigger .global-search-icon{font-size:12px;color:#ffffffbf}.global-search-trigger .global-search-placeholder{flex-grow:1;text-align:start}.global-search-trigger .global-search-kbd{font-size:10px;font-weight:600;line-height:1;padding:3px 5px;border-radius:4px;background:#fff3;border:1px solid rgba(255,255,255,.35);color:#fff}.global-search-trigger--collapsed{display:none!important}.global-search-compact-pill{display:flex;align-items:center;justify-content:center;height:31px;margin-right:8px;padding:0 9px;background:#ffffff26;border:1px solid rgba(255,255,255,.3);border-radius:6px;color:#ffffffe6;font-size:11px;font-weight:700;cursor:pointer;transition:background .15s,border-color .15s,color .15s}.global-search-compact-pill:hover,.global-search-compact-pill:focus-visible{background:#ffffff47;border-color:#ffffff80;color:#fff}.tabs-header-btn{display:flex;align-items:center;gap:4px;background:#ffffff1a;border:1px solid rgba(255,255,255,.15);color:#fff9;border-radius:20px;padding:3px 8px;font-size:11px;cursor:pointer;transition:background .15s,color .15s;white-space:nowrap}.tabs-header-btn:hover{background:#fff3;color:#fff}.tabs-header-btn--active{background:#ffffff2e;color:#fff;border-color:#ffffff4d}.tabs-header-btn i{font-size:12px}.tabs-header-count{font-weight:700;font-size:11px;background:#fff3;border-radius:10px;padding:0 5px;min-width:16px;text-align:center}\n"] }]
778
806
  }], propDecorators: { headerContainerRef: [{
779
807
  type: ViewChild,
780
808
  args: ['headerContainer']
@@ -787,6 +815,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
787
815
  }], compactSearchWrapperRef: [{
788
816
  type: ViewChild,
789
817
  args: ['compactSearchWrapper']
818
+ }], accesoTooltipRef: [{
819
+ type: ViewChild,
820
+ args: ['accesoTooltip']
790
821
  }], onDocumentClick: [{
791
822
  type: HostListener,
792
823
  args: ['document:click']