@sd-angular/core 19.0.31 → 19.0.33

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 (71) hide show
  1. package/assets/scss/core/_read-state-panel.scss +57 -0
  2. package/components/data-state/index.d.ts +1 -0
  3. package/components/data-state/src/data-state.component.d.ts +41 -0
  4. package/components/import-excel/src/import-excel.component.d.ts +23 -1
  5. package/components/index.d.ts +1 -0
  6. package/components/quick-action/src/quick-action.component.d.ts +3 -1
  7. package/components/table/src/components/selector-action/selector-action.component.d.ts +12 -0
  8. package/components/table/src/models/table-option-selector.model.d.ts +2 -0
  9. package/components/table/src/table.component.d.ts +10 -1
  10. package/components/upload-file/src/upload-file.component.d.ts +5 -1
  11. package/fesm2022/sd-angular-core-components-badge.mjs +2 -2
  12. package/fesm2022/sd-angular-core-components-badge.mjs.map +1 -1
  13. package/fesm2022/sd-angular-core-components-button.mjs +2 -2
  14. package/fesm2022/sd-angular-core-components-button.mjs.map +1 -1
  15. package/fesm2022/sd-angular-core-components-data-state.mjs +72 -0
  16. package/fesm2022/sd-angular-core-components-data-state.mjs.map +1 -0
  17. package/fesm2022/sd-angular-core-components-form-generic.mjs +14 -14
  18. package/fesm2022/sd-angular-core-components-form-generic.mjs.map +1 -1
  19. package/fesm2022/sd-angular-core-components-import-excel.mjs +67 -18
  20. package/fesm2022/sd-angular-core-components-import-excel.mjs.map +1 -1
  21. package/fesm2022/sd-angular-core-components-inform.mjs +6 -6
  22. package/fesm2022/sd-angular-core-components-inform.mjs.map +1 -1
  23. package/fesm2022/sd-angular-core-components-query-bar.mjs +3 -3
  24. package/fesm2022/sd-angular-core-components-query-bar.mjs.map +1 -1
  25. package/fesm2022/sd-angular-core-components-query-builder.mjs +1 -1
  26. package/fesm2022/sd-angular-core-components-query-builder.mjs.map +1 -1
  27. package/fesm2022/sd-angular-core-components-quick-action.mjs +4 -2
  28. package/fesm2022/sd-angular-core-components-quick-action.mjs.map +1 -1
  29. package/fesm2022/sd-angular-core-components-stepper.mjs +2 -2
  30. package/fesm2022/sd-angular-core-components-stepper.mjs.map +1 -1
  31. package/fesm2022/sd-angular-core-components-table.mjs +125 -31
  32. package/fesm2022/sd-angular-core-components-table.mjs.map +1 -1
  33. package/fesm2022/sd-angular-core-components-tree.mjs +1 -1
  34. package/fesm2022/sd-angular-core-components-tree.mjs.map +1 -1
  35. package/fesm2022/sd-angular-core-components-upload-file.mjs +20 -6
  36. package/fesm2022/sd-angular-core-components-upload-file.mjs.map +1 -1
  37. package/fesm2022/sd-angular-core-components.mjs +1 -0
  38. package/fesm2022/sd-angular-core-components.mjs.map +1 -1
  39. package/fesm2022/sd-angular-core-forms-autocomplete.mjs +247 -59
  40. package/fesm2022/sd-angular-core-forms-autocomplete.mjs.map +1 -1
  41. package/fesm2022/sd-angular-core-forms-date-range.mjs +2 -2
  42. package/fesm2022/sd-angular-core-forms-date-range.mjs.map +1 -1
  43. package/fesm2022/sd-angular-core-forms-date.mjs +2 -2
  44. package/fesm2022/sd-angular-core-forms-date.mjs.map +1 -1
  45. package/fesm2022/sd-angular-core-forms-datetime.mjs +2 -2
  46. package/fesm2022/sd-angular-core-forms-datetime.mjs.map +1 -1
  47. package/fesm2022/sd-angular-core-forms-select.mjs +290 -67
  48. package/fesm2022/sd-angular-core-forms-select.mjs.map +1 -1
  49. package/fesm2022/sd-angular-core-i18n.mjs +160 -0
  50. package/fesm2022/sd-angular-core-i18n.mjs.map +1 -1
  51. package/fesm2022/sd-angular-core-modules-layout.mjs +756 -15
  52. package/fesm2022/sd-angular-core-modules-layout.mjs.map +1 -1
  53. package/fesm2022/sd-angular-core-utilities-read-state.mjs +87 -0
  54. package/fesm2022/sd-angular-core-utilities-read-state.mjs.map +1 -0
  55. package/fesm2022/sd-angular-core-utilities.mjs +1 -0
  56. package/fesm2022/sd-angular-core-utilities.mjs.map +1 -1
  57. package/forms/autocomplete/src/autocomplete.component.d.ts +13 -2
  58. package/forms/select/src/select.component.d.ts +11 -1
  59. package/i18n/src/en.d.ts +32 -0
  60. package/modules/layout/components/layout-main/layout-main.component.d.ts +1 -1
  61. package/modules/layout/components/sidebar-mobile-v1/components/sidebar/sidebar.component.d.ts +2 -2
  62. package/modules/layout/components/sidebar-mobile-v1/main.component.d.ts +2 -2
  63. package/modules/layout/components/sidebar-v2/components/sidebar/sidebar.component.d.ts +56 -0
  64. package/modules/layout/components/sidebar-v2/components/user/user.component.d.ts +22 -0
  65. package/modules/layout/components/sidebar-v2/main.component.d.ts +25 -0
  66. package/modules/layout/configurations/layout.configuration.d.ts +5 -1
  67. package/modules/layout/services/layout.service.d.ts +2 -2
  68. package/package.json +9 -1
  69. package/utilities/index.d.ts +1 -0
  70. package/utilities/read-state/index.d.ts +1 -0
  71. package/utilities/read-state/src/read-state.d.ts +37 -0
@@ -0,0 +1,87 @@
1
+ import { signal, TemplateRef } from '@angular/core';
2
+
3
+ /** VALUE errors take precedence; otherwise SEARCH errors, then the active SEARCH channel. */
4
+ function combineReadStates(value, search) {
5
+ const current = value.status === 'error' ? value : search.status !== 'idle' ? search : value;
6
+ return Object.freeze({ ...current, channels: Object.freeze({ VALUE: value, SEARCH: search }) });
7
+ }
8
+ /** @internal Request guard shared by controls. Context is checked even before Angular effects run. */
9
+ class SdReadChannel {
10
+ operation;
11
+ changed;
12
+ #state;
13
+ state;
14
+ #revision = 0;
15
+ #valid = () => true;
16
+ constructor(operation, changed) {
17
+ this.operation = operation;
18
+ this.changed = changed;
19
+ this.#state = signal(Object.freeze({ status: 'idle', operation }));
20
+ this.state = this.#state.asReadonly();
21
+ }
22
+ begin(valid = () => true) {
23
+ this.#valid = valid;
24
+ const revision = ++this.#revision;
25
+ this.#set({ status: 'loading', operation: this.operation });
26
+ return revision;
27
+ }
28
+ isCurrent(revision) {
29
+ return revision === this.#revision && this.#valid();
30
+ }
31
+ succeed(revision, count) {
32
+ if (!this.isCurrent(revision))
33
+ return false;
34
+ this.#set({ status: count ? 'ready' : 'empty', operation: this.operation });
35
+ return true;
36
+ }
37
+ fail(revision, error) {
38
+ if (!this.isCurrent(revision))
39
+ return false;
40
+ this.#set({ status: 'error', operation: this.operation, error });
41
+ return true;
42
+ }
43
+ invalidate() {
44
+ ++this.#revision;
45
+ this.#valid = () => true;
46
+ if (this.state().status !== 'idle')
47
+ this.#set({ status: 'idle', operation: this.operation });
48
+ }
49
+ #set(state) {
50
+ const snapshot = Object.freeze(state);
51
+ this.#state.set(snapshot);
52
+ this.changed?.(snapshot);
53
+ }
54
+ }
55
+ /**
56
+ * @internal Copy request-owned data, including Dates. Unlike ObjectUtilities.clone,
57
+ * Dates must be copied too; structuredClone cannot retain table column callbacks.
58
+ */
59
+ function cloneReadRequest(value, seen = new WeakMap()) {
60
+ if (value === null || typeof value !== 'object')
61
+ return value;
62
+ // why: TemplateRef là handle thuộc Angular view, không phải dữ liệu request có thể sao chép.
63
+ if (value instanceof TemplateRef)
64
+ return value;
65
+ if (seen.has(value))
66
+ return seen.get(value);
67
+ if (value instanceof Date)
68
+ return new Date(value.getTime());
69
+ const copy = (Array.isArray(value) ? [] : Object.create(Object.getPrototypeOf(value)));
70
+ seen.set(value, copy);
71
+ for (const key of Object.keys(value)) {
72
+ Object.defineProperty(copy, key, {
73
+ value: cloneReadRequest(value[key], seen),
74
+ enumerable: true,
75
+ writable: true,
76
+ configurable: true,
77
+ });
78
+ }
79
+ return copy;
80
+ }
81
+
82
+ /**
83
+ * Generated bundle index. Do not edit.
84
+ */
85
+
86
+ export { SdReadChannel, cloneReadRequest, combineReadStates };
87
+ //# sourceMappingURL=sd-angular-core-utilities-read-state.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sd-angular-core-utilities-read-state.mjs","sources":["../../../projects/sd-angular/utilities/read-state/src/read-state.ts","../../../projects/sd-angular/utilities/read-state/sd-angular-core-utilities-read-state.ts"],"sourcesContent":["import { TemplateRef, signal } from '@angular/core';\n\nexport type SdReadOperation = 'TABLE' | 'VALUE' | 'SEARCH';\nexport type SdReadStatus = 'idle' | 'loading' | 'ready' | 'empty' | 'error';\n\n/** An accepted read result. Only an error snapshot contains the original exception. */\nexport type SdReadState = Readonly<\n { status: Exclude<SdReadStatus, 'error'>; operation: SdReadOperation } | { status: 'error'; operation: SdReadOperation; error: unknown }\n>;\n\nexport type SdSearchReadState = SdReadState & {\n readonly channels: Readonly<{ VALUE: SdReadState; SEARCH: SdReadState }>;\n};\n\n/** VALUE errors take precedence; otherwise SEARCH errors, then the active SEARCH channel. */\nexport function combineReadStates(value: SdReadState, search: SdReadState): SdSearchReadState {\n const current = value.status === 'error' ? value : search.status !== 'idle' ? search : value;\n return Object.freeze({ ...current, channels: Object.freeze({ VALUE: value, SEARCH: search }) });\n}\n\n/** @internal Request guard shared by controls. Context is checked even before Angular effects run. */\nexport class SdReadChannel {\n readonly #state;\n readonly state;\n #revision = 0;\n #valid = () => true;\n\n constructor(\n readonly operation: SdReadOperation,\n private readonly changed?: (state: SdReadState) => void\n ) {\n this.#state = signal<SdReadState>(Object.freeze({ status: 'idle', operation }));\n this.state = this.#state.asReadonly();\n }\n\n begin(valid: () => boolean = () => true): number {\n this.#valid = valid;\n const revision = ++this.#revision;\n this.#set({ status: 'loading', operation: this.operation });\n return revision;\n }\n\n isCurrent(revision: number): boolean {\n return revision === this.#revision && this.#valid();\n }\n\n succeed(revision: number, count: number): boolean {\n if (!this.isCurrent(revision)) return false;\n this.#set({ status: count ? 'ready' : 'empty', operation: this.operation });\n return true;\n }\n\n fail(revision: number, error: unknown): boolean {\n if (!this.isCurrent(revision)) return false;\n this.#set({ status: 'error', operation: this.operation, error });\n return true;\n }\n\n invalidate(): void {\n ++this.#revision;\n this.#valid = () => true;\n if (this.state().status !== 'idle') this.#set({ status: 'idle', operation: this.operation });\n }\n\n #set(state: SdReadState): void {\n const snapshot = Object.freeze(state);\n this.#state.set(snapshot);\n this.changed?.(snapshot);\n }\n}\n\n/**\n * @internal Copy request-owned data, including Dates. Unlike ObjectUtilities.clone,\n * Dates must be copied too; structuredClone cannot retain table column callbacks.\n */\nexport function cloneReadRequest<T>(value: T, seen = new WeakMap<object, unknown>()): T {\n if (value === null || typeof value !== 'object') return value;\n // why: TemplateRef là handle thuộc Angular view, không phải dữ liệu request có thể sao chép.\n if (value instanceof TemplateRef) return value;\n if (seen.has(value)) return seen.get(value) as T;\n if (value instanceof Date) return new Date(value.getTime()) as T;\n const copy = (Array.isArray(value) ? [] : Object.create(Object.getPrototypeOf(value))) as T;\n seen.set(value, copy);\n for (const key of Object.keys(value)) {\n Object.defineProperty(copy, key, {\n value: cloneReadRequest((value as Record<string, unknown>)[key], seen),\n enumerable: true,\n writable: true,\n configurable: true,\n });\n }\n return copy;\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  export * from '@sd-angular/core/utilities/extensions';
2
2
  export * from '@sd-angular/core/utilities/models';
3
3
  export * from '@sd-angular/core/utilities/data-state';
4
+ export * from '@sd-angular/core/utilities/read-state';
4
5
 
5
6
  /**
6
7
  * Generated bundle index. Do not edit.
@@ -1 +1 @@
1
- {"version":3,"file":"sd-angular-core-utilities.mjs","sources":["../../../projects/sd-angular/utilities/sd-angular-core-utilities.ts"],"sourcesContent":["/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAAA;;AAEG"}
1
+ {"version":3,"file":"sd-angular-core-utilities.mjs","sources":["../../../projects/sd-angular/utilities/sd-angular-core-utilities.ts"],"sourcesContent":["/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AAAA;;AAEG"}
@@ -3,6 +3,8 @@ import { FormControl, FormGroup, FormGroupDirective, NgForm } from '@angular/for
3
3
  import { MatAutocompleteTrigger } from '@angular/material/autocomplete';
4
4
  import { ErrorStateMatcher } from '@angular/material/core';
5
5
  import { MatFormFieldAppearance } from '@angular/material/form-field';
6
+ import { SdDataStateTemplateDirective } from '@sd-angular/core/components/data-state';
7
+ import { SdSearchReadState } from '@sd-angular/core/utilities/read-state';
6
8
  import { SdItemDefDefDirective, SdViewDefDirective } from '@sd-angular/core/forms/directives';
7
9
  import { SdCustomValidator, SdFormControl, SdSearch, SdSelectionData, SdViewed, SdViewedInput } from '@sd-angular/core/forms/models';
8
10
  import { Size } from '@sdcorejs/utils/models';
@@ -46,6 +48,11 @@ export declare class SdAutocomplete<T = any> implements OnInit, OnDestroy, After
46
48
  hyperlink: import("@angular/core").InputSignal<string | null | undefined>;
47
49
  items: import("@angular/core").InputSignal<SdSearch<T> | T[] | null | undefined>;
48
50
  hideInlineError: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
51
+ readonly hideReadError: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
52
+ readonly dataStateTemplate: import("@angular/core").Signal<SdDataStateTemplateDirective | undefined>;
53
+ readonly sdReadStateChange: import("@angular/core").OutputEmitterRef<SdSearchReadState>;
54
+ readonly readState: import("@angular/core").Signal<SdSearchReadState>;
55
+ readonly hasSearched: import("@angular/core").WritableSignal<boolean>;
49
56
  addable: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
50
57
  required: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
51
58
  disabled: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
@@ -91,7 +98,9 @@ export declare class SdAutocomplete<T = any> implements OnInit, OnDestroy, After
91
98
  ngOnDestroy(): void;
92
99
  onSelect: (item: T) => void;
93
100
  onFocus: () => void;
94
- onBlur: () => void;
101
+ onBlur: (event?: FocusEvent) => void;
102
+ protected focusReadRetry: (event: KeyboardEvent) => void;
103
+ protected onReadRegionKeydown: (event: KeyboardEvent) => void;
95
104
  onClick: () => void;
96
105
  blur: () => void;
97
106
  focus: () => void;
@@ -100,7 +109,9 @@ export declare class SdAutocomplete<T = any> implements OnInit, OnDestroy, After
100
109
  clear: ($event?: any) => void;
101
110
  onAdd: ($event: Event) => void;
102
111
  reValidate: () => void;
112
+ /** Retry bypasses text debounce/distinctness while using the original loader and request snapshot. */
113
+ retryRead: () => void;
103
114
  static ɵfac: i0.ɵɵFactoryDeclaration<SdAutocomplete<any>, never>;
104
- static ɵcmp: i0.ɵɵComponentDeclaration<SdAutocomplete<any>, "sd-autocomplete", never, { "autoIdInput": { "alias": "autoId"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "form": { "alias": "form"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "valueField": { "alias": "valueField"; "required": false; "isSignal": true; }; "displayField": { "alias": "displayField"; "required": false; "isSignal": true; }; "disabledField": { "alias": "disabledField"; "required": false; "isSignal": true; }; "limit": { "alias": "limit"; "required": false; "isSignal": true; }; "cacheChecksum": { "alias": "cacheChecksum"; "required": false; "isSignal": true; }; "hyperlink": { "alias": "hyperlink"; "required": false; "isSignal": true; }; "items": { "alias": "items"; "required": false; "isSignal": true; }; "hideInlineError": { "alias": "hideInlineError"; "required": false; "isSignal": true; }; "addable": { "alias": "addable"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "viewed": { "alias": "viewed"; "required": false; "isSignal": true; }; "clearable": { "alias": "clearable"; "required": false; "isSignal": true; }; "validator": { "alias": "validator"; "required": false; "isSignal": true; }; "inlineError": { "alias": "inlineError"; "required": false; "isSignal": true; }; "appearanceInput": { "alias": "appearance"; "required": false; "isSignal": true; }; "valueModel": { "alias": "model"; "required": false; "isSignal": true; }; }, { "valueModel": "modelChange"; "sdChange": "sdChange"; "sdSelection": "sdSelection"; "sdAdd": "sdAdd"; }, ["sdLabelTemplate", "sdValueTemplate", "itemDef", "sdViewDef"], ["[sdLabel]"], true, never>;
115
+ static ɵcmp: i0.ɵɵComponentDeclaration<SdAutocomplete<any>, "sd-autocomplete", never, { "autoIdInput": { "alias": "autoId"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "form": { "alias": "form"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "valueField": { "alias": "valueField"; "required": false; "isSignal": true; }; "displayField": { "alias": "displayField"; "required": false; "isSignal": true; }; "disabledField": { "alias": "disabledField"; "required": false; "isSignal": true; }; "limit": { "alias": "limit"; "required": false; "isSignal": true; }; "cacheChecksum": { "alias": "cacheChecksum"; "required": false; "isSignal": true; }; "hyperlink": { "alias": "hyperlink"; "required": false; "isSignal": true; }; "items": { "alias": "items"; "required": false; "isSignal": true; }; "hideInlineError": { "alias": "hideInlineError"; "required": false; "isSignal": true; }; "hideReadError": { "alias": "hideReadError"; "required": false; "isSignal": true; }; "addable": { "alias": "addable"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "viewed": { "alias": "viewed"; "required": false; "isSignal": true; }; "clearable": { "alias": "clearable"; "required": false; "isSignal": true; }; "validator": { "alias": "validator"; "required": false; "isSignal": true; }; "inlineError": { "alias": "inlineError"; "required": false; "isSignal": true; }; "appearanceInput": { "alias": "appearance"; "required": false; "isSignal": true; }; "valueModel": { "alias": "model"; "required": false; "isSignal": true; }; }, { "sdReadStateChange": "sdReadStateChange"; "valueModel": "modelChange"; "sdChange": "sdChange"; "sdSelection": "sdSelection"; "sdAdd": "sdAdd"; }, ["sdLabelTemplate", "sdValueTemplate", "itemDef", "sdViewDef", "dataStateTemplate"], ["[sdLabel]"], true, never>;
105
116
  }
106
117
  export {};
@@ -4,6 +4,8 @@ import { FormControl, FormGroup } from '@angular/forms';
4
4
  import { FloatLabelType, MatFormFieldAppearance } from '@angular/material/form-field';
5
5
  import { MatInput } from '@angular/material/input';
6
6
  import { MatSelect, MatSelectChange } from '@angular/material/select';
7
+ import { SdDataStateTemplateDirective } from '@sd-angular/core/components/data-state';
8
+ import { SdSearchReadState } from '@sd-angular/core/utilities/read-state';
7
9
  import { SdItemDefDefDirective, SdViewDefDirective } from '@sd-angular/core/forms/directives';
8
10
  import { SdCustomValidator, SdFormControl, SdSearch, SdSelectionData, SdViewed, SdViewedInput } from '@sd-angular/core/forms/models';
9
11
  import { Size } from '@sdcorejs/utils/models';
@@ -57,6 +59,10 @@ export declare class SdSelect<T extends object | string | number = Record<string
57
59
  hyperlink: import("@angular/core").InputSignal<string | null | undefined>;
58
60
  minWidthPanel: import("@angular/core").InputSignalWithTransform<string | number, string | number | null | undefined>;
59
61
  hideInlineError: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
62
+ readonly hideReadError: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
63
+ readonly dataStateTemplate: Signal<SdDataStateTemplateDirective | undefined>;
64
+ readonly sdReadStateChange: import("@angular/core").OutputEmitterRef<SdSearchReadState>;
65
+ readonly readState: Signal<SdSearchReadState>;
60
66
  required: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
61
67
  disabled: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
62
68
  /** Display mode: `false` edit · `true` static view · `'inline'` view + click-to-edit (bare editor). */
@@ -131,6 +137,10 @@ export declare class SdSelect<T extends object | string | number = Record<string
131
137
  updatePanelWidth: () => void;
132
138
  constructor();
133
139
  ngOnInit(): void;
140
+ protected focusReadRetry: (event: KeyboardEvent) => void;
141
+ protected onReadRegionKeydown: (event: KeyboardEvent) => void;
142
+ /** Retry the error selected by readState; VALUE and SEARCH retain separate snapshots. */
143
+ retryRead: () => Promise<void>;
134
144
  onSelectionChange: (change: MatSelectChange) => void;
135
145
  /**
136
146
  * Toggle "Chọn tất cả" theo scope hiện tại (items enabled khớp search):
@@ -148,5 +158,5 @@ export declare class SdSelect<T extends object | string | number = Record<string
148
158
  open: () => void;
149
159
  onOpenedChange: (isOpened: boolean) => void;
150
160
  static ɵfac: i0.ɵɵFactoryDeclaration<SdSelect<any>, never>;
151
- static ɵcmp: i0.ɵɵComponentDeclaration<SdSelect<any>, "sd-select", never, { "autoIdInput": { "alias": "autoId"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "form": { "alias": "form"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "valueField": { "alias": "valueField"; "required": false; "isSignal": true; }; "displayField": { "alias": "displayField"; "required": false; "isSignal": true; }; "disabledField": { "alias": "disabledField"; "required": false; "isSignal": true; }; "cacheChecksum": { "alias": "cacheChecksum"; "required": false; "isSignal": true; }; "limit": { "alias": "limit"; "required": false; "isSignal": true; }; "hyperlink": { "alias": "hyperlink"; "required": false; "isSignal": true; }; "minWidthPanel": { "alias": "minWidthPanel"; "required": false; "isSignal": true; }; "hideInlineError": { "alias": "hideInlineError"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "viewed": { "alias": "viewed"; "required": false; "isSignal": true; }; "multiple": { "alias": "multiple"; "required": false; "isSignal": true; }; "showSelectAll": { "alias": "showSelectAll"; "required": false; "isSignal": true; }; "clearable": { "alias": "clearable"; "required": false; "isSignal": true; }; "validator": { "alias": "validator"; "required": false; "isSignal": true; }; "inlineError": { "alias": "inlineError"; "required": false; "isSignal": true; }; "appearanceInput": { "alias": "appearance"; "required": false; "isSignal": true; }; "floatLabel": { "alias": "floatLabel"; "required": false; "isSignal": true; }; "items": { "alias": "items"; "required": false; "isSignal": true; }; "valueModel": { "alias": "model"; "required": false; "isSignal": true; }; }, { "valueModel": "modelChange"; "sdChange": "sdChange"; "sdSelection": "sdSelection"; }, ["sdLabelTemplate", "sdValueTemplate", "sdSelectedTemplate", "itemDef", "sdViewDef", "footerActions"], ["[sdLabel]"], true, never>;
161
+ static ɵcmp: i0.ɵɵComponentDeclaration<SdSelect<any>, "sd-select", never, { "autoIdInput": { "alias": "autoId"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "form": { "alias": "form"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "helperText": { "alias": "helperText"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "valueField": { "alias": "valueField"; "required": false; "isSignal": true; }; "displayField": { "alias": "displayField"; "required": false; "isSignal": true; }; "disabledField": { "alias": "disabledField"; "required": false; "isSignal": true; }; "cacheChecksum": { "alias": "cacheChecksum"; "required": false; "isSignal": true; }; "limit": { "alias": "limit"; "required": false; "isSignal": true; }; "hyperlink": { "alias": "hyperlink"; "required": false; "isSignal": true; }; "minWidthPanel": { "alias": "minWidthPanel"; "required": false; "isSignal": true; }; "hideInlineError": { "alias": "hideInlineError"; "required": false; "isSignal": true; }; "hideReadError": { "alias": "hideReadError"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "viewed": { "alias": "viewed"; "required": false; "isSignal": true; }; "multiple": { "alias": "multiple"; "required": false; "isSignal": true; }; "showSelectAll": { "alias": "showSelectAll"; "required": false; "isSignal": true; }; "clearable": { "alias": "clearable"; "required": false; "isSignal": true; }; "validator": { "alias": "validator"; "required": false; "isSignal": true; }; "inlineError": { "alias": "inlineError"; "required": false; "isSignal": true; }; "appearanceInput": { "alias": "appearance"; "required": false; "isSignal": true; }; "floatLabel": { "alias": "floatLabel"; "required": false; "isSignal": true; }; "items": { "alias": "items"; "required": false; "isSignal": true; }; "valueModel": { "alias": "model"; "required": false; "isSignal": true; }; }, { "sdReadStateChange": "sdReadStateChange"; "valueModel": "modelChange"; "sdChange": "sdChange"; "sdSelection": "sdSelection"; }, ["sdLabelTemplate", "sdValueTemplate", "sdSelectedTemplate", "itemDef", "sdViewDef", "footerActions", "dataStateTemplate"], ["[sdLabel]"], true, never>;
152
162
  }
package/i18n/src/en.d.ts CHANGED
@@ -1,4 +1,13 @@
1
1
  export declare const EN_MESSAGES: {
2
+ readonly 'core.component.data-state.loading.title': "Loading";
3
+ readonly 'core.component.data-state.loading.message': "Please wait while the data is being loaded.";
4
+ readonly 'core.component.data-state.empty.title': "No data";
5
+ readonly 'core.component.data-state.empty.message': "There is no data to display yet.";
6
+ readonly 'core.component.data-state.error.title': "Unable to load data";
7
+ readonly 'core.component.data-state.error.message': "An error occurred. Please try again.";
8
+ readonly 'core.component.data-state.forbidden.title': "Access denied";
9
+ readonly 'core.component.data-state.forbidden.message': "You do not have permission to view this content.";
10
+ readonly 'core.component.data-state.retry': "Try again";
2
11
  readonly 'core.common.cancel': "Cancel";
3
12
  readonly 'core.common.close': "Close";
4
13
  readonly 'core.common.reload': "Reload page";
@@ -114,6 +123,7 @@ export declare const EN_MESSAGES: {
114
123
  readonly 'core.form.date-range.invalid-max': "End date is invalid (above limit)";
115
124
  readonly 'core.form.date-range.from': "From";
116
125
  readonly 'core.form.date-range.to': "to";
126
+ readonly 'core.form.date-range.clear': "Clear date range";
117
127
  readonly 'core.form.datetime.required': "Please enter information";
118
128
  readonly 'core.form.datetime.min-date': "Minimum date: {date}";
119
129
  readonly 'core.form.datetime.max-date': "Maximum date: {date}";
@@ -139,6 +149,7 @@ export declare const EN_MESSAGES: {
139
149
  readonly 'core.form.checkbox.unchecked': "No";
140
150
  readonly 'core.form.select.required': "Please enter information";
141
151
  readonly 'core.form.select.selectAll': "All";
152
+ readonly 'core.form.select.selected-count': "{count} items selected";
142
153
  readonly 'core.form.textarea.required': "Please enter information";
143
154
  readonly 'core.form.textarea.maxlength': "Maximum characters: {max}";
144
155
  readonly 'core.form.textarea.invalid-pattern': "Invalid format";
@@ -280,6 +291,21 @@ export declare const EN_MESSAGES: {
280
291
  readonly 'core.component.code-editor.copied': "Copied!";
281
292
  readonly 'core.component.code-editor.copy': "Copy code";
282
293
  readonly 'core.component.history.by': "By";
294
+ readonly 'core.component.import-excel.choose-hint': "Choose an .xlsx template file with up to {limit} rows.";
295
+ readonly 'core.component.import-excel.choose-file': "Choose Excel file";
296
+ readonly 'core.component.import-excel.template': "Download template";
297
+ readonly 'core.component.import-excel.row-count': "{count} data rows";
298
+ readonly 'core.component.import-excel.replace-file': "Replace file";
299
+ readonly 'core.component.import-excel.filter-all': "All";
300
+ readonly 'core.component.import-excel.filter-success': "Valid";
301
+ readonly 'core.component.import-excel.filter-warning': "Warnings";
302
+ readonly 'core.component.import-excel.filter-error': "Errors";
303
+ readonly 'core.component.import-excel.preview': "Excel data preview";
304
+ readonly 'core.component.import-excel.issue-count': "View {count} issues";
305
+ readonly 'core.component.import-excel.no-filter-results': "No matching rows";
306
+ readonly 'core.component.import-excel.no-filter-results-hint': "Try another filter or view all data.";
307
+ readonly 'core.component.import-excel.blocked-errors': "Fix {count} rows with errors before importing.";
308
+ readonly 'core.component.import-excel.submitted': "Data has been submitted for processing.";
283
309
  readonly 'core.component.import-excel.title': "Import Excel data";
284
310
  readonly 'core.component.import-excel.see-all': "See all";
285
311
  readonly 'core.component.import-excel.valid-rows': "Valid rows";
@@ -406,9 +432,14 @@ export declare const EN_MESSAGES: {
406
432
  readonly 'core.component.table.not-an-array': "Data is not an array";
407
433
  readonly 'core.component.table.config.confirm-reset': "Confirm reset all settings to defaults";
408
434
  readonly 'core.component.table.popup-export.select-columns': "Please select columns to export";
435
+ readonly 'core.component.table.selector-action.selected': "Selected {count}";
436
+ readonly 'core.component.table.selector-action.more': "More";
409
437
  readonly 'core.component.table.selector-action.default-msg': "items selected";
410
438
  readonly 'core.component.tab-router.too-many-tabs': "You have opened too many tabs.";
411
439
  readonly 'core.component.upload-file.upload': "Upload file";
440
+ readonly 'core.component.upload-file.remove': "Remove file";
441
+ readonly 'core.component.upload-file.loading': "Uploading file";
442
+ readonly 'core.component.upload-file.choose-or-drop': "Choose a file or drag it here";
412
443
  readonly 'core.component.upload-file.error-occurred': "An error occurred";
413
444
  readonly 'core.component.upload-file.please-upload': "Please upload a file/image";
414
445
  readonly 'core.component.upload-file.desc-format-and-size': "Format: {exts} and up to: {maxSize}MB";
@@ -429,6 +460,7 @@ export declare const EN_MESSAGES: {
429
460
  readonly 'core.module.layout.user.change-password': "Change password";
430
461
  readonly 'core.module.layout.user.logout': "Sign out";
431
462
  readonly 'core.module.layout.sidebar.search': "Search";
463
+ readonly 'core.module.layout.sidebar.clear-search': "Clear search";
432
464
  readonly 'core.module.layout.greeting.hello': "Hello, {name}";
433
465
  readonly 'core.module.layout.weekday.0': "Sunday";
434
466
  readonly 'core.module.layout.weekday.1': "Monday";
@@ -5,7 +5,7 @@ export declare class SdLayoutComponent {
5
5
  menusInput: import("@angular/core").InputSignal<SdLayoutMenu[]>;
6
6
  menus: import("@angular/core").Signal<SdLayoutMenu[]>;
7
7
  userInfo: import("@angular/core").WritableSignal<import("@sd-angular/core/modules/layout").SdLayoutUserInfo | undefined>;
8
- sidebar: import("@angular/core").WritableSignal<import("@sd-angular/core/modules/layout").SidebarConfigurationV1 | undefined>;
8
+ sidebar: import("@angular/core").WritableSignal<import("@sd-angular/core/modules/layout").ISdSidebarConfiguration | undefined>;
9
9
  isMobileOrTablet: import("@angular/core").WritableSignal<boolean>;
10
10
  static ɵfac: i0.ɵɵFactoryDeclaration<SdLayoutComponent, never>;
11
11
  static ɵcmp: i0.ɵɵComponentDeclaration<SdLayoutComponent, "sd-layout", never, { "menusInput": { "alias": "menus"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
@@ -1,5 +1,5 @@
1
1
  import { Params } from '@angular/router';
2
- import { SdLayoutUserInfo, SidebarConfigurationV1 } from '../../../../configurations';
2
+ import { SdLayoutUserInfo, ISdSidebarConfiguration } from '../../../../configurations';
3
3
  import { SdLayoutMenu } from '../../../../services';
4
4
  import * as i0 from "@angular/core";
5
5
  export declare class SidebarMobileOverlayComponent {
@@ -7,7 +7,7 @@ export declare class SidebarMobileOverlayComponent {
7
7
  isShowSidebar: import("@angular/core").InputSignal<boolean>;
8
8
  menus: import("@angular/core").InputSignal<SdLayoutMenu[]>;
9
9
  userInfo: import("@angular/core").InputSignal<SdLayoutUserInfo>;
10
- sidebar: import("@angular/core").InputSignal<SidebarConfigurationV1>;
10
+ sidebar: import("@angular/core").InputSignal<ISdSidebarConfiguration>;
11
11
  showSideBar: import("@angular/core").OutputEmitterRef<boolean | null>;
12
12
  expandSideBar: import("@angular/core").OutputEmitterRef<void>;
13
13
  popupUserMenuOpened: import("@angular/core").OutputEmitterRef<void>;
@@ -1,11 +1,11 @@
1
- import { SdLayoutUserInfo, SidebarConfigurationV1 } from '../../configurations';
1
+ import { SdLayoutUserInfo, ISdSidebarConfiguration } from '../../configurations';
2
2
  import { SdLayoutMenu } from '../../services';
3
3
  import * as i0 from "@angular/core";
4
4
  export declare class SidebarMobileV1Component {
5
5
  #private;
6
6
  menus: import("@angular/core").InputSignal<SdLayoutMenu[]>;
7
7
  userInfo: import("@angular/core").InputSignal<SdLayoutUserInfo>;
8
- sidebar: import("@angular/core").InputSignal<SidebarConfigurationV1>;
8
+ sidebar: import("@angular/core").InputSignal<ISdSidebarConfiguration>;
9
9
  isShowSidebar: import("@angular/core").WritableSignal<boolean>;
10
10
  titleMenuGroup: import("@angular/core").WritableSignal<string | undefined>;
11
11
  constructor();
@@ -0,0 +1,56 @@
1
+ import { NestedTreeControl } from '@angular/cdk/tree';
2
+ import { MatTreeNestedDataSource } from '@angular/material/tree';
3
+ import { Params } from '@angular/router';
4
+ import { SdLayoutUserInfo, SidebarConfigurationV2 } from '../../../../configurations';
5
+ import { SdLayoutChildrenMenu, SdLayoutMenu } from '../../../../services';
6
+ import * as i0 from "@angular/core";
7
+ export declare class SidebarV2PanelComponent {
8
+ #private;
9
+ isShowSidebar: import("@angular/core").InputSignal<boolean>;
10
+ menus: import("@angular/core").InputSignal<SdLayoutMenu[]>;
11
+ userInfo: import("@angular/core").InputSignal<SdLayoutUserInfo>;
12
+ sidebar: import("@angular/core").InputSignal<SidebarConfigurationV2>;
13
+ expandSideBar: import("@angular/core").OutputEmitterRef<void>;
14
+ popupUserMenuClosed: import("@angular/core").OutputEmitterRef<void>;
15
+ popupUserMenuOpened: import("@angular/core").OutputEmitterRef<void>;
16
+ showSideBar: import("@angular/core").OutputEmitterRef<boolean | null>;
17
+ screenHeight: number;
18
+ isMobileOrTablet: boolean;
19
+ isMenuLock: import("@angular/core").WritableSignal<boolean>;
20
+ currentPath: import("@angular/core").WritableSignal<string>;
21
+ searchText: import("@angular/core").WritableSignal<string>;
22
+ titleMenuGroup: import("@angular/core").WritableSignal<string | undefined>;
23
+ idMenuGroupActive: import("@angular/core").WritableSignal<string | undefined>;
24
+ menusByGroup: import("@angular/core").WritableSignal<SdLayoutMenu[]>;
25
+ pinnedMenuGroup: import("@angular/core").WritableSignal<SdLayoutChildrenMenu>;
26
+ totalMenuInMenusByGroup: import("@angular/core").Signal<number>;
27
+ pinnedNodeKeys: import("@angular/core").Signal<Set<string>>;
28
+ isPinnedMenuGroupActive: import("@angular/core").Signal<boolean>;
29
+ activeMenuPath: import("@angular/core").Signal<string | null>;
30
+ dataSource: MatTreeNestedDataSource<SdLayoutMenu>;
31
+ treeControl: NestedTreeControl<SdLayoutMenu, SdLayoutMenu>;
32
+ constructor();
33
+ hasChild: (_: number, node: SdLayoutMenu) => boolean;
34
+ onToggleMenuNode: (menu: SdLayoutMenu) => void;
35
+ isPinnedNode: (node: SdLayoutMenu) => boolean;
36
+ isHoveredNode: (node: SdLayoutMenu) => boolean;
37
+ expandPinnedGroup: () => void;
38
+ onTogglePin: (event: MouseEvent, node: SdLayoutMenu) => void;
39
+ toggleMenuLock(event: Event): void;
40
+ openHomePage: () => void;
41
+ onUserMenuClosed: () => void;
42
+ onUserMenuOpened: () => void;
43
+ navigate: (args: {
44
+ path: string;
45
+ queryParams: Params;
46
+ }) => void;
47
+ onFilterSearchText: (event: string) => void;
48
+ onClearSearchText: () => void;
49
+ expandMenuGroup: (menuGroupNode: SdLayoutMenu) => void;
50
+ onMouseOverMenuGroupNode: (event: MouseEvent, menuNode: SdLayoutMenu) => void;
51
+ onMouseLeaveMenuGroupNode: (event: MouseEvent, menuNode: SdLayoutMenu) => void;
52
+ onMouseOverMenuNode: (event: MouseEvent, menuItem: SdLayoutMenu) => void;
53
+ onMouseLeaveMenuNode: (event: MouseEvent, menuItem: SdLayoutMenu) => void;
54
+ static ɵfac: i0.ɵɵFactoryDeclaration<SidebarV2PanelComponent, never>;
55
+ static ɵcmp: i0.ɵɵComponentDeclaration<SidebarV2PanelComponent, "sd-sidebar-v2-panel", never, { "isShowSidebar": { "alias": "isShowSidebar"; "required": true; "isSignal": true; }; "menus": { "alias": "menus"; "required": true; "isSignal": true; }; "userInfo": { "alias": "userInfo"; "required": true; "isSignal": true; }; "sidebar": { "alias": "sidebar"; "required": true; "isSignal": true; }; }, { "expandSideBar": "expandSideBar"; "popupUserMenuClosed": "popupUserMenuClosed"; "popupUserMenuOpened": "popupUserMenuOpened"; "showSideBar": "showSideBar"; }, never, never, true, never>;
56
+ }
@@ -0,0 +1,22 @@
1
+ import { SdLayoutUserInfo } from '../../../../configurations';
2
+ import * as i0 from "@angular/core";
3
+ export declare class SidebarV2UserComponent {
4
+ #private;
5
+ isMobileOrTablet: import("@angular/core").InputSignal<boolean>;
6
+ isMenuLock: import("@angular/core").InputSignal<boolean>;
7
+ isShowSidebar: import("@angular/core").InputSignal<boolean>;
8
+ userInfo: import("@angular/core").InputSignal<SdLayoutUserInfo>;
9
+ menuClosed: import("@angular/core").OutputEmitterRef<void>;
10
+ menuOpened: import("@angular/core").OutputEmitterRef<void>;
11
+ toggleMenuLock: import("@angular/core").OutputEmitterRef<Event>;
12
+ singoutLayoutConfig: () => void | Promise<void>;
13
+ changePasswordLayoutConfig: (() => void | Promise<void>) | undefined;
14
+ onMenuOpened: () => void;
15
+ onMenuClosed: () => void;
16
+ keepOpenWhenClickInsideMenu: (event: Event) => void;
17
+ logout: () => void;
18
+ changePassword: () => void;
19
+ onToggleMenuLock: (event: Event) => void;
20
+ static ɵfac: i0.ɵɵFactoryDeclaration<SidebarV2UserComponent, never>;
21
+ static ɵcmp: i0.ɵɵComponentDeclaration<SidebarV2UserComponent, "sd-sidebar-v2-user", never, { "isMobileOrTablet": { "alias": "isMobileOrTablet"; "required": false; "isSignal": true; }; "isMenuLock": { "alias": "isMenuLock"; "required": false; "isSignal": true; }; "isShowSidebar": { "alias": "isShowSidebar"; "required": false; "isSignal": true; }; "userInfo": { "alias": "userInfo"; "required": true; "isSignal": true; }; }, { "menuClosed": "menuClosed"; "menuOpened": "menuOpened"; "toggleMenuLock": "toggleMenuLock"; }, never, never, true, never>;
22
+ }
@@ -0,0 +1,25 @@
1
+ import { MatSidenav } from '@angular/material/sidenav';
2
+ import { SdLayoutUserInfo, SidebarConfigurationV2 } from '../../configurations';
3
+ import { SdLayoutMenu } from '../../services';
4
+ import * as i0 from "@angular/core";
5
+ export declare class SidebarV2Component {
6
+ #private;
7
+ sidenav: import("@angular/core").Signal<MatSidenav>;
8
+ menus: import("@angular/core").InputSignal<SdLayoutMenu[]>;
9
+ userInfo: import("@angular/core").InputSignal<SdLayoutUserInfo>;
10
+ sidebar: import("@angular/core").InputSignal<SidebarConfigurationV2>;
11
+ isMobileOrTablet: import("@angular/core").WritableSignal<boolean>;
12
+ isMenuLock: import("@angular/core").WritableSignal<boolean>;
13
+ isShowSidebar: import("@angular/core").WritableSignal<boolean>;
14
+ onhover: import("@angular/core").WritableSignal<boolean>;
15
+ constructor();
16
+ openSidebar: () => void;
17
+ onPopupOfSideBarOpened: () => void;
18
+ onPopupOfSideBarClosed: () => void;
19
+ onExpandSideBar: () => void;
20
+ onMouseleaveSideBar: () => void;
21
+ onSidenavOpenedChange: (opened: boolean) => void;
22
+ onToggle: (data: boolean | null) => void;
23
+ static ɵfac: i0.ɵɵFactoryDeclaration<SidebarV2Component, never>;
24
+ static ɵcmp: i0.ɵɵComponentDeclaration<SidebarV2Component, "sd-sidebar-v2", never, { "menus": { "alias": "menus"; "required": false; "isSignal": true; }; "userInfo": { "alias": "userInfo"; "required": true; "isSignal": true; }; "sidebar": { "alias": "sidebar"; "required": true; "isSignal": true; }; }, {}, never, ["*"], true, never>;
25
+ }
@@ -30,7 +30,7 @@ export interface SdLayoutUserInfo {
30
30
  */
31
31
  avatar?: string;
32
32
  }
33
- export type ISdSidebarConfiguration = SidebarConfigurationV1;
33
+ export type ISdSidebarConfiguration = SidebarConfigurationV1 | SidebarConfigurationV2;
34
34
  export interface SidebarConfigurationV1 {
35
35
  version: 1;
36
36
  /**
@@ -64,4 +64,8 @@ export interface SidebarConfigurationV1 {
64
64
  enabled?: boolean;
65
65
  };
66
66
  }
67
+ /** Opt in to the refreshed desktop sidebar while keeping the V1 configuration fields. */
68
+ export interface SidebarConfigurationV2 extends Omit<SidebarConfigurationV1, 'version'> {
69
+ version: 2;
70
+ }
67
71
  export declare const SD_LAYOUT_CONFIGURATION: InjectionToken<ISdLayoutConfiguration>;
@@ -1,9 +1,9 @@
1
- import { SdLayoutUserInfo } from '../configurations';
1
+ import { ISdSidebarConfiguration, SdLayoutUserInfo } from '../configurations';
2
2
  import * as i0 from "@angular/core";
3
3
  export declare class SdLayoutService {
4
4
  #private;
5
5
  userInfo: import("@angular/core").WritableSignal<SdLayoutUserInfo | undefined>;
6
- sidebar: import("@angular/core").WritableSignal<import("@sd-angular/core/modules/layout").SidebarConfigurationV1 | undefined>;
6
+ sidebar: import("@angular/core").WritableSignal<ISdSidebarConfiguration | undefined>;
7
7
  constructor();
8
8
  static ɵfac: i0.ɵɵFactoryDeclaration<SdLayoutService, never>;
9
9
  static ɵprov: i0.ɵɵInjectableDeclaration<SdLayoutService>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sd-angular/core",
3
- "version": "19.0.31",
3
+ "version": "19.0.33",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^19.0.0 || ^20.0.0 || ^21.0.0",
6
6
  "@angular/core": "^19.0.0 || ^20.0.0 || ^21.0.0",
@@ -116,6 +116,10 @@
116
116
  "types": "./components/code-editor/index.d.ts",
117
117
  "default": "./fesm2022/sd-angular-core-components-code-editor.mjs"
118
118
  },
119
+ "./components/data-state": {
120
+ "types": "./components/data-state/index.d.ts",
121
+ "default": "./fesm2022/sd-angular-core-components-data-state.mjs"
122
+ },
119
123
  "./components/document-builder": {
120
124
  "types": "./components/document-builder/index.d.ts",
121
125
  "default": "./fesm2022/sd-angular-core-components-document-builder.mjs"
@@ -355,6 +359,10 @@
355
359
  "./utilities/models": {
356
360
  "types": "./utilities/models/index.d.ts",
357
361
  "default": "./fesm2022/sd-angular-core-utilities-models.mjs"
362
+ },
363
+ "./utilities/read-state": {
364
+ "types": "./utilities/read-state/index.d.ts",
365
+ "default": "./fesm2022/sd-angular-core-utilities-read-state.mjs"
358
366
  }
359
367
  }
360
368
  }
@@ -1,3 +1,4 @@
1
1
  export * from '@sd-angular/core/utilities/extensions';
2
2
  export * from '@sd-angular/core/utilities/models';
3
3
  export * from '@sd-angular/core/utilities/data-state';
4
+ export * from '@sd-angular/core/utilities/read-state';
@@ -0,0 +1 @@
1
+ export * from './src/read-state';
@@ -0,0 +1,37 @@
1
+ export type SdReadOperation = 'TABLE' | 'VALUE' | 'SEARCH';
2
+ export type SdReadStatus = 'idle' | 'loading' | 'ready' | 'empty' | 'error';
3
+ /** An accepted read result. Only an error snapshot contains the original exception. */
4
+ export type SdReadState = Readonly<{
5
+ status: Exclude<SdReadStatus, 'error'>;
6
+ operation: SdReadOperation;
7
+ } | {
8
+ status: 'error';
9
+ operation: SdReadOperation;
10
+ error: unknown;
11
+ }>;
12
+ export type SdSearchReadState = SdReadState & {
13
+ readonly channels: Readonly<{
14
+ VALUE: SdReadState;
15
+ SEARCH: SdReadState;
16
+ }>;
17
+ };
18
+ /** VALUE errors take precedence; otherwise SEARCH errors, then the active SEARCH channel. */
19
+ export declare function combineReadStates(value: SdReadState, search: SdReadState): SdSearchReadState;
20
+ /** @internal Request guard shared by controls. Context is checked even before Angular effects run. */
21
+ export declare class SdReadChannel {
22
+ #private;
23
+ readonly operation: SdReadOperation;
24
+ private readonly changed?;
25
+ readonly state: import("@angular/core").Signal<SdReadState>;
26
+ constructor(operation: SdReadOperation, changed?: ((state: SdReadState) => void) | undefined);
27
+ begin(valid?: () => boolean): number;
28
+ isCurrent(revision: number): boolean;
29
+ succeed(revision: number, count: number): boolean;
30
+ fail(revision: number, error: unknown): boolean;
31
+ invalidate(): void;
32
+ }
33
+ /**
34
+ * @internal Copy request-owned data, including Dates. Unlike ObjectUtilities.clone,
35
+ * Dates must be copied too; structuredClone cannot retain table column callbacks.
36
+ */
37
+ export declare function cloneReadRequest<T>(value: T, seen?: WeakMap<object, unknown>): T;