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.
- package/boot/bridge/src/mfe-bridge.d.ts +42 -2
- package/boot/config/apps/index.d.ts +7 -1
- package/boot/config/apps/src/custom-reuse-outlet.component.d.ts +37 -0
- package/boot/config/apps/src/custom-route-reuse-strategy.class.d.ts +156 -0
- package/boot/config/apps/src/nav-link-context-menu.service.d.ts +33 -0
- package/boot/config/apps/src/nav-link.directive.d.ts +29 -0
- package/boot/config/apps/src/nav.service.d.ts +198 -0
- package/boot/config/apps/src/route-close.service.d.ts +9 -0
- package/boot/config/apps/src/safe-navigate.d.ts +17 -0
- package/boot/config/apps/src/tab-aware-url-serializer.d.ts +22 -0
- package/boot/config/plugins/src/getNgZone.d.ts +9 -1
- package/boot/mfe/src/app.component.d.ts +15 -4
- package/boot/mfe/src/isolated-location-strategy.d.ts +57 -0
- package/esm2022/boot/bridge/src/mfe-bridge.mjs +36 -5
- package/esm2022/boot/config/api/src/api.service.mjs +12 -3
- package/esm2022/boot/config/apps/index.mjs +8 -2
- package/esm2022/boot/config/apps/src/apps.service.mjs +14 -6
- package/esm2022/boot/config/apps/src/custom-reuse-outlet.component.mjs +207 -0
- package/esm2022/boot/config/apps/src/custom-route-reuse-strategy.class.mjs +540 -0
- package/esm2022/boot/config/apps/src/nav-link-context-menu.service.mjs +105 -0
- package/esm2022/boot/config/apps/src/nav-link.directive.mjs +45 -0
- package/esm2022/boot/config/apps/src/nav.service.mjs +675 -0
- package/esm2022/boot/config/apps/src/route-close.service.mjs +19 -0
- package/esm2022/boot/config/apps/src/safe-navigate.mjs +50 -0
- package/esm2022/boot/config/apps/src/tab-aware-url-serializer.mjs +50 -0
- package/esm2022/boot/config/plugins/src/getNgZone.mjs +13 -5
- package/esm2022/boot/host/src/app.component.mjs +1 -2
- package/esm2022/boot/host/src/bootstrap.mjs +22 -7
- package/esm2022/boot/mfe/src/app.component.mjs +143 -39
- package/esm2022/boot/mfe/src/bootstrap.mjs +197 -20
- package/esm2022/boot/mfe/src/isolated-location-strategy.mjs +142 -0
- package/esm2022/keycloak/src/auth.interceptor.mjs +17 -2
- package/esm2022/provider/src/prodiveHost.mjs +3 -5
- package/esm2022/provider/src/prodiveHostRouter.mjs +88 -9
- package/esm2022/theme/layout/app/expandableMenu/expandable-menu.component.mjs +81 -19
- package/esm2022/theme/layout/app/favorite-pages/favorite-pages-sidebar.component.mjs +6 -4
- package/esm2022/theme/layout/app/general-search/general-search.component.mjs +4 -4
- package/esm2022/theme/layout/app/history/history-sidebar.component.mjs +6 -4
- package/esm2022/theme/layout/app/layout/app.layout.component.mjs +422 -20
- package/esm2022/theme/layout/app/layout/mfe.layout.component.mjs +24 -35
- package/esm2022/theme/layout/app/site-map/site-map-sidebar.component.mjs +6 -4
- package/esm2022/theme/layout/app/static-sidebar/static-sidebar.component.mjs +85 -27
- package/esm2022/theme/layout/app/topbar/app.topbar.component.mjs +3 -3
- package/esm2022/theme/layout/service/breadcrumb-publisher.service.mjs +86 -0
- package/esm2022/theme/layout/service/tab-session.service.mjs +126 -0
- package/esm2022/ui-business/ref-value/src/ref-value.component.mjs +15 -7
- package/esm2022/util/sync-active-tab-route/src/sync-active-tab-route.directive.mjs +29 -9
- package/fesm2022/aril-app.component-s14ruALV.mjs +183 -0
- package/fesm2022/aril-app.component-s14ruALV.mjs.map +1 -0
- package/fesm2022/aril-boot-bridge.mjs +35 -4
- package/fesm2022/aril-boot-bridge.mjs.map +1 -1
- package/fesm2022/aril-boot-config-api.mjs +11 -2
- package/fesm2022/aril-boot-config-api.mjs.map +1 -1
- package/fesm2022/aril-boot-config-apps.mjs +1678 -10
- package/fesm2022/aril-boot-config-apps.mjs.map +1 -1
- package/fesm2022/aril-boot-config-plugins.mjs +12 -4
- package/fesm2022/aril-boot-config-plugins.mjs.map +1 -1
- package/fesm2022/aril-boot-host.mjs +21 -7
- package/fesm2022/aril-boot-host.mjs.map +1 -1
- package/fesm2022/aril-boot-mfe-app.component-a34GeuUv.mjs +183 -0
- package/fesm2022/aril-boot-mfe-app.component-a34GeuUv.mjs.map +1 -0
- package/fesm2022/aril-boot-mfe-aril-boot-mfe-KFO_X7yR.mjs +631 -0
- package/fesm2022/aril-boot-mfe-aril-boot-mfe-KFO_X7yR.mjs.map +1 -0
- package/fesm2022/aril-boot-mfe.mjs +5 -3
- package/fesm2022/aril-boot-mfe.mjs.map +1 -1
- package/fesm2022/aril-keycloak.mjs +16 -1
- package/fesm2022/aril-keycloak.mjs.map +1 -1
- package/fesm2022/aril-provider.mjs +90 -12
- package/fesm2022/aril-provider.mjs.map +1 -1
- package/fesm2022/aril-theme-layout.mjs +2630 -2017
- package/fesm2022/aril-theme-layout.mjs.map +1 -1
- package/fesm2022/aril-ui-business-ref-value.mjs +14 -6
- package/fesm2022/aril-ui-business-ref-value.mjs.map +1 -1
- package/fesm2022/aril-util-sync-active-tab-route.mjs +28 -8
- package/fesm2022/aril-util-sync-active-tab-route.mjs.map +1 -1
- package/fesm2022/aril.mjs +354 -25
- package/fesm2022/aril.mjs.map +1 -1
- package/keycloak/src/auth.interceptor.d.ts +7 -0
- package/package.json +216 -216
- package/provider/src/prodiveHost.d.ts +1 -0
- package/theme/layout/app/expandableMenu/expandable-menu.component.d.ts +21 -4
- package/theme/layout/app/expandableMenu/expandable-menu.component.html +19 -5
- package/theme/layout/app/expandableMenu/expandable-menu.component.ts +69 -9
- package/theme/layout/app/favorite-pages/favorite-pages-sidebar.component.html +1 -0
- package/theme/layout/app/favorite-pages/favorite-pages-sidebar.component.ts +3 -1
- package/theme/layout/app/general-search/general-search.component.html +2 -1
- package/theme/layout/app/general-search/general-search.component.ts +2 -2
- package/theme/layout/app/history/history-sidebar.component.html +3 -1
- package/theme/layout/app/history/history-sidebar.component.ts +3 -1
- package/theme/layout/app/layout/app.layout.component.d.ts +105 -5
- package/theme/layout/app/layout/app.layout.component.html +102 -1
- package/theme/layout/app/layout/app.layout.component.scss +372 -0
- package/theme/layout/app/layout/app.layout.component.ts +452 -13
- package/theme/layout/app/layout/mfe.layout.component.d.ts +7 -5
- package/theme/layout/app/layout/mfe.layout.component.ts +13 -39
- package/theme/layout/app/site-map/site-map-sidebar.component.html +1 -0
- package/theme/layout/app/site-map/site-map-sidebar.component.ts +3 -1
- package/theme/layout/app/static-sidebar/static-sidebar.component.d.ts +26 -5
- package/theme/layout/app/static-sidebar/static-sidebar.component.html +11 -5
- package/theme/layout/app/static-sidebar/static-sidebar.component.ts +68 -13
- package/theme/layout/app/topbar/app.topbar.component.html +0 -1
- package/theme/layout/app/topbar/app.topbar.component.scss +1 -1
- package/theme/layout/service/breadcrumb-publisher.service.d.ts +24 -0
- package/theme/layout/service/breadcrumb-publisher.service.ts +95 -0
- package/theme/layout/service/tab-session.service.d.ts +52 -0
- package/theme/layout/service/tab-session.service.ts +138 -0
- package/theme/styles/layout/_breadcrumb.scss +95 -0
- package/theme/styles/layout/_content.scss +2 -2
- package/ui-business/ref-value/src/ref-value.component.d.ts +4 -2
- package/util/sync-active-tab-route/src/sync-active-tab-route.directive.d.ts +15 -2
- package/boot/config/apps/src/reuse-strategy.d.ts +0 -4
- package/esm2022/boot/config/apps/src/reuse-strategy.mjs +0 -9
- package/esm2022/theme/layout/app/breadcrumb/app.breadcrumb.component.mjs +0 -107
- package/fesm2022/aril-app.component-wxP3y8dg.mjs +0 -81
- package/fesm2022/aril-app.component-wxP3y8dg.mjs.map +0 -1
- package/fesm2022/aril-boot-mfe-app.component-7IjAmjz0.mjs +0 -80
- package/fesm2022/aril-boot-mfe-app.component-7IjAmjz0.mjs.map +0 -1
- package/fesm2022/aril-boot-mfe-aril-boot-mfe-KXDpUyv7.mjs +0 -315
- package/fesm2022/aril-boot-mfe-aril-boot-mfe-KXDpUyv7.mjs.map +0 -1
- package/theme/layout/app/breadcrumb/app.breadcrumb.component.d.ts +0 -25
- package/theme/layout/app/breadcrumb/app.breadcrumb.component.html +0 -8
- package/theme/layout/app/breadcrumb/app.breadcrumb.component.ts +0 -127
|
@@ -1,25 +1,49 @@
|
|
|
1
|
+
import { CdkDragDrop, DragDropModule, moveItemInArray } from '@angular/cdk/drag-drop';
|
|
1
2
|
import { NgClass } from '@angular/common';
|
|
2
|
-
import {
|
|
3
|
-
|
|
3
|
+
import {
|
|
4
|
+
AfterViewInit,
|
|
5
|
+
Component,
|
|
6
|
+
ElementRef,
|
|
7
|
+
HostListener,
|
|
8
|
+
OnDestroy,
|
|
9
|
+
Renderer2,
|
|
10
|
+
ViewChild,
|
|
11
|
+
computed,
|
|
12
|
+
effect,
|
|
13
|
+
inject,
|
|
14
|
+
signal,
|
|
15
|
+
untracked
|
|
16
|
+
} from '@angular/core';
|
|
17
|
+
import { NavigationEnd, Router, RouterLink, RouterOutlet } from '@angular/router';
|
|
4
18
|
|
|
19
|
+
import { MenuItem } from 'primeng/api';
|
|
20
|
+
import { BreadcrumbModule } from 'primeng/breadcrumb';
|
|
5
21
|
import { ConfirmDialogModule } from 'primeng/confirmdialog';
|
|
6
22
|
import { ConfirmPopupModule } from 'primeng/confirmpopup';
|
|
23
|
+
import { ContextMenu, ContextMenuModule } from 'primeng/contextmenu';
|
|
7
24
|
import { DialogModule } from 'primeng/dialog';
|
|
8
25
|
import { MessagesModule } from 'primeng/messages';
|
|
9
26
|
import { ToastModule } from 'primeng/toast';
|
|
27
|
+
import { TooltipModule } from 'primeng/tooltip';
|
|
10
28
|
|
|
29
|
+
import { TranslocoModule, TranslocoService } from '@ngneat/transloco';
|
|
11
30
|
import { Subscription, filter } from 'rxjs';
|
|
12
31
|
|
|
32
|
+
import { bridge } from 'aril/boot/bridge';
|
|
33
|
+
import { NavItem, NavLinkContextMenuService, NavLinkDirective, NavService, PluginMenuItem } from 'aril/boot/config/apps';
|
|
34
|
+
import { TranslateJsonPipe } from 'aril/util/pipes';
|
|
35
|
+
|
|
13
36
|
import { LayoutService } from '../../service/app.layout.service';
|
|
14
37
|
import { AppMenuService } from '../../service/app.menu.service';
|
|
15
|
-
import {
|
|
38
|
+
import { TabSessionService } from '../../service/tab-session.service';
|
|
39
|
+
import { ExpandableMenuComponent } from '../expandableMenu/expandable-menu.component';
|
|
40
|
+
import { FavoritePagesSidebarComponent } from '../favorite-pages/favorite-pages-sidebar.component';
|
|
16
41
|
import { HistorySidebarComponent } from '../history/history-sidebar.component';
|
|
42
|
+
import { NotificationsSidebarComponent } from '../notifications/notifications-sidebar.component';
|
|
43
|
+
import { AppProfileSidebarComponent } from '../profileSidebar/app.profilesidebar.component';
|
|
17
44
|
import { SiteMapSidebarComponent } from '../site-map/site-map-sidebar.component';
|
|
18
|
-
import { FavoritePagesSidebarComponent } from '../favorite-pages/favorite-pages-sidebar.component';
|
|
19
|
-
import { AppTopbarComponent } from '../topbar/app.topbar.component';
|
|
20
|
-
import { ExpandableMenuComponent } from '../expandableMenu/expandable-menu.component';
|
|
21
45
|
import { StaticSidebarComponent } from '../static-sidebar/static-sidebar.component';
|
|
22
|
-
import {
|
|
46
|
+
import { AppTopbarComponent } from '../topbar/app.topbar.component';
|
|
23
47
|
|
|
24
48
|
@Component({
|
|
25
49
|
standalone: true,
|
|
@@ -27,6 +51,7 @@ import { NotificationsSidebarComponent } from '../notifications/notifications-si
|
|
|
27
51
|
imports: [
|
|
28
52
|
NgClass,
|
|
29
53
|
RouterOutlet,
|
|
54
|
+
RouterLink,
|
|
30
55
|
ConfirmDialogModule,
|
|
31
56
|
ConfirmPopupModule,
|
|
32
57
|
DialogModule,
|
|
@@ -39,11 +64,19 @@ import { NotificationsSidebarComponent } from '../notifications/notifications-si
|
|
|
39
64
|
FavoritePagesSidebarComponent,
|
|
40
65
|
ExpandableMenuComponent,
|
|
41
66
|
StaticSidebarComponent,
|
|
42
|
-
NotificationsSidebarComponent
|
|
67
|
+
NotificationsSidebarComponent,
|
|
68
|
+
DragDropModule,
|
|
69
|
+
BreadcrumbModule,
|
|
70
|
+
ContextMenuModule,
|
|
71
|
+
TooltipModule,
|
|
72
|
+
TranslocoModule,
|
|
73
|
+
NavLinkDirective
|
|
43
74
|
],
|
|
44
|
-
templateUrl: './app.layout.component.html'
|
|
75
|
+
templateUrl: './app.layout.component.html',
|
|
76
|
+
styleUrls: ['./app.layout.component.scss'],
|
|
77
|
+
providers: [TranslateJsonPipe]
|
|
45
78
|
})
|
|
46
|
-
export class AppLayoutComponent implements OnDestroy {
|
|
79
|
+
export class AppLayoutComponent implements OnDestroy, AfterViewInit {
|
|
47
80
|
overlayMenuOpenSubscription: Subscription;
|
|
48
81
|
menuOutsideClickListener: any;
|
|
49
82
|
menuScrollListener: any;
|
|
@@ -51,13 +84,387 @@ export class AppLayoutComponent implements OnDestroy {
|
|
|
51
84
|
@ViewChild(ExpandableMenuComponent) expandableMenuComponent!: ExpandableMenuComponent;
|
|
52
85
|
@ViewChild(StaticSidebarComponent) staticSidebarComponent!: StaticSidebarComponent;
|
|
53
86
|
@ViewChild(AppTopbarComponent) appTopbar!: AppTopbarComponent;
|
|
87
|
+
@ViewChild('tabsContainer', { static: false }) tabsContainer?: ElementRef<HTMLDivElement>;
|
|
88
|
+
@ViewChild('tabContextMenu') tabContextMenu?: ContextMenu;
|
|
89
|
+
@ViewChild('navLinkContextMenu') navLinkContextMenu?: ContextMenu;
|
|
90
|
+
navService: NavService = inject(NavService);
|
|
91
|
+
private readonly translocoService: TranslocoService = inject(TranslocoService);
|
|
92
|
+
private readonly navLinkContextMenuService: NavLinkContextMenuService = inject(NavLinkContextMenuService);
|
|
93
|
+
private readonly tabSession: TabSessionService = inject(TabSessionService);
|
|
94
|
+
|
|
95
|
+
tabContextMenuItems: MenuItem[] = [];
|
|
96
|
+
/** Tab bar yatay scroll konum/genişlik durumu. Chevron butonlarının görünürlüğünü kontrol eder. */
|
|
97
|
+
canScrollLeft = signal(false);
|
|
98
|
+
canScrollRight = signal(false);
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Breadcrumb segmentleri — `bridge.breadcrumbs()` üzerinden okunur.
|
|
103
|
+
* Source-of-truth aktif MFE'de çalışan `BreadcrumbPublisherService`'tir;
|
|
104
|
+
* MFE her NavigationEnd'de kendi route ağacından zinciri çıkarıp bridge'e yazar.
|
|
105
|
+
* Shell yalnızca presenter — başa "Anasayfa" item'ı ekleyip son item'ı aktif işaretler.
|
|
106
|
+
*/
|
|
107
|
+
breadcrumbItems = computed<(MenuItem & { active?: boolean; isHome?: boolean })[]>(() => {
|
|
108
|
+
const crumbs = bridge.breadcrumbs();
|
|
109
|
+
const homeItem: MenuItem & { isHome: boolean } = {
|
|
110
|
+
icon: 'pi pi-home',
|
|
111
|
+
isHome: true
|
|
112
|
+
};
|
|
113
|
+
if (!crumbs.length) {
|
|
114
|
+
return [homeItem];
|
|
115
|
+
}
|
|
116
|
+
return [
|
|
117
|
+
homeItem,
|
|
118
|
+
...crumbs.map((c, idx) => {
|
|
119
|
+
const isLast = idx === crumbs.length - 1;
|
|
120
|
+
return {
|
|
121
|
+
label: c.label,
|
|
122
|
+
// Aktif (son) item'da url verme — PrimeNG dış `.p-menuitem-link`'e href eklemesin,
|
|
123
|
+
// kullanıcı zaten o sayfada, tekrar tıklanmasının anlamı yok.
|
|
124
|
+
url: isLast ? undefined : c.url,
|
|
125
|
+
active: isLast
|
|
126
|
+
};
|
|
127
|
+
})
|
|
128
|
+
];
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Aktif olmayan breadcrumb segmentine tıklama → mevcut tab'ı parent route ile günceller.
|
|
133
|
+
* `<a [routerLink]>` directive'i `href`'i Router'ın location strategy'sine göre
|
|
134
|
+
* (hash/path) doğru formatlar — sağ tık "linki kopyala" davranışı çalışır.
|
|
135
|
+
* Default Router navigate'ini `preventDefault()` ile durdurup `navigateInCurrentTab`
|
|
136
|
+
* üzerinden ilerletiyoruz; böylece yeni sekme açılmaz.
|
|
137
|
+
*/
|
|
138
|
+
onBreadcrumbClick(event: MouseEvent, item: { label?: string; url?: string }): void {
|
|
139
|
+
event.preventDefault();
|
|
140
|
+
if (!item.url) return;
|
|
141
|
+
const navLink = item.url.startsWith('/') ? item.url.slice(1) : item.url;
|
|
142
|
+
this.navService.navigateInCurrentTab({
|
|
143
|
+
tabId: '',
|
|
144
|
+
navLink,
|
|
145
|
+
navName: item.label ?? ''
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Home icon → root menu item (`apps.service.ts:51`'de tanımlı `{ root: true, routerLink: '/' }`).
|
|
151
|
+
* Tek source-of-truth — etiket çevirisi `TranslateJsonPipe` ile aktif dilden okunur.
|
|
152
|
+
* Mevcut tab güncellenir, yeni sekme açılmaz.
|
|
153
|
+
*/
|
|
154
|
+
onHomeClick(event: MouseEvent): void {
|
|
155
|
+
event.preventDefault();
|
|
156
|
+
this.navService.navigateInCurrentTab(this.homeNavItem());
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Anasayfa için NavItem — breadcrumb home ikonunun hem `(click)` handler'ı hem
|
|
161
|
+
* `[arilNavLink]` context menu directive'i tarafından paylaşılır.
|
|
162
|
+
*/
|
|
163
|
+
readonly homeNavItem = computed<NavItem>(() => {
|
|
164
|
+
// NavService ile AYNI menü kaynağı (`bridge.hostMenuItems`) — `menuService.menuItems()`
|
|
165
|
+
// host `root` öğesini içermiyor (farklı/boş kaynak); `setMenuItemsProvider` de bridge'i kullanıyor.
|
|
166
|
+
const items = bridge.hostMenuItems();
|
|
167
|
+
const rootItem = items.find((i: PluginMenuItem) => i.root);
|
|
168
|
+
// Pipe runtime'da object kabul ediyor (translate-json.pipe.ts:18) ama tip imzası `string` —
|
|
169
|
+
// static-sidebar'ın `getLocalText(any)` pattern'iyle aynı şekilde cast.
|
|
170
|
+
const navName = rootItem ? this.translateJsonPipe.transform(rootItem.label as any) : '';
|
|
171
|
+
return {
|
|
172
|
+
tabId: '',
|
|
173
|
+
navLink: rootItem?.routerLink || '/',
|
|
174
|
+
navName,
|
|
175
|
+
icon: rootItem?.icon
|
|
176
|
+
};
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Tab icon resolution sırası:
|
|
181
|
+
* 1. NavItem.icon (sidebar click'inden iletilmişse — en doğru)
|
|
182
|
+
* 2. Menu config'inde navLink'i en uzun prefix olarak match eden item.icon
|
|
183
|
+
* 3. Generic fallback (`pi pi-file`)
|
|
184
|
+
*
|
|
185
|
+
* Best-match için longest-prefix kullanılır: `wdm/meters/123` için hem `wdm` hem
|
|
186
|
+
* `wdm/meters` match'lerse, daha spesifik `wdm/meters` kazanır. Böylece deep link
|
|
187
|
+
* refresh'inde (NavService.firstCheckForRoute path'inde icon set edilemediği zaman)
|
|
188
|
+
* doğru remote/section ikonu otomatik bulunur.
|
|
189
|
+
*/
|
|
190
|
+
getTabIcon(item: NavItem): string {
|
|
191
|
+
if (item.icon) return item.icon;
|
|
192
|
+
if (!item.navLink) return 'pi pi-file';
|
|
193
|
+
// `bridge.hostMenuItems()` — homeNavItem/NavService ile aynı menü kaynağı. `menuService`
|
|
194
|
+
// host `root`/MFE menüsünü içermediği için (boş kaynak) buradaki ikon eşleşmesi de oradan.
|
|
195
|
+
return this.findIconForRoute(bridge.hostMenuItems(), item.navLink) ?? 'pi pi-file';
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private findIconForRoute(items: PluginMenuItem[], navLink: string): string | undefined {
|
|
199
|
+
// Path leading slash içerebilir (Router URL), menu config routerLink'leri genelde
|
|
200
|
+
// leading slash'sız — ikisini normalize edip karşılaştır.
|
|
201
|
+
const normalize = (p: string): string => (p.startsWith('/') ? p.slice(1) : p);
|
|
202
|
+
const normalizedPath = normalize(navLink);
|
|
203
|
+
let best: { length: number; icon: string } | undefined;
|
|
204
|
+
const visit = (list: PluginMenuItem[]): void => {
|
|
205
|
+
for (const it of list) {
|
|
206
|
+
if (it.routerLink && it.icon) {
|
|
207
|
+
const r = normalize(it.routerLink);
|
|
208
|
+
if (r && (normalizedPath === r || normalizedPath.startsWith(r + '/'))) {
|
|
209
|
+
const length = r.length;
|
|
210
|
+
if (!best || length > best.length) best = { length, icon: it.icon };
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (it.items?.length) visit(it.items);
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
visit(items);
|
|
217
|
+
return best?.icon;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
drop(event: CdkDragDrop<any[]>) {
|
|
221
|
+
const currentRoutes = [...this.navService.activeRoutes()];
|
|
222
|
+
moveItemInArray(currentRoutes, event.previousIndex, event.currentIndex);
|
|
223
|
+
this.navService.reorderActiveRoutes(currentRoutes);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
isActive(item: NavItem): boolean {
|
|
227
|
+
// Aynı `navLink`'in birden fazla tab'i olabileceği için tabId tek doğru kimlik.
|
|
228
|
+
// `activeTabId` history.state üzerinden NavService tarafından senkronize edilir.
|
|
229
|
+
return this.navService.activeTabId() === item.tabId;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
closeTab(event: Event, item: NavItem) {
|
|
233
|
+
event.stopPropagation();
|
|
234
|
+
if (item.pinned) return;
|
|
235
|
+
this.navService.closeNavigation(item);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** Tab sol-click → o tab'a geç. NavService Router'a `?_tab` URL'i ve history state'i verir. */
|
|
239
|
+
onTabClick(event: MouseEvent, item: NavItem): void {
|
|
240
|
+
if (event.button !== 0) return; // sadece sol tık
|
|
241
|
+
this.navService.navigateToTab(item);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** Middle click (button 1) → tab kapat (Chrome standardı). Sabit tab'lar kapatılmaz. */
|
|
245
|
+
onTabAuxClick(event: MouseEvent, item: NavItem): void {
|
|
246
|
+
if (event.button !== 1) return;
|
|
247
|
+
event.preventDefault();
|
|
248
|
+
if (item.pinned) return;
|
|
249
|
+
this.navService.closeNavigation(item);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** Middle click default scroll davranışını engelle. */
|
|
253
|
+
onTabMousedown(event: MouseEvent): void {
|
|
254
|
+
if (event.button === 1) event.preventDefault();
|
|
255
|
+
}
|
|
54
256
|
|
|
257
|
+
/**
|
|
258
|
+
* Tab right-click → context menu. Sabit tab'larda "Kapat" disabled; pin/unpin item'ı
|
|
259
|
+
* Chrome'daki gibi en üstte yer alır. `closeOthers`/`closeToRight`/`closeAll` zaten
|
|
260
|
+
* NavService seviyesinde pinned'i koruyor — UI'da ekstra disabled gerekmez.
|
|
261
|
+
*/
|
|
262
|
+
onTabContextMenu(event: MouseEvent, item: NavItem): void {
|
|
263
|
+
event.preventDefault();
|
|
264
|
+
const t = (key: string): string => this.translocoService.translate(key);
|
|
265
|
+
const pinLabel = item.pinned ? t('tab.unpin') : t('tab.pin');
|
|
266
|
+
this.tabContextMenuItems = [
|
|
267
|
+
{ label: pinLabel, icon: 'pi pi-thumbtack', command: () => this.navService.togglePin(item) },
|
|
268
|
+
{ label: t('tab.duplicate'), icon: 'pi pi-clone', command: () => this.navService.duplicateTab(item) },
|
|
269
|
+
{ separator: true },
|
|
270
|
+
{
|
|
271
|
+
label: t('tab.close'),
|
|
272
|
+
icon: 'pi pi-times',
|
|
273
|
+
command: () => this.navService.closeNavigation(item),
|
|
274
|
+
disabled: !!item.pinned
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
label: t('tab.closeOthers'),
|
|
278
|
+
icon: 'pi pi-times-circle',
|
|
279
|
+
command: () => this.navService.closeOthers(item),
|
|
280
|
+
disabled: this.navService.activeRoutes().filter((r) => !r.pinned && r.tabId !== item.tabId).length === 0
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
label: t('tab.closeToRight'),
|
|
284
|
+
icon: 'pi pi-angle-double-right',
|
|
285
|
+
command: () => this.navService.closeToRight(item),
|
|
286
|
+
disabled: this.isLastTab(item)
|
|
287
|
+
},
|
|
288
|
+
{ separator: true },
|
|
289
|
+
{
|
|
290
|
+
label: t('tab.closeAll'),
|
|
291
|
+
icon: 'pi pi-trash',
|
|
292
|
+
command: () => this.navService.closeAll(),
|
|
293
|
+
disabled: this.navService.activeRoutes().every((r) => r.pinned)
|
|
294
|
+
}
|
|
295
|
+
];
|
|
296
|
+
this.tabContextMenu?.show(event);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
private isLastTab(item: NavItem): boolean {
|
|
300
|
+
const routes = this.navService.activeRoutes();
|
|
301
|
+
// `tabId` ile karşılaştır — aynı navLink'in birden fazla tab'i olabileceği için
|
|
302
|
+
// `navLink` ambiguous olur, kimlik bazlı arama tek doğru yöntem.
|
|
303
|
+
return routes[routes.length - 1]?.tabId === item.tabId;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Tek kalan tab anasayfaysa true → kapatma (✕) butonu gizlenir (NavService de kapatmayı
|
|
308
|
+
* reddeder). "Her zaman en az 1 tab açık" kuralının UI tarafı.
|
|
309
|
+
*/
|
|
310
|
+
isLastHomeTab(item: NavItem): boolean {
|
|
311
|
+
const routes = this.navService.activeRoutes();
|
|
312
|
+
return routes.length === 1 && item.navLink === (this.homeNavItem().navLink || '/');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/** "+" butonu → ana sayfaya navigate (yeni tab varsa onu aç, yoksa eklenir). */
|
|
316
|
+
onAddTab(): void {
|
|
317
|
+
this.router.navigateByUrl('');
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
scrollTabsLeft(): void {
|
|
321
|
+
this.tabsContainer?.nativeElement.scrollBy({ left: -200, behavior: 'smooth' });
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
scrollTabsRight(): void {
|
|
325
|
+
this.tabsContainer?.nativeElement.scrollBy({ left: 200, behavior: 'smooth' });
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
private updateScrollState(): void {
|
|
329
|
+
const el = this.tabsContainer?.nativeElement;
|
|
330
|
+
if (!el) return;
|
|
331
|
+
this.canScrollLeft.set(el.scrollLeft > 0);
|
|
332
|
+
this.canScrollRight.set(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Klavye kısayolları (Chrome paritesi):
|
|
337
|
+
* - `Alt+W` → aktif tab kapat (Ctrl+W browser-reserved)
|
|
338
|
+
* - `Ctrl+Tab` / `Ctrl+Shift+Tab` → sonraki/önceki tab (denemeli — bazı tarayıcılar override etmez)
|
|
339
|
+
* - `Ctrl+PageDown` / `Ctrl+PageUp` → sonraki/önceki tab (yedek)
|
|
340
|
+
* - `Alt+1..9` → ilgili index'teki tab (Ctrl+1..9 browser-reserved)
|
|
341
|
+
* - `Ctrl+Shift+T` veya `Alt+Shift+T` → son kapatılan tab'ı geri aç
|
|
342
|
+
*/
|
|
343
|
+
@HostListener('document:keydown', ['$event'])
|
|
344
|
+
onDocumentKeydown(event: KeyboardEvent): void {
|
|
345
|
+
if (this.isTypingInInput(event)) return;
|
|
346
|
+
const key = event.key;
|
|
347
|
+
const lower = key.toLowerCase();
|
|
348
|
+
|
|
349
|
+
if (event.altKey && !event.shiftKey && !event.ctrlKey && lower === 'w') {
|
|
350
|
+
const current = this.navService.activeRoute();
|
|
351
|
+
const item = current ? this.navService.activeRoutes().find((r) => r.navLink === current) : undefined;
|
|
352
|
+
if (item) {
|
|
353
|
+
event.preventDefault();
|
|
354
|
+
this.navService.closeNavigation(item);
|
|
355
|
+
}
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (event.ctrlKey && (key === 'Tab' || key === 'PageDown' || key === 'PageUp')) {
|
|
360
|
+
event.preventDefault();
|
|
361
|
+
if (key === 'PageUp' || (key === 'Tab' && event.shiftKey)) {
|
|
362
|
+
this.navService.goToPrevTab();
|
|
363
|
+
} else {
|
|
364
|
+
this.navService.goToNextTab();
|
|
365
|
+
}
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (event.altKey && !event.ctrlKey && !event.shiftKey && /^[1-9]$/.test(key)) {
|
|
370
|
+
event.preventDefault();
|
|
371
|
+
this.navService.goToTabByIndex(parseInt(key, 10) - 1);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
private isTypingInInput(event: KeyboardEvent): boolean {
|
|
377
|
+
const target = event.target as HTMLElement | null;
|
|
378
|
+
if (!target) return false;
|
|
379
|
+
const tag = target.tagName?.toLowerCase();
|
|
380
|
+
return tag === 'input' || tag === 'textarea' || tag === 'select' || !!target.isContentEditable;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
ngAfterViewInit(): void {
|
|
384
|
+
this.updateScrollState();
|
|
385
|
+
const el = this.tabsContainer?.nativeElement;
|
|
386
|
+
if (el) {
|
|
387
|
+
el.addEventListener('scroll', () => this.updateScrollState(), { passive: true });
|
|
388
|
+
if (typeof ResizeObserver !== 'undefined') {
|
|
389
|
+
const ro = new ResizeObserver(() => this.updateScrollState());
|
|
390
|
+
ro.observe(el);
|
|
391
|
+
this.resizeObserver = ro;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
// Global nav-link context menu'yu service'e tanıt — tüm `[arilNavLink]` directive'leri
|
|
395
|
+
// bu instance'ı paylaşır. ngAfterViewInit'te yapılır çünkü ViewChild burada kesin hazırdır.
|
|
396
|
+
if (this.navLinkContextMenu) {
|
|
397
|
+
this.navLinkContextMenuService.register(this.navLinkContextMenu);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
private resizeObserver: ResizeObserver | null = null;
|
|
55
402
|
constructor(
|
|
56
|
-
private menuService: AppMenuService,
|
|
403
|
+
private readonly menuService: AppMenuService,
|
|
57
404
|
public layoutService: LayoutService,
|
|
58
405
|
public renderer: Renderer2,
|
|
59
|
-
public router: Router
|
|
406
|
+
public router: Router,
|
|
407
|
+
private readonly translateJsonPipe: TranslateJsonPipe
|
|
60
408
|
) {
|
|
409
|
+
// NavService bridge'i doğrudan bilmez (cyclic dep önlemi); host shell `bridge.hostMenuItems()`
|
|
410
|
+
// üzerinden provider bağlar. Refresh sonrası adres çubuğuna yapıştırılmış URL'in tab adı/ikonu
|
|
411
|
+
// menüdeki gerçek değerlere göre çözülsün diye.
|
|
412
|
+
this.navService.setMenuItemsProvider(() => bridge.hostMenuItems());
|
|
413
|
+
this.navService.setHomeNavItemProvider(() => this.homeNavItem());
|
|
414
|
+
|
|
415
|
+
// Pinned tab'ları localStorage'dan geri yükle. Provider'lar set edildikten SONRA
|
|
416
|
+
// (restore edilen tab'lar reconcileTabsWithMenu ile menüden revize edilebilsin),
|
|
417
|
+
// ilk NavigationEnd handler'ından ÖNCE (senkron) — knownTab guard restore'u görsün.
|
|
418
|
+
this.tabSession.restore();
|
|
419
|
+
|
|
420
|
+
// Menü (bridge.hostMenuItems) async yüklenir; ilk açılış/refresh path'inde home tab
|
|
421
|
+
// fallback isim/iconla eklenmiş olabilir. Menü signal'i değişince tab'ları menüyle revize
|
|
422
|
+
// et → anasayfa adı/ikonu gerçek `root` öğesinden gelir, `tab.home` fallback'inde kalmaz.
|
|
423
|
+
effect(
|
|
424
|
+
() => {
|
|
425
|
+
bridge.hostMenuItems();
|
|
426
|
+
// `untracked`: reconcileTabsWithMenu içinde `activeRoutes()` okunuyor; reactive
|
|
427
|
+
// context'te kalırsa effect activeRoutes'a da abone olur → her tab mutasyonunda
|
|
428
|
+
// (aç/kapat/sürükle/pin) gereksiz menü-DFS çalışır. Yalnız hostMenuItems'a bağlı kalsın.
|
|
429
|
+
untracked(() => this.navService.reconcileTabsWithMenu());
|
|
430
|
+
},
|
|
431
|
+
{ allowSignalWrites: true }
|
|
432
|
+
);
|
|
433
|
+
|
|
434
|
+
// NavService.activeTabId → bridge.activeTabId mirror. Plugin'lerin Router proxy'leri
|
|
435
|
+
// (provideHostRouter) bu signal'ı okuyup navigate çağrılarına _tab state enjekte ediyor.
|
|
436
|
+
effect(
|
|
437
|
+
() => {
|
|
438
|
+
bridge.activeTabId.set(this.navService.activeTabId());
|
|
439
|
+
},
|
|
440
|
+
{ allowSignalWrites: true }
|
|
441
|
+
);
|
|
442
|
+
|
|
443
|
+
// MFE içi sayfa navigation'ları host Router'a yansımıyor (IsolatedLocationStrategy
|
|
444
|
+
// browser hash'ı değiştirmiyor) → NavService tab navLink'i güncel kalmıyordu. MFE
|
|
445
|
+
// AppComponent `bridge.activeMFEUrl`'a (kaynak tabId ile) yazıyor; bu effect ile
|
|
446
|
+
// dinleyip `syncActiveTabNavLinkForTab(tabId, url)` ile SADECE kaynak tab'ı update
|
|
447
|
+
// ederiz. Tab değiştirip geri dönüldüğünde doğru URL'e gidilir, cross-tab yok.
|
|
448
|
+
//
|
|
449
|
+
// **`untracked`**: `syncActiveTabNavLinkForTab` içinde `activeRoutes()` okunur;
|
|
450
|
+
// reactive context'te kalırsa effect activeRoutes'a da abone olur → tab.navLink
|
|
451
|
+
// değişikliği effect'i tekrar tetikler → eski `activeMFEUrl` değeriyle navLink'i
|
|
452
|
+
// geri çevirir (kullanıcının anasayfaya geçişi sürekli detail/meters'a dönerdi).
|
|
453
|
+
// Untracked sarması bu recursive loop'u kırar.
|
|
454
|
+
// `allowSignalWrites`: `activeRoutes.update` (signal write), effect içinden default
|
|
455
|
+
// `NG0600` yasaklı — bilinçli izin.
|
|
456
|
+
effect(
|
|
457
|
+
() => {
|
|
458
|
+
const data = bridge.activeMFEUrl();
|
|
459
|
+
if (data?.tabId && data?.url) {
|
|
460
|
+
untracked(() =>
|
|
461
|
+
this.navService.syncActiveTabNavLinkForTab(data.tabId, data.url, data.title)
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
},
|
|
465
|
+
{ allowSignalWrites: true }
|
|
466
|
+
);
|
|
467
|
+
|
|
61
468
|
this.overlayMenuOpenSubscription = this.layoutService.overlayOpen$.subscribe(() => {
|
|
62
469
|
if (!this.menuOutsideClickListener) {
|
|
63
470
|
this.menuOutsideClickListener = this.renderer.listen('document', 'click', (event) => {
|
|
@@ -78,9 +485,39 @@ export class AppLayoutComponent implements OnDestroy {
|
|
|
78
485
|
}
|
|
79
486
|
});
|
|
80
487
|
|
|
81
|
-
this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe(() => {
|
|
488
|
+
this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe((event) => {
|
|
82
489
|
if (!this.layoutService.mfeAppItemClicked()) this.hideMenu();
|
|
490
|
+
// Shell root route'a geçince (örn. `/`) MFE Router tetiklenmediği için
|
|
491
|
+
// `BreadcrumbPublisherService` set yapmaz, eski breadcrumb kalır. Manuel temizle.
|
|
492
|
+
// URL `?_tab=...` içerebileceği için query string'i sıyırıp path karşılaştır.
|
|
493
|
+
const url = (event as NavigationEnd).urlAfterRedirects;
|
|
494
|
+
const path = url.split('?')[0];
|
|
495
|
+
if (path === '/' || path === '') {
|
|
496
|
+
bridge.breadcrumbs.set([]);
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
// Tab listesi değiştiğinde scroll state'ini yeniden hesapla — DOM güncellemesinden sonra
|
|
501
|
+
// `queueMicrotask` ile bir sonraki frame'i bekliyoruz (yeni eklenen tab clientWidth/scrollWidth'i etkiler).
|
|
502
|
+
effect(() => {
|
|
503
|
+
this.navService.activeRoutes();
|
|
504
|
+
queueMicrotask(() => this.updateScrollState());
|
|
83
505
|
});
|
|
506
|
+
|
|
507
|
+
// Tab geçişinde breadcrumb'ı geçici olarak boşalt — yeni aktif MFE'nin
|
|
508
|
+
// `BreadcrumbPublisherService`'i yayın yapana kadar eski tab'ın breadcrumb'ı
|
|
509
|
+
// kullanıcıya görünmesin (kısa süreli stale UI'yi engeller).
|
|
510
|
+
let prevTabId: string | undefined;
|
|
511
|
+
effect(
|
|
512
|
+
() => {
|
|
513
|
+
const tabId = this.navService.activeTabId();
|
|
514
|
+
if (prevTabId !== undefined && prevTabId !== tabId) {
|
|
515
|
+
bridge.breadcrumbs.set([]);
|
|
516
|
+
}
|
|
517
|
+
prevTabId = tabId;
|
|
518
|
+
},
|
|
519
|
+
{ allowSignalWrites: true }
|
|
520
|
+
);
|
|
84
521
|
}
|
|
85
522
|
|
|
86
523
|
blockBodyScroll(): void {
|
|
@@ -165,5 +602,7 @@ export class AppLayoutComponent implements OnDestroy {
|
|
|
165
602
|
if (this.menuOutsideClickListener) {
|
|
166
603
|
this.menuOutsideClickListener();
|
|
167
604
|
}
|
|
605
|
+
|
|
606
|
+
this.resizeObserver?.disconnect();
|
|
168
607
|
}
|
|
169
608
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { Signal } from '@angular/core';
|
|
2
|
-
import { Router } from '@angular/router';
|
|
3
1
|
import * as i0 from "@angular/core";
|
|
4
2
|
export declare class MFELayoutComponent {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Side-effect injection — `BreadcrumbPublisherService` constructor'da Router
|
|
5
|
+
* subscription'ı kurar. Named field ki TS `noUnusedLocals` lint kuralı tetiklenmesin
|
|
6
|
+
* (underscore prefix exempt). Field'ı doğrudan kullanmıyoruz, sadece DI'nın service'i
|
|
7
|
+
* instantiate etmesini sağlıyor.
|
|
8
|
+
*/
|
|
9
|
+
private readonly _breadcrumbPublisher;
|
|
8
10
|
static ɵfac: i0.ɵɵFactoryDeclaration<MFELayoutComponent, never>;
|
|
9
11
|
static ɵcmp: i0.ɵɵComponentDeclaration<MFELayoutComponent, "mfe-layout", never, {}, {}, never, never, true, never>;
|
|
10
12
|
}
|
|
@@ -1,13 +1,5 @@
|
|
|
1
|
-
import { Component,
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
NavigationCancel,
|
|
5
|
-
NavigationEnd,
|
|
6
|
-
NavigationError,
|
|
7
|
-
NavigationSkipped,
|
|
8
|
-
Router,
|
|
9
|
-
RouterOutlet
|
|
10
|
-
} from '@angular/router';
|
|
1
|
+
import { Component, inject } from '@angular/core';
|
|
2
|
+
import { RouterOutlet } from '@angular/router';
|
|
11
3
|
|
|
12
4
|
import { ConfirmDialogModule } from 'primeng/confirmdialog';
|
|
13
5
|
import { ConfirmPopupModule } from 'primeng/confirmpopup';
|
|
@@ -15,17 +7,13 @@ import { DialogModule } from 'primeng/dialog';
|
|
|
15
7
|
import { MessagesModule } from 'primeng/messages';
|
|
16
8
|
import { ToastModule } from 'primeng/toast';
|
|
17
9
|
|
|
18
|
-
import {
|
|
19
|
-
import { AppBreadcrumbComponent } from '../breadcrumb/app.breadcrumb.component';
|
|
10
|
+
import { BreadcrumbPublisherService } from '../../service/breadcrumb-publisher.service';
|
|
20
11
|
|
|
21
12
|
@Component({
|
|
22
13
|
standalone: true,
|
|
23
14
|
selector: 'mfe-layout',
|
|
24
15
|
template: `
|
|
25
|
-
|
|
26
|
-
@if (!loading()) {
|
|
27
|
-
<router-outlet />
|
|
28
|
-
}
|
|
16
|
+
<router-outlet />
|
|
29
17
|
|
|
30
18
|
<p-toast key="toast-root"></p-toast>
|
|
31
19
|
<p-dialog key="dialog-root"></p-dialog>
|
|
@@ -33,28 +21,14 @@ import { AppBreadcrumbComponent } from '../breadcrumb/app.breadcrumb.component';
|
|
|
33
21
|
<p-confirmPopup key="confirmPopup-root"></p-confirmPopup>
|
|
34
22
|
<p-confirmDialog key="confirmDialog-root"></p-confirmDialog>
|
|
35
23
|
`,
|
|
36
|
-
imports: [RouterOutlet, ConfirmDialogModule, ConfirmPopupModule, DialogModule, MessagesModule, ToastModule
|
|
24
|
+
imports: [RouterOutlet, ConfirmDialogModule, ConfirmPopupModule, DialogModule, MessagesModule, ToastModule]
|
|
37
25
|
})
|
|
38
26
|
export class MFELayoutComponent {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
event instanceof NavigationCancel ||
|
|
48
|
-
event instanceof NavigationError ||
|
|
49
|
-
event instanceof NavigationSkipped
|
|
50
|
-
)
|
|
51
|
-
return false;
|
|
52
|
-
|
|
53
|
-
return true;
|
|
54
|
-
})
|
|
55
|
-
),
|
|
56
|
-
{ initialValue: true }
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
}
|
|
27
|
+
/**
|
|
28
|
+
* Side-effect injection — `BreadcrumbPublisherService` constructor'da Router
|
|
29
|
+
* subscription'ı kurar. Named field ki TS `noUnusedLocals` lint kuralı tetiklenmesin
|
|
30
|
+
* (underscore prefix exempt). Field'ı doğrudan kullanmıyoruz, sadece DI'nın service'i
|
|
31
|
+
* instantiate etmesini sağlıyor.
|
|
32
|
+
*/
|
|
33
|
+
private readonly _breadcrumbPublisher = inject(BreadcrumbPublisherService);
|
|
34
|
+
}
|
|
@@ -89,6 +89,7 @@
|
|
|
89
89
|
[class.has-children]="node.children && node.children.length > 0"
|
|
90
90
|
[class.is-page]="node.routerLink"
|
|
91
91
|
[routerLink]="node.routerLink"
|
|
92
|
+
[arilNavLink]="node.routerLink ? { tabId: '', navLink: node.routerLink, navName: (node.label | translateJson), icon: node.icon } : null"
|
|
92
93
|
(click)="!node.routerLink ? toggleNode(node) : (visible = false)">
|
|
93
94
|
<div class="node-content">
|
|
94
95
|
@if (node.children && node.children.length > 0) {
|
|
@@ -10,6 +10,7 @@ import { TooltipModule } from 'primeng/tooltip';
|
|
|
10
10
|
import { TranslocoModule, TranslocoService } from '@ngneat/transloco';
|
|
11
11
|
|
|
12
12
|
import { bridge } from 'aril/boot/bridge';
|
|
13
|
+
import { NavLinkDirective } from 'aril/boot/config/apps';
|
|
13
14
|
import { ButtonComponent } from 'aril/ui/button';
|
|
14
15
|
import { TextComponent } from 'aril/ui/text';
|
|
15
16
|
import { TranslateJsonPipe } from 'aril/util/pipes';
|
|
@@ -41,7 +42,8 @@ interface MenuNode {
|
|
|
41
42
|
TextComponent,
|
|
42
43
|
TranslocoModule,
|
|
43
44
|
TranslateJsonPipe,
|
|
44
|
-
FontAwesomeModule
|
|
45
|
+
FontAwesomeModule,
|
|
46
|
+
NavLinkDirective
|
|
45
47
|
],
|
|
46
48
|
templateUrl: './site-map-sidebar.component.html',
|
|
47
49
|
styleUrls: ['./site-map-sidebar.component.scss']
|