@sdcorejs/angular 21.1.5 → 21.2.0

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 (208) hide show
  1. package/README.md +12 -4
  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/fesm2022/sdcorejs-angular-components-anchor.mjs +37 -13
  6. package/fesm2022/sdcorejs-angular-components-anchor.mjs.map +1 -1
  7. package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs +2 -2
  8. package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs.map +1 -1
  9. package/fesm2022/sdcorejs-angular-components-badge.mjs +15 -2
  10. package/fesm2022/sdcorejs-angular-components-badge.mjs.map +1 -1
  11. package/fesm2022/sdcorejs-angular-components-button.mjs +2 -2
  12. package/fesm2022/sdcorejs-angular-components-button.mjs.map +1 -1
  13. package/fesm2022/sdcorejs-angular-components-ckeditor-styles.mjs +1 -1
  14. package/fesm2022/sdcorejs-angular-components-ckeditor-styles.mjs.map +1 -1
  15. package/fesm2022/sdcorejs-angular-components-code-editor.mjs +3 -3
  16. package/fesm2022/sdcorejs-angular-components-code-editor.mjs.map +1 -1
  17. package/fesm2022/sdcorejs-angular-components-data-state.mjs +3 -3
  18. package/fesm2022/sdcorejs-angular-components-data-state.mjs.map +1 -1
  19. package/fesm2022/sdcorejs-angular-components-editor.mjs +2 -2
  20. package/fesm2022/sdcorejs-angular-components-editor.mjs.map +1 -1
  21. package/fesm2022/sdcorejs-angular-components-form-generic.mjs +324 -192
  22. package/fesm2022/sdcorejs-angular-components-form-generic.mjs.map +1 -1
  23. package/fesm2022/sdcorejs-angular-components-history.mjs +4 -4
  24. package/fesm2022/sdcorejs-angular-components-history.mjs.map +1 -1
  25. package/fesm2022/sdcorejs-angular-components-import-excel.mjs +13 -5
  26. package/fesm2022/sdcorejs-angular-components-import-excel.mjs.map +1 -1
  27. package/fesm2022/sdcorejs-angular-components-inform.mjs +3 -3
  28. package/fesm2022/sdcorejs-angular-components-inform.mjs.map +1 -1
  29. package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs +164 -8
  30. package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs.map +1 -1
  31. package/fesm2022/sdcorejs-angular-components-modal.mjs +30 -18
  32. package/fesm2022/sdcorejs-angular-components-modal.mjs.map +1 -1
  33. package/fesm2022/sdcorejs-angular-components-org-chart.mjs +60 -13
  34. package/fesm2022/sdcorejs-angular-components-org-chart.mjs.map +1 -1
  35. package/fesm2022/sdcorejs-angular-components-preview.mjs +58 -19
  36. package/fesm2022/sdcorejs-angular-components-preview.mjs.map +1 -1
  37. package/fesm2022/sdcorejs-angular-components-query-bar.mjs +192 -30
  38. package/fesm2022/sdcorejs-angular-components-query-bar.mjs.map +1 -1
  39. package/fesm2022/sdcorejs-angular-components-query-builder.mjs +187 -127
  40. package/fesm2022/sdcorejs-angular-components-query-builder.mjs.map +1 -1
  41. package/fesm2022/sdcorejs-angular-components-section.mjs +16 -2
  42. package/fesm2022/sdcorejs-angular-components-section.mjs.map +1 -1
  43. package/fesm2022/sdcorejs-angular-components-side-drawer.mjs +83 -24
  44. package/fesm2022/sdcorejs-angular-components-side-drawer.mjs.map +1 -1
  45. package/fesm2022/sdcorejs-angular-components-splitter.mjs +57 -21
  46. package/fesm2022/sdcorejs-angular-components-splitter.mjs.map +1 -1
  47. package/fesm2022/sdcorejs-angular-components-stepper.mjs +2 -2
  48. package/fesm2022/sdcorejs-angular-components-stepper.mjs.map +1 -1
  49. package/fesm2022/sdcorejs-angular-components-tab-router.mjs +68 -20
  50. package/fesm2022/sdcorejs-angular-components-tab-router.mjs.map +1 -1
  51. package/fesm2022/sdcorejs-angular-components-tab.mjs +3 -2
  52. package/fesm2022/sdcorejs-angular-components-tab.mjs.map +1 -1
  53. package/fesm2022/sdcorejs-angular-components-table.mjs +557 -255
  54. package/fesm2022/sdcorejs-angular-components-table.mjs.map +1 -1
  55. package/fesm2022/sdcorejs-angular-components-tree.mjs +80 -17
  56. package/fesm2022/sdcorejs-angular-components-tree.mjs.map +1 -1
  57. package/fesm2022/sdcorejs-angular-components-upload-file.mjs +48 -13
  58. package/fesm2022/sdcorejs-angular-components-upload-file.mjs.map +1 -1
  59. package/fesm2022/sdcorejs-angular-components.mjs +6 -1
  60. package/fesm2022/sdcorejs-angular-components.mjs.map +1 -1
  61. package/fesm2022/sdcorejs-angular-configurations.mjs.map +1 -1
  62. package/fesm2022/sdcorejs-angular-directives.mjs +45 -8
  63. package/fesm2022/sdcorejs-angular-directives.mjs.map +1 -1
  64. package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs +40 -29
  65. package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs.map +1 -1
  66. package/fesm2022/sdcorejs-angular-forms-checkbox.mjs +5 -5
  67. package/fesm2022/sdcorejs-angular-forms-checkbox.mjs.map +1 -1
  68. package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs +67 -50
  69. package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs.map +1 -1
  70. package/fesm2022/sdcorejs-angular-forms-chip.mjs +57 -43
  71. package/fesm2022/sdcorejs-angular-forms-chip.mjs.map +1 -1
  72. package/fesm2022/sdcorejs-angular-forms-date-range.mjs +234 -64
  73. package/fesm2022/sdcorejs-angular-forms-date-range.mjs.map +1 -1
  74. package/fesm2022/sdcorejs-angular-forms-date.mjs +209 -42
  75. package/fesm2022/sdcorejs-angular-forms-date.mjs.map +1 -1
  76. package/fesm2022/sdcorejs-angular-forms-datetime.mjs +131 -27
  77. package/fesm2022/sdcorejs-angular-forms-datetime.mjs.map +1 -1
  78. package/fesm2022/sdcorejs-angular-forms-entity-picker.mjs +40 -16
  79. package/fesm2022/sdcorejs-angular-forms-entity-picker.mjs.map +1 -1
  80. package/fesm2022/sdcorejs-angular-forms-input-color.mjs +16 -5
  81. package/fesm2022/sdcorejs-angular-forms-input-color.mjs.map +1 -1
  82. package/fesm2022/sdcorejs-angular-forms-input-number.mjs +24 -9
  83. package/fesm2022/sdcorejs-angular-forms-input-number.mjs.map +1 -1
  84. package/fesm2022/sdcorejs-angular-forms-input.mjs +58 -10
  85. package/fesm2022/sdcorejs-angular-forms-input.mjs.map +1 -1
  86. package/fesm2022/sdcorejs-angular-forms-models.mjs +279 -6
  87. package/fesm2022/sdcorejs-angular-forms-models.mjs.map +1 -1
  88. package/fesm2022/sdcorejs-angular-forms-radio.mjs +4 -4
  89. package/fesm2022/sdcorejs-angular-forms-radio.mjs.map +1 -1
  90. package/fesm2022/sdcorejs-angular-forms-select.mjs +136 -38
  91. package/fesm2022/sdcorejs-angular-forms-select.mjs.map +1 -1
  92. package/fesm2022/sdcorejs-angular-forms-switch.mjs +18 -3
  93. package/fesm2022/sdcorejs-angular-forms-switch.mjs.map +1 -1
  94. package/fesm2022/sdcorejs-angular-forms-textarea.mjs +51 -62
  95. package/fesm2022/sdcorejs-angular-forms-textarea.mjs.map +1 -1
  96. package/fesm2022/sdcorejs-angular-forms-time-range.mjs +57 -12
  97. package/fesm2022/sdcorejs-angular-forms-time-range.mjs.map +1 -1
  98. package/fesm2022/sdcorejs-angular-forms-time.mjs +16 -5
  99. package/fesm2022/sdcorejs-angular-forms-time.mjs.map +1 -1
  100. package/fesm2022/sdcorejs-angular-forms-tree-select.mjs +100 -9
  101. package/fesm2022/sdcorejs-angular-forms-tree-select.mjs.map +1 -1
  102. package/fesm2022/sdcorejs-angular-forms.mjs +91 -46
  103. package/fesm2022/sdcorejs-angular-forms.mjs.map +1 -1
  104. package/fesm2022/sdcorejs-angular-handlers.mjs +18 -3
  105. package/fesm2022/sdcorejs-angular-handlers.mjs.map +1 -1
  106. package/fesm2022/sdcorejs-angular-i18n.mjs +510 -68
  107. package/fesm2022/sdcorejs-angular-i18n.mjs.map +1 -1
  108. package/fesm2022/sdcorejs-angular-interceptors.mjs +87 -29
  109. package/fesm2022/sdcorejs-angular-interceptors.mjs.map +1 -1
  110. package/fesm2022/sdcorejs-angular-modules-auth.mjs +25 -15
  111. package/fesm2022/sdcorejs-angular-modules-auth.mjs.map +1 -1
  112. package/fesm2022/sdcorejs-angular-modules-icon.mjs +2 -2
  113. package/fesm2022/sdcorejs-angular-modules-icon.mjs.map +1 -1
  114. package/fesm2022/sdcorejs-angular-modules-keycloak.mjs +32 -3
  115. package/fesm2022/sdcorejs-angular-modules-keycloak.mjs.map +1 -1
  116. package/fesm2022/sdcorejs-angular-modules-layout.mjs +441 -135
  117. package/fesm2022/sdcorejs-angular-modules-layout.mjs.map +1 -1
  118. package/fesm2022/sdcorejs-angular-modules-permission.mjs +177 -40
  119. package/fesm2022/sdcorejs-angular-modules-permission.mjs.map +1 -1
  120. package/fesm2022/sdcorejs-angular-pipes.mjs +62 -28
  121. package/fesm2022/sdcorejs-angular-pipes.mjs.map +1 -1
  122. package/fesm2022/sdcorejs-angular-services-api.mjs +123 -13
  123. package/fesm2022/sdcorejs-angular-services-api.mjs.map +1 -1
  124. package/fesm2022/sdcorejs-angular-services-cache.mjs +53 -3
  125. package/fesm2022/sdcorejs-angular-services-cache.mjs.map +1 -1
  126. package/fesm2022/sdcorejs-angular-services-confirm.mjs +25 -73
  127. package/fesm2022/sdcorejs-angular-services-confirm.mjs.map +1 -1
  128. package/fesm2022/sdcorejs-angular-services-excel.mjs +12 -10
  129. package/fesm2022/sdcorejs-angular-services-excel.mjs.map +1 -1
  130. package/fesm2022/sdcorejs-angular-services-loading.mjs +75 -84
  131. package/fesm2022/sdcorejs-angular-services-loading.mjs.map +1 -1
  132. package/fesm2022/sdcorejs-angular-services-notify.mjs +46 -5
  133. package/fesm2022/sdcorejs-angular-services-notify.mjs.map +1 -1
  134. package/fesm2022/sdcorejs-angular-services-storage.mjs +4 -0
  135. package/fesm2022/sdcorejs-angular-services-storage.mjs.map +1 -1
  136. package/fesm2022/sdcorejs-angular-services.mjs +0 -1
  137. package/fesm2022/sdcorejs-angular-services.mjs.map +1 -1
  138. package/fesm2022/sdcorejs-angular-utilities-extensions.mjs +225 -80
  139. package/fesm2022/sdcorejs-angular-utilities-extensions.mjs.map +1 -1
  140. package/fesm2022/sdcorejs-angular-utilities-models.mjs +1 -63
  141. package/fesm2022/sdcorejs-angular-utilities-models.mjs.map +1 -1
  142. package/fesm2022/sdcorejs-angular.mjs +17 -10
  143. package/fesm2022/sdcorejs-angular.mjs.map +1 -1
  144. package/package.json +40 -25
  145. package/types/sdcorejs-angular-components-anchor.d.ts +3 -1
  146. package/types/sdcorejs-angular-components-badge.d.ts +1 -0
  147. package/types/sdcorejs-angular-components-ckeditor-styles.d.ts +1 -1
  148. package/types/sdcorejs-angular-components-form-generic.d.ts +44 -33
  149. package/types/sdcorejs-angular-components-import-excel.d.ts +1 -0
  150. package/types/sdcorejs-angular-components-mini-editor.d.ts +1 -1
  151. package/types/sdcorejs-angular-components-modal-resizable.d.ts +92 -0
  152. package/types/sdcorejs-angular-components-modal.d.ts +3 -2
  153. package/types/sdcorejs-angular-components-org-chart.d.ts +6 -0
  154. package/types/sdcorejs-angular-components-preview.d.ts +1 -0
  155. package/types/sdcorejs-angular-components-query-bar.d.ts +55 -17
  156. package/types/sdcorejs-angular-components-query-builder.d.ts +150 -98
  157. package/types/sdcorejs-angular-components-section.d.ts +3 -0
  158. package/types/sdcorejs-angular-components-tab-router.d.ts +19 -19
  159. package/types/sdcorejs-angular-components-table.d.ts +252 -110
  160. package/types/sdcorejs-angular-components-tree.d.ts +48 -30
  161. package/types/sdcorejs-angular-components-upload-file.d.ts +6 -3
  162. package/types/sdcorejs-angular-components.d.ts +6 -1
  163. package/types/sdcorejs-angular-configurations.d.ts +1 -2
  164. package/types/sdcorejs-angular-forms-autocomplete.d.ts +5 -2
  165. package/types/sdcorejs-angular-forms-chip-calendar.d.ts +10 -0
  166. package/types/sdcorejs-angular-forms-date-range.d.ts +14 -2
  167. package/types/sdcorejs-angular-forms-date.d.ts +21 -3
  168. package/types/sdcorejs-angular-forms-datetime.d.ts +13 -2
  169. package/types/sdcorejs-angular-forms-entity-picker.d.ts +9 -3
  170. package/types/sdcorejs-angular-forms-input-color.d.ts +4 -0
  171. package/types/sdcorejs-angular-forms-input-number.d.ts +3 -0
  172. package/types/sdcorejs-angular-forms-input.d.ts +3 -0
  173. package/types/sdcorejs-angular-forms-models.d.ts +146 -5
  174. package/types/sdcorejs-angular-forms-select.d.ts +27 -4
  175. package/types/sdcorejs-angular-forms-switch.d.ts +7 -1
  176. package/types/sdcorejs-angular-forms-textarea.d.ts +5 -3
  177. package/types/sdcorejs-angular-forms-time-range.d.ts +22 -1
  178. package/types/sdcorejs-angular-forms-tree-select.d.ts +36 -5
  179. package/types/sdcorejs-angular-forms.d.ts +22 -13
  180. package/types/sdcorejs-angular-i18n.d.ts +99 -16
  181. package/types/sdcorejs-angular-interceptors.d.ts +13 -3
  182. package/types/sdcorejs-angular-modules-auth.d.ts +1 -1
  183. package/types/sdcorejs-angular-modules-keycloak.d.ts +1 -0
  184. package/types/sdcorejs-angular-modules-layout.d.ts +103 -28
  185. package/types/sdcorejs-angular-modules-permission.d.ts +66 -4
  186. package/types/sdcorejs-angular-pipes.d.ts +27 -5
  187. package/types/sdcorejs-angular-services-api.d.ts +21 -1
  188. package/types/sdcorejs-angular-services-cache.d.ts +11 -1
  189. package/types/sdcorejs-angular-services-notify.d.ts +1 -1
  190. package/types/sdcorejs-angular-services.d.ts +0 -1
  191. package/types/sdcorejs-angular-utilities-extensions.d.ts +83 -61
  192. package/types/sdcorejs-angular-utilities-models.d.ts +2 -76
  193. package/types/sdcorejs-angular.d.ts +11 -2
  194. package/fesm2022/sdcorejs-angular-components-base.mjs +0 -23
  195. package/fesm2022/sdcorejs-angular-components-base.mjs.map +0 -1
  196. package/fesm2022/sdcorejs-angular-components-document-builder.mjs +0 -5038
  197. package/fesm2022/sdcorejs-angular-components-document-builder.mjs.map +0 -1
  198. package/fesm2022/sdcorejs-angular-models.mjs +0 -6
  199. package/fesm2022/sdcorejs-angular-models.mjs.map +0 -1
  200. package/fesm2022/sdcorejs-angular-services-docx.mjs +0 -291
  201. package/fesm2022/sdcorejs-angular-services-docx.mjs.map +0 -1
  202. package/fesm2022/sdcorejs-angular-services-license.mjs +0 -97
  203. package/fesm2022/sdcorejs-angular-services-license.mjs.map +0 -1
  204. package/types/sdcorejs-angular-components-base.d.ts +0 -11
  205. package/types/sdcorejs-angular-components-document-builder.d.ts +0 -252
  206. package/types/sdcorejs-angular-models.d.ts +0 -2
  207. package/types/sdcorejs-angular-services-docx.d.ts +0 -27
  208. package/types/sdcorejs-angular-services-license.d.ts +0 -12
@@ -2,6 +2,8 @@ import * as i0 from '@angular/core';
2
2
  import { inject, ElementRef, input, model, booleanAttribute, effect, Component } from '@angular/core';
3
3
  import { SdIcon } from '@sdcorejs/angular/modules/icon';
4
4
 
5
+ // why: id tăng dần để nối aria-controls từ header sang body — mỗi instance cần một id duy nhất.
6
+ let sectionBodyIdSeq = 0;
5
7
  class SdSection {
6
8
  #el = inject(ElementRef);
7
9
  title = input(undefined, ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
@@ -11,6 +13,8 @@ class SdSection {
11
13
  collapsed = model(false, { ...(ngDevMode ? { debugName: "collapsed" } : /* istanbul ignore next */ {}), alias: 'collapsed' });
12
14
  collapsible = input(false, { ...(ngDevMode ? { debugName: "collapsible" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
13
15
  hideHeader = input(false, { ...(ngDevMode ? { debugName: "hideHeader" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
16
+ /** Id của vùng body, để header `aria-controls` trỏ tới khi section collapsible. */
17
+ bodyId = `sd-section-body-${++sectionBodyIdSeq}`;
14
18
  constructor() {
15
19
  effect(() => {
16
20
  if (this.title()) {
@@ -28,12 +32,22 @@ class SdSection {
28
32
  }
29
33
  }
30
34
  };
35
+ // why: header là div[role=button] nhưng consumer chiếu nội dung tuỳ ý vào [sdHeaderRight]
36
+ // (thường là nút bấm). Nếu không lọc theo target thì Enter/Space trên nút của consumer vừa
37
+ // kích hoạt nút vừa gập section. Chỉ xử lý khi chính header đang giữ focus.
38
+ onHeaderKeydown = (event) => {
39
+ if (event.target !== event.currentTarget)
40
+ return;
41
+ // why: chặn Space cuộn trang trước khi toggle.
42
+ event.preventDefault();
43
+ this.toggleCollapse();
44
+ };
31
45
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdSection, deps: [], target: i0.ɵɵFactoryTarget.Component });
32
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdSection, isStandalone: true, selector: "sd-section", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, subTitle: { classPropertyName: "subTitle", publicName: "subTitle", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, iconColor: { classPropertyName: "iconColor", publicName: "iconColor", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, hideHeader: { classPropertyName: "hideHeader", publicName: "hideHeader", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { collapsed: "collapsedChange" }, ngImport: i0, template: "@let _hideHeader = hideHeader();\n@let _collapsible = collapsible();\n@let _collapsed = collapsed();\n@let _title = title();\n@let _subTitle = subTitle();\n@let _icon = icon();\n@let _iconColor = iconColor();\n<div class=\"sd-section c-shadow-section\">\n @if (!_hideHeader) {\n <div\n class=\"sd-section-header cursor-pointer\"\n (click)=\"toggleCollapse()\"\n aria-hidden=\"true\">\n <div class=\"sd-section-header-left\">\n <ng-content select=\"[sdHeaderLeft]\">\n @if (_icon) {\n <span\n class=\"mr-8 material-icons-outlined\"\n [class.text-primary]=\"_iconColor === 'primary'\"\n [class.text-secondary]=\"_iconColor === 'secondary'\"\n [class.text-error]=\"_iconColor === 'error'\"\n [class.text-warning]=\"_iconColor === 'warning'\"\n [class.text-success]=\"_iconColor === 'success'\">\n {{ _icon }}\n </span>\n }\n <div class=\"sd-section-title-block\">\n @if (_title) {\n <div class=\"sd-section-title T16M\">{{ _title }}</div>\n }\n @if (_subTitle) {\n <div class=\"sd-section-subtitle T12R text-secondary\">{{ _subTitle }}</div>\n }\n </div>\n </ng-content>\n </div>\n <div class=\"sd-section-header-right\">\n <ng-content select=\"[sdHeaderRight]\"></ng-content>\n @if (_collapsible) {\n <div class=\"d-flex align-items-center\">\n @if (_collapsed) {\n <sd-icon name=\"expand_more\"></sd-icon>\n } @else {\n <sd-icon name=\"expand_less\"></sd-icon>\n }\n </div>\n }\n </div>\n </div>\n }\n @if (!_collapsed || _hideHeader) {\n <div class=\"sd-section-body\" [class.sd-section-body--with-header]=\"!_hideHeader\">\n <ng-content></ng-content>\n </div>\n }\n <div class=\"sd-section-footer\">\n <div class=\"sd-section-footer-left\"><ng-content select=\"[sdFooterLeft]\"></ng-content></div>\n <div class=\"sd-section-footer-right\"><ng-content select=\"[sdFooterRight]\"></ng-content></div>\n </div>\n</div>\n", styles: [":host{width:100%;display:block}.sd-section{border-radius:8px;background-color:#fff}.sd-section-header,.sd-section-footer{display:flex;align-items:center;justify-content:space-between;gap:16px;width:100%;padding:8px 16px}.sd-section-header-left,.sd-section-header-right,.sd-section-footer-left,.sd-section-footer-right{display:flex;align-items:center;min-width:0}.sd-section-header-left,.sd-section-footer-left{flex:1 1 auto}.sd-section-header-right,.sd-section-footer-right{flex:0 0 auto;justify-content:flex-end;gap:8px}.sd-section-footer-right{margin-left:auto}.sd-section-title-block,.sd-section-title,.sd-section-subtitle{min-width:0}.sd-section-body{padding:0}.sd-section-body--with-header,.sd-section-footer{border-top:1px solid #e6e6e6}.sd-section-footer-left:empty,.sd-section-footer-right:empty{display:none}.sd-section-footer:has(.sd-section-footer-left:empty):has(.sd-section-footer-right:empty){display:none}.c-shadow-section{box-shadow:-1px 0 #f2f2f2 inset,1px 0 #f2f2f2 inset,0 1px #f2f2f2 inset,0 -1px #f2f2f2 inset,0 1px 2px #2f31361a}.cursor-pointer{cursor:pointer}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }] });
46
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdSection, isStandalone: true, selector: "sd-section", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, subTitle: { classPropertyName: "subTitle", publicName: "subTitle", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, iconColor: { classPropertyName: "iconColor", publicName: "iconColor", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, hideHeader: { classPropertyName: "hideHeader", publicName: "hideHeader", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { collapsed: "collapsedChange" }, ngImport: i0, template: "@let _hideHeader = hideHeader();\n@let _collapsible = collapsible();\n@let _collapsed = collapsed();\n@let _title = title();\n@let _subTitle = subTitle();\n@let _icon = icon();\n@let _iconColor = iconColor();\n<div class=\"sd-section c-shadow-section\">\n @if (!_hideHeader) {\n <!-- why: b\u1ECF aria-hidden=\"true\" \u2014 header ch\u1EE9a title/subTitle v\u00E0 to\u00E0n b\u1ED9 n\u1ED9i dung chi\u1EBFu\n [sdHeaderLeft]/[sdHeaderRight] (th\u01B0\u1EDDng c\u00F3 n\u00FAt b\u1EA5m). aria-hidden xo\u00E1 t\u1EA5t c\u1EA3 kh\u1ECFi\n accessibility tree trong khi v\u00F9ng n\u00E0y v\u1EABn nh\u1EADn click. Khi collapsible, header tr\u1EDF th\u00E0nh\n n\u00FAt th\u1EADt: role=button + tabindex=0 + aria-expanded/aria-controls + Enter/Space. -->\n <div\n class=\"sd-section-header cursor-pointer\"\n [attr.role]=\"_collapsible ? 'button' : null\"\n [attr.tabindex]=\"_collapsible ? 0 : null\"\n [attr.aria-expanded]=\"_collapsible ? !_collapsed : null\"\n [attr.aria-controls]=\"_collapsible ? bodyId : null\"\n (click)=\"toggleCollapse()\"\n (keydown.enter)=\"onHeaderKeydown($event)\"\n (keydown.space)=\"onHeaderKeydown($event)\">\n <div class=\"sd-section-header-left\">\n <ng-content select=\"[sdHeaderLeft]\">\n @if (_icon) {\n <span\n class=\"mr-8 material-icons-outlined\"\n [class.text-primary]=\"_iconColor === 'primary'\"\n [class.text-secondary]=\"_iconColor === 'secondary'\"\n [class.text-error]=\"_iconColor === 'error'\"\n [class.text-warning]=\"_iconColor === 'warning'\"\n [class.text-success]=\"_iconColor === 'success'\">\n {{ _icon }}\n </span>\n }\n <div class=\"sd-section-title-block\">\n @if (_title) {\n <div class=\"sd-section-title T16M\">{{ _title }}</div>\n }\n @if (_subTitle) {\n <div class=\"sd-section-subtitle T12R text-secondary\">{{ _subTitle }}</div>\n }\n </div>\n </ng-content>\n </div>\n <div class=\"sd-section-header-right\">\n <ng-content select=\"[sdHeaderRight]\"></ng-content>\n @if (_collapsible) {\n <div class=\"d-flex align-items-center\">\n @if (_collapsed) {\n <sd-icon name=\"expand_more\"></sd-icon>\n } @else {\n <sd-icon name=\"expand_less\"></sd-icon>\n }\n </div>\n }\n </div>\n </div>\n }\n @if (!_collapsed || _hideHeader) {\n <div class=\"sd-section-body\" [id]=\"bodyId\" [class.sd-section-body--with-header]=\"!_hideHeader\">\n <ng-content></ng-content>\n </div>\n }\n <div class=\"sd-section-footer\">\n <div class=\"sd-section-footer-left\"><ng-content select=\"[sdFooterLeft]\"></ng-content></div>\n <div class=\"sd-section-footer-right\"><ng-content select=\"[sdFooterRight]\"></ng-content></div>\n </div>\n</div>\n", styles: [":host{width:100%;display:block}.sd-section{border-radius:8px;background-color:#fff}.sd-section-header,.sd-section-footer{display:flex;align-items:center;justify-content:space-between;gap:16px;width:100%;padding:8px 16px}.sd-section-header[role=button]:focus-visible{outline:2px solid var(--sd-primary, #005cbb);outline-offset:-2px;border-radius:8px}.sd-section-header-left,.sd-section-header-right,.sd-section-footer-left,.sd-section-footer-right{display:flex;align-items:center;min-width:0}.sd-section-header-left,.sd-section-footer-left{flex:1 1 auto}.sd-section-header-right,.sd-section-footer-right{flex:0 0 auto;justify-content:flex-end;gap:8px}.sd-section-footer-right{margin-left:auto}.sd-section-title-block,.sd-section-title,.sd-section-subtitle{min-width:0}.sd-section-body{padding:0}.sd-section-body--with-header,.sd-section-footer{border-top:1px solid #e6e6e6}.sd-section-footer-left:empty,.sd-section-footer-right:empty{display:none}.sd-section-footer:has(.sd-section-footer-left:empty):has(.sd-section-footer-right:empty){display:none}.c-shadow-section{box-shadow:-1px 0 #f2f2f2 inset,1px 0 #f2f2f2 inset,0 1px #f2f2f2 inset,0 -1px #f2f2f2 inset,0 1px 2px #2f31361a}.cursor-pointer{cursor:pointer}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }] });
33
47
  }
34
48
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdSection, decorators: [{
35
49
  type: Component,
36
- args: [{ selector: 'sd-section', imports: [SdIcon], template: "@let _hideHeader = hideHeader();\n@let _collapsible = collapsible();\n@let _collapsed = collapsed();\n@let _title = title();\n@let _subTitle = subTitle();\n@let _icon = icon();\n@let _iconColor = iconColor();\n<div class=\"sd-section c-shadow-section\">\n @if (!_hideHeader) {\n <div\n class=\"sd-section-header cursor-pointer\"\n (click)=\"toggleCollapse()\"\n aria-hidden=\"true\">\n <div class=\"sd-section-header-left\">\n <ng-content select=\"[sdHeaderLeft]\">\n @if (_icon) {\n <span\n class=\"mr-8 material-icons-outlined\"\n [class.text-primary]=\"_iconColor === 'primary'\"\n [class.text-secondary]=\"_iconColor === 'secondary'\"\n [class.text-error]=\"_iconColor === 'error'\"\n [class.text-warning]=\"_iconColor === 'warning'\"\n [class.text-success]=\"_iconColor === 'success'\">\n {{ _icon }}\n </span>\n }\n <div class=\"sd-section-title-block\">\n @if (_title) {\n <div class=\"sd-section-title T16M\">{{ _title }}</div>\n }\n @if (_subTitle) {\n <div class=\"sd-section-subtitle T12R text-secondary\">{{ _subTitle }}</div>\n }\n </div>\n </ng-content>\n </div>\n <div class=\"sd-section-header-right\">\n <ng-content select=\"[sdHeaderRight]\"></ng-content>\n @if (_collapsible) {\n <div class=\"d-flex align-items-center\">\n @if (_collapsed) {\n <sd-icon name=\"expand_more\"></sd-icon>\n } @else {\n <sd-icon name=\"expand_less\"></sd-icon>\n }\n </div>\n }\n </div>\n </div>\n }\n @if (!_collapsed || _hideHeader) {\n <div class=\"sd-section-body\" [class.sd-section-body--with-header]=\"!_hideHeader\">\n <ng-content></ng-content>\n </div>\n }\n <div class=\"sd-section-footer\">\n <div class=\"sd-section-footer-left\"><ng-content select=\"[sdFooterLeft]\"></ng-content></div>\n <div class=\"sd-section-footer-right\"><ng-content select=\"[sdFooterRight]\"></ng-content></div>\n </div>\n</div>\n", styles: [":host{width:100%;display:block}.sd-section{border-radius:8px;background-color:#fff}.sd-section-header,.sd-section-footer{display:flex;align-items:center;justify-content:space-between;gap:16px;width:100%;padding:8px 16px}.sd-section-header-left,.sd-section-header-right,.sd-section-footer-left,.sd-section-footer-right{display:flex;align-items:center;min-width:0}.sd-section-header-left,.sd-section-footer-left{flex:1 1 auto}.sd-section-header-right,.sd-section-footer-right{flex:0 0 auto;justify-content:flex-end;gap:8px}.sd-section-footer-right{margin-left:auto}.sd-section-title-block,.sd-section-title,.sd-section-subtitle{min-width:0}.sd-section-body{padding:0}.sd-section-body--with-header,.sd-section-footer{border-top:1px solid #e6e6e6}.sd-section-footer-left:empty,.sd-section-footer-right:empty{display:none}.sd-section-footer:has(.sd-section-footer-left:empty):has(.sd-section-footer-right:empty){display:none}.c-shadow-section{box-shadow:-1px 0 #f2f2f2 inset,1px 0 #f2f2f2 inset,0 1px #f2f2f2 inset,0 -1px #f2f2f2 inset,0 1px 2px #2f31361a}.cursor-pointer{cursor:pointer}\n"] }]
50
+ args: [{ selector: 'sd-section', imports: [SdIcon], template: "@let _hideHeader = hideHeader();\n@let _collapsible = collapsible();\n@let _collapsed = collapsed();\n@let _title = title();\n@let _subTitle = subTitle();\n@let _icon = icon();\n@let _iconColor = iconColor();\n<div class=\"sd-section c-shadow-section\">\n @if (!_hideHeader) {\n <!-- why: b\u1ECF aria-hidden=\"true\" \u2014 header ch\u1EE9a title/subTitle v\u00E0 to\u00E0n b\u1ED9 n\u1ED9i dung chi\u1EBFu\n [sdHeaderLeft]/[sdHeaderRight] (th\u01B0\u1EDDng c\u00F3 n\u00FAt b\u1EA5m). aria-hidden xo\u00E1 t\u1EA5t c\u1EA3 kh\u1ECFi\n accessibility tree trong khi v\u00F9ng n\u00E0y v\u1EABn nh\u1EADn click. Khi collapsible, header tr\u1EDF th\u00E0nh\n n\u00FAt th\u1EADt: role=button + tabindex=0 + aria-expanded/aria-controls + Enter/Space. -->\n <div\n class=\"sd-section-header cursor-pointer\"\n [attr.role]=\"_collapsible ? 'button' : null\"\n [attr.tabindex]=\"_collapsible ? 0 : null\"\n [attr.aria-expanded]=\"_collapsible ? !_collapsed : null\"\n [attr.aria-controls]=\"_collapsible ? bodyId : null\"\n (click)=\"toggleCollapse()\"\n (keydown.enter)=\"onHeaderKeydown($event)\"\n (keydown.space)=\"onHeaderKeydown($event)\">\n <div class=\"sd-section-header-left\">\n <ng-content select=\"[sdHeaderLeft]\">\n @if (_icon) {\n <span\n class=\"mr-8 material-icons-outlined\"\n [class.text-primary]=\"_iconColor === 'primary'\"\n [class.text-secondary]=\"_iconColor === 'secondary'\"\n [class.text-error]=\"_iconColor === 'error'\"\n [class.text-warning]=\"_iconColor === 'warning'\"\n [class.text-success]=\"_iconColor === 'success'\">\n {{ _icon }}\n </span>\n }\n <div class=\"sd-section-title-block\">\n @if (_title) {\n <div class=\"sd-section-title T16M\">{{ _title }}</div>\n }\n @if (_subTitle) {\n <div class=\"sd-section-subtitle T12R text-secondary\">{{ _subTitle }}</div>\n }\n </div>\n </ng-content>\n </div>\n <div class=\"sd-section-header-right\">\n <ng-content select=\"[sdHeaderRight]\"></ng-content>\n @if (_collapsible) {\n <div class=\"d-flex align-items-center\">\n @if (_collapsed) {\n <sd-icon name=\"expand_more\"></sd-icon>\n } @else {\n <sd-icon name=\"expand_less\"></sd-icon>\n }\n </div>\n }\n </div>\n </div>\n }\n @if (!_collapsed || _hideHeader) {\n <div class=\"sd-section-body\" [id]=\"bodyId\" [class.sd-section-body--with-header]=\"!_hideHeader\">\n <ng-content></ng-content>\n </div>\n }\n <div class=\"sd-section-footer\">\n <div class=\"sd-section-footer-left\"><ng-content select=\"[sdFooterLeft]\"></ng-content></div>\n <div class=\"sd-section-footer-right\"><ng-content select=\"[sdFooterRight]\"></ng-content></div>\n </div>\n</div>\n", styles: [":host{width:100%;display:block}.sd-section{border-radius:8px;background-color:#fff}.sd-section-header,.sd-section-footer{display:flex;align-items:center;justify-content:space-between;gap:16px;width:100%;padding:8px 16px}.sd-section-header[role=button]:focus-visible{outline:2px solid var(--sd-primary, #005cbb);outline-offset:-2px;border-radius:8px}.sd-section-header-left,.sd-section-header-right,.sd-section-footer-left,.sd-section-footer-right{display:flex;align-items:center;min-width:0}.sd-section-header-left,.sd-section-footer-left{flex:1 1 auto}.sd-section-header-right,.sd-section-footer-right{flex:0 0 auto;justify-content:flex-end;gap:8px}.sd-section-footer-right{margin-left:auto}.sd-section-title-block,.sd-section-title,.sd-section-subtitle{min-width:0}.sd-section-body{padding:0}.sd-section-body--with-header,.sd-section-footer{border-top:1px solid #e6e6e6}.sd-section-footer-left:empty,.sd-section-footer-right:empty{display:none}.sd-section-footer:has(.sd-section-footer-left:empty):has(.sd-section-footer-right:empty){display:none}.c-shadow-section{box-shadow:-1px 0 #f2f2f2 inset,1px 0 #f2f2f2 inset,0 1px #f2f2f2 inset,0 -1px #f2f2f2 inset,0 1px 2px #2f31361a}.cursor-pointer{cursor:pointer}\n"] }]
37
51
  }], ctorParameters: () => [], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], subTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subTitle", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], iconColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconColor", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }, { type: i0.Output, args: ["collapsedChange"] }], collapsible: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsible", required: false }] }], hideHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideHeader", required: false }] }] } });
38
52
 
39
53
  class SdSectionItem {
@@ -1 +1 @@
1
- {"version":3,"file":"sdcorejs-angular-components-section.mjs","sources":["../../../projects/sdcorejs-angular/components/section/src/section.component.ts","../../../projects/sdcorejs-angular/components/section/src/section.component.html","../../../projects/sdcorejs-angular/components/section/src/section-item/section-item.component.ts","../../../projects/sdcorejs-angular/components/section/src/section-item/section-item.component.html","../../../projects/sdcorejs-angular/components/section/sdcorejs-angular-components-section.ts"],"sourcesContent":["import { booleanAttribute, Component, effect, ElementRef, inject, input, model } from '@angular/core';\nimport { Color } from '@sdcorejs/utils/models';\nimport { SdIcon } from '@sdcorejs/angular/modules/icon';\n\n@Component({\n selector: 'sd-section',\n templateUrl: './section.component.html',\n styleUrl: './section.component.scss',\n imports: [SdIcon],\n})\nexport class SdSection {\n #el = inject(ElementRef);\n\n title = input<string | undefined | null>(undefined);\n subTitle = input<string | undefined | null>(undefined);\n icon = input<string | undefined | null>(undefined);\n iconColor = input<Color>('primary', { alias: 'iconColor' });\n\n collapsed = model<boolean>(false, { alias: 'collapsed' });\n collapsible = input(false, { transform: booleanAttribute });\n\n hideHeader = input(false, { transform: booleanAttribute });\n\n constructor() {\n effect(() => {\n if (this.title()) {\n this.#el.nativeElement.removeAttribute('title');\n }\n });\n }\n\n toggleCollapse = () => {\n if (this.collapsible()) {\n this.collapsed.set(!this.collapsed());\n } else {\n if (this.collapsed()) {\n this.collapsed.set(false);\n }\n }\n };\n}\n","@let _hideHeader = hideHeader();\n@let _collapsible = collapsible();\n@let _collapsed = collapsed();\n@let _title = title();\n@let _subTitle = subTitle();\n@let _icon = icon();\n@let _iconColor = iconColor();\n<div class=\"sd-section c-shadow-section\">\n @if (!_hideHeader) {\n <div\n class=\"sd-section-header cursor-pointer\"\n (click)=\"toggleCollapse()\"\n aria-hidden=\"true\">\n <div class=\"sd-section-header-left\">\n <ng-content select=\"[sdHeaderLeft]\">\n @if (_icon) {\n <span\n class=\"mr-8 material-icons-outlined\"\n [class.text-primary]=\"_iconColor === 'primary'\"\n [class.text-secondary]=\"_iconColor === 'secondary'\"\n [class.text-error]=\"_iconColor === 'error'\"\n [class.text-warning]=\"_iconColor === 'warning'\"\n [class.text-success]=\"_iconColor === 'success'\">\n {{ _icon }}\n </span>\n }\n <div class=\"sd-section-title-block\">\n @if (_title) {\n <div class=\"sd-section-title T16M\">{{ _title }}</div>\n }\n @if (_subTitle) {\n <div class=\"sd-section-subtitle T12R text-secondary\">{{ _subTitle }}</div>\n }\n </div>\n </ng-content>\n </div>\n <div class=\"sd-section-header-right\">\n <ng-content select=\"[sdHeaderRight]\"></ng-content>\n @if (_collapsible) {\n <div class=\"d-flex align-items-center\">\n @if (_collapsed) {\n <sd-icon name=\"expand_more\"></sd-icon>\n } @else {\n <sd-icon name=\"expand_less\"></sd-icon>\n }\n </div>\n }\n </div>\n </div>\n }\n @if (!_collapsed || _hideHeader) {\n <div class=\"sd-section-body\" [class.sd-section-body--with-header]=\"!_hideHeader\">\n <ng-content></ng-content>\n </div>\n }\n <div class=\"sd-section-footer\">\n <div class=\"sd-section-footer-left\"><ng-content select=\"[sdFooterLeft]\"></ng-content></div>\n <div class=\"sd-section-footer-right\"><ng-content select=\"[sdFooterRight]\"></ng-content></div>\n </div>\n</div>\n","import { Component, input } from '@angular/core';\n\n@Component({\n selector: 'sd-section-item',\n templateUrl: './section-item.component.html',\n styleUrl: 'section-item.component.scss',\n standalone: true,\n})\nexport class SdSectionItem {\n label = input.required<string>();\n labelWidth = input<string, string | null | undefined>('150px', {\n transform: (val: any): string => {\n return val || '150px';\n },\n });\n}\n","<div class=\"c-item\">\n <div class=\"c-item-label T14R text-secondary\" [style.width]=\"labelWidth()\">{{ label() }}</div>\n <div class=\"c-item-content\"><ng-content></ng-content></div>\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sdcorejs-angular-components-section.mjs","sources":["../../../projects/sdcorejs-angular/components/section/src/section.component.ts","../../../projects/sdcorejs-angular/components/section/src/section.component.html","../../../projects/sdcorejs-angular/components/section/src/section-item/section-item.component.ts","../../../projects/sdcorejs-angular/components/section/src/section-item/section-item.component.html","../../../projects/sdcorejs-angular/components/section/sdcorejs-angular-components-section.ts"],"sourcesContent":["import { booleanAttribute, Component, effect, ElementRef, inject, input, model } from '@angular/core';\nimport { Color } from '@sdcorejs/utils/models';\nimport { SdIcon } from '@sdcorejs/angular/modules/icon';\n\n// why: id tăng dần để nối aria-controls từ header sang body — mỗi instance cần một id duy nhất.\nlet sectionBodyIdSeq = 0;\n\n@Component({\n selector: 'sd-section',\n templateUrl: './section.component.html',\n styleUrl: './section.component.scss',\n imports: [SdIcon],\n})\nexport class SdSection {\n #el = inject(ElementRef);\n\n title = input<string | undefined | null>(undefined);\n subTitle = input<string | undefined | null>(undefined);\n icon = input<string | undefined | null>(undefined);\n iconColor = input<Color>('primary', { alias: 'iconColor' });\n\n collapsed = model<boolean>(false, { alias: 'collapsed' });\n collapsible = input(false, { transform: booleanAttribute });\n\n hideHeader = input(false, { transform: booleanAttribute });\n\n /** Id của vùng body, để header `aria-controls` trỏ tới khi section collapsible. */\n readonly bodyId = `sd-section-body-${++sectionBodyIdSeq}`;\n\n constructor() {\n effect(() => {\n if (this.title()) {\n this.#el.nativeElement.removeAttribute('title');\n }\n });\n }\n\n toggleCollapse = () => {\n if (this.collapsible()) {\n this.collapsed.set(!this.collapsed());\n } else {\n if (this.collapsed()) {\n this.collapsed.set(false);\n }\n }\n };\n\n // why: header là div[role=button] nhưng consumer chiếu nội dung tuỳ ý vào [sdHeaderRight]\n // (thường là nút bấm). Nếu không lọc theo target thì Enter/Space trên nút của consumer vừa\n // kích hoạt nút vừa gập section. Chỉ xử lý khi chính header đang giữ focus.\n onHeaderKeydown = (event: KeyboardEvent) => {\n if (event.target !== event.currentTarget) return;\n // why: chặn Space cuộn trang trước khi toggle.\n event.preventDefault();\n this.toggleCollapse();\n };\n}\n","@let _hideHeader = hideHeader();\n@let _collapsible = collapsible();\n@let _collapsed = collapsed();\n@let _title = title();\n@let _subTitle = subTitle();\n@let _icon = icon();\n@let _iconColor = iconColor();\n<div class=\"sd-section c-shadow-section\">\n @if (!_hideHeader) {\n <!-- why: bỏ aria-hidden=\"true\" — header chứa title/subTitle và toàn bộ nội dung chiếu\n [sdHeaderLeft]/[sdHeaderRight] (thường có nút bấm). aria-hidden xoá tất cả khỏi\n accessibility tree trong khi vùng này vẫn nhận click. Khi collapsible, header trở thành\n nút thật: role=button + tabindex=0 + aria-expanded/aria-controls + Enter/Space. -->\n <div\n class=\"sd-section-header cursor-pointer\"\n [attr.role]=\"_collapsible ? 'button' : null\"\n [attr.tabindex]=\"_collapsible ? 0 : null\"\n [attr.aria-expanded]=\"_collapsible ? !_collapsed : null\"\n [attr.aria-controls]=\"_collapsible ? bodyId : null\"\n (click)=\"toggleCollapse()\"\n (keydown.enter)=\"onHeaderKeydown($event)\"\n (keydown.space)=\"onHeaderKeydown($event)\">\n <div class=\"sd-section-header-left\">\n <ng-content select=\"[sdHeaderLeft]\">\n @if (_icon) {\n <span\n class=\"mr-8 material-icons-outlined\"\n [class.text-primary]=\"_iconColor === 'primary'\"\n [class.text-secondary]=\"_iconColor === 'secondary'\"\n [class.text-error]=\"_iconColor === 'error'\"\n [class.text-warning]=\"_iconColor === 'warning'\"\n [class.text-success]=\"_iconColor === 'success'\">\n {{ _icon }}\n </span>\n }\n <div class=\"sd-section-title-block\">\n @if (_title) {\n <div class=\"sd-section-title T16M\">{{ _title }}</div>\n }\n @if (_subTitle) {\n <div class=\"sd-section-subtitle T12R text-secondary\">{{ _subTitle }}</div>\n }\n </div>\n </ng-content>\n </div>\n <div class=\"sd-section-header-right\">\n <ng-content select=\"[sdHeaderRight]\"></ng-content>\n @if (_collapsible) {\n <div class=\"d-flex align-items-center\">\n @if (_collapsed) {\n <sd-icon name=\"expand_more\"></sd-icon>\n } @else {\n <sd-icon name=\"expand_less\"></sd-icon>\n }\n </div>\n }\n </div>\n </div>\n }\n @if (!_collapsed || _hideHeader) {\n <div class=\"sd-section-body\" [id]=\"bodyId\" [class.sd-section-body--with-header]=\"!_hideHeader\">\n <ng-content></ng-content>\n </div>\n }\n <div class=\"sd-section-footer\">\n <div class=\"sd-section-footer-left\"><ng-content select=\"[sdFooterLeft]\"></ng-content></div>\n <div class=\"sd-section-footer-right\"><ng-content select=\"[sdFooterRight]\"></ng-content></div>\n </div>\n</div>\n","import { Component, input } from '@angular/core';\n\n@Component({\n selector: 'sd-section-item',\n templateUrl: './section-item.component.html',\n styleUrl: 'section-item.component.scss',\n standalone: true,\n})\nexport class SdSectionItem {\n label = input.required<string>();\n labelWidth = input<string, string | null | undefined>('150px', {\n transform: (val: any): string => {\n return val || '150px';\n },\n });\n}\n","<div class=\"c-item\">\n <div class=\"c-item-label T14R text-secondary\" [style.width]=\"labelWidth()\">{{ label() }}</div>\n <div class=\"c-item-content\"><ng-content></ng-content></div>\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -1,14 +1,71 @@
1
1
  import * as i2 from '@angular/cdk/portal';
2
2
  import { CdkPortal, DomPortalOutlet, PortalModule } from '@angular/cdk/portal';
3
3
  import * as i1 from '@angular/common';
4
- import { CommonModule } from '@angular/common';
4
+ import { DOCUMENT, CommonModule } from '@angular/common';
5
5
  import * as i0 from '@angular/core';
6
- import { viewChild, input, booleanAttribute, computed, output, signal, inject, ViewContainerRef, ApplicationRef, Injector, ChangeDetectorRef, DestroyRef, afterNextRender, ChangeDetectionStrategy, Component } from '@angular/core';
6
+ import { inject, Injectable, viewChild, input, booleanAttribute, computed, output, signal, ViewContainerRef, ApplicationRef, Injector, ChangeDetectorRef, DestroyRef, afterNextRender, ChangeDetectionStrategy, Component } from '@angular/core';
7
7
  import { SdLoadingService } from '@sdcorejs/angular/services';
8
8
  import { Utilities } from '@sdcorejs/utils/fns';
9
9
  import { Subject, fromEvent, merge } from 'rxjs';
10
10
  import { map, startWith, distinctUntilChanged, takeUntil } from 'rxjs/operators';
11
11
  import { SdIcon } from '@sdcorejs/angular/modules/icon';
12
+ import { SdTranslatePipe } from '@sdcorejs/angular/i18n';
13
+
14
+ /**
15
+ * why: state nằm ở MODULE, khoá theo `Document`, KHÔNG nằm trong instance service.
16
+ * `providedIn: 'root'` cho một instance mỗi *ứng dụng Angular*, không phải mỗi *document*. Hai app
17
+ * bootstrap độc lập trên cùng một trang (micro-frontend, widget nhúng) sẽ có hai bộ đếm riêng, mỗi
18
+ * bên chụp lại `overflow` mà bên kia vừa ghi — tái lập đúng bug khoá-scroll-vĩnh-viễn mà service
19
+ * này sinh ra để sửa, chỉ dịch lên một tầng. Khoá theo `Document` thì mọi instance trong cùng
20
+ * document dùng chung một bộ đếm, bất kể injector nào tạo ra chúng.
21
+ */
22
+ const scrollLockStates = new WeakMap();
23
+ const stateFor = (doc) => {
24
+ let state = scrollLockStates.get(doc);
25
+ if (!state) {
26
+ state = { count: 0, previousOverflow: null };
27
+ scrollLockStates.set(doc, state);
28
+ }
29
+ return state;
30
+ };
31
+ class SdBodyScrollLockService {
32
+ #document = inject(DOCUMENT);
33
+ /** Số lượng khoá đang giữ trên document này — phục vụ chẩn đoán/test. */
34
+ get count() {
35
+ return stateFor(this.#document).count;
36
+ }
37
+ lock() {
38
+ const state = stateFor(this.#document);
39
+ const body = this.#document.body;
40
+ if (!body)
41
+ return;
42
+ if (state.count === 0) {
43
+ state.previousOverflow = body.style.overflow;
44
+ body.style.overflow = 'hidden';
45
+ }
46
+ state.count++;
47
+ }
48
+ release() {
49
+ const state = stateFor(this.#document);
50
+ // Nhả nhiều hơn khoá (double close, destroy sau close) không được đẩy count xuống âm,
51
+ // nếu không lần khoá kế tiếp sẽ không ghi DOM.
52
+ if (state.count === 0)
53
+ return;
54
+ state.count--;
55
+ if (state.count > 0)
56
+ return;
57
+ const body = this.#document.body;
58
+ if (body)
59
+ body.style.overflow = state.previousOverflow ?? '';
60
+ state.previousOverflow = null;
61
+ }
62
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdBodyScrollLockService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
63
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdBodyScrollLockService, providedIn: 'root' });
64
+ }
65
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdBodyScrollLockService, decorators: [{
66
+ type: Injectable,
67
+ args: [{ providedIn: 'root' }]
68
+ }] });
12
69
 
13
70
  class SdSideDrawer {
14
71
  id = `I${Utilities.generateUuid()}`;
@@ -33,7 +90,9 @@ class SdSideDrawer {
33
90
  dataLoading = computed(() => (this.#isLoadingSignal() ? 'true' : 'false'), ...(ngDevMode ? [{ debugName: "dataLoading" }] : /* istanbul ignore next */ []));
34
91
  isHovered$;
35
92
  #destroy$ = new Subject();
36
- #previousBodyOverflow = null;
93
+ // Instance này có đang giữ 1 khoá scroll hay không — giữ cho lock/release luôn cân bằng
94
+ // dù open()/close() bị gọi lặp.
95
+ #holdsScrollLock = false;
37
96
  #closeRequest;
38
97
  #viewContainerRef = inject(ViewContainerRef);
39
98
  #ar = inject(ApplicationRef);
@@ -41,6 +100,7 @@ class SdSideDrawer {
41
100
  #ref = inject(ChangeDetectorRef);
42
101
  #loadingService = inject(SdLoadingService);
43
102
  #destroyRef = inject(DestroyRef);
103
+ #scrollLock = inject(SdBodyScrollLockService);
44
104
  constructor() {
45
105
  // Thay thế ngAfterViewInit, tự động chạy nội dung này khi DOM sẵn sàng để render
46
106
  afterNextRender(() => {
@@ -57,22 +117,15 @@ class SdSideDrawer {
57
117
  if (this.#embeddedViewRef) {
58
118
  this.#embeddedViewRef.destroy();
59
119
  }
60
- if (this.#isOpenedSignal()) {
61
- if (this.#previousBodyOverflow !== null) {
62
- document.body.style.overflow = this.#previousBodyOverflow;
63
- }
64
- else {
65
- document.body.style.overflow = '';
66
- }
67
- }
120
+ // Destroy khi đang mở cũng phải nhả khoá, nếu không ref-count kẹt > 0 và trang khoá scroll mãi.
121
+ this.#releaseScrollLock();
68
122
  });
69
123
  }
70
124
  open = () => {
71
125
  this.#ref.markForCheck();
72
126
  this.#isOpenedSignal.set(true);
73
- // Chặn scroll ở document body
74
- this.#previousBodyOverflow = document.body.style.overflow;
75
- document.body.style.overflow = 'hidden';
127
+ // Chặn scroll ở document body qua khoá ref-count dùng chung (stack-safe với drawer lồng nhau)
128
+ this.#acquireScrollLock();
76
129
  };
77
130
  close = () => {
78
131
  if (this.beforeClose()) {
@@ -113,15 +166,21 @@ class SdSideDrawer {
113
166
  this.#isOpenedSignal.set(false);
114
167
  this.sdClosed.emit();
115
168
  this.stopLoading();
116
- // Khôi phục lại scroll ở document body
117
- if (this.#previousBodyOverflow !== null) {
118
- document.body.style.overflow = this.#previousBodyOverflow;
119
- this.#previousBodyOverflow = null;
120
- }
121
- else {
122
- document.body.style.overflow = '';
123
- }
169
+ // Khôi phục lại scroll ở document body (chỉ thật sự khôi phục khi drawer cuối cùng nhả khoá)
170
+ this.#releaseScrollLock();
124
171
  };
172
+ #acquireScrollLock() {
173
+ if (this.#holdsScrollLock)
174
+ return;
175
+ this.#holdsScrollLock = true;
176
+ this.#scrollLock.lock();
177
+ }
178
+ #releaseScrollLock() {
179
+ if (!this.#holdsScrollLock)
180
+ return;
181
+ this.#holdsScrollLock = false;
182
+ this.#scrollLock.release();
183
+ }
125
184
  startLoading = () => {
126
185
  this.#isLoadingSignal.set(true);
127
186
  this.#loadingService.stop(`#${this.id}`);
@@ -150,11 +209,11 @@ class SdSideDrawer {
150
209
  this.isHovered$ = merge(mouseEnter$, mouseLeave$).pipe(startWith(false), distinctUntilChanged(), takeUntil(this.#destroy$));
151
210
  }
152
211
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdSideDrawer, deps: [], target: i0.ɵɵFactoryTarget.Component });
153
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdSideDrawer, isStandalone: true, selector: "sd-side-drawer", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, hideClose: { classPropertyName: "hideClose", publicName: "hideClose", isSignal: true, isRequired: false, transformFunction: null }, disableBackdropClose: { classPropertyName: "disableBackdropClose", publicName: "disableBackdropClose", isSignal: true, isRequired: false, transformFunction: null }, beforeClose: { classPropertyName: "beforeClose", publicName: "beforeClose", isSignal: true, isRequired: false, transformFunction: null }, drawerClass: { classPropertyName: "drawerClass", publicName: "drawerClass", isSignal: true, isRequired: false, transformFunction: null }, autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sdClosed: "sdClosed", sdCloseError: "sdCloseError" }, viewQueries: [{ propertyName: "portal", first: true, predicate: CdkPortal, descendants: true, isSignal: true }], ngImport: i0, template: "<ng-template cdkPortal>\n @let _isOpened = isOpened();\n <div\n [id]=\"id\"\n class=\"sd-side-drawer\"\n [ngStyle]=\"{ width: width() }\"\n [ngClass]=\"drawerClass()\"\n [class.sd-side-drawer-active]=\"_isOpened\"\n [attr.data-autoId]=\"autoId()\"\n [attr.data-opened]=\"dataOpened()\"\n [attr.data-loading]=\"dataLoading()\"\n #drawerContainer>\n @if (_isOpened) {\n <ng-container>\n <div class=\"sd-side-drawer-header\">\n <div class=\"sd-side-drawer-header-left\">\n <ng-content select=\"[sdHeaderLeft]\">\n <div class=\"sd-side-drawer-title\">{{ title() }}</div>\n </ng-content>\n </div>\n <div class=\"sd-side-drawer-header-right\">\n <ng-content select=\"[sdHeaderRight]\"></ng-content>\n @if (!hideClose()) {\n <button type=\"button\" class=\"sd-side-drawer-close-btn\" (click)=\"close()\" aria-label=\"Close\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n }\n </div>\n </div>\n @if (_isOpened) {\n <div class=\"sd-side-drawer-body\">\n <div class=\"sd-side-drawer-content\">\n <ng-content></ng-content>\n </div>\n </div>\n }\n <div class=\"sd-side-drawer-footer\">\n <div class=\"sd-side-drawer-footer-left\"><ng-content select=\"[sdFooterLeft]\"></ng-content></div>\n <div class=\"sd-side-drawer-footer-right\"><ng-content select=\"[sdFooterRight]\"></ng-content></div>\n </div>\n </ng-container>\n }\n </div>\n @if (_isOpened) {\n <div\n aria-hidden=\"true\"\n class=\"sd-side-drawer-backdrop\"\n (click)=\"disableBackdropClose() ? null : close()\"\n (wheel)=\"preventScroll($event)\"\n (touchmove)=\"preventScroll($event)\"></div>\n }\n</ng-template>\n", styles: [".sd-side-drawer{--sd-side-drawer-inset: 10px;--sd-side-drawer-radius: var(--sd-overlay-radius, 24px);--sd-side-drawer-surface: var(--sd-surface, #fff);--sd-side-drawer-border: var(--sd-border, #dde0e5);--sd-side-drawer-shadow: 0 6px 18px rgba(15, 23, 42, .18), 0 1px 3px rgba(15, 23, 42, .12);position:fixed;right:var(--sd-side-drawer-inset);top:var(--sd-side-drawer-inset);bottom:var(--sd-side-drawer-inset);max-width:calc(100vw - var(--sd-side-drawer-inset) - var(--sd-side-drawer-inset));background-color:var(--sd-side-drawer-surface);color:var(--sd-text);z-index:999;display:flex;flex-direction:column;opacity:0;visibility:hidden;transform:translate3d(calc(100% + var(--sd-side-drawer-inset)),0,0);transition:all .3s ease-in-out;pointer-events:none;overflow:hidden;border-radius:var(--sd-side-drawer-radius);box-shadow:var(--sd-side-drawer-shadow)}.sd-side-drawer-active{opacity:1;visibility:visible;pointer-events:all;transform:translateZ(0)}.sd-side-drawer-header{min-height:64px;padding:16px;display:flex;align-items:center;justify-content:space-between;gap:16px}.sd-side-drawer-header-left{display:flex;align-items:center;flex:1 1 auto;min-width:0}.sd-side-drawer-header-right{display:flex;align-items:center;gap:8px;flex:0 0 auto}.sd-side-drawer-title{font-size:20px;line-height:28px;font-weight:500;min-width:0}.sd-side-drawer-close-btn{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;box-sizing:border-box;width:32px;height:32px;padding:0;border:0;border-radius:50%;background:transparent;color:var(--sd-text-secondary);line-height:0;cursor:pointer;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;transition:background-color 80ms ease,color 80ms ease}.sd-side-drawer-close-btn:hover{background:var(--sd-surface-muted);color:var(--sd-text)}.sd-side-drawer-close-btn:active{background:var(--sd-border)}.sd-side-drawer-close-btn:focus{outline:none}.sd-side-drawer-close-btn:focus-visible{outline:2px solid var(--sd-primary);outline-offset:2px}.sd-side-drawer-close-btn sd-icon{display:block;width:20px;height:20px;font-size:20px;line-height:20px}.sd-side-drawer-body{flex:1;position:relative}.sd-side-drawer-content{position:absolute;padding:0;inset:0;overflow:auto}.sd-side-drawer-footer{border-top:1px solid var(--sd-side-drawer-border);padding:16px;background-color:var(--sd-side-drawer-surface);display:flex;align-items:center;justify-content:space-between;gap:16px}.sd-side-drawer-footer-left,.sd-side-drawer-footer-right{display:flex;align-items:center;gap:8px;min-width:0}.sd-side-drawer-footer-left{flex:1 1 auto}.sd-side-drawer-footer-right{flex:0 0 auto;margin-left:auto;justify-content:flex-end}.sd-side-drawer-footer-left:empty,.sd-side-drawer-footer-right:empty{display:none}.sd-side-drawer-footer:has(.sd-side-drawer-footer-left:empty):has(.sd-side-drawer-footer-right:empty){display:none}.sd-side-drawer-backdrop{background-color:#0006;border:0;outline:none!important;position:fixed;inset:0;z-index:99;width:100%;height:100%;transition:all .3s ease-in-out}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: PortalModule }, { kind: "directive", type: i2.CdkPortal, selector: "[cdkPortal]", exportAs: ["cdkPortal"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
212
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdSideDrawer, isStandalone: true, selector: "sd-side-drawer", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, hideClose: { classPropertyName: "hideClose", publicName: "hideClose", isSignal: true, isRequired: false, transformFunction: null }, disableBackdropClose: { classPropertyName: "disableBackdropClose", publicName: "disableBackdropClose", isSignal: true, isRequired: false, transformFunction: null }, beforeClose: { classPropertyName: "beforeClose", publicName: "beforeClose", isSignal: true, isRequired: false, transformFunction: null }, drawerClass: { classPropertyName: "drawerClass", publicName: "drawerClass", isSignal: true, isRequired: false, transformFunction: null }, autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sdClosed: "sdClosed", sdCloseError: "sdCloseError" }, viewQueries: [{ propertyName: "portal", first: true, predicate: CdkPortal, descendants: true, isSignal: true }], ngImport: i0, template: "<ng-template cdkPortal>\n @let _isOpened = isOpened();\n <div\n [id]=\"id\"\n class=\"sd-side-drawer\"\n [ngStyle]=\"{ width: width() }\"\n [ngClass]=\"drawerClass()\"\n [class.sd-side-drawer-active]=\"_isOpened\"\n [attr.data-autoId]=\"autoId()\"\n [attr.data-opened]=\"dataOpened()\"\n [attr.data-loading]=\"dataLoading()\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"title() || null\"\n (keydown.escape)=\"disableBackdropClose() ? null : close()\"\n #drawerContainer>\n @if (_isOpened) {\n <ng-container>\n <div class=\"sd-side-drawer-header\">\n <div class=\"sd-side-drawer-header-left\">\n <ng-content select=\"[sdHeaderLeft]\">\n <div class=\"sd-side-drawer-title\">{{ title() }}</div>\n </ng-content>\n </div>\n <div class=\"sd-side-drawer-header-right\">\n <ng-content select=\"[sdHeaderRight]\"></ng-content>\n @if (!hideClose()) {\n <!-- why: aria-label=\"Close\" l\u00E0 chu\u1ED7i ti\u1EBFng Anh c\u1EE9ng trong m\u1ED9t th\u01B0 vi\u1EC7n \u0111\u00E3 c\u00F3 i18n. -->\n <button\n type=\"button\"\n class=\"sd-side-drawer-close-btn\"\n (click)=\"close()\"\n [attr.aria-label]=\"'core.common.close' | sdTranslate\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n }\n </div>\n </div>\n @if (_isOpened) {\n <div class=\"sd-side-drawer-body\">\n <div class=\"sd-side-drawer-content\">\n <ng-content></ng-content>\n </div>\n </div>\n }\n <div class=\"sd-side-drawer-footer\">\n <div class=\"sd-side-drawer-footer-left\"><ng-content select=\"[sdFooterLeft]\"></ng-content></div>\n <div class=\"sd-side-drawer-footer-right\"><ng-content select=\"[sdFooterRight]\"></ng-content></div>\n </div>\n </ng-container>\n }\n </div>\n <!-- why: backdrop l\u00E0 l\u1EDBp ph\u1EE7 b\u1EAFt click thu\u1EA7n trang tr\u00ED, kh\u00F4ng c\u00F3 n\u1ED9i dung \u2014 nh\u01B0ng aria-hidden\n kh\u00F4ng ph\u1EA3i l\u00E0 c\u00E1ch khai b\u00E1o \u0111\u00FAng cho n\u00F3. `role=\"presentation\"` m\u1EDBi l\u00E0 t\u00EDn hi\u1EC7u ch\u00EDnh x\u00E1c\n (ph\u1EA7n t\u1EED n\u00E0y kh\u00F4ng mang ng\u1EEF ngh\u0129a), v\u00E0 quan tr\u1ECDng h\u01A1n: tr\u01B0\u1EDBc \u0111\u00E2y \"\u0111\u00F3ng b\u1EB1ng c\u00E1ch click ra\n ngo\u00E0i\" KH\u00D4NG c\u00F3 t\u01B0\u01A1ng \u0111\u01B0\u01A1ng b\u00E0n ph\u00EDm n\u00E0o c\u1EA3. Escape \u0111\u01B0\u1EE3c th\u00EAm \u1EDF drawer container. -->\n @if (_isOpened) {\n <div\n role=\"presentation\"\n class=\"sd-side-drawer-backdrop\"\n (click)=\"disableBackdropClose() ? null : close()\"\n (wheel)=\"preventScroll($event)\"\n (touchmove)=\"preventScroll($event)\"></div>\n }\n</ng-template>\n", styles: [".sd-side-drawer{--sd-side-drawer-inset: 10px;--sd-side-drawer-radius: var(--sd-overlay-radius, 24px);--sd-side-drawer-surface: var(--sd-surface, #fff);--sd-side-drawer-border: var(--sd-border, #dde0e5);--sd-side-drawer-shadow: 0 6px 18px rgba(15, 23, 42, .18), 0 1px 3px rgba(15, 23, 42, .12);position:fixed;right:var(--sd-side-drawer-inset);top:var(--sd-side-drawer-inset);bottom:var(--sd-side-drawer-inset);max-width:calc(100vw - var(--sd-side-drawer-inset) - var(--sd-side-drawer-inset));background-color:var(--sd-side-drawer-surface);color:var(--sd-text);z-index:999;display:flex;flex-direction:column;opacity:0;visibility:hidden;transform:translate3d(calc(100% + var(--sd-side-drawer-inset)),0,0);transition:all .3s ease-in-out;pointer-events:none;overflow:hidden;border-radius:var(--sd-side-drawer-radius);box-shadow:var(--sd-side-drawer-shadow)}.sd-side-drawer-active{opacity:1;visibility:visible;pointer-events:all;transform:translateZ(0)}.sd-side-drawer-header{min-height:64px;padding:16px;display:flex;align-items:center;justify-content:space-between;gap:16px}.sd-side-drawer-header-left{display:flex;align-items:center;flex:1 1 auto;min-width:0}.sd-side-drawer-header-right{display:flex;align-items:center;gap:8px;flex:0 0 auto}.sd-side-drawer-title{font-size:20px;line-height:28px;font-weight:500;min-width:0}.sd-side-drawer-close-btn{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;box-sizing:border-box;width:32px;height:32px;padding:0;border:0;border-radius:50%;background:transparent;color:var(--sd-text-secondary);line-height:0;cursor:pointer;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;transition:background-color 80ms ease,color 80ms ease}.sd-side-drawer-close-btn:hover{background:var(--sd-surface-muted);color:var(--sd-text)}.sd-side-drawer-close-btn:active{background:var(--sd-border)}.sd-side-drawer-close-btn:focus{outline:none}.sd-side-drawer-close-btn:focus-visible{outline:2px solid var(--sd-primary);outline-offset:2px}.sd-side-drawer-close-btn sd-icon{display:block;width:20px;height:20px;font-size:20px;line-height:20px}.sd-side-drawer-body{flex:1;position:relative}.sd-side-drawer-content{position:absolute;padding:0;inset:0;overflow:auto}.sd-side-drawer-footer{border-top:1px solid var(--sd-side-drawer-border);padding:16px;background-color:var(--sd-side-drawer-surface);display:flex;align-items:center;justify-content:space-between;gap:16px}.sd-side-drawer-footer-left,.sd-side-drawer-footer-right{display:flex;align-items:center;gap:8px;min-width:0}.sd-side-drawer-footer-left{flex:1 1 auto}.sd-side-drawer-footer-right{flex:0 0 auto;margin-left:auto;justify-content:flex-end}.sd-side-drawer-footer-left:empty,.sd-side-drawer-footer-right:empty{display:none}.sd-side-drawer-footer:has(.sd-side-drawer-footer-left:empty):has(.sd-side-drawer-footer-right:empty){display:none}.sd-side-drawer-backdrop{background-color:#0006;border:0;outline:none!important;position:fixed;inset:0;z-index:99;width:100%;height:100%;transition:all .3s ease-in-out}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: PortalModule }, { kind: "directive", type: i2.CdkPortal, selector: "[cdkPortal]", exportAs: ["cdkPortal"] }, { kind: "pipe", type: SdTranslatePipe, name: "sdTranslate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
154
213
  }
155
214
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdSideDrawer, decorators: [{
156
215
  type: Component,
157
- args: [{ selector: 'sd-side-drawer', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [SdIcon, CommonModule, PortalModule], template: "<ng-template cdkPortal>\n @let _isOpened = isOpened();\n <div\n [id]=\"id\"\n class=\"sd-side-drawer\"\n [ngStyle]=\"{ width: width() }\"\n [ngClass]=\"drawerClass()\"\n [class.sd-side-drawer-active]=\"_isOpened\"\n [attr.data-autoId]=\"autoId()\"\n [attr.data-opened]=\"dataOpened()\"\n [attr.data-loading]=\"dataLoading()\"\n #drawerContainer>\n @if (_isOpened) {\n <ng-container>\n <div class=\"sd-side-drawer-header\">\n <div class=\"sd-side-drawer-header-left\">\n <ng-content select=\"[sdHeaderLeft]\">\n <div class=\"sd-side-drawer-title\">{{ title() }}</div>\n </ng-content>\n </div>\n <div class=\"sd-side-drawer-header-right\">\n <ng-content select=\"[sdHeaderRight]\"></ng-content>\n @if (!hideClose()) {\n <button type=\"button\" class=\"sd-side-drawer-close-btn\" (click)=\"close()\" aria-label=\"Close\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n }\n </div>\n </div>\n @if (_isOpened) {\n <div class=\"sd-side-drawer-body\">\n <div class=\"sd-side-drawer-content\">\n <ng-content></ng-content>\n </div>\n </div>\n }\n <div class=\"sd-side-drawer-footer\">\n <div class=\"sd-side-drawer-footer-left\"><ng-content select=\"[sdFooterLeft]\"></ng-content></div>\n <div class=\"sd-side-drawer-footer-right\"><ng-content select=\"[sdFooterRight]\"></ng-content></div>\n </div>\n </ng-container>\n }\n </div>\n @if (_isOpened) {\n <div\n aria-hidden=\"true\"\n class=\"sd-side-drawer-backdrop\"\n (click)=\"disableBackdropClose() ? null : close()\"\n (wheel)=\"preventScroll($event)\"\n (touchmove)=\"preventScroll($event)\"></div>\n }\n</ng-template>\n", styles: [".sd-side-drawer{--sd-side-drawer-inset: 10px;--sd-side-drawer-radius: var(--sd-overlay-radius, 24px);--sd-side-drawer-surface: var(--sd-surface, #fff);--sd-side-drawer-border: var(--sd-border, #dde0e5);--sd-side-drawer-shadow: 0 6px 18px rgba(15, 23, 42, .18), 0 1px 3px rgba(15, 23, 42, .12);position:fixed;right:var(--sd-side-drawer-inset);top:var(--sd-side-drawer-inset);bottom:var(--sd-side-drawer-inset);max-width:calc(100vw - var(--sd-side-drawer-inset) - var(--sd-side-drawer-inset));background-color:var(--sd-side-drawer-surface);color:var(--sd-text);z-index:999;display:flex;flex-direction:column;opacity:0;visibility:hidden;transform:translate3d(calc(100% + var(--sd-side-drawer-inset)),0,0);transition:all .3s ease-in-out;pointer-events:none;overflow:hidden;border-radius:var(--sd-side-drawer-radius);box-shadow:var(--sd-side-drawer-shadow)}.sd-side-drawer-active{opacity:1;visibility:visible;pointer-events:all;transform:translateZ(0)}.sd-side-drawer-header{min-height:64px;padding:16px;display:flex;align-items:center;justify-content:space-between;gap:16px}.sd-side-drawer-header-left{display:flex;align-items:center;flex:1 1 auto;min-width:0}.sd-side-drawer-header-right{display:flex;align-items:center;gap:8px;flex:0 0 auto}.sd-side-drawer-title{font-size:20px;line-height:28px;font-weight:500;min-width:0}.sd-side-drawer-close-btn{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;box-sizing:border-box;width:32px;height:32px;padding:0;border:0;border-radius:50%;background:transparent;color:var(--sd-text-secondary);line-height:0;cursor:pointer;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;transition:background-color 80ms ease,color 80ms ease}.sd-side-drawer-close-btn:hover{background:var(--sd-surface-muted);color:var(--sd-text)}.sd-side-drawer-close-btn:active{background:var(--sd-border)}.sd-side-drawer-close-btn:focus{outline:none}.sd-side-drawer-close-btn:focus-visible{outline:2px solid var(--sd-primary);outline-offset:2px}.sd-side-drawer-close-btn sd-icon{display:block;width:20px;height:20px;font-size:20px;line-height:20px}.sd-side-drawer-body{flex:1;position:relative}.sd-side-drawer-content{position:absolute;padding:0;inset:0;overflow:auto}.sd-side-drawer-footer{border-top:1px solid var(--sd-side-drawer-border);padding:16px;background-color:var(--sd-side-drawer-surface);display:flex;align-items:center;justify-content:space-between;gap:16px}.sd-side-drawer-footer-left,.sd-side-drawer-footer-right{display:flex;align-items:center;gap:8px;min-width:0}.sd-side-drawer-footer-left{flex:1 1 auto}.sd-side-drawer-footer-right{flex:0 0 auto;margin-left:auto;justify-content:flex-end}.sd-side-drawer-footer-left:empty,.sd-side-drawer-footer-right:empty{display:none}.sd-side-drawer-footer:has(.sd-side-drawer-footer-left:empty):has(.sd-side-drawer-footer-right:empty){display:none}.sd-side-drawer-backdrop{background-color:#0006;border:0;outline:none!important;position:fixed;inset:0;z-index:99;width:100%;height:100%;transition:all .3s ease-in-out}\n"] }]
216
+ args: [{ selector: 'sd-side-drawer', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [SdIcon, CommonModule, PortalModule, SdTranslatePipe], template: "<ng-template cdkPortal>\n @let _isOpened = isOpened();\n <div\n [id]=\"id\"\n class=\"sd-side-drawer\"\n [ngStyle]=\"{ width: width() }\"\n [ngClass]=\"drawerClass()\"\n [class.sd-side-drawer-active]=\"_isOpened\"\n [attr.data-autoId]=\"autoId()\"\n [attr.data-opened]=\"dataOpened()\"\n [attr.data-loading]=\"dataLoading()\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"title() || null\"\n (keydown.escape)=\"disableBackdropClose() ? null : close()\"\n #drawerContainer>\n @if (_isOpened) {\n <ng-container>\n <div class=\"sd-side-drawer-header\">\n <div class=\"sd-side-drawer-header-left\">\n <ng-content select=\"[sdHeaderLeft]\">\n <div class=\"sd-side-drawer-title\">{{ title() }}</div>\n </ng-content>\n </div>\n <div class=\"sd-side-drawer-header-right\">\n <ng-content select=\"[sdHeaderRight]\"></ng-content>\n @if (!hideClose()) {\n <!-- why: aria-label=\"Close\" l\u00E0 chu\u1ED7i ti\u1EBFng Anh c\u1EE9ng trong m\u1ED9t th\u01B0 vi\u1EC7n \u0111\u00E3 c\u00F3 i18n. -->\n <button\n type=\"button\"\n class=\"sd-side-drawer-close-btn\"\n (click)=\"close()\"\n [attr.aria-label]=\"'core.common.close' | sdTranslate\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n }\n </div>\n </div>\n @if (_isOpened) {\n <div class=\"sd-side-drawer-body\">\n <div class=\"sd-side-drawer-content\">\n <ng-content></ng-content>\n </div>\n </div>\n }\n <div class=\"sd-side-drawer-footer\">\n <div class=\"sd-side-drawer-footer-left\"><ng-content select=\"[sdFooterLeft]\"></ng-content></div>\n <div class=\"sd-side-drawer-footer-right\"><ng-content select=\"[sdFooterRight]\"></ng-content></div>\n </div>\n </ng-container>\n }\n </div>\n <!-- why: backdrop l\u00E0 l\u1EDBp ph\u1EE7 b\u1EAFt click thu\u1EA7n trang tr\u00ED, kh\u00F4ng c\u00F3 n\u1ED9i dung \u2014 nh\u01B0ng aria-hidden\n kh\u00F4ng ph\u1EA3i l\u00E0 c\u00E1ch khai b\u00E1o \u0111\u00FAng cho n\u00F3. `role=\"presentation\"` m\u1EDBi l\u00E0 t\u00EDn hi\u1EC7u ch\u00EDnh x\u00E1c\n (ph\u1EA7n t\u1EED n\u00E0y kh\u00F4ng mang ng\u1EEF ngh\u0129a), v\u00E0 quan tr\u1ECDng h\u01A1n: tr\u01B0\u1EDBc \u0111\u00E2y \"\u0111\u00F3ng b\u1EB1ng c\u00E1ch click ra\n ngo\u00E0i\" KH\u00D4NG c\u00F3 t\u01B0\u01A1ng \u0111\u01B0\u01A1ng b\u00E0n ph\u00EDm n\u00E0o c\u1EA3. Escape \u0111\u01B0\u1EE3c th\u00EAm \u1EDF drawer container. -->\n @if (_isOpened) {\n <div\n role=\"presentation\"\n class=\"sd-side-drawer-backdrop\"\n (click)=\"disableBackdropClose() ? null : close()\"\n (wheel)=\"preventScroll($event)\"\n (touchmove)=\"preventScroll($event)\"></div>\n }\n</ng-template>\n", styles: [".sd-side-drawer{--sd-side-drawer-inset: 10px;--sd-side-drawer-radius: var(--sd-overlay-radius, 24px);--sd-side-drawer-surface: var(--sd-surface, #fff);--sd-side-drawer-border: var(--sd-border, #dde0e5);--sd-side-drawer-shadow: 0 6px 18px rgba(15, 23, 42, .18), 0 1px 3px rgba(15, 23, 42, .12);position:fixed;right:var(--sd-side-drawer-inset);top:var(--sd-side-drawer-inset);bottom:var(--sd-side-drawer-inset);max-width:calc(100vw - var(--sd-side-drawer-inset) - var(--sd-side-drawer-inset));background-color:var(--sd-side-drawer-surface);color:var(--sd-text);z-index:999;display:flex;flex-direction:column;opacity:0;visibility:hidden;transform:translate3d(calc(100% + var(--sd-side-drawer-inset)),0,0);transition:all .3s ease-in-out;pointer-events:none;overflow:hidden;border-radius:var(--sd-side-drawer-radius);box-shadow:var(--sd-side-drawer-shadow)}.sd-side-drawer-active{opacity:1;visibility:visible;pointer-events:all;transform:translateZ(0)}.sd-side-drawer-header{min-height:64px;padding:16px;display:flex;align-items:center;justify-content:space-between;gap:16px}.sd-side-drawer-header-left{display:flex;align-items:center;flex:1 1 auto;min-width:0}.sd-side-drawer-header-right{display:flex;align-items:center;gap:8px;flex:0 0 auto}.sd-side-drawer-title{font-size:20px;line-height:28px;font-weight:500;min-width:0}.sd-side-drawer-close-btn{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;box-sizing:border-box;width:32px;height:32px;padding:0;border:0;border-radius:50%;background:transparent;color:var(--sd-text-secondary);line-height:0;cursor:pointer;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;transition:background-color 80ms ease,color 80ms ease}.sd-side-drawer-close-btn:hover{background:var(--sd-surface-muted);color:var(--sd-text)}.sd-side-drawer-close-btn:active{background:var(--sd-border)}.sd-side-drawer-close-btn:focus{outline:none}.sd-side-drawer-close-btn:focus-visible{outline:2px solid var(--sd-primary);outline-offset:2px}.sd-side-drawer-close-btn sd-icon{display:block;width:20px;height:20px;font-size:20px;line-height:20px}.sd-side-drawer-body{flex:1;position:relative}.sd-side-drawer-content{position:absolute;padding:0;inset:0;overflow:auto}.sd-side-drawer-footer{border-top:1px solid var(--sd-side-drawer-border);padding:16px;background-color:var(--sd-side-drawer-surface);display:flex;align-items:center;justify-content:space-between;gap:16px}.sd-side-drawer-footer-left,.sd-side-drawer-footer-right{display:flex;align-items:center;gap:8px;min-width:0}.sd-side-drawer-footer-left{flex:1 1 auto}.sd-side-drawer-footer-right{flex:0 0 auto;margin-left:auto;justify-content:flex-end}.sd-side-drawer-footer-left:empty,.sd-side-drawer-footer-right:empty{display:none}.sd-side-drawer-footer:has(.sd-side-drawer-footer-left:empty):has(.sd-side-drawer-footer-right:empty){display:none}.sd-side-drawer-backdrop{background-color:#0006;border:0;outline:none!important;position:fixed;inset:0;z-index:99;width:100%;height:100%;transition:all .3s ease-in-out}\n"] }]
158
217
  }], ctorParameters: () => [], propDecorators: { portal: [{ type: i0.ViewChild, args: [i0.forwardRef(() => CdkPortal), { isSignal: true }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], hideClose: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideClose", required: false }] }], disableBackdropClose: [{ type: i0.Input, args: [{ isSignal: true, alias: "disableBackdropClose", required: false }] }], beforeClose: [{ type: i0.Input, args: [{ isSignal: true, alias: "beforeClose", required: false }] }], drawerClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "drawerClass", required: false }] }], autoIdInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoId", required: false }] }], sdClosed: [{ type: i0.Output, args: ["sdClosed"] }], sdCloseError: [{ type: i0.Output, args: ["sdCloseError"] }] } });
159
218
 
160
219
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"sdcorejs-angular-components-side-drawer.mjs","sources":["../../../projects/sdcorejs-angular/components/side-drawer/src/side-drawer.component.ts","../../../projects/sdcorejs-angular/components/side-drawer/src/side-drawer.component.html","../../../projects/sdcorejs-angular/components/side-drawer/sdcorejs-angular-components-side-drawer.ts"],"sourcesContent":["import { CdkPortal, DomPortalOutlet, PortalModule } from '@angular/cdk/portal';\nimport { CommonModule } from '@angular/common';\nimport {\n ApplicationRef,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n DestroyRef,\n EmbeddedViewRef,\n Injector,\n ViewContainerRef,\n afterNextRender,\n booleanAttribute,\n computed,\n inject,\n input,\n output,\n signal,\n viewChild,\n} from '@angular/core';\nimport { SdLoadingService } from '@sdcorejs/angular/services';\nimport { Utilities } from '@sdcorejs/utils/fns';\nimport { fromEvent, merge, Observable, Subject } from 'rxjs';\nimport { map, takeUntil, startWith, distinctUntilChanged } from 'rxjs/operators';\nimport { SdIcon } from '@sdcorejs/angular/modules/icon';\n\nexport type SdSideDrawerBeforeClose = () => boolean | Promise<boolean>;\n\n@Component({\n selector: 'sd-side-drawer',\n templateUrl: './side-drawer.component.html',\n styleUrl: './side-drawer.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [SdIcon, CommonModule, PortalModule],\n})\nexport class SdSideDrawer {\n id = `I${Utilities.generateUuid()}`;\n\n portal = viewChild.required(CdkPortal);\n\n title = input<string>('');\n width = input<string>('480px');\n hideClose = input<boolean, boolean | ''>(false, { transform: booleanAttribute });\n disableBackdropClose = input<boolean, boolean | ''>(false, { transform: booleanAttribute });\n beforeClose = input<SdSideDrawerBeforeClose | undefined>(undefined);\n\n // Custom CSS class added to the root side-drawer container\n drawerClass = input<any>('');\n\n readonly autoIdInput = input<string | undefined | null>(undefined, { alias: 'autoId' });\n readonly autoId = computed(() => (this.autoIdInput() ? `components-side-drawer-${this.autoIdInput()}` : undefined));\n\n sdClosed = output<void>();\n sdCloseError = output<unknown>();\n\n #embeddedViewRef!: EmbeddedViewRef<any>;\n\n readonly #isOpenedSignal = signal(false);\n readonly #isLoadingSignal = signal(false);\n readonly isOpened = this.#isOpenedSignal.asReadonly();\n readonly isLoading = this.#isLoadingSignal.asReadonly();\n readonly dataOpened = computed(() => (this.#isOpenedSignal() ? 'true' : 'false'));\n readonly dataLoading = computed(() => (this.#isLoadingSignal() ? 'true' : 'false'));\n\n isHovered$!: Observable<boolean>;\n #destroy$ = new Subject<void>();\n #previousBodyOverflow: string | null = null;\n #closeRequest?: Promise<boolean>;\n\n #viewContainerRef = inject(ViewContainerRef);\n #ar = inject(ApplicationRef);\n #injector = inject(Injector);\n #ref = inject(ChangeDetectorRef);\n #loadingService = inject(SdLoadingService);\n #destroyRef = inject(DestroyRef);\n\n constructor() {\n // Thay thế ngAfterViewInit, tự động chạy nội dung này khi DOM sẵn sàng để render\n afterNextRender(() => {\n // 1. Gắn portal vào body và lưu lại EmbeddedViewRef\n const outlet = new DomPortalOutlet(document.body, this.#ar, this.#injector);\n this.#embeddedViewRef = outlet.attachTemplatePortal(this.portal());\n\n // 2. Setup sự kiện hover ngay sau khi DOM thật đã được in ra\n this.#setupHoverSubscription();\n });\n\n // Thay thế ngOnDestroy bằng logic destroy trực tiếp\n this.#destroyRef.onDestroy(() => {\n this.#destroy$.next();\n this.#destroy$.complete();\n\n if (this.#embeddedViewRef) {\n this.#embeddedViewRef.destroy();\n }\n\n if (this.#isOpenedSignal()) {\n if (this.#previousBodyOverflow !== null) {\n document.body.style.overflow = this.#previousBodyOverflow;\n } else {\n document.body.style.overflow = '';\n }\n }\n });\n }\n\n open = () => {\n this.#ref.markForCheck();\n this.#isOpenedSignal.set(true);\n\n // Chặn scroll ở document body\n this.#previousBodyOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n };\n\n close = () => {\n if (this.beforeClose()) {\n void this.requestClose();\n return;\n }\n this.forceClose();\n };\n\n requestClose = (): Promise<boolean> => {\n const guard = this.beforeClose();\n if (!guard) {\n this.forceClose();\n return Promise.resolve(true);\n }\n if (this.#closeRequest) return this.#closeRequest;\n\n const request = Promise.resolve()\n .then(() => guard())\n .then(canClose => {\n if (!canClose) return false;\n this.forceClose();\n return true;\n })\n .catch((error: unknown) => {\n this.sdCloseError.emit(error);\n return false;\n });\n\n this.#closeRequest = request;\n void request.finally(() => {\n if (this.#closeRequest === request) this.#closeRequest = undefined;\n });\n return request;\n };\n\n forceClose = () => {\n this.#ref.markForCheck();\n this.#isOpenedSignal.set(false);\n this.sdClosed.emit();\n this.stopLoading();\n\n // Khôi phục lại scroll ở document body\n if (this.#previousBodyOverflow !== null) {\n document.body.style.overflow = this.#previousBodyOverflow;\n this.#previousBodyOverflow = null;\n } else {\n document.body.style.overflow = '';\n }\n };\n\n startLoading = () => {\n this.#isLoadingSignal.set(true);\n this.#loadingService.stop(`#${this.id}`);\n this.#loadingService.start(`#${this.id}`);\n };\n\n stopLoading = () => {\n this.#isLoadingSignal.set(false);\n this.#loadingService.stop(`#${this.id}`);\n };\n\n preventScroll = (event: Event) => {\n event.preventDefault();\n };\n\n #setupHoverSubscription(): void {\n if (!this.#embeddedViewRef) return;\n\n // 3. Lấy DOM element trực tiếp từ rootNodes của EmbeddedViewRef\n const rootNodes = this.#embeddedViewRef.rootNodes;\n const element = rootNodes.find(\n node => node.nodeType === Node.ELEMENT_NODE && (node as HTMLElement).classList?.contains('sd-side-drawer')\n );\n\n if (!element) {\n console.warn('SdSideDrawer: Cannot find sd-side-drawer element to attach hover event');\n return;\n }\n\n // 4. Gắn event listeners trực tiếp lên element thật\n const mouseEnter$ = fromEvent(element, 'mouseenter').pipe(map(() => true));\n const mouseLeave$ = fromEvent(element, 'mouseleave').pipe(map(() => false));\n\n this.isHovered$ = merge(mouseEnter$, mouseLeave$).pipe(startWith(false), distinctUntilChanged(), takeUntil(this.#destroy$));\n }\n}\n","<ng-template cdkPortal>\n @let _isOpened = isOpened();\n <div\n [id]=\"id\"\n class=\"sd-side-drawer\"\n [ngStyle]=\"{ width: width() }\"\n [ngClass]=\"drawerClass()\"\n [class.sd-side-drawer-active]=\"_isOpened\"\n [attr.data-autoId]=\"autoId()\"\n [attr.data-opened]=\"dataOpened()\"\n [attr.data-loading]=\"dataLoading()\"\n #drawerContainer>\n @if (_isOpened) {\n <ng-container>\n <div class=\"sd-side-drawer-header\">\n <div class=\"sd-side-drawer-header-left\">\n <ng-content select=\"[sdHeaderLeft]\">\n <div class=\"sd-side-drawer-title\">{{ title() }}</div>\n </ng-content>\n </div>\n <div class=\"sd-side-drawer-header-right\">\n <ng-content select=\"[sdHeaderRight]\"></ng-content>\n @if (!hideClose()) {\n <button type=\"button\" class=\"sd-side-drawer-close-btn\" (click)=\"close()\" aria-label=\"Close\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n }\n </div>\n </div>\n @if (_isOpened) {\n <div class=\"sd-side-drawer-body\">\n <div class=\"sd-side-drawer-content\">\n <ng-content></ng-content>\n </div>\n </div>\n }\n <div class=\"sd-side-drawer-footer\">\n <div class=\"sd-side-drawer-footer-left\"><ng-content select=\"[sdFooterLeft]\"></ng-content></div>\n <div class=\"sd-side-drawer-footer-right\"><ng-content select=\"[sdFooterRight]\"></ng-content></div>\n </div>\n </ng-container>\n }\n </div>\n @if (_isOpened) {\n <div\n aria-hidden=\"true\"\n class=\"sd-side-drawer-backdrop\"\n (click)=\"disableBackdropClose() ? null : close()\"\n (wheel)=\"preventScroll($event)\"\n (touchmove)=\"preventScroll($event)\"></div>\n }\n</ng-template>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sdcorejs-angular-components-side-drawer.mjs","sources":["../../../projects/sdcorejs-angular/components/side-drawer/src/body-scroll-lock.service.ts","../../../projects/sdcorejs-angular/components/side-drawer/src/side-drawer.component.ts","../../../projects/sdcorejs-angular/components/side-drawer/src/side-drawer.component.html","../../../projects/sdcorejs-angular/components/side-drawer/sdcorejs-angular-components-side-drawer.ts"],"sourcesContent":["import { DOCUMENT } from '@angular/common';\nimport { Injectable, inject } from '@angular/core';\n\n/**\n * Khoá scroll của `document.body` theo kiểu đếm tham chiếu (ref-count).\n *\n * why: mỗi drawer tự lưu/khôi phục `document.body.style.overflow` là KHÔNG stack-safe. Với hai\n * drawer chồng nhau: drawer ngoài lưu `''` rồi set `'hidden'`; drawer trong lưu `'hidden'` (giá trị\n * do drawer ngoài vừa ghi) rồi cũng set `'hidden'`. Đóng drawer ngoài trước → khôi phục `''` →\n * trang scroll lại được dù drawer trong vẫn mở; đóng drawer trong sau → khôi phục `'hidden'` →\n * trang KHOÁ SCROLL VĨNH VIỄN dù không còn drawer nào. Ref-count chỉ ghi DOM ở lần khoá đầu và\n * lần nhả cuối, nên thứ tự đóng không còn ảnh hưởng.\n */\ninterface SdBodyScrollLockState {\n count: number;\n previousOverflow: string | null;\n}\n\n/**\n * why: state nằm ở MODULE, khoá theo `Document`, KHÔNG nằm trong instance service.\n * `providedIn: 'root'` cho một instance mỗi *ứng dụng Angular*, không phải mỗi *document*. Hai app\n * bootstrap độc lập trên cùng một trang (micro-frontend, widget nhúng) sẽ có hai bộ đếm riêng, mỗi\n * bên chụp lại `overflow` mà bên kia vừa ghi — tái lập đúng bug khoá-scroll-vĩnh-viễn mà service\n * này sinh ra để sửa, chỉ dịch lên một tầng. Khoá theo `Document` thì mọi instance trong cùng\n * document dùng chung một bộ đếm, bất kể injector nào tạo ra chúng.\n */\nconst scrollLockStates = new WeakMap<Document, SdBodyScrollLockState>();\n\nconst stateFor = (doc: Document): SdBodyScrollLockState => {\n let state = scrollLockStates.get(doc);\n if (!state) {\n state = { count: 0, previousOverflow: null };\n scrollLockStates.set(doc, state);\n }\n return state;\n};\n\n@Injectable({ providedIn: 'root' })\nexport class SdBodyScrollLockService {\n readonly #document = inject(DOCUMENT);\n\n /** Số lượng khoá đang giữ trên document này — phục vụ chẩn đoán/test. */\n get count(): number {\n return stateFor(this.#document).count;\n }\n\n lock(): void {\n const state = stateFor(this.#document);\n const body = this.#document.body;\n if (!body) return;\n if (state.count === 0) {\n state.previousOverflow = body.style.overflow;\n body.style.overflow = 'hidden';\n }\n state.count++;\n }\n\n release(): void {\n const state = stateFor(this.#document);\n // Nhả nhiều hơn khoá (double close, destroy sau close) không được đẩy count xuống âm,\n // nếu không lần khoá kế tiếp sẽ không ghi DOM.\n if (state.count === 0) return;\n state.count--;\n if (state.count > 0) return;\n const body = this.#document.body;\n if (body) body.style.overflow = state.previousOverflow ?? '';\n state.previousOverflow = null;\n }\n}\n","import { CdkPortal, DomPortalOutlet, PortalModule } from '@angular/cdk/portal';\nimport { CommonModule } from '@angular/common';\nimport {\n ApplicationRef,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n DestroyRef,\n EmbeddedViewRef,\n Injector,\n ViewContainerRef,\n afterNextRender,\n booleanAttribute,\n computed,\n inject,\n input,\n output,\n signal,\n viewChild,\n} from '@angular/core';\nimport { SdLoadingService } from '@sdcorejs/angular/services';\nimport { Utilities } from '@sdcorejs/utils/fns';\nimport { fromEvent, merge, Observable, Subject } from 'rxjs';\nimport { map, takeUntil, startWith, distinctUntilChanged } from 'rxjs/operators';\nimport { SdIcon } from '@sdcorejs/angular/modules/icon';\nimport { SdTranslatePipe } from '@sdcorejs/angular/i18n';\nimport { SdBodyScrollLockService } from './body-scroll-lock.service';\n\nexport type SdSideDrawerBeforeClose = () => boolean | Promise<boolean>;\n\n@Component({\n selector: 'sd-side-drawer',\n templateUrl: './side-drawer.component.html',\n styleUrl: './side-drawer.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [SdIcon, CommonModule, PortalModule, SdTranslatePipe],\n})\nexport class SdSideDrawer {\n id = `I${Utilities.generateUuid()}`;\n\n portal = viewChild.required(CdkPortal);\n\n title = input<string>('');\n width = input<string>('480px');\n hideClose = input<boolean, boolean | ''>(false, { transform: booleanAttribute });\n disableBackdropClose = input<boolean, boolean | ''>(false, { transform: booleanAttribute });\n beforeClose = input<SdSideDrawerBeforeClose | undefined>(undefined);\n\n // Custom CSS class added to the root side-drawer container\n drawerClass = input<any>('');\n\n readonly autoIdInput = input<string | undefined | null>(undefined, { alias: 'autoId' });\n readonly autoId = computed(() => (this.autoIdInput() ? `components-side-drawer-${this.autoIdInput()}` : undefined));\n\n sdClosed = output<void>();\n sdCloseError = output<unknown>();\n\n #embeddedViewRef!: EmbeddedViewRef<any>;\n\n readonly #isOpenedSignal = signal(false);\n readonly #isLoadingSignal = signal(false);\n readonly isOpened = this.#isOpenedSignal.asReadonly();\n readonly isLoading = this.#isLoadingSignal.asReadonly();\n readonly dataOpened = computed(() => (this.#isOpenedSignal() ? 'true' : 'false'));\n readonly dataLoading = computed(() => (this.#isLoadingSignal() ? 'true' : 'false'));\n\n isHovered$!: Observable<boolean>;\n #destroy$ = new Subject<void>();\n // Instance này có đang giữ 1 khoá scroll hay không — giữ cho lock/release luôn cân bằng\n // dù open()/close() bị gọi lặp.\n #holdsScrollLock = false;\n #closeRequest?: Promise<boolean>;\n\n #viewContainerRef = inject(ViewContainerRef);\n #ar = inject(ApplicationRef);\n #injector = inject(Injector);\n #ref = inject(ChangeDetectorRef);\n #loadingService = inject(SdLoadingService);\n #destroyRef = inject(DestroyRef);\n #scrollLock = inject(SdBodyScrollLockService);\n\n constructor() {\n // Thay thế ngAfterViewInit, tự động chạy nội dung này khi DOM sẵn sàng để render\n afterNextRender(() => {\n // 1. Gắn portal vào body và lưu lại EmbeddedViewRef\n const outlet = new DomPortalOutlet(document.body, this.#ar, this.#injector);\n this.#embeddedViewRef = outlet.attachTemplatePortal(this.portal());\n\n // 2. Setup sự kiện hover ngay sau khi DOM thật đã được in ra\n this.#setupHoverSubscription();\n });\n\n // Thay thế ngOnDestroy bằng logic destroy trực tiếp\n this.#destroyRef.onDestroy(() => {\n this.#destroy$.next();\n this.#destroy$.complete();\n\n if (this.#embeddedViewRef) {\n this.#embeddedViewRef.destroy();\n }\n\n // Destroy khi đang mở cũng phải nhả khoá, nếu không ref-count kẹt > 0 và trang khoá scroll mãi.\n this.#releaseScrollLock();\n });\n }\n\n open = () => {\n this.#ref.markForCheck();\n this.#isOpenedSignal.set(true);\n\n // Chặn scroll ở document body qua khoá ref-count dùng chung (stack-safe với drawer lồng nhau)\n this.#acquireScrollLock();\n };\n\n close = () => {\n if (this.beforeClose()) {\n void this.requestClose();\n return;\n }\n this.forceClose();\n };\n\n requestClose = (): Promise<boolean> => {\n const guard = this.beforeClose();\n if (!guard) {\n this.forceClose();\n return Promise.resolve(true);\n }\n if (this.#closeRequest) return this.#closeRequest;\n\n const request = Promise.resolve()\n .then(() => guard())\n .then(canClose => {\n if (!canClose) return false;\n this.forceClose();\n return true;\n })\n .catch((error: unknown) => {\n this.sdCloseError.emit(error);\n return false;\n });\n\n this.#closeRequest = request;\n void request.finally(() => {\n if (this.#closeRequest === request) this.#closeRequest = undefined;\n });\n return request;\n };\n\n forceClose = () => {\n this.#ref.markForCheck();\n this.#isOpenedSignal.set(false);\n this.sdClosed.emit();\n this.stopLoading();\n\n // Khôi phục lại scroll ở document body (chỉ thật sự khôi phục khi drawer cuối cùng nhả khoá)\n this.#releaseScrollLock();\n };\n\n #acquireScrollLock(): void {\n if (this.#holdsScrollLock) return;\n this.#holdsScrollLock = true;\n this.#scrollLock.lock();\n }\n\n #releaseScrollLock(): void {\n if (!this.#holdsScrollLock) return;\n this.#holdsScrollLock = false;\n this.#scrollLock.release();\n }\n\n startLoading = () => {\n this.#isLoadingSignal.set(true);\n this.#loadingService.stop(`#${this.id}`);\n this.#loadingService.start(`#${this.id}`);\n };\n\n stopLoading = () => {\n this.#isLoadingSignal.set(false);\n this.#loadingService.stop(`#${this.id}`);\n };\n\n preventScroll = (event: Event) => {\n event.preventDefault();\n };\n\n #setupHoverSubscription(): void {\n if (!this.#embeddedViewRef) return;\n\n // 3. Lấy DOM element trực tiếp từ rootNodes của EmbeddedViewRef\n const rootNodes = this.#embeddedViewRef.rootNodes;\n const element = rootNodes.find(\n node => node.nodeType === Node.ELEMENT_NODE && (node as HTMLElement).classList?.contains('sd-side-drawer')\n );\n\n if (!element) {\n console.warn('SdSideDrawer: Cannot find sd-side-drawer element to attach hover event');\n return;\n }\n\n // 4. Gắn event listeners trực tiếp lên element thật\n const mouseEnter$ = fromEvent(element, 'mouseenter').pipe(map(() => true));\n const mouseLeave$ = fromEvent(element, 'mouseleave').pipe(map(() => false));\n\n this.isHovered$ = merge(mouseEnter$, mouseLeave$).pipe(startWith(false), distinctUntilChanged(), takeUntil(this.#destroy$));\n }\n}\n","<ng-template cdkPortal>\n @let _isOpened = isOpened();\n <div\n [id]=\"id\"\n class=\"sd-side-drawer\"\n [ngStyle]=\"{ width: width() }\"\n [ngClass]=\"drawerClass()\"\n [class.sd-side-drawer-active]=\"_isOpened\"\n [attr.data-autoId]=\"autoId()\"\n [attr.data-opened]=\"dataOpened()\"\n [attr.data-loading]=\"dataLoading()\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"title() || null\"\n (keydown.escape)=\"disableBackdropClose() ? null : close()\"\n #drawerContainer>\n @if (_isOpened) {\n <ng-container>\n <div class=\"sd-side-drawer-header\">\n <div class=\"sd-side-drawer-header-left\">\n <ng-content select=\"[sdHeaderLeft]\">\n <div class=\"sd-side-drawer-title\">{{ title() }}</div>\n </ng-content>\n </div>\n <div class=\"sd-side-drawer-header-right\">\n <ng-content select=\"[sdHeaderRight]\"></ng-content>\n @if (!hideClose()) {\n <!-- why: aria-label=\"Close\" là chuỗi tiếng Anh cứng trong một thư viện đã có i18n. -->\n <button\n type=\"button\"\n class=\"sd-side-drawer-close-btn\"\n (click)=\"close()\"\n [attr.aria-label]=\"'core.common.close' | sdTranslate\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n }\n </div>\n </div>\n @if (_isOpened) {\n <div class=\"sd-side-drawer-body\">\n <div class=\"sd-side-drawer-content\">\n <ng-content></ng-content>\n </div>\n </div>\n }\n <div class=\"sd-side-drawer-footer\">\n <div class=\"sd-side-drawer-footer-left\"><ng-content select=\"[sdFooterLeft]\"></ng-content></div>\n <div class=\"sd-side-drawer-footer-right\"><ng-content select=\"[sdFooterRight]\"></ng-content></div>\n </div>\n </ng-container>\n }\n </div>\n <!-- why: backdrop là lớp phủ bắt click thuần trang trí, không có nội dung — nhưng aria-hidden\n không phải là cách khai báo đúng cho nó. `role=\"presentation\"` mới là tín hiệu chính xác\n (phần tử này không mang ngữ nghĩa), và quan trọng hơn: trước đây \"đóng bằng cách click ra\n ngoài\" KHÔNG có tương đương bàn phím nào cả. Escape được thêm ở drawer container. -->\n @if (_isOpened) {\n <div\n role=\"presentation\"\n class=\"sd-side-drawer-backdrop\"\n (click)=\"disableBackdropClose() ? null : close()\"\n (wheel)=\"preventScroll($event)\"\n (touchmove)=\"preventScroll($event)\"></div>\n }\n</ng-template>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}