@sdcorejs/angular 21.0.9 → 21.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/README.md +270 -140
  2. package/assets/scss/core/utilities/_grid.scss +25 -19
  3. package/fesm2022/sdcorejs-angular-components-anchor.mjs +15 -18
  4. package/fesm2022/sdcorejs-angular-components-anchor.mjs.map +1 -1
  5. package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs.map +1 -1
  6. package/fesm2022/sdcorejs-angular-components-avatar.mjs.map +1 -1
  7. package/fesm2022/sdcorejs-angular-components-badge.mjs +0 -1
  8. package/fesm2022/sdcorejs-angular-components-badge.mjs.map +1 -1
  9. package/fesm2022/sdcorejs-angular-components-button.mjs +6 -7
  10. package/fesm2022/sdcorejs-angular-components-button.mjs.map +1 -1
  11. package/fesm2022/sdcorejs-angular-components-chart.mjs.map +1 -1
  12. package/fesm2022/sdcorejs-angular-components-ckeditor-styles.mjs.map +1 -1
  13. package/fesm2022/sdcorejs-angular-components-code-editor.mjs +1 -1
  14. package/fesm2022/sdcorejs-angular-components-code-editor.mjs.map +1 -1
  15. package/fesm2022/sdcorejs-angular-components-document-builder.mjs +17 -26
  16. package/fesm2022/sdcorejs-angular-components-document-builder.mjs.map +1 -1
  17. package/fesm2022/sdcorejs-angular-components-editor.mjs +7 -2
  18. package/fesm2022/sdcorejs-angular-components-editor.mjs.map +1 -1
  19. package/fesm2022/sdcorejs-angular-components-form-generic.mjs +712 -427
  20. package/fesm2022/sdcorejs-angular-components-form-generic.mjs.map +1 -1
  21. package/fesm2022/sdcorejs-angular-components-import-excel.mjs +35 -18
  22. package/fesm2022/sdcorejs-angular-components-import-excel.mjs.map +1 -1
  23. package/fesm2022/sdcorejs-angular-components-inform.mjs +0 -1
  24. package/fesm2022/sdcorejs-angular-components-inform.mjs.map +1 -1
  25. package/fesm2022/sdcorejs-angular-components-mini-editor.mjs.map +1 -1
  26. package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs.map +1 -1
  27. package/fesm2022/sdcorejs-angular-components-modal.mjs +21 -14
  28. package/fesm2022/sdcorejs-angular-components-modal.mjs.map +1 -1
  29. package/fesm2022/sdcorejs-angular-components-operator.mjs +1 -1
  30. package/fesm2022/sdcorejs-angular-components-operator.mjs.map +1 -1
  31. package/fesm2022/sdcorejs-angular-components-org-chart.mjs.map +1 -1
  32. package/fesm2022/sdcorejs-angular-components-preview.mjs +33 -29
  33. package/fesm2022/sdcorejs-angular-components-preview.mjs.map +1 -1
  34. package/fesm2022/sdcorejs-angular-components-query-bar.mjs +15 -28
  35. package/fesm2022/sdcorejs-angular-components-query-bar.mjs.map +1 -1
  36. package/fesm2022/sdcorejs-angular-components-query-builder.mjs +2 -3
  37. package/fesm2022/sdcorejs-angular-components-query-builder.mjs.map +1 -1
  38. package/fesm2022/sdcorejs-angular-components-quick-action.mjs.map +1 -1
  39. package/fesm2022/sdcorejs-angular-components-section.mjs +0 -1
  40. package/fesm2022/sdcorejs-angular-components-section.mjs.map +1 -1
  41. package/fesm2022/sdcorejs-angular-components-side-drawer.mjs +3 -3
  42. package/fesm2022/sdcorejs-angular-components-side-drawer.mjs.map +1 -1
  43. package/fesm2022/sdcorejs-angular-components-splitter.mjs +4 -4
  44. package/fesm2022/sdcorejs-angular-components-splitter.mjs.map +1 -1
  45. package/fesm2022/sdcorejs-angular-components-stepper.mjs.map +1 -1
  46. package/fesm2022/sdcorejs-angular-components-tab-router.mjs +31 -31
  47. package/fesm2022/sdcorejs-angular-components-tab-router.mjs.map +1 -1
  48. package/fesm2022/sdcorejs-angular-components-tab.mjs.map +1 -1
  49. package/fesm2022/sdcorejs-angular-components-table.mjs +66 -84
  50. package/fesm2022/sdcorejs-angular-components-table.mjs.map +1 -1
  51. package/fesm2022/sdcorejs-angular-components-tree.mjs.map +1 -1
  52. package/fesm2022/sdcorejs-angular-components-upload-file.mjs +6 -6
  53. package/fesm2022/sdcorejs-angular-components-upload-file.mjs.map +1 -1
  54. package/fesm2022/sdcorejs-angular-components-view.mjs +3 -9
  55. package/fesm2022/sdcorejs-angular-components-view.mjs.map +1 -1
  56. package/fesm2022/sdcorejs-angular-configurations.mjs.map +1 -1
  57. package/fesm2022/sdcorejs-angular-directives.mjs +2 -7
  58. package/fesm2022/sdcorejs-angular-directives.mjs.map +1 -1
  59. package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs +0 -3
  60. package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs.map +1 -1
  61. package/fesm2022/sdcorejs-angular-forms-checkbox.mjs.map +1 -1
  62. package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs +2 -2
  63. package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs.map +1 -1
  64. package/fesm2022/sdcorejs-angular-forms-chip.mjs +2 -2
  65. package/fesm2022/sdcorejs-angular-forms-chip.mjs.map +1 -1
  66. package/fesm2022/sdcorejs-angular-forms-date-range.mjs +1 -3
  67. package/fesm2022/sdcorejs-angular-forms-date-range.mjs.map +1 -1
  68. package/fesm2022/sdcorejs-angular-forms-date.mjs +2 -4
  69. package/fesm2022/sdcorejs-angular-forms-date.mjs.map +1 -1
  70. package/fesm2022/sdcorejs-angular-forms-datetime.mjs +2 -6
  71. package/fesm2022/sdcorejs-angular-forms-datetime.mjs.map +1 -1
  72. package/fesm2022/sdcorejs-angular-forms-input-color.mjs +4 -4
  73. package/fesm2022/sdcorejs-angular-forms-input-color.mjs.map +1 -1
  74. package/fesm2022/sdcorejs-angular-forms-input-number.mjs +1 -1
  75. package/fesm2022/sdcorejs-angular-forms-input-number.mjs.map +1 -1
  76. package/fesm2022/sdcorejs-angular-forms-input.mjs.map +1 -1
  77. package/fesm2022/sdcorejs-angular-forms-label.mjs +0 -1
  78. package/fesm2022/sdcorejs-angular-forms-label.mjs.map +1 -1
  79. package/fesm2022/sdcorejs-angular-forms-models.mjs.map +1 -1
  80. package/fesm2022/sdcorejs-angular-forms-radio.mjs +12 -2
  81. package/fesm2022/sdcorejs-angular-forms-radio.mjs.map +1 -1
  82. package/fesm2022/sdcorejs-angular-forms-select.mjs +45 -14
  83. package/fesm2022/sdcorejs-angular-forms-select.mjs.map +1 -1
  84. package/fesm2022/sdcorejs-angular-forms-switch.mjs.map +1 -1
  85. package/fesm2022/sdcorejs-angular-forms-textarea.mjs +2 -2
  86. package/fesm2022/sdcorejs-angular-forms-textarea.mjs.map +1 -1
  87. package/fesm2022/sdcorejs-angular-handlers.mjs +3 -6
  88. package/fesm2022/sdcorejs-angular-handlers.mjs.map +1 -1
  89. package/fesm2022/sdcorejs-angular-i18n.mjs +11 -7
  90. package/fesm2022/sdcorejs-angular-i18n.mjs.map +1 -1
  91. package/fesm2022/sdcorejs-angular-interceptors.mjs +0 -2
  92. package/fesm2022/sdcorejs-angular-interceptors.mjs.map +1 -1
  93. package/fesm2022/sdcorejs-angular-modules-auth.mjs +14 -38
  94. package/fesm2022/sdcorejs-angular-modules-auth.mjs.map +1 -1
  95. package/fesm2022/sdcorejs-angular-modules-authom.mjs +14 -14
  96. package/fesm2022/sdcorejs-angular-modules-authom.mjs.map +1 -1
  97. package/fesm2022/sdcorejs-angular-modules-keycloak.mjs +22 -11
  98. package/fesm2022/sdcorejs-angular-modules-keycloak.mjs.map +1 -1
  99. package/fesm2022/sdcorejs-angular-modules-layout.mjs +30 -27
  100. package/fesm2022/sdcorejs-angular-modules-layout.mjs.map +1 -1
  101. package/fesm2022/sdcorejs-angular-modules-permission.mjs +5 -12
  102. package/fesm2022/sdcorejs-angular-modules-permission.mjs.map +1 -1
  103. package/fesm2022/sdcorejs-angular-pipes.mjs +71 -17
  104. package/fesm2022/sdcorejs-angular-pipes.mjs.map +1 -1
  105. package/fesm2022/sdcorejs-angular-services-api.mjs +0 -2
  106. package/fesm2022/sdcorejs-angular-services-api.mjs.map +1 -1
  107. package/fesm2022/sdcorejs-angular-services-cache.mjs +5 -11
  108. package/fesm2022/sdcorejs-angular-services-cache.mjs.map +1 -1
  109. package/fesm2022/sdcorejs-angular-services-confirm.mjs +16 -21
  110. package/fesm2022/sdcorejs-angular-services-confirm.mjs.map +1 -1
  111. package/fesm2022/sdcorejs-angular-services-docx.mjs +2 -8
  112. package/fesm2022/sdcorejs-angular-services-docx.mjs.map +1 -1
  113. package/fesm2022/sdcorejs-angular-services-excel.mjs +2 -2
  114. package/fesm2022/sdcorejs-angular-services-excel.mjs.map +1 -1
  115. package/fesm2022/sdcorejs-angular-services-license.mjs +5 -12
  116. package/fesm2022/sdcorejs-angular-services-license.mjs.map +1 -1
  117. package/fesm2022/sdcorejs-angular-services-notify.mjs +70 -87
  118. package/fesm2022/sdcorejs-angular-services-notify.mjs.map +1 -1
  119. package/fesm2022/sdcorejs-angular-services-storage.mjs +6 -15
  120. package/fesm2022/sdcorejs-angular-services-storage.mjs.map +1 -1
  121. package/fesm2022/sdcorejs-angular-utilities-data-state.mjs.map +1 -1
  122. package/fesm2022/sdcorejs-angular-utilities-extensions.mjs +47 -5
  123. package/fesm2022/sdcorejs-angular-utilities-extensions.mjs.map +1 -1
  124. package/fesm2022/sdcorejs-angular-utilities-models.mjs +18 -3
  125. package/fesm2022/sdcorejs-angular-utilities-models.mjs.map +1 -1
  126. package/fesm2022/sdcorejs-angular.mjs.map +1 -1
  127. package/package.json +2 -2
  128. package/types/sdcorejs-angular-components-document-builder.d.ts +2 -2
  129. package/types/sdcorejs-angular-components-form-generic.d.ts +45 -23
  130. package/types/sdcorejs-angular-components-import-excel.d.ts +2 -2
  131. package/types/sdcorejs-angular-components-mini-editor.d.ts +4 -4
  132. package/types/sdcorejs-angular-components-modal.d.ts +1 -1
  133. package/types/sdcorejs-angular-components-query-builder.d.ts +4 -4
  134. package/types/sdcorejs-angular-components-tab-router.d.ts +3 -3
  135. package/types/sdcorejs-angular-components-table.d.ts +3 -2
  136. package/types/sdcorejs-angular-components-view.d.ts +1 -1
  137. package/types/sdcorejs-angular-forms-chip-calendar.d.ts +2 -2
  138. package/types/sdcorejs-angular-forms-chip.d.ts +2 -2
  139. package/types/sdcorejs-angular-forms-input-color.d.ts +2 -1
  140. package/types/sdcorejs-angular-forms-select.d.ts +10 -2
  141. package/types/sdcorejs-angular-modules-auth.d.ts +7 -9
  142. package/types/sdcorejs-angular-modules-permission.d.ts +5 -6
  143. package/types/sdcorejs-angular-pipes.d.ts +22 -4
  144. package/types/sdcorejs-angular-services-confirm.d.ts +3 -3
  145. package/types/sdcorejs-angular-services-license.d.ts +2 -3
  146. package/types/sdcorejs-angular-services-notify.d.ts +4 -5
  147. package/types/sdcorejs-angular-services-storage.d.ts +2 -3
  148. package/types/sdcorejs-angular-utilities-extensions.d.ts +9 -1
package/README.md CHANGED
@@ -13,6 +13,7 @@
13
13
 
14
14
  - [Getting Started / Cài đặt](#getting-started--cài-đặt)
15
15
  - [Theming / SCSS Customization](#theming--scss-customization)
16
+ - [AI Agent Usage Guardrails](#ai-agent-usage-guardrails)
16
17
  - [Components](#components)
17
18
  - [SdButton](#sdbutton)
18
19
  - [SdBadge](#sdbadge)
@@ -31,12 +32,12 @@
31
32
 
32
33
  ### Prerequisites / Yêu cầu
33
34
 
34
- | Dependency | Version |
35
- |---|---|
36
- | `@angular/core` | `^19.0.0 \|\| ^20.0.0 \|\| ^21.0.0` |
37
- | `@angular/material` | `^19.0.0 \|\| ^20.0.0 \|\| ^21.0.0` |
35
+ | Dependency | Version |
36
+ | ------------------------------------ | ----------------------------------- |
37
+ | `@angular/core` | `^19.0.0 \|\| ^20.0.0 \|\| ^21.0.0` |
38
+ | `@angular/material` | `^19.0.0 \|\| ^20.0.0 \|\| ^21.0.0` |
38
39
  | `@angular/material-date-fns-adapter` | `^19.0.0 \|\| ^20.0.0 \|\| ^21.0.0` |
39
- | `date-fns` | `^3 \|\| ^4` |
40
+ | `date-fns` | `^3 \|\| ^4` |
40
41
 
41
42
  ### Installation / Cài đặt
42
43
 
@@ -53,9 +54,7 @@ Thêm vào `angular.json` (hoặc `styles.scss` của app):
53
54
  ```json
54
55
  // angular.json
55
56
  {
56
- "styles": [
57
- "node_modules/@sdcorejs/angular/assets/scss/sd-core.scss"
58
- ]
57
+ "styles": ["node_modules/@sdcorejs/angular/assets/scss/sd-core.scss"]
59
58
  }
60
59
  ```
61
60
 
@@ -98,26 +97,26 @@ export const appConfig: ApplicationConfig = {
98
97
 
99
98
  **Available color tokens / Các biến màu sắc:**
100
99
 
101
- | Variable | Default | Description |
102
- |---|---|---|
103
- | `--sd-primary` | `#2A66F4` | Màu chính |
104
- | `--sd-primary-light` | `#EAF1FF` | Màu chính nhạt |
105
- | `--sd-primary-dark` | `#1C4AD9` | Màu chính đậm |
106
- | `--sd-secondary` | `#212121` | Màu phụ |
107
- | `--sd-secondary-light` | `#E9E9E9` | Màu phụ nhạt |
108
- | `--sd-success` | `#4CAF50` | Thành công |
109
- | `--sd-success-light` | `#DBEFDC` | Thành công nhạt |
110
- | `--sd-warning` | `#FF9600` | Cảnh báo |
111
- | `--sd-warning-light` | `#FFEACC` | Cảnh báo nhạt |
112
- | `--sd-error` | `#F82C13` | Lỗi |
113
- | `--sd-error-light` | `#FED5D0` | Lỗi nhạt |
114
- | `--sd-info` | `#2962FF` | Thông tin |
115
- | `--sd-info-light` | `#E7E9FF` | Thông tin nhạt |
116
- | `--sd-black500` | `#212121` | Xám đậm nhất |
117
- | `--sd-black400` | `#757575` | Xám đậm |
118
- | `--sd-black300` | `#BFBFBF` | Xám trung |
119
- | `--sd-black200` | `#E6E6E6` | Xám nhạt |
120
- | `--sd-black100` | `#F2F2F2` | Xám nhạt nhất |
100
+ | Variable | Default | Description |
101
+ | ---------------------- | --------- | --------------- |
102
+ | `--sd-primary` | `#2A66F4` | Màu chính |
103
+ | `--sd-primary-light` | `#EAF1FF` | Màu chính nhạt |
104
+ | `--sd-primary-dark` | `#1C4AD9` | Màu chính đậm |
105
+ | `--sd-secondary` | `#212121` | Màu phụ |
106
+ | `--sd-secondary-light` | `#E9E9E9` | Màu phụ nhạt |
107
+ | `--sd-success` | `#4CAF50` | Thành công |
108
+ | `--sd-success-light` | `#DBEFDC` | Thành công nhạt |
109
+ | `--sd-warning` | `#FF9600` | Cảnh báo |
110
+ | `--sd-warning-light` | `#FFEACC` | Cảnh báo nhạt |
111
+ | `--sd-error` | `#F82C13` | Lỗi |
112
+ | `--sd-error-light` | `#FED5D0` | Lỗi nhạt |
113
+ | `--sd-info` | `#2962FF` | Thông tin |
114
+ | `--sd-info-light` | `#E7E9FF` | Thông tin nhạt |
115
+ | `--sd-black500` | `#212121` | Xám đậm nhất |
116
+ | `--sd-black400` | `#757575` | Xám đậm |
117
+ | `--sd-black300` | `#BFBFBF` | Xám trung |
118
+ | `--sd-black200` | `#E6E6E6` | Xám nhạt |
119
+ | `--sd-black100` | `#F2F2F2` | Xám nhạt nhất |
121
120
 
122
121
  ### Custom Theme / Tuỳ chỉnh theme
123
122
 
@@ -128,13 +127,15 @@ Ghi đè theme mặc định bằng cách truyền map SCSS vào mixin `theme()`
128
127
  @use '@sdcorejs/angular/assets/scss/themes/default' as default;
129
128
 
130
129
  html {
131
- @include default.theme((
132
- primary: #7C3AED,
133
- primary-light: #EDE9FE,
134
- primary-dark: #5B21B6,
135
- success: #10B981,
136
- error: #EF4444,
137
- ));
130
+ @include default.theme(
131
+ (
132
+ primary: #7c3aed,
133
+ primary-light: #ede9fe,
134
+ primary-dark: #5b21b6,
135
+ success: #10b981,
136
+ error: #ef4444,
137
+ )
138
+ );
138
139
  }
139
140
  ```
140
141
 
@@ -173,6 +174,131 @@ Thư viện cung cấp sẵn các utility class:
173
174
 
174
175
  ---
175
176
 
177
+ ## AI Agent Usage Guardrails
178
+
179
+ Use this section as a checklist when generating SDCoreJS Angular code from online docs.
180
+
181
+ ### 1. Standalone imports are mandatory
182
+
183
+ Every component, directive, and pipe used in a standalone template must be listed in the host component's `imports`. This includes template-only directives such as `sdTableCellDef`, `sdTableFilterDef`, `sdSuffixDef`, `sdViewDef`, `sdItemDef`, and shared display pipes.
184
+
185
+ ```ts
186
+ import { Component } from '@angular/core';
187
+ import { SdButton } from '@sdcorejs/angular/components/button';
188
+ import {
189
+ SdTable,
190
+ SdTableCellDefDirective,
191
+ SdTableFilterDefDirective,
192
+ SdTableFooterDefDirective,
193
+ SdTableTitleDefDirective,
194
+ } from '@sdcorejs/angular/components/table';
195
+ import { SdInput, SdInputNumber, SdSelect } from '@sdcorejs/angular/forms';
196
+ import { SdItemDefDefDirective, SdSuffixDefDirective, SdViewDefDirective } from '@sdcorejs/angular/forms/directives';
197
+ import { SdFormatDatePipe, SdFormatDatetimePipe, SdFormatNumberPipe, SdViewPipe } from '@sdcorejs/angular/pipes';
198
+
199
+ @Component({
200
+ standalone: true,
201
+ imports: [
202
+ SdButton,
203
+ SdTable,
204
+ SdTableCellDefDirective,
205
+ SdTableFilterDefDirective,
206
+ SdTableFooterDefDirective,
207
+ SdTableTitleDefDirective,
208
+ SdInput,
209
+ SdInputNumber,
210
+ SdSelect,
211
+ SdItemDefDefDirective,
212
+ SdSuffixDefDirective,
213
+ SdViewDefDirective,
214
+ SdFormatNumberPipe,
215
+ SdFormatDatePipe,
216
+ SdFormatDatetimePipe,
217
+ SdViewPipe,
218
+ ],
219
+ templateUrl: './list.component.html',
220
+ })
221
+ export class ListComponent {}
222
+ ```
223
+
224
+ Common missed imports:
225
+
226
+ | Template usage | Required import |
227
+ | ----------------------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------- |
228
+ | `<ng-template sdTableCellDef="amount">` | `SdTableCellDefDirective` from `@sdcorejs/angular/components/table` |
229
+ | `<ng-template sdTableFilterDef="status">` | `SdTableFilterDefDirective` from `@sdcorejs/angular/components/table` |
230
+ | `<ng-template sdTableTitleDef="name">` | `SdTableTitleDefDirective` from `@sdcorejs/angular/components/table` |
231
+ | `<ng-template sdTableFooterDef="amount">` | `SdTableFooterDefDirective` from `@sdcorejs/angular/components/table` |
232
+ | `<ng-template sdTableExpandDef>` | `SdTableExpandDefDirective` from `@sdcorejs/angular/components/table` |
233
+ | `<ng-template sdSuffixDef>` | `SdSuffixDefDirective` from `@sdcorejs/angular/forms/directives` |
234
+ | `<ng-template sdViewDef>` | `SdViewDefDirective` from `@sdcorejs/angular/forms/directives` |
235
+ | `<ng-template sdItemDef>` | `SdItemDefDefDirective` from `@sdcorejs/angular/forms/directives` |
236
+ | ` | sdFormatNumber` | `SdFormatNumberPipe` from `@sdcorejs/angular/pipes` |
237
+ | ` | sdFormatDate` | `SdFormatDatePipe` from `@sdcorejs/angular/pipes` |
238
+ | ` | sdFormatDatetime` | `SdFormatDatetimePipe` from `@sdcorejs/angular/pipes` |
239
+ | ` | sdView` | `SdViewPipe` from `@sdcorejs/angular/pipes` |
240
+
241
+ ### 2. Table cells use dense form controls
242
+
243
+ When rendering an editable SD form control inside `<sd-table>` cell templates or custom inline filter templates, always use `size="sm"` and `hideInlineError`. This keeps row height stable and surfaces validation through the compact error icon/tooltip instead of inserting inline error text inside the table row.
244
+
245
+ ```html
246
+ <sd-table [option]="tableOption">
247
+ <ng-template sdTableCellDef="quantity" let-row>
248
+ <sd-input-number size="sm" hideInlineError type="positive" [precision]="0" [(model)]="row.quantity"> </sd-input-number>
249
+ </ng-template>
250
+
251
+ <ng-template sdTableCellDef="status" let-row>
252
+ <sd-select size="sm" hideInlineError [items]="statusList" valueField="code" displayField="name" [(model)]="row.status"> </sd-select>
253
+ </ng-template>
254
+
255
+ <ng-template sdTableFilterDef="keyword" let-filter let-update="update">
256
+ <sd-input size="sm" hideInlineError [(model)]="filter.keyword" (keyupEnter)="update()"> </sd-input>
257
+ </ng-template>
258
+ </sd-table>
259
+ ```
260
+
261
+ ### 3. Prefer shared display pipes
262
+
263
+ Do not create app-local pipes for common display formatting.
264
+
265
+ ```html
266
+ <!-- Good -->
267
+ {{ row.amount | sdFormatNumber : 0 | sdView }} {{ row.issueDate | sdFormatDate | sdView }} {{ row.updatedAt | sdFormatDatetime : 'dd/MM/yyyy
268
+ HH:mm' | sdView }} {{ row.tags | sdView }}
269
+
270
+ <!-- Avoid -->
271
+ {{ row.amount | customCurrency }} {{ row.issueDate | date : 'dd/MM/yyyy' }} {{ row.tags?.join(', ') || '--' }}
272
+ ```
273
+
274
+ - `sdFormatNumber` formats numbers using SDCoreJS locale configuration.
275
+ - `sdFormatDate` formats date-only values with default `dd/MM/yyyy`.
276
+ - `sdFormatDatetime` formats date-time values with default `dd/MM/yyyy HH:mm:ss`.
277
+ - `sdView` renders `--` for `null`, `undefined`, `''`, `NaN`, and empty arrays; primitive arrays render as `A, B`.
278
+
279
+ ### 4. Command icons default to Material Symbols Outlined
280
+
281
+ Table row commands and child command menu items default to the outline Material icon font set. Only set `fontSet` when a specific icon family is required.
282
+
283
+ ```ts
284
+ command: {
285
+ align: 'right',
286
+ commands: [
287
+ { icon: 'edit', title: 'Edit', click: row => this.edit(row) },
288
+ {
289
+ icon: 'more_vert',
290
+ title: 'More',
291
+ children: [
292
+ { icon: 'visibility', title: 'View', click: row => this.view(row) },
293
+ { icon: 'delete', title: 'Delete', color: 'error', click: row => this.delete(row) },
294
+ ],
295
+ },
296
+ ],
297
+ }
298
+ ```
299
+
300
+ ---
301
+
176
302
  ## Components
177
303
 
178
304
  > Tất cả component đều là **standalone** và sử dụng **Angular Signals** API.
@@ -186,18 +312,18 @@ import { SdButton } from '@sdcorejs/angular/components/button';
186
312
 
187
313
  **Inputs:**
188
314
 
189
- | Input | Type | Default | Description |
190
- |---|---|---|---|
191
- | `type` | `'fill' \| 'light' \| 'outline' \| 'link'` | `'light'` | Kiểu nút |
192
- | `color` | `Color` | `'secondary'` | Màu sắc |
193
- | `size` | `'sm' \| 'md' \| 'lg'` | `'sm'` | Kích thước |
194
- | `title` | `string` | — | Nhãn nút |
195
- | `prefixIcon` | `string` | — | Icon Material trước text |
196
- | `suffixIcon` | `string` | — | Icon Material sau text |
197
- | `disabled` | `boolean` | `false` | Vô hiệu hoá |
198
- | `loading` | `boolean` | `false` | Trạng thái loading (tự chặn click) |
199
- | `tooltip` | `string` | — | Tooltip khi hover |
200
- | `width` | `string` | — | CSS width tuỳ chỉnh |
315
+ | Input | Type | Default | Description |
316
+ | ------------ | ------------------------------------------ | ------------- | ---------------------------------- |
317
+ | `type` | `'fill' \| 'light' \| 'outline' \| 'link'` | `'light'` | Kiểu nút |
318
+ | `color` | `Color` | `'secondary'` | Màu sắc |
319
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'sm'` | Kích thước |
320
+ | `title` | `string` | — | Nhãn nút |
321
+ | `prefixIcon` | `string` | — | Icon Material trước text |
322
+ | `suffixIcon` | `string` | — | Icon Material sau text |
323
+ | `disabled` | `boolean` | `false` | Vô hiệu hoá |
324
+ | `loading` | `boolean` | `false` | Trạng thái loading (tự chặn click) |
325
+ | `tooltip` | `string` | — | Tooltip khi hover |
326
+ | `width` | `string` | — | CSS width tuỳ chỉnh |
201
327
 
202
328
  **Output:** `(click): EventEmitter<Event>` — có throttle 300ms, tự chặn khi `disabled` hoặc `loading`.
203
329
 
@@ -218,14 +344,14 @@ import { SdBadge } from '@sdcorejs/angular/components/badge';
218
344
 
219
345
  **Inputs:**
220
346
 
221
- | Input | Type | Default | Description |
222
- |---|---|---|---|
223
- | `type` | `'tag' \| 'round' \| 'icon'` | `'icon'` | Kiểu badge |
224
- | `color` | `Color` | `'secondary'` | Màu sắc |
225
- | `title` | `string \| number` | — | Nội dung hiển thị |
226
- | `icon` | `string` | — | Icon Material |
227
- | `size` | `Size` | `'sm'` | Kích thước |
228
- | `tooltip` | `string` | — | Tooltip |
347
+ | Input | Type | Default | Description |
348
+ | --------- | ---------------------------- | ------------- | ----------------- |
349
+ | `type` | `'tag' \| 'round' \| 'icon'` | `'icon'` | Kiểu badge |
350
+ | `color` | `Color` | `'secondary'` | Màu sắc |
351
+ | `title` | `string \| number` | — | Nội dung hiển thị |
352
+ | `icon` | `string` | — | Icon Material |
353
+ | `size` | `Size` | `'sm'` | Kích thước |
354
+ | `tooltip` | `string` | — | Tooltip |
229
355
 
230
356
  Shorthand color inputs (boolean): `primary`, `secondary`, `success`, `info`, `warning`, `error`.
231
357
 
@@ -245,15 +371,15 @@ import { SdSection } from '@sdcorejs/angular/components/section';
245
371
 
246
372
  **Inputs:**
247
373
 
248
- | Input | Type | Default | Description |
249
- |---|---|---|---|
250
- | `title` | `string` | *required* | Tiêu đề section |
251
- | `subTitle` | `string` | — | Tiêu đề phụ |
252
- | `icon` | `string` | — | Icon Material |
253
- | `iconColor` | `Color` | `'primary'` | Màu icon |
254
- | `collapsable` | `boolean` | `false` | Cho phép thu gọn |
255
- | `collapsed` | `boolean` | `false` | Trạng thái ban đầu thu gọn |
256
- | `hideHeader` | `boolean` | `false` | Ẩn phần header |
374
+ | Input | Type | Default | Description |
375
+ | ------------- | --------- | ----------- | -------------------------- |
376
+ | `title` | `string` | _required_ | Tiêu đề section |
377
+ | `subTitle` | `string` | — | Tiêu đề phụ |
378
+ | `icon` | `string` | — | Icon Material |
379
+ | `iconColor` | `Color` | `'primary'` | Màu icon |
380
+ | `collapsable` | `boolean` | `false` | Cho phép thu gọn |
381
+ | `collapsed` | `boolean` | `false` | Trạng thái ban đầu thu gọn |
382
+ | `hideHeader` | `boolean` | `false` | Ẩn phần header |
257
383
 
258
384
  ```html
259
385
  <sd-section title="Thông tin cơ bản" icon="person" iconColor="primary">
@@ -275,18 +401,19 @@ import { SdModal } from '@sdcorejs/angular/components/modal';
275
401
 
276
402
  **Inputs:**
277
403
 
278
- | Input | Type | Default | Description |
279
- |---|---|---|---|
280
- | `title` | `string` | — | Tiêu đề modal |
281
- | `color` | `Color` | `'primary'` | Màu header |
282
- | `width` | `'sx' \| 'sm' \| 'md' \| 'lg' \| string` | `'md'` | Độ rộng (md = 60vw) |
283
- | `height` | `string` | `'auto'` | Chiều cao |
284
- | `view` | `'dialog' \| 'bottom-sheet'` | auto | Tự động bottom-sheet trên mobile |
285
- | `lazyLoadContent` | `boolean` | `true` | Lazy render nội dung |
404
+ | Input | Type | Default | Description |
405
+ | ----------------- | ---------------------------------------- | ----------- | -------------------------------- |
406
+ | `title` | `string` | — | Tiêu đề modal |
407
+ | `color` | `Color` | `'primary'` | Màu header |
408
+ | `width` | `'sx' \| 'sm' \| 'md' \| 'lg' \| string` | `'md'` | Độ rộng (md = 60vw) |
409
+ | `height` | `string` | `'auto'` | Chiều cao |
410
+ | `view` | `'dialog' \| 'bottom-sheet'` | auto | Tự động bottom-sheet trên mobile |
411
+ | `lazyLoadContent` | `boolean` | `true` | Lazy render nội dung |
286
412
 
287
413
  **Output:** `(sdClosed): EventEmitter` — phát ra khi modal đóng.
288
414
 
289
415
  **Methods** (dùng qua `@ViewChild`):
416
+
290
417
  - `modal.open()` — mở modal
291
418
  - `modal.close()` — đóng modal
292
419
 
@@ -321,17 +448,17 @@ SdTable nhận một object `option` duy nhất kiểu `SdTableOption<T>`.
321
448
 
322
449
  **Column types / Kiểu cột:**
323
450
 
324
- | `type` | Mô tả |
325
- |---|---|
326
- | `'string'` | Văn bản |
327
- | `'number'` | Số (tự format) |
328
- | `'boolean'` | True/False |
329
- | `'date'` | Ngày |
330
- | `'datetime'` | Ngày giờ |
331
- | `'time'` | Giờ |
332
- | `'values'` | Enum từ danh sách cố định |
333
- | `'lazy-values'` | Enum load async |
334
- | `'children'` | Cột nhóm (multi-header) |
451
+ | `type` | Mô tả |
452
+ | --------------- | ------------------------- |
453
+ | `'string'` | Văn bản |
454
+ | `'number'` | Số (tự format) |
455
+ | `'boolean'` | True/False |
456
+ | `'date'` | Ngày |
457
+ | `'datetime'` | Ngày giờ |
458
+ | `'time'` | Giờ |
459
+ | `'values'` | Enum từ danh sách cố định |
460
+ | `'lazy-values'` | Enum load async |
461
+ | `'children'` | Cột nhóm (multi-header) |
335
462
 
336
463
  **Local table:**
337
464
 
@@ -340,11 +467,14 @@ option: SdTableOption<Product> = {
340
467
  type: 'local',
341
468
  items: () => this.products,
342
469
  columns: [
343
- { field: 'code', type: 'string', title: 'Mã', width: '120px' },
344
- { field: 'name', type: 'string', title: 'Tên', sortable: true },
345
- { field: 'price', type: 'number', title: 'Đơn giá', align: 'right' },
346
- { field: 'active', type: 'boolean', title: 'Kích hoạt',
347
- useBadge: (val) => ({ color: val ? 'success' : 'secondary', title: val ? 'Có' : 'Không' })
470
+ { field: 'code', type: 'string', title: 'Mã', width: '120px' },
471
+ { field: 'name', type: 'string', title: 'Tên', sortable: true },
472
+ { field: 'price', type: 'number', title: 'Đơn giá', align: 'right' },
473
+ {
474
+ field: 'active',
475
+ type: 'boolean',
476
+ title: 'Kích hoạt',
477
+ useBadge: val => ({ color: val ? 'success' : 'secondary', title: val ? 'Có' : 'Không' }),
348
478
  },
349
479
  ],
350
480
  paginate: { pageSize: 20 },
@@ -368,15 +498,18 @@ option: SdTableOption<Product> = {
368
498
  columns: [
369
499
  { field: 'code', type: 'string', title: 'Mã', width: '120px' },
370
500
  { field: 'name', type: 'string', title: 'Tên', sortable: true },
371
- { field: 'status', type: 'values', title: 'Trạng thái',
372
- useBadge: (val) => ({ color: val === 'ACTIVE' ? 'success' : 'secondary', title: val })
501
+ {
502
+ field: 'status',
503
+ type: 'values',
504
+ title: 'Trạng thái',
505
+ useBadge: val => ({ color: val === 'ACTIVE' ? 'success' : 'secondary', title: val }),
373
506
  },
374
507
  ],
375
508
  command: {
376
509
  align: 'right',
377
510
  commands: [
378
- { icon: 'edit', color: 'primary', title: 'Sửa', click: (row) => this.onEdit(row) },
379
- { icon: 'delete', color: 'error', title: 'Xoá', click: (row) => this.onDelete(row) },
511
+ { icon: 'edit', color: 'primary', title: 'Sửa', click: row => this.onEdit(row) },
512
+ { icon: 'delete', color: 'error', title: 'Xoá', click: row => this.onDelete(row) },
380
513
  ],
381
514
  },
382
515
  };
@@ -414,28 +547,27 @@ import { SdAvatar } from '@sdcorejs/angular/components/avatar';
414
547
  ```
415
548
 
416
549
  ```html
417
- <sd-avatar src="/api/avatar/123" name="Nguyễn Văn A" size="md" />
418
- <sd-avatar name="NVA" color="primary" size="lg" />
550
+ <sd-avatar src="/api/avatar/123" name="Nguyễn Văn A" size="md" /> <sd-avatar name="NVA" color="primary" size="lg" />
419
551
  ```
420
552
 
421
553
  ---
422
554
 
423
555
  ### Other Components / Các component khác
424
556
 
425
- | Component | Import | Mô tả |
426
- |---|---|---|
427
- | `SdTabRouter` | `@sdcorejs/angular/components/tab-router` | Tab navigation với Angular Router |
428
- | `SdSideDrawer` | `@sdcorejs/angular/components/side-drawer` | Drawer layout trái/phải |
429
- | `SdUploadFile` | `@sdcorejs/angular/components/upload-file` | Upload file |
430
- | `SdQuickAction` | `@sdcorejs/angular/components/quick-action` | Nút action dạng icon |
431
- | `SdHistory` | `@sdcorejs/angular/components/history` | Lịch sử thay đổi |
432
- | `SdImportExcel` | `@sdcorejs/angular/components/import-excel` | Wizard import Excel |
433
- | `SdQueryBuilder` | `@sdcorejs/angular/components/query-builder` | Visual query builder |
434
- | `SdCodeEditor` | `@sdcorejs/angular/components/code-editor` | Code editor (PrismJS) |
435
- | `SdMiniEditor` | `@sdcorejs/angular/components/mini-editor` | Rich text editor nhỏ |
436
- | `SdDocumentBuilder` | `@sdcorejs/angular/components/document-builder` | Document builder |
437
- | `SdAnchorMain` | `@sdcorejs/angular/components/anchor` | Anchor / mục lục cuộn trang |
438
- | `SdView` | `@sdcorejs/angular/components/view` | View wrapper read-only |
557
+ | Component | Import | Mô tả |
558
+ | ------------------- | ----------------------------------------------- | --------------------------------- |
559
+ | `SdTabRouter` | `@sdcorejs/angular/components/tab-router` | Tab navigation với Angular Router |
560
+ | `SdSideDrawer` | `@sdcorejs/angular/components/side-drawer` | Drawer layout trái/phải |
561
+ | `SdUploadFile` | `@sdcorejs/angular/components/upload-file` | Upload file |
562
+ | `SdQuickAction` | `@sdcorejs/angular/components/quick-action` | Nút action dạng icon |
563
+ | `SdHistory` | `@sdcorejs/angular/components/history` | Lịch sử thay đổi |
564
+ | `SdImportExcel` | `@sdcorejs/angular/components/import-excel` | Wizard import Excel |
565
+ | `SdQueryBuilder` | `@sdcorejs/angular/components/query-builder` | Visual query builder |
566
+ | `SdCodeEditor` | `@sdcorejs/angular/components/code-editor` | Code editor (PrismJS) |
567
+ | `SdMiniEditor` | `@sdcorejs/angular/components/mini-editor` | Rich text editor nhỏ |
568
+ | `SdDocumentBuilder` | `@sdcorejs/angular/components/document-builder` | Document builder |
569
+ | `SdAnchorMain` | `@sdcorejs/angular/components/anchor` | Anchor / mục lục cuộn trang |
570
+ | `SdView` | `@sdcorejs/angular/components/view` | View wrapper read-only |
439
571
 
440
572
  ---
441
573
 
@@ -443,11 +575,11 @@ import { SdAvatar } from '@sdcorejs/angular/components/avatar';
443
575
 
444
576
  ```typescript
445
577
  import {
446
- SdInput, // Text input
447
- SdInputNumber, // Number input
448
- SdSelect, // Dropdown
578
+ SdInput, // Text input
579
+ SdInputNumber, // Number input
580
+ SdSelect, // Dropdown
449
581
  SdAutocomplete, // Autocomplete
450
- SdDate, // Date picker
582
+ SdDate, // Date picker
451
583
  } from '@sdcorejs/angular/forms';
452
584
  ```
453
585
 
@@ -456,8 +588,7 @@ import {
456
588
 
457
589
  <sd-input-number [(model)]="form.price" label="Đơn giá" [min]="0" suffix="VNĐ" />
458
590
 
459
- <sd-select [(model)]="form.status" label="Trạng thái"
460
- [items]="statusList" valueField="value" displayField="label" />
591
+ <sd-select [(model)]="form.status" label="Trạng thái" [items]="statusList" valueField="value" displayField="label" />
461
592
 
462
593
  <sd-date [(model)]="form.birthday" label="Ngày sinh" />
463
594
  ```
@@ -500,7 +631,7 @@ export class ProductListComponent implements OnInit {
500
631
  isSaving = signal(false);
501
632
 
502
633
  readonly STATUS_LIST = [
503
- { value: 'ACTIVE', label: 'Hoạt động' },
634
+ { value: 'ACTIVE', label: 'Hoạt động' },
504
635
  { value: 'INACTIVE', label: 'Dừng' },
505
636
  ];
506
637
 
@@ -513,13 +644,15 @@ export class ProductListComponent implements OnInit {
513
644
  type: 'server',
514
645
  items: async (filter, paging) => this.service.search(filter, paging),
515
646
  columns: [
516
- { field: 'code', type: 'string', title: 'Mã', width: '120px' },
517
- { field: 'name', type: 'string', title: 'Tên', sortable: true },
518
- { field: 'price', type: 'number', title: 'Đơn giá', align: 'right' },
647
+ { field: 'code', type: 'string', title: 'Mã', width: '120px' },
648
+ { field: 'name', type: 'string', title: 'Tên', sortable: true },
649
+ { field: 'price', type: 'number', title: 'Đơn giá', align: 'right' },
519
650
  {
520
- field: 'status', type: 'values', title: 'Trạng thái',
651
+ field: 'status',
652
+ type: 'values',
653
+ title: 'Trạng thái',
521
654
  option: { items: this.STATUS_LIST, valueField: 'value', displayField: 'label' },
522
- useBadge: (val) => ({
655
+ useBadge: val => ({
523
656
  color: val === 'ACTIVE' ? 'success' : 'secondary',
524
657
  title: this.STATUS_LIST.find(s => s.value === val)?.label,
525
658
  }),
@@ -528,8 +661,8 @@ export class ProductListComponent implements OnInit {
528
661
  command: {
529
662
  align: 'right',
530
663
  commands: [
531
- { icon: 'edit', color: 'primary', title: 'Sửa', click: (row) => this.openForm(row) },
532
- { icon: 'delete', color: 'error', title: 'Xoá', click: (row) => this.onDelete(row) },
664
+ { icon: 'edit', color: 'primary', title: 'Sửa', click: row => this.openForm(row) },
665
+ { icon: 'delete', color: 'error', title: 'Xoá', click: row => this.onDelete(row) },
533
666
  ],
534
667
  },
535
668
  paginate: { pageSize: 20 },
@@ -572,8 +705,7 @@ export class ProductListComponent implements OnInit {
572
705
  <!-- product-list.component.html -->
573
706
  <div class="d-flex justify-content-between align-items-center mb-16">
574
707
  <h2>Danh sách sản phẩm</h2>
575
- <sd-button type="fill" color="primary" title="Thêm mới"
576
- prefixIcon="add" (click)="openForm()" />
708
+ <sd-button type="fill" color="primary" title="Thêm mới" prefixIcon="add" (click)="openForm()" />
577
709
  </div>
578
710
 
579
711
  <sd-table #sdTable [option]="option" />
@@ -587,8 +719,7 @@ export class ProductListComponent implements OnInit {
587
719
  <sd-input [(model)]="formData.code" label="Mã" [required]="true" />
588
720
  </div>
589
721
  <div class="col-6">
590
- <sd-select [(model)]="formData.status" label="Trạng thái"
591
- [items]="STATUS_LIST" valueField="value" displayField="label" />
722
+ <sd-select [(model)]="formData.status" label="Trạng thái" [items]="STATUS_LIST" valueField="value" displayField="label" />
592
723
  </div>
593
724
  <div class="col-12">
594
725
  <sd-input [(model)]="formData.name" label="Tên sản phẩm" [required]="true" />
@@ -601,8 +732,7 @@ export class ProductListComponent implements OnInit {
601
732
  </div>
602
733
  <div class="modal-footer d-flex justify-content-end gap-8 p-16">
603
734
  <sd-button title="Huỷ" (click)="formModal.close()" />
604
- <sd-button type="fill" color="primary" title="Lưu"
605
- prefixIcon="save" [loading]="isSaving()" (click)="onSave()" />
735
+ <sd-button type="fill" color="primary" title="Lưu" prefixIcon="save" [loading]="isSaving()" (click)="onSave()" />
606
736
  </div>
607
737
  </ng-template>
608
738
  </sd-modal>
@@ -682,14 +812,14 @@ import { Color } from '@sdcorejs/angular/utilities';
682
812
  @Component({
683
813
  selector: 'sd-my-component',
684
814
  templateUrl: './my-component.component.html',
685
- styleUrls: ['./my-component.component.scss'],
815
+ styleUrl: './my-component.component.scss',
686
816
  changeDetection: ChangeDetectionStrategy.OnPush,
687
817
  standalone: true,
688
818
  imports: [],
689
819
  })
690
820
  export class SdMyComponent extends SdBaseSecureComponent {
691
821
  color = input<Color, Color | undefined | null>('primary', {
692
- transform: (value) => value || 'primary',
822
+ transform: value => value || 'primary',
693
823
  });
694
824
 
695
825
  title = input<string | undefined | null>(undefined);
@@ -705,16 +835,16 @@ export * from '@sdcorejs/angular/components/my-component';
705
835
 
706
836
  ### Quy ước / Conventions
707
837
 
708
- | Mục | Quy ước |
709
- |---|---|
710
- | Selector | `sd-<tên-component>` |
711
- | Class name | `Sd<TênComponent>` (Pascal) |
712
- | Input | Dùng `input<T>()` signal, **không** dùng `@Input()` decorator |
713
- | Null safety | Input transform phải handle `null/undefined` |
714
- | Base class | Extend `SdBaseSecureComponent` cho component có permission |
715
- | Change detection | Luôn dùng `ChangeDetectionStrategy.OnPush` |
716
- | Standalone | Luôn `standalone: true` |
717
- | Colors | Dùng `Color` type, không hardcode màu |
838
+ | Mục | Quy ước |
839
+ | ---------------- | ------------------------------------------------------------- |
840
+ | Selector | `sd-<tên-component>` |
841
+ | Class name | `Sd<TênComponent>` (Pascal) |
842
+ | Input | Dùng `input<T>()` signal, **không** dùng `@Input()` decorator |
843
+ | Null safety | Input transform phải handle `null/undefined` |
844
+ | Base class | Extend `SdBaseSecureComponent` cho component có permission |
845
+ | Change detection | Luôn dùng `ChangeDetectionStrategy.OnPush` |
846
+ | Standalone | Luôn `standalone: true` |
847
+ | Colors | Dùng `Color` type, không hardcode màu |
718
848
 
719
849
  ### Build
720
850