@sd-angular/core 19.0.0-beta.56 → 19.0.0-beta.58

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,38 +1,26 @@
1
1
  import { CdkPortal } from '@angular/cdk/portal';
2
- import { AfterViewInit, ApplicationRef, ChangeDetectorRef, EventEmitter, Injector, OnDestroy, ViewContainerRef } from '@angular/core';
3
2
  import { SdBaseSecureComponent } from '@sd-angular/core/components/base';
4
- import { SdLoadingService } from '@sd-angular/core/services';
5
3
  import { Observable } from 'rxjs';
6
4
  import * as i0 from "@angular/core";
7
- export declare class SdSideDrawer extends SdBaseSecureComponent implements AfterViewInit, OnDestroy {
5
+ export declare class SdSideDrawer extends SdBaseSecureComponent {
8
6
  #private;
9
- private viewContainerRef;
10
- private ar;
11
- private injector;
12
- private ref;
13
- private loadingService;
14
7
  id: string;
15
- portal: CdkPortal;
16
- title: string;
17
- set _title(val: string | null | undefined);
18
- width: string;
19
- set _width(val: string | null | undefined);
20
- hideClose: boolean;
21
- set _hideClose(val: boolean | '' | null | undefined);
22
- disableClose: boolean;
23
- set _disableClose(val: boolean | '' | null | undefined);
24
- sdClosed: EventEmitter<any>;
8
+ portal: import("@angular/core").Signal<CdkPortal>;
9
+ title: import("@angular/core").InputSignal<string>;
10
+ width: import("@angular/core").InputSignal<string>;
11
+ hideClose: import("@angular/core").InputSignalWithTransform<boolean, boolean | "">;
12
+ disableBackdropClose: import("@angular/core").InputSignalWithTransform<boolean, boolean | "">;
13
+ drawerClass: import("@angular/core").InputSignal<any>;
14
+ sdClosed: import("@angular/core").OutputEmitterRef<void>;
25
15
  isOpened: boolean;
26
16
  isLoading: boolean;
27
17
  isHovered$: Observable<boolean>;
28
- constructor(viewContainerRef: ViewContainerRef, ar: ApplicationRef, injector: Injector, ref: ChangeDetectorRef, loadingService: SdLoadingService);
29
- ngAfterViewInit(): void;
18
+ constructor();
30
19
  open: () => void;
31
20
  close: () => void;
32
21
  startLoading: () => void;
33
22
  stopLoading: () => void;
34
- ngOnDestroy(): void;
35
- getHoverStatusStream(): Observable<boolean>;
23
+ preventScroll: (event: Event) => void;
36
24
  static ɵfac: i0.ɵɵFactoryDeclaration<SdSideDrawer, never>;
37
- static ɵcmp: i0.ɵɵComponentDeclaration<SdSideDrawer, "sd-side-drawer", never, { "_title": { "alias": "title"; "required": false; }; "_width": { "alias": "width"; "required": false; }; "_hideClose": { "alias": "hideClose"; "required": false; }; "_disableClose": { "alias": "disableClose"; "required": false; }; }, { "sdClosed": "sdClosed"; }, never, ["[sdTitle]", "[sdBody]", "[sdFooter]"], true, never>;
25
+ static ɵcmp: i0.ɵɵComponentDeclaration<SdSideDrawer, "sd-side-drawer", never, { "title": { "alias": "title"; "required": false; "isSignal": true; }; "width": { "alias": "width"; "required": false; "isSignal": true; }; "hideClose": { "alias": "hideClose"; "required": false; "isSignal": true; }; "disableBackdropClose": { "alias": "disableBackdropClose"; "required": false; "isSignal": true; }; "drawerClass": { "alias": "drawerClass"; "required": false; "isSignal": true; }; }, { "sdClosed": "sdClosed"; }, never, ["[sdHeaderLeft]", "[sdHeaderRight]", "*", "[sdFooter]"], true, never>;
38
26
  }
@@ -4,7 +4,7 @@ export interface ISdUploadFileConfiguration<TArgs = any> {
4
4
  details: SdUploadFileFuncDetails<TArgs>;
5
5
  download?: SdUploadFileFuncDownload<TArgs>;
6
6
  }
7
- export declare const SD_UPLOAD_FILE_CONFIG: InjectionToken<ISdUploadFileConfiguration<any>>;
7
+ export declare const SD_UPLOAD_FILE_CONFIGURATION: InjectionToken<ISdUploadFileConfiguration<any>>;
8
8
  export type SdUploadFileFuncUpload<TArgs> = (files: File[], args?: TArgs) => Promise<string[]>;
9
9
  export type SdUploadFileFuncDetails<TArgs> = (idOrKey: (string | number)[], args?: TArgs) => Promise<SdUploadFileDetail[]>;
10
10
  export type SdUploadFileFuncDownload<TArgs> = (idOrKey: string | number, args?: TArgs) => Promise<void>;
@@ -1,7 +1,6 @@
1
1
  import * as i0 from "@angular/core";
2
2
  export declare class UploadFileService {
3
3
  #private;
4
- constructor();
5
4
  isHashedKey: (key: string) => boolean;
6
5
  get: (key: string) => File | null;
7
6
  add: (file: File) => string | null;
@@ -1,104 +1,105 @@
1
- import * as i5 from '@angular/cdk/portal';
2
- import { DomPortalOutlet, PortalModule, CdkPortal } from '@angular/cdk/portal';
3
- import * as i2 from '@angular/common';
1
+ import * as i2 from '@angular/cdk/portal';
2
+ import { CdkPortal, DomPortalOutlet, PortalModule } from '@angular/cdk/portal';
3
+ import * as i1 from '@angular/common';
4
4
  import { CommonModule } from '@angular/common';
5
5
  import * as i0 from '@angular/core';
6
- import { EventEmitter, Output, Input, ViewChild, ChangeDetectionStrategy, Component } from '@angular/core';
7
- import * as i4 from '@angular/material/button';
8
- import { MatButtonModule } from '@angular/material/button';
9
- import { MatDialogModule } from '@angular/material/dialog';
10
- import * as i3 from '@angular/material/icon';
11
- import { MatIconModule } from '@angular/material/icon';
6
+ import { viewChild, input, booleanAttribute, output, inject, ViewContainerRef, ApplicationRef, Injector, ChangeDetectorRef, DestroyRef, afterNextRender, ChangeDetectionStrategy, Component } from '@angular/core';
12
7
  import { SdBaseSecureComponent } from '@sd-angular/core/components/base';
8
+ import { SdLoadingService } from '@sd-angular/core/services';
13
9
  import * as uuid from 'uuid';
14
10
  import { Subject, fromEvent, merge } from 'rxjs';
15
11
  import { map, startWith, distinctUntilChanged, takeUntil } from 'rxjs/operators';
16
- import * as i1 from '@sd-angular/core/services';
17
12
 
18
13
  class SdSideDrawer extends SdBaseSecureComponent {
19
- viewContainerRef;
20
- ar;
21
- injector;
22
- ref;
23
- loadingService;
24
14
  id = `I${uuid.v4()}`;
25
- portal;
26
- title = '';
27
- set _title(val) {
28
- if (val) {
29
- this.title = val;
30
- }
31
- }
32
- width = '480px';
33
- set _width(val) {
34
- if (val) {
35
- this.width = val;
36
- }
37
- }
38
- hideClose = false;
39
- set _hideClose(val) {
40
- this.hideClose = val === '' || !!val;
41
- }
42
- disableClose = false;
43
- set _disableClose(val) {
44
- this.disableClose = val === '' || !!val;
45
- }
46
- sdClosed = new EventEmitter();
15
+ portal = viewChild.required(CdkPortal);
16
+ title = input('');
17
+ width = input('480px');
18
+ hideClose = input(false, { transform: booleanAttribute });
19
+ disableBackdropClose = input(false, { transform: booleanAttribute });
20
+ // Custom CSS class added to the root side-drawer container
21
+ drawerClass = input('');
22
+ sdClosed = output();
47
23
  #embeddedViewRef;
48
24
  isOpened = false;
49
25
  isLoading = false;
50
26
  isHovered$;
51
27
  #destroy$ = new Subject();
52
- constructor(viewContainerRef, ar, injector, ref, loadingService) {
28
+ #previousBodyOverflow = null;
29
+ #viewContainerRef = inject(ViewContainerRef);
30
+ #ar = inject(ApplicationRef);
31
+ #injector = inject(Injector);
32
+ #ref = inject(ChangeDetectorRef);
33
+ #loadingService = inject(SdLoadingService);
34
+ #destroyRef = inject(DestroyRef);
35
+ constructor() {
53
36
  super();
54
- this.viewContainerRef = viewContainerRef;
55
- this.ar = ar;
56
- this.injector = injector;
57
- this.ref = ref;
58
- this.loadingService = loadingService;
59
- }
60
- ngAfterViewInit() {
61
- // 1. Gắn portal vào body và lưu lại EmbeddedViewRef
62
- const outlet = new DomPortalOutlet(document.body, this.viewContainerRef, this.ar, this.injector);
63
- this.#embeddedViewRef = outlet.attachTemplatePortal(this.portal);
64
- // 2. Setup sự kiện hover ngay sau khi DOM thật đã được in ra
65
- this.#setupHoverSubscription();
37
+ // Thay thế ngAfterViewInit, tự động chạy nội dung này khi DOM sẵn sàng để render
38
+ afterNextRender(() => {
39
+ // 1. Gắn portal vào body và lưu lại EmbeddedViewRef
40
+ const outlet = new DomPortalOutlet(document.body, this.#viewContainerRef, this.#ar, this.#injector);
41
+ this.#embeddedViewRef = outlet.attachTemplatePortal(this.portal());
42
+ // 2. Setup sự kiện hover ngay sau khi DOM thật đã được in ra
43
+ this.#setupHoverSubscription();
44
+ });
45
+ // Thay thế ngOnDestroy bằng logic destroy trực tiếp
46
+ this.#destroyRef.onDestroy(() => {
47
+ this.#destroy$.next();
48
+ this.#destroy$.complete();
49
+ if (this.#embeddedViewRef) {
50
+ this.#embeddedViewRef.destroy();
51
+ }
52
+ if (this.isOpened) {
53
+ if (this.#previousBodyOverflow !== null) {
54
+ document.body.style.overflow = this.#previousBodyOverflow;
55
+ }
56
+ else {
57
+ document.body.style.overflow = '';
58
+ }
59
+ }
60
+ });
66
61
  }
67
62
  open = () => {
68
- this.ref.markForCheck();
63
+ this.#ref.markForCheck();
69
64
  this.isOpened = true;
65
+ // Chặn scroll ở document body
66
+ this.#previousBodyOverflow = document.body.style.overflow;
67
+ document.body.style.overflow = 'hidden';
70
68
  };
71
69
  close = () => {
72
- this.ref.markForCheck();
70
+ this.#ref.markForCheck();
73
71
  this.isOpened = false;
74
72
  this.sdClosed.emit();
75
73
  this.stopLoading();
74
+ // Khôi phục lại scroll ở document body
75
+ if (this.#previousBodyOverflow !== null) {
76
+ document.body.style.overflow = this.#previousBodyOverflow;
77
+ this.#previousBodyOverflow = null;
78
+ }
79
+ else {
80
+ document.body.style.overflow = '';
81
+ }
76
82
  };
77
83
  startLoading = () => {
78
84
  this.isLoading = true;
79
- this.loadingService.stop(`#${this.id}`);
80
- this.loadingService.start(`#${this.id}`);
85
+ this.#loadingService.stop(`#${this.id}`);
86
+ this.#loadingService.start(`#${this.id}`);
81
87
  };
82
88
  stopLoading = () => {
83
89
  this.isLoading = false;
84
- this.loadingService.stop(`#${this.id}`);
90
+ this.#loadingService.stop(`#${this.id}`);
91
+ };
92
+ preventScroll = (event) => {
93
+ event.preventDefault();
85
94
  };
86
- ngOnDestroy() {
87
- // Nhớ clear up subject để tránh memory leak từ RxJS
88
- this.#destroy$.next();
89
- this.#destroy$.complete();
90
- if (this.#embeddedViewRef) {
91
- this.#embeddedViewRef.destroy();
92
- }
93
- }
94
95
  #setupHoverSubscription() {
95
96
  if (!this.#embeddedViewRef)
96
97
  return;
97
98
  // 3. Lấy DOM element trực tiếp từ rootNodes của EmbeddedViewRef
98
99
  const rootNodes = this.#embeddedViewRef.rootNodes;
99
- const element = rootNodes.find((node) => node.nodeType === Node.ELEMENT_NODE && node.classList?.contains('side-drawer'));
100
+ const element = rootNodes.find((node) => node.nodeType === Node.ELEMENT_NODE && node.classList?.contains('sd-side-drawer'));
100
101
  if (!element) {
101
- console.warn('SdSideDrawer: Cannot find side-drawer element to attach hover event');
102
+ console.warn('SdSideDrawer: Cannot find sd-side-drawer element to attach hover event');
102
103
  return;
103
104
  }
104
105
  // 4. Gắn event listeners trực tiếp lên element thật
@@ -106,33 +107,13 @@ class SdSideDrawer extends SdBaseSecureComponent {
106
107
  const mouseLeave$ = fromEvent(element, 'mouseleave').pipe(map(() => false));
107
108
  this.isHovered$ = merge(mouseEnter$, mouseLeave$).pipe(startWith(false), distinctUntilChanged(), takeUntil(this.#destroy$));
108
109
  }
109
- getHoverStatusStream() {
110
- return this.isHovered$;
111
- }
112
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdSideDrawer, deps: [{ token: i0.ViewContainerRef }, { token: i0.ApplicationRef }, { token: i0.Injector }, { token: i0.ChangeDetectorRef }, { token: i1.SdLoadingService }], target: i0.ɵɵFactoryTarget.Component });
113
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: SdSideDrawer, isStandalone: true, selector: "sd-side-drawer", inputs: { _title: ["title", "_title"], _width: ["width", "_width"], _hideClose: ["hideClose", "_hideClose"], _disableClose: ["disableClose", "_disableClose"] }, outputs: { sdClosed: "sdClosed" }, viewQueries: [{ propertyName: "portal", first: true, predicate: CdkPortal, descendants: true }], usesInheritance: true, ngImport: i0, template: "<ng-template cdkPortal>\r\n <div\r\n [id]=\"id\"\r\n class=\"side-drawer\"\r\n [ngStyle]=\"{ width: width }\"\r\n [ngClass]=\"{\r\n 'side-drawer--active': isOpened,\r\n 'side-drawer--loading': isLoading\r\n }\"\r\n #drawerContainer>\r\n @if (isOpened) {\r\n <ng-container>\r\n <div class=\"side-drawer__header media align-items-center\">\r\n <div class=\"side-drawer__title media-body\">\r\n <ng-content select=\"[sdTitle]\"></ng-content>\r\n </div>\r\n @if (hideClose) {\r\n <button type=\"button\" class=\"c-action\" (click)=\"close()\" mat-icon-button>\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n }\r\n </div>\r\n @if (isOpened) {\r\n <div class=\"side-drawer__body\">\r\n <div class=\"side-drawer__content\">\r\n <ng-content select=\"[sdBody]\"></ng-content>\r\n </div>\r\n </div>\r\n }\r\n <div class=\"side-drawer__footer\">\r\n <ng-content select=\"[sdFooter]\"></ng-content>\r\n </div>\r\n </ng-container>\r\n }\r\n </div>\r\n @if (isOpened) {\r\n <button \r\n type=\"button\"\r\n class=\"side-drawer-backdrop\" \r\n (click)=\"disableClose ? null : close()\"\r\n >\r\n </button>\r\n }\r\n</ng-template>\r\n", styles: [".text-primary{color:var(--sd-primary)!important}.bg-primary{background:var(--sd-primary)!important}.border-primary{border-color:var(--sd-primary)!important}.text-primary-light{color:var(--sd-primary-light)!important}.bg-primary-light{background:var(--sd-primary-light)!important}.border-primary-light{border-color:var(--sd-primary-light)!important}.text-primary-dark{color:var(--sd-primary-dark)!important}.bg-primary-dark{background:var(--sd-primary-dark)!important}.border-primary-dark{border-color:var(--sd-primary-dark)!important}.text-info{color:var(--sd-info)!important}.bg-info{background:var(--sd-info)!important}.border-info{border-color:var(--sd-info)!important}.text-info-light{color:var(--sd-info-light)!important}.bg-info-light{background:var(--sd-info-light)!important}.border-info-light{border-color:var(--sd-info-light)!important}.text-info-dark{color:var(--sd-info-dark)!important}.bg-info-dark{background:var(--sd-info-dark)!important}.border-info-dark{border-color:var(--sd-info-dark)!important}.text-success{color:var(--sd-success)!important}.bg-success{background:var(--sd-success)!important}.border-success{border-color:var(--sd-success)!important}.text-success-light{color:var(--sd-success-light)!important}.bg-success-light{background:var(--sd-success-light)!important}.border-success-light{border-color:var(--sd-success-light)!important}.text-success-dark{color:var(--sd-success-dark)!important}.bg-success-dark{background:var(--sd-success-dark)!important}.border-success-dark{border-color:var(--sd-success-dark)!important}.text-warning{color:var(--sd-warning)!important}.bg-warning{background:var(--sd-warning)!important}.border-warning{border-color:var(--sd-warning)!important}.text-warning-light{color:var(--sd-warning-light)!important}.bg-warning-light{background:var(--sd-warning-light)!important}.border-warning-light{border-color:var(--sd-warning-light)!important}.text-warning-dark{color:var(--sd-warning-dark)!important}.bg-warning-dark{background:var(--sd-warning-dark)!important}.border-warning-dark{border-color:var(--sd-warning-dark)!important}.text-error{color:var(--sd-error)!important}.bg-error{background:var(--sd-error)!important}.border-error{border-color:var(--sd-error)!important}.text-error-light{color:var(--sd-error-light)!important}.bg-error-light{background:var(--sd-error-light)!important}.border-error-light{border-color:var(--sd-error-light)!important}.text-error-dark{color:var(--sd-error-dark)!important}.bg-error-dark{background:var(--sd-error-dark)!important}.border-error-dark{border-color:var(--sd-error-dark)!important}.text-secondary{color:var(--sd-secondary)!important}.bg-secondary{background:var(--sd-secondary)!important}.border-secondary{border-color:var(--sd-secondary)!important}.text-secondary-light{color:var(--sd-secondary-light)!important}.bg-secondary-light{background:var(--sd-secondary-light)!important}.border-secondary-light{border-color:var(--sd-secondary-light)!important}.text-secondary-dark{color:var(--sd-secondary-dark)!important}.bg-secondary-dark{background:var(--sd-secondary-dark)!important}.border-secondary-dark{border-color:var(--sd-secondary-dark)!important}.text-light{color:var(--sd-light)!important}.bg-light{background:var(--sd-light)!important}.border-light{border-color:var(--sd-light)!important}.text-dark{color:var(--sd-dark)!important}.bg-dark{background:var(--sd-dark)!important}.border-dark{border-color:var(--sd-dark)!important}.text-black500{color:var(--sd-black500)!important}.bg-black500{background:var(--sd-black500)!important}.border-black500{border-color:var(--sd-black500)!important}.text-black400{color:var(--sd-black400)!important}.bg-black400{background:var(--sd-black400)!important}.border-black400{border-color:var(--sd-black400)!important}.text-black300{color:var(--sd-black300)!important}.bg-black300{background:var(--sd-black300)!important}.border-black300{border-color:var(--sd-black300)!important}.text-black200{color:var(--sd-black200)!important}.bg-black200{background:var(--sd-black200)!important}.border-black200{border-color:var(--sd-black200)!important}.text-black100{color:var(--sd-black100)!important}.bg-black100{background:var(--sd-black100)!important}.border-black100{border-color:var(--sd-black100)!important}.text-white{color:#fff!important}.bg-white{background:#fff!important}.border-white{border-color:#fff!important}.text-black{color:#000!important}.bg-black{background:#000!important}.border-black{border-color:#000!important}.side-drawer{position:fixed;right:0;top:0;bottom:0;background-color:#fff;z-index:999;display:flex;flex-direction:column;opacity:0;visibility:hidden;transform:translate3d(100%,0,0);transition:all .3s ease-in-out;pointer-events:none;box-shadow:0 .4px .8px #0000001a,0 3px 6px #0003}.c-action{color:var(--sd-secondary)}.c-action:focus{outline:none}.side-drawer__icon{border:0;background:transparent;color:#212121;overflow:hidden;outline:none}.side-drawer__icon .mat-icon{float:left}.side-drawer__header{min-height:64px;padding:12px 16px;display:flex;justify-content:space-between}.side-drawer__body{flex:1;position:relative}.side-drawer__content{position:absolute;padding:0 16px 16px;inset:0;overflow:auto}.side-drawer__footer{border-top:1px solid #dde0e5;padding:8px 0}.side-drawer-backdrop{background-color:#0006;border:0;outline:none!important;position:fixed;inset:0;z-index:99;width:100%;height:100%;transition:all .3s ease-in-out}.side-drawer--active{opacity:1;visibility:visible;pointer-events:all;transform:translateZ(0)}.side-drawer__title{font-size:20px;line-height:28px;font-weight:500}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i4.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: PortalModule }, { kind: "directive", type: i5.CdkPortal, selector: "[cdkPortal]", exportAs: ["cdkPortal"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
110
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdSideDrawer, deps: [], target: i0.ɵɵFactoryTarget.Component });
111
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: SdSideDrawer, isStandalone: true, selector: "sd-side-drawer", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, hideClose: { classPropertyName: "hideClose", publicName: "hideClose", isSignal: true, isRequired: false, transformFunction: null }, disableBackdropClose: { classPropertyName: "disableBackdropClose", publicName: "disableBackdropClose", isSignal: true, isRequired: false, transformFunction: null }, drawerClass: { classPropertyName: "drawerClass", publicName: "drawerClass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sdClosed: "sdClosed" }, viewQueries: [{ propertyName: "portal", first: true, predicate: CdkPortal, descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<ng-template cdkPortal>\r\n <div\r\n [id]=\"id\"\r\n class=\"sd-side-drawer\"\r\n [ngStyle]=\"{ width: width() }\"\r\n [ngClass]=\"drawerClass()\"\r\n [class.sd-side-drawer-active]=\"isOpened\"\r\n #drawerContainer>\r\n @if (isOpened) {\r\n <ng-container>\r\n <div class=\"sd-side-drawer-header\">\r\n <div class=\"sd-side-drawer-title\">\r\n {{ title() }}\r\n <ng-content select=\"[sdHeaderLeft]\"></ng-content>\r\n </div>\r\n <div class=\"sd-side-drawer-header-actions\">\r\n <ng-content select=\"[sdHeaderRight]\"></ng-content>\r\n @if (!hideClose()) {\r\n <button type=\"button\" class=\"sd-side-drawer-close-btn\" (click)=\"close()\" aria-label=\"Close\">\r\n <svg viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" stroke=\"currentColor\" stroke-width=\"2\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\r\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\r\n </svg>\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n @if (isOpened) {\r\n <div class=\"sd-side-drawer-body\">\r\n <div class=\"sd-side-drawer-content\">\r\n <ng-content></ng-content>\r\n </div>\r\n </div>\r\n }\r\n <div class=\"sd-side-drawer-footer\">\r\n <ng-content select=\"[sdFooter]\"></ng-content>\r\n </div>\r\n </ng-container>\r\n }\r\n </div>\r\n @if (isOpened) {\r\n <button \r\n type=\"button\"\r\n class=\"sd-side-drawer-backdrop\" \r\n (click)=\"disableBackdropClose() ? null : close()\"\r\n (wheel)=\"preventScroll($event)\"\r\n (touchmove)=\"preventScroll($event)\"\r\n >\r\n </button>\r\n }\r\n</ng-template>\r\n", styles: [".sd-side-drawer{position:fixed;right:0;top:0;bottom:0;background-color:#fff;z-index:999;display:flex;flex-direction:column;opacity:0;visibility:hidden;transform:translate3d(100%,0,0);transition:all .3s ease-in-out;pointer-events:none;box-shadow:0 .4px .8px #0000001a,0 3px 6px #0003}.sd-side-drawer-active{opacity:1;visibility:visible;pointer-events:all;transform:translateZ(0)}.sd-side-drawer-header{min-height:64px;padding:12px 16px;display:flex;align-items:center;justify-content:space-between}.sd-side-drawer-header-actions{display:flex;align-items:center;gap:8px}.sd-side-drawer-title{font-size:20px;line-height:28px;font-weight:500;flex:1;margin-right:16px}.sd-side-drawer-close-btn{background:transparent;border:none;padding:8px;cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--sd-secondary-color, #666);border-radius:50%;transition:background-color .2s,color .2s}.sd-side-drawer-close-btn:hover{background-color:#0000000a;color:var(--sd-primary-color, #333)}.sd-side-drawer-close-btn:focus{outline:none;background-color:#00000014}.sd-side-drawer-body{flex:1;position:relative}.sd-side-drawer-content{position:absolute;padding:0 16px 16px;inset:0;overflow:auto}.sd-side-drawer-footer{border-top:1px solid #dde0e5;padding:8px 0}.sd-side-drawer-backdrop{background-color:#0006;border:0;outline:none!important;position:fixed;inset:0;z-index:99;width:100%;height:100%;transition:all .3s ease-in-out}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: PortalModule }, { kind: "directive", type: i2.CdkPortal, selector: "[cdkPortal]", exportAs: ["cdkPortal"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
114
112
  }
115
113
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdSideDrawer, decorators: [{
116
114
  type: Component,
117
- args: [{ selector: 'sd-side-drawer', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, MatIconModule, MatDialogModule, MatButtonModule, PortalModule], template: "<ng-template cdkPortal>\r\n <div\r\n [id]=\"id\"\r\n class=\"side-drawer\"\r\n [ngStyle]=\"{ width: width }\"\r\n [ngClass]=\"{\r\n 'side-drawer--active': isOpened,\r\n 'side-drawer--loading': isLoading\r\n }\"\r\n #drawerContainer>\r\n @if (isOpened) {\r\n <ng-container>\r\n <div class=\"side-drawer__header media align-items-center\">\r\n <div class=\"side-drawer__title media-body\">\r\n <ng-content select=\"[sdTitle]\"></ng-content>\r\n </div>\r\n @if (hideClose) {\r\n <button type=\"button\" class=\"c-action\" (click)=\"close()\" mat-icon-button>\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n }\r\n </div>\r\n @if (isOpened) {\r\n <div class=\"side-drawer__body\">\r\n <div class=\"side-drawer__content\">\r\n <ng-content select=\"[sdBody]\"></ng-content>\r\n </div>\r\n </div>\r\n }\r\n <div class=\"side-drawer__footer\">\r\n <ng-content select=\"[sdFooter]\"></ng-content>\r\n </div>\r\n </ng-container>\r\n }\r\n </div>\r\n @if (isOpened) {\r\n <button \r\n type=\"button\"\r\n class=\"side-drawer-backdrop\" \r\n (click)=\"disableClose ? null : close()\"\r\n >\r\n </button>\r\n }\r\n</ng-template>\r\n", styles: [".text-primary{color:var(--sd-primary)!important}.bg-primary{background:var(--sd-primary)!important}.border-primary{border-color:var(--sd-primary)!important}.text-primary-light{color:var(--sd-primary-light)!important}.bg-primary-light{background:var(--sd-primary-light)!important}.border-primary-light{border-color:var(--sd-primary-light)!important}.text-primary-dark{color:var(--sd-primary-dark)!important}.bg-primary-dark{background:var(--sd-primary-dark)!important}.border-primary-dark{border-color:var(--sd-primary-dark)!important}.text-info{color:var(--sd-info)!important}.bg-info{background:var(--sd-info)!important}.border-info{border-color:var(--sd-info)!important}.text-info-light{color:var(--sd-info-light)!important}.bg-info-light{background:var(--sd-info-light)!important}.border-info-light{border-color:var(--sd-info-light)!important}.text-info-dark{color:var(--sd-info-dark)!important}.bg-info-dark{background:var(--sd-info-dark)!important}.border-info-dark{border-color:var(--sd-info-dark)!important}.text-success{color:var(--sd-success)!important}.bg-success{background:var(--sd-success)!important}.border-success{border-color:var(--sd-success)!important}.text-success-light{color:var(--sd-success-light)!important}.bg-success-light{background:var(--sd-success-light)!important}.border-success-light{border-color:var(--sd-success-light)!important}.text-success-dark{color:var(--sd-success-dark)!important}.bg-success-dark{background:var(--sd-success-dark)!important}.border-success-dark{border-color:var(--sd-success-dark)!important}.text-warning{color:var(--sd-warning)!important}.bg-warning{background:var(--sd-warning)!important}.border-warning{border-color:var(--sd-warning)!important}.text-warning-light{color:var(--sd-warning-light)!important}.bg-warning-light{background:var(--sd-warning-light)!important}.border-warning-light{border-color:var(--sd-warning-light)!important}.text-warning-dark{color:var(--sd-warning-dark)!important}.bg-warning-dark{background:var(--sd-warning-dark)!important}.border-warning-dark{border-color:var(--sd-warning-dark)!important}.text-error{color:var(--sd-error)!important}.bg-error{background:var(--sd-error)!important}.border-error{border-color:var(--sd-error)!important}.text-error-light{color:var(--sd-error-light)!important}.bg-error-light{background:var(--sd-error-light)!important}.border-error-light{border-color:var(--sd-error-light)!important}.text-error-dark{color:var(--sd-error-dark)!important}.bg-error-dark{background:var(--sd-error-dark)!important}.border-error-dark{border-color:var(--sd-error-dark)!important}.text-secondary{color:var(--sd-secondary)!important}.bg-secondary{background:var(--sd-secondary)!important}.border-secondary{border-color:var(--sd-secondary)!important}.text-secondary-light{color:var(--sd-secondary-light)!important}.bg-secondary-light{background:var(--sd-secondary-light)!important}.border-secondary-light{border-color:var(--sd-secondary-light)!important}.text-secondary-dark{color:var(--sd-secondary-dark)!important}.bg-secondary-dark{background:var(--sd-secondary-dark)!important}.border-secondary-dark{border-color:var(--sd-secondary-dark)!important}.text-light{color:var(--sd-light)!important}.bg-light{background:var(--sd-light)!important}.border-light{border-color:var(--sd-light)!important}.text-dark{color:var(--sd-dark)!important}.bg-dark{background:var(--sd-dark)!important}.border-dark{border-color:var(--sd-dark)!important}.text-black500{color:var(--sd-black500)!important}.bg-black500{background:var(--sd-black500)!important}.border-black500{border-color:var(--sd-black500)!important}.text-black400{color:var(--sd-black400)!important}.bg-black400{background:var(--sd-black400)!important}.border-black400{border-color:var(--sd-black400)!important}.text-black300{color:var(--sd-black300)!important}.bg-black300{background:var(--sd-black300)!important}.border-black300{border-color:var(--sd-black300)!important}.text-black200{color:var(--sd-black200)!important}.bg-black200{background:var(--sd-black200)!important}.border-black200{border-color:var(--sd-black200)!important}.text-black100{color:var(--sd-black100)!important}.bg-black100{background:var(--sd-black100)!important}.border-black100{border-color:var(--sd-black100)!important}.text-white{color:#fff!important}.bg-white{background:#fff!important}.border-white{border-color:#fff!important}.text-black{color:#000!important}.bg-black{background:#000!important}.border-black{border-color:#000!important}.side-drawer{position:fixed;right:0;top:0;bottom:0;background-color:#fff;z-index:999;display:flex;flex-direction:column;opacity:0;visibility:hidden;transform:translate3d(100%,0,0);transition:all .3s ease-in-out;pointer-events:none;box-shadow:0 .4px .8px #0000001a,0 3px 6px #0003}.c-action{color:var(--sd-secondary)}.c-action:focus{outline:none}.side-drawer__icon{border:0;background:transparent;color:#212121;overflow:hidden;outline:none}.side-drawer__icon .mat-icon{float:left}.side-drawer__header{min-height:64px;padding:12px 16px;display:flex;justify-content:space-between}.side-drawer__body{flex:1;position:relative}.side-drawer__content{position:absolute;padding:0 16px 16px;inset:0;overflow:auto}.side-drawer__footer{border-top:1px solid #dde0e5;padding:8px 0}.side-drawer-backdrop{background-color:#0006;border:0;outline:none!important;position:fixed;inset:0;z-index:99;width:100%;height:100%;transition:all .3s ease-in-out}.side-drawer--active{opacity:1;visibility:visible;pointer-events:all;transform:translateZ(0)}.side-drawer__title{font-size:20px;line-height:28px;font-weight:500}\n"] }]
118
- }], ctorParameters: () => [{ type: i0.ViewContainerRef }, { type: i0.ApplicationRef }, { type: i0.Injector }, { type: i0.ChangeDetectorRef }, { type: i1.SdLoadingService }], propDecorators: { portal: [{
119
- type: ViewChild,
120
- args: [CdkPortal]
121
- }], _title: [{
122
- type: Input,
123
- args: ['title']
124
- }], _width: [{
125
- type: Input,
126
- args: ['width']
127
- }], _hideClose: [{
128
- type: Input,
129
- args: ['hideClose']
130
- }], _disableClose: [{
131
- type: Input,
132
- args: ['disableClose']
133
- }], sdClosed: [{
134
- type: Output
135
- }] } });
115
+ args: [{ selector: 'sd-side-drawer', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, PortalModule], template: "<ng-template cdkPortal>\r\n <div\r\n [id]=\"id\"\r\n class=\"sd-side-drawer\"\r\n [ngStyle]=\"{ width: width() }\"\r\n [ngClass]=\"drawerClass()\"\r\n [class.sd-side-drawer-active]=\"isOpened\"\r\n #drawerContainer>\r\n @if (isOpened) {\r\n <ng-container>\r\n <div class=\"sd-side-drawer-header\">\r\n <div class=\"sd-side-drawer-title\">\r\n {{ title() }}\r\n <ng-content select=\"[sdHeaderLeft]\"></ng-content>\r\n </div>\r\n <div class=\"sd-side-drawer-header-actions\">\r\n <ng-content select=\"[sdHeaderRight]\"></ng-content>\r\n @if (!hideClose()) {\r\n <button type=\"button\" class=\"sd-side-drawer-close-btn\" (click)=\"close()\" aria-label=\"Close\">\r\n <svg viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" stroke=\"currentColor\" stroke-width=\"2\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\r\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\r\n </svg>\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n @if (isOpened) {\r\n <div class=\"sd-side-drawer-body\">\r\n <div class=\"sd-side-drawer-content\">\r\n <ng-content></ng-content>\r\n </div>\r\n </div>\r\n }\r\n <div class=\"sd-side-drawer-footer\">\r\n <ng-content select=\"[sdFooter]\"></ng-content>\r\n </div>\r\n </ng-container>\r\n }\r\n </div>\r\n @if (isOpened) {\r\n <button \r\n type=\"button\"\r\n class=\"sd-side-drawer-backdrop\" \r\n (click)=\"disableBackdropClose() ? null : close()\"\r\n (wheel)=\"preventScroll($event)\"\r\n (touchmove)=\"preventScroll($event)\"\r\n >\r\n </button>\r\n }\r\n</ng-template>\r\n", styles: [".sd-side-drawer{position:fixed;right:0;top:0;bottom:0;background-color:#fff;z-index:999;display:flex;flex-direction:column;opacity:0;visibility:hidden;transform:translate3d(100%,0,0);transition:all .3s ease-in-out;pointer-events:none;box-shadow:0 .4px .8px #0000001a,0 3px 6px #0003}.sd-side-drawer-active{opacity:1;visibility:visible;pointer-events:all;transform:translateZ(0)}.sd-side-drawer-header{min-height:64px;padding:12px 16px;display:flex;align-items:center;justify-content:space-between}.sd-side-drawer-header-actions{display:flex;align-items:center;gap:8px}.sd-side-drawer-title{font-size:20px;line-height:28px;font-weight:500;flex:1;margin-right:16px}.sd-side-drawer-close-btn{background:transparent;border:none;padding:8px;cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--sd-secondary-color, #666);border-radius:50%;transition:background-color .2s,color .2s}.sd-side-drawer-close-btn:hover{background-color:#0000000a;color:var(--sd-primary-color, #333)}.sd-side-drawer-close-btn:focus{outline:none;background-color:#00000014}.sd-side-drawer-body{flex:1;position:relative}.sd-side-drawer-content{position:absolute;padding:0 16px 16px;inset:0;overflow:auto}.sd-side-drawer-footer{border-top:1px solid #dde0e5;padding:8px 0}.sd-side-drawer-backdrop{background-color:#0006;border:0;outline:none!important;position:fixed;inset:0;z-index:99;width:100%;height:100%;transition:all .3s ease-in-out}\n"] }]
116
+ }], ctorParameters: () => [] });
136
117
 
137
118
  /**
138
119
  * Generated bundle index. Do not edit.
@@ -1 +1 @@
1
- {"version":3,"file":"sd-angular-core-components-side-drawer.mjs","sources":["../../../projects/sd-angular/components/side-drawer/src/side-drawer.component.ts","../../../projects/sd-angular/components/side-drawer/src/side-drawer.component.html","../../../projects/sd-angular/components/side-drawer/sd-angular-core-components-side-drawer.ts"],"sourcesContent":["import { CdkPortal, DomPortalOutlet, PortalModule } from '@angular/cdk/portal';\r\nimport { CommonModule } from '@angular/common';\r\nimport {\r\n AfterViewInit,\r\n ApplicationRef,\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n EmbeddedViewRef,\r\n EventEmitter,\r\n Injector,\r\n Input,\r\n OnDestroy,\r\n Output,\r\n ViewChild,\r\n ViewContainerRef,\r\n} from '@angular/core';\r\nimport { MatButtonModule } from '@angular/material/button';\r\nimport { MatDialogModule } from '@angular/material/dialog';\r\nimport { MatIconModule } from '@angular/material/icon';\r\nimport { SdBaseSecureComponent } from '@sd-angular/core/components/base';\r\nimport { SdLoadingService } from '@sd-angular/core/services';\r\nimport * as uuid from 'uuid';\r\nimport { fromEvent, merge, Observable, Subject } from 'rxjs';\r\nimport { map, takeUntil, startWith, distinctUntilChanged } from 'rxjs/operators';\r\n\r\n@Component({\r\n selector: 'sd-side-drawer',\r\n templateUrl: './side-drawer.component.html',\r\n styleUrls: ['./side-drawer.component.scss'],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n standalone: true,\r\n imports: [CommonModule, MatIconModule, MatDialogModule, MatButtonModule, PortalModule],\r\n})\r\nexport class SdSideDrawer extends SdBaseSecureComponent implements AfterViewInit, OnDestroy {\r\n id = `I${uuid.v4()}`;\r\n \r\n @ViewChild(CdkPortal) portal!: CdkPortal;\r\n \r\n title = '';\r\n @Input('title') set _title(val: string | null | undefined) {\r\n if (val) {\r\n this.title = val;\r\n }\r\n }\r\n\r\n width = '480px';\r\n @Input('width') set _width(val: string | null | undefined) {\r\n if (val) {\r\n this.width = val;\r\n }\r\n }\r\n\r\n hideClose = false;\r\n @Input('hideClose') set _hideClose(val: boolean | '' | null | undefined) {\r\n this.hideClose = val === '' || !!val;\r\n }\r\n\r\n disableClose = false;\r\n @Input('disableClose') set _disableClose(val: boolean | '' | null | undefined) {\r\n this.disableClose = val === '' || !!val;\r\n }\r\n\r\n @Output() sdClosed = new EventEmitter();\r\n\r\n #embeddedViewRef!: EmbeddedViewRef<any>;\r\n isOpened = false;\r\n isLoading = false;\r\n isHovered$!: Observable<boolean>;\r\n #destroy$ = new Subject<void>();\r\n\r\n constructor(\r\n private viewContainerRef: ViewContainerRef,\r\n private ar: ApplicationRef,\r\n private injector: Injector,\r\n private ref: ChangeDetectorRef,\r\n private loadingService: SdLoadingService\r\n ) {\r\n super();\r\n }\r\n\r\n ngAfterViewInit() {\r\n // 1. Gắn portal vào body và lưu lại EmbeddedViewRef\r\n const outlet = new DomPortalOutlet(document.body, this.viewContainerRef, this.ar, this.injector);\r\n this.#embeddedViewRef = outlet.attachTemplatePortal(this.portal);\r\n \r\n // 2. Setup sự kiện hover ngay sau khi DOM thật đã được in ra\r\n this.#setupHoverSubscription();\r\n }\r\n\r\n open = () => {\r\n this.ref.markForCheck();\r\n this.isOpened = true;\r\n };\r\n\r\n close = () => {\r\n this.ref.markForCheck();\r\n this.isOpened = false;\r\n this.sdClosed.emit();\r\n this.stopLoading();\r\n };\r\n\r\n startLoading = () => {\r\n this.isLoading = true;\r\n this.loadingService.stop(`#${this.id}`);\r\n this.loadingService.start(`#${this.id}`);\r\n };\r\n\r\n stopLoading = () => {\r\n this.isLoading = false;\r\n this.loadingService.stop(`#${this.id}`);\r\n };\r\n\r\n ngOnDestroy() {\r\n // Nhớ clear up subject để tránh memory leak từ RxJS\r\n this.#destroy$.next();\r\n this.#destroy$.complete();\r\n \r\n if (this.#embeddedViewRef) {\r\n this.#embeddedViewRef.destroy();\r\n }\r\n }\r\n\r\n #setupHoverSubscription(): void {\r\n if (!this.#embeddedViewRef) return;\r\n\r\n // 3. Lấy DOM element trực tiếp từ rootNodes của EmbeddedViewRef\r\n const rootNodes = this.#embeddedViewRef.rootNodes;\r\n const element = rootNodes.find(\r\n (node) => node.nodeType === Node.ELEMENT_NODE && (node as HTMLElement).classList?.contains('side-drawer')\r\n );\r\n\r\n if (!element) {\r\n console.warn('SdSideDrawer: Cannot find side-drawer element to attach hover event');\r\n return;\r\n }\r\n\r\n // 4. Gắn event listeners trực tiếp lên element thật\r\n const mouseEnter$ = fromEvent(element, 'mouseenter').pipe(map(() => true));\r\n const mouseLeave$ = fromEvent(element, 'mouseleave').pipe(map(() => false));\r\n\r\n this.isHovered$ = merge(mouseEnter$, mouseLeave$).pipe(\r\n startWith(false),\r\n distinctUntilChanged(),\r\n takeUntil(this.#destroy$)\r\n );\r\n }\r\n\r\n getHoverStatusStream(): Observable<boolean> {\r\n return this.isHovered$;\r\n }\r\n}","<ng-template cdkPortal>\r\n <div\r\n [id]=\"id\"\r\n class=\"side-drawer\"\r\n [ngStyle]=\"{ width: width }\"\r\n [ngClass]=\"{\r\n 'side-drawer--active': isOpened,\r\n 'side-drawer--loading': isLoading\r\n }\"\r\n #drawerContainer>\r\n @if (isOpened) {\r\n <ng-container>\r\n <div class=\"side-drawer__header media align-items-center\">\r\n <div class=\"side-drawer__title media-body\">\r\n <ng-content select=\"[sdTitle]\"></ng-content>\r\n </div>\r\n @if (hideClose) {\r\n <button type=\"button\" class=\"c-action\" (click)=\"close()\" mat-icon-button>\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n }\r\n </div>\r\n @if (isOpened) {\r\n <div class=\"side-drawer__body\">\r\n <div class=\"side-drawer__content\">\r\n <ng-content select=\"[sdBody]\"></ng-content>\r\n </div>\r\n </div>\r\n }\r\n <div class=\"side-drawer__footer\">\r\n <ng-content select=\"[sdFooter]\"></ng-content>\r\n </div>\r\n </ng-container>\r\n }\r\n </div>\r\n @if (isOpened) {\r\n <button \r\n type=\"button\"\r\n class=\"side-drawer-backdrop\" \r\n (click)=\"disableClose ? null : close()\"\r\n >\r\n </button>\r\n }\r\n</ng-template>\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sd-angular-core-components-side-drawer.mjs","sources":["../../../projects/sd-angular/components/side-drawer/src/side-drawer.component.ts","../../../projects/sd-angular/components/side-drawer/src/side-drawer.component.html","../../../projects/sd-angular/components/side-drawer/sd-angular-core-components-side-drawer.ts"],"sourcesContent":["import { CdkPortal, DomPortalOutlet, PortalModule } from '@angular/cdk/portal';\r\nimport { CommonModule } from '@angular/common';\r\nimport {\r\n ApplicationRef,\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n DestroyRef,\r\n EmbeddedViewRef,\r\n Injector,\r\n ViewContainerRef,\r\n afterNextRender,\r\n booleanAttribute,\r\n inject,\r\n input,\r\n output,\r\n viewChild\r\n} from '@angular/core';\r\nimport { SdBaseSecureComponent } from '@sd-angular/core/components/base';\r\nimport { SdLoadingService } from '@sd-angular/core/services';\r\nimport * as uuid from 'uuid';\r\nimport { fromEvent, merge, Observable, Subject } from 'rxjs';\r\nimport { map, takeUntil, startWith, distinctUntilChanged } from 'rxjs/operators';\r\n\r\n@Component({\r\n selector: 'sd-side-drawer',\r\n templateUrl: './side-drawer.component.html',\r\n styleUrls: ['./side-drawer.component.scss'],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n standalone: true,\r\n imports: [CommonModule, PortalModule],\r\n})\r\nexport class SdSideDrawer extends SdBaseSecureComponent {\r\n id = `I${uuid.v4()}`;\r\n \r\n portal = viewChild.required(CdkPortal);\r\n \r\n title = input<string>('');\r\n width = input<string>('480px');\r\n hideClose = input<boolean, boolean | ''>(false, { transform: booleanAttribute });\r\n disableBackdropClose = input<boolean, boolean | ''>(false, { transform: booleanAttribute });\r\n\r\n // Custom CSS class added to the root side-drawer container \r\n drawerClass = input<any>('');\r\n\r\n sdClosed = output<void>();\r\n\r\n #embeddedViewRef!: EmbeddedViewRef<any>;\r\n isOpened = false;\r\n isLoading = false;\r\n isHovered$!: Observable<boolean>;\r\n #destroy$ = new Subject<void>();\r\n #previousBodyOverflow: string | null = null;\r\n\r\n #viewContainerRef = inject(ViewContainerRef);\r\n #ar = inject(ApplicationRef);\r\n #injector = inject(Injector);\r\n #ref = inject(ChangeDetectorRef);\r\n #loadingService = inject(SdLoadingService);\r\n #destroyRef = inject(DestroyRef);\r\n\r\n constructor() {\r\n super();\r\n\r\n // Thay thế ngAfterViewInit, tự động chạy nội dung này khi DOM sẵn sàng để render\r\n afterNextRender(() => {\r\n // 1. Gắn portal vào body và lưu lại EmbeddedViewRef\r\n const outlet = new DomPortalOutlet(document.body, this.#viewContainerRef, this.#ar, this.#injector);\r\n this.#embeddedViewRef = outlet.attachTemplatePortal(this.portal());\r\n \r\n // 2. Setup sự kiện hover ngay sau khi DOM thật đã được in ra\r\n this.#setupHoverSubscription();\r\n });\r\n\r\n // Thay thế ngOnDestroy bằng logic destroy trực tiếp \r\n this.#destroyRef.onDestroy(() => {\r\n this.#destroy$.next();\r\n this.#destroy$.complete();\r\n \r\n if (this.#embeddedViewRef) {\r\n this.#embeddedViewRef.destroy();\r\n }\r\n \r\n if (this.isOpened) {\r\n if (this.#previousBodyOverflow !== null) {\r\n document.body.style.overflow = this.#previousBodyOverflow;\r\n } else {\r\n document.body.style.overflow = '';\r\n }\r\n }\r\n });\r\n }\r\n\r\n open = () => {\r\n this.#ref.markForCheck();\r\n this.isOpened = true;\r\n \r\n // Chặn scroll ở document body\r\n this.#previousBodyOverflow = document.body.style.overflow;\r\n document.body.style.overflow = 'hidden';\r\n };\r\n\r\n close = () => {\r\n this.#ref.markForCheck();\r\n this.isOpened = false;\r\n this.sdClosed.emit();\r\n this.stopLoading();\r\n \r\n // Khôi phục lại scroll ở document body\r\n if (this.#previousBodyOverflow !== null) {\r\n document.body.style.overflow = this.#previousBodyOverflow;\r\n this.#previousBodyOverflow = null;\r\n } else {\r\n document.body.style.overflow = '';\r\n }\r\n };\r\n\r\n startLoading = () => {\r\n this.isLoading = true;\r\n this.#loadingService.stop(`#${this.id}`);\r\n this.#loadingService.start(`#${this.id}`);\r\n };\r\n\r\n stopLoading = () => {\r\n this.isLoading = false;\r\n this.#loadingService.stop(`#${this.id}`);\r\n };\r\n\r\n preventScroll = (event: Event) => {\r\n event.preventDefault();\r\n };\r\n\r\n #setupHoverSubscription(): void {\r\n if (!this.#embeddedViewRef) return;\r\n\r\n // 3. Lấy DOM element trực tiếp từ rootNodes của EmbeddedViewRef\r\n const rootNodes = this.#embeddedViewRef.rootNodes;\r\n const element = rootNodes.find(\r\n (node) => node.nodeType === Node.ELEMENT_NODE && (node as HTMLElement).classList?.contains('sd-side-drawer')\r\n );\r\n\r\n if (!element) {\r\n console.warn('SdSideDrawer: Cannot find sd-side-drawer element to attach hover event');\r\n return;\r\n }\r\n\r\n // 4. Gắn event listeners trực tiếp lên element thật\r\n const mouseEnter$ = fromEvent(element, 'mouseenter').pipe(map(() => true));\r\n const mouseLeave$ = fromEvent(element, 'mouseleave').pipe(map(() => false));\r\n\r\n this.isHovered$ = merge(mouseEnter$, mouseLeave$).pipe(\r\n startWith(false),\r\n distinctUntilChanged(),\r\n takeUntil(this.#destroy$)\r\n );\r\n }\r\n}","<ng-template cdkPortal>\r\n <div\r\n [id]=\"id\"\r\n class=\"sd-side-drawer\"\r\n [ngStyle]=\"{ width: width() }\"\r\n [ngClass]=\"drawerClass()\"\r\n [class.sd-side-drawer-active]=\"isOpened\"\r\n #drawerContainer>\r\n @if (isOpened) {\r\n <ng-container>\r\n <div class=\"sd-side-drawer-header\">\r\n <div class=\"sd-side-drawer-title\">\r\n {{ title() }}\r\n <ng-content select=\"[sdHeaderLeft]\"></ng-content>\r\n </div>\r\n <div class=\"sd-side-drawer-header-actions\">\r\n <ng-content select=\"[sdHeaderRight]\"></ng-content>\r\n @if (!hideClose()) {\r\n <button type=\"button\" class=\"sd-side-drawer-close-btn\" (click)=\"close()\" aria-label=\"Close\">\r\n <svg viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" stroke=\"currentColor\" stroke-width=\"2\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\r\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\r\n </svg>\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n @if (isOpened) {\r\n <div class=\"sd-side-drawer-body\">\r\n <div class=\"sd-side-drawer-content\">\r\n <ng-content></ng-content>\r\n </div>\r\n </div>\r\n }\r\n <div class=\"sd-side-drawer-footer\">\r\n <ng-content select=\"[sdFooter]\"></ng-content>\r\n </div>\r\n </ng-container>\r\n }\r\n </div>\r\n @if (isOpened) {\r\n <button \r\n type=\"button\"\r\n class=\"sd-side-drawer-backdrop\" \r\n (click)=\"disableBackdropClose() ? null : close()\"\r\n (wheel)=\"preventScroll($event)\"\r\n (touchmove)=\"preventScroll($event)\"\r\n >\r\n </button>\r\n }\r\n</ng-template>\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -4,7 +4,7 @@ import * as i4 from '@angular/cdk/drag-drop';
4
4
  import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop';
5
5
  import * as i1 from '@angular/common';
6
6
  import { CommonModule } from '@angular/common';
7
- import { NgForm, FormGroup, Validators } from '@angular/forms';
7
+ import { NgForm, FormGroup } from '@angular/forms';
8
8
  import * as i2 from '@angular/material/button';
9
9
  import { MatButtonModule } from '@angular/material/button';
10
10
  import * as i3 from '@angular/material/icon';
@@ -21,7 +21,7 @@ import { SdButton } from '@sd-angular/core/components/button';
21
21
  import { SdModal } from '@sd-angular/core/components/modal';
22
22
  import { SdUtilities } from '@sd-angular/core/utilities';
23
23
 
24
- const SD_UPLOAD_FILE_CONFIG = new InjectionToken('sd.upload-file.configuration');
24
+ const SD_UPLOAD_FILE_CONFIGURATION = new InjectionToken('sd.upload-file.configuration');
25
25
 
26
26
  class PreviewComponent {
27
27
  cd;
@@ -123,11 +123,8 @@ const IsImage = (extension) => {
123
123
  return IMAGE_EXTENSIONS.includes(extension?.toLowerCase());
124
124
  };
125
125
 
126
- /* eslint-disable @typescript-eslint/no-explicit-any */
127
- // import { sha1 } from 'object-hash';
128
126
  class UploadFileService {
129
127
  #cache = {};
130
- constructor() { }
131
128
  #hash = (file) => {
132
129
  if (!file) {
133
130
  return null;
@@ -174,7 +171,7 @@ class UploadFileService {
174
171
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: UploadFileService, decorators: [{
175
172
  type: Injectable,
176
173
  args: [{ providedIn: 'root' }]
177
- }], ctorParameters: () => [] });
174
+ }] });
178
175
 
179
176
  /* eslint-disable @angular-eslint/no-input-rename */
180
177
  /* eslint-disable @typescript-eslint/no-explicit-any */
@@ -183,7 +180,7 @@ class SdUploadFile {
183
180
  // ─── Injected Services ────────────────────────────────────────────────
184
181
  #notifyService = inject(SdNotifyService);
185
182
  #confirmService = inject(SdConfirmService);
186
- #uploadFileConfig = inject(SD_UPLOAD_FILE_CONFIG);
183
+ #uploadFileConfig = inject(SD_UPLOAD_FILE_CONFIGURATION, { optional: true });
187
184
  #uploadFileService = inject(UploadFileService);
188
185
  #destroyRef = inject(DestroyRef);
189
186
  // ─── Internal State ───────────────────────────────────────────────────
@@ -193,7 +190,6 @@ class SdUploadFile {
193
190
  #canvas2 = `C${uuid.v4()}`;
194
191
  #name = uuid.v4();
195
192
  #formGroup;
196
- #currentDropContainer;
197
193
  formControl = new SdFormControl();
198
194
  // ─── Signals (state) ──────────────────────────────────────────────────
199
195
  previewFiles = signal([]);
@@ -336,7 +332,6 @@ class SdUploadFile {
336
332
  }
337
333
  }
338
334
  #bindDropEvents(dropContainer) {
339
- this.#currentDropContainer = dropContainer;
340
335
  dropContainer.addEventListener('dragover', (evt) => {
341
336
  evt.preventDefault();
342
337
  dropContainer.style.opacity = 0.9;
@@ -365,18 +360,18 @@ class SdUploadFile {
365
360
  this.formControl.markAsTouched();
366
361
  this.formControl.setValue(this.model() || []);
367
362
  };
368
- #updateValidator = () => {
369
- this.formControl.clearValidators();
370
- this.formControl.clearAsyncValidators();
371
- const validators = [];
372
- const asyncValidators = [];
373
- if (this.required()) {
374
- validators.push(Validators.required);
375
- }
376
- this.formControl.setValidators(validators);
377
- this.formControl.setAsyncValidators(asyncValidators);
378
- this.formControl.updateValueAndValidity();
379
- };
363
+ // #updateValidator = () => {
364
+ // this.formControl.clearValidators();
365
+ // this.formControl.clearAsyncValidators();
366
+ // const validators: ValidatorFn[] = [];
367
+ // const asyncValidators: AsyncValidatorFn[] = [];
368
+ // if (this.required()) {
369
+ // validators.push(Validators.required);
370
+ // }
371
+ // this.formControl.setValidators(validators);
372
+ // this.formControl.setAsyncValidators(asyncValidators);
373
+ // this.formControl.updateValueAndValidity();
374
+ // };
380
375
  #validate = async (file) => {
381
376
  if (this.type() === 'image') {
382
377
  if (file.type.split('/')[0] !== 'image') {
@@ -616,12 +611,17 @@ class SdUploadFile {
616
611
  const uploadedFiles = items.map(e => e?.file).filter(file => !!file);
617
612
  let idOrKeys = [];
618
613
  if (uploadedFiles.length) {
619
- idOrKeys = await this.#uploadFileConfig.upload(uploadedFiles, this.args()).then(results => {
620
- for (const uploadedFile of uploadedFiles) {
621
- this.#uploadFileService.remove(uploadedFile);
622
- }
623
- return results;
624
- });
614
+ if (this.#uploadFileConfig) {
615
+ idOrKeys = await this.#uploadFileConfig.upload(uploadedFiles, this.args()).then(results => {
616
+ for (const uploadedFile of uploadedFiles) {
617
+ this.#uploadFileService.remove(uploadedFile);
618
+ }
619
+ return results;
620
+ });
621
+ }
622
+ else {
623
+ this.#notifyService.error(`Vui lòng inject SD_UPLOAD_FILE_CONFIGURATION`);
624
+ }
625
625
  }
626
626
  let idx = 0;
627
627
  for (const item of items) {
@@ -679,20 +679,12 @@ class SdUploadFile {
679
679
  }
680
680
  const idOrKeys = keys.filter(key => !!key && !this.#isCdn(key) && !this.#isHashedKey(key));
681
681
  let detailsList = [];
682
- const detailsFn = this.details();
683
- if (idOrKeys.length) {
684
- if (detailsFn) {
685
- detailsList = await detailsFn(idOrKeys, this.args()).catch(error => {
686
- console.error(error);
687
- return [];
688
- });
689
- }
690
- else if (this.#uploadFileConfig.details) {
691
- detailsList = await this.#uploadFileConfig.details(idOrKeys, this.args()).catch(error => {
692
- console.error(error);
693
- return [];
694
- });
695
- }
682
+ const details = this.details() || this.#uploadFileConfig?.details;
683
+ if (idOrKeys.length && details) {
684
+ detailsList = await details(idOrKeys, this.args()).catch(error => {
685
+ console.error(error);
686
+ return [];
687
+ });
696
688
  }
697
689
  const results = [];
698
690
  for (const key of keys.filter(val => !!val)) {
@@ -808,5 +800,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
808
800
  * Generated bundle index. Do not edit.
809
801
  */
810
802
 
811
- export { SD_UPLOAD_FILE_CONFIG, SdUploadFile };
803
+ export { SD_UPLOAD_FILE_CONFIGURATION, SdUploadFile };
812
804
  //# sourceMappingURL=sd-angular-core-components-upload-file.mjs.map