@excellore/ui 0.0.9 → 0.0.11

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,8 +1,8 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, PLATFORM_ID, signal, effect, Injectable, input, output, HostListener, Component, computed } from '@angular/core';
3
- import { isPlatformBrowser, CommonModule, NgClass, NgTemplateOutlet } from '@angular/common';
4
- import { RouterLink, RouterLinkActive, Router } from '@angular/router';
5
- import { LucideGrid, LucideBellOff, LucideCheckCheck, LucideTrash2, LucideBell, LucideChevronDown, LucideCreditCard, LucideLogOut, LucideSettings, LucideUser, LucideBuilding2, LucideMenu, LucideMonitor, LucideMoon, LucideSearch, LucideSun, LucideX } from '@lucide/angular';
2
+ import { inject, PLATFORM_ID, signal, effect, Injectable, input, output, HostListener, Component, computed, ElementRef, viewChild } from '@angular/core';
3
+ import { isPlatformBrowser, NgTemplateOutlet } from '@angular/common';
4
+ import { Router, RouterLink, RouterLinkActive } from '@angular/router';
5
+ import { LucideGrid, LucideBell, LucideBarChart3, LucideCheckSquare, LucideCalendar, LucideClipboardList, LucideBuilding2, LucideChevronDown, LucideChevronLeft, LucideChevronRight, LucideCreditCard, LucideFolderKanban, LucideHardDrive, LucideLayoutGrid, LucideLogOut, LucideLifeBuoy, LucideMail, LucideMenu, LucideMonitor, LucideMoon, LucideMessageSquare, LucidePieChart, LucideMoreHorizontal, LucidePlus, LucideSearch, LucideSettings, LucideSun, LucideUser, LucideUsers, LucideWorkflow, LucideFileText, LucideX, LucideBellOff, LucideCheckCheck, LucideTrash2 } from '@lucide/angular';
6
6
 
7
7
  const THEME_STORAGE_KEY = 'excellore-theme';
8
8
  class ThemeService {
@@ -13,6 +13,7 @@ class ThemeService {
13
13
  // The currently resolved actual theme applied ('light' | 'dark')
14
14
  resolvedTheme = signal('light', ...(ngDevMode ? [{ debugName: "resolvedTheme" }] : /* istanbul ignore next */ []));
15
15
  mediaQueryListener;
16
+ mediaQuery;
16
17
  constructor() {
17
18
  this.initializeTheme();
18
19
  // Effect to reactively update storage, apply document classes, and set up listeners
@@ -28,6 +29,9 @@ class ThemeService {
28
29
  getTheme() {
29
30
  return this.theme();
30
31
  }
32
+ ngOnDestroy() {
33
+ this.removeMediaQueryListener();
34
+ }
31
35
  toggleTheme() {
32
36
  const current = this.theme();
33
37
  if (current === 'light') {
@@ -73,12 +77,9 @@ class ThemeService {
73
77
  return;
74
78
  }
75
79
  const doc = document.documentElement;
80
+ this.removeMediaQueryListener();
76
81
  const mediaQuery = typeof window.matchMedia === 'function' ? window.matchMedia('(prefers-color-scheme: dark)') : null;
77
- // Clean up any previous listener to avoid leaks
78
- if (this.mediaQueryListener && mediaQuery) {
79
- mediaQuery.removeEventListener('change', this.mediaQueryListener);
80
- this.mediaQueryListener = undefined;
81
- }
82
+ this.mediaQuery = mediaQuery ?? undefined;
82
83
  let actualTheme = 'light';
83
84
  if (theme === 'system') {
84
85
  actualTheme = mediaQuery?.matches ? 'dark' : 'light';
@@ -109,10 +110,17 @@ class ThemeService {
109
110
  doc.classList.remove('dark');
110
111
  }
111
112
  }
112
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ThemeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
113
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ThemeService, providedIn: 'root' });
113
+ removeMediaQueryListener() {
114
+ if (this.mediaQuery && this.mediaQueryListener) {
115
+ this.mediaQuery.removeEventListener('change', this.mediaQueryListener);
116
+ }
117
+ this.mediaQuery = undefined;
118
+ this.mediaQueryListener = undefined;
119
+ }
120
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ThemeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
121
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ThemeService, providedIn: 'root' });
114
122
  }
115
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ThemeService, decorators: [{
123
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ThemeService, decorators: [{
116
124
  type: Injectable,
117
125
  args: [{
118
126
  providedIn: 'root',
@@ -149,22 +157,182 @@ class AppSwitcherComponent {
149
157
  this.closeSwitcher();
150
158
  }
151
159
  }
152
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AppSwitcherComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
153
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: AppSwitcherComponent, isStandalone: true, selector: "lib-app-switcher", inputs: { applications: { classPropertyName: "applications", publicName: "applications", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { applicationSelected: "applicationSelected" }, host: { listeners: { "document:click": "onOutsideClick($event)" } }, ngImport: i0, template: "<div class=\"app-switcher-wrapper\">\r\n <button\r\n type=\"button\"\r\n class=\"app-switcher-trigger\"\r\n (click)=\"toggleSwitcher()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"Toggle application switcher\"\r\n >\r\n <svg lucideGrid class=\"app-switcher-trigger-icon\"></svg>\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"app-switcher-dropdown\">\r\n <div class=\"app-switcher-header\">\r\n <div>\r\n <h3>Excellore Apps</h3>\r\n <p>Switch between available workspaces</p>\r\n </div>\r\n </div>\r\n \r\n @if (applications().length === 0) {\r\n <div class=\"app-switcher-empty\">\r\n No other apps available\r\n </div>\r\n } @else {\r\n <div class=\"app-switcher-list\">\r\n @for (app of applications(); track app.id) {\r\n @if (app.available !== false) {\r\n <a\r\n [href]=\"app.url\"\r\n (click)=\"onSelect(app)\"\r\n class=\"app-switcher-item app-switcher-link\"\r\n >\r\n <div class=\"app-switcher-app-mark\">\r\n {{ app.initials || getInitials(app.name) }}\r\n </div>\r\n <div class=\"app-switcher-app-content\">\r\n <div class=\"app-switcher-app-name\" [title]=\"app.name\">{{ app.name }}</div>\r\n <div class=\"app-switcher-app-url\">{{ app.url }}</div>\r\n </div>\r\n <span class=\"app-switcher-open-pill\">\r\n Open\r\n </span>\r\n </a>\r\n } @else {\r\n <div class=\"app-switcher-item unavailable\">\r\n <div class=\"app-switcher-app-mark unavailable\">\r\n {{ app.initials || getInitials(app.name) }}\r\n </div>\r\n <div class=\"app-switcher-app-content\">\r\n <div class=\"app-switcher-app-name\">{{ app.name }}</div>\r\n <div class=\"app-switcher-app-url\">{{ app.url }}</div>\r\n </div>\r\n <span class=\"app-switcher-soon-pill\">\r\n Soon\r\n </span>\r\n </div>\r\n }\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:inline-block}.app-switcher-wrapper{position:relative;display:inline-block;text-align:left}.app-switcher-trigger{display:grid;width:2.15rem;height:2.15rem;place-items:center;border:1px solid transparent;border-radius:.5rem;color:var(--foreground);background:transparent;cursor:pointer;transition:background-color .15s ease,border-color .15s ease}.app-switcher-trigger:hover{border-color:var(--border);background:color-mix(in srgb,var(--border) 35%,transparent)}.app-switcher-trigger:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.app-switcher-trigger-icon{width:1.125rem;height:1.125rem}.app-switcher-dropdown{position:fixed;z-index:50;top:3.75rem;right:.5rem;left:.5rem;max-height:calc(100dvh - 4.5rem);overflow:hidden;border:1px solid var(--border);border-radius:.875rem;background:var(--background);padding:.625rem;box-shadow:0 12px 30px #0f172a24;animation:switcherEnter .15s cubic-bezier(.16,1,.3,1)}.app-switcher-header{display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--border);margin-bottom:.375rem;padding:0 .25rem .625rem}.app-switcher-header h3{margin:0;color:var(--secondary);font-size:.65rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.app-switcher-header p{margin:.125rem 0 0;color:var(--secondary);font-size:.72rem;line-height:1.35}.app-switcher-empty{padding:1.5rem .5rem;color:var(--secondary);font-size:.8rem;text-align:center}.app-switcher-list{max-height:calc(100dvh - 10rem);overflow-y:auto;padding:.25rem 0}.app-switcher-item{display:flex;align-items:center;gap:.75rem;border:1px solid transparent;border-radius:.75rem;padding:.65rem;text-decoration:none;transition:background-color .15s ease,border-color .15s ease}.app-switcher-item+.app-switcher-item{margin-top:.125rem}.app-switcher-link:hover{border-color:var(--border);background:color-mix(in srgb,var(--border) 34%,transparent)}.app-switcher-link:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.app-switcher-item.unavailable{opacity:.55;-webkit-user-select:none;user-select:none}.app-switcher-app-mark{display:flex;width:2.5rem;height:2.5rem;align-items:center;justify-content:center;flex-shrink:0;border-radius:.7rem;color:var(--primary);background:color-mix(in srgb,var(--primary) 9%,transparent);box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--primary) 15%,transparent);font-size:.875rem;font-weight:800}.app-switcher-app-content{min-width:0;flex:1 1 auto}.app-switcher-app-name{overflow:hidden;color:var(--foreground);font-size:.875rem;font-weight:750;text-overflow:ellipsis;white-space:nowrap}.app-switcher-app-url{overflow:hidden;margin-top:.125rem;color:var(--secondary);font-size:.72rem;text-overflow:ellipsis;white-space:nowrap}.app-switcher-app-mark.unavailable{border:1px dashed var(--border);color:var(--secondary);background:color-mix(in srgb,var(--border) 32%,transparent);box-shadow:none}.app-switcher-open-pill{flex-shrink:0;border-radius:999px;color:var(--primary);background:color-mix(in srgb,var(--primary) 9%,transparent);padding:.15rem .5rem;font-size:.65rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase;opacity:0;transition:opacity .15s ease}.app-switcher-link:hover .app-switcher-open-pill{opacity:1}.app-switcher-soon-pill{flex-shrink:0;border:1px solid var(--border);border-radius:999px;color:var(--secondary);padding:.15rem .5rem;font-size:.65rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase}.app-switcher-dropdown ::-webkit-scrollbar{width:.45rem}.app-switcher-dropdown ::-webkit-scrollbar-track{background:transparent}.app-switcher-dropdown ::-webkit-scrollbar-thumb{background:var(--border);border-radius:999px}@keyframes switcherEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}@media(min-width:640px){.app-switcher-dropdown{position:absolute;top:calc(100% + .5rem);right:0;left:auto;width:22.5rem;max-width:calc(100vw - 1rem);max-height:calc(100dvh - 5rem);transform-origin:top right}.app-switcher-list{max-height:26.25rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: LucideGrid, selector: "svg[lucideGrid3x3], svg[lucideGrid], svg[lucideGrid3X3]" }] });
160
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: AppSwitcherComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
161
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: AppSwitcherComponent, isStandalone: true, selector: "lib-app-switcher", inputs: { applications: { classPropertyName: "applications", publicName: "applications", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { applicationSelected: "applicationSelected" }, host: { listeners: { "document:click": "onOutsideClick($event)" } }, ngImport: i0, template: "<div class=\"app-switcher-wrapper\">\r\n <button\r\n type=\"button\"\r\n class=\"app-switcher-trigger\"\r\n (click)=\"toggleSwitcher()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"Toggle application switcher\"\r\n >\r\n <svg lucideGrid class=\"app-switcher-trigger-icon\"></svg>\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"app-switcher-dropdown\">\r\n <div class=\"app-switcher-header\">\r\n <div>\r\n <h3>Excellore Apps</h3>\r\n <p>Switch between available workspaces</p>\r\n </div>\r\n </div>\r\n \r\n @if (applications().length === 0) {\r\n <div class=\"app-switcher-empty\">\r\n No other apps available\r\n </div>\r\n } @else {\r\n <div class=\"app-switcher-list\">\r\n @for (app of applications(); track app.id) {\r\n @if (app.available !== false) {\r\n <a\r\n [href]=\"app.url\"\r\n (click)=\"onSelect(app)\"\r\n class=\"app-switcher-item app-switcher-link\"\r\n >\r\n <div class=\"app-switcher-app-mark\">\r\n {{ app.initials || getInitials(app.name) }}\r\n </div>\r\n <div class=\"app-switcher-app-content\">\r\n <div class=\"app-switcher-app-name\" [title]=\"app.name\">{{ app.name }}</div>\r\n <div class=\"app-switcher-app-url\">{{ app.url }}</div>\r\n </div>\r\n <span class=\"app-switcher-open-pill\">\r\n Open\r\n </span>\r\n </a>\r\n } @else {\r\n <div class=\"app-switcher-item unavailable\">\r\n <div class=\"app-switcher-app-mark unavailable\">\r\n {{ app.initials || getInitials(app.name) }}\r\n </div>\r\n <div class=\"app-switcher-app-content\">\r\n <div class=\"app-switcher-app-name\">{{ app.name }}</div>\r\n <div class=\"app-switcher-app-url\">{{ app.url }}</div>\r\n </div>\r\n <span class=\"app-switcher-soon-pill\">\r\n Soon\r\n </span>\r\n </div>\r\n }\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:inline-block}.app-switcher-wrapper{position:relative;display:inline-block;text-align:left}.app-switcher-trigger{display:grid;width:2.15rem;height:2.15rem;place-items:center;border:1px solid transparent;border-radius:.5rem;color:var(--foreground);background:transparent;cursor:pointer;transition:background-color .15s ease,border-color .15s ease}.app-switcher-trigger:hover{border-color:var(--border);background:color-mix(in srgb,var(--border) 35%,transparent)}.app-switcher-trigger:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.app-switcher-trigger-icon{width:1.125rem;height:1.125rem}.app-switcher-dropdown{position:fixed;z-index:50;top:3.75rem;right:.5rem;left:.5rem;max-height:calc(100dvh - 4.5rem);overflow:hidden;border:1px solid var(--border);border-radius:.875rem;background:var(--background);padding:.625rem;box-shadow:0 12px 30px #0f172a24;animation:switcherEnter .15s cubic-bezier(.16,1,.3,1)}.app-switcher-header{display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--border);margin-bottom:.375rem;padding:0 .25rem .625rem}.app-switcher-header h3{margin:0;color:var(--secondary);font-size:.65rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.app-switcher-header p{margin:.125rem 0 0;color:var(--secondary);font-size:.72rem;line-height:1.35}.app-switcher-empty{padding:1.5rem .5rem;color:var(--secondary);font-size:.8rem;text-align:center}.app-switcher-list{max-height:calc(100dvh - 10rem);overflow-y:auto;padding:.25rem 0}.app-switcher-item{display:flex;align-items:center;gap:.75rem;border:1px solid transparent;border-radius:.75rem;padding:.65rem;text-decoration:none;transition:background-color .15s ease,border-color .15s ease}.app-switcher-item+.app-switcher-item{margin-top:.125rem}.app-switcher-link:hover{border-color:var(--border);background:color-mix(in srgb,var(--border) 34%,transparent)}.app-switcher-link:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.app-switcher-item.unavailable{opacity:.55;-webkit-user-select:none;user-select:none}.app-switcher-app-mark{display:flex;width:2.5rem;height:2.5rem;align-items:center;justify-content:center;flex-shrink:0;border-radius:.7rem;color:var(--primary);background:color-mix(in srgb,var(--primary) 9%,transparent);box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--primary) 15%,transparent);font-size:.875rem;font-weight:800}.app-switcher-app-content{min-width:0;flex:1 1 auto}.app-switcher-app-name{overflow:hidden;color:var(--foreground);font-size:.875rem;font-weight:750;text-overflow:ellipsis;white-space:nowrap}.app-switcher-app-url{overflow:hidden;margin-top:.125rem;color:var(--secondary);font-size:.72rem;text-overflow:ellipsis;white-space:nowrap}.app-switcher-app-mark.unavailable{border:1px dashed var(--border);color:var(--secondary);background:color-mix(in srgb,var(--border) 32%,transparent);box-shadow:none}.app-switcher-open-pill{flex-shrink:0;border-radius:999px;color:var(--primary);background:color-mix(in srgb,var(--primary) 9%,transparent);padding:.15rem .5rem;font-size:.65rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase;opacity:0;transition:opacity .15s ease}.app-switcher-link:hover .app-switcher-open-pill{opacity:1}.app-switcher-soon-pill{flex-shrink:0;border:1px solid var(--border);border-radius:999px;color:var(--secondary);padding:.15rem .5rem;font-size:.65rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase}.app-switcher-dropdown ::-webkit-scrollbar{width:.45rem}.app-switcher-dropdown ::-webkit-scrollbar-track{background:transparent}.app-switcher-dropdown ::-webkit-scrollbar-thumb{background:var(--border);border-radius:999px}@keyframes switcherEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}@media(min-width:640px){.app-switcher-dropdown{position:absolute;top:calc(100% + .5rem);right:0;left:auto;width:22.5rem;max-width:calc(100vw - 1rem);max-height:calc(100dvh - 5rem);transform-origin:top right}.app-switcher-list{max-height:26.25rem}}\n"], dependencies: [{ kind: "component", type: LucideGrid, selector: "svg[lucideGrid3x3], svg[lucideGrid], svg[lucideGrid3X3]" }] });
154
162
  }
155
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AppSwitcherComponent, decorators: [{
163
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: AppSwitcherComponent, decorators: [{
156
164
  type: Component,
157
- args: [{ selector: 'lib-app-switcher', standalone: true, imports: [CommonModule, LucideGrid], template: "<div class=\"app-switcher-wrapper\">\r\n <button\r\n type=\"button\"\r\n class=\"app-switcher-trigger\"\r\n (click)=\"toggleSwitcher()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"Toggle application switcher\"\r\n >\r\n <svg lucideGrid class=\"app-switcher-trigger-icon\"></svg>\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"app-switcher-dropdown\">\r\n <div class=\"app-switcher-header\">\r\n <div>\r\n <h3>Excellore Apps</h3>\r\n <p>Switch between available workspaces</p>\r\n </div>\r\n </div>\r\n \r\n @if (applications().length === 0) {\r\n <div class=\"app-switcher-empty\">\r\n No other apps available\r\n </div>\r\n } @else {\r\n <div class=\"app-switcher-list\">\r\n @for (app of applications(); track app.id) {\r\n @if (app.available !== false) {\r\n <a\r\n [href]=\"app.url\"\r\n (click)=\"onSelect(app)\"\r\n class=\"app-switcher-item app-switcher-link\"\r\n >\r\n <div class=\"app-switcher-app-mark\">\r\n {{ app.initials || getInitials(app.name) }}\r\n </div>\r\n <div class=\"app-switcher-app-content\">\r\n <div class=\"app-switcher-app-name\" [title]=\"app.name\">{{ app.name }}</div>\r\n <div class=\"app-switcher-app-url\">{{ app.url }}</div>\r\n </div>\r\n <span class=\"app-switcher-open-pill\">\r\n Open\r\n </span>\r\n </a>\r\n } @else {\r\n <div class=\"app-switcher-item unavailable\">\r\n <div class=\"app-switcher-app-mark unavailable\">\r\n {{ app.initials || getInitials(app.name) }}\r\n </div>\r\n <div class=\"app-switcher-app-content\">\r\n <div class=\"app-switcher-app-name\">{{ app.name }}</div>\r\n <div class=\"app-switcher-app-url\">{{ app.url }}</div>\r\n </div>\r\n <span class=\"app-switcher-soon-pill\">\r\n Soon\r\n </span>\r\n </div>\r\n }\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:inline-block}.app-switcher-wrapper{position:relative;display:inline-block;text-align:left}.app-switcher-trigger{display:grid;width:2.15rem;height:2.15rem;place-items:center;border:1px solid transparent;border-radius:.5rem;color:var(--foreground);background:transparent;cursor:pointer;transition:background-color .15s ease,border-color .15s ease}.app-switcher-trigger:hover{border-color:var(--border);background:color-mix(in srgb,var(--border) 35%,transparent)}.app-switcher-trigger:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.app-switcher-trigger-icon{width:1.125rem;height:1.125rem}.app-switcher-dropdown{position:fixed;z-index:50;top:3.75rem;right:.5rem;left:.5rem;max-height:calc(100dvh - 4.5rem);overflow:hidden;border:1px solid var(--border);border-radius:.875rem;background:var(--background);padding:.625rem;box-shadow:0 12px 30px #0f172a24;animation:switcherEnter .15s cubic-bezier(.16,1,.3,1)}.app-switcher-header{display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--border);margin-bottom:.375rem;padding:0 .25rem .625rem}.app-switcher-header h3{margin:0;color:var(--secondary);font-size:.65rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.app-switcher-header p{margin:.125rem 0 0;color:var(--secondary);font-size:.72rem;line-height:1.35}.app-switcher-empty{padding:1.5rem .5rem;color:var(--secondary);font-size:.8rem;text-align:center}.app-switcher-list{max-height:calc(100dvh - 10rem);overflow-y:auto;padding:.25rem 0}.app-switcher-item{display:flex;align-items:center;gap:.75rem;border:1px solid transparent;border-radius:.75rem;padding:.65rem;text-decoration:none;transition:background-color .15s ease,border-color .15s ease}.app-switcher-item+.app-switcher-item{margin-top:.125rem}.app-switcher-link:hover{border-color:var(--border);background:color-mix(in srgb,var(--border) 34%,transparent)}.app-switcher-link:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.app-switcher-item.unavailable{opacity:.55;-webkit-user-select:none;user-select:none}.app-switcher-app-mark{display:flex;width:2.5rem;height:2.5rem;align-items:center;justify-content:center;flex-shrink:0;border-radius:.7rem;color:var(--primary);background:color-mix(in srgb,var(--primary) 9%,transparent);box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--primary) 15%,transparent);font-size:.875rem;font-weight:800}.app-switcher-app-content{min-width:0;flex:1 1 auto}.app-switcher-app-name{overflow:hidden;color:var(--foreground);font-size:.875rem;font-weight:750;text-overflow:ellipsis;white-space:nowrap}.app-switcher-app-url{overflow:hidden;margin-top:.125rem;color:var(--secondary);font-size:.72rem;text-overflow:ellipsis;white-space:nowrap}.app-switcher-app-mark.unavailable{border:1px dashed var(--border);color:var(--secondary);background:color-mix(in srgb,var(--border) 32%,transparent);box-shadow:none}.app-switcher-open-pill{flex-shrink:0;border-radius:999px;color:var(--primary);background:color-mix(in srgb,var(--primary) 9%,transparent);padding:.15rem .5rem;font-size:.65rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase;opacity:0;transition:opacity .15s ease}.app-switcher-link:hover .app-switcher-open-pill{opacity:1}.app-switcher-soon-pill{flex-shrink:0;border:1px solid var(--border);border-radius:999px;color:var(--secondary);padding:.15rem .5rem;font-size:.65rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase}.app-switcher-dropdown ::-webkit-scrollbar{width:.45rem}.app-switcher-dropdown ::-webkit-scrollbar-track{background:transparent}.app-switcher-dropdown ::-webkit-scrollbar-thumb{background:var(--border);border-radius:999px}@keyframes switcherEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}@media(min-width:640px){.app-switcher-dropdown{position:absolute;top:calc(100% + .5rem);right:0;left:auto;width:22.5rem;max-width:calc(100vw - 1rem);max-height:calc(100dvh - 5rem);transform-origin:top right}.app-switcher-list{max-height:26.25rem}}\n"] }]
165
+ args: [{ selector: 'lib-app-switcher', standalone: true, imports: [LucideGrid], template: "<div class=\"app-switcher-wrapper\">\r\n <button\r\n type=\"button\"\r\n class=\"app-switcher-trigger\"\r\n (click)=\"toggleSwitcher()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"Toggle application switcher\"\r\n >\r\n <svg lucideGrid class=\"app-switcher-trigger-icon\"></svg>\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"app-switcher-dropdown\">\r\n <div class=\"app-switcher-header\">\r\n <div>\r\n <h3>Excellore Apps</h3>\r\n <p>Switch between available workspaces</p>\r\n </div>\r\n </div>\r\n \r\n @if (applications().length === 0) {\r\n <div class=\"app-switcher-empty\">\r\n No other apps available\r\n </div>\r\n } @else {\r\n <div class=\"app-switcher-list\">\r\n @for (app of applications(); track app.id) {\r\n @if (app.available !== false) {\r\n <a\r\n [href]=\"app.url\"\r\n (click)=\"onSelect(app)\"\r\n class=\"app-switcher-item app-switcher-link\"\r\n >\r\n <div class=\"app-switcher-app-mark\">\r\n {{ app.initials || getInitials(app.name) }}\r\n </div>\r\n <div class=\"app-switcher-app-content\">\r\n <div class=\"app-switcher-app-name\" [title]=\"app.name\">{{ app.name }}</div>\r\n <div class=\"app-switcher-app-url\">{{ app.url }}</div>\r\n </div>\r\n <span class=\"app-switcher-open-pill\">\r\n Open\r\n </span>\r\n </a>\r\n } @else {\r\n <div class=\"app-switcher-item unavailable\">\r\n <div class=\"app-switcher-app-mark unavailable\">\r\n {{ app.initials || getInitials(app.name) }}\r\n </div>\r\n <div class=\"app-switcher-app-content\">\r\n <div class=\"app-switcher-app-name\">{{ app.name }}</div>\r\n <div class=\"app-switcher-app-url\">{{ app.url }}</div>\r\n </div>\r\n <span class=\"app-switcher-soon-pill\">\r\n Soon\r\n </span>\r\n </div>\r\n }\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:inline-block}.app-switcher-wrapper{position:relative;display:inline-block;text-align:left}.app-switcher-trigger{display:grid;width:2.15rem;height:2.15rem;place-items:center;border:1px solid transparent;border-radius:.5rem;color:var(--foreground);background:transparent;cursor:pointer;transition:background-color .15s ease,border-color .15s ease}.app-switcher-trigger:hover{border-color:var(--border);background:color-mix(in srgb,var(--border) 35%,transparent)}.app-switcher-trigger:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.app-switcher-trigger-icon{width:1.125rem;height:1.125rem}.app-switcher-dropdown{position:fixed;z-index:50;top:3.75rem;right:.5rem;left:.5rem;max-height:calc(100dvh - 4.5rem);overflow:hidden;border:1px solid var(--border);border-radius:.875rem;background:var(--background);padding:.625rem;box-shadow:0 12px 30px #0f172a24;animation:switcherEnter .15s cubic-bezier(.16,1,.3,1)}.app-switcher-header{display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--border);margin-bottom:.375rem;padding:0 .25rem .625rem}.app-switcher-header h3{margin:0;color:var(--secondary);font-size:.65rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.app-switcher-header p{margin:.125rem 0 0;color:var(--secondary);font-size:.72rem;line-height:1.35}.app-switcher-empty{padding:1.5rem .5rem;color:var(--secondary);font-size:.8rem;text-align:center}.app-switcher-list{max-height:calc(100dvh - 10rem);overflow-y:auto;padding:.25rem 0}.app-switcher-item{display:flex;align-items:center;gap:.75rem;border:1px solid transparent;border-radius:.75rem;padding:.65rem;text-decoration:none;transition:background-color .15s ease,border-color .15s ease}.app-switcher-item+.app-switcher-item{margin-top:.125rem}.app-switcher-link:hover{border-color:var(--border);background:color-mix(in srgb,var(--border) 34%,transparent)}.app-switcher-link:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.app-switcher-item.unavailable{opacity:.55;-webkit-user-select:none;user-select:none}.app-switcher-app-mark{display:flex;width:2.5rem;height:2.5rem;align-items:center;justify-content:center;flex-shrink:0;border-radius:.7rem;color:var(--primary);background:color-mix(in srgb,var(--primary) 9%,transparent);box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--primary) 15%,transparent);font-size:.875rem;font-weight:800}.app-switcher-app-content{min-width:0;flex:1 1 auto}.app-switcher-app-name{overflow:hidden;color:var(--foreground);font-size:.875rem;font-weight:750;text-overflow:ellipsis;white-space:nowrap}.app-switcher-app-url{overflow:hidden;margin-top:.125rem;color:var(--secondary);font-size:.72rem;text-overflow:ellipsis;white-space:nowrap}.app-switcher-app-mark.unavailable{border:1px dashed var(--border);color:var(--secondary);background:color-mix(in srgb,var(--border) 32%,transparent);box-shadow:none}.app-switcher-open-pill{flex-shrink:0;border-radius:999px;color:var(--primary);background:color-mix(in srgb,var(--primary) 9%,transparent);padding:.15rem .5rem;font-size:.65rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase;opacity:0;transition:opacity .15s ease}.app-switcher-link:hover .app-switcher-open-pill{opacity:1}.app-switcher-soon-pill{flex-shrink:0;border:1px solid var(--border);border-radius:999px;color:var(--secondary);padding:.15rem .5rem;font-size:.65rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase}.app-switcher-dropdown ::-webkit-scrollbar{width:.45rem}.app-switcher-dropdown ::-webkit-scrollbar-track{background:transparent}.app-switcher-dropdown ::-webkit-scrollbar-thumb{background:var(--border);border-radius:999px}@keyframes switcherEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}@media(min-width:640px){.app-switcher-dropdown{position:absolute;top:calc(100% + .5rem);right:0;left:auto;width:22.5rem;max-width:calc(100vw - 1rem);max-height:calc(100dvh - 5rem);transform-origin:top right}.app-switcher-list{max-height:26.25rem}}\n"] }]
158
166
  }], propDecorators: { applications: [{ type: i0.Input, args: [{ isSignal: true, alias: "applications", required: false }] }], applicationSelected: [{ type: i0.Output, args: ["applicationSelected"] }], onOutsideClick: [{
159
167
  type: HostListener,
160
168
  args: ['document:click', ['$event']]
161
169
  }] } });
162
170
 
171
+ // lib/shared/icon/icon.component.ts
172
+ class IconComponent {
173
+ name = input.required(...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
174
+ size = input(24, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
175
+ color = input(undefined, ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
176
+ strokeWidth = input(undefined, ...(ngDevMode ? [{ debugName: "strokeWidth" }] : /* istanbul ignore next */ []));
177
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: IconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
178
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: IconComponent, isStandalone: true, selector: "lib-icon", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, strokeWidth: { classPropertyName: "strokeWidth", publicName: "strokeWidth", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
179
+ @switch (name()) {
180
+ @case ('menu') {
181
+ <svg lucideMenu [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
182
+ }
183
+ @case ('x') {
184
+ <svg lucideX [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
185
+ }
186
+ @case ('sun') {
187
+ <svg lucideSun [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
188
+ }
189
+ @case ('moon') {
190
+ <svg lucideMoon [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
191
+ }
192
+ @case ('monitor') {
193
+ <svg lucideMonitor [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
194
+ }
195
+ @case ('bell') {
196
+ <svg lucideBell [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
197
+ }
198
+ @case ('grid') {
199
+ <svg lucideGrid [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
200
+ }
201
+ @case ('chevronDown') {
202
+ <svg lucideChevronDown [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
203
+ }
204
+ @case ('building2') {
205
+ <svg lucideBuilding2 [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
206
+ }
207
+ @case ('dashboard') { <svg lucideLayoutGrid [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
208
+ @case ('analytics') { <svg lucideBarChart3 [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
209
+ @case ('projects') { <svg lucideFolderKanban [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
210
+ @case ('tasks') { <svg lucideCheckSquare [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
211
+ @case ('team') { <svg lucideUsers [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
212
+ @case ('billing') { <svg lucideCreditCard [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
213
+ @case ('settings') { <svg lucideSettings [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
214
+ @case ('user') { <svg lucideUser [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
215
+ @case ('logout') { <svg lucideLogOut [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
216
+ @case ('plus') { <svg lucidePlus [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
217
+ @case ('more') { <svg lucideMoreHorizontal [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
218
+ @case ('chevronLeft') { <svg lucideChevronLeft [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
219
+ @case ('chevronRight') { <svg lucideChevronRight [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
220
+ @case ('mail') { <svg lucideMail [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
221
+ @case ('calendar') { <svg lucideCalendar [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
222
+ @case ('docs') { <svg lucideFileText [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
223
+ @case ('drive') { <svg lucideHardDrive [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
224
+ @case ('chat') { <svg lucideMessageSquare [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
225
+ @case ('automations') { <svg lucideWorkflow [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
226
+ @case ('forms') { <svg lucideClipboardList [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
227
+ @case ('reports') { <svg lucidePieChart [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
228
+ @case ('support') { <svg lucideLifeBuoy [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
229
+ @default {
230
+ <svg lucideSearch [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
231
+ }
232
+ }
233
+ `, isInline: true, styles: [":host{display:inline-flex;align-items:center;justify-content:center;color:inherit}.lib-icon{display:block;color:inherit}\n"], dependencies: [{ kind: "component", type: LucideBell, selector: "svg[lucideBell]" }, { kind: "component", type: LucideBarChart3, selector: "svg[lucideChartColumn], svg[lucideBarChart3]" }, { kind: "component", type: LucideCheckSquare, selector: "svg[lucideSquareCheckBig], svg[lucideCheckSquare]" }, { kind: "component", type: LucideCalendar, selector: "svg[lucideCalendar]" }, { kind: "component", type: LucideClipboardList, selector: "svg[lucideClipboardList]" }, { kind: "component", type: LucideBuilding2, selector: "svg[lucideBuilding2]" }, { kind: "component", type: LucideChevronDown, selector: "svg[lucideChevronDown]" }, { kind: "component", type: LucideChevronLeft, selector: "svg[lucideChevronLeft]" }, { kind: "component", type: LucideChevronRight, selector: "svg[lucideChevronRight]" }, { kind: "component", type: LucideCreditCard, selector: "svg[lucideCreditCard]" }, { kind: "component", type: LucideFolderKanban, selector: "svg[lucideFolderKanban]" }, { kind: "component", type: LucideGrid, selector: "svg[lucideGrid3x3], svg[lucideGrid], svg[lucideGrid3X3]" }, { kind: "component", type: LucideHardDrive, selector: "svg[lucideHardDrive]" }, { kind: "component", type: LucideLayoutGrid, selector: "svg[lucideLayoutGrid]" }, { kind: "component", type: LucideLogOut, selector: "svg[lucideLogOut]" }, { kind: "component", type: LucideLifeBuoy, selector: "svg[lucideLifeBuoy]" }, { kind: "component", type: LucideMail, selector: "svg[lucideMail]" }, { kind: "component", type: LucideMenu, selector: "svg[lucideMenu]" }, { kind: "component", type: LucideMonitor, selector: "svg[lucideMonitor]" }, { kind: "component", type: LucideMoon, selector: "svg[lucideMoon]" }, { kind: "component", type: LucideMessageSquare, selector: "svg[lucideMessageSquare]" }, { kind: "component", type: LucidePieChart, selector: "svg[lucideChartPie], svg[lucidePieChart]" }, { kind: "component", type: LucideMoreHorizontal, selector: "svg[lucideEllipsis], svg[lucideMoreHorizontal]" }, { kind: "component", type: LucidePlus, selector: "svg[lucidePlus]" }, { kind: "component", type: LucideSearch, selector: "svg[lucideSearch]" }, { kind: "component", type: LucideSettings, selector: "svg[lucideSettings]" }, { kind: "component", type: LucideSun, selector: "svg[lucideSun]" }, { kind: "component", type: LucideUser, selector: "svg[lucideUser]" }, { kind: "component", type: LucideUsers, selector: "svg[lucideUsers]" }, { kind: "component", type: LucideWorkflow, selector: "svg[lucideWorkflow]" }, { kind: "component", type: LucideFileText, selector: "svg[lucideFileText]" }, { kind: "component", type: LucideX, selector: "svg[lucideX]" }] });
234
+ }
235
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: IconComponent, decorators: [{
236
+ type: Component,
237
+ args: [{ selector: 'lib-icon', standalone: true, imports: [
238
+ LucideBell,
239
+ LucideBarChart3,
240
+ LucideCheckSquare,
241
+ LucideCalendar,
242
+ LucideClipboardList,
243
+ LucideBuilding2,
244
+ LucideChevronDown,
245
+ LucideChevronLeft,
246
+ LucideChevronRight,
247
+ LucideCreditCard,
248
+ LucideFolderKanban,
249
+ LucideGrid,
250
+ LucideHardDrive,
251
+ LucideLayoutGrid,
252
+ LucideLogOut,
253
+ LucideLifeBuoy,
254
+ LucideMail,
255
+ LucideMenu,
256
+ LucideMonitor,
257
+ LucideMoon,
258
+ LucideMessageSquare,
259
+ LucidePieChart,
260
+ LucideMoreHorizontal,
261
+ LucidePlus,
262
+ LucideSearch,
263
+ LucideSettings,
264
+ LucideSun,
265
+ LucideUser,
266
+ LucideUsers,
267
+ LucideWorkflow,
268
+ LucideFileText,
269
+ LucideX,
270
+ ], template: `
271
+ @switch (name()) {
272
+ @case ('menu') {
273
+ <svg lucideMenu [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
274
+ }
275
+ @case ('x') {
276
+ <svg lucideX [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
277
+ }
278
+ @case ('sun') {
279
+ <svg lucideSun [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
280
+ }
281
+ @case ('moon') {
282
+ <svg lucideMoon [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
283
+ }
284
+ @case ('monitor') {
285
+ <svg lucideMonitor [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
286
+ }
287
+ @case ('bell') {
288
+ <svg lucideBell [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
289
+ }
290
+ @case ('grid') {
291
+ <svg lucideGrid [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
292
+ }
293
+ @case ('chevronDown') {
294
+ <svg lucideChevronDown [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
295
+ }
296
+ @case ('building2') {
297
+ <svg lucideBuilding2 [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
298
+ }
299
+ @case ('dashboard') { <svg lucideLayoutGrid [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
300
+ @case ('analytics') { <svg lucideBarChart3 [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
301
+ @case ('projects') { <svg lucideFolderKanban [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
302
+ @case ('tasks') { <svg lucideCheckSquare [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
303
+ @case ('team') { <svg lucideUsers [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
304
+ @case ('billing') { <svg lucideCreditCard [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
305
+ @case ('settings') { <svg lucideSettings [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
306
+ @case ('user') { <svg lucideUser [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
307
+ @case ('logout') { <svg lucideLogOut [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
308
+ @case ('plus') { <svg lucidePlus [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
309
+ @case ('more') { <svg lucideMoreHorizontal [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
310
+ @case ('chevronLeft') { <svg lucideChevronLeft [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
311
+ @case ('chevronRight') { <svg lucideChevronRight [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
312
+ @case ('mail') { <svg lucideMail [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
313
+ @case ('calendar') { <svg lucideCalendar [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
314
+ @case ('docs') { <svg lucideFileText [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
315
+ @case ('drive') { <svg lucideHardDrive [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
316
+ @case ('chat') { <svg lucideMessageSquare [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
317
+ @case ('automations') { <svg lucideWorkflow [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
318
+ @case ('forms') { <svg lucideClipboardList [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
319
+ @case ('reports') { <svg lucidePieChart [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
320
+ @case ('support') { <svg lucideLifeBuoy [size]="size()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg> }
321
+ @default {
322
+ <svg lucideSearch [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
323
+ }
324
+ }
325
+ `, styles: [":host{display:inline-flex;align-items:center;justify-content:center;color:inherit}.lib-icon{display:block;color:inherit}\n"] }]
326
+ }], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], strokeWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "strokeWidth", required: false }] }] } });
327
+
163
328
  class SidebarMenuComponent {
329
+ router = inject(Router);
164
330
  items = input([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
165
331
  errorMessage = input(null, ...(ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []));
166
- defaultIcon = input('bi-dot', ...(ngDevMode ? [{ debugName: "defaultIcon" }] : /* istanbul ignore next */ []));
332
+ defaultIcon = input('dashboard', ...(ngDevMode ? [{ debugName: "defaultIcon" }] : /* istanbul ignore next */ []));
333
+ compact = input(false, ...(ngDevMode ? [{ debugName: "compact" }] : /* istanbul ignore next */ []));
167
334
  menuItemSelected = output();
335
+ menuDismissed = output();
168
336
  expandedIds = signal(new Set(), ...(ngDevMode ? [{ debugName: "expandedIds" }] : /* istanbul ignore next */ []));
169
337
  sortedItems = computed(() => this.sortItems(this.items()), ...(ngDevMode ? [{ debugName: "sortedItems" }] : /* istanbul ignore next */ []));
170
338
  toggle(item) {
@@ -188,6 +356,9 @@ class SidebarMenuComponent {
188
356
  hasChildren(item) {
189
357
  return !!item.children?.length;
190
358
  }
359
+ isActiveBranch(item) {
360
+ return (item.children ?? []).some(child => (!!child.route && this.router.isActive(child.route, false)) || this.isActiveBranch(child));
361
+ }
191
362
  hasRoute(item) {
192
363
  return !!item.route;
193
364
  }
@@ -200,6 +371,13 @@ class SidebarMenuComponent {
200
371
  onSelect(item) {
201
372
  if (!item.disabled) {
202
373
  this.menuItemSelected.emit(item);
374
+ this.expandedIds.set(new Set());
375
+ this.menuDismissed.emit();
376
+ }
377
+ }
378
+ closeFlyouts() {
379
+ if (this.compact()) {
380
+ this.expandedIds.set(new Set());
203
381
  }
204
382
  }
205
383
  sortedChildren(item) {
@@ -213,13 +391,13 @@ class SidebarMenuComponent {
213
391
  children: item.children ? this.sortItems(item.children) : undefined,
214
392
  }));
215
393
  }
216
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: SidebarMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
217
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: SidebarMenuComponent, isStandalone: true, selector: "lib-sidebar-menu", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, errorMessage: { classPropertyName: "errorMessage", publicName: "errorMessage", isSignal: true, isRequired: false, transformFunction: null }, defaultIcon: { classPropertyName: "defaultIcon", publicName: "defaultIcon", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { menuItemSelected: "menuItemSelected" }, ngImport: i0, template: "<nav class=\"sidebar-menu\" aria-label=\"Sidebar menu\">\n @if (errorMessage()) {\n <div class=\"sidebar-menu__error\" role=\"alert\">\n {{ errorMessage() }}\n </div>\r\n }\r\n\r\n <ng-template #menuList let-menuItems let-level=\"level\">\r\n @for (item of menuItems; track item.id) {\r\n @if (hasChildren(item)) {\r\n <div class=\"sidebar-menu__group\">\r\n <button\r\n type=\"button\"\r\n class=\"sidebar-menu__item sidebar-menu__button\"\r\n [class.disabled]=\"item.disabled\"\r\n [class.expanded]=\"isExpanded(item)\"\r\n [style.padding-left.rem]=\"0.75 + level * 0.75\"\r\n [attr.aria-expanded]=\"isExpanded(item)\"\r\n (click)=\"toggle(item)\"\r\n >\r\n <span class=\"sidebar-menu__item-main\">\r\n <i class=\"bi sidebar-menu__icon\" [ngClass]=\"iconFor(item)\" aria-hidden=\"true\"></i>\r\n <span class=\"sidebar-menu__label\">{{ item.label }}</span>\r\n </span>\r\n <svg class=\"sidebar-menu__chevron\" [class.expanded]=\"isExpanded(item)\" viewBox=\"0 0 20 20\" aria-hidden=\"true\">\r\n <path d=\"M6 8l4 4 4-4\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" />\r\n </svg>\r\n </button>\r\n\r\n @if (isExpanded(item)) {\r\n <div class=\"sidebar-menu__children\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"menuList\"\r\n [ngTemplateOutletContext]=\"{ $implicit: sortedChildren(item), level: level + 1 }\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n } @else if (hasRoute(item)) {\r\n <a\r\n [routerLink]=\"item.route\"\r\n routerLinkActive=\"active\"\r\n [routerLinkActiveOptions]=\"{ exact: item.exact ?? true }\"\r\n class=\"sidebar-menu__item\"\r\n [class.disabled]=\"item.disabled\"\r\n [style.padding-left.rem]=\"0.75 + level * 0.75\"\r\n (click)=\"onSelect(item)\"\r\n >\r\n <i class=\"bi sidebar-menu__icon\" [ngClass]=\"iconFor(item)\" aria-hidden=\"true\"></i>\r\n <span class=\"sidebar-menu__label\">{{ item.label }}</span>\r\n </a>\r\n } @else if (hasHref(item)) {\r\n <a\r\n [href]=\"item.href\"\r\n [target]=\"item.target || '_self'\"\r\n class=\"sidebar-menu__item\"\r\n [class.disabled]=\"item.disabled\"\r\n [style.padding-left.rem]=\"0.75 + level * 0.75\"\r\n (click)=\"onSelect(item)\"\r\n >\r\n <i class=\"bi sidebar-menu__icon\" [ngClass]=\"iconFor(item)\" aria-hidden=\"true\"></i>\r\n <span class=\"sidebar-menu__label\">{{ item.label }}</span>\r\n </a>\r\n } @else {\r\n <button\r\n type=\"button\"\r\n class=\"sidebar-menu__item sidebar-menu__button\"\r\n [class.disabled]=\"item.disabled\"\r\n [style.padding-left.rem]=\"0.75 + level * 0.75\"\r\n (click)=\"onSelect(item)\"\r\n >\r\n <span class=\"sidebar-menu__item-main\">\r\n <i class=\"bi sidebar-menu__icon\" [ngClass]=\"iconFor(item)\" aria-hidden=\"true\"></i>\r\n <span class=\"sidebar-menu__label\">{{ item.label }}</span>\r\n </span>\r\n </button>\r\n }\r\n }\r\n </ng-template>\r\n\r\n <ng-container [ngTemplateOutlet]=\"menuList\" [ngTemplateOutletContext]=\"{ $implicit: sortedItems(), level: 0 }\" />\r\n</nav>\r\n", styles: [":host{display:block;height:100%;min-width:0;min-height:0}.sidebar-menu{display:flex;flex-direction:column;gap:.25rem;height:100%;min-height:0;overflow-x:hidden;overflow-y:auto;padding:.5rem}.sidebar-menu__item{display:flex;min-height:2.5rem;width:100%;align-items:center;gap:.625rem;border:0;border-radius:.375rem;background:transparent;color:var(--bs-body-color, var(--foreground, #111827));cursor:pointer;font:inherit;padding:.625rem .75rem;text-align:left;text-decoration:none;transition:background-color .15s ease,color .15s ease}.sidebar-menu__item:hover,.sidebar-menu__item.expanded{background:var(--bs-secondary-bg, color-mix(in srgb, var(--border, #e5e7eb) 65%, transparent));color:var(--bs-body-color, var(--foreground, #111827))}.sidebar-menu__item.active{background:var(--accent-light, color-mix(in srgb, var(--accent-primary, #0067b8) 12%, transparent));color:var(--accent-primary, #0067b8)}.sidebar-menu__button{justify-content:space-between}.sidebar-menu__item-main{display:inline-flex;min-width:0;align-items:center;gap:.625rem}.sidebar-menu__icon{width:1.1rem;min-width:1.1rem;font-size:1.1rem;line-height:1;text-align:center}.sidebar-menu__label{min-width:0;overflow:hidden;font-size:.875rem;font-weight:500;text-overflow:ellipsis;white-space:nowrap}.sidebar-menu__chevron{height:1rem;width:1rem;color:var(--text-secondary, var(--secondary, #6b7280));transition:transform .2s ease}.sidebar-menu__chevron.expanded{transform:rotate(180deg)}.sidebar-menu__children{display:grid;gap:.25rem;margin-top:.25rem}.sidebar-menu__error{border-radius:.375rem;color:var(--danger, #dc2626);font-size:.875rem;padding:.5rem .75rem}.sidebar-menu__item.disabled{cursor:not-allowed;opacity:.45;pointer-events:none}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }] });
394
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: SidebarMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
395
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: SidebarMenuComponent, isStandalone: true, selector: "lib-sidebar-menu", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, errorMessage: { classPropertyName: "errorMessage", publicName: "errorMessage", isSignal: true, isRequired: false, transformFunction: null }, defaultIcon: { classPropertyName: "defaultIcon", publicName: "defaultIcon", isSignal: true, isRequired: false, transformFunction: null }, compact: { classPropertyName: "compact", publicName: "compact", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { menuItemSelected: "menuItemSelected", menuDismissed: "menuDismissed" }, ngImport: i0, template: "<nav class=\"sidebar-menu\" [class.compact]=\"compact()\" aria-label=\"Sidebar menu\">\r\n @if (errorMessage()) { <div class=\"sidebar-menu__error\" role=\"alert\">{{ errorMessage() }}</div> }\r\n\r\n <ng-template #menuList let-menuItems let-level=\"level\">\r\n @for (item of menuItems; track item.id) {\r\n <div class=\"sidebar-menu__node\" [class.has-open-flyout]=\"compact() && isExpanded(item)\">\r\n @if (hasChildren(item)) {\r\n <button type=\"button\" class=\"sidebar-menu__item\" [class.disabled]=\"item.disabled\" [class.expanded]=\"isExpanded(item) || isActiveBranch(item)\" [class.active-ancestor]=\"isActiveBranch(item)\" [style.position]=\"compact() && level === 0 ? 'relative' : null\"\r\n [attr.aria-expanded]=\"isExpanded(item)\" [attr.aria-haspopup]=\"compact() ? 'menu' : null\"\r\n [attr.title]=\"compact() ? item.label : null\" (click)=\"toggle(item)\">\r\n <span class=\"sidebar-menu__item-main\" [style.position]=\"compact() && level === 0 ? 'absolute' : null\" [style.left]=\"compact() && level === 0 ? '50%' : null\" [style.transform]=\"compact() && level === 0 ? 'translateX(-50%)' : null\"><lib-icon [name]=\"iconFor(item)\" [size]=\"17\" [strokeWidth]=\"isExpanded(item) ? 2.4 : 2\" /><span class=\"sidebar-menu__label\">{{ item.label }}</span></span>\r\n <lib-icon class=\"sidebar-menu__chevron\" [class.expanded]=\"isExpanded(item)\" [name]=\"compact() ? 'chevronRight' : 'chevronDown'\" [size]=\"15\" />\r\n </button>\r\n @if (isExpanded(item)) {\r\n <div class=\"sidebar-menu__children\" [class.sidebar-menu__flyout]=\"compact()\" role=\"menu\" [attr.aria-label]=\"item.label\">\r\n @if (compact()) { <div class=\"sidebar-menu__flyout-title\">{{ item.label }}</div> }\r\n <ng-container [ngTemplateOutlet]=\"menuList\" [ngTemplateOutletContext]=\"{ $implicit: sortedChildren(item), level: level + 1 }\" />\r\n </div>\r\n }\r\n } @else {\r\n <ng-template #content><lib-icon [name]=\"iconFor(item)\" [size]=\"17\" [strokeWidth]=\"2\" /><span class=\"sidebar-menu__label\">{{ item.label }}</span>@if (item.badge !== undefined) { <span class=\"sidebar-menu__badge\">{{ item.badge }}</span> }</ng-template>\r\n @if (hasRoute(item)) {\r\n <a [routerLink]=\"item.route\" routerLinkActive=\"active\" [routerLinkActiveOptions]=\"{ exact: item.exact ?? true }\" class=\"sidebar-menu__item\" [class.disabled]=\"item.disabled\" [attr.title]=\"compact() ? item.label : null\" role=\"menuitem\" (click)=\"onSelect(item)\"><ng-container [ngTemplateOutlet]=\"content\" /></a>\r\n } @else if (hasHref(item)) {\r\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" class=\"sidebar-menu__item\" [class.disabled]=\"item.disabled\" [attr.title]=\"compact() ? item.label : null\" role=\"menuitem\" (click)=\"onSelect(item)\"><ng-container [ngTemplateOutlet]=\"content\" /></a>\r\n } @else {\r\n <button type=\"button\" class=\"sidebar-menu__item\" [class.disabled]=\"item.disabled\" [attr.title]=\"compact() ? item.label : null\" role=\"menuitem\" (click)=\"onSelect(item)\"><ng-container [ngTemplateOutlet]=\"content\" /></button>\r\n }\r\n }\r\n </div>\r\n }\r\n </ng-template>\r\n\r\n <section class=\"sidebar-menu__section\">\r\n <div class=\"sidebar-menu__list\">\r\n <ng-container [ngTemplateOutlet]=\"menuList\" [ngTemplateOutletContext]=\"{ $implicit: sortedItems(), level: 0 }\" />\r\n </div>\r\n </section>\r\n</nav>\r\n", styles: [":host{display:block;min-width:0;overflow:visible}.sidebar-menu{display:flex;flex-direction:column;gap:1rem;padding:0 var(--rail-pad,.75rem) 1rem;overflow:visible}.sidebar-menu__section,.sidebar-menu__list{display:grid;gap:.125rem}.sidebar-menu__heading{margin:0 0 .25rem .5rem;color:var(--rail-muted);font-size:.6875rem;font-weight:600;letter-spacing:.06em;text-transform:uppercase}.sidebar-menu__node{position:relative;min-width:0}.sidebar-menu__item{display:flex;width:100%;height:2rem;align-items:center;gap:.625rem;border:0;border-radius:.4rem;padding:.375rem .5rem;background:transparent;color:var(--rail-text);cursor:pointer;font:inherit;text-align:left;text-decoration:none;transition:background-color .15s,color .15s}.sidebar-menu__item:hover,.sidebar-menu__item.expanded{background:var(--rail-hover);color:var(--rail-text-strong)}.sidebar-menu__item.active{background:var(--rail-active-bg);color:var(--rail-active);font-weight:600}.sidebar-menu__item:focus-visible{outline:2px solid var(--rail-focus);outline-offset:2px}.sidebar-menu__item-main{display:flex;min-width:0;flex:1;align-items:center;gap:.625rem}.sidebar-menu__label{min-width:0;overflow:hidden;font-size:.8125rem;text-overflow:ellipsis;white-space:nowrap}.sidebar-menu__badge{margin-left:auto;color:var(--rail-muted);font-size:.7rem}.sidebar-menu__chevron{color:var(--rail-muted);transition:transform .15s}.sidebar-menu__chevron.expanded{transform:rotate(180deg)}.sidebar-menu__children:not(.sidebar-menu__flyout){display:grid;gap:.125rem;margin:.125rem 0 .125rem .65rem;padding-left:.55rem;border-left:1px solid var(--rail-border)}.sidebar-menu__error{padding:.5rem;color:var(--rail-danger);font-size:.8rem}.disabled{opacity:.45;pointer-events:none}.compact{align-items:center;gap:.5rem;padding-inline:.75rem}.compact .sidebar-menu__section,.compact .sidebar-menu__list{width:100%}.compact .sidebar-menu__heading,.compact>.sidebar-menu__section>.sidebar-menu__list>.sidebar-menu__node>.sidebar-menu__item .sidebar-menu__label,.compact>.sidebar-menu__section>.sidebar-menu__list>.sidebar-menu__node>.sidebar-menu__item .sidebar-menu__badge,.compact>.sidebar-menu__section>.sidebar-menu__list>.sidebar-menu__node>.sidebar-menu__item .sidebar-menu__chevron{display:none}.compact>.sidebar-menu__section>.sidebar-menu__list>.sidebar-menu__node>.sidebar-menu__item{justify-content:center}.compact>.sidebar-menu__section>.sidebar-menu__list>.sidebar-menu__node>.sidebar-menu__item .sidebar-menu__item-main{flex:0}.sidebar-menu__flyout{position:absolute;z-index:30;top:0;left:calc(100% + .65rem);display:grid;width:13rem;gap:.125rem;padding:.4rem;border:1px solid var(--rail-border);border-radius:.55rem;background:var(--rail-popover);box-shadow:var(--rail-popover-shadow)}.sidebar-menu__flyout .sidebar-menu__flyout{left:calc(100% + .5rem);top:-.4rem}.sidebar-menu__flyout-title{padding:.35rem .5rem;color:var(--rail-muted);font-size:.6875rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em}.sidebar-menu__flyout .sidebar-menu__chevron.expanded{transform:rotate(90deg)}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "component", type: IconComponent, selector: "lib-icon", inputs: ["name", "size", "color", "strokeWidth"] }] });
218
396
  }
219
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: SidebarMenuComponent, decorators: [{
397
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: SidebarMenuComponent, decorators: [{
220
398
  type: Component,
221
- args: [{ selector: 'lib-sidebar-menu', standalone: true, imports: [NgClass, NgTemplateOutlet, RouterLink, RouterLinkActive], template: "<nav class=\"sidebar-menu\" aria-label=\"Sidebar menu\">\n @if (errorMessage()) {\n <div class=\"sidebar-menu__error\" role=\"alert\">\n {{ errorMessage() }}\n </div>\r\n }\r\n\r\n <ng-template #menuList let-menuItems let-level=\"level\">\r\n @for (item of menuItems; track item.id) {\r\n @if (hasChildren(item)) {\r\n <div class=\"sidebar-menu__group\">\r\n <button\r\n type=\"button\"\r\n class=\"sidebar-menu__item sidebar-menu__button\"\r\n [class.disabled]=\"item.disabled\"\r\n [class.expanded]=\"isExpanded(item)\"\r\n [style.padding-left.rem]=\"0.75 + level * 0.75\"\r\n [attr.aria-expanded]=\"isExpanded(item)\"\r\n (click)=\"toggle(item)\"\r\n >\r\n <span class=\"sidebar-menu__item-main\">\r\n <i class=\"bi sidebar-menu__icon\" [ngClass]=\"iconFor(item)\" aria-hidden=\"true\"></i>\r\n <span class=\"sidebar-menu__label\">{{ item.label }}</span>\r\n </span>\r\n <svg class=\"sidebar-menu__chevron\" [class.expanded]=\"isExpanded(item)\" viewBox=\"0 0 20 20\" aria-hidden=\"true\">\r\n <path d=\"M6 8l4 4 4-4\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.8\" />\r\n </svg>\r\n </button>\r\n\r\n @if (isExpanded(item)) {\r\n <div class=\"sidebar-menu__children\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"menuList\"\r\n [ngTemplateOutletContext]=\"{ $implicit: sortedChildren(item), level: level + 1 }\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n } @else if (hasRoute(item)) {\r\n <a\r\n [routerLink]=\"item.route\"\r\n routerLinkActive=\"active\"\r\n [routerLinkActiveOptions]=\"{ exact: item.exact ?? true }\"\r\n class=\"sidebar-menu__item\"\r\n [class.disabled]=\"item.disabled\"\r\n [style.padding-left.rem]=\"0.75 + level * 0.75\"\r\n (click)=\"onSelect(item)\"\r\n >\r\n <i class=\"bi sidebar-menu__icon\" [ngClass]=\"iconFor(item)\" aria-hidden=\"true\"></i>\r\n <span class=\"sidebar-menu__label\">{{ item.label }}</span>\r\n </a>\r\n } @else if (hasHref(item)) {\r\n <a\r\n [href]=\"item.href\"\r\n [target]=\"item.target || '_self'\"\r\n class=\"sidebar-menu__item\"\r\n [class.disabled]=\"item.disabled\"\r\n [style.padding-left.rem]=\"0.75 + level * 0.75\"\r\n (click)=\"onSelect(item)\"\r\n >\r\n <i class=\"bi sidebar-menu__icon\" [ngClass]=\"iconFor(item)\" aria-hidden=\"true\"></i>\r\n <span class=\"sidebar-menu__label\">{{ item.label }}</span>\r\n </a>\r\n } @else {\r\n <button\r\n type=\"button\"\r\n class=\"sidebar-menu__item sidebar-menu__button\"\r\n [class.disabled]=\"item.disabled\"\r\n [style.padding-left.rem]=\"0.75 + level * 0.75\"\r\n (click)=\"onSelect(item)\"\r\n >\r\n <span class=\"sidebar-menu__item-main\">\r\n <i class=\"bi sidebar-menu__icon\" [ngClass]=\"iconFor(item)\" aria-hidden=\"true\"></i>\r\n <span class=\"sidebar-menu__label\">{{ item.label }}</span>\r\n </span>\r\n </button>\r\n }\r\n }\r\n </ng-template>\r\n\r\n <ng-container [ngTemplateOutlet]=\"menuList\" [ngTemplateOutletContext]=\"{ $implicit: sortedItems(), level: 0 }\" />\r\n</nav>\r\n", styles: [":host{display:block;height:100%;min-width:0;min-height:0}.sidebar-menu{display:flex;flex-direction:column;gap:.25rem;height:100%;min-height:0;overflow-x:hidden;overflow-y:auto;padding:.5rem}.sidebar-menu__item{display:flex;min-height:2.5rem;width:100%;align-items:center;gap:.625rem;border:0;border-radius:.375rem;background:transparent;color:var(--bs-body-color, var(--foreground, #111827));cursor:pointer;font:inherit;padding:.625rem .75rem;text-align:left;text-decoration:none;transition:background-color .15s ease,color .15s ease}.sidebar-menu__item:hover,.sidebar-menu__item.expanded{background:var(--bs-secondary-bg, color-mix(in srgb, var(--border, #e5e7eb) 65%, transparent));color:var(--bs-body-color, var(--foreground, #111827))}.sidebar-menu__item.active{background:var(--accent-light, color-mix(in srgb, var(--accent-primary, #0067b8) 12%, transparent));color:var(--accent-primary, #0067b8)}.sidebar-menu__button{justify-content:space-between}.sidebar-menu__item-main{display:inline-flex;min-width:0;align-items:center;gap:.625rem}.sidebar-menu__icon{width:1.1rem;min-width:1.1rem;font-size:1.1rem;line-height:1;text-align:center}.sidebar-menu__label{min-width:0;overflow:hidden;font-size:.875rem;font-weight:500;text-overflow:ellipsis;white-space:nowrap}.sidebar-menu__chevron{height:1rem;width:1rem;color:var(--text-secondary, var(--secondary, #6b7280));transition:transform .2s ease}.sidebar-menu__chevron.expanded{transform:rotate(180deg)}.sidebar-menu__children{display:grid;gap:.25rem;margin-top:.25rem}.sidebar-menu__error{border-radius:.375rem;color:var(--danger, #dc2626);font-size:.875rem;padding:.5rem .75rem}.sidebar-menu__item.disabled{cursor:not-allowed;opacity:.45;pointer-events:none}\n"] }]
222
- }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], errorMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessage", required: false }] }], defaultIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultIcon", required: false }] }], menuItemSelected: [{ type: i0.Output, args: ["menuItemSelected"] }] } });
399
+ args: [{ selector: 'lib-sidebar-menu', standalone: true, imports: [NgTemplateOutlet, RouterLink, RouterLinkActive, IconComponent], template: "<nav class=\"sidebar-menu\" [class.compact]=\"compact()\" aria-label=\"Sidebar menu\">\r\n @if (errorMessage()) { <div class=\"sidebar-menu__error\" role=\"alert\">{{ errorMessage() }}</div> }\r\n\r\n <ng-template #menuList let-menuItems let-level=\"level\">\r\n @for (item of menuItems; track item.id) {\r\n <div class=\"sidebar-menu__node\" [class.has-open-flyout]=\"compact() && isExpanded(item)\">\r\n @if (hasChildren(item)) {\r\n <button type=\"button\" class=\"sidebar-menu__item\" [class.disabled]=\"item.disabled\" [class.expanded]=\"isExpanded(item) || isActiveBranch(item)\" [class.active-ancestor]=\"isActiveBranch(item)\" [style.position]=\"compact() && level === 0 ? 'relative' : null\"\r\n [attr.aria-expanded]=\"isExpanded(item)\" [attr.aria-haspopup]=\"compact() ? 'menu' : null\"\r\n [attr.title]=\"compact() ? item.label : null\" (click)=\"toggle(item)\">\r\n <span class=\"sidebar-menu__item-main\" [style.position]=\"compact() && level === 0 ? 'absolute' : null\" [style.left]=\"compact() && level === 0 ? '50%' : null\" [style.transform]=\"compact() && level === 0 ? 'translateX(-50%)' : null\"><lib-icon [name]=\"iconFor(item)\" [size]=\"17\" [strokeWidth]=\"isExpanded(item) ? 2.4 : 2\" /><span class=\"sidebar-menu__label\">{{ item.label }}</span></span>\r\n <lib-icon class=\"sidebar-menu__chevron\" [class.expanded]=\"isExpanded(item)\" [name]=\"compact() ? 'chevronRight' : 'chevronDown'\" [size]=\"15\" />\r\n </button>\r\n @if (isExpanded(item)) {\r\n <div class=\"sidebar-menu__children\" [class.sidebar-menu__flyout]=\"compact()\" role=\"menu\" [attr.aria-label]=\"item.label\">\r\n @if (compact()) { <div class=\"sidebar-menu__flyout-title\">{{ item.label }}</div> }\r\n <ng-container [ngTemplateOutlet]=\"menuList\" [ngTemplateOutletContext]=\"{ $implicit: sortedChildren(item), level: level + 1 }\" />\r\n </div>\r\n }\r\n } @else {\r\n <ng-template #content><lib-icon [name]=\"iconFor(item)\" [size]=\"17\" [strokeWidth]=\"2\" /><span class=\"sidebar-menu__label\">{{ item.label }}</span>@if (item.badge !== undefined) { <span class=\"sidebar-menu__badge\">{{ item.badge }}</span> }</ng-template>\r\n @if (hasRoute(item)) {\r\n <a [routerLink]=\"item.route\" routerLinkActive=\"active\" [routerLinkActiveOptions]=\"{ exact: item.exact ?? true }\" class=\"sidebar-menu__item\" [class.disabled]=\"item.disabled\" [attr.title]=\"compact() ? item.label : null\" role=\"menuitem\" (click)=\"onSelect(item)\"><ng-container [ngTemplateOutlet]=\"content\" /></a>\r\n } @else if (hasHref(item)) {\r\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" class=\"sidebar-menu__item\" [class.disabled]=\"item.disabled\" [attr.title]=\"compact() ? item.label : null\" role=\"menuitem\" (click)=\"onSelect(item)\"><ng-container [ngTemplateOutlet]=\"content\" /></a>\r\n } @else {\r\n <button type=\"button\" class=\"sidebar-menu__item\" [class.disabled]=\"item.disabled\" [attr.title]=\"compact() ? item.label : null\" role=\"menuitem\" (click)=\"onSelect(item)\"><ng-container [ngTemplateOutlet]=\"content\" /></button>\r\n }\r\n }\r\n </div>\r\n }\r\n </ng-template>\r\n\r\n <section class=\"sidebar-menu__section\">\r\n <div class=\"sidebar-menu__list\">\r\n <ng-container [ngTemplateOutlet]=\"menuList\" [ngTemplateOutletContext]=\"{ $implicit: sortedItems(), level: 0 }\" />\r\n </div>\r\n </section>\r\n</nav>\r\n", styles: [":host{display:block;min-width:0;overflow:visible}.sidebar-menu{display:flex;flex-direction:column;gap:1rem;padding:0 var(--rail-pad,.75rem) 1rem;overflow:visible}.sidebar-menu__section,.sidebar-menu__list{display:grid;gap:.125rem}.sidebar-menu__heading{margin:0 0 .25rem .5rem;color:var(--rail-muted);font-size:.6875rem;font-weight:600;letter-spacing:.06em;text-transform:uppercase}.sidebar-menu__node{position:relative;min-width:0}.sidebar-menu__item{display:flex;width:100%;height:2rem;align-items:center;gap:.625rem;border:0;border-radius:.4rem;padding:.375rem .5rem;background:transparent;color:var(--rail-text);cursor:pointer;font:inherit;text-align:left;text-decoration:none;transition:background-color .15s,color .15s}.sidebar-menu__item:hover,.sidebar-menu__item.expanded{background:var(--rail-hover);color:var(--rail-text-strong)}.sidebar-menu__item.active{background:var(--rail-active-bg);color:var(--rail-active);font-weight:600}.sidebar-menu__item:focus-visible{outline:2px solid var(--rail-focus);outline-offset:2px}.sidebar-menu__item-main{display:flex;min-width:0;flex:1;align-items:center;gap:.625rem}.sidebar-menu__label{min-width:0;overflow:hidden;font-size:.8125rem;text-overflow:ellipsis;white-space:nowrap}.sidebar-menu__badge{margin-left:auto;color:var(--rail-muted);font-size:.7rem}.sidebar-menu__chevron{color:var(--rail-muted);transition:transform .15s}.sidebar-menu__chevron.expanded{transform:rotate(180deg)}.sidebar-menu__children:not(.sidebar-menu__flyout){display:grid;gap:.125rem;margin:.125rem 0 .125rem .65rem;padding-left:.55rem;border-left:1px solid var(--rail-border)}.sidebar-menu__error{padding:.5rem;color:var(--rail-danger);font-size:.8rem}.disabled{opacity:.45;pointer-events:none}.compact{align-items:center;gap:.5rem;padding-inline:.75rem}.compact .sidebar-menu__section,.compact .sidebar-menu__list{width:100%}.compact .sidebar-menu__heading,.compact>.sidebar-menu__section>.sidebar-menu__list>.sidebar-menu__node>.sidebar-menu__item .sidebar-menu__label,.compact>.sidebar-menu__section>.sidebar-menu__list>.sidebar-menu__node>.sidebar-menu__item .sidebar-menu__badge,.compact>.sidebar-menu__section>.sidebar-menu__list>.sidebar-menu__node>.sidebar-menu__item .sidebar-menu__chevron{display:none}.compact>.sidebar-menu__section>.sidebar-menu__list>.sidebar-menu__node>.sidebar-menu__item{justify-content:center}.compact>.sidebar-menu__section>.sidebar-menu__list>.sidebar-menu__node>.sidebar-menu__item .sidebar-menu__item-main{flex:0}.sidebar-menu__flyout{position:absolute;z-index:30;top:0;left:calc(100% + .65rem);display:grid;width:13rem;gap:.125rem;padding:.4rem;border:1px solid var(--rail-border);border-radius:.55rem;background:var(--rail-popover);box-shadow:var(--rail-popover-shadow)}.sidebar-menu__flyout .sidebar-menu__flyout{left:calc(100% + .5rem);top:-.4rem}.sidebar-menu__flyout-title{padding:.35rem .5rem;color:var(--rail-muted);font-size:.6875rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em}.sidebar-menu__flyout .sidebar-menu__chevron.expanded{transform:rotate(90deg)}\n"] }]
400
+ }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], errorMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessage", required: false }] }], defaultIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultIcon", required: false }] }], compact: [{ type: i0.Input, args: [{ isSignal: true, alias: "compact", required: false }] }], menuItemSelected: [{ type: i0.Output, args: ["menuItemSelected"] }], menuDismissed: [{ type: i0.Output, args: ["menuDismissed"] }] } });
223
401
 
224
402
  class NotificationPanelComponent {
225
403
  notifications = input([], ...(ngDevMode ? [{ debugName: "notifications" }] : /* istanbul ignore next */ []));
@@ -254,12 +432,12 @@ class NotificationPanelComponent {
254
432
  return 'Yesterday';
255
433
  return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
256
434
  }
257
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: NotificationPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
258
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: NotificationPanelComponent, isStandalone: true, selector: "lib-notification-panel", inputs: { notifications: { classPropertyName: "notifications", publicName: "notifications", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { notificationClick: "notificationClick", markRead: "markRead", clearAll: "clearAll" }, ngImport: i0, template: "<div class=\"notification-panel-inner flex flex-col h-full max-h-[380px] w-full text-left\">\r\n <!-- Panel Header -->\r\n <div class=\"flex items-center justify-between border-b border-[var(--border)] px-3.5 py-2\">\r\n <span class=\"text-[10px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Notifications</span>\r\n @if (notifications().length > 0) {\r\n <button\r\n type=\"button\"\r\n class=\"flex items-center gap-1 text-[9px] font-bold text-[var(--secondary)] hover:text-[var(--danger,#ef4444)] hover:underline cursor-pointer\"\r\n (click)=\"onClearAll($event)\"\r\n >\r\n <svg lucideTrash2 class=\"h-3 w-3\"></svg>\r\n <span>Clear All</span>\r\n </button>\r\n }\r\n </div>\r\n\r\n <!-- Panel Body -->\r\n <div class=\"flex-1 overflow-y-auto min-h-0 py-0.5 divide-y divide-[var(--border)]\">\r\n @if (notifications().length === 0) {\r\n <div class=\"flex flex-col items-center justify-center py-10 px-4 text-center select-none\">\r\n <div class=\"flex h-10 w-10 items-center justify-center rounded-full bg-[rgba(var(--secondary),0.06)] text-[var(--secondary)] mb-2.5\">\r\n <svg lucideBellOff class=\"h-4.5 w-4.5\"></svg>\r\n </div>\r\n <p class=\"text-xs font-semibold text-[var(--foreground)]\">All caught up</p>\r\n <p class=\"text-[10px] text-[var(--secondary)] mt-0.5\">No new notifications to display.</p>\r\n </div>\r\n } @else {\r\n @for (item of notifications(); track item.id) {\r\n <div\r\n (click)=\"onNotificationClick(item)\"\r\n class=\"notification-item flex gap-2.5 p-3 transition hover:bg-[rgba(var(--foreground),0.02)] cursor-pointer relative\"\r\n [class.bg-[rgba(var(--primary),0.015)]]=\"!item.read\"\r\n >\r\n <!-- Read status dot -->\r\n <div class=\"flex-shrink-0 pt-1\">\r\n @if (!item.read) {\r\n <span class=\"block h-1.5 w-1.5 rounded-full bg-[var(--primary)] animate-pulse\"></span>\r\n } @else {\r\n <span class=\"block h-1.5 w-1.5 rounded-full bg-transparent\"></span>\r\n }\r\n </div>\r\n\r\n <!-- Notification content -->\r\n <div class=\"flex-1 min-w-0\">\r\n <h4 class=\"text-xs font-semibold text-[var(--foreground)] truncate\" [class.font-bold]=\"!item.read\">\r\n {{ item.title }}\r\n </h4>\r\n <p class=\"text-[10.5px] text-[var(--secondary)] mt-0.5 leading-snug break-words\">\r\n {{ item.message }}\r\n </p>\r\n <span class=\"block text-[9px] text-[var(--secondary)] mt-1 font-medium\">\r\n {{ formatTime(item.createdAt) }}\r\n </span>\r\n </div>\r\n\r\n <!-- Actions -->\r\n @if (!item.read) {\r\n <div class=\"flex items-center flex-shrink-0\">\r\n <button\r\n type=\"button\"\r\n class=\"mark-read-btn grid h-6 w-6 place-items-center rounded-full text-[var(--secondary)] hover:text-[var(--primary)] hover:bg-[rgba(var(--foreground),0.05)] cursor-pointer\"\r\n [title]=\"'Mark as read'\"\r\n (click)=\"onMarkRead(item.id, $event)\"\r\n >\r\n <svg lucideCheckCheck class=\"h-3.5 w-3.5\"></svg>\r\n </button>\r\n </div>\r\n }\r\n </div>\r\n }\r\n }\r\n </div>\r\n</div>\r\n", styles: [":host{display:block;width:100%}.notification-item{border-left:2px solid transparent}.notification-item:hover{border-left-color:var(--primary)}.mark-read-btn{opacity:.5;transition:opacity .2s,background-color .2s}.notification-item:hover .mark-read-btn{opacity:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: LucideBellOff, selector: "svg[lucideBellOff]" }, { kind: "component", type: LucideCheckCheck, selector: "svg[lucideCheckCheck]" }, { kind: "component", type: LucideTrash2, selector: "svg[lucideTrash2]" }] });
435
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: NotificationPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
436
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: NotificationPanelComponent, isStandalone: true, selector: "lib-notification-panel", inputs: { notifications: { classPropertyName: "notifications", publicName: "notifications", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { notificationClick: "notificationClick", markRead: "markRead", clearAll: "clearAll" }, ngImport: i0, template: "<div class=\"notification-panel-inner flex flex-col h-full max-h-[380px] w-full text-left\">\r\n <!-- Panel Header -->\r\n <div class=\"flex items-center justify-between border-b border-[var(--border)] px-3.5 py-2\">\r\n <span class=\"text-[10px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Notifications</span>\r\n @if (notifications().length > 0) {\r\n <button\r\n type=\"button\"\r\n class=\"flex items-center gap-1 text-[9px] font-bold text-[var(--secondary)] hover:text-[var(--danger,#ef4444)] hover:underline cursor-pointer\"\r\n (click)=\"onClearAll($event)\"\r\n >\r\n <svg lucideTrash2 class=\"h-3 w-3\"></svg>\r\n <span>Clear All</span>\r\n </button>\r\n }\r\n </div>\r\n\r\n <!-- Panel Body -->\r\n <div class=\"flex-1 overflow-y-auto min-h-0 py-0.5 divide-y divide-[var(--border)]\">\r\n @if (notifications().length === 0) {\r\n <div class=\"flex flex-col items-center justify-center py-10 px-4 text-center select-none\">\r\n <div class=\"flex h-10 w-10 items-center justify-center rounded-full bg-[rgba(var(--secondary),0.06)] text-[var(--secondary)] mb-2.5\">\r\n <svg lucideBellOff class=\"h-4.5 w-4.5\"></svg>\r\n </div>\r\n <p class=\"text-xs font-semibold text-[var(--foreground)]\">All caught up</p>\r\n <p class=\"text-[10px] text-[var(--secondary)] mt-0.5\">No new notifications to display.</p>\r\n </div>\r\n } @else {\r\n @for (item of notifications(); track item.id) {\r\n <div\r\n (click)=\"onNotificationClick(item)\"\r\n class=\"notification-item flex gap-2.5 p-3 transition hover:bg-[rgba(var(--foreground),0.02)] cursor-pointer relative\"\r\n [class.bg-[rgba(var(--primary),0.015)]]=\"!item.read\"\r\n >\r\n <!-- Read status dot -->\r\n <div class=\"flex-shrink-0 pt-1\">\r\n @if (!item.read) {\r\n <span class=\"block h-1.5 w-1.5 rounded-full bg-[var(--primary)] animate-pulse\"></span>\r\n } @else {\r\n <span class=\"block h-1.5 w-1.5 rounded-full bg-transparent\"></span>\r\n }\r\n </div>\r\n\r\n <!-- Notification content -->\r\n <div class=\"flex-1 min-w-0\">\r\n <h4 class=\"text-xs font-semibold text-[var(--foreground)] truncate\" [class.font-bold]=\"!item.read\">\r\n {{ item.title }}\r\n </h4>\r\n <p class=\"text-[10.5px] text-[var(--secondary)] mt-0.5 leading-snug break-words\">\r\n {{ item.message }}\r\n </p>\r\n <span class=\"block text-[9px] text-[var(--secondary)] mt-1 font-medium\">\r\n {{ formatTime(item.createdAt) }}\r\n </span>\r\n </div>\r\n\r\n <!-- Actions -->\r\n @if (!item.read) {\r\n <div class=\"flex items-center flex-shrink-0\">\r\n <button\r\n type=\"button\"\r\n class=\"mark-read-btn grid h-6 w-6 place-items-center rounded-full text-[var(--secondary)] hover:text-[var(--primary)] hover:bg-[rgba(var(--foreground),0.05)] cursor-pointer\"\r\n [title]=\"'Mark as read'\"\r\n (click)=\"onMarkRead(item.id, $event)\"\r\n >\r\n <svg lucideCheckCheck class=\"h-3.5 w-3.5\"></svg>\r\n </button>\r\n </div>\r\n }\r\n </div>\r\n }\r\n }\r\n </div>\r\n</div>\r\n", styles: [":host{display:block;width:100%}.notification-item{border-left:2px solid transparent}.notification-item:hover{border-left-color:var(--primary)}.mark-read-btn{opacity:.5;transition:opacity .2s,background-color .2s}.notification-item:hover .mark-read-btn{opacity:1}\n"], dependencies: [{ kind: "component", type: LucideBellOff, selector: "svg[lucideBellOff]" }, { kind: "component", type: LucideCheckCheck, selector: "svg[lucideCheckCheck]" }, { kind: "component", type: LucideTrash2, selector: "svg[lucideTrash2]" }] });
259
437
  }
260
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: NotificationPanelComponent, decorators: [{
438
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: NotificationPanelComponent, decorators: [{
261
439
  type: Component,
262
- args: [{ selector: 'lib-notification-panel', standalone: true, imports: [CommonModule, LucideBellOff, LucideCheckCheck, LucideTrash2], template: "<div class=\"notification-panel-inner flex flex-col h-full max-h-[380px] w-full text-left\">\r\n <!-- Panel Header -->\r\n <div class=\"flex items-center justify-between border-b border-[var(--border)] px-3.5 py-2\">\r\n <span class=\"text-[10px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Notifications</span>\r\n @if (notifications().length > 0) {\r\n <button\r\n type=\"button\"\r\n class=\"flex items-center gap-1 text-[9px] font-bold text-[var(--secondary)] hover:text-[var(--danger,#ef4444)] hover:underline cursor-pointer\"\r\n (click)=\"onClearAll($event)\"\r\n >\r\n <svg lucideTrash2 class=\"h-3 w-3\"></svg>\r\n <span>Clear All</span>\r\n </button>\r\n }\r\n </div>\r\n\r\n <!-- Panel Body -->\r\n <div class=\"flex-1 overflow-y-auto min-h-0 py-0.5 divide-y divide-[var(--border)]\">\r\n @if (notifications().length === 0) {\r\n <div class=\"flex flex-col items-center justify-center py-10 px-4 text-center select-none\">\r\n <div class=\"flex h-10 w-10 items-center justify-center rounded-full bg-[rgba(var(--secondary),0.06)] text-[var(--secondary)] mb-2.5\">\r\n <svg lucideBellOff class=\"h-4.5 w-4.5\"></svg>\r\n </div>\r\n <p class=\"text-xs font-semibold text-[var(--foreground)]\">All caught up</p>\r\n <p class=\"text-[10px] text-[var(--secondary)] mt-0.5\">No new notifications to display.</p>\r\n </div>\r\n } @else {\r\n @for (item of notifications(); track item.id) {\r\n <div\r\n (click)=\"onNotificationClick(item)\"\r\n class=\"notification-item flex gap-2.5 p-3 transition hover:bg-[rgba(var(--foreground),0.02)] cursor-pointer relative\"\r\n [class.bg-[rgba(var(--primary),0.015)]]=\"!item.read\"\r\n >\r\n <!-- Read status dot -->\r\n <div class=\"flex-shrink-0 pt-1\">\r\n @if (!item.read) {\r\n <span class=\"block h-1.5 w-1.5 rounded-full bg-[var(--primary)] animate-pulse\"></span>\r\n } @else {\r\n <span class=\"block h-1.5 w-1.5 rounded-full bg-transparent\"></span>\r\n }\r\n </div>\r\n\r\n <!-- Notification content -->\r\n <div class=\"flex-1 min-w-0\">\r\n <h4 class=\"text-xs font-semibold text-[var(--foreground)] truncate\" [class.font-bold]=\"!item.read\">\r\n {{ item.title }}\r\n </h4>\r\n <p class=\"text-[10.5px] text-[var(--secondary)] mt-0.5 leading-snug break-words\">\r\n {{ item.message }}\r\n </p>\r\n <span class=\"block text-[9px] text-[var(--secondary)] mt-1 font-medium\">\r\n {{ formatTime(item.createdAt) }}\r\n </span>\r\n </div>\r\n\r\n <!-- Actions -->\r\n @if (!item.read) {\r\n <div class=\"flex items-center flex-shrink-0\">\r\n <button\r\n type=\"button\"\r\n class=\"mark-read-btn grid h-6 w-6 place-items-center rounded-full text-[var(--secondary)] hover:text-[var(--primary)] hover:bg-[rgba(var(--foreground),0.05)] cursor-pointer\"\r\n [title]=\"'Mark as read'\"\r\n (click)=\"onMarkRead(item.id, $event)\"\r\n >\r\n <svg lucideCheckCheck class=\"h-3.5 w-3.5\"></svg>\r\n </button>\r\n </div>\r\n }\r\n </div>\r\n }\r\n }\r\n </div>\r\n</div>\r\n", styles: [":host{display:block;width:100%}.notification-item{border-left:2px solid transparent}.notification-item:hover{border-left-color:var(--primary)}.mark-read-btn{opacity:.5;transition:opacity .2s,background-color .2s}.notification-item:hover .mark-read-btn{opacity:1}\n"] }]
440
+ args: [{ selector: 'lib-notification-panel', standalone: true, imports: [LucideBellOff, LucideCheckCheck, LucideTrash2], template: "<div class=\"notification-panel-inner flex flex-col h-full max-h-[380px] w-full text-left\">\r\n <!-- Panel Header -->\r\n <div class=\"flex items-center justify-between border-b border-[var(--border)] px-3.5 py-2\">\r\n <span class=\"text-[10px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Notifications</span>\r\n @if (notifications().length > 0) {\r\n <button\r\n type=\"button\"\r\n class=\"flex items-center gap-1 text-[9px] font-bold text-[var(--secondary)] hover:text-[var(--danger,#ef4444)] hover:underline cursor-pointer\"\r\n (click)=\"onClearAll($event)\"\r\n >\r\n <svg lucideTrash2 class=\"h-3 w-3\"></svg>\r\n <span>Clear All</span>\r\n </button>\r\n }\r\n </div>\r\n\r\n <!-- Panel Body -->\r\n <div class=\"flex-1 overflow-y-auto min-h-0 py-0.5 divide-y divide-[var(--border)]\">\r\n @if (notifications().length === 0) {\r\n <div class=\"flex flex-col items-center justify-center py-10 px-4 text-center select-none\">\r\n <div class=\"flex h-10 w-10 items-center justify-center rounded-full bg-[rgba(var(--secondary),0.06)] text-[var(--secondary)] mb-2.5\">\r\n <svg lucideBellOff class=\"h-4.5 w-4.5\"></svg>\r\n </div>\r\n <p class=\"text-xs font-semibold text-[var(--foreground)]\">All caught up</p>\r\n <p class=\"text-[10px] text-[var(--secondary)] mt-0.5\">No new notifications to display.</p>\r\n </div>\r\n } @else {\r\n @for (item of notifications(); track item.id) {\r\n <div\r\n (click)=\"onNotificationClick(item)\"\r\n class=\"notification-item flex gap-2.5 p-3 transition hover:bg-[rgba(var(--foreground),0.02)] cursor-pointer relative\"\r\n [class.bg-[rgba(var(--primary),0.015)]]=\"!item.read\"\r\n >\r\n <!-- Read status dot -->\r\n <div class=\"flex-shrink-0 pt-1\">\r\n @if (!item.read) {\r\n <span class=\"block h-1.5 w-1.5 rounded-full bg-[var(--primary)] animate-pulse\"></span>\r\n } @else {\r\n <span class=\"block h-1.5 w-1.5 rounded-full bg-transparent\"></span>\r\n }\r\n </div>\r\n\r\n <!-- Notification content -->\r\n <div class=\"flex-1 min-w-0\">\r\n <h4 class=\"text-xs font-semibold text-[var(--foreground)] truncate\" [class.font-bold]=\"!item.read\">\r\n {{ item.title }}\r\n </h4>\r\n <p class=\"text-[10.5px] text-[var(--secondary)] mt-0.5 leading-snug break-words\">\r\n {{ item.message }}\r\n </p>\r\n <span class=\"block text-[9px] text-[var(--secondary)] mt-1 font-medium\">\r\n {{ formatTime(item.createdAt) }}\r\n </span>\r\n </div>\r\n\r\n <!-- Actions -->\r\n @if (!item.read) {\r\n <div class=\"flex items-center flex-shrink-0\">\r\n <button\r\n type=\"button\"\r\n class=\"mark-read-btn grid h-6 w-6 place-items-center rounded-full text-[var(--secondary)] hover:text-[var(--primary)] hover:bg-[rgba(var(--foreground),0.05)] cursor-pointer\"\r\n [title]=\"'Mark as read'\"\r\n (click)=\"onMarkRead(item.id, $event)\"\r\n >\r\n <svg lucideCheckCheck class=\"h-3.5 w-3.5\"></svg>\r\n </button>\r\n </div>\r\n }\r\n </div>\r\n }\r\n }\r\n </div>\r\n</div>\r\n", styles: [":host{display:block;width:100%}.notification-item{border-left:2px solid transparent}.notification-item:hover{border-left-color:var(--primary)}.mark-read-btn{opacity:.5;transition:opacity .2s,background-color .2s}.notification-item:hover .mark-read-btn{opacity:1}\n"] }]
263
441
  }], propDecorators: { notifications: [{ type: i0.Input, args: [{ isSignal: true, alias: "notifications", required: false }] }], notificationClick: [{ type: i0.Output, args: ["notificationClick"] }], markRead: [{ type: i0.Output, args: ["markRead"] }], clearAll: [{ type: i0.Output, args: ["clearAll"] }] } });
264
442
 
265
443
  class NotificationBellComponent {
@@ -291,12 +469,12 @@ class NotificationBellComponent {
291
469
  this.closePanel();
292
470
  }
293
471
  }
294
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: NotificationBellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
295
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: NotificationBellComponent, isStandalone: true, selector: "lib-notification-bell", inputs: { unreadCount: { classPropertyName: "unreadCount", publicName: "unreadCount", isSignal: true, isRequired: false, transformFunction: null }, notifications: { classPropertyName: "notifications", publicName: "notifications", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { notificationClick: "notificationClick", markRead: "markRead", clearAll: "clearAll" }, host: { listeners: { "document:click": "onOutsideClick($event)" } }, ngImport: i0, template: "<div class=\"notification-bell-wrapper relative inline-block text-left\">\r\n <button\r\n type=\"button\"\r\n class=\"notification-trigger\"\r\n (click)=\"togglePanel()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"View notifications\"\r\n >\r\n <svg lucideBell class=\"h-4.5 w-4.5\"></svg>\r\n \r\n @if (unreadCount() > 0) {\r\n <span class=\"absolute top-1.5 right-1.5 flex h-2 w-2\">\r\n <span class=\"animate-ping absolute inline-flex h-full w-full rounded-full bg-[var(--danger,#ef4444)] opacity-75\"></span>\r\n <span class=\"relative inline-flex rounded-full h-2 w-2 bg-[var(--danger,#ef4444)]\"></span>\r\n </span>\r\n }\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"notification-dropdown fixed left-2 right-2 top-14 mt-2 origin-top rounded-xl border border-[var(--border)] bg-[var(--background)] p-0 shadow-lg focus:outline-none z-50 sm:absolute sm:left-auto sm:right-0 sm:top-auto sm:w-80 sm:origin-top-right\">\r\n <lib-notification-panel\r\n [notifications]=\"notifications()\"\r\n (notificationClick)=\"onNotificationClick($event)\"\r\n (markRead)=\"onMarkRead($event)\"\r\n (clearAll)=\"onClearAll()\"\r\n />\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:inline-block}.notification-trigger{position:relative;display:grid;width:2.15rem;height:2.15rem;place-items:center;border:1px solid transparent;border-radius:.5rem;color:var(--foreground);background:transparent;cursor:pointer;transition:background-color .15s ease,border-color .15s ease}.notification-trigger:hover{border-color:var(--border);background:color-mix(in srgb,var(--border) 35%,transparent)}.notification-trigger:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.notification-dropdown{box-shadow:0 12px 30px #0f172a24;animation:bellEnter .15s cubic-bezier(.16,1,.3,1);overflow:hidden}@keyframes bellEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: NotificationPanelComponent, selector: "lib-notification-panel", inputs: ["notifications"], outputs: ["notificationClick", "markRead", "clearAll"] }, { kind: "component", type: LucideBell, selector: "svg[lucideBell]" }] });
472
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: NotificationBellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
473
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: NotificationBellComponent, isStandalone: true, selector: "lib-notification-bell", inputs: { unreadCount: { classPropertyName: "unreadCount", publicName: "unreadCount", isSignal: true, isRequired: false, transformFunction: null }, notifications: { classPropertyName: "notifications", publicName: "notifications", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { notificationClick: "notificationClick", markRead: "markRead", clearAll: "clearAll" }, host: { listeners: { "document:click": "onOutsideClick($event)" } }, ngImport: i0, template: "<div class=\"notification-bell-wrapper relative inline-block text-left\">\r\n <button\r\n type=\"button\"\r\n class=\"notification-trigger\"\r\n (click)=\"togglePanel()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"View notifications\"\r\n >\r\n <svg lucideBell class=\"h-4.5 w-4.5\"></svg>\r\n \r\n @if (unreadCount() > 0) {\r\n <span class=\"absolute top-1.5 right-1.5 flex h-2 w-2\">\r\n <span class=\"animate-ping absolute inline-flex h-full w-full rounded-full bg-[var(--danger,#ef4444)] opacity-75\"></span>\r\n <span class=\"relative inline-flex rounded-full h-2 w-2 bg-[var(--danger,#ef4444)]\"></span>\r\n </span>\r\n }\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"notification-dropdown fixed left-2 right-2 top-14 mt-2 origin-top rounded-xl border border-[var(--border)] bg-[var(--background)] p-0 shadow-lg focus:outline-none z-50 sm:absolute sm:left-auto sm:right-0 sm:top-auto sm:w-80 sm:origin-top-right\">\r\n <lib-notification-panel\r\n [notifications]=\"notifications()\"\r\n (notificationClick)=\"onNotificationClick($event)\"\r\n (markRead)=\"onMarkRead($event)\"\r\n (clearAll)=\"onClearAll()\"\r\n />\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:inline-block}.notification-trigger{position:relative;display:grid;width:2.15rem;height:2.15rem;place-items:center;border:1px solid transparent;border-radius:.5rem;color:var(--foreground);background:transparent;cursor:pointer;transition:background-color .15s ease,border-color .15s ease}.notification-trigger:hover{border-color:var(--border);background:color-mix(in srgb,var(--border) 35%,transparent)}.notification-trigger:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.notification-dropdown{box-shadow:0 12px 30px #0f172a24;animation:bellEnter .15s cubic-bezier(.16,1,.3,1);overflow:hidden}@keyframes bellEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}\n"], dependencies: [{ kind: "component", type: NotificationPanelComponent, selector: "lib-notification-panel", inputs: ["notifications"], outputs: ["notificationClick", "markRead", "clearAll"] }, { kind: "component", type: LucideBell, selector: "svg[lucideBell]" }] });
296
474
  }
297
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: NotificationBellComponent, decorators: [{
475
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: NotificationBellComponent, decorators: [{
298
476
  type: Component,
299
- args: [{ selector: 'lib-notification-bell', standalone: true, imports: [CommonModule, NotificationPanelComponent, LucideBell], template: "<div class=\"notification-bell-wrapper relative inline-block text-left\">\r\n <button\r\n type=\"button\"\r\n class=\"notification-trigger\"\r\n (click)=\"togglePanel()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"View notifications\"\r\n >\r\n <svg lucideBell class=\"h-4.5 w-4.5\"></svg>\r\n \r\n @if (unreadCount() > 0) {\r\n <span class=\"absolute top-1.5 right-1.5 flex h-2 w-2\">\r\n <span class=\"animate-ping absolute inline-flex h-full w-full rounded-full bg-[var(--danger,#ef4444)] opacity-75\"></span>\r\n <span class=\"relative inline-flex rounded-full h-2 w-2 bg-[var(--danger,#ef4444)]\"></span>\r\n </span>\r\n }\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"notification-dropdown fixed left-2 right-2 top-14 mt-2 origin-top rounded-xl border border-[var(--border)] bg-[var(--background)] p-0 shadow-lg focus:outline-none z-50 sm:absolute sm:left-auto sm:right-0 sm:top-auto sm:w-80 sm:origin-top-right\">\r\n <lib-notification-panel\r\n [notifications]=\"notifications()\"\r\n (notificationClick)=\"onNotificationClick($event)\"\r\n (markRead)=\"onMarkRead($event)\"\r\n (clearAll)=\"onClearAll()\"\r\n />\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:inline-block}.notification-trigger{position:relative;display:grid;width:2.15rem;height:2.15rem;place-items:center;border:1px solid transparent;border-radius:.5rem;color:var(--foreground);background:transparent;cursor:pointer;transition:background-color .15s ease,border-color .15s ease}.notification-trigger:hover{border-color:var(--border);background:color-mix(in srgb,var(--border) 35%,transparent)}.notification-trigger:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.notification-dropdown{box-shadow:0 12px 30px #0f172a24;animation:bellEnter .15s cubic-bezier(.16,1,.3,1);overflow:hidden}@keyframes bellEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}\n"] }]
477
+ args: [{ selector: 'lib-notification-bell', standalone: true, imports: [NotificationPanelComponent, LucideBell], template: "<div class=\"notification-bell-wrapper relative inline-block text-left\">\r\n <button\r\n type=\"button\"\r\n class=\"notification-trigger\"\r\n (click)=\"togglePanel()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"View notifications\"\r\n >\r\n <svg lucideBell class=\"h-4.5 w-4.5\"></svg>\r\n \r\n @if (unreadCount() > 0) {\r\n <span class=\"absolute top-1.5 right-1.5 flex h-2 w-2\">\r\n <span class=\"animate-ping absolute inline-flex h-full w-full rounded-full bg-[var(--danger,#ef4444)] opacity-75\"></span>\r\n <span class=\"relative inline-flex rounded-full h-2 w-2 bg-[var(--danger,#ef4444)]\"></span>\r\n </span>\r\n }\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"notification-dropdown fixed left-2 right-2 top-14 mt-2 origin-top rounded-xl border border-[var(--border)] bg-[var(--background)] p-0 shadow-lg focus:outline-none z-50 sm:absolute sm:left-auto sm:right-0 sm:top-auto sm:w-80 sm:origin-top-right\">\r\n <lib-notification-panel\r\n [notifications]=\"notifications()\"\r\n (notificationClick)=\"onNotificationClick($event)\"\r\n (markRead)=\"onMarkRead($event)\"\r\n (clearAll)=\"onClearAll()\"\r\n />\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:inline-block}.notification-trigger{position:relative;display:grid;width:2.15rem;height:2.15rem;place-items:center;border:1px solid transparent;border-radius:.5rem;color:var(--foreground);background:transparent;cursor:pointer;transition:background-color .15s ease,border-color .15s ease}.notification-trigger:hover{border-color:var(--border);background:color-mix(in srgb,var(--border) 35%,transparent)}.notification-trigger:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.notification-dropdown{box-shadow:0 12px 30px #0f172a24;animation:bellEnter .15s cubic-bezier(.16,1,.3,1);overflow:hidden}@keyframes bellEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}\n"] }]
300
478
  }], propDecorators: { unreadCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "unreadCount", required: false }] }], notifications: [{ type: i0.Input, args: [{ isSignal: true, alias: "notifications", required: false }] }], notificationClick: [{ type: i0.Output, args: ["notificationClick"] }], markRead: [{ type: i0.Output, args: ["markRead"] }], clearAll: [{ type: i0.Output, args: ["clearAll"] }], onOutsideClick: [{
301
479
  type: HostListener,
302
480
  args: ['document:click', ['$event']]
@@ -323,12 +501,12 @@ class UserAvatarComponent {
323
501
  onImageError() {
324
502
  this.imageError = true;
325
503
  }
326
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: UserAvatarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
327
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: UserAvatarComponent, isStandalone: true, selector: "lib-user-avatar", inputs: { user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"avatar-container\">\r\n @if (user()?.avatar && !imageError) {\r\n <img [src]=\"user()?.avatar\" [alt]=\"user()?.name || 'User avatar'\" class=\"avatar-img\" (error)=\"onImageError()\" />\r\n } @else {\r\n <span class=\"avatar-initials\">{{ initials() }}</span>\r\n }\r\n</div>\r\n", styles: [":host{display:block;width:100%;height:100%}.avatar-container{display:grid;width:100%;height:100%;place-items:center;overflow:hidden;border:1px solid var(--border);border-radius:999px;background:linear-gradient(135deg,var(--primary, #0067b8) 0%,var(--secondary, #005a9e) 100%);color:var(--background, #ffffff);box-shadow:0 1px 3px #0000000d;-webkit-user-select:none;user-select:none;transition:box-shadow .2s ease,transform .2s ease}.avatar-img{display:block;width:100%;height:100%;object-fit:cover}:host:hover .avatar-container{transform:scale(1.02);box-shadow:0 4px 6px -1px #00000014,0 2px 4px -1px #0000000a}.avatar-initials{font-size:.75rem;font-weight:750;line-height:1;text-transform:uppercase;text-shadow:0 1px 1px rgba(0,0,0,.15)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
504
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: UserAvatarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
505
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: UserAvatarComponent, isStandalone: true, selector: "lib-user-avatar", inputs: { user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"avatar-container\">\r\n @if (user()?.avatar && !imageError) {\r\n <img [src]=\"user()?.avatar\" [alt]=\"user()?.name || 'User avatar'\" class=\"avatar-img\" (error)=\"onImageError()\" />\r\n } @else {\r\n <span class=\"avatar-initials\">{{ initials() }}</span>\r\n }\r\n</div>\r\n", styles: [":host{display:block;width:100%;height:100%}.avatar-container{display:grid;width:100%;height:100%;place-items:center;overflow:hidden;border:1px solid var(--border);border-radius:999px;background:linear-gradient(135deg,var(--primary, #0067b8) 0%,var(--secondary, #005a9e) 100%);color:var(--background, #ffffff);box-shadow:0 1px 3px #0000000d;-webkit-user-select:none;user-select:none;transition:box-shadow .2s ease,transform .2s ease}.avatar-img{display:block;width:100%;height:100%;object-fit:cover}:host:hover .avatar-container{transform:scale(1.02);box-shadow:0 4px 6px -1px #00000014,0 2px 4px -1px #0000000a}.avatar-initials{font-size:.75rem;font-weight:750;line-height:1;text-transform:uppercase;text-shadow:0 1px 1px rgba(0,0,0,.15)}\n"] });
328
506
  }
329
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: UserAvatarComponent, decorators: [{
507
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: UserAvatarComponent, decorators: [{
330
508
  type: Component,
331
- args: [{ selector: 'lib-user-avatar', standalone: true, imports: [CommonModule], template: "<div class=\"avatar-container\">\r\n @if (user()?.avatar && !imageError) {\r\n <img [src]=\"user()?.avatar\" [alt]=\"user()?.name || 'User avatar'\" class=\"avatar-img\" (error)=\"onImageError()\" />\r\n } @else {\r\n <span class=\"avatar-initials\">{{ initials() }}</span>\r\n }\r\n</div>\r\n", styles: [":host{display:block;width:100%;height:100%}.avatar-container{display:grid;width:100%;height:100%;place-items:center;overflow:hidden;border:1px solid var(--border);border-radius:999px;background:linear-gradient(135deg,var(--primary, #0067b8) 0%,var(--secondary, #005a9e) 100%);color:var(--background, #ffffff);box-shadow:0 1px 3px #0000000d;-webkit-user-select:none;user-select:none;transition:box-shadow .2s ease,transform .2s ease}.avatar-img{display:block;width:100%;height:100%;object-fit:cover}:host:hover .avatar-container{transform:scale(1.02);box-shadow:0 4px 6px -1px #00000014,0 2px 4px -1px #0000000a}.avatar-initials{font-size:.75rem;font-weight:750;line-height:1;text-transform:uppercase;text-shadow:0 1px 1px rgba(0,0,0,.15)}\n"] }]
509
+ args: [{ selector: 'lib-user-avatar', standalone: true, imports: [], template: "<div class=\"avatar-container\">\r\n @if (user()?.avatar && !imageError) {\r\n <img [src]=\"user()?.avatar\" [alt]=\"user()?.name || 'User avatar'\" class=\"avatar-img\" (error)=\"onImageError()\" />\r\n } @else {\r\n <span class=\"avatar-initials\">{{ initials() }}</span>\r\n }\r\n</div>\r\n", styles: [":host{display:block;width:100%;height:100%}.avatar-container{display:grid;width:100%;height:100%;place-items:center;overflow:hidden;border:1px solid var(--border);border-radius:999px;background:linear-gradient(135deg,var(--primary, #0067b8) 0%,var(--secondary, #005a9e) 100%);color:var(--background, #ffffff);box-shadow:0 1px 3px #0000000d;-webkit-user-select:none;user-select:none;transition:box-shadow .2s ease,transform .2s ease}.avatar-img{display:block;width:100%;height:100%;object-fit:cover}:host:hover .avatar-container{transform:scale(1.02);box-shadow:0 4px 6px -1px #00000014,0 2px 4px -1px #0000000a}.avatar-initials{font-size:.75rem;font-weight:750;line-height:1;text-transform:uppercase;text-shadow:0 1px 1px rgba(0,0,0,.15)}\n"] }]
332
510
  }], propDecorators: { user: [{ type: i0.Input, args: [{ isSignal: true, alias: "user", required: false }] }] } });
333
511
 
334
512
  class UserMenuComponent {
@@ -352,116 +530,22 @@ class UserMenuComponent {
352
530
  this.closeMenu();
353
531
  }
354
532
  }
355
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: UserMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
356
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: UserMenuComponent, isStandalone: true, selector: "lib-user-menu", inputs: { user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { menuAction: "menuAction" }, host: { listeners: { "document:click": "onOutsideClick($event)" } }, ngImport: i0, template: "<div class=\"user-menu-wrapper\">\r\n <button type=\"button\" class=\"user-menu-trigger\" (click)=\"toggleMenu()\" aria-haspopup=\"true\" [aria-expanded]=\"isOpen()\"\r\n aria-label=\"User profile menu\">\r\n <div class=\"user-menu-avatar\">\r\n <lib-user-avatar [user]=\"user()\" />\r\n </div>\r\n <svg lucideChevronDown class=\"user-menu-chevron\"\r\n [class.rotate-180]=\"isOpen()\"></svg>\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"user-dropdown-panel\">\r\n @if (user(); as u) {\r\n <div class=\"user-dropdown-profile\">\r\n <p class=\"user-dropdown-name\" [title]=\"u.name\">{{ u.name }}</p>\r\n <p class=\"user-dropdown-email\" [title]=\"u.email\">{{ u.email }}</p>\r\n </div>\r\n }\r\n\r\n <div class=\"user-menu-list\">\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('profile')\">\r\n <svg lucideUser class=\"user-menu-icon\"></svg>\r\n <span>My Profile</span>\r\n </button>\r\n\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('settings')\">\r\n <svg lucideSettings class=\"user-menu-icon\"></svg>\r\n <span>Settings</span>\r\n </button>\r\n\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('billing')\">\r\n <svg lucideCreditCard class=\"user-menu-icon\"></svg>\r\n <span>Billing</span>\r\n </button>\r\n </div>\r\n\r\n <div class=\"user-menu-danger-zone\">\r\n <button type=\"button\" class=\"user-menu-item danger\" (click)=\"onAction('logout')\">\r\n <svg lucideLogOut class=\"user-menu-icon\"></svg>\r\n <span>Sign Out</span>\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:inline-block}.user-menu-wrapper{position:relative;display:inline-block;text-align:left}.user-menu-trigger{display:flex;align-items:center;justify-content:center;gap:.35rem;min-height:2.25rem;border:1px solid var(--border);border-radius:999px;color:var(--foreground);background:var(--background);padding:.2rem .5rem .2rem .2rem;cursor:pointer;transition:background-color .15s ease,border-color .15s ease,box-shadow .15s ease}.user-menu-avatar{display:block;width:1.65rem;height:1.65rem;flex:0 0 1.65rem;overflow:hidden;border-radius:999px}.user-menu-chevron{width:.875rem;height:.875rem;color:var(--secondary);transition:transform .2s ease}.user-menu-chevron.rotate-180{transform:rotate(180deg)}.user-menu-trigger:hover{background:color-mix(in srgb,var(--border) 28%,transparent)}.user-menu-trigger:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.user-dropdown-panel{position:fixed;z-index:50;top:3.75rem;right:.5rem;left:.5rem;overflow:hidden;border:1px solid var(--border);border-radius:.875rem;background:var(--background);padding:.375rem;box-shadow:0 12px 30px #0f172a24;animation:dropdownEnter .15s cubic-bezier(.16,1,.3,1)}.user-dropdown-profile{border-bottom:1px solid var(--border);margin-bottom:.25rem;padding:.65rem .75rem}.user-dropdown-name,.user-dropdown-email{overflow:hidden;margin:0;text-overflow:ellipsis;white-space:nowrap}.user-dropdown-name{color:var(--foreground);font-size:.875rem;font-weight:750}.user-dropdown-email{margin-top:.125rem;color:var(--secondary);font-size:.78rem}.user-menu-list{display:grid;gap:.125rem;padding:.125rem 0}.user-menu-item{display:flex;width:100%;align-items:center;gap:.625rem;border:0;border-radius:.5rem;color:var(--foreground);background:transparent;padding:.55rem .75rem;font-size:.875rem;text-align:left;cursor:pointer;transition:background-color .15s ease,color .15s ease}.user-menu-icon{width:1rem;height:1rem;flex-shrink:0;color:var(--secondary)}.user-menu-item:hover{background:color-mix(in srgb,var(--border) 32%,transparent)}.user-menu-item.danger,.user-menu-item.danger .user-menu-icon{color:var(--danger, #ef4444)}.user-menu-item.danger:hover{background:color-mix(in srgb,var(--danger, #ef4444) 10%,transparent)}.user-menu-danger-zone{border-top:1px solid var(--border);margin-top:.25rem;padding-top:.25rem}@keyframes dropdownEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}@media(min-width:640px){.user-dropdown-panel{position:absolute;top:calc(100% + .5rem);right:0;left:auto;width:14rem;max-width:calc(100vw - 1rem);transform-origin:top right}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: UserAvatarComponent, selector: "lib-user-avatar", inputs: ["user"] }, { kind: "component", type: LucideChevronDown, selector: "svg[lucideChevronDown]" }, { kind: "component", type: LucideCreditCard, selector: "svg[lucideCreditCard]" }, { kind: "component", type: LucideLogOut, selector: "svg[lucideLogOut]" }, { kind: "component", type: LucideSettings, selector: "svg[lucideSettings]" }, { kind: "component", type: LucideUser, selector: "svg[lucideUser]" }] });
533
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: UserMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
534
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: UserMenuComponent, isStandalone: true, selector: "lib-user-menu", inputs: { user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { menuAction: "menuAction" }, host: { listeners: { "document:click": "onOutsideClick($event)" } }, ngImport: i0, template: "<div class=\"user-menu-wrapper\">\r\n <button type=\"button\" class=\"user-menu-trigger\" (click)=\"toggleMenu()\" aria-haspopup=\"true\" [aria-expanded]=\"isOpen()\"\r\n aria-label=\"User profile menu\">\r\n <div class=\"user-menu-avatar\">\r\n <lib-user-avatar [user]=\"user()\" />\r\n </div>\r\n <svg lucideChevronDown class=\"user-menu-chevron\" [class.rotate-180]=\"isOpen()\"></svg>\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"user-dropdown-panel\">\r\n @if (user(); as u) {\r\n <div class=\"user-dropdown-profile\">\r\n <p class=\"user-dropdown-name\" [title]=\"u.name\">{{ u.name }}</p>\r\n <p class=\"user-dropdown-email\" [title]=\"u.email\">{{ u.email }}</p>\r\n </div>\r\n }\r\n\r\n <div class=\"user-menu-list\">\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('profile')\">\r\n <svg lucideUser class=\"user-menu-icon\"></svg>\r\n <span>My Profile</span>\r\n </button>\r\n\r\n <!-- <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('settings')\">\r\n <svg lucideSettings class=\"user-menu-icon\"></svg>\r\n <span>Settings</span>\r\n </button>\r\n\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('billing')\">\r\n <svg lucideCreditCard class=\"user-menu-icon\"></svg>\r\n <span>Billing</span>\r\n </button> -->\r\n </div>\r\n\r\n <div class=\"user-menu-danger-zone\">\r\n <button type=\"button\" class=\"user-menu-item danger\" (click)=\"onAction('logout')\">\r\n <svg lucideLogOut class=\"user-menu-icon\"></svg>\r\n <span>Sign Out</span>\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n</div>", styles: [":host{display:inline-block}.user-menu-wrapper{position:relative;display:inline-block;text-align:left}.user-menu-trigger{display:flex;align-items:center;justify-content:center;gap:.35rem;min-height:2.25rem;border:1px solid var(--border);border-radius:999px;color:var(--foreground);background:var(--background);padding:.2rem .5rem .2rem .2rem;cursor:pointer;transition:background-color .15s ease,border-color .15s ease,box-shadow .15s ease}.user-menu-avatar{display:block;width:1.65rem;height:1.65rem;flex:0 0 1.65rem;overflow:hidden;border-radius:999px}.user-menu-chevron{width:.875rem;height:.875rem;color:var(--secondary);transition:transform .2s ease}.user-menu-chevron.rotate-180{transform:rotate(180deg)}.user-menu-trigger:hover{background:color-mix(in srgb,var(--border) 28%,transparent)}.user-menu-trigger:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.user-dropdown-panel{position:fixed;z-index:50;top:3.75rem;right:.5rem;left:.5rem;overflow:hidden;border:1px solid var(--border);border-radius:.875rem;background:var(--background);padding:.375rem;box-shadow:0 12px 30px #0f172a24;animation:dropdownEnter .15s cubic-bezier(.16,1,.3,1)}.user-dropdown-profile{border-bottom:1px solid var(--border);margin-bottom:.25rem;padding:.65rem .75rem}.user-dropdown-name,.user-dropdown-email{overflow:hidden;margin:0;text-overflow:ellipsis;white-space:nowrap}.user-dropdown-name{color:var(--foreground);font-size:.875rem;font-weight:750}.user-dropdown-email{margin-top:.125rem;color:var(--secondary);font-size:.78rem}.user-menu-list{display:grid;gap:.125rem;padding:.125rem 0}.user-menu-item{display:flex;width:100%;align-items:center;gap:.625rem;border:0;border-radius:.5rem;color:var(--foreground);background:transparent;padding:.55rem .75rem;font-size:.875rem;text-align:left;cursor:pointer;transition:background-color .15s ease,color .15s ease}.user-menu-icon{width:1rem;height:1rem;flex-shrink:0;color:var(--secondary)}.user-menu-item:hover{background:color-mix(in srgb,var(--border) 32%,transparent)}.user-menu-item.danger,.user-menu-item.danger .user-menu-icon{color:var(--danger, #ef4444)}.user-menu-item.danger:hover{background:color-mix(in srgb,var(--danger, #ef4444) 10%,transparent)}.user-menu-danger-zone{border-top:1px solid var(--border);margin-top:.25rem;padding-top:.25rem}@keyframes dropdownEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}@media(min-width:640px){.user-dropdown-panel{position:absolute;top:calc(100% + .5rem);right:0;left:auto;width:14rem;max-width:calc(100vw - 1rem);transform-origin:top right}}\n"], dependencies: [{ kind: "component", type: UserAvatarComponent, selector: "lib-user-avatar", inputs: ["user"] }, { kind: "component", type: LucideChevronDown, selector: "svg[lucideChevronDown]" }, { kind: "component", type: LucideLogOut, selector: "svg[lucideLogOut]" }, { kind: "component", type: LucideUser, selector: "svg[lucideUser]" }] });
357
535
  }
358
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: UserMenuComponent, decorators: [{
536
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: UserMenuComponent, decorators: [{
359
537
  type: Component,
360
- args: [{ selector: 'lib-user-menu', standalone: true, imports: [CommonModule, UserAvatarComponent, LucideChevronDown, LucideCreditCard, LucideLogOut, LucideSettings, LucideUser], template: "<div class=\"user-menu-wrapper\">\r\n <button type=\"button\" class=\"user-menu-trigger\" (click)=\"toggleMenu()\" aria-haspopup=\"true\" [aria-expanded]=\"isOpen()\"\r\n aria-label=\"User profile menu\">\r\n <div class=\"user-menu-avatar\">\r\n <lib-user-avatar [user]=\"user()\" />\r\n </div>\r\n <svg lucideChevronDown class=\"user-menu-chevron\"\r\n [class.rotate-180]=\"isOpen()\"></svg>\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"user-dropdown-panel\">\r\n @if (user(); as u) {\r\n <div class=\"user-dropdown-profile\">\r\n <p class=\"user-dropdown-name\" [title]=\"u.name\">{{ u.name }}</p>\r\n <p class=\"user-dropdown-email\" [title]=\"u.email\">{{ u.email }}</p>\r\n </div>\r\n }\r\n\r\n <div class=\"user-menu-list\">\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('profile')\">\r\n <svg lucideUser class=\"user-menu-icon\"></svg>\r\n <span>My Profile</span>\r\n </button>\r\n\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('settings')\">\r\n <svg lucideSettings class=\"user-menu-icon\"></svg>\r\n <span>Settings</span>\r\n </button>\r\n\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('billing')\">\r\n <svg lucideCreditCard class=\"user-menu-icon\"></svg>\r\n <span>Billing</span>\r\n </button>\r\n </div>\r\n\r\n <div class=\"user-menu-danger-zone\">\r\n <button type=\"button\" class=\"user-menu-item danger\" (click)=\"onAction('logout')\">\r\n <svg lucideLogOut class=\"user-menu-icon\"></svg>\r\n <span>Sign Out</span>\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:inline-block}.user-menu-wrapper{position:relative;display:inline-block;text-align:left}.user-menu-trigger{display:flex;align-items:center;justify-content:center;gap:.35rem;min-height:2.25rem;border:1px solid var(--border);border-radius:999px;color:var(--foreground);background:var(--background);padding:.2rem .5rem .2rem .2rem;cursor:pointer;transition:background-color .15s ease,border-color .15s ease,box-shadow .15s ease}.user-menu-avatar{display:block;width:1.65rem;height:1.65rem;flex:0 0 1.65rem;overflow:hidden;border-radius:999px}.user-menu-chevron{width:.875rem;height:.875rem;color:var(--secondary);transition:transform .2s ease}.user-menu-chevron.rotate-180{transform:rotate(180deg)}.user-menu-trigger:hover{background:color-mix(in srgb,var(--border) 28%,transparent)}.user-menu-trigger:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.user-dropdown-panel{position:fixed;z-index:50;top:3.75rem;right:.5rem;left:.5rem;overflow:hidden;border:1px solid var(--border);border-radius:.875rem;background:var(--background);padding:.375rem;box-shadow:0 12px 30px #0f172a24;animation:dropdownEnter .15s cubic-bezier(.16,1,.3,1)}.user-dropdown-profile{border-bottom:1px solid var(--border);margin-bottom:.25rem;padding:.65rem .75rem}.user-dropdown-name,.user-dropdown-email{overflow:hidden;margin:0;text-overflow:ellipsis;white-space:nowrap}.user-dropdown-name{color:var(--foreground);font-size:.875rem;font-weight:750}.user-dropdown-email{margin-top:.125rem;color:var(--secondary);font-size:.78rem}.user-menu-list{display:grid;gap:.125rem;padding:.125rem 0}.user-menu-item{display:flex;width:100%;align-items:center;gap:.625rem;border:0;border-radius:.5rem;color:var(--foreground);background:transparent;padding:.55rem .75rem;font-size:.875rem;text-align:left;cursor:pointer;transition:background-color .15s ease,color .15s ease}.user-menu-icon{width:1rem;height:1rem;flex-shrink:0;color:var(--secondary)}.user-menu-item:hover{background:color-mix(in srgb,var(--border) 32%,transparent)}.user-menu-item.danger,.user-menu-item.danger .user-menu-icon{color:var(--danger, #ef4444)}.user-menu-item.danger:hover{background:color-mix(in srgb,var(--danger, #ef4444) 10%,transparent)}.user-menu-danger-zone{border-top:1px solid var(--border);margin-top:.25rem;padding-top:.25rem}@keyframes dropdownEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}@media(min-width:640px){.user-dropdown-panel{position:absolute;top:calc(100% + .5rem);right:0;left:auto;width:14rem;max-width:calc(100vw - 1rem);transform-origin:top right}}\n"] }]
538
+ args: [{ selector: 'lib-user-menu', standalone: true, imports: [UserAvatarComponent, LucideChevronDown, LucideCreditCard, LucideLogOut, LucideSettings, LucideUser], template: "<div class=\"user-menu-wrapper\">\r\n <button type=\"button\" class=\"user-menu-trigger\" (click)=\"toggleMenu()\" aria-haspopup=\"true\" [aria-expanded]=\"isOpen()\"\r\n aria-label=\"User profile menu\">\r\n <div class=\"user-menu-avatar\">\r\n <lib-user-avatar [user]=\"user()\" />\r\n </div>\r\n <svg lucideChevronDown class=\"user-menu-chevron\" [class.rotate-180]=\"isOpen()\"></svg>\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"user-dropdown-panel\">\r\n @if (user(); as u) {\r\n <div class=\"user-dropdown-profile\">\r\n <p class=\"user-dropdown-name\" [title]=\"u.name\">{{ u.name }}</p>\r\n <p class=\"user-dropdown-email\" [title]=\"u.email\">{{ u.email }}</p>\r\n </div>\r\n }\r\n\r\n <div class=\"user-menu-list\">\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('profile')\">\r\n <svg lucideUser class=\"user-menu-icon\"></svg>\r\n <span>My Profile</span>\r\n </button>\r\n\r\n <!-- <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('settings')\">\r\n <svg lucideSettings class=\"user-menu-icon\"></svg>\r\n <span>Settings</span>\r\n </button>\r\n\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('billing')\">\r\n <svg lucideCreditCard class=\"user-menu-icon\"></svg>\r\n <span>Billing</span>\r\n </button> -->\r\n </div>\r\n\r\n <div class=\"user-menu-danger-zone\">\r\n <button type=\"button\" class=\"user-menu-item danger\" (click)=\"onAction('logout')\">\r\n <svg lucideLogOut class=\"user-menu-icon\"></svg>\r\n <span>Sign Out</span>\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n</div>", styles: [":host{display:inline-block}.user-menu-wrapper{position:relative;display:inline-block;text-align:left}.user-menu-trigger{display:flex;align-items:center;justify-content:center;gap:.35rem;min-height:2.25rem;border:1px solid var(--border);border-radius:999px;color:var(--foreground);background:var(--background);padding:.2rem .5rem .2rem .2rem;cursor:pointer;transition:background-color .15s ease,border-color .15s ease,box-shadow .15s ease}.user-menu-avatar{display:block;width:1.65rem;height:1.65rem;flex:0 0 1.65rem;overflow:hidden;border-radius:999px}.user-menu-chevron{width:.875rem;height:.875rem;color:var(--secondary);transition:transform .2s ease}.user-menu-chevron.rotate-180{transform:rotate(180deg)}.user-menu-trigger:hover{background:color-mix(in srgb,var(--border) 28%,transparent)}.user-menu-trigger:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.user-dropdown-panel{position:fixed;z-index:50;top:3.75rem;right:.5rem;left:.5rem;overflow:hidden;border:1px solid var(--border);border-radius:.875rem;background:var(--background);padding:.375rem;box-shadow:0 12px 30px #0f172a24;animation:dropdownEnter .15s cubic-bezier(.16,1,.3,1)}.user-dropdown-profile{border-bottom:1px solid var(--border);margin-bottom:.25rem;padding:.65rem .75rem}.user-dropdown-name,.user-dropdown-email{overflow:hidden;margin:0;text-overflow:ellipsis;white-space:nowrap}.user-dropdown-name{color:var(--foreground);font-size:.875rem;font-weight:750}.user-dropdown-email{margin-top:.125rem;color:var(--secondary);font-size:.78rem}.user-menu-list{display:grid;gap:.125rem;padding:.125rem 0}.user-menu-item{display:flex;width:100%;align-items:center;gap:.625rem;border:0;border-radius:.5rem;color:var(--foreground);background:transparent;padding:.55rem .75rem;font-size:.875rem;text-align:left;cursor:pointer;transition:background-color .15s ease,color .15s ease}.user-menu-icon{width:1rem;height:1rem;flex-shrink:0;color:var(--secondary)}.user-menu-item:hover{background:color-mix(in srgb,var(--border) 32%,transparent)}.user-menu-item.danger,.user-menu-item.danger .user-menu-icon{color:var(--danger, #ef4444)}.user-menu-item.danger:hover{background:color-mix(in srgb,var(--danger, #ef4444) 10%,transparent)}.user-menu-danger-zone{border-top:1px solid var(--border);margin-top:.25rem;padding-top:.25rem}@keyframes dropdownEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}@media(min-width:640px){.user-dropdown-panel{position:absolute;top:calc(100% + .5rem);right:0;left:auto;width:14rem;max-width:calc(100vw - 1rem);transform-origin:top right}}\n"] }]
361
539
  }], propDecorators: { user: [{ type: i0.Input, args: [{ isSignal: true, alias: "user", required: false }] }], menuAction: [{ type: i0.Output, args: ["menuAction"] }], onOutsideClick: [{
362
540
  type: HostListener,
363
541
  args: ['document:click', ['$event']]
364
542
  }] } });
365
543
 
366
- // lib/shared/icon/icon.component.ts
367
- class IconComponent {
368
- name = input.required(...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
369
- size = input(24, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
370
- color = input(undefined, ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
371
- strokeWidth = input(undefined, ...(ngDevMode ? [{ debugName: "strokeWidth" }] : /* istanbul ignore next */ []));
372
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: IconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
373
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: IconComponent, isStandalone: true, selector: "lib-icon", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, strokeWidth: { classPropertyName: "strokeWidth", publicName: "strokeWidth", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
374
- @switch (name()) {
375
- @case ('menu') {
376
- <svg lucideMenu [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
377
- }
378
- @case ('x') {
379
- <svg lucideX [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
380
- }
381
- @case ('sun') {
382
- <svg lucideSun [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
383
- }
384
- @case ('moon') {
385
- <svg lucideMoon [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
386
- }
387
- @case ('monitor') {
388
- <svg lucideMonitor [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
389
- }
390
- @case ('bell') {
391
- <svg lucideBell [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
392
- }
393
- @case ('grid') {
394
- <svg lucideGrid [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
395
- }
396
- @case ('chevronDown') {
397
- <svg lucideChevronDown [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
398
- }
399
- @case ('building2') {
400
- <svg lucideBuilding2 [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
401
- }
402
- @default {
403
- <svg lucideSearch [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
404
- }
405
- }
406
- `, isInline: true, styles: [":host{display:inline-flex;align-items:center;justify-content:center;color:inherit}.lib-icon{display:block;color:inherit}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: LucideBell, selector: "svg[lucideBell]" }, { kind: "component", type: LucideBuilding2, selector: "svg[lucideBuilding2]" }, { kind: "component", type: LucideChevronDown, selector: "svg[lucideChevronDown]" }, { kind: "component", type: LucideGrid, selector: "svg[lucideGrid3x3], svg[lucideGrid], svg[lucideGrid3X3]" }, { kind: "component", type: LucideMenu, selector: "svg[lucideMenu]" }, { kind: "component", type: LucideMonitor, selector: "svg[lucideMonitor]" }, { kind: "component", type: LucideMoon, selector: "svg[lucideMoon]" }, { kind: "component", type: LucideSearch, selector: "svg[lucideSearch]" }, { kind: "component", type: LucideSun, selector: "svg[lucideSun]" }, { kind: "component", type: LucideX, selector: "svg[lucideX]" }] });
407
- }
408
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: IconComponent, decorators: [{
409
- type: Component,
410
- args: [{ selector: 'lib-icon', standalone: true, imports: [
411
- CommonModule,
412
- LucideBell,
413
- LucideBuilding2,
414
- LucideChevronDown,
415
- LucideGrid,
416
- LucideMenu,
417
- LucideMonitor,
418
- LucideMoon,
419
- LucideSearch,
420
- LucideSun,
421
- LucideX,
422
- ], template: `
423
- @switch (name()) {
424
- @case ('menu') {
425
- <svg lucideMenu [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
426
- }
427
- @case ('x') {
428
- <svg lucideX [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
429
- }
430
- @case ('sun') {
431
- <svg lucideSun [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
432
- }
433
- @case ('moon') {
434
- <svg lucideMoon [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
435
- }
436
- @case ('monitor') {
437
- <svg lucideMonitor [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
438
- }
439
- @case ('bell') {
440
- <svg lucideBell [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
441
- }
442
- @case ('grid') {
443
- <svg lucideGrid [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
444
- }
445
- @case ('chevronDown') {
446
- <svg lucideChevronDown [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
447
- }
448
- @case ('building2') {
449
- <svg lucideBuilding2 [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
450
- }
451
- @default {
452
- <svg lucideSearch [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
453
- }
454
- }
455
- `, styles: [":host{display:inline-flex;align-items:center;justify-content:center;color:inherit}.lib-icon{display:block;color:inherit}\n"] }]
456
- }], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], strokeWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "strokeWidth", required: false }] }] } });
457
-
458
544
  class HeaderComponent {
459
545
  router = inject(Router);
460
546
  themeService = inject(ThemeService);
461
- // User Inputs
462
547
  user = input.required(...(ngDevMode ? [{ debugName: "user" }] : /* istanbul ignore next */ []));
463
548
  permissions = input([], ...(ngDevMode ? [{ debugName: "permissions" }] : /* istanbul ignore next */ []));
464
- // Navigation & Integration Inputs
465
549
  navigationItems = input([], ...(ngDevMode ? [{ debugName: "navigationItems" }] : /* istanbul ignore next */ []));
466
550
  applications = input([], ...(ngDevMode ? [{ debugName: "applications" }] : /* istanbul ignore next */ []));
467
551
  notifications = input([], ...(ngDevMode ? [{ debugName: "notifications" }] : /* istanbul ignore next */ []));
@@ -473,23 +557,13 @@ class HeaderComponent {
473
557
  showNotifications = input(true, ...(ngDevMode ? [{ debugName: "showNotifications" }] : /* istanbul ignore next */ []));
474
558
  showAppSwitcher = input(true, ...(ngDevMode ? [{ debugName: "showAppSwitcher" }] : /* istanbul ignore next */ []));
475
559
  showThemeSwitcher = input(true, ...(ngDevMode ? [{ debugName: "showThemeSwitcher" }] : /* istanbul ignore next */ []));
476
- // Outputs as decided in the architecture plan
477
560
  search = output();
478
561
  applicationSelected = output();
479
562
  notificationClick = output();
480
563
  markRead = output();
481
564
  clearAll = output();
482
565
  menuAction = output();
483
- // Lucide Icons
484
- // readonly searchIcon = Search;
485
- // readonly menuIcon = Menu;
486
- // readonly xIcon = X;
487
- // readonly sunIcon = Sun;
488
- // readonly moonIcon = Moon;
489
- // readonly monitorIcon = Monitor;
490
- // Header specific UI states
491
566
  isMobileMenuOpen = signal(false, ...(ngDevMode ? [{ debugName: "isMobileMenuOpen" }] : /* istanbul ignore next */ []));
492
- // Checks if the user has permission to see an item
493
567
  hasPermission(item) {
494
568
  if (!item.permissions || item.permissions.length === 0) {
495
569
  return true;
@@ -497,7 +571,6 @@ class HeaderComponent {
497
571
  const currentPermissions = this.permissions();
498
572
  return item.permissions.some((p) => currentPermissions.includes(p));
499
573
  }
500
- // Reactive unread count selector
501
574
  unreadCount = computed(() => {
502
575
  const overrideCount = this.notificationCount();
503
576
  if (overrideCount > 0)
@@ -505,7 +578,6 @@ class HeaderComponent {
505
578
  return this.notifications().filter((n) => !n.read).length;
506
579
  }, ...(ngDevMode ? [{ debugName: "unreadCount" }] : /* istanbul ignore next */ []));
507
580
  hasMobileMenuContent = computed(() => this.showSearch() || this.navigationItems().some((item) => this.hasPermission(item)), ...(ngDevMode ? [{ debugName: "hasMobileMenuContent" }] : /* istanbul ignore next */ []));
508
- // Determines if an item's internal route is active
509
581
  isActive(item) {
510
582
  if (!item.route)
511
583
  return false;
@@ -545,49 +617,203 @@ class HeaderComponent {
545
617
  onMenuAction(action) {
546
618
  this.menuAction.emit(action);
547
619
  }
548
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: HeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
549
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: HeaderComponent, isStandalone: true, selector: "lib-header", inputs: { user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: true, transformFunction: null }, permissions: { classPropertyName: "permissions", publicName: "permissions", isSignal: true, isRequired: false, transformFunction: null }, navigationItems: { classPropertyName: "navigationItems", publicName: "navigationItems", isSignal: true, isRequired: false, transformFunction: null }, applications: { classPropertyName: "applications", publicName: "applications", isSignal: true, isRequired: false, transformFunction: null }, notifications: { classPropertyName: "notifications", publicName: "notifications", isSignal: true, isRequired: false, transformFunction: null }, notificationCount: { classPropertyName: "notificationCount", publicName: "notificationCount", isSignal: true, isRequired: false, transformFunction: null }, logoUrl: { classPropertyName: "logoUrl", publicName: "logoUrl", isSignal: true, isRequired: false, transformFunction: null }, appName: { classPropertyName: "appName", publicName: "appName", isSignal: true, isRequired: false, transformFunction: null }, currentAppName: { classPropertyName: "currentAppName", publicName: "currentAppName", isSignal: true, isRequired: false, transformFunction: null }, showSearch: { classPropertyName: "showSearch", publicName: "showSearch", isSignal: true, isRequired: false, transformFunction: null }, showNotifications: { classPropertyName: "showNotifications", publicName: "showNotifications", isSignal: true, isRequired: false, transformFunction: null }, showAppSwitcher: { classPropertyName: "showAppSwitcher", publicName: "showAppSwitcher", isSignal: true, isRequired: false, transformFunction: null }, showThemeSwitcher: { classPropertyName: "showThemeSwitcher", publicName: "showThemeSwitcher", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { search: "search", applicationSelected: "applicationSelected", notificationClick: "notificationClick", markRead: "markRead", clearAll: "clearAll", menuAction: "menuAction" }, ngImport: i0, template: "<header class=\"header-container\">\r\n\r\n <!-- Left Side: Brand, Current App & Desktop Links -->\r\n <div class=\"header-left\">\r\n\r\n <a routerLink=\"/\" class=\"brand-identity\">\r\n <img [src]=\"logoUrl()\" [alt]=\"appName() + ' logo'\" width=\"26\" height=\"26\" class=\"brand-logo\" />\r\n <span class=\"brand-name\">{{ appName() }}</span>\r\n </a>\r\n\r\n @if (currentAppName()) {\r\n <div class=\"current-app-identity\" [attr.aria-label]=\"'Current app: ' + currentAppName()\">\r\n <span class=\"brand-separator\" aria-hidden=\"true\">|</span>\r\n <span class=\"current-app-name\">{{ currentAppName() }}</span>\r\n </div>\r\n }\r\n\r\n <!-- Desktop Navigation Links -->\r\n <nav class=\"desktop-nav ml-1 hidden items-center lg:flex\" aria-label=\"Desktop primary navigation\">\r\n @for (item of navigationItems(); track item.id) {\r\n @if (hasPermission(item)) {\r\n @if (item.route) {\r\n <a [routerLink]=\"item.route\" routerLinkActive=\"active\" [routerLinkActiveOptions]=\"{exact: true}\" class=\"nav-link\"\r\n [attr.aria-current]=\"isActive(item) ? 'page' : null\">\r\n {{ item.label }}\r\n </a>\r\n } @else {\r\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" class=\"nav-link\">\r\n {{ item.label }}\r\n </a>\r\n }\r\n }\r\n }\r\n </nav>\r\n </div>\r\n\r\n <!-- Middle: Search Input (Desktop) -->\r\n @if (showSearch()) {\r\n <div class=\"header-middle\">\r\n <div class=\"header-search-shell\">\r\n <lib-icon name=\"search\" [size]=\"14\"\r\n class=\"pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-[var(--secondary)]\" />\r\n\r\n <input type=\"search\" placeholder=\"Search Excellore...\" #searchInput\r\n (keyup.enter)=\"onSearch(searchInput.value); searchInput.value = ''\" class=\"header-search\" autocomplete=\"off\"\r\n aria-label=\"Search\" />\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"header-spacer\"></div>\r\n }\r\n\r\n <!-- Right Side Actions -->\r\n <div class=\"header-actions\">\r\n\r\n <!-- Mobile Search Trigger -->\r\n @if (showSearch()) {\r\n <button type=\"button\" class=\"header-action mobile-search-action\" (click)=\"toggleMobileMenu()\"\r\n aria-label=\"Open mobile menu\" aria-expanded=\"isMobileMenuOpen()\" #menuToggle>\r\n <lib-icon name=\"search\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\r\n </button>\r\n }\r\n\r\n <!-- Theme Switcher -->\r\n @if (showThemeSwitcher()) {\r\n <button type=\"button\" class=\"header-action\" (click)=\"themeService.toggleTheme()\" aria-label=\"Toggle theme\">\r\n @switch (themeService.theme()) {\r\n @case ('light') {\r\n <lib-icon name=\"sun\" [size]=\"16\" class=\"text-[var(--primary)]\" />\r\n }\r\n @case ('dark') {\r\n <lib-icon name=\"moon\" [size]=\"16\" class=\"text-[var(--primary)]\" />\r\n }\r\n @default {\r\n <lib-icon name=\"monitor\" [size]=\"16\" class=\"text-[var(--secondary)]\" />\r\n }\r\n }\r\n </button>\r\n }\r\n\r\n <!-- App Switcher -->\r\n @if (showAppSwitcher() && applications().length > 0) {\r\n <lib-app-switcher [applications]=\"applications()\" (applicationSelected)=\"onApplicationSelect($event)\" />\r\n }\r\n\r\n <!-- Notifications -->\r\n @if (showNotifications()) {\r\n <lib-notification-bell [unreadCount]=\"unreadCount()\" [notifications]=\"notifications()\"\r\n (notificationClick)=\"onNotificationClick($event)\" (markRead)=\"onMarkRead($event)\" (clearAll)=\"onClearAll()\" />\r\n }\r\n\r\n <!-- User Menu -->\n <div class=\"user-menu-divider\">\n <lib-user-menu [user]=\"user()\" (menuAction)=\"onMenuAction($event)\" />\n </div>\n\n <!-- Mobile Menu Toggle (Hamburger) -->\n @if (hasMobileMenuContent()) {\r\n <button type=\"button\" class=\"header-action mobile-menu-action\" (click)=\"toggleMobileMenu()\"\r\n aria-label=\"Toggle navigation menu\" aria-expanded=\"isMobileMenuOpen()\" #mobileMenuToggle>\r\n <lib-icon [name]=\"isMobileMenuOpen() ? 'x' : 'menu'\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\r\n </button>\r\n }\r\n </div>\r\n</header>\r\n\r\n<!-- Mobile Menu Backdrop -->\r\n@if (isMobileMenuOpen() && hasMobileMenuContent()) {\r\n<div class=\"mobile-backdrop fixed inset-0 z-30 lg:hidden\" (click)=\"closeMobileMenu()\"\r\n (keydown.escape)=\"closeMobileMenu()\" tabindex=\"-1\" aria-hidden=\"true\"></div>\r\n\r\n<!-- Mobile Navigation Drawer -->\r\n<div\r\n class=\"mobile-drawer fixed inset-x-2 top-16 z-40 flex max-h-[calc(100dvh-4.5rem)] flex-col rounded-2xl border border-[var(--border)] bg-[var(--background)] p-3 lg:hidden\"\r\n role=\"dialog\" aria-modal=\"true\" aria-label=\"Mobile navigation\" (keydown.escape)=\"closeMobileMenu()\">\r\n <!-- Mobile Search -->\r\n @if (showSearch()) {\r\n <div class=\"relative mb-3 w-full\">\r\n <lib-icon name=\"search\" [size]=\"16\"\r\n class=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[var(--secondary)] pointer-events-none\"\r\n aria-hidden=\"true\" />\r\n\r\n <input type=\"search\" placeholder=\"Search Excellore...\" #mobileSearchInput\r\n (keyup.enter)=\"onSearch(mobileSearchInput.value); mobileSearchInput.value = ''; closeMobileMenu()\"\r\n class=\"header-search mobile\" autocomplete=\"off\" aria-label=\"Search\" />\r\n </div>\r\n }\r\n\r\n <!-- Mobile Nav Links -->\r\n <nav class=\"flex flex-col gap-1 overflow-y-auto\" role=\"navigation\">\r\n @for (item of navigationItems(); track item.id) {\r\n @if (hasPermission(item)) {\r\n @if (item.route) {\r\n <a [routerLink]=\"item.route\" routerLinkActive=\"mobile-active\" [routerLinkActiveOptions]=\"{exact: true}\"\r\n (click)=\"closeMobileMenu()\" class=\"mobile-nav-link\" [attr.aria-current]=\"isActive(item) ? 'page' : null\">\r\n {{ item.label }}\r\n </a>\r\n } @else {\r\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" (click)=\"closeMobileMenu()\" class=\"mobile-nav-link\">\r\n {{ item.label }}\r\n </a>\r\n }\r\n }\r\n }\r\n </nav>\r\n</div>\r\n}\r\n", styles: [":host{display:block}.header-container{position:fixed;z-index:40;top:0;right:0;left:0;display:flex;height:3.75rem;align-items:center;justify-content:space-between;border-bottom:1px solid var(--border);color:var(--foreground);background:var(--background);box-shadow:0 1px 2px #0f172a0a;padding-inline:1rem;will-change:transform}.header-left{display:flex;min-width:0;align-items:center;gap:.75rem}.brand-identity{display:inline-flex;min-width:0;align-items:center;gap:.5rem;border-radius:.5rem;color:var(--foreground);text-decoration:none}.brand-identity:hover{opacity:.9}.brand-logo{width:1.625rem;height:1.625rem;flex-shrink:0;-webkit-user-select:none;user-select:none}.brand-name{display:none;color:var(--foreground);font-size:.95rem;font-weight:750;letter-spacing:-.01em;white-space:nowrap}.current-app-identity{display:inline-flex;min-width:0;align-items:center;gap:.5rem}.brand-separator{color:var(--border);font-size:1rem;font-weight:500}.current-app-name{max-width:11rem;overflow:hidden;color:var(--foreground);font-size:.875rem;font-weight:650;text-overflow:ellipsis;white-space:nowrap}.desktop-nav{display:none;align-items:center;gap:1.25rem;margin-left:.25rem;padding:0;background:transparent}.header-middle{display:none;flex:1 1 auto;justify-content:center;padding-inline:1rem}.header-spacer{display:none;flex:1 1 auto}.nav-link{position:relative;display:inline-flex;align-items:center;min-height:2rem;color:var(--secondary);padding:0;font-size:.78rem;font-weight:600;line-height:1;text-decoration:none;transition:color .15s ease,opacity .15s ease}.nav-link:hover{color:var(--foreground)}.nav-link.active{color:var(--foreground)!important;font-weight:700}.nav-link.active:after{content:\"\";position:absolute;right:0;bottom:-.85rem;left:0;height:.125rem;background-color:var(--primary);border-radius:9999px}.header-search-shell{position:relative;width:min(100%,20rem)}.header-search{width:100%;height:2.25rem;border:1px solid var(--border);border-radius:.5rem;outline:0;color:var(--foreground);background:var(--background);padding:0 .9rem 0 2.25rem;font-size:.78rem;transition:border-color .15s ease,box-shadow .15s ease}.header-search::placeholder{color:var(--secondary)}.header-search:focus{border-color:var(--primary);box-shadow:0 0 0 2px color-mix(in srgb,var(--primary) 16%,transparent)}.header-search.mobile{width:100%;height:2.6rem;border-radius:.625rem;font-size:.875rem}.header-search.mobile:focus{width:100%}.header-actions{display:flex;align-items:center;gap:.25rem;background:transparent}.header-action{display:grid;width:2.15rem;height:2.15rem;place-items:center;border:1px solid transparent;border-radius:.5rem;color:var(--foreground);background:transparent;cursor:pointer;transition:background-color .15s ease,border-color .15s ease,color .15s ease}.header-action:hover{border-color:var(--border);background:color-mix(in srgb,var(--border) 35%,transparent)}.user-menu-divider{display:flex;height:1.75rem;align-items:center;margin-left:.2rem;border-left:1px solid var(--border);padding-left:.35rem}.mobile-backdrop{background:#0f172a29}.mobile-drawer{box-shadow:0 12px 30px #0f172a24;animation:slideDown .2s cubic-bezier(.16,1,.3,1)}.mobile-nav-link{display:flex;align-items:center;min-height:2.75rem;border-radius:.625rem;color:var(--secondary);padding:0 .85rem;font-size:.925rem;font-weight:700;text-decoration:none;transition:background-color .15s ease,color .15s ease,transform .15s ease}.mobile-nav-link:hover{color:var(--foreground);background:color-mix(in srgb,var(--border) 35%,transparent)}.mobile-active{color:var(--foreground)!important;background:color-mix(in srgb,var(--primary) 10%,transparent);font-weight:700}@keyframes slideDown{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}:focus-visible{outline:2px solid var(--primary);outline-offset:2px}:focus:not(:focus-visible){outline:none}lucide-icon,svg{display:inline-flex;vertical-align:middle}*{transition-property:background-color,border-color,color;transition-duration:.15s;transition-timing-function:ease-in-out}@media(min-width:640px){.header-container{padding-inline:1.25rem}.brand-name{display:inline-block}.header-actions{gap:.25rem}.user-menu-divider{padding-left:.5rem}}@media(min-width:768px){.header-middle{display:flex}.header-spacer{display:block}.mobile-search-action{display:none}}@media(min-width:1024px){.header-container{padding-inline:1.5rem}.desktop-nav{display:flex}.mobile-menu-action{display:none}}@media(max-width:420px){.current-app-name{max-width:8rem}.header-actions{gap:0;padding:0;background:transparent}.header-action{width:2rem;height:2rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "component", type: AppSwitcherComponent, selector: "lib-app-switcher", inputs: ["applications"], outputs: ["applicationSelected"] }, { kind: "component", type: NotificationBellComponent, selector: "lib-notification-bell", inputs: ["unreadCount", "notifications"], outputs: ["notificationClick", "markRead", "clearAll"] }, { kind: "component", type: UserMenuComponent, selector: "lib-user-menu", inputs: ["user"], outputs: ["menuAction"] }, { kind: "component", type: IconComponent, selector: "lib-icon", inputs: ["name", "size", "color", "strokeWidth"] }] });
620
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: HeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
621
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: HeaderComponent, isStandalone: true, selector: "lib-header", inputs: { user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: true, transformFunction: null }, permissions: { classPropertyName: "permissions", publicName: "permissions", isSignal: true, isRequired: false, transformFunction: null }, navigationItems: { classPropertyName: "navigationItems", publicName: "navigationItems", isSignal: true, isRequired: false, transformFunction: null }, applications: { classPropertyName: "applications", publicName: "applications", isSignal: true, isRequired: false, transformFunction: null }, notifications: { classPropertyName: "notifications", publicName: "notifications", isSignal: true, isRequired: false, transformFunction: null }, notificationCount: { classPropertyName: "notificationCount", publicName: "notificationCount", isSignal: true, isRequired: false, transformFunction: null }, logoUrl: { classPropertyName: "logoUrl", publicName: "logoUrl", isSignal: true, isRequired: false, transformFunction: null }, appName: { classPropertyName: "appName", publicName: "appName", isSignal: true, isRequired: false, transformFunction: null }, currentAppName: { classPropertyName: "currentAppName", publicName: "currentAppName", isSignal: true, isRequired: false, transformFunction: null }, showSearch: { classPropertyName: "showSearch", publicName: "showSearch", isSignal: true, isRequired: false, transformFunction: null }, showNotifications: { classPropertyName: "showNotifications", publicName: "showNotifications", isSignal: true, isRequired: false, transformFunction: null }, showAppSwitcher: { classPropertyName: "showAppSwitcher", publicName: "showAppSwitcher", isSignal: true, isRequired: false, transformFunction: null }, showThemeSwitcher: { classPropertyName: "showThemeSwitcher", publicName: "showThemeSwitcher", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { search: "search", applicationSelected: "applicationSelected", notificationClick: "notificationClick", markRead: "markRead", clearAll: "clearAll", menuAction: "menuAction" }, ngImport: i0, template: "<header class=\"header-container\">\r\n\r\n <!-- Left Side: Brand, Current App & Desktop Links -->\r\n <div class=\"header-left\">\r\n\r\n <a routerLink=\"/\" class=\"brand-identity\">\r\n <img [src]=\"logoUrl()\" [alt]=\"appName() + ' logo'\" width=\"26\" height=\"26\" class=\"brand-logo\" />\r\n <span class=\"brand-name\">{{ appName() }}</span>\r\n </a>\r\n\r\n @if (currentAppName()) {\r\n <div class=\"current-app-identity\" [attr.aria-label]=\"'Current app: ' + currentAppName()\">\r\n <span class=\"brand-separator\" aria-hidden=\"true\">|</span>\r\n <span class=\"current-app-name\">{{ currentAppName() }}</span>\r\n </div>\r\n }\r\n\r\n <!-- Desktop Navigation Links -->\r\n <nav class=\"desktop-nav ml-1 hidden items-center lg:flex\" aria-label=\"Desktop primary navigation\">\r\n @for (item of navigationItems(); track item.id) {\r\n @if (hasPermission(item)) {\r\n @if (item.route) {\r\n <a [routerLink]=\"item.route\" routerLinkActive=\"active\" [routerLinkActiveOptions]=\"{exact: true}\" class=\"nav-link\"\r\n [attr.aria-current]=\"isActive(item) ? 'page' : null\">\r\n {{ item.label }}\r\n </a>\r\n } @else {\r\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" class=\"nav-link\">\r\n {{ item.label }}\r\n </a>\r\n }\r\n }\r\n }\r\n </nav>\r\n </div>\r\n\r\n <!-- Middle: Search Input (Desktop) -->\r\n @if (showSearch()) {\r\n <div class=\"header-middle\">\r\n <div class=\"header-search-shell\">\r\n <lib-icon name=\"search\" [size]=\"14\"\r\n class=\"pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-[var(--secondary)]\" />\r\n\r\n <input type=\"search\" placeholder=\"Search Excellore...\" #searchInput\r\n (keyup.enter)=\"onSearch(searchInput.value); searchInput.value = ''\" class=\"header-search\" autocomplete=\"off\"\r\n aria-label=\"Search\" />\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"header-spacer\"></div>\r\n }\r\n\r\n <!-- Right Side Actions -->\r\n <div class=\"header-actions\">\r\n\r\n <!-- Mobile Search Trigger -->\r\n @if (showSearch()) {\r\n <button type=\"button\" class=\"header-action mobile-search-action\" (click)=\"toggleMobileMenu()\"\r\n aria-label=\"Open mobile menu\" aria-expanded=\"isMobileMenuOpen()\" #menuToggle>\r\n <lib-icon name=\"search\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\r\n </button>\r\n }\r\n\r\n <!-- Theme Switcher -->\r\n @if (showThemeSwitcher()) {\r\n <button type=\"button\" class=\"header-action\" (click)=\"themeService.toggleTheme()\" aria-label=\"Toggle theme\">\r\n @switch (themeService.theme()) {\r\n @case ('light') {\r\n <lib-icon name=\"sun\" [size]=\"16\" class=\"text-[var(--primary)]\" />\r\n }\r\n @case ('dark') {\r\n <lib-icon name=\"moon\" [size]=\"16\" class=\"text-[var(--primary)]\" />\r\n }\r\n @default {\r\n <lib-icon name=\"monitor\" [size]=\"16\" class=\"text-[var(--secondary)]\" />\r\n }\r\n }\r\n </button>\r\n }\r\n\r\n <!-- App Switcher -->\r\n @if (showAppSwitcher() && applications().length > 0) {\r\n <lib-app-switcher [applications]=\"applications()\" (applicationSelected)=\"onApplicationSelect($event)\" />\r\n }\r\n\r\n <!-- Notifications -->\r\n @if (showNotifications()) {\r\n <lib-notification-bell [unreadCount]=\"unreadCount()\" [notifications]=\"notifications()\"\r\n (notificationClick)=\"onNotificationClick($event)\" (markRead)=\"onMarkRead($event)\" (clearAll)=\"onClearAll()\" />\r\n }\r\n\r\n <!-- User Menu -->\r\n <div class=\"user-menu-divider\">\r\n <lib-user-menu [user]=\"user()\" (menuAction)=\"onMenuAction($event)\" />\r\n </div>\r\n\r\n <!-- Mobile Menu Toggle (Hamburger) -->\r\n @if (hasMobileMenuContent()) {\r\n <button type=\"button\" class=\"header-action mobile-menu-action\" (click)=\"toggleMobileMenu()\"\r\n aria-label=\"Toggle navigation menu\" aria-expanded=\"isMobileMenuOpen()\" #mobileMenuToggle>\r\n <lib-icon [name]=\"isMobileMenuOpen() ? 'x' : 'menu'\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\r\n </button>\r\n }\r\n </div>\r\n</header>\r\n\r\n<!-- Mobile Menu Backdrop -->\r\n@if (isMobileMenuOpen() && hasMobileMenuContent()) {\r\n<div class=\"mobile-backdrop fixed inset-0 z-30 lg:hidden\" (click)=\"closeMobileMenu()\"\r\n (keydown.escape)=\"closeMobileMenu()\" tabindex=\"-1\" aria-hidden=\"true\"></div>\r\n\r\n<!-- Mobile Navigation Drawer -->\r\n<div\r\n class=\"mobile-drawer fixed inset-x-2 top-16 z-40 flex max-h-[calc(100dvh-4.5rem)] flex-col rounded-2xl border border-[var(--border)] bg-[var(--background)] p-3 lg:hidden\"\r\n role=\"dialog\" aria-modal=\"true\" aria-label=\"Mobile navigation\" (keydown.escape)=\"closeMobileMenu()\">\r\n <!-- Mobile Search -->\r\n @if (showSearch()) {\r\n <div class=\"relative mb-3 w-full\">\r\n <lib-icon name=\"search\" [size]=\"16\"\r\n class=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[var(--secondary)] pointer-events-none\"\r\n aria-hidden=\"true\" />\r\n\r\n <input type=\"search\" placeholder=\"Search Excellore...\" #mobileSearchInput\r\n (keyup.enter)=\"onSearch(mobileSearchInput.value); mobileSearchInput.value = ''; closeMobileMenu()\"\r\n class=\"header-search mobile\" autocomplete=\"off\" aria-label=\"Search\" />\r\n </div>\r\n }\r\n\r\n <!-- Mobile Nav Links -->\r\n <nav class=\"flex flex-col gap-1 overflow-y-auto\" role=\"navigation\">\r\n @for (item of navigationItems(); track item.id) {\r\n @if (hasPermission(item)) {\r\n @if (item.route) {\r\n <a [routerLink]=\"item.route\" routerLinkActive=\"mobile-active\" [routerLinkActiveOptions]=\"{exact: true}\"\r\n (click)=\"closeMobileMenu()\" class=\"mobile-nav-link\" [attr.aria-current]=\"isActive(item) ? 'page' : null\">\r\n {{ item.label }}\r\n </a>\r\n } @else {\r\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" (click)=\"closeMobileMenu()\" class=\"mobile-nav-link\">\r\n {{ item.label }}\r\n </a>\r\n }\r\n }\r\n }\r\n </nav>\r\n</div>\r\n}\r\n", styles: [":host{display:block}.header-container{position:fixed;z-index:40;top:0;right:0;left:0;display:flex;height:3.75rem;align-items:center;justify-content:space-between;border-bottom:1px solid var(--border);color:var(--foreground);background:var(--background);box-shadow:0 1px 2px #0f172a0a;padding-inline:1rem;will-change:transform}.header-left{display:flex;min-width:0;align-items:center;gap:.75rem}.brand-identity{display:inline-flex;min-width:0;align-items:center;gap:.5rem;border-radius:.5rem;color:var(--foreground);text-decoration:none}.brand-identity:hover{opacity:.9}.brand-logo{width:1.625rem;height:1.625rem;flex-shrink:0;-webkit-user-select:none;user-select:none}.brand-name{display:none;color:var(--foreground);font-size:.95rem;font-weight:750;letter-spacing:-.01em;white-space:nowrap}.current-app-identity{display:inline-flex;min-width:0;align-items:center;gap:.5rem}.brand-separator{color:var(--border);font-size:1rem;font-weight:500}.current-app-name{max-width:11rem;overflow:hidden;color:var(--foreground);font-size:.875rem;font-weight:650;text-overflow:ellipsis;white-space:nowrap}.desktop-nav{display:none;align-items:center;gap:1.25rem;margin-left:.25rem;padding:0;background:transparent}.header-middle{display:none;flex:1 1 auto;justify-content:center;padding-inline:1rem}.header-spacer{display:none;flex:1 1 auto}.nav-link{position:relative;display:inline-flex;align-items:center;min-height:2rem;color:var(--secondary);padding:0;font-size:.78rem;font-weight:600;line-height:1;text-decoration:none;transition:color .15s ease,opacity .15s ease}.nav-link:hover{color:var(--foreground)}.nav-link.active{color:var(--foreground)!important;font-weight:700}.nav-link.active:after{content:\"\";position:absolute;right:0;bottom:-.85rem;left:0;height:.125rem;background-color:var(--primary);border-radius:9999px}.header-search-shell{position:relative;width:min(100%,20rem)}.header-search{width:100%;height:2.25rem;border:1px solid var(--border);border-radius:.5rem;outline:0;color:var(--foreground);background:var(--background);padding:0 .9rem 0 2.25rem;font-size:.78rem;transition:border-color .15s ease,box-shadow .15s ease}.header-search::placeholder{color:var(--secondary)}.header-search:focus{border-color:var(--primary);box-shadow:0 0 0 2px color-mix(in srgb,var(--primary) 16%,transparent)}.header-search.mobile{width:100%;height:2.6rem;border-radius:.625rem;font-size:.875rem}.header-search.mobile:focus{width:100%}.header-actions{display:flex;align-items:center;gap:.25rem;background:transparent}.header-action{display:grid;width:2.15rem;height:2.15rem;place-items:center;border:1px solid transparent;border-radius:.5rem;color:var(--foreground);background:transparent;cursor:pointer;transition:background-color .15s ease,border-color .15s ease,color .15s ease}.header-action:hover{border-color:var(--border);background:color-mix(in srgb,var(--border) 35%,transparent)}.user-menu-divider{display:flex;height:1.75rem;align-items:center;margin-left:.2rem;border-left:1px solid var(--border);padding-left:.35rem}.mobile-backdrop{background:#0f172a29}.mobile-drawer{box-shadow:0 12px 30px #0f172a24;animation:slideDown .2s cubic-bezier(.16,1,.3,1)}.mobile-nav-link{display:flex;align-items:center;min-height:2.75rem;border-radius:.625rem;color:var(--secondary);padding:0 .85rem;font-size:.925rem;font-weight:700;text-decoration:none;transition:background-color .15s ease,color .15s ease,transform .15s ease}.mobile-nav-link:hover{color:var(--foreground);background:color-mix(in srgb,var(--border) 35%,transparent)}.mobile-active{color:var(--foreground)!important;background:color-mix(in srgb,var(--primary) 10%,transparent);font-weight:700}@keyframes slideDown{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}:focus-visible{outline:2px solid var(--primary);outline-offset:2px}:focus:not(:focus-visible){outline:none}lucide-icon,svg{display:inline-flex;vertical-align:middle}*{transition-property:background-color,border-color,color;transition-duration:.15s;transition-timing-function:ease-in-out}@media(min-width:640px){.header-container{padding-inline:1.25rem}.brand-name{display:inline-block}.header-actions{gap:.25rem}.user-menu-divider{padding-left:.5rem}}@media(min-width:768px){.header-middle{display:flex}.header-spacer{display:block}.mobile-search-action{display:none}}@media(min-width:1024px){.header-container{padding-inline:1.5rem}.desktop-nav{display:flex}.mobile-menu-action{display:none}}@media(max-width:420px){.current-app-name{max-width:8rem}.header-actions{gap:0;padding:0;background:transparent}.header-action{width:2rem;height:2rem}}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "component", type: AppSwitcherComponent, selector: "lib-app-switcher", inputs: ["applications"], outputs: ["applicationSelected"] }, { kind: "component", type: NotificationBellComponent, selector: "lib-notification-bell", inputs: ["unreadCount", "notifications"], outputs: ["notificationClick", "markRead", "clearAll"] }, { kind: "component", type: UserMenuComponent, selector: "lib-user-menu", inputs: ["user"], outputs: ["menuAction"] }, { kind: "component", type: IconComponent, selector: "lib-icon", inputs: ["name", "size", "color", "strokeWidth"] }] });
550
622
  }
551
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: HeaderComponent, decorators: [{
623
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: HeaderComponent, decorators: [{
552
624
  type: Component,
553
625
  args: [{ selector: 'lib-header', standalone: true, imports: [
554
- CommonModule,
555
626
  RouterLink,
556
627
  RouterLinkActive,
557
628
  AppSwitcherComponent,
558
629
  NotificationBellComponent,
559
630
  UserMenuComponent,
560
631
  IconComponent,
561
- ], template: "<header class=\"header-container\">\r\n\r\n <!-- Left Side: Brand, Current App & Desktop Links -->\r\n <div class=\"header-left\">\r\n\r\n <a routerLink=\"/\" class=\"brand-identity\">\r\n <img [src]=\"logoUrl()\" [alt]=\"appName() + ' logo'\" width=\"26\" height=\"26\" class=\"brand-logo\" />\r\n <span class=\"brand-name\">{{ appName() }}</span>\r\n </a>\r\n\r\n @if (currentAppName()) {\r\n <div class=\"current-app-identity\" [attr.aria-label]=\"'Current app: ' + currentAppName()\">\r\n <span class=\"brand-separator\" aria-hidden=\"true\">|</span>\r\n <span class=\"current-app-name\">{{ currentAppName() }}</span>\r\n </div>\r\n }\r\n\r\n <!-- Desktop Navigation Links -->\r\n <nav class=\"desktop-nav ml-1 hidden items-center lg:flex\" aria-label=\"Desktop primary navigation\">\r\n @for (item of navigationItems(); track item.id) {\r\n @if (hasPermission(item)) {\r\n @if (item.route) {\r\n <a [routerLink]=\"item.route\" routerLinkActive=\"active\" [routerLinkActiveOptions]=\"{exact: true}\" class=\"nav-link\"\r\n [attr.aria-current]=\"isActive(item) ? 'page' : null\">\r\n {{ item.label }}\r\n </a>\r\n } @else {\r\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" class=\"nav-link\">\r\n {{ item.label }}\r\n </a>\r\n }\r\n }\r\n }\r\n </nav>\r\n </div>\r\n\r\n <!-- Middle: Search Input (Desktop) -->\r\n @if (showSearch()) {\r\n <div class=\"header-middle\">\r\n <div class=\"header-search-shell\">\r\n <lib-icon name=\"search\" [size]=\"14\"\r\n class=\"pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-[var(--secondary)]\" />\r\n\r\n <input type=\"search\" placeholder=\"Search Excellore...\" #searchInput\r\n (keyup.enter)=\"onSearch(searchInput.value); searchInput.value = ''\" class=\"header-search\" autocomplete=\"off\"\r\n aria-label=\"Search\" />\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"header-spacer\"></div>\r\n }\r\n\r\n <!-- Right Side Actions -->\r\n <div class=\"header-actions\">\r\n\r\n <!-- Mobile Search Trigger -->\r\n @if (showSearch()) {\r\n <button type=\"button\" class=\"header-action mobile-search-action\" (click)=\"toggleMobileMenu()\"\r\n aria-label=\"Open mobile menu\" aria-expanded=\"isMobileMenuOpen()\" #menuToggle>\r\n <lib-icon name=\"search\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\r\n </button>\r\n }\r\n\r\n <!-- Theme Switcher -->\r\n @if (showThemeSwitcher()) {\r\n <button type=\"button\" class=\"header-action\" (click)=\"themeService.toggleTheme()\" aria-label=\"Toggle theme\">\r\n @switch (themeService.theme()) {\r\n @case ('light') {\r\n <lib-icon name=\"sun\" [size]=\"16\" class=\"text-[var(--primary)]\" />\r\n }\r\n @case ('dark') {\r\n <lib-icon name=\"moon\" [size]=\"16\" class=\"text-[var(--primary)]\" />\r\n }\r\n @default {\r\n <lib-icon name=\"monitor\" [size]=\"16\" class=\"text-[var(--secondary)]\" />\r\n }\r\n }\r\n </button>\r\n }\r\n\r\n <!-- App Switcher -->\r\n @if (showAppSwitcher() && applications().length > 0) {\r\n <lib-app-switcher [applications]=\"applications()\" (applicationSelected)=\"onApplicationSelect($event)\" />\r\n }\r\n\r\n <!-- Notifications -->\r\n @if (showNotifications()) {\r\n <lib-notification-bell [unreadCount]=\"unreadCount()\" [notifications]=\"notifications()\"\r\n (notificationClick)=\"onNotificationClick($event)\" (markRead)=\"onMarkRead($event)\" (clearAll)=\"onClearAll()\" />\r\n }\r\n\r\n <!-- User Menu -->\n <div class=\"user-menu-divider\">\n <lib-user-menu [user]=\"user()\" (menuAction)=\"onMenuAction($event)\" />\n </div>\n\n <!-- Mobile Menu Toggle (Hamburger) -->\n @if (hasMobileMenuContent()) {\r\n <button type=\"button\" class=\"header-action mobile-menu-action\" (click)=\"toggleMobileMenu()\"\r\n aria-label=\"Toggle navigation menu\" aria-expanded=\"isMobileMenuOpen()\" #mobileMenuToggle>\r\n <lib-icon [name]=\"isMobileMenuOpen() ? 'x' : 'menu'\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\r\n </button>\r\n }\r\n </div>\r\n</header>\r\n\r\n<!-- Mobile Menu Backdrop -->\r\n@if (isMobileMenuOpen() && hasMobileMenuContent()) {\r\n<div class=\"mobile-backdrop fixed inset-0 z-30 lg:hidden\" (click)=\"closeMobileMenu()\"\r\n (keydown.escape)=\"closeMobileMenu()\" tabindex=\"-1\" aria-hidden=\"true\"></div>\r\n\r\n<!-- Mobile Navigation Drawer -->\r\n<div\r\n class=\"mobile-drawer fixed inset-x-2 top-16 z-40 flex max-h-[calc(100dvh-4.5rem)] flex-col rounded-2xl border border-[var(--border)] bg-[var(--background)] p-3 lg:hidden\"\r\n role=\"dialog\" aria-modal=\"true\" aria-label=\"Mobile navigation\" (keydown.escape)=\"closeMobileMenu()\">\r\n <!-- Mobile Search -->\r\n @if (showSearch()) {\r\n <div class=\"relative mb-3 w-full\">\r\n <lib-icon name=\"search\" [size]=\"16\"\r\n class=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[var(--secondary)] pointer-events-none\"\r\n aria-hidden=\"true\" />\r\n\r\n <input type=\"search\" placeholder=\"Search Excellore...\" #mobileSearchInput\r\n (keyup.enter)=\"onSearch(mobileSearchInput.value); mobileSearchInput.value = ''; closeMobileMenu()\"\r\n class=\"header-search mobile\" autocomplete=\"off\" aria-label=\"Search\" />\r\n </div>\r\n }\r\n\r\n <!-- Mobile Nav Links -->\r\n <nav class=\"flex flex-col gap-1 overflow-y-auto\" role=\"navigation\">\r\n @for (item of navigationItems(); track item.id) {\r\n @if (hasPermission(item)) {\r\n @if (item.route) {\r\n <a [routerLink]=\"item.route\" routerLinkActive=\"mobile-active\" [routerLinkActiveOptions]=\"{exact: true}\"\r\n (click)=\"closeMobileMenu()\" class=\"mobile-nav-link\" [attr.aria-current]=\"isActive(item) ? 'page' : null\">\r\n {{ item.label }}\r\n </a>\r\n } @else {\r\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" (click)=\"closeMobileMenu()\" class=\"mobile-nav-link\">\r\n {{ item.label }}\r\n </a>\r\n }\r\n }\r\n }\r\n </nav>\r\n</div>\r\n}\r\n", styles: [":host{display:block}.header-container{position:fixed;z-index:40;top:0;right:0;left:0;display:flex;height:3.75rem;align-items:center;justify-content:space-between;border-bottom:1px solid var(--border);color:var(--foreground);background:var(--background);box-shadow:0 1px 2px #0f172a0a;padding-inline:1rem;will-change:transform}.header-left{display:flex;min-width:0;align-items:center;gap:.75rem}.brand-identity{display:inline-flex;min-width:0;align-items:center;gap:.5rem;border-radius:.5rem;color:var(--foreground);text-decoration:none}.brand-identity:hover{opacity:.9}.brand-logo{width:1.625rem;height:1.625rem;flex-shrink:0;-webkit-user-select:none;user-select:none}.brand-name{display:none;color:var(--foreground);font-size:.95rem;font-weight:750;letter-spacing:-.01em;white-space:nowrap}.current-app-identity{display:inline-flex;min-width:0;align-items:center;gap:.5rem}.brand-separator{color:var(--border);font-size:1rem;font-weight:500}.current-app-name{max-width:11rem;overflow:hidden;color:var(--foreground);font-size:.875rem;font-weight:650;text-overflow:ellipsis;white-space:nowrap}.desktop-nav{display:none;align-items:center;gap:1.25rem;margin-left:.25rem;padding:0;background:transparent}.header-middle{display:none;flex:1 1 auto;justify-content:center;padding-inline:1rem}.header-spacer{display:none;flex:1 1 auto}.nav-link{position:relative;display:inline-flex;align-items:center;min-height:2rem;color:var(--secondary);padding:0;font-size:.78rem;font-weight:600;line-height:1;text-decoration:none;transition:color .15s ease,opacity .15s ease}.nav-link:hover{color:var(--foreground)}.nav-link.active{color:var(--foreground)!important;font-weight:700}.nav-link.active:after{content:\"\";position:absolute;right:0;bottom:-.85rem;left:0;height:.125rem;background-color:var(--primary);border-radius:9999px}.header-search-shell{position:relative;width:min(100%,20rem)}.header-search{width:100%;height:2.25rem;border:1px solid var(--border);border-radius:.5rem;outline:0;color:var(--foreground);background:var(--background);padding:0 .9rem 0 2.25rem;font-size:.78rem;transition:border-color .15s ease,box-shadow .15s ease}.header-search::placeholder{color:var(--secondary)}.header-search:focus{border-color:var(--primary);box-shadow:0 0 0 2px color-mix(in srgb,var(--primary) 16%,transparent)}.header-search.mobile{width:100%;height:2.6rem;border-radius:.625rem;font-size:.875rem}.header-search.mobile:focus{width:100%}.header-actions{display:flex;align-items:center;gap:.25rem;background:transparent}.header-action{display:grid;width:2.15rem;height:2.15rem;place-items:center;border:1px solid transparent;border-radius:.5rem;color:var(--foreground);background:transparent;cursor:pointer;transition:background-color .15s ease,border-color .15s ease,color .15s ease}.header-action:hover{border-color:var(--border);background:color-mix(in srgb,var(--border) 35%,transparent)}.user-menu-divider{display:flex;height:1.75rem;align-items:center;margin-left:.2rem;border-left:1px solid var(--border);padding-left:.35rem}.mobile-backdrop{background:#0f172a29}.mobile-drawer{box-shadow:0 12px 30px #0f172a24;animation:slideDown .2s cubic-bezier(.16,1,.3,1)}.mobile-nav-link{display:flex;align-items:center;min-height:2.75rem;border-radius:.625rem;color:var(--secondary);padding:0 .85rem;font-size:.925rem;font-weight:700;text-decoration:none;transition:background-color .15s ease,color .15s ease,transform .15s ease}.mobile-nav-link:hover{color:var(--foreground);background:color-mix(in srgb,var(--border) 35%,transparent)}.mobile-active{color:var(--foreground)!important;background:color-mix(in srgb,var(--primary) 10%,transparent);font-weight:700}@keyframes slideDown{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}:focus-visible{outline:2px solid var(--primary);outline-offset:2px}:focus:not(:focus-visible){outline:none}lucide-icon,svg{display:inline-flex;vertical-align:middle}*{transition-property:background-color,border-color,color;transition-duration:.15s;transition-timing-function:ease-in-out}@media(min-width:640px){.header-container{padding-inline:1.25rem}.brand-name{display:inline-block}.header-actions{gap:.25rem}.user-menu-divider{padding-left:.5rem}}@media(min-width:768px){.header-middle{display:flex}.header-spacer{display:block}.mobile-search-action{display:none}}@media(min-width:1024px){.header-container{padding-inline:1.5rem}.desktop-nav{display:flex}.mobile-menu-action{display:none}}@media(max-width:420px){.current-app-name{max-width:8rem}.header-actions{gap:0;padding:0;background:transparent}.header-action{width:2rem;height:2rem}}\n"] }]
632
+ ], template: "<header class=\"header-container\">\r\n\r\n <!-- Left Side: Brand, Current App & Desktop Links -->\r\n <div class=\"header-left\">\r\n\r\n <a routerLink=\"/\" class=\"brand-identity\">\r\n <img [src]=\"logoUrl()\" [alt]=\"appName() + ' logo'\" width=\"26\" height=\"26\" class=\"brand-logo\" />\r\n <span class=\"brand-name\">{{ appName() }}</span>\r\n </a>\r\n\r\n @if (currentAppName()) {\r\n <div class=\"current-app-identity\" [attr.aria-label]=\"'Current app: ' + currentAppName()\">\r\n <span class=\"brand-separator\" aria-hidden=\"true\">|</span>\r\n <span class=\"current-app-name\">{{ currentAppName() }}</span>\r\n </div>\r\n }\r\n\r\n <!-- Desktop Navigation Links -->\r\n <nav class=\"desktop-nav ml-1 hidden items-center lg:flex\" aria-label=\"Desktop primary navigation\">\r\n @for (item of navigationItems(); track item.id) {\r\n @if (hasPermission(item)) {\r\n @if (item.route) {\r\n <a [routerLink]=\"item.route\" routerLinkActive=\"active\" [routerLinkActiveOptions]=\"{exact: true}\" class=\"nav-link\"\r\n [attr.aria-current]=\"isActive(item) ? 'page' : null\">\r\n {{ item.label }}\r\n </a>\r\n } @else {\r\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" class=\"nav-link\">\r\n {{ item.label }}\r\n </a>\r\n }\r\n }\r\n }\r\n </nav>\r\n </div>\r\n\r\n <!-- Middle: Search Input (Desktop) -->\r\n @if (showSearch()) {\r\n <div class=\"header-middle\">\r\n <div class=\"header-search-shell\">\r\n <lib-icon name=\"search\" [size]=\"14\"\r\n class=\"pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-[var(--secondary)]\" />\r\n\r\n <input type=\"search\" placeholder=\"Search Excellore...\" #searchInput\r\n (keyup.enter)=\"onSearch(searchInput.value); searchInput.value = ''\" class=\"header-search\" autocomplete=\"off\"\r\n aria-label=\"Search\" />\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"header-spacer\"></div>\r\n }\r\n\r\n <!-- Right Side Actions -->\r\n <div class=\"header-actions\">\r\n\r\n <!-- Mobile Search Trigger -->\r\n @if (showSearch()) {\r\n <button type=\"button\" class=\"header-action mobile-search-action\" (click)=\"toggleMobileMenu()\"\r\n aria-label=\"Open mobile menu\" aria-expanded=\"isMobileMenuOpen()\" #menuToggle>\r\n <lib-icon name=\"search\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\r\n </button>\r\n }\r\n\r\n <!-- Theme Switcher -->\r\n @if (showThemeSwitcher()) {\r\n <button type=\"button\" class=\"header-action\" (click)=\"themeService.toggleTheme()\" aria-label=\"Toggle theme\">\r\n @switch (themeService.theme()) {\r\n @case ('light') {\r\n <lib-icon name=\"sun\" [size]=\"16\" class=\"text-[var(--primary)]\" />\r\n }\r\n @case ('dark') {\r\n <lib-icon name=\"moon\" [size]=\"16\" class=\"text-[var(--primary)]\" />\r\n }\r\n @default {\r\n <lib-icon name=\"monitor\" [size]=\"16\" class=\"text-[var(--secondary)]\" />\r\n }\r\n }\r\n </button>\r\n }\r\n\r\n <!-- App Switcher -->\r\n @if (showAppSwitcher() && applications().length > 0) {\r\n <lib-app-switcher [applications]=\"applications()\" (applicationSelected)=\"onApplicationSelect($event)\" />\r\n }\r\n\r\n <!-- Notifications -->\r\n @if (showNotifications()) {\r\n <lib-notification-bell [unreadCount]=\"unreadCount()\" [notifications]=\"notifications()\"\r\n (notificationClick)=\"onNotificationClick($event)\" (markRead)=\"onMarkRead($event)\" (clearAll)=\"onClearAll()\" />\r\n }\r\n\r\n <!-- User Menu -->\r\n <div class=\"user-menu-divider\">\r\n <lib-user-menu [user]=\"user()\" (menuAction)=\"onMenuAction($event)\" />\r\n </div>\r\n\r\n <!-- Mobile Menu Toggle (Hamburger) -->\r\n @if (hasMobileMenuContent()) {\r\n <button type=\"button\" class=\"header-action mobile-menu-action\" (click)=\"toggleMobileMenu()\"\r\n aria-label=\"Toggle navigation menu\" aria-expanded=\"isMobileMenuOpen()\" #mobileMenuToggle>\r\n <lib-icon [name]=\"isMobileMenuOpen() ? 'x' : 'menu'\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\r\n </button>\r\n }\r\n </div>\r\n</header>\r\n\r\n<!-- Mobile Menu Backdrop -->\r\n@if (isMobileMenuOpen() && hasMobileMenuContent()) {\r\n<div class=\"mobile-backdrop fixed inset-0 z-30 lg:hidden\" (click)=\"closeMobileMenu()\"\r\n (keydown.escape)=\"closeMobileMenu()\" tabindex=\"-1\" aria-hidden=\"true\"></div>\r\n\r\n<!-- Mobile Navigation Drawer -->\r\n<div\r\n class=\"mobile-drawer fixed inset-x-2 top-16 z-40 flex max-h-[calc(100dvh-4.5rem)] flex-col rounded-2xl border border-[var(--border)] bg-[var(--background)] p-3 lg:hidden\"\r\n role=\"dialog\" aria-modal=\"true\" aria-label=\"Mobile navigation\" (keydown.escape)=\"closeMobileMenu()\">\r\n <!-- Mobile Search -->\r\n @if (showSearch()) {\r\n <div class=\"relative mb-3 w-full\">\r\n <lib-icon name=\"search\" [size]=\"16\"\r\n class=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[var(--secondary)] pointer-events-none\"\r\n aria-hidden=\"true\" />\r\n\r\n <input type=\"search\" placeholder=\"Search Excellore...\" #mobileSearchInput\r\n (keyup.enter)=\"onSearch(mobileSearchInput.value); mobileSearchInput.value = ''; closeMobileMenu()\"\r\n class=\"header-search mobile\" autocomplete=\"off\" aria-label=\"Search\" />\r\n </div>\r\n }\r\n\r\n <!-- Mobile Nav Links -->\r\n <nav class=\"flex flex-col gap-1 overflow-y-auto\" role=\"navigation\">\r\n @for (item of navigationItems(); track item.id) {\r\n @if (hasPermission(item)) {\r\n @if (item.route) {\r\n <a [routerLink]=\"item.route\" routerLinkActive=\"mobile-active\" [routerLinkActiveOptions]=\"{exact: true}\"\r\n (click)=\"closeMobileMenu()\" class=\"mobile-nav-link\" [attr.aria-current]=\"isActive(item) ? 'page' : null\">\r\n {{ item.label }}\r\n </a>\r\n } @else {\r\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" (click)=\"closeMobileMenu()\" class=\"mobile-nav-link\">\r\n {{ item.label }}\r\n </a>\r\n }\r\n }\r\n }\r\n </nav>\r\n</div>\r\n}\r\n", styles: [":host{display:block}.header-container{position:fixed;z-index:40;top:0;right:0;left:0;display:flex;height:3.75rem;align-items:center;justify-content:space-between;border-bottom:1px solid var(--border);color:var(--foreground);background:var(--background);box-shadow:0 1px 2px #0f172a0a;padding-inline:1rem;will-change:transform}.header-left{display:flex;min-width:0;align-items:center;gap:.75rem}.brand-identity{display:inline-flex;min-width:0;align-items:center;gap:.5rem;border-radius:.5rem;color:var(--foreground);text-decoration:none}.brand-identity:hover{opacity:.9}.brand-logo{width:1.625rem;height:1.625rem;flex-shrink:0;-webkit-user-select:none;user-select:none}.brand-name{display:none;color:var(--foreground);font-size:.95rem;font-weight:750;letter-spacing:-.01em;white-space:nowrap}.current-app-identity{display:inline-flex;min-width:0;align-items:center;gap:.5rem}.brand-separator{color:var(--border);font-size:1rem;font-weight:500}.current-app-name{max-width:11rem;overflow:hidden;color:var(--foreground);font-size:.875rem;font-weight:650;text-overflow:ellipsis;white-space:nowrap}.desktop-nav{display:none;align-items:center;gap:1.25rem;margin-left:.25rem;padding:0;background:transparent}.header-middle{display:none;flex:1 1 auto;justify-content:center;padding-inline:1rem}.header-spacer{display:none;flex:1 1 auto}.nav-link{position:relative;display:inline-flex;align-items:center;min-height:2rem;color:var(--secondary);padding:0;font-size:.78rem;font-weight:600;line-height:1;text-decoration:none;transition:color .15s ease,opacity .15s ease}.nav-link:hover{color:var(--foreground)}.nav-link.active{color:var(--foreground)!important;font-weight:700}.nav-link.active:after{content:\"\";position:absolute;right:0;bottom:-.85rem;left:0;height:.125rem;background-color:var(--primary);border-radius:9999px}.header-search-shell{position:relative;width:min(100%,20rem)}.header-search{width:100%;height:2.25rem;border:1px solid var(--border);border-radius:.5rem;outline:0;color:var(--foreground);background:var(--background);padding:0 .9rem 0 2.25rem;font-size:.78rem;transition:border-color .15s ease,box-shadow .15s ease}.header-search::placeholder{color:var(--secondary)}.header-search:focus{border-color:var(--primary);box-shadow:0 0 0 2px color-mix(in srgb,var(--primary) 16%,transparent)}.header-search.mobile{width:100%;height:2.6rem;border-radius:.625rem;font-size:.875rem}.header-search.mobile:focus{width:100%}.header-actions{display:flex;align-items:center;gap:.25rem;background:transparent}.header-action{display:grid;width:2.15rem;height:2.15rem;place-items:center;border:1px solid transparent;border-radius:.5rem;color:var(--foreground);background:transparent;cursor:pointer;transition:background-color .15s ease,border-color .15s ease,color .15s ease}.header-action:hover{border-color:var(--border);background:color-mix(in srgb,var(--border) 35%,transparent)}.user-menu-divider{display:flex;height:1.75rem;align-items:center;margin-left:.2rem;border-left:1px solid var(--border);padding-left:.35rem}.mobile-backdrop{background:#0f172a29}.mobile-drawer{box-shadow:0 12px 30px #0f172a24;animation:slideDown .2s cubic-bezier(.16,1,.3,1)}.mobile-nav-link{display:flex;align-items:center;min-height:2.75rem;border-radius:.625rem;color:var(--secondary);padding:0 .85rem;font-size:.925rem;font-weight:700;text-decoration:none;transition:background-color .15s ease,color .15s ease,transform .15s ease}.mobile-nav-link:hover{color:var(--foreground);background:color-mix(in srgb,var(--border) 35%,transparent)}.mobile-active{color:var(--foreground)!important;background:color-mix(in srgb,var(--primary) 10%,transparent);font-weight:700}@keyframes slideDown{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}:focus-visible{outline:2px solid var(--primary);outline-offset:2px}:focus:not(:focus-visible){outline:none}lucide-icon,svg{display:inline-flex;vertical-align:middle}*{transition-property:background-color,border-color,color;transition-duration:.15s;transition-timing-function:ease-in-out}@media(min-width:640px){.header-container{padding-inline:1.25rem}.brand-name{display:inline-block}.header-actions{gap:.25rem}.user-menu-divider{padding-left:.5rem}}@media(min-width:768px){.header-middle{display:flex}.header-spacer{display:block}.mobile-search-action{display:none}}@media(min-width:1024px){.header-container{padding-inline:1.5rem}.desktop-nav{display:flex}.mobile-menu-action{display:none}}@media(max-width:420px){.current-app-name{max-width:8rem}.header-actions{gap:0;padding:0;background:transparent}.header-action{width:2rem;height:2rem}}\n"] }]
562
633
  }], propDecorators: { user: [{ type: i0.Input, args: [{ isSignal: true, alias: "user", required: true }] }], permissions: [{ type: i0.Input, args: [{ isSignal: true, alias: "permissions", required: false }] }], navigationItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "navigationItems", required: false }] }], applications: [{ type: i0.Input, args: [{ isSignal: true, alias: "applications", required: false }] }], notifications: [{ type: i0.Input, args: [{ isSignal: true, alias: "notifications", required: false }] }], notificationCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "notificationCount", required: false }] }], logoUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "logoUrl", required: false }] }], appName: [{ type: i0.Input, args: [{ isSignal: true, alias: "appName", required: false }] }], currentAppName: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentAppName", required: false }] }], showSearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSearch", required: false }] }], showNotifications: [{ type: i0.Input, args: [{ isSignal: true, alias: "showNotifications", required: false }] }], showAppSwitcher: [{ type: i0.Input, args: [{ isSignal: true, alias: "showAppSwitcher", required: false }] }], showThemeSwitcher: [{ type: i0.Input, args: [{ isSignal: true, alias: "showThemeSwitcher", required: false }] }], search: [{ type: i0.Output, args: ["search"] }], applicationSelected: [{ type: i0.Output, args: ["applicationSelected"] }], notificationClick: [{ type: i0.Output, args: ["notificationClick"] }], markRead: [{ type: i0.Output, args: ["markRead"] }], clearAll: [{ type: i0.Output, args: ["clearAll"] }], menuAction: [{ type: i0.Output, args: ["menuAction"] }] } });
563
634
 
564
635
  class SidebarComponent {
636
+ host = inject(ElementRef);
637
+ menu = viewChild(SidebarMenuComponent, ...(ngDevMode ? [{ debugName: "menu" }] : /* istanbul ignore next */ []));
638
+ themeService = inject(ThemeService);
565
639
  menuItems = input([], ...(ngDevMode ? [{ debugName: "menuItems" }] : /* istanbul ignore next */ []));
566
640
  menuError = input(null, ...(ngDevMode ? [{ debugName: "menuError" }] : /* istanbul ignore next */ []));
567
641
  showMobileToggle = input(true, ...(ngDevMode ? [{ debugName: "showMobileToggle" }] : /* istanbul ignore next */ []));
642
+ workspace = input({ id: 'workspace', name: 'Workspace', initials: 'W' }, ...(ngDevMode ? [{ debugName: "workspace" }] : /* istanbul ignore next */ []));
643
+ workspaces = input([], ...(ngDevMode ? [{ debugName: "workspaces" }] : /* istanbul ignore next */ []));
644
+ user = input(null, ...(ngDevMode ? [{ debugName: "user" }] : /* istanbul ignore next */ []));
645
+ applications = input([], ...(ngDevMode ? [{ debugName: "applications" }] : /* istanbul ignore next */ []));
646
+ quickActions = input([], ...(ngDevMode ? [{ debugName: "quickActions" }] : /* istanbul ignore next */ []));
647
+ userActions = input([
648
+ { id: 'profile', label: 'Profile', icon: 'user' }, { id: 'settings', label: 'Settings', icon: 'settings' }, { id: 'logout', label: 'Log out', icon: 'logout', danger: true },
649
+ ], ...(ngDevMode ? [{ debugName: "userActions" }] : /* istanbul ignore next */ []));
650
+ collapsible = input(true, ...(ngDevMode ? [{ debugName: "collapsible" }] : /* istanbul ignore next */ []));
651
+ showSearch = input(true, ...(ngDevMode ? [{ debugName: "showSearch" }] : /* istanbul ignore next */ []));
652
+ showAppLauncher = input(true, ...(ngDevMode ? [{ debugName: "showAppLauncher" }] : /* istanbul ignore next */ []));
653
+ showNotifications = input(true, ...(ngDevMode ? [{ debugName: "showNotifications" }] : /* istanbul ignore next */ []));
654
+ showThemeToggle = input(true, ...(ngDevMode ? [{ debugName: "showThemeToggle" }] : /* istanbul ignore next */ []));
655
+ hasUnreadNotifications = input(false, ...(ngDevMode ? [{ debugName: "hasUnreadNotifications" }] : /* istanbul ignore next */ []));
568
656
  menuItemSelected = output();
657
+ workspaceSelected = output();
658
+ applicationSelected = output();
659
+ userActionSelected = output();
660
+ quickActionSelected = output();
661
+ notificationActivated = output();
662
+ collapsedChange = output();
569
663
  isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
570
- toggleSidebar() {
571
- this.isOpen.update((value) => !value);
572
- }
573
- closeSidebar() {
574
- this.isOpen.set(false);
664
+ collapsed = signal(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
665
+ workspaceMenuOpen = signal(false, ...(ngDevMode ? [{ debugName: "workspaceMenuOpen" }] : /* istanbul ignore next */ []));
666
+ profileMenuOpen = signal(false, ...(ngDevMode ? [{ debugName: "profileMenuOpen" }] : /* istanbul ignore next */ []));
667
+ appLauncherOpen = signal(false, ...(ngDevMode ? [{ debugName: "appLauncherOpen" }] : /* istanbul ignore next */ []));
668
+ commandOpen = signal(false, ...(ngDevMode ? [{ debugName: "commandOpen" }] : /* istanbul ignore next */ []));
669
+ appQuery = signal('', ...(ngDevMode ? [{ debugName: "appQuery" }] : /* istanbul ignore next */ []));
670
+ commandQuery = signal('', ...(ngDevMode ? [{ debugName: "commandQuery" }] : /* istanbul ignore next */ []));
671
+ activeCommandIndex = signal(0, ...(ngDevMode ? [{ debugName: "activeCommandIndex" }] : /* istanbul ignore next */ []));
672
+ returnFocus = null;
673
+ filteredApplications = computed(() => this.filterApplications(this.appQuery()), ...(ngDevMode ? [{ debugName: "filteredApplications" }] : /* istanbul ignore next */ []));
674
+ allCommands = computed(() => [
675
+ ...this.flattenMenu(this.menuItems()).filter(item => !item.children?.length).map(item => ({ id: `nav-${item.id}`, label: item.label, category: 'Navigate', icon: item.icon, disabled: item.disabled, kind: 'menu', source: item })),
676
+ ...this.applications().map(app => ({ id: `app-${app.id}`, label: app.name, category: 'Apps', icon: app.icon, disabled: app.available === false, kind: 'application', source: app })),
677
+ ...this.quickActions().map(action => ({ ...action, category: action.category || 'Quick actions', kind: 'action', source: action })),
678
+ ], ...(ngDevMode ? [{ debugName: "allCommands" }] : /* istanbul ignore next */ []));
679
+ filteredCommands = computed(() => {
680
+ const query = this.commandQuery().trim().toLowerCase();
681
+ return query ? this.allCommands().filter(command => command.label.toLowerCase().includes(query) || command.category?.toLowerCase().includes(query)) : this.allCommands();
682
+ }, ...(ngDevMode ? [{ debugName: "filteredCommands" }] : /* istanbul ignore next */ []));
683
+ commandGroups = computed(() => {
684
+ const groups = [];
685
+ for (const command of this.filteredCommands()) {
686
+ const category = command.category || 'Commands';
687
+ let group = groups.find(value => value.category === category);
688
+ if (!group) {
689
+ group = { category, commands: [] };
690
+ groups.push(group);
691
+ }
692
+ group.commands.push(command);
693
+ }
694
+ return groups;
695
+ }, ...(ngDevMode ? [{ debugName: "commandGroups" }] : /* istanbul ignore next */ []));
696
+ onDocumentKeydown(event) {
697
+ if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'k' && this.showSearch()) {
698
+ event.preventDefault();
699
+ this.openCommandPalette();
700
+ return;
701
+ }
702
+ if (event.key === 'Escape') {
703
+ this.closeAllOverlays(true);
704
+ return;
705
+ }
706
+ if (event.key === 'Tab' && (this.commandOpen() || this.appLauncherOpen())) {
707
+ this.trapDialogFocus(event);
708
+ return;
709
+ }
710
+ if (!this.commandOpen())
711
+ return;
712
+ const commands = this.filteredCommands();
713
+ if (event.key === 'ArrowDown') {
714
+ event.preventDefault();
715
+ this.activeCommandIndex.update(index => Math.min(index + 1, Math.max(0, commands.length - 1)));
716
+ }
717
+ if (event.key === 'ArrowUp') {
718
+ event.preventDefault();
719
+ this.activeCommandIndex.update(index => Math.max(index - 1, 0));
720
+ }
721
+ if (event.key === 'Enter') {
722
+ event.preventDefault();
723
+ const command = commands[this.activeCommandIndex()];
724
+ if (command)
725
+ this.runCommand(command);
726
+ }
575
727
  }
576
- onMenuItemSelected(item) {
577
- this.menuItemSelected.emit(item);
578
- this.closeSidebar();
728
+ onDocumentPointer(event) {
729
+ const target = event.target;
730
+ if (!(target instanceof Element))
731
+ return;
732
+ if (!target.closest('.workspace__trigger,.workspace-menu'))
733
+ this.workspaceMenuOpen.set(false);
734
+ if (!target.closest('.user__trigger,.profile-menu'))
735
+ this.profileMenuOpen.set(false);
736
+ if (!this.host.nativeElement.contains(target))
737
+ this.closeMenuFlyouts();
738
+ }
739
+ ngOnDestroy() { this.returnFocus = null; }
740
+ toggleSidebar() { this.isOpen.update(value => !value); }
741
+ closeSidebar() { this.isOpen.set(false); }
742
+ toggleCollapsed() { this.collapsed.update(value => !value); this.closeAllOverlays(false); this.collapsedChange.emit(this.collapsed()); }
743
+ toggleWorkspaceMenu(event) { this.captureTrigger(event); const next = !this.workspaceMenuOpen(); this.closeAllOverlays(false); this.workspaceMenuOpen.set(next); }
744
+ toggleProfileMenu(event) { this.captureTrigger(event); const next = !this.profileMenuOpen(); this.closeAllOverlays(false); this.profileMenuOpen.set(next); }
745
+ openAppLauncher(event) { this.captureTrigger(event); this.closeAllOverlays(false); this.appLauncherOpen.set(true); this.focusSoon('.launcher input'); }
746
+ openCommandPalette(event) { this.captureTrigger(event); this.closeAllOverlays(false); this.commandQuery.set(''); this.activeCommandIndex.set(0); this.commandOpen.set(true); this.focusSoon('.command-palette input'); }
747
+ closeAppLauncher() { this.appLauncherOpen.set(false); this.appQuery.set(''); this.restoreFocus(); }
748
+ closeCommandPalette() { this.commandOpen.set(false); this.commandQuery.set(''); this.restoreFocus(); }
749
+ closeAllOverlays(restoreFocus) { this.workspaceMenuOpen.set(false); this.profileMenuOpen.set(false); this.appLauncherOpen.set(false); this.commandOpen.set(false); this.closeMenuFlyouts(); if (restoreFocus)
750
+ this.restoreFocus(); }
751
+ closeMenuFlyouts() { this.menu()?.closeFlyouts(); }
752
+ onMenuItemSelected(item) { this.menuItemSelected.emit(item); this.closeSidebar(); }
753
+ selectWorkspace(item) { this.workspaceSelected.emit(item); this.workspaceMenuOpen.set(false); this.restoreFocus(); }
754
+ selectApplication(app) {
755
+ if (app.available === false)
756
+ return;
757
+ window.open(app.url, '_blank', 'noopener,noreferrer');
758
+ this.applicationSelected.emit(app);
759
+ this.closeAppLauncher();
760
+ }
761
+ selectUserAction(action) { this.userActionSelected.emit(action); this.profileMenuOpen.set(false); this.restoreFocus(); }
762
+ activateNotifications(event) { this.captureTrigger(event); this.notificationActivated.emit(); }
763
+ toggleTheme() { this.themeService.toggleTheme(); }
764
+ updateCommandQuery(value) { this.commandQuery.set(value); this.activeCommandIndex.set(0); }
765
+ runCommand(command) {
766
+ if (command.disabled)
767
+ return;
768
+ if (command.kind === 'menu')
769
+ this.onMenuItemSelected(command.source);
770
+ if (command.kind === 'application')
771
+ this.applicationSelected.emit(command.source);
772
+ if (command.kind === 'action')
773
+ this.quickActionSelected.emit(command.source);
774
+ this.closeCommandPalette();
775
+ }
776
+ commandIndex(command) { return this.filteredCommands().findIndex(item => item.id === command.id); }
777
+ initials(name) { return name.split(/\s+/).map(part => part[0]).join('').slice(0, 2).toUpperCase(); }
778
+ filterApplications(queryValue) { const query = queryValue.trim().toLowerCase(); return query ? this.applications().filter(app => app.name.toLowerCase().includes(query)) : this.applications(); }
779
+ flattenMenu(items) {
780
+ return items.flatMap(item => [item, ...this.flattenMenu(item.children || [])]);
781
+ }
782
+ captureTrigger(event) { const target = event?.currentTarget; if (target instanceof HTMLElement)
783
+ this.returnFocus = target; }
784
+ focusSoon(selector) { setTimeout(() => this.host.nativeElement.querySelector(selector)?.focus()); }
785
+ restoreFocus() { const element = this.returnFocus; this.returnFocus = null; setTimeout(() => element?.focus()); }
786
+ trapDialogFocus(event) {
787
+ const dialog = this.host.nativeElement.querySelector('.dialog');
788
+ const focusable = Array.from(dialog?.querySelectorAll('button:not(:disabled),input:not(:disabled),a[href]') ?? []);
789
+ if (!focusable.length)
790
+ return;
791
+ const first = focusable[0];
792
+ const last = focusable[focusable.length - 1];
793
+ if (event.shiftKey && document.activeElement === first) {
794
+ event.preventDefault();
795
+ last.focus();
796
+ }
797
+ else if (!event.shiftKey && document.activeElement === last) {
798
+ event.preventDefault();
799
+ first.focus();
800
+ }
579
801
  }
580
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: SidebarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
581
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: SidebarComponent, isStandalone: true, selector: "lib-sidebar", inputs: { menuItems: { classPropertyName: "menuItems", publicName: "menuItems", isSignal: true, isRequired: false, transformFunction: null }, menuError: { classPropertyName: "menuError", publicName: "menuError", isSignal: true, isRequired: false, transformFunction: null }, showMobileToggle: { classPropertyName: "showMobileToggle", publicName: "showMobileToggle", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { menuItemSelected: "menuItemSelected" }, ngImport: i0, template: "@if (showMobileToggle()) {\r\n<button\r\n class=\"sidebar-toggle\"\r\n type=\"button\"\r\n title=\"Toggle sidebar menu\"\r\n aria-label=\"Toggle sidebar menu\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n (click)=\"toggleSidebar()\"\r\n>\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n</button>\r\n}\r\n\r\n@if (isOpen()) {\r\n<div class=\"sidebar-overlay\" (click)=\"closeSidebar()\" (keydown.escape)=\"closeSidebar()\" tabindex=\"-1\" aria-hidden=\"true\"></div>\r\n}\r\n\r\n<aside class=\"sidebar\" [class.open]=\"isOpen()\" aria-label=\"Application sidebar\">\n <div class=\"sidebar__middle\">\n <lib-sidebar-menu\n [items]=\"menuItems()\"\n [errorMessage]=\"menuError()\"\n (menuItemSelected)=\"onMenuItemSelected($event)\"\n />\n </div>\n</aside>\n", styles: [":host{display:block}.sidebar-toggle{position:fixed;z-index:50;top:.75rem;left:.75rem;display:grid;gap:.25rem;width:2.5rem;height:2.5rem;place-content:center;border:1px solid var(--border, #e5e7eb);border-radius:.5rem;background:var(--background, #ffffff);box-shadow:0 1px 2px #0f172a14;cursor:pointer}.sidebar-toggle span{display:block;width:1.125rem;height:.125rem;border-radius:999px;background:var(--accent-primary, var(--primary, #0067b8))}.sidebar-overlay{position:fixed;z-index:40;inset:0;background:#0000004d}.sidebar{position:fixed;z-index:45;top:0;left:0;display:flex;width:17.1875rem;height:100dvh;flex-direction:column;overflow:hidden;border-right:1px solid var(--border-primary, var(--border, #e5e7eb));background:var(--bg-primary, var(--background, #ffffff));color:var(--text-primary, var(--foreground, #111827));transform:translate(-100%);transition:transform .3s ease-in-out}.sidebar.open{transform:translate(0)}.sidebar__middle{min-height:0;flex:1 1 auto;overflow:hidden}@media(min-width:768px){.sidebar-toggle,.sidebar-overlay{display:none}.sidebar{position:sticky;z-index:auto;transform:translate(0)}}\n"], dependencies: [{ kind: "component", type: SidebarMenuComponent, selector: "lib-sidebar-menu", inputs: ["items", "errorMessage", "defaultIcon"], outputs: ["menuItemSelected"] }] });
802
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: SidebarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
803
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: SidebarComponent, isStandalone: true, selector: "lib-sidebar", inputs: { menuItems: { classPropertyName: "menuItems", publicName: "menuItems", isSignal: true, isRequired: false, transformFunction: null }, menuError: { classPropertyName: "menuError", publicName: "menuError", isSignal: true, isRequired: false, transformFunction: null }, showMobileToggle: { classPropertyName: "showMobileToggle", publicName: "showMobileToggle", isSignal: true, isRequired: false, transformFunction: null }, workspace: { classPropertyName: "workspace", publicName: "workspace", isSignal: true, isRequired: false, transformFunction: null }, workspaces: { classPropertyName: "workspaces", publicName: "workspaces", isSignal: true, isRequired: false, transformFunction: null }, user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: false, transformFunction: null }, applications: { classPropertyName: "applications", publicName: "applications", isSignal: true, isRequired: false, transformFunction: null }, quickActions: { classPropertyName: "quickActions", publicName: "quickActions", isSignal: true, isRequired: false, transformFunction: null }, userActions: { classPropertyName: "userActions", publicName: "userActions", isSignal: true, isRequired: false, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, showSearch: { classPropertyName: "showSearch", publicName: "showSearch", isSignal: true, isRequired: false, transformFunction: null }, showAppLauncher: { classPropertyName: "showAppLauncher", publicName: "showAppLauncher", isSignal: true, isRequired: false, transformFunction: null }, showNotifications: { classPropertyName: "showNotifications", publicName: "showNotifications", isSignal: true, isRequired: false, transformFunction: null }, showThemeToggle: { classPropertyName: "showThemeToggle", publicName: "showThemeToggle", isSignal: true, isRequired: false, transformFunction: null }, hasUnreadNotifications: { classPropertyName: "hasUnreadNotifications", publicName: "hasUnreadNotifications", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { menuItemSelected: "menuItemSelected", workspaceSelected: "workspaceSelected", applicationSelected: "applicationSelected", userActionSelected: "userActionSelected", quickActionSelected: "quickActionSelected", notificationActivated: "notificationActivated", collapsedChange: "collapsedChange" }, host: { listeners: { "document:keydown": "onDocumentKeydown($event)", "document:mousedown": "onDocumentPointer($event)" } }, viewQueries: [{ propertyName: "menu", first: true, predicate: SidebarMenuComponent, descendants: true, isSignal: true }], ngImport: i0, template: "@if (showMobileToggle()) { <button class=\"sidebar-toggle\" type=\"button\" aria-label=\"Open sidebar\"\r\n [attr.aria-expanded]=\"isOpen()\" (click)=\"toggleSidebar()\"><lib-icon name=\"menu\" [size]=\"20\" /></button> }\r\n@if (isOpen()) { <div class=\"sidebar-overlay\" (click)=\"closeSidebar()\"></div> }\r\n\r\n<aside class=\"sidebar\" [class.open]=\"isOpen()\" [class.collapsed]=\"collapsed()\" aria-label=\"Application sidebar\">\r\n <div class=\"sidebar__workspace-wrap\">\r\n <button class=\"workspace workspace__trigger\" type=\"button\" [attr.aria-expanded]=\"workspaceMenuOpen()\"\r\n aria-haspopup=\"menu\" (click)=\"toggleWorkspaceMenu($event)\">\r\n <span class=\"workspace__avatar\">{{ workspace().initials || initials(workspace().name) }}</span>\r\n <span class=\"workspace__copy\"><strong>{{ workspace().name }}</strong>@if(workspace().plan){<small>{{\r\n workspace().plan }}</small>}</span>\r\n <lib-icon class=\"workspace__chevron\" name=\"chevronDown\" [size]=\"15\" />\r\n </button>\r\n @if (workspaceMenuOpen()) {\r\n <div class=\"popover workspace-menu\" role=\"menu\" aria-label=\"Select workspace\">\r\n @for (item of workspaces(); track item.id) {\r\n <button type=\"button\" role=\"menuitemradio\" [attr.aria-checked]=\"item.id === workspace().id\"\r\n (click)=\"selectWorkspace(item)\"><span class=\"mini-avatar\">{{ item.initials || initials(item.name)\r\n }}</span><span>{{ item.name }}</span>@if(item.id === workspace().id){<span class=\"check\">\u2713</span>}</button>\r\n }\r\n <div class=\"popover__divider\"></div><button type=\"button\" class=\"muted\"><lib-icon name=\"plus\" [size]=\"16\" />\r\n Create organization</button>\r\n </div>\r\n }\r\n </div>\r\n\r\n <div class=\"sidebar__tools\" aria-label=\"Sidebar utilities\">\r\n @if(showSearch() && !collapsed()){<button class=\"utility-button\" type=\"button\" title=\"Search (Ctrl/\u2318 K)\"\r\n aria-label=\"Search commands\" (click)=\"openCommandPalette($event)\"><lib-icon name=\"search\" [size]=\"16\" /></button>}\r\n @if(showAppLauncher()){<button class=\"utility-button\" type=\"button\" title=\"Apps\" aria-label=\"All apps\"\r\n (click)=\"openAppLauncher($event)\"><lib-icon name=\"grid\" [size]=\"16\" /></button>}\r\n @if(showNotifications() && !collapsed()){<button class=\"utility-button notification-button\" type=\"button\" title=\"Notifications\"\r\n aria-label=\"Notifications\" (click)=\"activateNotifications($event)\"><lib-icon name=\"bell\"\r\n [size]=\"16\" />@if(hasUnreadNotifications()){<span class=\"notification-dot\"></span>}</button>}\r\n @if(showThemeToggle() && !collapsed()){\r\n <button class=\"utility-button\" type=\"button\" [attr.title]=\"'Theme: ' + themeService.theme()\"\r\n [attr.aria-label]=\"'Current theme: ' + themeService.theme() + '. Toggle theme'\" (click)=\"toggleTheme()\">\r\n @switch (themeService.theme()) {\r\n @case ('light') { <lib-icon name=\"sun\" [size]=\"16\" /> }\r\n @case ('dark') { <lib-icon name=\"moon\" [size]=\"16\" /> }\r\n @default { <lib-icon name=\"monitor\" [size]=\"16\" /> }\r\n }\r\n </button>\r\n }\r\n @if (collapsible()) {\r\n <button class=\"collapse-toggle\" type=\"button\" [attr.title]=\"collapsed() ? 'Expand' : 'Collapse'\"\r\n [attr.aria-label]=\"collapsed() ? 'Expand sidebar' : 'Collapse sidebar'\" (click)=\"toggleCollapsed()\">\r\n <lib-icon [name]=\"collapsed() ? 'chevronRight' : 'chevronLeft'\" [size]=\"14\" [strokeWidth]=\"2\" />\r\n </button>\r\n }\r\n </div>\r\n\r\n <div class=\"sidebar__middle\" [style.overflow]=\"collapsed() ? 'visible' : null\"><lib-sidebar-menu [items]=\"menuItems()\"\r\n [errorMessage]=\"menuError()\" [compact]=\"collapsed()\" (menuItemSelected)=\"onMenuItemSelected($event)\" /></div>\r\n\r\n @if (user()) {\r\n <div class=\"sidebar__user-wrap\">\r\n @if (profileMenuOpen()) { <div class=\"popover profile-menu\" role=\"menu\">@for(action of userActions();track\r\n action.id){<button type=\"button\" role=\"menuitem\" [class.danger]=\"action.danger\"\r\n (click)=\"selectUserAction(action)\"><lib-icon [name]=\"action.icon || 'user'\"\r\n [size]=\"16\" />{{action.label}}</button>}</div> }\r\n <button class=\"user user__trigger\" type=\"button\" [attr.aria-expanded]=\"profileMenuOpen()\" aria-haspopup=\"menu\"\r\n (click)=\"toggleProfileMenu($event)\">\r\n <span class=\"user__avatar\">{{ initials(user()!.name) }}</span><span class=\"user__copy\"><strong>{{ user()!.name\r\n }}</strong><small>{{ user()!.email }}</small></span><lib-icon class=\"user__more\" name=\"more\" [size]=\"17\" />\r\n </button>\r\n </div>\r\n }\r\n</aside>\r\n\r\n@if (appLauncherOpen()) {\r\n<div class=\"dialog-backdrop\" (click)=\"closeAppLauncher()\">\r\n <section class=\"dialog launcher\" role=\"dialog\" aria-modal=\"true\" aria-label=\"All applications\"\r\n (click)=\"$event.stopPropagation()\">\r\n <div class=\"dialog__search\"><lib-icon name=\"search\" [size]=\"18\" /><input #appSearch type=\"search\"\r\n placeholder=\"Search apps\" [value]=\"appQuery()\" (input)=\"appQuery.set(appSearch.value)\"><button type=\"button\"\r\n aria-label=\"Close app launcher\" (click)=\"closeAppLauncher()\"><lib-icon name=\"x\" [size]=\"18\" /></button></div>\r\n <div class=\"launcher__body\">@if(filteredApplications().length === 0){<p class=\"empty-state\">No apps found for\r\n \u201C{{appQuery()}}\u201D</p>}@else{<div class=\"launcher__grid\">@for(app of filteredApplications();track app.id){<button\r\n type=\"button\" [disabled]=\"app.available === false\" (click)=\"selectApplication(app)\"><span class=\"app-icon\"\r\n [style.background]=\"app.iconBackground || null\"\r\n [style.color]=\"app.iconColor || null\">@if(app.icon){<lib-icon [name]=\"app.icon\" [size]=\"20\" />}@else { {{\r\n app.initials || initials(app.name) }} }</span><span>{{app.name}}</span></button>}</div>}</div>\r\n </section>\r\n</div>\r\n}\r\n\r\n@if (commandOpen()) {\r\n<div class=\"dialog-backdrop command-backdrop\" (click)=\"closeCommandPalette()\">\r\n <section class=\"dialog command-palette\" role=\"dialog\" aria-modal=\"true\" aria-label=\"Command palette\"\r\n (click)=\"$event.stopPropagation()\">\r\n <div class=\"dialog__search\"><lib-icon name=\"search\" [size]=\"18\" /><input #commandSearch type=\"search\"\r\n placeholder=\"Type a command or search...\" [value]=\"commandQuery()\"\r\n (input)=\"updateCommandQuery(commandSearch.value)\"><kbd>esc</kbd></div>\r\n <div class=\"command-palette__body\">@if(filteredCommands().length === 0){<p class=\"empty-state\">No results for\r\n \u201C{{commandQuery()}}\u201D</p>}@else{@for(group of commandGroups();track group.category){<div class=\"command-group\">\r\n <h2>{{group.category}}</h2>@for(command of group.commands;track command.id){<button type=\"button\"\r\n [disabled]=\"command.disabled\" [class.active]=\"commandIndex(command) === activeCommandIndex()\"\r\n (mouseenter)=\"activeCommandIndex.set(commandIndex(command))\" (click)=\"runCommand(command)\"><lib-icon\r\n [name]=\"command.icon || 'dashboard'\" [size]=\"16\" /><span>{{command.label}}</span></button>}\r\n </div>}}</div>\r\n <footer class=\"command-palette__footer\"><span>\u21B5 select</span><span>\u2191\u2193 navigate</span><span>esc close</span></footer>\r\n </section>\r\n</div>\r\n}\r\n", styles: [":host{display:block;position:relative;width:var(--rail-width, 16rem);padding:0;box-sizing:border-box;font-family:Inter,system-ui,sans-serif;transition:width .2s;--rail-width: 16rem;--rail-collapsed-width: 4.5rem;--rail-radius: 0;--rail-bg: #fff;--rail-border: #e2e8f0;--rail-text: #475569;--rail-text-strong: #0f172a;--rail-muted: #94a3b8;--rail-hover: #f1f5f9;--rail-active-bg: #eef2ff;--rail-active: #4f46e5;--rail-accent: #6366f1;--rail-focus: #818cf8;--rail-danger: #dc2626;--rail-popover: #fff;--rail-popover-shadow: 0 14px 34px #0f172a24;--rail-overlay: #0f172a80;--rail-dialog-bg: #fff;--rail-dialog-border: #e2e8f0;--rail-input: #0f172a;--rail-subtle: #f8fafc }:host-context(.dark){--rail-bg: #020617;--rail-border: #1e293b;--rail-text: #94a3b8;--rail-text-strong: #e2e8f0;--rail-muted: #64748b;--rail-hover: #0f172a;--rail-active-bg: #172033;--rail-active: #a5b4fc;--rail-danger: #f87171;--rail-popover: #0f172a;--rail-popover-shadow: 0 16px 40px #0007;--rail-overlay: #000a;--rail-dialog-bg: #0f172a;--rail-dialog-border: #263449;--rail-input: #e2e8f0;--rail-subtle: #111c2f }.sidebar{position:sticky;z-index:20;top:0;display:flex;width:var(--rail-width);height:100dvh;flex-direction:column;overflow:visible;border:1px solid var(--rail-border);border-radius:var(--rail-radius);background:var(--rail-bg);color:var(--rail-text);transition:width .2s,transform .25s,background-color .2s}.sidebar.collapsed{width:var(--rail-collapsed-width)}.collapse-toggle{position:absolute;z-index:35;top:auto;right:0;bottom:-.75rem;display:grid;width:1.5rem;height:1.5rem;place-items:center;border:1px solid var(--rail-border);border-radius:50%;background:var(--rail-dialog-bg);box-shadow:0 2px 8px #0f172a29;color:var(--rail-text);cursor:pointer;transform:translate(50%)}button:focus-visible,a:focus-visible,input:focus-visible{outline:2px solid var(--rail-focus);outline-offset:2px}.sidebar__workspace-wrap{position:relative;padding:1rem .75rem .25rem}.workspace,.user{display:flex;width:100%;align-items:center;gap:.55rem;border:0;border-radius:.5rem;background:transparent;color:inherit;cursor:pointer;text-align:left}.workspace{min-height:2.75rem;padding:.35rem .5rem}.workspace:hover,.user:hover{background:var(--rail-hover)}.workspace__avatar{display:grid;width:2rem;height:2rem;flex:0 0 auto;place-items:center;border-radius:.5rem;background:var(--rail-accent);color:#fff;font-size:.8rem;font-weight:700}.workspace__copy,.user__copy{display:grid;min-width:0;flex:1;gap:.08rem}.workspace strong,.user strong{overflow:hidden;color:var(--rail-text-strong);font-size:.8125rem;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.workspace small,.user small{overflow:hidden;color:var(--rail-muted);font-size:.7rem;text-overflow:ellipsis;white-space:nowrap}.workspace__chevron,.user__more{margin-left:auto;color:var(--rail-muted)}.sidebar__tools{position:relative;display:flex;align-items:center;gap:.15rem;margin-bottom:.75rem;border-bottom:1px solid var(--rail-border);padding:.65rem 1rem .9rem}.utility-button{position:relative;display:grid;width:2rem;height:2rem;place-items:center;border:0;border-radius:.4rem;background:transparent;color:var(--rail-text);cursor:pointer}.utility-button:hover{background:var(--rail-hover);color:var(--rail-text-strong)}.notification-dot{position:absolute;top:.4rem;right:.42rem;width:.38rem;height:.38rem;border:1px solid var(--rail-bg);border-radius:50%;background:var(--rail-accent)}.sidebar__middle{min-height:0;flex:1;overflow-x:visible;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--rail-border) transparent}.sidebar__user-wrap{position:relative;margin-top:.5rem;padding:.75rem;border-top:1px solid var(--rail-border)}.user{min-height:2.5rem;padding:.25rem}.user__avatar{display:grid;width:2rem;height:2rem;flex:0 0 auto;place-items:center;border-radius:50%;background:#475569;color:#fff;font-size:.7rem;font-weight:700}.popover{position:absolute;z-index:40;left:calc(100% + .65rem);display:grid;width:12rem;border:1px solid var(--rail-border);border-radius:.55rem;padding:.3rem;background:var(--rail-popover);box-shadow:var(--rail-popover-shadow)}.workspace-menu{top:1rem}.profile-menu{bottom:.75rem}.popover button{display:flex;min-height:2.15rem;align-items:center;gap:.55rem;border:0;border-radius:.35rem;padding:.4rem .5rem;background:transparent;color:var(--rail-text-strong);cursor:pointer;font:inherit;font-size:.8rem;text-align:left}.popover button:hover{background:var(--rail-hover)}.mini-avatar{display:grid;width:1.55rem;height:1.55rem;place-items:center;border-radius:.35rem;background:var(--rail-hover);font-size:.68rem}.check{margin-left:auto;color:var(--rail-active)}.popover__divider{height:1px;margin:.25rem;background:var(--rail-border)}.popover .muted{color:var(--rail-text)}.popover .danger{color:var(--rail-danger)}.sidebar.collapsed .workspace__copy,.sidebar.collapsed .workspace__chevron,.sidebar.collapsed .user__copy,.sidebar.collapsed .user__more{display:none}.sidebar.collapsed .workspace,.sidebar.collapsed .user{justify-content:center}.sidebar.collapsed .sidebar__tools{flex-direction:column;padding-inline:.75rem}.sidebar.collapsed .sidebar__workspace-wrap{padding-inline:.75rem}.sidebar-toggle{display:none}.sidebar-overlay{position:fixed;z-index:40;inset:0;background:var(--rail-overlay)}@media(max-width:767px){:host{width:0;padding:0}.sidebar{position:fixed;z-index:45;inset:0 auto 0 0;width:min(var(--rail-width),88vw);height:100dvh;border-width:0 1px 0 0;border-radius:0;transform:translate(-105%)}.sidebar.open{transform:none}.sidebar.collapsed{width:min(var(--rail-width),88vw)}.collapse-toggle{display:none}.sidebar-toggle{position:fixed;z-index:50;top:.75rem;left:.75rem;display:grid;width:2.5rem;height:2.5rem;place-items:center;border:1px solid var(--rail-border);border-radius:.5rem;background:var(--rail-dialog-bg);box-shadow:0 2px 8px #0f172a24;color:var(--rail-text-strong);cursor:pointer}.sidebar.collapsed .workspace__copy,.sidebar.collapsed .workspace__chevron,.sidebar.collapsed .user__copy,.sidebar.collapsed .user__more{display:grid}.sidebar.collapsed .sidebar__tools{flex-direction:row}.popover{left:.75rem;right:.75rem;width:auto}.workspace-menu{top:4.2rem}.profile-menu{bottom:4.2rem}}\n", ".dialog-backdrop{position:fixed;z-index:100;inset:0;display:flex;justify-content:center;align-items:flex-start;padding:8rem 1rem 1rem;background:var(--rail-overlay)}.dialog{width:min(28rem,100%);max-height:min(30rem,calc(100dvh - 10rem));overflow:hidden;border:1px solid var(--rail-dialog-border);border-radius:.75rem;background:var(--rail-dialog-bg);box-shadow:0 24px 60px #0005;color:var(--rail-text-strong)}.dialog__search{display:flex;height:3rem;align-items:center;gap:.55rem;border-bottom:1px solid var(--rail-dialog-border);padding:0 .9rem;color:var(--rail-muted)}.dialog__search input{min-width:0;flex:1;border:0;outline:0;background:transparent;color:var(--rail-input);font:inherit;font-size:.82rem}.dialog__search button{display:grid;place-items:center;border:0;background:transparent;color:inherit;cursor:pointer}.dialog__search kbd{border:1px solid var(--rail-dialog-border);border-radius:.25rem;padding:.12rem .35rem;font-size:.62rem}.launcher__body,.command-palette__body{max-height:22rem;overflow-y:auto;padding:.7rem}.launcher__grid{display:grid;grid-template-columns:repeat(3,1fr);gap:.15rem}.launcher__grid button{display:flex;min-height:5.25rem;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;border:0;border-radius:.55rem;background:transparent;color:var(--rail-text);cursor:pointer;font:inherit;font-size:.72rem}.launcher__grid button:hover{background:var(--rail-hover)}.launcher__grid button:disabled,.command-group button:disabled{cursor:not-allowed;opacity:.4}.app-icon{display:grid;width:2.5rem;height:2.5rem;place-items:center;border-radius:.5rem;background:#dbeafe;color:#2563eb;font-size:.72rem;font-weight:700}.launcher__grid button:nth-child(3n+2) .app-icon{background:#ffe4e6;color:#e11d48}.launcher__grid button:nth-child(3n) .app-icon{background:#fef3c7;color:#d97706}.empty-state{margin:0;padding:2rem;text-align:center;color:var(--rail-muted);font-size:.8rem}.command-palette{width:min(32rem,100%)}.command-group{display:grid;gap:.1rem;margin-bottom:.55rem}.command-group:last-child{margin-bottom:0}.command-group h2{margin:.15rem .5rem;color:var(--rail-muted);font-size:.65rem;font-weight:600;letter-spacing:.05em;text-transform:uppercase}.command-group button{display:flex;width:100%;height:2.1rem;align-items:center;gap:.6rem;border:0;border-radius:.35rem;padding:0 .5rem;background:transparent;color:var(--rail-text);cursor:pointer;font:inherit;font-size:.8rem;text-align:left}.command-group button:hover,.command-group button.active{background:var(--rail-active-bg);color:var(--rail-active)}.command-group button span{flex:1}.command-palette__footer{display:flex;gap:1rem;border-top:1px solid var(--rail-dialog-border);padding:.5rem .9rem;color:var(--rail-muted);font-size:.65rem}@media(max-width:767px){.dialog-backdrop{padding-top:4rem}}@media(max-width:420px){.launcher__grid{grid-template-columns:repeat(2,1fr)}}\n"], dependencies: [{ kind: "component", type: SidebarMenuComponent, selector: "lib-sidebar-menu", inputs: ["items", "errorMessage", "defaultIcon", "compact"], outputs: ["menuItemSelected", "menuDismissed"] }, { kind: "component", type: IconComponent, selector: "lib-icon", inputs: ["name", "size", "color", "strokeWidth"] }] });
582
804
  }
583
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: SidebarComponent, decorators: [{
805
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: SidebarComponent, decorators: [{
584
806
  type: Component,
585
- args: [{ selector: 'lib-sidebar', standalone: true, imports: [SidebarMenuComponent], template: "@if (showMobileToggle()) {\r\n<button\r\n class=\"sidebar-toggle\"\r\n type=\"button\"\r\n title=\"Toggle sidebar menu\"\r\n aria-label=\"Toggle sidebar menu\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n (click)=\"toggleSidebar()\"\r\n>\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n</button>\r\n}\r\n\r\n@if (isOpen()) {\r\n<div class=\"sidebar-overlay\" (click)=\"closeSidebar()\" (keydown.escape)=\"closeSidebar()\" tabindex=\"-1\" aria-hidden=\"true\"></div>\r\n}\r\n\r\n<aside class=\"sidebar\" [class.open]=\"isOpen()\" aria-label=\"Application sidebar\">\n <div class=\"sidebar__middle\">\n <lib-sidebar-menu\n [items]=\"menuItems()\"\n [errorMessage]=\"menuError()\"\n (menuItemSelected)=\"onMenuItemSelected($event)\"\n />\n </div>\n</aside>\n", styles: [":host{display:block}.sidebar-toggle{position:fixed;z-index:50;top:.75rem;left:.75rem;display:grid;gap:.25rem;width:2.5rem;height:2.5rem;place-content:center;border:1px solid var(--border, #e5e7eb);border-radius:.5rem;background:var(--background, #ffffff);box-shadow:0 1px 2px #0f172a14;cursor:pointer}.sidebar-toggle span{display:block;width:1.125rem;height:.125rem;border-radius:999px;background:var(--accent-primary, var(--primary, #0067b8))}.sidebar-overlay{position:fixed;z-index:40;inset:0;background:#0000004d}.sidebar{position:fixed;z-index:45;top:0;left:0;display:flex;width:17.1875rem;height:100dvh;flex-direction:column;overflow:hidden;border-right:1px solid var(--border-primary, var(--border, #e5e7eb));background:var(--bg-primary, var(--background, #ffffff));color:var(--text-primary, var(--foreground, #111827));transform:translate(-100%);transition:transform .3s ease-in-out}.sidebar.open{transform:translate(0)}.sidebar__middle{min-height:0;flex:1 1 auto;overflow:hidden}@media(min-width:768px){.sidebar-toggle,.sidebar-overlay{display:none}.sidebar{position:sticky;z-index:auto;transform:translate(0)}}\n"] }]
586
- }], propDecorators: { menuItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuItems", required: false }] }], menuError: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuError", required: false }] }], showMobileToggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "showMobileToggle", required: false }] }], menuItemSelected: [{ type: i0.Output, args: ["menuItemSelected"] }] } });
807
+ args: [{ selector: 'lib-sidebar', standalone: true, imports: [SidebarMenuComponent, IconComponent], template: "@if (showMobileToggle()) { <button class=\"sidebar-toggle\" type=\"button\" aria-label=\"Open sidebar\"\r\n [attr.aria-expanded]=\"isOpen()\" (click)=\"toggleSidebar()\"><lib-icon name=\"menu\" [size]=\"20\" /></button> }\r\n@if (isOpen()) { <div class=\"sidebar-overlay\" (click)=\"closeSidebar()\"></div> }\r\n\r\n<aside class=\"sidebar\" [class.open]=\"isOpen()\" [class.collapsed]=\"collapsed()\" aria-label=\"Application sidebar\">\r\n <div class=\"sidebar__workspace-wrap\">\r\n <button class=\"workspace workspace__trigger\" type=\"button\" [attr.aria-expanded]=\"workspaceMenuOpen()\"\r\n aria-haspopup=\"menu\" (click)=\"toggleWorkspaceMenu($event)\">\r\n <span class=\"workspace__avatar\">{{ workspace().initials || initials(workspace().name) }}</span>\r\n <span class=\"workspace__copy\"><strong>{{ workspace().name }}</strong>@if(workspace().plan){<small>{{\r\n workspace().plan }}</small>}</span>\r\n <lib-icon class=\"workspace__chevron\" name=\"chevronDown\" [size]=\"15\" />\r\n </button>\r\n @if (workspaceMenuOpen()) {\r\n <div class=\"popover workspace-menu\" role=\"menu\" aria-label=\"Select workspace\">\r\n @for (item of workspaces(); track item.id) {\r\n <button type=\"button\" role=\"menuitemradio\" [attr.aria-checked]=\"item.id === workspace().id\"\r\n (click)=\"selectWorkspace(item)\"><span class=\"mini-avatar\">{{ item.initials || initials(item.name)\r\n }}</span><span>{{ item.name }}</span>@if(item.id === workspace().id){<span class=\"check\">\u2713</span>}</button>\r\n }\r\n <div class=\"popover__divider\"></div><button type=\"button\" class=\"muted\"><lib-icon name=\"plus\" [size]=\"16\" />\r\n Create organization</button>\r\n </div>\r\n }\r\n </div>\r\n\r\n <div class=\"sidebar__tools\" aria-label=\"Sidebar utilities\">\r\n @if(showSearch() && !collapsed()){<button class=\"utility-button\" type=\"button\" title=\"Search (Ctrl/\u2318 K)\"\r\n aria-label=\"Search commands\" (click)=\"openCommandPalette($event)\"><lib-icon name=\"search\" [size]=\"16\" /></button>}\r\n @if(showAppLauncher()){<button class=\"utility-button\" type=\"button\" title=\"Apps\" aria-label=\"All apps\"\r\n (click)=\"openAppLauncher($event)\"><lib-icon name=\"grid\" [size]=\"16\" /></button>}\r\n @if(showNotifications() && !collapsed()){<button class=\"utility-button notification-button\" type=\"button\" title=\"Notifications\"\r\n aria-label=\"Notifications\" (click)=\"activateNotifications($event)\"><lib-icon name=\"bell\"\r\n [size]=\"16\" />@if(hasUnreadNotifications()){<span class=\"notification-dot\"></span>}</button>}\r\n @if(showThemeToggle() && !collapsed()){\r\n <button class=\"utility-button\" type=\"button\" [attr.title]=\"'Theme: ' + themeService.theme()\"\r\n [attr.aria-label]=\"'Current theme: ' + themeService.theme() + '. Toggle theme'\" (click)=\"toggleTheme()\">\r\n @switch (themeService.theme()) {\r\n @case ('light') { <lib-icon name=\"sun\" [size]=\"16\" /> }\r\n @case ('dark') { <lib-icon name=\"moon\" [size]=\"16\" /> }\r\n @default { <lib-icon name=\"monitor\" [size]=\"16\" /> }\r\n }\r\n </button>\r\n }\r\n @if (collapsible()) {\r\n <button class=\"collapse-toggle\" type=\"button\" [attr.title]=\"collapsed() ? 'Expand' : 'Collapse'\"\r\n [attr.aria-label]=\"collapsed() ? 'Expand sidebar' : 'Collapse sidebar'\" (click)=\"toggleCollapsed()\">\r\n <lib-icon [name]=\"collapsed() ? 'chevronRight' : 'chevronLeft'\" [size]=\"14\" [strokeWidth]=\"2\" />\r\n </button>\r\n }\r\n </div>\r\n\r\n <div class=\"sidebar__middle\" [style.overflow]=\"collapsed() ? 'visible' : null\"><lib-sidebar-menu [items]=\"menuItems()\"\r\n [errorMessage]=\"menuError()\" [compact]=\"collapsed()\" (menuItemSelected)=\"onMenuItemSelected($event)\" /></div>\r\n\r\n @if (user()) {\r\n <div class=\"sidebar__user-wrap\">\r\n @if (profileMenuOpen()) { <div class=\"popover profile-menu\" role=\"menu\">@for(action of userActions();track\r\n action.id){<button type=\"button\" role=\"menuitem\" [class.danger]=\"action.danger\"\r\n (click)=\"selectUserAction(action)\"><lib-icon [name]=\"action.icon || 'user'\"\r\n [size]=\"16\" />{{action.label}}</button>}</div> }\r\n <button class=\"user user__trigger\" type=\"button\" [attr.aria-expanded]=\"profileMenuOpen()\" aria-haspopup=\"menu\"\r\n (click)=\"toggleProfileMenu($event)\">\r\n <span class=\"user__avatar\">{{ initials(user()!.name) }}</span><span class=\"user__copy\"><strong>{{ user()!.name\r\n }}</strong><small>{{ user()!.email }}</small></span><lib-icon class=\"user__more\" name=\"more\" [size]=\"17\" />\r\n </button>\r\n </div>\r\n }\r\n</aside>\r\n\r\n@if (appLauncherOpen()) {\r\n<div class=\"dialog-backdrop\" (click)=\"closeAppLauncher()\">\r\n <section class=\"dialog launcher\" role=\"dialog\" aria-modal=\"true\" aria-label=\"All applications\"\r\n (click)=\"$event.stopPropagation()\">\r\n <div class=\"dialog__search\"><lib-icon name=\"search\" [size]=\"18\" /><input #appSearch type=\"search\"\r\n placeholder=\"Search apps\" [value]=\"appQuery()\" (input)=\"appQuery.set(appSearch.value)\"><button type=\"button\"\r\n aria-label=\"Close app launcher\" (click)=\"closeAppLauncher()\"><lib-icon name=\"x\" [size]=\"18\" /></button></div>\r\n <div class=\"launcher__body\">@if(filteredApplications().length === 0){<p class=\"empty-state\">No apps found for\r\n \u201C{{appQuery()}}\u201D</p>}@else{<div class=\"launcher__grid\">@for(app of filteredApplications();track app.id){<button\r\n type=\"button\" [disabled]=\"app.available === false\" (click)=\"selectApplication(app)\"><span class=\"app-icon\"\r\n [style.background]=\"app.iconBackground || null\"\r\n [style.color]=\"app.iconColor || null\">@if(app.icon){<lib-icon [name]=\"app.icon\" [size]=\"20\" />}@else { {{\r\n app.initials || initials(app.name) }} }</span><span>{{app.name}}</span></button>}</div>}</div>\r\n </section>\r\n</div>\r\n}\r\n\r\n@if (commandOpen()) {\r\n<div class=\"dialog-backdrop command-backdrop\" (click)=\"closeCommandPalette()\">\r\n <section class=\"dialog command-palette\" role=\"dialog\" aria-modal=\"true\" aria-label=\"Command palette\"\r\n (click)=\"$event.stopPropagation()\">\r\n <div class=\"dialog__search\"><lib-icon name=\"search\" [size]=\"18\" /><input #commandSearch type=\"search\"\r\n placeholder=\"Type a command or search...\" [value]=\"commandQuery()\"\r\n (input)=\"updateCommandQuery(commandSearch.value)\"><kbd>esc</kbd></div>\r\n <div class=\"command-palette__body\">@if(filteredCommands().length === 0){<p class=\"empty-state\">No results for\r\n \u201C{{commandQuery()}}\u201D</p>}@else{@for(group of commandGroups();track group.category){<div class=\"command-group\">\r\n <h2>{{group.category}}</h2>@for(command of group.commands;track command.id){<button type=\"button\"\r\n [disabled]=\"command.disabled\" [class.active]=\"commandIndex(command) === activeCommandIndex()\"\r\n (mouseenter)=\"activeCommandIndex.set(commandIndex(command))\" (click)=\"runCommand(command)\"><lib-icon\r\n [name]=\"command.icon || 'dashboard'\" [size]=\"16\" /><span>{{command.label}}</span></button>}\r\n </div>}}</div>\r\n <footer class=\"command-palette__footer\"><span>\u21B5 select</span><span>\u2191\u2193 navigate</span><span>esc close</span></footer>\r\n </section>\r\n</div>\r\n}\r\n", styles: [":host{display:block;position:relative;width:var(--rail-width, 16rem);padding:0;box-sizing:border-box;font-family:Inter,system-ui,sans-serif;transition:width .2s;--rail-width: 16rem;--rail-collapsed-width: 4.5rem;--rail-radius: 0;--rail-bg: #fff;--rail-border: #e2e8f0;--rail-text: #475569;--rail-text-strong: #0f172a;--rail-muted: #94a3b8;--rail-hover: #f1f5f9;--rail-active-bg: #eef2ff;--rail-active: #4f46e5;--rail-accent: #6366f1;--rail-focus: #818cf8;--rail-danger: #dc2626;--rail-popover: #fff;--rail-popover-shadow: 0 14px 34px #0f172a24;--rail-overlay: #0f172a80;--rail-dialog-bg: #fff;--rail-dialog-border: #e2e8f0;--rail-input: #0f172a;--rail-subtle: #f8fafc }:host-context(.dark){--rail-bg: #020617;--rail-border: #1e293b;--rail-text: #94a3b8;--rail-text-strong: #e2e8f0;--rail-muted: #64748b;--rail-hover: #0f172a;--rail-active-bg: #172033;--rail-active: #a5b4fc;--rail-danger: #f87171;--rail-popover: #0f172a;--rail-popover-shadow: 0 16px 40px #0007;--rail-overlay: #000a;--rail-dialog-bg: #0f172a;--rail-dialog-border: #263449;--rail-input: #e2e8f0;--rail-subtle: #111c2f }.sidebar{position:sticky;z-index:20;top:0;display:flex;width:var(--rail-width);height:100dvh;flex-direction:column;overflow:visible;border:1px solid var(--rail-border);border-radius:var(--rail-radius);background:var(--rail-bg);color:var(--rail-text);transition:width .2s,transform .25s,background-color .2s}.sidebar.collapsed{width:var(--rail-collapsed-width)}.collapse-toggle{position:absolute;z-index:35;top:auto;right:0;bottom:-.75rem;display:grid;width:1.5rem;height:1.5rem;place-items:center;border:1px solid var(--rail-border);border-radius:50%;background:var(--rail-dialog-bg);box-shadow:0 2px 8px #0f172a29;color:var(--rail-text);cursor:pointer;transform:translate(50%)}button:focus-visible,a:focus-visible,input:focus-visible{outline:2px solid var(--rail-focus);outline-offset:2px}.sidebar__workspace-wrap{position:relative;padding:1rem .75rem .25rem}.workspace,.user{display:flex;width:100%;align-items:center;gap:.55rem;border:0;border-radius:.5rem;background:transparent;color:inherit;cursor:pointer;text-align:left}.workspace{min-height:2.75rem;padding:.35rem .5rem}.workspace:hover,.user:hover{background:var(--rail-hover)}.workspace__avatar{display:grid;width:2rem;height:2rem;flex:0 0 auto;place-items:center;border-radius:.5rem;background:var(--rail-accent);color:#fff;font-size:.8rem;font-weight:700}.workspace__copy,.user__copy{display:grid;min-width:0;flex:1;gap:.08rem}.workspace strong,.user strong{overflow:hidden;color:var(--rail-text-strong);font-size:.8125rem;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.workspace small,.user small{overflow:hidden;color:var(--rail-muted);font-size:.7rem;text-overflow:ellipsis;white-space:nowrap}.workspace__chevron,.user__more{margin-left:auto;color:var(--rail-muted)}.sidebar__tools{position:relative;display:flex;align-items:center;gap:.15rem;margin-bottom:.75rem;border-bottom:1px solid var(--rail-border);padding:.65rem 1rem .9rem}.utility-button{position:relative;display:grid;width:2rem;height:2rem;place-items:center;border:0;border-radius:.4rem;background:transparent;color:var(--rail-text);cursor:pointer}.utility-button:hover{background:var(--rail-hover);color:var(--rail-text-strong)}.notification-dot{position:absolute;top:.4rem;right:.42rem;width:.38rem;height:.38rem;border:1px solid var(--rail-bg);border-radius:50%;background:var(--rail-accent)}.sidebar__middle{min-height:0;flex:1;overflow-x:visible;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--rail-border) transparent}.sidebar__user-wrap{position:relative;margin-top:.5rem;padding:.75rem;border-top:1px solid var(--rail-border)}.user{min-height:2.5rem;padding:.25rem}.user__avatar{display:grid;width:2rem;height:2rem;flex:0 0 auto;place-items:center;border-radius:50%;background:#475569;color:#fff;font-size:.7rem;font-weight:700}.popover{position:absolute;z-index:40;left:calc(100% + .65rem);display:grid;width:12rem;border:1px solid var(--rail-border);border-radius:.55rem;padding:.3rem;background:var(--rail-popover);box-shadow:var(--rail-popover-shadow)}.workspace-menu{top:1rem}.profile-menu{bottom:.75rem}.popover button{display:flex;min-height:2.15rem;align-items:center;gap:.55rem;border:0;border-radius:.35rem;padding:.4rem .5rem;background:transparent;color:var(--rail-text-strong);cursor:pointer;font:inherit;font-size:.8rem;text-align:left}.popover button:hover{background:var(--rail-hover)}.mini-avatar{display:grid;width:1.55rem;height:1.55rem;place-items:center;border-radius:.35rem;background:var(--rail-hover);font-size:.68rem}.check{margin-left:auto;color:var(--rail-active)}.popover__divider{height:1px;margin:.25rem;background:var(--rail-border)}.popover .muted{color:var(--rail-text)}.popover .danger{color:var(--rail-danger)}.sidebar.collapsed .workspace__copy,.sidebar.collapsed .workspace__chevron,.sidebar.collapsed .user__copy,.sidebar.collapsed .user__more{display:none}.sidebar.collapsed .workspace,.sidebar.collapsed .user{justify-content:center}.sidebar.collapsed .sidebar__tools{flex-direction:column;padding-inline:.75rem}.sidebar.collapsed .sidebar__workspace-wrap{padding-inline:.75rem}.sidebar-toggle{display:none}.sidebar-overlay{position:fixed;z-index:40;inset:0;background:var(--rail-overlay)}@media(max-width:767px){:host{width:0;padding:0}.sidebar{position:fixed;z-index:45;inset:0 auto 0 0;width:min(var(--rail-width),88vw);height:100dvh;border-width:0 1px 0 0;border-radius:0;transform:translate(-105%)}.sidebar.open{transform:none}.sidebar.collapsed{width:min(var(--rail-width),88vw)}.collapse-toggle{display:none}.sidebar-toggle{position:fixed;z-index:50;top:.75rem;left:.75rem;display:grid;width:2.5rem;height:2.5rem;place-items:center;border:1px solid var(--rail-border);border-radius:.5rem;background:var(--rail-dialog-bg);box-shadow:0 2px 8px #0f172a24;color:var(--rail-text-strong);cursor:pointer}.sidebar.collapsed .workspace__copy,.sidebar.collapsed .workspace__chevron,.sidebar.collapsed .user__copy,.sidebar.collapsed .user__more{display:grid}.sidebar.collapsed .sidebar__tools{flex-direction:row}.popover{left:.75rem;right:.75rem;width:auto}.workspace-menu{top:4.2rem}.profile-menu{bottom:4.2rem}}\n", ".dialog-backdrop{position:fixed;z-index:100;inset:0;display:flex;justify-content:center;align-items:flex-start;padding:8rem 1rem 1rem;background:var(--rail-overlay)}.dialog{width:min(28rem,100%);max-height:min(30rem,calc(100dvh - 10rem));overflow:hidden;border:1px solid var(--rail-dialog-border);border-radius:.75rem;background:var(--rail-dialog-bg);box-shadow:0 24px 60px #0005;color:var(--rail-text-strong)}.dialog__search{display:flex;height:3rem;align-items:center;gap:.55rem;border-bottom:1px solid var(--rail-dialog-border);padding:0 .9rem;color:var(--rail-muted)}.dialog__search input{min-width:0;flex:1;border:0;outline:0;background:transparent;color:var(--rail-input);font:inherit;font-size:.82rem}.dialog__search button{display:grid;place-items:center;border:0;background:transparent;color:inherit;cursor:pointer}.dialog__search kbd{border:1px solid var(--rail-dialog-border);border-radius:.25rem;padding:.12rem .35rem;font-size:.62rem}.launcher__body,.command-palette__body{max-height:22rem;overflow-y:auto;padding:.7rem}.launcher__grid{display:grid;grid-template-columns:repeat(3,1fr);gap:.15rem}.launcher__grid button{display:flex;min-height:5.25rem;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;border:0;border-radius:.55rem;background:transparent;color:var(--rail-text);cursor:pointer;font:inherit;font-size:.72rem}.launcher__grid button:hover{background:var(--rail-hover)}.launcher__grid button:disabled,.command-group button:disabled{cursor:not-allowed;opacity:.4}.app-icon{display:grid;width:2.5rem;height:2.5rem;place-items:center;border-radius:.5rem;background:#dbeafe;color:#2563eb;font-size:.72rem;font-weight:700}.launcher__grid button:nth-child(3n+2) .app-icon{background:#ffe4e6;color:#e11d48}.launcher__grid button:nth-child(3n) .app-icon{background:#fef3c7;color:#d97706}.empty-state{margin:0;padding:2rem;text-align:center;color:var(--rail-muted);font-size:.8rem}.command-palette{width:min(32rem,100%)}.command-group{display:grid;gap:.1rem;margin-bottom:.55rem}.command-group:last-child{margin-bottom:0}.command-group h2{margin:.15rem .5rem;color:var(--rail-muted);font-size:.65rem;font-weight:600;letter-spacing:.05em;text-transform:uppercase}.command-group button{display:flex;width:100%;height:2.1rem;align-items:center;gap:.6rem;border:0;border-radius:.35rem;padding:0 .5rem;background:transparent;color:var(--rail-text);cursor:pointer;font:inherit;font-size:.8rem;text-align:left}.command-group button:hover,.command-group button.active{background:var(--rail-active-bg);color:var(--rail-active)}.command-group button span{flex:1}.command-palette__footer{display:flex;gap:1rem;border-top:1px solid var(--rail-dialog-border);padding:.5rem .9rem;color:var(--rail-muted);font-size:.65rem}@media(max-width:767px){.dialog-backdrop{padding-top:4rem}}@media(max-width:420px){.launcher__grid{grid-template-columns:repeat(2,1fr)}}\n"] }]
808
+ }], propDecorators: { menu: [{ type: i0.ViewChild, args: [i0.forwardRef(() => SidebarMenuComponent), { isSignal: true }] }], menuItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuItems", required: false }] }], menuError: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuError", required: false }] }], showMobileToggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "showMobileToggle", required: false }] }], workspace: [{ type: i0.Input, args: [{ isSignal: true, alias: "workspace", required: false }] }], workspaces: [{ type: i0.Input, args: [{ isSignal: true, alias: "workspaces", required: false }] }], user: [{ type: i0.Input, args: [{ isSignal: true, alias: "user", required: false }] }], applications: [{ type: i0.Input, args: [{ isSignal: true, alias: "applications", required: false }] }], quickActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "quickActions", required: false }] }], userActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "userActions", required: false }] }], collapsible: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsible", required: false }] }], showSearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSearch", required: false }] }], showAppLauncher: [{ type: i0.Input, args: [{ isSignal: true, alias: "showAppLauncher", required: false }] }], showNotifications: [{ type: i0.Input, args: [{ isSignal: true, alias: "showNotifications", required: false }] }], showThemeToggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "showThemeToggle", required: false }] }], hasUnreadNotifications: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasUnreadNotifications", required: false }] }], menuItemSelected: [{ type: i0.Output, args: ["menuItemSelected"] }], workspaceSelected: [{ type: i0.Output, args: ["workspaceSelected"] }], applicationSelected: [{ type: i0.Output, args: ["applicationSelected"] }], userActionSelected: [{ type: i0.Output, args: ["userActionSelected"] }], quickActionSelected: [{ type: i0.Output, args: ["quickActionSelected"] }], notificationActivated: [{ type: i0.Output, args: ["notificationActivated"] }], collapsedChange: [{ type: i0.Output, args: ["collapsedChange"] }], onDocumentKeydown: [{
809
+ type: HostListener,
810
+ args: ['document:keydown', ['$event']]
811
+ }], onDocumentPointer: [{
812
+ type: HostListener,
813
+ args: ['document:mousedown', ['$event']]
814
+ }] } });
587
815
 
588
- /*
589
- * Public API Surface of ui
590
- */
816
+ /** Public API for @excellore/ui. Internal composition components stay private. */
591
817
 
592
818
  /**
593
819
  * Generated bundle index. Do not edit.