@sdcorejs/angular 21.1.2 → 21.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +127 -807
- package/fesm2022/sdcorejs-angular-components-anchor.mjs +14 -14
- package/fesm2022/sdcorejs-angular-components-anchor.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-audit-diff.mjs +332 -0
- package/fesm2022/sdcorejs-angular-components-audit-diff.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-badge.mjs +1 -0
- package/fesm2022/sdcorejs-angular-components-badge.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-breadcrumb.mjs +121 -0
- package/fesm2022/sdcorejs-angular-components-breadcrumb.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-data-state.mjs +68 -0
- package/fesm2022/sdcorejs-angular-components-data-state.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-document-builder.mjs +18 -23
- package/fesm2022/sdcorejs-angular-components-document-builder.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-editor.mjs +6 -4
- package/fesm2022/sdcorejs-angular-components-editor.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-form-generic.mjs +633 -886
- package/fesm2022/sdcorejs-angular-components-form-generic.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-import-excel.mjs +26 -41
- package/fesm2022/sdcorejs-angular-components-import-excel.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-job-progress.mjs +97 -0
- package/fesm2022/sdcorejs-angular-components-job-progress.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-mini-editor.mjs +10 -20
- package/fesm2022/sdcorejs-angular-components-mini-editor.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-modal.mjs +61 -4
- package/fesm2022/sdcorejs-angular-components-modal.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-preview.mjs +1438 -218
- package/fesm2022/sdcorejs-angular-components-preview.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-query-bar.mjs +9 -17
- package/fesm2022/sdcorejs-angular-components-query-bar.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-query-builder.mjs +1 -1
- package/fesm2022/sdcorejs-angular-components-query-builder.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-side-drawer.mjs +39 -2
- package/fesm2022/sdcorejs-angular-components-side-drawer.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-tab-router.mjs +76 -52
- package/fesm2022/sdcorejs-angular-components-tab-router.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-tab.mjs +48 -4
- package/fesm2022/sdcorejs-angular-components-tab.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-table.mjs +123 -89
- package/fesm2022/sdcorejs-angular-components-table.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-tree.mjs +188 -18
- package/fesm2022/sdcorejs-angular-components-tree.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-upload-file.mjs +17 -23
- package/fesm2022/sdcorejs-angular-components-upload-file.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components.mjs +4 -0
- package/fesm2022/sdcorejs-angular-components.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-directives.mjs +29 -41
- package/fesm2022/sdcorejs-angular-directives.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs +9 -8
- package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-checkbox.mjs +20 -71
- package/fesm2022/sdcorejs-angular-forms-checkbox.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs +8 -4
- package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-chip.mjs +8 -4
- package/fesm2022/sdcorejs-angular-forms-chip.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-date-range.mjs +18 -14
- package/fesm2022/sdcorejs-angular-forms-date-range.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-date.mjs +48 -70
- package/fesm2022/sdcorejs-angular-forms-date.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-datetime.mjs +62 -528
- package/fesm2022/sdcorejs-angular-forms-datetime.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-entity-picker.mjs +384 -0
- package/fesm2022/sdcorejs-angular-forms-entity-picker.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms-input-color.mjs +5 -3
- package/fesm2022/sdcorejs-angular-forms-input-color.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-input-number.mjs +16 -11
- package/fesm2022/sdcorejs-angular-forms-input-number.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-input.mjs +297 -59
- package/fesm2022/sdcorejs-angular-forms-input.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-models.mjs +155 -2
- package/fesm2022/sdcorejs-angular-forms-models.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-radio.mjs +44 -66
- package/fesm2022/sdcorejs-angular-forms-radio.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-select.mjs +8 -21
- package/fesm2022/sdcorejs-angular-forms-select.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-switch.mjs +19 -64
- package/fesm2022/sdcorejs-angular-forms-switch.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-textarea.mjs +8 -6
- package/fesm2022/sdcorejs-angular-forms-textarea.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-time-range.mjs +277 -0
- package/fesm2022/sdcorejs-angular-forms-time-range.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms-time.mjs +307 -0
- package/fesm2022/sdcorejs-angular-forms-time.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms-tree-select.mjs +210 -0
- package/fesm2022/sdcorejs-angular-forms-tree-select.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms.mjs +73 -4
- package/fesm2022/sdcorejs-angular-forms.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-i18n.mjs +355 -10
- package/fesm2022/sdcorejs-angular-i18n.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-interceptors.mjs +12 -17
- package/fesm2022/sdcorejs-angular-interceptors.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-modules-icon.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-modules-layout.mjs +861 -80
- package/fesm2022/sdcorejs-angular-modules-layout.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-modules.mjs +0 -1
- package/fesm2022/sdcorejs-angular-modules.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-pipes.mjs +5 -7
- package/fesm2022/sdcorejs-angular-pipes.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-api.mjs +407 -137
- package/fesm2022/sdcorejs-angular-services-api.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-cache.mjs +515 -105
- package/fesm2022/sdcorejs-angular-services-cache.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-confirm.mjs +5 -7
- package/fesm2022/sdcorejs-angular-services-confirm.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-docx.mjs +7 -10
- package/fesm2022/sdcorejs-angular-services-docx.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-loading.mjs +340 -66
- package/fesm2022/sdcorejs-angular-services-loading.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-notify.mjs +7 -10
- package/fesm2022/sdcorejs-angular-services-notify.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-persistence.mjs +899 -0
- package/fesm2022/sdcorejs-angular-services-persistence.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-services-storage.mjs +324 -122
- package/fesm2022/sdcorejs-angular-services-storage.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-task.mjs +389 -0
- package/fesm2022/sdcorejs-angular-services-task.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-services-unsaved-changes.mjs +255 -0
- package/fesm2022/sdcorejs-angular-services-unsaved-changes.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-services-viewport.mjs +76 -0
- package/fesm2022/sdcorejs-angular-services-viewport.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-services.mjs +4 -0
- package/fesm2022/sdcorejs-angular-services.mjs.map +1 -1
- package/package.json +50 -5
- package/types/sdcorejs-angular-components-audit-diff.d.ts +74 -0
- package/types/sdcorejs-angular-components-badge.d.ts +1 -1
- package/types/sdcorejs-angular-components-breadcrumb.d.ts +53 -0
- package/types/sdcorejs-angular-components-data-state.d.ts +44 -0
- package/types/sdcorejs-angular-components-document-builder.d.ts +5 -5
- package/types/sdcorejs-angular-components-form-generic.d.ts +89 -103
- package/types/sdcorejs-angular-components-import-excel.d.ts +4 -14
- package/types/sdcorejs-angular-components-job-progress.d.ts +38 -0
- package/types/sdcorejs-angular-components-mini-editor.d.ts +9 -9
- package/types/sdcorejs-angular-components-modal.d.ts +7 -1
- package/types/sdcorejs-angular-components-preview.d.ts +181 -28
- package/types/sdcorejs-angular-components-side-drawer.d.ts +7 -1
- package/types/sdcorejs-angular-components-tab.d.ts +9 -2
- package/types/sdcorejs-angular-components-table.d.ts +8 -7
- package/types/sdcorejs-angular-components-tree.d.ts +24 -2
- package/types/sdcorejs-angular-components-upload-file.d.ts +5 -4
- package/types/sdcorejs-angular-components.d.ts +4 -0
- package/types/sdcorejs-angular-directives.d.ts +10 -8
- package/types/sdcorejs-angular-forms-autocomplete.d.ts +3 -4
- package/types/sdcorejs-angular-forms-checkbox.d.ts +4 -8
- package/types/sdcorejs-angular-forms-date-range.d.ts +6 -7
- package/types/sdcorejs-angular-forms-date.d.ts +3 -3
- package/types/sdcorejs-angular-forms-datetime.d.ts +3 -3
- package/types/sdcorejs-angular-forms-entity-picker.d.ts +128 -0
- package/types/sdcorejs-angular-forms-input-color.d.ts +3 -1
- package/types/sdcorejs-angular-forms-input-number.d.ts +4 -2
- package/types/sdcorejs-angular-forms-input.d.ts +76 -11
- package/types/sdcorejs-angular-forms-models.d.ts +86 -3
- package/types/sdcorejs-angular-forms-radio.d.ts +12 -8
- package/types/sdcorejs-angular-forms-switch.d.ts +4 -7
- package/types/sdcorejs-angular-forms-time-range.d.ts +98 -0
- package/types/sdcorejs-angular-forms-time.d.ts +117 -0
- package/types/sdcorejs-angular-forms-tree-select.d.ts +76 -0
- package/types/sdcorejs-angular-forms.d.ts +15 -5
- package/types/sdcorejs-angular-i18n.d.ts +68 -2
- package/types/sdcorejs-angular-interceptors.d.ts +5 -6
- package/types/sdcorejs-angular-modules-icon.d.ts +2 -3
- package/types/sdcorejs-angular-modules-layout.d.ts +288 -15
- package/types/sdcorejs-angular-modules.d.ts +0 -1
- package/types/sdcorejs-angular-pipes.d.ts +2 -2
- package/types/sdcorejs-angular-services-api.d.ts +68 -24
- package/types/sdcorejs-angular-services-cache.d.ts +52 -16
- package/types/sdcorejs-angular-services-confirm.d.ts +3 -2
- package/types/sdcorejs-angular-services-docx.d.ts +2 -3
- package/types/sdcorejs-angular-services-loading.d.ts +16 -8
- package/types/sdcorejs-angular-services-persistence.d.ts +159 -0
- package/types/sdcorejs-angular-services-storage.d.ts +26 -8
- package/types/sdcorejs-angular-services-task.d.ts +104 -0
- package/types/sdcorejs-angular-services-unsaved-changes.d.ts +83 -0
- package/types/sdcorejs-angular-services-viewport.d.ts +42 -0
- package/types/sdcorejs-angular-services.d.ts +4 -0
- package/fesm2022/sdcorejs-angular-modules-authom.mjs +0 -360
- package/fesm2022/sdcorejs-angular-modules-authom.mjs.map +0 -1
- package/types/sdcorejs-angular-modules-authom.d.ts +0 -70
|
@@ -17,11 +17,13 @@ class SdSideDrawer {
|
|
|
17
17
|
width = input('480px', ...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
|
|
18
18
|
hideClose = input(false, { ...(ngDevMode ? { debugName: "hideClose" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
19
19
|
disableBackdropClose = input(false, { ...(ngDevMode ? { debugName: "disableBackdropClose" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
20
|
+
beforeClose = input(undefined, ...(ngDevMode ? [{ debugName: "beforeClose" }] : /* istanbul ignore next */ []));
|
|
20
21
|
// Custom CSS class added to the root side-drawer container
|
|
21
22
|
drawerClass = input('', ...(ngDevMode ? [{ debugName: "drawerClass" }] : /* istanbul ignore next */ []));
|
|
22
23
|
autoIdInput = input(undefined, { ...(ngDevMode ? { debugName: "autoIdInput" } : /* istanbul ignore next */ {}), alias: 'autoId' });
|
|
23
24
|
autoId = computed(() => (this.autoIdInput() ? `components-side-drawer-${this.autoIdInput()}` : undefined), ...(ngDevMode ? [{ debugName: "autoId" }] : /* istanbul ignore next */ []));
|
|
24
25
|
sdClosed = output();
|
|
26
|
+
sdCloseError = output();
|
|
25
27
|
#embeddedViewRef;
|
|
26
28
|
#isOpenedSignal = signal(false, ...(ngDevMode ? [{ debugName: "#isOpenedSignal" }] : /* istanbul ignore next */ []));
|
|
27
29
|
#isLoadingSignal = signal(false, ...(ngDevMode ? [{ debugName: "#isLoadingSignal" }] : /* istanbul ignore next */ []));
|
|
@@ -32,6 +34,7 @@ class SdSideDrawer {
|
|
|
32
34
|
isHovered$;
|
|
33
35
|
#destroy$ = new Subject();
|
|
34
36
|
#previousBodyOverflow = null;
|
|
37
|
+
#closeRequest;
|
|
35
38
|
#viewContainerRef = inject(ViewContainerRef);
|
|
36
39
|
#ar = inject(ApplicationRef);
|
|
37
40
|
#injector = inject(Injector);
|
|
@@ -72,6 +75,40 @@ class SdSideDrawer {
|
|
|
72
75
|
document.body.style.overflow = 'hidden';
|
|
73
76
|
};
|
|
74
77
|
close = () => {
|
|
78
|
+
if (this.beforeClose()) {
|
|
79
|
+
void this.requestClose();
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
this.forceClose();
|
|
83
|
+
};
|
|
84
|
+
requestClose = () => {
|
|
85
|
+
const guard = this.beforeClose();
|
|
86
|
+
if (!guard) {
|
|
87
|
+
this.forceClose();
|
|
88
|
+
return Promise.resolve(true);
|
|
89
|
+
}
|
|
90
|
+
if (this.#closeRequest)
|
|
91
|
+
return this.#closeRequest;
|
|
92
|
+
const request = Promise.resolve()
|
|
93
|
+
.then(() => guard())
|
|
94
|
+
.then(canClose => {
|
|
95
|
+
if (!canClose)
|
|
96
|
+
return false;
|
|
97
|
+
this.forceClose();
|
|
98
|
+
return true;
|
|
99
|
+
})
|
|
100
|
+
.catch((error) => {
|
|
101
|
+
this.sdCloseError.emit(error);
|
|
102
|
+
return false;
|
|
103
|
+
});
|
|
104
|
+
this.#closeRequest = request;
|
|
105
|
+
void request.finally(() => {
|
|
106
|
+
if (this.#closeRequest === request)
|
|
107
|
+
this.#closeRequest = undefined;
|
|
108
|
+
});
|
|
109
|
+
return request;
|
|
110
|
+
};
|
|
111
|
+
forceClose = () => {
|
|
75
112
|
this.#ref.markForCheck();
|
|
76
113
|
this.#isOpenedSignal.set(false);
|
|
77
114
|
this.sdClosed.emit();
|
|
@@ -113,12 +150,12 @@ class SdSideDrawer {
|
|
|
113
150
|
this.isHovered$ = merge(mouseEnter$, mouseLeave$).pipe(startWith(false), distinctUntilChanged(), takeUntil(this.#destroy$));
|
|
114
151
|
}
|
|
115
152
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdSideDrawer, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
116
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.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 }, autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sdClosed: "sdClosed" }, viewQueries: [{ propertyName: "portal", first: true, predicate: CdkPortal, descendants: true, isSignal: true }], ngImport: i0, template: "<ng-template cdkPortal>\n @let _isOpened = isOpened();\n <div\n [id]=\"id\"\n class=\"sd-side-drawer\"\n [ngStyle]=\"{ width: width() }\"\n [ngClass]=\"drawerClass()\"\n [class.sd-side-drawer-active]=\"_isOpened\"\n [attr.data-autoId]=\"autoId()\"\n [attr.data-opened]=\"dataOpened()\"\n [attr.data-loading]=\"dataLoading()\"\n #drawerContainer>\n @if (_isOpened) {\n <ng-container>\n <div class=\"sd-side-drawer-header\">\n <div class=\"sd-side-drawer-header-left\">\n <ng-content select=\"[sdHeaderLeft]\">\n <div class=\"sd-side-drawer-title\">{{ title() }}</div>\n </ng-content>\n </div>\n <div class=\"sd-side-drawer-header-right\">\n <ng-content select=\"[sdHeaderRight]\"></ng-content>\n @if (!hideClose()) {\n <button type=\"button\" class=\"sd-side-drawer-close-btn\" (click)=\"close()\" aria-label=\"Close\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n }\n </div>\n </div>\n @if (_isOpened) {\n <div class=\"sd-side-drawer-body\">\n <div class=\"sd-side-drawer-content\">\n <ng-content></ng-content>\n </div>\n </div>\n }\n <div class=\"sd-side-drawer-footer\">\n <div class=\"sd-side-drawer-footer-left\"><ng-content select=\"[sdFooterLeft]\"></ng-content></div>\n <div class=\"sd-side-drawer-footer-right\"><ng-content select=\"[sdFooterRight]\"></ng-content></div>\n </div>\n </ng-container>\n }\n </div>\n @if (_isOpened) {\n <div\n aria-hidden=\"true\"\n class=\"sd-side-drawer-backdrop\"\n (click)=\"disableBackdropClose() ? null : close()\"\n (wheel)=\"preventScroll($event)\"\n (touchmove)=\"preventScroll($event)\"></div>\n }\n</ng-template>\n", styles: [".sd-side-drawer{--sd-side-drawer-inset: 10px;--sd-side-drawer-radius: var(--sd-overlay-radius, 24px);--sd-side-drawer-surface: var(--sd-surface, #fff);--sd-side-drawer-border: var(--sd-border, #dde0e5);--sd-side-drawer-shadow: 0 6px 18px rgba(15, 23, 42, .18), 0 1px 3px rgba(15, 23, 42, .12);position:fixed;right:var(--sd-side-drawer-inset);top:var(--sd-side-drawer-inset);bottom:var(--sd-side-drawer-inset);max-width:calc(100vw - var(--sd-side-drawer-inset) - var(--sd-side-drawer-inset));background-color:var(--sd-side-drawer-surface);color:var(--sd-text);z-index:999;display:flex;flex-direction:column;opacity:0;visibility:hidden;transform:translate3d(calc(100% + var(--sd-side-drawer-inset)),0,0);transition:all .3s ease-in-out;pointer-events:none;overflow:hidden;border-radius:var(--sd-side-drawer-radius);box-shadow:var(--sd-side-drawer-shadow)}.sd-side-drawer-active{opacity:1;visibility:visible;pointer-events:all;transform:translateZ(0)}.sd-side-drawer-header{min-height:64px;padding:16px;display:flex;align-items:center;justify-content:space-between;gap:16px}.sd-side-drawer-header-left{display:flex;align-items:center;flex:1 1 auto;min-width:0}.sd-side-drawer-header-right{display:flex;align-items:center;gap:8px;flex:0 0 auto}.sd-side-drawer-title{font-size:20px;line-height:28px;font-weight:500;min-width:0}.sd-side-drawer-close-btn{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;box-sizing:border-box;width:32px;height:32px;padding:0;border:0;border-radius:50%;background:transparent;color:var(--sd-text-secondary);line-height:0;cursor:pointer;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;transition:background-color 80ms ease,color 80ms ease}.sd-side-drawer-close-btn:hover{background:var(--sd-surface-muted);color:var(--sd-text)}.sd-side-drawer-close-btn:active{background:var(--sd-border)}.sd-side-drawer-close-btn:focus{outline:none}.sd-side-drawer-close-btn:focus-visible{outline:2px solid var(--sd-primary);outline-offset:2px}.sd-side-drawer-close-btn sd-icon{display:block;width:20px;height:20px;font-size:20px;line-height:20px}.sd-side-drawer-body{flex:1;position:relative}.sd-side-drawer-content{position:absolute;padding:0;inset:0;overflow:auto}.sd-side-drawer-footer{border-top:1px solid var(--sd-side-drawer-border);padding:16px;background-color:var(--sd-side-drawer-surface);display:flex;align-items:center;justify-content:space-between;gap:16px}.sd-side-drawer-footer-left,.sd-side-drawer-footer-right{display:flex;align-items:center;gap:8px;min-width:0}.sd-side-drawer-footer-left{flex:1 1 auto}.sd-side-drawer-footer-right{flex:0 0 auto;margin-left:auto;justify-content:flex-end}.sd-side-drawer-footer-left:empty,.sd-side-drawer-footer-right:empty{display:none}.sd-side-drawer-footer:has(.sd-side-drawer-footer-left:empty):has(.sd-side-drawer-footer-right:empty){display:none}.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: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { 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 });
|
|
153
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.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 }, beforeClose: { classPropertyName: "beforeClose", publicName: "beforeClose", isSignal: true, isRequired: false, transformFunction: null }, drawerClass: { classPropertyName: "drawerClass", publicName: "drawerClass", isSignal: true, isRequired: false, transformFunction: null }, autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sdClosed: "sdClosed", sdCloseError: "sdCloseError" }, viewQueries: [{ propertyName: "portal", first: true, predicate: CdkPortal, descendants: true, isSignal: true }], ngImport: i0, template: "<ng-template cdkPortal>\n @let _isOpened = isOpened();\n <div\n [id]=\"id\"\n class=\"sd-side-drawer\"\n [ngStyle]=\"{ width: width() }\"\n [ngClass]=\"drawerClass()\"\n [class.sd-side-drawer-active]=\"_isOpened\"\n [attr.data-autoId]=\"autoId()\"\n [attr.data-opened]=\"dataOpened()\"\n [attr.data-loading]=\"dataLoading()\"\n #drawerContainer>\n @if (_isOpened) {\n <ng-container>\n <div class=\"sd-side-drawer-header\">\n <div class=\"sd-side-drawer-header-left\">\n <ng-content select=\"[sdHeaderLeft]\">\n <div class=\"sd-side-drawer-title\">{{ title() }}</div>\n </ng-content>\n </div>\n <div class=\"sd-side-drawer-header-right\">\n <ng-content select=\"[sdHeaderRight]\"></ng-content>\n @if (!hideClose()) {\n <button type=\"button\" class=\"sd-side-drawer-close-btn\" (click)=\"close()\" aria-label=\"Close\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n }\n </div>\n </div>\n @if (_isOpened) {\n <div class=\"sd-side-drawer-body\">\n <div class=\"sd-side-drawer-content\">\n <ng-content></ng-content>\n </div>\n </div>\n }\n <div class=\"sd-side-drawer-footer\">\n <div class=\"sd-side-drawer-footer-left\"><ng-content select=\"[sdFooterLeft]\"></ng-content></div>\n <div class=\"sd-side-drawer-footer-right\"><ng-content select=\"[sdFooterRight]\"></ng-content></div>\n </div>\n </ng-container>\n }\n </div>\n @if (_isOpened) {\n <div\n aria-hidden=\"true\"\n class=\"sd-side-drawer-backdrop\"\n (click)=\"disableBackdropClose() ? null : close()\"\n (wheel)=\"preventScroll($event)\"\n (touchmove)=\"preventScroll($event)\"></div>\n }\n</ng-template>\n", styles: [".sd-side-drawer{--sd-side-drawer-inset: 10px;--sd-side-drawer-radius: var(--sd-overlay-radius, 24px);--sd-side-drawer-surface: var(--sd-surface, #fff);--sd-side-drawer-border: var(--sd-border, #dde0e5);--sd-side-drawer-shadow: 0 6px 18px rgba(15, 23, 42, .18), 0 1px 3px rgba(15, 23, 42, .12);position:fixed;right:var(--sd-side-drawer-inset);top:var(--sd-side-drawer-inset);bottom:var(--sd-side-drawer-inset);max-width:calc(100vw - var(--sd-side-drawer-inset) - var(--sd-side-drawer-inset));background-color:var(--sd-side-drawer-surface);color:var(--sd-text);z-index:999;display:flex;flex-direction:column;opacity:0;visibility:hidden;transform:translate3d(calc(100% + var(--sd-side-drawer-inset)),0,0);transition:all .3s ease-in-out;pointer-events:none;overflow:hidden;border-radius:var(--sd-side-drawer-radius);box-shadow:var(--sd-side-drawer-shadow)}.sd-side-drawer-active{opacity:1;visibility:visible;pointer-events:all;transform:translateZ(0)}.sd-side-drawer-header{min-height:64px;padding:16px;display:flex;align-items:center;justify-content:space-between;gap:16px}.sd-side-drawer-header-left{display:flex;align-items:center;flex:1 1 auto;min-width:0}.sd-side-drawer-header-right{display:flex;align-items:center;gap:8px;flex:0 0 auto}.sd-side-drawer-title{font-size:20px;line-height:28px;font-weight:500;min-width:0}.sd-side-drawer-close-btn{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;box-sizing:border-box;width:32px;height:32px;padding:0;border:0;border-radius:50%;background:transparent;color:var(--sd-text-secondary);line-height:0;cursor:pointer;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;transition:background-color 80ms ease,color 80ms ease}.sd-side-drawer-close-btn:hover{background:var(--sd-surface-muted);color:var(--sd-text)}.sd-side-drawer-close-btn:active{background:var(--sd-border)}.sd-side-drawer-close-btn:focus{outline:none}.sd-side-drawer-close-btn:focus-visible{outline:2px solid var(--sd-primary);outline-offset:2px}.sd-side-drawer-close-btn sd-icon{display:block;width:20px;height:20px;font-size:20px;line-height:20px}.sd-side-drawer-body{flex:1;position:relative}.sd-side-drawer-content{position:absolute;padding:0;inset:0;overflow:auto}.sd-side-drawer-footer{border-top:1px solid var(--sd-side-drawer-border);padding:16px;background-color:var(--sd-side-drawer-surface);display:flex;align-items:center;justify-content:space-between;gap:16px}.sd-side-drawer-footer-left,.sd-side-drawer-footer-right{display:flex;align-items:center;gap:8px;min-width:0}.sd-side-drawer-footer-left{flex:1 1 auto}.sd-side-drawer-footer-right{flex:0 0 auto;margin-left:auto;justify-content:flex-end}.sd-side-drawer-footer-left:empty,.sd-side-drawer-footer-right:empty{display:none}.sd-side-drawer-footer:has(.sd-side-drawer-footer-left:empty):has(.sd-side-drawer-footer-right:empty){display:none}.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: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { 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 });
|
|
117
154
|
}
|
|
118
155
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdSideDrawer, decorators: [{
|
|
119
156
|
type: Component,
|
|
120
157
|
args: [{ selector: 'sd-side-drawer', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [SdIcon, CommonModule, PortalModule], template: "<ng-template cdkPortal>\n @let _isOpened = isOpened();\n <div\n [id]=\"id\"\n class=\"sd-side-drawer\"\n [ngStyle]=\"{ width: width() }\"\n [ngClass]=\"drawerClass()\"\n [class.sd-side-drawer-active]=\"_isOpened\"\n [attr.data-autoId]=\"autoId()\"\n [attr.data-opened]=\"dataOpened()\"\n [attr.data-loading]=\"dataLoading()\"\n #drawerContainer>\n @if (_isOpened) {\n <ng-container>\n <div class=\"sd-side-drawer-header\">\n <div class=\"sd-side-drawer-header-left\">\n <ng-content select=\"[sdHeaderLeft]\">\n <div class=\"sd-side-drawer-title\">{{ title() }}</div>\n </ng-content>\n </div>\n <div class=\"sd-side-drawer-header-right\">\n <ng-content select=\"[sdHeaderRight]\"></ng-content>\n @if (!hideClose()) {\n <button type=\"button\" class=\"sd-side-drawer-close-btn\" (click)=\"close()\" aria-label=\"Close\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n }\n </div>\n </div>\n @if (_isOpened) {\n <div class=\"sd-side-drawer-body\">\n <div class=\"sd-side-drawer-content\">\n <ng-content></ng-content>\n </div>\n </div>\n }\n <div class=\"sd-side-drawer-footer\">\n <div class=\"sd-side-drawer-footer-left\"><ng-content select=\"[sdFooterLeft]\"></ng-content></div>\n <div class=\"sd-side-drawer-footer-right\"><ng-content select=\"[sdFooterRight]\"></ng-content></div>\n </div>\n </ng-container>\n }\n </div>\n @if (_isOpened) {\n <div\n aria-hidden=\"true\"\n class=\"sd-side-drawer-backdrop\"\n (click)=\"disableBackdropClose() ? null : close()\"\n (wheel)=\"preventScroll($event)\"\n (touchmove)=\"preventScroll($event)\"></div>\n }\n</ng-template>\n", styles: [".sd-side-drawer{--sd-side-drawer-inset: 10px;--sd-side-drawer-radius: var(--sd-overlay-radius, 24px);--sd-side-drawer-surface: var(--sd-surface, #fff);--sd-side-drawer-border: var(--sd-border, #dde0e5);--sd-side-drawer-shadow: 0 6px 18px rgba(15, 23, 42, .18), 0 1px 3px rgba(15, 23, 42, .12);position:fixed;right:var(--sd-side-drawer-inset);top:var(--sd-side-drawer-inset);bottom:var(--sd-side-drawer-inset);max-width:calc(100vw - var(--sd-side-drawer-inset) - var(--sd-side-drawer-inset));background-color:var(--sd-side-drawer-surface);color:var(--sd-text);z-index:999;display:flex;flex-direction:column;opacity:0;visibility:hidden;transform:translate3d(calc(100% + var(--sd-side-drawer-inset)),0,0);transition:all .3s ease-in-out;pointer-events:none;overflow:hidden;border-radius:var(--sd-side-drawer-radius);box-shadow:var(--sd-side-drawer-shadow)}.sd-side-drawer-active{opacity:1;visibility:visible;pointer-events:all;transform:translateZ(0)}.sd-side-drawer-header{min-height:64px;padding:16px;display:flex;align-items:center;justify-content:space-between;gap:16px}.sd-side-drawer-header-left{display:flex;align-items:center;flex:1 1 auto;min-width:0}.sd-side-drawer-header-right{display:flex;align-items:center;gap:8px;flex:0 0 auto}.sd-side-drawer-title{font-size:20px;line-height:28px;font-weight:500;min-width:0}.sd-side-drawer-close-btn{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;box-sizing:border-box;width:32px;height:32px;padding:0;border:0;border-radius:50%;background:transparent;color:var(--sd-text-secondary);line-height:0;cursor:pointer;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;transition:background-color 80ms ease,color 80ms ease}.sd-side-drawer-close-btn:hover{background:var(--sd-surface-muted);color:var(--sd-text)}.sd-side-drawer-close-btn:active{background:var(--sd-border)}.sd-side-drawer-close-btn:focus{outline:none}.sd-side-drawer-close-btn:focus-visible{outline:2px solid var(--sd-primary);outline-offset:2px}.sd-side-drawer-close-btn sd-icon{display:block;width:20px;height:20px;font-size:20px;line-height:20px}.sd-side-drawer-body{flex:1;position:relative}.sd-side-drawer-content{position:absolute;padding:0;inset:0;overflow:auto}.sd-side-drawer-footer{border-top:1px solid var(--sd-side-drawer-border);padding:16px;background-color:var(--sd-side-drawer-surface);display:flex;align-items:center;justify-content:space-between;gap:16px}.sd-side-drawer-footer-left,.sd-side-drawer-footer-right{display:flex;align-items:center;gap:8px;min-width:0}.sd-side-drawer-footer-left{flex:1 1 auto}.sd-side-drawer-footer-right{flex:0 0 auto;margin-left:auto;justify-content:flex-end}.sd-side-drawer-footer-left:empty,.sd-side-drawer-footer-right:empty{display:none}.sd-side-drawer-footer:has(.sd-side-drawer-footer-left:empty):has(.sd-side-drawer-footer-right:empty){display:none}.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"] }]
|
|
121
|
-
}], ctorParameters: () => [], propDecorators: { portal: [{ type: i0.ViewChild, args: [i0.forwardRef(() => CdkPortal), { isSignal: true }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], hideClose: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideClose", required: false }] }], disableBackdropClose: [{ type: i0.Input, args: [{ isSignal: true, alias: "disableBackdropClose", required: false }] }], drawerClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "drawerClass", required: false }] }], autoIdInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoId", required: false }] }], sdClosed: [{ type: i0.Output, args: ["sdClosed"] }] } });
|
|
158
|
+
}], ctorParameters: () => [], propDecorators: { portal: [{ type: i0.ViewChild, args: [i0.forwardRef(() => CdkPortal), { isSignal: true }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], hideClose: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideClose", required: false }] }], disableBackdropClose: [{ type: i0.Input, args: [{ isSignal: true, alias: "disableBackdropClose", required: false }] }], beforeClose: [{ type: i0.Input, args: [{ isSignal: true, alias: "beforeClose", required: false }] }], drawerClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "drawerClass", required: false }] }], autoIdInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoId", required: false }] }], sdClosed: [{ type: i0.Output, args: ["sdClosed"] }], sdCloseError: [{ type: i0.Output, args: ["sdCloseError"] }] } });
|
|
122
159
|
|
|
123
160
|
/**
|
|
124
161
|
* Generated bundle index. Do not edit.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sdcorejs-angular-components-side-drawer.mjs","sources":["../../../projects/sdcorejs-angular/components/side-drawer/src/side-drawer.component.ts","../../../projects/sdcorejs-angular/components/side-drawer/src/side-drawer.component.html","../../../projects/sdcorejs-angular/components/side-drawer/sdcorejs-angular-components-side-drawer.ts"],"sourcesContent":["import { CdkPortal, DomPortalOutlet, PortalModule } from '@angular/cdk/portal';\nimport { CommonModule } from '@angular/common';\nimport {\n ApplicationRef,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n DestroyRef,\n EmbeddedViewRef,\n Injector,\n ViewContainerRef,\n afterNextRender,\n booleanAttribute,\n computed,\n inject,\n input,\n output,\n signal,\n viewChild,\n} from '@angular/core';\nimport { SdLoadingService } from '@sdcorejs/angular/services';\nimport { Utilities } from '@sdcorejs/utils/fns';\nimport { fromEvent, merge, Observable, Subject } from 'rxjs';\nimport { map, takeUntil, startWith, distinctUntilChanged } from 'rxjs/operators';\nimport { SdIcon } from '@sdcorejs/angular/modules/icon';\n\n@Component({\n selector: 'sd-side-drawer',\n templateUrl: './side-drawer.component.html',\n styleUrl: './side-drawer.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [SdIcon, CommonModule, PortalModule],\n})\nexport class SdSideDrawer {\n id = `I${Utilities.generateUuid()}`;\n\n portal = viewChild.required(CdkPortal);\n\n title = input<string>('');\n width = input<string>('480px');\n hideClose = input<boolean, boolean | ''>(false, { transform: booleanAttribute });\n disableBackdropClose = input<boolean, boolean | ''>(false, { transform: booleanAttribute });\n\n // Custom CSS class added to the root side-drawer container\n drawerClass = input<any>('');\n\n readonly autoIdInput = input<string | undefined | null>(undefined, { alias: 'autoId' });\n readonly autoId = computed(() => (this.autoIdInput() ? `components-side-drawer-${this.autoIdInput()}` : undefined));\n\n sdClosed = output<void>();\n\n #embeddedViewRef!: EmbeddedViewRef<any>;\n\n readonly #isOpenedSignal = signal(false);\n readonly #isLoadingSignal = signal(false);\n readonly isOpened = this.#isOpenedSignal.asReadonly();\n readonly isLoading = this.#isLoadingSignal.asReadonly();\n readonly dataOpened = computed(() => (this.#isOpenedSignal() ? 'true' : 'false'));\n readonly dataLoading = computed(() => (this.#isLoadingSignal() ? 'true' : 'false'));\n\n isHovered$!: Observable<boolean>;\n #destroy$ = new Subject<void>();\n #previousBodyOverflow: string | null = null;\n\n #viewContainerRef = inject(ViewContainerRef);\n #ar = inject(ApplicationRef);\n #injector = inject(Injector);\n #ref = inject(ChangeDetectorRef);\n #loadingService = inject(SdLoadingService);\n #destroyRef = inject(DestroyRef);\n\n constructor() {\n // Thay thế ngAfterViewInit, tự động chạy nội dung này khi DOM sẵn sàng để render\n afterNextRender(() => {\n // 1. Gắn portal vào body và lưu lại EmbeddedViewRef\n const outlet = new DomPortalOutlet(document.body, this.#ar, this.#injector);\n this.#embeddedViewRef = outlet.attachTemplatePortal(this.portal());\n\n // 2. Setup sự kiện hover ngay sau khi DOM thật đã được in ra\n this.#setupHoverSubscription();\n });\n\n // Thay thế ngOnDestroy bằng logic destroy trực tiếp\n this.#destroyRef.onDestroy(() => {\n this.#destroy$.next();\n this.#destroy$.complete();\n\n if (this.#embeddedViewRef) {\n this.#embeddedViewRef.destroy();\n }\n\n if (this.#isOpenedSignal()) {\n if (this.#previousBodyOverflow !== null) {\n document.body.style.overflow = this.#previousBodyOverflow;\n } else {\n document.body.style.overflow = '';\n }\n }\n });\n }\n\n open = () => {\n this.#ref.markForCheck();\n this.#isOpenedSignal.set(true);\n\n // Chặn scroll ở document body\n this.#previousBodyOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n };\n\n close = () => {\n this.#ref.markForCheck();\n this.#isOpenedSignal.set(false);\n this.sdClosed.emit();\n this.stopLoading();\n\n // Khôi phục lại scroll ở document body\n if (this.#previousBodyOverflow !== null) {\n document.body.style.overflow = this.#previousBodyOverflow;\n this.#previousBodyOverflow = null;\n } else {\n document.body.style.overflow = '';\n }\n };\n\n startLoading = () => {\n this.#isLoadingSignal.set(true);\n this.#loadingService.stop(`#${this.id}`);\n this.#loadingService.start(`#${this.id}`);\n };\n\n stopLoading = () => {\n this.#isLoadingSignal.set(false);\n this.#loadingService.stop(`#${this.id}`);\n };\n\n preventScroll = (event: Event) => {\n event.preventDefault();\n };\n\n #setupHoverSubscription(): void {\n if (!this.#embeddedViewRef) return;\n\n // 3. Lấy DOM element trực tiếp từ rootNodes của EmbeddedViewRef\n const rootNodes = this.#embeddedViewRef.rootNodes;\n const element = rootNodes.find(\n node => node.nodeType === Node.ELEMENT_NODE && (node as HTMLElement).classList?.contains('sd-side-drawer')\n );\n\n if (!element) {\n console.warn('SdSideDrawer: Cannot find sd-side-drawer element to attach hover event');\n return;\n }\n\n // 4. Gắn event listeners trực tiếp lên element thật\n const mouseEnter$ = fromEvent(element, 'mouseenter').pipe(map(() => true));\n const mouseLeave$ = fromEvent(element, 'mouseleave').pipe(map(() => false));\n\n this.isHovered$ = merge(mouseEnter$, mouseLeave$).pipe(startWith(false), distinctUntilChanged(), takeUntil(this.#destroy$));\n }\n}\n","<ng-template cdkPortal>\n @let _isOpened = isOpened();\n <div\n [id]=\"id\"\n class=\"sd-side-drawer\"\n [ngStyle]=\"{ width: width() }\"\n [ngClass]=\"drawerClass()\"\n [class.sd-side-drawer-active]=\"_isOpened\"\n [attr.data-autoId]=\"autoId()\"\n [attr.data-opened]=\"dataOpened()\"\n [attr.data-loading]=\"dataLoading()\"\n #drawerContainer>\n @if (_isOpened) {\n <ng-container>\n <div class=\"sd-side-drawer-header\">\n <div class=\"sd-side-drawer-header-left\">\n <ng-content select=\"[sdHeaderLeft]\">\n <div class=\"sd-side-drawer-title\">{{ title() }}</div>\n </ng-content>\n </div>\n <div class=\"sd-side-drawer-header-right\">\n <ng-content select=\"[sdHeaderRight]\"></ng-content>\n @if (!hideClose()) {\n <button type=\"button\" class=\"sd-side-drawer-close-btn\" (click)=\"close()\" aria-label=\"Close\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n }\n </div>\n </div>\n @if (_isOpened) {\n <div class=\"sd-side-drawer-body\">\n <div class=\"sd-side-drawer-content\">\n <ng-content></ng-content>\n </div>\n </div>\n }\n <div class=\"sd-side-drawer-footer\">\n <div class=\"sd-side-drawer-footer-left\"><ng-content select=\"[sdFooterLeft]\"></ng-content></div>\n <div class=\"sd-side-drawer-footer-right\"><ng-content select=\"[sdFooterRight]\"></ng-content></div>\n </div>\n </ng-container>\n }\n </div>\n @if (_isOpened) {\n <div\n aria-hidden=\"true\"\n class=\"sd-side-drawer-backdrop\"\n (click)=\"disableBackdropClose() ? null : close()\"\n (wheel)=\"preventScroll($event)\"\n (touchmove)=\"preventScroll($event)\"></div>\n }\n</ng-template>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sdcorejs-angular-components-side-drawer.mjs","sources":["../../../projects/sdcorejs-angular/components/side-drawer/src/side-drawer.component.ts","../../../projects/sdcorejs-angular/components/side-drawer/src/side-drawer.component.html","../../../projects/sdcorejs-angular/components/side-drawer/sdcorejs-angular-components-side-drawer.ts"],"sourcesContent":["import { CdkPortal, DomPortalOutlet, PortalModule } from '@angular/cdk/portal';\nimport { CommonModule } from '@angular/common';\nimport {\n ApplicationRef,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n DestroyRef,\n EmbeddedViewRef,\n Injector,\n ViewContainerRef,\n afterNextRender,\n booleanAttribute,\n computed,\n inject,\n input,\n output,\n signal,\n viewChild,\n} from '@angular/core';\nimport { SdLoadingService } from '@sdcorejs/angular/services';\nimport { Utilities } from '@sdcorejs/utils/fns';\nimport { fromEvent, merge, Observable, Subject } from 'rxjs';\nimport { map, takeUntil, startWith, distinctUntilChanged } from 'rxjs/operators';\nimport { SdIcon } from '@sdcorejs/angular/modules/icon';\n\nexport type SdSideDrawerBeforeClose = () => boolean | Promise<boolean>;\n\n@Component({\n selector: 'sd-side-drawer',\n templateUrl: './side-drawer.component.html',\n styleUrl: './side-drawer.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [SdIcon, CommonModule, PortalModule],\n})\nexport class SdSideDrawer {\n id = `I${Utilities.generateUuid()}`;\n\n portal = viewChild.required(CdkPortal);\n\n title = input<string>('');\n width = input<string>('480px');\n hideClose = input<boolean, boolean | ''>(false, { transform: booleanAttribute });\n disableBackdropClose = input<boolean, boolean | ''>(false, { transform: booleanAttribute });\n beforeClose = input<SdSideDrawerBeforeClose | undefined>(undefined);\n\n // Custom CSS class added to the root side-drawer container\n drawerClass = input<any>('');\n\n readonly autoIdInput = input<string | undefined | null>(undefined, { alias: 'autoId' });\n readonly autoId = computed(() => (this.autoIdInput() ? `components-side-drawer-${this.autoIdInput()}` : undefined));\n\n sdClosed = output<void>();\n sdCloseError = output<unknown>();\n\n #embeddedViewRef!: EmbeddedViewRef<any>;\n\n readonly #isOpenedSignal = signal(false);\n readonly #isLoadingSignal = signal(false);\n readonly isOpened = this.#isOpenedSignal.asReadonly();\n readonly isLoading = this.#isLoadingSignal.asReadonly();\n readonly dataOpened = computed(() => (this.#isOpenedSignal() ? 'true' : 'false'));\n readonly dataLoading = computed(() => (this.#isLoadingSignal() ? 'true' : 'false'));\n\n isHovered$!: Observable<boolean>;\n #destroy$ = new Subject<void>();\n #previousBodyOverflow: string | null = null;\n #closeRequest?: Promise<boolean>;\n\n #viewContainerRef = inject(ViewContainerRef);\n #ar = inject(ApplicationRef);\n #injector = inject(Injector);\n #ref = inject(ChangeDetectorRef);\n #loadingService = inject(SdLoadingService);\n #destroyRef = inject(DestroyRef);\n\n constructor() {\n // Thay thế ngAfterViewInit, tự động chạy nội dung này khi DOM sẵn sàng để render\n afterNextRender(() => {\n // 1. Gắn portal vào body và lưu lại EmbeddedViewRef\n const outlet = new DomPortalOutlet(document.body, this.#ar, this.#injector);\n this.#embeddedViewRef = outlet.attachTemplatePortal(this.portal());\n\n // 2. Setup sự kiện hover ngay sau khi DOM thật đã được in ra\n this.#setupHoverSubscription();\n });\n\n // Thay thế ngOnDestroy bằng logic destroy trực tiếp\n this.#destroyRef.onDestroy(() => {\n this.#destroy$.next();\n this.#destroy$.complete();\n\n if (this.#embeddedViewRef) {\n this.#embeddedViewRef.destroy();\n }\n\n if (this.#isOpenedSignal()) {\n if (this.#previousBodyOverflow !== null) {\n document.body.style.overflow = this.#previousBodyOverflow;\n } else {\n document.body.style.overflow = '';\n }\n }\n });\n }\n\n open = () => {\n this.#ref.markForCheck();\n this.#isOpenedSignal.set(true);\n\n // Chặn scroll ở document body\n this.#previousBodyOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n };\n\n close = () => {\n if (this.beforeClose()) {\n void this.requestClose();\n return;\n }\n this.forceClose();\n };\n\n requestClose = (): Promise<boolean> => {\n const guard = this.beforeClose();\n if (!guard) {\n this.forceClose();\n return Promise.resolve(true);\n }\n if (this.#closeRequest) return this.#closeRequest;\n\n const request = Promise.resolve()\n .then(() => guard())\n .then(canClose => {\n if (!canClose) return false;\n this.forceClose();\n return true;\n })\n .catch((error: unknown) => {\n this.sdCloseError.emit(error);\n return false;\n });\n\n this.#closeRequest = request;\n void request.finally(() => {\n if (this.#closeRequest === request) this.#closeRequest = undefined;\n });\n return request;\n };\n\n forceClose = () => {\n this.#ref.markForCheck();\n this.#isOpenedSignal.set(false);\n this.sdClosed.emit();\n this.stopLoading();\n\n // Khôi phục lại scroll ở document body\n if (this.#previousBodyOverflow !== null) {\n document.body.style.overflow = this.#previousBodyOverflow;\n this.#previousBodyOverflow = null;\n } else {\n document.body.style.overflow = '';\n }\n };\n\n startLoading = () => {\n this.#isLoadingSignal.set(true);\n this.#loadingService.stop(`#${this.id}`);\n this.#loadingService.start(`#${this.id}`);\n };\n\n stopLoading = () => {\n this.#isLoadingSignal.set(false);\n this.#loadingService.stop(`#${this.id}`);\n };\n\n preventScroll = (event: Event) => {\n event.preventDefault();\n };\n\n #setupHoverSubscription(): void {\n if (!this.#embeddedViewRef) return;\n\n // 3. Lấy DOM element trực tiếp từ rootNodes của EmbeddedViewRef\n const rootNodes = this.#embeddedViewRef.rootNodes;\n const element = rootNodes.find(\n node => node.nodeType === Node.ELEMENT_NODE && (node as HTMLElement).classList?.contains('sd-side-drawer')\n );\n\n if (!element) {\n console.warn('SdSideDrawer: Cannot find sd-side-drawer element to attach hover event');\n return;\n }\n\n // 4. Gắn event listeners trực tiếp lên element thật\n const mouseEnter$ = fromEvent(element, 'mouseenter').pipe(map(() => true));\n const mouseLeave$ = fromEvent(element, 'mouseleave').pipe(map(() => false));\n\n this.isHovered$ = merge(mouseEnter$, mouseLeave$).pipe(startWith(false), distinctUntilChanged(), takeUntil(this.#destroy$));\n }\n}\n","<ng-template cdkPortal>\n @let _isOpened = isOpened();\n <div\n [id]=\"id\"\n class=\"sd-side-drawer\"\n [ngStyle]=\"{ width: width() }\"\n [ngClass]=\"drawerClass()\"\n [class.sd-side-drawer-active]=\"_isOpened\"\n [attr.data-autoId]=\"autoId()\"\n [attr.data-opened]=\"dataOpened()\"\n [attr.data-loading]=\"dataLoading()\"\n #drawerContainer>\n @if (_isOpened) {\n <ng-container>\n <div class=\"sd-side-drawer-header\">\n <div class=\"sd-side-drawer-header-left\">\n <ng-content select=\"[sdHeaderLeft]\">\n <div class=\"sd-side-drawer-title\">{{ title() }}</div>\n </ng-content>\n </div>\n <div class=\"sd-side-drawer-header-right\">\n <ng-content select=\"[sdHeaderRight]\"></ng-content>\n @if (!hideClose()) {\n <button type=\"button\" class=\"sd-side-drawer-close-btn\" (click)=\"close()\" aria-label=\"Close\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n }\n </div>\n </div>\n @if (_isOpened) {\n <div class=\"sd-side-drawer-body\">\n <div class=\"sd-side-drawer-content\">\n <ng-content></ng-content>\n </div>\n </div>\n }\n <div class=\"sd-side-drawer-footer\">\n <div class=\"sd-side-drawer-footer-left\"><ng-content select=\"[sdFooterLeft]\"></ng-content></div>\n <div class=\"sd-side-drawer-footer-right\"><ng-content select=\"[sdFooterRight]\"></ng-content></div>\n </div>\n </ng-container>\n }\n </div>\n @if (_isOpened) {\n <div\n aria-hidden=\"true\"\n class=\"sd-side-drawer-backdrop\"\n (click)=\"disableBackdropClose() ? null : close()\"\n (wheel)=\"preventScroll($event)\"\n (touchmove)=\"preventScroll($event)\"></div>\n }\n</ng-template>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, Injectable, Pipe, Input, ChangeDetectionStrategy, Component, viewChild, input,
|
|
3
|
-
import
|
|
4
|
-
import { Router, RoutesRecognized, NavigationEnd, RouterOutlet, ActivatedRoute } from '@angular/router';
|
|
2
|
+
import { InjectionToken, Injectable, inject, Pipe, ChangeDetectorRef, Input, ChangeDetectionStrategy, Component, viewChild, input, ElementRef, HostListener, booleanAttribute, signal, Injector, afterNextRender, NgModuleFactory, createNgModule } from '@angular/core';
|
|
3
|
+
import { Router, RoutesRecognized, NavigationEnd, NavigationSkipped, NavigationCancel, NavigationError, NavigationSkippedCode, RouterOutlet, ActivatedRoute } from '@angular/router';
|
|
5
4
|
import * as i1$1 from '@angular/common';
|
|
6
5
|
import { CommonModule } from '@angular/common';
|
|
7
6
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
8
7
|
import { BehaviorSubject, Subscription, from, isObservable, lastValueFrom, Subject } from 'rxjs';
|
|
9
|
-
import { debounceTime, startWith, filter, concatMap, take } from 'rxjs/operators';
|
|
8
|
+
import { debounceTime, startWith, filter, map, concatMap, take } from 'rxjs/operators';
|
|
10
9
|
import { SdNotifyService } from '@sdcorejs/angular/services/notify';
|
|
11
10
|
import { I18nService } from '@sdcorejs/angular/i18n';
|
|
12
11
|
import { Utilities } from '@sdcorejs/utils/fns';
|
|
@@ -117,10 +116,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
|
|
|
117
116
|
|
|
118
117
|
class SdTabDecoratorService {
|
|
119
118
|
static tabRouterService = new BehaviorSubject(undefined);
|
|
120
|
-
constructor(
|
|
119
|
+
constructor() {
|
|
120
|
+
const tabRouterService = inject(SdTabRouterService);
|
|
121
121
|
SdTabDecoratorService.tabRouterService.next(tabRouterService);
|
|
122
122
|
}
|
|
123
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabDecoratorService, deps: [
|
|
123
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabDecoratorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
124
124
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabDecoratorService, providedIn: 'root' });
|
|
125
125
|
}
|
|
126
126
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabDecoratorService, decorators: [{
|
|
@@ -128,13 +128,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
|
|
|
128
128
|
args: [{
|
|
129
129
|
providedIn: 'root',
|
|
130
130
|
}]
|
|
131
|
-
}], ctorParameters: () => [
|
|
131
|
+
}], ctorParameters: () => [] });
|
|
132
132
|
|
|
133
133
|
class SdTabInfoPipe {
|
|
134
|
-
tabRouterService;
|
|
135
|
-
constructor(
|
|
136
|
-
this.tabRouterService = tabRouterService;
|
|
137
|
-
}
|
|
134
|
+
tabRouterService = inject(SdTabRouterService);
|
|
135
|
+
constructor() { }
|
|
138
136
|
transform(tabInfo, tab) {
|
|
139
137
|
if (tabInfo) {
|
|
140
138
|
return tabInfo;
|
|
@@ -155,7 +153,7 @@ class SdTabInfoPipe {
|
|
|
155
153
|
icon: undefined,
|
|
156
154
|
};
|
|
157
155
|
}
|
|
158
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabInfoPipe, deps: [
|
|
156
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabInfoPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
159
157
|
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.17", ngImport: i0, type: SdTabInfoPipe, isStandalone: true, name: "sdTabInfo" });
|
|
160
158
|
}
|
|
161
159
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabInfoPipe, decorators: [{
|
|
@@ -164,20 +162,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
|
|
|
164
162
|
name: 'sdTabInfo',
|
|
165
163
|
standalone: true,
|
|
166
164
|
}]
|
|
167
|
-
}], ctorParameters: () => [
|
|
165
|
+
}], ctorParameters: () => [] });
|
|
168
166
|
|
|
169
167
|
class SdTabRouterItemComponent {
|
|
170
|
-
cdRef;
|
|
171
|
-
tabRouterService;
|
|
172
|
-
router;
|
|
168
|
+
cdRef = inject(ChangeDetectorRef);
|
|
169
|
+
tabRouterService = inject(SdTabRouterService);
|
|
170
|
+
router = inject(Router);
|
|
173
171
|
tab;
|
|
174
172
|
#subsctiption = new Subscription();
|
|
175
173
|
tabInfo;
|
|
176
|
-
constructor(
|
|
177
|
-
this.cdRef = cdRef;
|
|
178
|
-
this.tabRouterService = tabRouterService;
|
|
179
|
-
this.router = router;
|
|
180
|
-
}
|
|
174
|
+
constructor() { }
|
|
181
175
|
ngOnInit() {
|
|
182
176
|
this.#subsctiption.add(this.tabRouterService.events.pipe(debounceTime(100)).subscribe(() => {
|
|
183
177
|
this.cdRef.markForCheck();
|
|
@@ -219,13 +213,13 @@ class SdTabRouterItemComponent {
|
|
|
219
213
|
#closeTab = () => {
|
|
220
214
|
this.tabRouterService.close(this.tab);
|
|
221
215
|
};
|
|
222
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabRouterItemComponent, deps: [
|
|
216
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabRouterItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
223
217
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdTabRouterItemComponent, isStandalone: true, selector: "sd-tab-router-item", inputs: { tab: "tab" }, ngImport: i0, template: "<a\n [href]=\"[tab.url]\"\n class=\"tab-router__item d-flex align-items-center gap-8\"\n [class.tab-router__item--active]=\"tab.isActive\"\n (click)=\"onTabClick($event)\"\n (mousedown)=\"onMousedown($event)\"\n (mouseup)=\"onMouseup($event)\">\n @let info = tabInfo | sdTabInfo: tab;\n @if (info) {\n <sd-badge\n style=\"overflow: hidden;white-space: nowrap;\"\n [icon]=\"info.icon\"\n [title]=\"info.icon\"\n [tooltip]=\"info.tooltip || info.name\"\n [title]=\"info.name\"\n [color]=\"info.color\"\n (click)=\"onTabClick($event)\"></sd-badge>\n <button\n aria-hidden=\"true\"\n class=\"tab-router__close d-flex align-items-center justify-content-center ml-auto p-0\"\n (click)=\"close($event)\"\n (mousedown)=\"$event.stopPropagation()\">\n <sd-icon aria-hidden=\"true\" fontIcon=\"close\"></sd-icon>\n </button>\n }\n</a>\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 sd-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 sd-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: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: CommonModule }, { 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 });
|
|
224
218
|
}
|
|
225
219
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabRouterItemComponent, decorators: [{
|
|
226
220
|
type: Component,
|
|
227
221
|
args: [{ selector: 'sd-tab-router-item', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [SdIcon, CommonModule, SdBadge, SdTabInfoPipe], template: "<a\n [href]=\"[tab.url]\"\n class=\"tab-router__item d-flex align-items-center gap-8\"\n [class.tab-router__item--active]=\"tab.isActive\"\n (click)=\"onTabClick($event)\"\n (mousedown)=\"onMousedown($event)\"\n (mouseup)=\"onMouseup($event)\">\n @let info = tabInfo | sdTabInfo: tab;\n @if (info) {\n <sd-badge\n style=\"overflow: hidden;white-space: nowrap;\"\n [icon]=\"info.icon\"\n [title]=\"info.icon\"\n [tooltip]=\"info.tooltip || info.name\"\n [title]=\"info.name\"\n [color]=\"info.color\"\n (click)=\"onTabClick($event)\"></sd-badge>\n <button\n aria-hidden=\"true\"\n class=\"tab-router__close d-flex align-items-center justify-content-center ml-auto p-0\"\n (click)=\"close($event)\"\n (mousedown)=\"$event.stopPropagation()\">\n <sd-icon aria-hidden=\"true\" fontIcon=\"close\"></sd-icon>\n </button>\n }\n</a>\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 sd-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 sd-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"] }]
|
|
228
|
-
}], ctorParameters: () => [
|
|
222
|
+
}], ctorParameters: () => [], propDecorators: { tab: [{
|
|
229
223
|
type: Input,
|
|
230
224
|
args: [{ required: true }]
|
|
231
225
|
}] } });
|
|
@@ -260,11 +254,11 @@ class SdTabRouterNavComponent {
|
|
|
260
254
|
moveItemInArray(this.tabs(), event.previousIndex, event.currentIndex);
|
|
261
255
|
};
|
|
262
256
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabRouterNavComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
263
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdTabRouterNavComponent, isStandalone: true, selector: "sd-tab-router-nav", inputs: { tabs: { classPropertyName: "tabs", publicName: "tabs", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "window:resize": "onResize()" } }, viewQueries: [{ propertyName: "tabRouterNav", first: true, predicate: ["tabRouterNav"], descendants: true, isSignal: 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 @for (tab of tabs(); track tab.key) {\n <sd-tab-router-item [tab]=\"tab\" cdkDrag [cdkDragBoundary]=\"elementRef.nativeElement\"></sd-tab-router-item>\n }\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: "ngmodule", type: DragDropModule }, { kind: "directive", type: i1.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i1.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 });
|
|
257
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdTabRouterNavComponent, isStandalone: true, selector: "sd-tab-router-nav", inputs: { tabs: { classPropertyName: "tabs", publicName: "tabs", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "window:resize": "onResize()" } }, viewQueries: [{ propertyName: "tabRouterNav", first: true, predicate: ["tabRouterNav"], descendants: true, isSignal: 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 @for (tab of tabs(); track tab.injector ?? tab.key) {\n <sd-tab-router-item [tab]=\"tab\" cdkDrag [cdkDragBoundary]=\"elementRef.nativeElement\"></sd-tab-router-item>\n }\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: "ngmodule", type: DragDropModule }, { kind: "directive", type: i1.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i1.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 });
|
|
264
258
|
}
|
|
265
259
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabRouterNavComponent, decorators: [{
|
|
266
260
|
type: Component,
|
|
267
|
-
args: [{ selector: 'sd-tab-router-nav', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, DragDropModule, SdTabRouterItemComponent], 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 @for (tab of tabs(); track tab.key) {\n <sd-tab-router-item [tab]=\"tab\" cdkDrag [cdkDragBoundary]=\"elementRef.nativeElement\"></sd-tab-router-item>\n }\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"] }]
|
|
261
|
+
args: [{ selector: 'sd-tab-router-nav', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, DragDropModule, SdTabRouterItemComponent], 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 @for (tab of tabs(); track tab.injector ?? tab.key) {\n <sd-tab-router-item [tab]=\"tab\" cdkDrag [cdkDragBoundary]=\"elementRef.nativeElement\"></sd-tab-router-item>\n }\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"] }]
|
|
268
262
|
}], propDecorators: { tabRouterNav: [{ type: i0.ViewChild, args: ['tabRouterNav', { isSignal: true }] }], tabs: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabs", required: false }] }], onResize: [{
|
|
269
263
|
type: HostListener,
|
|
270
264
|
args: ['window:resize']
|
|
@@ -284,10 +278,8 @@ class SdTabRouterOutletComponent {
|
|
|
284
278
|
#tabDecoratorService = inject(SdTabDecoratorService);
|
|
285
279
|
#rootRoute;
|
|
286
280
|
#subscription = new Subscription();
|
|
287
|
-
//
|
|
288
|
-
|
|
289
|
-
// còn lastSuccessfulNavigation và window.history.state không đáng tin cậy với mọi case.
|
|
290
|
-
#pendingNavigationState = {};
|
|
281
|
+
// Lưu state theo navigation id để các navigation chồng lấn không ghi đè lẫn nhau.
|
|
282
|
+
#pendingNavigationStates = new Map();
|
|
291
283
|
// Serialize mọi #activeRoute (router events + initial sync) để tránh race tạo duplicate tab.
|
|
292
284
|
#activationQueue = Promise.resolve();
|
|
293
285
|
constructor() {
|
|
@@ -296,13 +288,23 @@ class SdTabRouterOutletComponent {
|
|
|
296
288
|
// KHÔNG unwrap event.routerEvent (vd Scroll wrap NavigationEnd):
|
|
297
289
|
// Angular emit NavigationEnd RAW trước, RouterScroller emit Scroll(NavigationEnd) sau.
|
|
298
290
|
// Nếu unwrap thêm Scroll → handler fire 2 lần cho 1 nav → race condition tạo duplicate tabs.
|
|
299
|
-
//
|
|
291
|
+
// Navigation thành công cần CẢ HAI event vì mỗi event chứa data ở thời điểm khác nhau.
|
|
300
292
|
// - RoutesRecognized: navigation đang in-flight → getCurrentNavigation().extras.state đọc được
|
|
301
293
|
// - NavigationEnd: navigation hoàn tất → routerState.root đã update với route mới (cần cho lazy routes)
|
|
302
|
-
|
|
294
|
+
// Các terminal event khác chỉ xử lý same-URL forceReload hoặc dọn state theo navigation id.
|
|
295
|
+
filter((event) => event instanceof RoutesRecognized ||
|
|
296
|
+
event instanceof NavigationEnd ||
|
|
297
|
+
event instanceof NavigationSkipped ||
|
|
298
|
+
event instanceof NavigationCancel ||
|
|
299
|
+
event instanceof NavigationError),
|
|
300
|
+
// Chụp getCurrentNavigation đồng bộ vì concatMap có thể chạy sau khi Angular đã xoá navigation hiện tại.
|
|
301
|
+
map((event) => ({
|
|
302
|
+
event,
|
|
303
|
+
navigationState: this.#router.getCurrentNavigation()?.extras?.state ?? {},
|
|
304
|
+
})),
|
|
303
305
|
// Serialize: #handleEvent async (await getBestInjector). 2 nav liên tiếp
|
|
304
306
|
// không await xen kẽ → tránh race đọc this.tabs() = [] khi tab đầu chưa kịp set.
|
|
305
|
-
concatMap(
|
|
307
|
+
concatMap(context => from(this.#handleEvent(context))))
|
|
306
308
|
.subscribe());
|
|
307
309
|
this.#subscription.add(this.#tabRouterService.actions.subscribe((event) => {
|
|
308
310
|
if (this.disabled())
|
|
@@ -319,31 +321,45 @@ class SdTabRouterOutletComponent {
|
|
|
319
321
|
}
|
|
320
322
|
ngOnDestroy() {
|
|
321
323
|
this.#subscription.unsubscribe();
|
|
324
|
+
this.#pendingNavigationStates.clear();
|
|
322
325
|
}
|
|
323
326
|
#scheduleActivation = (task) => {
|
|
324
327
|
const run = this.#activationQueue.then(task);
|
|
325
328
|
this.#activationQueue = run.catch(() => undefined);
|
|
326
329
|
return run;
|
|
327
330
|
};
|
|
328
|
-
#handleEvent = async (event) => {
|
|
331
|
+
#handleEvent = async ({ event, navigationState }) => {
|
|
329
332
|
if (this.disabled()) {
|
|
330
|
-
this.#
|
|
333
|
+
this.#pendingNavigationStates.clear();
|
|
331
334
|
return;
|
|
332
335
|
}
|
|
333
336
|
if (event instanceof RoutesRecognized) {
|
|
334
|
-
|
|
335
|
-
// getCurrentNavigation() trả về Navigation object với extras.state nguyên vẹn.
|
|
336
|
-
this.#pendingNavigationState = this.#router.getCurrentNavigation()?.extras?.state ?? {};
|
|
337
|
+
this.#pendingNavigationStates.set(event.id, navigationState);
|
|
337
338
|
return;
|
|
338
339
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
340
|
+
if (event instanceof NavigationCancel || event instanceof NavigationError) {
|
|
341
|
+
this.#pendingNavigationStates.delete(event.id);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
let state;
|
|
345
|
+
let fullUrl;
|
|
346
|
+
if (event instanceof NavigationSkipped) {
|
|
347
|
+
this.#pendingNavigationStates.delete(event.id);
|
|
348
|
+
if (event.code !== NavigationSkippedCode.IgnoredSameUrlNavigation || navigationState['forceReload'] !== true) {
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
state = navigationState;
|
|
352
|
+
fullUrl = event.url;
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
state = this.#pendingNavigationStates.get(event.id) ?? navigationState;
|
|
356
|
+
this.#pendingNavigationStates.delete(event.id);
|
|
357
|
+
fullUrl = event.urlAfterRedirects || event.url;
|
|
358
|
+
}
|
|
343
359
|
await this.#scheduleActivation(async () => {
|
|
344
360
|
const route = this.#getActivatedRouteSnapshot(this.#router.routerState.snapshot.root);
|
|
345
361
|
this.#rootRoute = this.#router.routerState.root;
|
|
346
|
-
await this.#activeRoute(
|
|
362
|
+
await this.#activeRoute(fullUrl, route, state);
|
|
347
363
|
});
|
|
348
364
|
};
|
|
349
365
|
// Bù initial navigation nếu NavigationEnd đã fire trước khi outlet subscribe.
|
|
@@ -382,7 +398,7 @@ class SdTabRouterOutletComponent {
|
|
|
382
398
|
canClose = typeof result === 'boolean' ? result : await result;
|
|
383
399
|
}
|
|
384
400
|
catch {
|
|
385
|
-
canClose =
|
|
401
|
+
canClose = false;
|
|
386
402
|
}
|
|
387
403
|
if (!canClose)
|
|
388
404
|
return;
|
|
@@ -418,7 +434,7 @@ class SdTabRouterOutletComponent {
|
|
|
418
434
|
const params = { ...(route.params || {}) };
|
|
419
435
|
const data = { ...(route.data || {}) };
|
|
420
436
|
const [url] = fullUrl.split('?');
|
|
421
|
-
// Tab identity = hash(url + queryParams). Cùng key
|
|
437
|
+
// Tab identity = hash(url + queryParams). Cùng key mặc định giữ tab cũ; forceReload mới tạo lại.
|
|
422
438
|
const key = Utilities.hash({ url, queryParams });
|
|
423
439
|
let existedIndex = -1;
|
|
424
440
|
let activatedIndex = -1;
|
|
@@ -445,6 +461,7 @@ class SdTabRouterOutletComponent {
|
|
|
445
461
|
}
|
|
446
462
|
}
|
|
447
463
|
const replaceTab = state['replaceTab'];
|
|
464
|
+
const forceReload = state['forceReload'] === true;
|
|
448
465
|
// Resolve injector phù hợp với route. Cần xử lý 3 trường hợp:
|
|
449
466
|
// - Standalone route (Angular đã set _injector trên routeConfig sau khi activate)
|
|
450
467
|
// - NgModule lazy load (cần createNgModule từ class)
|
|
@@ -514,15 +531,22 @@ class SdTabRouterOutletComponent {
|
|
|
514
531
|
updatedTabs = updatedTabs.filter((_, i) => i !== activatedIndex);
|
|
515
532
|
}
|
|
516
533
|
if (existedIndex >= 0) {
|
|
517
|
-
//
|
|
518
|
-
//
|
|
519
|
-
// component → tab bị "reload" mỗi khi click lại hoặc navigate cùng URL.
|
|
534
|
+
// Mặc định tab đã tồn tại chỉ được activate, không thay tab object. forceReload là nhánh
|
|
535
|
+
// explicit duy nhất thay SdTab + injector để body và nav item được tạo lại.
|
|
520
536
|
//
|
|
521
537
|
// splice phía trên có thể đã shift index nếu activatedIndex < existedIndex.
|
|
522
538
|
const idx = replaceTab && activatedIndex >= 0 && activatedIndex < existedIndex ? existedIndex - 1 : existedIndex;
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
539
|
+
if (forceReload) {
|
|
540
|
+
updatedTabs = updatedTabs.map((tab, index) => (index === idx ? newTab : tab));
|
|
541
|
+
this.#tabRouterService.setCurrentTab(newTab);
|
|
542
|
+
this.#tabRouterService.pushEvent(newTab, SdTabActivated);
|
|
543
|
+
this.tabs.set(updatedTabs);
|
|
544
|
+
}
|
|
545
|
+
else {
|
|
546
|
+
this.#tabRouterService.setCurrentTab(updatedTabs[idx]);
|
|
547
|
+
this.#tabRouterService.pushEvent(updatedTabs[idx], SdTabActivated);
|
|
548
|
+
this.tabs.set(updatedTabs);
|
|
549
|
+
}
|
|
526
550
|
}
|
|
527
551
|
else {
|
|
528
552
|
// Tab chưa tồn tại → thêm mới ở cuối.
|
|
@@ -555,11 +579,11 @@ class SdTabRouterOutletComponent {
|
|
|
555
579
|
return null;
|
|
556
580
|
};
|
|
557
581
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabRouterOutletComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
558
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdTabRouterOutletComponent, isStandalone: true, selector: "sd-tab-router-outlet", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "tabRouterNav", first: true, predicate: ["tabRouterNav"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (disabled()) {\n <div class=\"tab-router__disabled-outlet\">\n <router-outlet></router-outlet>\n </div>\n} @else {\n <sd-tab-router-nav [tabs]=\"tabs()\" #tabRouterNav></sd-tab-router-nav>\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\"
|
|
582
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdTabRouterOutletComponent, isStandalone: true, selector: "sd-tab-router-outlet", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "tabRouterNav", first: true, predicate: ["tabRouterNav"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (disabled()) {\n <div class=\"tab-router__disabled-outlet\">\n <router-outlet></router-outlet>\n </div>\n} @else {\n <sd-tab-router-nav [tabs]=\"tabs()\" #tabRouterNav></sd-tab-router-nav>\n <div class=\"tab-router__list\">\n @for (tab of tabs(); track tab.injector ?? 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\"></div>\n }\n </div>\n}\n", styles: [":host{display:flex;flex-direction:column;width:100%;min-height:0;height:100vh}:host ::ng-deep .sd-loading{max-width:100%;max-height:100%}.tab-router__list{flex:1;min-height:0}.tab-router__disabled-outlet{flex:1;width:100%;min-height:0;overflow:auto}.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$1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "component", type: SdTabRouterNavComponent, selector: "sd-tab-router-nav", inputs: ["tabs"] }] });
|
|
559
583
|
}
|
|
560
584
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabRouterOutletComponent, decorators: [{
|
|
561
585
|
type: Component,
|
|
562
|
-
args: [{ selector: 'sd-tab-router-outlet', standalone: true, imports: [CommonModule, MatTooltipModule, RouterOutlet, SdTabRouterNavComponent], template: "@if (disabled()) {\n <div class=\"tab-router__disabled-outlet\">\n <router-outlet></router-outlet>\n </div>\n} @else {\n <sd-tab-router-nav [tabs]=\"tabs()\" #tabRouterNav></sd-tab-router-nav>\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\"
|
|
586
|
+
args: [{ selector: 'sd-tab-router-outlet', standalone: true, imports: [CommonModule, MatTooltipModule, RouterOutlet, SdTabRouterNavComponent], template: "@if (disabled()) {\n <div class=\"tab-router__disabled-outlet\">\n <router-outlet></router-outlet>\n </div>\n} @else {\n <sd-tab-router-nav [tabs]=\"tabs()\" #tabRouterNav></sd-tab-router-nav>\n <div class=\"tab-router__list\">\n @for (tab of tabs(); track tab.injector ?? 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\"></div>\n }\n </div>\n}\n", styles: [":host{display:flex;flex-direction:column;width:100%;min-height:0;height:100vh}:host ::ng-deep .sd-loading{max-width:100%;max-height:100%}.tab-router__list{flex:1;min-height:0}.tab-router__disabled-outlet{flex:1;width:100%;min-height:0;overflow:auto}.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"] }]
|
|
563
587
|
}], ctorParameters: () => [], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], tabRouterNav: [{ type: i0.ViewChild, args: ['tabRouterNav', { isSignal: true }] }] } });
|
|
564
588
|
// Custom Injector cho từng tab: override ActivatedRoute thành route của TAB ĐÓ
|
|
565
589
|
// (không phải route hiện tại của router). Nếu không override, mọi tab sẽ inject ActivatedRoute
|