@sdcorejs/angular 21.0.9 → 21.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/README.md +270 -140
  2. package/assets/scss/core/utilities/_grid.scss +25 -19
  3. package/fesm2022/sdcorejs-angular-components-anchor.mjs +15 -18
  4. package/fesm2022/sdcorejs-angular-components-anchor.mjs.map +1 -1
  5. package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs.map +1 -1
  6. package/fesm2022/sdcorejs-angular-components-avatar.mjs.map +1 -1
  7. package/fesm2022/sdcorejs-angular-components-badge.mjs +0 -1
  8. package/fesm2022/sdcorejs-angular-components-badge.mjs.map +1 -1
  9. package/fesm2022/sdcorejs-angular-components-button.mjs +6 -7
  10. package/fesm2022/sdcorejs-angular-components-button.mjs.map +1 -1
  11. package/fesm2022/sdcorejs-angular-components-chart.mjs.map +1 -1
  12. package/fesm2022/sdcorejs-angular-components-ckeditor-styles.mjs.map +1 -1
  13. package/fesm2022/sdcorejs-angular-components-code-editor.mjs +1 -1
  14. package/fesm2022/sdcorejs-angular-components-code-editor.mjs.map +1 -1
  15. package/fesm2022/sdcorejs-angular-components-document-builder.mjs +17 -26
  16. package/fesm2022/sdcorejs-angular-components-document-builder.mjs.map +1 -1
  17. package/fesm2022/sdcorejs-angular-components-editor.mjs +7 -2
  18. package/fesm2022/sdcorejs-angular-components-editor.mjs.map +1 -1
  19. package/fesm2022/sdcorejs-angular-components-form-generic.mjs +712 -427
  20. package/fesm2022/sdcorejs-angular-components-form-generic.mjs.map +1 -1
  21. package/fesm2022/sdcorejs-angular-components-import-excel.mjs +35 -18
  22. package/fesm2022/sdcorejs-angular-components-import-excel.mjs.map +1 -1
  23. package/fesm2022/sdcorejs-angular-components-inform.mjs +0 -1
  24. package/fesm2022/sdcorejs-angular-components-inform.mjs.map +1 -1
  25. package/fesm2022/sdcorejs-angular-components-mini-editor.mjs.map +1 -1
  26. package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs.map +1 -1
  27. package/fesm2022/sdcorejs-angular-components-modal.mjs +21 -14
  28. package/fesm2022/sdcorejs-angular-components-modal.mjs.map +1 -1
  29. package/fesm2022/sdcorejs-angular-components-operator.mjs +1 -1
  30. package/fesm2022/sdcorejs-angular-components-operator.mjs.map +1 -1
  31. package/fesm2022/sdcorejs-angular-components-org-chart.mjs.map +1 -1
  32. package/fesm2022/sdcorejs-angular-components-preview.mjs +33 -29
  33. package/fesm2022/sdcorejs-angular-components-preview.mjs.map +1 -1
  34. package/fesm2022/sdcorejs-angular-components-query-bar.mjs +15 -28
  35. package/fesm2022/sdcorejs-angular-components-query-bar.mjs.map +1 -1
  36. package/fesm2022/sdcorejs-angular-components-query-builder.mjs +2 -3
  37. package/fesm2022/sdcorejs-angular-components-query-builder.mjs.map +1 -1
  38. package/fesm2022/sdcorejs-angular-components-quick-action.mjs.map +1 -1
  39. package/fesm2022/sdcorejs-angular-components-section.mjs +0 -1
  40. package/fesm2022/sdcorejs-angular-components-section.mjs.map +1 -1
  41. package/fesm2022/sdcorejs-angular-components-side-drawer.mjs +3 -3
  42. package/fesm2022/sdcorejs-angular-components-side-drawer.mjs.map +1 -1
  43. package/fesm2022/sdcorejs-angular-components-splitter.mjs +4 -4
  44. package/fesm2022/sdcorejs-angular-components-splitter.mjs.map +1 -1
  45. package/fesm2022/sdcorejs-angular-components-stepper.mjs.map +1 -1
  46. package/fesm2022/sdcorejs-angular-components-tab-router.mjs +31 -31
  47. package/fesm2022/sdcorejs-angular-components-tab-router.mjs.map +1 -1
  48. package/fesm2022/sdcorejs-angular-components-tab.mjs.map +1 -1
  49. package/fesm2022/sdcorejs-angular-components-table.mjs +66 -84
  50. package/fesm2022/sdcorejs-angular-components-table.mjs.map +1 -1
  51. package/fesm2022/sdcorejs-angular-components-tree.mjs.map +1 -1
  52. package/fesm2022/sdcorejs-angular-components-upload-file.mjs +6 -6
  53. package/fesm2022/sdcorejs-angular-components-upload-file.mjs.map +1 -1
  54. package/fesm2022/sdcorejs-angular-components-view.mjs +3 -9
  55. package/fesm2022/sdcorejs-angular-components-view.mjs.map +1 -1
  56. package/fesm2022/sdcorejs-angular-configurations.mjs.map +1 -1
  57. package/fesm2022/sdcorejs-angular-directives.mjs +2 -7
  58. package/fesm2022/sdcorejs-angular-directives.mjs.map +1 -1
  59. package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs +0 -3
  60. package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs.map +1 -1
  61. package/fesm2022/sdcorejs-angular-forms-checkbox.mjs.map +1 -1
  62. package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs +2 -2
  63. package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs.map +1 -1
  64. package/fesm2022/sdcorejs-angular-forms-chip.mjs +2 -2
  65. package/fesm2022/sdcorejs-angular-forms-chip.mjs.map +1 -1
  66. package/fesm2022/sdcorejs-angular-forms-date-range.mjs +1 -3
  67. package/fesm2022/sdcorejs-angular-forms-date-range.mjs.map +1 -1
  68. package/fesm2022/sdcorejs-angular-forms-date.mjs +2 -4
  69. package/fesm2022/sdcorejs-angular-forms-date.mjs.map +1 -1
  70. package/fesm2022/sdcorejs-angular-forms-datetime.mjs +2 -6
  71. package/fesm2022/sdcorejs-angular-forms-datetime.mjs.map +1 -1
  72. package/fesm2022/sdcorejs-angular-forms-input-color.mjs +4 -4
  73. package/fesm2022/sdcorejs-angular-forms-input-color.mjs.map +1 -1
  74. package/fesm2022/sdcorejs-angular-forms-input-number.mjs +1 -1
  75. package/fesm2022/sdcorejs-angular-forms-input-number.mjs.map +1 -1
  76. package/fesm2022/sdcorejs-angular-forms-input.mjs.map +1 -1
  77. package/fesm2022/sdcorejs-angular-forms-label.mjs +0 -1
  78. package/fesm2022/sdcorejs-angular-forms-label.mjs.map +1 -1
  79. package/fesm2022/sdcorejs-angular-forms-models.mjs.map +1 -1
  80. package/fesm2022/sdcorejs-angular-forms-radio.mjs +12 -2
  81. package/fesm2022/sdcorejs-angular-forms-radio.mjs.map +1 -1
  82. package/fesm2022/sdcorejs-angular-forms-select.mjs +45 -14
  83. package/fesm2022/sdcorejs-angular-forms-select.mjs.map +1 -1
  84. package/fesm2022/sdcorejs-angular-forms-switch.mjs.map +1 -1
  85. package/fesm2022/sdcorejs-angular-forms-textarea.mjs +2 -2
  86. package/fesm2022/sdcorejs-angular-forms-textarea.mjs.map +1 -1
  87. package/fesm2022/sdcorejs-angular-handlers.mjs +3 -6
  88. package/fesm2022/sdcorejs-angular-handlers.mjs.map +1 -1
  89. package/fesm2022/sdcorejs-angular-i18n.mjs +11 -7
  90. package/fesm2022/sdcorejs-angular-i18n.mjs.map +1 -1
  91. package/fesm2022/sdcorejs-angular-interceptors.mjs +0 -2
  92. package/fesm2022/sdcorejs-angular-interceptors.mjs.map +1 -1
  93. package/fesm2022/sdcorejs-angular-modules-auth.mjs +14 -38
  94. package/fesm2022/sdcorejs-angular-modules-auth.mjs.map +1 -1
  95. package/fesm2022/sdcorejs-angular-modules-authom.mjs +14 -14
  96. package/fesm2022/sdcorejs-angular-modules-authom.mjs.map +1 -1
  97. package/fesm2022/sdcorejs-angular-modules-keycloak.mjs +22 -11
  98. package/fesm2022/sdcorejs-angular-modules-keycloak.mjs.map +1 -1
  99. package/fesm2022/sdcorejs-angular-modules-layout.mjs +30 -27
  100. package/fesm2022/sdcorejs-angular-modules-layout.mjs.map +1 -1
  101. package/fesm2022/sdcorejs-angular-modules-permission.mjs +5 -12
  102. package/fesm2022/sdcorejs-angular-modules-permission.mjs.map +1 -1
  103. package/fesm2022/sdcorejs-angular-pipes.mjs +71 -17
  104. package/fesm2022/sdcorejs-angular-pipes.mjs.map +1 -1
  105. package/fesm2022/sdcorejs-angular-services-api.mjs +0 -2
  106. package/fesm2022/sdcorejs-angular-services-api.mjs.map +1 -1
  107. package/fesm2022/sdcorejs-angular-services-cache.mjs +5 -11
  108. package/fesm2022/sdcorejs-angular-services-cache.mjs.map +1 -1
  109. package/fesm2022/sdcorejs-angular-services-confirm.mjs +16 -21
  110. package/fesm2022/sdcorejs-angular-services-confirm.mjs.map +1 -1
  111. package/fesm2022/sdcorejs-angular-services-docx.mjs +2 -8
  112. package/fesm2022/sdcorejs-angular-services-docx.mjs.map +1 -1
  113. package/fesm2022/sdcorejs-angular-services-excel.mjs +2 -2
  114. package/fesm2022/sdcorejs-angular-services-excel.mjs.map +1 -1
  115. package/fesm2022/sdcorejs-angular-services-license.mjs +5 -12
  116. package/fesm2022/sdcorejs-angular-services-license.mjs.map +1 -1
  117. package/fesm2022/sdcorejs-angular-services-notify.mjs +70 -87
  118. package/fesm2022/sdcorejs-angular-services-notify.mjs.map +1 -1
  119. package/fesm2022/sdcorejs-angular-services-storage.mjs +6 -15
  120. package/fesm2022/sdcorejs-angular-services-storage.mjs.map +1 -1
  121. package/fesm2022/sdcorejs-angular-utilities-data-state.mjs.map +1 -1
  122. package/fesm2022/sdcorejs-angular-utilities-extensions.mjs +47 -5
  123. package/fesm2022/sdcorejs-angular-utilities-extensions.mjs.map +1 -1
  124. package/fesm2022/sdcorejs-angular-utilities-models.mjs +18 -3
  125. package/fesm2022/sdcorejs-angular-utilities-models.mjs.map +1 -1
  126. package/fesm2022/sdcorejs-angular.mjs.map +1 -1
  127. package/package.json +2 -2
  128. package/types/sdcorejs-angular-components-document-builder.d.ts +2 -2
  129. package/types/sdcorejs-angular-components-form-generic.d.ts +45 -23
  130. package/types/sdcorejs-angular-components-import-excel.d.ts +2 -2
  131. package/types/sdcorejs-angular-components-mini-editor.d.ts +4 -4
  132. package/types/sdcorejs-angular-components-modal.d.ts +1 -1
  133. package/types/sdcorejs-angular-components-query-builder.d.ts +4 -4
  134. package/types/sdcorejs-angular-components-tab-router.d.ts +3 -3
  135. package/types/sdcorejs-angular-components-table.d.ts +3 -2
  136. package/types/sdcorejs-angular-components-view.d.ts +1 -1
  137. package/types/sdcorejs-angular-forms-chip-calendar.d.ts +2 -2
  138. package/types/sdcorejs-angular-forms-chip.d.ts +2 -2
  139. package/types/sdcorejs-angular-forms-input-color.d.ts +2 -1
  140. package/types/sdcorejs-angular-forms-select.d.ts +10 -2
  141. package/types/sdcorejs-angular-modules-auth.d.ts +7 -9
  142. package/types/sdcorejs-angular-modules-permission.d.ts +5 -6
  143. package/types/sdcorejs-angular-pipes.d.ts +22 -4
  144. package/types/sdcorejs-angular-services-confirm.d.ts +3 -3
  145. package/types/sdcorejs-angular-services-license.d.ts +2 -3
  146. package/types/sdcorejs-angular-services-notify.d.ts +4 -5
  147. package/types/sdcorejs-angular-services-storage.d.ts +2 -3
  148. package/types/sdcorejs-angular-utilities-extensions.d.ts +9 -1
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, inject, TemplateRef, Directive, booleanAttribute, computed, ChangeDetectionStrategy, Component, model, output, isSignal, effect, Pipe, ChangeDetectorRef, signal, untracked, viewChild, InjectionToken, Inject, Optional, Injectable, EventEmitter, Input, Output, ViewChild, ElementRef, DestroyRef, afterNextRender, Renderer2, NgZone, contentChild, contentChildren } from '@angular/core';
2
+ import { input, inject, TemplateRef, Directive, booleanAttribute, computed, ChangeDetectionStrategy, Component, model, output, isSignal, effect, Pipe, ChangeDetectorRef, signal, untracked, viewChild, InjectionToken, Injectable, EventEmitter, Input, Output, ViewChild, ElementRef, DestroyRef, afterNextRender, Renderer2, NgZone, contentChild, contentChildren } from '@angular/core';
3
3
  import { trigger, state, style, transition, animate } from '@angular/animations';
4
4
  import * as i4$1 from '@angular/material/paginator';
5
5
  import { MatPaginatorIntl, MatPaginator, MatPaginatorModule } from '@angular/material/paginator';
@@ -7,8 +7,8 @@ import * as i7 from '@angular/material/sort';
7
7
  import { MatSort, MatSortModule } from '@angular/material/sort';
8
8
  import * as i6 from '@angular/material/table';
9
9
  import { MatTable, MatTableModule } from '@angular/material/table';
10
- import * as i1$1 from '@sdcorejs/angular/services';
11
- import { SdExcelService, SdNotifyService } from '@sdcorejs/angular/services';
10
+ import * as i1$2 from '@sdcorejs/angular/services';
11
+ import { SdStorageService, SdExcelService, SdNotifyService } from '@sdcorejs/angular/services';
12
12
  import { Subject, fromEvent, Subscription, isObservable, firstValueFrom } from 'rxjs';
13
13
  import { debounceTime, startWith, map, switchMap } from 'rxjs/operators';
14
14
  import { DateUtilities, StringUtilities } from '@sdcorejs/angular/utilities';
@@ -32,8 +32,8 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
32
32
  import { SdButton } from '@sdcorejs/angular/components/button';
33
33
  import { SdQuickAction } from '@sdcorejs/angular/components/quick-action';
34
34
  import { SdTooltipDirective, SdScrollDirective, SdDesktopDirective, SdMobileDirective, SdHoverCopyDirective } from '@sdcorejs/angular/directives';
35
- import { SdSafeHtmlPipe, SdFormatNumberPipe } from '@sdcorejs/angular/pipes';
36
- import { BrowserUtilities, Utilities } from '@sdcorejs/utils/fns';
35
+ import { SdSafeHtmlPipe, SdFormatDatePipe, SdFormatDatetimePipe, SdFormatNumberPipe } from '@sdcorejs/angular/pipes';
36
+ import { Utilities, BrowserUtilities } from '@sdcorejs/utils/fns';
37
37
  import { DateUtilities as DateUtilities$1, ArrayUtilities, NumberUtilities } from '@sdcorejs/angular/utilities/extensions';
38
38
  import { SdBadge } from '@sdcorejs/angular/components/badge';
39
39
  import { SdOperator } from '@sdcorejs/angular/components/operator';
@@ -49,8 +49,9 @@ import { SdModal } from '@sdcorejs/angular/components/modal';
49
49
  import { SdConfirmService } from '@sdcorejs/angular/services/confirm';
50
50
  import * as i3 from '@angular/material/button';
51
51
  import { MatButtonModule } from '@angular/material/button';
52
- import * as i1$2 from '@angular/material/tooltip';
52
+ import * as i1$1 from '@angular/material/tooltip';
53
53
  import { MatTooltipModule } from '@angular/material/tooltip';
54
+ import { DefaultMaterialIconFontSet } from '@sdcorejs/angular/utilities/models';
54
55
  import * as i5 from '@angular/material/chips';
55
56
  import { MatChipsModule } from '@angular/material/chips';
56
57
 
@@ -398,7 +399,6 @@ const SdConvertToPagingReq = (filterRequest, args) => {
398
399
  return req;
399
400
  };
400
401
 
401
- /* eslint-disable @typescript-eslint/no-explicit-any */
402
402
  class ColumnTitleComponent {
403
403
  column = input.required(...(ngDevMode ? [{ debugName: "column" }] : /* istanbul ignore next */ []));
404
404
  titleDef = input(undefined, ...(ngDevMode ? [{ debugName: "titleDef" }] : /* istanbul ignore next */ []));
@@ -417,8 +417,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
417
417
  args: [{ selector: 'column-title', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [NgTemplateOutlet], template: "@let _column = column();\n@let _templateRef = templateRef();\n\n@if (_templateRef) {\n <ng-container *ngTemplateOutlet=\"_templateRef\"></ng-container>\n} @else {\n {{ _column.title }}\n}\n" }]
418
418
  }], propDecorators: { column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], titleDef: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleDef", required: false }] }] } });
419
419
 
420
- /* eslint-disable @typescript-eslint/no-explicit-any */
421
420
  class ColumnFilterComponent {
421
+ #lastOnChangeValueHash;
422
422
  // ==========================================
423
423
  // 1. SIGNAL INPUTS
424
424
  // ==========================================
@@ -503,14 +503,34 @@ class ColumnFilterComponent {
503
503
  if (col.type === 'number' && col.filter?.type === 'split-number') {
504
504
  filter[col.field] = filter[col.field] || { from: null, to: null };
505
505
  }
506
+ this.#lastOnChangeValueHash = this.#filterValueHash(filter[col.field]);
506
507
  });
507
508
  }
508
509
  // ==========================================
509
510
  // 4. HANDLERS
510
511
  // ==========================================
511
- onFilterChange = () => this.filterChange.emit();
512
+ onFilterChange = () => {
513
+ this.#emitFilterOnChange();
514
+ this.filterChange.emit();
515
+ };
512
516
  // Blur input: commit giá trị KHÔNG trigger reload.
513
- onFilterCommit = () => this.filterCommit.emit();
517
+ onFilterCommit = () => {
518
+ this.#emitFilterOnChange();
519
+ this.filterCommit.emit();
520
+ };
521
+ #emitFilterOnChange = () => {
522
+ const col = this.column();
523
+ const filter = this.columnFilter();
524
+ if (!col?.filter?.onChange || !filter)
525
+ return;
526
+ const value = filter[col.field];
527
+ const hash = this.#filterValueHash(value);
528
+ if (hash === this.#lastOnChangeValueHash)
529
+ return;
530
+ this.#lastOnChangeValueHash = hash;
531
+ col.filter.onChange(value, col, filter);
532
+ };
533
+ #filterValueHash = (value) => Utilities.hash({ value });
514
534
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ColumnFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
515
535
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: ColumnFilterComponent, isStandalone: true, selector: "column-filter", inputs: { autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, columnFilter: { classPropertyName: "columnFilter", publicName: "columnFilter", isSignal: true, isRequired: false, transformFunction: null }, cacheValues: { classPropertyName: "cacheValues", publicName: "cacheValues", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, operator: { classPropertyName: "operator", publicName: "operator", isSignal: true, isRequired: false, transformFunction: null }, isMobile: { classPropertyName: "isMobile", publicName: "isMobile", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { operator: "operatorChange", filterChange: "filterChange", filterCommit: "filterCommit" }, ngImport: i0, template: "@let _column = column();\n@let _columnFilter = columnFilter();\n@let _autoId = autoId();\n@let _templateRef = templateRef();\n@let _operators = operators();\n@let _items = items();\n@let _label = label();\n@let _context = { columnFilter: _columnFilter, autoId: _autoId };\n\n<div class=\"d-flex c-inline-column align-items-center\" style=\"width: 100%; max-width: 100%\">\n @if (\n _column.type === 'string' ||\n _column.type === 'number' ||\n _column.type === 'boolean' ||\n _column.type === 'values' ||\n _column.type === 'lazy-values' ||\n _column.type === 'date' ||\n _column.type === 'datetime' ||\n _column.type === 'time'\n ) {\n <!-- Operator filter -->\n @if (_operators.length) {\n <sd-operator class=\"mr-4\" [(model)]=\"operator\" [operators]=\"operatorValues()\" />\n }\n\n <!-- N\u1EBFu filter nh\u1EADn v\u00E0o template -->\n @if (_templateRef) {\n <ng-container *ngTemplateOutlet=\"_templateRef; context: _context\"></ng-container>\n }\n <!-- M\u1EB7c \u0111\u1ECBnh theo type v\u00E0 gi\u00E1 tr\u1ECB values defined c\u1EE7a column -->\n @else {\n @if (_column.type === 'string') {\n <sd-input\n [autoId]=\"_autoId\"\n style=\"flex: 1 1 0%; min-width: 0\"\n size=\"sm\"\n type=\"text\"\n [label]=\"_label\"\n [(model)]=\"_columnFilter[_column.field]\"\n (keyupEnter)=\"onFilterChange()\"\n (sdBlur)=\"onFilterCommit()\"\n (cleared)=\"onFilterChange()\"\n [disabled]=\"_column.filter?.disabled\"\n hideInlineError>\n </sd-input>\n } @else if (_column.type === 'number') {\n @if (!_column.filter?.type) {\n <sd-input-number\n [autoId]=\"_autoId\"\n style=\"flex: 1 1 0%; min-width: 0\"\n size=\"sm\"\n [label]=\"_label\"\n [(model)]=\"_columnFilter[_column.field]\"\n (keyupEnter)=\"onFilterChange()\"\n (sdBlur)=\"onFilterCommit()\"\n (cleared)=\"onFilterChange()\"\n [disabled]=\"_column.filter?.disabled\"\n hideInlineError>\n </sd-input-number>\n } @else if (_column.filter?.type === 'split-number') {\n <ng-container *ngTemplateOutlet=\"splitNumberTpl; context: { field: _column.field, disabled: _column.filter?.disabled }\">\n </ng-container>\n }\n } @else if (_column.type === 'boolean') {\n <sd-select\n minWidthPanel=\"200px\"\n [autoId]=\"_autoId\"\n style=\"flex: 1 1 0%; min-width: 0\"\n [style.width]=\"'100%'\"\n size=\"sm\"\n [label]=\"_label\"\n [items]=\"[\n { value: '1', display: _column.option?.displayOnTrue || 'True' },\n { value: '0', display: _column.option?.displayOnFalse || 'False' },\n ]\"\n valueField=\"value\"\n displayField=\"display\"\n [(model)]=\"_columnFilter[_column.field]\"\n (sdChange)=\"onFilterChange()\"\n [disabled]=\"_column.filter?.disabled\"\n hideInlineError>\n <ng-template sdItemDef let-item=\"item\">\n @if (item.value === '1') {\n <sd-badge color=\"success\" [title]=\"_column.option?.displayOnTrue || 'True'\"> </sd-badge>\n } @else {\n <sd-badge color=\"error\" [title]=\"_column.option?.displayOnFalse || 'False'\"> </sd-badge>\n }\n </ng-template>\n </sd-select>\n } @else if (_column.type === 'values') {\n <sd-select\n minWidthPanel=\"200px\"\n [autoId]=\"_autoId\"\n style=\"flex: 1 1 0%; min-width: 0\"\n [style.width]=\"'100%'\"\n size=\"sm\"\n [label]=\"_label\"\n [items]=\"_items\"\n [valueField]=\"_column.option.valueField\"\n [displayField]=\"_column.option.displayField\"\n [(model)]=\"_columnFilter[_column.field]\"\n (sdChange)=\"onFilterChange()\"\n [disabled]=\"_column.filter?.disabled\"\n [multiple]=\"_column.option.selection === 'MULTIPLE'\"\n hideInlineError>\n </sd-select>\n } @else if (_column.type === 'lazy-values') {\n <sd-select\n minWidthPanel=\"200px\"\n [autoId]=\"_autoId\"\n style=\"flex: 1 1 0%; min-width: 0\"\n [style.width]=\"'100%'\"\n size=\"sm\"\n [label]=\"_label\"\n [items]=\"_items\"\n [valueField]=\"_column.option.valueField\"\n [displayField]=\"_column.option.displayField\"\n [(model)]=\"_columnFilter[_column.field]\"\n (sdChange)=\"onFilterChange()\"\n [disabled]=\"_column.filter?.disabled\"\n [multiple]=\"_column.option.selection === 'MULTIPLE'\"\n hideInlineError>\n </sd-select>\n } @else if (_column.type === 'date' || _column.type === 'datetime' || _column.type === 'time') {\n @if (!_column.filter?.type || _column.filter?.type === 'daterange') {\n <sd-date-range\n style=\"flex: 1 1 0%; min-width: 0\"\n size=\"sm\"\n [label]=\"_label\"\n [(model)]=\"_columnFilter[_column.field]\"\n (sdChange)=\"onFilterChange()\"\n [disabled]=\"_column.filter?.disabled\"\n hideInlineError>\n </sd-date-range>\n } @else if (_column.filter?.type === 'date') {\n <sd-date\n [autoId]=\"_autoId\"\n style=\"flex: 1 1 0%; min-width: 0\"\n type=\"date\"\n size=\"sm\"\n [label]=\"_label\"\n [(model)]=\"_columnFilter[_column.field]\"\n (sdChange)=\"onFilterChange()\"\n [disabled]=\"_column.filter?.disabled\"\n hideInlineError>\n </sd-date>\n } @else if (_column.filter?.type === 'split-date') {\n <ng-container *ngTemplateOutlet=\"splitDateTpl; context: { field: _column.field, disabled: _column.filter?.disabled }\">\n </ng-container>\n }\n }\n }\n } @else {\n <sd-input [autoId]=\"_autoId\" style=\"flex: 1 1 0%; min-width: 0\" type=\"text\" size=\"sm\" disabled></sd-input>\n }\n</div>\n\n<!-- ================================================ -->\n<!-- Split from/to templates (\u0111\u01B0a xu\u1ED1ng cu\u1ED1i file) -->\n<!-- ================================================ -->\n<ng-template #splitNumberTpl let-field=\"field\" let-disabled=\"disabled\">\n <div class=\"d-flex align-items-center\" style=\"flex: 1 1 0%; min-width: 0\">\n <sd-input-number\n [autoId]=\"_autoId + '-from'\"\n style=\"flex: 1 1 0%; min-width: 0\"\n size=\"sm\"\n [label]=\"_label\"\n [(model)]=\"_columnFilter[field].from\"\n (sdChange)=\"onFilterChange()\"\n (sdBlur)=\"onFilterCommit()\"\n (cleared)=\"onFilterChange()\"\n [disabled]=\"disabled\"\n hideInlineError>\n </sd-input-number>\n <div class=\"mx-4\">-</div>\n <sd-input-number\n [autoId]=\"_autoId + '-to'\"\n style=\"flex: 1 1 0%; min-width: 0\"\n size=\"sm\"\n [(model)]=\"_columnFilter[field].to\"\n (sdChange)=\"onFilterChange()\"\n (sdBlur)=\"onFilterCommit()\"\n (cleared)=\"onFilterChange()\"\n [disabled]=\"disabled\"\n hideInlineError>\n </sd-input-number>\n </div>\n</ng-template>\n\n<ng-template #splitDateTpl let-field=\"field\" let-disabled=\"disabled\">\n <div class=\"d-flex align-items-center\" style=\"flex: 1 1 0%; min-width: 0\">\n <sd-date\n [autoId]=\"_autoId + '-from'\"\n style=\"flex: 1 1 0%; min-width: 0\"\n type=\"date\"\n size=\"sm\"\n [label]=\"_label\"\n [(model)]=\"_columnFilter[field].from\"\n (sdChange)=\"onFilterChange()\"\n [disabled]=\"disabled\"\n hideInlineError>\n </sd-date>\n <div class=\"mx-4\">-</div>\n <sd-date\n [autoId]=\"_autoId + '-to'\"\n style=\"flex: 1 1 0%; min-width: 0\"\n type=\"date\"\n size=\"sm\"\n [(model)]=\"_columnFilter[field].to\"\n (sdChange)=\"onFilterChange()\"\n [disabled]=\"disabled\"\n hideInlineError>\n </sd-date>\n </div>\n</ng-template>\n", styles: [":host ::ng-deep .c-inline-column .mat-mdc-text-field-wrapper{background-color:#fff}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: SdOperator, selector: "sd-operator", inputs: ["model", "operators", "disabled", "autoId"], outputs: ["modelChange"] }, { kind: "component", type: SdInput, selector: "sd-input", inputs: ["autoId", "name", "appearance", "floatLabel", "size", "form", "label", "helperText", "placeholder", "type", "hideInlineError", "blurOnEnter", "required", "readonly", "disabled", "viewed", "minlength", "maxlength", "pattern", "patternErrorMessage", "validator", "inlineError", "hyperlink", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }, { kind: "component", type: SdInputNumber, selector: "sd-input-number", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "blurOnEnter", "required", "readonly", "disabled", "viewed", "type", "precision", "format", "min", "max", "validator", "inlineError", "hyperlink", "appearance", "floatLabel", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }, { kind: "component", type: SdSelect, selector: "sd-select", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "valueField", "displayField", "disabledField", "cacheChecksum", "limit", "hyperlink", "minWidthPanel", "hideInlineError", "required", "disabled", "viewed", "multiple", "clearable", "validator", "inlineError", "appearance", "floatLabel", "items", "model"], outputs: ["modelChange", "sdChange", "sdSelection"] }, { kind: "component", type: SdDate, selector: "sd-date", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "clearable", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }, { kind: "component", type: SdDateRange, selector: "sd-date-range", inputs: ["autoId", "name", "size", "form", "label", "helperText", "hideInlineError", "required", "disabled", "viewed", "clearable", "appearance", "floatLabel", "min", "max", "model"], outputs: ["modelChange", "sdChange"] }, { kind: "component", type: SdBadge, selector: "sd-badge", inputs: ["type", "color", "primary", "secondary", "success", "info", "warning", "error", "fontSet", "title", "description", "tooltip", "icon", "size"], outputs: ["click"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
516
536
  }
@@ -766,8 +786,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
766
786
  args: [{ name: 'sdTree' }]
767
787
  }] });
768
788
 
769
- /* eslint-disable @typescript-eslint/no-explicit-any */
770
- // eslint-disable-next-line @angular-eslint/component-class-suffix
771
789
  class ExternalFilterComponent {
772
790
  // ==========================================
773
791
  // 1. SIGNAL INPUTS
@@ -915,7 +933,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
915
933
  ], template: "@let _filter = filter();\n@let _autoId = autoId();\n@let _filterItems = filterItems();\n@let _externalFilter = externalFilter();\n@let _inlineExternal = inlineExternal();\n@let _filtered = filtered();\n@let _col = col();\n\n@if (!_filter?.disabled) {\n @if (!isMobileOrTablet && _filterItems.length) {\n <sd-section\n [hideHeader]=\"_filter?.hideExternalFilterToolbar\"\n icon=\"filter_alt\"\n iconColor=\"secondary\"\n [title]=\"'core.component.table.filter' | translate\"\n [collapsible]=\"_filter?.collapsible ?? _filter?.collapsable ?? true\">\n <div class=\"d-flex mr-4\" sdHeaderRight>\n <sd-button\n [autoId]=\"_autoId + 'clear'\"\n [disabled]=\"!_filtered\"\n type=\"link\"\n prefixIcon=\"cleaning_services\"\n [tooltip]=\"'core.component.table.clear-selection' | translate\"\n (click)=\"$event.stopPropagation(); clearFilter($event)\"></sd-button>\n <sd-button\n [autoId]=\"_autoId + 'setting'\"\n type=\"link\"\n prefixIcon=\"settings\"\n [tooltip]=\"'core.component.table.customize' | translate\"\n [matMenuTriggerFor]=\"menu\"\n (click)=\"$event.stopPropagation()\"></sd-button>\n <mat-menu #menu=\"matMenu\" class=\"sd-custom-panel-filter-configuration\" [attr.data-autoid]=\"_autoId + 'setting-menu'\">\n @for (externalFilter of _filterItems; track externalFilter.field) {\n <button\n [attr.data-autoid]=\"_autoId + 'setting-' + externalFilter.field\"\n mat-menu-item\n (click)=\"onCheckboxChange($event, externalFilter)\"\n [disabled]=\"externalFilter.required\">\n <sd-checkbox\n [autoId]=\"_autoId + 'setting-' + externalFilter.field + '-checkbox'\"\n [label]=\"externalFilter.title\"\n [model]=\"_inlineExternal[externalFilter.field]\"\n [disabled]=\"externalFilter.required\" />\n </button>\n }\n </mat-menu>\n </div>\n <div class=\"row mx-0\" style=\"row-gap: 16px\">\n @for (item of _filterItems; track item.field) {\n @if (_inlineExternal[item.field]) {\n <div class=\"col-md-3 col-sm-6 px-8\" [class.col-lg-2]=\"_col === 2\" [class.col-lg-3]=\"_col === 3\">\n @if (item.type === 'string') {\n <sd-input\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n type=\"text\"\n [(model)]=\"_externalFilter[item.field]\"\n (keyupEnter)=\"onKeyupEnter()\"\n appearance=\"outline\"\n [form]=\"form\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-input>\n } @else if (item.type === 'number') {\n <sd-input-number\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [(model)]=\"_externalFilter[item.field]\"\n (keyupEnter)=\"onKeyupEnter()\"\n appearance=\"outline\"\n [form]=\"form\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-input-number>\n } @else if (item.type === 'boolean') {\n <sd-select\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [items]=\"[\n { value: true, display: item.option?.displayOnTrue || 'True' },\n { value: false, display: item.option?.displayOnFalse || 'False' },\n ]\"\n valueField=\"value\"\n displayField=\"display\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n appearance=\"outline\"\n [form]=\"form\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-select>\n } @else if (item.type === 'date') {\n <sd-date\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n appearance=\"outline\"\n [form]=\"form\"\n [required]=\"item.required\"\n [min]=\"item.minDate\"\n [max]=\"item.maxDate\"\n hideInlineError>\n </sd-date>\n } @else if (item.type === 'datetime') {\n <sd-datetime\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n appearance=\"outline\"\n [form]=\"form\"\n [required]=\"item.required\"\n [min]=\"item.minDate\"\n [max]=\"item.maxDate\"\n hideInlineError>\n </sd-datetime>\n } @else if (item.type === 'daterange') {\n <sd-date-range\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n [form]=\"form\"\n [required]=\"item.required\"\n [min]=\"item.minDate\"\n [max]=\"item.maxDate\"\n appearance=\"outline\"\n hideInlineError>\n </sd-date-range>\n } @else if (item.type === 'values') {\n @if (item.option.selection === 'MULTIPLE') {\n <sd-select\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [items]=\"item.option.items | filterValues | async\"\n [valueField]=\"item.option.valueField\"\n [displayField]=\"item.option.displayField\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n appearance=\"outline\"\n multiple\n [form]=\"form\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-select>\n } @else if (item.option.selection === 'AUTOCOMPLETE') {\n <sd-select\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [items]=\"item.option.items | filterValues | async\"\n [valueField]=\"item.option.valueField\"\n [displayField]=\"item.option.displayField\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n appearance=\"outline\"\n [form]=\"form\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-select>\n } @else if (item.option.selection === 'MULTIPLEAUTOCOMPLETE') {\n <sd-select\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [items]=\"item.option.items | filterValues | async\"\n [valueField]=\"item.option.valueField\"\n [displayField]=\"item.option.displayField\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n appearance=\"outline\"\n multiple\n [form]=\"form\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-select>\n } @else {\n <sd-select\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [items]=\"item.option.items | filterValues | async\"\n [valueField]=\"item.option.valueField\"\n [displayField]=\"item.option.displayField\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n appearance=\"outline\"\n [form]=\"form\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-select>\n }\n } @else if (item.type === 'lazy-values') {\n @if (item.option.selection === 'MULTIPLE') {\n <sd-select\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [items]=\"item.option.items\"\n [valueField]=\"item.option.valueField\"\n [displayField]=\"item.option.displayField\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n appearance=\"outline\"\n multiple\n [form]=\"form\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-select>\n } @else {\n <sd-select\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [items]=\"item.option.items\"\n [valueField]=\"item.option.valueField\"\n [displayField]=\"item.option.displayField\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n appearance=\"outline\"\n [form]=\"form\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-select>\n }\n } @else if (item.type === 'custom' && item.filterDef) {\n <ng-container\n *ngTemplateOutlet=\"\n item.filterDef;\n context: { form: form, externalFilter: _externalFilter, autoId: _autoId + item.field }\n \">\n </ng-container>\n }\n </div>\n }\n }\n </div>\n @if (_filter?.manualFilter) {\n <div class=\"d-flex justify-content-end mt-16 px-8\">\n <sd-button\n [autoId]=\"_autoId + 'submit'\"\n [disabled]=\"form.invalid\"\n type=\"fill\"\n color=\"primary\"\n [title]=\"'core.component.table.search' | translate\"\n prefixIcon=\"filter_alt\"\n (click)=\"onSubmit()\">\n </sd-button>\n </div>\n }\n </sd-section>\n }\n}\n", styles: [":host{display:block;padding-left:0;padding-right:0}::ng-deep .sd-custom-panel-filter-configuration{width:240px;max-height:550px;overflow-y:auto}\n"] }]
916
934
  }], ctorParameters: () => [], propDecorators: { autoIdInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoId", required: false }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }], externalFilters: [{ type: i0.Input, args: [{ isSignal: true, alias: "externalFilters", required: false }] }], filterRegister: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterRegister", required: false }] }] } });
917
935
 
918
- /* eslint-disable @typescript-eslint/no-explicit-any */
919
936
  /**
920
937
  * Mobile-filter — side-drawer chiếm ~90% width thiết bị. Dùng cho table khi
921
938
  * render trên màn hình điện thoại (inline filter bị ẩn).
@@ -1133,8 +1150,6 @@ const DEFAULT_TABLE_CONFIG = {
1133
1150
  const SD_TABLE_CONFIGURATION = new InjectionToken('sd-table.configuration');
1134
1151
 
1135
1152
  class ConfigService {
1136
- storageService;
1137
- tableConfiguration;
1138
1153
  #COLUMNS = {
1139
1154
  SUBINFORMATION: 'sdSubInformationAction',
1140
1155
  COMMAND: 'sdCommand',
@@ -1148,10 +1163,8 @@ class ConfigService {
1148
1163
  #storage;
1149
1164
  #widthChange = new Subject();
1150
1165
  widthChange$ = this.#widthChange.asObservable();
1151
- constructor(storageService, tableConfiguration) {
1152
- this.storageService = storageService;
1153
- this.tableConfiguration = tableConfiguration;
1154
- }
1166
+ storageService = inject(SdStorageService);
1167
+ tableConfiguration = inject(SD_TABLE_CONFIGURATION, { optional: true });
1155
1168
  #loadConfiguredTable = (option) => {
1156
1169
  // Nếu không có key thì không lấy được setting
1157
1170
  if (!option?.key) {
@@ -1340,20 +1353,13 @@ class ConfigService {
1340
1353
  columns,
1341
1354
  };
1342
1355
  };
1343
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ConfigService, deps: [{ token: i1$1.SdStorageService }, { token: SD_TABLE_CONFIGURATION, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
1356
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ConfigService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1344
1357
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ConfigService });
1345
1358
  }
1346
1359
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ConfigService, decorators: [{
1347
1360
  type: Injectable
1348
- }], ctorParameters: () => [{ type: i1$1.SdStorageService }, { type: undefined, decorators: [{
1349
- type: Inject,
1350
- args: [SD_TABLE_CONFIGURATION]
1351
- }, {
1352
- type: Optional
1353
- }] }] });
1361
+ }] });
1354
1362
 
1355
- /* eslint-disable @typescript-eslint/no-explicit-any */
1356
- // eslint-disable-next-line @angular-eslint/component-class-suffix
1357
1363
  class ConfigComponent {
1358
1364
  // ==========================================
1359
1365
  // 1. SIGNAL INPUTS / OUTPUTS
@@ -1580,18 +1586,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
1580
1586
  }], ctorParameters: () => [], propDecorators: { contentContainer: [{ type: i0.ViewChild, args: ['contentContainer', { isSignal: true }] }], column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], item: [{ type: i0.Input, args: [{ isSignal: true, alias: "item", required: true }] }], cellDef: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellDef", required: true }] }] } });
1581
1587
 
1582
1588
  /**
1583
- * Pipe gộp resolve metadata của 1 command theo row hiện tại return 1 object
1584
- * chứa `disabled`, `title`, `icon`, và `htmlTemplate` (khi command là child có
1585
- * htmlTemplate). Một lần gọi cho cả 4 truy vấn, template dùng `@let` để alias.
1586
- *
1587
- * Trước đây tách thành 3 pipe `commandDisable` / `commandTitle` / `commandIcon`.
1588
- *
1589
- * @example
1590
- * @let meta = item | command:cmd;
1591
- * <button [disabled]="meta.disabled" [matTooltip]="meta.title">
1592
- * <mat-icon>{{ meta.icon }}</mat-icon>
1593
- * <div [innerHTML]="meta.htmlTemplate"></div>
1594
- * </button>
1589
+ * Resolves command metadata for the current row once, then lets the template reuse it with `@let`.
1595
1590
  */
1596
1591
  class CommandPipe {
1597
1592
  transform(item, command) {
@@ -1618,8 +1613,7 @@ class CommandPipe {
1618
1613
  return '';
1619
1614
  return typeof command.icon === 'string' ? command.icon : command.icon(item.data);
1620
1615
  };
1621
- // Chỉ child command (SdTableCommandNormal) mới htmlTemplate.
1622
- // Parent (SdTableCommandChildren) không khai báo — trả về undefined.
1616
+ // Only child commands can provide htmlTemplate.
1623
1617
  #htmlTemplate = (item, command) => {
1624
1618
  const tpl = command?.htmlTemplate;
1625
1619
  return typeof tpl === 'function' ? tpl(item.data) : undefined;
@@ -1629,18 +1623,11 @@ class CommandPipe {
1629
1623
  }
1630
1624
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: CommandPipe, decorators: [{
1631
1625
  type: Pipe,
1632
- args: [{ name: 'command' }]
1626
+ args: [{ name: 'command', standalone: true }]
1633
1627
  }] });
1634
1628
 
1635
1629
  /**
1636
- * Lọc danh sách commands theo `hidden` (sync / async) + pre-resolve `icon`, `title`, `key`
1637
- * cho từng row. Output dùng trực tiếp trong template không cần thêm pipe phụ.
1638
- *
1639
- * Đã rename từ `commandFilter` → `filter` sau khi gom toàn bộ pipe command vào
1640
- * `components/command/pipes/`.
1641
- *
1642
- * @example
1643
- * @let filteredCommands = item | filter:commands | async;
1630
+ * Filters visible commands and pre-resolves icon, title, and stable key for the current row.
1644
1631
  */
1645
1632
  class CommandFilterPipe {
1646
1633
  async transform(item, commands) {
@@ -1707,7 +1694,7 @@ class CommandFilterPipe {
1707
1694
  return '';
1708
1695
  return typeof command.title === 'string' ? command.title : command.title(item.data);
1709
1696
  };
1710
- // Key dùng để gán cho autoId cần stable theo (icon, title).
1697
+ // Stable autoId key generated from resolved icon and title.
1711
1698
  #key = (item, command) => {
1712
1699
  const icon = this.#icon(item, command);
1713
1700
  const title = this.#title(item, command);
@@ -1718,46 +1705,36 @@ class CommandFilterPipe {
1718
1705
  }
1719
1706
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: CommandFilterPipe, decorators: [{
1720
1707
  type: Pipe,
1721
- args: [{ name: 'filter' }]
1708
+ args: [{ name: 'filter', standalone: true }]
1722
1709
  }] });
1723
1710
 
1724
- // eslint-disable-next-line @angular-eslint/component-class-suffix
1725
1711
  class DesktopCommand {
1726
- // ==========================================
1727
- // 1. SIGNAL INPUTS
1728
- // ==========================================
1712
+ defaultIconFontSet = DefaultMaterialIconFontSet;
1729
1713
  autoIdInput = input(undefined, { ...(ngDevMode ? { debugName: "autoIdInput" } : /* istanbul ignore next */ {}), alias: 'autoId' });
1730
1714
  item = input.required(...(ngDevMode ? [{ debugName: "item" }] : /* istanbul ignore next */ []));
1731
1715
  itemIndex = input.required(...(ngDevMode ? [{ debugName: "itemIndex" }] : /* istanbul ignore next */ []));
1732
1716
  commands = input([], ...(ngDevMode ? [{ debugName: "commands" }] : /* istanbul ignore next */ []));
1733
- // ==========================================
1734
- // 2. COMPUTED
1735
- // ==========================================
1736
- // Key định danh row dùng để build autoId: ưu tiên id → code → value → fallback itemIndex.
1717
+ // Row key for autoId: prefer id, then code, then value, then itemIndex.
1737
1718
  itemKey = computed(() => {
1738
1719
  const data = this.item()?.data;
1739
1720
  return data?.['id']?.toString() || data?.['code']?.toString() || data?.['value']?.toString() || this.itemIndex().toString();
1740
1721
  }, ...(ngDevMode ? [{ debugName: "itemKey" }] : /* istanbul ignore next */ []));
1741
- // autoId của host: `<base>-command-<itemKey>` đã unique theo row.
1742
- // Template ghép tiếp `-<commandKey>` (và `-<childKey>` nếu child) cho mỗi button.
1722
+ // Host autoId: `<base>-command-<itemKey>`. Template appends command and child keys.
1743
1723
  autoId = computed(() => {
1744
1724
  const base = this.autoIdInput();
1745
1725
  return base ? `${base}-command-${this.itemKey()}` : '';
1746
1726
  }, ...(ngDevMode ? [{ debugName: "autoId" }] : /* istanbul ignore next */ []));
1747
- // Commands signal (mặc định mảng rỗng).
1748
1727
  _commands = computed(() => this.commands() || [], ...(ngDevMode ? [{ debugName: "_commands" }] : /* istanbul ignore next */ []));
1749
1728
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DesktopCommand, deps: [], target: i0.ɵɵFactoryTarget.Component });
1750
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: DesktopCommand, isStandalone: true, selector: "desktop-command", inputs: { autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: true, transformFunction: null }, itemIndex: { classPropertyName: "itemIndex", publicName: "itemIndex", isSignal: true, isRequired: true, transformFunction: null }, commands: { classPropertyName: "commands", publicName: "commands", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-autoid": "autoId()" } }, ngImport: i0, template: "@let _autoId = autoId();\n@let _item = item();\n@let filteredCommands = _item | filter: _commands() | async;\n\n@if (filteredCommands?.length) {\n @for (command of filteredCommands; track command.key) {\n @let _meta = _item | command: command;\n @if (command.type === 'normal') {\n <button\n [attr.data-autoid]=\"_autoId + '-' + command.key\"\n (click)=\"command.click(_item.data)\"\n [matTooltip]=\"_meta.title\"\n aria-hidden=\"true\"\n [disabled]=\"_meta.disabled\"\n mat-icon-button>\n <mat-icon class=\"c-icon\" [color]=\"command.color || 'secondary'\" [fontSet]=\"command.fontSet!\">\n {{ _meta.icon }}\n </mat-icon>\n </button>\n } @else {\n <button\n [attr.data-autoid]=\"_autoId + '-' + command.key\"\n [matMenuTriggerFor]=\"menu\"\n aria-hidden=\"true\"\n mat-icon-button>\n @if (_meta.icon) {\n <mat-icon class=\"c-icon\" [fontSet]=\"command.fontSet!\">{{ _meta.icon }}</mat-icon>\n } @else {\n <mat-icon class=\"c-icon\">more_vert</mat-icon>\n }\n </button>\n <mat-menu #menu=\"matMenu\" [attr.data-autoid]=\"_autoId + '-' + command.key + '-menu'\">\n <span>\n @for (childCommand of command.children; track childCommand.key) {\n @let _childMeta = _item | command: childCommand;\n <button\n [attr.data-autoid]=\"_autoId + '-' + command.key + '-' + childCommand.key\"\n mat-menu-item\n (click)=\"childCommand.click(_item.data)\"\n [disabled]=\"_childMeta.disabled\">\n @if (_childMeta.htmlTemplate) {\n <div [innerHTML]=\"_childMeta.htmlTemplate\"></div>\n } @else {\n <div>\n <mat-icon [color]=\"childCommand.color || 'secondary'\" [fontSet]=\"command.fontSet!\">\n {{ _childMeta.icon }}\n </mat-icon>\n <span> {{ _childMeta.title }}</span>\n </div>\n }\n </button>\n }\n </span>\n </mat-menu>\n }\n }\n}\n", styles: [".text-primary{color:var(--sd-primary)!important}.bg-primary{background:var(--sd-primary)!important}.border-primary{border-color:var(--sd-primary)!important}.text-primary-light{color:var(--sd-primary-light)!important}.bg-primary-light{background:var(--sd-primary-light)!important}.border-primary-light{border-color:var(--sd-primary-light)!important}.text-primary-dark{color:var(--sd-primary-dark)!important}.bg-primary-dark{background:var(--sd-primary-dark)!important}.border-primary-dark{border-color:var(--sd-primary-dark)!important}.text-info{color:var(--sd-info)!important}.bg-info{background:var(--sd-info)!important}.border-info{border-color:var(--sd-info)!important}.text-info-light{color:var(--sd-info-light)!important}.bg-info-light{background:var(--sd-info-light)!important}.border-info-light{border-color:var(--sd-info-light)!important}.text-info-dark{color:var(--sd-info-dark)!important}.bg-info-dark{background:var(--sd-info-dark)!important}.border-info-dark{border-color:var(--sd-info-dark)!important}.text-success{color:var(--sd-success)!important}.bg-success{background:var(--sd-success)!important}.border-success{border-color:var(--sd-success)!important}.text-success-light{color:var(--sd-success-light)!important}.bg-success-light{background:var(--sd-success-light)!important}.border-success-light{border-color:var(--sd-success-light)!important}.text-success-dark{color:var(--sd-success-dark)!important}.bg-success-dark{background:var(--sd-success-dark)!important}.border-success-dark{border-color:var(--sd-success-dark)!important}.text-warning{color:var(--sd-warning)!important}.bg-warning{background:var(--sd-warning)!important}.border-warning{border-color:var(--sd-warning)!important}.text-warning-light{color:var(--sd-warning-light)!important}.bg-warning-light{background:var(--sd-warning-light)!important}.border-warning-light{border-color:var(--sd-warning-light)!important}.text-warning-dark{color:var(--sd-warning-dark)!important}.bg-warning-dark{background:var(--sd-warning-dark)!important}.border-warning-dark{border-color:var(--sd-warning-dark)!important}.text-error{color:var(--sd-error)!important}.bg-error{background:var(--sd-error)!important}.border-error{border-color:var(--sd-error)!important}.text-error-light{color:var(--sd-error-light)!important}.bg-error-light{background:var(--sd-error-light)!important}.border-error-light{border-color:var(--sd-error-light)!important}.text-error-dark{color:var(--sd-error-dark)!important}.bg-error-dark{background:var(--sd-error-dark)!important}.border-error-dark{border-color:var(--sd-error-dark)!important}.text-secondary{color:var(--sd-secondary)!important}.bg-secondary{background:var(--sd-secondary)!important}.border-secondary{border-color:var(--sd-secondary)!important}.text-secondary-light{color:var(--sd-secondary-light)!important}.bg-secondary-light{background:var(--sd-secondary-light)!important}.border-secondary-light{border-color:var(--sd-secondary-light)!important}.text-secondary-dark{color:var(--sd-secondary-dark)!important}.bg-secondary-dark{background:var(--sd-secondary-dark)!important}.border-secondary-dark{border-color:var(--sd-secondary-dark)!important}.text-light{color:var(--sd-light)!important}.bg-light{background:var(--sd-light)!important}.border-light{border-color:var(--sd-light)!important}.text-dark{color:var(--sd-dark)!important}.bg-dark{background:var(--sd-dark)!important}.border-dark{border-color:var(--sd-dark)!important}.text-black500{color:var(--sd-black500)!important}.bg-black500{background:var(--sd-black500)!important}.border-black500{border-color:var(--sd-black500)!important}.text-black400{color:var(--sd-black400)!important}.bg-black400{background:var(--sd-black400)!important}.border-black400{border-color:var(--sd-black400)!important}.text-black300{color:var(--sd-black300)!important}.bg-black300{background:var(--sd-black300)!important}.border-black300{border-color:var(--sd-black300)!important}.text-black200{color:var(--sd-black200)!important}.bg-black200{background:var(--sd-black200)!important}.border-black200{border-color:var(--sd-black200)!important}.text-black100{color:var(--sd-black100)!important}.bg-black100{background:var(--sd-black100)!important}.border-black100{border-color:var(--sd-black100)!important}.text-white{color:#fff!important}.bg-white{background:#fff!important}.border-white{border-color:#fff!important}.text-black{color:#000!important}.bg-black{background:#000!important}.border-black{border-color:#000!important}:host{display:flex;align-items:center}button[mat-icon-button]{width:24px;height:24px;line-height:24px;padding:0}mat-icon.c-icon{color:var(--sd-black400)!important;width:16px;height:16px;font-size:16px;line-height:16px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i1$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i2.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i2.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i2.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: CommandPipe, name: "command" }, { kind: "pipe", type: CommandFilterPipe, name: "filter" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1729
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: DesktopCommand, isStandalone: true, selector: "desktop-command", inputs: { autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: true, transformFunction: null }, itemIndex: { classPropertyName: "itemIndex", publicName: "itemIndex", isSignal: true, isRequired: true, transformFunction: null }, commands: { classPropertyName: "commands", publicName: "commands", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-autoid": "autoId()" } }, ngImport: i0, template: "@let _autoId = autoId();\n@let _item = item();\n@let filteredCommands = _item | filter: _commands() | async;\n\n@if (filteredCommands?.length) {\n @for (command of filteredCommands; track command.key) {\n @let _meta = _item | command: command;\n @if (command.type === 'normal') {\n <button\n [attr.data-autoid]=\"_autoId + '-' + command.key\"\n (click)=\"command.click(_item.data)\"\n [matTooltip]=\"_meta.title\"\n aria-hidden=\"true\"\n [disabled]=\"_meta.disabled\"\n mat-icon-button>\n <mat-icon class=\"c-icon\" [color]=\"command.color || 'secondary'\" [fontSet]=\"command.fontSet || defaultIconFontSet\">\n {{ _meta.icon }}\n </mat-icon>\n </button>\n } @else {\n <button [attr.data-autoid]=\"_autoId + '-' + command.key\" [matMenuTriggerFor]=\"menu\" aria-hidden=\"true\" mat-icon-button>\n @if (_meta.icon) {\n <mat-icon class=\"c-icon\" [fontSet]=\"command.fontSet || defaultIconFontSet\">{{ _meta.icon }}</mat-icon>\n } @else {\n <mat-icon class=\"c-icon\" [fontSet]=\"command.fontSet || defaultIconFontSet\">more_vert</mat-icon>\n }\n </button>\n <mat-menu #menu=\"matMenu\" [attr.data-autoid]=\"_autoId + '-' + command.key + '-menu'\">\n <span>\n @for (childCommand of command.children; track childCommand.key) {\n @let _childMeta = _item | command: childCommand;\n <button\n [attr.data-autoid]=\"_autoId + '-' + command.key + '-' + childCommand.key\"\n mat-menu-item\n (click)=\"childCommand.click(_item.data)\"\n [disabled]=\"_childMeta.disabled\">\n @if (_childMeta.htmlTemplate) {\n <div [innerHTML]=\"_childMeta.htmlTemplate\"></div>\n } @else {\n <span class=\"sd-command-menu-item__content\">\n <mat-icon\n class=\"sd-command-menu-item__icon\"\n [color]=\"childCommand.color || 'secondary'\"\n [fontSet]=\"childCommand.fontSet || defaultIconFontSet\">\n {{ _childMeta.icon }}\n </mat-icon>\n <span class=\"sd-command-menu-item__title\">{{ _childMeta.title }}</span>\n </span>\n }\n </button>\n }\n </span>\n </mat-menu>\n }\n }\n}\n", styles: [".text-primary{color:var(--sd-primary)!important}.bg-primary{background:var(--sd-primary)!important}.border-primary{border-color:var(--sd-primary)!important}.text-primary-light{color:var(--sd-primary-light)!important}.bg-primary-light{background:var(--sd-primary-light)!important}.border-primary-light{border-color:var(--sd-primary-light)!important}.text-primary-dark{color:var(--sd-primary-dark)!important}.bg-primary-dark{background:var(--sd-primary-dark)!important}.border-primary-dark{border-color:var(--sd-primary-dark)!important}.text-info{color:var(--sd-info)!important}.bg-info{background:var(--sd-info)!important}.border-info{border-color:var(--sd-info)!important}.text-info-light{color:var(--sd-info-light)!important}.bg-info-light{background:var(--sd-info-light)!important}.border-info-light{border-color:var(--sd-info-light)!important}.text-info-dark{color:var(--sd-info-dark)!important}.bg-info-dark{background:var(--sd-info-dark)!important}.border-info-dark{border-color:var(--sd-info-dark)!important}.text-success{color:var(--sd-success)!important}.bg-success{background:var(--sd-success)!important}.border-success{border-color:var(--sd-success)!important}.text-success-light{color:var(--sd-success-light)!important}.bg-success-light{background:var(--sd-success-light)!important}.border-success-light{border-color:var(--sd-success-light)!important}.text-success-dark{color:var(--sd-success-dark)!important}.bg-success-dark{background:var(--sd-success-dark)!important}.border-success-dark{border-color:var(--sd-success-dark)!important}.text-warning{color:var(--sd-warning)!important}.bg-warning{background:var(--sd-warning)!important}.border-warning{border-color:var(--sd-warning)!important}.text-warning-light{color:var(--sd-warning-light)!important}.bg-warning-light{background:var(--sd-warning-light)!important}.border-warning-light{border-color:var(--sd-warning-light)!important}.text-warning-dark{color:var(--sd-warning-dark)!important}.bg-warning-dark{background:var(--sd-warning-dark)!important}.border-warning-dark{border-color:var(--sd-warning-dark)!important}.text-error{color:var(--sd-error)!important}.bg-error{background:var(--sd-error)!important}.border-error{border-color:var(--sd-error)!important}.text-error-light{color:var(--sd-error-light)!important}.bg-error-light{background:var(--sd-error-light)!important}.border-error-light{border-color:var(--sd-error-light)!important}.text-error-dark{color:var(--sd-error-dark)!important}.bg-error-dark{background:var(--sd-error-dark)!important}.border-error-dark{border-color:var(--sd-error-dark)!important}.text-secondary{color:var(--sd-secondary)!important}.bg-secondary{background:var(--sd-secondary)!important}.border-secondary{border-color:var(--sd-secondary)!important}.text-secondary-light{color:var(--sd-secondary-light)!important}.bg-secondary-light{background:var(--sd-secondary-light)!important}.border-secondary-light{border-color:var(--sd-secondary-light)!important}.text-secondary-dark{color:var(--sd-secondary-dark)!important}.bg-secondary-dark{background:var(--sd-secondary-dark)!important}.border-secondary-dark{border-color:var(--sd-secondary-dark)!important}.text-light{color:var(--sd-light)!important}.bg-light{background:var(--sd-light)!important}.border-light{border-color:var(--sd-light)!important}.text-dark{color:var(--sd-dark)!important}.bg-dark{background:var(--sd-dark)!important}.border-dark{border-color:var(--sd-dark)!important}.text-black500{color:var(--sd-black500)!important}.bg-black500{background:var(--sd-black500)!important}.border-black500{border-color:var(--sd-black500)!important}.text-black400{color:var(--sd-black400)!important}.bg-black400{background:var(--sd-black400)!important}.border-black400{border-color:var(--sd-black400)!important}.text-black300{color:var(--sd-black300)!important}.bg-black300{background:var(--sd-black300)!important}.border-black300{border-color:var(--sd-black300)!important}.text-black200{color:var(--sd-black200)!important}.bg-black200{background:var(--sd-black200)!important}.border-black200{border-color:var(--sd-black200)!important}.text-black100{color:var(--sd-black100)!important}.bg-black100{background:var(--sd-black100)!important}.border-black100{border-color:var(--sd-black100)!important}.text-white{color:#fff!important}.bg-white{background:#fff!important}.border-white{border-color:#fff!important}.text-black{color:#000!important}.bg-black{background:#000!important}.border-black{border-color:#000!important}:host{display:flex;align-items:center}button[mat-icon-button]{width:24px;height:24px;line-height:24px;padding:0}mat-icon.c-icon{color:var(--sd-black400)!important;width:16px;height:16px;font-size:16px;line-height:16px}.sd-command-menu-item__content{display:inline-flex;align-items:center;gap:8px;min-width:0;width:100%}.sd-command-menu-item__icon{color:var(--sd-black400)!important;width:24px;height:24px;font-size:18px;line-height:24px!important;margin-right:0!important;flex:0 0 24px}.sd-command-menu-item__title{min-width:0;line-height:20px;white-space:nowrap}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i1$1.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i2.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i2.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i2.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: CommandPipe, name: "command" }, { kind: "pipe", type: CommandFilterPipe, name: "filter" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1751
1730
  }
1752
1731
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DesktopCommand, decorators: [{
1753
1732
  type: Component,
1754
1733
  args: [{ selector: 'desktop-command', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, host: {
1755
1734
  '[attr.data-autoid]': 'autoId()',
1756
- }, imports: [CommonModule, MatTooltipModule, MatMenuModule, MatButtonModule, MatIconModule, CommandPipe, CommandFilterPipe], template: "@let _autoId = autoId();\n@let _item = item();\n@let filteredCommands = _item | filter: _commands() | async;\n\n@if (filteredCommands?.length) {\n @for (command of filteredCommands; track command.key) {\n @let _meta = _item | command: command;\n @if (command.type === 'normal') {\n <button\n [attr.data-autoid]=\"_autoId + '-' + command.key\"\n (click)=\"command.click(_item.data)\"\n [matTooltip]=\"_meta.title\"\n aria-hidden=\"true\"\n [disabled]=\"_meta.disabled\"\n mat-icon-button>\n <mat-icon class=\"c-icon\" [color]=\"command.color || 'secondary'\" [fontSet]=\"command.fontSet!\">\n {{ _meta.icon }}\n </mat-icon>\n </button>\n } @else {\n <button\n [attr.data-autoid]=\"_autoId + '-' + command.key\"\n [matMenuTriggerFor]=\"menu\"\n aria-hidden=\"true\"\n mat-icon-button>\n @if (_meta.icon) {\n <mat-icon class=\"c-icon\" [fontSet]=\"command.fontSet!\">{{ _meta.icon }}</mat-icon>\n } @else {\n <mat-icon class=\"c-icon\">more_vert</mat-icon>\n }\n </button>\n <mat-menu #menu=\"matMenu\" [attr.data-autoid]=\"_autoId + '-' + command.key + '-menu'\">\n <span>\n @for (childCommand of command.children; track childCommand.key) {\n @let _childMeta = _item | command: childCommand;\n <button\n [attr.data-autoid]=\"_autoId + '-' + command.key + '-' + childCommand.key\"\n mat-menu-item\n (click)=\"childCommand.click(_item.data)\"\n [disabled]=\"_childMeta.disabled\">\n @if (_childMeta.htmlTemplate) {\n <div [innerHTML]=\"_childMeta.htmlTemplate\"></div>\n } @else {\n <div>\n <mat-icon [color]=\"childCommand.color || 'secondary'\" [fontSet]=\"command.fontSet!\">\n {{ _childMeta.icon }}\n </mat-icon>\n <span> {{ _childMeta.title }}</span>\n </div>\n }\n </button>\n }\n </span>\n </mat-menu>\n }\n }\n}\n", styles: [".text-primary{color:var(--sd-primary)!important}.bg-primary{background:var(--sd-primary)!important}.border-primary{border-color:var(--sd-primary)!important}.text-primary-light{color:var(--sd-primary-light)!important}.bg-primary-light{background:var(--sd-primary-light)!important}.border-primary-light{border-color:var(--sd-primary-light)!important}.text-primary-dark{color:var(--sd-primary-dark)!important}.bg-primary-dark{background:var(--sd-primary-dark)!important}.border-primary-dark{border-color:var(--sd-primary-dark)!important}.text-info{color:var(--sd-info)!important}.bg-info{background:var(--sd-info)!important}.border-info{border-color:var(--sd-info)!important}.text-info-light{color:var(--sd-info-light)!important}.bg-info-light{background:var(--sd-info-light)!important}.border-info-light{border-color:var(--sd-info-light)!important}.text-info-dark{color:var(--sd-info-dark)!important}.bg-info-dark{background:var(--sd-info-dark)!important}.border-info-dark{border-color:var(--sd-info-dark)!important}.text-success{color:var(--sd-success)!important}.bg-success{background:var(--sd-success)!important}.border-success{border-color:var(--sd-success)!important}.text-success-light{color:var(--sd-success-light)!important}.bg-success-light{background:var(--sd-success-light)!important}.border-success-light{border-color:var(--sd-success-light)!important}.text-success-dark{color:var(--sd-success-dark)!important}.bg-success-dark{background:var(--sd-success-dark)!important}.border-success-dark{border-color:var(--sd-success-dark)!important}.text-warning{color:var(--sd-warning)!important}.bg-warning{background:var(--sd-warning)!important}.border-warning{border-color:var(--sd-warning)!important}.text-warning-light{color:var(--sd-warning-light)!important}.bg-warning-light{background:var(--sd-warning-light)!important}.border-warning-light{border-color:var(--sd-warning-light)!important}.text-warning-dark{color:var(--sd-warning-dark)!important}.bg-warning-dark{background:var(--sd-warning-dark)!important}.border-warning-dark{border-color:var(--sd-warning-dark)!important}.text-error{color:var(--sd-error)!important}.bg-error{background:var(--sd-error)!important}.border-error{border-color:var(--sd-error)!important}.text-error-light{color:var(--sd-error-light)!important}.bg-error-light{background:var(--sd-error-light)!important}.border-error-light{border-color:var(--sd-error-light)!important}.text-error-dark{color:var(--sd-error-dark)!important}.bg-error-dark{background:var(--sd-error-dark)!important}.border-error-dark{border-color:var(--sd-error-dark)!important}.text-secondary{color:var(--sd-secondary)!important}.bg-secondary{background:var(--sd-secondary)!important}.border-secondary{border-color:var(--sd-secondary)!important}.text-secondary-light{color:var(--sd-secondary-light)!important}.bg-secondary-light{background:var(--sd-secondary-light)!important}.border-secondary-light{border-color:var(--sd-secondary-light)!important}.text-secondary-dark{color:var(--sd-secondary-dark)!important}.bg-secondary-dark{background:var(--sd-secondary-dark)!important}.border-secondary-dark{border-color:var(--sd-secondary-dark)!important}.text-light{color:var(--sd-light)!important}.bg-light{background:var(--sd-light)!important}.border-light{border-color:var(--sd-light)!important}.text-dark{color:var(--sd-dark)!important}.bg-dark{background:var(--sd-dark)!important}.border-dark{border-color:var(--sd-dark)!important}.text-black500{color:var(--sd-black500)!important}.bg-black500{background:var(--sd-black500)!important}.border-black500{border-color:var(--sd-black500)!important}.text-black400{color:var(--sd-black400)!important}.bg-black400{background:var(--sd-black400)!important}.border-black400{border-color:var(--sd-black400)!important}.text-black300{color:var(--sd-black300)!important}.bg-black300{background:var(--sd-black300)!important}.border-black300{border-color:var(--sd-black300)!important}.text-black200{color:var(--sd-black200)!important}.bg-black200{background:var(--sd-black200)!important}.border-black200{border-color:var(--sd-black200)!important}.text-black100{color:var(--sd-black100)!important}.bg-black100{background:var(--sd-black100)!important}.border-black100{border-color:var(--sd-black100)!important}.text-white{color:#fff!important}.bg-white{background:#fff!important}.border-white{border-color:#fff!important}.text-black{color:#000!important}.bg-black{background:#000!important}.border-black{border-color:#000!important}:host{display:flex;align-items:center}button[mat-icon-button]{width:24px;height:24px;line-height:24px;padding:0}mat-icon.c-icon{color:var(--sd-black400)!important;width:16px;height:16px;font-size:16px;line-height:16px}\n"] }]
1735
+ }, imports: [CommonModule, MatTooltipModule, MatMenuModule, MatButtonModule, MatIconModule, CommandPipe, CommandFilterPipe], template: "@let _autoId = autoId();\n@let _item = item();\n@let filteredCommands = _item | filter: _commands() | async;\n\n@if (filteredCommands?.length) {\n @for (command of filteredCommands; track command.key) {\n @let _meta = _item | command: command;\n @if (command.type === 'normal') {\n <button\n [attr.data-autoid]=\"_autoId + '-' + command.key\"\n (click)=\"command.click(_item.data)\"\n [matTooltip]=\"_meta.title\"\n aria-hidden=\"true\"\n [disabled]=\"_meta.disabled\"\n mat-icon-button>\n <mat-icon class=\"c-icon\" [color]=\"command.color || 'secondary'\" [fontSet]=\"command.fontSet || defaultIconFontSet\">\n {{ _meta.icon }}\n </mat-icon>\n </button>\n } @else {\n <button [attr.data-autoid]=\"_autoId + '-' + command.key\" [matMenuTriggerFor]=\"menu\" aria-hidden=\"true\" mat-icon-button>\n @if (_meta.icon) {\n <mat-icon class=\"c-icon\" [fontSet]=\"command.fontSet || defaultIconFontSet\">{{ _meta.icon }}</mat-icon>\n } @else {\n <mat-icon class=\"c-icon\" [fontSet]=\"command.fontSet || defaultIconFontSet\">more_vert</mat-icon>\n }\n </button>\n <mat-menu #menu=\"matMenu\" [attr.data-autoid]=\"_autoId + '-' + command.key + '-menu'\">\n <span>\n @for (childCommand of command.children; track childCommand.key) {\n @let _childMeta = _item | command: childCommand;\n <button\n [attr.data-autoid]=\"_autoId + '-' + command.key + '-' + childCommand.key\"\n mat-menu-item\n (click)=\"childCommand.click(_item.data)\"\n [disabled]=\"_childMeta.disabled\">\n @if (_childMeta.htmlTemplate) {\n <div [innerHTML]=\"_childMeta.htmlTemplate\"></div>\n } @else {\n <span class=\"sd-command-menu-item__content\">\n <mat-icon\n class=\"sd-command-menu-item__icon\"\n [color]=\"childCommand.color || 'secondary'\"\n [fontSet]=\"childCommand.fontSet || defaultIconFontSet\">\n {{ _childMeta.icon }}\n </mat-icon>\n <span class=\"sd-command-menu-item__title\">{{ _childMeta.title }}</span>\n </span>\n }\n </button>\n }\n </span>\n </mat-menu>\n }\n }\n}\n", styles: [".text-primary{color:var(--sd-primary)!important}.bg-primary{background:var(--sd-primary)!important}.border-primary{border-color:var(--sd-primary)!important}.text-primary-light{color:var(--sd-primary-light)!important}.bg-primary-light{background:var(--sd-primary-light)!important}.border-primary-light{border-color:var(--sd-primary-light)!important}.text-primary-dark{color:var(--sd-primary-dark)!important}.bg-primary-dark{background:var(--sd-primary-dark)!important}.border-primary-dark{border-color:var(--sd-primary-dark)!important}.text-info{color:var(--sd-info)!important}.bg-info{background:var(--sd-info)!important}.border-info{border-color:var(--sd-info)!important}.text-info-light{color:var(--sd-info-light)!important}.bg-info-light{background:var(--sd-info-light)!important}.border-info-light{border-color:var(--sd-info-light)!important}.text-info-dark{color:var(--sd-info-dark)!important}.bg-info-dark{background:var(--sd-info-dark)!important}.border-info-dark{border-color:var(--sd-info-dark)!important}.text-success{color:var(--sd-success)!important}.bg-success{background:var(--sd-success)!important}.border-success{border-color:var(--sd-success)!important}.text-success-light{color:var(--sd-success-light)!important}.bg-success-light{background:var(--sd-success-light)!important}.border-success-light{border-color:var(--sd-success-light)!important}.text-success-dark{color:var(--sd-success-dark)!important}.bg-success-dark{background:var(--sd-success-dark)!important}.border-success-dark{border-color:var(--sd-success-dark)!important}.text-warning{color:var(--sd-warning)!important}.bg-warning{background:var(--sd-warning)!important}.border-warning{border-color:var(--sd-warning)!important}.text-warning-light{color:var(--sd-warning-light)!important}.bg-warning-light{background:var(--sd-warning-light)!important}.border-warning-light{border-color:var(--sd-warning-light)!important}.text-warning-dark{color:var(--sd-warning-dark)!important}.bg-warning-dark{background:var(--sd-warning-dark)!important}.border-warning-dark{border-color:var(--sd-warning-dark)!important}.text-error{color:var(--sd-error)!important}.bg-error{background:var(--sd-error)!important}.border-error{border-color:var(--sd-error)!important}.text-error-light{color:var(--sd-error-light)!important}.bg-error-light{background:var(--sd-error-light)!important}.border-error-light{border-color:var(--sd-error-light)!important}.text-error-dark{color:var(--sd-error-dark)!important}.bg-error-dark{background:var(--sd-error-dark)!important}.border-error-dark{border-color:var(--sd-error-dark)!important}.text-secondary{color:var(--sd-secondary)!important}.bg-secondary{background:var(--sd-secondary)!important}.border-secondary{border-color:var(--sd-secondary)!important}.text-secondary-light{color:var(--sd-secondary-light)!important}.bg-secondary-light{background:var(--sd-secondary-light)!important}.border-secondary-light{border-color:var(--sd-secondary-light)!important}.text-secondary-dark{color:var(--sd-secondary-dark)!important}.bg-secondary-dark{background:var(--sd-secondary-dark)!important}.border-secondary-dark{border-color:var(--sd-secondary-dark)!important}.text-light{color:var(--sd-light)!important}.bg-light{background:var(--sd-light)!important}.border-light{border-color:var(--sd-light)!important}.text-dark{color:var(--sd-dark)!important}.bg-dark{background:var(--sd-dark)!important}.border-dark{border-color:var(--sd-dark)!important}.text-black500{color:var(--sd-black500)!important}.bg-black500{background:var(--sd-black500)!important}.border-black500{border-color:var(--sd-black500)!important}.text-black400{color:var(--sd-black400)!important}.bg-black400{background:var(--sd-black400)!important}.border-black400{border-color:var(--sd-black400)!important}.text-black300{color:var(--sd-black300)!important}.bg-black300{background:var(--sd-black300)!important}.border-black300{border-color:var(--sd-black300)!important}.text-black200{color:var(--sd-black200)!important}.bg-black200{background:var(--sd-black200)!important}.border-black200{border-color:var(--sd-black200)!important}.text-black100{color:var(--sd-black100)!important}.bg-black100{background:var(--sd-black100)!important}.border-black100{border-color:var(--sd-black100)!important}.text-white{color:#fff!important}.bg-white{background:#fff!important}.border-white{border-color:#fff!important}.text-black{color:#000!important}.bg-black{background:#000!important}.border-black{border-color:#000!important}:host{display:flex;align-items:center}button[mat-icon-button]{width:24px;height:24px;line-height:24px;padding:0}mat-icon.c-icon{color:var(--sd-black400)!important;width:16px;height:16px;font-size:16px;line-height:16px}.sd-command-menu-item__content{display:inline-flex;align-items:center;gap:8px;min-width:0;width:100%}.sd-command-menu-item__icon{color:var(--sd-black400)!important;width:24px;height:24px;font-size:18px;line-height:24px!important;margin-right:0!important;flex:0 0 24px}.sd-command-menu-item__title{min-width:0;line-height:20px;white-space:nowrap}\n"] }]
1757
1736
  }], propDecorators: { autoIdInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoId", required: false }] }], item: [{ type: i0.Input, args: [{ isSignal: true, alias: "item", required: true }] }], itemIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemIndex", required: true }] }], commands: [{ type: i0.Input, args: [{ isSignal: true, alias: "commands", required: false }] }] } });
1758
1737
 
1759
- /* eslint-disable @angular-eslint/no-input-rename */
1760
- // eslint-disable-next-line @angular-eslint/component-class-suffix
1761
1738
  class SdPopupExport {
1762
1739
  ref;
1763
1740
  loadingService;
@@ -1936,7 +1913,7 @@ class SdPopupExport {
1936
1913
  generateAndUploadTemplate = async (fileName) => {
1937
1914
  const sheets = [];
1938
1915
  if (Array.isArray(this.exportOption?.sheets)) {
1939
- for (const sheet of this.exportOption?.sheets) {
1916
+ for (const sheet of this.exportOption.sheets) {
1940
1917
  if (sheet.name && sheet.items && sheet.headers) {
1941
1918
  if (Array.isArray(sheet.items)) {
1942
1919
  sheets.push({
@@ -2038,13 +2015,13 @@ class SdPopupExport {
2038
2015
  }
2039
2016
  return '';
2040
2017
  };
2041
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdPopupExport, deps: [{ token: i0.ChangeDetectorRef }, { token: i1$1.SdLoadingService }, { token: i1$1.SdExcelService }, { token: i1$1.SdNotifyService }, { token: i1$1.SdConfirmService }], target: i0.ɵɵFactoryTarget.Component });
2018
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdPopupExport, deps: [{ token: i0.ChangeDetectorRef }, { token: i1$2.SdLoadingService }, { token: i1$2.SdExcelService }, { token: i1$2.SdNotifyService }, { token: i1$2.SdConfirmService }], target: i0.ɵɵFactoryTarget.Component });
2042
2019
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.17", type: SdPopupExport, isStandalone: true, selector: "sd-popup-export", inputs: { _tableOption: ["tableOption", "_tableOption"], configuration: "configuration" }, outputs: { export: "export" }, viewQueries: [{ propertyName: "modal", first: true, predicate: ["modal"], descendants: true }, { propertyName: "listGroups", first: true, predicate: ["listGroups"], descendants: true }], ngImport: i0, template: "<sd-modal width=\"907px\" [title]=\"'Exported template'\" #modal>\n <div class=\"row\">\n <div *ngIf=\"!files?.length\" class=\"col-12\">\n <div class=\"c-empty\">\n <img class=\"sd-image-data-empty\" alt=\"empty-image\" />\n <div class=\"T14R\">{{ 'There are no exported templates' }}</div>\n </div>\n </div>\n <ng-container *ngIf=\"files?.length\">\n <div class=\"col-12\">\n <div class=\"c-table\" style=\"max-height: 50vh\">\n <table mat-table [dataSource]=\"files\">\n <ng-container matColumnDef=\"stt\">\n <th mat-header-cell class=\"c-th text-center px-5\" *matHeaderCellDef style=\"width: 50px\">#</th>\n <td class=\"text-center px-5\" mat-cell *matCellDef=\"let item; let idx = index\">\n {{ idx + 1 }}\n </td>\n </ng-container>\n <ng-container matColumnDef=\"fileName\">\n <th mat-header-cell class=\"c-th px-5\" *matHeaderCellDef>\n {{ 'File name' }}\n </th>\n <td class=\"px-5\" mat-cell *matCellDef=\"let item\">\n {{ item.fileName }}\n </td>\n </ng-container>\n <ng-container matColumnDef=\"action\">\n <th mat-header-cell class=\"c-th px-5\" *matHeaderCellDef style=\"width: 80px\"></th>\n <td class=\"px-5\" mat-cell *matCellDef=\"let item\">\n <div class=\"d-flex\">\n <button mat-icon-button aria-label=\"delete\" (click)=\"removeFile(item)\">\n <mat-icon>delete</mat-icon>\n </button>\n <button mat-icon-button aria-label=\"download\" (click)=\"onExport(item)\">\n <mat-icon>download</mat-icon>\n </button>\n </div>\n </td>\n </ng-container>\n <tr mat-header-row *matHeaderRowDef=\"['stt', 'fileName', 'action']; sticky: true\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: ['stt', 'fileName', 'action']\"></tr>\n </table>\n </div>\n </div>\n <div *ngIf=\"files.length < 5\" class=\"col-12 mt-10\">\n <sd-button class=\"mr-4\" [title]=\"'New template'\" (click)=\"enableCreating = true\" color=\"info\" [disabled]=\"enableCreating\">\n </sd-button>\n <sd-button *ngIf=\"exportOption?.enableUpload\" [title]=\"'Upload template'\" (click)=\"uploadTemplate()\" type=\"outline\">\n </sd-button>\n </div>\n </ng-container>\n <ng-container *ngIf=\"enableCreating || !files?.length\">\n <div class=\"col-12 mt-20\">\n <div class=\"T14R\">{{ 'Select columns for creating template' }}:</div>\n </div>\n <div class=\"col-12\">\n <mat-chip-listbox selectable=\"true\" multiple=\"true\">\n <ng-container *ngFor=\"let column of columns\">\n <mat-chip-option [selected]=\"selected[column.field]\" (click)=\"selected[column.field] = !selected[column.field]\">\n {{ column.title }}\n </mat-chip-option>\n </ng-container>\n </mat-chip-listbox>\n </div>\n <div class=\"col-12 row\">\n <div class=\"col-10\">\n <sd-input [form]=\"form\" [label]=\"'Template name'\" [(model)]=\"templateName\" [validator]=\"templateNameValidator\"></sd-input>\n </div>\n <div class=\"col-2 mt-8\">\n <sd-button width=\"100%\" [title]=\"'Save'\" color=\"info\" (click)=\"createTemplate()\"> </sd-button>\n </div>\n </div>\n </ng-container>\n </div>\n</sd-modal>\n", styles: [".c-radio-group{display:flex;flex-direction:column;margin:15px 0}.c-radio-button{margin:5px}.c-empty{display:flex;flex-direction:column;justify-content:center;align-items:center;position:static;height:116px;background:#f6f6f6;border-radius:4px}.c-table{position:relative;overflow:auto}.c-table table{width:100%}.c-table .c-th{font-weight:500;font-size:14px;line-height:20px;color:#000}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i6.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i6.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i6.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i6.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i6.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i6.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i6.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i6.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i6.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i6.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i5.MatChipListbox, selector: "mat-chip-listbox", inputs: ["multiple", "aria-orientation", "selectable", "compareWith", "required", "hideSingleSelectionIndicator", "value"], outputs: ["change"] }, { kind: "component", type: i5.MatChipOption, selector: "mat-basic-chip-option, [mat-basic-chip-option], mat-chip-option, [mat-chip-option]", inputs: ["selectable", "selected"], outputs: ["selectionChange"] }, { kind: "component", type: SdButton, selector: "sd-button", inputs: ["autoId", "type", "color", "size", "fontSet", "title", "width", "tooltip", "prefixIcon", "suffixIcon", "disabled", "loading", "block", "htmlType"], outputs: ["click"] }, { kind: "component", type: SdInput, selector: "sd-input", inputs: ["autoId", "name", "appearance", "floatLabel", "size", "form", "label", "helperText", "placeholder", "type", "hideInlineError", "blurOnEnter", "required", "readonly", "disabled", "viewed", "minlength", "maxlength", "pattern", "patternErrorMessage", "validator", "inlineError", "hyperlink", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }, { kind: "component", type: SdModal, selector: "sd-modal", inputs: ["autoId", "title", "color", "width", "height", "view", "modalClass", "lazyLoadContent", "hideClose", "disableBackdropClose"], outputs: ["sdClosed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2043
2020
  }
2044
2021
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdPopupExport, decorators: [{
2045
2022
  type: Component,
2046
2023
  args: [{ selector: 'sd-popup-export', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, MatTableModule, MatIconModule, MatChipsModule, SdButton, SdInput, SdModal], template: "<sd-modal width=\"907px\" [title]=\"'Exported template'\" #modal>\n <div class=\"row\">\n <div *ngIf=\"!files?.length\" class=\"col-12\">\n <div class=\"c-empty\">\n <img class=\"sd-image-data-empty\" alt=\"empty-image\" />\n <div class=\"T14R\">{{ 'There are no exported templates' }}</div>\n </div>\n </div>\n <ng-container *ngIf=\"files?.length\">\n <div class=\"col-12\">\n <div class=\"c-table\" style=\"max-height: 50vh\">\n <table mat-table [dataSource]=\"files\">\n <ng-container matColumnDef=\"stt\">\n <th mat-header-cell class=\"c-th text-center px-5\" *matHeaderCellDef style=\"width: 50px\">#</th>\n <td class=\"text-center px-5\" mat-cell *matCellDef=\"let item; let idx = index\">\n {{ idx + 1 }}\n </td>\n </ng-container>\n <ng-container matColumnDef=\"fileName\">\n <th mat-header-cell class=\"c-th px-5\" *matHeaderCellDef>\n {{ 'File name' }}\n </th>\n <td class=\"px-5\" mat-cell *matCellDef=\"let item\">\n {{ item.fileName }}\n </td>\n </ng-container>\n <ng-container matColumnDef=\"action\">\n <th mat-header-cell class=\"c-th px-5\" *matHeaderCellDef style=\"width: 80px\"></th>\n <td class=\"px-5\" mat-cell *matCellDef=\"let item\">\n <div class=\"d-flex\">\n <button mat-icon-button aria-label=\"delete\" (click)=\"removeFile(item)\">\n <mat-icon>delete</mat-icon>\n </button>\n <button mat-icon-button aria-label=\"download\" (click)=\"onExport(item)\">\n <mat-icon>download</mat-icon>\n </button>\n </div>\n </td>\n </ng-container>\n <tr mat-header-row *matHeaderRowDef=\"['stt', 'fileName', 'action']; sticky: true\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: ['stt', 'fileName', 'action']\"></tr>\n </table>\n </div>\n </div>\n <div *ngIf=\"files.length < 5\" class=\"col-12 mt-10\">\n <sd-button class=\"mr-4\" [title]=\"'New template'\" (click)=\"enableCreating = true\" color=\"info\" [disabled]=\"enableCreating\">\n </sd-button>\n <sd-button *ngIf=\"exportOption?.enableUpload\" [title]=\"'Upload template'\" (click)=\"uploadTemplate()\" type=\"outline\">\n </sd-button>\n </div>\n </ng-container>\n <ng-container *ngIf=\"enableCreating || !files?.length\">\n <div class=\"col-12 mt-20\">\n <div class=\"T14R\">{{ 'Select columns for creating template' }}:</div>\n </div>\n <div class=\"col-12\">\n <mat-chip-listbox selectable=\"true\" multiple=\"true\">\n <ng-container *ngFor=\"let column of columns\">\n <mat-chip-option [selected]=\"selected[column.field]\" (click)=\"selected[column.field] = !selected[column.field]\">\n {{ column.title }}\n </mat-chip-option>\n </ng-container>\n </mat-chip-listbox>\n </div>\n <div class=\"col-12 row\">\n <div class=\"col-10\">\n <sd-input [form]=\"form\" [label]=\"'Template name'\" [(model)]=\"templateName\" [validator]=\"templateNameValidator\"></sd-input>\n </div>\n <div class=\"col-2 mt-8\">\n <sd-button width=\"100%\" [title]=\"'Save'\" color=\"info\" (click)=\"createTemplate()\"> </sd-button>\n </div>\n </div>\n </ng-container>\n </div>\n</sd-modal>\n", styles: [".c-radio-group{display:flex;flex-direction:column;margin:15px 0}.c-radio-button{margin:5px}.c-empty{display:flex;flex-direction:column;justify-content:center;align-items:center;position:static;height:116px;background:#f6f6f6;border-radius:4px}.c-table{position:relative;overflow:auto}.c-table table{width:100%}.c-table .c-th{font-weight:500;font-size:14px;line-height:20px;color:#000}\n"] }]
2047
- }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1$1.SdLoadingService }, { type: i1$1.SdExcelService }, { type: i1$1.SdNotifyService }, { type: i1$1.SdConfirmService }], propDecorators: { modal: [{
2024
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1$2.SdLoadingService }, { type: i1$2.SdExcelService }, { type: i1$2.SdNotifyService }, { type: i1$2.SdConfirmService }], propDecorators: { modal: [{
2048
2025
  type: ViewChild,
2049
2026
  args: ['modal']
2050
2027
  }], listGroups: [{
@@ -2394,19 +2371,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
2394
2371
  class SdSelectionDisabledPipe {
2395
2372
  transform = (selectedItems, rowData, selection) => {
2396
2373
  const { disabled, actions } = selection;
2374
+ if (rowData.meta.selector.isSelected) {
2375
+ rowData.meta.selector.selectable = true;
2376
+ return false;
2377
+ }
2397
2378
  if (!actions?.length) {
2398
2379
  if (!disabled) {
2399
2380
  rowData.meta.selector.selectable = true;
2400
2381
  return false;
2401
2382
  }
2402
- rowData.meta.selector.selectable = !disabled(rowData, selectedItems);
2383
+ rowData.meta.selector.selectable = !disabled(rowData.data, selectedItems.map(e => e.data));
2403
2384
  return !rowData.meta.selector.selectable;
2404
2385
  }
2405
2386
  // Kiểm tra có bị disabled theo function không
2406
2387
  // Dữ liệu chưa được check thì kiểm tra hàm disable nếu có
2407
- if (disabled && !rowData.meta.selector.isSelected) {
2388
+ if (disabled) {
2408
2389
  // Nếu disabled và dữ liệu chưa được check
2409
- if (disabled(rowData, selectedItems)) {
2390
+ if (disabled(rowData.data, selectedItems.map(e => e.data))) {
2410
2391
  return true;
2411
2392
  }
2412
2393
  }
@@ -2471,7 +2452,7 @@ class SdSelectionVisibleSelectAllPipe {
2471
2452
  await new Promise(resolve => setTimeout(resolve, 500));
2472
2453
  const first = items.find(t => t.meta.selector?.actions?.length);
2473
2454
  if (first) {
2474
- for (const action of first.meta.selector?.actions) {
2455
+ for (const action of first.meta.selector?.actions ?? []) {
2475
2456
  if (items.filter(t => t.meta.selector?.actions?.length).every(e => e.meta.selector?.actions?.includes(action))) {
2476
2457
  return true;
2477
2458
  }
@@ -2497,7 +2478,7 @@ class SdSelectionVisiblePipe {
2497
2478
  rowData.meta.selector.selectable = true;
2498
2479
  return rowData.meta.selector.selectable;
2499
2480
  }
2500
- for (const action of selection?.actions) {
2481
+ for (const action of selection.actions) {
2501
2482
  if ('children' in action) {
2502
2483
  let flag = false;
2503
2484
  let hasGroup = false;
@@ -2826,7 +2807,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
2826
2807
  }] });
2827
2808
 
2828
2809
  class TableFormatService {
2829
- // Inject các pipe/service dùng để format dữ liệu
2810
+ // Keep table display formatting aligned with the public template pipes.
2811
+ #formatDatePipe = inject(SdFormatDatePipe);
2812
+ #formatDatetimePipe = inject(SdFormatDatetimePipe);
2830
2813
  #formatNumberPipe = inject(SdFormatNumberPipe);
2831
2814
  // ==========================================
2832
2815
  // PUBLIC METHODS
@@ -2987,15 +2970,15 @@ class TableFormatService {
2987
2970
  // PRIVATE HELPERS
2988
2971
  // ==========================================
2989
2972
  #formatDateDisplay(value, type) {
2990
- const date = DateUtilities$1.toFormat(value, 'dd/MM/yyyy');
2991
- const time = DateUtilities$1.toFormat(value, 'HH:mm:ss');
2973
+ const date = this.#formatDatePipe.transform(value);
2974
+ const time = this.#formatDatetimePipe.transform(value, 'HH:mm:ss');
2992
2975
  if (type === 'datetime') {
2993
2976
  return time && date ? `<div class="T14R">${date}<span class="T14R text-black400 ml-4">${time}</span></div>` : '';
2994
2977
  }
2995
2978
  if (type === 'date')
2996
- return date || '';
2979
+ return date ?? '';
2997
2980
  if (type === 'time')
2998
- return time || '';
2981
+ return time ?? '';
2999
2982
  return '';
3000
2983
  }
3001
2984
  #processValuesDisplay(value, column, field, cacheObjValues) {
@@ -3049,7 +3032,6 @@ const buildColumnWidthMap = (column) => {
3049
3032
  return result;
3050
3033
  };
3051
3034
 
3052
- /* eslint-disable @typescript-eslint/no-explicit-any */
3053
3035
  const isTableItem = (item) => !!item && typeof item === 'object' && 'data' in item && 'meta' in item;
3054
3036
  const resolveItemData = (item) => (isTableItem(item) ? item.data : item);
3055
3037
  const hasActiveColumnFilter = (rawColumnFilter = {}, columns = []) => columns.some(col => {
@@ -3525,12 +3507,12 @@ class SdTableFilterService {
3525
3507
  columnOperator,
3526
3508
  };
3527
3509
  };
3528
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTableFilterService, deps: [{ token: i1$1.SdStorageService }], target: i0.ɵɵFactoryTarget.Injectable });
3510
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTableFilterService, deps: [{ token: i1$2.SdStorageService }], target: i0.ɵɵFactoryTarget.Injectable });
3529
3511
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTableFilterService });
3530
3512
  }
3531
3513
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTableFilterService, decorators: [{
3532
3514
  type: Injectable
3533
- }], ctorParameters: () => [{ type: i1$1.SdStorageService }] });
3515
+ }], ctorParameters: () => [{ type: i1$2.SdStorageService }] });
3534
3516
 
3535
3517
  class MatPaginatorIntlCro extends MatPaginatorIntl {
3536
3518
  // i18n labels resolved at construction. Reads VI/EN from I18nService.