@sdcorejs/angular 20.1.5 → 20.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/components/anchor/index.d.ts +3 -1
  6. package/components/badge/index.d.ts +1 -0
  7. package/components/ckeditor-styles/index.d.ts +1 -1
  8. package/components/form-generic/index.d.ts +44 -33
  9. package/components/import-excel/index.d.ts +1 -0
  10. package/components/index.d.ts +6 -1
  11. package/components/mini-editor/index.d.ts +1 -1
  12. package/components/modal/index.d.ts +3 -2
  13. package/components/modal-resizable/index.d.ts +92 -0
  14. package/components/org-chart/index.d.ts +6 -0
  15. package/components/preview/index.d.ts +1 -0
  16. package/components/query-bar/index.d.ts +55 -17
  17. package/components/query-builder/index.d.ts +150 -98
  18. package/components/section/index.d.ts +3 -0
  19. package/components/tab-router/index.d.ts +19 -19
  20. package/components/table/index.d.ts +252 -110
  21. package/components/tree/index.d.ts +48 -30
  22. package/components/upload-file/index.d.ts +6 -3
  23. package/configurations/index.d.ts +1 -2
  24. package/fesm2022/sdcorejs-angular-components-anchor.mjs +37 -13
  25. package/fesm2022/sdcorejs-angular-components-anchor.mjs.map +1 -1
  26. package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs +2 -2
  27. package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs.map +1 -1
  28. package/fesm2022/sdcorejs-angular-components-badge.mjs +15 -2
  29. package/fesm2022/sdcorejs-angular-components-badge.mjs.map +1 -1
  30. package/fesm2022/sdcorejs-angular-components-button.mjs +2 -2
  31. package/fesm2022/sdcorejs-angular-components-button.mjs.map +1 -1
  32. package/fesm2022/sdcorejs-angular-components-ckeditor-styles.mjs +1 -1
  33. package/fesm2022/sdcorejs-angular-components-ckeditor-styles.mjs.map +1 -1
  34. package/fesm2022/sdcorejs-angular-components-code-editor.mjs +3 -3
  35. package/fesm2022/sdcorejs-angular-components-code-editor.mjs.map +1 -1
  36. package/fesm2022/sdcorejs-angular-components-data-state.mjs +3 -3
  37. package/fesm2022/sdcorejs-angular-components-data-state.mjs.map +1 -1
  38. package/fesm2022/sdcorejs-angular-components-editor.mjs +2 -2
  39. package/fesm2022/sdcorejs-angular-components-editor.mjs.map +1 -1
  40. package/fesm2022/sdcorejs-angular-components-form-generic.mjs +324 -192
  41. package/fesm2022/sdcorejs-angular-components-form-generic.mjs.map +1 -1
  42. package/fesm2022/sdcorejs-angular-components-history.mjs +4 -4
  43. package/fesm2022/sdcorejs-angular-components-history.mjs.map +1 -1
  44. package/fesm2022/sdcorejs-angular-components-import-excel.mjs +13 -5
  45. package/fesm2022/sdcorejs-angular-components-import-excel.mjs.map +1 -1
  46. package/fesm2022/sdcorejs-angular-components-inform.mjs +3 -3
  47. package/fesm2022/sdcorejs-angular-components-inform.mjs.map +1 -1
  48. package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs +164 -8
  49. package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs.map +1 -1
  50. package/fesm2022/sdcorejs-angular-components-modal.mjs +30 -18
  51. package/fesm2022/sdcorejs-angular-components-modal.mjs.map +1 -1
  52. package/fesm2022/sdcorejs-angular-components-org-chart.mjs +60 -13
  53. package/fesm2022/sdcorejs-angular-components-org-chart.mjs.map +1 -1
  54. package/fesm2022/sdcorejs-angular-components-preview.mjs +58 -19
  55. package/fesm2022/sdcorejs-angular-components-preview.mjs.map +1 -1
  56. package/fesm2022/sdcorejs-angular-components-query-bar.mjs +192 -30
  57. package/fesm2022/sdcorejs-angular-components-query-bar.mjs.map +1 -1
  58. package/fesm2022/sdcorejs-angular-components-query-builder.mjs +187 -127
  59. package/fesm2022/sdcorejs-angular-components-query-builder.mjs.map +1 -1
  60. package/fesm2022/sdcorejs-angular-components-section.mjs +16 -2
  61. package/fesm2022/sdcorejs-angular-components-section.mjs.map +1 -1
  62. package/fesm2022/sdcorejs-angular-components-side-drawer.mjs +83 -24
  63. package/fesm2022/sdcorejs-angular-components-side-drawer.mjs.map +1 -1
  64. package/fesm2022/sdcorejs-angular-components-splitter.mjs +57 -21
  65. package/fesm2022/sdcorejs-angular-components-splitter.mjs.map +1 -1
  66. package/fesm2022/sdcorejs-angular-components-stepper.mjs +2 -2
  67. package/fesm2022/sdcorejs-angular-components-stepper.mjs.map +1 -1
  68. package/fesm2022/sdcorejs-angular-components-tab-router.mjs +68 -20
  69. package/fesm2022/sdcorejs-angular-components-tab-router.mjs.map +1 -1
  70. package/fesm2022/sdcorejs-angular-components-tab.mjs +3 -2
  71. package/fesm2022/sdcorejs-angular-components-tab.mjs.map +1 -1
  72. package/fesm2022/sdcorejs-angular-components-table.mjs +557 -255
  73. package/fesm2022/sdcorejs-angular-components-table.mjs.map +1 -1
  74. package/fesm2022/sdcorejs-angular-components-tree.mjs +80 -17
  75. package/fesm2022/sdcorejs-angular-components-tree.mjs.map +1 -1
  76. package/fesm2022/sdcorejs-angular-components-upload-file.mjs +48 -13
  77. package/fesm2022/sdcorejs-angular-components-upload-file.mjs.map +1 -1
  78. package/fesm2022/sdcorejs-angular-components.mjs +6 -1
  79. package/fesm2022/sdcorejs-angular-components.mjs.map +1 -1
  80. package/fesm2022/sdcorejs-angular-configurations.mjs.map +1 -1
  81. package/fesm2022/sdcorejs-angular-directives.mjs +45 -8
  82. package/fesm2022/sdcorejs-angular-directives.mjs.map +1 -1
  83. package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs +40 -29
  84. package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs.map +1 -1
  85. package/fesm2022/sdcorejs-angular-forms-checkbox.mjs +5 -5
  86. package/fesm2022/sdcorejs-angular-forms-checkbox.mjs.map +1 -1
  87. package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs +67 -50
  88. package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs.map +1 -1
  89. package/fesm2022/sdcorejs-angular-forms-chip.mjs +57 -43
  90. package/fesm2022/sdcorejs-angular-forms-chip.mjs.map +1 -1
  91. package/fesm2022/sdcorejs-angular-forms-date-range.mjs +234 -64
  92. package/fesm2022/sdcorejs-angular-forms-date-range.mjs.map +1 -1
  93. package/fesm2022/sdcorejs-angular-forms-date.mjs +209 -42
  94. package/fesm2022/sdcorejs-angular-forms-date.mjs.map +1 -1
  95. package/fesm2022/sdcorejs-angular-forms-datetime.mjs +131 -27
  96. package/fesm2022/sdcorejs-angular-forms-datetime.mjs.map +1 -1
  97. package/fesm2022/sdcorejs-angular-forms-entity-picker.mjs +40 -16
  98. package/fesm2022/sdcorejs-angular-forms-entity-picker.mjs.map +1 -1
  99. package/fesm2022/sdcorejs-angular-forms-input-color.mjs +16 -5
  100. package/fesm2022/sdcorejs-angular-forms-input-color.mjs.map +1 -1
  101. package/fesm2022/sdcorejs-angular-forms-input-number.mjs +24 -9
  102. package/fesm2022/sdcorejs-angular-forms-input-number.mjs.map +1 -1
  103. package/fesm2022/sdcorejs-angular-forms-input.mjs +58 -10
  104. package/fesm2022/sdcorejs-angular-forms-input.mjs.map +1 -1
  105. package/fesm2022/sdcorejs-angular-forms-models.mjs +279 -6
  106. package/fesm2022/sdcorejs-angular-forms-models.mjs.map +1 -1
  107. package/fesm2022/sdcorejs-angular-forms-radio.mjs +4 -4
  108. package/fesm2022/sdcorejs-angular-forms-radio.mjs.map +1 -1
  109. package/fesm2022/sdcorejs-angular-forms-select.mjs +136 -38
  110. package/fesm2022/sdcorejs-angular-forms-select.mjs.map +1 -1
  111. package/fesm2022/sdcorejs-angular-forms-switch.mjs +18 -3
  112. package/fesm2022/sdcorejs-angular-forms-switch.mjs.map +1 -1
  113. package/fesm2022/sdcorejs-angular-forms-textarea.mjs +51 -62
  114. package/fesm2022/sdcorejs-angular-forms-textarea.mjs.map +1 -1
  115. package/fesm2022/sdcorejs-angular-forms-time-range.mjs +57 -12
  116. package/fesm2022/sdcorejs-angular-forms-time-range.mjs.map +1 -1
  117. package/fesm2022/sdcorejs-angular-forms-time.mjs +16 -5
  118. package/fesm2022/sdcorejs-angular-forms-time.mjs.map +1 -1
  119. package/fesm2022/sdcorejs-angular-forms-tree-select.mjs +100 -9
  120. package/fesm2022/sdcorejs-angular-forms-tree-select.mjs.map +1 -1
  121. package/fesm2022/sdcorejs-angular-forms.mjs +91 -46
  122. package/fesm2022/sdcorejs-angular-forms.mjs.map +1 -1
  123. package/fesm2022/sdcorejs-angular-handlers.mjs +18 -3
  124. package/fesm2022/sdcorejs-angular-handlers.mjs.map +1 -1
  125. package/fesm2022/sdcorejs-angular-i18n.mjs +510 -68
  126. package/fesm2022/sdcorejs-angular-i18n.mjs.map +1 -1
  127. package/fesm2022/sdcorejs-angular-interceptors.mjs +87 -29
  128. package/fesm2022/sdcorejs-angular-interceptors.mjs.map +1 -1
  129. package/fesm2022/sdcorejs-angular-modules-auth.mjs +25 -15
  130. package/fesm2022/sdcorejs-angular-modules-auth.mjs.map +1 -1
  131. package/fesm2022/sdcorejs-angular-modules-icon.mjs +2 -2
  132. package/fesm2022/sdcorejs-angular-modules-icon.mjs.map +1 -1
  133. package/fesm2022/sdcorejs-angular-modules-keycloak.mjs +32 -3
  134. package/fesm2022/sdcorejs-angular-modules-keycloak.mjs.map +1 -1
  135. package/fesm2022/sdcorejs-angular-modules-layout.mjs +441 -135
  136. package/fesm2022/sdcorejs-angular-modules-layout.mjs.map +1 -1
  137. package/fesm2022/sdcorejs-angular-modules-permission.mjs +177 -40
  138. package/fesm2022/sdcorejs-angular-modules-permission.mjs.map +1 -1
  139. package/fesm2022/sdcorejs-angular-pipes.mjs +62 -28
  140. package/fesm2022/sdcorejs-angular-pipes.mjs.map +1 -1
  141. package/fesm2022/sdcorejs-angular-services-api.mjs +123 -13
  142. package/fesm2022/sdcorejs-angular-services-api.mjs.map +1 -1
  143. package/fesm2022/sdcorejs-angular-services-cache.mjs +53 -3
  144. package/fesm2022/sdcorejs-angular-services-cache.mjs.map +1 -1
  145. package/fesm2022/sdcorejs-angular-services-confirm.mjs +25 -73
  146. package/fesm2022/sdcorejs-angular-services-confirm.mjs.map +1 -1
  147. package/fesm2022/sdcorejs-angular-services-excel.mjs +12 -10
  148. package/fesm2022/sdcorejs-angular-services-excel.mjs.map +1 -1
  149. package/fesm2022/sdcorejs-angular-services-loading.mjs +75 -84
  150. package/fesm2022/sdcorejs-angular-services-loading.mjs.map +1 -1
  151. package/fesm2022/sdcorejs-angular-services-notify.mjs +46 -5
  152. package/fesm2022/sdcorejs-angular-services-notify.mjs.map +1 -1
  153. package/fesm2022/sdcorejs-angular-services-storage.mjs +4 -0
  154. package/fesm2022/sdcorejs-angular-services-storage.mjs.map +1 -1
  155. package/fesm2022/sdcorejs-angular-services.mjs +0 -1
  156. package/fesm2022/sdcorejs-angular-services.mjs.map +1 -1
  157. package/fesm2022/sdcorejs-angular-utilities-extensions.mjs +225 -80
  158. package/fesm2022/sdcorejs-angular-utilities-extensions.mjs.map +1 -1
  159. package/fesm2022/sdcorejs-angular-utilities-models.mjs +1 -63
  160. package/fesm2022/sdcorejs-angular-utilities-models.mjs.map +1 -1
  161. package/fesm2022/sdcorejs-angular.mjs +17 -10
  162. package/fesm2022/sdcorejs-angular.mjs.map +1 -1
  163. package/forms/autocomplete/index.d.ts +5 -2
  164. package/forms/chip-calendar/index.d.ts +10 -0
  165. package/forms/date/index.d.ts +21 -3
  166. package/forms/date-range/index.d.ts +14 -2
  167. package/forms/datetime/index.d.ts +13 -2
  168. package/forms/entity-picker/index.d.ts +9 -3
  169. package/forms/index.d.ts +22 -13
  170. package/forms/input/index.d.ts +3 -0
  171. package/forms/input-color/index.d.ts +4 -0
  172. package/forms/input-number/index.d.ts +3 -0
  173. package/forms/models/index.d.ts +146 -5
  174. package/forms/select/index.d.ts +27 -4
  175. package/forms/switch/index.d.ts +7 -1
  176. package/forms/textarea/index.d.ts +5 -3
  177. package/forms/time-range/index.d.ts +22 -1
  178. package/forms/tree-select/index.d.ts +36 -5
  179. package/i18n/index.d.ts +99 -16
  180. package/index.d.ts +11 -2
  181. package/interceptors/index.d.ts +13 -3
  182. package/modules/auth/index.d.ts +1 -1
  183. package/modules/keycloak/index.d.ts +1 -0
  184. package/modules/layout/index.d.ts +103 -28
  185. package/modules/permission/index.d.ts +66 -4
  186. package/package.json +40 -25
  187. package/pipes/index.d.ts +27 -5
  188. package/services/api/index.d.ts +21 -1
  189. package/services/cache/index.d.ts +11 -1
  190. package/services/index.d.ts +0 -1
  191. package/services/notify/index.d.ts +1 -1
  192. package/utilities/extensions/index.d.ts +83 -61
  193. package/utilities/models/index.d.ts +2 -76
  194. package/components/base/index.d.ts +0 -11
  195. package/components/document-builder/index.d.ts +0 -252
  196. package/fesm2022/sdcorejs-angular-components-base.mjs +0 -23
  197. package/fesm2022/sdcorejs-angular-components-base.mjs.map +0 -1
  198. package/fesm2022/sdcorejs-angular-components-document-builder.mjs +0 -5038
  199. package/fesm2022/sdcorejs-angular-components-document-builder.mjs.map +0 -1
  200. package/fesm2022/sdcorejs-angular-models.mjs +0 -6
  201. package/fesm2022/sdcorejs-angular-models.mjs.map +0 -1
  202. package/fesm2022/sdcorejs-angular-services-docx.mjs +0 -291
  203. package/fesm2022/sdcorejs-angular-services-docx.mjs.map +0 -1
  204. package/fesm2022/sdcorejs-angular-services-license.mjs +0 -97
  205. package/fesm2022/sdcorejs-angular-services-license.mjs.map +0 -1
  206. package/models/index.d.ts +0 -2
  207. package/services/docx/index.d.ts +0 -27
  208. package/services/license/index.d.ts +0 -12
@@ -1,6 +1,6 @@
1
1
  import { DOCUMENT, isPlatformBrowser } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
- import { inject, DestroyRef, PLATFORM_ID, RendererFactory2, Injectable } from '@angular/core';
3
+ import { inject, DestroyRef, PLATFORM_ID, Injectable } from '@angular/core';
4
4
 
5
5
  var _a;
6
6
  const SD_LOADING_STYLE_ATTRIBUTE = 'data-sd-loading-styles';
@@ -38,10 +38,18 @@ const CLOSED_LOADING_REF = Object.freeze({
38
38
  // A closed ref deliberately has no work to release.
39
39
  },
40
40
  });
41
+ /*
42
+ * why: service này cố tình KHÔNG dùng Renderer2. Khi app bật animations, `RendererFactory2` trả về
43
+ * `AnimationRendererFactory` và `Renderer2.removeChild()` chỉ đẩy element vào hàng đợi
44
+ * `collectedLeaveElements` của animation engine — DOM chỉ thật sự bị gỡ ở lần flush kế tiếp (sau một
45
+ * chu kỳ change detection). Overlay và `style[data-sd-loading-styles]` ở đây là DOM ngoài view, được
46
+ * gỡ lúc destroy injector hoặc từ ngữ cảnh async ngoài Angular, tức không còn flush nào chạy sau đó →
47
+ * node nằm lại trong document vĩnh viễn (overlay z-index 99999 khoá cả trang). Ghi DOM trực tiếp trên
48
+ * `DOCUMENT` giữ append/remove đối xứng và đồng bộ; nhánh SSR đã được `isPlatformBrowser` chặn sẵn.
49
+ */
41
50
  class SdLoadingService {
42
51
  static documentRecords = new WeakMap();
43
52
  #document;
44
- #renderer;
45
53
  #isBrowser;
46
54
  #destroyRef = inject(DestroyRef);
47
55
  #handleQueue = [];
@@ -50,20 +58,17 @@ class SdLoadingService {
50
58
  constructor() {
51
59
  this.#document = inject(DOCUMENT);
52
60
  this.#isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
53
- const rendererFactory = inject(RendererFactory2);
54
- this.#renderer = this.#isBrowser ? rendererFactory.createRenderer(null, null) : null;
55
61
  this.#destroyRef.onDestroy(() => this.#destroy());
56
62
  }
57
63
  /** Starts loading for every current selector match and returns ownership of those exact hosts. */
58
64
  start = (selector = 'body') => {
59
- const renderer = this.#renderer;
60
- if (this.#destroyed || !this.#isBrowser || !renderer)
65
+ if (this.#destroyed || !this.#isBrowser)
61
66
  return CLOSED_LOADING_REF;
62
67
  const hosts = Array.from(this.#document.querySelectorAll(selector));
63
68
  if (hosts.length === 0)
64
69
  return CLOSED_LOADING_REF;
65
70
  const documentRecord = this.#getOrCreateDocumentRecord();
66
- this.#ensureStyles(documentRecord, renderer);
71
+ this.#ensureStyles(documentRecord);
67
72
  const state = { selector, contributions: [], closed: false };
68
73
  for (const host of hosts) {
69
74
  const contribution = {
@@ -73,14 +78,14 @@ class SdLoadingService {
73
78
  released: false,
74
79
  };
75
80
  state.contributions.push(contribution);
76
- this.#acquireHost(documentRecord, contribution, renderer);
81
+ this.#acquireHost(documentRecord, contribution);
77
82
  }
78
83
  this.#handleQueue.push(state);
79
84
  return {
80
85
  get closed() {
81
86
  return state.closed;
82
87
  },
83
- close: () => this.#closeHandle(state, renderer),
88
+ close: () => this.#closeHandle(state),
84
89
  };
85
90
  };
86
91
  /** Returns the first matched globally loading host, `false` for idle matches, or `null` when unavailable. */
@@ -100,12 +105,11 @@ class SdLoadingService {
100
105
  };
101
106
  /** Releases the oldest exact-selector start, with a current-host fallback for legacy cross-selector calls. */
102
107
  stop = (selector = 'body') => {
103
- const renderer = this.#renderer;
104
- if (this.#destroyed || !this.#isBrowser || !renderer)
108
+ if (this.#destroyed || !this.#isBrowser)
105
109
  return;
106
110
  const exactHandle = this.#handleQueue.find(state => !state.closed && state.selector === selector);
107
111
  if (exactHandle) {
108
- this.#closeHandle(exactHandle, renderer);
112
+ this.#closeHandle(exactHandle);
109
113
  return;
110
114
  }
111
115
  const documentRecord = _a.documentRecords.get(this.#document);
@@ -116,7 +120,7 @@ class SdLoadingService {
116
120
  .get(host)
117
121
  ?.contributions.find(candidate => !candidate.released && candidate.service === this);
118
122
  if (contribution)
119
- this.#releaseContribution(documentRecord, contribution, renderer);
123
+ this.#releaseContribution(documentRecord, contribution);
120
124
  }
121
125
  };
122
126
  /** Runs a task inside a loading lifetime and always releases it without replacing the task result or error. */
@@ -142,37 +146,34 @@ class SdLoadingService {
142
146
  }
143
147
  return record;
144
148
  }
145
- #acquireHost(documentRecord, contribution, renderer) {
149
+ #acquireHost(documentRecord, contribution) {
146
150
  const host = contribution.host;
147
151
  const existing = documentRecord.hostStates.get(host);
148
152
  if (existing) {
149
- this.#repairOverlay(host, existing, renderer);
153
+ this.#repairOverlay(host, existing);
150
154
  existing.count += 1;
151
155
  existing.contributions.push(contribution);
152
156
  return;
153
157
  }
154
158
  const state = {
155
- overlay: this.#createOverlay(renderer),
159
+ overlay: this.#createOverlay(),
156
160
  previousAriaBusy: host.getAttribute('aria-busy'),
157
161
  contributions: [contribution],
158
162
  count: 1,
159
163
  };
160
- renderer.setAttribute(host, 'aria-busy', 'true');
161
- renderer.appendChild(host, state.overlay);
164
+ host.setAttribute('aria-busy', 'true');
165
+ host.appendChild(state.overlay);
162
166
  documentRecord.hostStates.set(host, state);
163
167
  documentRecord.liveHosts.add(host);
164
168
  }
165
- #repairOverlay(host, state, renderer) {
166
- const currentParent = state.overlay.parentNode;
167
- if (currentParent !== host) {
168
- if (currentParent)
169
- renderer.removeChild(currentParent, state.overlay);
170
- renderer.appendChild(host, state.overlay);
171
- }
169
+ #repairOverlay(host, state) {
170
+ // why: appendChild tự động rút overlay khỏi parent cũ, nên không cần removeChild riêng.
171
+ if (state.overlay.parentNode !== host)
172
+ host.appendChild(state.overlay);
172
173
  if (host.getAttribute('aria-busy') !== 'true')
173
- renderer.setAttribute(host, 'aria-busy', 'true');
174
+ host.setAttribute('aria-busy', 'true');
174
175
  }
175
- #releaseContribution(documentRecord, contribution, renderer) {
176
+ #releaseContribution(documentRecord, contribution) {
176
177
  if (contribution.released)
177
178
  return;
178
179
  contribution.released = true;
@@ -183,25 +184,23 @@ class SdLoadingService {
183
184
  state.contributions.splice(contributionIndex, 1);
184
185
  state.count -= 1;
185
186
  if (state.count <= 0)
186
- this.#removeHost(documentRecord, contribution.host, state, renderer);
187
+ this.#removeHost(documentRecord, contribution.host, state);
187
188
  }
188
189
  contribution.service.#closeOwnerWhenReleased(contribution.owner);
189
190
  }
190
- #removeHost(documentRecord, host, state, renderer) {
191
- const parent = state.overlay.parentNode;
192
- if (parent)
193
- renderer.removeChild(parent, state.overlay);
191
+ #removeHost(documentRecord, host, state) {
192
+ state.overlay.remove();
194
193
  if (host.getAttribute('aria-busy') === 'true') {
195
194
  if (state.previousAriaBusy === null)
196
- renderer.removeAttribute(host, 'aria-busy');
195
+ host.removeAttribute('aria-busy');
197
196
  else
198
- renderer.setAttribute(host, 'aria-busy', state.previousAriaBusy);
197
+ host.setAttribute('aria-busy', state.previousAriaBusy);
199
198
  }
200
199
  documentRecord.hostStates.delete(host);
201
200
  documentRecord.liveHosts.delete(host);
202
201
  this.#deleteDocumentRecordWhenEmpty(documentRecord);
203
202
  }
204
- #closeHandle(state, renderer) {
203
+ #closeHandle(state) {
205
204
  if (state.closed)
206
205
  return;
207
206
  state.closed = true;
@@ -209,7 +208,7 @@ class SdLoadingService {
209
208
  const documentRecord = _a.documentRecords.get(this.#document);
210
209
  if (documentRecord) {
211
210
  for (const contribution of [...state.contributions]) {
212
- this.#releaseContribution(documentRecord, contribution, renderer);
211
+ this.#releaseContribution(documentRecord, contribution);
213
212
  }
214
213
  }
215
214
  else {
@@ -230,32 +229,32 @@ class SdLoadingService {
230
229
  if (handleIndex >= 0)
231
230
  this.#handleQueue.splice(handleIndex, 1);
232
231
  }
233
- #createOverlay(renderer) {
234
- const container = renderer.createElement('div');
235
- const spinner = renderer.createElement('div');
236
- renderer.addClass(container, 'sd-loading');
237
- renderer.setAttribute(container, 'role', 'status');
238
- renderer.setAttribute(container, 'aria-live', 'polite');
239
- renderer.setAttribute(container, 'aria-label', 'Loading');
240
- renderer.addClass(spinner, 'sd-loading-spinner');
241
- renderer.setAttribute(spinner, 'aria-hidden', 'true');
242
- renderer.appendChild(container, spinner);
232
+ #createOverlay() {
233
+ const container = this.#document.createElement('div');
234
+ const spinner = this.#document.createElement('div');
235
+ container.classList.add('sd-loading');
236
+ container.setAttribute('role', 'status');
237
+ container.setAttribute('aria-live', 'polite');
238
+ container.setAttribute('aria-label', 'Loading');
239
+ spinner.classList.add('sd-loading-spinner');
240
+ spinner.setAttribute('aria-hidden', 'true');
241
+ container.appendChild(spinner);
243
242
  return container;
244
243
  }
245
- #ensureStyles(documentRecord, renderer) {
244
+ #ensureStyles(documentRecord) {
246
245
  let record = documentRecord.style;
247
246
  if (record && !this.#isCurrentStyle(record.element)) {
248
247
  const owners = record.owners;
249
- this.#detachOwnedStyleContent(record, renderer);
250
- record = this.#createStyleRecord(renderer, owners);
248
+ this.#detachOwnedStyleContent(record);
249
+ record = this.#createStyleRecord(owners);
251
250
  documentRecord.style = record;
252
251
  }
253
252
  else if (!record) {
254
- record = this.#createStyleRecord(renderer, new Set());
253
+ record = this.#createStyleRecord(new Set());
255
254
  documentRecord.style = record;
256
255
  }
257
256
  else {
258
- this.#ensureRequiredStyleText(record, renderer);
257
+ this.#ensureRequiredStyleText(record);
259
258
  }
260
259
  record.owners.add(this);
261
260
  this.#styleRegistered = true;
@@ -263,7 +262,7 @@ class SdLoadingService {
263
262
  #isCurrentStyle(element) {
264
263
  return element.isConnected && element.parentNode === this.#document.head && element.hasAttribute(SD_LOADING_STYLE_ATTRIBUTE);
265
264
  }
266
- #createStyleRecord(renderer, owners) {
265
+ #createStyleRecord(owners) {
267
266
  const existing = this.#document.head.querySelector(`style[${SD_LOADING_STYLE_ATTRIBUTE}]`);
268
267
  if (existing) {
269
268
  const record = {
@@ -272,17 +271,17 @@ class SdLoadingService {
272
271
  owners,
273
272
  ownedText: null,
274
273
  };
275
- this.#ensureRequiredStyleText(record, renderer);
274
+ this.#ensureRequiredStyleText(record);
276
275
  return record;
277
276
  }
278
- const style = renderer.createElement('style');
279
- const ownedText = renderer.createText(SD_LOADING_STYLES);
280
- renderer.setAttribute(style, SD_LOADING_STYLE_ATTRIBUTE, '');
281
- renderer.appendChild(style, ownedText);
282
- renderer.appendChild(this.#document.head, style);
277
+ const style = this.#document.createElement('style');
278
+ const ownedText = this.#document.createTextNode(SD_LOADING_STYLES);
279
+ style.setAttribute(SD_LOADING_STYLE_ATTRIBUTE, '');
280
+ style.appendChild(ownedText);
281
+ this.#document.head.appendChild(style);
283
282
  return { element: style, libraryOwned: true, owners, ownedText };
284
283
  }
285
- #ensureRequiredStyleText(record, renderer) {
284
+ #ensureRequiredStyleText(record) {
286
285
  const currentText = record.element.textContent ?? '';
287
286
  const hasRequiredRules = currentText.includes('.sd-loading {') &&
288
287
  currentText.includes('.sd-loading-spinner') &&
@@ -292,42 +291,34 @@ class SdLoadingService {
292
291
  if (record.ownedText && record.ownedText.parentNode !== record.element)
293
292
  record.ownedText = null;
294
293
  if (!record.ownedText) {
295
- record.ownedText = renderer.createText(SD_LOADING_STYLES);
296
- renderer.appendChild(record.element, record.ownedText);
294
+ record.ownedText = this.#document.createTextNode(SD_LOADING_STYLES);
295
+ record.element.appendChild(record.ownedText);
297
296
  }
298
297
  }
299
- #detachOwnedStyleContent(record, renderer) {
300
- if (record.libraryOwned) {
301
- const parent = record.element.parentNode;
302
- if (parent)
303
- renderer.removeChild(parent, record.element);
304
- }
305
- else if (record.ownedText) {
306
- const textParent = record.ownedText.parentNode;
307
- if (textParent)
308
- renderer.removeChild(textParent, record.ownedText);
309
- }
298
+ #detachOwnedStyleContent(record) {
299
+ if (record.libraryOwned)
300
+ record.element.remove();
301
+ else
302
+ record.ownedText?.remove();
310
303
  record.ownedText = null;
311
304
  }
312
305
  #destroy() {
313
306
  if (this.#destroyed)
314
307
  return;
315
308
  this.#destroyed = true;
316
- const renderer = this.#renderer;
317
- if (renderer) {
309
+ if (this.#isBrowser) {
318
310
  for (const state of [...this.#handleQueue])
319
- this.#closeHandle(state, renderer);
320
- this.#unregisterStyles(renderer);
311
+ this.#closeHandle(state);
312
+ this.#unregisterStyles();
313
+ return;
321
314
  }
322
- else {
323
- for (const state of this.#handleQueue) {
324
- state.closed = true;
325
- state.contributions.length = 0;
326
- }
327
- this.#handleQueue.length = 0;
315
+ for (const state of this.#handleQueue) {
316
+ state.closed = true;
317
+ state.contributions.length = 0;
328
318
  }
319
+ this.#handleQueue.length = 0;
329
320
  }
330
- #unregisterStyles(renderer) {
321
+ #unregisterStyles() {
331
322
  if (!this.#styleRegistered)
332
323
  return;
333
324
  this.#styleRegistered = false;
@@ -337,7 +328,7 @@ class SdLoadingService {
337
328
  return;
338
329
  record.owners.delete(this);
339
330
  if (record.owners.size === 0) {
340
- this.#detachOwnedStyleContent(record, renderer);
331
+ this.#detachOwnedStyleContent(record);
341
332
  documentRecord.style = undefined;
342
333
  }
343
334
  this.#deleteDocumentRecordWhenEmpty(documentRecord);
@@ -1 +1 @@
1
- {"version":3,"file":"sdcorejs-angular-services-loading.mjs","sources":["../../../projects/sdcorejs-angular/services/loading/src/loading.service.ts","../../../projects/sdcorejs-angular/services/loading/sdcorejs-angular-services-loading.ts"],"sourcesContent":["import { DOCUMENT, isPlatformBrowser } from '@angular/common';\nimport { DestroyRef, Injectable, PLATFORM_ID, Renderer2, RendererFactory2, inject } from '@angular/core';\n\nexport interface SdLoadingRef {\n readonly closed: boolean;\n close(): void;\n}\n\ninterface SdLoadingDocumentRecord {\n readonly hostStates: WeakMap<Element, SdLoadingHostState>;\n readonly liveHosts: Set<Element>;\n style: SdLoadingStyleRecord | undefined;\n}\n\ninterface SdLoadingHostState {\n readonly overlay: HTMLElement;\n readonly previousAriaBusy: string | null;\n readonly contributions: SdLoadingContribution[];\n count: number;\n}\n\ninterface SdLoadingHandleState {\n readonly selector: string;\n readonly contributions: SdLoadingContribution[];\n closed: boolean;\n}\n\ninterface SdLoadingContribution {\n readonly host: Element;\n readonly owner: SdLoadingHandleState;\n readonly service: SdLoadingService;\n released: boolean;\n}\n\ninterface SdLoadingStyleRecord {\n readonly element: HTMLStyleElement;\n readonly libraryOwned: boolean;\n readonly owners: Set<SdLoadingService>;\n ownedText: Text | null;\n}\n\nconst SD_LOADING_STYLE_ATTRIBUTE = 'data-sd-loading-styles';\nconst SD_LOADING_STYLES = `\n.sd-loading {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n opacity: 0.6;\n background: #fff;\n z-index: 99999;\n}\n\n.sd-loading-spinner {\n position: absolute;\n top: calc(50% - 2.5rem);\n left: calc(50% - 2.5rem);\n width: 5rem;\n height: 5rem;\n border: 0.5rem solid var(--sd-primary);\n border-top-color: var(--sd-border);\n border-radius: 50%;\n animation: sd-loading-spin 1s linear infinite;\n}\n\n@keyframes sd-loading-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n}\n`;\n\nconst CLOSED_LOADING_REF: SdLoadingRef = Object.freeze({\n closed: true,\n close(): void {\n // A closed ref deliberately has no work to release.\n },\n});\n\n@Injectable({\n providedIn: 'root',\n})\nexport class SdLoadingService {\n private static readonly documentRecords = new WeakMap<Document, SdLoadingDocumentRecord>();\n\n readonly #document: Document;\n readonly #renderer: Renderer2 | null;\n readonly #isBrowser: boolean;\n readonly #destroyRef = inject(DestroyRef);\n readonly #handleQueue: SdLoadingHandleState[] = [];\n\n #styleRegistered = false;\n #destroyed = false;\n\n /** Inserted by Angular inject() migration for backwards compatibility. */\n constructor(...args: unknown[]);\n constructor() {\n this.#document = inject(DOCUMENT);\n this.#isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n const rendererFactory = inject(RendererFactory2);\n this.#renderer = this.#isBrowser ? rendererFactory.createRenderer(null, null) : null;\n this.#destroyRef.onDestroy(() => this.#destroy());\n }\n\n /** Starts loading for every current selector match and returns ownership of those exact hosts. */\n start = (selector = 'body'): SdLoadingRef => {\n const renderer = this.#renderer;\n if (this.#destroyed || !this.#isBrowser || !renderer) return CLOSED_LOADING_REF;\n\n const hosts = Array.from(this.#document.querySelectorAll(selector));\n if (hosts.length === 0) return CLOSED_LOADING_REF;\n\n const documentRecord = this.#getOrCreateDocumentRecord();\n this.#ensureStyles(documentRecord, renderer);\n const state: SdLoadingHandleState = { selector, contributions: [], closed: false };\n for (const host of hosts) {\n const contribution: SdLoadingContribution = {\n host,\n owner: state,\n service: this,\n released: false,\n };\n state.contributions.push(contribution);\n this.#acquireHost(documentRecord, contribution, renderer);\n }\n this.#handleQueue.push(state);\n\n return {\n get closed(): boolean {\n return state.closed;\n },\n close: () => this.#closeHandle(state, renderer),\n };\n };\n\n /** Returns the first matched globally loading host, `false` for idle matches, or `null` when unavailable. */\n isLoading = (selector = 'body'): Element | false | null => {\n if (this.#destroyed || !this.#isBrowser) return null;\n\n const hosts = this.#document.querySelectorAll(selector);\n if (hosts.length === 0) return null;\n\n const documentRecord = SdLoadingService.documentRecords.get(this.#document);\n for (const host of Array.from(hosts)) {\n const state = documentRecord?.hostStates.get(host);\n if (state && state.count > 0 && state.overlay.parentNode === host) return host;\n }\n return false;\n };\n\n /** Releases the oldest exact-selector start, with a current-host fallback for legacy cross-selector calls. */\n stop = (selector = 'body'): void => {\n const renderer = this.#renderer;\n if (this.#destroyed || !this.#isBrowser || !renderer) return;\n\n const exactHandle = this.#handleQueue.find(state => !state.closed && state.selector === selector);\n if (exactHandle) {\n this.#closeHandle(exactHandle, renderer);\n return;\n }\n\n const documentRecord = SdLoadingService.documentRecords.get(this.#document);\n if (!documentRecord) return;\n\n for (const host of Array.from(this.#document.querySelectorAll(selector))) {\n const contribution = documentRecord.hostStates\n .get(host)\n ?.contributions.find(candidate => !candidate.released && candidate.service === this);\n if (contribution) this.#releaseContribution(documentRecord, contribution, renderer);\n }\n };\n\n run<T>(task: () => T | PromiseLike<T>, selector?: string): Promise<T>;\n run<T>(task: PromiseLike<T>, selector?: string): Promise<T>;\n /** Runs a task inside a loading lifetime and always releases it without replacing the task result or error. */\n async run<T>(task: (() => T | PromiseLike<T>) | PromiseLike<T>, selector = 'body'): Promise<T> {\n const ref = this.start(selector);\n try {\n const result = typeof task === 'function' ? task() : task;\n return await result;\n } finally {\n ref.close();\n }\n }\n\n #getOrCreateDocumentRecord(): SdLoadingDocumentRecord {\n let record = SdLoadingService.documentRecords.get(this.#document);\n if (!record) {\n record = {\n hostStates: new WeakMap<Element, SdLoadingHostState>(),\n liveHosts: new Set<Element>(),\n style: undefined,\n };\n SdLoadingService.documentRecords.set(this.#document, record);\n }\n return record;\n }\n\n #acquireHost(documentRecord: SdLoadingDocumentRecord, contribution: SdLoadingContribution, renderer: Renderer2): void {\n const host = contribution.host;\n const existing = documentRecord.hostStates.get(host);\n if (existing) {\n this.#repairOverlay(host, existing, renderer);\n existing.count += 1;\n existing.contributions.push(contribution);\n return;\n }\n\n const state: SdLoadingHostState = {\n overlay: this.#createOverlay(renderer),\n previousAriaBusy: host.getAttribute('aria-busy'),\n contributions: [contribution],\n count: 1,\n };\n renderer.setAttribute(host, 'aria-busy', 'true');\n renderer.appendChild(host, state.overlay);\n documentRecord.hostStates.set(host, state);\n documentRecord.liveHosts.add(host);\n }\n\n #repairOverlay(host: Element, state: SdLoadingHostState, renderer: Renderer2): void {\n const currentParent = state.overlay.parentNode;\n if (currentParent !== host) {\n if (currentParent) renderer.removeChild(currentParent, state.overlay);\n renderer.appendChild(host, state.overlay);\n }\n if (host.getAttribute('aria-busy') !== 'true') renderer.setAttribute(host, 'aria-busy', 'true');\n }\n\n #releaseContribution(documentRecord: SdLoadingDocumentRecord, contribution: SdLoadingContribution, renderer: Renderer2): void {\n if (contribution.released) return;\n contribution.released = true;\n\n const state = documentRecord.hostStates.get(contribution.host);\n if (state) {\n const contributionIndex = state.contributions.indexOf(contribution);\n if (contributionIndex >= 0) state.contributions.splice(contributionIndex, 1);\n state.count -= 1;\n\n if (state.count <= 0) this.#removeHost(documentRecord, contribution.host, state, renderer);\n }\n\n contribution.service.#closeOwnerWhenReleased(contribution.owner);\n }\n\n #removeHost(documentRecord: SdLoadingDocumentRecord, host: Element, state: SdLoadingHostState, renderer: Renderer2): void {\n const parent = state.overlay.parentNode;\n if (parent) renderer.removeChild(parent, state.overlay);\n\n if (host.getAttribute('aria-busy') === 'true') {\n if (state.previousAriaBusy === null) renderer.removeAttribute(host, 'aria-busy');\n else renderer.setAttribute(host, 'aria-busy', state.previousAriaBusy);\n }\n\n documentRecord.hostStates.delete(host);\n documentRecord.liveHosts.delete(host);\n this.#deleteDocumentRecordWhenEmpty(documentRecord);\n }\n\n #closeHandle(state: SdLoadingHandleState, renderer: Renderer2): void {\n if (state.closed) return;\n\n state.closed = true;\n this.#dequeueHandle(state);\n const documentRecord = SdLoadingService.documentRecords.get(this.#document);\n if (documentRecord) {\n for (const contribution of [...state.contributions]) {\n this.#releaseContribution(documentRecord, contribution, renderer);\n }\n } else {\n for (const contribution of state.contributions) contribution.released = true;\n }\n state.contributions.length = 0;\n }\n\n #closeOwnerWhenReleased(state: SdLoadingHandleState): void {\n if (state.closed || state.contributions.some(contribution => !contribution.released)) return;\n\n state.closed = true;\n this.#dequeueHandle(state);\n state.contributions.length = 0;\n }\n\n #dequeueHandle(state: SdLoadingHandleState): void {\n const handleIndex = this.#handleQueue.indexOf(state);\n if (handleIndex >= 0) this.#handleQueue.splice(handleIndex, 1);\n }\n\n #createOverlay(renderer: Renderer2): HTMLElement {\n const container: HTMLElement = renderer.createElement('div');\n const spinner: HTMLElement = renderer.createElement('div');\n\n renderer.addClass(container, 'sd-loading');\n renderer.setAttribute(container, 'role', 'status');\n renderer.setAttribute(container, 'aria-live', 'polite');\n renderer.setAttribute(container, 'aria-label', 'Loading');\n\n renderer.addClass(spinner, 'sd-loading-spinner');\n renderer.setAttribute(spinner, 'aria-hidden', 'true');\n renderer.appendChild(container, spinner);\n return container;\n }\n\n #ensureStyles(documentRecord: SdLoadingDocumentRecord, renderer: Renderer2): void {\n let record = documentRecord.style;\n if (record && !this.#isCurrentStyle(record.element)) {\n const owners = record.owners;\n this.#detachOwnedStyleContent(record, renderer);\n record = this.#createStyleRecord(renderer, owners);\n documentRecord.style = record;\n } else if (!record) {\n record = this.#createStyleRecord(renderer, new Set<SdLoadingService>());\n documentRecord.style = record;\n } else {\n this.#ensureRequiredStyleText(record, renderer);\n }\n\n record.owners.add(this);\n this.#styleRegistered = true;\n }\n\n #isCurrentStyle(element: HTMLStyleElement): boolean {\n return element.isConnected && element.parentNode === this.#document.head && element.hasAttribute(SD_LOADING_STYLE_ATTRIBUTE);\n }\n\n #createStyleRecord(renderer: Renderer2, owners: Set<SdLoadingService>): SdLoadingStyleRecord {\n const existing = this.#document.head.querySelector<HTMLStyleElement>(`style[${SD_LOADING_STYLE_ATTRIBUTE}]`);\n if (existing) {\n const record: SdLoadingStyleRecord = {\n element: existing,\n libraryOwned: false,\n owners,\n ownedText: null,\n };\n this.#ensureRequiredStyleText(record, renderer);\n return record;\n }\n\n const style: HTMLStyleElement = renderer.createElement('style');\n const ownedText: Text = renderer.createText(SD_LOADING_STYLES);\n renderer.setAttribute(style, SD_LOADING_STYLE_ATTRIBUTE, '');\n renderer.appendChild(style, ownedText);\n renderer.appendChild(this.#document.head, style);\n return { element: style, libraryOwned: true, owners, ownedText };\n }\n\n #ensureRequiredStyleText(record: SdLoadingStyleRecord, renderer: Renderer2): void {\n const currentText = record.element.textContent ?? '';\n const hasRequiredRules =\n currentText.includes('.sd-loading {') &&\n currentText.includes('.sd-loading-spinner') &&\n currentText.includes('@keyframes sd-loading-spin');\n if (hasRequiredRules) return;\n\n if (record.ownedText && record.ownedText.parentNode !== record.element) record.ownedText = null;\n if (!record.ownedText) {\n record.ownedText = renderer.createText(SD_LOADING_STYLES);\n renderer.appendChild(record.element, record.ownedText);\n }\n }\n\n #detachOwnedStyleContent(record: SdLoadingStyleRecord, renderer: Renderer2): void {\n if (record.libraryOwned) {\n const parent = record.element.parentNode;\n if (parent) renderer.removeChild(parent, record.element);\n } else if (record.ownedText) {\n const textParent = record.ownedText.parentNode;\n if (textParent) renderer.removeChild(textParent, record.ownedText);\n }\n record.ownedText = null;\n }\n\n #destroy(): void {\n if (this.#destroyed) return;\n this.#destroyed = true;\n\n const renderer = this.#renderer;\n if (renderer) {\n for (const state of [...this.#handleQueue]) this.#closeHandle(state, renderer);\n this.#unregisterStyles(renderer);\n } else {\n for (const state of this.#handleQueue) {\n state.closed = true;\n state.contributions.length = 0;\n }\n this.#handleQueue.length = 0;\n }\n }\n\n #unregisterStyles(renderer: Renderer2): void {\n if (!this.#styleRegistered) return;\n this.#styleRegistered = false;\n\n const documentRecord = SdLoadingService.documentRecords.get(this.#document);\n const record = documentRecord?.style;\n if (!documentRecord || !record) return;\n\n record.owners.delete(this);\n if (record.owners.size === 0) {\n this.#detachOwnedStyleContent(record, renderer);\n documentRecord.style = undefined;\n }\n this.#deleteDocumentRecordWhenEmpty(documentRecord);\n }\n\n #deleteDocumentRecordWhenEmpty(documentRecord: SdLoadingDocumentRecord): void {\n if (documentRecord.liveHosts.size === 0 && !documentRecord.style) {\n SdLoadingService.documentRecords.delete(this.#document);\n }\n }\n}\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-services-loading.mjs","sources":["../../../projects/sdcorejs-angular/services/loading/src/loading.service.ts","../../../projects/sdcorejs-angular/services/loading/sdcorejs-angular-services-loading.ts"],"sourcesContent":["import { DOCUMENT, isPlatformBrowser } from '@angular/common';\nimport { DestroyRef, Injectable, PLATFORM_ID, inject } from '@angular/core';\n\nexport interface SdLoadingRef {\n readonly closed: boolean;\n close(): void;\n}\n\ninterface SdLoadingDocumentRecord {\n readonly hostStates: WeakMap<Element, SdLoadingHostState>;\n readonly liveHosts: Set<Element>;\n style: SdLoadingStyleRecord | undefined;\n}\n\ninterface SdLoadingHostState {\n readonly overlay: HTMLElement;\n readonly previousAriaBusy: string | null;\n readonly contributions: SdLoadingContribution[];\n count: number;\n}\n\ninterface SdLoadingHandleState {\n readonly selector: string;\n readonly contributions: SdLoadingContribution[];\n closed: boolean;\n}\n\ninterface SdLoadingContribution {\n readonly host: Element;\n readonly owner: SdLoadingHandleState;\n readonly service: SdLoadingService;\n released: boolean;\n}\n\ninterface SdLoadingStyleRecord {\n readonly element: HTMLStyleElement;\n readonly libraryOwned: boolean;\n readonly owners: Set<SdLoadingService>;\n ownedText: Text | null;\n}\n\nconst SD_LOADING_STYLE_ATTRIBUTE = 'data-sd-loading-styles';\nconst SD_LOADING_STYLES = `\n.sd-loading {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n opacity: 0.6;\n background: #fff;\n z-index: 99999;\n}\n\n.sd-loading-spinner {\n position: absolute;\n top: calc(50% - 2.5rem);\n left: calc(50% - 2.5rem);\n width: 5rem;\n height: 5rem;\n border: 0.5rem solid var(--sd-primary);\n border-top-color: var(--sd-border);\n border-radius: 50%;\n animation: sd-loading-spin 1s linear infinite;\n}\n\n@keyframes sd-loading-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n}\n`;\n\nconst CLOSED_LOADING_REF: SdLoadingRef = Object.freeze({\n closed: true,\n close(): void {\n // A closed ref deliberately has no work to release.\n },\n});\n\n/*\n * why: service này cố tình KHÔNG dùng Renderer2. Khi app bật animations, `RendererFactory2` trả về\n * `AnimationRendererFactory` và `Renderer2.removeChild()` chỉ đẩy element vào hàng đợi\n * `collectedLeaveElements` của animation engine — DOM chỉ thật sự bị gỡ ở lần flush kế tiếp (sau một\n * chu kỳ change detection). Overlay và `style[data-sd-loading-styles]` ở đây là DOM ngoài view, được\n * gỡ lúc destroy injector hoặc từ ngữ cảnh async ngoài Angular, tức không còn flush nào chạy sau đó →\n * node nằm lại trong document vĩnh viễn (overlay z-index 99999 khoá cả trang). Ghi DOM trực tiếp trên\n * `DOCUMENT` giữ append/remove đối xứng và đồng bộ; nhánh SSR đã được `isPlatformBrowser` chặn sẵn.\n */\n@Injectable({\n providedIn: 'root',\n})\nexport class SdLoadingService {\n private static readonly documentRecords = new WeakMap<Document, SdLoadingDocumentRecord>();\n\n readonly #document: Document;\n readonly #isBrowser: boolean;\n readonly #destroyRef = inject(DestroyRef);\n readonly #handleQueue: SdLoadingHandleState[] = [];\n\n #styleRegistered = false;\n #destroyed = false;\n\n /** Inserted by Angular inject() migration for backwards compatibility. */\n constructor(...args: unknown[]);\n constructor() {\n this.#document = inject(DOCUMENT);\n this.#isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n this.#destroyRef.onDestroy(() => this.#destroy());\n }\n\n /** Starts loading for every current selector match and returns ownership of those exact hosts. */\n start = (selector = 'body'): SdLoadingRef => {\n if (this.#destroyed || !this.#isBrowser) return CLOSED_LOADING_REF;\n\n const hosts = Array.from(this.#document.querySelectorAll(selector));\n if (hosts.length === 0) return CLOSED_LOADING_REF;\n\n const documentRecord = this.#getOrCreateDocumentRecord();\n this.#ensureStyles(documentRecord);\n const state: SdLoadingHandleState = { selector, contributions: [], closed: false };\n for (const host of hosts) {\n const contribution: SdLoadingContribution = {\n host,\n owner: state,\n service: this,\n released: false,\n };\n state.contributions.push(contribution);\n this.#acquireHost(documentRecord, contribution);\n }\n this.#handleQueue.push(state);\n\n return {\n get closed(): boolean {\n return state.closed;\n },\n close: () => this.#closeHandle(state),\n };\n };\n\n /** Returns the first matched globally loading host, `false` for idle matches, or `null` when unavailable. */\n isLoading = (selector = 'body'): Element | false | null => {\n if (this.#destroyed || !this.#isBrowser) return null;\n\n const hosts = this.#document.querySelectorAll(selector);\n if (hosts.length === 0) return null;\n\n const documentRecord = SdLoadingService.documentRecords.get(this.#document);\n for (const host of Array.from(hosts)) {\n const state = documentRecord?.hostStates.get(host);\n if (state && state.count > 0 && state.overlay.parentNode === host) return host;\n }\n return false;\n };\n\n /** Releases the oldest exact-selector start, with a current-host fallback for legacy cross-selector calls. */\n stop = (selector = 'body'): void => {\n if (this.#destroyed || !this.#isBrowser) return;\n\n const exactHandle = this.#handleQueue.find(state => !state.closed && state.selector === selector);\n if (exactHandle) {\n this.#closeHandle(exactHandle);\n return;\n }\n\n const documentRecord = SdLoadingService.documentRecords.get(this.#document);\n if (!documentRecord) return;\n\n for (const host of Array.from(this.#document.querySelectorAll(selector))) {\n const contribution = documentRecord.hostStates\n .get(host)\n ?.contributions.find(candidate => !candidate.released && candidate.service === this);\n if (contribution) this.#releaseContribution(documentRecord, contribution);\n }\n };\n\n run<T>(task: () => T | PromiseLike<T>, selector?: string): Promise<T>;\n run<T>(task: PromiseLike<T>, selector?: string): Promise<T>;\n /** Runs a task inside a loading lifetime and always releases it without replacing the task result or error. */\n async run<T>(task: (() => T | PromiseLike<T>) | PromiseLike<T>, selector = 'body'): Promise<T> {\n const ref = this.start(selector);\n try {\n const result = typeof task === 'function' ? task() : task;\n return await result;\n } finally {\n ref.close();\n }\n }\n\n #getOrCreateDocumentRecord(): SdLoadingDocumentRecord {\n let record = SdLoadingService.documentRecords.get(this.#document);\n if (!record) {\n record = {\n hostStates: new WeakMap<Element, SdLoadingHostState>(),\n liveHosts: new Set<Element>(),\n style: undefined,\n };\n SdLoadingService.documentRecords.set(this.#document, record);\n }\n return record;\n }\n\n #acquireHost(documentRecord: SdLoadingDocumentRecord, contribution: SdLoadingContribution): void {\n const host = contribution.host;\n const existing = documentRecord.hostStates.get(host);\n if (existing) {\n this.#repairOverlay(host, existing);\n existing.count += 1;\n existing.contributions.push(contribution);\n return;\n }\n\n const state: SdLoadingHostState = {\n overlay: this.#createOverlay(),\n previousAriaBusy: host.getAttribute('aria-busy'),\n contributions: [contribution],\n count: 1,\n };\n host.setAttribute('aria-busy', 'true');\n host.appendChild(state.overlay);\n documentRecord.hostStates.set(host, state);\n documentRecord.liveHosts.add(host);\n }\n\n #repairOverlay(host: Element, state: SdLoadingHostState): void {\n // why: appendChild tự động rút overlay khỏi parent cũ, nên không cần removeChild riêng.\n if (state.overlay.parentNode !== host) host.appendChild(state.overlay);\n if (host.getAttribute('aria-busy') !== 'true') host.setAttribute('aria-busy', 'true');\n }\n\n #releaseContribution(documentRecord: SdLoadingDocumentRecord, contribution: SdLoadingContribution): void {\n if (contribution.released) return;\n contribution.released = true;\n\n const state = documentRecord.hostStates.get(contribution.host);\n if (state) {\n const contributionIndex = state.contributions.indexOf(contribution);\n if (contributionIndex >= 0) state.contributions.splice(contributionIndex, 1);\n state.count -= 1;\n\n if (state.count <= 0) this.#removeHost(documentRecord, contribution.host, state);\n }\n\n contribution.service.#closeOwnerWhenReleased(contribution.owner);\n }\n\n #removeHost(documentRecord: SdLoadingDocumentRecord, host: Element, state: SdLoadingHostState): void {\n state.overlay.remove();\n\n if (host.getAttribute('aria-busy') === 'true') {\n if (state.previousAriaBusy === null) host.removeAttribute('aria-busy');\n else host.setAttribute('aria-busy', state.previousAriaBusy);\n }\n\n documentRecord.hostStates.delete(host);\n documentRecord.liveHosts.delete(host);\n this.#deleteDocumentRecordWhenEmpty(documentRecord);\n }\n\n #closeHandle(state: SdLoadingHandleState): void {\n if (state.closed) return;\n\n state.closed = true;\n this.#dequeueHandle(state);\n const documentRecord = SdLoadingService.documentRecords.get(this.#document);\n if (documentRecord) {\n for (const contribution of [...state.contributions]) {\n this.#releaseContribution(documentRecord, contribution);\n }\n } else {\n for (const contribution of state.contributions) contribution.released = true;\n }\n state.contributions.length = 0;\n }\n\n #closeOwnerWhenReleased(state: SdLoadingHandleState): void {\n if (state.closed || state.contributions.some(contribution => !contribution.released)) return;\n\n state.closed = true;\n this.#dequeueHandle(state);\n state.contributions.length = 0;\n }\n\n #dequeueHandle(state: SdLoadingHandleState): void {\n const handleIndex = this.#handleQueue.indexOf(state);\n if (handleIndex >= 0) this.#handleQueue.splice(handleIndex, 1);\n }\n\n #createOverlay(): HTMLElement {\n const container = this.#document.createElement('div');\n const spinner = this.#document.createElement('div');\n\n container.classList.add('sd-loading');\n container.setAttribute('role', 'status');\n container.setAttribute('aria-live', 'polite');\n container.setAttribute('aria-label', 'Loading');\n\n spinner.classList.add('sd-loading-spinner');\n spinner.setAttribute('aria-hidden', 'true');\n container.appendChild(spinner);\n return container;\n }\n\n #ensureStyles(documentRecord: SdLoadingDocumentRecord): void {\n let record = documentRecord.style;\n if (record && !this.#isCurrentStyle(record.element)) {\n const owners = record.owners;\n this.#detachOwnedStyleContent(record);\n record = this.#createStyleRecord(owners);\n documentRecord.style = record;\n } else if (!record) {\n record = this.#createStyleRecord(new Set<SdLoadingService>());\n documentRecord.style = record;\n } else {\n this.#ensureRequiredStyleText(record);\n }\n\n record.owners.add(this);\n this.#styleRegistered = true;\n }\n\n #isCurrentStyle(element: HTMLStyleElement): boolean {\n return element.isConnected && element.parentNode === this.#document.head && element.hasAttribute(SD_LOADING_STYLE_ATTRIBUTE);\n }\n\n #createStyleRecord(owners: Set<SdLoadingService>): SdLoadingStyleRecord {\n const existing = this.#document.head.querySelector<HTMLStyleElement>(`style[${SD_LOADING_STYLE_ATTRIBUTE}]`);\n if (existing) {\n const record: SdLoadingStyleRecord = {\n element: existing,\n libraryOwned: false,\n owners,\n ownedText: null,\n };\n this.#ensureRequiredStyleText(record);\n return record;\n }\n\n const style = this.#document.createElement('style');\n const ownedText = this.#document.createTextNode(SD_LOADING_STYLES);\n style.setAttribute(SD_LOADING_STYLE_ATTRIBUTE, '');\n style.appendChild(ownedText);\n this.#document.head.appendChild(style);\n return { element: style, libraryOwned: true, owners, ownedText };\n }\n\n #ensureRequiredStyleText(record: SdLoadingStyleRecord): void {\n const currentText = record.element.textContent ?? '';\n const hasRequiredRules =\n currentText.includes('.sd-loading {') &&\n currentText.includes('.sd-loading-spinner') &&\n currentText.includes('@keyframes sd-loading-spin');\n if (hasRequiredRules) return;\n\n if (record.ownedText && record.ownedText.parentNode !== record.element) record.ownedText = null;\n if (!record.ownedText) {\n record.ownedText = this.#document.createTextNode(SD_LOADING_STYLES);\n record.element.appendChild(record.ownedText);\n }\n }\n\n #detachOwnedStyleContent(record: SdLoadingStyleRecord): void {\n if (record.libraryOwned) record.element.remove();\n else record.ownedText?.remove();\n record.ownedText = null;\n }\n\n #destroy(): void {\n if (this.#destroyed) return;\n this.#destroyed = true;\n\n if (this.#isBrowser) {\n for (const state of [...this.#handleQueue]) this.#closeHandle(state);\n this.#unregisterStyles();\n return;\n }\n\n for (const state of this.#handleQueue) {\n state.closed = true;\n state.contributions.length = 0;\n }\n this.#handleQueue.length = 0;\n }\n\n #unregisterStyles(): void {\n if (!this.#styleRegistered) return;\n this.#styleRegistered = false;\n\n const documentRecord = SdLoadingService.documentRecords.get(this.#document);\n const record = documentRecord?.style;\n if (!documentRecord || !record) return;\n\n record.owners.delete(this);\n if (record.owners.size === 0) {\n this.#detachOwnedStyleContent(record);\n documentRecord.style = undefined;\n }\n this.#deleteDocumentRecordWhenEmpty(documentRecord);\n }\n\n #deleteDocumentRecordWhenEmpty(documentRecord: SdLoadingDocumentRecord): void {\n if (documentRecord.liveHosts.size === 0 && !documentRecord.style) {\n SdLoadingService.documentRecords.delete(this.#document);\n }\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { CommonModule, DOCUMENT } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
- import { inject, signal, computed, SecurityContext, HostListener, Input, ChangeDetectionStrategy, Component, ApplicationRef, EnvironmentInjector, createComponent, Injectable } from '@angular/core';
3
+ import { inject, signal, computed, SecurityContext, HostListener, Input, ChangeDetectionStrategy, Component, ApplicationRef, EnvironmentInjector, DestroyRef, createComponent, Injectable } from '@angular/core';
4
4
  import { Utilities } from '@sdcorejs/utils/fns';
5
- import { TranslatePipe, I18nService } from '@sdcorejs/angular/i18n';
5
+ import { SdTranslatePipe, I18nService } from '@sdcorejs/angular/i18n';
6
6
  import { DomSanitizer } from '@angular/platform-browser';
7
7
 
8
8
  const TOAST_EXIT_ANIMATION_MS = 200;
@@ -106,17 +106,17 @@ class ToastComponent {
106
106
  this.closeTimer = null;
107
107
  }
108
108
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ToastComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
109
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: ToastComponent, isStandalone: true, selector: "toast", inputs: { data: "data" }, host: { listeners: { "mouseenter": "pauseTimer()", "mouseleave": "resumeTimer()" }, properties: { "class": "hostClasses()", "attr.data-autoid": "autoId", "attr.data-type": "data.type", "attr.data-title": "data.title ?? null", "attr.data-message": "dataMessage" } }, ngImport: i0, template: "<div class=\"sd-toast__bar\" [class]=\"'bg-' + data.type\"></div>\n\n<div class=\"sd-toast__icon\" [class]=\"'text-' + data.type\">\n @switch (data.type) {\n @case ('success') {\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z\"/></svg>\n }\n @case ('info') {\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z\"/></svg>\n }\n @case ('warning') {\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z\"/></svg>\n }\n @case ('error') {\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\"/></svg>\n }\n }\n</div>\n\n<div class=\"sd-toast__content\">\n <div class=\"sd-toast__header\">\n <strong class=\"sd-toast__title\">{{ data.title || ('core.notify.type.' + data.type | translate) }}</strong>\n <button class=\"sd-toast__close\" (click)=\"close()\">&times;</button>\n </div>\n\n <div class=\"sd-toast__body\">\n @if (!isMultiMessage) {\n <!-- Text rendering is the default safe path; HTML is opt-in and sanitized. -->\n @if (data.html) {\n <div [innerHTML]=\"sanitizeHtml(singleMessage)\"></div>\n } @else {\n <div>{{ singleMessage }}</div>\n }\n } @else {\n <ul class=\"msg-list\">\n @for (msg of displayMessages; track msg) {\n @if (data.html) {\n <li [innerHTML]=\"sanitizeHtml('- ' + msg)\"></li>\n } @else {\n <li>{{ '- ' + msg }}</li>\n }\n }\n </ul>\n\n @if (hasMore) {\n <button\n type=\"button\"\n class=\"toggle-more btn-link\"\n [class]=\"'text-' + data.type\"\n (click)=\"toggleExpand()\">\n {{ isExpanded() ? ('core.notify.show-less' | translate) : ('core.notify.show-more' | translate: { count: restCount }) }}\n </button>\n }\n }\n </div>\n\n @if (data.actionLabel) {\n <div class=\"sd-toast__actions\">\n <button\n class=\"btn-action\"\n [class]=\"'bg-' + data.type + '-light text-' + data.type\"\n (click)=\"onActionClick()\">\n {{ data.actionLabel }}\n </button>\n </div>\n }\n</div>\n", styles: [":host{display:flex;width:350px;border-radius:8px;box-shadow:0 4px 12px #00000026;margin-bottom:12px;overflow:hidden;position:relative;font-family:Roboto,sans-serif;pointer-events:auto;animation:sd-toast-enter .3s ease-out}:host(.sd-toast--closing){pointer-events:none;animation:sd-toast-leave .2s ease-in forwards}@keyframes sd-toast-enter{0%{transform:translate(100%);opacity:0}to{transform:translate(0);opacity:1}}@keyframes sd-toast-leave{0%{transform:translate(0);opacity:1}to{transform:translate(100%);opacity:0}}@media(prefers-reduced-motion:reduce){:host,:host(.sd-toast--closing){animation:none}}.sd-toast__bar{width:6px;flex-shrink:0}.sd-toast__icon{padding:12px 0 0 12px;flex-shrink:0}.sd-toast__icon svg{width:24px;height:24px}.sd-toast__content{flex-grow:1;padding:12px;display:flex;flex-direction:column}.sd-toast__header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:4px}.sd-toast__title{font-size:16px;font-weight:600}.sd-toast__close{background:none;border:none;font-size:16px;cursor:pointer;line-height:1;padding:0;opacity:.5;transition:opacity .2s}.sd-toast__close:hover{opacity:1}.sd-toast__body{font-size:14px;line-height:1.4;word-break:break-word}.sd-toast__actions{display:flex;justify-content:flex-end;margin-top:8px}.msg-list{padding-left:0;list-style:none;margin:0}.toggle-more{background:none;border:none;padding:0;font:inherit;cursor:pointer;font-size:13px;margin-top:4px;font-weight:500;text-align:left}.toggle-more:hover{text-decoration:underline}.toggle-more:focus-visible{outline:2px solid currentColor;outline-offset:2px}.btn-action{border:none;padding:6px 12px;border-radius:4px;font-weight:600;cursor:pointer;font-size:13px;transition:filter .2s}.btn-action:hover{filter:brightness(.95)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
109
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: ToastComponent, isStandalone: true, selector: "toast", inputs: { data: "data" }, host: { listeners: { "mouseenter": "pauseTimer()", "mouseleave": "resumeTimer()" }, properties: { "class": "hostClasses()", "attr.data-autoid": "autoId", "attr.data-type": "data.type", "attr.data-title": "data.title ?? null", "attr.data-message": "dataMessage" } }, ngImport: i0, template: "<div class=\"sd-toast__bar\" [class]=\"'bg-' + data.type\"></div>\n\n<div class=\"sd-toast__icon\" [class]=\"'text-' + data.type\">\n @switch (data.type) {\n @case ('success') {\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z\"/></svg>\n }\n @case ('info') {\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z\"/></svg>\n }\n @case ('warning') {\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z\"/></svg>\n }\n @case ('error') {\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\"/></svg>\n }\n }\n</div>\n\n<div class=\"sd-toast__content\">\n <div class=\"sd-toast__header\">\n <strong class=\"sd-toast__title\">{{ data.title || ('core.notify.type.' + data.type | sdTranslate) }}</strong>\n <button class=\"sd-toast__close\" (click)=\"close()\">&times;</button>\n </div>\n\n <div class=\"sd-toast__body\">\n @if (!isMultiMessage) {\n <!-- Text rendering is the default safe path; HTML is opt-in and sanitized. -->\n @if (data.html) {\n <div [innerHTML]=\"sanitizeHtml(singleMessage)\"></div>\n } @else {\n <div>{{ singleMessage }}</div>\n }\n } @else {\n <ul class=\"msg-list\">\n @for (msg of displayMessages; track msg) {\n @if (data.html) {\n <li [innerHTML]=\"sanitizeHtml('- ' + msg)\"></li>\n } @else {\n <li>{{ '- ' + msg }}</li>\n }\n }\n </ul>\n\n @if (hasMore) {\n <button\n type=\"button\"\n class=\"toggle-more btn-link\"\n [class]=\"'text-' + data.type\"\n (click)=\"toggleExpand()\">\n {{ isExpanded() ? ('core.notify.show-less' | sdTranslate) : ('core.notify.show-more' | sdTranslate: { count: restCount }) }}\n </button>\n }\n }\n </div>\n\n @if (data.actionLabel) {\n <div class=\"sd-toast__actions\">\n <button\n class=\"btn-action\"\n [class]=\"'bg-' + data.type + '-light text-' + data.type\"\n (click)=\"onActionClick()\">\n {{ data.actionLabel }}\n </button>\n </div>\n }\n</div>\n", styles: [":host{display:flex;width:350px;border-radius:8px;box-shadow:0 4px 12px #00000026;margin-bottom:12px;overflow:hidden;position:relative;font-family:Roboto,sans-serif;pointer-events:auto;animation:sd-toast-enter .3s ease-out}:host(.sd-toast--closing){pointer-events:none;animation:sd-toast-leave .2s ease-in forwards}@keyframes sd-toast-enter{0%{transform:translate(100%);opacity:0}to{transform:translate(0);opacity:1}}@keyframes sd-toast-leave{0%{transform:translate(0);opacity:1}to{transform:translate(100%);opacity:0}}@media(prefers-reduced-motion:reduce){:host,:host(.sd-toast--closing){animation:none}}.sd-toast__bar{width:6px;flex-shrink:0}.sd-toast__icon{padding:12px 0 0 12px;flex-shrink:0}.sd-toast__icon svg{width:24px;height:24px}.sd-toast__content{flex-grow:1;padding:12px;display:flex;flex-direction:column}.sd-toast__header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:4px}.sd-toast__title{font-size:16px;font-weight:600}.sd-toast__close{background:none;border:none;font-size:16px;cursor:pointer;line-height:1;padding:0;opacity:.5;transition:opacity .2s}.sd-toast__close:hover{opacity:1}.sd-toast__body{font-size:14px;line-height:1.4;word-break:break-word}.sd-toast__actions{display:flex;justify-content:flex-end;margin-top:8px}.msg-list{padding-left:0;list-style:none;margin:0}.toggle-more{background:none;border:none;padding:0;font:inherit;cursor:pointer;font-size:13px;margin-top:4px;font-weight:500;text-align:left}.toggle-more:hover{text-decoration:underline}.toggle-more:focus-visible{outline:2px solid currentColor;outline-offset:2px}.btn-action{border:none;padding:6px 12px;border-radius:4px;font-weight:600;cursor:pointer;font-size:13px;transition:filter .2s}.btn-action:hover{filter:brightness(.95)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: SdTranslatePipe, name: "sdTranslate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
110
110
  }
111
111
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ToastComponent, decorators: [{
112
112
  type: Component,
113
- args: [{ selector: 'toast', standalone: true, imports: [CommonModule, TranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, host: {
113
+ args: [{ selector: 'toast', standalone: true, imports: [CommonModule, SdTranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, host: {
114
114
  '[class]': 'hostClasses()',
115
115
  '[attr.data-autoid]': 'autoId',
116
116
  '[attr.data-type]': 'data.type',
117
117
  '[attr.data-title]': 'data.title ?? null',
118
118
  '[attr.data-message]': 'dataMessage',
119
- }, template: "<div class=\"sd-toast__bar\" [class]=\"'bg-' + data.type\"></div>\n\n<div class=\"sd-toast__icon\" [class]=\"'text-' + data.type\">\n @switch (data.type) {\n @case ('success') {\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z\"/></svg>\n }\n @case ('info') {\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z\"/></svg>\n }\n @case ('warning') {\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z\"/></svg>\n }\n @case ('error') {\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\"/></svg>\n }\n }\n</div>\n\n<div class=\"sd-toast__content\">\n <div class=\"sd-toast__header\">\n <strong class=\"sd-toast__title\">{{ data.title || ('core.notify.type.' + data.type | translate) }}</strong>\n <button class=\"sd-toast__close\" (click)=\"close()\">&times;</button>\n </div>\n\n <div class=\"sd-toast__body\">\n @if (!isMultiMessage) {\n <!-- Text rendering is the default safe path; HTML is opt-in and sanitized. -->\n @if (data.html) {\n <div [innerHTML]=\"sanitizeHtml(singleMessage)\"></div>\n } @else {\n <div>{{ singleMessage }}</div>\n }\n } @else {\n <ul class=\"msg-list\">\n @for (msg of displayMessages; track msg) {\n @if (data.html) {\n <li [innerHTML]=\"sanitizeHtml('- ' + msg)\"></li>\n } @else {\n <li>{{ '- ' + msg }}</li>\n }\n }\n </ul>\n\n @if (hasMore) {\n <button\n type=\"button\"\n class=\"toggle-more btn-link\"\n [class]=\"'text-' + data.type\"\n (click)=\"toggleExpand()\">\n {{ isExpanded() ? ('core.notify.show-less' | translate) : ('core.notify.show-more' | translate: { count: restCount }) }}\n </button>\n }\n }\n </div>\n\n @if (data.actionLabel) {\n <div class=\"sd-toast__actions\">\n <button\n class=\"btn-action\"\n [class]=\"'bg-' + data.type + '-light text-' + data.type\"\n (click)=\"onActionClick()\">\n {{ data.actionLabel }}\n </button>\n </div>\n }\n</div>\n", styles: [":host{display:flex;width:350px;border-radius:8px;box-shadow:0 4px 12px #00000026;margin-bottom:12px;overflow:hidden;position:relative;font-family:Roboto,sans-serif;pointer-events:auto;animation:sd-toast-enter .3s ease-out}:host(.sd-toast--closing){pointer-events:none;animation:sd-toast-leave .2s ease-in forwards}@keyframes sd-toast-enter{0%{transform:translate(100%);opacity:0}to{transform:translate(0);opacity:1}}@keyframes sd-toast-leave{0%{transform:translate(0);opacity:1}to{transform:translate(100%);opacity:0}}@media(prefers-reduced-motion:reduce){:host,:host(.sd-toast--closing){animation:none}}.sd-toast__bar{width:6px;flex-shrink:0}.sd-toast__icon{padding:12px 0 0 12px;flex-shrink:0}.sd-toast__icon svg{width:24px;height:24px}.sd-toast__content{flex-grow:1;padding:12px;display:flex;flex-direction:column}.sd-toast__header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:4px}.sd-toast__title{font-size:16px;font-weight:600}.sd-toast__close{background:none;border:none;font-size:16px;cursor:pointer;line-height:1;padding:0;opacity:.5;transition:opacity .2s}.sd-toast__close:hover{opacity:1}.sd-toast__body{font-size:14px;line-height:1.4;word-break:break-word}.sd-toast__actions{display:flex;justify-content:flex-end;margin-top:8px}.msg-list{padding-left:0;list-style:none;margin:0}.toggle-more{background:none;border:none;padding:0;font:inherit;cursor:pointer;font-size:13px;margin-top:4px;font-weight:500;text-align:left}.toggle-more:hover{text-decoration:underline}.toggle-more:focus-visible{outline:2px solid currentColor;outline-offset:2px}.btn-action{border:none;padding:6px 12px;border-radius:4px;font-weight:600;cursor:pointer;font-size:13px;transition:filter .2s}.btn-action:hover{filter:brightness(.95)}\n"] }]
119
+ }, template: "<div class=\"sd-toast__bar\" [class]=\"'bg-' + data.type\"></div>\n\n<div class=\"sd-toast__icon\" [class]=\"'text-' + data.type\">\n @switch (data.type) {\n @case ('success') {\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z\"/></svg>\n }\n @case ('info') {\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z\"/></svg>\n }\n @case ('warning') {\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z\"/></svg>\n }\n @case ('error') {\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\"/></svg>\n }\n }\n</div>\n\n<div class=\"sd-toast__content\">\n <div class=\"sd-toast__header\">\n <strong class=\"sd-toast__title\">{{ data.title || ('core.notify.type.' + data.type | sdTranslate) }}</strong>\n <button class=\"sd-toast__close\" (click)=\"close()\">&times;</button>\n </div>\n\n <div class=\"sd-toast__body\">\n @if (!isMultiMessage) {\n <!-- Text rendering is the default safe path; HTML is opt-in and sanitized. -->\n @if (data.html) {\n <div [innerHTML]=\"sanitizeHtml(singleMessage)\"></div>\n } @else {\n <div>{{ singleMessage }}</div>\n }\n } @else {\n <ul class=\"msg-list\">\n @for (msg of displayMessages; track msg) {\n @if (data.html) {\n <li [innerHTML]=\"sanitizeHtml('- ' + msg)\"></li>\n } @else {\n <li>{{ '- ' + msg }}</li>\n }\n }\n </ul>\n\n @if (hasMore) {\n <button\n type=\"button\"\n class=\"toggle-more btn-link\"\n [class]=\"'text-' + data.type\"\n (click)=\"toggleExpand()\">\n {{ isExpanded() ? ('core.notify.show-less' | sdTranslate) : ('core.notify.show-more' | sdTranslate: { count: restCount }) }}\n </button>\n }\n }\n </div>\n\n @if (data.actionLabel) {\n <div class=\"sd-toast__actions\">\n <button\n class=\"btn-action\"\n [class]=\"'bg-' + data.type + '-light text-' + data.type\"\n (click)=\"onActionClick()\">\n {{ data.actionLabel }}\n </button>\n </div>\n }\n</div>\n", styles: [":host{display:flex;width:350px;border-radius:8px;box-shadow:0 4px 12px #00000026;margin-bottom:12px;overflow:hidden;position:relative;font-family:Roboto,sans-serif;pointer-events:auto;animation:sd-toast-enter .3s ease-out}:host(.sd-toast--closing){pointer-events:none;animation:sd-toast-leave .2s ease-in forwards}@keyframes sd-toast-enter{0%{transform:translate(100%);opacity:0}to{transform:translate(0);opacity:1}}@keyframes sd-toast-leave{0%{transform:translate(0);opacity:1}to{transform:translate(100%);opacity:0}}@media(prefers-reduced-motion:reduce){:host,:host(.sd-toast--closing){animation:none}}.sd-toast__bar{width:6px;flex-shrink:0}.sd-toast__icon{padding:12px 0 0 12px;flex-shrink:0}.sd-toast__icon svg{width:24px;height:24px}.sd-toast__content{flex-grow:1;padding:12px;display:flex;flex-direction:column}.sd-toast__header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:4px}.sd-toast__title{font-size:16px;font-weight:600}.sd-toast__close{background:none;border:none;font-size:16px;cursor:pointer;line-height:1;padding:0;opacity:.5;transition:opacity .2s}.sd-toast__close:hover{opacity:1}.sd-toast__body{font-size:14px;line-height:1.4;word-break:break-word}.sd-toast__actions{display:flex;justify-content:flex-end;margin-top:8px}.msg-list{padding-left:0;list-style:none;margin:0}.toggle-more{background:none;border:none;padding:0;font:inherit;cursor:pointer;font-size:13px;margin-top:4px;font-weight:500;text-align:left}.toggle-more:hover{text-decoration:underline}.toggle-more:focus-visible{outline:2px solid currentColor;outline-offset:2px}.btn-action{border:none;padding:6px 12px;border-radius:4px;font-weight:600;cursor:pointer;font-size:13px;transition:filter .2s}.btn-action:hover{filter:brightness(.95)}\n"] }]
120
120
  }], ctorParameters: () => [], propDecorators: { data: [{
121
121
  type: Input,
122
122
  args: [{ required: true }]
@@ -160,12 +160,22 @@ class SdNotifyService {
160
160
  // State
161
161
  #buffer = {};
162
162
  #timers = {};
163
+ #containerRef;
164
+ #containerElement;
163
165
  appRef = inject(ApplicationRef);
164
166
  injector = inject(EnvironmentInjector);
165
167
  document = inject(DOCUMENT);
168
+ #destroyRef = inject(DestroyRef);
166
169
  #i18n = inject(I18nService);
170
+ /** Đặt trong `#teardown()` — mọi API public trở thành no-op sau khi injector bị destroy. */
171
+ #destroyed = false;
167
172
  constructor() {
168
173
  this.#initContainer();
174
+ // why: providedIn:'root' KHÔNG có nghĩa là sống mãi — root injector vẫn bị destroy
175
+ // (TestBed reset, mỗi request khi SSR, micro-frontend unmount). Không có hook teardown thì
176
+ // ComponentRef + node <body> ở lại vĩnh viễn và timer debounce đang chờ sẽ flush vào signal
177
+ // của service đã chết → toast ma + leak view/DOM tích luỹ theo từng lần bootstrap.
178
+ this.#destroyRef.onDestroy(() => this.#teardown());
169
179
  }
170
180
  #initContainer() {
171
181
  const componentRef = createComponent(ToastContainerComponent, {
@@ -175,6 +185,33 @@ class SdNotifyService {
175
185
  this.appRef.attachView(componentRef.hostView);
176
186
  const domElem = componentRef.hostView.rootNodes[0];
177
187
  this.document.body.appendChild(domElem);
188
+ this.#containerRef = componentRef;
189
+ this.#containerElement = domElem;
190
+ }
191
+ #teardown() {
192
+ // why: cờ này chặn MỌI lời gọi sau teardown. Chỉ dọn timer đang chờ là chưa đủ: một
193
+ // `error()`/`warning()` gọi sau khi injector đã destroy vẫn chạy `#addToBuffer` và hẹn một
194
+ // timer 500ms MỚI mà không còn ai clear — nó flush vào signal của service đã chết.
195
+ this.#destroyed = true;
196
+ // why: dọn timer TRƯỚC khi huỷ view — flush chạy sau khi container đã destroy sẽ
197
+ // update signal mà không còn ai render, thuần tuý là công vô ích trên state đã chết.
198
+ this.#clearAllTimers();
199
+ this.#buffer = {};
200
+ const componentRef = this.#containerRef;
201
+ const containerElement = this.#containerElement;
202
+ this.#containerRef = undefined;
203
+ this.#containerElement = undefined;
204
+ // why: hook destroy của service chạy SAU ApplicationRef.destroy() khi root injector bị tháo
205
+ // (hook đăng ký theo thứ tự khởi tạo, mà appRef luôn có trước). Lúc đó view đã bị huỷ và
206
+ // detachView chỉ log NG0406 — nên chỉ detach khi appRef còn sống.
207
+ if (componentRef && !this.appRef.destroyed && !componentRef.hostView.destroyed) {
208
+ this.appRef.detachView(componentRef.hostView);
209
+ componentRef.destroy();
210
+ }
211
+ // why: createComponent() tạo host node rời rồi ta tự appendChild, nên destroy() không
212
+ // bảo đảm gỡ node khỏi <body> — gỡ tay để không để lại rác DOM. remove() an toàn khi
213
+ // node đã bị tách.
214
+ containerElement?.remove();
178
215
  }
179
216
  // Public API
180
217
  success(message, option) {
@@ -201,6 +238,8 @@ class SdNotifyService {
201
238
  }
202
239
  // Private helpers
203
240
  #addImmediate(type, message, option) {
241
+ if (this.#destroyed)
242
+ return;
204
243
  const newToast = {
205
244
  id: Utilities.generateUuid(),
206
245
  type,
@@ -217,6 +256,8 @@ class SdNotifyService {
217
256
  });
218
257
  }
219
258
  #addToBuffer(type, message, option) {
259
+ if (this.#destroyed)
260
+ return;
220
261
  if (!this.#buffer[type]) {
221
262
  this.#buffer[type] = [];
222
263
  }