@excellore/ui 0.0.3 → 0.0.5

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.
@@ -2,8 +2,7 @@ import * as i0 from '@angular/core';
2
2
  import { inject, PLATFORM_ID, signal, effect, Injectable, input, output, HostListener, Component, computed } from '@angular/core';
3
3
  import { isPlatformBrowser, CommonModule } from '@angular/common';
4
4
  import { Router, RouterLink, RouterLinkActive } from '@angular/router';
5
- import * as i1 from 'lucide-angular';
6
- import { Grid, ChevronDown, LucideAngularModule, Check, Building, CheckCheck, Trash2, BellOff, Circle, Bell, User, Settings, CreditCard, LogOut, Building2, Monitor, Moon, Sun, X, Menu, Search } from 'lucide-angular';
5
+ import { LucideGrid, LucideBuilding, LucideCheck, LucideChevronDown, LucideBellOff, LucideCheckCheck, LucideTrash2, LucideBell, LucideCreditCard, LucideLogOut, LucideSettings, LucideUser, LucideBuilding2, LucideMenu, LucideMonitor, LucideMoon, LucideSearch, LucideSun, LucideX } from '@lucide/angular';
7
6
 
8
7
  const THEME_STORAGE_KEY = 'excellore-theme';
9
8
  class ThemeService {
@@ -123,8 +122,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
123
122
  class AppSwitcherComponent {
124
123
  applications = input([], ...(ngDevMode ? [{ debugName: "applications" }] : /* istanbul ignore next */ []));
125
124
  applicationSelected = output();
126
- gridIcon = Grid;
127
- chevronDownIcon = ChevronDown;
128
125
  isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
129
126
  toggleSwitcher() {
130
127
  this.isOpen.update((v) => !v);
@@ -153,11 +150,11 @@ class AppSwitcherComponent {
153
150
  }
154
151
  }
155
152
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AppSwitcherComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
156
- 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 relative inline-block text-left\">\r\n <button\n type=\"button\"\n class=\"app-switcher-trigger\"\n (click)=\"toggleSwitcher()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"Toggle application switcher\"\r\n >\r\n <lucide-icon [name]=\"gridIcon\" class=\"h-4.5 w-4.5\" />\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"app-switcher-dropdown fixed left-2 right-2 top-14 mt-2 max-h-[calc(100dvh-4.5rem)] origin-top rounded-xl border border-[var(--border)] bg-[var(--background)] p-2.5 shadow-lg focus:outline-none z-50 sm:absolute sm:left-auto sm:right-0 sm:top-auto sm:w-[360px] sm:max-w-[calc(100vw-1rem)] sm:origin-top-right\">\n <div class=\"mb-1.5 flex items-center justify-between border-b border-[var(--border)] px-1 pb-2.5\">\n <div>\n <h3 class=\"text-[10px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Excellore Apps</h3>\n <p class=\"mt-0.5 text-[11px] text-[var(--secondary)]\">Switch between available workspaces</p>\n </div>\n </div>\n \n @if (applications().length === 0) {\n <div class=\"py-6 text-center text-xs text-[var(--secondary)]\">\n No other apps available\n </div>\n } @else {\n <div class=\"max-h-[calc(100dvh-10rem)] overflow-y-auto py-1 sm:max-h-[420px]\">\n @for (app of applications(); track app.id) {\n @if (app.available !== false) {\n <a\n [href]=\"app.url\"\n (click)=\"onSelect(app)\"\n class=\"app-switcher-item app-switcher-link group flex items-center gap-3 rounded-lg border border-transparent px-2.5 py-2.5 no-underline transition focus:outline-none focus:ring-2 focus:ring-[var(--primary)]\"\n >\n <div class=\"app-switcher-app-mark\">\n {{ app.initials || getInitials(app.name) }}\n </div>\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold text-[var(--foreground)]\" [title]=\"app.name\">{{ app.name }}</div>\n <div class=\"mt-0.5 truncate text-[11px] text-[var(--secondary)]\">{{ app.url }}</div>\n </div>\n <span class=\"app-switcher-open-pill shrink-0 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide opacity-0 transition group-hover:opacity-100\">\n Open\n </span>\n </a>\n } @else {\n <div class=\"app-switcher-item flex items-center gap-3 rounded-lg px-2.5 py-2.5 opacity-55 select-none\">\n <div class=\"app-switcher-app-mark unavailable\">\n {{ app.initials || getInitials(app.name) }}\n </div>\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold text-[var(--foreground)]\">{{ app.name }}</div>\n <div class=\"mt-0.5 truncate text-[11px] text-[var(--secondary)]\">{{ app.url }}</div>\n </div>\n <span class=\"shrink-0 rounded-full border border-[var(--border)] px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-[var(--secondary)]\">\n Soon\n </span>\n </div>\n }\n }\n </div>\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:inline-block}.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-dropdown{box-shadow:0 12px 30px #0f172a24;animation:switcherEnter .15s cubic-bezier(.16,1,.3,1)}.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-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-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{color:var(--primary);background:color-mix(in srgb,var(--primary) 9%,transparent)}.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)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: LucideAngularModule }, { kind: "component", type: i1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }] });
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 relative inline-block text-left\">\r\n <button\n type=\"button\"\n class=\"app-switcher-trigger\"\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=\"h-4.5 w-4.5\"></svg>\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"app-switcher-dropdown fixed left-2 right-2 top-14 mt-2 max-h-[calc(100dvh-4.5rem)] origin-top rounded-xl border border-[var(--border)] bg-[var(--background)] p-2.5 shadow-lg focus:outline-none z-50 sm:absolute sm:left-auto sm:right-0 sm:top-auto sm:w-[360px] sm:max-w-[calc(100vw-1rem)] sm:origin-top-right\">\n <div class=\"mb-1.5 flex items-center justify-between border-b border-[var(--border)] px-1 pb-2.5\">\n <div>\n <h3 class=\"text-[10px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Excellore Apps</h3>\n <p class=\"mt-0.5 text-[11px] text-[var(--secondary)]\">Switch between available workspaces</p>\n </div>\n </div>\n \n @if (applications().length === 0) {\n <div class=\"py-6 text-center text-xs text-[var(--secondary)]\">\n No other apps available\n </div>\n } @else {\n <div class=\"max-h-[calc(100dvh-10rem)] overflow-y-auto py-1 sm:max-h-[420px]\">\n @for (app of applications(); track app.id) {\n @if (app.available !== false) {\n <a\n [href]=\"app.url\"\n (click)=\"onSelect(app)\"\n class=\"app-switcher-item app-switcher-link group flex items-center gap-3 rounded-lg border border-transparent px-2.5 py-2.5 no-underline transition focus:outline-none focus:ring-2 focus:ring-[var(--primary)]\"\n >\n <div class=\"app-switcher-app-mark\">\n {{ app.initials || getInitials(app.name) }}\n </div>\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold text-[var(--foreground)]\" [title]=\"app.name\">{{ app.name }}</div>\n <div class=\"mt-0.5 truncate text-[11px] text-[var(--secondary)]\">{{ app.url }}</div>\n </div>\n <span class=\"app-switcher-open-pill shrink-0 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide opacity-0 transition group-hover:opacity-100\">\n Open\n </span>\n </a>\n } @else {\n <div class=\"app-switcher-item flex items-center gap-3 rounded-lg px-2.5 py-2.5 opacity-55 select-none\">\n <div class=\"app-switcher-app-mark unavailable\">\n {{ app.initials || getInitials(app.name) }}\n </div>\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold text-[var(--foreground)]\">{{ app.name }}</div>\n <div class=\"mt-0.5 truncate text-[11px] text-[var(--secondary)]\">{{ app.url }}</div>\n </div>\n <span class=\"shrink-0 rounded-full border border-[var(--border)] px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-[var(--secondary)]\">\n Soon\n </span>\n </div>\n }\n }\n </div>\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:inline-block}.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-dropdown{box-shadow:0 12px 30px #0f172a24;animation:switcherEnter .15s cubic-bezier(.16,1,.3,1)}.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-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-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{color:var(--primary);background:color-mix(in srgb,var(--primary) 9%,transparent)}.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)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: LucideGrid, selector: "svg[lucideGrid3x3], svg[lucideGrid], svg[lucideGrid3X3]" }] });
157
154
  }
158
155
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AppSwitcherComponent, decorators: [{
159
156
  type: Component,
160
- args: [{ selector: 'lib-app-switcher', standalone: true, imports: [CommonModule, LucideAngularModule], template: "<div class=\"app-switcher-wrapper relative inline-block text-left\">\r\n <button\n type=\"button\"\n class=\"app-switcher-trigger\"\n (click)=\"toggleSwitcher()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"Toggle application switcher\"\r\n >\r\n <lucide-icon [name]=\"gridIcon\" class=\"h-4.5 w-4.5\" />\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"app-switcher-dropdown fixed left-2 right-2 top-14 mt-2 max-h-[calc(100dvh-4.5rem)] origin-top rounded-xl border border-[var(--border)] bg-[var(--background)] p-2.5 shadow-lg focus:outline-none z-50 sm:absolute sm:left-auto sm:right-0 sm:top-auto sm:w-[360px] sm:max-w-[calc(100vw-1rem)] sm:origin-top-right\">\n <div class=\"mb-1.5 flex items-center justify-between border-b border-[var(--border)] px-1 pb-2.5\">\n <div>\n <h3 class=\"text-[10px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Excellore Apps</h3>\n <p class=\"mt-0.5 text-[11px] text-[var(--secondary)]\">Switch between available workspaces</p>\n </div>\n </div>\n \n @if (applications().length === 0) {\n <div class=\"py-6 text-center text-xs text-[var(--secondary)]\">\n No other apps available\n </div>\n } @else {\n <div class=\"max-h-[calc(100dvh-10rem)] overflow-y-auto py-1 sm:max-h-[420px]\">\n @for (app of applications(); track app.id) {\n @if (app.available !== false) {\n <a\n [href]=\"app.url\"\n (click)=\"onSelect(app)\"\n class=\"app-switcher-item app-switcher-link group flex items-center gap-3 rounded-lg border border-transparent px-2.5 py-2.5 no-underline transition focus:outline-none focus:ring-2 focus:ring-[var(--primary)]\"\n >\n <div class=\"app-switcher-app-mark\">\n {{ app.initials || getInitials(app.name) }}\n </div>\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold text-[var(--foreground)]\" [title]=\"app.name\">{{ app.name }}</div>\n <div class=\"mt-0.5 truncate text-[11px] text-[var(--secondary)]\">{{ app.url }}</div>\n </div>\n <span class=\"app-switcher-open-pill shrink-0 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide opacity-0 transition group-hover:opacity-100\">\n Open\n </span>\n </a>\n } @else {\n <div class=\"app-switcher-item flex items-center gap-3 rounded-lg px-2.5 py-2.5 opacity-55 select-none\">\n <div class=\"app-switcher-app-mark unavailable\">\n {{ app.initials || getInitials(app.name) }}\n </div>\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold text-[var(--foreground)]\">{{ app.name }}</div>\n <div class=\"mt-0.5 truncate text-[11px] text-[var(--secondary)]\">{{ app.url }}</div>\n </div>\n <span class=\"shrink-0 rounded-full border border-[var(--border)] px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-[var(--secondary)]\">\n Soon\n </span>\n </div>\n }\n }\n </div>\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:inline-block}.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-dropdown{box-shadow:0 12px 30px #0f172a24;animation:switcherEnter .15s cubic-bezier(.16,1,.3,1)}.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-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-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{color:var(--primary);background:color-mix(in srgb,var(--primary) 9%,transparent)}.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)}}\n"] }]
157
+ args: [{ selector: 'lib-app-switcher', standalone: true, imports: [CommonModule, LucideGrid], template: "<div class=\"app-switcher-wrapper relative inline-block text-left\">\r\n <button\n type=\"button\"\n class=\"app-switcher-trigger\"\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=\"h-4.5 w-4.5\"></svg>\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"app-switcher-dropdown fixed left-2 right-2 top-14 mt-2 max-h-[calc(100dvh-4.5rem)] origin-top rounded-xl border border-[var(--border)] bg-[var(--background)] p-2.5 shadow-lg focus:outline-none z-50 sm:absolute sm:left-auto sm:right-0 sm:top-auto sm:w-[360px] sm:max-w-[calc(100vw-1rem)] sm:origin-top-right\">\n <div class=\"mb-1.5 flex items-center justify-between border-b border-[var(--border)] px-1 pb-2.5\">\n <div>\n <h3 class=\"text-[10px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Excellore Apps</h3>\n <p class=\"mt-0.5 text-[11px] text-[var(--secondary)]\">Switch between available workspaces</p>\n </div>\n </div>\n \n @if (applications().length === 0) {\n <div class=\"py-6 text-center text-xs text-[var(--secondary)]\">\n No other apps available\n </div>\n } @else {\n <div class=\"max-h-[calc(100dvh-10rem)] overflow-y-auto py-1 sm:max-h-[420px]\">\n @for (app of applications(); track app.id) {\n @if (app.available !== false) {\n <a\n [href]=\"app.url\"\n (click)=\"onSelect(app)\"\n class=\"app-switcher-item app-switcher-link group flex items-center gap-3 rounded-lg border border-transparent px-2.5 py-2.5 no-underline transition focus:outline-none focus:ring-2 focus:ring-[var(--primary)]\"\n >\n <div class=\"app-switcher-app-mark\">\n {{ app.initials || getInitials(app.name) }}\n </div>\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold text-[var(--foreground)]\" [title]=\"app.name\">{{ app.name }}</div>\n <div class=\"mt-0.5 truncate text-[11px] text-[var(--secondary)]\">{{ app.url }}</div>\n </div>\n <span class=\"app-switcher-open-pill shrink-0 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide opacity-0 transition group-hover:opacity-100\">\n Open\n </span>\n </a>\n } @else {\n <div class=\"app-switcher-item flex items-center gap-3 rounded-lg px-2.5 py-2.5 opacity-55 select-none\">\n <div class=\"app-switcher-app-mark unavailable\">\n {{ app.initials || getInitials(app.name) }}\n </div>\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold text-[var(--foreground)]\">{{ app.name }}</div>\n <div class=\"mt-0.5 truncate text-[11px] text-[var(--secondary)]\">{{ app.url }}</div>\n </div>\n <span class=\"shrink-0 rounded-full border border-[var(--border)] px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-[var(--secondary)]\">\n Soon\n </span>\n </div>\n }\n }\n </div>\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:inline-block}.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-dropdown{box-shadow:0 12px 30px #0f172a24;animation:switcherEnter .15s cubic-bezier(.16,1,.3,1)}.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-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-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{color:var(--primary);background:color-mix(in srgb,var(--primary) 9%,transparent)}.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)}}\n"] }]
161
158
  }], propDecorators: { applications: [{ type: i0.Input, args: [{ isSignal: true, alias: "applications", required: false }] }], applicationSelected: [{ type: i0.Output, args: ["applicationSelected"] }], onOutsideClick: [{
162
159
  type: HostListener,
163
160
  args: ['document:click', ['$event']]
@@ -167,9 +164,6 @@ class OrganizationSwitcherComponent {
167
164
  organizations = input([], ...(ngDevMode ? [{ debugName: "organizations" }] : /* istanbul ignore next */ []));
168
165
  selectedOrganization = input(null, ...(ngDevMode ? [{ debugName: "selectedOrganization" }] : /* istanbul ignore next */ []));
169
166
  organizationChanged = output();
170
- chevronDownIcon = ChevronDown;
171
- checkIcon = Check;
172
- buildingIcon = Building;
173
167
  isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
174
168
  toggleSwitcher() {
175
169
  this.isOpen.update((v) => !v);
@@ -188,11 +182,11 @@ class OrganizationSwitcherComponent {
188
182
  }
189
183
  }
190
184
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: OrganizationSwitcherComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
191
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: OrganizationSwitcherComponent, isStandalone: true, selector: "lib-organization-switcher", inputs: { organizations: { classPropertyName: "organizations", publicName: "organizations", isSignal: true, isRequired: false, transformFunction: null }, selectedOrganization: { classPropertyName: "selectedOrganization", publicName: "selectedOrganization", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { organizationChanged: "organizationChanged" }, host: { listeners: { "document:click": "onOutsideClick($event)" } }, ngImport: i0, template: "<div class=\"org-switcher-wrapper relative inline-block text-left\">\r\n <button\r\n type=\"button\"\r\n class=\"flex items-center gap-1.5 rounded-md border border-[var(--border)] bg-[var(--background)] px-2.5 py-1.5 text-xs font-medium text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] cursor-pointer\"\r\n (click)=\"toggleSwitcher()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"Switch organization\"\r\n >\r\n <lucide-icon [name]=\"buildingIcon\" class=\"h-3.5 w-3.5 text-[var(--secondary)]\" />\r\n <span class=\"truncate max-w-[120px]\">{{ selectedOrganization()?.name || 'Select Tenant' }}</span>\r\n <lucide-icon [name]=\"chevronDownIcon\" class=\"h-3 w-3 text-[var(--secondary)] transition-transform duration-200\" [class.rotate-180]=\"isOpen()\" />\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"org-switcher-dropdown absolute left-0 mt-2 w-52 origin-top-left rounded-lg border border-[var(--border)] bg-[var(--background)] p-1 shadow-lg focus:outline-none z-50\">\r\n <div class=\"px-2 py-1.5 border-b border-[var(--border)] mb-1\">\r\n <span class=\"text-[9px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Switch Organization</span>\r\n </div>\r\n\r\n <div class=\"py-0.5 space-y-0.5 max-h-48 overflow-y-auto\">\r\n @for (org of organizations(); track org.id) {\r\n <button\r\n type=\"button\"\r\n class=\"flex w-full items-center justify-between rounded-md px-2 py-1.5 text-xs text-[var(--foreground)] transition hover:bg-[rgba(var(--foreground),0.05)] text-left cursor-pointer\"\r\n (click)=\"onSelect(org)\"\r\n >\r\n <span class=\"truncate pr-2\" [class.font-semibold]=\"org.id === selectedOrganization()?.id\">{{ org.name }}</span>\r\n @if (org.id === selectedOrganization()?.id) {\r\n <lucide-icon [name]=\"checkIcon\" class=\"h-3.5 w-3.5 text-[var(--primary)] flex-shrink-0\" />\r\n }\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:inline-block}.org-switcher-dropdown{box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;animation:orgEnter .15s cubic-bezier(.16,1,.3,1)}@keyframes orgEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: LucideAngularModule }, { kind: "component", type: i1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }] });
185
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: OrganizationSwitcherComponent, isStandalone: true, selector: "lib-organization-switcher", inputs: { organizations: { classPropertyName: "organizations", publicName: "organizations", isSignal: true, isRequired: false, transformFunction: null }, selectedOrganization: { classPropertyName: "selectedOrganization", publicName: "selectedOrganization", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { organizationChanged: "organizationChanged" }, host: { listeners: { "document:click": "onOutsideClick($event)" } }, ngImport: i0, template: "<div class=\"org-switcher-wrapper relative inline-block text-left\">\r\n <button\r\n type=\"button\"\r\n class=\"flex items-center gap-1.5 rounded-md border border-[var(--border)] bg-[var(--background)] px-2.5 py-1.5 text-xs font-medium text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] cursor-pointer\"\r\n (click)=\"toggleSwitcher()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"Switch organization\"\r\n >\r\n <svg lucideBuilding class=\"h-3.5 w-3.5 text-[var(--secondary)]\"></svg>\n <span class=\"truncate max-w-[120px]\">{{ selectedOrganization()?.name || 'Select Tenant' }}</span>\n <svg lucideChevronDown class=\"h-3 w-3 text-[var(--secondary)] transition-transform duration-200\" [class.rotate-180]=\"isOpen()\"></svg>\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"org-switcher-dropdown absolute left-0 mt-2 w-52 origin-top-left rounded-lg border border-[var(--border)] bg-[var(--background)] p-1 shadow-lg focus:outline-none z-50\">\r\n <div class=\"px-2 py-1.5 border-b border-[var(--border)] mb-1\">\r\n <span class=\"text-[9px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Switch Organization</span>\r\n </div>\r\n\r\n <div class=\"py-0.5 space-y-0.5 max-h-48 overflow-y-auto\">\r\n @for (org of organizations(); track org.id) {\r\n <button\r\n type=\"button\"\r\n class=\"flex w-full items-center justify-between rounded-md px-2 py-1.5 text-xs text-[var(--foreground)] transition hover:bg-[rgba(var(--foreground),0.05)] text-left cursor-pointer\"\r\n (click)=\"onSelect(org)\"\r\n >\r\n <span class=\"truncate pr-2\" [class.font-semibold]=\"org.id === selectedOrganization()?.id\">{{ org.name }}</span>\r\n @if (org.id === selectedOrganization()?.id) {\r\n <svg lucideCheck class=\"h-3.5 w-3.5 text-[var(--primary)] flex-shrink-0\"></svg>\n }\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:inline-block}.org-switcher-dropdown{box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;animation:orgEnter .15s cubic-bezier(.16,1,.3,1)}@keyframes orgEnter{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: LucideBuilding, selector: "svg[lucideBuilding]" }, { kind: "component", type: LucideCheck, selector: "svg[lucideCheck]" }, { kind: "component", type: LucideChevronDown, selector: "svg[lucideChevronDown]" }] });
192
186
  }
193
187
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: OrganizationSwitcherComponent, decorators: [{
194
188
  type: Component,
195
- args: [{ selector: 'lib-organization-switcher', standalone: true, imports: [CommonModule, LucideAngularModule], template: "<div class=\"org-switcher-wrapper relative inline-block text-left\">\r\n <button\r\n type=\"button\"\r\n class=\"flex items-center gap-1.5 rounded-md border border-[var(--border)] bg-[var(--background)] px-2.5 py-1.5 text-xs font-medium text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] cursor-pointer\"\r\n (click)=\"toggleSwitcher()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"Switch organization\"\r\n >\r\n <lucide-icon [name]=\"buildingIcon\" class=\"h-3.5 w-3.5 text-[var(--secondary)]\" />\r\n <span class=\"truncate max-w-[120px]\">{{ selectedOrganization()?.name || 'Select Tenant' }}</span>\r\n <lucide-icon [name]=\"chevronDownIcon\" class=\"h-3 w-3 text-[var(--secondary)] transition-transform duration-200\" [class.rotate-180]=\"isOpen()\" />\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"org-switcher-dropdown absolute left-0 mt-2 w-52 origin-top-left rounded-lg border border-[var(--border)] bg-[var(--background)] p-1 shadow-lg focus:outline-none z-50\">\r\n <div class=\"px-2 py-1.5 border-b border-[var(--border)] mb-1\">\r\n <span class=\"text-[9px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Switch Organization</span>\r\n </div>\r\n\r\n <div class=\"py-0.5 space-y-0.5 max-h-48 overflow-y-auto\">\r\n @for (org of organizations(); track org.id) {\r\n <button\r\n type=\"button\"\r\n class=\"flex w-full items-center justify-between rounded-md px-2 py-1.5 text-xs text-[var(--foreground)] transition hover:bg-[rgba(var(--foreground),0.05)] text-left cursor-pointer\"\r\n (click)=\"onSelect(org)\"\r\n >\r\n <span class=\"truncate pr-2\" [class.font-semibold]=\"org.id === selectedOrganization()?.id\">{{ org.name }}</span>\r\n @if (org.id === selectedOrganization()?.id) {\r\n <lucide-icon [name]=\"checkIcon\" class=\"h-3.5 w-3.5 text-[var(--primary)] flex-shrink-0\" />\r\n }\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:inline-block}.org-switcher-dropdown{box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;animation:orgEnter .15s cubic-bezier(.16,1,.3,1)}@keyframes orgEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}\n"] }]
189
+ args: [{ selector: 'lib-organization-switcher', standalone: true, imports: [CommonModule, LucideBuilding, LucideCheck, LucideChevronDown], template: "<div class=\"org-switcher-wrapper relative inline-block text-left\">\r\n <button\r\n type=\"button\"\r\n class=\"flex items-center gap-1.5 rounded-md border border-[var(--border)] bg-[var(--background)] px-2.5 py-1.5 text-xs font-medium text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] cursor-pointer\"\r\n (click)=\"toggleSwitcher()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"Switch organization\"\r\n >\r\n <svg lucideBuilding class=\"h-3.5 w-3.5 text-[var(--secondary)]\"></svg>\n <span class=\"truncate max-w-[120px]\">{{ selectedOrganization()?.name || 'Select Tenant' }}</span>\n <svg lucideChevronDown class=\"h-3 w-3 text-[var(--secondary)] transition-transform duration-200\" [class.rotate-180]=\"isOpen()\"></svg>\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"org-switcher-dropdown absolute left-0 mt-2 w-52 origin-top-left rounded-lg border border-[var(--border)] bg-[var(--background)] p-1 shadow-lg focus:outline-none z-50\">\r\n <div class=\"px-2 py-1.5 border-b border-[var(--border)] mb-1\">\r\n <span class=\"text-[9px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Switch Organization</span>\r\n </div>\r\n\r\n <div class=\"py-0.5 space-y-0.5 max-h-48 overflow-y-auto\">\r\n @for (org of organizations(); track org.id) {\r\n <button\r\n type=\"button\"\r\n class=\"flex w-full items-center justify-between rounded-md px-2 py-1.5 text-xs text-[var(--foreground)] transition hover:bg-[rgba(var(--foreground),0.05)] text-left cursor-pointer\"\r\n (click)=\"onSelect(org)\"\r\n >\r\n <span class=\"truncate pr-2\" [class.font-semibold]=\"org.id === selectedOrganization()?.id\">{{ org.name }}</span>\r\n @if (org.id === selectedOrganization()?.id) {\r\n <svg lucideCheck class=\"h-3.5 w-3.5 text-[var(--primary)] flex-shrink-0\"></svg>\n }\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:inline-block}.org-switcher-dropdown{box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;animation:orgEnter .15s cubic-bezier(.16,1,.3,1)}@keyframes orgEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}\n"] }]
196
190
  }], propDecorators: { organizations: [{ type: i0.Input, args: [{ isSignal: true, alias: "organizations", required: false }] }], selectedOrganization: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedOrganization", required: false }] }], organizationChanged: [{ type: i0.Output, args: ["organizationChanged"] }], onOutsideClick: [{
197
191
  type: HostListener,
198
192
  args: ['document:click', ['$event']]
@@ -203,10 +197,6 @@ class NotificationPanelComponent {
203
197
  notificationClick = output();
204
198
  markRead = output();
205
199
  clearAll = output();
206
- checkCheckIcon = CheckCheck;
207
- trashIcon = Trash2;
208
- bellOffIcon = BellOff;
209
- circleIcon = Circle;
210
200
  onNotificationClick(notification) {
211
201
  this.notificationClick.emit(notification);
212
202
  }
@@ -236,11 +226,11 @@ class NotificationPanelComponent {
236
226
  return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
237
227
  }
238
228
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: NotificationPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
239
- 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 <lucide-icon [name]=\"trashIcon\" class=\"h-3 w-3\" />\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 <lucide-icon [name]=\"bellOffIcon\" class=\"h-4.5 w-4.5\" />\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 <lucide-icon [name]=\"checkCheckIcon\" class=\"h-3.5 w-3.5\" />\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: "ngmodule", type: LucideAngularModule }, { kind: "component", type: i1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }] });
229
+ 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>\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>\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>\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]" }] });
240
230
  }
241
231
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: NotificationPanelComponent, decorators: [{
242
232
  type: Component,
243
- args: [{ selector: 'lib-notification-panel', standalone: true, imports: [CommonModule, LucideAngularModule], 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 <lucide-icon [name]=\"trashIcon\" class=\"h-3 w-3\" />\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 <lucide-icon [name]=\"bellOffIcon\" class=\"h-4.5 w-4.5\" />\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 <lucide-icon [name]=\"checkCheckIcon\" class=\"h-3.5 w-3.5\" />\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"] }]
233
+ 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>\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>\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>\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"] }]
244
234
  }], 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"] }] } });
245
235
 
246
236
  class NotificationBellComponent {
@@ -249,7 +239,6 @@ class NotificationBellComponent {
249
239
  notificationClick = output();
250
240
  markRead = output();
251
241
  clearAll = output();
252
- bellIcon = Bell;
253
242
  isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
254
243
  togglePanel() {
255
244
  this.isOpen.update((v) => !v);
@@ -274,11 +263,11 @@ class NotificationBellComponent {
274
263
  }
275
264
  }
276
265
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: NotificationBellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
277
- 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\n type=\"button\"\n class=\"notification-trigger\"\n (click)=\"togglePanel()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"View notifications\"\r\n >\r\n <lucide-icon [name]=\"bellIcon\" class=\"h-4.5 w-4.5\" />\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\">\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: "ngmodule", type: LucideAngularModule }, { kind: "component", type: i1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }] });
266
+ 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\n type=\"button\"\n class=\"notification-trigger\"\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>\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\">\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]" }] });
278
267
  }
279
268
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: NotificationBellComponent, decorators: [{
280
269
  type: Component,
281
- args: [{ selector: 'lib-notification-bell', standalone: true, imports: [CommonModule, NotificationPanelComponent, LucideAngularModule], template: "<div class=\"notification-bell-wrapper relative inline-block text-left\">\r\n <button\n type=\"button\"\n class=\"notification-trigger\"\n (click)=\"togglePanel()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"View notifications\"\r\n >\r\n <lucide-icon [name]=\"bellIcon\" class=\"h-4.5 w-4.5\" />\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\">\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"] }]
270
+ 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\n type=\"button\"\n class=\"notification-trigger\"\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>\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\">\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"] }]
282
271
  }], 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: [{
283
272
  type: HostListener,
284
273
  args: ['document:click', ['$event']]
@@ -316,12 +305,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
316
305
  class UserMenuComponent {
317
306
  user = input(null, ...(ngDevMode ? [{ debugName: "user" }] : /* istanbul ignore next */ []));
318
307
  menuAction = output();
319
- // Lucide Icons mapping
320
- userIcon = User;
321
- settingsIcon = Settings;
322
- creditCardIcon = CreditCard;
323
- logOutIcon = LogOut;
324
- chevronDownIcon = ChevronDown;
325
308
  // Signalled open state
326
309
  isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
327
310
  toggleMenu() {
@@ -341,60 +324,106 @@ class UserMenuComponent {
341
324
  }
342
325
  }
343
326
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: UserMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
344
- 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 relative inline-block text-left\">\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=\"h-7 w-7 rounded-full overflow-hidden\">\r\n <lib-user-avatar [user]=\"user()\" />\r\n </div>\r\n <lucide-icon [name]=\"chevronDownIcon\" class=\"h-3.5 w-3.5 text-[var(--secondary)] transition-transform duration-200\"\r\n [class.rotate-180]=\"isOpen()\" />\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div\r\n class=\"user-dropdown-panel fixed left-2 right-2 top-14 mt-2 origin-top rounded-xl border border-[var(--border)] bg-[var(--background)] p-1.5 shadow-lg focus:outline-none z-50 sm:absolute sm:left-auto sm:right-0 sm:top-auto sm:w-56 sm:origin-top-right\">\r\n @if (user(); as u) {\r\n <div class=\"px-3 py-2 border-b border-[var(--border)] mb-1\">\r\n <p class=\"text-sm font-semibold truncate text-[var(--foreground)]\" [title]=\"u.name\">{{ u.name }}</p>\r\n <p class=\"text-xs truncate text-[var(--secondary)] mt-0.5\" [title]=\"u.email\">{{ u.email }}</p>\r\n </div>\r\n }\r\n\r\n <div class=\"py-0.5 space-y-0.5\">\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('profile')\">\r\n <lucide-icon [name]=\"userIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" />\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 <lucide-icon [name]=\"settingsIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" />\r\n <span>Settings</span>\r\n </button>\r\n\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('billing')\">\r\n <lucide-icon [name]=\"creditCardIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" />\r\n <span>Billing</span>\r\n </button>\r\n </div>\r\n\r\n <div class=\"border-t border-[var(--border)] mt-1 pt-1\">\r\n <button type=\"button\" class=\"user-menu-item danger\" (click)=\"onAction('logout')\">\r\n <lucide-icon [name]=\"logOutIcon\" class=\"h-4 w-4 text-[var(--danger,#ef4444)]\" />\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-trigger{display:flex;align-items:center;gap:.375rem;border:1px solid var(--border);border-radius:999px;color:var(--foreground);background:var(--background);padding:.125rem .45rem .125rem .125rem;cursor:pointer;transition:background-color .15s ease,border-color .15s ease,box-shadow .15s ease}.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{box-shadow:0 12px 30px #0f172a24;animation:dropdownEnter .15s cubic-bezier(.16,1,.3,1)}.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-item:hover{background:color-mix(in srgb,var(--border) 32%,transparent)}.user-menu-item.danger{color:var(--danger, #ef4444)}.user-menu-item.danger:hover{background:color-mix(in srgb,var(--danger, #ef4444) 10%,transparent)}@keyframes dropdownEnter{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: UserAvatarComponent, selector: "lib-user-avatar", inputs: ["user"] }, { kind: "ngmodule", type: LucideAngularModule }, { kind: "component", type: i1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }] });
327
+ 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 relative inline-block text-left\">\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=\"h-7 w-7 rounded-full overflow-hidden\">\r\n <lib-user-avatar [user]=\"user()\" />\r\n </div>\r\n <svg lucideChevronDown class=\"h-3.5 w-3.5 text-[var(--secondary)] transition-transform duration-200\"\n [class.rotate-180]=\"isOpen()\"></svg>\n </button>\r\n\r\n @if (isOpen()) {\r\n <div\r\n class=\"user-dropdown-panel fixed left-2 right-2 top-14 mt-2 origin-top rounded-xl border border-[var(--border)] bg-[var(--background)] p-1.5 shadow-lg focus:outline-none z-50 sm:absolute sm:left-auto sm:right-0 sm:top-auto sm:w-56 sm:origin-top-right\">\r\n @if (user(); as u) {\r\n <div class=\"px-3 py-2 border-b border-[var(--border)] mb-1\">\r\n <p class=\"text-sm font-semibold truncate text-[var(--foreground)]\" [title]=\"u.name\">{{ u.name }}</p>\r\n <p class=\"text-xs truncate text-[var(--secondary)] mt-0.5\" [title]=\"u.email\">{{ u.email }}</p>\r\n </div>\r\n }\r\n\r\n <div class=\"py-0.5 space-y-0.5\">\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('profile')\">\r\n <svg lucideUser class=\"h-4 w-4 text-[var(--secondary)]\"></svg>\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=\"h-4 w-4 text-[var(--secondary)]\"></svg>\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=\"h-4 w-4 text-[var(--secondary)]\"></svg>\n <span>Billing</span>\r\n </button>\r\n </div>\r\n\r\n <div class=\"border-t border-[var(--border)] mt-1 pt-1\">\r\n <button type=\"button\" class=\"user-menu-item danger\" (click)=\"onAction('logout')\">\r\n <svg lucideLogOut class=\"h-4 w-4 text-[var(--danger,#ef4444)]\"></svg>\n <span>Sign Out</span>\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n</div>\n", styles: [":host{display:inline-block}.user-menu-trigger{display:flex;align-items:center;gap:.375rem;border:1px solid var(--border);border-radius:999px;color:var(--foreground);background:var(--background);padding:.125rem .45rem .125rem .125rem;cursor:pointer;transition:background-color .15s ease,border-color .15s ease,box-shadow .15s ease}.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{box-shadow:0 12px 30px #0f172a24;animation:dropdownEnter .15s cubic-bezier(.16,1,.3,1)}.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-item:hover{background:color-mix(in srgb,var(--border) 32%,transparent)}.user-menu-item.danger{color:var(--danger, #ef4444)}.user-menu-item.danger:hover{background:color-mix(in srgb,var(--danger, #ef4444) 10%,transparent)}@keyframes dropdownEnter{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: 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]" }] });
345
328
  }
346
329
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: UserMenuComponent, decorators: [{
347
330
  type: Component,
348
- args: [{ selector: 'lib-user-menu', standalone: true, imports: [CommonModule, UserAvatarComponent, LucideAngularModule], template: "<div class=\"user-menu-wrapper relative inline-block text-left\">\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=\"h-7 w-7 rounded-full overflow-hidden\">\r\n <lib-user-avatar [user]=\"user()\" />\r\n </div>\r\n <lucide-icon [name]=\"chevronDownIcon\" class=\"h-3.5 w-3.5 text-[var(--secondary)] transition-transform duration-200\"\r\n [class.rotate-180]=\"isOpen()\" />\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div\r\n class=\"user-dropdown-panel fixed left-2 right-2 top-14 mt-2 origin-top rounded-xl border border-[var(--border)] bg-[var(--background)] p-1.5 shadow-lg focus:outline-none z-50 sm:absolute sm:left-auto sm:right-0 sm:top-auto sm:w-56 sm:origin-top-right\">\r\n @if (user(); as u) {\r\n <div class=\"px-3 py-2 border-b border-[var(--border)] mb-1\">\r\n <p class=\"text-sm font-semibold truncate text-[var(--foreground)]\" [title]=\"u.name\">{{ u.name }}</p>\r\n <p class=\"text-xs truncate text-[var(--secondary)] mt-0.5\" [title]=\"u.email\">{{ u.email }}</p>\r\n </div>\r\n }\r\n\r\n <div class=\"py-0.5 space-y-0.5\">\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('profile')\">\r\n <lucide-icon [name]=\"userIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" />\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 <lucide-icon [name]=\"settingsIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" />\r\n <span>Settings</span>\r\n </button>\r\n\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('billing')\">\r\n <lucide-icon [name]=\"creditCardIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" />\r\n <span>Billing</span>\r\n </button>\r\n </div>\r\n\r\n <div class=\"border-t border-[var(--border)] mt-1 pt-1\">\r\n <button type=\"button\" class=\"user-menu-item danger\" (click)=\"onAction('logout')\">\r\n <lucide-icon [name]=\"logOutIcon\" class=\"h-4 w-4 text-[var(--danger,#ef4444)]\" />\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-trigger{display:flex;align-items:center;gap:.375rem;border:1px solid var(--border);border-radius:999px;color:var(--foreground);background:var(--background);padding:.125rem .45rem .125rem .125rem;cursor:pointer;transition:background-color .15s ease,border-color .15s ease,box-shadow .15s ease}.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{box-shadow:0 12px 30px #0f172a24;animation:dropdownEnter .15s cubic-bezier(.16,1,.3,1)}.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-item:hover{background:color-mix(in srgb,var(--border) 32%,transparent)}.user-menu-item.danger{color:var(--danger, #ef4444)}.user-menu-item.danger:hover{background:color-mix(in srgb,var(--danger, #ef4444) 10%,transparent)}@keyframes dropdownEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}\n"] }]
331
+ args: [{ selector: 'lib-user-menu', standalone: true, imports: [CommonModule, UserAvatarComponent, LucideChevronDown, LucideCreditCard, LucideLogOut, LucideSettings, LucideUser], template: "<div class=\"user-menu-wrapper relative inline-block text-left\">\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=\"h-7 w-7 rounded-full overflow-hidden\">\r\n <lib-user-avatar [user]=\"user()\" />\r\n </div>\r\n <svg lucideChevronDown class=\"h-3.5 w-3.5 text-[var(--secondary)] transition-transform duration-200\"\n [class.rotate-180]=\"isOpen()\"></svg>\n </button>\r\n\r\n @if (isOpen()) {\r\n <div\r\n class=\"user-dropdown-panel fixed left-2 right-2 top-14 mt-2 origin-top rounded-xl border border-[var(--border)] bg-[var(--background)] p-1.5 shadow-lg focus:outline-none z-50 sm:absolute sm:left-auto sm:right-0 sm:top-auto sm:w-56 sm:origin-top-right\">\r\n @if (user(); as u) {\r\n <div class=\"px-3 py-2 border-b border-[var(--border)] mb-1\">\r\n <p class=\"text-sm font-semibold truncate text-[var(--foreground)]\" [title]=\"u.name\">{{ u.name }}</p>\r\n <p class=\"text-xs truncate text-[var(--secondary)] mt-0.5\" [title]=\"u.email\">{{ u.email }}</p>\r\n </div>\r\n }\r\n\r\n <div class=\"py-0.5 space-y-0.5\">\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('profile')\">\r\n <svg lucideUser class=\"h-4 w-4 text-[var(--secondary)]\"></svg>\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=\"h-4 w-4 text-[var(--secondary)]\"></svg>\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=\"h-4 w-4 text-[var(--secondary)]\"></svg>\n <span>Billing</span>\r\n </button>\r\n </div>\r\n\r\n <div class=\"border-t border-[var(--border)] mt-1 pt-1\">\r\n <button type=\"button\" class=\"user-menu-item danger\" (click)=\"onAction('logout')\">\r\n <svg lucideLogOut class=\"h-4 w-4 text-[var(--danger,#ef4444)]\"></svg>\n <span>Sign Out</span>\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n</div>\n", styles: [":host{display:inline-block}.user-menu-trigger{display:flex;align-items:center;gap:.375rem;border:1px solid var(--border);border-radius:999px;color:var(--foreground);background:var(--background);padding:.125rem .45rem .125rem .125rem;cursor:pointer;transition:background-color .15s ease,border-color .15s ease,box-shadow .15s ease}.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{box-shadow:0 12px 30px #0f172a24;animation:dropdownEnter .15s cubic-bezier(.16,1,.3,1)}.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-item:hover{background:color-mix(in srgb,var(--border) 32%,transparent)}.user-menu-item.danger{color:var(--danger, #ef4444)}.user-menu-item.danger:hover{background:color-mix(in srgb,var(--danger, #ef4444) 10%,transparent)}@keyframes dropdownEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}\n"] }]
349
332
  }], propDecorators: { user: [{ type: i0.Input, args: [{ isSignal: true, alias: "user", required: false }] }], menuAction: [{ type: i0.Output, args: ["menuAction"] }], onOutsideClick: [{
350
333
  type: HostListener,
351
334
  args: ['document:click', ['$event']]
352
335
  }] } });
353
336
 
354
337
  // lib/shared/icon/icon.component.ts
355
- const ICONS = {
356
- search: Search,
357
- menu: Menu,
358
- x: X,
359
- sun: Sun,
360
- moon: Moon,
361
- monitor: Monitor,
362
- bell: Bell,
363
- grid: Grid,
364
- chevronDown: ChevronDown,
365
- building2: Building2
366
- };
367
338
  class IconComponent {
368
339
  name = input.required(...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
369
340
  size = input(24, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
370
341
  color = input(undefined, ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
371
342
  strokeWidth = input(undefined, ...(ngDevMode ? [{ debugName: "strokeWidth" }] : /* istanbul ignore next */ []));
372
- iconName = computed(() => {
373
- const name = this.name();
374
- return ICONS[name] || Search;
375
- }, ...(ngDevMode ? [{ debugName: "iconName" }] : /* istanbul ignore next */ []));
376
343
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: IconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
377
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.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: `
378
- <lucide-icon
379
- [name]="iconName()"
380
- [size]="size()"
381
- [color]="color()"
382
- [strokeWidth]="strokeWidth()"
383
- class="lib-icon"
384
- />
385
- `, isInline: true, styles: [":host{display:inline-flex;align-items:center;justify-content:center}.lib-icon{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: LucideAngularModule }, { kind: "component", type: i1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }] });
344
+ 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: `
345
+ @switch (name()) {
346
+ @case ('menu') {
347
+ <svg lucideMenu [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
348
+ }
349
+ @case ('x') {
350
+ <svg lucideX [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
351
+ }
352
+ @case ('sun') {
353
+ <svg lucideSun [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
354
+ }
355
+ @case ('moon') {
356
+ <svg lucideMoon [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
357
+ }
358
+ @case ('monitor') {
359
+ <svg lucideMonitor [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
360
+ }
361
+ @case ('bell') {
362
+ <svg lucideBell [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
363
+ }
364
+ @case ('grid') {
365
+ <svg lucideGrid [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
366
+ }
367
+ @case ('chevronDown') {
368
+ <svg lucideChevronDown [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
369
+ }
370
+ @case ('building2') {
371
+ <svg lucideBuilding2 [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
372
+ }
373
+ @default {
374
+ <svg lucideSearch [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
375
+ }
376
+ }
377
+ `, 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]" }] });
386
378
  }
387
379
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: IconComponent, decorators: [{
388
380
  type: Component,
389
- args: [{ selector: 'lib-icon', standalone: true, imports: [CommonModule, LucideAngularModule], template: `
390
- <lucide-icon
391
- [name]="iconName()"
392
- [size]="size()"
393
- [color]="color()"
394
- [strokeWidth]="strokeWidth()"
395
- class="lib-icon"
396
- />
397
- `, styles: [":host{display:inline-flex;align-items:center;justify-content:center}.lib-icon{display:block}\n"] }]
381
+ args: [{ selector: 'lib-icon', standalone: true, imports: [
382
+ CommonModule,
383
+ LucideBell,
384
+ LucideBuilding2,
385
+ LucideChevronDown,
386
+ LucideGrid,
387
+ LucideMenu,
388
+ LucideMonitor,
389
+ LucideMoon,
390
+ LucideSearch,
391
+ LucideSun,
392
+ LucideX,
393
+ ], template: `
394
+ @switch (name()) {
395
+ @case ('menu') {
396
+ <svg lucideMenu [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
397
+ }
398
+ @case ('x') {
399
+ <svg lucideX [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
400
+ }
401
+ @case ('sun') {
402
+ <svg lucideSun [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
403
+ }
404
+ @case ('moon') {
405
+ <svg lucideMoon [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
406
+ }
407
+ @case ('monitor') {
408
+ <svg lucideMonitor [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
409
+ }
410
+ @case ('bell') {
411
+ <svg lucideBell [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
412
+ }
413
+ @case ('grid') {
414
+ <svg lucideGrid [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
415
+ }
416
+ @case ('chevronDown') {
417
+ <svg lucideChevronDown [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
418
+ }
419
+ @case ('building2') {
420
+ <svg lucideBuilding2 [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
421
+ }
422
+ @default {
423
+ <svg lucideSearch [size]="size()" [color]="color()" [strokeWidth]="strokeWidth()" class="lib-icon"></svg>
424
+ }
425
+ }
426
+ `, styles: [":host{display:inline-flex;align-items:center;justify-content:center;color:inherit}.lib-icon{display:block;color:inherit}\n"] }]
398
427
  }], 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 }] }] } });
399
428
 
400
429
  class HeaderComponent {
@@ -491,7 +520,7 @@ class HeaderComponent {
491
520
  this.menuAction.emit(action);
492
521
  }
493
522
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: HeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
494
- 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: false, transformFunction: null }, permissions: { classPropertyName: "permissions", publicName: "permissions", isSignal: true, isRequired: false, transformFunction: null }, organization: { classPropertyName: "organization", publicName: "organization", 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 }, showSignIn: { classPropertyName: "showSignIn", publicName: "showSignIn", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { search: "search", signIn: "signIn", applicationSelected: "applicationSelected", notificationClick: "notificationClick", markRead: "markRead", clearAll: "clearAll", menuAction: "menuAction" }, ngImport: i0, template: "<header\n class=\"header-container fixed top-0 inset-x-0 z-40 flex h-[3.75rem] items-center justify-between px-3 text-[var(--foreground)] sm:px-4 lg:px-6\">\n\n <!-- Left Side: Brand, Current App & Desktop Links -->\n <div class=\"flex min-w-0 items-center gap-3\">\n\n <a routerLink=\"/\" class=\"brand-identity\">\n <img [src]=\"logoUrl()\" [alt]=\"appName() + ' logo'\" width=\"26\" height=\"26\" class=\"brand-logo\" />\n <span class=\"brand-name\">{{ appName() }}</span>\n </a>\n\n @if (currentAppName()) {\n <div class=\"current-app-identity\" [attr.aria-label]=\"'Current app: ' + currentAppName()\">\n <span class=\"brand-separator\" aria-hidden=\"true\">|</span>\n <span class=\"current-app-name\">{{ currentAppName() }}</span>\n </div>\n }\n\n <!-- Desktop Navigation Links -->\n <nav class=\"desktop-nav ml-1 hidden items-center lg:flex\" aria-label=\"Desktop primary navigation\">\n @for (item of navigationItems(); track item.id) {\n @if (hasPermission(item)) {\n @if (item.route) {\n <a [routerLink]=\"item.route\" routerLinkActive=\"active\" [routerLinkActiveOptions]=\"{exact: true}\" class=\"nav-link\"\n [attr.aria-current]=\"isActive(item) ? 'page' : null\">\n {{ item.label }}\n </a>\n } @else {\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" class=\"nav-link\">\n {{ item.label }}\n </a>\n }\n }\n }\n </nav>\n </div>\n\n <!-- Middle: Search Input (Desktop) -->\n @if (showSearch()) {\n <div class=\"hidden flex-1 justify-center px-4 md:flex\">\n <div class=\"header-search-shell\">\n <lib-icon name=\"search\" [size]=\"14\"\n class=\"pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-[var(--secondary)]\" />\n\n <input type=\"search\" placeholder=\"Search Excellore...\" #searchInput\n (keyup.enter)=\"onSearch(searchInput.value); searchInput.value = ''\" class=\"header-search\" autocomplete=\"off\"\n aria-label=\"Search\" />\n </div>\n </div>\n } @else {\n <div class=\"hidden flex-1 md:block\"></div>\n }\n\n <!-- Right Side Actions -->\n <div class=\"header-actions\">\n\n <!-- Mobile Search Trigger -->\n @if (showSearch()) {\n <button type=\"button\" class=\"header-action mobile-search-action\" (click)=\"toggleMobileMenu()\"\n aria-label=\"Open mobile menu\" aria-expanded=\"isMobileMenuOpen()\" #menuToggle>\n <lib-icon name=\"search\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\n </button>\n }\n\n <!-- Theme Switcher -->\n @if (showThemeSwitcher()) {\n <button type=\"button\" class=\"header-action\" (click)=\"themeService.toggleTheme()\" aria-label=\"Toggle theme\">\n @switch (themeService.theme()) {\n @case ('light') {\n <lib-icon name=\"sun\" [size]=\"16\" class=\"text-[var(--primary)]\" />\n }\n @case ('dark') {\n <lib-icon name=\"moon\" [size]=\"16\" class=\"text-[var(--primary)]\" />\n }\n @default {\n <lib-icon name=\"monitor\" [size]=\"16\" class=\"text-[var(--secondary)]\" />\n }\n }\n </button>\n }\n\n <!-- App Switcher -->\n @if (showAppSwitcher() && applications().length > 0) {\n <lib-app-switcher [applications]=\"applications()\" (applicationSelected)=\"onApplicationSelect($event)\" />\n }\n\n <!-- Notifications -->\n @if (showNotifications()) {\n <lib-notification-bell [unreadCount]=\"unreadCount()\" [notifications]=\"notifications()\"\n (notificationClick)=\"onNotificationClick($event)\" (markRead)=\"onMarkRead($event)\" (clearAll)=\"onClearAll()\" />\n }\n\n <!-- User Menu -->\n @if (user()) {\n <div class=\"user-menu-divider\">\n <lib-user-menu [user]=\"user()\" (menuAction)=\"onMenuAction($event)\" />\n </div>\n } @else if (showSignIn()) {\n <button type=\"button\" class=\"sign-in-button\" (click)=\"signIn.emit()\">\n Sign in\n </button>\n }\n\n <!-- Mobile Menu Toggle (Hamburger) -->\n @if (hasMobileMenuContent()) {\n <button type=\"button\" class=\"header-action mobile-menu-action\" (click)=\"toggleMobileMenu()\"\n aria-label=\"Toggle navigation menu\" aria-expanded=\"isMobileMenuOpen()\" #mobileMenuToggle>\n <lib-icon [name]=\"isMobileMenuOpen() ? 'x' : 'menu'\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\n </button>\n }\n </div>\n</header>\n\n<!-- Mobile Menu Backdrop -->\n@if (isMobileMenuOpen() && hasMobileMenuContent()) {\n<div class=\"mobile-backdrop fixed inset-0 z-30 lg:hidden\" (click)=\"closeMobileMenu()\"\n (keydown.escape)=\"closeMobileMenu()\" tabindex=\"-1\" aria-hidden=\"true\"></div>\n\n<!-- Mobile Navigation Drawer -->\n<div\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\"\n role=\"dialog\" aria-modal=\"true\" aria-label=\"Mobile navigation\" (keydown.escape)=\"closeMobileMenu()\">\n <!-- Mobile Search -->\n @if (showSearch()) {\n <div class=\"relative mb-3 w-full\">\n <lib-icon name=\"search\" [size]=\"16\"\n class=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[var(--secondary)] pointer-events-none\"\n aria-hidden=\"true\" />\n\n <input type=\"search\" placeholder=\"Search Excellore...\" #mobileSearchInput\n (keyup.enter)=\"onSearch(mobileSearchInput.value); mobileSearchInput.value = ''; closeMobileMenu()\"\n class=\"header-search mobile\" autocomplete=\"off\" aria-label=\"Search\" />\n </div>\n }\n\n <!-- Mobile Nav Links -->\n <nav class=\"flex flex-col gap-1 overflow-y-auto\" role=\"navigation\">\n @for (item of navigationItems(); track item.id) {\n @if (hasPermission(item)) {\n @if (item.route) {\n <a [routerLink]=\"item.route\" routerLinkActive=\"mobile-active\" [routerLinkActiveOptions]=\"{exact: true}\"\n (click)=\"closeMobileMenu()\" class=\"mobile-nav-link\" [attr.aria-current]=\"isActive(item) ? 'page' : null\">\n {{ item.label }}\n </a>\n } @else {\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" (click)=\"closeMobileMenu()\" class=\"mobile-nav-link\">\n {{ item.label }}\n </a>\n }\n }\n }\n </nav>\n</div>\n}\n", styles: [":host{display:block}.header-container{border-bottom:1px solid var(--border);background:var(--background);box-shadow:0 1px 2px #0f172a0a;will-change:transform}.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{gap:1.25rem;padding:0;background:transparent}.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}.sign-in-button{height:2.15rem;border:1px solid var(--primary);border-radius:.5rem;color:#fff;background:var(--primary);padding:0 .85rem;font-size:.825rem;font-weight:700;cursor:pointer;transition:opacity .15s ease,box-shadow .15s ease}.sign-in-button:hover{opacity:.92;box-shadow:0 4px 10px color-mix(in srgb,var(--primary) 18%,transparent)}.sign-in-button:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.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{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){.brand-name{display:inline-block}.header-actions{gap:.25rem}.user-menu-divider{padding-left:.5rem}}@media(min-width:768px){.mobile-search-action{display:none}}@media(min-width:1024px){.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"] }] });
523
+ 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: false, transformFunction: null }, permissions: { classPropertyName: "permissions", publicName: "permissions", isSignal: true, isRequired: false, transformFunction: null }, organization: { classPropertyName: "organization", publicName: "organization", 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 }, showSignIn: { classPropertyName: "showSignIn", publicName: "showSignIn", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { search: "search", signIn: "signIn", applicationSelected: "applicationSelected", notificationClick: "notificationClick", markRead: "markRead", clearAll: "clearAll", menuAction: "menuAction" }, ngImport: i0, template: "<header\n class=\"header-container fixed top-0 inset-x-0 z-40 flex h-[3.75rem] items-center justify-between px-3 text-[var(--foreground)] sm:px-4 lg:px-6\">\n\n <!-- Left Side: Brand, Current App & Desktop Links -->\n <div class=\"flex min-w-0 items-center gap-3\">\n\n <a routerLink=\"/\" class=\"brand-identity\">\n <img [src]=\"logoUrl()\" [alt]=\"appName() + ' logo'\" width=\"26\" height=\"26\" class=\"brand-logo\" />\n <span class=\"brand-name\">{{ appName() }}</span>\n </a>\n\n @if (currentAppName()) {\n <div class=\"current-app-identity\" [attr.aria-label]=\"'Current app: ' + currentAppName()\">\n <span class=\"brand-separator\" aria-hidden=\"true\">|</span>\n <span class=\"current-app-name\">{{ currentAppName() }}</span>\n </div>\n }\n\n <!-- Desktop Navigation Links -->\n <nav class=\"desktop-nav ml-1 hidden items-center lg:flex\" aria-label=\"Desktop primary navigation\">\n @for (item of navigationItems(); track item.id) {\n @if (hasPermission(item)) {\n @if (item.route) {\n <a [routerLink]=\"item.route\" routerLinkActive=\"active\" [routerLinkActiveOptions]=\"{exact: true}\" class=\"nav-link\"\n [attr.aria-current]=\"isActive(item) ? 'page' : null\">\n {{ item.label }}\n </a>\n } @else {\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" class=\"nav-link\">\n {{ item.label }}\n </a>\n }\n }\n }\n </nav>\n </div>\n\n <!-- Middle: Search Input (Desktop) -->\n @if (showSearch()) {\n <div class=\"hidden flex-1 justify-center px-4 md:flex\">\n <div class=\"header-search-shell\">\n <lib-icon name=\"search\" [size]=\"14\"\n class=\"pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-[var(--secondary)]\" />\n\n <input type=\"search\" placeholder=\"Search Excellore...\" #searchInput\n (keyup.enter)=\"onSearch(searchInput.value); searchInput.value = ''\" class=\"header-search\" autocomplete=\"off\"\n aria-label=\"Search\" />\n </div>\n </div>\n } @else {\n <div class=\"hidden flex-1 md:block\"></div>\n }\n\n <!-- Right Side Actions -->\n <div class=\"header-actions\">\n\n <!-- Mobile Search Trigger -->\n @if (showSearch()) {\n <button type=\"button\" class=\"header-action mobile-search-action\" (click)=\"toggleMobileMenu()\"\n aria-label=\"Open mobile menu\" aria-expanded=\"isMobileMenuOpen()\" #menuToggle>\n <lib-icon name=\"search\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\n </button>\n }\n\n <!-- Theme Switcher -->\n @if (showThemeSwitcher()) {\n <button type=\"button\" class=\"header-action\" (click)=\"themeService.toggleTheme()\" aria-label=\"Toggle theme\">\n @switch (themeService.theme()) {\n @case ('light') {\n <lib-icon name=\"sun\" [size]=\"16\" class=\"text-[var(--primary)]\" />\n }\n @case ('dark') {\n <lib-icon name=\"moon\" [size]=\"16\" class=\"text-[var(--primary)]\" />\n }\n @default {\n <lib-icon name=\"monitor\" [size]=\"16\" class=\"text-[var(--secondary)]\" />\n }\n }\n </button>\n }\n\n <!-- App Switcher -->\n @if (showAppSwitcher() && applications().length > 0) {\n <lib-app-switcher [applications]=\"applications()\" (applicationSelected)=\"onApplicationSelect($event)\" />\n }\n\n <!-- Notifications -->\n @if (showNotifications()) {\n <lib-notification-bell [unreadCount]=\"unreadCount()\" [notifications]=\"notifications()\"\n (notificationClick)=\"onNotificationClick($event)\" (markRead)=\"onMarkRead($event)\" (clearAll)=\"onClearAll()\" />\n }\n\n <!-- User Menu -->\n @if (user()) {\n <div class=\"user-menu-divider\">\n <lib-user-menu [user]=\"user()\" (menuAction)=\"onMenuAction($event)\" />\n </div>\n } @else if (showSignIn()) {\n <button type=\"button\" class=\"sign-in-button\" (click)=\"signIn.emit()\">\n Sign in\n </button>\n }\n\n <!-- Mobile Menu Toggle (Hamburger) -->\n @if (hasMobileMenuContent()) {\n <button type=\"button\" class=\"header-action mobile-menu-action\" (click)=\"toggleMobileMenu()\"\n aria-label=\"Toggle navigation menu\" aria-expanded=\"isMobileMenuOpen()\" #mobileMenuToggle>\n <lib-icon [name]=\"isMobileMenuOpen() ? 'x' : 'menu'\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\n </button>\n }\n </div>\n</header>\n\n<!-- Mobile Menu Backdrop -->\n@if (isMobileMenuOpen() && hasMobileMenuContent()) {\n<div class=\"mobile-backdrop fixed inset-0 z-30 lg:hidden\" (click)=\"closeMobileMenu()\"\n (keydown.escape)=\"closeMobileMenu()\" tabindex=\"-1\" aria-hidden=\"true\"></div>\n\n<!-- Mobile Navigation Drawer -->\n<div\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\"\n role=\"dialog\" aria-modal=\"true\" aria-label=\"Mobile navigation\" (keydown.escape)=\"closeMobileMenu()\">\n <!-- Mobile Search -->\n @if (showSearch()) {\n <div class=\"relative mb-3 w-full\">\n <lib-icon name=\"search\" [size]=\"16\"\n class=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[var(--secondary)] pointer-events-none\"\n aria-hidden=\"true\" />\n\n <input type=\"search\" placeholder=\"Search Excellore...\" #mobileSearchInput\n (keyup.enter)=\"onSearch(mobileSearchInput.value); mobileSearchInput.value = ''; closeMobileMenu()\"\n class=\"header-search mobile\" autocomplete=\"off\" aria-label=\"Search\" />\n </div>\n }\n\n <!-- Mobile Nav Links -->\n <nav class=\"flex flex-col gap-1 overflow-y-auto\" role=\"navigation\">\n @for (item of navigationItems(); track item.id) {\n @if (hasPermission(item)) {\n @if (item.route) {\n <a [routerLink]=\"item.route\" routerLinkActive=\"mobile-active\" [routerLinkActiveOptions]=\"{exact: true}\"\n (click)=\"closeMobileMenu()\" class=\"mobile-nav-link\" [attr.aria-current]=\"isActive(item) ? 'page' : null\">\n {{ item.label }}\n </a>\n } @else {\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" (click)=\"closeMobileMenu()\" class=\"mobile-nav-link\">\n {{ item.label }}\n </a>\n }\n }\n }\n </nav>\n</div>\n}\n", styles: [":host{display:block}.header-container{border-bottom:1px solid var(--border);background:var(--background);box-shadow:0 1px 2px #0f172a0a;padding-inline:1rem;will-change:transform}.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{gap:1.25rem;padding:0;background:transparent}.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}.sign-in-button{height:2.15rem;border:1px solid var(--primary);border-radius:.5rem;color:#fff;background:var(--primary);padding:0 .85rem;font-size:.825rem;font-weight:700;cursor:pointer;transition:opacity .15s ease,box-shadow .15s ease}.sign-in-button:hover{opacity:.92;box-shadow:0 4px 10px color-mix(in srgb,var(--primary) 18%,transparent)}.sign-in-button:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.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{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){.mobile-search-action{display:none}}@media(min-width:1024px){.header-container{padding-inline:1.5rem}.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"] }] });
495
524
  }
496
525
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: HeaderComponent, decorators: [{
497
526
  type: Component,
@@ -503,7 +532,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
503
532
  NotificationBellComponent,
504
533
  UserMenuComponent,
505
534
  IconComponent,
506
- ], template: "<header\n class=\"header-container fixed top-0 inset-x-0 z-40 flex h-[3.75rem] items-center justify-between px-3 text-[var(--foreground)] sm:px-4 lg:px-6\">\n\n <!-- Left Side: Brand, Current App & Desktop Links -->\n <div class=\"flex min-w-0 items-center gap-3\">\n\n <a routerLink=\"/\" class=\"brand-identity\">\n <img [src]=\"logoUrl()\" [alt]=\"appName() + ' logo'\" width=\"26\" height=\"26\" class=\"brand-logo\" />\n <span class=\"brand-name\">{{ appName() }}</span>\n </a>\n\n @if (currentAppName()) {\n <div class=\"current-app-identity\" [attr.aria-label]=\"'Current app: ' + currentAppName()\">\n <span class=\"brand-separator\" aria-hidden=\"true\">|</span>\n <span class=\"current-app-name\">{{ currentAppName() }}</span>\n </div>\n }\n\n <!-- Desktop Navigation Links -->\n <nav class=\"desktop-nav ml-1 hidden items-center lg:flex\" aria-label=\"Desktop primary navigation\">\n @for (item of navigationItems(); track item.id) {\n @if (hasPermission(item)) {\n @if (item.route) {\n <a [routerLink]=\"item.route\" routerLinkActive=\"active\" [routerLinkActiveOptions]=\"{exact: true}\" class=\"nav-link\"\n [attr.aria-current]=\"isActive(item) ? 'page' : null\">\n {{ item.label }}\n </a>\n } @else {\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" class=\"nav-link\">\n {{ item.label }}\n </a>\n }\n }\n }\n </nav>\n </div>\n\n <!-- Middle: Search Input (Desktop) -->\n @if (showSearch()) {\n <div class=\"hidden flex-1 justify-center px-4 md:flex\">\n <div class=\"header-search-shell\">\n <lib-icon name=\"search\" [size]=\"14\"\n class=\"pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-[var(--secondary)]\" />\n\n <input type=\"search\" placeholder=\"Search Excellore...\" #searchInput\n (keyup.enter)=\"onSearch(searchInput.value); searchInput.value = ''\" class=\"header-search\" autocomplete=\"off\"\n aria-label=\"Search\" />\n </div>\n </div>\n } @else {\n <div class=\"hidden flex-1 md:block\"></div>\n }\n\n <!-- Right Side Actions -->\n <div class=\"header-actions\">\n\n <!-- Mobile Search Trigger -->\n @if (showSearch()) {\n <button type=\"button\" class=\"header-action mobile-search-action\" (click)=\"toggleMobileMenu()\"\n aria-label=\"Open mobile menu\" aria-expanded=\"isMobileMenuOpen()\" #menuToggle>\n <lib-icon name=\"search\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\n </button>\n }\n\n <!-- Theme Switcher -->\n @if (showThemeSwitcher()) {\n <button type=\"button\" class=\"header-action\" (click)=\"themeService.toggleTheme()\" aria-label=\"Toggle theme\">\n @switch (themeService.theme()) {\n @case ('light') {\n <lib-icon name=\"sun\" [size]=\"16\" class=\"text-[var(--primary)]\" />\n }\n @case ('dark') {\n <lib-icon name=\"moon\" [size]=\"16\" class=\"text-[var(--primary)]\" />\n }\n @default {\n <lib-icon name=\"monitor\" [size]=\"16\" class=\"text-[var(--secondary)]\" />\n }\n }\n </button>\n }\n\n <!-- App Switcher -->\n @if (showAppSwitcher() && applications().length > 0) {\n <lib-app-switcher [applications]=\"applications()\" (applicationSelected)=\"onApplicationSelect($event)\" />\n }\n\n <!-- Notifications -->\n @if (showNotifications()) {\n <lib-notification-bell [unreadCount]=\"unreadCount()\" [notifications]=\"notifications()\"\n (notificationClick)=\"onNotificationClick($event)\" (markRead)=\"onMarkRead($event)\" (clearAll)=\"onClearAll()\" />\n }\n\n <!-- User Menu -->\n @if (user()) {\n <div class=\"user-menu-divider\">\n <lib-user-menu [user]=\"user()\" (menuAction)=\"onMenuAction($event)\" />\n </div>\n } @else if (showSignIn()) {\n <button type=\"button\" class=\"sign-in-button\" (click)=\"signIn.emit()\">\n Sign in\n </button>\n }\n\n <!-- Mobile Menu Toggle (Hamburger) -->\n @if (hasMobileMenuContent()) {\n <button type=\"button\" class=\"header-action mobile-menu-action\" (click)=\"toggleMobileMenu()\"\n aria-label=\"Toggle navigation menu\" aria-expanded=\"isMobileMenuOpen()\" #mobileMenuToggle>\n <lib-icon [name]=\"isMobileMenuOpen() ? 'x' : 'menu'\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\n </button>\n }\n </div>\n</header>\n\n<!-- Mobile Menu Backdrop -->\n@if (isMobileMenuOpen() && hasMobileMenuContent()) {\n<div class=\"mobile-backdrop fixed inset-0 z-30 lg:hidden\" (click)=\"closeMobileMenu()\"\n (keydown.escape)=\"closeMobileMenu()\" tabindex=\"-1\" aria-hidden=\"true\"></div>\n\n<!-- Mobile Navigation Drawer -->\n<div\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\"\n role=\"dialog\" aria-modal=\"true\" aria-label=\"Mobile navigation\" (keydown.escape)=\"closeMobileMenu()\">\n <!-- Mobile Search -->\n @if (showSearch()) {\n <div class=\"relative mb-3 w-full\">\n <lib-icon name=\"search\" [size]=\"16\"\n class=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[var(--secondary)] pointer-events-none\"\n aria-hidden=\"true\" />\n\n <input type=\"search\" placeholder=\"Search Excellore...\" #mobileSearchInput\n (keyup.enter)=\"onSearch(mobileSearchInput.value); mobileSearchInput.value = ''; closeMobileMenu()\"\n class=\"header-search mobile\" autocomplete=\"off\" aria-label=\"Search\" />\n </div>\n }\n\n <!-- Mobile Nav Links -->\n <nav class=\"flex flex-col gap-1 overflow-y-auto\" role=\"navigation\">\n @for (item of navigationItems(); track item.id) {\n @if (hasPermission(item)) {\n @if (item.route) {\n <a [routerLink]=\"item.route\" routerLinkActive=\"mobile-active\" [routerLinkActiveOptions]=\"{exact: true}\"\n (click)=\"closeMobileMenu()\" class=\"mobile-nav-link\" [attr.aria-current]=\"isActive(item) ? 'page' : null\">\n {{ item.label }}\n </a>\n } @else {\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" (click)=\"closeMobileMenu()\" class=\"mobile-nav-link\">\n {{ item.label }}\n </a>\n }\n }\n }\n </nav>\n</div>\n}\n", styles: [":host{display:block}.header-container{border-bottom:1px solid var(--border);background:var(--background);box-shadow:0 1px 2px #0f172a0a;will-change:transform}.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{gap:1.25rem;padding:0;background:transparent}.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}.sign-in-button{height:2.15rem;border:1px solid var(--primary);border-radius:.5rem;color:#fff;background:var(--primary);padding:0 .85rem;font-size:.825rem;font-weight:700;cursor:pointer;transition:opacity .15s ease,box-shadow .15s ease}.sign-in-button:hover{opacity:.92;box-shadow:0 4px 10px color-mix(in srgb,var(--primary) 18%,transparent)}.sign-in-button:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.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{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){.brand-name{display:inline-block}.header-actions{gap:.25rem}.user-menu-divider{padding-left:.5rem}}@media(min-width:768px){.mobile-search-action{display:none}}@media(min-width:1024px){.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"] }]
535
+ ], template: "<header\n class=\"header-container fixed top-0 inset-x-0 z-40 flex h-[3.75rem] items-center justify-between px-3 text-[var(--foreground)] sm:px-4 lg:px-6\">\n\n <!-- Left Side: Brand, Current App & Desktop Links -->\n <div class=\"flex min-w-0 items-center gap-3\">\n\n <a routerLink=\"/\" class=\"brand-identity\">\n <img [src]=\"logoUrl()\" [alt]=\"appName() + ' logo'\" width=\"26\" height=\"26\" class=\"brand-logo\" />\n <span class=\"brand-name\">{{ appName() }}</span>\n </a>\n\n @if (currentAppName()) {\n <div class=\"current-app-identity\" [attr.aria-label]=\"'Current app: ' + currentAppName()\">\n <span class=\"brand-separator\" aria-hidden=\"true\">|</span>\n <span class=\"current-app-name\">{{ currentAppName() }}</span>\n </div>\n }\n\n <!-- Desktop Navigation Links -->\n <nav class=\"desktop-nav ml-1 hidden items-center lg:flex\" aria-label=\"Desktop primary navigation\">\n @for (item of navigationItems(); track item.id) {\n @if (hasPermission(item)) {\n @if (item.route) {\n <a [routerLink]=\"item.route\" routerLinkActive=\"active\" [routerLinkActiveOptions]=\"{exact: true}\" class=\"nav-link\"\n [attr.aria-current]=\"isActive(item) ? 'page' : null\">\n {{ item.label }}\n </a>\n } @else {\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" class=\"nav-link\">\n {{ item.label }}\n </a>\n }\n }\n }\n </nav>\n </div>\n\n <!-- Middle: Search Input (Desktop) -->\n @if (showSearch()) {\n <div class=\"hidden flex-1 justify-center px-4 md:flex\">\n <div class=\"header-search-shell\">\n <lib-icon name=\"search\" [size]=\"14\"\n class=\"pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-[var(--secondary)]\" />\n\n <input type=\"search\" placeholder=\"Search Excellore...\" #searchInput\n (keyup.enter)=\"onSearch(searchInput.value); searchInput.value = ''\" class=\"header-search\" autocomplete=\"off\"\n aria-label=\"Search\" />\n </div>\n </div>\n } @else {\n <div class=\"hidden flex-1 md:block\"></div>\n }\n\n <!-- Right Side Actions -->\n <div class=\"header-actions\">\n\n <!-- Mobile Search Trigger -->\n @if (showSearch()) {\n <button type=\"button\" class=\"header-action mobile-search-action\" (click)=\"toggleMobileMenu()\"\n aria-label=\"Open mobile menu\" aria-expanded=\"isMobileMenuOpen()\" #menuToggle>\n <lib-icon name=\"search\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\n </button>\n }\n\n <!-- Theme Switcher -->\n @if (showThemeSwitcher()) {\n <button type=\"button\" class=\"header-action\" (click)=\"themeService.toggleTheme()\" aria-label=\"Toggle theme\">\n @switch (themeService.theme()) {\n @case ('light') {\n <lib-icon name=\"sun\" [size]=\"16\" class=\"text-[var(--primary)]\" />\n }\n @case ('dark') {\n <lib-icon name=\"moon\" [size]=\"16\" class=\"text-[var(--primary)]\" />\n }\n @default {\n <lib-icon name=\"monitor\" [size]=\"16\" class=\"text-[var(--secondary)]\" />\n }\n }\n </button>\n }\n\n <!-- App Switcher -->\n @if (showAppSwitcher() && applications().length > 0) {\n <lib-app-switcher [applications]=\"applications()\" (applicationSelected)=\"onApplicationSelect($event)\" />\n }\n\n <!-- Notifications -->\n @if (showNotifications()) {\n <lib-notification-bell [unreadCount]=\"unreadCount()\" [notifications]=\"notifications()\"\n (notificationClick)=\"onNotificationClick($event)\" (markRead)=\"onMarkRead($event)\" (clearAll)=\"onClearAll()\" />\n }\n\n <!-- User Menu -->\n @if (user()) {\n <div class=\"user-menu-divider\">\n <lib-user-menu [user]=\"user()\" (menuAction)=\"onMenuAction($event)\" />\n </div>\n } @else if (showSignIn()) {\n <button type=\"button\" class=\"sign-in-button\" (click)=\"signIn.emit()\">\n Sign in\n </button>\n }\n\n <!-- Mobile Menu Toggle (Hamburger) -->\n @if (hasMobileMenuContent()) {\n <button type=\"button\" class=\"header-action mobile-menu-action\" (click)=\"toggleMobileMenu()\"\n aria-label=\"Toggle navigation menu\" aria-expanded=\"isMobileMenuOpen()\" #mobileMenuToggle>\n <lib-icon [name]=\"isMobileMenuOpen() ? 'x' : 'menu'\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\n </button>\n }\n </div>\n</header>\n\n<!-- Mobile Menu Backdrop -->\n@if (isMobileMenuOpen() && hasMobileMenuContent()) {\n<div class=\"mobile-backdrop fixed inset-0 z-30 lg:hidden\" (click)=\"closeMobileMenu()\"\n (keydown.escape)=\"closeMobileMenu()\" tabindex=\"-1\" aria-hidden=\"true\"></div>\n\n<!-- Mobile Navigation Drawer -->\n<div\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\"\n role=\"dialog\" aria-modal=\"true\" aria-label=\"Mobile navigation\" (keydown.escape)=\"closeMobileMenu()\">\n <!-- Mobile Search -->\n @if (showSearch()) {\n <div class=\"relative mb-3 w-full\">\n <lib-icon name=\"search\" [size]=\"16\"\n class=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[var(--secondary)] pointer-events-none\"\n aria-hidden=\"true\" />\n\n <input type=\"search\" placeholder=\"Search Excellore...\" #mobileSearchInput\n (keyup.enter)=\"onSearch(mobileSearchInput.value); mobileSearchInput.value = ''; closeMobileMenu()\"\n class=\"header-search mobile\" autocomplete=\"off\" aria-label=\"Search\" />\n </div>\n }\n\n <!-- Mobile Nav Links -->\n <nav class=\"flex flex-col gap-1 overflow-y-auto\" role=\"navigation\">\n @for (item of navigationItems(); track item.id) {\n @if (hasPermission(item)) {\n @if (item.route) {\n <a [routerLink]=\"item.route\" routerLinkActive=\"mobile-active\" [routerLinkActiveOptions]=\"{exact: true}\"\n (click)=\"closeMobileMenu()\" class=\"mobile-nav-link\" [attr.aria-current]=\"isActive(item) ? 'page' : null\">\n {{ item.label }}\n </a>\n } @else {\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" (click)=\"closeMobileMenu()\" class=\"mobile-nav-link\">\n {{ item.label }}\n </a>\n }\n }\n }\n </nav>\n</div>\n}\n", styles: [":host{display:block}.header-container{border-bottom:1px solid var(--border);background:var(--background);box-shadow:0 1px 2px #0f172a0a;padding-inline:1rem;will-change:transform}.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{gap:1.25rem;padding:0;background:transparent}.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}.sign-in-button{height:2.15rem;border:1px solid var(--primary);border-radius:.5rem;color:#fff;background:var(--primary);padding:0 .85rem;font-size:.825rem;font-weight:700;cursor:pointer;transition:opacity .15s ease,box-shadow .15s ease}.sign-in-button:hover{opacity:.92;box-shadow:0 4px 10px color-mix(in srgb,var(--primary) 18%,transparent)}.sign-in-button:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.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{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){.mobile-search-action{display:none}}@media(min-width:1024px){.header-container{padding-inline:1.5rem}.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"] }]
507
536
  }], propDecorators: { user: [{ type: i0.Input, args: [{ isSignal: true, alias: "user", required: false }] }], permissions: [{ type: i0.Input, args: [{ isSignal: true, alias: "permissions", required: false }] }], organization: [{ type: i0.Input, args: [{ isSignal: true, alias: "organization", 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 }] }], showSignIn: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSignIn", required: false }] }], search: [{ type: i0.Output, args: ["search"] }], signIn: [{ type: i0.Output, args: ["signIn"] }], 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"] }] } });
508
537
 
509
538
  /*
@@ -1 +1 @@
1
- {"version":3,"file":"excellore-ui.mjs","sources":["../../../projects/ui/src/lib/services/theme.service.ts","../../../projects/ui/src/lib/navigation/app-switcher/app-switcher.ts","../../../projects/ui/src/lib/navigation/app-switcher/app-switcher.html","../../../projects/ui/src/lib/navigation/organization-switcher/organization-switcher.ts","../../../projects/ui/src/lib/navigation/organization-switcher/organization-switcher.html","../../../projects/ui/src/lib/notifications/notification-panel/notification-panel.ts","../../../projects/ui/src/lib/notifications/notification-panel/notification-panel.html","../../../projects/ui/src/lib/notifications/notification-bell/notification-bell.ts","../../../projects/ui/src/lib/notifications/notification-bell/notification-bell.html","../../../projects/ui/src/lib/user/user-avatar/user-avatar.ts","../../../projects/ui/src/lib/user/user-avatar/user-avatar.html","../../../projects/ui/src/lib/user/user-menu/user-menu.ts","../../../projects/ui/src/lib/user/user-menu/user-menu.html","../../../projects/ui/src/lib/shared/icon/icon.component.ts","../../../projects/ui/src/lib/layout/header/header.ts","../../../projects/ui/src/lib/layout/header/header.html","../../../projects/ui/src/public-api.ts","../../../projects/ui/src/excellore-ui.ts"],"sourcesContent":["import { Injectable, signal, effect, Inject, PLATFORM_ID, inject } from '@angular/core';\r\nimport { isPlatformBrowser } from '@angular/common';\r\nimport { Theme } from '../models/models.interface';\r\n\r\nconst THEME_STORAGE_KEY = 'excellore-theme';\r\n\r\n@Injectable({\r\n providedIn: 'root',\r\n})\r\nexport class ThemeService {\r\n private readonly platformId = inject(PLATFORM_ID);\r\n private readonly isBrowser = isPlatformBrowser(this.platformId);\r\n\r\n // The active theme preference ('light', 'dark', 'system')\r\n readonly theme = signal<Theme>('system');\r\n\r\n // The currently resolved actual theme applied ('light' | 'dark')\r\n readonly resolvedTheme = signal<'light' | 'dark'>('light');\r\n\r\n private mediaQueryListener?: (e: MediaQueryListEvent) => void;\r\n\r\n constructor() {\r\n this.initializeTheme();\r\n\r\n // Effect to reactively update storage, apply document classes, and set up listeners\r\n effect(() => {\r\n const activeTheme = this.theme();\r\n this.saveThemeToStorage(activeTheme);\r\n this.applyTheme(activeTheme);\r\n });\r\n }\r\n\r\n setTheme(theme: Theme): void {\r\n this.theme.set(theme);\r\n }\r\n\r\n getTheme(): Theme {\r\n return this.theme();\r\n }\r\n\r\n toggleTheme(): void {\r\n const current = this.theme();\r\n if (current === 'light') {\r\n this.setTheme('dark');\r\n } else if (current === 'dark') {\r\n this.setTheme('system');\r\n } else {\r\n this.setTheme('light');\r\n }\r\n }\r\n\r\n private initializeTheme(): void {\r\n if (!this.isBrowser) {\r\n return;\r\n }\r\n\r\n try {\r\n const savedTheme = localStorage.getItem(THEME_STORAGE_KEY) as Theme;\r\n if (savedTheme === 'light' || savedTheme === 'dark' || savedTheme === 'system') {\r\n this.theme.set(savedTheme);\r\n } else {\r\n this.theme.set('system');\r\n }\r\n } catch {\r\n this.theme.set('system');\r\n }\r\n }\r\n\r\n private saveThemeToStorage(theme: Theme): void {\r\n if (!this.isBrowser) {\r\n return;\r\n }\r\n\r\n try {\r\n localStorage.setItem(THEME_STORAGE_KEY, theme);\r\n } catch (e) {\r\n console.warn('Failed to save theme to localStorage:', e);\r\n }\r\n }\r\n\r\n private applyTheme(theme: Theme): void {\r\n if (!this.isBrowser) {\r\n return;\r\n }\r\n\r\n const doc = document.documentElement;\r\n const mediaQuery = typeof window.matchMedia === 'function' ? window.matchMedia('(prefers-color-scheme: dark)') : null;\r\n\r\n // Clean up any previous listener to avoid leaks\r\n if (this.mediaQueryListener && mediaQuery) {\r\n mediaQuery.removeEventListener('change', this.mediaQueryListener);\r\n this.mediaQueryListener = undefined;\r\n }\r\n\r\n let actualTheme: 'light' | 'dark' = 'light';\r\n\r\n if (theme === 'system') {\r\n actualTheme = mediaQuery?.matches ? 'dark' : 'light';\r\n\r\n if (mediaQuery) {\r\n // Watch for system preference changes in real-time\r\n this.mediaQueryListener = (e: MediaQueryListEvent) => {\r\n const newResolvedTheme = e.matches ? 'dark' : 'light';\r\n this.resolvedTheme.set(newResolvedTheme);\r\n this.updateDocumentClass(newResolvedTheme);\r\n };\r\n mediaQuery.addEventListener('change', this.mediaQueryListener);\r\n }\r\n } else {\r\n actualTheme = theme;\r\n }\r\n\r\n this.resolvedTheme.set(actualTheme);\r\n this.updateDocumentClass(actualTheme);\r\n }\r\n\r\n private updateDocumentClass(resolved: 'light' | 'dark'): void {\r\n const doc = document.documentElement;\r\n if (resolved === 'dark') {\r\n doc.classList.add('dark');\r\n doc.classList.remove('light');\r\n } else {\r\n doc.classList.add('light');\r\n doc.classList.remove('dark');\r\n }\r\n }\r\n}\r\n","import { Component, input, output, HostListener, signal } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { Application } from '../../models/models.interface';\r\nimport { LucideAngularModule, Grid, ChevronDown } from 'lucide-angular';\r\n\r\n@Component({\r\n selector: 'lib-app-switcher',\r\n standalone: true,\r\n imports: [CommonModule, LucideAngularModule],\r\n templateUrl: './app-switcher.html',\r\n styleUrl: './app-switcher.css',\r\n})\r\nexport class AppSwitcherComponent {\r\n readonly applications = input<Application[]>([]);\r\n readonly applicationSelected = output<Application>();\r\n\r\n readonly gridIcon = Grid;\r\n readonly chevronDownIcon = ChevronDown;\r\n\r\n readonly isOpen = signal(false);\r\n\r\n toggleSwitcher(): void {\r\n this.isOpen.update((v) => !v);\r\n }\r\n\r\n closeSwitcher(): void {\r\n this.isOpen.set(false);\r\n }\r\n\r\n onSelect(app: Application): void {\r\n if (app.available !== false) {\r\n this.applicationSelected.emit(app);\r\n this.closeSwitcher();\r\n }\r\n }\r\n\r\n getInitials(name: string): string {\r\n return name\r\n .split(/[.\\s_-]+/)\r\n .filter(Boolean)\r\n .slice(0, 2)\r\n .map((part) => part[0]?.toUpperCase())\r\n .join('') || 'AP';\r\n }\r\n\r\n @HostListener('document:click', ['$event'])\r\n onOutsideClick(event: MouseEvent): void {\r\n const target = event.target as HTMLElement;\r\n if (!target.closest('.app-switcher-wrapper')) {\r\n this.closeSwitcher();\r\n }\r\n }\r\n}\r\n","<div class=\"app-switcher-wrapper relative inline-block text-left\">\r\n <button\n type=\"button\"\n class=\"app-switcher-trigger\"\n (click)=\"toggleSwitcher()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"Toggle application switcher\"\r\n >\r\n <lucide-icon [name]=\"gridIcon\" class=\"h-4.5 w-4.5\" />\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"app-switcher-dropdown fixed left-2 right-2 top-14 mt-2 max-h-[calc(100dvh-4.5rem)] origin-top rounded-xl border border-[var(--border)] bg-[var(--background)] p-2.5 shadow-lg focus:outline-none z-50 sm:absolute sm:left-auto sm:right-0 sm:top-auto sm:w-[360px] sm:max-w-[calc(100vw-1rem)] sm:origin-top-right\">\n <div class=\"mb-1.5 flex items-center justify-between border-b border-[var(--border)] px-1 pb-2.5\">\n <div>\n <h3 class=\"text-[10px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Excellore Apps</h3>\n <p class=\"mt-0.5 text-[11px] text-[var(--secondary)]\">Switch between available workspaces</p>\n </div>\n </div>\n \n @if (applications().length === 0) {\n <div class=\"py-6 text-center text-xs text-[var(--secondary)]\">\n No other apps available\n </div>\n } @else {\n <div class=\"max-h-[calc(100dvh-10rem)] overflow-y-auto py-1 sm:max-h-[420px]\">\n @for (app of applications(); track app.id) {\n @if (app.available !== false) {\n <a\n [href]=\"app.url\"\n (click)=\"onSelect(app)\"\n class=\"app-switcher-item app-switcher-link group flex items-center gap-3 rounded-lg border border-transparent px-2.5 py-2.5 no-underline transition focus:outline-none focus:ring-2 focus:ring-[var(--primary)]\"\n >\n <div class=\"app-switcher-app-mark\">\n {{ app.initials || getInitials(app.name) }}\n </div>\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold text-[var(--foreground)]\" [title]=\"app.name\">{{ app.name }}</div>\n <div class=\"mt-0.5 truncate text-[11px] text-[var(--secondary)]\">{{ app.url }}</div>\n </div>\n <span class=\"app-switcher-open-pill shrink-0 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide opacity-0 transition group-hover:opacity-100\">\n Open\n </span>\n </a>\n } @else {\n <div class=\"app-switcher-item flex items-center gap-3 rounded-lg px-2.5 py-2.5 opacity-55 select-none\">\n <div class=\"app-switcher-app-mark unavailable\">\n {{ app.initials || getInitials(app.name) }}\n </div>\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold text-[var(--foreground)]\">{{ app.name }}</div>\n <div class=\"mt-0.5 truncate text-[11px] text-[var(--secondary)]\">{{ app.url }}</div>\n </div>\n <span class=\"shrink-0 rounded-full border border-[var(--border)] px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-[var(--secondary)]\">\n Soon\n </span>\n </div>\n }\n }\n </div>\r\n }\r\n </div>\r\n }\r\n</div>\r\n","import { Component, input, output, HostListener, signal } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { Organization } from '../../models/models.interface';\r\nimport { LucideAngularModule, ChevronDown, Check, Building } from 'lucide-angular';\r\n\r\n@Component({\r\n selector: 'lib-organization-switcher',\r\n standalone: true,\r\n imports: [CommonModule, LucideAngularModule],\r\n templateUrl: './organization-switcher.html',\r\n styleUrl: './organization-switcher.css',\r\n})\r\nexport class OrganizationSwitcherComponent {\r\n readonly organizations = input<Organization[]>([]);\r\n readonly selectedOrganization = input<Organization | null>(null);\r\n readonly organizationChanged = output<Organization>();\r\n\r\n readonly chevronDownIcon = ChevronDown;\r\n readonly checkIcon = Check;\r\n readonly buildingIcon = Building;\r\n\r\n readonly isOpen = signal(false);\r\n\r\n toggleSwitcher(): void {\r\n this.isOpen.update((v) => !v);\r\n }\r\n\r\n closeSwitcher(): void {\r\n this.isOpen.set(false);\r\n }\r\n\r\n onSelect(org: Organization): void {\r\n this.organizationChanged.emit(org);\r\n this.closeSwitcher();\r\n }\r\n\r\n @HostListener('document:click', ['$event'])\r\n onOutsideClick(event: MouseEvent): void {\r\n const target = event.target as HTMLElement;\r\n if (!target.closest('.org-switcher-wrapper')) {\r\n this.closeSwitcher();\r\n }\r\n }\r\n}\r\n","<div class=\"org-switcher-wrapper relative inline-block text-left\">\r\n <button\r\n type=\"button\"\r\n class=\"flex items-center gap-1.5 rounded-md border border-[var(--border)] bg-[var(--background)] px-2.5 py-1.5 text-xs font-medium text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] cursor-pointer\"\r\n (click)=\"toggleSwitcher()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"Switch organization\"\r\n >\r\n <lucide-icon [name]=\"buildingIcon\" class=\"h-3.5 w-3.5 text-[var(--secondary)]\" />\r\n <span class=\"truncate max-w-[120px]\">{{ selectedOrganization()?.name || 'Select Tenant' }}</span>\r\n <lucide-icon [name]=\"chevronDownIcon\" class=\"h-3 w-3 text-[var(--secondary)] transition-transform duration-200\" [class.rotate-180]=\"isOpen()\" />\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"org-switcher-dropdown absolute left-0 mt-2 w-52 origin-top-left rounded-lg border border-[var(--border)] bg-[var(--background)] p-1 shadow-lg focus:outline-none z-50\">\r\n <div class=\"px-2 py-1.5 border-b border-[var(--border)] mb-1\">\r\n <span class=\"text-[9px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Switch Organization</span>\r\n </div>\r\n\r\n <div class=\"py-0.5 space-y-0.5 max-h-48 overflow-y-auto\">\r\n @for (org of organizations(); track org.id) {\r\n <button\r\n type=\"button\"\r\n class=\"flex w-full items-center justify-between rounded-md px-2 py-1.5 text-xs text-[var(--foreground)] transition hover:bg-[rgba(var(--foreground),0.05)] text-left cursor-pointer\"\r\n (click)=\"onSelect(org)\"\r\n >\r\n <span class=\"truncate pr-2\" [class.font-semibold]=\"org.id === selectedOrganization()?.id\">{{ org.name }}</span>\r\n @if (org.id === selectedOrganization()?.id) {\r\n <lucide-icon [name]=\"checkIcon\" class=\"h-3.5 w-3.5 text-[var(--primary)] flex-shrink-0\" />\r\n }\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n","import { Component, input, output } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { Notification } from '../../models/models.interface';\r\nimport { LucideAngularModule, CheckCheck, Trash2, BellOff, Circle } from 'lucide-angular';\r\n\r\n@Component({\r\n selector: 'lib-notification-panel',\r\n standalone: true,\r\n imports: [CommonModule, LucideAngularModule],\r\n templateUrl: './notification-panel.html',\r\n styleUrl: './notification-panel.css',\r\n})\r\nexport class NotificationPanelComponent {\r\n readonly notifications = input<Notification[]>([]);\r\n readonly notificationClick = output<Notification>();\r\n readonly markRead = output<string>();\r\n readonly clearAll = output<void>();\r\n\r\n readonly checkCheckIcon = CheckCheck;\r\n readonly trashIcon = Trash2;\r\n readonly bellOffIcon = BellOff;\r\n readonly circleIcon = Circle;\r\n\r\n onNotificationClick(notification: Notification): void {\r\n this.notificationClick.emit(notification);\r\n }\r\n\r\n onMarkRead(id: string, event: MouseEvent): void {\r\n event.stopPropagation();\r\n this.markRead.emit(id);\r\n }\r\n\r\n onClearAll(event: MouseEvent): void {\r\n event.stopPropagation();\r\n this.clearAll.emit();\r\n }\r\n\r\n formatTime(date: Date | string): string {\r\n const d = new Date(date);\r\n const now = new Date();\r\n const diffMs = now.getTime() - d.getTime();\r\n const diffMins = Math.floor(diffMs / 60000);\r\n const diffHours = Math.floor(diffMins / 60);\r\n const diffDays = Math.floor(diffHours / 24);\r\n\r\n if (diffMins < 1) return 'Just now';\r\n if (diffMins < 60) return `${diffMins}m ago`;\r\n if (diffHours < 24) return `${diffHours}h ago`;\r\n if (diffDays === 1) return 'Yesterday';\r\n return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });\r\n }\r\n}\r\n","<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 <lucide-icon [name]=\"trashIcon\" class=\"h-3 w-3\" />\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 <lucide-icon [name]=\"bellOffIcon\" class=\"h-4.5 w-4.5\" />\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 <lucide-icon [name]=\"checkCheckIcon\" class=\"h-3.5 w-3.5\" />\r\n </button>\r\n </div>\r\n }\r\n </div>\r\n }\r\n }\r\n </div>\r\n</div>\r\n","import { Component, input, output, HostListener, signal } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { Notification } from '../../models/models.interface';\r\nimport { NotificationPanelComponent } from '../notification-panel/notification-panel';\r\nimport { LucideAngularModule, Bell } from 'lucide-angular';\r\n\r\n@Component({\r\n selector: 'lib-notification-bell',\r\n standalone: true,\r\n imports: [CommonModule, NotificationPanelComponent, LucideAngularModule],\r\n templateUrl: './notification-bell.html',\r\n styleUrl: './notification-bell.css',\r\n})\r\nexport class NotificationBellComponent {\r\n readonly unreadCount = input<number>(0);\r\n readonly notifications = input<Notification[]>([]);\r\n\r\n readonly notificationClick = output<Notification>();\r\n readonly markRead = output<string>();\r\n readonly clearAll = output<void>();\r\n\r\n readonly bellIcon = Bell;\r\n\r\n readonly isOpen = signal(false);\r\n\r\n togglePanel(): void {\r\n this.isOpen.update((v) => !v);\r\n }\r\n\r\n closePanel(): void {\r\n this.isOpen.set(false);\r\n }\r\n\r\n onNotificationClick(notification: Notification): void {\r\n this.notificationClick.emit(notification);\r\n this.closePanel();\r\n }\r\n\r\n onMarkRead(id: string): void {\r\n this.markRead.emit(id);\r\n }\r\n\r\n onClearAll(): void {\r\n this.clearAll.emit();\r\n }\r\n\r\n @HostListener('document:click', ['$event'])\r\n onOutsideClick(event: MouseEvent): void {\r\n const target = event.target as HTMLElement;\r\n if (!target.closest('.notification-bell-wrapper')) {\r\n this.closePanel();\r\n }\r\n }\r\n}\r\n","<div class=\"notification-bell-wrapper relative inline-block text-left\">\r\n <button\n type=\"button\"\n class=\"notification-trigger\"\n (click)=\"togglePanel()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"View notifications\"\r\n >\r\n <lucide-icon [name]=\"bellIcon\" class=\"h-4.5 w-4.5\" />\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\">\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","import { Component, input, computed } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { User } from '../../models/models.interface';\r\n\r\n@Component({\r\n selector: 'lib-user-avatar',\r\n standalone: true,\r\n imports: [CommonModule],\r\n templateUrl: './user-avatar.html',\r\n styleUrl: './user-avatar.css',\r\n})\r\nexport class UserAvatarComponent {\r\n // Input using the modern Angular signal inputs\r\n readonly user = input<User | null>(null);\r\n\r\n // Computes the initials from the user's name or email\r\n readonly initials = computed(() => {\r\n const userData = this.user();\r\n if (!userData) return 'EU';\r\n\r\n const source = userData.name || userData.email || 'Excellore User';\r\n return source\r\n .split(/[.@\\s_-]+/)\r\n .filter(Boolean)\r\n .slice(0, 2)\r\n .map(part => part[0]?.toUpperCase())\r\n .join('') || 'EU';\r\n });\r\n\r\n // Check if image loading failed to dynamically switch to initials\r\n protected imageError = false;\r\n\r\n onImageError(): void {\r\n this.imageError = true;\r\n }\r\n}\r\n","<div class=\"avatar-container flex h-full w-full items-center justify-center rounded-full border border-[var(--border)] select-none overflow-hidden transition-all duration-200\">\r\n @if (user()?.avatar && !imageError) {\r\n <img [src]=\"user()?.avatar\" [alt]=\"user()?.name || 'User avatar'\" class=\"avatar-img h-full w-full object-cover\" (error)=\"onImageError()\" />\r\n } @else {\r\n <span class=\"avatar-initials text-xs font-semibold uppercase leading-none\">{{ initials() }}</span>\r\n }\r\n</div>\r\n","import { Component, input, output, HostListener, signal } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { User } from '../../models/models.interface';\r\nimport { UserAvatarComponent } from '../user-avatar/user-avatar';\r\nimport {\r\n LucideAngularModule,\r\n User as UserIcon,\r\n Settings as SettingsIcon,\r\n CreditCard as CreditCardIcon,\r\n LogOut as LogOutIcon,\r\n ChevronDown as ChevronDownIcon,\r\n} from 'lucide-angular';\r\n\r\n@Component({\r\n selector: 'lib-user-menu',\r\n standalone: true,\r\n imports: [CommonModule, UserAvatarComponent, LucideAngularModule],\r\n templateUrl: './user-menu.html',\r\n styleUrl: './user-menu.css',\r\n})\r\nexport class UserMenuComponent {\r\n readonly user = input<User | null>(null);\r\n readonly menuAction = output<'profile' | 'settings' | 'billing' | 'logout'>();\r\n\r\n // Lucide Icons mapping\r\n readonly userIcon = UserIcon;\r\n readonly settingsIcon = SettingsIcon;\r\n readonly creditCardIcon = CreditCardIcon;\r\n readonly logOutIcon = LogOutIcon;\r\n readonly chevronDownIcon = ChevronDownIcon;\r\n\r\n // Signalled open state\r\n readonly isOpen = signal(false);\r\n\r\n toggleMenu(): void {\r\n this.isOpen.update((v) => !v);\r\n }\r\n\r\n closeMenu(): void {\r\n this.isOpen.set(false);\r\n }\r\n\r\n onAction(action: 'profile' | 'settings' | 'billing' | 'logout'): void {\r\n this.menuAction.emit(action);\r\n this.closeMenu();\r\n }\r\n\r\n @HostListener('document:click', ['$event'])\r\n onOutsideClick(event: MouseEvent): void {\r\n const target = event.target as HTMLElement;\r\n if (!target.closest('.user-menu-wrapper')) {\r\n this.closeMenu();\r\n }\r\n }\r\n}\r\n","<div class=\"user-menu-wrapper relative inline-block text-left\">\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=\"h-7 w-7 rounded-full overflow-hidden\">\r\n <lib-user-avatar [user]=\"user()\" />\r\n </div>\r\n <lucide-icon [name]=\"chevronDownIcon\" class=\"h-3.5 w-3.5 text-[var(--secondary)] transition-transform duration-200\"\r\n [class.rotate-180]=\"isOpen()\" />\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div\r\n class=\"user-dropdown-panel fixed left-2 right-2 top-14 mt-2 origin-top rounded-xl border border-[var(--border)] bg-[var(--background)] p-1.5 shadow-lg focus:outline-none z-50 sm:absolute sm:left-auto sm:right-0 sm:top-auto sm:w-56 sm:origin-top-right\">\r\n @if (user(); as u) {\r\n <div class=\"px-3 py-2 border-b border-[var(--border)] mb-1\">\r\n <p class=\"text-sm font-semibold truncate text-[var(--foreground)]\" [title]=\"u.name\">{{ u.name }}</p>\r\n <p class=\"text-xs truncate text-[var(--secondary)] mt-0.5\" [title]=\"u.email\">{{ u.email }}</p>\r\n </div>\r\n }\r\n\r\n <div class=\"py-0.5 space-y-0.5\">\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('profile')\">\r\n <lucide-icon [name]=\"userIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" />\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 <lucide-icon [name]=\"settingsIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" />\r\n <span>Settings</span>\r\n </button>\r\n\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('billing')\">\r\n <lucide-icon [name]=\"creditCardIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" />\r\n <span>Billing</span>\r\n </button>\r\n </div>\r\n\r\n <div class=\"border-t border-[var(--border)] mt-1 pt-1\">\r\n <button type=\"button\" class=\"user-menu-item danger\" (click)=\"onAction('logout')\">\r\n <lucide-icon [name]=\"logOutIcon\" class=\"h-4 w-4 text-[var(--danger,#ef4444)]\" />\r\n <span>Sign Out</span>\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n</div>","// lib/shared/icon/icon.component.ts\r\nimport { Component, input, computed } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport {\r\n LucideAngularModule,\r\n Search,\r\n Menu,\r\n X,\r\n Sun,\r\n Moon,\r\n Monitor,\r\n Bell,\r\n Grid,\r\n ChevronDown,\r\n Building2\r\n} from 'lucide-angular';\r\n\r\nconst ICONS = {\r\n search: Search,\r\n menu: Menu,\r\n x: X,\r\n sun: Sun,\r\n moon: Moon,\r\n monitor: Monitor,\r\n bell: Bell,\r\n grid: Grid,\r\n chevronDown: ChevronDown,\r\n building2: Building2\r\n};\r\n\r\n@Component({\r\n selector: 'lib-icon',\r\n standalone: true,\r\n imports: [CommonModule, LucideAngularModule],\r\n template: `\r\n <lucide-icon \r\n [name]=\"iconName()\" \r\n [size]=\"size()\"\r\n [color]=\"color()\"\r\n [strokeWidth]=\"strokeWidth()\"\r\n class=\"lib-icon\"\r\n />\r\n `,\r\n styles: [`\r\n :host {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n }\r\n .lib-icon {\r\n display: block;\r\n }\r\n `]\r\n})\r\nexport class IconComponent {\r\n readonly name = input.required<string>();\r\n readonly size = input<number>(24);\r\n readonly color = input<string | undefined>(undefined);\r\n readonly strokeWidth = input<number | undefined>(undefined);\r\n\r\n protected readonly iconName = computed(() => {\r\n const name = this.name();\r\n return ICONS[name as keyof typeof ICONS] || Search;\r\n });\r\n}","import { Component, inject, input, output, signal, computed } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { Router, RouterLink, RouterLinkActive } from '@angular/router';\r\nimport { User, Organization, NavigationItem, Notification, Application } from '../../models/models.interface';\r\nimport { ThemeService } from '../../services/theme.service';\r\nimport { AppSwitcherComponent } from '../../navigation';\r\nimport { NotificationBellComponent } from '../../notifications';\r\nimport { UserMenuComponent } from '../../user';\r\n\r\nimport { IconComponent } from '../../shared/icon/icon.component';\r\n\r\n\r\n@Component({\r\n selector: 'lib-header',\r\n standalone: true,\r\n imports: [\r\n CommonModule,\r\n RouterLink,\r\n RouterLinkActive,\r\n AppSwitcherComponent,\r\n NotificationBellComponent,\r\n UserMenuComponent,\r\n IconComponent,\r\n ],\r\n templateUrl: './header.html',\r\n styleUrl: './header.css',\r\n})\r\nexport class HeaderComponent {\r\n private readonly router = inject(Router);\r\n protected readonly themeService = inject(ThemeService);\r\n\r\n // User & Organization Inputs\r\n readonly user = input<User | null>(null);\r\n readonly permissions = input<string[]>([]);\r\n readonly organization = input<Organization | null>(null);\r\n\r\n // Navigation & Integration Inputs\r\n readonly navigationItems = input<NavigationItem[]>([]);\r\n readonly applications = input<Application[]>([]);\r\n readonly notifications = input<Notification[]>([]);\r\n readonly notificationCount = input<number>(0);\r\n readonly logoUrl = input<string>('https://cdn.jsdelivr.net/gh/Excellore/excellore-static-assets@main/images/excellore-logo-origin.png');\n readonly appName = input<string>('Excellore');\n readonly currentAppName = input<string>('');\n readonly showSearch = input<boolean>(true);\n readonly showNotifications = input<boolean>(true);\n readonly showAppSwitcher = input<boolean>(true);\n readonly showThemeSwitcher = input<boolean>(true);\n readonly showSignIn = input<boolean>(true);\n\n // Outputs as decided in the architecture plan\n readonly search = output<string>();\n readonly signIn = output<void>();\n readonly applicationSelected = output<Application>();\n readonly notificationClick = output<Notification>();\r\n readonly markRead = output<string>();\r\n readonly clearAll = output<void>();\r\n readonly menuAction = output<'profile' | 'settings' | 'billing' | 'logout'>();\r\n\r\n // Lucide Icons\r\n // readonly searchIcon = Search;\r\n // readonly menuIcon = Menu;\r\n // readonly xIcon = X;\r\n // readonly sunIcon = Sun;\r\n // readonly moonIcon = Moon;\r\n // readonly monitorIcon = Monitor;\r\n\r\n // Header specific UI states\r\n readonly isMobileMenuOpen = signal(false);\r\n\r\n // Checks if the user has permission to see an item\r\n hasPermission(item: NavigationItem): boolean {\r\n if (!item.permissions || item.permissions.length === 0) {\r\n return true;\r\n }\r\n const currentPermissions = this.permissions();\r\n return item.permissions.some((p) => currentPermissions.includes(p));\r\n }\r\n\r\n // Reactive unread count selector\r\n readonly unreadCount = computed(() => {\n const overrideCount = this.notificationCount();\n if (overrideCount > 0) return overrideCount;\n return this.notifications().filter((n) => !n.read).length;\n });\n\n readonly hasMobileMenuContent = computed(() =>\n this.showSearch() || this.navigationItems().some((item) => this.hasPermission(item))\n );\n\r\n // Determines if an item's internal route is active\r\n isActive(item: NavigationItem): boolean {\r\n if (!item.route) return false;\r\n try {\r\n return this.router.isActive(item.route, {\r\n paths: 'subset',\r\n queryParams: 'ignored',\r\n fragment: 'ignored',\r\n matrixParams: 'ignored',\r\n });\r\n } catch {\r\n return false;\r\n }\r\n }\r\n\r\n toggleMobileMenu(): void {\r\n this.isMobileMenuOpen.update((v) => !v);\r\n }\r\n\r\n closeMobileMenu(): void {\r\n this.isMobileMenuOpen.set(false);\r\n }\r\n\r\n onSearch(query: string): void {\r\n this.search.emit(query);\r\n }\r\n\r\n onApplicationSelect(app: Application): void {\r\n this.applicationSelected.emit(app);\r\n }\r\n\r\n onNotificationClick(notification: Notification): void {\r\n this.notificationClick.emit(notification);\r\n }\r\n\r\n onMarkRead(id: string): void {\r\n this.markRead.emit(id);\r\n }\r\n\r\n onClearAll(): void {\r\n this.clearAll.emit();\r\n }\r\n\r\n onMenuAction(action: 'profile' | 'settings' | 'billing' | 'logout'): void {\r\n this.menuAction.emit(action);\r\n }\r\n}\r\n","<header\n class=\"header-container fixed top-0 inset-x-0 z-40 flex h-[3.75rem] items-center justify-between px-3 text-[var(--foreground)] sm:px-4 lg:px-6\">\n\n <!-- Left Side: Brand, Current App & Desktop Links -->\n <div class=\"flex min-w-0 items-center gap-3\">\n\n <a routerLink=\"/\" class=\"brand-identity\">\n <img [src]=\"logoUrl()\" [alt]=\"appName() + ' logo'\" width=\"26\" height=\"26\" class=\"brand-logo\" />\n <span class=\"brand-name\">{{ appName() }}</span>\n </a>\n\n @if (currentAppName()) {\n <div class=\"current-app-identity\" [attr.aria-label]=\"'Current app: ' + currentAppName()\">\n <span class=\"brand-separator\" aria-hidden=\"true\">|</span>\n <span class=\"current-app-name\">{{ currentAppName() }}</span>\n </div>\n }\n\n <!-- Desktop Navigation Links -->\n <nav class=\"desktop-nav ml-1 hidden items-center lg:flex\" aria-label=\"Desktop primary navigation\">\n @for (item of navigationItems(); track item.id) {\n @if (hasPermission(item)) {\n @if (item.route) {\n <a [routerLink]=\"item.route\" routerLinkActive=\"active\" [routerLinkActiveOptions]=\"{exact: true}\" class=\"nav-link\"\n [attr.aria-current]=\"isActive(item) ? 'page' : null\">\n {{ item.label }}\n </a>\n } @else {\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" class=\"nav-link\">\n {{ item.label }}\n </a>\n }\n }\n }\n </nav>\n </div>\n\n <!-- Middle: Search Input (Desktop) -->\n @if (showSearch()) {\n <div class=\"hidden flex-1 justify-center px-4 md:flex\">\n <div class=\"header-search-shell\">\n <lib-icon name=\"search\" [size]=\"14\"\n class=\"pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-[var(--secondary)]\" />\n\n <input type=\"search\" placeholder=\"Search Excellore...\" #searchInput\n (keyup.enter)=\"onSearch(searchInput.value); searchInput.value = ''\" class=\"header-search\" autocomplete=\"off\"\n aria-label=\"Search\" />\n </div>\n </div>\n } @else {\n <div class=\"hidden flex-1 md:block\"></div>\n }\n\n <!-- Right Side Actions -->\n <div class=\"header-actions\">\n\n <!-- Mobile Search Trigger -->\n @if (showSearch()) {\n <button type=\"button\" class=\"header-action mobile-search-action\" (click)=\"toggleMobileMenu()\"\n aria-label=\"Open mobile menu\" aria-expanded=\"isMobileMenuOpen()\" #menuToggle>\n <lib-icon name=\"search\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\n </button>\n }\n\n <!-- Theme Switcher -->\n @if (showThemeSwitcher()) {\n <button type=\"button\" class=\"header-action\" (click)=\"themeService.toggleTheme()\" aria-label=\"Toggle theme\">\n @switch (themeService.theme()) {\n @case ('light') {\n <lib-icon name=\"sun\" [size]=\"16\" class=\"text-[var(--primary)]\" />\n }\n @case ('dark') {\n <lib-icon name=\"moon\" [size]=\"16\" class=\"text-[var(--primary)]\" />\n }\n @default {\n <lib-icon name=\"monitor\" [size]=\"16\" class=\"text-[var(--secondary)]\" />\n }\n }\n </button>\n }\n\n <!-- App Switcher -->\n @if (showAppSwitcher() && applications().length > 0) {\n <lib-app-switcher [applications]=\"applications()\" (applicationSelected)=\"onApplicationSelect($event)\" />\n }\n\n <!-- Notifications -->\n @if (showNotifications()) {\n <lib-notification-bell [unreadCount]=\"unreadCount()\" [notifications]=\"notifications()\"\n (notificationClick)=\"onNotificationClick($event)\" (markRead)=\"onMarkRead($event)\" (clearAll)=\"onClearAll()\" />\n }\n\n <!-- User Menu -->\n @if (user()) {\n <div class=\"user-menu-divider\">\n <lib-user-menu [user]=\"user()\" (menuAction)=\"onMenuAction($event)\" />\n </div>\n } @else if (showSignIn()) {\n <button type=\"button\" class=\"sign-in-button\" (click)=\"signIn.emit()\">\n Sign in\n </button>\n }\n\n <!-- Mobile Menu Toggle (Hamburger) -->\n @if (hasMobileMenuContent()) {\n <button type=\"button\" class=\"header-action mobile-menu-action\" (click)=\"toggleMobileMenu()\"\n aria-label=\"Toggle navigation menu\" aria-expanded=\"isMobileMenuOpen()\" #mobileMenuToggle>\n <lib-icon [name]=\"isMobileMenuOpen() ? 'x' : 'menu'\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\n </button>\n }\n </div>\n</header>\n\n<!-- Mobile Menu Backdrop -->\n@if (isMobileMenuOpen() && hasMobileMenuContent()) {\n<div class=\"mobile-backdrop fixed inset-0 z-30 lg:hidden\" (click)=\"closeMobileMenu()\"\n (keydown.escape)=\"closeMobileMenu()\" tabindex=\"-1\" aria-hidden=\"true\"></div>\n\n<!-- Mobile Navigation Drawer -->\n<div\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\"\n role=\"dialog\" aria-modal=\"true\" aria-label=\"Mobile navigation\" (keydown.escape)=\"closeMobileMenu()\">\n <!-- Mobile Search -->\n @if (showSearch()) {\n <div class=\"relative mb-3 w-full\">\n <lib-icon name=\"search\" [size]=\"16\"\n class=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[var(--secondary)] pointer-events-none\"\n aria-hidden=\"true\" />\n\n <input type=\"search\" placeholder=\"Search Excellore...\" #mobileSearchInput\n (keyup.enter)=\"onSearch(mobileSearchInput.value); mobileSearchInput.value = ''; closeMobileMenu()\"\n class=\"header-search mobile\" autocomplete=\"off\" aria-label=\"Search\" />\n </div>\n }\n\n <!-- Mobile Nav Links -->\n <nav class=\"flex flex-col gap-1 overflow-y-auto\" role=\"navigation\">\n @for (item of navigationItems(); track item.id) {\n @if (hasPermission(item)) {\n @if (item.route) {\n <a [routerLink]=\"item.route\" routerLinkActive=\"mobile-active\" [routerLinkActiveOptions]=\"{exact: true}\"\n (click)=\"closeMobileMenu()\" class=\"mobile-nav-link\" [attr.aria-current]=\"isActive(item) ? 'page' : null\">\n {{ item.label }}\n </a>\n } @else {\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" (click)=\"closeMobileMenu()\" class=\"mobile-nav-link\">\n {{ item.label }}\n </a>\n }\n }\n }\n </nav>\n</div>\n}\n","/*\r\n * Public API Surface of ui\r\n */\r\n\r\nexport * from './lib/models';\r\nexport * from './lib/services';\r\nexport * from './lib/layout';\r\nexport * from './lib/navigation';\r\nexport * from './lib/user';\r\nexport * from './lib/notifications';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["UserIcon","SettingsIcon","CreditCardIcon","LogOutIcon","ChevronDownIcon"],"mappings":"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"}
1
+ {"version":3,"file":"excellore-ui.mjs","sources":["../../../projects/ui/src/lib/services/theme.service.ts","../../../projects/ui/src/lib/navigation/app-switcher/app-switcher.ts","../../../projects/ui/src/lib/navigation/app-switcher/app-switcher.html","../../../projects/ui/src/lib/navigation/organization-switcher/organization-switcher.ts","../../../projects/ui/src/lib/navigation/organization-switcher/organization-switcher.html","../../../projects/ui/src/lib/notifications/notification-panel/notification-panel.ts","../../../projects/ui/src/lib/notifications/notification-panel/notification-panel.html","../../../projects/ui/src/lib/notifications/notification-bell/notification-bell.ts","../../../projects/ui/src/lib/notifications/notification-bell/notification-bell.html","../../../projects/ui/src/lib/user/user-avatar/user-avatar.ts","../../../projects/ui/src/lib/user/user-avatar/user-avatar.html","../../../projects/ui/src/lib/user/user-menu/user-menu.ts","../../../projects/ui/src/lib/user/user-menu/user-menu.html","../../../projects/ui/src/lib/shared/icon/icon.component.ts","../../../projects/ui/src/lib/layout/header/header.ts","../../../projects/ui/src/lib/layout/header/header.html","../../../projects/ui/src/public-api.ts","../../../projects/ui/src/excellore-ui.ts"],"sourcesContent":["import { Injectable, signal, effect, Inject, PLATFORM_ID, inject } from '@angular/core';\r\nimport { isPlatformBrowser } from '@angular/common';\r\nimport { Theme } from '../models/models.interface';\r\n\r\nconst THEME_STORAGE_KEY = 'excellore-theme';\r\n\r\n@Injectable({\r\n providedIn: 'root',\r\n})\r\nexport class ThemeService {\r\n private readonly platformId = inject(PLATFORM_ID);\r\n private readonly isBrowser = isPlatformBrowser(this.platformId);\r\n\r\n // The active theme preference ('light', 'dark', 'system')\r\n readonly theme = signal<Theme>('system');\r\n\r\n // The currently resolved actual theme applied ('light' | 'dark')\r\n readonly resolvedTheme = signal<'light' | 'dark'>('light');\r\n\r\n private mediaQueryListener?: (e: MediaQueryListEvent) => void;\r\n\r\n constructor() {\r\n this.initializeTheme();\r\n\r\n // Effect to reactively update storage, apply document classes, and set up listeners\r\n effect(() => {\r\n const activeTheme = this.theme();\r\n this.saveThemeToStorage(activeTheme);\r\n this.applyTheme(activeTheme);\r\n });\r\n }\r\n\r\n setTheme(theme: Theme): void {\r\n this.theme.set(theme);\r\n }\r\n\r\n getTheme(): Theme {\r\n return this.theme();\r\n }\r\n\r\n toggleTheme(): void {\r\n const current = this.theme();\r\n if (current === 'light') {\r\n this.setTheme('dark');\r\n } else if (current === 'dark') {\r\n this.setTheme('system');\r\n } else {\r\n this.setTheme('light');\r\n }\r\n }\r\n\r\n private initializeTheme(): void {\r\n if (!this.isBrowser) {\r\n return;\r\n }\r\n\r\n try {\r\n const savedTheme = localStorage.getItem(THEME_STORAGE_KEY) as Theme;\r\n if (savedTheme === 'light' || savedTheme === 'dark' || savedTheme === 'system') {\r\n this.theme.set(savedTheme);\r\n } else {\r\n this.theme.set('system');\r\n }\r\n } catch {\r\n this.theme.set('system');\r\n }\r\n }\r\n\r\n private saveThemeToStorage(theme: Theme): void {\r\n if (!this.isBrowser) {\r\n return;\r\n }\r\n\r\n try {\r\n localStorage.setItem(THEME_STORAGE_KEY, theme);\r\n } catch (e) {\r\n console.warn('Failed to save theme to localStorage:', e);\r\n }\r\n }\r\n\r\n private applyTheme(theme: Theme): void {\r\n if (!this.isBrowser) {\r\n return;\r\n }\r\n\r\n const doc = document.documentElement;\r\n const mediaQuery = typeof window.matchMedia === 'function' ? window.matchMedia('(prefers-color-scheme: dark)') : null;\r\n\r\n // Clean up any previous listener to avoid leaks\r\n if (this.mediaQueryListener && mediaQuery) {\r\n mediaQuery.removeEventListener('change', this.mediaQueryListener);\r\n this.mediaQueryListener = undefined;\r\n }\r\n\r\n let actualTheme: 'light' | 'dark' = 'light';\r\n\r\n if (theme === 'system') {\r\n actualTheme = mediaQuery?.matches ? 'dark' : 'light';\r\n\r\n if (mediaQuery) {\r\n // Watch for system preference changes in real-time\r\n this.mediaQueryListener = (e: MediaQueryListEvent) => {\r\n const newResolvedTheme = e.matches ? 'dark' : 'light';\r\n this.resolvedTheme.set(newResolvedTheme);\r\n this.updateDocumentClass(newResolvedTheme);\r\n };\r\n mediaQuery.addEventListener('change', this.mediaQueryListener);\r\n }\r\n } else {\r\n actualTheme = theme;\r\n }\r\n\r\n this.resolvedTheme.set(actualTheme);\r\n this.updateDocumentClass(actualTheme);\r\n }\r\n\r\n private updateDocumentClass(resolved: 'light' | 'dark'): void {\r\n const doc = document.documentElement;\r\n if (resolved === 'dark') {\r\n doc.classList.add('dark');\r\n doc.classList.remove('light');\r\n } else {\r\n doc.classList.add('light');\r\n doc.classList.remove('dark');\r\n }\r\n }\r\n}\r\n","import { Component, input, output, HostListener, signal } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { Application } from '../../models/models.interface';\nimport { LucideGrid } from '@lucide/angular';\n\r\n@Component({\r\n selector: 'lib-app-switcher',\n standalone: true,\n imports: [CommonModule, LucideGrid],\n templateUrl: './app-switcher.html',\n styleUrl: './app-switcher.css',\n})\nexport class AppSwitcherComponent {\n readonly applications = input<Application[]>([]);\n readonly applicationSelected = output<Application>();\n\n readonly isOpen = signal(false);\n\r\n toggleSwitcher(): void {\r\n this.isOpen.update((v) => !v);\r\n }\r\n\r\n closeSwitcher(): void {\r\n this.isOpen.set(false);\r\n }\r\n\r\n onSelect(app: Application): void {\r\n if (app.available !== false) {\r\n this.applicationSelected.emit(app);\r\n this.closeSwitcher();\r\n }\r\n }\r\n\r\n getInitials(name: string): string {\r\n return name\r\n .split(/[.\\s_-]+/)\r\n .filter(Boolean)\r\n .slice(0, 2)\r\n .map((part) => part[0]?.toUpperCase())\r\n .join('') || 'AP';\r\n }\r\n\r\n @HostListener('document:click', ['$event'])\r\n onOutsideClick(event: MouseEvent): void {\r\n const target = event.target as HTMLElement;\r\n if (!target.closest('.app-switcher-wrapper')) {\r\n this.closeSwitcher();\r\n }\r\n }\r\n}\r\n","<div class=\"app-switcher-wrapper relative inline-block text-left\">\r\n <button\n type=\"button\"\n class=\"app-switcher-trigger\"\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=\"h-4.5 w-4.5\"></svg>\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"app-switcher-dropdown fixed left-2 right-2 top-14 mt-2 max-h-[calc(100dvh-4.5rem)] origin-top rounded-xl border border-[var(--border)] bg-[var(--background)] p-2.5 shadow-lg focus:outline-none z-50 sm:absolute sm:left-auto sm:right-0 sm:top-auto sm:w-[360px] sm:max-w-[calc(100vw-1rem)] sm:origin-top-right\">\n <div class=\"mb-1.5 flex items-center justify-between border-b border-[var(--border)] px-1 pb-2.5\">\n <div>\n <h3 class=\"text-[10px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Excellore Apps</h3>\n <p class=\"mt-0.5 text-[11px] text-[var(--secondary)]\">Switch between available workspaces</p>\n </div>\n </div>\n \n @if (applications().length === 0) {\n <div class=\"py-6 text-center text-xs text-[var(--secondary)]\">\n No other apps available\n </div>\n } @else {\n <div class=\"max-h-[calc(100dvh-10rem)] overflow-y-auto py-1 sm:max-h-[420px]\">\n @for (app of applications(); track app.id) {\n @if (app.available !== false) {\n <a\n [href]=\"app.url\"\n (click)=\"onSelect(app)\"\n class=\"app-switcher-item app-switcher-link group flex items-center gap-3 rounded-lg border border-transparent px-2.5 py-2.5 no-underline transition focus:outline-none focus:ring-2 focus:ring-[var(--primary)]\"\n >\n <div class=\"app-switcher-app-mark\">\n {{ app.initials || getInitials(app.name) }}\n </div>\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold text-[var(--foreground)]\" [title]=\"app.name\">{{ app.name }}</div>\n <div class=\"mt-0.5 truncate text-[11px] text-[var(--secondary)]\">{{ app.url }}</div>\n </div>\n <span class=\"app-switcher-open-pill shrink-0 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide opacity-0 transition group-hover:opacity-100\">\n Open\n </span>\n </a>\n } @else {\n <div class=\"app-switcher-item flex items-center gap-3 rounded-lg px-2.5 py-2.5 opacity-55 select-none\">\n <div class=\"app-switcher-app-mark unavailable\">\n {{ app.initials || getInitials(app.name) }}\n </div>\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold text-[var(--foreground)]\">{{ app.name }}</div>\n <div class=\"mt-0.5 truncate text-[11px] text-[var(--secondary)]\">{{ app.url }}</div>\n </div>\n <span class=\"shrink-0 rounded-full border border-[var(--border)] px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-[var(--secondary)]\">\n Soon\n </span>\n </div>\n }\n }\n </div>\r\n }\r\n </div>\r\n }\r\n</div>\r\n","import { Component, input, output, HostListener, signal } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { Organization } from '../../models/models.interface';\nimport { LucideBuilding, LucideCheck, LucideChevronDown } from '@lucide/angular';\n\r\n@Component({\r\n selector: 'lib-organization-switcher',\n standalone: true,\n imports: [CommonModule, LucideBuilding, LucideCheck, LucideChevronDown],\n templateUrl: './organization-switcher.html',\n styleUrl: './organization-switcher.css',\n})\nexport class OrganizationSwitcherComponent {\r\n readonly organizations = input<Organization[]>([]);\r\n readonly selectedOrganization = input<Organization | null>(null);\n readonly organizationChanged = output<Organization>();\n\n readonly isOpen = signal(false);\n\r\n toggleSwitcher(): void {\r\n this.isOpen.update((v) => !v);\r\n }\r\n\r\n closeSwitcher(): void {\r\n this.isOpen.set(false);\r\n }\r\n\r\n onSelect(org: Organization): void {\r\n this.organizationChanged.emit(org);\r\n this.closeSwitcher();\r\n }\r\n\r\n @HostListener('document:click', ['$event'])\r\n onOutsideClick(event: MouseEvent): void {\r\n const target = event.target as HTMLElement;\r\n if (!target.closest('.org-switcher-wrapper')) {\r\n this.closeSwitcher();\r\n }\r\n }\r\n}\r\n","<div class=\"org-switcher-wrapper relative inline-block text-left\">\r\n <button\r\n type=\"button\"\r\n class=\"flex items-center gap-1.5 rounded-md border border-[var(--border)] bg-[var(--background)] px-2.5 py-1.5 text-xs font-medium text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] cursor-pointer\"\r\n (click)=\"toggleSwitcher()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"Switch organization\"\r\n >\r\n <svg lucideBuilding class=\"h-3.5 w-3.5 text-[var(--secondary)]\"></svg>\n <span class=\"truncate max-w-[120px]\">{{ selectedOrganization()?.name || 'Select Tenant' }}</span>\n <svg lucideChevronDown class=\"h-3 w-3 text-[var(--secondary)] transition-transform duration-200\" [class.rotate-180]=\"isOpen()\"></svg>\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"org-switcher-dropdown absolute left-0 mt-2 w-52 origin-top-left rounded-lg border border-[var(--border)] bg-[var(--background)] p-1 shadow-lg focus:outline-none z-50\">\r\n <div class=\"px-2 py-1.5 border-b border-[var(--border)] mb-1\">\r\n <span class=\"text-[9px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Switch Organization</span>\r\n </div>\r\n\r\n <div class=\"py-0.5 space-y-0.5 max-h-48 overflow-y-auto\">\r\n @for (org of organizations(); track org.id) {\r\n <button\r\n type=\"button\"\r\n class=\"flex w-full items-center justify-between rounded-md px-2 py-1.5 text-xs text-[var(--foreground)] transition hover:bg-[rgba(var(--foreground),0.05)] text-left cursor-pointer\"\r\n (click)=\"onSelect(org)\"\r\n >\r\n <span class=\"truncate pr-2\" [class.font-semibold]=\"org.id === selectedOrganization()?.id\">{{ org.name }}</span>\r\n @if (org.id === selectedOrganization()?.id) {\r\n <svg lucideCheck class=\"h-3.5 w-3.5 text-[var(--primary)] flex-shrink-0\"></svg>\n }\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n","import { Component, input, output } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { Notification } from '../../models/models.interface';\nimport { LucideBellOff, LucideCheckCheck, LucideTrash2 } from '@lucide/angular';\n\r\n@Component({\r\n selector: 'lib-notification-panel',\n standalone: true,\n imports: [CommonModule, LucideBellOff, LucideCheckCheck, LucideTrash2],\n templateUrl: './notification-panel.html',\n styleUrl: './notification-panel.css',\n})\nexport class NotificationPanelComponent {\r\n readonly notifications = input<Notification[]>([]);\r\n readonly notificationClick = output<Notification>();\r\n readonly markRead = output<string>();\n readonly clearAll = output<void>();\n\n onNotificationClick(notification: Notification): void {\n this.notificationClick.emit(notification);\r\n }\r\n\r\n onMarkRead(id: string, event: MouseEvent): void {\r\n event.stopPropagation();\r\n this.markRead.emit(id);\r\n }\r\n\r\n onClearAll(event: MouseEvent): void {\r\n event.stopPropagation();\r\n this.clearAll.emit();\r\n }\r\n\r\n formatTime(date: Date | string): string {\r\n const d = new Date(date);\r\n const now = new Date();\r\n const diffMs = now.getTime() - d.getTime();\r\n const diffMins = Math.floor(diffMs / 60000);\r\n const diffHours = Math.floor(diffMins / 60);\r\n const diffDays = Math.floor(diffHours / 24);\r\n\r\n if (diffMins < 1) return 'Just now';\r\n if (diffMins < 60) return `${diffMins}m ago`;\r\n if (diffHours < 24) return `${diffHours}h ago`;\r\n if (diffDays === 1) return 'Yesterday';\r\n return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });\r\n }\r\n}\r\n","<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>\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>\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>\n </button>\r\n </div>\r\n }\r\n </div>\r\n }\r\n }\r\n </div>\r\n</div>\r\n","import { Component, input, output, HostListener, signal } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { Notification } from '../../models/models.interface';\nimport { NotificationPanelComponent } from '../notification-panel/notification-panel';\nimport { LucideBell } from '@lucide/angular';\n\r\n@Component({\r\n selector: 'lib-notification-bell',\n standalone: true,\n imports: [CommonModule, NotificationPanelComponent, LucideBell],\n templateUrl: './notification-bell.html',\n styleUrl: './notification-bell.css',\n})\nexport class NotificationBellComponent {\r\n readonly unreadCount = input<number>(0);\r\n readonly notifications = input<Notification[]>([]);\r\n\r\n readonly notificationClick = output<Notification>();\r\n readonly markRead = output<string>();\n readonly clearAll = output<void>();\n\n readonly isOpen = signal(false);\n\r\n togglePanel(): void {\r\n this.isOpen.update((v) => !v);\r\n }\r\n\r\n closePanel(): void {\r\n this.isOpen.set(false);\r\n }\r\n\r\n onNotificationClick(notification: Notification): void {\r\n this.notificationClick.emit(notification);\r\n this.closePanel();\r\n }\r\n\r\n onMarkRead(id: string): void {\r\n this.markRead.emit(id);\r\n }\r\n\r\n onClearAll(): void {\r\n this.clearAll.emit();\r\n }\r\n\r\n @HostListener('document:click', ['$event'])\r\n onOutsideClick(event: MouseEvent): void {\r\n const target = event.target as HTMLElement;\r\n if (!target.closest('.notification-bell-wrapper')) {\r\n this.closePanel();\r\n }\r\n }\r\n}\r\n","<div class=\"notification-bell-wrapper relative inline-block text-left\">\r\n <button\n type=\"button\"\n class=\"notification-trigger\"\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>\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\">\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","import { Component, input, computed } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { User } from '../../models/models.interface';\r\n\r\n@Component({\r\n selector: 'lib-user-avatar',\r\n standalone: true,\r\n imports: [CommonModule],\r\n templateUrl: './user-avatar.html',\r\n styleUrl: './user-avatar.css',\r\n})\r\nexport class UserAvatarComponent {\r\n // Input using the modern Angular signal inputs\r\n readonly user = input<User | null>(null);\r\n\r\n // Computes the initials from the user's name or email\r\n readonly initials = computed(() => {\r\n const userData = this.user();\r\n if (!userData) return 'EU';\r\n\r\n const source = userData.name || userData.email || 'Excellore User';\r\n return source\r\n .split(/[.@\\s_-]+/)\r\n .filter(Boolean)\r\n .slice(0, 2)\r\n .map(part => part[0]?.toUpperCase())\r\n .join('') || 'EU';\r\n });\r\n\r\n // Check if image loading failed to dynamically switch to initials\r\n protected imageError = false;\r\n\r\n onImageError(): void {\r\n this.imageError = true;\r\n }\r\n}\r\n","<div class=\"avatar-container flex h-full w-full items-center justify-center rounded-full border border-[var(--border)] select-none overflow-hidden transition-all duration-200\">\r\n @if (user()?.avatar && !imageError) {\r\n <img [src]=\"user()?.avatar\" [alt]=\"user()?.name || 'User avatar'\" class=\"avatar-img h-full w-full object-cover\" (error)=\"onImageError()\" />\r\n } @else {\r\n <span class=\"avatar-initials text-xs font-semibold uppercase leading-none\">{{ initials() }}</span>\r\n }\r\n</div>\r\n","import { Component, input, output, HostListener, signal } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\nimport { User } from '../../models/models.interface';\nimport { UserAvatarComponent } from '../user-avatar/user-avatar';\nimport {\n LucideChevronDown,\n LucideCreditCard,\n LucideLogOut,\n LucideSettings,\n LucideUser,\n} from '@lucide/angular';\n\r\n@Component({\r\n selector: 'lib-user-menu',\n standalone: true,\n imports: [CommonModule, UserAvatarComponent, LucideChevronDown, LucideCreditCard, LucideLogOut, LucideSettings, LucideUser],\n templateUrl: './user-menu.html',\n styleUrl: './user-menu.css',\n})\nexport class UserMenuComponent {\r\n readonly user = input<User | null>(null);\n readonly menuAction = output<'profile' | 'settings' | 'billing' | 'logout'>();\n\n // Signalled open state\n readonly isOpen = signal(false);\n\r\n toggleMenu(): void {\r\n this.isOpen.update((v) => !v);\r\n }\r\n\r\n closeMenu(): void {\r\n this.isOpen.set(false);\r\n }\r\n\r\n onAction(action: 'profile' | 'settings' | 'billing' | 'logout'): void {\r\n this.menuAction.emit(action);\r\n this.closeMenu();\r\n }\r\n\r\n @HostListener('document:click', ['$event'])\r\n onOutsideClick(event: MouseEvent): void {\r\n const target = event.target as HTMLElement;\r\n if (!target.closest('.user-menu-wrapper')) {\r\n this.closeMenu();\r\n }\r\n }\r\n}\r\n","<div class=\"user-menu-wrapper relative inline-block text-left\">\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=\"h-7 w-7 rounded-full overflow-hidden\">\r\n <lib-user-avatar [user]=\"user()\" />\r\n </div>\r\n <svg lucideChevronDown class=\"h-3.5 w-3.5 text-[var(--secondary)] transition-transform duration-200\"\n [class.rotate-180]=\"isOpen()\"></svg>\n </button>\r\n\r\n @if (isOpen()) {\r\n <div\r\n class=\"user-dropdown-panel fixed left-2 right-2 top-14 mt-2 origin-top rounded-xl border border-[var(--border)] bg-[var(--background)] p-1.5 shadow-lg focus:outline-none z-50 sm:absolute sm:left-auto sm:right-0 sm:top-auto sm:w-56 sm:origin-top-right\">\r\n @if (user(); as u) {\r\n <div class=\"px-3 py-2 border-b border-[var(--border)] mb-1\">\r\n <p class=\"text-sm font-semibold truncate text-[var(--foreground)]\" [title]=\"u.name\">{{ u.name }}</p>\r\n <p class=\"text-xs truncate text-[var(--secondary)] mt-0.5\" [title]=\"u.email\">{{ u.email }}</p>\r\n </div>\r\n }\r\n\r\n <div class=\"py-0.5 space-y-0.5\">\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('profile')\">\r\n <svg lucideUser class=\"h-4 w-4 text-[var(--secondary)]\"></svg>\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=\"h-4 w-4 text-[var(--secondary)]\"></svg>\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=\"h-4 w-4 text-[var(--secondary)]\"></svg>\n <span>Billing</span>\r\n </button>\r\n </div>\r\n\r\n <div class=\"border-t border-[var(--border)] mt-1 pt-1\">\r\n <button type=\"button\" class=\"user-menu-item danger\" (click)=\"onAction('logout')\">\r\n <svg lucideLogOut class=\"h-4 w-4 text-[var(--danger,#ef4444)]\"></svg>\n <span>Sign Out</span>\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n</div>\n","// lib/shared/icon/icon.component.ts\nimport { Component, input } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport {\n LucideBell,\n LucideBuilding2,\n LucideChevronDown,\n LucideGrid,\n LucideMenu,\n LucideMonitor,\n LucideMoon,\n LucideSearch,\n LucideSun,\n LucideX,\n} from '@lucide/angular';\n\n@Component({\n selector: 'lib-icon',\n standalone: true,\n imports: [\n CommonModule,\n LucideBell,\n LucideBuilding2,\n LucideChevronDown,\n LucideGrid,\n LucideMenu,\n LucideMonitor,\n LucideMoon,\n LucideSearch,\n LucideSun,\n LucideX,\n ],\n template: `\n @switch (name()) {\n @case ('menu') {\n <svg lucideMenu [size]=\"size()\" [color]=\"color()\" [strokeWidth]=\"strokeWidth()\" class=\"lib-icon\"></svg>\n }\n @case ('x') {\n <svg lucideX [size]=\"size()\" [color]=\"color()\" [strokeWidth]=\"strokeWidth()\" class=\"lib-icon\"></svg>\n }\n @case ('sun') {\n <svg lucideSun [size]=\"size()\" [color]=\"color()\" [strokeWidth]=\"strokeWidth()\" class=\"lib-icon\"></svg>\n }\n @case ('moon') {\n <svg lucideMoon [size]=\"size()\" [color]=\"color()\" [strokeWidth]=\"strokeWidth()\" class=\"lib-icon\"></svg>\n }\n @case ('monitor') {\n <svg lucideMonitor [size]=\"size()\" [color]=\"color()\" [strokeWidth]=\"strokeWidth()\" class=\"lib-icon\"></svg>\n }\n @case ('bell') {\n <svg lucideBell [size]=\"size()\" [color]=\"color()\" [strokeWidth]=\"strokeWidth()\" class=\"lib-icon\"></svg>\n }\n @case ('grid') {\n <svg lucideGrid [size]=\"size()\" [color]=\"color()\" [strokeWidth]=\"strokeWidth()\" class=\"lib-icon\"></svg>\n }\n @case ('chevronDown') {\n <svg lucideChevronDown [size]=\"size()\" [color]=\"color()\" [strokeWidth]=\"strokeWidth()\" class=\"lib-icon\"></svg>\n }\n @case ('building2') {\n <svg lucideBuilding2 [size]=\"size()\" [color]=\"color()\" [strokeWidth]=\"strokeWidth()\" class=\"lib-icon\"></svg>\n }\n @default {\n <svg lucideSearch [size]=\"size()\" [color]=\"color()\" [strokeWidth]=\"strokeWidth()\" class=\"lib-icon\"></svg>\n }\n }\n `,\n styles: [`\n :host {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: inherit;\n }\n .lib-icon {\n display: block;\n color: inherit;\n }\n `]\n})\nexport class IconComponent {\r\n readonly name = input.required<string>();\r\n readonly size = input<number>(24);\n readonly color = input<string | undefined>(undefined);\n readonly strokeWidth = input<number | undefined>(undefined);\n}\n","import { Component, inject, input, output, signal, computed } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { Router, RouterLink, RouterLinkActive } from '@angular/router';\r\nimport { User, Organization, NavigationItem, Notification, Application } from '../../models/models.interface';\r\nimport { ThemeService } from '../../services/theme.service';\r\nimport { AppSwitcherComponent } from '../../navigation';\r\nimport { NotificationBellComponent } from '../../notifications';\r\nimport { UserMenuComponent } from '../../user';\r\n\r\nimport { IconComponent } from '../../shared/icon/icon.component';\r\n\r\n\r\n@Component({\r\n selector: 'lib-header',\r\n standalone: true,\r\n imports: [\r\n CommonModule,\r\n RouterLink,\r\n RouterLinkActive,\r\n AppSwitcherComponent,\r\n NotificationBellComponent,\r\n UserMenuComponent,\r\n IconComponent,\r\n ],\r\n templateUrl: './header.html',\r\n styleUrl: './header.css',\r\n})\r\nexport class HeaderComponent {\r\n private readonly router = inject(Router);\r\n protected readonly themeService = inject(ThemeService);\r\n\r\n // User & Organization Inputs\r\n readonly user = input<User | null>(null);\r\n readonly permissions = input<string[]>([]);\r\n readonly organization = input<Organization | null>(null);\r\n\r\n // Navigation & Integration Inputs\r\n readonly navigationItems = input<NavigationItem[]>([]);\r\n readonly applications = input<Application[]>([]);\r\n readonly notifications = input<Notification[]>([]);\r\n readonly notificationCount = input<number>(0);\r\n readonly logoUrl = input<string>('https://cdn.jsdelivr.net/gh/Excellore/excellore-static-assets@main/images/excellore-logo-origin.png');\n readonly appName = input<string>('Excellore');\n readonly currentAppName = input<string>('');\n readonly showSearch = input<boolean>(true);\n readonly showNotifications = input<boolean>(true);\n readonly showAppSwitcher = input<boolean>(true);\n readonly showThemeSwitcher = input<boolean>(true);\n readonly showSignIn = input<boolean>(true);\n\n // Outputs as decided in the architecture plan\n readonly search = output<string>();\n readonly signIn = output<void>();\n readonly applicationSelected = output<Application>();\n readonly notificationClick = output<Notification>();\r\n readonly markRead = output<string>();\r\n readonly clearAll = output<void>();\r\n readonly menuAction = output<'profile' | 'settings' | 'billing' | 'logout'>();\r\n\r\n // Lucide Icons\r\n // readonly searchIcon = Search;\r\n // readonly menuIcon = Menu;\r\n // readonly xIcon = X;\r\n // readonly sunIcon = Sun;\r\n // readonly moonIcon = Moon;\r\n // readonly monitorIcon = Monitor;\r\n\r\n // Header specific UI states\r\n readonly isMobileMenuOpen = signal(false);\r\n\r\n // Checks if the user has permission to see an item\r\n hasPermission(item: NavigationItem): boolean {\r\n if (!item.permissions || item.permissions.length === 0) {\r\n return true;\r\n }\r\n const currentPermissions = this.permissions();\r\n return item.permissions.some((p) => currentPermissions.includes(p));\r\n }\r\n\r\n // Reactive unread count selector\r\n readonly unreadCount = computed(() => {\n const overrideCount = this.notificationCount();\n if (overrideCount > 0) return overrideCount;\n return this.notifications().filter((n) => !n.read).length;\n });\n\n readonly hasMobileMenuContent = computed(() =>\n this.showSearch() || this.navigationItems().some((item) => this.hasPermission(item))\n );\n\r\n // Determines if an item's internal route is active\r\n isActive(item: NavigationItem): boolean {\r\n if (!item.route) return false;\r\n try {\r\n return this.router.isActive(item.route, {\r\n paths: 'subset',\r\n queryParams: 'ignored',\r\n fragment: 'ignored',\r\n matrixParams: 'ignored',\r\n });\r\n } catch {\r\n return false;\r\n }\r\n }\r\n\r\n toggleMobileMenu(): void {\r\n this.isMobileMenuOpen.update((v) => !v);\r\n }\r\n\r\n closeMobileMenu(): void {\r\n this.isMobileMenuOpen.set(false);\r\n }\r\n\r\n onSearch(query: string): void {\r\n this.search.emit(query);\r\n }\r\n\r\n onApplicationSelect(app: Application): void {\r\n this.applicationSelected.emit(app);\r\n }\r\n\r\n onNotificationClick(notification: Notification): void {\r\n this.notificationClick.emit(notification);\r\n }\r\n\r\n onMarkRead(id: string): void {\r\n this.markRead.emit(id);\r\n }\r\n\r\n onClearAll(): void {\r\n this.clearAll.emit();\r\n }\r\n\r\n onMenuAction(action: 'profile' | 'settings' | 'billing' | 'logout'): void {\r\n this.menuAction.emit(action);\r\n }\r\n}\r\n","<header\n class=\"header-container fixed top-0 inset-x-0 z-40 flex h-[3.75rem] items-center justify-between px-3 text-[var(--foreground)] sm:px-4 lg:px-6\">\n\n <!-- Left Side: Brand, Current App & Desktop Links -->\n <div class=\"flex min-w-0 items-center gap-3\">\n\n <a routerLink=\"/\" class=\"brand-identity\">\n <img [src]=\"logoUrl()\" [alt]=\"appName() + ' logo'\" width=\"26\" height=\"26\" class=\"brand-logo\" />\n <span class=\"brand-name\">{{ appName() }}</span>\n </a>\n\n @if (currentAppName()) {\n <div class=\"current-app-identity\" [attr.aria-label]=\"'Current app: ' + currentAppName()\">\n <span class=\"brand-separator\" aria-hidden=\"true\">|</span>\n <span class=\"current-app-name\">{{ currentAppName() }}</span>\n </div>\n }\n\n <!-- Desktop Navigation Links -->\n <nav class=\"desktop-nav ml-1 hidden items-center lg:flex\" aria-label=\"Desktop primary navigation\">\n @for (item of navigationItems(); track item.id) {\n @if (hasPermission(item)) {\n @if (item.route) {\n <a [routerLink]=\"item.route\" routerLinkActive=\"active\" [routerLinkActiveOptions]=\"{exact: true}\" class=\"nav-link\"\n [attr.aria-current]=\"isActive(item) ? 'page' : null\">\n {{ item.label }}\n </a>\n } @else {\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" class=\"nav-link\">\n {{ item.label }}\n </a>\n }\n }\n }\n </nav>\n </div>\n\n <!-- Middle: Search Input (Desktop) -->\n @if (showSearch()) {\n <div class=\"hidden flex-1 justify-center px-4 md:flex\">\n <div class=\"header-search-shell\">\n <lib-icon name=\"search\" [size]=\"14\"\n class=\"pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-[var(--secondary)]\" />\n\n <input type=\"search\" placeholder=\"Search Excellore...\" #searchInput\n (keyup.enter)=\"onSearch(searchInput.value); searchInput.value = ''\" class=\"header-search\" autocomplete=\"off\"\n aria-label=\"Search\" />\n </div>\n </div>\n } @else {\n <div class=\"hidden flex-1 md:block\"></div>\n }\n\n <!-- Right Side Actions -->\n <div class=\"header-actions\">\n\n <!-- Mobile Search Trigger -->\n @if (showSearch()) {\n <button type=\"button\" class=\"header-action mobile-search-action\" (click)=\"toggleMobileMenu()\"\n aria-label=\"Open mobile menu\" aria-expanded=\"isMobileMenuOpen()\" #menuToggle>\n <lib-icon name=\"search\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\n </button>\n }\n\n <!-- Theme Switcher -->\n @if (showThemeSwitcher()) {\n <button type=\"button\" class=\"header-action\" (click)=\"themeService.toggleTheme()\" aria-label=\"Toggle theme\">\n @switch (themeService.theme()) {\n @case ('light') {\n <lib-icon name=\"sun\" [size]=\"16\" class=\"text-[var(--primary)]\" />\n }\n @case ('dark') {\n <lib-icon name=\"moon\" [size]=\"16\" class=\"text-[var(--primary)]\" />\n }\n @default {\n <lib-icon name=\"monitor\" [size]=\"16\" class=\"text-[var(--secondary)]\" />\n }\n }\n </button>\n }\n\n <!-- App Switcher -->\n @if (showAppSwitcher() && applications().length > 0) {\n <lib-app-switcher [applications]=\"applications()\" (applicationSelected)=\"onApplicationSelect($event)\" />\n }\n\n <!-- Notifications -->\n @if (showNotifications()) {\n <lib-notification-bell [unreadCount]=\"unreadCount()\" [notifications]=\"notifications()\"\n (notificationClick)=\"onNotificationClick($event)\" (markRead)=\"onMarkRead($event)\" (clearAll)=\"onClearAll()\" />\n }\n\n <!-- User Menu -->\n @if (user()) {\n <div class=\"user-menu-divider\">\n <lib-user-menu [user]=\"user()\" (menuAction)=\"onMenuAction($event)\" />\n </div>\n } @else if (showSignIn()) {\n <button type=\"button\" class=\"sign-in-button\" (click)=\"signIn.emit()\">\n Sign in\n </button>\n }\n\n <!-- Mobile Menu Toggle (Hamburger) -->\n @if (hasMobileMenuContent()) {\n <button type=\"button\" class=\"header-action mobile-menu-action\" (click)=\"toggleMobileMenu()\"\n aria-label=\"Toggle navigation menu\" aria-expanded=\"isMobileMenuOpen()\" #mobileMenuToggle>\n <lib-icon [name]=\"isMobileMenuOpen() ? 'x' : 'menu'\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\n </button>\n }\n </div>\n</header>\n\n<!-- Mobile Menu Backdrop -->\n@if (isMobileMenuOpen() && hasMobileMenuContent()) {\n<div class=\"mobile-backdrop fixed inset-0 z-30 lg:hidden\" (click)=\"closeMobileMenu()\"\n (keydown.escape)=\"closeMobileMenu()\" tabindex=\"-1\" aria-hidden=\"true\"></div>\n\n<!-- Mobile Navigation Drawer -->\n<div\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\"\n role=\"dialog\" aria-modal=\"true\" aria-label=\"Mobile navigation\" (keydown.escape)=\"closeMobileMenu()\">\n <!-- Mobile Search -->\n @if (showSearch()) {\n <div class=\"relative mb-3 w-full\">\n <lib-icon name=\"search\" [size]=\"16\"\n class=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[var(--secondary)] pointer-events-none\"\n aria-hidden=\"true\" />\n\n <input type=\"search\" placeholder=\"Search Excellore...\" #mobileSearchInput\n (keyup.enter)=\"onSearch(mobileSearchInput.value); mobileSearchInput.value = ''; closeMobileMenu()\"\n class=\"header-search mobile\" autocomplete=\"off\" aria-label=\"Search\" />\n </div>\n }\n\n <!-- Mobile Nav Links -->\n <nav class=\"flex flex-col gap-1 overflow-y-auto\" role=\"navigation\">\n @for (item of navigationItems(); track item.id) {\n @if (hasPermission(item)) {\n @if (item.route) {\n <a [routerLink]=\"item.route\" routerLinkActive=\"mobile-active\" [routerLinkActiveOptions]=\"{exact: true}\"\n (click)=\"closeMobileMenu()\" class=\"mobile-nav-link\" [attr.aria-current]=\"isActive(item) ? 'page' : null\">\n {{ item.label }}\n </a>\n } @else {\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" (click)=\"closeMobileMenu()\" class=\"mobile-nav-link\">\n {{ item.label }}\n </a>\n }\n }\n }\n </nav>\n</div>\n}\n","/*\r\n * Public API Surface of ui\r\n */\r\n\r\nexport * from './lib/models';\r\nexport * from './lib/services';\r\nexport * from './lib/layout';\r\nexport * from './lib/navigation';\r\nexport * from './lib/user';\r\nexport * from './lib/notifications';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@excellore/ui",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -9,6 +9,7 @@
9
9
  "@angular/core": "^21.0.0"
10
10
  },
11
11
  "dependencies": {
12
+ "@lucide/angular": "^1.21.0",
12
13
  "tslib": "^2.3.0"
13
14
  },
14
15
  "sideEffects": false,
@@ -1,5 +1,4 @@
1
1
  import * as _angular_core from '@angular/core';
2
- import * as lucide_angular from 'lucide-angular';
3
2
 
4
3
  interface User {
5
4
  id: string;
@@ -102,8 +101,6 @@ declare class HeaderComponent {
102
101
  declare class AppSwitcherComponent {
103
102
  readonly applications: _angular_core.InputSignal<Application[]>;
104
103
  readonly applicationSelected: _angular_core.OutputEmitterRef<Application>;
105
- readonly gridIcon: lucide_angular.LucideIconData;
106
- readonly chevronDownIcon: lucide_angular.LucideIconData;
107
104
  readonly isOpen: _angular_core.WritableSignal<boolean>;
108
105
  toggleSwitcher(): void;
109
106
  closeSwitcher(): void;
@@ -118,9 +115,6 @@ declare class OrganizationSwitcherComponent {
118
115
  readonly organizations: _angular_core.InputSignal<Organization[]>;
119
116
  readonly selectedOrganization: _angular_core.InputSignal<Organization | null>;
120
117
  readonly organizationChanged: _angular_core.OutputEmitterRef<Organization>;
121
- readonly chevronDownIcon: lucide_angular.LucideIconData;
122
- readonly checkIcon: lucide_angular.LucideIconData;
123
- readonly buildingIcon: lucide_angular.LucideIconData;
124
118
  readonly isOpen: _angular_core.WritableSignal<boolean>;
125
119
  toggleSwitcher(): void;
126
120
  closeSwitcher(): void;
@@ -142,11 +136,6 @@ declare class UserAvatarComponent {
142
136
  declare class UserMenuComponent {
143
137
  readonly user: _angular_core.InputSignal<User | null>;
144
138
  readonly menuAction: _angular_core.OutputEmitterRef<"profile" | "settings" | "billing" | "logout">;
145
- readonly userIcon: lucide_angular.LucideIconData;
146
- readonly settingsIcon: lucide_angular.LucideIconData;
147
- readonly creditCardIcon: lucide_angular.LucideIconData;
148
- readonly logOutIcon: lucide_angular.LucideIconData;
149
- readonly chevronDownIcon: lucide_angular.LucideIconData;
150
139
  readonly isOpen: _angular_core.WritableSignal<boolean>;
151
140
  toggleMenu(): void;
152
141
  closeMenu(): void;
@@ -162,7 +151,6 @@ declare class NotificationBellComponent {
162
151
  readonly notificationClick: _angular_core.OutputEmitterRef<Notification>;
163
152
  readonly markRead: _angular_core.OutputEmitterRef<string>;
164
153
  readonly clearAll: _angular_core.OutputEmitterRef<void>;
165
- readonly bellIcon: lucide_angular.LucideIconData;
166
154
  readonly isOpen: _angular_core.WritableSignal<boolean>;
167
155
  togglePanel(): void;
168
156
  closePanel(): void;
@@ -179,10 +167,6 @@ declare class NotificationPanelComponent {
179
167
  readonly notificationClick: _angular_core.OutputEmitterRef<Notification>;
180
168
  readonly markRead: _angular_core.OutputEmitterRef<string>;
181
169
  readonly clearAll: _angular_core.OutputEmitterRef<void>;
182
- readonly checkCheckIcon: lucide_angular.LucideIconData;
183
- readonly trashIcon: lucide_angular.LucideIconData;
184
- readonly bellOffIcon: lucide_angular.LucideIconData;
185
- readonly circleIcon: lucide_angular.LucideIconData;
186
170
  onNotificationClick(notification: Notification): void;
187
171
  onMarkRead(id: string, event: MouseEvent): void;
188
172
  onClearAll(event: MouseEvent): void;