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