@sd-angular/core 19.0.0-beta.36 → 19.0.0-beta.38

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 (39) hide show
  1. package/assets/scss/core/bootstrap.scss +17 -0
  2. package/assets/scss/core/grid.scss +40 -0
  3. package/assets/scss/sd-core.scss +1 -0
  4. package/components/avatar/src/avatar.component.d.ts +2 -1
  5. package/components/button/src/button.component.d.ts +26 -27
  6. package/components/document-builder/index.d.ts +1 -0
  7. package/components/document-builder/src/document-builder.component.d.ts +3 -42
  8. package/components/document-builder/src/document-builder.model.d.ts +3 -14
  9. package/components/document-builder/src/plugins/ck-comment/ck-comment.plugin.d.ts +43 -0
  10. package/components/document-builder/src/plugins/ck-comment/ck-comment.plugin.model.d.ts +50 -0
  11. package/components/document-builder/src/plugins/index.d.ts +2 -1
  12. package/components/table/src/components/selector-action/action-filter.pipe.d.ts +11 -10
  13. package/components/table/src/models/table-option-selector.model.d.ts +11 -10
  14. package/fesm2022/sd-angular-core-components-avatar.mjs +15 -13
  15. package/fesm2022/sd-angular-core-components-avatar.mjs.map +1 -1
  16. package/fesm2022/sd-angular-core-components-button.mjs +63 -96
  17. package/fesm2022/sd-angular-core-components-button.mjs.map +1 -1
  18. package/fesm2022/sd-angular-core-components-document-builder.mjs +729 -255
  19. package/fesm2022/sd-angular-core-components-document-builder.mjs.map +1 -1
  20. package/fesm2022/sd-angular-core-components-import-excel.mjs +1 -1
  21. package/fesm2022/sd-angular-core-components-import-excel.mjs.map +1 -1
  22. package/fesm2022/sd-angular-core-components-preview.mjs +1 -1
  23. package/fesm2022/sd-angular-core-components-preview.mjs.map +1 -1
  24. package/fesm2022/sd-angular-core-components-table.mjs +6 -6
  25. package/fesm2022/sd-angular-core-components-table.mjs.map +1 -1
  26. package/fesm2022/sd-angular-core-components-upload-file.mjs +1 -1
  27. package/fesm2022/sd-angular-core-components-upload-file.mjs.map +1 -1
  28. package/fesm2022/sd-angular-core-components-workflow.mjs +11 -11
  29. package/fesm2022/sd-angular-core-components-workflow.mjs.map +1 -1
  30. package/fesm2022/sd-angular-core-modules-layout.mjs +3 -3
  31. package/fesm2022/sd-angular-core-modules-layout.mjs.map +1 -1
  32. package/fesm2022/sd-angular-core-services-confirm.mjs +1 -1
  33. package/fesm2022/sd-angular-core-services-confirm.mjs.map +1 -1
  34. package/package.json +52 -52
  35. package/sd-angular-core-19.0.0-beta.38.tgz +0 -0
  36. package/utilities/models/index.d.ts +1 -0
  37. package/utilities/models/src/unwrap-signal.model.d.ts +6 -0
  38. package/components/document-builder/src/plugins/comment/comment.plugin.d.ts +0 -4
  39. package/sd-angular-core-19.0.0-beta.36.tgz +0 -0
@@ -81,6 +81,23 @@
81
81
  .fs-#{$i} {
82
82
  font-size: #{$i}px !important;
83
83
  }
84
+
85
+ // Gap
86
+ .gap-#{$i} {
87
+ gap: #{$i}px !important;
88
+ }
89
+
90
+ .gap-y-#{$i} {
91
+ row-gap: #{$i}px !important;
92
+ }
93
+
94
+ .gap-x-#{$i} {
95
+ column-gap: #{$i}px !important;
96
+ }
97
+ }
98
+
99
+ .flex-1 {
100
+ flex: 1;
84
101
  }
85
102
 
86
103
  .w-full {
@@ -0,0 +1,40 @@
1
+ /* ==========================================================================
2
+ CSS GRID UTILITIES
3
+ ========================================================================== */
4
+
5
+ /* 1. Kích hoạt Grid cơ bản */
6
+ .sd-grid-container {
7
+ display: grid;
8
+ column-gap: 8px;
9
+ row-gap: 0px;
10
+
11
+ /* 2. Định nghĩa tổng số cột của Grid Cha (từ 1 đến 12 cột) */
12
+ @for $i from 1 through 12 {
13
+ &.grid-cols-#{$i} {
14
+ /* Dùng minmax(0, 1fr) thay vì 1fr để chống vỡ layout khi nội dung text quá dài */
15
+ grid-template-columns: repeat($i, minmax(0, 1fr));
16
+ }
17
+ }
18
+
19
+ /* 3. Tiện ích chiếm CỘT (Column Spanning) cho phần tử con */
20
+ @for $i from 1 through 12 {
21
+ .col-span-#{$i} {
22
+ grid-column: span #{$i} / span #{$i};
23
+ }
24
+ }
25
+ /* Chiếm toàn bộ số cột hiện có (Full width) */
26
+ .col-span-full {
27
+ grid-column: 1 / -1;
28
+ }
29
+
30
+ /* 4. Tiện ích chiếm HÀNG (Row Spanning) cho phần tử con */
31
+ @for $i from 1 through 12 {
32
+ .row-span-#{$i} {
33
+ grid-row: span #{$i} / span #{$i};
34
+ }
35
+ }
36
+ /* Chiếm toàn bộ hàng (Full height) */
37
+ .row-span-full {
38
+ grid-row: 1 / -1;
39
+ }
40
+ }
@@ -7,6 +7,7 @@
7
7
  @use './core/typography.scss';
8
8
  @use './core/print.scss';
9
9
  @use './core/elevation.scss';
10
+ @use './core/grid.scss';
10
11
  @use './themes/default.scss';
11
12
  @use './themes/material-theme.scss';
12
13
 
@@ -4,11 +4,12 @@ export declare class SdAvatar implements OnInit {
4
4
  #private;
5
5
  src: string | undefined | null;
6
6
  size: number;
7
+ name: string;
7
8
  isUrl: boolean;
8
9
  initials: string;
9
10
  bgColor: string;
10
11
  ngOnInit(): void;
11
12
  handleError(): void;
12
13
  static ɵfac: i0.ɵɵFactoryDeclaration<SdAvatar, never>;
13
- static ɵcmp: i0.ɵɵComponentDeclaration<SdAvatar, "sd-avatar", never, { "src": { "alias": "src"; "required": true; }; "size": { "alias": "size"; "required": false; }; }, {}, never, never, true, never>;
14
+ static ɵcmp: i0.ɵɵComponentDeclaration<SdAvatar, "sd-avatar", never, { "src": { "alias": "src"; "required": true; }; "size": { "alias": "size"; "required": false; }; "name": { "alias": "name"; "required": false; }; }, {}, never, never, true, never>;
14
15
  }
@@ -1,39 +1,38 @@
1
- import { EventEmitter, OnDestroy, OnInit } from '@angular/core';
1
+ import { OnDestroy, OnInit } from '@angular/core';
2
2
  import { SdBaseSecureComponent } from '@sd-angular/core/components/base';
3
3
  import { SdColor } from '@sd-angular/core/utilities/models';
4
4
  import * as i0 from "@angular/core";
5
+ export type SdButtonFontSet = 'material-icons' | 'material-icons-outlined' | 'material-icons-round' | 'material-icons-sharp' | 'material-symbols-outlined';
6
+ export type SdButtonType = 'fill' | 'light' | 'outline' | 'link';
7
+ export type SdButtonSize = 'sm' | 'md' | 'lg';
5
8
  export declare class SdButton extends SdBaseSecureComponent implements OnInit, OnDestroy {
6
9
  #private;
7
- autoId?: string;
8
- set _autoId(val: string | undefined | null);
9
- type: 'fill' | 'light' | 'outline' | 'link';
10
- set _type(type: 'fill' | 'light' | 'outline' | 'link' | undefined | null);
11
- color: SdColor;
12
- set _color(color: SdColor | undefined | null);
13
- title: string | undefined | null;
14
- width: string | undefined | null;
15
- size: 'sm' | 'md' | 'lg' | undefined | null;
16
- tooltip: string | undefined | null;
17
- prefixIcon: string | undefined | null;
18
- suffixIcon: string | undefined | null;
19
- fontSet: 'material-icons' | 'material-icons-outlined' | 'material-icons-round' | 'material-icons-sharp' | 'material-symbols-outlined';
20
- set _fontSet(fontSet: SdButton['fontSet'] | undefined | null);
21
- disabled: boolean;
22
- get disabledAttr(): "true" | null;
23
- set _disabled(value: '' | boolean | undefined | null);
24
- loading: boolean;
25
- click: EventEmitter<Event>;
26
- constructor();
27
- ngOnInit(): void;
28
- onInternalClick(event: Event): void;
29
- ngOnDestroy(): void;
30
- get buttonClasses(): {
10
+ private el;
11
+ autoIdInput: import("@angular/core").InputSignal<string | null | undefined>;
12
+ type: import("@angular/core").InputSignalWithTransform<SdButtonType, SdButtonType | null | undefined>;
13
+ color: import("@angular/core").InputSignalWithTransform<SdColor, SdColor | null | undefined>;
14
+ size: import("@angular/core").InputSignalWithTransform<SdButtonSize, SdButtonSize | null | undefined>;
15
+ fontSet: import("@angular/core").InputSignalWithTransform<SdButtonFontSet, SdButtonFontSet | null | undefined>;
16
+ title: import("@angular/core").InputSignal<string | null | undefined>;
17
+ width: import("@angular/core").InputSignal<string | null | undefined>;
18
+ tooltip: import("@angular/core").InputSignal<string | null | undefined>;
19
+ prefixIcon: import("@angular/core").InputSignal<string | null | undefined>;
20
+ suffixIcon: import("@angular/core").InputSignal<string | null | undefined>;
21
+ disabled: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
22
+ loading: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
23
+ autoId: import("@angular/core").Signal<string | undefined>;
24
+ buttonClasses: import("@angular/core").Signal<{
31
25
  'c-square': boolean | "" | null | undefined;
32
26
  'c-sm': boolean;
33
27
  'c-md': boolean;
34
28
  'c-lg': boolean;
35
29
  'c-disabled': boolean;
36
- };
30
+ }>;
31
+ click: import("@angular/core").OutputEmitterRef<Event>;
32
+ constructor();
33
+ ngOnInit(): void;
34
+ onInternalClick(event: Event): void;
35
+ ngOnDestroy(): void;
37
36
  static ɵfac: i0.ɵɵFactoryDeclaration<SdButton, never>;
38
- static ɵcmp: i0.ɵɵComponentDeclaration<SdButton, "sd-button", never, { "_autoId": { "alias": "autoId"; "required": false; }; "_type": { "alias": "type"; "required": false; }; "_color": { "alias": "color"; "required": false; }; "title": { "alias": "title"; "required": false; }; "width": { "alias": "width"; "required": false; }; "size": { "alias": "size"; "required": false; }; "tooltip": { "alias": "tooltip"; "required": false; }; "prefixIcon": { "alias": "prefixIcon"; "required": false; }; "suffixIcon": { "alias": "suffixIcon"; "required": false; }; "_fontSet": { "alias": "fontSet"; "required": false; }; "_disabled": { "alias": "disabled"; "required": false; }; "loading": { "alias": "loading"; "required": false; }; }, { "click": "click"; }, never, never, true, never>;
37
+ static ɵcmp: i0.ɵɵComponentDeclaration<SdButton, "sd-button", never, { "autoIdInput": { "alias": "autoId"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "color": { "alias": "color"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "fontSet": { "alias": "fontSet"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "width": { "alias": "width"; "required": false; "isSignal": true; }; "tooltip": { "alias": "tooltip"; "required": false; "isSignal": true; }; "prefixIcon": { "alias": "prefixIcon"; "required": false; "isSignal": true; }; "suffixIcon": { "alias": "suffixIcon"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; }, { "click": "click"; }, never, never, true, never>;
39
38
  }
@@ -1,3 +1,4 @@
1
1
  export * from './src/components';
2
2
  export * from './src/document-builder.model';
3
3
  export * from './src/document-builder.component';
4
+ export * from './src/plugins/ck-comment/ck-comment.plugin.model';
@@ -1,6 +1,7 @@
1
1
  import { EventEmitter } from '@angular/core';
2
2
  import { ClassicEditor, ModelRange } from 'ckeditor5';
3
- import { SdDocumentBuilderComment, SdDocumentBuilderHeading, SdDocumentBuilderOption, SdDocumentBuilderVariable, SdEditorConfig } from './document-builder.model';
3
+ import { CkCommentPlugin } from './plugins';
4
+ import { SdDocumentBuilderHeading, SdDocumentBuilderOption, SdDocumentBuilderVariable, SdEditorConfig } from './document-builder.model';
4
5
  import * as i0 from "@angular/core";
5
6
  export declare class SdDocumentBuilder {
6
7
  #private;
@@ -30,47 +31,7 @@ export declare class SdDocumentBuilder {
30
31
  */
31
32
  scroll: (id: string) => void;
32
33
  };
33
- comment: {
34
- /**
35
- * Lấy tất cả comments trong document
36
- * @returns Danh sách tất cả comments
37
- */
38
- all: () => SdDocumentBuilderComment[];
39
- /**
40
- * Thêm comment vào vùng text đang được chọn
41
- * @param comment - Dữ liệu comment
42
- * @param data - Dữ liệu extra data
43
- * @returns SdDocumentBuilderComment hoặc null nếu không có text được chọn
44
- */
45
- add: <T = any>(range: ModelRange, comment: string, args?: {
46
- markerIdExternal?: string | number;
47
- data?: T;
48
- }) => SdDocumentBuilderComment<T> | null;
49
- /**
50
- * Cập nhật nội dung comment
51
- * @param markerId - ID của marker
52
- * @param commentData - Dữ liệu mới
53
- * @returns Comment đã cập nhật hoặc null
54
- */
55
- update: <T = any>(markerId: string, comment: string, data?: T) => SdDocumentBuilderComment<T> | null;
56
- /**
57
- * Lấy chi tiết comment theo markerId
58
- * @param markerId - ID của marker
59
- * @returns Comment hoặc null
60
- */
61
- detail: (markerId: string) => SdDocumentBuilderComment | null;
62
- /**
63
- * Xóa comment theo markerId
64
- * @param markerId - ID của marker cần xóa
65
- * @returns true nếu xóa thành công, false nếu không tìm thấy
66
- */
67
- remove: (markerId: string) => boolean;
68
- /**
69
- * Scroll đến vị trí comment
70
- * @param markerId - ID của marker cần scroll tới
71
- */
72
- scroll: (markerId: string) => void;
73
- };
34
+ getCommentPluginAPI(): CkCommentPlugin | null;
74
35
  variable: {
75
36
  /**
76
37
  * Lấy tất cả variabes trong document
@@ -1,11 +1,11 @@
1
- import { EditorConfig, EventInfo, ModelDocumentSelection, ModelRange, ViewDataTransfer } from 'ckeditor5';
1
+ import { EditorConfig, EventInfo, ModelDocumentSelection, ViewDataTransfer } from 'ckeditor5';
2
+ import { CkCommentConfig } from './plugins/ck-comment/ck-comment.plugin.model';
2
3
  export type SdEditorConfig = EditorConfig & {
3
4
  getOption?: () => SdDocumentBuilderOption;
4
5
  };
5
6
  export interface SdDocumentBuilderOption {
6
7
  onDropVariable?: (variable: SdDocumentBuilderVariable, dropIndex: number) => boolean | Promise<boolean | SdDocumentBuilderVariable>;
7
- onAddComment?: (selectedComment: SdDocumentBuilderSelectedComment) => void;
8
- onSelectComment?: (markerId: string) => void;
8
+ comment?: CkCommentConfig;
9
9
  onSelection?: (selection: ModelDocumentSelection, $event: EventInfo<string, unknown>) => void;
10
10
  onOrientation?: (orientation: 'PORTRAIT' | 'LANDSCAPE') => void;
11
11
  onPaste?: (data: SdPasteEventData) => void | Promise<void>;
@@ -18,17 +18,6 @@ export interface SdDocumentBuilderVariable<T = any> {
18
18
  display: string;
19
19
  data?: T;
20
20
  }
21
- export interface SdDocumentBuilderSelectedComment {
22
- range: ModelRange;
23
- selectedText: string;
24
- }
25
- export interface SdDocumentBuilderComment<T = any> {
26
- markerId: string;
27
- selectedText: string;
28
- comment?: string;
29
- createdAt?: Date;
30
- data?: T;
31
- }
32
21
  export interface SdDocumentBuilderHeading {
33
22
  id: string;
34
23
  text: string;
@@ -0,0 +1,43 @@
1
+ import { Plugin, ContextualBalloon } from 'ckeditor5';
2
+ import { CkComment, CkCommentConfig, CkCommentColors } from './ck-comment.plugin.model';
3
+ export declare class CkCommentPlugin extends Plugin {
4
+ #private;
5
+ static get pluginName(): string;
6
+ static get requires(): (typeof ContextualBalloon)[];
7
+ static readonly PENDING_MARKER_ID = "__pending_comment__";
8
+ static readonly DEFAULT_SEARCH_RANGE = 5;
9
+ static readonly DEFAULT_COLORS: CkCommentColors;
10
+ init(): void;
11
+ /**
12
+ * Thiết lập config với callbacks
13
+ */
14
+ setConfig(config: CkCommentConfig): void;
15
+ /**
16
+ * Thêm comment và tạo marker
17
+ */
18
+ addComment(comment: CkComment): boolean;
19
+ /**
20
+ * Xóa comment theo id
21
+ */
22
+ removeComment(id: string | number): boolean;
23
+ /**
24
+ * Chọn comment theo id - chỉ thêm class highlight, không bôi đen text
25
+ */
26
+ selectComment(id: string | number, scrollIntoView?: boolean): void;
27
+ /**
28
+ * Thiết lập tất cả comments (khôi phục từ dữ liệu)
29
+ */
30
+ setComments(comments: CkComment[]): void;
31
+ /**
32
+ * Lấy tất cả comments
33
+ */
34
+ get comments(): CkComment[];
35
+ /**
36
+ * Thiết lập pending highlight cho selection (khi user đang nhập nội dung comment)
37
+ */
38
+ setPendingSelection(startPath: number[], endPath: number[]): boolean;
39
+ /**
40
+ * Xóa pending highlight và fire onCancelPending callback
41
+ */
42
+ clearPendingSelection(): void;
43
+ }
@@ -0,0 +1,50 @@
1
+ import { ModelRange } from 'ckeditor5';
2
+ /**
3
+ * Comment status based on text changes
4
+ */
5
+ export type CkCommentStatus = 'normal' | 'modified' | 'broken';
6
+ /**
7
+ * Color configuration for comment markers
8
+ */
9
+ export interface CkCommentColors {
10
+ marker?: string;
11
+ markerSelected?: string;
12
+ markerPending?: string;
13
+ markerModified?: string;
14
+ markerBroken?: string;
15
+ }
16
+ /**
17
+ * Comment data structure for CkCommentPlugin
18
+ */
19
+ export interface CkComment<T = any> {
20
+ id: string | number;
21
+ startPath: number[];
22
+ endPath: number[];
23
+ originalText: string;
24
+ currentText: string;
25
+ status: CkCommentStatus;
26
+ data?: T;
27
+ }
28
+ /**
29
+ * Config for CkCommentPlugin
30
+ */
31
+ export interface CkCommentConfig {
32
+ onPendingComment?: (comment: CkComment) => void;
33
+ onAddComment?: (comment: CkComment) => void;
34
+ onSelectComment?: (id: string | number) => void;
35
+ onRemoveComment?: (id: string | number) => void;
36
+ onChange?: (comments: CkComment[]) => void;
37
+ onCancelPending?: () => void;
38
+ searchRange?: number;
39
+ debug?: boolean;
40
+ colors?: CkCommentColors;
41
+ }
42
+ /**
43
+ * Data returned when user selects text for comment
44
+ */
45
+ export interface CkCommentSelection {
46
+ range: ModelRange;
47
+ startPath: number[];
48
+ endPath: number[];
49
+ text: string;
50
+ }
@@ -1,5 +1,4 @@
1
1
  export * from './heading/heading.plugin';
2
- export * from './comment/comment.plugin';
3
2
  export * from './variable/variable.plugin';
4
3
  export * from './table-custom';
5
4
  export * from './image-upload/image-upload.plugin';
@@ -8,3 +7,5 @@ export * from './page-orientation/page-orientation.plugin';
8
7
  export * from './paste-handler';
9
8
  export * from './highlight-range/highlight-range.plugin';
10
9
  export * from './block-space/block-space.plugin';
10
+ export * from './ck-comment/ck-comment.plugin';
11
+ export * from './ck-comment/ck-comment.plugin.model';
@@ -2,6 +2,7 @@ import { PipeTransform } from '@angular/core';
2
2
  import { SdTableAction } from '../../models/table-option-selector.model';
3
3
  import { SdTableItem } from '../../models/table-item.model';
4
4
  import { SdButton } from '@sd-angular/core/components/button';
5
+ import { SdUnwrapSignal } from '@sd-angular/core/utilities/models';
5
6
  import * as i0 from "@angular/core";
6
7
  export declare class ActionFilterPipe implements PipeTransform {
7
8
  #private;
@@ -13,21 +14,21 @@ export type Action<T = any> = ActionNormal<T> | ActionChildren<T>;
13
14
  interface ActionNormal<T = any> {
14
15
  variant: 'normal';
15
16
  icon?: string;
16
- fontSet?: SdButton['fontSet'];
17
- tooltip?: SdButton['tooltip'];
18
- title?: SdButton['title'];
19
- color?: SdButton['color'];
20
- type?: SdButton['type'];
17
+ fontSet?: SdUnwrapSignal<SdButton['fontSet']>;
18
+ tooltip?: SdUnwrapSignal<SdButton['tooltip']>;
19
+ title?: SdUnwrapSignal<SdButton['title']>;
20
+ color?: SdUnwrapSignal<SdButton['color']>;
21
+ type?: SdUnwrapSignal<SdButton['type']>;
21
22
  click: (selectedItems?: T[]) => void;
22
23
  }
23
24
  interface ActionChildren<T = any> {
24
25
  variant: 'children';
25
26
  icon?: string;
26
- fontSet?: SdButton['fontSet'];
27
- tooltip?: SdButton['tooltip'];
28
- title?: SdButton['title'];
29
- color?: SdButton['color'];
30
- type?: SdButton['type'];
27
+ fontSet?: SdUnwrapSignal<SdButton['fontSet']>;
28
+ tooltip?: SdUnwrapSignal<SdButton['tooltip']>;
29
+ title?: SdUnwrapSignal<SdButton['title']>;
30
+ color?: SdUnwrapSignal<SdButton['color']>;
31
+ type?: SdUnwrapSignal<SdButton['type']>;
31
32
  children: ActionNormal<T>[];
32
33
  }
33
34
  export {};
@@ -1,4 +1,5 @@
1
1
  import { SdButton } from '@sd-angular/core/components/button';
2
+ import { SdUnwrapSignal } from '@sd-angular/core/utilities/models';
2
3
  export interface SdTableOptionSelector<T = any> {
3
4
  visible?: boolean;
4
5
  single?: boolean;
@@ -11,22 +12,22 @@ export interface SdTableOptionSelector<T = any> {
11
12
  export type SdTableAction<T = any> = SdTableActionNormal<T> | SdTableActionChildren<T>;
12
13
  export interface SdTableActionNormal<T = any> {
13
14
  icon?: string;
14
- fontSet?: SdButton['fontSet'];
15
- tooltip?: SdButton['tooltip'];
16
- title?: SdButton['title'];
17
- color?: SdButton['color'];
18
- type?: SdButton['type'];
15
+ fontSet?: SdUnwrapSignal<SdButton['fontSet']>;
16
+ tooltip?: SdUnwrapSignal<SdButton['tooltip']>;
17
+ title?: SdUnwrapSignal<SdButton['title']>;
18
+ color?: SdUnwrapSignal<SdButton['color']>;
19
+ type?: SdUnwrapSignal<SdButton['type']>;
19
20
  hidden?: boolean | ((rowData?: T) => boolean);
20
21
  isGrouped?: boolean;
21
22
  click: (selectedItems?: T[]) => void;
22
23
  }
23
24
  interface SdTableActionChildren<T = any> {
24
25
  icon?: string;
25
- fontSet?: SdButton['fontSet'];
26
- tooltip?: SdButton['tooltip'];
27
- title?: SdButton['title'];
28
- color?: SdButton['color'];
29
- type?: SdButton['type'];
26
+ fontSet?: SdUnwrapSignal<SdButton['fontSet']>;
27
+ tooltip?: SdUnwrapSignal<SdButton['tooltip']>;
28
+ title?: SdUnwrapSignal<SdButton['title']>;
29
+ color?: SdUnwrapSignal<SdButton['color']>;
30
+ type?: SdUnwrapSignal<SdButton['type']>;
30
31
  hidden?: boolean | ((rowData?: T) => boolean);
31
32
  isGrouped?: boolean;
32
33
  children: SdTableActionNormal<T>[];
@@ -5,6 +5,7 @@ import { Input, ChangeDetectionStrategy, Component } from '@angular/core';
5
5
  class SdAvatar {
6
6
  src;
7
7
  size = 32;
8
+ name = '';
8
9
  isUrl = false;
9
10
  initials = '';
10
11
  bgColor = '#ccc';
@@ -12,18 +13,16 @@ class SdAvatar {
12
13
  this.#init();
13
14
  }
14
15
  #init = () => {
15
- if (!this.src) {
16
- this.isUrl = false;
17
- this.initials = '?';
18
- this.bgColor = '#bdc3c7';
19
- return;
20
- }
21
16
  // Kiểm tra xem src có phải là URL (http, https, data:image, hoặc path /)
22
17
  const urlPattern = /^(http|https|data:image|\/)/;
23
- this.isUrl = urlPattern.test(this.src);
24
- if (!this.isUrl) {
25
- this.initials = this.#getInitials(this.src);
26
- this.bgColor = this.#generateColor(this.src);
18
+ this.isUrl = urlPattern.test(this.src || '');
19
+ if (this.name || !this.isUrl) {
20
+ this.bgColor = this.#generateColor(this.name || this.src || '');
21
+ this.initials = this.#getInitials(this.name || this.src || '');
22
+ }
23
+ else {
24
+ this.bgColor = '#bdc3c7';
25
+ this.initials = '?';
27
26
  }
28
27
  };
29
28
  handleError() {
@@ -31,13 +30,14 @@ class SdAvatar {
31
30
  this.#init();
32
31
  }
33
32
  #getInitials = (name) => {
34
- return name
33
+ return (name
35
34
  .trim()
36
35
  .split(' ')
36
+ .filter(Boolean)
37
37
  .map(n => n[0])
38
38
  .join('')
39
39
  .toUpperCase()
40
- .substring(0, 2);
40
+ .substring(0, 2) || '');
41
41
  };
42
42
  #generateColor = (name) => {
43
43
  const colors = [
@@ -68,7 +68,7 @@ class SdAvatar {
68
68
  return colors[Math.abs(hash) % colors.length];
69
69
  };
70
70
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdAvatar, deps: [], target: i0.ɵɵFactoryTarget.Component });
71
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: SdAvatar, isStandalone: true, selector: "sd-avatar", inputs: { src: "src", size: "size" }, ngImport: i0, template: "<div class=\"sd-avatar\" [style.width.px]=\"size\" [style.height.px]=\"size\" [style.line-height.px]=\"size\" [style.backgroundColor]=\"bgColor\">\r\n @if (isUrl) {\r\n <img [src]=\"src\" (error)=\"handleError()\" alt=\"avatar\" />\r\n } @else {\r\n <span class=\"sd-avatar-text\" [style.fontSize.px]=\"size / 2.5\">\r\n {{ initials }}\r\n </span>\r\n }\r\n</div>\r\n", styles: [".sd-avatar{display:inline-flex;align-items:center;justify-content:center;border-radius:50%;overflow:hidden;color:#fff;font-weight:500;-webkit-user-select:none;user-select:none;background-size:cover}.sd-avatar img{width:100%;height:100%;object-fit:cover}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
71
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: SdAvatar, isStandalone: true, selector: "sd-avatar", inputs: { src: "src", size: "size", name: "name" }, ngImport: i0, template: "<div class=\"sd-avatar\" [style.width.px]=\"size\" [style.height.px]=\"size\" [style.line-height.px]=\"size\" [style.backgroundColor]=\"bgColor\">\r\n @if (isUrl) {\r\n <img [src]=\"src\" (error)=\"handleError()\" alt=\"avatar\" />\r\n } @else {\r\n <span class=\"sd-avatar-text\" [style.fontSize.px]=\"size / 2.5\">\r\n {{ initials }}\r\n </span>\r\n }\r\n</div>\r\n", styles: [".sd-avatar{display:inline-flex;align-items:center;justify-content:center;border-radius:50%;overflow:hidden;color:#fff;font-weight:500;-webkit-user-select:none;user-select:none;background-size:cover}.sd-avatar img{width:100%;height:100%;object-fit:cover}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
72
72
  }
73
73
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdAvatar, decorators: [{
74
74
  type: Component,
@@ -78,6 +78,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
78
78
  args: [{ required: true }]
79
79
  }], size: [{
80
80
  type: Input
81
+ }], name: [{
82
+ type: Input
81
83
  }] } });
82
84
 
83
85
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"sd-angular-core-components-avatar.mjs","sources":["../../../projects/sd-angular/components/avatar/src/avatar.component.ts","../../../projects/sd-angular/components/avatar/src/avatar.component.html","../../../projects/sd-angular/components/avatar/sd-angular-core-components-avatar.ts"],"sourcesContent":["import { CommonModule } from '@angular/common';\r\nimport { ChangeDetectionStrategy, Component, Input, OnChanges, OnInit } from '@angular/core';\r\n\r\n@Component({\r\n selector: 'sd-avatar',\r\n standalone: true,\r\n imports: [CommonModule],\r\n templateUrl: './avatar.component.html',\r\n styleUrls: ['./avatar.component.scss'],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class SdAvatar implements OnInit {\r\n @Input({ required: true }) src: string | undefined | null;\r\n @Input() size: number = 32;\r\n\r\n isUrl: boolean = false;\r\n initials: string = '';\r\n bgColor: string = '#ccc';\r\n\r\n ngOnInit(): void {\r\n this.#init();\r\n }\r\n\r\n #init = () => {\r\n if (!this.src) {\r\n this.isUrl = false;\r\n this.initials = '?';\r\n this.bgColor = '#bdc3c7';\r\n return;\r\n }\r\n // Kiểm tra xem src có phải là URL (http, https, data:image, hoặc path /)\r\n const urlPattern = /^(http|https|data:image|\\/)/;\r\n this.isUrl = urlPattern.test(this.src);\r\n if (!this.isUrl) {\r\n this.initials = this.#getInitials(this.src);\r\n this.bgColor = this.#generateColor(this.src);\r\n }\r\n };\r\n\r\n handleError() {\r\n this.src = undefined; // Nếu ảnh lỗi, chuyển sang hiển thị initials\r\n this.#init();\r\n }\r\n\r\n #getInitials = (name: string): string => {\r\n return name\r\n .trim()\r\n .split(' ')\r\n .map(n => n[0])\r\n .join('')\r\n .toUpperCase()\r\n .substring(0, 2);\r\n };\r\n\r\n #generateColor = (name: string): string => {\r\n const colors = [\r\n '#1abc9c',\r\n '#2ecc71',\r\n '#3498db',\r\n '#9b59b6',\r\n '#34495e',\r\n '#16a085',\r\n '#27ae60',\r\n '#2980b9',\r\n '#8e44ad',\r\n '#2c3e50',\r\n '#f1c40f',\r\n '#e67e22',\r\n '#e74c3c',\r\n '#95a5a6',\r\n '#f39c12',\r\n '#d35400',\r\n '#c0392b',\r\n '#bdc3c7',\r\n '#7f8c8d',\r\n ];\r\n let hash = 0;\r\n for (let i = 0; i < name.length; i++) {\r\n hash = name.charCodeAt(i) + ((hash << 5) - hash);\r\n }\r\n return colors[Math.abs(hash) % colors.length];\r\n };\r\n}\r\n","<div class=\"sd-avatar\" [style.width.px]=\"size\" [style.height.px]=\"size\" [style.line-height.px]=\"size\" [style.backgroundColor]=\"bgColor\">\r\n @if (isUrl) {\r\n <img [src]=\"src\" (error)=\"handleError()\" alt=\"avatar\" />\r\n } @else {\r\n <span class=\"sd-avatar-text\" [style.fontSize.px]=\"size / 2.5\">\r\n {{ initials }}\r\n </span>\r\n }\r\n</div>\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sd-angular-core-components-avatar.mjs","sources":["../../../projects/sd-angular/components/avatar/src/avatar.component.ts","../../../projects/sd-angular/components/avatar/src/avatar.component.html","../../../projects/sd-angular/components/avatar/sd-angular-core-components-avatar.ts"],"sourcesContent":["import { CommonModule } from '@angular/common';\r\nimport { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';\r\n\r\n@Component({\r\n selector: 'sd-avatar',\r\n standalone: true,\r\n imports: [CommonModule],\r\n templateUrl: './avatar.component.html',\r\n styleUrls: ['./avatar.component.scss'],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class SdAvatar implements OnInit {\r\n @Input({ required: true }) src: string | undefined | null;\r\n @Input() size: number = 32;\r\n @Input() name: string = '';\r\n\r\n isUrl: boolean = false;\r\n initials: string = '';\r\n bgColor: string = '#ccc';\r\n\r\n ngOnInit(): void {\r\n this.#init();\r\n }\r\n\r\n #init = () => {\r\n // Kiểm tra xem src có phải là URL (http, https, data:image, hoặc path /)\r\n const urlPattern = /^(http|https|data:image|\\/)/;\r\n this.isUrl = urlPattern.test(this.src || '');\r\n if (this.name || !this.isUrl) {\r\n this.bgColor = this.#generateColor(this.name || this.src || '');\r\n this.initials = this.#getInitials(this.name || this.src || '');\r\n } else {\r\n this.bgColor = '#bdc3c7';\r\n this.initials = '?';\r\n }\r\n };\r\n\r\n handleError() {\r\n this.src = undefined; // Nếu ảnh lỗi, chuyển sang hiển thị initials\r\n this.#init();\r\n }\r\n\r\n #getInitials = (name: string): string => {\r\n return (\r\n name\r\n .trim()\r\n .split(' ')\r\n .filter(Boolean)\r\n .map(n => n[0])\r\n .join('')\r\n .toUpperCase()\r\n .substring(0, 2) || ''\r\n );\r\n };\r\n\r\n #generateColor = (name: string): string => {\r\n const colors = [\r\n '#1abc9c',\r\n '#2ecc71',\r\n '#3498db',\r\n '#9b59b6',\r\n '#34495e',\r\n '#16a085',\r\n '#27ae60',\r\n '#2980b9',\r\n '#8e44ad',\r\n '#2c3e50',\r\n '#f1c40f',\r\n '#e67e22',\r\n '#e74c3c',\r\n '#95a5a6',\r\n '#f39c12',\r\n '#d35400',\r\n '#c0392b',\r\n '#bdc3c7',\r\n '#7f8c8d',\r\n ];\r\n let hash = 0;\r\n for (let i = 0; i < name.length; i++) {\r\n hash = name.charCodeAt(i) + ((hash << 5) - hash);\r\n }\r\n return colors[Math.abs(hash) % colors.length];\r\n };\r\n}\r\n","<div class=\"sd-avatar\" [style.width.px]=\"size\" [style.height.px]=\"size\" [style.line-height.px]=\"size\" [style.backgroundColor]=\"bgColor\">\r\n @if (isUrl) {\r\n <img [src]=\"src\" (error)=\"handleError()\" alt=\"avatar\" />\r\n } @else {\r\n <span class=\"sd-avatar-text\" [style.fontSize.px]=\"size / 2.5\">\r\n {{ initials }}\r\n </span>\r\n }\r\n</div>\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}