@sdcorejs/angular 21.1.6 → 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.
- package/README.md +10 -0
- package/assets/scss/core/menu.scss +18 -0
- package/assets/scss/core/utilities/_misc.scss +15 -0
- package/assets/scss/sd-core.scss +1 -0
- package/fesm2022/sdcorejs-angular-components-anchor.mjs +37 -13
- package/fesm2022/sdcorejs-angular-components-anchor.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs +2 -2
- package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-badge.mjs +15 -2
- package/fesm2022/sdcorejs-angular-components-badge.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-button.mjs +2 -2
- package/fesm2022/sdcorejs-angular-components-button.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-ckeditor-styles.mjs +1 -1
- package/fesm2022/sdcorejs-angular-components-ckeditor-styles.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-code-editor.mjs +3 -3
- package/fesm2022/sdcorejs-angular-components-code-editor.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-data-state.mjs +3 -3
- package/fesm2022/sdcorejs-angular-components-data-state.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-editor.mjs +2 -2
- package/fesm2022/sdcorejs-angular-components-editor.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-form-generic.mjs +324 -192
- package/fesm2022/sdcorejs-angular-components-form-generic.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-history.mjs +4 -4
- package/fesm2022/sdcorejs-angular-components-history.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-import-excel.mjs +13 -5
- package/fesm2022/sdcorejs-angular-components-import-excel.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-inform.mjs +3 -3
- package/fesm2022/sdcorejs-angular-components-inform.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs +164 -8
- package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-modal.mjs +30 -18
- package/fesm2022/sdcorejs-angular-components-modal.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-org-chart.mjs +60 -13
- package/fesm2022/sdcorejs-angular-components-org-chart.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-preview.mjs +27 -17
- package/fesm2022/sdcorejs-angular-components-preview.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-query-bar.mjs +192 -30
- package/fesm2022/sdcorejs-angular-components-query-bar.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-query-builder.mjs +187 -127
- package/fesm2022/sdcorejs-angular-components-query-builder.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-section.mjs +16 -2
- package/fesm2022/sdcorejs-angular-components-section.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-side-drawer.mjs +83 -24
- package/fesm2022/sdcorejs-angular-components-side-drawer.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-splitter.mjs +57 -21
- package/fesm2022/sdcorejs-angular-components-splitter.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-stepper.mjs +2 -2
- package/fesm2022/sdcorejs-angular-components-stepper.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-tab-router.mjs +68 -20
- package/fesm2022/sdcorejs-angular-components-tab-router.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-tab.mjs +3 -2
- package/fesm2022/sdcorejs-angular-components-tab.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-table.mjs +542 -255
- package/fesm2022/sdcorejs-angular-components-table.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-tree.mjs +80 -17
- package/fesm2022/sdcorejs-angular-components-tree.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-upload-file.mjs +48 -13
- package/fesm2022/sdcorejs-angular-components-upload-file.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components.mjs +6 -1
- package/fesm2022/sdcorejs-angular-components.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-configurations.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-directives.mjs +45 -8
- package/fesm2022/sdcorejs-angular-directives.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs +40 -29
- package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-checkbox.mjs +5 -5
- package/fesm2022/sdcorejs-angular-forms-checkbox.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs +67 -50
- package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-chip.mjs +57 -43
- package/fesm2022/sdcorejs-angular-forms-chip.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-date-range.mjs +232 -61
- package/fesm2022/sdcorejs-angular-forms-date-range.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-date.mjs +96 -24
- package/fesm2022/sdcorejs-angular-forms-date.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-datetime.mjs +131 -27
- package/fesm2022/sdcorejs-angular-forms-datetime.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-entity-picker.mjs +40 -16
- package/fesm2022/sdcorejs-angular-forms-entity-picker.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-input-color.mjs +16 -5
- package/fesm2022/sdcorejs-angular-forms-input-color.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-input-number.mjs +24 -9
- package/fesm2022/sdcorejs-angular-forms-input-number.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-input.mjs +58 -10
- package/fesm2022/sdcorejs-angular-forms-input.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-models.mjs +227 -6
- package/fesm2022/sdcorejs-angular-forms-models.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-radio.mjs +4 -4
- package/fesm2022/sdcorejs-angular-forms-radio.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-select.mjs +136 -38
- package/fesm2022/sdcorejs-angular-forms-select.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-switch.mjs +18 -3
- package/fesm2022/sdcorejs-angular-forms-switch.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-textarea.mjs +51 -62
- package/fesm2022/sdcorejs-angular-forms-textarea.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-time-range.mjs +57 -12
- package/fesm2022/sdcorejs-angular-forms-time-range.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-time.mjs +16 -5
- package/fesm2022/sdcorejs-angular-forms-time.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-tree-select.mjs +100 -9
- package/fesm2022/sdcorejs-angular-forms-tree-select.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms.mjs +91 -46
- package/fesm2022/sdcorejs-angular-forms.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-handlers.mjs +18 -3
- package/fesm2022/sdcorejs-angular-handlers.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-i18n.mjs +490 -68
- package/fesm2022/sdcorejs-angular-i18n.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-interceptors.mjs +87 -29
- package/fesm2022/sdcorejs-angular-interceptors.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-modules-auth.mjs +25 -15
- package/fesm2022/sdcorejs-angular-modules-auth.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-modules-icon.mjs +2 -2
- package/fesm2022/sdcorejs-angular-modules-icon.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-modules-keycloak.mjs +32 -3
- package/fesm2022/sdcorejs-angular-modules-keycloak.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-modules-layout.mjs +332 -111
- package/fesm2022/sdcorejs-angular-modules-layout.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-modules-permission.mjs +177 -40
- package/fesm2022/sdcorejs-angular-modules-permission.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-pipes.mjs +62 -28
- package/fesm2022/sdcorejs-angular-pipes.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-api.mjs +123 -13
- package/fesm2022/sdcorejs-angular-services-api.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-cache.mjs +53 -3
- package/fesm2022/sdcorejs-angular-services-cache.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-confirm.mjs +25 -73
- package/fesm2022/sdcorejs-angular-services-confirm.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-excel.mjs +12 -10
- package/fesm2022/sdcorejs-angular-services-excel.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-loading.mjs +75 -84
- package/fesm2022/sdcorejs-angular-services-loading.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-notify.mjs +46 -5
- package/fesm2022/sdcorejs-angular-services-notify.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-storage.mjs +4 -0
- package/fesm2022/sdcorejs-angular-services-storage.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services.mjs +0 -1
- package/fesm2022/sdcorejs-angular-services.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-utilities-extensions.mjs +225 -80
- package/fesm2022/sdcorejs-angular-utilities-extensions.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-utilities-models.mjs +1 -63
- package/fesm2022/sdcorejs-angular-utilities-models.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular.mjs +17 -10
- package/fesm2022/sdcorejs-angular.mjs.map +1 -1
- package/package.json +32 -25
- package/types/sdcorejs-angular-components-anchor.d.ts +3 -1
- package/types/sdcorejs-angular-components-badge.d.ts +1 -0
- package/types/sdcorejs-angular-components-ckeditor-styles.d.ts +1 -1
- package/types/sdcorejs-angular-components-form-generic.d.ts +44 -33
- package/types/sdcorejs-angular-components-import-excel.d.ts +1 -0
- package/types/sdcorejs-angular-components-mini-editor.d.ts +1 -1
- package/types/sdcorejs-angular-components-modal-resizable.d.ts +92 -0
- package/types/sdcorejs-angular-components-modal.d.ts +3 -2
- package/types/sdcorejs-angular-components-org-chart.d.ts +6 -0
- package/types/sdcorejs-angular-components-preview.d.ts +1 -0
- package/types/sdcorejs-angular-components-query-bar.d.ts +55 -17
- package/types/sdcorejs-angular-components-query-builder.d.ts +150 -98
- package/types/sdcorejs-angular-components-section.d.ts +3 -0
- package/types/sdcorejs-angular-components-tab-router.d.ts +19 -19
- package/types/sdcorejs-angular-components-table.d.ts +251 -109
- package/types/sdcorejs-angular-components-tree.d.ts +48 -30
- package/types/sdcorejs-angular-components-upload-file.d.ts +6 -3
- package/types/sdcorejs-angular-components.d.ts +6 -1
- package/types/sdcorejs-angular-configurations.d.ts +1 -2
- package/types/sdcorejs-angular-forms-autocomplete.d.ts +5 -2
- package/types/sdcorejs-angular-forms-chip-calendar.d.ts +10 -0
- package/types/sdcorejs-angular-forms-date-range.d.ts +14 -2
- package/types/sdcorejs-angular-forms-date.d.ts +13 -2
- package/types/sdcorejs-angular-forms-datetime.d.ts +13 -2
- package/types/sdcorejs-angular-forms-entity-picker.d.ts +9 -3
- package/types/sdcorejs-angular-forms-input-color.d.ts +4 -0
- package/types/sdcorejs-angular-forms-input-number.d.ts +3 -0
- package/types/sdcorejs-angular-forms-input.d.ts +3 -0
- package/types/sdcorejs-angular-forms-models.d.ts +119 -4
- package/types/sdcorejs-angular-forms-select.d.ts +27 -4
- package/types/sdcorejs-angular-forms-switch.d.ts +7 -1
- package/types/sdcorejs-angular-forms-textarea.d.ts +5 -3
- package/types/sdcorejs-angular-forms-time-range.d.ts +22 -1
- package/types/sdcorejs-angular-forms-tree-select.d.ts +36 -5
- package/types/sdcorejs-angular-forms.d.ts +22 -13
- package/types/sdcorejs-angular-i18n.d.ts +95 -16
- package/types/sdcorejs-angular-interceptors.d.ts +13 -3
- package/types/sdcorejs-angular-modules-auth.d.ts +1 -1
- package/types/sdcorejs-angular-modules-keycloak.d.ts +1 -0
- package/types/sdcorejs-angular-modules-layout.d.ts +94 -28
- package/types/sdcorejs-angular-modules-permission.d.ts +66 -4
- package/types/sdcorejs-angular-pipes.d.ts +27 -5
- package/types/sdcorejs-angular-services-api.d.ts +21 -1
- package/types/sdcorejs-angular-services-cache.d.ts +11 -1
- package/types/sdcorejs-angular-services-notify.d.ts +1 -1
- package/types/sdcorejs-angular-services.d.ts +0 -1
- package/types/sdcorejs-angular-utilities-extensions.d.ts +83 -61
- package/types/sdcorejs-angular-utilities-models.d.ts +2 -76
- package/types/sdcorejs-angular.d.ts +11 -2
- package/fesm2022/sdcorejs-angular-components-base.mjs +0 -23
- package/fesm2022/sdcorejs-angular-components-base.mjs.map +0 -1
- package/fesm2022/sdcorejs-angular-components-document-builder.mjs +0 -5038
- package/fesm2022/sdcorejs-angular-components-document-builder.mjs.map +0 -1
- package/fesm2022/sdcorejs-angular-models.mjs +0 -6
- package/fesm2022/sdcorejs-angular-models.mjs.map +0 -1
- package/fesm2022/sdcorejs-angular-services-docx.mjs +0 -291
- package/fesm2022/sdcorejs-angular-services-docx.mjs.map +0 -1
- package/fesm2022/sdcorejs-angular-services-license.mjs +0 -97
- package/fesm2022/sdcorejs-angular-services-license.mjs.map +0 -1
- package/types/sdcorejs-angular-components-base.d.ts +0 -11
- package/types/sdcorejs-angular-components-document-builder.d.ts +0 -252
- package/types/sdcorejs-angular-models.d.ts +0 -2
- package/types/sdcorejs-angular-services-docx.d.ts +0 -27
- package/types/sdcorejs-angular-services-license.d.ts +0 -12
|
@@ -1,41 +1,59 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { HttpBackend, HttpRequest, HttpHeaders, HttpResponse } from '@angular/common/http';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { inject,
|
|
3
|
+
import { InjectionToken, inject, DestroyRef, Injectable } from '@angular/core';
|
|
4
4
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
|
5
5
|
import { I18nService } from '@sdcorejs/angular/i18n';
|
|
6
|
-
import { throwError } from 'rxjs';
|
|
7
|
-
import { catchError, switchMap } from 'rxjs/operators';
|
|
6
|
+
import { throwError, of } from 'rxjs';
|
|
7
|
+
import { catchError, switchMap, filter, map } from 'rxjs/operators';
|
|
8
8
|
import { SdAuthService } from '@sdcorejs/angular/modules';
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* URL dùng để dò kết nối khi một request trả về status 0.
|
|
12
|
+
*
|
|
13
|
+
* why: bản cũ hard-code `https://jsonplaceholder.typicode.com/todos/1`, không override được. Mọi
|
|
14
|
+
* app tiêu dùng thư viện đều ping một bên thứ ba lạ mỗi 3 giây khi mất mạng, và vì probe đi qua
|
|
15
|
+
* NGUYÊN chuỗi interceptor của app nên bất kỳ interceptor nào gắn header auth vô điều kiện đều
|
|
16
|
+
* rò token ra ngoài origin. Mặc định giờ là một đường dẫn same-origin; app đổi bằng cách provide
|
|
17
|
+
* lại token này (nên trỏ tới endpoint tĩnh, rẻ, không cần auth).
|
|
18
|
+
*/
|
|
19
|
+
const SD_NO_INTERNET_PROBE_URL = new InjectionToken('sd.no-internet.probe-url', {
|
|
20
|
+
providedIn: 'root',
|
|
21
|
+
factory: () => '/favicon.ico',
|
|
22
|
+
});
|
|
10
23
|
class SdNoInternetInterceptor {
|
|
11
24
|
snackBar = inject(MatSnackBar);
|
|
12
|
-
injector = inject(Injector);
|
|
13
25
|
#i18n = inject(I18nService);
|
|
26
|
+
// why: HttpBackend là tầng cuối, KHÔNG đi qua chuỗi interceptor — probe vì thế không kéo theo
|
|
27
|
+
// header do interceptor của app gắn, và cũng không tự đệ quy vào chính interceptor này.
|
|
28
|
+
// Dùng backend cũng loại luôn lý do phải lazy-resolve HttpClient qua Injector (circular DI).
|
|
29
|
+
#backend = inject(HttpBackend);
|
|
30
|
+
#probeUrl = inject(SD_NO_INTERNET_PROBE_URL);
|
|
31
|
+
#destroyRef = inject(DestroyRef);
|
|
14
32
|
// Trạng thái offline để tránh spam request check hoặc hiển thị nhiều snackbar
|
|
15
33
|
#isOffline = false;
|
|
16
34
|
// Giữ tham chiếu để có thể đóng/thao tác snackbar
|
|
17
35
|
#snackBarRef = null;
|
|
18
36
|
// Giữ tham chiếu interval để clear khi có mạng lại
|
|
19
37
|
#pollInterval = null;
|
|
20
|
-
//
|
|
21
|
-
#
|
|
22
|
-
// Endpoint kiểm tra mạng (Nên dùng 1 file tĩnh nhẹ hoặc API ping public uy tín)
|
|
23
|
-
#healthCheckUrl = 'https://jsonplaceholder.typicode.com/todos/1';
|
|
38
|
+
// Giữ tham chiếu request probe đang bay để huỷ được khi dừng polling
|
|
39
|
+
#pollSubscription = null;
|
|
24
40
|
// Thời gian lặp lại việc kiểm tra mạng (ms)
|
|
25
41
|
#checkIntervalDuration = 3000;
|
|
26
|
-
constructor() {
|
|
42
|
+
constructor() {
|
|
43
|
+
// why: interceptor sống ở root injector nhưng vòng polling không có teardown nào — nó chỉ
|
|
44
|
+
// dừng khi mạng phục hồi. App/microfrontend bị destroy giữa lúc offline sẽ bỏ lại một timer
|
|
45
|
+
// 3s cùng request HTTP của nó chạy mãi.
|
|
46
|
+
this.#destroyRef.onDestroy(() => this.#stopPolling());
|
|
47
|
+
}
|
|
27
48
|
intercept(request, next) {
|
|
28
49
|
return next.handle(request).pipe(catchError((error) => {
|
|
29
50
|
// --- TRƯỜNG HỢP 1: Lỗi mất kết nối (Status 0) ---
|
|
30
51
|
if (error.status === 0 && !this.#isOffline) {
|
|
31
52
|
this.#isOffline = true;
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
// Gọi thử một API public để xác minh xem là Mất mạng thật hay do CORS/Server chặn
|
|
37
|
-
return this.#http.get(this.#healthCheckUrl).pipe(
|
|
38
|
-
// 1. Ưu tiên bắt lỗi kết nối trước (Đây là logic MẤT MẠNG THẬT)
|
|
53
|
+
// Gọi thử probe để xác minh xem là Mất mạng thật hay do CORS/SSL/Server chặn
|
|
54
|
+
return this.#probe().pipe(
|
|
55
|
+
// 1. Ưu tiên bắt lỗi kết nối trước (MẤT MẠNG THẬT — probe cũng không mở nổi kết nối,
|
|
56
|
+
// tức status 0; mọi status HTTP khác đã được #probe quy về "có mạng")
|
|
39
57
|
catchError(_checkError => {
|
|
40
58
|
// Hiển thị thông báo và bắt đầu polling chờ mạng
|
|
41
59
|
this.#showReloadSnackbar(this.#i18n.t('core.interceptor.no-internet.offline'), { isSticky: true });
|
|
@@ -69,6 +87,28 @@ class SdNoInternetInterceptor {
|
|
|
69
87
|
return throwError(() => error);
|
|
70
88
|
}));
|
|
71
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Bắn một request dò kết nối đi thẳng qua HttpBackend.
|
|
92
|
+
*
|
|
93
|
+
* Phát `true` khi CÓ mạng; chỉ ném lỗi khi thật sự không mở được kết nối (`status === 0`).
|
|
94
|
+
*/
|
|
95
|
+
#probe = () => {
|
|
96
|
+
const request = new HttpRequest('GET', this.#probeUrl, {
|
|
97
|
+
responseType: 'text',
|
|
98
|
+
// why: probe mà đọc được từ HTTP cache thì lúc offline vẫn "thành công" và interceptor sẽ
|
|
99
|
+
// kết luận nhầm là đã có mạng. no-cache buộc trình duyệt phải đi hỏi lại server.
|
|
100
|
+
headers: new HttpHeaders({ 'Cache-Control': 'no-cache', Pragma: 'no-cache' }),
|
|
101
|
+
});
|
|
102
|
+
// why: HttpBackend phát HttpEvent thô (có thể gồm sent/progress), chỉ HttpResponse mới là
|
|
103
|
+
// tín hiệu "đã có mạng".
|
|
104
|
+
return this.#backend.handle(request).pipe(filter((event) => event instanceof HttpResponse), map(() => true),
|
|
105
|
+
// why: HttpBackend ném HttpErrorResponse cho MỌI status không phải 2xx, nên coi "probe lỗi"
|
|
106
|
+
// = "mất mạng thật" là sai. Một `404` rất phổ biến (app xoá favicon mặc định của CLI, static
|
|
107
|
+
// host trả 404/403 cho đường dẫn không tồn tại) và chính nó ĐÃ chứng minh là có mạng — nhưng
|
|
108
|
+
// vẫn bị phân loại thành offline, dựng snackbar offline dính cứng cùng vòng poll 3 giây không
|
|
109
|
+
// bao giờ thoát được. Chỉ status 0 (không thiết lập nổi kết nối) mới là offline.
|
|
110
|
+
catchError((error) => (error.status === 0 ? throwError(() => error) : of(true))));
|
|
111
|
+
};
|
|
72
112
|
/**
|
|
73
113
|
* Hiển thị Snackbar thông báo trạng thái mạng
|
|
74
114
|
*/
|
|
@@ -94,23 +134,19 @@ class SdNoInternetInterceptor {
|
|
|
94
134
|
*/
|
|
95
135
|
#startPolling = () => {
|
|
96
136
|
this.#stopPolling(); // Clear cũ nếu có
|
|
97
|
-
// @i18n-ignore — dev console log
|
|
98
137
|
this.#pollInterval = setInterval(() => {
|
|
99
|
-
if (!this.#http) {
|
|
100
|
-
this.#http = this.injector.get(HttpClient);
|
|
101
|
-
}
|
|
102
138
|
// Check nhẹ
|
|
103
|
-
this.#
|
|
139
|
+
this.#pollSubscription?.unsubscribe();
|
|
140
|
+
this.#pollSubscription = this.#probe().subscribe({
|
|
104
141
|
next: () => {
|
|
105
|
-
// --> ĐÃ CÓ MẠNG LẠI
|
|
106
|
-
// @i18n-ignore — dev console log
|
|
142
|
+
// --> ĐÃ CÓ MẠNG LẠI (2xx, hoặc bất kỳ status HTTP nào — xem #probe)
|
|
107
143
|
this.#stopPolling();
|
|
108
144
|
this.#isOffline = false;
|
|
109
145
|
// Thông báo thành công (tự tắt sau 5s)
|
|
110
146
|
this.#showReloadSnackbar(this.#i18n.t('core.interceptor.no-internet.restored'), { duration: 5000, isSticky: false });
|
|
111
147
|
},
|
|
112
148
|
error: () => {
|
|
113
|
-
// --> VẪN MẤT MẠNG: Không làm gì cả, chờ lần check tiếp theo
|
|
149
|
+
// --> VẪN MẤT MẠNG (status 0): Không làm gì cả, chờ lần check tiếp theo
|
|
114
150
|
},
|
|
115
151
|
});
|
|
116
152
|
}, this.#checkIntervalDuration);
|
|
@@ -123,6 +159,8 @@ class SdNoInternetInterceptor {
|
|
|
123
159
|
clearInterval(this.#pollInterval);
|
|
124
160
|
this.#pollInterval = null;
|
|
125
161
|
}
|
|
162
|
+
this.#pollSubscription?.unsubscribe();
|
|
163
|
+
this.#pollSubscription = null;
|
|
126
164
|
};
|
|
127
165
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdNoInternetInterceptor, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
128
166
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdNoInternetInterceptor });
|
|
@@ -133,17 +171,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
|
|
|
133
171
|
|
|
134
172
|
class SdUnauthorizedInterceptor {
|
|
135
173
|
authService = inject(SdAuthService);
|
|
136
|
-
|
|
174
|
+
/**
|
|
175
|
+
* why: bản cũ dùng một boolean `#unauthorizedHandled` set một lần rồi KHÔNG bao giờ mở lại.
|
|
176
|
+
* Hệ quả: sau lần 401 đầu tiên, mọi 401 còn lại trong vòng đời trang đều bị nuốt — người dùng
|
|
177
|
+
* đăng nhập lại xong, phiên hết hạn lần hai thì không còn bị ép signout nữa. Tệ hơn: nếu
|
|
178
|
+
* `SD_AUTH_CONFIGURATION.action.signout` không được cấu hình thì `SdAuthService.signout()` là
|
|
179
|
+
* no-op, nên lần 401 đầu tiên đốt latch mà chẳng làm gì cả.
|
|
180
|
+
*
|
|
181
|
+
* Thay bằng một cửa sổ debounce ngắn: 401 song song trong cùng cửa sổ vẫn chỉ gọi signout một
|
|
182
|
+
* lần (đúng mục đích ban đầu của latch), nhưng hết cửa sổ là latch tự mở lại.
|
|
183
|
+
*
|
|
184
|
+
* why: latch CHỈ mở lại theo thời gian. Bản trước còn reset `#lastSignoutAt` ở mỗi `HttpResponse`
|
|
185
|
+
* thành công, nhưng một 2xx KHÔNG chứng minh phiên còn sống — endpoint công khai vẫn trả 200 khi
|
|
186
|
+
* token đã hết hạn. Đúng kịch bản latch sinh ra để chặn (một burst request song song, vài cái
|
|
187
|
+
* trúng endpoint công khai trả 2xx xen giữa những cái trả 401) sẽ gọi signout một lần cho MỖI 200
|
|
188
|
+
* xen vào.
|
|
189
|
+
*/
|
|
190
|
+
#signoutDebounceMs = 3000;
|
|
191
|
+
#lastSignoutAt = null;
|
|
137
192
|
constructor() { }
|
|
138
193
|
intercept(request, next) {
|
|
139
194
|
return next.handle(request).pipe(catchError((error) => {
|
|
140
|
-
if (error.status === 401 &&
|
|
141
|
-
this.#
|
|
195
|
+
if (error.status === 401 && this.#shouldSignout()) {
|
|
196
|
+
this.#lastSignoutAt = Date.now();
|
|
142
197
|
this.authService.signout();
|
|
143
198
|
}
|
|
144
199
|
return throwError(() => error);
|
|
145
200
|
}));
|
|
146
201
|
}
|
|
202
|
+
#shouldSignout = () => {
|
|
203
|
+
return this.#lastSignoutAt === null || Date.now() - this.#lastSignoutAt >= this.#signoutDebounceMs;
|
|
204
|
+
};
|
|
147
205
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdUnauthorizedInterceptor, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
148
206
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdUnauthorizedInterceptor });
|
|
149
207
|
}
|
|
@@ -155,5 +213,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
|
|
|
155
213
|
* Generated bundle index. Do not edit.
|
|
156
214
|
*/
|
|
157
215
|
|
|
158
|
-
export { SdNoInternetInterceptor, SdUnauthorizedInterceptor };
|
|
216
|
+
export { SD_NO_INTERNET_PROBE_URL, SdNoInternetInterceptor, SdUnauthorizedInterceptor };
|
|
159
217
|
//# sourceMappingURL=sdcorejs-angular-interceptors.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sdcorejs-angular-interceptors.mjs","sources":["../../../projects/sdcorejs-angular/interceptors/no-internet/no-internet.interceptor.ts","../../../projects/sdcorejs-angular/interceptors/unauthorized/unauthorized.interceptor.ts","../../../projects/sdcorejs-angular/interceptors/sdcorejs-angular-interceptors.ts"],"sourcesContent":["import { HttpClient, HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';\nimport { inject, Injectable, Injector } from '@angular/core';\nimport { MatSnackBar, MatSnackBarRef } from '@angular/material/snack-bar';\nimport { I18nService } from '@sdcorejs/angular/i18n';\nimport { Observable, throwError } from 'rxjs';\nimport { catchError, switchMap } from 'rxjs/operators';\n\n@Injectable()\nexport class SdNoInternetInterceptor implements HttpInterceptor {\n private snackBar = inject(MatSnackBar);\n private injector = inject(Injector);\n\n readonly #i18n = inject(I18nService);\n\n // Trạng thái offline để tránh spam request check hoặc hiển thị nhiều snackbar\n #isOffline = false;\n\n // Giữ tham chiếu để có thể đóng/thao tác snackbar\n #snackBarRef: MatSnackBarRef<any> | null = null;\n\n // Giữ tham chiếu interval để clear khi có mạng lại\n #pollInterval: ReturnType<typeof setInterval> | null = null;\n\n // Lazy load HttpClient\n #http: HttpClient | null = null;\n\n // Endpoint kiểm tra mạng (Nên dùng 1 file tĩnh nhẹ hoặc API ping public uy tín)\n readonly #healthCheckUrl = 'https://jsonplaceholder.typicode.com/todos/1';\n\n // Thời gian lặp lại việc kiểm tra mạng (ms)\n readonly #checkIntervalDuration = 3000;\n\n /** Inserted by Angular inject() migration for backwards compatibility */\n constructor(...args: unknown[]);\n\n constructor() {}\n\n intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {\n return next.handle(request).pipe(\n catchError((error: HttpErrorResponse) => {\n // --- TRƯỜNG HỢP 1: Lỗi mất kết nối (Status 0) ---\n if (error.status === 0 && !this.#isOffline) {\n this.#isOffline = true;\n\n // Lazy load HttpClient để tránh lỗi Circular Dependency\n if (!this.#http) {\n this.#http = this.injector.get(HttpClient);\n }\n\n // Gọi thử một API public để xác minh xem là Mất mạng thật hay do CORS/Server chặn\n return this.#http.get(this.#healthCheckUrl).pipe(\n // 1. Ưu tiên bắt lỗi kết nối trước (Đây là logic MẤT MẠNG THẬT)\n catchError(_checkError => {\n // Hiển thị thông báo và bắt đầu polling chờ mạng\n this.#showReloadSnackbar(this.#i18n.t('core.interceptor.no-internet.offline'), { isSticky: true });\n this.#startPolling();\n\n // Ném lại lỗi gốc để Component biết request thất bại\n return throwError(() => error);\n }),\n\n // 2. Nếu không vào catchError ở trên -> Check thành công -> CÓ MẠNG\n // Lỗi status 0 ban đầu là do CORS, SSL, hoặc Server chặn connection\n switchMap(() => {\n this.#isOffline = false; // Reset cờ\n\n this.snackBar.open(this.#i18n.t('core.interceptor.no-internet.cors-error'), this.#i18n.t('core.common.close'), {\n duration: 5000,\n horizontalPosition: 'center',\n verticalPosition: 'top',\n });\n\n // Ném lại lỗi gốc\n return throwError(() => error);\n })\n );\n }\n\n // --- TRƯỜNG HỢP 2: Server bảo trì (503) ---\n else if (error.status === 503) {\n this.snackBar.open(this.#i18n.t('core.interceptor.no-internet.maintenance'), this.#i18n.t('core.common.close'), {\n horizontalPosition: 'center',\n verticalPosition: 'top',\n duration: 5000,\n });\n }\n\n // --- TRƯỜNG HỢP KHÁC: 401, 404, 500... ---\n // Ném lỗi ra để component xử lý bình thường\n return throwError(() => error);\n })\n );\n }\n\n /**\n * Hiển thị Snackbar thông báo trạng thái mạng\n */\n #showReloadSnackbar = (message: string, options?: { duration?: number; isSticky?: boolean }): void => {\n // Đóng snackbar cũ nếu đang hiện\n if (this.#snackBarRef) {\n this.#snackBarRef.dismiss();\n }\n\n this.#snackBarRef = this.snackBar.open(message, this.#i18n.t('core.common.reload'), {\n horizontalPosition: 'center',\n verticalPosition: 'top',\n // Nếu isSticky = true (mất mạng) -> Không tự tắt. Nếu có duration -> tự tắt.\n duration: options?.duration,\n panelClass: options?.isSticky ? ['offline-snackbar'] : undefined, // Class CSS tùy chọn\n });\n\n // Xử lý sự kiện bấm nút \"Tải lại trang\"\n this.#snackBarRef.onAction().subscribe(() => {\n window.location.reload();\n });\n };\n\n /**\n * Bắt đầu vòng lặp kiểm tra kết nối mạng\n */\n #startPolling = (): void => {\n this.#stopPolling(); // Clear cũ nếu có\n\n // @i18n-ignore — dev console log\n\n this.#pollInterval = setInterval(() => {\n if (!this.#http) {\n this.#http = this.injector.get(HttpClient);\n }\n\n // Check nhẹ\n this.#http.get(this.#healthCheckUrl).subscribe({\n next: () => {\n // --> ĐÃ CÓ MẠNG LẠI\n // @i18n-ignore — dev console log\n\n this.#stopPolling();\n this.#isOffline = false;\n\n // Thông báo thành công (tự tắt sau 5s)\n this.#showReloadSnackbar(this.#i18n.t('core.interceptor.no-internet.restored'), { duration: 5000, isSticky: false });\n },\n error: () => {\n // --> VẪN MẤT MẠNG: Không làm gì cả, chờ lần check tiếp theo\n },\n });\n }, this.#checkIntervalDuration);\n };\n\n /**\n * Dừng vòng lặp kiểm tra\n */\n #stopPolling = (): void => {\n if (this.#pollInterval) {\n clearInterval(this.#pollInterval);\n this.#pollInterval = null;\n }\n };\n}\n","import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';\nimport { Injectable, inject } from '@angular/core';\nimport { SdAuthService } from '@sdcorejs/angular/modules';\nimport { Observable, throwError } from 'rxjs';\nimport { catchError } from 'rxjs/operators';\n\n@Injectable()\nexport class SdUnauthorizedInterceptor implements HttpInterceptor {\n private authService = inject(SdAuthService);\n\n #unauthorizedHandled = false;\n\n /** Inserted by Angular inject() migration for backwards compatibility */\n constructor(...args: unknown[]);\n\n constructor() {}\n\n intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {\n return next.handle(request).pipe(\n catchError((error: HttpErrorResponse) => {\n if (error.status === 401 && !this.#unauthorizedHandled) {\n this.#unauthorizedHandled = true;\n this.authService.signout();\n }\n\n return throwError(() => error);\n })\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-interceptors.mjs","sources":["../../../projects/sdcorejs-angular/interceptors/no-internet/no-internet.interceptor.ts","../../../projects/sdcorejs-angular/interceptors/unauthorized/unauthorized.interceptor.ts","../../../projects/sdcorejs-angular/interceptors/sdcorejs-angular-interceptors.ts"],"sourcesContent":["import {\n HttpBackend,\n HttpErrorResponse,\n HttpEvent,\n HttpHandler,\n HttpHeaders,\n HttpInterceptor,\n HttpRequest,\n HttpResponse,\n} from '@angular/common/http';\nimport { DestroyRef, inject, Injectable, InjectionToken } from '@angular/core';\nimport { MatSnackBar, MatSnackBarRef } from '@angular/material/snack-bar';\nimport { I18nService } from '@sdcorejs/angular/i18n';\nimport { Observable, of, Subscription, throwError } from 'rxjs';\nimport { catchError, filter, map, switchMap } from 'rxjs/operators';\n\n/**\n * URL dùng để dò kết nối khi một request trả về status 0.\n *\n * why: bản cũ hard-code `https://jsonplaceholder.typicode.com/todos/1`, không override được. Mọi\n * app tiêu dùng thư viện đều ping một bên thứ ba lạ mỗi 3 giây khi mất mạng, và vì probe đi qua\n * NGUYÊN chuỗi interceptor của app nên bất kỳ interceptor nào gắn header auth vô điều kiện đều\n * rò token ra ngoài origin. Mặc định giờ là một đường dẫn same-origin; app đổi bằng cách provide\n * lại token này (nên trỏ tới endpoint tĩnh, rẻ, không cần auth).\n */\nexport const SD_NO_INTERNET_PROBE_URL = new InjectionToken<string>('sd.no-internet.probe-url', {\n providedIn: 'root',\n factory: () => '/favicon.ico',\n});\n\n@Injectable()\nexport class SdNoInternetInterceptor implements HttpInterceptor {\n private snackBar = inject(MatSnackBar);\n\n readonly #i18n = inject(I18nService);\n\n // why: HttpBackend là tầng cuối, KHÔNG đi qua chuỗi interceptor — probe vì thế không kéo theo\n // header do interceptor của app gắn, và cũng không tự đệ quy vào chính interceptor này.\n // Dùng backend cũng loại luôn lý do phải lazy-resolve HttpClient qua Injector (circular DI).\n readonly #backend = inject(HttpBackend);\n\n readonly #probeUrl = inject(SD_NO_INTERNET_PROBE_URL);\n\n readonly #destroyRef = inject(DestroyRef);\n\n // Trạng thái offline để tránh spam request check hoặc hiển thị nhiều snackbar\n #isOffline = false;\n\n // Giữ tham chiếu để có thể đóng/thao tác snackbar\n #snackBarRef: MatSnackBarRef<any> | null = null;\n\n // Giữ tham chiếu interval để clear khi có mạng lại\n #pollInterval: ReturnType<typeof setInterval> | null = null;\n\n // Giữ tham chiếu request probe đang bay để huỷ được khi dừng polling\n #pollSubscription: Subscription | null = null;\n\n // Thời gian lặp lại việc kiểm tra mạng (ms)\n readonly #checkIntervalDuration = 3000;\n\n /** Inserted by Angular inject() migration for backwards compatibility */\n constructor(...args: unknown[]);\n\n constructor() {\n // why: interceptor sống ở root injector nhưng vòng polling không có teardown nào — nó chỉ\n // dừng khi mạng phục hồi. App/microfrontend bị destroy giữa lúc offline sẽ bỏ lại một timer\n // 3s cùng request HTTP của nó chạy mãi.\n this.#destroyRef.onDestroy(() => this.#stopPolling());\n }\n\n intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {\n return next.handle(request).pipe(\n catchError((error: HttpErrorResponse) => {\n // --- TRƯỜNG HỢP 1: Lỗi mất kết nối (Status 0) ---\n if (error.status === 0 && !this.#isOffline) {\n this.#isOffline = true;\n\n // Gọi thử probe để xác minh xem là Mất mạng thật hay do CORS/SSL/Server chặn\n return this.#probe().pipe(\n // 1. Ưu tiên bắt lỗi kết nối trước (MẤT MẠNG THẬT — probe cũng không mở nổi kết nối,\n // tức status 0; mọi status HTTP khác đã được #probe quy về \"có mạng\")\n catchError(_checkError => {\n // Hiển thị thông báo và bắt đầu polling chờ mạng\n this.#showReloadSnackbar(this.#i18n.t('core.interceptor.no-internet.offline'), { isSticky: true });\n this.#startPolling();\n\n // Ném lại lỗi gốc để Component biết request thất bại\n return throwError(() => error);\n }),\n\n // 2. Nếu không vào catchError ở trên -> Check thành công -> CÓ MẠNG\n // Lỗi status 0 ban đầu là do CORS, SSL, hoặc Server chặn connection\n switchMap(() => {\n this.#isOffline = false; // Reset cờ\n\n this.snackBar.open(this.#i18n.t('core.interceptor.no-internet.cors-error'), this.#i18n.t('core.common.close'), {\n duration: 5000,\n horizontalPosition: 'center',\n verticalPosition: 'top',\n });\n\n // Ném lại lỗi gốc\n return throwError(() => error);\n })\n );\n }\n\n // --- TRƯỜNG HỢP 2: Server bảo trì (503) ---\n else if (error.status === 503) {\n this.snackBar.open(this.#i18n.t('core.interceptor.no-internet.maintenance'), this.#i18n.t('core.common.close'), {\n horizontalPosition: 'center',\n verticalPosition: 'top',\n duration: 5000,\n });\n }\n\n // --- TRƯỜNG HỢP KHÁC: 401, 404, 500... ---\n // Ném lỗi ra để component xử lý bình thường\n return throwError(() => error);\n })\n );\n }\n\n /**\n * Bắn một request dò kết nối đi thẳng qua HttpBackend.\n *\n * Phát `true` khi CÓ mạng; chỉ ném lỗi khi thật sự không mở được kết nối (`status === 0`).\n */\n #probe = (): Observable<boolean> => {\n const request = new HttpRequest<null>('GET', this.#probeUrl, {\n responseType: 'text',\n // why: probe mà đọc được từ HTTP cache thì lúc offline vẫn \"thành công\" và interceptor sẽ\n // kết luận nhầm là đã có mạng. no-cache buộc trình duyệt phải đi hỏi lại server.\n headers: new HttpHeaders({ 'Cache-Control': 'no-cache', Pragma: 'no-cache' }),\n });\n\n // why: HttpBackend phát HttpEvent thô (có thể gồm sent/progress), chỉ HttpResponse mới là\n // tín hiệu \"đã có mạng\".\n return this.#backend.handle(request).pipe(\n filter((event): event is HttpResponse<unknown> => event instanceof HttpResponse),\n map(() => true),\n // why: HttpBackend ném HttpErrorResponse cho MỌI status không phải 2xx, nên coi \"probe lỗi\"\n // = \"mất mạng thật\" là sai. Một `404` rất phổ biến (app xoá favicon mặc định của CLI, static\n // host trả 404/403 cho đường dẫn không tồn tại) và chính nó ĐÃ chứng minh là có mạng — nhưng\n // vẫn bị phân loại thành offline, dựng snackbar offline dính cứng cùng vòng poll 3 giây không\n // bao giờ thoát được. Chỉ status 0 (không thiết lập nổi kết nối) mới là offline.\n catchError((error: HttpErrorResponse) => (error.status === 0 ? throwError(() => error) : of(true)))\n );\n };\n\n /**\n * Hiển thị Snackbar thông báo trạng thái mạng\n */\n #showReloadSnackbar = (message: string, options?: { duration?: number; isSticky?: boolean }): void => {\n // Đóng snackbar cũ nếu đang hiện\n if (this.#snackBarRef) {\n this.#snackBarRef.dismiss();\n }\n\n this.#snackBarRef = this.snackBar.open(message, this.#i18n.t('core.common.reload'), {\n horizontalPosition: 'center',\n verticalPosition: 'top',\n // Nếu isSticky = true (mất mạng) -> Không tự tắt. Nếu có duration -> tự tắt.\n duration: options?.duration,\n panelClass: options?.isSticky ? ['offline-snackbar'] : undefined, // Class CSS tùy chọn\n });\n\n // Xử lý sự kiện bấm nút \"Tải lại trang\"\n this.#snackBarRef.onAction().subscribe(() => {\n window.location.reload();\n });\n };\n\n /**\n * Bắt đầu vòng lặp kiểm tra kết nối mạng\n */\n #startPolling = (): void => {\n this.#stopPolling(); // Clear cũ nếu có\n\n this.#pollInterval = setInterval(() => {\n // Check nhẹ\n this.#pollSubscription?.unsubscribe();\n this.#pollSubscription = this.#probe().subscribe({\n next: () => {\n // --> ĐÃ CÓ MẠNG LẠI (2xx, hoặc bất kỳ status HTTP nào — xem #probe)\n this.#stopPolling();\n this.#isOffline = false;\n\n // Thông báo thành công (tự tắt sau 5s)\n this.#showReloadSnackbar(this.#i18n.t('core.interceptor.no-internet.restored'), { duration: 5000, isSticky: false });\n },\n error: () => {\n // --> VẪN MẤT MẠNG (status 0): Không làm gì cả, chờ lần check tiếp theo\n },\n });\n }, this.#checkIntervalDuration);\n };\n\n /**\n * Dừng vòng lặp kiểm tra\n */\n #stopPolling = (): void => {\n if (this.#pollInterval) {\n clearInterval(this.#pollInterval);\n this.#pollInterval = null;\n }\n this.#pollSubscription?.unsubscribe();\n this.#pollSubscription = null;\n };\n}\n","import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';\nimport { Injectable, inject } from '@angular/core';\nimport { SdAuthService } from '@sdcorejs/angular/modules';\nimport { Observable, throwError } from 'rxjs';\nimport { catchError } from 'rxjs/operators';\n\n@Injectable()\nexport class SdUnauthorizedInterceptor implements HttpInterceptor {\n private authService = inject(SdAuthService);\n\n /**\n * why: bản cũ dùng một boolean `#unauthorizedHandled` set một lần rồi KHÔNG bao giờ mở lại.\n * Hệ quả: sau lần 401 đầu tiên, mọi 401 còn lại trong vòng đời trang đều bị nuốt — người dùng\n * đăng nhập lại xong, phiên hết hạn lần hai thì không còn bị ép signout nữa. Tệ hơn: nếu\n * `SD_AUTH_CONFIGURATION.action.signout` không được cấu hình thì `SdAuthService.signout()` là\n * no-op, nên lần 401 đầu tiên đốt latch mà chẳng làm gì cả.\n *\n * Thay bằng một cửa sổ debounce ngắn: 401 song song trong cùng cửa sổ vẫn chỉ gọi signout một\n * lần (đúng mục đích ban đầu của latch), nhưng hết cửa sổ là latch tự mở lại.\n *\n * why: latch CHỈ mở lại theo thời gian. Bản trước còn reset `#lastSignoutAt` ở mỗi `HttpResponse`\n * thành công, nhưng một 2xx KHÔNG chứng minh phiên còn sống — endpoint công khai vẫn trả 200 khi\n * token đã hết hạn. Đúng kịch bản latch sinh ra để chặn (một burst request song song, vài cái\n * trúng endpoint công khai trả 2xx xen giữa những cái trả 401) sẽ gọi signout một lần cho MỖI 200\n * xen vào.\n */\n readonly #signoutDebounceMs = 3000;\n #lastSignoutAt: number | null = null;\n\n /** Inserted by Angular inject() migration for backwards compatibility */\n constructor(...args: unknown[]);\n\n constructor() {}\n\n intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {\n return next.handle(request).pipe(\n catchError((error: HttpErrorResponse) => {\n if (error.status === 401 && this.#shouldSignout()) {\n this.#lastSignoutAt = Date.now();\n this.authService.signout();\n }\n\n return throwError(() => error);\n })\n );\n }\n\n #shouldSignout = (): boolean => {\n return this.#lastSignoutAt === null || Date.now() - this.#lastSignoutAt >= this.#signoutDebounceMs;\n };\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, inject, signal, Injectable } from '@angular/core';
|
|
2
|
+
import { InjectionToken, inject, signal, Injectable, isDevMode } from '@angular/core';
|
|
3
3
|
import { Subject } from 'rxjs';
|
|
4
4
|
import { normalizeAsync, resolveMaybeAsync } from '@sdcorejs/utils/models';
|
|
5
5
|
import { toSignal } from '@angular/core/rxjs-interop';
|
|
@@ -23,19 +23,16 @@ class SdAuthService {
|
|
|
23
23
|
// this.#handleSignin();
|
|
24
24
|
this.#handleSignout();
|
|
25
25
|
this.#handleChangePassword();
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
};
|
|
26
|
+
// why: code cũ dựng sẵn một identity giả (`guest` / `guest@gmail.com`) và dùng nó vừa làm giá trị
|
|
27
|
+
// khi CHƯA cấu hình `guard.authInfo`, vừa làm `initialValue` trong lúc lookup thật còn pending.
|
|
28
|
+
// Hệ quả: template thấy một user "đã đăng nhập" hợp lệ và render UI của người dùng đã xác thực,
|
|
29
|
+
// dù thực tế chưa ai đăng nhập. `undefined` là trạng thái trung thực duy nhất cho cả hai ca —
|
|
30
|
+
// template bắt buộc phải xử lý nhánh chưa xác thực (`@if (user(); as u)`) thay vì tin vào user giả.
|
|
32
31
|
if (this.authConfiguration?.guard?.authInfo) {
|
|
33
|
-
this.getAuthInfo = toSignal(normalizeAsync(this.authConfiguration.guard?.authInfo())
|
|
34
|
-
initialValue: defaultUser,
|
|
35
|
-
});
|
|
32
|
+
this.getAuthInfo = toSignal(normalizeAsync(this.authConfiguration.guard?.authInfo()));
|
|
36
33
|
}
|
|
37
34
|
else {
|
|
38
|
-
this.getAuthInfo = signal(
|
|
35
|
+
this.getAuthInfo = signal(undefined, ...(ngDevMode ? [{ debugName: "getAuthInfo" }] : /* istanbul ignore next */ []));
|
|
39
36
|
}
|
|
40
37
|
}
|
|
41
38
|
// #handleSignin = () => {
|
|
@@ -76,8 +73,15 @@ class SdAuthGuard {
|
|
|
76
73
|
if (this.authConfiguration?.guard?.auth) {
|
|
77
74
|
return this.authConfiguration.guard.auth(route, state);
|
|
78
75
|
}
|
|
79
|
-
//
|
|
80
|
-
|
|
76
|
+
// why: đặt `SdAuthGuard` vào route table tức là consumer đã tuyên bố route ĐÓ cần bảo vệ. Code cũ
|
|
77
|
+
// trả `true` khi thiếu `SD_AUTH_CONFIGURATION.guard.auth`, nên chỉ cần quên một provider là toàn bộ
|
|
78
|
+
// route "được bảo vệ" trở thành công khai mà giao diện vẫn trông đúng — không có tín hiệu nào cả.
|
|
79
|
+
// Vì vậy mặc định là TỪ CHỐI (fail closed); ở dev mode log thật to để lỗi cấu hình lộ ra ngay.
|
|
80
|
+
if (isDevMode()) {
|
|
81
|
+
console.error('[SdAuthGuard] Navigation denied: SD_AUTH_CONFIGURATION.guard.auth is not configured. ' +
|
|
82
|
+
'Provide SD_AUTH_CONFIGURATION with a guard.auth callback, or remove SdAuthGuard from this route if it is meant to be public.');
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
81
85
|
};
|
|
82
86
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdAuthGuard, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
83
87
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdAuthGuard, providedIn: 'root' });
|
|
@@ -94,8 +98,14 @@ class SdPortalGuard {
|
|
|
94
98
|
if (this.authConfiguration?.guard?.portal) {
|
|
95
99
|
return this.authConfiguration.guard.portal(route, state);
|
|
96
100
|
}
|
|
97
|
-
//
|
|
98
|
-
|
|
101
|
+
// why: giống `SdAuthGuard` — guard có mặt trong route table là lời tuyên bố "route này cần kiểm
|
|
102
|
+
// tra". Trả `true` khi thiếu `SD_AUTH_CONFIGURATION.guard.portal` biến hàng rào portal thành vô
|
|
103
|
+
// hiệu một cách âm thầm. Fail closed + log to ở dev mode để phát hiện thiếu provider ngay lập tức.
|
|
104
|
+
if (isDevMode()) {
|
|
105
|
+
console.error('[SdPortalGuard] Navigation denied: SD_AUTH_CONFIGURATION.guard.portal is not configured. ' +
|
|
106
|
+
'Provide SD_AUTH_CONFIGURATION with a guard.portal callback, or remove SdPortalGuard from this route if it is meant to be public.');
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
99
109
|
};
|
|
100
110
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdPortalGuard, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
101
111
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdPortalGuard, providedIn: 'root' });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sdcorejs-angular-modules-auth.mjs","sources":["../../../projects/sdcorejs-angular/modules/auth/configurations/auth.configuration.ts","../../../projects/sdcorejs-angular/modules/auth/services/auth.service.ts","../../../projects/sdcorejs-angular/modules/auth/guards/auth.guard.ts","../../../projects/sdcorejs-angular/modules/auth/guards/portal.guard.ts","../../../projects/sdcorejs-angular/modules/auth/sdcorejs-angular-modules-auth.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\nimport { CanActivate } from '@angular/router';\nimport { MaybeAsync } from '@sdcorejs/utils/models';\nimport { SdAuthInfo } from '../services';\n\nexport interface ISdAuthConfiguration {\n action?: IAuthConfigurationAction;\n guard?: IAuthConfigurationGuard;\n}\n\nexport const SD_AUTH_CONFIGURATION = new InjectionToken<ISdAuthConfiguration>('sd.auth.configuration');\n\ninterface IAuthConfigurationAction {\n signout: () => MaybeAsync<void>;\n changePassword?: () => MaybeAsync<void>;\n}\n\ninterface IAuthConfigurationGuard {\n auth?: CanActivate['canActivate'];\n portal?: CanActivate['canActivate'];\n authInfo: () => MaybeAsync<SdAuthInfo>;\n}\n","import { inject, Injectable, signal, Signal } from '@angular/core';\nimport { Observable, Subject } from 'rxjs';\nimport { ISdAuthConfiguration, SD_AUTH_CONFIGURATION } from '../configurations';\nimport { normalizeAsync, resolveMaybeAsync } from '@sdcorejs/utils/models';\nimport { SdAuthInfo } from './auth.model';\nimport { toSignal } from '@angular/core/rxjs-interop';\n\n@Injectable({ providedIn: 'root' })\nexport class SdAuthService {\n // Đăng nhập\n // #signin = new Subject<void>();\n // signin$?: Observable<void>;\n getAuthInfo?: Signal<SdAuthInfo | undefined>;\n\n // Đăng xuất\n #signout = new Subject<void>();\n signout$?: Observable<void>;\n\n // Thay đổi mật khẩu\n #changePassword = new Subject<void>();\n changePassword$?: Observable<void>;\n private readonly authConfiguration: ISdAuthConfiguration | null = inject(SD_AUTH_CONFIGURATION, { optional: true });\n\n constructor() {\n // this.#handleSignin();\n this.#handleSignout();\n this.#handleChangePassword();\n
|
|
1
|
+
{"version":3,"file":"sdcorejs-angular-modules-auth.mjs","sources":["../../../projects/sdcorejs-angular/modules/auth/configurations/auth.configuration.ts","../../../projects/sdcorejs-angular/modules/auth/services/auth.service.ts","../../../projects/sdcorejs-angular/modules/auth/guards/auth.guard.ts","../../../projects/sdcorejs-angular/modules/auth/guards/portal.guard.ts","../../../projects/sdcorejs-angular/modules/auth/sdcorejs-angular-modules-auth.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\nimport { CanActivate } from '@angular/router';\nimport { MaybeAsync } from '@sdcorejs/utils/models';\nimport { SdAuthInfo } from '../services';\n\nexport interface ISdAuthConfiguration {\n action?: IAuthConfigurationAction;\n guard?: IAuthConfigurationGuard;\n}\n\nexport const SD_AUTH_CONFIGURATION = new InjectionToken<ISdAuthConfiguration>('sd.auth.configuration');\n\ninterface IAuthConfigurationAction {\n signout: () => MaybeAsync<void>;\n changePassword?: () => MaybeAsync<void>;\n}\n\ninterface IAuthConfigurationGuard {\n auth?: CanActivate['canActivate'];\n portal?: CanActivate['canActivate'];\n authInfo: () => MaybeAsync<SdAuthInfo>;\n}\n","import { inject, Injectable, signal, Signal } from '@angular/core';\nimport { Observable, Subject } from 'rxjs';\nimport { ISdAuthConfiguration, SD_AUTH_CONFIGURATION } from '../configurations';\nimport { normalizeAsync, resolveMaybeAsync } from '@sdcorejs/utils/models';\nimport { SdAuthInfo } from './auth.model';\nimport { toSignal } from '@angular/core/rxjs-interop';\n\n@Injectable({ providedIn: 'root' })\nexport class SdAuthService {\n // Đăng nhập\n // #signin = new Subject<void>();\n // signin$?: Observable<void>;\n getAuthInfo?: Signal<SdAuthInfo | undefined>;\n\n // Đăng xuất\n #signout = new Subject<void>();\n signout$?: Observable<void>;\n\n // Thay đổi mật khẩu\n #changePassword = new Subject<void>();\n changePassword$?: Observable<void>;\n private readonly authConfiguration: ISdAuthConfiguration | null = inject(SD_AUTH_CONFIGURATION, { optional: true });\n\n constructor() {\n // this.#handleSignin();\n this.#handleSignout();\n this.#handleChangePassword();\n // why: code cũ dựng sẵn một identity giả (`guest` / `guest@gmail.com`) và dùng nó vừa làm giá trị\n // khi CHƯA cấu hình `guard.authInfo`, vừa làm `initialValue` trong lúc lookup thật còn pending.\n // Hệ quả: template thấy một user \"đã đăng nhập\" hợp lệ và render UI của người dùng đã xác thực,\n // dù thực tế chưa ai đăng nhập. `undefined` là trạng thái trung thực duy nhất cho cả hai ca —\n // template bắt buộc phải xử lý nhánh chưa xác thực (`@if (user(); as u)`) thay vì tin vào user giả.\n if (this.authConfiguration?.guard?.authInfo) {\n this.getAuthInfo = toSignal(normalizeAsync(this.authConfiguration.guard?.authInfo()));\n } else {\n this.getAuthInfo = signal<SdAuthInfo | undefined>(undefined);\n }\n }\n\n // #handleSignin = () => {\n // this.signin$ = this.#signin.asObservable();\n // };\n\n #handleSignout = () => {\n this.signout$ = this.#signout.asObservable();\n };\n\n #handleChangePassword = () => {\n this.changePassword$ = this.#changePassword.asObservable();\n };\n signout = () => {\n if (this.authConfiguration?.action?.signout) {\n resolveMaybeAsync(this.authConfiguration.action.signout()).then(() => {\n this.#signout.next();\n });\n }\n };\n\n changePassword = () => {\n if (this.authConfiguration?.action?.changePassword) {\n resolveMaybeAsync(this.authConfiguration.action.changePassword()).then(() => {\n this.#changePassword.next();\n });\n }\n };\n}\n","import { inject, Injectable, isDevMode } from '@angular/core';\nimport { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';\nimport { ISdAuthConfiguration, SD_AUTH_CONFIGURATION } from '../configurations';\n\n@Injectable({ providedIn: 'root' })\nexport class SdAuthGuard implements CanActivate {\n private readonly authConfiguration: ISdAuthConfiguration | null = inject(SD_AUTH_CONFIGURATION, { optional: true });\n protected readonly router = inject(Router);\n\n canActivate = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {\n if (this.authConfiguration?.guard?.auth) {\n return this.authConfiguration.guard.auth(route, state);\n }\n\n // why: đặt `SdAuthGuard` vào route table tức là consumer đã tuyên bố route ĐÓ cần bảo vệ. Code cũ\n // trả `true` khi thiếu `SD_AUTH_CONFIGURATION.guard.auth`, nên chỉ cần quên một provider là toàn bộ\n // route \"được bảo vệ\" trở thành công khai mà giao diện vẫn trông đúng — không có tín hiệu nào cả.\n // Vì vậy mặc định là TỪ CHỐI (fail closed); ở dev mode log thật to để lỗi cấu hình lộ ra ngay.\n if (isDevMode()) {\n console.error(\n '[SdAuthGuard] Navigation denied: SD_AUTH_CONFIGURATION.guard.auth is not configured. ' +\n 'Provide SD_AUTH_CONFIGURATION with a guard.auth callback, or remove SdAuthGuard from this route if it is meant to be public.'\n );\n }\n return false;\n };\n}\n","import { inject, Injectable, isDevMode } from '@angular/core';\nimport { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';\nimport { ISdAuthConfiguration, SD_AUTH_CONFIGURATION } from '../configurations';\n\n@Injectable({ providedIn: 'root' })\nexport class SdPortalGuard implements CanActivate {\n private readonly authConfiguration: ISdAuthConfiguration | null = inject(SD_AUTH_CONFIGURATION, { optional: true });\n protected readonly router = inject(Router);\n\n canActivate = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {\n if (this.authConfiguration?.guard?.portal) {\n return this.authConfiguration.guard.portal(route, state);\n }\n\n // why: giống `SdAuthGuard` — guard có mặt trong route table là lời tuyên bố \"route này cần kiểm\n // tra\". Trả `true` khi thiếu `SD_AUTH_CONFIGURATION.guard.portal` biến hàng rào portal thành vô\n // hiệu một cách âm thầm. Fail closed + log to ở dev mode để phát hiện thiếu provider ngay lập tức.\n if (isDevMode()) {\n console.error(\n '[SdPortalGuard] Navigation denied: SD_AUTH_CONFIGURATION.guard.portal is not configured. ' +\n 'Provide SD_AUTH_CONFIGURATION with a guard.portal callback, or remove SdPortalGuard from this route if it is meant to be public.'\n );\n }\n return false;\n };\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -442,7 +442,7 @@ class SdIcon {
|
|
|
442
442
|
</mat-icon>
|
|
443
443
|
}
|
|
444
444
|
}
|
|
445
|
-
`, isInline: true, styles: [":host{display:inline-flex;align-items:center;justify-content:center;width:var(--sd-icon-size);height:var(--sd-icon-size);min-width:var(--sd-icon-size);color:currentColor;font-size:var(--sd-icon-size);line-height:1;vertical-align:middle;flex:0 0 auto}.sd-icon__material{display:inline-flex;align-items:center;justify-content:center;width:100%;height:100%;color:currentColor;font-size:inherit;line-height:1;overflow:hidden}.sd-icon__svg{display:block;width:100%;height:100%;color:currentColor;stroke:currentColor;flex:0 0 auto}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: LucideDynamicIcon, selector: "svg[lucideIcon]", inputs: ["lucideIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
445
|
+
`, isInline: true, styles: [":host{display:inline-flex;align-items:center;justify-content:center;width:var(--sd-icon-size);height:var(--sd-icon-size);min-width:var(--sd-icon-size);color:currentColor;font-size:var(--sd-icon-size);line-height:1;vertical-align:middle;flex:0 0 auto}:host .sd-icon__material{display:inline-flex;align-items:center;justify-content:center;width:100%;height:100%;margin:0;color:currentColor;font-size:inherit;line-height:1;overflow:hidden}.sd-icon__svg{display:block;width:100%;height:100%;color:currentColor;stroke:currentColor;flex:0 0 auto}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: LucideDynamicIcon, selector: "svg[lucideIcon]", inputs: ["lucideIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
446
446
|
}
|
|
447
447
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdIcon, decorators: [{
|
|
448
448
|
type: Component,
|
|
@@ -475,7 +475,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
|
|
|
475
475
|
'[attr.aria-label]': 'ariaLabel()',
|
|
476
476
|
'[attr.aria-hidden]': 'ariaLabel() ? null : "true"',
|
|
477
477
|
'[attr.color]': 'color()',
|
|
478
|
-
}, styles: [":host{display:inline-flex;align-items:center;justify-content:center;width:var(--sd-icon-size);height:var(--sd-icon-size);min-width:var(--sd-icon-size);color:currentColor;font-size:var(--sd-icon-size);line-height:1;vertical-align:middle;flex:0 0 auto}.sd-icon__material{display:inline-flex;align-items:center;justify-content:center;width:100%;height:100%;color:currentColor;font-size:inherit;line-height:1;overflow:hidden}.sd-icon__svg{display:block;width:100%;height:100%;color:currentColor;stroke:currentColor;flex:0 0 auto}\n"] }]
|
|
478
|
+
}, styles: [":host{display:inline-flex;align-items:center;justify-content:center;width:var(--sd-icon-size);height:var(--sd-icon-size);min-width:var(--sd-icon-size);color:currentColor;font-size:var(--sd-icon-size);line-height:1;vertical-align:middle;flex:0 0 auto}:host .sd-icon__material{display:inline-flex;align-items:center;justify-content:center;width:100%;height:100%;margin:0;color:currentColor;font-size:inherit;line-height:1;overflow:hidden}.sd-icon__svg{display:block;width:100%;height:100%;color:currentColor;stroke:currentColor;flex:0 0 auto}\n"] }]
|
|
479
479
|
}], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], fontIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "fontIcon", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], set: [{ type: i0.Input, args: [{ isSignal: true, alias: "set", required: false }] }], fontSet: [{ type: i0.Input, args: [{ isSignal: true, alias: "fontSet", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], strokeWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "strokeWidth", required: false }] }], absoluteStrokeWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "absoluteStrokeWidth", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
|
|
480
480
|
/**
|
|
481
481
|
* Chuẩn hóa string input rỗng thành `undefined` để fallback provider hoạt động nhất quán.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sdcorejs-angular-modules-icon.mjs","sources":["../../../projects/sdcorejs-angular/modules/icon/src/icon.model.ts","../../../projects/sdcorejs-angular/modules/icon/src/icon.provider.ts","../../../projects/sdcorejs-angular/modules/icon/src/icon.component.ts","../../../projects/sdcorejs-angular/modules/icon/sdcorejs-angular-modules-icon.ts"],"sourcesContent":["import { type LucideConfig, type LucideIcon, type LucideIconData } from '@lucide/angular';\nimport { type Size } from '@sdcorejs/utils/models';\nimport { DefaultSdIconSet, type SdIconSet, type SdMaterialIconSet } from '@sdcorejs/angular/utilities/models';\n\n/**\n * Chọn renderer icon chính cho `SdIcon`.\n *\n * Material dùng đúng tên font set để tránh thêm mapping nội bộ; `lucide` chuyển sang renderer SVG của `@lucide/angular`.\n */\nexport type { SdIconSet, SdMaterialIconSet };\n\n/** @deprecated Use `SdIconSet` instead. */\nexport type SdIconFontSet = SdIconSet;\n\n/**\n * Một icon Lucide có thể đăng ký bằng class icon Angular hoặc dữ liệu path thuần.\n */\nexport type SdLucideIconRegistration = LucideIcon | LucideIconData;\n\n/**\n * Danh sách đăng ký Lucide cho provider, hỗ trợ cả array và object map để app có thể giữ cấu trúc import riêng.\n */\nexport type SdLucideIconRegistrations = SdLucideIconRegistration[] | Record<string, SdLucideIconRegistration>;\n\n/**\n * Cấu hình app-level cho `SdIcon`.\n *\n * Các alias tách riêng theo renderer để app có thể giữ tên Material trong template trong lúc chuyển dần sang Lucide.\n */\nexport interface ISdIconConfiguration {\n /** Default icon font set when component does not pass `fontSet`. */\n defaultFontSet?: SdIconSet;\n /** Alias áp dụng khi renderer hiện tại là Material. */\n materialAliases?: Record<string, string>;\n /** Alias áp dụng khi renderer hiện tại là Lucide. */\n lucideAliases?: Record<string, string>;\n /** Cấu hình stroke toàn cục cho Lucide SVG. */\n lucideConfig?: Partial<LucideConfig>;\n /** Icon Lucide cần đăng ký vào `@lucide/angular`. */\n lucideIcons?: SdLucideIconRegistrations;\n}\n\n/**\n * Cấu hình đã resolve đầy đủ để component không phải tự xử lý fallback nhiều lần.\n */\nexport type ISdIconResolvedConfiguration = Required<Omit<ISdIconConfiguration, 'lucideIcons'>>;\n\n/**\n * Quy đổi token `Size` chung của SDCoreJS sang pixel cho icon.\n */\nexport const SD_ICON_SIZE_MAP: Record<Size, number> = {\n sm: 16,\n md: 20,\n lg: 24,\n};\n\n/**\n * Alias mặc định giúp template giữ tên Material trong khi renderer có thể là Lucide.\n */\nexport const SD_DEFAULT_LUCIDE_ALIASES: Record<string, string> = {\n account_balance: 'landmark',\n account_circle: 'circle-user-round',\n account_tree: 'workflow',\n add: 'plus',\n add_circle: 'circle-plus',\n arrow_back: 'arrow-left',\n arrow_downward: 'arrow-down',\n arrow_drop_down: 'chevron-down',\n arrow_drop_up: 'chevron-up',\n arrow_forward: 'arrow-right',\n arrow_upward: 'arrow-up',\n bar_chart: 'chart-column',\n block: 'ban',\n bookmark_add: 'bookmark-plus',\n bookmark_border: 'bookmark',\n build: 'wrench',\n calendar_month: 'calendar-days',\n campaign: 'megaphone',\n cancel: 'circle-x',\n celebration: 'party-popper',\n chevron_left: 'chevron-left',\n chevron_right: 'chevron-right',\n check: 'check',\n check_circle: 'circle-check',\n close: 'x',\n cloud_download: 'cloud-download',\n contact_page: 'contact',\n content_copy: 'copy',\n dashboard: 'layout-dashboard',\n delete: 'trash-2',\n delete_sweep: 'trash-2',\n description: 'file-text',\n done: 'check',\n done_all: 'check-check',\n drag_indicator: 'grip-vertical',\n edit: 'pencil',\n edit_note: 'file-pen-line',\n error: 'circle-alert',\n error_outline: 'circle-alert',\n expand_less: 'chevron-up',\n expand_more: 'chevron-down',\n filter_alt: 'list-filter',\n file_download: 'download',\n file_upload: 'upload',\n fiber_manual_record: 'circle',\n fit_screen: 'scan',\n folder: 'folder',\n first_page: 'chevrons-left',\n format_indent_decrease: 'list-indent-decrease',\n format_indent_increase: 'list-indent-increase',\n fullscreen: 'maximize',\n fullscreen_exit: 'minimize',\n get_app: 'download',\n group: 'users',\n hourglass_empty: 'hourglass',\n hourglass_top: 'hourglass',\n image: 'image',\n image_not_supported: 'image-off',\n info: 'info',\n info_outline: 'info',\n insert_drive_file: 'file',\n inventory_2: 'package',\n keyboard_arrow_down: 'chevron-down',\n keyboard_arrow_left: 'chevron-left',\n keyboard_arrow_right: 'chevron-right',\n keyboard_arrow_up: 'chevron-up',\n last_page: 'chevrons-right',\n list: 'list',\n local_offer: 'tag',\n lock: 'lock-keyhole',\n lock_outline: 'lock-keyhole',\n login: 'log-in',\n logout: 'log-out',\n match_case: 'case-sensitive',\n menu: 'menu',\n menu_open: 'panel-left-close',\n more_horiz: 'ellipsis',\n more_vert: 'ellipsis-vertical',\n notifications: 'bell',\n open_in_new: 'external-link',\n open_in_full: 'maximize',\n person: 'user',\n picture_as_pdf: 'file-text',\n play_arrow: 'play',\n print: 'printer',\n refresh: 'refresh-cw',\n remove: 'minus',\n remove_circle: 'circle-minus',\n restart_alt: 'rotate-ccw',\n rotate_right: 'rotate-cw',\n search_off: 'search-x',\n settings: 'settings',\n share: 'share-2',\n smart_toy: 'bot',\n space_dashboard: 'layout-dashboard',\n storefront: 'store',\n swap_horiz: 'arrow-left-right',\n table_chart: 'table',\n table_view: 'table-2',\n task_alt: 'circle-check',\n text_fields: 'text',\n today: 'calendar',\n unfold_more: 'chevrons-up-down',\n upload_file: 'upload',\n view_carousel: 'gallery-horizontal',\n view_day: 'panel-top',\n view_list: 'list',\n visibility: 'eye',\n visibility_off: 'eye-off',\n warning: 'triangle-alert',\n warning_amber: 'triangle-alert',\n widgets: 'layout-grid',\n zoom_in: 'zoom-in',\n};\n\n/**\n * Alias mac dinh giup template dung ten Lucide van render duoc khi quay ve Material font.\n */\nexport const SD_DEFAULT_MATERIAL_ALIASES: Record<string, string> = {\n 'arrow-down': 'arrow_downward',\n 'arrow-left': 'arrow_back',\n 'arrow-right': 'arrow_forward',\n 'arrow-up': 'arrow_upward',\n 'calendar-days': 'calendar_month',\n 'case-sensitive': 'match_case',\n 'chart-column': 'bar_chart',\n 'check-check': 'done_all',\n 'chevron-down': 'expand_more',\n 'chevron-left': 'chevron_left',\n 'chevron-right': 'chevron_right',\n 'chevron-up': 'expand_less',\n 'chevrons-left': 'first_page',\n 'chevrons-right': 'last_page',\n 'circle-alert': 'error',\n 'circle-check': 'check_circle',\n 'circle-minus': 'remove_circle',\n 'circle-plus': 'add_circle',\n 'circle-user-round': 'account_circle',\n 'circle-x': 'cancel',\n 'cloud-download': 'cloud_download',\n 'file-down': 'file_download',\n 'file-pen-line': 'edit_note',\n 'file-text': 'description',\n 'file-up': 'upload_file',\n 'gallery-horizontal': 'view_carousel',\n get_app: 'file_download',\n 'grip-vertical': 'drag_indicator',\n 'layout-dashboard': 'dashboard',\n 'layout-grid': 'widgets',\n 'list-indent-decrease': 'format_indent_decrease',\n 'list-indent-increase': 'format_indent_increase',\n 'list-filter': 'filter_alt',\n 'lock-keyhole': 'lock',\n 'log-in': 'login',\n 'log-out': 'logout',\n 'panel-left-close': 'menu_open',\n 'party-popper': 'celebration',\n 'refresh-cw': 'refresh',\n 'rotate-ccw': 'restart_alt',\n 'rotate-cw': 'rotate_right',\n 'share-2': 'share',\n 'square-pen': 'edit',\n 'trash-2': 'delete',\n 'triangle-alert': 'warning',\n 'zoom-in': 'zoom_in',\n ban: 'block',\n contact: 'contact_page',\n copy: 'content_copy',\n circle: 'fiber_manual_record',\n download: 'file_download',\n ellipsis: 'more_horiz',\n 'ellipsis-vertical': 'more_vert',\n error_outline: 'error',\n eye: 'visibility',\n 'eye-off': 'visibility_off',\n file: 'insert_drive_file',\n folder: 'folder',\n fullscreen: 'fullscreen',\n hourglass: 'hourglass_top',\n image: 'image',\n 'image-off': 'image_not_supported',\n info_outline: 'info',\n landmark: 'account_balance',\n list: 'list',\n lock_outline: 'lock',\n maximize: 'fullscreen',\n megaphone: 'campaign',\n menu: 'menu',\n minimize: 'fullscreen_exit',\n minus: 'remove',\n package: 'inventory_2',\n pencil: 'edit',\n plus: 'add',\n play: 'play_arrow',\n printer: 'print',\n scan: 'fit_screen',\n 'search-x': 'search_off',\n settings: 'settings',\n 'save-all': 'done_all',\n store: 'storefront',\n tag: 'local_offer',\n text: 'text_fields',\n upload: 'upload_file',\n user: 'person',\n users: 'group',\n workflow: 'account_tree',\n wrench: 'build',\n x: 'close',\n};\n\n/**\n * Cấu hình mặc định của Core UI icon.\n *\n * `material-icons-outlined` được giữ làm mặc định để không đổi visual của các component Material hiện có.\n */\nexport const SD_ICON_DEFAULT_CONFIG: ISdIconResolvedConfiguration = {\n defaultFontSet: DefaultSdIconSet,\n materialAliases: SD_DEFAULT_MATERIAL_ALIASES,\n lucideAliases: SD_DEFAULT_LUCIDE_ALIASES,\n lucideConfig: {},\n};\n","import { InjectionToken, makeEnvironmentProviders, type EnvironmentProviders, type Provider } from '@angular/core';\nimport { provideLucideConfig, provideLucideIcons } from '@lucide/angular';\nimport {\n SD_ICON_DEFAULT_CONFIG,\n type ISdIconConfiguration,\n type ISdIconResolvedConfiguration,\n type SdLucideIconRegistration,\n type SdLucideIconRegistrations,\n} from './icon.model';\n\n/**\n * Injection token chứa cấu hình icon đã resolve cho toàn bộ app.\n *\n * Token có factory mặc định để `SdIcon` vẫn chạy khi app chưa gọi `provideSdIcon`.\n */\nexport const SD_ICON_CONFIGURATION = new InjectionToken<ISdIconResolvedConfiguration>('SD_ICON_CONFIGURATION', {\n providedIn: 'root',\n factory: () => resolveSdIconConfig(),\n});\n\n/**\n * Đăng ký cấu hình icon Core UI ở app-level.\n *\n * Provider này gom cả cấu hình `SdIcon` và registry của `@lucide/angular` để consumer chỉ cần gọi một hàm.\n *\n * @param config - cấu hình app-level cho Material/Lucide icon.\n * @returns environment providers dùng được trong `ApplicationConfig.providers`.\n */\nexport function provideSdIcon(config: ISdIconConfiguration = {}): EnvironmentProviders {\n const providers: Provider[] = [\n {\n provide: SD_ICON_CONFIGURATION,\n useValue: resolveSdIconConfig(config),\n },\n ];\n\n if (config.lucideConfig) {\n providers.push(provideLucideConfig(config.lucideConfig));\n }\n\n const lucideIcons = normalizeSdLucideIcons(config.lucideIcons);\n if (lucideIcons.length) {\n // why: @lucide/angular chỉ render dynamic icon đã được đăng ký trước qua provider\n providers.push(provideLucideIcons(...lucideIcons));\n }\n\n return makeEnvironmentProviders(providers);\n}\n\n/**\n * Resolve cấu hình `SdIcon` thành object đầy đủ, không còn field optional.\n *\n * Alias mặc định đứng trước để app có thể override từng key mà không phải copy toàn bộ bảng alias.\n *\n * @param config - cấu hình rời rạc từ consumer hoặc mặc định rỗng.\n * @returns cấu hình đầy đủ dùng trực tiếp trong component.\n */\nexport function resolveSdIconConfig(config: ISdIconConfiguration = {}): ISdIconResolvedConfiguration {\n return {\n defaultFontSet: config.defaultFontSet ?? SD_ICON_DEFAULT_CONFIG.defaultFontSet,\n materialAliases: {\n ...SD_ICON_DEFAULT_CONFIG.materialAliases,\n ...config.materialAliases,\n },\n lucideAliases: {\n ...SD_ICON_DEFAULT_CONFIG.lucideAliases,\n ...config.lucideAliases,\n },\n lucideConfig: {\n ...SD_ICON_DEFAULT_CONFIG.lucideConfig,\n ...config.lucideConfig,\n },\n };\n}\n\n/**\n * Chuẩn hóa nhiều kiểu khai báo Lucide icon về array cho `provideLucideIcons`.\n *\n * @param registrations - array hoặc object map icon Lucide từ consumer.\n * @returns danh sách icon Lucide đã sẵn sàng để spread vào provider của Lucide.\n */\nexport function normalizeSdLucideIcons(registrations?: SdLucideIconRegistrations): SdLucideIconRegistration[] {\n if (!registrations) {\n return [];\n }\n\n return Array.isArray(registrations) ? registrations : Object.values(registrations);\n}\n","import { ChangeDetectionStrategy, Component, booleanAttribute, computed, inject, input } from '@angular/core';\nimport { MatIconModule } from '@angular/material/icon';\nimport { LucideDynamicIcon } from '@lucide/angular';\nimport { type Size } from '@sdcorejs/utils/models';\nimport { DefaultSdMaterialIconSet } from '@sdcorejs/angular/utilities/models';\nimport { SD_ICON_SIZE_MAP, type SdIconSet, type SdMaterialIconSet } from './icon.model';\nimport { SD_ICON_CONFIGURATION } from './icon.provider';\n\n/**\n * Facade icon dùng chung cho Core UI.\n *\n * Component này giữ template app theo tên icon ổn định, còn renderer có thể đổi giữa Material font và Lucide SVG qua provider hoặc input.\n */\n@Component({\n selector: 'sd-icon',\n standalone: true,\n imports: [MatIconModule, LucideDynamicIcon],\n template: `\n @if (resolvedName(); as _name) {\n @if (resolvedFontSet() === 'lucide') {\n <svg\n class=\"sd-icon__svg\"\n [lucideIcon]=\"_name\"\n [size]=\"resolvedSize()\"\n [strokeWidth]=\"resolvedStrokeWidth()\"\n [absoluteStrokeWidth]=\"resolvedAbsoluteStrokeWidth()\"\n [title]=\"ariaLabel()\"></svg>\n } @else {\n <mat-icon\n class=\"sd-icon__material\"\n [fontSet]=\"resolvedMaterialFontSet()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-hidden]=\"ariaLabel() ? null : 'true'\">\n {{ _name }}\n </mat-icon>\n }\n }\n `,\n styles: [\n `\n :host {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--sd-icon-size);\n height: var(--sd-icon-size);\n min-width: var(--sd-icon-size);\n color: currentColor;\n font-size: var(--sd-icon-size);\n line-height: 1;\n vertical-align: middle;\n flex: 0 0 auto;\n }\n\n .sd-icon__material {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n color: currentColor;\n font-size: inherit;\n line-height: 1;\n overflow: hidden;\n }\n\n .sd-icon__svg {\n display: block;\n width: 100%;\n height: 100%;\n color: currentColor;\n stroke: currentColor;\n flex: 0 0 auto;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: 'sd-icon',\n '[class.sd-icon--material]': 'resolvedFontSet() !== \"lucide\"',\n '[class.sd-icon--material-icons-outlined]': 'resolvedFontSet() === \"material-icons-outlined\"',\n '[class.sd-icon--lucide]': 'resolvedFontSet() === \"lucide\"',\n '[style.--sd-icon-size]': 'resolvedCssSize()',\n '[attr.aria-label]': 'ariaLabel()',\n '[attr.aria-hidden]': 'ariaLabel() ? null : \"true\"',\n '[attr.color]': 'color()',\n },\n})\nexport class SdIcon {\n // why: InjectionToken cho phép app đổi default/alias mà không buộc từng component nhận thêm input cấu hình\n private readonly config = inject(SD_ICON_CONFIGURATION);\n\n /**\n * Tên icon theo namespace hiện tại.\n *\n * Khi `set` là `lucide`, tên này sẽ đi qua alias để app có thể tiếp tục dùng tên Material trong template.\n */\n name = input<string | undefined, string | undefined | null>(undefined, {\n transform: coerceStringInput,\n });\n\n fontIcon = input<string | undefined, string | undefined | null>(undefined, {\n transform: coerceStringInput,\n });\n\n color = input<string | undefined, string | undefined | null>(undefined, {\n transform: coerceStringInput,\n });\n\n /**\n * Renderer icon của riêng instance này.\n *\n * Deprecated alias for `fontSet`.\n */\n set = input<SdIconSet | undefined, SdIconSet | undefined | null>(undefined, {\n transform: value => value ?? undefined,\n });\n\n /**\n * Escape hatch cho Material icon font set.\n *\n * Prefer this input for both Material font families and Lucide SVG icons.\n */\n fontSet = input<SdIconSet | undefined, SdIconSet | undefined | null>(undefined, {\n transform: value => value ?? undefined,\n });\n\n /**\n * Kích thước icon theo token SDCoreJS hoặc CSS size tùy biến.\n *\n * Token `sm | md | lg` giữ icon đồng bộ với các component Core UI; CSS string xử lý các ngoại lệ layout.\n */\n size = input<Size | (string & {}), Size | (string & {}) | undefined | null>('md', {\n transform: value => value ?? 'md',\n });\n\n /**\n * Độ dày stroke riêng cho Lucide SVG.\n *\n * Bỏ trống để nhận giá trị từ `lucideConfig` app-level.\n */\n strokeWidth = input<number | string | undefined, number | string | undefined | null>(undefined, {\n transform: value => value ?? undefined,\n });\n\n /**\n * Giữ stroke width tuyệt đối cho Lucide khi SVG đổi kích thước.\n *\n * Input này đi qua `booleanAttribute` để hỗ trợ cả attribute trần trong template.\n */\n absoluteStrokeWidth = input<boolean | undefined, boolean | string | undefined | null>(undefined, {\n transform: value => (value == null ? undefined : booleanAttribute(value)),\n });\n\n /**\n * Nhãn truy cập cho icon có ý nghĩa.\n *\n * Khi không có nhãn, component tự đánh dấu decorative bằng `aria-hidden`.\n */\n ariaLabel = input<string | undefined, string | undefined | null>(undefined, {\n transform: coerceStringInput,\n });\n\n /**\n * Set icon cuối cùng sau khi áp dụng input và provider.\n *\n * @returns renderer hiện tại của component.\n */\n resolvedFontSet = computed<SdIconSet>(() => this.fontSet() ?? this.set() ?? this.config.defaultFontSet);\n\n /** @deprecated Use `resolvedFontSet` instead. */\n resolvedSet = computed<SdIconSet>(() => this.resolvedFontSet());\n\n /**\n * Tên icon cuối cùng theo renderer hiện tại.\n *\n * @returns tên icon đã áp alias nếu renderer yêu cầu.\n */\n resolvedName = computed(() => {\n const name = this.name() ?? this.fontIcon();\n if (!name) {\n return undefined;\n }\n\n const aliases = this.resolvedFontSet() === 'lucide' ? this.config.lucideAliases : this.config.materialAliases;\n return aliases[name] ?? name;\n });\n\n /**\n * Font set Material cuối cùng.\n *\n * @returns font set dùng cho `mat-icon`; với Lucide trả về fallback Material để computed luôn có type ổn định.\n */\n resolvedMaterialFontSet = computed<SdMaterialIconSet>(() => {\n const fontSet = this.resolvedFontSet();\n return fontSet === 'lucide' ? DefaultSdMaterialIconSet : fontSet;\n });\n\n /**\n * Kích thước runtime cho renderer hiện tại.\n *\n * @returns số pixel cho token chuẩn hoặc CSS string nguyên bản cho custom size.\n */\n resolvedSize = computed<number | string>(() => {\n const size = this.size();\n return isSizeToken(size) ? SD_ICON_SIZE_MAP[size] : size;\n });\n\n /**\n * Kích thước CSS cuối cùng cho host element.\n *\n * @returns CSS length dùng cho custom property `--sd-icon-size`.\n */\n resolvedCssSize = computed(() => {\n const size = this.resolvedSize();\n return typeof size === 'number' ? `${size}px` : size;\n });\n\n /**\n * Stroke width cuối cùng cho Lucide SVG.\n *\n * @returns giá trị input instance hoặc fallback từ provider.\n */\n resolvedStrokeWidth = computed(() => this.strokeWidth() ?? this.config.lucideConfig.strokeWidth);\n\n /**\n * Absolute stroke width cuối cùng cho Lucide SVG.\n *\n * @returns giá trị input instance hoặc fallback từ provider.\n */\n resolvedAbsoluteStrokeWidth = computed(() => this.absoluteStrokeWidth() ?? this.config.lucideConfig.absoluteStrokeWidth);\n}\n\n/**\n * Chuẩn hóa string input rỗng thành `undefined` để fallback provider hoạt động nhất quán.\n *\n * @param value - giá trị string từ input Angular.\n * @returns string đã trim hoặc `undefined`.\n */\nfunction coerceStringInput(value: string | undefined | null): string | undefined {\n const trimmed = value?.trim();\n return trimmed || undefined;\n}\n\n/**\n * Kiểm tra một size có phải token chuẩn của SDCoreJS hay không.\n *\n * @param value - token size hoặc CSS size string.\n * @returns `true` khi value có mapping pixel trong `SD_ICON_SIZE_MAP`.\n */\nfunction isSizeToken(value: Size | (string & {})): value is Size {\n return value in SD_ICON_SIZE_MAP;\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-modules-icon.mjs","sources":["../../../projects/sdcorejs-angular/modules/icon/src/icon.model.ts","../../../projects/sdcorejs-angular/modules/icon/src/icon.provider.ts","../../../projects/sdcorejs-angular/modules/icon/src/icon.component.ts","../../../projects/sdcorejs-angular/modules/icon/sdcorejs-angular-modules-icon.ts"],"sourcesContent":["import { type LucideConfig, type LucideIcon, type LucideIconData } from '@lucide/angular';\nimport { type Size } from '@sdcorejs/utils/models';\nimport { DefaultSdIconSet, type SdIconSet, type SdMaterialIconSet } from '@sdcorejs/angular/utilities/models';\n\n/**\n * Chọn renderer icon chính cho `SdIcon`.\n *\n * Material dùng đúng tên font set để tránh thêm mapping nội bộ; `lucide` chuyển sang renderer SVG của `@lucide/angular`.\n */\nexport type { SdIconSet, SdMaterialIconSet };\n\n/** @deprecated Use `SdIconSet` instead. */\nexport type SdIconFontSet = SdIconSet;\n\n/**\n * Một icon Lucide có thể đăng ký bằng class icon Angular hoặc dữ liệu path thuần.\n */\nexport type SdLucideIconRegistration = LucideIcon | LucideIconData;\n\n/**\n * Danh sách đăng ký Lucide cho provider, hỗ trợ cả array và object map để app có thể giữ cấu trúc import riêng.\n */\nexport type SdLucideIconRegistrations = SdLucideIconRegistration[] | Record<string, SdLucideIconRegistration>;\n\n/**\n * Cấu hình app-level cho `SdIcon`.\n *\n * Các alias tách riêng theo renderer để app có thể giữ tên Material trong template trong lúc chuyển dần sang Lucide.\n */\nexport interface ISdIconConfiguration {\n /** Default icon font set when component does not pass `fontSet`. */\n defaultFontSet?: SdIconSet;\n /** Alias áp dụng khi renderer hiện tại là Material. */\n materialAliases?: Record<string, string>;\n /** Alias áp dụng khi renderer hiện tại là Lucide. */\n lucideAliases?: Record<string, string>;\n /** Cấu hình stroke toàn cục cho Lucide SVG. */\n lucideConfig?: Partial<LucideConfig>;\n /** Icon Lucide cần đăng ký vào `@lucide/angular`. */\n lucideIcons?: SdLucideIconRegistrations;\n}\n\n/**\n * Cấu hình đã resolve đầy đủ để component không phải tự xử lý fallback nhiều lần.\n */\nexport type ISdIconResolvedConfiguration = Required<Omit<ISdIconConfiguration, 'lucideIcons'>>;\n\n/**\n * Quy đổi token `Size` chung của SDCoreJS sang pixel cho icon.\n */\nexport const SD_ICON_SIZE_MAP: Record<Size, number> = {\n sm: 16,\n md: 20,\n lg: 24,\n};\n\n/**\n * Alias mặc định giúp template giữ tên Material trong khi renderer có thể là Lucide.\n */\nexport const SD_DEFAULT_LUCIDE_ALIASES: Record<string, string> = {\n account_balance: 'landmark',\n account_circle: 'circle-user-round',\n account_tree: 'workflow',\n add: 'plus',\n add_circle: 'circle-plus',\n arrow_back: 'arrow-left',\n arrow_downward: 'arrow-down',\n arrow_drop_down: 'chevron-down',\n arrow_drop_up: 'chevron-up',\n arrow_forward: 'arrow-right',\n arrow_upward: 'arrow-up',\n bar_chart: 'chart-column',\n block: 'ban',\n bookmark_add: 'bookmark-plus',\n bookmark_border: 'bookmark',\n build: 'wrench',\n calendar_month: 'calendar-days',\n campaign: 'megaphone',\n cancel: 'circle-x',\n celebration: 'party-popper',\n chevron_left: 'chevron-left',\n chevron_right: 'chevron-right',\n check: 'check',\n check_circle: 'circle-check',\n close: 'x',\n cloud_download: 'cloud-download',\n contact_page: 'contact',\n content_copy: 'copy',\n dashboard: 'layout-dashboard',\n delete: 'trash-2',\n delete_sweep: 'trash-2',\n description: 'file-text',\n done: 'check',\n done_all: 'check-check',\n drag_indicator: 'grip-vertical',\n edit: 'pencil',\n edit_note: 'file-pen-line',\n error: 'circle-alert',\n error_outline: 'circle-alert',\n expand_less: 'chevron-up',\n expand_more: 'chevron-down',\n filter_alt: 'list-filter',\n file_download: 'download',\n file_upload: 'upload',\n fiber_manual_record: 'circle',\n fit_screen: 'scan',\n folder: 'folder',\n first_page: 'chevrons-left',\n format_indent_decrease: 'list-indent-decrease',\n format_indent_increase: 'list-indent-increase',\n fullscreen: 'maximize',\n fullscreen_exit: 'minimize',\n get_app: 'download',\n group: 'users',\n hourglass_empty: 'hourglass',\n hourglass_top: 'hourglass',\n image: 'image',\n image_not_supported: 'image-off',\n info: 'info',\n info_outline: 'info',\n insert_drive_file: 'file',\n inventory_2: 'package',\n keyboard_arrow_down: 'chevron-down',\n keyboard_arrow_left: 'chevron-left',\n keyboard_arrow_right: 'chevron-right',\n keyboard_arrow_up: 'chevron-up',\n last_page: 'chevrons-right',\n list: 'list',\n local_offer: 'tag',\n lock: 'lock-keyhole',\n lock_outline: 'lock-keyhole',\n login: 'log-in',\n logout: 'log-out',\n match_case: 'case-sensitive',\n menu: 'menu',\n menu_open: 'panel-left-close',\n more_horiz: 'ellipsis',\n more_vert: 'ellipsis-vertical',\n notifications: 'bell',\n open_in_new: 'external-link',\n open_in_full: 'maximize',\n person: 'user',\n picture_as_pdf: 'file-text',\n play_arrow: 'play',\n print: 'printer',\n refresh: 'refresh-cw',\n remove: 'minus',\n remove_circle: 'circle-minus',\n restart_alt: 'rotate-ccw',\n rotate_right: 'rotate-cw',\n search_off: 'search-x',\n settings: 'settings',\n share: 'share-2',\n smart_toy: 'bot',\n space_dashboard: 'layout-dashboard',\n storefront: 'store',\n swap_horiz: 'arrow-left-right',\n table_chart: 'table',\n table_view: 'table-2',\n task_alt: 'circle-check',\n text_fields: 'text',\n today: 'calendar',\n unfold_more: 'chevrons-up-down',\n upload_file: 'upload',\n view_carousel: 'gallery-horizontal',\n view_day: 'panel-top',\n view_list: 'list',\n visibility: 'eye',\n visibility_off: 'eye-off',\n warning: 'triangle-alert',\n warning_amber: 'triangle-alert',\n widgets: 'layout-grid',\n zoom_in: 'zoom-in',\n};\n\n/**\n * Alias mac dinh giup template dung ten Lucide van render duoc khi quay ve Material font.\n */\nexport const SD_DEFAULT_MATERIAL_ALIASES: Record<string, string> = {\n 'arrow-down': 'arrow_downward',\n 'arrow-left': 'arrow_back',\n 'arrow-right': 'arrow_forward',\n 'arrow-up': 'arrow_upward',\n 'calendar-days': 'calendar_month',\n 'case-sensitive': 'match_case',\n 'chart-column': 'bar_chart',\n 'check-check': 'done_all',\n 'chevron-down': 'expand_more',\n 'chevron-left': 'chevron_left',\n 'chevron-right': 'chevron_right',\n 'chevron-up': 'expand_less',\n 'chevrons-left': 'first_page',\n 'chevrons-right': 'last_page',\n 'circle-alert': 'error',\n 'circle-check': 'check_circle',\n 'circle-minus': 'remove_circle',\n 'circle-plus': 'add_circle',\n 'circle-user-round': 'account_circle',\n 'circle-x': 'cancel',\n 'cloud-download': 'cloud_download',\n 'file-down': 'file_download',\n 'file-pen-line': 'edit_note',\n 'file-text': 'description',\n 'file-up': 'upload_file',\n 'gallery-horizontal': 'view_carousel',\n get_app: 'file_download',\n 'grip-vertical': 'drag_indicator',\n 'layout-dashboard': 'dashboard',\n 'layout-grid': 'widgets',\n 'list-indent-decrease': 'format_indent_decrease',\n 'list-indent-increase': 'format_indent_increase',\n 'list-filter': 'filter_alt',\n 'lock-keyhole': 'lock',\n 'log-in': 'login',\n 'log-out': 'logout',\n 'panel-left-close': 'menu_open',\n 'party-popper': 'celebration',\n 'refresh-cw': 'refresh',\n 'rotate-ccw': 'restart_alt',\n 'rotate-cw': 'rotate_right',\n 'share-2': 'share',\n 'square-pen': 'edit',\n 'trash-2': 'delete',\n 'triangle-alert': 'warning',\n 'zoom-in': 'zoom_in',\n ban: 'block',\n contact: 'contact_page',\n copy: 'content_copy',\n circle: 'fiber_manual_record',\n download: 'file_download',\n ellipsis: 'more_horiz',\n 'ellipsis-vertical': 'more_vert',\n error_outline: 'error',\n eye: 'visibility',\n 'eye-off': 'visibility_off',\n file: 'insert_drive_file',\n folder: 'folder',\n fullscreen: 'fullscreen',\n hourglass: 'hourglass_top',\n image: 'image',\n 'image-off': 'image_not_supported',\n info_outline: 'info',\n landmark: 'account_balance',\n list: 'list',\n lock_outline: 'lock',\n maximize: 'fullscreen',\n megaphone: 'campaign',\n menu: 'menu',\n minimize: 'fullscreen_exit',\n minus: 'remove',\n package: 'inventory_2',\n pencil: 'edit',\n plus: 'add',\n play: 'play_arrow',\n printer: 'print',\n scan: 'fit_screen',\n 'search-x': 'search_off',\n settings: 'settings',\n 'save-all': 'done_all',\n store: 'storefront',\n tag: 'local_offer',\n text: 'text_fields',\n upload: 'upload_file',\n user: 'person',\n users: 'group',\n workflow: 'account_tree',\n wrench: 'build',\n x: 'close',\n};\n\n/**\n * Cấu hình mặc định của Core UI icon.\n *\n * `material-icons-outlined` được giữ làm mặc định để không đổi visual của các component Material hiện có.\n */\nexport const SD_ICON_DEFAULT_CONFIG: ISdIconResolvedConfiguration = {\n defaultFontSet: DefaultSdIconSet,\n materialAliases: SD_DEFAULT_MATERIAL_ALIASES,\n lucideAliases: SD_DEFAULT_LUCIDE_ALIASES,\n lucideConfig: {},\n};\n","import { InjectionToken, makeEnvironmentProviders, type EnvironmentProviders, type Provider } from '@angular/core';\nimport { provideLucideConfig, provideLucideIcons } from '@lucide/angular';\nimport {\n SD_ICON_DEFAULT_CONFIG,\n type ISdIconConfiguration,\n type ISdIconResolvedConfiguration,\n type SdLucideIconRegistration,\n type SdLucideIconRegistrations,\n} from './icon.model';\n\n/**\n * Injection token chứa cấu hình icon đã resolve cho toàn bộ app.\n *\n * Token có factory mặc định để `SdIcon` vẫn chạy khi app chưa gọi `provideSdIcon`.\n */\nexport const SD_ICON_CONFIGURATION = new InjectionToken<ISdIconResolvedConfiguration>('SD_ICON_CONFIGURATION', {\n providedIn: 'root',\n factory: () => resolveSdIconConfig(),\n});\n\n/**\n * Đăng ký cấu hình icon Core UI ở app-level.\n *\n * Provider này gom cả cấu hình `SdIcon` và registry của `@lucide/angular` để consumer chỉ cần gọi một hàm.\n *\n * @param config - cấu hình app-level cho Material/Lucide icon.\n * @returns environment providers dùng được trong `ApplicationConfig.providers`.\n */\nexport function provideSdIcon(config: ISdIconConfiguration = {}): EnvironmentProviders {\n const providers: Provider[] = [\n {\n provide: SD_ICON_CONFIGURATION,\n useValue: resolveSdIconConfig(config),\n },\n ];\n\n if (config.lucideConfig) {\n providers.push(provideLucideConfig(config.lucideConfig));\n }\n\n const lucideIcons = normalizeSdLucideIcons(config.lucideIcons);\n if (lucideIcons.length) {\n // why: @lucide/angular chỉ render dynamic icon đã được đăng ký trước qua provider\n providers.push(provideLucideIcons(...lucideIcons));\n }\n\n return makeEnvironmentProviders(providers);\n}\n\n/**\n * Resolve cấu hình `SdIcon` thành object đầy đủ, không còn field optional.\n *\n * Alias mặc định đứng trước để app có thể override từng key mà không phải copy toàn bộ bảng alias.\n *\n * @param config - cấu hình rời rạc từ consumer hoặc mặc định rỗng.\n * @returns cấu hình đầy đủ dùng trực tiếp trong component.\n */\nexport function resolveSdIconConfig(config: ISdIconConfiguration = {}): ISdIconResolvedConfiguration {\n return {\n defaultFontSet: config.defaultFontSet ?? SD_ICON_DEFAULT_CONFIG.defaultFontSet,\n materialAliases: {\n ...SD_ICON_DEFAULT_CONFIG.materialAliases,\n ...config.materialAliases,\n },\n lucideAliases: {\n ...SD_ICON_DEFAULT_CONFIG.lucideAliases,\n ...config.lucideAliases,\n },\n lucideConfig: {\n ...SD_ICON_DEFAULT_CONFIG.lucideConfig,\n ...config.lucideConfig,\n },\n };\n}\n\n/**\n * Chuẩn hóa nhiều kiểu khai báo Lucide icon về array cho `provideLucideIcons`.\n *\n * @param registrations - array hoặc object map icon Lucide từ consumer.\n * @returns danh sách icon Lucide đã sẵn sàng để spread vào provider của Lucide.\n */\nexport function normalizeSdLucideIcons(registrations?: SdLucideIconRegistrations): SdLucideIconRegistration[] {\n if (!registrations) {\n return [];\n }\n\n return Array.isArray(registrations) ? registrations : Object.values(registrations);\n}\n","import { ChangeDetectionStrategy, Component, booleanAttribute, computed, inject, input } from '@angular/core';\nimport { MatIconModule } from '@angular/material/icon';\nimport { LucideDynamicIcon } from '@lucide/angular';\nimport { type Size } from '@sdcorejs/utils/models';\nimport { DefaultSdMaterialIconSet } from '@sdcorejs/angular/utilities/models';\nimport { SD_ICON_SIZE_MAP, type SdIconSet, type SdMaterialIconSet } from './icon.model';\nimport { SD_ICON_CONFIGURATION } from './icon.provider';\n\n/**\n * Facade icon dùng chung cho Core UI.\n *\n * Component này giữ template app theo tên icon ổn định, còn renderer có thể đổi giữa Material font và Lucide SVG qua provider hoặc input.\n */\n@Component({\n selector: 'sd-icon',\n standalone: true,\n imports: [MatIconModule, LucideDynamicIcon],\n template: `\n @if (resolvedName(); as _name) {\n @if (resolvedFontSet() === 'lucide') {\n <svg\n class=\"sd-icon__svg\"\n [lucideIcon]=\"_name\"\n [size]=\"resolvedSize()\"\n [strokeWidth]=\"resolvedStrokeWidth()\"\n [absoluteStrokeWidth]=\"resolvedAbsoluteStrokeWidth()\"\n [title]=\"ariaLabel()\"></svg>\n } @else {\n <mat-icon\n class=\"sd-icon__material\"\n [fontSet]=\"resolvedMaterialFontSet()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-hidden]=\"ariaLabel() ? null : 'true'\">\n {{ _name }}\n </mat-icon>\n }\n }\n `,\n styles: [\n `\n :host {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--sd-icon-size);\n height: var(--sd-icon-size);\n min-width: var(--sd-icon-size);\n color: currentColor;\n font-size: var(--sd-icon-size);\n line-height: 1;\n vertical-align: middle;\n flex: 0 0 auto;\n }\n\n /* why: base CSS của Material nhắm thẳng .mat-icon con trong menu/list/button item và ép\n width/height 24px + margin-right 12px lên nó. Glyph khi đó to hơn host --sd-icon-size,\n bị overflow hidden xén mất và lệch sang trái — lỗi \"icon bị cắt đè\" trong mat-menu. Selector\n đi qua :host (0,3,0) để luôn thắng .mat-mdc-menu-item .mat-icon (0,2,0) bất kể thứ tự nạp\n stylesheet; khoảng cách với nhãn để menu/list lo ở cấp host, không nhét vào trong glyph. */\n :host .sd-icon__material {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n margin: 0;\n color: currentColor;\n font-size: inherit;\n line-height: 1;\n overflow: hidden;\n }\n\n .sd-icon__svg {\n display: block;\n width: 100%;\n height: 100%;\n color: currentColor;\n stroke: currentColor;\n flex: 0 0 auto;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: 'sd-icon',\n '[class.sd-icon--material]': 'resolvedFontSet() !== \"lucide\"',\n '[class.sd-icon--material-icons-outlined]': 'resolvedFontSet() === \"material-icons-outlined\"',\n '[class.sd-icon--lucide]': 'resolvedFontSet() === \"lucide\"',\n '[style.--sd-icon-size]': 'resolvedCssSize()',\n '[attr.aria-label]': 'ariaLabel()',\n '[attr.aria-hidden]': 'ariaLabel() ? null : \"true\"',\n '[attr.color]': 'color()',\n },\n})\nexport class SdIcon {\n // why: InjectionToken cho phép app đổi default/alias mà không buộc từng component nhận thêm input cấu hình\n private readonly config = inject(SD_ICON_CONFIGURATION);\n\n /**\n * Tên icon theo namespace hiện tại.\n *\n * Khi `set` là `lucide`, tên này sẽ đi qua alias để app có thể tiếp tục dùng tên Material trong template.\n */\n name = input<string | undefined, string | undefined | null>(undefined, {\n transform: coerceStringInput,\n });\n\n fontIcon = input<string | undefined, string | undefined | null>(undefined, {\n transform: coerceStringInput,\n });\n\n color = input<string | undefined, string | undefined | null>(undefined, {\n transform: coerceStringInput,\n });\n\n /**\n * Renderer icon của riêng instance này.\n *\n * Deprecated alias for `fontSet`.\n */\n set = input<SdIconSet | undefined, SdIconSet | undefined | null>(undefined, {\n transform: value => value ?? undefined,\n });\n\n /**\n * Escape hatch cho Material icon font set.\n *\n * Prefer this input for both Material font families and Lucide SVG icons.\n */\n fontSet = input<SdIconSet | undefined, SdIconSet | undefined | null>(undefined, {\n transform: value => value ?? undefined,\n });\n\n /**\n * Kích thước icon theo token SDCoreJS hoặc CSS size tùy biến.\n *\n * Token `sm | md | lg` giữ icon đồng bộ với các component Core UI; CSS string xử lý các ngoại lệ layout.\n */\n size = input<Size | (string & {}), Size | (string & {}) | undefined | null>('md', {\n transform: value => value ?? 'md',\n });\n\n /**\n * Độ dày stroke riêng cho Lucide SVG.\n *\n * Bỏ trống để nhận giá trị từ `lucideConfig` app-level.\n */\n strokeWidth = input<number | string | undefined, number | string | undefined | null>(undefined, {\n transform: value => value ?? undefined,\n });\n\n /**\n * Giữ stroke width tuyệt đối cho Lucide khi SVG đổi kích thước.\n *\n * Input này đi qua `booleanAttribute` để hỗ trợ cả attribute trần trong template.\n */\n absoluteStrokeWidth = input<boolean | undefined, boolean | string | undefined | null>(undefined, {\n transform: value => (value == null ? undefined : booleanAttribute(value)),\n });\n\n /**\n * Nhãn truy cập cho icon có ý nghĩa.\n *\n * Khi không có nhãn, component tự đánh dấu decorative bằng `aria-hidden`.\n */\n ariaLabel = input<string | undefined, string | undefined | null>(undefined, {\n transform: coerceStringInput,\n });\n\n /**\n * Set icon cuối cùng sau khi áp dụng input và provider.\n *\n * @returns renderer hiện tại của component.\n */\n resolvedFontSet = computed<SdIconSet>(() => this.fontSet() ?? this.set() ?? this.config.defaultFontSet);\n\n /** @deprecated Use `resolvedFontSet` instead. */\n resolvedSet = computed<SdIconSet>(() => this.resolvedFontSet());\n\n /**\n * Tên icon cuối cùng theo renderer hiện tại.\n *\n * @returns tên icon đã áp alias nếu renderer yêu cầu.\n */\n resolvedName = computed(() => {\n const name = this.name() ?? this.fontIcon();\n if (!name) {\n return undefined;\n }\n\n const aliases = this.resolvedFontSet() === 'lucide' ? this.config.lucideAliases : this.config.materialAliases;\n return aliases[name] ?? name;\n });\n\n /**\n * Font set Material cuối cùng.\n *\n * @returns font set dùng cho `mat-icon`; với Lucide trả về fallback Material để computed luôn có type ổn định.\n */\n resolvedMaterialFontSet = computed<SdMaterialIconSet>(() => {\n const fontSet = this.resolvedFontSet();\n return fontSet === 'lucide' ? DefaultSdMaterialIconSet : fontSet;\n });\n\n /**\n * Kích thước runtime cho renderer hiện tại.\n *\n * @returns số pixel cho token chuẩn hoặc CSS string nguyên bản cho custom size.\n */\n resolvedSize = computed<number | string>(() => {\n const size = this.size();\n return isSizeToken(size) ? SD_ICON_SIZE_MAP[size] : size;\n });\n\n /**\n * Kích thước CSS cuối cùng cho host element.\n *\n * @returns CSS length dùng cho custom property `--sd-icon-size`.\n */\n resolvedCssSize = computed(() => {\n const size = this.resolvedSize();\n return typeof size === 'number' ? `${size}px` : size;\n });\n\n /**\n * Stroke width cuối cùng cho Lucide SVG.\n *\n * @returns giá trị input instance hoặc fallback từ provider.\n */\n resolvedStrokeWidth = computed(() => this.strokeWidth() ?? this.config.lucideConfig.strokeWidth);\n\n /**\n * Absolute stroke width cuối cùng cho Lucide SVG.\n *\n * @returns giá trị input instance hoặc fallback từ provider.\n */\n resolvedAbsoluteStrokeWidth = computed(() => this.absoluteStrokeWidth() ?? this.config.lucideConfig.absoluteStrokeWidth);\n}\n\n/**\n * Chuẩn hóa string input rỗng thành `undefined` để fallback provider hoạt động nhất quán.\n *\n * @param value - giá trị string từ input Angular.\n * @returns string đã trim hoặc `undefined`.\n */\nfunction coerceStringInput(value: string | undefined | null): string | undefined {\n const trimmed = value?.trim();\n return trimmed || undefined;\n}\n\n/**\n * Kiểm tra một size có phải token chuẩn của SDCoreJS hay không.\n *\n * @param value - token size hoặc CSS size string.\n * @returns `true` khi value có mapping pixel trong `SD_ICON_SIZE_MAP`.\n */\nfunction isSizeToken(value: Size | (string & {})): value is Size {\n return value in SD_ICON_SIZE_MAP;\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|