@sd-angular/core 19.0.0-beta.49 → 19.0.0-beta.50

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.
@@ -1,24 +1,12 @@
1
- import { ChangeDetectorRef, Injector, OnDestroy } from '@angular/core';
2
- import { ActivatedRoute, Router } from '@angular/router';
3
- import { SdNotifyService } from '@sd-angular/core/services/notify';
1
+ import { OnDestroy } from '@angular/core';
4
2
  import { SdTab } from '../../models';
5
- import { SdTabDecoratorService } from '../../services/tab-decorator.service';
6
- import { SdTabRouterService } from '../../services/tab-router.service';
7
3
  import { SdTabRouterNavComponent } from '../tab-router-nav/tab-router-nav.component';
8
4
  import * as i0 from "@angular/core";
9
5
  export declare class SdTabRouterOutletComponent implements OnDestroy {
10
6
  #private;
11
- private router;
12
- private activatedRoute;
13
- private cd;
14
- private injector;
15
- private tabDecoratorService;
16
- private tabRouterService;
17
- private sdNotifyService;
18
7
  tabRouterNav?: SdTabRouterNavComponent;
19
- tabs: SdTab[];
20
- constructor(router: Router, activatedRoute: ActivatedRoute, cd: ChangeDetectorRef, injector: Injector, tabDecoratorService: SdTabDecoratorService, // KHÔNG XÓA
21
- tabRouterService: SdTabRouterService, sdNotifyService: SdNotifyService);
8
+ tabs: import("@angular/core").WritableSignal<SdTab[]>;
9
+ constructor();
22
10
  ngOnDestroy(): void;
23
11
  tabTrackBy: (index: number, tab: SdTab) => string;
24
12
  static ɵfac: i0.ɵɵFactoryDeclaration<SdTabRouterOutletComponent, never>;
@@ -1,19 +1,19 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, Pipe, Input, ChangeDetectionStrategy, Component, HostListener, ViewChild, createNgModule } from '@angular/core';
3
- import * as i1 from '@angular/router';
4
- import { RouterEvent, RoutesRecognized, NavigationEnd, ActivatedRoute } from '@angular/router';
5
- import * as i5 from '@angular/common';
2
+ import { Injectable, Pipe, Input, ChangeDetectionStrategy, Component, HostListener, ViewChild, signal, inject, Injector, NgModuleFactory, createNgModule } from '@angular/core';
3
+ import * as i2 from '@angular/router';
4
+ import { Router, ActivatedRoute, RouterEvent, RoutesRecognized, NavigationEnd } from '@angular/router';
5
+ import * as i1 from '@angular/common';
6
6
  import { CommonModule } from '@angular/common';
7
7
  import * as i3 from '@angular/material/icon';
8
8
  import { MatIconModule } from '@angular/material/icon';
9
9
  import { MatTooltipModule } from '@angular/material/tooltip';
10
- import { BehaviorSubject, Subscription, Subject } from 'rxjs';
10
+ import { BehaviorSubject, Subscription, isObservable, lastValueFrom, Subject } from 'rxjs';
11
11
  import { debounceTime, startWith, map, filter, take } from 'rxjs/operators';
12
- import * as i2 from '@angular/cdk/drag-drop';
12
+ import { SdNotifyService } from '@sd-angular/core/services/notify';
13
+ import { SdUtilities } from '@sd-angular/core/utilities';
14
+ import * as i2$1 from '@angular/cdk/drag-drop';
13
15
  import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop';
14
16
  import { SdBadge } from '@sd-angular/core/components/badge';
15
- import { SdUtilities } from '@sd-angular/core/utilities';
16
- import * as i4 from '@sd-angular/core/services/notify';
17
17
 
18
18
  class SdTabBase {
19
19
  #tab;
@@ -114,6 +114,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
114
114
  }]
115
115
  }], ctorParameters: () => [] });
116
116
 
117
+ class SdTabDecoratorService {
118
+ static tabRouterService = new BehaviorSubject(undefined);
119
+ constructor(tabRouterService) {
120
+ SdTabDecoratorService.tabRouterService.next(tabRouterService);
121
+ }
122
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdTabDecoratorService, deps: [{ token: SdTabRouterService }], target: i0.ɵɵFactoryTarget.Injectable });
123
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdTabDecoratorService, providedIn: 'root' });
124
+ }
125
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdTabDecoratorService, decorators: [{
126
+ type: Injectable,
127
+ args: [{
128
+ providedIn: 'root',
129
+ }]
130
+ }], ctorParameters: () => [{ type: SdTabRouterService }] });
131
+
117
132
  class SdTabInfoPipe {
118
133
  tabRouterService;
119
134
  constructor(tabRouterService) {
@@ -221,13 +236,13 @@ class SdTabRouterItemComponent {
221
236
  this.tabRouterService.close(this.tab);
222
237
  }
223
238
  };
224
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdTabRouterItemComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: SdTabRouterService }, { token: i1.Router }], target: i0.ɵɵFactoryTarget.Component });
239
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdTabRouterItemComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: SdTabRouterService }, { token: i2.Router }], target: i0.ɵɵFactoryTarget.Component });
225
240
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: SdTabRouterItemComponent, isStandalone: true, selector: "sd-tab-router-item", inputs: { tab: "tab" }, ngImport: i0, template: "<a\r\n [href]=\"[tab.url]\"\r\n class=\"tab-router__item d-flex align-items-center gap-8\"\r\n [class.tab-router__item--active]=\"tab.isActive\"\r\n (click)=\"onTabClick($event)\"\r\n (mousedown)=\"onMousedown($event)\"\r\n (mouseup)=\"onMouseup($event)\">\r\n @let info = tabInfo | sdTabInfo: tab;\r\n @if (info) {\r\n <sd-badge\r\n style=\"overflow: hidden;white-space: nowrap;\"\r\n [icon]=\"info.icon\"\r\n [title]=\"info.icon\"\r\n [tooltip]=\"info.tooltip || info.name\"\r\n [title]=\"info.name\"\r\n [color]=\"info.color\"\r\n (click)=\"onTabClick($event)\"></sd-badge>\r\n <button\r\n aria-hidden=\"true\"\r\n class=\"tab-router__close d-flex align-items-center justify-content-center ml-auto p-0\"\r\n (click)=\"close($event)\"\r\n (mousedown)=\"$event.stopPropagation()\">\r\n <mat-icon aria-hidden=\"true\" fontIcon=\"close\"></mat-icon>\r\n </button>\r\n }\r\n</a>\r\n", styles: [":host{display:block;overflow:hidden;position:relative;flex:1 1 64px;max-width:240px}:host:after{content:\"\";position:absolute;right:0;top:0;bottom:0;height:16px;width:1px;background:#dde0e5;margin:auto}:host:last-child:after{content:none}:host::ng-deep .tab-router__item sd-badge .c-badge-title{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;width:184px}.flex-1{flex:1}.tab-router__icon{background-color:#5c6bc0;width:16px;height:16px;line-height:16px;text-align:center;font-size:10px;color:#fff;border-radius:2px;text-transform:uppercase}.tab-router__icon .mat-icon{height:10px;width:10px;font-size:10px}.tab-router__close{color:#757575;outline:none;border:0;background:none;border-radius:50%;height:16px;width:16px}.tab-router__close:hover{background-color:#0000001f}.tab-router__close .mat-icon{font-size:12px;height:12px;width:12px}.tab-router__item{background:#f2f3f4;padding:8px;color:inherit;text-decoration:none;font-size:12px;line-height:16px;overflow:hidden}.tab-router__item:hover{background-color:#fff}.tab-router__name{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.tab-router__item--active{border-radius:8px 8px 0 0;background-color:#fff}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: SdBadge, selector: "sd-badge", inputs: ["type", "color", "primary", "secondary", "success", "info", "warning", "error", "fontSet", "title", "description", "tooltip", "icon", "size"], outputs: ["click"] }, { kind: "pipe", type: SdTabInfoPipe, name: "sdTabInfo" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
226
241
  }
227
242
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdTabRouterItemComponent, decorators: [{
228
243
  type: Component,
229
244
  args: [{ selector: 'sd-tab-router-item', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, MatIconModule, SdBadge, SdTabInfoPipe], template: "<a\r\n [href]=\"[tab.url]\"\r\n class=\"tab-router__item d-flex align-items-center gap-8\"\r\n [class.tab-router__item--active]=\"tab.isActive\"\r\n (click)=\"onTabClick($event)\"\r\n (mousedown)=\"onMousedown($event)\"\r\n (mouseup)=\"onMouseup($event)\">\r\n @let info = tabInfo | sdTabInfo: tab;\r\n @if (info) {\r\n <sd-badge\r\n style=\"overflow: hidden;white-space: nowrap;\"\r\n [icon]=\"info.icon\"\r\n [title]=\"info.icon\"\r\n [tooltip]=\"info.tooltip || info.name\"\r\n [title]=\"info.name\"\r\n [color]=\"info.color\"\r\n (click)=\"onTabClick($event)\"></sd-badge>\r\n <button\r\n aria-hidden=\"true\"\r\n class=\"tab-router__close d-flex align-items-center justify-content-center ml-auto p-0\"\r\n (click)=\"close($event)\"\r\n (mousedown)=\"$event.stopPropagation()\">\r\n <mat-icon aria-hidden=\"true\" fontIcon=\"close\"></mat-icon>\r\n </button>\r\n }\r\n</a>\r\n", styles: [":host{display:block;overflow:hidden;position:relative;flex:1 1 64px;max-width:240px}:host:after{content:\"\";position:absolute;right:0;top:0;bottom:0;height:16px;width:1px;background:#dde0e5;margin:auto}:host:last-child:after{content:none}:host::ng-deep .tab-router__item sd-badge .c-badge-title{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;width:184px}.flex-1{flex:1}.tab-router__icon{background-color:#5c6bc0;width:16px;height:16px;line-height:16px;text-align:center;font-size:10px;color:#fff;border-radius:2px;text-transform:uppercase}.tab-router__icon .mat-icon{height:10px;width:10px;font-size:10px}.tab-router__close{color:#757575;outline:none;border:0;background:none;border-radius:50%;height:16px;width:16px}.tab-router__close:hover{background-color:#0000001f}.tab-router__close .mat-icon{font-size:12px;height:12px;width:12px}.tab-router__item{background:#f2f3f4;padding:8px;color:inherit;text-decoration:none;font-size:12px;line-height:16px;overflow:hidden}.tab-router__item:hover{background-color:#fff}.tab-router__name{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.tab-router__item--active{border-radius:8px 8px 0 0;background-color:#fff}\n"] }]
230
- }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: SdTabRouterService }, { type: i1.Router }], propDecorators: { tab: [{
245
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: SdTabRouterService }, { type: i2.Router }], propDecorators: { tab: [{
231
246
  type: Input,
232
247
  args: [{ required: true }]
233
248
  }] } });
@@ -262,7 +277,7 @@ class SdTabRouterNavComponent {
262
277
  moveItemInArray(this.tabs, event.previousIndex, event.currentIndex);
263
278
  };
264
279
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdTabRouterNavComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
265
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: SdTabRouterNavComponent, isStandalone: true, selector: "sd-tab-router-nav", inputs: { tabs: "tabs" }, host: { listeners: { "window:resize": "onResize($event)" } }, viewQueries: [{ propertyName: "tabRouterNav", first: true, predicate: ["tabRouterNav"], descendants: true }], ngImport: i0, template: "<div\n #tabRouterNav\n cdkDropList\n cdkDropListLockAxis=\"x\"\n cdkDropListOrientation=\"horizontal\"\n (cdkDropListDropped)=\"onDrop($event)\"\n class=\"tab-router__nav tab-router__nav--{{ mode }} d-flex align-items-center flex-nowrap\"\n [class.d-none]=\"tabs.length > 1\">\n <ng-container *ngFor=\"let tab of tabs\">\n <sd-tab-router-item [tab]=\"tab\" cdkDrag [cdkDragBoundary]=\"elementRef?.nativeElement\"></sd-tab-router-item>\n </ng-container>\n</div>\n", styles: [".tab-router__nav{background:#f9f9f9;overflow:hidden}.tab-router__nav--compact::ng-deep .tab-router__name{display:none}.tab-router__nav--compact::ng-deep .tab-router__icon{margin:0!important}.tab-router__nav--compact::ng-deep .tab-router__item--active{min-width:240px}.tab-router__nav--compact::ng-deep .tab-router__item--active .tab-router__icon{margin-right:8px!important}.tab-router__nav--compact::ng-deep .tab-router__item--active .tab-router__name{display:-webkit-box}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i2.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i2.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "component", type: SdTabRouterItemComponent, selector: "sd-tab-router-item", inputs: ["tab"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
280
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: SdTabRouterNavComponent, isStandalone: true, selector: "sd-tab-router-nav", inputs: { tabs: "tabs" }, host: { listeners: { "window:resize": "onResize($event)" } }, viewQueries: [{ propertyName: "tabRouterNav", first: true, predicate: ["tabRouterNav"], descendants: true }], ngImport: i0, template: "<div\n #tabRouterNav\n cdkDropList\n cdkDropListLockAxis=\"x\"\n cdkDropListOrientation=\"horizontal\"\n (cdkDropListDropped)=\"onDrop($event)\"\n class=\"tab-router__nav tab-router__nav--{{ mode }} d-flex align-items-center flex-nowrap\"\n [class.d-none]=\"tabs.length > 1\">\n <ng-container *ngFor=\"let tab of tabs\">\n <sd-tab-router-item [tab]=\"tab\" cdkDrag [cdkDragBoundary]=\"elementRef?.nativeElement\"></sd-tab-router-item>\n </ng-container>\n</div>\n", styles: [".tab-router__nav{background:#f9f9f9;overflow:hidden}.tab-router__nav--compact::ng-deep .tab-router__name{display:none}.tab-router__nav--compact::ng-deep .tab-router__icon{margin:0!important}.tab-router__nav--compact::ng-deep .tab-router__item--active{min-width:240px}.tab-router__nav--compact::ng-deep .tab-router__item--active .tab-router__icon{margin-right:8px!important}.tab-router__nav--compact::ng-deep .tab-router__item--active .tab-router__name{display:-webkit-box}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i2$1.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i2$1.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "component", type: SdTabRouterItemComponent, selector: "sd-tab-router-item", inputs: ["tab"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
266
281
  }
267
282
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdTabRouterNavComponent, decorators: [{
268
283
  type: Component,
@@ -277,64 +292,38 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
277
292
  args: ['window:resize', ['$event']]
278
293
  }] } });
279
294
 
280
- class SdTabDecoratorService {
281
- static tabRouterService = new BehaviorSubject(undefined);
282
- constructor(tabRouterService) {
283
- SdTabDecoratorService.tabRouterService.next(tabRouterService);
284
- }
285
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdTabDecoratorService, deps: [{ token: SdTabRouterService }], target: i0.ɵɵFactoryTarget.Injectable });
286
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdTabDecoratorService, providedIn: 'root' });
287
- }
288
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdTabDecoratorService, decorators: [{
289
- type: Injectable,
290
- args: [{
291
- providedIn: 'root',
292
- }]
293
- }], ctorParameters: () => [{ type: SdTabRouterService }] });
294
-
295
295
  /* eslint-disable @typescript-eslint/no-explicit-any */
296
296
  class SdTabRouterOutletComponent {
297
- router;
298
- activatedRoute;
299
- cd;
300
- injector;
301
- tabDecoratorService;
302
- tabRouterService;
303
- sdNotifyService;
304
297
  tabRouterNav;
305
- tabs = [];
298
+ tabs = signal([]);
299
+ #router = inject(Router);
300
+ #activatedRoute = inject(ActivatedRoute);
301
+ #injector = inject(Injector);
302
+ #tabRouterService = inject(SdTabRouterService);
303
+ #sdNotifyService = inject(SdNotifyService);
304
+ #tabDecoratorService = inject(SdTabDecoratorService);
306
305
  #rootRoute;
307
306
  #subscription = new Subscription();
308
307
  #firstLoad = true;
309
- constructor(router, activatedRoute, cd, injector, tabDecoratorService, // KHÔNG XÓA
310
- tabRouterService, sdNotifyService) {
311
- this.router = router;
312
- this.activatedRoute = activatedRoute;
313
- this.cd = cd;
314
- this.injector = injector;
315
- this.tabDecoratorService = tabDecoratorService;
316
- this.tabRouterService = tabRouterService;
317
- this.sdNotifyService = sdNotifyService;
318
- this.#subscription.add(router.events
319
- .pipe(map((event) => (event instanceof RouterEvent ? event : event.routerEvent)), filter(event => {
320
- return event instanceof RoutesRecognized || event instanceof NavigationEnd;
321
- }))
308
+ constructor() {
309
+ this.#subscription.add(this.#router.events
310
+ .pipe(map((event) => (event instanceof RouterEvent ? event : event.routerEvent)), filter(event => event instanceof RoutesRecognized || event instanceof NavigationEnd))
322
311
  .subscribe(async (event) => {
323
312
  if (this.#firstLoad && event instanceof NavigationEnd) {
324
313
  this.#firstLoad = false;
325
- const route = this.#getActivatedRouteSnapshot(this.activatedRoute.snapshot);
326
- this.#rootRoute = this.router.routerState.root;
314
+ const route = this.#getActivatedRouteSnapshot(this.#activatedRoute.snapshot);
315
+ this.#rootRoute = this.#router.routerState.root;
327
316
  await this.#activeRoute(event.urlAfterRedirects || event.url, route);
328
317
  return;
329
318
  }
330
319
  if (!this.#firstLoad && event instanceof RoutesRecognized) {
331
320
  const route = this.#getActivatedRouteSnapshot(event.state.root);
332
- this.#rootRoute = this.router.routerState.root;
321
+ this.#rootRoute = this.#router.routerState.root;
333
322
  await this.#activeRoute(event.urlAfterRedirects || event.url, route);
334
323
  }
335
324
  }));
336
- this.#subscription.add(tabRouterService.actions.subscribe((event) => {
337
- if (event && event.type === 'close') {
325
+ this.#subscription.add(this.#tabRouterService.actions.subscribe((event) => {
326
+ if (event?.type === 'close') {
338
327
  this.#closeTab(event.tab);
339
328
  }
340
329
  }));
@@ -342,61 +331,42 @@ class SdTabRouterOutletComponent {
342
331
  ngOnDestroy() {
343
332
  this.#subscription.unsubscribe();
344
333
  }
345
- tabTrackBy = (index, tab) => {
346
- return tab.key;
347
- };
334
+ tabTrackBy = (index, tab) => tab.key;
348
335
  #closeTab = (tab) => {
336
+ const currentTabs = this.tabs();
349
337
  const { isActive, key: activeKey } = tab;
350
338
  if (isActive) {
351
- const activeIndex = this.tabs.findIndex(({ key }) => key === activeKey);
352
- const nextTab = this.tabs[activeIndex + 1] || this.tabs[activeIndex - 1];
339
+ const activeIndex = currentTabs.findIndex(({ key }) => key === activeKey);
340
+ const nextTab = currentTabs[activeIndex + 1] || currentTabs[activeIndex - 1];
341
+ this.tabs.set(currentTabs.filter(({ key }) => key !== activeKey));
353
342
  if (nextTab) {
354
- const { url: nextUrl } = nextTab;
355
- const nextQueryParams = {
356
- ...(nextTab.queryParams || {}),
357
- };
358
- this.tabs = this.tabs.filter(({ key }) => key !== activeKey);
359
- this.router.navigate([nextUrl], {
360
- queryParams: nextQueryParams,
361
- state: {
362
- switchTab: true,
363
- },
343
+ this.#router.navigate([nextTab.url], {
344
+ queryParams: { ...(nextTab.queryParams || {}) },
345
+ state: { switchTab: true },
364
346
  });
365
347
  }
366
348
  else {
367
- this.tabs = this.tabs.filter(({ key }) => key !== activeKey);
368
- this.router.navigateByUrl('/', {
369
- state: {
370
- switchTab: true,
371
- },
372
- });
349
+ this.#router.navigateByUrl('/', { state: { switchTab: true } });
373
350
  }
374
351
  }
375
352
  else {
376
- this.tabs = this.tabs.filter(({ key }) => key !== tab.key);
353
+ this.tabs.set(currentTabs.filter(({ key }) => key !== tab.key));
377
354
  this.tabRouterNav?.checkUI();
378
- this.cd.markForCheck();
379
355
  }
380
356
  };
381
357
  #activeRoute = async (fullUrl, route) => {
382
- if (!route?.component) {
358
+ if (!route?.component)
383
359
  return;
384
- }
385
360
  const component = route.component;
386
- const queryParams = {
387
- ...(route.queryParams || {}),
388
- };
389
- const params = {
390
- ...(route.params || {}),
391
- };
392
- const data = {
393
- ...(route.data || {}),
394
- };
361
+ const queryParams = { ...(route.queryParams || {}) };
362
+ const params = { ...(route.params || {}) };
363
+ const data = { ...(route.data || {}) };
395
364
  const [url] = fullUrl.split('?');
396
365
  const key = SdUtilities.hash({ url, queryParams });
397
366
  let existedIndex = -1;
398
367
  let activatedIndex = -1;
399
- this.tabs.forEach((tab, index) => {
368
+ const currentTabs = this.tabs();
369
+ currentTabs.forEach((tab, index) => {
400
370
  if (tab.key === key) {
401
371
  tab.isActive = true;
402
372
  existedIndex = index;
@@ -404,179 +374,126 @@ class SdTabRouterOutletComponent {
404
374
  else {
405
375
  if (tab.isActive) {
406
376
  activatedIndex = index;
407
- this.tabRouterService.pushEvent(tab, SdTabDeactivated);
377
+ this.#tabRouterService.pushEvent(tab, SdTabDeactivated);
408
378
  }
409
379
  tab.isActive = false;
410
380
  }
411
381
  });
412
- const currentNavigation = this.router.getCurrentNavigation();
413
- // Switch tab sẽ ko re-render lại trang
382
+ const currentNavigation = this.#router.getCurrentNavigation();
414
383
  const switchTab = currentNavigation?.extras?.state?.['switchTab'];
415
- // Replace tab sẽ close trang hiện tại
416
384
  const replaceTab = currentNavigation?.extras?.state?.['replaceTab'];
385
+ // --- XỬ LÝ INJECTOR VÀ FIX LỖI TYPE TS(2345) ---
386
+ const getBestInjector = async (snapshot) => {
387
+ // 1. Nếu là Standalone Route, lấy injector từ chính route config (đã được router resolve)
388
+ const routeInjector = snapshot._resolvedGui || snapshot.routeConfig?._injector;
389
+ if (routeInjector)
390
+ return routeInjector;
391
+ // 2. Xử lý NgModule (Lazy load kiểu cũ)
392
+ const loadChildren = snapshot.parent?.routeConfig?.loadChildren;
393
+ if (typeof loadChildren === 'function') {
394
+ let loaded = await loadChildren();
395
+ // Unwrap Observable
396
+ if (isObservable(loaded)) {
397
+ loaded = await lastValueFrom(loaded);
398
+ }
399
+ // Unwrap Default Export (ES Module)
400
+ if (loaded && typeof loaded === 'object' && 'default' in loaded) {
401
+ loaded = loaded.default;
402
+ }
403
+ // Nếu là NgModuleFactory (Angular cũ hơn)
404
+ if (loaded instanceof NgModuleFactory) {
405
+ return loaded.create(this.#injector).injector;
406
+ }
407
+ // Nếu là Type (Class NgModule) - Đây là chỗ fix lỗi TS(2345)
408
+ if (typeof loaded === 'function' && !Array.isArray(loaded)) {
409
+ try {
410
+ return createNgModule(loaded, this.#injector).injector;
411
+ }
412
+ catch {
413
+ return this.#injector;
414
+ }
415
+ }
416
+ }
417
+ return this.#injector;
418
+ };
419
+ const finalInjector = await getBestInjector(route);
420
+ const activatedRoute = this.#getActivatedRoute(this.#rootRoute, component);
421
+ const newTab = {
422
+ key,
423
+ component,
424
+ injector: new SdOutletInjector(activatedRoute, finalInjector),
425
+ isActive: true,
426
+ url,
427
+ params,
428
+ queryParams,
429
+ data,
430
+ tabInfoChanges: new Subject(),
431
+ };
417
432
  if (existedIndex >= 0) {
418
- const existedTab = this.tabs[existedIndex];
433
+ const updatedTabs = [...currentTabs];
419
434
  if (replaceTab && activatedIndex >= 0) {
420
- if (activatedIndex >= 0) {
421
- this.tabs.splice(activatedIndex, 1);
422
- }
435
+ updatedTabs.splice(activatedIndex, 1);
423
436
  }
424
437
  if (switchTab) {
425
- this.tabRouterService.setCurrentTab(existedTab);
426
- this.tabRouterService.pushEvent(existedTab, SdTabActivated);
438
+ this.#tabRouterService.setCurrentTab(updatedTabs[existedIndex]);
439
+ this.#tabRouterService.pushEvent(updatedTabs[existedIndex], SdTabActivated);
427
440
  }
428
441
  else {
429
- if (typeof route?.parent?.routeConfig?.loadChildren === 'function') {
430
- const module = (await route.parent.routeConfig.loadChildren());
431
- const moduleRef = createNgModule(module, this.injector);
432
- const activatedRoute = this.#getActivatedRoute(this.#rootRoute, component);
433
- const tab = {
434
- key,
435
- component,
436
- injector: new SdOutletInjector(activatedRoute, moduleRef),
437
- isActive: true,
438
- url,
439
- params,
440
- queryParams,
441
- data,
442
- tabInfoChanges: new Subject(),
443
- };
444
- this.tabs[this.tabs.indexOf(existedTab)] = tab;
445
- }
442
+ updatedTabs[existedIndex] = newTab;
446
443
  }
444
+ this.tabs.set(updatedTabs);
447
445
  }
448
446
  else {
449
- if (typeof route?.parent?.routeConfig?.loadChildren === 'function') {
450
- const module = (await route.parent.routeConfig.loadChildren());
451
- const moduleRef = createNgModule(module, this.injector);
452
- const activatedRoute = this.#getActivatedRoute(this.#rootRoute, component);
453
- const tab = {
454
- key,
455
- component,
456
- injector: new SdOutletInjector(activatedRoute, moduleRef),
457
- isActive: true,
458
- url,
459
- params,
460
- queryParams,
461
- data,
462
- tabInfoChanges: new Subject(),
463
- };
464
- this.tabRouterService.setCurrentTab(tab);
465
- if (activatedIndex >= 0 && replaceTab) {
466
- this.tabs.splice(activatedIndex, 1);
467
- }
468
- this.tabs.push(tab);
469
- // if (existedIndex >= 0 && !switchTab) {
470
- // this.tabs[existedIndex] = tab;
471
- // } else {
472
- // this.tabs.push(tab);
473
- // }
447
+ const updatedTabs = [...currentTabs];
448
+ this.#tabRouterService.setCurrentTab(newTab);
449
+ if (activatedIndex >= 0 && replaceTab) {
450
+ updatedTabs.splice(activatedIndex, 1);
474
451
  }
475
- this.tabRouterNav?.checkUI();
476
- if (this.tabs.length > 10) {
477
- this.sdNotifyService.warning('Bạn đã mở quá nhiều tab. Vui lòng tắt các tab không dùng để hệ thống hoạt động tốt hơn.');
452
+ this.tabs.set([...updatedTabs, newTab]);
453
+ if (this.tabs().length > 10) {
454
+ this.#sdNotifyService.warning('Bạn đã mở quá nhiều tab.');
478
455
  }
479
- // if (this.tabs.length > 15) {
480
- // this.tabs.splice(0, this.tabs.length - 10);
481
- // }
482
456
  }
483
- // if (existedIndex >= 0 && switchTab) {
484
- // const existedTab = this.tabs[existedIndex];
485
- // this.tabRouterService.setCurrentTab(existedTab);
486
- // this.tabRouterService.pushEvent(existedTab, SdTabActivated);
487
- // } else {
488
- // if (typeof route?.parent?.routeConfig?.loadChildren === 'function') {
489
- // const module = await route.parent.routeConfig.loadChildren();
490
- // const factory = await this.compiler.compileModuleAsync(module);
491
- // const injector = factory.create(this.injector);
492
- // const activatedRoute = this.#getActivatedRoute(
493
- // this.#rootRoute,
494
- // component
495
- // );
496
- // const tab = {
497
- // key,
498
- // component,
499
- // injector: new SdOutletInjector(activatedRoute, injector),
500
- // isActive: true,
501
- // name: url,
502
- // url,
503
- // params,
504
- // queryParams,
505
- // data
506
- // };
507
- // this.tabRouterService.setCurrentTab(tab);
508
- // if (activatedIndex >= 0 && replaceTab) {
509
- // this.tabs.splice(activatedIndex, 1);
510
- // }
511
- // if (existedIndex >= 0 && !switchTab) {
512
- // this.tabs[existedIndex] = tab;
513
- // } else {
514
- // this.tabs.push(tab);
515
- // }
516
- // }
517
- // this.tabRouterNav?.checkUI();
518
- // if (this.tabs.length > 10) {
519
- // this.sdNotifyService.notify.warning(
520
- // 'Bạn đã mở quá nhiều tab. Vui lòng tắt các tab không dùng để hệ thống hoạt động tốt hơn.'
521
- // );
522
- // }
523
- // if (this.tabs.length > 15) {
524
- // this.tabs.splice(0, this.tabs.length - 10);
525
- // }
526
- // }
527
- this.cd.markForCheck();
457
+ this.tabRouterNav?.checkUI();
528
458
  };
529
- #getActivatedRouteSnapshot = (activatedRouteSnapshot) => {
530
- if (!activatedRouteSnapshot) {
531
- return null;
532
- }
533
- while (activatedRouteSnapshot.firstChild) {
534
- activatedRouteSnapshot = activatedRouteSnapshot.firstChild;
535
- }
536
- return activatedRouteSnapshot;
459
+ #getActivatedRouteSnapshot = (snapshot) => {
460
+ let node = snapshot;
461
+ while (node.firstChild)
462
+ node = node.firstChild;
463
+ return node;
537
464
  };
538
465
  #getActivatedRoute = (activatedRoute, component) => {
539
- if (!activatedRoute) {
540
- return null;
541
- }
542
- if (activatedRoute.component && activatedRoute.component === component) {
466
+ if (activatedRoute.component === component)
543
467
  return activatedRoute;
544
- }
545
- while (activatedRoute.firstChild) {
546
- activatedRoute = activatedRoute.firstChild;
547
- if (activatedRoute.component && activatedRoute.component === component) {
548
- return activatedRoute;
549
- }
468
+ for (const child of activatedRoute.children) {
469
+ const result = this.#getActivatedRoute(child, component);
470
+ if (result)
471
+ return result;
550
472
  }
551
473
  return null;
552
474
  };
553
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdTabRouterOutletComponent, deps: [{ token: i1.Router }, { token: i1.ActivatedRoute }, { token: i0.ChangeDetectorRef }, { token: i0.Injector }, { token: SdTabDecoratorService }, { token: SdTabRouterService }, { token: i4.SdNotifyService }], target: i0.ɵɵFactoryTarget.Component });
554
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: SdTabRouterOutletComponent, isStandalone: true, selector: "sd-tab-router-outlet", viewQueries: [{ propertyName: "tabRouterNav", first: true, predicate: ["tabRouterNav"], descendants: true }], ngImport: i0, template: "<sd-tab-router-nav [tabs]=\"tabs\" #tabRouterNav></sd-tab-router-nav>\n\n<div class=\"tab-router__list\">\n <ng-container *ngFor=\"let tab of tabs; trackBy: tabTrackBy\">\n <div class=\"tab-router__pane\" [class.active]=\"tab.isActive\" [id]=\"tab.key\">\n <div class=\"tab-router__content\">\n <ng-container *ngComponentOutlet=\"tab.component; injector: tab.injector\"></ng-container>\n </div>\n </div>\n </ng-container>\n</div>\n", styles: [":host{display:flex;flex-direction:column;width:100%;height:calc(100vh - 64px)}:host ::ng-deep .sd-loading{max-width:100%;max-height:100%}.tab-router__list{flex:1}.tab-router__pane{display:none;position:relative;height:100%;width:100%}.tab-router__pane.active{display:block}.tab-router__content{position:absolute;inset:0;overflow:auto;height:100%;width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i5.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "component", type: SdTabRouterNavComponent, selector: "sd-tab-router-nav", inputs: ["tabs"] }] });
475
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdTabRouterOutletComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
476
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: SdTabRouterOutletComponent, isStandalone: true, selector: "sd-tab-router-outlet", viewQueries: [{ propertyName: "tabRouterNav", first: true, predicate: ["tabRouterNav"], descendants: true }], ngImport: i0, template: "<sd-tab-router-nav [tabs]=\"tabs()\" #tabRouterNav></sd-tab-router-nav>\n\n<div class=\"tab-router__list\">\n @for (tab of tabs(); track tab.key) {\n <div class=\"tab-router__pane\" [class.active]=\"tab.isActive\" [id]=\"tab.key\">\n <div class=\"tab-router__content\">\n <ng-container *ngComponentOutlet=\"tab.component; injector: tab.injector\"></ng-container>\n </div>\n </div>\n } @empty {\n <div class=\"tab-router__empty\">\n </div>\n }\n</div>", styles: [":host{display:flex;flex-direction:column;width:100%;height:calc(100vh - 64px)}:host ::ng-deep .sd-loading{max-width:100%;max-height:100%}.tab-router__list{flex:1}.tab-router__pane{display:none;position:relative;height:100%;width:100%}.tab-router__pane.active{display:block}.tab-router__content{position:absolute;inset:0;overflow:auto;height:100%;width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "component", type: SdTabRouterNavComponent, selector: "sd-tab-router-nav", inputs: ["tabs"] }] });
555
477
  }
556
478
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdTabRouterOutletComponent, decorators: [{
557
479
  type: Component,
558
- args: [{ selector: 'sd-tab-router-outlet', standalone: true, imports: [CommonModule, MatIconModule, MatTooltipModule, SdTabRouterNavComponent], template: "<sd-tab-router-nav [tabs]=\"tabs\" #tabRouterNav></sd-tab-router-nav>\n\n<div class=\"tab-router__list\">\n <ng-container *ngFor=\"let tab of tabs; trackBy: tabTrackBy\">\n <div class=\"tab-router__pane\" [class.active]=\"tab.isActive\" [id]=\"tab.key\">\n <div class=\"tab-router__content\">\n <ng-container *ngComponentOutlet=\"tab.component; injector: tab.injector\"></ng-container>\n </div>\n </div>\n </ng-container>\n</div>\n", styles: [":host{display:flex;flex-direction:column;width:100%;height:calc(100vh - 64px)}:host ::ng-deep .sd-loading{max-width:100%;max-height:100%}.tab-router__list{flex:1}.tab-router__pane{display:none;position:relative;height:100%;width:100%}.tab-router__pane.active{display:block}.tab-router__content{position:absolute;inset:0;overflow:auto;height:100%;width:100%}\n"] }]
559
- }], ctorParameters: () => [{ type: i1.Router }, { type: i1.ActivatedRoute }, { type: i0.ChangeDetectorRef }, { type: i0.Injector }, { type: SdTabDecoratorService }, { type: SdTabRouterService }, { type: i4.SdNotifyService }], propDecorators: { tabRouterNav: [{
480
+ args: [{ selector: 'sd-tab-router-outlet', standalone: true, imports: [CommonModule, MatIconModule, MatTooltipModule, SdTabRouterNavComponent], template: "<sd-tab-router-nav [tabs]=\"tabs()\" #tabRouterNav></sd-tab-router-nav>\n\n<div class=\"tab-router__list\">\n @for (tab of tabs(); track tab.key) {\n <div class=\"tab-router__pane\" [class.active]=\"tab.isActive\" [id]=\"tab.key\">\n <div class=\"tab-router__content\">\n <ng-container *ngComponentOutlet=\"tab.component; injector: tab.injector\"></ng-container>\n </div>\n </div>\n } @empty {\n <div class=\"tab-router__empty\">\n </div>\n }\n</div>", styles: [":host{display:flex;flex-direction:column;width:100%;height:calc(100vh - 64px)}:host ::ng-deep .sd-loading{max-width:100%;max-height:100%}.tab-router__list{flex:1}.tab-router__pane{display:none;position:relative;height:100%;width:100%}.tab-router__pane.active{display:block}.tab-router__content{position:absolute;inset:0;overflow:auto;height:100%;width:100%}\n"] }]
481
+ }], ctorParameters: () => [], propDecorators: { tabRouterNav: [{
560
482
  type: ViewChild,
561
483
  args: ['tabRouterNav']
562
484
  }] } });
563
485
  class SdOutletInjector {
564
486
  route;
565
- parent;
566
- constructor(route,
567
- // private childContexts: ChildrenOutletContexts,
568
- parent) {
487
+ parentInjector;
488
+ constructor(route, parentInjector) {
569
489
  this.route = route;
570
- this.parent = parent;
490
+ this.parentInjector = parentInjector;
571
491
  }
572
492
  get(token, notFoundValue) {
573
- if (token === ActivatedRoute && this.route) {
574
- return this.route;
493
+ if (token === ActivatedRoute) {
494
+ return this.route || notFoundValue;
575
495
  }
576
- // if (token === ChildrenOutletContexts) {
577
- // return this.childContexts;
578
- // }
579
- return this.parent.injector.get(token, notFoundValue);
496
+ return this.parentInjector.get(token, notFoundValue);
580
497
  }
581
498
  }
582
499