@egose/shadcn-theme-ng 0.1.0 → 0.2.0

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.
Files changed (87) hide show
  1. package/README.md +1 -1
  2. package/accordion/README.md +405 -2
  3. package/alert/README.md +372 -2
  4. package/alert-dialog/README.md +471 -5
  5. package/aspect-ratio/README.md +272 -5
  6. package/autocomplete/README.md +502 -2
  7. package/avatar/README.md +357 -5
  8. package/badge/README.md +318 -2
  9. package/basic-alert/README.md +353 -2
  10. package/breadcrumb/README.md +406 -5
  11. package/button/README.md +482 -2
  12. package/button/fesm2022/button.mjs +85 -107
  13. package/button/types/button.d.ts +5 -8
  14. package/button-group/README.md +318 -5
  15. package/calendar/README.md +357 -2
  16. package/card/README.md +331 -5
  17. package/carousel/README.md +333 -5
  18. package/carousel/fesm2022/carousel.mjs +4 -1
  19. package/checkbox/README.md +320 -2
  20. package/collapsible/README.md +332 -5
  21. package/combobox/README.md +507 -5
  22. package/combobox/fesm2022/combobox.mjs +4 -1
  23. package/command/README.md +435 -5
  24. package/confirmation-dialog/README.md +301 -2
  25. package/context-menu/README.md +366 -5
  26. package/date-picker/README.md +465 -2
  27. package/date-picker/fesm2022/date-picker.mjs +2 -2
  28. package/dialog/README.md +448 -2
  29. package/drawer/README.md +395 -5
  30. package/dropdown-menu/README.md +417 -5
  31. package/empty/README.md +329 -5
  32. package/field/README.md +385 -5
  33. package/form-checkbox/README.md +312 -2
  34. package/form-date-picker/README.md +322 -2
  35. package/form-field/README.md +356 -2
  36. package/form-field-simple/README.md +340 -2
  37. package/form-searchable-multiselect/README.md +361 -2
  38. package/form-select/README.md +350 -2
  39. package/form-text-input/README.md +371 -2
  40. package/form-textarea/README.md +347 -2
  41. package/hover-card/README.md +256 -5
  42. package/icon/README.md +239 -2
  43. package/input/README.md +269 -2
  44. package/input-group/README.md +335 -5
  45. package/input-group/fesm2022/input-group.mjs +3 -3
  46. package/input-otp/README.md +375 -5
  47. package/item/README.md +385 -5
  48. package/item/fesm2022/item.mjs +3 -3
  49. package/kbd/README.md +291 -5
  50. package/label/README.md +272 -2
  51. package/layout-simple/README.md +193 -2
  52. package/layout-simple/fesm2022/layout-simple.mjs +472 -236
  53. package/layout-simple/types/layout-simple.d.ts +174 -137
  54. package/menu/README.md +417 -2
  55. package/menubar/README.md +343 -5
  56. package/native-select/README.md +323 -5
  57. package/navigation-menu/README.md +369 -5
  58. package/package.json +1 -1
  59. package/pagination/README.md +388 -5
  60. package/popover/README.md +331 -2
  61. package/progress/README.md +311 -5
  62. package/radio-group/README.md +364 -2
  63. package/radio-group/fesm2022/radio-group.mjs +5 -1
  64. package/radio-group/types/radio-group.d.ts +1 -1
  65. package/resizable/README.md +269 -5
  66. package/scroll-area/README.md +233 -5
  67. package/searchable-multiselect/README.md +323 -2
  68. package/select/README.md +437 -2
  69. package/separator/README.md +222 -2
  70. package/sheet/README.md +311 -2
  71. package/sidebar/README.md +457 -5
  72. package/skeleton/README.md +217 -5
  73. package/slider/README.md +273 -5
  74. package/slider/fesm2022/slider.mjs +3 -3
  75. package/sonner/README.md +346 -2
  76. package/spinner/README.md +284 -2
  77. package/switch/README.md +310 -2
  78. package/table/README.md +423 -5
  79. package/tabs/README.md +411 -2
  80. package/tabs/fesm2022/tabs.mjs +2 -2
  81. package/textarea/README.md +282 -5
  82. package/toggle/README.md +270 -5
  83. package/toggle-group/README.md +340 -5
  84. package/tooltip/README.md +269 -2
  85. package/typography/README.md +271 -5
  86. package/typography/types/typography.d.ts +8 -8
  87. package/utils/README.md +303 -2
@@ -1,44 +1,56 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, Component, signal, computed, output, ChangeDetectionStrategy, viewChild, effect, inject, DestroyRef } from '@angular/core';
2
+ import { input, ChangeDetectionStrategy, Component, output, signal, viewChild, viewChildren, effect, computed, inject, ElementRef, Injector, afterNextRender, DestroyRef } from '@angular/core';
3
+ import * as i1 from '@angular/common';
4
+ import { CommonModule, NgTemplateOutlet } from '@angular/common';
3
5
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
- import { RouterLink, RouterModule } from '@angular/router';
6
+ import { RouterLink, RouterLinkActive, Router, NavigationEnd } from '@angular/router';
7
+ import { BreakpointObserver } from '@angular/cdk/layout';
8
+ import { _IdGenerator } from '@angular/cdk/a11y';
5
9
  import { NgIcon, provideIcons } from '@ng-icons/core';
6
- import { lucideUser, lucideSearch, lucideMenu } from '@ng-icons/lucide';
10
+ import { lucideUser, lucideSearch, lucideChevronDown, lucideX, lucideMenu, lucidePanelLeft } from '@ng-icons/lucide';
7
11
  import { hlm } from '@egose/shadcn-theme-ng/utils';
8
- import * as i4 from '@egose/shadcn-theme-ng/button';
9
- import { HlmButton, HlmButtonImports } from '@egose/shadcn-theme-ng/button';
10
- import * as i1 from '@angular/common';
11
- import { CommonModule } from '@angular/common';
12
12
  import { HlmDropdownMenu, HlmDropdownMenuTrigger, HlmDropdownMenuItem, HlmDropdownMenuLabel, HlmDropdownMenuSeparator, HlmDropdownMenuGroup } from '@egose/shadcn-theme-ng/dropdown-menu';
13
- import * as i1$1 from '@angular/forms';
14
- import { FormsModule } from '@angular/forms';
13
+ import * as i3 from '@egose/shadcn-theme-ng/button';
14
+ import { HlmButton, HlmButtonImports } from '@egose/shadcn-theme-ng/button';
15
15
  import { HlmPopover, HlmPopoverTrigger, HlmPopoverContent, HlmPopoverPortal } from '@egose/shadcn-theme-ng/popover';
16
16
  import { HlmInput } from '@egose/shadcn-theme-ng/input';
17
- import * as i2 from '@spartan-ng/brain/sheet';
17
+ import * as i1$1 from '@spartan-ng/brain/sheet';
18
18
  import { BrnSheet, BrnSheetImports } from '@spartan-ng/brain/sheet';
19
- import { HlmLabelImports } from '@egose/shadcn-theme-ng/label';
20
- import * as i3 from '@egose/shadcn-theme-ng/sheet';
19
+ import * as i2 from '@egose/shadcn-theme-ng/sheet';
21
20
  import { HlmSheetImports } from '@egose/shadcn-theme-ng/sheet';
22
- import * as i1$2 from '@angular/cdk/layout';
21
+
22
+ /** Internal matching contract shared by links and dropdown destinations. */
23
+ function navigationMatchOptions(item) {
24
+ return {
25
+ paths: item.activeMatch === 'prefix' ? 'subset' : 'exact',
26
+ queryParams: 'ignored',
27
+ fragment: 'ignored',
28
+ matrixParams: 'ignored',
29
+ };
30
+ }
23
31
 
24
32
  class EgLayoutSimpleUserMenu {
25
33
  hlm = hlm;
26
- lucideUser = lucideUser;
27
- menus = input([], /* @ts-ignore */
28
- ...(ngDevMode ? [{ debugName: "menus" }] : /* istanbul ignore next */ []));
29
- menuTrigger = input(/* @ts-ignore */
30
- ...(ngDevMode ? [undefined, { debugName: "menuTrigger" }] : /* istanbul ignore next */ []));
34
+ navigationMatchOptions = navigationMatchOptions;
35
+ userIcon = lucideUser;
36
+ sections = input([], /* @ts-ignore */
37
+ ...(ngDevMode ? [{ debugName: "sections" }] : /* istanbul ignore next */ []));
38
+ triggerTemplate = input(/* @ts-ignore */
39
+ ...(ngDevMode ? [undefined, { debugName: "triggerTemplate" }] : /* istanbul ignore next */ []));
40
+ triggerLabel = input('Open account menu', /* @ts-ignore */
41
+ ...(ngDevMode ? [{ debugName: "triggerLabel" }] : /* istanbul ignore next */ []));
31
42
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgLayoutSimpleUserMenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
32
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgLayoutSimpleUserMenu, isStandalone: true, selector: "eg-layout-simple-user-menu", inputs: { menus: { classPropertyName: "menus", publicName: "menus", isSignal: true, isRequired: false, transformFunction: null }, menuTrigger: { classPropertyName: "menuTrigger", publicName: "menuTrigger", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
43
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgLayoutSimpleUserMenu, isStandalone: true, selector: "eg-layout-simple-user-menu", inputs: { sections: { classPropertyName: "sections", publicName: "sections", isSignal: true, isRequired: false, transformFunction: null }, triggerTemplate: { classPropertyName: "triggerTemplate", publicName: "triggerTemplate", isSignal: true, isRequired: false, transformFunction: null }, triggerLabel: { classPropertyName: "triggerLabel", publicName: "triggerLabel", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
33
44
  provideIcons({
34
45
  lucideUser,
35
46
  }),
36
- ], ngImport: i0, template: "\n <ng-template #iconTemplate>\n <ng-icon [svg]=\"lucideUser\" size=\"1.5rem\" class=\"[&_svg]:w-[inherit]! [&_svg]:h-[inherit]!\" />\n </ng-template>\n\n @let _menuTrigger = menuTrigger();\n\n <div class=\"flex w-full items-center justify-center\">\n @if (_menuTrigger) {\n <button\n type=\"button\"\n [hlmDropdownMenuTrigger]=\"menu\"\n class=\"cursor-pointer bg-transparent border-0 p-0\"\n >\n <ng-container *ngTemplateOutlet=\"_menuTrigger\" />\n </button>\n } @else {\n <button\n hlmButton\n variant=\"primary\"\n size=\"icon\"\n [hlmDropdownMenuTrigger]=\"menu\"\n [icon]=\"iconTemplate\"\n class=\"rounded-full border border-gray-400\"\n ></button>\n }\n\n <ng-template #menu>\n <div hlmDropdownMenu class=\"w-56\">\n @for (section of menus(); track section) {\n @if (section.label) {\n <div hlmDropdownMenuLabel>{{ section.label }}</div>\n }\n @if (section.items?.length) {\n <div hlmDropdownMenuGroup>\n @for (item of section.items; track item) {\n @if (item.link) {\n <a\n hlmDropdownMenuItem\n [routerLink]=\"item.link\"\n [class]=\"hlm('tw:cursor-pointer tw:no-underline', item.class)\"\n >\n @if (item.icon) {\n <ng-icon\n [svg]=\"item.icon\"\n size=\"1rem\"\n [class]=\"\n hlm(\n 'tw:[_svg]:text-[inherit]! tw:[_svg]:bg-[inherit]! tw:cursor-pointer tw:mr-2',\n item.class\n )\n \"\n />\n }\n <span>{{ item.label }}</span>\n </a>\n } @else {\n <button\n hlmDropdownMenuItem\n (click)=\"item.action?.()\"\n [class]=\"hlm('tw:cursor-pointer tw:no-underline', item.class)\"\n >\n @if (item.icon) {\n <ng-icon\n [svg]=\"item.icon\"\n size=\"1rem\"\n [class]=\"\n hlm(\n 'tw:[_svg]:text-[inherit]! tw:[_svg]:bg-[inherit]! tw:cursor-pointer tw:mr-2',\n item.class\n )\n \"\n />\n }\n <span>{{ item.label }}</span>\n </button>\n }\n }\n </div>\n }\n @if (section.separator) {\n <div hlmDropdownMenuSeparator></div>\n }\n }\n </div>\n </ng-template>\n </div>\n ", isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: HlmDropdownMenu, selector: "[hlmDropdownMenu],hlm-dropdown-menu", inputs: ["sideOffset"] }, { kind: "directive", type: HlmDropdownMenuTrigger, selector: "[hlmDropdownMenuTrigger]", inputs: ["align", "side"] }, { kind: "directive", type: HlmDropdownMenuItem, selector: "[hlmDropdownMenuItem],hlm-dropdown-menu-item", inputs: ["disabled", "variant", "inset"] }, { kind: "directive", type: HlmDropdownMenuLabel, selector: "[hlmDropdownMenuLabel],hlm-dropdown-menu-label", inputs: ["inset"] }, { kind: "directive", type: HlmDropdownMenuSeparator, selector: "[hlmDropdownMenuSeparator],hlm-dropdown-menu-separator" }, { kind: "directive", type: HlmDropdownMenuGroup, selector: "[hlmDropdownMenuGroup],hlm-dropdown-menu-group" }, { kind: "component", type: HlmButton, selector: "button[hlmButton], a[hlmButton]", inputs: ["variant", "size", "appearance", "loading", "icon", "iconPosition", "class", "spinnerUserClass", "disabled", "type"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }] });
47
+ ], ngImport: i0, template: "\n <ng-template #iconTemplate>\n <ng-icon [svg]=\"userIcon\" size=\"1.25rem\" aria-hidden=\"true\" />\n </ng-template>\n\n @let customTrigger = triggerTemplate();\n\n <div class=\"flex w-full items-center justify-center\">\n @if (customTrigger) {\n <button\n type=\"button\"\n [hlmDropdownMenuTrigger]=\"menu\"\n [attr.aria-label]=\"triggerLabel()\"\n class=\"min-h-11 min-w-11 rounded-full cursor-pointer bg-transparent border-0 p-0 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\"\n >\n <ng-container *ngTemplateOutlet=\"customTrigger\" />\n </button>\n } @else {\n <button\n hlmButton\n type=\"button\"\n variant=\"secondary\"\n appearance=\"outline\"\n size=\"icon\"\n [hlmDropdownMenuTrigger]=\"menu\"\n [icon]=\"iconTemplate\"\n [attr.aria-label]=\"triggerLabel()\"\n class=\"size-11 rounded-full border border-border text-foreground\"\n ></button>\n }\n\n <ng-template #menu>\n <div hlmDropdownMenu class=\"w-56\">\n @for (section of sections(); track $index) {\n @if (section.label) {\n <div hlmDropdownMenuLabel>{{ section.label }}</div>\n }\n @if (section.items?.length) {\n <div hlmDropdownMenuGroup>\n @for (item of section.items; track item) {\n @if (item.link && !item.disabled) {\n <a\n hlmDropdownMenuItem\n [routerLink]=\"item.link\"\n routerLinkActive=\"tw:bg-foreground/10 tw:font-semibold\"\n [routerLinkActiveOptions]=\"navigationMatchOptions(item)\"\n ariaCurrentWhenActive=\"page\"\n [class]=\"hlm('tw:min-h-11 tw:cursor-pointer tw:no-underline', item.class)\"\n >\n @if (item.icon) {\n <ng-icon\n [svg]=\"item.icon\"\n aria-hidden=\"true\"\n size=\"1rem\"\n [class]=\"\n hlm(\n 'tw:[_svg]:text-[inherit]! tw:[_svg]:bg-[inherit]! tw:cursor-pointer tw:mr-2',\n item.class\n )\n \"\n />\n }\n <span>{{ item.label }}</span>\n </a>\n } @else {\n <button\n hlmDropdownMenuItem\n type=\"button\"\n [disabled]=\"item.disabled || !item.action\"\n (click)=\"item.action?.()\"\n [class]=\"hlm('tw:min-h-11 tw:cursor-pointer tw:no-underline', item.class)\"\n >\n @if (item.icon) {\n <ng-icon\n [svg]=\"item.icon\"\n aria-hidden=\"true\"\n size=\"1rem\"\n [class]=\"\n hlm(\n 'tw:[_svg]:text-[inherit]! tw:[_svg]:bg-[inherit]! tw:cursor-pointer tw:mr-2',\n item.class\n )\n \"\n />\n }\n <span>{{ item.label }}</span>\n </button>\n }\n }\n </div>\n }\n @if (section.separator) {\n <div hlmDropdownMenuSeparator></div>\n }\n }\n </div>\n </ng-template>\n </div>\n ", isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: HlmDropdownMenu, selector: "[hlmDropdownMenu],hlm-dropdown-menu", inputs: ["sideOffset"] }, { kind: "directive", type: HlmDropdownMenuTrigger, selector: "[hlmDropdownMenuTrigger]", inputs: ["align", "side"] }, { kind: "directive", type: HlmDropdownMenuItem, selector: "[hlmDropdownMenuItem],hlm-dropdown-menu-item", inputs: ["disabled", "variant", "inset"] }, { kind: "directive", type: HlmDropdownMenuLabel, selector: "[hlmDropdownMenuLabel],hlm-dropdown-menu-label", inputs: ["inset"] }, { kind: "directive", type: HlmDropdownMenuSeparator, selector: "[hlmDropdownMenuSeparator],hlm-dropdown-menu-separator" }, { kind: "directive", type: HlmDropdownMenuGroup, selector: "[hlmDropdownMenuGroup],hlm-dropdown-menu-group" }, { kind: "component", type: HlmButton, selector: "button[hlmButton], a[hlmButton]", inputs: ["variant", "size", "appearance", "loading", "icon", "iconPosition", "class", "spinnerUserClass", "disabled", "type"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
37
48
  }
38
49
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgLayoutSimpleUserMenu, decorators: [{
39
50
  type: Component,
40
51
  args: [{
41
52
  selector: 'eg-layout-simple-user-menu',
53
+ changeDetection: ChangeDetectionStrategy.OnPush,
42
54
  imports: [
43
55
  CommonModule,
44
56
  HlmDropdownMenu,
@@ -50,286 +62,506 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
50
62
  HlmButton,
51
63
  NgIcon,
52
64
  RouterLink,
65
+ RouterLinkActive,
53
66
  ],
54
67
  providers: [
55
68
  provideIcons({
56
69
  lucideUser,
57
70
  }),
58
71
  ],
59
- template: "\n <ng-template #iconTemplate>\n <ng-icon [svg]=\"lucideUser\" size=\"1.5rem\" class=\"[&_svg]:w-[inherit]! [&_svg]:h-[inherit]!\" />\n </ng-template>\n\n @let _menuTrigger = menuTrigger();\n\n <div class=\"flex w-full items-center justify-center\">\n @if (_menuTrigger) {\n <button\n type=\"button\"\n [hlmDropdownMenuTrigger]=\"menu\"\n class=\"cursor-pointer bg-transparent border-0 p-0\"\n >\n <ng-container *ngTemplateOutlet=\"_menuTrigger\" />\n </button>\n } @else {\n <button\n hlmButton\n variant=\"primary\"\n size=\"icon\"\n [hlmDropdownMenuTrigger]=\"menu\"\n [icon]=\"iconTemplate\"\n class=\"rounded-full border border-gray-400\"\n ></button>\n }\n\n <ng-template #menu>\n <div hlmDropdownMenu class=\"w-56\">\n @for (section of menus(); track section) {\n @if (section.label) {\n <div hlmDropdownMenuLabel>{{ section.label }}</div>\n }\n @if (section.items?.length) {\n <div hlmDropdownMenuGroup>\n @for (item of section.items; track item) {\n @if (item.link) {\n <a\n hlmDropdownMenuItem\n [routerLink]=\"item.link\"\n [class]=\"hlm('tw:cursor-pointer tw:no-underline', item.class)\"\n >\n @if (item.icon) {\n <ng-icon\n [svg]=\"item.icon\"\n size=\"1rem\"\n [class]=\"\n hlm(\n 'tw:[_svg]:text-[inherit]! tw:[_svg]:bg-[inherit]! tw:cursor-pointer tw:mr-2',\n item.class\n )\n \"\n />\n }\n <span>{{ item.label }}</span>\n </a>\n } @else {\n <button\n hlmDropdownMenuItem\n (click)=\"item.action?.()\"\n [class]=\"hlm('tw:cursor-pointer tw:no-underline', item.class)\"\n >\n @if (item.icon) {\n <ng-icon\n [svg]=\"item.icon\"\n size=\"1rem\"\n [class]=\"\n hlm(\n 'tw:[_svg]:text-[inherit]! tw:[_svg]:bg-[inherit]! tw:cursor-pointer tw:mr-2',\n item.class\n )\n \"\n />\n }\n <span>{{ item.label }}</span>\n </button>\n }\n }\n </div>\n }\n @if (section.separator) {\n <div hlmDropdownMenuSeparator></div>\n }\n }\n </div>\n </ng-template>\n </div>\n ",
72
+ template: "\n <ng-template #iconTemplate>\n <ng-icon [svg]=\"userIcon\" size=\"1.25rem\" aria-hidden=\"true\" />\n </ng-template>\n\n @let customTrigger = triggerTemplate();\n\n <div class=\"flex w-full items-center justify-center\">\n @if (customTrigger) {\n <button\n type=\"button\"\n [hlmDropdownMenuTrigger]=\"menu\"\n [attr.aria-label]=\"triggerLabel()\"\n class=\"min-h-11 min-w-11 rounded-full cursor-pointer bg-transparent border-0 p-0 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\"\n >\n <ng-container *ngTemplateOutlet=\"customTrigger\" />\n </button>\n } @else {\n <button\n hlmButton\n type=\"button\"\n variant=\"secondary\"\n appearance=\"outline\"\n size=\"icon\"\n [hlmDropdownMenuTrigger]=\"menu\"\n [icon]=\"iconTemplate\"\n [attr.aria-label]=\"triggerLabel()\"\n class=\"size-11 rounded-full border border-border text-foreground\"\n ></button>\n }\n\n <ng-template #menu>\n <div hlmDropdownMenu class=\"w-56\">\n @for (section of sections(); track $index) {\n @if (section.label) {\n <div hlmDropdownMenuLabel>{{ section.label }}</div>\n }\n @if (section.items?.length) {\n <div hlmDropdownMenuGroup>\n @for (item of section.items; track item) {\n @if (item.link && !item.disabled) {\n <a\n hlmDropdownMenuItem\n [routerLink]=\"item.link\"\n routerLinkActive=\"tw:bg-foreground/10 tw:font-semibold\"\n [routerLinkActiveOptions]=\"navigationMatchOptions(item)\"\n ariaCurrentWhenActive=\"page\"\n [class]=\"hlm('tw:min-h-11 tw:cursor-pointer tw:no-underline', item.class)\"\n >\n @if (item.icon) {\n <ng-icon\n [svg]=\"item.icon\"\n aria-hidden=\"true\"\n size=\"1rem\"\n [class]=\"\n hlm(\n 'tw:[_svg]:text-[inherit]! tw:[_svg]:bg-[inherit]! tw:cursor-pointer tw:mr-2',\n item.class\n )\n \"\n />\n }\n <span>{{ item.label }}</span>\n </a>\n } @else {\n <button\n hlmDropdownMenuItem\n type=\"button\"\n [disabled]=\"item.disabled || !item.action\"\n (click)=\"item.action?.()\"\n [class]=\"hlm('tw:min-h-11 tw:cursor-pointer tw:no-underline', item.class)\"\n >\n @if (item.icon) {\n <ng-icon\n [svg]=\"item.icon\"\n aria-hidden=\"true\"\n size=\"1rem\"\n [class]=\"\n hlm(\n 'tw:[_svg]:text-[inherit]! tw:[_svg]:bg-[inherit]! tw:cursor-pointer tw:mr-2',\n item.class\n )\n \"\n />\n }\n <span>{{ item.label }}</span>\n </button>\n }\n }\n </div>\n }\n @if (section.separator) {\n <div hlmDropdownMenuSeparator></div>\n }\n }\n </div>\n </ng-template>\n </div>\n ",
60
73
  }]
61
- }], propDecorators: { menus: [{ type: i0.Input, args: [{ isSignal: true, alias: "menus", required: false }] }], menuTrigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuTrigger", required: false }] }] } });
74
+ }], propDecorators: { sections: [{ type: i0.Input, args: [{ isSignal: true, alias: "sections", required: false }] }], triggerTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "triggerTemplate", required: false }] }], triggerLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "triggerLabel", required: false }] }] } });
62
75
 
63
- class EgGenericAutocomplete {
64
- placeholderText = input('Select an option', /* @ts-ignore */
65
- ...(ngDevMode ? [{ debugName: "placeholderText" }] : /* istanbul ignore next */ []));
66
- emptyText = input('No options found', /* @ts-ignore */
76
+ /** Async page search with debouncing, stale-response protection, and keyboard navigation. */
77
+ class EgLayoutSearch {
78
+ placeholder = input('Search pages…', /* @ts-ignore */
79
+ ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
80
+ emptyText = input('No matching pages found.', /* @ts-ignore */
67
81
  ...(ngDevMode ? [{ debugName: "emptyText" }] : /* istanbul ignore next */ []));
68
- /** Loader function: takes search string, returns options. */
69
- loaderFn = input(async () => [], /* @ts-ignore */
70
- ...(ngDevMode ? [{ debugName: "loaderFn" }] : /* istanbul ignore next */ []));
71
- transformValueToSearch = input((value) => String(value), /* @ts-ignore */
72
- ...(ngDevMode ? [{ debugName: "transformValueToSearch" }] : /* istanbul ignore next */ []));
73
- search = signal('', /* @ts-ignore */
74
- ...(ngDevMode ? [{ debugName: "search" }] : /* istanbul ignore next */ []));
75
- options = signal([], /* @ts-ignore */
76
- ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
77
- filtered = computed(() => {
78
- const opts = this.options();
79
- const search = this.search().toLowerCase();
80
- const transform = this.transformValueToSearch() ?? ((value) => String(value));
81
- const mapped = opts.map((o) => ({ label: transform(o), value: o, raw: o }));
82
- if (!search)
83
- return mapped;
84
- return mapped.filter((o) => o.label.toLowerCase().includes(search));
85
- }, /* @ts-ignore */
86
- ...(ngDevMode ? [{ debugName: "filtered" }] : /* istanbul ignore next */ []));
87
- optionChange = output();
82
+ errorText = input('Search is unavailable. Please try again.', /* @ts-ignore */
83
+ ...(ngDevMode ? [{ debugName: "errorText" }] : /* istanbul ignore next */ []));
84
+ loadingText = input('Searching…', /* @ts-ignore */
85
+ ...(ngDevMode ? [{ debugName: "loadingText" }] : /* istanbul ignore next */ []));
86
+ retryText = input('Try again', /* @ts-ignore */
87
+ ...(ngDevMode ? [{ debugName: "retryText" }] : /* istanbul ignore next */ []));
88
+ loader = input(/* @ts-ignore */
89
+ ...(ngDevMode ? [undefined, { debugName: "loader" }] : /* istanbul ignore next */ []));
90
+ resultLabel = input(/* @ts-ignore */
91
+ ...(ngDevMode ? [undefined, { debugName: "resultLabel" }] : /* istanbul ignore next */ []));
92
+ resultTemplate = input(/* @ts-ignore */
93
+ ...(ngDevMode ? [undefined, { debugName: "resultTemplate" }] : /* istanbul ignore next */ []));
94
+ resultSelected = output();
95
+ searchIcon = lucideSearch;
96
+ query = signal('', /* @ts-ignore */
97
+ ...(ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []));
98
+ results = signal([], /* @ts-ignore */
99
+ ...(ngDevMode ? [{ debugName: "results" }] : /* istanbul ignore next */ []));
100
+ isOpen = signal(false, /* @ts-ignore */
101
+ ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
102
+ isLoading = signal(false, /* @ts-ignore */
103
+ ...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
104
+ hasError = signal(false, /* @ts-ignore */
105
+ ...(ngDevMode ? [{ debugName: "hasError" }] : /* istanbul ignore next */ []));
106
+ retryVersion = signal(0, /* @ts-ignore */
107
+ ...(ngDevMode ? [{ debugName: "retryVersion" }] : /* istanbul ignore next */ []));
108
+ searchInput = viewChild('searchInput', /* @ts-ignore */
109
+ ...(ngDevMode ? [{ debugName: "searchInput" }] : /* istanbul ignore next */ []));
110
+ resultButtons = viewChildren('resultButton', /* @ts-ignore */
111
+ ...(ngDevMode ? [{ debugName: "resultButtons" }] : /* istanbul ignore next */ []));
88
112
  constructor() {
89
- // Trigger initial load
90
- this.loadOptions();
113
+ effect((onCleanup) => {
114
+ const open = this.isOpen();
115
+ const search = this.query().trim();
116
+ const loader = this.loader();
117
+ this.retryVersion();
118
+ this.results.set([]);
119
+ this.hasError.set(false);
120
+ this.isLoading.set(open && !!loader);
121
+ if (!open || !loader)
122
+ return;
123
+ // Cleanup also invalidates an in-flight request when the component is destroyed.
124
+ let cancelled = false;
125
+ const timeout = setTimeout(async () => {
126
+ try {
127
+ const results = await loader({ search });
128
+ if (!cancelled)
129
+ this.results.set(results);
130
+ }
131
+ catch {
132
+ if (!cancelled)
133
+ this.hasError.set(true);
134
+ }
135
+ finally {
136
+ if (!cancelled)
137
+ this.isLoading.set(false);
138
+ }
139
+ }, search ? 180 : 0);
140
+ onCleanup(() => {
141
+ cancelled = true;
142
+ clearTimeout(timeout);
143
+ });
144
+ });
91
145
  }
92
- async loadOptions() {
93
- const fn = this.loaderFn();
94
- if (!fn) {
95
- this.options.set([]);
96
- return;
97
- }
98
- try {
99
- const params = { search: this.search() };
100
- const result = await fn(params);
101
- this.options.set(result ?? []);
102
- }
103
- catch {
104
- this.options.set([]);
105
- }
146
+ labelFor(item) {
147
+ return this.resultLabel()?.(item) ?? String(item);
148
+ }
149
+ setOpen(open) {
150
+ this.isOpen.set(open);
151
+ if (!open)
152
+ this.query.set('');
153
+ }
154
+ retry() {
155
+ this.retryVersion.update((version) => version + 1);
106
156
  }
107
- select(opt) {
108
- this.optionChange.emit(opt.value);
109
- this.search.set('');
157
+ selectResult(item) {
158
+ this.setOpen(false);
159
+ this.resultSelected.emit(item);
110
160
  }
111
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgGenericAutocomplete, deps: [], target: i0.ɵɵFactoryTarget.Component });
112
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgGenericAutocomplete, isStandalone: true, selector: "eg-generic-autocomplete", inputs: { placeholderText: { classPropertyName: "placeholderText", publicName: "placeholderText", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, loaderFn: { classPropertyName: "loaderFn", publicName: "loaderFn", isSignal: true, isRequired: false, transformFunction: null }, transformValueToSearch: { classPropertyName: "transformValueToSearch", publicName: "transformValueToSearch", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { optionChange: "optionChange" }, providers: [provideIcons({ lucideSearch })], ngImport: i0, template: "\n <hlm-popover>\n <button\n hlmPopoverTrigger\n hlmButton\n variant=\"secondary\"\n appearance=\"outline\"\n type=\"button\"\n class=\"w-64 justify-start gap-2 text-muted-foreground\"\n >\n <ng-icon name=\"lucideSearch\" size=\"14px\" />\n <span class=\"truncate\">{{ placeholderText() }}</span>\n </button>\n\n <hlm-popover-content *hlmPopoverPortal class=\"w-64 p-2\">\n <input\n hlmInput\n [ngModel]=\"search()\"\n (ngModelChange)=\"search.set($event)\"\n [placeholder]=\"placeholderText()\"\n class=\"mb-2\"\n />\n <div class=\"max-h-60 overflow-auto flex flex-col\">\n @if (filtered().length === 0) {\n <span class=\"text-xs text-muted-foreground px-2 py-2\">{{ emptyText() }}</span>\n } @else {\n @for (opt of filtered(); track opt.label) {\n <button\n type=\"button\"\n (click)=\"select(opt)\"\n class=\"text-left px-2 py-1.5 text-sm rounded-sm hover:bg-secondary cursor-pointer\"\n >\n {{ opt.label }}\n </button>\n }\n }\n </div>\n </hlm-popover-content>\n </hlm-popover>\n ", isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: HlmPopover, selector: "[hlmPopover],hlm-popover" }, { kind: "directive", type: HlmPopoverTrigger, selector: "button[hlmPopoverTrigger],button[hlmPopoverTriggerFor]" }, { kind: "directive", type: HlmPopoverContent, selector: "[hlmPopoverContent],hlm-popover-content" }, { kind: "directive", type: HlmPopoverPortal, selector: "[hlmPopoverPortal]" }, { kind: "directive", type: HlmInput, selector: "[hlmInput]", inputs: ["ariaDescribedby"] }, { kind: "component", type: HlmButton, selector: "button[hlmButton], a[hlmButton]", inputs: ["variant", "size", "appearance", "loading", "icon", "iconPosition", "class", "spinnerUserClass", "disabled", "type"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
161
+ focusResult(index, event) {
162
+ const buttons = this.resultButtons();
163
+ if (!buttons.length)
164
+ return;
165
+ event.preventDefault();
166
+ if (index < 0)
167
+ this.searchInput()?.nativeElement.focus();
168
+ else
169
+ buttons[Math.min(index, buttons.length - 1)]?.nativeElement.focus();
170
+ }
171
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgLayoutSearch, deps: [], target: i0.ɵɵFactoryTarget.Component });
172
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgLayoutSearch, isStandalone: true, selector: "eg-layout-search", inputs: { placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, errorText: { classPropertyName: "errorText", publicName: "errorText", isSignal: true, isRequired: false, transformFunction: null }, loadingText: { classPropertyName: "loadingText", publicName: "loadingText", isSignal: true, isRequired: false, transformFunction: null }, retryText: { classPropertyName: "retryText", publicName: "retryText", isSignal: true, isRequired: false, transformFunction: null }, loader: { classPropertyName: "loader", publicName: "loader", isSignal: true, isRequired: false, transformFunction: null }, resultLabel: { classPropertyName: "resultLabel", publicName: "resultLabel", isSignal: true, isRequired: false, transformFunction: null }, resultTemplate: { classPropertyName: "resultTemplate", publicName: "resultTemplate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { resultSelected: "resultSelected" }, host: { classAttribute: "block min-w-0" }, viewQueries: [{ propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }, { propertyName: "resultButtons", predicate: ["resultButton"], descendants: true, isSignal: true }], ngImport: i0, template: "\n <hlm-popover\n [state]=\"isOpen() ? 'open' : 'closed'\"\n (stateChanged)=\"setOpen($event === 'open')\"\n (closed)=\"setOpen(false)\"\n >\n <button\n hlmPopoverTrigger\n type=\"button\"\n class=\"flex min-h-11 w-full items-center gap-2 rounded-lg border border-border bg-foreground/3 px-3 text-left text-sm text-foreground/75 cursor-pointer hover:bg-foreground/5 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring md:w-56\"\n >\n <ng-icon [svg]=\"searchIcon\" size=\"16px\" aria-hidden=\"true\" class=\"shrink-0\" />\n <span class=\"truncate\">{{ placeholder() }}</span>\n </button>\n <hlm-popover-content *hlmPopoverPortal class=\"w-80 max-w-[calc(100vw-2rem)] p-2\">\n <input\n #searchInput\n hlmInput\n type=\"search\"\n autocomplete=\"off\"\n [value]=\"query()\"\n (input)=\"query.set($any($event.target).value)\"\n [placeholder]=\"placeholder()\"\n [attr.aria-label]=\"placeholder()\"\n (keydown.arrowdown)=\"focusResult(0, $event)\"\n class=\"mb-2 min-h-11\"\n />\n <div [attr.aria-busy]=\"isLoading()\" class=\"max-h-72 overflow-y-auto overscroll-contain\">\n @if (isLoading()) {\n <p role=\"status\" class=\"px-3 py-6 text-center text-sm text-foreground/75\">\n {{ loadingText() }}\n </p>\n } @else if (hasError()) {\n <div role=\"status\" class=\"space-y-3 px-3 py-4 text-sm\">\n <p>{{ errorText() }}</p>\n <button\n type=\"button\"\n (click)=\"retry()\"\n class=\"min-h-11 rounded-md border border-border px-3 font-medium cursor-pointer focus-visible:outline-2 focus-visible:outline-ring\"\n >\n {{ retryText() }}\n </button>\n </div>\n } @else if (!results().length) {\n <p role=\"status\" class=\"px-3 py-6 text-center text-sm text-foreground/75\">\n {{ emptyText() }}\n </p>\n } @else {\n @for (result of results(); track $index; let index = $index) {\n <button\n #resultButton\n type=\"button\"\n (click)=\"selectResult(result)\"\n (keydown.arrowdown)=\"focusResult(index + 1, $event)\"\n (keydown.arrowup)=\"focusResult(index - 1, $event)\"\n (keydown.home)=\"focusResult(0, $event)\"\n (keydown.end)=\"focusResult(results().length - 1, $event)\"\n class=\"flex min-h-11 w-full items-center rounded-md px-3 py-2 text-left text-sm cursor-pointer hover:bg-foreground/5 focus-visible:bg-foreground/10 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring\"\n >\n @if (resultTemplate(); as template) {\n <ng-container [ngTemplateOutlet]=\"template\" [ngTemplateOutletContext]=\"{ $implicit: result }\" />\n } @else {\n {{ labelFor(result) }}\n }\n </button>\n }\n }\n </div>\n </hlm-popover-content>\n </hlm-popover>\n ", isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: HlmPopover, selector: "[hlmPopover],hlm-popover" }, { kind: "directive", type: HlmPopoverTrigger, selector: "button[hlmPopoverTrigger],button[hlmPopoverTriggerFor]" }, { kind: "directive", type: HlmPopoverContent, selector: "[hlmPopoverContent],hlm-popover-content" }, { kind: "directive", type: HlmPopoverPortal, selector: "[hlmPopoverPortal]" }, { kind: "directive", type: HlmInput, selector: "[hlmInput]", inputs: ["ariaDescribedby"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
113
173
  }
114
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgGenericAutocomplete, decorators: [{
174
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgLayoutSearch, decorators: [{
115
175
  type: Component,
116
176
  args: [{
117
- selector: 'eg-generic-autocomplete',
118
- imports: [
119
- FormsModule,
120
- HlmPopover,
121
- HlmPopoverTrigger,
122
- HlmPopoverContent,
123
- HlmPopoverPortal,
124
- HlmInput,
125
- HlmButton,
126
- NgIcon,
127
- ],
128
- providers: [provideIcons({ lucideSearch })],
129
- template: "\n <hlm-popover>\n <button\n hlmPopoverTrigger\n hlmButton\n variant=\"secondary\"\n appearance=\"outline\"\n type=\"button\"\n class=\"w-64 justify-start gap-2 text-muted-foreground\"\n >\n <ng-icon name=\"lucideSearch\" size=\"14px\" />\n <span class=\"truncate\">{{ placeholderText() }}</span>\n </button>\n\n <hlm-popover-content *hlmPopoverPortal class=\"w-64 p-2\">\n <input\n hlmInput\n [ngModel]=\"search()\"\n (ngModelChange)=\"search.set($event)\"\n [placeholder]=\"placeholderText()\"\n class=\"mb-2\"\n />\n <div class=\"max-h-60 overflow-auto flex flex-col\">\n @if (filtered().length === 0) {\n <span class=\"text-xs text-muted-foreground px-2 py-2\">{{ emptyText() }}</span>\n } @else {\n @for (opt of filtered(); track opt.label) {\n <button\n type=\"button\"\n (click)=\"select(opt)\"\n class=\"text-left px-2 py-1.5 text-sm rounded-sm hover:bg-secondary cursor-pointer\"\n >\n {{ opt.label }}\n </button>\n }\n }\n </div>\n </hlm-popover-content>\n </hlm-popover>\n ",
177
+ selector: 'eg-layout-search',
178
+ imports: [NgTemplateOutlet, HlmPopover, HlmPopoverTrigger, HlmPopoverContent, HlmPopoverPortal, HlmInput, NgIcon],
179
+ host: { class: "block min-w-0" },
180
+ template: "\n <hlm-popover\n [state]=\"isOpen() ? 'open' : 'closed'\"\n (stateChanged)=\"setOpen($event === 'open')\"\n (closed)=\"setOpen(false)\"\n >\n <button\n hlmPopoverTrigger\n type=\"button\"\n class=\"flex min-h-11 w-full items-center gap-2 rounded-lg border border-border bg-foreground/3 px-3 text-left text-sm text-foreground/75 cursor-pointer hover:bg-foreground/5 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring md:w-56\"\n >\n <ng-icon [svg]=\"searchIcon\" size=\"16px\" aria-hidden=\"true\" class=\"shrink-0\" />\n <span class=\"truncate\">{{ placeholder() }}</span>\n </button>\n <hlm-popover-content *hlmPopoverPortal class=\"w-80 max-w-[calc(100vw-2rem)] p-2\">\n <input\n #searchInput\n hlmInput\n type=\"search\"\n autocomplete=\"off\"\n [value]=\"query()\"\n (input)=\"query.set($any($event.target).value)\"\n [placeholder]=\"placeholder()\"\n [attr.aria-label]=\"placeholder()\"\n (keydown.arrowdown)=\"focusResult(0, $event)\"\n class=\"mb-2 min-h-11\"\n />\n <div [attr.aria-busy]=\"isLoading()\" class=\"max-h-72 overflow-y-auto overscroll-contain\">\n @if (isLoading()) {\n <p role=\"status\" class=\"px-3 py-6 text-center text-sm text-foreground/75\">\n {{ loadingText() }}\n </p>\n } @else if (hasError()) {\n <div role=\"status\" class=\"space-y-3 px-3 py-4 text-sm\">\n <p>{{ errorText() }}</p>\n <button\n type=\"button\"\n (click)=\"retry()\"\n class=\"min-h-11 rounded-md border border-border px-3 font-medium cursor-pointer focus-visible:outline-2 focus-visible:outline-ring\"\n >\n {{ retryText() }}\n </button>\n </div>\n } @else if (!results().length) {\n <p role=\"status\" class=\"px-3 py-6 text-center text-sm text-foreground/75\">\n {{ emptyText() }}\n </p>\n } @else {\n @for (result of results(); track $index; let index = $index) {\n <button\n #resultButton\n type=\"button\"\n (click)=\"selectResult(result)\"\n (keydown.arrowdown)=\"focusResult(index + 1, $event)\"\n (keydown.arrowup)=\"focusResult(index - 1, $event)\"\n (keydown.home)=\"focusResult(0, $event)\"\n (keydown.end)=\"focusResult(results().length - 1, $event)\"\n class=\"flex min-h-11 w-full items-center rounded-md px-3 py-2 text-left text-sm cursor-pointer hover:bg-foreground/5 focus-visible:bg-foreground/10 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring\"\n >\n @if (resultTemplate(); as template) {\n <ng-container [ngTemplateOutlet]=\"template\" [ngTemplateOutletContext]=\"{ $implicit: result }\" />\n } @else {\n {{ labelFor(result) }}\n }\n </button>\n }\n }\n </div>\n </hlm-popover-content>\n </hlm-popover>\n ",
130
181
  changeDetection: ChangeDetectionStrategy.OnPush,
131
182
  }]
132
- }], ctorParameters: () => [], propDecorators: { placeholderText: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholderText", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], loaderFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "loaderFn", required: false }] }], transformValueToSearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "transformValueToSearch", required: false }] }], optionChange: [{ type: i0.Output, args: ["optionChange"] }] } });
183
+ }], ctorParameters: () => [], propDecorators: { placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], errorText: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorText", required: false }] }], loadingText: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingText", required: false }] }], retryText: [{ type: i0.Input, args: [{ isSignal: true, alias: "retryText", required: false }] }], loader: [{ type: i0.Input, args: [{ isSignal: true, alias: "loader", required: false }] }], resultLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "resultLabel", required: false }] }], resultTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "resultTemplate", required: false }] }], resultSelected: [{ type: i0.Output, args: ["resultSelected"] }], searchInput: [{ type: i0.ViewChild, args: ['searchInput', { isSignal: true }] }], resultButtons: [{ type: i0.ViewChildren, args: ['resultButton', { isSignal: true }] }] } });
133
184
 
134
185
  class EgLayoutSimpleSidebar {
135
- viewchildSheetRef = viewChild(BrnSheet, /* @ts-ignore */
136
- ...(ngDevMode ? [{ debugName: "viewchildSheetRef" }] : /* istanbul ignore next */ []));
137
- side = input('left', /* @ts-ignore */
138
- ...(ngDevMode ? [{ debugName: "side" }] : /* istanbul ignore next */ []));
139
- title = input('', /* @ts-ignore */
186
+ sheet = viewChild(BrnSheet, /* @ts-ignore */
187
+ ...(ngDevMode ? [{ debugName: "sheet" }] : /* istanbul ignore next */ []));
188
+ panelId = input.required(/* @ts-ignore */
189
+ ...(ngDevMode ? [{ debugName: "panelId" }] : /* istanbul ignore next */ []));
190
+ title = input('Navigation', /* @ts-ignore */
140
191
  ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
141
192
  content = input(/* @ts-ignore */
142
193
  ...(ngDevMode ? [undefined, { debugName: "content" }] : /* istanbul ignore next */ []));
143
194
  contentContext = input(null, /* @ts-ignore */
144
195
  ...(ngDevMode ? [{ debugName: "contentContext" }] : /* istanbul ignore next */ []));
145
- constructor() {
146
- effect(() => { });
147
- }
148
- openSheet() {
149
- this.viewchildSheetRef()?.open();
196
+ open() {
197
+ this.sheet()?.open();
150
198
  }
151
- closeSheet() {
152
- this.viewchildSheetRef()?.close();
199
+ close() {
200
+ this.sheet()?.close();
153
201
  }
154
202
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgLayoutSimpleSidebar, deps: [], target: i0.ɵɵFactoryTarget.Component });
155
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgLayoutSimpleSidebar, isStandalone: true, selector: "eg-layout-simple-sidebar", inputs: { side: { classPropertyName: "side", publicName: "side", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, content: { classPropertyName: "content", publicName: "content", isSignal: true, isRequired: false, transformFunction: null }, contentContext: { classPropertyName: "contentContext", publicName: "contentContext", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "viewchildSheetRef", first: true, predicate: BrnSheet, descendants: true, isSignal: true }], ngImport: i0, template: "\n @let _title = title();\n @let _content = content();\n\n <hlm-sheet id=\"eg-layout-simple-sidebar\" [side]=\"side() ?? 'left'\">\n <hlm-sheet-content *brnSheetContent=\"let ctx\" class=\"gap-0\">\n @if (_title) {\n <hlm-sheet-header>\n <h3 hlmSheetTitle>{{ _title }}</h3>\n </hlm-sheet-header>\n }\n\n @if (_content) {\n <ng-container *ngTemplateOutlet=\"_content; context: contentContext()\"></ng-container>\n }\n\n <hlm-sheet-footer>\n <button brnSheetClose hlmButton variant=\"secondary\" appearance=\"outline-filled\">Close</button>\n </hlm-sheet-footer>\n </hlm-sheet-content>\n </hlm-sheet>\n ", isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2.BrnSheetClose, selector: "button[brnSheetClose]" }, { kind: "directive", type: i2.BrnSheetContent, selector: "[brnSheetContent]" }, { kind: "component", type: i3.HlmSheet, selector: "hlm-sheet", exportAs: ["hlmSheet"] }, { kind: "component", type: i3.HlmSheetContent, selector: "hlm-sheet-content", inputs: ["class"] }, { kind: "component", type: i3.HlmSheetFooter, selector: "hlm-sheet-footer", inputs: ["class"] }, { kind: "component", type: i3.HlmSheetHeader, selector: "hlm-sheet-header", inputs: ["class"] }, { kind: "directive", type: i3.HlmSheetTitle, selector: "[hlmSheetTitle]", inputs: ["class"] }, { kind: "component", type: i4.HlmButton, selector: "button[hlmButton], a[hlmButton]", inputs: ["variant", "size", "appearance", "loading", "icon", "iconPosition", "class", "spinnerUserClass", "disabled", "type"] }] });
203
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.3", type: EgLayoutSimpleSidebar, isStandalone: true, selector: "eg-layout-simple-sidebar", inputs: { panelId: { classPropertyName: "panelId", publicName: "panelId", isSignal: true, isRequired: true, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, content: { classPropertyName: "content", publicName: "content", isSignal: true, isRequired: false, transformFunction: null }, contentContext: { classPropertyName: "contentContext", publicName: "contentContext", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "sheet", first: true, predicate: BrnSheet, descendants: true, isSignal: true }], ngImport: i0, template: "\n <hlm-sheet [id]=\"panelId()\" side=\"left\">\n <hlm-sheet-content\n *brnSheetContent=\"let context\"\n class=\"flex h-full max-w-[calc(100vw-2rem)] flex-col gap-0\"\n >\n <hlm-sheet-header class=\"shrink-0 border-b border-border pr-12\">\n <h2 hlmSheetTitle>{{ title() }}</h2>\n </hlm-sheet-header>\n <div class=\"min-h-0 flex-1 overflow-y-auto overscroll-contain p-4\">\n <ng-container [ngTemplateOutlet]=\"content() ?? null\" [ngTemplateOutletContext]=\"contentContext()\" />\n </div>\n <hlm-sheet-footer class=\"shrink-0 border-t border-border\">\n <button type=\"button\" brnSheetClose hlmButton variant=\"secondary\" appearance=\"outline\">\n Close navigation\n </button>\n </hlm-sheet-footer>\n </hlm-sheet-content>\n </hlm-sheet>\n ", isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$1.BrnSheetClose, selector: "button[brnSheetClose]" }, { kind: "directive", type: i1$1.BrnSheetContent, selector: "[brnSheetContent]" }, { kind: "component", type: i2.HlmSheet, selector: "hlm-sheet", exportAs: ["hlmSheet"] }, { kind: "component", type: i2.HlmSheetContent, selector: "hlm-sheet-content", inputs: ["class"] }, { kind: "component", type: i2.HlmSheetFooter, selector: "hlm-sheet-footer", inputs: ["class"] }, { kind: "component", type: i2.HlmSheetHeader, selector: "hlm-sheet-header", inputs: ["class"] }, { kind: "directive", type: i2.HlmSheetTitle, selector: "[hlmSheetTitle]", inputs: ["class"] }, { kind: "component", type: i3.HlmButton, selector: "button[hlmButton], a[hlmButton]", inputs: ["variant", "size", "appearance", "loading", "icon", "iconPosition", "class", "spinnerUserClass", "disabled", "type"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
156
204
  }
157
205
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgLayoutSimpleSidebar, decorators: [{
158
206
  type: Component,
159
207
  args: [{
160
208
  selector: 'eg-layout-simple-sidebar',
161
- standalone: true,
162
- imports: [CommonModule, RouterModule, BrnSheetImports, HlmSheetImports, HlmButtonImports, HlmLabelImports],
163
- template: "\n @let _title = title();\n @let _content = content();\n\n <hlm-sheet id=\"eg-layout-simple-sidebar\" [side]=\"side() ?? 'left'\">\n <hlm-sheet-content *brnSheetContent=\"let ctx\" class=\"gap-0\">\n @if (_title) {\n <hlm-sheet-header>\n <h3 hlmSheetTitle>{{ _title }}</h3>\n </hlm-sheet-header>\n }\n\n @if (_content) {\n <ng-container *ngTemplateOutlet=\"_content; context: contentContext()\"></ng-container>\n }\n\n <hlm-sheet-footer>\n <button brnSheetClose hlmButton variant=\"secondary\" appearance=\"outline-filled\">Close</button>\n </hlm-sheet-footer>\n </hlm-sheet-content>\n </hlm-sheet>\n ",
209
+ imports: [NgTemplateOutlet, BrnSheetImports, HlmSheetImports, HlmButtonImports],
210
+ changeDetection: ChangeDetectionStrategy.OnPush,
211
+ template: "\n <hlm-sheet [id]=\"panelId()\" side=\"left\">\n <hlm-sheet-content\n *brnSheetContent=\"let context\"\n class=\"flex h-full max-w-[calc(100vw-2rem)] flex-col gap-0\"\n >\n <hlm-sheet-header class=\"shrink-0 border-b border-border pr-12\">\n <h2 hlmSheetTitle>{{ title() }}</h2>\n </hlm-sheet-header>\n <div class=\"min-h-0 flex-1 overflow-y-auto overscroll-contain p-4\">\n <ng-container [ngTemplateOutlet]=\"content() ?? null\" [ngTemplateOutletContext]=\"contentContext()\" />\n </div>\n <hlm-sheet-footer class=\"shrink-0 border-t border-border\">\n <button type=\"button\" brnSheetClose hlmButton variant=\"secondary\" appearance=\"outline\">\n Close navigation\n </button>\n </hlm-sheet-footer>\n </hlm-sheet-content>\n </hlm-sheet>\n ",
164
212
  }]
165
- }], ctorParameters: () => [], propDecorators: { viewchildSheetRef: [{ type: i0.ViewChild, args: [i0.forwardRef(() => BrnSheet), { isSignal: true }] }], side: [{ type: i0.Input, args: [{ isSignal: true, alias: "side", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], content: [{ type: i0.Input, args: [{ isSignal: true, alias: "content", required: false }] }], contentContext: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentContext", required: false }] }] } });
213
+ }], propDecorators: { sheet: [{ type: i0.ViewChild, args: [i0.forwardRef(() => BrnSheet), { isSignal: true }] }], panelId: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelId", required: true }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], content: [{ type: i0.Input, args: [{ isSignal: true, alias: "content", required: false }] }], contentContext: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentContext", required: false }] }] } });
214
+
215
+ /** Internal shared renderer keeps desktop, mobile, and footer navigation consistent. */
216
+ class EgLayoutNavigationItem {
217
+ item = input.required(/* @ts-ignore */
218
+ ...(ngDevMode ? [{ debugName: "item" }] : /* istanbul ignore next */ []));
219
+ itemClass = input('', /* @ts-ignore */
220
+ ...(ngDevMode ? [{ debugName: "itemClass" }] : /* istanbul ignore next */ []));
221
+ appearance = input('link', /* @ts-ignore */
222
+ ...(ngDevMode ? [{ debugName: "appearance" }] : /* istanbul ignore next */ []));
223
+ selected = output();
224
+ activeClasses = "bg-foreground/10 font-semibold underline decoration-2 underline-offset-4";
225
+ classes = computed(() => hlm("flex min-h-11 w-full items-center gap-2 rounded-lg px-3 py-2 text-left text-sm font-medium text-foreground no-underline cursor-pointer motion-safe:transition-colors hover:bg-foreground/5 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring disabled:cursor-not-allowed disabled:opacity-50", this.appearance() === 'card' &&
226
+ "h-full min-h-11 md:min-h-24 flex-col items-start justify-between gap-5 rounded-xl border border-transparent bg-foreground/3 p-4 font-semibold break-words hover:border-border hover:bg-foreground/7", this.itemClass(), this.item().class), /* @ts-ignore */
227
+ ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
228
+ matchOptions = computed(() => navigationMatchOptions(this.item()), /* @ts-ignore */
229
+ ...(ngDevMode ? [{ debugName: "matchOptions" }] : /* istanbul ignore next */ []));
230
+ activate() {
231
+ const item = this.item();
232
+ if (item.disabled || !item.action)
233
+ return;
234
+ item.action();
235
+ this.selected.emit(item);
236
+ }
237
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgLayoutNavigationItem, deps: [], target: i0.ɵɵFactoryTarget.Component });
238
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgLayoutNavigationItem, isStandalone: true, selector: "eg-layout-navigation-item", inputs: { item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: true, transformFunction: null }, itemClass: { classPropertyName: "itemClass", publicName: "itemClass", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selected: "selected" }, host: { classAttribute: "block min-w-0 shrink-0" }, ngImport: i0, template: "\n @let entry = item();\n @if (entry.link && !entry.disabled) {\n <a\n [routerLink]=\"entry.link\"\n [routerLinkActive]=\"activeClasses\"\n [routerLinkActiveOptions]=\"matchOptions()\"\n ariaCurrentWhenActive=\"page\"\n [class]=\"classes()\"\n (click)=\"selected.emit(entry)\"\n >\n <ng-container [ngTemplateOutlet]=\"content\" />\n </a>\n } @else {\n <button type=\"button\" [class]=\"classes()\" [disabled]=\"entry.disabled || !entry.action\" (click)=\"activate()\">\n <ng-container [ngTemplateOutlet]=\"content\" />\n </button>\n }\n <ng-template #content>\n @if (entry.icon) {\n <ng-icon\n [svg]=\"entry.icon\"\n [size]=\"appearance() === 'card' ? '1.25rem' : '1rem'\"\n aria-hidden=\"true\"\n class=\"shrink-0\"\n />\n }\n <span class=\"min-w-0\">\n <span class=\"block\">{{ entry.label }}</span>\n @if (appearance() === 'card' && entry.description) {\n <span class=\"mt-1.5 block text-sm font-normal leading-relaxed text-foreground/75\">{{\n entry.description\n }}</span>\n }\n </span>\n </ng-template>\n ", isInline: true, dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
239
+ }
240
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgLayoutNavigationItem, decorators: [{
241
+ type: Component,
242
+ args: [{
243
+ selector: 'eg-layout-navigation-item',
244
+ imports: [RouterLink, RouterLinkActive, NgIcon, NgTemplateOutlet],
245
+ changeDetection: ChangeDetectionStrategy.OnPush,
246
+ host: { class: "block min-w-0 shrink-0" },
247
+ template: "\n @let entry = item();\n @if (entry.link && !entry.disabled) {\n <a\n [routerLink]=\"entry.link\"\n [routerLinkActive]=\"activeClasses\"\n [routerLinkActiveOptions]=\"matchOptions()\"\n ariaCurrentWhenActive=\"page\"\n [class]=\"classes()\"\n (click)=\"selected.emit(entry)\"\n >\n <ng-container [ngTemplateOutlet]=\"content\" />\n </a>\n } @else {\n <button type=\"button\" [class]=\"classes()\" [disabled]=\"entry.disabled || !entry.action\" (click)=\"activate()\">\n <ng-container [ngTemplateOutlet]=\"content\" />\n </button>\n }\n <ng-template #content>\n @if (entry.icon) {\n <ng-icon\n [svg]=\"entry.icon\"\n [size]=\"appearance() === 'card' ? '1.25rem' : '1rem'\"\n aria-hidden=\"true\"\n class=\"shrink-0\"\n />\n }\n <span class=\"min-w-0\">\n <span class=\"block\">{{ entry.label }}</span>\n @if (appearance() === 'card' && entry.description) {\n <span class=\"mt-1.5 block text-sm font-normal leading-relaxed text-foreground/75\">{{\n entry.description\n }}</span>\n }\n </span>\n </ng-template>\n ",
248
+ }]
249
+ }], propDecorators: { item: [{ type: i0.Input, args: [{ isSignal: true, alias: "item", required: true }] }], itemClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemClass", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], selected: [{ type: i0.Output, args: ["selected"] }] } });
166
250
 
167
251
  class EgLayoutSimpleMobileMenuGroup {
168
- hlm = hlm;
169
- label = input('', /* @ts-ignore */
170
- ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
252
+ label = input(/* @ts-ignore */
253
+ ...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
171
254
  items = input(/* @ts-ignore */
172
255
  ...(ngDevMode ? [undefined, { debugName: "items" }] : /* istanbul ignore next */ []));
173
- itemClick = output();
174
- handleClick(item) {
175
- item.action?.();
176
- this.itemClick.emit(item);
177
- }
256
+ itemSelected = output();
178
257
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgLayoutSimpleMobileMenuGroup, deps: [], target: i0.ɵɵFactoryTarget.Component });
179
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgLayoutSimpleMobileMenuGroup, isStandalone: true, selector: "eg-layout-simple-mobile-menu-group", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemClick: "itemClick" }, providers: [], ngImport: i0, template: "\n @let _label = label();\n @let _items = items();\n\n @if (_items && _items.length > 0) {\n @if (_label) {\n <div class=\"px-3 py-1 text-xs font-semibold text-muted-foreground\">{{ _label }}</div>\n }\n <div class=\"flex flex-col\">\n @for (item of _items; track item.label) {\n @if (item.link) {\n <a\n [routerLink]=\"item.link\"\n [class]=\"\n hlm(\n 'tw:cursor-pointer tw:no-underline tw:px-3 tw:py-2 tw:text-sm tw:hover:bg-secondary tw:rounded-sm tw:flex tw:items-center tw:gap-2'\n )\n \"\n (click)=\"handleClick(item)\"\n >\n @if (item.icon) {\n <ng-icon [svg]=\"item.icon\" size=\"1rem\" [class]=\"hlm('tw:cursor-pointer tw:mr-2')\"> </ng-icon>\n }\n <span>{{ item.label }}</span>\n </a>\n } @else {\n <button\n type=\"button\"\n (click)=\"handleClick(item)\"\n [class]=\"\n hlm(\n 'tw:cursor-pointer tw:no-underline tw:px-3 tw:py-2 tw:text-sm tw:hover:bg-secondary tw:rounded-sm tw:flex tw:items-center tw:gap-2 tw:bg-transparent tw:border-0 tw:text-left tw:w-full'\n )\n \"\n >\n @if (item.icon) {\n <ng-icon [svg]=\"item.icon\" size=\"1rem\" [class]=\"hlm('tw:cursor-pointer tw:mr-2')\"> </ng-icon>\n }\n <span>{{ item.label }}</span>\n </button>\n }\n }\n </div>\n }\n ", isInline: true, dependencies: [{ kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }] });
258
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgLayoutSimpleMobileMenuGroup, isStandalone: true, selector: "eg-layout-simple-mobile-menu-group", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemSelected: "itemSelected" }, host: { classAttribute: "block empty:hidden" }, ngImport: i0, template: "\n @if (items()?.length) {\n @if (label()) {\n <div class=\"px-3 pb-2 text-xs font-semibold text-foreground/75\">{{ label() }}</div>\n }\n <div class=\"grid gap-1\">\n @for (item of items(); track $index) {\n <eg-layout-navigation-item [item]=\"item\" (selected)=\"itemSelected.emit($event)\" />\n }\n </div>\n }\n ", isInline: true, dependencies: [{ kind: "component", type: EgLayoutNavigationItem, selector: "eg-layout-navigation-item", inputs: ["item", "itemClass", "appearance"], outputs: ["selected"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
180
259
  }
181
260
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgLayoutSimpleMobileMenuGroup, decorators: [{
182
261
  type: Component,
183
262
  args: [{
184
263
  selector: 'eg-layout-simple-mobile-menu-group',
185
- imports: [NgIcon, RouterLink],
186
- providers: [],
187
- template: "\n @let _label = label();\n @let _items = items();\n\n @if (_items && _items.length > 0) {\n @if (_label) {\n <div class=\"px-3 py-1 text-xs font-semibold text-muted-foreground\">{{ _label }}</div>\n }\n <div class=\"flex flex-col\">\n @for (item of _items; track item.label) {\n @if (item.link) {\n <a\n [routerLink]=\"item.link\"\n [class]=\"\n hlm(\n 'tw:cursor-pointer tw:no-underline tw:px-3 tw:py-2 tw:text-sm tw:hover:bg-secondary tw:rounded-sm tw:flex tw:items-center tw:gap-2'\n )\n \"\n (click)=\"handleClick(item)\"\n >\n @if (item.icon) {\n <ng-icon [svg]=\"item.icon\" size=\"1rem\" [class]=\"hlm('tw:cursor-pointer tw:mr-2')\"> </ng-icon>\n }\n <span>{{ item.label }}</span>\n </a>\n } @else {\n <button\n type=\"button\"\n (click)=\"handleClick(item)\"\n [class]=\"\n hlm(\n 'tw:cursor-pointer tw:no-underline tw:px-3 tw:py-2 tw:text-sm tw:hover:bg-secondary tw:rounded-sm tw:flex tw:items-center tw:gap-2 tw:bg-transparent tw:border-0 tw:text-left tw:w-full'\n )\n \"\n >\n @if (item.icon) {\n <ng-icon [svg]=\"item.icon\" size=\"1rem\" [class]=\"hlm('tw:cursor-pointer tw:mr-2')\"> </ng-icon>\n }\n <span>{{ item.label }}</span>\n </button>\n }\n }\n </div>\n }\n ",
264
+ imports: [EgLayoutNavigationItem],
265
+ changeDetection: ChangeDetectionStrategy.OnPush,
266
+ host: { class: "block empty:hidden" },
267
+ template: "\n @if (items()?.length) {\n @if (label()) {\n <div class=\"px-3 pb-2 text-xs font-semibold text-foreground/75\">{{ label() }}</div>\n }\n <div class=\"grid gap-1\">\n @for (item of items(); track $index) {\n <eg-layout-navigation-item [item]=\"item\" (selected)=\"itemSelected.emit($event)\" />\n }\n </div>\n }\n ",
188
268
  }]
189
- }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], itemClick: [{ type: i0.Output, args: ["itemClick"] }] } });
269
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], itemSelected: [{ type: i0.Output, args: ["itemSelected"] }] } });
190
270
 
191
- /**
192
- * Single viewport contract for the shell: below Tailwind's `md` (768px) breakpoint the
193
- * mobile menu trigger replaces all desktop navigation rows. CSS (`tw:hidden tw:md:flex`)
194
- * and runtime rendering both derive from this one boundary.
195
- */
271
+ /** Internal disclosure navigation: native links, normal Tab order, no application-menu roles. */
272
+ class EgLayoutFlyoutNavbar {
273
+ groups = input.required(/* @ts-ignore */
274
+ ...(ngDevMode ? [{ debugName: "groups" }] : /* istanbul ignore next */ []));
275
+ label = input('Fly-out navigation', /* @ts-ignore */
276
+ ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
277
+ closeLabel = input('Close navigation panel', /* @ts-ignore */
278
+ ...(ngDevMode ? [{ debugName: "closeLabel" }] : /* istanbul ignore next */ []));
279
+ navbarClass = input('', /* @ts-ignore */
280
+ ...(ngDevMode ? [{ debugName: "navbarClass" }] : /* istanbul ignore next */ []));
281
+ panelClass = input('', /* @ts-ignore */
282
+ ...(ngDevMode ? [{ debugName: "panelClass" }] : /* istanbul ignore next */ []));
283
+ itemClass = input('', /* @ts-ignore */
284
+ ...(ngDevMode ? [{ debugName: "itemClass" }] : /* istanbul ignore next */ []));
285
+ host = inject(ElementRef);
286
+ injector = inject(Injector);
287
+ router = inject(Router);
288
+ currentUrl = signal(this.router.url, /* @ts-ignore */
289
+ ...(ngDevMode ? [{ debugName: "currentUrl" }] : /* istanbul ignore next */ []));
290
+ triggers = viewChildren('trigger', /* @ts-ignore */
291
+ ...(ngDevMode ? [{ debugName: "triggers" }] : /* istanbul ignore next */ []));
292
+ panel = viewChild('panel', /* @ts-ignore */
293
+ ...(ngDevMode ? [{ debugName: "panel" }] : /* istanbul ignore next */ []));
294
+ id = inject(_IdGenerator).getId('eg-layout-flyout-');
295
+ hlm = hlm;
296
+ chevronIcon = lucideChevronDown;
297
+ closeIcon = lucideX;
298
+ openIndex = signal(null, /* @ts-ignore */
299
+ ...(ngDevMode ? [{ debugName: "openIndex" }] : /* istanbul ignore next */ []));
300
+ navbarClasses = computed(() => hlm("flex flex-wrap items-center gap-1 border-b border-border bg-background px-4 py-2 sm:px-6", this.navbarClass()), /* @ts-ignore */
301
+ ...(ngDevMode ? [{ debugName: "navbarClasses" }] : /* istanbul ignore next */ []));
302
+ panelClasses = computed(() => hlm("absolute inset-x-0 top-full max-h-[min(70dvh,36rem)] overflow-y-auto overscroll-contain rounded-b-2xl border border-t-0 border-border bg-background p-5 text-foreground shadow-xl sm:p-6", this.panelClass()), /* @ts-ignore */
303
+ ...(ngDevMode ? [{ debugName: "panelClasses" }] : /* istanbul ignore next */ []));
304
+ activeGroups = computed(() => {
305
+ this.currentUrl();
306
+ return this.groups().map((group) => group.items.some((item) => item.link && !item.disabled && this.router.isActive(item.link, navigationMatchOptions(item))));
307
+ }, /* @ts-ignore */
308
+ ...(ngDevMode ? [{ debugName: "activeGroups" }] : /* istanbul ignore next */ []));
309
+ constructor() {
310
+ this.router.events.pipe(takeUntilDestroyed()).subscribe((event) => {
311
+ if (event instanceof NavigationEnd) {
312
+ this.currentUrl.set(event.urlAfterRedirects);
313
+ this.close();
314
+ }
315
+ });
316
+ effect(() => {
317
+ this.groups();
318
+ this.close();
319
+ });
320
+ }
321
+ toggle(index) {
322
+ this.openIndex.update((open) => (open === index ? null : index));
323
+ }
324
+ close(restoreFocus = false) {
325
+ const index = restoreFocus ? this.openIndex() : null;
326
+ this.openIndex.set(null);
327
+ if (restoreFocus && index !== null)
328
+ this.triggers()[index]?.nativeElement.focus();
329
+ }
330
+ closeOnOutsidePointer(event) {
331
+ if (!this.host.nativeElement.contains(event.target))
332
+ this.close();
333
+ }
334
+ closeOnFocusOut(event) {
335
+ if (!this.host.nativeElement.contains(event.relatedTarget))
336
+ this.close();
337
+ }
338
+ onEscape(event) {
339
+ if (this.openIndex() === null)
340
+ return;
341
+ event.preventDefault();
342
+ event.stopPropagation();
343
+ this.close(true);
344
+ }
345
+ onTriggerKeydown(event, index) {
346
+ if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
347
+ event.preventDefault();
348
+ this.openIndex.set(index);
349
+ afterNextRender(() => {
350
+ const items = this.panel()?.nativeElement.querySelectorAll('eg-layout-navigation-item a, eg-layout-navigation-item button:not(:disabled)');
351
+ const position = event.key === 'ArrowUp' ? (items?.length ?? 0) - 1 : 0;
352
+ items?.[position]?.focus();
353
+ }, { injector: this.injector });
354
+ return;
355
+ }
356
+ const triggers = this.triggers()
357
+ .map((trigger) => trigger.nativeElement)
358
+ .filter((trigger) => !trigger.disabled);
359
+ const current = triggers.indexOf(event.currentTarget);
360
+ let next;
361
+ switch (event.key) {
362
+ case 'ArrowRight':
363
+ next = (current + 1) % triggers.length;
364
+ break;
365
+ case 'ArrowLeft':
366
+ next = (current - 1 + triggers.length) % triggers.length;
367
+ break;
368
+ case 'Home':
369
+ next = 0;
370
+ break;
371
+ case 'End':
372
+ next = triggers.length - 1;
373
+ break;
374
+ default:
375
+ return;
376
+ }
377
+ event.preventDefault();
378
+ this.close();
379
+ triggers[next]?.focus();
380
+ }
381
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgLayoutFlyoutNavbar, deps: [], target: i0.ɵɵFactoryTarget.Component });
382
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgLayoutFlyoutNavbar, isStandalone: true, selector: "eg-layout-flyout-navbar", inputs: { groups: { classPropertyName: "groups", publicName: "groups", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, closeLabel: { classPropertyName: "closeLabel", publicName: "closeLabel", isSignal: true, isRequired: false, transformFunction: null }, navbarClass: { classPropertyName: "navbarClass", publicName: "navbarClass", isSignal: true, isRequired: false, transformFunction: null }, panelClass: { classPropertyName: "panelClass", publicName: "panelClass", isSignal: true, isRequired: false, transformFunction: null }, itemClass: { classPropertyName: "itemClass", publicName: "itemClass", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "document:pointerdown": "closeOnOutsidePointer($event)", "focusout": "closeOnFocusOut($event)", "keydown.escape": "onEscape($event)" }, classAttribute: "relative z-20 block min-w-0" }, viewQueries: [{ propertyName: "triggers", predicate: ["trigger"], descendants: true, isSignal: true }, { propertyName: "panel", first: true, predicate: ["panel"], descendants: true, isSignal: true }], ngImport: i0, template: "\n <nav [attr.aria-label]=\"label()\" [class]=\"navbarClasses()\">\n @for (group of groups(); track $index; let index = $index) {\n <button\n #trigger\n type=\"button\"\n [id]=\"id + '-trigger-' + index\"\n [attr.aria-controls]=\"id + '-panel-' + index\"\n [attr.aria-expanded]=\"openIndex() === index\"\n [disabled]=\"!group.items.length\"\n (click)=\"toggle(index)\"\n (keydown)=\"onTriggerKeydown($event, index)\"\n [attr.data-active]=\"activeGroups()[index] ? '' : null\"\n [class]=\"\n hlm(\n 'tw:flex tw:min-h-11 tw:items-center tw:gap-2 tw:rounded-lg tw:px-3 tw:py-2 tw:text-left tw:text-sm tw:font-medium tw:text-foreground tw:cursor-pointer tw:hover:bg-foreground/5 tw:focus-visible:outline-2 tw:focus-visible:outline-offset-2 tw:focus-visible:outline-ring tw:disabled:cursor-not-allowed tw:disabled:opacity-50',\n (openIndex() === index || activeGroups()[index]) && 'tw:bg-foreground/7 tw:font-semibold'\n )\n \"\n >\n @if (group.icon) {\n <ng-icon [svg]=\"group.icon\" size=\"1rem\" aria-hidden=\"true\" />\n }\n {{ group.label }}\n <ng-icon\n [svg]=\"chevronIcon\"\n size=\"14px\"\n aria-hidden=\"true\"\n [class]=\"hlm('tw:shrink-0 tw:motion-safe:transition-transform', openIndex() === index && 'tw:rotate-180')\"\n />\n </button>\n @if (openIndex() === index) {\n <section\n #panel\n [id]=\"id + '-panel-' + index\"\n [attr.aria-labelledby]=\"id + '-trigger-' + index\"\n data-slot=\"flyout-panel\"\n [class]=\"panelClasses()\"\n >\n <div class=\"mb-4 flex items-start justify-between gap-4\">\n <div class=\"space-y-1\">\n <p class=\"text-xs font-semibold uppercase tracking-wider text-foreground/75\">\n {{ group.label }}\n </p>\n @if (group.description) {\n <p class=\"max-w-2xl text-sm leading-relaxed text-foreground/75\">\n {{ group.description }}\n </p>\n }\n </div>\n <button\n type=\"button\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"close(true)\"\n class=\"inline-flex size-11 shrink-0 items-center justify-center rounded-lg text-foreground/75 cursor-pointer hover:bg-foreground/5 focus-visible:outline-2 focus-visible:outline-ring\"\n >\n <ng-icon [svg]=\"closeIcon\" size=\"18px\" aria-hidden=\"true\" />\n </button>\n </div>\n <div class=\"grid grid-cols-2 gap-3 lg:grid-cols-3\">\n @for (item of group.items; track $index) {\n <eg-layout-navigation-item\n [item]=\"item\"\n appearance=\"card\"\n [itemClass]=\"itemClass()\"\n (selected)=\"close(true)\"\n />\n }\n </div>\n </section>\n }\n }\n </nav>\n ", isInline: true, dependencies: [{ kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "component", type: EgLayoutNavigationItem, selector: "eg-layout-navigation-item", inputs: ["item", "itemClass", "appearance"], outputs: ["selected"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
383
+ }
384
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgLayoutFlyoutNavbar, decorators: [{
385
+ type: Component,
386
+ args: [{
387
+ selector: 'eg-layout-flyout-navbar',
388
+ imports: [NgIcon, EgLayoutNavigationItem],
389
+ changeDetection: ChangeDetectionStrategy.OnPush,
390
+ host: {
391
+ class: "relative z-20 block min-w-0",
392
+ '(document:pointerdown)': 'closeOnOutsidePointer($event)',
393
+ '(focusout)': 'closeOnFocusOut($event)',
394
+ '(keydown.escape)': 'onEscape($event)',
395
+ },
396
+ template: "\n <nav [attr.aria-label]=\"label()\" [class]=\"navbarClasses()\">\n @for (group of groups(); track $index; let index = $index) {\n <button\n #trigger\n type=\"button\"\n [id]=\"id + '-trigger-' + index\"\n [attr.aria-controls]=\"id + '-panel-' + index\"\n [attr.aria-expanded]=\"openIndex() === index\"\n [disabled]=\"!group.items.length\"\n (click)=\"toggle(index)\"\n (keydown)=\"onTriggerKeydown($event, index)\"\n [attr.data-active]=\"activeGroups()[index] ? '' : null\"\n [class]=\"\n hlm(\n 'tw:flex tw:min-h-11 tw:items-center tw:gap-2 tw:rounded-lg tw:px-3 tw:py-2 tw:text-left tw:text-sm tw:font-medium tw:text-foreground tw:cursor-pointer tw:hover:bg-foreground/5 tw:focus-visible:outline-2 tw:focus-visible:outline-offset-2 tw:focus-visible:outline-ring tw:disabled:cursor-not-allowed tw:disabled:opacity-50',\n (openIndex() === index || activeGroups()[index]) && 'tw:bg-foreground/7 tw:font-semibold'\n )\n \"\n >\n @if (group.icon) {\n <ng-icon [svg]=\"group.icon\" size=\"1rem\" aria-hidden=\"true\" />\n }\n {{ group.label }}\n <ng-icon\n [svg]=\"chevronIcon\"\n size=\"14px\"\n aria-hidden=\"true\"\n [class]=\"hlm('tw:shrink-0 tw:motion-safe:transition-transform', openIndex() === index && 'tw:rotate-180')\"\n />\n </button>\n @if (openIndex() === index) {\n <section\n #panel\n [id]=\"id + '-panel-' + index\"\n [attr.aria-labelledby]=\"id + '-trigger-' + index\"\n data-slot=\"flyout-panel\"\n [class]=\"panelClasses()\"\n >\n <div class=\"mb-4 flex items-start justify-between gap-4\">\n <div class=\"space-y-1\">\n <p class=\"text-xs font-semibold uppercase tracking-wider text-foreground/75\">\n {{ group.label }}\n </p>\n @if (group.description) {\n <p class=\"max-w-2xl text-sm leading-relaxed text-foreground/75\">\n {{ group.description }}\n </p>\n }\n </div>\n <button\n type=\"button\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"close(true)\"\n class=\"inline-flex size-11 shrink-0 items-center justify-center rounded-lg text-foreground/75 cursor-pointer hover:bg-foreground/5 focus-visible:outline-2 focus-visible:outline-ring\"\n >\n <ng-icon [svg]=\"closeIcon\" size=\"18px\" aria-hidden=\"true\" />\n </button>\n </div>\n <div class=\"grid grid-cols-2 gap-3 lg:grid-cols-3\">\n @for (item of group.items; track $index) {\n <eg-layout-navigation-item\n [item]=\"item\"\n appearance=\"card\"\n [itemClass]=\"itemClass()\"\n (selected)=\"close(true)\"\n />\n }\n </div>\n </section>\n }\n }\n </nav>\n ",
397
+ }]
398
+ }], ctorParameters: () => [], propDecorators: { groups: [{ type: i0.Input, args: [{ isSignal: true, alias: "groups", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], closeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeLabel", required: false }] }], navbarClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "navbarClass", required: false }] }], panelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelClass", required: false }] }], itemClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemClass", required: false }] }], triggers: [{ type: i0.ViewChildren, args: ['trigger', { isSignal: true }] }], panel: [{ type: i0.ViewChild, args: ['panel', { isSignal: true }] }] } });
399
+
400
+ /** Matches Tailwind's default `md` breakpoint. */
196
401
  const EG_LAYOUT_SIMPLE_MOBILE_BREAKPOINT = '(max-width: 767.98px)';
197
- const commonLinkGroupClasses = "hidden md:flex space-x-4 items-center";
198
- const commonLinkClasses = "text-left text-secondary visited:text-secondary hover:text-primary cursor-pointer no-underline";
402
+ /** Responsive application shell. All navigation surfaces share the same item model. */
199
403
  class EgLayoutSimple {
200
- breakpointObserver;
201
404
  destroyRef = inject(DestroyRef);
405
+ breakpointObserver = inject(BreakpointObserver);
406
+ router = inject(Router);
407
+ instanceId = inject(_IdGenerator).getId('eg-layout-simple-');
408
+ mainId = `${this.instanceId}-main`;
409
+ mobileMenuId = `${this.instanceId}-mobile-menu`;
410
+ sidebarId = `${this.instanceId}-sidebar`;
202
411
  hlm = hlm;
412
+ navigationMatchOptions = navigationMatchOptions;
203
413
  menuIcon = lucideMenu;
414
+ sidebarIcon = lucidePanelLeft;
415
+ closeIcon = lucideX;
416
+ chevronIcon = lucideChevronDown;
417
+ mobileMenuTrigger = viewChild('mobileMenuTrigger', /* @ts-ignore */
418
+ ...(ngDevMode ? [{ debugName: "mobileMenuTrigger" }] : /* istanbul ignore next */ []));
419
+ mainContent = viewChild('mainContent', /* @ts-ignore */
420
+ ...(ngDevMode ? [{ debugName: "mainContent" }] : /* istanbul ignore next */ []));
421
+ sidebar = viewChild(EgLayoutSimpleSidebar, /* @ts-ignore */
422
+ ...(ngDevMode ? [{ debugName: "sidebar" }] : /* istanbul ignore next */ []));
204
423
  sidebarEnabled = input(false, /* @ts-ignore */
205
424
  ...(ngDevMode ? [{ debugName: "sidebarEnabled" }] : /* istanbul ignore next */ []));
206
- sidebarTitle = input('Menu', /* @ts-ignore */
425
+ sidebarTitle = input('Navigation', /* @ts-ignore */
207
426
  ...(ngDevMode ? [{ debugName: "sidebarTitle" }] : /* istanbul ignore next */ []));
208
427
  sidebarContent = input(/* @ts-ignore */
209
428
  ...(ngDevMode ? [undefined, { debugName: "sidebarContent" }] : /* istanbul ignore next */ []));
210
- /** Accessible name for the icon-only sidebar trigger. */
211
429
  sidebarToggleLabel = input('Open navigation sidebar', /* @ts-ignore */
212
430
  ...(ngDevMode ? [{ debugName: "sidebarToggleLabel" }] : /* istanbul ignore next */ []));
213
- /** Accessible name for the icon-only mobile menu trigger. */
214
431
  mobileMenuLabel = input('Open navigation menu', /* @ts-ignore */
215
432
  ...(ngDevMode ? [{ debugName: "mobileMenuLabel" }] : /* istanbul ignore next */ []));
433
+ mobileMenuCloseLabel = input('Close navigation menu', /* @ts-ignore */
434
+ ...(ngDevMode ? [{ debugName: "mobileMenuCloseLabel" }] : /* istanbul ignore next */ []));
216
435
  userMenuTrigger = input(/* @ts-ignore */
217
436
  ...(ngDevMode ? [undefined, { debugName: "userMenuTrigger" }] : /* istanbul ignore next */ []));
218
- /** Menu data inputs */
219
- leftMenus = input([], /* @ts-ignore */
220
- ...(ngDevMode ? [{ debugName: "leftMenus" }] : /* istanbul ignore next */ []));
221
- leftMenuGroups = input([], /* @ts-ignore */
222
- ...(ngDevMode ? [{ debugName: "leftMenuGroups" }] : /* istanbul ignore next */ []));
223
- rightMenus = input([], /* @ts-ignore */
224
- ...(ngDevMode ? [{ debugName: "rightMenus" }] : /* istanbul ignore next */ []));
225
- topMenus = input([], /* @ts-ignore */
226
- ...(ngDevMode ? [{ debugName: "topMenus" }] : /* istanbul ignore next */ []));
227
- topSecondaryMenus = input([], /* @ts-ignore */
228
- ...(ngDevMode ? [{ debugName: "topSecondaryMenus" }] : /* istanbul ignore next */ []));
229
- userMenus = input([], /* @ts-ignore */
230
- ...(ngDevMode ? [{ debugName: "userMenus" }] : /* istanbul ignore next */ []));
231
- logo = input('assets/logo.png', /* @ts-ignore */
437
+ userMenuLabel = input('Open account menu', /* @ts-ignore */
438
+ ...(ngDevMode ? [{ debugName: "userMenuLabel" }] : /* istanbul ignore next */ []));
439
+ /** Main header destinations; use `activeMatch: 'prefix'` for whole sections. */
440
+ primaryNavigation = input([], /* @ts-ignore */
441
+ ...(ngDevMode ? [{ debugName: "primaryNavigation" }] : /* istanbul ignore next */ []));
442
+ navigationGroups = input([], /* @ts-ignore */
443
+ ...(ngDevMode ? [{ debugName: "navigationGroups" }] : /* istanbul ignore next */ []));
444
+ utilityNavigation = input([], /* @ts-ignore */
445
+ ...(ngDevMode ? [{ debugName: "utilityNavigation" }] : /* istanbul ignore next */ []));
446
+ sectionNavigation = input([], /* @ts-ignore */
447
+ ...(ngDevMode ? [{ debugName: "sectionNavigation" }] : /* istanbul ignore next */ []));
448
+ secondaryNavigationGroups = input([], /* @ts-ignore */
449
+ ...(ngDevMode ? [{ debugName: "secondaryNavigationGroups" }] : /* istanbul ignore next */ []));
450
+ /** Category navbar with wide card panels; mobile renders expandable groups. */
451
+ flyoutNavigationGroups = input([], /* @ts-ignore */
452
+ ...(ngDevMode ? [{ debugName: "flyoutNavigationGroups" }] : /* istanbul ignore next */ []));
453
+ flyoutNavigationLabel = input('Fly-out navigation', /* @ts-ignore */
454
+ ...(ngDevMode ? [{ debugName: "flyoutNavigationLabel" }] : /* istanbul ignore next */ []));
455
+ flyoutCloseLabel = input('Close navigation panel', /* @ts-ignore */
456
+ ...(ngDevMode ? [{ debugName: "flyoutCloseLabel" }] : /* istanbul ignore next */ []));
457
+ userMenuSections = input([], /* @ts-ignore */
458
+ ...(ngDevMode ? [{ debugName: "userMenuSections" }] : /* istanbul ignore next */ []));
459
+ /** Optional image. Without it, the brand name is shown instead of a broken image. */
460
+ logo = input('', /* @ts-ignore */
232
461
  ...(ngDevMode ? [{ debugName: "logo" }] : /* istanbul ignore next */ []));
462
+ brandName = input('Workspace', /* @ts-ignore */
463
+ ...(ngDevMode ? [{ debugName: "brandName" }] : /* istanbul ignore next */ []));
233
464
  logoLink = input('/', /* @ts-ignore */
234
465
  ...(ngDevMode ? [{ debugName: "logoLink" }] : /* istanbul ignore next */ []));
235
- logoClass = input('', { ...(ngDevMode ? { debugName: "logoClass" } : /* istanbul ignore next */ {}), alias: 'logoClass' });
236
- headerClass = input('', { ...(ngDevMode ? { debugName: "headerClass" } : /* istanbul ignore next */ {}), alias: 'headerClass' });
237
- contentClass = input('', { ...(ngDevMode ? { debugName: "contentClass" } : /* istanbul ignore next */ {}), alias: 'contentClass' });
238
- contentBottomClass = input('', { ...(ngDevMode ? { debugName: "contentBottomClass" } : /* istanbul ignore next */ {}), alias: 'contentBottomClass' });
239
- /** Container class inputs */
240
- leftMenuClass = input('', { ...(ngDevMode ? { debugName: "leftMenuClass" } : /* istanbul ignore next */ {}), alias: 'leftClass' });
241
- rightMenuClass = input('', { ...(ngDevMode ? { debugName: "rightMenuClass" } : /* istanbul ignore next */ {}), alias: 'rightClass' });
242
- topMenuClass = input('', { ...(ngDevMode ? { debugName: "topMenuClass" } : /* istanbul ignore next */ {}), alias: 'topClass' });
243
- /** Link/button base class inputs */
244
- leftLinkClass = input('', { ...(ngDevMode ? { debugName: "leftLinkClass" } : /* istanbul ignore next */ {}), alias: 'leftLinkClass' });
245
- rightLinkClass = input('', { ...(ngDevMode ? { debugName: "rightLinkClass" } : /* istanbul ignore next */ {}), alias: 'rightLinkClass' });
246
- topLinkClass = input('', { ...(ngDevMode ? { debugName: "topLinkClass" } : /* istanbul ignore next */ {}), alias: 'topLinkClass' });
247
- searchEnabled = input(false, /* @ts-ignore */
248
- ...(ngDevMode ? [{ debugName: "searchEnabled" }] : /* istanbul ignore next */ []));
466
+ logoClass = input('', /* @ts-ignore */
467
+ ...(ngDevMode ? [{ debugName: "logoClass" }] : /* istanbul ignore next */ []));
468
+ /** Disable for a shell preview embedded inside another page. */
469
+ fullHeight = input(true, /* @ts-ignore */
470
+ ...(ngDevMode ? [{ debugName: "fullHeight" }] : /* istanbul ignore next */ []));
471
+ headerClass = input('', /* @ts-ignore */
472
+ ...(ngDevMode ? [{ debugName: "headerClass" }] : /* istanbul ignore next */ []));
473
+ contentClass = input('', /* @ts-ignore */
474
+ ...(ngDevMode ? [{ debugName: "contentClass" }] : /* istanbul ignore next */ []));
475
+ primaryNavigationClass = input('', /* @ts-ignore */
476
+ ...(ngDevMode ? [{ debugName: "primaryNavigationClass" }] : /* istanbul ignore next */ []));
477
+ utilityNavigationClass = input('', /* @ts-ignore */
478
+ ...(ngDevMode ? [{ debugName: "utilityNavigationClass" }] : /* istanbul ignore next */ []));
479
+ sectionNavigationClass = input('', /* @ts-ignore */
480
+ ...(ngDevMode ? [{ debugName: "sectionNavigationClass" }] : /* istanbul ignore next */ []));
481
+ secondaryNavigationClass = input('', /* @ts-ignore */
482
+ ...(ngDevMode ? [{ debugName: "secondaryNavigationClass" }] : /* istanbul ignore next */ []));
483
+ flyoutNavigationClass = input('', /* @ts-ignore */
484
+ ...(ngDevMode ? [{ debugName: "flyoutNavigationClass" }] : /* istanbul ignore next */ []));
485
+ flyoutPanelClass = input('', /* @ts-ignore */
486
+ ...(ngDevMode ? [{ debugName: "flyoutPanelClass" }] : /* istanbul ignore next */ []));
487
+ navigationItemClass = input('', /* @ts-ignore */
488
+ ...(ngDevMode ? [{ debugName: "navigationItemClass" }] : /* istanbul ignore next */ []));
249
489
  loading = input(false, /* @ts-ignore */
250
490
  ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
251
- searchPlaceholderText = input('Select an page', /* @ts-ignore */
252
- ...(ngDevMode ? [{ debugName: "searchPlaceholderText" }] : /* istanbul ignore next */ []));
253
- searchEmptyText = input('No pages found', /* @ts-ignore */
491
+ loadingText = input('Loading content…', /* @ts-ignore */
492
+ ...(ngDevMode ? [{ debugName: "loadingText" }] : /* istanbul ignore next */ []));
493
+ skipLinkText = input('Skip to content', /* @ts-ignore */
494
+ ...(ngDevMode ? [{ debugName: "skipLinkText" }] : /* istanbul ignore next */ []));
495
+ searchEnabled = input(false, /* @ts-ignore */
496
+ ...(ngDevMode ? [{ debugName: "searchEnabled" }] : /* istanbul ignore next */ []));
497
+ searchPlaceholder = input('Search pages…', /* @ts-ignore */
498
+ ...(ngDevMode ? [{ debugName: "searchPlaceholder" }] : /* istanbul ignore next */ []));
499
+ searchEmptyText = input('No matching pages found.', /* @ts-ignore */
254
500
  ...(ngDevMode ? [{ debugName: "searchEmptyText" }] : /* istanbul ignore next */ []));
255
- searchOptionTemplate = input(/* @ts-ignore */
256
- ...(ngDevMode ? [undefined, { debugName: "searchOptionTemplate" }] : /* istanbul ignore next */ []));
257
- searchLoaderFn = input(/* @ts-ignore */
258
- ...(ngDevMode ? [undefined, { debugName: "searchLoaderFn" }] : /* istanbul ignore next */ []));
259
- searchTransformValueToSearch = input(/* @ts-ignore */
260
- ...(ngDevMode ? [undefined, { debugName: "searchTransformValueToSearch" }] : /* istanbul ignore next */ []));
261
- searchOptionChange = output();
262
- _logoClass = computed(() => hlm("h-10", this.logoClass()), /* @ts-ignore */
263
- ...(ngDevMode ? [{ debugName: "_logoClass" }] : /* istanbul ignore next */ []));
264
- _headerClass = computed(() => hlm("px-4 py-2 flex items-center justify-between bg-gray-100 border-b border-gray-300", this.headerClass()), /* @ts-ignore */
265
- ...(ngDevMode ? [{ debugName: "_headerClass" }] : /* istanbul ignore next */ []));
266
- _contentClass = computed(() => hlm("p-4 flex flex-col flex-1", this.contentClass()), /* @ts-ignore */
267
- ...(ngDevMode ? [{ debugName: "_contentClass" }] : /* istanbul ignore next */ []));
268
- _contentBottomClass = computed(() => hlm("flex-1", this.contentBottomClass()), /* @ts-ignore */
269
- ...(ngDevMode ? [{ debugName: "_contentBottomClass" }] : /* istanbul ignore next */ []));
270
- /** Computed merged classes for containers */
271
- _computedLeftClass = computed(() => hlm(commonLinkGroupClasses, this.leftMenuClass()), /* @ts-ignore */
272
- ...(ngDevMode ? [{ debugName: "_computedLeftClass" }] : /* istanbul ignore next */ []));
273
- _computedRightClass = computed(() => hlm(commonLinkGroupClasses, this.rightMenuClass()), /* @ts-ignore */
274
- ...(ngDevMode ? [{ debugName: "_computedRightClass" }] : /* istanbul ignore next */ []));
275
- _computedTopClass = computed(() => hlm("hidden md:flex flex-wrap items-center gap-x-4 gap-y-1 px-4 py-2 bg-gray-50 border-b border-gray-200", this.topMenuClass()), /* @ts-ignore */
276
- ...(ngDevMode ? [{ debugName: "_computedTopClass" }] : /* istanbul ignore next */ []));
277
- _computedTopSecondaryClass = computed(() => hlm("hidden md:flex flex-wrap items-center gap-x-4 gap-y-1 px-4 py-1.5 bg-gray-100/60 border-b border-gray-200", this.topMenuClass()), /* @ts-ignore */
278
- ...(ngDevMode ? [{ debugName: "_computedTopSecondaryClass" }] : /* istanbul ignore next */ []));
279
- /** Computed merged classes for link/button elements */
280
- _computedLeftLinkClass = computed(() => hlm(commonLinkClasses, this.leftLinkClass()), /* @ts-ignore */
281
- ...(ngDevMode ? [{ debugName: "_computedLeftLinkClass" }] : /* istanbul ignore next */ []));
282
- _computedRightLinkClass = computed(() => hlm(commonLinkClasses, this.rightLinkClass()), /* @ts-ignore */
283
- ...(ngDevMode ? [{ debugName: "_computedRightLinkClass" }] : /* istanbul ignore next */ []));
284
- _computedTopLinkClass = computed(() => hlm("text-secondary visited:text-secondary hover:text-primary cursor-pointer no-underline text-sm", this.topLinkClass()), /* @ts-ignore */
285
- ...(ngDevMode ? [{ debugName: "_computedTopLinkClass" }] : /* istanbul ignore next */ []));
286
- mobileMenuItemClass = commonLinkClasses;
501
+ searchErrorText = input('Search is unavailable. Please try again.', /* @ts-ignore */
502
+ ...(ngDevMode ? [{ debugName: "searchErrorText" }] : /* istanbul ignore next */ []));
503
+ searchResultTemplate = input(/* @ts-ignore */
504
+ ...(ngDevMode ? [undefined, { debugName: "searchResultTemplate" }] : /* istanbul ignore next */ []));
505
+ searchLoader = input(/* @ts-ignore */
506
+ ...(ngDevMode ? [undefined, { debugName: "searchLoader" }] : /* istanbul ignore next */ []));
507
+ searchResultLabel = input(/* @ts-ignore */
508
+ ...(ngDevMode ? [undefined, { debugName: "searchResultLabel" }] : /* istanbul ignore next */ []));
509
+ searchResultSelected = output();
287
510
  footerEnabled = input(false, /* @ts-ignore */
288
511
  ...(ngDevMode ? [{ debugName: "footerEnabled" }] : /* istanbul ignore next */ []));
289
- footerMenus = input([], /* @ts-ignore */
290
- ...(ngDevMode ? [{ debugName: "footerMenus" }] : /* istanbul ignore next */ []));
291
- footerMenuClass = input('', { ...(ngDevMode ? { debugName: "footerMenuClass" } : /* istanbul ignore next */ {}), alias: 'footerMenuClass' });
292
- footerLinkClass = input('', { ...(ngDevMode ? { debugName: "footerLinkClass" } : /* istanbul ignore next */ {}), alias: 'footerLinkClass' });
293
- footerContent = input('© 2024 My Company', /* @ts-ignore */
294
- ...(ngDevMode ? [{ debugName: "footerContent" }] : /* istanbul ignore next */ []));
295
- footerClass = input('', { ...(ngDevMode ? { debugName: "footerClass" } : /* istanbul ignore next */ {}), alias: 'footerClass' });
296
- _footerClass = computed(() => hlm("px-4 py-4 bg-gray-100 border-t border-gray-300 text-sm text-gray-600", this.footerClass()), /* @ts-ignore */
297
- ...(ngDevMode ? [{ debugName: "_footerClass" }] : /* istanbul ignore next */ []));
298
- _computedFooterMenuClass = computed(() => hlm("flex flex-wrap justify-center space-x-4", this.footerMenuClass()), /* @ts-ignore */
299
- ...(ngDevMode ? [{ debugName: "_computedFooterMenuClass" }] : /* istanbul ignore next */ []));
300
- _computedFooterLinkClass = computed(() => hlm("text-secondary hover:text-primary cursor-pointer no-underline", this.footerLinkClass()), /* @ts-ignore */
301
- ...(ngDevMode ? [{ debugName: "_computedFooterLinkClass" }] : /* istanbul ignore next */ []));
302
- /** Mobile menu state */
512
+ footerNavigation = input([], /* @ts-ignore */
513
+ ...(ngDevMode ? [{ debugName: "footerNavigation" }] : /* istanbul ignore next */ []));
514
+ footerText = input('', /* @ts-ignore */
515
+ ...(ngDevMode ? [{ debugName: "footerText" }] : /* istanbul ignore next */ []));
516
+ footerClass = input('', /* @ts-ignore */
517
+ ...(ngDevMode ? [{ debugName: "footerClass" }] : /* istanbul ignore next */ []));
518
+ logoClasses = computed(() => hlm("h-8 w-auto max-w-40 object-contain", this.logoClass()), /* @ts-ignore */
519
+ ...(ngDevMode ? [{ debugName: "logoClasses" }] : /* istanbul ignore next */ []));
520
+ headerClasses = computed(() => hlm("flex min-h-16 flex-wrap items-center justify-between gap-3 border-b border-border bg-background px-4 py-3 sm:px-6", this.headerClass()), /* @ts-ignore */
521
+ ...(ngDevMode ? [{ debugName: "headerClasses" }] : /* istanbul ignore next */ []));
522
+ contentClasses = computed(() => hlm("flex min-w-0 flex-1 flex-col p-4 outline-none sm:p-6", this.contentClass()), /* @ts-ignore */
523
+ ...(ngDevMode ? [{ debugName: "contentClasses" }] : /* istanbul ignore next */ []));
524
+ primaryNavigationClasses = computed(() => hlm("hidden md:flex flex-wrap items-center gap-1", this.primaryNavigationClass()), /* @ts-ignore */
525
+ ...(ngDevMode ? [{ debugName: "primaryNavigationClasses" }] : /* istanbul ignore next */ []));
526
+ utilityNavigationClasses = computed(() => hlm("hidden md:flex flex-wrap items-center gap-1", this.utilityNavigationClass()), /* @ts-ignore */
527
+ ...(ngDevMode ? [{ debugName: "utilityNavigationClasses" }] : /* istanbul ignore next */ []));
528
+ sectionNavigationClasses = computed(() => hlm("hidden md:flex items-center gap-1 overflow-x-auto border-b border-border bg-background px-4 py-2 sm:px-6", this.sectionNavigationClass()), /* @ts-ignore */
529
+ ...(ngDevMode ? [{ debugName: "sectionNavigationClasses" }] : /* istanbul ignore next */ []));
530
+ secondaryNavigationClasses = computed(() => hlm("hidden md:flex items-center gap-5 overflow-x-auto border-b border-border bg-foreground/3 px-4 py-2 sm:px-6", this.secondaryNavigationClass()), /* @ts-ignore */
531
+ ...(ngDevMode ? [{ debugName: "secondaryNavigationClasses" }] : /* istanbul ignore next */ []));
532
+ footerClasses = computed(() => hlm("flex flex-wrap items-center justify-between gap-x-6 gap-y-2 border-t border-border bg-background px-4 py-4 text-sm text-foreground/75 sm:px-6", this.footerClass()), /* @ts-ignore */
533
+ ...(ngDevMode ? [{ debugName: "footerClasses" }] : /* istanbul ignore next */ []));
534
+ iconButtonClasses = "inline-flex size-11 shrink-0 items-center justify-center rounded-lg border border-border bg-background text-foreground cursor-pointer hover:bg-foreground/5 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring";
303
535
  mobileMenuOpen = signal(false, /* @ts-ignore */
304
536
  ...(ngDevMode ? [{ debugName: "mobileMenuOpen" }] : /* istanbul ignore next */ []));
305
537
  isMobile = signal(false, /* @ts-ignore */
306
538
  ...(ngDevMode ? [{ debugName: "isMobile" }] : /* istanbul ignore next */ []));
307
- /** Expanded state of the sidebar sheet, kept in sync with BrnSheet state changes. */
308
539
  sidebarOpen = signal(false, /* @ts-ignore */
309
540
  ...(ngDevMode ? [{ debugName: "sidebarOpen" }] : /* istanbul ignore next */ []));
310
- constructor(breakpointObserver) {
311
- this.breakpointObserver = breakpointObserver;
541
+ sidebarContext = { close: () => this.closeSidebar() };
542
+ constructor() {
312
543
  this.breakpointObserver
313
544
  .observe([EG_LAYOUT_SIMPLE_MOBILE_BREAKPOINT])
314
545
  .pipe(takeUntilDestroyed(this.destroyRef))
315
- .subscribe((result) => {
316
- this.isMobile.set(result.matches);
317
- });
318
- effect(() => {
319
- // Desktop navigation replaces the mobile menu above the breakpoint, so an
320
- // open mobile menu must not cross the boundary into desktop layout.
321
- if (!this.isMobile() && this.mobileMenuOpen()) {
546
+ .subscribe(({ matches }) => {
547
+ this.isMobile.set(matches);
548
+ if (!matches)
322
549
  this.mobileMenuOpen.set(false);
550
+ });
551
+ this.router.events.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((event) => {
552
+ if (event instanceof NavigationEnd) {
553
+ if (this.mobileMenuOpen())
554
+ this.closeMobileMenu(true);
555
+ this.closeSidebar();
323
556
  }
324
557
  });
325
558
  effect((onCleanup) => {
326
- const sheet = this.viewchildSheetRef()?.viewchildSheetRef();
559
+ const sheet = this.sidebar()?.sheet();
327
560
  if (!sheet)
328
561
  return;
329
562
  this.sidebarOpen.set(sheet.stateComputed() === 'open');
330
563
  const subscriptions = [
331
564
  sheet.stateChanged.subscribe((state) => this.sidebarOpen.set(state === 'open')),
332
- // Escape/backdrop closes complete the dialog without a 'closed' state emission.
333
565
  sheet.closed.subscribe(() => this.sidebarOpen.set(false)),
334
566
  ];
335
567
  onCleanup(() => subscriptions.forEach((subscription) => subscription.unsubscribe()));
@@ -338,43 +570,47 @@ class EgLayoutSimple {
338
570
  toggleMobileMenu() {
339
571
  this.mobileMenuOpen.update((open) => !open);
340
572
  }
341
- closeMobileMenu() {
573
+ closeMobileMenu(restoreFocus = false) {
342
574
  this.mobileMenuOpen.set(false);
575
+ if (restoreFocus)
576
+ this.mobileMenuTrigger()?.nativeElement.focus();
577
+ }
578
+ selectSearchResult(item) {
579
+ this.closeMobileMenu(true);
580
+ this.searchResultSelected.emit(item);
343
581
  }
344
- onSearchOptionChange(value) {
345
- this.searchOptionChange.emit(value);
582
+ focusContent(event) {
583
+ event.preventDefault();
584
+ this.mainContent()?.nativeElement.focus();
346
585
  }
347
- viewchildSheetRef = viewChild(EgLayoutSimpleSidebar, /* @ts-ignore */
348
- ...(ngDevMode ? [{ debugName: "viewchildSheetRef" }] : /* istanbul ignore next */ []));
349
586
  openSidebar() {
350
- this.viewchildSheetRef()?.openSheet();
587
+ this.closeMobileMenu();
588
+ this.sidebar()?.open();
351
589
  }
352
590
  closeSidebar() {
353
- this.viewchildSheetRef()?.closeSheet();
591
+ this.sidebar()?.close();
354
592
  }
355
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgLayoutSimple, deps: [{ token: i1$2.BreakpointObserver }], target: i0.ɵɵFactoryTarget.Component });
356
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgLayoutSimple, isStandalone: true, selector: "eg-layout-simple", inputs: { sidebarEnabled: { classPropertyName: "sidebarEnabled", publicName: "sidebarEnabled", isSignal: true, isRequired: false, transformFunction: null }, sidebarTitle: { classPropertyName: "sidebarTitle", publicName: "sidebarTitle", isSignal: true, isRequired: false, transformFunction: null }, sidebarContent: { classPropertyName: "sidebarContent", publicName: "sidebarContent", isSignal: true, isRequired: false, transformFunction: null }, sidebarToggleLabel: { classPropertyName: "sidebarToggleLabel", publicName: "sidebarToggleLabel", isSignal: true, isRequired: false, transformFunction: null }, mobileMenuLabel: { classPropertyName: "mobileMenuLabel", publicName: "mobileMenuLabel", isSignal: true, isRequired: false, transformFunction: null }, userMenuTrigger: { classPropertyName: "userMenuTrigger", publicName: "userMenuTrigger", isSignal: true, isRequired: false, transformFunction: null }, leftMenus: { classPropertyName: "leftMenus", publicName: "leftMenus", isSignal: true, isRequired: false, transformFunction: null }, leftMenuGroups: { classPropertyName: "leftMenuGroups", publicName: "leftMenuGroups", isSignal: true, isRequired: false, transformFunction: null }, rightMenus: { classPropertyName: "rightMenus", publicName: "rightMenus", isSignal: true, isRequired: false, transformFunction: null }, topMenus: { classPropertyName: "topMenus", publicName: "topMenus", isSignal: true, isRequired: false, transformFunction: null }, topSecondaryMenus: { classPropertyName: "topSecondaryMenus", publicName: "topSecondaryMenus", isSignal: true, isRequired: false, transformFunction: null }, userMenus: { classPropertyName: "userMenus", publicName: "userMenus", isSignal: true, isRequired: false, transformFunction: null }, logo: { classPropertyName: "logo", publicName: "logo", isSignal: true, isRequired: false, transformFunction: null }, logoLink: { classPropertyName: "logoLink", publicName: "logoLink", isSignal: true, isRequired: false, transformFunction: null }, logoClass: { classPropertyName: "logoClass", publicName: "logoClass", isSignal: true, isRequired: false, transformFunction: null }, headerClass: { classPropertyName: "headerClass", publicName: "headerClass", isSignal: true, isRequired: false, transformFunction: null }, contentClass: { classPropertyName: "contentClass", publicName: "contentClass", isSignal: true, isRequired: false, transformFunction: null }, contentBottomClass: { classPropertyName: "contentBottomClass", publicName: "contentBottomClass", isSignal: true, isRequired: false, transformFunction: null }, leftMenuClass: { classPropertyName: "leftMenuClass", publicName: "leftClass", isSignal: true, isRequired: false, transformFunction: null }, rightMenuClass: { classPropertyName: "rightMenuClass", publicName: "rightClass", isSignal: true, isRequired: false, transformFunction: null }, topMenuClass: { classPropertyName: "topMenuClass", publicName: "topClass", isSignal: true, isRequired: false, transformFunction: null }, leftLinkClass: { classPropertyName: "leftLinkClass", publicName: "leftLinkClass", isSignal: true, isRequired: false, transformFunction: null }, rightLinkClass: { classPropertyName: "rightLinkClass", publicName: "rightLinkClass", isSignal: true, isRequired: false, transformFunction: null }, topLinkClass: { classPropertyName: "topLinkClass", publicName: "topLinkClass", isSignal: true, isRequired: false, transformFunction: null }, searchEnabled: { classPropertyName: "searchEnabled", publicName: "searchEnabled", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholderText: { classPropertyName: "searchPlaceholderText", publicName: "searchPlaceholderText", isSignal: true, isRequired: false, transformFunction: null }, searchEmptyText: { classPropertyName: "searchEmptyText", publicName: "searchEmptyText", isSignal: true, isRequired: false, transformFunction: null }, searchOptionTemplate: { classPropertyName: "searchOptionTemplate", publicName: "searchOptionTemplate", isSignal: true, isRequired: false, transformFunction: null }, searchLoaderFn: { classPropertyName: "searchLoaderFn", publicName: "searchLoaderFn", isSignal: true, isRequired: false, transformFunction: null }, searchTransformValueToSearch: { classPropertyName: "searchTransformValueToSearch", publicName: "searchTransformValueToSearch", isSignal: true, isRequired: false, transformFunction: null }, footerEnabled: { classPropertyName: "footerEnabled", publicName: "footerEnabled", isSignal: true, isRequired: false, transformFunction: null }, footerMenus: { classPropertyName: "footerMenus", publicName: "footerMenus", isSignal: true, isRequired: false, transformFunction: null }, footerMenuClass: { classPropertyName: "footerMenuClass", publicName: "footerMenuClass", isSignal: true, isRequired: false, transformFunction: null }, footerLinkClass: { classPropertyName: "footerLinkClass", publicName: "footerLinkClass", isSignal: true, isRequired: false, transformFunction: null }, footerContent: { classPropertyName: "footerContent", publicName: "footerContent", isSignal: true, isRequired: false, transformFunction: null }, footerClass: { classPropertyName: "footerClass", publicName: "footerClass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { searchOptionChange: "searchOptionChange" }, providers: [
357
- provideIcons({
358
- lucideMenu,
359
- }),
360
- ], viewQueries: [{ propertyName: "viewchildSheetRef", first: true, predicate: EgLayoutSimpleSidebar, descendants: true, isSignal: true }], ngImport: i0, template: "<ng-template #menuIconTemplate>\n <ng-icon [svg]=\"menuIcon\" size=\"20px\" class=\"[&_svg]:w-[inherit]! [&_svg]:h-[inherit]!\" />\n</ng-template>\n\n<eg-layout-simple-sidebar\n [title]=\"sidebarTitle()\"\n [content]=\"sidebarContent()\"\n [contentContext]=\"{ close: closeSidebar.bind(this) }\"\n/>\n\n<div class=\"flex flex-col min-h-screen\">\n <!-- Header -->\n <header [class]=\"_headerClass()\">\n <div class=\"flex items-center space-x-4\">\n @if (sidebarEnabled()) {\n <button\n hlmButton\n variant=\"secondary\"\n appearance=\"outline\"\n size=\"icon\"\n [icon]=\"menuIconTemplate\"\n iconPosition=\"left\"\n class=\"bg-transparent! border-gray-400! hover:border-gray-500!\"\n [attr.aria-label]=\"sidebarToggleLabel()\"\n [attr.aria-expanded]=\"sidebarOpen()\"\n aria-controls=\"eg-layout-simple-sidebar\"\n (click)=\"openSidebar()\"\n ></button>\n }\n\n <div class=\"logo\">\n <a [routerLink]=\"logoLink()\">\n <img [src]=\"logo()\" alt=\"Logo\" [class]=\"_logoClass()\" />\n </a>\n </div>\n <!-- Left menus (desktop) -->\n <nav [class]=\"_computedLeftClass()\">\n @for (item of leftMenus(); track item.label) { @if (item.link) {\n <a [routerLink]=\"item.link\" [class]=\"hlm(_computedLeftLinkClass(), item.class || '')\"> {{ item.label }} </a>\n } @else {\n <button type=\"button\" (click)=\"item.action?.()\" [class]=\"hlm(_computedLeftLinkClass(), item.class || '')\">\n {{ item.label }}\n </button>\n } } @for (group of leftMenuGroups(); track $index) {\n <div class=\"relative group\">\n <button\n type=\"button\"\n class=\"text-left text-secondary hover:text-primary cursor-pointer no-underline py-1 flex items-center gap-1\"\n >\n {{ group.label ?? 'Menu' }}\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-3 w-3\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\" />\n </svg>\n </button>\n <div\n class=\"absolute left-0 top-full mt-1 hidden group-hover:flex flex-col min-w-[12rem] bg-popover text-popover-foreground border border-gray-200 rounded-md shadow-md py-1 z-50\"\n >\n @if (group.label) {\n <div class=\"px-3 py-1 text-xs font-semibold text-muted-foreground\">{{ group.label }}</div>\n } @for (item of group.items; track item.label) { @if (item.link) {\n <a\n [routerLink]=\"item.link\"\n [class]=\"hlm('tw:px-3 tw:py-1.5 tw:text-sm tw:no-underline tw:hover:bg-secondary tw:cursor-pointer', item.class || '')\"\n >\n {{ item.label }}\n </a>\n } @else {\n <button\n type=\"button\"\n (click)=\"item.action?.()\"\n [class]=\"hlm('tw:px-3 tw:py-1.5 tw:text-sm tw:text-left tw:hover:bg-secondary tw:cursor-pointer tw:bg-transparent tw:border-0 tw:w-full', item.class || '')\"\n >\n {{ item.label }}\n </button>\n } }\n </div>\n </div>\n }\n </nav>\n </div>\n\n <!-- Right menus (desktop) -->\n <nav [class]=\"_computedRightClass()\">\n @if (searchEnabled()) {\n <eg-generic-autocomplete\n [loaderFn]=\"searchLoaderFn()\"\n [transformValueToSearch]=\"searchTransformValueToSearch()\"\n [placeholderText]=\"searchPlaceholderText()\"\n [emptyText]=\"searchEmptyText()\"\n (optionChange)=\"onSearchOptionChange($event)\"\n ></eg-generic-autocomplete>\n } @for (item of rightMenus(); track item.label) { @if (item.link) {\n <a [routerLink]=\"item.link\" [class]=\"hlm(_computedRightLinkClass(), item.class || '')\"> {{ item.label }} </a>\n } @else {\n <button type=\"button\" (click)=\"item.action?.()\" [class]=\"hlm(_computedRightLinkClass(), item.class || '')\">\n {{ item.label }}\n </button>\n } } @let uMenus = userMenus(); @if (uMenus && uMenus.length > 0) {\n <eg-layout-simple-user-menu [menus]=\"uMenus\" [menuTrigger]=\"userMenuTrigger()\" />\n }\n </nav>\n\n <!-- Mobile menu button -->\n <button\n (click)=\"toggleMobileMenu()\"\n class=\"md:hidden p-2 text-gray-700 hover:text-primary focus:outline-none cursor-pointer\"\n [attr.aria-label]=\"mobileMenuLabel()\"\n [attr.aria-expanded]=\"mobileMenuOpen()\"\n aria-controls=\"eg-layout-simple-mobile-menu\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-6 w-6\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 6h16M4 12h16M4 18h16\" />\n </svg>\n </button>\n </header>\n\n @let _topMenus = leftMenus().concat(rightMenus()); @let _userMenus = userMenus(); @let _leftMenuGroups =\n leftMenuGroups(); @let _topMenuItems = topMenus(); @let _topSecondaryGroups = topSecondaryMenus();\n\n <!-- Top menus (desktop) -->\n @if (_topMenuItems.length > 0) {\n <nav [class]=\"_computedTopClass()\">\n @for (item of _topMenuItems; track item.label) { @if (item.link) {\n <a [routerLink]=\"item.link\" [class]=\"hlm(_computedTopLinkClass(), item.class || '')\"> {{ item.label }} </a>\n } @else {\n <span [class]=\"hlm(_computedTopLinkClass(), item.class || '')\">{{ item.label }}</span>\n } }\n </nav>\n }\n\n <!-- Top secondary menus (desktop) — second row showing items grouped by parent label -->\n @if (_topSecondaryGroups.length > 0) {\n <nav [class]=\"_computedTopSecondaryClass()\">\n @for (group of _topSecondaryGroups; track group.label) {\n <span class=\"flex flex-wrap items-center gap-x-3\">\n @if (group.label) {\n <span class=\"text-xs font-semibold uppercase text-muted-foreground\">{{ group.label }}</span>\n } @for (item of group.items; track item.label) { @if (item.link) {\n <a\n [routerLink]=\"item.link\"\n [class]=\"hlm(_computedTopLinkClass(), 'tw:text-xs tw:hover:underline', item.class || '')\"\n >\n {{ item.label }}\n </a>\n } @else {\n <button\n type=\"button\"\n (click)=\"item.action?.()\"\n [class]=\"hlm(_computedTopLinkClass(), 'tw:text-xs tw:hover:underline', item.class || '')\"\n >\n {{ item.label }}\n </button>\n } }\n </span>\n }\n </nav>\n }\n\n <!-- Mobile menu -->\n @if (isMobile() && mobileMenuOpen()) {\n <div id=\"eg-layout-simple-mobile-menu\" class=\"w-full border-none\" (keydown.escape)=\"closeMobileMenu()\">\n @if (_topMenuItems.length > 0) {\n <eg-layout-simple-mobile-menu-group [items]=\"_topMenuItems\" (itemClick)=\"closeMobileMenu()\" />\n } @for (group of _topSecondaryGroups; track group.label) { @if (group.items && group.items.length > 0) {\n <eg-layout-simple-mobile-menu-group\n [items]=\"group.items\"\n [label]=\"group.label\"\n (itemClick)=\"closeMobileMenu()\"\n ></eg-layout-simple-mobile-menu-group>\n } }\n\n <eg-layout-simple-mobile-menu-group [items]=\"_topMenus\" (itemClick)=\"closeMobileMenu()\" />\n\n @for (group of _leftMenuGroups; track $index) { @if (group.items && group.items.length > 0) {\n <eg-layout-simple-mobile-menu-group\n [items]=\"group.items\"\n [label]=\"group.label\"\n (itemClick)=\"closeMobileMenu()\"\n ></eg-layout-simple-mobile-menu-group>\n } } @if ((_topMenus.length > 0 || _leftMenuGroups.length > 0) && _userMenus.length > 0) {\n <hr class=\"border-gray-300 my-2\" />\n } @for (section of userMenus(); track section.label) {\n <eg-layout-simple-mobile-menu-group\n [items]=\"section.items\"\n [label]=\"section.label\"\n (itemClick)=\"closeMobileMenu()\"\n ></eg-layout-simple-mobile-menu-group>\n\n @if (section.separator) {\n <hr class=\"border-gray-300 my-2\" />\n } }\n </div>\n }\n\n <!-- Main content -->\n <main [class]=\"_contentClass()\">\n @if (!loading()) {\n <div class=\"flex items-center justify-center h-full eg-layout-simple-main-content\">\n <ng-content />\n </div>\n }\n <div [class]=\"_contentBottomClass()\"></div>\n </main>\n\n @if (footerEnabled()) {\n <footer [class]=\"_footerClass()\">\n <nav [class]=\"_computedFooterMenuClass()\">\n @for (item of footerMenus(); track item.label) { @if (item.link) {\n <a [routerLink]=\"item.link\" [class]=\"hlm(_computedFooterLinkClass(), item.class || '')\"> {{ item.label }} </a>\n } @else {\n <button type=\"button\" (click)=\"item.action?.()\" [class]=\"hlm(_computedFooterLinkClass(), item.class || '')\">\n {{ item.label }}\n </button>\n } }\n </nav>\n\n <div class=\"mt-2 text-center\">{{ footerContent() }}</div>\n </footer>\n }\n</div>\n", dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "component", type: EgLayoutSimpleUserMenu, selector: "eg-layout-simple-user-menu", inputs: ["menus", "menuTrigger"] }, { kind: "component", type: EgLayoutSimpleSidebar, selector: "eg-layout-simple-sidebar", inputs: ["side", "title", "content", "contentContext"] }, { kind: "component", type: EgLayoutSimpleMobileMenuGroup, selector: "eg-layout-simple-mobile-menu-group", inputs: ["label", "items"], outputs: ["itemClick"] }, { kind: "component", type: EgGenericAutocomplete, selector: "eg-generic-autocomplete", inputs: ["placeholderText", "emptyText", "loaderFn", "transformValueToSearch"], outputs: ["optionChange"] }, { kind: "component", type: HlmButton, selector: "button[hlmButton], a[hlmButton]", inputs: ["variant", "size", "appearance", "loading", "icon", "iconPosition", "class", "spinnerUserClass", "disabled", "type"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }] });
593
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgLayoutSimple, deps: [], target: i0.ɵɵFactoryTarget.Component });
594
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgLayoutSimple, isStandalone: true, selector: "eg-layout-simple", inputs: { sidebarEnabled: { classPropertyName: "sidebarEnabled", publicName: "sidebarEnabled", isSignal: true, isRequired: false, transformFunction: null }, sidebarTitle: { classPropertyName: "sidebarTitle", publicName: "sidebarTitle", isSignal: true, isRequired: false, transformFunction: null }, sidebarContent: { classPropertyName: "sidebarContent", publicName: "sidebarContent", isSignal: true, isRequired: false, transformFunction: null }, sidebarToggleLabel: { classPropertyName: "sidebarToggleLabel", publicName: "sidebarToggleLabel", isSignal: true, isRequired: false, transformFunction: null }, mobileMenuLabel: { classPropertyName: "mobileMenuLabel", publicName: "mobileMenuLabel", isSignal: true, isRequired: false, transformFunction: null }, mobileMenuCloseLabel: { classPropertyName: "mobileMenuCloseLabel", publicName: "mobileMenuCloseLabel", isSignal: true, isRequired: false, transformFunction: null }, userMenuTrigger: { classPropertyName: "userMenuTrigger", publicName: "userMenuTrigger", isSignal: true, isRequired: false, transformFunction: null }, userMenuLabel: { classPropertyName: "userMenuLabel", publicName: "userMenuLabel", isSignal: true, isRequired: false, transformFunction: null }, primaryNavigation: { classPropertyName: "primaryNavigation", publicName: "primaryNavigation", isSignal: true, isRequired: false, transformFunction: null }, navigationGroups: { classPropertyName: "navigationGroups", publicName: "navigationGroups", isSignal: true, isRequired: false, transformFunction: null }, utilityNavigation: { classPropertyName: "utilityNavigation", publicName: "utilityNavigation", isSignal: true, isRequired: false, transformFunction: null }, sectionNavigation: { classPropertyName: "sectionNavigation", publicName: "sectionNavigation", isSignal: true, isRequired: false, transformFunction: null }, secondaryNavigationGroups: { classPropertyName: "secondaryNavigationGroups", publicName: "secondaryNavigationGroups", isSignal: true, isRequired: false, transformFunction: null }, flyoutNavigationGroups: { classPropertyName: "flyoutNavigationGroups", publicName: "flyoutNavigationGroups", isSignal: true, isRequired: false, transformFunction: null }, flyoutNavigationLabel: { classPropertyName: "flyoutNavigationLabel", publicName: "flyoutNavigationLabel", isSignal: true, isRequired: false, transformFunction: null }, flyoutCloseLabel: { classPropertyName: "flyoutCloseLabel", publicName: "flyoutCloseLabel", isSignal: true, isRequired: false, transformFunction: null }, userMenuSections: { classPropertyName: "userMenuSections", publicName: "userMenuSections", isSignal: true, isRequired: false, transformFunction: null }, logo: { classPropertyName: "logo", publicName: "logo", isSignal: true, isRequired: false, transformFunction: null }, brandName: { classPropertyName: "brandName", publicName: "brandName", isSignal: true, isRequired: false, transformFunction: null }, logoLink: { classPropertyName: "logoLink", publicName: "logoLink", isSignal: true, isRequired: false, transformFunction: null }, logoClass: { classPropertyName: "logoClass", publicName: "logoClass", isSignal: true, isRequired: false, transformFunction: null }, fullHeight: { classPropertyName: "fullHeight", publicName: "fullHeight", isSignal: true, isRequired: false, transformFunction: null }, headerClass: { classPropertyName: "headerClass", publicName: "headerClass", isSignal: true, isRequired: false, transformFunction: null }, contentClass: { classPropertyName: "contentClass", publicName: "contentClass", isSignal: true, isRequired: false, transformFunction: null }, primaryNavigationClass: { classPropertyName: "primaryNavigationClass", publicName: "primaryNavigationClass", isSignal: true, isRequired: false, transformFunction: null }, utilityNavigationClass: { classPropertyName: "utilityNavigationClass", publicName: "utilityNavigationClass", isSignal: true, isRequired: false, transformFunction: null }, sectionNavigationClass: { classPropertyName: "sectionNavigationClass", publicName: "sectionNavigationClass", isSignal: true, isRequired: false, transformFunction: null }, secondaryNavigationClass: { classPropertyName: "secondaryNavigationClass", publicName: "secondaryNavigationClass", isSignal: true, isRequired: false, transformFunction: null }, flyoutNavigationClass: { classPropertyName: "flyoutNavigationClass", publicName: "flyoutNavigationClass", isSignal: true, isRequired: false, transformFunction: null }, flyoutPanelClass: { classPropertyName: "flyoutPanelClass", publicName: "flyoutPanelClass", isSignal: true, isRequired: false, transformFunction: null }, navigationItemClass: { classPropertyName: "navigationItemClass", publicName: "navigationItemClass", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, loadingText: { classPropertyName: "loadingText", publicName: "loadingText", isSignal: true, isRequired: false, transformFunction: null }, skipLinkText: { classPropertyName: "skipLinkText", publicName: "skipLinkText", isSignal: true, isRequired: false, transformFunction: null }, searchEnabled: { classPropertyName: "searchEnabled", publicName: "searchEnabled", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, searchEmptyText: { classPropertyName: "searchEmptyText", publicName: "searchEmptyText", isSignal: true, isRequired: false, transformFunction: null }, searchErrorText: { classPropertyName: "searchErrorText", publicName: "searchErrorText", isSignal: true, isRequired: false, transformFunction: null }, searchResultTemplate: { classPropertyName: "searchResultTemplate", publicName: "searchResultTemplate", isSignal: true, isRequired: false, transformFunction: null }, searchLoader: { classPropertyName: "searchLoader", publicName: "searchLoader", isSignal: true, isRequired: false, transformFunction: null }, searchResultLabel: { classPropertyName: "searchResultLabel", publicName: "searchResultLabel", isSignal: true, isRequired: false, transformFunction: null }, footerEnabled: { classPropertyName: "footerEnabled", publicName: "footerEnabled", isSignal: true, isRequired: false, transformFunction: null }, footerNavigation: { classPropertyName: "footerNavigation", publicName: "footerNavigation", isSignal: true, isRequired: false, transformFunction: null }, footerText: { classPropertyName: "footerText", publicName: "footerText", isSignal: true, isRequired: false, transformFunction: null }, footerClass: { classPropertyName: "footerClass", publicName: "footerClass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { searchResultSelected: "searchResultSelected" }, host: { classAttribute: "block min-w-0" }, viewQueries: [{ propertyName: "mobileMenuTrigger", first: true, predicate: ["mobileMenuTrigger"], descendants: true, isSignal: true }, { propertyName: "mainContent", first: true, predicate: ["mainContent"], descendants: true, isSignal: true }, { propertyName: "sidebar", first: true, predicate: EgLayoutSimpleSidebar, descendants: true, isSignal: true }], ngImport: i0, template: "@if (sidebarEnabled()) {\n<eg-layout-simple-sidebar\n [panelId]=\"sidebarId\"\n [title]=\"sidebarTitle()\"\n [content]=\"sidebarContent()\"\n [contentContext]=\"sidebarContext\"\n/>\n}\n\n<div\n [class]=\"hlm('tw:relative tw:isolate tw:flex tw:min-w-0 tw:flex-col tw:bg-background tw:text-foreground', fullHeight() && 'tw:min-h-dvh')\"\n>\n <a\n [href]=\"'#' + mainId\"\n (click)=\"focusContent($event)\"\n class=\"sr-only focus:not-sr-only focus:absolute focus:left-4 focus:top-2 focus:z-50 focus:rounded-lg focus:bg-background focus:px-4 focus:py-3 focus:text-foreground focus:outline-2 focus:outline-ring\"\n >{{ skipLinkText() }}</a\n >\n\n <header [class]=\"headerClasses()\">\n <div class=\"flex min-w-0 flex-1 items-center gap-3 md:flex-none md:flex-wrap\">\n @if (sidebarEnabled()) {\n <button\n type=\"button\"\n [class]=\"iconButtonClasses\"\n [attr.aria-label]=\"sidebarToggleLabel()\"\n [attr.aria-expanded]=\"sidebarOpen()\"\n [attr.aria-controls]=\"sidebarId\"\n (click)=\"openSidebar()\"\n >\n <ng-icon [svg]=\"sidebarIcon\" size=\"20px\" aria-hidden=\"true\" />\n </button>\n }\n <a\n [routerLink]=\"logoLink()\"\n [attr.aria-label]=\"brandName() + ' home'\"\n class=\"flex min-h-11 min-w-0 items-center gap-2.5 rounded-lg pr-2 text-foreground no-underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\"\n >\n @if (logo()) { <img [src]=\"logo()\" alt=\"\" [class]=\"logoClasses()\" /> }\n <span class=\"truncate text-base font-semibold tracking-tight\">{{ brandName() }}</span>\n </a>\n <nav aria-label=\"Primary navigation\" [class]=\"primaryNavigationClasses()\">\n @for (item of primaryNavigation(); track $index) {\n <eg-layout-navigation-item [item]=\"item\" [itemClass]=\"navigationItemClass()\" />\n } @for (group of navigationGroups(); track $index) {\n <button\n type=\"button\"\n [hlmDropdownMenuTrigger]=\"groupMenu\"\n class=\"flex min-h-11 items-center gap-2 rounded-lg px-3 text-sm font-medium cursor-pointer hover:bg-foreground/5 focus-visible:outline-2 focus-visible:outline-ring\"\n >\n {{ group.label || 'More' }} <ng-icon [svg]=\"chevronIcon\" size=\"14px\" aria-hidden=\"true\" />\n </button>\n <ng-template #groupMenu>\n <div hlmDropdownMenu class=\"w-60\">\n @for (item of group.items; track $index) { @if (item.link && !item.disabled) {\n <a\n hlmDropdownMenuItem\n [routerLink]=\"item.link\"\n routerLinkActive=\"tw:bg-foreground/10 tw:font-semibold\"\n [routerLinkActiveOptions]=\"navigationMatchOptions(item)\"\n ariaCurrentWhenActive=\"page\"\n [class]=\"hlm('tw:min-h-11 tw:gap-2 tw:no-underline', item.class)\"\n >\n @if (item.icon) { <ng-icon [svg]=\"item.icon\" size=\"1rem\" aria-hidden=\"true\" /> } {{ item.label }}\n </a>\n } @else {\n <button\n type=\"button\"\n hlmDropdownMenuItem\n [disabled]=\"item.disabled || !item.action\"\n [class]=\"hlm('tw:min-h-11 tw:gap-2', item.class)\"\n (click)=\"item.action?.()\"\n >\n @if (item.icon) { <ng-icon [svg]=\"item.icon\" size=\"1rem\" aria-hidden=\"true\" /> } {{ item.label }}\n </button>\n } }\n </div>\n </ng-template>\n }\n </nav>\n </div>\n\n <div class=\"hidden md:flex flex-wrap items-center gap-3\">\n @if (searchEnabled() && !isMobile()) { <ng-container [ngTemplateOutlet]=\"searchTemplate\" /> } @if\n (utilityNavigation().length) {\n <nav aria-label=\"Utility navigation\" [class]=\"utilityNavigationClasses()\">\n @for (item of utilityNavigation(); track $index) {\n <eg-layout-navigation-item [item]=\"item\" [itemClass]=\"navigationItemClass()\" />\n }\n </nav>\n } @if (userMenuSections().length) {\n <eg-layout-simple-user-menu\n [sections]=\"userMenuSections()\"\n [triggerTemplate]=\"userMenuTrigger()\"\n [triggerLabel]=\"userMenuLabel()\"\n />\n }\n </div>\n\n <button\n #mobileMenuTrigger\n type=\"button\"\n (click)=\"toggleMobileMenu()\"\n [class]=\"hlm(iconButtonClasses, 'tw:md:hidden')\"\n [attr.aria-label]=\"mobileMenuOpen() ? mobileMenuCloseLabel() : mobileMenuLabel()\"\n [attr.aria-expanded]=\"mobileMenuOpen()\"\n [attr.aria-controls]=\"mobileMenuId\"\n >\n <ng-icon [svg]=\"mobileMenuOpen() ? closeIcon : menuIcon\" size=\"20px\" aria-hidden=\"true\" />\n </button>\n </header>\n\n @if (sectionNavigation().length) {\n <nav aria-label=\"Section navigation\" [class]=\"sectionNavigationClasses()\">\n @for (item of sectionNavigation(); track $index) {\n <eg-layout-navigation-item [item]=\"item\" [itemClass]=\"navigationItemClass()\" />\n }\n </nav>\n } @if (flyoutNavigationGroups().length && !isMobile()) {\n <eg-layout-flyout-navbar\n class=\"hidden md:block\"\n [groups]=\"flyoutNavigationGroups()\"\n [label]=\"flyoutNavigationLabel()\"\n [closeLabel]=\"flyoutCloseLabel()\"\n [navbarClass]=\"flyoutNavigationClass()\"\n [panelClass]=\"flyoutPanelClass()\"\n [itemClass]=\"navigationItemClass()\"\n />\n } @if (secondaryNavigationGroups().length) {\n <nav aria-label=\"Secondary navigation\" [class]=\"secondaryNavigationClasses()\" tabindex=\"0\">\n @for (group of secondaryNavigationGroups(); track $index; let first = $first) {\n <div\n [class]=\"hlm('tw:flex tw:shrink-0 tw:items-center tw:gap-1', !first && 'tw:border-l tw:border-border tw:pl-5')\"\n >\n @if (group.label) {\n <span class=\"mr-2 text-xs font-semibold text-foreground/75 whitespace-nowrap\"\n >{{ group.label }}</span\n >\n } @for (item of group.items; track $index) {\n <eg-layout-navigation-item\n [item]=\"item\"\n [itemClass]=\"hlm('tw:rounded-full tw:whitespace-nowrap', navigationItemClass())\"\n />\n }\n </div>\n }\n </nav>\n } @if (isMobile() && mobileMenuOpen()) {\n <nav\n [id]=\"mobileMenuId\"\n aria-label=\"Mobile navigation\"\n (keydown.escape)=\"closeMobileMenu(true); $event.stopPropagation()\"\n class=\"md:hidden max-h-[70dvh] overflow-y-auto overscroll-contain space-y-4 border-b border-border bg-background p-4\"\n >\n @if (searchEnabled()) { <ng-container [ngTemplateOutlet]=\"searchTemplate\" /> }\n <eg-layout-simple-mobile-menu-group [items]=\"primaryNavigation()\" (itemSelected)=\"closeMobileMenu(true)\" />\n @for (group of navigationGroups(); track $index) {\n <eg-layout-simple-mobile-menu-group\n [items]=\"group.items\"\n [label]=\"group.label\"\n (itemSelected)=\"closeMobileMenu(true)\"\n />\n }\n <eg-layout-simple-mobile-menu-group [items]=\"sectionNavigation()\" (itemSelected)=\"closeMobileMenu(true)\" />\n @for (group of flyoutNavigationGroups(); track $index) {\n <details class=\"group/flyout rounded-xl border border-border bg-foreground/3\">\n <summary\n class=\"flex min-h-11 list-none items-center justify-between gap-2 rounded-xl px-3 py-3 text-sm font-semibold cursor-pointer focus-visible:outline-2 focus-visible:outline-ring [&::-webkit-details-marker]:hidden\"\n >\n <span class=\"flex items-center gap-2\">\n @if (group.icon) { <ng-icon [svg]=\"group.icon\" size=\"1rem\" aria-hidden=\"true\" /> } {{ group.label }}\n </span>\n <ng-icon\n [svg]=\"chevronIcon\"\n size=\"14px\"\n aria-hidden=\"true\"\n class=\"shrink-0 group-open/flyout:rotate-180\"\n />\n </summary>\n <div class=\"space-y-2 border-t border-border p-2\">\n @if (group.description) {\n <p class=\"px-3 py-2 text-sm text-foreground/75\">{{ group.description }}</p>\n } @for (item of group.items; track $index) {\n <eg-layout-navigation-item\n [item]=\"item\"\n appearance=\"card\"\n [itemClass]=\"navigationItemClass()\"\n (selected)=\"closeMobileMenu(true)\"\n />\n }\n </div>\n </details>\n } @for (group of secondaryNavigationGroups(); track $index) {\n <eg-layout-simple-mobile-menu-group\n [items]=\"group.items\"\n [label]=\"group.label\"\n (itemSelected)=\"closeMobileMenu(true)\"\n />\n }\n <eg-layout-simple-mobile-menu-group [items]=\"utilityNavigation()\" (itemSelected)=\"closeMobileMenu(true)\" />\n @if (userMenuSections().length) {\n <div class=\"space-y-4 border-t border-border pt-4\">\n @for (section of userMenuSections(); track $index) {\n <eg-layout-simple-mobile-menu-group\n [items]=\"section.items\"\n [label]=\"section.label\"\n (itemSelected)=\"closeMobileMenu(true)\"\n />\n }\n </div>\n }\n </nav>\n }\n\n <main #mainContent [id]=\"mainId\" tabindex=\"-1\" [attr.aria-busy]=\"loading()\" [class]=\"contentClasses()\">\n @if (loading()) {\n <div\n role=\"status\"\n class=\"flex min-h-48 flex-1 items-center justify-center gap-3 text-sm text-foreground/75\"\n >\n <span\n aria-hidden=\"true\"\n class=\"size-5 rounded-full border-2 border-border border-t-foreground motion-safe:animate-spin\"\n ></span>\n {{ loadingText() }}\n </div>\n }\n <div\n [hidden]=\"loading()\"\n [style.display]=\"loading() ? 'none' : null\"\n class=\"w-full min-w-0 eg-layout-simple-main-content\"\n >\n <ng-content />\n </div>\n </main>\n\n @if (footerEnabled()) {\n <footer [class]=\"footerClasses()\">\n @if (footerText()) {\n <p>{{ footerText() }}</p>\n } @if (footerNavigation().length) {\n <nav aria-label=\"Footer navigation\" class=\"flex flex-wrap items-center gap-1\">\n @for (item of footerNavigation(); track $index) { <eg-layout-navigation-item [item]=\"item\" /> }\n </nav>\n }\n </footer>\n }\n</div>\n\n<ng-template #searchTemplate>\n <eg-layout-search\n [loader]=\"searchLoader()\"\n [resultLabel]=\"searchResultLabel()\"\n [placeholder]=\"searchPlaceholder()\"\n [emptyText]=\"searchEmptyText()\"\n [errorText]=\"searchErrorText()\"\n [resultTemplate]=\"searchResultTemplate()\"\n (resultSelected)=\"selectSearchResult($event)\"\n />\n</ng-template>\n", dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "component", type: EgLayoutSimpleUserMenu, selector: "eg-layout-simple-user-menu", inputs: ["sections", "triggerTemplate", "triggerLabel"] }, { kind: "component", type: EgLayoutSimpleSidebar, selector: "eg-layout-simple-sidebar", inputs: ["panelId", "title", "content", "contentContext"] }, { kind: "component", type: EgLayoutSimpleMobileMenuGroup, selector: "eg-layout-simple-mobile-menu-group", inputs: ["label", "items"], outputs: ["itemSelected"] }, { kind: "component", type: EgLayoutSearch, selector: "eg-layout-search", inputs: ["placeholder", "emptyText", "errorText", "loadingText", "retryText", "loader", "resultLabel", "resultTemplate"], outputs: ["resultSelected"] }, { kind: "component", type: EgLayoutNavigationItem, selector: "eg-layout-navigation-item", inputs: ["item", "itemClass", "appearance"], outputs: ["selected"] }, { kind: "component", type: EgLayoutFlyoutNavbar, selector: "eg-layout-flyout-navbar", inputs: ["groups", "label", "closeLabel", "navbarClass", "panelClass", "itemClass"] }, { kind: "directive", type: HlmDropdownMenu, selector: "[hlmDropdownMenu],hlm-dropdown-menu", inputs: ["sideOffset"] }, { kind: "directive", type: HlmDropdownMenuItem, selector: "[hlmDropdownMenuItem],hlm-dropdown-menu-item", inputs: ["disabled", "variant", "inset"] }, { kind: "directive", type: HlmDropdownMenuTrigger, selector: "[hlmDropdownMenuTrigger]", inputs: ["align", "side"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
361
595
  }
362
596
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgLayoutSimple, decorators: [{
363
597
  type: Component,
364
- args: [{ selector: 'eg-layout-simple', standalone: true, imports: [
598
+ args: [{ selector: 'eg-layout-simple', imports: [
365
599
  RouterLink,
600
+ RouterLinkActive,
601
+ NgTemplateOutlet,
602
+ NgIcon,
366
603
  EgLayoutSimpleUserMenu,
367
604
  EgLayoutSimpleSidebar,
368
605
  EgLayoutSimpleMobileMenuGroup,
369
- EgGenericAutocomplete,
370
- HlmButton,
371
- NgIcon,
372
- ], providers: [
373
- provideIcons({
374
- lucideMenu,
375
- }),
376
- ], template: "<ng-template #menuIconTemplate>\n <ng-icon [svg]=\"menuIcon\" size=\"20px\" class=\"[&_svg]:w-[inherit]! [&_svg]:h-[inherit]!\" />\n</ng-template>\n\n<eg-layout-simple-sidebar\n [title]=\"sidebarTitle()\"\n [content]=\"sidebarContent()\"\n [contentContext]=\"{ close: closeSidebar.bind(this) }\"\n/>\n\n<div class=\"flex flex-col min-h-screen\">\n <!-- Header -->\n <header [class]=\"_headerClass()\">\n <div class=\"flex items-center space-x-4\">\n @if (sidebarEnabled()) {\n <button\n hlmButton\n variant=\"secondary\"\n appearance=\"outline\"\n size=\"icon\"\n [icon]=\"menuIconTemplate\"\n iconPosition=\"left\"\n class=\"bg-transparent! border-gray-400! hover:border-gray-500!\"\n [attr.aria-label]=\"sidebarToggleLabel()\"\n [attr.aria-expanded]=\"sidebarOpen()\"\n aria-controls=\"eg-layout-simple-sidebar\"\n (click)=\"openSidebar()\"\n ></button>\n }\n\n <div class=\"logo\">\n <a [routerLink]=\"logoLink()\">\n <img [src]=\"logo()\" alt=\"Logo\" [class]=\"_logoClass()\" />\n </a>\n </div>\n <!-- Left menus (desktop) -->\n <nav [class]=\"_computedLeftClass()\">\n @for (item of leftMenus(); track item.label) { @if (item.link) {\n <a [routerLink]=\"item.link\" [class]=\"hlm(_computedLeftLinkClass(), item.class || '')\"> {{ item.label }} </a>\n } @else {\n <button type=\"button\" (click)=\"item.action?.()\" [class]=\"hlm(_computedLeftLinkClass(), item.class || '')\">\n {{ item.label }}\n </button>\n } } @for (group of leftMenuGroups(); track $index) {\n <div class=\"relative group\">\n <button\n type=\"button\"\n class=\"text-left text-secondary hover:text-primary cursor-pointer no-underline py-1 flex items-center gap-1\"\n >\n {{ group.label ?? 'Menu' }}\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-3 w-3\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\" />\n </svg>\n </button>\n <div\n class=\"absolute left-0 top-full mt-1 hidden group-hover:flex flex-col min-w-[12rem] bg-popover text-popover-foreground border border-gray-200 rounded-md shadow-md py-1 z-50\"\n >\n @if (group.label) {\n <div class=\"px-3 py-1 text-xs font-semibold text-muted-foreground\">{{ group.label }}</div>\n } @for (item of group.items; track item.label) { @if (item.link) {\n <a\n [routerLink]=\"item.link\"\n [class]=\"hlm('tw:px-3 tw:py-1.5 tw:text-sm tw:no-underline tw:hover:bg-secondary tw:cursor-pointer', item.class || '')\"\n >\n {{ item.label }}\n </a>\n } @else {\n <button\n type=\"button\"\n (click)=\"item.action?.()\"\n [class]=\"hlm('tw:px-3 tw:py-1.5 tw:text-sm tw:text-left tw:hover:bg-secondary tw:cursor-pointer tw:bg-transparent tw:border-0 tw:w-full', item.class || '')\"\n >\n {{ item.label }}\n </button>\n } }\n </div>\n </div>\n }\n </nav>\n </div>\n\n <!-- Right menus (desktop) -->\n <nav [class]=\"_computedRightClass()\">\n @if (searchEnabled()) {\n <eg-generic-autocomplete\n [loaderFn]=\"searchLoaderFn()\"\n [transformValueToSearch]=\"searchTransformValueToSearch()\"\n [placeholderText]=\"searchPlaceholderText()\"\n [emptyText]=\"searchEmptyText()\"\n (optionChange)=\"onSearchOptionChange($event)\"\n ></eg-generic-autocomplete>\n } @for (item of rightMenus(); track item.label) { @if (item.link) {\n <a [routerLink]=\"item.link\" [class]=\"hlm(_computedRightLinkClass(), item.class || '')\"> {{ item.label }} </a>\n } @else {\n <button type=\"button\" (click)=\"item.action?.()\" [class]=\"hlm(_computedRightLinkClass(), item.class || '')\">\n {{ item.label }}\n </button>\n } } @let uMenus = userMenus(); @if (uMenus && uMenus.length > 0) {\n <eg-layout-simple-user-menu [menus]=\"uMenus\" [menuTrigger]=\"userMenuTrigger()\" />\n }\n </nav>\n\n <!-- Mobile menu button -->\n <button\n (click)=\"toggleMobileMenu()\"\n class=\"md:hidden p-2 text-gray-700 hover:text-primary focus:outline-none cursor-pointer\"\n [attr.aria-label]=\"mobileMenuLabel()\"\n [attr.aria-expanded]=\"mobileMenuOpen()\"\n aria-controls=\"eg-layout-simple-mobile-menu\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-6 w-6\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 6h16M4 12h16M4 18h16\" />\n </svg>\n </button>\n </header>\n\n @let _topMenus = leftMenus().concat(rightMenus()); @let _userMenus = userMenus(); @let _leftMenuGroups =\n leftMenuGroups(); @let _topMenuItems = topMenus(); @let _topSecondaryGroups = topSecondaryMenus();\n\n <!-- Top menus (desktop) -->\n @if (_topMenuItems.length > 0) {\n <nav [class]=\"_computedTopClass()\">\n @for (item of _topMenuItems; track item.label) { @if (item.link) {\n <a [routerLink]=\"item.link\" [class]=\"hlm(_computedTopLinkClass(), item.class || '')\"> {{ item.label }} </a>\n } @else {\n <span [class]=\"hlm(_computedTopLinkClass(), item.class || '')\">{{ item.label }}</span>\n } }\n </nav>\n }\n\n <!-- Top secondary menus (desktop) — second row showing items grouped by parent label -->\n @if (_topSecondaryGroups.length > 0) {\n <nav [class]=\"_computedTopSecondaryClass()\">\n @for (group of _topSecondaryGroups; track group.label) {\n <span class=\"flex flex-wrap items-center gap-x-3\">\n @if (group.label) {\n <span class=\"text-xs font-semibold uppercase text-muted-foreground\">{{ group.label }}</span>\n } @for (item of group.items; track item.label) { @if (item.link) {\n <a\n [routerLink]=\"item.link\"\n [class]=\"hlm(_computedTopLinkClass(), 'tw:text-xs tw:hover:underline', item.class || '')\"\n >\n {{ item.label }}\n </a>\n } @else {\n <button\n type=\"button\"\n (click)=\"item.action?.()\"\n [class]=\"hlm(_computedTopLinkClass(), 'tw:text-xs tw:hover:underline', item.class || '')\"\n >\n {{ item.label }}\n </button>\n } }\n </span>\n }\n </nav>\n }\n\n <!-- Mobile menu -->\n @if (isMobile() && mobileMenuOpen()) {\n <div id=\"eg-layout-simple-mobile-menu\" class=\"w-full border-none\" (keydown.escape)=\"closeMobileMenu()\">\n @if (_topMenuItems.length > 0) {\n <eg-layout-simple-mobile-menu-group [items]=\"_topMenuItems\" (itemClick)=\"closeMobileMenu()\" />\n } @for (group of _topSecondaryGroups; track group.label) { @if (group.items && group.items.length > 0) {\n <eg-layout-simple-mobile-menu-group\n [items]=\"group.items\"\n [label]=\"group.label\"\n (itemClick)=\"closeMobileMenu()\"\n ></eg-layout-simple-mobile-menu-group>\n } }\n\n <eg-layout-simple-mobile-menu-group [items]=\"_topMenus\" (itemClick)=\"closeMobileMenu()\" />\n\n @for (group of _leftMenuGroups; track $index) { @if (group.items && group.items.length > 0) {\n <eg-layout-simple-mobile-menu-group\n [items]=\"group.items\"\n [label]=\"group.label\"\n (itemClick)=\"closeMobileMenu()\"\n ></eg-layout-simple-mobile-menu-group>\n } } @if ((_topMenus.length > 0 || _leftMenuGroups.length > 0) && _userMenus.length > 0) {\n <hr class=\"border-gray-300 my-2\" />\n } @for (section of userMenus(); track section.label) {\n <eg-layout-simple-mobile-menu-group\n [items]=\"section.items\"\n [label]=\"section.label\"\n (itemClick)=\"closeMobileMenu()\"\n ></eg-layout-simple-mobile-menu-group>\n\n @if (section.separator) {\n <hr class=\"border-gray-300 my-2\" />\n } }\n </div>\n }\n\n <!-- Main content -->\n <main [class]=\"_contentClass()\">\n @if (!loading()) {\n <div class=\"flex items-center justify-center h-full eg-layout-simple-main-content\">\n <ng-content />\n </div>\n }\n <div [class]=\"_contentBottomClass()\"></div>\n </main>\n\n @if (footerEnabled()) {\n <footer [class]=\"_footerClass()\">\n <nav [class]=\"_computedFooterMenuClass()\">\n @for (item of footerMenus(); track item.label) { @if (item.link) {\n <a [routerLink]=\"item.link\" [class]=\"hlm(_computedFooterLinkClass(), item.class || '')\"> {{ item.label }} </a>\n } @else {\n <button type=\"button\" (click)=\"item.action?.()\" [class]=\"hlm(_computedFooterLinkClass(), item.class || '')\">\n {{ item.label }}\n </button>\n } }\n </nav>\n\n <div class=\"mt-2 text-center\">{{ footerContent() }}</div>\n </footer>\n }\n</div>\n" }]
377
- }], ctorParameters: () => [{ type: i1$2.BreakpointObserver }], propDecorators: { sidebarEnabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarEnabled", required: false }] }], sidebarTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarTitle", required: false }] }], sidebarContent: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarContent", required: false }] }], sidebarToggleLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarToggleLabel", required: false }] }], mobileMenuLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "mobileMenuLabel", required: false }] }], userMenuTrigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "userMenuTrigger", required: false }] }], leftMenus: [{ type: i0.Input, args: [{ isSignal: true, alias: "leftMenus", required: false }] }], leftMenuGroups: [{ type: i0.Input, args: [{ isSignal: true, alias: "leftMenuGroups", required: false }] }], rightMenus: [{ type: i0.Input, args: [{ isSignal: true, alias: "rightMenus", required: false }] }], topMenus: [{ type: i0.Input, args: [{ isSignal: true, alias: "topMenus", required: false }] }], topSecondaryMenus: [{ type: i0.Input, args: [{ isSignal: true, alias: "topSecondaryMenus", required: false }] }], userMenus: [{ type: i0.Input, args: [{ isSignal: true, alias: "userMenus", required: false }] }], logo: [{ type: i0.Input, args: [{ isSignal: true, alias: "logo", required: false }] }], logoLink: [{ type: i0.Input, args: [{ isSignal: true, alias: "logoLink", required: false }] }], logoClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "logoClass", required: false }] }], headerClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerClass", required: false }] }], contentClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentClass", required: false }] }], contentBottomClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentBottomClass", required: false }] }], leftMenuClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "leftClass", required: false }] }], rightMenuClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "rightClass", required: false }] }], topMenuClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "topClass", required: false }] }], leftLinkClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "leftLinkClass", required: false }] }], rightLinkClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "rightLinkClass", required: false }] }], topLinkClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "topLinkClass", required: false }] }], searchEnabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchEnabled", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], searchPlaceholderText: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchPlaceholderText", required: false }] }], searchEmptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchEmptyText", required: false }] }], searchOptionTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchOptionTemplate", required: false }] }], searchLoaderFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchLoaderFn", required: false }] }], searchTransformValueToSearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchTransformValueToSearch", required: false }] }], searchOptionChange: [{ type: i0.Output, args: ["searchOptionChange"] }], footerEnabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerEnabled", required: false }] }], footerMenus: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerMenus", required: false }] }], footerMenuClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerMenuClass", required: false }] }], footerLinkClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerLinkClass", required: false }] }], footerContent: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerContent", required: false }] }], footerClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerClass", required: false }] }], viewchildSheetRef: [{ type: i0.ViewChild, args: [i0.forwardRef(() => EgLayoutSimpleSidebar), { isSignal: true }] }] } });
606
+ EgLayoutSearch,
607
+ EgLayoutNavigationItem,
608
+ EgLayoutFlyoutNavbar,
609
+ HlmDropdownMenu,
610
+ HlmDropdownMenuItem,
611
+ HlmDropdownMenuTrigger,
612
+ ], changeDetection: ChangeDetectionStrategy.OnPush, host: { class: "block min-w-0" }, template: "@if (sidebarEnabled()) {\n<eg-layout-simple-sidebar\n [panelId]=\"sidebarId\"\n [title]=\"sidebarTitle()\"\n [content]=\"sidebarContent()\"\n [contentContext]=\"sidebarContext\"\n/>\n}\n\n<div\n [class]=\"hlm('tw:relative tw:isolate tw:flex tw:min-w-0 tw:flex-col tw:bg-background tw:text-foreground', fullHeight() && 'tw:min-h-dvh')\"\n>\n <a\n [href]=\"'#' + mainId\"\n (click)=\"focusContent($event)\"\n class=\"sr-only focus:not-sr-only focus:absolute focus:left-4 focus:top-2 focus:z-50 focus:rounded-lg focus:bg-background focus:px-4 focus:py-3 focus:text-foreground focus:outline-2 focus:outline-ring\"\n >{{ skipLinkText() }}</a\n >\n\n <header [class]=\"headerClasses()\">\n <div class=\"flex min-w-0 flex-1 items-center gap-3 md:flex-none md:flex-wrap\">\n @if (sidebarEnabled()) {\n <button\n type=\"button\"\n [class]=\"iconButtonClasses\"\n [attr.aria-label]=\"sidebarToggleLabel()\"\n [attr.aria-expanded]=\"sidebarOpen()\"\n [attr.aria-controls]=\"sidebarId\"\n (click)=\"openSidebar()\"\n >\n <ng-icon [svg]=\"sidebarIcon\" size=\"20px\" aria-hidden=\"true\" />\n </button>\n }\n <a\n [routerLink]=\"logoLink()\"\n [attr.aria-label]=\"brandName() + ' home'\"\n class=\"flex min-h-11 min-w-0 items-center gap-2.5 rounded-lg pr-2 text-foreground no-underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\"\n >\n @if (logo()) { <img [src]=\"logo()\" alt=\"\" [class]=\"logoClasses()\" /> }\n <span class=\"truncate text-base font-semibold tracking-tight\">{{ brandName() }}</span>\n </a>\n <nav aria-label=\"Primary navigation\" [class]=\"primaryNavigationClasses()\">\n @for (item of primaryNavigation(); track $index) {\n <eg-layout-navigation-item [item]=\"item\" [itemClass]=\"navigationItemClass()\" />\n } @for (group of navigationGroups(); track $index) {\n <button\n type=\"button\"\n [hlmDropdownMenuTrigger]=\"groupMenu\"\n class=\"flex min-h-11 items-center gap-2 rounded-lg px-3 text-sm font-medium cursor-pointer hover:bg-foreground/5 focus-visible:outline-2 focus-visible:outline-ring\"\n >\n {{ group.label || 'More' }} <ng-icon [svg]=\"chevronIcon\" size=\"14px\" aria-hidden=\"true\" />\n </button>\n <ng-template #groupMenu>\n <div hlmDropdownMenu class=\"w-60\">\n @for (item of group.items; track $index) { @if (item.link && !item.disabled) {\n <a\n hlmDropdownMenuItem\n [routerLink]=\"item.link\"\n routerLinkActive=\"tw:bg-foreground/10 tw:font-semibold\"\n [routerLinkActiveOptions]=\"navigationMatchOptions(item)\"\n ariaCurrentWhenActive=\"page\"\n [class]=\"hlm('tw:min-h-11 tw:gap-2 tw:no-underline', item.class)\"\n >\n @if (item.icon) { <ng-icon [svg]=\"item.icon\" size=\"1rem\" aria-hidden=\"true\" /> } {{ item.label }}\n </a>\n } @else {\n <button\n type=\"button\"\n hlmDropdownMenuItem\n [disabled]=\"item.disabled || !item.action\"\n [class]=\"hlm('tw:min-h-11 tw:gap-2', item.class)\"\n (click)=\"item.action?.()\"\n >\n @if (item.icon) { <ng-icon [svg]=\"item.icon\" size=\"1rem\" aria-hidden=\"true\" /> } {{ item.label }}\n </button>\n } }\n </div>\n </ng-template>\n }\n </nav>\n </div>\n\n <div class=\"hidden md:flex flex-wrap items-center gap-3\">\n @if (searchEnabled() && !isMobile()) { <ng-container [ngTemplateOutlet]=\"searchTemplate\" /> } @if\n (utilityNavigation().length) {\n <nav aria-label=\"Utility navigation\" [class]=\"utilityNavigationClasses()\">\n @for (item of utilityNavigation(); track $index) {\n <eg-layout-navigation-item [item]=\"item\" [itemClass]=\"navigationItemClass()\" />\n }\n </nav>\n } @if (userMenuSections().length) {\n <eg-layout-simple-user-menu\n [sections]=\"userMenuSections()\"\n [triggerTemplate]=\"userMenuTrigger()\"\n [triggerLabel]=\"userMenuLabel()\"\n />\n }\n </div>\n\n <button\n #mobileMenuTrigger\n type=\"button\"\n (click)=\"toggleMobileMenu()\"\n [class]=\"hlm(iconButtonClasses, 'tw:md:hidden')\"\n [attr.aria-label]=\"mobileMenuOpen() ? mobileMenuCloseLabel() : mobileMenuLabel()\"\n [attr.aria-expanded]=\"mobileMenuOpen()\"\n [attr.aria-controls]=\"mobileMenuId\"\n >\n <ng-icon [svg]=\"mobileMenuOpen() ? closeIcon : menuIcon\" size=\"20px\" aria-hidden=\"true\" />\n </button>\n </header>\n\n @if (sectionNavigation().length) {\n <nav aria-label=\"Section navigation\" [class]=\"sectionNavigationClasses()\">\n @for (item of sectionNavigation(); track $index) {\n <eg-layout-navigation-item [item]=\"item\" [itemClass]=\"navigationItemClass()\" />\n }\n </nav>\n } @if (flyoutNavigationGroups().length && !isMobile()) {\n <eg-layout-flyout-navbar\n class=\"hidden md:block\"\n [groups]=\"flyoutNavigationGroups()\"\n [label]=\"flyoutNavigationLabel()\"\n [closeLabel]=\"flyoutCloseLabel()\"\n [navbarClass]=\"flyoutNavigationClass()\"\n [panelClass]=\"flyoutPanelClass()\"\n [itemClass]=\"navigationItemClass()\"\n />\n } @if (secondaryNavigationGroups().length) {\n <nav aria-label=\"Secondary navigation\" [class]=\"secondaryNavigationClasses()\" tabindex=\"0\">\n @for (group of secondaryNavigationGroups(); track $index; let first = $first) {\n <div\n [class]=\"hlm('tw:flex tw:shrink-0 tw:items-center tw:gap-1', !first && 'tw:border-l tw:border-border tw:pl-5')\"\n >\n @if (group.label) {\n <span class=\"mr-2 text-xs font-semibold text-foreground/75 whitespace-nowrap\"\n >{{ group.label }}</span\n >\n } @for (item of group.items; track $index) {\n <eg-layout-navigation-item\n [item]=\"item\"\n [itemClass]=\"hlm('tw:rounded-full tw:whitespace-nowrap', navigationItemClass())\"\n />\n }\n </div>\n }\n </nav>\n } @if (isMobile() && mobileMenuOpen()) {\n <nav\n [id]=\"mobileMenuId\"\n aria-label=\"Mobile navigation\"\n (keydown.escape)=\"closeMobileMenu(true); $event.stopPropagation()\"\n class=\"md:hidden max-h-[70dvh] overflow-y-auto overscroll-contain space-y-4 border-b border-border bg-background p-4\"\n >\n @if (searchEnabled()) { <ng-container [ngTemplateOutlet]=\"searchTemplate\" /> }\n <eg-layout-simple-mobile-menu-group [items]=\"primaryNavigation()\" (itemSelected)=\"closeMobileMenu(true)\" />\n @for (group of navigationGroups(); track $index) {\n <eg-layout-simple-mobile-menu-group\n [items]=\"group.items\"\n [label]=\"group.label\"\n (itemSelected)=\"closeMobileMenu(true)\"\n />\n }\n <eg-layout-simple-mobile-menu-group [items]=\"sectionNavigation()\" (itemSelected)=\"closeMobileMenu(true)\" />\n @for (group of flyoutNavigationGroups(); track $index) {\n <details class=\"group/flyout rounded-xl border border-border bg-foreground/3\">\n <summary\n class=\"flex min-h-11 list-none items-center justify-between gap-2 rounded-xl px-3 py-3 text-sm font-semibold cursor-pointer focus-visible:outline-2 focus-visible:outline-ring [&::-webkit-details-marker]:hidden\"\n >\n <span class=\"flex items-center gap-2\">\n @if (group.icon) { <ng-icon [svg]=\"group.icon\" size=\"1rem\" aria-hidden=\"true\" /> } {{ group.label }}\n </span>\n <ng-icon\n [svg]=\"chevronIcon\"\n size=\"14px\"\n aria-hidden=\"true\"\n class=\"shrink-0 group-open/flyout:rotate-180\"\n />\n </summary>\n <div class=\"space-y-2 border-t border-border p-2\">\n @if (group.description) {\n <p class=\"px-3 py-2 text-sm text-foreground/75\">{{ group.description }}</p>\n } @for (item of group.items; track $index) {\n <eg-layout-navigation-item\n [item]=\"item\"\n appearance=\"card\"\n [itemClass]=\"navigationItemClass()\"\n (selected)=\"closeMobileMenu(true)\"\n />\n }\n </div>\n </details>\n } @for (group of secondaryNavigationGroups(); track $index) {\n <eg-layout-simple-mobile-menu-group\n [items]=\"group.items\"\n [label]=\"group.label\"\n (itemSelected)=\"closeMobileMenu(true)\"\n />\n }\n <eg-layout-simple-mobile-menu-group [items]=\"utilityNavigation()\" (itemSelected)=\"closeMobileMenu(true)\" />\n @if (userMenuSections().length) {\n <div class=\"space-y-4 border-t border-border pt-4\">\n @for (section of userMenuSections(); track $index) {\n <eg-layout-simple-mobile-menu-group\n [items]=\"section.items\"\n [label]=\"section.label\"\n (itemSelected)=\"closeMobileMenu(true)\"\n />\n }\n </div>\n }\n </nav>\n }\n\n <main #mainContent [id]=\"mainId\" tabindex=\"-1\" [attr.aria-busy]=\"loading()\" [class]=\"contentClasses()\">\n @if (loading()) {\n <div\n role=\"status\"\n class=\"flex min-h-48 flex-1 items-center justify-center gap-3 text-sm text-foreground/75\"\n >\n <span\n aria-hidden=\"true\"\n class=\"size-5 rounded-full border-2 border-border border-t-foreground motion-safe:animate-spin\"\n ></span>\n {{ loadingText() }}\n </div>\n }\n <div\n [hidden]=\"loading()\"\n [style.display]=\"loading() ? 'none' : null\"\n class=\"w-full min-w-0 eg-layout-simple-main-content\"\n >\n <ng-content />\n </div>\n </main>\n\n @if (footerEnabled()) {\n <footer [class]=\"footerClasses()\">\n @if (footerText()) {\n <p>{{ footerText() }}</p>\n } @if (footerNavigation().length) {\n <nav aria-label=\"Footer navigation\" class=\"flex flex-wrap items-center gap-1\">\n @for (item of footerNavigation(); track $index) { <eg-layout-navigation-item [item]=\"item\" /> }\n </nav>\n }\n </footer>\n }\n</div>\n\n<ng-template #searchTemplate>\n <eg-layout-search\n [loader]=\"searchLoader()\"\n [resultLabel]=\"searchResultLabel()\"\n [placeholder]=\"searchPlaceholder()\"\n [emptyText]=\"searchEmptyText()\"\n [errorText]=\"searchErrorText()\"\n [resultTemplate]=\"searchResultTemplate()\"\n (resultSelected)=\"selectSearchResult($event)\"\n />\n</ng-template>\n" }]
613
+ }], ctorParameters: () => [], propDecorators: { mobileMenuTrigger: [{ type: i0.ViewChild, args: ['mobileMenuTrigger', { isSignal: true }] }], mainContent: [{ type: i0.ViewChild, args: ['mainContent', { isSignal: true }] }], sidebar: [{ type: i0.ViewChild, args: [i0.forwardRef(() => EgLayoutSimpleSidebar), { isSignal: true }] }], sidebarEnabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarEnabled", required: false }] }], sidebarTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarTitle", required: false }] }], sidebarContent: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarContent", required: false }] }], sidebarToggleLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarToggleLabel", required: false }] }], mobileMenuLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "mobileMenuLabel", required: false }] }], mobileMenuCloseLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "mobileMenuCloseLabel", required: false }] }], userMenuTrigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "userMenuTrigger", required: false }] }], userMenuLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "userMenuLabel", required: false }] }], primaryNavigation: [{ type: i0.Input, args: [{ isSignal: true, alias: "primaryNavigation", required: false }] }], navigationGroups: [{ type: i0.Input, args: [{ isSignal: true, alias: "navigationGroups", required: false }] }], utilityNavigation: [{ type: i0.Input, args: [{ isSignal: true, alias: "utilityNavigation", required: false }] }], sectionNavigation: [{ type: i0.Input, args: [{ isSignal: true, alias: "sectionNavigation", required: false }] }], secondaryNavigationGroups: [{ type: i0.Input, args: [{ isSignal: true, alias: "secondaryNavigationGroups", required: false }] }], flyoutNavigationGroups: [{ type: i0.Input, args: [{ isSignal: true, alias: "flyoutNavigationGroups", required: false }] }], flyoutNavigationLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "flyoutNavigationLabel", required: false }] }], flyoutCloseLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "flyoutCloseLabel", required: false }] }], userMenuSections: [{ type: i0.Input, args: [{ isSignal: true, alias: "userMenuSections", required: false }] }], logo: [{ type: i0.Input, args: [{ isSignal: true, alias: "logo", required: false }] }], brandName: [{ type: i0.Input, args: [{ isSignal: true, alias: "brandName", required: false }] }], logoLink: [{ type: i0.Input, args: [{ isSignal: true, alias: "logoLink", required: false }] }], logoClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "logoClass", required: false }] }], fullHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullHeight", required: false }] }], headerClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerClass", required: false }] }], contentClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentClass", required: false }] }], primaryNavigationClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "primaryNavigationClass", required: false }] }], utilityNavigationClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "utilityNavigationClass", required: false }] }], sectionNavigationClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "sectionNavigationClass", required: false }] }], secondaryNavigationClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "secondaryNavigationClass", required: false }] }], flyoutNavigationClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "flyoutNavigationClass", required: false }] }], flyoutPanelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "flyoutPanelClass", required: false }] }], navigationItemClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "navigationItemClass", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], loadingText: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingText", required: false }] }], skipLinkText: [{ type: i0.Input, args: [{ isSignal: true, alias: "skipLinkText", required: false }] }], searchEnabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchEnabled", required: false }] }], searchPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchPlaceholder", required: false }] }], searchEmptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchEmptyText", required: false }] }], searchErrorText: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchErrorText", required: false }] }], searchResultTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchResultTemplate", required: false }] }], searchLoader: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchLoader", required: false }] }], searchResultLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchResultLabel", required: false }] }], searchResultSelected: [{ type: i0.Output, args: ["searchResultSelected"] }], footerEnabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerEnabled", required: false }] }], footerNavigation: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerNavigation", required: false }] }], footerText: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerText", required: false }] }], footerClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerClass", required: false }] }] } });
378
614
 
379
615
  /*
380
616
  * Public API Surface of layout-simple
@@ -384,5 +620,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
384
620
  * Generated bundle index. Do not edit.
385
621
  */
386
622
 
387
- export { EG_LAYOUT_SIMPLE_MOBILE_BREAKPOINT, EgGenericAutocomplete, EgLayoutSimple, EgLayoutSimpleUserMenu };
623
+ export { EG_LAYOUT_SIMPLE_MOBILE_BREAKPOINT, EgLayoutSearch, EgLayoutSimple, EgLayoutSimpleUserMenu };
388
624