@sdcorejs/angular 21.1.6 → 21.2.1
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 +10 -0
- package/assets/scss/core/menu.scss +18 -0
- package/assets/scss/core/utilities/_misc.scss +15 -0
- package/assets/scss/sd-core.scss +1 -0
- package/fesm2022/sdcorejs-angular-components-anchor.mjs +37 -13
- package/fesm2022/sdcorejs-angular-components-anchor.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-api-contract-builder.mjs +3409 -0
- package/fesm2022/sdcorejs-angular-components-api-contract-builder.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs +2 -2
- package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-badge.mjs +27 -2
- package/fesm2022/sdcorejs-angular-components-badge.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-button.mjs +2 -2
- package/fesm2022/sdcorejs-angular-components-button.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-ckeditor-styles.mjs +1 -1
- package/fesm2022/sdcorejs-angular-components-ckeditor-styles.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-code-editor.mjs +3 -3
- package/fesm2022/sdcorejs-angular-components-code-editor.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-data-state.mjs +3 -3
- package/fesm2022/sdcorejs-angular-components-data-state.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-editor.mjs +2 -2
- package/fesm2022/sdcorejs-angular-components-editor.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-form-generic.mjs +324 -192
- package/fesm2022/sdcorejs-angular-components-form-generic.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-history.mjs +4 -4
- package/fesm2022/sdcorejs-angular-components-history.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-import-excel.mjs +13 -5
- package/fesm2022/sdcorejs-angular-components-import-excel.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-inform.mjs +3 -3
- package/fesm2022/sdcorejs-angular-components-inform.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs +164 -8
- package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-modal.mjs +30 -18
- package/fesm2022/sdcorejs-angular-components-modal.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-operator.mjs +2 -2
- package/fesm2022/sdcorejs-angular-components-operator.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-org-chart.mjs +60 -13
- package/fesm2022/sdcorejs-angular-components-org-chart.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-preview.mjs +27 -17
- package/fesm2022/sdcorejs-angular-components-preview.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-query-bar.mjs +192 -30
- package/fesm2022/sdcorejs-angular-components-query-bar.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-query-builder.mjs +187 -127
- package/fesm2022/sdcorejs-angular-components-query-builder.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-section.mjs +33 -2
- package/fesm2022/sdcorejs-angular-components-section.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-side-drawer.mjs +83 -24
- package/fesm2022/sdcorejs-angular-components-side-drawer.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-splitter.mjs +57 -21
- package/fesm2022/sdcorejs-angular-components-splitter.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-stepper.mjs +2 -2
- package/fesm2022/sdcorejs-angular-components-stepper.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-tab-router.mjs +68 -20
- package/fesm2022/sdcorejs-angular-components-tab-router.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-tab.mjs +3 -2
- package/fesm2022/sdcorejs-angular-components-tab.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-table.mjs +556 -268
- package/fesm2022/sdcorejs-angular-components-table.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-tree.mjs +80 -17
- package/fesm2022/sdcorejs-angular-components-tree.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-upload-file.mjs +48 -13
- package/fesm2022/sdcorejs-angular-components-upload-file.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components.mjs +7 -1
- package/fesm2022/sdcorejs-angular-components.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-configurations.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-directives.mjs +45 -8
- package/fesm2022/sdcorejs-angular-directives.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs +40 -29
- package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-checkbox.mjs +5 -5
- package/fesm2022/sdcorejs-angular-forms-checkbox.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs +67 -50
- package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-chip.mjs +57 -43
- package/fesm2022/sdcorejs-angular-forms-chip.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-date-range.mjs +232 -61
- package/fesm2022/sdcorejs-angular-forms-date-range.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-date.mjs +96 -24
- package/fesm2022/sdcorejs-angular-forms-date.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-datetime.mjs +131 -27
- package/fesm2022/sdcorejs-angular-forms-datetime.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-entity-picker.mjs +40 -16
- package/fesm2022/sdcorejs-angular-forms-entity-picker.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-input-color.mjs +16 -5
- package/fesm2022/sdcorejs-angular-forms-input-color.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-input-number.mjs +24 -9
- package/fesm2022/sdcorejs-angular-forms-input-number.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-input.mjs +58 -10
- package/fesm2022/sdcorejs-angular-forms-input.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-models.mjs +227 -6
- package/fesm2022/sdcorejs-angular-forms-models.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-radio.mjs +4 -4
- package/fesm2022/sdcorejs-angular-forms-radio.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-select.mjs +136 -38
- package/fesm2022/sdcorejs-angular-forms-select.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-switch.mjs +18 -3
- package/fesm2022/sdcorejs-angular-forms-switch.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-textarea.mjs +51 -62
- package/fesm2022/sdcorejs-angular-forms-textarea.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-time-range.mjs +57 -12
- package/fesm2022/sdcorejs-angular-forms-time-range.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-time.mjs +16 -5
- package/fesm2022/sdcorejs-angular-forms-time.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-tree-select.mjs +100 -9
- package/fesm2022/sdcorejs-angular-forms-tree-select.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms.mjs +91 -46
- package/fesm2022/sdcorejs-angular-forms.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-handlers.mjs +18 -3
- package/fesm2022/sdcorejs-angular-handlers.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-i18n.mjs +895 -68
- package/fesm2022/sdcorejs-angular-i18n.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-interceptors.mjs +87 -29
- package/fesm2022/sdcorejs-angular-interceptors.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-modules-auth.mjs +25 -15
- package/fesm2022/sdcorejs-angular-modules-auth.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-modules-icon.mjs +2 -2
- package/fesm2022/sdcorejs-angular-modules-icon.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-modules-keycloak.mjs +32 -3
- package/fesm2022/sdcorejs-angular-modules-keycloak.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-modules-layout.mjs +342 -111
- package/fesm2022/sdcorejs-angular-modules-layout.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-modules-permission.mjs +177 -40
- package/fesm2022/sdcorejs-angular-modules-permission.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-pipes.mjs +62 -28
- package/fesm2022/sdcorejs-angular-pipes.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-api.mjs +123 -13
- package/fesm2022/sdcorejs-angular-services-api.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-cache.mjs +53 -3
- package/fesm2022/sdcorejs-angular-services-cache.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-confirm.mjs +25 -73
- package/fesm2022/sdcorejs-angular-services-confirm.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-excel.mjs +12 -10
- package/fesm2022/sdcorejs-angular-services-excel.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-loading.mjs +75 -84
- package/fesm2022/sdcorejs-angular-services-loading.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-notify.mjs +46 -5
- package/fesm2022/sdcorejs-angular-services-notify.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-storage.mjs +4 -0
- package/fesm2022/sdcorejs-angular-services-storage.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services.mjs +0 -1
- package/fesm2022/sdcorejs-angular-services.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-utilities-extensions.mjs +225 -80
- package/fesm2022/sdcorejs-angular-utilities-extensions.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-utilities-models.mjs +1 -63
- package/fesm2022/sdcorejs-angular-utilities-models.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular.mjs +17 -10
- package/fesm2022/sdcorejs-angular.mjs.map +1 -1
- package/package.json +36 -25
- package/types/sdcorejs-angular-components-anchor.d.ts +3 -1
- package/types/sdcorejs-angular-components-api-contract-builder.d.ts +694 -0
- package/types/sdcorejs-angular-components-badge.d.ts +2 -0
- package/types/sdcorejs-angular-components-ckeditor-styles.d.ts +1 -1
- package/types/sdcorejs-angular-components-form-generic.d.ts +44 -33
- package/types/sdcorejs-angular-components-import-excel.d.ts +1 -0
- package/types/sdcorejs-angular-components-mini-editor.d.ts +1 -1
- package/types/sdcorejs-angular-components-modal-resizable.d.ts +92 -0
- package/types/sdcorejs-angular-components-modal.d.ts +3 -2
- package/types/sdcorejs-angular-components-org-chart.d.ts +6 -0
- package/types/sdcorejs-angular-components-preview.d.ts +1 -0
- package/types/sdcorejs-angular-components-query-bar.d.ts +55 -17
- package/types/sdcorejs-angular-components-query-builder.d.ts +150 -98
- package/types/sdcorejs-angular-components-section.d.ts +5 -0
- package/types/sdcorejs-angular-components-tab-router.d.ts +19 -19
- package/types/sdcorejs-angular-components-table.d.ts +251 -109
- package/types/sdcorejs-angular-components-tree.d.ts +48 -30
- package/types/sdcorejs-angular-components-upload-file.d.ts +6 -3
- package/types/sdcorejs-angular-components.d.ts +7 -1
- package/types/sdcorejs-angular-configurations.d.ts +1 -2
- package/types/sdcorejs-angular-forms-autocomplete.d.ts +5 -2
- package/types/sdcorejs-angular-forms-chip-calendar.d.ts +10 -0
- package/types/sdcorejs-angular-forms-date-range.d.ts +14 -2
- package/types/sdcorejs-angular-forms-date.d.ts +13 -2
- package/types/sdcorejs-angular-forms-datetime.d.ts +13 -2
- package/types/sdcorejs-angular-forms-entity-picker.d.ts +9 -3
- package/types/sdcorejs-angular-forms-input-color.d.ts +4 -0
- package/types/sdcorejs-angular-forms-input-number.d.ts +3 -0
- package/types/sdcorejs-angular-forms-input.d.ts +3 -0
- package/types/sdcorejs-angular-forms-models.d.ts +119 -4
- package/types/sdcorejs-angular-forms-select.d.ts +27 -4
- package/types/sdcorejs-angular-forms-switch.d.ts +7 -1
- package/types/sdcorejs-angular-forms-textarea.d.ts +5 -3
- package/types/sdcorejs-angular-forms-time-range.d.ts +22 -1
- package/types/sdcorejs-angular-forms-tree-select.d.ts +36 -5
- package/types/sdcorejs-angular-forms.d.ts +22 -13
- package/types/sdcorejs-angular-i18n.d.ts +175 -16
- package/types/sdcorejs-angular-interceptors.d.ts +13 -3
- package/types/sdcorejs-angular-modules-auth.d.ts +1 -1
- package/types/sdcorejs-angular-modules-keycloak.d.ts +1 -0
- package/types/sdcorejs-angular-modules-layout.d.ts +94 -28
- package/types/sdcorejs-angular-modules-permission.d.ts +66 -4
- package/types/sdcorejs-angular-pipes.d.ts +27 -5
- package/types/sdcorejs-angular-services-api.d.ts +21 -1
- package/types/sdcorejs-angular-services-cache.d.ts +11 -1
- package/types/sdcorejs-angular-services-notify.d.ts +1 -1
- package/types/sdcorejs-angular-services.d.ts +0 -1
- package/types/sdcorejs-angular-utilities-extensions.d.ts +83 -61
- package/types/sdcorejs-angular-utilities-models.d.ts +2 -76
- package/types/sdcorejs-angular.d.ts +11 -2
- package/fesm2022/sdcorejs-angular-components-base.mjs +0 -23
- package/fesm2022/sdcorejs-angular-components-base.mjs.map +0 -1
- package/fesm2022/sdcorejs-angular-components-document-builder.mjs +0 -5038
- package/fesm2022/sdcorejs-angular-components-document-builder.mjs.map +0 -1
- package/fesm2022/sdcorejs-angular-models.mjs +0 -6
- package/fesm2022/sdcorejs-angular-models.mjs.map +0 -1
- package/fesm2022/sdcorejs-angular-services-docx.mjs +0 -291
- package/fesm2022/sdcorejs-angular-services-docx.mjs.map +0 -1
- package/fesm2022/sdcorejs-angular-services-license.mjs +0 -97
- package/fesm2022/sdcorejs-angular-services-license.mjs.map +0 -1
- package/types/sdcorejs-angular-components-base.d.ts +0 -11
- package/types/sdcorejs-angular-components-document-builder.d.ts +0 -252
- package/types/sdcorejs-angular-models.d.ts +0 -2
- package/types/sdcorejs-angular-services-docx.d.ts +0 -27
- package/types/sdcorejs-angular-services-license.d.ts +0 -12
|
@@ -1,25 +1,155 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { viewChild, input, booleanAttribute, output,
|
|
2
|
+
import { inject, Injectable, viewChild, input, booleanAttribute, output, ChangeDetectorRef, DestroyRef, signal, afterNextRender, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
3
|
import { Utilities } from '@sdcorejs/utils/fns';
|
|
4
4
|
import * as i2 from '@angular/cdk/portal';
|
|
5
5
|
import { CdkPortal, DomPortalOutlet, PortalModule } from '@angular/cdk/portal';
|
|
6
|
-
import { CommonModule } from '@angular/common';
|
|
6
|
+
import { DOCUMENT, CommonModule } from '@angular/common';
|
|
7
7
|
import * as i1 from '@angular/material/button';
|
|
8
8
|
import { MatButtonModule } from '@angular/material/button';
|
|
9
9
|
import { MatDialogModule } from '@angular/material/dialog';
|
|
10
10
|
import { SdLoadingService } from '@sdcorejs/angular/services';
|
|
11
11
|
import { SdIcon } from '@sdcorejs/angular/modules/icon';
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Sổ đăng ký các panel `<sd-modal-resizable>` đang sống.
|
|
15
|
+
*
|
|
16
|
+
* why: mỗi panel được portal ra thẳng `document.body` nên việc xếp chỗ (width/right) là bài toán
|
|
17
|
+
* chung của cả stack. Trước đây `#arrangePanels` quét `document.querySelectorAll('.modal-resizable')`
|
|
18
|
+
* — query đó bắt MỌI phần tử mang class này trong tài liệu, kể cả markup của component khác hoặc
|
|
19
|
+
* của app dùng trùng tên class, rồi ghi đè inline style lên chúng. Registry giới hạn phạm vi ghi
|
|
20
|
+
* đúng vào các instance do library tạo ra và còn sống.
|
|
21
|
+
*/
|
|
22
|
+
class SdModalResizableRegistry {
|
|
23
|
+
// why: dùng token DOCUMENT thay vì global `document`, đồng bộ với `preview-image` — service
|
|
24
|
+
// `providedIn:'root'` có thể được khởi tạo trong quá trình render phía server.
|
|
25
|
+
#document = inject(DOCUMENT);
|
|
26
|
+
// Set giữ nguyên thứ tự đăng ký → thứ tự xếp chỗ ổn định, khớp thứ tự panel được mở.
|
|
27
|
+
#ids = new Set();
|
|
28
|
+
register(id) {
|
|
29
|
+
this.#ids.add(id);
|
|
30
|
+
}
|
|
31
|
+
unregister(id) {
|
|
32
|
+
this.#ids.delete(id);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Phần tử panel của các instance đã đăng ký và đang có mặt trong DOM.
|
|
36
|
+
* Instance đã destroy hoặc chưa attach portal sẽ bị bỏ qua.
|
|
37
|
+
*/
|
|
38
|
+
panels() {
|
|
39
|
+
const elements = [];
|
|
40
|
+
for (const id of this.#ids) {
|
|
41
|
+
const element = this.#document.getElementById(id);
|
|
42
|
+
if (element)
|
|
43
|
+
elements.push(element);
|
|
44
|
+
}
|
|
45
|
+
return elements;
|
|
46
|
+
}
|
|
47
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdModalResizableRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
48
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdModalResizableRegistry, providedIn: 'root' });
|
|
49
|
+
}
|
|
50
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdModalResizableRegistry, decorators: [{
|
|
51
|
+
type: Injectable,
|
|
52
|
+
args: [{ providedIn: 'root' }]
|
|
53
|
+
}] });
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* `<sd-modal-resizable>` — right-docked, non-modal panel that can be minimized, expanded or pushed
|
|
57
|
+
* fullscreen. Several panels stack side by side along the right edge of the viewport, so the user
|
|
58
|
+
* can keep multiple records open at once while still working with the page behind them.
|
|
59
|
+
*
|
|
60
|
+
* Use it for side-by-side comparison / inspector workflows. Use `<sd-modal>` when the task is
|
|
61
|
+
* blocking, and `<sd-side-drawer>` when a single overlay with a backdrop is enough.
|
|
62
|
+
*
|
|
63
|
+
* NOTE ON DOCS: this component deliberately has **no** sibling `sd-modal-resizable.md`. Every `*.md`
|
|
64
|
+
* under the library is swept into `published-docs/<version>/index.json` by `scripts/collect-docs.mjs`,
|
|
65
|
+
* and `scripts/generate-showcase-example-sources.test.mjs` asserts that list is 1:1 with the
|
|
66
|
+
* `publishedDocId` set in the showcase documentation registry. A published doc with no showcase page
|
|
67
|
+
* therefore turns that release guard red. Keep the reference here until the component ships a
|
|
68
|
+
* showcase demo + registry entry; only then add the `.md`.
|
|
69
|
+
*
|
|
70
|
+
* ```ts
|
|
71
|
+
* import { SdModalResizable } from '@sdcorejs/angular/components/modal-resizable';
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* ```html
|
|
75
|
+
* <sd-button title="Open detail" type="fill" color="primary" (click)="panel.open()"></sd-button>
|
|
76
|
+
*
|
|
77
|
+
* <sd-modal-resizable #panel width="520px">
|
|
78
|
+
* <span sdTitle>Đơn hàng #1024</span>
|
|
79
|
+
*
|
|
80
|
+
* <div sdBody class="panel-body">
|
|
81
|
+
* <sd-section icon="receipt" title="Thông tin chung">
|
|
82
|
+
* <sd-section-item label="Khách hàng">Nguyen Van An</sd-section-item>
|
|
83
|
+
* </sd-section>
|
|
84
|
+
* </div>
|
|
85
|
+
*
|
|
86
|
+
* <sd-button sdFooterRight type="text" title="Đóng" (click)="panel.close()"></sd-button>
|
|
87
|
+
* </sd-modal-resizable>
|
|
88
|
+
* ```
|
|
89
|
+
*
|
|
90
|
+
* ### Slots
|
|
91
|
+
* | Selector | Where it renders |
|
|
92
|
+
* | ----------------- | ------------------------- |
|
|
93
|
+
* | `[sdTitle]` | Header title area. |
|
|
94
|
+
* | `[sdBody]` | Scrollable content area. |
|
|
95
|
+
* | `[sdFooterLeft]` | Footer left action group. |
|
|
96
|
+
* | `[sdFooterRight]` | Footer right action group.|
|
|
97
|
+
*
|
|
98
|
+
* Header, body and footer only render while the panel is open.
|
|
99
|
+
*
|
|
100
|
+
* ### Panel stacking
|
|
101
|
+
* The panel is portalled onto `document.body` (via `CdkPortal` + `DomPortalOutlet`), so laying panels
|
|
102
|
+
* out is a concern shared by all live instances rather than by a single view. Arrangement is scoped
|
|
103
|
+
* through the root-provided `SdModalResizableRegistry`: each instance registers its panel id on
|
|
104
|
+
* construction and unregisters it on destroy. `open`, `close`, `minimize`, `maximize` and
|
|
105
|
+
* `toggleMaximum` re-run the arrangement and rewrite inline `width` / `right` **only on registered,
|
|
106
|
+
* still-mounted panels**, in registration order. Consequences worth knowing:
|
|
107
|
+
* - Markup of your own that happens to carry the `.modal-resizable` class is **not** touched.
|
|
108
|
+
* (Earlier builds ran `document.querySelectorAll('.modal-resizable')` and rewrote every match.)
|
|
109
|
+
* - A destroyed panel immediately stops taking up a slot for the instances that outlive it.
|
|
110
|
+
* - Panels marked `c-closed`, and expanded panels with no captured `data-width`, are skipped.
|
|
111
|
+
*
|
|
112
|
+
* ### Lifecycle / teardown
|
|
113
|
+
* `open()`, `close()` and the arrangement pass each schedule a short `setTimeout` (100ms / 100ms /
|
|
114
|
+
* 200ms) that mutates DOM. All of them are tracked and cleared in `DestroyRef.onDestroy`, together
|
|
115
|
+
* with unregistering from the registry and destroying the portal view. Destroying the component
|
|
116
|
+
* mid-animation cannot run layout work against a detached panel.
|
|
117
|
+
*
|
|
118
|
+
* ### Anti-patterns
|
|
119
|
+
* - Rendering it inside a scroll container and expecting it to be clipped — it lives on `<body>`.
|
|
120
|
+
* - Reusing the `.modal-resizable` class on your own elements to "join" the stack; register a real
|
|
121
|
+
* component instead.
|
|
122
|
+
* - Writing inline `width` / `right` on a panel yourself — the next arrangement pass overwrites both.
|
|
123
|
+
*
|
|
124
|
+
* ### Tests
|
|
125
|
+
* `src/modal-resizable.component.spec.ts` — portal attach, open/close/minimize/maximize math,
|
|
126
|
+
* loading, editing, timer teardown, registry scoping. Run:
|
|
127
|
+
* `npx ng test sdcorejs-angular --watch=false --browsers=ChromeHeadless --include='projects/sdcorejs-angular/components/modal-resizable/src/modal-resizable.component.spec.ts'`
|
|
128
|
+
*
|
|
129
|
+
* ### Related
|
|
130
|
+
* `<sd-modal>` (blocking dialog / bottom-sheet), `<sd-side-drawer>` (single right-side overlay with
|
|
131
|
+
* a body scroll lock), `SdLoadingService` (powers `startLoading()` / `stopLoading()`).
|
|
132
|
+
*/
|
|
13
133
|
class SdModalResizable {
|
|
134
|
+
/** Generated DOM id of the panel element (`I<uuid>`). */
|
|
14
135
|
id = `I${Utilities.generateUuid()}`;
|
|
15
136
|
portal = viewChild.required(CdkPortal);
|
|
137
|
+
/** Marks the panel as editable. Bare attribute = `true`. Default `false`. */
|
|
16
138
|
editable = input(false, { ...(ngDevMode ? { debugName: "editable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
139
|
+
/** CSS width used while the panel is expanded (not a minimum). Default `'480px'`. */
|
|
17
140
|
width = input('480px', ...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
|
|
141
|
+
/** Emitted by `toggleEditable()` with the new editing state. */
|
|
18
142
|
editingChanged = output();
|
|
19
143
|
#embeddedViewRef;
|
|
20
144
|
#ref = inject(ChangeDetectorRef);
|
|
21
145
|
#loadingService = inject(SdLoadingService);
|
|
22
146
|
#destroyRef = inject(DestroyRef);
|
|
147
|
+
#registry = inject(SdModalResizableRegistry);
|
|
148
|
+
// why: open/close/#arrangePanels đều hẹn setTimeout thao tác DOM. Gom id vào một set để
|
|
149
|
+
// onDestroy clear sạch, tránh callback chạy trên portal đã tháo (element không còn, hoặc tệ
|
|
150
|
+
// hơn: element của instance khác vừa tái sử dụng id).
|
|
151
|
+
#timers = new Set();
|
|
152
|
+
// Trạng thái panel — đọc được từ ngoài (template/consumer), chỉ component tự ghi.
|
|
23
153
|
isEditing = signal(false, ...(ngDevMode ? [{ debugName: "isEditing" }] : /* istanbul ignore next */ []));
|
|
24
154
|
isOpened = signal(false, ...(ngDevMode ? [{ debugName: "isOpened" }] : /* istanbul ignore next */ []));
|
|
25
155
|
isHover = signal(false, ...(ngDevMode ? [{ debugName: "isHover" }] : /* istanbul ignore next */ []));
|
|
@@ -27,6 +157,7 @@ class SdModalResizable {
|
|
|
27
157
|
isMinimum = signal(false, ...(ngDevMode ? [{ debugName: "isMinimum" }] : /* istanbul ignore next */ []));
|
|
28
158
|
isMaximum = signal(false, ...(ngDevMode ? [{ debugName: "isMaximum" }] : /* istanbul ignore next */ []));
|
|
29
159
|
constructor() {
|
|
160
|
+
this.#registry.register(this.id);
|
|
30
161
|
afterNextRender(() => {
|
|
31
162
|
// why: attachTemplatePortal tạo embedded view từ VCR của chính CdkPortal →
|
|
32
163
|
// KHÔNG cần appRef/injector/document. Chỉ truyền outletElement để tương thích
|
|
@@ -36,15 +167,28 @@ class SdModalResizable {
|
|
|
36
167
|
this.#detectChanges();
|
|
37
168
|
});
|
|
38
169
|
this.#destroyRef.onDestroy(() => {
|
|
170
|
+
this.#registry.unregister(this.id);
|
|
171
|
+
for (const timer of this.#timers)
|
|
172
|
+
clearTimeout(timer);
|
|
173
|
+
this.#timers.clear();
|
|
39
174
|
this.#embeddedViewRef?.destroy();
|
|
40
175
|
});
|
|
41
176
|
}
|
|
177
|
+
/** setTimeout có theo dõi — huỷ được hàng loạt trong onDestroy. */
|
|
178
|
+
#schedule(callback, delay) {
|
|
179
|
+
const timer = setTimeout(() => {
|
|
180
|
+
this.#timers.delete(timer);
|
|
181
|
+
callback();
|
|
182
|
+
}, delay);
|
|
183
|
+
this.#timers.add(timer);
|
|
184
|
+
}
|
|
185
|
+
/** Opens the panel, captures its rendered width, and expands it. */
|
|
42
186
|
open = () => {
|
|
43
187
|
this.isOpened.set(true);
|
|
44
188
|
this.#detectChanges();
|
|
45
189
|
const element = this.#getElement();
|
|
46
190
|
if (element && !element.classList.contains('c-minium')) {
|
|
47
|
-
|
|
191
|
+
this.#schedule(() => {
|
|
48
192
|
const maximumWidth = element.offsetWidth;
|
|
49
193
|
if (maximumWidth > 0) {
|
|
50
194
|
element.dataset['width'] = maximumWidth.toString();
|
|
@@ -54,10 +198,11 @@ class SdModalResizable {
|
|
|
54
198
|
}
|
|
55
199
|
this.maximize();
|
|
56
200
|
};
|
|
201
|
+
/** Collapses the panel to zero width and stops loading. */
|
|
57
202
|
close = () => {
|
|
58
203
|
this.isOpened.set(false);
|
|
59
204
|
this.#detectChanges();
|
|
60
|
-
|
|
205
|
+
this.#schedule(() => {
|
|
61
206
|
const element = this.#getElement();
|
|
62
207
|
element?.style.setProperty('width', '0px');
|
|
63
208
|
element?.style.setProperty('right', '0px');
|
|
@@ -67,48 +212,59 @@ class SdModalResizable {
|
|
|
67
212
|
this.stopLoading();
|
|
68
213
|
this.#arrangePanels();
|
|
69
214
|
};
|
|
215
|
+
/** Shrinks the panel to the 300px minimum strip. */
|
|
70
216
|
minimize = () => {
|
|
71
217
|
this.isMinimum.set(true);
|
|
72
218
|
this.#detectChanges();
|
|
73
219
|
this.#arrangePanels();
|
|
74
220
|
};
|
|
221
|
+
/** Restores the panel to its expanded width. */
|
|
75
222
|
maximize = () => {
|
|
76
223
|
this.isMinimum.set(false);
|
|
77
224
|
this.#detectChanges();
|
|
78
225
|
this.#arrangePanels();
|
|
79
226
|
};
|
|
227
|
+
/** Toggles fullscreen (`calc(100% - 16px)`). */
|
|
80
228
|
toggleMaximum = () => {
|
|
81
229
|
this.isMaximum.update(isMaximum => !isMaximum);
|
|
82
230
|
this.#detectChanges();
|
|
83
231
|
this.#arrangePanels();
|
|
84
232
|
};
|
|
233
|
+
/** Drives the scoped loading overlay through `SdLoadingService`. */
|
|
85
234
|
startLoading = () => {
|
|
86
235
|
this.isLoading.set(true);
|
|
87
236
|
this.#detectChanges();
|
|
88
237
|
this.#loadingService.stop(`#${this.id}`);
|
|
89
238
|
this.#loadingService.start(`#${this.id}`);
|
|
90
239
|
};
|
|
240
|
+
/** Stops the scoped loading overlay. */
|
|
91
241
|
stopLoading = () => {
|
|
92
242
|
this.isLoading.set(false);
|
|
93
243
|
this.#detectChanges();
|
|
94
244
|
this.#loadingService.stop(`#${this.id}`);
|
|
95
245
|
};
|
|
246
|
+
/** Flips `isEditing` and emits `editingChanged`. */
|
|
96
247
|
toggleEditable() {
|
|
97
248
|
this.isEditing.update(isEditing => !isEditing);
|
|
98
249
|
this.#detectChanges();
|
|
99
250
|
this.editingChanged.emit(this.isEditing());
|
|
100
251
|
}
|
|
252
|
+
/** Hover state hook bound in the template. */
|
|
101
253
|
onFocus = () => {
|
|
102
254
|
this.isHover.set(true);
|
|
103
255
|
};
|
|
256
|
+
/** Hover state hook bound in the template. */
|
|
104
257
|
onBlur = () => {
|
|
105
258
|
this.isHover.set(false);
|
|
106
259
|
};
|
|
107
260
|
#arrangePanels = () => {
|
|
108
|
-
|
|
261
|
+
this.#schedule(() => {
|
|
109
262
|
let totalRight = 0;
|
|
110
263
|
const minimumWidth = 300;
|
|
111
|
-
|
|
264
|
+
// why: chỉ xếp chỗ các panel do <sd-modal-resizable> đang sống sở hữu (registry), KHÔNG
|
|
265
|
+
// querySelectorAll('.modal-resizable') toàn document — query đó ghi đè inline width/right
|
|
266
|
+
// lên mọi phần tử trùng class, kể cả markup của component khác không liên quan.
|
|
267
|
+
this.#registry.panels().forEach(item => {
|
|
112
268
|
if (item.classList.contains('c-minium')) {
|
|
113
269
|
item.style.setProperty('width', `${minimumWidth}px`);
|
|
114
270
|
item.style.setProperty('right', `${totalRight}px`);
|
|
@@ -142,11 +298,11 @@ class SdModalResizable {
|
|
|
142
298
|
this.#embeddedViewRef?.detectChanges();
|
|
143
299
|
}
|
|
144
300
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdModalResizable, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
145
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdModalResizable, isStandalone: true, selector: "sd-modal-resizable", inputs: { editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { editingChanged: "editingChanged" }, viewQueries: [{ propertyName: "portal", first: true, predicate: CdkPortal, descendants: true, isSignal: true }], ngImport: i0, template: "<ng-template cdkPortal>\n <div\n [id]=\"id\"\n class=\"modal-resizable\"\n [style.width]=\"width()\"\n [class.modal-resizable--active]=\"isOpened()\"\n [class.modal-resizable--loading]=\"isLoading()\"\n [class.c-minium]=\"isMinimum()\"\n [class.c-fullscreen]=\"isMaximum()\"\n (mouseover)=\"onFocus()\"\n (focus)=\"onFocus()\"\n (mouseout)=\"onBlur()\"\n (blur)=\"onBlur()\">\n @if (isOpened()) {\n <div class=\"modal-resizable__header\">\n <div class=\"modal-resizable__title\">\n <ng-content select=\"[sdTitle]\"></ng-content>\n </div>\n <div class=\"modal-resizable__actions\">\n @if (!isMinimum()) {\n <button type=\"button\" class=\"c-action--white c-min\" (click)=\"minimize()\" mat-icon-button title=\"Thu nh\u1ECF\">\n <sd-icon name=\"remove\"></sd-icon>\n </button>\n }\n @if (isMinimum()) {\n <button type=\"button\" class=\"c-action--white c-max\" (click)=\"maximize()\" mat-icon-button title=\"Ph\u00F3ng to\">\n <sd-icon name=\"remove\"></sd-icon>\n </button>\n }\n @if (!isMaximum()) {\n <button type=\"button\" class=\"c-action--white\" (click)=\"toggleMaximum()\" mat-icon-button title=\"To\u00E0n m\u00E0n h\u00ECnh\">\n <sd-icon name=\"open_in_full\"></sd-icon>\n </button>\n }\n @if (isMaximum()) {\n <button type=\"button\" class=\"c-action--white\" (click)=\"toggleMaximum()\" mat-icon-button title=\"Tho\u00E1t to\u00E0n m\u00E0n h\u00ECnh\">\n <sd-icon name=\"fullscreen_exit\"></sd-icon>\n </button>\n }\n <button type=\"button\" class=\"c-action--white\" (click)=\"close()\" mat-icon-button title=\"\u0110\u00F3ng\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n </div>\n </div>\n <div class=\"modal-resizable__body\">\n <div class=\"modal-resizable__content\">\n <ng-content select=\"[sdBody]\"></ng-content>\n </div>\n </div>\n <div class=\"modal-resizable__footer\">\n <div class=\"modal-resizable__footer-left\"><ng-content select=\"[sdFooterLeft]\"></ng-content></div>\n <div class=\"modal-resizable__footer-right\"><ng-content select=\"[sdFooterRight]\"></ng-content></div>\n </div>\n }\n </div>\n</ng-template>\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-primary-contrast{color:var(--sd-primary-contrast)!important}.bg-primary-contrast{background:var(--sd-primary-contrast)!important}.border-primary-contrast{border-color:var(--sd-primary-contrast)!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-secondary-contrast{color:var(--sd-secondary-contrast)!important}.bg-secondary-contrast{background:var(--sd-secondary-contrast)!important}.border-secondary-contrast{border-color:var(--sd-secondary-contrast)!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-info-contrast{color:var(--sd-info-contrast)!important}.bg-info-contrast{background:var(--sd-info-contrast)!important}.border-info-contrast{border-color:var(--sd-info-contrast)!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-success-contrast{color:var(--sd-success-contrast)!important}.bg-success-contrast{background:var(--sd-success-contrast)!important}.border-success-contrast{border-color:var(--sd-success-contrast)!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-warning-contrast{color:var(--sd-warning-contrast)!important}.bg-warning-contrast{background:var(--sd-warning-contrast)!important}.border-warning-contrast{border-color:var(--sd-warning-contrast)!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-error-contrast{color:var(--sd-error-contrast)!important}.bg-error-contrast{background:var(--sd-error-contrast)!important}.border-error-contrast{border-color:var(--sd-error-contrast)!important}.text-surface{color:var(--sd-surface)!important}.bg-surface{background:var(--sd-surface)!important}.border-surface{border-color:var(--sd-surface)!important}.text-surface-muted{color:var(--sd-surface-muted)!important}.bg-surface-muted{background:var(--sd-surface-muted)!important}.border-surface-muted{border-color:var(--sd-surface-muted)!important}.text-text{color:var(--sd-text)!important}.bg-text{background:var(--sd-text)!important}.border-text{border-color:var(--sd-text)!important}.text-text-secondary{color:var(--sd-text-secondary)!important}.bg-text-secondary{background:var(--sd-text-secondary)!important}.border-text-secondary{border-color:var(--sd-text-secondary)!important}.text-text-muted{color:var(--sd-text-muted)!important}.bg-text-muted{background:var(--sd-text-muted)!important}.border-text-muted{border-color:var(--sd-text-muted)!important}.text-border{color:var(--sd-border)!important}.bg-border{background:var(--sd-border)!important}.border-border{border-color:var(--sd-border)!important}.text-border-strong{color:var(--sd-border-strong)!important}.bg-border-strong{background:var(--sd-border-strong)!important}.border-border-strong{border-color:var(--sd-border-strong)!important}.text-disabled-bg{color:var(--sd-disabled-bg)!important}.bg-disabled-bg{background:var(--sd-disabled-bg)!important}.border-disabled-bg{border-color:var(--sd-disabled-bg)!important}.text-disabled-text{color:var(--sd-disabled-text)!important}.bg-disabled-text{background:var(--sd-disabled-text)!important}.border-disabled-text{border-color:var(--sd-disabled-text)!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}.text-secondary{color:var(--sd-text-secondary)!important}.text-muted{color:var(--sd-text-muted)!important}.text-body{color:var(--sd-text)!important}.bg-muted{background:var(--sd-surface-muted)!important}.modal-resizable{position:fixed;right:12px;top:80px;bottom:0;background-color:#fff;z-index:998;display:flex;flex-direction:column;opacity:0;visibility:hidden;transform:translate3d(0,100%,0);transition:all .3s ease-in-out;pointer-events:none;box-shadow:0 .4px .8px #0000001a,0 3px 6px #0003;border-radius:4px 4px 0 0}.modal-resizable button.c-action--white:focus{outline:none!important}.modal-resizable.c-minium-of-list{top:auto;bottom:0;opacity:1;visibility:visible;pointer-events:all;transform:translateZ(0)}.modal-resizable.c-minium{top:auto!important;z-index:998!important}.modal-resizable.c-minium .modal-resizable__body,.modal-resizable.c-minium .modal-resizable__footer{display:none}.modal-resizable.c-fullscreen{top:0;z-index:999}.c-action{color:var(--sd-secondary)}.c-action:focus{outline:none}.c-action--white.c-min{margin-bottom:-12px}.c-action--white.c-max{margin-top:-12px}.modal-resizable__icon{border:0;background:transparent;color:#212121;overflow:hidden;outline:none}.modal-resizable__icon sd-icon{float:left}.modal-resizable__header{display:flex;align-items:center;flex-wrap:nowrap;gap:4px;min-height:40px;padding:0 16px;background-color:#5d6165;border-radius:4px 4px 0 0}.modal-resizable__body{flex:1;position:relative}.modal-resizable__content{position:absolute;padding:8px 16px 16px;inset:0;overflow:auto}.modal-resizable__footer{border-top:1px solid #dde0e5;padding:5px 16px;display:flex;align-items:center;justify-content:space-between;gap:8px}.modal-resizable__footer-left,.modal-resizable__footer-right{display:inline-flex;align-items:center;gap:8px}.modal-resizable__footer-right{margin-left:auto}.modal-resizable--active{opacity:1;visibility:visible;pointer-events:all;transform:translateZ(0)}.modal-resizable__title{flex:1 1 0;min-width:0;font-size:14px;line-height:20px;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.modal-resizable__title>*{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.modal-resizable__actions{display:flex;align-items:center;flex:0 0 auto;gap:4px;margin-left:auto}.modal-resizable__actions .c-action--white{flex:0 0 auto;width:32px;height:32px;line-height:32px}.modal-resizable__actions .c-action--white.c-min,.modal-resizable__actions .c-action--white.c-max{margin:0}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatDialogModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: PortalModule }, { kind: "directive", type: i2.CdkPortal, selector: "[cdkPortal]", exportAs: ["cdkPortal"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
301
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdModalResizable, isStandalone: true, selector: "sd-modal-resizable", inputs: { editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { editingChanged: "editingChanged" }, viewQueries: [{ propertyName: "portal", first: true, predicate: CdkPortal, descendants: true, isSignal: true }], ngImport: i0, template: "<ng-template cdkPortal>\n <div\n [id]=\"id\"\n class=\"modal-resizable\"\n [style.width]=\"width()\"\n [class.modal-resizable--active]=\"isOpened()\"\n [class.modal-resizable--loading]=\"isLoading()\"\n [class.c-minium]=\"isMinimum()\"\n [class.c-fullscreen]=\"isMaximum()\"\n (mouseover)=\"onFocus()\"\n (focus)=\"onFocus()\"\n (mouseout)=\"onBlur()\"\n (blur)=\"onBlur()\">\n @if (isOpened()) {\n <div class=\"modal-resizable__header\">\n <div class=\"modal-resizable__title\">\n <ng-content select=\"[sdTitle]\"></ng-content>\n </div>\n <div class=\"modal-resizable__actions\">\n @if (!isMinimum()) {\n <button type=\"button\" class=\"c-action--white c-min\" (click)=\"minimize()\" mat-icon-button title=\"Thu nh\u1ECF\">\n <sd-icon name=\"remove\"></sd-icon>\n </button>\n }\n @if (isMinimum()) {\n <button type=\"button\" class=\"c-action--white c-max\" (click)=\"maximize()\" mat-icon-button title=\"Ph\u00F3ng to\">\n <sd-icon name=\"remove\"></sd-icon>\n </button>\n }\n @if (!isMaximum()) {\n <button type=\"button\" class=\"c-action--white\" (click)=\"toggleMaximum()\" mat-icon-button title=\"To\u00E0n m\u00E0n h\u00ECnh\">\n <sd-icon name=\"open_in_full\"></sd-icon>\n </button>\n }\n @if (isMaximum()) {\n <button type=\"button\" class=\"c-action--white\" (click)=\"toggleMaximum()\" mat-icon-button title=\"Tho\u00E1t to\u00E0n m\u00E0n h\u00ECnh\">\n <sd-icon name=\"fullscreen_exit\"></sd-icon>\n </button>\n }\n <button type=\"button\" class=\"c-action--white\" (click)=\"close()\" mat-icon-button title=\"\u0110\u00F3ng\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n </div>\n </div>\n <div class=\"modal-resizable__body\">\n <div class=\"modal-resizable__content\">\n <ng-content select=\"[sdBody]\"></ng-content>\n </div>\n </div>\n <div class=\"modal-resizable__footer\">\n <div class=\"modal-resizable__footer-left\"><ng-content select=\"[sdFooterLeft]\"></ng-content></div>\n <div class=\"modal-resizable__footer-right\"><ng-content select=\"[sdFooterRight]\"></ng-content></div>\n </div>\n }\n </div>\n</ng-template>\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-primary-contrast{color:var(--sd-primary-contrast)!important}.bg-primary-contrast{background:var(--sd-primary-contrast)!important}.border-primary-contrast{border-color:var(--sd-primary-contrast)!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-secondary-contrast{color:var(--sd-secondary-contrast)!important}.bg-secondary-contrast{background:var(--sd-secondary-contrast)!important}.border-secondary-contrast{border-color:var(--sd-secondary-contrast)!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-info-contrast{color:var(--sd-info-contrast)!important}.bg-info-contrast{background:var(--sd-info-contrast)!important}.border-info-contrast{border-color:var(--sd-info-contrast)!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-success-contrast{color:var(--sd-success-contrast)!important}.bg-success-contrast{background:var(--sd-success-contrast)!important}.border-success-contrast{border-color:var(--sd-success-contrast)!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-warning-contrast{color:var(--sd-warning-contrast)!important}.bg-warning-contrast{background:var(--sd-warning-contrast)!important}.border-warning-contrast{border-color:var(--sd-warning-contrast)!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-error-contrast{color:var(--sd-error-contrast)!important}.bg-error-contrast{background:var(--sd-error-contrast)!important}.border-error-contrast{border-color:var(--sd-error-contrast)!important}.text-surface{color:var(--sd-surface)!important}.bg-surface{background:var(--sd-surface)!important}.border-surface{border-color:var(--sd-surface)!important}.text-surface-muted{color:var(--sd-surface-muted)!important}.bg-surface-muted{background:var(--sd-surface-muted)!important}.border-surface-muted{border-color:var(--sd-surface-muted)!important}.text-text{color:var(--sd-text)!important}.bg-text{background:var(--sd-text)!important}.border-text{border-color:var(--sd-text)!important}.text-text-secondary{color:var(--sd-text-secondary)!important}.bg-text-secondary{background:var(--sd-text-secondary)!important}.border-text-secondary{border-color:var(--sd-text-secondary)!important}.text-text-muted{color:var(--sd-text-muted)!important}.bg-text-muted{background:var(--sd-text-muted)!important}.border-text-muted{border-color:var(--sd-text-muted)!important}.text-border{color:var(--sd-border)!important}.bg-border{background:var(--sd-border)!important}.border-border{border-color:var(--sd-border)!important}.text-border-strong{color:var(--sd-border-strong)!important}.bg-border-strong{background:var(--sd-border-strong)!important}.border-border-strong{border-color:var(--sd-border-strong)!important}.text-disabled-bg{color:var(--sd-disabled-bg)!important}.bg-disabled-bg{background:var(--sd-disabled-bg)!important}.border-disabled-bg{border-color:var(--sd-disabled-bg)!important}.text-disabled-text{color:var(--sd-disabled-text)!important}.bg-disabled-text{background:var(--sd-disabled-text)!important}.border-disabled-text{border-color:var(--sd-disabled-text)!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}.text-secondary{color:var(--sd-text-secondary)!important}.text-muted{color:var(--sd-text-muted)!important}.text-body{color:var(--sd-text)!important}.bg-muted{background:var(--sd-surface-muted)!important}.modal-resizable{position:fixed;right:12px;top:80px;bottom:0;background-color:#fff;z-index:998;display:flex;flex-direction:column;opacity:0;visibility:hidden;transform:translate3d(0,100%,0);transition:all .3s ease-in-out;pointer-events:none;box-shadow:0 .4px .8px #0000001a,0 3px 6px #0003;border-radius:4px 4px 0 0}.modal-resizable button.c-action--white:focus{outline:none!important}.modal-resizable button.c-action--white:focus-visible{outline:2px solid #fff!important;outline-offset:2px}.modal-resizable.c-minium-of-list{top:auto;bottom:0;opacity:1;visibility:visible;pointer-events:all;transform:translateZ(0)}.modal-resizable.c-minium{top:auto!important;z-index:998!important}.modal-resizable.c-minium .modal-resizable__body,.modal-resizable.c-minium .modal-resizable__footer{display:none}.modal-resizable.c-fullscreen{top:0;z-index:999}.c-action{color:var(--sd-secondary)}.c-action:focus{outline:none}.c-action:focus-visible{outline:2px solid var(--sd-primary, #005cbb);outline-offset:2px}.c-action--white.c-min{margin-bottom:-12px}.c-action--white.c-max{margin-top:-12px}.modal-resizable__icon{border:0;background:transparent;color:#212121;overflow:hidden;outline:none}.modal-resizable__icon:focus-visible{outline:2px solid var(--sd-primary, #005cbb);outline-offset:2px}.modal-resizable__icon sd-icon{float:left}.modal-resizable__header{display:flex;align-items:center;flex-wrap:nowrap;gap:4px;min-height:40px;padding:0 16px;background-color:#5d6165;border-radius:4px 4px 0 0}.modal-resizable__body{flex:1;position:relative}.modal-resizable__content{position:absolute;padding:8px 16px 16px;inset:0;overflow:auto}.modal-resizable__footer{border-top:1px solid #dde0e5;padding:5px 16px;display:flex;align-items:center;justify-content:space-between;gap:8px}.modal-resizable__footer-left,.modal-resizable__footer-right{display:inline-flex;align-items:center;gap:8px}.modal-resizable__footer-right{margin-left:auto}.modal-resizable--active{opacity:1;visibility:visible;pointer-events:all;transform:translateZ(0)}.modal-resizable__title{flex:1 1 0;min-width:0;font-size:14px;line-height:20px;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.modal-resizable__title>*{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.modal-resizable__actions{display:flex;align-items:center;flex:0 0 auto;gap:4px;margin-left:auto}.modal-resizable__actions .c-action--white{flex:0 0 auto;width:32px;height:32px;line-height:32px}.modal-resizable__actions .c-action--white.c-min,.modal-resizable__actions .c-action--white.c-max{margin:0}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatDialogModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: PortalModule }, { kind: "directive", type: i2.CdkPortal, selector: "[cdkPortal]", exportAs: ["cdkPortal"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
146
302
|
}
|
|
147
303
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdModalResizable, decorators: [{
|
|
148
304
|
type: Component,
|
|
149
|
-
args: [{ selector: 'sd-modal-resizable', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [SdIcon, CommonModule, MatDialogModule, MatButtonModule, PortalModule], template: "<ng-template cdkPortal>\n <div\n [id]=\"id\"\n class=\"modal-resizable\"\n [style.width]=\"width()\"\n [class.modal-resizable--active]=\"isOpened()\"\n [class.modal-resizable--loading]=\"isLoading()\"\n [class.c-minium]=\"isMinimum()\"\n [class.c-fullscreen]=\"isMaximum()\"\n (mouseover)=\"onFocus()\"\n (focus)=\"onFocus()\"\n (mouseout)=\"onBlur()\"\n (blur)=\"onBlur()\">\n @if (isOpened()) {\n <div class=\"modal-resizable__header\">\n <div class=\"modal-resizable__title\">\n <ng-content select=\"[sdTitle]\"></ng-content>\n </div>\n <div class=\"modal-resizable__actions\">\n @if (!isMinimum()) {\n <button type=\"button\" class=\"c-action--white c-min\" (click)=\"minimize()\" mat-icon-button title=\"Thu nh\u1ECF\">\n <sd-icon name=\"remove\"></sd-icon>\n </button>\n }\n @if (isMinimum()) {\n <button type=\"button\" class=\"c-action--white c-max\" (click)=\"maximize()\" mat-icon-button title=\"Ph\u00F3ng to\">\n <sd-icon name=\"remove\"></sd-icon>\n </button>\n }\n @if (!isMaximum()) {\n <button type=\"button\" class=\"c-action--white\" (click)=\"toggleMaximum()\" mat-icon-button title=\"To\u00E0n m\u00E0n h\u00ECnh\">\n <sd-icon name=\"open_in_full\"></sd-icon>\n </button>\n }\n @if (isMaximum()) {\n <button type=\"button\" class=\"c-action--white\" (click)=\"toggleMaximum()\" mat-icon-button title=\"Tho\u00E1t to\u00E0n m\u00E0n h\u00ECnh\">\n <sd-icon name=\"fullscreen_exit\"></sd-icon>\n </button>\n }\n <button type=\"button\" class=\"c-action--white\" (click)=\"close()\" mat-icon-button title=\"\u0110\u00F3ng\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n </div>\n </div>\n <div class=\"modal-resizable__body\">\n <div class=\"modal-resizable__content\">\n <ng-content select=\"[sdBody]\"></ng-content>\n </div>\n </div>\n <div class=\"modal-resizable__footer\">\n <div class=\"modal-resizable__footer-left\"><ng-content select=\"[sdFooterLeft]\"></ng-content></div>\n <div class=\"modal-resizable__footer-right\"><ng-content select=\"[sdFooterRight]\"></ng-content></div>\n </div>\n }\n </div>\n</ng-template>\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-primary-contrast{color:var(--sd-primary-contrast)!important}.bg-primary-contrast{background:var(--sd-primary-contrast)!important}.border-primary-contrast{border-color:var(--sd-primary-contrast)!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-secondary-contrast{color:var(--sd-secondary-contrast)!important}.bg-secondary-contrast{background:var(--sd-secondary-contrast)!important}.border-secondary-contrast{border-color:var(--sd-secondary-contrast)!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-info-contrast{color:var(--sd-info-contrast)!important}.bg-info-contrast{background:var(--sd-info-contrast)!important}.border-info-contrast{border-color:var(--sd-info-contrast)!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-success-contrast{color:var(--sd-success-contrast)!important}.bg-success-contrast{background:var(--sd-success-contrast)!important}.border-success-contrast{border-color:var(--sd-success-contrast)!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-warning-contrast{color:var(--sd-warning-contrast)!important}.bg-warning-contrast{background:var(--sd-warning-contrast)!important}.border-warning-contrast{border-color:var(--sd-warning-contrast)!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-error-contrast{color:var(--sd-error-contrast)!important}.bg-error-contrast{background:var(--sd-error-contrast)!important}.border-error-contrast{border-color:var(--sd-error-contrast)!important}.text-surface{color:var(--sd-surface)!important}.bg-surface{background:var(--sd-surface)!important}.border-surface{border-color:var(--sd-surface)!important}.text-surface-muted{color:var(--sd-surface-muted)!important}.bg-surface-muted{background:var(--sd-surface-muted)!important}.border-surface-muted{border-color:var(--sd-surface-muted)!important}.text-text{color:var(--sd-text)!important}.bg-text{background:var(--sd-text)!important}.border-text{border-color:var(--sd-text)!important}.text-text-secondary{color:var(--sd-text-secondary)!important}.bg-text-secondary{background:var(--sd-text-secondary)!important}.border-text-secondary{border-color:var(--sd-text-secondary)!important}.text-text-muted{color:var(--sd-text-muted)!important}.bg-text-muted{background:var(--sd-text-muted)!important}.border-text-muted{border-color:var(--sd-text-muted)!important}.text-border{color:var(--sd-border)!important}.bg-border{background:var(--sd-border)!important}.border-border{border-color:var(--sd-border)!important}.text-border-strong{color:var(--sd-border-strong)!important}.bg-border-strong{background:var(--sd-border-strong)!important}.border-border-strong{border-color:var(--sd-border-strong)!important}.text-disabled-bg{color:var(--sd-disabled-bg)!important}.bg-disabled-bg{background:var(--sd-disabled-bg)!important}.border-disabled-bg{border-color:var(--sd-disabled-bg)!important}.text-disabled-text{color:var(--sd-disabled-text)!important}.bg-disabled-text{background:var(--sd-disabled-text)!important}.border-disabled-text{border-color:var(--sd-disabled-text)!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}.text-secondary{color:var(--sd-text-secondary)!important}.text-muted{color:var(--sd-text-muted)!important}.text-body{color:var(--sd-text)!important}.bg-muted{background:var(--sd-surface-muted)!important}.modal-resizable{position:fixed;right:12px;top:80px;bottom:0;background-color:#fff;z-index:998;display:flex;flex-direction:column;opacity:0;visibility:hidden;transform:translate3d(0,100%,0);transition:all .3s ease-in-out;pointer-events:none;box-shadow:0 .4px .8px #0000001a,0 3px 6px #0003;border-radius:4px 4px 0 0}.modal-resizable button.c-action--white:focus{outline:none!important}.modal-resizable.c-minium-of-list{top:auto;bottom:0;opacity:1;visibility:visible;pointer-events:all;transform:translateZ(0)}.modal-resizable.c-minium{top:auto!important;z-index:998!important}.modal-resizable.c-minium .modal-resizable__body,.modal-resizable.c-minium .modal-resizable__footer{display:none}.modal-resizable.c-fullscreen{top:0;z-index:999}.c-action{color:var(--sd-secondary)}.c-action:focus{outline:none}.c-action--white.c-min{margin-bottom:-12px}.c-action--white.c-max{margin-top:-12px}.modal-resizable__icon{border:0;background:transparent;color:#212121;overflow:hidden;outline:none}.modal-resizable__icon sd-icon{float:left}.modal-resizable__header{display:flex;align-items:center;flex-wrap:nowrap;gap:4px;min-height:40px;padding:0 16px;background-color:#5d6165;border-radius:4px 4px 0 0}.modal-resizable__body{flex:1;position:relative}.modal-resizable__content{position:absolute;padding:8px 16px 16px;inset:0;overflow:auto}.modal-resizable__footer{border-top:1px solid #dde0e5;padding:5px 16px;display:flex;align-items:center;justify-content:space-between;gap:8px}.modal-resizable__footer-left,.modal-resizable__footer-right{display:inline-flex;align-items:center;gap:8px}.modal-resizable__footer-right{margin-left:auto}.modal-resizable--active{opacity:1;visibility:visible;pointer-events:all;transform:translateZ(0)}.modal-resizable__title{flex:1 1 0;min-width:0;font-size:14px;line-height:20px;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.modal-resizable__title>*{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.modal-resizable__actions{display:flex;align-items:center;flex:0 0 auto;gap:4px;margin-left:auto}.modal-resizable__actions .c-action--white{flex:0 0 auto;width:32px;height:32px;line-height:32px}.modal-resizable__actions .c-action--white.c-min,.modal-resizable__actions .c-action--white.c-max{margin:0}\n"] }]
|
|
305
|
+
args: [{ selector: 'sd-modal-resizable', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [SdIcon, CommonModule, MatDialogModule, MatButtonModule, PortalModule], template: "<ng-template cdkPortal>\n <div\n [id]=\"id\"\n class=\"modal-resizable\"\n [style.width]=\"width()\"\n [class.modal-resizable--active]=\"isOpened()\"\n [class.modal-resizable--loading]=\"isLoading()\"\n [class.c-minium]=\"isMinimum()\"\n [class.c-fullscreen]=\"isMaximum()\"\n (mouseover)=\"onFocus()\"\n (focus)=\"onFocus()\"\n (mouseout)=\"onBlur()\"\n (blur)=\"onBlur()\">\n @if (isOpened()) {\n <div class=\"modal-resizable__header\">\n <div class=\"modal-resizable__title\">\n <ng-content select=\"[sdTitle]\"></ng-content>\n </div>\n <div class=\"modal-resizable__actions\">\n @if (!isMinimum()) {\n <button type=\"button\" class=\"c-action--white c-min\" (click)=\"minimize()\" mat-icon-button title=\"Thu nh\u1ECF\">\n <sd-icon name=\"remove\"></sd-icon>\n </button>\n }\n @if (isMinimum()) {\n <button type=\"button\" class=\"c-action--white c-max\" (click)=\"maximize()\" mat-icon-button title=\"Ph\u00F3ng to\">\n <sd-icon name=\"remove\"></sd-icon>\n </button>\n }\n @if (!isMaximum()) {\n <button type=\"button\" class=\"c-action--white\" (click)=\"toggleMaximum()\" mat-icon-button title=\"To\u00E0n m\u00E0n h\u00ECnh\">\n <sd-icon name=\"open_in_full\"></sd-icon>\n </button>\n }\n @if (isMaximum()) {\n <button type=\"button\" class=\"c-action--white\" (click)=\"toggleMaximum()\" mat-icon-button title=\"Tho\u00E1t to\u00E0n m\u00E0n h\u00ECnh\">\n <sd-icon name=\"fullscreen_exit\"></sd-icon>\n </button>\n }\n <button type=\"button\" class=\"c-action--white\" (click)=\"close()\" mat-icon-button title=\"\u0110\u00F3ng\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n </div>\n </div>\n <div class=\"modal-resizable__body\">\n <div class=\"modal-resizable__content\">\n <ng-content select=\"[sdBody]\"></ng-content>\n </div>\n </div>\n <div class=\"modal-resizable__footer\">\n <div class=\"modal-resizable__footer-left\"><ng-content select=\"[sdFooterLeft]\"></ng-content></div>\n <div class=\"modal-resizable__footer-right\"><ng-content select=\"[sdFooterRight]\"></ng-content></div>\n </div>\n }\n </div>\n</ng-template>\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-primary-contrast{color:var(--sd-primary-contrast)!important}.bg-primary-contrast{background:var(--sd-primary-contrast)!important}.border-primary-contrast{border-color:var(--sd-primary-contrast)!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-secondary-contrast{color:var(--sd-secondary-contrast)!important}.bg-secondary-contrast{background:var(--sd-secondary-contrast)!important}.border-secondary-contrast{border-color:var(--sd-secondary-contrast)!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-info-contrast{color:var(--sd-info-contrast)!important}.bg-info-contrast{background:var(--sd-info-contrast)!important}.border-info-contrast{border-color:var(--sd-info-contrast)!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-success-contrast{color:var(--sd-success-contrast)!important}.bg-success-contrast{background:var(--sd-success-contrast)!important}.border-success-contrast{border-color:var(--sd-success-contrast)!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-warning-contrast{color:var(--sd-warning-contrast)!important}.bg-warning-contrast{background:var(--sd-warning-contrast)!important}.border-warning-contrast{border-color:var(--sd-warning-contrast)!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-error-contrast{color:var(--sd-error-contrast)!important}.bg-error-contrast{background:var(--sd-error-contrast)!important}.border-error-contrast{border-color:var(--sd-error-contrast)!important}.text-surface{color:var(--sd-surface)!important}.bg-surface{background:var(--sd-surface)!important}.border-surface{border-color:var(--sd-surface)!important}.text-surface-muted{color:var(--sd-surface-muted)!important}.bg-surface-muted{background:var(--sd-surface-muted)!important}.border-surface-muted{border-color:var(--sd-surface-muted)!important}.text-text{color:var(--sd-text)!important}.bg-text{background:var(--sd-text)!important}.border-text{border-color:var(--sd-text)!important}.text-text-secondary{color:var(--sd-text-secondary)!important}.bg-text-secondary{background:var(--sd-text-secondary)!important}.border-text-secondary{border-color:var(--sd-text-secondary)!important}.text-text-muted{color:var(--sd-text-muted)!important}.bg-text-muted{background:var(--sd-text-muted)!important}.border-text-muted{border-color:var(--sd-text-muted)!important}.text-border{color:var(--sd-border)!important}.bg-border{background:var(--sd-border)!important}.border-border{border-color:var(--sd-border)!important}.text-border-strong{color:var(--sd-border-strong)!important}.bg-border-strong{background:var(--sd-border-strong)!important}.border-border-strong{border-color:var(--sd-border-strong)!important}.text-disabled-bg{color:var(--sd-disabled-bg)!important}.bg-disabled-bg{background:var(--sd-disabled-bg)!important}.border-disabled-bg{border-color:var(--sd-disabled-bg)!important}.text-disabled-text{color:var(--sd-disabled-text)!important}.bg-disabled-text{background:var(--sd-disabled-text)!important}.border-disabled-text{border-color:var(--sd-disabled-text)!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}.text-secondary{color:var(--sd-text-secondary)!important}.text-muted{color:var(--sd-text-muted)!important}.text-body{color:var(--sd-text)!important}.bg-muted{background:var(--sd-surface-muted)!important}.modal-resizable{position:fixed;right:12px;top:80px;bottom:0;background-color:#fff;z-index:998;display:flex;flex-direction:column;opacity:0;visibility:hidden;transform:translate3d(0,100%,0);transition:all .3s ease-in-out;pointer-events:none;box-shadow:0 .4px .8px #0000001a,0 3px 6px #0003;border-radius:4px 4px 0 0}.modal-resizable button.c-action--white:focus{outline:none!important}.modal-resizable button.c-action--white:focus-visible{outline:2px solid #fff!important;outline-offset:2px}.modal-resizable.c-minium-of-list{top:auto;bottom:0;opacity:1;visibility:visible;pointer-events:all;transform:translateZ(0)}.modal-resizable.c-minium{top:auto!important;z-index:998!important}.modal-resizable.c-minium .modal-resizable__body,.modal-resizable.c-minium .modal-resizable__footer{display:none}.modal-resizable.c-fullscreen{top:0;z-index:999}.c-action{color:var(--sd-secondary)}.c-action:focus{outline:none}.c-action:focus-visible{outline:2px solid var(--sd-primary, #005cbb);outline-offset:2px}.c-action--white.c-min{margin-bottom:-12px}.c-action--white.c-max{margin-top:-12px}.modal-resizable__icon{border:0;background:transparent;color:#212121;overflow:hidden;outline:none}.modal-resizable__icon:focus-visible{outline:2px solid var(--sd-primary, #005cbb);outline-offset:2px}.modal-resizable__icon sd-icon{float:left}.modal-resizable__header{display:flex;align-items:center;flex-wrap:nowrap;gap:4px;min-height:40px;padding:0 16px;background-color:#5d6165;border-radius:4px 4px 0 0}.modal-resizable__body{flex:1;position:relative}.modal-resizable__content{position:absolute;padding:8px 16px 16px;inset:0;overflow:auto}.modal-resizable__footer{border-top:1px solid #dde0e5;padding:5px 16px;display:flex;align-items:center;justify-content:space-between;gap:8px}.modal-resizable__footer-left,.modal-resizable__footer-right{display:inline-flex;align-items:center;gap:8px}.modal-resizable__footer-right{margin-left:auto}.modal-resizable--active{opacity:1;visibility:visible;pointer-events:all;transform:translateZ(0)}.modal-resizable__title{flex:1 1 0;min-width:0;font-size:14px;line-height:20px;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.modal-resizable__title>*{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.modal-resizable__actions{display:flex;align-items:center;flex:0 0 auto;gap:4px;margin-left:auto}.modal-resizable__actions .c-action--white{flex:0 0 auto;width:32px;height:32px;line-height:32px}.modal-resizable__actions .c-action--white.c-min,.modal-resizable__actions .c-action--white.c-max{margin:0}\n"] }]
|
|
150
306
|
}], ctorParameters: () => [], propDecorators: { portal: [{ type: i0.ViewChild, args: [i0.forwardRef(() => CdkPortal), { isSignal: true }] }], editable: [{ type: i0.Input, args: [{ isSignal: true, alias: "editable", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], editingChanged: [{ type: i0.Output, args: ["editingChanged"] }] } });
|
|
151
307
|
|
|
152
308
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sdcorejs-angular-components-modal-resizable.mjs","sources":["../../../projects/sdcorejs-angular/components/modal-resizable/src/modal-resizable.component.ts","../../../projects/sdcorejs-angular/components/modal-resizable/src/modal-resizable.component.html","../../../projects/sdcorejs-angular/components/modal-resizable/sdcorejs-angular-components-modal-resizable.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n DestroyRef,\n EmbeddedViewRef,\n afterNextRender,\n booleanAttribute,\n inject,\n input,\n output,\n signal,\n viewChild,\n} from '@angular/core';\nimport { Utilities } from '@sdcorejs/utils/fns';\nimport { CdkPortal, DomPortalOutlet, PortalModule } from '@angular/cdk/portal';\nimport { CommonModule } from '@angular/common';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatDialogModule } from '@angular/material/dialog';\nimport { SdLoadingService } from '@sdcorejs/angular/services';\nimport { SdIcon } from '@sdcorejs/angular/modules/icon';\n\n@Component({\n selector: 'sd-modal-resizable',\n templateUrl: './modal-resizable.component.html',\n styleUrl: './modal-resizable.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [SdIcon, CommonModule, MatDialogModule, MatButtonModule, PortalModule],\n})\nexport class SdModalResizable {\n readonly id = `I${Utilities.generateUuid()}`;\n readonly portal = viewChild.required(CdkPortal);\n readonly editable = input<boolean, boolean | ''>(false, { transform: booleanAttribute });\n readonly width = input<string>('480px');\n readonly editingChanged = output<boolean>();\n #embeddedViewRef!: EmbeddedViewRef<any>;\n readonly #ref = inject(ChangeDetectorRef);\n readonly #loadingService = inject(SdLoadingService);\n readonly #destroyRef = inject(DestroyRef);\n readonly isEditing = signal(false);\n readonly isOpened = signal(false);\n readonly isHover = signal(false);\n readonly isLoading = signal(false);\n readonly isMinimum = signal(false);\n readonly isMaximum = signal(false);\n\n constructor() {\n afterNextRender(() => {\n // why: attachTemplatePortal tạo embedded view từ VCR của chính CdkPortal →\n // KHÔNG cần appRef/injector/document. Chỉ truyền outletElement để tương thích\n // mọi CDK (19 còn slot ComponentFactoryResolver, 20/21 đã bỏ → tránh lệch arg).\n const outlet = new DomPortalOutlet(document.body);\n this.#embeddedViewRef = outlet.attachTemplatePortal(this.portal());\n this.#detectChanges();\n });\n\n this.#destroyRef.onDestroy(() => {\n this.#embeddedViewRef?.destroy();\n });\n }\n\n open = () => {\n this.isOpened.set(true);\n this.#detectChanges();\n\n const element = this.#getElement();\n if (element && !element.classList.contains('c-minium')) {\n setTimeout(() => {\n const maximumWidth = element.offsetWidth;\n if (maximumWidth > 0) {\n element.dataset['width'] = maximumWidth.toString();\n }\n element.classList.remove('c-closed');\n }, 100);\n }\n\n this.maximize();\n };\n\n close = () => {\n this.isOpened.set(false);\n this.#detectChanges();\n setTimeout(() => {\n const element = this.#getElement();\n element?.style.setProperty('width', '0px');\n element?.style.setProperty('right', '0px');\n element?.classList.remove('c-minium');\n element?.classList.add('c-closed');\n }, 100);\n this.stopLoading();\n\n this.#arrangePanels();\n };\n\n minimize = () => {\n this.isMinimum.set(true);\n this.#detectChanges();\n this.#arrangePanels();\n };\n\n maximize = () => {\n this.isMinimum.set(false);\n this.#detectChanges();\n this.#arrangePanels();\n };\n\n toggleMaximum = () => {\n this.isMaximum.update(isMaximum => !isMaximum);\n this.#detectChanges();\n this.#arrangePanels();\n };\n\n startLoading = () => {\n this.isLoading.set(true);\n this.#detectChanges();\n this.#loadingService.stop(`#${this.id}`);\n this.#loadingService.start(`#${this.id}`);\n };\n\n stopLoading = () => {\n this.isLoading.set(false);\n this.#detectChanges();\n this.#loadingService.stop(`#${this.id}`);\n };\n\n toggleEditable() {\n this.isEditing.update(isEditing => !isEditing);\n this.#detectChanges();\n this.editingChanged.emit(this.isEditing());\n }\n\n onFocus = () => {\n this.isHover.set(true);\n };\n\n onBlur = () => {\n this.isHover.set(false);\n };\n\n #arrangePanels = () => {\n setTimeout(() => {\n let totalRight = 0;\n const minimumWidth = 300;\n document.querySelectorAll<HTMLElement>('.modal-resizable').forEach(item => {\n if (item.classList.contains('c-minium')) {\n item.style.setProperty('width', `${minimumWidth}px`);\n item.style.setProperty('right', `${totalRight}px`);\n totalRight += minimumWidth + 8;\n return;\n }\n\n if (item.classList.contains('c-closed')) {\n return;\n }\n\n const expandedWidth = Number.parseInt(item.dataset['width'] || '', 10);\n if (Number.isNaN(expandedWidth)) {\n return;\n }\n\n if (item.classList.contains('c-fullscreen')) {\n item.style.setProperty('width', 'calc(100% - 16px)');\n item.style.setProperty('right', '8px');\n } else {\n item.style.setProperty('width', `${expandedWidth}px`);\n item.style.setProperty('right', `${totalRight}px`);\n }\n totalRight += expandedWidth + 8;\n });\n }, 200);\n };\n\n #getElement() {\n return document.getElementById(this.id);\n }\n\n #detectChanges() {\n this.#ref.markForCheck();\n this.#embeddedViewRef?.detectChanges();\n }\n}\n","<ng-template cdkPortal>\n <div\n [id]=\"id\"\n class=\"modal-resizable\"\n [style.width]=\"width()\"\n [class.modal-resizable--active]=\"isOpened()\"\n [class.modal-resizable--loading]=\"isLoading()\"\n [class.c-minium]=\"isMinimum()\"\n [class.c-fullscreen]=\"isMaximum()\"\n (mouseover)=\"onFocus()\"\n (focus)=\"onFocus()\"\n (mouseout)=\"onBlur()\"\n (blur)=\"onBlur()\">\n @if (isOpened()) {\n <div class=\"modal-resizable__header\">\n <div class=\"modal-resizable__title\">\n <ng-content select=\"[sdTitle]\"></ng-content>\n </div>\n <div class=\"modal-resizable__actions\">\n @if (!isMinimum()) {\n <button type=\"button\" class=\"c-action--white c-min\" (click)=\"minimize()\" mat-icon-button title=\"Thu nhỏ\">\n <sd-icon name=\"remove\"></sd-icon>\n </button>\n }\n @if (isMinimum()) {\n <button type=\"button\" class=\"c-action--white c-max\" (click)=\"maximize()\" mat-icon-button title=\"Phóng to\">\n <sd-icon name=\"remove\"></sd-icon>\n </button>\n }\n @if (!isMaximum()) {\n <button type=\"button\" class=\"c-action--white\" (click)=\"toggleMaximum()\" mat-icon-button title=\"Toàn màn hình\">\n <sd-icon name=\"open_in_full\"></sd-icon>\n </button>\n }\n @if (isMaximum()) {\n <button type=\"button\" class=\"c-action--white\" (click)=\"toggleMaximum()\" mat-icon-button title=\"Thoát toàn màn hình\">\n <sd-icon name=\"fullscreen_exit\"></sd-icon>\n </button>\n }\n <button type=\"button\" class=\"c-action--white\" (click)=\"close()\" mat-icon-button title=\"Đóng\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n </div>\n </div>\n <div class=\"modal-resizable__body\">\n <div class=\"modal-resizable__content\">\n <ng-content select=\"[sdBody]\"></ng-content>\n </div>\n </div>\n <div class=\"modal-resizable__footer\">\n <div class=\"modal-resizable__footer-left\"><ng-content select=\"[sdFooterLeft]\"></ng-content></div>\n <div class=\"modal-resizable__footer-right\"><ng-content select=\"[sdFooterRight]\"></ng-content></div>\n </div>\n }\n </div>\n</ng-template>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;;;;;;;MA8Ba,gBAAgB,CAAA;AAClB,IAAA,EAAE,GAAG,CAAA,CAAA,EAAI,SAAS,CAAC,YAAY,EAAE,EAAE;AACnC,IAAA,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC;IACtC,QAAQ,GAAG,KAAK,CAAwB,KAAK,gFAAI,SAAS,EAAE,gBAAgB,EAAA,CAAG;AAC/E,IAAA,KAAK,GAAG,KAAK,CAAS,OAAO,4EAAC;IAC9B,cAAc,GAAG,MAAM,EAAW;AAC3C,IAAA,gBAAgB;AACP,IAAA,IAAI,GAAG,MAAM,CAAC,iBAAiB,CAAC;AAChC,IAAA,eAAe,GAAG,MAAM,CAAC,gBAAgB,CAAC;AAC1C,IAAA,WAAW,GAAG,MAAM,CAAC,UAAU,CAAC;AAChC,IAAA,SAAS,GAAG,MAAM,CAAC,KAAK,gFAAC;AACzB,IAAA,QAAQ,GAAG,MAAM,CAAC,KAAK,+EAAC;AACxB,IAAA,OAAO,GAAG,MAAM,CAAC,KAAK,8EAAC;AACvB,IAAA,SAAS,GAAG,MAAM,CAAC,KAAK,gFAAC;AACzB,IAAA,SAAS,GAAG,MAAM,CAAC,KAAK,gFAAC;AACzB,IAAA,SAAS,GAAG,MAAM,CAAC,KAAK,gFAAC;AAElC,IAAA,WAAA,GAAA;QACE,eAAe,CAAC,MAAK;;;;YAInB,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC;AACjD,YAAA,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAC,oBAAoB,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YAClE,IAAI,CAAC,cAAc,EAAE;AACvB,QAAA,CAAC,CAAC;AAEF,QAAA,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,MAAK;AAC9B,YAAA,IAAI,CAAC,gBAAgB,EAAE,OAAO,EAAE;AAClC,QAAA,CAAC,CAAC;IACJ;IAEA,IAAI,GAAG,MAAK;AACV,QAAA,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;QACvB,IAAI,CAAC,cAAc,EAAE;AAErB,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE;AAClC,QAAA,IAAI,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE;YACtD,UAAU,CAAC,MAAK;AACd,gBAAA,MAAM,YAAY,GAAG,OAAO,CAAC,WAAW;AACxC,gBAAA,IAAI,YAAY,GAAG,CAAC,EAAE;oBACpB,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,YAAY,CAAC,QAAQ,EAAE;gBACpD;AACA,gBAAA,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC;YACtC,CAAC,EAAE,GAAG,CAAC;QACT;QAEA,IAAI,CAAC,QAAQ,EAAE;AACjB,IAAA,CAAC;IAED,KAAK,GAAG,MAAK;AACX,QAAA,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC;QACxB,IAAI,CAAC,cAAc,EAAE;QACrB,UAAU,CAAC,MAAK;AACd,YAAA,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE;YAClC,OAAO,EAAE,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC;YAC1C,OAAO,EAAE,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC;AAC1C,YAAA,OAAO,EAAE,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC;AACrC,YAAA,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC;QACpC,CAAC,EAAE,GAAG,CAAC;QACP,IAAI,CAAC,WAAW,EAAE;QAElB,IAAI,CAAC,cAAc,EAAE;AACvB,IAAA,CAAC;IAED,QAAQ,GAAG,MAAK;AACd,QAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC;QACxB,IAAI,CAAC,cAAc,EAAE;QACrB,IAAI,CAAC,cAAc,EAAE;AACvB,IAAA,CAAC;IAED,QAAQ,GAAG,MAAK;AACd,QAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC;QACzB,IAAI,CAAC,cAAc,EAAE;QACrB,IAAI,CAAC,cAAc,EAAE;AACvB,IAAA,CAAC;IAED,aAAa,GAAG,MAAK;AACnB,QAAA,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC;QAC9C,IAAI,CAAC,cAAc,EAAE;QACrB,IAAI,CAAC,cAAc,EAAE;AACvB,IAAA,CAAC;IAED,YAAY,GAAG,MAAK;AAClB,QAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC;QACxB,IAAI,CAAC,cAAc,EAAE;QACrB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAA,CAAA,EAAI,IAAI,CAAC,EAAE,CAAA,CAAE,CAAC;QACxC,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,CAAA,CAAA,EAAI,IAAI,CAAC,EAAE,CAAA,CAAE,CAAC;AAC3C,IAAA,CAAC;IAED,WAAW,GAAG,MAAK;AACjB,QAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC;QACzB,IAAI,CAAC,cAAc,EAAE;QACrB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAA,CAAA,EAAI,IAAI,CAAC,EAAE,CAAA,CAAE,CAAC;AAC1C,IAAA,CAAC;IAED,cAAc,GAAA;AACZ,QAAA,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC;QAC9C,IAAI,CAAC,cAAc,EAAE;QACrB,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;IAC5C;IAEA,OAAO,GAAG,MAAK;AACb,QAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;AACxB,IAAA,CAAC;IAED,MAAM,GAAG,MAAK;AACZ,QAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;AACzB,IAAA,CAAC;IAED,cAAc,GAAG,MAAK;QACpB,UAAU,CAAC,MAAK;YACd,IAAI,UAAU,GAAG,CAAC;YAClB,MAAM,YAAY,GAAG,GAAG;YACxB,QAAQ,CAAC,gBAAgB,CAAc,kBAAkB,CAAC,CAAC,OAAO,CAAC,IAAI,IAAG;gBACxE,IAAI,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE;oBACvC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,CAAA,EAAG,YAAY,CAAA,EAAA,CAAI,CAAC;oBACpD,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,CAAA,EAAG,UAAU,CAAA,EAAA,CAAI,CAAC;AAClD,oBAAA,UAAU,IAAI,YAAY,GAAG,CAAC;oBAC9B;gBACF;gBAEA,IAAI,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE;oBACvC;gBACF;AAEA,gBAAA,MAAM,aAAa,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC;AACtE,gBAAA,IAAI,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE;oBAC/B;gBACF;gBAEA,IAAI,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE;oBAC3C,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,mBAAmB,CAAC;oBACpD,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC;gBACxC;qBAAO;oBACL,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,CAAA,EAAG,aAAa,CAAA,EAAA,CAAI,CAAC;oBACrD,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,CAAA,EAAG,UAAU,CAAA,EAAA,CAAI,CAAC;gBACpD;AACA,gBAAA,UAAU,IAAI,aAAa,GAAG,CAAC;AACjC,YAAA,CAAC,CAAC;QACJ,CAAC,EAAE,GAAG,CAAC;AACT,IAAA,CAAC;IAED,WAAW,GAAA;QACT,OAAO,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;IACzC;IAEA,cAAc,GAAA;AACZ,QAAA,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE;AACxB,QAAA,IAAI,CAAC,gBAAgB,EAAE,aAAa,EAAE;IACxC;wGAtJW,gBAAgB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAhB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,gBAAgB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,oBAAA,EAAA,MAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,cAAA,EAAA,gBAAA,EAAA,EAAA,WAAA,EAAA,CAAA,EAAA,YAAA,EAAA,QAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAEU,SAAS,EAAA,WAAA,EAAA,IAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EChChD,q4EAwDA,EAAA,MAAA,EAAA,CAAA,08QAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,ED5BY,MAAM,EAAA,QAAA,EAAA,SAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,UAAA,EAAA,OAAA,EAAA,KAAA,EAAA,SAAA,EAAA,MAAA,EAAA,aAAA,EAAA,qBAAA,EAAA,WAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAE,YAAY,EAAA,EAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAE,eAAe,EAAA,EAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAE,eAAe,qNAAE,YAAY,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,SAAA,EAAA,QAAA,EAAA,aAAA,EAAA,QAAA,EAAA,CAAA,WAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAEnE,gBAAgB,EAAA,UAAA,EAAA,CAAA;kBAR5B,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,oBAAoB,mBAGb,uBAAuB,CAAC,MAAM,EAAA,UAAA,EACnC,IAAI,EAAA,OAAA,EACP,CAAC,MAAM,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,YAAY,CAAC,EAAA,QAAA,EAAA,q4EAAA,EAAA,MAAA,EAAA,CAAA,08QAAA,CAAA,EAAA;kHAI1C,SAAS,CAAA,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,QAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,UAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,KAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,OAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,cAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,MAAA,EAAA,IAAA,EAAA,CAAA,gBAAA,CAAA,EAAA,CAAA,EAAA,EAAA,CAAA;;AEhChD;;AAEG;;;;"}
|
|
1
|
+
{"version":3,"file":"sdcorejs-angular-components-modal-resizable.mjs","sources":["../../../projects/sdcorejs-angular/components/modal-resizable/src/modal-resizable.registry.ts","../../../projects/sdcorejs-angular/components/modal-resizable/src/modal-resizable.component.ts","../../../projects/sdcorejs-angular/components/modal-resizable/src/modal-resizable.component.html","../../../projects/sdcorejs-angular/components/modal-resizable/sdcorejs-angular-components-modal-resizable.ts"],"sourcesContent":["import { DOCUMENT } from '@angular/common';\nimport { Injectable, inject } from '@angular/core';\n\n/**\n * Sổ đăng ký các panel `<sd-modal-resizable>` đang sống.\n *\n * why: mỗi panel được portal ra thẳng `document.body` nên việc xếp chỗ (width/right) là bài toán\n * chung của cả stack. Trước đây `#arrangePanels` quét `document.querySelectorAll('.modal-resizable')`\n * — query đó bắt MỌI phần tử mang class này trong tài liệu, kể cả markup của component khác hoặc\n * của app dùng trùng tên class, rồi ghi đè inline style lên chúng. Registry giới hạn phạm vi ghi\n * đúng vào các instance do library tạo ra và còn sống.\n */\n@Injectable({ providedIn: 'root' })\nexport class SdModalResizableRegistry {\n // why: dùng token DOCUMENT thay vì global `document`, đồng bộ với `preview-image` — service\n // `providedIn:'root'` có thể được khởi tạo trong quá trình render phía server.\n readonly #document = inject(DOCUMENT);\n\n // Set giữ nguyên thứ tự đăng ký → thứ tự xếp chỗ ổn định, khớp thứ tự panel được mở.\n readonly #ids = new Set<string>();\n\n register(id: string): void {\n this.#ids.add(id);\n }\n\n unregister(id: string): void {\n this.#ids.delete(id);\n }\n\n /**\n * Phần tử panel của các instance đã đăng ký và đang có mặt trong DOM.\n * Instance đã destroy hoặc chưa attach portal sẽ bị bỏ qua.\n */\n panels(): HTMLElement[] {\n const elements: HTMLElement[] = [];\n for (const id of this.#ids) {\n const element = this.#document.getElementById(id);\n if (element) elements.push(element);\n }\n return elements;\n }\n}\n","import {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n DestroyRef,\n EmbeddedViewRef,\n afterNextRender,\n booleanAttribute,\n inject,\n input,\n output,\n signal,\n viewChild,\n} from '@angular/core';\nimport { Utilities } from '@sdcorejs/utils/fns';\nimport { CdkPortal, DomPortalOutlet, PortalModule } from '@angular/cdk/portal';\nimport { CommonModule } from '@angular/common';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatDialogModule } from '@angular/material/dialog';\nimport { SdLoadingService } from '@sdcorejs/angular/services';\nimport { SdIcon } from '@sdcorejs/angular/modules/icon';\nimport { SdModalResizableRegistry } from './modal-resizable.registry';\n\n/**\n * `<sd-modal-resizable>` — right-docked, non-modal panel that can be minimized, expanded or pushed\n * fullscreen. Several panels stack side by side along the right edge of the viewport, so the user\n * can keep multiple records open at once while still working with the page behind them.\n *\n * Use it for side-by-side comparison / inspector workflows. Use `<sd-modal>` when the task is\n * blocking, and `<sd-side-drawer>` when a single overlay with a backdrop is enough.\n *\n * NOTE ON DOCS: this component deliberately has **no** sibling `sd-modal-resizable.md`. Every `*.md`\n * under the library is swept into `published-docs/<version>/index.json` by `scripts/collect-docs.mjs`,\n * and `scripts/generate-showcase-example-sources.test.mjs` asserts that list is 1:1 with the\n * `publishedDocId` set in the showcase documentation registry. A published doc with no showcase page\n * therefore turns that release guard red. Keep the reference here until the component ships a\n * showcase demo + registry entry; only then add the `.md`.\n *\n * ```ts\n * import { SdModalResizable } from '@sdcorejs/angular/components/modal-resizable';\n * ```\n *\n * ```html\n * <sd-button title=\"Open detail\" type=\"fill\" color=\"primary\" (click)=\"panel.open()\"></sd-button>\n *\n * <sd-modal-resizable #panel width=\"520px\">\n * <span sdTitle>Đơn hàng #1024</span>\n *\n * <div sdBody class=\"panel-body\">\n * <sd-section icon=\"receipt\" title=\"Thông tin chung\">\n * <sd-section-item label=\"Khách hàng\">Nguyen Van An</sd-section-item>\n * </sd-section>\n * </div>\n *\n * <sd-button sdFooterRight type=\"text\" title=\"Đóng\" (click)=\"panel.close()\"></sd-button>\n * </sd-modal-resizable>\n * ```\n *\n * ### Slots\n * | Selector | Where it renders |\n * | ----------------- | ------------------------- |\n * | `[sdTitle]` | Header title area. |\n * | `[sdBody]` | Scrollable content area. |\n * | `[sdFooterLeft]` | Footer left action group. |\n * | `[sdFooterRight]` | Footer right action group.|\n *\n * Header, body and footer only render while the panel is open.\n *\n * ### Panel stacking\n * The panel is portalled onto `document.body` (via `CdkPortal` + `DomPortalOutlet`), so laying panels\n * out is a concern shared by all live instances rather than by a single view. Arrangement is scoped\n * through the root-provided `SdModalResizableRegistry`: each instance registers its panel id on\n * construction and unregisters it on destroy. `open`, `close`, `minimize`, `maximize` and\n * `toggleMaximum` re-run the arrangement and rewrite inline `width` / `right` **only on registered,\n * still-mounted panels**, in registration order. Consequences worth knowing:\n * - Markup of your own that happens to carry the `.modal-resizable` class is **not** touched.\n * (Earlier builds ran `document.querySelectorAll('.modal-resizable')` and rewrote every match.)\n * - A destroyed panel immediately stops taking up a slot for the instances that outlive it.\n * - Panels marked `c-closed`, and expanded panels with no captured `data-width`, are skipped.\n *\n * ### Lifecycle / teardown\n * `open()`, `close()` and the arrangement pass each schedule a short `setTimeout` (100ms / 100ms /\n * 200ms) that mutates DOM. All of them are tracked and cleared in `DestroyRef.onDestroy`, together\n * with unregistering from the registry and destroying the portal view. Destroying the component\n * mid-animation cannot run layout work against a detached panel.\n *\n * ### Anti-patterns\n * - Rendering it inside a scroll container and expecting it to be clipped — it lives on `<body>`.\n * - Reusing the `.modal-resizable` class on your own elements to \"join\" the stack; register a real\n * component instead.\n * - Writing inline `width` / `right` on a panel yourself — the next arrangement pass overwrites both.\n *\n * ### Tests\n * `src/modal-resizable.component.spec.ts` — portal attach, open/close/minimize/maximize math,\n * loading, editing, timer teardown, registry scoping. Run:\n * `npx ng test sdcorejs-angular --watch=false --browsers=ChromeHeadless --include='projects/sdcorejs-angular/components/modal-resizable/src/modal-resizable.component.spec.ts'`\n *\n * ### Related\n * `<sd-modal>` (blocking dialog / bottom-sheet), `<sd-side-drawer>` (single right-side overlay with\n * a body scroll lock), `SdLoadingService` (powers `startLoading()` / `stopLoading()`).\n */\n@Component({\n selector: 'sd-modal-resizable',\n templateUrl: './modal-resizable.component.html',\n styleUrl: './modal-resizable.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [SdIcon, CommonModule, MatDialogModule, MatButtonModule, PortalModule],\n})\nexport class SdModalResizable {\n /** Generated DOM id of the panel element (`I<uuid>`). */\n readonly id = `I${Utilities.generateUuid()}`;\n readonly portal = viewChild.required(CdkPortal);\n /** Marks the panel as editable. Bare attribute = `true`. Default `false`. */\n readonly editable = input<boolean, boolean | ''>(false, { transform: booleanAttribute });\n /** CSS width used while the panel is expanded (not a minimum). Default `'480px'`. */\n readonly width = input<string>('480px');\n /** Emitted by `toggleEditable()` with the new editing state. */\n readonly editingChanged = output<boolean>();\n #embeddedViewRef!: EmbeddedViewRef<any>;\n readonly #ref = inject(ChangeDetectorRef);\n readonly #loadingService = inject(SdLoadingService);\n readonly #destroyRef = inject(DestroyRef);\n readonly #registry = inject(SdModalResizableRegistry);\n // why: open/close/#arrangePanels đều hẹn setTimeout thao tác DOM. Gom id vào một set để\n // onDestroy clear sạch, tránh callback chạy trên portal đã tháo (element không còn, hoặc tệ\n // hơn: element của instance khác vừa tái sử dụng id).\n readonly #timers = new Set<ReturnType<typeof setTimeout>>();\n // Trạng thái panel — đọc được từ ngoài (template/consumer), chỉ component tự ghi.\n readonly isEditing = signal(false);\n readonly isOpened = signal(false);\n readonly isHover = signal(false);\n readonly isLoading = signal(false);\n readonly isMinimum = signal(false);\n readonly isMaximum = signal(false);\n\n constructor() {\n this.#registry.register(this.id);\n\n afterNextRender(() => {\n // why: attachTemplatePortal tạo embedded view từ VCR của chính CdkPortal →\n // KHÔNG cần appRef/injector/document. Chỉ truyền outletElement để tương thích\n // mọi CDK (19 còn slot ComponentFactoryResolver, 20/21 đã bỏ → tránh lệch arg).\n const outlet = new DomPortalOutlet(document.body);\n this.#embeddedViewRef = outlet.attachTemplatePortal(this.portal());\n this.#detectChanges();\n });\n\n this.#destroyRef.onDestroy(() => {\n this.#registry.unregister(this.id);\n for (const timer of this.#timers) clearTimeout(timer);\n this.#timers.clear();\n this.#embeddedViewRef?.destroy();\n });\n }\n\n /** setTimeout có theo dõi — huỷ được hàng loạt trong onDestroy. */\n #schedule(callback: () => void, delay: number): void {\n const timer = setTimeout(() => {\n this.#timers.delete(timer);\n callback();\n }, delay);\n this.#timers.add(timer);\n }\n\n /** Opens the panel, captures its rendered width, and expands it. */\n open = () => {\n this.isOpened.set(true);\n this.#detectChanges();\n\n const element = this.#getElement();\n if (element && !element.classList.contains('c-minium')) {\n this.#schedule(() => {\n const maximumWidth = element.offsetWidth;\n if (maximumWidth > 0) {\n element.dataset['width'] = maximumWidth.toString();\n }\n element.classList.remove('c-closed');\n }, 100);\n }\n\n this.maximize();\n };\n\n /** Collapses the panel to zero width and stops loading. */\n close = () => {\n this.isOpened.set(false);\n this.#detectChanges();\n this.#schedule(() => {\n const element = this.#getElement();\n element?.style.setProperty('width', '0px');\n element?.style.setProperty('right', '0px');\n element?.classList.remove('c-minium');\n element?.classList.add('c-closed');\n }, 100);\n this.stopLoading();\n\n this.#arrangePanels();\n };\n\n /** Shrinks the panel to the 300px minimum strip. */\n minimize = () => {\n this.isMinimum.set(true);\n this.#detectChanges();\n this.#arrangePanels();\n };\n\n /** Restores the panel to its expanded width. */\n maximize = () => {\n this.isMinimum.set(false);\n this.#detectChanges();\n this.#arrangePanels();\n };\n\n /** Toggles fullscreen (`calc(100% - 16px)`). */\n toggleMaximum = () => {\n this.isMaximum.update(isMaximum => !isMaximum);\n this.#detectChanges();\n this.#arrangePanels();\n };\n\n /** Drives the scoped loading overlay through `SdLoadingService`. */\n startLoading = () => {\n this.isLoading.set(true);\n this.#detectChanges();\n this.#loadingService.stop(`#${this.id}`);\n this.#loadingService.start(`#${this.id}`);\n };\n\n /** Stops the scoped loading overlay. */\n stopLoading = () => {\n this.isLoading.set(false);\n this.#detectChanges();\n this.#loadingService.stop(`#${this.id}`);\n };\n\n /** Flips `isEditing` and emits `editingChanged`. */\n toggleEditable() {\n this.isEditing.update(isEditing => !isEditing);\n this.#detectChanges();\n this.editingChanged.emit(this.isEditing());\n }\n\n /** Hover state hook bound in the template. */\n onFocus = () => {\n this.isHover.set(true);\n };\n\n /** Hover state hook bound in the template. */\n onBlur = () => {\n this.isHover.set(false);\n };\n\n #arrangePanels = () => {\n this.#schedule(() => {\n let totalRight = 0;\n const minimumWidth = 300;\n // why: chỉ xếp chỗ các panel do <sd-modal-resizable> đang sống sở hữu (registry), KHÔNG\n // querySelectorAll('.modal-resizable') toàn document — query đó ghi đè inline width/right\n // lên mọi phần tử trùng class, kể cả markup của component khác không liên quan.\n this.#registry.panels().forEach(item => {\n if (item.classList.contains('c-minium')) {\n item.style.setProperty('width', `${minimumWidth}px`);\n item.style.setProperty('right', `${totalRight}px`);\n totalRight += minimumWidth + 8;\n return;\n }\n\n if (item.classList.contains('c-closed')) {\n return;\n }\n\n const expandedWidth = Number.parseInt(item.dataset['width'] || '', 10);\n if (Number.isNaN(expandedWidth)) {\n return;\n }\n\n if (item.classList.contains('c-fullscreen')) {\n item.style.setProperty('width', 'calc(100% - 16px)');\n item.style.setProperty('right', '8px');\n } else {\n item.style.setProperty('width', `${expandedWidth}px`);\n item.style.setProperty('right', `${totalRight}px`);\n }\n totalRight += expandedWidth + 8;\n });\n }, 200);\n };\n\n #getElement() {\n return document.getElementById(this.id);\n }\n\n #detectChanges() {\n this.#ref.markForCheck();\n this.#embeddedViewRef?.detectChanges();\n }\n}\n","<ng-template cdkPortal>\n <div\n [id]=\"id\"\n class=\"modal-resizable\"\n [style.width]=\"width()\"\n [class.modal-resizable--active]=\"isOpened()\"\n [class.modal-resizable--loading]=\"isLoading()\"\n [class.c-minium]=\"isMinimum()\"\n [class.c-fullscreen]=\"isMaximum()\"\n (mouseover)=\"onFocus()\"\n (focus)=\"onFocus()\"\n (mouseout)=\"onBlur()\"\n (blur)=\"onBlur()\">\n @if (isOpened()) {\n <div class=\"modal-resizable__header\">\n <div class=\"modal-resizable__title\">\n <ng-content select=\"[sdTitle]\"></ng-content>\n </div>\n <div class=\"modal-resizable__actions\">\n @if (!isMinimum()) {\n <button type=\"button\" class=\"c-action--white c-min\" (click)=\"minimize()\" mat-icon-button title=\"Thu nhỏ\">\n <sd-icon name=\"remove\"></sd-icon>\n </button>\n }\n @if (isMinimum()) {\n <button type=\"button\" class=\"c-action--white c-max\" (click)=\"maximize()\" mat-icon-button title=\"Phóng to\">\n <sd-icon name=\"remove\"></sd-icon>\n </button>\n }\n @if (!isMaximum()) {\n <button type=\"button\" class=\"c-action--white\" (click)=\"toggleMaximum()\" mat-icon-button title=\"Toàn màn hình\">\n <sd-icon name=\"open_in_full\"></sd-icon>\n </button>\n }\n @if (isMaximum()) {\n <button type=\"button\" class=\"c-action--white\" (click)=\"toggleMaximum()\" mat-icon-button title=\"Thoát toàn màn hình\">\n <sd-icon name=\"fullscreen_exit\"></sd-icon>\n </button>\n }\n <button type=\"button\" class=\"c-action--white\" (click)=\"close()\" mat-icon-button title=\"Đóng\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n </div>\n </div>\n <div class=\"modal-resizable__body\">\n <div class=\"modal-resizable__content\">\n <ng-content select=\"[sdBody]\"></ng-content>\n </div>\n </div>\n <div class=\"modal-resizable__footer\">\n <div class=\"modal-resizable__footer-left\"><ng-content select=\"[sdFooterLeft]\"></ng-content></div>\n <div class=\"modal-resizable__footer-right\"><ng-content select=\"[sdFooterRight]\"></ng-content></div>\n </div>\n }\n </div>\n</ng-template>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|