aril 1.2.18 → 2.0.1-dev.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 (122) hide show
  1. package/boot/bridge/src/mfe-bridge.d.ts +42 -2
  2. package/boot/config/apps/index.d.ts +7 -1
  3. package/boot/config/apps/src/custom-reuse-outlet.component.d.ts +37 -0
  4. package/boot/config/apps/src/custom-route-reuse-strategy.class.d.ts +156 -0
  5. package/boot/config/apps/src/nav-link-context-menu.service.d.ts +33 -0
  6. package/boot/config/apps/src/nav-link.directive.d.ts +29 -0
  7. package/boot/config/apps/src/nav.service.d.ts +198 -0
  8. package/boot/config/apps/src/route-close.service.d.ts +9 -0
  9. package/boot/config/apps/src/safe-navigate.d.ts +17 -0
  10. package/boot/config/apps/src/tab-aware-url-serializer.d.ts +22 -0
  11. package/boot/config/plugins/src/getNgZone.d.ts +9 -1
  12. package/boot/mfe/src/app.component.d.ts +15 -4
  13. package/boot/mfe/src/isolated-location-strategy.d.ts +57 -0
  14. package/esm2022/boot/bridge/src/mfe-bridge.mjs +36 -5
  15. package/esm2022/boot/config/api/src/api.service.mjs +12 -3
  16. package/esm2022/boot/config/apps/index.mjs +8 -2
  17. package/esm2022/boot/config/apps/src/apps.service.mjs +14 -6
  18. package/esm2022/boot/config/apps/src/custom-reuse-outlet.component.mjs +207 -0
  19. package/esm2022/boot/config/apps/src/custom-route-reuse-strategy.class.mjs +540 -0
  20. package/esm2022/boot/config/apps/src/nav-link-context-menu.service.mjs +105 -0
  21. package/esm2022/boot/config/apps/src/nav-link.directive.mjs +45 -0
  22. package/esm2022/boot/config/apps/src/nav.service.mjs +675 -0
  23. package/esm2022/boot/config/apps/src/route-close.service.mjs +19 -0
  24. package/esm2022/boot/config/apps/src/safe-navigate.mjs +50 -0
  25. package/esm2022/boot/config/apps/src/tab-aware-url-serializer.mjs +50 -0
  26. package/esm2022/boot/config/plugins/src/getNgZone.mjs +13 -5
  27. package/esm2022/boot/host/src/app.component.mjs +1 -2
  28. package/esm2022/boot/host/src/bootstrap.mjs +22 -7
  29. package/esm2022/boot/mfe/src/app.component.mjs +143 -39
  30. package/esm2022/boot/mfe/src/bootstrap.mjs +197 -20
  31. package/esm2022/boot/mfe/src/isolated-location-strategy.mjs +142 -0
  32. package/esm2022/keycloak/src/auth.interceptor.mjs +17 -2
  33. package/esm2022/provider/src/prodiveHost.mjs +3 -5
  34. package/esm2022/provider/src/prodiveHostRouter.mjs +88 -9
  35. package/esm2022/theme/layout/app/expandableMenu/expandable-menu.component.mjs +81 -19
  36. package/esm2022/theme/layout/app/favorite-pages/favorite-pages-sidebar.component.mjs +6 -4
  37. package/esm2022/theme/layout/app/general-search/general-search.component.mjs +4 -4
  38. package/esm2022/theme/layout/app/history/history-sidebar.component.mjs +6 -4
  39. package/esm2022/theme/layout/app/layout/app.layout.component.mjs +422 -20
  40. package/esm2022/theme/layout/app/layout/mfe.layout.component.mjs +24 -35
  41. package/esm2022/theme/layout/app/site-map/site-map-sidebar.component.mjs +6 -4
  42. package/esm2022/theme/layout/app/static-sidebar/static-sidebar.component.mjs +85 -27
  43. package/esm2022/theme/layout/app/topbar/app.topbar.component.mjs +3 -3
  44. package/esm2022/theme/layout/service/breadcrumb-publisher.service.mjs +86 -0
  45. package/esm2022/theme/layout/service/tab-session.service.mjs +126 -0
  46. package/esm2022/ui-business/ref-value/src/ref-value.component.mjs +15 -7
  47. package/esm2022/util/sync-active-tab-route/src/sync-active-tab-route.directive.mjs +29 -9
  48. package/fesm2022/aril-app.component-s14ruALV.mjs +183 -0
  49. package/fesm2022/aril-app.component-s14ruALV.mjs.map +1 -0
  50. package/fesm2022/aril-boot-bridge.mjs +35 -4
  51. package/fesm2022/aril-boot-bridge.mjs.map +1 -1
  52. package/fesm2022/aril-boot-config-api.mjs +11 -2
  53. package/fesm2022/aril-boot-config-api.mjs.map +1 -1
  54. package/fesm2022/aril-boot-config-apps.mjs +1678 -10
  55. package/fesm2022/aril-boot-config-apps.mjs.map +1 -1
  56. package/fesm2022/aril-boot-config-plugins.mjs +12 -4
  57. package/fesm2022/aril-boot-config-plugins.mjs.map +1 -1
  58. package/fesm2022/aril-boot-host.mjs +21 -7
  59. package/fesm2022/aril-boot-host.mjs.map +1 -1
  60. package/fesm2022/aril-boot-mfe-app.component-a34GeuUv.mjs +183 -0
  61. package/fesm2022/aril-boot-mfe-app.component-a34GeuUv.mjs.map +1 -0
  62. package/fesm2022/aril-boot-mfe-aril-boot-mfe-KFO_X7yR.mjs +631 -0
  63. package/fesm2022/aril-boot-mfe-aril-boot-mfe-KFO_X7yR.mjs.map +1 -0
  64. package/fesm2022/aril-boot-mfe.mjs +5 -3
  65. package/fesm2022/aril-boot-mfe.mjs.map +1 -1
  66. package/fesm2022/aril-keycloak.mjs +16 -1
  67. package/fesm2022/aril-keycloak.mjs.map +1 -1
  68. package/fesm2022/aril-provider.mjs +90 -12
  69. package/fesm2022/aril-provider.mjs.map +1 -1
  70. package/fesm2022/aril-theme-layout.mjs +2630 -2017
  71. package/fesm2022/aril-theme-layout.mjs.map +1 -1
  72. package/fesm2022/aril-ui-business-ref-value.mjs +14 -6
  73. package/fesm2022/aril-ui-business-ref-value.mjs.map +1 -1
  74. package/fesm2022/aril-util-sync-active-tab-route.mjs +28 -8
  75. package/fesm2022/aril-util-sync-active-tab-route.mjs.map +1 -1
  76. package/fesm2022/aril.mjs +354 -25
  77. package/fesm2022/aril.mjs.map +1 -1
  78. package/keycloak/src/auth.interceptor.d.ts +7 -0
  79. package/package.json +216 -216
  80. package/provider/src/prodiveHost.d.ts +1 -0
  81. package/theme/layout/app/expandableMenu/expandable-menu.component.d.ts +21 -4
  82. package/theme/layout/app/expandableMenu/expandable-menu.component.html +19 -5
  83. package/theme/layout/app/expandableMenu/expandable-menu.component.ts +69 -9
  84. package/theme/layout/app/favorite-pages/favorite-pages-sidebar.component.html +1 -0
  85. package/theme/layout/app/favorite-pages/favorite-pages-sidebar.component.ts +3 -1
  86. package/theme/layout/app/general-search/general-search.component.html +2 -1
  87. package/theme/layout/app/general-search/general-search.component.ts +2 -2
  88. package/theme/layout/app/history/history-sidebar.component.html +3 -1
  89. package/theme/layout/app/history/history-sidebar.component.ts +3 -1
  90. package/theme/layout/app/layout/app.layout.component.d.ts +105 -5
  91. package/theme/layout/app/layout/app.layout.component.html +102 -1
  92. package/theme/layout/app/layout/app.layout.component.scss +372 -0
  93. package/theme/layout/app/layout/app.layout.component.ts +452 -13
  94. package/theme/layout/app/layout/mfe.layout.component.d.ts +7 -5
  95. package/theme/layout/app/layout/mfe.layout.component.ts +13 -39
  96. package/theme/layout/app/site-map/site-map-sidebar.component.html +1 -0
  97. package/theme/layout/app/site-map/site-map-sidebar.component.ts +3 -1
  98. package/theme/layout/app/static-sidebar/static-sidebar.component.d.ts +26 -5
  99. package/theme/layout/app/static-sidebar/static-sidebar.component.html +11 -5
  100. package/theme/layout/app/static-sidebar/static-sidebar.component.ts +68 -13
  101. package/theme/layout/app/topbar/app.topbar.component.html +0 -1
  102. package/theme/layout/app/topbar/app.topbar.component.scss +1 -1
  103. package/theme/layout/service/breadcrumb-publisher.service.d.ts +24 -0
  104. package/theme/layout/service/breadcrumb-publisher.service.ts +95 -0
  105. package/theme/layout/service/tab-session.service.d.ts +52 -0
  106. package/theme/layout/service/tab-session.service.ts +138 -0
  107. package/theme/styles/layout/_breadcrumb.scss +95 -0
  108. package/theme/styles/layout/_content.scss +2 -2
  109. package/ui-business/ref-value/src/ref-value.component.d.ts +4 -2
  110. package/util/sync-active-tab-route/src/sync-active-tab-route.directive.d.ts +15 -2
  111. package/boot/config/apps/src/reuse-strategy.d.ts +0 -4
  112. package/esm2022/boot/config/apps/src/reuse-strategy.mjs +0 -9
  113. package/esm2022/theme/layout/app/breadcrumb/app.breadcrumb.component.mjs +0 -107
  114. package/fesm2022/aril-app.component-wxP3y8dg.mjs +0 -81
  115. package/fesm2022/aril-app.component-wxP3y8dg.mjs.map +0 -1
  116. package/fesm2022/aril-boot-mfe-app.component-7IjAmjz0.mjs +0 -80
  117. package/fesm2022/aril-boot-mfe-app.component-7IjAmjz0.mjs.map +0 -1
  118. package/fesm2022/aril-boot-mfe-aril-boot-mfe-KXDpUyv7.mjs +0 -315
  119. package/fesm2022/aril-boot-mfe-aril-boot-mfe-KXDpUyv7.mjs.map +0 -1
  120. package/theme/layout/app/breadcrumb/app.breadcrumb.component.d.ts +0 -25
  121. package/theme/layout/app/breadcrumb/app.breadcrumb.component.html +0 -8
  122. package/theme/layout/app/breadcrumb/app.breadcrumb.component.ts +0 -127
@@ -7,11 +7,9 @@ import { Subscription } from 'rxjs';
7
7
  import { PluginMenuItem } from 'aril/boot/config/apps';
8
8
  import { TranslateJsonPipe } from 'aril/util/pipes';
9
9
  import { LayoutService } from '../../service/app.layout.service';
10
- import { AppMenuService } from '../../service/app.menu.service';
11
10
  import * as i0 from "@angular/core";
12
11
  export declare class ExpandableMenuComponent implements OnInit, OnDestroy {
13
12
  private translateJsonPipe;
14
- private menuService;
15
13
  private router;
16
14
  private keycloak;
17
15
  private translocoService;
@@ -50,7 +48,8 @@ export declare class ExpandableMenuComponent implements OnInit, OnDestroy {
50
48
  private readonly MAX_LENGTH;
51
49
  actionLoadingItems: Set<string>;
52
50
  private actionSubscription;
53
- constructor(translateJsonPipe: TranslateJsonPipe, menuService: AppMenuService, router: Router, keycloak: KeycloakService, translocoService: TranslocoService, layoutService: LayoutService, http: HttpClient);
51
+ private readonly navService;
52
+ constructor(translateJsonPipe: TranslateJsonPipe, router: Router, keycloak: KeycloakService, translocoService: TranslocoService, layoutService: LayoutService, http: HttpClient);
54
53
  get selectedMainItem(): number | null;
55
54
  get secondarySidebarExpanded(): boolean;
56
55
  isTextTruncated(label: any, lang: string | null, menuType?: 'main' | 'secondary' | 'tertiary' | 'nested'): boolean;
@@ -61,18 +60,36 @@ export declare class ExpandableMenuComponent implements OnInit, OnDestroy {
61
60
  updateMenuFromRootKey(index: number): void;
62
61
  updateMenuItems(): void;
63
62
  getMenuItems(): PluginMenuItem[];
64
- selectMainItem(index: number, item: PluginMenuItem): void;
63
+ selectMainItem(event: MouseEvent, index: number, item: PluginMenuItem): void;
65
64
  toggleSecondarySidebar(): void;
66
65
  isMobile(): boolean;
67
66
  onResize(): void;
68
67
  updateActiveMenuFromCurrentRoute(): void;
69
68
  isHaveSelectedChildItem(item: PluginMenuItem): boolean;
70
69
  getLocalText(text: any): string;
70
+ /**
71
+ * Hash strategy için routerLink'i `#`-prefix'li href'e çevirir. routerLink config'te
72
+ * leading slash içerebilir (örn. anasayfa için `'/'`); naive `'#/' + routerLink`
73
+ * concatenation `#//` üretirdi — sağ tık "linki kopyala" UX'inde bu çirkin görünür.
74
+ */
75
+ getHashHref(routerLink: string | undefined | null): string;
71
76
  getActionItemKey(item: PluginMenuItem): string;
72
77
  isActionLoading(item: PluginMenuItem): boolean;
73
78
  onActionItemClick(item: PluginMenuItem, event?: Event): void;
74
79
  private buildSuccessRoute;
75
80
  private getValueByPath;
81
+ /**
82
+ * Chrome-like menü click davranışı (secondary/tertiary/nested seviyeleri için):
83
+ * - Normal click → aktif tab'da navigate
84
+ * - Ctrl/Cmd + Click → arka planda yeni tab
85
+ * - Ctrl/Cmd + Shift + Click → ön planda yeni tab
86
+ */
87
+ onMenuClick(event: MouseEvent, item: PluginMenuItem): void;
88
+ /** Middle click (button 1) → arka planda yeni tab. */
89
+ onMenuAuxClick(event: MouseEvent, item: PluginMenuItem): void;
90
+ /** Middle click default scroll davranışını engelle. */
91
+ onMenuMousedown(event: MouseEvent): void;
92
+ private dispatchNavigation;
76
93
  toggleSidebar(): void;
77
94
  static ɵfac: i0.ɵɵFactoryDeclaration<ExpandableMenuComponent, never>;
78
95
  static ɵcmp: i0.ɵɵComponentDeclaration<ExpandableMenuComponent, "app-expandable-menu", never, {}, {}, never, never, true, never>;
@@ -7,7 +7,9 @@
7
7
  @if(!item.separator){
8
8
  <li
9
9
  [ngClass]="{'active': isActiveRoute(item) || isHaveSelectedChildItem(item)}"
10
- (click)="selectMainItem(i, item)"
10
+ (click)="selectMainItem($event, i, item)"
11
+ (auxclick)="onMenuAuxClick($event, item)"
12
+ (mousedown)="onMenuMousedown($event)"
11
13
  [pTooltip]="(item.label && activeLang() ? item.label[activeLang()!] || '' : '')"
12
14
  tooltipPosition="right">
13
15
  <div class="main-menu-item">
@@ -55,7 +57,11 @@
55
57
  } @else {
56
58
  <!-- Regular menu item -->
57
59
  <li class="secondary-menu-item" [ngClass]="{'active': isActiveRoute(subItem)}">
58
- <a [routerLink]="subItem.routerLink"
60
+ <a [attr.href]="subItem.routerLink ? getHashHref(subItem.routerLink) : null"
61
+ [arilNavLink]="{ tabId: '', navLink: subItem.routerLink || '', navName: getLocalText(subItem.label), icon: subItem.icon }"
62
+ (click)="onMenuClick($event, subItem)"
63
+ (auxclick)="onMenuAuxClick($event, subItem)"
64
+ (mousedown)="onMenuMousedown($event)"
59
65
  class="secondary-menu-link"
60
66
  [pTooltip]="!isMobile() && isTextTruncated(subItem.label, activeLang(), 'secondary') ? (subItem.label && activeLang() ? subItem.label[activeLang()!] || '' : '') : ''"
61
67
  tooltipPosition="right">
@@ -82,7 +88,11 @@
82
88
  </li>
83
89
  } @else {
84
90
  <li class="tertiary-menu-item" [ngClass]="{'active': isActiveRoute(childItem)}">
85
- <a [routerLink]="childItem.routerLink"
91
+ <a [attr.href]="childItem.routerLink ? getHashHref(childItem.routerLink) : null"
92
+ [arilNavLink]="{ tabId: '', navLink: childItem.routerLink || '', navName: getLocalText(childItem.label), icon: childItem.icon }"
93
+ (click)="onMenuClick($event, childItem)"
94
+ (auxclick)="onMenuAuxClick($event, childItem)"
95
+ (mousedown)="onMenuMousedown($event)"
86
96
  class="tertiary-menu-link"
87
97
  [pTooltip]="!isMobile() && isTextTruncated(childItem.label, activeLang(), 'tertiary') ? (childItem.label && activeLang() ? childItem.label[activeLang()!] || '' : '') : ''"
88
98
  tooltipPosition="right">
@@ -106,10 +116,14 @@
106
116
  </a>
107
117
  </li>
108
118
  } @else {
109
- <li
119
+ <li
110
120
  class="nested-menu-item"
111
121
  [ngClass]="{'active': isActiveRoute(nestedItem)}">
112
- <a [routerLink]="nestedItem.routerLink"
122
+ <a [attr.href]="nestedItem.routerLink ? getHashHref(nestedItem.routerLink) : null"
123
+ [arilNavLink]="{ tabId: '', navLink: nestedItem.routerLink || '', navName: getLocalText(nestedItem.label), icon: nestedItem.icon }"
124
+ (click)="onMenuClick($event, nestedItem)"
125
+ (auxclick)="onMenuAuxClick($event, nestedItem)"
126
+ (mousedown)="onMenuMousedown($event)"
113
127
  class="nested-menu-link"
114
128
  [pTooltip]="!isMobile() && isTextTruncated(nestedItem.label, activeLang(), 'nested') ? (nestedItem.label && activeLang() ? nestedItem.label[activeLang()!] || '' : '') : ''"
115
129
  tooltipPosition="right">
@@ -1,8 +1,8 @@
1
1
  import { NgClass } from '@angular/common';
2
2
  import { HttpClient } from '@angular/common/http';
3
- import { Component, HostListener, OnDestroy, OnInit, Signal } from '@angular/core';
3
+ import { Component, HostListener, OnDestroy, OnInit, Signal, inject } from '@angular/core';
4
4
  import { toSignal } from '@angular/core/rxjs-interop';
5
- import { NavigationEnd, Router, RouterLink } from '@angular/router';
5
+ import { NavigationEnd, Router } from '@angular/router';
6
6
 
7
7
  import { TooltipModule } from 'primeng/tooltip';
8
8
 
@@ -11,16 +11,15 @@ import { KeycloakService } from 'keycloak-angular';
11
11
  import { Subscription, filter } from 'rxjs';
12
12
 
13
13
  import { bridge } from 'aril/boot/bridge';
14
- import { PluginMenuItem } from 'aril/boot/config/apps';
14
+ import { NavItem, NavLinkDirective, NavService, PluginMenuItem } from 'aril/boot/config/apps';
15
15
  import { TranslateJsonPipe } from 'aril/util/pipes';
16
16
 
17
17
  import { LayoutService } from '../../service/app.layout.service';
18
- import { AppMenuService } from '../../service/app.menu.service';
19
18
 
20
19
  @Component({
21
20
  standalone: true,
22
21
  selector: 'app-expandable-menu',
23
- imports: [RouterLink, NgClass, TooltipModule],
22
+ imports: [NgClass, TooltipModule, NavLinkDirective],
24
23
  templateUrl: 'expandable-menu.component.html',
25
24
  providers: [TranslateJsonPipe]
26
25
  })
@@ -73,10 +72,10 @@ export class ExpandableMenuComponent implements OnInit, OnDestroy {
73
72
 
74
73
  actionLoadingItems = new Set<string>();
75
74
  private actionSubscription: Subscription | null = null;
75
+ private readonly navService = inject(NavService);
76
76
 
77
77
  constructor(
78
78
  private translateJsonPipe: TranslateJsonPipe,
79
- private menuService: AppMenuService,
80
79
  private router: Router,
81
80
  private keycloak: KeycloakService,
82
81
  private translocoService: TranslocoService,
@@ -195,14 +194,14 @@ export class ExpandableMenuComponent implements OnInit, OnDestroy {
195
194
  }
196
195
 
197
196
  updateMenuItems(): void {
198
- this.cachedMenuItems = bridge.isHostMode() ? bridge.hostMenuItems() : this.menuService.menuItems();
197
+ this.cachedMenuItems = bridge.hostMenuItems();
199
198
  }
200
199
 
201
200
  getMenuItems(): PluginMenuItem[] {
202
201
  return this.cachedMenuItems;
203
202
  }
204
203
 
205
- selectMainItem(index: number, item: PluginMenuItem): void {
204
+ selectMainItem(event: MouseEvent, index: number, item: PluginMenuItem): void {
206
205
  // Handle action items at top level
207
206
  if (item.action && (!item.items || item.items.length === 0)) {
208
207
  this.onActionItemClick(item);
@@ -213,7 +212,12 @@ export class ExpandableMenuComponent implements OnInit, OnDestroy {
213
212
  this.layoutService.setSecondarySidebarExpanded(false);
214
213
  this.layoutService.setSelectedMainItem(index);
215
214
  this.selectedMainMenuItem = item;
216
- this.router.navigate([item.routerLink]);
215
+ this.dispatchNavigation(event, {
216
+ tabId: '',
217
+ navLink: item.routerLink,
218
+ navName: this.getLocalText(item.label),
219
+ icon: item.icon
220
+ });
217
221
  return;
218
222
  }
219
223
 
@@ -296,6 +300,16 @@ export class ExpandableMenuComponent implements OnInit, OnDestroy {
296
300
  return this.translateJsonPipe.transform(text);
297
301
  }
298
302
 
303
+ /**
304
+ * Hash strategy için routerLink'i `#`-prefix'li href'e çevirir. routerLink config'te
305
+ * leading slash içerebilir (örn. anasayfa için `'/'`); naive `'#/' + routerLink`
306
+ * concatenation `#//` üretirdi — sağ tık "linki kopyala" UX'inde bu çirkin görünür.
307
+ */
308
+ getHashHref(routerLink: string | undefined | null): string {
309
+ if (!routerLink) return '#';
310
+ return routerLink.startsWith('/') ? '#' + routerLink : '#/' + routerLink;
311
+ }
312
+
299
313
  getActionItemKey(item: PluginMenuItem): string {
300
314
  return item.action?.url + '|' + item.action?.successRoute;
301
315
  }
@@ -362,6 +376,52 @@ export class ExpandableMenuComponent implements OnInit, OnDestroy {
362
376
  }, obj);
363
377
  }
364
378
 
379
+ /**
380
+ * Chrome-like menü click davranışı (secondary/tertiary/nested seviyeleri için):
381
+ * - Normal click → aktif tab'da navigate
382
+ * - Ctrl/Cmd + Click → arka planda yeni tab
383
+ * - Ctrl/Cmd + Shift + Click → ön planda yeni tab
384
+ */
385
+ onMenuClick(event: MouseEvent, item: PluginMenuItem): void {
386
+ if (!item.routerLink) return;
387
+ event.preventDefault();
388
+ this.dispatchNavigation(event, {
389
+ tabId: '',
390
+ navLink: item.routerLink,
391
+ navName: this.getLocalText(item.label),
392
+ icon: item.icon
393
+ });
394
+ }
395
+
396
+ /** Middle click (button 1) → arka planda yeni tab. */
397
+ onMenuAuxClick(event: MouseEvent, item: PluginMenuItem): void {
398
+ if (event.button !== 1 || !item.routerLink) return;
399
+ event.preventDefault();
400
+ this.navService.openInBackgroundTab({
401
+ tabId: '',
402
+ navLink: item.routerLink,
403
+ navName: this.getLocalText(item.label),
404
+ icon: item.icon
405
+ });
406
+ }
407
+
408
+ /** Middle click default scroll davranışını engelle. */
409
+ onMenuMousedown(event: MouseEvent): void {
410
+ if (event.button === 1) event.preventDefault();
411
+ }
412
+
413
+ private dispatchNavigation(event: MouseEvent, navItem: NavItem): void {
414
+ if (event.ctrlKey || event.metaKey) {
415
+ if (event.shiftKey) {
416
+ this.navService.openInNewTabAndFocus(navItem);
417
+ } else {
418
+ this.navService.openInBackgroundTab(navItem);
419
+ }
420
+ } else {
421
+ this.navService.navigateInCurrentTab(navItem);
422
+ }
423
+ }
424
+
365
425
  toggleSidebar() {
366
426
  // Eğer seçili bir ana menü öğesi varsa ve alt menüleri varsa toggle yapabiliriz
367
427
  if (this.selectedMainMenuItem && this.selectedMainMenuItem.items && this.selectedMainMenuItem.items.length > 0) {
@@ -51,6 +51,7 @@
51
51
  <div
52
52
  class="favorite-item"
53
53
  [class.default-item]="favorite.id < 0"
54
+ [arilNavLink]="{ tabId: '', navLink: favorite.url, navName: favorite.label }"
54
55
  (click)="navigateToFavorite(favorite)"
55
56
  [pTooltip]="getFullUrl(favorite.url)"
56
57
  tooltipPosition="top">
@@ -13,6 +13,7 @@ import { TooltipModule } from 'primeng/tooltip';
13
13
  import { TranslocoModule, TranslocoService } from '@ngneat/transloco';
14
14
  import { Subject, switchMap } from 'rxjs';
15
15
 
16
+ import { NavLinkDirective } from 'aril/boot/config/apps';
16
17
  import { ButtonComponent } from 'aril/ui/button';
17
18
 
18
19
  import { LayoutService } from '../../service/app.layout.service';
@@ -34,7 +35,8 @@ import { solidIcons } from 'aril/util/lib';
34
35
  ButtonComponent,
35
36
  TranslocoModule,
36
37
  TooltipModule,
37
- FontAwesomeModule
38
+ FontAwesomeModule,
39
+ NavLinkDirective
38
40
  ],
39
41
  templateUrl: './favorite-pages-sidebar.component.html',
40
42
  styleUrls: ['./favorite-pages-sidebar.component.scss'],
@@ -29,7 +29,8 @@
29
29
  </div>
30
30
  </ng-template>
31
31
  <ng-template let-result pTemplate="item">
32
- <div class="flex items-center gap-2">
32
+ <div class="flex items-center gap-2"
33
+ [arilNavLink]="result.url ? { tabId: '', navLink: result.url, navName: result.title } : null">
33
34
  <div class="result-icon">
34
35
  <i class="pi {{ result.icon }}"></i>
35
36
  </div>
@@ -11,7 +11,7 @@ import { of, startWith, Subject } from 'rxjs';
11
11
  import { catchError, filter, map, switchMap } from 'rxjs/operators';
12
12
 
13
13
  import { bridge } from 'aril/boot/bridge';
14
- import { Apps } from 'aril/boot/config/apps';
14
+ import { Apps, NavLinkDirective } from 'aril/boot/config/apps';
15
15
 
16
16
  import { SearchService } from '../../service/search.service';
17
17
  import { getPresentableData } from './helpers';
@@ -20,7 +20,7 @@ import { CacheSearchRequestDTO, DefaultSearchAggregateAssetAggregate, Presentabl
20
20
  @Component({
21
21
  selector: 'app-general-search',
22
22
  standalone: true,
23
- imports: [CommonModule, FormsModule, TranslocoModule, AutoCompleteModule],
23
+ imports: [CommonModule, FormsModule, TranslocoModule, AutoCompleteModule, NavLinkDirective],
24
24
  templateUrl: './general-search.component.html',
25
25
  styleUrls: ['./general-search.component.scss']
26
26
  })
@@ -41,7 +41,9 @@
41
41
  <p-scrollPanel [style]="{ width: '100%', height: '100%' }">
42
42
  <div class="history-items">
43
43
  @for (item of filteredHistory(); track trackByItemId($index, item)) {
44
- <div class="history-item" (click)="navigateToItem(item)">
44
+ <div class="history-item"
45
+ [arilNavLink]="{ tabId: '', navLink: item.url, navName: getLocalizedTitle(item) }"
46
+ (click)="navigateToItem(item)">
45
47
  <div class="item-content">
46
48
  <div class="item-header">
47
49
  <div class="item-title">
@@ -13,6 +13,7 @@ import { TooltipModule } from 'primeng/tooltip';
13
13
  import { TranslocoModule, TranslocoService } from '@ngneat/transloco';
14
14
  import { Subject, takeUntil } from 'rxjs';
15
15
 
16
+ import { NavLinkDirective } from 'aril/boot/config/apps';
16
17
  import { ButtonComponent } from 'aril/ui/button';
17
18
  import { TextComponent } from 'aril/ui/text';
18
19
 
@@ -36,7 +37,8 @@ import { solidIcons } from 'aril/util/lib';
36
37
  BadgeModule,
37
38
  TranslocoModule,
38
39
  TextComponent,
39
- FontAwesomeModule
40
+ FontAwesomeModule,
41
+ NavLinkDirective
40
42
  ],
41
43
  templateUrl: './history-sidebar.component.html',
42
44
  styleUrls: ['./history-sidebar.component.scss'],
@@ -1,24 +1,124 @@
1
- import { OnDestroy, Renderer2 } from '@angular/core';
1
+ import { CdkDragDrop } from '@angular/cdk/drag-drop';
2
+ import { AfterViewInit, ElementRef, OnDestroy, Renderer2 } from '@angular/core';
2
3
  import { Router } from '@angular/router';
4
+ import { MenuItem } from 'primeng/api';
5
+ import { ContextMenu } from 'primeng/contextmenu';
3
6
  import { Subscription } from 'rxjs';
7
+ import { NavItem, NavService } from 'aril/boot/config/apps';
8
+ import { TranslateJsonPipe } from 'aril/util/pipes';
4
9
  import { LayoutService } from '../../service/app.layout.service';
5
10
  import { AppMenuService } from '../../service/app.menu.service';
6
- import { AppTopbarComponent } from '../topbar/app.topbar.component';
7
11
  import { ExpandableMenuComponent } from '../expandableMenu/expandable-menu.component';
8
12
  import { StaticSidebarComponent } from '../static-sidebar/static-sidebar.component';
13
+ import { AppTopbarComponent } from '../topbar/app.topbar.component';
9
14
  import * as i0 from "@angular/core";
10
- export declare class AppLayoutComponent implements OnDestroy {
11
- private menuService;
15
+ export declare class AppLayoutComponent implements OnDestroy, AfterViewInit {
16
+ private readonly menuService;
12
17
  layoutService: LayoutService;
13
18
  renderer: Renderer2;
14
19
  router: Router;
20
+ private readonly translateJsonPipe;
15
21
  overlayMenuOpenSubscription: Subscription;
16
22
  menuOutsideClickListener: any;
17
23
  menuScrollListener: any;
18
24
  expandableMenuComponent: ExpandableMenuComponent;
19
25
  staticSidebarComponent: StaticSidebarComponent;
20
26
  appTopbar: AppTopbarComponent;
21
- constructor(menuService: AppMenuService, layoutService: LayoutService, renderer: Renderer2, router: Router);
27
+ tabsContainer?: ElementRef<HTMLDivElement>;
28
+ tabContextMenu?: ContextMenu;
29
+ navLinkContextMenu?: ContextMenu;
30
+ navService: NavService;
31
+ private readonly translocoService;
32
+ private readonly navLinkContextMenuService;
33
+ private readonly tabSession;
34
+ tabContextMenuItems: MenuItem[];
35
+ /** Tab bar yatay scroll konum/genişlik durumu. Chevron butonlarının görünürlüğünü kontrol eder. */
36
+ canScrollLeft: import("@angular/core").WritableSignal<boolean>;
37
+ canScrollRight: import("@angular/core").WritableSignal<boolean>;
38
+ /**
39
+ * Breadcrumb segmentleri — `bridge.breadcrumbs()` üzerinden okunur.
40
+ * Source-of-truth aktif MFE'de çalışan `BreadcrumbPublisherService`'tir;
41
+ * MFE her NavigationEnd'de kendi route ağacından zinciri çıkarıp bridge'e yazar.
42
+ * Shell yalnızca presenter — başa "Anasayfa" item'ı ekleyip son item'ı aktif işaretler.
43
+ */
44
+ breadcrumbItems: import("@angular/core").Signal<(MenuItem & {
45
+ active?: boolean | undefined;
46
+ isHome?: boolean | undefined;
47
+ })[]>;
48
+ /**
49
+ * Aktif olmayan breadcrumb segmentine tıklama → mevcut tab'ı parent route ile günceller.
50
+ * `<a [routerLink]>` directive'i `href`'i Router'ın location strategy'sine göre
51
+ * (hash/path) doğru formatlar — sağ tık "linki kopyala" davranışı çalışır.
52
+ * Default Router navigate'ini `preventDefault()` ile durdurup `navigateInCurrentTab`
53
+ * üzerinden ilerletiyoruz; böylece yeni sekme açılmaz.
54
+ */
55
+ onBreadcrumbClick(event: MouseEvent, item: {
56
+ label?: string;
57
+ url?: string;
58
+ }): void;
59
+ /**
60
+ * Home icon → root menu item (`apps.service.ts:51`'de tanımlı `{ root: true, routerLink: '/' }`).
61
+ * Tek source-of-truth — etiket çevirisi `TranslateJsonPipe` ile aktif dilden okunur.
62
+ * Mevcut tab güncellenir, yeni sekme açılmaz.
63
+ */
64
+ onHomeClick(event: MouseEvent): void;
65
+ /**
66
+ * Anasayfa için NavItem — breadcrumb home ikonunun hem `(click)` handler'ı hem
67
+ * `[arilNavLink]` context menu directive'i tarafından paylaşılır.
68
+ */
69
+ readonly homeNavItem: import("@angular/core").Signal<NavItem>;
70
+ /**
71
+ * Tab icon resolution sırası:
72
+ * 1. NavItem.icon (sidebar click'inden iletilmişse — en doğru)
73
+ * 2. Menu config'inde navLink'i en uzun prefix olarak match eden item.icon
74
+ * 3. Generic fallback (`pi pi-file`)
75
+ *
76
+ * Best-match için longest-prefix kullanılır: `wdm/meters/123` için hem `wdm` hem
77
+ * `wdm/meters` match'lerse, daha spesifik `wdm/meters` kazanır. Böylece deep link
78
+ * refresh'inde (NavService.firstCheckForRoute path'inde icon set edilemediği zaman)
79
+ * doğru remote/section ikonu otomatik bulunur.
80
+ */
81
+ getTabIcon(item: NavItem): string;
82
+ private findIconForRoute;
83
+ drop(event: CdkDragDrop<any[]>): void;
84
+ isActive(item: NavItem): boolean;
85
+ closeTab(event: Event, item: NavItem): void;
86
+ /** Tab sol-click → o tab'a geç. NavService Router'a `?_tab` URL'i ve history state'i verir. */
87
+ onTabClick(event: MouseEvent, item: NavItem): void;
88
+ /** Middle click (button 1) → tab kapat (Chrome standardı). Sabit tab'lar kapatılmaz. */
89
+ onTabAuxClick(event: MouseEvent, item: NavItem): void;
90
+ /** Middle click default scroll davranışını engelle. */
91
+ onTabMousedown(event: MouseEvent): void;
92
+ /**
93
+ * Tab right-click → context menu. Sabit tab'larda "Kapat" disabled; pin/unpin item'ı
94
+ * Chrome'daki gibi en üstte yer alır. `closeOthers`/`closeToRight`/`closeAll` zaten
95
+ * NavService seviyesinde pinned'i koruyor — UI'da ekstra disabled gerekmez.
96
+ */
97
+ onTabContextMenu(event: MouseEvent, item: NavItem): void;
98
+ private isLastTab;
99
+ /**
100
+ * Tek kalan tab anasayfaysa true → kapatma (✕) butonu gizlenir (NavService de kapatmayı
101
+ * reddeder). "Her zaman en az 1 tab açık" kuralının UI tarafı.
102
+ */
103
+ isLastHomeTab(item: NavItem): boolean;
104
+ /** "+" butonu → ana sayfaya navigate (yeni tab varsa onu aç, yoksa eklenir). */
105
+ onAddTab(): void;
106
+ scrollTabsLeft(): void;
107
+ scrollTabsRight(): void;
108
+ private updateScrollState;
109
+ /**
110
+ * Klavye kısayolları (Chrome paritesi):
111
+ * - `Alt+W` → aktif tab kapat (Ctrl+W browser-reserved)
112
+ * - `Ctrl+Tab` / `Ctrl+Shift+Tab` → sonraki/önceki tab (denemeli — bazı tarayıcılar override etmez)
113
+ * - `Ctrl+PageDown` / `Ctrl+PageUp` → sonraki/önceki tab (yedek)
114
+ * - `Alt+1..9` → ilgili index'teki tab (Ctrl+1..9 browser-reserved)
115
+ * - `Ctrl+Shift+T` veya `Alt+Shift+T` → son kapatılan tab'ı geri aç
116
+ */
117
+ onDocumentKeydown(event: KeyboardEvent): void;
118
+ private isTypingInInput;
119
+ ngAfterViewInit(): void;
120
+ private resizeObserver;
121
+ constructor(menuService: AppMenuService, layoutService: LayoutService, renderer: Renderer2, router: Router, translateJsonPipe: TranslateJsonPipe);
22
122
  blockBodyScroll(): void;
23
123
  unblockBodyScroll(): void;
24
124
  private isElementInside;
@@ -7,7 +7,108 @@
7
7
  <div class="layout-content-wrapper">
8
8
  <app-topbar />
9
9
  <div class="layout-content">
10
- <router-outlet />
10
+ <p-breadcrumb styleClass="breadcrumb-strip" [model]="breadcrumbItems()">
11
+ <ng-template pTemplate="item" let-item>
12
+ @if (item.isHome) {
13
+ <a class="breadcrumb-home"
14
+ [routerLink]="'/'"
15
+ [arilNavLink]="homeNavItem()"
16
+ (click)="onHomeClick($event)"
17
+ [attr.aria-label]="'breadcrumb.home' | transloco">
18
+ <i class="pi pi-home" aria-hidden="true"></i>
19
+ </a>
20
+ } @else if (item.active || !item.url) {
21
+ <span class="breadcrumb-item active">{{ item.label }}</span>
22
+ } @else {
23
+ <a
24
+ class="breadcrumb-item breadcrumb-link"
25
+ [routerLink]="item.url"
26
+ [arilNavLink]="{ tabId: '', navLink: item.url, navName: item.label }"
27
+ (click)="onBreadcrumbClick($event, item)">
28
+ {{ item.label }}
29
+ </a>
30
+ }
31
+ </ng-template>
32
+ <ng-template pTemplate="separator">
33
+ <i class="breadcrumb-sep pi pi-angle-right text-base" aria-hidden="true"></i>
34
+ </ng-template>
35
+ </p-breadcrumb>
36
+ <div class="tabs-bar">
37
+ @if (canScrollLeft()) {
38
+ <button
39
+ type="button"
40
+ class="tabs-scroll-btn tabs-scroll-left"
41
+ (click)="scrollTabsLeft()"
42
+ [pTooltip]="'tab.scrollLeft' | transloco"
43
+ tooltipPosition="bottom"
44
+ [attr.aria-label]="'tab.scrollLeft' | transloco">
45
+ <i class="pi pi-chevron-left"></i>
46
+ </button>
47
+ }
48
+ <div
49
+ #tabsContainer
50
+ class="tabs-container"
51
+ cdkDropList
52
+ cdkDropListOrientation="horizontal"
53
+ (cdkDropListDropped)="drop($event)">
54
+ @for (item of navService.activeRoutes(); track item.tabId; let i = $index) {
55
+ <div
56
+ class="tab-item"
57
+ cdkDrag
58
+ [class.active]="isActive(item)"
59
+ [class.pinned]="item.pinned"
60
+ [pTooltip]="item.navName"
61
+ tooltipPosition="bottom"
62
+ [tooltipDisabled]="isActive(item) && !item.pinned"
63
+ [showDelay]="500"
64
+ (click)="onTabClick($event, item)"
65
+ (auxclick)="onTabAuxClick($event, item)"
66
+ (mousedown)="onTabMousedown($event)"
67
+ (contextmenu)="onTabContextMenu($event, item)">
68
+ <i class="tab-icon" [ngClass]="getTabIcon(item)"></i>
69
+ @if (!item.pinned) {
70
+ <span class="tab-title">{{ item.navName }}</span>
71
+ @if (!isLastHomeTab(item)) {
72
+ <button
73
+ type="button"
74
+ class="tab-close-btn"
75
+ (click)="closeTab($event, item)"
76
+ [attr.aria-label]="'tab.close' | transloco">
77
+ <i class="pi pi-times"></i>
78
+ </button>
79
+ }
80
+ }
81
+ </div>
82
+ }
83
+ </div>
84
+ @if (canScrollRight()) {
85
+ <button
86
+ type="button"
87
+ class="tabs-scroll-btn tabs-scroll-right"
88
+ (click)="scrollTabsRight()"
89
+ [pTooltip]="'tab.scrollRight' | transloco"
90
+ tooltipPosition="bottom"
91
+ [attr.aria-label]="'tab.scrollRight' | transloco">
92
+ <i class="pi pi-chevron-right"></i>
93
+ </button>
94
+ }
95
+ <button
96
+ type="button"
97
+ class="tabs-add-btn"
98
+ (click)="onAddTab()"
99
+ [pTooltip]="'tab.newTab' | transloco"
100
+ tooltipPosition="bottom"
101
+ [attr.aria-label]="'tab.newTab' | transloco">
102
+ <i class="pi pi-plus"></i>
103
+ </button>
104
+ </div>
105
+ <p-contextMenu #tabContextMenu [model]="tabContextMenuItems" appendTo="body"></p-contextMenu>
106
+ <!-- Global nav-link context menu: tüm linkli yapılarda `[arilNavLink]` directive'i bu instance'ı paylaşır. -->
107
+ <p-contextMenu #navLinkContextMenu [model]="[]" appendTo="body"></p-contextMenu>
108
+
109
+ <div class="layout-page">
110
+ <router-outlet />
111
+ </div>
11
112
  </div>
12
113
  </div>
13
114
  <app-profilemenu />