@sdcorejs/angular 20.1.2 → 20.1.3

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 (60) hide show
  1. package/README.md +124 -807
  2. package/components/form-generic/index.d.ts +25 -2
  3. package/fesm2022/sdcorejs-angular-components-form-generic.mjs +150 -60
  4. package/fesm2022/sdcorejs-angular-components-form-generic.mjs.map +1 -1
  5. package/fesm2022/sdcorejs-angular-components-import-excel.mjs +2 -7
  6. package/fesm2022/sdcorejs-angular-components-import-excel.mjs.map +1 -1
  7. package/fesm2022/sdcorejs-angular-components-query-bar.mjs +7 -15
  8. package/fesm2022/sdcorejs-angular-components-query-bar.mjs.map +1 -1
  9. package/fesm2022/sdcorejs-angular-components-query-builder.mjs +1 -1
  10. package/fesm2022/sdcorejs-angular-components-query-builder.mjs.map +1 -1
  11. package/fesm2022/sdcorejs-angular-components-tab-router.mjs +60 -30
  12. package/fesm2022/sdcorejs-angular-components-tab-router.mjs.map +1 -1
  13. package/fesm2022/sdcorejs-angular-components-table.mjs +32 -31
  14. package/fesm2022/sdcorejs-angular-components-table.mjs.map +1 -1
  15. package/fesm2022/sdcorejs-angular-components-tree.mjs +2 -1
  16. package/fesm2022/sdcorejs-angular-components-tree.mjs.map +1 -1
  17. package/fesm2022/sdcorejs-angular-components-upload-file.mjs +2 -1
  18. package/fesm2022/sdcorejs-angular-components-upload-file.mjs.map +1 -1
  19. package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs +2 -1
  20. package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs.map +1 -1
  21. package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs +2 -1
  22. package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs.map +1 -1
  23. package/fesm2022/sdcorejs-angular-forms-chip.mjs +2 -1
  24. package/fesm2022/sdcorejs-angular-forms-chip.mjs.map +1 -1
  25. package/fesm2022/sdcorejs-angular-forms-date-range.mjs +2 -1
  26. package/fesm2022/sdcorejs-angular-forms-date-range.mjs.map +1 -1
  27. package/fesm2022/sdcorejs-angular-forms-date.mjs +6 -5
  28. package/fesm2022/sdcorejs-angular-forms-date.mjs.map +1 -1
  29. package/fesm2022/sdcorejs-angular-forms-datetime.mjs +14 -465
  30. package/fesm2022/sdcorejs-angular-forms-datetime.mjs.map +1 -1
  31. package/fesm2022/sdcorejs-angular-forms-input-color.mjs +5 -3
  32. package/fesm2022/sdcorejs-angular-forms-input-color.mjs.map +1 -1
  33. package/fesm2022/sdcorejs-angular-forms-input-number.mjs +9 -6
  34. package/fesm2022/sdcorejs-angular-forms-input-number.mjs.map +1 -1
  35. package/fesm2022/sdcorejs-angular-forms-input.mjs +10 -6
  36. package/fesm2022/sdcorejs-angular-forms-input.mjs.map +1 -1
  37. package/fesm2022/sdcorejs-angular-forms-select.mjs +2 -1
  38. package/fesm2022/sdcorejs-angular-forms-select.mjs.map +1 -1
  39. package/fesm2022/sdcorejs-angular-forms-textarea.mjs +2 -1
  40. package/fesm2022/sdcorejs-angular-forms-textarea.mjs.map +1 -1
  41. package/fesm2022/sdcorejs-angular-modules-icon.mjs.map +1 -1
  42. package/fesm2022/sdcorejs-angular-modules-layout.mjs +1 -1
  43. package/fesm2022/sdcorejs-angular-modules-layout.mjs.map +1 -1
  44. package/fesm2022/sdcorejs-angular-modules.mjs +0 -1
  45. package/fesm2022/sdcorejs-angular-modules.mjs.map +1 -1
  46. package/fesm2022/sdcorejs-angular-pipes.mjs.map +1 -1
  47. package/fesm2022/sdcorejs-angular-services-loading.mjs +23 -14
  48. package/fesm2022/sdcorejs-angular-services-loading.mjs.map +1 -1
  49. package/forms/date/index.d.ts +1 -1
  50. package/forms/datetime/index.d.ts +1 -1
  51. package/forms/input/index.d.ts +3 -1
  52. package/forms/input-color/index.d.ts +3 -1
  53. package/forms/input-number/index.d.ts +3 -1
  54. package/modules/icon/index.d.ts +2 -3
  55. package/modules/index.d.ts +0 -1
  56. package/package.json +2 -5
  57. package/services/loading/index.d.ts +1 -1
  58. package/fesm2022/sdcorejs-angular-modules-authom.mjs +0 -360
  59. package/fesm2022/sdcorejs-angular-modules-authom.mjs.map +0 -1
  60. package/modules/authom/index.d.ts +0 -70
package/README.md CHANGED
@@ -1,885 +1,202 @@
1
1
  # @sdcorejs/angular
2
2
 
3
- > Angular UI library built on Angular Material - supports Angular 19 / 20 / 21.
3
+ Angular Material-based UI building blocks for data-heavy business applications. The package combines standalone components, consistent form controls, workflow primitives, application services, theming, and localization for Angular 19, 20, and 21.
4
4
 
5
- [![Angular](https://img.shields.io/badge/Angular-19%2B-red)](https://angular.dev)
6
- [![Angular Material](https://img.shields.io/badge/Angular_Material-19%2B-purple)](https://material.angular.io)
5
+ [![npm version](https://img.shields.io/npm/v/@sdcorejs/angular.svg)](https://www.npmjs.com/package/@sdcorejs/angular)
6
+ [![monthly npm downloads](https://img.shields.io/npm/dm/@sdcorejs/angular.svg)](https://www.npmjs.com/package/@sdcorejs/angular)
7
+ [![Angular 19, 20, and 21](https://img.shields.io/badge/Angular-19%20%7C%2020%20%7C%2021-DD0031?logo=angular&logoColor=white)](#compatibility)
8
+ [![MIT license](https://img.shields.io/github/license/sdcorejs/sdcorejs-angular.svg)](https://github.com/sdcorejs/sdcorejs-angular/blob/main/LICENSE)
7
9
 
8
- **🚀 [Showcase](https://sdcorejs.github.io/sdcorejs-angular)** · **📖 [Storybook code samples, props & API](https://sdcorejs.github.io/portal-template)**
10
+ [Showcase](https://sdcorejs.github.io/sdcorejs-angular/) · [Quick start](#quick-start) · [API manifest](https://sdcorejs.github.io/sdcorejs-angular/docs/latest/index.json) · [Source](https://github.com/sdcorejs/sdcorejs-angular) · [Changelog](https://github.com/sdcorejs/sdcorejs-angular/blob/main/CHANGELOG.md) · [Issues](https://github.com/sdcorejs/sdcorejs-angular/issues)
9
11
 
10
- ---
12
+ ## Compatibility
11
13
 
12
- ## Table of Contents / Mục lục
14
+ Install the package major that matches your Angular application.
13
15
 
14
- - [Getting Started / Cài đặt](#getting-started--cài-đặt)
15
- - [Theming / SCSS Customization](#theming--scss-customization)
16
- - [AI Agent Usage Guardrails](#ai-agent-usage-guardrails)
17
- - [Components](#components)
18
- - [SdButton](#sdbutton)
19
- - [SdBadge](#sdbadge)
20
- - [SdSection](#sdsection)
21
- - [SdModal](#sdmodal)
22
- - [SdTable](#sdtable)
23
- - [SdAvatar](#sdavatar)
24
- - [Other Components](#other-components--các-component-khác)
25
- - [Form Components](#form-components)
26
- - [CRUD Patterns / Code mẫu CRUD](#crud-patterns--code-mẫu-crud)
27
- - [Contributing Guide / Hướng dẫn đóng góp](#contributing-guide--hướng-dẫn-đóng-góp)
16
+ | Angular | Package | Recommended install |
17
+ | ------- | ----------------------- | ------------------------------------------------------------------------------------------------ |
18
+ | 19.x | `@sdcorejs/angular@^19` | `npm install @sdcorejs/angular@^19 @angular/material@^19 @angular/material-date-fns-adapter@^19` |
19
+ | 20.x | `@sdcorejs/angular@^20` | `npm install @sdcorejs/angular@^20 @angular/material@^20 @angular/material-date-fns-adapter@^20` |
20
+ | 21.x | `@sdcorejs/angular@^21` | `npm install @sdcorejs/angular@^21 @angular/material@^21 @angular/material-date-fns-adapter@^21` |
28
21
 
29
- ---
22
+ The package manifests accept Angular 19–21 peers, while releases provide an Angular-aligned package line for each major. The first version number is reserved for Angular compatibility, so read the [changelog](https://github.com/sdcorejs/sdcorejs-angular/blob/main/CHANGELOG.md) for explicitly labeled breaking changes before upgrading.
30
23
 
31
- ## Getting Started / Cài đặt
24
+ ## Installation and setup
32
25
 
33
- ### Prerequisites / Yêu cầu
34
-
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` |
39
- | `@angular/material-date-fns-adapter` | `^19.0.0 \|\| ^20.0.0 \|\| ^21.0.0` |
40
- | `date-fns` | `^3 \|\| ^4` |
41
-
42
- ### Installation / Cài đặt
26
+ The examples below use Angular 19; replace `19` with your application's Angular major.
43
27
 
44
28
  ```bash
45
- npm install @sdcorejs/angular
29
+ npm install @sdcorejs/angular@^19 @angular/material@^19 @angular/material-date-fns-adapter@^19
46
30
  ```
47
31
 
48
- ### Setup
49
-
50
- **1. Import global styles / Import style toàn cục**
51
-
52
- Thêm vào `angular.json` (hoặc `styles.scss` của app):
53
-
54
- ```json
55
- // angular.json
56
- {
57
- "styles": ["node_modules/@sdcorejs/angular/assets/scss/sd-core.scss"]
58
- }
59
- ```
60
-
61
- hoặc trong `styles.scss`:
32
+ Load the global stylesheet once:
62
33
 
63
34
  ```scss
35
+ /* styles.scss */
64
36
  @use '@sdcorejs/angular/assets/scss/sd-core';
65
37
  ```
66
38
 
67
- **2. Import Material Icons font / Font icon**
68
-
69
- Thêm vào `index.html`:
70
-
71
- ```html
72
- <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" rel="stylesheet" />
73
- <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;600&display=swap" rel="stylesheet" />
74
- ```
75
-
76
- **3. Configure providers / Cấu hình providers**
77
-
78
- ```typescript
79
- // app.config.ts
80
- import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
81
-
82
- export const appConfig: ApplicationConfig = {
83
- providers: [
84
- provideAnimationsAsync(),
85
- // ... các providers khác
86
- ],
87
- };
88
- ```
89
-
90
- ---
91
-
92
- ## Theming / SCSS Customization
93
-
94
- `sd-core.scss` emits the Angular Material M3 theme with `mat.theme(...)`.
95
- Prefer Core UI variables such as `--sd-primary`, `--sd-surface`, `--sd-text`,
96
- and `--sd-border` in application code. Material system variables remain an
97
- implementation detail for Angular Material overrides.
98
-
99
- ### CSS Variables
100
-
101
- `@sdcorejs/angular` sử dụng CSS custom properties (variables) để quản lý màu sắc. Mỗi màu được expose dưới dạng `--sd-<color>`.
39
+ The stylesheet includes the Roboto, Material Icons, and Material Symbols font files used by the library. No Google Fonts link is required. `@sdcorejs/utils`, `date-fns`, and other declared implementation dependencies install transitively.
102
40
 
103
- **Available color tokens / Các biến màu sắc:**
41
+ ## Quick start
104
42
 
105
- Core UI exposes a small public token surface: semantic palettes for component
106
- states, plus neutral tokens for surface, text, border, and disabled states.
107
-
108
- | Variable | Default bridge / fallback | Description |
109
- | -------- | ------------------------- | ----------- |
110
- | `--sd-primary` | `var(--mat-sys-primary, #005cbb)` | Main action color |
111
- | `--sd-primary-light` | `color-mix(in srgb, var(--sd-primary) 14%, white)` | Soft primary background |
112
- | `--sd-primary-dark` | `color-mix(in srgb, var(--sd-primary) 84%, black)` | Hover/active primary shade |
113
- | `--sd-primary-contrast` | `#ffffff` | Text/icon on primary |
114
- | `--sd-secondary` | `var(--mat-sys-secondary, #5c6270)` | Secondary action/accent |
115
- | `--sd-secondary-light` | `color-mix(in srgb, var(--sd-secondary) 12%, white)` | Soft secondary background |
116
- | `--sd-secondary-dark` | `color-mix(in srgb, var(--sd-secondary) 84%, black)` | Hover/active secondary shade |
117
- | `--sd-secondary-contrast` | `#ffffff` | Text/icon on secondary |
118
- | `--sd-info`, `--sd-success`, `--sd-warning`, `--sd-error` | Semantic base colors | Info, success, warning, and error states |
119
- | `--sd-*-light` / `--sd-*-dark` / `--sd-*-contrast` | Generated from each semantic base | Soft background, active shade, and readable foreground |
120
- | `--sd-surface` | `var(--mat-sys-surface, #fdfbff)` | App/page surface |
121
- | `--sd-surface-muted` | `var(--mat-sys-surface-container-highest, #e7e8ed)` | Muted neutral background |
122
- | `--sd-text` | `var(--mat-sys-on-surface, #1a1b1f)` | Primary text |
123
- | `--sd-text-secondary` | `var(--mat-sys-on-surface-variant, #44474f)` | Secondary text |
124
- | `--sd-text-muted` | `color-mix(in srgb, var(--sd-text) 62%, transparent)` | Muted text |
125
- | `--sd-border` | `var(--mat-sys-outline-variant, #c4c6d0)` | Divider/subtle border |
126
- | `--sd-border-strong` | `var(--mat-sys-outline, #74777f)` | Strong border/focus outline |
127
- | `--sd-disabled-bg` | `color-mix(in srgb, var(--sd-text) 8%, transparent)` | Disabled background |
128
- | `--sd-disabled-text` | `color-mix(in srgb, var(--sd-text) 60%, transparent)` | Disabled text/icon |
129
-
130
- ### Custom Theme / Tuỳ chỉnh theme
131
-
132
- Ghi đè theme mặc định bằng cách truyền map SCSS vào mixin `theme()`:
133
-
134
- ```scss
135
- // styles.scss
136
- @use '@sdcorejs/angular/assets/scss/themes/default' as default;
137
-
138
- html {
139
- @include default.theme(
140
- (
141
- primary: #7c3aed,
142
- primary-light: #ede9fe,
143
- primary-dark: #5b21b6,
144
- success: #10b981,
145
- error: #ef4444,
146
- )
147
- );
148
- }
149
- ```
150
-
151
- Chỉ cần override các màu muốn thay đổi — các màu còn lại giữ nguyên giá trị mặc định.
152
-
153
- ### Utility Classes / Các class tiện ích
154
-
155
- Thư viện cung cấp sẵn các utility class:
156
-
157
- ```html
158
- <!-- Text color -->
159
- <span class="text-primary">Text màu primary</span>
160
- <span class="text-error">Text màu error</span>
161
-
162
- <!-- Background -->
163
- <div class="bg-primary-light">Background nhạt</div>
164
-
165
- <!-- Spacing (đơn vị px, từ 0–200) -->
166
- <div class="mt-16 mb-8 px-24">margin-top: 16px, padding: 0 24px</div>
167
-
168
- <!-- Gap -->
169
- <div class="d-flex gap-8">gap: 8px</div>
170
-
171
- <!-- Grid -->
172
- <div class="sd-grid-container grid-cols-3">
173
- <div class="col-span-2">Chiếm 2 cột</div>
174
- <div class="col-span-1">Chiếm 1 cột</div>
175
- </div>
176
-
177
- <!-- Bootstrap grid -->
178
- <div class="row">
179
- <div class="col-6">50%</div>
180
- <div class="col-6">50%</div>
181
- </div>
182
- ```
183
-
184
- ---
185
-
186
- ## AI Agent Usage Guardrails
187
-
188
- Use this section as a checklist when generating SDCoreJS Angular code from online docs.
189
-
190
- ### 1. Standalone imports are mandatory
191
-
192
- 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.
43
+ This standalone component renders a primary action and shows the button's built-in loading state while work is in progress. No SDCoreJS-specific provider is required for this example.
193
44
 
194
45
  ```ts
195
- import { Component } from '@angular/core';
46
+ import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
196
47
  import { SdButton } from '@sdcorejs/angular/components/button';
197
- import {
198
- SdTable,
199
- SdTableCellDefDirective,
200
- SdTableFilterDefDirective,
201
- SdTableFooterDefDirective,
202
- SdTableTitleDefDirective,
203
- } from '@sdcorejs/angular/components/table';
204
- import { SdInput, SdInputNumber, SdSelect } from '@sdcorejs/angular/forms';
205
- import { SdItemDefDefDirective, SdSuffixDefDirective, SdViewDefDirective } from '@sdcorejs/angular/forms/directives';
206
- import { SdFormatDatePipe, SdFormatDatetimePipe, SdFormatNumberPipe, SdViewPipe } from '@sdcorejs/angular/pipes';
207
48
 
208
49
  @Component({
50
+ selector: 'app-root',
209
51
  standalone: true,
210
- imports: [
211
- SdButton,
212
- SdTable,
213
- SdTableCellDefDirective,
214
- SdTableFilterDefDirective,
215
- SdTableFooterDefDirective,
216
- SdTableTitleDefDirective,
217
- SdInput,
218
- SdInputNumber,
219
- SdSelect,
220
- SdItemDefDefDirective,
221
- SdSuffixDefDirective,
222
- SdViewDefDirective,
223
- SdFormatNumberPipe,
224
- SdFormatDatePipe,
225
- SdFormatDatetimePipe,
226
- SdViewPipe,
227
- ],
228
- templateUrl: './list.component.html',
229
- })
230
- export class ListComponent {}
231
- ```
232
-
233
- Common missed imports:
234
-
235
- | Template usage | Required import |
236
- | ----------------------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------- |
237
- | `<ng-template sdTableCellDef="amount">` | `SdTableCellDefDirective` from `@sdcorejs/angular/components/table` |
238
- | `<ng-template sdTableFilterDef="status">` | `SdTableFilterDefDirective` from `@sdcorejs/angular/components/table` |
239
- | `<ng-template sdTableTitleDef="name">` | `SdTableTitleDefDirective` from `@sdcorejs/angular/components/table` |
240
- | `<ng-template sdTableFooterDef="amount">` | `SdTableFooterDefDirective` from `@sdcorejs/angular/components/table` |
241
- | `<ng-template sdTableExpandDef>` | `SdTableExpandDefDirective` from `@sdcorejs/angular/components/table` |
242
- | `<ng-template sdSuffixDef>` | `SdSuffixDefDirective` from `@sdcorejs/angular/forms/directives` |
243
- | `<ng-template sdViewDef>` | `SdViewDefDirective` from `@sdcorejs/angular/forms/directives` |
244
- | `<ng-template sdItemDef>` | `SdItemDefDefDirective` from `@sdcorejs/angular/forms/directives` |
245
- | ` | sdFormatNumber` | `SdFormatNumberPipe` from `@sdcorejs/angular/pipes` |
246
- | ` | sdFormatDate` | `SdFormatDatePipe` from `@sdcorejs/angular/pipes` |
247
- | ` | sdFormatDatetime` | `SdFormatDatetimePipe` from `@sdcorejs/angular/pipes` |
248
- | ` | sdView` | `SdViewPipe` from `@sdcorejs/angular/pipes` |
249
-
250
- ### 2. Table cells use dense form controls
251
-
252
- 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.
253
-
254
- ```html
255
- <sd-table [option]="tableOption">
256
- <ng-template sdTableCellDef="quantity" let-row>
257
- <sd-input-number size="sm" hideInlineError type="positive" [precision]="0" [(model)]="row.quantity"> </sd-input-number>
258
- </ng-template>
259
-
260
- <ng-template sdTableCellDef="status" let-row>
261
- <sd-select size="sm" hideInlineError [items]="statusList" valueField="code" displayField="name" [(model)]="row.status"> </sd-select>
262
- </ng-template>
263
-
264
- <ng-template sdTableFilterDef="keyword" let-filter let-update="update">
265
- <sd-input size="sm" hideInlineError [(model)]="filter.keyword" (keyupEnter)="update()"> </sd-input>
266
- </ng-template>
267
- </sd-table>
268
- ```
269
-
270
- ### 3. Prefer shared display pipes
271
-
272
- Do not create app-local pipes for common display formatting.
273
-
274
- ```html
275
- <!-- Good -->
276
- {{ row.amount | sdFormatNumber : 0 | sdView }} {{ row.issueDate | sdFormatDate | sdView }} {{ row.updatedAt | sdFormatDatetime : 'dd/MM/yyyy
277
- HH:mm' | sdView }} {{ row.tags | sdView }}
278
-
279
- <!-- Avoid -->
280
- {{ row.amount | customCurrency }} {{ row.issueDate | date : 'dd/MM/yyyy' }} {{ row.tags?.join(', ') || '--' }}
281
- ```
282
-
283
- - `sdFormatNumber` formats numbers using SDCoreJS locale configuration.
284
- - `sdFormatDate` formats date-only values with default `dd/MM/yyyy`.
285
- - `sdFormatDatetime` formats date-time values with default `dd/MM/yyyy HH:mm:ss`.
286
- - `sdView` renders `--` for `null`, `undefined`, `''`, `NaN`, and empty arrays; primitive arrays render as `A, B`.
287
-
288
- ### 4. Command icons default to Material Symbols Outlined
289
-
290
- 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.
291
-
292
- ```ts
293
- command: {
294
- align: 'right',
295
- commands: [
296
- { icon: 'edit', title: 'Edit', click: row => this.edit(row) },
297
- {
298
- icon: 'more_vert',
299
- title: 'More',
300
- children: [
301
- { icon: 'visibility', title: 'View', click: row => this.view(row) },
302
- { icon: 'delete', title: 'Delete', color: 'error', click: row => this.delete(row) },
303
- ],
304
- },
305
- ],
306
- }
307
- ```
308
-
309
- ---
310
-
311
- ## Components
312
-
313
- > Tất cả component đều là **standalone** và sử dụng **Angular Signals** API.
314
- > All components are **standalone** and use **Angular Signals** API.
315
-
316
- ### SdButton
317
-
318
- ```typescript
319
- import { SdButton } from '@sdcorejs/angular/components/button';
320
- ```
321
-
322
- **Inputs:**
323
-
324
- | Input | Type | Default | Description |
325
- | ------------ | ------------------------------------------ | ------------- | ---------------------------------- |
326
- | `type` | `'fill' \| 'light' \| 'outline' \| 'text'` | `'light'` | Kiểu nút |
327
- | `color` | `Color` | `'secondary'` | Màu sắc |
328
- | `size` | `'sm' \| 'md' \| 'lg'` | `'sm'` | Kích thước |
329
- | `title` | `string` | — | Nhãn nút |
330
- | `prefixIcon` | `string` | — | Icon Material trước text |
331
- | `suffixIcon` | `string` | — | Icon Material sau text |
332
- | `disabled` | `boolean` | `false` | Vô hiệu hoá |
333
- | `loading` | `boolean` | `false` | Trạng thái loading (tự chặn click) |
334
- | `tooltip` | `string` | — | Tooltip khi hover |
335
- | `width` | `string` | — | CSS width tuỳ chỉnh |
336
-
337
- **Output:** `(click): EventEmitter<Event>` — có throttle 300ms, tự chặn khi `disabled` hoặc `loading`.
338
-
339
- ```html
340
- <sd-button type="fill" color="primary" title="Lưu" prefixIcon="save" (click)="onSave()" />
341
- <sd-button type="outline" color="error" prefixIcon="delete" tooltip="Xoá" />
342
- <sd-button type="light" title="Huỷ" (click)="modal.close()" />
343
- <sd-button type="fill" color="primary" title="Đang xử lý" [loading]="true" />
344
- ```
345
-
346
- ---
347
-
348
- ### SdBadge
349
-
350
- ```typescript
351
- import { SdBadge } from '@sdcorejs/angular/components/badge';
352
- ```
353
-
354
- **Inputs:**
355
-
356
- | Input | Type | Default | Description |
357
- | --------- | ---------------------------- | ------------- | ----------------- |
358
- | `type` | `'tag' \| 'round' \| 'icon'` | `'icon'` | Kiểu badge |
359
- | `color` | `Color` | `'secondary'` | Màu sắc |
360
- | `title` | `string \| number` | — | Nội dung hiển thị |
361
- | `icon` | `string` | — | Icon Material |
362
- | `size` | `Size` | `'sm'` | Kích thước |
363
- | `tooltip` | `string` | — | Tooltip |
364
-
365
- Shorthand color inputs (boolean): `primary`, `secondary`, `success`, `info`, `warning`, `error`.
366
-
367
- ```html
368
- <sd-badge type="tag" color="success" title="Hoạt động" />
369
- <sd-badge type="round" [warning]="true" title="Chờ duyệt" />
370
- <sd-badge type="icon" color="error" icon="close" title="Từ chối" />
371
- ```
372
-
373
- ---
374
-
375
- ### SdSection
376
-
377
- ```typescript
378
- import { SdSection } from '@sdcorejs/angular/components/section';
379
- ```
380
-
381
- **Inputs:**
382
-
383
- | Input | Type | Default | Description |
384
- | ------------- | --------- | ----------- | -------------------------- |
385
- | `title` | `string` | _required_ | Tiêu đề section |
386
- | `subTitle` | `string` | — | Tiêu đề phụ |
387
- | `icon` | `string` | — | Icon Material |
388
- | `iconColor` | `Color` | `'primary'` | Màu icon |
389
- | `collapsible` | `boolean` | `false` | Cho phép thu gọn |
390
- | `collapsed` | `boolean` | `false` | Trạng thái ban đầu thu gọn |
391
- | `hideHeader` | `boolean` | `false` | Ẩn phần header |
392
-
393
- ```html
394
- <sd-section title="Thông tin cơ bản" icon="person" iconColor="primary">
395
- <!-- nội dung -->
396
- </sd-section>
397
-
398
- <sd-section title="Cài đặt nâng cao" icon="settings" collapsible [collapsed]="true">
399
- <!-- nội dung ẩn mặc định -->
400
- </sd-section>
401
- ```
402
-
403
- ---
404
-
405
- ### SdModal
406
-
407
- ```typescript
408
- import { SdModal } from '@sdcorejs/angular/components/modal';
409
- ```
410
-
411
- **Inputs:**
412
-
413
- | Input | Type | Default | Description |
414
- | ----------------- | ---------------------------------------- | ----------- | -------------------------------- |
415
- | `title` | `string` | — | Tiêu đề modal |
416
- | `color` | `Color` | `'primary'` | Màu header |
417
- | `width` | `'sx' \| 'sm' \| 'md' \| 'lg' \| string` | `'md'` | Độ rộng (md = 60vw) |
418
- | `height` | `string` | `'auto'` | Chiều cao |
419
- | `view` | `'dialog' \| 'bottom-sheet'` | auto | Tự động bottom-sheet trên mobile |
420
- | `lazyLoadContent` | `boolean` | `true` | Lazy render nội dung |
421
-
422
- **Output:** `(sdClosed): EventEmitter` — phát ra khi modal đóng.
423
-
424
- **Methods** (dùng qua `@ViewChild`):
425
-
426
- - `modal.open()` — mở modal
427
- - `modal.close()` — đóng modal
428
-
429
- > ⚠️ Nội dung modal phải đặt trong `<ng-template>`.
430
-
431
- ```html
432
- <sd-modal #myModal title="Thêm mới" width="md" (sdClosed)="onClosed()">
433
- <ng-template>
434
- <div class="modal-body p-16">
435
- <!-- nội dung form -->
436
- </div>
437
- <div class="modal-footer d-flex justify-content-end gap-8 p-16">
438
- <sd-button title="Huỷ" (click)="myModal.close()" />
439
- <sd-button type="fill" color="primary" title="Lưu" (click)="onSave()" />
440
- </div>
441
- </ng-template>
442
- </sd-modal>
443
-
444
- <sd-button title="Mở modal" prefixIcon="add" (click)="myModal.open()" />
445
- ```
446
-
447
- ---
448
-
449
- ### SdTable
450
-
451
- ```typescript
452
- import { SdTable } from '@sdcorejs/angular/components/table';
453
- import type { SdTableOption, SdTableColumn } from '@sdcorejs/angular/components/table';
454
- ```
455
-
456
- SdTable nhận một object `option` duy nhất kiểu `SdTableOption<T>`.
457
-
458
- **Column types / Kiểu cột:**
459
-
460
- | `type` | Mô tả |
461
- | --------------- | ------------------------- |
462
- | `'string'` | Văn bản |
463
- | `'number'` | Số (tự format) |
464
- | `'boolean'` | True/False |
465
- | `'date'` | Ngày |
466
- | `'datetime'` | Ngày giờ |
467
- | `'time'` | Giờ |
468
- | `'values'` | Enum từ danh sách cố định |
469
- | `'lazy-values'` | Enum load async |
470
- | `'children'` | Cột nhóm (multi-header) |
471
-
472
- **Local table:**
473
-
474
- ```typescript
475
- option: SdTableOption<Product> = {
476
- type: 'local',
477
- items: () => this.products,
478
- columns: [
479
- { field: 'code', type: 'string', title: 'Mã', width: '120px' },
480
- { field: 'name', type: 'string', title: 'Tên', sortable: true },
481
- { field: 'price', type: 'number', title: 'Đơn giá', align: 'right' },
482
- {
483
- field: 'active',
484
- type: 'boolean',
485
- title: 'Kích hoạt',
486
- useBadge: val => ({ color: val ? 'success' : 'secondary', title: val ? 'Có' : 'Không' }),
487
- },
488
- ],
489
- paginate: { pageSize: 20 },
490
- reload: { visible: true },
491
- };
492
- ```
493
-
494
- **Server-side table:**
495
-
496
- ```typescript
497
- option: SdTableOption<Product> = {
498
- type: 'server',
499
- items: async (filterRequest, pagingReq) => {
500
- const res = await this.service.search({
501
- keyword: filterRequest.keyword,
502
- page: pagingReq.page,
503
- pageSize: pagingReq.pageSize,
504
- });
505
- return { items: res.data, total: res.total };
506
- },
507
- columns: [
508
- { field: 'code', type: 'string', title: 'Mã', width: '120px' },
509
- { field: 'name', type: 'string', title: 'Tên', sortable: true },
510
- {
511
- field: 'status',
512
- type: 'values',
513
- title: 'Trạng thái',
514
- useBadge: val => ({ color: val === 'ACTIVE' ? 'success' : 'secondary', title: val }),
515
- },
516
- ],
517
- command: {
518
- align: 'right',
519
- commands: [
520
- { icon: 'edit', color: 'primary', title: 'Sửa', click: row => this.onEdit(row) },
521
- { icon: 'delete', color: 'error', title: 'Xoá', click: row => this.onDelete(row) },
522
- ],
523
- },
524
- };
525
- ```
526
-
527
- ```html
528
- <sd-table #sdTable [option]="option" />
529
- ```
52
+ imports: [SdButton],
53
+ changeDetection: ChangeDetectionStrategy.OnPush,
54
+ template: `
55
+ <sd-button type="fill" color="primary" title="Save changes" [loading]="saving()" (click)="save()" />
530
56
 
531
- **Tree rows / Dòng cây:**
57
+ <p aria-live="polite">{{ status() }}</p>
58
+ `,
59
+ })
60
+ export class AppComponent {
61
+ readonly saving = signal(false);
62
+ readonly status = signal('Ready');
532
63
 
533
- `tree` là discriminated union theo `loadType`. Icon expand (`chevron_right` / `expand_more`) nằm ở **cột đầu** — cột STT khi bật `index`, ngược lại cột data đầu tiên — thụt lề theo cấp.
64
+ async save(): Promise<void> {
65
+ this.saving.set(true);
66
+ this.status.set('Saving…');
534
67
 
535
- ```typescript
536
- // loadType: 'static' — children embedded sẵn trong mỗi row
537
- tree: { loadType: 'static', childrenKey: 'children', defaultExpanded: 1 }
68
+ await new Promise<void>(resolve => setTimeout(resolve, 700));
538
69
 
539
- // loadType: 'lazy' — nạp con khi bung (Promise); hasChildren gate icon expand
540
- tree: {
541
- loadType: 'lazy',
542
- hasChildren: (row) => row.type === 'Folder', // chỉ Folder mới có icon expand
543
- onExpandChildren: (row) => api.getChildren(row.id), // () => Promise<T[]>
70
+ this.status.set('Saved');
71
+ this.saving.set(false);
72
+ }
544
73
  }
545
74
  ```
546
75
 
547
- - **Child-level search** (`type: 'local'` + `loadType: 'static'`): lọc inline tìm cả **cấp con** giữ nhánh cha của node khớp, ẩn sibling không khớp, tự bung tới node khớp. Clear filter khôi phục cây.
548
- - **Lazy loading**: spinner hiện trong ô chevron khi `onExpandChildren` đang chạy; `hasChildren` quyết định dòng nào hiện icon (không truyền = mọi node đều hiện).
76
+ Replace the timer with your typed service call and reset `saving` in a `finally` block in production code.
549
77
 
550
- ---
78
+ ## Main capabilities
551
79
 
552
- ### SdAvatar
80
+ | Area | Representative APIs |
81
+ | ----------------- | ------------------------------------------------------------------------------------------------------------------------------- |
82
+ | UI components | Buttons, badges, avatars, sections, tabs, steppers, modals, drawers, charts, editors, previews, and navigation primitives |
83
+ | Data and workflow | Local or server tables, tree data, query bars, query builders, generic forms, document building, file upload, and Excel import |
84
+ | Form controls | Text, number, date, date range, datetime, select, autocomplete, checkbox, radio, switch, chip, color, inline text, and textarea |
85
+ | Services | Notifications, confirmation dialogs, loading state, API access, storage, cache, Excel, and DOCX helpers |
86
+ | Portal modules | Auth, Keycloak, permission, layout, and icon modules |
87
+ | Localization | Built-in `vi`, `en`, `ja`, `ko`, and `zh` catalogs, plus a synchronous custom-catalog provider |
553
88
 
554
- ```typescript
555
- import { SdAvatar } from '@sdcorejs/angular/components/avatar';
556
- ```
557
-
558
- ```html
559
- <sd-avatar src="/api/avatar/123" name="Nguyễn Văn A" size="md" /> <sd-avatar name="NVA" color="primary" size="lg" />
560
- ```
561
-
562
- ---
563
-
564
- ### Other Components / Các component khác
565
-
566
- | Component | Import | Mô tả |
567
- | ------------------- | ----------------------------------------------- | --------------------------------- |
568
- | `SdTabRouter` | `@sdcorejs/angular/components/tab-router` | Tab navigation với Angular Router |
569
- | `SdSideDrawer` | `@sdcorejs/angular/components/side-drawer` | Drawer layout trái/phải |
570
- | `SdUploadFile` | `@sdcorejs/angular/components/upload-file` | Upload file |
571
- | `SdQuickAction` | `@sdcorejs/angular/components/quick-action` | Nút action dạng icon |
572
- | `SdHistory` | `@sdcorejs/angular/components/history` | Lịch sử thay đổi |
573
- | `SdImportExcel` | `@sdcorejs/angular/components/import-excel` | Wizard import Excel |
574
- | `SdQueryBuilder` | `@sdcorejs/angular/components/query-builder` | Visual query builder |
575
- | `SdCodeEditor` | `@sdcorejs/angular/components/code-editor` | Code editor (PrismJS) |
576
- | `SdMiniEditor` | `@sdcorejs/angular/components/mini-editor` | Rich text editor nhỏ |
577
- | `SdDocumentBuilder` | `@sdcorejs/angular/components/document-builder` | Document builder |
578
- | `SdAnchorMain` | `@sdcorejs/angular/components/anchor` | Anchor / mục lục cuộn trang |
579
- | `SdView` | `@sdcorejs/angular/components/view` | View wrapper read-only |
580
-
581
- ---
582
-
583
- ## Form Components
584
-
585
- ```typescript
586
- import {
587
- SdInput, // Text input
588
- SdInputNumber, // Number input
589
- SdSelect, // Dropdown
590
- SdAutocomplete, // Autocomplete
591
- SdDate, // Date picker
592
- } from '@sdcorejs/angular/forms';
593
- ```
89
+ The [live showcase](https://sdcorejs.github.io/sdcorejs-angular/) demonstrates components, forms, and services. The [latest API manifest](https://sdcorejs.github.io/sdcorejs-angular/docs/latest/index.json) lists every published reference document without duplicating the full API here.
594
90
 
595
- ```html
596
- <sd-input [(model)]="form.name" label="Họ tên" [required]="true" />
91
+ ## Standalone and subpath imports
597
92
 
598
- <sd-input-number [(model)]="form.price" label="Đơn giá" [min]="0" suffix="VNĐ" />
93
+ Prefer public leaf entry points so dependencies stay explicit and unused entry points can be removed from the application graph. The package declares `sideEffects: false`.
599
94
 
600
- <sd-select [(model)]="form.status" label="Trạng thái" [items]="statusList" valueField="value" displayField="label" />
601
-
602
- <sd-date [(model)]="form.birthday" label="Ngày sinh" />
95
+ ```ts
96
+ import { SdButton } from '@sdcorejs/angular/components/button';
97
+ import { SdTable, type SdTableOption } from '@sdcorejs/angular/components/table';
98
+ import { SdInput } from '@sdcorejs/angular/forms/input';
99
+ import { SdNotifyService } from '@sdcorejs/angular/services/notify';
100
+ import { I18nService } from '@sdcorejs/angular/i18n';
603
101
  ```
604
102
 
605
- ---
103
+ Import standalone components in the host component's `imports` array and inject services normally.
606
104
 
607
- ## CRUD Patterns / Code mẫu CRUD
105
+ Form controls use `[(model)]`. For group validation, pass a `FormGroup` through `[form]` and provide a `name`; SDCoreJS controls do not use `formControlName` or `[(ngModel)]` as their integration contract.
608
106
 
609
- ### List Component
610
-
611
- ```typescript
612
- // product-list.component.ts
613
- import { Component, OnInit, ViewChild, signal } from '@angular/core';
614
- import { SdTable, SdTableOption } from '@sdcorejs/angular/components/table';
615
- import { SdButton } from '@sdcorejs/angular/components/button';
616
- import { SdModal } from '@sdcorejs/angular/components/modal';
617
- import { SdSection } from '@sdcorejs/angular/components/section';
618
- import { SdInput, SdSelect } from '@sdcorejs/angular/forms';
619
-
620
- interface Product {
621
- id: number;
622
- code: string;
623
- name: string;
624
- price: number;
625
- status: 'ACTIVE' | 'INACTIVE';
626
- }
107
+ ```ts
108
+ import { Component } from '@angular/core';
109
+ import { FormGroup } from '@angular/forms';
110
+ import { SdInput } from '@sdcorejs/angular/forms/input';
627
111
 
628
112
  @Component({
629
- selector: 'app-product-list',
630
- templateUrl: './product-list.component.html',
113
+ selector: 'app-customer-form',
631
114
  standalone: true,
632
- imports: [SdTable, SdButton, SdModal, SdSection, SdInput, SdSelect],
115
+ imports: [SdInput],
116
+ template: `
117
+ <sd-input [form]="customerForm" name="customerName" label="Customer name" required maxlength="100" [(model)]="customer.name" />
118
+ `,
633
119
  })
634
- export class ProductListComponent implements OnInit {
635
- @ViewChild('formModal') formModal!: SdModal;
636
- @ViewChild('sdTable') sdTable?: SdTable<Product>;
637
-
638
- selectedItem: Product | null = null;
639
- formData: Partial<Product> = {};
640
- isSaving = signal(false);
641
-
642
- readonly STATUS_LIST = [
643
- { value: 'ACTIVE', label: 'Hoạt động' },
644
- { value: 'INACTIVE', label: 'Dừng' },
645
- ];
646
-
647
- option!: SdTableOption<Product>;
648
-
649
- constructor(private service: ProductService) {}
650
-
651
- ngOnInit() {
652
- this.option = {
653
- type: 'server',
654
- items: async (filter, paging) => this.service.search(filter, paging),
655
- columns: [
656
- { field: 'code', type: 'string', title: 'Mã', width: '120px' },
657
- { field: 'name', type: 'string', title: 'Tên', sortable: true },
658
- { field: 'price', type: 'number', title: 'Đơn giá', align: 'right' },
659
- {
660
- field: 'status',
661
- type: 'values',
662
- title: 'Trạng thái',
663
- option: { items: this.STATUS_LIST, valueField: 'value', displayField: 'label' },
664
- useBadge: val => ({
665
- color: val === 'ACTIVE' ? 'success' : 'secondary',
666
- title: this.STATUS_LIST.find(s => s.value === val)?.label,
667
- }),
668
- },
669
- ],
670
- command: {
671
- align: 'right',
672
- commands: [
673
- { icon: 'edit', color: 'primary', title: 'Sửa', click: row => this.openForm(row) },
674
- { icon: 'delete', color: 'error', title: 'Xoá', click: row => this.onDelete(row) },
675
- ],
676
- },
677
- paginate: { pageSize: 20 },
678
- reload: { visible: true },
679
- };
680
- }
681
-
682
- openForm(item?: Product) {
683
- this.selectedItem = item || null;
684
- this.formData = item ? { ...item } : { status: 'ACTIVE' };
685
- this.formModal.open();
686
- }
687
-
688
- async onSave() {
689
- this.isSaving.set(true);
690
- try {
691
- if (this.selectedItem) {
692
- await this.service.update(this.selectedItem.id, this.formData);
693
- } else {
694
- await this.service.create(this.formData);
695
- }
696
- this.formModal.close();
697
- this.sdTable?.reload?.();
698
- } finally {
699
- this.isSaving.set(false);
700
- }
701
- }
702
-
703
- async onDelete(item: Product) {
704
- if (!confirm(`Xoá "${item.name}"?`)) return;
705
- await this.service.delete(item.id);
706
- this.sdTable?.reload?.();
707
- }
120
+ export class CustomerFormComponent {
121
+ readonly customerForm = new FormGroup({});
122
+ readonly customer = { name: '' };
708
123
  }
709
124
  ```
710
125
 
711
- ### Template
712
-
713
- ```html
714
- <!-- product-list.component.html -->
715
- <div class="d-flex justify-content-between align-items-center mb-16">
716
- <h2>Danh sách sản phẩm</h2>
717
- <sd-button type="fill" color="primary" title="Thêm mới" prefixIcon="add" (click)="openForm()" />
718
- </div>
719
-
720
- <sd-table #sdTable [option]="option" />
721
-
722
- <sd-modal #formModal [title]="selectedItem ? 'Chỉnh sửa' : 'Thêm mới'" width="md">
723
- <ng-template>
724
- <div class="modal-body p-16">
725
- <sd-section title="Thông tin sản phẩm" icon="inventory">
726
- <div class="row">
727
- <div class="col-6">
728
- <sd-input [(model)]="formData.code" label="Mã" [required]="true" />
729
- </div>
730
- <div class="col-6">
731
- <sd-select [(model)]="formData.status" label="Trạng thái" [items]="STATUS_LIST" valueField="value" displayField="label" />
732
- </div>
733
- <div class="col-12">
734
- <sd-input [(model)]="formData.name" label="Tên sản phẩm" [required]="true" />
735
- </div>
736
- <div class="col-6">
737
- <sd-input-number [(model)]="formData.price" label="Đơn giá" suffix="VNĐ" />
738
- </div>
739
- </div>
740
- </sd-section>
741
- </div>
742
- <div class="modal-footer d-flex justify-content-end gap-8 p-16">
743
- <sd-button title="Huỷ" (click)="formModal.close()" />
744
- <sd-button type="fill" color="primary" title="Lưu" prefixIcon="save" [loading]="isSaving()" (click)="onSave()" />
745
- </div>
746
- </ng-template>
747
- </sd-modal>
748
- ```
126
+ ## Theming
749
127
 
750
- ---
751
-
752
- ## Contributing Guide / Hướng dẫn đóng góp
753
-
754
- ### Cấu trúc thư viện / Project structure
755
-
756
- ```
757
- sdcorejs-angular/
758
- ├── src/
759
- │ └── public-api.ts # Entry point chính
760
- ├── assets/
761
- │ └── scss/
762
- │ ├── sd-core.scss # SCSS entry (import vào app)
763
- │ ├── core/ # Base utilities (color, grid, form, ...)
764
- │ └── themes/ # Theme mặc định + Material theme
765
- ├── components/ # UI Components
766
- │ ├── button/
767
- │ ├── table/
768
- │ ├── modal/
769
- │ └── ...
770
- ├── forms/ # Form components
771
- │ ├── input/
772
- │ ├── select/
773
- │ └── ...
774
- ├── directives/ # Angular directives
775
- ├── pipes/ # Angular pipes
776
- ├── services/ # Shared services
777
- ├── utilities/ # Types, models, helpers
778
- └── modules/ # Feature modules (layout, permission, ...)
779
- ```
780
-
781
- ### Thêm component mới / Adding a new component
782
-
783
- **1. Tạo thư mục component:**
784
-
785
- ```
786
- components/
787
- └── my-component/
788
- ├── index.ts # Export public API
789
- ├── ng-package.json # ng-packagr entry
790
- └── src/
791
- ├── my-component.component.ts
792
- ├── my-component.component.html
793
- └── my-component.component.scss
794
- ```
128
+ `sd-core.scss` loads the reset, utilities, bundled fonts, semantic colors, form styles, and Angular Material theme baseline. Override public semantic colors with `sd.theme()`:
795
129
 
796
- **2. `ng-package.json`:**
130
+ ```scss
131
+ @use '@sdcorejs/angular/assets/scss/sd-core';
132
+ @use '@sdcorejs/angular/assets/scss/themes/default' as sd;
797
133
 
798
- ```json
799
- {
800
- "$schema": "../../../../node_modules/ng-packagr/ng-package.schema.json",
801
- "lib": {
802
- "entryFile": "index.ts"
803
- }
134
+ html {
135
+ @include sd.theme(
136
+ (
137
+ primary: #2563eb,
138
+ primary-light: #dbeafe,
139
+ primary-dark: #1d4ed8,
140
+ )
141
+ );
804
142
  }
805
143
  ```
806
144
 
807
- **3. `index.ts`:**
808
-
809
- ```typescript
810
- export * from './src/my-component.component';
811
- ```
145
+ See the [assets and SCSS reference](https://sdcorejs.github.io/sdcorejs-angular/docs/latest/assets/STYLE-GUIDE.md) for supported `--sd-*` tokens, Material M3 guidance, utilities, fonts, and image assets.
812
146
 
813
- **4. Component template:**
147
+ ## Internationalization
814
148
 
815
- ```typescript
816
- // my-component.component.ts
817
- import { ChangeDetectionStrategy, Component, input } from '@angular/core';
818
- import { SdBaseSecureComponent } from '@sdcorejs/angular/components/base';
819
- import { Color } from '@sdcorejs/angular/utilities';
149
+ Set the default Core UI language through `SD_CORE_CONFIGURATION`:
820
150
 
821
- @Component({
822
- selector: 'sd-my-component',
823
- templateUrl: './my-component.component.html',
824
- styleUrl: './my-component.component.scss',
825
- changeDetection: ChangeDetectionStrategy.OnPush,
826
- standalone: true,
827
- imports: [],
828
- })
829
- export class SdMyComponent extends SdBaseSecureComponent {
830
- color = input<Color, Color | undefined | null>('primary', {
831
- transform: value => value || 'primary',
832
- });
833
-
834
- title = input<string | undefined | null>(undefined);
835
- }
836
- ```
151
+ ```ts
152
+ import { ApplicationConfig } from '@angular/core';
153
+ import { type ISdCoreConfiguration, SD_CORE_CONFIGURATION } from '@sdcorejs/angular/configurations';
837
154
 
838
- **5. Export từ `components/index.ts`:**
155
+ const sdCoreConfig = {
156
+ language: 'en',
157
+ } satisfies ISdCoreConfiguration;
839
158
 
840
- ```typescript
841
- // components/index.ts
842
- export * from '@sdcorejs/angular/components/my-component';
159
+ export const appConfig: ApplicationConfig = {
160
+ providers: [{ provide: SD_CORE_CONFIGURATION, useValue: sdCoreConfig }],
161
+ };
843
162
  ```
844
163
 
845
- ### Quy ước / Conventions
164
+ `I18nService.setLanguage()` persists a built-in language and reloads by default. A complete custom catalog can be supplied through the synchronous `language: () => catalog` hook. See the [i18n reference](https://sdcorejs.github.io/sdcorejs-angular/docs/latest/i18n/i18n.md) for catalog typing and fallback behavior.
846
165
 
847
- | Mục | Quy ước |
848
- | ---------------- | ------------------------------------------------------------- |
849
- | Selector | `sd-<tên-component>` |
850
- | Class name | `Sd<TênComponent>` (Pascal) |
851
- | Input | Dùng `input<T>()` signal, **không** dùng `@Input()` decorator |
852
- | Null safety | Input transform phải handle `null/undefined` |
853
- | Base class | Extend `SdBaseSecureComponent` cho component có permission |
854
- | Change detection | Luôn dùng `ChangeDetectionStrategy.OnPush` |
855
- | Standalone | Luôn `standalone: true` |
856
- | Colors | Dùng `Color` type, không hardcode màu |
166
+ ## Documentation and examples
857
167
 
858
- ### Build
168
+ | Resource | Purpose |
169
+ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
170
+ | [Live showcase](https://sdcorejs.github.io/sdcorejs-angular/) | Interactive component, form, and service demos |
171
+ | [Button example source](https://github.com/sdcorejs/sdcorejs-angular/blob/main/versions/v19/projects/showcase/src/app/pages/components/button/button-demo.component.ts) | Action variants, icons, disabled state, and loading state |
172
+ | [Input example source](https://github.com/sdcorejs/sdcorejs-angular/blob/main/versions/v19/projects/showcase/src/app/pages/forms/input/input-demo.component.ts) | Model binding, validation, and viewed states |
173
+ | [Table example source](https://github.com/sdcorejs/sdcorejs-angular/blob/main/versions/v19/projects/showcase/src/app/pages/components/table/table-demo.component.ts) | Data, selection, filters, grouping, paging, and tree workflows |
174
+ | [Latest API manifest](https://sdcorejs.github.io/sdcorejs-angular/docs/latest/index.json) | Discover all Markdown docs for the latest release |
175
+ | [Versions registry](https://sdcorejs.github.io/sdcorejs-angular/docs/versions.json) | Select docs matching an installed package version |
176
+ | [Machine-readable catalog](https://sdcorejs.github.io/sdcorejs-angular/docs/catalog.json) | Discover documentation across maintained package lines |
177
+ | [E2E attributes](https://github.com/sdcorejs/sdcorejs-angular/blob/main/versions/v19/projects/sdcorejs-angular/docs/E2E-ATTRIBUTES.md) | Stable runtime selectors and state attributes |
859
178
 
860
- ```bash
861
- # Build toàn bộ thư viện
862
- ng-packagr -p ng-package.json
179
+ ## Versioning
863
180
 
864
- # Watch mode
865
- ng-packagr -p ng-package.json --watch
866
- ```
181
+ - Use `@sdcorejs/angular@^19`, `@^20`, or `@^21` to match the application's Angular major.
182
+ - Maintained package lines are released from the same feature surface with required Angular-major adaptations.
183
+ - Consumer-breaking changes and migration notes are recorded in the [changelog](https://github.com/sdcorejs/sdcorejs-angular/blob/main/CHANGELOG.md).
184
+ - Version-pinned reference docs remain available under `https://sdcorejs.github.io/sdcorejs-angular/docs/<package-version>/`.
867
185
 
868
- ### Versioning
186
+ ## Contributing
869
187
 
870
- Scheme: `<angular-major>.0.<release>`. Major digit **khoá theo Angular line** (19.x = Angular 19, 20.x = Angular 20, 21.x = Angular 21) **KHÔNG** dùng để báo breaking. Mỗi release publish đồng thời 3 major cùng nội dung feature, chỉ khác Angular shim.
188
+ Contributions are welcome through focused pull requests. See the [repository contribution workflow](https://github.com/sdcorejs/sdcorejs-angular#contributing) for setup, validation, and source-workspace guidance.
871
189
 
872
- - Luôn pin theo Angular line của bạn: `npm i @sdcorejs/angular@^19` (hoặc `@^20` / `@^21`).
873
- - Breaking change được ghi rõ ở `CHANGELOG.md`, mục **Changed (BREAKING for consumers)**, kèm migration.
190
+ ## Support
874
191
 
875
- ---
192
+ Use [GitHub Issues](https://github.com/sdcorejs/sdcorejs-angular/issues) for reproducible bugs and focused feature proposals. Include the Angular major, package version, a minimal reproduction, and the expected behavior.
876
193
 
877
- ## QA / E2E
194
+ ## Maintainer
878
195
 
879
- Core UI components expose runtime state via lowercase `data-*` attributes for e2e selectors. The full catalog, component matrix, selector cookbook, and YAML schema for AI agents live in `docs/E2E-ATTRIBUTES.md` in the source repository.
196
+ **Trần Thuận Nghĩa** Full Stack Developer and maintainer of `@sdcorejs/angular`. He builds practical, strongly typed web applications and reusable tools that make complex business workflows easier to deliver and maintain.
880
197
 
881
- ---
198
+ [SDCoreJS on GitHub](https://github.com/sdcorejs) · [LinkedIn](https://www.linkedin.com/in/tran-thuan-nghia/) · [Email](mailto:tran.thuan.nghia@gmail.com)
882
199
 
883
200
  ## License
884
201
 
885
- MIT - see the LICENSE file.
202
+ MIT © Trần Thuận Nghĩa. See the [repository license](https://github.com/sdcorejs/sdcorejs-angular/blob/main/LICENSE).