@shival99/z-ui 1.0.5 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,13 +19,15 @@ class ZMenuComponent {
19
19
  _platformId = inject(PLATFORM_ID);
20
20
  _router = inject(Router);
21
21
  zLogo = input('assets/images/avatar.svg', ...(ngDevMode ? [{ debugName: "zLogo" }] : []));
22
+ zLogoMobile = input('', ...(ngDevMode ? [{ debugName: "zLogoMobile" }] : []));
22
23
  zMenus = input([], ...(ngDevMode ? [{ debugName: "zMenus" }] : []));
23
24
  zUser = input(null, ...(ngDevMode ? [{ debugName: "zUser" }] : []));
25
+ zUserActions = input([], ...(ngDevMode ? [{ debugName: "zUserActions" }] : []));
24
26
  zActionsTemplate = input(null, ...(ngDevMode ? [{ debugName: "zActionsTemplate" }] : []));
25
27
  zCurrentPath = input('', ...(ngDevMode ? [{ debugName: "zCurrentPath" }] : []));
26
28
  zKey = input('z-menu', ...(ngDevMode ? [{ debugName: "zKey" }] : []));
27
29
  zOnSelect = output();
28
- zOnLogout = output();
30
+ zOnMenuAction = output();
29
31
  zControl = output();
30
32
  selectedParent = signal(null, ...(ngDevMode ? [{ debugName: "selectedParent" }] : []));
31
33
  selectedMenuItem = signal(null, ...(ngDevMode ? [{ debugName: "selectedMenuItem" }] : []));
@@ -40,6 +42,7 @@ class ZMenuComponent {
40
42
  };
41
43
  _isMobile$ = this._breakpointObserver.observe([Breakpoints.XSmall, Breakpoints.Small]).pipe(map(result => result.matches), takeUntilDestroyed(this._destroyRef));
42
44
  isMobile = toSignal(this._isMobile$, { initialValue: false });
45
+ mobileLogo = computed(() => this.zLogoMobile() || this.zLogo(), ...(ngDevMode ? [{ debugName: "mobileLogo" }] : []));
43
46
  _routerPath = toSignal(this._router.events.pipe(filter((e) => e instanceof NavigationEnd), map(e => e.urlAfterRedirects), startWith(this._router.url)), { initialValue: this._router.url });
44
47
  _currentPath = computed(() => this.zCurrentPath() || this._routerPath(), ...(ngDevMode ? [{ debugName: "_currentPath" }] : []));
45
48
  overlayType = computed(() => {
@@ -261,13 +264,28 @@ class ZMenuComponent {
261
264
  item.expanded = true;
262
265
  }
263
266
  toggleSidebar() {
267
+ if (!this.selectedParent()?.children?.length) {
268
+ return;
269
+ }
264
270
  this.sidebarCollapsed.set(!this.sidebarCollapsed());
265
271
  }
266
272
  closeSidebar() {
267
273
  this.sidebarCollapsed.set(true);
268
274
  }
269
- onLogoutClick() {
270
- this.zOnLogout.emit();
275
+ onUserActionClick(action) {
276
+ if (action.disabled) {
277
+ return;
278
+ }
279
+ this.zOnMenuAction.emit(action);
280
+ }
281
+ getVisibleUserActions() {
282
+ return this.zUserActions().filter(action => !action.hidden);
283
+ }
284
+ shouldShowDividerBefore(action, index) {
285
+ return action.divide === 'before' && index > 0;
286
+ }
287
+ shouldShowDividerAfter(action, index) {
288
+ return action.divide === 'after' && index < this.getVisibleUserActions().length - 1;
271
289
  }
272
290
  onBackdropClick() {
273
291
  if (this.mobileTemplateDrawerOpen()) {
@@ -350,7 +368,7 @@ class ZMenuComponent {
350
368
  return ZCacheService.get(this.zKey(), false) ?? false;
351
369
  }
352
370
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
353
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ZMenuComponent, isStandalone: true, selector: "z-menu", inputs: { zLogo: { classPropertyName: "zLogo", publicName: "zLogo", isSignal: true, isRequired: false, transformFunction: null }, zMenus: { classPropertyName: "zMenus", publicName: "zMenus", isSignal: true, isRequired: false, transformFunction: null }, zUser: { classPropertyName: "zUser", publicName: "zUser", isSignal: true, isRequired: false, transformFunction: null }, zActionsTemplate: { classPropertyName: "zActionsTemplate", publicName: "zActionsTemplate", isSignal: true, isRequired: false, transformFunction: null }, zCurrentPath: { classPropertyName: "zCurrentPath", publicName: "zCurrentPath", isSignal: true, isRequired: false, transformFunction: null }, zKey: { classPropertyName: "zKey", publicName: "zKey", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { zOnSelect: "zOnSelect", zOnLogout: "zOnLogout", zControl: "zControl" }, host: { properties: { "class.z-menu-collapsed": "sidebarCollapsed()", "class.z-menu-no-child": "selectedParentHasNoChild()", "class.z-menu-mobile": "isMobile()", "class.z-menu-mobile-open": "mobileMenuOpen()" }, classAttribute: "z-menu block" }, ngImport: i0, template: "<!-- DESKTOP LAYOUT - Hidden on mobile -->\n<div class=\"z-menu-desktop relative z-200 hidden h-full py-1 pl-1 md:block\" [class.collapsed]=\"sidebarCollapsed()\">\n <main class=\"z-menu-main relative flex h-full\">\n <div class=\"z-sidebar-main h-full\">\n <div\n class=\"z-shadow-menu bg-sidebar border-border/40 dark:border-sidebar-border relative flex h-full w-[50px] flex-col items-center gap-1.5 rounded-[6px] border\"></div>\n\n <div class=\"bg-sidebar text-sidebar-foreground absolute inset-px z-20 flex flex-col rounded-[6px]\">\n @if (zLogo()) {\n <div class=\"border-sidebar-border flex h-[52px] items-center justify-center border-b px-2\">\n <img [src]=\"zLogo()\" alt=\"Logo\" class=\"h-8 w-auto object-contain\" />\n </div>\n }\n\n <!-- Expand Button - Only show when collapsed -->\n @if (sidebarCollapsed() && selectedParent()) {\n <div\n class=\"z-expand-btn hover:bg-sidebar-accent mx-auto mt-2 flex h-9 w-9 cursor-pointer items-center justify-center rounded-[6px]\"\n (click)=\"toggleSidebar()\">\n <z-icon zType=\"lucideChevronsRight\" zSize=\"20\" class=\"opacity-70\" />\n </div>\n }\n\n <div class=\"flex flex-1 flex-col items-center gap-2 p-2\">\n @for (item of menuParents(); track item.id) {\n @let hasActiveChild = parentWithActiveChild()?.id === item.id;\n @let isCurrentlySelected = selectedParent()?.id === item.id;\n @let showActiveIndicator = hasActiveChild && !isCurrentlySelected;\n <div\n zTooltip\n [zContent]=\"item.name\"\n zPosition=\"right\"\n [zArrow]=\"false\"\n [zAlwaysShow]=\"true\"\n class=\"relative flex h-9 w-9 cursor-pointer items-center justify-center rounded-[6px] select-none\"\n [class.bg-sidebar-primary]=\"selectedParent()?.id === item.id\"\n [class.text-sidebar-primary-foreground]=\"selectedParent()?.id === item.id\"\n [class.hover:bg-sidebar-accent]=\"selectedParent()?.id !== item.id\"\n (click)=\"onDesktopParentClick(item)\">\n @if (item.icon) {\n <z-icon [zType]=\"item.icon\" zSize=\"20\" />\n } @else {\n <z-icon [zSvg]=\"item.iconSvg || ''\" zSize=\"20\" />\n }\n @if (showActiveIndicator) {\n <div\n class=\"bg-sidebar-primary absolute top-1/2 -left-[5px] h-4 w-1 -translate-y-1/2 rounded-full\"></div>\n }\n </div>\n }\n </div>\n\n <div class=\"border-sidebar-border flex items-center justify-center border-t p-2\">\n <div\n z-popover\n [zOffset]=\"11\"\n [zPopoverContent]=\"userPopoverTpl\"\n zPosition=\"right-bottom\"\n class=\"aspect-square h-9 w-9 shrink-0 cursor-pointer overflow-hidden rounded-full\">\n <img [src]=\"avatarSrc()\" alt=\"User Avatar\" class=\"h-full w-full object-cover\" />\n </div>\n </div>\n </div>\n </div>\n\n <ng-template #userPopoverTpl let-close=\"close\">\n <div class=\"min-w-56 p-1\">\n <!-- User Info Header - Always shown -->\n <div class=\"p-0 text-sm font-normal\">\n <div class=\"flex items-center gap-2 px-1 py-1.5 text-left text-sm\">\n <div class=\"h-8 w-8 shrink-0 overflow-hidden rounded-full\">\n <img [src]=\"avatarSrc()\" alt=\"User Avatar\" class=\"aspect-square size-full object-cover\" />\n </div>\n <div class=\"grid flex-1 text-left text-sm leading-tight\">\n <span class=\"truncate font-medium\">{{ zUser()?.name || 'User Name' }}</span>\n <span class=\"text-muted-foreground mt-0.5 truncate text-xs\">\n {{ zUser()?.email || 'user@example.com' }}\n </span>\n </div>\n </div>\n </div>\n\n <div class=\"bg-border -mx-1 my-1 h-px\"></div>\n\n <!-- Actions - Custom or Default -->\n @if (zActionsTemplate()) {\n <ng-container *ngTemplateOutlet=\"zActionsTemplate(); context: { close: close }\" />\n } @else {\n <button\n class=\"hover:bg-accent focus:bg-accent flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none\"\n (click)=\"close()\">\n <z-icon zType=\"lucideSparkles\" zSize=\"14\" class=\"text-muted-foreground\" />\n <span>Upgrade to Pro</span>\n </button>\n\n <div class=\"bg-border -mx-1 my-1 h-px\"></div>\n\n <button\n class=\"hover:bg-accent focus:bg-accent flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none\"\n (click)=\"close()\">\n <z-icon zType=\"lucideBadgeCheck\" zSize=\"14\" class=\"text-muted-foreground\" />\n <span>Account</span>\n </button>\n <button\n class=\"hover:bg-accent focus:bg-accent flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none\"\n (click)=\"close()\">\n <z-icon zType=\"lucideCreditCard\" zSize=\"14\" class=\"text-muted-foreground\" />\n <span>Billing</span>\n </button>\n <button\n class=\"hover:bg-accent focus:bg-accent flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none\"\n (click)=\"close()\">\n <z-icon zType=\"lucideBell\" zSize=\"14\" class=\"text-muted-foreground\" />\n <span>Notifications</span>\n </button>\n\n <div class=\"bg-border -mx-1 my-1 h-px\"></div>\n\n <button\n class=\"hover:bg-accent focus:bg-accent flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none\"\n (click)=\"onLogoutClick(); close()\">\n <z-icon zType=\"lucideLogOut\" zSize=\"14\" class=\"text-muted-foreground\" />\n <span>Log out</span>\n </button>\n }\n </div>\n </ng-template>\n\n @if (selectedParent(); as parent) {\n @let hasChildrenOrTemplate = parent.menuTemplate || (parent.children && parent.children.length > 0);\n @if (hasChildrenOrTemplate) {\n <div\n class=\"z-sidebar-child-wrapper\"\n [class.collapsed]=\"sidebarCollapsed()\"\n [style.--sidebar-child-width.px]=\"sidebarChildWidth()\">\n <div\n class=\"z-sidebar-child z-shadow-menu bg-card text-card-foreground border-border/40 dark:border-sidebar-border flex h-full shrink-0 flex-col items-start rounded-[6px] border\"\n [style.width.px]=\"sidebarChildWidth()\"\n [class.collapsed]=\"sidebarCollapsed()\">\n <div class=\"border-border flex h-[52px] w-full shrink-0 items-center border-b px-3\">\n @if (selectedParent()?.icon) {\n <z-icon [zType]=\"selectedParent()!.icon\" zSize=\"20\" class=\"mr-2 shrink-0\" />\n }\n <div\n class=\"mr-4 min-w-0 flex-1 truncate text-base font-semibold\"\n zTooltip\n zPosition=\"top-left\"\n [zContent]=\"selectedParent()?.name || ''\"\n [zArrow]=\"false\">\n {{ selectedParent()?.name }}\n </div>\n\n <z-icon\n zType=\"lucidePanelRightOpen\"\n zSize=\"20\"\n class=\"shrink-0 cursor-pointer opacity-60 hover:opacity-100\"\n (click)=\"toggleSidebar()\" />\n </div>\n\n <!-- Show menuTemplate if available, otherwise show children -->\n @if (selectedParent()?.menuTemplate) {\n <ng-scrollbar class=\"z-menu-scrollbar min-h-0 w-full flex-1\" track=\"vertical\">\n <div class=\"flex w-full flex-col p-3 pr-2\">\n <ng-container\n *ngTemplateOutlet=\"\n selectedParent()!.menuTemplate!;\n context: { $implicit: selectedParent()!, close: closeSidebarFn }\n \" />\n </div>\n </ng-scrollbar>\n } @else {\n <ng-scrollbar class=\"z-menu-scrollbar min-h-0 w-full flex-1\" track=\"vertical\">\n <div class=\"flex w-full flex-col gap-1 py-1 pr-2 pl-2\">\n <ng-container *ngTemplateOutlet=\"menuChildrenTpl; context: { $implicit: menuChildren() }\" />\n </div>\n </ng-scrollbar>\n }\n </div>\n </div>\n }\n }\n </main>\n</div>\n\n<!-- MOBILE LAYOUT - Visible only on mobile -->\n<div class=\"z-menu-mobile-wrapper hidden max-md:block\">\n <!-- Mobile Backdrop -->\n @if (mobileMenuOpen() || mobileTemplateDrawerOpen()) {\n <div\n class=\"z-menu-backdrop fixed inset-0 z-9998\"\n [class.z-menu-backdrop-dark]=\"overlayType() === 'dark'\"\n [class.z-menu-backdrop-blur]=\"overlayType() === 'blur'\"\n (click)=\"onBackdropClick()\"></div>\n }\n\n <!-- Mobile Drawer -->\n <aside\n class=\"z-menu-drawer bg-background border-border fixed top-0 left-0 z-9999 flex h-full w-70 flex-col rounded-r-lg border-r\"\n [class.open]=\"mobileMenuOpen()\">\n <!-- Drawer Header -->\n <div class=\"border-border flex h-14 shrink-0 items-center justify-between border-b px-4\">\n @if (zLogo()) {\n <img [src]=\"zLogo()\" alt=\"Logo\" class=\"h-8 w-auto object-contain\" />\n }\n <button z-button zType=\"ghost\" [zWave]=\"false\" class=\"bg-accent h-9 w-9 shrink-0\" (click)=\"closeMobileMenu()\">\n <z-icon zType=\"lucideX\" zSize=\"20\" />\n </button>\n </div>\n\n <!-- Drawer Content - Accordion Menu -->\n <ng-scrollbar class=\"z-menu-scrollbar flex-1\" track=\"vertical\">\n <div class=\"p-3\">\n @for (parent of menuParents(); track parent.id) {\n <div class=\"mb-1\">\n <!-- Parent Item -->\n @let isParentActive =\n selectedMenuItem()?.id === parent.id && (!parent.children || parent.children.length === 0);\n @let hasActiveChild = parentWithActiveChild()?.id === parent.id && !isParentActive;\n @let isCurrentlyViewing = selectedParent()?.id === parent.id;\n <div\n class=\"relative flex cursor-pointer items-center gap-3 rounded-[6px] p-2\"\n [class.bg-primary/20]=\"isParentActive\"\n [class.text-primary]=\"isParentActive || hasActiveChild\"\n [class.hover:bg-accent]=\"!isParentActive\"\n (click)=\"onMobileParentClick(parent)\">\n @if (parent.icon) {\n <z-icon [zType]=\"parent.icon\" zSize=\"18\" class=\"shrink-0\" />\n } @else if (parent.iconSvg) {\n <z-icon [zSvg]=\"parent.iconSvg\" zSize=\"18\" class=\"shrink-0\" />\n }\n <span class=\"min-w-0 flex-1 truncate text-[13px] font-[450]\">{{ parent.name }}</span>\n @if ((parent.children && parent.children.length > 0) || parent.menuTemplate) {\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"16\"\n class=\"z-menu-arrow shrink-0 opacity-60 transition-transform\"\n [class.expanded]=\"parent.expanded && isCurrentlyViewing\" />\n }\n </div>\n\n <!-- Children (Accordion) - Only render if NO menuTemplate -->\n @if (parent.children && parent.children.length > 0 && !parent.menuTemplate) {\n <div class=\"z-menu-submenu\" [class.expanded]=\"parent.expanded && isCurrentlyViewing\">\n <div class=\"z-menu-submenu-inner\">\n <div class=\"z-menu-tree relative ml-4 flex flex-col gap-1 pt-1 pl-3\">\n <div class=\"absolute top-0 bottom-2 left-0 w-px bg-gray-300 dark:bg-gray-600\"></div>\n\n @for (child of parent.children; track child.id) {\n @let isChildActive =\n selectedMenuItem()?.id === child.id && (!child.children || child.children.length === 0);\n @let isChildParentOfActive = isParentOfActiveItem(child);\n @let hasChildren = child.children && child.children.length > 0;\n\n <div>\n <div\n class=\"relative flex cursor-pointer items-center gap-2 rounded-[6px] p-2\"\n [class.bg-primary]=\"isChildActive\"\n [class.text-primary-foreground]=\"isChildActive\"\n [class.text-primary]=\"isChildParentOfActive && !isChildActive\"\n [class.hover:bg-accent]=\"!isChildActive\"\n (click)=\"onMenuItemClick(child)\">\n <div class=\"absolute top-1/2 -left-3 h-px w-3 bg-gray-300 dark:bg-gray-600\"></div>\n @if (child.icon) {\n <z-icon [zType]=\"child.icon\" zSize=\"16\" class=\"shrink-0\" />\n } @else if (child.iconSvg) {\n <z-icon [zSvg]=\"child.iconSvg\" zSize=\"16\" class=\"shrink-0\" />\n } @else {\n <div\n class=\"h-1.5 w-1.5 shrink-0 rounded-full\"\n [class.bg-primary-foreground]=\"isChildActive\"\n [class.bg-gray-400]=\"!isChildActive\"></div>\n }\n <span class=\"min-w-0 flex-1 truncate text-[13px] font-[450]\">{{ child.name }}</span>\n @if (hasChildren) {\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"z-menu-arrow shrink-0 opacity-60 transition-transform\"\n [class.expanded]=\"child.expanded\" />\n }\n </div>\n\n <!-- Level 3 -->\n @if (hasChildren) {\n <div class=\"z-menu-submenu\" [class.expanded]=\"child.expanded\">\n <div class=\"z-menu-submenu-inner\">\n <div class=\"relative ml-4 flex flex-col gap-1 pt-1 pl-3\">\n <div class=\"absolute top-0 bottom-2 left-0 w-px bg-gray-300 dark:bg-gray-600\"></div>\n\n @for (subChild of child.children; track subChild.id) {\n @let isSubChildActive = selectedMenuItem()?.id === subChild.id;\n\n <div class=\"relative\">\n <div class=\"absolute top-1/2 -left-3 h-px w-3 bg-gray-300 dark:bg-gray-600\"></div>\n\n <div\n class=\"flex cursor-pointer items-center gap-2 rounded-[6px] p-2\"\n [class.bg-primary]=\"isSubChildActive\"\n [class.text-primary-foreground]=\"isSubChildActive\"\n [class.hover:bg-accent]=\"!isSubChildActive\"\n (click)=\"onMenuItemClick(subChild)\">\n <div\n class=\"h-1 w-1 shrink-0 rounded-full\"\n [class.bg-primary-foreground]=\"isSubChildActive\"\n [class.bg-gray-400]=\"!isSubChildActive\"></div>\n <span class=\"min-w-0 flex-1 truncate text-[13px] font-[450]\">\n {{ subChild.name }}\n </span>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n }\n </div>\n }\n </div>\n </div>\n </div>\n }\n </div>\n }\n </div>\n </ng-scrollbar>\n\n <!-- Drawer Footer - User Info -->\n <div class=\"border-border shrink-0 border-t p-3\">\n <div\n z-popover\n [zOffset]=\"8\"\n [zPopoverContent]=\"mobileActionsPopoverTpl\"\n zPosition=\"top-right\"\n zTrigger=\"click\"\n class=\"hover:bg-accent flex cursor-pointer items-center gap-3 rounded-[6px] p-2\">\n <div class=\"h-10 w-10 shrink-0 overflow-hidden rounded-full\">\n <img [src]=\"avatarSrc()\" alt=\"User Avatar\" class=\"h-full w-full object-cover\" />\n </div>\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-medium\">{{ zUser()?.name || 'User Name' }}</div>\n <div class=\"text-muted-foreground truncate text-xs\">{{ zUser()?.email || 'user@example.com' }}</div>\n </div>\n <div class=\"text-muted-foreground flex shrink-0 flex-col\">\n <z-icon zType=\"lucideChevronUp\" zSize=\"14\" />\n <z-icon zType=\"lucideChevronDown\" zSize=\"14\" class=\"-mt-1\" />\n </div>\n </div>\n </div>\n </aside>\n\n <!-- Mobile Custom Template Drawer -->\n <aside\n class=\"z-menu-custom-drawer bg-background border-border fixed top-0 left-0 z-9999 flex h-full max-w-[calc(100vw-16px)] flex-col rounded-r-lg border-r\"\n [style.width.px]=\"mobileCustomDrawerWidth()\"\n [class.open]=\"mobileTemplateDrawerOpen() && customDrawerParent()?.menuTemplate\">\n @if (customDrawerParent()?.menuTemplate) {\n <!-- Custom Drawer Header -->\n <div class=\"border-border flex h-14 shrink-0 items-center gap-3 border-b px-4\">\n <button\n z-button\n zType=\"ghost\"\n [zWave]=\"false\"\n class=\"bg-accent h-9 w-9 shrink-0\"\n (click)=\"closeMobileTemplateDrawer()\">\n <z-icon zType=\"lucideChevronLeft\" zSize=\"20\" />\n </button>\n <span class=\"min-w-0 flex-1 truncate text-base font-semibold\">\n {{ customDrawerParent()?.name }}\n </span>\n </div>\n\n <!-- Custom Template Content -->\n <ng-scrollbar class=\"z-menu-scrollbar flex-1\" track=\"vertical\">\n <div class=\"p-4\">\n <ng-container\n *ngTemplateOutlet=\"\n customDrawerParent()!.menuTemplate!;\n context: { $implicit: customDrawerParent()!, close: closeMobileDrawerFn }\n \" />\n </div>\n </ng-scrollbar>\n }\n </aside>\n\n <ng-template #mobileActionsPopoverTpl let-close=\"close\">\n <div class=\"min-w-48 p-1\">\n @if (zActionsTemplate()) {\n <ng-container *ngTemplateOutlet=\"zActionsTemplate(); context: { close: close }\" />\n } @else {\n <button\n class=\"hover:bg-accent focus:bg-accent flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none\"\n (click)=\"close()\">\n <z-icon zType=\"lucideSparkles\" zSize=\"14\" class=\"text-muted-foreground\" />\n <span>Upgrade to Pro</span>\n </button>\n\n <div class=\"bg-border -mx-1 my-1 h-px\"></div>\n\n <button\n class=\"hover:bg-accent focus:bg-accent flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none\"\n (click)=\"close()\">\n <z-icon zType=\"lucideBadgeCheck\" zSize=\"14\" class=\"text-muted-foreground\" />\n <span>Account</span>\n </button>\n <button\n class=\"hover:bg-accent focus:bg-accent flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none\"\n (click)=\"close()\">\n <z-icon zType=\"lucideCreditCard\" zSize=\"14\" class=\"text-muted-foreground\" />\n <span>Billing</span>\n </button>\n <button\n class=\"hover:bg-accent focus:bg-accent flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none\"\n (click)=\"close()\">\n <z-icon zType=\"lucideBell\" zSize=\"14\" class=\"text-muted-foreground\" />\n <span>Notifications</span>\n </button>\n\n <div class=\"bg-border -mx-1 my-1 h-px\"></div>\n\n <button\n class=\"hover:bg-accent focus:bg-accent flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none\"\n (click)=\"onLogoutClick(); close()\">\n <z-icon zType=\"lucideLogOut\" zSize=\"14\" class=\"text-muted-foreground\" />\n <span>Log out</span>\n </button>\n }\n </div>\n </ng-template>\n</div>\n\n<!-- Shared Template for Menu Children -->\n<ng-template #menuChildrenTpl let-children>\n @for (child of children; track child.id) {\n @let isChildActive = selectedMenuItem()?.id === child.id && (!child.children || child.children.length === 0);\n @let isChildParentOfActive = isParentOfActiveItem(child);\n @let hasChildren = child.children && child.children.length > 0;\n <div>\n <div\n #divLevel1\n class=\"z-menu-item flex w-full cursor-pointer items-center gap-2 rounded-[6px] p-2\"\n [class.bg-primary]=\"isChildActive\"\n [class.text-primary-foreground]=\"isChildActive\"\n [class.text-primary]=\"isChildParentOfActive && !isChildActive\"\n [class.hover:bg-gray-200]=\"!isChildActive\"\n [class.dark:hover:bg-input/50]=\"!isChildActive\"\n (click)=\"onMenuItemClick(child)\">\n @if (child.icon) {\n <z-icon [zType]=\"child.icon\" zSize=\"18\" class=\"shrink-0\" />\n } @else if (child.iconSvg) {\n <z-icon [zSvg]=\"child.iconSvg\" zSize=\"18\" class=\"shrink-0\" />\n }\n <span\n class=\"min-w-0 flex-1 truncate text-[13px] font-[450]\"\n zTooltip\n [zContent]=\"child.name\"\n [zArrow]=\"false\"\n [zOffset]=\"10\"\n zMaxWidth=\"200px\"\n zPosition=\"right\"\n [zTriggerElement]=\"divLevel1\">\n {{ child.name }}\n </span>\n @if (hasChildren) {\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"z-menu-arrow shrink-0 opacity-60 transition-transform\"\n [class.expanded]=\"child.expanded\" />\n }\n </div>\n\n @if (hasChildren) {\n <div class=\"z-menu-submenu\" [class.expanded]=\"child.expanded\">\n <div class=\"z-menu-submenu-inner\">\n <div class=\"z-menu-tree relative ml-4 flex flex-col gap-1 pt-1 pl-3\">\n <div class=\"absolute top-0 bottom-2 left-0 w-px bg-gray-300 dark:bg-gray-600\"></div>\n\n @for (subChild of child.children; track subChild.id; let isLast = $last) {\n @let isSubChildActive = selectedMenuItem()?.id === subChild.id;\n <div class=\"relative\">\n <div class=\"absolute top-1/2 -left-3 h-px w-3 bg-gray-300 dark:bg-gray-600\"></div>\n\n <div\n #divLevel2\n class=\"z-menu-item flex w-full cursor-pointer items-center gap-2 rounded-[6px] p-2\"\n [class.bg-primary]=\"isSubChildActive\"\n [class.text-primary-foreground]=\"isSubChildActive\"\n [class.hover:bg-gray-200]=\"!isSubChildActive\"\n [class.dark:hover:bg-input/50]=\"!isSubChildActive\"\n (click)=\"onMenuItemClick(subChild)\">\n @if (subChild.icon) {\n <z-icon [zType]=\"subChild.icon\" zSize=\"16\" class=\"shrink-0\" />\n } @else if (subChild.iconSvg) {\n <z-icon [zSvg]=\"subChild.iconSvg\" zSize=\"16\" class=\"shrink-0\" />\n } @else {\n <div\n class=\"h-1 w-1 shrink-0 rounded-full\"\n [class.bg-primary-foreground]=\"isSubChildActive\"\n [class.bg-gray-400]=\"!isSubChildActive\"></div>\n }\n <span\n class=\"min-w-0 flex-1 truncate text-[13px] font-[450]\"\n zTooltip\n [zContent]=\"subChild.name\"\n [zArrow]=\"false\"\n [zOffset]=\"10\"\n zMaxWidth=\"200px\"\n zPosition=\"right\"\n [zTriggerElement]=\"divLevel2\">\n {{ subChild.name }}\n </span>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n }\n </div>\n }\n</ng-template>\n", styles: [".z-menu{display:block;flex-shrink:0;height:100%}.z-menu [class*=cursor-pointer]{-webkit-user-select:none;user-select:none}@media(min-width:768px){.z-menu{contain:layout style;min-width:286px;transition:min-width .25s cubic-bezier(.4,0,.2,1);will-change:min-width}.z-menu.z-menu-collapsed,.z-menu.z-menu-no-child{min-width:56px}}.z-shadow-menu{box-shadow:0 0 10px #0000001f}@media(max-width:767px){.z-menu-desktop{display:none!important;pointer-events:none;visibility:hidden}}.z-sidebar-main{position:relative;z-index:20;flex-shrink:0}.z-sidebar-child-wrapper{--sidebar-child-width: 230px;position:absolute;left:0;top:0;width:calc(100% + -0px);height:100%;clip-path:inset(0 -100px 0 0);pointer-events:none;transition:clip-path .25s cubic-bezier(.4,0,.2,1);will-change:clip-path;contain:layout}.z-sidebar-child-wrapper.collapsed{clip-path:inset(0 -100px 0 56px)}.z-sidebar-child{flex-shrink:0;height:100%;margin-left:54px;transform:translate(0);transition:transform .25s cubic-bezier(.4,0,.2,1),width .25s cubic-bezier(.4,0,.2,1);pointer-events:auto;will-change:transform,width}.z-sidebar-child.collapsed{transform:translate(calc(-1 * (var(--sidebar-child-width) + 54px + 5px)))}.z-menu-arrow.expanded{transform:rotate(90deg)}.z-menu-submenu{display:grid;grid-template-rows:0fr;transition:grid-template-rows .15s ease-out}.z-menu-submenu.expanded{grid-template-rows:1fr}.z-menu-submenu>.z-menu-submenu-inner{overflow:hidden;min-height:0}.z-menu-backdrop{animation:z-menu-backdrop-enter .2s ease-out forwards}.z-menu-backdrop-dark{background-color:#0009}.z-menu-backdrop-blur{background-color:#0000000d;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.z-menu-drawer{transform:translate(-100%);transition:transform .3s cubic-bezier(.32,.72,0,1);box-shadow:4px 0 24px #0000001a}.z-menu-drawer.open{transform:translate(0)}:host.z-menu-mobile-open .z-menu-drawer{transform:translate(0)}.z-menu-desktop.collapsed .z-sidebar-child-wrapper{pointer-events:none}.z-menu-custom-drawer{transform:translate(-100%);transition:transform .3s cubic-bezier(.32,.72,0,1);box-shadow:4px 0 24px #0000001a;will-change:transform}.z-menu-custom-drawer.open{transform:translate(0)}@keyframes z-menu-backdrop-enter{0%{opacity:0}to{opacity:1}}@keyframes z-expand-btn-enter{0%{opacity:0;transform:scale(.5) translateY(10px)}70%{transform:scale(1.1) translateY(-2px)}to{opacity:1;transform:scale(1) translateY(0)}}.z-expand-btn{animation:z-expand-btn-enter .25s cubic-bezier(.34,1.56,.64,1) forwards}.z-menu-scrollbar{--scrollbar-padding: 0}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ZButtonComponent, selector: "z-button, button[z-button], a[z-button]", inputs: ["class", "zType", "zSize", "zShape", "zLabel", "zLoading", "zDisabled", "zTypeIcon", "zSizeIcon", "zStrokeWidthIcon", "zWave"], exportAs: ["zButton"] }, { kind: "component", type: ZIconComponent, selector: "z-icon, [z-icon]", inputs: ["class", "zType", "zSize", "zStrokeWidth", "zSvg"] }, { kind: "directive", type: ZTooltipDirective, selector: "[z-tooltip], [zTooltip]", inputs: ["zContent", "zPosition", "zTrigger", "zTooltipType", "zTooltipSize", "zClass", "zShowDelay", "zHideDelay", "zArrow", "zDisabled", "zOffset", "zAutoDetect", "zTriggerElement", "zAlwaysShow", "zMaxWidth"], outputs: ["zShow", "zHide"], exportAs: ["zTooltip"] }, { kind: "directive", type: ZPopoverDirective, selector: "[z-popover]", inputs: ["zPopoverContent", "zPosition", "zTrigger", "zClass", "zShowDelay", "zHideDelay", "zDisabled", "zOffset", "zPopoverWidth", "zManualClose", "zScrollClose", "zShowArrow"], outputs: ["zShow", "zHide", "zHideStart", "zControl"], exportAs: ["zPopover"] }, { kind: "component", type: NgScrollbar, selector: "ng-scrollbar:not([externalViewport]), [ngScrollbar]", exportAs: ["ngScrollbar"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
371
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ZMenuComponent, isStandalone: true, selector: "z-menu", inputs: { zLogo: { classPropertyName: "zLogo", publicName: "zLogo", isSignal: true, isRequired: false, transformFunction: null }, zLogoMobile: { classPropertyName: "zLogoMobile", publicName: "zLogoMobile", isSignal: true, isRequired: false, transformFunction: null }, zMenus: { classPropertyName: "zMenus", publicName: "zMenus", isSignal: true, isRequired: false, transformFunction: null }, zUser: { classPropertyName: "zUser", publicName: "zUser", isSignal: true, isRequired: false, transformFunction: null }, zUserActions: { classPropertyName: "zUserActions", publicName: "zUserActions", isSignal: true, isRequired: false, transformFunction: null }, zActionsTemplate: { classPropertyName: "zActionsTemplate", publicName: "zActionsTemplate", isSignal: true, isRequired: false, transformFunction: null }, zCurrentPath: { classPropertyName: "zCurrentPath", publicName: "zCurrentPath", isSignal: true, isRequired: false, transformFunction: null }, zKey: { classPropertyName: "zKey", publicName: "zKey", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { zOnSelect: "zOnSelect", zOnMenuAction: "zOnMenuAction", zControl: "zControl" }, host: { properties: { "class.z-menu-collapsed": "sidebarCollapsed()", "class.z-menu-no-child": "selectedParentHasNoChild()", "class.z-menu-mobile": "isMobile()", "class.z-menu-mobile-open": "mobileMenuOpen()" }, classAttribute: "z-menu block" }, ngImport: i0, template: "<!-- DESKTOP LAYOUT - Hidden on mobile -->\n<div class=\"z-menu-desktop relative z-200 hidden h-full py-1 pl-1 md:block\" [class.collapsed]=\"sidebarCollapsed()\">\n <main class=\"z-menu-main relative flex h-full\">\n <div class=\"z-sidebar-main h-full\">\n <div\n class=\"z-shadow-menu bg-sidebar border-border/40 dark:border-sidebar-border relative flex h-full w-[50px] flex-col items-center gap-1.5 rounded-[6px] border\"></div>\n\n <div class=\"bg-sidebar text-sidebar-foreground absolute inset-px z-20 flex flex-col rounded-[6px]\">\n @if (zLogo()) {\n <div class=\"border-sidebar-border flex h-[52px] items-center justify-center border-b px-2\">\n <img [src]=\"zLogo()\" alt=\"Logo\" class=\"h-8 w-auto object-contain\" />\n </div>\n }\n\n <!-- Expand Button - Only show when collapsed -->\n @if (sidebarCollapsed() && selectedParent()) {\n <div\n class=\"z-expand-btn hover:bg-sidebar-accent mx-auto mt-2 flex h-9 w-9 cursor-pointer items-center justify-center rounded-[6px]\"\n (click)=\"toggleSidebar()\">\n <z-icon zType=\"lucideChevronsRight\" zSize=\"20\" class=\"opacity-70\" />\n </div>\n }\n\n <div class=\"flex flex-1 flex-col items-center gap-2 p-2\">\n @for (item of menuParents(); track item.id) {\n @let hasActiveChild = parentWithActiveChild()?.id === item.id;\n @let isCurrentlySelected = selectedParent()?.id === item.id;\n @let showActiveIndicator = hasActiveChild && !isCurrentlySelected;\n <div\n zTooltip\n [zContent]=\"item.name\"\n zPosition=\"right\"\n [zArrow]=\"false\"\n [zAlwaysShow]=\"true\"\n class=\"relative flex h-9 w-9 cursor-pointer items-center justify-center rounded-[6px] select-none\"\n [class.bg-sidebar-primary]=\"selectedParent()?.id === item.id\"\n [class.text-sidebar-primary-foreground]=\"selectedParent()?.id === item.id\"\n [class.hover:bg-sidebar-accent]=\"selectedParent()?.id !== item.id\"\n (click)=\"onDesktopParentClick(item)\">\n @if (item.icon) {\n <z-icon [zType]=\"item.icon\" zSize=\"20\" />\n } @else {\n <z-icon [zSvg]=\"item.iconSvg || ''\" zSize=\"20\" />\n }\n @if (showActiveIndicator) {\n <div\n class=\"bg-sidebar-primary absolute top-1/2 -left-[5px] h-4 w-1 -translate-y-1/2 rounded-full\"></div>\n }\n </div>\n }\n </div>\n\n <div class=\"border-sidebar-border flex items-center justify-center border-t p-2\">\n <div\n z-popover\n [zOffset]=\"11\"\n [zPopoverContent]=\"userPopoverTpl\"\n zPosition=\"right-bottom\"\n class=\"aspect-square h-9 w-9 shrink-0 cursor-pointer overflow-hidden rounded-full\">\n <img [src]=\"avatarSrc()\" alt=\"User Avatar\" class=\"h-full w-full object-cover\" />\n </div>\n </div>\n </div>\n </div>\n\n <ng-template #userPopoverTpl let-close=\"close\">\n <div class=\"min-w-56 p-1\">\n <!-- User Info Header - Always shown -->\n <div class=\"p-0 text-sm font-normal\">\n <div class=\"flex items-center gap-2 px-1 py-1.5 text-left text-sm\">\n <div class=\"h-8 w-8 shrink-0 overflow-hidden rounded-full\">\n <img [src]=\"avatarSrc()\" alt=\"User Avatar\" class=\"aspect-square size-full object-cover\" />\n </div>\n <div class=\"grid flex-1 text-left text-sm leading-tight\">\n <span class=\"truncate font-medium\">{{ zUser()?.name || 'User Name' }}</span>\n <span class=\"text-muted-foreground mt-0.5 truncate text-xs\">\n {{ zUser()?.email || 'user@example.com' }}\n </span>\n </div>\n </div>\n </div>\n\n @if (getVisibleUserActions().length > 0 || zActionsTemplate()) {\n <div class=\"bg-border -mx-1 my-1 h-px\"></div>\n }\n\n <!-- Actions - Custom or from zUserActions -->\n @if (zActionsTemplate()) {\n <ng-container *ngTemplateOutlet=\"zActionsTemplate(); context: { close: close }\" />\n } @else if (getVisibleUserActions().length > 0) {\n @for (action of getVisibleUserActions(); track action.id; let idx = $index) {\n @if (shouldShowDividerBefore(action, idx)) {\n <div class=\"bg-border -mx-1 my-1 h-px\"></div>\n }\n\n <button\n type=\"button\"\n [disabled]=\"action.disabled\"\n [class]=\"action.class\"\n class=\"hover:bg-accent focus:bg-accent flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none disabled:pointer-events-none disabled:opacity-50\"\n (click)=\"onUserActionClick(action); close()\">\n @if (action.icon) {\n <z-icon [zType]=\"action.icon\" zSize=\"14\" class=\"text-muted-foreground\" />\n }\n <span>{{ action.label }}</span>\n </button>\n\n @if (shouldShowDividerAfter(action, idx)) {\n <div class=\"bg-border -mx-1 my-1 h-px\"></div>\n }\n }\n }\n </div>\n </ng-template>\n\n @if (selectedParent(); as parent) {\n @let hasChildrenOrTemplate = parent.menuTemplate || (parent.children && parent.children.length > 0);\n @if (hasChildrenOrTemplate) {\n <div\n class=\"z-sidebar-child-wrapper\"\n [class.collapsed]=\"sidebarCollapsed()\"\n [style.--sidebar-child-width.px]=\"sidebarChildWidth()\">\n <div\n class=\"z-sidebar-child z-shadow-menu bg-card text-card-foreground border-border/40 dark:border-sidebar-border flex h-full shrink-0 flex-col items-start rounded-[6px] border\"\n [style.width.px]=\"sidebarChildWidth()\"\n [class.collapsed]=\"sidebarCollapsed()\">\n <div class=\"border-border flex h-[52px] w-full shrink-0 items-center border-b px-3\">\n @if (selectedParent()?.icon) {\n <z-icon [zType]=\"selectedParent()!.icon\" zSize=\"20\" class=\"mr-2 shrink-0\" />\n }\n <div\n class=\"mr-4 min-w-0 flex-1 truncate text-base font-semibold\"\n zTooltip\n zPosition=\"top-left\"\n [zContent]=\"selectedParent()?.name || ''\"\n [zArrow]=\"false\">\n {{ selectedParent()?.name }}\n </div>\n\n <z-icon\n zType=\"lucidePanelRightOpen\"\n zSize=\"20\"\n class=\"shrink-0 cursor-pointer opacity-60 hover:opacity-100\"\n (click)=\"toggleSidebar()\" />\n </div>\n\n <!-- Show menuTemplate if available, otherwise show children -->\n @if (selectedParent()?.menuTemplate) {\n <ng-scrollbar class=\"z-menu-scrollbar min-h-0 w-full flex-1\" track=\"vertical\">\n <div class=\"flex w-full flex-col p-3 pr-2\">\n <ng-container\n *ngTemplateOutlet=\"\n selectedParent()!.menuTemplate!;\n context: { $implicit: selectedParent()!, close: closeSidebarFn }\n \" />\n </div>\n </ng-scrollbar>\n } @else {\n <ng-scrollbar class=\"z-menu-scrollbar min-h-0 w-full flex-1\" track=\"vertical\">\n <div class=\"flex w-full flex-col gap-1 py-1 pr-2 pl-2\">\n <ng-container *ngTemplateOutlet=\"menuChildrenTpl; context: { $implicit: menuChildren() }\" />\n </div>\n </ng-scrollbar>\n }\n </div>\n </div>\n }\n }\n </main>\n</div>\n\n<!-- MOBILE LAYOUT - Visible only on mobile -->\n<div class=\"z-menu-mobile-wrapper hidden max-md:block\">\n <!-- Mobile Backdrop -->\n @if (mobileMenuOpen() || mobileTemplateDrawerOpen()) {\n <div\n class=\"z-menu-backdrop fixed inset-0 z-9998\"\n [class.z-menu-backdrop-dark]=\"overlayType() === 'dark'\"\n [class.z-menu-backdrop-blur]=\"overlayType() === 'blur'\"\n (click)=\"onBackdropClick()\"></div>\n }\n\n <!-- Mobile Drawer -->\n <aside\n class=\"z-menu-drawer bg-background border-border fixed top-0 left-0 z-9999 flex h-full w-70 flex-col rounded-r-lg border-r\"\n [class.open]=\"mobileMenuOpen()\">\n <!-- Drawer Header -->\n <div class=\"border-border flex h-14 shrink-0 items-center justify-between border-b px-4\">\n @if (mobileLogo()) {\n <img [src]=\"mobileLogo()\" alt=\"Logo\" class=\"h-8 w-auto object-contain\" />\n }\n <button z-button zType=\"ghost\" [zWave]=\"false\" class=\"bg-accent h-9 w-9 shrink-0\" (click)=\"closeMobileMenu()\">\n <z-icon zType=\"lucideX\" zSize=\"20\" />\n </button>\n </div>\n\n <!-- Drawer Content - Accordion Menu -->\n <ng-scrollbar class=\"z-menu-scrollbar flex-1\" track=\"vertical\">\n <div class=\"p-3\">\n @for (parent of menuParents(); track parent.id) {\n <div class=\"mb-1\">\n <!-- Parent Item -->\n @let isParentActive =\n selectedMenuItem()?.id === parent.id && (!parent.children || parent.children.length === 0);\n @let hasActiveChild = parentWithActiveChild()?.id === parent.id && !isParentActive;\n @let isCurrentlyViewing = selectedParent()?.id === parent.id;\n <div\n class=\"relative flex cursor-pointer items-center gap-3 rounded-[6px] p-2\"\n [class.bg-primary/20]=\"isParentActive\"\n [class.text-primary]=\"isParentActive || hasActiveChild\"\n [class.hover:bg-accent]=\"!isParentActive\"\n (click)=\"onMobileParentClick(parent)\">\n @if (parent.icon) {\n <z-icon [zType]=\"parent.icon\" zSize=\"18\" class=\"shrink-0\" />\n } @else if (parent.iconSvg) {\n <z-icon [zSvg]=\"parent.iconSvg\" zSize=\"18\" class=\"shrink-0\" />\n }\n <span class=\"min-w-0 flex-1 truncate text-[13px] font-[450]\">{{ parent.name }}</span>\n @if ((parent.children && parent.children.length > 0) || parent.menuTemplate) {\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"16\"\n class=\"z-menu-arrow shrink-0 opacity-60 transition-transform\"\n [class.expanded]=\"parent.expanded && isCurrentlyViewing\" />\n }\n </div>\n\n <!-- Children (Accordion) - Only render if NO menuTemplate -->\n @if (parent.children && parent.children.length > 0 && !parent.menuTemplate) {\n <div class=\"z-menu-submenu\" [class.expanded]=\"parent.expanded && isCurrentlyViewing\">\n <div class=\"z-menu-submenu-inner\">\n <div class=\"z-menu-tree relative ml-4 flex flex-col gap-1 pt-1 pl-3\">\n <div class=\"absolute top-0 bottom-2 left-0 w-px bg-gray-300 dark:bg-gray-600\"></div>\n\n @for (child of parent.children; track child.id) {\n @let isChildActive =\n selectedMenuItem()?.id === child.id && (!child.children || child.children.length === 0);\n @let isChildParentOfActive = isParentOfActiveItem(child);\n @let hasChildren = child.children && child.children.length > 0;\n\n <div>\n <div\n class=\"relative flex cursor-pointer items-center gap-2 rounded-[6px] p-2\"\n [class.bg-primary]=\"isChildActive\"\n [class.text-primary-foreground]=\"isChildActive\"\n [class.text-primary]=\"isChildParentOfActive && !isChildActive\"\n [class.hover:bg-accent]=\"!isChildActive\"\n (click)=\"onMenuItemClick(child)\">\n <div class=\"absolute top-1/2 -left-3 h-px w-3 bg-gray-300 dark:bg-gray-600\"></div>\n @if (child.icon) {\n <z-icon [zType]=\"child.icon\" zSize=\"16\" class=\"shrink-0\" />\n } @else if (child.iconSvg) {\n <z-icon [zSvg]=\"child.iconSvg\" zSize=\"16\" class=\"shrink-0\" />\n } @else {\n <div\n class=\"h-1.5 w-1.5 shrink-0 rounded-full\"\n [class.bg-primary-foreground]=\"isChildActive\"\n [class.bg-gray-400]=\"!isChildActive\"></div>\n }\n <span class=\"min-w-0 flex-1 truncate text-[13px] font-[450]\">{{ child.name }}</span>\n @if (hasChildren) {\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"z-menu-arrow shrink-0 opacity-60 transition-transform\"\n [class.expanded]=\"child.expanded\" />\n }\n </div>\n\n <!-- Level 3 -->\n @if (hasChildren) {\n <div class=\"z-menu-submenu\" [class.expanded]=\"child.expanded\">\n <div class=\"z-menu-submenu-inner\">\n <div class=\"relative ml-4 flex flex-col gap-1 pt-1 pl-3\">\n <div class=\"absolute top-0 bottom-2 left-0 w-px bg-gray-300 dark:bg-gray-600\"></div>\n\n @for (subChild of child.children; track subChild.id) {\n @let isSubChildActive = selectedMenuItem()?.id === subChild.id;\n\n <div class=\"relative\">\n <div class=\"absolute top-1/2 -left-3 h-px w-3 bg-gray-300 dark:bg-gray-600\"></div>\n\n <div\n class=\"flex cursor-pointer items-center gap-2 rounded-[6px] p-2\"\n [class.bg-primary]=\"isSubChildActive\"\n [class.text-primary-foreground]=\"isSubChildActive\"\n [class.hover:bg-accent]=\"!isSubChildActive\"\n (click)=\"onMenuItemClick(subChild)\">\n <div\n class=\"h-1 w-1 shrink-0 rounded-full\"\n [class.bg-primary-foreground]=\"isSubChildActive\"\n [class.bg-gray-400]=\"!isSubChildActive\"></div>\n <span class=\"min-w-0 flex-1 truncate text-[13px] font-[450]\">\n {{ subChild.name }}\n </span>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n }\n </div>\n }\n </div>\n </div>\n </div>\n }\n </div>\n }\n </div>\n </ng-scrollbar>\n\n <!-- Drawer Footer - User Info -->\n <div class=\"border-border shrink-0 border-t p-3\">\n <div\n z-popover\n [zOffset]=\"8\"\n [zPopoverContent]=\"mobileActionsPopoverTpl\"\n zPosition=\"top-right\"\n zTrigger=\"click\"\n class=\"hover:bg-accent flex cursor-pointer items-center gap-3 rounded-[6px] p-2\">\n <div class=\"h-10 w-10 shrink-0 overflow-hidden rounded-full\">\n <img [src]=\"avatarSrc()\" alt=\"User Avatar\" class=\"h-full w-full object-cover\" />\n </div>\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-medium\">{{ zUser()?.name || 'User Name' }}</div>\n <div class=\"text-muted-foreground truncate text-xs\">{{ zUser()?.email || 'user@example.com' }}</div>\n </div>\n <div class=\"text-muted-foreground flex shrink-0 flex-col\">\n <z-icon zType=\"lucideChevronUp\" zSize=\"14\" />\n <z-icon zType=\"lucideChevronDown\" zSize=\"14\" class=\"-mt-1\" />\n </div>\n </div>\n </div>\n </aside>\n\n <!-- Mobile Custom Template Drawer -->\n <aside\n class=\"z-menu-custom-drawer bg-background border-border fixed top-0 left-0 z-9999 flex h-full max-w-[calc(100vw-16px)] flex-col rounded-r-lg border-r\"\n [style.width.px]=\"mobileCustomDrawerWidth()\"\n [class.open]=\"mobileTemplateDrawerOpen() && customDrawerParent()?.menuTemplate\">\n @if (customDrawerParent()?.menuTemplate) {\n <!-- Custom Drawer Header -->\n <div class=\"border-border flex h-14 shrink-0 items-center gap-3 border-b px-4\">\n <button\n z-button\n zType=\"ghost\"\n [zWave]=\"false\"\n class=\"bg-accent h-9 w-9 shrink-0\"\n (click)=\"closeMobileTemplateDrawer()\">\n <z-icon zType=\"lucideChevronLeft\" zSize=\"20\" />\n </button>\n <span class=\"min-w-0 flex-1 truncate text-base font-semibold\">\n {{ customDrawerParent()?.name }}\n </span>\n </div>\n\n <!-- Custom Template Content -->\n <ng-scrollbar class=\"z-menu-scrollbar flex-1\" track=\"vertical\">\n <div class=\"p-4\">\n <ng-container\n *ngTemplateOutlet=\"\n customDrawerParent()!.menuTemplate!;\n context: { $implicit: customDrawerParent()!, close: closeMobileDrawerFn }\n \" />\n </div>\n </ng-scrollbar>\n }\n </aside>\n\n <ng-template #mobileActionsPopoverTpl let-close=\"close\">\n <div class=\"min-w-48 p-1\">\n @if (zActionsTemplate()) {\n <ng-container *ngTemplateOutlet=\"zActionsTemplate(); context: { close: close }\" />\n } @else if (getVisibleUserActions().length > 0) {\n @for (action of getVisibleUserActions(); track action.id; let idx = $index) {\n @if (shouldShowDividerBefore(action, idx)) {\n <div class=\"bg-border -mx-1 my-1 h-px\"></div>\n }\n\n <button\n type=\"button\"\n [disabled]=\"action.disabled\"\n [class]=\"action.class\"\n class=\"hover:bg-accent focus:bg-accent flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none disabled:pointer-events-none disabled:opacity-50\"\n (click)=\"onUserActionClick(action); close()\">\n @if (action.icon) {\n <z-icon [zType]=\"action.icon\" zSize=\"14\" class=\"text-muted-foreground\" />\n }\n <span>{{ action.label }}</span>\n </button>\n\n @if (shouldShowDividerAfter(action, idx)) {\n <div class=\"bg-border -mx-1 my-1 h-px\"></div>\n }\n }\n }\n </div>\n </ng-template>\n</div>\n\n<!-- Shared Template for Menu Children -->\n<ng-template #menuChildrenTpl let-children>\n @for (child of children; track child.id) {\n @let isChildActive = selectedMenuItem()?.id === child.id && (!child.children || child.children.length === 0);\n @let isChildParentOfActive = isParentOfActiveItem(child);\n @let hasChildren = child.children && child.children.length > 0;\n <div>\n <div\n #divLevel1\n class=\"z-menu-item flex w-full cursor-pointer items-center gap-2 rounded-[6px] p-2\"\n [class.bg-primary]=\"isChildActive\"\n [class.text-primary-foreground]=\"isChildActive\"\n [class.text-primary]=\"isChildParentOfActive && !isChildActive\"\n [class.hover:bg-gray-200]=\"!isChildActive\"\n [class.dark:hover:bg-input/50]=\"!isChildActive\"\n (click)=\"onMenuItemClick(child)\">\n @if (child.icon) {\n <z-icon [zType]=\"child.icon\" zSize=\"18\" class=\"shrink-0\" />\n } @else if (child.iconSvg) {\n <z-icon [zSvg]=\"child.iconSvg\" zSize=\"18\" class=\"shrink-0\" />\n }\n <span\n class=\"min-w-0 flex-1 truncate text-[13px] font-[450]\"\n zTooltip\n [zContent]=\"child.name\"\n [zArrow]=\"false\"\n [zOffset]=\"10\"\n zMaxWidth=\"200px\"\n zPosition=\"right\"\n [zTriggerElement]=\"divLevel1\">\n {{ child.name }}\n </span>\n @if (hasChildren) {\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"z-menu-arrow shrink-0 opacity-60 transition-transform\"\n [class.expanded]=\"child.expanded\" />\n }\n </div>\n\n @if (hasChildren) {\n <div class=\"z-menu-submenu\" [class.expanded]=\"child.expanded\">\n <div class=\"z-menu-submenu-inner\">\n <div class=\"z-menu-tree relative ml-4 flex flex-col gap-1 pt-1 pl-3\">\n <div class=\"absolute top-0 bottom-2 left-0 w-px bg-gray-300 dark:bg-gray-600\"></div>\n\n @for (subChild of child.children; track subChild.id; let isLast = $last) {\n @let isSubChildActive = selectedMenuItem()?.id === subChild.id;\n <div class=\"relative\">\n <div class=\"absolute top-1/2 -left-3 h-px w-3 bg-gray-300 dark:bg-gray-600\"></div>\n\n <div\n #divLevel2\n class=\"z-menu-item flex w-full cursor-pointer items-center gap-2 rounded-[6px] p-2\"\n [class.bg-primary]=\"isSubChildActive\"\n [class.text-primary-foreground]=\"isSubChildActive\"\n [class.hover:bg-gray-200]=\"!isSubChildActive\"\n [class.dark:hover:bg-input/50]=\"!isSubChildActive\"\n (click)=\"onMenuItemClick(subChild)\">\n @if (subChild.icon) {\n <z-icon [zType]=\"subChild.icon\" zSize=\"16\" class=\"shrink-0\" />\n } @else if (subChild.iconSvg) {\n <z-icon [zSvg]=\"subChild.iconSvg\" zSize=\"16\" class=\"shrink-0\" />\n } @else {\n <div\n class=\"h-1 w-1 shrink-0 rounded-full\"\n [class.bg-primary-foreground]=\"isSubChildActive\"\n [class.bg-gray-400]=\"!isSubChildActive\"></div>\n }\n <span\n class=\"min-w-0 flex-1 truncate text-[13px] font-[450]\"\n zTooltip\n [zContent]=\"subChild.name\"\n [zArrow]=\"false\"\n [zOffset]=\"10\"\n zMaxWidth=\"200px\"\n zPosition=\"right\"\n [zTriggerElement]=\"divLevel2\">\n {{ subChild.name }}\n </span>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n }\n </div>\n }\n</ng-template>\n", styles: [".z-menu{display:block;flex-shrink:0;height:100%}.z-menu [class*=cursor-pointer]{-webkit-user-select:none;user-select:none}@media(min-width:768px){.z-menu{contain:layout style;min-width:286px;transition:min-width .25s cubic-bezier(.4,0,.2,1);will-change:min-width}.z-menu.z-menu-collapsed,.z-menu.z-menu-no-child{min-width:56px}}.z-shadow-menu{box-shadow:0 0 10px #0000001f}@media(max-width:767px){.z-menu-desktop{display:none!important;pointer-events:none;visibility:hidden}}.z-sidebar-main{position:relative;z-index:20;flex-shrink:0}.z-sidebar-child-wrapper{--sidebar-child-width: 230px;position:absolute;left:0;top:0;width:calc(100% + -0px);height:100%;clip-path:inset(0 -100px 0 0);pointer-events:none;transition:clip-path .25s cubic-bezier(.4,0,.2,1);will-change:clip-path;contain:layout}.z-sidebar-child-wrapper.collapsed{clip-path:inset(0 -100px 0 56px)}.z-sidebar-child{flex-shrink:0;height:100%;margin-left:54px;transform:translate(0);transition:transform .25s cubic-bezier(.4,0,.2,1),width .25s cubic-bezier(.4,0,.2,1);pointer-events:auto;will-change:transform,width}.z-sidebar-child.collapsed{transform:translate(calc(-1 * (var(--sidebar-child-width) + 54px + 5px)))}.z-menu-arrow.expanded{transform:rotate(90deg)}.z-menu-submenu{display:grid;grid-template-rows:0fr;transition:grid-template-rows .15s ease-out}.z-menu-submenu.expanded{grid-template-rows:1fr}.z-menu-submenu>.z-menu-submenu-inner{overflow:hidden;min-height:0}.z-menu-backdrop{animation:z-menu-backdrop-enter .2s ease-out forwards}.z-menu-backdrop-dark{background-color:#0009}.z-menu-backdrop-blur{background-color:#0000000d;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.z-menu-drawer{transform:translate(-100%);transition:transform .3s cubic-bezier(.32,.72,0,1);box-shadow:4px 0 24px #0000001a}.z-menu-drawer.open{transform:translate(0)}:host.z-menu-mobile-open .z-menu-drawer{transform:translate(0)}.z-menu-desktop.collapsed .z-sidebar-child-wrapper{pointer-events:none}.z-menu-custom-drawer{transform:translate(-100%);transition:transform .3s cubic-bezier(.32,.72,0,1);box-shadow:4px 0 24px #0000001a;will-change:transform}.z-menu-custom-drawer.open{transform:translate(0)}@keyframes z-menu-backdrop-enter{0%{opacity:0}to{opacity:1}}@keyframes z-expand-btn-enter{0%{opacity:0;transform:scale(.5) translateY(10px)}70%{transform:scale(1.1) translateY(-2px)}to{opacity:1;transform:scale(1) translateY(0)}}.z-expand-btn{animation:z-expand-btn-enter .25s cubic-bezier(.34,1.56,.64,1) forwards}.z-menu-scrollbar{--scrollbar-padding: 0}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ZButtonComponent, selector: "z-button, button[z-button], a[z-button]", inputs: ["class", "zType", "zSize", "zShape", "zLabel", "zLoading", "zDisabled", "zTypeIcon", "zSizeIcon", "zStrokeWidthIcon", "zWave"], exportAs: ["zButton"] }, { kind: "component", type: ZIconComponent, selector: "z-icon, [z-icon]", inputs: ["class", "zType", "zSize", "zStrokeWidth", "zSvg"] }, { kind: "directive", type: ZTooltipDirective, selector: "[z-tooltip], [zTooltip]", inputs: ["zContent", "zPosition", "zTrigger", "zTooltipType", "zTooltipSize", "zClass", "zShowDelay", "zHideDelay", "zArrow", "zDisabled", "zOffset", "zAutoDetect", "zTriggerElement", "zAlwaysShow", "zMaxWidth"], outputs: ["zShow", "zHide"], exportAs: ["zTooltip"] }, { kind: "directive", type: ZPopoverDirective, selector: "[z-popover]", inputs: ["zPopoverContent", "zPosition", "zTrigger", "zClass", "zShowDelay", "zHideDelay", "zDisabled", "zOffset", "zPopoverWidth", "zManualClose", "zScrollClose", "zShowArrow"], outputs: ["zShow", "zHide", "zHideStart", "zControl"], exportAs: ["zPopover"] }, { kind: "component", type: NgScrollbar, selector: "ng-scrollbar:not([externalViewport]), [ngScrollbar]", exportAs: ["ngScrollbar"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
354
372
  }
355
373
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZMenuComponent, decorators: [{
356
374
  type: Component,
@@ -360,8 +378,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
360
378
  '[class.z-menu-no-child]': 'selectedParentHasNoChild()',
361
379
  '[class.z-menu-mobile]': 'isMobile()',
362
380
  '[class.z-menu-mobile-open]': 'mobileMenuOpen()',
363
- }, template: "<!-- DESKTOP LAYOUT - Hidden on mobile -->\n<div class=\"z-menu-desktop relative z-200 hidden h-full py-1 pl-1 md:block\" [class.collapsed]=\"sidebarCollapsed()\">\n <main class=\"z-menu-main relative flex h-full\">\n <div class=\"z-sidebar-main h-full\">\n <div\n class=\"z-shadow-menu bg-sidebar border-border/40 dark:border-sidebar-border relative flex h-full w-[50px] flex-col items-center gap-1.5 rounded-[6px] border\"></div>\n\n <div class=\"bg-sidebar text-sidebar-foreground absolute inset-px z-20 flex flex-col rounded-[6px]\">\n @if (zLogo()) {\n <div class=\"border-sidebar-border flex h-[52px] items-center justify-center border-b px-2\">\n <img [src]=\"zLogo()\" alt=\"Logo\" class=\"h-8 w-auto object-contain\" />\n </div>\n }\n\n <!-- Expand Button - Only show when collapsed -->\n @if (sidebarCollapsed() && selectedParent()) {\n <div\n class=\"z-expand-btn hover:bg-sidebar-accent mx-auto mt-2 flex h-9 w-9 cursor-pointer items-center justify-center rounded-[6px]\"\n (click)=\"toggleSidebar()\">\n <z-icon zType=\"lucideChevronsRight\" zSize=\"20\" class=\"opacity-70\" />\n </div>\n }\n\n <div class=\"flex flex-1 flex-col items-center gap-2 p-2\">\n @for (item of menuParents(); track item.id) {\n @let hasActiveChild = parentWithActiveChild()?.id === item.id;\n @let isCurrentlySelected = selectedParent()?.id === item.id;\n @let showActiveIndicator = hasActiveChild && !isCurrentlySelected;\n <div\n zTooltip\n [zContent]=\"item.name\"\n zPosition=\"right\"\n [zArrow]=\"false\"\n [zAlwaysShow]=\"true\"\n class=\"relative flex h-9 w-9 cursor-pointer items-center justify-center rounded-[6px] select-none\"\n [class.bg-sidebar-primary]=\"selectedParent()?.id === item.id\"\n [class.text-sidebar-primary-foreground]=\"selectedParent()?.id === item.id\"\n [class.hover:bg-sidebar-accent]=\"selectedParent()?.id !== item.id\"\n (click)=\"onDesktopParentClick(item)\">\n @if (item.icon) {\n <z-icon [zType]=\"item.icon\" zSize=\"20\" />\n } @else {\n <z-icon [zSvg]=\"item.iconSvg || ''\" zSize=\"20\" />\n }\n @if (showActiveIndicator) {\n <div\n class=\"bg-sidebar-primary absolute top-1/2 -left-[5px] h-4 w-1 -translate-y-1/2 rounded-full\"></div>\n }\n </div>\n }\n </div>\n\n <div class=\"border-sidebar-border flex items-center justify-center border-t p-2\">\n <div\n z-popover\n [zOffset]=\"11\"\n [zPopoverContent]=\"userPopoverTpl\"\n zPosition=\"right-bottom\"\n class=\"aspect-square h-9 w-9 shrink-0 cursor-pointer overflow-hidden rounded-full\">\n <img [src]=\"avatarSrc()\" alt=\"User Avatar\" class=\"h-full w-full object-cover\" />\n </div>\n </div>\n </div>\n </div>\n\n <ng-template #userPopoverTpl let-close=\"close\">\n <div class=\"min-w-56 p-1\">\n <!-- User Info Header - Always shown -->\n <div class=\"p-0 text-sm font-normal\">\n <div class=\"flex items-center gap-2 px-1 py-1.5 text-left text-sm\">\n <div class=\"h-8 w-8 shrink-0 overflow-hidden rounded-full\">\n <img [src]=\"avatarSrc()\" alt=\"User Avatar\" class=\"aspect-square size-full object-cover\" />\n </div>\n <div class=\"grid flex-1 text-left text-sm leading-tight\">\n <span class=\"truncate font-medium\">{{ zUser()?.name || 'User Name' }}</span>\n <span class=\"text-muted-foreground mt-0.5 truncate text-xs\">\n {{ zUser()?.email || 'user@example.com' }}\n </span>\n </div>\n </div>\n </div>\n\n <div class=\"bg-border -mx-1 my-1 h-px\"></div>\n\n <!-- Actions - Custom or Default -->\n @if (zActionsTemplate()) {\n <ng-container *ngTemplateOutlet=\"zActionsTemplate(); context: { close: close }\" />\n } @else {\n <button\n class=\"hover:bg-accent focus:bg-accent flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none\"\n (click)=\"close()\">\n <z-icon zType=\"lucideSparkles\" zSize=\"14\" class=\"text-muted-foreground\" />\n <span>Upgrade to Pro</span>\n </button>\n\n <div class=\"bg-border -mx-1 my-1 h-px\"></div>\n\n <button\n class=\"hover:bg-accent focus:bg-accent flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none\"\n (click)=\"close()\">\n <z-icon zType=\"lucideBadgeCheck\" zSize=\"14\" class=\"text-muted-foreground\" />\n <span>Account</span>\n </button>\n <button\n class=\"hover:bg-accent focus:bg-accent flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none\"\n (click)=\"close()\">\n <z-icon zType=\"lucideCreditCard\" zSize=\"14\" class=\"text-muted-foreground\" />\n <span>Billing</span>\n </button>\n <button\n class=\"hover:bg-accent focus:bg-accent flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none\"\n (click)=\"close()\">\n <z-icon zType=\"lucideBell\" zSize=\"14\" class=\"text-muted-foreground\" />\n <span>Notifications</span>\n </button>\n\n <div class=\"bg-border -mx-1 my-1 h-px\"></div>\n\n <button\n class=\"hover:bg-accent focus:bg-accent flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none\"\n (click)=\"onLogoutClick(); close()\">\n <z-icon zType=\"lucideLogOut\" zSize=\"14\" class=\"text-muted-foreground\" />\n <span>Log out</span>\n </button>\n }\n </div>\n </ng-template>\n\n @if (selectedParent(); as parent) {\n @let hasChildrenOrTemplate = parent.menuTemplate || (parent.children && parent.children.length > 0);\n @if (hasChildrenOrTemplate) {\n <div\n class=\"z-sidebar-child-wrapper\"\n [class.collapsed]=\"sidebarCollapsed()\"\n [style.--sidebar-child-width.px]=\"sidebarChildWidth()\">\n <div\n class=\"z-sidebar-child z-shadow-menu bg-card text-card-foreground border-border/40 dark:border-sidebar-border flex h-full shrink-0 flex-col items-start rounded-[6px] border\"\n [style.width.px]=\"sidebarChildWidth()\"\n [class.collapsed]=\"sidebarCollapsed()\">\n <div class=\"border-border flex h-[52px] w-full shrink-0 items-center border-b px-3\">\n @if (selectedParent()?.icon) {\n <z-icon [zType]=\"selectedParent()!.icon\" zSize=\"20\" class=\"mr-2 shrink-0\" />\n }\n <div\n class=\"mr-4 min-w-0 flex-1 truncate text-base font-semibold\"\n zTooltip\n zPosition=\"top-left\"\n [zContent]=\"selectedParent()?.name || ''\"\n [zArrow]=\"false\">\n {{ selectedParent()?.name }}\n </div>\n\n <z-icon\n zType=\"lucidePanelRightOpen\"\n zSize=\"20\"\n class=\"shrink-0 cursor-pointer opacity-60 hover:opacity-100\"\n (click)=\"toggleSidebar()\" />\n </div>\n\n <!-- Show menuTemplate if available, otherwise show children -->\n @if (selectedParent()?.menuTemplate) {\n <ng-scrollbar class=\"z-menu-scrollbar min-h-0 w-full flex-1\" track=\"vertical\">\n <div class=\"flex w-full flex-col p-3 pr-2\">\n <ng-container\n *ngTemplateOutlet=\"\n selectedParent()!.menuTemplate!;\n context: { $implicit: selectedParent()!, close: closeSidebarFn }\n \" />\n </div>\n </ng-scrollbar>\n } @else {\n <ng-scrollbar class=\"z-menu-scrollbar min-h-0 w-full flex-1\" track=\"vertical\">\n <div class=\"flex w-full flex-col gap-1 py-1 pr-2 pl-2\">\n <ng-container *ngTemplateOutlet=\"menuChildrenTpl; context: { $implicit: menuChildren() }\" />\n </div>\n </ng-scrollbar>\n }\n </div>\n </div>\n }\n }\n </main>\n</div>\n\n<!-- MOBILE LAYOUT - Visible only on mobile -->\n<div class=\"z-menu-mobile-wrapper hidden max-md:block\">\n <!-- Mobile Backdrop -->\n @if (mobileMenuOpen() || mobileTemplateDrawerOpen()) {\n <div\n class=\"z-menu-backdrop fixed inset-0 z-9998\"\n [class.z-menu-backdrop-dark]=\"overlayType() === 'dark'\"\n [class.z-menu-backdrop-blur]=\"overlayType() === 'blur'\"\n (click)=\"onBackdropClick()\"></div>\n }\n\n <!-- Mobile Drawer -->\n <aside\n class=\"z-menu-drawer bg-background border-border fixed top-0 left-0 z-9999 flex h-full w-70 flex-col rounded-r-lg border-r\"\n [class.open]=\"mobileMenuOpen()\">\n <!-- Drawer Header -->\n <div class=\"border-border flex h-14 shrink-0 items-center justify-between border-b px-4\">\n @if (zLogo()) {\n <img [src]=\"zLogo()\" alt=\"Logo\" class=\"h-8 w-auto object-contain\" />\n }\n <button z-button zType=\"ghost\" [zWave]=\"false\" class=\"bg-accent h-9 w-9 shrink-0\" (click)=\"closeMobileMenu()\">\n <z-icon zType=\"lucideX\" zSize=\"20\" />\n </button>\n </div>\n\n <!-- Drawer Content - Accordion Menu -->\n <ng-scrollbar class=\"z-menu-scrollbar flex-1\" track=\"vertical\">\n <div class=\"p-3\">\n @for (parent of menuParents(); track parent.id) {\n <div class=\"mb-1\">\n <!-- Parent Item -->\n @let isParentActive =\n selectedMenuItem()?.id === parent.id && (!parent.children || parent.children.length === 0);\n @let hasActiveChild = parentWithActiveChild()?.id === parent.id && !isParentActive;\n @let isCurrentlyViewing = selectedParent()?.id === parent.id;\n <div\n class=\"relative flex cursor-pointer items-center gap-3 rounded-[6px] p-2\"\n [class.bg-primary/20]=\"isParentActive\"\n [class.text-primary]=\"isParentActive || hasActiveChild\"\n [class.hover:bg-accent]=\"!isParentActive\"\n (click)=\"onMobileParentClick(parent)\">\n @if (parent.icon) {\n <z-icon [zType]=\"parent.icon\" zSize=\"18\" class=\"shrink-0\" />\n } @else if (parent.iconSvg) {\n <z-icon [zSvg]=\"parent.iconSvg\" zSize=\"18\" class=\"shrink-0\" />\n }\n <span class=\"min-w-0 flex-1 truncate text-[13px] font-[450]\">{{ parent.name }}</span>\n @if ((parent.children && parent.children.length > 0) || parent.menuTemplate) {\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"16\"\n class=\"z-menu-arrow shrink-0 opacity-60 transition-transform\"\n [class.expanded]=\"parent.expanded && isCurrentlyViewing\" />\n }\n </div>\n\n <!-- Children (Accordion) - Only render if NO menuTemplate -->\n @if (parent.children && parent.children.length > 0 && !parent.menuTemplate) {\n <div class=\"z-menu-submenu\" [class.expanded]=\"parent.expanded && isCurrentlyViewing\">\n <div class=\"z-menu-submenu-inner\">\n <div class=\"z-menu-tree relative ml-4 flex flex-col gap-1 pt-1 pl-3\">\n <div class=\"absolute top-0 bottom-2 left-0 w-px bg-gray-300 dark:bg-gray-600\"></div>\n\n @for (child of parent.children; track child.id) {\n @let isChildActive =\n selectedMenuItem()?.id === child.id && (!child.children || child.children.length === 0);\n @let isChildParentOfActive = isParentOfActiveItem(child);\n @let hasChildren = child.children && child.children.length > 0;\n\n <div>\n <div\n class=\"relative flex cursor-pointer items-center gap-2 rounded-[6px] p-2\"\n [class.bg-primary]=\"isChildActive\"\n [class.text-primary-foreground]=\"isChildActive\"\n [class.text-primary]=\"isChildParentOfActive && !isChildActive\"\n [class.hover:bg-accent]=\"!isChildActive\"\n (click)=\"onMenuItemClick(child)\">\n <div class=\"absolute top-1/2 -left-3 h-px w-3 bg-gray-300 dark:bg-gray-600\"></div>\n @if (child.icon) {\n <z-icon [zType]=\"child.icon\" zSize=\"16\" class=\"shrink-0\" />\n } @else if (child.iconSvg) {\n <z-icon [zSvg]=\"child.iconSvg\" zSize=\"16\" class=\"shrink-0\" />\n } @else {\n <div\n class=\"h-1.5 w-1.5 shrink-0 rounded-full\"\n [class.bg-primary-foreground]=\"isChildActive\"\n [class.bg-gray-400]=\"!isChildActive\"></div>\n }\n <span class=\"min-w-0 flex-1 truncate text-[13px] font-[450]\">{{ child.name }}</span>\n @if (hasChildren) {\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"z-menu-arrow shrink-0 opacity-60 transition-transform\"\n [class.expanded]=\"child.expanded\" />\n }\n </div>\n\n <!-- Level 3 -->\n @if (hasChildren) {\n <div class=\"z-menu-submenu\" [class.expanded]=\"child.expanded\">\n <div class=\"z-menu-submenu-inner\">\n <div class=\"relative ml-4 flex flex-col gap-1 pt-1 pl-3\">\n <div class=\"absolute top-0 bottom-2 left-0 w-px bg-gray-300 dark:bg-gray-600\"></div>\n\n @for (subChild of child.children; track subChild.id) {\n @let isSubChildActive = selectedMenuItem()?.id === subChild.id;\n\n <div class=\"relative\">\n <div class=\"absolute top-1/2 -left-3 h-px w-3 bg-gray-300 dark:bg-gray-600\"></div>\n\n <div\n class=\"flex cursor-pointer items-center gap-2 rounded-[6px] p-2\"\n [class.bg-primary]=\"isSubChildActive\"\n [class.text-primary-foreground]=\"isSubChildActive\"\n [class.hover:bg-accent]=\"!isSubChildActive\"\n (click)=\"onMenuItemClick(subChild)\">\n <div\n class=\"h-1 w-1 shrink-0 rounded-full\"\n [class.bg-primary-foreground]=\"isSubChildActive\"\n [class.bg-gray-400]=\"!isSubChildActive\"></div>\n <span class=\"min-w-0 flex-1 truncate text-[13px] font-[450]\">\n {{ subChild.name }}\n </span>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n }\n </div>\n }\n </div>\n </div>\n </div>\n }\n </div>\n }\n </div>\n </ng-scrollbar>\n\n <!-- Drawer Footer - User Info -->\n <div class=\"border-border shrink-0 border-t p-3\">\n <div\n z-popover\n [zOffset]=\"8\"\n [zPopoverContent]=\"mobileActionsPopoverTpl\"\n zPosition=\"top-right\"\n zTrigger=\"click\"\n class=\"hover:bg-accent flex cursor-pointer items-center gap-3 rounded-[6px] p-2\">\n <div class=\"h-10 w-10 shrink-0 overflow-hidden rounded-full\">\n <img [src]=\"avatarSrc()\" alt=\"User Avatar\" class=\"h-full w-full object-cover\" />\n </div>\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-medium\">{{ zUser()?.name || 'User Name' }}</div>\n <div class=\"text-muted-foreground truncate text-xs\">{{ zUser()?.email || 'user@example.com' }}</div>\n </div>\n <div class=\"text-muted-foreground flex shrink-0 flex-col\">\n <z-icon zType=\"lucideChevronUp\" zSize=\"14\" />\n <z-icon zType=\"lucideChevronDown\" zSize=\"14\" class=\"-mt-1\" />\n </div>\n </div>\n </div>\n </aside>\n\n <!-- Mobile Custom Template Drawer -->\n <aside\n class=\"z-menu-custom-drawer bg-background border-border fixed top-0 left-0 z-9999 flex h-full max-w-[calc(100vw-16px)] flex-col rounded-r-lg border-r\"\n [style.width.px]=\"mobileCustomDrawerWidth()\"\n [class.open]=\"mobileTemplateDrawerOpen() && customDrawerParent()?.menuTemplate\">\n @if (customDrawerParent()?.menuTemplate) {\n <!-- Custom Drawer Header -->\n <div class=\"border-border flex h-14 shrink-0 items-center gap-3 border-b px-4\">\n <button\n z-button\n zType=\"ghost\"\n [zWave]=\"false\"\n class=\"bg-accent h-9 w-9 shrink-0\"\n (click)=\"closeMobileTemplateDrawer()\">\n <z-icon zType=\"lucideChevronLeft\" zSize=\"20\" />\n </button>\n <span class=\"min-w-0 flex-1 truncate text-base font-semibold\">\n {{ customDrawerParent()?.name }}\n </span>\n </div>\n\n <!-- Custom Template Content -->\n <ng-scrollbar class=\"z-menu-scrollbar flex-1\" track=\"vertical\">\n <div class=\"p-4\">\n <ng-container\n *ngTemplateOutlet=\"\n customDrawerParent()!.menuTemplate!;\n context: { $implicit: customDrawerParent()!, close: closeMobileDrawerFn }\n \" />\n </div>\n </ng-scrollbar>\n }\n </aside>\n\n <ng-template #mobileActionsPopoverTpl let-close=\"close\">\n <div class=\"min-w-48 p-1\">\n @if (zActionsTemplate()) {\n <ng-container *ngTemplateOutlet=\"zActionsTemplate(); context: { close: close }\" />\n } @else {\n <button\n class=\"hover:bg-accent focus:bg-accent flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none\"\n (click)=\"close()\">\n <z-icon zType=\"lucideSparkles\" zSize=\"14\" class=\"text-muted-foreground\" />\n <span>Upgrade to Pro</span>\n </button>\n\n <div class=\"bg-border -mx-1 my-1 h-px\"></div>\n\n <button\n class=\"hover:bg-accent focus:bg-accent flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none\"\n (click)=\"close()\">\n <z-icon zType=\"lucideBadgeCheck\" zSize=\"14\" class=\"text-muted-foreground\" />\n <span>Account</span>\n </button>\n <button\n class=\"hover:bg-accent focus:bg-accent flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none\"\n (click)=\"close()\">\n <z-icon zType=\"lucideCreditCard\" zSize=\"14\" class=\"text-muted-foreground\" />\n <span>Billing</span>\n </button>\n <button\n class=\"hover:bg-accent focus:bg-accent flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none\"\n (click)=\"close()\">\n <z-icon zType=\"lucideBell\" zSize=\"14\" class=\"text-muted-foreground\" />\n <span>Notifications</span>\n </button>\n\n <div class=\"bg-border -mx-1 my-1 h-px\"></div>\n\n <button\n class=\"hover:bg-accent focus:bg-accent flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none\"\n (click)=\"onLogoutClick(); close()\">\n <z-icon zType=\"lucideLogOut\" zSize=\"14\" class=\"text-muted-foreground\" />\n <span>Log out</span>\n </button>\n }\n </div>\n </ng-template>\n</div>\n\n<!-- Shared Template for Menu Children -->\n<ng-template #menuChildrenTpl let-children>\n @for (child of children; track child.id) {\n @let isChildActive = selectedMenuItem()?.id === child.id && (!child.children || child.children.length === 0);\n @let isChildParentOfActive = isParentOfActiveItem(child);\n @let hasChildren = child.children && child.children.length > 0;\n <div>\n <div\n #divLevel1\n class=\"z-menu-item flex w-full cursor-pointer items-center gap-2 rounded-[6px] p-2\"\n [class.bg-primary]=\"isChildActive\"\n [class.text-primary-foreground]=\"isChildActive\"\n [class.text-primary]=\"isChildParentOfActive && !isChildActive\"\n [class.hover:bg-gray-200]=\"!isChildActive\"\n [class.dark:hover:bg-input/50]=\"!isChildActive\"\n (click)=\"onMenuItemClick(child)\">\n @if (child.icon) {\n <z-icon [zType]=\"child.icon\" zSize=\"18\" class=\"shrink-0\" />\n } @else if (child.iconSvg) {\n <z-icon [zSvg]=\"child.iconSvg\" zSize=\"18\" class=\"shrink-0\" />\n }\n <span\n class=\"min-w-0 flex-1 truncate text-[13px] font-[450]\"\n zTooltip\n [zContent]=\"child.name\"\n [zArrow]=\"false\"\n [zOffset]=\"10\"\n zMaxWidth=\"200px\"\n zPosition=\"right\"\n [zTriggerElement]=\"divLevel1\">\n {{ child.name }}\n </span>\n @if (hasChildren) {\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"z-menu-arrow shrink-0 opacity-60 transition-transform\"\n [class.expanded]=\"child.expanded\" />\n }\n </div>\n\n @if (hasChildren) {\n <div class=\"z-menu-submenu\" [class.expanded]=\"child.expanded\">\n <div class=\"z-menu-submenu-inner\">\n <div class=\"z-menu-tree relative ml-4 flex flex-col gap-1 pt-1 pl-3\">\n <div class=\"absolute top-0 bottom-2 left-0 w-px bg-gray-300 dark:bg-gray-600\"></div>\n\n @for (subChild of child.children; track subChild.id; let isLast = $last) {\n @let isSubChildActive = selectedMenuItem()?.id === subChild.id;\n <div class=\"relative\">\n <div class=\"absolute top-1/2 -left-3 h-px w-3 bg-gray-300 dark:bg-gray-600\"></div>\n\n <div\n #divLevel2\n class=\"z-menu-item flex w-full cursor-pointer items-center gap-2 rounded-[6px] p-2\"\n [class.bg-primary]=\"isSubChildActive\"\n [class.text-primary-foreground]=\"isSubChildActive\"\n [class.hover:bg-gray-200]=\"!isSubChildActive\"\n [class.dark:hover:bg-input/50]=\"!isSubChildActive\"\n (click)=\"onMenuItemClick(subChild)\">\n @if (subChild.icon) {\n <z-icon [zType]=\"subChild.icon\" zSize=\"16\" class=\"shrink-0\" />\n } @else if (subChild.iconSvg) {\n <z-icon [zSvg]=\"subChild.iconSvg\" zSize=\"16\" class=\"shrink-0\" />\n } @else {\n <div\n class=\"h-1 w-1 shrink-0 rounded-full\"\n [class.bg-primary-foreground]=\"isSubChildActive\"\n [class.bg-gray-400]=\"!isSubChildActive\"></div>\n }\n <span\n class=\"min-w-0 flex-1 truncate text-[13px] font-[450]\"\n zTooltip\n [zContent]=\"subChild.name\"\n [zArrow]=\"false\"\n [zOffset]=\"10\"\n zMaxWidth=\"200px\"\n zPosition=\"right\"\n [zTriggerElement]=\"divLevel2\">\n {{ subChild.name }}\n </span>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n }\n </div>\n }\n</ng-template>\n", styles: [".z-menu{display:block;flex-shrink:0;height:100%}.z-menu [class*=cursor-pointer]{-webkit-user-select:none;user-select:none}@media(min-width:768px){.z-menu{contain:layout style;min-width:286px;transition:min-width .25s cubic-bezier(.4,0,.2,1);will-change:min-width}.z-menu.z-menu-collapsed,.z-menu.z-menu-no-child{min-width:56px}}.z-shadow-menu{box-shadow:0 0 10px #0000001f}@media(max-width:767px){.z-menu-desktop{display:none!important;pointer-events:none;visibility:hidden}}.z-sidebar-main{position:relative;z-index:20;flex-shrink:0}.z-sidebar-child-wrapper{--sidebar-child-width: 230px;position:absolute;left:0;top:0;width:calc(100% + -0px);height:100%;clip-path:inset(0 -100px 0 0);pointer-events:none;transition:clip-path .25s cubic-bezier(.4,0,.2,1);will-change:clip-path;contain:layout}.z-sidebar-child-wrapper.collapsed{clip-path:inset(0 -100px 0 56px)}.z-sidebar-child{flex-shrink:0;height:100%;margin-left:54px;transform:translate(0);transition:transform .25s cubic-bezier(.4,0,.2,1),width .25s cubic-bezier(.4,0,.2,1);pointer-events:auto;will-change:transform,width}.z-sidebar-child.collapsed{transform:translate(calc(-1 * (var(--sidebar-child-width) + 54px + 5px)))}.z-menu-arrow.expanded{transform:rotate(90deg)}.z-menu-submenu{display:grid;grid-template-rows:0fr;transition:grid-template-rows .15s ease-out}.z-menu-submenu.expanded{grid-template-rows:1fr}.z-menu-submenu>.z-menu-submenu-inner{overflow:hidden;min-height:0}.z-menu-backdrop{animation:z-menu-backdrop-enter .2s ease-out forwards}.z-menu-backdrop-dark{background-color:#0009}.z-menu-backdrop-blur{background-color:#0000000d;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.z-menu-drawer{transform:translate(-100%);transition:transform .3s cubic-bezier(.32,.72,0,1);box-shadow:4px 0 24px #0000001a}.z-menu-drawer.open{transform:translate(0)}:host.z-menu-mobile-open .z-menu-drawer{transform:translate(0)}.z-menu-desktop.collapsed .z-sidebar-child-wrapper{pointer-events:none}.z-menu-custom-drawer{transform:translate(-100%);transition:transform .3s cubic-bezier(.32,.72,0,1);box-shadow:4px 0 24px #0000001a;will-change:transform}.z-menu-custom-drawer.open{transform:translate(0)}@keyframes z-menu-backdrop-enter{0%{opacity:0}to{opacity:1}}@keyframes z-expand-btn-enter{0%{opacity:0;transform:scale(.5) translateY(10px)}70%{transform:scale(1.1) translateY(-2px)}to{opacity:1;transform:scale(1) translateY(0)}}.z-expand-btn{animation:z-expand-btn-enter .25s cubic-bezier(.34,1.56,.64,1) forwards}.z-menu-scrollbar{--scrollbar-padding: 0}\n"] }]
364
- }], ctorParameters: () => [], propDecorators: { zLogo: [{ type: i0.Input, args: [{ isSignal: true, alias: "zLogo", required: false }] }], zMenus: [{ type: i0.Input, args: [{ isSignal: true, alias: "zMenus", required: false }] }], zUser: [{ type: i0.Input, args: [{ isSignal: true, alias: "zUser", required: false }] }], zActionsTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "zActionsTemplate", required: false }] }], zCurrentPath: [{ type: i0.Input, args: [{ isSignal: true, alias: "zCurrentPath", required: false }] }], zKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "zKey", required: false }] }], zOnSelect: [{ type: i0.Output, args: ["zOnSelect"] }], zOnLogout: [{ type: i0.Output, args: ["zOnLogout"] }], zControl: [{ type: i0.Output, args: ["zControl"] }] } });
381
+ }, template: "<!-- DESKTOP LAYOUT - Hidden on mobile -->\n<div class=\"z-menu-desktop relative z-200 hidden h-full py-1 pl-1 md:block\" [class.collapsed]=\"sidebarCollapsed()\">\n <main class=\"z-menu-main relative flex h-full\">\n <div class=\"z-sidebar-main h-full\">\n <div\n class=\"z-shadow-menu bg-sidebar border-border/40 dark:border-sidebar-border relative flex h-full w-[50px] flex-col items-center gap-1.5 rounded-[6px] border\"></div>\n\n <div class=\"bg-sidebar text-sidebar-foreground absolute inset-px z-20 flex flex-col rounded-[6px]\">\n @if (zLogo()) {\n <div class=\"border-sidebar-border flex h-[52px] items-center justify-center border-b px-2\">\n <img [src]=\"zLogo()\" alt=\"Logo\" class=\"h-8 w-auto object-contain\" />\n </div>\n }\n\n <!-- Expand Button - Only show when collapsed -->\n @if (sidebarCollapsed() && selectedParent()) {\n <div\n class=\"z-expand-btn hover:bg-sidebar-accent mx-auto mt-2 flex h-9 w-9 cursor-pointer items-center justify-center rounded-[6px]\"\n (click)=\"toggleSidebar()\">\n <z-icon zType=\"lucideChevronsRight\" zSize=\"20\" class=\"opacity-70\" />\n </div>\n }\n\n <div class=\"flex flex-1 flex-col items-center gap-2 p-2\">\n @for (item of menuParents(); track item.id) {\n @let hasActiveChild = parentWithActiveChild()?.id === item.id;\n @let isCurrentlySelected = selectedParent()?.id === item.id;\n @let showActiveIndicator = hasActiveChild && !isCurrentlySelected;\n <div\n zTooltip\n [zContent]=\"item.name\"\n zPosition=\"right\"\n [zArrow]=\"false\"\n [zAlwaysShow]=\"true\"\n class=\"relative flex h-9 w-9 cursor-pointer items-center justify-center rounded-[6px] select-none\"\n [class.bg-sidebar-primary]=\"selectedParent()?.id === item.id\"\n [class.text-sidebar-primary-foreground]=\"selectedParent()?.id === item.id\"\n [class.hover:bg-sidebar-accent]=\"selectedParent()?.id !== item.id\"\n (click)=\"onDesktopParentClick(item)\">\n @if (item.icon) {\n <z-icon [zType]=\"item.icon\" zSize=\"20\" />\n } @else {\n <z-icon [zSvg]=\"item.iconSvg || ''\" zSize=\"20\" />\n }\n @if (showActiveIndicator) {\n <div\n class=\"bg-sidebar-primary absolute top-1/2 -left-[5px] h-4 w-1 -translate-y-1/2 rounded-full\"></div>\n }\n </div>\n }\n </div>\n\n <div class=\"border-sidebar-border flex items-center justify-center border-t p-2\">\n <div\n z-popover\n [zOffset]=\"11\"\n [zPopoverContent]=\"userPopoverTpl\"\n zPosition=\"right-bottom\"\n class=\"aspect-square h-9 w-9 shrink-0 cursor-pointer overflow-hidden rounded-full\">\n <img [src]=\"avatarSrc()\" alt=\"User Avatar\" class=\"h-full w-full object-cover\" />\n </div>\n </div>\n </div>\n </div>\n\n <ng-template #userPopoverTpl let-close=\"close\">\n <div class=\"min-w-56 p-1\">\n <!-- User Info Header - Always shown -->\n <div class=\"p-0 text-sm font-normal\">\n <div class=\"flex items-center gap-2 px-1 py-1.5 text-left text-sm\">\n <div class=\"h-8 w-8 shrink-0 overflow-hidden rounded-full\">\n <img [src]=\"avatarSrc()\" alt=\"User Avatar\" class=\"aspect-square size-full object-cover\" />\n </div>\n <div class=\"grid flex-1 text-left text-sm leading-tight\">\n <span class=\"truncate font-medium\">{{ zUser()?.name || 'User Name' }}</span>\n <span class=\"text-muted-foreground mt-0.5 truncate text-xs\">\n {{ zUser()?.email || 'user@example.com' }}\n </span>\n </div>\n </div>\n </div>\n\n @if (getVisibleUserActions().length > 0 || zActionsTemplate()) {\n <div class=\"bg-border -mx-1 my-1 h-px\"></div>\n }\n\n <!-- Actions - Custom or from zUserActions -->\n @if (zActionsTemplate()) {\n <ng-container *ngTemplateOutlet=\"zActionsTemplate(); context: { close: close }\" />\n } @else if (getVisibleUserActions().length > 0) {\n @for (action of getVisibleUserActions(); track action.id; let idx = $index) {\n @if (shouldShowDividerBefore(action, idx)) {\n <div class=\"bg-border -mx-1 my-1 h-px\"></div>\n }\n\n <button\n type=\"button\"\n [disabled]=\"action.disabled\"\n [class]=\"action.class\"\n class=\"hover:bg-accent focus:bg-accent flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none disabled:pointer-events-none disabled:opacity-50\"\n (click)=\"onUserActionClick(action); close()\">\n @if (action.icon) {\n <z-icon [zType]=\"action.icon\" zSize=\"14\" class=\"text-muted-foreground\" />\n }\n <span>{{ action.label }}</span>\n </button>\n\n @if (shouldShowDividerAfter(action, idx)) {\n <div class=\"bg-border -mx-1 my-1 h-px\"></div>\n }\n }\n }\n </div>\n </ng-template>\n\n @if (selectedParent(); as parent) {\n @let hasChildrenOrTemplate = parent.menuTemplate || (parent.children && parent.children.length > 0);\n @if (hasChildrenOrTemplate) {\n <div\n class=\"z-sidebar-child-wrapper\"\n [class.collapsed]=\"sidebarCollapsed()\"\n [style.--sidebar-child-width.px]=\"sidebarChildWidth()\">\n <div\n class=\"z-sidebar-child z-shadow-menu bg-card text-card-foreground border-border/40 dark:border-sidebar-border flex h-full shrink-0 flex-col items-start rounded-[6px] border\"\n [style.width.px]=\"sidebarChildWidth()\"\n [class.collapsed]=\"sidebarCollapsed()\">\n <div class=\"border-border flex h-[52px] w-full shrink-0 items-center border-b px-3\">\n @if (selectedParent()?.icon) {\n <z-icon [zType]=\"selectedParent()!.icon\" zSize=\"20\" class=\"mr-2 shrink-0\" />\n }\n <div\n class=\"mr-4 min-w-0 flex-1 truncate text-base font-semibold\"\n zTooltip\n zPosition=\"top-left\"\n [zContent]=\"selectedParent()?.name || ''\"\n [zArrow]=\"false\">\n {{ selectedParent()?.name }}\n </div>\n\n <z-icon\n zType=\"lucidePanelRightOpen\"\n zSize=\"20\"\n class=\"shrink-0 cursor-pointer opacity-60 hover:opacity-100\"\n (click)=\"toggleSidebar()\" />\n </div>\n\n <!-- Show menuTemplate if available, otherwise show children -->\n @if (selectedParent()?.menuTemplate) {\n <ng-scrollbar class=\"z-menu-scrollbar min-h-0 w-full flex-1\" track=\"vertical\">\n <div class=\"flex w-full flex-col p-3 pr-2\">\n <ng-container\n *ngTemplateOutlet=\"\n selectedParent()!.menuTemplate!;\n context: { $implicit: selectedParent()!, close: closeSidebarFn }\n \" />\n </div>\n </ng-scrollbar>\n } @else {\n <ng-scrollbar class=\"z-menu-scrollbar min-h-0 w-full flex-1\" track=\"vertical\">\n <div class=\"flex w-full flex-col gap-1 py-1 pr-2 pl-2\">\n <ng-container *ngTemplateOutlet=\"menuChildrenTpl; context: { $implicit: menuChildren() }\" />\n </div>\n </ng-scrollbar>\n }\n </div>\n </div>\n }\n }\n </main>\n</div>\n\n<!-- MOBILE LAYOUT - Visible only on mobile -->\n<div class=\"z-menu-mobile-wrapper hidden max-md:block\">\n <!-- Mobile Backdrop -->\n @if (mobileMenuOpen() || mobileTemplateDrawerOpen()) {\n <div\n class=\"z-menu-backdrop fixed inset-0 z-9998\"\n [class.z-menu-backdrop-dark]=\"overlayType() === 'dark'\"\n [class.z-menu-backdrop-blur]=\"overlayType() === 'blur'\"\n (click)=\"onBackdropClick()\"></div>\n }\n\n <!-- Mobile Drawer -->\n <aside\n class=\"z-menu-drawer bg-background border-border fixed top-0 left-0 z-9999 flex h-full w-70 flex-col rounded-r-lg border-r\"\n [class.open]=\"mobileMenuOpen()\">\n <!-- Drawer Header -->\n <div class=\"border-border flex h-14 shrink-0 items-center justify-between border-b px-4\">\n @if (mobileLogo()) {\n <img [src]=\"mobileLogo()\" alt=\"Logo\" class=\"h-8 w-auto object-contain\" />\n }\n <button z-button zType=\"ghost\" [zWave]=\"false\" class=\"bg-accent h-9 w-9 shrink-0\" (click)=\"closeMobileMenu()\">\n <z-icon zType=\"lucideX\" zSize=\"20\" />\n </button>\n </div>\n\n <!-- Drawer Content - Accordion Menu -->\n <ng-scrollbar class=\"z-menu-scrollbar flex-1\" track=\"vertical\">\n <div class=\"p-3\">\n @for (parent of menuParents(); track parent.id) {\n <div class=\"mb-1\">\n <!-- Parent Item -->\n @let isParentActive =\n selectedMenuItem()?.id === parent.id && (!parent.children || parent.children.length === 0);\n @let hasActiveChild = parentWithActiveChild()?.id === parent.id && !isParentActive;\n @let isCurrentlyViewing = selectedParent()?.id === parent.id;\n <div\n class=\"relative flex cursor-pointer items-center gap-3 rounded-[6px] p-2\"\n [class.bg-primary/20]=\"isParentActive\"\n [class.text-primary]=\"isParentActive || hasActiveChild\"\n [class.hover:bg-accent]=\"!isParentActive\"\n (click)=\"onMobileParentClick(parent)\">\n @if (parent.icon) {\n <z-icon [zType]=\"parent.icon\" zSize=\"18\" class=\"shrink-0\" />\n } @else if (parent.iconSvg) {\n <z-icon [zSvg]=\"parent.iconSvg\" zSize=\"18\" class=\"shrink-0\" />\n }\n <span class=\"min-w-0 flex-1 truncate text-[13px] font-[450]\">{{ parent.name }}</span>\n @if ((parent.children && parent.children.length > 0) || parent.menuTemplate) {\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"16\"\n class=\"z-menu-arrow shrink-0 opacity-60 transition-transform\"\n [class.expanded]=\"parent.expanded && isCurrentlyViewing\" />\n }\n </div>\n\n <!-- Children (Accordion) - Only render if NO menuTemplate -->\n @if (parent.children && parent.children.length > 0 && !parent.menuTemplate) {\n <div class=\"z-menu-submenu\" [class.expanded]=\"parent.expanded && isCurrentlyViewing\">\n <div class=\"z-menu-submenu-inner\">\n <div class=\"z-menu-tree relative ml-4 flex flex-col gap-1 pt-1 pl-3\">\n <div class=\"absolute top-0 bottom-2 left-0 w-px bg-gray-300 dark:bg-gray-600\"></div>\n\n @for (child of parent.children; track child.id) {\n @let isChildActive =\n selectedMenuItem()?.id === child.id && (!child.children || child.children.length === 0);\n @let isChildParentOfActive = isParentOfActiveItem(child);\n @let hasChildren = child.children && child.children.length > 0;\n\n <div>\n <div\n class=\"relative flex cursor-pointer items-center gap-2 rounded-[6px] p-2\"\n [class.bg-primary]=\"isChildActive\"\n [class.text-primary-foreground]=\"isChildActive\"\n [class.text-primary]=\"isChildParentOfActive && !isChildActive\"\n [class.hover:bg-accent]=\"!isChildActive\"\n (click)=\"onMenuItemClick(child)\">\n <div class=\"absolute top-1/2 -left-3 h-px w-3 bg-gray-300 dark:bg-gray-600\"></div>\n @if (child.icon) {\n <z-icon [zType]=\"child.icon\" zSize=\"16\" class=\"shrink-0\" />\n } @else if (child.iconSvg) {\n <z-icon [zSvg]=\"child.iconSvg\" zSize=\"16\" class=\"shrink-0\" />\n } @else {\n <div\n class=\"h-1.5 w-1.5 shrink-0 rounded-full\"\n [class.bg-primary-foreground]=\"isChildActive\"\n [class.bg-gray-400]=\"!isChildActive\"></div>\n }\n <span class=\"min-w-0 flex-1 truncate text-[13px] font-[450]\">{{ child.name }}</span>\n @if (hasChildren) {\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"z-menu-arrow shrink-0 opacity-60 transition-transform\"\n [class.expanded]=\"child.expanded\" />\n }\n </div>\n\n <!-- Level 3 -->\n @if (hasChildren) {\n <div class=\"z-menu-submenu\" [class.expanded]=\"child.expanded\">\n <div class=\"z-menu-submenu-inner\">\n <div class=\"relative ml-4 flex flex-col gap-1 pt-1 pl-3\">\n <div class=\"absolute top-0 bottom-2 left-0 w-px bg-gray-300 dark:bg-gray-600\"></div>\n\n @for (subChild of child.children; track subChild.id) {\n @let isSubChildActive = selectedMenuItem()?.id === subChild.id;\n\n <div class=\"relative\">\n <div class=\"absolute top-1/2 -left-3 h-px w-3 bg-gray-300 dark:bg-gray-600\"></div>\n\n <div\n class=\"flex cursor-pointer items-center gap-2 rounded-[6px] p-2\"\n [class.bg-primary]=\"isSubChildActive\"\n [class.text-primary-foreground]=\"isSubChildActive\"\n [class.hover:bg-accent]=\"!isSubChildActive\"\n (click)=\"onMenuItemClick(subChild)\">\n <div\n class=\"h-1 w-1 shrink-0 rounded-full\"\n [class.bg-primary-foreground]=\"isSubChildActive\"\n [class.bg-gray-400]=\"!isSubChildActive\"></div>\n <span class=\"min-w-0 flex-1 truncate text-[13px] font-[450]\">\n {{ subChild.name }}\n </span>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n }\n </div>\n }\n </div>\n </div>\n </div>\n }\n </div>\n }\n </div>\n </ng-scrollbar>\n\n <!-- Drawer Footer - User Info -->\n <div class=\"border-border shrink-0 border-t p-3\">\n <div\n z-popover\n [zOffset]=\"8\"\n [zPopoverContent]=\"mobileActionsPopoverTpl\"\n zPosition=\"top-right\"\n zTrigger=\"click\"\n class=\"hover:bg-accent flex cursor-pointer items-center gap-3 rounded-[6px] p-2\">\n <div class=\"h-10 w-10 shrink-0 overflow-hidden rounded-full\">\n <img [src]=\"avatarSrc()\" alt=\"User Avatar\" class=\"h-full w-full object-cover\" />\n </div>\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-medium\">{{ zUser()?.name || 'User Name' }}</div>\n <div class=\"text-muted-foreground truncate text-xs\">{{ zUser()?.email || 'user@example.com' }}</div>\n </div>\n <div class=\"text-muted-foreground flex shrink-0 flex-col\">\n <z-icon zType=\"lucideChevronUp\" zSize=\"14\" />\n <z-icon zType=\"lucideChevronDown\" zSize=\"14\" class=\"-mt-1\" />\n </div>\n </div>\n </div>\n </aside>\n\n <!-- Mobile Custom Template Drawer -->\n <aside\n class=\"z-menu-custom-drawer bg-background border-border fixed top-0 left-0 z-9999 flex h-full max-w-[calc(100vw-16px)] flex-col rounded-r-lg border-r\"\n [style.width.px]=\"mobileCustomDrawerWidth()\"\n [class.open]=\"mobileTemplateDrawerOpen() && customDrawerParent()?.menuTemplate\">\n @if (customDrawerParent()?.menuTemplate) {\n <!-- Custom Drawer Header -->\n <div class=\"border-border flex h-14 shrink-0 items-center gap-3 border-b px-4\">\n <button\n z-button\n zType=\"ghost\"\n [zWave]=\"false\"\n class=\"bg-accent h-9 w-9 shrink-0\"\n (click)=\"closeMobileTemplateDrawer()\">\n <z-icon zType=\"lucideChevronLeft\" zSize=\"20\" />\n </button>\n <span class=\"min-w-0 flex-1 truncate text-base font-semibold\">\n {{ customDrawerParent()?.name }}\n </span>\n </div>\n\n <!-- Custom Template Content -->\n <ng-scrollbar class=\"z-menu-scrollbar flex-1\" track=\"vertical\">\n <div class=\"p-4\">\n <ng-container\n *ngTemplateOutlet=\"\n customDrawerParent()!.menuTemplate!;\n context: { $implicit: customDrawerParent()!, close: closeMobileDrawerFn }\n \" />\n </div>\n </ng-scrollbar>\n }\n </aside>\n\n <ng-template #mobileActionsPopoverTpl let-close=\"close\">\n <div class=\"min-w-48 p-1\">\n @if (zActionsTemplate()) {\n <ng-container *ngTemplateOutlet=\"zActionsTemplate(); context: { close: close }\" />\n } @else if (getVisibleUserActions().length > 0) {\n @for (action of getVisibleUserActions(); track action.id; let idx = $index) {\n @if (shouldShowDividerBefore(action, idx)) {\n <div class=\"bg-border -mx-1 my-1 h-px\"></div>\n }\n\n <button\n type=\"button\"\n [disabled]=\"action.disabled\"\n [class]=\"action.class\"\n class=\"hover:bg-accent focus:bg-accent flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none disabled:pointer-events-none disabled:opacity-50\"\n (click)=\"onUserActionClick(action); close()\">\n @if (action.icon) {\n <z-icon [zType]=\"action.icon\" zSize=\"14\" class=\"text-muted-foreground\" />\n }\n <span>{{ action.label }}</span>\n </button>\n\n @if (shouldShowDividerAfter(action, idx)) {\n <div class=\"bg-border -mx-1 my-1 h-px\"></div>\n }\n }\n }\n </div>\n </ng-template>\n</div>\n\n<!-- Shared Template for Menu Children -->\n<ng-template #menuChildrenTpl let-children>\n @for (child of children; track child.id) {\n @let isChildActive = selectedMenuItem()?.id === child.id && (!child.children || child.children.length === 0);\n @let isChildParentOfActive = isParentOfActiveItem(child);\n @let hasChildren = child.children && child.children.length > 0;\n <div>\n <div\n #divLevel1\n class=\"z-menu-item flex w-full cursor-pointer items-center gap-2 rounded-[6px] p-2\"\n [class.bg-primary]=\"isChildActive\"\n [class.text-primary-foreground]=\"isChildActive\"\n [class.text-primary]=\"isChildParentOfActive && !isChildActive\"\n [class.hover:bg-gray-200]=\"!isChildActive\"\n [class.dark:hover:bg-input/50]=\"!isChildActive\"\n (click)=\"onMenuItemClick(child)\">\n @if (child.icon) {\n <z-icon [zType]=\"child.icon\" zSize=\"18\" class=\"shrink-0\" />\n } @else if (child.iconSvg) {\n <z-icon [zSvg]=\"child.iconSvg\" zSize=\"18\" class=\"shrink-0\" />\n }\n <span\n class=\"min-w-0 flex-1 truncate text-[13px] font-[450]\"\n zTooltip\n [zContent]=\"child.name\"\n [zArrow]=\"false\"\n [zOffset]=\"10\"\n zMaxWidth=\"200px\"\n zPosition=\"right\"\n [zTriggerElement]=\"divLevel1\">\n {{ child.name }}\n </span>\n @if (hasChildren) {\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"z-menu-arrow shrink-0 opacity-60 transition-transform\"\n [class.expanded]=\"child.expanded\" />\n }\n </div>\n\n @if (hasChildren) {\n <div class=\"z-menu-submenu\" [class.expanded]=\"child.expanded\">\n <div class=\"z-menu-submenu-inner\">\n <div class=\"z-menu-tree relative ml-4 flex flex-col gap-1 pt-1 pl-3\">\n <div class=\"absolute top-0 bottom-2 left-0 w-px bg-gray-300 dark:bg-gray-600\"></div>\n\n @for (subChild of child.children; track subChild.id; let isLast = $last) {\n @let isSubChildActive = selectedMenuItem()?.id === subChild.id;\n <div class=\"relative\">\n <div class=\"absolute top-1/2 -left-3 h-px w-3 bg-gray-300 dark:bg-gray-600\"></div>\n\n <div\n #divLevel2\n class=\"z-menu-item flex w-full cursor-pointer items-center gap-2 rounded-[6px] p-2\"\n [class.bg-primary]=\"isSubChildActive\"\n [class.text-primary-foreground]=\"isSubChildActive\"\n [class.hover:bg-gray-200]=\"!isSubChildActive\"\n [class.dark:hover:bg-input/50]=\"!isSubChildActive\"\n (click)=\"onMenuItemClick(subChild)\">\n @if (subChild.icon) {\n <z-icon [zType]=\"subChild.icon\" zSize=\"16\" class=\"shrink-0\" />\n } @else if (subChild.iconSvg) {\n <z-icon [zSvg]=\"subChild.iconSvg\" zSize=\"16\" class=\"shrink-0\" />\n } @else {\n <div\n class=\"h-1 w-1 shrink-0 rounded-full\"\n [class.bg-primary-foreground]=\"isSubChildActive\"\n [class.bg-gray-400]=\"!isSubChildActive\"></div>\n }\n <span\n class=\"min-w-0 flex-1 truncate text-[13px] font-[450]\"\n zTooltip\n [zContent]=\"subChild.name\"\n [zArrow]=\"false\"\n [zOffset]=\"10\"\n zMaxWidth=\"200px\"\n zPosition=\"right\"\n [zTriggerElement]=\"divLevel2\">\n {{ subChild.name }}\n </span>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n }\n </div>\n }\n</ng-template>\n", styles: [".z-menu{display:block;flex-shrink:0;height:100%}.z-menu [class*=cursor-pointer]{-webkit-user-select:none;user-select:none}@media(min-width:768px){.z-menu{contain:layout style;min-width:286px;transition:min-width .25s cubic-bezier(.4,0,.2,1);will-change:min-width}.z-menu.z-menu-collapsed,.z-menu.z-menu-no-child{min-width:56px}}.z-shadow-menu{box-shadow:0 0 10px #0000001f}@media(max-width:767px){.z-menu-desktop{display:none!important;pointer-events:none;visibility:hidden}}.z-sidebar-main{position:relative;z-index:20;flex-shrink:0}.z-sidebar-child-wrapper{--sidebar-child-width: 230px;position:absolute;left:0;top:0;width:calc(100% + -0px);height:100%;clip-path:inset(0 -100px 0 0);pointer-events:none;transition:clip-path .25s cubic-bezier(.4,0,.2,1);will-change:clip-path;contain:layout}.z-sidebar-child-wrapper.collapsed{clip-path:inset(0 -100px 0 56px)}.z-sidebar-child{flex-shrink:0;height:100%;margin-left:54px;transform:translate(0);transition:transform .25s cubic-bezier(.4,0,.2,1),width .25s cubic-bezier(.4,0,.2,1);pointer-events:auto;will-change:transform,width}.z-sidebar-child.collapsed{transform:translate(calc(-1 * (var(--sidebar-child-width) + 54px + 5px)))}.z-menu-arrow.expanded{transform:rotate(90deg)}.z-menu-submenu{display:grid;grid-template-rows:0fr;transition:grid-template-rows .15s ease-out}.z-menu-submenu.expanded{grid-template-rows:1fr}.z-menu-submenu>.z-menu-submenu-inner{overflow:hidden;min-height:0}.z-menu-backdrop{animation:z-menu-backdrop-enter .2s ease-out forwards}.z-menu-backdrop-dark{background-color:#0009}.z-menu-backdrop-blur{background-color:#0000000d;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.z-menu-drawer{transform:translate(-100%);transition:transform .3s cubic-bezier(.32,.72,0,1);box-shadow:4px 0 24px #0000001a}.z-menu-drawer.open{transform:translate(0)}:host.z-menu-mobile-open .z-menu-drawer{transform:translate(0)}.z-menu-desktop.collapsed .z-sidebar-child-wrapper{pointer-events:none}.z-menu-custom-drawer{transform:translate(-100%);transition:transform .3s cubic-bezier(.32,.72,0,1);box-shadow:4px 0 24px #0000001a;will-change:transform}.z-menu-custom-drawer.open{transform:translate(0)}@keyframes z-menu-backdrop-enter{0%{opacity:0}to{opacity:1}}@keyframes z-expand-btn-enter{0%{opacity:0;transform:scale(.5) translateY(10px)}70%{transform:scale(1.1) translateY(-2px)}to{opacity:1;transform:scale(1) translateY(0)}}.z-expand-btn{animation:z-expand-btn-enter .25s cubic-bezier(.34,1.56,.64,1) forwards}.z-menu-scrollbar{--scrollbar-padding: 0}\n"] }]
382
+ }], ctorParameters: () => [], propDecorators: { zLogo: [{ type: i0.Input, args: [{ isSignal: true, alias: "zLogo", required: false }] }], zLogoMobile: [{ type: i0.Input, args: [{ isSignal: true, alias: "zLogoMobile", required: false }] }], zMenus: [{ type: i0.Input, args: [{ isSignal: true, alias: "zMenus", required: false }] }], zUser: [{ type: i0.Input, args: [{ isSignal: true, alias: "zUser", required: false }] }], zUserActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "zUserActions", required: false }] }], zActionsTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "zActionsTemplate", required: false }] }], zCurrentPath: [{ type: i0.Input, args: [{ isSignal: true, alias: "zCurrentPath", required: false }] }], zKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "zKey", required: false }] }], zOnSelect: [{ type: i0.Output, args: ["zOnSelect"] }], zOnMenuAction: [{ type: i0.Output, args: ["zOnMenuAction"] }], zControl: [{ type: i0.Output, args: ["zControl"] }] } });
365
383
 
366
384
  /**
367
385
  * Generated bundle index. Do not edit.