@sdcorejs/angular 20.1.6 → 20.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.
Files changed (213) hide show
  1. package/README.md +10 -0
  2. package/assets/scss/core/menu.scss +18 -0
  3. package/assets/scss/core/utilities/_misc.scss +15 -0
  4. package/assets/scss/sd-core.scss +1 -0
  5. package/components/anchor/index.d.ts +3 -1
  6. package/components/api-contract-builder/index.d.ts +694 -0
  7. package/components/badge/index.d.ts +2 -0
  8. package/components/ckeditor-styles/index.d.ts +1 -1
  9. package/components/form-generic/index.d.ts +44 -33
  10. package/components/import-excel/index.d.ts +1 -0
  11. package/components/index.d.ts +7 -1
  12. package/components/mini-editor/index.d.ts +1 -1
  13. package/components/modal/index.d.ts +3 -2
  14. package/components/modal-resizable/index.d.ts +92 -0
  15. package/components/org-chart/index.d.ts +6 -0
  16. package/components/preview/index.d.ts +1 -0
  17. package/components/query-bar/index.d.ts +55 -17
  18. package/components/query-builder/index.d.ts +150 -98
  19. package/components/section/index.d.ts +5 -0
  20. package/components/tab-router/index.d.ts +19 -19
  21. package/components/table/index.d.ts +251 -109
  22. package/components/tree/index.d.ts +48 -30
  23. package/components/upload-file/index.d.ts +6 -3
  24. package/configurations/index.d.ts +1 -2
  25. package/fesm2022/sdcorejs-angular-components-anchor.mjs +37 -13
  26. package/fesm2022/sdcorejs-angular-components-anchor.mjs.map +1 -1
  27. package/fesm2022/sdcorejs-angular-components-api-contract-builder.mjs +3417 -0
  28. package/fesm2022/sdcorejs-angular-components-api-contract-builder.mjs.map +1 -0
  29. package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs +2 -2
  30. package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs.map +1 -1
  31. package/fesm2022/sdcorejs-angular-components-badge.mjs +27 -2
  32. package/fesm2022/sdcorejs-angular-components-badge.mjs.map +1 -1
  33. package/fesm2022/sdcorejs-angular-components-button.mjs +2 -2
  34. package/fesm2022/sdcorejs-angular-components-button.mjs.map +1 -1
  35. package/fesm2022/sdcorejs-angular-components-ckeditor-styles.mjs +1 -1
  36. package/fesm2022/sdcorejs-angular-components-ckeditor-styles.mjs.map +1 -1
  37. package/fesm2022/sdcorejs-angular-components-code-editor.mjs +3 -3
  38. package/fesm2022/sdcorejs-angular-components-code-editor.mjs.map +1 -1
  39. package/fesm2022/sdcorejs-angular-components-data-state.mjs +3 -3
  40. package/fesm2022/sdcorejs-angular-components-data-state.mjs.map +1 -1
  41. package/fesm2022/sdcorejs-angular-components-editor.mjs +2 -2
  42. package/fesm2022/sdcorejs-angular-components-editor.mjs.map +1 -1
  43. package/fesm2022/sdcorejs-angular-components-form-generic.mjs +324 -192
  44. package/fesm2022/sdcorejs-angular-components-form-generic.mjs.map +1 -1
  45. package/fesm2022/sdcorejs-angular-components-history.mjs +4 -4
  46. package/fesm2022/sdcorejs-angular-components-history.mjs.map +1 -1
  47. package/fesm2022/sdcorejs-angular-components-import-excel.mjs +13 -5
  48. package/fesm2022/sdcorejs-angular-components-import-excel.mjs.map +1 -1
  49. package/fesm2022/sdcorejs-angular-components-inform.mjs +3 -3
  50. package/fesm2022/sdcorejs-angular-components-inform.mjs.map +1 -1
  51. package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs +164 -8
  52. package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs.map +1 -1
  53. package/fesm2022/sdcorejs-angular-components-modal.mjs +30 -18
  54. package/fesm2022/sdcorejs-angular-components-modal.mjs.map +1 -1
  55. package/fesm2022/sdcorejs-angular-components-operator.mjs +2 -2
  56. package/fesm2022/sdcorejs-angular-components-operator.mjs.map +1 -1
  57. package/fesm2022/sdcorejs-angular-components-org-chart.mjs +60 -13
  58. package/fesm2022/sdcorejs-angular-components-org-chart.mjs.map +1 -1
  59. package/fesm2022/sdcorejs-angular-components-preview.mjs +27 -17
  60. package/fesm2022/sdcorejs-angular-components-preview.mjs.map +1 -1
  61. package/fesm2022/sdcorejs-angular-components-query-bar.mjs +192 -30
  62. package/fesm2022/sdcorejs-angular-components-query-bar.mjs.map +1 -1
  63. package/fesm2022/sdcorejs-angular-components-query-builder.mjs +187 -127
  64. package/fesm2022/sdcorejs-angular-components-query-builder.mjs.map +1 -1
  65. package/fesm2022/sdcorejs-angular-components-section.mjs +33 -2
  66. package/fesm2022/sdcorejs-angular-components-section.mjs.map +1 -1
  67. package/fesm2022/sdcorejs-angular-components-side-drawer.mjs +83 -24
  68. package/fesm2022/sdcorejs-angular-components-side-drawer.mjs.map +1 -1
  69. package/fesm2022/sdcorejs-angular-components-splitter.mjs +57 -21
  70. package/fesm2022/sdcorejs-angular-components-splitter.mjs.map +1 -1
  71. package/fesm2022/sdcorejs-angular-components-stepper.mjs +2 -2
  72. package/fesm2022/sdcorejs-angular-components-stepper.mjs.map +1 -1
  73. package/fesm2022/sdcorejs-angular-components-tab-router.mjs +68 -20
  74. package/fesm2022/sdcorejs-angular-components-tab-router.mjs.map +1 -1
  75. package/fesm2022/sdcorejs-angular-components-tab.mjs +3 -2
  76. package/fesm2022/sdcorejs-angular-components-tab.mjs.map +1 -1
  77. package/fesm2022/sdcorejs-angular-components-table.mjs +556 -268
  78. package/fesm2022/sdcorejs-angular-components-table.mjs.map +1 -1
  79. package/fesm2022/sdcorejs-angular-components-tree.mjs +80 -17
  80. package/fesm2022/sdcorejs-angular-components-tree.mjs.map +1 -1
  81. package/fesm2022/sdcorejs-angular-components-upload-file.mjs +48 -13
  82. package/fesm2022/sdcorejs-angular-components-upload-file.mjs.map +1 -1
  83. package/fesm2022/sdcorejs-angular-components.mjs +7 -1
  84. package/fesm2022/sdcorejs-angular-components.mjs.map +1 -1
  85. package/fesm2022/sdcorejs-angular-configurations.mjs.map +1 -1
  86. package/fesm2022/sdcorejs-angular-directives.mjs +45 -8
  87. package/fesm2022/sdcorejs-angular-directives.mjs.map +1 -1
  88. package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs +40 -29
  89. package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs.map +1 -1
  90. package/fesm2022/sdcorejs-angular-forms-checkbox.mjs +5 -5
  91. package/fesm2022/sdcorejs-angular-forms-checkbox.mjs.map +1 -1
  92. package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs +67 -50
  93. package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs.map +1 -1
  94. package/fesm2022/sdcorejs-angular-forms-chip.mjs +57 -43
  95. package/fesm2022/sdcorejs-angular-forms-chip.mjs.map +1 -1
  96. package/fesm2022/sdcorejs-angular-forms-date-range.mjs +232 -61
  97. package/fesm2022/sdcorejs-angular-forms-date-range.mjs.map +1 -1
  98. package/fesm2022/sdcorejs-angular-forms-date.mjs +96 -24
  99. package/fesm2022/sdcorejs-angular-forms-date.mjs.map +1 -1
  100. package/fesm2022/sdcorejs-angular-forms-datetime.mjs +131 -27
  101. package/fesm2022/sdcorejs-angular-forms-datetime.mjs.map +1 -1
  102. package/fesm2022/sdcorejs-angular-forms-entity-picker.mjs +40 -16
  103. package/fesm2022/sdcorejs-angular-forms-entity-picker.mjs.map +1 -1
  104. package/fesm2022/sdcorejs-angular-forms-input-color.mjs +16 -5
  105. package/fesm2022/sdcorejs-angular-forms-input-color.mjs.map +1 -1
  106. package/fesm2022/sdcorejs-angular-forms-input-number.mjs +24 -9
  107. package/fesm2022/sdcorejs-angular-forms-input-number.mjs.map +1 -1
  108. package/fesm2022/sdcorejs-angular-forms-input.mjs +58 -10
  109. package/fesm2022/sdcorejs-angular-forms-input.mjs.map +1 -1
  110. package/fesm2022/sdcorejs-angular-forms-models.mjs +227 -6
  111. package/fesm2022/sdcorejs-angular-forms-models.mjs.map +1 -1
  112. package/fesm2022/sdcorejs-angular-forms-radio.mjs +4 -4
  113. package/fesm2022/sdcorejs-angular-forms-radio.mjs.map +1 -1
  114. package/fesm2022/sdcorejs-angular-forms-select.mjs +136 -38
  115. package/fesm2022/sdcorejs-angular-forms-select.mjs.map +1 -1
  116. package/fesm2022/sdcorejs-angular-forms-switch.mjs +18 -3
  117. package/fesm2022/sdcorejs-angular-forms-switch.mjs.map +1 -1
  118. package/fesm2022/sdcorejs-angular-forms-textarea.mjs +51 -62
  119. package/fesm2022/sdcorejs-angular-forms-textarea.mjs.map +1 -1
  120. package/fesm2022/sdcorejs-angular-forms-time-range.mjs +57 -12
  121. package/fesm2022/sdcorejs-angular-forms-time-range.mjs.map +1 -1
  122. package/fesm2022/sdcorejs-angular-forms-time.mjs +16 -5
  123. package/fesm2022/sdcorejs-angular-forms-time.mjs.map +1 -1
  124. package/fesm2022/sdcorejs-angular-forms-tree-select.mjs +100 -9
  125. package/fesm2022/sdcorejs-angular-forms-tree-select.mjs.map +1 -1
  126. package/fesm2022/sdcorejs-angular-forms.mjs +91 -46
  127. package/fesm2022/sdcorejs-angular-forms.mjs.map +1 -1
  128. package/fesm2022/sdcorejs-angular-handlers.mjs +18 -3
  129. package/fesm2022/sdcorejs-angular-handlers.mjs.map +1 -1
  130. package/fesm2022/sdcorejs-angular-i18n.mjs +895 -68
  131. package/fesm2022/sdcorejs-angular-i18n.mjs.map +1 -1
  132. package/fesm2022/sdcorejs-angular-interceptors.mjs +87 -29
  133. package/fesm2022/sdcorejs-angular-interceptors.mjs.map +1 -1
  134. package/fesm2022/sdcorejs-angular-modules-auth.mjs +25 -15
  135. package/fesm2022/sdcorejs-angular-modules-auth.mjs.map +1 -1
  136. package/fesm2022/sdcorejs-angular-modules-icon.mjs +2 -2
  137. package/fesm2022/sdcorejs-angular-modules-icon.mjs.map +1 -1
  138. package/fesm2022/sdcorejs-angular-modules-keycloak.mjs +32 -3
  139. package/fesm2022/sdcorejs-angular-modules-keycloak.mjs.map +1 -1
  140. package/fesm2022/sdcorejs-angular-modules-layout.mjs +342 -111
  141. package/fesm2022/sdcorejs-angular-modules-layout.mjs.map +1 -1
  142. package/fesm2022/sdcorejs-angular-modules-permission.mjs +177 -40
  143. package/fesm2022/sdcorejs-angular-modules-permission.mjs.map +1 -1
  144. package/fesm2022/sdcorejs-angular-pipes.mjs +62 -28
  145. package/fesm2022/sdcorejs-angular-pipes.mjs.map +1 -1
  146. package/fesm2022/sdcorejs-angular-services-api.mjs +123 -13
  147. package/fesm2022/sdcorejs-angular-services-api.mjs.map +1 -1
  148. package/fesm2022/sdcorejs-angular-services-cache.mjs +53 -3
  149. package/fesm2022/sdcorejs-angular-services-cache.mjs.map +1 -1
  150. package/fesm2022/sdcorejs-angular-services-confirm.mjs +25 -73
  151. package/fesm2022/sdcorejs-angular-services-confirm.mjs.map +1 -1
  152. package/fesm2022/sdcorejs-angular-services-excel.mjs +12 -10
  153. package/fesm2022/sdcorejs-angular-services-excel.mjs.map +1 -1
  154. package/fesm2022/sdcorejs-angular-services-loading.mjs +75 -84
  155. package/fesm2022/sdcorejs-angular-services-loading.mjs.map +1 -1
  156. package/fesm2022/sdcorejs-angular-services-notify.mjs +46 -5
  157. package/fesm2022/sdcorejs-angular-services-notify.mjs.map +1 -1
  158. package/fesm2022/sdcorejs-angular-services-storage.mjs +4 -0
  159. package/fesm2022/sdcorejs-angular-services-storage.mjs.map +1 -1
  160. package/fesm2022/sdcorejs-angular-services.mjs +0 -1
  161. package/fesm2022/sdcorejs-angular-services.mjs.map +1 -1
  162. package/fesm2022/sdcorejs-angular-utilities-extensions.mjs +225 -80
  163. package/fesm2022/sdcorejs-angular-utilities-extensions.mjs.map +1 -1
  164. package/fesm2022/sdcorejs-angular-utilities-models.mjs +1 -63
  165. package/fesm2022/sdcorejs-angular-utilities-models.mjs.map +1 -1
  166. package/fesm2022/sdcorejs-angular.mjs +17 -10
  167. package/fesm2022/sdcorejs-angular.mjs.map +1 -1
  168. package/forms/autocomplete/index.d.ts +5 -2
  169. package/forms/chip-calendar/index.d.ts +10 -0
  170. package/forms/date/index.d.ts +13 -2
  171. package/forms/date-range/index.d.ts +14 -2
  172. package/forms/datetime/index.d.ts +13 -2
  173. package/forms/entity-picker/index.d.ts +9 -3
  174. package/forms/index.d.ts +22 -13
  175. package/forms/input/index.d.ts +3 -0
  176. package/forms/input-color/index.d.ts +4 -0
  177. package/forms/input-number/index.d.ts +3 -0
  178. package/forms/models/index.d.ts +119 -4
  179. package/forms/select/index.d.ts +27 -4
  180. package/forms/switch/index.d.ts +7 -1
  181. package/forms/textarea/index.d.ts +5 -3
  182. package/forms/time-range/index.d.ts +22 -1
  183. package/forms/tree-select/index.d.ts +36 -5
  184. package/i18n/index.d.ts +175 -16
  185. package/index.d.ts +11 -2
  186. package/interceptors/index.d.ts +13 -3
  187. package/modules/auth/index.d.ts +1 -1
  188. package/modules/keycloak/index.d.ts +1 -0
  189. package/modules/layout/index.d.ts +94 -28
  190. package/modules/permission/index.d.ts +66 -4
  191. package/package.json +36 -25
  192. package/pipes/index.d.ts +27 -5
  193. package/services/api/index.d.ts +21 -1
  194. package/services/cache/index.d.ts +11 -1
  195. package/services/index.d.ts +0 -1
  196. package/services/notify/index.d.ts +1 -1
  197. package/utilities/extensions/index.d.ts +83 -61
  198. package/utilities/models/index.d.ts +2 -76
  199. package/components/base/index.d.ts +0 -11
  200. package/components/document-builder/index.d.ts +0 -252
  201. package/fesm2022/sdcorejs-angular-components-base.mjs +0 -23
  202. package/fesm2022/sdcorejs-angular-components-base.mjs.map +0 -1
  203. package/fesm2022/sdcorejs-angular-components-document-builder.mjs +0 -5038
  204. package/fesm2022/sdcorejs-angular-components-document-builder.mjs.map +0 -1
  205. package/fesm2022/sdcorejs-angular-models.mjs +0 -6
  206. package/fesm2022/sdcorejs-angular-models.mjs.map +0 -1
  207. package/fesm2022/sdcorejs-angular-services-docx.mjs +0 -291
  208. package/fesm2022/sdcorejs-angular-services-docx.mjs.map +0 -1
  209. package/fesm2022/sdcorejs-angular-services-license.mjs +0 -97
  210. package/fesm2022/sdcorejs-angular-services-license.mjs.map +0 -1
  211. package/models/index.d.ts +0 -2
  212. package/services/docx/index.d.ts +0 -27
  213. package/services/license/index.d.ts +0 -12
@@ -1,25 +1,155 @@
1
1
  import * as i0 from '@angular/core';
2
- import { viewChild, input, booleanAttribute, output, inject, ChangeDetectorRef, DestroyRef, signal, afterNextRender, ChangeDetectionStrategy, Component } from '@angular/core';
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: "20.3.25", ngImport: i0, type: SdModalResizableRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
48
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdModalResizableRegistry, providedIn: 'root' });
49
+ }
50
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", 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", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
139
+ /** CSS width used while the panel is expanded (not a minimum). Default `'480px'`. */
17
140
  width = input('480px', ...(ngDevMode ? [{ debugName: "width" }] : []));
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" }] : []));
24
154
  isOpened = signal(false, ...(ngDevMode ? [{ debugName: "isOpened" }] : []));
25
155
  isHover = signal(false, ...(ngDevMode ? [{ debugName: "isHover" }] : []));
@@ -27,6 +157,7 @@ class SdModalResizable {
27
157
  isMinimum = signal(false, ...(ngDevMode ? [{ debugName: "isMinimum" }] : []));
28
158
  isMaximum = signal(false, ...(ngDevMode ? [{ debugName: "isMaximum" }] : []));
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
- setTimeout(() => {
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
- setTimeout(() => {
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
- setTimeout(() => {
261
+ this.#schedule(() => {
109
262
  let totalRight = 0;
110
263
  const minimumWidth = 300;
111
- document.querySelectorAll('.modal-resizable').forEach(item => {
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: "20.3.25", ngImport: i0, type: SdModalResizable, deps: [], target: i0.ɵɵFactoryTarget.Component });
145
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", 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: "20.3.25", 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: "20.3.25", 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;AACtC,IAAA,QAAQ,GAAG,KAAK,CAAwB,KAAK,4CAAI,SAAS,EAAE,gBAAgB,EAAA,CAAA,GAAA,CAA7B,EAAE,SAAS,EAAE,gBAAgB,EAAE,GAAC;AAC/E,IAAA,KAAK,GAAG,KAAK,CAAS,OAAO,iDAAC;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,qDAAC;AACzB,IAAA,QAAQ,GAAG,MAAM,CAAC,KAAK,oDAAC;AACxB,IAAA,OAAO,GAAG,MAAM,CAAC,KAAK,mDAAC;AACvB,IAAA,SAAS,GAAG,MAAM,CAAC,KAAK,qDAAC;AACzB,IAAA,SAAS,GAAG,MAAM,CAAC,KAAK,qDAAC;AACzB,IAAA,SAAS,GAAG,MAAM,CAAC,KAAK,qDAAC;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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"}