@sd-angular/core 19.0.0-beta.6 → 19.0.0-beta.60
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 +686 -33
- package/assets/scss/ckeditor5.scss +60 -2
- package/assets/scss/core/bootstrap.scss +17 -0
- package/assets/scss/core/form.scss +4 -1
- package/assets/scss/core/grid.scss +40 -0
- package/assets/scss/sd-core.scss +1 -0
- package/components/avatar/index.d.ts +1 -0
- package/components/avatar/src/avatar.component.d.ts +19 -0
- package/components/badge/src/badge.component.d.ts +77 -19
- package/components/button/src/button.component.d.ts +30 -28
- package/components/code-editor/index.d.ts +1 -0
- package/components/code-editor/src/code-editor.component.d.ts +25 -0
- package/components/document-builder/index.d.ts +1 -0
- package/components/document-builder/src/document-builder.component.d.ts +12 -41
- package/components/document-builder/src/document-builder.model.d.ts +14 -11
- package/components/document-builder/src/plugins/block-space/block-space.plugin.d.ts +9 -0
- package/components/document-builder/src/plugins/ck-comment/ck-comment.plugin.d.ts +44 -0
- package/components/document-builder/src/plugins/ck-comment/ck-comment.plugin.model.d.ts +57 -0
- package/components/document-builder/src/plugins/heading/heading.plugin.d.ts +1 -0
- package/components/document-builder/src/plugins/highlight-range/highlight-range.plugin.d.ts +4 -0
- package/components/document-builder/src/plugins/image-custom/image-custom.plugin.d.ts +31 -0
- package/components/document-builder/src/plugins/index.d.ts +7 -2
- package/components/document-builder/src/plugins/page-orientation/page-orientation.plugin.d.ts +2 -2
- package/components/document-builder/src/plugins/paste-handler/filters/bookmark.d.ts +14 -0
- package/components/document-builder/src/plugins/paste-handler/filters/br.d.ts +15 -0
- package/components/document-builder/src/plugins/paste-handler/filters/image.d.ts +25 -0
- package/components/document-builder/src/plugins/paste-handler/filters/list.d.ts +29 -0
- package/components/document-builder/src/plugins/paste-handler/filters/parse.d.ts +35 -0
- package/components/document-builder/src/plugins/paste-handler/filters/removeboldwrapper.d.ts +15 -0
- package/components/document-builder/src/plugins/paste-handler/filters/removegooglesheetstag.d.ts +15 -0
- package/components/document-builder/src/plugins/paste-handler/filters/removeinvalidtablewidth.d.ts +15 -0
- package/components/document-builder/src/plugins/paste-handler/filters/removemsattributes.d.ts +15 -0
- package/components/document-builder/src/plugins/paste-handler/filters/removestyleblock.d.ts +15 -0
- package/components/document-builder/src/plugins/paste-handler/filters/removexmlns.d.ts +15 -0
- package/components/document-builder/src/plugins/paste-handler/filters/replacemsfootnotes.d.ts +54 -0
- package/components/document-builder/src/plugins/paste-handler/filters/replacetabswithinprewithspaces.d.ts +24 -0
- package/components/document-builder/src/plugins/paste-handler/filters/space.d.ts +27 -0
- package/components/document-builder/src/plugins/paste-handler/filters/table.d.ts +16 -0
- package/components/document-builder/src/plugins/paste-handler/filters/utils.d.ts +25 -0
- package/components/document-builder/src/plugins/paste-handler/index.d.ts +35 -0
- package/components/document-builder/src/plugins/paste-handler/normalizers/googledocsnormalizer.d.ts +31 -0
- package/components/document-builder/src/plugins/paste-handler/normalizers/googlesheetsnormalizer.d.ts +31 -0
- package/components/document-builder/src/plugins/paste-handler/normalizers/mswordnormalizer.d.ts +29 -0
- package/components/document-builder/src/plugins/paste-handler/types.d.ts +30 -0
- package/components/document-builder/src/plugins/table-custom/index.d.ts +34 -0
- package/components/document-builder/src/plugins/variable/variable.plugin.d.ts +39 -0
- package/components/index.d.ts +4 -0
- package/components/mini-editor/index.d.ts +2 -0
- package/components/mini-editor/src/mini-editor.component.d.ts +90 -0
- package/components/mini-editor/src/mini-editor.model.d.ts +44 -0
- package/components/section/src/section.component.d.ts +10 -11
- package/components/side-drawer/src/side-drawer.component.d.ts +11 -24
- package/components/tab-router/src/components/tab-router-item/tab-router-item.component.d.ts +4 -1
- package/components/tab-router/src/components/tab-router-outlet/tab-router-outlet.component.d.ts +3 -15
- package/components/table/src/components/column-filter/column-filter.component.d.ts +3 -3
- package/components/table/src/components/desktop-cell/desktop-cell.component.d.ts +12 -2
- package/components/table/src/components/desktop-cell-view/desktop-cell-view.component.d.ts +12 -2
- package/components/table/src/components/external-filter/external-filter.component.d.ts +1 -1
- package/components/table/src/components/selector-action/action-filter.pipe.d.ts +11 -10
- package/components/table/src/components/selector-action/selector-action.component.d.ts +5 -3
- package/components/table/src/directives/index.d.ts +2 -0
- package/components/table/src/directives/sd-table-column-filter-def.directive.d.ts +9 -0
- package/components/table/src/directives/sticky-shadow.directive.d.ts +17 -0
- package/components/table/src/models/table-column.model.d.ts +34 -34
- package/components/table/src/models/table-command.model.d.ts +7 -3
- package/components/table/src/models/table-item.model.d.ts +5 -4
- package/components/table/src/models/table-option-config.model.d.ts +5 -0
- package/components/table/src/models/table-option-export.model.d.ts +3 -2
- package/components/table/src/models/table-option-selector.model.d.ts +11 -10
- package/components/table/src/models/table-option.model.d.ts +10 -9
- package/components/table/src/services/index.d.ts +3 -0
- package/components/table/src/services/table-export/table-export.service.d.ts +26 -0
- package/components/table/src/services/table-filter/table-filter.model.d.ts +5 -4
- package/components/table/src/services/table-format/table-format.service.d.ts +16 -0
- package/components/table/src/table.component.d.ts +39 -53
- package/components/upload-file/src/configurations/upload-file.configuration.d.ts +1 -1
- package/components/upload-file/src/services/upload-file.service.d.ts +0 -1
- package/components/upload-file/src/upload-file.component.d.ts +49 -54
- package/components/view/index.d.ts +1 -0
- package/components/view/src/view.component.d.ts +16 -0
- package/components/workflow/src/models/form-generic-component.model.d.ts +5 -4
- package/components/workflow/src/models/index.d.ts +1 -0
- package/directives/index.d.ts +1 -0
- package/directives/src/sd-href.directive.d.ts +9 -0
- package/fesm2022/sd-angular-core-components-avatar.mjs +103 -0
- package/fesm2022/sd-angular-core-components-avatar.mjs.map +1 -0
- package/fesm2022/sd-angular-core-components-badge.mjs +101 -91
- package/fesm2022/sd-angular-core-components-badge.mjs.map +1 -1
- package/fesm2022/sd-angular-core-components-button.mjs +70 -96
- package/fesm2022/sd-angular-core-components-button.mjs.map +1 -1
- package/fesm2022/sd-angular-core-components-code-editor.mjs +129 -0
- package/fesm2022/sd-angular-core-components-code-editor.mjs.map +1 -0
- package/fesm2022/sd-angular-core-components-document-builder.mjs +3994 -608
- package/fesm2022/sd-angular-core-components-document-builder.mjs.map +1 -1
- package/fesm2022/sd-angular-core-components-history.mjs +1 -1
- package/fesm2022/sd-angular-core-components-history.mjs.map +1 -1
- package/fesm2022/sd-angular-core-components-import-excel.mjs +1 -1
- package/fesm2022/sd-angular-core-components-import-excel.mjs.map +1 -1
- package/fesm2022/sd-angular-core-components-mini-editor.mjs +326 -0
- package/fesm2022/sd-angular-core-components-mini-editor.mjs.map +1 -0
- package/fesm2022/sd-angular-core-components-preview.mjs +1 -1
- package/fesm2022/sd-angular-core-components-preview.mjs.map +1 -1
- package/fesm2022/sd-angular-core-components-section.mjs +24 -42
- package/fesm2022/sd-angular-core-components-section.mjs.map +1 -1
- package/fesm2022/sd-angular-core-components-side-drawer.mjs +78 -84
- package/fesm2022/sd-angular-core-components-side-drawer.mjs.map +1 -1
- package/fesm2022/sd-angular-core-components-tab-router.mjs +152 -226
- package/fesm2022/sd-angular-core-components-tab-router.mjs.map +1 -1
- package/fesm2022/sd-angular-core-components-table.mjs +1129 -1131
- package/fesm2022/sd-angular-core-components-table.mjs.map +1 -1
- package/fesm2022/sd-angular-core-components-upload-file.mjs +339 -444
- package/fesm2022/sd-angular-core-components-upload-file.mjs.map +1 -1
- package/fesm2022/sd-angular-core-components-view.mjs +45 -0
- package/fesm2022/sd-angular-core-components-view.mjs.map +1 -0
- package/fesm2022/sd-angular-core-components-workflow.mjs +47 -57
- package/fesm2022/sd-angular-core-components-workflow.mjs.map +1 -1
- package/fesm2022/sd-angular-core-components.mjs +4 -0
- package/fesm2022/sd-angular-core-components.mjs.map +1 -1
- package/fesm2022/sd-angular-core-directives.mjs +80 -27
- package/fesm2022/sd-angular-core-directives.mjs.map +1 -1
- package/fesm2022/sd-angular-core-forms-autocomplete.mjs +274 -364
- package/fesm2022/sd-angular-core-forms-autocomplete.mjs.map +1 -1
- package/fesm2022/sd-angular-core-forms-chip-calendar.mjs +5 -2
- package/fesm2022/sd-angular-core-forms-chip-calendar.mjs.map +1 -1
- package/fesm2022/sd-angular-core-forms-chip.mjs +5 -2
- package/fesm2022/sd-angular-core-forms-chip.mjs.map +1 -1
- package/fesm2022/sd-angular-core-forms-date-range.mjs +160 -245
- package/fesm2022/sd-angular-core-forms-date-range.mjs.map +1 -1
- package/fesm2022/sd-angular-core-forms-date.mjs +153 -273
- package/fesm2022/sd-angular-core-forms-date.mjs.map +1 -1
- package/fesm2022/sd-angular-core-forms-datetime.mjs +152 -288
- package/fesm2022/sd-angular-core-forms-datetime.mjs.map +1 -1
- package/fesm2022/sd-angular-core-forms-input-number.mjs +191 -338
- package/fesm2022/sd-angular-core-forms-input-number.mjs.map +1 -1
- package/fesm2022/sd-angular-core-forms-input.mjs +149 -287
- package/fesm2022/sd-angular-core-forms-input.mjs.map +1 -1
- package/fesm2022/sd-angular-core-forms-radio.mjs +3 -2
- package/fesm2022/sd-angular-core-forms-radio.mjs.map +1 -1
- package/fesm2022/sd-angular-core-forms-select.mjs +375 -448
- package/fesm2022/sd-angular-core-forms-select.mjs.map +1 -1
- package/fesm2022/sd-angular-core-forms-textarea.mjs +138 -227
- package/fesm2022/sd-angular-core-forms-textarea.mjs.map +1 -1
- package/fesm2022/sd-angular-core-modules-keycloak.mjs +126 -0
- package/fesm2022/sd-angular-core-modules-keycloak.mjs.map +1 -0
- package/fesm2022/sd-angular-core-modules-layout.mjs +454 -453
- package/fesm2022/sd-angular-core-modules-layout.mjs.map +1 -1
- package/fesm2022/sd-angular-core-modules.mjs +1 -1
- package/fesm2022/sd-angular-core-services-api.mjs +5 -10
- package/fesm2022/sd-angular-core-services-api.mjs.map +1 -1
- package/fesm2022/sd-angular-core-services-confirm.mjs +2 -2
- package/fesm2022/sd-angular-core-services-confirm.mjs.map +1 -1
- package/fesm2022/sd-angular-core-services-docx.mjs +173 -0
- package/fesm2022/sd-angular-core-services-docx.mjs.map +1 -0
- package/fesm2022/sd-angular-core-services-notify.mjs +2 -2
- package/fesm2022/sd-angular-core-services-notify.mjs.map +1 -1
- package/fesm2022/sd-angular-core-services.mjs +1 -0
- package/fesm2022/sd-angular-core-services.mjs.map +1 -1
- package/fesm2022/sd-angular-core-utilities-extensions.mjs +21 -45
- package/fesm2022/sd-angular-core-utilities-extensions.mjs.map +1 -1
- package/fesm2022/sd-angular-core-utilities-models.mjs +15 -1
- package/fesm2022/sd-angular-core-utilities-models.mjs.map +1 -1
- package/forms/autocomplete/src/autocomplete.component.d.ts +50 -55
- package/forms/chip/src/chip.component.d.ts +3 -2
- package/forms/chip-calendar/src/chip-calendar.component.d.ts +3 -2
- package/forms/date/src/date.component.d.ts +43 -46
- package/forms/date-range/src/date-range.component.d.ts +30 -34
- package/forms/datetime/src/datetime.component.d.ts +43 -49
- package/forms/input/src/input.component.d.ts +48 -57
- package/forms/input-number/src/input-number.component.d.ts +48 -54
- package/forms/select/src/select.component.d.ts +62 -64
- package/forms/textarea/src/textarea.component.d.ts +36 -42
- package/modules/index.d.ts +1 -1
- package/modules/keycloak/index.d.ts +4 -0
- package/modules/keycloak/keycloak.configuration.d.ts +11 -0
- package/modules/keycloak/keycloak.interceptor.d.ts +2 -0
- package/modules/keycloak/keycloak.module.d.ts +18 -0
- package/modules/keycloak/keycloak.service.d.ts +14 -0
- package/modules/layout/components/layout-main/layout-main.component.d.ts +7 -12
- package/modules/layout/components/page/page.component.d.ts +5 -7
- package/modules/layout/components/sidebar-v1/components/sidebar/sidebar.component.d.ts +22 -29
- package/modules/layout/components/sidebar-v1/components/user/user.component.d.ts +11 -17
- package/modules/layout/components/sidebar-v1/main.component.d.ts +14 -14
- package/modules/layout/configurations/layout.configuration.d.ts +46 -3
- package/modules/layout/modules/forbidden/pages/root/root.component.d.ts +3 -8
- package/modules/layout/modules/home/components/home-page/home-page.component.d.ts +2 -5
- package/modules/layout/modules/not-found/pages/root/root.component.d.ts +3 -8
- package/modules/layout/pipes/high-light-search.pipe.d.ts +1 -1
- package/modules/layout/services/index.d.ts +1 -0
- package/modules/layout/services/layout.service.d.ts +10 -0
- package/modules/layout/services/menu/menu.model.d.ts +2 -0
- package/modules/layout/services/storage/storage.service.d.ts +0 -3
- package/package.json +93 -70
- package/sd-angular-core-19.0.0-beta.60.tgz +0 -0
- package/services/api/src/api.model.d.ts +6 -1
- package/services/confirm/src/lib/confirm.service.d.ts +1 -0
- package/services/docx/index.d.ts +1 -0
- package/services/docx/src/lib/docx.model.d.ts +9 -0
- package/services/docx/src/lib/docx.service.d.ts +13 -0
- package/services/docx/src/public-api.d.ts +2 -0
- package/services/index.d.ts +1 -0
- package/services/notify/index.d.ts +1 -0
- package/services/notify/src/notify.model.d.ts +1 -1
- package/services/notify/src/notify.service.d.ts +5 -5
- package/utilities/extensions/src/string.extension.d.ts +2 -0
- package/utilities/extensions/src/utility.extension.d.ts +1 -0
- package/utilities/models/index.d.ts +3 -0
- package/utilities/models/src/filter.model.d.ts +14 -2
- package/utilities/models/src/icon.model.d.ts +2 -0
- package/utilities/models/src/nested-key-of.model.d.ts +5 -0
- package/utilities/models/src/pattern.model.d.ts +1 -1
- package/utilities/models/src/unwrap-signal.model.d.ts +6 -0
- package/components/document-builder/src/plugins/comment/comment.plugin.d.ts +0 -4
- package/components/document-builder/src/plugins/table-fit/table-fit.plugin.d.ts +0 -4
- package/fesm2022/sd-angular-core-modules-oidc.mjs +0 -127
- package/fesm2022/sd-angular-core-modules-oidc.mjs.map +0 -1
- package/modules/oidc/dynamic-sts.loader.d.ts +0 -11
- package/modules/oidc/index.d.ts +0 -2
- package/modules/oidc/oidc.configuration.d.ts +0 -11
- package/modules/oidc/oidc.module.d.ts +0 -14
package/README.md
CHANGED
|
@@ -1,64 +1,717 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @sd-angular/core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> **VI** — Thư viện UI nội bộ xây dựng trên Angular Material, hỗ trợ Angular 19+
|
|
4
|
+
> **EN** — Internal UI component library built on Angular Material, supporting Angular 19+
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
[](./package.json)
|
|
7
|
+
[](https://angular.dev)
|
|
8
|
+
[](https://material.angular.io)
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Table of Contents / Mục lục
|
|
13
|
+
|
|
14
|
+
- [Getting Started / Cài đặt](#getting-started--cài-đặt)
|
|
15
|
+
- [Theming / SCSS Customization](#theming--scss-customization)
|
|
16
|
+
- [Components](#components)
|
|
17
|
+
- [SdButton](#sdbutton)
|
|
18
|
+
- [SdBadge](#sdbadge)
|
|
19
|
+
- [SdSection](#sdsection)
|
|
20
|
+
- [SdModal](#sdmodal)
|
|
21
|
+
- [SdTable](#sdtable)
|
|
22
|
+
- [SdAvatar](#sdavatar)
|
|
23
|
+
- [Other Components](#other-components--các-component-khác)
|
|
24
|
+
- [Form Components](#form-components)
|
|
25
|
+
- [CRUD Patterns / Code mẫu CRUD](#crud-patterns--code-mẫu-crud)
|
|
26
|
+
- [Contributing Guide / Hướng dẫn đóng góp](#contributing-guide--hướng-dẫn-đóng-góp)
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Getting Started / Cài đặt
|
|
31
|
+
|
|
32
|
+
### Prerequisites / Yêu cầu
|
|
33
|
+
|
|
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` |
|
|
38
|
+
| `@angular/material-moment-adapter` | `^19.0.0 \|\| ^20.0.0 \|\| ^21.0.0` |
|
|
39
|
+
|
|
40
|
+
### Installation / Cài đặt
|
|
8
41
|
|
|
9
42
|
```bash
|
|
10
|
-
|
|
43
|
+
npm install @sd-angular/core
|
|
11
44
|
```
|
|
12
45
|
|
|
13
|
-
|
|
46
|
+
### Setup
|
|
14
47
|
|
|
15
|
-
|
|
16
|
-
|
|
48
|
+
**1. Import global styles / Import style toàn cục**
|
|
49
|
+
|
|
50
|
+
Thêm vào `angular.json` (hoặc `styles.scss` của app):
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
// angular.json
|
|
54
|
+
{
|
|
55
|
+
"styles": [
|
|
56
|
+
"node_modules/@sd-angular/core/assets/scss/sd-core.scss"
|
|
57
|
+
]
|
|
58
|
+
}
|
|
17
59
|
```
|
|
18
60
|
|
|
19
|
-
|
|
61
|
+
hoặc trong `styles.scss`:
|
|
20
62
|
|
|
21
|
-
|
|
63
|
+
```scss
|
|
64
|
+
@use '@sd-angular/core/assets/scss/sd-core';
|
|
65
|
+
```
|
|
22
66
|
|
|
23
|
-
|
|
24
|
-
|
|
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
|
+
};
|
|
25
88
|
```
|
|
26
89
|
|
|
27
|
-
|
|
90
|
+
---
|
|
28
91
|
|
|
29
|
-
|
|
92
|
+
## Theming / SCSS Customization
|
|
30
93
|
|
|
31
|
-
|
|
94
|
+
### CSS Variables
|
|
32
95
|
|
|
33
|
-
|
|
96
|
+
`@sd-angular/core` 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>`.
|
|
34
97
|
|
|
35
|
-
|
|
36
|
-
cd dist/sd-angular
|
|
37
|
-
```
|
|
98
|
+
**Available color tokens / Các biến màu sắc:**
|
|
38
99
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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 |
|
|
43
120
|
|
|
44
|
-
|
|
121
|
+
### Custom Theme / Tuỳ chỉnh theme
|
|
45
122
|
|
|
46
|
-
|
|
123
|
+
Ghi đè theme mặc định bằng cách truyền map SCSS vào mixin `theme()`:
|
|
47
124
|
|
|
48
|
-
```
|
|
49
|
-
|
|
125
|
+
```scss
|
|
126
|
+
// styles.scss
|
|
127
|
+
@use '@sd-angular/core/assets/scss/themes/default' as default;
|
|
128
|
+
|
|
129
|
+
html {
|
|
130
|
+
@include default.theme((
|
|
131
|
+
primary: #7C3AED,
|
|
132
|
+
primary-light: #EDE9FE,
|
|
133
|
+
primary-dark: #5B21B6,
|
|
134
|
+
success: #10B981,
|
|
135
|
+
error: #EF4444,
|
|
136
|
+
));
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
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.
|
|
141
|
+
|
|
142
|
+
### Utility Classes / Các class tiện ích
|
|
143
|
+
|
|
144
|
+
Thư viện cung cấp sẵn các utility class:
|
|
145
|
+
|
|
146
|
+
```html
|
|
147
|
+
<!-- Text color -->
|
|
148
|
+
<span class="text-primary">Text màu primary</span>
|
|
149
|
+
<span class="text-error">Text màu error</span>
|
|
150
|
+
|
|
151
|
+
<!-- Background -->
|
|
152
|
+
<div class="bg-primary-light">Background nhạt</div>
|
|
153
|
+
|
|
154
|
+
<!-- Spacing (đơn vị px, từ 0–200) -->
|
|
155
|
+
<div class="mt-16 mb-8 px-24">margin-top: 16px, padding: 0 24px</div>
|
|
156
|
+
|
|
157
|
+
<!-- Gap -->
|
|
158
|
+
<div class="d-flex gap-8">gap: 8px</div>
|
|
159
|
+
|
|
160
|
+
<!-- Grid -->
|
|
161
|
+
<div class="sd-grid-container grid-cols-3">
|
|
162
|
+
<div class="col-span-2">Chiếm 2 cột</div>
|
|
163
|
+
<div class="col-span-1">Chiếm 1 cột</div>
|
|
164
|
+
</div>
|
|
165
|
+
|
|
166
|
+
<!-- Bootstrap grid -->
|
|
167
|
+
<div class="row">
|
|
168
|
+
<div class="col-6">50%</div>
|
|
169
|
+
<div class="col-6">50%</div>
|
|
170
|
+
</div>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Components
|
|
176
|
+
|
|
177
|
+
> Tất cả component đều là **standalone** và sử dụng **Angular Signals** API.
|
|
178
|
+
> All components are **standalone** and use **Angular Signals** API.
|
|
179
|
+
|
|
180
|
+
### SdButton
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
import { SdButton } from '@sd-angular/core/components/button';
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
**Inputs:**
|
|
187
|
+
|
|
188
|
+
| Input | Type | Default | Description |
|
|
189
|
+
|---|---|---|---|
|
|
190
|
+
| `type` | `'fill' \| 'light' \| 'outline' \| 'link'` | `'light'` | Kiểu nút |
|
|
191
|
+
| `color` | `SdColor` | `'secondary'` | Màu sắc |
|
|
192
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | `'sm'` | Kích thước |
|
|
193
|
+
| `title` | `string` | — | Nhãn nút |
|
|
194
|
+
| `prefixIcon` | `string` | — | Icon Material trước text |
|
|
195
|
+
| `suffixIcon` | `string` | — | Icon Material sau text |
|
|
196
|
+
| `disabled` | `boolean` | `false` | Vô hiệu hoá |
|
|
197
|
+
| `loading` | `boolean` | `false` | Trạng thái loading (tự chặn click) |
|
|
198
|
+
| `tooltip` | `string` | — | Tooltip khi hover |
|
|
199
|
+
| `width` | `string` | — | CSS width tuỳ chỉnh |
|
|
200
|
+
|
|
201
|
+
**Output:** `(click): EventEmitter<Event>` — có throttle 300ms, tự chặn khi `disabled` hoặc `loading`.
|
|
202
|
+
|
|
203
|
+
```html
|
|
204
|
+
<sd-button type="fill" color="primary" title="Lưu" prefixIcon="save" (click)="onSave()" />
|
|
205
|
+
<sd-button type="outline" color="error" prefixIcon="delete" tooltip="Xoá" />
|
|
206
|
+
<sd-button type="light" title="Huỷ" (click)="modal.close()" />
|
|
207
|
+
<sd-button type="fill" color="primary" title="Đang xử lý" [loading]="true" />
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
### SdBadge
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
import { SdBadge } from '@sd-angular/core/components/badge';
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
**Inputs:**
|
|
219
|
+
|
|
220
|
+
| Input | Type | Default | Description |
|
|
221
|
+
|---|---|---|---|
|
|
222
|
+
| `type` | `'tag' \| 'round' \| 'icon'` | `'icon'` | Kiểu badge |
|
|
223
|
+
| `color` | `SdColor` | `'secondary'` | Màu sắc |
|
|
224
|
+
| `title` | `string \| number` | — | Nội dung hiển thị |
|
|
225
|
+
| `icon` | `string` | — | Icon Material |
|
|
226
|
+
| `size` | `SdSize` | `'sm'` | Kích thước |
|
|
227
|
+
| `tooltip` | `string` | — | Tooltip |
|
|
228
|
+
|
|
229
|
+
Shorthand color inputs (boolean): `primary`, `secondary`, `success`, `info`, `warning`, `error`.
|
|
230
|
+
|
|
231
|
+
```html
|
|
232
|
+
<sd-badge type="tag" color="success" title="Hoạt động" />
|
|
233
|
+
<sd-badge type="round" [warning]="true" title="Chờ duyệt" />
|
|
234
|
+
<sd-badge type="icon" color="error" icon="close" title="Từ chối" />
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
### SdSection
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
import { SdSection } from '@sd-angular/core/components/section';
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**Inputs:**
|
|
246
|
+
|
|
247
|
+
| Input | Type | Default | Description |
|
|
248
|
+
|---|---|---|---|
|
|
249
|
+
| `title` | `string` | *required* | Tiêu đề section |
|
|
250
|
+
| `subTitle` | `string` | — | Tiêu đề phụ |
|
|
251
|
+
| `icon` | `string` | — | Icon Material |
|
|
252
|
+
| `iconColor` | `SdColor` | `'primary'` | Màu icon |
|
|
253
|
+
| `collapsable` | `boolean` | `false` | Cho phép thu gọn |
|
|
254
|
+
| `collapsed` | `boolean` | `false` | Trạng thái ban đầu thu gọn |
|
|
255
|
+
| `hideHeader` | `boolean` | `false` | Ẩn phần header |
|
|
256
|
+
|
|
257
|
+
```html
|
|
258
|
+
<sd-section title="Thông tin cơ bản" icon="person" iconColor="primary">
|
|
259
|
+
<!-- nội dung -->
|
|
260
|
+
</sd-section>
|
|
261
|
+
|
|
262
|
+
<sd-section title="Cài đặt nâng cao" icon="settings" collapsable [collapsed]="true">
|
|
263
|
+
<!-- nội dung ẩn mặc định -->
|
|
264
|
+
</sd-section>
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
### SdModal
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
import { SdModal } from '@sd-angular/core/components/modal';
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
**Inputs:**
|
|
276
|
+
|
|
277
|
+
| Input | Type | Default | Description |
|
|
278
|
+
|---|---|---|---|
|
|
279
|
+
| `title` | `string` | — | Tiêu đề modal |
|
|
280
|
+
| `color` | `SdColor` | `'primary'` | Màu header |
|
|
281
|
+
| `width` | `'sx' \| 'sm' \| 'md' \| 'lg' \| string` | `'md'` | Độ rộng (md = 60vw) |
|
|
282
|
+
| `height` | `string` | `'auto'` | Chiều cao |
|
|
283
|
+
| `view` | `'dialog' \| 'bottom-sheet'` | auto | Tự động bottom-sheet trên mobile |
|
|
284
|
+
| `lazyLoadContent` | `boolean` | `true` | Lazy render nội dung |
|
|
285
|
+
|
|
286
|
+
**Output:** `(sdClosed): EventEmitter` — phát ra khi modal đóng.
|
|
287
|
+
|
|
288
|
+
**Methods** (dùng qua `@ViewChild`):
|
|
289
|
+
- `modal.open()` — mở modal
|
|
290
|
+
- `modal.close()` — đóng modal
|
|
291
|
+
|
|
292
|
+
> ⚠️ Nội dung modal phải đặt trong `<ng-template>`.
|
|
293
|
+
|
|
294
|
+
```html
|
|
295
|
+
<sd-modal #myModal title="Thêm mới" width="md" (sdClosed)="onClosed()">
|
|
296
|
+
<ng-template>
|
|
297
|
+
<div class="modal-body p-16">
|
|
298
|
+
<!-- nội dung form -->
|
|
299
|
+
</div>
|
|
300
|
+
<div class="modal-footer d-flex justify-content-end gap-8 p-16">
|
|
301
|
+
<sd-button title="Huỷ" (click)="myModal.close()" />
|
|
302
|
+
<sd-button type="fill" color="primary" title="Lưu" (click)="onSave()" />
|
|
303
|
+
</div>
|
|
304
|
+
</ng-template>
|
|
305
|
+
</sd-modal>
|
|
306
|
+
|
|
307
|
+
<sd-button title="Mở modal" prefixIcon="add" (click)="myModal.open()" />
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
### SdTable
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
import { SdTable } from '@sd-angular/core/components/table';
|
|
316
|
+
import type { SdTableOption, SdTableColumn } from '@sd-angular/core/components/table';
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
SdTable nhận một object `option` duy nhất kiểu `SdTableOption<T>`.
|
|
320
|
+
|
|
321
|
+
**Column types / Kiểu cột:**
|
|
322
|
+
|
|
323
|
+
| `type` | Mô tả |
|
|
324
|
+
|---|---|
|
|
325
|
+
| `'string'` | Văn bản |
|
|
326
|
+
| `'number'` | Số (tự format) |
|
|
327
|
+
| `'boolean'` | True/False |
|
|
328
|
+
| `'date'` | Ngày |
|
|
329
|
+
| `'datetime'` | Ngày giờ |
|
|
330
|
+
| `'time'` | Giờ |
|
|
331
|
+
| `'values'` | Enum từ danh sách cố định |
|
|
332
|
+
| `'lazy-values'` | Enum load async |
|
|
333
|
+
| `'children'` | Cột nhóm (multi-header) |
|
|
334
|
+
|
|
335
|
+
**Local table:**
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
option: SdTableOption<Product> = {
|
|
339
|
+
type: 'local',
|
|
340
|
+
items: () => this.products,
|
|
341
|
+
columns: [
|
|
342
|
+
{ field: 'code', type: 'string', title: 'Mã', width: '120px' },
|
|
343
|
+
{ field: 'name', type: 'string', title: 'Tên', sortable: true },
|
|
344
|
+
{ field: 'price', type: 'number', title: 'Đơn giá', align: 'right' },
|
|
345
|
+
{ field: 'active', type: 'boolean', title: 'Kích hoạt',
|
|
346
|
+
useBadge: (val) => ({ color: val ? 'success' : 'secondary', title: val ? 'Có' : 'Không' })
|
|
347
|
+
},
|
|
348
|
+
],
|
|
349
|
+
paginate: { pageSize: 20 },
|
|
350
|
+
reload: { visible: true },
|
|
351
|
+
};
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
**Server-side table:**
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
option: SdTableOption<Product> = {
|
|
358
|
+
type: 'server',
|
|
359
|
+
items: async (filterRequest, pagingReq) => {
|
|
360
|
+
const res = await this.service.search({
|
|
361
|
+
keyword: filterRequest.keyword,
|
|
362
|
+
page: pagingReq.page,
|
|
363
|
+
pageSize: pagingReq.pageSize,
|
|
364
|
+
});
|
|
365
|
+
return { items: res.data, total: res.total };
|
|
366
|
+
},
|
|
367
|
+
columns: [...],
|
|
368
|
+
command: {
|
|
369
|
+
align: 'right',
|
|
370
|
+
commands: [
|
|
371
|
+
{ icon: 'edit', color: 'primary', title: 'Sửa', click: (row) => this.onEdit(row) },
|
|
372
|
+
{ icon: 'delete', color: 'error', title: 'Xoá', click: (row) => this.onDelete(row) },
|
|
373
|
+
],
|
|
374
|
+
},
|
|
375
|
+
};
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
```html
|
|
379
|
+
<sd-table #sdTable [option]="option" />
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
---
|
|
383
|
+
|
|
384
|
+
### SdAvatar
|
|
385
|
+
|
|
386
|
+
```typescript
|
|
387
|
+
import { SdAvatar } from '@sd-angular/core/components/avatar';
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
```html
|
|
391
|
+
<sd-avatar src="/api/avatar/123" name="Nguyễn Văn A" size="md" />
|
|
392
|
+
<sd-avatar name="NVA" color="primary" size="lg" />
|
|
50
393
|
```
|
|
51
394
|
|
|
52
|
-
|
|
395
|
+
---
|
|
53
396
|
|
|
54
|
-
|
|
397
|
+
### Other Components / Các component khác
|
|
398
|
+
|
|
399
|
+
| Component | Import | Mô tả |
|
|
400
|
+
|---|---|---|
|
|
401
|
+
| `SdTabRouter` | `@sd-angular/core/components/tab-router` | Tab navigation với Angular Router |
|
|
402
|
+
| `SdSideDrawer` | `@sd-angular/core/components/side-drawer` | Drawer layout trái/phải |
|
|
403
|
+
| `SdUploadFile` | `@sd-angular/core/components/upload-file` | Upload file |
|
|
404
|
+
| `SdQuickAction` | `@sd-angular/core/components/quick-action` | Nút action dạng icon |
|
|
405
|
+
| `SdHistory` | `@sd-angular/core/components/history` | Lịch sử thay đổi |
|
|
406
|
+
| `SdImportExcel` | `@sd-angular/core/components/import-excel` | Wizard import Excel |
|
|
407
|
+
| `SdQueryBuilder` | `@sd-angular/core/components/query-builder` | Visual query builder |
|
|
408
|
+
| `SdWorkflow` | `@sd-angular/core/components/workflow` | Workflow builder |
|
|
409
|
+
| `SdCodeEditor` | `@sd-angular/core/components/code-editor` | Code editor (PrismJS) |
|
|
410
|
+
| `SdMiniEditor` | `@sd-angular/core/components/mini-editor` | Rich text editor nhỏ |
|
|
411
|
+
| `SdDocumentBuilder` | `@sd-angular/core/components/document-builder` | Document builder |
|
|
412
|
+
| `SdAnchorMain` | `@sd-angular/core/components/anchor` | Anchor / mục lục cuộn trang |
|
|
413
|
+
| `SdView` | `@sd-angular/core/components/view` | View wrapper read-only |
|
|
414
|
+
|
|
415
|
+
---
|
|
416
|
+
|
|
417
|
+
## Form Components
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
import {
|
|
421
|
+
SdInput, // Text input
|
|
422
|
+
SdInputNumber, // Number input
|
|
423
|
+
SdSelect, // Dropdown
|
|
424
|
+
SdAutocomplete, // Autocomplete
|
|
425
|
+
SdDate, // Date picker
|
|
426
|
+
SdSearch, // Search với debounce
|
|
427
|
+
} from '@sd-angular/core/forms';
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
```html
|
|
431
|
+
<sd-input [(ngModel)]="form.name" label="Họ tên" [required]="true" />
|
|
432
|
+
|
|
433
|
+
<sd-input-number [(ngModel)]="form.price" label="Đơn giá" [min]="0" suffix="VNĐ" />
|
|
434
|
+
|
|
435
|
+
<sd-select [(ngModel)]="form.status" label="Trạng thái"
|
|
436
|
+
[items]="statusList" valueField="value" displayField="label" />
|
|
437
|
+
|
|
438
|
+
<sd-date [(ngModel)]="form.birthday" label="Ngày sinh" />
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
## CRUD Patterns / Code mẫu CRUD
|
|
444
|
+
|
|
445
|
+
### List Component
|
|
446
|
+
|
|
447
|
+
```typescript
|
|
448
|
+
// product-list.component.ts
|
|
449
|
+
import { Component, OnInit, ViewChild, signal } from '@angular/core';
|
|
450
|
+
import { SdTable, SdTableOption } from '@sd-angular/core/components/table';
|
|
451
|
+
import { SdButton } from '@sd-angular/core/components/button';
|
|
452
|
+
import { SdModal } from '@sd-angular/core/components/modal';
|
|
453
|
+
import { SdSection } from '@sd-angular/core/components/section';
|
|
454
|
+
import { SdInput, SdSelect } from '@sd-angular/core/forms';
|
|
455
|
+
|
|
456
|
+
interface Product {
|
|
457
|
+
id: number;
|
|
458
|
+
code: string;
|
|
459
|
+
name: string;
|
|
460
|
+
price: number;
|
|
461
|
+
status: 'ACTIVE' | 'INACTIVE';
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
@Component({
|
|
465
|
+
selector: 'app-product-list',
|
|
466
|
+
templateUrl: './product-list.component.html',
|
|
467
|
+
standalone: true,
|
|
468
|
+
imports: [SdTable, SdButton, SdModal, SdSection, SdInput, SdSelect],
|
|
469
|
+
})
|
|
470
|
+
export class ProductListComponent implements OnInit {
|
|
471
|
+
@ViewChild('formModal') formModal!: SdModal;
|
|
472
|
+
@ViewChild('sdTable') sdTable?: SdTable<Product>;
|
|
473
|
+
|
|
474
|
+
selectedItem: Product | null = null;
|
|
475
|
+
formData: Partial<Product> = {};
|
|
476
|
+
isSaving = signal(false);
|
|
477
|
+
|
|
478
|
+
readonly STATUS_LIST = [
|
|
479
|
+
{ value: 'ACTIVE', label: 'Hoạt động' },
|
|
480
|
+
{ value: 'INACTIVE', label: 'Dừng' },
|
|
481
|
+
];
|
|
482
|
+
|
|
483
|
+
option!: SdTableOption<Product>;
|
|
484
|
+
|
|
485
|
+
constructor(private service: ProductService) {}
|
|
486
|
+
|
|
487
|
+
ngOnInit() {
|
|
488
|
+
this.option = {
|
|
489
|
+
type: 'server',
|
|
490
|
+
items: async (filter, paging) => this.service.search(filter, paging),
|
|
491
|
+
columns: [
|
|
492
|
+
{ field: 'code', type: 'string', title: 'Mã', width: '120px' },
|
|
493
|
+
{ field: 'name', type: 'string', title: 'Tên', sortable: true },
|
|
494
|
+
{ field: 'price', type: 'number', title: 'Đơn giá', align: 'right' },
|
|
495
|
+
{
|
|
496
|
+
field: 'status', type: 'values', title: 'Trạng thái',
|
|
497
|
+
option: { items: this.STATUS_LIST, valueField: 'value', displayField: 'label' },
|
|
498
|
+
useBadge: (val) => ({
|
|
499
|
+
color: val === 'ACTIVE' ? 'success' : 'secondary',
|
|
500
|
+
title: this.STATUS_LIST.find(s => s.value === val)?.label,
|
|
501
|
+
}),
|
|
502
|
+
},
|
|
503
|
+
],
|
|
504
|
+
command: {
|
|
505
|
+
align: 'right',
|
|
506
|
+
commands: [
|
|
507
|
+
{ icon: 'edit', color: 'primary', title: 'Sửa', click: (row) => this.openForm(row) },
|
|
508
|
+
{ icon: 'delete', color: 'error', title: 'Xoá', click: (row) => this.onDelete(row) },
|
|
509
|
+
],
|
|
510
|
+
},
|
|
511
|
+
paginate: { pageSize: 20 },
|
|
512
|
+
reload: { visible: true },
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
openForm(item?: Product) {
|
|
517
|
+
this.selectedItem = item || null;
|
|
518
|
+
this.formData = item ? { ...item } : { status: 'ACTIVE' };
|
|
519
|
+
this.formModal.open();
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
async onSave() {
|
|
523
|
+
this.isSaving.set(true);
|
|
524
|
+
try {
|
|
525
|
+
if (this.selectedItem) {
|
|
526
|
+
await this.service.update(this.selectedItem.id, this.formData);
|
|
527
|
+
} else {
|
|
528
|
+
await this.service.create(this.formData);
|
|
529
|
+
}
|
|
530
|
+
this.formModal.close();
|
|
531
|
+
this.sdTable?.reload?.();
|
|
532
|
+
} finally {
|
|
533
|
+
this.isSaving.set(false);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
async onDelete(item: Product) {
|
|
538
|
+
if (!confirm(`Xoá "${item.name}"?`)) return;
|
|
539
|
+
await this.service.delete(item.id);
|
|
540
|
+
this.sdTable?.reload?.();
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
### Template
|
|
546
|
+
|
|
547
|
+
```html
|
|
548
|
+
<!-- product-list.component.html -->
|
|
549
|
+
<div class="d-flex justify-content-between align-items-center mb-16">
|
|
550
|
+
<h2>Danh sách sản phẩm</h2>
|
|
551
|
+
<sd-button type="fill" color="primary" title="Thêm mới"
|
|
552
|
+
prefixIcon="add" (click)="openForm()" />
|
|
553
|
+
</div>
|
|
554
|
+
|
|
555
|
+
<sd-table #sdTable [option]="option" />
|
|
556
|
+
|
|
557
|
+
<sd-modal #formModal [title]="selectedItem ? 'Chỉnh sửa' : 'Thêm mới'" width="md">
|
|
558
|
+
<ng-template>
|
|
559
|
+
<div class="modal-body p-16">
|
|
560
|
+
<sd-section title="Thông tin sản phẩm" icon="inventory">
|
|
561
|
+
<div class="row">
|
|
562
|
+
<div class="col-6">
|
|
563
|
+
<sd-input [(ngModel)]="formData.code" label="Mã" [required]="true" />
|
|
564
|
+
</div>
|
|
565
|
+
<div class="col-6">
|
|
566
|
+
<sd-select [(ngModel)]="formData.status" label="Trạng thái"
|
|
567
|
+
[items]="STATUS_LIST" valueField="value" displayField="label" />
|
|
568
|
+
</div>
|
|
569
|
+
<div class="col-12">
|
|
570
|
+
<sd-input [(ngModel)]="formData.name" label="Tên sản phẩm" [required]="true" />
|
|
571
|
+
</div>
|
|
572
|
+
<div class="col-6">
|
|
573
|
+
<sd-input-number [(ngModel)]="formData.price" label="Đơn giá" suffix="VNĐ" />
|
|
574
|
+
</div>
|
|
575
|
+
</div>
|
|
576
|
+
</sd-section>
|
|
577
|
+
</div>
|
|
578
|
+
<div class="modal-footer d-flex justify-content-end gap-8 p-16">
|
|
579
|
+
<sd-button title="Huỷ" (click)="formModal.close()" />
|
|
580
|
+
<sd-button type="fill" color="primary" title="Lưu"
|
|
581
|
+
prefixIcon="save" [loading]="isSaving()" (click)="onSave()" />
|
|
582
|
+
</div>
|
|
583
|
+
</ng-template>
|
|
584
|
+
</sd-modal>
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
---
|
|
588
|
+
|
|
589
|
+
## Contributing Guide / Hướng dẫn đóng góp
|
|
590
|
+
|
|
591
|
+
### Cấu trúc thư viện / Project structure
|
|
592
|
+
|
|
593
|
+
```
|
|
594
|
+
sd-angular/
|
|
595
|
+
├── src/
|
|
596
|
+
│ └── public-api.ts # Entry point chính
|
|
597
|
+
├── assets/
|
|
598
|
+
│ └── scss/
|
|
599
|
+
│ ├── sd-core.scss # SCSS entry (import vào app)
|
|
600
|
+
│ ├── core/ # Base utilities (color, grid, form, ...)
|
|
601
|
+
│ └── themes/ # Theme mặc định + Material theme
|
|
602
|
+
├── components/ # UI Components
|
|
603
|
+
│ ├── button/
|
|
604
|
+
│ ├── table/
|
|
605
|
+
│ ├── modal/
|
|
606
|
+
│ └── ...
|
|
607
|
+
├── forms/ # Form components
|
|
608
|
+
│ ├── input/
|
|
609
|
+
│ ├── select/
|
|
610
|
+
│ └── ...
|
|
611
|
+
├── directives/ # Angular directives
|
|
612
|
+
├── pipes/ # Angular pipes
|
|
613
|
+
├── services/ # Shared services
|
|
614
|
+
├── utilities/ # Types, models, helpers
|
|
615
|
+
└── modules/ # Feature modules (layout, permission, ...)
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
### Thêm component mới / Adding a new component
|
|
619
|
+
|
|
620
|
+
**1. Tạo thư mục component:**
|
|
621
|
+
|
|
622
|
+
```
|
|
623
|
+
components/
|
|
624
|
+
└── my-component/
|
|
625
|
+
├── index.ts # Export public API
|
|
626
|
+
├── ng-package.json # ng-packagr entry
|
|
627
|
+
└── src/
|
|
628
|
+
├── my-component.component.ts
|
|
629
|
+
├── my-component.component.html
|
|
630
|
+
└── my-component.component.scss
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
**2. `ng-package.json`:**
|
|
634
|
+
|
|
635
|
+
```json
|
|
636
|
+
{
|
|
637
|
+
"$schema": "../../../../node_modules/ng-packagr/ng-package.schema.json",
|
|
638
|
+
"lib": {
|
|
639
|
+
"entryFile": "index.ts"
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
**3. `index.ts`:**
|
|
645
|
+
|
|
646
|
+
```typescript
|
|
647
|
+
export * from './src/my-component.component';
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
**4. Component template:**
|
|
651
|
+
|
|
652
|
+
```typescript
|
|
653
|
+
// my-component.component.ts
|
|
654
|
+
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
|
|
655
|
+
import { SdBaseSecureComponent } from '@sd-angular/core/components/base';
|
|
656
|
+
import { SdColor } from '@sd-angular/core/utilities';
|
|
657
|
+
|
|
658
|
+
@Component({
|
|
659
|
+
selector: 'sd-my-component',
|
|
660
|
+
templateUrl: './my-component.component.html',
|
|
661
|
+
styleUrls: ['./my-component.component.scss'],
|
|
662
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
663
|
+
standalone: true,
|
|
664
|
+
imports: [],
|
|
665
|
+
})
|
|
666
|
+
export class SdMyComponent extends SdBaseSecureComponent {
|
|
667
|
+
color = input<SdColor, SdColor | undefined | null>('primary', {
|
|
668
|
+
transform: (value) => value || 'primary',
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
title = input<string | undefined | null>(undefined);
|
|
672
|
+
}
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
**5. Export từ `components/index.ts`:**
|
|
676
|
+
|
|
677
|
+
```typescript
|
|
678
|
+
// components/index.ts
|
|
679
|
+
export * from '@sd-angular/core/components/my-component';
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
### Quy ước / Conventions
|
|
683
|
+
|
|
684
|
+
| Mục | Quy ước |
|
|
685
|
+
|---|---|
|
|
686
|
+
| Selector | `sd-<tên-component>` |
|
|
687
|
+
| Class name | `Sd<TênComponent>` (Pascal) |
|
|
688
|
+
| Input | Dùng `input<T>()` signal, **không** dùng `@Input()` decorator |
|
|
689
|
+
| Null safety | Input transform phải handle `null/undefined` |
|
|
690
|
+
| Base class | Extend `SdBaseSecureComponent` cho component có permission |
|
|
691
|
+
| Change detection | Luôn dùng `ChangeDetectionStrategy.OnPush` |
|
|
692
|
+
| Standalone | Luôn `standalone: true` |
|
|
693
|
+
| Colors | Dùng `SdColor` type, không hardcode màu |
|
|
694
|
+
|
|
695
|
+
### Build
|
|
55
696
|
|
|
56
697
|
```bash
|
|
57
|
-
|
|
698
|
+
# Build toàn bộ thư viện
|
|
699
|
+
ng-packagr -p ng-package.json
|
|
700
|
+
|
|
701
|
+
# Watch mode
|
|
702
|
+
ng-packagr -p ng-package.json --watch
|
|
58
703
|
```
|
|
59
704
|
|
|
60
|
-
|
|
705
|
+
### Versioning
|
|
706
|
+
|
|
707
|
+
Thư viện tuân theo [Semantic Versioning](https://semver.org):
|
|
708
|
+
|
|
709
|
+
- `MAJOR` — Breaking changes (thay đổi API không tương thích)
|
|
710
|
+
- `MINOR` — Tính năng mới (tương thích ngược)
|
|
711
|
+
- `PATCH` — Bug fixes
|
|
712
|
+
|
|
713
|
+
---
|
|
61
714
|
|
|
62
|
-
##
|
|
715
|
+
## License
|
|
63
716
|
|
|
64
|
-
|
|
717
|
+
Internal use only — © SD Team
|